mcp-off-shelf
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-off-shelfadd 128 and 256, then show the current time"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
TypeScript MCP server template for WASI p3
The unmodified official @modelcontextprotocol/server 2.0.0 SDK runs in a
self-contained WebAssembly component. dist/mcp.wasm exports
wasi:http/handler@0.3.0 and imports wasi:http/client@0.3.0. HTTP is p3 in both
directions. The component uses JCO's Node compatibility adapters and a patched
QuickJS compiler. The MCP protocol core stays upstream. A small Rust p3 HTTP adapter handles
native request cancellation; there is no Hono dependency.
The server supports MCP 2026-07-28 and legacy clients. Its default tools are
add, echo, current_time, http_get, and http_request. It serves discovery,
health, and embedded skills, and includes policy guards, deadlines, structured
logs, optional native OTEL, and Cosmonic Desktop manifests.
Use this template
Create a repository from this GitHub template, clone it, and follow the build steps below. This is the TypeScript counterpart to the Rust template.
Customize the mcp-off-shelf application name consistently in package.json,
package-lock.json, the skill frontmatter and URIs, workload manifests,
.wash/config.yaml, and tests. Update the image registry, ingress hosts, and
MCP domain labels for your server. Implement tools in src/server.ts and
update the embedded playbook under skills/server/. Keep outbound destinations
explicit in allowedHosts.
Related MCP server: MCP Server TypeScript
Parity with the Rust template
Compared against cosmonic-labs/mcp-server-template-rs at
bbb0cc315c504a226eb23857d4bd6dcc3d0a5042:
Rust template feature | TypeScript implementation |
Stateless official SDK, current MCP revision | Official SDK 2.0.0; modern and legacy official-client tests |
Async p3 HTTP inbound and outbound | Both interfaces use 0.3.0; build rejects p2 HTTP imports |
| All included; |
Discovery and health | GET/HEAD |
Skills extension |
|
Immutable skill manifest | Embedded YAML frontmatter, SHA-256 digests, UTF-8 byte sizes, public one-hour cache hints |
DNS rebinding defense | Safe localhost default, configurable |
SSRF defense for client URLs | Local/private IP literals, IPv4-mapped IPv6, and localhost names denied; redirects checked too |
Incoming body cap | 4 MiB, enforced while reading p3 chunks |
Bounded outbound HTTP | Configurable full-exchange deadline and response cap, plus 4 MiB request cap |
Streaming SSE | SDK SSE mode, progress notifications, 15-second keepalives, bounded response inactivity, peer cancellation |
Concurrent native invocations | Tested overlapping outbound requests on one shared native instance, plus independent instances |
Structured request/tool logs | JSON on stderr, level filtering, W3C trace correlation and outbound propagation |
Optional host-joined native tracing | Separate |
Desktop integration |
|
Authorization guidance | docs/auth.md; both templates default to platform-boundary authorization and |
Automated verification | Native protocol/parity fixtures, actual-production p3 boundary tests, native OTEL checks, and CI workflow |
The remaining differences are runtime/toolchain and deployment validation: QuickJS needs the included compiler patch and Web API glue, the component still has standard non-HTTP p2 runtime imports, and live Cosmonic deployment and external DNS/TLS have not been tested in this sandbox. The optional OTEL build requires matching host WIT signatures, as does the Rust feature. This is feature parity for the tested template paths, not a conformance claim for every optional MCP feature or an assertion that the new CI workflow has run remotely.
Build
Prerequisites: Node.js 22.15+, npm, wasm-tools, and the patched QuickJS compiler.
Composition also requires Rust 1.96+, wasm32-wasip2, and wac 0.12.0.
Tests require Wasmtime 49.0.2.
Production compilation uses JCO 1.37.0's bundler and a standalone patched
componentize-qjs CLI. npm dependencies are locked.
npm ci
npm run build:compiler
npm run check
npm run build
npm run test:allbuild:compiler checks out upstream componentize-qjs revision
eee19a60b144321b2535a50cc58e05b73d9f5242, applies
patches/componentize-qjs.patch, and builds it.
This needs network access on first use. The compiler also downloads wasi-sdk 33
and Binaryen 130 unless cached or supplied through WASI_SDK_PATH and
WASM_OPT. The patch comes from the local sandboxed-agents/harnesses/pi
project; it supplies correctly typed async initialization stubs and real p2
HTTP resources during initialization. It does not add p2 HTTP imports here.
The upstream MCP SDK is unmodified.
The compiler selection order is COMPONENTIZE_QJS, gen/componentize-qjs,
then componentize-qjs on PATH. This workspace has a cached patched binary in
gen/, which is excluded from source control. The release compiler's
async-import initializer fails without the patch. JCO's default StarlingMonkey
backend also fails to compile this p3 handler.
npm run build:otel # also emits dist/mcp.otel.wasm
npm test # production component + composed Rust test driver
npm run test:outbound # outbound and parity fixtures, including streaming
npm run test:boundary # independent production p3 invocation
npm run serve # local Wasmtime verification listenerstat reports the exact size of dist/mcp.wasm; it is approximately 16.6 MB
uncompressed, including the engine, SDK, polyfills, and embedded skills. Snapshot
sizes vary slightly between builds. The optional OTEL component is separate.
Customize tools and skills
Edit src/server.ts using upstream McpServer, Zod, and tool
callbacks. Its registration table also supplies discovery tool names. The SDK
owns protocol validation, negotiation/discovery, routing, errors, schemas, and
SSE framing. Keep requests stateless; durable state belongs in a host capability
or an upstream service.
http_get performs one GET without following redirects, returning an HTTP status
line and up to 4096 UTF-8 bytes, with a truncation marker. http_request supports
methods, headers, text bodies, and follow/manual/error redirect policies. HTTP
404 and other non-2xx responses retain their status and body. Network, policy,
size, and timeout failures become tool errors. Cross-origin redirects remove
credentials. Progress is emitted when a caller supplies a progress token.
Write the operating playbook in skills/server/SKILL.md,
with supporting files under that directory. Build-time YAML parsing preserves
frontmatter in the manifest; name must equal package.json's name. Every UTF-8
file is embedded with its URI, digest, and byte size. No runtime filesystem
lookup is performed. Rename the frontmatter and package together when forking.
The server advertises io.modelcontextprotocol/skills with directoryRead: true.
Clients can use skills/list, skills/get, resources/directory/read, and
resources/read under skill://mcp-off-shelf. The index resource and server
instructions provide fallback discovery for clients without the extension.
Supporting files are in the manifest and directory listings; only the playbook
and index appear in resources/list. Unknown and traversal-shaped URIs fail.
Configuration and policy
Environment variable | Default | Meaning |
| localhost, 127.0.0.1, ::1 | Comma-separated incoming authorities; |
| unset |
|
| 30000 | Whole outbound exchange deadline, including body read |
| 4194304 | Buffered outbound response limit |
| 15000 | SSE keepalive interval |
| 60000 | Response-stream inactivity deadline, including a peer that stops reading |
| info | trace/debug/info/warn/error/off stderr threshold |
| info | Compatibility fallback for a single global log level |
The incoming protocol cap and outbound request cap are 4 MiB. Numeric settings
must be positive safe integers no greater than 2147483647. Invalid configuration
fails closed. RUST_LOG module-specific filter expressions are not interpreted;
use MCP_LOG_LEVEL for this TypeScript server.
The in-guest SSRF check covers literal addresses and localhost spellings. The
host must enforce DNS-resolved destinations and allowedHosts; a public-looking
hostname can resolve to a private address. Both supplied manifests explicitly
set allowedHosts: [], meaning deny-all. Add each required upstream there and in
.wash/config.yaml. Neither network policy nor the Host guard authenticates a
caller. See authorization guidance.
Discovery contains public server/tool/skill metadata, works under probe Host values, and does not read a request body. An ingress can gate it separately if that inventory is sensitive. No response enables cross-origin browser access.
Deploy on Cosmonic Desktop
Register this project using .wash/config.yaml, promote its
build, then put the returned digest-pinned image into workload.yaml
and apply through Desktop. Before applying, select an unused workload name,
namespace, and ingress host so an existing deployment is not replaced. The local
host is mcp-off-shelf.localhost; MCP_ALLOWED_HOSTS matches it.
For a public image, update the registry placeholder and host in
deploy/workload.yaml. Both manifests bind the p3
handler, advertise current MCP/skills/stateless/streamable-HTTP metadata, and
start with no outbound destinations. The same MCP catalog labels are declared
in .wash/config.yaml so dev workloads and promoted drafts carry them too.
The explicit manifests include desktop.cosmonic.com/source: mcp, matching
the Rust template. Use host secretFrom references for API
keys. The manifests describe deployment configuration; no workload is applied
by a build or test command.
Desktop dev builds use node scripts/build.mjs --desktop and serve
gen/desktop/mcp.wasm. All generated assets, WIT, Rust targets, and dev bundle
outputs live under gen/, which Desktop's watcher ignores. The watcher does
not read .gitignore; writing these files under generated/ or writing dev
bundles under dist/ causes continuous rebuilds. Release builds still emit
dist/mcp.wasm. Restart an existing dev session after this configuration change.
Run the local dev session in one terminal, then run the smoke test in another:
cosmonic dev --path .
npm run test:desktopThe smoke test connects to 127.0.0.1:8200 with the configured
mcp-off-shelf.localhost Host header, avoiding wildcard localhost DNS. Override
MCP_DESKTOP_URL and MCP_DESKTOP_HOST for another ingress address or binding.
It checks both MCP versions, tools, skills and hashes, concurrent calls, local
address rejection, and host-enforced denied egress to https://example.org/.
Keep that destination outside the workload's allowlist; change the probe with
MCP_DESKTOP_DENIED_URL if needed. A DNS or TLS failure does not count as a
successful policy-denial check.
To also verify live DNS/TLS and SSE progress, grant only example.com in this
project's workload.allowedHosts in .wash/config.yaml, restart its dev session,
and run:
MCP_DESKTOP_ALLOWED_URL=https://example.com/ npm run test:desktopRestore allowedHosts: [] after testing. The smoke script neither deploys nor
changes Desktop policy. Native I/O cancellation and OTEL parentage are covered
by the fixture suite; this smoke test does not verify Desktop cancellation or
collected OTLP spans. This sandbox denies both the Desktop Unix socket and
loopback ingress (EPERM), so the smoke test has not completed against Desktop.
CI
The CI workflow builds both component variants,
checks TypeScript, runs protocol/parity/native-boundary/OTEL tests, checks Rust
formatting, and lints the runtime adapter with Clippy. It caches npm downloads,
Cargo dependencies and all three Rust build targets, plus the pinned component
tools and patched compiler binary. Tool caches include OS, architecture, Rust
version, tool versions, and compiler patch/build-script hashes. Rust and tool
caches are saved only after successful pushes to main.
The zizmor workflow runs a blocking pedantic security audit. Actions are pinned to commit hashes, checkout credentials are not persisted, and workflows have read-only repository permissions. Locally:
zizmor --offline --pedantic --strict-collection .github/workflows
actionlint .github/workflows/*.yaml
npm run test:allThe workflows have passed local static checks; a clean hosted GitHub Actions run and cache restore remain to be verified after pushing this repository.
Observability
Request, tool, and outbound spans emit structured JSON to stderr. They carry
trace/span IDs, parent IDs, durations, and request method/path without bodies,
credentials, or query values. Valid W3C traceparent is accepted on ingress and
propagated on outbound requests unless a tool explicitly provides that header.
The platform owns collection and OTLP export.
npm run build:otel additionally emits a component using native
wasi:otel/tracing@0.2.0-rc.2. It creates request/tool/client spans under the
host's active context when no incoming parent is supplied. The native fixture
checks span delivery and parentage. Ordinary Wasmtime serving has no OTEL host
provider; use the standard artifact there. Hosts must match the vendored WIT
signatures, especially tracing.on-start; the same nominal rc.2 version has
had incompatible definitions in different host builds.
Verification and runtime notes
The test harness composes an independent Rust driver with the actual production artifact. The official TypeScript client drives its HTTP entry through native Wasmtime. Creating and consuming WASI resources on opposite component boundaries also exercises structured future payloads correctly. The test driver is separate from the Rust cancellation adapter included in the production component. Neither implements MCP.
Tests cover both MCP eras, skills manifests and directory traversal, discovery, Host policy, SSRF including redirects and mapped addresses, body limits, configurable timeouts and size caps, status codes, Unicode, streaming progress before completion, peer cancellation, response backpressure, and overlapping outbound calls on one native instance. The fixture uses p3 HTTP hooks with no p2 HTTP linker and no sockets. Native OTEL uses a separate composed artifact. CI installs the pinned compiler patch and runs the same checks.
src/runtime.ts and src/web.ts supply upstream Web Streams, AbortController, Fetch data classes, Buffer, URL, and secure UUIDs. src/timers.ts schedules SDK timers on p3 monotonic-clock waits; one shared short wait bounds retention after timers are cleared. src/future.ts handles QuickJS's explicit WIT future resources. The HTTP bridge buffers incoming messages within the cap and streams responses; outbound Fetch is buffered and policy checked. The composed HTTP adapter races each p3 exchange against an explicit cancellation signal, buffers the response within the limit, and drops native I/O when a deadline or disconnect cancels the operation. This is needed because QuickJS import promises cannot cancel their native subtask. The final component exposes only standard WASI host imports; the adapter's control interface is linked internally. JCO and ponyfill internal paths are pinned and need review during upgrades.
This is not a complete browser/Node runtime: automatic HTTP decompression and full Web Crypto are absent. Persistent subscriptions, sampling/elicitation, OAuth flows, and other optional MCP features beyond the Rust template's tested surface remain unverified. JCO-transpiled JavaScript hosts previously trapped on overlapping invocations; native Wasmtime shared-instance tests now pass. Live socket serving, external networking, and Desktop OTLP collection were not available for verification in this sandbox.
Sources
WASI p3 WIT comes from JCO 1.37.0. Optional OTEL and its p2 type dependencies
come from opentelemetry-wasi revision
2962a286aa66188314bcafd79c800c4e39ce41e1, matching the Rust template's feature.
This server cannot be deployed
Maintenance
Related MCP Connectors
- UnifAPIOAuthcom.unifapi
Hosted MCP server for live public-data APIs and Skills for AI agents.
Skills, agents and memory in the cloud, plus a gateway to your other MCP servers.
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Remote MCP server exposing SMI Aware tools, resources, and skills over Streamable HTTP.
Related MCP Servers
- -licenseBqualityNot gradedmaintenanceA boilerplate project for quickly developing Model Context Protocol (MCP) servers using TypeScript SDK. Includes example tools for calculations and greetings, plus system information resources.23-
- -licenseNot gradedqualityNot gradedmaintenanceA production-ready TypeScript MCP server providing basic tools (add, echo, timestamp), resources (server info, greetings, data access), and prompt templates (analyze, code-review, summarize). Serves as a foundation for building custom MCP servers with extensible architecture.237 npm-
- AlicenseBqualityDmaintenanceA boilerplate project for quickly developing Model Context Protocol (MCP) servers using TypeScript SDK. Includes example tools like calculator and greeting functions, plus system information resources.3ISC
- AlicenseBqualityDmaintenanceA demonstration TypeScript MCP server that showcases basic MCP concepts with simple tools (greeting, calculator), text resources, and prompt templates for learning the Model Context Protocol.2MIT