Build Your First (or Next) MCP Server with the TypeScript MCP Template

Build Your First (or Next) MCP Server with the TypeScript MCP Template

6 minute read

Build Your First (or Next) MCP Server with the TypeScript MCP Template

If you've been wanting to build your own Model Context Protocol (MCP) server but weren't sure where to start, I've got something that might save you a bunch of time. I recently extracted a TypeScript template from my real-world MCP projects that handles all the boilerplate and lets you focus on what actually matters: building your tools and resources.

The https://github.com/nickytonline/mcp-typescript-template repository on GitHubOpenGraph image for https://github.com/nickytonline/mcp-typescript-template

MCP Is Still Pretty New permalink

Here's the thing: MCP is still pretty new, and people are just starting to explore building their own servers. There aren't a ton of templates and starter projects out there yet, so I decided to create one.

After building the dev.to MCP server, I realized I had a solid foundation that other folks could benefit from. So I extracted all the good parts (the TypeScript config, the MCP SDK integration, the Vite bundling setup, and Node.js's type-stripping development mode starting from v22.18.0) into a template anyone can use.

Just yesterday, I used this template to spin up my new Pimp My Ride MCP server. I was able to vibe code with the template in about 30 minutes to build my new MCP server.

An animated version of Xzibit with two cars on his head

What You Get Out of the Box permalink

This template comes loaded with everything you need for modern MCP server development:

MCP Foundation:

  • Built on the official MCP TypeScript SDK
  • HTTP Streamable transport (not SSE, which is deprecated)
  • Express-based server for handling MCP connections (I'm a fan of Hono.js, but the MCP SDK leans on Express, so that's what we're using here)
  • Ready-to-extend structure for tools and resources

Modern TypeScript Development:

  • Live reload using Node.js's built-in type stripping during development (Node.js 22.18.0+, no build step needed while coding)
  • Vite for production-ready ES module bundling
  • Full TypeScript support with sensible defaults

Quality Tools:

  • Vitest for testing (with an interactive runner that actually makes testing enjoyable)
  • ESLint and Prettier configured and ready to go with sensible defaults
  • Zod for runtime validation of environment variables and tool inputs

Production Ready:

  • Express-based MCP server setup
  • Pino for structured logging
  • Docker support included
  • Environment configuration through a clean config layer

Getting Started permalink

GitHub makes it easy to use this as a starting point. Instead of forking, you can create a repository from the template. Just click "Use this template" on the repo page, give your new MCP server a name, and you're good to go.

Once you've created your repo from the template:


git clone https://github.com/YOUR_USERNAME/your-mcp-server.git
cd your-mcp-server
npm install
npm run dev

or my favourite, leverage the GitHub CLI


gh repo clone YOUR_USERNAME/your-mcp-server

New to the GitHub CLI? Check out 👇

The Project Structure permalink

Here's how everything's organized:


src/
├── index.ts        # Your MCP Express server lives here
├── config.ts       # Environment variable validation with Zod
├── logger.ts       # Pino logging setup
└── lib/            # Your tools, resources, and helpers
    └── utils.test.ts  # Tests colocated with code

Your server code goes in src/index.ts, reusable utilities and MCP tools live in src/lib/, and tests sit right next to the code they're testing. Tool input schemas use Zod for runtime validation, so you get type safety and validation in one shot.

Testing with Vitest permalink


npm run test        # Interactive mode for development
npm run test:ci     # CI mode with JSON output

Write your tests in *.test.ts files colocated to what it's testing

Configuration Through Environment Variables permalink

All configuration comes through environment variables, validated with Zod and parsed in src/config.ts:

  • PORT - Server port (default: 3000)
  • SERVER_NAME - Your server's name
  • LOG_LEVEL - Pino log level (info, debug, etc.)

The Zod schema ensures your environment variables are valid at startup, so you catch config issues early instead of at runtime. Add new config values by extending the schema in src/config.ts. No hard-coded secrets, everything's validated and documented with defaults.

Logging with Pino permalink

Pino is wired up and ready for structured logging. Just import the logger and use it:


import logger from './logger';

logger.info({ userId: 123 }, 'User logged in');
logger.error({ error: err }, 'Something broke');

The logs are structured JSON in production and pretty-printed during development. Perfect for debugging locally and parsing in production.

Linting and Formatting permalink

The template comes with ESLint and Prettier pre-configured:


npm run lint        # Check for issues
npm run lint:fix    # Auto-fix what we can
npm run format      # Format everything
npm run format:check  # CI-friendly format check

The rules are sensible: two-space indent, double quotes, trailing commas, and TypeScript-aware linting that catches the stuff that matters. Unused variables prefixed with _ are fine (because sometimes you need that for API contracts), and any is discouraged but not forbidden.

Production Ready permalink

When you're ready to ship:


npm run build  # Compile to dist/
npm start      # Run the compiled version

Or use the included Dockerfile:


docker build -t my-mcp-server .
docker run -p 3000:3000 my-mcp-server

Securing Your MCP Server

If you're deploying your MCP server remotely (not just localhost), the MCP security best practices recommend using a proxy or gateway for authentication and authorization. I use Pomerium to secure my MCP servers since it handles auth and access policies without me having to build that stuff myself. It's how I secure the dev.to MCP server and pimp-my-ride-mcp in production.

Full disclosure: I work at Pomerium, but I'd be using it for this even if I didn't. Managing auth and access control for remote services is a pain, and having a proxy that handles it cleanly is genuinely useful.

Why This Setup Works permalink

After building the dev.to MCP server and now pimp-my-ride-mcp with this setup, here's what I appreciate most:

  1. The dev loop is fast. Node.js 22.18.0+ includes built-in type stripping, which means instant reloads without a build step during development.
  2. Vite handles production bundling. When you're ready to ship, Vite compiles everything into clean ES modules.
  3. The MCP SDK integration is straightforward. You can focus on building tools and resources, not wrestling with protocol details.
  4. It's opinionated but not rigid. You can swap out pieces if you want, but the defaults work great.

What's Next permalink

I've got an OAuth 2.0 PR in the works that streamlines authentication and adds coarse-grained authorization. Should be merged soon.

The https://github.com/nickytonline/mcp-typescript-template/pull/2 repository on GitHubOpenGraph image for https://github.com/nickytonline/mcp-typescript-template/pull/2

Clone the template and start building your MCP server. Whether you're exposing an API (like I did with the dev.to MCP server), wrapping a database, or creating custom tools for your AI workflow (like pimp-my-ride-mcp), the template handles the boring parts so you can focus on the interesting problems.

Check out the template repo and give it a star if you find it useful. And if you build something cool with it, let me know!

If you want to stay in touch, all my socials are on nickyt.online.

Until the next one!

Photo by Homa Appliances on Unsplash