{"_id":"@bounded-systems/bounded-tools-mcp","name":"@bounded-systems/bounded-tools-mcp","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@bounded-systems/bounded-tools-mcp","version":"0.1.0","mcpName":"io.github.bounded-systems/bounded-tools-mcp","description":"Read-only MCP server over bounded.tools's signed static API. A thin implementation of @bounded-systems/static-mcp: the site's verbs + resource catalog + config, served as a Sigstore-verified static-response MCP server over stdio.","license":"MIT","author":{"name":"Bounded Systems"},"type":"module","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"bin":{"bounded-tools-mcp":"dist/index.js"},"publishConfig":{"access":"public","provenance":true},"engines":{"node":">=18.17"},"scripts":{"build":"tsc -p tsconfig.json","prepare":"npm run build","start":"node dist/index.js","typecheck":"tsc -p tsconfig.json --noEmit","test":"node --import tsx --test test/*.test.ts"},"dependencies":{"@bounded-systems/static-mcp":"npm:@jsr/bounded-systems__static-mcp@^0.1.0","@bounded-systems/verbspec":"npm:@jsr/bounded-systems__verbspec@^0.3.1","zod":"^4.4.3"},"devDependencies":{"@modelcontextprotocol/sdk":"^1.29.0","@types/node":"^20.14.0","tsx":"^4.16.0","typescript":"^5.5.0"},"keywords":["mcp","model-context-protocol","verbspec","sigstore","provenance","static-api","stdio"],"repository":{"type":"git","url":"git+https://github.com/bounded-systems/bounded-tools-mcp.git"},"homepage":"https://bounded.tools","gitHead":"44597ad928490a4807aac3ee0cb6e79fd930ab37","_id":"@bounded-systems/bounded-tools-mcp@0.1.0","bugs":{"url":"https://github.com/bounded-systems/bounded-tools-mcp/issues"},"_nodeVersion":"24.16.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-tYSe1GYhRTIQLbFEUEA5aDqU+Jnnynsw/8uAi12Oy0Tmq85iMsmyccVqGLyJnKW74pllC5TxPpwFm7ZVPrIBpQ==","shasum":"09c1ad5df80d0888cfc7317a4a672c941e316152","tarball":"https://registry.npmjs.org/@bounded-systems/bounded-tools-mcp/-/bounded-tools-mcp-0.1.0.tgz","fileCount":18,"unpackedSize":28795,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCnhPCHynjvSdrZgrGuGwZsa+Q88s75hBL2OW5uXI1F9QIgEMAL8KEHiUbdbojMRFATqkhW1zwABN8ITNsAyVLHchs="}]},"_npmUser":{"name":"bdelanghe","email":"bdelanghe@gmail.com"},"directories":{},"maintainers":[{"name":"bdelanghe","email":"bdelanghe@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/bounded-tools-mcp_0.1.0_1782710370835_0.344616755574906"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-29T05:19:30.704Z","0.1.0":"2026-06-29T05:19:30.967Z","modified":"2026-06-29T05:19:31.172Z"},"maintainers":[{"name":"bdelanghe","email":"bdelanghe@gmail.com"}],"description":"Read-only MCP server over bounded.tools's signed static API. A thin implementation of @bounded-systems/static-mcp: the site's verbs + resource catalog + config, served as a Sigstore-verified static-response MCP server over stdio.","homepage":"https://bounded.tools","keywords":["mcp","model-context-protocol","verbspec","sigstore","provenance","static-api","stdio"],"repository":{"type":"git","url":"git+https://github.com/bounded-systems/bounded-tools-mcp.git"},"author":{"name":"Bounded Systems"},"bugs":{"url":"https://github.com/bounded-systems/bounded-tools-mcp/issues"},"license":"MIT","readme":"# @bounded-systems/bounded-tools-mcp\n\nA **local, read-only [MCP](https://modelcontextprotocol.io) server** (and a\nmatching CLI) over [bounded.tools](https://bounded.tools)' **signed static API**.\n\nIt exposes the parts of the site that are served as **verifiable, content-addressed\nJSON** — the Web-Build Conformance report and the SPDX SBOM — to any MCP client\n(Claude Desktop, Claude Code, etc.), and **verifies every response byte-for-byte\nagainst the site's Sigstore-signed `sha256` manifest** before handing it back. If\nthe bytes a client would receive don't match the signed manifest, it refuses to\nreturn them.\n\nIt runs **locally over stdio** — the client spawns it as a subprocess. There is\nno hosted server and no network listener, which preserves the site's\nstatic / no-attack-surface posture.\n\n## A thin implementation of a generic core\n\nThis package is **thin**. All of the reusable machinery — the verifying fetch\nclient, the `sha256` manifest + Sigstore checks, and the\n`VerbSpec → MCP (tools + resources)` / `VerbSpec → CLI` projection — lives in\n[`@bounded-systems/static-mcp`](https://github.com/bounded-systems/static-mcp).\nbounded-tools-mcp supplies only:\n\n- **the verbs** ([`src/verbs.ts`](./src/verbs.ts)) — `get_conformance`,\n  `get_sbom`, each authored once as a\n  [`@bounded-systems/verbspec`](https://jsr.io/@bounded-systems/verbspec) `VerbSpec`;\n- **the resource catalog** ([`src/catalog.ts`](./src/catalog.ts)) — the\n  `tools://…` resources;\n- **the config values** ([`src/config.ts`](./src/config.ts)) — the origin and\n  expected signer identity; and\n- **the entry** ([`src/index.ts`](./src/index.ts)) — which picks a surface and\n  hands the spec to the core.\n\n```\nsrc/verbs.ts ─┐\nsrc/catalog.ts ├─▶ buildToolsSpec(config) ─▶ @bounded-systems/static-mcp\nsrc/config.ts ─┘        serveVerifiedStaticMcp(spec, config)   (MCP, stdio)\n                        runStaticCli(spec, config, argv)        (CLI)\n```\n\n> **Two surfaces, one definition.** verbspec projects each verb to **both** an\n> MCP tool and a CLI subcommand. The exact same verb set backs the MCP tools and\n> the CLI commands — no second definition, no drift.\n\n## What's exposed (and what isn't)\n\nbounded.tools is a static site whose **verifiable surface is intentionally\nsmall**. Only artifacts the site serves as **signed, content-addressed JSON** are\nexposed here — each is fetched and verified byte-for-byte against the signed\nmanifest:\n\n| Served as | Artifact | Exposed |\n| --------- | -------- | ------- |\n| **signed JSON** | `api/v1/conformance.json` | ✅ `get_conformance` · `tools://conformance` |\n| **signed JSON** | `sbom.spdx.json` | ✅ `get_sbom` · `tools://sbom` |\n| **signed JSON** | `site.webmanifest` | ✅ `tools://webmanifest` (resource only) |\n| signed **HTML + Markdown** | the blog (`blog/*.html`, `blog/*.md`) | ❌ not JSON — see below |\n\n> **Honesty over surface area.** The blog *is* covered by the signed manifest,\n> but it is served as HTML + Markdown, not as a JSON feed or per-post JSON\n> documents. The verified-static core fetches-and-JSON-parses each artifact, so\n> exposing the blog as a \"tool\" would mean fabricating a JSON shape the site does\n> not actually serve. It is therefore left out rather than faked. If bounded.tools\n> later publishes a signed `posts.json` (and per-post JSON), `list_posts` /\n> `get_post` verbs drop straight in, exactly as in\n> [`site-mcp`](https://github.com/bounded-systems/site-mcp).\n\n## Install / run\n\nRequires Node ≥ 18.17. The verbspec dependency is published to JSR, so installs\nresolve it through JSR's npm bridge — the included [`.npmrc`](./.npmrc) sets\n`@jsr:registry=https://npm.jsr.io`. (Consuming from a fresh environment, add that\none line to your npm config.)\n\n```bash\n# MCP server over stdio (what an MCP client launches):\nnpx -y @bounded-systems/bounded-tools-mcp\n\n# CLI — the SAME verbs, printing the verified JSON:\nnpx -y @bounded-systems/bounded-tools-mcp get_conformance\nnpx -y @bounded-systems/bounded-tools-mcp get_sbom\n```\n\nThe MCP server logs a readiness line to **stderr** (stdout is the MCP channel):\n\n```\nbounded-tools-mcp ready (stdio) → https://bounded.tools; signature mode=off\n```\n\n## MCP client configuration\n\n```json\n{\n  \"mcpServers\": {\n    \"bounded-tools\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@bounded-systems/bounded-tools-mcp\"],\n      \"env\": { \"BOUNDED_TOOLS_MCP_SIGNATURE_MODE\": \"warn\" }\n    }\n  }\n}\n```\n\n## Resources\n\n| Resource URI            | Endpoint                  | Contents |\n| ----------------------- | ------------------------- | -------- |\n| `tools://conformance`   | `api/v1/conformance.json` | Web-Build Conformance Standard report (HTML / WCAG 2.2 / ARIA) |\n| `tools://sbom`          | `sbom.spdx.json`          | SPDX software bill of materials |\n| `tools://webmanifest`   | `site.webmanifest`        | W3C web app manifest (PWA site metadata) |\n\n## Tools / CLI commands (read-only)\n\nThe same two verbs, on both surfaces:\n\n| Tool / command    | Args | Returns |\n| ----------------- | ---- | ------- |\n| `get_conformance` | —    | The Web-Build Conformance report |\n| `get_sbom`        | —    | The SPDX software bill of materials |\n\nResource reads and tool results carry a `_meta.verification` block (the\nmanifest-relative path, source URL, the verified `sha256`, and the manifest\nsignature status). The CLI prints the verified JSON; a verification failure exits\nnon-zero with nothing on stdout.\n\n## Verification / trust model\n\nThe site publishes a single signed manifest, `https://bounded.tools/site.sha256`\n(`sha256sum` format), and a Sigstore bundle over it, `site.sha256.sigstore.json`.\nThe core enforces:\n\n1. **Per-file hash check (always on).** Fetch the manifest once per process; for\n   every resource, fetch it, SHA-256 the received bytes, and require that digest\n   to equal the manifest entry. A tampered file, a stale CDN edge, or a MITM →\n   mismatch → `VerificationError` instead of a response. A path absent from the\n   manifest is likewise refused.\n2. **Manifest signature check (optional).** `BOUNDED_TOOLS_MCP_SIGNATURE_MODE=warn|require`\n   verifies the Sigstore bundle against the deploy workflow identity\n   (`…/bounded-systems/site/.github/workflows/deploy.yml@refs/heads/main`, OIDC\n   issuer `https://token.actions.githubusercontent.com`). This is the same\n   keyless identity published in\n   [`https://bounded.tools/provenance.json`](https://bounded.tools/provenance.json).\n\n## Configuration\n\n| Variable                            | Default | Meaning |\n| ----------------------------------- | ------- | ------- |\n| `BOUNDED_TOOLS_MCP_BASE_URL`        | `https://bounded.tools` | Origin serving the site + API + manifest |\n| `BOUNDED_TOOLS_MCP_SIGNATURE_MODE`  | `off`   | `off` \\| `warn` \\| `require` |\n| `BOUNDED_TOOLS_MCP_SIGNER_IDENTITY` | deploy workflow SAN | Expected Sigstore certificate identity |\n| `BOUNDED_TOOLS_MCP_SIGNER_ISSUER`   | GitHub Actions OIDC | Expected Sigstore OIDC issuer |\n| `BOUNDED_TOOLS_MCP_FETCH_TIMEOUT_MS`| `15000` | Per-request fetch timeout |\n\n## Development\n\n```bash\nnpm install         # resolves @bounded-systems/static-mcp (npm) + verbspec (JSR bridge)\nnpm run build       # tsc → dist/\nnpm test            # node --test via tsx (server + CLI; no network)\nnpm run typecheck\nnode scripts/headless-check.mjs   # live end-to-end against bounded.tools\n```\n\n## Publishing\n\n**One tag publishes the same version to three registries, mirrored.** Pushing a\n`v*` tag runs [`publish.yml`](./.github/workflows/publish.yml), which fans out to:\n\n| # | Registry | Identifier | Auth |\n| - | -------- | ---------- | ---- |\n| 1 | **npm** | `@bounded-systems/bounded-tools-mcp` | trusted publishing (OIDC) + [provenance](https://docs.npmjs.com/generating-provenance-statements) |\n| 2 | **JSR** (mirror) | `@bounded-systems/bounded-tools-mcp` | tokenless OIDC (`npx jsr publish`) |\n| 3 | **MCP Registry** | `io.github.bounded-systems/bounded-tools-mcp` | GitHub-OIDC namespace auth (`mcp-publisher`) |\n\nThere are **no long-lived secrets** — every registry authenticates with the\njob's short-lived GitHub Actions OIDC token (`id-token: write`). npm needs\nnpm ≥ 11.5 (the workflow upgrades npm to guarantee this). The `mcp-registry` job\nis **decoupled from the `npm` job** (`needs: verify`, NOT `needs: npm`): the\nregistry proves package ownership by reading the `mcpName` field off the\nalready-published npm package, so it can run/retry independently.\n\n> [!IMPORTANT]\n> **Versions must stay in sync.** The release version lives in **four** places\n> that must all match: `package.json`, `deno.json`, `server.json`, and the\n> `v<version>` git tag. The workflow's `verify` job hard-fails the whole release\n> on any mismatch, so npm and JSR can never drift apart. The MCP Registry also\n> requires `package.json` to carry\n> `\"mcpName\": \"io.github.bounded-systems/bounded-tools-mcp\"` (it reads that field\n> off the published npm package to prove ownership).\n\n### One-time setup (maintainer) — do these BEFORE the first tag\n\n**(a) npm — Trusted Publisher** (on [npmjs.com](https://www.npmjs.com/))\n\n1. Sign in as an owner of the `@bounded-systems` scope.\n2. Open the package page for **`@bounded-systems/bounded-tools-mcp`** →\n   **Settings** → **Trusted Publisher**. For a brand-new package you may need to\n   publish `0.1.0` once manually (or create the package), then switch to trusted\n   publishing.\n3. Choose **GitHub Actions** and enter:\n   - **Organization / user:** `bounded-systems`\n   - **Repository:** `bounded-tools-mcp`\n   - **Workflow filename:** `publish.yml`\n   - **Environment:** *(leave blank)*\n4. Save. No token is generated or stored anywhere.\n\n**(b) JSR — create + link the package** (on [jsr.io](https://jsr.io/))\n\n1. Sign in to jsr.io with GitHub and create the package\n   **`@bounded-systems/bounded-tools-mcp`** under the `@bounded-systems` scope.\n2. Open the package's **Settings** tab → under **GitHub Repository** enter\n   `bounded-systems/bounded-tools-mcp` and click **Link**. Linking the repo\n   enables **tokenless OIDC publishing** from this workflow.\n\n**(c) MCP Registry — nothing to pre-authorize**\n\nThe `io.github.bounded-systems/*` namespace is **auto-authorized via GitHub\nOIDC**: because this repo lives under `github.com/bounded-systems`,\n`mcp-publisher login github-oidc` proves ownership from the Actions run itself.\n\n### Cut a release (the single command)\n\n```bash\n# 1. Bump the version in ALL of: package.json, deno.json, server.json. Commit.\n# 2. Tag with the SAME version and push — this is the only command:\ngit tag v0.1.0 && git push origin v0.1.0\n```\n\n### Local dry-runs (verify without publishing)\n\n```bash\nnpm pack --dry-run                                   # npm tarball contents\nnpx --yes jsr publish --dry-run --allow-slow-types   # JSR (or: deno publish --dry-run --allow-slow-types)\nmcp-publisher validate ./server.json                 # MCP Registry schema check\n```\n\n> bounded-tools-mcp depends on `@bounded-systems/static-mcp`; that core is\n> published independently (its own `v*` tag → JSR + npm) before cutting this tag.\n\n## License\n\nMIT — see [LICENSE](./LICENSE).\n","readmeFilename":"README.md","_rev":"1-7c7f79db43d4dda16003a09bc554193f"}