Skip to content

phughesmcr/deno-mcp-template

Repository files navigation

Deno MCP Server Template

GitHub Actions Workflow Status GitHub Actions Workflow Status GitHub Actions Workflow Status GitHub Actions Workflow Status

Typescript JSR JSR Score Repo Size License

Sponsor

Repo Logo - a happy dinosaur with a happy robot on its back walks in-front of a cliff-face with the letters M C P carved into it

The batteries-included starting point for building production MCP servers in TypeScript.

Clone, run setup, start building. Ships with STDIO + HTTP transports, security middleware, persistent state, sandboxed execution, CI/CD workflows, and every distribution format you need -- JSR package, native binary, DXT extension, or hosted on Deno Deploy.

Why This Template?

You get a working MCP server in under 2 minutes. Not a toy -- a real server with rate limiting, CORS, session management, TLS support, and structured logging already wired up.

Deno gives you superpowers that other runtimes don't. Built-in KV database (no Postgres/Redis to set up), native cron scheduling, sandboxed code execution in microVMs, compile-to-binary, and first-class Deploy hosting. This template puts all of them to work.

Ship anywhere, any way. One codebase produces:

  • a local STDIO server for Cursor, Claude Desktop, or any MCP client
  • a remote HTTP server with Streamable HTTP transport
  • a JSR package anyone can deno run without cloning
  • a standalone native binary (no runtime needed)
  • a DXT extension for one-click Claude Desktop install
  • a Deno Deploy app for managed cloud hosting

AI-agent friendly codebase. Small files (<200 LOC), feature-grouped structure, explicit types. Your coding agent can navigate and extend it without context overload.

Quick Start

# Install Deno (if needed)
curl -fsSL https://deno.land/install.sh | sh

# Create your project from template
gh repo create my-mcp-server --template phughesmcr/deno-mcp-template
cd my-mcp-server

# One-time setup -- renames placeholders, then self-destructs
deno task setup

# Start the server (STDIO + HTTP on localhost:3001)
deno task start

# Or launch with MCP Inspector for interactive testing
deno task dev

Connect from any MCP client:

{
  "mcpServers": {
    "my-mcp-server": {
      "command": "deno",
      "args": ["run", "-A", "/absolute/path/to/main.ts"]
    }
  }
}

That's it. You have a running MCP server with tools, resources, prompts, and task workflows.

JSR subpath imports

When this package is published to JSR, you can import the full program (main.ts) or narrower entrypoints:

  • jsr:@scope/name — same as main.ts (CLI entry).
  • jsr:@scope/name/mcpcreateMcpServer, mcpServerDefinition, context/subscription helpers, and related exports. The host must still open Deno KV (and any other shared services) before handling requests, same as createApp does.
  • jsr:@scope/name/appcreateApp for embedding the STDIO + HTTP host without copying main.ts.

See .cursor/rules/project.mdc (Architecture) for transport-scoped McpServer instances and shared context.

What's Included

Out-of-the-box infrastructure

What How Where
Dual transports STDIO + Streamable HTTP from a single app src/app/
HTTP middleware Rate limiting, CORS, optional bearer auth (not applied to /mcp-elicitation/* browser pages), security headers, timeouts, sessions src/app/http/hono.ts
Persistent state Deno KV -- zero-config locally, built-in on Deploy src/kv/
Session resumability KV-backed event store for stream recovery src/app/http/kvEventStore.ts
Background tasks MCP experimental tasks: KV TaskStore + TaskMessageQueue, plus listenQueue worker for the delayed-echo demo src/mcp/tasks/
Scheduled jobs Deno.cron for periodic maintenance src/app/cron.ts
Sandboxed execution @deno/sandbox microVMs for untrusted code src/mcp/tools/sandbox.ts
MCP Apps (interactive UI) Example tool + ui:// HTML bundle via @modelcontextprotocol/ext-apps mcp-ui/, src/mcp/apps/, static/mcp-apps/
CI/CD workflows GitHub Actions for CI, release, deploy, JSR publish .github/workflows/
Config management CLI flags + env vars with validation and merging src/app/cli.ts
Permission preflight Fail-fast startup checks with actionable guidance src/app/permissions.ts

Example MCP features (replace with your own)

Prompts: review-code, language-snippet

Resources: hello://world, greetings://{name}, counter://value (KV-backed, subscribable)

Tools: elicit-input, elicit-form-wizard (two-step form elicitation), url-elicitation-demo (URL-mode elicitation; streamable HTTP with a session only), fetch-website-info (text + optional MCP Apps UI in supporting clients), increment-counter, log-message, notify-list-changed, poem (sampling), execute-code (sandboxed)

Task workflows (experimental): delayed-echo, guided-poem (elicitation + sampling pipeline)

These cover prompts with arguments, static and dynamic resources, subscriptions, sampling, form and URL elicitation, notifications, list-changed events, KV persistence, sandboxed execution, async task patterns, and an MCP Apps UI example (fetch-website-info). Use them as reference, then swap in your domain logic.

Make It Yours

Run deno task setup first -- it rewrites package names, scopes, and metadata across the project in one pass.

Where to start editing

  1. src/shared/constants/ -- server name, description, version, defaults
  2. src/mcp/tools/ -- add your tools (follow existing patterns)
  3. src/mcp/apps/ -- register MCP App tools/resources (@modelcontextprotocol/ext-apps/server) when you add interactive UIs
  4. mcp-ui/ -- Vite bundle for MCP App HTML (run deno task build:mcp-ui; Deno installs npm deps and runs Vite — see mcp-ui/README.md)
  5. src/mcp/resources/ -- add your resources
  6. src/mcp/prompts/ -- add your prompts
  7. src/mcp/serverDefinition.ts -- feature lists, capability flags, and derived SERVER_CAPABILITIES (re-exported from src/shared/constants/mcp.ts)
  8. src/mcp/mod.ts -- server construction (registration follows the definition)
  9. src/app/http/hono.ts -- adjust CORS, middleware, routes

What to remove

Delete the example files you don't need from src/mcp/tools/, src/mcp/resources/, src/mcp/prompts/, and (if you drop MCP Apps) src/mcp/apps/ plus mcp-ui/. Update the corresponding mod.ts barrel exports and any registration in src/mcp/mod.ts. Done.

HTTP security

  • Authentication: Set MCP_HTTP_BEARER_TOKEN (or --http-bearer-token) so clients must send Authorization: Bearer … or x-api-key: … on /mcp. For CI or production templates, MCP_REQUIRE_HTTP_AUTH=true fails startup if no token is set. Paths under /mcp-elicitation/ intentionally skip bearer auth so normal browser tabs can open URL-mode elicitation pages without the MCP token.
  • Public URL for links: Behind a reverse proxy, set MCP_PUBLIC_BASE_URL (or --public-base-url) to the https:// origin users open in a browser so URL elicitation links match your deployment. If unset, the server derives a URL from the bind address (see src/shared/publicBaseUrl.ts).
  • Exposure: Binding to a non-loopback hostname without a token logs a warning: anyone who can reach the port can use MCP. Use a token or terminate TLS and auth at a reverse proxy.
  • All interfaces: Listening on 0.0.0.0 or :: requires --dnsRebinding plus --host / MCP_ALLOWED_HOSTS (validated at startup).
  • CORS: Wildcard * origins are not allowed; list explicit origins (e.g. MCP_ALLOWED_ORIGINS).
  • Rate limits: With MCP_TRUST_PROXY=true, limits follow proxy client IP headers (only safe behind a real proxy). Requests with no socket IP and no session use a lower cap (RATE_LIMIT_UNKNOWN_CLIENT in src/shared/constants/http.ts).
  • fetch-website-info: Only public HTTPS URLs are allowed by default (blocks private IPs, localhost, link-local, .internal, etc.). Redirects are followed manually with the same checks. Set MCP_DOMAIN_TOOL_ALLOW_HTTP=1 to allow http:// for demos. MCP Apps-capable hosts may load ui://deno-mcp-template/fetch-website-info.html for an inline UI; others still get JSON text in the tool result.

See .env.example for copy-paste variables.

Ship It

Choose your distribution

Goal Command Output
Run locally via STDIO deno task start Direct process
Serve over HTTP deno task start localhost:3001/mcp
Publish as JSR package deno publish jsr:@scope/package
Compile native binary deno task compile:all dist/server/
Package as DXT deno task dxt:all dist/server.dxt
Deploy to cloud deno deploy --prod Deno Deploy URL

Platform-specific compile/DXT tasks are also available: compile:win, compile:mac:arm64, compile:mac:x64, compile:linux:x64, compile:linux:arm64 (and matching dxt:* variants).

Compile tasks bundle the static/ directory, so binaries serve static routes without depending on the working directory.

DXT prerequisite: install the DXT CLI once with npm install -g @anthropic-ai/dxt. Update static/dxt-manifest.json before packaging.

JSR prerequisite: you need a JSR.io account. If you don't plan to publish on JSR, remove .github/workflows/publish.yml.

Client configuration examples

STDIO (Cursor, Claude Desktop, etc.):

{
  "mcpServers": {
    "my-mcp-server": {
      "command": "deno",
      "args": ["run", "-A", "/absolute/path/to/main.ts"]
    }
  }
}

HTTP with mcp-remote:

{
  "mcpServers": {
    "my-mcp-server": {
      "command": "npx",
      "args": ["mcp-remote", "http://localhost:3001/mcp"]
    }
  }
}

HTTP direct (clients with URL support):

{
  "mcpServers": {
    "my-mcp-server": {
      "url": "http://localhost:3001/mcp",
      "headers": { "origin": "http://localhost:3001" }
    }
  }
}

JSR package:

{
  "mcpServers": {
    "my-mcp-server": {
      "command": "deno",
      "args": ["run", "-A", "jsr:@your-scope/your-package"]
    }
  }
}

Compiled binary:

{
  "mcpServers": {
    "my-mcp-server": {
      "command": "/absolute/path/to/binary"
    }
  }
}

Claude Code:

claude mcp add my-mcp-server /absolute/path/to/binary
claude mcp add --transport http my-mcp-server http://localhost:3001/mcp

Deploy to Deno Deploy (optional)

# Create app (one-time)
deno deploy create \
  --org <YOUR_ORG> \
  --app <YOUR_APP> \
  --source local \
  --runtime-mode dynamic \
  --entrypoint main.ts \
  --build-timeout 5 \
  --build-memory-limit 1024 \
  --region us

# Deploy
deno deploy --prod

Set DENO_DEPLOY_TOKEN, DENO_DEPLOY_ORG, and DENO_DEPLOY_APP in GitHub Actions secrets for automatic deploys. Remove .github/workflows/deploy.yml if not using Deploy.

Production Checklist

  • Run deno task setup and verify all placeholder names are replaced
  • Remove or replace demo tools/resources/prompts you won't ship
  • Update static/.well-known/openapi.yaml and static/dxt-manifest.json
  • Configure MCP_ALLOWED_ORIGINS and MCP_ALLOWED_HOSTS for your environments
  • Replace src/app/http/kvEventStore.ts with a production-grade event store (the included one is a demo)
  • Switch from deno run -A to explicit permissions for production
  • Review --allow-net scope for any outbound tool calls
  • Set environment variables in CI and hosting provider
  • Run deno task ci (format, lint, type-check, test)

Configuration

Variable Flag Default Description
MCP_NO_HTTP --no-http false Disable HTTP server
MCP_NO_STDIO --no-stdio false Disable STDIO transport
MCP_HOSTNAME -n localhost HTTP listen hostname
MCP_PORT -p 3001 HTTP listen port
MCP_PUBLIC_BASE_URL --public-base-url Public http(s):// origin for browser links (URL elicitation); no trailing slash
MCP_TLS_CERT --tls-cert PEM certificate path (requires --tls-key)
MCP_TLS_KEY --tls-key PEM private key path (requires --tls-cert)
MCP_HEADERS -H Response headers (collection)
MCP_JSON_RESPONSE --json-response false JSON-only responses (disable SSE)
MCP_DNS_REBINDING --dnsRebinding false Enable transport-level Origin/Host checks (loopback binds already get Host validation)
MCP_ALLOWED_ORIGINS --origin Allowed CORS origins (collection)
MCP_ALLOWED_HOSTS --host Allowed hostnames (collection)
MCP_KV_PATH --kv-path Custom Deno KV database path
MCP_MAX_TASK_TTL_MS --max-task-ttl-ms 86400000 (24h) Max client-requested TTL (ms) for experimental MCP tasks; clamped in KvTaskStore (min 60s, max 1y — see src/shared/validation/config.ts)
DENO_DEPLOY_TOKEN Deploy token (required by execute-code sandbox tool)

CLI flags override env vars. Collection values (-H, --origin, --host) are merged from both sources.

Collection config examples

Collection-style values can be set as comma-separated strings in .env:

MCP_HEADERS=x-api-key:demo,cache-control:no-store
MCP_ALLOWED_ORIGINS=http://localhost:6274,https://app.example.com
MCP_ALLOWED_HOSTS=localhost,127.0.0.1,app.example.com

Or repeated CLI flags:

deno task start -- \
  -H "x-api-key:demo" \
  -H "cache-control:no-store" \
  --origin "http://localhost:6274" \
  --origin "https://app.example.com" \
  --host "localhost" \
  --host "app.example.com"

Use --origin for full origins (e.g. https://example.com) and --host for hostnames or IPs (e.g. example.com, 127.0.0.1).

Development

Command What it does
deno task start Start server
deno task dev Start with MCP Inspector + watch mode
deno task build:mcp-ui Build MCP App HTML into static/mcp-apps/ (Deno + mcp-ui/deno.lock; no Node.js)
deno task ci Runs build:mcp-ui, then format, lint, type-check, and test
deno task test:integration Run integration tests
deno task test:coverage Tests with coverage report
deno task bench Run benchmarks

deno task ci runs build:mcp-ui first, which uses Deno-only install + Vite under mcp-ui/ (see mcp-ui/README.md). CI only needs Deno for that step.

Runtime permissions

The app validates required permissions at startup and fails fast with actionable guidance. For production, prefer explicit permissions over -A:

# HTTP + STDIO (local defaults)
deno run --env-file=.env \
  --allow-env --allow-read --allow-write --allow-sys \
  --allow-net=localhost:3001 \
  main.ts

# STDIO only (no HTTP listener)
deno run --env-file=.env \
  --allow-env --allow-read --allow-write --allow-sys \
  main.ts --no-http

If you keep networked tools (such as fetch-website-info), include their destinations in --allow-net.

Lockfile and reproducibility

This template tracks deno.lock for deterministic dependency resolution.

  • Refresh lock data: deno install --entrypoint main.ts --frozen=false
  • Verify locked resolution (CI): deno install --frozen --entrypoint main.ts
  • Commit deno.lock with dependency changes.

Project Structure

main.ts                     # Entry point
src/
  app/                      # Runtime shell: transports, HTTP, KV, cron, signals
  mcp/                      # MCP server: tools, resources, prompts, tasks, apps
  shared/                   # Constants, types, validation, utilities
mcp-ui/                     # Vite + ext-apps front-end for MCP App HTML bundles
static/                     # Static files, OpenAPI spec, DXT manifest, mcp-apps/*.html
scripts/                    # Setup, build, and packaging helpers
test/                       # Integration tests and benchmarks
.github/workflows/          # CI, release, deploy, publish

For the annotated source tree, transport internals, and development caveats, see .cursor/rules/project.mdc.

Extras

Included quality-of-life files:

  • .cursor/rules/ -- Cursor agent rules for this project
  • .cursor/skills/ -- Agent skills for implementing tools, resources, and prompts (see below)
  • .cursorignore -- tells Cursor to exclude files in addition to .gitignore
  • .vscode/ -- recommended extensions, Deno as default formatter
  • .github/ -- CI/CD workflows, sponsors config, issue templates
  • CLAUDE.md -- optional Claude Code project context
  • CODE_OF_CONDUCT.md, CONTRIBUTING.md, etc. -- community management files for GitHub

Skills

The .cursor/skills/ directory contains agent skills that guide Cursor through implementing MCP features in this template. Each skill provides file templates, registration steps, type signatures, and working examples.

Skill What it covers
implementing-mcp-tools Standard tools, sampling, form and URL elicitation, resource-backed tools, notifications
implementing-mcp-resources Static resources, KV-backed resources, resource templates, subscriptions
implementing-mcp-prompts Prompts with static arguments or dynamic completions

These skills are picked up automatically by Cursor when relevant. Ask the agent to "add a new tool" or "create a resource" and it will follow the project's patterns.

References

Acknowledgements

If you use this template, please consider contributing fixes and features, starring the repo, and sponsoring.

This is not an official Deno project.

License

MIT -- this is a template, not a library. You're expected to modify it before shipping to production.

Sponsor this project

 

Contributors