deno-mcp-template

mcp
Guvenlik Denetimi
Gecti
Health Gecti
  • License — License: MIT
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Community trust — 34 GitHub stars
Code Gecti
  • Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Gecti
  • Permissions — No dangerous permissions requested

Bu listing icin henuz AI raporu yok.

SUMMARY

A template repo for writing and publishing local, remote, DXT, and binary MCP servers using Deno.

README.md

Deno MCP Server Template

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, MCP Bundle, 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, TLS support, and structured logging already wired up. It is stateless and speaks MCP protocol revision 2026-07-28, on @modelcontextprotocol/server 2.x, so any instance can answer any request. For clients that are not there yet, --legacy-stdio also serves 2025-era clients over STDIO.

Deno gives you superpowers that other runtimes don't. Built-in KV database (no Postgres/Redis to set up), 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 any 2026-07-28 MCP client, or 2025-era clients such as Claude Desktop with --legacy-stdio
  • a remote, stateless HTTP server
  • a JSR package anyone can deno run without cloning
  • a standalone native binary (no runtime needed)
  • an MCP Bundle (.mcpb) for one-click Claude Desktop install, which passes --legacy-stdio
  • 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 --private --clone
cd my-mcp-server

# One-time setup on a committed tree -- 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", "--no-http"]
    }
  }
}

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

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/mcp — createMcpServer, mcpServerDefinition, the factory context type, 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/app — createApp for embedding the STDIO + HTTP host without copying main.ts.

See AGENTS.md (Architecture) for the per-request McpServer instances and the shared factory context.

What's Included

Out-of-the-box infrastructure

What How Where
Dual transports STDIO (serveStdio) + stateless HTTP (createMcpHandler) from a single app src/app/
HTTP middleware Rate limiting, CORS, optional bearer auth (not applied to /mcp-elicitation/* browser pages), security headers, timeouts src/app/http/hono.ts
Persistent state Deno KV -- zero-config locally, attach a database on Deploy src/kv/
Multi-round tools inputRequired results with signed requestState, so no server-side conversation state src/mcp/requestState.ts
Change notifications subscriptions/listen streams fed by a ServerEventBus; KV watches publish resource updates src/app/app.ts, src/kv/watch.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, 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, updates via subscriptions/listen)

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

Conformance fixtures: the test_* tools, prompts and test:// resources exist for the MCP conformance suite. Delete them with the examples if you drop the suite from CI.

These cover prompts with arguments, static and dynamic resources, subscriptions, sampling, form and URL elicitation, notifications, list-changed events, KV persistence, sandboxed execution, and an MCP Apps UI example (fetch-website-info). Sampling and logging are deprecated by SEP-2577, still in the spec for at least twelve months. Use the examples 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 and flags
  8. src/mcp/mod.ts -- server construction (registration follows the definition)
  9. src/app/http/hono.ts -- adjust CORS, middleware, routes

What to remove

Run deno task strip on a committed tree, before adding your own features. It leaves one tool (echo), one resource (hello://world) and one prompt (review-code), and removes everything else in those folders, the MCP App and mcp-ui/, the conformance suite, their tests and their dependencies. deno task ci still passes afterwards. Add --dry-run to see the list first. It tells you what is left to do by hand, such as updating this README.

To keep some examples, delete what you don't need yourself: remove the files from src/mcp/tools/, src/mcp/resources/, src/mcp/prompts/ and src/mcp/apps/, then remove them from the mod.ts in the same folder.

HTTP security

  • Authentication: Set MCP_HTTP_BEARER_TOKEN (or --http-bearer-token) so clients must send Authorization: Bearer … 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).
  • Origin and CORS: /mcp answers 403 to any Origin not in MCP_ALLOWED_ORIGINS; requests with no Origin pass. Wildcard * origins are not allowed, so list explicit 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 use a lower cap (RATE_LIMIT_UNKNOWN_CLIENT in src/shared/constants/http.ts). Toggle the limiter with MCP_HTTP_RATE_LIMIT_ENABLED or --http-rate-limit / --no-http-rate-limit (default on). The MCP conformance script turns it off so long active-suite runs are not throttled with HTTP 429.
  • 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. DNS / SSRF depth: URL checks use the request URL string (and redirect targets), not post-resolution addresses; hostnames that resolve to private or unstable targets can still be risky in hostile networks—tighten with egress controls or disable the tool if that matters for your deployment (see src/shared/validation/safeToolFetchUrl.ts).

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 MCP Bundle deno task mcpb:all dist/server.mcpb
Deploy to cloud deno deploy --prod Deno Deploy URL

compile:all cleans dist/ once, then builds all five targets. Single-target tasks are also available: compile:win, compile:mac:arm64, compile:mac:x64, compile:linux:x64, compile:linux:arm64.

mcpb:all runs compile:all, then packs one bundle holding all five binaries. A launcher picks the right binary at startup, so there are no per-platform bundles. mcpb:pack repackages existing binaries and fails if any is missing.

The binaries workflow (.github/workflows/binaries.yml) smoke-tests each target on its own platform.

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

MCPB prerequisite: install the MCPB CLI once with npm install -g @anthropic-ai/mcpb. Update static/mcpb-manifest.json before packaging. The bundle is not signed, as mcpb sign produces a file Claude Desktop refuses.

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

Client compatibility: by default the server only accepts 2026-07-28 clients, and a 2025-era client gets an unsupported-version error. Claude Desktop still speaks 2025-11-25, so --legacy-stdio (MCP_LEGACY_STDIO=true) also serves 2025-era clients over STDIO, and the MCP Bundle passes it. HTTP always refuses them; to change that, edit legacy: "reject" in src/app/http/mod.ts.

On a 2025-era connection the tools, prompts, resources, logging and change notifications all work. The SDK turns each inputRequired result into a real elicitation, sampling or roots request, so handlers need no changes. The differences:

  • A feature fails with a capability error if the client did not declare what it needs: an isError result from a tool, a JSON-RPC error from a prompt or resource.
  • url-elicitation-demo needs the HTTP server, so it returns an error under --no-http. With HTTP on it asks again until the browser page is confirmed, then stops after eight rounds, as the SDK does not bridge the completion notification.
  • test_input_required_result_capabilities reads the 2026-07-28 request envelope, so it reports no capabilities.

Client configuration examples

STDIO (Cursor, Claude Desktop, etc.). --no-http stops each client's copy of the server binding the HTTP port, so two can run at once. Add --legacy-stdio for a 2025-era client such as Claude Desktop:

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

HTTP with mcp-remote:

This fails while the server rejects legacy clients: mcp-remote 0.16.0 sends 2025-11-25 and gets an unsupported-version error. Use it only if you change legacy: "reject" (see above).

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

HTTP direct (clients with URL support):

Claude Code 2.1.318 connects with claude mcp add --transport http my-mcp-server http://localhost:3001/mcp. Other clients must speak 2026-07-28.

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

JSR package:

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

Compiled binary:

{
  "mcpServers": {
    "my-mcp-server": {
      "command": "/absolute/path/to/binary",
      "args": ["--no-http"]
    }
  }
}

Claude Code:

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

Deploy to Deno Deploy (optional)

Set up a new app once, after logging in with deno deploy or setting DENO_DEPLOY_TOKEN:

deno task deploy:setup --org <YOUR_ORG> --app <YOUR_APP>

This creates the app, attaches a KV database, sets the variables below, and deploys to production. It prints a generated bearer token as soon as it's stored, so save it. Add --dry-run to see the commands first, --host <domain> for a custom domain, and --region to change from us. Without a terminal it needs --yes. If a step fails it lists the remaining commands. You can also rerun it: that keeps the app and replaces the variables, which rotates a generated token.

Setting Why Deploy needs it
KV database The server can't start without one, and it can only attach after deno deploy create has run.
MCP_DNS_REBINDING, MCP_ALLOWED_HOSTS Without them every request gets a 403 Invalid Host. *.<YOUR_ORG>.deno.net also covers preview URLs, whose hostnames change with each revision.
MCP_HTTP_BEARER_TOKEN The app is public, so /mcp needs a token.
MCP_REQUEST_STATE_KEY Multi-round calls must survive restarts and work across instances.
MCP_PUBLIC_BASE_URL URL elicitation links would otherwise point at localhost. Setup uses the first --host, or https://<YOUR_APP>.<YOUR_ORG>.deno.net.
MCP_HTTP_RATE_LIMIT_UNKNOWN_MAX The server sees no client IP on Deploy, so all clients share this limit. The default of 20 requests per 15 minutes is too low, so setup sets 1000. Requests to /mcp without a valid token don't count, but failed token guesses aren't rate limited.

For automatic deploys, set the DENO_DEPLOY_TOKEN secret and the DENO_DEPLOY_ORG and DENO_DEPLOY_APP variables in GitHub Actions. Remove .github/workflows/deploy.yml if not using Deploy.

deploy.yml deploys to production on pushes to main. A manual run makes a preview unless you tick prod. It first masks your org slugs in the log and checks the token can reach the org and app, and afterwards smoke-tests the deployed URL with scripts/smoke-deploy.ts. Add the deployed token as an MCP_HTTP_BEARER_TOKEN repository secret for a full check rather than stopping at the 401. If a run fails, it prints recent app logs, which are public on public repositories.

Production Checklist

  • Run deno task setup (it also resets the version to 0.1.0 and CHANGELOG.md) 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/mcpb-manifest.json
  • Configure MCP_ALLOWED_ORIGINS and MCP_ALLOWED_HOSTS for your environments
  • Set MCP_REQUEST_STATE_KEY (32+ bytes, same on every instance) so multi-round calls survive restarts and load balancing
  • Running more than one instance? InMemoryServerEventBus is per process: supply a shared ServerEventBus in src/app/app.ts so subscriptions/listen streams see every change
  • Scope the --allow-* flags in deno.json for your deployment (Deno security)
  • 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_LEGACY_STDIO --legacy-stdio false Also serve 2025-era clients over STDIO; HTTP always refuses them
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 Host checks for MCP_ALLOWED_HOSTS (loopback binds already get Host validation; the Origin check on /mcp is always on)
MCP_ALLOWED_ORIGINS --origin Allowed origins for CORS and the /mcp Origin check (collection)
MCP_ALLOWED_HOSTS --host Allowed hostnames (collection). *.example.com matches one subdomain label
MCP_KV_PATH --kv-path Custom Deno KV database path
MCP_REQUEST_STATE_KEY random per process Secret (32+ bytes) that signs requestState; env only, share it across instances
MCP_HTTP_BEARER_TOKEN --http-bearer-token Shared secret for /mcp; prefer the env var, as flags show up in process lists
MCP_REQUIRE_HTTP_AUTH --require-http-auth false Fail startup when no bearer token is set
MCP_TRUST_PROXY --trust-proxy false Trust CF-Connecting-IP, X-Forwarded-For and X-Real-IP for rate limiting
MCP_HTTP_RATE_LIMIT_ENABLED --http-rate-limit / --no-http-rate-limit true Enable per-IP HTTP rate limiting (MCP_HTTP_RATE_LIMIT_* for window and caps — see .env.example)
MCP_HTTP_RATE_LIMIT_WINDOW_MS --http-rate-limit-window-ms 900000 Rate limit window in milliseconds
MCP_HTTP_RATE_LIMIT_MAX --http-rate-limit-max 100 Requests per window for identified clients
MCP_HTTP_RATE_LIMIT_UNKNOWN_MAX --http-rate-limit-unknown-max 20 Requests per window for clients with no known IP
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-custom-header: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-custom-header: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 strip Remove the example features (--dry-run to preview)
deno task build:mcp-ui Check mcp-ui, then 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 conformance:server Run pinned @modelcontextprotocol/conformance against HTTP /mcp (requires Node.js for npx; see scripts/run-mcp-conformance.sh)
deno task conformance:list List scenarios for the same pinned conformance CLI

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.

MCP conformance

This template ships built-in showcase features named like the upstream reference server (test_* tools, test://… resources, test_* prompts) alongside the demo tools—see the individual modules in src/mcp/tools/test*.ts, src/mcp/resources/test*Resource.ts, and src/mcp/prompts/test*Prompt*.ts. deno task conformance:server runs the pinned @modelcontextprotocol/conformance CLI against the default HTTP server (rate limiting is disabled in the helper script to avoid HTTP 429 during long runs). A scenario such as tools-call-simple-text can still “pass” on non-reference tools if the response is plain text—even when isError is true—so use the conformance task or Inspector when validating behavior. The run reports some failed checks that do not fail it: they are in scenarios the 2026-07-28 requirement set does not score, such as tasks-*. Expected failures in scored scenarios live in scripts/conformance-baseline.yml (empty server: [] means CI fails on any scored failure). The script stops if its port (MCP_PORT, default 3001) is in use.

Runtime permissions

The app checks its permissions at startup and fails fast, naming the flag it needs. deno task start grants --allow-env --allow-net --allow-read --allow-write. Scope read and write to your paths for production:

deno run --env-file=.env \
  --allow-env --allow-net \
  --allow-read=static,/var/lib/my-server \
  --allow-write=/var/lib/my-server \
  main.ts

Set MCP_KV_PATH inside that folder, and add any TLS files to --allow-read. KV's default location needs neither flag. --allow-net stays unscoped while a tool declares runtime.requiresNet, because tools choose their destinations at run time. Without such tools, --allow-net=localhost:3001 is enough, and STDIO alone needs none.

Lockfile and reproducibility

This template tracks deno.lock for deterministic dependency resolution.

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

Project Structure

main.ts                     # Entry point
src/
  app/                      # Runtime shell: transports, HTTP, signals
  mcp/                      # MCP server: tools, resources, prompts, apps
  kv/                       # Deno KV runtime and watches
  shared/                   # Constants, types, validation, utilities
mcp-ui/                     # Vite + ext-apps front-end for MCP App HTML bundles
static/                     # Static files, OpenAPI spec, MCPB manifest, mcp-apps/*.html
scripts/                    # Setup, build, and packaging helpers
__test__                    # Tests
__bench__                   # Benchmarks
.github/workflows/          # CI, deploy, publish

For the annotated source tree, transport internals, and development caveats, see
AGENTS.md.

Extras

Included quality-of-life files:

  • AGENTS.md -- Agent rules for this project
  • .agents/skills/ -- Agent skills for implementing tools, resources, and prompts (see below)
  • .claude/skills -- Symlink to .agents/skills/, which is where Claude Code looks for them
  • .vscode/ -- recommended extensions, Deno as default formatter
  • .github/ -- CI/CD workflows, sponsors config, issue templates, contributing and security policies

Skills

The .agents/skills/ directory contains agent skills that guide coding agents 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 and Claude Code when relevant. On Windows, Claude Code needs Git symlinks enabled (core.symlinks). 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.

Yorumlar (0)

Sonuc bulunamadi