A template repository for bootstrapping Model Context Protocol (MCP) servers with WebSocket and SSE support, enabling real-time bidirectional communication and server-to-client streaming in Cloudflare Workers.
A template repository for bootstrapping MCPs (Model Context Protocol) for the xava-labs/typescript-agent-framework.
Option A: Use this template
Option B: Use deploy to cloudflare button
The following button will create a new repo in your organization and setup teh CI/CD using Cloudflare:
NOTE: The configuration only needs npm run deploy
for the Deploy command to work
Option C: Use cloudflare create
You can create a new project based on this template using wrangler:
xava-labs/mcp-template
npm create cloudflare@latest --git https://github.com/xava-labs/mcp-template
Once you have completed one of the above methods, then run the following commands in your terminal to get started:
npm install
npm run dev
The above will boostrap a serverless cloudflare compatible MCP Server with the following urls:
npm run dev
: Runs both the MCP Inspector (port 6274) and Cloudflare Worker (port 8787) concurrentlynpm start
: Runs only the Cloudflare Worker (port 8787)npm test
: Runs tests with Vitestnpm run deploy
: Deploys your MCP to Cloudflare Workersnpm run cf-typegen
: Generates TypeScript types for Cloudflare Workers (run this everytime you add new changes to wrangler.jsonc)This template implements an MCP server using Durable Objects for stateful connections. The base project structure offers two main approaches for extending functionality:
By default, the template uses McpHonoServerDO
which combines the MCP server with Hono, a fast and lightweight web framework. This provides a clean routing system and middleware capabilities.
The main server implementation is in src/server.ts
and extends McpHonoServerDO
:
export class ExampleMcpServer extends McpHonoServerDO { // Required abstract method implementation getImplementation(): Implementation { return { name: 'ExampleMcpServer', version: '1.0.0', }; } // Configure server by adding tools, resources, and prompts configureServer(server: McpServer): void { setupServerTools(server); setupServerResources(server); setupServerPrompts(server); } }
To add functionality, use the following modules:
src/tools.ts
): Define functions that clients can callexport function setupServerTools(server: McpServer) { server.tool( 'tool_name', // Name of the tool 'Tool description', // Description { // Parameters schema using zod param1: z.string().describe('Parameter description'), }, async ({ param1 }) => { // Tool implementation return { content: [ { type: "text", text: `Result: ${param1}` } ] }; } ); }
src/resources.ts
): Define persistent resources clients can accessexport function setupServerResources(server: McpServer) { server.resource( 'resource_name', 'resource://path/{id}', async (uri: URL) => { // Resource implementation return { contents: [ { text: `Resource data`, uri: uri.href } ] }; } ); }
src/prompts.ts
): Define prompt templatesexport function setupServerPrompts(server: McpServer) { server.prompt( 'prompt_name', 'Prompt description', () => ({ messages: [{ role: 'assistant', content: { type: 'text', text: `Your prompt text here` } }] }) ); }
To add custom HTTP endpoints with McpHonoServerDO
, extend the setupRoutes
method:
export class ExampleMcpServer extends McpHonoServerDO { // Other methods... protected setupRoutes(app: Hono): void { // Call the parent implementation to set up MCP routes super.setupRoutes(app); // Add your custom routes app.get('/api/status', (c) => { return c.json({ status: 'ok' }); }); app.post('/api/data', async (c) => { const body = await c.req.json(); // Process data return c.json({ success: true }); }); } }
If you need more control over the HTTP request handling, you can directly extend McpServerDO
instead. This gives you full control over the fetch
method:
export class CustomMcpServer extends McpServerDO { // Required abstract method implementations getImplementation(): Implementation { return { name: 'CustomMcpServer', version: '1.0.0', }; } configureServer(server: McpServer): void { setupServerTools(server); setupServerResources(server); setupServerPrompts(server); } // Override the fetch method for complete control over routing async fetch(request: Request): Promise { const url = new URL(request.url); const path = url.pathname; // Handle custom routes if (path === '/api/custom') { return new Response(JSON.stringify({ custom: true }), { headers: { 'Content-Type': 'application/json' } }); } // Pass through MCP-related requests to the parent implementation return super.fetch(request); } }
This approach is useful when you need to:
For a complete working example, check out the CRUD Todo List MCP Example which demonstrates:
Join our community to get help, share ideas, and contribute to the project:
#mcp
channel for feature requests, support, and discussionsWe welcome contributions to improve this template! Here's how you can contribute:
Fork the repository: Create a fork to make your changes
Create a branch: Make your changes in a new branch
git checkout -b feature/your-feature-name
Commit your changes: Make meaningful commits
git commit -m "Add feature: brief description"
Push to your fork: Push your changes to your fork
git push origin feature/your-feature-name
Create a pull request: Open a PR with a detailed description of your changes
For larger changes or features, we recommend discussing them first in our Discord channel to ensure alignment with the project direction.
Or use the Deploy to Cloudflare button above to deploy directly from GitHub.
Discover shared experiences
Shared threads will appear here, showcasing real-world applications and insights from the community. Check back soon for updates!