Skip to main content

Provider

A Provider gives an agent access to an external service — GitHub, Telegram, Slack, AWS, GCP, Azure, MCP servers, or any HTTP API. The agent's requests to that service are transparently intercepted by the Pai sidecar, which injects credentials and enforces policy.

The agent never holds real credentials. It makes requests normally, and Pai handles authentication behind the scenes.

pai apply -f provider.yaml
pai get services
pai describe provider <name>
pai delete service <name>

How it works

When a provider is attached to an agent:

  1. DNS for the provider's hostname resolves to 127.0.0.1 (the sidecar) inside the agent container
  2. The sidecar intercepts the HTTPS request
  3. Policy is checked — if denied, the request is blocked with HTTP 403
  4. Credentials are injected (Bearer token, SigV4 signature, OAuth2 token, etc.)
  5. The real request is forwarded to the external service

Field reference

FieldRequiredDescription
typeYesProvider type (see below)
hostNoHostname to intercept. Defaults to the provider's standard host
auth.typeYes (most types)Authentication method
auth.secretRefYes (most types)Name of the Secret holding credentials
auth.secretKeyNoKey within the Secret (default: token)
auth.agentEnvVarNoEnv var injected into the agent with a dummy value so libraries initialize correctly
config.regionNoCloud region (AWS, Azure)
config.projectNoGCP project ID
config.tenantIdNoAzure AD tenant ID
config.servicesNoRestrict to specific cloud services (e.g. [s3, sqs])
mcp.transportYes (MCP)sse (default) or stdio
mcp.urlYes (MCP SSE)SSE endpoint URL
mcp.commandYes (MCP stdio)Command and args to launch the stdio server
policy.allowNoAllowed actions. ["*"] = allow all
policy.denyNoDenied actions (takes precedence over allow)
policy.httpRulesNoRaw HTTP method + path rules (see below)
policy.mcp.allowedToolsNoMCP only — tool names callable by the agent (empty = all)
policy.mcp.deniedToolsNoMCP only — tools explicitly blocked
scope.repositoriesNoAllowed GitHub repositories
scope.channelsNoAllowed Slack channels
scope.resourcesNoAllowed cloud resource patterns (ARNs, paths)
audit.logRequestsNoLog every API call (default: true)
audit.enforcementNoenforce (default) blocks violations · audit logs but allows — useful for testing a new policy

Provider types

typeauth.typeDescription
githubpatGitHub Personal Access Token
telegrambot-tokenTelegram Bot API
slackbot-tokenSlack Bot token
awsaws-sigv4AWS SigV4 request signing
azureazure-client-credentialsAzure OAuth2 client credentials
gcpgcp-service-accountGCP service account JSON key
mcpapi-key (SSE) / none (stdio)Model Context Protocol server — exposes a tool catalogue to the agent
http-tokenbearerGeneric HTTP API with a bearer token (internal services, webhooks, anything OpenAPI-shaped)

Examples

GitHub

Personal Access Token auth, narrow action allowlist, scoped to specific repositories.

apiVersion: pai.io/v1
kind: Provider
metadata:
name: github-writer
spec:
type: github
auth:
type: pat
secretRef: github-pat
policy:
allow:
- pulls:create
- pulls:comment
- issues:read
- contents:read
deny:
- admin:*
scope:
repositories:
- "myorg/repo-a"
- "myorg/repo-b"

Secret required:

pai add secret github-pat --from-literal token=ghp_YOUR_TOKEN

Telegram

Bot-token auth. agentEnvVar sets a placeholder env var inside the agent so Telegram SDKs initialize correctly without ever seeing the real token.

apiVersion: pai.io/v1
kind: Provider
metadata:
name: telegram-bot
spec:
type: telegram
host: api.telegram.org
auth:
type: bot-token
secretRef: telegram-token
secretKey: token
agentEnvVar: TELEGRAM_BOT_TOKEN
policy:
allow: ["*"]

Secret required:

pai add secret telegram-token --from-literal token=YOUR_BOT_TOKEN

AWS S3

SigV4 request signing from access keys. Scoped to one service and one bucket; read-only via explicit deny on mutation actions.

apiVersion: pai.io/v1
kind: Provider
metadata:
name: s3-reader
spec:
type: aws
auth:
type: aws-sigv4
secretRef: aws-creds
config:
region: us-east-1
services: [s3]
policy:
allow:
- s3:GetObject
- s3:ListBucket
deny:
- s3:DeleteObject
- s3:PutObject
scope:
resources:
- "arn:aws:s3:::my-bucket/*"

Secret required:

pai add secret aws-creds \
--from-literal access_key_id=AKIAIOSFODNN7EXAMPLE \
--from-literal secret_access_key=wJalrXUtnFEMI...

GCP

Service account JSON key exchanged for short-lived OAuth2 tokens (refreshed in the background). Scoped to one project and service.

apiVersion: pai.io/v1
kind: Provider
metadata:
name: gcp-storage
spec:
type: gcp
auth:
type: gcp-service-account
secretRef: gcp-sa-key
config:
project: my-gcp-project
services: [storage]
policy:
allow:
- storage.objects.get
- storage.objects.list
deny:
- storage.objects.delete

Secret required:

pai add secret gcp-sa-key --from-literal key.json="$(cat service-account.json)"

Azure

Service principal client credentials exchanged for OAuth2 tokens against Azure AD. Scoped to one tenant and service.

apiVersion: pai.io/v1
kind: Provider
metadata:
name: azure-storage
spec:
type: azure
auth:
type: azure-client-credentials
secretRef: azure-sp-creds
config:
tenantId: "your-tenant-id"
services: [storage]
policy:
allow:
- "Microsoft.Storage/storageAccounts/read"
deny:
- "Microsoft.Storage/storageAccounts/delete"

Secret required:

pai add secret azure-sp-creds \
--from-literal client_id=YOUR_CLIENT_ID \
--from-literal client_secret=YOUR_CLIENT_SECRET

MCP server (SSE)

Connect the agent to a hosted Model Context Protocol server — Notion, Linear, Sentry, GitHub's MCP, anything that speaks SSE. Pai injects the bearer token on outbound requests; the agent never sees it.

apiVersion: pai.io/v1
kind: Provider
metadata:
name: linear
spec:
type: mcp
mcp:
url: https://mcp.linear.app/sse
transport: sse
auth:
type: api-key
secretRef: linear-mcp-token
policy:
mcp:
allowedTools: [list_issues, create_issue, search_issues]
deniedTools: [delete_issue]

Secret required:

pai add secret linear-mcp-token --from-literal token=lin_api_YOUR_TOKEN

How the agent actually uses it depends on its mode:

  • Harness-backed agents (task / template, spec.type: task or no type). Just list the Provider in spec.providers — nothing else needed. At startup the harness calls tools/list on the MCP server (Pai has a built-in MCP client), applies the allowedTools / deniedTools filter, and registers each remaining tool with the LLM as linear__<tool_name>. The model can invoke tools directly.

  • Service agents (custom container image, spec.type: service). Your runtime (OpenClaw, LangChain, a hand-rolled loop, etc.) is its own MCP client — Pai doesn't reach inside your process to register tools. Point your runtime at the upstream URL (https://mcp.linear.app/sse); the sidecar intercepts the HTTPS request, enforces the policy, and injects the real token. Configure the connection in your agent's own config, typically via configFiles:

    # In your service Agent:
    providers: [linear] # so Pai sets up sidecar interception + policy
    configFiles:
    - path: /home/node/.openclaw/openclaw.json
    content: |
    {
    "mcpServers": [
    {"name": "linear", "url": "https://mcp.linear.app/sse"}
    ]
    }

    The .mcp.json / openclaw.json / whatever format your runtime reads is opaque to Pai — only the HTTPS calls going out matter.

See the MCP Server provider for the full flow and a Linear worked example.

MCP server (stdio)

For self-hosted MCP servers that run as a subprocess (e.g. the official reference servers):

apiVersion: pai.io/v1
kind: Provider
metadata:
name: filesystem
spec:
type: mcp
mcp:
transport: stdio
command: ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/workspace"]
# no auth: stdio servers don't need one

For stdio servers, list the package on the Agent's packages so the binary is installed before the harness starts:

spec:
packages:
npm: ["@modelcontextprotocol/server-filesystem"]
MCP deep-dives
  • MCP Server provider — connecting Pai agents to MCP servers, transport options, and a Linear worked example.
  • MCP Gateway — exposing a Pai-hosted MCP Provider to tools running on developer laptops (Claude Code, Cline, etc.).

Attaching a provider to an agent

spec:
providers:
- github-writer
- telegram-bot

Policy

spec.policy controls which operations an agent can perform through this provider. Pai supports three flavors:

  • Action-based — list the high-level operations (e.g. pulls:create, s3:GetObject) to allow or deny. Works for providers with a built-in action catalogue: GitHub, AWS, Azure, GCP, Telegram.
  • HTTP-level — list raw method + path rules. Works for any HTTP provider, and is the right choice for generic services or when action-based isn't granular enough.
  • MCP tool-list — allow or deny specific tools on an MCP server. Pai filters tools/call frames before they reach the upstream server.

All three live on spec.policy and can be combined.

Action-based policy

spec:
policy:
allow:
- pulls:create
- pulls:comment
- issues:read
deny:
- admin:* # deny always wins
FieldDescription
allowActions permitted. Use ["*"] to allow everything the Provider supports.
denyActions blocked. Evaluated before allow — a matching deny always wins.

Action names follow the upstream service's convention:

ProviderFormatExamples
GitHubresource:actionpulls:create, issues:read, admin:*
AWSservice:Actions3:GetObject, sqs:SendMessage
AzureProvider/resource/actionMicrosoft.Storage/storageAccounts/read
GCPservice.resource.actionstorage.objects.get
Telegrammethod namesendMessage, getUpdates

GitHub example — read-only access to issues and pull requests:

spec:
type: github
auth: {type: pat, secretRef: github-pat}
policy:
allow:
- issues:read
- pulls:read
- contents:read

AWS example — read-only S3, never write or delete:

spec:
type: aws
auth: {type: aws-sigv4, secretRef: aws-creds}
config: {region: us-east-1, services: [s3]}
policy:
allow:
- s3:GetObject
- s3:ListBucket
deny:
- s3:DeleteObject
- s3:PutObject

Telegram example — can send messages, cannot delete chats:

spec:
type: telegram
auth: {type: bot-token, secretRef: telegram-token}
policy:
allow: ["*"]
deny:
- deleteChat
- banChatMember

HTTP-level rules

spec:
policy:
allow: ["*"]
httpRules:
- methods: [GET]
paths: ["/repos/*/issues", "/repos/*/pulls"]
effect: allow
- methods: [POST, PUT, PATCH, DELETE]
paths: ["*"]
effect: deny
FieldDescription
methodsHTTP methods this rule matches (case-insensitive)
pathsPath patterns. Uses fnmatch globs (* = any chars in a segment, ** = across slashes)
effectallow or deny. Deny rules are evaluated first; the first match wins.

If httpRules is defined but nothing matches the incoming request, the request is denied by default. Combine with policy.allow/deny to layer action-based and HTTP-based rules.

Example — read-only GitHub via raw paths:

spec:
type: github
auth: {type: pat, secretRef: github-pat}
policy:
httpRules:
- methods: [GET]
paths: ["/repos/*/*/issues", "/repos/*/*/pulls", "/repos/*/*/contents/**"]
effect: allow
- methods: [POST, PUT, PATCH, DELETE]
paths: ["*"]
effect: deny

Example — generic HTTP service (type: http-token) scoped to one path prefix:

spec:
type: http-token
host: api.internal.example.com
auth: {type: bearer, secretRef: internal-api-token}
policy:
httpRules:
- methods: [GET, POST]
paths: ["/v2/customers/**"]
effect: allow

MCP tool policy

For type: mcp Providers, filter which tools the agent can call on the upstream server. Pai intercepts every tools/call JSON-RPC frame and checks it against the allow/deny lists before forwarding.

spec:
type: mcp
mcp: {url: https://mcp.notion.com/sse, transport: sse}
auth: {type: api-key, secretRef: notion-mcp-token}
policy:
mcp:
allowedTools: [search_pages, fetch_document, fetch_database]
deniedTools: [delete_page, delete_database]
FieldDescription
allowedToolsTool names the agent may call. Empty = all tools permitted.
deniedToolsTool names explicitly blocked. Evaluated before allowedTools — a matching deny always wins.

Tool names come from the MCP server's tools/list response. Pai filters that catalogue at registration time, so the agent only sees tools the policy allows.

Audit mode

Roll a new policy out safely without breaking live traffic. In audit mode, policy violations are logged with an AUDIT (not blocked): prefix and the request is still forwarded — use it to validate a tightening change against real traffic before flipping to enforce.

spec:
audit:
logRequests: true
enforcement: audit # switch to "enforce" once you're confident
FieldDescription
logRequestsLog every API call (default true)
enforcementenforce (default) blocks violations · audit logs them but allows the request

Policy changes (policy, httpRules, audit, scope) take effect on running agents within ~1 minute — no restart required.