@webpieces/mcp-server 0.4.990 → 0.4.992

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.
Files changed (48) hide show
  1. package/README.md +199 -42
  2. package/package.json +7 -6
  3. package/src/McpApiDispatcher.d.ts +6 -3
  4. package/src/McpApiDispatcher.js +6 -2
  5. package/src/McpApiDispatcher.js.map +1 -1
  6. package/src/McpAuth.d.ts +115 -56
  7. package/src/McpAuth.js +180 -111
  8. package/src/McpAuth.js.map +1 -1
  9. package/src/McpAuthorityCompileAssertions.d.ts +12 -2
  10. package/src/McpAuthorityCompileAssertions.js +32 -4
  11. package/src/McpAuthorityCompileAssertions.js.map +1 -1
  12. package/src/McpBindOptions.d.ts +5 -20
  13. package/src/McpBindOptions.js +5 -15
  14. package/src/McpBindOptions.js.map +1 -1
  15. package/src/McpClientSet.d.ts +35 -0
  16. package/src/McpClientSet.js +86 -0
  17. package/src/McpClientSet.js.map +1 -0
  18. package/src/McpDoorAuth.d.ts +117 -0
  19. package/src/McpDoorAuth.js +455 -0
  20. package/src/McpDoorAuth.js.map +1 -0
  21. package/src/McpToolCallRendering.d.ts +5 -4
  22. package/src/McpToolCallRendering.js.map +1 -1
  23. package/src/McpToolCatalog.d.ts +1 -1
  24. package/src/McpToolCatalog.js +1 -1
  25. package/src/McpToolCatalog.js.map +1 -1
  26. package/src/McpToolRegistry.js +8 -2
  27. package/src/McpToolRegistry.js.map +1 -1
  28. package/src/McpToolset.d.ts +115 -0
  29. package/src/McpToolset.js +323 -0
  30. package/src/McpToolset.js.map +1 -0
  31. package/src/WpMcpErrorTranslator.d.ts +5 -7
  32. package/src/WpMcpErrorTranslator.js +7 -13
  33. package/src/WpMcpErrorTranslator.js.map +1 -1
  34. package/src/WpMcpInMemory.d.ts +30 -0
  35. package/src/WpMcpInMemory.js +97 -0
  36. package/src/WpMcpInMemory.js.map +1 -0
  37. package/src/WpMcpRemote.d.ts +23 -0
  38. package/src/WpMcpRemote.js +89 -0
  39. package/src/WpMcpRemote.js.map +1 -0
  40. package/src/WpMcpServer.d.ts +45 -76
  41. package/src/WpMcpServer.js +101 -327
  42. package/src/WpMcpServer.js.map +1 -1
  43. package/src/__tests__/McpTransportFixtures.d.ts +107 -0
  44. package/src/__tests__/McpTransportFixtures.js +266 -0
  45. package/src/__tests__/McpTransportFixtures.js.map +1 -0
  46. package/src/index.d.ts +9 -2
  47. package/src/index.js +17 -1
  48. package/src/index.js.map +1 -1
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.McpProfilesCompileAssertions = exports.McpPrincipalCompileAssertions = exports.McpAuthorityCompileAssertions = void 0;
3
+ exports.McpServerConfigCompileAssertions = exports.McpProfilesCompileAssertions = exports.McpPrincipalCompileAssertions = exports.McpAuthorityCompileAssertions = void 0;
4
4
  const http_routing_1 = require("@webpieces/http-routing");
5
5
  const McpAuth_1 = require("./McpAuth");
6
+ const McpToolset_1 = require("./McpToolset");
6
7
  class ExampleJwtMintRequest {
7
8
  subject;
8
9
  constructor(subject) {
@@ -46,10 +47,37 @@ class McpPrincipalCompileAssertions {
46
47
  exports.McpPrincipalCompileAssertions = McpPrincipalCompileAssertions;
47
48
  /** Profile-selection arity is checked by tsc, not only by runtime specs. */
48
49
  class McpProfilesCompileAssertions {
49
- empty(config) {
50
- // @ts-expect-error an explicit server selection must include at least one group
51
- config.setToolProfiles([]);
50
+ empty(bindings, policy) {
51
+ // @ts-expect-error a toolset must serve at least one profile group
52
+ new McpToolset_1.McpToolset(bindings, [], policy, []);
52
53
  }
53
54
  }
54
55
  exports.McpProfilesCompileAssertions = McpProfilesCompileAssertions;
56
+ /**
57
+ * The OAuth-only setters moved into `mcpOAuth(...)` (#1416) and the toolset owns WHICH tools: none of
58
+ * the old spellings compiles, so an agent upgrading is told by tsc, not by a deprecation it never sees.
59
+ */
60
+ class McpServerConfigCompileAssertions {
61
+ removedOAuthSetters(config, authority) {
62
+ // @ts-expect-error moved to mcpOAuth(authority, authorizationServers, requiredScopes, tokenFormat)
63
+ config.setAccessTokenAuthority(authority);
64
+ // @ts-expect-error moved to mcpOAuth(authority, authorizationServers, ...)
65
+ config.setAuthorizationServers(['https://login.example.test']);
66
+ // @ts-expect-error moved to mcpOAuth(authority, authorizationServers, requiredScopes, ...)
67
+ config.setRequiredScopes(['tools']);
68
+ // @ts-expect-error moved to mcpOAuth(..., maxAccountValidationAgeSeconds)
69
+ config.setMaxAccountValidationAgeSeconds(60);
70
+ }
71
+ noPublicDoor(config) {
72
+ // @ts-expect-error a public mode identifies no caller to authorize tools against
73
+ config.setAuth([{ kind: 'public' }]);
74
+ }
75
+ removedToolsetSetters(config, policy) {
76
+ // @ts-expect-error profiles are the McpToolset's: new McpToolset(bindings, catalogs, policy, profiles)
77
+ config.setToolProfiles(['default']);
78
+ // @ts-expect-error the policy is the McpToolset's: new McpToolset(bindings, catalogs, policy, profiles)
79
+ config.setAuthorizationService(policy);
80
+ }
81
+ }
82
+ exports.McpServerConfigCompileAssertions = McpServerConfigCompileAssertions;
55
83
  //# sourceMappingURL=McpAuthorityCompileAssertions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"McpAuthorityCompileAssertions.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/McpAuthorityCompileAssertions.ts"],"names":[],"mappings":";;;AAAA,0DAAkF;AAClF,uCAAoH;AAEpH,MAAM,qBAAqB;IACK;IAA5B,YAA4B,OAAe;QAAf,YAAO,GAAP,OAAO,CAAQ;IAAG,CAAC;CAClD;AAED,MAAM,eAAe;IAEG;IACA;IAFpB,YACoB,OAAe,EACf,QAAgB;QADhB,YAAO,GAAP,OAAO,CAAQ;QACf,aAAQ,GAAR,QAAQ,CAAQ;IACjC,CAAC;CACP;AAED,uGAAuG;AACvG,MAAa,6BACT,SAAQ,sBAA8B;IAG7B,KAAK,CAAC,IAAI,CAAC,OAA8B;QAC9C,OAAO,IAAI,wBAAS,CAAC,YAAY,OAAO,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC;IAChF,CAAC;IAEQ,KAAK,CAAC,QAAQ,CAAC,KAAa;QACjC,OAAO,IAAI,kCAAmB,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,KAAsB;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAC1C,OAAO,IAAI,8BAAoB,CAAC,OAAO,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC;IAC3F,CAAC;IAED,KAAK,CAAC,iBAAiB,CACnB,YAAoB,EACpB,gBAAwB;QAExB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAC1C,OAAO,IAAI,+BAAqB,CAC5B,cAAc,EACd,6BAA6B,EAC7B,gBAAgB,EAChB,GAAG,EACH,GAAG,GAAG,EAAE,EACR,CAAC,OAAO,CAAC,EACT,GAAG,EACH,IAAI,kCAAmB,CAAC,cAAc,CAAC,CAC1C,CAAC;IACN,CAAC;CACJ;AAjCD,sEAiCC;AAED,+EAA+E;AAC/E,MAAa,6BAA6B;IACtC,KAAK,CAAC,UAAiC;QACnC,iGAAiG;QACjG,UAAU,CAAC,YAAY,CAAC;QACxB,OAAO,UAAU,CAAC,cAAc,CAAC;IACrC,CAAC;CACJ;AAND,sEAMC;AAED,4EAA4E;AAC5E,MAAa,4BAA4B;IACrC,KAAK,CAAC,MAAiC;QACnC,gFAAgF;QAChF,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC;CACJ;AALD,oEAKC","sourcesContent":["import { AuthenticatedCaller, JwtHook, MintedJwt } from '@webpieces/http-routing';\nimport { McpAccessTokenAuthority, MintedMcpAccessToken, VerifiedMcpCredential, WpMcpServerConfig } from './McpAuth';\n\nclass ExampleJwtMintRequest {\n constructor(public readonly subject: string) {}\n}\n\nclass ExampleMcpGrant {\n constructor(\n public readonly subject: string,\n public readonly resource: string,\n ) {}\n}\n\n/** Compile assertion: one application authority can own both endpoint JWT and MCP token lifecycles. */\nexport class McpAuthorityCompileAssertions\n extends JwtHook<ExampleJwtMintRequest>\n implements McpAccessTokenAuthority<ExampleMcpGrant>\n{\n override async mint(request: ExampleJwtMintRequest): Promise<MintedJwt> {\n return new MintedJwt(`endpoint:${request.subject}`, Date.now() / 1000 + 60);\n }\n\n override async parseJwt(token: string): Promise<AuthenticatedCaller> {\n return new AuthenticatedCaller(token);\n }\n\n async mintAccessToken(grant: ExampleMcpGrant): Promise<MintedMcpAccessToken> {\n const now = Math.floor(Date.now() / 1000);\n return new MintedMcpAccessToken(`mcp:${grant.subject}`, grant.resource, now, now + 60);\n }\n\n async verifyAccessToken(\n _accessToken: string,\n expectedResource: string,\n ): Promise<VerifiedMcpCredential> {\n const now = Math.floor(Date.now() / 1000);\n return new VerifiedMcpCredential(\n 'example-user',\n 'https://issuer.example.test',\n expectedResource,\n now,\n now + 60,\n ['tools'],\n now,\n new AuthenticatedCaller('example-user'),\n );\n }\n}\n\n/** Compiled hard-cut assertions; Vitest does not type-check specifications. */\nexport class McpPrincipalCompileAssertions {\n roles(credential: VerifiedMcpCredential): readonly string[] {\n // @ts-expect-error listingRoles was replaced by principalRoles derived from the canonical caller\n credential.listingRoles;\n return credential.principalRoles;\n }\n}\n\n/** Profile-selection arity is checked by tsc, not only by runtime specs. */\nexport class McpProfilesCompileAssertions {\n empty(config: WpMcpServerConfig<string>): void {\n // @ts-expect-error an explicit server selection must include at least one group\n config.setToolProfiles([]);\n }\n}\n"]}
1
+ {"version":3,"file":"McpAuthorityCompileAssertions.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/McpAuthorityCompileAssertions.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AuthenticatedCaller,\n AuthorizationService,\n JwtHook,\n MintedJwt,\n} from '@webpieces/http-routing';\nimport {\n McpAccessTokenAuthority,\n MintedMcpAccessToken,\n VerifiedMcpCredential,\n WpMcpServerConfig,\n} from './McpAuth';\nimport { McpApiBinding } from './McpApiBinding';\nimport { McpToolset } from './McpToolset';\n\nclass ExampleJwtMintRequest {\n constructor(public readonly subject: string) {}\n}\n\nclass ExampleMcpGrant {\n constructor(\n public readonly subject: string,\n public readonly resource: string,\n ) {}\n}\n\n/** Compile assertion: one application authority can own both endpoint JWT and MCP token lifecycles. */\nexport class McpAuthorityCompileAssertions\n extends JwtHook<ExampleJwtMintRequest>\n implements McpAccessTokenAuthority<ExampleMcpGrant>\n{\n override async mint(request: ExampleJwtMintRequest): Promise<MintedJwt> {\n return new MintedJwt(`endpoint:${request.subject}`, Date.now() / 1000 + 60);\n }\n\n override async parseJwt(token: string): Promise<AuthenticatedCaller> {\n return new AuthenticatedCaller(token);\n }\n\n async mintAccessToken(grant: ExampleMcpGrant): Promise<MintedMcpAccessToken> {\n const now = Math.floor(Date.now() / 1000);\n return new MintedMcpAccessToken(`mcp:${grant.subject}`, grant.resource, now, now + 60);\n }\n\n async verifyAccessToken(\n _accessToken: string,\n expectedResource: string,\n ): Promise<VerifiedMcpCredential> {\n const now = Math.floor(Date.now() / 1000);\n return new VerifiedMcpCredential(\n 'example-user',\n 'https://issuer.example.test',\n expectedResource,\n now,\n now + 60,\n ['tools'],\n now,\n new AuthenticatedCaller('example-user'),\n );\n }\n}\n\n/** Compiled hard-cut assertions; Vitest does not type-check specifications. */\nexport class McpPrincipalCompileAssertions {\n roles(credential: VerifiedMcpCredential): readonly string[] {\n // @ts-expect-error listingRoles was replaced by principalRoles derived from the canonical caller\n credential.listingRoles;\n return credential.principalRoles;\n }\n}\n\n/** Profile-selection arity is checked by tsc, not only by runtime specs. */\nexport class McpProfilesCompileAssertions {\n empty(bindings: readonly McpApiBinding[], policy: AuthorizationService): void {\n // @ts-expect-error a toolset must serve at least one profile group\n new McpToolset(bindings, [], policy, []);\n }\n}\n\n/**\n * The OAuth-only setters moved into `mcpOAuth(...)` (#1416) and the toolset owns WHICH tools: none of\n * the old spellings compiles, so an agent upgrading is told by tsc, not by a deprecation it never sees.\n */\nexport class McpServerConfigCompileAssertions {\n removedOAuthSetters(\n config: WpMcpServerConfig,\n authority: McpAccessTokenAuthority<string>,\n ): void {\n // @ts-expect-error moved to mcpOAuth(authority, authorizationServers, requiredScopes, tokenFormat)\n config.setAccessTokenAuthority(authority);\n // @ts-expect-error moved to mcpOAuth(authority, authorizationServers, ...)\n config.setAuthorizationServers(['https://login.example.test']);\n // @ts-expect-error moved to mcpOAuth(authority, authorizationServers, requiredScopes, ...)\n config.setRequiredScopes(['tools']);\n // @ts-expect-error moved to mcpOAuth(..., maxAccountValidationAgeSeconds)\n config.setMaxAccountValidationAgeSeconds(60);\n }\n\n noPublicDoor(config: WpMcpServerConfig): void {\n // @ts-expect-error a public mode identifies no caller to authorize tools against\n config.setAuth([{ kind: 'public' }]);\n }\n\n removedToolsetSetters(config: WpMcpServerConfig, policy: AuthorizationService): void {\n // @ts-expect-error profiles are the McpToolset's: new McpToolset(bindings, catalogs, policy, profiles)\n config.setToolProfiles(['default']);\n // @ts-expect-error the policy is the McpToolset's: new McpToolset(bindings, catalogs, policy, profiles)\n config.setAuthorizationService(policy);\n }\n}\n"]}
@@ -1,18 +1,11 @@
1
- import { McpApiBinding } from './McpApiBinding';
2
1
  import { McpDeployment } from './McpDeployment';
3
- import { McpToolCatalog } from './McpToolCatalog';
2
+ /**
3
+ * WHERE and HOW one `WpMcpServer` is mounted. WHICH tools it serves is the `McpToolset` handed to
4
+ * `new WpMcpServer(config, toolset)`, never a bind option: one server is one path is one toolset.
5
+ */
4
6
  export declare class McpBindOptions {
5
7
  /** Supplied by the application/discovery config; Webpieces never assumes `/mcp`. */
6
8
  readonly endpointPath: string;
7
- readonly bindings: readonly McpApiBinding[];
8
- /**
9
- * The tools the build published — ONE catalog per bound contract, each the
10
- * `mcp-<ContractClass>-tools.json` `wp-openapi` wrote beside its library's OpenAPI documents,
11
- * read with `McpToolCatalog.fromPackages([...], __dirname)`. REQUIRED because it is the only
12
- * source of a tool's documentation and schemas; there is no reflect-metadata fallback.
13
- * `McpToolRegistry` refuses to boot unless it holds exactly one catalog per bound contract.
14
- */
15
- readonly toolCatalogs: readonly McpToolCatalog[];
16
9
  readonly deployment: McpDeployment;
17
10
  /** Requests without Origin remain valid for non-browser MCP clients. */
18
11
  readonly allowedOrigins: readonly string[];
@@ -20,15 +13,7 @@ export declare class McpBindOptions {
20
13
  readonly keepAliveMs: number;
21
14
  constructor(
22
15
  /** Supplied by the application/discovery config; Webpieces never assumes `/mcp`. */
23
- endpointPath: string, bindings: readonly McpApiBinding[],
24
- /**
25
- * The tools the build published — ONE catalog per bound contract, each the
26
- * `mcp-<ContractClass>-tools.json` `wp-openapi` wrote beside its library's OpenAPI documents,
27
- * read with `McpToolCatalog.fromPackages([...], __dirname)`. REQUIRED because it is the only
28
- * source of a tool's documentation and schemas; there is no reflect-metadata fallback.
29
- * `McpToolRegistry` refuses to boot unless it holds exactly one catalog per bound contract.
30
- */
31
- toolCatalogs: readonly McpToolCatalog[], deployment: McpDeployment,
16
+ endpointPath: string, deployment: McpDeployment,
32
17
  /** Requests without Origin remain valid for non-browser MCP clients. */
33
18
  allowedOrigins?: readonly string[], maxSubscriptions?: number, keepAliveMs?: number);
34
19
  }
@@ -1,30 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.McpBindOptions = void 0;
4
+ /**
5
+ * WHERE and HOW one `WpMcpServer` is mounted. WHICH tools it serves is the `McpToolset` handed to
6
+ * `new WpMcpServer(config, toolset)`, never a bind option: one server is one path is one toolset.
7
+ */
4
8
  class McpBindOptions {
5
9
  endpointPath;
6
- bindings;
7
- toolCatalogs;
8
10
  deployment;
9
11
  allowedOrigins;
10
12
  maxSubscriptions;
11
13
  keepAliveMs;
12
14
  constructor(
13
15
  /** Supplied by the application/discovery config; Webpieces never assumes `/mcp`. */
14
- endpointPath, bindings,
15
- /**
16
- * The tools the build published — ONE catalog per bound contract, each the
17
- * `mcp-<ContractClass>-tools.json` `wp-openapi` wrote beside its library's OpenAPI documents,
18
- * read with `McpToolCatalog.fromPackages([...], __dirname)`. REQUIRED because it is the only
19
- * source of a tool's documentation and schemas; there is no reflect-metadata fallback.
20
- * `McpToolRegistry` refuses to boot unless it holds exactly one catalog per bound contract.
21
- */
22
- toolCatalogs, deployment,
16
+ endpointPath, deployment,
23
17
  /** Requests without Origin remain valid for non-browser MCP clients. */
24
18
  allowedOrigins = [], maxSubscriptions = 1024, keepAliveMs = 15_000) {
25
19
  this.endpointPath = endpointPath;
26
- this.bindings = bindings;
27
- this.toolCatalogs = toolCatalogs;
28
20
  this.deployment = deployment;
29
21
  this.allowedOrigins = allowedOrigins;
30
22
  this.maxSubscriptions = maxSubscriptions;
@@ -32,8 +24,6 @@ class McpBindOptions {
32
24
  if (!endpointPath.startsWith('/') || endpointPath.includes('?')) {
33
25
  throw new Error('MCP endpointPath must be an absolute path without a query string.');
34
26
  }
35
- if (bindings.length === 0)
36
- throw new Error('MCP binding requires at least one API contract.');
37
27
  if (!Number.isInteger(maxSubscriptions) || maxSubscriptions <= 0) {
38
28
  throw new Error('MCP maxSubscriptions must be a positive integer.');
39
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"McpBindOptions.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/McpBindOptions.ts"],"names":[],"mappings":";;;AAIA,MAAa,cAAc;IAGH;IACA;IAQA;IACA;IAEA;IACA;IACA;IAhBpB;IACI,oFAAoF;IACpE,YAAoB,EACpB,QAAkC;IAClD;;;;;;OAMG;IACa,YAAuC,EACvC,UAAyB;IACzC,wEAAwE;IACxD,iBAAoC,EAAE,EACtC,mBAAmB,IAAI,EACvB,cAAc,MAAM;QAdpB,iBAAY,GAAZ,YAAY,CAAQ;QACpB,aAAQ,GAAR,QAAQ,CAA0B;QAQlC,iBAAY,GAAZ,YAAY,CAA2B;QACvC,eAAU,GAAV,UAAU,CAAe;QAEzB,mBAAc,GAAd,cAAc,CAAwB;QACtC,qBAAgB,GAAhB,gBAAgB,CAAO;QACvB,gBAAW,GAAX,WAAW,CAAS;QAEpC,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9D,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;QACzF,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACvE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC,IAAI,gBAAgB,IAAI,CAAC,EAAE,CAAC;YAC/D,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACxE,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;YACnD,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;QAC1E,CAAC;IACL,CAAC;CACJ;AA/BD,wCA+BC","sourcesContent":["import { McpApiBinding } from './McpApiBinding';\nimport { McpDeployment } from './McpDeployment';\nimport { McpToolCatalog } from './McpToolCatalog';\n\nexport class McpBindOptions {\n constructor(\n /** Supplied by the application/discovery config; Webpieces never assumes `/mcp`. */\n public readonly endpointPath: string,\n public readonly bindings: readonly McpApiBinding[],\n /**\n * The tools the build published — ONE catalog per bound contract, each the\n * `mcp-<ContractClass>-tools.json` `wp-openapi` wrote beside its library's OpenAPI documents,\n * read with `McpToolCatalog.fromPackages([...], __dirname)`. REQUIRED because it is the only\n * source of a tool's documentation and schemas; there is no reflect-metadata fallback.\n * `McpToolRegistry` refuses to boot unless it holds exactly one catalog per bound contract.\n */\n public readonly toolCatalogs: readonly McpToolCatalog[],\n public readonly deployment: McpDeployment,\n /** Requests without Origin remain valid for non-browser MCP clients. */\n public readonly allowedOrigins: readonly string[] = [],\n public readonly maxSubscriptions = 1024,\n public readonly keepAliveMs = 15_000,\n ) {\n if (!endpointPath.startsWith('/') || endpointPath.includes('?')) {\n throw new Error('MCP endpointPath must be an absolute path without a query string.');\n }\n if (bindings.length === 0)\n throw new Error('MCP binding requires at least one API contract.');\n if (!Number.isInteger(maxSubscriptions) || maxSubscriptions <= 0) {\n throw new Error('MCP maxSubscriptions must be a positive integer.');\n }\n if (!Number.isFinite(keepAliveMs) || keepAliveMs < 0) {\n throw new Error('MCP keepAliveMs must be zero or a positive number.');\n }\n }\n}\n"]}
1
+ {"version":3,"file":"McpBindOptions.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/McpBindOptions.ts"],"names":[],"mappings":";;;AAEA;;;GAGG;AACH,MAAa,cAAc;IAGH;IACA;IAEA;IACA;IACA;IAPpB;IACI,oFAAoF;IACpE,YAAoB,EACpB,UAAyB;IACzC,wEAAwE;IACxD,iBAAoC,EAAE,EACtC,mBAAmB,IAAI,EACvB,cAAc,MAAM;QALpB,iBAAY,GAAZ,YAAY,CAAQ;QACpB,eAAU,GAAV,UAAU,CAAe;QAEzB,mBAAc,GAAd,cAAc,CAAwB;QACtC,qBAAgB,GAAhB,gBAAgB,CAAO;QACvB,gBAAW,GAAX,WAAW,CAAS;QAEpC,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9D,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;QACzF,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC,IAAI,gBAAgB,IAAI,CAAC,EAAE,CAAC;YAC/D,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACxE,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;YACnD,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;QAC1E,CAAC;IACL,CAAC;CACJ;AApBD,wCAoBC","sourcesContent":["import { McpDeployment } from './McpDeployment';\n\n/**\n * WHERE and HOW one `WpMcpServer` is mounted. WHICH tools it serves is the `McpToolset` handed to\n * `new WpMcpServer(config, toolset)`, never a bind option: one server is one path is one toolset.\n */\nexport class McpBindOptions {\n constructor(\n /** Supplied by the application/discovery config; Webpieces never assumes `/mcp`. */\n public readonly endpointPath: string,\n public readonly deployment: McpDeployment,\n /** Requests without Origin remain valid for non-browser MCP clients. */\n public readonly allowedOrigins: readonly string[] = [],\n public readonly maxSubscriptions = 1024,\n public readonly keepAliveMs = 15_000,\n ) {\n if (!endpointPath.startsWith('/') || endpointPath.includes('?')) {\n throw new Error('MCP endpointPath must be an absolute path without a query string.');\n }\n if (!Number.isInteger(maxSubscriptions) || maxSubscriptions <= 0) {\n throw new Error('MCP maxSubscriptions must be a positive integer.');\n }\n if (!Number.isFinite(keepAliveMs) || keepAliveMs < 0) {\n throw new Error('MCP keepAliveMs must be zero or a positive number.');\n }\n }\n}\n"]}
@@ -0,0 +1,35 @@
1
+ import { CallToolResult, Client, Tool } from '@modelcontextprotocol/client';
2
+ /** The `tools/call` parameters, exactly as `Client.callTool` takes them. */
3
+ export type McpClientSetCall = Parameters<Client['callTool']>[0];
4
+ /** Two connected clients publish one tool name: tool names are flat across servers, so it is refused. */
5
+ export declare class McpDuplicateToolError extends Error {
6
+ readonly toolName: string;
7
+ readonly firstClient: number;
8
+ readonly secondClient: number;
9
+ constructor(toolName: string, firstClient: number, secondClient: number);
10
+ }
11
+ /** A tool name no merged client publishes to the current caller. */
12
+ export declare class McpUnknownToolError extends Error {
13
+ readonly toolName: string;
14
+ constructor(toolName: string);
15
+ }
16
+ /**
17
+ * Several MCP clients an agent holds — its in-memory toolset plus another service's remote endpoint —
18
+ * presented as one: `listTools()` is the union, `callTool()` routes by name.
19
+ *
20
+ * Tool names are flat across servers, so a name published by two clients is REFUSED with
21
+ * {@link McpDuplicateToolError}: at {@link merge} (connect time) and again on every `listTools()`,
22
+ * because each server projects its tools per caller and a later caller can see a tool an earlier one
23
+ * could not. Like the clients it merges, it reads the caller per request: call it inside the
24
+ * `RequestContext` of the caller.
25
+ */
26
+ export declare class McpClientSet {
27
+ private readonly clients;
28
+ private routes;
29
+ private constructor();
30
+ static merge(clients: readonly Client[]): Promise<McpClientSet>;
31
+ /** The union of every client's tools for the current caller; refuses a name two clients publish. */
32
+ listTools(): Promise<Tool[]>;
33
+ /** Routes the call to the client that publishes `params.name`, refreshing the routes once if unknown. */
34
+ callTool(params: McpClientSetCall): Promise<CallToolResult>;
35
+ }
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.McpClientSet = exports.McpUnknownToolError = exports.McpDuplicateToolError = void 0;
4
+ /** Two connected clients publish one tool name: tool names are flat across servers, so it is refused. */
5
+ class McpDuplicateToolError extends Error {
6
+ toolName;
7
+ firstClient;
8
+ secondClient;
9
+ constructor(toolName, firstClient, secondClient) {
10
+ super(`MCP tool '${toolName}' is published by client #${firstClient} and client #${secondClient}. ` +
11
+ 'Tool names are flat across every server an agent holds, so the call could not be routed; ' +
12
+ 'rename one tool or drop one client.');
13
+ this.toolName = toolName;
14
+ this.firstClient = firstClient;
15
+ this.secondClient = secondClient;
16
+ this.name = 'McpDuplicateToolError';
17
+ }
18
+ }
19
+ exports.McpDuplicateToolError = McpDuplicateToolError;
20
+ /** A tool name no merged client publishes to the current caller. */
21
+ class McpUnknownToolError extends Error {
22
+ toolName;
23
+ constructor(toolName) {
24
+ super(`Unknown MCP tool '${toolName}': no merged client publishes it to this caller.`);
25
+ this.toolName = toolName;
26
+ this.name = 'McpUnknownToolError';
27
+ }
28
+ }
29
+ exports.McpUnknownToolError = McpUnknownToolError;
30
+ /**
31
+ * Several MCP clients an agent holds — its in-memory toolset plus another service's remote endpoint —
32
+ * presented as one: `listTools()` is the union, `callTool()` routes by name.
33
+ *
34
+ * Tool names are flat across servers, so a name published by two clients is REFUSED with
35
+ * {@link McpDuplicateToolError}: at {@link merge} (connect time) and again on every `listTools()`,
36
+ * because each server projects its tools per caller and a later caller can see a tool an earlier one
37
+ * could not. Like the clients it merges, it reads the caller per request: call it inside the
38
+ * `RequestContext` of the caller.
39
+ */
40
+ class McpClientSet {
41
+ clients;
42
+ routes = new Map();
43
+ constructor(clients) {
44
+ this.clients = clients;
45
+ }
46
+ // webpieces-disable no-function-outside-class -- validating factory; refuses duplicate names before the set exists
47
+ static async merge(clients) {
48
+ if (clients.length === 0)
49
+ throw new Error('McpClientSet.merge(...) requires at least one client.');
50
+ const set = new McpClientSet([...clients]);
51
+ await set.listTools();
52
+ return set;
53
+ }
54
+ /** The union of every client's tools for the current caller; refuses a name two clients publish. */
55
+ async listTools() {
56
+ const lists = await Promise.all(this.clients.map((client) => client.listTools()));
57
+ const routes = new Map();
58
+ const owners = new Map();
59
+ const tools = [];
60
+ lists.forEach((list, index) => {
61
+ for (const tool of list.tools) {
62
+ const owner = owners.get(tool.name);
63
+ if (owner !== undefined)
64
+ throw new McpDuplicateToolError(tool.name, owner, index);
65
+ owners.set(tool.name, index);
66
+ routes.set(tool.name, this.clients[index]);
67
+ tools.push(tool);
68
+ }
69
+ });
70
+ this.routes = routes;
71
+ return tools;
72
+ }
73
+ /** Routes the call to the client that publishes `params.name`, refreshing the routes once if unknown. */
74
+ async callTool(params) {
75
+ let client = this.routes.get(params.name);
76
+ if (client === undefined) {
77
+ await this.listTools();
78
+ client = this.routes.get(params.name);
79
+ }
80
+ if (client === undefined)
81
+ throw new McpUnknownToolError(params.name);
82
+ return client.callTool(params);
83
+ }
84
+ }
85
+ exports.McpClientSet = McpClientSet;
86
+ //# sourceMappingURL=McpClientSet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"McpClientSet.js","sourceRoot":"","sources":["../../../../../packages/http/mcp-server/src/McpClientSet.ts"],"names":[],"mappings":"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","sourcesContent":["import { CallToolResult, Client, ListToolsResult, Tool } from '@modelcontextprotocol/client';\n\n/** The `tools/call` parameters, exactly as `Client.callTool` takes them. */\nexport type McpClientSetCall = Parameters<Client['callTool']>[0];\n\n/** Two connected clients publish one tool name: tool names are flat across servers, so it is refused. */\nexport class McpDuplicateToolError extends Error {\n constructor(\n public readonly toolName: string,\n public readonly firstClient: number,\n public readonly secondClient: number,\n ) {\n super(\n `MCP tool '${toolName}' is published by client #${firstClient} and client #${secondClient}. ` +\n 'Tool names are flat across every server an agent holds, so the call could not be routed; ' +\n 'rename one tool or drop one client.',\n );\n this.name = 'McpDuplicateToolError';\n }\n}\n\n/** A tool name no merged client publishes to the current caller. */\nexport class McpUnknownToolError extends Error {\n constructor(public readonly toolName: string) {\n super(`Unknown MCP tool '${toolName}': no merged client publishes it to this caller.`);\n this.name = 'McpUnknownToolError';\n }\n}\n\n/**\n * Several MCP clients an agent holds — its in-memory toolset plus another service's remote endpoint —\n * presented as one: `listTools()` is the union, `callTool()` routes by name.\n *\n * Tool names are flat across servers, so a name published by two clients is REFUSED with\n * {@link McpDuplicateToolError}: at {@link merge} (connect time) and again on every `listTools()`,\n * because each server projects its tools per caller and a later caller can see a tool an earlier one\n * could not. Like the clients it merges, it reads the caller per request: call it inside the\n * `RequestContext` of the caller.\n */\nexport class McpClientSet {\n private routes = new Map<string, Client>();\n\n private constructor(private readonly clients: readonly Client[]) {}\n\n // webpieces-disable no-function-outside-class -- validating factory; refuses duplicate names before the set exists\n static async merge(clients: readonly Client[]): Promise<McpClientSet> {\n if (clients.length === 0)\n throw new Error('McpClientSet.merge(...) requires at least one client.');\n const set = new McpClientSet([...clients]);\n await set.listTools();\n return set;\n }\n\n /** The union of every client's tools for the current caller; refuses a name two clients publish. */\n async listTools(): Promise<Tool[]> {\n const lists = await Promise.all(this.clients.map((client: Client) => client.listTools()));\n const routes = new Map<string, Client>();\n const owners = new Map<string, number>();\n const tools: Tool[] = [];\n lists.forEach((list: ListToolsResult, index: number) => {\n for (const tool of list.tools) {\n const owner = owners.get(tool.name);\n if (owner !== undefined) throw new McpDuplicateToolError(tool.name, owner, index);\n owners.set(tool.name, index);\n routes.set(tool.name, this.clients[index]);\n tools.push(tool);\n }\n });\n this.routes = routes;\n return tools;\n }\n\n /** Routes the call to the client that publishes `params.name`, refreshing the routes once if unknown. */\n async callTool(params: McpClientSetCall): Promise<CallToolResult> {\n let client = this.routes.get(params.name);\n if (client === undefined) {\n await this.listTools();\n client = this.routes.get(params.name);\n }\n if (client === undefined) throw new McpUnknownToolError(params.name);\n return client.callTool(params);\n }\n}\n"]}
@@ -0,0 +1,117 @@
1
+ import { HttpRequest } from '@webpieces/core-context';
2
+ import { DtoValue } from '@webpieces/core-util';
3
+ import { CredentialVerifiers } from '@webpieces/http-routing';
4
+ import { McpHttpAuth, McpOAuthMode } from './McpAuth';
5
+ /**
6
+ * JwtPeek: base64url-decode a JWT's PAYLOAD only. NEVER trusted — the result is used solely to pick
7
+ * which ONE verifier sees the request, and that verifier fully verifies, so a token that merely
8
+ * claims an issuer fails its verification and is a 401. It never falls through to another mode.
9
+ */
10
+ export declare class JwtPeek {
11
+ /** The unverified payload claims, or `undefined` when `token` is not a well-formed JWT. */
12
+ static payloadOf(token: string): Record<string, DtoValue> | undefined;
13
+ /** The unverified `iss` claim, or `undefined`. */
14
+ static issuerOf(token: string): string | undefined;
15
+ private static decodeRecord;
16
+ }
17
+ /** The inbound facts one door mode classifies and verifies: the transport-neutral request. */
18
+ export declare class McpDoorRequest {
19
+ readonly http: HttpRequest;
20
+ constructor(http: HttpRequest);
21
+ header(name: string): string | undefined;
22
+ /** The `Authorization: Bearer <token>` value, or `undefined`. */
23
+ bearer(): string | undefined;
24
+ /** The `Authorization: Webpieces <secret>` value, or `undefined`. */
25
+ sharedSecret(): string | undefined;
26
+ }
27
+ /**
28
+ * What a successful authentication established. The caller itself is already PUBLISHED into
29
+ * `RequestContext` by the mode; these are the facts the door needs afterwards.
30
+ */
31
+ export declare class McpDoorPass {
32
+ /** Which mode authenticated, e.g. `jwt()`, `mcpOAuth(...)`. */
33
+ readonly mode: string;
34
+ /** True for an internal hop (OIDC, shared secret) whose forwarded trusted context is believed. */
35
+ readonly callerVerified: boolean;
36
+ /** The raw credential, for the SDK's `AuthInfo.token`. Never forwarded downstream. */
37
+ readonly token: string;
38
+ /** The verified subject (user id or machine identity). */
39
+ readonly subject: string;
40
+ readonly scopes: readonly string[];
41
+ readonly expiresAtEpochSeconds?: number | undefined;
42
+ constructor(
43
+ /** Which mode authenticated, e.g. `jwt()`, `mcpOAuth(...)`. */
44
+ mode: string,
45
+ /** True for an internal hop (OIDC, shared secret) whose forwarded trusted context is believed. */
46
+ callerVerified: boolean,
47
+ /** The raw credential, for the SDK's `AuthInfo.token`. Never forwarded downstream. */
48
+ token: string,
49
+ /** The verified subject (user id or machine identity). */
50
+ subject: string, scopes: readonly string[], expiresAtEpochSeconds?: number | undefined);
51
+ }
52
+ /** What a door mode needs to say about a request BEFORE any verification. */
53
+ interface McpDoorMode {
54
+ readonly name: string;
55
+ /** Claims some `Authorization: Bearer` credentials, so it can overlap another bearer mode. */
56
+ readonly bearerFamily: boolean;
57
+ /** Pure, cheap, NO crypto, NO I/O: does this request carry MY kind of credential? */
58
+ claims(req: McpDoorRequest): boolean;
59
+ /** Full verification; throws `ApiUnauthorizedError` on reject. Publishes the trusted caller. */
60
+ authenticate(req: McpDoorRequest): Promise<McpDoorPass>;
61
+ }
62
+ /** `mcpOAuth(...)` — an audience-bound access token minted by the application's authority. */
63
+ export declare class McpOAuthDoorMode implements McpDoorMode {
64
+ readonly mode: McpOAuthMode;
65
+ private readonly resource;
66
+ readonly name = "mcpOAuth(...)";
67
+ readonly bearerFamily = true;
68
+ private readonly callerContext;
69
+ constructor(mode: McpOAuthMode, resource: string);
70
+ claims(req: McpDoorRequest): boolean;
71
+ authenticate(req: McpDoorRequest): Promise<McpDoorPass>;
72
+ private validateCredential;
73
+ }
74
+ /**
75
+ * CLASSIFY, then verify with exactly ONE verifier.
76
+ *
77
+ * Credentials are self-describing, so one path serves every mode. The request is classified from
78
+ * UNVERIFIED surface facts — which header; a JWT's unverified `iss` — then handed to exactly ONE mode.
79
+ * Classifying on unverified data is safe because the chosen verifier fully verifies: a token that
80
+ * merely claims `iss: accounts.google.com` fails Google's verification and the request is a 401. It
81
+ * never falls through to another mode.
82
+ *
83
+ * - No claimant: only an `mcpOAuth(...)` server answers with the discovery challenge; others a plain 401.
84
+ * - Two claimants (an api-key header AND a bearer): ambiguous, refused — never "pick the one that works".
85
+ * - One claimant: its rejection IS the answer.
86
+ *
87
+ * The set is checked at construction (bind time): `vendorAuth` / public are refused, every mode's
88
+ * verifier must be bound, and no two modes may be able to claim one request.
89
+ */
90
+ export declare class McpDoorAuthenticator {
91
+ private readonly modes;
92
+ readonly oauth: McpOAuthDoorMode | undefined;
93
+ private constructor();
94
+ static create(auth: readonly McpHttpAuth[], resource: string, verifiers: CredentialVerifiers | undefined): McpDoorAuthenticator;
95
+ /**
96
+ * Whether a 401 for this request carries the `mcpOAuth(...)` discovery challenge: only when the
97
+ * server has that mode AND the request either carried no credential at all or was classified as an
98
+ * OAuth access token. A rejected `jwt()` / OIDC / api-key credential gets a plain 401, with no hint
99
+ * of which mode came closest.
100
+ */
101
+ challengeApplies(req: McpDoorRequest): boolean;
102
+ authenticate(req: McpDoorRequest): Promise<McpDoorPass>;
103
+ private static refuseNonCallers;
104
+ private static refuseUnverifiable;
105
+ private static doorModes;
106
+ /**
107
+ * `claims()` sets must be DISJOINT BY CONSTRUCTION. The bearer space is the only shared one, so the
108
+ * refusals are about it:
109
+ * - `mcpOAuth(..., everyBearer())` claims every bearer, so it stands alone in the bearer family;
110
+ * - a bearer `apiKey(...)` claims every bearer too, so it stands alone as well;
111
+ * - `mcpOAuth(..., jwtIssuer())` may not list a Google issuer beside an OIDC mode.
112
+ * `jwt()` claims the remainder, and `McpAccessTokenFormat.opaquePrefix` refuses a prefix that could
113
+ * overlap a JWT or a dev-oidc token, so nothing else can collide.
114
+ */
115
+ private static refuseOverlaps;
116
+ }
117
+ export {};