MCP TypeScript Template

MCP TypeScript Template

A production-grade TypeScript template for building Model Context Protocol (MCP) servers with declarative tools/resources, authentication, storage abstraction, and support for both local and edge deployment. Provides example echo tools and resources to demonstrate MCP server development patterns.

Category
访问服务器

README

<div align="center"> <h1>mcp-ts-template</h1> <p><b>Production-grade TypeScript template for building Model Context Protocol (MCP) servers. Ships with declarative tools/resources, robust error handling, DI, easy auth, optional OpenTelemetry, and first-class support for both local and edge (Cloudflare Workers) runtimes.</b></p> </div>

<div align="center">

Version MCP Spec MCP SDK License Status TypeScript Bun Code Coverage

</div>


✨ Features

  • Declarative Tools & Resources: Define capabilities in single, self-contained files. The framework handles registration and execution.
  • Elicitation Support: Tools can interactively prompt the user for missing parameters during execution, streamlining user workflows.
  • Robust Error Handling: A unified McpError system ensures consistent, structured error responses across the server.
  • Pluggable Authentication: Secure your server with zero-fuss support for none, jwt, or oauth modes.
  • Abstracted Storage: Swap storage backends (in-memory, filesystem, Supabase, Cloudflare KV/R2) without changing business logic.
  • Full-Stack Observability: Get deep insights with structured logging (Pino) and optional, auto-instrumented OpenTelemetry for traces and metrics.
  • Dependency Injection: Built with tsyringe for a clean, decoupled, and testable architecture.
  • Service Integrations: Pluggable services for external APIs, including LLM providers (OpenRouter) and text-to-speech (ElevenLabs).
  • Rich Built-in Utility Suite: Helpers for parsing (PDF, YAML, CSV), scheduling, security, and more.
  • Edge-Ready: Write code once and run it seamlessly on your local machine or at the edge on Cloudflare Workers.

🚀 Getting Started

MCP Client Settings/Configuration

Add the following to your MCP Client configuration file (e.g., cline_mcp_settings.json).

{
  "mcpServers": {
    "mcp-ts-template": {
      "command": "bunx",
      "args": ["mcp-ts-template@latest"],
      "env": {
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Prerequisites

Installation

  1. Clone the repository:
git clone https://github.com/cyanheads/mcp-ts-template.git
  1. Navigate into the directory:
cd mcp-ts-template
  1. Install dependencies:
bun install

🛠️ Understanding the Template: Tools & Resources

This template includes working examples of tools and resources.

1. Example Tool: template_echo_message

This tool echoes back a message with optional formatting. You can find the full source at src/mcp-server/tools/definitions/template-echo-message.tool.ts.

<details> <summary>Click to see the echoTool definition structure</summary>

// Located at: src/mcp-server/tools/definitions/template-echo-message.tool.ts
import { z } from 'zod';
import type {
  SdkContext,
  ToolDefinition,
} from '@/mcp-server/tools/utils/toolDefinition.js';
import { withToolAuth } from '@/mcp-server/transports/auth/lib/withAuth.js';
import { type RequestContext, logger } from '@/utils/index.js';

// 1. Define Input and Output Schemas with Zod for validation.
const InputSchema = z.object({
  message: z.string().min(1).describe('The message to echo back.'),
  mode: z
    .enum(['standard', 'uppercase', 'lowercase'])
    .default('standard')
    .describe('Formatting mode.'),
  repeat: z
    .number()
    .int()
    .min(1)
    .max(5)
    .default(1)
    .describe('Number of times to repeat the message.'),
});

const OutputSchema = z.object({
  repeatedMessage: z
    .string()
    .describe('The final, formatted, and repeated message.'),
  // ... other fields from the actual file
});

// 2. Implement the pure business logic for the tool.
async function echoToolLogic(
  input: z.infer<typeof InputSchema>,
  appContext: RequestContext,
  sdkContext: SdkContext,
): Promise<z.infer<typeof OutputSchema>> {
  // ... logic to format and repeat the message
  const formattedMessage = input.message.toUpperCase(); // simplified for example
  const repeatedMessage = Array(input.repeat).fill(formattedMessage).join(' ');
  return { repeatedMessage };
}

// 3. Assemble the final Tool Definition.
export const echoTool: ToolDefinition<typeof InputSchema, typeof OutputSchema> =
  {
    name: 'template_echo_message', // The official tool name
    title: 'Template Echo Message',
    description:
      'Echoes a message back with optional formatting and repetition.',
    inputSchema: InputSchema,
    outputSchema: OutputSchema,
    logic: withToolAuth(['tool:echo:read'], echoToolLogic), // Secure the tool
  };

The echoTool is registered in src/mcp-server/tools/definitions/index.ts, making it available to the server on startup. For an example of how to use the new elicitation feature, see template_madlibs_elicitation.tool.ts.

</details>

2. Example Resource: echo-resource

This resource provides a simple echo response via a URI. The source is located at src/mcp-server/resources/definitions/echo.resource.ts.

<details> <summary>Click to see the echoResourceDefinition structure</summary>

// Located at: src/mcp-server/resources/definitions/echo.resource.ts
import { z } from 'zod';
import type { ResourceDefinition } from '@/mcp-server/resources/utils/resourceDefinition.js';
import { withResourceAuth } from '@/mcp-server/transports/auth/lib/withAuth.js';
import { type RequestContext, logger } from '@/utils/index.js';

// 1. Define Parameter and Output Schemas.
const ParamsSchema = z.object({
  message: z.string().optional().describe('Message to echo from the URI.'),
});

const OutputSchema = z.object({
  message: z.string().describe('The echoed message.'),
  timestamp: z.string().datetime().describe('Timestamp of the response.'),
  requestUri: z.string().url().describe('The original request URI.'),
});

// 2. Implement the pure read logic for the resource.
function echoLogic(
  uri: URL,
  params: z.infer<typeof ParamsSchema>,
  context: RequestContext,
): z.infer<typeof OutputSchema> {
  const messageToEcho = params.message || uri.hostname || 'Default echo';
  return {
    message: messageToEcho,
    timestamp: new Date().toISOString(),
    requestUri: uri.href,
  };
}

// 3. Assemble the final Resource Definition.
export const echoResourceDefinition: ResourceDefinition<
  typeof ParamsSchema,
  typeof OutputSchema
> = {
  name: 'echo-resource', // The official resource name
  title: 'Echo Message Resource',
  description: 'A simple echo resource that returns a message.',
  uriTemplate: 'echo://{message}',
  paramsSchema: ParamsSchema,
  outputSchema: OutputSchema,
  logic: withResourceAuth(['resource:echo:read'], echoLogic), // Secure the resource
};

Like the tool, echoResourceDefinition is registered in src/mcp-server/resources/definitions/index.ts.

</details>

⚙️ Core Concepts

Configuration

All configuration is centralized and validated at startup in src/config/index.ts. Key environment variables in your .env file include:

Variable Description Default
MCP_TRANSPORT_TYPE The transport to use: stdio or http. http
MCP_HTTP_PORT The port for the HTTP server. 3010
MCP_AUTH_MODE Authentication mode: none, jwt, or oauth. none
STORAGE_PROVIDER_TYPE Storage backend: in-memory, filesystem, supabase, cloudflare-kv, r2. in-memory
OTEL_ENABLED Set to true to enable OpenTelemetry. false
LOG_LEVEL The minimum level for logging. info

Authentication & Authorization

  • Modes: none (default), jwt (requires MCP_AUTH_SECRET_KEY), or oauth (requires OAUTH_ISSUER_URL and OAUTH_AUDIENCE).
  • Enforcement: Wrap your tool/resource logic functions with withToolAuth([...]) or withResourceAuth([...]) to enforce scope checks. Scope checks are bypassed for developer convenience when auth mode is none.

Storage

  • Service: A DI-managed StorageService provides a consistent API for persistence. Never access fs or other storage SDKs directly from tool logic.
  • Providers: The default is in-memory. Node-only providers include filesystem. Edge-compatible providers include supabase, cloudflare-kv, and cloudflare-r2.
  • Multi-Tenancy: The StorageService requires context.tenantId. This is automatically propagated from the tid claim in a JWT when auth is enabled.

Observability

  • Structured Logging: Pino is integrated out-of-the-box. All logs are JSON and include the RequestContext.
  • OpenTelemetry: Disabled by default. Enable with OTEL_ENABLED=true and configure OTLP endpoints. Traces, metrics (duration, payload sizes), and errors are automatically captured for every tool call.

▶️ Running the Server

Local Development

  • Build and run the production version:

    # One-time build
    bun rebuild
    
    # Run the built server
    bun start:http
    # or
    bun start:stdio
    
  • Run checks and tests:

    bun devcheck # Lints, formats, type-checks, and more
    bun test # Runs the test suite
    

Cloudflare Workers

  1. Build the Worker bundle:
bun build:worker
  1. Run locally with Wrangler:
bun deploy:dev
  1. Deploy to Cloudflare: sh bun deploy:prod > Note: The wrangler.toml file is pre-configured to enable nodejs_compat for best results.

📂 Project Structure

Directory Purpose & Contents
src/mcp-server/tools/definitions Your tool definitions (*.tool.ts). This is where you add new capabilities.
src/mcp-server/resources/definitions Your resource definitions (*.resource.ts). This is where you add new data sources.
src/mcp-server/transports Implementations for HTTP and STDIO transports, including auth middleware.
src/storage The StorageService abstraction and all storage provider implementations.
src/services Integrations with external services (e.g., the default OpenRouter LLM provider).
src/container Dependency injection container registrations and tokens.
src/utils Core utilities for logging, error handling, performance, security, and telemetry.
src/config Environment variable parsing and validation with Zod.
tests/ Unit and integration tests, mirroring the src/ directory structure.

🧑‍💻 Agent Development Guide

For a strict set of rules when using this template with an AI agent, please refer to AGENTS.md. Key principles include:

  • Logic Throws, Handlers Catch: Never use try/catch in your tool/resource logic. Throw an McpError instead.
  • Use Elicitation for Missing Input: If a tool requires user input that wasn't provided, use the elicitInput function from the SdkContext to ask the user for it.
  • Pass the Context: Always pass the RequestContext object through your call stack.
  • Use the Barrel Exports: Register new tools and resources only in the index.ts barrel files.

❓ FAQ

  • Does this work with both STDIO and Streamable HTTP?
    • Yes. Both transports are first-class citizens. Use bun run dev:stdio or bun run dev:http.
  • Can I deploy this to the edge?
    • Yes. The template is designed for Cloudflare Workers. Run bun run build:worker and deploy with Wrangler.
  • Do I have to use OpenTelemetry?
    • No, it is disabled by default. Enable it by setting OTEL_ENABLED=true in your .env file.
  • How do I publish my server to the MCP Registry?
    • Follow the step-by-step guide in docs/publishing-mcp-server-registry.md.

🤝 Contributing

Issues and pull requests are welcome! If you plan to contribute, please run the local checks and tests before submitting your PR.

bun run devcheck
bun test

📜 License

This project is licensed under the Apache 2.0 License. See the LICENSE file for details.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选