Skip to content

Commit 001cb7e

Browse files
committed
feat: Add workers-bindings mcp server with KV tools
1 parent 669184c commit 001cb7e

20 files changed

+383
-258
lines changed
-91.5 KB
Binary file not shown.
-466 KB
Binary file not shown.
-403 KB
Binary file not shown.
-333 KB
Binary file not shown.
-487 KB
Binary file not shown.
-426 KB
Binary file not shown.

apps/workers-bindings/package.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@
1111
"types": "wrangler types"
1212
},
1313
"devDependencies": {
14-
"@types/node": "^22.14.1",
15-
"marked": "^15.0.7",
16-
"typescript": "^5.5.2",
14+
"@types/node": "22.14.1",
15+
"marked": "15.0.7",
16+
"typescript": "5.5.4",
1717
"workers-mcp": "^0.1.0-3",
18-
"wrangler": "^4.10.0"
18+
"wrangler": "4.10.0"
1919
},
2020
"dependencies": {
21-
"@cloudflare/workers-oauth-provider": "^0.0.2",
22-
"@modelcontextprotocol/sdk": "^1.7.0",
23-
"agents": "^0.0.43",
24-
"hono": "^4.7.4",
25-
"zod": "^3.24.2",
21+
"@cloudflare/workers-oauth-provider": "0.0.2",
22+
"@modelcontextprotocol/sdk": "1.8.0",
23+
"agents": "0.0.49",
24+
"hono": "4.7.6",
25+
"zod": "3.24.2",
2626
"@repo/mcp-common": "workspace:*"
2727
}
2828
}

apps/workers-bindings/src/index.ts

Lines changed: 43 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,75 @@
1-
import app from "./app";
2-
import { McpAgent } from "agents/mcp";
3-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4-
import { z } from "zod";
5-
import OAuthProvider from "@cloudflare/workers-oauth-provider";
6-
import { handleKVNamespacesList } from "@repo/mcp-common/src/api/kv";
7-
import { getCloudflareClient } from "@repo/mcp-common/src/cloudflare-api";
1+
import OAuthProvider from '@cloudflare/workers-oauth-provider'
2+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
3+
import { McpAgent } from 'agents/mcp'
4+
85
import {
96
CloudflareAuthHandler,
107
handleTokenExchangeCallback,
11-
type AccountSchema,
12-
type UserSchema,
13-
} from "@repo/mcp-common/src/cloudflare-oauth-handler";
8+
} from '@repo/mcp-common/src/cloudflare-oauth-handler'
9+
import { registerAccountTools } from '@repo/mcp-common/src/tools/account'
10+
import { registerKVTools } from '@repo/mcp-common/src/tools/kv_namespace'
11+
import { registerWorkersTools } from '@repo/mcp-common/src/tools/worker'
12+
13+
import type { AccountSchema, UserSchema } from '@repo/mcp-common/src/cloudflare-oauth-handler'
1414

15-
export type WorkersBindingsMCPState = { activeAccountId: string | null };
15+
export type WorkersBindingsMCPState = { activeAccountId: string | null }
1616

1717
// Context from the auth process, encrypted & stored in the auth token
1818
// and provided to the DurableMCP as this.props
1919
export type Props = {
20-
accessToken: string;
21-
user: UserSchema["result"];
22-
accounts: AccountSchema["result"];
23-
};
20+
accessToken: string
21+
user: UserSchema['result']
22+
accounts: AccountSchema['result']
23+
}
2424

2525
export class WorkersBindingsMCP extends McpAgent<Env, WorkersBindingsMCPState, Props> {
2626
server = new McpServer({
27-
name: "Demo",
28-
version: "1.0.0",
29-
});
27+
name: 'Demo',
28+
version: '1.0.0',
29+
})
3030

3131
initialState: WorkersBindingsMCPState = {
3232
activeAccountId: null,
33-
};
33+
}
3434

3535
async init() {
36-
this.server.tool("add", { a: z.number(), b: z.number() }, async ({ a, b }) => ({
37-
content: [{ type: "text", text: String(a + b) }],
38-
}));
39-
this.server.tool('kv_namespaces_list', "List all of the kv namespaces in your Cloudflare account", {}, async () => {
40-
const account_id = this.getActiveAccountId();
41-
if (!account_id) {
42-
return {
43-
content: [
44-
{
45-
type: "text",
46-
text: "No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)",
47-
},
48-
],
49-
};
50-
}
51-
try {
52-
const namespaces = await handleKVNamespacesList({
53-
client: getCloudflareClient(this.props.accessToken),
54-
account_id
55-
});
56-
57-
return {
58-
content: [
59-
{
60-
type: "text",
61-
text: JSON.stringify({
62-
namespaces,
63-
count: namespaces.length
64-
})
65-
}
66-
]
67-
}
68-
} catch(error) {
69-
return {
70-
content: [
71-
{
72-
type: "text",
73-
text: `Error listing KV namespaces: ${error instanceof Error && error.message}`,
74-
},
75-
],
76-
};
77-
}
78-
})
36+
registerAccountTools(this)
37+
registerKVTools(this)
38+
registerWorkersTools(this)
7939
}
8040
getActiveAccountId() {
8141
// TODO: Figure out why this fail sometimes, and why we need to wrap this in a try catch
8242
try {
83-
return this.state.activeAccountId ?? null;
43+
return this.state.activeAccountId ?? null
44+
} catch (e) {
45+
return null
46+
}
47+
}
48+
49+
setActiveAccountId(accountId: string) {
50+
// TODO: Figure out why this fail sometimes, and why we need to wrap this in a try catch
51+
try {
52+
this.setState({
53+
...this.state,
54+
activeAccountId: accountId,
55+
})
8456
} catch (e) {
85-
return null;
57+
return null
8658
}
8759
}
8860
}
8961

9062
// Export the OAuth handler as the default
9163
export default new OAuthProvider({
92-
apiRoute: "/workers/bindings/sse",
93-
// TODO: fix these types
64+
apiRoute: '/workers/bindings/sse',
9465
// @ts-ignore
95-
apiHandler: WorkersBindingsMCP.mount("/workers/bindings/sse"),
66+
apiHandler: WorkersBindingsMCP.mount('/workers/bindings/sse'),
9667
// @ts-ignore
9768
defaultHandler: CloudflareAuthHandler,
98-
authorizeEndpoint: "/oauth/authorize",
99-
tokenEndpoint: "/token",
69+
authorizeEndpoint: '/oauth/authorize',
70+
tokenEndpoint: '/token',
10071
tokenExchangeCallback: handleTokenExchangeCallback,
72+
// Cloudflare access token TTL
10173
accessTokenTTL: 3600,
102-
clientRegistrationEndpoint: "/register",
103-
});
74+
clientRegistrationEndpoint: '/register',
75+
})

apps/workers-bindings/static/README.md

Lines changed: 0 additions & 117 deletions
This file was deleted.

apps/workers-bindings/static/img

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)