MCP Server Boilerplate logo

MCP Server Boilerplate

by Falkicon

A well-structured boilerplate for building Model Context Protocol (MCP) servers using TypeScript. It aims to accelerate development and promote consistency for MCP integrations.

View on GitHub

Last updated: N/A

MCP Server Boilerplate (TypeScript)

A well-structured, maintainable, and extensible boilerplate project for building Model Context Protocol (MCP) servers using TypeScript. This boilerplate aims to accelerate development, promote consistency, and incorporate best practices for creating MCP integrations.

Features

  • TypeScript: Modern, type-safe JavaScript.
  • MCP SDK: Uses the official @modelcontextprotocol/sdk.
  • Focused Implementation: Streamlined with stdio transport support for simplicity and reliability.
  • Example Primitives: Includes basic examples for:
    • Tools: greet (greeting with configurable prefix) and add (simple calculator)
    • Resources: welcome (welcome message resource)
    • Prompts: summarize (topic summarization prompt)
  • Configuration: Loads configuration from .env files and environment variables (src/config.ts).
  • Validation: Uses zod for robust argument validation in primitives.
  • Structured Logging: Uses pino for structured JSON logging (src/logger.ts). Pretty-printed logs in development.
  • Error Handling: Custom error classes (src/errors.ts) and consistent try/catch patterns in all handlers.
  • Containerization: Multi-stage Dockerfile for creating optimized production images.
  • Linting/Formatting: Configured with ESLint and Prettier.
  • Testing: Setup with Vitest. Includes example tests for configuration loading (tests/config.test.ts) and the greet tool handler (tests/greet.test.ts).

Prerequisites

  • Node.js (v18 or later recommended)
  • npm (usually included with Node.js)
  • Docker (optional, for running in a container)

Setup and Installation

  1. Clone the repository (or use as a template):

    # If you cloned it
    git clone <repository-url>
    cd mcp-server-boilerplate
    
  2. Install dependencies:

    npm install
    
  3. Configure environment (Optional):

    • Copy the example environment file:
      cp .env.example .env
      
    • Edit the .env file to set your desired configuration (see Configuration section).

Configuration

Configuration is managed via environment variables, which can be loaded from a .env file for local development.

  • LOG_LEVEL: The minimum level for logging (e.g., trace, debug, info, warn, error, fatal). (Default: info).
  • NODE_ENV: Set to production for optimized builds and standard JSON logs. Defaults to development (enables pretty logs).
  • CUSTOM_GREETING_PREFIX: Optional prefix for the greeting tool (e.g., "👋 "). Default is empty.

Running the Server

Development Mode (with Hot-Reload and Pretty Logs)

This command uses tsx to run the TypeScript code directly and pipes the output through pino-pretty.

npm run dev

Note: The dev script requires pino-pretty to be installed (npm install --save-dev pino-pretty).

  • Logs will be pretty-printed to the console.

Production Mode (Compiled JavaScript)

  1. Build the TypeScript code:

    npm run build
    

    This compiles the code into the dist/ directory.

  2. Run the server:

    # Example: Set custom greeting prefix with emoji
    set CUSTOM_GREETING_PREFIX=👋 
    set NODE_ENV=production
    npm start
    

    Logs will be in JSON format.

Running with Docker

  1. Build the Docker image:

    docker build -t mcp-server-boilerplate .
    
  2. Run the container:

    # Run with stdio transport 
    # (Needs an interactive terminal)
    docker run -i --rm --name mcp-server-stdio mcp-server-boilerplate
    
    • Adjust environment variables (-e) as needed.

Extending the Boilerplate

  1. Adding Tools:

    • Define a Zod schema for the arguments in src/index.ts (or a separate src/tools/ directory).
    • Use server.tool(name, schemaDefinition, handler).
    • Implement the handler logic, potentially using custom errors from src/errors.ts for consistency.
    • IMPORTANT: Register tools with simple names only (e.g., "add", not "mcp_minimal_add"). Cursor automatically applies prefixing for AI tool calls.
  2. Adding Resources:

    • For static resources: server.resource(name, uri, metadata, handler).
    • For dynamic resources: Define a ResourceTemplate and use server.resource(name, template, metadata, handler).
    • Implement the handler logic.
  3. Adding Prompts:

    • Define a Zod schema for the arguments.
    • Use server.prompt(name, schemaDefinition, handler).
    • Implement the handler logic to return the desired message structure.
  4. Adding Configuration:

    • Add new environment variables to .env.example.
    • Read and export the validated value from src/config.ts.

MCP Tool Invocation

When working with MCP tools, it's important to understand how they're invoked:

  1. AI Tool Invocation: MCP tools are designed to be called by the AI assistant, not directly by users. The AI can call tools directly using their prefixed name.

  2. Tool Naming:

    • Register tools with simple names (e.g., "add")
    • The AI will see tools with prefixed names (e.g., "mcp_mcp_minimal_add")
    • Do not register tools with prefixed names, as this creates duplication
  3. Logging Considerations:

    • When running in stdio mode, all logging must go to stderr, never stdout
    • Stdout is strictly reserved for JSON-RPC messages
    • Using console.log() will break the JSON-RPC protocol - use a custom logger that writes to stderr
  4. User Interface:

    • Users cannot directly invoke MCP tools in the chat interface
    • The AI assistant acts as the intermediary to invoke tools on behalf of the user

Common Issues

Based on our experience developing this boilerplate, here are some common issues to be aware of:

  1. JSON-RPC Protocol Issues:

    • Any non-JSON-RPC messages (like console.log output) sent to stdout will break the stdio transport mechanism. All debugging logs MUST be written to stderr instead.
    • Only valid JSON-RPC messages should be sent to stdout in stdio mode.
  2. Tool Name Prefixing:

    • Cursor automatically prefixes tool names for AI assistant calls. For example, if your server is named "mcp_minimal" and you register a tool named "add", the AI will call it as "mcp_mcp_minimal_add".
    • Avoid registering tools with both simple and prefixed names as this creates duplicate entries in the AI's available tools.
  3. MCP Tool Invocation:

    • MCP tools are designed to be called by the AI assistant, not directly by users in chat. Users cannot invoke MCP tools using "@Tool" syntax in the chat interface.
    • The AI automatically discovers registered tools and can call them without user configuration.

Client Configuration Examples

Here are examples of how to configure a client like Cursor to connect to this server.

Connecting via Stdio (Recommended for Local Development)

This method uses Cursor's mcp.json file (typically ~/.cursor/mcp.json on Linux/macOS or C:\Users\<username>\.cursor\mcp.json on Windows).

  1. Ensure you have run npm run build to create the dist/index.js file.

  2. Add a script to your package.json to explicitly run the compiled stdio server:

    "scripts": {
      // ... other scripts
      "start:stdio": "cross-env NODE_ENV=production node dist/index.js --stdio"
    }
    
  3. Install cross-env if you haven't already: npm install --save-dev cross-env.

  4. Configure the server in mcp.json (ensure no JSON comments // exist in the file):

    {
      "mcpServers": {
        "mcp_minimal": {
          "displayName": "MCP Minimal",
          "command": "node",
          "args": [
            "C:\\path\\to\\your\\project\\dist\\index.js",
            "--stdio"
          ],
          "env": {
            "NODE_ENV": "production"
          },
          "enabled": true
        }
      }
    }
    
    • Important: Replace "C:\\path\\to\\your\\project\\dist\\index.js" with the correct absolute path to the dist/index.js file in your project directory. Use double backslashes (\\) as path separators on Windows.
    • This configuration directly calls node with the absolute script path, bypassing potential issues with npm and relative paths when launched by Cursor.

Basic Security Considerations

  • Input Validation: Zod schemas are used for basic validation. Ensure schemas are strict enough for your needs.
  • Error Handling: Avoid leaking sensitive information in error messages sent back to the client. The current setup throws errors, which the SDK translates; review the SDK's error formatting.
  • Resource Access: Implement authorization checks within resource/tool handlers if they access sensitive data or perform restricted actions (currently not implemented).
  • Dependencies: Regularly audit dependencies for vulnerabilities (npm audit).
  • Container Security: The Dockerfile uses a non-root user. Keep the base image updated.

Testing

  • Run tests using Vitest:
    npm test
    
  • Run tests in watch mode:
    npm run test:watch
    
  • Generate coverage report:
    npm run coverage
    
  • Add new test files in the tests/ directory following the *.test.ts pattern.

Linting and Formatting

  • Check formatting: npm run format
  • Apply formatting: npm run format:fix
  • Run linter: npm run lint
  • Fix linter errors: npm run lint:fix

Limitations

MCP is a very new protocol and is still in active development. There are some known caveats to be aware of:

  • AI Model Compatibility: The ability for the Cursor AI agent to automatically use or be prompted to use custom MCP tools can be model-dependent. Some models may have difficulty initiating calls to dynamically discovered tools even if they are listed correctly in the settings. Testing with different AI models within Cursor may be necessary if you encounter issues with AI tool invocation.
  • Tool Quantity: Some MCP servers, or user's with many MCP servers active, may have many tools available for Cursor to use. Currently, Cursor will only send the first 40 tools to the Agent.
  • Remote Development: Cursor directly communicates with MCP servers from your local machine, either directly through stdio or via the network using sse. Therefore, MCP servers may not work properly when accessing Cursor over SSH or other development environments. We are hoping to improve this in future releases.
  • MCP Features Support: MCP servers offer three main capabilities: tools, resources, and prompts. Currently, Cursor only supports tools. Resources and prompts are defined in the protocol and implemented in this server, but are not yet accessible through Cursor. We are hoping to see support for these additional capabilities in future Cursor releases.

License

This project is licensed under the MIT License - see the LICENSE file for details.