Package Exports
- mcp-ts-template
- mcp-ts-template/index.js
- mcp-ts-template/worker.js
Readme
mcp-ts-template
The definitive, production-grade template for building powerful and scalable Model Context Protocol (MCP) servers with TypeScript, featuring built-in observability (OpenTelemetry), declarative tooling, robust error handling, and a modular, DI-driven architecture.
mcp-ts-template
is more than just a template; it's a feature-rich, production-ready framework for building robust, observable, and secure MCP servers, providing a solid architectural foundation so you can focus entirely on creating powerful tools and resources for AI agents.
This project is designed to be AI-agent-friendly, providing an LLM-optimized AGENTS.md and detailed rules in .clinerules/clinerules.md to ensure your coding agents adhere to best practices from the start.
✨ Core Features
This template is packed with production-grade features designed for high-performance, secure, and maintainable MCP servers.
Feature | Description |
---|---|
Declarative Tooling | Define tools in a single, self-contained file (*.tool.ts ). The framework handles registration, validation, error handling, and performance metrics automatically. |
Full Observability | Zero-configuration OpenTelemetry integration. Get distributed traces and metrics out-of-the-box for all your tools and underlying dependencies (HTTP, DNS). |
Pluggable Auth | Built-in authentication middleware supporting JWT and OAuth 2.1. Easily toggle auth modes or extend with new strategies via the AuthStrategy interface. |
Stateful & Stateless Transports | Choose between stdio or HTTP transports. The HTTP transport supports both persistent, stateful sessions and ephemeral, stateless requests intelligently. |
Robust Error Handling | A centralized ErrorHandler maps all exceptions to standardized JsonRpcErrorCode s and automatically correlates them with OpenTelemetry traces for easy debugging. |
Type-Safe & Validated | Zod is used everywhere for rigorous schema validation of configuration, tool inputs/outputs, and API boundaries, preventing invalid data at the source. |
Abstracted Storage Layer | A flexible, provider-based storage service (IStorageProvider ) with ready-to-use backends for In-Memory, Filesystem, and Supabase. |
Comprehensive Utilities | A rich set of internal utilities for logging (Pino ), rate-limiting, security sanitization, ID generation, cron scheduling, and network requests. |
Integration-First Testing | Pre-configured with Vitest and msw for writing meaningful integration tests that reflect real-world usage, ensuring reliability from end to end. |
Agent-Ready Design | Includes detailed guidance in AGENTS.md and .clinerules/ to direct developer LLM agents, ensuring they adhere to the project's architectural standards. |
🚀 Getting Started
Prerequisites
- Bun (v1.2.0 or higher)
Installation
Clone the Repository
git clone https://github.com/cyanheads/mcp-ts-template.git cd mcp-ts-template
Install Dependencies
bun install
Build the Project
bun build # or bun rebuild
🏃 Running the Server
You can run the server in several modes for development and production.
Standard Transports
- STDIO Transport: Ideal for local development or when the server is a child process.
bun run start:stdio
- HTTP Transport: For network-accessible deployments.
bun run start:http # Server now running at http://127.0.0.1:3010
Cloudflare Workers Deployment
This template is optimized for deployment to Cloudflare Workers, a global, serverless execution environment.
Build the Worker:
bun run build:worker
Run Locally with Wrangler:
bun run deploy:dev
Deploy to Cloudflare:
bun run deploy:prod
🏗️ Architectural Principles
This template enforces a set of non-negotiable architectural principles to ensure every server built from it is robust, maintainable, and debuggable.
1. The "Logic Throws, Handler Catches" Pattern
This is the cornerstone of control flow and error handling. It creates a complete separation between pure business logic and the surrounding infrastructure concerns.
- Core Logic (
logic
): Defined within yourToolDefinition
, this is a pure, statelessasync
function. It contains only the business logic for the tool. If an operational or validation error occurs, it must terminate bythrow
ing a structuredMcpError
. It never contains atry...catch
block. - Handler (Auto-Generated): The
toolHandlerFactory
automatically wraps yourlogic
function in a robusttry...catch
block at runtime. This factory-generated handler is responsible for creating theRequestContext
, measuring performance with OpenTelemetry, invoking your logic, and catching any thrown errors. It is the only place where errors are caught and formatted into a finalCallToolResult
.
This pattern allows you to write clean, focused business logic while the framework guarantees it's executed within a fully instrumented, safe, and observable context.
2. Full-Stack Observability by Default
Every operation is traceable from end to end without any manual setup.
- OpenTelemetry SDK: Initialized in
src/utils/telemetry/instrumentation.ts
before any other module, it automatically instruments supported I/O operations (HTTP, DNS, etc.). - Trace-Aware Context: The
requestContextService
automatically injects the activetraceId
andspanId
into everyRequestContext
. - Error-Trace Correlation: The central
ErrorHandler
records every handled exception on the active OTel span and sets its status toERROR
, ensuring every failure is visible and searchable in your tracing backend. - Performance Spans: The
measureToolExecution
utility wraps every tool call in a dedicated span, capturing duration, status, and input/output sizes as attributes.
3. Declarative, Self-Contained Components
Tools and resources are defined declaratively in single, self-contained files. This makes the system highly modular and easy to reason about.
4. Dependency Injection for Maximum Decoupling
The entire architecture is built around a Dependency Injection (DI) container (tsyringe
).
- Centralized Container: All services, providers, and managers are registered in a central DI container, configured in
src/container/
. - Inversion of Control: Components never create their own dependencies. Instead, they receive them via constructor injection, making them highly testable and decoupled.
- Auto-Registration: Tool and resource definitions are automatically discovered and registered with the container from barrel exports, eliminating manual wiring.
📁 Project Structure
.
├── .clinerules/ # --> Rules and mandates for LLM-based development agents.
├── .github/ # --> GitHub Actions workflows (e.g., CI/CD).
├── scripts/ # --> Helper scripts for development (cleaning, docs, etc.).
├── src/
│ ├── config/ # --> Application configuration (Zod schemas, loader).
│ ├── container/ # --> Dependency Injection container setup and registrations.
│ ├── mcp-server/
│ │ ├── resources/ # --> Declarative resource definitions (*.resource.ts).
│ │ ├── tools/ # --> Declarative tool definitions (*.tool.ts).
│ │ ├── transports/ # --> HTTP and STDIO transport layers, including auth.
│ │ └── server.ts # --> Core McpServer setup (resolves components from DI).
│ ├── services/ # --> Clients for external services (e.g., LLM providers).
│ ├── storage/ # --> Abstracted storage layer and providers.
│ ├── types-global/ # --> Global TypeScript types (e.g., McpError).
│ └── utils/ # --> Core utilities (logger, error handler, security).
├── tests/ # --> Vitest integration and unit tests.
├── .env.example # --> Example environment variables.
├── AGENTS.md # --> Detailed architectural guide for LLM agents.
└── Dockerfile # --> For building and running the server in a container.
🔧 Extending the Template
Adding a New Tool
- Create the Definition: Create a new file at
src/mcp-server/tools/definitions/my-new-tool.tool.ts
. Use an existing tool as a template. - Define the Tool: Export a single
const
of typeToolDefinition
containing the name, Zod schemas, and pure business logic. - Register via Barrel Export: Open
src/mcp-server/tools/definitions/index.ts
and add your new tool definition to theallToolDefinitions
array.
// src/mcp-server/tools/definitions/index.ts
import { myNewTool } from './my-new-tool.tool.js';
// ... other imports
export const allToolDefinitions = [
// ... other tools
myNewTool,
];
That's it. The DI container automatically discovers and registers all tools from this array at startup.
Adding a New Storage Provider
- Create Provider: Create a new class under
src/storage/providers/
that implements theIStorageProvider
interface. - Add to Factory: Open
src/storage/core/storageFactory.ts
. Add a case to theswitch
statement to instantiate your new provider based on theSTORAGE_PROVIDER_TYPE
from the config. - Update Config Schema: Add your new provider's name to the
StorageProviderType
enum insrc/config/index.ts
. - Set Environment Variable: In your
.env
file, setSTORAGE_PROVIDER_TYPE
to your new provider's name.
⚙️ Configuration
The server is configured via environment variables, loaded and validated by src/config/index.ts
. Copy .env.example
to .env
and fill in the required values.
Variable | Description | Default |
---|---|---|
MCP_TRANSPORT_TYPE |
Transport to use: stdio or http . |
http |
MCP_SESSION_MODE |
HTTP session mode: stateless , stateful , or auto . |
auto |
MCP_AUTH_MODE |
Authentication mode: none , jwt , or oauth . |
none |
MCP_LOG_LEVEL |
Minimum log level: debug , info , warning , error , etc. |
debug |
LOGS_DIR |
Directory for log files. | logs/ |
STORAGE_PROVIDER_TYPE |
Storage backend: in-memory , filesystem , supabase . |
filesystem |
STORAGE_FILESYSTEM_PATH |
Path for the filesystem storage provider. | ./.storage |
OPENROUTER_API_KEY |
API key for the OpenRouter LLM service. | |
OTEL_ENABLED |
Set to true to enable OpenTelemetry. |
false |
MCP_AUTH_SECRET_KEY |
Secret key for signing JWTs (required for jwt auth mode). |
|
SUPABASE_URL |
URL for your Supabase project. | |
SUPABASE_SERVICE_ROLE_KEY |
Service role key for Supabase admin tasks. | |
Refer to .env.example
for a complete list of configurable options.
📜 Available Scripts
Key scripts available in package.json
:
Script | Description |
---|---|
bun run devdocs |
Generates a comprehensive development documentation prompt for AI analysis. |
bun run rebuild |
Clears logs, cache, and compiles the TypeScript source code to JavaScript in dist/ . |
bun run build:worker |
Builds the server specifically for the Cloudflare Workers runtime. |
bun run start:http |
Starts the compiled server using the HTTP transport. |
bun run start:stdio |
Starts the compiled server using the STDIO transport. |
bun run deploy:dev |
Runs the worker locally using the Wrangler CLI for development. |
bun run deploy:prod |
Deploys the worker to your Cloudflare account. |
bun run test |
Runs all unit and integration tests with Vitest. |
bun run test:coverage |
Runs all tests and generates a code coverage report. |
bun run devcheck |
A comprehensive script that runs linting, type-checking, and formatting. |
bun run publish-mcp |
(Recommended) An all-in-one script to sync, validate, commit, and publish your server to the MCP Registry. |
You can find these scripts in the scripts/
directory.
📦 Publishing to the MCP Registry
This template is configured for easy publishing to the public MCP Registry, making your server discoverable by any MCP-compatible client. The recommended method is to use the all-in-one publishing script.
For a complete walkthrough, including alternative methods and CI/CD automation, please refer to the detailed guide:
➡️ How to Publish Your MCP Server
🤝 Contributing
This is an open-source project. Contributions, issues, and feature requests are welcome. Please feel free to fork the repository, make changes, and open a pull request.
📄 License
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.