@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 +2 -2
- package/dist/api/group.d.ts +36 -0
- package/dist/{chunk-DR65PT4S.js → chunk-NJ4CITCV.js} +110 -159
- package/dist/chunk-NJ4CITCV.js.map +1 -0
- package/dist/core.js +50 -2
- package/dist/core.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/promise.d.ts +1 -1
- package/dist/react/McpSourceSummary.d.ts +1 -1
- package/dist/sdk/binding-store.d.ts +2 -1
- package/dist/sdk/config-file-store.d.ts +10 -0
- package/dist/sdk/index.d.ts +3 -2
- package/dist/sdk/invoke.d.ts +1 -1
- package/dist/sdk/plugin.d.ts +12 -2
- package/dist/sdk/stored-source.d.ts +115 -0
- package/package.json +30 -28
- package/dist/chunk-DR65PT4S.js.map +0 -1
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
|
|
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
|
|
54
|
+
import { mcpPlugin } from "@executor/plugin-mcp";
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
## Status
|
package/dist/api/group.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
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
|
-
(
|
|
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({
|
|
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
|
-
|
|
984
|
-
)
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
1203
|
+
//# sourceMappingURL=chunk-NJ4CITCV.js.map
|