mcpose 2.1.1 → 3.1.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 +461 -62
- package/dist/backendClient.d.ts +1 -1
- package/dist/backendClient.d.ts.map +1 -1
- package/dist/backendClient.js +6 -1
- package/dist/backendClient.js.map +1 -1
- package/dist/core.d.ts +309 -35
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +1023 -186
- package/dist/core.js.map +1 -1
- package/dist/delegation.d.ts +89 -0
- package/dist/delegation.d.ts.map +1 -0
- package/dist/delegation.js +175 -0
- package/dist/delegation.js.map +1 -0
- package/dist/eventStore.d.ts +12 -0
- package/dist/eventStore.d.ts.map +1 -1
- package/dist/eventStore.js +43 -5
- package/dist/eventStore.js.map +1 -1
- package/dist/hiddenTools.d.ts +42 -0
- package/dist/hiddenTools.d.ts.map +1 -0
- package/dist/hiddenTools.js +53 -0
- package/dist/hiddenTools.js.map +1 -0
- package/dist/index.d.ts +15 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/mesh.d.ts +82 -0
- package/dist/mesh.d.ts.map +1 -0
- package/dist/mesh.js +141 -0
- package/dist/mesh.js.map +1 -0
- package/dist/middleware.d.ts +13 -0
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +27 -3
- package/dist/middleware.js.map +1 -1
- package/dist/proxyContext.d.ts +65 -5
- package/dist/proxyContext.d.ts.map +1 -1
- package/dist/proxyContext.js +39 -4
- package/dist/proxyContext.js.map +1 -1
- package/dist/rejection.d.ts +7 -1
- package/dist/rejection.d.ts.map +1 -1
- package/dist/rejection.js +24 -1
- package/dist/rejection.js.map +1 -1
- package/dist/sanitizeCatalog.d.ts +37 -0
- package/dist/sanitizeCatalog.d.ts.map +1 -0
- package/dist/sanitizeCatalog.js +92 -0
- package/dist/sanitizeCatalog.js.map +1 -0
- package/dist/sessionRegistry.d.ts +79 -0
- package/dist/sessionRegistry.d.ts.map +1 -0
- package/dist/sessionRegistry.js +83 -0
- package/dist/sessionRegistry.js.map +1 -0
- package/dist/telemetry.d.ts +29 -1
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/testing.d.ts +18 -2
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +30 -8
- package/dist/testing.js.map +1 -1
- package/dist/version.d.ts +7 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +7 -0
- package/dist/version.js.map +1 -0
- package/package.json +22 -8
- package/src/__tests__/_helpers.ts +64 -0
- package/src/__tests__/backendClient.test.ts +198 -0
- package/src/__tests__/core.delegation.test.ts +964 -0
- package/src/__tests__/core.edge.test.ts +314 -0
- package/src/__tests__/core.hiddenTools.predicate.test.ts +236 -0
- package/src/__tests__/core.localTools.test.ts +314 -0
- package/src/__tests__/core.mesh.test.ts +1182 -0
- package/src/__tests__/core.stripRequestMeta.test.ts +226 -0
- package/src/__tests__/core.stripResultMeta.test.ts +217 -0
- package/src/__tests__/core.test.ts +1105 -0
- package/src/__tests__/core.toolCallTelemetry.test.ts +157 -0
- package/src/__tests__/eventStore.test.ts +141 -0
- package/src/__tests__/hasToolContent.test.ts +63 -0
- package/src/__tests__/mapToolResult.test.ts +101 -0
- package/src/__tests__/middleware.edge.test.ts +126 -0
- package/src/__tests__/middleware.test.ts +233 -0
- package/src/__tests__/proxyContext.test.ts +140 -0
- package/src/__tests__/sanitizeCatalog.test.ts +380 -0
- package/src/__tests__/startHttpProxy.lifecycle.test.ts +675 -0
- package/src/__tests__/startHttpProxy.registry.test.ts +409 -0
- package/src/__tests__/startHttpProxy.replay.test.ts +116 -0
- package/src/__tests__/startHttpProxy.security.test.ts +288 -0
- package/src/__tests__/startHttpProxy.test.ts +1065 -0
- package/src/__tests__/testing.test.ts +250 -0
- package/src/__tests__/version.test.ts +12 -0
- package/src/backendClient.ts +83 -0
- package/src/core.ts +2129 -0
- package/src/delegation.ts +224 -0
- package/src/eventStore.ts +106 -0
- package/src/hiddenTools.ts +84 -0
- package/src/identity.ts +15 -0
- package/src/index.ts +63 -0
- package/src/mesh.ts +204 -0
- package/src/middleware.ts +101 -0
- package/src/proxyContext.ts +108 -0
- package/src/rejection.ts +62 -0
- package/src/sanitizeCatalog.ts +126 -0
- package/src/sessionRegistry.ts +142 -0
- package/src/telemetry.ts +46 -0
- package/src/testing.ts +174 -0
- package/src/version.ts +6 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
export type { Middleware } from './middleware.js';
|
|
2
|
-
export { compose } from './middleware.js';
|
|
2
|
+
export { compose, markPassThroughObserver } from './middleware.js';
|
|
3
3
|
export type { BackendConfig, BackendClient } from './backendClient.js';
|
|
4
4
|
export { createBackendClient } from './backendClient.js';
|
|
5
5
|
export type { Identity } from './identity.js';
|
|
6
|
-
export type { ProxyContext } from './proxyContext.js';
|
|
7
|
-
export { createProxyContext } from './proxyContext.js';
|
|
8
|
-
export type { TelemetryEvent } from './telemetry.js';
|
|
6
|
+
export type { ProxyContext, ProxyIdentity, PolicyDecision, } from './proxyContext.js';
|
|
7
|
+
export { createProxyContext, outboundDelegationChain } from './proxyContext.js';
|
|
8
|
+
export type { TelemetryEvent, ToolCallTelemetryEvent, BackendDegradedTelemetryEvent, } from './telemetry.js';
|
|
9
|
+
export type { Backends } from './mesh.js';
|
|
9
10
|
export type { PersistentEventStore } from './eventStore.js';
|
|
10
11
|
export { createInMemoryEventStore } from './eventStore.js';
|
|
12
|
+
export type { SessionRecord, SessionRegistry } from './sessionRegistry.js';
|
|
13
|
+
export type { DelegationWireEntry, DelegationWirePayload, } from './delegation.js';
|
|
14
|
+
export { DELEGATION_META_KEY, serializeDelegationChain } from './delegation.js';
|
|
11
15
|
export type { RejectionReason } from './rejection.js';
|
|
12
|
-
export
|
|
13
|
-
export {
|
|
16
|
+
export { rejectionMcpError } from './rejection.js';
|
|
17
|
+
export type { HiddenToolPredicate, DispatcherAwareBlockOptions, } from './hiddenTools.js';
|
|
18
|
+
export { dispatcherAwareBlock } from './hiddenTools.js';
|
|
19
|
+
export type { SanitizeToolDescriptionsOptions } from './sanitizeCatalog.js';
|
|
20
|
+
export { sanitizeToolDescriptions } from './sanitizeCatalog.js';
|
|
21
|
+
export type { ProxyOptions, HttpProxyOptions, LocalTool, ToolMiddleware, ResourceMiddleware, PromptMiddleware, ListToolsMiddleware, ToolResultHandlers, } from './core.js';
|
|
22
|
+
export { hasToolContent, mapToolResult, createProxyServer, startProxy, startHttpProxy, } from './core.js';
|
|
14
23
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAEnE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,YAAY,EACV,YAAY,EACZ,aAAa,EACb,cAAc,GACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAEhF,YAAY,EACV,cAAc,EACd,sBAAsB,EACtB,6BAA6B,GAC9B,MAAM,gBAAgB,CAAC;AAExB,YAAY,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1C,YAAY,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE3E,YAAY,EACV,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAEhF,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD,YAAY,EACV,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,YAAY,EAAE,+BAA+B,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,SAAS,EACT,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,cAAc,GACf,MAAM,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
export { compose } from './middleware.js';
|
|
1
|
+
export { compose, markPassThroughObserver } from './middleware.js';
|
|
2
2
|
export { createBackendClient } from './backendClient.js';
|
|
3
|
-
export { createProxyContext } from './proxyContext.js';
|
|
3
|
+
export { createProxyContext, outboundDelegationChain } from './proxyContext.js';
|
|
4
4
|
export { createInMemoryEventStore } from './eventStore.js';
|
|
5
|
-
export {
|
|
5
|
+
export { DELEGATION_META_KEY, serializeDelegationChain } from './delegation.js';
|
|
6
|
+
export { rejectionMcpError } from './rejection.js';
|
|
7
|
+
export { dispatcherAwareBlock } from './hiddenTools.js';
|
|
8
|
+
export { sanitizeToolDescriptions } from './sanitizeCatalog.js';
|
|
9
|
+
export { hasToolContent, mapToolResult, createProxyServer, startProxy, startHttpProxy, } from './core.js';
|
|
6
10
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAGnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AASzD,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAWhF,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAQ3D,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAGhF,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAMnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAGxD,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAYhE,OAAO,EACL,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,cAAc,GACf,MAAM,WAAW,CAAC"}
|
package/dist/mesh.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-backend (mesh) composition: backend-key validation, namespacing,
|
|
3
|
+
* routing, and failure-isolated listing. See ADR-0013.
|
|
4
|
+
*/
|
|
5
|
+
import type { BackendClient } from './backendClient.js';
|
|
6
|
+
/** One named upstream of a mesh. */
|
|
7
|
+
export interface MeshEntry {
|
|
8
|
+
readonly key: string;
|
|
9
|
+
readonly client: BackendClient;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Either a single upstream (1:1 proxy, no namespacing) or a record of named
|
|
13
|
+
* backends keyed by backend key (mesh mode). See ADR-0013.
|
|
14
|
+
*/
|
|
15
|
+
export type Backends = BackendClient | Readonly<Record<string, BackendClient>>;
|
|
16
|
+
/**
|
|
17
|
+
* Normalizes the `backends` argument to an ordered entry list. The 1:1 form
|
|
18
|
+
* yields a single entry with an empty key and `mesh: false`.
|
|
19
|
+
*
|
|
20
|
+
* @throws On an empty record, an empty backend key, or a key containing the
|
|
21
|
+
* namespace separator — each would make an exposed name ambiguous, and
|
|
22
|
+
* guessing which upstream a caller meant is the failure mesh routing exists
|
|
23
|
+
* to prevent.
|
|
24
|
+
*/
|
|
25
|
+
export declare function normalizeBackends(backends: Backends): {
|
|
26
|
+
mesh: boolean;
|
|
27
|
+
entries: ReadonlyArray<MeshEntry>;
|
|
28
|
+
};
|
|
29
|
+
/** Namespaces a listed tool or prompt as `<key>__<name>`. */
|
|
30
|
+
export declare function namespaceName<Item extends {
|
|
31
|
+
name: string;
|
|
32
|
+
}>(key: string, item: Item): Item;
|
|
33
|
+
/**
|
|
34
|
+
* Exposes a listed resource as `mcpose://<key>/<uri>`. The upstream URI is
|
|
35
|
+
* appended untouched, so what the client holds is still one valid URI whose
|
|
36
|
+
* only meaning is "read me through this proxy", exactly as opaque as before.
|
|
37
|
+
*/
|
|
38
|
+
export declare function namespaceUri<Item extends {
|
|
39
|
+
uri: string;
|
|
40
|
+
}>(key: string, item: Item): Item;
|
|
41
|
+
/**
|
|
42
|
+
* Resolves an exposed resource URI to its backend and upstream URI.
|
|
43
|
+
*
|
|
44
|
+
* Returns `undefined` for a URI not under `mcpose://`, an unknown key, or an
|
|
45
|
+
* empty upstream URI, and the caller rejects it as `BACKEND_UNROUTABLE`:
|
|
46
|
+
* there is no "which backend listed this URI" lookup, for the same reason
|
|
47
|
+
* tool names get no single-backend fallback.
|
|
48
|
+
*/
|
|
49
|
+
export declare function routeResourceUri(uri: string, byKey: ReadonlyMap<string, BackendClient>): {
|
|
50
|
+
client: BackendClient;
|
|
51
|
+
uri: string;
|
|
52
|
+
} | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* Resolves an exposed name to its backend and upstream name.
|
|
55
|
+
*
|
|
56
|
+
* Returns `undefined` for an un-namespaced name, an unknown key, or an empty
|
|
57
|
+
* upstream name. The caller turns that into a loud `BACKEND_UNROUTABLE`
|
|
58
|
+
* rejection: there is deliberately no "resolve it if only one backend has
|
|
59
|
+
* that name" fallback, because it silently re-routes once a backend is added.
|
|
60
|
+
*/
|
|
61
|
+
export declare function routeNamespaced(name: string, byKey: ReadonlyMap<string, BackendClient>): {
|
|
62
|
+
client: BackendClient;
|
|
63
|
+
name: string;
|
|
64
|
+
} | undefined;
|
|
65
|
+
/** One page of a paginated list surface. */
|
|
66
|
+
export interface MeshPage<Item> {
|
|
67
|
+
items: ReadonlyArray<Item>;
|
|
68
|
+
nextCursor: string | undefined;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Lists one paginated surface across every backend concurrently, exposing
|
|
72
|
+
* each entry under its backend key via `expose` and isolating failures: a
|
|
73
|
+
* backend that throws (or never finishes paginating) contributes nothing and
|
|
74
|
+
* is reported through `onDegraded`, while the live backends' entries are
|
|
75
|
+
* still returned.
|
|
76
|
+
*
|
|
77
|
+
* Entries keep backend declaration order. A backend that fails part-way
|
|
78
|
+
* through pagination contributes nothing at all rather than a truncated
|
|
79
|
+
* list, so the response never looks complete when it is not.
|
|
80
|
+
*/
|
|
81
|
+
export declare function listAcrossMesh<Item>(entries: ReadonlyArray<MeshEntry>, listPage: (client: BackendClient, cursor: string | undefined) => Promise<MeshPage<Item>>, onDegraded: (key: string, error: unknown) => void, expose: (key: string, item: Item) => Item): Promise<Item[]>;
|
|
82
|
+
//# sourceMappingURL=mesh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mesh.d.ts","sourceRoot":"","sources":["../src/mesh.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AA6BxD,oCAAoC;AACpC,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;AAK/E;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG;IACrD,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CACnC,CAgCA;AAED,6DAA6D;AAC7D,wBAAgB,aAAa,CAAC,IAAI,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EACzD,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,IAAI,GACT,IAAI,CAEN;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,SAAS;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,EACvD,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,IAAI,GACT,IAAI,CAEN;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,GACxC;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CASpD;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,GACxC;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAOrD;AAED,4CAA4C;AAC5C,MAAM,WAAW,QAAQ,CAAC,IAAI;IAC5B,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAAC,IAAI,EACvC,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,EACjC,QAAQ,EAAE,CACR,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,MAAM,GAAG,SAAS,KACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAC5B,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,EACjD,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,KAAK,IAAI,GACxC,OAAO,CAAC,IAAI,EAAE,CAAC,CAsBjB"}
|
package/dist/mesh.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Separates a backend key from the upstream name in mesh mode:
|
|
3
|
+
* `<backendKey>__<name>`. A key may not contain it, so the first occurrence
|
|
4
|
+
* always splits the two, even when the upstream name contains one itself.
|
|
5
|
+
*/
|
|
6
|
+
const BACKEND_NAMESPACE_SEPARATOR = '__';
|
|
7
|
+
/**
|
|
8
|
+
* A backend key is an identifier: it is spliced into tool and prompt names
|
|
9
|
+
* and, for resources, into the authority of a `mcpose://<key>/<uri>` URI
|
|
10
|
+
* (ADR-0022), so it is limited to what both a name and a URI host accept.
|
|
11
|
+
*/
|
|
12
|
+
const BACKEND_KEY = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
13
|
+
/**
|
|
14
|
+
* The scheme a mesh exposes upstream resources under. The key is the
|
|
15
|
+
* authority and the upstream URI follows verbatim as the path, so the
|
|
16
|
+
* original is recovered by stripping a fixed prefix and never parsed.
|
|
17
|
+
*/
|
|
18
|
+
const RESOURCE_SCHEME = 'mcpose://';
|
|
19
|
+
/**
|
|
20
|
+
* Pages drained from one backend before the proxy gives up on it. A backend
|
|
21
|
+
* that never advances its cursor would otherwise hang the whole list.
|
|
22
|
+
*/
|
|
23
|
+
const MAX_PAGES_PER_BACKEND = 100;
|
|
24
|
+
const isBackendClient = (backends) => typeof backends.getServerCapabilities === 'function';
|
|
25
|
+
/**
|
|
26
|
+
* Normalizes the `backends` argument to an ordered entry list. The 1:1 form
|
|
27
|
+
* yields a single entry with an empty key and `mesh: false`.
|
|
28
|
+
*
|
|
29
|
+
* @throws On an empty record, an empty backend key, or a key containing the
|
|
30
|
+
* namespace separator — each would make an exposed name ambiguous, and
|
|
31
|
+
* guessing which upstream a caller meant is the failure mesh routing exists
|
|
32
|
+
* to prevent.
|
|
33
|
+
*/
|
|
34
|
+
export function normalizeBackends(backends) {
|
|
35
|
+
if (isBackendClient(backends)) {
|
|
36
|
+
return { mesh: false, entries: [{ key: '', client: backends }] };
|
|
37
|
+
}
|
|
38
|
+
const entries = Object.entries(backends).map(([key, client]) => ({
|
|
39
|
+
key,
|
|
40
|
+
client,
|
|
41
|
+
}));
|
|
42
|
+
if (entries.length === 0) {
|
|
43
|
+
throw new Error('mcpose: backends record is empty — pass at least one named backend, or a single BackendClient for a 1:1 proxy.');
|
|
44
|
+
}
|
|
45
|
+
for (const { key } of entries) {
|
|
46
|
+
if (key === '') {
|
|
47
|
+
throw new Error('mcpose: backend key must not be empty');
|
|
48
|
+
}
|
|
49
|
+
if (key.includes(BACKEND_NAMESPACE_SEPARATOR)) {
|
|
50
|
+
throw new Error(`mcpose: backend key "${key}" must not contain "${BACKEND_NAMESPACE_SEPARATOR}" — it is the namespace separator`);
|
|
51
|
+
}
|
|
52
|
+
if (!BACKEND_KEY.test(key)) {
|
|
53
|
+
throw new Error(`mcpose: backend key "${key}" must be an identifier (letters, digits, ".", "_" and "-", starting with a letter or digit) — it names the backend inside tool names and resource URIs`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { mesh: true, entries };
|
|
57
|
+
}
|
|
58
|
+
/** Namespaces a listed tool or prompt as `<key>__<name>`. */
|
|
59
|
+
export function namespaceName(key, item) {
|
|
60
|
+
return { ...item, name: `${key}${BACKEND_NAMESPACE_SEPARATOR}${item.name}` };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Exposes a listed resource as `mcpose://<key>/<uri>`. The upstream URI is
|
|
64
|
+
* appended untouched, so what the client holds is still one valid URI whose
|
|
65
|
+
* only meaning is "read me through this proxy", exactly as opaque as before.
|
|
66
|
+
*/
|
|
67
|
+
export function namespaceUri(key, item) {
|
|
68
|
+
return { ...item, uri: `${RESOURCE_SCHEME}${key}/${item.uri}` };
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Resolves an exposed resource URI to its backend and upstream URI.
|
|
72
|
+
*
|
|
73
|
+
* Returns `undefined` for a URI not under `mcpose://`, an unknown key, or an
|
|
74
|
+
* empty upstream URI, and the caller rejects it as `BACKEND_UNROUTABLE`:
|
|
75
|
+
* there is no "which backend listed this URI" lookup, for the same reason
|
|
76
|
+
* tool names get no single-backend fallback.
|
|
77
|
+
*/
|
|
78
|
+
export function routeResourceUri(uri, byKey) {
|
|
79
|
+
if (!uri.startsWith(RESOURCE_SCHEME))
|
|
80
|
+
return undefined;
|
|
81
|
+
const rest = uri.slice(RESOURCE_SCHEME.length);
|
|
82
|
+
const slash = rest.indexOf('/');
|
|
83
|
+
if (slash <= 0)
|
|
84
|
+
return undefined;
|
|
85
|
+
const client = byKey.get(rest.slice(0, slash));
|
|
86
|
+
const upstreamUri = rest.slice(slash + 1);
|
|
87
|
+
if (client === undefined || upstreamUri === '')
|
|
88
|
+
return undefined;
|
|
89
|
+
return { client, uri: upstreamUri };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Resolves an exposed name to its backend and upstream name.
|
|
93
|
+
*
|
|
94
|
+
* Returns `undefined` for an un-namespaced name, an unknown key, or an empty
|
|
95
|
+
* upstream name. The caller turns that into a loud `BACKEND_UNROUTABLE`
|
|
96
|
+
* rejection: there is deliberately no "resolve it if only one backend has
|
|
97
|
+
* that name" fallback, because it silently re-routes once a backend is added.
|
|
98
|
+
*/
|
|
99
|
+
export function routeNamespaced(name, byKey) {
|
|
100
|
+
const at = name.indexOf(BACKEND_NAMESPACE_SEPARATOR);
|
|
101
|
+
if (at <= 0)
|
|
102
|
+
return undefined;
|
|
103
|
+
const client = byKey.get(name.slice(0, at));
|
|
104
|
+
const upstreamName = name.slice(at + BACKEND_NAMESPACE_SEPARATOR.length);
|
|
105
|
+
if (client === undefined || upstreamName === '')
|
|
106
|
+
return undefined;
|
|
107
|
+
return { client, name: upstreamName };
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Lists one paginated surface across every backend concurrently, exposing
|
|
111
|
+
* each entry under its backend key via `expose` and isolating failures: a
|
|
112
|
+
* backend that throws (or never finishes paginating) contributes nothing and
|
|
113
|
+
* is reported through `onDegraded`, while the live backends' entries are
|
|
114
|
+
* still returned.
|
|
115
|
+
*
|
|
116
|
+
* Entries keep backend declaration order. A backend that fails part-way
|
|
117
|
+
* through pagination contributes nothing at all rather than a truncated
|
|
118
|
+
* list, so the response never looks complete when it is not.
|
|
119
|
+
*/
|
|
120
|
+
export async function listAcrossMesh(entries, listPage, onDegraded, expose) {
|
|
121
|
+
const settled = await Promise.allSettled(entries.map(async ({ key, client }) => {
|
|
122
|
+
const collected = [];
|
|
123
|
+
let cursor;
|
|
124
|
+
for (let page = 0; page < MAX_PAGES_PER_BACKEND; page += 1) {
|
|
125
|
+
const { items, nextCursor } = await listPage(client, cursor);
|
|
126
|
+
for (const item of items)
|
|
127
|
+
collected.push(expose(key, item));
|
|
128
|
+
if (nextCursor === undefined)
|
|
129
|
+
return collected;
|
|
130
|
+
cursor = nextCursor;
|
|
131
|
+
}
|
|
132
|
+
throw new Error(`mcpose: backend "${key}" did not finish paginating within ${MAX_PAGES_PER_BACKEND} pages`);
|
|
133
|
+
}));
|
|
134
|
+
return settled.flatMap((outcome, index) => {
|
|
135
|
+
if (outcome.status === 'fulfilled')
|
|
136
|
+
return outcome.value;
|
|
137
|
+
onDegraded(entries[index].key, outcome.reason);
|
|
138
|
+
return [];
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=mesh.js.map
|
package/dist/mesh.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mesh.js","sourceRoot":"","sources":["../src/mesh.ts"],"names":[],"mappings":"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"}
|
package/dist/middleware.d.ts
CHANGED
|
@@ -32,5 +32,18 @@ export declare function compose<Req, Res>(middlewares: ReadonlyArray<Middleware<
|
|
|
32
32
|
* Used internally by mcpose core — consumers pass arrays to `ProxyOptions`.
|
|
33
33
|
*/
|
|
34
34
|
export declare function pipe<Req, Res>(middlewares: ReadonlyArray<Middleware<Req, Res>>): MiddlewarePipeline<Req, Res>;
|
|
35
|
+
/**
|
|
36
|
+
* Marks a middleware as a pass-through observer: it still runs for tools
|
|
37
|
+
* listed in `ProxyOptions.passThroughTools`, which skip all other middleware.
|
|
38
|
+
*
|
|
39
|
+
* Use for middleware that must see every call regardless of pass-through
|
|
40
|
+
* status — audit logging, telemetry — never for middleware that transforms
|
|
41
|
+
* requests or responses (pass-through means "forward upstream as-is").
|
|
42
|
+
*
|
|
43
|
+
* Returns a new middleware; the input is not mutated.
|
|
44
|
+
*/
|
|
45
|
+
export declare function markPassThroughObserver<Req, Res>(mw: Middleware<Req, Res>): Middleware<Req, Res>;
|
|
46
|
+
/** True when the middleware was wrapped by {@link markPassThroughObserver}. */
|
|
47
|
+
export declare function isPassThroughObserver(mw: unknown): boolean;
|
|
35
48
|
export {};
|
|
36
49
|
//# sourceMappingURL=middleware.d.ts.map
|
package/dist/middleware.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE1E,4EAA4E;AAC5E,MAAM,MAAM,UAAU,CAAC,GAAG,EAAE,GAAG,IAAI,CACjC,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,EAChC,OAAO,EAAE,YAAY,KAClB,OAAO,CAAC,GAAG,CAAC,CAAC;AAElB,KAAK,kBAAkB,CAAC,GAAG,EAAE,GAAG,IAAI;IAClC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3D,CACE,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,EAChC,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,GAAG,CAAC,CAAC;CACjB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,GAAG,EAC9B,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAC/C,kBAAkB,CAAC,GAAG,EAAE,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE1E,4EAA4E;AAC5E,MAAM,MAAM,UAAU,CAAC,GAAG,EAAE,GAAG,IAAI,CACjC,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,EAChC,OAAO,EAAE,YAAY,KAClB,OAAO,CAAC,GAAG,CAAC,CAAC;AAElB,KAAK,kBAAkB,CAAC,GAAG,EAAE,GAAG,IAAI;IAClC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3D,CACE,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,EAChC,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,GAAG,CAAC,CAAC;CACjB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,GAAG,EAC9B,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAC/C,kBAAkB,CAAC,GAAG,EAAE,GAAG,CAAC,CAoB9B;AAED;;;;GAIG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,GAAG,EAC3B,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAC/C,kBAAkB,CAAC,GAAG,EAAE,GAAG,CAAC,CAE9B;AAID;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,GAAG,EAC9C,EAAE,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,GACvB,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAQtB;AAED,+EAA+E;AAC/E,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAE1D"}
|
package/dist/middleware.js
CHANGED
|
@@ -20,18 +20,19 @@ import { createProxyContext } from './proxyContext.js';
|
|
|
20
20
|
* const result = await pipeline(req, (r) => upstream.callTool(r.params));
|
|
21
21
|
*/
|
|
22
22
|
export function compose(middlewares) {
|
|
23
|
-
return (
|
|
23
|
+
return (req, next, context = createProxyContext()) => {
|
|
24
24
|
let index = -1;
|
|
25
25
|
const dispatch = (i, currentReq) => {
|
|
26
26
|
if (i <= index) {
|
|
27
27
|
return Promise.reject(new Error('next() called multiple times'));
|
|
28
28
|
}
|
|
29
29
|
index = i;
|
|
30
|
-
|
|
30
|
+
// Past the end of the pipeline, the terminal handler runs.
|
|
31
|
+
const fn = middlewares[i] ?? ((r) => next(r));
|
|
31
32
|
return Promise.resolve(fn(currentReq, (r) => dispatch(i + 1, r), context));
|
|
32
33
|
};
|
|
33
34
|
return dispatch(0, req);
|
|
34
|
-
}
|
|
35
|
+
};
|
|
35
36
|
}
|
|
36
37
|
/**
|
|
37
38
|
* Like `compose` but in response-processing order (first = innermost).
|
|
@@ -41,4 +42,27 @@ export function compose(middlewares) {
|
|
|
41
42
|
export function pipe(middlewares) {
|
|
42
43
|
return compose([...middlewares].reverse());
|
|
43
44
|
}
|
|
45
|
+
const PASS_THROUGH_OBSERVER = Symbol.for('mcpose.passThroughObserver');
|
|
46
|
+
/**
|
|
47
|
+
* Marks a middleware as a pass-through observer: it still runs for tools
|
|
48
|
+
* listed in `ProxyOptions.passThroughTools`, which skip all other middleware.
|
|
49
|
+
*
|
|
50
|
+
* Use for middleware that must see every call regardless of pass-through
|
|
51
|
+
* status — audit logging, telemetry — never for middleware that transforms
|
|
52
|
+
* requests or responses (pass-through means "forward upstream as-is").
|
|
53
|
+
*
|
|
54
|
+
* Returns a new middleware; the input is not mutated.
|
|
55
|
+
*/
|
|
56
|
+
export function markPassThroughObserver(mw) {
|
|
57
|
+
const marked = (req, next, context) => mw(req, next, context);
|
|
58
|
+
Object.defineProperty(marked, PASS_THROUGH_OBSERVER, {
|
|
59
|
+
value: true,
|
|
60
|
+
enumerable: false,
|
|
61
|
+
});
|
|
62
|
+
return marked;
|
|
63
|
+
}
|
|
64
|
+
/** True when the middleware was wrapped by {@link markPassThroughObserver}. */
|
|
65
|
+
export function isPassThroughObserver(mw) {
|
|
66
|
+
return typeof mw === 'function' && PASS_THROUGH_OBSERVER in mw;
|
|
67
|
+
}
|
|
44
68
|
//# sourceMappingURL=middleware.js.map
|
package/dist/middleware.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,kBAAkB,EAAqB,MAAM,mBAAmB,CAAC;AAkB1E;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CACrB,WAAgD;IAEhD,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,kBAAkB,EAAqB,MAAM,mBAAmB,CAAC;AAkB1E;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CACrB,WAAgD;IAEhD,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,UAAwB,kBAAkB,EAAE,EAAE,EAAE;QACjE,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC;QAEf,MAAM,QAAQ,GAAG,CAAC,CAAS,EAAE,UAAe,EAAgB,EAAE;YAC5D,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;gBACf,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC,CAAC;YACnE,CAAC;YACD,KAAK,GAAG,CAAC,CAAC;YAEV,2DAA2D;YAC3D,MAAM,EAAE,GAAyB,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAEpE,OAAO,OAAO,CAAC,OAAO,CACpB,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CACnD,CAAC;QACJ,CAAC,CAAC;QAEF,OAAO,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC1B,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,IAAI,CAClB,WAAgD;IAEhD,OAAO,OAAO,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,qBAAqB,GAAG,MAAM,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;AAEvE;;;;;;;;;GASG;AACH,MAAM,UAAU,uBAAuB,CACrC,EAAwB;IAExB,MAAM,MAAM,GAAyB,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAC1D,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACzB,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,qBAAqB,EAAE;QACnD,KAAK,EAAE,IAAI;QACX,UAAU,EAAE,KAAK;KAClB,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,qBAAqB,CAAC,EAAW;IAC/C,OAAO,OAAO,EAAE,KAAK,UAAU,IAAI,qBAAqB,IAAI,EAAE,CAAC;AACjE,CAAC"}
|
package/dist/proxyContext.d.ts
CHANGED
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
import type { Identity } from './identity.js';
|
|
2
|
+
/**
|
|
3
|
+
* The proxy instance a request passed through, as resolved from
|
|
4
|
+
* `ProxyOptions.name`/`version` (defaults included). Provenance, not a
|
|
5
|
+
* principal: it is never an entry in `delegatedFrom` and takes no part in
|
|
6
|
+
* the caller-attribution model (ADR-0011, ADR-0012).
|
|
7
|
+
*/
|
|
8
|
+
export interface ProxyIdentity {
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly version: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A policy decision already made, stamped on the context by the policy
|
|
14
|
+
* middleware (ADR-0017). It is a record, not a handle to query: core never
|
|
15
|
+
* evaluates policy itself, and a second evaluation path would let inner
|
|
16
|
+
* middleware ask a different question than the gate answered.
|
|
17
|
+
*
|
|
18
|
+
* `reason` carries the `RejectionReason` that accompanied a denial.
|
|
19
|
+
*/
|
|
20
|
+
export interface PolicyDecision {
|
|
21
|
+
readonly decision: 'allow' | 'deny';
|
|
22
|
+
readonly ruleId?: string;
|
|
23
|
+
readonly reason?: string;
|
|
24
|
+
}
|
|
2
25
|
/** Normalized request metadata that mcpose passes through middleware layers. */
|
|
3
26
|
export interface ProxyContext {
|
|
4
27
|
requestId: string;
|
|
@@ -8,11 +31,48 @@ export interface ProxyContext {
|
|
|
8
31
|
signal?: AbortSignal;
|
|
9
32
|
/** Resolved caller identity. Present when {@link HttpProxyOptions.resolveIdentity} is configured. */
|
|
10
33
|
identity?: Identity;
|
|
11
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Agent delegation chain, oldest-first. Populated from the inbound
|
|
36
|
+
* request's `_meta["mcpose/delegation"]` when a caller presents one
|
|
37
|
+
* (ADR-0016), or by a host that stamps its own, which wins.
|
|
38
|
+
*
|
|
39
|
+
* Attribution, never authorization: every entry extracted from the wire
|
|
40
|
+
* carries empty `roles` and `claims`, because a presented chain is written
|
|
41
|
+
* by the previous hop.
|
|
42
|
+
*/
|
|
12
43
|
delegatedFrom?: Identity[];
|
|
13
|
-
/**
|
|
14
|
-
|
|
44
|
+
/** The proxy instance handling this request. Stamped by {@link createProxyServer}. */
|
|
45
|
+
proxy?: ProxyIdentity;
|
|
46
|
+
/**
|
|
47
|
+
* The policy decision for this call. Stamped by the `@mcpose/policy`
|
|
48
|
+
* middleware before it calls `next` (allow) or throws (deny), so
|
|
49
|
+
* middleware running inside the policy layer can rely on
|
|
50
|
+
* `decision: 'allow'` (ADR-0017). Core never writes it.
|
|
51
|
+
*/
|
|
52
|
+
policy?: PolicyDecision;
|
|
15
53
|
}
|
|
16
|
-
/**
|
|
17
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Canonical oldest-first delegation chain a host attaches to outbound calls
|
|
56
|
+
* made on behalf of the inbound caller (ADR-0011). Returns a fresh array and
|
|
57
|
+
* never mutates or aliases `ctx.delegatedFrom`, because the audit middleware
|
|
58
|
+
* reads the same context object when recording the inbound call.
|
|
59
|
+
*
|
|
60
|
+
* Core attaches this chain itself to every call it forwards to a backend, at
|
|
61
|
+
* `params._meta["mcpose/delegation"]` (ADR-0016). A local tool handler makes
|
|
62
|
+
* its own outbound calls, so its host attaches the chain: for an outbound MCP
|
|
63
|
+
* call, `serializeDelegationChain(outboundDelegationChain(context))` under
|
|
64
|
+
* `DELEGATION_META_KEY` produces exactly what core sends.
|
|
65
|
+
*/
|
|
66
|
+
export declare function outboundDelegationChain(ctx: ProxyContext): Identity[];
|
|
67
|
+
/**
|
|
68
|
+
* Creates a middleware context with a fresh request ID.
|
|
69
|
+
*
|
|
70
|
+
* Every override may be passed explicitly as `undefined` — it means "not
|
|
71
|
+
* provided" and the key is dropped from the result, which is why the
|
|
72
|
+
* parameter is not a plain `Partial<ProxyContext>` under
|
|
73
|
+
* `exactOptionalPropertyTypes`.
|
|
74
|
+
*/
|
|
75
|
+
export declare function createProxyContext(overrides?: {
|
|
76
|
+
[K in keyof ProxyContext]?: ProxyContext[K] | undefined;
|
|
77
|
+
}): ProxyContext;
|
|
18
78
|
//# sourceMappingURL=proxyContext.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxyContext.d.ts","sourceRoot":"","sources":["../src/proxyContext.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,gFAAgF;AAChF,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,GAAG,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3C,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,qGAAqG;IACrG,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB
|
|
1
|
+
{"version":3,"file":"proxyContext.d.ts","sourceRoot":"","sources":["../src/proxyContext.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,gFAAgF;AAChF,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,GAAG,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3C,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,qGAAqG;IACrG,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,QAAQ,EAAE,CAAC;IAC3B,sFAAsF;IACtF,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,cAAc,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,YAAY,GAAG,QAAQ,EAAE,CAKrE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,GAAE;KAAG,CAAC,IAAI,MAAM,YAAY,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS;CAAO,GAC1E,YAAY,CAoBd"}
|
package/dist/proxyContext.js
CHANGED
|
@@ -1,14 +1,49 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Canonical oldest-first delegation chain a host attaches to outbound calls
|
|
4
|
+
* made on behalf of the inbound caller (ADR-0011). Returns a fresh array and
|
|
5
|
+
* never mutates or aliases `ctx.delegatedFrom`, because the audit middleware
|
|
6
|
+
* reads the same context object when recording the inbound call.
|
|
7
|
+
*
|
|
8
|
+
* Core attaches this chain itself to every call it forwards to a backend, at
|
|
9
|
+
* `params._meta["mcpose/delegation"]` (ADR-0016). A local tool handler makes
|
|
10
|
+
* its own outbound calls, so its host attaches the chain: for an outbound MCP
|
|
11
|
+
* call, `serializeDelegationChain(outboundDelegationChain(context))` under
|
|
12
|
+
* `DELEGATION_META_KEY` produces exactly what core sends.
|
|
13
|
+
*/
|
|
14
|
+
export function outboundDelegationChain(ctx) {
|
|
15
|
+
return [
|
|
16
|
+
...(ctx.delegatedFrom ?? []),
|
|
17
|
+
...(ctx.identity === undefined ? [] : [ctx.identity]),
|
|
18
|
+
];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Creates a middleware context with a fresh request ID.
|
|
22
|
+
*
|
|
23
|
+
* Every override may be passed explicitly as `undefined` — it means "not
|
|
24
|
+
* provided" and the key is dropped from the result, which is why the
|
|
25
|
+
* parameter is not a plain `Partial<ProxyContext>` under
|
|
26
|
+
* `exactOptionalPropertyTypes`.
|
|
27
|
+
*/
|
|
3
28
|
export function createProxyContext(overrides = {}) {
|
|
4
29
|
return {
|
|
30
|
+
// Deliberately `||`, not `??`: an empty-string requestId is useless for
|
|
31
|
+
// correlation, so it is regenerated like a missing one.
|
|
5
32
|
requestId: overrides.requestId || randomUUID(),
|
|
6
33
|
transport: overrides.transport ?? 'stdio',
|
|
7
|
-
...(overrides.sessionId === undefined
|
|
34
|
+
...(overrides.sessionId === undefined
|
|
35
|
+
? {}
|
|
36
|
+
: { sessionId: overrides.sessionId }),
|
|
8
37
|
...(overrides.headers === undefined ? {} : { headers: overrides.headers }),
|
|
9
38
|
...(overrides.signal === undefined ? {} : { signal: overrides.signal }),
|
|
10
|
-
...(overrides.identity === undefined
|
|
11
|
-
|
|
39
|
+
...(overrides.identity === undefined
|
|
40
|
+
? {}
|
|
41
|
+
: { identity: overrides.identity }),
|
|
42
|
+
...(overrides.delegatedFrom === undefined
|
|
43
|
+
? {}
|
|
44
|
+
: { delegatedFrom: overrides.delegatedFrom }),
|
|
45
|
+
...(overrides.proxy === undefined ? {} : { proxy: overrides.proxy }),
|
|
46
|
+
...(overrides.policy === undefined ? {} : { policy: overrides.policy }),
|
|
12
47
|
};
|
|
13
48
|
}
|
|
14
49
|
//# sourceMappingURL=proxyContext.js.map
|
package/dist/proxyContext.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxyContext.js","sourceRoot":"","sources":["../src/proxyContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"proxyContext.js","sourceRoot":"","sources":["../src/proxyContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA0DzC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,uBAAuB,CAAC,GAAiB;IACvD,OAAO;QACL,GAAG,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC;QAC5B,GAAG,CAAC,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;KACtD,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,YAAyE,EAAE;IAE3E,OAAO;QACL,wEAAwE;QACxE,wDAAwD;QACxD,SAAS,EAAE,SAAS,CAAC,SAAS,IAAI,UAAU,EAAE;QAC9C,SAAS,EAAE,SAAS,CAAC,SAAS,IAAI,OAAO;QACzC,GAAG,CAAC,SAAS,CAAC,SAAS,KAAK,SAAS;YACnC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,SAAS,EAAE,CAAC;QACvC,GAAG,CAAC,SAAS,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC;QAC1E,GAAG,CAAC,SAAS,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;QACvE,GAAG,CAAC,SAAS,CAAC,QAAQ,KAAK,SAAS;YAClC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,QAAQ,EAAE,CAAC;QACrC,GAAG,CAAC,SAAS,CAAC,aAAa,KAAK,SAAS;YACvC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,aAAa,EAAE,SAAS,CAAC,aAAa,EAAE,CAAC;QAC/C,GAAG,CAAC,SAAS,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,CAAC;QACpE,GAAG,CAAC,SAAS,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;KACxE,CAAC;AACJ,CAAC"}
|
package/dist/rejection.d.ts
CHANGED
|
@@ -9,7 +9,13 @@ import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
|
|
|
9
9
|
*
|
|
10
10
|
* @stable
|
|
11
11
|
*/
|
|
12
|
-
export type RejectionReason = 'TOOL_HIDDEN' | 'RESOURCE_HIDDEN' | 'POLICY_DENIED' | 'IDENTITY_UNRESOLVED' | 'CONSENT_MISSING' | 'SENSITIVITY_BLOCKED' | 'DELEGATION_INVALID' | 'BUDGET_EXCEEDED' | 'SESSION_LIMIT' | 'BODY_LIMIT';
|
|
12
|
+
export type RejectionReason = 'TOOL_HIDDEN' | 'RESOURCE_HIDDEN' | 'BACKEND_UNROUTABLE' | 'POLICY_DENIED' | 'IDENTITY_UNRESOLVED' | 'CONSENT_MISSING' | 'SENSITIVITY_BLOCKED' | 'DELEGATION_INVALID' | 'BUDGET_EXCEEDED' | 'SESSION_LIMIT' | 'BODY_LIMIT';
|
|
13
|
+
/**
|
|
14
|
+
* Narrows an untrusted value to `RejectionReason`.
|
|
15
|
+
* `Object.hasOwn`, not `in`: the value can come from an upstream error, so a
|
|
16
|
+
* prototype key like `constructor` must not pass as a member.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isRejectionReason(value: unknown): value is RejectionReason;
|
|
13
19
|
/**
|
|
14
20
|
* Creates an `McpError` with the rejection reason embedded in `error.data`.
|
|
15
21
|
* The top-level `code` is unchanged so clients that only inspect the code
|
package/dist/rejection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rejection.d.ts","sourceRoot":"","sources":["../src/rejection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAEzE;;;;;;;;;GASG;AACH,MAAM,MAAM,eAAe,GACvB,aAAa,GACb,iBAAiB,GACjB,eAAe,GACf,qBAAqB,GACrB,iBAAiB,GACjB,qBAAqB,GACrB,oBAAoB,GACpB,iBAAiB,GACjB,eAAe,GACf,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"rejection.d.ts","sourceRoot":"","sources":["../src/rejection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAEzE;;;;;;;;;GASG;AACH,MAAM,MAAM,eAAe,GACvB,aAAa,GACb,iBAAiB,GACjB,oBAAoB,GACpB,eAAe,GACf,qBAAqB,GACrB,iBAAiB,GACjB,qBAAqB,GACrB,oBAAoB,GACpB,iBAAiB,GACjB,eAAe,GACf,YAAY,CAAC;AAkBjB;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAE1E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,MAAM,GACd,QAAQ,CAEV"}
|
package/dist/rejection.js
CHANGED
|
@@ -1,4 +1,27 @@
|
|
|
1
|
-
import { McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
1
|
+
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
// The `Record` annotation keeps this table and the union from drifting apart:
|
|
3
|
+
// a missing or extra key is a compile error.
|
|
4
|
+
const REJECTION_REASONS = {
|
|
5
|
+
TOOL_HIDDEN: true,
|
|
6
|
+
RESOURCE_HIDDEN: true,
|
|
7
|
+
BACKEND_UNROUTABLE: true,
|
|
8
|
+
POLICY_DENIED: true,
|
|
9
|
+
IDENTITY_UNRESOLVED: true,
|
|
10
|
+
CONSENT_MISSING: true,
|
|
11
|
+
SENSITIVITY_BLOCKED: true,
|
|
12
|
+
DELEGATION_INVALID: true,
|
|
13
|
+
BUDGET_EXCEEDED: true,
|
|
14
|
+
SESSION_LIMIT: true,
|
|
15
|
+
BODY_LIMIT: true,
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Narrows an untrusted value to `RejectionReason`.
|
|
19
|
+
* `Object.hasOwn`, not `in`: the value can come from an upstream error, so a
|
|
20
|
+
* prototype key like `constructor` must not pass as a member.
|
|
21
|
+
*/
|
|
22
|
+
export function isRejectionReason(value) {
|
|
23
|
+
return typeof value === 'string' && Object.hasOwn(REJECTION_REASONS, value);
|
|
24
|
+
}
|
|
2
25
|
/**
|
|
3
26
|
* Creates an `McpError` with the rejection reason embedded in `error.data`.
|
|
4
27
|
* The top-level `code` is unchanged so clients that only inspect the code
|
package/dist/rejection.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rejection.js","sourceRoot":"","sources":["../src/rejection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"rejection.js","sourceRoot":"","sources":["../src/rejection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAyBzE,8EAA8E;AAC9E,6CAA6C;AAC7C,MAAM,iBAAiB,GAAkC;IACvD,WAAW,EAAE,IAAI;IACjB,eAAe,EAAE,IAAI;IACrB,kBAAkB,EAAE,IAAI;IACxB,aAAa,EAAE,IAAI;IACnB,mBAAmB,EAAE,IAAI;IACzB,eAAe,EAAE,IAAI;IACrB,mBAAmB,EAAE,IAAI;IACzB,kBAAkB,EAAE,IAAI;IACxB,eAAe,EAAE,IAAI;IACrB,aAAa,EAAE,IAAI;IACnB,UAAU,EAAE,IAAI;CACjB,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAuB,EACvB,IAAe,EACf,OAAe;IAEf,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC,CAAC;AAClE,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Egress sanitizer for the tool catalog: `tools/list` forwards upstream
|
|
3
|
+
* descriptions verbatim into model context, and real upstreams leak org
|
|
4
|
+
* slugs and internal hostnames there. See ADR-0010.
|
|
5
|
+
*/
|
|
6
|
+
import type { ListToolsMiddleware } from './core.js';
|
|
7
|
+
/** Options for {@link sanitizeToolDescriptions}. */
|
|
8
|
+
export interface SanitizeToolDescriptionsOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Extra patterns to remove. A string replaces every literal occurrence;
|
|
11
|
+
* a RegExp is normalized to global, with any sticky flag dropped, so
|
|
12
|
+
* every match is replaced.
|
|
13
|
+
*/
|
|
14
|
+
patterns?: ReadonlyArray<string | RegExp>;
|
|
15
|
+
/** Replacement text for every match. Default: `''` (plain removal). */
|
|
16
|
+
replacement?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A {@link ListToolsMiddleware} that sanitizes tool descriptions and every
|
|
20
|
+
* `description` field nested in `inputSchema` / `outputSchema`. Names,
|
|
21
|
+
* titles, and schema structure are untouched, because clients route on
|
|
22
|
+
* them.
|
|
23
|
+
*
|
|
24
|
+
* Always strips http(s) URLs; `patterns` adds more. Place it **last** in
|
|
25
|
+
* `listToolsMiddleware` so it sanitizes the output of other list
|
|
26
|
+
* middleware and local tools.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* listToolsMiddleware: [
|
|
30
|
+
* sanitizeToolDescriptions({
|
|
31
|
+
* patterns: ['acme-corp', /\bi-[0-9a-f]{8,17}\b/],
|
|
32
|
+
* replacement: '[redacted]',
|
|
33
|
+
* }),
|
|
34
|
+
* ]
|
|
35
|
+
*/
|
|
36
|
+
export declare function sanitizeToolDescriptions(options?: SanitizeToolDescriptionsOptions): ListToolsMiddleware;
|
|
37
|
+
//# sourceMappingURL=sanitizeCatalog.d.ts.map
|