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.
- 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
McpErrorsystem ensures consistent, structured error responses across the server. - Pluggable Authentication: Secure your server with zero-fuss support for
none,jwt, oroauthmodes. - Abstracted Storage: Swap storage backends (
in-memory,filesystem,Supabase,SurrealDB,Cloudflare D1/KV/R2) without changing business logic. Features secure opaque cursor pagination, parallel batch operations, and comprehensive validation. - Graph Database Operations: Optional graph service for relationship management, graph traversals, and pathfinding algorithms (SurrealDB provider).
- Full-Stack Observability: Get deep insights with structured logging (Pino) and optional, auto-instrumented OpenTelemetry for traces and metrics.
- Dependency Injection: Built with
tsyringefor a clean, decoupled, and testable architecture. - Service Integrations: Pluggable services for external APIs, including LLM providers (OpenRouter), text-to-speech (ElevenLabs), and graph operations (SurrealDB).
- Rich Built-in Utility Suite: Helpers for parsing (PDF, YAML, CSV, frontmatter), formatting (diffs, tables, trees, markdown), scheduling, security, and more.
- Edge-Ready: Write code once and run it seamlessly on your local machine or at the edge on Cloudflare Workers.
This template follows a modular, domain-driven architecture with clear separation of concerns:
┌─────────────────────────────────────────────────────────┐
│ MCP Client (Claude Code, ChatGPT, etc.) │
└────────────────────┬────────────────────────────────────┘
│ JSON-RPC 2.0
▼
┌─────────────────────────────────────────────────────────┐
│ MCP Server (Tools, Resources) │
│ 📖 [MCP Server Guide](src/mcp-server/) │
└────────────────────┬────────────────────────────────────┘
│ Dependency Injection
▼
┌─────────────────────────────────────────────────────────┐
│ Dependency Injection Container │
│ 📦 [Container Guide](src/container/) │
└────────────────────┬────────────────────────────────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Services │ │ Storage │ │ Utilities│
│ 🔌 [→] │ │ 💾 [→] │ │ 🛠️ [→] │
└──────────┘ └──────────┘ └──────────┘
[→]: src/services/ [→]: src/storage/ [→]: src/utils/
Key Modules:
- MCP Server - Tools, resources, prompts, and transport layer implementations
- Container - Dependency injection setup with tsyringe for clean architecture
- Services - External service integrations (LLM, Speech, Graph) with pluggable providers
- Storage - Abstracted persistence layer with multiple backend support
- Utilities - Cross-cutting concerns (logging, security, parsing, telemetry)
💡 Tip: Each module has its own comprehensive README with architecture diagrams, usage examples, and best practices. Click the links above to dive deeper!
This template includes working examples to get you started.
| Tool | Description |
|---|---|
template_echo_message |
Echoes a message back with optional formatting and repetition. |
template_cat_fact |
Fetches a random cat fact from an external API. |
template_madlibs_elicitation |
Demonstrates elicitation by asking for words to complete a story. |
template_code_review_sampling |
Uses the LLM service to perform a simulated code review. |
template_image_test |
Returns a test image as a base64-encoded data URI. |
| Resource | URI | Description |
|---|---|---|
echo |
echo://{message} |
A simple resource that echoes back a message. |
| Prompt | Description |
|---|---|
code-review |
A structured prompt for guiding an LLM to perform a code review. |
Add the following to your MCP Client configuration file (e.g., cline_mcp_settings.json).
{
"mcpServers": {
"mcp-ts-template": {
"type": "stdio",
"command": "bunx",
"args": ["mcp-ts-template@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"STORAGE_PROVIDER_TYPE": "filesystem",
"STORAGE_FILESYSTEM_PATH": "/path/to/your/storage"
}
}
}
}- Bun v1.2.21 or higher.
- Clone the repository:
git clone https://github.com/cyanheads/mcp-ts-template.git- Navigate into the directory:
cd mcp-ts-template- Install dependencies:
bun installAll 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_HTTP_HOST |
The hostname for the HTTP server. | 127.0.0.1 |
MCP_AUTH_MODE |
Authentication mode: none, jwt, or oauth. |
none |
MCP_AUTH_SECRET_KEY |
Required for jwt auth mode. A 32+ character secret. |
(none) |
OAUTH_ISSUER_URL |
Required for oauth auth mode. URL of the OIDC provider. |
(none) |
STORAGE_PROVIDER_TYPE |
Storage backend: in-memory, filesystem, supabase, surrealdb, cloudflare-d1, cloudflare-kv, cloudflare-r2. |
in-memory |
STORAGE_FILESYSTEM_PATH |
Required for filesystem storage. Path to the storage directory. |
(none) |
SUPABASE_URL |
Required for supabase storage. Your Supabase project URL. |
(none) |
SUPABASE_SERVICE_ROLE_KEY |
Required for supabase storage. Your Supabase service role key. |
(none) |
SURREALDB_URL |
Required for surrealdb storage. SurrealDB endpoint (e.g., wss://cloud.surrealdb.com/rpc). |
(none) |
SURREALDB_NAMESPACE |
Required for surrealdb storage. SurrealDB namespace. |
(none) |
SURREALDB_DATABASE |
Required for surrealdb storage. SurrealDB database name. |
(none) |
SURREALDB_USERNAME |
Optional for surrealdb storage. Database username for authentication. |
(none) |
SURREALDB_PASSWORD |
Optional for surrealdb storage. Database password for authentication. |
(none) |
OTEL_ENABLED |
Set to true to enable OpenTelemetry. |
false |
LOG_LEVEL |
The minimum level for logging (debug, info, warn, error). |
info |
OPENROUTER_API_KEY |
API key for OpenRouter LLM service. | (none) |
- Modes:
none(default),jwt(requiresMCP_AUTH_SECRET_KEY), oroauth(requiresOAUTH_ISSUER_URLandOAUTH_AUDIENCE). - Enforcement: Wrap your tool/resource
logicfunctions withwithToolAuth([...])orwithResourceAuth([...])to enforce scope checks. Scope checks are bypassed for developer convenience when auth mode isnone.
- Service: A DI-managed
StorageServiceprovides a consistent API for persistence. Never accessfsor other storage SDKs directly from tool logic. - Providers: The default is
in-memory. Node-only providers includefilesystem. Edge-compatible providers includesupabase,surrealdb,cloudflare-kv, andcloudflare-r2. - SurrealDB Setup: When using
surrealdbprovider, initialize the database schema usingdocs/surrealdb-schema.surqlbefore first use. - Multi-Tenancy: The
StorageServicerequirescontext.tenantId. This is automatically propagated from thetidclaim in a JWT when auth is enabled. - Advanced Features:
- Secure Pagination: Opaque cursors with tenant ID binding prevent cross-tenant attacks
- Batch Operations: Parallel execution for
getMany(),setMany(),deleteMany() - TTL Support: Time-to-live with proper expiration handling across all providers
- Comprehensive Validation: Centralized input validation for tenant IDs, keys, and options
- 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=trueand configure OTLP endpoints. Traces, metrics (duration, payload sizes), and errors are automatically captured for every tool call.
-
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 run test # Runs the test suite (Do not use 'bun test' directly as it may not work correctly)
- Build the Worker bundle:
bun build:worker- Run locally with Wrangler:
bun deploy:dev- Deploy to Cloudflare:
bun deploy:prodNote: The
wrangler.tomlfile is pre-configured to enablenodejs_compatfor best results.
| Directory | Purpose & Contents | Guide |
|---|---|---|
src/mcp-server/tools/definitions |
Your tool definitions (*.tool.ts). This is where you add new capabilities. |
📖 MCP Guide |
src/mcp-server/resources/definitions |
Your resource definitions (*.resource.ts). This is where you add new data sources. |
📖 MCP Guide |
src/mcp-server/transports |
Implementations for HTTP and STDIO transports, including auth middleware. | 📖 MCP Guide |
src/storage |
The StorageService abstraction and all storage provider implementations. |
💾 Storage Guide |
src/services |
Integrations with external services (e.g., the default OpenRouter LLM provider). | 🔌 Services Guide |
src/container |
Dependency injection container registrations and tokens. | 📦 Container Guide |
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. |
Each major module includes comprehensive documentation with architecture diagrams, usage examples, and best practices:
-
MCP Server Guide - Complete guide to building MCP tools and resources
- Creating tools with declarative definitions
- Resource development with URI templates
- Authentication and authorization
- Transport layer (HTTP/stdio) configuration
- SDK context and client interaction
- Response formatting and error handling
-
Container Guide - Dependency injection with tsyringe
- Understanding DI tokens and registration
- Service lifetimes (singleton, transient, instance)
- Constructor injection patterns
- Testing with mocked dependencies
- Adding new services to the container
-
Services Guide - External service integration patterns
- LLM provider integration (OpenRouter)
- Speech services (TTS/STT with ElevenLabs, Whisper)
- Graph database operations (SurrealDB)
- Creating custom service providers
- Health checks and error handling
-
Storage Guide - Abstracted persistence layer
- Storage provider implementations
- Multi-tenancy and tenant isolation
- Secure cursor-based pagination
- Batch operations and TTL support
- Provider-specific setup guides
- AGENTS.md - Strict development rules for AI agents
- CHANGELOG.md - Version history and breaking changes
- docs/tree.md - Complete visual directory structure
- docs/publishing-mcp-server-registry.md - Publishing guide for MCP Registry
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/catchin your tool/resourcelogic. Throw anMcpErrorinstead. - Use Elicitation for Missing Input: If a tool requires user input that wasn't provided, use the
elicitInputfunction from theSdkContextto ask the user for it. - Pass the Context: Always pass the
RequestContextobject through your call stack. - Use the Barrel Exports: Register new tools and resources only in the
index.tsbarrel files.
- Does this work with both STDIO and Streamable HTTP?
- Yes. Both transports are first-class citizens. Use
bun run dev:stdioorbun run dev:http.
- Yes. Both transports are first-class citizens. Use
- Can I deploy this to the edge?
- Yes. The template is designed for Cloudflare Workers. Run
bun run build:workerand deploy with Wrangler.
- Yes. The template is designed for Cloudflare Workers. Run
- Do I have to use OpenTelemetry?
- No, it is disabled by default. Enable it by setting
OTEL_ENABLED=truein your.envfile.
- No, it is disabled by default. Enable it by setting
- How do I publish my server to the MCP Registry?
- Follow the step-by-step guide in
docs/publishing-mcp-server-registry.md.
- Follow the step-by-step guide in
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 testThis project is licensed under the Apache 2.0 License. See the LICENSE file for details.