-
Notifications
You must be signed in to change notification settings - Fork 66
fix MCP client OAuth origin/redirect guidance for user-added servers #1324
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # Connect remote MCP servers to Kody | ||
|
|
||
| Kody can act as an **MCP client**: you add a remote MCP server, and its tools | ||
| become callable as `kody.mcp["server-name"].tool_name(...)`. | ||
|
|
||
| This is the inverse of [connecting your agent to Kody](./connect-your-agent.md) | ||
| (where Kody is the MCP _server_). | ||
|
|
||
| ## Add a server | ||
|
|
||
| 1. Open [`/account/mcp-servers`](https://heykody.app/account/mcp-servers), or | ||
| ask your agent to use `mcp_server_add` with a short kebab-case `name` and the | ||
| server `url` (https required). | ||
| 2. If the server needs OAuth, Kody returns an authorization link. Open it, sign | ||
| in at the provider, and approve access. | ||
| 3. Confirm with `mcp_server_list` (or refresh the account page). Connected tools | ||
| show up in `search` under a `mcp:<name>` domain. | ||
|
|
||
| ## OAuth allowlists (common failure) | ||
|
|
||
| When Kody connects, it registers as an OAuth client using: | ||
|
|
||
| - **Client origin:** the deployment's canonical app origin (for hosted Kody, | ||
| `https://heykody.app`) | ||
| - **Redirect URI:** `{origin}/account/mcp-servers/oauth/callback` | ||
|
|
||
| Many authorization servers (including FusionAuth "authorized origins" / redirect | ||
| URI settings, and other providers with similar allowlists) reject the authorize | ||
| step unless those values are permitted. | ||
|
|
||
| If authorization fails with a message like `Invalid origin uri https://…` or an | ||
| invalid redirect URI error: | ||
|
|
||
| 1. In the remote MCP server's identity provider, allow Kody's client origin and | ||
| register the exact redirect URI shown on `/account/mcp-servers` (also | ||
| returned as `oauthClientOrigin` / `oauthCallbackUrl` from `mcp_server_add` | ||
| and `mcp_server_list`). | ||
| 2. Remove and re-add the server in Kody (or reconnect) so client registration | ||
| picks up the allowlisted values. | ||
|
Comment on lines
+38
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
if command -v ast-grep >/dev/null 2>&1; then
ast-grep outline packages/worker/src/mcp-client \
--items all \
--type function \
--match 'reconnectServer|addServer|register'
fi
rg -n -C 12 \
'reconnectServer|addServer|register.*client|clientMetadata|redirectUri|redirectUris|oauthCallbackUrl|oauthClientOrigin' \
packages/worker/src/mcp-client \
packages/worker/src/mcpRepository: kentcdodds/kody Length of output: 32970 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo 'hub.ts relevant sections:'
sed -n '136,210p' packages/worker/src/mcp-client/hub.ts
echo
echo 'manager files:'
fd -a '.*\.(ts|tsx)$' packages/worker/src/mcp-client packages/worker/src/mcp | sed 's#^\./##' | head -200
echo
echo 'search mcp manager symbols:'
rg -n -C 8 \
'removeServer|connectToServer|registerServer|addMcpServer|disconnects|unregister|store|metadata|client_metadata|redirect_uri|redirect_uris|origin' \
packages/worker/src/mcp-client packages/worker/src/mcp | head -300
echo
echo 'files containing reconnect/add/remove managers:'
rg -n -C 15 \
'reconnectServer|connectToServer|removeServer|addServer' \
packages/worker/src/mcp-client packages/worker/src/mcpRepository: kentcdodds/kody Length of output: 20038 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo 'hub.ts full file outline and relevant sections:'
wc -l packages/worker/src/mcp-client/hub.ts
sed -n '136,226p' packages/worker/src/mcp-client/hub.ts
echo
echo 'find relevant test/source files:'
rg -n -C 12 \
'DurableObjectOAuthClientProvider|McpClientHub|registerServer|removeServer|connectToServer|restoreConnectionsFromStorage|client_metadata|redirect_uri|redirect_uris|redirectUri|callbackUrl|mcpClientName' \
packages/worker/src/mcp-client packages/worker/src/mcp packages/worker/test packages/worker/workers | head -500
echo
echo 'list mcp-client files:'
fd -e ts . packages/worker/src/mcp-client | sort
echo
echo 'files with agent/sdk imports:'
rg -n \
'`@modelcontextprotocol/sdk`|Agent|OAuth|client_metadata|redirect_uri|callbackUrl|registerServer|removeServer|connectToServer' \
packages/worker/src/mcp-client packages/worker/src/mcp | head -500Repository: kentcdodds/kody Length of output: 42210 🌐 Web query:
💡 Result: In Cloudflare Agents, the MCPClientManager and DurableObjectOAuthClientProvider are components of the framework's Model Context Protocol (MCP) support, designed to manage persistent connections and OAuth authentication within Durable Objects (DOs) [1][2]. MCPClientManager is responsible for managing MCP server connections. Its restoreConnectionsFromStorage(clientName: string) method is called during Agent initialization to recover connections that were previously stored [1][3]. This method retrieves registered servers from storage and initiates connection attempts, typically in the background to avoid blocking the DO's execution [1][4]. DurableObjectOAuthClientProvider is the default implementation of the OAuth provider interface (AgentMcpOAuthProvider) used by the MCP client [5][2]. It manages the OAuth flow specifically within a Durable Object, utilizing the DO's storage to securely maintain sensitive data, including OAuth tokens, nonces, and PKCE-related code verifiers [5][6][2]. Key technical aspects include: - Client Metadata and Redirect URIs: The DurableObjectOAuthClientProvider exposes properties such as clientMetadata, clientUri, and redirectUrl [5]. The redirect_uri is a critical component of the OAuth flow, enabling the provider to receive authorization codes back from the OAuth server. When configuring or customizing this, developers ensure the callback path is correctly registered to maintain security and proper flow completion [7][8]. - Customization: Users can override the default provider by implementing the AgentMcpOAuthProvider interface and specifying it through the agent's createMcpOAuthProvider method [9][10][11]. Recent framework updates have improved support for these custom providers by ensuring that restoreConnectionsFromStorage respects these overrides rather than hardcoding the default provider [10]. - Storage: Because the default provider is tied to ctx.storage, tokens are scoped to a single DO instance. Advanced implementations often utilize custom providers to route token storage to external databases (e.g., KV) to enable cross-DO access [9]. Citations:
🌐 Web query:
💡 Result: The query relates to the Citations:
🌐 Web query:
💡 Result: The Citations:
🌐 Web query:
💡 Result: In Cloudflare Agents, cf_agents_mcp_servers is a SQLite table used to store metadata about connected Model Context Protocol (MCP) servers [1][2][3]. It is managed by the Agent class to persist connection details across sessions, enabling agents to interact with external services like GitHub, Slack, or databases [1][2][4]. Key technical aspects regarding the fields and management of this system include: 1. cf_agents_mcp_servers Table: This internal table stores critical connection information, including the server's unique ID, name, URL, client_id, auth_url, callback_url, and additional server_options [1][3]. This table is automatically maintained by the Agent; its lifecycle (creation and deletion) is managed by the Agent class [2]. 2. OAuth Callback URL (callback_url): When an MCP server requires OAuth authentication, the agent constructs a callback URL to handle the OAuth flow [5][6][7]. * Construction: By default, the URL is constructed as https://{host}/{agentsPrefix}/{agent-name}/{instance-name}/callback [6]. * Configuration: Developers can influence this using options like callbackHost (to set the origin) and callbackPath (to define a custom path) in addMcpServer [6][8][9]. * Optionality: Recent updates have made callbackHost optional, deferring OAuth setup only to cases where it is strictly required, which simplifies connections to non-OAuth MCP servers [10][8]. 3. client_metadata: When implementing a custom OAuth provider for an MCP server, the client_metadata (typically part of an interface like AgentMcpOAuthProvider) is used to supply necessary OAuth credentials, such as client_id, client_secret, and the redirect_uris [5]. This metadata allows the agent to securely manage the authentication handshake with the MCP provider [7]. 4. Stable IDs: Users can provide a stable ID when calling addMcpServer [11][4]. When provided, this ID replaces auto-generated identifiers in the cf_agents_mcp_servers table and other related storage keys, ensuring that tool names and OAuth state remain predictable across restarts [3][4]. The system includes migration logic (migrateServerId) to transition servers from auto-generated IDs to these stable IDs without losing existing connection data [1][11][3]. Citations:
🌐 Web query:
💡 Result: The file do-oauth-client-provider.ts in the cloudflare/agents repository defines the DurableObjectOAuthClientProvider class, which implements the AgentsOAuthProvider interface [1][2][3]. In the constructor, the provider requires an instance of DurableObjectStorage, along with a clientName and baseRedirectUrl [2]. The DurableObjectStorage instance is used to persist OAuth-related data, such as token storage, PKCE code verifiers, and state verification data, enabling secure handling of OAuth flows within Durable Objects [1][2][3]. Key aspects of the implementation include: Constructor: The constructor accepts a DurableObjectStorage object, enforcing its presence with a check that throws an error if it is invalid [2]. It also initializes the client name and base redirect URL [2]. Storage Usage: The class leverages the storage object to perform asynchronous operations [2]. For instance, it uses storage.put and storage.get to manage: - OAuth tokens [2]. - PKCE code verifiers, often bound to specific client IDs or states to prevent overwriting during concurrent flows [1][4][2]. - State parameter validation data, which includes nonces and server IDs, to ensure the security of the OAuth handshake [3]. The callbackUrl is typically managed in the context of the authentication flow, with the provider handling the necessary redirects and state validation to ensure the integrity of the process [1][4][3]. Recent updates have focused on security hardening, such as binding PKCE verifiers to specific callback states and removing sensitive data from logs [4][5][6][3]. Citations:
🌐 Web query:
💡 Result: DurableObjectOAuthClientProvider is a built-in class in the Cloudflare Agents SDK used to manage secure OAuth flows for MCP (Model Context Protocol) servers [1][2]. It handles security-sensitive logic such as CSRF state generation, PKCE (Proof Key for Code Exchange), nonce management, and token storage [1][3]. Key technical details regarding the requested components: - baseRedirectUrl: This is a string parameter passed to the constructor of DurableObjectOAuthClientProvider [4]. It defines the base URL for the OAuth redirect callback used in the authentication flow [4][1]. - clientMetadata: This is a getter method on the DurableObjectOAuthClientProvider class that returns an OAuthClientMetadata object [4][5]. It provides the necessary metadata (such as client IDs and redirect URIs) required for the OAuth handshake [6][7]. - agents/mcp/do-oauth-client-provider: This is the internal module path where the DurableObjectOAuthClientProvider class is defined [3]. Recent updates to the Cloudflare Agents SDK (specifically in version 0.20.1 and later) have exported this class at the top level of the agents package, allowing users to import it directly as { DurableObjectOAuthClientProvider } from "agents" rather than using deep imports [8][9][3]. Developers often use this class when they wish to customize the storage backend for OAuth tokens (e.g., using a database or KV store instead of the default Durable Object storage) while retaining the built-in security logic of the SDK [6][10][7]. This is typically done by overriding the createMcpOAuthProvider method in an Agent subclass [6][10]. Recent patches have ensured that this custom provider override is correctly respected even during Durable Object restarts and connection restorations [8][9][11]. Citations:
🌐 Web query:
💡 Result: In the Cloudflare Agents SDK, particularly within the context of Model Context Protocol (MCP) OAuth authentication, the Citations:
🌐 Web query:
💡 Result: The DurableObjectOAuthClientProvider is a component provided by the Cloudflare Agents SDK designed to manage OAuth authentication state securely within a Durable Object [1][2]. It handles tasks such as storing nonces, generating and validating state for CSRF protection, PKCE (Proof Key for Code Exchange) support, and token persistence [3][2][4]. Key properties and methods of DurableObjectOAuthClientProvider include: - Constructor: It is initialized with a DurableObjectStorage instance (or compatible adapter), the client name, and the baseRedirectUrl [5][4]. - clientId: You can get and set the clientId via the clientId property [5][3]. - baseRedirectUrl: This is passed during instantiation and defines the base URL for OAuth callbacks [5][4]. - clientMetadata: This getter returns an OAuthClientMetadata object, which typically encapsulates information about the OAuth client [5]. - callback_url: While not an explicit property named exactly callback_url, the class utilizes the baseRedirectUrl provided at construction to manage redirection and callback logic [5][4]. The SDK allows developers to customize the OAuth provider by overriding the createMcpOAuthProvider method in their Agent class [4][6]. This is commonly used to implement custom storage backends (e.g., routing tokens to a different database) while maintaining the built-in OAuth logic [4]. The SDK ensures that these custom providers are respected, including during connection restoration from storage [7]. Citations:
🌐 Web query:
💡 Result: In the context of Cloudflare Agents and the Model Context Protocol (MCP), Citations:
🌐 Web query:
💡 Result: The DurableObjectOAuthClientProvider is a component within the Cloudflare Agents framework used to manage OAuth 2.0 flows for MCP (Model Context Protocol) clients [1][2]. It securely handles operations such as CSRF state, PKCE, nonce generation, token management, and storage [1][3][2]. Key components related to your query include: - clientMetadata: This is a property of the provider that returns an object containing OAuth client details, such as the client_id, client_secret, and redirect_uris [1][3][4]. - redirect_uris: An array of URIs where the authorization server sends the user after authentication [1][3][4]. - callbackUrl: Often passed as a parameter to the provider, this represents the endpoint to which the OAuth flow redirects [1][3]. - DurableObjectOAuthClientProvider: Users can override the default OAuth provider by implementing createMcpOAuthProvider in their Agent class, which allows for custom strategies (e.g., pre-registered credentials) or routing token storage to different backends (such as KV or an external database) by passing a custom storage adapter to this provider [1][3][4]. For more details on implementation, developers can refer to the official Cloudflare Agents documentation on the MCP Client API [1][3]. The provider is exported directly from the agents package, enabling easier access for custom implementations [5][6]. Citations:
🌐 Web query:
💡 Result: The Citations:
Clarify what reconnect refreshes for OAuth registrations.
🤖 Prompt for AI Agents |
||
| 3. Authorize again. | ||
|
|
||
| Kody itself does not maintain a per-provider allowlist for this flow — the | ||
| remote authorization server does. | ||
|
|
||
| Servers that do not use OAuth connect immediately and do not need these steps. | ||
|
|
||
| ## Related | ||
|
|
||
| - [Architecture: MCP client servers](../contributing/architecture/mcp-client-servers.md) | ||
| - [Troubleshooting](./troubleshooting.md) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Document the loopback HTTP exception.
Line 13 says that the server URL requires HTTPS. The runtime contract in
docs/contributing/architecture/mcp-client-servers.mdLines 28-31 also permits plain HTTP for loopback hosts. This wording tells users not to use a supported local MCP server. State the exception explicitly.Suggested wording
🤖 Prompt for AI Agents