AutoStack .AI
AI Architecture

Building Production-Grade AI Agents with Model Context Protocol (MCP)

AutoStack Team · ·
#mcp #typescript #ai-agents #llm-ops

Custom integrations between Large Language Models (LLMs) and internal enterprise systems have historically relied on fragmented, custom function-calling wrappers. Model Context Protocol (MCP), an open standard introduced by Anthropic, standardizes how client applications expose data sources, developer tools, and API prompts to AI models.

Instead of writing bespoke API handlers for every LLM host (Cursor, Claude Desktop, local agents), engineering teams can build modular, self-contained MCP servers once and expose them across any supported client.

Here is an architectural guide to building and deploying a production-ready MCP server in TypeScript.


1. Core Concepts: Hosts, Clients, and Servers

MCP uses a client-server architecture running over local standard I/O (stdio) or Server-Sent Events (SSE):

┌────────────────────────────────────────────────────────┐
│                      MCP Host                          │
│          (Cursor / Claude Desktop / Custom Agent)     │
│                                                        │
│  ┌────────────────┐            ┌────────────────────┐  │
│  │   LLM Engine   │            │     MCP Client     │  │
│  └───────┬────────┘            └─────────┬──────────┘  │
└──────────┼───────────────────────────────┼─────────────┘
           │                               │
           │ JSON-RPC over stdio / SSE     │
           ▼                               ▼
┌────────────────────────────────────────────────────────┐
│                      MCP Server                        │
│                                                        │
│   ┌──────────────┐   ┌───────────────┐  ┌───────────┐  │
│   │  Resources   │   │     Tools     │  │  Prompts  │  │
│   └──────────────┘   └───────────────┘  └───────────┘  │
└────────────────────────────────────────────────────────┘
  • Resources: Passive contextual data (file contents, database logs, API responses) exposed to the model.
  • Tools: Executable functions that allow models to trigger side-effects (running SQL queries, creating GitHub issues).
  • Prompts: Pre-engineered template workflows exposed directly to end users.

2. Implementing a Custom TypeScript MCP Server

Below is a complete implementation using the official @modelcontextprotocol/sdk. This server exposes a database query tool with strict Zod validation.

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';

// Initialize MCP Server instance
const server = new Server(
  {
    name: 'autostack-database-mcp',
    version: '1.0.0',
  },
  {
    capabilities: {
      tools: {},
    },
  }
);

// Define schema for input validation
const QueryDatabaseSchema = z.object({
  table: z.string().describe('Target database table name'),
  limit: z.number().default(10).describe('Maximum records to fetch'),
});

// 1. List Available Tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: 'query_internal_db',
        description: 'Queries internal metrics database with schema validation',
        inputSchema: {
          type: 'object',
          properties: {
            table: { type: 'string', description: 'Table name to query' },
            limit: { type: 'number', description: 'Limit rows returned' },
          },
          required: ['table'],
        },
      },
    ],
  };
});

// 2. Handle Tool Calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name !== 'query_internal_db') {
    throw new Error(`Tool not found: ${request.params.name}`);
  }

  const { table, limit } = QueryDatabaseSchema.parse(request.params.arguments);

  // Mock execution logic (replace with real ORM query)
  const records = [
    { id: 1, metric: 'cache_hit_ratio', value: '98.4%' },
    { id: 2, metric: 'p99_latency', value: '42ms' },
  ].slice(0, limit);

  return {
    content: [
      {
        type: 'text',
        text: JSON.stringify({ table, rows: records }, null, 2),
      },
    ],
  };
});

// 3. Connect via Standard I/O Transport
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error('🚀 AutoStack MCP Server operational via stdio');
}

main().catch((err) => {
  console.error('Fatal error:', err);
  process.exit(1);
});

3. Configuring MCP in Desktop Hosts

To link this MCP server to Claude Desktop or Cursor, add the executable path to your client settings file (claude_desktop_config.json):

{
  "mcpServers": {
    "autostack-db": {
      "command": "node",
      "args": ["/path/to/dist/mcp-server.js"],
      "env": {
        "DATABASE_URL": "postgresql://postgres:secret@localhost:5432/production"
      }
    }
  }
}

4. Key Performance & Architecture Comparison

Metric / DimensionCustom REST Tool CallingModel Context Protocol (MCP)
Protocol TransportCustom HTTP EndpointsStandardized stdio / SSE
PortabilitySingle app couplingCross-client (Cursor, Claude, Local)
Type SafetyRuntime manual mappingBuilt-in JSON Schema & Zod support
Security ScopeGlobal API Key leakage riskGranular user-approved tool permissions

Summary Roadmap

  1. Decouple Tool Logics: Shift standalone function schemas out of agent prompt files into dedicated MCP servers.
  2. Standardize Transport: Use stdio for local tools and SSE for remote containerized VPC agents.
  3. Enforce Type Validation: Wrap all incoming tool parameters in zod schemas to catch execution errors before hitting database drivers.
← Back to Blog