@executor-js/plugin-mcp 1.4.29 → 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-2DOCEPYN.js → chunk-H5PLTEMB.js} +632 -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-2DOCEPYN.js.map +0 -1
  44. package/dist/chunk-4ORPFRLI.js.map +0 -1
  45. package/dist/chunk-M6REVU6O.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
  }
@@ -975,8 +747,117 @@ var probeMcpEndpointShape = (endpoint, options = {}) => Effect5.gen(function* ()
975
747
 
976
748
  // src/sdk/plugin.ts
977
749
  import {
978
- SECRET_REF_PREFIX
750
+ headerToConfigValue
979
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
+ );
980
861
  var toStoredSourceData = (config, remoteCredentials) => {
981
862
  if (config.transport === "stdio") {
982
863
  return {
@@ -1010,6 +891,22 @@ var toBinding = (entry) => McpToolBinding.make({
1010
891
  annotations: entry.annotations
1011
892
  });
1012
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
+ };
1013
910
  var urlMatchesToken = (url, token) => {
1014
911
  const re = new RegExp(`(?:^|[^a-z0-9])${token}(?:$|[^a-z0-9])`, "i");
1015
912
  return re.test(url.hostname) || re.test(url.pathname);
@@ -1032,26 +929,6 @@ var userFacingProbeMessage = (shape) => {
1032
929
  };
1033
930
  var scopeRanks = (ctx) => new Map(ctx.scopes.map((scope, index) => [String(scope.id), index]));
1034
931
  var scopeRank = (ranks, scopeId) => ranks.get(scopeId) ?? Infinity;
1035
- var coreBindingToMcpBinding = (binding) => McpSourceBindingRef.make({
1036
- sourceId: binding.sourceId,
1037
- sourceScopeId: binding.sourceScopeId,
1038
- scopeId: binding.scopeId,
1039
- slot: binding.slotKey,
1040
- value: binding.value,
1041
- createdAt: binding.createdAt,
1042
- updatedAt: binding.updatedAt
1043
- });
1044
- var listMcpSourceBindings = (ctx, sourceId, sourceScope) => Effect6.gen(function* () {
1045
- const ranks = scopeRanks(ctx);
1046
- const sourceSourceRank = scopeRank(ranks, sourceScope);
1047
- if (sourceSourceRank === Infinity) return [];
1048
- const bindings = yield* ctx.credentialBindings.listForSource({
1049
- pluginId: MCP_PLUGIN_ID,
1050
- sourceId,
1051
- sourceScope: ScopeId.make(sourceScope)
1052
- });
1053
- return bindings.filter((binding) => scopeRank(ranks, binding.scopeId) <= sourceSourceRank).map(coreBindingToMcpBinding);
1054
- });
1055
932
  var resolveMcpSourceBinding = (ctx, sourceId, sourceScope, slot) => Effect6.gen(function* () {
1056
933
  const ranks = scopeRanks(ctx);
1057
934
  const sourceSourceRank = scopeRank(ranks, sourceScope);
@@ -1064,7 +941,7 @@ var resolveMcpSourceBinding = (ctx, sourceId, sourceScope, slot) => Effect6.gen(
1064
941
  const binding = bindings.filter(
1065
942
  (candidate) => candidate.slotKey === slot && scopeRank(ranks, candidate.scopeId) <= sourceSourceRank
1066
943
  ).sort((a, b) => scopeRank(ranks, a.scopeId) - scopeRank(ranks, b.scopeId))[0];
1067
- return binding ? coreBindingToMcpBinding(binding) : null;
944
+ return binding ?? null;
1068
945
  });
1069
946
  var validateMcpBindingTarget = (ctx, input) => Effect6.gen(function* () {
1070
947
  const ranks = scopeRanks(ctx);
@@ -1090,108 +967,64 @@ var validateMcpBindingTarget = (ctx, input) => Effect6.gen(function* () {
1090
967
  });
1091
968
  }
1092
969
  });
1093
- var bindingTargetScope = (targetScope, bindings) => {
1094
- if (bindings.length === 0) return Effect6.succeed(void 0);
1095
- if (targetScope) return Effect6.succeed(targetScope);
1096
- return Effect6.fail(
1097
- new McpConnectionError({
1098
- transport: "remote",
1099
- message: "credentialTargetScope is required when adding direct MCP credentials"
1100
- })
1101
- );
1102
- };
1103
- var targetScopeForBinding = (fallbackTargetScope, binding) => {
1104
- const targetScope = binding.targetScope ?? fallbackTargetScope;
1105
- if (targetScope) return Effect6.succeed(targetScope);
1106
- return Effect6.fail(
1107
- new McpConnectionError({
1108
- transport: "remote",
1109
- message: "credentialTargetScope is required when adding direct MCP credentials"
1110
- })
1111
- );
1112
- };
1113
- var canonicalizeCredentialMap = (values, slotForName) => {
1114
- const nextValues = {};
1115
- const bindings = [];
970
+ var canonicalizeCredentialMap = compileHttpNamedCredentialMap;
971
+ var canonicalizeConfiguredValueMap = (values, slotForName) => {
972
+ const next = {};
1116
973
  for (const [name, value] of Object.entries(values ?? {})) {
1117
974
  if (typeof value === "string") {
1118
- nextValues[name] = value;
1119
- continue;
1120
- }
1121
- if ("kind" in value) {
1122
- nextValues[name] = value;
975
+ next[name] = value;
1123
976
  continue;
1124
977
  }
1125
- const slot = slotForName(name);
1126
- nextValues[name] = ConfiguredCredentialBinding2.make({
978
+ next[name] = {
1127
979
  kind: "binding",
1128
- slot,
980
+ slot: slotForName(name),
1129
981
  prefix: value.prefix
1130
- });
1131
- bindings.push({
1132
- slot,
1133
- targetScope: "targetScope" in value ? value.targetScope : void 0,
1134
- value: {
1135
- kind: "secret",
1136
- secretId: SecretId.make(value.secretId),
1137
- ..."secretScopeId" in value && value.secretScopeId ? { secretScopeId: value.secretScopeId } : {}
1138
- }
1139
- });
982
+ };
1140
983
  }
1141
- return { values: nextValues, bindings };
984
+ return next;
1142
985
  };
1143
- var canonicalizeAuth = (auth) => {
1144
- if (!auth || auth.kind === "none") return { auth: { kind: "none" }, bindings: [] };
1145
- if (auth.kind === "header") {
1146
- if ("secretSlot" in auth) return { auth, bindings: [] };
1147
- return {
1148
- auth: {
1149
- kind: "header",
1150
- headerName: auth.headerName,
1151
- secretSlot: MCP_HEADER_AUTH_SLOT,
1152
- prefix: auth.prefix
1153
- },
1154
- bindings: [
1155
- {
1156
- slot: MCP_HEADER_AUTH_SLOT,
1157
- targetScope: auth.targetScope,
1158
- value: {
1159
- kind: "secret",
1160
- secretId: SecretId.make(auth.secretId),
1161
- ...auth.secretScopeId ? { secretScopeId: auth.secretScopeId } : {}
1162
- }
1163
- }
1164
- ]
1165
- };
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;
1166
991
  }
1167
- if ("connectionSlot" in auth) return { auth, bindings: [] };
1168
- const bindings = [
1169
- {
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({
1170
1006
  slot: MCP_OAUTH_CONNECTION_SLOT,
1171
- value: {
1172
- kind: "connection",
1173
- connectionId: ConnectionId.make(auth.connectionId)
1174
- }
1175
- }
1176
- ];
1177
- if (auth.clientIdSecretId) {
1007
+ value: httpCredentialInputToBindingValue(oauth.connection)
1008
+ });
1009
+ }
1010
+ if (oauth.clientId) {
1178
1011
  bindings.push({
1179
1012
  slot: MCP_OAUTH_CLIENT_ID_SLOT,
1180
- value: { kind: "secret", secretId: SecretId.make(auth.clientIdSecretId) }
1013
+ value: httpCredentialInputToBindingValue(oauth.clientId)
1181
1014
  });
1182
1015
  }
1183
- if (auth.clientSecretSecretId) {
1016
+ if (oauth.clientSecret) {
1184
1017
  bindings.push({
1185
1018
  slot: MCP_OAUTH_CLIENT_SECRET_SLOT,
1186
- value: { kind: "secret", secretId: SecretId.make(auth.clientSecretSecretId) }
1019
+ value: httpCredentialInputToBindingValue(oauth.clientSecret)
1187
1020
  });
1188
1021
  }
1189
1022
  return {
1190
1023
  auth: {
1191
1024
  kind: "oauth2",
1192
1025
  connectionSlot: MCP_OAUTH_CONNECTION_SLOT,
1193
- ...auth.clientIdSecretId ? { clientIdSlot: MCP_OAUTH_CLIENT_ID_SLOT } : {},
1194
- ...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 } : {}
1195
1028
  },
1196
1029
  bindings
1197
1030
  };
@@ -1230,21 +1063,32 @@ var resolveSecretBackedMap = (values, ctx) => resolveSharedSecretBackedMap({
1230
1063
  transport: "remote",
1231
1064
  message: `Failed to resolve secret "${value.secretId}"`
1232
1065
  }),
1233
- onError: (err, _name, value) => Predicate2.isTagged("SecretOwnedByConnectionError")(err) ? new McpConnectionError({
1066
+ onError: (err, _name, value) => Predicate3.isTagged("SecretOwnedByConnectionError")(err) ? new McpConnectionError({
1234
1067
  transport: "remote",
1235
1068
  message: `Failed to resolve secret "${value.secretId}"`
1236
1069
  }) : err
1237
1070
  }).pipe(
1238
1071
  Effect6.mapError(
1239
- (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
1240
1073
  )
1241
1074
  );
1242
- var plainStringMap = (values) => {
1075
+ var credentialInputMapToConfigValues = (values) => {
1243
1076
  if (!values) return void 0;
1244
- const entries = Object.entries(values).filter(
1245
- (entry) => typeof entry[1] === "string"
1246
- );
1247
- 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;
1248
1092
  };
1249
1093
  var resolveMcpBindingValueMap = (ctx, values, params) => Effect6.gen(function* () {
1250
1094
  if (!values) return void 0;
@@ -1292,49 +1136,58 @@ var resolveMcpBindingValueMap = (ctx, values, params) => Effect6.gen(function* (
1292
1136
  }
1293
1137
  return Object.keys(resolved).length > 0 ? resolved : void 0;
1294
1138
  });
1295
- var resolveMcpCredentialInputMap = (ctx, values, params) => Effect6.gen(function* () {
1296
- 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]));
1297
1141
  const resolved = {};
1298
1142
  for (const [name, value] of Object.entries(values)) {
1299
1143
  if (typeof value === "string") {
1300
1144
  resolved[name] = value;
1301
1145
  continue;
1302
1146
  }
1303
- if ("kind" in value) {
1304
- const slotResolved = yield* resolveMcpBindingValueMap(
1305
- ctx,
1306
- { [name]: value },
1307
- {
1308
- sourceId: params.sourceId,
1309
- sourceScope: params.sourceScope,
1310
- missingLabel: params.missingLabel
1311
- }
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
+ )
1312
1159
  );
1313
- 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;
1314
1167
  continue;
1315
1168
  }
1316
- const secretScope = "secretScopeId" in value ? value.secretScopeId ?? value.targetScope : params.targetScope ?? params.sourceScope;
1317
- const secret = yield* ctx.secrets.getAtScope(SecretId.make(value.secretId), secretScope).pipe(
1318
- Effect6.catchTag(
1319
- "SecretOwnedByConnectionError",
1320
- () => Effect6.fail(
1321
- new McpConnectionError({
1322
- transport: "remote",
1323
- message: `Failed to resolve secret for ${params.missingLabel} "${name}"`
1324
- })
1325
- )
1326
- )
1327
- );
1328
- if (secret === null) {
1329
- return yield* new McpConnectionError({
1330
- transport: "remote",
1331
- message: `Missing secret "${value.secretId}" for ${params.missingLabel} "${name}"`
1332
- });
1169
+ if (binding?.kind === "text") {
1170
+ resolved[name] = value.prefix ? `${value.prefix}${binding.text}` : binding.text;
1333
1171
  }
1334
- resolved[name] = value.prefix ? `${value.prefix}${secret}` : secret;
1335
1172
  }
1336
1173
  return Object.keys(resolved).length > 0 ? resolved : void 0;
1337
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
+ });
1338
1191
  var resolveMcpHeaderAuth = (ctx, sourceId, sourceScope, auth) => Effect6.gen(function* () {
1339
1192
  if (auth.kind !== "header") return {};
1340
1193
  const binding = yield* resolveMcpSourceBinding(ctx, sourceId, sourceScope, auth.secretSlot);
@@ -1388,60 +1241,6 @@ var resolveMcpStoredOauthProvider = (ctx, sourceId, sourceScope, auth) => Effect
1388
1241
  );
1389
1242
  return makeOAuthProvider(accessToken);
1390
1243
  });
1391
- var resolveMcpInputAuth = (ctx, sourceId, sourceScope, targetScope, auth) => Effect6.gen(function* () {
1392
- if (!auth || auth.kind === "none") return { headers: {} };
1393
- if (auth.kind === "header") {
1394
- if ("secretSlot" in auth) {
1395
- const headers = yield* resolveMcpHeaderAuth(ctx, sourceId, sourceScope, auth);
1396
- return { headers };
1397
- }
1398
- const secretScope = auth.secretScopeId ?? auth.targetScope ?? targetScope ?? sourceScope;
1399
- const secret = yield* ctx.secrets.getAtScope(SecretId.make(auth.secretId), secretScope).pipe(
1400
- Effect6.catchTag(
1401
- "SecretOwnedByConnectionError",
1402
- () => Effect6.fail(
1403
- new McpConnectionError({
1404
- transport: "remote",
1405
- message: `Failed to resolve secret "${auth.secretId}"`
1406
- })
1407
- )
1408
- )
1409
- );
1410
- if (secret === null) {
1411
- return yield* new McpConnectionError({
1412
- transport: "remote",
1413
- message: `Failed to resolve secret "${auth.secretId}"`
1414
- });
1415
- }
1416
- return {
1417
- headers: { [auth.headerName]: auth.prefix ? `${auth.prefix}${secret}` : secret }
1418
- };
1419
- }
1420
- const connection = "connectionId" in auth ? { id: ConnectionId.make(auth.connectionId), scope: targetScope ?? sourceScope } : yield* Effect6.gen(function* () {
1421
- const binding = yield* resolveMcpSourceBinding(
1422
- ctx,
1423
- sourceId,
1424
- sourceScope,
1425
- auth.connectionSlot
1426
- );
1427
- return binding?.value.kind === "connection" ? { id: binding.value.connectionId, scope: binding.scopeId } : null;
1428
- });
1429
- if (connection === null) {
1430
- return yield* new McpConnectionError({
1431
- transport: "remote",
1432
- message: `Missing OAuth connection binding for MCP source "${sourceId}"`
1433
- });
1434
- }
1435
- const accessToken = yield* ctx.connections.accessTokenAtScope(connection.id, connection.scope).pipe(
1436
- Effect6.mapError(
1437
- ({ message }) => new McpConnectionError({
1438
- transport: "remote",
1439
- message: `Failed to resolve OAuth connection "${connection.id}": ${message}`
1440
- })
1441
- )
1442
- );
1443
- return { headers: {}, authProvider: makeOAuthProvider(accessToken) };
1444
- });
1445
1244
  var resolveConnectorInput = (sourceId, sourceScope, sd, ctx, allowStdio) => {
1446
1245
  if (sd.transport === "stdio") {
1447
1246
  if (!allowStdio) {
@@ -1519,23 +1318,16 @@ var makeRuntime = () => Effect6.gen(function* () {
1519
1318
  }).pipe(Scope.provide(cacheScope));
1520
1319
  return { connectionCache, pendingConnectors, cacheScope };
1521
1320
  });
1522
- var secretRef = (id) => `${SECRET_REF_PREFIX}${id}`;
1523
1321
  var authToConfig = (auth) => {
1524
1322
  if (!auth) return void 0;
1525
- if (auth.kind === "none") return { kind: "none" };
1526
- if (auth.kind === "header") {
1527
- if (!("secretId" in auth)) return void 0;
1528
- return {
1529
- kind: "header",
1530
- headerName: auth.headerName,
1531
- secret: secretRef(auth.secretId),
1532
- prefix: auth.prefix
1533
- };
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;
1534
1327
  }
1535
- if (!("connectionId" in auth)) return void 0;
1536
1328
  return {
1537
1329
  kind: "oauth2",
1538
- connectionId: auth.connectionId
1330
+ connectionId: connection.connectionId
1539
1331
  };
1540
1332
  };
1541
1333
  var toCredentialInput = (bySlot, configured) => {
@@ -1544,7 +1336,9 @@ var toCredentialInput = (bySlot, configured) => {
1544
1336
  if (!value) return void 0;
1545
1337
  if (value.kind === "secret") {
1546
1338
  return {
1339
+ kind: "secret",
1547
1340
  secretId: value.secretId,
1341
+ ...value.secretScopeId ? { secretScope: value.secretScopeId } : {},
1548
1342
  ...configured.prefix ? { prefix: configured.prefix } : {}
1549
1343
  };
1550
1344
  }
@@ -1566,15 +1360,15 @@ var toAuthInput = (bySlot, auth) => {
1566
1360
  const value = bySlot.get(auth.secretSlot);
1567
1361
  if (value?.kind !== "secret") return void 0;
1568
1362
  return {
1569
- kind: "header",
1570
- headerName: auth.headerName,
1571
- secretId: value.secretId,
1572
- prefix: auth.prefix
1363
+ kind: "none"
1573
1364
  };
1574
1365
  }
1575
1366
  const connection = bySlot.get(auth.connectionSlot);
1576
- if (connection?.kind !== "connection") return void 0;
1577
- return { kind: "oauth2", connectionId: connection.connectionId };
1367
+ return {
1368
+ oauth2: {
1369
+ ...connection?.kind === "connection" ? { connection: { kind: "connection", connectionId: connection.connectionId } } : {}
1370
+ }
1371
+ };
1578
1372
  };
1579
1373
  var inputFormFromStored = (bindings, stored, scope, sourceName, namespace) => {
1580
1374
  if (stored.transport === "stdio") {
@@ -1622,8 +1416,8 @@ var toMcpConfigEntry = (namespace, sourceName, config) => {
1622
1416
  name: sourceName,
1623
1417
  endpoint: config.endpoint,
1624
1418
  remoteTransport: config.remoteTransport,
1625
- queryParams: plainStringMap(config.queryParams),
1626
- headers: plainStringMap(config.headers),
1419
+ queryParams: credentialInputMapToConfigValues(config.queryParams),
1420
+ headers: credentialInputMapToConfigValues(config.headers),
1627
1421
  namespace,
1628
1422
  auth: authToConfig(config.auth)
1629
1423
  };
@@ -1642,6 +1436,13 @@ var mcpPlugin = definePlugin((options) => {
1642
1436
  return {
1643
1437
  id: "mcp",
1644
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
+ })),
1645
1446
  // Surfaced to the client bundle via the Vite plugin (see
1646
1447
  // `@executor-js/vite-plugin`). The MCP `./client` factory reads
1647
1448
  // `allowStdio` and gates the stdio tab + presets in AddMcpSource —
@@ -1734,72 +1535,72 @@ var mcpPlugin = definePlugin((options) => {
1734
1535
  const addSource = (config) => Effect6.gen(function* () {
1735
1536
  const namespace = normalizeNamespace(config);
1736
1537
  const canonicalRemote = config.transport === "remote" ? {
1737
- headers: canonicalizeCredentialMap(config.headers, mcpHeaderSlot),
1738
- queryParams: canonicalizeCredentialMap(config.queryParams, mcpQueryParamSlot),
1739
- auth: canonicalizeAuth(config.auth)
1538
+ headers: canonicalizeConfiguredValueMap(config.headers, mcpHeaderSlot),
1539
+ queryParams: canonicalizeConfiguredValueMap(
1540
+ config.queryParams,
1541
+ mcpQueryParamSlot
1542
+ )
1740
1543
  } : null;
1741
- const directBindings = canonicalRemote ? [
1742
- ...canonicalRemote.headers.bindings,
1743
- ...canonicalRemote.queryParams.bindings,
1744
- ...canonicalRemote.auth.bindings
1745
- ] : [];
1746
- for (const binding of directBindings) {
1747
- const bindingTargetScope2 = yield* targetScopeForBinding(
1748
- config.transport === "remote" ? config.credentialTargetScope : void 0,
1749
- binding
1750
- );
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) {
1751
1562
  yield* validateMcpBindingTarget(ctx, {
1752
1563
  sourceId: namespace,
1753
1564
  sourceScope: config.scope,
1754
- targetScope: bindingTargetScope2
1565
+ targetScope: initialRemote.scope
1755
1566
  });
1756
1567
  }
1757
- const targetScope = config.transport === "remote" && directBindings[0] ? yield* targetScopeForBinding(config.credentialTargetScope, directBindings[0]) : void 0;
1758
- const sd = toStoredSourceData(
1759
- config,
1760
- canonicalRemote ? {
1761
- headers: canonicalRemote.headers.values,
1762
- queryParams: canonicalRemote.queryParams.values,
1763
- auth: canonicalRemote.auth.auth
1764
- } : void 0
1765
- );
1766
- const resolved = yield* (config.transport === "remote" ? Effect6.gen(function* () {
1767
- const resolvedHeaders = yield* resolveMcpCredentialInputMap(ctx, config.headers, {
1768
- sourceId: namespace,
1769
- sourceScope: config.scope,
1770
- targetScope,
1771
- missingLabel: "header"
1772
- });
1773
- const resolvedQueryParams = yield* resolveMcpCredentialInputMap(
1774
- ctx,
1775
- config.queryParams,
1776
- {
1777
- sourceId: namespace,
1778
- sourceScope: config.scope,
1779
- targetScope,
1780
- missingLabel: "query parameter"
1781
- }
1782
- );
1783
- const resolvedAuth = yield* resolveMcpInputAuth(
1784
- ctx,
1785
- namespace,
1786
- config.scope,
1787
- targetScope,
1788
- config.auth
1789
- );
1790
- const headers = {
1791
- ...resolvedHeaders ?? {},
1792
- ...resolvedAuth.headers
1793
- };
1794
- return {
1795
- transport: "remote",
1796
- endpoint: config.endpoint,
1797
- remoteTransport: config.remoteTransport ?? "auto",
1798
- queryParams: resolvedQueryParams,
1799
- headers: Object.keys(headers).length > 0 ? headers : void 0,
1800
- authProvider: resolvedAuth.authProvider
1801
- };
1802
- }) : 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(
1803
1604
  Effect6.result,
1804
1605
  Effect6.withSpan("mcp.plugin.resolve_connector", {
1805
1606
  attributes: {
@@ -1859,21 +1660,22 @@ var mcpPlugin = definePlugin((options) => {
1859
1660
  outputSchema: e.outputSchema
1860
1661
  }))
1861
1662
  });
1862
- if (directBindings.length > 0) {
1863
- for (const binding of directBindings) {
1864
- const bindingTargetScope2 = yield* targetScopeForBinding(
1865
- config.transport === "remote" ? config.credentialTargetScope : void 0,
1866
- binding
1867
- );
1868
- yield* ctx.credentialBindings.set({
1869
- targetScope: ScopeId.make(bindingTargetScope2),
1870
- pluginId: MCP_PLUGIN_ID,
1871
- sourceId: namespace,
1872
- 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) => ({
1873
1675
  slotKey: binding.slot,
1874
1676
  value: binding.value
1875
- });
1876
- }
1677
+ }))
1678
+ });
1877
1679
  }
1878
1680
  })
1879
1681
  ).pipe(
@@ -1996,8 +1798,25 @@ var mcpPlugin = definePlugin((options) => {
1996
1798
  attributes: { "mcp.source.namespace": namespace }
1997
1799
  })
1998
1800
  );
1999
- const updateSource = (namespace, scope, input) => Effect6.gen(function* () {
2000
- 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);
2001
1820
  if (!existing || existing.config.transport !== "remote") return;
2002
1821
  const canonicalHeaders = input.headers !== void 0 ? canonicalizeCredentialMap(input.headers, mcpHeaderSlot) : null;
2003
1822
  const canonicalQueryParams = input.queryParams !== void 0 ? canonicalizeCredentialMap(input.queryParams, mcpQueryParamSlot) : null;
@@ -2007,46 +1826,40 @@ var mcpPlugin = definePlugin((options) => {
2007
1826
  ...canonicalQueryParams?.bindings ?? [],
2008
1827
  ...canonicalAuth?.bindings ?? []
2009
1828
  ];
2010
- const targetScope = yield* bindingTargetScope(
2011
- input.credentialTargetScope,
2012
- directBindings
2013
- );
2014
- if (targetScope) {
1829
+ if (directBindings.length > 0) {
2015
1830
  yield* validateMcpBindingTarget(ctx, {
2016
- sourceId: namespace,
2017
- sourceScope: scope,
1831
+ sourceId,
1832
+ sourceScope,
2018
1833
  targetScope
2019
1834
  });
2020
1835
  }
2021
- const remote = existing.config;
2022
1836
  const updatedConfig = {
2023
- ...remote,
1837
+ ...existing.config,
2024
1838
  ...input.endpoint !== void 0 ? { endpoint: input.endpoint } : {},
2025
1839
  ...canonicalHeaders ? { headers: canonicalHeaders.values } : {},
2026
1840
  ...canonicalAuth ? { auth: canonicalAuth.auth } : {},
2027
1841
  ...canonicalQueryParams ? { queryParams: canonicalQueryParams.values } : {}
2028
1842
  };
2029
- const sourceName = input.name?.trim() || existing.name;
2030
1843
  const affectedPrefixes = [
2031
1844
  ...input.headers !== void 0 ? ["header:"] : [],
2032
1845
  ...input.queryParams !== void 0 ? ["query_param:"] : [],
2033
1846
  ...input.auth !== void 0 ? ["auth:"] : []
2034
1847
  ];
2035
- const replacementTargetScope = targetScope ?? input.credentialTargetScope ?? scope;
1848
+ const sourceName = input.name?.trim() || existing.name;
2036
1849
  yield* ctx.transaction(
2037
1850
  Effect6.gen(function* () {
2038
1851
  yield* ctx.storage.putSource({
2039
- namespace,
2040
- scope,
1852
+ namespace: sourceId,
1853
+ scope: sourceScope,
2041
1854
  name: sourceName,
2042
1855
  config: updatedConfig
2043
1856
  });
2044
1857
  if (affectedPrefixes.length > 0 || directBindings.length > 0) {
2045
1858
  yield* ctx.credentialBindings.replaceForSource({
2046
- targetScope: ScopeId.make(replacementTargetScope),
1859
+ targetScope: ScopeId.make(targetScope),
2047
1860
  pluginId: MCP_PLUGIN_ID,
2048
- sourceId: namespace,
2049
- sourceScope: ScopeId.make(scope),
1861
+ sourceId,
1862
+ sourceScope: ScopeId.make(sourceScope),
2050
1863
  slotPrefixes: affectedPrefixes,
2051
1864
  bindings: directBindings.map((binding) => ({
2052
1865
  slotKey: binding.slot,
@@ -2056,71 +1869,168 @@ var mcpPlugin = definePlugin((options) => {
2056
1869
  }
2057
1870
  })
2058
1871
  );
2059
- if (configFile) {
1872
+ if (options?.configFile) {
2060
1873
  const bindings = yield* ctx.credentialBindings.listForSource({
2061
1874
  pluginId: MCP_PLUGIN_ID,
2062
- sourceId: namespace,
2063
- sourceScope: ScopeId.make(scope)
1875
+ sourceId,
1876
+ sourceScope: ScopeId.make(sourceScope)
2064
1877
  });
2065
1878
  const inputForm = inputFormFromStored(
2066
1879
  bindings,
2067
1880
  updatedConfig,
2068
- scope,
1881
+ sourceScope,
2069
1882
  sourceName,
2070
- namespace
1883
+ sourceId
2071
1884
  );
2072
- 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"));
2073
1886
  }
2074
- }).pipe(
2075
- Effect6.withSpan("mcp.plugin.update_source", {
2076
- attributes: { "mcp.source.namespace": namespace }
2077
- })
2078
- );
2079
- const getSource = (namespace, scope) => ctx.storage.getSource(namespace, scope).pipe(
2080
- Effect6.withSpan("mcp.plugin.get_source", {
2081
- attributes: { "mcp.source.namespace": namespace }
2082
- })
2083
- );
2084
- return {
2085
- probeEndpoint,
2086
- addSource,
2087
- removeSource,
2088
- refreshSource,
2089
- getSource,
2090
- updateSource,
2091
- listSourceBindings: (sourceId, sourceScope) => listMcpSourceBindings(ctx, sourceId, sourceScope),
2092
- setSourceBinding: (input) => Effect6.gen(function* () {
2093
- yield* validateMcpBindingTarget(ctx, {
2094
- sourceId: input.sourceId,
2095
- sourceScope: input.sourceScope,
2096
- targetScope: input.scope
2097
- });
2098
- const binding = yield* ctx.credentialBindings.set({
2099
- targetScope: input.scope,
2100
- pluginId: MCP_PLUGIN_ID,
2101
- sourceId: input.sourceId,
2102
- sourceScope: input.sourceScope,
2103
- slotKey: input.slot,
2104
- value: input.value
2105
- });
2106
- return coreBindingToMcpBinding(binding);
2107
- }),
2108
- removeSourceBinding: (sourceId, sourceScope, slot, scope) => Effect6.gen(function* () {
2109
- yield* validateMcpBindingTarget(ctx, {
2110
- sourceId,
2111
- sourceScope,
2112
- targetScope: scope
2113
- });
2114
- yield* ctx.credentialBindings.remove({
2115
- targetScope: ScopeId.make(scope),
2116
- pluginId: MCP_PLUGIN_ID,
2117
- sourceId,
2118
- sourceScope: ScopeId.make(sourceScope),
2119
- slotKey: slot
2120
- });
2121
- })
2122
- };
1887
+ })
2123
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
+ ],
2124
2034
  invokeTool: ({ ctx, toolRow, args, elicit }) => Effect6.gen(function* () {
2125
2035
  const runtime = yield* ensureRuntime();
2126
2036
  const toolScope = toolRow.scope_id;
@@ -2146,7 +2056,7 @@ var mcpPlugin = definePlugin((options) => {
2146
2056
  message: `No MCP source config for namespace "${entry.namespace}"`
2147
2057
  });
2148
2058
  }
2149
- return yield* invokeMcpTool({
2059
+ const raw = yield* invokeMcpTool({
2150
2060
  toolId: toolRow.id,
2151
2061
  toolName: entry.binding.toolName,
2152
2062
  args,
@@ -2181,6 +2091,15 @@ var mcpPlugin = definePlugin((options) => {
2181
2091
  pendingConnectors: runtime.pendingConnectors,
2182
2092
  elicit
2183
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);
2184
2103
  }).pipe(
2185
2104
  Effect6.withSpan("mcp.plugin.invoke_tool", {
2186
2105
  attributes: {
@@ -2247,8 +2166,8 @@ var mcpPlugin = definePlugin((options) => {
2247
2166
  // Honor upstream destructiveHint from MCP ToolAnnotations.
2248
2167
  // Bindings are fetched per scope so shadowed sources (e.g. an org-level
2249
2168
  // source overridden per-user) each resolve against their own scope's
2250
- // row rather than collapsing onto whichever row the scoped adapter
2251
- // sees first.
2169
+ // row rather than collapsing onto whichever visible row would otherwise
2170
+ // win first.
2252
2171
  resolveAnnotations: ({ ctx, sourceId, toolRows }) => Effect6.gen(function* () {
2253
2172
  const scopes = new Set(toolRows.map((row) => row.scope_id));
2254
2173
  const entries = yield* Effect6.forEach(
@@ -2315,4 +2234,4 @@ export {
2315
2234
  makeMcpStore,
2316
2235
  mcpPlugin
2317
2236
  };
2318
- //# sourceMappingURL=chunk-2DOCEPYN.js.map
2237
+ //# sourceMappingURL=chunk-H5PLTEMB.js.map