@executor-js/plugin-mcp 0.0.1-beta.5 → 0.0.1-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -48,10 +48,10 @@ const result = await executor.tools.invoke(
48
48
 
49
49
  ## Using with Effect
50
50
 
51
- If you're building on `@executor/sdk/core` (the raw Effect entry), import this plugin from its `/core` subpath instead:
51
+ If you're building on `@executor/sdk` (the raw Effect entry), import this plugin from its `/core` subpath instead:
52
52
 
53
53
  ```ts
54
- import { mcpPlugin } from "@executor/plugin-mcp/core";
54
+ import { mcpPlugin } from "@executor/plugin-mcp";
55
55
  ```
56
56
 
57
57
  ## Status
@@ -1,4 +1,5 @@
1
1
  import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "@effect/platform";
2
+ import { McpStoredSourceSchema } from "../sdk/stored-source";
2
3
  export { HttpApiSchema };
3
4
  declare const McpGroup_base: HttpApiGroup.HttpApiGroup<"mcp", HttpApiEndpoint.HttpApiEndpoint<"probeEndpoint", "POST", {
4
5
  readonly scopeId: string & import("effect/Brand").Brand<"ScopeId">;
@@ -107,6 +108,41 @@ declare const McpGroup_base: HttpApiGroup.HttpApiGroup<"mcp", HttpApiEndpoint.Ht
107
108
  readonly error_description?: string | undefined;
108
109
  }, never, never, string, {
109
110
  readonly message: string;
111
+ }, never, never> | HttpApiEndpoint.HttpApiEndpoint<"getSource", "GET", {
112
+ readonly scopeId: string & import("effect/Brand").Brand<"ScopeId">;
113
+ readonly namespace: string;
114
+ }, never, never, never, McpStoredSourceSchema | null, {
115
+ readonly message: string;
116
+ }, never, never> | HttpApiEndpoint.HttpApiEndpoint<"updateSource", "PATCH", {
117
+ readonly scopeId: string & import("effect/Brand").Brand<"ScopeId">;
118
+ readonly namespace: string;
119
+ }, never, {
120
+ readonly endpoint?: string | undefined;
121
+ readonly queryParams?: {
122
+ readonly [x: string]: string;
123
+ } | undefined;
124
+ readonly headers?: {
125
+ readonly [x: string]: string;
126
+ } | undefined;
127
+ readonly auth?: {
128
+ readonly kind: "none";
129
+ } | {
130
+ readonly secretId: string;
131
+ readonly kind: "header";
132
+ readonly headerName: string;
133
+ readonly prefix?: string | undefined;
134
+ } | {
135
+ readonly kind: "oauth2";
136
+ readonly scope: string | null;
137
+ readonly accessTokenSecretId: string;
138
+ readonly refreshTokenSecretId: string | null;
139
+ readonly tokenType?: string | undefined;
140
+ readonly expiresAt: number | null;
141
+ } | undefined;
142
+ }, never, {
143
+ readonly updated: boolean;
144
+ }, {
145
+ readonly message: string;
110
146
  }, never, never>, never, never, false>;
111
147
  export declare class McpGroup extends McpGroup_base {
112
148
  }
@@ -1,9 +1,6 @@
1
1
  // src/sdk/binding-store.ts
2
2
  import { Effect, Schema as Schema2 } from "effect";
3
- import {
4
- makeInMemoryScopedKv,
5
- scopeKv
6
- } from "@executor-js/sdk/core";
3
+ import { makeInMemoryScopedKv, scopeKv } from "@executor-js/sdk";
7
4
 
8
5
  // src/sdk/types.ts
9
6
  import { Schema } from "effect";
@@ -51,13 +48,8 @@ var McpStdioSourceData = Schema.Struct({
51
48
  /** Working directory */
52
49
  cwd: Schema.optional(Schema.String)
53
50
  });
54
- var McpStoredSourceData = Schema.Union(
55
- McpRemoteSourceData,
56
- McpStdioSourceData
57
- );
58
- var McpToolBinding = class extends Schema.Class(
59
- "McpToolBinding"
60
- )({
51
+ var McpStoredSourceData = Schema.Union(McpRemoteSourceData, McpStdioSourceData);
52
+ var McpToolBinding = class extends Schema.Class("McpToolBinding")({
61
53
  toolId: Schema.String,
62
54
  toolName: Schema.String,
63
55
  description: Schema.NullOr(Schema.String),
@@ -72,12 +64,8 @@ var StoredBindingEntry = Schema2.Struct({
72
64
  binding: McpToolBinding,
73
65
  sourceData: Schema2.Unknown
74
66
  });
75
- var encodeBindingEntry = Schema2.encodeSync(
76
- Schema2.parseJson(StoredBindingEntry)
77
- );
78
- var decodeBindingEntry = Schema2.decodeUnknownSync(
79
- Schema2.parseJson(StoredBindingEntry)
80
- );
67
+ var encodeBindingEntry = Schema2.encodeSync(Schema2.parseJson(StoredBindingEntry));
68
+ var decodeBindingEntry = Schema2.decodeUnknownSync(Schema2.parseJson(StoredBindingEntry));
81
69
  var makeStore = (bindings, sources) => ({
82
70
  // ---- Bindings ----
83
71
  get: (toolId) => Effect.gen(function* () {
@@ -89,11 +77,8 @@ var makeStore = (bindings, sources) => ({
89
77
  sourceData: entry.sourceData
90
78
  };
91
79
  }),
92
- put: (toolId, namespace, binding, sourceData) => bindings.set(
93
- toolId,
94
- encodeBindingEntry({ namespace, binding, sourceData })
95
- ),
96
- remove: (toolId) => bindings.delete(toolId).pipe(Effect.asVoid),
80
+ put: (toolId, namespace, binding, sourceData) => bindings.set([{ key: toolId, value: encodeBindingEntry({ namespace, binding, sourceData }) }]),
81
+ remove: (toolId) => bindings.delete([toolId]).pipe(Effect.asVoid),
97
82
  listByNamespace: (namespace) => Effect.gen(function* () {
98
83
  const entries = yield* bindings.list();
99
84
  const ids = [];
@@ -108,20 +93,23 @@ var makeStore = (bindings, sources) => ({
108
93
  const ids = [];
109
94
  for (const e of entries) {
110
95
  const entry = decodeBindingEntry(e.value);
111
- if (entry.namespace === namespace) {
112
- ids.push(e.key);
113
- yield* bindings.delete(e.key);
114
- }
96
+ if (entry.namespace === namespace) ids.push(e.key);
115
97
  }
98
+ if (ids.length > 0) yield* bindings.delete(ids);
116
99
  return ids;
117
100
  }),
118
101
  // ---- Sources (meta + config combined) ----
119
- putSource: (source) => sources.set(source.namespace, JSON.stringify(source)),
120
- removeSource: (namespace) => sources.delete(namespace).pipe(Effect.asVoid),
102
+ putSource: (source) => sources.set([{ key: source.namespace, value: JSON.stringify(source) }]),
103
+ removeSource: (namespace) => sources.delete([namespace]).pipe(Effect.asVoid),
121
104
  listSources: () => Effect.gen(function* () {
122
105
  const entries = yield* sources.list();
123
106
  return entries.map((e) => JSON.parse(e.value));
124
107
  }),
108
+ getSource: (namespace) => Effect.gen(function* () {
109
+ const raw = yield* sources.get(namespace);
110
+ if (!raw) return null;
111
+ return JSON.parse(raw);
112
+ }),
125
113
  getSourceConfig: (namespace) => Effect.gen(function* () {
126
114
  const raw = yield* sources.get(namespace);
127
115
  if (!raw) return null;
@@ -129,14 +117,8 @@ var makeStore = (bindings, sources) => ({
129
117
  return source.config;
130
118
  })
131
119
  });
132
- var makeKvBindingStore = (kv, namespace) => makeStore(
133
- scopeKv(kv, `${namespace}.bindings`),
134
- scopeKv(kv, `${namespace}.sources`)
135
- );
136
- var makeInMemoryBindingStore = () => makeStore(
137
- makeInMemoryScopedKv(),
138
- makeInMemoryScopedKv()
139
- );
120
+ var makeKvBindingStore = (kv, namespace) => makeStore(scopeKv(kv, `${namespace}.bindings`), scopeKv(kv, `${namespace}.sources`));
121
+ var makeInMemoryBindingStore = () => makeStore(makeInMemoryScopedKv(), makeInMemoryScopedKv());
140
122
 
141
123
  // src/sdk/plugin.ts
142
124
  import { Effect as Effect6, Exit, ScopedCache, Duration, Scope } from "effect";
@@ -146,7 +128,7 @@ import {
146
128
  definePlugin,
147
129
  ToolId,
148
130
  SecretId
149
- } from "@executor-js/sdk/core";
131
+ } from "@executor-js/sdk";
150
132
 
151
133
  // src/sdk/connection.ts
152
134
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -181,12 +163,9 @@ var McpInvocationError = class extends Schema3.TaggedError()(
181
163
  }
182
164
  ) {
183
165
  };
184
- var McpOAuthError = class extends Schema3.TaggedError()(
185
- "McpOAuthError",
186
- {
187
- message: Schema3.String
188
- }
189
- ) {
166
+ var McpOAuthError = class extends Schema3.TaggedError()("McpOAuthError", {
167
+ message: Schema3.String
168
+ }) {
190
169
  };
191
170
 
192
171
  // src/sdk/connection.ts
@@ -239,10 +218,7 @@ var createMcpConnector = (input) => {
239
218
  const headers = input.headers ?? {};
240
219
  const remoteTransport = input.remoteTransport ?? "auto";
241
220
  const requestInit = Object.keys(headers).length > 0 ? { headers } : void 0;
242
- const endpoint = buildEndpointUrl(
243
- input.endpoint,
244
- input.queryParams ?? {}
245
- );
221
+ const endpoint = buildEndpointUrl(input.endpoint, input.queryParams ?? {});
246
222
  const connectStreamableHttp = connectClient({
247
223
  transport: "streamable-http",
248
224
  createTransport: () => new StreamableHTTPClientTransport(endpoint, {
@@ -490,7 +466,7 @@ import {
490
466
  ElicitationResponse,
491
467
  FormElicitation,
492
468
  UrlElicitation
493
- } from "@executor-js/sdk/core";
469
+ } from "@executor-js/sdk";
494
470
  var asRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
495
471
  var makeOAuthProvider = (accessToken, tokenType, refreshToken) => ({
496
472
  get redirectUrl() {
@@ -551,19 +527,16 @@ var toElicitationRequest = (params) => params.mode === "url" ? new UrlElicitatio
551
527
  requestedSchema: params.requestedSchema
552
528
  });
553
529
  var installElicitationHandler = (client, toolId, args, handler) => {
554
- client.setRequestHandler(
555
- ElicitRequestSchema,
556
- async (request) => {
557
- const params = decodeElicitParams(request.params);
558
- const response = await Effect5.runPromise(
559
- handler({ toolId, args, request: toElicitationRequest(params) })
560
- );
561
- return {
562
- action: response.action,
563
- ...response.action === "accept" && response.content ? { content: response.content } : {}
564
- };
565
- }
566
- );
530
+ client.setRequestHandler(ElicitRequestSchema, async (request) => {
531
+ const params = decodeElicitParams(request.params);
532
+ const response = await Effect5.runPromise(
533
+ handler({ toolId, args, request: toElicitationRequest(params) })
534
+ );
535
+ return {
536
+ action: response.action,
537
+ ...response.action === "accept" && response.content ? { content: response.content } : {}
538
+ };
539
+ });
567
540
  };
568
541
  var resolveConnectorInput = (sourceData, secrets, scopeId) => {
569
542
  if (sourceData.transport === "stdio") {
@@ -576,7 +549,7 @@ var resolveConnectorInput = (sourceData, secrets, scopeId) => {
576
549
  });
577
550
  }
578
551
  return Effect5.gen(function* () {
579
- const headers = { ...sourceData.headers ?? {} };
552
+ const headers = { ...sourceData.headers };
580
553
  let authProvider;
581
554
  const auth2 = sourceData.auth;
582
555
  if (auth2.kind === "header") {
@@ -607,11 +580,7 @@ var resolveConnectorInput = (sourceData, secrets, scopeId) => {
607
580
  Effect5.map((o) => o._tag === "Some" ? o.value : void 0)
608
581
  );
609
582
  }
610
- authProvider = makeOAuthProvider(
611
- accessToken,
612
- auth2.tokenType ?? "Bearer",
613
- refreshToken
614
- );
583
+ authProvider = makeOAuthProvider(accessToken, auth2.tokenType ?? "Bearer", refreshToken);
615
584
  }
616
585
  return {
617
586
  transport: "remote",
@@ -639,9 +608,7 @@ var useMcpConnection = (connection, toolId, toolName, args, handler) => Effect5.
639
608
  var makeMcpInvoker = (opts) => {
640
609
  const { connectionCache, pendingConnectors } = opts;
641
610
  return {
642
- resolveAnnotations: () => Effect5.succeed(
643
- new ToolAnnotations({ requiresApproval: false })
644
- ),
611
+ resolveAnnotations: () => Effect5.succeed(new ToolAnnotations({ requiresApproval: false })),
645
612
  invoke: (toolId, args, options) => Effect5.gen(function* () {
646
613
  const entry = yield* opts.bindingStore.get(toolId);
647
614
  if (!entry) {
@@ -653,11 +620,7 @@ var makeMcpInvoker = (opts) => {
653
620
  }
654
621
  const { binding, sourceData } = entry;
655
622
  const cacheKey = connectionCacheKey(sourceData);
656
- const connector = resolveConnectorInput(
657
- sourceData,
658
- opts.secrets,
659
- opts.scopeId
660
- ).pipe(
623
+ const connector = resolveConnectorInput(sourceData, opts.secrets, opts.scopeId).pipe(
661
624
  Effect5.flatMap((ci) => createMcpConnector(ci)),
662
625
  Effect5.mapError(
663
626
  (err) => new McpConnectionError({
@@ -686,7 +649,7 @@ var makeMcpInvoker = (opts) => {
686
649
  ).pipe(
687
650
  // On failure, invalidate the cached connection and retry once
688
651
  Effect5.catchAll(
689
- (err) => Effect5.gen(function* () {
652
+ () => Effect5.gen(function* () {
690
653
  yield* connectionCache.invalidate(cacheKey);
691
654
  pendingConnectors.set(cacheKey, connector);
692
655
  const freshConnection = yield* connectionCache.get(cacheKey).pipe(
@@ -733,9 +696,7 @@ var makeMcpInvoker = (opts) => {
733
696
  ),
734
697
  closeConnections: () => Effect5.sync(() => {
735
698
  pendingConnectors.clear();
736
- }).pipe(
737
- Effect5.flatMap(() => connectionCache.invalidateAll)
738
- )
699
+ }).pipe(Effect5.flatMap(() => connectionCache.invalidateAll))
739
700
  };
740
701
  };
741
702
 
@@ -857,9 +818,15 @@ var mcpPlugin = (options) => {
857
818
  yield* ctx.tools.registerInvoker("mcp", invoker);
858
819
  const savedSources = yield* bindingStore.listSources();
859
820
  for (const s of savedSources) {
821
+ const isRemote = s.config.transport === "remote";
860
822
  addedSources.set(
861
823
  s.namespace,
862
- new Source({ id: s.namespace, name: s.name, kind: "mcp" })
824
+ new Source({
825
+ id: s.namespace,
826
+ name: s.name,
827
+ kind: "mcp",
828
+ canEdit: isRemote
829
+ })
863
830
  );
864
831
  }
865
832
  const resolveConnectorInput2 = (sd) => {
@@ -874,30 +841,26 @@ var mcpPlugin = (options) => {
874
841
  }
875
842
  return Effect6.gen(function* () {
876
843
  const headers = {
877
- ...sd.headers ?? {}
844
+ ...sd.headers
878
845
  };
879
846
  let authProvider;
880
847
  const auth2 = sd.auth;
881
848
  if (auth2.kind === "header") {
882
- const val = yield* ctx.secrets.resolve(auth2.secretId, ctx.scope.id).pipe(
849
+ const val = yield* ctx.secrets.resolve(SecretId.make(auth2.secretId), ctx.scope.id).pipe(
883
850
  Effect6.mapError(
884
- () => remoteConnectionError(
885
- `Failed to resolve secret "${auth2.secretId}"`
886
- )
851
+ () => remoteConnectionError(`Failed to resolve secret "${auth2.secretId}"`)
887
852
  )
888
853
  );
889
854
  headers[auth2.headerName] = auth2.prefix ? `${auth2.prefix}${val}` : val;
890
855
  } else if (auth2.kind === "oauth2") {
891
- const accessToken = yield* ctx.secrets.resolve(auth2.accessTokenSecretId, ctx.scope.id).pipe(
856
+ const accessToken = yield* ctx.secrets.resolve(SecretId.make(auth2.accessTokenSecretId), ctx.scope.id).pipe(
892
857
  Effect6.mapError(
893
- () => remoteConnectionError(
894
- "Failed to resolve OAuth access token"
895
- )
858
+ () => remoteConnectionError("Failed to resolve OAuth access token")
896
859
  )
897
860
  );
898
861
  let refreshToken;
899
862
  if (auth2.refreshTokenSecretId) {
900
- refreshToken = yield* ctx.secrets.resolve(auth2.refreshTokenSecretId, ctx.scope.id).pipe(
863
+ refreshToken = yield* ctx.secrets.resolve(SecretId.make(auth2.refreshTokenSecretId), ctx.scope.id).pipe(
901
864
  Effect6.option,
902
865
  Effect6.map((o) => o._tag === "Some" ? o.value : void 0)
903
866
  );
@@ -930,9 +893,7 @@ var mcpPlugin = (options) => {
930
893
  detect: (url) => Effect6.gen(function* () {
931
894
  const trimmed = url.trim();
932
895
  if (!trimmed) return null;
933
- const parsed = yield* Effect6.try(() => new URL(trimmed)).pipe(
934
- Effect6.option
935
- );
896
+ const parsed = yield* Effect6.try(() => new URL(trimmed)).pipe(Effect6.option);
936
897
  if (parsed._tag === "None") return null;
937
898
  const name = parsed.value.hostname || "mcp";
938
899
  const namespace = deriveMcpNamespace({ endpoint: trimmed });
@@ -979,9 +940,9 @@ var mcpPlugin = (options) => {
979
940
  Effect6.catchAll(() => Effect6.succeed(null))
980
941
  );
981
942
  if (!ci) return;
982
- const manifest = yield* discoverTools(
983
- createMcpConnector(ci)
984
- ).pipe(Effect6.catchAll(() => Effect6.succeed(null)));
943
+ const manifest = yield* discoverTools(createMcpConnector(ci)).pipe(
944
+ Effect6.catchAll(() => Effect6.succeed(null))
945
+ );
985
946
  if (!manifest) return;
986
947
  const oldIds = yield* bindingStore.removeByNamespace(sourceId);
987
948
  if (oldIds.length > 0) yield* ctx.tools.unregister(oldIds);
@@ -995,15 +956,12 @@ var mcpPlugin = (options) => {
995
956
  ),
996
957
  { discard: true }
997
958
  );
998
- yield* ctx.tools.register(
999
- manifest.tools.map((e) => toRegistration(e, sourceId))
1000
- );
959
+ yield* ctx.tools.register(manifest.tools.map((e) => toRegistration(e, sourceId)));
1001
960
  })
1002
961
  });
1003
962
  const probeEndpoint = (endpoint) => Effect6.gen(function* () {
1004
963
  const trimmed = endpoint.trim();
1005
- if (!trimmed)
1006
- return yield* remoteConnectionError("Endpoint URL is required");
964
+ if (!trimmed) return yield* remoteConnectionError("Endpoint URL is required");
1007
965
  const name = yield* Effect6.try(() => new URL(trimmed).hostname).pipe(
1008
966
  Effect6.orElseSucceed(() => "mcp")
1009
967
  );
@@ -1014,9 +972,7 @@ var mcpPlugin = (options) => {
1014
972
  });
1015
973
  const result = yield* discoverTools(connector).pipe(
1016
974
  Effect6.map((m) => ({ ok: true, manifest: m })),
1017
- Effect6.catchAll(
1018
- () => Effect6.succeed({ ok: false, manifest: null })
1019
- )
975
+ Effect6.catchAll(() => Effect6.succeed({ ok: false, manifest: null }))
1020
976
  );
1021
977
  if (result.ok && result.manifest) {
1022
978
  return {
@@ -1056,13 +1012,9 @@ var mcpPlugin = (options) => {
1056
1012
  const ci = yield* resolveConnectorInput2(sd);
1057
1013
  const connector = createMcpConnector(ci);
1058
1014
  const manifest = yield* discoverTools(connector).pipe(
1059
- Effect6.mapError(
1060
- (err) => mcpDiscoveryError(`MCP discovery failed: ${err.message}`)
1061
- )
1062
- );
1063
- const registrations = manifest.tools.map(
1064
- (e) => toRegistration(e, namespace)
1015
+ Effect6.mapError((err) => mcpDiscoveryError(`MCP discovery failed: ${err.message}`))
1065
1016
  );
1017
+ const registrations = manifest.tools.map((e) => toRegistration(e, namespace));
1066
1018
  yield* Effect6.forEach(
1067
1019
  manifest.tools,
1068
1020
  (e) => bindingStore.put(
@@ -1085,7 +1037,8 @@ var mcpPlugin = (options) => {
1085
1037
  new Source({
1086
1038
  id: namespace,
1087
1039
  name: sourceName,
1088
- kind: "mcp"
1040
+ kind: "mcp",
1041
+ canEdit: config.transport === "remote"
1089
1042
  })
1090
1043
  );
1091
1044
  return { toolCount: registrations.length, namespace };
@@ -1099,16 +1052,10 @@ var mcpPlugin = (options) => {
1099
1052
  const refreshSource = (namespace) => Effect6.gen(function* () {
1100
1053
  const sd = yield* bindingStore.getSourceConfig(namespace);
1101
1054
  if (!sd)
1102
- return yield* remoteConnectionError(
1103
- `No stored config for MCP source "${namespace}"`
1104
- );
1055
+ return yield* remoteConnectionError(`No stored config for MCP source "${namespace}"`);
1105
1056
  const ci = yield* resolveConnectorInput2(sd);
1106
- const manifest = yield* discoverTools(
1107
- createMcpConnector(ci)
1108
- ).pipe(
1109
- Effect6.mapError(
1110
- (err) => mcpDiscoveryError(`MCP refresh failed: ${err.message}`)
1111
- )
1057
+ const manifest = yield* discoverTools(createMcpConnector(ci)).pipe(
1058
+ Effect6.mapError((err) => mcpDiscoveryError(`MCP refresh failed: ${err.message}`))
1112
1059
  );
1113
1060
  const oldIds = yield* bindingStore.removeByNamespace(namespace);
1114
1061
  if (oldIds.length > 0) yield* ctx.tools.unregister(oldIds);
@@ -1122,20 +1069,16 @@ var mcpPlugin = (options) => {
1122
1069
  ),
1123
1070
  { discard: true }
1124
1071
  );
1125
- yield* ctx.tools.register(
1126
- manifest.tools.map((e) => toRegistration(e, namespace))
1127
- );
1072
+ yield* ctx.tools.register(manifest.tools.map((e) => toRegistration(e, namespace)));
1128
1073
  return { toolCount: manifest.tools.length };
1129
1074
  });
1130
1075
  const startOAuth = (input) => Effect6.gen(function* () {
1131
1076
  const endpoint = input.endpoint.trim();
1132
- if (!endpoint)
1133
- return yield* mcpOAuthError("MCP OAuth requires an endpoint");
1077
+ if (!endpoint) return yield* mcpOAuthError("MCP OAuth requires an endpoint");
1134
1078
  let fullEndpoint = endpoint;
1135
1079
  if (input.queryParams && Object.keys(input.queryParams).length > 0) {
1136
1080
  const u = new URL(endpoint);
1137
- for (const [k, v] of Object.entries(input.queryParams))
1138
- u.searchParams.set(k, v);
1081
+ for (const [k, v] of Object.entries(input.queryParams)) u.searchParams.set(k, v);
1139
1082
  fullEndpoint = u.toString();
1140
1083
  }
1141
1084
  const sessionId = `mcp_oauth_${crypto.randomUUID()}`;
@@ -1143,11 +1086,7 @@ var mcpPlugin = (options) => {
1143
1086
  endpoint: fullEndpoint,
1144
1087
  redirectUrl: input.redirectUrl,
1145
1088
  state: sessionId
1146
- }).pipe(
1147
- Effect6.mapError(
1148
- (e) => mcpOAuthError(`OAuth start failed: ${e.message}`)
1149
- )
1150
- );
1089
+ }).pipe(Effect6.mapError((e) => mcpOAuthError(`OAuth start failed: ${e.message}`)));
1151
1090
  oauthSessions.set(sessionId, {
1152
1091
  endpoint: fullEndpoint,
1153
1092
  redirectUrl: input.redirectUrl,
@@ -1164,51 +1103,34 @@ var mcpPlugin = (options) => {
1164
1103
  };
1165
1104
  });
1166
1105
  const completeOAuth = (input) => Effect6.gen(function* () {
1167
- if (input.error)
1168
- return yield* mcpOAuthError(`OAuth error: ${input.error}`);
1169
- if (!input.code)
1170
- return yield* mcpOAuthError("Missing OAuth authorization code");
1106
+ if (input.error) return yield* mcpOAuthError(`OAuth error: ${input.error}`);
1107
+ if (!input.code) return yield* mcpOAuthError("Missing OAuth authorization code");
1171
1108
  const session = oauthSessions.get(input.state);
1172
- if (!session)
1173
- return yield* mcpOAuthError(`OAuth session not found: ${input.state}`);
1109
+ if (!session) return yield* mcpOAuthError(`OAuth session not found: ${input.state}`);
1174
1110
  const exchanged = yield* exchangeMcpOAuthCode({
1175
1111
  session,
1176
1112
  code: input.code
1177
- }).pipe(
1178
- Effect6.mapError(
1179
- (e) => mcpOAuthError(`OAuth exchange failed: ${e.message}`)
1180
- )
1181
- );
1113
+ }).pipe(Effect6.mapError((e) => mcpOAuthError(`OAuth exchange failed: ${e.message}`)));
1182
1114
  const accessTokenRef = yield* ctx.secrets.set({
1183
- id: SecretId.make(
1184
- `mcp-oauth-access-${input.state}`
1185
- ),
1115
+ id: SecretId.make(`mcp-oauth-access-${input.state}`),
1186
1116
  scopeId: ctx.scope.id,
1187
1117
  name: "MCP OAuth Access Token",
1188
1118
  value: exchanged.tokens.access_token,
1189
1119
  purpose: "oauth_access_token"
1190
1120
  }).pipe(
1191
- Effect6.mapError(
1192
- (e) => mcpOAuthError(
1193
- `Failed to store access token: ${String(e)}`
1194
- )
1195
- )
1121
+ Effect6.mapError((e) => mcpOAuthError(`Failed to store access token: ${String(e)}`))
1196
1122
  );
1197
1123
  let refreshTokenSecretId = null;
1198
1124
  if (exchanged.tokens.refresh_token) {
1199
1125
  const ref = yield* ctx.secrets.set({
1200
- id: SecretId.make(
1201
- `mcp-oauth-refresh-${input.state}`
1202
- ),
1126
+ id: SecretId.make(`mcp-oauth-refresh-${input.state}`),
1203
1127
  scopeId: ctx.scope.id,
1204
1128
  name: "MCP OAuth Refresh Token",
1205
1129
  value: exchanged.tokens.refresh_token,
1206
1130
  purpose: "oauth_refresh_token"
1207
1131
  }).pipe(
1208
1132
  Effect6.mapError(
1209
- (e) => mcpOAuthError(
1210
- `Failed to store refresh token: ${String(e)}`
1211
- )
1133
+ (e) => mcpOAuthError(`Failed to store refresh token: ${String(e)}`)
1212
1134
  )
1213
1135
  );
1214
1136
  refreshTokenSecretId = ref.id;
@@ -1223,6 +1145,33 @@ var mcpPlugin = (options) => {
1223
1145
  scope: exchanged.tokens.scope ?? null
1224
1146
  };
1225
1147
  });
1148
+ const updateSource = (namespace, input) => Effect6.gen(function* () {
1149
+ const existingConfig = yield* bindingStore.getSourceConfig(namespace);
1150
+ if (!existingConfig || existingConfig.transport !== "remote") return;
1151
+ const remote = existingConfig;
1152
+ const updatedConfig = {
1153
+ ...remote,
1154
+ ...input.endpoint !== void 0 ? { endpoint: input.endpoint } : {},
1155
+ ...input.headers !== void 0 ? { headers: input.headers } : {},
1156
+ ...input.auth !== void 0 ? { auth: input.auth } : {},
1157
+ ...input.queryParams !== void 0 ? { queryParams: input.queryParams } : {}
1158
+ };
1159
+ const sources = yield* bindingStore.listSources();
1160
+ const existingMeta = sources.find((s) => s.namespace === namespace);
1161
+ yield* bindingStore.putSource({
1162
+ namespace,
1163
+ name: existingMeta?.name ?? namespace,
1164
+ config: updatedConfig
1165
+ });
1166
+ const toolIds = yield* bindingStore.listByNamespace(namespace);
1167
+ for (const toolId of toolIds) {
1168
+ const entry = yield* bindingStore.get(toolId);
1169
+ if (entry) {
1170
+ yield* bindingStore.put(toolId, namespace, entry.binding, updatedConfig);
1171
+ }
1172
+ }
1173
+ });
1174
+ const getSource = (namespace) => bindingStore.getSource(namespace);
1226
1175
  return {
1227
1176
  extension: {
1228
1177
  probeEndpoint,
@@ -1230,7 +1179,9 @@ var mcpPlugin = (options) => {
1230
1179
  removeSource,
1231
1180
  refreshSource,
1232
1181
  startOAuth,
1233
- completeOAuth
1182
+ completeOAuth,
1183
+ getSource,
1184
+ updateSource
1234
1185
  },
1235
1186
  close: () => Effect6.gen(function* () {
1236
1187
  yield* invoker.closeConnections();
@@ -1249,4 +1200,4 @@ export {
1249
1200
  makeKvBindingStore,
1250
1201
  mcpPlugin
1251
1202
  };
1252
- //# sourceMappingURL=chunk-DR65PT4S.js.map
1203
+ //# sourceMappingURL=chunk-NJ4CITCV.js.map