Introduction
Building an MCP server is one of the highest-leverage investments an AI engineering team can make. A well-designed MCP server, once built, can be used by Claude, Cursor, custom agents, and any future MCP-compatible system — turning a single engineering effort into permanently reusable infrastructure. This guide walks you through building and deploying a production-ready MCP server from scratch.
We'll build a database MCP server — one of the most common and useful server types — using the TypeScript SDK. By the end, you'll have a server that Claude can use to query your database, insert records, and retrieve context-relevant data, deployed to production with proper auth, logging, and error handling.
Project Setup and Dependencies
Start with a fresh Node.js project: `mkdir my-mcp-server && cd my-mcp-server && npm init -y`. Install the core dependencies: `npm install @modelcontextprotocol/sdk zod`. For a database server, add your database client: `npm install pg` for PostgreSQL or `npm install mysql2` for MySQL.
Configure TypeScript: `npm install -D typescript @types/node tsx`. Create a `tsconfig.json` with `strict: true`, `module: NodeNext`, and `target: ES2022`. Strict TypeScript prevents the type errors that cause subtle MCP protocol bugs.
Create your entry point at `src/index.ts`. Import the MCP Server class and create your server instance with a name and version. These are used during capability negotiation and should be meaningful identifiers.
Build with Greta
Got an idea? Let's start building.
Just describe your idea in plain English. Greta turns it into a working AI-powered app — no coding required.
Start BuildingDefining Tools with Precise Schemas
Tool definitions are the most important part of your MCP server. The schema description is what the AI reads to decide when and how to use your tool — treat it as documentation for a very literal reader that will act on your descriptions exactly.
Use Zod to define input schemas: `z.object({ query: z.string().describe('SQL SELECT query to execute. Only SELECT queries are allowed.'), limit: z.number().optional().default(100).describe('Maximum rows to return') })`. Every field should have a `.describe()` call with a clear, specific description.
Register your tools using `server.setRequestHandler(ListToolsRequestSchema, ...)` and `server.setRequestHandler(CallToolRequestSchema, ...)`. The list handler returns your tool catalog; the call handler executes the requested tool and returns results.
Return results in structured formats the AI can reason about. For database queries, return an object with `rows`, `rowCount`, and `columns` arrays rather than a raw string. Structured data enables the AI to do follow-up reasoning without re-parsing.
Transport Configuration
For local development and Claude Desktop integration, use stdio transport: `const transport = new StdioServerTransport()`. This runs your server as a child process, communicating through standard streams. It's simple, fast, and requires no network configuration.
For production deployment where multiple clients need access, use HTTP/SSE transport. This requires an Express or Hono server alongside your MCP server. The SSE endpoint handles incoming connections; a POST endpoint handles tool call requests.
For remote servers, implement authentication middleware before the MCP handlers. Token-based auth with a bearer token is the most common pattern — validate the `Authorization` header on every request and reject unauthorized connections immediately.
Build with Greta
Got an idea? Let's start building.
Just describe your idea in plain English. Greta turns it into a working AI-powered app — no coding required.
Start BuildingDeploying to Production
Package your server as a Docker container for consistent deployment. Use a minimal Node.js base image and a multi-stage build to keep the final image small. Set `NODE_ENV=production` and ensure all secrets come from environment variables, never from the image.
Deploy to a platform that supports persistent connections for SSE: Railway, Render, or a dedicated EC2/Cloud Run instance. Serverless platforms like Vercel can work for stateless stdio servers but struggle with long-lived SSE connections.
Implement health checks, structured logging (use pino or winston), and error monitoring (Sentry). Add a `/health` endpoint that verifies database connectivity. Configure your deployment platform to restart unhealthy containers automatically.
Monitor tool invocation patterns after launch. High error rates on specific tools indicate schema problems or edge cases. Latency spikes indicate database or upstream API issues. Build dashboards for these metrics from day one.
Related Resources
Key Takeaways
Core concepts covered in this guide
- Introduction
Building an MCP server is one of the highest-leverage investments an AI engineering team can make. A…
- Project Setup and Dependencies
Start with a fresh Node.js project: `mkdir my-mcp-server && cd my-mcp-server && npm init -y`. Instal…
- Defining Tools with Precise Schemas
Tool definitions are the most important part of your MCP server. The schema description is what the …
- Transport Configuration
For local development and Claude Desktop integration, use stdio transport: `const transport = new St…
- Deploying to Production
Package your server as a Docker container for consistent deployment. Use a minimal Node.js base imag…
Related MCP Guides
Build with Greta
Got an idea? Let's start building.
Just describe your idea in plain English. Greta turns it into a working AI-powered app — no coding required.
Start Building