Skip to content

Latest commit

 

History

12 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cosmonic MCP examples

Five example Model Context Protocol (MCP) servers, each built as a WebAssembly component with mcp-server-template-rs and deployable to Cosmonic Desktop. They are worked examples of the MCP-server "factory": the same template, the same guardrails, five different shapes of server.

Every example:

  • speaks MCP 2026-07-28 (stateless streamable HTTP, header routing, structured output),
  • exports wasi:http/handler@0.3.0 (WASI p3),
  • ships a hermetic scripts/e2e.sh test suite (run in CI),
  • deploys to Cosmonic Desktop with a workload.yaml (local builds) and a deploy/workload.yaml (the published image).

The five examples

Example What it shows Tools
after-effects-mcp Driving a live desktop app via a polling bridge + wasi:keyvalue state create/animate compositions, layers, keyframes, expressions, masks, cameras, effects; render frames; plus pure-compute timecode/easing/colour helpers
premiere-mcp Pure-compute with tricky domain math SMPTE drop-frame timecode, offset, sequence duration, frame-rate conform
sec-edgar-mcp Outbound HTTP, no key, caching SEC EDGAR company facts & filing history (ticker→CIK, SEC_USER_AGENT)
cfpb-complaints-mcp Outbound HTTP against a large, quirky dataset — faceted search, aggregations, and correcting misleading upstream semantics CFPB consumer complaints: search, single record, facet ranking, time-series trends, company autocomplete
fred-mcp Outbound HTTP with an API key FRED economic-data search, series metadata & observations (FRED_API_KEY)

Together they cover the whole framework surface: pure compute, domain-math correctness, outbound APIs, caching, secrets, host state, controlling a local application a component cannot call directly, faceted search over millions of records, and the concurrency/robustness patterns the template enforces (bounded buffers, deadlines, panic-free numeric code, DNS-rebinding and SSRF guards).

The reusable test harness is scripts/mcp_e2e_lib.sh: each example's e2e.sh sources it for the framework-level checks (protocol, spec enforcement, robustness, Host guard) and adds its own tool cases.

Build & test any example

Prerequisites: Rust 1.90+ with the wasip2 target (rustup target add wasm32-wasip2); the e2e suite additionally needs wasm-tools, wasmtime ≥ 46 (test harness only), python3, and curl.

$ cd after-effects-mcp        # or any example
$ cargo build --release       # -> target/wasm32-wasip2/release/<name>.wasm
$ ./scripts/e2e.sh            # runs the hermetic test suite

cargo test is not used — the build target is wasm, so scripts/e2e.sh is the test entry point.

Running an example on Cosmonic Desktop

Deployment is via Cosmonic Desktop, which runs WASI p3 components natively and routes ingress by HTTP Host header. Each example ships two manifests: workload.yaml for a locally built/promoted image, and deploy/workload.yaml for the published image. The daemon's control socket is ~/Library/Application Support/Cosmonic/cosmonicd.sock.

If you just want to run one, skip to step 3 and apply deploy/workload.yaml: it references a public image on GHCR (ghcr.io/cosmonic-labs/mcp-examples/<name>:<version>), so nothing needs building. Steps 1 and 2 are for iterating on your own copy.

  1. Register the project directory (it has a .wash/config.yaml):

    $ SOCK="$HOME/Library/Application Support/Cosmonic/cosmonicd.sock"
    $ curl --unix-socket "$SOCK" -X POST http://localhost/v1/projects \
        -H 'Content-Type: application/json' \
        -d '{"path":"'"$PWD"'"}'
  2. Promote — build and push to the daemon's built-in registry. This returns a digest-pinned image reference; use it in the workload.

    $ curl --unix-socket "$SOCK" -X POST \
        http://localhost/v1/projects/<name>/promote \
        -H 'Content-Type: application/json' \
        -d '{"ref":"oci-registry.localhost:8200/<name>:0.1.0","insecure":true}'

    Note: at the time of writing the cosmonic_promote MCP tool sends the wrong field name (reference instead of ref); the socket call above is the reliable path.

  3. Apply the workload — workload.yaml with the digest-pinned image from promote, or deploy/workload.yaml for the published image — via the Cosmonic Desktop UI, the cosmonic_apply_workload MCP tool, or POST /v1/workloads. Key fields:

    spec:
      hostInterfaces:
        - namespace: wasi
          package: http
          interfaces: ["handler"]        # p3 handler — NOT incoming-handler
          config: { host: <name>.localhost }
      components:
        - name: mcp
          image: oci-registry.localhost:8200/<name>:0.1.0@sha256:…
          localResources:
            environment:
              config:
                MCP_ALLOWED_HOSTS: "<name>.localhost"   # matches the ingress host
            allowedHosts: [ … ]          # outbound allow-list (deny-all if empty)
  4. Call it through the ingress (routes by Host):

    $ curl -X POST http://<name>.localhost:8200/ \
        -H 'Content-Type: application/json' \
        -H 'Accept: application/json, text/event-stream' \
        -H 'MCP-Protocol-Version: 2026-07-28' \
        -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2026-07-28","capabilities":{},"clientInfo":{"name":"you","version":"0"}}}'

    A successful response is SSE-framed (data: {…"protocolVersion":"2026-07-28"…}). Tool calls add Mcp-Method: tools/call, Mcp-Name: <tool>, and a per-request _meta (see each example's README for a full call).

Deployment notes

  • Host header guard: the template accepts only localhost by default. On Desktop, set MCP_ALLOWED_HOSTS to the workload's ingress host, or requests are rejected with Forbidden.
  • Outbound allow-list: allowedHosts is deny-all when empty. List every upstream host a tool dials (e.g. www.sec.gov, data.sec.gov, api.stlouisfed.org).
  • Secrets: for API keys, register a Cosmonic secret and reference it with secretFrom rather than inlining the value — see fred-mcp.
  • Don't overwrite existing workloads: apply is idempotent by namespace/name, and the ingress host is global. Pick a fresh namespace/name/host if one is taken.

Releasing

Each example is released independently, from a tag named <example>/v<version> whose version matches that example's Cargo.toml:

$ git tag fred-mcp/v0.1.0
$ git push origin fred-mcp/v0.1.0

.github/workflows/release.yml then checks the manifests, builds the component, runs its e2e suite, and only on success pushes it to ghcr.io/cosmonic-labs/mcp-examples/<example> — as an OCI wasm artifact (application/wasm layer, pushed with wkg), not a container image — under both <version> and latest. It finishes by cutting a GitHub Release whose notes carry the digest-pinned reference. Use workflow_dispatch (with dry_run) to rehearse a release without publishing.

The version in the tag, in Cargo.toml, and in the image reference in deploy/workload.yaml must agree, or the release fails before pushing anything — this is what stops a manifest from pointing at an image that was never published. scripts/check-manifests.sh enforces that on every PR too, along with the rule that every ingress host appears in MCP_ALLOWED_HOSTS (miss that and the deployment answers nothing but Forbidden).

To bump an example: edit its Cargo.toml version, update app.kubernetes.io/version and the image tag in both manifests, run ./scripts/check-manifests.sh, merge, then tag.

.github/workflows/ci.yml runs every example's e2e suite, cargo fmt --check, and the manifest check on each push and PR.

Building your own

These examples are the reference implementations for the building-mcp-servers skill that ships in the template repo. Start from the template, follow the skill's phases (scaffold → tools → gates → e2e → deploy), and consult its living "Pitfalls" list — most of which was learned building exactly these four servers.

License

Apache-2.0

About

Example MCP servers built with mcp-server-template-rs for Cosmonic Desktop

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages