Skip to content

Commit cf485d0

Browse files
docs: MCP client storage adapter refactor and security improvements
Documents the MCP client manager refactoring that introduces a storage adapter interface and implements automatic OAuth credential cleanup for enhanced security. Changes: - Add new mcp-storage.mdx explaining storage adapter architecture - Document automatic OAuth credential cleanup after authentication - Add security notes about replay attack prevention - Document automatic connection restoration after hibernation - Update OAuth guide with security information Related PR: cloudflare/agents#652 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent b3a1020 commit cf485d0

File tree

3 files changed

+194
-2
lines changed

3 files changed

+194
-2
lines changed

src/content/docs/agents/guides/oauth-mcp-client.mdx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ When your Agent connects to an OAuth-protected MCP server, here's what happens:
2020
3. **Your application presents** the `authUrl` to your user
2121
4. **Your user authenticates** on the provider's site (Slack, etc.)
2222
5. **The provider redirects** back to your Agent's callback URL with an authorization code
23-
6. **Your Agent completes** the connection automatically
23+
6. **Your Agent completes** the connection automatically and clears OAuth credentials for security
24+
25+
:::note[Security]
26+
27+
After successful OAuth completion, the Agent automatically clears the `auth_url` and `callback_url` from storage. This security measure prevents replay attacks and ensures completed authentications cannot be hijacked by malicious actors.
28+
29+
:::
2430

2531
## Connect and initiate OAuth
2632

src/content/docs/agents/model-context-protocol/mcp-client-api.mdx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export class MyAgent extends Agent<Env, never> {
4444

4545
</TypeScriptExample>
4646

47-
Connections persist in the Agent's [SQL storage](/agents/api-reference/store-and-sync-state), and when an Agent connects to an MCP server, all tools from that server become available automatically.
47+
Connections persist in the Agent's [SQL storage](/agents/api-reference/store-and-sync-state), and when an Agent connects to an MCP server, all tools from that server become available automatically. The Agent automatically restores MCP connections after hibernation without requiring manual reconnection.
4848

4949
## Agent MCP Client Methods
5050

@@ -115,10 +115,17 @@ export class MyAgent extends Agent<Env, never> {
115115

116116
If the MCP server requires OAuth authentication, `authUrl` will be returned for user authentication. Connections persist across requests and the Agent will automatically reconnect if the connection is lost.
117117

118+
:::note[Security]
119+
120+
After successful OAuth authentication, the Agent automatically clears sensitive OAuth credentials (`auth_url` and `callback_url`) from storage. This prevents replay attacks and ensures completed authentications cannot be hijacked. Learn more in [MCP storage architecture](/agents/model-context-protocol/mcp-storage/).
121+
122+
:::
123+
118124
**Related:**
119125

120126
- [OAuth handling guide](/agents/guides/oauth-mcp-client)
121127
- [Transport options](/agents/model-context-protocol/transport)
128+
- [MCP storage architecture](/agents/model-context-protocol/mcp-storage/)
122129
- [removeMcpServer()](#removemcpserver)
123130

124131
### `removeMcpServer()`
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
---
2+
title: MCP storage architecture
3+
pcx_content_type: concept
4+
tags:
5+
- MCP
6+
sidebar:
7+
order: 9
8+
---
9+
10+
import { TypeScriptExample } from "~/components";
11+
12+
The MCP client manager uses a storage adapter interface to persist server configurations and OAuth credentials. This architecture allows for flexible storage implementations while maintaining security best practices.
13+
14+
## Storage adapter interface
15+
16+
The `MCPStorageAdapter` interface defines how MCP server configurations are persisted:
17+
18+
<TypeScriptExample>
19+
20+
```ts
21+
interface MCPStorageAdapter {
22+
// Create storage schema
23+
create(): void | Promise<void>;
24+
25+
// Destroy storage schema
26+
destroy(): void | Promise<void>;
27+
28+
// Save or update server configuration
29+
saveServer(server: MCPServerRow): void | Promise<void>;
30+
31+
// Remove server from storage
32+
removeServer(serverId: string): void | Promise<void>;
33+
34+
// List all servers
35+
listServers(): MCPServerRow[] | Promise<MCPServerRow[]>;
36+
37+
// Get server by callback URL (for OAuth)
38+
getServerByCallbackUrl(callbackUrl: string): MCPServerRow | null | Promise<MCPServerRow | null>;
39+
40+
// Clear OAuth credentials after successful auth
41+
clearOAuthCredentials(serverId: string): void | Promise<void>;
42+
}
43+
```
44+
45+
</TypeScriptExample>
46+
47+
### Server configuration structure
48+
49+
Each MCP server configuration includes:
50+
51+
```ts
52+
type MCPServerRow = {
53+
id: string; // Unique server identifier
54+
name: string; // Display name
55+
server_url: string; // MCP server endpoint
56+
client_id: string | null; // OAuth client ID (if applicable)
57+
auth_url: string | null; // OAuth authorization URL (cleared after auth)
58+
callback_url: string; // OAuth callback endpoint
59+
server_options: string | null; // JSON-stringified connection options
60+
};
61+
```
62+
63+
## Default SQL storage
64+
65+
Agents use the `AgentMCPStorageAdapter` by default, which stores MCP configurations in the Agent's SQL database:
66+
67+
- **Table**: `cf_agents_mcp_servers`
68+
- **Persistence**: Configurations survive Agent hibernation
69+
- **Security**: OAuth credentials automatically cleared after authentication
70+
71+
The storage adapter is automatically initialized when you create an Agent:
72+
73+
<TypeScriptExample>
74+
75+
```ts
76+
import { Agent } from "agents";
77+
78+
export class MyAgent extends Agent<Env, never> {
79+
// Storage adapter is automatically configured
80+
// No setup required for default SQL storage
81+
}
82+
```
83+
84+
</TypeScriptExample>
85+
86+
## Security considerations
87+
88+
### Automatic credential cleanup
89+
90+
After successful OAuth authentication, the storage adapter automatically clears sensitive credentials:
91+
92+
- **`callback_url`**: Cleared to prevent malicious second callbacks
93+
- **`auth_url`**: Cleared to prevent re-authentication loops
94+
95+
This ensures:
96+
- OAuth tokens cannot be replayed
97+
- Completed authentications cannot be hijacked
98+
- Storage contains only necessary persistent data
99+
100+
### Callback URL validation
101+
102+
The MCP client manager validates OAuth callbacks by:
103+
104+
1. Checking callback URLs against the database
105+
2. Matching the exact callback URL prefix
106+
3. Rejecting callbacks after credentials are cleared
107+
108+
This prevents unauthorized callback requests from being processed.
109+
110+
## Connection restoration
111+
112+
When an Agent wakes from hibernation, the MCP client manager automatically:
113+
114+
1. Reads server configurations from storage
115+
2. Recreates OAuth-protected connections in `authenticating` state
116+
3. Reconnects to non-OAuth servers
117+
4. Preserves connection state across Agent lifecycles
118+
119+
You don't need to manually restore connections - the Agent handles this automatically.
120+
121+
## Advanced: Custom storage adapters
122+
123+
For advanced use cases, you can implement a custom storage adapter. This is typically only needed for:
124+
125+
- Non-SQL storage backends
126+
- Custom encryption requirements
127+
- Multi-tenant isolation patterns
128+
- External configuration management
129+
130+
:::note
131+
132+
Most users should use the default SQL storage adapter. Custom storage adapters are an advanced feature.
133+
134+
:::
135+
136+
To implement a custom adapter:
137+
138+
<TypeScriptExample>
139+
140+
```ts
141+
import {
142+
Agent,
143+
type MCPStorageAdapter,
144+
type MCPServerRow
145+
} from "agents";
146+
147+
class CustomStorageAdapter implements MCPStorageAdapter {
148+
async create(): Promise<void> {
149+
// Initialize your storage
150+
}
151+
152+
async saveServer(server: MCPServerRow): Promise<void> {
153+
// Persist server configuration
154+
}
155+
156+
async listServers(): Promise<MCPServerRow[]> {
157+
// Retrieve all servers
158+
}
159+
160+
// Implement remaining methods...
161+
}
162+
163+
export class MyAgent extends Agent<Env, never> {
164+
constructor(ctx: AgentContext, env: Env) {
165+
super(ctx, env);
166+
167+
// Custom storage adapter must be configured in constructor
168+
// This is an advanced pattern - most users should use default storage
169+
}
170+
}
171+
```
172+
173+
</TypeScriptExample>
174+
175+
## Related
176+
177+
- [McpClient — API reference](/agents/model-context-protocol/mcp-client-api/) — MCP client methods
178+
- [Handle OAuth with MCP servers](/agents/guides/oauth-mcp-client/) — OAuth integration guide
179+
- [Store and sync state](/agents/api-reference/store-and-sync-state/) — Agent SQL storage

0 commit comments

Comments
 (0)