agentfootprint 7.13.0 → 7.15.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/dist/adapters/hosting/agentcore.js +350 -0
- package/dist/adapters/hosting/agentcore.js.map +1 -0
- package/dist/adapters/memory/agentcore.js +125 -1
- package/dist/adapters/memory/agentcore.js.map +1 -1
- package/dist/adapters/security/agentcore.js +0 -0
- package/dist/adapters/security/agentcore.js.map +1 -0
- package/dist/core/Agent.js +79 -2
- package/dist/core/Agent.js.map +1 -1
- package/dist/esm/adapters/hosting/agentcore.d.ts +199 -0
- package/dist/esm/adapters/hosting/agentcore.js +321 -0
- package/dist/esm/adapters/hosting/agentcore.js.map +1 -0
- package/dist/esm/adapters/memory/agentcore.d.ts +88 -2
- package/dist/esm/adapters/memory/agentcore.js +125 -1
- package/dist/esm/adapters/memory/agentcore.js.map +1 -1
- package/dist/esm/adapters/security/agentcore.d.ts +157 -0
- package/dist/esm/adapters/security/agentcore.js +0 -0
- package/dist/esm/adapters/security/agentcore.js.map +1 -0
- package/dist/esm/core/Agent.d.ts +47 -0
- package/dist/esm/core/Agent.js +79 -2
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/hosting/envelope.d.ts +33 -0
- package/dist/esm/hosting/envelope.js +49 -0
- package/dist/esm/hosting/envelope.js.map +1 -0
- package/dist/esm/hosting/errors.d.ts +78 -0
- package/dist/esm/hosting/errors.js +119 -0
- package/dist/esm/hosting/errors.js.map +1 -0
- package/dist/esm/hosting/httpHost.d.ts +121 -0
- package/dist/esm/hosting/httpHost.js +248 -0
- package/dist/esm/hosting/httpHost.js.map +1 -0
- package/dist/esm/hosting/index.d.ts +60 -0
- package/dist/esm/hosting/index.js +58 -0
- package/dist/esm/hosting/index.js.map +1 -0
- package/dist/esm/hosting/memorySessions.d.ts +22 -0
- package/dist/esm/hosting/memorySessions.js +31 -0
- package/dist/esm/hosting/memorySessions.js.map +1 -0
- package/dist/esm/hosting/nodeHost.d.ts +71 -0
- package/dist/esm/hosting/nodeHost.js +78 -0
- package/dist/esm/hosting/nodeHost.js.map +1 -0
- package/dist/esm/hosting/standingAgent.d.ts +62 -0
- package/dist/esm/hosting/standingAgent.js +192 -0
- package/dist/esm/hosting/standingAgent.js.map +1 -0
- package/dist/esm/hosting/types.d.ts +206 -0
- package/dist/esm/hosting/types.js +21 -0
- package/dist/esm/hosting/types.js.map +1 -0
- package/dist/esm/hosting-providers.d.ts +50 -0
- package/dist/esm/hosting-providers.js +50 -0
- package/dist/esm/hosting-providers.js.map +1 -0
- package/dist/esm/lib/mcp/gatewayTransport.d.ts +103 -0
- package/dist/esm/lib/mcp/gatewayTransport.js +123 -0
- package/dist/esm/lib/mcp/gatewayTransport.js.map +1 -0
- package/dist/esm/lib/mcp/index.d.ts +2 -1
- package/dist/esm/lib/mcp/index.js +1 -0
- package/dist/esm/lib/mcp/index.js.map +1 -1
- package/dist/esm/lib/mcp/mcpClient.js +10 -2
- package/dist/esm/lib/mcp/mcpClient.js.map +1 -1
- package/dist/esm/lib/mcp/types.d.ts +45 -1
- package/dist/esm/memory/store/types.d.ts +22 -0
- package/dist/esm/memory-providers.d.ts +1 -1
- package/dist/esm/memory-providers.js.map +1 -1
- package/dist/esm/security/index.d.ts +10 -1
- package/dist/esm/security/index.js +14 -1
- package/dist/esm/security/index.js.map +1 -1
- package/dist/esm/tool-providers/index.d.ts +6 -2
- package/dist/esm/tool-providers/index.js +5 -1
- package/dist/esm/tool-providers/index.js.map +1 -1
- package/dist/hosting/envelope.js +54 -0
- package/dist/hosting/envelope.js.map +1 -0
- package/dist/hosting/errors.js +126 -0
- package/dist/hosting/errors.js.map +1 -0
- package/dist/hosting/httpHost.js +276 -0
- package/dist/hosting/httpHost.js.map +1 -0
- package/dist/hosting/index.js +73 -0
- package/dist/hosting/index.js.map +1 -0
- package/dist/hosting/memorySessions.js +35 -0
- package/dist/hosting/memorySessions.js.map +1 -0
- package/dist/hosting/nodeHost.js +82 -0
- package/dist/hosting/nodeHost.js.map +1 -0
- package/dist/hosting/standingAgent.js +196 -0
- package/dist/hosting/standingAgent.js.map +1 -0
- package/dist/hosting/types.js +22 -0
- package/dist/hosting/types.js.map +1 -0
- package/dist/hosting-providers.js +57 -0
- package/dist/hosting-providers.js.map +1 -0
- package/dist/lib/mcp/gatewayTransport.js +129 -0
- package/dist/lib/mcp/gatewayTransport.js.map +1 -0
- package/dist/lib/mcp/index.js +4 -1
- package/dist/lib/mcp/index.js.map +1 -1
- package/dist/lib/mcp/mcpClient.js +10 -2
- package/dist/lib/mcp/mcpClient.js.map +1 -1
- package/dist/memory-providers.js.map +1 -1
- package/dist/security/index.js +16 -2
- package/dist/security/index.js.map +1 -1
- package/dist/tool-providers/index.js +7 -1
- package/dist/tool-providers/index.js.map +1 -1
- package/dist/types/adapters/hosting/agentcore.d.ts +200 -0
- package/dist/types/adapters/hosting/agentcore.d.ts.map +1 -0
- package/dist/types/adapters/memory/agentcore.d.ts +88 -2
- package/dist/types/adapters/memory/agentcore.d.ts.map +1 -1
- package/dist/types/adapters/security/agentcore.d.ts +158 -0
- package/dist/types/adapters/security/agentcore.d.ts.map +1 -0
- package/dist/types/core/Agent.d.ts +47 -0
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/hosting/envelope.d.ts +34 -0
- package/dist/types/hosting/envelope.d.ts.map +1 -0
- package/dist/types/hosting/errors.d.ts +79 -0
- package/dist/types/hosting/errors.d.ts.map +1 -0
- package/dist/types/hosting/httpHost.d.ts +122 -0
- package/dist/types/hosting/httpHost.d.ts.map +1 -0
- package/dist/types/hosting/index.d.ts +61 -0
- package/dist/types/hosting/index.d.ts.map +1 -0
- package/dist/types/hosting/memorySessions.d.ts +23 -0
- package/dist/types/hosting/memorySessions.d.ts.map +1 -0
- package/dist/types/hosting/nodeHost.d.ts +72 -0
- package/dist/types/hosting/nodeHost.d.ts.map +1 -0
- package/dist/types/hosting/standingAgent.d.ts +63 -0
- package/dist/types/hosting/standingAgent.d.ts.map +1 -0
- package/dist/types/hosting/types.d.ts +207 -0
- package/dist/types/hosting/types.d.ts.map +1 -0
- package/dist/types/hosting-providers.d.ts +51 -0
- package/dist/types/hosting-providers.d.ts.map +1 -0
- package/dist/types/lib/mcp/gatewayTransport.d.ts +104 -0
- package/dist/types/lib/mcp/gatewayTransport.d.ts.map +1 -0
- package/dist/types/lib/mcp/index.d.ts +2 -1
- package/dist/types/lib/mcp/index.d.ts.map +1 -1
- package/dist/types/lib/mcp/mcpClient.d.ts.map +1 -1
- package/dist/types/lib/mcp/types.d.ts +45 -1
- package/dist/types/lib/mcp/types.d.ts.map +1 -1
- package/dist/types/memory/store/types.d.ts +22 -0
- package/dist/types/memory/store/types.d.ts.map +1 -1
- package/dist/types/memory-providers.d.ts +1 -1
- package/dist/types/memory-providers.d.ts.map +1 -1
- package/dist/types/security/index.d.ts +10 -1
- package/dist/types/security/index.d.ts.map +1 -1
- package/dist/types/tool-providers/index.d.ts +6 -2
- package/dist/types/tool-providers/index.d.ts.map +1 -1
- package/package.json +27 -1
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hosting/types — the two ports an agent needs to stand up and stay up.
|
|
3
|
+
*
|
|
4
|
+
* `AgentHost` is "something can call me". `SessionLifecycle` is "the
|
|
5
|
+
* conversation outlives the request". Both are deliberately written in the
|
|
6
|
+
* vocabulary every transport and every store already has — an input, a reply,
|
|
7
|
+
* a session id, a stored blob — and in nothing else.
|
|
8
|
+
*
|
|
9
|
+
* **The rule these types are written under:** no runtime, product or protocol
|
|
10
|
+
* gets a field, a name or an assumption here. A port shaped around one
|
|
11
|
+
* provider's request envelope stops being a port and becomes that provider's
|
|
12
|
+
* SDK with extra steps, and every later adapter pays for it. If a decision only
|
|
13
|
+
* makes sense for one place you might deploy, it belongs in the adapter for
|
|
14
|
+
* that place, not in this file. `nodeHost` is the first adapter and it does not
|
|
15
|
+
* get special treatment either: its paths, its status codes and its JSON body
|
|
16
|
+
* shape all live in `nodeHost.ts`, and nothing in this file knows they exist.
|
|
17
|
+
*
|
|
18
|
+
* Pattern: Ports & adapters (hexagonal). Role: the port side, exclusively.
|
|
19
|
+
*/
|
|
20
|
+
import type { Agent } from '../core/Agent.js';
|
|
21
|
+
import type { AgentRunCheckpoint } from '../core/runCheckpoint.js';
|
|
22
|
+
/**
|
|
23
|
+
* Something a host can do BEYOND the baseline of "accept a request, deliver one
|
|
24
|
+
* reply". Read it from {@link AgentHost.capabilities} and branch on it — never
|
|
25
|
+
* assume it, and never infer it from the adapter's name.
|
|
26
|
+
*
|
|
27
|
+
* The union starts at exactly what a shipped adapter can honour today. A name
|
|
28
|
+
* is added when an adapter can actually keep the promise, never in anticipation
|
|
29
|
+
* of a transport that does not exist yet: a capability nobody implements is a
|
|
30
|
+
* promise the library cannot keep, and pre-minting one for an imagined future
|
|
31
|
+
* transport would bake that transport's assumptions in before it arrives.
|
|
32
|
+
*/
|
|
33
|
+
export type HostCapability = 'streaming';
|
|
34
|
+
/**
|
|
35
|
+
* One inbound request, as the transport described it.
|
|
36
|
+
*/
|
|
37
|
+
export interface HostRequest {
|
|
38
|
+
/** What the caller is asking. */
|
|
39
|
+
readonly input: string;
|
|
40
|
+
/**
|
|
41
|
+
* The conversation this request CLAIMS to belong to — caller data, exactly as
|
|
42
|
+
* the transport declared it (a JSON field, a header, a path segment).
|
|
43
|
+
*
|
|
44
|
+
* It is **not identity** and must never be trusted as identity on its own:
|
|
45
|
+
* anyone who can reach the host can put any string here, including someone
|
|
46
|
+
* else's. Authenticate the caller by your own means, then check that the
|
|
47
|
+
* authenticated principal is allowed this session, before you serve it.
|
|
48
|
+
*/
|
|
49
|
+
readonly sessionId?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Transport headers with lower-cased names, as delivered. Present so a
|
|
52
|
+
* handler can map its own conventions (a correlation id, a tenant) without
|
|
53
|
+
* the port having to guess which ones matter.
|
|
54
|
+
*/
|
|
55
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
56
|
+
/** Aborted when the caller goes away. */
|
|
57
|
+
readonly signal?: AbortSignal;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The one reply a request gets. Exactly one of {@link HostReply.complete} or
|
|
61
|
+
* {@link HostReply.fail} ends it; a second call is ignored rather than allowed
|
|
62
|
+
* to corrupt the wire.
|
|
63
|
+
*/
|
|
64
|
+
export interface HostReply {
|
|
65
|
+
/** Deliver the final answer and end the reply. */
|
|
66
|
+
complete(output: string): void;
|
|
67
|
+
/**
|
|
68
|
+
* A piece of the answer, as it is produced.
|
|
69
|
+
*
|
|
70
|
+
* Optional on the TYPE so a minimal adapter need not implement it — every
|
|
71
|
+
* shipped adapter does. Whether the caller SEES the pieces as they arrive is
|
|
72
|
+
* the whole difference between hosts, and that is what `'streaming'` in
|
|
73
|
+
* {@link AgentHost.capabilities} reports. A host without it buffers what it is
|
|
74
|
+
* handed and the authoritative `complete(output)` is what the caller
|
|
75
|
+
* receives; the buffer is settled by the completion, never sent alongside it,
|
|
76
|
+
* because a chunk is a preview of the same text and delivering both would
|
|
77
|
+
* hand the caller the answer twice.
|
|
78
|
+
*
|
|
79
|
+
* Handler code is identical either way: emit freely, complete once.
|
|
80
|
+
*/
|
|
81
|
+
emit?(chunk: string): void;
|
|
82
|
+
/** End the reply with a failure. */
|
|
83
|
+
fail(error: Error): void;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* What you hand {@link AgentHost.serve}. Throwing is treated exactly like
|
|
87
|
+
* calling `reply.fail(err)` — a handler that throws is a failed request, never
|
|
88
|
+
* a hung one.
|
|
89
|
+
*/
|
|
90
|
+
export type HostHandler = (request: HostRequest, reply: HostReply) => void | Promise<void>;
|
|
91
|
+
/** A live host. */
|
|
92
|
+
export interface HostHandle {
|
|
93
|
+
/**
|
|
94
|
+
* Stop taking new requests, let the in-flight ones finish, then release the
|
|
95
|
+
* transport. Idempotent, so a shutdown hook and an explicit close can
|
|
96
|
+
* coexist. Requests arriving after it are refused with a
|
|
97
|
+
* {@link HostClosedError} naming the adapter.
|
|
98
|
+
*/
|
|
99
|
+
close(): Promise<void>;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The port: something that can carry requests to one handler and carry its
|
|
103
|
+
* replies back.
|
|
104
|
+
*/
|
|
105
|
+
export interface AgentHost {
|
|
106
|
+
/**
|
|
107
|
+
* Which adapter this is. Every refusal names it, so an error tells you WHO
|
|
108
|
+
* refused rather than leaving you to guess which layer you are looking at.
|
|
109
|
+
*/
|
|
110
|
+
readonly name: string;
|
|
111
|
+
/** What this adapter can do beyond the baseline. Feature-detect; never assume. */
|
|
112
|
+
readonly capabilities: readonly HostCapability[];
|
|
113
|
+
/** Start serving. Resolves once the host is actually live. */
|
|
114
|
+
serve(handler: HostHandler): Promise<HostHandle>;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* A conversation packed for storage.
|
|
118
|
+
*
|
|
119
|
+
* `format` names WHAT is inside, so a reader that does not know the shape
|
|
120
|
+
* refuses BY NAME instead of restoring a conversation it cannot actually read.
|
|
121
|
+
* Formats are ADDED, never redefined: an old runtime meeting a new format says
|
|
122
|
+
* so and stops, which is the only safe thing it can do with a payload it cannot
|
|
123
|
+
* interpret.
|
|
124
|
+
*
|
|
125
|
+
* `'conversation-v1'` stores a conversation and only a conversation. A run that
|
|
126
|
+
* paused mid-flow is a conversation PLUS an engine checkpoint, and this format
|
|
127
|
+
* has nowhere to put the second half — which is why `standingAgent` refuses to
|
|
128
|
+
* store a paused run rather than storing half of it. Carrying a pause would be
|
|
129
|
+
* a NEW format name in this same envelope, read by a runtime that knows it and
|
|
130
|
+
* refused by name everywhere else. That is what the version field is for.
|
|
131
|
+
*/
|
|
132
|
+
export interface CheckpointEnvelope {
|
|
133
|
+
/** Names the shape of `data`. Unknown values are refused, never guessed at. */
|
|
134
|
+
readonly format: 'conversation-v1';
|
|
135
|
+
/** The conversation itself — an `AgentRunCheckpoint` for `'conversation-v1'`. */
|
|
136
|
+
readonly data: AgentRunCheckpoint;
|
|
137
|
+
/** Wall-clock when it was packed. Diagnostic. */
|
|
138
|
+
readonly savedAt: number;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Why a session is being woken.
|
|
142
|
+
*
|
|
143
|
+
* One member, because one thing in this release can actually fire it: a request
|
|
144
|
+
* arrived for that session. Naming reasons nothing can produce would be an
|
|
145
|
+
* interface describing a system that does not exist.
|
|
146
|
+
*/
|
|
147
|
+
export type WakeReason = 'invoke';
|
|
148
|
+
/**
|
|
149
|
+
* The port: where a conversation lives between requests.
|
|
150
|
+
*
|
|
151
|
+
* Deliberately two required methods. Anything a real store also wants — a TTL,
|
|
152
|
+
* a scan, a delete — is that store's own API, not a demand this port makes of
|
|
153
|
+
* every store that will ever implement it.
|
|
154
|
+
*/
|
|
155
|
+
export interface SessionLifecycle {
|
|
156
|
+
/** The stored conversation, or `undefined` for a session that has none yet. */
|
|
157
|
+
hydrate(sessionId: string): Promise<CheckpointEnvelope | undefined>;
|
|
158
|
+
/** Store the conversation for this session. Last write wins. */
|
|
159
|
+
persist(sessionId: string, envelope: CheckpointEnvelope): Promise<void>;
|
|
160
|
+
/**
|
|
161
|
+
* Called once per served request, before `hydrate`, for stores that need to
|
|
162
|
+
* spin something up before they can answer. Errors from it fail the request —
|
|
163
|
+
* a store that could not wake cannot be read from either.
|
|
164
|
+
*/
|
|
165
|
+
onWake?(sessionId: string, reason: WakeReason): void | Promise<void>;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* What to do when a request arrives for a session that already has a run in
|
|
169
|
+
* flight.
|
|
170
|
+
*
|
|
171
|
+
* - `'reject'` (default) — refuse it, naming the run that is already going.
|
|
172
|
+
* A user who double-submits gets one answer and one refusal, not two runs
|
|
173
|
+
* racing to write the same conversation.
|
|
174
|
+
* - `'enqueue'` — queue it. It starts after the active run has persisted, so
|
|
175
|
+
* the second turn sees the first turn's stored state rather than the state
|
|
176
|
+
* it was about to overwrite.
|
|
177
|
+
*
|
|
178
|
+
* This governs the SAME session only. A request for a different session is
|
|
179
|
+
* never refused — there is nothing wrong with it; it simply waits its turn.
|
|
180
|
+
*/
|
|
181
|
+
export type ConcurrentInvokePolicy = 'reject' | 'enqueue';
|
|
182
|
+
/**
|
|
183
|
+
* Options for {@link standingAgent}.
|
|
184
|
+
*
|
|
185
|
+
* Generic in the host's own handle type so composing does not cost you what
|
|
186
|
+
* the adapter told you. `nodeHost` hands back the URL it actually bound —
|
|
187
|
+
* which is the only way to find out when you asked for port `0` — and passing
|
|
188
|
+
* it through `standingAgent` keeps that, without the port having to know that
|
|
189
|
+
* "a URL" is a thing some adapters have.
|
|
190
|
+
*/
|
|
191
|
+
export interface StandingAgentOptions<TH extends HostHandle = HostHandle> {
|
|
192
|
+
/**
|
|
193
|
+
* The agent that answers. ONE instance, shared by every session — which is
|
|
194
|
+
* why the composer runs one request at a time (see
|
|
195
|
+
* {@link ConcurrentInvokePolicy}).
|
|
196
|
+
*/
|
|
197
|
+
readonly agent: Agent;
|
|
198
|
+
/** Where conversations live between requests. */
|
|
199
|
+
readonly sessions: SessionLifecycle;
|
|
200
|
+
/** What carries requests in. */
|
|
201
|
+
readonly host: AgentHost & {
|
|
202
|
+
serve(handler: HostHandler): Promise<TH>;
|
|
203
|
+
};
|
|
204
|
+
/** Default `'reject'`. See {@link ConcurrentInvokePolicy}. */
|
|
205
|
+
readonly onConcurrentInvoke?: ConcurrentInvokePolicy;
|
|
206
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hosting/types — the two ports an agent needs to stand up and stay up.
|
|
3
|
+
*
|
|
4
|
+
* `AgentHost` is "something can call me". `SessionLifecycle` is "the
|
|
5
|
+
* conversation outlives the request". Both are deliberately written in the
|
|
6
|
+
* vocabulary every transport and every store already has — an input, a reply,
|
|
7
|
+
* a session id, a stored blob — and in nothing else.
|
|
8
|
+
*
|
|
9
|
+
* **The rule these types are written under:** no runtime, product or protocol
|
|
10
|
+
* gets a field, a name or an assumption here. A port shaped around one
|
|
11
|
+
* provider's request envelope stops being a port and becomes that provider's
|
|
12
|
+
* SDK with extra steps, and every later adapter pays for it. If a decision only
|
|
13
|
+
* makes sense for one place you might deploy, it belongs in the adapter for
|
|
14
|
+
* that place, not in this file. `nodeHost` is the first adapter and it does not
|
|
15
|
+
* get special treatment either: its paths, its status codes and its JSON body
|
|
16
|
+
* shape all live in `nodeHost.ts`, and nothing in this file knows they exist.
|
|
17
|
+
*
|
|
18
|
+
* Pattern: Ports & adapters (hexagonal). Role: the port side, exclusively.
|
|
19
|
+
*/
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/hosting/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agentfootprint/hosting-providers — adapters for the two hosting ports.
|
|
3
|
+
*
|
|
4
|
+
* agentfootprint/hosting ← the PORTS (`AgentHost`, `SessionLifecycle`),
|
|
5
|
+
* the composer, and the local adapters
|
|
6
|
+
* agentfootprint/hosting-providers ← adapters for somewhere you deploy (this file)
|
|
7
|
+
*
|
|
8
|
+
* Same split as `agentfootprint/memory` ↔ `agentfootprint/memory-providers`, and
|
|
9
|
+
* for the same reason. `agentfootprint/hosting` promises that not one field,
|
|
10
|
+
* name or assumption in it comes from any cloud, and a test greps its sources
|
|
11
|
+
* for vendor names to keep that honest — including the barrel. So the cloud
|
|
12
|
+
* adapters get their own subpath, and the port subpath stays a file you can
|
|
13
|
+
* read without learning anybody's product names.
|
|
14
|
+
*
|
|
15
|
+
* One subpath that grows: an adapter for the next runtime lands here beside
|
|
16
|
+
* this one rather than minting `agentfootprint/hosting-<vendor>`.
|
|
17
|
+
*
|
|
18
|
+
* ── What ships here ──────────────────────────────────────────────────────────
|
|
19
|
+
* • `agentCoreRuntimeHost({ port?, hostname?, busy? })` — an `AgentHost` for
|
|
20
|
+
* AWS Bedrock AgentCore Runtime's container contract: `POST /invocations`,
|
|
21
|
+
* `GET /ping`, port 8080, and the conversation id read from the
|
|
22
|
+
* `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` header.
|
|
23
|
+
* • `agentCoreSessions({ store })` — a `SessionLifecycle` whose checkpoint
|
|
24
|
+
* home you choose at construction: a JSON file in the runtime's own session
|
|
25
|
+
* storage (survives a stop/resume, needs no SDK), or one AgentCore Memory
|
|
26
|
+
* event per persist (outlives the session).
|
|
27
|
+
*
|
|
28
|
+
* ── How much of this is verified ─────────────────────────────────────────────
|
|
29
|
+
* `agentCoreRuntimeHost` is plain HTTP with no SDK on its path, and it passes
|
|
30
|
+
* the same host conformance suite as `nodeHost` over a real socket — that is
|
|
31
|
+
* real verification, not a mapping asserted in prose.
|
|
32
|
+
*
|
|
33
|
+
* Everything that talks to AWS — `agentCoreSessions({ store: 'memory' })` — is
|
|
34
|
+
* **contract-mapped and injection-tested**: the SDK calls are exercised through
|
|
35
|
+
* the `_client` seam and no test pretends to have reached AWS. Real-cloud
|
|
36
|
+
* verification lands with a field deployment.
|
|
37
|
+
*
|
|
38
|
+
* @example An agent in an AgentCore Runtime container
|
|
39
|
+
* import { standingAgent } from 'agentfootprint/hosting';
|
|
40
|
+
* import { agentCoreRuntimeHost, agentCoreSessions } from 'agentfootprint/hosting-providers';
|
|
41
|
+
*
|
|
42
|
+
* const handle = await standingAgent({
|
|
43
|
+
* agent,
|
|
44
|
+
* host: agentCoreRuntimeHost(),
|
|
45
|
+
* sessions: agentCoreSessions({ store: 'session-storage' }),
|
|
46
|
+
* });
|
|
47
|
+
* process.on('SIGTERM', () => void handle.close());
|
|
48
|
+
*/
|
|
49
|
+
export { agentCoreRuntimeHost, agentCoreRuntimeWire, agentCoreSessions, DEFAULT_SESSION_STORAGE_PATH, } from './adapters/hosting/agentcore.js';
|
|
50
|
+
export type { AgentCoreRuntimeHostOptions, AgentCoreSessionsOptions, AgentCoreSessionStore, AgentCoreFileSessionsOptions, AgentCoreMemorySessionsOptions, AgentCoreSessionClientLike, AgentCoreSessionEvent, BedrockAgentCoreSessionSdkModule, } from './adapters/hosting/agentcore.js';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agentfootprint/hosting-providers — adapters for the two hosting ports.
|
|
3
|
+
*
|
|
4
|
+
* agentfootprint/hosting ← the PORTS (`AgentHost`, `SessionLifecycle`),
|
|
5
|
+
* the composer, and the local adapters
|
|
6
|
+
* agentfootprint/hosting-providers ← adapters for somewhere you deploy (this file)
|
|
7
|
+
*
|
|
8
|
+
* Same split as `agentfootprint/memory` ↔ `agentfootprint/memory-providers`, and
|
|
9
|
+
* for the same reason. `agentfootprint/hosting` promises that not one field,
|
|
10
|
+
* name or assumption in it comes from any cloud, and a test greps its sources
|
|
11
|
+
* for vendor names to keep that honest — including the barrel. So the cloud
|
|
12
|
+
* adapters get their own subpath, and the port subpath stays a file you can
|
|
13
|
+
* read without learning anybody's product names.
|
|
14
|
+
*
|
|
15
|
+
* One subpath that grows: an adapter for the next runtime lands here beside
|
|
16
|
+
* this one rather than minting `agentfootprint/hosting-<vendor>`.
|
|
17
|
+
*
|
|
18
|
+
* ── What ships here ──────────────────────────────────────────────────────────
|
|
19
|
+
* • `agentCoreRuntimeHost({ port?, hostname?, busy? })` — an `AgentHost` for
|
|
20
|
+
* AWS Bedrock AgentCore Runtime's container contract: `POST /invocations`,
|
|
21
|
+
* `GET /ping`, port 8080, and the conversation id read from the
|
|
22
|
+
* `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` header.
|
|
23
|
+
* • `agentCoreSessions({ store })` — a `SessionLifecycle` whose checkpoint
|
|
24
|
+
* home you choose at construction: a JSON file in the runtime's own session
|
|
25
|
+
* storage (survives a stop/resume, needs no SDK), or one AgentCore Memory
|
|
26
|
+
* event per persist (outlives the session).
|
|
27
|
+
*
|
|
28
|
+
* ── How much of this is verified ─────────────────────────────────────────────
|
|
29
|
+
* `agentCoreRuntimeHost` is plain HTTP with no SDK on its path, and it passes
|
|
30
|
+
* the same host conformance suite as `nodeHost` over a real socket — that is
|
|
31
|
+
* real verification, not a mapping asserted in prose.
|
|
32
|
+
*
|
|
33
|
+
* Everything that talks to AWS — `agentCoreSessions({ store: 'memory' })` — is
|
|
34
|
+
* **contract-mapped and injection-tested**: the SDK calls are exercised through
|
|
35
|
+
* the `_client` seam and no test pretends to have reached AWS. Real-cloud
|
|
36
|
+
* verification lands with a field deployment.
|
|
37
|
+
*
|
|
38
|
+
* @example An agent in an AgentCore Runtime container
|
|
39
|
+
* import { standingAgent } from 'agentfootprint/hosting';
|
|
40
|
+
* import { agentCoreRuntimeHost, agentCoreSessions } from 'agentfootprint/hosting-providers';
|
|
41
|
+
*
|
|
42
|
+
* const handle = await standingAgent({
|
|
43
|
+
* agent,
|
|
44
|
+
* host: agentCoreRuntimeHost(),
|
|
45
|
+
* sessions: agentCoreSessions({ store: 'session-storage' }),
|
|
46
|
+
* });
|
|
47
|
+
* process.on('SIGTERM', () => void handle.close());
|
|
48
|
+
*/
|
|
49
|
+
export { agentCoreRuntimeHost, agentCoreRuntimeWire, agentCoreSessions, DEFAULT_SESSION_STORAGE_PATH, } from './adapters/hosting/agentcore.js';
|
|
50
|
+
//# sourceMappingURL=hosting-providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hosting-providers.js","sourceRoot":"","sources":["../../src/hosting-providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,4BAA4B,GAC7B,MAAM,iCAAiC,CAAC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gatewayTransport — an MCP transport whose auth headers are vended per
|
|
3
|
+
* request, not per connection.
|
|
4
|
+
*
|
|
5
|
+
* const gateway = await mcpClient({
|
|
6
|
+
* name: 'gateway',
|
|
7
|
+
* transport: gatewayTransport({ url, credentials, service: 'gateway' }),
|
|
8
|
+
* });
|
|
9
|
+
*
|
|
10
|
+
* ── Why this exists ──────────────────────────────────────────────────────────
|
|
11
|
+
* The `http` transport takes headers once and reuses them for the life of the
|
|
12
|
+
* connection. That is right for a static API key and wrong for anything with an
|
|
13
|
+
* expiry: a standing agent outlives its bearer token, and the failure mode is a
|
|
14
|
+
* burst of 401s an hour into a session that worked perfectly when you tested
|
|
15
|
+
* it. Managed gateways — AgentCore's among them — hand out exactly that kind of
|
|
16
|
+
* token, so "get a fresh one before each request" is not an optimisation, it is
|
|
17
|
+
* the only way the connection stays up.
|
|
18
|
+
*
|
|
19
|
+
* Nothing here is vendor-specific. It is the {@link CredentialProvider} port on
|
|
20
|
+
* one side and Streamable HTTP on the other; who vends is your choice.
|
|
21
|
+
*
|
|
22
|
+
* ── The secrecy invariant, and how it is kept ────────────────────────────────
|
|
23
|
+
* **A vended token is used once and dropped.** It is not cached between
|
|
24
|
+
* requests, not stored on the transport object, not put in an event payload,
|
|
25
|
+
* not written to a log, and not included in any error this module throws —
|
|
26
|
+
* including the errors it throws WHILE holding one. A test asserts that: a
|
|
27
|
+
* hostile logger subscribed to everything the library can say never sees the
|
|
28
|
+
* header value.
|
|
29
|
+
*
|
|
30
|
+
* That is why the vend happens inside `fetch` rather than at construction. A
|
|
31
|
+
* token resolved at construction has to live somewhere for the connection to
|
|
32
|
+
* use it later, and "somewhere" is what leaks.
|
|
33
|
+
*
|
|
34
|
+
* Pattern: Adapter + Decorator over `fetch`. Role: Layer-3 tool integration.
|
|
35
|
+
*/
|
|
36
|
+
import { type CredentialProvider } from '../../identity/types.js';
|
|
37
|
+
import type { McpGatewayTransport } from './types.js';
|
|
38
|
+
/** Options for {@link gatewayTransport}. */
|
|
39
|
+
export interface GatewayTransportOptions {
|
|
40
|
+
/** The MCP endpoint URL. */
|
|
41
|
+
readonly url: string;
|
|
42
|
+
/** Who vends the token — any `CredentialProvider`. */
|
|
43
|
+
readonly credentials: CredentialProvider;
|
|
44
|
+
/**
|
|
45
|
+
* The downstream service id the provider understands. Default `'gateway'`.
|
|
46
|
+
* `staticTokens({ gateway: '...' })` and `agentCoreIdentity()` both key on it.
|
|
47
|
+
*/
|
|
48
|
+
readonly service?: string;
|
|
49
|
+
/** OAuth scopes to request, when the provider uses them. */
|
|
50
|
+
readonly scopes?: readonly string[];
|
|
51
|
+
/** `machine` (2-legged, the default) or `user` (3-legged, on behalf of a user). */
|
|
52
|
+
readonly mode?: 'machine' | 'user';
|
|
53
|
+
/**
|
|
54
|
+
* Extra headers sent alongside the vended ones. Applied FIRST, so a vended
|
|
55
|
+
* auth header always wins. **Put no secrets here** — these are held for the
|
|
56
|
+
* life of the transport, which is what the vended ones exist to avoid.
|
|
57
|
+
*/
|
|
58
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Describe an MCP connection whose auth headers are vended per request.
|
|
62
|
+
*
|
|
63
|
+
* @example A managed gateway with a token vault behind it
|
|
64
|
+
* import { agentCoreIdentity } from 'agentfootprint/identity';
|
|
65
|
+
* import { gatewayTransport, mcpClient } from 'agentfootprint/tool-providers';
|
|
66
|
+
*
|
|
67
|
+
* const gateway = await mcpClient({
|
|
68
|
+
* name: 'gateway',
|
|
69
|
+
* transport: gatewayTransport({
|
|
70
|
+
* url: process.env.GATEWAY_MCP_URL!,
|
|
71
|
+
* credentials: agentCoreIdentity({ region: 'us-west-2' }),
|
|
72
|
+
* service: 'gateway',
|
|
73
|
+
* }),
|
|
74
|
+
* });
|
|
75
|
+
* const agent = Agent.create({ provider, model }).tools(await gateway.tools()).build();
|
|
76
|
+
*/
|
|
77
|
+
export declare function gatewayTransport(options: GatewayTransportOptions): McpGatewayTransport;
|
|
78
|
+
/**
|
|
79
|
+
* Raised when the provider needs a human to authorize before it can vend.
|
|
80
|
+
*
|
|
81
|
+
* The URL is carried so the caller can surface it (pause the run, show a
|
|
82
|
+
* link); **the token is not, because there is not one yet** — that is the whole
|
|
83
|
+
* meaning of this error.
|
|
84
|
+
*/
|
|
85
|
+
export declare class GatewayAuthorizationRequiredError extends Error {
|
|
86
|
+
readonly code: "ERR_GATEWAY_AUTHORIZATION_REQUIRED";
|
|
87
|
+
/** Send the user here, then retry. */
|
|
88
|
+
readonly authorizationUrl: string;
|
|
89
|
+
constructor(service: string, authorizationUrl: string);
|
|
90
|
+
}
|
|
91
|
+
/** Just the part of `fetch` this module needs, so it is trivially injectable in tests. */
|
|
92
|
+
export type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
93
|
+
/**
|
|
94
|
+
* Build the `fetch` the MCP SDK will use: one that vends a credential, applies
|
|
95
|
+
* it to this one request, and keeps nothing.
|
|
96
|
+
*
|
|
97
|
+
* Exported for tests — the secrecy invariant is asserted against this function
|
|
98
|
+
* directly, because that is where a leak would happen and driving a whole SDK
|
|
99
|
+
* to reach it would test less while looking like it tested more.
|
|
100
|
+
*
|
|
101
|
+
* @internal
|
|
102
|
+
*/
|
|
103
|
+
export declare function createVendingFetch(transport: McpGatewayTransport, baseFetch?: FetchLike): FetchLike;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gatewayTransport — an MCP transport whose auth headers are vended per
|
|
3
|
+
* request, not per connection.
|
|
4
|
+
*
|
|
5
|
+
* const gateway = await mcpClient({
|
|
6
|
+
* name: 'gateway',
|
|
7
|
+
* transport: gatewayTransport({ url, credentials, service: 'gateway' }),
|
|
8
|
+
* });
|
|
9
|
+
*
|
|
10
|
+
* ── Why this exists ──────────────────────────────────────────────────────────
|
|
11
|
+
* The `http` transport takes headers once and reuses them for the life of the
|
|
12
|
+
* connection. That is right for a static API key and wrong for anything with an
|
|
13
|
+
* expiry: a standing agent outlives its bearer token, and the failure mode is a
|
|
14
|
+
* burst of 401s an hour into a session that worked perfectly when you tested
|
|
15
|
+
* it. Managed gateways — AgentCore's among them — hand out exactly that kind of
|
|
16
|
+
* token, so "get a fresh one before each request" is not an optimisation, it is
|
|
17
|
+
* the only way the connection stays up.
|
|
18
|
+
*
|
|
19
|
+
* Nothing here is vendor-specific. It is the {@link CredentialProvider} port on
|
|
20
|
+
* one side and Streamable HTTP on the other; who vends is your choice.
|
|
21
|
+
*
|
|
22
|
+
* ── The secrecy invariant, and how it is kept ────────────────────────────────
|
|
23
|
+
* **A vended token is used once and dropped.** It is not cached between
|
|
24
|
+
* requests, not stored on the transport object, not put in an event payload,
|
|
25
|
+
* not written to a log, and not included in any error this module throws —
|
|
26
|
+
* including the errors it throws WHILE holding one. A test asserts that: a
|
|
27
|
+
* hostile logger subscribed to everything the library can say never sees the
|
|
28
|
+
* header value.
|
|
29
|
+
*
|
|
30
|
+
* That is why the vend happens inside `fetch` rather than at construction. A
|
|
31
|
+
* token resolved at construction has to live somewhere for the connection to
|
|
32
|
+
* use it later, and "somewhere" is what leaks.
|
|
33
|
+
*
|
|
34
|
+
* Pattern: Adapter + Decorator over `fetch`. Role: Layer-3 tool integration.
|
|
35
|
+
*/
|
|
36
|
+
import { isCredentialIssued } from '../../identity/types.js';
|
|
37
|
+
const DEFAULT_SERVICE = 'gateway';
|
|
38
|
+
/**
|
|
39
|
+
* Describe an MCP connection whose auth headers are vended per request.
|
|
40
|
+
*
|
|
41
|
+
* @example A managed gateway with a token vault behind it
|
|
42
|
+
* import { agentCoreIdentity } from 'agentfootprint/identity';
|
|
43
|
+
* import { gatewayTransport, mcpClient } from 'agentfootprint/tool-providers';
|
|
44
|
+
*
|
|
45
|
+
* const gateway = await mcpClient({
|
|
46
|
+
* name: 'gateway',
|
|
47
|
+
* transport: gatewayTransport({
|
|
48
|
+
* url: process.env.GATEWAY_MCP_URL!,
|
|
49
|
+
* credentials: agentCoreIdentity({ region: 'us-west-2' }),
|
|
50
|
+
* service: 'gateway',
|
|
51
|
+
* }),
|
|
52
|
+
* });
|
|
53
|
+
* const agent = Agent.create({ provider, model }).tools(await gateway.tools()).build();
|
|
54
|
+
*/
|
|
55
|
+
export function gatewayTransport(options) {
|
|
56
|
+
if (!options.url)
|
|
57
|
+
throw new Error(`gatewayTransport requires 'url'.`);
|
|
58
|
+
if (!options.credentials)
|
|
59
|
+
throw new Error(`gatewayTransport requires 'credentials'.`);
|
|
60
|
+
return {
|
|
61
|
+
transport: 'gateway',
|
|
62
|
+
url: options.url,
|
|
63
|
+
credentials: options.credentials,
|
|
64
|
+
service: options.service ?? DEFAULT_SERVICE,
|
|
65
|
+
...(options.scopes !== undefined && { scopes: options.scopes }),
|
|
66
|
+
...(options.mode !== undefined && { mode: options.mode }),
|
|
67
|
+
...(options.headers !== undefined && { headers: options.headers }),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Raised when the provider needs a human to authorize before it can vend.
|
|
72
|
+
*
|
|
73
|
+
* The URL is carried so the caller can surface it (pause the run, show a
|
|
74
|
+
* link); **the token is not, because there is not one yet** — that is the whole
|
|
75
|
+
* meaning of this error.
|
|
76
|
+
*/
|
|
77
|
+
export class GatewayAuthorizationRequiredError extends Error {
|
|
78
|
+
code = 'ERR_GATEWAY_AUTHORIZATION_REQUIRED';
|
|
79
|
+
/** Send the user here, then retry. */
|
|
80
|
+
authorizationUrl;
|
|
81
|
+
constructor(service, authorizationUrl) {
|
|
82
|
+
super(`[mcp] the credential provider needs a person to authorize '${service}' before it can ` +
|
|
83
|
+
`vend a token for this gateway. A transport cannot run a consent flow mid-request — ` +
|
|
84
|
+
`surface the authorization URL to the user (pause the run), then call the tool again ` +
|
|
85
|
+
`once they have authorized.`);
|
|
86
|
+
this.name = 'GatewayAuthorizationRequiredError';
|
|
87
|
+
this.authorizationUrl = authorizationUrl;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Build the `fetch` the MCP SDK will use: one that vends a credential, applies
|
|
92
|
+
* it to this one request, and keeps nothing.
|
|
93
|
+
*
|
|
94
|
+
* Exported for tests — the secrecy invariant is asserted against this function
|
|
95
|
+
* directly, because that is where a leak would happen and driving a whole SDK
|
|
96
|
+
* to reach it would test less while looking like it tested more.
|
|
97
|
+
*
|
|
98
|
+
* @internal
|
|
99
|
+
*/
|
|
100
|
+
export function createVendingFetch(transport, baseFetch = globalThis.fetch.bind(globalThis)) {
|
|
101
|
+
return async (input, init) => {
|
|
102
|
+
const result = await transport.credentials.getCredential({
|
|
103
|
+
service: transport.service,
|
|
104
|
+
...(transport.scopes !== undefined && { scopes: transport.scopes }),
|
|
105
|
+
...(transport.mode !== undefined && { mode: transport.mode }),
|
|
106
|
+
});
|
|
107
|
+
if (!isCredentialIssued(result)) {
|
|
108
|
+
throw new GatewayAuthorizationRequiredError(transport.service, result.authorizationUrl);
|
|
109
|
+
}
|
|
110
|
+
// Static headers first, vended ones last: a vended auth header always wins,
|
|
111
|
+
// so a stale static one can never quietly shadow the live credential.
|
|
112
|
+
const headers = new Headers(init?.headers ?? {});
|
|
113
|
+
for (const [key, value] of Object.entries(transport.headers ?? {}))
|
|
114
|
+
headers.set(key, value);
|
|
115
|
+
for (const [key, value] of Object.entries(result.credential.toHeaders())) {
|
|
116
|
+
headers.set(key, value);
|
|
117
|
+
}
|
|
118
|
+
// The vended value exists only inside this call, on this Headers object,
|
|
119
|
+
// for the duration of this request. Nothing above closes over it.
|
|
120
|
+
return baseFetch(input, { ...init, headers });
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=gatewayTransport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gatewayTransport.js","sourceRoot":"","sources":["../../../../src/lib/mcp/gatewayTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAE,kBAAkB,EAA2B,MAAM,yBAAyB,CAAC;AA0BtF,MAAM,eAAe,GAAG,SAAS,CAAC;AAElC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAgC;IAC/D,IAAI,CAAC,OAAO,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtE,IAAI,CAAC,OAAO,CAAC,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IACtF,OAAO;QACL,SAAS,EAAE,SAAS;QACpB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,eAAe;QAC3C,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;QAC/D,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;QACzD,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;KACnE,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,OAAO,iCAAkC,SAAQ,KAAK;IACjD,IAAI,GAAG,oCAA6C,CAAC;IAC9D,sCAAsC;IAC7B,gBAAgB,CAAS;IAElC,YAAY,OAAe,EAAE,gBAAwB;QACnD,KAAK,CACH,8DAA8D,OAAO,kBAAkB;YACrF,qFAAqF;YACrF,sFAAsF;YACtF,4BAA4B,CAC/B,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,mCAAmC,CAAC;QAChD,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;IAC3C,CAAC;CACF;AAKD;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAChC,SAA8B,EAC9B,YAAuB,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;IAExD,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,aAAa,CAAC;YACvD,OAAO,EAAE,SAAS,CAAC,OAAO;YAC1B,GAAG,CAAC,SAAS,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;YACnE,GAAG,CAAC,SAAS,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC;SAC9D,CAAC,CAAC;QACH,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,iCAAiC,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC;QAC1F,CAAC;QAED,4EAA4E;QAC5E,sEAAsE;QACtE,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC;QACjD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,IAAI,EAAE,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC5F,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC;YACzE,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC1B,CAAC;QAED,yEAAyE;QACzE,kEAAkE;QAClE,OAAO,SAAS,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IAChD,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -7,4 +7,5 @@
|
|
|
7
7
|
export { mcpClient } from './mcpClient.js';
|
|
8
8
|
export { mcpServe } from './mcpServe.js';
|
|
9
9
|
export { mockMcpClient, type MockMcpClientOptions, type MockMcpTool } from './mockMcpClient.js';
|
|
10
|
-
export
|
|
10
|
+
export { gatewayTransport, GatewayAuthorizationRequiredError, type GatewayTransportOptions, } from './gatewayTransport.js';
|
|
11
|
+
export type { McpClient, McpClientOptions, McpGatewayTransport, McpHttpTransport, McpStdioTransport, McpTransport, McpSdkClient, McpServeOptions, McpServeHandle, McpServeTransport, McpHttpServeTransport, McpStdioServeTransport, McpSdkServer, } from './types.js';
|
|
@@ -7,4 +7,5 @@
|
|
|
7
7
|
export { mcpClient } from './mcpClient.js';
|
|
8
8
|
export { mcpServe } from './mcpServe.js';
|
|
9
9
|
export { mockMcpClient } from './mockMcpClient.js';
|
|
10
|
+
export { gatewayTransport, GatewayAuthorizationRequiredError, } from './gatewayTransport.js';
|
|
10
11
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/lib/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAA+C,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/lib/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAA+C,MAAM,oBAAoB,CAAC;AAChG,OAAO,EACL,gBAAgB,EAChB,iCAAiC,GAElC,MAAM,uBAAuB,CAAC"}
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
* loads only when a consumer actually constructs a client. Tests
|
|
34
34
|
* inject `_client` and skip the import path entirely.
|
|
35
35
|
*/
|
|
36
|
+
import { createVendingFetch } from './gatewayTransport.js';
|
|
36
37
|
import { lazyRequire } from '../lazyRequire.js';
|
|
37
38
|
// Version-less identity. The MCP `clientInfo` field is informational
|
|
38
39
|
// (server logs it); a hardcoded number drifts every release. Consumers
|
|
@@ -131,15 +132,22 @@ async function buildTransport(t) {
|
|
|
131
132
|
...(t.cwd !== undefined && { cwd: t.cwd }),
|
|
132
133
|
});
|
|
133
134
|
}
|
|
134
|
-
// http
|
|
135
|
+
// http + gateway transports both ride Streamable HTTP. They differ only in
|
|
136
|
+
// WHEN the auth headers are decided: `http` fixes them at construction,
|
|
137
|
+
// `gateway` vends them inside every request (see gatewayTransport.ts).
|
|
135
138
|
let httpMod;
|
|
136
139
|
try {
|
|
137
140
|
httpMod = lazyRequire('@modelcontextprotocol/sdk/client/streamableHttp.js');
|
|
138
141
|
}
|
|
139
142
|
catch {
|
|
140
|
-
throw new Error(
|
|
143
|
+
throw new Error(`mcpClient(${t.transport}) requires @modelcontextprotocol/sdk/client/streamableHttp.js — ` +
|
|
141
144
|
'check that @modelcontextprotocol/sdk is installed at the latest version.');
|
|
142
145
|
}
|
|
146
|
+
if (t.transport === 'gateway') {
|
|
147
|
+
return new httpMod.StreamableHTTPClientTransport(new URL(t.url), {
|
|
148
|
+
fetch: createVendingFetch(t),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
143
151
|
return new httpMod.StreamableHTTPClientTransport(new URL(t.url), {
|
|
144
152
|
...(t.headers && { requestInit: { headers: { ...t.headers } } }),
|
|
145
153
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcpClient.js","sourceRoot":"","sources":["../../../../src/lib/mcp/mcpClient.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"mcpClient.js","sourceRoot":"","sources":["../../../../src/lib/mcp/mcpClient.ts"],"names":[],"mappings":"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"}
|