Build Your First MCP Server in 20 Minutes (TypeScript)
Source: Dev.to
Overview
MCP (Model Context Protocol) lets AI assistants call external tools directly. This guide shows how to build a simple MCP server in TypeScript that extracts structured data from text.
Prerequisites
- Node.js 18+
- TypeScript
- An MCP‑compatible client (e.g., Claude Desktop, Cursor)
Project Setup
mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"declaration": true
},
"include": ["src/**/*"]
}
Note: The MCP SDK requires
module: "Node16"andmoduleResolution: "Node16".
Server Implementation (src/index.ts)
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "my-tool",
version: "1.0.0",
});
// -------------------------------------------------------------------
// Tool definition
// -------------------------------------------------------------------
server.tool(
"my_tool_name",
"Description of what this tool does",
{
// Parameters defined with Zod
input_text: z.string().describe("The text to process"),
format: z.enum(["json", "csv"]).optional().describe("Output format"),
},
async ({ input_text, format }) => {
// Your logic here
const result = await processText(input_text, format);
return {
content: [
{
type: "text" as const,
text: JSON.stringify(result, null, 2),
},
],
};
}
);
// -------------------------------------------------------------------
// Server startup
// -------------------------------------------------------------------
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch(console.error);
Tool Definition Breakdown
| Part | Purpose |
|---|---|
| Name | Identifier the AI uses to call the tool |
| Description | Helps the AI decide when to use it |
| Schema | Zod schema describing parameters |
| Handler | Async function that performs the work |
Build and Run
npx tsc
node dist/index.js
The server now listens for MCP messages on stdin.
Testing with MCP Inspector
npx @modelcontextprotocol/inspector node dist/index.js
Registering the Server with Claude Desktop
Add the following entry to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"my-tool": {
"command": "node",
"args": ["/absolute/path/to/dist/index.js"]
}
}
}
Restart Claude Desktop; the tool will appear in the tools menu.
Wrapping a REST API as an MCP Tool
server.tool(
"extract_data",
"Extract structured JSON from unstructured text",
{
text: z.string().describe("Text to extract from"),
schema: z.enum(["receipt", "invoice", "email", "resume"]),
},
async ({ text, schema }) => {
const response = await fetch("https://your-api.com/extract", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text, schema }),
});
const result = await response.json();
return {
content: [
{
type: "text" as const,
text: JSON.stringify(result.data, null, 2),
},
],
};
}
);
Best Practices
- Be specific in tool descriptions; the AI uses them to choose the right tool.
- Use
zod .describe()on every parameter to guide the AI. - Return results with
type: "text"; the AI can parse JSON from plain text. - Handle errors gracefully: return an error message as text instead of throwing.
Skip the Tutorial – Use a Ready‑Made Server
git clone https://github.com/avatrix1/structureai-mcp.git
cd structureai-mcp && npm install && npm run build
This pre‑built server extracts structured data from receipts, invoices, emails, and more. It includes 10 free requests and requires no API key.
Built by Avatrix LLC. Full source: