{"_id":"@cyanmycelium/mcp-broker-provider","_rev":"5-9e5d88009af6d2ee0232635eba184253","name":"@cyanmycelium/mcp-broker-provider","dist-tags":{"latest":"0.4.1"},"versions":{"0.1.0":{"name":"@cyanmycelium/mcp-broker-provider","version":"0.1.0","keywords":["mcp","model-context-protocol","broker","provider","tunnel","websocket","transport","cyanmycelium"],"author":{"url":"CyanMycelium","name":"Guillaume Pelletier"},"license":"Apache-2.0","_id":"@cyanmycelium/mcp-broker-provider@0.1.0","maintainers":[{"name":"gaume","email":"gaume.pelletier@gmail.com"}],"homepage":"https://github.com/pandaGaume/mcp-broker#readme","bugs":{"url":"https://github.com/pandaGaume/mcp-broker/issues"},"dist":{"shasum":"b6b81eff8277badfcf4428b647d3af848758e5ae","tarball":"https://registry.npmjs.org/@cyanmycelium/mcp-broker-provider/-/mcp-broker-provider-0.1.0.tgz","fileCount":23,"integrity":"sha512-WOqB29T6Pv7VPQUfVwrTDMjR0X6d1NWekBqJnUULk6Q2lhj65qx8CYnSi7yF7K4XuUgZk6sOp0kaYIV6alDMiw==","signatures":[{"sig":"MEYCIQCUm22yOSYcrREoQKIqC5K7EXFCVuBkUQgxYiRG/XWn9AIhAJ9SihdGH9LrOfPXPO9MFooarIwsanYQuxSl5sr4NwJH","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":67454},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20.11.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./protocol":{"types":"./dist/protocol/index.d.ts","import":"./dist/protocol/index.js","default":"./dist/protocol/index.js"}},"gitHead":"2c6c9519046cc4ebdf465c6d2663561f24a6517b","scripts":{"lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","test":"vitest run","build":"npm run clean && npm run compile","clean":"rimraf dist tsconfig.build.tsbuildinfo","watch":"tsc -b tsconfig.build.json -w","format":"prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"","compile":"tsc -b tsconfig.build.json","lint:fix":"eslint \"src/**/*.ts\" \"tests/**/*.ts\" --fix","format:fix":"prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"","test:watch":"vitest","prepublishOnly":"npm run lint && npm run build && npm test"},"_npmUser":{"name":"gaume","email":"gaume.pelletier@gmail.com"},"repository":{"url":"git+https://github.com/pandaGaume/mcp-broker.git","type":"git","directory":"node/packages/provider"},"_npmVersion":"10.9.3","description":"Provider side of the CyanMycelium MCP broker tunnel: publish an MCP server to a broker slot over the shared envelope protocol.","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","_hasShrinkwrap":false,"devDependencies":{"tslib":"^2.8.1","eslint":"^8.57.0","rimraf":"~6.0.1","vitest":"^4.1.9","prettier":"^3.2.0","typescript":"^5.4.0","@types/node":"^20.11.0","@cyanmycelium/mcp-core":"^0.4.0","eslint-config-prettier":"^9.1.0","eslint-plugin-prettier":"^5.1.0","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"peerDependencies":{"@cyanmycelium/mcp-core":"^0.4.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp-broker-provider_0.1.0_1785172894222_0.6093137369920638","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@cyanmycelium/mcp-broker-provider","version":"0.1.1","keywords":["mcp","model-context-protocol","broker","provider","tunnel","websocket","transport","cyanmycelium"],"author":{"url":"CyanMycelium","name":"Guillaume Pelletier"},"license":"Apache-2.0","_id":"@cyanmycelium/mcp-broker-provider@0.1.1","maintainers":[{"name":"gaume","email":"gaume.pelletier@gmail.com"}],"homepage":"https://github.com/pandaGaume/mcp-broker#readme","bugs":{"url":"https://github.com/pandaGaume/mcp-broker/issues"},"dist":{"shasum":"31a7ee81633a89d821f3a29fff1bae5dd899068d","tarball":"https://registry.npmjs.org/@cyanmycelium/mcp-broker-provider/-/mcp-broker-provider-0.1.1.tgz","fileCount":14,"integrity":"sha512-ht4I7tl+hgwimMyG/81GiN0iOIvaENTvbUmg7DUTKaNUSiYzfbD0aak+DypkMAwMmsEUYN038onAvAlRvWuJ5Q==","signatures":[{"sig":"MEYCIQDLxfaMAFE1HHlKNNXOue89UQgemVYiEAXF3R37fIVgMgIhAPVqS2z6xXRwo4GKMZI69FIU0cTMAjPVL+6g2KPEBEQ0","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cyanmycelium%2fmcp-broker-provider@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":85053},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20.11.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./protocol":{"types":"./dist/protocol/index.d.ts","import":"./dist/protocol/index.js","default":"./dist/protocol/index.js"}},"gitHead":"352af21280295ae5739e21b769a87c3d614556ed","scripts":{"lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","test":"vitest run","build":"tsup","clean":"rimraf dist","watch":"tsup --watch","format":"prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"","lint:fix":"eslint \"src/**/*.ts\" \"tests/**/*.ts\" --fix","typecheck":"tsc -p tsconfig.json --noEmit","format:fix":"prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"","test:watch":"vitest","prepublishOnly":"npm run lint && npm run build && npm test"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:2093f441-466c-4196-a6ae-beac09f2746b"}},"repository":{"url":"git+https://github.com/pandaGaume/mcp-broker.git","type":"git","directory":"node/packages/provider"},"_npmVersion":"11.18.0","description":"Provider side of the CyanMycelium MCP broker tunnel: publish an MCP server to a broker slot over the shared envelope protocol.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","tslib":"^2.8.1","eslint":"^8.57.0","rimraf":"~6.0.1","vitest":"^4.1.9","prettier":"^3.2.0","typescript":"^5.4.0","@types/node":"^20.11.0","@cyanmycelium/mcp-core":"^0.7.0","eslint-config-prettier":"^9.1.0","eslint-plugin-prettier":"^5.1.0","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"peerDependencies":{"@cyanmycelium/mcp-core":">=0.4.0 <1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp-broker-provider_0.1.1_1785256377190_0.1530709276837987","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@cyanmycelium/mcp-broker-provider","version":"0.2.0","keywords":["mcp","model-context-protocol","broker","provider","tunnel","websocket","transport","cyanmycelium"],"author":{"url":"CyanMycelium","name":"Guillaume Pelletier"},"license":"Apache-2.0","_id":"@cyanmycelium/mcp-broker-provider@0.2.0","maintainers":[{"name":"gaume","email":"gaume.pelletier@gmail.com"}],"homepage":"https://github.com/pandaGaume/mcp-broker#readme","bugs":{"url":"https://github.com/pandaGaume/mcp-broker/issues"},"dist":{"shasum":"187960edefbadc28ff60622430e6aa33990175c5","tarball":"https://registry.npmjs.org/@cyanmycelium/mcp-broker-provider/-/mcp-broker-provider-0.2.0.tgz","fileCount":15,"integrity":"sha512-gHOHxxGfOOtRbF+wkl5ZZwSQBk9JOadoZJ3EpHJs74GljJKSn3gFqzCjSWFXAKvwx4uUFDfdRs6T7I7fEjbGmw==","signatures":[{"sig":"MEYCIQDW3JFAep2dL3FOI4/rpK59U83BjH5Au4TXaCF/5MYzhgIhANvORFqFTsjrSMKjT1eBjo0cgzLOVWv48COqfDyFdDbS","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIAm5pdWncyDHy9va8TiyBBfyXOFleUqmiP0glN7Uup1JAiApXHUtuZ33itCE6GG/mg1Yg/V4QV119y2gsiB1PTF9tA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cyanmycelium%2fmcp-broker-provider@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":183852},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20.11.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./protocol":{"types":"./dist/protocol/index.d.ts","import":"./dist/protocol/index.js","default":"./dist/protocol/index.js"}},"gitHead":"a3370839484d80fdb26df14d6411644e2df9b64a","scripts":{"lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","test":"vitest run","build":"tsup","clean":"rimraf dist","watch":"tsup --watch","format":"prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"","lint:fix":"eslint \"src/**/*.ts\" \"tests/**/*.ts\" --fix","typecheck":"tsc -p tsconfig.json --noEmit","format:fix":"prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"","test:watch":"vitest","prepublishOnly":"npm run lint && npm run build && npm test"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:2093f441-466c-4196-a6ae-beac09f2746b"}},"repository":{"url":"git+https://github.com/pandaGaume/mcp-broker.git","type":"git","directory":"node/packages/provider"},"_npmVersion":"11.19.1","description":"Provider side of the CyanMycelium MCP broker tunnel: publish an MCP server to a broker slot over the shared envelope protocol.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","tslib":"^2.8.1","eslint":"^8.57.0","rimraf":"~6.0.1","vitest":"^4.1.9","prettier":"^3.2.0","typescript":"^5.4.0","@types/node":"^20.11.0","@cyanmycelium/mcp-core":"^1.0.0","eslint-config-prettier":"^9.1.0","eslint-plugin-prettier":"^5.1.0","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"peerDependencies":{"@cyanmycelium/mcp-core":">=0.7.0 <2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp-broker-provider_0.2.0_1789231657880_0.6484990515338498","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@cyanmycelium/mcp-broker-provider","version":"0.3.0","keywords":["mcp","model-context-protocol","broker","provider","tunnel","websocket","transport","cyanmycelium"],"author":{"url":"CyanMycelium","name":"Guillaume Pelletier"},"license":"Apache-2.0","_id":"@cyanmycelium/mcp-broker-provider@0.3.0","maintainers":[{"name":"gaume","email":"gaume.pelletier@gmail.com"}],"homepage":"https://github.com/pandaGaume/mcp-broker#readme","bugs":{"url":"https://github.com/pandaGaume/mcp-broker/issues"},"dist":{"shasum":"50984c93505b4b0a2e9d3f9d2ca5b0edb838e2c7","tarball":"https://registry.npmjs.org/@cyanmycelium/mcp-broker-provider/-/mcp-broker-provider-0.3.0.tgz","fileCount":16,"integrity":"sha512-SV+RdCEuKLiYBYoDL/nSNyLRqsEsT/jY7ZAi5pqAPsgP48Cwlats4dWT76Ha/As1szsIUl8Q6oFVE9Kw6C18WQ==","signatures":[{"sig":"MEYCIQCz6oGcIsJNpu9ccSSXpAu8oA0kRnpXCzVGDi2TY+zV5gIhALvP0DgJELSccbwW05/ULpLCsaXrmMdf458J8jVuHY64","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCICsi1BGwxg7cYepAcA/b1uLoUJrxkJudMkFHiaFHChM4AiEAqM+u5Bo/9gK29hRy0Vbqhx/qlJvTVQCOWJxUzKWJBIk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cyanmycelium%2fmcp-broker-provider@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":272319},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20.11.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./protocol":{"types":"./dist/protocol/index.d.ts","import":"./dist/protocol/index.js","default":"./dist/protocol/index.js"}},"gitHead":"32ce109abd52988d1fb533d4a5e7084a62e7c22c","scripts":{"lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","test":"vitest run","build":"tsup","clean":"rimraf dist","watch":"tsup --watch","format":"prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"","lint:fix":"eslint \"src/**/*.ts\" \"tests/**/*.ts\" --fix","typecheck":"tsc -p tsconfig.json --noEmit","format:fix":"prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"","test:watch":"vitest","prepublishOnly":"npm run lint && npm run build && npm test"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"2093f441-466c-4196-a6ae-beac09f2746b"}},"repository":{"url":"git+https://github.com/pandaGaume/mcp-broker.git","type":"git","directory":"node/packages/provider"},"_npmVersion":"11.21.0","description":"Provider side of the CyanMycelium MCP broker tunnel: publish an MCP server to a broker slot over the shared envelope protocol.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","tslib":"^2.8.1","eslint":"^8.57.0","rimraf":"~6.0.1","vitest":"^4.1.9","prettier":"^3.2.0","typescript":"^5.4.0","@types/node":"^20.11.0","@cyanmycelium/mcp-core":"^1.4.0","eslint-config-prettier":"^9.1.0","eslint-plugin-prettier":"^5.1.0","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"peerDependencies":{"@cyanmycelium/mcp-core":">=0.7.0 <2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp-broker-provider_0.3.0_1790875921532_0.2571488891596232","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"_id":"@cyanmycelium/mcp-broker-provider@0.4.1","bugs":{"url":"https://github.com/pandaGaume/mcp-broker/issues"},"dist":{"shasum":"ad82c73ab8e6f12e4d1e4c5016dab2ec708e33b3","tarball":"https://registry.npmjs.org/@cyanmycelium/mcp-broker-provider/-/mcp-broker-provider-0.4.1.tgz","fileCount":16,"integrity":"sha512-slzE6ULQysOUR57y2qtvsMa5IcRAKFzo9dEZK+t50XoGpUHOlZNqugdbB7Bs9h8nxkOZbeqOc+y/Y6/LJWRN/Q==","signatures":[{"sig":"MEYCIQDfLpWGdOzAo3DLuurnvSxS4b+7SXySjcPHddxuV+tTaAIhAMJTnSorbm++pMWwdHzw0yTVbrFp/1MjKpqkfAfxjTVN","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDNUN5IRWkO2c1t5rFNTxVODhcNSprzywhB67Sa4ZynCAiAvEP1IUxyb4VL/4V07VYBgQOW63oFCsIwCq/7i+jy+TA=="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cyanmycelium%2fmcp-broker-provider@0.4.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":280581},"main":"./dist/index.js","name":"@cyanmycelium/mcp-broker-provider","type":"module","types":"./dist/index.d.ts","author":{"url":"CyanMycelium","name":"Guillaume Pelletier"},"module":"./dist/index.js","engines":{"node":">=20.11.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./protocol":{"types":"./dist/protocol/index.d.ts","import":"./dist/protocol/index.js","default":"./dist/protocol/index.js"}},"gitHead":"25a0602a7183cf4fe869cbc065b1a7518f4671c4","license":"Apache-2.0","scripts":{"lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","test":"vitest run","build":"tsup","clean":"rimraf dist","watch":"tsup --watch","format":"prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"","lint:fix":"eslint \"src/**/*.ts\" \"tests/**/*.ts\" --fix","typecheck":"tsc -p tsconfig.json --noEmit","format:fix":"prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"","test:watch":"vitest","prepublishOnly":"npm run lint && npm run build && npm test"},"version":"0.4.1","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"2093f441-466c-4196-a6ae-beac09f2746b"}},"homepage":"https://github.com/pandaGaume/mcp-broker#readme","keywords":["mcp","model-context-protocol","broker","provider","tunnel","websocket","transport","cyanmycelium"],"repository":{"url":"git+https://github.com/pandaGaume/mcp-broker.git","type":"git","directory":"node/packages/provider"},"_npmVersion":"11.21.0","description":"Provider side of the CyanMycelium MCP broker tunnel: publish an MCP server to a broker slot over the shared envelope protocol.","directories":{},"maintainers":[{"name":"gaume","email":"gaume.pelletier@gmail.com"}],"sideEffects":false,"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","tslib":"^2.8.1","eslint":"^8.57.0","rimraf":"~6.0.1","vitest":"^4.1.9","prettier":"^3.2.0","typescript":"^5.4.0","@types/node":"^20.11.0","@cyanmycelium/mcp-core":"^1.4.0","eslint-config-prettier":"^9.1.0","eslint-plugin-prettier":"^5.1.0","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"peerDependencies":{"@cyanmycelium/mcp-core":">=0.7.0 <2.0.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mcp-broker-provider_0.4.1_1790950657848_0.32168372353529917"}}},"time":{"created":"2026-07-27T17:21:34.016Z","modified":"2026-10-02T14:17:38.829Z","0.1.0":"2026-07-27T17:21:34.363Z","0.1.1":"2026-07-28T16:32:57.350Z","0.2.0":"2026-09-12T16:47:37.973Z","0.3.0":"2026-10-01T17:32:01.625Z","0.4.1":"2026-10-02T14:17:37.961Z"},"bugs":{"url":"https://github.com/pandaGaume/mcp-broker/issues"},"author":{"url":"CyanMycelium","name":"Guillaume Pelletier"},"license":"Apache-2.0","homepage":"https://github.com/pandaGaume/mcp-broker#readme","keywords":["mcp","model-context-protocol","broker","provider","tunnel","websocket","transport","cyanmycelium"],"repository":{"url":"git+https://github.com/pandaGaume/mcp-broker.git","type":"git","directory":"node/packages/provider"},"description":"Provider side of the CyanMycelium MCP broker tunnel: publish an MCP server to a broker slot over the shared envelope protocol.","maintainers":[{"name":"gaume","email":"gaume.pelletier@gmail.com"}],"readme":"# @cyanmycelium/mcp-broker-provider\n\nProvider side of the [CyanMycelium MCP broker](https://github.com/pandaGaume/mcp-broker) tunnel: what an application uses to **publish** its MCP server to a broker slot.\n\nMCP defines two standard transports, stdio and Streamable HTTP, and both live in [`@cyanmycelium/mcp-core`](https://www.npmjs.com/package/@cyanmycelium/mcp-core). The WebSocket tunnel is not one of them: it is CyanMycelium topology, where an MCP server runs next to a browser application and reaches the outside world through a broker. That is what this package covers, so `mcp-core` stays a faithful implementation of the specification and nothing else.\n\nSee [docs/packages.md](https://github.com/pandaGaume/mcp-broker/blob/main/docs/packages.md) for how this package relates to the broker, and why *provider* rather than *client*.\n\n## Install\n\n```sh\nnpm install @cyanmycelium/mcp-broker-provider\n```\n\n## Entry points\n\n```ts\nimport { MultiplexTransport } from \"@cyanmycelium/mcp-broker-provider\";\nimport { decodeEnvelope } from \"@cyanmycelium/mcp-broker-provider/protocol\";\n```\n\n| Entry point | Contents |\n|---|---|\n| `.` | The tunnel transports, plus everything below |\n| `./protocol` | The envelope wire format: types, codec, registration notification, error codes. No dependencies, isomorphic |\n\n## The envelope protocol\n\nA multiplexed tunnel socket carries traffic for several providers at once, so every JSON-RPC message is wrapped with the name of the provider slot it belongs to:\n\n```json\n{ \"provider\": \"scene-1\", \"payload\": { \"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"tools/list\" } }\n```\n\n`./protocol` is the single definition of that format, and the broker imports it from here rather than re-declaring the shape inline, so the two ends cannot drift. The broker depends on this package because it is itself a provider: it publishes its own `_broker` introspection slot and its `_all` aggregate slot.\n\nBeyond the envelope it also covers:\n\n- `notifications/register`, sent as soon as the tunnel opens to claim a provider slot. Without it the broker only learns a provider name on its first real message, and an MCP client connecting in between is told the provider is not connected. It optionally carries `params: { aggregate: true }`, which also joins the broker's `_all` slot, see [Joining the `_all` aggregate slot](#joining-the-_all-aggregate-slot).\n- The tunnel error codes: `-32001` when the provider's credentials do not allow publishing on the requested slot, `-32000` when the slot is unavailable.\n\nWhen the broker refuses a slot, the transport surfaces it through `onError` rather than forwarding it. An id-less error frame handed to an MCP server would be classified as an unknown notification and dropped without a word, so the publisher would never learn it was refused.\n\nMalformed frames decode to `undefined` rather than throwing: a tunnel socket is a public surface, and a peer sending garbage must not take the receiver down.\n\n## Transports\n\n| Transport | Endpoint it speaks to | Framing | Reconnects | Use case |\n|---|---|---|---|---|\n| `MultiplexTransport` | the shared multiplex base, `ws://<broker>/providers` | envelopes `{ provider, payload }` | yes, on the shared socket | Several servers published by one application. A single socket carries them all, keyed by slot name |\n| `DirectTransport` | a slot-scoped path, `ws://<broker>/provider/<name>` | plain JSON-RPC frames | **no** | One server, one socket |\n\n**The transport and the path are a pair, not a preference.** The broker decides framing from the endpoint the socket landed on, so a mismatch does not fail the handshake: the socket opens, looks healthy, and every frame is dropped on one side or the other with nothing logged by the broker. Both transports warn on the console when they spot the mismatch at connect time. And `ws://<broker>/providers/<name>`, the natural-looking blend of the two, is neither: the broker accepts it as a *client* connection on a slot of that name.\n\n```ts\nimport { McpServerBuilder } from \"@cyanmycelium/mcp-core/server\";\nimport { MultiplexTransport } from \"@cyanmycelium/mcp-broker-provider\";\n\nconst server = new McpServerBuilder()\n    .withName(\"scene-1\")\n    .withTransport(MultiplexTransport.create(\"scene-1\", \"ws://localhost:3000/providers\"))\n    .register(behavior)\n    .build();\n\nawait server.start();\n```\n\nTransports created for the same tunnel URL share one WebSocket, whichever order they are opened in. Reconnection is handled by that shared socket, with exponential back-off and jitter, and individual transports never reconnect on their own. `DirectTransport` does not reconnect at all: when its socket closes it stays closed, and the application decides whether to call `connect()` again.\n\n`server.start()` resolving means the transport reported itself open, not that the broker accepted the slot. A refusal arrives afterwards, and reaches you as an `onError` on the transport and a line on the console.\n\n`@cyanmycelium/mcp-core` is a peer dependency: the transports import its `IMessageTransport` type and nothing else at runtime, so your application keeps a single copy of it.\n\n## Joining the `_all` aggregate slot\n\nThe broker publishes an aggregate slot, `_all`, which exposes every opted-in provider's tools and prompts through one MCP connection. Membership is opt-in, because `_all` is a confidentiality boundary: a provider that does not ask for it stays reachable only on its own slot.\n\n```ts\nimport { DirectTransport, MultiplexTransport } from \"@cyanmycelium/mcp-broker-provider\";\n\n// One socket per server\nconst direct = new DirectTransport(\"ws://localhost:3000/provider/scene-1\", { aggregate: true });\n\n// Or on the shared tunnel\nconst shared = MultiplexTransport.create(\"scene-1\", \"ws://localhost:3000/providers\", { aggregate: true });\n```\n\nEither form sends the registration notification with `params: { aggregate: true }` as its first frame:\n\n```json\n{ \"jsonrpc\": \"2.0\", \"method\": \"notifications/register\", \"params\": { \"aggregate\": true } }\n```\n\n`DirectTransport` sends it verbatim, since the slot-scoped path carries plain JSON-RPC and the broker already knows the slot name from the URL. `MultiplexTransport` sends it inside the usual envelope, as `{ \"provider\": \"scene-1\", \"payload\": { ... } }`.\n\n**Wire the message handler before you connect.** The broker runs `initialize` against a newly aggregated provider immediately, and a provider that does not answer is dropped from `_all` silently. Handing the transport to an MCP server does this for you, since the server assigns `onMessage` before calling `connect()`. Assigning it yourself, after connecting, loses the handshake and the provider never appears in the aggregate.\n\n## Letting the broker decide (broker 1.5.0)\n\nA provider that serves its own kind of resource can declare an authorization\ndomain and ask the broker for decisions, instead of carrying a policy of its\nown. Both transports expose the broker's methods as `transport.broker`:\n\n```ts\nimport { DirectTransport, callerReferenceOf } from \"@cyanmycelium/mcp-broker-provider\";\n\nconst transport = new DirectTransport(\"ws://broker:3000/provider/scada\");\n// ... start the MCP server on it, then:\nawait transport.broker.declare({\n    version: \"2026-10-01.1\",\n    domain: \"scada\",\n    namespace: { resource: \"/production/site1\" },\n    capabilities: [\"scada.observe\", \"scada.control\"],\n    resources: [\n        {\n            resource: \"uns://production/site1/line1/motor01/speed_sp\",\n            resourcePath: \"/production/site1/line1/motor01/speed_sp\",\n            limits: { minValue: 0, maxValue: 1500 },\n        },\n    ],\n    resultsRequired: [\"scada.control\"],\n});\n\n// In a tool handler (mcp-core 1.4.0 hands the adapter the request's _meta):\nconst caller = callerReferenceOf(request?.meta);\nconst { decisions } = await transport.broker.authorize({\n    principal: { type: \"caller-ref\", ref: caller!.ref },\n    checks: [{ capability: \"scada.control\", resource: \"uns://production/site1/line1/motor01/speed_sp\", resourcePath: \"/production/site1/line1/motor01/speed_sp\" }],\n});\n// decisions[0]: { effect: \"allow-with-constraints\", allowed: false, obligations: { constraints: { minValue: 0, maxValue: 1500 } }, ... }\n\n// Once the write is done, or refused by a constraint:\ntransport.broker.reportResult({ decisionId: decisions[0].decisionId, result: \"success\", nativeStatus: \"Good\" });\n```\n\n- `effect` is `allow`, `deny` or `allow-with-constraints`. The last one comes\n  with the resource's declared limits in `obligations.constraints`; apply them\n  right before executing. `allowed` is `true` only for a plain `allow`, so code\n  that reads `allowed` alone refuses a constrained allow instead of ignoring\n  its limits.\n- `reportResult()` is a notification: nothing comes back. The broker writes the\n  outcome next to the decision in its audit, and `broker_diagnose` reports\n  decisions of `resultsRequired` capabilities still unreported.\n\n- The provider needs its own identity on the broker (an entry in the security\n  file's `providers` table), which means presenting a secret:\n  `new DirectTransport(url, { secret })`, sent as `X-Provider-Token`. Node 22\n  and later can; a browser cannot, so this is for Node and native providers.\n- To test all of this without an authorization server, start the broker with\n  `startTestBroker()` from `@cyanmycelium/mcp-broker/testing`\n  ([guide](../broker/docs/testing.md)).\n- The broker's answers are taken off the socket before the MCP server sees\n  them. A refused request rejects with `BrokerRequestError` (`code`, `data`).\n- There is no timeout by default: a broker from 1.4.1 on answers at once. Set\n  `brokerRequestTimeoutMs` only to talk to an older one, which drops methods it\n  does not know.\n\n## Distributed traces (broker 1.5.0)\n\nEvery addressed request arriving from broker 1.5 carries W3C Trace Context in\n`params._meta.traceparent`. Read it from the request metadata, create a SERVER\nspan, then propagate a child context when this provider calls another slot:\n\n```ts\nimport {\n    childTraceparent,\n    traceparentOf,\n    withTraceparent,\n} from \"@cyanmycelium/mcp-broker-provider\";\n\nconst incoming = traceparentOf(request?.meta);\nconst downstreamMeta = incoming\n    ? withTraceparent(undefined, childTraceparent(incoming, clientSpanId))\n    : undefined;\n```\n\nCompleted spans are validated before emission:\n\n```ts\nconst emitted = transport.broker.span({\n    traceId,\n    spanId,\n    parentSpanId,\n    name: \"modbus.read\",\n    kind: 3,\n    startTimeUnixNano,\n    endTimeUnixNano,\n    attributes: { \"modbus.unit_id\": 1 },\n});\n```\n\nThe call is a notification and never waits for an answer. The broker consumes\n`broker/telemetry`, sends it through its bounded exporter queue, and never\nbroadcasts it to MCP clients. It returns `false` and drops the span when the\nprovider link is down, so telemetry never occupies the transport queue ahead\nof MCP control traffic. See [the complete telemetry contract](../../../docs/telemetry.md).\n\n## Diagnostics\n\nThis stack used to fail quietly. The transports now name what went wrong, on the console, because a browser-hosted provider has nowhere else to report:\n\n- A frame written before the socket is open is queued (64 frames, oldest dropped first with a warning) and flushed on open, rather than discarded. That window covers the whole reconnect back-off, up to 30 seconds.\n- A close with a code other than `1000` is reported through `onError` with the code and the broker's own reason, before `onClose`. A `1008` is a policy refusal: the slot is already connected, is reserved, or provider authentication rejected it.\n- An incoming frame that is not an envelope, or one for a slot this socket does not publish, is logged with the likely cause and the fix. Repeats are sampled (the first in full, then one in fifty) so a mismatched tunnel cannot flood the console.\n\n## In a browser\n\nThe transports are written for the browser, but the npm path needs a bundler: neither this package nor `@cyanmycelium/mcp-core` ships a UMD build or declares a `browser` field, so a bare `<script>` tag will not load them. Any bundler works; there is nothing to configure beyond resolving the two packages.\n\nImport only the isomorphic entry points. `@cyanmycelium/mcp-core/server` and `@cyanmycelium/mcp-core/client` run in a browser; `@cyanmycelium/mcp-core/node` does not, and pulling it in is the usual cause of a build that fails on Node built-ins.\n\nIf you would rather not add a build step, the broker ships a dependency-free ES module that speaks the same tunnel, `web/js/lib/broker-tunnel.js` (inside the `@cyanmycelium/mcp-broker` package, at `node/packages/broker/web` in the repository). It is the zero-build alternative, not a replacement: it carries no MCP server implementation.\n\nOne limitation worth knowing before you deploy: if the broker is configured with provider authentication, a browser-hosted provider cannot connect. The broker reads its credential from the `X-Provider-Token` or `Authorization` header of the upgrade request, and the browser `WebSocket` constructor cannot set headers, so the handshake is refused with a 401 the page sees only as a generic error. A browser provider needs provider auth off, or an authenticating reverse proxy in front of the broker.\n\n## Status\n\nThis package is the only home of the tunnel transports. They also shipped in `@cyanmycelium/mcp-core@0.4.x`, were removed there in `0.5.0`, and are absent from `0.7.x` and `1.x`, so migrate those imports here before upgrading `mcp-core`.\n\nThe protocol is shared with the broker, and later with the consumer side. Import it through the `./protocol` subpath rather than the package root, so it can move to a package of its own one day without touching your call sites.\n\n## License\n\nApache-2.0\n","readmeFilename":"README.md"}