{"slug": "model-context-protocol-mcp-for-typescript-developers-a-10-minute-guide", "title": "Model Context Protocol (MCP) for TypeScript Developers: A 10-Minute Guide", "summary": "Anthropic released the Model Context Protocol (MCP) in late 2024 to standardize how AI agents connect to data sources. A developer built a TypeScript filesystem server using the MCP SDK, demonstrating how to expose filesystem operations to any MCP-compatible agent without custom integration code.", "body_md": "Anthropic released Model Context Protocol in late 2024 to solve a real problem: every AI agent needs custom code to connect to data sources. MCP standardizes this. Here's what it is and how to use it with TypeScript.\n\nBefore MCP, connecting an AI agent to your database looked like this:\n\n``` js\n// Custom integration for Agent A\nasync function agentAGetData() {\n const db = new DatabaseClient();\n return await db.query('SELECT * FROM users');\n}\n\n// Different custom integration for Agent B\nasync function agentBGetData() {\n const conn = connectToDatabase();\n return conn.fetchUsers();\n}\n```\n\nEvery agent, every tool, every integration required custom code. MCP creates a standard protocol so you write the integration once and any MCP-compatible agent can use it.\n\nThink of it like OAuth did for authentication—instead of building login flows for every app, you implement OAuth once and everyone can use it.\n\nModel Context Protocol is:\n\n**In practice:** You write an MCP server that exposes your filesystem, database, or API. Any agent that speaks MCP can then access that data without custom integration code.\n\n**Use MCP when:**\n\n**Skip MCP when:**\n\nMCP adds structure. That's great for maintainability, but it's overhead for simple projects.\n\nWe'll build a filesystem server that lets agents read files.\n\n```\nmkdir mcp-filesystem-server\ncd mcp-filesystem-server\nnpm init -y\nnpm install @modelcontextprotocol/sdk\nnpm install -D typescript @types/node tsx\nnpx tsc --init\n```\n\nCreate `src/index.ts`\n\n:\n\n``` js\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport fs from 'fs/promises';\nimport path from 'path';\n\n// Create the MCP server\nconst server = new Server(\n {\n   name: 'filesystem-server',\n   version: '1.0.0',\n },\n {\n   capabilities: {\n     tools: {},\n   },\n }\n);\n\n// Define what tools this server provides\nserver.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n   tools: [\n     {\n       name: 'read_file',\n       description: 'Read the contents of a file',\n       inputSchema: {\n         type: 'object',\n         properties: {\n           path: {\n             type: 'string',\n             description: 'Path to the file to read',\n           },\n         },\n         required: ['path'],\n       },\n     },\n     {\n       name: 'list_directory',\n       description: 'List files in a directory',\n       inputSchema: {\n         type: 'object',\n         properties: {\n           path: {\n             type: 'string',\n             description: 'Path to the directory',\n           },\n         },\n         required: ['path'],\n       },\n     },\n   ],\n };\n});\n\n// Handle tool execution\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n   if (name === 'read_file') {\n     const filePath = args.path as string;\n     const content = await fs.readFile(filePath, 'utf-8');\n     return {\n       content: [\n         {\n           type: 'text',\n           text: content,\n         },\n       ],\n     };\n   }\n\n   if (name === 'list_directory') {\n     const dirPath = args.path as string;\n     const files = await fs.readdir(dirPath);\n     const fileList = files.join('\\n');\n     return {\n       content: [\n         {\n           type: 'text',\n           text: `Files in ${dirPath}:\\n${fileList}`,\n         },\n       ],\n     };\n   }\n\n   return {\n     content: [\n       {\n         type: 'text',\n         text: `Unknown tool: ${name}`,\n       },\n     ],\n     isError: true,\n   };\n } catch (error) {\n   return {\n     content: [\n       {\n         type: 'text',\n         text: `Error: ${error instanceof Error ? error.message : String(error)}`,\n       },\n     ],\n     isError: true,\n   };\n }\n});\n\n// Start the server\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error('Filesystem MCP server running on stdio');\n}\n\nmain().catch(console.error);\n# Build\nnpx tsc\n\n# Test (the server communicates over stdio)\nnode dist/index.js\n```\n\nThe server is now running and waiting for MCP requests over stdin/stdout.\n\nYou created an MCP server with two capabilities:\n\nWhen a client (like Claude Desktop) connects:\n\nClaude Desktop has built-in MCP support. To use your server:\n\nOn macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`\n\nOn Windows: `%APPDATA%/Claude/claude_desktop_config.json`\n\n```\n{\n \"mcpServers\": {\n   \"filesystem\": {\n     \"command\": \"node\",\n     \"args\": [\"/absolute/path/to/your/dist/index.js\"]\n   }\n }\n}\n```\n\nAfter restarting, Claude can now read files and list directories using your MCP server.\n\nAsk Claude: \"What files are in my Documents folder?\" and it will use your MCP server to answer.\n\nYou don't have to write every integration. Several official servers exist:\n\n```\n# Install pre-built servers\nnpm install -g @modelcontextprotocol/server-filesystem\nnpm install -g @modelcontextprotocol/server-github\nnpm install -g @modelcontextprotocol/server-postgres\n```\n\nConfigure them in `claude_desktop_config.json`\n\n:\n\n```\n{\n \"mcpServers\": {\n   \"filesystem\": {\n     \"command\": \"npx\",\n     \"args\": [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/Users/you/Documents\"]\n   },\n   \"github\": {\n     \"command\": \"npx\",\n     \"args\": [\"-y\", \"@modelcontextprotocol/server-github\"],\n     \"env\": {\n       \"GITHUB_TOKEN\": \"your-token-here\"\n     }\n   }\n }\n}\n```\n\nNow Claude can access your filesystem and GitHub without you writing any integration code.\n\nLet's build something more practical—a PostgreSQL server:\n\n``` js\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { Client } from 'pg';\n\nconst dbClient = new Client({\n connectionString: process.env.DATABASE_URL,\n});\n\nawait dbClient.connect();\n\nconst server = new Server(\n {\n   name: 'postgres-server',\n   version: '1.0.0',\n },\n {\n   capabilities: {\n     tools: {},\n   },\n }\n);\n\nserver.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n   tools: [\n     {\n       name: 'query',\n       description: 'Execute a SQL query (SELECT only for safety)',\n       inputSchema: {\n         type: 'object',\n         properties: {\n           sql: {\n             type: 'string',\n             description: 'SQL query to execute',\n           },\n         },\n         required: ['sql'],\n       },\n     },\n   ],\n };\n});\n\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n if (name === 'query') {\n   const sql = args.sql as string;\n\n   // Safety check - only allow SELECT\n   if (!sql.trim().toLowerCase().startsWith('select')) {\n     return {\n       content: [\n         {\n           type: 'text',\n           text: 'Only SELECT queries are allowed',\n         },\n       ],\n       isError: true,\n     };\n   }\n\n   try {\n     const result = await dbClient.query(sql);\n     return {\n       content: [\n         {\n           type: 'text',\n           text: JSON.stringify(result.rows, null, 2),\n         },\n       ],\n     };\n   } catch (error) {\n     return {\n       content: [\n         {\n           type: 'text',\n           text: `Query error: ${error instanceof Error ? error.message : String(error)}`,\n         },\n       ],\n       isError: true,\n     };\n   }\n }\n\n return {\n   content: [{ type: 'text', text: `Unknown tool: ${name}` }],\n   isError: true,\n };\n});\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n```\n\nNow any MCP client can query your database safely.\n\nWant to use MCP servers from your own agent? Use the client SDK:\n\n``` js\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';\n\n// Connect to an MCP server\nconst transport = new StdioClientTransport({\n command: 'node',\n args: ['./path/to/mcp-server.js'],\n});\n\nconst client = new Client(\n {\n   name: 'my-agent',\n   version: '1.0.0',\n },\n {\n   capabilities: {},\n }\n);\n\nawait client.connect(transport);\n\n// List available tools\nconst tools = await client.listTools();\nconsole.log('Available tools:', tools);\n\n// Call a tool\nconst result = await client.callTool({\n name: 'read_file',\n arguments: {\n   path: '/path/to/file.txt',\n },\n});\n\nconsole.log('Result:', result);\n```\n\nThis lets you build agents that use MCP servers as tools.\n\nMCP servers can expose resources (data) and tools (actions):\n\n``` js\nimport { ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';\n\n// List available resources\nserver.setRequestHandler(ListResourcesRequestSchema, async () => {\n return {\n   resources: [\n     {\n       uri: 'config://app/settings',\n       name: 'App Settings',\n       mimeType: 'application/json',\n     },\n   ],\n };\n});\n\n// Read a specific resource\nserver.setRequestHandler(ReadResourceRequestSchema, async (request) => {\n const { uri } = request.params;\n\n if (uri === 'config://app/settings') {\n   const settings = { theme: 'dark', notifications: true };\n   return {\n     contents: [\n       {\n         uri,\n         mimeType: 'application/json',\n         text: JSON.stringify(settings, null, 2),\n       },\n     ],\n   };\n }\n\n return { contents: [] };\n});\n```\n\nResources are for data that agents read; tools are for actions they execute.\n\n**MCP servers have full system access.** Consider:\n\nExample safe file access:\n\n``` js\nconst ALLOWED_DIR = '/safe/directory';\n\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n if (request.params.name === 'read_file') {\n   const requestedPath = request.params.arguments.path as string;\n   const resolvedPath = path.resolve(ALLOWED_DIR, requestedPath);\n\n   // Prevent directory traversal\n   if (!resolvedPath.startsWith(ALLOWED_DIR)) {\n     return {\n       content: [{ type: 'text', text: 'Access denied' }],\n       isError: true,\n     };\n   }\n\n   // Safe to proceed\n   const content = await fs.readFile(resolvedPath, 'utf-8');\n   return { content: [{ type: 'text', text: content }] };\n }\n});\n```\n\n**Good fit:**\n\n**Overkill:**\n\nMCP trades flexibility for structure. That's great when you need consistency across projects, but it's extra work for simple cases.\n\nThe MCP ecosystem is early but growing. More servers are being published, and frameworks are adding MCP support.\n\nTo learn more:\n\nFor your projects:\n\nMCP standardizes how AI agents access data. Instead of building custom integrations for every agent, you write an MCP server once and any compatible agent can use it.\n\nIt's useful when you have multiple agents or want reusable integrations. For simple projects, custom tools are often faster.\n\nThe TypeScript SDK makes building MCP servers straightforward. The pattern is consistent: define tools, handle requests, return results. Everything else is application-specific logic.\n\nIf you're building agent infrastructure, MCP is worth learning. If you're building a single agent, evaluate whether the standardization benefits outweigh the added complexity.", "url": "https://wpnews.pro/news/model-context-protocol-mcp-for-typescript-developers-a-10-minute-guide", "canonical_source": "https://dev.to/raju_dandigam/model-context-protocol-mcp-for-typescript-developers-a-10-minute-guide-4dfe", "published_at": "2026-07-07 17:01:45+00:00", "updated_at": "2026-07-07 17:28:34.495258+00:00", "lang": "en", "topics": ["large-language-models", "ai-agents", "developer-tools"], "entities": ["Anthropic", "Model Context Protocol", "TypeScript", "MCP SDK"], "alternates": {"html": "https://wpnews.pro/news/model-context-protocol-mcp-for-typescript-developers-a-10-minute-guide", "markdown": "https://wpnews.pro/news/model-context-protocol-mcp-for-typescript-developers-a-10-minute-guide.md", "text": "https://wpnews.pro/news/model-context-protocol-mcp-for-typescript-developers-a-10-minute-guide.txt", "jsonld": "https://wpnews.pro/news/model-context-protocol-mcp-for-typescript-developers-a-10-minute-guide.jsonld"}}