Skip to main content
Glama
JeonJunYeong

sample-mcp

by JeonJunYeong
README.md
# sample-mcp

NestJS + TypeScript MCP (Model Context Protocol) server boilerplate.

This project is intentionally small. Use it as a starting point when you want to add your own MCP tools and serve them through either stdio or Streamable HTTP.

## Features

- NestJS dependency injection for MCP tools
- MCP SDK `Server` integration
- stdio transport for MCP clients such as Claude Desktop and Cursor
- Streamable HTTP transport for local HTTP testing
- Zod-based input validation
- ESLint + Prettier setup for TypeScript

## Project Structure

```text
src/
  main.ts                         # Transport bootstrap: stdio or HTTP
  app.module.ts                   # Root Nest module
  config/
    app.config.ts                 # Environment config
  mcp/
    mcp.module.ts                 # MCP Nest module
    mcp.service.ts                # MCP server and request handlers
    http/
      mcp-http.controller.ts      # POST /mcp Streamable HTTP endpoint
    tools/
      base.tool.ts                # Base class for tools
      tool.registry.ts            # Tool list and execution registry
      tools.module.ts             # Nest providers for tools
      ping/
        ping.dto.ts               # Zod schema and output type
        ping.tool.ts              # Example tool implementation
```

## Setup

```powershell
pnpm install
```

Copy `.env.example` to `.env` if you want local overrides.

```env
MCP_SERVER_NAME=sample-mcp
MCP_SERVER_VERSION=1.0.0
LOG_LEVEL=log
NODE_ENV=development
TRANSPORT=stdio
PORT=3000
```

## Run

### stdio mode

```powershell
pnpm start
```

Use stdio mode when another MCP client launches this server process.

### HTTP mode

```powershell
pnpm run start:http
```

The MCP endpoint is:

```text
POST http://localhost:3000/mcp
```

HTTP requests must include:

```http
Accept: application/json, text/event-stream
Content-Type: application/json
```

## Test With HTTP

List tools:

```powershell
$body = @{
  jsonrpc = "2.0"
  id = 1
  method = "tools/list"
  params = @{}
} | ConvertTo-Json -Depth 10

Invoke-RestMethod `
  -Method Post `
  -Uri "http://localhost:3000/mcp" `
  -ContentType "application/json" `
  -Headers @{ Accept = "application/json, text/event-stream" } `
  -Body $body
```

Call the sample `ping` tool:

```powershell
$body = @{
  jsonrpc = "2.0"
  id = 2
  method = "tools/call"
  params = @{
    name = "ping"
    arguments = @{
      message = "hello"
    }
  }
} | ConvertTo-Json -Depth 10

Invoke-RestMethod `
  -Method Post `
  -Uri "http://localhost:3000/mcp" `
  -ContentType "application/json" `
  -Headers @{ Accept = "application/json, text/event-stream" } `
  -Body $body
```

## Add A New MCP Tool

1. Create a folder under `src/mcp/tools/<tool-name>/`.
2. Create a Zod schema file, for example `<tool-name>.dto.ts`.
3. Create a tool class that extends `BaseTool`.
4. Add the tool class to `ToolsModule.providers`.
5. Inject it into `ToolRegistry` and add it to `registerTools([...])`.

Minimal tool shape:

```typescript
import { Injectable } from '@nestjs/common';
import type { CallToolResult, Tool } from '@modelcontextprotocol/sdk/types.js';
import { BaseTool } from '../base.tool';

@Injectable()
export class MyTool extends BaseTool {
  get definition(): Tool {
    return {
      name: 'my-tool',
      description: 'Describe what this tool does.',
      inputSchema: {
        type: 'object',
        properties: {
          input: { type: 'string' },
        },
        required: ['input'],
      },
    };
  }

  execute(args: Record<string, unknown>): Promise<CallToolResult> {
    return Promise.resolve(this.success({ args }));
  }
}
```

## Quality Checks

This boilerplate uses ESLint 8 with `.eslintrc.cjs` for broad editor and CLI compatibility.

```powershell
pnpm lint
pnpm run lint:fix
pnpm exec tsc --noEmit
pnpm run build
```