Skip to content

JavaScript/TypeScript Development

MCP Server Development with JavaScript

Build MCP servers using JavaScript or TypeScript with the official SDK.

Quick Start

Installation

# npm
npm install @modelcontextprotocol/sdk

# yarn
yarn add @modelcontextprotocol/sdk

# pnpm
pnpm add @modelcontextprotocol/sdk

Basic Server (JavaScript)

const { Server } = require('@modelcontextprotocol/sdk');

const server = new Server({
  name: 'my-js-server',
  version: '1.0.0'
});

server.tool('greet', {
  description: 'Greet someone',
  parameters: {
    type: 'object',
    properties: {
      name: { type: 'string' }
    },
    required: ['name']
  }
}, async ({ name }) => {
  return `Hello, ${name}!`;
});

server.start();

TypeScript Server

import { Server, Tool } from '@modelcontextprotocol/sdk';

interface GreetParams {
  name: string;
  greeting?: string;
}

const server = new Server({
  name: 'my-ts-server',
  version: '1.0.0'
});

const greetTool: Tool<GreetParams> = {
  name: 'greet',
  description: 'Greet someone',
  parameters: {
    type: 'object',
    properties: {
      name: { type: 'string' },
      greeting: { type: 'string', default: 'Hello' }
    },
    required: ['name']
  },
  handler: async ({ name, greeting = 'Hello' }) => {
    return `${greeting}, ${name}!`;
  }
};

server.registerTool(greetTool);
server.start();

Project Setup

Package.json

{
  "name": "my-mcp-server",
  "version": "1.0.0",
  "type": "module",
  "main": "dist/index.js",
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "tsx watch src/index.ts",
    "test": "jest",
    "lint": "eslint src/**/*.ts"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.0"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.0.0",
    "tsx": "^4.0.0",
    "jest": "^29.0.0",
    "@types/jest": "^29.0.0",
    "eslint": "^8.0.0",
    "@typescript-eslint/eslint-plugin": "^6.0.0"
  }
}

TypeScript Configuration

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "commonjs",
    "lib": ["ES2022"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "**/*.test.ts"]
}

Advanced Features

Async Operations

server.tool('fetchData', {
  description: 'Fetch data from API',
  parameters: {
    type: 'object',
    properties: {
      url: { type: 'string', format: 'uri' }
    },
    required: ['url']
  }
}, async ({ url }) => {
  const response = await fetch(url);
  const data = await response.json();
  return JSON.stringify(data, null, 2);
});

Resource Providers

server.resource('config', {
  description: 'Server configuration',
  handler: async () => {
    return {
      version: '1.0.0',
      features: ['tools', 'resources'],
      environment: process.env.NODE_ENV
    };
  }
});

Error Handling

class McpError extends Error {
  constructor(message: string, public code: number = -32000) {
    super(message);
    this.name = 'McpError';
  }
}

server.tool('riskyOperation', {
  // ... parameters
}, async (params) => {
  try {
    return await performOperation(params);
  } catch (error) {
    if (error instanceof ValidationError) {
      throw new McpError('Invalid input', -32602);
    }
    throw new McpError('Operation failed', -32000);
  }
});

Testing

Jest Configuration

// jest.config.js
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  roots: ['<rootDir>/src'],
  testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
  collectCoverageFrom: [
    'src/**/*.ts',
    '!src/**/*.d.ts',
    '!src/**/*.test.ts'
  ]
};

Unit Tests

// src/__tests__/tools.test.ts
import { greetTool } from '../tools';

describe('greet tool', () => {
  it('should greet with name', async () => {
    const result = await greetTool.handler({ name: 'Alice' });
    expect(result).toBe('Hello, Alice!');
  });

  it('should use custom greeting', async () => {
    const result = await greetTool.handler({ 
      name: 'Bob', 
      greeting: 'Hi' 
    });
    expect(result).toBe('Hi, Bob!');
  });
});

Deployment

Docker

FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY dist ./dist

EXPOSE 8000

CMD ["node", "dist/index.js"]

Environment Variables

const config = {
  port: process.env.MCP_PORT || 8000,
  apiKey: process.env.MCP_API_KEY,
  debug: process.env.MCP_DEBUG === 'true'
};

if (!config.apiKey) {
  throw new Error('MCP_API_KEY is required');
}

Performance Optimization

Connection Pooling

import { Pool } from 'pg';

const pool = new Pool({
  max: 20,
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});

server.tool('queryDatabase', {
  // parameters...
}, async ({ query }) => {
  const client = await pool.connect();
  try {
    const result = await client.query(query);
    return result.rows;
  } finally {
    client.release();
  }
});

Caching

const cache = new Map<string, { data: any; expires: number }>();

function cached<T>(key: string, ttl: number, fn: () => Promise<T>): Promise<T> {
  const now = Date.now();
  const cached = cache.get(key);
  
  if (cached && cached.expires > now) {
    return Promise.resolve(cached.data);
  }
  
  return fn().then(data => {
    cache.set(key, { data, expires: now + ttl });
    return data;
  });
}

Common Patterns

Middleware Pattern

type Middleware = (context: any, next: () => Promise<any>) => Promise<any>;

class MiddlewareServer extends Server {
  private middlewares: Middleware[] = [];
  
  use(middleware: Middleware) {
    this.middlewares.push(middleware);
  }
  
  async execute(context: any, handler: () => Promise<any>) {
    let index = 0;
    
    const next = async (): Promise<any> => {
      if (index >= this.middlewares.length) {
        return handler();
      }
      
      const middleware = this.middlewares[index++];
      return middleware(context, next);
    };
    
    return next();
  }
}

Next Steps