> ## Documentation Index
> Fetch the complete documentation index at: https://docs.polpo.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Connections

> Project-level external accounts and shared credentials for agents.

Connections let a project authorize external systems once, then expose that access to agents and tools without putting tokens in prompts, loop JSON, or source code.

Use Connections for external integrations such as GitHub, Slack, Google Drive, Telegram, Discord, WhatsApp, remote MCP servers, or API keys. The [Secrets Vault](/docs/agents/vault) is a legacy compatibility surface for existing agent-scoped secrets.

Polpo provides the OAuth callback, encrypted token storage, refresh path, scope metadata, and runtime token resolution. A hosted product can provide managed provider apps; an independently operated instance can configure its own provider apps and callback URLs.

## Connections vs Vault

| Surface     | Scope                              | Best for                                                                                  |
| ----------- | ---------------------------------- | ----------------------------------------------------------------------------------------- |
| Connections | Project-level, grantable to agents | External accounts, OAuth authorizations, MCP URLs, provider API keys, channel credentials |
| Vault       | Agent-level legacy                 | Existing private secrets read through `vault_get` or `vault_list`                         |

<Note>
  Connections store non-secret metadata separately from encrypted tokens and API keys. List APIs, MCP metadata tools, and the dashboard never return stored secret values. The authenticated token endpoint is a separate runtime operation that returns a scoped token to trusted callers.
</Note>

## Provider setup modes

Connections use one project-level inventory, but provider setup can come from different owners:

| Mode           | Who owns the setup?     | User experience                                                         | Best for                                                 |
| -------------- | ----------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------- |
| Managed app    | The hosted platform     | Click **Connect**, approve the provider consent screen, return to Polpo | Fast onboarding in the hosted product                    |
| Instance OAuth | The deployment operator | Configure provider client id/secret once, then users click **Connect**  | Custom branding, custom scopes, dedicated provider quota |
| API key        | The project             | Save an encrypted server-side credential                                | Service accounts, bot tokens, and provider keys          |
| Remote MCP     | The project             | Add an MCP endpoint and optional bearer token                           | Official or custom MCP servers                           |

In OAuth modes, end users should not paste OAuth tokens into chat. The dashboard starts the OAuth flow and the server stores the resulting token encrypted.

OAuth providers redirect to a single server callback:

```txt theme={null}
https://api.polpo.sh/v1/connect/oauth/callback
```

For self-hosting, set `POLPO_CONNECT_CALLBACK_URL` to the exact callback URL registered in the provider console. Google and most OAuth providers require the callback URI to match exactly, including scheme, host, path, and trailing slash.

## Supported providers

Polpo currently ships these provider definitions:

| Provider     | Auth                       | Provider id    |
| ------------ | -------------------------- | -------------- |
| GitHub       | OAuth 2.0                  | `github`       |
| Slack        | OAuth 2.0                  | `slack`        |
| Google Drive | OAuth 2.0                  | `google_drive` |
| Telegram     | Bot token                  | `telegram`     |
| Discord      | Bot token                  | `discord`      |
| WhatsApp     | Cloud API token            | `whatsapp`     |
| MCP URL      | URL / optional bearer auth | `mcp_url`      |
| API key      | Encrypted key              | `api_key`      |

Independent deployments configure OAuth provider apps with environment variables:

| Provider     | Client id                                                                  | Client secret                                                                      |
| ------------ | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| GitHub       | `POLPO_CONNECT_GITHUB_CLIENT_ID`                                           | `POLPO_CONNECT_GITHUB_CLIENT_SECRET`                                               |
| Slack        | `POLPO_CONNECT_SLACK_CLIENT_ID`                                            | `POLPO_CONNECT_SLACK_CLIENT_SECRET`                                                |
| Google Drive | `POLPO_CONNECT_GOOGLE_CLIENT_ID` or `POLPO_CONNECT_GOOGLE_DRIVE_CLIENT_ID` | `POLPO_CONNECT_GOOGLE_CLIENT_SECRET` or `POLPO_CONNECT_GOOGLE_DRIVE_CLIENT_SECRET` |

Optional URL settings:

| Variable                           | Purpose                                                            |
| ---------------------------------- | ------------------------------------------------------------------ |
| `POLPO_CONNECT_CALLBACK_URL`       | Public callback URL used in OAuth authorization and token exchange |
| `APP_URL` or `POLPO_DASHBOARD_URL` | Dashboard URL used after the server completes OAuth                |

## Dashboard flow

1. Open a project.
2. Go to **Connections**.
3. Choose a provider.
4. Complete the OAuth flow, save an API-key connection, or add a remote MCP server URL.
5. Use the connections list to inspect status, scopes, owner, and whether a secret exists.

The dashboard can create and revoke connections. It never displays stored tokens.

Provider cards show how the connection is configured:

* **Managed app**: the hosted platform owns the provider app and users can connect immediately.
* **OAuth**: the instance owns the provider app and users can connect immediately.
* **Setup required**: an admin must configure provider client id/secret before users can authorize.
* **MCP**: the project connects a remote MCP server.
* **Channel**: the project stores bot/channel credentials.

## Using connections from agents

Connections do not automatically grant every agent access. Enable the specific runtime actions in the agent's **Tools** tab or through `allowedTools`:

| Provider     | Runtime tools                                                  |
| ------------ | -------------------------------------------------------------- |
| GitHub       | `github_list_repos`, `github_create_issue`, `github_read_file` |
| Slack        | `slack_list_channels`, `slack_send_message`                    |
| Google Drive | `drive_search_files`, `drive_read_file`                        |

At runtime, these tools resolve the project Connection server-side. The model receives the action result, not the OAuth token or API key.

Connection tools should be assigned explicitly instead of via provider wildcards. That keeps each granted action visible in the agent configuration and audit trail.

If a project has exactly one active connection for a provider, the tool can omit `connectionId`. If there are multiple active connections for the same provider, pass `connectionId` explicitly.

Connections can also be granted to one agent/tool pair. Grants make the runtime permission boundary explicit and auditable:

1. Create or reuse a Connection.
2. Grant a specific agent access to a specific connection-backed tool.
3. Runtime resolves only the matching connection for that tool call.

For remote MCP servers, open the agent's **Integrations** tab:

1. Connect a new MCP server or reuse an existing MCP Connection.
2. Discover the server's tools.
3. Enable only the discovered tools the agent should call.

The agent stores a non-secret MCP server reference and explicit `allowedTools`
entries such as `mcp__linear__list_issues`. Runtime resolves the connection
secret server-side immediately before opening the MCP client.

Custom tools use the same Connection inventory through `ctx.connections`.
Use `ctx.connections.getToken("provider_or_connection_id")` or
`ctx.connections.getHeaders("provider_or_connection_id")` inside `defineTool`
code instead of storing secrets in source, loop JSON, or prompts.

## Builder and MCP behavior

Builder and MCP clients can inspect connection availability, but they cannot create secrets in a transcript.

| Tool                          | Purpose                                                   |
| ----------------------------- | --------------------------------------------------------- |
| `polpo_connections_providers` | List configured providers and whether OAuth is configured |
| `polpo_connections_list`      | List non-secret project connections by provider/status    |

If a user asks the builder to add GitHub, Slack, Drive, MCP, or an API-key credential, it should list provider status and navigate the user to **Connections** or the agent **Integrations** tab. It should not ask the user to paste tokens into chat.

### How this relates to MCP

Connections are independent from MCP, but they can power MCP use cases:

* **MCP as a provider**: `mcp_url` stores a remote MCP server URL and optional bearer credential so Polpo can discover and call tools through the same connection inventory.
* **OAuth providers**: GitHub, Slack, and Google Drive use OAuth directly; they do not require MCP.
* **Polpo MCP tools**: tools such as `polpo_connections_list` expose non-secret connection metadata to builders and external MCP clients. They are not a backdoor for token access.

In practice: use OAuth/API-key Connections for provider credentials; use `mcp_url` when the integration is already exposed as a remote MCP server.

## API

Connections are project-scoped:

| Method | Path                                                                             | Description                                        |
| ------ | -------------------------------------------------------------------------------- | -------------------------------------------------- |
| `GET`  | `/v1/projects/{projectId}/connect/providers`                                     | List configured providers                          |
| `GET`  | `/v1/projects/{projectId}/connect/connections`                                   | List public connection metadata                    |
| `POST` | `/v1/projects/{projectId}/connect/connections/api-key`                           | Store an encrypted API-key connection              |
| `POST` | `/v1/projects/{projectId}/connect/connections/mcp`                               | Store a remote MCP server connection               |
| `POST` | `/v1/projects/{projectId}/connect/connections/{connectionId}/mcp/discover`       | Discover tools from a remote MCP server connection |
| `GET`  | `/v1/projects/{projectId}/connect/grants`                                        | List agent/tool grants for project connections     |
| `POST` | `/v1/projects/{projectId}/connect/grants`                                        | Grant one agent/tool access to a connection        |
| `POST` | `/v1/projects/{projectId}/connect/grants/{grantId}/revoke`                       | Revoke an agent/tool connection grant              |
| `GET`  | `/v1/projects/{projectId}/connect/events`                                        | List connection audit events                       |
| `POST` | `/v1/projects/{projectId}/connect/oauth/start`                                   | Start an OAuth authorization                       |
| `GET`  | `/v1/connect/oauth/callback`                                                     | Canonical OAuth provider callback                  |
| `POST` | `/v1/projects/{projectId}/connect/oauth/callback`                                | Legacy dashboard callback for local/custom flows   |
| `POST` | `/v1/projects/{projectId}/connect/connections/{connectionId}/token`              | Resolve a scoped token for runtime use             |
| `POST` | `/v1/projects/{projectId}/connect/connections/{connectionId}/actions/{actionId}` | Execute a provider action through a connection     |
| `POST` | `/v1/projects/{projectId}/connect/connections/{connectionId}/revoke`             | Revoke a connection                                |

Example API-key connection:

```bash theme={null}
curl -X POST "$POLPO_URL/v1/projects/$PROJECT_ID/connect/connections/api-key" \
  -H "Authorization: Bearer $POLPO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "providerId": "api_key",
    "name": "Internal CRM",
    "apiKey": "crm_live_...",
    "scopes": ["crm:read"]
  }'
```

Example remote MCP connection:

```bash theme={null}
curl -X POST "$POLPO_URL/v1/projects/$PROJECT_ID/connect/connections/mcp" \
  -H "Authorization: Bearer $POLPO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Linear MCP",
    "url": "https://mcp.linear.app/mcp",
    "transport": "http",
    "auth": "bearer",
    "bearerToken": "lin_..."
  }'
```

Discover tools from that server:

```bash theme={null}
curl -X POST "$POLPO_URL/v1/projects/$PROJECT_ID/connect/connections/$CONNECTION_ID/mcp/discover" \
  -H "Authorization: Bearer $POLPO_API_KEY"
```

Example non-secret list response:

```json theme={null}
{
  "ok": true,
  "data": [
    {
      "id": "conn_...",
      "providerId": "github",
      "name": "Engineering GitHub",
      "authType": "oauth2",
      "status": "active",
      "grantedScopes": ["repo"],
      "hasSecret": true
    }
  ]
}
```

## Security model

* Tokens and API keys are encrypted before storage.
* Public connection records include `hasSecret`, not the secret value.
* OAuth state is project-scoped and expires automatically.
* MCP tools are read-only for connection metadata.
* Secret creation happens in the dashboard or through authenticated API calls, not through LLM chat.
* Agent prompts, transcripts, traces, and `mcpServers` config must not contain raw MCP bearer tokens.
