@executor-js/plugin-mcp 1.4.28 → 1.4.30

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.
Files changed (46) hide show
  1. package/dist/{AddMcpSource-TLAL463B.js → AddMcpSource-3HUBFR3K.js} +68 -141
  2. package/dist/AddMcpSource-3HUBFR3K.js.map +1 -0
  3. package/dist/{EditMcpSource-FAWEECNU.js → EditMcpSource-UVGSSC2R.js} +106 -83
  4. package/dist/EditMcpSource-UVGSSC2R.js.map +1 -0
  5. package/dist/McpSourceSummary-UWVCAJOU.js +171 -0
  6. package/dist/McpSourceSummary-UWVCAJOU.js.map +1 -0
  7. package/dist/api/group.d.ts +92 -180
  8. package/dist/api/index.d.ts +97 -382
  9. package/dist/{chunk-4ORPFRLI.js → chunk-2A4H3UVR.js} +21 -80
  10. package/dist/chunk-2A4H3UVR.js.map +1 -0
  11. package/dist/{chunk-M6REVU6O.js → chunk-3TGDWTNE.js} +14 -40
  12. package/dist/chunk-3TGDWTNE.js.map +1 -0
  13. package/dist/{chunk-NQT7NAGE.js → chunk-H5PLTEMB.js} +673 -713
  14. package/dist/chunk-H5PLTEMB.js.map +1 -0
  15. package/dist/chunk-PZ5AY32C.js +10 -0
  16. package/dist/chunk-PZ5AY32C.js.map +1 -0
  17. package/dist/{chunk-SKSXXFOA.js → chunk-TW44CBXJ.js} +12 -1
  18. package/dist/chunk-TW44CBXJ.js.map +1 -0
  19. package/dist/client.js +5 -4
  20. package/dist/client.js.map +1 -1
  21. package/dist/core.js +4 -6
  22. package/dist/index.js +4 -2
  23. package/dist/promise.d.ts +1 -1
  24. package/dist/react/atoms.d.ts +198 -236
  25. package/dist/react/client.d.ts +91 -179
  26. package/dist/sdk/binding-store.d.ts +3 -163
  27. package/dist/sdk/index.d.ts +2 -2
  28. package/dist/sdk/plugin.d.ts +172 -225
  29. package/dist/sdk/presets.d.ts +1 -0
  30. package/dist/sdk/testing-fixtures.test.d.ts +1 -0
  31. package/dist/sdk/types.d.ts +58 -83
  32. package/dist/{stdio-connector-AA5S6UUJ.js → stdio-connector-MDW6PW36.js} +3 -1
  33. package/dist/{stdio-connector-AA5S6UUJ.js.map → stdio-connector-MDW6PW36.js.map} +1 -1
  34. package/dist/testing/index.d.ts +1 -1
  35. package/dist/testing/server.d.ts +70 -4
  36. package/dist/testing.js +14085 -30
  37. package/dist/testing.js.map +1 -1
  38. package/package.json +3 -4
  39. package/dist/AddMcpSource-TLAL463B.js.map +0 -1
  40. package/dist/EditMcpSource-FAWEECNU.js.map +0 -1
  41. package/dist/McpSourceSummary-257JNETP.js +0 -85
  42. package/dist/McpSourceSummary-257JNETP.js.map +0 -1
  43. package/dist/chunk-4ORPFRLI.js.map +0 -1
  44. package/dist/chunk-M6REVU6O.js.map +0 -1
  45. package/dist/chunk-NQT7NAGE.js.map +0 -1
  46. package/dist/chunk-SKSXXFOA.js.map +0 -1
@@ -1,94 +1,30 @@
1
1
  import {
2
- MCP_HEADER_AUTH_SLOT,
2
+ mcpPresets
3
+ } from "./chunk-TW44CBXJ.js";
4
+ import {
3
5
  MCP_OAUTH_CLIENT_ID_SLOT,
4
6
  MCP_OAUTH_CLIENT_SECRET_SLOT,
5
7
  MCP_OAUTH_CONNECTION_SLOT,
8
+ McpConfiguredValueInput,
9
+ McpConnectionAuthInput,
6
10
  McpConnectionError,
11
+ McpCredentialInput,
7
12
  McpInvocationError,
8
- McpSourceBindingRef,
13
+ McpRemoteTransport,
9
14
  McpStoredSourceData,
10
15
  McpToolAnnotations,
11
16
  McpToolBinding,
12
17
  McpToolDiscoveryError,
18
+ SecretBackedValue,
13
19
  mcpHeaderSlot,
14
20
  mcpQueryParamSlot
15
- } from "./chunk-M6REVU6O.js";
21
+ } from "./chunk-3TGDWTNE.js";
16
22
 
17
23
  // src/sdk/binding-store.ts
18
- import { Effect, Option, Schema } from "effect";
19
- import {
20
- ConfiguredCredentialBinding,
21
- defineSchema
22
- } from "@executor-js/sdk/core";
23
- var mcpSchema = defineSchema({
24
- mcp_source: {
25
- fields: {
26
- id: { type: "string", required: true },
27
- scope_id: { type: "string", required: true, index: true },
28
- name: { type: "string", required: true },
29
- // Plugin-private structural data minus the ref-bearing fields
30
- // (auth, headers, queryParams). For remote sources: transport,
31
- // endpoint, remoteTransport. For stdio: transport, command,
32
- // args, env, cwd.
33
- config: { type: "json", required: true },
34
- // Flattened McpConnectionAuth. The stored source only names slots;
35
- // concrete per-user/per-workspace values live in core credential_binding.
36
- auth_kind: {
37
- type: ["none", "header", "oauth2"],
38
- required: true,
39
- defaultValue: "none"
40
- },
41
- // Header-auth fields.
42
- auth_header_name: { type: "string", required: false },
43
- auth_header_slot: { type: "string", required: false },
44
- auth_header_prefix: { type: "string", required: false },
45
- // OAuth2 auth fields.
46
- auth_connection_slot: { type: "string", required: false },
47
- auth_client_id_slot: {
48
- type: "string",
49
- required: false
50
- },
51
- auth_client_secret_slot: {
52
- type: "string",
53
- required: false
54
- },
55
- created_at: { type: "date", required: true }
56
- }
57
- },
58
- mcp_source_header: {
59
- fields: {
60
- id: { type: "string", required: true },
61
- scope_id: { type: "string", required: true, index: true },
62
- source_id: { type: "string", required: true, index: true },
63
- name: { type: "string", required: true },
64
- kind: { type: ["text", "binding"], required: true },
65
- text_value: { type: "string", required: false },
66
- slot_key: { type: "string", required: false },
67
- prefix: { type: "string", required: false }
68
- }
69
- },
70
- mcp_source_query_param: {
71
- fields: {
72
- id: { type: "string", required: true },
73
- scope_id: { type: "string", required: true, index: true },
74
- source_id: { type: "string", required: true, index: true },
75
- name: { type: "string", required: true },
76
- kind: { type: ["text", "binding"], required: true },
77
- text_value: { type: "string", required: false },
78
- slot_key: { type: "string", required: false },
79
- prefix: { type: "string", required: false }
80
- }
81
- },
82
- mcp_binding: {
83
- fields: {
84
- id: { type: "string", required: true },
85
- scope_id: { type: "string", required: true, index: true },
86
- source_id: { type: "string", required: true, index: true },
87
- binding: { type: "json", required: true },
88
- created_at: { type: "date", required: true }
89
- }
90
- }
91
- });
24
+ import { Effect, Option, Predicate, Schema } from "effect";
25
+ var mcpSchema = {};
26
+ var SOURCE_COLLECTION = "source";
27
+ var BINDING_COLLECTION = "binding";
92
28
  var decodeSourceData = Schema.decodeUnknownSync(McpStoredSourceData);
93
29
  var encodeSourceData = Schema.encodeSync(McpStoredSourceData);
94
30
  var decodeBinding = Schema.decodeUnknownSync(McpToolBinding);
@@ -98,279 +34,109 @@ var coerceJson = (value) => {
98
34
  if (typeof value !== "string") return value;
99
35
  return Option.getOrElse(decodeJson(value), () => value);
100
36
  };
101
- var authToColumns = (auth) => {
102
- if (auth.kind === "header") {
103
- return {
104
- auth_kind: "header",
105
- auth_header_name: auth.headerName,
106
- auth_header_slot: auth.secretSlot,
107
- auth_header_prefix: auth.prefix
108
- };
109
- }
110
- if (auth.kind === "oauth2") {
111
- return {
112
- auth_kind: "oauth2",
113
- auth_connection_slot: auth.connectionSlot,
114
- auth_client_id_slot: auth.clientIdSlot,
115
- auth_client_secret_slot: auth.clientSecretSlot
116
- };
37
+ var sourceData = (source) => ({
38
+ namespace: source.namespace,
39
+ scope: source.scope,
40
+ name: source.name,
41
+ config: encodeSourceData(source.config)
42
+ });
43
+ var bindingData = (namespace, entry) => ({
44
+ namespace,
45
+ toolId: entry.toolId,
46
+ binding: encodeBinding(entry.binding)
47
+ });
48
+ var rowToSource = (row) => {
49
+ const raw = coerceJson(row.data);
50
+ if (!raw || typeof raw !== "object") return null;
51
+ const record = raw;
52
+ if (typeof record.namespace !== "string" || typeof record.scope !== "string" || typeof record.name !== "string") {
53
+ return null;
117
54
  }
118
- return { auth_kind: "none" };
55
+ return {
56
+ namespace: record.namespace,
57
+ scope: record.scope,
58
+ name: record.name,
59
+ config: decodeSourceData(coerceJson(record.config))
60
+ };
119
61
  };
120
- var columnsToAuth = (row) => {
121
- const kind = row.auth_kind;
122
- if (kind === "header" && typeof row.auth_header_slot === "string") {
123
- const prefix = row.auth_header_prefix;
124
- return {
125
- kind: "header",
126
- headerName: row.auth_header_name ?? "",
127
- secretSlot: row.auth_header_slot,
128
- ...prefix ? { prefix } : {}
129
- };
130
- }
131
- if (kind === "oauth2" && typeof row.auth_connection_slot === "string") {
132
- const cid = row.auth_client_id_slot;
133
- const csec = row.auth_client_secret_slot;
134
- return {
135
- kind: "oauth2",
136
- connectionSlot: row.auth_connection_slot,
137
- ...cid ? { clientIdSlot: cid } : {},
138
- ...csec ? { clientSecretSlot: csec } : {}
139
- };
140
- }
141
- return { kind: "none" };
62
+ var rowToBinding = (row) => {
63
+ const raw = coerceJson(row.data);
64
+ if (!raw || typeof raw !== "object") return null;
65
+ const record = raw;
66
+ if (typeof record.toolId !== "string" || typeof record.namespace !== "string") return null;
67
+ return {
68
+ toolId: record.toolId,
69
+ namespace: record.namespace,
70
+ binding: decodeBinding(coerceJson(record.binding))
71
+ };
142
72
  };
143
- var valueMapToRows = (sourceId, scope, values) => {
144
- if (!values) return [];
145
- return Object.entries(values).map(([name, value]) => {
146
- const id = JSON.stringify([sourceId, name]);
147
- if (typeof value === "string") {
148
- return {
149
- id,
150
- scope_id: scope,
151
- source_id: sourceId,
152
- name,
153
- kind: "text",
154
- text_value: value
155
- };
73
+ var makeMcpStore = ({ pluginStorage }) => {
74
+ const listBindingRowsForSourceScope = (namespace, scope) => pluginStorage.list({
75
+ collection: BINDING_COLLECTION,
76
+ keyPrefix: `${namespace}.`
77
+ }).pipe(
78
+ Effect.map(
79
+ (rows) => rows.filter((row) => {
80
+ if (String(row.scopeId) !== scope) return false;
81
+ return rowToBinding(row)?.namespace === namespace;
82
+ })
83
+ )
84
+ );
85
+ const removeBindingsForSourceScope = (namespace, scope) => Effect.gen(function* () {
86
+ const rows = yield* listBindingRowsForSourceScope(namespace, scope);
87
+ for (const row of rows) {
88
+ yield* pluginStorage.remove({
89
+ scope,
90
+ collection: BINDING_COLLECTION,
91
+ key: row.key
92
+ });
156
93
  }
157
- return {
158
- id,
159
- scope_id: scope,
160
- source_id: sourceId,
161
- name,
162
- kind: "binding",
163
- slot_key: value.slot,
164
- prefix: value.prefix
165
- };
166
94
  });
167
- };
168
- var rowsToValueMap = (rows) => {
169
- const out = {};
170
- for (const row of rows) {
171
- if (typeof row.name !== "string") continue;
172
- const name = row.name;
173
- if (row.kind === "binding" && typeof row.slot_key === "string") {
174
- const prefix = row.prefix;
175
- out[name] = prefix ? ConfiguredCredentialBinding.make({ kind: "binding", slot: row.slot_key, prefix }) : ConfiguredCredentialBinding.make({ kind: "binding", slot: row.slot_key });
176
- } else if (row.kind === "text" && typeof row.text_value === "string") {
177
- out[name] = row.text_value;
178
- }
179
- }
180
- return out;
181
- };
182
- var makeMcpStore = ({ adapter: db }) => {
183
95
  return {
184
- listBindingsBySource: (namespace, scope) => Effect.gen(function* () {
185
- const rows = yield* db.findMany({
186
- model: "mcp_binding",
187
- where: [
188
- { field: "source_id", value: namespace },
189
- { field: "scope_id", value: scope }
190
- ]
191
- });
192
- return rows.map((row) => ({
193
- toolId: row.id,
194
- binding: decodeBinding(coerceJson(row.binding))
195
- }));
196
- }),
197
- getBinding: (toolId, scope) => Effect.gen(function* () {
198
- const row = yield* db.findOne({
199
- model: "mcp_binding",
200
- where: [
201
- { field: "id", value: toolId },
202
- { field: "scope_id", value: scope }
203
- ]
204
- });
205
- if (!row) return null;
206
- const binding = decodeBinding(coerceJson(row.binding));
207
- return { binding, namespace: row.source_id };
208
- }),
96
+ listBindingsBySource: (namespace, scope) => listBindingRowsForSourceScope(namespace, scope).pipe(
97
+ Effect.map(
98
+ (rows) => rows.map(rowToBinding).filter(Predicate.isNotNull).map((row) => ({ toolId: row.toolId, binding: row.binding }))
99
+ )
100
+ ),
101
+ getBinding: (toolId, scope) => pluginStorage.getAtScope({ scope, collection: BINDING_COLLECTION, key: toolId }).pipe(
102
+ Effect.map((row) => {
103
+ const binding = row ? rowToBinding(row) : null;
104
+ return binding ? { binding: binding.binding, namespace: binding.namespace } : null;
105
+ })
106
+ ),
209
107
  putBindings: (namespace, scope, entries) => Effect.gen(function* () {
210
- if (entries.length === 0) return;
211
- const now = /* @__PURE__ */ new Date();
212
- yield* db.createMany({
213
- model: "mcp_binding",
214
- data: entries.map((e) => ({
215
- id: e.toolId,
216
- scope_id: scope,
217
- source_id: namespace,
218
- binding: encodeBinding(e.binding),
219
- created_at: now
220
- })),
221
- forceAllowId: true
222
- });
223
- }),
224
- removeBindingsByNamespace: (namespace, scope) => db.deleteMany({
225
- model: "mcp_binding",
226
- where: [
227
- { field: "source_id", value: namespace },
228
- { field: "scope_id", value: scope }
229
- ]
230
- }).pipe(Effect.asVoid),
231
- getSource: (namespace, scope) => Effect.gen(function* () {
232
- const row = yield* db.findOne({
233
- model: "mcp_source",
234
- where: [
235
- { field: "id", value: namespace },
236
- { field: "scope_id", value: scope }
237
- ]
238
- });
239
- if (!row) return null;
240
- return {
241
- namespace: row.id,
242
- scope: row.scope_id,
243
- name: row.name,
244
- config: yield* hydrateSourceData(row, namespace, scope)
245
- };
246
- }),
247
- getSourceConfig: (namespace, scope) => Effect.gen(function* () {
248
- const row = yield* db.findOne({
249
- model: "mcp_source",
250
- where: [
251
- { field: "id", value: namespace },
252
- { field: "scope_id", value: scope }
253
- ]
254
- });
255
- if (!row) return null;
256
- return yield* hydrateSourceData(row, namespace, scope);
257
- }),
258
- putSource: (source) => Effect.gen(function* () {
259
- const now = /* @__PURE__ */ new Date();
260
- yield* db.delete({
261
- model: "mcp_source",
262
- where: [
263
- { field: "id", value: source.namespace },
264
- { field: "scope_id", value: source.scope }
265
- ]
266
- });
267
- yield* deleteSourceChildren(source.namespace, source.scope);
268
- const auth = source.config.transport === "remote" ? source.config.auth : { kind: "none" };
269
- const authCols = authToColumns(auth);
270
- const headers = source.config.transport === "remote" ? source.config.headers : void 0;
271
- const queryParams = source.config.transport === "remote" ? source.config.queryParams : void 0;
272
- const encodedConfig = stripExtractedFields(
273
- encodeSourceData(source.config)
274
- );
275
- yield* db.create({
276
- model: "mcp_source",
277
- data: {
278
- id: source.namespace,
279
- scope_id: source.scope,
280
- name: source.name,
281
- config: encodedConfig,
282
- created_at: now,
283
- ...authCols
284
- },
285
- forceAllowId: true
286
- });
287
- const headerRows = valueMapToRows(source.namespace, source.scope, headers);
288
- if (headerRows.length > 0) {
289
- yield* db.createMany({
290
- model: "mcp_source_header",
291
- data: headerRows,
292
- forceAllowId: true
293
- });
294
- }
295
- const paramRows = valueMapToRows(source.namespace, source.scope, queryParams);
296
- if (paramRows.length > 0) {
297
- yield* db.createMany({
298
- model: "mcp_source_query_param",
299
- data: paramRows,
300
- forceAllowId: true
108
+ for (const entry of entries) {
109
+ yield* pluginStorage.put({
110
+ scope,
111
+ collection: BINDING_COLLECTION,
112
+ key: entry.toolId,
113
+ data: bindingData(namespace, entry)
301
114
  });
302
115
  }
303
116
  }),
117
+ removeBindingsByNamespace: (namespace, scope) => removeBindingsForSourceScope(namespace, scope),
118
+ getSource: (namespace, scope) => pluginStorage.getAtScope({ scope, collection: SOURCE_COLLECTION, key: namespace }).pipe(Effect.map((row) => row ? rowToSource(row) : null)),
119
+ getSourceConfig: (namespace, scope) => pluginStorage.getAtScope({ scope, collection: SOURCE_COLLECTION, key: namespace }).pipe(
120
+ Effect.map((row) => {
121
+ const source = row ? rowToSource(row) : null;
122
+ return source?.config ?? null;
123
+ })
124
+ ),
125
+ putSource: (source) => pluginStorage.put({
126
+ scope: source.scope,
127
+ collection: SOURCE_COLLECTION,
128
+ key: source.namespace,
129
+ data: sourceData(source)
130
+ }).pipe(Effect.asVoid),
304
131
  removeSource: (namespace, scope) => Effect.gen(function* () {
305
- yield* db.deleteMany({
306
- model: "mcp_binding",
307
- where: [
308
- { field: "source_id", value: namespace },
309
- { field: "scope_id", value: scope }
310
- ]
311
- });
312
- yield* deleteSourceChildren(namespace, scope);
313
- yield* db.delete({
314
- model: "mcp_source",
315
- where: [
316
- { field: "id", value: namespace },
317
- { field: "scope_id", value: scope }
318
- ]
132
+ yield* removeBindingsForSourceScope(namespace, scope);
133
+ yield* pluginStorage.remove({
134
+ scope,
135
+ collection: SOURCE_COLLECTION,
136
+ key: namespace
319
137
  });
320
138
  })
321
139
  };
322
- function deleteSourceChildren(namespace, scope) {
323
- return Effect.gen(function* () {
324
- for (const model of ["mcp_source_header", "mcp_source_query_param"]) {
325
- yield* db.deleteMany({
326
- model,
327
- where: [
328
- { field: "source_id", value: namespace },
329
- { field: "scope_id", value: scope }
330
- ]
331
- });
332
- }
333
- });
334
- }
335
- function hydrateSourceData(row, namespace, scope) {
336
- return Effect.gen(function* () {
337
- const partial = coerceJson(row.config);
338
- if (partial.transport !== "remote") {
339
- return decodeSourceData(partial);
340
- }
341
- const headerRows = yield* db.findMany({
342
- model: "mcp_source_header",
343
- where: [
344
- { field: "source_id", value: namespace },
345
- { field: "scope_id", value: scope }
346
- ]
347
- });
348
- const paramRows = yield* db.findMany({
349
- model: "mcp_source_query_param",
350
- where: [
351
- { field: "source_id", value: namespace },
352
- { field: "scope_id", value: scope }
353
- ]
354
- });
355
- const headers = rowsToValueMap(headerRows);
356
- const queryParams = rowsToValueMap(paramRows);
357
- const reassembled = {
358
- ...partial,
359
- ...Object.keys(headers).length > 0 ? { headers } : {},
360
- ...Object.keys(queryParams).length > 0 ? { queryParams } : {},
361
- auth: columnsToAuth(row)
362
- };
363
- return decodeSourceData(reassembled);
364
- });
365
- }
366
- };
367
- var stripExtractedFields = (encoded) => {
368
- if (encoded.transport !== "remote") return encoded;
369
- const { auth, headers, queryParams, ...rest } = encoded;
370
- void auth;
371
- void headers;
372
- void queryParams;
373
- return rest;
374
140
  };
375
141
 
376
142
  // src/sdk/plugin.ts
@@ -380,21 +146,27 @@ import {
380
146
  Exit as Exit2,
381
147
  Match,
382
148
  Option as Option5,
383
- Predicate as Predicate2,
149
+ Predicate as Predicate3,
384
150
  Result,
385
151
  Scope,
152
+ Schema as Schema5,
386
153
  ScopedCache as ScopedCache2
387
154
  } from "effect";
388
155
  import {
389
- ConfiguredCredentialBinding as ConfiguredCredentialBinding2,
390
- ConnectionId,
391
156
  ScopeId,
392
- SecretId,
393
157
  SourceDetectionResult,
158
+ ToolResult,
159
+ defaultSourceInstallScopeId,
394
160
  definePlugin,
161
+ tool,
395
162
  resolveSecretBackedMap as resolveSharedSecretBackedMap,
396
163
  StorageError
397
164
  } from "@executor-js/sdk/core";
165
+ import {
166
+ compileHttpNamedCredentialMap,
167
+ OAuth2SourceConfig,
168
+ httpCredentialInputToBindingValue
169
+ } from "@executor-js/sdk/http-source";
398
170
 
399
171
  // src/sdk/connection.ts
400
172
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -449,7 +221,7 @@ var createMcpConnector = (input) => {
449
221
  }
450
222
  return Effect2.gen(function* () {
451
223
  const { createStdioTransport } = yield* Effect2.tryPromise({
452
- try: () => import("./stdio-connector-AA5S6UUJ.js"),
224
+ try: () => import("./stdio-connector-MDW6PW36.js"),
453
225
  catch: () => new McpConnectionError({
454
226
  transport: "stdio",
455
227
  message: "Failed to load stdio transport module"
@@ -537,17 +309,17 @@ var extractManifestFromListToolsResult = (listToolsResult, metadata) => {
537
309
  ),
538
310
  Option2.getOrNull
539
311
  );
540
- const tools = listed.flatMap((tool) => {
541
- const toolName = tool.name.trim();
312
+ const tools = listed.flatMap((tool2) => {
313
+ const toolName = tool2.name.trim();
542
314
  if (!toolName) return [];
543
315
  return [
544
316
  {
545
317
  toolId: uniqueId(toolName, seen),
546
318
  toolName,
547
- description: tool.description ?? null,
548
- inputSchema: tool.inputSchema ?? tool.parameters,
549
- outputSchema: tool.outputSchema,
550
- annotations: tool.annotations
319
+ description: tool2.description ?? null,
320
+ inputSchema: tool2.inputSchema ?? tool2.parameters,
321
+ outputSchema: tool2.outputSchema,
322
+ annotations: tool2.annotations
551
323
  }
552
324
  ];
553
325
  });
@@ -608,7 +380,7 @@ var closeConnection = (connection) => Effect3.ignore(
608
380
  );
609
381
 
610
382
  // src/sdk/invoke.ts
611
- import { Cause, Effect as Effect4, Exit, Option as Option3, Predicate, Schema as Schema3, ScopedCache } from "effect";
383
+ import { Cause, Effect as Effect4, Exit, Option as Option3, Predicate as Predicate2, Schema as Schema3, ScopedCache } from "effect";
612
384
  import { ElicitRequestSchema } from "@modelcontextprotocol/sdk/types.js";
613
385
  import {
614
386
  FormElicitation,
@@ -691,8 +463,8 @@ var installElicitationHandler = (client, elicit) => {
691
463
  const failure = exit.cause.reasons.find(Cause.isFailReason);
692
464
  if (failure) {
693
465
  const err = failure.error;
694
- if (Predicate.isTagged(err, "ElicitationDeclinedError")) {
695
- const action = Predicate.hasProperty(err, "action") && err.action === "cancel" ? "cancel" : "decline";
466
+ if (Predicate2.isTagged(err, "ElicitationDeclinedError")) {
467
+ const action = Predicate2.hasProperty(err, "action") && err.action === "cancel" ? "cancel" : "decline";
696
468
  return { action };
697
469
  }
698
470
  }
@@ -808,6 +580,41 @@ var isOAuthErrorBody = (body) => {
808
580
  if (Array.isArray(obj.errors)) return false;
809
581
  return typeof obj.error === "string";
810
582
  };
583
+ var ProtectedResourceMetadata = Schema4.Struct({
584
+ resource: Schema4.String,
585
+ authorization_servers: Schema4.Array(Schema4.String)
586
+ });
587
+ var decodeProtectedResourceMetadata = Schema4.decodeUnknownOption(
588
+ Schema4.fromJsonString(ProtectedResourceMetadata)
589
+ );
590
+ var protectedResourceMetadataUrl = (endpoint) => {
591
+ const path = endpoint.pathname === "/" ? "" : endpoint.pathname;
592
+ return `${endpoint.origin}/.well-known/oauth-protected-resource${path}`;
593
+ };
594
+ var resourceMatchesEndpoint = (resource, endpoint) => {
595
+ if (!URL.canParse(resource)) return false;
596
+ const parsed = new URL(resource);
597
+ if (parsed.origin !== endpoint.origin) return false;
598
+ const resourcePath = parsed.pathname.replace(/\/+$/, "");
599
+ const endpointPath = endpoint.pathname.replace(/\/+$/, "");
600
+ return endpointPath === resourcePath || endpointPath.startsWith(`${resourcePath}/`);
601
+ };
602
+ var probeProtectedResourceMetadata = (client, endpoint, timeoutMs) => Effect5.gen(function* () {
603
+ const response = yield* client.execute(
604
+ HttpClientRequest.get(protectedResourceMetadataUrl(endpoint)).pipe(
605
+ HttpClientRequest.setHeader("accept", "application/json")
606
+ )
607
+ ).pipe(Effect5.timeout(Duration.millis(timeoutMs)));
608
+ if (response.status < 200 || response.status >= 300) return false;
609
+ const body = yield* response.text.pipe(
610
+ Effect5.timeout(Duration.millis(timeoutMs)),
611
+ Effect5.catch(() => Effect5.succeed(""))
612
+ );
613
+ const metadata = decodeProtectedResourceMetadata(body);
614
+ if (Option4.isNone(metadata)) return false;
615
+ if (metadata.value.authorization_servers.length === 0) return false;
616
+ return resourceMatchesEndpoint(metadata.value.resource, endpoint);
617
+ }).pipe(Effect5.catch(() => Effect5.succeed(false)));
811
618
  var ErrorMessageShape = Schema4.Struct({ message: Schema4.String });
812
619
  var decodeErrorMessageShape = Schema4.decodeUnknownOption(ErrorMessageShape);
813
620
  var reasonFromBoundaryCause = (cause) => {
@@ -836,6 +643,9 @@ var probeMcpEndpointShape = (endpoint, options = {}) => Effect5.gen(function* ()
836
643
  if (response.status === 401) {
837
644
  const wwwAuth = readHeader(response.headers, "www-authenticate");
838
645
  if (!wwwAuth || !/^\s*bearer\b/i.test(wwwAuth)) {
646
+ if (yield* probeProtectedResourceMetadata(client, url, timeoutMs)) {
647
+ return { kind: "mcp", requiresAuth: true };
648
+ }
839
649
  return {
840
650
  kind: "not-mcp",
841
651
  category: "auth-required",
@@ -851,6 +661,9 @@ var probeMcpEndpointShape = (endpoint, options = {}) => Effect5.gen(function* ()
851
661
  if (isSse) return { kind: "mcp", requiresAuth: true };
852
662
  const body = yield* readBody(response);
853
663
  if (!isJsonRpcEnvelope(body) && !isOAuthErrorBody(body)) {
664
+ if (yield* probeProtectedResourceMetadata(client, url, timeoutMs)) {
665
+ return { kind: "mcp", requiresAuth: true };
666
+ }
854
667
  return {
855
668
  kind: "not-mcp",
856
669
  category: "auth-required",
@@ -934,8 +747,117 @@ var probeMcpEndpointShape = (endpoint, options = {}) => Effect5.gen(function* ()
934
747
 
935
748
  // src/sdk/plugin.ts
936
749
  import {
937
- SECRET_REF_PREFIX
750
+ headerToConfigValue
938
751
  } from "@executor-js/config";
752
+ var McpConfigureSourcePayloadSchema = Schema5.Struct({
753
+ name: Schema5.optional(Schema5.String),
754
+ endpoint: Schema5.optional(Schema5.String),
755
+ headers: Schema5.optional(Schema5.Record(Schema5.String, McpCredentialInput)),
756
+ queryParams: Schema5.optional(Schema5.Record(Schema5.String, McpCredentialInput)),
757
+ auth: Schema5.optional(McpConnectionAuthInput)
758
+ });
759
+ var McpConfigureSourceInputSchema = Schema5.Struct({
760
+ scope: Schema5.String,
761
+ ...McpConfigureSourcePayloadSchema.fields
762
+ });
763
+ var McpInitialCredentialsInputSchema = Schema5.Struct({
764
+ scope: Schema5.String,
765
+ headers: Schema5.optional(Schema5.Record(Schema5.String, McpCredentialInput)),
766
+ queryParams: Schema5.optional(Schema5.Record(Schema5.String, McpCredentialInput)),
767
+ auth: Schema5.optional(McpConnectionAuthInput)
768
+ });
769
+ var McpRemoteAddSourceInputSchema = Schema5.Struct({
770
+ transport: Schema5.Literal("remote"),
771
+ name: Schema5.String,
772
+ endpoint: Schema5.String,
773
+ remoteTransport: Schema5.optional(McpRemoteTransport),
774
+ queryParams: Schema5.optional(Schema5.Record(Schema5.String, McpConfiguredValueInput)),
775
+ headers: Schema5.optional(Schema5.Record(Schema5.String, McpConfiguredValueInput)),
776
+ namespace: Schema5.optional(Schema5.String),
777
+ oauth2: Schema5.optional(OAuth2SourceConfig),
778
+ credentials: Schema5.optional(McpInitialCredentialsInputSchema)
779
+ });
780
+ var McpStdioAddSourceInputSchema = Schema5.Struct({
781
+ transport: Schema5.Literal("stdio"),
782
+ name: Schema5.String,
783
+ command: Schema5.String,
784
+ args: Schema5.optional(Schema5.Array(Schema5.String)),
785
+ env: Schema5.optional(Schema5.Record(Schema5.String, Schema5.String)),
786
+ cwd: Schema5.optional(Schema5.String),
787
+ namespace: Schema5.optional(Schema5.String)
788
+ });
789
+ var McpAddSourceInputSchema = Schema5.Union([
790
+ McpRemoteAddSourceInputSchema,
791
+ McpStdioAddSourceInputSchema
792
+ ]);
793
+ var McpAddSourceOutputSchema = Schema5.Struct({
794
+ namespace: Schema5.String,
795
+ source: Schema5.Struct({
796
+ id: Schema5.String,
797
+ scope: Schema5.String
798
+ }),
799
+ toolCount: Schema5.Number,
800
+ discovery: Schema5.optional(
801
+ Schema5.Struct({
802
+ status: Schema5.Literals(["ok", "failed"]),
803
+ message: Schema5.optional(Schema5.String),
804
+ stage: Schema5.optional(Schema5.String)
805
+ })
806
+ )
807
+ });
808
+ var McpProbeEndpointInputSchema = Schema5.Struct({
809
+ endpoint: Schema5.String,
810
+ headers: Schema5.optional(Schema5.Record(Schema5.String, SecretBackedValue)),
811
+ queryParams: Schema5.optional(Schema5.Record(Schema5.String, SecretBackedValue))
812
+ });
813
+ var McpProbeEndpointOutputSchema = Schema5.Struct({
814
+ connected: Schema5.Boolean,
815
+ requiresOAuth: Schema5.Boolean,
816
+ supportsDynamicRegistration: Schema5.Boolean,
817
+ name: Schema5.String,
818
+ namespace: Schema5.String,
819
+ toolCount: Schema5.NullOr(Schema5.Number),
820
+ serverName: Schema5.NullOr(Schema5.String)
821
+ });
822
+ var McpGetSourceInputSchema = Schema5.Struct({
823
+ namespace: Schema5.String,
824
+ scope: Schema5.String
825
+ });
826
+ var McpGetSourceOutputSchema = Schema5.Struct({
827
+ source: Schema5.NullOr(Schema5.Unknown)
828
+ });
829
+ var McpStaticConfigureSourceInputSchema = Schema5.Struct({
830
+ source: Schema5.Struct({
831
+ id: Schema5.String,
832
+ scope: Schema5.String
833
+ }),
834
+ scope: Schema5.String,
835
+ ...McpConfigureSourcePayloadSchema.fields
836
+ });
837
+ var McpStaticConfigureSourceOutputSchema = Schema5.Struct({
838
+ configured: Schema5.Boolean
839
+ });
840
+ var schemaToStaticToolSchema = (schema) => Schema5.toStandardSchemaV1(Schema5.toStandardJSONSchemaV1(schema));
841
+ var mcpToolFailure = (code, message, details) => ToolResult.fail({
842
+ code,
843
+ message,
844
+ ...details === void 0 ? {} : { details }
845
+ });
846
+ var McpAddSourceInputStandardSchema = schemaToStaticToolSchema(McpAddSourceInputSchema);
847
+ var McpAddSourceOutputStandardSchema = schemaToStaticToolSchema(McpAddSourceOutputSchema);
848
+ var McpProbeEndpointInputStandardSchema = schemaToStaticToolSchema(McpProbeEndpointInputSchema);
849
+ var McpProbeEndpointOutputStandardSchema = schemaToStaticToolSchema(McpProbeEndpointOutputSchema);
850
+ var McpGetSourceInputStandardSchema = schemaToStaticToolSchema(McpGetSourceInputSchema);
851
+ var McpGetSourceOutputStandardSchema = schemaToStaticToolSchema(McpGetSourceOutputSchema);
852
+ var McpStaticConfigureSourceInputStandardSchema = schemaToStaticToolSchema(
853
+ McpStaticConfigureSourceInputSchema
854
+ );
855
+ var McpStaticConfigureSourceOutputStandardSchema = schemaToStaticToolSchema(
856
+ McpStaticConfigureSourceOutputSchema
857
+ );
858
+ var resolveStaticScopeInput = (ctx, value) => String(
859
+ ctx.scopes.find((scope) => scope.name === value || String(scope.id) === value)?.id ?? value
860
+ );
939
861
  var toStoredSourceData = (config, remoteCredentials) => {
940
862
  if (config.transport === "stdio") {
941
863
  return {
@@ -969,6 +891,22 @@ var toBinding = (entry) => McpToolBinding.make({
969
891
  annotations: entry.annotations
970
892
  });
971
893
  var MCP_PLUGIN_ID = "mcp";
894
+ var McpTextContent = Schema5.Struct({ type: Schema5.Literal("text"), text: Schema5.String });
895
+ var McpToolCallEnvelope = Schema5.Struct({
896
+ isError: Schema5.optional(Schema5.Boolean),
897
+ content: Schema5.optional(Schema5.Array(Schema5.Unknown))
898
+ });
899
+ var decodeMcpTextContent = Schema5.decodeUnknownOption(McpTextContent);
900
+ var decodeMcpToolCallEnvelope = Schema5.decodeUnknownOption(McpToolCallEnvelope);
901
+ var extractMcpErrorMessage = (content) => {
902
+ if (Array.isArray(content)) {
903
+ for (const item of content) {
904
+ const decoded = Option5.getOrUndefined(decodeMcpTextContent(item));
905
+ if (decoded !== void 0 && decoded.text.length > 0) return decoded.text;
906
+ }
907
+ }
908
+ return "MCP tool returned an error";
909
+ };
972
910
  var urlMatchesToken = (url, token) => {
973
911
  const re = new RegExp(`(?:^|[^a-z0-9])${token}(?:$|[^a-z0-9])`, "i");
974
912
  return re.test(url.hostname) || re.test(url.pathname);
@@ -991,26 +929,6 @@ var userFacingProbeMessage = (shape) => {
991
929
  };
992
930
  var scopeRanks = (ctx) => new Map(ctx.scopes.map((scope, index) => [String(scope.id), index]));
993
931
  var scopeRank = (ranks, scopeId) => ranks.get(scopeId) ?? Infinity;
994
- var coreBindingToMcpBinding = (binding) => McpSourceBindingRef.make({
995
- sourceId: binding.sourceId,
996
- sourceScopeId: binding.sourceScopeId,
997
- scopeId: binding.scopeId,
998
- slot: binding.slotKey,
999
- value: binding.value,
1000
- createdAt: binding.createdAt,
1001
- updatedAt: binding.updatedAt
1002
- });
1003
- var listMcpSourceBindings = (ctx, sourceId, sourceScope) => Effect6.gen(function* () {
1004
- const ranks = scopeRanks(ctx);
1005
- const sourceSourceRank = scopeRank(ranks, sourceScope);
1006
- if (sourceSourceRank === Infinity) return [];
1007
- const bindings = yield* ctx.credentialBindings.listForSource({
1008
- pluginId: MCP_PLUGIN_ID,
1009
- sourceId,
1010
- sourceScope: ScopeId.make(sourceScope)
1011
- });
1012
- return bindings.filter((binding) => scopeRank(ranks, binding.scopeId) <= sourceSourceRank).map(coreBindingToMcpBinding);
1013
- });
1014
932
  var resolveMcpSourceBinding = (ctx, sourceId, sourceScope, slot) => Effect6.gen(function* () {
1015
933
  const ranks = scopeRanks(ctx);
1016
934
  const sourceSourceRank = scopeRank(ranks, sourceScope);
@@ -1023,7 +941,7 @@ var resolveMcpSourceBinding = (ctx, sourceId, sourceScope, slot) => Effect6.gen(
1023
941
  const binding = bindings.filter(
1024
942
  (candidate) => candidate.slotKey === slot && scopeRank(ranks, candidate.scopeId) <= sourceSourceRank
1025
943
  ).sort((a, b) => scopeRank(ranks, a.scopeId) - scopeRank(ranks, b.scopeId))[0];
1026
- return binding ? coreBindingToMcpBinding(binding) : null;
944
+ return binding ?? null;
1027
945
  });
1028
946
  var validateMcpBindingTarget = (ctx, input) => Effect6.gen(function* () {
1029
947
  const ranks = scopeRanks(ctx);
@@ -1049,108 +967,64 @@ var validateMcpBindingTarget = (ctx, input) => Effect6.gen(function* () {
1049
967
  });
1050
968
  }
1051
969
  });
1052
- var bindingTargetScope = (targetScope, bindings) => {
1053
- if (bindings.length === 0) return Effect6.succeed(void 0);
1054
- if (targetScope) return Effect6.succeed(targetScope);
1055
- return Effect6.fail(
1056
- new McpConnectionError({
1057
- transport: "remote",
1058
- message: "credentialTargetScope is required when adding direct MCP credentials"
1059
- })
1060
- );
1061
- };
1062
- var targetScopeForBinding = (fallbackTargetScope, binding) => {
1063
- const targetScope = binding.targetScope ?? fallbackTargetScope;
1064
- if (targetScope) return Effect6.succeed(targetScope);
1065
- return Effect6.fail(
1066
- new McpConnectionError({
1067
- transport: "remote",
1068
- message: "credentialTargetScope is required when adding direct MCP credentials"
1069
- })
1070
- );
1071
- };
1072
- var canonicalizeCredentialMap = (values, slotForName) => {
1073
- const nextValues = {};
1074
- const bindings = [];
970
+ var canonicalizeCredentialMap = compileHttpNamedCredentialMap;
971
+ var canonicalizeConfiguredValueMap = (values, slotForName) => {
972
+ const next = {};
1075
973
  for (const [name, value] of Object.entries(values ?? {})) {
1076
974
  if (typeof value === "string") {
1077
- nextValues[name] = value;
975
+ next[name] = value;
1078
976
  continue;
1079
977
  }
1080
- if ("kind" in value) {
1081
- nextValues[name] = value;
1082
- continue;
1083
- }
1084
- const slot = slotForName(name);
1085
- nextValues[name] = ConfiguredCredentialBinding2.make({
978
+ next[name] = {
1086
979
  kind: "binding",
1087
- slot,
980
+ slot: slotForName(name),
1088
981
  prefix: value.prefix
1089
- });
1090
- bindings.push({
1091
- slot,
1092
- targetScope: "targetScope" in value ? value.targetScope : void 0,
1093
- value: {
1094
- kind: "secret",
1095
- secretId: SecretId.make(value.secretId),
1096
- ..."secretScopeId" in value && value.secretScopeId ? { secretScopeId: value.secretScopeId } : {}
1097
- }
1098
- });
982
+ };
1099
983
  }
1100
- return { values: nextValues, bindings };
984
+ return next;
1101
985
  };
1102
- var canonicalizeAuth = (auth) => {
1103
- if (!auth || auth.kind === "none") return { auth: { kind: "none" }, bindings: [] };
1104
- if (auth.kind === "header") {
1105
- if ("secretSlot" in auth) return { auth, bindings: [] };
1106
- return {
1107
- auth: {
1108
- kind: "header",
1109
- headerName: auth.headerName,
1110
- secretSlot: MCP_HEADER_AUTH_SLOT,
1111
- prefix: auth.prefix
1112
- },
1113
- bindings: [
1114
- {
1115
- slot: MCP_HEADER_AUTH_SLOT,
1116
- targetScope: auth.targetScope,
1117
- value: {
1118
- kind: "secret",
1119
- secretId: SecretId.make(auth.secretId),
1120
- ...auth.secretScopeId ? { secretScopeId: auth.secretScopeId } : {}
1121
- }
1122
- }
1123
- ]
1124
- };
986
+ var resolveConfiguredValueMap = (values) => {
987
+ if (!values) return void 0;
988
+ const resolved = {};
989
+ for (const [name, value] of Object.entries(values)) {
990
+ if (typeof value === "string") resolved[name] = value;
1125
991
  }
1126
- if ("connectionSlot" in auth) return { auth, bindings: [] };
1127
- const bindings = [
1128
- {
992
+ return Object.keys(resolved).length > 0 ? resolved : void 0;
993
+ };
994
+ var authFromOAuth2Source = (oauth2) => oauth2 ? {
995
+ kind: "oauth2",
996
+ connectionSlot: oauth2.connectionSlot,
997
+ clientIdSlot: oauth2.clientIdSlot,
998
+ ...oauth2.clientSecretSlot ? { clientSecretSlot: oauth2.clientSecretSlot } : {}
999
+ } : { kind: "none" };
1000
+ var canonicalizeAuth = (auth) => {
1001
+ if (!auth || "kind" in auth || !auth.oauth2) return { auth: { kind: "none" }, bindings: [] };
1002
+ const oauth = auth.oauth2;
1003
+ const bindings = [];
1004
+ if (oauth.connection) {
1005
+ bindings.push({
1129
1006
  slot: MCP_OAUTH_CONNECTION_SLOT,
1130
- value: {
1131
- kind: "connection",
1132
- connectionId: ConnectionId.make(auth.connectionId)
1133
- }
1134
- }
1135
- ];
1136
- if (auth.clientIdSecretId) {
1007
+ value: httpCredentialInputToBindingValue(oauth.connection)
1008
+ });
1009
+ }
1010
+ if (oauth.clientId) {
1137
1011
  bindings.push({
1138
1012
  slot: MCP_OAUTH_CLIENT_ID_SLOT,
1139
- value: { kind: "secret", secretId: SecretId.make(auth.clientIdSecretId) }
1013
+ value: httpCredentialInputToBindingValue(oauth.clientId)
1140
1014
  });
1141
1015
  }
1142
- if (auth.clientSecretSecretId) {
1016
+ if (oauth.clientSecret) {
1143
1017
  bindings.push({
1144
1018
  slot: MCP_OAUTH_CLIENT_SECRET_SLOT,
1145
- value: { kind: "secret", secretId: SecretId.make(auth.clientSecretSecretId) }
1019
+ value: httpCredentialInputToBindingValue(oauth.clientSecret)
1146
1020
  });
1147
1021
  }
1148
1022
  return {
1149
1023
  auth: {
1150
1024
  kind: "oauth2",
1151
1025
  connectionSlot: MCP_OAUTH_CONNECTION_SLOT,
1152
- ...auth.clientIdSecretId ? { clientIdSlot: MCP_OAUTH_CLIENT_ID_SLOT } : {},
1153
- ...auth.clientSecretSecretId ? { clientSecretSlot: MCP_OAUTH_CLIENT_SECRET_SLOT } : {}
1026
+ ...oauth.clientId ? { clientIdSlot: MCP_OAUTH_CLIENT_ID_SLOT } : {},
1027
+ ...oauth.clientSecret ? { clientSecretSlot: MCP_OAUTH_CLIENT_SECRET_SLOT } : {}
1154
1028
  },
1155
1029
  bindings
1156
1030
  };
@@ -1189,21 +1063,32 @@ var resolveSecretBackedMap = (values, ctx) => resolveSharedSecretBackedMap({
1189
1063
  transport: "remote",
1190
1064
  message: `Failed to resolve secret "${value.secretId}"`
1191
1065
  }),
1192
- onError: (err, _name, value) => Predicate2.isTagged("SecretOwnedByConnectionError")(err) ? new McpConnectionError({
1066
+ onError: (err, _name, value) => Predicate3.isTagged("SecretOwnedByConnectionError")(err) ? new McpConnectionError({
1193
1067
  transport: "remote",
1194
1068
  message: `Failed to resolve secret "${value.secretId}"`
1195
1069
  }) : err
1196
1070
  }).pipe(
1197
1071
  Effect6.mapError(
1198
- (err) => Predicate2.isTagged("SecretOwnedByConnectionError")(err) ? new McpConnectionError({ transport: "remote", message: "Failed to resolve secret" }) : err
1072
+ (err) => Predicate3.isTagged("SecretOwnedByConnectionError")(err) ? new McpConnectionError({ transport: "remote", message: "Failed to resolve secret" }) : err
1199
1073
  )
1200
1074
  );
1201
- var plainStringMap = (values) => {
1075
+ var credentialInputMapToConfigValues = (values) => {
1202
1076
  if (!values) return void 0;
1203
- const entries = Object.entries(values).filter(
1204
- (entry) => typeof entry[1] === "string"
1205
- );
1206
- return entries.length > 0 ? Object.fromEntries(entries) : void 0;
1077
+ const out = {};
1078
+ for (const [name, value] of Object.entries(values)) {
1079
+ if (typeof value === "string") {
1080
+ out[name] = value;
1081
+ continue;
1082
+ }
1083
+ if (value.kind === "secret" && "secretId" in value) {
1084
+ out[name] = headerToConfigValue({ secretId: value.secretId, prefix: value.prefix });
1085
+ continue;
1086
+ }
1087
+ if (value.kind === "text") {
1088
+ out[name] = value.prefix ? `${value.prefix}${value.text}` : value.text;
1089
+ }
1090
+ }
1091
+ return Object.keys(out).length > 0 ? out : void 0;
1207
1092
  };
1208
1093
  var resolveMcpBindingValueMap = (ctx, values, params) => Effect6.gen(function* () {
1209
1094
  if (!values) return void 0;
@@ -1251,49 +1136,58 @@ var resolveMcpBindingValueMap = (ctx, values, params) => Effect6.gen(function* (
1251
1136
  }
1252
1137
  return Object.keys(resolved).length > 0 ? resolved : void 0;
1253
1138
  });
1254
- var resolveMcpCredentialInputMap = (ctx, values, params) => Effect6.gen(function* () {
1255
- if (!values) return void 0;
1139
+ var resolveInitialMcpCredentialValueMap = (ctx, values, bindings, targetScope, missingLabel) => Effect6.gen(function* () {
1140
+ const bySlot = new Map(bindings.map((binding) => [binding.slot, binding.value]));
1256
1141
  const resolved = {};
1257
1142
  for (const [name, value] of Object.entries(values)) {
1258
1143
  if (typeof value === "string") {
1259
1144
  resolved[name] = value;
1260
1145
  continue;
1261
1146
  }
1262
- if ("kind" in value) {
1263
- const slotResolved = yield* resolveMcpBindingValueMap(
1264
- ctx,
1265
- { [name]: value },
1266
- {
1267
- sourceId: params.sourceId,
1268
- sourceScope: params.sourceScope,
1269
- missingLabel: params.missingLabel
1270
- }
1147
+ const binding = bySlot.get(value.slot);
1148
+ if (binding?.kind === "secret") {
1149
+ const secret = yield* ctx.secrets.getAtScope(binding.secretId, binding.secretScopeId ?? ScopeId.make(targetScope)).pipe(
1150
+ Effect6.catchTag(
1151
+ "SecretOwnedByConnectionError",
1152
+ () => Effect6.fail(
1153
+ new McpConnectionError({
1154
+ transport: "remote",
1155
+ message: `Failed to resolve secret for ${missingLabel} "${name}"`
1156
+ })
1157
+ )
1158
+ )
1271
1159
  );
1272
- if (slotResolved?.[name] !== void 0) resolved[name] = slotResolved[name];
1160
+ if (secret === null) {
1161
+ return yield* new McpConnectionError({
1162
+ transport: "remote",
1163
+ message: `Missing secret "${binding.secretId}" for ${missingLabel} "${name}"`
1164
+ });
1165
+ }
1166
+ resolved[name] = value.prefix ? `${value.prefix}${secret}` : secret;
1273
1167
  continue;
1274
1168
  }
1275
- const secretScope = "secretScopeId" in value ? value.secretScopeId ?? value.targetScope : params.targetScope ?? params.sourceScope;
1276
- const secret = yield* ctx.secrets.getAtScope(SecretId.make(value.secretId), secretScope).pipe(
1277
- Effect6.catchTag(
1278
- "SecretOwnedByConnectionError",
1279
- () => Effect6.fail(
1280
- new McpConnectionError({
1281
- transport: "remote",
1282
- message: `Failed to resolve secret for ${params.missingLabel} "${name}"`
1283
- })
1284
- )
1285
- )
1286
- );
1287
- if (secret === null) {
1288
- return yield* new McpConnectionError({
1289
- transport: "remote",
1290
- message: `Missing secret "${value.secretId}" for ${params.missingLabel} "${name}"`
1291
- });
1169
+ if (binding?.kind === "text") {
1170
+ resolved[name] = value.prefix ? `${value.prefix}${binding.text}` : binding.text;
1292
1171
  }
1293
- resolved[name] = value.prefix ? `${value.prefix}${secret}` : secret;
1294
1172
  }
1295
1173
  return Object.keys(resolved).length > 0 ? resolved : void 0;
1296
1174
  });
1175
+ var resolveInitialMcpOauthProvider = (ctx, bindings, targetScope) => Effect6.gen(function* () {
1176
+ const connection = bindings.find(
1177
+ (binding) => binding.slot === MCP_OAUTH_CONNECTION_SLOT && binding.value.kind === "connection"
1178
+ );
1179
+ if (!connection || connection.value.kind !== "connection") return void 0;
1180
+ const connectionId = connection.value.connectionId;
1181
+ const accessToken = yield* ctx.connections.accessTokenAtScope(connectionId, ScopeId.make(targetScope)).pipe(
1182
+ Effect6.mapError(
1183
+ ({ message }) => new McpConnectionError({
1184
+ transport: "remote",
1185
+ message: `Failed to resolve OAuth connection "${connectionId}": ${message}`
1186
+ })
1187
+ )
1188
+ );
1189
+ return makeOAuthProvider(accessToken);
1190
+ });
1297
1191
  var resolveMcpHeaderAuth = (ctx, sourceId, sourceScope, auth) => Effect6.gen(function* () {
1298
1192
  if (auth.kind !== "header") return {};
1299
1193
  const binding = yield* resolveMcpSourceBinding(ctx, sourceId, sourceScope, auth.secretSlot);
@@ -1347,60 +1241,6 @@ var resolveMcpStoredOauthProvider = (ctx, sourceId, sourceScope, auth) => Effect
1347
1241
  );
1348
1242
  return makeOAuthProvider(accessToken);
1349
1243
  });
1350
- var resolveMcpInputAuth = (ctx, sourceId, sourceScope, targetScope, auth) => Effect6.gen(function* () {
1351
- if (!auth || auth.kind === "none") return { headers: {} };
1352
- if (auth.kind === "header") {
1353
- if ("secretSlot" in auth) {
1354
- const headers = yield* resolveMcpHeaderAuth(ctx, sourceId, sourceScope, auth);
1355
- return { headers };
1356
- }
1357
- const secretScope = auth.secretScopeId ?? auth.targetScope ?? targetScope ?? sourceScope;
1358
- const secret = yield* ctx.secrets.getAtScope(SecretId.make(auth.secretId), secretScope).pipe(
1359
- Effect6.catchTag(
1360
- "SecretOwnedByConnectionError",
1361
- () => Effect6.fail(
1362
- new McpConnectionError({
1363
- transport: "remote",
1364
- message: `Failed to resolve secret "${auth.secretId}"`
1365
- })
1366
- )
1367
- )
1368
- );
1369
- if (secret === null) {
1370
- return yield* new McpConnectionError({
1371
- transport: "remote",
1372
- message: `Failed to resolve secret "${auth.secretId}"`
1373
- });
1374
- }
1375
- return {
1376
- headers: { [auth.headerName]: auth.prefix ? `${auth.prefix}${secret}` : secret }
1377
- };
1378
- }
1379
- const connection = "connectionId" in auth ? { id: ConnectionId.make(auth.connectionId), scope: targetScope ?? sourceScope } : yield* Effect6.gen(function* () {
1380
- const binding = yield* resolveMcpSourceBinding(
1381
- ctx,
1382
- sourceId,
1383
- sourceScope,
1384
- auth.connectionSlot
1385
- );
1386
- return binding?.value.kind === "connection" ? { id: binding.value.connectionId, scope: binding.scopeId } : null;
1387
- });
1388
- if (connection === null) {
1389
- return yield* new McpConnectionError({
1390
- transport: "remote",
1391
- message: `Missing OAuth connection binding for MCP source "${sourceId}"`
1392
- });
1393
- }
1394
- const accessToken = yield* ctx.connections.accessTokenAtScope(connection.id, connection.scope).pipe(
1395
- Effect6.mapError(
1396
- ({ message }) => new McpConnectionError({
1397
- transport: "remote",
1398
- message: `Failed to resolve OAuth connection "${connection.id}": ${message}`
1399
- })
1400
- )
1401
- );
1402
- return { headers: {}, authProvider: makeOAuthProvider(accessToken) };
1403
- });
1404
1244
  var resolveConnectorInput = (sourceId, sourceScope, sd, ctx, allowStdio) => {
1405
1245
  if (sd.transport === "stdio") {
1406
1246
  if (!allowStdio) {
@@ -1478,23 +1318,16 @@ var makeRuntime = () => Effect6.gen(function* () {
1478
1318
  }).pipe(Scope.provide(cacheScope));
1479
1319
  return { connectionCache, pendingConnectors, cacheScope };
1480
1320
  });
1481
- var secretRef = (id) => `${SECRET_REF_PREFIX}${id}`;
1482
1321
  var authToConfig = (auth) => {
1483
1322
  if (!auth) return void 0;
1484
- if (auth.kind === "none") return { kind: "none" };
1485
- if (auth.kind === "header") {
1486
- if (!("secretId" in auth)) return void 0;
1487
- return {
1488
- kind: "header",
1489
- headerName: auth.headerName,
1490
- secret: secretRef(auth.secretId),
1491
- prefix: auth.prefix
1492
- };
1323
+ if ("kind" in auth) return { kind: "none" };
1324
+ const connection = auth.oauth2?.connection;
1325
+ if (!connection || typeof connection === "string" || connection.kind !== "connection") {
1326
+ return void 0;
1493
1327
  }
1494
- if (!("connectionId" in auth)) return void 0;
1495
1328
  return {
1496
1329
  kind: "oauth2",
1497
- connectionId: auth.connectionId
1330
+ connectionId: connection.connectionId
1498
1331
  };
1499
1332
  };
1500
1333
  var toCredentialInput = (bySlot, configured) => {
@@ -1503,7 +1336,9 @@ var toCredentialInput = (bySlot, configured) => {
1503
1336
  if (!value) return void 0;
1504
1337
  if (value.kind === "secret") {
1505
1338
  return {
1339
+ kind: "secret",
1506
1340
  secretId: value.secretId,
1341
+ ...value.secretScopeId ? { secretScope: value.secretScopeId } : {},
1507
1342
  ...configured.prefix ? { prefix: configured.prefix } : {}
1508
1343
  };
1509
1344
  }
@@ -1525,15 +1360,15 @@ var toAuthInput = (bySlot, auth) => {
1525
1360
  const value = bySlot.get(auth.secretSlot);
1526
1361
  if (value?.kind !== "secret") return void 0;
1527
1362
  return {
1528
- kind: "header",
1529
- headerName: auth.headerName,
1530
- secretId: value.secretId,
1531
- prefix: auth.prefix
1363
+ kind: "none"
1532
1364
  };
1533
1365
  }
1534
1366
  const connection = bySlot.get(auth.connectionSlot);
1535
- if (connection?.kind !== "connection") return void 0;
1536
- return { kind: "oauth2", connectionId: connection.connectionId };
1367
+ return {
1368
+ oauth2: {
1369
+ ...connection?.kind === "connection" ? { connection: { kind: "connection", connectionId: connection.connectionId } } : {}
1370
+ }
1371
+ };
1537
1372
  };
1538
1373
  var inputFormFromStored = (bindings, stored, scope, sourceName, namespace) => {
1539
1374
  if (stored.transport === "stdio") {
@@ -1581,8 +1416,8 @@ var toMcpConfigEntry = (namespace, sourceName, config) => {
1581
1416
  name: sourceName,
1582
1417
  endpoint: config.endpoint,
1583
1418
  remoteTransport: config.remoteTransport,
1584
- queryParams: plainStringMap(config.queryParams),
1585
- headers: plainStringMap(config.headers),
1419
+ queryParams: credentialInputMapToConfigValues(config.queryParams),
1420
+ headers: credentialInputMapToConfigValues(config.headers),
1586
1421
  namespace,
1587
1422
  auth: authToConfig(config.auth)
1588
1423
  };
@@ -1601,6 +1436,13 @@ var mcpPlugin = definePlugin((options) => {
1601
1436
  return {
1602
1437
  id: "mcp",
1603
1438
  packageName: "@executor-js/plugin-mcp",
1439
+ sourcePresets: allowStdio ? mcpPresets.map((preset) => ({
1440
+ ...preset,
1441
+ transport: "transport" in preset ? preset.transport : "remote"
1442
+ })) : mcpPresets.filter((preset) => !("transport" in preset && preset.transport === "stdio")).map((preset) => ({
1443
+ ...preset,
1444
+ transport: "remote"
1445
+ })),
1604
1446
  // Surfaced to the client bundle via the Vite plugin (see
1605
1447
  // `@executor-js/vite-plugin`). The MCP `./client` factory reads
1606
1448
  // `allowStdio` and gates the stdio tab + presets in AddMcpSource —
@@ -1693,72 +1535,72 @@ var mcpPlugin = definePlugin((options) => {
1693
1535
  const addSource = (config) => Effect6.gen(function* () {
1694
1536
  const namespace = normalizeNamespace(config);
1695
1537
  const canonicalRemote = config.transport === "remote" ? {
1696
- headers: canonicalizeCredentialMap(config.headers, mcpHeaderSlot),
1697
- queryParams: canonicalizeCredentialMap(config.queryParams, mcpQueryParamSlot),
1698
- auth: canonicalizeAuth(config.auth)
1538
+ headers: canonicalizeConfiguredValueMap(config.headers, mcpHeaderSlot),
1539
+ queryParams: canonicalizeConfiguredValueMap(
1540
+ config.queryParams,
1541
+ mcpQueryParamSlot
1542
+ )
1699
1543
  } : null;
1700
- const directBindings = canonicalRemote ? [
1701
- ...canonicalRemote.headers.bindings,
1702
- ...canonicalRemote.queryParams.bindings,
1703
- ...canonicalRemote.auth.bindings
1704
- ] : [];
1705
- for (const binding of directBindings) {
1706
- const bindingTargetScope2 = yield* targetScopeForBinding(
1707
- config.transport === "remote" ? config.credentialTargetScope : void 0,
1708
- binding
1709
- );
1544
+ const initialRemote = config.transport === "remote" && config.credentials ? {
1545
+ scope: config.credentials.scope,
1546
+ headers: config.credentials.headers !== void 0 ? canonicalizeCredentialMap(config.credentials.headers, mcpHeaderSlot) : null,
1547
+ queryParams: config.credentials.queryParams !== void 0 ? canonicalizeCredentialMap(config.credentials.queryParams, mcpQueryParamSlot) : null,
1548
+ auth: config.credentials.auth !== void 0 ? canonicalizeAuth(config.credentials.auth) : null
1549
+ } : null;
1550
+ const remoteAuth = config.transport === "remote" ? config.oauth2 ? authFromOAuth2Source(config.oauth2) : initialRemote?.auth?.auth ?? { kind: "none" } : null;
1551
+ const remoteCredentials = canonicalRemote && remoteAuth ? {
1552
+ headers: canonicalRemote.headers,
1553
+ queryParams: canonicalRemote.queryParams,
1554
+ auth: remoteAuth
1555
+ } : void 0;
1556
+ const initialBindings = [
1557
+ ...initialRemote?.headers?.bindings ?? [],
1558
+ ...initialRemote?.queryParams?.bindings ?? [],
1559
+ ...initialRemote?.auth?.bindings ?? []
1560
+ ];
1561
+ if (initialRemote && initialBindings.length > 0) {
1710
1562
  yield* validateMcpBindingTarget(ctx, {
1711
1563
  sourceId: namespace,
1712
1564
  sourceScope: config.scope,
1713
- targetScope: bindingTargetScope2
1565
+ targetScope: initialRemote.scope
1714
1566
  });
1715
1567
  }
1716
- const targetScope = config.transport === "remote" && directBindings[0] ? yield* targetScopeForBinding(config.credentialTargetScope, directBindings[0]) : void 0;
1717
- const sd = toStoredSourceData(
1718
- config,
1719
- canonicalRemote ? {
1720
- headers: canonicalRemote.headers.values,
1721
- queryParams: canonicalRemote.queryParams.values,
1722
- auth: canonicalRemote.auth.auth
1723
- } : void 0
1724
- );
1725
- const resolved = yield* (config.transport === "remote" ? Effect6.gen(function* () {
1726
- const resolvedHeaders = yield* resolveMcpCredentialInputMap(ctx, config.headers, {
1727
- sourceId: namespace,
1728
- sourceScope: config.scope,
1729
- targetScope,
1730
- missingLabel: "header"
1731
- });
1732
- const resolvedQueryParams = yield* resolveMcpCredentialInputMap(
1733
- ctx,
1734
- config.queryParams,
1735
- {
1736
- sourceId: namespace,
1737
- sourceScope: config.scope,
1738
- targetScope,
1739
- missingLabel: "query parameter"
1740
- }
1741
- );
1742
- const resolvedAuth = yield* resolveMcpInputAuth(
1743
- ctx,
1744
- namespace,
1745
- config.scope,
1746
- targetScope,
1747
- config.auth
1748
- );
1749
- const headers = {
1750
- ...resolvedHeaders ?? {},
1751
- ...resolvedAuth.headers
1752
- };
1753
- return {
1754
- transport: "remote",
1755
- endpoint: config.endpoint,
1756
- remoteTransport: config.remoteTransport ?? "auto",
1757
- queryParams: resolvedQueryParams,
1758
- headers: Object.keys(headers).length > 0 ? headers : void 0,
1759
- authProvider: resolvedAuth.authProvider
1760
- };
1761
- }) : resolveConnectorInput(namespace, config.scope, sd, ctx, allowStdio)).pipe(
1568
+ const sd = toStoredSourceData(config, remoteCredentials);
1569
+ const initialQueryParams = initialRemote?.queryParams && (yield* resolveInitialMcpCredentialValueMap(
1570
+ ctx,
1571
+ canonicalRemote?.queryParams ?? initialRemote.queryParams.values,
1572
+ initialRemote.queryParams.bindings,
1573
+ initialRemote.scope,
1574
+ "query parameter"
1575
+ ));
1576
+ const initialHeaders = initialRemote?.headers && (yield* resolveInitialMcpCredentialValueMap(
1577
+ ctx,
1578
+ canonicalRemote?.headers ?? initialRemote.headers.values,
1579
+ initialRemote.headers.bindings,
1580
+ initialRemote.scope,
1581
+ "header"
1582
+ ));
1583
+ const remoteQueryParams = {
1584
+ ...config.transport === "remote" ? resolveConfiguredValueMap(config.queryParams) ?? {} : {},
1585
+ ...initialQueryParams || {}
1586
+ };
1587
+ const remoteHeaders = {
1588
+ ...config.transport === "remote" ? resolveConfiguredValueMap(config.headers) ?? {} : {},
1589
+ ...initialHeaders || {}
1590
+ };
1591
+ const initialAuthProvider = initialRemote?.auth !== null && initialRemote?.auth !== void 0 ? yield* resolveInitialMcpOauthProvider(
1592
+ ctx,
1593
+ initialRemote.auth.bindings,
1594
+ initialRemote.scope
1595
+ ) : void 0;
1596
+ const resolved = config.transport === "remote" ? Result.succeed({
1597
+ transport: "remote",
1598
+ endpoint: config.endpoint,
1599
+ remoteTransport: config.remoteTransport ?? "auto",
1600
+ queryParams: Object.keys(remoteQueryParams).length > 0 ? remoteQueryParams : void 0,
1601
+ headers: Object.keys(remoteHeaders).length > 0 ? remoteHeaders : void 0,
1602
+ authProvider: initialAuthProvider
1603
+ }) : yield* resolveConnectorInput(namespace, config.scope, sd, ctx, allowStdio).pipe(
1762
1604
  Effect6.result,
1763
1605
  Effect6.withSpan("mcp.plugin.resolve_connector", {
1764
1606
  attributes: {
@@ -1818,21 +1660,22 @@ var mcpPlugin = definePlugin((options) => {
1818
1660
  outputSchema: e.outputSchema
1819
1661
  }))
1820
1662
  });
1821
- if (directBindings.length > 0) {
1822
- for (const binding of directBindings) {
1823
- const bindingTargetScope2 = yield* targetScopeForBinding(
1824
- config.transport === "remote" ? config.credentialTargetScope : void 0,
1825
- binding
1826
- );
1827
- yield* ctx.credentialBindings.set({
1828
- targetScope: ScopeId.make(bindingTargetScope2),
1829
- pluginId: MCP_PLUGIN_ID,
1830
- sourceId: namespace,
1831
- sourceScope: ScopeId.make(config.scope),
1663
+ if (initialRemote && initialBindings.length > 0) {
1664
+ yield* ctx.credentialBindings.replaceForSource({
1665
+ targetScope: ScopeId.make(initialRemote.scope),
1666
+ pluginId: MCP_PLUGIN_ID,
1667
+ sourceId: namespace,
1668
+ sourceScope: ScopeId.make(config.scope),
1669
+ slotPrefixes: [
1670
+ ...initialRemote.headers !== null ? ["header:"] : [],
1671
+ ...initialRemote.queryParams !== null ? ["query_param:"] : [],
1672
+ ...initialRemote.auth !== null ? ["auth:"] : []
1673
+ ],
1674
+ bindings: initialBindings.map((binding) => ({
1832
1675
  slotKey: binding.slot,
1833
1676
  value: binding.value
1834
- });
1835
- }
1677
+ }))
1678
+ });
1836
1679
  }
1837
1680
  })
1838
1681
  ).pipe(
@@ -1955,8 +1798,25 @@ var mcpPlugin = definePlugin((options) => {
1955
1798
  attributes: { "mcp.source.namespace": namespace }
1956
1799
  })
1957
1800
  );
1958
- const updateSource = (namespace, scope, input) => Effect6.gen(function* () {
1959
- const existing = yield* ctx.storage.getSource(namespace, scope);
1801
+ const getSource = (namespace, scope) => ctx.storage.getSource(namespace, scope).pipe(
1802
+ Effect6.withSpan("mcp.plugin.get_source", {
1803
+ attributes: { "mcp.source.namespace": namespace }
1804
+ })
1805
+ );
1806
+ return {
1807
+ probeEndpoint,
1808
+ addSource,
1809
+ removeSource,
1810
+ refreshSource,
1811
+ getSource
1812
+ };
1813
+ },
1814
+ sourceConfigure: {
1815
+ type: "mcp",
1816
+ schema: McpConfigureSourcePayloadSchema,
1817
+ configure: ({ ctx, sourceId, sourceScope, targetScope, config }) => Effect6.gen(function* () {
1818
+ const input = config;
1819
+ const existing = yield* ctx.storage.getSource(sourceId, sourceScope);
1960
1820
  if (!existing || existing.config.transport !== "remote") return;
1961
1821
  const canonicalHeaders = input.headers !== void 0 ? canonicalizeCredentialMap(input.headers, mcpHeaderSlot) : null;
1962
1822
  const canonicalQueryParams = input.queryParams !== void 0 ? canonicalizeCredentialMap(input.queryParams, mcpQueryParamSlot) : null;
@@ -1966,46 +1826,40 @@ var mcpPlugin = definePlugin((options) => {
1966
1826
  ...canonicalQueryParams?.bindings ?? [],
1967
1827
  ...canonicalAuth?.bindings ?? []
1968
1828
  ];
1969
- const targetScope = yield* bindingTargetScope(
1970
- input.credentialTargetScope,
1971
- directBindings
1972
- );
1973
- if (targetScope) {
1829
+ if (directBindings.length > 0) {
1974
1830
  yield* validateMcpBindingTarget(ctx, {
1975
- sourceId: namespace,
1976
- sourceScope: scope,
1831
+ sourceId,
1832
+ sourceScope,
1977
1833
  targetScope
1978
1834
  });
1979
1835
  }
1980
- const remote = existing.config;
1981
1836
  const updatedConfig = {
1982
- ...remote,
1837
+ ...existing.config,
1983
1838
  ...input.endpoint !== void 0 ? { endpoint: input.endpoint } : {},
1984
1839
  ...canonicalHeaders ? { headers: canonicalHeaders.values } : {},
1985
1840
  ...canonicalAuth ? { auth: canonicalAuth.auth } : {},
1986
1841
  ...canonicalQueryParams ? { queryParams: canonicalQueryParams.values } : {}
1987
1842
  };
1988
- const sourceName = input.name?.trim() || existing.name;
1989
1843
  const affectedPrefixes = [
1990
1844
  ...input.headers !== void 0 ? ["header:"] : [],
1991
1845
  ...input.queryParams !== void 0 ? ["query_param:"] : [],
1992
1846
  ...input.auth !== void 0 ? ["auth:"] : []
1993
1847
  ];
1994
- const replacementTargetScope = targetScope ?? input.credentialTargetScope ?? scope;
1848
+ const sourceName = input.name?.trim() || existing.name;
1995
1849
  yield* ctx.transaction(
1996
1850
  Effect6.gen(function* () {
1997
1851
  yield* ctx.storage.putSource({
1998
- namespace,
1999
- scope,
1852
+ namespace: sourceId,
1853
+ scope: sourceScope,
2000
1854
  name: sourceName,
2001
1855
  config: updatedConfig
2002
1856
  });
2003
1857
  if (affectedPrefixes.length > 0 || directBindings.length > 0) {
2004
1858
  yield* ctx.credentialBindings.replaceForSource({
2005
- targetScope: ScopeId.make(replacementTargetScope),
1859
+ targetScope: ScopeId.make(targetScope),
2006
1860
  pluginId: MCP_PLUGIN_ID,
2007
- sourceId: namespace,
2008
- sourceScope: ScopeId.make(scope),
1861
+ sourceId,
1862
+ sourceScope: ScopeId.make(sourceScope),
2009
1863
  slotPrefixes: affectedPrefixes,
2010
1864
  bindings: directBindings.map((binding) => ({
2011
1865
  slotKey: binding.slot,
@@ -2015,71 +1869,168 @@ var mcpPlugin = definePlugin((options) => {
2015
1869
  }
2016
1870
  })
2017
1871
  );
2018
- if (configFile) {
1872
+ if (options?.configFile) {
2019
1873
  const bindings = yield* ctx.credentialBindings.listForSource({
2020
1874
  pluginId: MCP_PLUGIN_ID,
2021
- sourceId: namespace,
2022
- sourceScope: ScopeId.make(scope)
1875
+ sourceId,
1876
+ sourceScope: ScopeId.make(sourceScope)
2023
1877
  });
2024
1878
  const inputForm = inputFormFromStored(
2025
1879
  bindings,
2026
1880
  updatedConfig,
2027
- scope,
1881
+ sourceScope,
2028
1882
  sourceName,
2029
- namespace
1883
+ sourceId
2030
1884
  );
2031
- yield* configFile.upsertSource(toMcpConfigEntry(namespace, sourceName, inputForm)).pipe(Effect6.withSpan("mcp.plugin.config_file.upsert"));
1885
+ yield* options.configFile.upsertSource(toMcpConfigEntry(sourceId, sourceName, inputForm)).pipe(Effect6.withSpan("mcp.plugin.config_file.upsert"));
2032
1886
  }
2033
- }).pipe(
2034
- Effect6.withSpan("mcp.plugin.update_source", {
2035
- attributes: { "mcp.source.namespace": namespace }
2036
- })
2037
- );
2038
- const getSource = (namespace, scope) => ctx.storage.getSource(namespace, scope).pipe(
2039
- Effect6.withSpan("mcp.plugin.get_source", {
2040
- attributes: { "mcp.source.namespace": namespace }
2041
- })
2042
- );
2043
- return {
2044
- probeEndpoint,
2045
- addSource,
2046
- removeSource,
2047
- refreshSource,
2048
- getSource,
2049
- updateSource,
2050
- listSourceBindings: (sourceId, sourceScope) => listMcpSourceBindings(ctx, sourceId, sourceScope),
2051
- setSourceBinding: (input) => Effect6.gen(function* () {
2052
- yield* validateMcpBindingTarget(ctx, {
2053
- sourceId: input.sourceId,
2054
- sourceScope: input.sourceScope,
2055
- targetScope: input.scope
2056
- });
2057
- const binding = yield* ctx.credentialBindings.set({
2058
- targetScope: input.scope,
2059
- pluginId: MCP_PLUGIN_ID,
2060
- sourceId: input.sourceId,
2061
- sourceScope: input.sourceScope,
2062
- slotKey: input.slot,
2063
- value: input.value
2064
- });
2065
- return coreBindingToMcpBinding(binding);
2066
- }),
2067
- removeSourceBinding: (sourceId, sourceScope, slot, scope) => Effect6.gen(function* () {
2068
- yield* validateMcpBindingTarget(ctx, {
2069
- sourceId,
2070
- sourceScope,
2071
- targetScope: scope
2072
- });
2073
- yield* ctx.credentialBindings.remove({
2074
- targetScope: ScopeId.make(scope),
2075
- pluginId: MCP_PLUGIN_ID,
2076
- sourceId,
2077
- sourceScope: ScopeId.make(sourceScope),
2078
- slotKey: slot
2079
- });
2080
- })
2081
- };
1887
+ })
2082
1888
  },
1889
+ staticSources: (self) => [
1890
+ {
1891
+ id: "mcp",
1892
+ kind: "executor",
1893
+ name: "MCP",
1894
+ tools: [
1895
+ tool({
1896
+ name: "probeEndpoint",
1897
+ description: "Probe a remote MCP endpoint before adding it. If the result requires OAuth, call `executor.coreTools.oauth.probe` and `executor.coreTools.oauth.start` with `credentialScope` set to the user's chosen personal or organization credential scope first, then pass the resulting connection through `addSource` credentials or `mcp.configureSource`.",
1898
+ inputSchema: McpProbeEndpointInputStandardSchema,
1899
+ outputSchema: McpProbeEndpointOutputStandardSchema,
1900
+ execute: (input) => self.probeEndpoint(input).pipe(
1901
+ Effect6.map(ToolResult.ok),
1902
+ Effect6.catchTag(
1903
+ "McpConnectionError",
1904
+ ({ message, transport }) => Effect6.succeed(mcpToolFailure("mcp_connection_failed", message, { transport }))
1905
+ )
1906
+ )
1907
+ }),
1908
+ tool({
1909
+ name: "getSource",
1910
+ description: "Inspect an existing MCP source, including transport, endpoint/command, auth mode, configured headers/query params, and credential slots. Use this before repairing an existing source with `mcp.configureSource`, `secrets.create`, or `oauth.start`.",
1911
+ inputSchema: McpGetSourceInputStandardSchema,
1912
+ outputSchema: McpGetSourceOutputStandardSchema,
1913
+ execute: (input, { ctx }) => {
1914
+ const args = input;
1915
+ return Effect6.map(
1916
+ self.getSource(args.namespace, resolveStaticScopeInput(ctx, args.scope)),
1917
+ (source) => ToolResult.ok({ source })
1918
+ );
1919
+ }
1920
+ }),
1921
+ tool({
1922
+ name: "addSource",
1923
+ description: "Add an MCP source and register its tools. Executor chooses the source install scope (local scope locally, organization scope in cloud) and returns it as `source`. For remote OAuth-protected servers, first use `probeEndpoint` and the core OAuth browser handoff (`oauth.probe`, `oauth.start` with the user's chosen `credentialScope`), then bind the completed connection with `mcp.configureSource` if needed. For header/API-key auth, first call `secrets.create` at the user's chosen credential scope so the value is entered in the browser, then pass the secret reference in `credentials`. Remote sources are still saved if discovery fails; inspect the returned `discovery` field and use `sources.refresh` after credentials or network access are fixed.",
1924
+ annotations: {
1925
+ requiresApproval: true,
1926
+ approvalDescription: "Add an MCP source"
1927
+ },
1928
+ inputSchema: McpAddSourceInputStandardSchema,
1929
+ outputSchema: McpAddSourceOutputStandardSchema,
1930
+ execute: (rawInput, { ctx }) => {
1931
+ const input = rawInput;
1932
+ const sourceScope = defaultSourceInstallScopeId(ctx.scopes);
1933
+ if (sourceScope === null) {
1934
+ return Effect6.succeed(
1935
+ mcpToolFailure(
1936
+ "source_scope_unavailable",
1937
+ "Cannot add an MCP source because this executor has no source install scope."
1938
+ )
1939
+ );
1940
+ }
1941
+ const normalizedInput = {
1942
+ ...input,
1943
+ scope: sourceScope
1944
+ };
1945
+ const added = self.addSource(normalizedInput).pipe(
1946
+ Effect6.map(
1947
+ (result) => ToolResult.ok({
1948
+ ...result,
1949
+ source: { id: result.namespace, scope: sourceScope },
1950
+ discovery: { status: "ok" }
1951
+ })
1952
+ )
1953
+ );
1954
+ if (normalizedInput.transport !== "remote") return added;
1955
+ const savedWithDiscoveryFailure = (failure) => Effect6.succeed(
1956
+ ToolResult.ok({
1957
+ namespace: normalizedInput.namespace ?? deriveMcpNamespace({
1958
+ name: normalizedInput.name,
1959
+ endpoint: normalizedInput.endpoint
1960
+ }),
1961
+ source: {
1962
+ id: normalizedInput.namespace ?? deriveMcpNamespace({
1963
+ name: normalizedInput.name,
1964
+ endpoint: normalizedInput.endpoint
1965
+ }),
1966
+ scope: sourceScope
1967
+ },
1968
+ toolCount: 0,
1969
+ discovery: {
1970
+ status: "failed",
1971
+ message: failure.message,
1972
+ ...failure.stage ? { stage: failure.stage } : {}
1973
+ }
1974
+ })
1975
+ );
1976
+ return added.pipe(
1977
+ Effect6.catchTags({
1978
+ McpToolDiscoveryError: savedWithDiscoveryFailure,
1979
+ McpConnectionError: ({ message }) => Effect6.succeed(
1980
+ ToolResult.ok({
1981
+ namespace: normalizedInput.namespace ?? deriveMcpNamespace({
1982
+ name: normalizedInput.name,
1983
+ endpoint: normalizedInput.endpoint
1984
+ }),
1985
+ source: {
1986
+ id: normalizedInput.namespace ?? deriveMcpNamespace({
1987
+ name: normalizedInput.name,
1988
+ endpoint: normalizedInput.endpoint
1989
+ }),
1990
+ scope: sourceScope
1991
+ },
1992
+ toolCount: 0,
1993
+ discovery: {
1994
+ status: "failed",
1995
+ message
1996
+ }
1997
+ })
1998
+ )
1999
+ })
2000
+ );
2001
+ }
2002
+ }),
2003
+ tool({
2004
+ name: "configureSource",
2005
+ description: 'Configure an existing remote MCP source with concrete fields. Use `source` returned by `mcp.addSource` or `sources.list`. The top-level `scope` is the credential target scope for bindings; in cloud, choose the user or organization credential scope deliberately. Pass secret refs as `{kind:"secret", secretId}` and OAuth connections as `{kind:"connection", connectionId}`.',
2006
+ annotations: {
2007
+ requiresApproval: true,
2008
+ approvalDescription: "Configure an MCP source"
2009
+ },
2010
+ inputSchema: McpStaticConfigureSourceInputStandardSchema,
2011
+ outputSchema: McpStaticConfigureSourceOutputStandardSchema,
2012
+ execute: (rawInput, { ctx }) => Effect6.gen(function* () {
2013
+ const { source, ...config } = rawInput;
2014
+ const sourceScope = resolveStaticScopeInput(ctx, source.scope);
2015
+ const targetScope = resolveStaticScopeInput(ctx, config.scope);
2016
+ yield* ctx.core.sources.configure({
2017
+ source: { id: source.id, scope: sourceScope },
2018
+ scope: targetScope,
2019
+ type: "mcp",
2020
+ config: {
2021
+ ...config.name !== void 0 ? { name: config.name } : {},
2022
+ ...config.endpoint !== void 0 ? { endpoint: config.endpoint } : {},
2023
+ ...config.headers !== void 0 ? { headers: config.headers } : {},
2024
+ ...config.queryParams !== void 0 ? { queryParams: config.queryParams } : {},
2025
+ ...config.auth !== void 0 ? { auth: config.auth } : {}
2026
+ }
2027
+ });
2028
+ return ToolResult.ok({ configured: true });
2029
+ })
2030
+ })
2031
+ ]
2032
+ }
2033
+ ],
2083
2034
  invokeTool: ({ ctx, toolRow, args, elicit }) => Effect6.gen(function* () {
2084
2035
  const runtime = yield* ensureRuntime();
2085
2036
  const toolScope = toolRow.scope_id;
@@ -2105,7 +2056,7 @@ var mcpPlugin = definePlugin((options) => {
2105
2056
  message: `No MCP source config for namespace "${entry.namespace}"`
2106
2057
  });
2107
2058
  }
2108
- return yield* invokeMcpTool({
2059
+ const raw = yield* invokeMcpTool({
2109
2060
  toolId: toolRow.id,
2110
2061
  toolName: entry.binding.toolName,
2111
2062
  args,
@@ -2140,6 +2091,15 @@ var mcpPlugin = definePlugin((options) => {
2140
2091
  pendingConnectors: runtime.pendingConnectors,
2141
2092
  elicit
2142
2093
  });
2094
+ const envelope = Option5.getOrUndefined(decodeMcpToolCallEnvelope(raw));
2095
+ if (envelope?.isError === true) {
2096
+ return ToolResult.fail({
2097
+ code: "mcp_tool_error",
2098
+ message: extractMcpErrorMessage(envelope.content),
2099
+ details: { content: envelope.content }
2100
+ });
2101
+ }
2102
+ return ToolResult.ok(raw);
2143
2103
  }).pipe(
2144
2104
  Effect6.withSpan("mcp.plugin.invoke_tool", {
2145
2105
  attributes: {
@@ -2206,8 +2166,8 @@ var mcpPlugin = definePlugin((options) => {
2206
2166
  // Honor upstream destructiveHint from MCP ToolAnnotations.
2207
2167
  // Bindings are fetched per scope so shadowed sources (e.g. an org-level
2208
2168
  // source overridden per-user) each resolve against their own scope's
2209
- // row rather than collapsing onto whichever row the scoped adapter
2210
- // sees first.
2169
+ // row rather than collapsing onto whichever visible row would otherwise
2170
+ // win first.
2211
2171
  resolveAnnotations: ({ ctx, sourceId, toolRows }) => Effect6.gen(function* () {
2212
2172
  const scopes = new Set(toolRows.map((row) => row.scope_id));
2213
2173
  const entries = yield* Effect6.forEach(
@@ -2274,4 +2234,4 @@ export {
2274
2234
  makeMcpStore,
2275
2235
  mcpPlugin
2276
2236
  };
2277
- //# sourceMappingURL=chunk-NQT7NAGE.js.map
2237
+ //# sourceMappingURL=chunk-H5PLTEMB.js.map