diff --git a/MCP-Servers.md b/MCP-Servers.md index a8bfd99..33fefa8 100644 --- a/MCP-Servers.md +++ b/MCP-Servers.md @@ -30,9 +30,9 @@ allows it to be omitted and infers `stdio` from the presence of a `command`). So a new server, look at its docs and find the Claude Code configuration example. You should be able to use the exact same configuration in your `functions/mcp.json` file. Just make sure every entry has an explicit `type`. -**Note:** Coyote does not support Claude Code's `"streamable-http"` alias (use `"http"` instead), nor extras -like `oauth` or `envFile`. For secrets, use [Coyote Vault](Vault) interpolation rather than Claude Code's `${VAR}` -shell-style expansion. +**Note:** Coyote does not support Claude Code's `"streamable-http"` alias (use `"http"` instead) or `envFile`. +For secrets, use [Coyote Vault](Vault) interpolation rather than Claude Code's `${VAR}` shell-style expansion. +OAuth-protected remote servers are supported natively (see [OAuth Authentication](#oauth-authentication) below). Every server entry **must** include a `"type"` field set to one of: `"stdio"`, `"http"`, or `"sse"`. @@ -93,11 +93,12 @@ For remote MCP servers that support the Streamable HTTP transport: } ``` -| Field | Required | Description | -|-----------|----------|--------------------------------------------------------| -| `type` | yes | Must be `"http"` | -| `url` | yes | The server endpoint URL | -| `headers` | no | Custom HTTP headers to include with every request | +| Field | Required | Description | +|-------------------|----------|------------------------------------------------------------------------------------------------| +| `type` | yes | Must be `"http"` | +| `url` | yes | The server endpoint URL | +| `headers` | no | Custom HTTP headers to include with every request | +| `oauth_client_id` | no | OAuth client ID. Omit to use Dynamic Client Registration (auto-registers on first `.mcp auth`) | ## SSE Servers @@ -118,16 +119,120 @@ prefer `http` where the server supports it): } ``` -| Field | Required | Description | -|-----------|----------|--------------------------------------------------------| -| `type` | yes | Must be `"sse"` | -| `url` | yes | The server SSE endpoint URL | -| `headers` | no | Custom HTTP headers to include with every request | +| Field | Required | Description | +|-------------------|----------|------------------------------------------------------------------------------------------------| +| `type` | yes | Must be `"sse"` | +| `url` | yes | The server SSE endpoint URL | +| `headers` | no | Custom HTTP headers to include with every request | +| `oauth_client_id` | no | OAuth client ID. Omit to use Dynamic Client Registration (auto-registers on first `.mcp auth`) | **Note:** Both `http` and `sse` types use the same underlying transport, which auto-negotiates the protocol with the server. The `type` field primarily serves as documentation of which protocol the server speaks. Neither type supports `command`, `args`, or `cwd` fields. +## OAuth Authentication + +Some remote MCP servers require OAuth 2.0 authentication (e.g. Notion, Jira). Coyote supports these +natively, meaning no manual token management is required. + +### Example: Notion + +```json +{ + "mcpServers": { + "notion": { + "type": "http", + "url": "https://mcp.notion.com/mcp" + } + } +} +``` + +That's it. Then in the REPL: + +``` +.mcp auth notion +``` + +Or from the command line: + +```shell +coyote --auth-mcp notion +``` + +Your browser opens, you log into Notion, and the token is saved. On subsequent startups Coyote injects the token +automatically. When the token expires it is refreshed silently. + +### How it works + +1. **Discovery:** Coyote fetches `/.well-known/oauth-protected-resource` and `/.well-known/oauth-authorization-server` + to find the server's authorization and token endpoints (per RFC 9728 / RFC 8414). No manual endpoint configuration needed. +2. **Dynamic Client Registration (RFC 7591):** If the server supports it (the Notion MCP server used in this example does), Coyote + registers itself automatically and caches the client ID in `~/.cache/coyote/oauth/mcp__registration.json`. The + `oauth_client_id` field is only needed when DCR is unavailable. +3. **PKCE authorization code flow:** A localhost callback server is bound on an ephemeral port. Your browser opens for login. + The token is exchanged and stored in `/oauth/mcp__oauth_tokens.json`. +4. **Token injection:** On every connection to the server, Coyote loads the stored token (refreshing if expired) and injects + it as an `Authorization: Bearer` header. No changes to `mcp.json` required. + +### If authentication hasn't been run yet + +If Coyote tries to connect to an OAuth-protected server at startup and gets an auth challenge, it +**warns and skips** the server rather than failing: + +``` +warn: MCP server 'notion' requires authentication. Run `.mcp auth notion` to authenticate. +``` + +Run `.mcp auth notion` (or `coyote --auth-mcp notion`) once to complete the flow, then restart Coyote. + +### Using a pre-existing client ID + +If your organization pre-registers a client with the server (this is rare, as DCR handles this automatically for most servers): + +```json +{ + "mcpServers": { + "my-server": { + "type": "http", + "url": "https://mcp.example.com/mcp", + "oauth_client_id": "your-registered-client-id" + } + } +} +``` + +When `oauth_client_id` is set, DCR is skipped and the provided ID is used directly. + +### Using a static token instead of OAuth + +If the server issues long-lived tokens (e.g. Notion internal integrations), you can skip the OAuth flow +entirely and use a static `Authorization` header: + +```json +{ + "mcpServers": { + "notion": { + "type": "http", + "url": "https://mcp.notion.com/mcp", + "headers": { + "Authorization": "Bearer " + } + } + } +} +``` + +Use [Coyote Vault](Vault) to avoid storing the token in plaintext: + +```json +{ + "headers": { + "Authorization": "Bearer {{notion_token}}" + } +} +``` + ## Secret Injection As mentioned in the [Coyote Vault documentation](Vault), you can use Coyote Vault to inject secrets into your MCP configuration file. diff --git a/REPL.md b/REPL.md index 17a815c..6d6cda1 100644 --- a/REPL.md +++ b/REPL.md @@ -15,6 +15,7 @@ things like * `.model ` to complete chat models * `.set ` to complete configuration keys * `.set key ` to complete configuration values + * `.mcp auth ` to complete remote MCP server names * **Multi-Line Prompts:** You can also type prompts that span more than one line to help organize your thoughts. This can be done in the following ways: * `Ctrl-o` to open the current input buffer in your preferred editor (either the value of `editor` or `$EDITOR`) @@ -338,6 +339,31 @@ The following entities are supported: | `.info tools` | List every tool that would be sent in the next chat completion request (composed across role, agent, skills, and MCP filters). Errors when `function_calling_support` is disabled. | | `.info todo` | Show the current todo list driving auto-continuation (goal, progress count, and per-task status). Only available when `auto_continue` is enabled. | +## `.mcp auth` - Authenticate with an OAuth-protected MCP server + +Some remote MCP servers (such as Notion, Jira, etc.) require OAuth authentication before they can be used. +Run this command once per server to complete the authorization flow: + +``` +.mcp auth +``` + +where `` matches the key in your `mcp.json` file. Tab completion is available. + +**What happens:** +1. Coyote discovers the server's OAuth endpoints automatically via [RFC 9728](https://www.rfc-editor.org/rfc/rfc9728) metadata discovery. +2. If the server supports [Dynamic Client Registration (RFC 7591)](https://www.rfc-editor.org/rfc/rfc7591), Coyote registers itself automatically, meaning no `oauth_client_id` configuration is required. +3. Your browser opens to the server's authorization page. Log in and approve access. +4. The token is saved to `/coyote/oauth/` and loaded automatically on subsequent startups. +5. Tokens are refreshed automatically when they expire. + +**If a server requires authentication but you haven't run `.mcp auth` yet**, Coyote will skip that server at startup +with a warning rather than failing outright. The warning tells you exactly which command to run. + +The same flow is available outside the REPL via `coyote --auth-mcp `. + +For full configuration options and an example for Notion, see the [MCP Servers documentation](MCP-Servers#oauth-authentication). + ## `.authenticate` - Authenticate the current model client via OAuth The `.authenticate` command will start the OAuth flow for the current model client if * The client supports OAuth (See the [clients documentation](Clients#providers-that-support-oauth) for supported clients)