How to Build an MCP Server in TypeScript



This content originally appeared on DEV Community and was authored by Shayan

I’ve been working on UserJot, my SaaS for user feedback and roadmaps, and the next item on my sprint is adding MCP (Model Context Protocol) server support. I want to let users connect their UserJot data directly to Claude Desktop and other AI assistants.

UserJot Dashboard

My backend is built with Node.js and TypeScript, so I needed a TypeScript friendly way to create MCP servers. After exploring various options, I discovered FastMCP and it’s exactly what I was looking for.

What is MCP

MCP (Model Context Protocol) is an open protocol that enables AI assistants like Claude to interact with external tools and data sources. It’s basically a standardized way for AI to use your APIs. You can read more in the official docs.

Getting Started with FastMCP

The setup is simple. First, install the package:

npm install fastmcp

Then create your server with just a few lines of code:

import { FastMCP } from "fastmcp";
import { z } from "zod";

const server = new FastMCP({
  name: "My Server",
  version: "1.0.0",
});

server.addTool({
  name: "add",
  description: "Add two numbers",
  parameters: z.object({
    a: z.number(),
    b: z.number(),
  }),
  execute: async (args) => {
    return String(args.a + args.b);
  },
});

server.start({
  transportType: "stdio",
});

That’s it. You now have a working MCP server that Claude can use to add numbers.

Building a Real Tool

Here’s a more practical example, a text search tool using Prisma. (Note: This is just an example. In production, you’d want to be extremely careful about what database access you expose through MCP. Consider read-only access, query whitelisting, or sandboxed environments.)

import { FastMCP } from "fastmcp";
import { z } from "zod";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();
const server = new FastMCP({
  name: "database-search-mcp",
  version: "1.0.0",
});

server.addTool({
  name: "searchPosts",
  description: "Search posts by title or content",
  parameters: z.object({
    query: z.string().describe("Search term to look for"),
    limit: z.number().min(1).max(20).default(5),
  }),
  execute: async (args) => {
    const posts = await prisma.post.findMany({
      where: {
        OR: [
          { title: { contains: args.query, mode: 'insensitive' } },
          { content: { contains: args.query, mode: 'insensitive' } }
        ]
      },
      take: args.limit,
      orderBy: { updatedAt: 'desc' },
    });

    return JSON.stringify(posts, null, 2);
  },
});

server.start({ transportType: "stdio" });

Testing Your Server

FastMCP includes a CLI for testing:

npx fastmcp dev your-server.ts

This launches an interactive terminal where you can test your tools before connecting them to Claude.

Connecting to Claude Desktop

Once your server is working, add it to Claude Desktop by editing your configuration file:

On macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

On Windows: %APPDATA%\Claude\claude_desktop_config.json

Add your server configuration:

{
  "mcpServers": {
    "database-search": {
      "command": "node",
      "args": ["/path/to/your/server.js"],
      "env": {
        "DATABASE_URL": "postgresql://user:password@localhost:5432/mydb"
      }
    }
  }
}

Restart Claude Desktop, and your tools will be available.

What I Love About FastMCP

After working with it, here’s what stands out:

  • TypeScript-first: Proper types throughout
  • Flexible validation: Works with Zod, ArkType, Valibot, or any Standard Schema validator
  • Built-in features: Sessions, authentication, streaming, progress reporting
  • Great developer experience: Simple API, good docs, easy testing

The authentication support is particularly nice for SaaS products:

const server = new FastMCP({
  name: "My Server",
  version: "1.0.0",
  authenticate: (request) => {
    const apiKey = request.headers["x-api-key"];

    if (!isValidApiKey(apiKey)) {
      throw new Response(null, { status: 401 });
    }

    return { userId: getUserIdFromApiKey(apiKey) };
  },
});

Next Steps

This is exactly what I needed for UserJot. My plan is to build MCP tools that let users:

  • Query their feedback database
  • Create and update roadmap items
  • Generate changelog entries
  • Analyze user sentiment and trends

The beauty of MCP is that once these tools exist, users can interact with their UserJot data naturally through Claude, creating powerful workflows I never imagined.

If you’re building a TypeScript backend and need MCP support, FastMCP is the way to go. It takes the complexity out of the protocol and lets you focus on building useful tools.

Now, back to building. I’ve got some MCP servers to ship!

P.S. If you’re building something and looking for a feedback platform, I’d love for you to give UserJot a try and let me know what you think!


This content originally appeared on DEV Community and was authored by Shayan