@executor-js/plugin-mcp 0.0.1 → 0.0.2
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 +19 -15
- package/dist/api/group.d.ts +113 -144
- package/dist/api/handlers.d.ts +2 -2
- package/dist/chunk-DJANY5EU.js +1325 -0
- package/dist/chunk-DJANY5EU.js.map +1 -0
- package/dist/core.js +8 -52
- package/dist/core.js.map +1 -1
- package/dist/index.js +2 -5
- package/dist/index.js.map +1 -1
- package/dist/promise.d.ts +2 -6
- package/dist/react/AddMcpSource.d.ts +2 -0
- package/dist/react/McpSignInButton.d.ts +3 -0
- package/dist/react/atoms.d.ts +153 -0
- package/dist/react/client.d.ts +437 -3
- package/dist/react/index.d.ts +3 -2
- package/dist/react/source-plugin.d.ts +13 -1
- package/dist/sdk/binding-store.d.ts +79 -25
- package/dist/sdk/connection-pool.test.d.ts +1 -0
- package/dist/sdk/connection.d.ts +3 -1
- package/dist/sdk/cross-user-isolation.test.d.ts +1 -0
- package/dist/sdk/errors.d.ts +15 -23
- package/dist/sdk/index.d.ts +3 -3
- package/dist/sdk/invoke.d.ts +18 -17
- package/dist/sdk/manifest.d.ts +1 -0
- package/dist/sdk/per-user-auth-isolation.test.d.ts +1 -0
- package/dist/sdk/plugin.d.ts +108 -43
- package/dist/sdk/probe-shape.d.ts +39 -0
- package/dist/sdk/probe-shape.test.d.ts +1 -0
- package/dist/sdk/stdio-connector.d.ts +8 -0
- package/dist/sdk/stored-source.d.ts +31 -105
- package/dist/sdk/types.d.ts +77 -93
- package/dist/stdio-connector-KNHLETKM.js +12 -0
- package/dist/stdio-connector-KNHLETKM.js.map +1 -0
- package/package.json +11 -21
- package/dist/chunk-X3JTTDWJ.js +0 -1255
- package/dist/chunk-X3JTTDWJ.js.map +0 -1
- package/dist/react/McpSourceSummary.d.ts +0 -3
- package/dist/sdk/config-file-store.d.ts +0 -10
- package/dist/sdk/oauth.d.ts +0 -40
package/dist/chunk-X3JTTDWJ.js
DELETED
|
@@ -1,1255 +0,0 @@
|
|
|
1
|
-
// src/sdk/binding-store.ts
|
|
2
|
-
import { Effect as Effect2, Schema as Schema5 } from "effect";
|
|
3
|
-
import { makeInMemoryScopedKv, scopeKv } from "@executor-js/sdk";
|
|
4
|
-
|
|
5
|
-
// src/sdk/types.ts
|
|
6
|
-
import { Schema } from "effect";
|
|
7
|
-
var McpRemoteTransport = Schema.Literal("streamable-http", "sse", "auto");
|
|
8
|
-
var McpTransport = Schema.Literal("streamable-http", "sse", "stdio", "auto");
|
|
9
|
-
var McpConnectionAuth = Schema.Union(
|
|
10
|
-
Schema.Struct({ kind: Schema.Literal("none") }),
|
|
11
|
-
Schema.Struct({
|
|
12
|
-
kind: Schema.Literal("header"),
|
|
13
|
-
headerName: Schema.String,
|
|
14
|
-
secretId: Schema.String,
|
|
15
|
-
prefix: Schema.optional(Schema.String)
|
|
16
|
-
}),
|
|
17
|
-
Schema.Struct({
|
|
18
|
-
kind: Schema.Literal("oauth2"),
|
|
19
|
-
accessTokenSecretId: Schema.String,
|
|
20
|
-
refreshTokenSecretId: Schema.NullOr(Schema.String),
|
|
21
|
-
tokenType: Schema.optionalWith(Schema.String, { default: () => "Bearer" }),
|
|
22
|
-
expiresAt: Schema.NullOr(Schema.Number),
|
|
23
|
-
scope: Schema.NullOr(Schema.String)
|
|
24
|
-
})
|
|
25
|
-
);
|
|
26
|
-
var StringMap = Schema.Record({ key: Schema.String, value: Schema.String });
|
|
27
|
-
var McpRemoteSourceData = Schema.Struct({
|
|
28
|
-
transport: Schema.Literal("remote"),
|
|
29
|
-
/** The MCP server endpoint URL */
|
|
30
|
-
endpoint: Schema.String,
|
|
31
|
-
/** Transport preference for this remote source */
|
|
32
|
-
remoteTransport: Schema.optionalWith(McpRemoteTransport, { default: () => "auto" }),
|
|
33
|
-
/** Extra query params appended to the endpoint URL */
|
|
34
|
-
queryParams: Schema.optional(StringMap),
|
|
35
|
-
/** Extra headers sent on every request */
|
|
36
|
-
headers: Schema.optional(StringMap),
|
|
37
|
-
/** Auth configuration */
|
|
38
|
-
auth: McpConnectionAuth
|
|
39
|
-
});
|
|
40
|
-
var McpStdioSourceData = Schema.Struct({
|
|
41
|
-
transport: Schema.Literal("stdio"),
|
|
42
|
-
/** The command to run */
|
|
43
|
-
command: Schema.String,
|
|
44
|
-
/** Arguments to the command */
|
|
45
|
-
args: Schema.optional(Schema.Array(Schema.String)),
|
|
46
|
-
/** Environment variables */
|
|
47
|
-
env: Schema.optional(StringMap),
|
|
48
|
-
/** Working directory */
|
|
49
|
-
cwd: Schema.optional(Schema.String)
|
|
50
|
-
});
|
|
51
|
-
var McpStoredSourceData = Schema.Union(McpRemoteSourceData, McpStdioSourceData);
|
|
52
|
-
var McpToolBinding = class extends Schema.Class("McpToolBinding")({
|
|
53
|
-
toolId: Schema.String,
|
|
54
|
-
toolName: Schema.String,
|
|
55
|
-
description: Schema.NullOr(Schema.String),
|
|
56
|
-
inputSchema: Schema.optional(Schema.Unknown),
|
|
57
|
-
outputSchema: Schema.optional(Schema.Unknown)
|
|
58
|
-
}) {
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// src/sdk/oauth.ts
|
|
62
|
-
import {
|
|
63
|
-
auth
|
|
64
|
-
} from "@modelcontextprotocol/sdk/client/auth.js";
|
|
65
|
-
import { Effect, Schema as Schema3 } from "effect";
|
|
66
|
-
|
|
67
|
-
// src/sdk/errors.ts
|
|
68
|
-
import { Schema as Schema2 } from "effect";
|
|
69
|
-
var McpConnectionError = class extends Schema2.TaggedError()(
|
|
70
|
-
"McpConnectionError",
|
|
71
|
-
{
|
|
72
|
-
transport: Schema2.String,
|
|
73
|
-
message: Schema2.String
|
|
74
|
-
}
|
|
75
|
-
) {
|
|
76
|
-
};
|
|
77
|
-
var McpToolDiscoveryError = class extends Schema2.TaggedError()(
|
|
78
|
-
"McpToolDiscoveryError",
|
|
79
|
-
{
|
|
80
|
-
stage: Schema2.Literal("connect", "list_tools"),
|
|
81
|
-
message: Schema2.String
|
|
82
|
-
}
|
|
83
|
-
) {
|
|
84
|
-
};
|
|
85
|
-
var McpInvocationError = class extends Schema2.TaggedError()(
|
|
86
|
-
"McpInvocationError",
|
|
87
|
-
{
|
|
88
|
-
toolName: Schema2.String,
|
|
89
|
-
message: Schema2.String
|
|
90
|
-
}
|
|
91
|
-
) {
|
|
92
|
-
};
|
|
93
|
-
var McpOAuthError = class extends Schema2.TaggedError()("McpOAuthError", {
|
|
94
|
-
message: Schema2.String
|
|
95
|
-
}) {
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
// src/sdk/oauth.ts
|
|
99
|
-
var JsonObject = Schema3.Record({ key: Schema3.String, value: Schema3.Unknown });
|
|
100
|
-
var McpOAuthDiscoveryState = Schema3.Struct({
|
|
101
|
-
resourceMetadataUrl: Schema3.NullOr(Schema3.String),
|
|
102
|
-
authorizationServerUrl: Schema3.NullOr(Schema3.String),
|
|
103
|
-
resourceMetadata: Schema3.NullOr(JsonObject),
|
|
104
|
-
authorizationServerMetadata: Schema3.NullOr(JsonObject),
|
|
105
|
-
clientInformation: Schema3.NullOr(JsonObject)
|
|
106
|
-
});
|
|
107
|
-
var McpOAuthSession = Schema3.Struct({
|
|
108
|
-
...McpOAuthDiscoveryState.fields,
|
|
109
|
-
endpoint: Schema3.String,
|
|
110
|
-
redirectUrl: Schema3.String,
|
|
111
|
-
codeVerifier: Schema3.String
|
|
112
|
-
});
|
|
113
|
-
var toJsonObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
114
|
-
var CLIENT_METADATA = {
|
|
115
|
-
grant_types: ["authorization_code", "refresh_token"],
|
|
116
|
-
response_types: ["code"],
|
|
117
|
-
token_endpoint_auth_method: "none",
|
|
118
|
-
client_name: "Executor"
|
|
119
|
-
};
|
|
120
|
-
var extractDiscoveryState = (discoveryState, clientInformation) => ({
|
|
121
|
-
resourceMetadataUrl: discoveryState?.resourceMetadataUrl ?? null,
|
|
122
|
-
authorizationServerUrl: discoveryState?.authorizationServerUrl ?? null,
|
|
123
|
-
resourceMetadata: toJsonObject(discoveryState?.resourceMetadata),
|
|
124
|
-
authorizationServerMetadata: toJsonObject(discoveryState?.authorizationServerMetadata),
|
|
125
|
-
clientInformation: toJsonObject(clientInformation)
|
|
126
|
-
});
|
|
127
|
-
var callAuth = (provider, opts) => Effect.tryPromise({
|
|
128
|
-
try: () => auth(provider, opts),
|
|
129
|
-
catch: (cause) => new McpOAuthError({
|
|
130
|
-
message: cause instanceof Error ? cause.message : String(cause)
|
|
131
|
-
})
|
|
132
|
-
});
|
|
133
|
-
var startMcpOAuthAuthorization = (input) => Effect.gen(function* () {
|
|
134
|
-
let authorizationUrl;
|
|
135
|
-
let codeVerifier;
|
|
136
|
-
let discoveryState;
|
|
137
|
-
let clientInformation;
|
|
138
|
-
const provider = {
|
|
139
|
-
get redirectUrl() {
|
|
140
|
-
return input.redirectUrl;
|
|
141
|
-
},
|
|
142
|
-
get clientMetadata() {
|
|
143
|
-
return { ...CLIENT_METADATA, redirect_uris: [input.redirectUrl] };
|
|
144
|
-
},
|
|
145
|
-
state: () => input.state,
|
|
146
|
-
clientInformation: () => clientInformation,
|
|
147
|
-
saveClientInformation: (ci) => {
|
|
148
|
-
clientInformation = ci;
|
|
149
|
-
},
|
|
150
|
-
tokens: () => void 0,
|
|
151
|
-
saveTokens: () => void 0,
|
|
152
|
-
redirectToAuthorization: (url) => {
|
|
153
|
-
authorizationUrl = url;
|
|
154
|
-
},
|
|
155
|
-
saveCodeVerifier: (cv) => {
|
|
156
|
-
codeVerifier = cv;
|
|
157
|
-
},
|
|
158
|
-
codeVerifier: () => {
|
|
159
|
-
if (!codeVerifier) throw new Error("Code verifier not captured");
|
|
160
|
-
return codeVerifier;
|
|
161
|
-
},
|
|
162
|
-
saveDiscoveryState: (s) => {
|
|
163
|
-
discoveryState = s;
|
|
164
|
-
},
|
|
165
|
-
discoveryState: () => discoveryState
|
|
166
|
-
};
|
|
167
|
-
const result = yield* callAuth(provider, { serverUrl: input.endpoint });
|
|
168
|
-
if (result !== "REDIRECT" || !authorizationUrl || !codeVerifier) {
|
|
169
|
-
return yield* new McpOAuthError({
|
|
170
|
-
message: "OAuth flow did not produce an authorization redirect"
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
return {
|
|
174
|
-
authorizationUrl: authorizationUrl.toString(),
|
|
175
|
-
codeVerifier,
|
|
176
|
-
...extractDiscoveryState(discoveryState, clientInformation)
|
|
177
|
-
};
|
|
178
|
-
});
|
|
179
|
-
var exchangeMcpOAuthCode = (input) => Effect.gen(function* () {
|
|
180
|
-
const { session } = input;
|
|
181
|
-
let tokens;
|
|
182
|
-
let discoveryState = {
|
|
183
|
-
authorizationServerUrl: session.authorizationServerUrl ?? new URL("/", session.endpoint).toString(),
|
|
184
|
-
resourceMetadataUrl: session.resourceMetadataUrl ?? void 0,
|
|
185
|
-
resourceMetadata: session.resourceMetadata,
|
|
186
|
-
authorizationServerMetadata: session.authorizationServerMetadata
|
|
187
|
-
};
|
|
188
|
-
let clientInformation = session.clientInformation;
|
|
189
|
-
const provider = {
|
|
190
|
-
get redirectUrl() {
|
|
191
|
-
return session.redirectUrl;
|
|
192
|
-
},
|
|
193
|
-
get clientMetadata() {
|
|
194
|
-
return { ...CLIENT_METADATA, redirect_uris: [session.redirectUrl] };
|
|
195
|
-
},
|
|
196
|
-
clientInformation: () => clientInformation,
|
|
197
|
-
saveClientInformation: (ci) => {
|
|
198
|
-
clientInformation = ci;
|
|
199
|
-
},
|
|
200
|
-
tokens: () => void 0,
|
|
201
|
-
saveTokens: (t) => {
|
|
202
|
-
tokens = t;
|
|
203
|
-
},
|
|
204
|
-
redirectToAuthorization: () => {
|
|
205
|
-
throw new Error("Unexpected redirect during code exchange");
|
|
206
|
-
},
|
|
207
|
-
saveCodeVerifier: () => void 0,
|
|
208
|
-
codeVerifier: () => session.codeVerifier,
|
|
209
|
-
saveDiscoveryState: (s) => {
|
|
210
|
-
discoveryState = s;
|
|
211
|
-
},
|
|
212
|
-
discoveryState: () => discoveryState
|
|
213
|
-
};
|
|
214
|
-
const result = yield* callAuth(provider, {
|
|
215
|
-
serverUrl: session.endpoint,
|
|
216
|
-
authorizationCode: input.code
|
|
217
|
-
});
|
|
218
|
-
if (result !== "AUTHORIZED" || !tokens) {
|
|
219
|
-
return yield* new McpOAuthError({
|
|
220
|
-
message: "OAuth exchange did not produce tokens"
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
return {
|
|
224
|
-
tokens,
|
|
225
|
-
...extractDiscoveryState(discoveryState, clientInformation)
|
|
226
|
-
};
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
// src/sdk/stored-source.ts
|
|
230
|
-
import { Schema as Schema4 } from "effect";
|
|
231
|
-
var McpStoredSourceSchema = class extends Schema4.Class("McpStoredSource")({
|
|
232
|
-
namespace: Schema4.String,
|
|
233
|
-
name: Schema4.String,
|
|
234
|
-
config: McpStoredSourceData
|
|
235
|
-
}) {
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
// src/sdk/binding-store.ts
|
|
239
|
-
var MCP_OAUTH_SESSION_TTL_MS = 15 * 60 * 1e3;
|
|
240
|
-
var StoredOAuthSession = Schema5.Struct({
|
|
241
|
-
session: McpOAuthSession,
|
|
242
|
-
expiresAt: Schema5.Number
|
|
243
|
-
});
|
|
244
|
-
var encodeOAuthSession = Schema5.encodeSync(Schema5.parseJson(StoredOAuthSession));
|
|
245
|
-
var decodeOAuthSession = Schema5.decodeUnknownSync(Schema5.parseJson(StoredOAuthSession));
|
|
246
|
-
var encodeSource = Schema5.encodeSync(Schema5.parseJson(McpStoredSourceSchema));
|
|
247
|
-
var decodeSource = Schema5.decodeUnknownSync(Schema5.parseJson(McpStoredSourceSchema));
|
|
248
|
-
var StoredBindingEntry = Schema5.Struct({
|
|
249
|
-
namespace: Schema5.String,
|
|
250
|
-
binding: McpToolBinding
|
|
251
|
-
});
|
|
252
|
-
var encodeBindingEntry = Schema5.encodeSync(Schema5.parseJson(StoredBindingEntry));
|
|
253
|
-
var decodeBindingEntry = Schema5.decodeUnknownSync(Schema5.parseJson(StoredBindingEntry));
|
|
254
|
-
var makeStore = (bindings, sources, oauthSessions) => ({
|
|
255
|
-
// ---- Bindings ----
|
|
256
|
-
get: (toolId) => Effect2.gen(function* () {
|
|
257
|
-
const raw = yield* bindings.get(toolId);
|
|
258
|
-
if (!raw) return null;
|
|
259
|
-
const entry = decodeBindingEntry(raw);
|
|
260
|
-
return {
|
|
261
|
-
binding: entry.binding,
|
|
262
|
-
namespace: entry.namespace
|
|
263
|
-
};
|
|
264
|
-
}),
|
|
265
|
-
put: (toolId, namespace, binding) => bindings.set([{ key: toolId, value: encodeBindingEntry({ namespace, binding }) }]),
|
|
266
|
-
remove: (toolId) => bindings.delete([toolId]).pipe(Effect2.asVoid),
|
|
267
|
-
listByNamespace: (namespace) => Effect2.gen(function* () {
|
|
268
|
-
const entries = yield* bindings.list();
|
|
269
|
-
const ids = [];
|
|
270
|
-
for (const e of entries) {
|
|
271
|
-
const entry = decodeBindingEntry(e.value);
|
|
272
|
-
if (entry.namespace === namespace) ids.push(e.key);
|
|
273
|
-
}
|
|
274
|
-
return ids;
|
|
275
|
-
}),
|
|
276
|
-
removeByNamespace: (namespace) => Effect2.gen(function* () {
|
|
277
|
-
const entries = yield* bindings.list();
|
|
278
|
-
const ids = [];
|
|
279
|
-
for (const e of entries) {
|
|
280
|
-
const entry = decodeBindingEntry(e.value);
|
|
281
|
-
if (entry.namespace === namespace) ids.push(e.key);
|
|
282
|
-
}
|
|
283
|
-
if (ids.length > 0) yield* bindings.delete(ids);
|
|
284
|
-
return ids;
|
|
285
|
-
}),
|
|
286
|
-
// ---- Sources (meta + config combined) ----
|
|
287
|
-
putSource: (source) => sources.set([{ key: source.namespace, value: encodeSource(source) }]),
|
|
288
|
-
removeSource: (namespace) => sources.delete([namespace]).pipe(Effect2.asVoid),
|
|
289
|
-
listSources: () => Effect2.gen(function* () {
|
|
290
|
-
const entries = yield* sources.list();
|
|
291
|
-
return entries.map((e) => decodeSource(e.value));
|
|
292
|
-
}),
|
|
293
|
-
getSource: (namespace) => Effect2.gen(function* () {
|
|
294
|
-
const raw = yield* sources.get(namespace);
|
|
295
|
-
if (!raw) return null;
|
|
296
|
-
return decodeSource(raw);
|
|
297
|
-
}),
|
|
298
|
-
getSourceConfig: (namespace) => Effect2.gen(function* () {
|
|
299
|
-
const raw = yield* sources.get(namespace);
|
|
300
|
-
if (!raw) return null;
|
|
301
|
-
return decodeSource(raw).config;
|
|
302
|
-
}),
|
|
303
|
-
// ---- Pending OAuth sessions (short-lived, between startOAuth and completeOAuth) ----
|
|
304
|
-
putOAuthSession: (sessionId, session) => oauthSessions.set([
|
|
305
|
-
{
|
|
306
|
-
key: sessionId,
|
|
307
|
-
value: encodeOAuthSession({
|
|
308
|
-
session,
|
|
309
|
-
expiresAt: Date.now() + MCP_OAUTH_SESSION_TTL_MS
|
|
310
|
-
})
|
|
311
|
-
}
|
|
312
|
-
]),
|
|
313
|
-
getOAuthSession: (sessionId) => Effect2.gen(function* () {
|
|
314
|
-
const raw = yield* oauthSessions.get(sessionId);
|
|
315
|
-
if (!raw) return null;
|
|
316
|
-
const entry = decodeOAuthSession(raw);
|
|
317
|
-
if (entry.expiresAt < Date.now()) {
|
|
318
|
-
yield* oauthSessions.delete([sessionId]);
|
|
319
|
-
return null;
|
|
320
|
-
}
|
|
321
|
-
return entry.session;
|
|
322
|
-
}),
|
|
323
|
-
deleteOAuthSession: (sessionId) => oauthSessions.delete([sessionId]).pipe(Effect2.asVoid)
|
|
324
|
-
});
|
|
325
|
-
var makeKvBindingStore = (kv, namespace) => makeStore(
|
|
326
|
-
scopeKv(kv, `${namespace}.bindings`),
|
|
327
|
-
scopeKv(kv, `${namespace}.sources`),
|
|
328
|
-
scopeKv(kv, `${namespace}.oauth-sessions`)
|
|
329
|
-
);
|
|
330
|
-
var makeInMemoryBindingStore = () => makeStore(makeInMemoryScopedKv(), makeInMemoryScopedKv(), makeInMemoryScopedKv());
|
|
331
|
-
|
|
332
|
-
// src/sdk/plugin.ts
|
|
333
|
-
import { Effect as Effect6, Exit, ScopedCache, Duration, Scope } from "effect";
|
|
334
|
-
import {
|
|
335
|
-
Source,
|
|
336
|
-
SourceDetectionResult,
|
|
337
|
-
definePlugin,
|
|
338
|
-
ToolId,
|
|
339
|
-
SecretId
|
|
340
|
-
} from "@executor-js/sdk";
|
|
341
|
-
|
|
342
|
-
// src/sdk/connection.ts
|
|
343
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
344
|
-
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
345
|
-
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
346
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
347
|
-
import { Effect as Effect3 } from "effect";
|
|
348
|
-
var buildEndpointUrl = (endpoint, queryParams) => {
|
|
349
|
-
const url = new URL(endpoint);
|
|
350
|
-
for (const [key, value] of Object.entries(queryParams)) {
|
|
351
|
-
url.searchParams.set(key, value);
|
|
352
|
-
}
|
|
353
|
-
return url;
|
|
354
|
-
};
|
|
355
|
-
var createClient = () => new Client(
|
|
356
|
-
{ name: "executor-mcp", version: "0.1.0" },
|
|
357
|
-
{ capabilities: { elicitation: { form: {}, url: {} } } }
|
|
358
|
-
);
|
|
359
|
-
var connectionFromClient = (client) => ({
|
|
360
|
-
client,
|
|
361
|
-
close: () => client.close()
|
|
362
|
-
});
|
|
363
|
-
var connectClient = (input) => Effect3.gen(function* () {
|
|
364
|
-
const client = createClient();
|
|
365
|
-
const transportInstance = input.createTransport();
|
|
366
|
-
yield* Effect3.tryPromise({
|
|
367
|
-
try: () => client.connect(transportInstance),
|
|
368
|
-
catch: (cause) => new McpConnectionError({
|
|
369
|
-
transport: input.transport,
|
|
370
|
-
message: `Failed connecting via ${input.transport}: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
371
|
-
})
|
|
372
|
-
});
|
|
373
|
-
return connectionFromClient(client);
|
|
374
|
-
});
|
|
375
|
-
var createMcpConnector = (input) => {
|
|
376
|
-
if (input.transport === "stdio") {
|
|
377
|
-
const command = input.command.trim();
|
|
378
|
-
if (!command) {
|
|
379
|
-
return new McpConnectionError({
|
|
380
|
-
transport: "stdio",
|
|
381
|
-
message: "MCP stdio transport requires a command"
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
return connectClient({
|
|
385
|
-
transport: "stdio",
|
|
386
|
-
createTransport: () => new StdioClientTransport({
|
|
387
|
-
command,
|
|
388
|
-
args: input.args ? [...input.args] : void 0,
|
|
389
|
-
env: input.env ? { ...process.env, ...input.env } : void 0,
|
|
390
|
-
cwd: input.cwd?.trim().length ? input.cwd.trim() : void 0
|
|
391
|
-
})
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
const headers = input.headers ?? {};
|
|
395
|
-
const remoteTransport = input.remoteTransport ?? "auto";
|
|
396
|
-
const requestInit = Object.keys(headers).length > 0 ? { headers } : void 0;
|
|
397
|
-
const endpoint = buildEndpointUrl(input.endpoint, input.queryParams ?? {});
|
|
398
|
-
const connectStreamableHttp = connectClient({
|
|
399
|
-
transport: "streamable-http",
|
|
400
|
-
createTransport: () => new StreamableHTTPClientTransport(endpoint, {
|
|
401
|
-
requestInit,
|
|
402
|
-
authProvider: input.authProvider
|
|
403
|
-
})
|
|
404
|
-
});
|
|
405
|
-
const connectSse = connectClient({
|
|
406
|
-
transport: "sse",
|
|
407
|
-
createTransport: () => new SSEClientTransport(endpoint, {
|
|
408
|
-
requestInit,
|
|
409
|
-
authProvider: input.authProvider
|
|
410
|
-
})
|
|
411
|
-
});
|
|
412
|
-
if (remoteTransport === "streamable-http") return connectStreamableHttp;
|
|
413
|
-
if (remoteTransport === "sse") return connectSse;
|
|
414
|
-
return connectStreamableHttp.pipe(Effect3.catchAll(() => connectSse));
|
|
415
|
-
};
|
|
416
|
-
|
|
417
|
-
// src/sdk/discover.ts
|
|
418
|
-
import { Effect as Effect4 } from "effect";
|
|
419
|
-
|
|
420
|
-
// src/sdk/manifest.ts
|
|
421
|
-
import { Schema as Schema6 } from "effect";
|
|
422
|
-
var ListedTool = Schema6.Struct({
|
|
423
|
-
name: Schema6.String,
|
|
424
|
-
description: Schema6.optional(Schema6.NullOr(Schema6.String)),
|
|
425
|
-
inputSchema: Schema6.optional(Schema6.Unknown),
|
|
426
|
-
parameters: Schema6.optional(Schema6.Unknown),
|
|
427
|
-
outputSchema: Schema6.optional(Schema6.Unknown)
|
|
428
|
-
});
|
|
429
|
-
var ListToolsResult = Schema6.Struct({
|
|
430
|
-
tools: Schema6.Array(ListedTool)
|
|
431
|
-
});
|
|
432
|
-
var ServerInfo = Schema6.Struct({
|
|
433
|
-
name: Schema6.optional(Schema6.String),
|
|
434
|
-
version: Schema6.optional(Schema6.String)
|
|
435
|
-
});
|
|
436
|
-
var decodeListToolsResult = Schema6.decodeUnknownOption(ListToolsResult);
|
|
437
|
-
var decodeServerInfo = Schema6.decodeUnknownOption(ServerInfo);
|
|
438
|
-
var sanitize = (value) => {
|
|
439
|
-
const s = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
440
|
-
return s || "tool";
|
|
441
|
-
};
|
|
442
|
-
var uniqueId = (value, seen) => {
|
|
443
|
-
const base = sanitize(value);
|
|
444
|
-
const n = (seen.get(base) ?? 0) + 1;
|
|
445
|
-
seen.set(base, n);
|
|
446
|
-
return n === 1 ? base : `${base}_${n}`;
|
|
447
|
-
};
|
|
448
|
-
var joinToolPath = (namespace, toolId) => namespace?.trim() ? `${namespace}.${toolId}` : toolId;
|
|
449
|
-
var extractManifestFromListToolsResult = (listToolsResult, metadata) => {
|
|
450
|
-
const seen = /* @__PURE__ */ new Map();
|
|
451
|
-
const listed = decodeListToolsResult(listToolsResult).pipe(
|
|
452
|
-
(opt) => opt._tag === "Some" ? opt.value.tools : []
|
|
453
|
-
);
|
|
454
|
-
const server = decodeServerInfo(metadata?.serverInfo).pipe(
|
|
455
|
-
(opt) => opt._tag === "Some" ? { name: opt.value.name ?? null, version: opt.value.version ?? null } : null
|
|
456
|
-
);
|
|
457
|
-
const tools = listed.flatMap((tool) => {
|
|
458
|
-
const toolName = tool.name.trim();
|
|
459
|
-
if (!toolName) return [];
|
|
460
|
-
return [
|
|
461
|
-
{
|
|
462
|
-
toolId: uniqueId(toolName, seen),
|
|
463
|
-
toolName,
|
|
464
|
-
description: tool.description ?? null,
|
|
465
|
-
inputSchema: tool.inputSchema ?? tool.parameters,
|
|
466
|
-
outputSchema: tool.outputSchema
|
|
467
|
-
}
|
|
468
|
-
];
|
|
469
|
-
});
|
|
470
|
-
return { server, tools };
|
|
471
|
-
};
|
|
472
|
-
var slugify = (value) => value.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
473
|
-
var hostnameOf = (url) => {
|
|
474
|
-
try {
|
|
475
|
-
return new URL(url).hostname;
|
|
476
|
-
} catch {
|
|
477
|
-
return null;
|
|
478
|
-
}
|
|
479
|
-
};
|
|
480
|
-
var basenameOf = (path) => path.trim().split(/[\\/]/).pop() ?? path.trim();
|
|
481
|
-
var deriveMcpNamespace = (input) => {
|
|
482
|
-
if (input.name?.trim()) return slugify(input.name) || "mcp";
|
|
483
|
-
const fromEndpoint = input.endpoint?.trim() ? hostnameOf(input.endpoint) : null;
|
|
484
|
-
if (fromEndpoint) return slugify(fromEndpoint) || "mcp";
|
|
485
|
-
if (input.command?.trim()) return slugify(basenameOf(input.command)) || "mcp";
|
|
486
|
-
return "mcp";
|
|
487
|
-
};
|
|
488
|
-
|
|
489
|
-
// src/sdk/discover.ts
|
|
490
|
-
var discoverTools = (connector) => Effect4.gen(function* () {
|
|
491
|
-
const connection = yield* connector.pipe(
|
|
492
|
-
Effect4.mapError(
|
|
493
|
-
(err) => new McpToolDiscoveryError({
|
|
494
|
-
stage: "connect",
|
|
495
|
-
message: `Failed connecting to MCP server: ${err.message}`
|
|
496
|
-
})
|
|
497
|
-
)
|
|
498
|
-
);
|
|
499
|
-
const listResult = yield* Effect4.tryPromise({
|
|
500
|
-
try: () => connection.client.listTools(),
|
|
501
|
-
catch: (cause) => new McpToolDiscoveryError({
|
|
502
|
-
stage: "list_tools",
|
|
503
|
-
message: `Failed listing MCP tools: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
504
|
-
})
|
|
505
|
-
});
|
|
506
|
-
const manifest = extractManifestFromListToolsResult(listResult, {
|
|
507
|
-
serverInfo: connection.client.getServerVersion?.()
|
|
508
|
-
});
|
|
509
|
-
yield* Effect4.promise(() => connection.close().catch(() => {
|
|
510
|
-
}));
|
|
511
|
-
return manifest;
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
// src/sdk/invoke.ts
|
|
515
|
-
import { Effect as Effect5, Schema as Schema7 } from "effect";
|
|
516
|
-
import { ElicitRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
517
|
-
import {
|
|
518
|
-
ToolInvocationResult,
|
|
519
|
-
ToolInvocationError,
|
|
520
|
-
ToolAnnotations,
|
|
521
|
-
ElicitationResponse,
|
|
522
|
-
FormElicitation,
|
|
523
|
-
UrlElicitation
|
|
524
|
-
} from "@executor-js/sdk";
|
|
525
|
-
var asRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
526
|
-
var makeOAuthProvider = (accessToken, tokenType, refreshToken) => ({
|
|
527
|
-
get redirectUrl() {
|
|
528
|
-
return "http://localhost/oauth/callback";
|
|
529
|
-
},
|
|
530
|
-
get clientMetadata() {
|
|
531
|
-
return {
|
|
532
|
-
redirect_uris: ["http://localhost/oauth/callback"],
|
|
533
|
-
grant_types: ["authorization_code", "refresh_token"],
|
|
534
|
-
response_types: ["code"],
|
|
535
|
-
token_endpoint_auth_method: "none",
|
|
536
|
-
client_name: "Executor"
|
|
537
|
-
};
|
|
538
|
-
},
|
|
539
|
-
clientInformation: () => void 0,
|
|
540
|
-
saveClientInformation: () => {
|
|
541
|
-
},
|
|
542
|
-
tokens: async () => ({
|
|
543
|
-
access_token: accessToken,
|
|
544
|
-
token_type: tokenType,
|
|
545
|
-
...refreshToken ? { refresh_token: refreshToken } : {}
|
|
546
|
-
}),
|
|
547
|
-
saveTokens: async () => {
|
|
548
|
-
},
|
|
549
|
-
redirectToAuthorization: async () => {
|
|
550
|
-
throw new Error("MCP OAuth re-authorization required");
|
|
551
|
-
},
|
|
552
|
-
saveCodeVerifier: () => {
|
|
553
|
-
},
|
|
554
|
-
codeVerifier: () => {
|
|
555
|
-
throw new Error("No active PKCE verifier");
|
|
556
|
-
},
|
|
557
|
-
saveDiscoveryState: () => {
|
|
558
|
-
},
|
|
559
|
-
discoveryState: () => void 0
|
|
560
|
-
});
|
|
561
|
-
var McpElicitParams = Schema7.Union(
|
|
562
|
-
Schema7.Struct({
|
|
563
|
-
mode: Schema7.Literal("url"),
|
|
564
|
-
message: Schema7.String,
|
|
565
|
-
url: Schema7.String,
|
|
566
|
-
elicitationId: Schema7.optional(Schema7.String),
|
|
567
|
-
id: Schema7.optional(Schema7.String)
|
|
568
|
-
}),
|
|
569
|
-
Schema7.Struct({
|
|
570
|
-
mode: Schema7.optional(Schema7.Literal("form")),
|
|
571
|
-
message: Schema7.String,
|
|
572
|
-
requestedSchema: Schema7.Record({ key: Schema7.String, value: Schema7.Unknown })
|
|
573
|
-
})
|
|
574
|
-
);
|
|
575
|
-
var decodeElicitParams = Schema7.decodeUnknownSync(McpElicitParams);
|
|
576
|
-
var toElicitationRequest = (params) => params.mode === "url" ? new UrlElicitation({
|
|
577
|
-
message: params.message,
|
|
578
|
-
url: params.url,
|
|
579
|
-
elicitationId: params.elicitationId ?? params.id ?? ""
|
|
580
|
-
}) : new FormElicitation({
|
|
581
|
-
message: params.message,
|
|
582
|
-
requestedSchema: params.requestedSchema
|
|
583
|
-
});
|
|
584
|
-
var installElicitationHandler = (client, toolId, args, handler) => {
|
|
585
|
-
client.setRequestHandler(ElicitRequestSchema, async (request) => {
|
|
586
|
-
const params = decodeElicitParams(request.params);
|
|
587
|
-
const response = await Effect5.runPromise(
|
|
588
|
-
handler({ toolId, args, request: toElicitationRequest(params) })
|
|
589
|
-
);
|
|
590
|
-
return {
|
|
591
|
-
action: response.action,
|
|
592
|
-
...response.action === "accept" && response.content ? { content: response.content } : {}
|
|
593
|
-
};
|
|
594
|
-
});
|
|
595
|
-
};
|
|
596
|
-
var resolveConnectorInput = (sourceData, secrets, scopeId) => {
|
|
597
|
-
if (sourceData.transport === "stdio") {
|
|
598
|
-
return Effect5.succeed({
|
|
599
|
-
transport: "stdio",
|
|
600
|
-
command: sourceData.command,
|
|
601
|
-
args: sourceData.args,
|
|
602
|
-
env: sourceData.env,
|
|
603
|
-
cwd: sourceData.cwd
|
|
604
|
-
});
|
|
605
|
-
}
|
|
606
|
-
return Effect5.gen(function* () {
|
|
607
|
-
const headers = { ...sourceData.headers };
|
|
608
|
-
let authProvider;
|
|
609
|
-
const auth2 = sourceData.auth;
|
|
610
|
-
if (auth2.kind === "header") {
|
|
611
|
-
const secretValue = yield* secrets.resolve(auth2.secretId, scopeId).pipe(
|
|
612
|
-
Effect5.mapError(
|
|
613
|
-
() => new ToolInvocationError({
|
|
614
|
-
toolId: "",
|
|
615
|
-
message: `Failed to resolve secret "${auth2.secretId}" for MCP auth`,
|
|
616
|
-
cause: void 0
|
|
617
|
-
})
|
|
618
|
-
)
|
|
619
|
-
);
|
|
620
|
-
headers[auth2.headerName] = auth2.prefix ? `${auth2.prefix}${secretValue}` : secretValue;
|
|
621
|
-
} else if (auth2.kind === "oauth2") {
|
|
622
|
-
const accessToken = yield* secrets.resolve(auth2.accessTokenSecretId, scopeId).pipe(
|
|
623
|
-
Effect5.mapError(
|
|
624
|
-
() => new ToolInvocationError({
|
|
625
|
-
toolId: "",
|
|
626
|
-
message: "Failed to resolve OAuth access token for MCP auth",
|
|
627
|
-
cause: void 0
|
|
628
|
-
})
|
|
629
|
-
)
|
|
630
|
-
);
|
|
631
|
-
let refreshToken;
|
|
632
|
-
if (auth2.refreshTokenSecretId) {
|
|
633
|
-
refreshToken = yield* secrets.resolve(auth2.refreshTokenSecretId, scopeId).pipe(
|
|
634
|
-
Effect5.option,
|
|
635
|
-
Effect5.map((o) => o._tag === "Some" ? o.value : void 0)
|
|
636
|
-
);
|
|
637
|
-
}
|
|
638
|
-
authProvider = makeOAuthProvider(accessToken, auth2.tokenType ?? "Bearer", refreshToken);
|
|
639
|
-
}
|
|
640
|
-
return {
|
|
641
|
-
transport: "remote",
|
|
642
|
-
endpoint: sourceData.endpoint,
|
|
643
|
-
remoteTransport: sourceData.remoteTransport,
|
|
644
|
-
queryParams: sourceData.queryParams,
|
|
645
|
-
headers: Object.keys(headers).length > 0 ? headers : void 0,
|
|
646
|
-
authProvider
|
|
647
|
-
};
|
|
648
|
-
});
|
|
649
|
-
};
|
|
650
|
-
var connectionCacheKey = (sourceData) => sourceData.transport === "stdio" ? `stdio:${sourceData.command}` : `remote:${sourceData.endpoint}`;
|
|
651
|
-
var resolveElicitationHandler = (options) => options.onElicitation === "accept-all" ? () => Effect5.succeed(new ElicitationResponse({ action: "accept" })) : options.onElicitation;
|
|
652
|
-
var useMcpConnection = (connection, toolId, toolName, args, handler) => Effect5.gen(function* () {
|
|
653
|
-
installElicitationHandler(connection.client, toolId, args, handler);
|
|
654
|
-
return yield* Effect5.tryPromise({
|
|
655
|
-
try: () => connection.client.callTool({ name: toolName, arguments: args }),
|
|
656
|
-
catch: (cause) => new ToolInvocationError({
|
|
657
|
-
toolId,
|
|
658
|
-
message: `MCP tool call failed for ${toolName}: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
659
|
-
cause
|
|
660
|
-
})
|
|
661
|
-
});
|
|
662
|
-
}).pipe(Effect5.withSpan(`mcp.callTool.${toolName}`));
|
|
663
|
-
var makeMcpInvoker = (opts) => {
|
|
664
|
-
const { connectionCache, pendingConnectors } = opts;
|
|
665
|
-
return {
|
|
666
|
-
resolveAnnotations: () => Effect5.succeed(new ToolAnnotations({ requiresApproval: false })),
|
|
667
|
-
invoke: (toolId, args, options) => Effect5.gen(function* () {
|
|
668
|
-
const entry = yield* opts.bindingStore.get(toolId);
|
|
669
|
-
if (!entry) {
|
|
670
|
-
return yield* new ToolInvocationError({
|
|
671
|
-
toolId,
|
|
672
|
-
message: `No MCP binding found for tool "${toolId}"`,
|
|
673
|
-
cause: void 0
|
|
674
|
-
});
|
|
675
|
-
}
|
|
676
|
-
const sourceData = yield* opts.bindingStore.getSourceConfig(entry.namespace);
|
|
677
|
-
if (!sourceData) {
|
|
678
|
-
return yield* new ToolInvocationError({
|
|
679
|
-
toolId,
|
|
680
|
-
message: `No MCP source config found for namespace "${entry.namespace}"`,
|
|
681
|
-
cause: void 0
|
|
682
|
-
});
|
|
683
|
-
}
|
|
684
|
-
const { binding } = entry;
|
|
685
|
-
const cacheKey = connectionCacheKey(sourceData);
|
|
686
|
-
const connector = resolveConnectorInput(sourceData, opts.secrets, opts.scopeId).pipe(
|
|
687
|
-
Effect5.flatMap((ci) => createMcpConnector(ci)),
|
|
688
|
-
Effect5.mapError(
|
|
689
|
-
(err) => new McpConnectionError({
|
|
690
|
-
transport: "auto",
|
|
691
|
-
message: err instanceof Error ? err.message : String(err)
|
|
692
|
-
})
|
|
693
|
-
)
|
|
694
|
-
);
|
|
695
|
-
pendingConnectors.set(cacheKey, connector);
|
|
696
|
-
const connection = yield* connectionCache.get(cacheKey).pipe(
|
|
697
|
-
Effect5.mapError(
|
|
698
|
-
(err) => new ToolInvocationError({
|
|
699
|
-
toolId,
|
|
700
|
-
message: `Failed connecting to MCP server: ${err instanceof Error ? err.message : String(err)}`,
|
|
701
|
-
cause: err
|
|
702
|
-
})
|
|
703
|
-
)
|
|
704
|
-
);
|
|
705
|
-
const elicitationHandler = resolveElicitationHandler(options);
|
|
706
|
-
return yield* useMcpConnection(
|
|
707
|
-
connection,
|
|
708
|
-
toolId,
|
|
709
|
-
binding.toolName,
|
|
710
|
-
asRecord(args),
|
|
711
|
-
elicitationHandler
|
|
712
|
-
).pipe(
|
|
713
|
-
// On failure, invalidate the cached connection and retry once
|
|
714
|
-
Effect5.catchAll(
|
|
715
|
-
() => Effect5.gen(function* () {
|
|
716
|
-
yield* connectionCache.invalidate(cacheKey);
|
|
717
|
-
pendingConnectors.set(cacheKey, connector);
|
|
718
|
-
const freshConnection = yield* connectionCache.get(cacheKey).pipe(
|
|
719
|
-
Effect5.mapError(
|
|
720
|
-
(retryErr) => new ToolInvocationError({
|
|
721
|
-
toolId,
|
|
722
|
-
message: `Failed reconnecting: ${retryErr instanceof Error ? retryErr.message : String(retryErr)}`,
|
|
723
|
-
cause: retryErr
|
|
724
|
-
})
|
|
725
|
-
)
|
|
726
|
-
);
|
|
727
|
-
return yield* useMcpConnection(
|
|
728
|
-
freshConnection,
|
|
729
|
-
toolId,
|
|
730
|
-
binding.toolName,
|
|
731
|
-
asRecord(args),
|
|
732
|
-
elicitationHandler
|
|
733
|
-
);
|
|
734
|
-
})
|
|
735
|
-
)
|
|
736
|
-
);
|
|
737
|
-
}).pipe(
|
|
738
|
-
Effect5.scoped,
|
|
739
|
-
Effect5.map((callResult) => {
|
|
740
|
-
const resultRecord = asRecord(callResult);
|
|
741
|
-
const isError = resultRecord.isError === true;
|
|
742
|
-
return new ToolInvocationResult({
|
|
743
|
-
data: isError ? null : callResult ?? null,
|
|
744
|
-
error: isError ? callResult : null
|
|
745
|
-
});
|
|
746
|
-
}),
|
|
747
|
-
Effect5.catchAll((err) => {
|
|
748
|
-
if (typeof err === "object" && err !== null && "_tag" in err && err._tag === "ToolInvocationError") {
|
|
749
|
-
return Effect5.fail(err);
|
|
750
|
-
}
|
|
751
|
-
return Effect5.fail(
|
|
752
|
-
new ToolInvocationError({
|
|
753
|
-
toolId,
|
|
754
|
-
message: `MCP invocation failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
755
|
-
cause: err
|
|
756
|
-
})
|
|
757
|
-
);
|
|
758
|
-
})
|
|
759
|
-
),
|
|
760
|
-
closeConnections: () => Effect5.sync(() => {
|
|
761
|
-
pendingConnectors.clear();
|
|
762
|
-
}).pipe(Effect5.flatMap(() => connectionCache.invalidateAll))
|
|
763
|
-
};
|
|
764
|
-
};
|
|
765
|
-
|
|
766
|
-
// src/sdk/plugin.ts
|
|
767
|
-
var toRegistration = (entry, namespace) => ({
|
|
768
|
-
id: ToolId.make(joinToolPath(namespace, entry.toolId)),
|
|
769
|
-
pluginKey: "mcp",
|
|
770
|
-
sourceId: namespace,
|
|
771
|
-
name: entry.toolName,
|
|
772
|
-
description: entry.description ?? `MCP tool: ${entry.toolName}`,
|
|
773
|
-
inputSchema: entry.inputSchema,
|
|
774
|
-
outputSchema: entry.outputSchema
|
|
775
|
-
});
|
|
776
|
-
var toBinding = (entry) => new McpToolBinding({
|
|
777
|
-
toolId: entry.toolId,
|
|
778
|
-
toolName: entry.toolName,
|
|
779
|
-
description: entry.description,
|
|
780
|
-
inputSchema: entry.inputSchema,
|
|
781
|
-
outputSchema: entry.outputSchema
|
|
782
|
-
});
|
|
783
|
-
var toStoredSourceData = (config) => {
|
|
784
|
-
if (config.transport === "stdio") {
|
|
785
|
-
return {
|
|
786
|
-
transport: "stdio",
|
|
787
|
-
command: config.command,
|
|
788
|
-
args: config.args,
|
|
789
|
-
env: config.env,
|
|
790
|
-
cwd: config.cwd
|
|
791
|
-
};
|
|
792
|
-
}
|
|
793
|
-
return {
|
|
794
|
-
transport: "remote",
|
|
795
|
-
endpoint: config.endpoint,
|
|
796
|
-
remoteTransport: config.remoteTransport ?? "auto",
|
|
797
|
-
queryParams: config.queryParams,
|
|
798
|
-
headers: config.headers,
|
|
799
|
-
auth: config.auth ?? { kind: "none" }
|
|
800
|
-
};
|
|
801
|
-
};
|
|
802
|
-
var normalizeNamespace = (config) => config.namespace ?? deriveMcpNamespace({
|
|
803
|
-
name: config.name,
|
|
804
|
-
endpoint: config.transport === "remote" ? config.endpoint : void 0,
|
|
805
|
-
command: config.transport === "stdio" ? config.command : void 0
|
|
806
|
-
});
|
|
807
|
-
var makeOAuthProvider2 = (accessToken, tokenType, refreshToken) => ({
|
|
808
|
-
get redirectUrl() {
|
|
809
|
-
return "http://localhost/oauth/callback";
|
|
810
|
-
},
|
|
811
|
-
get clientMetadata() {
|
|
812
|
-
return {
|
|
813
|
-
redirect_uris: ["http://localhost/oauth/callback"],
|
|
814
|
-
grant_types: ["authorization_code", "refresh_token"],
|
|
815
|
-
response_types: ["code"],
|
|
816
|
-
token_endpoint_auth_method: "none",
|
|
817
|
-
client_name: "Executor"
|
|
818
|
-
};
|
|
819
|
-
},
|
|
820
|
-
clientInformation: () => void 0,
|
|
821
|
-
saveClientInformation: () => {
|
|
822
|
-
},
|
|
823
|
-
tokens: async () => ({
|
|
824
|
-
access_token: accessToken,
|
|
825
|
-
token_type: tokenType,
|
|
826
|
-
...refreshToken ? { refresh_token: refreshToken } : {}
|
|
827
|
-
}),
|
|
828
|
-
saveTokens: async () => {
|
|
829
|
-
},
|
|
830
|
-
redirectToAuthorization: async () => {
|
|
831
|
-
throw new Error("MCP OAuth re-authorization required");
|
|
832
|
-
},
|
|
833
|
-
saveCodeVerifier: () => {
|
|
834
|
-
},
|
|
835
|
-
codeVerifier: () => {
|
|
836
|
-
throw new Error("No active PKCE verifier");
|
|
837
|
-
},
|
|
838
|
-
saveDiscoveryState: () => {
|
|
839
|
-
},
|
|
840
|
-
discoveryState: () => void 0
|
|
841
|
-
});
|
|
842
|
-
var remoteConnectionError = (message) => new McpConnectionError({ transport: "remote", message });
|
|
843
|
-
var mcpOAuthError = (message) => new McpOAuthError({ message });
|
|
844
|
-
var mcpDiscoveryError = (message) => new McpToolDiscoveryError({ stage: "list_tools", message });
|
|
845
|
-
var mcpPlugin = (options) => {
|
|
846
|
-
const bindingStore = options?.bindingStore ?? makeInMemoryBindingStore();
|
|
847
|
-
const addedSources = /* @__PURE__ */ new Map();
|
|
848
|
-
return definePlugin({
|
|
849
|
-
key: "mcp",
|
|
850
|
-
init: (ctx) => Effect6.gen(function* () {
|
|
851
|
-
const cacheScope = yield* Scope.make();
|
|
852
|
-
const pendingConnectors = /* @__PURE__ */ new Map();
|
|
853
|
-
const connectionCache = yield* ScopedCache.make({
|
|
854
|
-
lookup: (key) => Effect6.acquireRelease(
|
|
855
|
-
Effect6.suspend(() => {
|
|
856
|
-
const connector = pendingConnectors.get(key);
|
|
857
|
-
if (!connector) {
|
|
858
|
-
return Effect6.fail(
|
|
859
|
-
new McpConnectionError({
|
|
860
|
-
transport: "auto",
|
|
861
|
-
message: `No pending connector for key: ${key}`
|
|
862
|
-
})
|
|
863
|
-
);
|
|
864
|
-
}
|
|
865
|
-
return connector;
|
|
866
|
-
}),
|
|
867
|
-
(connection) => Effect6.promise(() => connection.close().catch(() => {
|
|
868
|
-
}))
|
|
869
|
-
),
|
|
870
|
-
capacity: 64,
|
|
871
|
-
timeToLive: Duration.minutes(5)
|
|
872
|
-
}).pipe(Scope.extend(cacheScope));
|
|
873
|
-
const invoker = makeMcpInvoker({
|
|
874
|
-
bindingStore,
|
|
875
|
-
secrets: ctx.secrets,
|
|
876
|
-
scopeId: ctx.scope.id,
|
|
877
|
-
connectionCache,
|
|
878
|
-
pendingConnectors
|
|
879
|
-
});
|
|
880
|
-
yield* ctx.tools.registerInvoker("mcp", invoker);
|
|
881
|
-
const savedSources = yield* bindingStore.listSources();
|
|
882
|
-
for (const s of savedSources) {
|
|
883
|
-
const isRemote = s.config.transport === "remote";
|
|
884
|
-
addedSources.set(
|
|
885
|
-
s.namespace,
|
|
886
|
-
new Source({
|
|
887
|
-
id: s.namespace,
|
|
888
|
-
name: s.name,
|
|
889
|
-
kind: "mcp",
|
|
890
|
-
url: s.config.transport === "remote" ? s.config.endpoint : void 0,
|
|
891
|
-
canEdit: isRemote
|
|
892
|
-
})
|
|
893
|
-
);
|
|
894
|
-
}
|
|
895
|
-
const resolveConnectorInput2 = (sd) => {
|
|
896
|
-
if (sd.transport === "stdio") {
|
|
897
|
-
return Effect6.succeed({
|
|
898
|
-
transport: "stdio",
|
|
899
|
-
command: sd.command,
|
|
900
|
-
args: sd.args,
|
|
901
|
-
env: sd.env,
|
|
902
|
-
cwd: sd.cwd
|
|
903
|
-
});
|
|
904
|
-
}
|
|
905
|
-
return Effect6.gen(function* () {
|
|
906
|
-
const headers = {
|
|
907
|
-
...sd.headers
|
|
908
|
-
};
|
|
909
|
-
let authProvider;
|
|
910
|
-
const auth2 = sd.auth;
|
|
911
|
-
if (auth2.kind === "header") {
|
|
912
|
-
const val = yield* ctx.secrets.resolve(SecretId.make(auth2.secretId), ctx.scope.id).pipe(
|
|
913
|
-
Effect6.mapError(
|
|
914
|
-
() => remoteConnectionError(`Failed to resolve secret "${auth2.secretId}"`)
|
|
915
|
-
)
|
|
916
|
-
);
|
|
917
|
-
headers[auth2.headerName] = auth2.prefix ? `${auth2.prefix}${val}` : val;
|
|
918
|
-
} else if (auth2.kind === "oauth2") {
|
|
919
|
-
const accessToken = yield* ctx.secrets.resolve(SecretId.make(auth2.accessTokenSecretId), ctx.scope.id).pipe(
|
|
920
|
-
Effect6.mapError(
|
|
921
|
-
() => remoteConnectionError("Failed to resolve OAuth access token")
|
|
922
|
-
)
|
|
923
|
-
);
|
|
924
|
-
let refreshToken;
|
|
925
|
-
if (auth2.refreshTokenSecretId) {
|
|
926
|
-
refreshToken = yield* ctx.secrets.resolve(SecretId.make(auth2.refreshTokenSecretId), ctx.scope.id).pipe(
|
|
927
|
-
Effect6.option,
|
|
928
|
-
Effect6.map((o) => o._tag === "Some" ? o.value : void 0)
|
|
929
|
-
);
|
|
930
|
-
}
|
|
931
|
-
authProvider = makeOAuthProvider2(
|
|
932
|
-
accessToken,
|
|
933
|
-
auth2.tokenType ?? "Bearer",
|
|
934
|
-
refreshToken
|
|
935
|
-
);
|
|
936
|
-
}
|
|
937
|
-
return {
|
|
938
|
-
transport: "remote",
|
|
939
|
-
endpoint: sd.endpoint,
|
|
940
|
-
remoteTransport: sd.remoteTransport,
|
|
941
|
-
queryParams: sd.queryParams,
|
|
942
|
-
headers: Object.keys(headers).length > 0 ? headers : void 0,
|
|
943
|
-
authProvider
|
|
944
|
-
};
|
|
945
|
-
});
|
|
946
|
-
};
|
|
947
|
-
yield* ctx.sources.addManager({
|
|
948
|
-
kind: "mcp",
|
|
949
|
-
list: () => Effect6.sync(() => [...addedSources.values()]),
|
|
950
|
-
remove: (sourceId) => Effect6.gen(function* () {
|
|
951
|
-
yield* bindingStore.removeByNamespace(sourceId);
|
|
952
|
-
yield* bindingStore.removeSource(sourceId);
|
|
953
|
-
yield* ctx.tools.unregisterBySource(sourceId);
|
|
954
|
-
addedSources.delete(sourceId);
|
|
955
|
-
}),
|
|
956
|
-
detect: (url) => Effect6.gen(function* () {
|
|
957
|
-
const trimmed = url.trim();
|
|
958
|
-
if (!trimmed) return null;
|
|
959
|
-
const parsed = yield* Effect6.try(() => new URL(trimmed)).pipe(Effect6.option);
|
|
960
|
-
if (parsed._tag === "None") return null;
|
|
961
|
-
const name = parsed.value.hostname || "mcp";
|
|
962
|
-
const namespace = deriveMcpNamespace({ endpoint: trimmed });
|
|
963
|
-
const connector = createMcpConnector({
|
|
964
|
-
transport: "remote",
|
|
965
|
-
endpoint: trimmed
|
|
966
|
-
});
|
|
967
|
-
const connected = yield* discoverTools(connector).pipe(
|
|
968
|
-
Effect6.map(() => true),
|
|
969
|
-
Effect6.catchAll(() => Effect6.succeed(false))
|
|
970
|
-
);
|
|
971
|
-
if (connected) {
|
|
972
|
-
return new SourceDetectionResult({
|
|
973
|
-
kind: "mcp",
|
|
974
|
-
confidence: "high",
|
|
975
|
-
endpoint: trimmed,
|
|
976
|
-
name,
|
|
977
|
-
namespace
|
|
978
|
-
});
|
|
979
|
-
}
|
|
980
|
-
const hasOAuth = yield* startMcpOAuthAuthorization({
|
|
981
|
-
endpoint: trimmed,
|
|
982
|
-
redirectUrl: "http://127.0.0.1/executor/discovery/oauth/probe",
|
|
983
|
-
state: "probe"
|
|
984
|
-
}).pipe(
|
|
985
|
-
Effect6.map(() => true),
|
|
986
|
-
Effect6.catchAll(() => Effect6.succeed(false))
|
|
987
|
-
);
|
|
988
|
-
if (hasOAuth) {
|
|
989
|
-
return new SourceDetectionResult({
|
|
990
|
-
kind: "mcp",
|
|
991
|
-
confidence: "high",
|
|
992
|
-
endpoint: trimmed,
|
|
993
|
-
name,
|
|
994
|
-
namespace
|
|
995
|
-
});
|
|
996
|
-
}
|
|
997
|
-
return null;
|
|
998
|
-
}),
|
|
999
|
-
refresh: (sourceId) => Effect6.gen(function* () {
|
|
1000
|
-
const sd = yield* bindingStore.getSourceConfig(sourceId);
|
|
1001
|
-
if (!sd || !addedSources.has(sourceId)) return;
|
|
1002
|
-
const ci = yield* resolveConnectorInput2(sd).pipe(
|
|
1003
|
-
Effect6.catchAll(() => Effect6.succeed(null))
|
|
1004
|
-
);
|
|
1005
|
-
if (!ci) return;
|
|
1006
|
-
const manifest = yield* discoverTools(createMcpConnector(ci)).pipe(
|
|
1007
|
-
Effect6.catchAll(() => Effect6.succeed(null))
|
|
1008
|
-
);
|
|
1009
|
-
if (!manifest) return;
|
|
1010
|
-
const oldIds = yield* bindingStore.removeByNamespace(sourceId);
|
|
1011
|
-
if (oldIds.length > 0) yield* ctx.tools.unregister(oldIds);
|
|
1012
|
-
yield* Effect6.forEach(
|
|
1013
|
-
manifest.tools,
|
|
1014
|
-
(e) => bindingStore.put(
|
|
1015
|
-
ToolId.make(joinToolPath(sourceId, e.toolId)),
|
|
1016
|
-
sourceId,
|
|
1017
|
-
toBinding(e)
|
|
1018
|
-
),
|
|
1019
|
-
{ discard: true }
|
|
1020
|
-
);
|
|
1021
|
-
yield* ctx.tools.register(manifest.tools.map((e) => toRegistration(e, sourceId)));
|
|
1022
|
-
})
|
|
1023
|
-
});
|
|
1024
|
-
const probeEndpoint = (endpoint) => Effect6.gen(function* () {
|
|
1025
|
-
const trimmed = endpoint.trim();
|
|
1026
|
-
if (!trimmed) return yield* remoteConnectionError("Endpoint URL is required");
|
|
1027
|
-
const name = yield* Effect6.try(() => new URL(trimmed).hostname).pipe(
|
|
1028
|
-
Effect6.orElseSucceed(() => "mcp")
|
|
1029
|
-
);
|
|
1030
|
-
const namespace = deriveMcpNamespace({ endpoint: trimmed });
|
|
1031
|
-
const connector = createMcpConnector({
|
|
1032
|
-
transport: "remote",
|
|
1033
|
-
endpoint: trimmed
|
|
1034
|
-
});
|
|
1035
|
-
const result = yield* discoverTools(connector).pipe(
|
|
1036
|
-
Effect6.map((m) => ({ ok: true, manifest: m })),
|
|
1037
|
-
Effect6.catchAll(() => Effect6.succeed({ ok: false, manifest: null }))
|
|
1038
|
-
);
|
|
1039
|
-
if (result.ok && result.manifest) {
|
|
1040
|
-
return {
|
|
1041
|
-
connected: true,
|
|
1042
|
-
requiresOAuth: false,
|
|
1043
|
-
name: result.manifest.server?.name ?? name,
|
|
1044
|
-
namespace,
|
|
1045
|
-
toolCount: result.manifest.tools.length,
|
|
1046
|
-
serverName: result.manifest.server?.name ?? null
|
|
1047
|
-
};
|
|
1048
|
-
}
|
|
1049
|
-
const hasOAuth = yield* startMcpOAuthAuthorization({
|
|
1050
|
-
endpoint: trimmed,
|
|
1051
|
-
redirectUrl: "http://127.0.0.1/executor/discovery/oauth/probe",
|
|
1052
|
-
state: "probe"
|
|
1053
|
-
}).pipe(
|
|
1054
|
-
Effect6.map(() => true),
|
|
1055
|
-
Effect6.catchAll(() => Effect6.succeed(false))
|
|
1056
|
-
);
|
|
1057
|
-
if (hasOAuth) {
|
|
1058
|
-
return {
|
|
1059
|
-
connected: false,
|
|
1060
|
-
requiresOAuth: true,
|
|
1061
|
-
name,
|
|
1062
|
-
namespace,
|
|
1063
|
-
toolCount: null,
|
|
1064
|
-
serverName: null
|
|
1065
|
-
};
|
|
1066
|
-
}
|
|
1067
|
-
return yield* remoteConnectionError(
|
|
1068
|
-
"Could not connect to MCP endpoint and no OAuth was detected"
|
|
1069
|
-
);
|
|
1070
|
-
});
|
|
1071
|
-
const addSource = (config) => Effect6.gen(function* () {
|
|
1072
|
-
const namespace = normalizeNamespace(config);
|
|
1073
|
-
const sd = toStoredSourceData(config);
|
|
1074
|
-
const ci = yield* resolveConnectorInput2(sd);
|
|
1075
|
-
const connector = createMcpConnector(ci);
|
|
1076
|
-
const manifest = yield* discoverTools(connector).pipe(
|
|
1077
|
-
Effect6.mapError((err) => mcpDiscoveryError(`MCP discovery failed: ${err.message}`))
|
|
1078
|
-
);
|
|
1079
|
-
const registrations = manifest.tools.map((e) => toRegistration(e, namespace));
|
|
1080
|
-
yield* Effect6.forEach(
|
|
1081
|
-
manifest.tools,
|
|
1082
|
-
(e) => bindingStore.put(
|
|
1083
|
-
ToolId.make(joinToolPath(namespace, e.toolId)),
|
|
1084
|
-
namespace,
|
|
1085
|
-
toBinding(e)
|
|
1086
|
-
),
|
|
1087
|
-
{ discard: true }
|
|
1088
|
-
);
|
|
1089
|
-
yield* ctx.tools.register(registrations);
|
|
1090
|
-
const sourceName = manifest.server?.name ?? config.name ?? namespace;
|
|
1091
|
-
yield* bindingStore.putSource({
|
|
1092
|
-
namespace,
|
|
1093
|
-
name: sourceName,
|
|
1094
|
-
config: sd
|
|
1095
|
-
});
|
|
1096
|
-
addedSources.set(
|
|
1097
|
-
namespace,
|
|
1098
|
-
new Source({
|
|
1099
|
-
id: namespace,
|
|
1100
|
-
name: sourceName,
|
|
1101
|
-
kind: "mcp",
|
|
1102
|
-
url: sd.transport === "remote" ? sd.endpoint : void 0,
|
|
1103
|
-
canEdit: config.transport === "remote"
|
|
1104
|
-
})
|
|
1105
|
-
);
|
|
1106
|
-
return { toolCount: registrations.length, namespace };
|
|
1107
|
-
});
|
|
1108
|
-
const removeSource = (namespace) => Effect6.gen(function* () {
|
|
1109
|
-
const ids = yield* bindingStore.removeByNamespace(namespace);
|
|
1110
|
-
if (ids.length > 0) yield* ctx.tools.unregister(ids);
|
|
1111
|
-
yield* bindingStore.removeSource(namespace);
|
|
1112
|
-
addedSources.delete(namespace);
|
|
1113
|
-
});
|
|
1114
|
-
const refreshSource = (namespace) => Effect6.gen(function* () {
|
|
1115
|
-
const sd = yield* bindingStore.getSourceConfig(namespace);
|
|
1116
|
-
if (!sd)
|
|
1117
|
-
return yield* remoteConnectionError(`No stored config for MCP source "${namespace}"`);
|
|
1118
|
-
const ci = yield* resolveConnectorInput2(sd);
|
|
1119
|
-
const manifest = yield* discoverTools(createMcpConnector(ci)).pipe(
|
|
1120
|
-
Effect6.mapError((err) => mcpDiscoveryError(`MCP refresh failed: ${err.message}`))
|
|
1121
|
-
);
|
|
1122
|
-
const oldIds = yield* bindingStore.removeByNamespace(namespace);
|
|
1123
|
-
if (oldIds.length > 0) yield* ctx.tools.unregister(oldIds);
|
|
1124
|
-
yield* Effect6.forEach(
|
|
1125
|
-
manifest.tools,
|
|
1126
|
-
(e) => bindingStore.put(
|
|
1127
|
-
ToolId.make(joinToolPath(namespace, e.toolId)),
|
|
1128
|
-
namespace,
|
|
1129
|
-
toBinding(e)
|
|
1130
|
-
),
|
|
1131
|
-
{ discard: true }
|
|
1132
|
-
);
|
|
1133
|
-
yield* ctx.tools.register(manifest.tools.map((e) => toRegistration(e, namespace)));
|
|
1134
|
-
return { toolCount: manifest.tools.length };
|
|
1135
|
-
});
|
|
1136
|
-
const startOAuth = (input) => Effect6.gen(function* () {
|
|
1137
|
-
const endpoint = input.endpoint.trim();
|
|
1138
|
-
if (!endpoint) return yield* mcpOAuthError("MCP OAuth requires an endpoint");
|
|
1139
|
-
let fullEndpoint = endpoint;
|
|
1140
|
-
if (input.queryParams && Object.keys(input.queryParams).length > 0) {
|
|
1141
|
-
const u = new URL(endpoint);
|
|
1142
|
-
for (const [k, v] of Object.entries(input.queryParams)) u.searchParams.set(k, v);
|
|
1143
|
-
fullEndpoint = u.toString();
|
|
1144
|
-
}
|
|
1145
|
-
const sessionId = `mcp_oauth_${crypto.randomUUID()}`;
|
|
1146
|
-
const started = yield* startMcpOAuthAuthorization({
|
|
1147
|
-
endpoint: fullEndpoint,
|
|
1148
|
-
redirectUrl: input.redirectUrl,
|
|
1149
|
-
state: sessionId
|
|
1150
|
-
}).pipe(Effect6.mapError((e) => mcpOAuthError(`OAuth start failed: ${e.message}`)));
|
|
1151
|
-
yield* bindingStore.putOAuthSession(sessionId, {
|
|
1152
|
-
endpoint: fullEndpoint,
|
|
1153
|
-
redirectUrl: input.redirectUrl,
|
|
1154
|
-
codeVerifier: started.codeVerifier,
|
|
1155
|
-
resourceMetadataUrl: started.resourceMetadataUrl,
|
|
1156
|
-
authorizationServerUrl: started.authorizationServerUrl,
|
|
1157
|
-
resourceMetadata: started.resourceMetadata,
|
|
1158
|
-
authorizationServerMetadata: started.authorizationServerMetadata,
|
|
1159
|
-
clientInformation: started.clientInformation
|
|
1160
|
-
});
|
|
1161
|
-
return {
|
|
1162
|
-
sessionId,
|
|
1163
|
-
authorizationUrl: started.authorizationUrl
|
|
1164
|
-
};
|
|
1165
|
-
});
|
|
1166
|
-
const completeOAuth = (input) => Effect6.gen(function* () {
|
|
1167
|
-
if (input.error) return yield* mcpOAuthError(`OAuth error: ${input.error}`);
|
|
1168
|
-
if (!input.code) return yield* mcpOAuthError("Missing OAuth authorization code");
|
|
1169
|
-
const session = yield* bindingStore.getOAuthSession(input.state);
|
|
1170
|
-
if (!session) return yield* mcpOAuthError(`OAuth session not found: ${input.state}`);
|
|
1171
|
-
const exchanged = yield* exchangeMcpOAuthCode({
|
|
1172
|
-
session,
|
|
1173
|
-
code: input.code
|
|
1174
|
-
}).pipe(Effect6.mapError((e) => mcpOAuthError(`OAuth exchange failed: ${e.message}`)));
|
|
1175
|
-
const accessTokenRef = yield* ctx.secrets.set({
|
|
1176
|
-
id: SecretId.make(`mcp-oauth-access-${input.state}`),
|
|
1177
|
-
scopeId: ctx.scope.id,
|
|
1178
|
-
name: "MCP OAuth Access Token",
|
|
1179
|
-
value: exchanged.tokens.access_token,
|
|
1180
|
-
purpose: "oauth_access_token"
|
|
1181
|
-
}).pipe(
|
|
1182
|
-
Effect6.mapError((e) => mcpOAuthError(`Failed to store access token: ${String(e)}`))
|
|
1183
|
-
);
|
|
1184
|
-
let refreshTokenSecretId = null;
|
|
1185
|
-
if (exchanged.tokens.refresh_token) {
|
|
1186
|
-
const ref = yield* ctx.secrets.set({
|
|
1187
|
-
id: SecretId.make(`mcp-oauth-refresh-${input.state}`),
|
|
1188
|
-
scopeId: ctx.scope.id,
|
|
1189
|
-
name: "MCP OAuth Refresh Token",
|
|
1190
|
-
value: exchanged.tokens.refresh_token,
|
|
1191
|
-
purpose: "oauth_refresh_token"
|
|
1192
|
-
}).pipe(
|
|
1193
|
-
Effect6.mapError(
|
|
1194
|
-
(e) => mcpOAuthError(`Failed to store refresh token: ${String(e)}`)
|
|
1195
|
-
)
|
|
1196
|
-
);
|
|
1197
|
-
refreshTokenSecretId = ref.id;
|
|
1198
|
-
}
|
|
1199
|
-
yield* bindingStore.deleteOAuthSession(input.state);
|
|
1200
|
-
const expiresAt = typeof exchanged.tokens.expires_in === "number" ? Date.now() + exchanged.tokens.expires_in * 1e3 : null;
|
|
1201
|
-
return {
|
|
1202
|
-
accessTokenSecretId: accessTokenRef.id,
|
|
1203
|
-
refreshTokenSecretId,
|
|
1204
|
-
tokenType: exchanged.tokens.token_type ?? "Bearer",
|
|
1205
|
-
expiresAt,
|
|
1206
|
-
scope: exchanged.tokens.scope ?? null
|
|
1207
|
-
};
|
|
1208
|
-
});
|
|
1209
|
-
const updateSource = (namespace, input) => Effect6.gen(function* () {
|
|
1210
|
-
const existing = yield* bindingStore.getSource(namespace);
|
|
1211
|
-
if (!existing || existing.config.transport !== "remote") return;
|
|
1212
|
-
const remote = existing.config;
|
|
1213
|
-
const updatedConfig = {
|
|
1214
|
-
...remote,
|
|
1215
|
-
...input.endpoint !== void 0 ? { endpoint: input.endpoint } : {},
|
|
1216
|
-
...input.headers !== void 0 ? { headers: input.headers } : {},
|
|
1217
|
-
...input.auth !== void 0 ? { auth: input.auth } : {},
|
|
1218
|
-
...input.queryParams !== void 0 ? { queryParams: input.queryParams } : {}
|
|
1219
|
-
};
|
|
1220
|
-
yield* bindingStore.putSource({
|
|
1221
|
-
namespace,
|
|
1222
|
-
name: input.name?.trim() || existing.name,
|
|
1223
|
-
config: updatedConfig
|
|
1224
|
-
});
|
|
1225
|
-
});
|
|
1226
|
-
const getSource = (namespace) => bindingStore.getSource(namespace);
|
|
1227
|
-
return {
|
|
1228
|
-
extension: {
|
|
1229
|
-
probeEndpoint,
|
|
1230
|
-
addSource,
|
|
1231
|
-
removeSource,
|
|
1232
|
-
refreshSource,
|
|
1233
|
-
startOAuth,
|
|
1234
|
-
completeOAuth,
|
|
1235
|
-
getSource,
|
|
1236
|
-
updateSource
|
|
1237
|
-
},
|
|
1238
|
-
close: () => Effect6.gen(function* () {
|
|
1239
|
-
yield* invoker.closeConnections();
|
|
1240
|
-
yield* Scope.close(cacheScope, Exit.void);
|
|
1241
|
-
for (const sourceId of addedSources.keys()) {
|
|
1242
|
-
yield* ctx.tools.unregisterBySource(sourceId);
|
|
1243
|
-
}
|
|
1244
|
-
addedSources.clear();
|
|
1245
|
-
})
|
|
1246
|
-
};
|
|
1247
|
-
})
|
|
1248
|
-
});
|
|
1249
|
-
};
|
|
1250
|
-
|
|
1251
|
-
export {
|
|
1252
|
-
makeKvBindingStore,
|
|
1253
|
-
mcpPlugin
|
|
1254
|
-
};
|
|
1255
|
-
//# sourceMappingURL=chunk-X3JTTDWJ.js.map
|