{
  "schema": "leumas.docs.page/1",
  "id": "pkg:@leumas/mcp-bridge",
  "slug": "packages/mcp-bridge",
  "kind": "capabilities",
  "bucket": "package",
  "title": "@leumas/mcp-bridge",
  "name": "@leumas/mcp-bridge",
  "eyebrow": null,
  "chip": null,
  "summary": "The stdio ⇄ streamable-HTTP bridge — one line in any coding agent's config connects it to a Leumas install, ours or the customer's. Carries no tools of its own.",
  "keywords": [
    "mcp-bridge",
    "stdio",
    "ours",
    "coding",
    "bridge",
    "mcp bridge api",
    "leumas mcp bridge",
    "customer"
  ],
  "audience": "both",
  "funnel": {
    "product": null,
    "cta": null
  },
  "body": "# `@leumas/mcp-bridge`\n\n<!-- npm:strip -->\n> Published to npm as **`leumas-mcp`** — unscoped, zero dependencies, its two `@leumas/*`\n> imports inlined by `pnpm bridge:build`. This package stays `private` in the workspace for ever;\n> the published manifest is derived, so the two cannot drift. `pnpm bridge:release` is the gate.\n<!-- /npm:strip -->\n\n**One line in any coding agent's config, pointed at any Leumas install.**\n\nleumas-api speaks MCP over streamable HTTP. Coding agents are not uniform about it — some speak it,\nsome only spawn a subprocess and talk newline-delimited JSON-RPC over its stdio, and the ones that do\nboth configure HTTP differently from each other. This is the subprocess.\n\n```sh\nleumas-mcp --url https://api.leumas.tech        # Leumas-hosted\nleumas-mcp --url http://localhost:3000          # the customer's own Studio\nleumas-mcp --url http://localhost:3000 --site my-app   # one hosted MCP server\n```\n\nBecause the endpoint is an argument, **\"our infrastructure or yours\" is the same line with a\ndifferent URL.** There is no second server, and no second thing to keep in sync.\n\n## What it does, and what it refuses to do\n\nIt forwards. Every message goes out verbatim and every response comes back verbatim, so a tool added\nto an install appears here with no release of this package. That is not laziness — the moment a\nbridge knows what a tool is, it can be wrong about one.\n\n### The one exception, and its physical reason\n\n`leumas__deploy` is implemented **here**, not on the server, because **the files are on this\nmachine**. A remote tool cannot see your `dist/`, and handing it the bytes as an argument is capped\nby the `/mcp` body limit and inflated 4/3 by base64 on top — a mistake this repo has already paid\nfor once, at 78 MB against a 2 MB limit.\n\nIt is one tool rather than five because a deploy is an ordered sequence — mint a session, create the\nsite, push N files, retarget, publish — and an agent handed five ordered tools gets the order wrong.\nThe failure is a live site pointed at a half-uploaded build, with nothing reporting an error. So the\nsequence is code.\n\n```\nleumas__deploy { folder: \"/path/to/my-app\", name: \"my-app\" }\n  → walks the folder, strips the wrapper, skips node_modules and .env\n  → mints a deploy credential scoped to one site and one build directory\n  → uploads into builds/<new>, so the live build is untouched throughout\n  → points the site at it in ONE write, and publishes\n  → returns the live URL\n```\n\nThis is the exception that has a physical reason, not the start of a habit. Everything else forwards.\n\n## The two rules\n\n**1 · stdout is the protocol.** One JSON message per line and nothing else, ever. A stray banner or\nprogress line is a parse error inside the agent, and what the person sees is *\"the MCP server failed\nto start\"* with nothing naming the cause. Every human-readable notice goes to stderr — including the\nusage text, which is why `--help` writes nothing to stdout.\n\n**2 · an HTTP failure must become a JSON-RPC error.** The agent is waiting on an `id`. A 401, a 403\nfrom the gateway scope check, a 502 from a proxy, a refused connection — none of those produce a\nJSON-RPC envelope of their own. A bridge that logs one and moves on leaves that request pending\n**for ever**: the agent does not time out, it simply never answers the person. So every non-2xx,\nevery 200 with a non-JSON body, and every thrown fetch is translated, carrying the id it answers and\nquoting what the server actually said.\n\nA **notification** is the one thing that is never answered — not even when it is refused. Answering\none makes several clients error out.\n\n## Which endpoint a URL means\n\n| You pass | It posts to | Why |\n|---|---|---|\n| `https://api.leumas.tech` | `…/mcp/rpc` | a bare origin means the whole gateway |\n| `…` + `--site abc` | `…/mcp/s/abc` | one hosted server — **the only form that works on a local appliance**, where there is no DNS for a subdomain to resolve through |\n| `https://api.leumas.tech/mcp/s/abc` | itself | an explicit path is honoured as given |\n\nA URL that does not parse **throws**. Posting nowhere silently is indistinguishable from a server\nthat has no tools.\n\n## Credentials\n\n`--key`, or `$LEUMAS_API_KEY`. Mint one at `<origin>/connect`; it is scoped `mcp` and reaches the MCP\nplanes and nothing else.\n\nA missing key is a **warning, not a refusal** — against a local appliance the caller may already be\nthe owner over a session, and a hosted server may be deliberately public. The server's own 401 is a\nbetter error than one this bridge could invent, because it is the one that is actually true.\n\n## Verify\n\n```sh\npnpm smoke:mcp-bridge\n```\n\nFraming and failure are checked on injected streams and a fake fetch; the last section boots the API\nin-process, mints a key through `POST /keys/agent`, and drives the real `bin/leumas-mcp.mjs` as a\nchild process — because neither \"does the credential exist\" nor \"does the spawned binary speak the\nprotocol\" can be seen by reading the code.\n",
  "source": {
    "path": "shared/packages/mcp-bridge/README.md",
    "blobSha": "",
    "commit": "",
    "committedAt": "",
    "provenance": "no-git",
    "bytes": 5174,
    "hash": "e9dc2ae036a9aa40822ff78dc935c6c0f8d091c2"
  },
  "urls": {
    "html": "/p/packages/mcp-bridge",
    "json": "/docs/packages/mcp-bridge.json",
    "md": "/docs/packages/mcp-bridge.md"
  },
  "links": {
    "composes": [
      "pkg:@leumas/hosting-upload"
    ],
    "usedBy": [],
    "product": [],
    "howTo": [],
    "skills": [
      "skill:expose-as-mcp"
    ]
  },
  "exports": {
    "total": 6,
    "component": 0,
    "hook": 0,
    "helper": 6,
    "names": [
      {
        "n": "forward",
        "k": "helper"
      },
      {
        "n": "main",
        "k": "helper"
      },
      {
        "n": "parseArgs",
        "k": "helper"
      },
      {
        "n": "resolveEndpoint",
        "k": "helper"
      },
      {
        "n": "runBridge",
        "k": "helper"
      },
      {
        "n": "transportError",
        "k": "helper"
      }
    ]
  }
}
