-
Why write our own MCP Server
- connect AI to our own code
-
Tech stack
- TypeScript
- Deno
- Deno Deploy
-
Project setup
- install deno: https://docs.deno.com/runtime/getting_started/installation/
- deno init .
- deno add npm:@modelcontextprotocol/[email protected] npm:[email protected] npm:[email protected] npm:@mikemcbride/[email protected] npm:[email protected]
-
Code and test a local MCP server
-
Change to Streamable HTTP MCP Server
-
Deploy
Created
October 30, 2025 09:41
-
-
Save ynonp/421ab41a0c313e8cf04f9ddf4ee99813 to your computer and use it in GitHub Desktop.
mc-from-scratch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; | |
| import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; | |
| import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; | |
| import dadJokes from '@mikemcbride/dad-jokes' | |
| import express from 'express'; | |
| import { z } from "zod"; | |
| // Create server instance | |
| const server = new McpServer({ | |
| name: "dadjokes", | |
| version: "1.0.0", | |
| capabilities: { | |
| resources: {}, | |
| tools: {}, | |
| }, | |
| }); | |
| server.registerTool( | |
| "tell-me-a-joke", | |
| { | |
| inputSchema: { | |
| id: z.number().min(0).max(dadJokes.all.length - 1).describe('joke id') | |
| }, | |
| title: 'Joke Teller', | |
| description: `Tells a joke according to its index. Valid ids 0-${dadJokes.all.length - 1}`, | |
| }, | |
| async ({id}) => { | |
| const joke = { joke: dadJokes.all[id] }; | |
| return ({ | |
| content: [ | |
| { | |
| type: "text", | |
| text: JSON.stringify(joke), | |
| } | |
| ], | |
| structuredContent: {joke}, | |
| }) | |
| } | |
| ) | |
| server.registerTool( | |
| "tell-me-a-random-joke", | |
| { | |
| title: 'Random Joke Teller', | |
| description: 'Tells a random joke', | |
| }, | |
| async () => { | |
| const joke = {joke: dadJokes.random()}; | |
| return ({ | |
| content: [ | |
| { | |
| type: "text", | |
| text: JSON.stringify(joke), | |
| } | |
| ], | |
| structuredContent: joke | |
| }) | |
| } | |
| ); | |
| // Streamable HTTP Server | |
| const app = express(); | |
| app.use(express.json()); | |
| app.post('/mcp', async (req: any, res: any) => { | |
| // Create a new transport for each request to prevent request ID collisions | |
| const transport = new StreamableHTTPServerTransport({ | |
| sessionIdGenerator: undefined, | |
| enableJsonResponse: true | |
| }); | |
| res.on('close', () => { | |
| transport.close(); | |
| }); | |
| await server.connect(transport); | |
| await transport.handleRequest(req, res, req.body); | |
| }); | |
| const port = parseInt(process.env.PORT || '3000'); | |
| app.listen(port, () => { | |
| console.log(`Demo MCP Server running on http://localhost:${port}/mcp`); | |
| }).on('error', error => { | |
| console.error('Server error:', error); | |
| process.exit(1); | |
| }); | |
| // STDIO Server | |
| // const transport = new StdioServerTransport(); | |
| // await server.connect(transport); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment