Skip to main content
Glama

template-mcp

MCP (Model Context Protocol) server template with TypeScript, Zod validation, and dual transport support (stdio/HTTP). Compatible with any MCP client: Claude Code, Claude Desktop, Cursor, VS Code Copilot, Windsurf, Cline, and more.

Features

  • Dual transport: stdio (local) and Streamable HTTP (remote)

  • TypeScript strict with ESM modules

  • Zod validation for tool input schemas

  • Joi env validation (fail-fast on startup)

  • Pino logging to stderr (stdio-safe)

  • Modular architecture: tools, resources, and prompts as separate modules

  • Factory pattern: createServer() for testability

  • Full test suite with MCP SDK in-memory transport

  • Quality tooling: ESLint + Prettier + Husky + lint-staged

  • Docker ready: multi-stage build

  • CI/CD: GitHub Actions pipeline

Related MCP server: xmcp Application

Quick Start

pnpm install
pnpm dev

Scripts

Script

Description

pnpm dev

Start with hot reload (tsx watch)

pnpm build

Compile TypeScript + resolve aliases

pnpm start

Run compiled server

pnpm test

Run tests

pnpm lint

Lint source code

pnpm type-check

Type check without emit

Configuration

Copy .env.example to .env and adjust:

Variable

Default

Description

MCP_TRANSPORT

stdio

Transport: stdio or http

PORT

3000

HTTP port (only for http transport)

LOG_LEVEL

info

Pino log level

NODE_ENV

development

Environment

Project Structure

src/
├── main.ts              # Entrypoint: transport selection
├── server.ts            # createServer() factory
├── config/              # Env validation + constants
├── common/              # Logger, error helpers, types
├── tools/               # MCP tools (callable by LLMs)
├── resources/           # MCP resources (read-only data)
└── prompts/             # MCP prompts (reusable templates)

Adding a New Tool

  1. Create src/tools/my-tool.tool.ts:

import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';

export function registerMyTool(server: McpServer): void {
  server.registerTool(
    'my_tool',
    {
      title: 'My Tool',
      description: 'What this tool does',
      inputSchema: {
        param: z.string().describe('Parameter description'),
      },
      annotations: {
        readOnlyHint: true,
        destructiveHint: false,
        idempotentHint: true,
        openWorldHint: false,
      },
    },
    async ({ param }) => ({
      content: [{ type: 'text', text: `Result: ${param}` }],
    }),
  );
}
  1. Register in src/tools/index.ts:

import { registerMyTool } from './my-tool.tool.js';

export function registerTools(server: McpServer): void {
  registerGreetTool(server);
  registerMyTool(server); // add here
}
  1. Add tests in src/tools/__tests__/my-tool.tool.spec.ts

Client Configuration

Claude Code

Add to .claude/settings.json:

{
  "mcpServers": {
    "template-mcp": {
      "command": "node",
      "args": ["/absolute/path/to/template-mcp/dist/main.js"]
    }
  }
}

Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "template-mcp": {
      "command": "node",
      "args": ["/absolute/path/to/template-mcp/dist/main.js"]
    }
  }
}

Cursor

Add to Cursor Settings > MCP Servers:

{
  "mcpServers": {
    "template-mcp": {
      "command": "node",
      "args": ["/absolute/path/to/template-mcp/dist/main.js"]
    }
  }
}

VS Code (Copilot)

Add to .vscode/settings.json:

{
  "mcp": {
    "servers": {
      "template-mcp": {
        "command": "node",
        "args": ["/absolute/path/to/template-mcp/dist/main.js"]
      }
    }
  }
}

Docker

# Build
docker build -t template-mcp .

# Run (HTTP mode, used for remote access)
docker run -p 3000:3000 template-mcp

Tech Stack

  • Node.js 22 + TypeScript (strict, ESM)

  • MCP SDK v1 (@modelcontextprotocol/sdk)

  • Zod (tool input validation)

  • Joi (env validation)

  • Pino (stderr logging)

  • Vitest (testing)

  • ESLint + Prettier + Husky


Verificación

Todo lo siguiente está comprobado y funcionando al 100%.

Calidad de código

Check

Comando

Lint + formato

pnpm lint

Tipado estricto

pnpm type-check

Build (tsc + alias)

pnpm build

Tests unitarios (11/11)

pnpm test

Suite

Cubre

greet.tool.spec.ts (5)

Listado, estilos casual/formal/enthusiastic, rechazo de nombre vacío

server-info.resource.spec.ts (2)

Listado, campos JSON (name, version, uptime, timestamp)

summarize.prompt.spec.ts (4)

Listado, estilos brief/bullet-points, coerción numérica, defaults

Sin red ni puertos — usa InMemoryTransport del SDK.

Runtime — transporte stdio (modo por defecto)

echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}' \
  | MCP_TRANSPORT=stdio node dist/main.js

Respuesta JSON-RPC en stdout, logs en stderr.

Runtime — transporte HTTP

MCP_TRANSPORT=http PORT=3100 node dist/main.js &
# Initialize → capturar Mcp-Session-Id del header
# tools/list, resources/list, prompts/list, tools/call greet, resources/read info://server

Endpoint verificado

Resultado esperado

tools/call greet {"name":"Freddy","style":"casual"}

"Hey Freddy! How's it going?"

resources/read info://server

JSON con name, version, uptime, nodeVersion, timestamp

Docker

docker build -t template-mcp .  # multi-stage: base → deps → build → production
docker run -p 3000:3000 template-mcp  # arranca en HTTP mode

CI (GitHub Actions)

pnpm installpnpm lintpnpm buildpnpm test
Corre en cada push y PR a main/master.

Pipeline de commits (local)

git commit → husky → lint-staged → eslint --fix + prettier --write (solo archivos staged)

Gaps conocidos

  • HTTP transport sin tests automáticos (medio): los unit tests usan InMemoryTransport; el transporte HTTP (StreamableHTTPServerTransport) solo se verificó manualmente con curl. Para producción remota añadir tests de integración con sesión real

  • Integración con cliente MCP (medio): verificar manualmente agregando a .claude/settings.json o Cursor y confirmando que tools/resources/prompts aparecen en el cliente

  • Tool inputs extremos (bajo): strings muy largos, unicode malformado — Zod los rechaza pero el error response no está testeado vía HTTP

  • Sesiones concurrentes (bajo): fuera del scope de un template

Available Tools

1 tool
greetGreet UserA
Read-onlyIdempotent

Generates a personalized greeting message. Use this tool when you need to greet someone by name with an optional custom greeting style.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesThe name of the person to greet
styleNoThe greeting style to usecasual

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate readOnlyHint=true, destructiveHint=false, and idempotentHint=true, so the description does not need to repeat those. It adds the behavioral detail that the greeting is 'personalized' and that style is optional with a default value, which is sufficient given the high annotation coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, clear sentence that is front-loaded with the main action and immediately provides usage guidance. Every word is necessary and there is no redundant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (2 parameters, no output schema, no nested objects) and the rich annotations, the description is complete enough. It covers the purpose and usage adequately. No output schema is needed as the return is self-evident.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents both parameters thoroughly (name with length constraints, style with enum and default). The description adds no new parameter information beyond what's in the schema, earning a baseline score of 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states that the tool generates a personalized greeting message, with a specific verb ('greet') and resource ('someone by name'). It also mentions the optional custom greeting style, distinguishing it from any potential siblings (though none exist).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says 'Use this tool when you need to greet someone by name with an optional custom greeting style,' providing clear usage context. However, it does not specify when not to use it or mention any alternatives, but since there are no siblings, this is adequate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 1 tool updatev1.0.0
    • First observedgreet

TDQS

A3.8/5.0
Disambiguation5/5

Only one tool exists, so there is no ambiguity in choosing between tools.

Naming Consistency3/5

With only one tool, consistency is not applicable, but the name 'greet' is clear and follows a common verb pattern.

Tool Count2/5

A single tool for a server called 'template-mcp' suggests an extremely narrow scope, which is likely insufficient for meaningful tasks.

Completeness2/5

The server only offers a greeting function, which is too limited for any substantial workflow; missing any broader functionality.

Maintenance

ActivityInactive
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    A TypeScript-based template for rapidly developing MCP servers with modular tool architecture, built-in validation using Zod schemas, and comprehensive error handling.
    13
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    A Model Context Protocol (MCP) server template designed for building structured tools, prompts, and resources with built-in support for HTTP and STDIO transports. It provides a standardized framework for developers to create and deploy AI-driven services using TypeScript and Zod schema validation.
    10
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    A minimal TypeScript MCP server template with example tool, Zod validation, stdio transport, and dotenv setup.
    37
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Freddymhs/template-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server