@cyanmycelium/mcp-broker 0.4.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp-broker.example/CONFIGURATION-EN.md +861 -0
- package/.mcp-broker.example/CONFIGURATION-FR.md +871 -0
- package/.mcp-broker.example/README.md +8 -3
- package/.mcp-broker.example/config.json +86 -0
- package/README.md +122 -3
- package/dist/bin.d.ts +0 -1
- package/dist/bin.js +180 -208
- package/dist/bin.js.map +1 -1
- package/dist/chunk-FTDKH2C4.js +3670 -0
- package/dist/chunk-FTDKH2C4.js.map +1 -0
- package/dist/{broker/grammars → grammars}/claude/en.json +1 -1
- package/dist/{broker/grammars → grammars}/claude/fr.json +1 -1
- package/dist/index.d.ts +1790 -18
- package/dist/index.js +2 -14
- package/dist/index.js.map +1 -1
- package/package.json +14 -8
- package/scripts/copy-assets.mjs +16 -10
- package/scripts/gen-cert.mjs +5 -5
- package/scripts/pack-mcpb.mjs +5 -5
- package/scripts/sign-bundle.mjs +6 -6
- package/src/auth/auth.config.ts +98 -0
- package/src/auth/auth.types.ts +120 -0
- package/src/auth/http.auth.ts +128 -0
- package/src/auth/index.ts +34 -0
- package/src/auth/jwt.validator.ts +63 -0
- package/src/auth/provider.auth.ts +114 -0
- package/src/auth/resource.metadata.ts +34 -0
- package/src/authorization/audit.ts +35 -0
- package/src/authorization/capability.classifier.ts +114 -0
- package/src/authorization/index.ts +37 -0
- package/src/authorization/policy.engine.ts +212 -0
- package/src/authorization/policy.types.ts +105 -0
- package/src/authorization/resource.path.ts +126 -0
- package/src/authorization/runtime.ts +56 -0
- package/src/authorization/slot.resource.ts +50 -0
- package/src/authorization/subject.mapper.ts +81 -0
- package/src/bin.ts +90 -7
- package/src/broker/adapters/broker.adapter.info.ts +3 -3
- package/src/broker/adapters/broker.adapter.providers.ts +3 -3
- package/src/broker/aggregate/aggregate.catalog.ts +49 -21
- package/src/broker/aggregate/aggregate.server.ts +149 -19
- package/src/broker/aggregate/provider.client.session.ts +22 -22
- package/src/broker/behaviors/broker.behavior.info.ts +4 -4
- package/src/broker/behaviors/broker.behavior.providers.ts +6 -6
- package/src/broker/broker.context.ts +10 -4
- package/src/broker/broker.grammars.ts +16 -13
- package/src/broker/broker.server.ts +12 -9
- package/src/broker/grammars/claude/en.json +1 -1
- package/src/broker/grammars/claude/fr.json +1 -1
- package/src/broker/index.ts +9 -9
- package/src/config.ts +81 -8
- package/src/index.ts +121 -20
- package/src/{mcpb.loader.ts → mcpb/mcpb.loader.ts} +24 -21
- package/src/{mcpb.unzip.ts → mcpb/mcpb.unzip.ts} +2 -2
- package/src/remote.transports.ts +11 -8
- package/src/remote.upstream.ts +11 -8
- package/src/stdio.upstream.ts +9 -6
- package/src/upstream.ts +6 -3
- package/src/ws/ws.interfaces.ts +357 -0
- package/src/{ws.tunnel.builder.ts → ws/ws.tunnel.builder.ts} +112 -9
- package/src/{ws.tunnel.ts → ws/ws.tunnel.ts} +646 -457
- package/web/README.md +94 -0
- package/web/assets/logo.png +0 -0
- package/web/broker-self-mcp.html +338 -0
- package/web/css/styles.css +580 -0
- package/web/demos/DemoPlaceholder.html +258 -0
- package/web/demos/broker-explorer/css/app.css +393 -0
- package/web/demos/broker-explorer/index.html +94 -0
- package/web/demos/broker-explorer/js/app.js +271 -0
- package/web/demos/broker-explorer/js/mcp-ws-client.js +132 -0
- package/web/demos/oauth-lab/README.md +94 -0
- package/web/demos/oauth-lab/config.json +117 -0
- package/web/demos/oauth-lab/css/app.css +1097 -0
- package/web/demos/oauth-lab/index.html +323 -0
- package/web/demos/oauth-lab/js/app.js +654 -0
- package/web/demos/oauth-lab/server/auth-server.mjs +426 -0
- package/web/demos/oauth-lab/server/factory-provider.mjs +269 -0
- package/web/demos/oauth-lab/server/smoke-test.mjs +308 -0
- package/web/demos/oauth-lab/server/start.mjs +106 -0
- package/web/demos/provider-tunnel/css/app.css +384 -0
- package/web/demos/provider-tunnel/index.html +99 -0
- package/web/demos/provider-tunnel/js/app.js +226 -0
- package/web/demos/provider-tunnel/js/toolbox-server.js +186 -0
- package/web/index.html +558 -0
- package/web/js/lib/broker-tunnel.js +173 -0
- package/dist/broker/adapters/broker.adapter.info.d.ts +0 -16
- package/dist/broker/adapters/broker.adapter.info.js +0 -43
- package/dist/broker/adapters/broker.adapter.info.js.map +0 -1
- package/dist/broker/adapters/broker.adapter.providers.d.ts +0 -18
- package/dist/broker/adapters/broker.adapter.providers.js +0 -61
- package/dist/broker/adapters/broker.adapter.providers.js.map +0 -1
- package/dist/broker/aggregate/aggregate.catalog.d.ts +0 -54
- package/dist/broker/aggregate/aggregate.catalog.js +0 -105
- package/dist/broker/aggregate/aggregate.catalog.js.map +0 -1
- package/dist/broker/aggregate/aggregate.server.d.ts +0 -47
- package/dist/broker/aggregate/aggregate.server.js +0 -151
- package/dist/broker/aggregate/aggregate.server.js.map +0 -1
- package/dist/broker/aggregate/provider.client.session.d.ts +0 -52
- package/dist/broker/aggregate/provider.client.session.js +0 -140
- package/dist/broker/aggregate/provider.client.session.js.map +0 -1
- package/dist/broker/behaviors/broker.behavior.info.d.ts +0 -15
- package/dist/broker/behaviors/broker.behavior.info.js +0 -41
- package/dist/broker/behaviors/broker.behavior.info.js.map +0 -1
- package/dist/broker/behaviors/broker.behavior.providers.d.ts +0 -19
- package/dist/broker/behaviors/broker.behavior.providers.js +0 -69
- package/dist/broker/behaviors/broker.behavior.providers.js.map +0 -1
- package/dist/broker/broker.context.d.ts +0 -59
- package/dist/broker/broker.context.js +0 -2
- package/dist/broker/broker.context.js.map +0 -1
- package/dist/broker/broker.grammars.d.ts +0 -130
- package/dist/broker/broker.grammars.js +0 -229
- package/dist/broker/broker.grammars.js.map +0 -1
- package/dist/broker/broker.server.d.ts +0 -66
- package/dist/broker/broker.server.js +0 -73
- package/dist/broker/broker.server.js.map +0 -1
- package/dist/broker/index.d.ts +0 -9
- package/dist/broker/index.js +0 -7
- package/dist/broker/index.js.map +0 -1
- package/dist/config.d.ts +0 -136
- package/dist/config.js +0 -61
- package/dist/config.js.map +0 -1
- package/dist/mcpb.loader.d.ts +0 -24
- package/dist/mcpb.loader.js +0 -161
- package/dist/mcpb.loader.js.map +0 -1
- package/dist/mcpb.unzip.d.ts +0 -6
- package/dist/mcpb.unzip.js +0 -95
- package/dist/mcpb.unzip.js.map +0 -1
- package/dist/remote.transports.d.ts +0 -16
- package/dist/remote.transports.js +0 -297
- package/dist/remote.transports.js.map +0 -1
- package/dist/remote.upstream.d.ts +0 -36
- package/dist/remote.upstream.js +0 -52
- package/dist/remote.upstream.js.map +0 -1
- package/dist/stdio.upstream.d.ts +0 -45
- package/dist/stdio.upstream.js +0 -85
- package/dist/stdio.upstream.js.map +0 -1
- package/dist/upstream.d.ts +0 -33
- package/dist/upstream.js +0 -2
- package/dist/upstream.js.map +0 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.js +0 -9
- package/dist/version.js.map +0 -1
- package/dist/ws.tunnel.builder.d.ts +0 -139
- package/dist/ws.tunnel.builder.js +0 -205
- package/dist/ws.tunnel.builder.js.map +0 -1
- package/dist/ws.tunnel.d.ts +0 -373
- package/dist/ws.tunnel.js +0 -1090
- package/dist/ws.tunnel.js.map +0 -1
- /package/dist/{broker/grammars → grammars}/default/en.json +0 -0
- /package/dist/{broker/grammars → grammars}/default/fr.json +0 -0
- /package/dist/{broker/grammars → grammars}/default/zh.json +0 -0
|
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import { McpGrammar } from "@cyanmycelium/mcp-core";
|
|
5
5
|
|
|
6
6
|
// ---------------------------------------------------------------------------
|
|
7
|
-
// Open string types
|
|
7
|
+
// Open string types, extensibility by convention
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -27,7 +27,7 @@ export type BrokerLocale = string;
|
|
|
27
27
|
* the defaults emitted by `grammarResolverFromOptions`: `claude`, `gpt`,
|
|
28
28
|
* `mistral`, `copilot`, plus the universal `default`. Custom families
|
|
29
29
|
* are supported by passing a custom `agents` map in
|
|
30
|
-
* `
|
|
30
|
+
* `IStartBrokerServerOptions.grammarResolverOptions`.
|
|
31
31
|
*/
|
|
32
32
|
export type BrokerUserAgent = string;
|
|
33
33
|
|
|
@@ -40,8 +40,8 @@ export type BrokerUserAgent = string;
|
|
|
40
40
|
* matrix the broker registers on disk.
|
|
41
41
|
*
|
|
42
42
|
* Pattern:
|
|
43
|
-
* - `"<userAgent>:<locale>"` (no version)
|
|
44
|
-
* - `"<userAgent>:<locale>@<version>"` (versioned)
|
|
43
|
+
* - `"<userAgent>:<locale>"` (no version), e.g. `"claude:fr"`, `"default:en"`
|
|
44
|
+
* - `"<userAgent>:<locale>@<version>"` (versioned), e.g. `"claude:fr@v2"`
|
|
45
45
|
*
|
|
46
46
|
* The colon separator is reserved for the `<ua>:<locale>` composition; the
|
|
47
47
|
* `@` separator is reserved for the optional version suffix. Neither
|
|
@@ -94,7 +94,7 @@ export function parseBrokerGrammarStem(stem: string): { locale: BrokerLocale; ve
|
|
|
94
94
|
* JSON files live alongside this module in `src/broker/grammars/` during
|
|
95
95
|
* development and are mirrored under `dist/broker/grammars/` at build time
|
|
96
96
|
* by `scripts/copy-assets.mjs`. Adding a new `(userAgent, locale)` pair is
|
|
97
|
-
* just a matter of dropping a new JSON file
|
|
97
|
+
* just a matter of dropping a new JSON file: no code change required.
|
|
98
98
|
*/
|
|
99
99
|
const GRAMMARS_DIR = join(dirname(fileURLToPath(import.meta.url)), "grammars");
|
|
100
100
|
|
|
@@ -132,9 +132,9 @@ export function loadBrokerGrammar(userAgent: BrokerUserAgent, locale: BrokerLoca
|
|
|
132
132
|
*
|
|
133
133
|
* Used by the broker server at startup to bulk-register both the packaged
|
|
134
134
|
* grammars and any local overrides. No hard-coded list of supported
|
|
135
|
-
* user-agents or locales
|
|
135
|
+
* user-agents or locales, adding a new grammar is dropping a JSON file.
|
|
136
136
|
*/
|
|
137
|
-
export interface
|
|
137
|
+
export interface IBrokerGrammarEntry {
|
|
138
138
|
userAgent: BrokerUserAgent;
|
|
139
139
|
locale: BrokerLocale;
|
|
140
140
|
/** Set only for filenames carrying an `@<version>` suffix. */
|
|
@@ -144,7 +144,7 @@ export interface BrokerGrammarEntry {
|
|
|
144
144
|
grammar: McpGrammar;
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
export function* iterBrokerGrammarsFrom(grammarsDir: string): Generator<
|
|
147
|
+
export function* iterBrokerGrammarsFrom(grammarsDir: string): Generator<IBrokerGrammarEntry> {
|
|
148
148
|
if (!existsSync(grammarsDir)) return;
|
|
149
149
|
|
|
150
150
|
const userAgents = readdirSync(grammarsDir).sort();
|
|
@@ -186,12 +186,15 @@ export function* iterBrokerGrammarsFrom(grammarsDir: string): Generator<BrokerGr
|
|
|
186
186
|
* mcp-broker package). Equivalent to `iterBrokerGrammarsFrom(<packaged-dir>)`.
|
|
187
187
|
*
|
|
188
188
|
* For local user overrides, see {@link iterBrokerGrammarsFrom} with a custom
|
|
189
|
-
* directory
|
|
189
|
+
* directory, typically `.mcp-broker/grammars/` next to the config file.
|
|
190
190
|
*/
|
|
191
|
-
export function* iterAvailableBrokerGrammars(): Generator<
|
|
191
|
+
export function* iterAvailableBrokerGrammars(): Generator<IBrokerGrammarEntry> {
|
|
192
192
|
yield* iterBrokerGrammarsFrom(GRAMMARS_DIR);
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
/** @deprecated Use {@link IBrokerGrammarEntry}. */
|
|
196
|
+
export type BrokerGrammarEntry = IBrokerGrammarEntry;
|
|
197
|
+
|
|
195
198
|
// ---------------------------------------------------------------------------
|
|
196
199
|
// Baseline helpers (used by behaviors to source their inline descriptions)
|
|
197
200
|
// ---------------------------------------------------------------------------
|
|
@@ -203,20 +206,20 @@ export function* iterAvailableBrokerGrammars(): Generator<BrokerGrammarEntry> {
|
|
|
203
206
|
* Conventionally this is `default:en`. Session-specific grammars selected by
|
|
204
207
|
* the resolver override individual entries on top of this baseline.
|
|
205
208
|
*
|
|
206
|
-
* Throws if the JSON resource is missing
|
|
209
|
+
* Throws if the JSON resource is missing: the broker behaviors cannot be
|
|
207
210
|
* built without baseline descriptions.
|
|
208
211
|
*/
|
|
209
212
|
export function brokerBaselineGrammar(): McpGrammar {
|
|
210
213
|
const g = loadBrokerGrammar("default", "en");
|
|
211
214
|
if (!g) {
|
|
212
|
-
throw new Error(`Required baseline broker grammar "default:en" is missing
|
|
215
|
+
throw new Error(`Required baseline broker grammar "default:en" is missing, expected at ${join(GRAMMARS_DIR, "default", "en.json")}.`);
|
|
213
216
|
}
|
|
214
217
|
return g;
|
|
215
218
|
}
|
|
216
219
|
|
|
217
220
|
/**
|
|
218
221
|
* Convenience accessor for a baseline tool description. Throws when the
|
|
219
|
-
* tool is not listed in the baseline grammar
|
|
222
|
+
* tool is not listed in the baseline grammar, i.e. the JSON file is missing
|
|
220
223
|
* an entry for a tool the code knows about.
|
|
221
224
|
*/
|
|
222
225
|
export function brokerBaselineToolDescription(toolName: string): string {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { McpServerBuilder, LoopbackTransport } from "@cyanmycelium/mcp-core";
|
|
2
2
|
import type { GrammarResolverOptions, IMcpServer, IMessageTransport } from "@cyanmycelium/mcp-core";
|
|
3
|
-
import { BrokerInfoBehavior } from "./behaviors/broker.behavior.info
|
|
4
|
-
import { BrokerProvidersBehavior } from "./behaviors/broker.behavior.providers
|
|
5
|
-
import { iterAvailableBrokerGrammars, iterBrokerGrammarsFrom } from "./broker.grammars
|
|
6
|
-
import type {
|
|
3
|
+
import { BrokerInfoBehavior } from "./behaviors/broker.behavior.info";
|
|
4
|
+
import { BrokerProvidersBehavior } from "./behaviors/broker.behavior.providers";
|
|
5
|
+
import { iterAvailableBrokerGrammars, iterBrokerGrammarsFrom } from "./broker.grammars";
|
|
6
|
+
import type { IBrokerContext } from "./broker.context";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Reserved provider slot name under which the broker exposes itself as an MCP
|
|
@@ -19,7 +19,7 @@ export const BROKER_PROVIDER_NAME = "_broker";
|
|
|
19
19
|
* replace either resolver with custom logic without touching mcp-broker
|
|
20
20
|
* internals.
|
|
21
21
|
*/
|
|
22
|
-
export interface
|
|
22
|
+
export interface IStartBrokerServerOptions {
|
|
23
23
|
/**
|
|
24
24
|
* Overrides for the built-in grammar resolver from `@cyanmycelium/mcp-core`.
|
|
25
25
|
*
|
|
@@ -44,7 +44,7 @@ export interface StartBrokerServerOptions {
|
|
|
44
44
|
* via `withGrammar(brokerGrammarKey(ua, locale), grammar)`. The
|
|
45
45
|
* candidate-chain resolution implemented by `McpServer.initialize` in
|
|
46
46
|
* mcp-core@0.3.0 then walks the chain and merges the four layers
|
|
47
|
-
* (behavior, adapter, static, store) for the first matching key
|
|
47
|
+
* (behavior, adapter, static, store) for the first matching key ,
|
|
48
48
|
* the old hand-rolled pre-merge cascade is no longer needed.
|
|
49
49
|
*
|
|
50
50
|
* When `undefined` (default), only the packaged grammars are loaded.
|
|
@@ -69,8 +69,8 @@ export interface StartBrokerServerOptions {
|
|
|
69
69
|
* loopback transport to attach to the tunnel.
|
|
70
70
|
*/
|
|
71
71
|
export async function startBrokerServer(
|
|
72
|
-
context:
|
|
73
|
-
options:
|
|
72
|
+
context: IBrokerContext,
|
|
73
|
+
options: IStartBrokerServerOptions = {}
|
|
74
74
|
): Promise<{
|
|
75
75
|
server: IMcpServer;
|
|
76
76
|
clientTransport: IMessageTransport;
|
|
@@ -93,7 +93,7 @@ export async function startBrokerServer(
|
|
|
93
93
|
// Register every `(userAgent, locale)` JSON found on disk as a raw
|
|
94
94
|
// grammar layer. The candidate-chain resolution in
|
|
95
95
|
// McpServer.initialize (mcp-core@0.3.0) walks the resolver's chain
|
|
96
|
-
// and merges all matching layers
|
|
96
|
+
// and merges all matching layers: so partial user-agent files no
|
|
97
97
|
// longer need to be pre-merged with the default-locale baseline at
|
|
98
98
|
// boot. Local overrides come after packaged entries; identical keys
|
|
99
99
|
// get overlaid via the registry's last-write-wins.
|
|
@@ -124,3 +124,6 @@ export async function startBrokerServer(
|
|
|
124
124
|
|
|
125
125
|
return { server, clientTransport: clientEnd };
|
|
126
126
|
}
|
|
127
|
+
|
|
128
|
+
/** @deprecated Use {@link IStartBrokerServerOptions}. */
|
|
129
|
+
export type StartBrokerServerOptions = IStartBrokerServerOptions;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"description": "List every provider slot on this broker, connected or not. Each entry tells you the transport kind, current client count, and how many requests are in flight. Use this to discover what you can route to."
|
|
8
8
|
},
|
|
9
9
|
"provider_status": {
|
|
10
|
-
"description": "Get the full status of one provider slot by name. Returns an error if the slot does not exist
|
|
10
|
+
"description": "Get the full status of one provider slot by name. Returns an error if the slot does not exist, start with providers_list if you are not sure of the name.",
|
|
11
11
|
"properties": {
|
|
12
12
|
"name": "The exact provider slot name. Case-sensitive. Use providers_list to discover valid values."
|
|
13
13
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"description": "Liste tous les emplacements de providers de ce broker, connectés ou non. Chaque entrée indique le type de transport, le nombre de clients actuels et le nombre de requêtes en cours. Utilise-le pour découvrir vers qui tu peux router."
|
|
8
8
|
},
|
|
9
9
|
"provider_status": {
|
|
10
|
-
"description": "Récupère le statut complet d'un emplacement de provider par son nom. Retourne une erreur si l'emplacement n'existe pas
|
|
10
|
+
"description": "Récupère le statut complet d'un emplacement de provider par son nom. Retourne une erreur si l'emplacement n'existe pas, commence par providers_list si tu n'es pas sûr du nom.",
|
|
11
11
|
"properties": {
|
|
12
12
|
"name": "Le nom exact de l'emplacement provider. Sensible à la casse. Utilise providers_list pour découvrir les valeurs valides."
|
|
13
13
|
}
|
package/src/broker/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { BrokerInfoBehavior } from "./behaviors/broker.behavior.info
|
|
2
|
-
export { BrokerProvidersBehavior } from "./behaviors/broker.behavior.providers
|
|
3
|
-
export { BrokerInfoAdapter, BROKER_INFO_URI } from "./adapters/broker.adapter.info
|
|
4
|
-
export { BrokerProvidersAdapter, PROVIDERS_URI, PROVIDER_URI_TEMPLATE } from "./adapters/broker.adapter.providers
|
|
5
|
-
export { startBrokerServer, BROKER_PROVIDER_NAME } from "./broker.server
|
|
6
|
-
export type { StartBrokerServerOptions } from "./broker.server
|
|
1
|
+
export { BrokerInfoBehavior } from "./behaviors/broker.behavior.info";
|
|
2
|
+
export { BrokerProvidersBehavior } from "./behaviors/broker.behavior.providers";
|
|
3
|
+
export { BrokerInfoAdapter, BROKER_INFO_URI } from "./adapters/broker.adapter.info";
|
|
4
|
+
export { BrokerProvidersAdapter, PROVIDERS_URI, PROVIDER_URI_TEMPLATE } from "./adapters/broker.adapter.providers";
|
|
5
|
+
export { startBrokerServer, BROKER_PROVIDER_NAME } from "./broker.server";
|
|
6
|
+
export type { IStartBrokerServerOptions, StartBrokerServerOptions } from "./broker.server";
|
|
7
7
|
export {
|
|
8
8
|
brokerBaselineGrammar,
|
|
9
9
|
brokerBaselinePropertyDescription,
|
|
@@ -17,6 +17,6 @@ export {
|
|
|
17
17
|
iterBrokerGrammarsFrom,
|
|
18
18
|
loadBrokerGrammar,
|
|
19
19
|
parseBrokerGrammarStem,
|
|
20
|
-
} from "./broker.grammars
|
|
21
|
-
export type { BrokerGrammarEntry, BrokerLocale, BrokerUserAgent } from "./broker.grammars
|
|
22
|
-
export type { BrokerContext, BrokerProviderInfo, BrokerProviderTransport } from "./broker.context
|
|
20
|
+
} from "./broker.grammars";
|
|
21
|
+
export type { IBrokerGrammarEntry, BrokerGrammarEntry, BrokerLocale, BrokerUserAgent } from "./broker.grammars";
|
|
22
|
+
export type { IBrokerContext, IBrokerProviderInfo, BrokerContext, BrokerProviderInfo, BrokerProviderTransport } from "./broker.context";
|
package/src/config.ts
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
|
+
import type { IAuthorizationPolicyConfig } from "./authorization/index";
|
|
4
|
+
|
|
5
|
+
export interface IBrokerAuthConfig extends IAuthorizationPolicyConfig {
|
|
6
|
+
/** Master switch. Absent/`false` keeps the broker unauthenticated. */
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
/** Public origin the broker is reached at (e.g. `https://mcp.example.com`). */
|
|
9
|
+
publicBaseUrl?: string;
|
|
10
|
+
/** Authorization server issuer URL(s) advertised in the metadata. */
|
|
11
|
+
authorizationServers?: string[];
|
|
12
|
+
/** URL of the authorization server's JWKS document. */
|
|
13
|
+
jwks?: string;
|
|
14
|
+
/** Expected token issuer. Defaults to the sole `authorizationServers` entry. */
|
|
15
|
+
issuer?: string;
|
|
16
|
+
/** Scopes advertised in the metadata `scopes_supported`. */
|
|
17
|
+
scopesSupported?: string[];
|
|
18
|
+
/** Baseline scope(s) required to reach any slot. */
|
|
19
|
+
requiredScopes?: string[];
|
|
20
|
+
/** Per-slot required-scope overrides (e.g. an admin scope for `_broker`). */
|
|
21
|
+
perSlotScopes?: Record<string, string[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Per-provider scope requirements for the `_all` aggregate. Deprecated in
|
|
24
|
+
* favor of hierarchical policy assignments.
|
|
25
|
+
*/
|
|
26
|
+
providerScopes?: Record<string, string[]>;
|
|
27
|
+
/**
|
|
28
|
+
* Shared secret every provider must present to occupy a slot. Independent
|
|
29
|
+
* of client authorization.
|
|
30
|
+
*/
|
|
31
|
+
providerSecret?: string;
|
|
32
|
+
}
|
|
3
33
|
|
|
4
34
|
/**
|
|
5
35
|
* Shape of the optional JSON config file consumed by `bin.ts` at startup.
|
|
@@ -18,7 +48,7 @@ import { dirname, resolve } from "node:path";
|
|
|
18
48
|
* }
|
|
19
49
|
* ```
|
|
20
50
|
*/
|
|
21
|
-
export interface
|
|
51
|
+
export interface IBrokerConfig {
|
|
22
52
|
/** TCP port. Maps to `MCP_BROKER_PORT`. */
|
|
23
53
|
port?: number;
|
|
24
54
|
|
|
@@ -37,6 +67,40 @@ export interface BrokerConfig {
|
|
|
37
67
|
/** Logical broker name reported by `broker_info`. */
|
|
38
68
|
brokerName?: string;
|
|
39
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Browser origins allowed to reach `/<slot>/mcp`.
|
|
72
|
+
*
|
|
73
|
+
* Absent means no browser origin is accepted: a request carrying an
|
|
74
|
+
* `Origin` header gets `403`, one carrying none (Claude Desktop, MCP
|
|
75
|
+
* Inspector, any server-side SDK) always passes. The MCP specification asks
|
|
76
|
+
* for this check, since without it any web page loaded in a browser that
|
|
77
|
+
* can reach the broker could drive it.
|
|
78
|
+
*
|
|
79
|
+
* An array lists origins matched **exactly**. An object supplies a regular
|
|
80
|
+
* expression tested against the whole `Origin` header. Also maps to
|
|
81
|
+
* `MCP_BROKER_ALLOWED_ORIGINS` (comma-separated list form only; a pattern
|
|
82
|
+
* needs the config file).
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```json
|
|
86
|
+
* { "allowedOrigins": ["https://app.example.com", "http://localhost:5173"] }
|
|
87
|
+
* { "allowedOrigins": { "pattern": "^https://[a-z0-9-]+\\.example\\.com$" } }
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
allowedOrigins?: string[] | { pattern: string; flags?: string };
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* OAuth 2.1 resource-server authorization. When `enabled` is `true`, every
|
|
94
|
+
* HTTP client request to a slot must carry a valid `Authorization: Bearer`
|
|
95
|
+
* token issued for that slot, and the broker publishes Protected Resource
|
|
96
|
+
* Metadata (RFC 9728). Absent/`false` ⇒ no authentication (trusted-network
|
|
97
|
+
* mode, the historical behavior).
|
|
98
|
+
*
|
|
99
|
+
* Scalars also map to env vars (which win): `MCP_BROKER_AUTH_ENABLED`,
|
|
100
|
+
* `MCP_BROKER_PUBLIC_BASE_URL`, `MCP_BROKER_JWKS`, `MCP_BROKER_ISSUER`.
|
|
101
|
+
*/
|
|
102
|
+
auth?: IBrokerAuthConfig;
|
|
103
|
+
|
|
40
104
|
/** URL paths (override the defaults). */
|
|
41
105
|
paths?: {
|
|
42
106
|
provider?: string;
|
|
@@ -119,8 +183,8 @@ export interface BrokerConfig {
|
|
|
119
183
|
* (the directory containing the config file when one was found, otherwise
|
|
120
184
|
* `process.cwd()`).
|
|
121
185
|
*/
|
|
122
|
-
export interface
|
|
123
|
-
config:
|
|
186
|
+
export interface ILoadedBrokerConfig {
|
|
187
|
+
config: IBrokerConfig;
|
|
124
188
|
baseDir: string;
|
|
125
189
|
/** Absolute path of the config file that was loaded, or `null` if none. */
|
|
126
190
|
sourcePath: string | null;
|
|
@@ -142,17 +206,17 @@ export const LEGACY_CONFIG_FILENAME = "mcp-broker.config.json";
|
|
|
142
206
|
* 1. The `path` argument when provided (explicit override).
|
|
143
207
|
* 2. The `MCP_BROKER_CONFIG` env var.
|
|
144
208
|
* 3. `./.mcp-broker/config.json` relative to `process.cwd()`.
|
|
145
|
-
* 4. `./mcp-broker.config.json` relative to `process.cwd()` (legacy layout
|
|
209
|
+
* 4. `./mcp-broker.config.json` relative to `process.cwd()` (legacy layout ,
|
|
146
210
|
* a deprecation warning is written to stderr).
|
|
147
211
|
*
|
|
148
212
|
* When no file is found, returns the built-in empty config with
|
|
149
213
|
* `baseDir = process.cwd()`. On invalid JSON, logs a warning to stderr and
|
|
150
|
-
* returns the same empty config
|
|
214
|
+
* returns the same empty config, never throws.
|
|
151
215
|
*
|
|
152
216
|
* Paths inside the config file are intended to be resolved against
|
|
153
|
-
* {@link
|
|
217
|
+
* {@link ILoadedBrokerConfig.baseDir} by the consumer.
|
|
154
218
|
*/
|
|
155
|
-
export function loadBrokerConfig(path?: string):
|
|
219
|
+
export function loadBrokerConfig(path?: string): ILoadedBrokerConfig {
|
|
156
220
|
const cwd = process.cwd();
|
|
157
221
|
const envPath = process.env["MCP_BROKER_CONFIG"];
|
|
158
222
|
|
|
@@ -183,10 +247,19 @@ export function loadBrokerConfig(path?: string): LoadedBrokerConfig {
|
|
|
183
247
|
|
|
184
248
|
try {
|
|
185
249
|
const raw = readFileSync(sourcePath, "utf-8");
|
|
186
|
-
const config = JSON.parse(raw) as
|
|
250
|
+
const config = JSON.parse(raw) as IBrokerConfig;
|
|
187
251
|
return { config, baseDir, sourcePath };
|
|
188
252
|
} catch (err) {
|
|
189
253
|
process.stderr.write(`[mcp-broker] Failed to parse config file at ${sourcePath}: ${(err as Error).message}\n`);
|
|
190
254
|
return { config: {}, baseDir: cwd, sourcePath: null };
|
|
191
255
|
}
|
|
192
256
|
}
|
|
257
|
+
|
|
258
|
+
/** @deprecated Use {@link IBrokerAuthConfig}. */
|
|
259
|
+
export type BrokerAuthConfig = IBrokerAuthConfig;
|
|
260
|
+
|
|
261
|
+
/** @deprecated Use {@link IBrokerConfig}. */
|
|
262
|
+
export type BrokerConfig = IBrokerConfig;
|
|
263
|
+
|
|
264
|
+
/** @deprecated Use {@link ILoadedBrokerConfig}. */
|
|
265
|
+
export type LoadedBrokerConfig = ILoadedBrokerConfig;
|
package/src/index.ts
CHANGED
|
@@ -1,26 +1,127 @@
|
|
|
1
|
-
export { WsTunnel } from "./ws.tunnel
|
|
2
|
-
export { WsTunnelBuilder } from "./ws.tunnel.builder
|
|
3
|
-
export type { WsTunnelOptions, StaticMount } from "./ws.
|
|
4
|
-
export { StdioUpstream } from "./stdio.upstream
|
|
5
|
-
export type { StdioUpstreamConfig } from "./stdio.upstream
|
|
6
|
-
export { RemoteUpstream } from "./remote.upstream
|
|
7
|
-
export type { RemoteUpstreamConfig } from "./remote.upstream
|
|
8
|
-
export type { Upstream } from "./upstream
|
|
1
|
+
export { WsTunnel } from "./ws/ws.tunnel";
|
|
2
|
+
export { WsTunnelBuilder } from "./ws/ws.tunnel.builder";
|
|
3
|
+
export type { AllowedOrigins, IInternalClient, IWsTunnelOptions, IStaticMount, InternalClient, WsTunnelOptions, StaticMount } from "./ws/ws.interfaces";
|
|
4
|
+
export { StdioUpstream } from "./stdio.upstream";
|
|
5
|
+
export type { IStdioUpstreamConfig, StdioUpstreamConfig } from "./stdio.upstream";
|
|
6
|
+
export { RemoteUpstream } from "./remote.upstream";
|
|
7
|
+
export type { IRemoteUpstreamConfig, RemoteUpstreamConfig } from "./remote.upstream";
|
|
8
|
+
export type { IUpstream, Upstream } from "./upstream";
|
|
9
9
|
|
|
10
|
-
// `.mcpb` bundle loading
|
|
11
|
-
export { loadMcpbBundle } from "./mcpb.loader
|
|
12
|
-
export type { McpbBundleConfig } from "./mcpb.loader
|
|
13
|
-
export { unzipMcpb } from "./mcpb.unzip
|
|
10
|
+
// `.mcpb` bundle loading, verifies + unpacks a bundle into a stdio upstream.
|
|
11
|
+
export { loadMcpbBundle } from "./mcpb/mcpb.loader";
|
|
12
|
+
export type { IMcpbBundleConfig, McpbBundleConfig } from "./mcpb/mcpb.loader";
|
|
13
|
+
export { unzipMcpb } from "./mcpb/mcpb.unzip";
|
|
14
14
|
|
|
15
|
-
// Broker introspection
|
|
16
|
-
export { BrokerInfoBehavior, BrokerProvidersBehavior, startBrokerServer, BROKER_PROVIDER_NAME } from "./broker/index
|
|
17
|
-
export type { StartBrokerServerOptions } from "./broker/index
|
|
18
|
-
export { brokerGrammarKey, iterAvailableBrokerGrammars, iterBrokerGrammarsFrom, loadBrokerGrammar } from "./broker/index
|
|
19
|
-
export type {
|
|
15
|
+
// Broker introspection, tier 1.
|
|
16
|
+
export { BrokerInfoBehavior, BrokerProvidersBehavior, startBrokerServer, BROKER_PROVIDER_NAME } from "./broker/index";
|
|
17
|
+
export type { IStartBrokerServerOptions, StartBrokerServerOptions } from "./broker/index";
|
|
18
|
+
export { brokerGrammarKey, iterAvailableBrokerGrammars, iterBrokerGrammarsFrom, loadBrokerGrammar } from "./broker/index";
|
|
19
|
+
export type {
|
|
20
|
+
IBrokerContext,
|
|
21
|
+
IBrokerGrammarEntry,
|
|
22
|
+
IBrokerProviderInfo,
|
|
23
|
+
BrokerContext,
|
|
24
|
+
BrokerGrammarEntry,
|
|
25
|
+
BrokerProviderInfo,
|
|
26
|
+
BrokerProviderTransport,
|
|
27
|
+
BrokerLocale,
|
|
28
|
+
BrokerUserAgent,
|
|
29
|
+
} from "./broker/index";
|
|
20
30
|
|
|
21
|
-
export { VERSION, PACKAGE_NAME } from "./version
|
|
31
|
+
export { VERSION, PACKAGE_NAME } from "./version";
|
|
32
|
+
|
|
33
|
+
// OAuth 2.1 resource-server authorization.
|
|
34
|
+
export {
|
|
35
|
+
AuthError,
|
|
36
|
+
scopesOf,
|
|
37
|
+
JwtTokenValidator,
|
|
38
|
+
buildResourceMetadata,
|
|
39
|
+
HttpAuthGuard,
|
|
40
|
+
buildJwtAuth,
|
|
41
|
+
normalizeProviderAuthentication,
|
|
42
|
+
providerMayPublish,
|
|
43
|
+
SharedSecretProviderAuthenticator,
|
|
44
|
+
} from "./auth/index";
|
|
45
|
+
export type {
|
|
46
|
+
IAccessTokenClaims,
|
|
47
|
+
ITokenValidator,
|
|
48
|
+
IPrincipal,
|
|
49
|
+
IResolvedAuth,
|
|
50
|
+
IJwtValidatorOptions,
|
|
51
|
+
IProtectedResourceMetadata,
|
|
52
|
+
IJwtAuthOptions,
|
|
53
|
+
IProviderAuthenticator,
|
|
54
|
+
IProviderPrincipal,
|
|
55
|
+
AccessTokenClaims,
|
|
56
|
+
TokenValidator,
|
|
57
|
+
AuthErrorCode,
|
|
58
|
+
Principal,
|
|
59
|
+
ResolvedAuth,
|
|
60
|
+
JwtValidatorOptions,
|
|
61
|
+
ProtectedResourceMetadata,
|
|
62
|
+
JwtAuthOptions,
|
|
63
|
+
ProviderAuthenticator,
|
|
64
|
+
AggregateScopeFilter,
|
|
65
|
+
ProviderAuthenticationResult,
|
|
66
|
+
ProviderAuthenticatorReturn,
|
|
67
|
+
ProviderPrincipal,
|
|
68
|
+
} from "./auth/index";
|
|
69
|
+
|
|
70
|
+
// Hierarchical, domain-neutral authorization.
|
|
71
|
+
export {
|
|
72
|
+
ConfigPolicyEngine,
|
|
73
|
+
ConfiguredCapabilityClassifier,
|
|
74
|
+
DefaultSlotResourceResolver,
|
|
75
|
+
JwtSubjectMapper,
|
|
76
|
+
ResourcePath,
|
|
77
|
+
ResourcePathPattern,
|
|
78
|
+
SubjectMappingError,
|
|
79
|
+
authorizationWithEngine,
|
|
80
|
+
compileAuthorizationPolicy,
|
|
81
|
+
hasAuthorizationPolicies,
|
|
82
|
+
validateCapability,
|
|
83
|
+
} from "./authorization/index";
|
|
84
|
+
export type {
|
|
85
|
+
IAuditContext,
|
|
86
|
+
IAuthorizationAuditConfig,
|
|
87
|
+
IAuthorizationAuditEvent,
|
|
88
|
+
IAuthorizationDecision,
|
|
89
|
+
IAuthorizationPolicyConfig,
|
|
90
|
+
IAuthorizationRequest,
|
|
91
|
+
IAuthorizationSubject,
|
|
92
|
+
ICapabilityClassifier,
|
|
93
|
+
IClassifiedCapability,
|
|
94
|
+
IDenyPolicy,
|
|
95
|
+
IMcpOperation,
|
|
96
|
+
IPolicyAssignment,
|
|
97
|
+
IPolicyAuthorization,
|
|
98
|
+
IPolicyEngine,
|
|
99
|
+
IRoleDefinition,
|
|
100
|
+
ISlotResourceResolver,
|
|
101
|
+
ISubjectMapper,
|
|
102
|
+
ISubjectMappingConfig,
|
|
103
|
+
AuditContext,
|
|
104
|
+
AuthorizationAuditConfig,
|
|
105
|
+
AuthorizationAuditEvent,
|
|
106
|
+
AuthorizationDecision,
|
|
107
|
+
AuthorizationDecisionReason,
|
|
108
|
+
AuthorizationPolicyConfig,
|
|
109
|
+
AuthorizationRequest,
|
|
110
|
+
AuthorizationSubject,
|
|
111
|
+
CapabilityClassifier,
|
|
112
|
+
ClassifiedCapability,
|
|
113
|
+
DenyPolicy,
|
|
114
|
+
McpOperation,
|
|
115
|
+
PolicyAssignment,
|
|
116
|
+
PolicyAuthorization,
|
|
117
|
+
PolicyEngine,
|
|
118
|
+
RoleDefinition,
|
|
119
|
+
SlotResourceResolver,
|
|
120
|
+
SubjectMapper,
|
|
121
|
+
SubjectMappingConfig,
|
|
122
|
+
} from "./authorization/index";
|
|
22
123
|
|
|
23
124
|
// JSON config file used by `bin.ts` at startup. Exported so a programmatic
|
|
24
125
|
// embedder can re-use the same loader against a custom path.
|
|
25
|
-
export { loadBrokerConfig, DEFAULT_CONFIG_FILENAME } from "./config
|
|
26
|
-
export type { BrokerConfig } from "./config
|
|
126
|
+
export { loadBrokerConfig, DEFAULT_CONFIG_FILENAME } from "./config";
|
|
127
|
+
export type { IBrokerAuthConfig, IBrokerConfig, ILoadedBrokerConfig, BrokerAuthConfig, BrokerConfig, LoadedBrokerConfig } from "./config";
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Loads a local `.mcpb` bundle into
|
|
2
|
+
* Loads a local `.mcpb` bundle into an {@link IStdioUpstreamConfig}.
|
|
3
3
|
*
|
|
4
4
|
* A `.mcpb` bundle is a ZIP holding a `manifest.json` whose `server.mcp_config`
|
|
5
5
|
* describes a stdio MCP server process. The broker:
|
|
6
6
|
*
|
|
7
7
|
* 1. verifies a **detached signature** of the `.mcpb` file against a trusted
|
|
8
|
-
* public key (PEM)
|
|
8
|
+
* public key (PEM), integrity *and* provenance, using `node:crypto` only;
|
|
9
9
|
* 2. unpacks the archive;
|
|
10
10
|
* 3. reads the manifest, expands the `mcp_config` placeholders, and produces a
|
|
11
|
-
* `
|
|
11
|
+
* `IStdioUpstreamConfig` that the existing upstream wiring spawns.
|
|
12
12
|
*
|
|
13
13
|
* The broker stays compatible with the `.mcpb` format without depending on the
|
|
14
14
|
* `@anthropic-ai/mcpb` package: the bundle's *own* (native PKCS#7) signature is
|
|
15
|
-
* never parsed
|
|
15
|
+
* never parsed: the detached signature layer is the broker's trust anchor.
|
|
16
16
|
*
|
|
17
17
|
* Any failure (missing files, bad signature, malformed manifest, missing
|
|
18
18
|
* `user_config` value) is logged and yields `null`: the bundle is refused and
|
|
@@ -22,11 +22,11 @@ import { createPublicKey, verify, X509Certificate, type KeyObject } from "node:c
|
|
|
22
22
|
import { existsSync, readFileSync, rmSync } from "node:fs";
|
|
23
23
|
import { homedir } from "node:os";
|
|
24
24
|
import { join, resolve, sep } from "node:path";
|
|
25
|
-
import { unzipMcpb } from "./mcpb.unzip
|
|
26
|
-
import type {
|
|
25
|
+
import { unzipMcpb } from "./mcpb.unzip";
|
|
26
|
+
import type { IStdioUpstreamConfig } from "../stdio.upstream";
|
|
27
27
|
|
|
28
28
|
/** A `.mcpb` bundle entry from the broker config file. */
|
|
29
|
-
export interface
|
|
29
|
+
export interface IMcpbBundleConfig {
|
|
30
30
|
/** Provider slot name the bundle is bound to. */
|
|
31
31
|
name: string;
|
|
32
32
|
/** Path to the `.mcpb` file. */
|
|
@@ -41,7 +41,7 @@ export interface McpbBundleConfig {
|
|
|
41
41
|
aggregate?: boolean;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
interface
|
|
44
|
+
interface IMcpConfig {
|
|
45
45
|
command?: string;
|
|
46
46
|
args?: string[];
|
|
47
47
|
env?: Record<string, string>;
|
|
@@ -65,7 +65,7 @@ function verifyDetachedSignature(data: Buffer, signature: Buffer, publicKey: Key
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/** Resolves a single `${...}` placeholder key, or `undefined` when unknown. */
|
|
68
|
-
function resolvePlaceholder(key: string, dirname: string, userConfig:
|
|
68
|
+
function resolvePlaceholder(key: string, dirname: string, userConfig: IMcpbBundleConfig["userConfig"]): string | number | boolean | Array<string | number> | undefined {
|
|
69
69
|
if (key === "__dirname") return dirname;
|
|
70
70
|
if (key === "HOME") return homedir();
|
|
71
71
|
if (key === "DESKTOP") return join(homedir(), "Desktop");
|
|
@@ -82,17 +82,17 @@ function resolvePlaceholder(key: string, dirname: string, userConfig: McpbBundle
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/** Expands placeholders in a scalar string (command, env value). */
|
|
85
|
-
function expandScalar(input: string, dirname: string, userConfig:
|
|
85
|
+
function expandScalar(input: string, dirname: string, userConfig: IMcpbBundleConfig["userConfig"]): string {
|
|
86
86
|
return input.replace(/\$\{([^}]+)\}/g, (match, key: string) => {
|
|
87
87
|
const value = resolvePlaceholder(key, dirname, userConfig);
|
|
88
|
-
if (value === undefined) return match; // unknown placeholder
|
|
88
|
+
if (value === undefined) return match; // unknown placeholder, leave verbatim
|
|
89
89
|
if (Array.isArray(value)) throw new Error(`placeholder "\${${key}}" is multi-valued and cannot be used here`);
|
|
90
90
|
return String(value);
|
|
91
91
|
});
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
/** Expands one manifest argument; a standalone multi-valued placeholder spreads. */
|
|
95
|
-
function expandArg(arg: string, dirname: string, userConfig:
|
|
95
|
+
function expandArg(arg: string, dirname: string, userConfig: IMcpbBundleConfig["userConfig"]): string[] {
|
|
96
96
|
const standalone = /^\$\{(user_config\.[^}]+)\}$/.exec(arg);
|
|
97
97
|
if (standalone) {
|
|
98
98
|
const value = resolvePlaceholder(standalone[1], dirname, userConfig);
|
|
@@ -103,13 +103,13 @@ function expandArg(arg: string, dirname: string, userConfig: McpbBundleConfig["u
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
/**
|
|
106
|
-
* Verifies, unpacks and resolves a `.mcpb` bundle into
|
|
106
|
+
* Verifies, unpacks and resolves a `.mcpb` bundle into an `IStdioUpstreamConfig`.
|
|
107
107
|
*
|
|
108
108
|
* @param cfg The bundle entry from the broker config.
|
|
109
109
|
* @param baseDir Directory the bundle paths are resolved against.
|
|
110
110
|
* @returns A ready upstream config, or `null` when the bundle is refused.
|
|
111
111
|
*/
|
|
112
|
-
export async function loadMcpbBundle(cfg:
|
|
112
|
+
export async function loadMcpbBundle(cfg: IMcpbBundleConfig, baseDir: string): Promise<IStdioUpstreamConfig | null> {
|
|
113
113
|
const tag = `[mcp-broker] mcpb bundle "${cfg.name}"`;
|
|
114
114
|
try {
|
|
115
115
|
const mcpbPath = resolve(baseDir, cfg.path);
|
|
@@ -122,7 +122,7 @@ export async function loadMcpbBundle(cfg: McpbBundleConfig, baseDir: string): Pr
|
|
|
122
122
|
["signature", signaturePath],
|
|
123
123
|
] as const) {
|
|
124
124
|
if (!existsSync(file)) {
|
|
125
|
-
console.error(`${tag}: ${label} file not found at ${file}
|
|
125
|
+
console.error(`${tag}: ${label} file not found at ${file}, bundle refused.`);
|
|
126
126
|
return null;
|
|
127
127
|
}
|
|
128
128
|
}
|
|
@@ -132,7 +132,7 @@ export async function loadMcpbBundle(cfg: McpbBundleConfig, baseDir: string): Pr
|
|
|
132
132
|
const signatureBytes = readFileSync(signaturePath);
|
|
133
133
|
const publicKey = loadPublicKey(readFileSync(publicKeyPath, "utf8"));
|
|
134
134
|
if (!verifyDetachedSignature(bundleBytes, signatureBytes, publicKey)) {
|
|
135
|
-
console.error(`${tag}: detached signature is invalid for the configured public key
|
|
135
|
+
console.error(`${tag}: detached signature is invalid for the configured public key, bundle refused.`);
|
|
136
136
|
return null;
|
|
137
137
|
}
|
|
138
138
|
|
|
@@ -144,13 +144,13 @@ export async function loadMcpbBundle(cfg: McpbBundleConfig, baseDir: string): Pr
|
|
|
144
144
|
// ── Manifest → mcp_config ───────────────────────────────────────────
|
|
145
145
|
const manifestPath = join(outputDir, "manifest.json");
|
|
146
146
|
if (!existsSync(manifestPath)) {
|
|
147
|
-
console.error(`${tag}: manifest.json missing inside the bundle
|
|
147
|
+
console.error(`${tag}: manifest.json missing inside the bundle, bundle refused.`);
|
|
148
148
|
return null;
|
|
149
149
|
}
|
|
150
|
-
const manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as { server?: { mcp_config?:
|
|
150
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as { server?: { mcp_config?: IMcpConfig } };
|
|
151
151
|
const mcpConfig = manifest.server?.mcp_config;
|
|
152
152
|
if (!mcpConfig) {
|
|
153
|
-
console.error(`${tag}: manifest has no server.mcp_config
|
|
153
|
+
console.error(`${tag}: manifest has no server.mcp_config, bundle refused.`);
|
|
154
154
|
return null;
|
|
155
155
|
}
|
|
156
156
|
|
|
@@ -160,7 +160,7 @@ export async function loadMcpbBundle(cfg: McpbBundleConfig, baseDir: string): Pr
|
|
|
160
160
|
const rawArgs = override?.args ?? mcpConfig.args ?? [];
|
|
161
161
|
const rawEnv = { ...mcpConfig.env, ...override?.env };
|
|
162
162
|
if (!command) {
|
|
163
|
-
console.error(`${tag}: manifest mcp_config has no command
|
|
163
|
+
console.error(`${tag}: manifest mcp_config has no command, bundle refused.`);
|
|
164
164
|
return null;
|
|
165
165
|
}
|
|
166
166
|
|
|
@@ -180,7 +180,10 @@ export async function loadMcpbBundle(cfg: McpbBundleConfig, baseDir: string): Pr
|
|
|
180
180
|
aggregate: cfg.aggregate ?? true,
|
|
181
181
|
};
|
|
182
182
|
} catch (err) {
|
|
183
|
-
console.error(`${tag}: ${(err as Error).message}
|
|
183
|
+
console.error(`${tag}: ${(err as Error).message}, bundle refused.`);
|
|
184
184
|
return null;
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
|
+
|
|
188
|
+
/** @deprecated Use {@link IMcpbBundleConfig}. */
|
|
189
|
+
export type McpbBundleConfig = IMcpbBundleConfig;
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A `.mcpb` bundle is an ordinary ZIP archive. The broker deliberately avoids
|
|
5
5
|
* the `@anthropic-ai/mcpb` package so it stays compatible with the format
|
|
6
|
-
* without being coupled to Anthropic's tooling
|
|
6
|
+
* without being coupled to Anthropic's tooling, hence this small reader.
|
|
7
7
|
*
|
|
8
8
|
* Supports the two compression methods used in practice: stored (0) and
|
|
9
9
|
* deflate (8). ZIP64 archives are rejected with a clear error (`.mcpb` bundles
|
|
10
10
|
* are small and never need it). Any trailing bytes after the End Of Central
|
|
11
|
-
* Directory record
|
|
11
|
+
* Directory record (e.g. a native PKCS#7 signature block) are ignored, since
|
|
12
12
|
* extraction is driven entirely by the central directory.
|
|
13
13
|
*/
|
|
14
14
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|