DEV Community

Alex
Alex

Posted on

Build Your First MCP Server in 20 Minutes (TypeScript)

MCP (Model Context Protocol) lets AI assistants call external tools. Instead of copy-pasting data into Claude, the AI calls your tool directly.

I built an MCP server that extracts structured data from text. Here's exactly how, step by step.

What We're Building

An MCP server that exposes one tool: extract_structured_data. When an AI assistant needs to parse a receipt, invoice, or email, it calls this tool automatically.

Prerequisites

  • Node.js 18+
  • TypeScript
  • An MCP-compatible client (Claude Desktop, Cursor, etc.)

Step 1: Set Up the Project

mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
Enter fullscreen mode Exit fullscreen mode

Create tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "declaration": true
  },
  "include": ["src/**/*"]
}
Enter fullscreen mode Exit fullscreen mode

Important: Use module: "Node16" and moduleResolution: "Node16". The MCP SDK requires these settings.

Step 2: Create the Server

Create 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",
});
Enter fullscreen mode Exit fullscreen mode

Three key imports:

  1. McpServer — the server itself
  2. StdioServerTransport — communicates over stdin/stdout
  3. z (zod) — defines tool parameter schemas

Step 3: Define a Tool

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),
        },
      ],
    };
  }
);
Enter fullscreen mode Exit fullscreen mode

The tool definition has four parts:

  1. Name — what the AI calls
  2. Description — helps the AI decide when to use it
  3. Schema — zod schema for parameters
  4. Handler — async function that does the work

Step 4: Connect to Transport

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

main().catch(console.error);
Enter fullscreen mode Exit fullscreen mode

Step 5: Build and Test

npx tsc
node dist/index.js
Enter fullscreen mode Exit fullscreen mode

The server starts and waits for MCP messages on stdin. To test properly, use the MCP Inspector:

npx @modelcontextprotocol/inspector node dist/index.js
Enter fullscreen mode Exit fullscreen mode

Step 6: Connect to Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "my-tool": {
      "command": "node",
      "args": ["/absolute/path/to/dist/index.js"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Restart Claude Desktop. Your tool now appears in the tools menu.

Real Example: Calling an External API

Here's how to wrap any 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) }],
    };
  }
);
Enter fullscreen mode Exit fullscreen mode

Tips

  1. Tool descriptions matter. The AI reads them to decide which tool to use. Be specific.
  2. Use zod .describe() on every parameter. It helps the AI fill in the right values.
  3. Return type: "text" for all results. The AI can parse JSON from text.
  4. Handle errors gracefully. Return error messages as text, don't throw.

Try a Working Example

Want to skip the tutorial and use a real MCP server?

git clone https://github.com/avatrix1/structureai-mcp.git
cd structureai-mcp && npm install && npm run build
Enter fullscreen mode Exit fullscreen mode

This server extracts structured data from receipts, invoices, emails, and more. 10 free requests included, no API key needed.


Built by Avatrix LLC. Full source at github.com/avatrix1/structureai-mcp.

Top comments (0)