@agentdocumentformat/cli 0.7.2
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/LICENSE +21 -0
- package/README.md +45 -0
- package/dist/adf.mjs +162 -0
- package/dist/chunks/APEv2Parser-2CNNHMXV.mjs +22 -0
- package/dist/chunks/AiffParser-7BR4RL6G.mjs +162 -0
- package/dist/chunks/App-L5L37JI5.mjs +16747 -0
- package/dist/chunks/AsfParser-VVRSLFOM.mjs +538 -0
- package/dist/chunks/DsdiffParser-CMD4AX7A.mjs +156 -0
- package/dist/chunks/DsfParser-UOGZDS2M.mjs +94 -0
- package/dist/chunks/FlacParser-HB3T3CD4.mjs +23 -0
- package/dist/chunks/MP4Parser-66HBRUSX.mjs +973 -0
- package/dist/chunks/MatroskaParser-J3HUIMEW.mjs +579 -0
- package/dist/chunks/MpegParser-YW6LIUEW.mjs +442 -0
- package/dist/chunks/MusepackParser-PAUSXP7D.mjs +252 -0
- package/dist/chunks/OggParser-NUBV3GWC.mjs +359 -0
- package/dist/chunks/WavPackParser-WQAGT7TC.mjs +171 -0
- package/dist/chunks/WaveParser-EWLQVDX4.mjs +238 -0
- package/dist/chunks/adf-schema-JRU2BTOG.mjs +62 -0
- package/dist/chunks/build-QF5W473T.mjs +60 -0
- package/dist/chunks/chatgpt-subscription-BQGWYZXE.mjs +23 -0
- package/dist/chunks/chunk-2DIECGFL.mjs +6 -0
- package/dist/chunks/chunk-2NAP2ZF7.mjs +60 -0
- package/dist/chunks/chunk-3AT7IIP7.mjs +25696 -0
- package/dist/chunks/chunk-3GNIDM53.mjs +35 -0
- package/dist/chunks/chunk-3M4ZPOA3.mjs +1477 -0
- package/dist/chunks/chunk-4MQPTCXY.mjs +510 -0
- package/dist/chunks/chunk-5PO5PKRV.mjs +366 -0
- package/dist/chunks/chunk-5QU5VY7N.mjs +52 -0
- package/dist/chunks/chunk-5W7KES6P.mjs +436 -0
- package/dist/chunks/chunk-62NE4LG4.mjs +61 -0
- package/dist/chunks/chunk-64OKE4X3.mjs +45 -0
- package/dist/chunks/chunk-6GJ4QHRI.mjs +1278 -0
- package/dist/chunks/chunk-6VQEXLE2.mjs +144 -0
- package/dist/chunks/chunk-6XUV6EK6.mjs +41 -0
- package/dist/chunks/chunk-776TSXEP.mjs +43 -0
- package/dist/chunks/chunk-7CV2SA3H.mjs +285 -0
- package/dist/chunks/chunk-7MTRVW2U.mjs +3103 -0
- package/dist/chunks/chunk-7N2R2VCG.mjs +1434 -0
- package/dist/chunks/chunk-7T2YMTWL.mjs +374 -0
- package/dist/chunks/chunk-7XYNI52E.mjs +149 -0
- package/dist/chunks/chunk-APHX6HSV.mjs +7731 -0
- package/dist/chunks/chunk-APRU6PHC.mjs +40 -0
- package/dist/chunks/chunk-AUYKRQPJ.mjs +499 -0
- package/dist/chunks/chunk-DXWDD5XJ.mjs +136 -0
- package/dist/chunks/chunk-EWL5FG4I.mjs +39 -0
- package/dist/chunks/chunk-EYX2YLJW.mjs +117 -0
- package/dist/chunks/chunk-F5ELW46V.mjs +27 -0
- package/dist/chunks/chunk-G426FACA.mjs +49 -0
- package/dist/chunks/chunk-GEKSJHIV.mjs +2472 -0
- package/dist/chunks/chunk-HBUENVQL.mjs +570 -0
- package/dist/chunks/chunk-HPVFJYXR.mjs +112 -0
- package/dist/chunks/chunk-HWLWPXQ7.mjs +721 -0
- package/dist/chunks/chunk-I2HJYHFT.mjs +665 -0
- package/dist/chunks/chunk-I3UYSITV.mjs +68 -0
- package/dist/chunks/chunk-IC4JDZCI.mjs +3864 -0
- package/dist/chunks/chunk-IHDDCZBS.mjs +112 -0
- package/dist/chunks/chunk-IIGMZHQQ.mjs +10416 -0
- package/dist/chunks/chunk-KWFOS5WC.mjs +41 -0
- package/dist/chunks/chunk-LAZIXV2F.mjs +5290 -0
- package/dist/chunks/chunk-LFMB4PAA.mjs +10814 -0
- package/dist/chunks/chunk-M2RX3S2T.mjs +49 -0
- package/dist/chunks/chunk-M3WPIJYM.mjs +65 -0
- package/dist/chunks/chunk-NBTUWMGP.mjs +263 -0
- package/dist/chunks/chunk-NGK6PVSF.mjs +2676 -0
- package/dist/chunks/chunk-NZ7JTHEO.mjs +85 -0
- package/dist/chunks/chunk-OQN33RGQ.mjs +52 -0
- package/dist/chunks/chunk-RIX34QHC.mjs +2935 -0
- package/dist/chunks/chunk-SGUTYISF.mjs +16 -0
- package/dist/chunks/chunk-SLJ5L7PY.mjs +1473 -0
- package/dist/chunks/chunk-SLUKGXKH.mjs +68 -0
- package/dist/chunks/chunk-TMT32MXT.mjs +419 -0
- package/dist/chunks/chunk-UYY5M2PB.mjs +151 -0
- package/dist/chunks/chunk-VAUOM3IN.mjs +298 -0
- package/dist/chunks/chunk-VMHTYDH4.mjs +172 -0
- package/dist/chunks/chunk-VZS54AA5.mjs +200 -0
- package/dist/chunks/chunk-WFQUVXML.mjs +295 -0
- package/dist/chunks/chunk-X34PDRUH.mjs +809 -0
- package/dist/chunks/chunk-XNPZBLYL.mjs +33 -0
- package/dist/chunks/chunk-XX7OSWTO.mjs +157 -0
- package/dist/chunks/chunk-XYTWGHFS.mjs +600 -0
- package/dist/chunks/chunk-XYV3VCIX.mjs +336 -0
- package/dist/chunks/chunk-Y5ZLKFLE.mjs +1469 -0
- package/dist/chunks/chunk-ZJKXHM4Q.mjs +230 -0
- package/dist/chunks/cli-TCMBNZF6.mjs +1030 -0
- package/dist/chunks/commands-DTEKM4ZL.mjs +20 -0
- package/dist/chunks/daemon-ZC3FZ2P4.mjs +112449 -0
- package/dist/chunks/daemon-control-WDX7PMTC.mjs +300 -0
- package/dist/chunks/daemon-token-WRSULP7G.mjs +23 -0
- package/dist/chunks/devtools-H7CI2ZVG.mjs +85 -0
- package/dist/chunks/discord-IWDQJA7A.mjs +70588 -0
- package/dist/chunks/dist-C57KYFS2.mjs +4952 -0
- package/dist/chunks/dist-I3XBAGDL.mjs +11 -0
- package/dist/chunks/email-V4Q4VJON.mjs +58679 -0
- package/dist/chunks/environment-NX277MQG.mjs +15 -0
- package/dist/chunks/grok-subscription-4PYX7TBF.mjs +19 -0
- package/dist/chunks/layout-EPJQNFAI.mjs +22 -0
- package/dist/chunks/lib-N7FCZWKH.mjs +4038 -0
- package/dist/chunks/open-OTQDGDH5.mjs +36 -0
- package/dist/chunks/parser-JVY2PWSM.mjs +19 -0
- package/dist/chunks/pipeline-executor-BHBSJX2K.mjs +19 -0
- package/dist/chunks/react-IUIDQ5LX.mjs +11 -0
- package/dist/chunks/screen-54BUTYCW.mjs +27 -0
- package/dist/chunks/slack-3L5QTDZX.mjs +8113 -0
- package/dist/chunks/store-WR7TZJZS.mjs +32 -0
- package/dist/chunks/telegram-5ISVFBV3.mjs +14918 -0
- package/dist/chunks/terminal-O5WO7C5U.mjs +48 -0
- package/dist/chunks/theme-NORU4J74.mjs +20 -0
- package/dist/chunks/themes-F2FKU7CP.mjs +23 -0
- package/dist/chunks/token-6G3HCMNX.mjs +54 -0
- package/dist/chunks/token-util-UXYSDQIW.mjs +12 -0
- package/dist/chunks/tui-ODVYUIMT.mjs +197 -0
- package/dist/chunks/welcome-XHXFV7SH.mjs +32 -0
- package/dist/chunks/whatsapp-XSAMNGPJ.mjs +85412 -0
- package/dist/chunks/xterm-headless-YF53XWXE.mjs +3353 -0
- package/package.json +53 -0
- package/resources/wasm/coreutils.LICENSE +18 -0
- package/resources/wasm/coreutils.wasm +0 -0
|
@@ -0,0 +1,2472 @@
|
|
|
1
|
+
import { createRequire as __adfCreateRequire } from 'node:module';
|
|
2
|
+
import { fileURLToPath as __adfFileURLToPath } from 'node:url';
|
|
3
|
+
import { dirname as __adfDirname } from 'node:path';
|
|
4
|
+
const require = __adfCreateRequire(import.meta.url);
|
|
5
|
+
const __filename = __adfFileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = __adfDirname(__filename);
|
|
7
|
+
import {
|
|
8
|
+
require_jsx_runtime
|
|
9
|
+
} from "./chunk-7CV2SA3H.mjs";
|
|
10
|
+
import {
|
|
11
|
+
WELCOME_OVERLAY
|
|
12
|
+
} from "./chunk-I3UYSITV.mjs";
|
|
13
|
+
import {
|
|
14
|
+
cjs
|
|
15
|
+
} from "./chunk-SGUTYISF.mjs";
|
|
16
|
+
import {
|
|
17
|
+
mcp_config_exports
|
|
18
|
+
} from "./chunk-VMHTYDH4.mjs";
|
|
19
|
+
import {
|
|
20
|
+
Box_default,
|
|
21
|
+
Text
|
|
22
|
+
} from "./chunk-3AT7IIP7.mjs";
|
|
23
|
+
import {
|
|
24
|
+
useTheme
|
|
25
|
+
} from "./chunk-EYX2YLJW.mjs";
|
|
26
|
+
import {
|
|
27
|
+
require_react
|
|
28
|
+
} from "./chunk-6GJ4QHRI.mjs";
|
|
29
|
+
import {
|
|
30
|
+
adapter_registry_exports
|
|
31
|
+
} from "./chunk-UYY5M2PB.mjs";
|
|
32
|
+
import {
|
|
33
|
+
external_exports
|
|
34
|
+
} from "./chunk-ZJKXHM4Q.mjs";
|
|
35
|
+
import {
|
|
36
|
+
__export,
|
|
37
|
+
__toESM
|
|
38
|
+
} from "./chunk-M2RX3S2T.mjs";
|
|
39
|
+
|
|
40
|
+
// src/main/tui/ui/KeyHint.tsx
|
|
41
|
+
var import_react2 = __toESM(require_react(), 1);
|
|
42
|
+
|
|
43
|
+
// src/main/tui/app/keys.tsx
|
|
44
|
+
var import_react = __toESM(require_react(), 1), import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
|
|
45
|
+
function elementRect(element) {
|
|
46
|
+
if (!element?.yogaNode) return null;
|
|
47
|
+
let x = 0, y = 0;
|
|
48
|
+
for (let node = element; node; node = node.parentNode)
|
|
49
|
+
node.yogaNode && (x += node.yogaNode.getComputedLeft(), y += node.yogaNode.getComputedTop());
|
|
50
|
+
return { x, y, width: element.yogaNode.getComputedWidth(), height: element.yogaNode.getComputedHeight() };
|
|
51
|
+
}
|
|
52
|
+
function rectContains(rect, x, y) {
|
|
53
|
+
return !!rect && x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height;
|
|
54
|
+
}
|
|
55
|
+
function createKeyRouter() {
|
|
56
|
+
let bindings = [], wheels = [], clicks = [], nextId = 1;
|
|
57
|
+
return {
|
|
58
|
+
registerClick(binding) {
|
|
59
|
+
return clicks.push(binding), () => {
|
|
60
|
+
let index = clicks.indexOf(binding);
|
|
61
|
+
index >= 0 && clicks.splice(index, 1);
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
click(event) {
|
|
65
|
+
let best = null;
|
|
66
|
+
for (let binding of clicks) {
|
|
67
|
+
if (!binding.active.current) continue;
|
|
68
|
+
let rect = elementRect(binding.ref.current);
|
|
69
|
+
if (!rect || !rectContains(rect, event.x, event.y)) continue;
|
|
70
|
+
let area = rect.width * rect.height;
|
|
71
|
+
(!best || area <= best.area) && (best = { binding, area });
|
|
72
|
+
}
|
|
73
|
+
return best ? best.binding.handler.current(event) === !0 : !1;
|
|
74
|
+
},
|
|
75
|
+
registerWheel(binding) {
|
|
76
|
+
return wheels.push(binding), () => {
|
|
77
|
+
let index = wheels.indexOf(binding);
|
|
78
|
+
index >= 0 && wheels.splice(index, 1);
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
wheel(event, context) {
|
|
82
|
+
let best = null;
|
|
83
|
+
for (let binding of wheels) {
|
|
84
|
+
if (!binding.active.current || binding.layer === "overlay" !== context.overlayOpen) continue;
|
|
85
|
+
let rect = elementRect(binding.ref.current);
|
|
86
|
+
if (!rect || !rectContains(rect, event.x, event.y)) continue;
|
|
87
|
+
let area = rect.width * rect.height;
|
|
88
|
+
(!best || area <= best.area) && (best = { binding, area });
|
|
89
|
+
}
|
|
90
|
+
return !best || event.delta === 0 ? !!best : (best.binding.handler.current(event.delta, event), !0);
|
|
91
|
+
},
|
|
92
|
+
register(layer, handler, active) {
|
|
93
|
+
let binding = { id: nextId++, layer, handler, active };
|
|
94
|
+
return bindings.push(binding), () => {
|
|
95
|
+
let index = bindings.indexOf(binding);
|
|
96
|
+
index >= 0 && bindings.splice(index, 1);
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
dispatch(input, key, context) {
|
|
100
|
+
let order = context.overlayOpen ? ["overlay", "global"] : context.focus === "input" || context.focus === "tabs" ? [context.focus, "global"] : [context.focus, "view", "global"];
|
|
101
|
+
for (let layer of order) {
|
|
102
|
+
let candidates = bindings.filter((b) => b.layer === layer && b.active.current);
|
|
103
|
+
for (let i = candidates.length - 1; i >= 0; i--)
|
|
104
|
+
if (candidates[i].handler.current(input, key) === !0) return !0;
|
|
105
|
+
}
|
|
106
|
+
return !1;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
var KeyRouterContext = (0, import_react.createContext)(null);
|
|
111
|
+
function KeyRouterProvider({ router, children }) {
|
|
112
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(KeyRouterContext.Provider, { value: router, children });
|
|
113
|
+
}
|
|
114
|
+
function useKeyRouter() {
|
|
115
|
+
let router = (0, import_react.useContext)(KeyRouterContext);
|
|
116
|
+
if (!router) throw new Error("useKeys must be used inside the TUI shell");
|
|
117
|
+
return router;
|
|
118
|
+
}
|
|
119
|
+
function useKeys(handler, options) {
|
|
120
|
+
let router = useKeyRouter(), handlerRef = (0, import_react.useRef)(handler), activeRef = (0, import_react.useRef)(options.active !== !1);
|
|
121
|
+
handlerRef.current = handler, activeRef.current = options.active !== !1, (0, import_react.useEffect)(() => router.register(options.layer, handlerRef, activeRef), [router, options.layer]);
|
|
122
|
+
}
|
|
123
|
+
var WHEEL_STEP = 3;
|
|
124
|
+
function useWheel(ref, onWheel, options = {}) {
|
|
125
|
+
let router = useKeyRouter(), handler = (0, import_react.useRef)(onWheel), active = (0, import_react.useRef)(options.active !== !1);
|
|
126
|
+
handler.current = onWheel, active.current = options.active !== !1;
|
|
127
|
+
let layer = options.layer ?? "main";
|
|
128
|
+
(0, import_react.useEffect)(() => router.registerWheel({ layer, ref, handler, active }), [router, layer, ref]);
|
|
129
|
+
}
|
|
130
|
+
function useClick(ref, onClick, options = {}) {
|
|
131
|
+
let router = useKeyRouter(), handler = (0, import_react.useRef)(onClick), active = (0, import_react.useRef)(options.active !== !1);
|
|
132
|
+
handler.current = onClick, active.current = options.active !== !1, (0, import_react.useEffect)(() => router.registerClick({ ref, handler, active }), [router, ref]);
|
|
133
|
+
}
|
|
134
|
+
function keyLabel(spec) {
|
|
135
|
+
let parts = spec.split("+"), ctrl = parts.some((p) => p.toLowerCase() === "ctrl");
|
|
136
|
+
return parts.map((part) => {
|
|
137
|
+
let p = part.toLowerCase();
|
|
138
|
+
return p === "ctrl" ? "Ctrl+" : p === "alt" || p === "meta" ? "Alt+" : p === "shift" ? "Shift+" : p === "cmd" || p === "super" ? "Cmd+" : p === "backspace" ? "Backspace" : p === "delete" || p === "del" ? "Delete" : p === "home" ? "Home" : p === "end" ? "End" : p === "enter" || p === "return" ? "Enter" : p === "esc" || p === "escape" ? "Esc" : p === "tab" ? "Tab" : p === "up" ? "\u2191" : p === "down" ? "\u2193" : p === "left" ? "\u2190" : p === "right" ? "\u2192" : p === "pgup" || p === "pageup" ? "PgUp" : p === "pgdn" || p === "pagedown" ? "PgDn" : part.length === 1 && ctrl ? part.toUpperCase() : part;
|
|
139
|
+
}).join("");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// src/main/tui/ui/KeyHint.tsx
|
|
143
|
+
var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
|
|
144
|
+
function KeyHint({ keys, label }) {
|
|
145
|
+
let theme = useTheme();
|
|
146
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { children: [
|
|
147
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { bold: !0, color: theme.color.accent, children: keys.split(" ").map(keyLabel).join("/") }),
|
|
148
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { color: theme.color.muted, children: [
|
|
149
|
+
" ",
|
|
150
|
+
label
|
|
151
|
+
] })
|
|
152
|
+
] });
|
|
153
|
+
}
|
|
154
|
+
function KeyHints({ hints }) {
|
|
155
|
+
let theme = useTheme();
|
|
156
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { wrap: "truncate-end", children: hints.map((hint, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react2.Fragment, { children: [
|
|
157
|
+
index > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { color: theme.color.dim, children: [
|
|
158
|
+
" ",
|
|
159
|
+
theme.glyph.sep,
|
|
160
|
+
" "
|
|
161
|
+
] }) : null,
|
|
162
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(KeyHint, { ...hint })
|
|
163
|
+
] }, `${hint.keys}:${hint.label}`)) });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/main/tui/identity/model.ts
|
|
167
|
+
var IDENTITY_OVERLAY = "identity", NEW_AGENT_OVERLAY = "new-agent", IDENTITY_BANNER_KEY = "shell.identity.banner";
|
|
168
|
+
var SAVED_WORD = "saved";
|
|
169
|
+
function shortDid(did, keep = 6) {
|
|
170
|
+
if (!did) return "";
|
|
171
|
+
let m = did.match(/^(did:[a-z0-9]+:)(.*)$/i);
|
|
172
|
+
if (!m) return did.length > 16 ? `${did.slice(0, 10)}\u2026${did.slice(-4)}` : did;
|
|
173
|
+
let id = m[2];
|
|
174
|
+
return id.length > keep + 5 ? `${m[1]}${id.slice(0, keep)}\u2026${id.slice(-4)}` : did;
|
|
175
|
+
}
|
|
176
|
+
function normalizePhrase(text) {
|
|
177
|
+
return phraseWords(text).join(" ");
|
|
178
|
+
}
|
|
179
|
+
function phraseWords(text) {
|
|
180
|
+
return text.toLowerCase().split(/[\s,;]+/).map((w) => w.replace(/^\d+[.):-]?$/, "").replace(/^\d+[.):-]/, "").replace(/[^a-z]/g, "")).filter(Boolean);
|
|
181
|
+
}
|
|
182
|
+
function isLoopbackUrl(url) {
|
|
183
|
+
try {
|
|
184
|
+
let host = new URL(url).hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
185
|
+
return host === "localhost" || host === "::1" || /^127\./.test(host) || host.startsWith("::ffff:127.");
|
|
186
|
+
} catch {
|
|
187
|
+
return !1;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
var LOOPBACK_ONLY_TEXT = "The seed phrase and passphrase never travel over the network: create, restore and unlock only work from the daemon\u2019s own machine. Run the terminal app (`adf`) or `adf identity` there.", IDENTITY_EXPLAINER = "Your owner identity proves these agents are yours. It is 12 words: the same words restore it anywhere, in the terminal or in ADF Studio.";
|
|
191
|
+
function identityErrorText(code, fallback, identity) {
|
|
192
|
+
switch (code) {
|
|
193
|
+
case "invalid_mnemonic":
|
|
194
|
+
return "Those are not 12 valid seed words. Check each word and the order.";
|
|
195
|
+
case "owner_mismatch":
|
|
196
|
+
return identity?.ownerDid ? `That phrase belongs to a different owner. This machine\u2019s owner is ${shortDid(identity.ownerDid)}: enter its phrase. (To switch owners, import the new phrase in ADF Studio.)` : "That phrase belongs to a different owner than this machine\u2019s.";
|
|
197
|
+
case "wrong_passphrase":
|
|
198
|
+
return "Wrong passphrase.";
|
|
199
|
+
case "weak_passphrase":
|
|
200
|
+
return "Choose a passphrase of at least 8 characters.";
|
|
201
|
+
case "passphrase_required":
|
|
202
|
+
return "This machine keeps the identity in a passphrase-protected file: enter the passphrase.";
|
|
203
|
+
case "loopback_only":
|
|
204
|
+
return LOOPBACK_ONLY_TEXT;
|
|
205
|
+
case "identity_exists":
|
|
206
|
+
return "An owner identity already exists here; nothing was created.";
|
|
207
|
+
case "nothing_to_unlock":
|
|
208
|
+
return "There is nothing to unlock yet: create or restore an identity.";
|
|
209
|
+
default:
|
|
210
|
+
return fallback;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function identityBadge(identity) {
|
|
214
|
+
if (!identity) return null;
|
|
215
|
+
switch (identity.status) {
|
|
216
|
+
case "ready":
|
|
217
|
+
return identity.backupConfirmed ? { text: `owner ${shortDid(identity.ownerDid, 4).replace(/^did:key:/, "")}`, tone: "muted" } : { text: `owner ${shortDid(identity.ownerDid, 4).replace(/^did:key:/, "")} not backed up`, tone: "warn" };
|
|
218
|
+
case "locked":
|
|
219
|
+
return { text: "owner locked", tone: "warn" };
|
|
220
|
+
case "restore-needed":
|
|
221
|
+
return { text: "owner: restore", tone: "warn" };
|
|
222
|
+
default:
|
|
223
|
+
return { text: "no owner", tone: "warn" };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function onboardingLines(identity) {
|
|
227
|
+
switch (identity.status) {
|
|
228
|
+
case "locked":
|
|
229
|
+
return { title: "Your owner identity is locked", next: "Unlock it with its passphrase to create and seal agents." };
|
|
230
|
+
case "restore-needed":
|
|
231
|
+
return {
|
|
232
|
+
title: "Restore your owner identity",
|
|
233
|
+
next: `This machine already belongs to ${shortDid(identity.ownerDid)}: enter the 12-word phrase for this owner.`
|
|
234
|
+
};
|
|
235
|
+
case "ready":
|
|
236
|
+
return { title: "Owner identity ready", next: "" };
|
|
237
|
+
default:
|
|
238
|
+
return { title: "Set up your owner identity", next: "Create a new one, or restore yours from its 12-word seed phrase." };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function onboardingChoices(identity) {
|
|
242
|
+
switch (identity.status) {
|
|
243
|
+
case "none":
|
|
244
|
+
return [
|
|
245
|
+
{ key: "c", label: "Create new", mode: "create" },
|
|
246
|
+
{ key: "r", label: "Restore from seed phrase", mode: "restore" }
|
|
247
|
+
];
|
|
248
|
+
case "restore-needed":
|
|
249
|
+
return [{ key: "r", label: "Restore from seed phrase", mode: "restore" }];
|
|
250
|
+
case "locked":
|
|
251
|
+
return identity.storage === "file" && identity.passphraseRequired ? [{ key: "u", label: "Unlock with passphrase", mode: "unlock" }] : [];
|
|
252
|
+
default:
|
|
253
|
+
return [];
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
function passphraseProblem(first, second, isNew) {
|
|
257
|
+
return first ? isNew ? first.length < 8 ? "At least 8 characters." : second !== null && first !== second ? "The two passphrases differ. Type them again." : null : null : "Enter the passphrase.";
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// src/main/tui/identity/Onboarding.tsx
|
|
261
|
+
var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
|
|
262
|
+
function openIdentity(store, props = {}) {
|
|
263
|
+
store.actions.pushOverlay({ kind: IDENTITY_OVERLAY, props: { ...props } });
|
|
264
|
+
}
|
|
265
|
+
function openNewAgent(store, name) {
|
|
266
|
+
let identity = store.getState().identity;
|
|
267
|
+
if (identity && identity.status !== "ready") {
|
|
268
|
+
openIdentity(store, { mode: "status", then: NEW_AGENT_OVERLAY, name, reason: "Agents are sealed under your owner identity: set it up first, then the new agent follows." });
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
store.actions.pushOverlay({ kind: NEW_AGENT_OVERLAY, props: name ? { name } : {} });
|
|
272
|
+
}
|
|
273
|
+
function bannerKeyOf(identity) {
|
|
274
|
+
return `${identity.status}:${identity.backupConfirmed ? 1 : 0}`;
|
|
275
|
+
}
|
|
276
|
+
function needsAttention(identity) {
|
|
277
|
+
return !!identity && (identity.status !== "ready" || !identity.backupConfirmed);
|
|
278
|
+
}
|
|
279
|
+
function identityKey(store, identity, input, panel) {
|
|
280
|
+
if (!identity) return !1;
|
|
281
|
+
if (input === "i")
|
|
282
|
+
return openIdentity(store), !0;
|
|
283
|
+
if (input === "I" && !panel && needsAttention(identity))
|
|
284
|
+
return store.actions.setViewState(IDENTITY_BANNER_KEY, bannerKeyOf(identity)), !0;
|
|
285
|
+
if (!panel) return !1;
|
|
286
|
+
let choice = onboardingChoices(identity).find((c) => c.key === input);
|
|
287
|
+
return choice ? (openIdentity(store, { mode: choice.mode }), !0) : !1;
|
|
288
|
+
}
|
|
289
|
+
function IdentityOnboarding({ identity, width, focused }) {
|
|
290
|
+
let theme = useTheme(), { title, next } = onboardingLines(identity), choices = onboardingChoices(identity), inner = Math.max(30, Math.min(width - 2, 72));
|
|
291
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { flexDirection: "column", width: inner, marginTop: 1, children: [
|
|
292
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { bold: !0, color: theme.color.accent, children: [
|
|
293
|
+
theme.glyph.wordmark,
|
|
294
|
+
" ",
|
|
295
|
+
title
|
|
296
|
+
] }),
|
|
297
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: " " }),
|
|
298
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { wrap: "wrap", color: theme.color.text, children: IDENTITY_EXPLAINER }),
|
|
299
|
+
next ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { wrap: "wrap", color: theme.color.muted, children: next }) : null,
|
|
300
|
+
identity.status === "locked" && choices.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { wrap: "wrap", color: theme.color.warn, children: identity.message }) : null,
|
|
301
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: " " }),
|
|
302
|
+
choices.map((c) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { children: [
|
|
303
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { bold: !0, color: theme.color.accent, children: [
|
|
304
|
+
" [",
|
|
305
|
+
c.key,
|
|
306
|
+
"]"
|
|
307
|
+
] }),
|
|
308
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: theme.color.text, children: [
|
|
309
|
+
" ",
|
|
310
|
+
c.label
|
|
311
|
+
] })
|
|
312
|
+
] }, c.key)),
|
|
313
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: " " }),
|
|
314
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(KeyHints, { hints: focused ? [{ keys: "i", label: "details" }, { keys: "o", label: "load an existing .adf" }] : [{ keys: "shift+tab", label: "focus here to choose" }] })
|
|
315
|
+
] });
|
|
316
|
+
}
|
|
317
|
+
function IdentityBanner({ identity, width }) {
|
|
318
|
+
let theme = useTheme(), text = identity.status === "ready" ? `Seed phrase backup not confirmed for ${shortDid(identity.ownerDid, 4)}. Make sure the 12 words are written down.` : identity.status === "locked" ? "Owner identity locked: new agents cannot be created until it is unlocked." : identity.status === "restore-needed" ? `Restore owner ${shortDid(identity.ownerDid, 4)} from its 12 words to create agents here.` : "No owner identity yet: set one up to create agents.";
|
|
319
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { wrap: "truncate-end", children: [
|
|
320
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: theme.color.warn, children: [
|
|
321
|
+
theme.glyph.warn,
|
|
322
|
+
" ",
|
|
323
|
+
text.slice(0, Math.max(10, width - 24))
|
|
324
|
+
] }),
|
|
325
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: theme.color.dim, children: " " }),
|
|
326
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { bold: !0, color: theme.color.accent, children: "i" }),
|
|
327
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: theme.color.muted, children: " open" }),
|
|
328
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: theme.color.dim, children: [
|
|
329
|
+
" ",
|
|
330
|
+
theme.glyph.sep,
|
|
331
|
+
" "
|
|
332
|
+
] }),
|
|
333
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { bold: !0, color: theme.color.accent, children: "I" }),
|
|
334
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: theme.color.muted, children: " hide" })
|
|
335
|
+
] });
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// src/main/tui/setup/channels.ts
|
|
339
|
+
var { ADAPTER_REGISTRY } = cjs(adapter_registry_exports), CHANNELS_OVERLAY = "channels", CHANNEL_ORDER = ["telegram", "discord", "slack", "email", "whatsapp"];
|
|
340
|
+
function channelEntries() {
|
|
341
|
+
let byType = new Map(ADAPTER_REGISTRY.map((e) => [e.type, e]));
|
|
342
|
+
return CHANNEL_ORDER.map((t) => byType.get(t)).filter((e) => !!e);
|
|
343
|
+
}
|
|
344
|
+
function findChannel(type) {
|
|
345
|
+
if (!type) return;
|
|
346
|
+
let wanted = type.trim().toLowerCase();
|
|
347
|
+
return channelEntries().find((e) => e.type === wanted || e.displayName.toLowerCase() === wanted);
|
|
348
|
+
}
|
|
349
|
+
function credentialFields(entry) {
|
|
350
|
+
return entry.credentials ?? [
|
|
351
|
+
...entry.requiredEnvKeys.map((key) => ({ key, label: key, required: !0 })),
|
|
352
|
+
...(entry.optionalEnvKeys ?? []).map((key) => ({ key, label: key, required: !1 }))
|
|
353
|
+
];
|
|
354
|
+
}
|
|
355
|
+
var DEFAULT_ADAPTER_CONFIG = { enabled: !0, policy: { dm: "all", groups: "mention" } }, SHAPES = {
|
|
356
|
+
TELEGRAM_BOT_TOKEN: { test: /^\d{5,}:[A-Za-z0-9_-]{30,}$/, message: "A bot token looks like 123456789:AA\u2026 (digits, a colon, then 30+ characters)." },
|
|
357
|
+
DISCORD_BOT_TOKEN: { test: /^[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{20,}$/, message: "A Discord bot token has three dot-separated parts (Bot page \u2192 Reset Token)." },
|
|
358
|
+
DISCORD_APPLICATION_ID: { test: /^\d{15,22}$/, message: "The application ID is a long number (General Information page)." },
|
|
359
|
+
SLACK_APP_TOKEN: { test: /^xapp-/, message: "The app-level token starts with xapp- (Socket Mode page)." },
|
|
360
|
+
SLACK_BOT_TOKEN: { test: /^xoxb-/, message: "The bot token starts with xoxb- (Install App page)." },
|
|
361
|
+
EMAIL_USERNAME: { test: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: "Enter the full email address." }
|
|
362
|
+
};
|
|
363
|
+
function validateCredentials(entry, values) {
|
|
364
|
+
let errors = {};
|
|
365
|
+
for (let field of credentialFields(entry)) {
|
|
366
|
+
let value = (values[field.key] ?? "").trim();
|
|
367
|
+
if (!value) {
|
|
368
|
+
field.required && (errors[field.key] = `${field.label} is required.`);
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
if (/\s/.test(value)) {
|
|
372
|
+
errors[field.key] = "No spaces: paste the value exactly.";
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
let shape = SHAPES[field.key];
|
|
376
|
+
shape && !shape.test.test(value) && (errors[field.key] = shape.message);
|
|
377
|
+
}
|
|
378
|
+
return errors;
|
|
379
|
+
}
|
|
380
|
+
function adapterLiveState(diag, type) {
|
|
381
|
+
let state = diag?.states?.find((s) => s.type === type);
|
|
382
|
+
return state ? { status: ["connected", "connecting", "disconnected", "error"].includes(state.status) ? state.status : "unknown", ...state.error ? { error: state.error } : {} } : { status: diag?.configured?.some((c) => c.type === type) ? "not running" : "unknown" };
|
|
383
|
+
}
|
|
384
|
+
function configuredChannels(config) {
|
|
385
|
+
return Object.keys(config?.adapters ?? {});
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// src/shared/constants/provider-catalog.ts
|
|
389
|
+
var provider_catalog_exports = {};
|
|
390
|
+
__export(provider_catalog_exports, {
|
|
391
|
+
PROVIDER_CATALOG: () => PROVIDER_CATALOG,
|
|
392
|
+
PROVIDER_CATALOG_GROUPS: () => PROVIDER_CATALOG_GROUPS,
|
|
393
|
+
catalogEntryForProvider: () => catalogEntryForProvider,
|
|
394
|
+
findCatalogEntry: () => findCatalogEntry,
|
|
395
|
+
isSubscriptionType: () => isSubscriptionType,
|
|
396
|
+
nextProviderName: () => nextProviderName,
|
|
397
|
+
providerTypeHint: () => providerTypeHint
|
|
398
|
+
});
|
|
399
|
+
var PROVIDER_CATALOG_GROUPS = [
|
|
400
|
+
{ id: "subscription", label: "Subscriptions", hint: "Sign in with an existing consumer plan. No API key." },
|
|
401
|
+
{ id: "api", label: "APIs", hint: "Pay-as-you-go endpoints. Paste an API key." },
|
|
402
|
+
{ id: "local", label: "Local", hint: "Servers running on this machine. Usually no key." },
|
|
403
|
+
{ id: "other", label: "Other", hint: "Anything that speaks the OpenAI API." }
|
|
404
|
+
], oc = (key, label, baseUrl, description, extra = {}) => ({
|
|
405
|
+
key,
|
|
406
|
+
label,
|
|
407
|
+
type: "openai-compatible",
|
|
408
|
+
group: "api",
|
|
409
|
+
baseUrl,
|
|
410
|
+
description,
|
|
411
|
+
keyPlaceholder: "API key",
|
|
412
|
+
modelPlaceholder: "model id",
|
|
413
|
+
...extra
|
|
414
|
+
}), local = (key, label, baseUrl, description, extra = {}) => ({
|
|
415
|
+
key,
|
|
416
|
+
label,
|
|
417
|
+
type: "openai-compatible",
|
|
418
|
+
group: "local",
|
|
419
|
+
baseUrl,
|
|
420
|
+
description,
|
|
421
|
+
keyPlaceholder: "Usually not needed",
|
|
422
|
+
modelPlaceholder: "loaded model id",
|
|
423
|
+
keyOptional: !0,
|
|
424
|
+
...extra
|
|
425
|
+
}), PROVIDER_CATALOG = [
|
|
426
|
+
// ---- Subscriptions --------------------------------------------------------
|
|
427
|
+
{
|
|
428
|
+
key: "chatgpt-subscription",
|
|
429
|
+
label: "ChatGPT",
|
|
430
|
+
type: "chatgpt-subscription",
|
|
431
|
+
group: "subscription",
|
|
432
|
+
description: "Use your ChatGPT Plus / Pro plan. Sign in with your OpenAI account.",
|
|
433
|
+
modelPlaceholder: "e.g. gpt-5.6-sol",
|
|
434
|
+
iconKey: "openai"
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
key: "grok-subscription",
|
|
438
|
+
label: "Grok",
|
|
439
|
+
type: "grok-subscription",
|
|
440
|
+
group: "subscription",
|
|
441
|
+
description: "Use your SuperGrok or X Premium plan. Sign in with xAI.",
|
|
442
|
+
modelPlaceholder: "e.g. grok-4.5",
|
|
443
|
+
iconKey: "x"
|
|
444
|
+
},
|
|
445
|
+
// ---- First-party APIs -----------------------------------------------------
|
|
446
|
+
{
|
|
447
|
+
key: "anthropic",
|
|
448
|
+
label: "Anthropic",
|
|
449
|
+
type: "anthropic",
|
|
450
|
+
group: "api",
|
|
451
|
+
description: "Claude models via the Anthropic API.",
|
|
452
|
+
keyPlaceholder: "sk-ant-...",
|
|
453
|
+
modelPlaceholder: "e.g. claude-sonnet-4-5",
|
|
454
|
+
iconKey: "anthropic",
|
|
455
|
+
keysUrl: "https://console.anthropic.com/settings/keys"
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
key: "openai",
|
|
459
|
+
label: "OpenAI",
|
|
460
|
+
type: "openai",
|
|
461
|
+
group: "api",
|
|
462
|
+
description: "GPT and o-series models via the OpenAI API.",
|
|
463
|
+
keyPlaceholder: "sk-...",
|
|
464
|
+
modelPlaceholder: "e.g. gpt-4o, o3-mini",
|
|
465
|
+
iconKey: "openai",
|
|
466
|
+
keysUrl: "https://platform.openai.com/api-keys"
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
key: "openrouter",
|
|
470
|
+
label: "OpenRouter",
|
|
471
|
+
type: "openrouter",
|
|
472
|
+
group: "api",
|
|
473
|
+
description: "One key for hundreds of models across providers.",
|
|
474
|
+
keyPlaceholder: "sk-or-...",
|
|
475
|
+
modelPlaceholder: "e.g. anthropic/claude-sonnet-4",
|
|
476
|
+
iconKey: "openrouter",
|
|
477
|
+
keysUrl: "https://openrouter.ai/keys"
|
|
478
|
+
},
|
|
479
|
+
// ---- OpenAI-compatible hosted APIs ---------------------------------------
|
|
480
|
+
oc("gemini", "Google Gemini", "https://generativelanguage.googleapis.com/v1beta/openai", "Gemini models through the OpenAI-compatible endpoint.", { iconKey: "gemini", keysUrl: "https://aistudio.google.com/apikey", modelPlaceholder: "e.g. gemini-2.5-pro" }),
|
|
481
|
+
oc("xai", "xAI", "https://api.x.ai/v1", "Grok models via the xAI API.", { iconKey: "x", keysUrl: "https://console.x.ai", modelPlaceholder: "e.g. grok-4" }),
|
|
482
|
+
oc("mistral", "Mistral", "https://api.mistral.ai/v1", "Mistral and Codestral models.", { iconKey: "mistral", keysUrl: "https://console.mistral.ai/api-keys", modelPlaceholder: "e.g. mistral-large-latest" }),
|
|
483
|
+
oc("deepseek", "DeepSeek", "https://api.deepseek.com/v1", "DeepSeek chat and reasoner models.", { iconKey: "deepseek", keysUrl: "https://platform.deepseek.com/api_keys", modelPlaceholder: "e.g. deepseek-chat" }),
|
|
484
|
+
oc("groq", "Groq", "https://api.groq.com/openai/v1", "Fast inference for open models on LPUs.", { keysUrl: "https://console.groq.com/keys", modelPlaceholder: "e.g. llama-3.3-70b-versatile" }),
|
|
485
|
+
oc("cerebras", "Cerebras", "https://api.cerebras.ai/v1", "Very fast inference for open models.", { keysUrl: "https://cloud.cerebras.ai", modelPlaceholder: "e.g. llama-3.3-70b" }),
|
|
486
|
+
oc("together", "Together AI", "https://api.together.xyz/v1", "Open models, fine-tunes, and dedicated endpoints.", { keysUrl: "https://api.together.ai/settings/api-keys", modelPlaceholder: "e.g. meta-llama/Llama-3.3-70B-Instruct-Turbo" }),
|
|
487
|
+
oc("fireworks", "Fireworks AI", "https://api.fireworks.ai/inference/v1", "Fast serverless inference for open models.", { keysUrl: "https://fireworks.ai/account/api-keys", modelPlaceholder: "e.g. accounts/fireworks/models/llama-v3p3-70b-instruct" }),
|
|
488
|
+
oc("perplexity", "Perplexity", "https://api.perplexity.ai", "Sonar models with built-in web search.", { iconKey: "perplexity", keysUrl: "https://www.perplexity.ai/settings/api", modelPlaceholder: "e.g. sonar-pro" }),
|
|
489
|
+
oc("cohere", "Cohere", "https://api.cohere.ai/compatibility/v1", "Command models through the compatibility API.", { keysUrl: "https://dashboard.cohere.com/api-keys", modelPlaceholder: "e.g. command-a-03-2025" }),
|
|
490
|
+
oc("huggingface", "Hugging Face", "https://router.huggingface.co/v1", "Inference Providers router. Uses your HF token.", { iconKey: "huggingface", keysUrl: "https://huggingface.co/settings/tokens", keyPlaceholder: "hf_...", modelPlaceholder: "e.g. meta-llama/Llama-3.3-70B-Instruct" }),
|
|
491
|
+
oc("nvidia", "NVIDIA NIM", "https://integrate.api.nvidia.com/v1", "Hosted NIM endpoints on build.nvidia.com.", { iconKey: "nvidia", keysUrl: "https://build.nvidia.com", keyPlaceholder: "nvapi-...", modelPlaceholder: "e.g. meta/llama-3.3-70b-instruct" }),
|
|
492
|
+
oc("vercel-ai-gateway", "Vercel AI Gateway", "https://ai-gateway.vercel.sh/v1", "Route to many providers through one Vercel key.", { iconKey: "vercel", keysUrl: "https://vercel.com/dashboard/ai-gateway", modelPlaceholder: "e.g. anthropic/claude-sonnet-4" }),
|
|
493
|
+
oc("cloudflare", "Cloudflare Workers AI", "https://api.cloudflare.com/client/v4/accounts/YOUR_ACCOUNT_ID/ai/v1", "Open models at the edge. Replace YOUR_ACCOUNT_ID in the base URL.", { iconKey: "cloudflare", keysUrl: "https://dash.cloudflare.com/profile/api-tokens", modelPlaceholder: "e.g. @cf/meta/llama-3.3-70b-instruct-fp8-fast" }),
|
|
494
|
+
oc("azure-openai", "Azure OpenAI", "https://YOUR_RESOURCE.openai.azure.com/openai/v1", "Azure-hosted OpenAI models. Replace YOUR_RESOURCE in the base URL.", { keysUrl: "https://portal.azure.com", modelPlaceholder: "your deployment name" }),
|
|
495
|
+
oc("deepinfra", "DeepInfra", "https://api.deepinfra.com/v1/openai", "Low-cost hosting for open models.", { keysUrl: "https://deepinfra.com/dash/api_keys", modelPlaceholder: "e.g. meta-llama/Llama-3.3-70B-Instruct" }),
|
|
496
|
+
oc("sambanova", "SambaNova", "https://api.sambanova.ai/v1", "Fast inference on SambaNova RDUs.", { keysUrl: "https://cloud.sambanova.ai/apis", modelPlaceholder: "e.g. Meta-Llama-3.3-70B-Instruct" }),
|
|
497
|
+
oc("nebius", "Nebius", "https://api.studio.nebius.com/v1", "Nebius AI Studio open-model endpoints.", { keysUrl: "https://studio.nebius.com/settings/api-keys", modelPlaceholder: "e.g. meta-llama/Llama-3.3-70B-Instruct" }),
|
|
498
|
+
oc("hyperbolic", "Hyperbolic", "https://api.hyperbolic.xyz/v1", "Open models on decentralized GPU capacity.", { keysUrl: "https://app.hyperbolic.xyz/settings", modelPlaceholder: "e.g. meta-llama/Llama-3.3-70B-Instruct" }),
|
|
499
|
+
oc("novita", "Novita AI", "https://api.novita.ai/v3/openai", "Serverless open-model inference.", { keysUrl: "https://novita.ai/settings/key-management", modelPlaceholder: "e.g. meta-llama/llama-3.3-70b-instruct" }),
|
|
500
|
+
oc("baseten", "Baseten", "https://inference.baseten.co/v1", "Model APIs and dedicated deployments.", { keysUrl: "https://app.baseten.co/settings/api_keys", modelPlaceholder: "e.g. deepseek-ai/DeepSeek-V3-0324" }),
|
|
501
|
+
oc("scaleway", "Scaleway", "https://api.scaleway.ai/v1", "EU-hosted generative APIs.", { iconKey: "scaleway", keysUrl: "https://console.scaleway.com/iam/api-keys", modelPlaceholder: "e.g. llama-3.3-70b-instruct" }),
|
|
502
|
+
oc("venice", "Venice", "https://api.venice.ai/api/v1", "Private, uncensored open-model inference.", { keysUrl: "https://venice.ai/settings/api", modelPlaceholder: "e.g. llama-3.3-70b" }),
|
|
503
|
+
oc("kluster", "kluster.ai", "https://api.kluster.ai/v1", "Open-model inference with batch pricing.", { keysUrl: "https://platform.kluster.ai/apikeys", modelPlaceholder: "e.g. deepseek-ai/DeepSeek-V3-0324" }),
|
|
504
|
+
oc("moonshot", "Moonshot (Kimi)", "https://api.moonshot.ai/v1", "Kimi models from Moonshot AI.", { iconKey: "kimi", keysUrl: "https://platform.moonshot.ai/console/api-keys", modelPlaceholder: "e.g. kimi-k2-0711-preview" }),
|
|
505
|
+
oc("zai", "Z.ai (GLM)", "https://api.z.ai/api/paas/v4", "GLM models from Zhipu / Z.ai.", { keysUrl: "https://z.ai/manage-apikey/apikey-list", modelPlaceholder: "e.g. glm-4.5" }),
|
|
506
|
+
oc("qwen", "Qwen (Alibaba)", "https://dashscope-intl.aliyuncs.com/compatible-mode/v1", "Qwen models via Model Studio (international).", { iconKey: "qwen", keysUrl: "https://modelstudio.console.alibabacloud.com/?tab=playground#/api-key", modelPlaceholder: "e.g. qwen-plus" }),
|
|
507
|
+
oc("minimax", "MiniMax", "https://api.minimax.io/v1", "MiniMax text models.", { iconKey: "minimax", keysUrl: "https://platform.minimax.io/user-center/basic-information/interface-key", modelPlaceholder: "e.g. MiniMax-M1" }),
|
|
508
|
+
oc("inception", "Inception", "https://api.inceptionlabs.ai/v1", "Mercury diffusion language models.", { keysUrl: "https://platform.inceptionlabs.ai", modelPlaceholder: "e.g. mercury-coder" }),
|
|
509
|
+
// ---- Local servers ---------------------------------------------------------
|
|
510
|
+
local("lmstudio", "LM Studio", "http://localhost:1234/v1", "Local server from the LM Studio app.", { iconKey: "lmstudio" }),
|
|
511
|
+
local("ollama", "Ollama", "http://localhost:11434/v1", "Local models served by Ollama.", { iconKey: "ollama", modelPlaceholder: "e.g. llama3.3" }),
|
|
512
|
+
local("vllm", "vLLM", "http://localhost:8000/v1", "vLLM OpenAI-compatible server.", { iconKey: "vllm" }),
|
|
513
|
+
local("llamacpp", "llama.cpp", "http://localhost:8080/v1", "llama-server from llama.cpp."),
|
|
514
|
+
local("litellm", "LiteLLM", "http://localhost:4000/v1", "LiteLLM proxy in front of any provider.", { keyOptional: !1, keyPlaceholder: "Proxy key, if set" }),
|
|
515
|
+
local("jan", "Jan", "http://localhost:1337/v1", "Local server from the Jan app."),
|
|
516
|
+
local("localai", "LocalAI", "http://localhost:8080/v1", "LocalAI drop-in OpenAI server."),
|
|
517
|
+
local("tgwui", "text-generation-webui", "http://localhost:5000/v1", "oobabooga OpenAI extension."),
|
|
518
|
+
// ---- Other -----------------------------------------------------------------
|
|
519
|
+
{
|
|
520
|
+
key: "openai-compatible",
|
|
521
|
+
label: "OpenAI-compatible",
|
|
522
|
+
type: "openai-compatible",
|
|
523
|
+
group: "other",
|
|
524
|
+
description: "Any endpoint that speaks the OpenAI chat API. Enter the base URL.",
|
|
525
|
+
keyPlaceholder: "Optional",
|
|
526
|
+
modelPlaceholder: "model id"
|
|
527
|
+
}
|
|
528
|
+
], BY_KEY = new Map(PROVIDER_CATALOG.map((e) => [e.key, e]));
|
|
529
|
+
function findCatalogEntry(key) {
|
|
530
|
+
return key ? BY_KEY.get(key) : void 0;
|
|
531
|
+
}
|
|
532
|
+
function catalogEntryForProvider(p) {
|
|
533
|
+
return findCatalogEntry(p.preset) ?? findCatalogEntry(p.type);
|
|
534
|
+
}
|
|
535
|
+
function nextProviderName(label, existing) {
|
|
536
|
+
let taken = new Set(existing.map((p) => p.name.trim().toLowerCase()));
|
|
537
|
+
if (!taken.has(label.toLowerCase())) return label;
|
|
538
|
+
for (let n = 2; n < 1e3; n++) {
|
|
539
|
+
let candidate = `${label} ${n}`;
|
|
540
|
+
if (!taken.has(candidate.toLowerCase())) return candidate;
|
|
541
|
+
}
|
|
542
|
+
return `${label} ${Date.now()}`;
|
|
543
|
+
}
|
|
544
|
+
function providerTypeHint(type) {
|
|
545
|
+
switch (type) {
|
|
546
|
+
case "anthropic":
|
|
547
|
+
return "Anthropic API";
|
|
548
|
+
case "openai":
|
|
549
|
+
return "OpenAI API";
|
|
550
|
+
case "openrouter":
|
|
551
|
+
return "OpenRouter API";
|
|
552
|
+
case "openai-compatible":
|
|
553
|
+
return "OpenAI-compatible API";
|
|
554
|
+
case "chatgpt-subscription":
|
|
555
|
+
return "ChatGPT subscription (OAuth)";
|
|
556
|
+
case "grok-subscription":
|
|
557
|
+
return "Grok subscription (OAuth)";
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
function isSubscriptionType(type) {
|
|
561
|
+
return type === "chatgpt-subscription" || type === "grok-subscription";
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// src/main/tui/setup/provider.ts
|
|
565
|
+
var { PROVIDER_CATALOG: PROVIDER_CATALOG2 } = cjs(provider_catalog_exports), PROVIDER_OVERLAY = "provider.add", API_TYPES = /* @__PURE__ */ new Set(["anthropic", "openai", "openrouter", "openai-compatible"]);
|
|
566
|
+
function providerRows() {
|
|
567
|
+
let order = { subscription: 0, api: 1, local: 2, other: 3 };
|
|
568
|
+
return [...PROVIDER_CATALOG2].filter((e) => e.group === "subscription" || API_TYPES.has(e.type)).sort((a, b) => (order[a.group] ?? 9) - (order[b.group] ?? 9)).map((entry) => ({
|
|
569
|
+
entry,
|
|
570
|
+
...entry.type === "chatgpt-subscription" ? { signIn: "chatgpt" } : entry.type === "grok-subscription" ? { signIn: "grok" } : {}
|
|
571
|
+
}));
|
|
572
|
+
}
|
|
573
|
+
function findProviderRow(key) {
|
|
574
|
+
if (!key) return;
|
|
575
|
+
let wanted = key.trim().toLowerCase();
|
|
576
|
+
return providerRows().find((r) => r.entry.key === wanted || r.entry.label.toLowerCase() === wanted || r.entry.type === wanted);
|
|
577
|
+
}
|
|
578
|
+
function keyOptional(entry) {
|
|
579
|
+
return entry.keyOptional === !0 || entry.group === "other";
|
|
580
|
+
}
|
|
581
|
+
function needsBaseUrl(entry) {
|
|
582
|
+
return entry.type === "openai-compatible";
|
|
583
|
+
}
|
|
584
|
+
function validateProvider(entry, v) {
|
|
585
|
+
let errors = {};
|
|
586
|
+
if (needsBaseUrl(entry)) {
|
|
587
|
+
let url = v.baseUrl.trim();
|
|
588
|
+
if (!url) errors.baseUrl = "The server\u2019s base URL, e.g. http://localhost:11434/v1.";
|
|
589
|
+
else if (/YOUR_[A-Z_]+/.test(url)) errors.baseUrl = "Replace the YOUR_\u2026 part of the URL with your own value.";
|
|
590
|
+
else
|
|
591
|
+
try {
|
|
592
|
+
let parsed = new URL(url);
|
|
593
|
+
parsed.protocol !== "http:" && parsed.protocol !== "https:" && (errors.baseUrl = "Use an http(s) URL.");
|
|
594
|
+
} catch {
|
|
595
|
+
errors.baseUrl = "Not a URL.";
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
let key = v.apiKey.trim();
|
|
599
|
+
return !key && !keyOptional(entry) ? errors.apiKey = "Paste the API key." : /\s/.test(key) && (errors.apiKey = "No spaces: paste the key exactly."), errors;
|
|
600
|
+
}
|
|
601
|
+
function providerInput(entry, v) {
|
|
602
|
+
let key = v.apiKey.trim();
|
|
603
|
+
return {
|
|
604
|
+
type: entry.type,
|
|
605
|
+
name: v.name.trim() || entry.label,
|
|
606
|
+
...needsBaseUrl(entry) ? { baseUrl: v.baseUrl.trim() } : {},
|
|
607
|
+
...v.defaultModel.trim() ? { defaultModel: v.defaultModel.trim() } : {},
|
|
608
|
+
preset: entry.key,
|
|
609
|
+
...key ? { apiKey: key } : {}
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
function keyStorageText(storage) {
|
|
613
|
+
return storage === "file" ? "the daemon\u2019s passphrase-protected secret file" : "the OS keychain";
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// src/shared/constants/mcp-registry.ts
|
|
617
|
+
var mcp_registry_exports = {};
|
|
618
|
+
__export(mcp_registry_exports, {
|
|
619
|
+
BUNDLED_REGISTRY_UPDATED_AT: () => BUNDLED_REGISTRY_UPDATED_AT,
|
|
620
|
+
HOST_ONLY_REGISTRY_ENTRY_NAMES: () => HOST_ONLY_REGISTRY_ENTRY_NAMES,
|
|
621
|
+
MCP_REGISTRY: () => MCP_REGISTRY,
|
|
622
|
+
REGISTRY_ARG_PLACEHOLDER_RE: () => REGISTRY_ARG_PLACEHOLDER_RE,
|
|
623
|
+
findEntryIn: () => findEntryIn,
|
|
624
|
+
findRegistryEntry: () => findRegistryEntry,
|
|
625
|
+
findRegistryEntryByName: () => findRegistryEntryByName,
|
|
626
|
+
findRegistryEntryByPypiPackage: () => findRegistryEntryByPypiPackage,
|
|
627
|
+
findRegistryEntryByUrl: () => findRegistryEntryByUrl,
|
|
628
|
+
hasUnresolvedPlaceholderArgs: () => hasUnresolvedPlaceholderArgs,
|
|
629
|
+
registrationFromRegistryEntry: () => registrationFromRegistryEntry,
|
|
630
|
+
registryEntryRunLocation: () => registryEntryRunLocation
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// mcp-registry.json
|
|
634
|
+
var mcp_registry_default = {
|
|
635
|
+
$notes: "Curated registry of well-known MCP servers, maintained per docs/design/mcp-registry-expansion.md. Never map an entry to the name-squatted packages: PyPI mcp-gmail / telegram-mcp / mcp-server-milvus, npm mcp-filesystem-server / github-mcp-server \u2014 they squat well-known project names but are unrelated (or hostile) third parties.",
|
|
636
|
+
schemaVersion: 1,
|
|
637
|
+
updatedAt: "2026-08-25",
|
|
638
|
+
entries: [
|
|
639
|
+
{
|
|
640
|
+
name: "filesystem",
|
|
641
|
+
displayName: "Filesystem",
|
|
642
|
+
npmPackage: "@modelcontextprotocol/server-filesystem",
|
|
643
|
+
args: [
|
|
644
|
+
"{directory}"
|
|
645
|
+
],
|
|
646
|
+
description: "Read, write, and manage local files and directories",
|
|
647
|
+
category: "tools",
|
|
648
|
+
requiredEnvKeys: [],
|
|
649
|
+
repo: "https://github.com/modelcontextprotocol/servers/blob/main/src/filesystem",
|
|
650
|
+
runLocation: "host",
|
|
651
|
+
verified: !0
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
name: "playwright",
|
|
655
|
+
displayName: "Playwright Browser",
|
|
656
|
+
npmPackage: "@playwright/mcp",
|
|
657
|
+
description: "Maintained browser automation that attaches to the agent's persistent visible Chromium session",
|
|
658
|
+
category: "tools",
|
|
659
|
+
requiredEnvKeys: [],
|
|
660
|
+
repo: "https://github.com/microsoft/playwright-mcp",
|
|
661
|
+
verified: !0
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
name: "sequential-thinking",
|
|
665
|
+
displayName: "Sequential Thinking",
|
|
666
|
+
npmPackage: "@modelcontextprotocol/server-sequential-thinking",
|
|
667
|
+
description: "Dynamic, reflective problem-solving through thought sequences",
|
|
668
|
+
category: "tools",
|
|
669
|
+
requiredEnvKeys: [],
|
|
670
|
+
repo: "https://github.com/modelcontextprotocol/servers/tree/main/src/sequentialthinking",
|
|
671
|
+
verified: !0
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
name: "markitdown",
|
|
675
|
+
displayName: "MarkItDown",
|
|
676
|
+
pypiPackage: "markitdown-mcp",
|
|
677
|
+
runtime: "python",
|
|
678
|
+
description: "Convert documents (PDF, Office, images, audio) to Markdown",
|
|
679
|
+
category: "tools",
|
|
680
|
+
requiredEnvKeys: [],
|
|
681
|
+
repo: "https://github.com/microsoft/markitdown",
|
|
682
|
+
runLocation: "host",
|
|
683
|
+
verified: !0
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
name: "pandoc",
|
|
687
|
+
displayName: "Pandoc",
|
|
688
|
+
iconKey: "pandoc",
|
|
689
|
+
pypiPackage: "mcp-pandoc",
|
|
690
|
+
runtime: "python",
|
|
691
|
+
description: "Convert documents between Markdown, HTML, DOCX, PDF, EPUB, and more",
|
|
692
|
+
category: "tools",
|
|
693
|
+
requiredEnvKeys: [],
|
|
694
|
+
repo: "https://github.com/vivekVells/mcp-pandoc",
|
|
695
|
+
runLocation: "host",
|
|
696
|
+
verified: !1,
|
|
697
|
+
prerequisite: "Needs the pandoc binary installed where the server runs (host run recommended; PDF output additionally needs TeX)."
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
name: "brave-search",
|
|
701
|
+
displayName: "Brave Search",
|
|
702
|
+
iconKey: "brave",
|
|
703
|
+
npmPackage: "@brave/brave-search-mcp-server",
|
|
704
|
+
description: "Search the web using Brave Search API",
|
|
705
|
+
category: "search",
|
|
706
|
+
requiredEnvKeys: [
|
|
707
|
+
"BRAVE_API_KEY"
|
|
708
|
+
],
|
|
709
|
+
repo: "https://github.com/brave/brave-search-mcp-server",
|
|
710
|
+
verified: !0
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
name: "context7",
|
|
714
|
+
displayName: "Context7",
|
|
715
|
+
npmPackage: "@upstash/context7-mcp",
|
|
716
|
+
description: "Up-to-date, version-specific library documentation and code examples",
|
|
717
|
+
category: "search",
|
|
718
|
+
requiredEnvKeys: [],
|
|
719
|
+
repo: "https://github.com/upstash/context7",
|
|
720
|
+
verified: !0
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
name: "tavily",
|
|
724
|
+
displayName: "Tavily",
|
|
725
|
+
npmPackage: "tavily-mcp",
|
|
726
|
+
description: "AI-optimized web search, extract, map, and crawl via the Tavily API",
|
|
727
|
+
category: "search",
|
|
728
|
+
requiredEnvKeys: [
|
|
729
|
+
"TAVILY_API_KEY"
|
|
730
|
+
],
|
|
731
|
+
repo: "https://github.com/tavily-ai/tavily-mcp",
|
|
732
|
+
verified: !0
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
name: "exa",
|
|
736
|
+
displayName: "Exa",
|
|
737
|
+
npmPackage: "exa-mcp-server",
|
|
738
|
+
description: "Neural web search and content retrieval via the Exa API",
|
|
739
|
+
category: "search",
|
|
740
|
+
requiredEnvKeys: [
|
|
741
|
+
"EXA_API_KEY"
|
|
742
|
+
],
|
|
743
|
+
repo: "https://github.com/exa-labs/exa-mcp-server",
|
|
744
|
+
verified: !0
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
name: "perplexity",
|
|
748
|
+
displayName: "Perplexity",
|
|
749
|
+
iconKey: "perplexity",
|
|
750
|
+
npmPackage: "@perplexity-ai/mcp-server",
|
|
751
|
+
description: "Web search, research, and reasoning via the Perplexity Sonar API",
|
|
752
|
+
category: "search",
|
|
753
|
+
requiredEnvKeys: [
|
|
754
|
+
"PERPLEXITY_API_KEY"
|
|
755
|
+
],
|
|
756
|
+
repo: "https://github.com/perplexityai/modelcontextprotocol",
|
|
757
|
+
verified: !0
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
name: "kagi",
|
|
761
|
+
displayName: "Kagi Search",
|
|
762
|
+
iconKey: "kagi",
|
|
763
|
+
pypiPackage: "kagimcp",
|
|
764
|
+
runtime: "python",
|
|
765
|
+
description: "Search the web using the Kagi Search API",
|
|
766
|
+
category: "search",
|
|
767
|
+
requiredEnvKeys: [
|
|
768
|
+
"KAGI_API_KEY"
|
|
769
|
+
],
|
|
770
|
+
repo: "https://github.com/kagisearch/kagi-mcp",
|
|
771
|
+
verified: !0,
|
|
772
|
+
prerequisite: "Needs a Kagi account with Search API access; the server pins Python 3.12."
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
name: "aws-docs",
|
|
776
|
+
displayName: "AWS Documentation",
|
|
777
|
+
pypiPackage: "awslabs.aws-documentation-mcp-server",
|
|
778
|
+
runtime: "python",
|
|
779
|
+
description: "Search, read, and get recommendations from AWS documentation",
|
|
780
|
+
category: "search",
|
|
781
|
+
requiredEnvKeys: [],
|
|
782
|
+
repo: "https://github.com/awslabs/mcp",
|
|
783
|
+
verified: !0
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
name: "deepwiki",
|
|
787
|
+
displayName: "DeepWiki",
|
|
788
|
+
url: "https://mcp.deepwiki.com/mcp",
|
|
789
|
+
description: "Ask questions about any public GitHub repository via DeepWiki",
|
|
790
|
+
category: "search",
|
|
791
|
+
requiredEnvKeys: [],
|
|
792
|
+
repo: "https://docs.devin.ai/work-with-devin/deepwiki-mcp",
|
|
793
|
+
verified: !0
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
name: "microsoft-learn",
|
|
797
|
+
displayName: "Microsoft Learn",
|
|
798
|
+
url: "https://learn.microsoft.com/api/mcp",
|
|
799
|
+
description: "Search and fetch official Microsoft and Azure documentation and code samples",
|
|
800
|
+
category: "search",
|
|
801
|
+
requiredEnvKeys: [],
|
|
802
|
+
repo: "https://github.com/microsoftdocs/mcp",
|
|
803
|
+
verified: !0
|
|
804
|
+
},
|
|
805
|
+
{
|
|
806
|
+
name: "cloudflare-docs",
|
|
807
|
+
displayName: "Cloudflare Docs",
|
|
808
|
+
iconKey: "cloudflare",
|
|
809
|
+
url: "https://docs.mcp.cloudflare.com/mcp",
|
|
810
|
+
description: "Search Cloudflare developer documentation",
|
|
811
|
+
category: "search",
|
|
812
|
+
requiredEnvKeys: [],
|
|
813
|
+
repo: "https://github.com/cloudflare/mcp-server-cloudflare",
|
|
814
|
+
verified: !0
|
|
815
|
+
},
|
|
816
|
+
{
|
|
817
|
+
name: "duckduckgo",
|
|
818
|
+
displayName: "DuckDuckGo",
|
|
819
|
+
iconKey: "duckduckgo",
|
|
820
|
+
pypiPackage: "duckduckgo-mcp-server",
|
|
821
|
+
runtime: "python",
|
|
822
|
+
description: "Zero-key web search via DuckDuckGo with built-in client-side rate limiting",
|
|
823
|
+
category: "search",
|
|
824
|
+
requiredEnvKeys: [],
|
|
825
|
+
repo: "https://github.com/nickclyde/duckduckgo-mcp-server",
|
|
826
|
+
verified: !1
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
name: "serper",
|
|
830
|
+
displayName: "Serper",
|
|
831
|
+
pypiPackage: "serper-mcp-server",
|
|
832
|
+
runtime: "python",
|
|
833
|
+
description: "Google results (web, images, news, maps, scholar) via the Serper.dev API",
|
|
834
|
+
category: "search",
|
|
835
|
+
requiredEnvKeys: [
|
|
836
|
+
"SERPER_API_KEY"
|
|
837
|
+
],
|
|
838
|
+
repo: "https://github.com/garylab/serper-mcp-server",
|
|
839
|
+
verified: !1
|
|
840
|
+
},
|
|
841
|
+
{
|
|
842
|
+
name: "searxng",
|
|
843
|
+
displayName: "SearXNG",
|
|
844
|
+
iconKey: "searxng",
|
|
845
|
+
npmPackage: "mcp-searxng",
|
|
846
|
+
description: "Meta-search through a SearXNG instance, plus URL reading",
|
|
847
|
+
category: "search",
|
|
848
|
+
requiredEnvKeys: [
|
|
849
|
+
"SEARXNG_URL"
|
|
850
|
+
],
|
|
851
|
+
optionalEnvKeys: [
|
|
852
|
+
"AUTH_USERNAME",
|
|
853
|
+
"AUTH_PASSWORD"
|
|
854
|
+
],
|
|
855
|
+
repo: "https://github.com/ihor-sokoliuk/mcp-searxng",
|
|
856
|
+
verified: !1,
|
|
857
|
+
prerequisite: "Needs a reachable SearXNG instance with the JSON output format enabled."
|
|
858
|
+
},
|
|
859
|
+
{
|
|
860
|
+
name: "arxiv",
|
|
861
|
+
displayName: "arXiv",
|
|
862
|
+
iconKey: "arxiv",
|
|
863
|
+
pypiPackage: "arxiv-mcp-server",
|
|
864
|
+
runtime: "python",
|
|
865
|
+
description: "Search, download, and read arXiv papers",
|
|
866
|
+
category: "search",
|
|
867
|
+
requiredEnvKeys: [],
|
|
868
|
+
repo: "https://github.com/blazickjp/arxiv-mcp-server",
|
|
869
|
+
verified: !1
|
|
870
|
+
},
|
|
871
|
+
{
|
|
872
|
+
name: "fetch",
|
|
873
|
+
displayName: "Fetch",
|
|
874
|
+
pypiPackage: "mcp-server-fetch",
|
|
875
|
+
runtime: "python",
|
|
876
|
+
description: "Fetch web pages and convert them to markdown for reading",
|
|
877
|
+
category: "web",
|
|
878
|
+
requiredEnvKeys: [],
|
|
879
|
+
repo: "https://github.com/modelcontextprotocol/servers/tree/main/src/fetch",
|
|
880
|
+
verified: !0,
|
|
881
|
+
prerequisite: "Can reach local and internal addresses (SSRF-capable) \u2014 the container run location is the safe default."
|
|
882
|
+
},
|
|
883
|
+
{
|
|
884
|
+
name: "firecrawl",
|
|
885
|
+
displayName: "Firecrawl",
|
|
886
|
+
npmPackage: "firecrawl-mcp",
|
|
887
|
+
description: "Web scraping, crawling, and structured extraction via the Firecrawl API",
|
|
888
|
+
category: "web",
|
|
889
|
+
requiredEnvKeys: [
|
|
890
|
+
"FIRECRAWL_API_KEY"
|
|
891
|
+
],
|
|
892
|
+
optionalEnvKeys: [
|
|
893
|
+
"FIRECRAWL_API_URL"
|
|
894
|
+
],
|
|
895
|
+
repo: "https://github.com/firecrawl/firecrawl-mcp-server",
|
|
896
|
+
verified: !0
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
name: "apify",
|
|
900
|
+
displayName: "Apify",
|
|
901
|
+
npmPackage: "@apify/actors-mcp-server",
|
|
902
|
+
description: "Run Apify Actors for web scraping and automation at scale",
|
|
903
|
+
category: "web",
|
|
904
|
+
requiredEnvKeys: [
|
|
905
|
+
"APIFY_TOKEN"
|
|
906
|
+
],
|
|
907
|
+
repo: "https://github.com/apify/apify-mcp-server",
|
|
908
|
+
verified: !0
|
|
909
|
+
},
|
|
910
|
+
{
|
|
911
|
+
name: "oxylabs",
|
|
912
|
+
displayName: "Oxylabs",
|
|
913
|
+
pypiPackage: "oxylabs-mcp",
|
|
914
|
+
runtime: "python",
|
|
915
|
+
description: "Proxy-backed web scraping (universal, Google, Amazon) via Oxylabs",
|
|
916
|
+
category: "web",
|
|
917
|
+
requiredEnvKeys: [
|
|
918
|
+
"OXYLABS_USERNAME",
|
|
919
|
+
"OXYLABS_PASSWORD"
|
|
920
|
+
],
|
|
921
|
+
repo: "https://github.com/oxylabs/oxylabs-mcp",
|
|
922
|
+
verified: !0,
|
|
923
|
+
prerequisite: "Needs a paid Oxylabs account with Web Scraper API access."
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
name: "youtube-transcript",
|
|
927
|
+
displayName: "YouTube Transcript",
|
|
928
|
+
npmPackage: "@kimtaeyoon83/mcp-server-youtube-transcript",
|
|
929
|
+
description: "Fetch transcripts and captions for YouTube videos",
|
|
930
|
+
category: "web",
|
|
931
|
+
requiredEnvKeys: [],
|
|
932
|
+
repo: "https://github.com/kimtaeyoon83/mcp-server-youtube-transcript",
|
|
933
|
+
verified: !1,
|
|
934
|
+
prerequisite: "YouTube may block datacenter IPs \u2014 if transcript fetches fail from a container, run on host."
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
name: "github",
|
|
938
|
+
displayName: "GitHub",
|
|
939
|
+
iconKey: "github",
|
|
940
|
+
url: "https://api.githubcopilot.com/mcp/",
|
|
941
|
+
oauth: !0,
|
|
942
|
+
bearerTokenEnvVar: "GITHUB_PERSONAL_ACCESS_TOKEN",
|
|
943
|
+
description: "Official GitHub remote server \u2014 repositories, issues, pull requests, and actions",
|
|
944
|
+
category: "dev",
|
|
945
|
+
requiredEnvKeys: [
|
|
946
|
+
"GITHUB_PERSONAL_ACCESS_TOKEN"
|
|
947
|
+
],
|
|
948
|
+
repo: "https://github.com/github/github-mcp-server",
|
|
949
|
+
verified: !0,
|
|
950
|
+
prerequisite: "Needs a GitHub personal access token (github.com/settings/tokens) scoped to the repositories it should reach."
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
name: "git",
|
|
954
|
+
displayName: "Git",
|
|
955
|
+
pypiPackage: "mcp-server-git",
|
|
956
|
+
runtime: "python",
|
|
957
|
+
description: "Inspect and operate on local Git repositories (repository path is passed per tool call)",
|
|
958
|
+
category: "dev",
|
|
959
|
+
requiredEnvKeys: [],
|
|
960
|
+
repo: "https://github.com/modelcontextprotocol/servers/tree/main/src/git",
|
|
961
|
+
runLocation: "host",
|
|
962
|
+
verified: !0
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
name: "sentry",
|
|
966
|
+
displayName: "Sentry",
|
|
967
|
+
iconKey: "sentry",
|
|
968
|
+
npmPackage: "@sentry/mcp-server",
|
|
969
|
+
description: "Query Sentry issues, events, and projects",
|
|
970
|
+
category: "dev",
|
|
971
|
+
requiredEnvKeys: [
|
|
972
|
+
"SENTRY_ACCESS_TOKEN"
|
|
973
|
+
],
|
|
974
|
+
optionalEnvKeys: [
|
|
975
|
+
"SENTRY_HOST"
|
|
976
|
+
],
|
|
977
|
+
repo: "https://github.com/getsentry/sentry-mcp",
|
|
978
|
+
verified: !0
|
|
979
|
+
},
|
|
980
|
+
{
|
|
981
|
+
name: "sentry-remote",
|
|
982
|
+
displayName: "Sentry (remote)",
|
|
983
|
+
iconKey: "sentry",
|
|
984
|
+
url: "https://mcp.sentry.dev/mcp",
|
|
985
|
+
oauth: !0,
|
|
986
|
+
description: "Official Sentry remote server \u2014 issues, events, releases, and projects via browser OAuth",
|
|
987
|
+
category: "dev",
|
|
988
|
+
requiredEnvKeys: [],
|
|
989
|
+
repo: "https://github.com/getsentry/sentry-mcp",
|
|
990
|
+
verified: !0,
|
|
991
|
+
prerequisite: "Sign in to Sentry in your browser on first connect \u2014 no token to paste."
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
name: "supabase-remote",
|
|
995
|
+
displayName: "Supabase (remote)",
|
|
996
|
+
iconKey: "supabase",
|
|
997
|
+
url: "https://mcp.supabase.com/mcp",
|
|
998
|
+
oauth: !0,
|
|
999
|
+
description: "Official Supabase remote server \u2014 database, auth, storage, and edge functions via browser OAuth",
|
|
1000
|
+
category: "dev",
|
|
1001
|
+
requiredEnvKeys: [],
|
|
1002
|
+
repo: "https://github.com/supabase-community/supabase-mcp",
|
|
1003
|
+
verified: !0,
|
|
1004
|
+
prerequisite: "Sign in to Supabase in your browser on first connect \u2014 no token to paste."
|
|
1005
|
+
},
|
|
1006
|
+
{
|
|
1007
|
+
name: "supabase",
|
|
1008
|
+
displayName: "Supabase",
|
|
1009
|
+
iconKey: "supabase",
|
|
1010
|
+
npmPackage: "@supabase/mcp-server-supabase",
|
|
1011
|
+
description: "Manage Supabase projects: database, auth, storage, and edge functions",
|
|
1012
|
+
category: "dev",
|
|
1013
|
+
requiredEnvKeys: [
|
|
1014
|
+
"SUPABASE_ACCESS_TOKEN"
|
|
1015
|
+
],
|
|
1016
|
+
repo: "https://github.com/supabase-community/supabase-mcp",
|
|
1017
|
+
verified: !0
|
|
1018
|
+
},
|
|
1019
|
+
{
|
|
1020
|
+
name: "semgrep",
|
|
1021
|
+
displayName: "Semgrep",
|
|
1022
|
+
pypiPackage: "semgrep-mcp",
|
|
1023
|
+
runtime: "python",
|
|
1024
|
+
description: "Static-analysis security scanning of code with Semgrep",
|
|
1025
|
+
category: "dev",
|
|
1026
|
+
requiredEnvKeys: [],
|
|
1027
|
+
repo: "https://github.com/semgrep/mcp",
|
|
1028
|
+
runLocation: "host",
|
|
1029
|
+
verified: !0
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
name: "chrome-devtools",
|
|
1033
|
+
displayName: "Chrome DevTools",
|
|
1034
|
+
iconKey: "chrome",
|
|
1035
|
+
npmPackage: "chrome-devtools-mcp",
|
|
1036
|
+
description: "Drive and debug Chrome via the DevTools protocol \u2014 performance traces, network, console",
|
|
1037
|
+
category: "dev",
|
|
1038
|
+
requiredEnvKeys: [],
|
|
1039
|
+
repo: "https://github.com/ChromeDevTools/chrome-devtools-mcp",
|
|
1040
|
+
runLocation: "host",
|
|
1041
|
+
verified: !0,
|
|
1042
|
+
prerequisite: "Drives a local Chrome installation \u2014 run on host with Chrome installed."
|
|
1043
|
+
},
|
|
1044
|
+
{
|
|
1045
|
+
name: "shopify-dev",
|
|
1046
|
+
displayName: "Shopify Dev",
|
|
1047
|
+
iconKey: "shopify",
|
|
1048
|
+
npmPackage: "@shopify/dev-mcp",
|
|
1049
|
+
description: "Shopify developer docs and API schema exploration (zero-config, docs only)",
|
|
1050
|
+
category: "dev",
|
|
1051
|
+
requiredEnvKeys: [],
|
|
1052
|
+
repo: "https://github.com/Shopify/dev-mcp",
|
|
1053
|
+
verified: !0
|
|
1054
|
+
},
|
|
1055
|
+
{
|
|
1056
|
+
name: "gitlab",
|
|
1057
|
+
displayName: "GitLab",
|
|
1058
|
+
iconKey: "gitlab",
|
|
1059
|
+
npmPackage: "@zereight/mcp-gitlab",
|
|
1060
|
+
description: "GitLab projects, issues, merge requests, pipelines, and wikis",
|
|
1061
|
+
category: "dev",
|
|
1062
|
+
requiredEnvKeys: [
|
|
1063
|
+
"GITLAB_API_URL",
|
|
1064
|
+
"GITLAB_PERSONAL_ACCESS_TOKEN"
|
|
1065
|
+
],
|
|
1066
|
+
optionalEnvKeys: [
|
|
1067
|
+
"GITLAB_PERMISSION_MODE"
|
|
1068
|
+
],
|
|
1069
|
+
repo: "https://github.com/zereight/gitlab-mcp",
|
|
1070
|
+
verified: !1,
|
|
1071
|
+
prerequisite: "Set GITLAB_API_URL to your instance API base (https://gitlab.com/api/v4 for gitlab.com) and use a personal access token."
|
|
1072
|
+
},
|
|
1073
|
+
{
|
|
1074
|
+
name: "n8n",
|
|
1075
|
+
displayName: "n8n",
|
|
1076
|
+
iconKey: "n8n",
|
|
1077
|
+
npmPackage: "n8n-mcp",
|
|
1078
|
+
description: "n8n node documentation, workflow building, and optional workflow management",
|
|
1079
|
+
category: "dev",
|
|
1080
|
+
requiredEnvKeys: [],
|
|
1081
|
+
optionalEnvKeys: [
|
|
1082
|
+
"N8N_API_URL",
|
|
1083
|
+
"N8N_API_KEY"
|
|
1084
|
+
],
|
|
1085
|
+
repo: "https://github.com/czlonkowski/n8n-mcp",
|
|
1086
|
+
verified: !1,
|
|
1087
|
+
prerequisite: "Documentation tools work with no configuration; managing workflows needs your n8n instance URL and API key."
|
|
1088
|
+
},
|
|
1089
|
+
{
|
|
1090
|
+
name: "memory",
|
|
1091
|
+
displayName: "Memory",
|
|
1092
|
+
npmPackage: "@modelcontextprotocol/server-memory",
|
|
1093
|
+
description: "Persistent knowledge graph memory for agents",
|
|
1094
|
+
category: "data",
|
|
1095
|
+
requiredEnvKeys: [],
|
|
1096
|
+
repo: "https://github.com/modelcontextprotocol/servers/blob/main/src/memory",
|
|
1097
|
+
verified: !0
|
|
1098
|
+
},
|
|
1099
|
+
{
|
|
1100
|
+
name: "mongodb",
|
|
1101
|
+
displayName: "MongoDB",
|
|
1102
|
+
iconKey: "mongodb",
|
|
1103
|
+
npmPackage: "mongodb-mcp-server",
|
|
1104
|
+
description: "Query and manage MongoDB databases and Atlas clusters",
|
|
1105
|
+
category: "data",
|
|
1106
|
+
requiredEnvKeys: [
|
|
1107
|
+
"MDB_MCP_CONNECTION_STRING"
|
|
1108
|
+
],
|
|
1109
|
+
repo: "https://github.com/mongodb-js/mongodb-mcp-server",
|
|
1110
|
+
verified: !0
|
|
1111
|
+
},
|
|
1112
|
+
{
|
|
1113
|
+
name: "redis",
|
|
1114
|
+
displayName: "Redis",
|
|
1115
|
+
iconKey: "redis",
|
|
1116
|
+
pypiPackage: "redis-mcp-server",
|
|
1117
|
+
runtime: "python",
|
|
1118
|
+
description: "Query and manage Redis data structures",
|
|
1119
|
+
category: "data",
|
|
1120
|
+
requiredEnvKeys: [
|
|
1121
|
+
"REDIS_URL"
|
|
1122
|
+
],
|
|
1123
|
+
optionalEnvKeys: [
|
|
1124
|
+
"REDIS_HOST",
|
|
1125
|
+
"REDIS_PORT",
|
|
1126
|
+
"REDIS_USERNAME",
|
|
1127
|
+
"REDIS_PWD"
|
|
1128
|
+
],
|
|
1129
|
+
repo: "https://github.com/redis/mcp-redis",
|
|
1130
|
+
verified: !0
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
name: "clickhouse",
|
|
1134
|
+
displayName: "ClickHouse",
|
|
1135
|
+
iconKey: "clickhouse",
|
|
1136
|
+
pypiPackage: "mcp-clickhouse",
|
|
1137
|
+
runtime: "python",
|
|
1138
|
+
description: "Run queries against ClickHouse (writes gated off by default)",
|
|
1139
|
+
category: "data",
|
|
1140
|
+
requiredEnvKeys: [
|
|
1141
|
+
"CLICKHOUSE_HOST",
|
|
1142
|
+
"CLICKHOUSE_USER",
|
|
1143
|
+
"CLICKHOUSE_PASSWORD"
|
|
1144
|
+
],
|
|
1145
|
+
optionalEnvKeys: [
|
|
1146
|
+
"CLICKHOUSE_PORT",
|
|
1147
|
+
"CLICKHOUSE_SECURE",
|
|
1148
|
+
"CLICKHOUSE_DATABASE"
|
|
1149
|
+
],
|
|
1150
|
+
repo: "https://github.com/ClickHouse/mcp-clickhouse",
|
|
1151
|
+
verified: !0
|
|
1152
|
+
},
|
|
1153
|
+
{
|
|
1154
|
+
name: "duckdb",
|
|
1155
|
+
displayName: "DuckDB / MotherDuck",
|
|
1156
|
+
iconKey: "duckdb",
|
|
1157
|
+
pypiPackage: "mcp-server-motherduck",
|
|
1158
|
+
runtime: "python",
|
|
1159
|
+
args: [
|
|
1160
|
+
"--db-path",
|
|
1161
|
+
"{database-path}"
|
|
1162
|
+
],
|
|
1163
|
+
description: "Query local DuckDB files or MotherDuck cloud databases (read-only by default)",
|
|
1164
|
+
category: "data",
|
|
1165
|
+
requiredEnvKeys: [],
|
|
1166
|
+
optionalEnvKeys: [
|
|
1167
|
+
"MOTHERDUCK_TOKEN"
|
|
1168
|
+
],
|
|
1169
|
+
repo: "https://github.com/motherduckdb/mcp-server-motherduck",
|
|
1170
|
+
runLocation: "host",
|
|
1171
|
+
verified: !0,
|
|
1172
|
+
prerequisite: "Database path can be a local .duckdb file or :memory:; MOTHERDUCK_TOKEN is only needed for MotherDuck cloud."
|
|
1173
|
+
},
|
|
1174
|
+
{
|
|
1175
|
+
name: "dbhub",
|
|
1176
|
+
displayName: "DBHub",
|
|
1177
|
+
npmPackage: "@bytebase/dbhub",
|
|
1178
|
+
description: "Universal database gateway: SQLite, PostgreSQL, MySQL, MariaDB, and SQL Server",
|
|
1179
|
+
category: "data",
|
|
1180
|
+
requiredEnvKeys: [
|
|
1181
|
+
"DSN"
|
|
1182
|
+
],
|
|
1183
|
+
repo: "https://github.com/bytebase/dbhub",
|
|
1184
|
+
verified: !0,
|
|
1185
|
+
prerequisite: "Set DSN to the database connection string (e.g. postgres://user:pass@host/db); requires Node 22.5+."
|
|
1186
|
+
},
|
|
1187
|
+
{
|
|
1188
|
+
name: "chroma",
|
|
1189
|
+
displayName: "Chroma",
|
|
1190
|
+
pypiPackage: "chroma-mcp",
|
|
1191
|
+
runtime: "python",
|
|
1192
|
+
description: "Vector search and collections on Chroma (ephemeral mode is zero-config)",
|
|
1193
|
+
category: "data",
|
|
1194
|
+
requiredEnvKeys: [],
|
|
1195
|
+
optionalEnvKeys: [
|
|
1196
|
+
"CHROMA_CLIENT_TYPE",
|
|
1197
|
+
"CHROMA_DATA_DIR",
|
|
1198
|
+
"CHROMA_API_KEY",
|
|
1199
|
+
"CHROMA_TENANT",
|
|
1200
|
+
"CHROMA_DATABASE"
|
|
1201
|
+
],
|
|
1202
|
+
repo: "https://github.com/chroma-core/chroma-mcp",
|
|
1203
|
+
verified: !0
|
|
1204
|
+
},
|
|
1205
|
+
{
|
|
1206
|
+
name: "qdrant",
|
|
1207
|
+
displayName: "Qdrant",
|
|
1208
|
+
iconKey: "qdrant",
|
|
1209
|
+
pypiPackage: "mcp-server-qdrant",
|
|
1210
|
+
runtime: "python",
|
|
1211
|
+
description: "Semantic memory on Qdrant with built-in local embeddings (fastembed)",
|
|
1212
|
+
category: "data",
|
|
1213
|
+
requiredEnvKeys: [
|
|
1214
|
+
"QDRANT_URL",
|
|
1215
|
+
"COLLECTION_NAME"
|
|
1216
|
+
],
|
|
1217
|
+
optionalEnvKeys: [
|
|
1218
|
+
"QDRANT_API_KEY"
|
|
1219
|
+
],
|
|
1220
|
+
repo: "https://github.com/qdrant/mcp-server-qdrant",
|
|
1221
|
+
verified: !0
|
|
1222
|
+
},
|
|
1223
|
+
{
|
|
1224
|
+
name: "pinecone",
|
|
1225
|
+
displayName: "Pinecone",
|
|
1226
|
+
npmPackage: "@pinecone-database/mcp",
|
|
1227
|
+
description: "Search and manage Pinecone vector indexes",
|
|
1228
|
+
category: "data",
|
|
1229
|
+
requiredEnvKeys: [
|
|
1230
|
+
"PINECONE_API_KEY"
|
|
1231
|
+
],
|
|
1232
|
+
repo: "https://github.com/pinecone-io/pinecone-mcp",
|
|
1233
|
+
verified: !0
|
|
1234
|
+
},
|
|
1235
|
+
{
|
|
1236
|
+
name: "dbt",
|
|
1237
|
+
displayName: "dbt",
|
|
1238
|
+
pypiPackage: "dbt-mcp",
|
|
1239
|
+
runtime: "python",
|
|
1240
|
+
description: "dbt platform: semantic layer, discovery, and dbt CLI operations",
|
|
1241
|
+
category: "data",
|
|
1242
|
+
requiredEnvKeys: [
|
|
1243
|
+
"DBT_HOST",
|
|
1244
|
+
"DBT_TOKEN",
|
|
1245
|
+
"DBT_PROD_ENV_ID"
|
|
1246
|
+
],
|
|
1247
|
+
optionalEnvKeys: [
|
|
1248
|
+
"DBT_ACCOUNT_ID",
|
|
1249
|
+
"DBT_DEV_ENV_ID",
|
|
1250
|
+
"DBT_USER_ID"
|
|
1251
|
+
],
|
|
1252
|
+
repo: "https://github.com/dbt-labs/dbt-mcp",
|
|
1253
|
+
verified: !0
|
|
1254
|
+
},
|
|
1255
|
+
{
|
|
1256
|
+
name: "neon",
|
|
1257
|
+
displayName: "Neon",
|
|
1258
|
+
url: "https://mcp.neon.tech/mcp",
|
|
1259
|
+
oauth: !0,
|
|
1260
|
+
bearerTokenEnvVar: "NEON_API_KEY",
|
|
1261
|
+
description: "Manage Neon serverless Postgres projects, branches, and queries",
|
|
1262
|
+
category: "data",
|
|
1263
|
+
requiredEnvKeys: [
|
|
1264
|
+
"NEON_API_KEY"
|
|
1265
|
+
],
|
|
1266
|
+
repo: "https://github.com/neondatabase/mcp-server-neon",
|
|
1267
|
+
verified: !0
|
|
1268
|
+
},
|
|
1269
|
+
{
|
|
1270
|
+
name: "postgres",
|
|
1271
|
+
displayName: "PostgreSQL",
|
|
1272
|
+
iconKey: "postgresql",
|
|
1273
|
+
pypiPackage: "postgres-mcp",
|
|
1274
|
+
runtime: "python",
|
|
1275
|
+
description: "Postgres queries, schema intelligence, index tuning, and health checks",
|
|
1276
|
+
category: "data",
|
|
1277
|
+
requiredEnvKeys: [
|
|
1278
|
+
"DATABASE_URI"
|
|
1279
|
+
],
|
|
1280
|
+
repo: "https://github.com/crystaldba/postgres-mcp",
|
|
1281
|
+
verified: !1
|
|
1282
|
+
},
|
|
1283
|
+
{
|
|
1284
|
+
name: "mysql",
|
|
1285
|
+
displayName: "MySQL",
|
|
1286
|
+
iconKey: "mysql",
|
|
1287
|
+
npmPackage: "@benborla29/mcp-server-mysql",
|
|
1288
|
+
description: "Query MySQL databases (read-only by default, writes gated per-operation)",
|
|
1289
|
+
category: "data",
|
|
1290
|
+
requiredEnvKeys: [
|
|
1291
|
+
"MYSQL_HOST",
|
|
1292
|
+
"MYSQL_PORT",
|
|
1293
|
+
"MYSQL_USER",
|
|
1294
|
+
"MYSQL_PASS",
|
|
1295
|
+
"MYSQL_DB"
|
|
1296
|
+
],
|
|
1297
|
+
optionalEnvKeys: [
|
|
1298
|
+
"ALLOW_INSERT_OPERATION",
|
|
1299
|
+
"ALLOW_UPDATE_OPERATION",
|
|
1300
|
+
"ALLOW_DELETE_OPERATION"
|
|
1301
|
+
],
|
|
1302
|
+
repo: "https://github.com/benborla/mcp-server-mysql",
|
|
1303
|
+
verified: !1
|
|
1304
|
+
},
|
|
1305
|
+
{
|
|
1306
|
+
name: "sqlite",
|
|
1307
|
+
displayName: "SQLite",
|
|
1308
|
+
iconKey: "sqlite",
|
|
1309
|
+
npmPackage: "mcp-sqlite",
|
|
1310
|
+
args: [
|
|
1311
|
+
"{database-path}"
|
|
1312
|
+
],
|
|
1313
|
+
description: "Query and manage a local SQLite database file",
|
|
1314
|
+
category: "data",
|
|
1315
|
+
requiredEnvKeys: [],
|
|
1316
|
+
repo: "https://github.com/jparkerweb/mcp-sqlite",
|
|
1317
|
+
runLocation: "host",
|
|
1318
|
+
verified: !1
|
|
1319
|
+
},
|
|
1320
|
+
{
|
|
1321
|
+
name: "airtable",
|
|
1322
|
+
displayName: "Airtable",
|
|
1323
|
+
iconKey: "airtable",
|
|
1324
|
+
npmPackage: "airtable-mcp-server",
|
|
1325
|
+
description: "Read and write Airtable bases, tables, and records",
|
|
1326
|
+
category: "data",
|
|
1327
|
+
requiredEnvKeys: [
|
|
1328
|
+
"AIRTABLE_API_KEY"
|
|
1329
|
+
],
|
|
1330
|
+
repo: "https://github.com/domdomegg/airtable-mcp-server",
|
|
1331
|
+
verified: !1,
|
|
1332
|
+
prerequisite: "AIRTABLE_API_KEY is an Airtable personal access token from airtable.com/create/tokens (schema + records scopes)."
|
|
1333
|
+
},
|
|
1334
|
+
{
|
|
1335
|
+
name: "google-sheets",
|
|
1336
|
+
displayName: "Google Sheets",
|
|
1337
|
+
iconKey: "google-sheets",
|
|
1338
|
+
pypiPackage: "mcp-google-sheets",
|
|
1339
|
+
runtime: "python",
|
|
1340
|
+
description: "Read and write Google Sheets spreadsheets",
|
|
1341
|
+
category: "data",
|
|
1342
|
+
requiredEnvKeys: [
|
|
1343
|
+
"SERVICE_ACCOUNT_PATH",
|
|
1344
|
+
"DRIVE_FOLDER_ID"
|
|
1345
|
+
],
|
|
1346
|
+
repo: "https://github.com/xing5/mcp-google-sheets",
|
|
1347
|
+
runLocation: "host",
|
|
1348
|
+
verified: !1,
|
|
1349
|
+
prerequisite: "Headless service-account mode: needs a Google Cloud service-account JSON key (Sheets + Drive APIs enabled) and a Drive folder shared with the service account's email. SERVICE_ACCOUNT_PATH points at the key file."
|
|
1350
|
+
},
|
|
1351
|
+
{
|
|
1352
|
+
name: "excel",
|
|
1353
|
+
displayName: "Excel",
|
|
1354
|
+
pypiPackage: "excel-mcp-server",
|
|
1355
|
+
runtime: "python",
|
|
1356
|
+
args: [
|
|
1357
|
+
"stdio"
|
|
1358
|
+
],
|
|
1359
|
+
description: "Create, read, and edit Excel workbooks without Excel installed",
|
|
1360
|
+
category: "data",
|
|
1361
|
+
requiredEnvKeys: [],
|
|
1362
|
+
repo: "https://github.com/haris-musa/excel-mcp-server",
|
|
1363
|
+
runLocation: "host",
|
|
1364
|
+
verified: !1
|
|
1365
|
+
},
|
|
1366
|
+
{
|
|
1367
|
+
name: "google-drive",
|
|
1368
|
+
displayName: "Google Drive",
|
|
1369
|
+
iconKey: "google-drive",
|
|
1370
|
+
npmPackage: "@piotr-agier/google-drive-mcp",
|
|
1371
|
+
description: "Read, search, and manage Google Drive, Docs, Sheets, and Slides",
|
|
1372
|
+
category: "data",
|
|
1373
|
+
requiredEnvKeys: [],
|
|
1374
|
+
repo: "https://github.com/piotr-agier/google-drive-mcp",
|
|
1375
|
+
runLocation: "host",
|
|
1376
|
+
verified: !1,
|
|
1377
|
+
auth: !0,
|
|
1378
|
+
authArgs: [
|
|
1379
|
+
"auth"
|
|
1380
|
+
],
|
|
1381
|
+
credentialFiles: [
|
|
1382
|
+
{
|
|
1383
|
+
path: "~/.config/google-drive-mcp/gcp-oauth.keys.json",
|
|
1384
|
+
required: !0
|
|
1385
|
+
},
|
|
1386
|
+
{
|
|
1387
|
+
path: "~/.config/google-drive-mcp/tokens.json"
|
|
1388
|
+
}
|
|
1389
|
+
],
|
|
1390
|
+
prerequisite: "Needs a Google OAuth client JSON (Desktop app) from console.cloud.google.com with the Drive, Docs, Sheets, and Slides APIs enabled."
|
|
1391
|
+
},
|
|
1392
|
+
{
|
|
1393
|
+
name: "resend",
|
|
1394
|
+
displayName: "Resend",
|
|
1395
|
+
npmPackage: "resend-mcp",
|
|
1396
|
+
description: "Send emails, manage contacts, and broadcasts via the Resend platform",
|
|
1397
|
+
category: "communication",
|
|
1398
|
+
requiredEnvKeys: [
|
|
1399
|
+
"RESEND_API_KEY"
|
|
1400
|
+
],
|
|
1401
|
+
optionalEnvKeys: [
|
|
1402
|
+
"SENDER_EMAIL_ADDRESS",
|
|
1403
|
+
"REPLY_TO_EMAIL_ADDRESSES"
|
|
1404
|
+
],
|
|
1405
|
+
repo: "https://github.com/resend/resend-mcp",
|
|
1406
|
+
verified: !0
|
|
1407
|
+
},
|
|
1408
|
+
{
|
|
1409
|
+
name: "slack",
|
|
1410
|
+
displayName: "Slack",
|
|
1411
|
+
npmPackage: "@zencoderai/slack-mcp-server",
|
|
1412
|
+
description: "Interact with Slack workspaces via a bot token",
|
|
1413
|
+
category: "communication",
|
|
1414
|
+
requiredEnvKeys: [
|
|
1415
|
+
"SLACK_BOT_TOKEN",
|
|
1416
|
+
"SLACK_TEAM_ID"
|
|
1417
|
+
],
|
|
1418
|
+
optionalEnvKeys: [
|
|
1419
|
+
"SLACK_CHANNEL_IDS"
|
|
1420
|
+
],
|
|
1421
|
+
repo: "https://github.com/zencoderai/slack-mcp-server",
|
|
1422
|
+
verified: !1,
|
|
1423
|
+
prerequisite: "Needs a Slack app (api.slack.com/apps) with bot scopes channels:history, channels:read, chat:write, reactions:write, users:read."
|
|
1424
|
+
},
|
|
1425
|
+
{
|
|
1426
|
+
name: "email",
|
|
1427
|
+
displayName: "Email (IMAP/SMTP)",
|
|
1428
|
+
npmPackage: "@codefuturist/email-mcp",
|
|
1429
|
+
description: "Search, read, and send email over IMAP and SMTP",
|
|
1430
|
+
category: "communication",
|
|
1431
|
+
requiredEnvKeys: [
|
|
1432
|
+
"MCP_EMAIL_ADDRESS",
|
|
1433
|
+
"MCP_EMAIL_PASSWORD",
|
|
1434
|
+
"MCP_EMAIL_IMAP_HOST",
|
|
1435
|
+
"MCP_EMAIL_SMTP_HOST"
|
|
1436
|
+
],
|
|
1437
|
+
optionalEnvKeys: [
|
|
1438
|
+
"MCP_EMAIL_IMAP_PORT",
|
|
1439
|
+
"MCP_EMAIL_SMTP_PORT",
|
|
1440
|
+
"MCP_EMAIL_IMAP_TLS",
|
|
1441
|
+
"MCP_EMAIL_SMTP_TLS",
|
|
1442
|
+
"MCP_EMAIL_SMTP_STARTTLS"
|
|
1443
|
+
],
|
|
1444
|
+
repo: "https://github.com/codefuturist/email-mcp",
|
|
1445
|
+
verified: !1,
|
|
1446
|
+
prerequisite: "Use an app password for providers with 2FA (e.g. Gmail); requires Node 22+."
|
|
1447
|
+
},
|
|
1448
|
+
{
|
|
1449
|
+
name: "telegram",
|
|
1450
|
+
displayName: "Telegram",
|
|
1451
|
+
iconKey: "telegram",
|
|
1452
|
+
npmPackage: "@iqai/mcp-telegram",
|
|
1453
|
+
description: "Interact with Telegram via bot API for messaging and channel management",
|
|
1454
|
+
category: "communication",
|
|
1455
|
+
requiredEnvKeys: [
|
|
1456
|
+
"TELEGRAM_BOT_TOKEN"
|
|
1457
|
+
],
|
|
1458
|
+
repo: "https://github.com/IQAIcom/mcp-telegram",
|
|
1459
|
+
verified: !1
|
|
1460
|
+
},
|
|
1461
|
+
{
|
|
1462
|
+
name: "discord",
|
|
1463
|
+
displayName: "Discord",
|
|
1464
|
+
iconKey: "discord",
|
|
1465
|
+
npmPackage: "mcp-discord",
|
|
1466
|
+
description: "Discord bot integration for messages, channels, forums, and webhooks",
|
|
1467
|
+
category: "communication",
|
|
1468
|
+
requiredEnvKeys: [
|
|
1469
|
+
"DISCORD_TOKEN"
|
|
1470
|
+
],
|
|
1471
|
+
repo: "https://github.com/barryyip0625/mcp-discord",
|
|
1472
|
+
verified: !1
|
|
1473
|
+
},
|
|
1474
|
+
{
|
|
1475
|
+
name: "gmail",
|
|
1476
|
+
displayName: "Gmail",
|
|
1477
|
+
iconKey: "gmail",
|
|
1478
|
+
npmPackage: "@gongrzhe/server-gmail-autoauth-mcp",
|
|
1479
|
+
description: "Search, read, label, and send Gmail",
|
|
1480
|
+
category: "communication",
|
|
1481
|
+
requiredEnvKeys: [],
|
|
1482
|
+
repo: "https://github.com/GongRzhe/Gmail-MCP-Server",
|
|
1483
|
+
runLocation: "host",
|
|
1484
|
+
verified: !1,
|
|
1485
|
+
auth: !0,
|
|
1486
|
+
authArgs: [
|
|
1487
|
+
"auth"
|
|
1488
|
+
],
|
|
1489
|
+
credentialFiles: [
|
|
1490
|
+
{
|
|
1491
|
+
path: "~/.gmail-mcp/gcp-oauth.keys.json",
|
|
1492
|
+
required: !0
|
|
1493
|
+
},
|
|
1494
|
+
{
|
|
1495
|
+
path: "~/.gmail-mcp/credentials.json"
|
|
1496
|
+
}
|
|
1497
|
+
],
|
|
1498
|
+
prerequisite: "Needs a Google OAuth client JSON (Desktop app) from console.cloud.google.com with the Gmail API enabled. Upstream repo was archived in 2025 but this remains the most-used Gmail server."
|
|
1499
|
+
},
|
|
1500
|
+
{
|
|
1501
|
+
name: "teams",
|
|
1502
|
+
displayName: "Microsoft Teams",
|
|
1503
|
+
npmPackage: "@floriscornel/teams-mcp",
|
|
1504
|
+
description: "Read and send Microsoft Teams channel and chat messages as the signed-in user",
|
|
1505
|
+
category: "communication",
|
|
1506
|
+
requiredEnvKeys: [],
|
|
1507
|
+
repo: "https://github.com/floriscornel/teams-mcp",
|
|
1508
|
+
runLocation: "host",
|
|
1509
|
+
verified: !1,
|
|
1510
|
+
auth: !0,
|
|
1511
|
+
authArgs: [
|
|
1512
|
+
"authenticate"
|
|
1513
|
+
],
|
|
1514
|
+
credentialFiles: [
|
|
1515
|
+
{
|
|
1516
|
+
path: "~/.teams-mcp-token-cache.json"
|
|
1517
|
+
},
|
|
1518
|
+
{
|
|
1519
|
+
path: "~/.msgraph-mcp-auth.json"
|
|
1520
|
+
}
|
|
1521
|
+
],
|
|
1522
|
+
prerequisite: "Signs into Microsoft 365 with a device code on first auth \u2014 no Azure app registration needed (some tenants require admin consent for the shared client)."
|
|
1523
|
+
},
|
|
1524
|
+
{
|
|
1525
|
+
name: "notion",
|
|
1526
|
+
displayName: "Notion",
|
|
1527
|
+
iconKey: "notion",
|
|
1528
|
+
npmPackage: "@notionhq/notion-mcp-server",
|
|
1529
|
+
description: "Search, read, and write Notion pages and databases",
|
|
1530
|
+
category: "productivity",
|
|
1531
|
+
requiredEnvKeys: [
|
|
1532
|
+
"NOTION_TOKEN"
|
|
1533
|
+
],
|
|
1534
|
+
repo: "https://github.com/makenotion/notion-mcp-server",
|
|
1535
|
+
verified: !0,
|
|
1536
|
+
prerequisite: "Needs an internal integration token (notion.so/profile/integrations); share the target pages with the integration."
|
|
1537
|
+
},
|
|
1538
|
+
{
|
|
1539
|
+
name: "notion-remote",
|
|
1540
|
+
displayName: "Notion (remote)",
|
|
1541
|
+
iconKey: "notion",
|
|
1542
|
+
url: "https://mcp.notion.com/mcp",
|
|
1543
|
+
oauth: !0,
|
|
1544
|
+
description: "Official Notion remote server \u2014 search, read, and write pages and databases via browser OAuth",
|
|
1545
|
+
category: "productivity",
|
|
1546
|
+
requiredEnvKeys: [],
|
|
1547
|
+
repo: "https://developers.notion.com/docs/mcp",
|
|
1548
|
+
verified: !0,
|
|
1549
|
+
prerequisite: "Sign in to Notion in your browser on first connect \u2014 no integration token to paste."
|
|
1550
|
+
},
|
|
1551
|
+
{
|
|
1552
|
+
name: "linear",
|
|
1553
|
+
displayName: "Linear",
|
|
1554
|
+
iconKey: "linear",
|
|
1555
|
+
url: "https://mcp.linear.app/mcp",
|
|
1556
|
+
oauth: !0,
|
|
1557
|
+
bearerTokenEnvVar: "LINEAR_API_KEY",
|
|
1558
|
+
description: "Official Linear remote server \u2014 issues, projects, and cycles",
|
|
1559
|
+
category: "productivity",
|
|
1560
|
+
requiredEnvKeys: [
|
|
1561
|
+
"LINEAR_API_KEY"
|
|
1562
|
+
],
|
|
1563
|
+
repo: "https://linear.app/docs/mcp",
|
|
1564
|
+
verified: !0,
|
|
1565
|
+
prerequisite: "Needs a Linear personal API key (Settings > Security & access > Personal API keys)."
|
|
1566
|
+
},
|
|
1567
|
+
{
|
|
1568
|
+
name: "atlassian-cloud",
|
|
1569
|
+
displayName: "Atlassian Cloud",
|
|
1570
|
+
iconKey: "atlassian",
|
|
1571
|
+
url: "https://mcp.atlassian.com/v1/mcp",
|
|
1572
|
+
oauth: !0,
|
|
1573
|
+
bearerTokenEnvVar: "ATLASSIAN_API_KEY",
|
|
1574
|
+
description: "Official Atlassian remote server \u2014 Jira, Confluence, JSM, Bitbucket, and Compass",
|
|
1575
|
+
category: "productivity",
|
|
1576
|
+
requiredEnvKeys: [
|
|
1577
|
+
"ATLASSIAN_API_KEY"
|
|
1578
|
+
],
|
|
1579
|
+
repo: "https://github.com/atlassian/atlassian-mcp-server",
|
|
1580
|
+
verified: !0,
|
|
1581
|
+
prerequisite: "Needs a service-account API key, and an org admin must enable API-token access to the MCP server. Atlassian Cloud only."
|
|
1582
|
+
},
|
|
1583
|
+
{
|
|
1584
|
+
name: "todoist",
|
|
1585
|
+
displayName: "Todoist",
|
|
1586
|
+
iconKey: "todoist",
|
|
1587
|
+
npmPackage: "@doist/todoist-mcp",
|
|
1588
|
+
description: "Manage Todoist tasks, projects, and labels",
|
|
1589
|
+
category: "productivity",
|
|
1590
|
+
requiredEnvKeys: [
|
|
1591
|
+
"TODOIST_API_KEY"
|
|
1592
|
+
],
|
|
1593
|
+
repo: "https://github.com/Doist/todoist-mcp",
|
|
1594
|
+
verified: !0,
|
|
1595
|
+
prerequisite: "API key from Todoist Settings > Integrations > Developer."
|
|
1596
|
+
},
|
|
1597
|
+
{
|
|
1598
|
+
name: "todoist-remote",
|
|
1599
|
+
displayName: "Todoist (remote)",
|
|
1600
|
+
iconKey: "todoist",
|
|
1601
|
+
url: "https://ai.todoist.net/mcp",
|
|
1602
|
+
oauth: !0,
|
|
1603
|
+
description: "Official Doist hosted server \u2014 manage Todoist tasks, projects, and labels via browser OAuth",
|
|
1604
|
+
category: "productivity",
|
|
1605
|
+
requiredEnvKeys: [],
|
|
1606
|
+
repo: "https://github.com/Doist/todoist-mcp",
|
|
1607
|
+
verified: !0,
|
|
1608
|
+
prerequisite: "Sign in to Todoist in your browser on first connect \u2014 no API key to paste."
|
|
1609
|
+
},
|
|
1610
|
+
{
|
|
1611
|
+
name: "clickup",
|
|
1612
|
+
displayName: "ClickUp",
|
|
1613
|
+
url: "https://mcp.clickup.com/mcp",
|
|
1614
|
+
oauth: !0,
|
|
1615
|
+
description: "Official ClickUp remote server \u2014 tasks, lists, docs, and workspaces via browser OAuth",
|
|
1616
|
+
category: "productivity",
|
|
1617
|
+
requiredEnvKeys: [],
|
|
1618
|
+
repo: "https://developer.clickup.com/docs/connect-an-ai-assistant-to-clickups-mcp-server",
|
|
1619
|
+
verified: !0,
|
|
1620
|
+
prerequisite: "Sign in to ClickUp in your browser on first connect (OAuth only \u2014 API keys are not accepted). Public beta; rate-limited."
|
|
1621
|
+
},
|
|
1622
|
+
{
|
|
1623
|
+
name: "readwise",
|
|
1624
|
+
displayName: "Readwise",
|
|
1625
|
+
url: "https://mcp2.readwise.io/mcp",
|
|
1626
|
+
oauth: !0,
|
|
1627
|
+
description: "Official Readwise remote server \u2014 Highlights and Reader documents via browser OAuth",
|
|
1628
|
+
category: "productivity",
|
|
1629
|
+
requiredEnvKeys: [],
|
|
1630
|
+
repo: "https://github.com/readwiseio/readwise-mcp",
|
|
1631
|
+
verified: !0,
|
|
1632
|
+
prerequisite: "Sign in to Readwise in your browser on first connect \u2014 no access token to paste."
|
|
1633
|
+
},
|
|
1634
|
+
{
|
|
1635
|
+
name: "monday",
|
|
1636
|
+
displayName: "monday.com",
|
|
1637
|
+
npmPackage: "@mondaydotcomorg/monday-api-mcp",
|
|
1638
|
+
description: "Manage monday.com boards, items, and workflows",
|
|
1639
|
+
category: "productivity",
|
|
1640
|
+
requiredEnvKeys: [
|
|
1641
|
+
"monday_token"
|
|
1642
|
+
],
|
|
1643
|
+
repo: "https://github.com/mondaycom/monday-ai",
|
|
1644
|
+
verified: !0
|
|
1645
|
+
},
|
|
1646
|
+
{
|
|
1647
|
+
name: "cal-com",
|
|
1648
|
+
displayName: "Cal.com",
|
|
1649
|
+
iconKey: "cal-com",
|
|
1650
|
+
npmPackage: "@calcom/cal-mcp",
|
|
1651
|
+
description: "Manage Cal.com bookings and event types",
|
|
1652
|
+
category: "productivity",
|
|
1653
|
+
requiredEnvKeys: [
|
|
1654
|
+
"CAL_API_KEY"
|
|
1655
|
+
],
|
|
1656
|
+
repo: "https://github.com/calcom/cal-mcp",
|
|
1657
|
+
verified: !0
|
|
1658
|
+
},
|
|
1659
|
+
{
|
|
1660
|
+
name: "stripe",
|
|
1661
|
+
displayName: "Stripe",
|
|
1662
|
+
iconKey: "stripe",
|
|
1663
|
+
npmPackage: "@stripe/mcp",
|
|
1664
|
+
description: "Query and manage Stripe payments, customers, and subscriptions",
|
|
1665
|
+
category: "productivity",
|
|
1666
|
+
requiredEnvKeys: [
|
|
1667
|
+
"STRIPE_SECRET_KEY"
|
|
1668
|
+
],
|
|
1669
|
+
repo: "https://github.com/stripe/ai",
|
|
1670
|
+
verified: !0,
|
|
1671
|
+
prerequisite: "A restricted API key (dashboard.stripe.com/apikeys) scoped to just the resources the agent needs is strongly recommended."
|
|
1672
|
+
},
|
|
1673
|
+
{
|
|
1674
|
+
name: "paypal",
|
|
1675
|
+
displayName: "PayPal",
|
|
1676
|
+
iconKey: "paypal",
|
|
1677
|
+
npmPackage: "@paypal/mcp",
|
|
1678
|
+
description: "Manage PayPal invoices, orders, and subscriptions",
|
|
1679
|
+
category: "productivity",
|
|
1680
|
+
requiredEnvKeys: [
|
|
1681
|
+
"PAYPAL_ACCESS_TOKEN",
|
|
1682
|
+
"PAYPAL_ENVIRONMENT"
|
|
1683
|
+
],
|
|
1684
|
+
repo: "https://github.com/paypal/agent-toolkit",
|
|
1685
|
+
verified: !0,
|
|
1686
|
+
prerequisite: "Generate an access token from your PayPal developer client ID + secret; set PAYPAL_ENVIRONMENT to SANDBOX or PRODUCTION."
|
|
1687
|
+
},
|
|
1688
|
+
{
|
|
1689
|
+
name: "hubspot",
|
|
1690
|
+
displayName: "HubSpot",
|
|
1691
|
+
iconKey: "hubspot",
|
|
1692
|
+
npmPackage: "@hubspot/mcp-server",
|
|
1693
|
+
description: "Query and manage HubSpot CRM objects and associations",
|
|
1694
|
+
category: "productivity",
|
|
1695
|
+
requiredEnvKeys: [
|
|
1696
|
+
"PRIVATE_APP_ACCESS_TOKEN"
|
|
1697
|
+
],
|
|
1698
|
+
repo: "https://developers.hubspot.com/mcp",
|
|
1699
|
+
verified: !0,
|
|
1700
|
+
prerequisite: "Needs a HubSpot private-app access token (Settings > Integrations > Private Apps). Beta."
|
|
1701
|
+
},
|
|
1702
|
+
{
|
|
1703
|
+
name: "zapier",
|
|
1704
|
+
displayName: "Zapier",
|
|
1705
|
+
iconKey: "zapier",
|
|
1706
|
+
url: "https://mcp.zapier.com/api/v1/connect",
|
|
1707
|
+
bearerTokenEnvVar: "ZAPIER_MCP_TOKEN",
|
|
1708
|
+
description: "Run actions across 8000+ apps through your Zapier MCP server",
|
|
1709
|
+
category: "productivity",
|
|
1710
|
+
requiredEnvKeys: [
|
|
1711
|
+
"ZAPIER_MCP_TOKEN"
|
|
1712
|
+
],
|
|
1713
|
+
repo: "https://zapier.com/mcp",
|
|
1714
|
+
verified: !0,
|
|
1715
|
+
prerequisite: "Create an MCP server and pick its tools at mcp.zapier.com first; use the token from its Connect tab."
|
|
1716
|
+
},
|
|
1717
|
+
{
|
|
1718
|
+
name: "atlassian",
|
|
1719
|
+
displayName: "Atlassian (self-host)",
|
|
1720
|
+
iconKey: "atlassian",
|
|
1721
|
+
pypiPackage: "mcp-atlassian",
|
|
1722
|
+
runtime: "python",
|
|
1723
|
+
description: "Jira and Confluence \u2014 Cloud, Server, and Data Center",
|
|
1724
|
+
category: "productivity",
|
|
1725
|
+
requiredEnvKeys: [],
|
|
1726
|
+
optionalEnvKeys: [
|
|
1727
|
+
"JIRA_URL",
|
|
1728
|
+
"JIRA_USERNAME",
|
|
1729
|
+
"JIRA_API_TOKEN",
|
|
1730
|
+
"JIRA_PERSONAL_TOKEN",
|
|
1731
|
+
"CONFLUENCE_URL",
|
|
1732
|
+
"CONFLUENCE_USERNAME",
|
|
1733
|
+
"CONFLUENCE_API_TOKEN"
|
|
1734
|
+
],
|
|
1735
|
+
repo: "https://github.com/sooperset/mcp-atlassian",
|
|
1736
|
+
verified: !1,
|
|
1737
|
+
prerequisite: "Set the JIRA_* and/or CONFLUENCE_* trio for the products you use (Cloud: URL + username + API token; Server/DC: URL + personal access token)."
|
|
1738
|
+
},
|
|
1739
|
+
{
|
|
1740
|
+
name: "trello",
|
|
1741
|
+
displayName: "Trello",
|
|
1742
|
+
iconKey: "trello",
|
|
1743
|
+
npmPackage: "@delorenj/mcp-server-trello",
|
|
1744
|
+
description: "Manage Trello boards, lists, and cards",
|
|
1745
|
+
category: "productivity",
|
|
1746
|
+
requiredEnvKeys: [
|
|
1747
|
+
"TRELLO_API_KEY",
|
|
1748
|
+
"TRELLO_TOKEN"
|
|
1749
|
+
],
|
|
1750
|
+
optionalEnvKeys: [
|
|
1751
|
+
"TRELLO_WORKSPACE_ID"
|
|
1752
|
+
],
|
|
1753
|
+
repo: "https://github.com/delorenj/mcp-server-trello",
|
|
1754
|
+
verified: !1,
|
|
1755
|
+
prerequisite: "API key and token from trello.com/app-key."
|
|
1756
|
+
},
|
|
1757
|
+
{
|
|
1758
|
+
name: "google-calendar",
|
|
1759
|
+
displayName: "Google Calendar",
|
|
1760
|
+
iconKey: "google-calendar",
|
|
1761
|
+
npmPackage: "@cocal/google-calendar-mcp",
|
|
1762
|
+
description: "Read, create, and manage Google Calendar events",
|
|
1763
|
+
category: "productivity",
|
|
1764
|
+
requiredEnvKeys: [
|
|
1765
|
+
"GOOGLE_OAUTH_CREDENTIALS"
|
|
1766
|
+
],
|
|
1767
|
+
repo: "https://github.com/nspady/google-calendar-mcp",
|
|
1768
|
+
runLocation: "host",
|
|
1769
|
+
verified: !1,
|
|
1770
|
+
auth: !0,
|
|
1771
|
+
authArgs: [
|
|
1772
|
+
"auth"
|
|
1773
|
+
],
|
|
1774
|
+
credentialFiles: [
|
|
1775
|
+
{
|
|
1776
|
+
path: "~/.config/google-calendar-mcp/gcp-oauth.keys.json",
|
|
1777
|
+
required: !0
|
|
1778
|
+
},
|
|
1779
|
+
{
|
|
1780
|
+
path: "~/.config/google-calendar-mcp/tokens.json"
|
|
1781
|
+
}
|
|
1782
|
+
],
|
|
1783
|
+
prerequisite: "Needs a Google OAuth client JSON (Desktop app) with the Calendar API enabled. GOOGLE_OAUTH_CREDENTIALS is the key file PATH (e.g. ~/.config/google-calendar-mcp/gcp-oauth.keys.json), not a token."
|
|
1784
|
+
},
|
|
1785
|
+
{
|
|
1786
|
+
name: "google-docs",
|
|
1787
|
+
displayName: "Google Docs",
|
|
1788
|
+
iconKey: "google-docs",
|
|
1789
|
+
npmPackage: "@a-bonus/google-docs-mcp",
|
|
1790
|
+
description: "Read and edit Google Docs, Sheets, and Drive files",
|
|
1791
|
+
category: "productivity",
|
|
1792
|
+
requiredEnvKeys: [
|
|
1793
|
+
"GOOGLE_CLIENT_ID",
|
|
1794
|
+
"GOOGLE_CLIENT_SECRET"
|
|
1795
|
+
],
|
|
1796
|
+
repo: "https://github.com/a-bonus/google-docs-mcp",
|
|
1797
|
+
runLocation: "host",
|
|
1798
|
+
verified: !1,
|
|
1799
|
+
auth: !0,
|
|
1800
|
+
authArgs: [
|
|
1801
|
+
"auth"
|
|
1802
|
+
],
|
|
1803
|
+
credentialFiles: [
|
|
1804
|
+
{
|
|
1805
|
+
path: "~/.config/google-docs-mcp/token.json"
|
|
1806
|
+
}
|
|
1807
|
+
],
|
|
1808
|
+
prerequisite: "Needs a Google OAuth client (ID + secret) from console.cloud.google.com with the Docs, Sheets, and Drive APIs enabled."
|
|
1809
|
+
},
|
|
1810
|
+
{
|
|
1811
|
+
name: "workspace",
|
|
1812
|
+
displayName: "Google Workspace",
|
|
1813
|
+
pypiPackage: "workspace-mcp",
|
|
1814
|
+
runtime: "python",
|
|
1815
|
+
description: "All-in-one Google Workspace: Gmail, Drive, Calendar, Docs, Sheets, Slides, and more (120+ tools)",
|
|
1816
|
+
category: "productivity",
|
|
1817
|
+
requiredEnvKeys: [
|
|
1818
|
+
"GOOGLE_OAUTH_CLIENT_ID",
|
|
1819
|
+
"GOOGLE_OAUTH_CLIENT_SECRET"
|
|
1820
|
+
],
|
|
1821
|
+
repo: "https://github.com/taylorwilsdon/google_workspace_mcp",
|
|
1822
|
+
runLocation: "host",
|
|
1823
|
+
verified: !1,
|
|
1824
|
+
prerequisite: "Needs a Google OAuth client from console.cloud.google.com with each Workspace API enabled. Browser sign-in happens in-flow on first use (port 8000) \u2014 host run recommended."
|
|
1825
|
+
},
|
|
1826
|
+
{
|
|
1827
|
+
name: "raindrop",
|
|
1828
|
+
displayName: "Raindrop.io",
|
|
1829
|
+
npmPackage: "@adeze/raindrop-mcp",
|
|
1830
|
+
description: "Manage Raindrop.io bookmarks, collections, and tags",
|
|
1831
|
+
category: "productivity",
|
|
1832
|
+
requiredEnvKeys: [
|
|
1833
|
+
"RAINDROP_ACCESS_TOKEN"
|
|
1834
|
+
],
|
|
1835
|
+
repo: "https://github.com/adeze/raindrop-mcp",
|
|
1836
|
+
verified: !1,
|
|
1837
|
+
prerequisite: 'Test token from app.raindrop.io/settings/integrations (create an app, then "Test token").'
|
|
1838
|
+
},
|
|
1839
|
+
{
|
|
1840
|
+
name: "grafana",
|
|
1841
|
+
displayName: "Grafana",
|
|
1842
|
+
iconKey: "grafana",
|
|
1843
|
+
pypiPackage: "mcp-grafana",
|
|
1844
|
+
runtime: "python",
|
|
1845
|
+
description: "Query Grafana dashboards, datasources, alerts, and incidents",
|
|
1846
|
+
category: "infra",
|
|
1847
|
+
requiredEnvKeys: [
|
|
1848
|
+
"GRAFANA_URL",
|
|
1849
|
+
"GRAFANA_SERVICE_ACCOUNT_TOKEN"
|
|
1850
|
+
],
|
|
1851
|
+
repo: "https://github.com/grafana/mcp-grafana",
|
|
1852
|
+
verified: !0,
|
|
1853
|
+
prerequisite: "Needs a reachable Grafana 9.0+ instance and a service-account token."
|
|
1854
|
+
},
|
|
1855
|
+
{
|
|
1856
|
+
name: "pagerduty",
|
|
1857
|
+
displayName: "PagerDuty",
|
|
1858
|
+
iconKey: "pagerduty",
|
|
1859
|
+
pypiPackage: "pagerduty-mcp",
|
|
1860
|
+
runtime: "python",
|
|
1861
|
+
description: "Query PagerDuty incidents, services, and on-call schedules (writes off by default)",
|
|
1862
|
+
category: "infra",
|
|
1863
|
+
requiredEnvKeys: [
|
|
1864
|
+
"PAGERDUTY_USER_API_KEY"
|
|
1865
|
+
],
|
|
1866
|
+
repo: "https://github.com/PagerDuty/pagerduty-mcp-server",
|
|
1867
|
+
verified: !0
|
|
1868
|
+
},
|
|
1869
|
+
{
|
|
1870
|
+
name: "netlify",
|
|
1871
|
+
displayName: "Netlify",
|
|
1872
|
+
iconKey: "netlify",
|
|
1873
|
+
npmPackage: "@netlify/mcp",
|
|
1874
|
+
description: "Create, build, deploy, and manage Netlify projects",
|
|
1875
|
+
category: "infra",
|
|
1876
|
+
requiredEnvKeys: [],
|
|
1877
|
+
optionalEnvKeys: [
|
|
1878
|
+
"NETLIFY_PERSONAL_ACCESS_TOKEN"
|
|
1879
|
+
],
|
|
1880
|
+
repo: "https://github.com/netlify/netlify-mcp",
|
|
1881
|
+
runLocation: "host",
|
|
1882
|
+
verified: !0,
|
|
1883
|
+
prerequisite: "Reuses Netlify CLI login state \u2014 run `netlify login` on host first (or set NETLIFY_PERSONAL_ACCESS_TOKEN). Requires Node 22+."
|
|
1884
|
+
},
|
|
1885
|
+
{
|
|
1886
|
+
name: "cloudflare-bindings",
|
|
1887
|
+
displayName: "Cloudflare Bindings",
|
|
1888
|
+
iconKey: "cloudflare",
|
|
1889
|
+
url: "https://bindings.mcp.cloudflare.com/mcp",
|
|
1890
|
+
oauth: !0,
|
|
1891
|
+
bearerTokenEnvVar: "CLOUDFLARE_API_TOKEN",
|
|
1892
|
+
description: "Build with Cloudflare Workers: KV, R2, D1, and Durable Objects",
|
|
1893
|
+
category: "infra",
|
|
1894
|
+
requiredEnvKeys: [
|
|
1895
|
+
"CLOUDFLARE_API_TOKEN"
|
|
1896
|
+
],
|
|
1897
|
+
repo: "https://github.com/cloudflare/mcp-server-cloudflare",
|
|
1898
|
+
verified: !0,
|
|
1899
|
+
prerequisite: "Needs a Cloudflare API token scoped for Workers resources."
|
|
1900
|
+
},
|
|
1901
|
+
{
|
|
1902
|
+
name: "cloudflare-observability",
|
|
1903
|
+
displayName: "Cloudflare Observability",
|
|
1904
|
+
iconKey: "cloudflare",
|
|
1905
|
+
url: "https://observability.mcp.cloudflare.com/mcp",
|
|
1906
|
+
oauth: !0,
|
|
1907
|
+
bearerTokenEnvVar: "CLOUDFLARE_API_TOKEN",
|
|
1908
|
+
description: "Debug Cloudflare Workers: logs, analytics, and errors",
|
|
1909
|
+
category: "infra",
|
|
1910
|
+
requiredEnvKeys: [
|
|
1911
|
+
"CLOUDFLARE_API_TOKEN"
|
|
1912
|
+
],
|
|
1913
|
+
repo: "https://github.com/cloudflare/mcp-server-cloudflare",
|
|
1914
|
+
verified: !0,
|
|
1915
|
+
prerequisite: "Needs a Cloudflare API token with Workers observability scopes."
|
|
1916
|
+
},
|
|
1917
|
+
{
|
|
1918
|
+
name: "datadog",
|
|
1919
|
+
displayName: "Datadog",
|
|
1920
|
+
url: "https://mcp.datadoghq.com/v1/mcp",
|
|
1921
|
+
headerEnv: [
|
|
1922
|
+
{
|
|
1923
|
+
header: "DD-API-KEY",
|
|
1924
|
+
env: "DD_API_KEY"
|
|
1925
|
+
},
|
|
1926
|
+
{
|
|
1927
|
+
header: "DD-APPLICATION-KEY",
|
|
1928
|
+
env: "DD_APPLICATION_KEY"
|
|
1929
|
+
}
|
|
1930
|
+
],
|
|
1931
|
+
description: "Query Datadog monitors, dashboards, metrics, and incidents",
|
|
1932
|
+
category: "infra",
|
|
1933
|
+
requiredEnvKeys: [
|
|
1934
|
+
"DD_API_KEY",
|
|
1935
|
+
"DD_APPLICATION_KEY"
|
|
1936
|
+
],
|
|
1937
|
+
repo: "https://docs.datadoghq.com/mcp_server/",
|
|
1938
|
+
verified: !0,
|
|
1939
|
+
prerequisite: "US1 endpoint \u2014 swap the host for your region (e.g. mcp.datadoghq.eu). Needs a Datadog API key and application key."
|
|
1940
|
+
},
|
|
1941
|
+
{
|
|
1942
|
+
name: "prometheus",
|
|
1943
|
+
displayName: "Prometheus",
|
|
1944
|
+
iconKey: "prometheus",
|
|
1945
|
+
pypiPackage: "prometheus-mcp-server",
|
|
1946
|
+
runtime: "python",
|
|
1947
|
+
description: "Run PromQL queries and explore Prometheus metrics",
|
|
1948
|
+
category: "infra",
|
|
1949
|
+
requiredEnvKeys: [
|
|
1950
|
+
"PROMETHEUS_URL"
|
|
1951
|
+
],
|
|
1952
|
+
repo: "https://github.com/pab1it0/prometheus-mcp-server",
|
|
1953
|
+
verified: !1,
|
|
1954
|
+
prerequisite: "Needs a reachable Prometheus server."
|
|
1955
|
+
},
|
|
1956
|
+
{
|
|
1957
|
+
name: "kubernetes",
|
|
1958
|
+
displayName: "Kubernetes",
|
|
1959
|
+
iconKey: "kubernetes",
|
|
1960
|
+
npmPackage: "kubernetes-mcp-server",
|
|
1961
|
+
description: "Inspect and manage Kubernetes clusters via the native API (no kubectl needed)",
|
|
1962
|
+
category: "infra",
|
|
1963
|
+
requiredEnvKeys: [],
|
|
1964
|
+
optionalEnvKeys: [
|
|
1965
|
+
"KUBECONFIG"
|
|
1966
|
+
],
|
|
1967
|
+
repo: "https://github.com/containers/kubernetes-mcp-server",
|
|
1968
|
+
runLocation: "host",
|
|
1969
|
+
verified: !1,
|
|
1970
|
+
prerequisite: "Needs a kubeconfig on the machine it runs on \u2014 host run recommended."
|
|
1971
|
+
},
|
|
1972
|
+
{
|
|
1973
|
+
name: "docker",
|
|
1974
|
+
displayName: "Docker",
|
|
1975
|
+
iconKey: "docker",
|
|
1976
|
+
pypiPackage: "mcp-server-docker",
|
|
1977
|
+
runtime: "python",
|
|
1978
|
+
description: "Manage Docker containers, images, volumes, and networks",
|
|
1979
|
+
category: "infra",
|
|
1980
|
+
requiredEnvKeys: [],
|
|
1981
|
+
optionalEnvKeys: [
|
|
1982
|
+
"DOCKER_HOST"
|
|
1983
|
+
],
|
|
1984
|
+
repo: "https://github.com/ckreiling/mcp-server-docker",
|
|
1985
|
+
runLocation: "host",
|
|
1986
|
+
verified: !1,
|
|
1987
|
+
prerequisite: "Needs access to a Docker daemon \u2014 host run recommended (DOCKER_HOST supports ssh:// remotes)."
|
|
1988
|
+
},
|
|
1989
|
+
{
|
|
1990
|
+
name: "elevenlabs",
|
|
1991
|
+
displayName: "ElevenLabs",
|
|
1992
|
+
iconKey: "elevenlabs",
|
|
1993
|
+
pypiPackage: "elevenlabs-mcp",
|
|
1994
|
+
runtime: "python",
|
|
1995
|
+
description: "Text-to-speech, voice cloning, and audio generation via ElevenLabs",
|
|
1996
|
+
category: "ai",
|
|
1997
|
+
requiredEnvKeys: [
|
|
1998
|
+
"ELEVENLABS_API_KEY"
|
|
1999
|
+
],
|
|
2000
|
+
optionalEnvKeys: [
|
|
2001
|
+
"ELEVENLABS_MCP_BASE_PATH",
|
|
2002
|
+
"ELEVENLABS_MCP_OUTPUT_MODE"
|
|
2003
|
+
],
|
|
2004
|
+
repo: "https://github.com/elevenlabs/elevenlabs-mcp",
|
|
2005
|
+
verified: !0
|
|
2006
|
+
},
|
|
2007
|
+
{
|
|
2008
|
+
name: "antv-chart",
|
|
2009
|
+
displayName: "AntV Chart",
|
|
2010
|
+
npmPackage: "@antv/mcp-server-chart",
|
|
2011
|
+
description: "Generate 25+ chart types as images via AntV",
|
|
2012
|
+
category: "ai",
|
|
2013
|
+
requiredEnvKeys: [],
|
|
2014
|
+
repo: "https://github.com/antvis/mcp-server-chart",
|
|
2015
|
+
verified: !0
|
|
2016
|
+
},
|
|
2017
|
+
{
|
|
2018
|
+
name: "browserbase",
|
|
2019
|
+
displayName: "Browserbase",
|
|
2020
|
+
npmPackage: "@browserbasehq/mcp",
|
|
2021
|
+
description: "Cloud browser automation with Stagehand \u2014 act, extract, and observe on any site",
|
|
2022
|
+
category: "ai",
|
|
2023
|
+
requiredEnvKeys: [
|
|
2024
|
+
"BROWSERBASE_API_KEY",
|
|
2025
|
+
"BROWSERBASE_PROJECT_ID",
|
|
2026
|
+
"GEMINI_API_KEY"
|
|
2027
|
+
],
|
|
2028
|
+
repo: "https://github.com/browserbase/mcp-server-browserbase",
|
|
2029
|
+
verified: !0,
|
|
2030
|
+
prerequisite: "Needs a Browserbase API key + project ID, plus a Gemini API key for the default Stagehand model."
|
|
2031
|
+
},
|
|
2032
|
+
{
|
|
2033
|
+
name: "huggingface",
|
|
2034
|
+
displayName: "Hugging Face",
|
|
2035
|
+
iconKey: "huggingface",
|
|
2036
|
+
url: "https://huggingface.co/mcp",
|
|
2037
|
+
oauth: !0,
|
|
2038
|
+
bearerTokenEnvVar: "HF_TOKEN",
|
|
2039
|
+
description: "Search Hugging Face models, datasets, papers, and Spaces",
|
|
2040
|
+
category: "ai",
|
|
2041
|
+
requiredEnvKeys: [
|
|
2042
|
+
"HF_TOKEN"
|
|
2043
|
+
],
|
|
2044
|
+
repo: "https://github.com/huggingface/hf-mcp-server",
|
|
2045
|
+
verified: !0,
|
|
2046
|
+
prerequisite: "Needs a Hugging Face access token (huggingface.co/settings/tokens, Read scope)."
|
|
2047
|
+
},
|
|
2048
|
+
{
|
|
2049
|
+
name: "mermaid",
|
|
2050
|
+
displayName: "Mermaid",
|
|
2051
|
+
iconKey: "mermaid",
|
|
2052
|
+
npmPackage: "mcp-mermaid",
|
|
2053
|
+
description: "Generate and render Mermaid diagrams",
|
|
2054
|
+
category: "ai",
|
|
2055
|
+
requiredEnvKeys: [],
|
|
2056
|
+
repo: "https://github.com/hustcc/mcp-mermaid",
|
|
2057
|
+
verified: !1
|
|
2058
|
+
},
|
|
2059
|
+
{
|
|
2060
|
+
name: "blender",
|
|
2061
|
+
displayName: "Blender",
|
|
2062
|
+
iconKey: "blender",
|
|
2063
|
+
pypiPackage: "blender-mcp",
|
|
2064
|
+
runtime: "python",
|
|
2065
|
+
description: "Control Blender for 3D modeling and scene creation",
|
|
2066
|
+
category: "ai",
|
|
2067
|
+
requiredEnvKeys: [],
|
|
2068
|
+
repo: "https://github.com/ahujasid/blender-mcp",
|
|
2069
|
+
runLocation: "host",
|
|
2070
|
+
verified: !1,
|
|
2071
|
+
prerequisite: 'Install the Blender MCP addon and click "Start MCP Server" inside Blender first; connects to localhost:9876 \u2014 host run required.'
|
|
2072
|
+
}
|
|
2073
|
+
]
|
|
2074
|
+
};
|
|
2075
|
+
|
|
2076
|
+
// src/shared/schemas/mcp-registry.schema.ts
|
|
2077
|
+
var McpRegistryEntrySchema = external_exports.object({
|
|
2078
|
+
name: external_exports.string().min(1),
|
|
2079
|
+
displayName: external_exports.string().min(1),
|
|
2080
|
+
npmPackage: external_exports.string().min(1).optional(),
|
|
2081
|
+
pypiPackage: external_exports.string().min(1).optional(),
|
|
2082
|
+
runtime: external_exports.enum(["node", "python"]).optional(),
|
|
2083
|
+
args: external_exports.array(external_exports.string()).optional(),
|
|
2084
|
+
url: external_exports.string().min(1).optional(),
|
|
2085
|
+
headerEnv: external_exports.array(external_exports.object({ header: external_exports.string().min(1), env: external_exports.string().min(1) })).optional(),
|
|
2086
|
+
bearerTokenEnvVar: external_exports.string().min(1).optional(),
|
|
2087
|
+
// OAuth fields are only meaningful on `url` (remote HTTP) entries — the
|
|
2088
|
+
// schema does not enforce that (keeps the parser simple), but a non-url
|
|
2089
|
+
// entry carrying them is ignored by registrationFromRegistryEntry.
|
|
2090
|
+
oauth: external_exports.boolean().optional(),
|
|
2091
|
+
oauthClientId: external_exports.string().optional(),
|
|
2092
|
+
oauthScopes: external_exports.array(external_exports.string()).optional(),
|
|
2093
|
+
description: external_exports.string().min(1),
|
|
2094
|
+
category: external_exports.enum(["tools", "data", "dev", "communication", "web", "search", "productivity", "infra", "ai"]),
|
|
2095
|
+
requiredEnvKeys: external_exports.array(external_exports.string()),
|
|
2096
|
+
optionalEnvKeys: external_exports.array(external_exports.string()).optional(),
|
|
2097
|
+
repo: external_exports.string().optional(),
|
|
2098
|
+
verified: external_exports.boolean(),
|
|
2099
|
+
iconKey: external_exports.string().optional(),
|
|
2100
|
+
auth: external_exports.boolean().optional(),
|
|
2101
|
+
authArgs: external_exports.array(external_exports.string()).optional(),
|
|
2102
|
+
credentialFiles: external_exports.array(external_exports.object({
|
|
2103
|
+
path: external_exports.string().min(1),
|
|
2104
|
+
required: external_exports.boolean().optional(),
|
|
2105
|
+
writeBack: external_exports.boolean().optional()
|
|
2106
|
+
})).optional(),
|
|
2107
|
+
prerequisite: external_exports.string().optional(),
|
|
2108
|
+
deprecated: external_exports.string().optional(),
|
|
2109
|
+
advisory: external_exports.string().optional(),
|
|
2110
|
+
// Absent = containerized (the default for every install). 'host' marks an
|
|
2111
|
+
// entry that cannot work in the container — see McpRegistryEntry.runLocation
|
|
2112
|
+
// for the criteria. Only meaningful on stdio (non-url) entries.
|
|
2113
|
+
runLocation: external_exports.enum(["host", "shared"]).optional()
|
|
2114
|
+
}).refine(
|
|
2115
|
+
(e) => !!(e.npmPackage || e.pypiPackage || e.url),
|
|
2116
|
+
{ message: "entry needs at least one of npmPackage / pypiPackage / url" }
|
|
2117
|
+
);
|
|
2118
|
+
function parseMcpRegistryDocument(json) {
|
|
2119
|
+
if (typeof json != "object" || json === null || Array.isArray(json)) return null;
|
|
2120
|
+
let doc = json;
|
|
2121
|
+
if (typeof doc.schemaVersion != "number" || doc.schemaVersion > 1 || !Array.isArray(doc.entries)) return null;
|
|
2122
|
+
let entries = [], dropped = 0;
|
|
2123
|
+
for (let raw of doc.entries) {
|
|
2124
|
+
let result = McpRegistryEntrySchema.safeParse(raw);
|
|
2125
|
+
result.success ? entries.push(result.data) : dropped++;
|
|
2126
|
+
}
|
|
2127
|
+
return {
|
|
2128
|
+
entries,
|
|
2129
|
+
updatedAt: typeof doc.updatedAt == "string" ? doc.updatedAt : void 0,
|
|
2130
|
+
dropped
|
|
2131
|
+
};
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
// src/shared/constants/mcp-registry.ts
|
|
2135
|
+
var parsedBundledRegistry = parseMcpRegistryDocument(mcp_registry_default);
|
|
2136
|
+
if (!parsedBundledRegistry || parsedBundledRegistry.dropped > 0)
|
|
2137
|
+
throw new Error("Bundled mcp-registry.json failed schema validation \u2014 fix /mcp-registry.json");
|
|
2138
|
+
var MCP_REGISTRY = parsedBundledRegistry.entries, BUNDLED_REGISTRY_UPDATED_AT = parsedBundledRegistry.updatedAt, REGISTRY_ARG_PLACEHOLDER_RE = /\{[a-z0-9-]+\}/;
|
|
2139
|
+
function hasUnresolvedPlaceholderArgs(args) {
|
|
2140
|
+
return (args ?? []).some((arg) => REGISTRY_ARG_PLACEHOLDER_RE.test(arg));
|
|
2141
|
+
}
|
|
2142
|
+
var HOST_ONLY_REGISTRY_ENTRIES = /* @__PURE__ */ new Set([
|
|
2143
|
+
// Host binary, daemon, or loopback listener
|
|
2144
|
+
"pandoc",
|
|
2145
|
+
"docker",
|
|
2146
|
+
"kubernetes",
|
|
2147
|
+
"blender",
|
|
2148
|
+
"chrome-devtools",
|
|
2149
|
+
// Host CLI login state / in-flow browser sign-in the preflight can't drive
|
|
2150
|
+
"netlify",
|
|
2151
|
+
"workspace",
|
|
2152
|
+
// Host filesystem paths (the container has no bind mount of the host FS)
|
|
2153
|
+
"filesystem",
|
|
2154
|
+
"sqlite",
|
|
2155
|
+
"duckdb",
|
|
2156
|
+
"git",
|
|
2157
|
+
"markitdown",
|
|
2158
|
+
"excel",
|
|
2159
|
+
"semgrep",
|
|
2160
|
+
"google-sheets",
|
|
2161
|
+
// Settings-side auth preflight + credential-file storage (host-only today)
|
|
2162
|
+
"gmail",
|
|
2163
|
+
"google-drive",
|
|
2164
|
+
"google-calendar",
|
|
2165
|
+
"google-docs",
|
|
2166
|
+
"teams"
|
|
2167
|
+
]);
|
|
2168
|
+
function registryEntryRunLocation(entry) {
|
|
2169
|
+
if (!entry.url)
|
|
2170
|
+
return entry.runLocation ? entry.runLocation : HOST_ONLY_REGISTRY_ENTRIES.has(entry.name) ? "host" : void 0;
|
|
2171
|
+
}
|
|
2172
|
+
var HOST_ONLY_REGISTRY_ENTRY_NAMES = [...HOST_ONLY_REGISTRY_ENTRIES];
|
|
2173
|
+
function registrationFromRegistryEntry(entry, id) {
|
|
2174
|
+
if (entry.url) {
|
|
2175
|
+
let envKeys = [.../* @__PURE__ */ new Set([
|
|
2176
|
+
...entry.requiredEnvKeys,
|
|
2177
|
+
...entry.optionalEnvKeys ?? [],
|
|
2178
|
+
...entry.bearerTokenEnvVar ? [entry.bearerTokenEnvVar] : [],
|
|
2179
|
+
...(entry.headerEnv ?? []).map(({ env }) => env)
|
|
2180
|
+
])];
|
|
2181
|
+
return {
|
|
2182
|
+
id,
|
|
2183
|
+
name: entry.name,
|
|
2184
|
+
type: "http",
|
|
2185
|
+
url: entry.url,
|
|
2186
|
+
description: entry.description,
|
|
2187
|
+
repo: entry.repo,
|
|
2188
|
+
env: envKeys.map((k) => ({ key: k, value: "" })),
|
|
2189
|
+
...entry.oauth ? { oauth: !0 } : {},
|
|
2190
|
+
// Dual-mode: even when oauth is the default, seed the bearer/header env so
|
|
2191
|
+
// the paste-token fallback stays available (CI/daemon users).
|
|
2192
|
+
...entry.bearerTokenEnvVar ? { bearerTokenEnvVar: entry.bearerTokenEnvVar } : {},
|
|
2193
|
+
// Registration headerEnv rows are { key: headerName, value: envVarName }.
|
|
2194
|
+
...entry.headerEnv?.length ? { headerEnv: entry.headerEnv.map(({ header, env }) => ({ key: header, value: env })) } : {}
|
|
2195
|
+
};
|
|
2196
|
+
}
|
|
2197
|
+
let isPython = entry.runtime === "python", runLocation = registryEntryRunLocation(entry);
|
|
2198
|
+
return {
|
|
2199
|
+
id,
|
|
2200
|
+
name: entry.name,
|
|
2201
|
+
type: isPython ? "uvx" : "npm",
|
|
2202
|
+
npmPackage: isPython ? void 0 : entry.npmPackage,
|
|
2203
|
+
pypiPackage: isPython ? entry.pypiPackage : void 0,
|
|
2204
|
+
description: entry.description,
|
|
2205
|
+
managed: !0,
|
|
2206
|
+
env: [...entry.requiredEnvKeys, ...entry.optionalEnvKeys ?? []].map((k) => ({ key: k, value: "" })),
|
|
2207
|
+
repo: entry.repo,
|
|
2208
|
+
// Only entries that ask for a run location carry one; absent = container.
|
|
2209
|
+
...runLocation ? { runLocation } : {},
|
|
2210
|
+
// Placeholders are copied verbatim — the modal resolves them before install.
|
|
2211
|
+
...entry.args?.length ? { args: [...entry.args] } : {},
|
|
2212
|
+
...entry.auth ? { auth: !0 } : {},
|
|
2213
|
+
...entry.authArgs ? { authArgs: entry.authArgs } : {},
|
|
2214
|
+
...entry.credentialFiles ? { credentialFiles: entry.credentialFiles.map((f) => ({ ...f })) } : {}
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2217
|
+
function findEntryIn(entries, lookup) {
|
|
2218
|
+
if (lookup.npmPackage !== void 0) return entries.find((e) => e.npmPackage === lookup.npmPackage);
|
|
2219
|
+
if (lookup.pypiPackage !== void 0) return entries.find((e) => e.pypiPackage === lookup.pypiPackage);
|
|
2220
|
+
if (lookup.url !== void 0) return entries.find((e) => e.url === lookup.url);
|
|
2221
|
+
if (lookup.name !== void 0) return entries.find((e) => e.name === lookup.name);
|
|
2222
|
+
}
|
|
2223
|
+
function findRegistryEntry(npmPackage) {
|
|
2224
|
+
return MCP_REGISTRY.find((e) => e.npmPackage === npmPackage);
|
|
2225
|
+
}
|
|
2226
|
+
function findRegistryEntryByPypiPackage(pypiPackage) {
|
|
2227
|
+
return MCP_REGISTRY.find((e) => e.pypiPackage === pypiPackage);
|
|
2228
|
+
}
|
|
2229
|
+
function findRegistryEntryByName(name) {
|
|
2230
|
+
return MCP_REGISTRY.find((e) => e.name === name);
|
|
2231
|
+
}
|
|
2232
|
+
function findRegistryEntryByUrl(url) {
|
|
2233
|
+
return MCP_REGISTRY.find((e) => e.url === url);
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2236
|
+
// src/main/tui/setup/mcp.ts
|
|
2237
|
+
var { MCP_REGISTRY: MCP_REGISTRY2, registrationFromRegistryEntry: registrationFromRegistryEntry2, hasUnresolvedPlaceholderArgs: hasUnresolvedPlaceholderArgs2, REGISTRY_ARG_PLACEHOLDER_RE: REGISTRY_ARG_PLACEHOLDER_RE2 } = cjs(mcp_registry_exports), { buildMcpServerConfigFromRegistration, mcpCredentialNamespace } = cjs(mcp_config_exports), MCP_OVERLAY = "mcp";
|
|
2238
|
+
function mcpSourceRows() {
|
|
2239
|
+
let custom = [
|
|
2240
|
+
{ kind: "npm", label: "npm package", detail: "Any MCP server published to npm (e.g. @modelcontextprotocol/server-everything)" },
|
|
2241
|
+
{ kind: "python", label: "Python package (uvx)", detail: "Any MCP server on PyPI, run with uvx (e.g. mcp-server-fetch)" },
|
|
2242
|
+
{ kind: "http", label: "Remote server (URL)", detail: "A Streamable HTTP MCP endpoint; token from an env var" }
|
|
2243
|
+
], catalog = MCP_REGISTRY2.filter((e) => !e.deprecated).map((entry) => ({ kind: "catalog", entry, label: entry.displayName, detail: entry.description }));
|
|
2244
|
+
return [...custom, ...catalog];
|
|
2245
|
+
}
|
|
2246
|
+
function parseAddTarget(arg) {
|
|
2247
|
+
let raw = (arg ?? "").trim();
|
|
2248
|
+
if (!raw) return {};
|
|
2249
|
+
let rows = mcpSourceRows();
|
|
2250
|
+
if (/^https?:\/\//i.test(raw)) {
|
|
2251
|
+
let known = rows.find((r) => r.entry?.url === raw);
|
|
2252
|
+
return known ? { row: known } : { row: rows.find((r) => r.kind === "http"), value: raw };
|
|
2253
|
+
}
|
|
2254
|
+
let prefixed = raw.match(/^(npm|python|uvx|pip):(.+)$/i);
|
|
2255
|
+
if (prefixed) {
|
|
2256
|
+
let kind = prefixed[1].toLowerCase() === "npm" ? "npm" : "python";
|
|
2257
|
+
return { row: rows.find((r) => r.kind === kind), value: prefixed[2].trim() };
|
|
2258
|
+
}
|
|
2259
|
+
let wanted = raw.toLowerCase(), entry = rows.find((r) => r.entry && (r.entry.name === wanted || r.entry.npmPackage === raw || r.entry.pypiPackage === raw || r.entry.displayName.toLowerCase() === wanted));
|
|
2260
|
+
return entry ? { row: entry } : { row: rows.find((r) => r.kind === "npm"), value: raw };
|
|
2261
|
+
}
|
|
2262
|
+
function sanitizeServerName(value) {
|
|
2263
|
+
return value.toLowerCase().replace(/^@[^/]+\//, "").replace(/^mcp-server-|-mcp-server$|^server-|-mcp$|^mcp-/g, "").replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
2264
|
+
}
|
|
2265
|
+
function defaultServerName(row, value = "") {
|
|
2266
|
+
if (row.entry) return row.entry.name;
|
|
2267
|
+
if (row.kind === "http")
|
|
2268
|
+
try {
|
|
2269
|
+
return sanitizeServerName(new URL(value).hostname.split(".").slice(-2, -1)[0] ?? "");
|
|
2270
|
+
} catch {
|
|
2271
|
+
return "";
|
|
2272
|
+
}
|
|
2273
|
+
return sanitizeServerName(value.split("/").pop() ?? value);
|
|
2274
|
+
}
|
|
2275
|
+
function envKeysOf(row) {
|
|
2276
|
+
let e = row.entry;
|
|
2277
|
+
if (!e) return row.kind === "http" ? [{ key: "MCP_TOKEN", required: !1 }] : [];
|
|
2278
|
+
let keys = [
|
|
2279
|
+
...e.requiredEnvKeys.map((key) => ({ key, required: !0 })),
|
|
2280
|
+
...(e.optionalEnvKeys ?? []).map((key) => ({ key, required: !1 })),
|
|
2281
|
+
...e.bearerTokenEnvVar ? [{ key: e.bearerTokenEnvVar, required: !!e.url && !e.oauth }] : [],
|
|
2282
|
+
...(e.headerEnv ?? []).map((h) => ({ key: h.env, required: !0 }))
|
|
2283
|
+
];
|
|
2284
|
+
return keys.filter((k, i) => keys.findIndex((x) => x.key === k.key) === i);
|
|
2285
|
+
}
|
|
2286
|
+
function studioOnlyReason(row) {
|
|
2287
|
+
let e = row.entry;
|
|
2288
|
+
return e ? e.url && e.oauth && !e.bearerTokenEnvVar && !e.headerEnv?.length ? `${e.displayName} signs in with OAuth in the browser, which only ADF Studio runs today. Add it in Studio (Settings \u2192 MCP \u2192 ${e.displayName}, Connect), then it shows up here.` : e.auth || (e.credentialFiles?.length ?? 0) > 0 ? `${e.displayName} needs a one-time sign-in step (it writes a credential file), which ADF Studio runs. Add it in Studio (Settings \u2192 MCP), then it shows up here.` : null : null;
|
|
2289
|
+
}
|
|
2290
|
+
function validateMcpForm(row, form, taken) {
|
|
2291
|
+
let errors = {};
|
|
2292
|
+
if (form.name ? form.name !== sanitizeServerName(form.name) || !/^[a-z0-9][a-z0-9_-]*$/.test(form.name) ? errors.name = "Lowercase letters, digits, - and _ only." : taken.includes(form.name) && (errors.name = `This agent already has a server named ${form.name}.`) : errors.name = "Give it a name (letters, digits, - and _).", !row.entry) {
|
|
2293
|
+
let source = form.source.trim();
|
|
2294
|
+
if (!source) errors.source = row.kind === "http" ? "The server URL." : "The package name.";
|
|
2295
|
+
else if (row.kind === "http")
|
|
2296
|
+
try {
|
|
2297
|
+
/^https?:$/.test(new URL(source).protocol) || (errors.source = "Use an http(s) URL.");
|
|
2298
|
+
} catch {
|
|
2299
|
+
errors.source = "Not a URL.";
|
|
2300
|
+
}
|
|
2301
|
+
else /\s/.test(source) && (errors.source = "One package name, no spaces.");
|
|
2302
|
+
}
|
|
2303
|
+
hasUnresolvedPlaceholderArgs2(splitArgs(form.args)) && (errors.args = "Fill in the {placeholders}.");
|
|
2304
|
+
for (let { key, required } of envKeysOf(row))
|
|
2305
|
+
required && !(form.env[key] ?? "").trim() && (errors[`env:${key}`] = `${key} is required.`);
|
|
2306
|
+
return errors;
|
|
2307
|
+
}
|
|
2308
|
+
function splitArgs(text) {
|
|
2309
|
+
let out = [], pattern = /"((?:[^"\\]|\\.)*)"|'([^']*)'|(\S+)/g, m;
|
|
2310
|
+
for (; m = pattern.exec(text); ) out.push(m[1] !== void 0 ? m[1].replace(/\\(.)/g, "$1") : m[2] ?? m[3]);
|
|
2311
|
+
return out;
|
|
2312
|
+
}
|
|
2313
|
+
function joinArgs(args) {
|
|
2314
|
+
return (args ?? []).map((a) => /\s/.test(a) ? `"${a.replace(/"/g, '\\"')}"` : a).join(" ");
|
|
2315
|
+
}
|
|
2316
|
+
function registrationFor(row, form) {
|
|
2317
|
+
let envKeys = envKeysOf(row).map((k) => k.key), base = row.entry ? registrationFromRegistryEntry2(row.entry, `tui:${form.name}`) : row.kind === "http" ? { id: `tui:${form.name}`, name: form.name, type: "http", url: form.source.trim(), ...form.env.MCP_TOKEN?.trim() ? { bearerTokenEnvVar: "MCP_TOKEN" } : {} } : row.kind === "python" ? { id: `tui:${form.name}`, name: form.name, type: "uvx", pypiPackage: form.source.trim(), managed: !0 } : { id: `tui:${form.name}`, name: form.name, type: "npm", npmPackage: form.source.trim(), managed: !0 }, isHttp = base.type === "http", args = splitArgs(form.args), keysWithValues = envKeys.filter((k) => (form.env[k] ?? "").trim());
|
|
2318
|
+
return {
|
|
2319
|
+
...base,
|
|
2320
|
+
name: form.name,
|
|
2321
|
+
credentialStorage: "agent",
|
|
2322
|
+
// Keys only: values go to the agent's identity store.
|
|
2323
|
+
env: (row.entry ? envKeys : keysWithValues).map((key) => ({ key, value: "" })),
|
|
2324
|
+
// Remote catalog entries that also take a token: the token path (OAuth runs in Studio only).
|
|
2325
|
+
...isHttp && base.oauth && base.bearerTokenEnvVar ? { oauth: !1 } : {},
|
|
2326
|
+
...isHttp ? {} : { args: args.length ? args : void 0 },
|
|
2327
|
+
// Container unless the user picked the host (a catalog entry's host default is only a preselection).
|
|
2328
|
+
runLocation: !isHttp && form.runOn === "host" ? "host" : void 0
|
|
2329
|
+
};
|
|
2330
|
+
}
|
|
2331
|
+
function serverConfigFor(row, form) {
|
|
2332
|
+
return buildMcpServerConfigFromRegistration(registrationFor(row, form));
|
|
2333
|
+
}
|
|
2334
|
+
function credentialNamespace(server) {
|
|
2335
|
+
return mcpCredentialNamespace(server);
|
|
2336
|
+
}
|
|
2337
|
+
function packageToInstall(server) {
|
|
2338
|
+
return server.transport === "http" ? null : server.npm_package ? { kind: "npm", name: server.npm_package } : server.pypi_package ? { kind: "python", name: server.pypi_package } : null;
|
|
2339
|
+
}
|
|
2340
|
+
function serverEnvKeys(server) {
|
|
2341
|
+
return [.../* @__PURE__ */ new Set([
|
|
2342
|
+
...server.env_keys ?? [],
|
|
2343
|
+
...(server.env_schema ?? []).map((e) => e.key),
|
|
2344
|
+
...server.bearer_token_env_var ? [server.bearer_token_env_var] : [],
|
|
2345
|
+
...(server.header_env ?? []).map((h) => h.env)
|
|
2346
|
+
])];
|
|
2347
|
+
}
|
|
2348
|
+
function runsOnText(server, location) {
|
|
2349
|
+
return location || (server ? server.transport === "http" ? "remote" : server.run_location === "host" ? "host" : server.run_location === "shared" ? "shared container" : "container" : "");
|
|
2350
|
+
}
|
|
2351
|
+
function serverToolDecls(tools, server) {
|
|
2352
|
+
let prefix = `mcp_${server}_`;
|
|
2353
|
+
return (tools ?? []).filter((t) => t.name.startsWith(prefix)).map((t) => ({ name: t.name, tool: t.name.slice(prefix.length), enabled: t.enabled !== !1 }));
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
// src/main/tui/setup/open.ts
|
|
2357
|
+
function channelsIdentityReady(store) {
|
|
2358
|
+
let identity = store.getState().identity;
|
|
2359
|
+
return !identity || identity.status === "ready";
|
|
2360
|
+
}
|
|
2361
|
+
function secretStoreReady(store) {
|
|
2362
|
+
let identity = store.getState().identity;
|
|
2363
|
+
return identity ? identity.storage === "keychain" ? identity.status !== "locked" : identity.status === "ready" : !0;
|
|
2364
|
+
}
|
|
2365
|
+
function openChannels(store, props = {}) {
|
|
2366
|
+
let state = store.getState(), agentId = props.agentId ?? state.selectedAgentId;
|
|
2367
|
+
if (!agentId || !state.agents[agentId]) {
|
|
2368
|
+
store.actions.toast("Select an agent first (sidebar or /agent), or create one with /new", "warn");
|
|
2369
|
+
return;
|
|
2370
|
+
}
|
|
2371
|
+
let next = { agentId, ...props.channel ? { channel: props.channel } : {} };
|
|
2372
|
+
if (props.channel && !channelsIdentityReady(store)) {
|
|
2373
|
+
identityFirst(store, CHANNELS_OVERLAY, next, "Channel credentials are sealed under your owner identity: set it up first, then the channel setup follows.");
|
|
2374
|
+
return;
|
|
2375
|
+
}
|
|
2376
|
+
store.actions.pushOverlay({ kind: CHANNELS_OVERLAY, props: { ...next } });
|
|
2377
|
+
}
|
|
2378
|
+
function openProviderAdd(store, props = {}) {
|
|
2379
|
+
if (!secretStoreReady(store)) {
|
|
2380
|
+
identityFirst(store, PROVIDER_OVERLAY, { ...props }, "API keys go into the daemon\u2019s secret store, which is protected by your owner identity here: set it up or unlock it first.");
|
|
2381
|
+
return;
|
|
2382
|
+
}
|
|
2383
|
+
store.actions.pushOverlay({ kind: PROVIDER_OVERLAY, props: { ...props } });
|
|
2384
|
+
}
|
|
2385
|
+
function openMcp(store, props = {}) {
|
|
2386
|
+
let state = store.getState(), agentId = props.agentId ?? state.selectedAgentId;
|
|
2387
|
+
if (!agentId || !state.agents[agentId]) {
|
|
2388
|
+
store.actions.toast("Select an agent first (sidebar or /agent), or create one with /new", "warn");
|
|
2389
|
+
return;
|
|
2390
|
+
}
|
|
2391
|
+
store.actions.pushOverlay({ kind: MCP_OVERLAY, props: { ...props, agentId } });
|
|
2392
|
+
}
|
|
2393
|
+
function openWelcome(store) {
|
|
2394
|
+
store.actions.pushOverlay({ id: WELCOME_OVERLAY, kind: WELCOME_OVERLAY });
|
|
2395
|
+
}
|
|
2396
|
+
function identityFirst(store, then, thenProps, reason) {
|
|
2397
|
+
openIdentity(store, { mode: "status", then, thenProps, reason });
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
export {
|
|
2401
|
+
elementRect,
|
|
2402
|
+
rectContains,
|
|
2403
|
+
createKeyRouter,
|
|
2404
|
+
KeyRouterProvider,
|
|
2405
|
+
useKeyRouter,
|
|
2406
|
+
useKeys,
|
|
2407
|
+
WHEEL_STEP,
|
|
2408
|
+
useWheel,
|
|
2409
|
+
useClick,
|
|
2410
|
+
keyLabel,
|
|
2411
|
+
IDENTITY_OVERLAY,
|
|
2412
|
+
NEW_AGENT_OVERLAY,
|
|
2413
|
+
IDENTITY_BANNER_KEY,
|
|
2414
|
+
SAVED_WORD,
|
|
2415
|
+
shortDid,
|
|
2416
|
+
normalizePhrase,
|
|
2417
|
+
phraseWords,
|
|
2418
|
+
isLoopbackUrl,
|
|
2419
|
+
LOOPBACK_ONLY_TEXT,
|
|
2420
|
+
IDENTITY_EXPLAINER,
|
|
2421
|
+
identityErrorText,
|
|
2422
|
+
identityBadge,
|
|
2423
|
+
onboardingLines,
|
|
2424
|
+
onboardingChoices,
|
|
2425
|
+
passphraseProblem,
|
|
2426
|
+
KeyHints,
|
|
2427
|
+
openIdentity,
|
|
2428
|
+
openNewAgent,
|
|
2429
|
+
bannerKeyOf,
|
|
2430
|
+
needsAttention,
|
|
2431
|
+
identityKey,
|
|
2432
|
+
IdentityOnboarding,
|
|
2433
|
+
IdentityBanner,
|
|
2434
|
+
CHANNELS_OVERLAY,
|
|
2435
|
+
CHANNEL_ORDER,
|
|
2436
|
+
channelEntries,
|
|
2437
|
+
findChannel,
|
|
2438
|
+
credentialFields,
|
|
2439
|
+
DEFAULT_ADAPTER_CONFIG,
|
|
2440
|
+
validateCredentials,
|
|
2441
|
+
adapterLiveState,
|
|
2442
|
+
configuredChannels,
|
|
2443
|
+
PROVIDER_OVERLAY,
|
|
2444
|
+
providerRows,
|
|
2445
|
+
findProviderRow,
|
|
2446
|
+
keyOptional,
|
|
2447
|
+
needsBaseUrl,
|
|
2448
|
+
validateProvider,
|
|
2449
|
+
providerInput,
|
|
2450
|
+
keyStorageText,
|
|
2451
|
+
MCP_OVERLAY,
|
|
2452
|
+
mcpSourceRows,
|
|
2453
|
+
parseAddTarget,
|
|
2454
|
+
defaultServerName,
|
|
2455
|
+
envKeysOf,
|
|
2456
|
+
studioOnlyReason,
|
|
2457
|
+
validateMcpForm,
|
|
2458
|
+
joinArgs,
|
|
2459
|
+
serverConfigFor,
|
|
2460
|
+
credentialNamespace,
|
|
2461
|
+
packageToInstall,
|
|
2462
|
+
serverEnvKeys,
|
|
2463
|
+
runsOnText,
|
|
2464
|
+
serverToolDecls,
|
|
2465
|
+
channelsIdentityReady,
|
|
2466
|
+
secretStoreReady,
|
|
2467
|
+
openChannels,
|
|
2468
|
+
openProviderAdd,
|
|
2469
|
+
openMcp,
|
|
2470
|
+
openWelcome,
|
|
2471
|
+
identityFirst
|
|
2472
|
+
};
|