@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.
- package/README.md +199 -42
- package/package.json +7 -6
- package/src/McpApiDispatcher.d.ts +6 -3
- package/src/McpApiDispatcher.js +6 -2
- package/src/McpApiDispatcher.js.map +1 -1
- package/src/McpAuth.d.ts +115 -56
- package/src/McpAuth.js +180 -111
- package/src/McpAuth.js.map +1 -1
- package/src/McpAuthorityCompileAssertions.d.ts +12 -2
- package/src/McpAuthorityCompileAssertions.js +32 -4
- package/src/McpAuthorityCompileAssertions.js.map +1 -1
- package/src/McpBindOptions.d.ts +5 -20
- package/src/McpBindOptions.js +5 -15
- package/src/McpBindOptions.js.map +1 -1
- package/src/McpClientSet.d.ts +35 -0
- package/src/McpClientSet.js +86 -0
- package/src/McpClientSet.js.map +1 -0
- package/src/McpDoorAuth.d.ts +117 -0
- package/src/McpDoorAuth.js +455 -0
- package/src/McpDoorAuth.js.map +1 -0
- package/src/McpToolCallRendering.d.ts +5 -4
- package/src/McpToolCallRendering.js.map +1 -1
- package/src/McpToolCatalog.d.ts +1 -1
- package/src/McpToolCatalog.js +1 -1
- package/src/McpToolCatalog.js.map +1 -1
- package/src/McpToolRegistry.js +8 -2
- package/src/McpToolRegistry.js.map +1 -1
- package/src/McpToolset.d.ts +115 -0
- package/src/McpToolset.js +323 -0
- package/src/McpToolset.js.map +1 -0
- package/src/WpMcpErrorTranslator.d.ts +5 -7
- package/src/WpMcpErrorTranslator.js +7 -13
- package/src/WpMcpErrorTranslator.js.map +1 -1
- package/src/WpMcpInMemory.d.ts +30 -0
- package/src/WpMcpInMemory.js +97 -0
- package/src/WpMcpInMemory.js.map +1 -0
- package/src/WpMcpRemote.d.ts +23 -0
- package/src/WpMcpRemote.js +89 -0
- package/src/WpMcpRemote.js.map +1 -0
- package/src/WpMcpServer.d.ts +45 -76
- package/src/WpMcpServer.js +101 -327
- package/src/WpMcpServer.js.map +1 -1
- package/src/__tests__/McpTransportFixtures.d.ts +107 -0
- package/src/__tests__/McpTransportFixtures.js +266 -0
- package/src/__tests__/McpTransportFixtures.js.map +1 -0
- package/src/index.d.ts +9 -2
- package/src/index.js +17 -1
- package/src/index.js.map +1 -1
package/src/McpAuth.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { AuthenticatedCaller,
|
|
1
|
+
import { AuthMethod } from '@webpieces/core-util';
|
|
2
|
+
import { AuthenticatedCaller, CredentialVerifiers } from '@webpieces/http-routing';
|
|
3
3
|
import { Icon } from '@modelcontextprotocol/server';
|
|
4
4
|
import { McpIcon } from './McpIcon';
|
|
5
5
|
export declare const MAX_MCP_ACCESS_TOKEN_LIFETIME_SECONDS: number;
|
|
@@ -41,8 +41,8 @@ export declare class VerifiedMcpCredential {
|
|
|
41
41
|
get principalRoles(): readonly string[];
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
|
-
* Application-owned paired mint/verify authority for resource-bound MCP access tokens
|
|
45
|
-
* `verifyAccessToken` must throw `ApiUnauthorizedError` for a token it rejects (the bind boundary
|
|
44
|
+
* Application-owned paired mint/verify authority for resource-bound MCP access tokens, handed to
|
|
45
|
+
* `mcpOAuth(...)`. `verifyAccessToken` must throw `ApiUnauthorizedError` for a token it rejects (the bind boundary
|
|
46
46
|
* answers 401 + `WWW-Authenticate`); any other throw is treated as an implementation failure (500).
|
|
47
47
|
*/
|
|
48
48
|
export interface McpAccessTokenAuthority<TGrant> {
|
|
@@ -58,41 +58,51 @@ export declare class McpProtectedResourceMetadata {
|
|
|
58
58
|
constructor(resource: string, authorizationServers: readonly string[], scopesSupported?: readonly string[]);
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
* One member of a server's `setAuth([...])` set: an `@WpAuth` builder (`oidcSameProject()`,
|
|
62
|
+
* `oidcSvcs([...])`, `jwt()`, `apiKey(...)`, `sharedSecret(...)`) or `mcpOAuth(...)`.
|
|
63
|
+
*/
|
|
64
|
+
export type McpHttpAuth = AuthMethod | McpOAuthMode;
|
|
65
|
+
/** A server's `setAuth([...])` set: at least one mode. */
|
|
66
|
+
export type McpHttpAuths = readonly [McpHttpAuth, ...McpHttpAuth[]];
|
|
67
|
+
/**
|
|
68
|
+
* Node MCP HTTP server configuration, built with fluent setters: every name sits next to its own
|
|
69
|
+
* value, so no two settings can be swapped, and a future setting is an additive setter rather than a
|
|
70
|
+
* breaking signature change.
|
|
65
71
|
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
72
|
+
* WHICH tools the server serves is not here: that is the `McpToolset` handed to
|
|
73
|
+
* `new WpMcpServer(config, toolset)`. This config says only who the server is and HOW A CALLER GETS
|
|
74
|
+
* IN — {@link setAuth}, an OR-set of the same `@WpAuth` builders an endpoint uses plus `mcpOAuth(...)`.
|
|
75
|
+
*
|
|
76
|
+
* SINGLE CANONICAL ORIGIN: one server serves exactly one resource. {@link setResource} is fixed for
|
|
77
|
+
* the lifetime of the process, is the audience of every `mcpOAuth(...)` access token, and is never
|
|
78
|
+
* derived from the request `Host` — see that setter for why a host-derived audience is a confused
|
|
79
|
+
* deputy.
|
|
69
80
|
*
|
|
70
81
|
* Each setter validates its own value immediately and names itself in the failure. Setters cannot
|
|
71
82
|
* make a field impossible to forget the way a positional constructor could, so
|
|
72
83
|
* {@link WpMcpServerConfig.validate} runs from `WpMcpServer.bind(...)` and fails at startup listing
|
|
73
84
|
* EVERY missing required setter at once. A misconfiguration is therefore a boot failure naming the
|
|
74
|
-
* setter, instead of a first-request 401
|
|
75
|
-
* so the client's correct response is to re-authenticate and fail again, forever.
|
|
85
|
+
* setter, instead of a first-request 401 — which, with `mcpOAuth(...)`, is the OAuth discovery
|
|
86
|
+
* signal, so the client's correct response is to re-authenticate and fail again, forever.
|
|
76
87
|
*
|
|
77
88
|
* ```ts
|
|
78
|
-
* const config = new WpMcpServerConfig
|
|
89
|
+
* const config = new WpMcpServerConfig()
|
|
79
90
|
* .setName('my-server')
|
|
80
91
|
* .setTitle('My Product')
|
|
81
92
|
* .setDescription('Search and manage My Product accounts from any MCP client.')
|
|
82
93
|
* .setWebsiteUrl('https://www.example.com/my-product')
|
|
83
94
|
* .setVersion('1.0.0')
|
|
84
|
-
* .setResource('https://api.example.com/mcp')
|
|
85
|
-
* .
|
|
86
|
-
* .
|
|
87
|
-
*
|
|
88
|
-
*
|
|
95
|
+
* .setResource('https://api.example.com/mcp/user')
|
|
96
|
+
* .setAuth([oidcSameProject()]) // internal svc-to-svc MCP
|
|
97
|
+
* .setCredentialVerifiers(router.credentialVerifiers());
|
|
98
|
+
* // .setAuth([oidcSameProject(), jwt(), apiKey('partner', creds),
|
|
99
|
+
* // mcpOAuth(authority, ['https://login.example.com'], ['tools'], McpAccessTokenFormat.opaquePrefix('wpmcp_'))])
|
|
89
100
|
*
|
|
90
101
|
* // the tools/call error translator is a PROCESS-GLOBAL, not a member of this config:
|
|
91
102
|
* McpRegistry.setErrorTranslator(new MyMcpErrorTranslator());
|
|
92
103
|
* ```
|
|
93
104
|
*/
|
|
94
|
-
export declare class WpMcpServerConfig
|
|
95
|
-
private toolProfilesValue;
|
|
105
|
+
export declare class WpMcpServerConfig {
|
|
96
106
|
private nameValue?;
|
|
97
107
|
private titleValue?;
|
|
98
108
|
private descriptionValue?;
|
|
@@ -100,14 +110,8 @@ export declare class WpMcpServerConfig<TGrant> {
|
|
|
100
110
|
private versionValue?;
|
|
101
111
|
private iconsValue?;
|
|
102
112
|
private resourceValue?;
|
|
103
|
-
private
|
|
104
|
-
private
|
|
105
|
-
private authorizationServersValue?;
|
|
106
|
-
private requiredScopesValue?;
|
|
107
|
-
private maxAccountValidationAgeSecondsValue;
|
|
108
|
-
/** OPTIONAL. Union of groups exposed by this instance; defaults to [Mcp.DEFAULT]. */
|
|
109
|
-
setToolProfiles(profiles: McpToolProfiles): this;
|
|
110
|
-
get toolProfiles(): readonly string[];
|
|
113
|
+
private authValue?;
|
|
114
|
+
private credentialVerifiersValue?;
|
|
111
115
|
/** REQUIRED. The MCP server name reported by `initialize`. */
|
|
112
116
|
setName(name: string): this;
|
|
113
117
|
/**
|
|
@@ -140,9 +144,9 @@ export declare class WpMcpServerConfig<TGrant> {
|
|
|
140
144
|
/** Immutable snapshot; omitted icons remain absent from SDK implementation metadata. */
|
|
141
145
|
get icons(): readonly McpIcon[] | undefined;
|
|
142
146
|
/**
|
|
143
|
-
* REQUIRED. The ONE canonical
|
|
144
|
-
*
|
|
145
|
-
* derived from it for the 401 challenge, so a non-URL here is always a mistake — including a
|
|
147
|
+
* REQUIRED. The ONE canonical resource URI, which must be an absolute URL: it is the exact
|
|
148
|
+
* audience every `mcpOAuth(...)` access token is checked against, and {@link resourceMetadataUrl}
|
|
149
|
+
* is derived from it for the 401 challenge, so a non-URL here is always a mistake — including a
|
|
146
150
|
* name or version landed on it.
|
|
147
151
|
*
|
|
148
152
|
* It is fixed for the lifetime of the process and must NEVER be derived from the request `Host`
|
|
@@ -153,23 +157,23 @@ export declare class WpMcpServerConfig<TGrant> {
|
|
|
153
157
|
* audience.
|
|
154
158
|
*/
|
|
155
159
|
setResource(resource: string): this;
|
|
156
|
-
/** REQUIRED. Application-owned paired mint/verify authority for MCP access tokens. */
|
|
157
|
-
setAccessTokenAuthority(authority: McpAccessTokenAuthority<TGrant>): this;
|
|
158
|
-
/** REQUIRED. The common authorization service for tool projection and invocation. */
|
|
159
|
-
setAuthorizationService(authority: AuthorizationService): this;
|
|
160
160
|
/**
|
|
161
|
-
* REQUIRED.
|
|
162
|
-
*
|
|
163
|
-
*
|
|
161
|
+
* REQUIRED. HOW a caller gets in: an OR-set of the `@WpAuth` builders endpoints already use —
|
|
162
|
+
* `oidcSameProject()`, `oidcSvcs([...])`, `jwt()`, `apiKey(regime, creds)`, `sharedSecret(key)` —
|
|
163
|
+
* plus `mcpOAuth(...)` for off-the-shelf connectors. Each request is CLASSIFIED from unverified
|
|
164
|
+
* surface facts (which header; a JWT's unverified `iss`) and handed to exactly ONE verifier, whose
|
|
165
|
+
* rejection is the answer — it never falls through to another mode.
|
|
166
|
+
*
|
|
167
|
+
* `vendorAuth(...)` and `@WpAuthPublic` identify no caller to authorize tools against, so they are
|
|
168
|
+
* refused at bind; so is a set whose modes could both claim one request.
|
|
164
169
|
*/
|
|
165
|
-
|
|
170
|
+
setAuth(auth: McpHttpAuths): this;
|
|
166
171
|
/**
|
|
167
|
-
* REQUIRED
|
|
168
|
-
*
|
|
172
|
+
* REQUIRED when {@link setAuth} holds any `@WpAuth` mode (everything but `mcpOAuth(...)`): the
|
|
173
|
+
* verifiers this app bound, from `router.credentialVerifiers()` — the SAME JwtHook, OidcHook /
|
|
174
|
+
* DefaultOidcVerifier, AuthConfig and ApiKeyHook the endpoint `AuthFilter` uses.
|
|
169
175
|
*/
|
|
170
|
-
|
|
171
|
-
/** OPTIONAL ceiling, 1 second to 1 hour. Defaults to 1 hour. */
|
|
172
|
-
setMaxAccountValidationAgeSeconds(seconds: number): this;
|
|
176
|
+
setCredentialVerifiers(verifiers: CredentialVerifiers): this;
|
|
173
177
|
/**
|
|
174
178
|
* Called by `WpMcpServer.bind(...)`. Throws naming EVERY missing required setter at once, so a
|
|
175
179
|
* half-built config is repaired in one pass rather than one boot failure at a time.
|
|
@@ -181,29 +185,84 @@ export declare class WpMcpServerConfig<TGrant> {
|
|
|
181
185
|
get websiteUrl(): string;
|
|
182
186
|
get version(): string;
|
|
183
187
|
get resource(): string;
|
|
184
|
-
get
|
|
185
|
-
|
|
186
|
-
get
|
|
187
|
-
get requiredScopes(): readonly string[];
|
|
188
|
-
get maxAccountValidationAgeSeconds(): number;
|
|
188
|
+
get auth(): McpHttpAuths;
|
|
189
|
+
/** The bound verifiers, or `undefined` for an `mcpOAuth(...)`-only server, which needs none. */
|
|
190
|
+
get credentialVerifiers(): CredentialVerifiers | undefined;
|
|
189
191
|
/**
|
|
190
192
|
* RFC 9728 §3.1: the URL of this resource's protected-resource METADATA DOCUMENT, derived from
|
|
191
193
|
* {@link resource} by inserting `/.well-known/oauth-protected-resource` between origin and path
|
|
192
194
|
* (`https://host/mcp` → `https://host/.well-known/oauth-protected-resource/mcp`).
|
|
193
195
|
*
|
|
194
196
|
* This — never the resource identifier itself — is what RFC 9728 §5.1 defines `resource_metadata`
|
|
195
|
-
* to be, so it is the value webpieces puts in the 401 `WWW-Authenticate`
|
|
196
|
-
* mounts no `.well-known` route; publish
|
|
197
|
+
* to be, so it is the value webpieces puts in the `mcpOAuth(...)` 401 `WWW-Authenticate`
|
|
198
|
+
* challenge. webpieces mounts no `.well-known` route; publish
|
|
199
|
+
* `WpMcpServer.protectedResourceMetadata()` at this URL.
|
|
197
200
|
*/
|
|
198
201
|
get resourceMetadataUrl(): string;
|
|
199
|
-
protectedResourceMetadata(): McpProtectedResourceMetadata;
|
|
200
202
|
/** Copies caller-owned descriptors and nested size arrays before freezing the snapshot. */
|
|
201
203
|
private snapshotIcons;
|
|
202
204
|
private requireText;
|
|
203
205
|
private requireUrl;
|
|
204
206
|
private requireMaxLength;
|
|
205
|
-
private requireArray;
|
|
206
|
-
private requireSeconds;
|
|
207
|
-
private requirePresent;
|
|
208
207
|
private read;
|
|
209
208
|
}
|
|
209
|
+
/** Google's OIDC issuers. An `mcpOAuth(...)` issuer may never be one: it would claim Google tokens. */
|
|
210
|
+
export declare const GOOGLE_OIDC_ISSUERS: readonly string[];
|
|
211
|
+
/** The unsigned off-GCP OIDC token prefix `@webpieces/gcp-identity` mints and verifies. */
|
|
212
|
+
export declare const DEV_OIDC_TOKEN_PREFIX = "dev-oidc.";
|
|
213
|
+
/** Every JWT starts with the base64url of `{"`. An opaque prefix starting so would claim JWTs. */
|
|
214
|
+
export declare const JWT_PREFIX = "eyJ";
|
|
215
|
+
export type McpAccessTokenFormatKind = 'opaque-prefix' | 'jwt-issuer' | 'every-bearer';
|
|
216
|
+
/** The trusted token issuers of an `mcpOAuth(...)` mode: at least one absolute URL. */
|
|
217
|
+
export type McpAuthorizationServers = readonly [string, ...string[]];
|
|
218
|
+
/**
|
|
219
|
+
* How an `mcpOAuth(...)` access token LOOKS, decided by the authority that mints it. The MCP door
|
|
220
|
+
* classifies every request from unverified surface facts before handing it to exactly ONE verifier, so
|
|
221
|
+
* the OAuth mode must say which bearers are its kind without any crypto or I/O:
|
|
222
|
+
*
|
|
223
|
+
* - {@link opaquePrefix} — the authority mints opaque tokens that all start with `prefix`
|
|
224
|
+
* (e.g. `'wpmcp_'`). Disjoint from every JWT and from off-GCP OIDC tokens by construction.
|
|
225
|
+
* - {@link jwtIssuer} — the authority mints JWTs whose `iss` is one of the mode's
|
|
226
|
+
* `authorizationServers`. A JWT with any other issuer is not this mode's.
|
|
227
|
+
* - {@link everyBearer} — every `Authorization: Bearer` credential is this mode's. Legal ONLY when no
|
|
228
|
+
* other bearer mode (`oidcSameProject()`, `oidcSvcs(...)`, `jwt()`, a bearer `apiKey(...)`) is in the
|
|
229
|
+
* same `setAuth([...])`; startup refuses the combination, because two modes claiming one token is
|
|
230
|
+
* exactly the overlap classify-then-verify forbids.
|
|
231
|
+
*/
|
|
232
|
+
export declare class McpAccessTokenFormat {
|
|
233
|
+
readonly kind: McpAccessTokenFormatKind;
|
|
234
|
+
readonly prefix: string;
|
|
235
|
+
private constructor();
|
|
236
|
+
static opaquePrefix(prefix: string): McpAccessTokenFormat;
|
|
237
|
+
static jwtIssuer(): McpAccessTokenFormat;
|
|
238
|
+
static everyBearer(): McpAccessTokenFormat;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* The MCP OAuth mode: today's connector behavior (Claude / ChatGPT) as ONE member of a server's
|
|
242
|
+
* `setAuth([...])` set. It is the only mode that publishes RFC 9728 protected-resource metadata and
|
|
243
|
+
* answers a request carrying no credential with the `401 WWW-Authenticate` discovery challenge, and
|
|
244
|
+
* its access tokens are audience-bound to the server's canonical resource. Build it with
|
|
245
|
+
* {@link mcpOAuth}.
|
|
246
|
+
*/
|
|
247
|
+
export declare class McpOAuthMode {
|
|
248
|
+
readonly authority: McpAccessTokenAuthority<never>;
|
|
249
|
+
readonly authorizationServers: McpAuthorizationServers;
|
|
250
|
+
readonly requiredScopes: readonly string[];
|
|
251
|
+
readonly tokenFormat: McpAccessTokenFormat;
|
|
252
|
+
readonly maxAccountValidationAgeSeconds: number;
|
|
253
|
+
readonly kind = "mcp-oauth";
|
|
254
|
+
constructor(authority: McpAccessTokenAuthority<never>, authorizationServers: McpAuthorizationServers, requiredScopes: readonly string[], tokenFormat: McpAccessTokenFormat, maxAccountValidationAgeSeconds: number);
|
|
255
|
+
/** RFC 9728 metadata for `resource`, published by the app at the server's `resourceMetadataUrl`. */
|
|
256
|
+
protectedResourceMetadata(resource: string): McpProtectedResourceMetadata;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* The MCP OAuth door mode, for off-the-shelf connectors. Everything OAuth-only lives here and is
|
|
260
|
+
* required only when this mode is in the server's `setAuth([...])`:
|
|
261
|
+
*
|
|
262
|
+
* @param authority the application-owned paired mint/verify authority
|
|
263
|
+
* @param authorizationServers the trusted token issuers, published as `authorization_servers`
|
|
264
|
+
* @param requiredScopes scopes every access token must carry before any tool is dispatched
|
|
265
|
+
* @param tokenFormat how the authority's tokens look, so the door can classify them
|
|
266
|
+
* @param maxAccountValidationAgeSeconds how fresh the account facts must be, 1 second to 1 hour
|
|
267
|
+
*/
|
|
268
|
+
export declare function mcpOAuth<TGrant>(authority: McpAccessTokenAuthority<TGrant>, authorizationServers: McpAuthorizationServers, requiredScopes: readonly string[], tokenFormat: McpAccessTokenFormat, maxAccountValidationAgeSeconds?: number): McpOAuthMode;
|