Skip to content

Commit 57f349d

Browse files
authored
Merge pull request #242 from cloudflare/mhart/stateless-docs-server
Move docs-vectorize MCP server to be stateless
2 parents e8457bb + fcbedd1 commit 57f349d

File tree

7 files changed

+62
-36
lines changed

7 files changed

+62
-36
lines changed

.changeset/cold-teeth-repeat.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'docs-vectorize': minor
3+
---
4+
5+
Moved Docs MCP server to be stateless for /mcp
Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { McpAgent } from 'agents/mcp'
1+
import { createMcpHandler, McpAgent } from 'agents/mcp'
22

3-
import { createApiHandler } from '@repo/mcp-common/src/api-handler'
43
import { getEnv } from '@repo/mcp-common/src/env'
54
import { registerPrompts } from '@repo/mcp-common/src/prompts/docs-vectorize.prompts'
65
import { initSentry } from '@repo/mcp-common/src/sentry'
@@ -11,12 +10,7 @@ import type { Env } from './docs-vectorize.context'
1110

1211
const env = getEnv<Env>()
1312

14-
// The docs MCP server isn't stateful, so we don't have state/props
15-
export type Props = never
16-
17-
export type State = never
18-
19-
export class CloudflareDocumentationMCP extends McpAgent<Env, State, Props> {
13+
export class CloudflareDocumentationMCP extends McpAgent<Env, never, never> {
2014
_server: CloudflareMCPServer | undefined
2115
set server(server: CloudflareMCPServer) {
2216
this._server = server
@@ -25,7 +19,6 @@ export class CloudflareDocumentationMCP extends McpAgent<Env, State, Props> {
2519
if (!this._server) {
2620
throw new Error('Tried to access server before it was initialized')
2721
}
28-
2922
return this._server
3023
}
3124

@@ -37,20 +30,47 @@ export class CloudflareDocumentationMCP extends McpAgent<Env, State, Props> {
3730
}
3831

3932
async init() {
40-
const sentry = initSentry(env, this.ctx)
41-
42-
this.server = new CloudflareMCPServer({
43-
wae: env.MCP_METRICS,
44-
serverInfo: {
45-
name: env.MCP_SERVER_NAME,
46-
version: env.MCP_SERVER_VERSION,
47-
},
48-
sentry,
49-
})
50-
51-
registerDocsTools(this, this.env)
52-
registerPrompts(this)
33+
this.server = createMcpServer(env, this.ctx)
5334
}
5435
}
5536

56-
export default createApiHandler(CloudflareDocumentationMCP)
37+
const sseHandler = CloudflareDocumentationMCP.serveSSE('/sse')
38+
39+
export default {
40+
fetch: async (req: Request, env: Env, ctx: ExecutionContext) => {
41+
const url = new URL(req.url)
42+
if (url.pathname === '/sse' || url.pathname === '/sse/message') {
43+
return sseHandler.fetch(req, env, ctx)
44+
}
45+
if (url.pathname === '/mcp') {
46+
const server = createMcpServer(env, ctx, req)
47+
const mcpHandler = createMcpHandler(server)
48+
return mcpHandler(req, env, ctx)
49+
}
50+
return new Response('Not found', { status: 404 })
51+
},
52+
}
53+
54+
function createMcpServer(
55+
env: Env,
56+
ctx: {
57+
waitUntil: ExecutionContext['waitUntil']
58+
},
59+
req?: Request
60+
) {
61+
const sentry = initSentry(env, ctx, req)
62+
63+
const server = new CloudflareMCPServer({
64+
wae: env.MCP_METRICS,
65+
serverInfo: {
66+
name: env.MCP_SERVER_NAME,
67+
version: env.MCP_SERVER_VERSION,
68+
},
69+
sentry,
70+
})
71+
72+
registerDocsTools(server, env)
73+
registerPrompts(server)
74+
75+
return server
76+
}

apps/docs-vectorize/wrangler.jsonc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"compatibility_date": "2025-03-10",
99
"compatibility_flags": ["nodejs_compat"],
1010
"name": "mcp-cloudflare-docs-vectorize-dev",
11+
"minify": true,
1112
"migrations": [
1213
{
1314
"new_sqlite_classes": ["CloudflareDocumentationMCP"],

apps/workers-bindings/src/bindings.app.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ export class WorkersBindingsMCP extends McpAgent<Env, WorkersBindingsMCPState, P
8383
registerHyperdriveTools(this)
8484

8585
// Add docs tools
86-
registerDocsTools(this, this.env)
87-
registerPrompts(this)
86+
registerDocsTools(this.server, this.env)
87+
registerPrompts(this.server)
8888
}
8989

9090
async getActiveAccountId() {

apps/workers-observability/src/workers-observability.app.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ export class ObservabilityMCP extends McpAgent<Env, State, Props> {
8787
registerObservabilityTools(this)
8888

8989
// Add docs tools
90-
registerDocsTools(this, this.env)
91-
registerPrompts(this)
90+
registerDocsTools(this.server, this.env)
91+
registerPrompts(this.server)
9292
}
9393

9494
async getActiveAccountId() {

packages/mcp-common/src/prompts/docs-vectorize.prompts.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import type { CloudflareMcpAgentNoAccount } from '../types/cloudflare-mcp-agent.types'
1+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
22

33
/**
44
* Registers developer-platform-related prompts with the MCP server
5-
* @param agent The MCP server instance
5+
* @param server The MCP server instance
66
*/
7-
export function registerPrompts(agent: CloudflareMcpAgentNoAccount) {
8-
agent.server.prompt(
7+
export function registerPrompts(server: McpServer) {
8+
server.prompt(
99
'workers-prompt-full',
1010
'Detailed prompt for generating Cloudflare Workers code (and other developer platform products) from https://developers.cloudflare.com/workers/prompt.txt',
1111
async () => ({

packages/mcp-common/src/tools/docs-vectorize.tools.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { z } from 'zod'
22

3-
import type { CloudflareMcpAgentNoAccount } from '../types/cloudflare-mcp-agent.types'
3+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
44

55
interface RequiredEnv {
66
AI: Ai
@@ -12,10 +12,10 @@ const TOP_K = 10
1212

1313
/**
1414
* Registers the docs search tool with the MCP server
15-
* @param agent The MCP server instance
15+
* @param server The MCP server instance
1616
*/
17-
export function registerDocsTools(agent: CloudflareMcpAgentNoAccount, env: RequiredEnv) {
18-
agent.server.tool(
17+
export function registerDocsTools(server: McpServer, env: RequiredEnv) {
18+
server.tool(
1919
'search_cloudflare_documentation',
2020
`Search the Cloudflare documentation.
2121
@@ -57,7 +57,7 @@ ${result.text}
5757

5858
// Note: this is a tool instead of a prompt because
5959
// prompt support is much less common than tools.
60-
agent.server.tool(
60+
server.tool(
6161
'migrate_pages_to_workers_guide',
6262
`ALWAYS read this guide before migrating Pages projects to Workers.`,
6363
{},

0 commit comments

Comments
 (0)