@executor-js/plugin-mcp 1.4.32 → 1.5.0
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/dist/AddMcpSource-4LLERUW5.js +602 -0
- package/dist/AddMcpSource-4LLERUW5.js.map +1 -0
- package/dist/EditMcpSource-GKJRP75X.js +313 -0
- package/dist/EditMcpSource-GKJRP75X.js.map +1 -0
- package/dist/McpAccountsPanel-UX7MHEIG.js +132 -0
- package/dist/McpAccountsPanel-UX7MHEIG.js.map +1 -0
- package/dist/api/group.d.ts +79 -143
- package/dist/api/index.d.ts +99 -155
- package/dist/chunk-2TXHTMKM.js +1298 -0
- package/dist/chunk-2TXHTMKM.js.map +1 -0
- package/dist/chunk-6OEQZ72N.js +124 -0
- package/dist/chunk-6OEQZ72N.js.map +1 -0
- package/dist/chunk-7FJ3PUUL.js +21 -0
- package/dist/chunk-7FJ3PUUL.js.map +1 -0
- package/dist/chunk-N4EAF5CA.js +146 -0
- package/dist/chunk-N4EAF5CA.js.map +1 -0
- package/dist/client.js +9 -9
- package/dist/client.js.map +1 -1
- package/dist/core.js +36 -26
- package/dist/index.js +2 -2
- package/dist/promise.d.ts +1 -1
- package/dist/react/AddMcpSource.d.ts +1 -1
- package/dist/react/McpAccountsPanel.d.ts +6 -0
- package/dist/react/McpRemoteSourceFields.d.ts +4 -2
- package/dist/react/McpSignInButton.d.ts +2 -0
- package/dist/react/atoms.d.ts +93 -313
- package/dist/react/auth-method-config.d.ts +8 -0
- package/dist/react/client.d.ts +78 -142
- package/dist/react/index.d.ts +3 -3
- package/dist/react/source-plugin.d.ts +5 -5
- package/dist/sdk/connection.d.ts +4 -4
- package/dist/sdk/errors.d.ts +0 -19
- package/dist/sdk/index.d.ts +4 -3
- package/dist/sdk/invoke.d.ts +9 -16
- package/dist/sdk/plugin.d.ts +101 -236
- package/dist/sdk/types.d.ts +25 -130
- package/package.json +5 -4
- package/dist/AddMcpSource-PADMBVX2.js +0 -688
- package/dist/AddMcpSource-PADMBVX2.js.map +0 -1
- package/dist/EditMcpSource-L5GC2B4J.js +0 -281
- package/dist/EditMcpSource-L5GC2B4J.js.map +0 -1
- package/dist/McpSourceSummary-LE3WXFUE.js +0 -170
- package/dist/McpSourceSummary-LE3WXFUE.js.map +0 -1
- package/dist/chunk-6OYEXHU3.js +0 -156
- package/dist/chunk-6OYEXHU3.js.map +0 -1
- package/dist/chunk-FMTVLO5L.js +0 -179
- package/dist/chunk-FMTVLO5L.js.map +0 -1
- package/dist/chunk-LEGVPKYH.js +0 -2391
- package/dist/chunk-LEGVPKYH.js.map +0 -1
- package/dist/chunk-ZIRGIRGP.js +0 -115
- package/dist/chunk-ZIRGIRGP.js.map +0 -1
- package/dist/react/McpSourceSummary.d.ts +0 -5
- package/dist/sdk/binding-store.d.ts +0 -31
- package/dist/sdk/stored-source.d.ts +0 -42
- /package/dist/{sdk/connection-pool.test.d.ts → react/auth-method-config.test.d.ts} +0 -0
- /package/dist/sdk/{cross-user-isolation.test.d.ts → describe-auth-methods.test.d.ts} +0 -0
- /package/dist/sdk/{per-user-auth-isolation.test.d.ts → owner-isolation.test.d.ts} +0 -0
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mcpPresets
|
|
3
|
+
} from "./chunk-TW44CBXJ.js";
|
|
4
|
+
import {
|
|
5
|
+
mcpAuthTemplateFromEditorValue
|
|
6
|
+
} from "./chunk-7FJ3PUUL.js";
|
|
7
|
+
import {
|
|
8
|
+
addMcpServer,
|
|
9
|
+
probeMcpEndpoint
|
|
10
|
+
} from "./chunk-N4EAF5CA.js";
|
|
11
|
+
import "./chunk-6OEQZ72N.js";
|
|
12
|
+
|
|
13
|
+
// src/react/AddMcpSource.tsx
|
|
14
|
+
import { useReducer, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
15
|
+
import { useAtomSet, useAtomValue } from "@effect/atom-react";
|
|
16
|
+
import { Link } from "@tanstack/react-router";
|
|
17
|
+
import * as Exit from "effect/Exit";
|
|
18
|
+
import * as Match from "effect/Match";
|
|
19
|
+
import * as Option from "effect/Option";
|
|
20
|
+
import * as Predicate from "effect/Predicate";
|
|
21
|
+
import * as Schema from "effect/Schema";
|
|
22
|
+
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
|
|
23
|
+
import { integrationsOptimisticAtom } from "@executor-js/react/api/atoms";
|
|
24
|
+
import { Button as Button2 } from "@executor-js/react/components/button";
|
|
25
|
+
import {
|
|
26
|
+
AuthTemplateEditor
|
|
27
|
+
} from "@executor-js/react/components/auth-template-editor";
|
|
28
|
+
import {
|
|
29
|
+
CardStack as CardStack2,
|
|
30
|
+
CardStackContent as CardStackContent2,
|
|
31
|
+
CardStackEntryField as CardStackEntryField2
|
|
32
|
+
} from "@executor-js/react/components/card-stack";
|
|
33
|
+
import { FieldLabel } from "@executor-js/react/components/field";
|
|
34
|
+
import { FloatActions } from "@executor-js/react/components/float-actions";
|
|
35
|
+
import { Input as Input2 } from "@executor-js/react/components/input";
|
|
36
|
+
import { Spinner } from "@executor-js/react/components/spinner";
|
|
37
|
+
import { Textarea } from "@executor-js/react/components/textarea";
|
|
38
|
+
import {
|
|
39
|
+
integrationDisplayNameFromUrl,
|
|
40
|
+
slugifyNamespace,
|
|
41
|
+
IntegrationIdentityFields,
|
|
42
|
+
useIntegrationIdentity
|
|
43
|
+
} from "@executor-js/react/plugins/integration-identity";
|
|
44
|
+
import { integrationWriteKeys } from "@executor-js/react/api/reactivity-keys";
|
|
45
|
+
|
|
46
|
+
// src/react/McpRemoteSourceFields.tsx
|
|
47
|
+
import { Badge } from "@executor-js/react/components/badge";
|
|
48
|
+
import {
|
|
49
|
+
CardStack,
|
|
50
|
+
CardStackContent,
|
|
51
|
+
CardStackEntry,
|
|
52
|
+
CardStackEntryActions,
|
|
53
|
+
CardStackEntryContent,
|
|
54
|
+
CardStackEntryDescription,
|
|
55
|
+
CardStackEntryField,
|
|
56
|
+
CardStackEntryMedia,
|
|
57
|
+
CardStackEntryTitle
|
|
58
|
+
} from "@executor-js/react/components/card-stack";
|
|
59
|
+
import { FieldError } from "@executor-js/react/components/field";
|
|
60
|
+
import { Input } from "@executor-js/react/components/input";
|
|
61
|
+
import { Skeleton } from "@executor-js/react/components/skeleton";
|
|
62
|
+
import { IntegrationFavicon } from "@executor-js/react/components/integration-favicon";
|
|
63
|
+
import { IOSSpinner } from "@executor-js/react/components/spinner";
|
|
64
|
+
import { Button } from "@executor-js/react/components/button";
|
|
65
|
+
import {
|
|
66
|
+
IntegrationIdentityFieldRows
|
|
67
|
+
} from "@executor-js/react/plugins/integration-identity";
|
|
68
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
69
|
+
function McpRemoteSourceFields(props) {
|
|
70
|
+
const previewDescription = props.preview ? props.preview.connected ? props.preview.toolCount === null ? null : `${props.preview.toolCount} tool${props.preview.toolCount !== 1 ? "s" : ""} available` : props.preview.requiresOAuth ? "OAuth required to discover tools" : props.preview.requiresAuthentication ? "Authentication required to discover tools" : "Ready to add" : null;
|
|
71
|
+
if (props.preview) {
|
|
72
|
+
return /* @__PURE__ */ jsx(CardStack, { children: /* @__PURE__ */ jsxs(CardStackContent, { className: "border-t-0", children: [
|
|
73
|
+
/* @__PURE__ */ jsxs(CardStackEntry, { children: [
|
|
74
|
+
/* @__PURE__ */ jsx(CardStackEntryMedia, { children: /* @__PURE__ */ jsx(IntegrationFavicon, { url: props.url, size: 32 }) }),
|
|
75
|
+
/* @__PURE__ */ jsxs(CardStackEntryContent, { children: [
|
|
76
|
+
/* @__PURE__ */ jsx(CardStackEntryTitle, { children: props.preview.serverName ?? props.preview.name }),
|
|
77
|
+
previewDescription ? /* @__PURE__ */ jsx(CardStackEntryDescription, { children: previewDescription }) : null
|
|
78
|
+
] }),
|
|
79
|
+
/* @__PURE__ */ jsx(CardStackEntryActions, { children: props.preview.connected ? /* @__PURE__ */ jsx(
|
|
80
|
+
Badge,
|
|
81
|
+
{
|
|
82
|
+
variant: "outline",
|
|
83
|
+
className: "border-emerald-500/20 bg-emerald-500/10 text-[10px] text-emerald-600 dark:text-emerald-400",
|
|
84
|
+
children: "Connected"
|
|
85
|
+
}
|
|
86
|
+
) : props.preview.requiresOAuth ? /* @__PURE__ */ jsx(
|
|
87
|
+
Badge,
|
|
88
|
+
{
|
|
89
|
+
variant: "outline",
|
|
90
|
+
className: "border-amber-500/20 bg-amber-500/10 text-[10px] text-amber-600 dark:text-amber-400",
|
|
91
|
+
children: "OAuth required"
|
|
92
|
+
}
|
|
93
|
+
) : /* @__PURE__ */ jsx(
|
|
94
|
+
Badge,
|
|
95
|
+
{
|
|
96
|
+
variant: "outline",
|
|
97
|
+
className: "border-amber-500/20 bg-amber-500/10 text-[10px] text-amber-600 dark:text-amber-400",
|
|
98
|
+
children: "Auth required"
|
|
99
|
+
}
|
|
100
|
+
) })
|
|
101
|
+
] }),
|
|
102
|
+
/* @__PURE__ */ jsx(
|
|
103
|
+
IntegrationIdentityFieldRows,
|
|
104
|
+
{
|
|
105
|
+
identity: props.identity,
|
|
106
|
+
namePlaceholder: "e.g. Linear",
|
|
107
|
+
namespaceReadOnly: props.namespaceReadOnly
|
|
108
|
+
}
|
|
109
|
+
),
|
|
110
|
+
/* @__PURE__ */ jsx(CardStackEntryField, { label: "Server URL", children: /* @__PURE__ */ jsx(
|
|
111
|
+
Input,
|
|
112
|
+
{
|
|
113
|
+
value: props.url,
|
|
114
|
+
onChange: (e) => props.onUrlChange(e.target.value),
|
|
115
|
+
placeholder: "https://mcp.example.com",
|
|
116
|
+
className: "w-full font-mono text-sm",
|
|
117
|
+
disabled: props.urlDisabled
|
|
118
|
+
}
|
|
119
|
+
) })
|
|
120
|
+
] }) });
|
|
121
|
+
}
|
|
122
|
+
if (props.probing) {
|
|
123
|
+
return /* @__PURE__ */ jsx(CardStack, { children: /* @__PURE__ */ jsx(CardStackContent, { className: "border-t-0", children: /* @__PURE__ */ jsxs(CardStackEntry, { children: [
|
|
124
|
+
/* @__PURE__ */ jsx(CardStackEntryMedia, { children: /* @__PURE__ */ jsx(Skeleton, { className: "size-4 rounded" }) }),
|
|
125
|
+
/* @__PURE__ */ jsxs(CardStackEntryContent, { children: [
|
|
126
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-40" }),
|
|
127
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-1 h-3 w-32" })
|
|
128
|
+
] }),
|
|
129
|
+
/* @__PURE__ */ jsx(CardStackEntryActions, { children: /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-20 rounded-full" }) })
|
|
130
|
+
] }) }) });
|
|
131
|
+
}
|
|
132
|
+
return /* @__PURE__ */ jsx(CardStack, { children: /* @__PURE__ */ jsx(CardStackContent, { className: "border-t-0", children: /* @__PURE__ */ jsxs(CardStackEntryField, { label: "Server URL", children: [
|
|
133
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
134
|
+
/* @__PURE__ */ jsx(
|
|
135
|
+
Input,
|
|
136
|
+
{
|
|
137
|
+
value: props.url,
|
|
138
|
+
onChange: (e) => props.onUrlChange(e.target.value),
|
|
139
|
+
placeholder: "https://mcp.example.com",
|
|
140
|
+
className: "w-full pr-9 font-mono text-sm",
|
|
141
|
+
"aria-invalid": props.error ? true : void 0,
|
|
142
|
+
disabled: props.urlDisabled
|
|
143
|
+
}
|
|
144
|
+
),
|
|
145
|
+
props.probing && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute right-2 top-1/2 -translate-y-1/2", children: /* @__PURE__ */ jsx(IOSSpinner, { className: "size-4" }) })
|
|
146
|
+
] }),
|
|
147
|
+
props.error && /* @__PURE__ */ jsxs("div", { className: "mt-2 space-y-2", children: [
|
|
148
|
+
/* @__PURE__ */ jsx(FieldError, { children: props.error }),
|
|
149
|
+
props.onRetry && /* @__PURE__ */ jsx(
|
|
150
|
+
Button,
|
|
151
|
+
{
|
|
152
|
+
type: "button",
|
|
153
|
+
variant: "outline",
|
|
154
|
+
size: "sm",
|
|
155
|
+
onClick: props.onRetry,
|
|
156
|
+
className: "h-7 px-2 text-xs",
|
|
157
|
+
children: "Try again"
|
|
158
|
+
}
|
|
159
|
+
)
|
|
160
|
+
] })
|
|
161
|
+
] }) }) });
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/react/AddMcpSource.tsx
|
|
165
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
166
|
+
var ErrorMessage = Schema.Struct({ message: Schema.String });
|
|
167
|
+
var decodeErrorMessage = Schema.decodeUnknownOption(ErrorMessage);
|
|
168
|
+
var STDIO_ENV_ESCAPE_REPLACEMENTS = {
|
|
169
|
+
"\\": "\\",
|
|
170
|
+
n: "\n",
|
|
171
|
+
r: "\r",
|
|
172
|
+
t: " ",
|
|
173
|
+
'"': '"'
|
|
174
|
+
};
|
|
175
|
+
var errorMessageFromExit = (exit, fallback) => Option.match(Option.flatMap(Exit.findErrorOption(exit), decodeErrorMessage), {
|
|
176
|
+
onNone: () => fallback,
|
|
177
|
+
onSome: ({ message }) => message
|
|
178
|
+
});
|
|
179
|
+
var isIntegrationAlreadyExistsExit = (exit) => Option.match(Exit.findErrorOption(exit), {
|
|
180
|
+
onNone: () => false,
|
|
181
|
+
onSome: Predicate.isTagged("IntegrationAlreadyExistsError")
|
|
182
|
+
});
|
|
183
|
+
var integrationExistsMessage = (slug) => `An integration named "${slug}" already exists. To add more authentication, update your existing integration.`;
|
|
184
|
+
function findPreset(id) {
|
|
185
|
+
if (!id) return void 0;
|
|
186
|
+
return mcpPresets.find((p) => p.id === id);
|
|
187
|
+
}
|
|
188
|
+
var init = { step: "url", url: "" };
|
|
189
|
+
function reducer(state, action) {
|
|
190
|
+
return Match.value(action).pipe(
|
|
191
|
+
Match.discriminator("type")("set-url", (a) => ({ step: "url", url: a.url })),
|
|
192
|
+
Match.discriminator("type")(
|
|
193
|
+
"probe-start",
|
|
194
|
+
() => ({
|
|
195
|
+
step: "probing",
|
|
196
|
+
url: state.url,
|
|
197
|
+
probe: "probe" in state ? state.probe : null
|
|
198
|
+
})
|
|
199
|
+
),
|
|
200
|
+
Match.discriminator("type")(
|
|
201
|
+
"probe-ok",
|
|
202
|
+
(a) => ({ step: "probed", url: state.url, probe: a.probe })
|
|
203
|
+
),
|
|
204
|
+
Match.discriminator("type")(
|
|
205
|
+
"probe-fail",
|
|
206
|
+
(a) => ({
|
|
207
|
+
step: "error",
|
|
208
|
+
url: state.url,
|
|
209
|
+
probe: null,
|
|
210
|
+
error: a.error
|
|
211
|
+
})
|
|
212
|
+
),
|
|
213
|
+
Match.discriminator("type")("add-start", () => {
|
|
214
|
+
const probe = "probe" in state ? state.probe : null;
|
|
215
|
+
if (!probe) return state;
|
|
216
|
+
return { step: "adding", url: state.url, probe };
|
|
217
|
+
}),
|
|
218
|
+
Match.discriminator("type")("add-fail", (a) => {
|
|
219
|
+
if (state.step !== "adding") return state;
|
|
220
|
+
return {
|
|
221
|
+
step: "error",
|
|
222
|
+
url: state.url,
|
|
223
|
+
probe: state.probe,
|
|
224
|
+
error: a.error
|
|
225
|
+
};
|
|
226
|
+
}),
|
|
227
|
+
Match.discriminator("type")("retry", () => {
|
|
228
|
+
if (state.step !== "error") return state;
|
|
229
|
+
return state.probe ? { step: "probed", url: state.url, probe: state.probe } : { step: "url", url: state.url };
|
|
230
|
+
}),
|
|
231
|
+
Match.exhaustive
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
function AddMcpSource(props) {
|
|
235
|
+
const allowStdio = props.allowStdio ?? false;
|
|
236
|
+
const rawPreset = findPreset(props.initialPreset);
|
|
237
|
+
const preset = rawPreset?.transport === "stdio" && !allowStdio ? void 0 : rawPreset;
|
|
238
|
+
const isStdioPreset = preset?.transport === "stdio";
|
|
239
|
+
const [transport, setTransport] = useState(
|
|
240
|
+
isStdioPreset && allowStdio ? "stdio" : "remote"
|
|
241
|
+
);
|
|
242
|
+
const [stdioCommand, setStdioCommand] = useState(isStdioPreset ? preset.command : "");
|
|
243
|
+
const [stdioArgs, setStdioArgs] = useState(
|
|
244
|
+
isStdioPreset && preset.args ? preset.args.join(" ") : ""
|
|
245
|
+
);
|
|
246
|
+
const [stdioEnv, setStdioEnv] = useState("");
|
|
247
|
+
const stdioIdentity = useIntegrationIdentity({
|
|
248
|
+
fallbackName: isStdioPreset ? preset.name : stdioCommand
|
|
249
|
+
});
|
|
250
|
+
const [stdioAdding, setStdioAdding] = useState(false);
|
|
251
|
+
const [stdioError, setStdioError] = useState(null);
|
|
252
|
+
const remoteUrl = !isStdioPreset && preset?.transport === void 0 && preset?.url ? preset.url : props.initialUrl ?? "";
|
|
253
|
+
const [state, dispatch] = useReducer(
|
|
254
|
+
reducer,
|
|
255
|
+
remoteUrl ? { step: "url", url: remoteUrl } : init
|
|
256
|
+
);
|
|
257
|
+
const doProbe = useAtomSet(probeMcpEndpoint, { mode: "promiseExit" });
|
|
258
|
+
const doAddServer = useAtomSet(addMcpServer, { mode: "promiseExit" });
|
|
259
|
+
const [authValue, setAuthValue] = useState({ kind: "none" });
|
|
260
|
+
const probe = "probe" in state ? state.probe : null;
|
|
261
|
+
const allowedAuthKinds = probe?.requiresOAuth && probe.supportsDynamicRegistration ? ["oauth"] : probe?.requiresAuthentication ? ["apikey", "oauth"] : ["none", "apikey", "oauth"];
|
|
262
|
+
const remoteIdentity = useIntegrationIdentity({
|
|
263
|
+
fallbackName: integrationDisplayNameFromUrl(state.url, "MCP") ?? probe?.serverName ?? probe?.name ?? ""
|
|
264
|
+
});
|
|
265
|
+
const isProbing = state.step === "probing";
|
|
266
|
+
const isAdding = state.step === "adding";
|
|
267
|
+
const integrationsResult = useAtomValue(integrationsOptimisticAtom);
|
|
268
|
+
const existingSlugs = useMemo(
|
|
269
|
+
() => AsyncResult.isSuccess(integrationsResult) ? integrationsResult.value.map((integration) => String(integration.slug)) : [],
|
|
270
|
+
[integrationsResult]
|
|
271
|
+
);
|
|
272
|
+
const remoteSlug = slugifyNamespace(remoteIdentity.namespace);
|
|
273
|
+
const stdioSlug = slugifyNamespace(stdioIdentity.namespace);
|
|
274
|
+
const remoteSlugExists = remoteSlug.length > 0 && existingSlugs.includes(remoteSlug);
|
|
275
|
+
const stdioSlugExists = stdioSlug.length > 0 && existingSlugs.includes(stdioSlug);
|
|
276
|
+
const canAdd = Boolean(probe) && !isAdding && !remoteSlugExists;
|
|
277
|
+
const probeError = state.step === "error" && state.probe === null ? state.error : null;
|
|
278
|
+
const otherError = state.step === "error" && state.probe !== null ? state.error : null;
|
|
279
|
+
const handleProbe = useCallback(async () => {
|
|
280
|
+
dispatch({ type: "probe-start" });
|
|
281
|
+
const exit = await doProbe({
|
|
282
|
+
payload: { endpoint: state.url.trim() }
|
|
283
|
+
});
|
|
284
|
+
if (Exit.isFailure(exit)) {
|
|
285
|
+
dispatch({
|
|
286
|
+
type: "probe-fail",
|
|
287
|
+
error: errorMessageFromExit(exit, "Failed to connect")
|
|
288
|
+
});
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
setAuthValue(
|
|
292
|
+
exit.value.requiresOAuth ? { kind: "oauth", authorizationUrl: "", tokenUrl: "", scopes: [] } : exit.value.requiresAuthentication ? {
|
|
293
|
+
kind: "apikey",
|
|
294
|
+
placements: [{ carrier: "header", name: "Authorization", prefix: "Bearer " }]
|
|
295
|
+
} : { kind: "none" }
|
|
296
|
+
);
|
|
297
|
+
dispatch({ type: "probe-ok", probe: exit.value });
|
|
298
|
+
}, [state.url, doProbe]);
|
|
299
|
+
const handleProbeRef = useRef(handleProbe);
|
|
300
|
+
handleProbeRef.current = handleProbe;
|
|
301
|
+
useEffect(() => {
|
|
302
|
+
if (transport !== "remote") return;
|
|
303
|
+
if (state.step !== "url") return;
|
|
304
|
+
const trimmed = state.url.trim();
|
|
305
|
+
if (!trimmed) return;
|
|
306
|
+
const handle = setTimeout(() => {
|
|
307
|
+
handleProbeRef.current();
|
|
308
|
+
}, 400);
|
|
309
|
+
return () => clearTimeout(handle);
|
|
310
|
+
}, [transport, state.step, state.url]);
|
|
311
|
+
const registerIntegration = useCallback(
|
|
312
|
+
async (auth) => {
|
|
313
|
+
const displayName = remoteIdentity.name.trim() || probe?.serverName || probe?.name || "MCP";
|
|
314
|
+
const slug = slugifyNamespace(remoteIdentity.namespace) || void 0;
|
|
315
|
+
const exit = await doAddServer({
|
|
316
|
+
payload: {
|
|
317
|
+
transport: "remote",
|
|
318
|
+
name: displayName,
|
|
319
|
+
endpoint: state.url.trim(),
|
|
320
|
+
...slug ? { slug } : {},
|
|
321
|
+
auth
|
|
322
|
+
},
|
|
323
|
+
reactivityKeys: integrationWriteKeys
|
|
324
|
+
});
|
|
325
|
+
if (Exit.isFailure(exit)) {
|
|
326
|
+
dispatch({
|
|
327
|
+
type: "add-fail",
|
|
328
|
+
error: isIntegrationAlreadyExistsExit(exit) ? integrationExistsMessage(slug ?? displayName) : errorMessageFromExit(exit, "Failed to add server")
|
|
329
|
+
});
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
return exit.value.slug;
|
|
333
|
+
},
|
|
334
|
+
[doAddServer, probe, remoteIdentity, state.url]
|
|
335
|
+
);
|
|
336
|
+
const handleAddRemote = useCallback(async () => {
|
|
337
|
+
if (!probe) return;
|
|
338
|
+
dispatch({ type: "add-start" });
|
|
339
|
+
const slug = await registerIntegration(mcpAuthTemplateFromEditorValue(authValue));
|
|
340
|
+
if (slug === null) return;
|
|
341
|
+
props.onComplete(slug);
|
|
342
|
+
}, [probe, authValue, registerIntegration, props]);
|
|
343
|
+
const parseStdioArgs = (raw) => {
|
|
344
|
+
if (!raw.trim()) return [];
|
|
345
|
+
const args = [];
|
|
346
|
+
const regex = /[^\s"]+|"([^"]*)"/g;
|
|
347
|
+
let match2;
|
|
348
|
+
while ((match2 = regex.exec(raw)) !== null) {
|
|
349
|
+
args.push(match2[1] ?? match2[0]);
|
|
350
|
+
}
|
|
351
|
+
return args;
|
|
352
|
+
};
|
|
353
|
+
const parseStdioEnvValue = (raw) => {
|
|
354
|
+
const value2 = raw.trim();
|
|
355
|
+
if (value2.length < 2) return value2;
|
|
356
|
+
const quote = value2[0];
|
|
357
|
+
if (quote !== '"' && quote !== "'" || value2[value2.length - 1] !== quote) {
|
|
358
|
+
return value2;
|
|
359
|
+
}
|
|
360
|
+
const inner = value2.slice(1, -1);
|
|
361
|
+
if (quote === "'") return inner;
|
|
362
|
+
return inner.replace(
|
|
363
|
+
/\\([\\nrt"])/g,
|
|
364
|
+
(_, escaped) => STDIO_ENV_ESCAPE_REPLACEMENTS[escaped] ?? escaped
|
|
365
|
+
);
|
|
366
|
+
};
|
|
367
|
+
const parseStdioEnv = (raw) => {
|
|
368
|
+
if (!raw.trim()) return void 0;
|
|
369
|
+
const env = {};
|
|
370
|
+
for (const line of raw.split("\n")) {
|
|
371
|
+
const eq = line.indexOf("=");
|
|
372
|
+
if (eq > 0) {
|
|
373
|
+
env[line.slice(0, eq).trim()] = parseStdioEnvValue(line.slice(eq + 1));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return Object.keys(env).length > 0 ? env : void 0;
|
|
377
|
+
};
|
|
378
|
+
const handleAddStdio = useCallback(async () => {
|
|
379
|
+
const cmd = stdioCommand.trim();
|
|
380
|
+
if (!cmd) return;
|
|
381
|
+
setStdioAdding(true);
|
|
382
|
+
setStdioError(null);
|
|
383
|
+
const displayName = stdioIdentity.name.trim() || cmd;
|
|
384
|
+
const slug = slugifyNamespace(stdioIdentity.namespace) || void 0;
|
|
385
|
+
const exit = await doAddServer({
|
|
386
|
+
payload: {
|
|
387
|
+
transport: "stdio",
|
|
388
|
+
name: displayName,
|
|
389
|
+
...slug ? { slug } : {},
|
|
390
|
+
command: cmd,
|
|
391
|
+
args: parseStdioArgs(stdioArgs),
|
|
392
|
+
env: parseStdioEnv(stdioEnv)
|
|
393
|
+
},
|
|
394
|
+
reactivityKeys: integrationWriteKeys
|
|
395
|
+
});
|
|
396
|
+
if (Exit.isFailure(exit)) {
|
|
397
|
+
setStdioError(
|
|
398
|
+
isIntegrationAlreadyExistsExit(exit) ? integrationExistsMessage(slug ?? displayName) : errorMessageFromExit(exit, "Failed to add server")
|
|
399
|
+
);
|
|
400
|
+
setStdioAdding(false);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
props.onComplete(exit.value.slug);
|
|
404
|
+
}, [stdioCommand, stdioArgs, stdioEnv, stdioIdentity, doAddServer, props]);
|
|
405
|
+
return /* @__PURE__ */ jsxs2("div", { className: "flex flex-1 flex-col gap-6", children: [
|
|
406
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
407
|
+
/* @__PURE__ */ jsx2("h1", { className: "text-xl font-semibold text-foreground", children: "Add MCP Source" }),
|
|
408
|
+
/* @__PURE__ */ jsx2("p", { className: "mt-1 text-[13px] text-muted-foreground", children: "Connect to an MCP server to discover and use its tools." })
|
|
409
|
+
] }),
|
|
410
|
+
allowStdio && /* @__PURE__ */ jsxs2("div", { className: "flex gap-1 rounded-lg border border-border bg-muted/30 p-1", children: [
|
|
411
|
+
/* @__PURE__ */ jsx2(
|
|
412
|
+
Button2,
|
|
413
|
+
{
|
|
414
|
+
variant: "ghost",
|
|
415
|
+
type: "button",
|
|
416
|
+
onClick: () => setTransport("remote"),
|
|
417
|
+
className: `flex-1 rounded-md px-3 py-1.5 text-sm font-medium transition-colors ${transport === "remote" ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`,
|
|
418
|
+
children: "Remote"
|
|
419
|
+
}
|
|
420
|
+
),
|
|
421
|
+
/* @__PURE__ */ jsx2(
|
|
422
|
+
Button2,
|
|
423
|
+
{
|
|
424
|
+
variant: "ghost",
|
|
425
|
+
type: "button",
|
|
426
|
+
onClick: () => setTransport("stdio"),
|
|
427
|
+
className: `flex-1 rounded-md px-3 py-1.5 text-sm font-medium transition-colors ${transport === "stdio" ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`,
|
|
428
|
+
children: "Stdio"
|
|
429
|
+
}
|
|
430
|
+
)
|
|
431
|
+
] }),
|
|
432
|
+
transport === "remote" ? /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
433
|
+
/* @__PURE__ */ jsx2(
|
|
434
|
+
McpRemoteSourceFields,
|
|
435
|
+
{
|
|
436
|
+
url: state.url,
|
|
437
|
+
onUrlChange: (url) => dispatch({ type: "set-url", url }),
|
|
438
|
+
identity: remoteIdentity,
|
|
439
|
+
preview: probe,
|
|
440
|
+
probing: isProbing,
|
|
441
|
+
error: probeError,
|
|
442
|
+
onRetry: handleProbe
|
|
443
|
+
}
|
|
444
|
+
),
|
|
445
|
+
probe && /* @__PURE__ */ jsxs2("section", { className: "space-y-2.5", children: [
|
|
446
|
+
/* @__PURE__ */ jsx2(FieldLabel, { children: "How does this server authenticate?" }),
|
|
447
|
+
/* @__PURE__ */ jsx2(
|
|
448
|
+
AuthTemplateEditor,
|
|
449
|
+
{
|
|
450
|
+
value: authValue,
|
|
451
|
+
onChange: setAuthValue,
|
|
452
|
+
allowedKinds: allowedAuthKinds,
|
|
453
|
+
oauthMetadata: "discovered"
|
|
454
|
+
}
|
|
455
|
+
)
|
|
456
|
+
] }),
|
|
457
|
+
otherError && /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
|
|
458
|
+
/* @__PURE__ */ jsx2("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2", children: /* @__PURE__ */ jsx2("p", { className: "text-[12px] text-destructive", children: otherError }) }),
|
|
459
|
+
/* @__PURE__ */ jsx2(
|
|
460
|
+
Button2,
|
|
461
|
+
{
|
|
462
|
+
type: "button",
|
|
463
|
+
variant: "outline",
|
|
464
|
+
size: "sm",
|
|
465
|
+
onClick: () => dispatch({ type: "retry" }),
|
|
466
|
+
className: "text-xs",
|
|
467
|
+
children: "Try again"
|
|
468
|
+
}
|
|
469
|
+
)
|
|
470
|
+
] }),
|
|
471
|
+
remoteSlugExists && !isAdding && /* @__PURE__ */ jsx2("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2", children: /* @__PURE__ */ jsxs2("p", { className: "text-[12px] text-destructive", children: [
|
|
472
|
+
'An integration named "',
|
|
473
|
+
remoteSlug,
|
|
474
|
+
'" already exists. To add more authentication, update your existing integration.',
|
|
475
|
+
" ",
|
|
476
|
+
/* @__PURE__ */ jsx2(
|
|
477
|
+
Link,
|
|
478
|
+
{
|
|
479
|
+
to: "/integrations/$namespace",
|
|
480
|
+
params: { namespace: remoteSlug },
|
|
481
|
+
className: "font-medium underline underline-offset-2",
|
|
482
|
+
children: "Open it"
|
|
483
|
+
}
|
|
484
|
+
)
|
|
485
|
+
] }) }),
|
|
486
|
+
/* @__PURE__ */ jsxs2(FloatActions, { children: [
|
|
487
|
+
/* @__PURE__ */ jsx2(
|
|
488
|
+
Button2,
|
|
489
|
+
{
|
|
490
|
+
type: "button",
|
|
491
|
+
variant: "ghost",
|
|
492
|
+
onClick: () => props.onCancel(),
|
|
493
|
+
disabled: isAdding,
|
|
494
|
+
children: "Cancel"
|
|
495
|
+
}
|
|
496
|
+
),
|
|
497
|
+
(probe || isProbing) && /* @__PURE__ */ jsx2(Button2, { type: "button", onClick: handleAddRemote, disabled: !canAdd, children: isAdding ? /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
498
|
+
/* @__PURE__ */ jsx2(Spinner, { className: "size-3.5" }),
|
|
499
|
+
" Adding\u2026"
|
|
500
|
+
] }) : "Add source" })
|
|
501
|
+
] })
|
|
502
|
+
] }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
503
|
+
/* @__PURE__ */ jsx2(CardStack2, { children: /* @__PURE__ */ jsxs2(CardStackContent2, { className: "border-t-0", children: [
|
|
504
|
+
/* @__PURE__ */ jsx2(
|
|
505
|
+
CardStackEntryField2,
|
|
506
|
+
{
|
|
507
|
+
label: "Command",
|
|
508
|
+
description: "- The executable to run (e.g. npx, uvx, node).",
|
|
509
|
+
children: /* @__PURE__ */ jsx2(
|
|
510
|
+
Input2,
|
|
511
|
+
{
|
|
512
|
+
value: stdioCommand,
|
|
513
|
+
onChange: (e) => setStdioCommand(e.target.value),
|
|
514
|
+
placeholder: "npx",
|
|
515
|
+
className: "font-mono text-sm"
|
|
516
|
+
}
|
|
517
|
+
)
|
|
518
|
+
}
|
|
519
|
+
),
|
|
520
|
+
/* @__PURE__ */ jsx2(
|
|
521
|
+
CardStackEntryField2,
|
|
522
|
+
{
|
|
523
|
+
label: "Arguments",
|
|
524
|
+
description: "- Space-separated arguments passed to the command.",
|
|
525
|
+
children: /* @__PURE__ */ jsx2(
|
|
526
|
+
Input2,
|
|
527
|
+
{
|
|
528
|
+
value: stdioArgs,
|
|
529
|
+
onChange: (e) => setStdioArgs(e.target.value),
|
|
530
|
+
placeholder: "-y chrome-devtools-mcp@latest",
|
|
531
|
+
className: "font-mono text-sm"
|
|
532
|
+
}
|
|
533
|
+
)
|
|
534
|
+
}
|
|
535
|
+
),
|
|
536
|
+
/* @__PURE__ */ jsx2(
|
|
537
|
+
CardStackEntryField2,
|
|
538
|
+
{
|
|
539
|
+
label: "Environment variables",
|
|
540
|
+
description: "- One per line, KEY=value format.",
|
|
541
|
+
children: /* @__PURE__ */ jsx2(
|
|
542
|
+
Textarea,
|
|
543
|
+
{
|
|
544
|
+
value: stdioEnv,
|
|
545
|
+
onChange: (e) => setStdioEnv(e.target.value),
|
|
546
|
+
placeholder: "KEY=value\nANOTHER=value",
|
|
547
|
+
rows: 3,
|
|
548
|
+
maxRows: 10,
|
|
549
|
+
className: "font-mono text-sm"
|
|
550
|
+
}
|
|
551
|
+
)
|
|
552
|
+
}
|
|
553
|
+
)
|
|
554
|
+
] }) }),
|
|
555
|
+
/* @__PURE__ */ jsx2(IntegrationIdentityFields, { identity: stdioIdentity, namePlaceholder: "My MCP Server" }),
|
|
556
|
+
stdioError && /* @__PURE__ */ jsx2("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2", children: /* @__PURE__ */ jsx2("p", { className: "text-[12px] text-destructive", children: stdioError }) }),
|
|
557
|
+
stdioSlugExists && !stdioAdding && /* @__PURE__ */ jsx2("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2", children: /* @__PURE__ */ jsxs2("p", { className: "text-[12px] text-destructive", children: [
|
|
558
|
+
'An integration named "',
|
|
559
|
+
stdioSlug,
|
|
560
|
+
'" already exists. To add more authentication, update your existing integration.',
|
|
561
|
+
" ",
|
|
562
|
+
/* @__PURE__ */ jsx2(
|
|
563
|
+
Link,
|
|
564
|
+
{
|
|
565
|
+
to: "/integrations/$namespace",
|
|
566
|
+
params: { namespace: stdioSlug },
|
|
567
|
+
className: "font-medium underline underline-offset-2",
|
|
568
|
+
children: "Open it"
|
|
569
|
+
}
|
|
570
|
+
)
|
|
571
|
+
] }) }),
|
|
572
|
+
/* @__PURE__ */ jsxs2(FloatActions, { children: [
|
|
573
|
+
/* @__PURE__ */ jsx2(
|
|
574
|
+
Button2,
|
|
575
|
+
{
|
|
576
|
+
type: "button",
|
|
577
|
+
variant: "ghost",
|
|
578
|
+
onClick: () => props.onCancel(),
|
|
579
|
+
disabled: stdioAdding,
|
|
580
|
+
children: "Cancel"
|
|
581
|
+
}
|
|
582
|
+
),
|
|
583
|
+
/* @__PURE__ */ jsx2(
|
|
584
|
+
Button2,
|
|
585
|
+
{
|
|
586
|
+
type: "button",
|
|
587
|
+
onClick: handleAddStdio,
|
|
588
|
+
disabled: !stdioCommand.trim() || stdioAdding || stdioSlugExists,
|
|
589
|
+
children: stdioAdding ? /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
590
|
+
/* @__PURE__ */ jsx2(Spinner, { className: "size-3.5" }),
|
|
591
|
+
" Adding\u2026"
|
|
592
|
+
] }) : "Add source"
|
|
593
|
+
}
|
|
594
|
+
)
|
|
595
|
+
] })
|
|
596
|
+
] })
|
|
597
|
+
] });
|
|
598
|
+
}
|
|
599
|
+
export {
|
|
600
|
+
AddMcpSource as default
|
|
601
|
+
};
|
|
602
|
+
//# sourceMappingURL=AddMcpSource-4LLERUW5.js.map
|