@heddleagent/execution-host-client 6.4.0 → 6.5.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/README.md +47 -1
- package/dist/host/errors.d.ts +23 -0
- package/dist/host/errors.d.ts.map +1 -0
- package/dist/host/errors.js +32 -0
- package/dist/host/errors.js.map +1 -0
- package/dist/host/index.d.ts +7 -0
- package/dist/host/index.d.ts.map +1 -0
- package/dist/host/index.js +6 -0
- package/dist/host/index.js.map +1 -0
- package/dist/host/jwt-execution-host-mcp-capability-verifier.d.ts +8 -0
- package/dist/host/jwt-execution-host-mcp-capability-verifier.d.ts.map +1 -0
- package/dist/host/jwt-execution-host-mcp-capability-verifier.js +86 -0
- package/dist/host/jwt-execution-host-mcp-capability-verifier.js.map +1 -0
- package/dist/host/jwt-execution-identity-verifier.d.ts +8 -0
- package/dist/host/jwt-execution-identity-verifier.d.ts.map +1 -0
- package/dist/host/jwt-execution-identity-verifier.js +76 -0
- package/dist/host/jwt-execution-identity-verifier.js.map +1 -0
- package/dist/host/schemas.d.ts +37 -0
- package/dist/host/schemas.d.ts.map +1 -0
- package/dist/host/schemas.js +24 -0
- package/dist/host/schemas.js.map +1 -0
- package/dist/host/types.d.ts +62 -0
- package/dist/host/types.d.ts.map +1 -0
- package/dist/host/types.js +7 -0
- package/dist/host/types.js.map +1 -0
- package/dist/internal/jwt-verification.d.ts +5 -0
- package/dist/internal/jwt-verification.d.ts.map +1 -0
- package/dist/internal/jwt-verification.js +25 -0
- package/dist/internal/jwt-verification.js.map +1 -0
- package/dist/mcp/jwt-capability-verifier.d.ts.map +1 -1
- package/dist/mcp/jwt-capability-verifier.js +6 -25
- package/dist/mcp/jwt-capability-verifier.js.map +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -5,7 +5,8 @@ products that invoke a separately deployed Heddle Execution Host. It lets an
|
|
|
5
5
|
adopter keep its own language stack, authentication, database, product policy,
|
|
6
6
|
MCP tools, and UI while reusing the security-sensitive v1 contract machinery.
|
|
7
7
|
|
|
8
|
-
> **Current availability:**
|
|
8
|
+
> **Current availability:** `6.5.0` is the next manual release from this
|
|
9
|
+
> repository; `6.4.0` remains the latest published version until then. The former
|
|
9
10
|
> `@roackb2/heddle-adopter@5.13.0` coordinate is deprecated and remains
|
|
10
11
|
> installable only for existing consumers. Heddle does not currently distribute
|
|
11
12
|
> the compatible Execution Host implementation or offer a hosted service. The
|
|
@@ -37,6 +38,7 @@ modular AWS SDK for its AgentCore transport and the official MCP SDK, plus
|
|
|
37
38
|
| `@heddleagent/execution-host-client/mcp/node` | Stateless official-SDK Streamable HTTP lifecycle around adopter-defined toolsets |
|
|
38
39
|
| `@heddleagent/execution-host-client/http-sse` | Transport-neutral conversation and heartbeat ports plus the strict direct-development HTTP/SSE client |
|
|
39
40
|
| `@heddleagent/execution-host-client/agentcore` | Official AWS AgentCore/SigV4 implementation of the same conversation and heartbeat ports |
|
|
41
|
+
| `@heddleagent/execution-host-client/host` | Invocation-bound execution-identity and MCP-capability verification shared by compatible Execution Host implementations |
|
|
40
42
|
| `@heddleagent/execution-host-client/testing` | Node-only loopback v1 fixture plus durable-turn store conformance for real adapters |
|
|
41
43
|
| `@heddleagent/execution-host-client/node` | Optional Node JWKS/conversation HTTP edge plus safe local signing-key helpers |
|
|
42
44
|
|
|
@@ -230,6 +232,50 @@ const productMcp = new NodeStreamableHttpMcpService({
|
|
|
230
232
|
Use the lower-level `NodeMcpToolset` interface only when a tool needs custom MCP
|
|
231
233
|
content or lifecycle semantics.
|
|
232
234
|
|
|
235
|
+
## Verify authority inside a compatible Execution Host
|
|
236
|
+
|
|
237
|
+
The host must independently verify the adopter's execution assertion before it
|
|
238
|
+
trusts product scope, then bind any optional MCP capability to that same
|
|
239
|
+
verified invocation. Compatible host implementations can reuse that generic
|
|
240
|
+
security boundary instead of maintaining their own JOSE logic:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
import {
|
|
244
|
+
JwtExecutionHostMcpCapabilityVerifier,
|
|
245
|
+
JwtExecutionIdentityVerifier,
|
|
246
|
+
} from '@heddleagent/execution-host-client/host'
|
|
247
|
+
|
|
248
|
+
const identity = await new JwtExecutionIdentityVerifier({
|
|
249
|
+
executionIssuer: 'https://api.example.com',
|
|
250
|
+
executionAudience: 'heddle-execution-host',
|
|
251
|
+
executionJwksUrl: new URL('https://api.example.com/.well-known/jwks.json'),
|
|
252
|
+
executionJwtAlgorithms: ['ES256'],
|
|
253
|
+
trustedAdopterId: 'example-product',
|
|
254
|
+
maxAssertionAgeSeconds: 300,
|
|
255
|
+
assertionClockToleranceSeconds: 5,
|
|
256
|
+
}).verify({
|
|
257
|
+
assertion: executionAssertion,
|
|
258
|
+
runtimeSessionId,
|
|
259
|
+
invocationId,
|
|
260
|
+
workflow: 'conversation-turn',
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
const capability = await new JwtExecutionHostMcpCapabilityVerifier({
|
|
264
|
+
issuer: 'https://api.example.com',
|
|
265
|
+
audience: 'example-product-mcp',
|
|
266
|
+
jwksUrl: new URL('https://api.example.com/.well-known/jwks.json'),
|
|
267
|
+
jwtAlgorithms: ['ES256'],
|
|
268
|
+
trustedAdopterId: 'example-product',
|
|
269
|
+
serverId: 'product_capabilities',
|
|
270
|
+
maxCapabilityAgeSeconds: 900,
|
|
271
|
+
clockToleranceSeconds: 5,
|
|
272
|
+
}).verify({ assertion: mcpCapability, identity })
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
This surface owns credential verification and exact scope binding. It does not
|
|
276
|
+
own HTTP ingress, Runtime-session isolation, model credentials, streaming,
|
|
277
|
+
provider bootstrap, or deployment.
|
|
278
|
+
|
|
233
279
|
## Invoke an Execution Host
|
|
234
280
|
|
|
235
281
|
Use the official AgentCore client for a Runtime deployed in the adopter's AWS
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { JwtVerificationUnavailableCategory } from '../internal/jwt-verification.js';
|
|
2
|
+
export declare class ExecutionIdentityVerificationError extends Error {
|
|
3
|
+
readonly name = "ExecutionIdentityVerificationError";
|
|
4
|
+
constructor(options?: ErrorOptions);
|
|
5
|
+
}
|
|
6
|
+
export declare class ExecutionIdentityUnavailableError extends Error {
|
|
7
|
+
readonly category: JwtVerificationUnavailableCategory;
|
|
8
|
+
readonly name = "ExecutionIdentityUnavailableError";
|
|
9
|
+
constructor(category: JwtVerificationUnavailableCategory, options?: ErrorOptions);
|
|
10
|
+
}
|
|
11
|
+
export declare class ExecutionHostMcpCapabilityVerificationError extends Error {
|
|
12
|
+
readonly name = "ExecutionHostMcpCapabilityVerificationError";
|
|
13
|
+
constructor(options?: ErrorOptions);
|
|
14
|
+
}
|
|
15
|
+
export declare class ExecutionHostMcpCapabilityUnavailableError extends Error {
|
|
16
|
+
readonly category: JwtVerificationUnavailableCategory;
|
|
17
|
+
readonly name = "ExecutionHostMcpCapabilityUnavailableError";
|
|
18
|
+
constructor(category: JwtVerificationUnavailableCategory, options?: ErrorOptions);
|
|
19
|
+
}
|
|
20
|
+
export declare class UnexpectedExecutionHostMcpCapabilityError extends Error {
|
|
21
|
+
readonly name = "UnexpectedExecutionHostMcpCapabilityError";
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/host/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,iCAAiC,CAAC;AAE1F,qBAAa,kCAAmC,SAAQ,KAAK;IAC3D,QAAQ,CAAC,IAAI,wCAAwC;gBAEzC,OAAO,CAAC,EAAE,YAAY;CAGnC;AAED,qBAAa,iCAAkC,SAAQ,KAAK;IAIxD,QAAQ,CAAC,QAAQ,EAAE,kCAAkC;IAHvD,QAAQ,CAAC,IAAI,uCAAuC;gBAGzC,QAAQ,EAAE,kCAAkC,EACrD,OAAO,CAAC,EAAE,YAAY;CAIzB;AAED,qBAAa,2CAA4C,SAAQ,KAAK;IACpE,QAAQ,CAAC,IAAI,iDAAiD;gBAElD,OAAO,CAAC,EAAE,YAAY;CAGnC;AAED,qBAAa,0CAA2C,SAAQ,KAAK;IAIjE,QAAQ,CAAC,QAAQ,EAAE,kCAAkC;IAHvD,QAAQ,CAAC,IAAI,gDAAgD;gBAGlD,QAAQ,EAAE,kCAAkC,EACrD,OAAO,CAAC,EAAE,YAAY;CAIzB;AAED,qBAAa,yCAA0C,SAAQ,KAAK;IAClE,QAAQ,CAAC,IAAI,+CAA+C;CAC7D"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export class ExecutionIdentityVerificationError extends Error {
|
|
2
|
+
name = 'ExecutionIdentityVerificationError';
|
|
3
|
+
constructor(options) {
|
|
4
|
+
super('Execution identity verification failed.', options);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class ExecutionIdentityUnavailableError extends Error {
|
|
8
|
+
category;
|
|
9
|
+
name = 'ExecutionIdentityUnavailableError';
|
|
10
|
+
constructor(category, options) {
|
|
11
|
+
super('Execution identity verification is temporarily unavailable.', options);
|
|
12
|
+
this.category = category;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export class ExecutionHostMcpCapabilityVerificationError extends Error {
|
|
16
|
+
name = 'ExecutionHostMcpCapabilityVerificationError';
|
|
17
|
+
constructor(options) {
|
|
18
|
+
super('MCP capability verification failed.', options);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export class ExecutionHostMcpCapabilityUnavailableError extends Error {
|
|
22
|
+
category;
|
|
23
|
+
name = 'ExecutionHostMcpCapabilityUnavailableError';
|
|
24
|
+
constructor(category, options) {
|
|
25
|
+
super('MCP capability verification is temporarily unavailable.', options);
|
|
26
|
+
this.category = category;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export class UnexpectedExecutionHostMcpCapabilityError extends Error {
|
|
30
|
+
name = 'UnexpectedExecutionHostMcpCapabilityError';
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/host/errors.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,kCAAmC,SAAQ,KAAK;IAClD,IAAI,GAAG,oCAAoC,CAAC;IAErD,YAAY,OAAsB;QAChC,KAAK,CAAC,yCAAyC,EAAE,OAAO,CAAC,CAAC;IAC5D,CAAC;CACF;AAED,MAAM,OAAO,iCAAkC,SAAQ,KAAK;IAI/C;IAHF,IAAI,GAAG,mCAAmC,CAAC;IAEpD,YACW,QAA4C,EACrD,OAAsB;QAEtB,KAAK,CAAC,6DAA6D,EAAE,OAAO,CAAC,CAAC;QAHrE,aAAQ,GAAR,QAAQ,CAAoC;IAIvD,CAAC;CACF;AAED,MAAM,OAAO,2CAA4C,SAAQ,KAAK;IAC3D,IAAI,GAAG,6CAA6C,CAAC;IAE9D,YAAY,OAAsB;QAChC,KAAK,CAAC,qCAAqC,EAAE,OAAO,CAAC,CAAC;IACxD,CAAC;CACF;AAED,MAAM,OAAO,0CAA2C,SAAQ,KAAK;IAIxD;IAHF,IAAI,GAAG,4CAA4C,CAAC;IAE7D,YACW,QAA4C,EACrD,OAAsB;QAEtB,KAAK,CAAC,yDAAyD,EAAE,OAAO,CAAC,CAAC;QAHjE,aAAQ,GAAR,QAAQ,CAAoC;IAIvD,CAAC;CACF;AAED,MAAM,OAAO,yCAA0C,SAAQ,KAAK;IACzD,IAAI,GAAG,2CAA2C,CAAC;CAC7D","sourcesContent":["import type { JwtVerificationUnavailableCategory } from '../internal/jwt-verification.js';\n\nexport class ExecutionIdentityVerificationError extends Error {\n readonly name = 'ExecutionIdentityVerificationError';\n\n constructor(options?: ErrorOptions) {\n super('Execution identity verification failed.', options);\n }\n}\n\nexport class ExecutionIdentityUnavailableError extends Error {\n readonly name = 'ExecutionIdentityUnavailableError';\n\n constructor(\n readonly category: JwtVerificationUnavailableCategory,\n options?: ErrorOptions,\n ) {\n super('Execution identity verification is temporarily unavailable.', options);\n }\n}\n\nexport class ExecutionHostMcpCapabilityVerificationError extends Error {\n readonly name = 'ExecutionHostMcpCapabilityVerificationError';\n\n constructor(options?: ErrorOptions) {\n super('MCP capability verification failed.', options);\n }\n}\n\nexport class ExecutionHostMcpCapabilityUnavailableError extends Error {\n readonly name = 'ExecutionHostMcpCapabilityUnavailableError';\n\n constructor(\n readonly category: JwtVerificationUnavailableCategory,\n options?: ErrorOptions,\n ) {\n super('MCP capability verification is temporarily unavailable.', options);\n }\n}\n\nexport class UnexpectedExecutionHostMcpCapabilityError extends Error {\n readonly name = 'UnexpectedExecutionHostMcpCapabilityError';\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { ExecutionHostMcpCapabilityUnavailableError, ExecutionHostMcpCapabilityVerificationError, ExecutionIdentityUnavailableError, ExecutionIdentityVerificationError, UnexpectedExecutionHostMcpCapabilityError, } from './errors.js';
|
|
2
|
+
export { JwtExecutionIdentityVerifier } from './jwt-execution-identity-verifier.js';
|
|
3
|
+
export { JwtExecutionHostMcpCapabilityVerifier } from './jwt-execution-host-mcp-capability-verifier.js';
|
|
4
|
+
export { ExecutionHostJwtAlgorithmsSchema, JwtExecutionHostMcpCapabilityVerifierConfigSchema, JwtExecutionIdentityVerifierConfigSchema, } from './schemas.js';
|
|
5
|
+
export { EXECUTION_HOST_SUPPORTED_JWT_ALGORITHMS, } from './types.js';
|
|
6
|
+
export type { ExecutionHostJwtAlgorithm, ExecutionHostJwtVerifierOptions, ExecutionHostMcpCapabilityVerificationRequest, ExecutionHostMcpCapabilityVerifier, ExecutionIdentityVerificationRequest, ExecutionIdentityVerifier, JwtExecutionHostMcpCapabilityVerifierConfig, JwtExecutionIdentityVerifierConfig, VerifiedExecutionHostMcpCapability, VerifiedExecutionIdentity, } from './types.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/host/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0CAA0C,EAC1C,2CAA2C,EAC3C,iCAAiC,EACjC,kCAAkC,EAClC,yCAAyC,GAC1C,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;AACpF,OAAO,EAAE,qCAAqC,EAAE,MAAM,iDAAiD,CAAC;AACxG,OAAO,EACL,gCAAgC,EAChC,iDAAiD,EACjD,wCAAwC,GACzC,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,uCAAuC,GACxC,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,yBAAyB,EACzB,+BAA+B,EAC/B,6CAA6C,EAC7C,kCAAkC,EAClC,oCAAoC,EACpC,yBAAyB,EACzB,2CAA2C,EAC3C,kCAAkC,EAClC,kCAAkC,EAClC,yBAAyB,GAC1B,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { ExecutionHostMcpCapabilityUnavailableError, ExecutionHostMcpCapabilityVerificationError, ExecutionIdentityUnavailableError, ExecutionIdentityVerificationError, UnexpectedExecutionHostMcpCapabilityError, } from './errors.js';
|
|
2
|
+
export { JwtExecutionIdentityVerifier } from './jwt-execution-identity-verifier.js';
|
|
3
|
+
export { JwtExecutionHostMcpCapabilityVerifier } from './jwt-execution-host-mcp-capability-verifier.js';
|
|
4
|
+
export { ExecutionHostJwtAlgorithmsSchema, JwtExecutionHostMcpCapabilityVerifierConfigSchema, JwtExecutionIdentityVerifierConfigSchema, } from './schemas.js';
|
|
5
|
+
export { EXECUTION_HOST_SUPPORTED_JWT_ALGORITHMS, } from './types.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/host/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0CAA0C,EAC1C,2CAA2C,EAC3C,iCAAiC,EACjC,kCAAkC,EAClC,yCAAyC,GAC1C,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;AACpF,OAAO,EAAE,qCAAqC,EAAE,MAAM,iDAAiD,CAAC;AACxG,OAAO,EACL,gCAAgC,EAChC,iDAAiD,EACjD,wCAAwC,GACzC,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,uCAAuC,GACxC,MAAM,YAAY,CAAC","sourcesContent":["export {\n ExecutionHostMcpCapabilityUnavailableError,\n ExecutionHostMcpCapabilityVerificationError,\n ExecutionIdentityUnavailableError,\n ExecutionIdentityVerificationError,\n UnexpectedExecutionHostMcpCapabilityError,\n} from './errors.js';\nexport { JwtExecutionIdentityVerifier } from './jwt-execution-identity-verifier.js';\nexport { JwtExecutionHostMcpCapabilityVerifier } from './jwt-execution-host-mcp-capability-verifier.js';\nexport {\n ExecutionHostJwtAlgorithmsSchema,\n JwtExecutionHostMcpCapabilityVerifierConfigSchema,\n JwtExecutionIdentityVerifierConfigSchema,\n} from './schemas.js';\nexport {\n EXECUTION_HOST_SUPPORTED_JWT_ALGORITHMS,\n} from './types.js';\nexport type {\n ExecutionHostJwtAlgorithm,\n ExecutionHostJwtVerifierOptions,\n ExecutionHostMcpCapabilityVerificationRequest,\n ExecutionHostMcpCapabilityVerifier,\n ExecutionIdentityVerificationRequest,\n ExecutionIdentityVerifier,\n JwtExecutionHostMcpCapabilityVerifierConfig,\n JwtExecutionIdentityVerifierConfig,\n VerifiedExecutionHostMcpCapability,\n VerifiedExecutionIdentity,\n} from './types.js';\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ExecutionHostJwtVerifierOptions, ExecutionHostMcpCapabilityVerificationRequest, ExecutionHostMcpCapabilityVerifier, JwtExecutionHostMcpCapabilityVerifierConfig, VerifiedExecutionHostMcpCapability } from './types.js';
|
|
2
|
+
/** Verifies MCP authority against an independently verified host invocation. */
|
|
3
|
+
export declare class JwtExecutionHostMcpCapabilityVerifier implements ExecutionHostMcpCapabilityVerifier {
|
|
4
|
+
#private;
|
|
5
|
+
constructor(config: JwtExecutionHostMcpCapabilityVerifierConfig, options?: ExecutionHostJwtVerifierOptions);
|
|
6
|
+
verify(rawRequest: ExecutionHostMcpCapabilityVerificationRequest): Promise<VerifiedExecutionHostMcpCapability>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=jwt-execution-host-mcp-capability-verifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-execution-host-mcp-capability-verifier.d.ts","sourceRoot":"","sources":["../../src/host/jwt-execution-host-mcp-capability-verifier.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,+BAA+B,EAC/B,6CAA6C,EAC7C,kCAAkC,EAClC,2CAA2C,EAC3C,kCAAkC,EACnC,MAAM,YAAY,CAAC;AAOpB,gFAAgF;AAChF,qBAAa,qCACb,YAAW,kCAAkC;;gBAMzC,MAAM,EAAE,2CAA2C,EACnD,OAAO,GAAE,+BAAoC;IAczC,MAAM,CACV,UAAU,EAAE,6CAA6C,GACxD,OAAO,CAAC,kCAAkC,CAAC;CAkF/C"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { jwtVerify } from 'jose';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { MCP_CAPABILITY_TYPE, McpCapabilityClaimsSchema, } from '../contracts/index.js';
|
|
4
|
+
import { createJwtKeyResolver, resolveJwtVerificationUnavailableCategory, } from '../internal/jwt-verification.js';
|
|
5
|
+
import { ExecutionHostMcpCapabilityUnavailableError, ExecutionHostMcpCapabilityVerificationError, } from './errors.js';
|
|
6
|
+
import { JwtExecutionHostMcpCapabilityVerifierConfigSchema } from './schemas.js';
|
|
7
|
+
const AssertionSchema = z.string().trim().min(32).max(4_096);
|
|
8
|
+
/** Verifies MCP authority against an independently verified host invocation. */
|
|
9
|
+
export class JwtExecutionHostMcpCapabilityVerifier {
|
|
10
|
+
#config;
|
|
11
|
+
#keyResolver;
|
|
12
|
+
#now;
|
|
13
|
+
constructor(config, options = {}) {
|
|
14
|
+
const parsed = JwtExecutionHostMcpCapabilityVerifierConfigSchema.parse(config);
|
|
15
|
+
this.#config = Object.freeze({
|
|
16
|
+
...parsed,
|
|
17
|
+
jwksUrl: new URL(parsed.jwksUrl),
|
|
18
|
+
});
|
|
19
|
+
this.#keyResolver = options.keyResolver
|
|
20
|
+
?? createJwtKeyResolver(this.#config.jwksUrl);
|
|
21
|
+
this.#now = options.now ?? (() => new Date());
|
|
22
|
+
}
|
|
23
|
+
async verify(rawRequest) {
|
|
24
|
+
try {
|
|
25
|
+
const request = Object.freeze({
|
|
26
|
+
...rawRequest,
|
|
27
|
+
assertion: AssertionSchema.parse(rawRequest.assertion),
|
|
28
|
+
});
|
|
29
|
+
const { payload } = await jwtVerify(request.assertion, this.#keyResolver, {
|
|
30
|
+
algorithms: [...this.#config.jwtAlgorithms],
|
|
31
|
+
audience: this.#config.audience,
|
|
32
|
+
clockTolerance: this.#config.clockToleranceSeconds,
|
|
33
|
+
currentDate: this.#now(),
|
|
34
|
+
issuer: this.#config.issuer,
|
|
35
|
+
maxTokenAge: this.#config.maxCapabilityAgeSeconds,
|
|
36
|
+
requiredClaims: ['exp', 'iat', 'jti', 'sub'],
|
|
37
|
+
typ: MCP_CAPABILITY_TYPE,
|
|
38
|
+
});
|
|
39
|
+
const claims = McpCapabilityClaimsSchema.parse(payload);
|
|
40
|
+
this.#assertInvocationBinding(claims, request);
|
|
41
|
+
this.#assertBoundedLifetime(claims);
|
|
42
|
+
return Object.freeze({
|
|
43
|
+
assertion: request.assertion,
|
|
44
|
+
capabilityId: claims.jti,
|
|
45
|
+
serverId: claims.serverId,
|
|
46
|
+
allowedTools: Object.freeze([...claims.allowedTools]),
|
|
47
|
+
issuedAt: new Date(claims.iat * 1_000).toISOString(),
|
|
48
|
+
expiresAt: new Date(claims.exp * 1_000).toISOString(),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
if (error instanceof ExecutionHostMcpCapabilityVerificationError
|
|
53
|
+
|| error instanceof ExecutionHostMcpCapabilityUnavailableError) {
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
const category = resolveJwtVerificationUnavailableCategory(error);
|
|
57
|
+
if (category) {
|
|
58
|
+
throw new ExecutionHostMcpCapabilityUnavailableError(category, { cause: error });
|
|
59
|
+
}
|
|
60
|
+
throw new ExecutionHostMcpCapabilityVerificationError({ cause: error });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
#assertInvocationBinding(claims, request) {
|
|
64
|
+
const { identity } = request;
|
|
65
|
+
if (claims.adopterId !== this.#config.trustedAdopterId
|
|
66
|
+
|| claims.adopterId !== identity.scope.adopterId
|
|
67
|
+
|| claims.tenantId !== identity.scope.tenantId
|
|
68
|
+
|| claims.sub !== identity.scope.subjectId
|
|
69
|
+
|| claims.productSessionId !== identity.scope.productSessionId
|
|
70
|
+
|| claims.runtimeSessionId !== identity.runtimeSessionId
|
|
71
|
+
|| claims.invocationId !== identity.invocationId
|
|
72
|
+
|| claims.workflow !== identity.workflow
|
|
73
|
+
|| claims.serverId !== this.#config.serverId
|
|
74
|
+
|| claims.jti === identity.invocationId) {
|
|
75
|
+
throw new ExecutionHostMcpCapabilityVerificationError();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
#assertBoundedLifetime(claims) {
|
|
79
|
+
const lifetimeSeconds = claims.exp - claims.iat;
|
|
80
|
+
if (lifetimeSeconds <= 0
|
|
81
|
+
|| lifetimeSeconds > this.#config.maxCapabilityAgeSeconds) {
|
|
82
|
+
throw new ExecutionHostMcpCapabilityVerificationError();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=jwt-execution-host-mcp-capability-verifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-execution-host-mcp-capability-verifier.js","sourceRoot":"","sources":["../../src/host/jwt-execution-host-mcp-capability-verifier.ts"],"names":[],"mappings":"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","sourcesContent":["import { jwtVerify } from 'jose';\nimport { z } from 'zod';\nimport {\n MCP_CAPABILITY_TYPE,\n McpCapabilityClaimsSchema,\n} from '../contracts/index.js';\nimport {\n createJwtKeyResolver,\n resolveJwtVerificationUnavailableCategory,\n} from '../internal/jwt-verification.js';\nimport {\n ExecutionHostMcpCapabilityUnavailableError,\n ExecutionHostMcpCapabilityVerificationError,\n} from './errors.js';\nimport { JwtExecutionHostMcpCapabilityVerifierConfigSchema } from './schemas.js';\nimport type {\n ExecutionHostJwtVerifierOptions,\n ExecutionHostMcpCapabilityVerificationRequest,\n ExecutionHostMcpCapabilityVerifier,\n JwtExecutionHostMcpCapabilityVerifierConfig,\n VerifiedExecutionHostMcpCapability,\n} from './types.js';\n\nconst AssertionSchema = z.string().trim().min(32).max(4_096);\ntype ParsedConfig = z.infer<\n typeof JwtExecutionHostMcpCapabilityVerifierConfigSchema\n>;\n\n/** Verifies MCP authority against an independently verified host invocation. */\nexport class JwtExecutionHostMcpCapabilityVerifier\nimplements ExecutionHostMcpCapabilityVerifier {\n readonly #config: Readonly<ParsedConfig>;\n readonly #keyResolver: NonNullable<ExecutionHostJwtVerifierOptions['keyResolver']>;\n readonly #now: () => Date;\n\n constructor(\n config: JwtExecutionHostMcpCapabilityVerifierConfig,\n options: ExecutionHostJwtVerifierOptions = {},\n ) {\n const parsed = JwtExecutionHostMcpCapabilityVerifierConfigSchema.parse(\n config,\n );\n this.#config = Object.freeze({\n ...parsed,\n jwksUrl: new URL(parsed.jwksUrl),\n });\n this.#keyResolver = options.keyResolver\n ?? createJwtKeyResolver(this.#config.jwksUrl);\n this.#now = options.now ?? (() => new Date());\n }\n\n async verify(\n rawRequest: ExecutionHostMcpCapabilityVerificationRequest,\n ): Promise<VerifiedExecutionHostMcpCapability> {\n try {\n const request = Object.freeze({\n ...rawRequest,\n assertion: AssertionSchema.parse(rawRequest.assertion),\n });\n const { payload } = await jwtVerify(\n request.assertion,\n this.#keyResolver,\n {\n algorithms: [...this.#config.jwtAlgorithms],\n audience: this.#config.audience,\n clockTolerance: this.#config.clockToleranceSeconds,\n currentDate: this.#now(),\n issuer: this.#config.issuer,\n maxTokenAge: this.#config.maxCapabilityAgeSeconds,\n requiredClaims: ['exp', 'iat', 'jti', 'sub'],\n typ: MCP_CAPABILITY_TYPE,\n },\n );\n const claims = McpCapabilityClaimsSchema.parse(payload);\n this.#assertInvocationBinding(claims, request);\n this.#assertBoundedLifetime(claims);\n\n return Object.freeze({\n assertion: request.assertion,\n capabilityId: claims.jti,\n serverId: claims.serverId,\n allowedTools: Object.freeze([...claims.allowedTools]),\n issuedAt: new Date(claims.iat * 1_000).toISOString(),\n expiresAt: new Date(claims.exp * 1_000).toISOString(),\n });\n } catch (error) {\n if (\n error instanceof ExecutionHostMcpCapabilityVerificationError\n || error instanceof ExecutionHostMcpCapabilityUnavailableError\n ) {\n throw error;\n }\n const category = resolveJwtVerificationUnavailableCategory(error);\n if (category) {\n throw new ExecutionHostMcpCapabilityUnavailableError(\n category,\n { cause: error },\n );\n }\n throw new ExecutionHostMcpCapabilityVerificationError({ cause: error });\n }\n }\n\n #assertInvocationBinding(\n claims: z.infer<typeof McpCapabilityClaimsSchema>,\n request: ExecutionHostMcpCapabilityVerificationRequest,\n ): void {\n const { identity } = request;\n if (\n claims.adopterId !== this.#config.trustedAdopterId\n || claims.adopterId !== identity.scope.adopterId\n || claims.tenantId !== identity.scope.tenantId\n || claims.sub !== identity.scope.subjectId\n || claims.productSessionId !== identity.scope.productSessionId\n || claims.runtimeSessionId !== identity.runtimeSessionId\n || claims.invocationId !== identity.invocationId\n || claims.workflow !== identity.workflow\n || claims.serverId !== this.#config.serverId\n || claims.jti === identity.invocationId\n ) {\n throw new ExecutionHostMcpCapabilityVerificationError();\n }\n }\n\n #assertBoundedLifetime(\n claims: z.infer<typeof McpCapabilityClaimsSchema>,\n ): void {\n const lifetimeSeconds = claims.exp - claims.iat;\n if (\n lifetimeSeconds <= 0\n || lifetimeSeconds > this.#config.maxCapabilityAgeSeconds\n ) {\n throw new ExecutionHostMcpCapabilityVerificationError();\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ExecutionIdentityVerificationRequest, ExecutionIdentityVerifier, ExecutionHostJwtVerifierOptions, JwtExecutionIdentityVerifierConfig, VerifiedExecutionIdentity } from './types.js';
|
|
2
|
+
/** Verifies adopter authority and binds it to one exact host invocation. */
|
|
3
|
+
export declare class JwtExecutionIdentityVerifier implements ExecutionIdentityVerifier {
|
|
4
|
+
#private;
|
|
5
|
+
constructor(config: JwtExecutionIdentityVerifierConfig, options?: ExecutionHostJwtVerifierOptions);
|
|
6
|
+
verify(rawRequest: ExecutionIdentityVerificationRequest): Promise<VerifiedExecutionIdentity>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=jwt-execution-identity-verifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-execution-identity-verifier.d.ts","sourceRoot":"","sources":["../../src/host/jwt-execution-identity-verifier.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,oCAAoC,EACpC,yBAAyB,EACzB,+BAA+B,EAC/B,kCAAkC,EAClC,yBAAyB,EAC1B,MAAM,YAAY,CAAC;AAKpB,4EAA4E;AAC5E,qBAAa,4BAA6B,YAAW,yBAAyB;;gBAM1E,MAAM,EAAE,kCAAkC,EAC1C,OAAO,GAAE,+BAAoC;IAYzC,MAAM,CACV,UAAU,EAAE,oCAAoC,GAC/C,OAAO,CAAC,yBAAyB,CAAC;CAgEtC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { jwtVerify } from 'jose';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { EXECUTION_ASSERTION_TYPE, ExecutionAssertionClaimsSchema, } from '../contracts/index.js';
|
|
4
|
+
import { createJwtKeyResolver, resolveJwtVerificationUnavailableCategory, } from '../internal/jwt-verification.js';
|
|
5
|
+
import { ExecutionIdentityUnavailableError, ExecutionIdentityVerificationError, } from './errors.js';
|
|
6
|
+
import { JwtExecutionIdentityVerifierConfigSchema } from './schemas.js';
|
|
7
|
+
const AssertionSchema = z.string().trim().min(32).max(4_096);
|
|
8
|
+
/** Verifies adopter authority and binds it to one exact host invocation. */
|
|
9
|
+
export class JwtExecutionIdentityVerifier {
|
|
10
|
+
#config;
|
|
11
|
+
#keyResolver;
|
|
12
|
+
#now;
|
|
13
|
+
constructor(config, options = {}) {
|
|
14
|
+
const parsed = JwtExecutionIdentityVerifierConfigSchema.parse(config);
|
|
15
|
+
this.#config = Object.freeze({
|
|
16
|
+
...parsed,
|
|
17
|
+
executionJwksUrl: new URL(parsed.executionJwksUrl),
|
|
18
|
+
});
|
|
19
|
+
this.#keyResolver = options.keyResolver
|
|
20
|
+
?? createJwtKeyResolver(this.#config.executionJwksUrl);
|
|
21
|
+
this.#now = options.now ?? (() => new Date());
|
|
22
|
+
}
|
|
23
|
+
async verify(rawRequest) {
|
|
24
|
+
try {
|
|
25
|
+
const request = Object.freeze({
|
|
26
|
+
...rawRequest,
|
|
27
|
+
assertion: AssertionSchema.parse(rawRequest.assertion),
|
|
28
|
+
});
|
|
29
|
+
const { payload } = await jwtVerify(request.assertion, this.#keyResolver, {
|
|
30
|
+
algorithms: [...this.#config.executionJwtAlgorithms],
|
|
31
|
+
audience: this.#config.executionAudience,
|
|
32
|
+
clockTolerance: this.#config.assertionClockToleranceSeconds,
|
|
33
|
+
currentDate: this.#now(),
|
|
34
|
+
issuer: this.#config.executionIssuer,
|
|
35
|
+
maxTokenAge: this.#config.maxAssertionAgeSeconds,
|
|
36
|
+
requiredClaims: ['exp', 'iat', 'jti', 'sub'],
|
|
37
|
+
typ: EXECUTION_ASSERTION_TYPE,
|
|
38
|
+
});
|
|
39
|
+
const claims = ExecutionAssertionClaimsSchema.parse(payload);
|
|
40
|
+
this.#assertRequestBinding(claims, request);
|
|
41
|
+
return Object.freeze({
|
|
42
|
+
scope: Object.freeze({
|
|
43
|
+
adopterId: claims.adopterId,
|
|
44
|
+
tenantId: claims.tenantId,
|
|
45
|
+
subjectId: claims.sub,
|
|
46
|
+
productSessionId: claims.productSessionId,
|
|
47
|
+
}),
|
|
48
|
+
runtimeSessionId: claims.runtimeSessionId,
|
|
49
|
+
invocationId: claims.jti,
|
|
50
|
+
workflow: claims.workflow,
|
|
51
|
+
issuedAt: new Date(claims.iat * 1_000).toISOString(),
|
|
52
|
+
expiresAt: new Date(claims.exp * 1_000).toISOString(),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (error instanceof ExecutionIdentityVerificationError
|
|
57
|
+
|| error instanceof ExecutionIdentityUnavailableError) {
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
const category = resolveJwtVerificationUnavailableCategory(error);
|
|
61
|
+
if (category) {
|
|
62
|
+
throw new ExecutionIdentityUnavailableError(category, { cause: error });
|
|
63
|
+
}
|
|
64
|
+
throw new ExecutionIdentityVerificationError({ cause: error });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
#assertRequestBinding(claims, request) {
|
|
68
|
+
if (claims.adopterId !== this.#config.trustedAdopterId
|
|
69
|
+
|| claims.runtimeSessionId !== request.runtimeSessionId
|
|
70
|
+
|| claims.jti !== request.invocationId
|
|
71
|
+
|| claims.workflow !== request.workflow) {
|
|
72
|
+
throw new ExecutionIdentityVerificationError();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=jwt-execution-identity-verifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-execution-identity-verifier.js","sourceRoot":"","sources":["../../src/host/jwt-execution-identity-verifier.ts"],"names":[],"mappings":"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","sourcesContent":["import { jwtVerify } from 'jose';\nimport { z } from 'zod';\nimport {\n EXECUTION_ASSERTION_TYPE,\n ExecutionAssertionClaimsSchema,\n} from '../contracts/index.js';\nimport {\n createJwtKeyResolver,\n resolveJwtVerificationUnavailableCategory,\n} from '../internal/jwt-verification.js';\nimport {\n ExecutionIdentityUnavailableError,\n ExecutionIdentityVerificationError,\n} from './errors.js';\nimport { JwtExecutionIdentityVerifierConfigSchema } from './schemas.js';\nimport type {\n ExecutionIdentityVerificationRequest,\n ExecutionIdentityVerifier,\n ExecutionHostJwtVerifierOptions,\n JwtExecutionIdentityVerifierConfig,\n VerifiedExecutionIdentity,\n} from './types.js';\n\nconst AssertionSchema = z.string().trim().min(32).max(4_096);\ntype ParsedConfig = z.infer<typeof JwtExecutionIdentityVerifierConfigSchema>;\n\n/** Verifies adopter authority and binds it to one exact host invocation. */\nexport class JwtExecutionIdentityVerifier implements ExecutionIdentityVerifier {\n readonly #config: Readonly<ParsedConfig>;\n readonly #keyResolver: NonNullable<ExecutionHostJwtVerifierOptions['keyResolver']>;\n readonly #now: () => Date;\n\n constructor(\n config: JwtExecutionIdentityVerifierConfig,\n options: ExecutionHostJwtVerifierOptions = {},\n ) {\n const parsed = JwtExecutionIdentityVerifierConfigSchema.parse(config);\n this.#config = Object.freeze({\n ...parsed,\n executionJwksUrl: new URL(parsed.executionJwksUrl),\n });\n this.#keyResolver = options.keyResolver\n ?? createJwtKeyResolver(this.#config.executionJwksUrl);\n this.#now = options.now ?? (() => new Date());\n }\n\n async verify(\n rawRequest: ExecutionIdentityVerificationRequest,\n ): Promise<VerifiedExecutionIdentity> {\n try {\n const request = Object.freeze({\n ...rawRequest,\n assertion: AssertionSchema.parse(rawRequest.assertion),\n });\n const { payload } = await jwtVerify(\n request.assertion,\n this.#keyResolver,\n {\n algorithms: [...this.#config.executionJwtAlgorithms],\n audience: this.#config.executionAudience,\n clockTolerance: this.#config.assertionClockToleranceSeconds,\n currentDate: this.#now(),\n issuer: this.#config.executionIssuer,\n maxTokenAge: this.#config.maxAssertionAgeSeconds,\n requiredClaims: ['exp', 'iat', 'jti', 'sub'],\n typ: EXECUTION_ASSERTION_TYPE,\n },\n );\n const claims = ExecutionAssertionClaimsSchema.parse(payload);\n this.#assertRequestBinding(claims, request);\n\n return Object.freeze({\n scope: Object.freeze({\n adopterId: claims.adopterId,\n tenantId: claims.tenantId,\n subjectId: claims.sub,\n productSessionId: claims.productSessionId,\n }),\n runtimeSessionId: claims.runtimeSessionId,\n invocationId: claims.jti,\n workflow: claims.workflow,\n issuedAt: new Date(claims.iat * 1_000).toISOString(),\n expiresAt: new Date(claims.exp * 1_000).toISOString(),\n });\n } catch (error) {\n if (\n error instanceof ExecutionIdentityVerificationError\n || error instanceof ExecutionIdentityUnavailableError\n ) {\n throw error;\n }\n const category = resolveJwtVerificationUnavailableCategory(error);\n if (category) {\n throw new ExecutionIdentityUnavailableError(category, { cause: error });\n }\n throw new ExecutionIdentityVerificationError({ cause: error });\n }\n }\n\n #assertRequestBinding(\n claims: z.infer<typeof ExecutionAssertionClaimsSchema>,\n request: ExecutionIdentityVerificationRequest,\n ): void {\n if (\n claims.adopterId !== this.#config.trustedAdopterId\n || claims.runtimeSessionId !== request.runtimeSessionId\n || claims.jti !== request.invocationId\n || claims.workflow !== request.workflow\n ) {\n throw new ExecutionIdentityVerificationError();\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const ExecutionHostJwtAlgorithmsSchema: z.ZodPipe<z.ZodArray<z.ZodEnum<{
|
|
3
|
+
ES256: "ES256";
|
|
4
|
+
RS256: "RS256";
|
|
5
|
+
PS256: "PS256";
|
|
6
|
+
EdDSA: "EdDSA";
|
|
7
|
+
}>>, z.ZodTransform<readonly ("ES256" | "RS256" | "PS256" | "EdDSA")[], ("ES256" | "RS256" | "PS256" | "EdDSA")[]>>;
|
|
8
|
+
export declare const JwtExecutionIdentityVerifierConfigSchema: z.ZodObject<{
|
|
9
|
+
executionIssuer: z.ZodURL;
|
|
10
|
+
executionAudience: z.ZodString;
|
|
11
|
+
executionJwksUrl: z.ZodCustom<URL, URL>;
|
|
12
|
+
executionJwtAlgorithms: z.ZodPipe<z.ZodArray<z.ZodEnum<{
|
|
13
|
+
ES256: "ES256";
|
|
14
|
+
RS256: "RS256";
|
|
15
|
+
PS256: "PS256";
|
|
16
|
+
EdDSA: "EdDSA";
|
|
17
|
+
}>>, z.ZodTransform<readonly ("ES256" | "RS256" | "PS256" | "EdDSA")[], ("ES256" | "RS256" | "PS256" | "EdDSA")[]>>;
|
|
18
|
+
trustedAdopterId: z.ZodString;
|
|
19
|
+
maxAssertionAgeSeconds: z.ZodNumber;
|
|
20
|
+
assertionClockToleranceSeconds: z.ZodNumber;
|
|
21
|
+
}, z.core.$strict>;
|
|
22
|
+
export declare const JwtExecutionHostMcpCapabilityVerifierConfigSchema: z.ZodObject<{
|
|
23
|
+
issuer: z.ZodURL;
|
|
24
|
+
audience: z.ZodString;
|
|
25
|
+
jwksUrl: z.ZodCustom<URL, URL>;
|
|
26
|
+
jwtAlgorithms: z.ZodPipe<z.ZodArray<z.ZodEnum<{
|
|
27
|
+
ES256: "ES256";
|
|
28
|
+
RS256: "RS256";
|
|
29
|
+
PS256: "PS256";
|
|
30
|
+
EdDSA: "EdDSA";
|
|
31
|
+
}>>, z.ZodTransform<readonly ("ES256" | "RS256" | "PS256" | "EdDSA")[], ("ES256" | "RS256" | "PS256" | "EdDSA")[]>>;
|
|
32
|
+
trustedAdopterId: z.ZodString;
|
|
33
|
+
serverId: z.ZodString;
|
|
34
|
+
maxCapabilityAgeSeconds: z.ZodNumber;
|
|
35
|
+
clockToleranceSeconds: z.ZodNumber;
|
|
36
|
+
}, z.core.$strict>;
|
|
37
|
+
//# sourceMappingURL=schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/host/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAUxB,eAAO,MAAM,gCAAgC;;;;;mHAI5C,CAAC;AAEF,eAAO,MAAM,wCAAwC;;;;;;;;;;;;;kBAW1C,CAAC;AAEZ,eAAO,MAAM,iDAAiD;;;;;;;;;;;;;;kBAYnD,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { JwtAudienceSchema, JwtIssuerSchema, McpServerIdSchema, OpaqueIdSchema, isSafeWebUrl, } from '../contracts/index.js';
|
|
3
|
+
import { EXECUTION_HOST_SUPPORTED_JWT_ALGORITHMS } from './types.js';
|
|
4
|
+
export const ExecutionHostJwtAlgorithmsSchema = z.array(z.enum(EXECUTION_HOST_SUPPORTED_JWT_ALGORITHMS)).min(1).max(EXECUTION_HOST_SUPPORTED_JWT_ALGORITHMS.length).transform((algorithms) => Object.freeze([...new Set(algorithms)]));
|
|
5
|
+
export const JwtExecutionIdentityVerifierConfigSchema = z.object({
|
|
6
|
+
executionIssuer: JwtIssuerSchema,
|
|
7
|
+
executionAudience: JwtAudienceSchema,
|
|
8
|
+
executionJwksUrl: z.instanceof(URL).refine(isSafeWebUrl, 'must use HTTPS or loopback HTTP and contain no credentials, query, or fragment'),
|
|
9
|
+
executionJwtAlgorithms: ExecutionHostJwtAlgorithmsSchema,
|
|
10
|
+
trustedAdopterId: OpaqueIdSchema,
|
|
11
|
+
maxAssertionAgeSeconds: z.number().int().min(1).max(15 * 60),
|
|
12
|
+
assertionClockToleranceSeconds: z.number().int().min(0).max(60),
|
|
13
|
+
}).strict();
|
|
14
|
+
export const JwtExecutionHostMcpCapabilityVerifierConfigSchema = z.object({
|
|
15
|
+
issuer: JwtIssuerSchema,
|
|
16
|
+
audience: JwtAudienceSchema,
|
|
17
|
+
jwksUrl: z.instanceof(URL).refine(isSafeWebUrl, 'must use HTTPS or loopback HTTP and contain no credentials, query, or fragment'),
|
|
18
|
+
jwtAlgorithms: ExecutionHostJwtAlgorithmsSchema,
|
|
19
|
+
trustedAdopterId: OpaqueIdSchema,
|
|
20
|
+
serverId: McpServerIdSchema,
|
|
21
|
+
maxCapabilityAgeSeconds: z.number().int().min(1).max(15 * 60),
|
|
22
|
+
clockToleranceSeconds: z.number().int().min(0).max(60),
|
|
23
|
+
}).strict();
|
|
24
|
+
//# sourceMappingURL=schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/host/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,YAAY,GACb,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,uCAAuC,EAAE,MAAM,YAAY,CAAC;AAErE,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,CAAC,KAAK,CACrD,CAAC,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAChD,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,uCAAuC,CAAC,MAAM,CAAC,CAAC,SAAS,CACpE,CAAC,UAAU,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CACxD,CAAC;AAEF,MAAM,CAAC,MAAM,wCAAwC,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/D,eAAe,EAAE,eAAe;IAChC,iBAAiB,EAAE,iBAAiB;IACpC,gBAAgB,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,MAAM,CACxC,YAAY,EACZ,gFAAgF,CACjF;IACD,sBAAsB,EAAE,gCAAgC;IACxD,gBAAgB,EAAE,cAAc;IAChC,sBAAsB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC;IAC5D,8BAA8B,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;CAChE,CAAC,CAAC,MAAM,EAAE,CAAC;AAEZ,MAAM,CAAC,MAAM,iDAAiD,GAAG,CAAC,CAAC,MAAM,CAAC;IACxE,MAAM,EAAE,eAAe;IACvB,QAAQ,EAAE,iBAAiB;IAC3B,OAAO,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,MAAM,CAC/B,YAAY,EACZ,gFAAgF,CACjF;IACD,aAAa,EAAE,gCAAgC;IAC/C,gBAAgB,EAAE,cAAc;IAChC,QAAQ,EAAE,iBAAiB;IAC3B,uBAAuB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC;IAC7D,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;CACvD,CAAC,CAAC,MAAM,EAAE,CAAC","sourcesContent":["import { z } from 'zod';\nimport {\n JwtAudienceSchema,\n JwtIssuerSchema,\n McpServerIdSchema,\n OpaqueIdSchema,\n isSafeWebUrl,\n} from '../contracts/index.js';\nimport { EXECUTION_HOST_SUPPORTED_JWT_ALGORITHMS } from './types.js';\n\nexport const ExecutionHostJwtAlgorithmsSchema = z.array(\n z.enum(EXECUTION_HOST_SUPPORTED_JWT_ALGORITHMS),\n).min(1).max(EXECUTION_HOST_SUPPORTED_JWT_ALGORITHMS.length).transform(\n (algorithms) => Object.freeze([...new Set(algorithms)]),\n);\n\nexport const JwtExecutionIdentityVerifierConfigSchema = z.object({\n executionIssuer: JwtIssuerSchema,\n executionAudience: JwtAudienceSchema,\n executionJwksUrl: z.instanceof(URL).refine(\n isSafeWebUrl,\n 'must use HTTPS or loopback HTTP and contain no credentials, query, or fragment',\n ),\n executionJwtAlgorithms: ExecutionHostJwtAlgorithmsSchema,\n trustedAdopterId: OpaqueIdSchema,\n maxAssertionAgeSeconds: z.number().int().min(1).max(15 * 60),\n assertionClockToleranceSeconds: z.number().int().min(0).max(60),\n}).strict();\n\nexport const JwtExecutionHostMcpCapabilityVerifierConfigSchema = z.object({\n issuer: JwtIssuerSchema,\n audience: JwtAudienceSchema,\n jwksUrl: z.instanceof(URL).refine(\n isSafeWebUrl,\n 'must use HTTPS or loopback HTTP and contain no credentials, query, or fragment',\n ),\n jwtAlgorithms: ExecutionHostJwtAlgorithmsSchema,\n trustedAdopterId: OpaqueIdSchema,\n serverId: McpServerIdSchema,\n maxCapabilityAgeSeconds: z.number().int().min(1).max(15 * 60),\n clockToleranceSeconds: z.number().int().min(0).max(60),\n}).strict();\n"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { JWTVerifyGetKey } from 'jose';
|
|
2
|
+
import type { ExecutionScope, HostedExecutionWorkflow } from '../contracts/index.js';
|
|
3
|
+
export declare const EXECUTION_HOST_SUPPORTED_JWT_ALGORITHMS: readonly ["RS256", "PS256", "ES256", "EdDSA"];
|
|
4
|
+
export type ExecutionHostJwtAlgorithm = typeof EXECUTION_HOST_SUPPORTED_JWT_ALGORITHMS[number];
|
|
5
|
+
/** Authority established from one signed assertion and its request binding. */
|
|
6
|
+
export type VerifiedExecutionIdentity = Readonly<{
|
|
7
|
+
scope: Readonly<ExecutionScope>;
|
|
8
|
+
runtimeSessionId: string;
|
|
9
|
+
invocationId: string;
|
|
10
|
+
workflow: HostedExecutionWorkflow;
|
|
11
|
+
issuedAt: string;
|
|
12
|
+
expiresAt: string;
|
|
13
|
+
}>;
|
|
14
|
+
export type ExecutionIdentityVerificationRequest = Readonly<{
|
|
15
|
+
assertion: string;
|
|
16
|
+
runtimeSessionId: string;
|
|
17
|
+
invocationId: string;
|
|
18
|
+
workflow: HostedExecutionWorkflow;
|
|
19
|
+
}>;
|
|
20
|
+
export interface ExecutionIdentityVerifier {
|
|
21
|
+
verify(request: ExecutionIdentityVerificationRequest): Promise<VerifiedExecutionIdentity>;
|
|
22
|
+
}
|
|
23
|
+
export type JwtExecutionIdentityVerifierConfig = Readonly<{
|
|
24
|
+
executionIssuer: string;
|
|
25
|
+
executionAudience: string;
|
|
26
|
+
executionJwksUrl: URL;
|
|
27
|
+
executionJwtAlgorithms: readonly ExecutionHostJwtAlgorithm[];
|
|
28
|
+
trustedAdopterId: string;
|
|
29
|
+
maxAssertionAgeSeconds: number;
|
|
30
|
+
assertionClockToleranceSeconds: number;
|
|
31
|
+
}>;
|
|
32
|
+
/** Opaque MCP bearer whose claims match the independently verified execution. */
|
|
33
|
+
export type VerifiedExecutionHostMcpCapability = Readonly<{
|
|
34
|
+
assertion: string;
|
|
35
|
+
capabilityId: string;
|
|
36
|
+
serverId: string;
|
|
37
|
+
allowedTools: readonly string[];
|
|
38
|
+
issuedAt: string;
|
|
39
|
+
expiresAt: string;
|
|
40
|
+
}>;
|
|
41
|
+
export type ExecutionHostMcpCapabilityVerificationRequest = Readonly<{
|
|
42
|
+
assertion: string;
|
|
43
|
+
identity: VerifiedExecutionIdentity;
|
|
44
|
+
}>;
|
|
45
|
+
export interface ExecutionHostMcpCapabilityVerifier {
|
|
46
|
+
verify(request: ExecutionHostMcpCapabilityVerificationRequest): Promise<VerifiedExecutionHostMcpCapability>;
|
|
47
|
+
}
|
|
48
|
+
export type JwtExecutionHostMcpCapabilityVerifierConfig = Readonly<{
|
|
49
|
+
issuer: string;
|
|
50
|
+
audience: string;
|
|
51
|
+
jwksUrl: URL;
|
|
52
|
+
jwtAlgorithms: readonly ExecutionHostJwtAlgorithm[];
|
|
53
|
+
trustedAdopterId: string;
|
|
54
|
+
serverId: string;
|
|
55
|
+
maxCapabilityAgeSeconds: number;
|
|
56
|
+
clockToleranceSeconds: number;
|
|
57
|
+
}>;
|
|
58
|
+
export type ExecutionHostJwtVerifierOptions = Readonly<{
|
|
59
|
+
keyResolver?: JWTVerifyGetKey;
|
|
60
|
+
now?: () => Date;
|
|
61
|
+
}>;
|
|
62
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/host/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAC5C,OAAO,KAAK,EACV,cAAc,EACd,uBAAuB,EACxB,MAAM,uBAAuB,CAAC;AAE/B,eAAO,MAAM,uCAAuC,+CAK1C,CAAC;AAEX,MAAM,MAAM,yBAAyB,GACnC,OAAO,uCAAuC,CAAC,MAAM,CAAC,CAAC;AAEzD,+EAA+E;AAC/E,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAC/C,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,uBAAuB,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,CAAC;AAEH,MAAM,MAAM,oCAAoC,GAAG,QAAQ,CAAC;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,uBAAuB,CAAC;CACnC,CAAC,CAAC;AAEH,MAAM,WAAW,yBAAyB;IACxC,MAAM,CACJ,OAAO,EAAE,oCAAoC,GAC5C,OAAO,CAAC,yBAAyB,CAAC,CAAC;CACvC;AAED,MAAM,MAAM,kCAAkC,GAAG,QAAQ,CAAC;IACxD,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,GAAG,CAAC;IACtB,sBAAsB,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAC7D,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,8BAA8B,EAAE,MAAM,CAAC;CACxC,CAAC,CAAC;AAEH,iFAAiF;AACjF,MAAM,MAAM,kCAAkC,GAAG,QAAQ,CAAC;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,CAAC;AAEH,MAAM,MAAM,6CAA6C,GAAG,QAAQ,CAAC;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,yBAAyB,CAAC;CACrC,CAAC,CAAC;AAEH,MAAM,WAAW,kCAAkC;IACjD,MAAM,CACJ,OAAO,EAAE,6CAA6C,GACrD,OAAO,CAAC,kCAAkC,CAAC,CAAC;CAChD;AAED,MAAM,MAAM,2CAA2C,GAAG,QAAQ,CAAC;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,GAAG,CAAC;IACb,aAAa,EAAE,SAAS,yBAAyB,EAAE,CAAC;IACpD,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,uBAAuB,EAAE,MAAM,CAAC;IAChC,qBAAqB,EAAE,MAAM,CAAC;CAC/B,CAAC,CAAC;AAEH,MAAM,MAAM,+BAA+B,GAAG,QAAQ,CAAC;IACrD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/host/types.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,uCAAuC,GAAG;IACrD,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;CACC,CAAC","sourcesContent":["import type { JWTVerifyGetKey } from 'jose';\nimport type {\n ExecutionScope,\n HostedExecutionWorkflow,\n} from '../contracts/index.js';\n\nexport const EXECUTION_HOST_SUPPORTED_JWT_ALGORITHMS = [\n 'RS256',\n 'PS256',\n 'ES256',\n 'EdDSA',\n] as const;\n\nexport type ExecutionHostJwtAlgorithm =\n typeof EXECUTION_HOST_SUPPORTED_JWT_ALGORITHMS[number];\n\n/** Authority established from one signed assertion and its request binding. */\nexport type VerifiedExecutionIdentity = Readonly<{\n scope: Readonly<ExecutionScope>;\n runtimeSessionId: string;\n invocationId: string;\n workflow: HostedExecutionWorkflow;\n issuedAt: string;\n expiresAt: string;\n}>;\n\nexport type ExecutionIdentityVerificationRequest = Readonly<{\n assertion: string;\n runtimeSessionId: string;\n invocationId: string;\n workflow: HostedExecutionWorkflow;\n}>;\n\nexport interface ExecutionIdentityVerifier {\n verify(\n request: ExecutionIdentityVerificationRequest,\n ): Promise<VerifiedExecutionIdentity>;\n}\n\nexport type JwtExecutionIdentityVerifierConfig = Readonly<{\n executionIssuer: string;\n executionAudience: string;\n executionJwksUrl: URL;\n executionJwtAlgorithms: readonly ExecutionHostJwtAlgorithm[];\n trustedAdopterId: string;\n maxAssertionAgeSeconds: number;\n assertionClockToleranceSeconds: number;\n}>;\n\n/** Opaque MCP bearer whose claims match the independently verified execution. */\nexport type VerifiedExecutionHostMcpCapability = Readonly<{\n assertion: string;\n capabilityId: string;\n serverId: string;\n allowedTools: readonly string[];\n issuedAt: string;\n expiresAt: string;\n}>;\n\nexport type ExecutionHostMcpCapabilityVerificationRequest = Readonly<{\n assertion: string;\n identity: VerifiedExecutionIdentity;\n}>;\n\nexport interface ExecutionHostMcpCapabilityVerifier {\n verify(\n request: ExecutionHostMcpCapabilityVerificationRequest,\n ): Promise<VerifiedExecutionHostMcpCapability>;\n}\n\nexport type JwtExecutionHostMcpCapabilityVerifierConfig = Readonly<{\n issuer: string;\n audience: string;\n jwksUrl: URL;\n jwtAlgorithms: readonly ExecutionHostJwtAlgorithm[];\n trustedAdopterId: string;\n serverId: string;\n maxCapabilityAgeSeconds: number;\n clockToleranceSeconds: number;\n}>;\n\nexport type ExecutionHostJwtVerifierOptions = Readonly<{\n keyResolver?: JWTVerifyGetKey;\n now?: () => Date;\n}>;\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type JWTVerifyGetKey } from 'jose';
|
|
2
|
+
export type JwtVerificationUnavailableCategory = 'network' | 'jwks_response' | 'jwks_timeout';
|
|
3
|
+
export declare function createJwtKeyResolver(jwksUrl: URL): JWTVerifyGetKey;
|
|
4
|
+
export declare function resolveJwtVerificationUnavailableCategory(error: unknown): JwtVerificationUnavailableCategory | undefined;
|
|
5
|
+
//# sourceMappingURL=jwt-verification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-verification.d.ts","sourceRoot":"","sources":["../../src/internal/jwt-verification.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,MAAM,CAAC;AAgBd,MAAM,MAAM,kCAAkC,GAC1C,SAAS,GACT,eAAe,GACf,cAAc,CAAC;AAEnB,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,GAAG,GAAG,eAAe,CAElE;AAED,wBAAgB,yCAAyC,CACvD,KAAK,EAAE,OAAO,GACb,kCAAkC,GAAG,SAAS,CAOhD"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createRemoteJWKSet, errors, } from 'jose';
|
|
2
|
+
const REMOTE_JWKS_OPTIONS = Object.freeze({
|
|
3
|
+
cacheMaxAge: 10 * 60_000,
|
|
4
|
+
cooldownDuration: 30_000,
|
|
5
|
+
timeoutDuration: 3_000,
|
|
6
|
+
});
|
|
7
|
+
const UNAVAILABLE_JOSE_CATEGORIES = new Map([
|
|
8
|
+
['ERR_JOSE_GENERIC', 'jwks_response'],
|
|
9
|
+
['ERR_JWK_INVALID', 'jwks_response'],
|
|
10
|
+
['ERR_JWKS_INVALID', 'jwks_response'],
|
|
11
|
+
['ERR_JWKS_MULTIPLE_MATCHING_KEYS', 'jwks_response'],
|
|
12
|
+
['ERR_JWKS_TIMEOUT', 'jwks_timeout'],
|
|
13
|
+
]);
|
|
14
|
+
export function createJwtKeyResolver(jwksUrl) {
|
|
15
|
+
return createRemoteJWKSet(jwksUrl, REMOTE_JWKS_OPTIONS);
|
|
16
|
+
}
|
|
17
|
+
export function resolveJwtVerificationUnavailableCategory(error) {
|
|
18
|
+
if (error instanceof TypeError) {
|
|
19
|
+
return 'network';
|
|
20
|
+
}
|
|
21
|
+
return error instanceof errors.JOSEError
|
|
22
|
+
? UNAVAILABLE_JOSE_CATEGORIES.get(error.code)
|
|
23
|
+
: undefined;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=jwt-verification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-verification.js","sourceRoot":"","sources":["../../src/internal/jwt-verification.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,MAAM,GAEP,MAAM,MAAM,CAAC;AAEd,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,CAAC;IACxC,WAAW,EAAE,EAAE,GAAG,MAAM;IACxB,gBAAgB,EAAE,MAAM;IACxB,eAAe,EAAE,KAAK;CACvB,CAAC,CAAC;AAEH,MAAM,2BAA2B,GAAG,IAAI,GAAG,CAA6C;IACtF,CAAC,kBAAkB,EAAE,eAAe,CAAC;IACrC,CAAC,iBAAiB,EAAE,eAAe,CAAC;IACpC,CAAC,kBAAkB,EAAE,eAAe,CAAC;IACrC,CAAC,iCAAiC,EAAE,eAAe,CAAC;IACpD,CAAC,kBAAkB,EAAE,cAAc,CAAC;CACrC,CAAC,CAAC;AAOH,MAAM,UAAU,oBAAoB,CAAC,OAAY;IAC/C,OAAO,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,yCAAyC,CACvD,KAAc;IAEd,IAAI,KAAK,YAAY,SAAS,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,YAAY,MAAM,CAAC,SAAS;QACtC,CAAC,CAAC,2BAA2B,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;QAC7C,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC","sourcesContent":["import {\n createRemoteJWKSet,\n errors,\n type JWTVerifyGetKey,\n} from 'jose';\n\nconst REMOTE_JWKS_OPTIONS = Object.freeze({\n cacheMaxAge: 10 * 60_000,\n cooldownDuration: 30_000,\n timeoutDuration: 3_000,\n});\n\nconst UNAVAILABLE_JOSE_CATEGORIES = new Map<string, JwtVerificationUnavailableCategory>([\n ['ERR_JOSE_GENERIC', 'jwks_response'],\n ['ERR_JWK_INVALID', 'jwks_response'],\n ['ERR_JWKS_INVALID', 'jwks_response'],\n ['ERR_JWKS_MULTIPLE_MATCHING_KEYS', 'jwks_response'],\n ['ERR_JWKS_TIMEOUT', 'jwks_timeout'],\n]);\n\nexport type JwtVerificationUnavailableCategory =\n | 'network'\n | 'jwks_response'\n | 'jwks_timeout';\n\nexport function createJwtKeyResolver(jwksUrl: URL): JWTVerifyGetKey {\n return createRemoteJWKSet(jwksUrl, REMOTE_JWKS_OPTIONS);\n}\n\nexport function resolveJwtVerificationUnavailableCategory(\n error: unknown,\n): JwtVerificationUnavailableCategory | undefined {\n if (error instanceof TypeError) {\n return 'network';\n }\n return error instanceof errors.JOSEError\n ? UNAVAILABLE_JOSE_CATEGORIES.get(error.code)\n : undefined;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt-capability-verifier.d.ts","sourceRoot":"","sources":["../../src/mcp/jwt-capability-verifier.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,8BAA8B,EAC9B,+BAA+B,EAC/B,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAqBpB;;;GAGG;AACH,qBAAa,wBAAwB,CAAC,SAAS,SAAS,MAAM,CAC9D,YAAW,qBAAqB,CAAC,SAAS,CAAC;;gBAOvC,MAAM,EAAE,8BAA8B,CAAC,SAAS,CAAC,EACjD,OAAO,GAAE,+BAAoC;
|
|
1
|
+
{"version":3,"file":"jwt-capability-verifier.d.ts","sourceRoot":"","sources":["../../src/mcp/jwt-capability-verifier.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,8BAA8B,EAC9B,+BAA+B,EAC/B,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAqBpB;;;GAGG;AACH,qBAAa,wBAAwB,CAAC,SAAS,SAAS,MAAM,CAC9D,YAAW,qBAAqB,CAAC,SAAS,CAAC;;gBAOvC,MAAM,EAAE,8BAA8B,CAAC,SAAS,CAAC,EACjD,OAAO,GAAE,+BAAoC;IAczC,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC;CAkF3E;AAED,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,qBAAqB,EACjC,GAAG,OAAa,GACf,IAAI,CAKN"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jwtVerify } from 'jose';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { JwtAudienceSchema, JwtIssuerSchema, MCP_CAPABILITY_TYPE, McpAllowedToolsSchema, McpCapabilityClaimsSchema, McpServerIdSchema, OpaqueIdSchema, isSafeWebUrl, } from '../contracts/index.js';
|
|
4
4
|
import { McpCapabilityUnavailableError, McpCapabilityVerificationError, } from './errors.js';
|
|
5
|
+
import { createJwtKeyResolver, resolveJwtVerificationUnavailableCategory, } from '../internal/jwt-verification.js';
|
|
5
6
|
const CapabilityAssertionSchema = z.string().min(32).max(4_096);
|
|
6
7
|
const ConfigSchema = z.object({
|
|
7
8
|
issuer: JwtIssuerSchema,
|
|
@@ -30,11 +31,8 @@ export class JwtMcpCapabilityVerifier {
|
|
|
30
31
|
supportedTools: Object.freeze([...parsed.supportedTools]),
|
|
31
32
|
});
|
|
32
33
|
this.#supportedTools = new Set(this.#config.supportedTools);
|
|
33
|
-
this.#keyResolver = options.keyResolver
|
|
34
|
-
|
|
35
|
-
cooldownDuration: 30_000,
|
|
36
|
-
timeoutDuration: 3_000,
|
|
37
|
-
});
|
|
34
|
+
this.#keyResolver = options.keyResolver
|
|
35
|
+
?? createJwtKeyResolver(this.#config.jwksUrl);
|
|
38
36
|
this.#now = options.now ?? (() => new Date());
|
|
39
37
|
}
|
|
40
38
|
async verify(assertion) {
|
|
@@ -76,9 +74,9 @@ export class JwtMcpCapabilityVerifier {
|
|
|
76
74
|
|| error instanceof McpCapabilityUnavailableError) {
|
|
77
75
|
throw error;
|
|
78
76
|
}
|
|
79
|
-
const unavailableCategory =
|
|
77
|
+
const unavailableCategory = resolveJwtVerificationUnavailableCategory(error);
|
|
80
78
|
if (unavailableCategory) {
|
|
81
|
-
throw new McpCapabilityUnavailableError(unavailableCategory);
|
|
79
|
+
throw new McpCapabilityUnavailableError(unavailableCategory === 'network' ? 'network' : 'jwks');
|
|
82
80
|
}
|
|
83
81
|
throw new McpCapabilityVerificationError();
|
|
84
82
|
}
|
|
@@ -116,21 +114,4 @@ function freezeVerifiedCapability(capability) {
|
|
|
116
114
|
scope: Object.freeze({ ...capability.scope }),
|
|
117
115
|
});
|
|
118
116
|
}
|
|
119
|
-
const UNAVAILABLE_JOSE_CODES = new Set([
|
|
120
|
-
'ERR_JOSE_GENERIC',
|
|
121
|
-
'ERR_JWK_INVALID',
|
|
122
|
-
'ERR_JWKS_INVALID',
|
|
123
|
-
'ERR_JWKS_MULTIPLE_MATCHING_KEYS',
|
|
124
|
-
'ERR_JWKS_TIMEOUT',
|
|
125
|
-
]);
|
|
126
|
-
function resolveUnavailableCategory(error) {
|
|
127
|
-
if (error instanceof TypeError) {
|
|
128
|
-
return 'network';
|
|
129
|
-
}
|
|
130
|
-
if (error instanceof errors.JOSEError
|
|
131
|
-
&& UNAVAILABLE_JOSE_CODES.has(error.code)) {
|
|
132
|
-
return 'jwks';
|
|
133
|
-
}
|
|
134
|
-
return undefined;
|
|
135
|
-
}
|
|
136
117
|
//# sourceMappingURL=jwt-capability-verifier.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt-capability-verifier.js","sourceRoot":"","sources":["../../src/mcp/jwt-capability-verifier.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createRemoteJWKSet,\n errors,\n jwtVerify,\n} from 'jose';\nimport { z } from 'zod';\nimport {\n JwtAudienceSchema,\n JwtIssuerSchema,\n MCP_CAPABILITY_TYPE,\n McpAllowedToolsSchema,\n McpCapabilityClaimsSchema,\n McpServerIdSchema,\n OpaqueIdSchema,\n isSafeWebUrl,\n} from '../contracts/index.js';\nimport {\n McpCapabilityUnavailableError,\n McpCapabilityVerificationError,\n} from './errors.js';\nimport type {\n JwtMcpCapabilityVerifierConfig,\n JwtMcpCapabilityVerifierOptions,\n McpCapabilityVerifier,\n VerifiedMcpCapability,\n} from './types.js';\n\nconst CapabilityAssertionSchema = z.string().min(32).max(4_096);\nconst ConfigSchema = z.object({\n issuer: JwtIssuerSchema,\n audience: JwtAudienceSchema,\n jwksUrl: z.instanceof(URL).refine(\n isSafeWebUrl,\n 'must use HTTPS or loopback HTTP and contain no credentials, query, or fragment',\n ),\n trustedAdopterId: OpaqueIdSchema,\n serverId: McpServerIdSchema,\n supportedTools: McpAllowedToolsSchema,\n maxCapabilityAgeSeconds: z.number().int().min(1).max(15 * 60),\n clockToleranceSeconds: z.number().int().min(0).max(60).default(5),\n}).strict();\n\ntype ParsedConfig = Omit<z.infer<typeof ConfigSchema>, 'supportedTools'> & {\n supportedTools: readonly string[];\n};\n\n/**\n * Independently verifies an adopter-signed capability at the adopter's MCP\n * edge. Execution Host verification is defense in depth, not forwarded trust.\n */\nexport class JwtMcpCapabilityVerifier<TToolName extends string>\nimplements McpCapabilityVerifier<TToolName> {\n readonly #config: Readonly<ParsedConfig>;\n readonly #supportedTools: ReadonlySet<string>;\n readonly #keyResolver: NonNullable<JwtMcpCapabilityVerifierOptions['keyResolver']>;\n readonly #now: () => Date;\n\n constructor(\n config: JwtMcpCapabilityVerifierConfig<TToolName>,\n options: JwtMcpCapabilityVerifierOptions = {},\n ) {\n const parsed = ConfigSchema.parse(config);\n this.#config = Object.freeze({\n ...parsed,\n jwksUrl: new URL(parsed.jwksUrl),\n supportedTools: Object.freeze([...parsed.supportedTools]),\n });\n this.#supportedTools = new Set(this.#config.supportedTools);\n this.#keyResolver = options.keyResolver ?? createRemoteJWKSet(\n this.#config.jwksUrl,\n {\n cacheMaxAge: 10 * 60_000,\n cooldownDuration: 30_000,\n timeoutDuration: 3_000,\n },\n );\n this.#now = options.now ?? (() => new Date());\n }\n\n async verify(assertion: string): Promise<VerifiedMcpCapability<TToolName>> {\n try {\n const compactJwt = CapabilityAssertionSchema.parse(assertion);\n const { payload } = await jwtVerify(compactJwt, this.#keyResolver, {\n algorithms: ['ES256'],\n audience: this.#config.audience,\n clockTolerance: this.#config.clockToleranceSeconds,\n currentDate: this.#now(),\n issuer: this.#config.issuer,\n maxTokenAge: this.#config.maxCapabilityAgeSeconds,\n requiredClaims: ['exp', 'iat', 'jti', 'sub'],\n typ: MCP_CAPABILITY_TYPE,\n });\n const claims = McpCapabilityClaimsSchema.parse(payload);\n this.#assertDeploymentBinding(claims);\n this.#assertBoundedLifetime(claims);\n this.#assertSupportedTools(claims.allowedTools);\n\n return freezeVerifiedCapability({\n capabilityId: claims.jti,\n serverId: claims.serverId,\n allowedTools: claims.allowedTools as TToolName[],\n scope: {\n adopterId: claims.adopterId,\n tenantId: claims.tenantId,\n subjectId: claims.sub,\n productSessionId: claims.productSessionId,\n runtimeSessionId: claims.runtimeSessionId,\n invocationId: claims.invocationId,\n workflow: claims.workflow,\n },\n issuedAt: new Date(claims.iat * 1_000).toISOString(),\n expiresAt: new Date(claims.exp * 1_000).toISOString(),\n });\n } catch (error) {\n if (\n error instanceof McpCapabilityVerificationError\n || error instanceof McpCapabilityUnavailableError\n ) {\n throw error;\n }\n const unavailableCategory = resolveUnavailableCategory(error);\n if (unavailableCategory) {\n throw new McpCapabilityUnavailableError(unavailableCategory);\n }\n throw new McpCapabilityVerificationError();\n }\n }\n\n #assertDeploymentBinding(\n claims: z.infer<typeof McpCapabilityClaimsSchema>,\n ): void {\n if (\n claims.adopterId !== this.#config.trustedAdopterId\n || claims.serverId !== this.#config.serverId\n || claims.jti === claims.invocationId\n ) {\n throw new McpCapabilityVerificationError();\n }\n }\n\n #assertBoundedLifetime(\n claims: z.infer<typeof McpCapabilityClaimsSchema>,\n ): void {\n const lifetimeSeconds = claims.exp - claims.iat;\n if (\n lifetimeSeconds <= 0\n || lifetimeSeconds > this.#config.maxCapabilityAgeSeconds\n ) {\n throw new McpCapabilityVerificationError();\n }\n }\n\n #assertSupportedTools(tools: readonly string[]): void {\n if (!tools.every((tool) => this.#supportedTools.has(tool))) {\n throw new McpCapabilityVerificationError();\n }\n }\n}\n\nexport function assertMcpCapabilityActive(\n capability: VerifiedMcpCapability,\n now = new Date(),\n): void {\n const expiresAt = Date.parse(capability.expiresAt);\n if (!Number.isFinite(expiresAt) || now.getTime() >= expiresAt) {\n throw new McpCapabilityVerificationError();\n }\n}\n\nfunction freezeVerifiedCapability<TToolName extends string>(\n capability: VerifiedMcpCapability<TToolName>,\n): VerifiedMcpCapability<TToolName> {\n return Object.freeze({\n ...capability,\n allowedTools: Object.freeze([...capability.allowedTools]),\n scope: Object.freeze({ ...capability.scope }),\n });\n}\n\nconst UNAVAILABLE_JOSE_CODES = new Set([\n 'ERR_JOSE_GENERIC',\n 'ERR_JWK_INVALID',\n 'ERR_JWKS_INVALID',\n 'ERR_JWKS_MULTIPLE_MATCHING_KEYS',\n 'ERR_JWKS_TIMEOUT',\n]);\n\nfunction resolveUnavailableCategory(\n error: unknown,\n): 'network' | 'jwks' | undefined {\n if (error instanceof TypeError) {\n return 'network';\n }\n if (\n error instanceof errors.JOSEError\n && UNAVAILABLE_JOSE_CODES.has(error.code)\n ) {\n return 'jwks';\n }\n return undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"jwt-capability-verifier.js","sourceRoot":"","sources":["../../src/mcp/jwt-capability-verifier.ts"],"names":[],"mappings":"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","sourcesContent":["import { jwtVerify } from 'jose';\nimport { z } from 'zod';\nimport {\n JwtAudienceSchema,\n JwtIssuerSchema,\n MCP_CAPABILITY_TYPE,\n McpAllowedToolsSchema,\n McpCapabilityClaimsSchema,\n McpServerIdSchema,\n OpaqueIdSchema,\n isSafeWebUrl,\n} from '../contracts/index.js';\nimport {\n McpCapabilityUnavailableError,\n McpCapabilityVerificationError,\n} from './errors.js';\nimport {\n createJwtKeyResolver,\n resolveJwtVerificationUnavailableCategory,\n} from '../internal/jwt-verification.js';\nimport type {\n JwtMcpCapabilityVerifierConfig,\n JwtMcpCapabilityVerifierOptions,\n McpCapabilityVerifier,\n VerifiedMcpCapability,\n} from './types.js';\n\nconst CapabilityAssertionSchema = z.string().min(32).max(4_096);\nconst ConfigSchema = z.object({\n issuer: JwtIssuerSchema,\n audience: JwtAudienceSchema,\n jwksUrl: z.instanceof(URL).refine(\n isSafeWebUrl,\n 'must use HTTPS or loopback HTTP and contain no credentials, query, or fragment',\n ),\n trustedAdopterId: OpaqueIdSchema,\n serverId: McpServerIdSchema,\n supportedTools: McpAllowedToolsSchema,\n maxCapabilityAgeSeconds: z.number().int().min(1).max(15 * 60),\n clockToleranceSeconds: z.number().int().min(0).max(60).default(5),\n}).strict();\n\ntype ParsedConfig = Omit<z.infer<typeof ConfigSchema>, 'supportedTools'> & {\n supportedTools: readonly string[];\n};\n\n/**\n * Independently verifies an adopter-signed capability at the adopter's MCP\n * edge. Execution Host verification is defense in depth, not forwarded trust.\n */\nexport class JwtMcpCapabilityVerifier<TToolName extends string>\nimplements McpCapabilityVerifier<TToolName> {\n readonly #config: Readonly<ParsedConfig>;\n readonly #supportedTools: ReadonlySet<string>;\n readonly #keyResolver: NonNullable<JwtMcpCapabilityVerifierOptions['keyResolver']>;\n readonly #now: () => Date;\n\n constructor(\n config: JwtMcpCapabilityVerifierConfig<TToolName>,\n options: JwtMcpCapabilityVerifierOptions = {},\n ) {\n const parsed = ConfigSchema.parse(config);\n this.#config = Object.freeze({\n ...parsed,\n jwksUrl: new URL(parsed.jwksUrl),\n supportedTools: Object.freeze([...parsed.supportedTools]),\n });\n this.#supportedTools = new Set(this.#config.supportedTools);\n this.#keyResolver = options.keyResolver\n ?? createJwtKeyResolver(this.#config.jwksUrl);\n this.#now = options.now ?? (() => new Date());\n }\n\n async verify(assertion: string): Promise<VerifiedMcpCapability<TToolName>> {\n try {\n const compactJwt = CapabilityAssertionSchema.parse(assertion);\n const { payload } = await jwtVerify(compactJwt, this.#keyResolver, {\n algorithms: ['ES256'],\n audience: this.#config.audience,\n clockTolerance: this.#config.clockToleranceSeconds,\n currentDate: this.#now(),\n issuer: this.#config.issuer,\n maxTokenAge: this.#config.maxCapabilityAgeSeconds,\n requiredClaims: ['exp', 'iat', 'jti', 'sub'],\n typ: MCP_CAPABILITY_TYPE,\n });\n const claims = McpCapabilityClaimsSchema.parse(payload);\n this.#assertDeploymentBinding(claims);\n this.#assertBoundedLifetime(claims);\n this.#assertSupportedTools(claims.allowedTools);\n\n return freezeVerifiedCapability({\n capabilityId: claims.jti,\n serverId: claims.serverId,\n allowedTools: claims.allowedTools as TToolName[],\n scope: {\n adopterId: claims.adopterId,\n tenantId: claims.tenantId,\n subjectId: claims.sub,\n productSessionId: claims.productSessionId,\n runtimeSessionId: claims.runtimeSessionId,\n invocationId: claims.invocationId,\n workflow: claims.workflow,\n },\n issuedAt: new Date(claims.iat * 1_000).toISOString(),\n expiresAt: new Date(claims.exp * 1_000).toISOString(),\n });\n } catch (error) {\n if (\n error instanceof McpCapabilityVerificationError\n || error instanceof McpCapabilityUnavailableError\n ) {\n throw error;\n }\n const unavailableCategory = resolveJwtVerificationUnavailableCategory(\n error,\n );\n if (unavailableCategory) {\n throw new McpCapabilityUnavailableError(\n unavailableCategory === 'network' ? 'network' : 'jwks',\n );\n }\n throw new McpCapabilityVerificationError();\n }\n }\n\n #assertDeploymentBinding(\n claims: z.infer<typeof McpCapabilityClaimsSchema>,\n ): void {\n if (\n claims.adopterId !== this.#config.trustedAdopterId\n || claims.serverId !== this.#config.serverId\n || claims.jti === claims.invocationId\n ) {\n throw new McpCapabilityVerificationError();\n }\n }\n\n #assertBoundedLifetime(\n claims: z.infer<typeof McpCapabilityClaimsSchema>,\n ): void {\n const lifetimeSeconds = claims.exp - claims.iat;\n if (\n lifetimeSeconds <= 0\n || lifetimeSeconds > this.#config.maxCapabilityAgeSeconds\n ) {\n throw new McpCapabilityVerificationError();\n }\n }\n\n #assertSupportedTools(tools: readonly string[]): void {\n if (!tools.every((tool) => this.#supportedTools.has(tool))) {\n throw new McpCapabilityVerificationError();\n }\n }\n}\n\nexport function assertMcpCapabilityActive(\n capability: VerifiedMcpCapability,\n now = new Date(),\n): void {\n const expiresAt = Date.parse(capability.expiresAt);\n if (!Number.isFinite(expiresAt) || now.getTime() >= expiresAt) {\n throw new McpCapabilityVerificationError();\n }\n}\n\nfunction freezeVerifiedCapability<TToolName extends string>(\n capability: VerifiedMcpCapability<TToolName>,\n): VerifiedMcpCapability<TToolName> {\n return Object.freeze({\n ...capability,\n allowedTools: Object.freeze([...capability.allowedTools]),\n scope: Object.freeze({ ...capability.scope }),\n });\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heddleagent/execution-host-client",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.5.0",
|
|
4
4
|
"description": "Backend contracts, execution authority, lifecycle services, and clients for compatible Heddle Execution Hosts",
|
|
5
5
|
"author": "Jay / Fienna Liang <roackb2@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -70,6 +70,10 @@
|
|
|
70
70
|
"types": "./dist/agentcore/index.d.ts",
|
|
71
71
|
"import": "./dist/agentcore/index.js"
|
|
72
72
|
},
|
|
73
|
+
"./host": {
|
|
74
|
+
"types": "./dist/host/index.d.ts",
|
|
75
|
+
"import": "./dist/host/index.js"
|
|
76
|
+
},
|
|
73
77
|
"./testing": {
|
|
74
78
|
"types": "./dist/testing/index.d.ts",
|
|
75
79
|
"import": "./dist/testing/index.js"
|