TypeScript template for building Model Context Protocol (MCP) servers. Ships with declarative tools/resources, pluggable auth, multi-backend storage, OpenTelemetry observability, and support for both local and edge (Cloudflare Workers) runtimes.
Try it live — A public demo instance is running at
https://mcp-ts-template.caseyjhand.com/mcp. Connect any MCP client to test the template's tools and resources without installing anything.
- Define tools and resources in single, self-contained files. The framework handles registration.
- Tools can prompt users for missing parameters mid-execution via elicitation.
- Unified
McpErrorsystem for consistent, structured error responses. - Auth modes:
none,jwt, oroauth. Wrap logic withwithToolAuth/withResourceAuth. - Swap storage backends (
in-memory,filesystem,Supabase,Cloudflare D1/KV/R2) without changing tool logic. Includes cursor pagination, batch ops, and input validation. - Structured logging (Pino) with optional OpenTelemetry for tracing and metrics.
- Custom typed DI container with
Token<T>phantom branding. No external deps. - Pluggable service integrations: LLM (OpenRouter), TTS (ElevenLabs).
- Parsing helpers (PDF, YAML, CSV, frontmatter), formatting (diffs, tables, trees, markdown), scheduling, security.
- Runs on local (stdio/HTTP) and edge (Cloudflare Workers) with the same code.
Modular, domain-driven layout 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
- Container — Typed DI container, no external deps
- Services — External integrations (LLM, Speech, Graph) with pluggable providers
- Storage — Persistence layer with multiple backend support
- Utilities — Logging, security, parsing, telemetry
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. |
template_async_countdown |
Demonstrates MCP Tasks API with an async countdown timer (experimental). |
template_data_explorer |
Generates sample sales data with an interactive explorer UI (MCP Apps). |
| Resource | URI | Description |
|---|---|---|
echo |
echo://{message} |
A simple resource that echoes back a message. |
data-explorer-ui |
ui://template-data-explorer/app.html |
Interactive HTML app for the data explorer tool (MCP Apps). |
| 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.
{
"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"
}
}
}
}Or connect to the public demo server over HTTP — no installation required:
{
"mcpServers": {
"mcp-ts-template": {
"type": "streamable-http",
"url": "https://mcp-ts-template.caseyjhand.com/mcp"
}
}
}- Bun v1.2.0 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. |
stdio |
MCP_HTTP_PORT |
The port for the HTTP server. | 3010 |
MCP_HTTP_HOST |
The hostname for the HTTP server. | 127.0.0.1 |
MCP_LOG_LEVEL |
Logging level (debug, info, notice, warning, error, crit, alert, emerg). |
debug |
MCP_AUTH_MODE |
Authentication mode: none, jwt, or oauth. |
none |
MCP_AUTH_SECRET_KEY |
Required for jwt auth mode. A 32+ character secret. |
(none) |
DEV_MCP_AUTH_BYPASS |
Set to true to bypass JWT auth in development (requires no secret key). |
false |
OAUTH_ISSUER_URL |
Required for oauth auth mode. URL of the OIDC provider. |
(none) |
OAUTH_AUDIENCE |
Required for oauth auth mode. Expected audience claim in the JWT. |
(none) |
STORAGE_PROVIDER_TYPE |
Storage backend: in-memory, filesystem, supabase, cloudflare-d1, cloudflare-kv, cloudflare-r2. |
in-memory |
STORAGE_FILESYSTEM_PATH |
Path to the storage directory (for filesystem provider). |
./.storage |
SUPABASE_URL |
Required for supabase storage. Your Supabase project URL. |
(none) |
SUPABASE_ANON_KEY |
Required for supabase storage. Your Supabase anon key. |
(none) |
OTEL_ENABLED |
Set to true to enable OpenTelemetry. |
false |
OPENROUTER_API_KEY |
API key for OpenRouter LLM service. | (none) |
- Modes:
none(default),jwt(requiresMCP_AUTH_SECRET_KEY), oroauth(requiresOAUTH_ISSUER_URLandOAUTH_AUDIENCE). - In development, set
DEV_MCP_AUTH_BYPASS=trueto skip JWT validation without a secret key. Rejected in production. - Wrap tool/resource
logicwithwithToolAuth([...])orwithResourceAuth([...])for scope checks. Checks are bypassed when auth mode isnone.
- DI-managed
StorageServiceprovides a consistent API for persistence. Never accessfsor storage SDKs directly from tool logic. - Default provider is
in-memory. Node-only:filesystem. Edge-compatible:supabase,cloudflare-kv,cloudflare-r2. StorageServicerequirescontext.tenantId, auto-propagated from the JWTtidclaim when auth is enabled.- Opaque cursor pagination with tenant binding, parallel batch ops (
getMany/setMany/deleteMany), TTL support, centralized input validation.
- Pino for structured JSON logging. All logs include
RequestContext. - OpenTelemetry disabled by default. Enable with
OTEL_ENABLED=true. HTTP spans via@hono/otel(works on Bun). Tool-call metrics (duration, payload sizes, errors) captured automatically. Pino logs correlate to traces viatrace_id/span_id.
-
Build and run the production version:
# One-time build bun run rebuild # Run the built server bun run start:http # or bun run start:stdio
-
Run checks and tests:
bun run 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 run build:worker- Run locally with Wrangler:
bun run deploy:dev- Deploy to Cloudflare:
bun run deploy:prodNote: The
wrangler.tomlfile is pre-configured to enablenodejs_compatfor best results.
| Directory | Purpose & Contents | Guide |
|---|---|---|
src/mcp-server/tools/definitions |
Tool definitions (*.tool.ts). Add new capabilities here. |
MCP Guide |
src/mcp-server/resources/definitions |
Resource definitions (*.resource.ts). Add new data sources here. |
MCP Guide |
src/mcp-server/prompts/definitions |
Prompt definitions (*.prompt.ts). Add new prompt templates here. |
MCP Guide |
src/mcp-server/tasks |
Async task infrastructure (MCP Tasks API, experimental). | MCP Guide |
src/mcp-server/transports |
HTTP and STDIO transports, including auth middleware. | MCP Guide |
src/storage |
StorageService abstraction and provider implementations. |
Storage Guide |
src/services |
External service integrations (LLM, Speech, Graph) with pluggable providers. | Services Guide |
src/container |
DI 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 module directory has its own README with architecture details and examples.
- MCP Server Guide — Building tools, resources, auth, transports, SDK context, response formatting
- Container Guide — DI tokens, registration, service lifetimes, testing with mocks
- Services Guide — LLM (OpenRouter), Speech (ElevenLabs, Whisper), Graph, custom providers
- Storage Guide — Provider implementations, multi-tenancy, pagination, batch ops, TTL
- AGENTS.md — Development rules for AI agents
- CHANGELOG.md — Version history and breaking changes
- docs/tree.md — Visual directory structure
- docs/publishing-mcp-server-registry.md — Publishing to MCP Registry
See AGENTS.md for the full rules when using this template with an AI agent. Key principles:
- Never use
try/catchin tool/resourcelogic. ThrowMcpErrorinstead — handlers catch. - Use
elicitInputfromSdkContextto ask for missing user input. - Pass
RequestContextthrough the call stack. - Import from the defining file, not barrel
index.ts. Register new tools/resources indefinitions/index.ts.
- Does this work with both STDIO and Streamable HTTP? Yes. Use
bun run dev:stdioorbun run dev:http. - Can I deploy this to the edge? Yes. Run
bun run build:workerand deploy with Wrangler. - Do I have to use OpenTelemetry? No, disabled by default. Set
OTEL_ENABLED=trueto enable. - How do I publish my server to the MCP Registry? See
docs/publishing-mcp-server-registry.md.
Issues and pull requests are welcome. Run checks and tests before submitting:
bun run devcheck
bun run testThis project is licensed under the Apache 2.0 License. See the LICENSE file for details.