lurqrun 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +312 -148
- package/dist/{auditHandler-JCOJSUYW.js → auditHandler-VWCXSDVO.js} +12 -11
- package/dist/autopilotInit-Q62MX5M6.js +178 -0
- package/dist/bin/lurq.js +235 -83
- package/dist/{browserAuth-B6G27USY.js → browserAuth-J7APSD4P.js} +4 -3
- package/dist/{cache-DKI34SVS.js → cache-AEAKCV4I.js} +2 -2
- package/dist/check-2JTQDQA6.js +2613 -0
- package/dist/{check-2FDGUOV7.js → check-PQSQ2MMA.js} +1 -1
- package/dist/checkUpgrade-5YW77OBL.js +24 -0
- package/dist/{chunk-BCANRN5B.js → chunk-2GAYNIHG.js} +8 -1
- package/dist/{chunk-EA3DP5HG.js → chunk-2O6Z37IT.js} +155 -25
- package/dist/{chunk-J5FZYN4A.js → chunk-2WD4QAH4.js} +1 -1
- package/dist/{chunk-CUP36ND3.js → chunk-3676XOBU.js} +145 -36
- package/dist/chunk-3D2MPGOQ.js +181 -0
- package/dist/{chunk-LYMPYETA.js → chunk-4JHDSVIN.js} +270 -6
- package/dist/chunk-53SG6XWA.js +141 -0
- package/dist/chunk-5ABNDB3I.js +37 -0
- package/dist/chunk-5ALIJFDA.js +114 -0
- package/dist/{chunk-BULZAX2U.js → chunk-64ZF7KO4.js} +55 -38
- package/dist/{chunk-2VMOWSRY.js → chunk-6DM6UQFD.js} +19 -1
- package/dist/{chunk-HWBBVLOT.js → chunk-6L67QMCP.js} +1 -1
- package/dist/{chunk-GPTKSNR6.js → chunk-72Y2FDVS.js} +2 -1
- package/dist/{chunk-B43LCXBM.js → chunk-A3TYI2TF.js} +81 -16
- package/dist/{chunk-IQ4SDKKY.js → chunk-B2UUYRBX.js} +31 -228
- package/dist/{chunk-DB2VF5UD.js → chunk-BJT2LHAR.js} +16 -11
- package/dist/chunk-BM3LF5T6.js +132 -0
- package/dist/{chunk-3N3Q3DSW.js → chunk-BN3NM47Q.js} +8 -8
- package/dist/chunk-D3K6IZVR.js +12845 -0
- package/dist/{chunk-AHBP533R.js → chunk-D3VTZS7V.js} +7 -5
- package/dist/{chunk-UDVNA7EA.js → chunk-DM37MTOO.js} +161 -8
- package/dist/chunk-DONTW3NE.js +171 -0
- package/dist/{chunk-M6NYEXCW.js → chunk-DSROTZ3Z.js} +1 -1
- package/dist/{chunk-4F7ZESPO.js → chunk-EQCXENKZ.js} +3 -5
- package/dist/{chunk-CW5HJ3LX.js → chunk-EYAKM6YN.js} +1 -1
- package/dist/{chunk-VL2E5T2A.js → chunk-FDFPMBCA.js} +6 -3
- package/dist/{chunk-BV32UNLL.js → chunk-FVYFV5HA.js} +2 -2
- package/dist/{chunk-DWAGCNEL.js → chunk-GM5Y555O.js} +36 -103
- package/dist/{chunk-PSX3GIAN.js → chunk-HWFX4BC4.js} +2 -1
- package/dist/chunk-IFNONGRQ.js +603 -0
- package/dist/chunk-J3U2CPSK.js +136 -0
- package/dist/{chunk-Y5GI7RL3.js → chunk-J6XQ7OPZ.js} +9 -1
- package/dist/{chunk-LBFGSSF2.js → chunk-KMV4Y6R3.js} +2 -1
- package/dist/{chunk-L6N4JFW2.js → chunk-KP5ZDDPE.js} +81 -4
- package/dist/{chunk-SJOVFF7B.js → chunk-KYKSZ3XZ.js} +3 -2
- package/dist/chunk-NEZNAPPN.js +41 -0
- package/dist/{chunk-NH5I4KHH.js → chunk-NPVN2THM.js} +1 -5
- package/dist/{chunk-6L3U7DZ5.js → chunk-NYZU5NCW.js} +1 -1
- package/dist/chunk-O4KHCV6I.js +320 -0
- package/dist/{chunk-LO35HLQZ.js → chunk-ONDVTXUO.js} +25 -4
- package/dist/{chunk-4ZYCIMY4.js → chunk-OTJFHESY.js} +25 -4
- package/dist/chunk-P6MIKSPY.js +243 -0
- package/dist/{checkUpgrade-F2WESBPM.js → chunk-QCRBX3LV.js} +72 -10
- package/dist/{chunk-LRD4GBYE.js → chunk-QUIMQ7YL.js} +1 -1
- package/dist/chunk-RG3YLU6E.js +49 -0
- package/dist/{chunk-ST2X2GYN.js → chunk-SHZFOSXC.js} +109 -20
- package/dist/{chunk-CIDWNUAP.js → chunk-SOXFMMXC.js} +8 -2
- package/dist/chunk-TDTYV6QI.js +119 -0
- package/dist/{chunk-FW6BD2NO.js → chunk-TUXQ32WE.js} +457 -121
- package/dist/{chunk-WQ23REYT.js → chunk-VGCDRK3S.js} +1 -1
- package/dist/{chunk-U5AT3TD4.js → chunk-X4W64MHU.js} +1735 -1858
- package/dist/chunk-XBQA4I36.js +92 -0
- package/dist/chunk-XGOV6KV4.js +413 -0
- package/dist/{chunk-OQS4GUSI.js → chunk-XIFQIFSZ.js} +210 -661
- package/dist/{chunk-XBIDMHSK.js → chunk-XLXPVN3Y.js} +2 -34
- package/dist/chunk-ZJMLQ37G.js +90 -0
- package/dist/{client-IRKAMDYZ.js → client-MU4EUDZV.js} +3 -3
- package/dist/{commands-FIL5ET6P.js → commands-UOOTMBKA.js} +145 -33
- package/dist/{compat-R3UTFQ5Q.js → compat-Q5DVPITN.js} +6 -6
- package/dist/constants-DJFOFZ44.js +24 -0
- package/dist/{dashboardIssue-WYYMBFIB.js → dashboardIssue-KBKLQMGC.js} +105 -23
- package/dist/{diff-GZA2774U.js → diff-I4SNUHU3.js} +1 -1
- package/dist/domains-VRT4BRVZ.js +14 -0
- package/dist/env-UN6KGZ7S.js +15 -0
- package/dist/envCheck-KUPTORFQ.js +71 -0
- package/dist/{extract-NKGKLIVT.js → extract-6WG265BL.js} +14 -3
- package/dist/{fetch-QYTUJVG2.js → fetch-PJ3SC32M.js} +4 -4
- package/dist/{firstTouch-QQTJH4QX.js → firstTouch-COJCMOB3.js} +1 -1
- package/dist/fix-3EDWNKW3.js +220 -0
- package/dist/{handlers-GEJAVZDK.js → handlers-4Q37WIFT.js} +19 -17
- package/dist/hook-NA66XJLC.js +412 -0
- package/dist/{http-ETPYMBGD.js → http-25GJ6SKK.js} +2078 -510
- package/dist/{ingestQueue-VKN4KBIF.js → ingestQueue-FSVJC3AA.js} +14 -12
- package/dist/{install-EK3W2COX.js → install-G2HHXYJ2.js} +6 -6
- package/dist/{installSkill-VQJWJVCL.js → installSkill-5TIMYIJZ.js} +21 -3
- package/dist/manifest-ENMIC54B.js +13 -0
- package/dist/{mcp-3JHD5SUZ.js → mcp-PENLL2PK.js} +60 -32
- package/dist/mcpConfig-3LYPRY5Q.js +80 -0
- package/dist/{mcpHandlers-QFN4MCFL.js → mcpHandlers-PO7GENA7.js} +7 -7
- package/dist/{mcpScan-QZHWENIP.js → mcpScan-ULP626TQ.js} +306 -442
- package/dist/{mcpScanWorkflow-JPYCBGC4.js → mcpScanWorkflow-27O44FOE.js} +29 -12
- package/dist/{mcpStack-BYJCZNIV.js → mcpStack-ZNYIFXZ5.js} +1 -1
- package/dist/model-ASMZWLM7.js +214 -0
- package/dist/{packages-THVFMWFP.js → packages-RWF2LXST.js} +3 -5
- package/dist/{plan-V4OEI3J4.js → plan-SDILIDZT.js} +23 -21
- package/dist/{planView-JRXGCG2N.js → planView-ZAKRTGVS.js} +4 -1
- package/dist/{policy-3P6BGRDN.js → policy-JAGNMQJI.js} +5 -5
- package/dist/probe-3IK646WN.js +610 -0
- package/dist/publicUpgrades-WTBGLDIK.js +42 -0
- package/dist/{references-6N7VEPHT.js → references-I6Y4BSRR.js} +3 -1
- package/dist/{release-4U4ABNSJ.js → release-7IU2R4YG.js} +5 -5
- package/dist/{remote-YGCXYI4V.js → remote-FN6225AH.js} +13 -3
- package/dist/rename-OS6JYBZQ.js +204 -0
- package/dist/{reportRuns-CKQQYLY4.js → reportRuns-O4X7ZFTX.js} +15 -2
- package/dist/safeFetch-COERXGFN.js +19 -0
- package/dist/{sandbox-MEZLAODF.js → sandbox-SAPSRKEI.js} +4 -4
- package/dist/sarif-WLVJFJ7U.js +125 -0
- package/dist/server-OLTY3NYT.js +59 -0
- package/dist/{surface-V62GE554.js → surface-7MHE3RL7.js} +18 -16
- package/dist/{surface-YL6O67W3.js → surface-JS2XU3U3.js} +5 -3
- package/dist/{surfaceHandlers-X2IZKA3E.js → surfaceHandlers-4B6TMZ34.js} +5 -5
- package/dist/{typecheck-X7U5ELMR.js → typecheck-NGOKNAJY.js} +23 -9
- package/dist/{uninstall-P7RGBX6G.js → uninstall-I2IJP5JZ.js} +30 -4
- package/dist/{upgrade-NQ5IWFZB.js → upgrade-FS26DJGG.js} +86 -32
- package/dist/{upgradePlan-A4LRKLEG.js → upgradePlan-ZS5IOVMZ.js} +46 -11
- package/dist/upkeepHandler-VFGYXJKB.js +13 -0
- package/dist/{userConfig-Z3GQE776.js → userConfig-ICASHI47.js} +1 -1
- package/dist/{watch-DGFMWUDL.js → watch-OCNEUXG6.js} +24 -34
- package/package.json +3 -2
- package/templates/skill-instructions.md +22 -3
- package/dist/chunk-6AAEVQ7I.js +0 -6336
- package/dist/chunk-I2JUKN6Z.js +0 -24
- package/dist/server-KFUD5X57.js +0 -55
|
@@ -0,0 +1,2613 @@
|
|
|
1
|
+
import { createRequire as __lurqCreateRequire } from 'node:module'; const require = __lurqCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
findRegistryServers,
|
|
4
|
+
getDeclaredHeaders,
|
|
5
|
+
getEndpointByUrl,
|
|
6
|
+
getEndpointsForServer,
|
|
7
|
+
listEndpointChanges
|
|
8
|
+
} from "./chunk-ZJMLQ37G.js";
|
|
9
|
+
import {
|
|
10
|
+
getContract
|
|
11
|
+
} from "./chunk-P6MIKSPY.js";
|
|
12
|
+
import "./chunk-XGOV6KV4.js";
|
|
13
|
+
import "./chunk-63MUJNZL.js";
|
|
14
|
+
import "./chunk-QKGYDMWB.js";
|
|
15
|
+
import {
|
|
16
|
+
endpointIdentity
|
|
17
|
+
} from "./chunk-5ABNDB3I.js";
|
|
18
|
+
import {
|
|
19
|
+
fetchServerManifest
|
|
20
|
+
} from "./chunk-LEGPB7DO.js";
|
|
21
|
+
import "./chunk-4JHDSVIN.js";
|
|
22
|
+
import "./chunk-MYGSLK44.js";
|
|
23
|
+
import "./chunk-72Y2FDVS.js";
|
|
24
|
+
import {
|
|
25
|
+
init_esm_shims
|
|
26
|
+
} from "./chunk-I3M2KJXB.js";
|
|
27
|
+
|
|
28
|
+
// src/connect/check.ts
|
|
29
|
+
init_esm_shims();
|
|
30
|
+
|
|
31
|
+
// src/clients/evaluate.ts
|
|
32
|
+
init_esm_shims();
|
|
33
|
+
|
|
34
|
+
// src/remoteProbe/types.ts
|
|
35
|
+
init_esm_shims();
|
|
36
|
+
var DEAD_STATUSES = /* @__PURE__ */ new Set([
|
|
37
|
+
"not_found",
|
|
38
|
+
"server_error",
|
|
39
|
+
"unreachable",
|
|
40
|
+
"dns_failed"
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
// src/clients/evaluate.ts
|
|
44
|
+
var usable = (s) => s === "yes" || s === "partial";
|
|
45
|
+
var MAX_EXAMPLES = 3;
|
|
46
|
+
var Acc = class {
|
|
47
|
+
blockers = [];
|
|
48
|
+
setup = [];
|
|
49
|
+
warnings = [];
|
|
50
|
+
unknowns = [];
|
|
51
|
+
block(code, detail) {
|
|
52
|
+
this.blockers.push({ code, detail });
|
|
53
|
+
}
|
|
54
|
+
step(code, detail) {
|
|
55
|
+
this.setup.push({ code, detail });
|
|
56
|
+
}
|
|
57
|
+
warn(code, detail) {
|
|
58
|
+
this.warnings.push({ code, detail });
|
|
59
|
+
}
|
|
60
|
+
unknown(code, detail, decisive) {
|
|
61
|
+
this.unknowns.push({ code, detail, decisive });
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
function route(server, client) {
|
|
65
|
+
const remoteSupport = server.remote ? client.transports[server.remote.transport === "sse" ? "sse" : "streamableHttp"] : "no";
|
|
66
|
+
if (server.remote && remoteSupport !== "no") return "remote";
|
|
67
|
+
if (server.package && client.transports.stdio !== "no") return "package";
|
|
68
|
+
return server.remote ? "remote" : server.package ? "package" : null;
|
|
69
|
+
}
|
|
70
|
+
function headerStep(headers) {
|
|
71
|
+
const names = headers.filter((h) => h.required || h.secret).map((h) => h.name);
|
|
72
|
+
const list = names.length ? names : headers.map((h) => h.name);
|
|
73
|
+
return list.length ? `send ${list.join(", ")} with every request` : "send the server\u2019s API key, usually as `Authorization: Bearer <token>`";
|
|
74
|
+
}
|
|
75
|
+
function evaluateAuth(remote, client, acc) {
|
|
76
|
+
const auth = remote.auth;
|
|
77
|
+
const mode = auth?.mode ?? (remote.status === "open" ? "none" : "unknown");
|
|
78
|
+
const c = client.auth;
|
|
79
|
+
if (mode === "none") {
|
|
80
|
+
if (remote.declaredHeaders.some((h) => h.required)) {
|
|
81
|
+
acc.warn(
|
|
82
|
+
"declared_header_not_enforced",
|
|
83
|
+
`the registry lists a required header (${headerStep(remote.declaredHeaders)}), but the endpoint answered without one`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (mode === "unknown") {
|
|
89
|
+
if (remote.status && !DEAD_STATUSES.has(remote.status))
|
|
90
|
+
acc.unknown("auth_unknown", "how this endpoint authenticates was not established", false);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (c.note && !c.strict) acc.warn("client_auth_note", c.note);
|
|
94
|
+
if (mode === "static") {
|
|
95
|
+
const step = headerStep(remote.declaredHeaders);
|
|
96
|
+
if (c.staticHeaders === "no")
|
|
97
|
+
acc.block(
|
|
98
|
+
"no_static_headers",
|
|
99
|
+
`the server wants a key or token configured by hand (${step}), and ${client.name} cannot send custom headers to a remote server`
|
|
100
|
+
);
|
|
101
|
+
else if (c.staticHeaders === "unknown")
|
|
102
|
+
acc.unknown(
|
|
103
|
+
"static_headers_unknown",
|
|
104
|
+
`the server wants a key configured by hand; no source confirms ${client.name} can send custom headers`,
|
|
105
|
+
true
|
|
106
|
+
);
|
|
107
|
+
else {
|
|
108
|
+
acc.step("configure_key", step);
|
|
109
|
+
if (c.staticHeaders === "partial")
|
|
110
|
+
acc.warn(
|
|
111
|
+
"static_headers_partial",
|
|
112
|
+
`${client.name} supports custom headers only with limitations`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const o = auth.oauth;
|
|
118
|
+
if (c.oauth === "no") {
|
|
119
|
+
if (usable(c.staticHeaders)) {
|
|
120
|
+
acc.step(
|
|
121
|
+
"token_instead_of_oauth",
|
|
122
|
+
`${client.name} cannot run the OAuth sign-in; supply an access token as a header instead, if the server accepts one`
|
|
123
|
+
);
|
|
124
|
+
acc.warn("oauth_unsupported", `${client.name} has no OAuth flow`);
|
|
125
|
+
} else {
|
|
126
|
+
acc.block(
|
|
127
|
+
"oauth_unsupported",
|
|
128
|
+
`the server requires OAuth sign-in and ${client.name} has no OAuth flow`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (c.oauth === "unknown") {
|
|
134
|
+
acc.unknown("oauth_unknown", `no source confirms ${client.name} runs the MCP OAuth flow`, true);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (c.oauth === "partial")
|
|
138
|
+
acc.warn("oauth_partial", `${client.name}'s OAuth support has known limitations`);
|
|
139
|
+
const viaCimd = o.cimd && usable(c.cimd);
|
|
140
|
+
const viaDcr = o.dcr && usable(c.dcr);
|
|
141
|
+
const offered = [o.cimd && "Client ID Metadata Documents", o.dcr && "Dynamic Client Registration"].filter(Boolean).join(" and ") || "no automatic registration";
|
|
142
|
+
const redirects = c.redirectUris.length ? ` Allow redirect URI(s): ${c.redirectUris.join(", ")}.` : "";
|
|
143
|
+
if (!viaCimd && !viaDcr) {
|
|
144
|
+
const couldMatch = o.cimd && c.cimd === "unknown" || o.dcr && c.dcr === "unknown";
|
|
145
|
+
if (couldMatch) {
|
|
146
|
+
acc.unknown(
|
|
147
|
+
"registration_unknown",
|
|
148
|
+
`the server offers ${offered}; no source confirms whether ${client.name} supports it`,
|
|
149
|
+
true
|
|
150
|
+
);
|
|
151
|
+
} else if (usable(c.preRegistered)) {
|
|
152
|
+
acc.step(
|
|
153
|
+
"pre_register_client",
|
|
154
|
+
`the server offers ${offered}, which ${client.name} cannot use: register an OAuth client with ${o.issuer ?? "its authorization server"} and enter its client id in ${client.name}.${redirects}`
|
|
155
|
+
);
|
|
156
|
+
} else if (c.preRegistered === "unknown") {
|
|
157
|
+
acc.unknown(
|
|
158
|
+
"registration_unknown",
|
|
159
|
+
`the server offers ${offered}, which ${client.name} cannot use, and no source confirms it accepts a pre-registered client`,
|
|
160
|
+
true
|
|
161
|
+
);
|
|
162
|
+
} else {
|
|
163
|
+
acc.block(
|
|
164
|
+
"no_shared_registration",
|
|
165
|
+
`the server offers ${offered}; ${client.name} can use neither, and has no way to enter a pre-registered client`
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
} else if (viaCimd && c.cimd === "partial" && !viaDcr || viaDcr && c.dcr === "partial" && !viaCimd) {
|
|
169
|
+
acc.warn(
|
|
170
|
+
"registration_partial",
|
|
171
|
+
`${client.name} supports the registration method this server offers only with limitations`
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
for (const v of remote.violations) {
|
|
175
|
+
switch (v.code) {
|
|
176
|
+
case "pkce_s256_not_advertised":
|
|
177
|
+
if (c.strict?.pkceS256Required)
|
|
178
|
+
acc.block(
|
|
179
|
+
"pkce_required",
|
|
180
|
+
`${client.name} refuses authorization servers that do not advertise PKCE S256, and this one does not`
|
|
181
|
+
);
|
|
182
|
+
else acc.warn("pkce_not_advertised", v.detail);
|
|
183
|
+
break;
|
|
184
|
+
case "as_metadata_unreachable":
|
|
185
|
+
acc.unknown("as_metadata_unreachable", v.detail, true);
|
|
186
|
+
break;
|
|
187
|
+
default:
|
|
188
|
+
acc.warn(v.code, v.detail);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function modelToolName(client, alias, tool) {
|
|
193
|
+
if (!client.toolNaming.format) return null;
|
|
194
|
+
let name = client.toolNaming.format.replace("{server}", alias).replace("{tool}", tool);
|
|
195
|
+
if (client.toolNaming.replacedChars)
|
|
196
|
+
name = name.replace(new RegExp(client.toolNaming.replacedChars, "g"), "_");
|
|
197
|
+
return name;
|
|
198
|
+
}
|
|
199
|
+
function evaluateTools(server, client, acc) {
|
|
200
|
+
const tools = server.tools;
|
|
201
|
+
if (!tools) {
|
|
202
|
+
acc.unknown(
|
|
203
|
+
"tools_unread",
|
|
204
|
+
"the tool list was not readable without credentials, so name and schema limits were not checked",
|
|
205
|
+
false
|
|
206
|
+
);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const max = client.toolNaming.maxLength;
|
|
210
|
+
if (client.toolNaming.format && max) {
|
|
211
|
+
const long = tools.map((t) => ({ tool: t.name, name: modelToolName(client, server.alias, t.name) })).filter((t) => t.name.length > max);
|
|
212
|
+
if (long.length) {
|
|
213
|
+
const ex = long.slice(0, MAX_EXAMPLES).map((t) => `${t.name} (${t.name.length})`).join(", ");
|
|
214
|
+
const what = `${long.length} tool name(s) exceed ${client.name}'s ${max}-character limit once prefixed as "${client.toolNaming.format}", e.g. ${ex}`;
|
|
215
|
+
switch (client.toolNaming.onOverflow) {
|
|
216
|
+
case "error":
|
|
217
|
+
acc.block("tool_name_too_long", `${what}; requests fail`);
|
|
218
|
+
break;
|
|
219
|
+
case "drop_tool":
|
|
220
|
+
acc.warn("tool_name_dropped", `${what}; those tools are dropped`);
|
|
221
|
+
break;
|
|
222
|
+
case "truncate": {
|
|
223
|
+
const cut = /* @__PURE__ */ new Map();
|
|
224
|
+
for (const t of tools) {
|
|
225
|
+
const n = modelToolName(client, server.alias, t.name).slice(0, max);
|
|
226
|
+
cut.set(n, [...cut.get(n) ?? [], t.name]);
|
|
227
|
+
}
|
|
228
|
+
const clashes = [...cut.values()].filter((v) => v.length > 1);
|
|
229
|
+
if (clashes.length)
|
|
230
|
+
acc.block(
|
|
231
|
+
"tool_name_collision",
|
|
232
|
+
`${what}; truncation makes ${clashes.slice(0, MAX_EXAMPLES).map((c) => c.join(" and ")).join("; ")} indistinguishable`
|
|
233
|
+
);
|
|
234
|
+
else acc.warn("tool_name_truncated", `${what}; they are truncated`);
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
case "truncate_hash":
|
|
238
|
+
acc.warn("tool_name_truncated", `${what}; they are truncated with a hash suffix`);
|
|
239
|
+
break;
|
|
240
|
+
default:
|
|
241
|
+
acc.warn(
|
|
242
|
+
"tool_name_too_long",
|
|
243
|
+
`${what}; what ${client.name} does with them is not documented`
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
} else if (tools.length) {
|
|
248
|
+
acc.unknown("tool_naming_unknown", `${client.name}'s tool-name limit is not documented`, false);
|
|
249
|
+
}
|
|
250
|
+
const schema = client.schema;
|
|
251
|
+
const offending = [];
|
|
252
|
+
for (const t of tools) {
|
|
253
|
+
const s = t.inputSchema;
|
|
254
|
+
if (!s || typeof s !== "object") continue;
|
|
255
|
+
const text = JSON.stringify(s);
|
|
256
|
+
if (schema.rootCombinators === "no" && ("anyOf" in s || "oneOf" in s || "allOf" in s))
|
|
257
|
+
offending.push({ rule: "a root-level anyOf/oneOf/allOf", tool: t.name });
|
|
258
|
+
for (const kw of schema.rejectedKeywords)
|
|
259
|
+
if (text.includes(`"${kw}"`)) offending.push({ rule: `\`${kw}\``, tool: t.name });
|
|
260
|
+
if ((schema.typeArrays === "no" || schema.typeArrays === "partial") && /"type"\s*:\s*\[/.test(text))
|
|
261
|
+
offending.push({ rule: "type arrays", tool: t.name });
|
|
262
|
+
}
|
|
263
|
+
if (offending.length) {
|
|
264
|
+
const rules = [...new Set(offending.map((o) => o.rule))].join(", ");
|
|
265
|
+
const names = [...new Set(offending.map((o) => o.tool))];
|
|
266
|
+
const what = `${names.length} tool(s) use ${rules}, which ${client.name} does not accept (e.g. ${names.slice(0, MAX_EXAMPLES).join(", ")})`;
|
|
267
|
+
switch (schema.onInvalid) {
|
|
268
|
+
case "fail_request":
|
|
269
|
+
acc.block("schema_rejected", `${what}; one such tool fails every request`);
|
|
270
|
+
break;
|
|
271
|
+
case "drop_tool":
|
|
272
|
+
acc.warn("schema_tool_dropped", `${what}; those tools are dropped`);
|
|
273
|
+
break;
|
|
274
|
+
case "sanitize":
|
|
275
|
+
break;
|
|
276
|
+
default:
|
|
277
|
+
acc.warn("schema_rejected", `${what}; how it handles them is not documented`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const cap = client.toolLimits.maxTools;
|
|
281
|
+
if (cap && tools.length > cap && client.toolLimits.toolSearch !== "yes") {
|
|
282
|
+
acc.warn(
|
|
283
|
+
"too_many_tools",
|
|
284
|
+
`${tools.length} tools exceeds ${client.name}'s documented limit of ${cap}${client.toolLimits.scope ? ` (${client.toolLimits.scope.replace("_", " ")})` : ""}`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function evaluateClient(server, client) {
|
|
289
|
+
const acc = new Acc();
|
|
290
|
+
const via = route(server, client);
|
|
291
|
+
if (!via) {
|
|
292
|
+
acc.unknown(
|
|
293
|
+
"no_connection_info",
|
|
294
|
+
"lurq has neither a remote endpoint nor a package for this server",
|
|
295
|
+
true
|
|
296
|
+
);
|
|
297
|
+
} else if (via === "remote") {
|
|
298
|
+
const r = server.remote;
|
|
299
|
+
const kind = r.transport === "sse" ? "sse" : "streamableHttp";
|
|
300
|
+
const support = client.transports[kind];
|
|
301
|
+
const label = kind === "sse" ? "legacy HTTP+SSE" : "Streamable HTTP";
|
|
302
|
+
if (support === "no") {
|
|
303
|
+
acc.block(
|
|
304
|
+
"transport_unsupported",
|
|
305
|
+
`the server is reachable only over ${label}${server.package ? " or as a local package" : ""}, and ${client.name} supports neither`
|
|
306
|
+
);
|
|
307
|
+
} else if (support === "unknown") {
|
|
308
|
+
acc.unknown("transport_unknown", `no source confirms ${client.name} supports ${label}`, true);
|
|
309
|
+
} else if (support === "partial") {
|
|
310
|
+
acc.warn(
|
|
311
|
+
"transport_partial",
|
|
312
|
+
`${client.name} supports ${label} only partially${kind === "sse" ? " (deprecated)" : ""}`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
if (r.templated)
|
|
316
|
+
acc.step("fill_url", `the URL has placeholders to fill in before connecting: ${r.url}`);
|
|
317
|
+
if (r.status === null) {
|
|
318
|
+
if (!r.templated) acc.unknown("not_probed", "this endpoint has not been probed yet", true);
|
|
319
|
+
} else if (DEAD_STATUSES.has(r.status)) {
|
|
320
|
+
acc.block(
|
|
321
|
+
"endpoint_dead",
|
|
322
|
+
`the endpoint was ${r.status.replace("_", " ")} when last checked${r.observedAt ? ` (${r.observedAt.toISOString().slice(0, 10)})` : ""}`
|
|
323
|
+
);
|
|
324
|
+
} else if (r.status === "blocked" || r.status === "protocol_error" || r.status === "timeout") {
|
|
325
|
+
acc.unknown(
|
|
326
|
+
"probe_inconclusive",
|
|
327
|
+
`the last probe was inconclusive (${r.status.replace("_", " ")})`,
|
|
328
|
+
true
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
if (!acc.blockers.length) {
|
|
332
|
+
evaluateAuth(r, client, acc);
|
|
333
|
+
evaluateTools(server, client, acc);
|
|
334
|
+
}
|
|
335
|
+
} else {
|
|
336
|
+
const p = server.package;
|
|
337
|
+
if (client.transports.stdio === "no") {
|
|
338
|
+
acc.block(
|
|
339
|
+
"stdio_unsupported",
|
|
340
|
+
`the server runs as a local ${p.registryType} package and ${client.name} cannot run local servers`
|
|
341
|
+
);
|
|
342
|
+
} else if (client.transports.stdio === "unknown") {
|
|
343
|
+
acc.unknown(
|
|
344
|
+
"stdio_unknown",
|
|
345
|
+
`no source confirms ${client.name} can run local (stdio) servers`,
|
|
346
|
+
true
|
|
347
|
+
);
|
|
348
|
+
} else {
|
|
349
|
+
if (client.transports.stdio === "partial")
|
|
350
|
+
acc.warn("stdio_partial", `${client.name} runs local servers only in some environments`);
|
|
351
|
+
const required = p.env.filter((e) => e.required);
|
|
352
|
+
if (required.length)
|
|
353
|
+
acc.step(
|
|
354
|
+
"set_env",
|
|
355
|
+
`set ${required.map((e) => e.name).join(", ")} in the server's environment`
|
|
356
|
+
);
|
|
357
|
+
evaluateTools(server, client, acc);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const verdict = acc.blockers.length ? "blocked" : acc.unknowns.some((u) => u.decisive) ? "unknown" : acc.setup.length ? "needs_setup" : "works";
|
|
361
|
+
return { client: client.id, clientName: client.name, verdict, via, ...acc };
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// src/clients/profiles.ts
|
|
365
|
+
init_esm_shims();
|
|
366
|
+
var CLIENT_PROFILES = [
|
|
367
|
+
{
|
|
368
|
+
id: "claude-code",
|
|
369
|
+
name: "Claude Code",
|
|
370
|
+
kind: "cli",
|
|
371
|
+
transports: {
|
|
372
|
+
stdio: "yes",
|
|
373
|
+
streamableHttp: "yes",
|
|
374
|
+
sse: "partial"
|
|
375
|
+
},
|
|
376
|
+
auth: {
|
|
377
|
+
oauth: "yes",
|
|
378
|
+
dcr: "yes",
|
|
379
|
+
cimd: "yes",
|
|
380
|
+
preRegistered: "yes",
|
|
381
|
+
staticHeaders: "yes",
|
|
382
|
+
redirectUris: ["http://localhost:{port}/callback", "http://127.0.0.1:{port}/callback"],
|
|
383
|
+
note: "Authorization servers must accept both localhost and 127.0.0.1 loopback callbacks on any port, since the port is random unless --callback-port pins it."
|
|
384
|
+
},
|
|
385
|
+
toolNaming: {
|
|
386
|
+
format: "mcp__{server}__{tool}",
|
|
387
|
+
replacedChars: null,
|
|
388
|
+
maxLength: 128,
|
|
389
|
+
onOverflow: "unknown"
|
|
390
|
+
},
|
|
391
|
+
toolLimits: {
|
|
392
|
+
maxTools: null,
|
|
393
|
+
scope: null,
|
|
394
|
+
toolSearch: "yes"
|
|
395
|
+
},
|
|
396
|
+
schema: {
|
|
397
|
+
rootCombinators: "partial",
|
|
398
|
+
rejectedKeywords: [],
|
|
399
|
+
typeArrays: "unknown",
|
|
400
|
+
onInvalid: "drop_tool"
|
|
401
|
+
},
|
|
402
|
+
protocol: {
|
|
403
|
+
revision: "2026-07-28",
|
|
404
|
+
resources: "yes",
|
|
405
|
+
prompts: "yes",
|
|
406
|
+
sampling: "unknown",
|
|
407
|
+
elicitation: "yes"
|
|
408
|
+
},
|
|
409
|
+
config: {
|
|
410
|
+
remote: {
|
|
411
|
+
file: ".mcp.json or ~/.claude.json",
|
|
412
|
+
containerKey: "mcpServers",
|
|
413
|
+
typeKey: "type",
|
|
414
|
+
typeValue: "http",
|
|
415
|
+
urlKey: "url",
|
|
416
|
+
headersKey: "headers"
|
|
417
|
+
},
|
|
418
|
+
addCommand: 'claude mcp add --transport http {name} {url} --header "{header}"',
|
|
419
|
+
deeplink: null
|
|
420
|
+
},
|
|
421
|
+
availability: "Local MCP has no plan gating; managed settings (managed-mcp.json, allowedMcpServers/deniedMcpServers) can block servers.",
|
|
422
|
+
verifiedAt: "2026-09-15",
|
|
423
|
+
sources: [
|
|
424
|
+
{
|
|
425
|
+
url: "https://code.claude.com/docs/en/mcp",
|
|
426
|
+
fact: "transports stdio/http/sse(deprecated)/ws; ws not in --transport and header-only auth; url without type is an error"
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
url: "https://code.claude.com/docs/en/mcp",
|
|
430
|
+
fact: "claude mcp add syntax, --header, --env, --scope, --client-id/--client-secret/--callback-port, add-json, add-from-claude-desktop"
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
url: "https://code.claude.com/docs/en/mcp",
|
|
434
|
+
fact: "config files .mcp.json, ~/.claude.json, managed-mcp.json; mcpServers key; entry keys incl. headersHelper, oauth, alwaysLoad, timeout"
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
url: "https://code.claude.com/docs/en/mcp",
|
|
438
|
+
fact: "${VAR} and ${VAR:-default} expansion locations; credential env vars read as empty in url/headers"
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
url: "https://code.claude.com/docs/en/mcp",
|
|
442
|
+
fact: "DCR default; CIMD supported and discovered automatically; redirect http://localhost:PORT/callback; v2.1.229 127.0.0.1 regression fixed in v2.1.231"
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
url: "https://code.claude.com/docs/en/mcp",
|
|
446
|
+
fact: "plugin tool name mcp__plugin_<plugin>_<server>__<tool>, invalid chars -> _; server names limited to letters/numbers/-/_"
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
url: "https://code.claude.com/docs/en/mcp",
|
|
450
|
+
fact: "MAX_MCP_OUTPUT_TOKENS default 25,000, warning at 10,000; anthropic/maxResultSizeChars up to 500,000 chars"
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
url: "https://code.claude.com/docs/en/mcp",
|
|
454
|
+
fact: "tool search on by default, ENABLE_TOOL_SEARCH values, auto = 10% threshold, alwaysLoad, no fixed per-server cap, descriptions/instructions truncated at 2KB"
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
url: "https://code.claude.com/docs/en/mcp",
|
|
458
|
+
fact: "root-level anyOf/oneOf/allOf flattened (v2.1.195+); property names 1-64 [A-Za-z0-9_.-]; draft 2020-12 meta-schema check; invalid tools excluded (v2.1.216+)"
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
url: "https://code.claude.com/docs/en/mcp",
|
|
462
|
+
fact: "resources via @ mentions, prompts as /server:prompt, elicitation form/URL, roots/list, list_changed, anthropic/requiresUserInteraction"
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
url: "https://code.claude.com/docs/en/mcp",
|
|
466
|
+
fact: "v1/v2 runtimes; v2 = TS SDK 2.0 with revision 2026-07-28; MCP_SDK_GENERATION, MCP_PROTOCOL_NEGOTIATION"
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
url: "https://code.claude.com/docs/en/mcp",
|
|
470
|
+
fact: "claude.ai connectors only with a claude.ai subscription login; allowedMcpServers/deniedMcpServers; ENABLE_CLAUDEAI_MCP_SERVERS; managedMcpServers precedence v2.1.259+"
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
url: "https://code.claude.com/docs/en/changelog",
|
|
474
|
+
fact: "Support MCP structuredContent field in tool responses; CIMD (SEP-991) support added; http entries fall back to legacy SSE"
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
url: "https://claude.com/docs/connectors/building/authentication",
|
|
478
|
+
fact: "Claude Code uses RFC 8252 loopback redirect on ephemeral port; CIMD declares http://localhost/callback and http://127.0.0.1/callback; does not use Anthropic-held creds; PKCE S256"
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
url: "https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools",
|
|
482
|
+
fact: "Claude API tool name regex ^[a-zA-Z0-9_-]{1,128}$"
|
|
483
|
+
}
|
|
484
|
+
]
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
id: "claude-desktop",
|
|
488
|
+
name: "Claude Desktop",
|
|
489
|
+
kind: "desktop",
|
|
490
|
+
transports: {
|
|
491
|
+
stdio: "yes",
|
|
492
|
+
streamableHttp: "yes",
|
|
493
|
+
sse: "partial"
|
|
494
|
+
},
|
|
495
|
+
auth: {
|
|
496
|
+
oauth: "yes",
|
|
497
|
+
dcr: "yes",
|
|
498
|
+
cimd: "yes",
|
|
499
|
+
preRegistered: "yes",
|
|
500
|
+
staticHeaders: "partial",
|
|
501
|
+
redirectUris: ["https://claude.ai/api/mcp/auth_callback", "http://127.0.0.1:53280/callback"],
|
|
502
|
+
note: "Remote servers are added as connectors that connect from Anthropic's cloud (160.79.104.0/21), and request headers are an admin-set beta (max 4) that cannot carry Authorization on an OAuth connection."
|
|
503
|
+
},
|
|
504
|
+
toolNaming: {
|
|
505
|
+
format: null,
|
|
506
|
+
replacedChars: null,
|
|
507
|
+
maxLength: null,
|
|
508
|
+
onOverflow: "unknown"
|
|
509
|
+
},
|
|
510
|
+
toolLimits: {
|
|
511
|
+
maxTools: null,
|
|
512
|
+
scope: null,
|
|
513
|
+
toolSearch: "unknown"
|
|
514
|
+
},
|
|
515
|
+
schema: {
|
|
516
|
+
rootCombinators: "unknown",
|
|
517
|
+
rejectedKeywords: [],
|
|
518
|
+
typeArrays: "unknown",
|
|
519
|
+
onInvalid: "unknown"
|
|
520
|
+
},
|
|
521
|
+
protocol: {
|
|
522
|
+
revision: null,
|
|
523
|
+
resources: "yes",
|
|
524
|
+
prompts: "yes",
|
|
525
|
+
sampling: "no",
|
|
526
|
+
elicitation: "unknown"
|
|
527
|
+
},
|
|
528
|
+
config: {
|
|
529
|
+
remote: null,
|
|
530
|
+
addCommand: null,
|
|
531
|
+
deeplink: null
|
|
532
|
+
},
|
|
533
|
+
availability: "Custom connectors on Free (one), Pro, Max, Team and Enterprise; on Team/Enterprise only Owners add them.",
|
|
534
|
+
verifiedAt: "2026-09-15",
|
|
535
|
+
sources: [
|
|
536
|
+
{
|
|
537
|
+
url: "https://modelcontextprotocol.io/docs/develop/connect-local-servers",
|
|
538
|
+
fact: "claude_desktop_config.json paths macOS/Windows, mcpServers command/args/env, Settings > Developer > Edit Config, restart, log paths"
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
url: "https://modelcontextprotocol.io/docs/develop/connect-remote-servers",
|
|
542
|
+
fact: "remote servers added via Settings > Connectors > Add custom connector in Desktop or browser; resources and prompts selectable in the attach menu"
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
url: "https://claude.com/docs/connectors/building/authentication",
|
|
546
|
+
fact: "DCR/CIMD out of the box, CIMD selection conditions, static_headers beta, callback https://claude.ai/api/mcp/auth_callback for Desktop, PKCE S256, 10s/30s endpoint latency, egress 160.79.104.0/21"
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
url: "https://claude.com/docs/connectors/custom/remote-mcp",
|
|
550
|
+
fact: "Add custom connector dialog: CIMD/DCR/own client, request headers up to four, header approval, /sse selects SSE, auth settings immutable after add"
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
url: "https://claude.com/docs/connectors/building/index",
|
|
554
|
+
fact: "Streamable HTTP and legacy SSE (deprecating); tools/prompts/resources, text+image results, text+binary resources; subscriptions & sampling not yet supported; ~150,000 char result; 240s timeout"
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
url: "https://claude.com/docs/connectors/building/mcpb",
|
|
558
|
+
fact: ".mcpb zip with manifest.json, stdio, user_config UI, mcpb CLI, macOS/Windows"
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
url: "https://claude.com/docs/connectors/building/review-criteria",
|
|
562
|
+
fact: "tool names <= 64 chars; title + readOnlyHint/destructiveHint required and drive auto-permissions"
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
url: "https://claude.com/docs/connectors/overview",
|
|
566
|
+
fact: "Claude Desktop: Full MCP support and local desktop extensions"
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
url: "https://claude.com/docs/third-party/claude-desktop/extensions",
|
|
570
|
+
fact: "3P managedMcpServers (http/sse/stdio), fixed OAuth loopback http://127.0.0.1:53280/callback, headersHelper 30s"
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
url: "https://support.claude.com/en/articles/10949351-getting-started-with-local-mcp-servers-on-claude-desktop",
|
|
574
|
+
fact: "Settings > Extensions install flow; Team/Enterprise owner controls for extensions"
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
url: "https://support.claude.com/en/articles/11175166-getting-started-with-custom-connectors-using-remote-mcp",
|
|
578
|
+
fact: "custom connectors on Free (1), Pro, Max, Team, Enterprise; only Owners add on Team/Enterprise"
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
url: "https://github.com/webflow/mcp-server/issues/73",
|
|
582
|
+
fact: "CORROBORATION ONLY (non-vendor): Desktop does not connect to remote servers configured in claude_desktop_config.json"
|
|
583
|
+
}
|
|
584
|
+
]
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
id: "claude-ai",
|
|
588
|
+
name: "Claude.ai",
|
|
589
|
+
kind: "web",
|
|
590
|
+
transports: {
|
|
591
|
+
stdio: "no",
|
|
592
|
+
streamableHttp: "yes",
|
|
593
|
+
sse: "partial"
|
|
594
|
+
},
|
|
595
|
+
auth: {
|
|
596
|
+
oauth: "yes",
|
|
597
|
+
dcr: "yes",
|
|
598
|
+
cimd: "yes",
|
|
599
|
+
preRegistered: "yes",
|
|
600
|
+
staticHeaders: "partial",
|
|
601
|
+
redirectUris: ["https://claude.ai/api/mcp/auth_callback"],
|
|
602
|
+
note: "Request headers are an admin-set beta for limited orgs (max 4) that cannot be combined with OAuth, and the server must be publicly reachable over IPv4 from 160.79.104.0/21."
|
|
603
|
+
},
|
|
604
|
+
toolNaming: {
|
|
605
|
+
format: null,
|
|
606
|
+
replacedChars: null,
|
|
607
|
+
maxLength: null,
|
|
608
|
+
onOverflow: "unknown"
|
|
609
|
+
},
|
|
610
|
+
toolLimits: {
|
|
611
|
+
maxTools: null,
|
|
612
|
+
scope: null,
|
|
613
|
+
toolSearch: "unknown"
|
|
614
|
+
},
|
|
615
|
+
schema: {
|
|
616
|
+
rootCombinators: "unknown",
|
|
617
|
+
rejectedKeywords: [],
|
|
618
|
+
typeArrays: "unknown",
|
|
619
|
+
onInvalid: "unknown"
|
|
620
|
+
},
|
|
621
|
+
protocol: {
|
|
622
|
+
revision: null,
|
|
623
|
+
resources: "yes",
|
|
624
|
+
prompts: "yes",
|
|
625
|
+
sampling: "no",
|
|
626
|
+
elicitation: "unknown"
|
|
627
|
+
},
|
|
628
|
+
config: {
|
|
629
|
+
remote: null,
|
|
630
|
+
addCommand: null,
|
|
631
|
+
deeplink: null
|
|
632
|
+
},
|
|
633
|
+
availability: "Free (one custom connector), Pro, Max, Team and Enterprise; on Team/Enterprise only Owners add connectors.",
|
|
634
|
+
verifiedAt: "2026-09-15",
|
|
635
|
+
sources: [
|
|
636
|
+
{
|
|
637
|
+
url: "https://support.claude.com/en/articles/11175166-getting-started-with-custom-connectors-using-remote-mcp",
|
|
638
|
+
fact: "Free/Pro/Max/Team/Enterprise; Free limited to one; Owners only on Team/Enterprise; Advanced settings OAuth Client ID/Secret; reachable from Anthropic IP ranges"
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
url: "https://support.claude.com/en/articles/11176164-use-connectors-to-extend-claude-s-capabilities",
|
|
642
|
+
fact: "connectors on web, Desktop, Cowork, iOS, Android; installing connectors on mobile is beta"
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
url: "https://claude.com/docs/connectors/building/authentication",
|
|
646
|
+
fact: "auth types oauth_dcr/oauth_cimd/oauth_anthropic_creds/custom_connection/static_headers(beta)/none; CIMD selection rule; PKCE S256; callback https://claude.ai/api/mcp/auth_callback; 401+resource_metadata; token refresh; 10s/30s latency; client_credentials unsupported"
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
url: "https://claude.com/docs/connectors/custom/remote-mcp",
|
|
650
|
+
fact: "UI paths per plan, dialog fields, request headers beta up to four, header name approval, no Authorization header with OAuth, /sse selects SSE, auth immutable after add"
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
url: "https://claude.com/docs/connectors/building/index",
|
|
654
|
+
fact: "Streamable HTTP + legacy SSE (deprecating); auth specs 2025-03-26/2025-06-18/2025-11-25; tools/prompts/resources; subscriptions, sampling, draft capabilities not yet supported; ~150k char results; 240s timeout"
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
url: "https://claude.com/docs/connectors/building/troubleshooting",
|
|
658
|
+
fact: "IPv4-only, all resolved addresses must be public; RFC 8707 resource param; cross-host redirect drops Authorization header"
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
url: "https://claude.com/docs/connectors/building/review-criteria",
|
|
662
|
+
fact: "tool names <= 64 chars; title + readOnlyHint/destructiveHint required"
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
url: "https://claude.com/docs/connectors/building/testing",
|
|
666
|
+
fact: "any plan can add a custom connector for testing; clientInfo name varies (claude-ai / Anthropic / claude-code)"
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
url: "https://claude.com/docs/connectors/overview",
|
|
670
|
+
fact: "Claude.ai full remote MCP & MCP Apps; Mobile remote MCP access"
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
url: "https://platform.claude.com/docs/en/api/ip-addresses",
|
|
674
|
+
fact: "outbound IPv4 160.79.104.0/21 for MCP tool calls"
|
|
675
|
+
}
|
|
676
|
+
]
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
id: "claude-api",
|
|
680
|
+
name: "Claude API MCP connector",
|
|
681
|
+
kind: "api",
|
|
682
|
+
transports: {
|
|
683
|
+
stdio: "no",
|
|
684
|
+
streamableHttp: "yes",
|
|
685
|
+
sse: "yes"
|
|
686
|
+
},
|
|
687
|
+
auth: {
|
|
688
|
+
oauth: "no",
|
|
689
|
+
dcr: "no",
|
|
690
|
+
cimd: "no",
|
|
691
|
+
preRegistered: "no",
|
|
692
|
+
staticHeaders: "partial",
|
|
693
|
+
redirectUris: [],
|
|
694
|
+
note: "The caller runs OAuth and passes the access token as authorization_token; no other request headers are documented."
|
|
695
|
+
},
|
|
696
|
+
toolNaming: {
|
|
697
|
+
format: "{tool}",
|
|
698
|
+
replacedChars: null,
|
|
699
|
+
maxLength: null,
|
|
700
|
+
onOverflow: "unknown"
|
|
701
|
+
},
|
|
702
|
+
toolLimits: {
|
|
703
|
+
maxTools: null,
|
|
704
|
+
scope: null,
|
|
705
|
+
toolSearch: "yes"
|
|
706
|
+
},
|
|
707
|
+
schema: {
|
|
708
|
+
rootCombinators: "no",
|
|
709
|
+
rejectedKeywords: [],
|
|
710
|
+
typeArrays: "unknown",
|
|
711
|
+
onInvalid: "unknown"
|
|
712
|
+
},
|
|
713
|
+
protocol: {
|
|
714
|
+
revision: null,
|
|
715
|
+
resources: "no",
|
|
716
|
+
prompts: "no",
|
|
717
|
+
sampling: "no",
|
|
718
|
+
elicitation: "no"
|
|
719
|
+
},
|
|
720
|
+
config: {
|
|
721
|
+
remote: null,
|
|
722
|
+
addCommand: null,
|
|
723
|
+
deeplink: null
|
|
724
|
+
},
|
|
725
|
+
availability: "Beta (anthropic-beta: mcp-client-2025-11-20) on the Claude API, Claude Platform on AWS and Microsoft Foundry; not on Amazon Bedrock or Google Cloud, and not ZDR-eligible.",
|
|
726
|
+
verifiedAt: "2026-09-15",
|
|
727
|
+
sources: [
|
|
728
|
+
{
|
|
729
|
+
url: "https://platform.claude.com/docs/en/agents-and-tools/mcp-connector",
|
|
730
|
+
fact: "beta header mcp-client-2025-11-20, 2025-04-04 deprecated; Beta; ZDR not eligible; platforms Claude API/AWS/Foundry, not Bedrock/Google Cloud"
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
url: "https://platform.claude.com/docs/en/agents-and-tools/mcp-connector",
|
|
734
|
+
fact: "only tool calls supported; Streamable HTTP and SSE; no local STDIO; url must start with https://"
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
url: "https://platform.claude.com/docs/en/agents-and-tools/mcp-connector",
|
|
738
|
+
fact: "mcp_servers fields type/url/name/authorization_token; mcp_toolset default_config/configs/enabled/defer_loading/cache_control; validation rules; unknown tool names only warn"
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
url: "https://platform.claude.com/docs/en/agents-and-tools/mcp-connector",
|
|
742
|
+
fact: "API consumers handle OAuth and token refresh; mcp_tool_use/mcp_tool_result blocks with server_name; Batches supported; SDK helpers for prompts/resources/local servers"
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
url: "https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool",
|
|
746
|
+
fact: "tool_search_tool_regex_20251119 / bm25; defer_loading; at least one non-deferred tool; up to 5 tool_reference results by default"
|
|
747
|
+
},
|
|
748
|
+
{
|
|
749
|
+
url: "https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools",
|
|
750
|
+
fact: "tool name regex ^[a-zA-Z0-9_-]{1,128}$"
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
url: "https://platform.claude.com/docs/en/build-with-claude/structured-outputs",
|
|
754
|
+
fact: "strict tool use JSON Schema limitations (no recursion, no min/max/length, additionalProperties false, minItems 0/1, regex limits, 400 on unsupported)"
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
url: "https://code.claude.com/docs/en/mcp",
|
|
758
|
+
fact: "Claude API rejects root-level anyOf/oneOf/allOf; property names 1-64; whole request 400 on one invalid schema"
|
|
759
|
+
},
|
|
760
|
+
{
|
|
761
|
+
url: "https://platform.claude.com/docs/en/api/ip-addresses",
|
|
762
|
+
fact: "outbound 160.79.104.0/21 for MCP connector calls"
|
|
763
|
+
}
|
|
764
|
+
]
|
|
765
|
+
},
|
|
766
|
+
{
|
|
767
|
+
id: "chatgpt",
|
|
768
|
+
name: "ChatGPT",
|
|
769
|
+
kind: "web",
|
|
770
|
+
transports: {
|
|
771
|
+
stdio: "no",
|
|
772
|
+
streamableHttp: "yes",
|
|
773
|
+
sse: "partial"
|
|
774
|
+
},
|
|
775
|
+
auth: {
|
|
776
|
+
oauth: "yes",
|
|
777
|
+
dcr: "yes",
|
|
778
|
+
cimd: "yes",
|
|
779
|
+
preRegistered: "yes",
|
|
780
|
+
staticHeaders: "unknown",
|
|
781
|
+
redirectUris: [
|
|
782
|
+
"https://chatgpt.com/connector_platform_oauth_redirect",
|
|
783
|
+
"https://chatgpt.com/connector/oauth/{callback_id}"
|
|
784
|
+
],
|
|
785
|
+
note: "The authorization server must advertise S256 in code_challenge_methods_supported or ChatGPT will not connect.",
|
|
786
|
+
strict: { pkceS256Required: true }
|
|
787
|
+
},
|
|
788
|
+
toolNaming: {
|
|
789
|
+
format: null,
|
|
790
|
+
replacedChars: null,
|
|
791
|
+
maxLength: null,
|
|
792
|
+
onOverflow: "unknown"
|
|
793
|
+
},
|
|
794
|
+
toolLimits: {
|
|
795
|
+
maxTools: null,
|
|
796
|
+
scope: null,
|
|
797
|
+
toolSearch: "unknown"
|
|
798
|
+
},
|
|
799
|
+
schema: {
|
|
800
|
+
rootCombinators: "unknown",
|
|
801
|
+
rejectedKeywords: [],
|
|
802
|
+
typeArrays: "unknown",
|
|
803
|
+
onInvalid: "unknown"
|
|
804
|
+
},
|
|
805
|
+
protocol: {
|
|
806
|
+
revision: null,
|
|
807
|
+
resources: "partial",
|
|
808
|
+
prompts: "unknown",
|
|
809
|
+
sampling: "unknown",
|
|
810
|
+
elicitation: "unknown"
|
|
811
|
+
},
|
|
812
|
+
config: {
|
|
813
|
+
remote: null,
|
|
814
|
+
addCommand: null,
|
|
815
|
+
deeplink: null
|
|
816
|
+
},
|
|
817
|
+
availability: "Developer mode on Pro, Plus, Business, Enterprise and Education (web only); Business/Enterprise/Edu workspace admins must enable it.",
|
|
818
|
+
verifiedAt: "2026-09-15",
|
|
819
|
+
sources: [
|
|
820
|
+
{
|
|
821
|
+
url: "https://developers.openai.com/api/docs/guides/developer-mode",
|
|
822
|
+
fact: "Plans Pro/Plus/Business/Enterprise/Education on web; transports SSE and streaming HTTP; auth OAuth/No Auth/Mixed; readOnlyHint respected; toggle/refresh tools; instructions 512 chars"
|
|
823
|
+
},
|
|
824
|
+
{
|
|
825
|
+
url: "https://developers.openai.com/apps-sdk/build/auth",
|
|
826
|
+
fact: "PKCE S256 required; CIMD preferred with chatgpt.com client.json URLs; DCR alternative; predefined OAuth client option; redirect URIs; RFC 9728/8414; resource param; RFC 9207 iss; token endpoint auth methods; securitySchemes; mcp/www_authenticate; cites MCP auth 2025-11-25 and 2026-07-28"
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
url: "https://developers.openai.com/apps-sdk/reference",
|
|
830
|
+
fact: "_meta keys (openai/outputTemplate, ui.resourceUri, fileParams), annotations readOnlyHint/destructiveHint/openWorldHint required, structuredContent/content/_meta audiences"
|
|
831
|
+
},
|
|
832
|
+
{
|
|
833
|
+
url: "https://developers.openai.com/apps-sdk/mcp-apps-in-chatgpt",
|
|
834
|
+
fact: "ChatGPT implements MCP Apps standard; text/html;profile=mcp-app; _meta.ui.resourceUri"
|
|
835
|
+
},
|
|
836
|
+
{
|
|
837
|
+
url: "https://developers.openai.com/plugins/build/mcp-server",
|
|
838
|
+
fact: "Streamable HTTP transport; structuredContent + content; annotations; instructions first 512 chars; elicitation mentioned"
|
|
839
|
+
},
|
|
840
|
+
{
|
|
841
|
+
url: "https://developers.openai.com/apps-sdk/deploy/connect-chatgpt",
|
|
842
|
+
fact: "Public HTTPS /mcp endpoint or Secure MCP Tunnel (dev only); Refresh after tool changes in developer mode"
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
url: "https://help.openai.com/en/articles/11509118-admin-controls-security-and-compliance-in-apps-enterprise-edu-and-business",
|
|
846
|
+
fact: "Workspace admin enablement and Enterprise/Edu RBAC for developer mode (search snippet only; direct fetch returned 403)"
|
|
847
|
+
},
|
|
848
|
+
{
|
|
849
|
+
url: "https://help.openai.com/en/articles/12584461-developer-mode-and-mcp-apps-in-chatgpt",
|
|
850
|
+
fact: "Help article on developer mode (direct fetch 403; not used as sole source)"
|
|
851
|
+
}
|
|
852
|
+
]
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
id: "openai-responses",
|
|
856
|
+
name: "OpenAI Responses API",
|
|
857
|
+
kind: "api",
|
|
858
|
+
transports: {
|
|
859
|
+
stdio: "no",
|
|
860
|
+
streamableHttp: "yes",
|
|
861
|
+
sse: "yes"
|
|
862
|
+
},
|
|
863
|
+
auth: {
|
|
864
|
+
oauth: "no",
|
|
865
|
+
dcr: "no",
|
|
866
|
+
cimd: "no",
|
|
867
|
+
preRegistered: "no",
|
|
868
|
+
staticHeaders: "yes",
|
|
869
|
+
redirectUris: [],
|
|
870
|
+
note: "The caller runs OAuth itself and must resend the access token in `authorization` on every request, since it is not stored."
|
|
871
|
+
},
|
|
872
|
+
toolNaming: {
|
|
873
|
+
format: null,
|
|
874
|
+
replacedChars: null,
|
|
875
|
+
maxLength: null,
|
|
876
|
+
onOverflow: "unknown"
|
|
877
|
+
},
|
|
878
|
+
toolLimits: {
|
|
879
|
+
maxTools: null,
|
|
880
|
+
scope: null,
|
|
881
|
+
toolSearch: "partial"
|
|
882
|
+
},
|
|
883
|
+
schema: {
|
|
884
|
+
rootCombinators: "unknown",
|
|
885
|
+
rejectedKeywords: [],
|
|
886
|
+
typeArrays: "unknown",
|
|
887
|
+
onInvalid: "unknown"
|
|
888
|
+
},
|
|
889
|
+
protocol: {
|
|
890
|
+
revision: null,
|
|
891
|
+
resources: "unknown",
|
|
892
|
+
prompts: "unknown",
|
|
893
|
+
sampling: "unknown",
|
|
894
|
+
elicitation: "unknown"
|
|
895
|
+
},
|
|
896
|
+
config: {
|
|
897
|
+
remote: null,
|
|
898
|
+
addCommand: null,
|
|
899
|
+
deeplink: null
|
|
900
|
+
},
|
|
901
|
+
availability: null,
|
|
902
|
+
verifiedAt: "2026-09-15",
|
|
903
|
+
sources: [
|
|
904
|
+
{
|
|
905
|
+
url: "https://developers.openai.com/api/docs/guides/tools-connectors-mcp",
|
|
906
|
+
fact: "Streamable HTTP or HTTP/SSE; params; authorization not stored; app must handle OAuth; mcp_list_tools caching; defer_loading with tool search; 8 connectors; ZDR"
|
|
907
|
+
},
|
|
908
|
+
{
|
|
909
|
+
url: "https://developers.openai.com/api/reference/resources/responses/methods/create",
|
|
910
|
+
fact: "mcp tool fields: headers map, defer_loading, tunnel_id, connector_id enum, authorization text, allowed_tools filter with read_only matching readOnlyHint, require_approval"
|
|
911
|
+
},
|
|
912
|
+
{
|
|
913
|
+
url: "https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create",
|
|
914
|
+
fact: "Function name: a-z, A-Z, 0-9, underscores and dashes, max length 64"
|
|
915
|
+
},
|
|
916
|
+
{
|
|
917
|
+
url: "https://developers.openai.com/api/docs/guides/function-calling",
|
|
918
|
+
fact: "strict mode requirements; rejected on violation; <20 functions soft guidance; tool_search only gpt-5.4+; defer_loading/namespaces"
|
|
919
|
+
},
|
|
920
|
+
{
|
|
921
|
+
url: "https://developers.openai.com/api/docs/guides/structured-outputs",
|
|
922
|
+
fact: "Supported types/properties/format list; unsupported composition keywords; size/nesting/enum limits; $defs and recursion supported; error on unsupported schema"
|
|
923
|
+
}
|
|
924
|
+
]
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
id: "codex",
|
|
928
|
+
name: "Codex",
|
|
929
|
+
kind: "cli",
|
|
930
|
+
transports: {
|
|
931
|
+
stdio: "yes",
|
|
932
|
+
streamableHttp: "yes",
|
|
933
|
+
sse: "no"
|
|
934
|
+
},
|
|
935
|
+
auth: {
|
|
936
|
+
oauth: "yes",
|
|
937
|
+
dcr: "yes",
|
|
938
|
+
cimd: "yes",
|
|
939
|
+
preRegistered: "partial",
|
|
940
|
+
staticHeaders: "yes",
|
|
941
|
+
redirectUris: ["http://127.0.0.1:{port}/callback"],
|
|
942
|
+
note: "Authorization servers that advertise issuer identification must return a matching iss (RFC 9207) or Codex rejects the response.",
|
|
943
|
+
strict: { issValidated: true }
|
|
944
|
+
},
|
|
945
|
+
toolNaming: {
|
|
946
|
+
format: "mcp__{server}__{tool}",
|
|
947
|
+
replacedChars: "[^A-Za-z0-9_]",
|
|
948
|
+
maxLength: 128,
|
|
949
|
+
onOverflow: "truncate_hash"
|
|
950
|
+
},
|
|
951
|
+
toolLimits: {
|
|
952
|
+
maxTools: null,
|
|
953
|
+
scope: null,
|
|
954
|
+
toolSearch: "partial"
|
|
955
|
+
},
|
|
956
|
+
schema: {
|
|
957
|
+
rootCombinators: "unknown",
|
|
958
|
+
rejectedKeywords: [],
|
|
959
|
+
typeArrays: "unknown",
|
|
960
|
+
onInvalid: "unknown"
|
|
961
|
+
},
|
|
962
|
+
protocol: {
|
|
963
|
+
revision: "2025-11-25",
|
|
964
|
+
resources: "yes",
|
|
965
|
+
prompts: "unknown",
|
|
966
|
+
sampling: "no",
|
|
967
|
+
elicitation: "partial"
|
|
968
|
+
},
|
|
969
|
+
config: {
|
|
970
|
+
remote: {
|
|
971
|
+
file: "~/.codex/config.toml or .codex/config.toml",
|
|
972
|
+
containerKey: "mcp_servers",
|
|
973
|
+
typeKey: null,
|
|
974
|
+
typeValue: null,
|
|
975
|
+
urlKey: "url",
|
|
976
|
+
headersKey: "http_headers"
|
|
977
|
+
},
|
|
978
|
+
addCommand: "codex mcp add {name} --url {url}",
|
|
979
|
+
deeplink: null
|
|
980
|
+
},
|
|
981
|
+
availability: null,
|
|
982
|
+
verifiedAt: "2026-09-15",
|
|
983
|
+
sources: [
|
|
984
|
+
{
|
|
985
|
+
url: "https://learn.chatgpt.com/docs/extend/mcp?surface=cli",
|
|
986
|
+
fact: "STDIO + Streamable HTTP; config paths; shared by desktop app/CLI/IDE; config keys; bearer, OAuth CIMD/DCR, ChatGPT session auth; codex mcp add/login/list"
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
url: "https://github.com/openai/codex/blob/main/codex-rs/codex-mcp/src/tools.rs",
|
|
990
|
+
fact: "Source code: mcp__ prefix, __ delimiter, MAX_TOOL_NAME_LENGTH 128, SHA-1 12-hex suffix on collision/overflow, duplicate skip"
|
|
991
|
+
},
|
|
992
|
+
{
|
|
993
|
+
url: "https://github.com/openai/codex/blob/main/codex-rs/codex-mcp/src/mcp/mod.rs",
|
|
994
|
+
fact: "Source code: sanitize_responses_api_tool_name keeps only ASCII alphanumeric and _"
|
|
995
|
+
},
|
|
996
|
+
{
|
|
997
|
+
url: "https://github.com/openai/codex/blob/main/codex-rs/core/config.schema.json",
|
|
998
|
+
fact: "Source code: RawMcpServerConfig keys; McpServerAuth oauth/chatgpt/ema_auth; mcp_oauth_callback_port/url; credentials store; tool_search flags; non_prefixed_mcp_tool_names; omit_tools_from surfaces"
|
|
999
|
+
},
|
|
1000
|
+
{
|
|
1001
|
+
url: "https://github.com/openai/codex/blob/main/codex-rs/cli/src/mcp_cmd.rs",
|
|
1002
|
+
fact: "Source code: codex mcp add usage and flags incl. --oauth-client-registration AUTO|CIMD|DCR"
|
|
1003
|
+
},
|
|
1004
|
+
{
|
|
1005
|
+
url: "https://github.com/openai/codex/blob/main/codex-rs/rmcp-client/src/oauth_callback.rs",
|
|
1006
|
+
fact: "Source code: default callback http://127.0.0.1/callback; cites MCP 2026-07-28 authorization response validation"
|
|
1007
|
+
},
|
|
1008
|
+
{
|
|
1009
|
+
url: "https://github.com/openai/codex/blob/main/codex-rs/rmcp-client/src/rmcp_client.rs",
|
|
1010
|
+
fact: "Source code: Streamable HTTP transports only; elicitation service; no sampling handler"
|
|
1011
|
+
},
|
|
1012
|
+
{
|
|
1013
|
+
url: "https://github.com/openai/codex/blob/main/codex-rs/Cargo.toml",
|
|
1014
|
+
fact: "Source code: rmcp = =3.2.0"
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
url: "https://github.com/modelcontextprotocol/rust-sdk/blob/rmcp-v3.2.0/crates/rmcp/src/model.rs",
|
|
1018
|
+
fact: "Source code: ProtocolVersion::LATEST = V_2025_11_25; V_2026_07_28 defined"
|
|
1019
|
+
},
|
|
1020
|
+
{
|
|
1021
|
+
url: "https://github.com/openai/codex/pull/17043",
|
|
1022
|
+
fact: "MCP elicitation for custom servers merged 2026-04-08"
|
|
1023
|
+
},
|
|
1024
|
+
{
|
|
1025
|
+
url: "https://github.com/openai/codex/issues/45621",
|
|
1026
|
+
fact: "app-server auto-declines downstream MCP elicitation (issue title; status not verified)"
|
|
1027
|
+
},
|
|
1028
|
+
{
|
|
1029
|
+
url: "https://github.com/openai/codex/blob/main/codex-rs/core/src/tools/handlers/mcp_resource.rs",
|
|
1030
|
+
fact: "Source code: list_mcp_resources / list_mcp_resource_templates / read_mcp_resource handlers"
|
|
1031
|
+
},
|
|
1032
|
+
{
|
|
1033
|
+
url: "https://developers.openai.com/apps-sdk/build/auth",
|
|
1034
|
+
fact: "ChatGPT and Codex reject missing/mismatched iss when issuer identification advertised"
|
|
1035
|
+
}
|
|
1036
|
+
]
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
id: "gemini-cli",
|
|
1040
|
+
name: "Gemini CLI",
|
|
1041
|
+
kind: "cli",
|
|
1042
|
+
transports: {
|
|
1043
|
+
stdio: "yes",
|
|
1044
|
+
streamableHttp: "yes",
|
|
1045
|
+
sse: "yes"
|
|
1046
|
+
},
|
|
1047
|
+
auth: {
|
|
1048
|
+
oauth: "yes",
|
|
1049
|
+
dcr: "yes",
|
|
1050
|
+
cimd: "unknown",
|
|
1051
|
+
preRegistered: "yes",
|
|
1052
|
+
staticHeaders: "yes",
|
|
1053
|
+
redirectUris: ["http://localhost:{port}/oauth/callback"],
|
|
1054
|
+
note: "Authorization servers must return an iss matching the issuer (RFC 9207) or Gemini CLI rejects the response.",
|
|
1055
|
+
strict: { issValidated: true }
|
|
1056
|
+
},
|
|
1057
|
+
toolNaming: {
|
|
1058
|
+
format: "mcp_{server}_{tool}",
|
|
1059
|
+
replacedChars: "[^A-Za-z0-9_.:-]",
|
|
1060
|
+
maxLength: 63,
|
|
1061
|
+
onOverflow: "truncate"
|
|
1062
|
+
},
|
|
1063
|
+
toolLimits: {
|
|
1064
|
+
maxTools: null,
|
|
1065
|
+
scope: null,
|
|
1066
|
+
toolSearch: "unknown"
|
|
1067
|
+
},
|
|
1068
|
+
schema: {
|
|
1069
|
+
rootCombinators: "unknown",
|
|
1070
|
+
rejectedKeywords: [],
|
|
1071
|
+
typeArrays: "unknown",
|
|
1072
|
+
onInvalid: "sanitize"
|
|
1073
|
+
},
|
|
1074
|
+
protocol: {
|
|
1075
|
+
revision: "2025-06-18",
|
|
1076
|
+
resources: "yes",
|
|
1077
|
+
prompts: "yes",
|
|
1078
|
+
sampling: "no",
|
|
1079
|
+
elicitation: "no"
|
|
1080
|
+
},
|
|
1081
|
+
config: {
|
|
1082
|
+
remote: {
|
|
1083
|
+
file: "~/.gemini/settings.json or .gemini/settings.json",
|
|
1084
|
+
containerKey: "mcpServers",
|
|
1085
|
+
typeKey: null,
|
|
1086
|
+
typeValue: null,
|
|
1087
|
+
urlKey: "httpUrl",
|
|
1088
|
+
headersKey: "headers"
|
|
1089
|
+
},
|
|
1090
|
+
addCommand: 'gemini mcp add --transport http --header "{header}" {name} {url}',
|
|
1091
|
+
deeplink: null
|
|
1092
|
+
},
|
|
1093
|
+
availability: null,
|
|
1094
|
+
verifiedAt: "2026-09-15",
|
|
1095
|
+
sources: [
|
|
1096
|
+
{
|
|
1097
|
+
url: "https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md",
|
|
1098
|
+
fact: "Source code: config keys, transports, env interpolation, mcp_{server}_{tool} FQN, char sanitization, 63-char truncation, underscore warning, last-registered-wins, schema sanitization, OAuth discovery/DCR, localhost callback, RFC 9207, token path, /mcp auth, prompts/resources, gemini mcp add/list/remove, extension merge"
|
|
1099
|
+
},
|
|
1100
|
+
{
|
|
1101
|
+
url: "https://github.com/google-gemini/gemini-cli/blob/main/packages/core/src/tools/mcp-tool.ts",
|
|
1102
|
+
fact: "Source code: MAX_FUNCTION_NAME_LENGTH 64; regex ^[a-zA-Z_][a-zA-Z0-9_\\-.:]{0,63}$; slice(0,30)+'...'+slice(-30); parametersJsonSchema = inputSchema; content block transforms"
|
|
1103
|
+
},
|
|
1104
|
+
{
|
|
1105
|
+
url: "https://github.com/google-gemini/gemini-cli/blob/main/packages/core/src/tools/mcp-client.ts",
|
|
1106
|
+
fact: "Source code: SSE + StreamableHTTP transports, SSE fallback, roots capability, list_changed handlers, readOnlyHint, includeTools/excludeTools, no sampling/elicitation handlers"
|
|
1107
|
+
},
|
|
1108
|
+
{
|
|
1109
|
+
url: "https://github.com/google-gemini/gemini-cli/blob/main/packages/core/src/mcp/oauth-provider.ts",
|
|
1110
|
+
fact: "Source code: DCR via registration_endpoint; no CIMD code found"
|
|
1111
|
+
},
|
|
1112
|
+
{
|
|
1113
|
+
url: "https://github.com/google-gemini/gemini-cli/blob/main/package.json",
|
|
1114
|
+
fact: "Source code: @modelcontextprotocol/sdk 1.23.0 (version 0.62.0-nightly.20260915)"
|
|
1115
|
+
},
|
|
1116
|
+
{
|
|
1117
|
+
url: "https://unpkg.com/@modelcontextprotocol/sdk@1.23.0/dist/esm/types.js",
|
|
1118
|
+
fact: "Source code: LATEST_PROTOCOL_VERSION = 2025-06-18"
|
|
1119
|
+
},
|
|
1120
|
+
{
|
|
1121
|
+
url: "https://github.com/google-gemini/gemini-cli/issues/22249",
|
|
1122
|
+
fact: "Elicitation unsupported, 'Method not found'; open, p2, opened 2026-03-13"
|
|
1123
|
+
},
|
|
1124
|
+
{
|
|
1125
|
+
url: "https://github.com/google-gemini/gemini-cli/issues/7056",
|
|
1126
|
+
fact: "MCP prompt workflow reported incomplete"
|
|
1127
|
+
},
|
|
1128
|
+
{
|
|
1129
|
+
url: "https://ai.google.dev/gemini-api/docs/function-calling",
|
|
1130
|
+
fact: "Only subset of OpenAPI schema supported; names without spaces/special chars; keep active set 10-20 tools"
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
url: "https://ai.google.dev/gemini-api/docs/generate-content/function-calling",
|
|
1134
|
+
fact: "API may reject very large or deeply nested schemas; reduce number of declarations"
|
|
1135
|
+
}
|
|
1136
|
+
]
|
|
1137
|
+
},
|
|
1138
|
+
{
|
|
1139
|
+
id: "cursor",
|
|
1140
|
+
name: "Cursor",
|
|
1141
|
+
kind: "ide",
|
|
1142
|
+
transports: {
|
|
1143
|
+
stdio: "yes",
|
|
1144
|
+
streamableHttp: "yes",
|
|
1145
|
+
sse: "yes"
|
|
1146
|
+
},
|
|
1147
|
+
auth: {
|
|
1148
|
+
oauth: "yes",
|
|
1149
|
+
dcr: "yes",
|
|
1150
|
+
cimd: "unknown",
|
|
1151
|
+
preRegistered: "yes",
|
|
1152
|
+
staticHeaders: "yes",
|
|
1153
|
+
redirectUris: [
|
|
1154
|
+
"https://www.cursor.com/agents/mcp/oauth/callback",
|
|
1155
|
+
"http://localhost:8787/callback"
|
|
1156
|
+
],
|
|
1157
|
+
note: null
|
|
1158
|
+
},
|
|
1159
|
+
toolNaming: {
|
|
1160
|
+
format: null,
|
|
1161
|
+
replacedChars: null,
|
|
1162
|
+
maxLength: 60,
|
|
1163
|
+
onOverflow: "unknown"
|
|
1164
|
+
},
|
|
1165
|
+
toolLimits: {
|
|
1166
|
+
maxTools: null,
|
|
1167
|
+
scope: null,
|
|
1168
|
+
toolSearch: "yes"
|
|
1169
|
+
},
|
|
1170
|
+
schema: {
|
|
1171
|
+
rootCombinators: "unknown",
|
|
1172
|
+
rejectedKeywords: [],
|
|
1173
|
+
typeArrays: "unknown",
|
|
1174
|
+
onInvalid: "unknown"
|
|
1175
|
+
},
|
|
1176
|
+
protocol: {
|
|
1177
|
+
revision: null,
|
|
1178
|
+
resources: "yes",
|
|
1179
|
+
prompts: "yes",
|
|
1180
|
+
sampling: "unknown",
|
|
1181
|
+
elicitation: "yes"
|
|
1182
|
+
},
|
|
1183
|
+
config: {
|
|
1184
|
+
remote: {
|
|
1185
|
+
file: "~/.cursor/mcp.json or .cursor/mcp.json",
|
|
1186
|
+
containerKey: "mcpServers",
|
|
1187
|
+
typeKey: null,
|
|
1188
|
+
typeValue: null,
|
|
1189
|
+
urlKey: "url",
|
|
1190
|
+
headersKey: "headers"
|
|
1191
|
+
},
|
|
1192
|
+
addCommand: null,
|
|
1193
|
+
deeplink: "cursor://anysphere.cursor-deeplink/mcp/install?name={name}&config={config}"
|
|
1194
|
+
},
|
|
1195
|
+
availability: "No plan gate for MCP; Enterprise admins can restrict servers with a team-dashboard allowlist of command and URL patterns.",
|
|
1196
|
+
verifiedAt: "2026-09-15",
|
|
1197
|
+
sources: [
|
|
1198
|
+
{
|
|
1199
|
+
url: "https://cursor.com/docs/context/mcp",
|
|
1200
|
+
fact: "Transports stdio/SSE/Streamable HTTP; config paths; mcpServers keys; interpolation vars; auth object (CLIENT_ID/CLIENT_SECRET/scopes) for providers without DCR; fixed redirect URLs; capability table Tools/Prompts/Resources/Roots/Elicitation/Apps supported; base64 images; Enterprise MCP allowlist"
|
|
1201
|
+
},
|
|
1202
|
+
{
|
|
1203
|
+
url: "https://cursor.com/docs/context/mcp/install-links",
|
|
1204
|
+
fact: "Deeplink format cursor://anysphere.cursor-deeplink/mcp/install?name=&config=<base64 JSON>"
|
|
1205
|
+
},
|
|
1206
|
+
{
|
|
1207
|
+
url: "https://cursor.com/docs/enterprise/model-and-integration-management",
|
|
1208
|
+
fact: "MCP Configuration allowlist is Enterprise only; server:tool patterns; command/URL matching; priority order dashboard > permissions.json > editor; allowlist does not distribute servers"
|
|
1209
|
+
},
|
|
1210
|
+
{
|
|
1211
|
+
url: "https://forum.cursor.com/t/google-gws-cli-tool-names-too-long/153918/4",
|
|
1212
|
+
fact: "Cursor staff (deanrie, 2026-03-07): combined server name + tool name can't be more than 60 characters; flagged to team"
|
|
1213
|
+
},
|
|
1214
|
+
{
|
|
1215
|
+
url: "https://cursor.com/blog/dynamic-context-discovery",
|
|
1216
|
+
fact: "MCP tool descriptions synced to folder, loaded on demand; 46.9% token reduction (2026-01-06)"
|
|
1217
|
+
},
|
|
1218
|
+
{
|
|
1219
|
+
url: "https://forum.cursor.com/t/tools-limited-to-40-total/67976",
|
|
1220
|
+
fact: "Corroboration only (community): historical 40-tool limit"
|
|
1221
|
+
},
|
|
1222
|
+
{
|
|
1223
|
+
url: "https://zuplo.com/learn/mcp/compatibility",
|
|
1224
|
+
fact: "Corroboration only (secondary)"
|
|
1225
|
+
}
|
|
1226
|
+
]
|
|
1227
|
+
},
|
|
1228
|
+
{
|
|
1229
|
+
id: "vscode",
|
|
1230
|
+
name: "VS Code (GitHub Copilot)",
|
|
1231
|
+
kind: "ide",
|
|
1232
|
+
transports: {
|
|
1233
|
+
stdio: "yes",
|
|
1234
|
+
streamableHttp: "yes",
|
|
1235
|
+
sse: "partial"
|
|
1236
|
+
},
|
|
1237
|
+
auth: {
|
|
1238
|
+
oauth: "yes",
|
|
1239
|
+
dcr: "yes",
|
|
1240
|
+
cimd: "yes",
|
|
1241
|
+
preRegistered: "yes",
|
|
1242
|
+
staticHeaders: "yes",
|
|
1243
|
+
redirectUris: ["http://127.0.0.1:33418", "https://vscode.dev/redirect"],
|
|
1244
|
+
note: null
|
|
1245
|
+
},
|
|
1246
|
+
toolNaming: {
|
|
1247
|
+
format: "mcp_{server}_{tool}",
|
|
1248
|
+
replacedChars: "[^A-Za-z0-9_-]",
|
|
1249
|
+
maxLength: 64,
|
|
1250
|
+
onOverflow: "truncate"
|
|
1251
|
+
},
|
|
1252
|
+
toolLimits: {
|
|
1253
|
+
maxTools: 128,
|
|
1254
|
+
scope: "per_request",
|
|
1255
|
+
toolSearch: "yes"
|
|
1256
|
+
},
|
|
1257
|
+
schema: {
|
|
1258
|
+
rootCombinators: "unknown",
|
|
1259
|
+
rejectedKeywords: [],
|
|
1260
|
+
typeArrays: "unknown",
|
|
1261
|
+
onInvalid: "drop_tool"
|
|
1262
|
+
},
|
|
1263
|
+
protocol: {
|
|
1264
|
+
revision: "2025-11-25",
|
|
1265
|
+
resources: "yes",
|
|
1266
|
+
prompts: "yes",
|
|
1267
|
+
sampling: "yes",
|
|
1268
|
+
elicitation: "yes"
|
|
1269
|
+
},
|
|
1270
|
+
config: {
|
|
1271
|
+
remote: {
|
|
1272
|
+
file: ".vscode/mcp.json",
|
|
1273
|
+
containerKey: "servers",
|
|
1274
|
+
typeKey: "type",
|
|
1275
|
+
typeValue: "http",
|
|
1276
|
+
urlKey: "url",
|
|
1277
|
+
headersKey: "headers"
|
|
1278
|
+
},
|
|
1279
|
+
addCommand: `code --add-mcp '{"name":"{name}","type":"http","url":"{url}"}'`,
|
|
1280
|
+
deeplink: "vscode:mcp/install?{config}"
|
|
1281
|
+
},
|
|
1282
|
+
availability: "Copilot Business/Enterprise orgs must enable the 'MCP servers in Copilot' policy, which is off by default.",
|
|
1283
|
+
verifiedAt: "2026-09-15",
|
|
1284
|
+
sources: [
|
|
1285
|
+
{
|
|
1286
|
+
url: "https://code.visualstudio.com/docs/copilot/customization/mcp-servers",
|
|
1287
|
+
fact: "servers key example, .vscode/mcp.json, ~/.copilot/mcp-config.json, code --add-mcp, resources/prompts/MCP Apps, GitHub policies"
|
|
1288
|
+
},
|
|
1289
|
+
{
|
|
1290
|
+
url: "https://code.visualstudio.com/docs/agents/reference/mcp-configuration",
|
|
1291
|
+
fact: "servers/inputs/sandbox; stdio fields incl cwd/env/envFile; http|sse with url/headers/oauth; oauth.clientId, oauth.enterpriseManaged; ${input:}, ${env:}, ${workspaceFolder}; input types; dev mode; chat.mcp.access/discovery/autostart/apps.enabled"
|
|
1292
|
+
},
|
|
1293
|
+
{
|
|
1294
|
+
url: "https://code.visualstudio.com/api/extension-guides/ai/mcp",
|
|
1295
|
+
fact: "Supported: tools, resources, prompts, elicitation, sampling, OAuth with DCR, roots, MCP Apps; annotations title/readOnlyHint; stdio/http/sse(legacy); redirect URLs 127.0.0.1:33418 and vscode.dev/redirect; vscode:mcp/install and vscode-insiders"
|
|
1296
|
+
},
|
|
1297
|
+
{
|
|
1298
|
+
url: "https://code.visualstudio.com/docs/copilot/agents/agent-tools",
|
|
1299
|
+
fact: "Max 128 tools per chat request; virtual tools via github.copilot.chat.virtualTools.threshold; tool sets"
|
|
1300
|
+
},
|
|
1301
|
+
{
|
|
1302
|
+
url: "https://code.visualstudio.com/updates/v1_103",
|
|
1303
|
+
fact: "Virtual tools grouping; 2025-06-18 spec incl. structured output and resource_link; client credentials (client ID/secret) flow when no DCR"
|
|
1304
|
+
},
|
|
1305
|
+
{
|
|
1306
|
+
url: "https://code.visualstudio.com/updates/v1_107",
|
|
1307
|
+
fact: "2025-11-25 spec: URL elicitation, tasks, enum elicitation, WWW-Authenticate scope consent, CIMD flow, icons"
|
|
1308
|
+
},
|
|
1309
|
+
{
|
|
1310
|
+
url: "https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/mcp/common/mcpServer.ts",
|
|
1311
|
+
fact: "Source code: toolInvalidCharRe /[^a-z0-9_-]/gi, invalid chars replaced with '_' + warning; draft-07 validation, invalid tools omitted; properties normalized; id = (prefix+name) dots->'_' sliced to MaxLength; prefix generator"
|
|
1312
|
+
},
|
|
1313
|
+
{
|
|
1314
|
+
url: "https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/mcp/common/mcpTypes.ts",
|
|
1315
|
+
fact: "Source code: McpToolName Prefix='mcp_', MaxPrefixLen=18, MaxLength=64"
|
|
1316
|
+
},
|
|
1317
|
+
{
|
|
1318
|
+
url: "https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/mcp/common/mcpServerRequestHandler.ts",
|
|
1319
|
+
fact: "Source code: initialize capabilities: roots listChanged, sampling, elicitation form+url, tasks, io.modelcontextprotocol/ui extension; protocolVersion LATEST_PROTOCOL_VERSION"
|
|
1320
|
+
},
|
|
1321
|
+
{
|
|
1322
|
+
url: "https://github.com/microsoft/vscode/blob/main/src/vs/platform/mcp/common/modelContextProtocol.ts",
|
|
1323
|
+
fact: 'Source code: LATEST_PROTOCOL_VERSION = "2025-11-25"'
|
|
1324
|
+
},
|
|
1325
|
+
{
|
|
1326
|
+
url: "https://github.com/microsoft/vscode/issues/243602",
|
|
1327
|
+
fact: "Maintainer (Connor Peet, 2025-03-14): CAPI/model limits tool names to 64; prefixing produced 400s; fixed in PR #243607"
|
|
1328
|
+
},
|
|
1329
|
+
{
|
|
1330
|
+
url: "https://github.com/microsoft/vscode/issues/308463",
|
|
1331
|
+
fact: "Warning text for dotted tool names (open, assigned sandy081, 2026-04-08)"
|
|
1332
|
+
},
|
|
1333
|
+
{
|
|
1334
|
+
url: "https://github.com/microsoft/vscode/issues/257415",
|
|
1335
|
+
fact: "Option to skip DCR / use static client info; closed via PR #317950, milestone 1.122.0"
|
|
1336
|
+
},
|
|
1337
|
+
{
|
|
1338
|
+
url: "https://github.com/microsoft/vscode/issues/320145",
|
|
1339
|
+
fact: "Tools without inputSchema sent to OpenAI-compatible endpoints omit parameters -> 400"
|
|
1340
|
+
},
|
|
1341
|
+
{
|
|
1342
|
+
url: "https://docs.github.com/en/copilot/concepts/context/mcp",
|
|
1343
|
+
fact: "MCP policy only applies to Copilot Business/Enterprise; disabled by default; IDEs VS Code, JetBrains, Visual Studio, Xcode, Eclipse"
|
|
1344
|
+
},
|
|
1345
|
+
{
|
|
1346
|
+
url: "https://docs.github.com/en/copilot/how-tos/administer-copilot/manage-mcp-usage/configure-mcp-server-access",
|
|
1347
|
+
fact: "Registry URL + access policy Allow all / Registry only; public preview; managed-settings.json recommended"
|
|
1348
|
+
}
|
|
1349
|
+
]
|
|
1350
|
+
},
|
|
1351
|
+
{
|
|
1352
|
+
id: "windsurf",
|
|
1353
|
+
name: "Windsurf (Devin Desktop)",
|
|
1354
|
+
kind: "ide",
|
|
1355
|
+
transports: {
|
|
1356
|
+
stdio: "yes",
|
|
1357
|
+
streamableHttp: "yes",
|
|
1358
|
+
sse: "yes"
|
|
1359
|
+
},
|
|
1360
|
+
auth: {
|
|
1361
|
+
oauth: "yes",
|
|
1362
|
+
dcr: "unknown",
|
|
1363
|
+
cimd: "unknown",
|
|
1364
|
+
preRegistered: "unknown",
|
|
1365
|
+
staticHeaders: "yes",
|
|
1366
|
+
redirectUris: [],
|
|
1367
|
+
note: null
|
|
1368
|
+
},
|
|
1369
|
+
toolNaming: {
|
|
1370
|
+
format: null,
|
|
1371
|
+
replacedChars: null,
|
|
1372
|
+
maxLength: null,
|
|
1373
|
+
onOverflow: "unknown"
|
|
1374
|
+
},
|
|
1375
|
+
toolLimits: {
|
|
1376
|
+
maxTools: 100,
|
|
1377
|
+
scope: "total",
|
|
1378
|
+
toolSearch: "unknown"
|
|
1379
|
+
},
|
|
1380
|
+
schema: {
|
|
1381
|
+
rootCombinators: "unknown",
|
|
1382
|
+
rejectedKeywords: [],
|
|
1383
|
+
typeArrays: "unknown",
|
|
1384
|
+
onInvalid: "unknown"
|
|
1385
|
+
},
|
|
1386
|
+
protocol: {
|
|
1387
|
+
revision: null,
|
|
1388
|
+
resources: "yes",
|
|
1389
|
+
prompts: "yes",
|
|
1390
|
+
sampling: "unknown",
|
|
1391
|
+
elicitation: "unknown"
|
|
1392
|
+
},
|
|
1393
|
+
config: {
|
|
1394
|
+
remote: {
|
|
1395
|
+
file: "~/.codeium/windsurf/mcp_config.json",
|
|
1396
|
+
containerKey: "mcpServers",
|
|
1397
|
+
typeKey: null,
|
|
1398
|
+
typeValue: null,
|
|
1399
|
+
urlKey: "serverUrl",
|
|
1400
|
+
headersKey: "headers"
|
|
1401
|
+
},
|
|
1402
|
+
addCommand: null,
|
|
1403
|
+
deeplink: null
|
|
1404
|
+
},
|
|
1405
|
+
availability: "Enterprise users must turn MCP on in settings, and once a Teams/Enterprise admin allowlists any server all others are blocked.",
|
|
1406
|
+
verifiedAt: "2026-09-15",
|
|
1407
|
+
sources: [
|
|
1408
|
+
{
|
|
1409
|
+
url: "https://docs.devin.ai/desktop/cascade/mcp",
|
|
1410
|
+
fact: "Redirect target of docs.windsurf.com/windsurf/cascade/mcp. Transports stdio/Streamable HTTP/SSE; OAuth for each transport; ~/.codeium/windsurf/mcp_config.json; mcpServers, serverUrl/url, headers, env; ${env:} and ${file:} interpolation; 100 tool limit; tools/resources/prompts supported; admin allowlist regex; enterprise manual enable; deeplink windsurf://windsurf-mcp-registry; legacy Cascade note"
|
|
1411
|
+
}
|
|
1412
|
+
]
|
|
1413
|
+
},
|
|
1414
|
+
{
|
|
1415
|
+
id: "zed",
|
|
1416
|
+
name: "Zed",
|
|
1417
|
+
kind: "ide",
|
|
1418
|
+
transports: {
|
|
1419
|
+
stdio: "yes",
|
|
1420
|
+
streamableHttp: "yes",
|
|
1421
|
+
sse: "no"
|
|
1422
|
+
},
|
|
1423
|
+
auth: {
|
|
1424
|
+
oauth: "yes",
|
|
1425
|
+
dcr: "yes",
|
|
1426
|
+
cimd: "yes",
|
|
1427
|
+
preRegistered: "unknown",
|
|
1428
|
+
staticHeaders: "yes",
|
|
1429
|
+
redirectUris: [],
|
|
1430
|
+
note: "OAuth starts only when no Authorization header is configured, and its loopback callback prefers port 27523 (exact redirect URI undocumented)."
|
|
1431
|
+
},
|
|
1432
|
+
toolNaming: {
|
|
1433
|
+
format: "{tool}",
|
|
1434
|
+
replacedChars: "[^A-Za-z0-9_-]",
|
|
1435
|
+
maxLength: 64,
|
|
1436
|
+
onOverflow: "truncate"
|
|
1437
|
+
},
|
|
1438
|
+
toolLimits: {
|
|
1439
|
+
maxTools: null,
|
|
1440
|
+
scope: null,
|
|
1441
|
+
toolSearch: "unknown"
|
|
1442
|
+
},
|
|
1443
|
+
schema: {
|
|
1444
|
+
rootCombinators: "unknown",
|
|
1445
|
+
rejectedKeywords: [],
|
|
1446
|
+
typeArrays: "unknown",
|
|
1447
|
+
onInvalid: "sanitize"
|
|
1448
|
+
},
|
|
1449
|
+
protocol: {
|
|
1450
|
+
revision: "2025-11-25",
|
|
1451
|
+
resources: "no",
|
|
1452
|
+
prompts: "yes",
|
|
1453
|
+
sampling: "no",
|
|
1454
|
+
elicitation: "no"
|
|
1455
|
+
},
|
|
1456
|
+
config: {
|
|
1457
|
+
remote: {
|
|
1458
|
+
file: "~/.config/zed/settings.json or .zed/settings.json",
|
|
1459
|
+
containerKey: "context_servers",
|
|
1460
|
+
typeKey: null,
|
|
1461
|
+
typeValue: null,
|
|
1462
|
+
urlKey: "url",
|
|
1463
|
+
headersKey: "headers"
|
|
1464
|
+
},
|
|
1465
|
+
addCommand: null,
|
|
1466
|
+
deeplink: null
|
|
1467
|
+
},
|
|
1468
|
+
availability: null,
|
|
1469
|
+
verifiedAt: "2026-09-15",
|
|
1470
|
+
sources: [
|
|
1471
|
+
{
|
|
1472
|
+
url: "https://zed.dev/docs/ai/mcp",
|
|
1473
|
+
fact: "context_servers local (command/args/env) and remote (url/headers); OAuth prompt when no Authorization header; supports Tools and Prompts only; tools/list_changed; mcp:<server>:<tool_name> permission key; tool_permissions default"
|
|
1474
|
+
},
|
|
1475
|
+
{
|
|
1476
|
+
url: "https://zed.dev/docs/extensions/mcp-extensions",
|
|
1477
|
+
fact: "Extension context servers via extension.toml + context_server_command; remote servers added natively; extensions deprecated in favor of MCP registry"
|
|
1478
|
+
},
|
|
1479
|
+
{
|
|
1480
|
+
url: "https://zed.dev/docs/configuring-zed",
|
|
1481
|
+
fact: "Settings file paths"
|
|
1482
|
+
},
|
|
1483
|
+
{
|
|
1484
|
+
url: "https://github.com/zed-industries/zed/pull/51768",
|
|
1485
|
+
fact: "OAuth for remote Streamable HTTP servers: CIMD at zed.dev first, DCR fallback, Auth Code + PKCE, loopback port 27523, keychain; merged 2026-03-23"
|
|
1486
|
+
},
|
|
1487
|
+
{
|
|
1488
|
+
url: "https://github.com/zed-industries/zed/blob/main/crates/context_server/src/oauth.rs",
|
|
1489
|
+
fact: "Source code: CIMD_URL https://zed.dev/oauth/client-metadata.json; CIMD first, DCR fallback; PKCE S256"
|
|
1490
|
+
},
|
|
1491
|
+
{
|
|
1492
|
+
url: "https://github.com/zed-industries/zed/blob/main/crates/context_server/src/transport/http.rs",
|
|
1493
|
+
fact: "Source code: Streamable HTTP transport with SSE response streams, Mcp-Session-Id, MCP-Protocol-Version headers"
|
|
1494
|
+
},
|
|
1495
|
+
{
|
|
1496
|
+
url: "https://github.com/zed-industries/zed/blob/main/crates/context_server/src/protocol.rs",
|
|
1497
|
+
fact: "Source code: Supported protocol versions LATEST (2025-11-25), 2025-06-18, 2025-03-26, 2024-11-05; client capabilities sampling: None, roots: None"
|
|
1498
|
+
},
|
|
1499
|
+
{
|
|
1500
|
+
url: "https://github.com/zed-industries/zed/blob/main/crates/context_server/src/types.rs",
|
|
1501
|
+
fact: "Source code: LATEST_PROTOCOL_VERSION = 2025-11-25; ClientCapabilities has only experimental/sampling/roots (no elicitation)"
|
|
1502
|
+
},
|
|
1503
|
+
{
|
|
1504
|
+
url: "https://github.com/zed-industries/zed/blob/main/crates/agent/src/thread.rs",
|
|
1505
|
+
fact: "Source code: MAX_TOOL_NAME_LENGTH 64; provider_compatible_tool_name sanitizes to [A-Za-z0-9_-]; duplicate names prefixed with snake_case server id, else last one kept"
|
|
1506
|
+
},
|
|
1507
|
+
{
|
|
1508
|
+
url: "https://github.com/zed-industries/zed/blob/main/crates/agent/src/tools/context_server_registry.rs",
|
|
1509
|
+
fact: "Source code: Tool name = raw MCP name; empty/null schema -> object; normalize_tool_schema applied"
|
|
1510
|
+
},
|
|
1511
|
+
{
|
|
1512
|
+
url: "https://github.com/zed-industries/zed/blob/main/crates/language_model_core/src/tool_schema.rs",
|
|
1513
|
+
fact: "Source code: normalize_tool_schema removes root $schema/title/description, inlines #/$defs and #/definitions refs only"
|
|
1514
|
+
}
|
|
1515
|
+
]
|
|
1516
|
+
},
|
|
1517
|
+
{
|
|
1518
|
+
id: "jetbrains-ai",
|
|
1519
|
+
name: "JetBrains AI Assistant",
|
|
1520
|
+
kind: "ide",
|
|
1521
|
+
transports: {
|
|
1522
|
+
stdio: "yes",
|
|
1523
|
+
streamableHttp: "yes",
|
|
1524
|
+
sse: "partial"
|
|
1525
|
+
},
|
|
1526
|
+
auth: {
|
|
1527
|
+
oauth: "no",
|
|
1528
|
+
dcr: "no",
|
|
1529
|
+
cimd: "no",
|
|
1530
|
+
preRegistered: "no",
|
|
1531
|
+
staticHeaders: "partial",
|
|
1532
|
+
redirectUris: [],
|
|
1533
|
+
note: "There is no OAuth flow, so servers that answer 401 fail to connect; JetBrains staff suggest bridging through `npx -y mcp-remote <url>` over stdio."
|
|
1534
|
+
},
|
|
1535
|
+
toolNaming: {
|
|
1536
|
+
format: null,
|
|
1537
|
+
replacedChars: null,
|
|
1538
|
+
maxLength: null,
|
|
1539
|
+
onOverflow: "unknown"
|
|
1540
|
+
},
|
|
1541
|
+
toolLimits: {
|
|
1542
|
+
maxTools: null,
|
|
1543
|
+
scope: null,
|
|
1544
|
+
toolSearch: "unknown"
|
|
1545
|
+
},
|
|
1546
|
+
schema: {
|
|
1547
|
+
rootCombinators: "unknown",
|
|
1548
|
+
rejectedKeywords: [],
|
|
1549
|
+
typeArrays: "unknown",
|
|
1550
|
+
onInvalid: "unknown"
|
|
1551
|
+
},
|
|
1552
|
+
protocol: {
|
|
1553
|
+
revision: null,
|
|
1554
|
+
resources: "unknown",
|
|
1555
|
+
prompts: "unknown",
|
|
1556
|
+
sampling: "unknown",
|
|
1557
|
+
elicitation: "unknown"
|
|
1558
|
+
},
|
|
1559
|
+
config: {
|
|
1560
|
+
remote: null,
|
|
1561
|
+
addCommand: null,
|
|
1562
|
+
deeplink: null
|
|
1563
|
+
},
|
|
1564
|
+
availability: "Org admins can preconfigure servers and block custom ones; AI Free is unavailable in IntelliJ IDEA without Ultimate and PyCharm without Pro.",
|
|
1565
|
+
verifiedAt: "2026-09-15",
|
|
1566
|
+
sources: [
|
|
1567
|
+
{
|
|
1568
|
+
url: "https://www.jetbrains.com/help/ai-assistant/mcp.html",
|
|
1569
|
+
fact: "STDIO, Streamable HTTP, and legacy SSE transports; mcpServers JSON; Settings path; global/project level; import from Claude Desktop; admin preconfiguration/restriction; no mention of OAuth or headers"
|
|
1570
|
+
},
|
|
1571
|
+
{
|
|
1572
|
+
url: "https://youtrack.jetbrains.com/issue/LLM-24541",
|
|
1573
|
+
fact: "AI Assistant MCP gives 401 with no authentication link; state Open; staff (vladislav.kuzmeniuk) reproduced 2026-02-13 and suggested npx mcp-remote; description notes Junie MCP handles the same server correctly; user comment 2026-04-30 questions Authorization header support"
|
|
1574
|
+
},
|
|
1575
|
+
{
|
|
1576
|
+
url: "https://youtrack.jetbrains.com/issue/LLM-25012",
|
|
1577
|
+
fact: "Feature request 'OAuth2 Authentication for MCP Server Connections' created 2026-02-23, State Open, updated 2026-09-14, Available in: empty"
|
|
1578
|
+
},
|
|
1579
|
+
{
|
|
1580
|
+
url: "https://www.jetbrains.com/help/jetbrains-console/mcp-servers.html",
|
|
1581
|
+
fact: "Org-level MCP servers apply only to AI Assistant (and Air), not Central CLI agents; command vs remote HTTP servers; remote supports HTTP headers; must be added to AI policy; preview/EAP"
|
|
1582
|
+
},
|
|
1583
|
+
{
|
|
1584
|
+
url: "https://lp.jetbrains.com/ai-ides-faq/",
|
|
1585
|
+
fact: "AI Free not available in Android Studio, IntelliJ IDEA without Ultimate, PyCharm without Pro"
|
|
1586
|
+
},
|
|
1587
|
+
{
|
|
1588
|
+
url: "https://zuplo.com/learn/mcp/compatibility/clients/jetbrains-ai-assistant",
|
|
1589
|
+
fact: "Corroboration only (secondary): no OAuth browser flow in AI Assistant docs 2025.2-2026.2"
|
|
1590
|
+
}
|
|
1591
|
+
]
|
|
1592
|
+
},
|
|
1593
|
+
{
|
|
1594
|
+
id: "junie",
|
|
1595
|
+
name: "Junie",
|
|
1596
|
+
kind: "ide",
|
|
1597
|
+
transports: {
|
|
1598
|
+
stdio: "yes",
|
|
1599
|
+
streamableHttp: "unknown",
|
|
1600
|
+
sse: "unknown"
|
|
1601
|
+
},
|
|
1602
|
+
auth: {
|
|
1603
|
+
oauth: "yes",
|
|
1604
|
+
dcr: "unknown",
|
|
1605
|
+
cimd: "unknown",
|
|
1606
|
+
preRegistered: "unknown",
|
|
1607
|
+
staticHeaders: "partial",
|
|
1608
|
+
redirectUris: [],
|
|
1609
|
+
note: "The IDE plugin docs say security tokens in MCP configs are unsupported, while the Junie CLI docs document a headers key."
|
|
1610
|
+
},
|
|
1611
|
+
toolNaming: {
|
|
1612
|
+
format: null,
|
|
1613
|
+
replacedChars: null,
|
|
1614
|
+
maxLength: null,
|
|
1615
|
+
onOverflow: "unknown"
|
|
1616
|
+
},
|
|
1617
|
+
toolLimits: {
|
|
1618
|
+
maxTools: 100,
|
|
1619
|
+
scope: "total",
|
|
1620
|
+
toolSearch: "unknown"
|
|
1621
|
+
},
|
|
1622
|
+
schema: {
|
|
1623
|
+
rootCombinators: "unknown",
|
|
1624
|
+
rejectedKeywords: [],
|
|
1625
|
+
typeArrays: "unknown",
|
|
1626
|
+
onInvalid: "unknown"
|
|
1627
|
+
},
|
|
1628
|
+
protocol: {
|
|
1629
|
+
revision: null,
|
|
1630
|
+
resources: "unknown",
|
|
1631
|
+
prompts: "unknown",
|
|
1632
|
+
sampling: "unknown",
|
|
1633
|
+
elicitation: "unknown"
|
|
1634
|
+
},
|
|
1635
|
+
config: {
|
|
1636
|
+
remote: {
|
|
1637
|
+
file: "~/.junie/mcp/mcp.json or .junie/mcp/mcp.json",
|
|
1638
|
+
containerKey: "mcpServers",
|
|
1639
|
+
typeKey: null,
|
|
1640
|
+
typeValue: null,
|
|
1641
|
+
urlKey: "url",
|
|
1642
|
+
headersKey: "headers"
|
|
1643
|
+
},
|
|
1644
|
+
addCommand: null,
|
|
1645
|
+
deeplink: null
|
|
1646
|
+
},
|
|
1647
|
+
availability: "Requires a JetBrains AI license (a 30-day AI Pro trial is available).",
|
|
1648
|
+
verifiedAt: "2026-09-15",
|
|
1649
|
+
sources: [
|
|
1650
|
+
{
|
|
1651
|
+
url: "https://junie.jetbrains.com/docs/junie-cli-mcp-configuration.html",
|
|
1652
|
+
fact: "Project .junie/mcp/mcp.json and user ~/.junie/mcp/mcp.json; mcpServers with command/args/env/url/headers; --mcp-location, --mcp-default-locations; /mcp command; 'Authorization required' status with browser OAuth via MCP Installation Assistant"
|
|
1653
|
+
},
|
|
1654
|
+
{
|
|
1655
|
+
url: "https://junie.jetbrains.com/docs/junie-plugin-mcp-settings.html",
|
|
1656
|
+
fact: "mcp.json with mcpServers; global or project level; IDE settings UI lists servers and their tools"
|
|
1657
|
+
},
|
|
1658
|
+
{
|
|
1659
|
+
url: "https://junie.jetbrains.com/docs/junie-ide-plugin.html",
|
|
1660
|
+
fact: "'Junie supports a maximum of 100 tools from all configured MCP servers'; 'At the moment, Junie does not support security tokens in MCP configs.' (env-file workaround); license/trial; IDE version minimums"
|
|
1661
|
+
},
|
|
1662
|
+
{
|
|
1663
|
+
url: "https://youtrack.jetbrains.com/issue/JUNIE-711",
|
|
1664
|
+
fact: "Server name with spaces produced provider error 'Invalid tools[12].name ... pattern ^[a-zA-Z0-9_-]+$'; created 2025-09-02; resolved as Duplicate of JUNIE-538"
|
|
1665
|
+
},
|
|
1666
|
+
{
|
|
1667
|
+
url: "https://youtrack.jetbrains.com/issue/LLM-24541",
|
|
1668
|
+
fact: "Issue text: 'In the Junie MCP (Settings | Tools | Junie | MCP Settings) everything works as expected' for an HTTP server needing auth"
|
|
1669
|
+
},
|
|
1670
|
+
{
|
|
1671
|
+
url: "https://www.jetbrains.com/help/jetbrains-console/mcp-servers.html",
|
|
1672
|
+
fact: "Org-level MCP servers apply only to AI Assistant (and Air)"
|
|
1673
|
+
}
|
|
1674
|
+
]
|
|
1675
|
+
},
|
|
1676
|
+
{
|
|
1677
|
+
id: "cline",
|
|
1678
|
+
name: "Cline",
|
|
1679
|
+
kind: "ide",
|
|
1680
|
+
transports: {
|
|
1681
|
+
stdio: "yes",
|
|
1682
|
+
streamableHttp: "yes",
|
|
1683
|
+
sse: "partial"
|
|
1684
|
+
},
|
|
1685
|
+
auth: {
|
|
1686
|
+
oauth: "partial",
|
|
1687
|
+
dcr: "yes",
|
|
1688
|
+
cimd: "no",
|
|
1689
|
+
preRegistered: "yes",
|
|
1690
|
+
staticHeaders: "yes",
|
|
1691
|
+
redirectUris: [
|
|
1692
|
+
"http://127.0.0.1:1456/mcp/oauth/callback",
|
|
1693
|
+
"http://127.0.0.1:1457/mcp/oauth/callback",
|
|
1694
|
+
"http://127.0.0.1:1458/mcp/oauth/callback",
|
|
1695
|
+
"http://127.0.0.1:1459/mcp/oauth/callback",
|
|
1696
|
+
"http://127.0.0.1:1460/mcp/oauth/callback",
|
|
1697
|
+
"http://127.0.0.1:1461/mcp/oauth/callback"
|
|
1698
|
+
],
|
|
1699
|
+
note: "Re-authorization after token expiry can fail with 'Invalid OAuth state' (open issue #7964)."
|
|
1700
|
+
},
|
|
1701
|
+
toolNaming: {
|
|
1702
|
+
format: "{server}__{tool}",
|
|
1703
|
+
replacedChars: "[^A-Za-z0-9_-]",
|
|
1704
|
+
maxLength: 64,
|
|
1705
|
+
onOverflow: "truncate_hash"
|
|
1706
|
+
},
|
|
1707
|
+
toolLimits: {
|
|
1708
|
+
maxTools: null,
|
|
1709
|
+
scope: null,
|
|
1710
|
+
toolSearch: "unknown"
|
|
1711
|
+
},
|
|
1712
|
+
schema: {
|
|
1713
|
+
rootCombinators: "unknown",
|
|
1714
|
+
rejectedKeywords: [],
|
|
1715
|
+
typeArrays: "unknown",
|
|
1716
|
+
onInvalid: "unknown"
|
|
1717
|
+
},
|
|
1718
|
+
protocol: {
|
|
1719
|
+
revision: null,
|
|
1720
|
+
resources: "yes",
|
|
1721
|
+
prompts: "yes",
|
|
1722
|
+
sampling: "no",
|
|
1723
|
+
elicitation: "no"
|
|
1724
|
+
},
|
|
1725
|
+
config: {
|
|
1726
|
+
remote: {
|
|
1727
|
+
file: "~/.cline/data/settings/cline_mcp_settings.json",
|
|
1728
|
+
containerKey: "mcpServers",
|
|
1729
|
+
typeKey: "type",
|
|
1730
|
+
typeValue: "streamableHttp",
|
|
1731
|
+
urlKey: "url",
|
|
1732
|
+
headersKey: "headers"
|
|
1733
|
+
},
|
|
1734
|
+
addCommand: null,
|
|
1735
|
+
deeplink: null
|
|
1736
|
+
},
|
|
1737
|
+
availability: "Enterprise remote config can restrict servers to an allowlist and block personal remote servers.",
|
|
1738
|
+
verifiedAt: "2026-09-15",
|
|
1739
|
+
sources: [
|
|
1740
|
+
{
|
|
1741
|
+
url: "https://docs.cline.bot/mcp/mcp-overview",
|
|
1742
|
+
fact: "STDIO, streamableHttp (recommended), sse (legacy); CLI ~/.cline/mcp.json; `cline mcp` management; mcpServers/type/url/headers/disabled/autoApprove keys"
|
|
1743
|
+
},
|
|
1744
|
+
{
|
|
1745
|
+
url: "https://docs.cline.bot/mcp/connecting-to-a-remote-server",
|
|
1746
|
+
fact: 'Remote headers {"Authorization": "Bearer your-token"}; untyped url defaults to legacy sse for backward compatibility'
|
|
1747
|
+
},
|
|
1748
|
+
{
|
|
1749
|
+
url: "https://github.com/cline/cline/blob/82b8e1fa0471f728e3d9540fd19aa063e2049497/apps/vscode/src/services/mcp/schemas.ts",
|
|
1750
|
+
fact: "Source code: zod schema: type literals stdio/sse/streamableHttp; sse arm precedes streamableHttp, so an untyped url defaults to sse; legacy transportType 'http' maps to streamableHttp; headers, autoApprove, disabled, timeout; nested CLI transport form"
|
|
1751
|
+
},
|
|
1752
|
+
{
|
|
1753
|
+
url: "https://github.com/cline/cline/blob/82b8e1fa0471f728e3d9540fd19aa063e2049497/sdk/packages/core/src/extensions/mcp/name-transform.ts",
|
|
1754
|
+
fact: "Source code: serverName__toolName; invalid chars [^a-zA-Z0-9_-]+ replaced with '_'; MAX 64; overflow/sanitized names become 55-char base + '_' + 8-hex sha1"
|
|
1755
|
+
},
|
|
1756
|
+
{
|
|
1757
|
+
url: "https://github.com/cline/cline/blob/82b8e1fa0471f728e3d9540fd19aa063e2049497/sdk/packages/core/src/extensions/mcp/tools.ts",
|
|
1758
|
+
fact: "Source code: createMcpTools passes descriptor.inputSchema unchanged"
|
|
1759
|
+
},
|
|
1760
|
+
{
|
|
1761
|
+
url: "https://github.com/cline/cline/blob/82b8e1fa0471f728e3d9540fd19aa063e2049497/apps/vscode/src/sdk/message-translator.ts",
|
|
1762
|
+
fact: "Source code: parseMcpToolName splits on the first '__'"
|
|
1763
|
+
},
|
|
1764
|
+
{
|
|
1765
|
+
url: "https://github.com/cline/cline/blob/82b8e1fa0471f728e3d9540fd19aa063e2049497/apps/vscode/src/services/mcp/McpHub.ts",
|
|
1766
|
+
fact: "Source code: Client capabilities {}; Stdio/SSE/StreamableHTTP transports; resources/list, resources/templates/list, prompts/list; ${env:VAR_NAME} expansion in URLs, headers, env"
|
|
1767
|
+
},
|
|
1768
|
+
{
|
|
1769
|
+
url: "https://github.com/cline/cline/blob/82b8e1fa0471f728e3d9540fd19aa063e2049497/apps/vscode/src/services/mcp/McpOAuthManager.ts",
|
|
1770
|
+
fact: "Source code: OAuth state in ~/.cline/data/settings/cline_mcp_settings.json; redirect http://127.0.0.1:1456/mcp/oauth/callback; ports 1456-1461; client_name 'Cline'; code verifier storage"
|
|
1771
|
+
},
|
|
1772
|
+
{
|
|
1773
|
+
url: "https://github.com/cline/cline/blob/82b8e1fa0471f728e3d9540fd19aa063e2049497/sdk/packages/core/src/extensions/mcp/oauth.ts",
|
|
1774
|
+
fact: "Source code: createMcpOAuthClientInformation maps oauthClient.clientId/clientSecret to pre-registered client; saveClientInformation (DCR persistence); token_endpoint_auth_method none; no clientMetadataUrl"
|
|
1775
|
+
},
|
|
1776
|
+
{
|
|
1777
|
+
url: "https://github.com/cline/cline/blob/82b8e1fa0471f728e3d9540fd19aa063e2049497/sdk/packages/core/src/extensions/mcp/config-loader.ts",
|
|
1778
|
+
fact: "Source code: oauthClientSchema {clientId, clientSecret?} under per-server oauthClient; transport schemas stdio/sse/streamableHttp"
|
|
1779
|
+
},
|
|
1780
|
+
{
|
|
1781
|
+
url: "https://github.com/cline/cline/blob/82b8e1fa0471f728e3d9540fd19aa063e2049497/apps/vscode/webview-ui/src/components/mcp/configuration/tabs/installed/server-row/ServerRow.tsx",
|
|
1782
|
+
fact: "Source code: 'Authenticate' button shown when oauthRequired and unauthenticated"
|
|
1783
|
+
},
|
|
1784
|
+
{
|
|
1785
|
+
url: "https://github.com/cline/cline/blob/82b8e1fa0471f728e3d9540fd19aa063e2049497/sdk/packages/shared/src/mcp.ts",
|
|
1786
|
+
fact: "Source code: DEFAULT_MCP_TIMEOUT_SECONDS=60, MIN=1, MAX=3600"
|
|
1787
|
+
},
|
|
1788
|
+
{
|
|
1789
|
+
url: "https://github.com/cline/cline/issues/7964",
|
|
1790
|
+
fact: "Remote MCP re-auth fails ('Invalid OAuth state - possible CSRF attack'); opened 2025-12-07; open"
|
|
1791
|
+
},
|
|
1792
|
+
{
|
|
1793
|
+
url: "https://docs.cline.bot/enterprise-solutions/configuration/infrastructure-configuration/control-other-cline-features/mcp-server-controls",
|
|
1794
|
+
fact: "mcpMarketplaceEnabled, allowedMCPServers, remoteMCPServers (alwaysEnabled), blockPersonalRemoteMCPServers; header example Bearer ${AUTH_TOKEN}"
|
|
1795
|
+
}
|
|
1796
|
+
]
|
|
1797
|
+
},
|
|
1798
|
+
{
|
|
1799
|
+
id: "continue",
|
|
1800
|
+
name: "Continue",
|
|
1801
|
+
kind: "ide",
|
|
1802
|
+
transports: {
|
|
1803
|
+
stdio: "yes",
|
|
1804
|
+
streamableHttp: "yes",
|
|
1805
|
+
sse: "yes"
|
|
1806
|
+
},
|
|
1807
|
+
auth: {
|
|
1808
|
+
oauth: "yes",
|
|
1809
|
+
dcr: "yes",
|
|
1810
|
+
cimd: "no",
|
|
1811
|
+
preRegistered: "unknown",
|
|
1812
|
+
staticHeaders: "yes",
|
|
1813
|
+
redirectUris: ["http://localhost:3000"],
|
|
1814
|
+
note: "In remote VS Code environments the redirect is the getExternalUri equivalent of http://localhost:3000."
|
|
1815
|
+
},
|
|
1816
|
+
toolNaming: {
|
|
1817
|
+
format: "{server}_{tool}",
|
|
1818
|
+
replacedChars: null,
|
|
1819
|
+
maxLength: null,
|
|
1820
|
+
onOverflow: "unknown"
|
|
1821
|
+
},
|
|
1822
|
+
toolLimits: {
|
|
1823
|
+
maxTools: null,
|
|
1824
|
+
scope: null,
|
|
1825
|
+
toolSearch: "unknown"
|
|
1826
|
+
},
|
|
1827
|
+
schema: {
|
|
1828
|
+
rootCombinators: "unknown",
|
|
1829
|
+
rejectedKeywords: ["$ref"],
|
|
1830
|
+
typeArrays: "partial",
|
|
1831
|
+
onInvalid: "unknown"
|
|
1832
|
+
},
|
|
1833
|
+
protocol: {
|
|
1834
|
+
revision: null,
|
|
1835
|
+
resources: "yes",
|
|
1836
|
+
prompts: "yes",
|
|
1837
|
+
sampling: "no",
|
|
1838
|
+
elicitation: "no"
|
|
1839
|
+
},
|
|
1840
|
+
config: {
|
|
1841
|
+
remote: {
|
|
1842
|
+
file: ".continue/mcpServers/*.json",
|
|
1843
|
+
containerKey: "mcpServers",
|
|
1844
|
+
typeKey: "type",
|
|
1845
|
+
typeValue: "http",
|
|
1846
|
+
urlKey: "url",
|
|
1847
|
+
headersKey: "headers"
|
|
1848
|
+
},
|
|
1849
|
+
addCommand: null,
|
|
1850
|
+
deeplink: null
|
|
1851
|
+
},
|
|
1852
|
+
availability: "MCP works only in agent mode, not chat or autocomplete.",
|
|
1853
|
+
verifiedAt: "2026-09-15",
|
|
1854
|
+
sources: [
|
|
1855
|
+
{
|
|
1856
|
+
url: "https://docs.continue.dev/customize/deep-dives/mcp",
|
|
1857
|
+
fact: "mcpServers in config.yaml; .continue/mcpServers/; stdio/sse/streamable-http; ${{ secrets.X }}; 'MCP can only be used in the agent mode'; copy Claude Desktop/Cursor/Cline JSON"
|
|
1858
|
+
},
|
|
1859
|
+
{
|
|
1860
|
+
url: "https://docs.continue.dev/customize/deep-dives/mcp-examples",
|
|
1861
|
+
fact: "YAML examples with type sse, streamable-http; apiKey: ${{ secrets.LINEAR_OAUTH_TOKEN }}"
|
|
1862
|
+
},
|
|
1863
|
+
{
|
|
1864
|
+
url: "https://docs.continue.dev/reference",
|
|
1865
|
+
fact: "mcpServers properties name, command, args, env, cwd, requestOptions (sse/streamable-http), connectionTimeout"
|
|
1866
|
+
},
|
|
1867
|
+
{
|
|
1868
|
+
url: "https://github.com/continuedev/continue/blob/5522c6f44ca0ac3528b37244818fbfa39b5af470/packages/config-yaml/src/schemas/mcp/index.ts",
|
|
1869
|
+
fact: "Source code: zod: stdio {command,args,env,cwd}; sse|streamable-http {url, apiKey, requestOptions}; connectionTimeout"
|
|
1870
|
+
},
|
|
1871
|
+
{
|
|
1872
|
+
url: "https://github.com/continuedev/continue/blob/5522c6f44ca0ac3528b37244818fbfa39b5af470/packages/config-yaml/src/schemas/mcp/convertJson.ts",
|
|
1873
|
+
fact: "Source code: JSON type 'http' \u2192 'streamable-http', any other type \u2192 'sse'; JSON headers map to requestOptions.headers"
|
|
1874
|
+
},
|
|
1875
|
+
{
|
|
1876
|
+
url: "https://github.com/continuedev/continue/blob/5522c6f44ca0ac3528b37244818fbfa39b5af470/core/tools/mcpToolName.ts",
|
|
1877
|
+
fact: "Source code: Tool name = sanitized lowercase server prefix + '_' + tool name, unless tool already starts with the prefix; no length cap"
|
|
1878
|
+
},
|
|
1879
|
+
{
|
|
1880
|
+
url: "https://github.com/continuedev/continue/blob/5522c6f44ca0ac3528b37244818fbfa39b5af470/core/context/mcp/MCPConnection.ts",
|
|
1881
|
+
fact: "Source code: Stdio/SSE/StreamableHTTP/WebSocket transports; capabilities {}; DEFAULT_MCP_TIMEOUT 20s; apiKey merged as Bearer header; listResources/listResourceTemplates/listPrompts"
|
|
1882
|
+
},
|
|
1883
|
+
{
|
|
1884
|
+
url: "https://github.com/continuedev/continue/blob/5522c6f44ca0ac3528b37244818fbfa39b5af470/core/context/mcp/MCPOauth.ts",
|
|
1885
|
+
fact: "Source code: OAuth via SDK auth(); redirect http://localhost:3000 or getExternalUri; client metadata token_endpoint_auth_method none, client_name 'Continue Dev, Inc'; clientInformation/saveClientInformation (DCR persistence); no clientMetadataUrl"
|
|
1886
|
+
},
|
|
1887
|
+
{
|
|
1888
|
+
url: "https://github.com/continuedev/continue/blob/5522c6f44ca0ac3528b37244818fbfa39b5af470/core/llm/llms/gemini-types.ts",
|
|
1889
|
+
fact: "Source code: convertJsonSchemaToGeminiSchema throws when a node lacks a string type; copies only whitelisted keys; format unsupported (TODO)"
|
|
1890
|
+
},
|
|
1891
|
+
{
|
|
1892
|
+
url: "https://github.com/continuedev/continue/blob/5522c6f44ca0ac3528b37244818fbfa39b5af470/core/context/providers/MCPContextProvider.ts",
|
|
1893
|
+
fact: "Source code: MCP resources exposed as the 'MCP Resources' context provider"
|
|
1894
|
+
},
|
|
1895
|
+
{
|
|
1896
|
+
url: "https://github.com/continuedev/continue/blob/5522c6f44ca0ac3528b37244818fbfa39b5af470/core/commands/slash/mcpSlashCommand.ts",
|
|
1897
|
+
fact: "Source code: MCP prompts exposed as slash commands (source 'mcp-prompt')"
|
|
1898
|
+
}
|
|
1899
|
+
]
|
|
1900
|
+
},
|
|
1901
|
+
{
|
|
1902
|
+
id: "goose",
|
|
1903
|
+
name: "Goose",
|
|
1904
|
+
kind: "cli",
|
|
1905
|
+
transports: {
|
|
1906
|
+
stdio: "yes",
|
|
1907
|
+
streamableHttp: "yes",
|
|
1908
|
+
sse: "no"
|
|
1909
|
+
},
|
|
1910
|
+
auth: {
|
|
1911
|
+
oauth: "yes",
|
|
1912
|
+
dcr: "yes",
|
|
1913
|
+
cimd: "yes",
|
|
1914
|
+
preRegistered: "yes",
|
|
1915
|
+
staticHeaders: "yes",
|
|
1916
|
+
redirectUris: [],
|
|
1917
|
+
note: null
|
|
1918
|
+
},
|
|
1919
|
+
toolNaming: {
|
|
1920
|
+
format: "{server}__{tool}",
|
|
1921
|
+
replacedChars: null,
|
|
1922
|
+
maxLength: null,
|
|
1923
|
+
onOverflow: "unknown"
|
|
1924
|
+
},
|
|
1925
|
+
toolLimits: {
|
|
1926
|
+
maxTools: null,
|
|
1927
|
+
scope: null,
|
|
1928
|
+
toolSearch: "unknown"
|
|
1929
|
+
},
|
|
1930
|
+
schema: {
|
|
1931
|
+
rootCombinators: "unknown",
|
|
1932
|
+
rejectedKeywords: [],
|
|
1933
|
+
typeArrays: "unknown",
|
|
1934
|
+
onInvalid: "unknown"
|
|
1935
|
+
},
|
|
1936
|
+
protocol: {
|
|
1937
|
+
revision: "2026-07-28",
|
|
1938
|
+
resources: "yes",
|
|
1939
|
+
prompts: "yes",
|
|
1940
|
+
sampling: "yes",
|
|
1941
|
+
elicitation: "yes"
|
|
1942
|
+
},
|
|
1943
|
+
config: {
|
|
1944
|
+
remote: {
|
|
1945
|
+
file: "~/.config/goose/config.yaml",
|
|
1946
|
+
containerKey: "extensions",
|
|
1947
|
+
typeKey: "type",
|
|
1948
|
+
typeValue: "streamable_http",
|
|
1949
|
+
urlKey: "uri",
|
|
1950
|
+
headersKey: "headers"
|
|
1951
|
+
},
|
|
1952
|
+
addCommand: null,
|
|
1953
|
+
deeplink: "goose://extension?url={url}&type=streamable_http&id={name}&name={name}"
|
|
1954
|
+
},
|
|
1955
|
+
availability: "GOOSE_ALLOWLIST can restrict extensions to an org-hosted allowlist.",
|
|
1956
|
+
verifiedAt: "2026-09-15",
|
|
1957
|
+
sources: [
|
|
1958
|
+
{
|
|
1959
|
+
url: "https://goose-docs.ai/docs/getting-started/using-extensions/",
|
|
1960
|
+
fact: "config path; stdio/streamable_http YAML keys; client_id/client_secret_key/scopes; CIMD+DCR; GOOSE_OAUTH_CALLBACK_PORT; CLI flags; goose configure; deeplink formats (URL-encoded)"
|
|
1961
|
+
},
|
|
1962
|
+
{
|
|
1963
|
+
url: "https://goose-docs.ai/docs/guides/config-files/",
|
|
1964
|
+
fact: "Types builtin/platform/stdio/streamable_http; 'SSE is not supported; migrate old SSE configurations to streamable_http'; headers, env_keys, envs, available_tools; GOOSE_ALLOWLIST; keyring/secrets.yaml; precedence; Windows path"
|
|
1965
|
+
},
|
|
1966
|
+
{
|
|
1967
|
+
url: "https://goose-docs.ai/docs/guides/managing-tools/tool-permissions/",
|
|
1968
|
+
fact: "'goose performs best with fewer than 25 total tools enabled across all extensions'"
|
|
1969
|
+
},
|
|
1970
|
+
{
|
|
1971
|
+
url: "https://github.com/block/goose/blob/abb47465996cd1041c4dbb83decf3f27216d3007/crates/goose/src/agents/extension_manager.rs",
|
|
1972
|
+
fact: 'Source code: public tool name format!("{}__{}", name, tool.name) unless unprefixed_tools; ProtocolVersion::V_2025_11_25 legacy capabilities'
|
|
1973
|
+
},
|
|
1974
|
+
{
|
|
1975
|
+
url: "https://github.com/block/goose/blob/abb47465996cd1041c4dbb83decf3f27216d3007/crates/goose/src/agents/mcp_client.rs",
|
|
1976
|
+
fact: "Source code: ClientCapabilities enable_roots/enable_sampling/enable_elicitation; lifecycle Auto preferred [V_2026_07_28, V_2025_11_25], Discover for >= STANDARD_HEADERS; working_directory root"
|
|
1977
|
+
},
|
|
1978
|
+
{
|
|
1979
|
+
url: "https://github.com/block/goose/blob/abb47465996cd1041c4dbb83decf3f27216d3007/crates/goose/src/oauth/mod.rs",
|
|
1980
|
+
fact: "Source code: CLIENT_METADATA_URL https://goose-docs.ai/oauth/client-metadata.json; GOOSE_MCP_OAUTH_CLIENT_METADATA_URL; preregistered client for servers with neither DCR nor CIMD; callback timeout default 300s (GOOSE_OAUTH_CALLBACK_TIMEOUT_SECONDS)"
|
|
1981
|
+
},
|
|
1982
|
+
{
|
|
1983
|
+
url: "https://github.com/block/goose/blob/abb47465996cd1041c4dbb83decf3f27216d3007/crates/goose/src/config/extensions.rs",
|
|
1984
|
+
fact: 'Source code: Warning "SSE is unsupported, migrate to streamable_http" for type: sse'
|
|
1985
|
+
},
|
|
1986
|
+
{
|
|
1987
|
+
url: "https://github.com/block/goose/blob/abb47465996cd1041c4dbb83decf3f27216d3007/crates/goose-cli/src/cli.rs",
|
|
1988
|
+
fact: "Source code: --with-extension, --with-streamable-http-extension 'url [timeout=N]', --with-builtin"
|
|
1989
|
+
},
|
|
1990
|
+
{
|
|
1991
|
+
url: "https://github.com/block/goose/blob/abb47465996cd1041c4dbb83decf3f27216d3007/Cargo.toml",
|
|
1992
|
+
fact: "Source code: rmcp 3.2.0 with auth feature"
|
|
1993
|
+
},
|
|
1994
|
+
{
|
|
1995
|
+
url: "https://block.github.io/goose/docs/guides/tool-router/",
|
|
1996
|
+
fact: "Tool Router (preview) page now returns 404; status unknown"
|
|
1997
|
+
}
|
|
1998
|
+
]
|
|
1999
|
+
},
|
|
2000
|
+
{
|
|
2001
|
+
id: "lovable",
|
|
2002
|
+
name: "Lovable",
|
|
2003
|
+
kind: "app_builder",
|
|
2004
|
+
transports: {
|
|
2005
|
+
stdio: "partial",
|
|
2006
|
+
streamableHttp: "yes",
|
|
2007
|
+
sse: "unknown"
|
|
2008
|
+
},
|
|
2009
|
+
auth: {
|
|
2010
|
+
oauth: "yes",
|
|
2011
|
+
dcr: "unknown",
|
|
2012
|
+
cimd: "unknown",
|
|
2013
|
+
preRegistered: "unknown",
|
|
2014
|
+
staticHeaders: "partial",
|
|
2015
|
+
redirectUris: [],
|
|
2016
|
+
note: "Only a bearer token or API key can be sent (no arbitrary header names), and connections come from non-fixed IPs unless Enterprise static IPs are enabled."
|
|
2017
|
+
},
|
|
2018
|
+
toolNaming: {
|
|
2019
|
+
format: null,
|
|
2020
|
+
replacedChars: null,
|
|
2021
|
+
maxLength: null,
|
|
2022
|
+
onOverflow: "unknown"
|
|
2023
|
+
},
|
|
2024
|
+
toolLimits: {
|
|
2025
|
+
maxTools: null,
|
|
2026
|
+
scope: null,
|
|
2027
|
+
toolSearch: "unknown"
|
|
2028
|
+
},
|
|
2029
|
+
schema: {
|
|
2030
|
+
rootCombinators: "unknown",
|
|
2031
|
+
rejectedKeywords: [],
|
|
2032
|
+
typeArrays: "unknown",
|
|
2033
|
+
onInvalid: "unknown"
|
|
2034
|
+
},
|
|
2035
|
+
protocol: {
|
|
2036
|
+
revision: null,
|
|
2037
|
+
resources: "unknown",
|
|
2038
|
+
prompts: "unknown",
|
|
2039
|
+
sampling: "unknown",
|
|
2040
|
+
elicitation: "unknown"
|
|
2041
|
+
},
|
|
2042
|
+
config: {
|
|
2043
|
+
remote: null,
|
|
2044
|
+
addCommand: null,
|
|
2045
|
+
deeplink: null
|
|
2046
|
+
},
|
|
2047
|
+
availability: "All plans including Free; Business/Enterprise admins can disable custom MCP or limit who creates connections, and local servers need the desktop app.",
|
|
2048
|
+
verifiedAt: "2026-09-15",
|
|
2049
|
+
sources: [
|
|
2050
|
+
{
|
|
2051
|
+
url: "https://docs.lovable.dev/integrations/custom-mcp",
|
|
2052
|
+
fact: "Custom MCP: remote server by URL; auth OAuth (default) / Bearer token or API key / No authentication; all plans; static IPs Enterprise-only with ranges; connection type fixed at creation; never part of published app"
|
|
2053
|
+
},
|
|
2054
|
+
{
|
|
2055
|
+
url: "https://docs.lovable.dev/integrations/mcp-servers",
|
|
2056
|
+
fact: "Chat connectors enabled by default on all plans; Business/Enterprise admins can turn off; chat-only, not in published app; prebuilt connector list"
|
|
2057
|
+
},
|
|
2058
|
+
{
|
|
2059
|
+
url: "https://docs.lovable.dev/integrations/desktop-app",
|
|
2060
|
+
fact: "Desktop app supports remote and local MCP servers; custom local server via command and arguments or local HTTP URL; local servers desktop-only; all plans incl. Free"
|
|
2061
|
+
},
|
|
2062
|
+
{
|
|
2063
|
+
url: "https://docs.lovable.dev/features/privacy-and-security-settings",
|
|
2064
|
+
fact: "Workspace toggles 'Remote MCP connectors' and 'Local desktop MCP servers' (latter requires former)"
|
|
2065
|
+
},
|
|
2066
|
+
{
|
|
2067
|
+
url: "https://docs.lovable.dev/integrations/admin-controls",
|
|
2068
|
+
fact: "Admin settings and 'who can create connections' on Business/Enterprise; Free/Pro editors+ can create; Custom MCP row governs all custom servers"
|
|
2069
|
+
},
|
|
2070
|
+
{
|
|
2071
|
+
url: "https://docs.lovable.dev/integrations/mcp-registries",
|
|
2072
|
+
fact: "MCP registries on all plans, follow MCP registry spec, registry auth is bearer token only; servers from registry use direct connection; local servers need desktop app"
|
|
2073
|
+
},
|
|
2074
|
+
{
|
|
2075
|
+
url: "https://docs.lovable.dev/integrations/security",
|
|
2076
|
+
fact: "Custom MCP servers do not route through connector gateway; gateway 1,000 req/min per connector per project"
|
|
2077
|
+
},
|
|
2078
|
+
{
|
|
2079
|
+
url: "https://docs.lovable.dev/changelog",
|
|
2080
|
+
fact: "Changelog: 'Custom MCP servers are now available on all plans, no paid plan required'; desktop app with local MCP support"
|
|
2081
|
+
}
|
|
2082
|
+
]
|
|
2083
|
+
},
|
|
2084
|
+
{
|
|
2085
|
+
id: "replit",
|
|
2086
|
+
name: "Replit Agent",
|
|
2087
|
+
kind: "app_builder",
|
|
2088
|
+
transports: {
|
|
2089
|
+
stdio: "no",
|
|
2090
|
+
streamableHttp: "yes",
|
|
2091
|
+
sse: "unknown"
|
|
2092
|
+
},
|
|
2093
|
+
auth: {
|
|
2094
|
+
oauth: "yes",
|
|
2095
|
+
dcr: "yes",
|
|
2096
|
+
cimd: "unknown",
|
|
2097
|
+
preRegistered: "unknown",
|
|
2098
|
+
staticHeaders: "yes",
|
|
2099
|
+
redirectUris: [],
|
|
2100
|
+
note: "All MCP traffic passes through Replit's security scanner, which can block individual tools."
|
|
2101
|
+
},
|
|
2102
|
+
toolNaming: {
|
|
2103
|
+
format: null,
|
|
2104
|
+
replacedChars: null,
|
|
2105
|
+
maxLength: null,
|
|
2106
|
+
onOverflow: "unknown"
|
|
2107
|
+
},
|
|
2108
|
+
toolLimits: {
|
|
2109
|
+
maxTools: null,
|
|
2110
|
+
scope: null,
|
|
2111
|
+
toolSearch: "unknown"
|
|
2112
|
+
},
|
|
2113
|
+
schema: {
|
|
2114
|
+
rootCombinators: "unknown",
|
|
2115
|
+
rejectedKeywords: [],
|
|
2116
|
+
typeArrays: "unknown",
|
|
2117
|
+
onInvalid: "unknown"
|
|
2118
|
+
},
|
|
2119
|
+
protocol: {
|
|
2120
|
+
revision: null,
|
|
2121
|
+
resources: "unknown",
|
|
2122
|
+
prompts: "unknown",
|
|
2123
|
+
sampling: "unknown",
|
|
2124
|
+
elicitation: "unknown"
|
|
2125
|
+
},
|
|
2126
|
+
config: {
|
|
2127
|
+
remote: null,
|
|
2128
|
+
addCommand: null,
|
|
2129
|
+
deeplink: "https://replit.com/integrations?mcp={config}"
|
|
2130
|
+
},
|
|
2131
|
+
availability: null,
|
|
2132
|
+
verifiedAt: "2026-09-15",
|
|
2133
|
+
sources: [
|
|
2134
|
+
{
|
|
2135
|
+
url: "https://docs.replit.com/build/connect-via-mcp",
|
|
2136
|
+
fact: "Add custom MCP: HTTPS endpoint, Advanced settings custom headers, Test & save walks through OAuth; tools available across all projects; confirmation prompt if tool requires"
|
|
2137
|
+
},
|
|
2138
|
+
{
|
|
2139
|
+
url: "https://docs.replit.com/features/mcp/overview",
|
|
2140
|
+
fact: "Auth: OAuth DCR auto-detect/register; custom headers on every request; security scanner blocks unsafe tools; install link format base64 JSON displayName/baseUrl/headers; pre-listed links include /sse endpoints"
|
|
2141
|
+
},
|
|
2142
|
+
{
|
|
2143
|
+
url: "https://docs.replit.com/features/mcp/install-links",
|
|
2144
|
+
fact: "Install link https://replit.com/integrations?mcp=[base64-encoded-json]; baseUrl must be HTTPS; headers array of {key,value}"
|
|
2145
|
+
},
|
|
2146
|
+
{
|
|
2147
|
+
url: "https://docs.replit.com/updates/2025/12/12/changelog",
|
|
2148
|
+
fact: "Agent now supports custom (remote) MCP servers; automatically loads tools"
|
|
2149
|
+
},
|
|
2150
|
+
{
|
|
2151
|
+
url: "https://docs.replit.com/home/integrations",
|
|
2152
|
+
fact: "Connector catalog shows what is available on your plan (no MCP-specific plan statement)"
|
|
2153
|
+
}
|
|
2154
|
+
]
|
|
2155
|
+
},
|
|
2156
|
+
{
|
|
2157
|
+
id: "bolt",
|
|
2158
|
+
name: "Bolt.new",
|
|
2159
|
+
kind: "app_builder",
|
|
2160
|
+
transports: {
|
|
2161
|
+
stdio: "no",
|
|
2162
|
+
streamableHttp: "yes",
|
|
2163
|
+
sse: "yes"
|
|
2164
|
+
},
|
|
2165
|
+
auth: {
|
|
2166
|
+
oauth: "yes",
|
|
2167
|
+
dcr: "unknown",
|
|
2168
|
+
cimd: "unknown",
|
|
2169
|
+
preRegistered: "unknown",
|
|
2170
|
+
staticHeaders: "partial",
|
|
2171
|
+
redirectUris: [],
|
|
2172
|
+
note: "The API key option's header name and format are undocumented, and arbitrary custom headers are not offered."
|
|
2173
|
+
},
|
|
2174
|
+
toolNaming: {
|
|
2175
|
+
format: null,
|
|
2176
|
+
replacedChars: null,
|
|
2177
|
+
maxLength: null,
|
|
2178
|
+
onOverflow: "unknown"
|
|
2179
|
+
},
|
|
2180
|
+
toolLimits: {
|
|
2181
|
+
maxTools: null,
|
|
2182
|
+
scope: null,
|
|
2183
|
+
toolSearch: "unknown"
|
|
2184
|
+
},
|
|
2185
|
+
schema: {
|
|
2186
|
+
rootCombinators: "unknown",
|
|
2187
|
+
rejectedKeywords: [],
|
|
2188
|
+
typeArrays: "unknown",
|
|
2189
|
+
onInvalid: "unknown"
|
|
2190
|
+
},
|
|
2191
|
+
protocol: {
|
|
2192
|
+
revision: null,
|
|
2193
|
+
resources: "unknown",
|
|
2194
|
+
prompts: "unknown",
|
|
2195
|
+
sampling: "unknown",
|
|
2196
|
+
elicitation: "unknown"
|
|
2197
|
+
},
|
|
2198
|
+
config: {
|
|
2199
|
+
remote: null,
|
|
2200
|
+
addCommand: null,
|
|
2201
|
+
deeplink: null
|
|
2202
|
+
},
|
|
2203
|
+
availability: null,
|
|
2204
|
+
verifiedAt: "2026-09-15",
|
|
2205
|
+
sources: [
|
|
2206
|
+
{
|
|
2207
|
+
url: "https://support.bolt.new/building/using-bolt/connect-mcp",
|
|
2208
|
+
fact: "Custom MCP fields Name/URL/Transport type (HTTP or SSE)/Authentication (API key, MCP OAuth, None); tools on by default, account-wide tool toggles; per-project connector toggles; auto tool discovery; context/token warning"
|
|
2209
|
+
},
|
|
2210
|
+
{
|
|
2211
|
+
url: "https://bolt.new/blog/introducing-connectors",
|
|
2212
|
+
fact: "2026-03-11: eight built-in connectors; custom connector requires remote MCP server; transport usually HTTP or SSE; API key or sign-in"
|
|
2213
|
+
},
|
|
2214
|
+
{
|
|
2215
|
+
url: "https://support.bolt.new/settings/account-settings",
|
|
2216
|
+
fact: "Connectors (MCP) live in account settings"
|
|
2217
|
+
},
|
|
2218
|
+
{
|
|
2219
|
+
url: "https://support.bolt.new/account-and-subscription/team-plans",
|
|
2220
|
+
fact: "No connector/MCP plan gating mentioned (negative check)"
|
|
2221
|
+
}
|
|
2222
|
+
]
|
|
2223
|
+
},
|
|
2224
|
+
{
|
|
2225
|
+
id: "v0",
|
|
2226
|
+
name: "v0",
|
|
2227
|
+
kind: "app_builder",
|
|
2228
|
+
transports: {
|
|
2229
|
+
stdio: "no",
|
|
2230
|
+
streamableHttp: "yes",
|
|
2231
|
+
sse: "unknown"
|
|
2232
|
+
},
|
|
2233
|
+
auth: {
|
|
2234
|
+
oauth: "yes",
|
|
2235
|
+
dcr: "partial",
|
|
2236
|
+
cimd: "partial",
|
|
2237
|
+
preRegistered: "partial",
|
|
2238
|
+
staticHeaders: "yes",
|
|
2239
|
+
redirectUris: ["https://api.v0.dev/v1/mcp-servers/oauth/callback"],
|
|
2240
|
+
note: "CIMD and pre-registered clients are documented only for the v0 API, whose OAuth config requires authorizationUrl, tokenUrl and clientId; the v0.app UI callback URL is undocumented."
|
|
2241
|
+
},
|
|
2242
|
+
toolNaming: {
|
|
2243
|
+
format: null,
|
|
2244
|
+
replacedChars: null,
|
|
2245
|
+
maxLength: null,
|
|
2246
|
+
onOverflow: "unknown"
|
|
2247
|
+
},
|
|
2248
|
+
toolLimits: {
|
|
2249
|
+
maxTools: null,
|
|
2250
|
+
scope: null,
|
|
2251
|
+
toolSearch: "unknown"
|
|
2252
|
+
},
|
|
2253
|
+
schema: {
|
|
2254
|
+
rootCombinators: "unknown",
|
|
2255
|
+
rejectedKeywords: [],
|
|
2256
|
+
typeArrays: "unknown",
|
|
2257
|
+
onInvalid: "unknown"
|
|
2258
|
+
},
|
|
2259
|
+
protocol: {
|
|
2260
|
+
revision: null,
|
|
2261
|
+
resources: "no",
|
|
2262
|
+
prompts: "no",
|
|
2263
|
+
sampling: "unknown",
|
|
2264
|
+
elicitation: "unknown"
|
|
2265
|
+
},
|
|
2266
|
+
config: {
|
|
2267
|
+
remote: null,
|
|
2268
|
+
addCommand: null,
|
|
2269
|
+
deeplink: null
|
|
2270
|
+
},
|
|
2271
|
+
availability: null,
|
|
2272
|
+
verifiedAt: "2026-09-15",
|
|
2273
|
+
sources: [
|
|
2274
|
+
{
|
|
2275
|
+
url: "https://v0.app/docs/MCP",
|
|
2276
|
+
fact: "BYO MCP via + menu -> MCPs; auth No Auth/Custom Headers/Bearer Token/OAuth 2.0; MCP servers only provide tools; generated app cannot use MCP tools; availability vs approval modes; presets"
|
|
2277
|
+
},
|
|
2278
|
+
{
|
|
2279
|
+
url: "https://v0.app/docs/api/platform/reference/mcp-servers/create",
|
|
2280
|
+
fact: "Create MCP server schema: auth types, token max 1000, max 10 headers, oauth config fields incl. registrationUrl, usePKCE default true, clientIdMetadataDocumentSupported, scope user|team, HTTPS url max 500"
|
|
2281
|
+
},
|
|
2282
|
+
{
|
|
2283
|
+
url: "https://v0.app/docs/api/v1/guides/oauth-mcp-servers",
|
|
2284
|
+
fact: "Redirect URI https://api.v0.dev/v1/mcp-servers/oauth/callback; CIMD client ID URL https://v0.app/api/chat/integrations/oauth/client-metadata.json; DCR/manual registration done via provider flow; resource sent as RFC 8707 indicator; POST /v1/mcp-servers/{id}/oauth/authorize"
|
|
2285
|
+
},
|
|
2286
|
+
{
|
|
2287
|
+
url: "https://vercel.com/changelog/v0-api-now-supports-custom-mcp-servers",
|
|
2288
|
+
fact: "2026-03-06: v0 API supports any custom MCP server; reference by server ID in chat creation"
|
|
2289
|
+
},
|
|
2290
|
+
{
|
|
2291
|
+
url: "https://v0.app/pricing",
|
|
2292
|
+
fact: "Plans Free/Plus/Business/Enterprise; no MCP mention (negative check)"
|
|
2293
|
+
}
|
|
2294
|
+
]
|
|
2295
|
+
}
|
|
2296
|
+
];
|
|
2297
|
+
|
|
2298
|
+
// src/clients/render.ts
|
|
2299
|
+
init_esm_shims();
|
|
2300
|
+
var placeholderFor = (header) => /^authorization$/i.test(header) ? "Bearer <your-token>" : `<your-${header.toLowerCase().replace(/[^a-z0-9]+/g, "-")}>`;
|
|
2301
|
+
var headerObject = (names) => Object.fromEntries(names.map((h) => [h, placeholderFor(h)]));
|
|
2302
|
+
function entry(client, input) {
|
|
2303
|
+
const t = client.config.remote;
|
|
2304
|
+
const e = {};
|
|
2305
|
+
if (t.typeKey && t.typeValue) e[t.typeKey] = t.typeValue;
|
|
2306
|
+
e[t.urlKey] = input.url;
|
|
2307
|
+
if (t.headersKey && input.headers.length) e[t.headersKey] = headerObject(input.headers);
|
|
2308
|
+
return e;
|
|
2309
|
+
}
|
|
2310
|
+
var tomlString = (s) => JSON.stringify(s);
|
|
2311
|
+
var tomlKey = (s) => /^[A-Za-z0-9_-]+$/.test(s) ? s : JSON.stringify(s);
|
|
2312
|
+
function renderFile(client, input) {
|
|
2313
|
+
const t = client.config.remote;
|
|
2314
|
+
const e = entry(client, input);
|
|
2315
|
+
if (/\.toml\b/.test(t.file)) {
|
|
2316
|
+
const lines = [`[${t.containerKey}.${tomlKey(input.name)}]`];
|
|
2317
|
+
const nested = [];
|
|
2318
|
+
for (const [k, v] of Object.entries(e)) {
|
|
2319
|
+
if (v && typeof v === "object") nested.push([k, v]);
|
|
2320
|
+
else lines.push(`${tomlKey(k)} = ${tomlString(String(v))}`);
|
|
2321
|
+
}
|
|
2322
|
+
for (const [k, obj] of nested) {
|
|
2323
|
+
lines.push("", `[${t.containerKey}.${tomlKey(input.name)}.${tomlKey(k)}]`);
|
|
2324
|
+
for (const [hk, hv] of Object.entries(obj)) lines.push(`${tomlKey(hk)} = ${tomlString(hv)}`);
|
|
2325
|
+
}
|
|
2326
|
+
return { kind: "toml", target: t.file, text: lines.join("\n") };
|
|
2327
|
+
}
|
|
2328
|
+
if (/\.ya?ml\b/.test(t.file)) {
|
|
2329
|
+
const lines = [`${t.containerKey}:`, ` ${input.name}:`];
|
|
2330
|
+
for (const [k, v] of Object.entries(e)) {
|
|
2331
|
+
if (v && typeof v === "object") {
|
|
2332
|
+
lines.push(` ${k}:`);
|
|
2333
|
+
for (const [hk, hv] of Object.entries(v))
|
|
2334
|
+
lines.push(` ${hk}: ${JSON.stringify(hv)}`);
|
|
2335
|
+
} else lines.push(` ${k}: ${JSON.stringify(v)}`);
|
|
2336
|
+
}
|
|
2337
|
+
return { kind: "yaml", target: t.file, text: lines.join("\n") };
|
|
2338
|
+
}
|
|
2339
|
+
return {
|
|
2340
|
+
kind: "json",
|
|
2341
|
+
target: t.file,
|
|
2342
|
+
text: JSON.stringify({ [t.containerKey]: { [input.name]: e } }, null, 2)
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
function renderCommand(template, input) {
|
|
2346
|
+
const shellQuote = (s) => /^[A-Za-z0-9_./:@%+=-]+$/.test(s) ? s : `'${s.replace(/'/g, `'\\''`)}'`;
|
|
2347
|
+
let cmd = template;
|
|
2348
|
+
const headerPart = /\s*--header\s+"\{header\}"/.exec(cmd);
|
|
2349
|
+
if (headerPart) {
|
|
2350
|
+
const repeated = input.headers.map((h) => ` --header "${h}: ${placeholderFor(h)}"`).join("");
|
|
2351
|
+
cmd = cmd.replace(headerPart[0], repeated);
|
|
2352
|
+
}
|
|
2353
|
+
return cmd.replace(/\{name\}/g, shellQuote(input.name)).replace(/\{url\}/g, shellQuote(input.url));
|
|
2354
|
+
}
|
|
2355
|
+
function renderDeeplink(client, input) {
|
|
2356
|
+
const link = client.config.deeplink;
|
|
2357
|
+
if (!link) return null;
|
|
2358
|
+
const headers = input.headers.length ? { headers: headerObject(input.headers) } : {};
|
|
2359
|
+
switch (client.id) {
|
|
2360
|
+
case "cursor": {
|
|
2361
|
+
const config = Buffer.from(JSON.stringify({ url: input.url, ...headers })).toString("base64");
|
|
2362
|
+
return link.replace("{name}", encodeURIComponent(input.name)).replace("{config}", encodeURIComponent(config));
|
|
2363
|
+
}
|
|
2364
|
+
case "vscode":
|
|
2365
|
+
return link.replace(
|
|
2366
|
+
"{config}",
|
|
2367
|
+
encodeURIComponent(
|
|
2368
|
+
JSON.stringify({ name: input.name, type: "http", url: input.url, ...headers })
|
|
2369
|
+
)
|
|
2370
|
+
);
|
|
2371
|
+
case "goose":
|
|
2372
|
+
return input.headers.length ? null : link.replace("{url}", encodeURIComponent(input.url)).replace(/\{name\}/g, encodeURIComponent(input.name));
|
|
2373
|
+
default:
|
|
2374
|
+
return null;
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
function renderClientConfig(client, input) {
|
|
2378
|
+
const out = [];
|
|
2379
|
+
if (client.config.remote) out.push(renderFile(client, input));
|
|
2380
|
+
const command = client.config.addCommand;
|
|
2381
|
+
if (command && (input.headers.length === 0 || command.includes("{header}"))) {
|
|
2382
|
+
out.push({ kind: "command", target: "terminal", text: renderCommand(command, input) });
|
|
2383
|
+
}
|
|
2384
|
+
const link = renderDeeplink(client, input);
|
|
2385
|
+
if (link) out.push({ kind: "deeplink", target: "browser", text: link });
|
|
2386
|
+
return out;
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
// src/connect/check.ts
|
|
2390
|
+
var CLIENT_FACTS_READ = "2026-09-15";
|
|
2391
|
+
var LIVE_BUDGET_MS = 8e3;
|
|
2392
|
+
function aliasFor(name) {
|
|
2393
|
+
let source = name;
|
|
2394
|
+
if (/^https?:\/\//i.test(name)) {
|
|
2395
|
+
try {
|
|
2396
|
+
const labels = new URL(name).hostname.toLowerCase().split(".").slice(0, -1).filter((l) => !["mcp", "api", "www", "server"].includes(l));
|
|
2397
|
+
source = labels[0] ?? source;
|
|
2398
|
+
} catch {
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
const last = source.replace(/\/+$/, "").split("/").pop() ?? source;
|
|
2402
|
+
const base = last.replace(/^@[^/]+\//, "").replace(/[-_.]?mcp[-_.]?(server)?$/i, "") || last;
|
|
2403
|
+
return (base.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "server").slice(0, 30);
|
|
2404
|
+
}
|
|
2405
|
+
function remoteFactsFromRow(row, declared) {
|
|
2406
|
+
return {
|
|
2407
|
+
url: row.url,
|
|
2408
|
+
transport: row.transport,
|
|
2409
|
+
templated: row.templated,
|
|
2410
|
+
status: row.lastStatus,
|
|
2411
|
+
observedAt: row.lastProbedAt,
|
|
2412
|
+
auth: row.auth ? { ...row.auth, declaredHeaders: declared } : null,
|
|
2413
|
+
violations: row.violations ?? [],
|
|
2414
|
+
declaredHeaders: declared
|
|
2415
|
+
};
|
|
2416
|
+
}
|
|
2417
|
+
async function toolsFor(db, contentHash) {
|
|
2418
|
+
if (!contentHash) return null;
|
|
2419
|
+
const contract = await getContract(db, contentHash);
|
|
2420
|
+
return contract?.tools ?? null;
|
|
2421
|
+
}
|
|
2422
|
+
function pickEndpoint(rows) {
|
|
2423
|
+
const rank = (s) => s === "open" ? 0 : s === "auth_required" ? 1 : s === null ? 2 : 3;
|
|
2424
|
+
return [...rows].sort(
|
|
2425
|
+
(a, b) => rank(a.lastStatus) - rank(b.lastStatus) || (b.lastProbedAt?.getTime() ?? 0) - (a.lastProbedAt?.getTime() ?? 0)
|
|
2426
|
+
)[0] ?? null;
|
|
2427
|
+
}
|
|
2428
|
+
function packageFacts(packages) {
|
|
2429
|
+
const pkg = packages.find((p) => p.registryType === "npm" && p.identifier) ?? packages.find((p) => p.identifier);
|
|
2430
|
+
if (!pkg?.identifier) return null;
|
|
2431
|
+
const env = (pkg.environmentVariables ?? []).map((e) => ({
|
|
2432
|
+
name: e.name,
|
|
2433
|
+
description: e.description ?? null,
|
|
2434
|
+
required: e.isRequired === true,
|
|
2435
|
+
secret: e.isSecret === true,
|
|
2436
|
+
format: e.format ?? null
|
|
2437
|
+
}));
|
|
2438
|
+
return { registryType: pkg.registryType ?? "unknown", identifier: pkg.identifier, env };
|
|
2439
|
+
}
|
|
2440
|
+
function headersToRender(remote) {
|
|
2441
|
+
if (!remote) return [];
|
|
2442
|
+
const declared = remote.declaredHeaders.filter((h) => h.required || h.secret).map((h) => h.name);
|
|
2443
|
+
if (declared.length) return declared;
|
|
2444
|
+
return remote.auth?.mode === "static" ? ["Authorization"] : [];
|
|
2445
|
+
}
|
|
2446
|
+
async function resolveServerEndpoint(db, query) {
|
|
2447
|
+
const q = query.trim();
|
|
2448
|
+
if (!q) return null;
|
|
2449
|
+
if (/^https?:\/\//i.test(q)) {
|
|
2450
|
+
const row = await getEndpointByUrl(db, q);
|
|
2451
|
+
return row && !row.removedAt ? row : null;
|
|
2452
|
+
}
|
|
2453
|
+
const [server] = await findRegistryServers(db, q, 1);
|
|
2454
|
+
if (!server) return null;
|
|
2455
|
+
return pickEndpoint((await getEndpointsForServer(db, server.name)).map((l) => l.endpoint));
|
|
2456
|
+
}
|
|
2457
|
+
async function handleConnectCheck(db, input, deps = {}) {
|
|
2458
|
+
const query = input.server.trim();
|
|
2459
|
+
let resolvedAs = null;
|
|
2460
|
+
let source = "none";
|
|
2461
|
+
let facts = {
|
|
2462
|
+
name: query,
|
|
2463
|
+
alias: aliasFor(query),
|
|
2464
|
+
remote: null,
|
|
2465
|
+
package: null,
|
|
2466
|
+
tools: null
|
|
2467
|
+
};
|
|
2468
|
+
let registryName = null;
|
|
2469
|
+
let endpointId = null;
|
|
2470
|
+
if (/^https?:\/\//i.test(query)) {
|
|
2471
|
+
resolvedAs = "url";
|
|
2472
|
+
const row = await getEndpointByUrl(db, query);
|
|
2473
|
+
if (row && !row.removedAt) {
|
|
2474
|
+
resolvedAs = "endpoint";
|
|
2475
|
+
endpointId = row.id;
|
|
2476
|
+
const declared = (await getDeclaredHeaders(db, [row.id])).get(row.id) ?? [];
|
|
2477
|
+
facts = {
|
|
2478
|
+
...facts,
|
|
2479
|
+
remote: remoteFactsFromRow(row, declared),
|
|
2480
|
+
tools: await toolsFor(db, row.lastContentHash)
|
|
2481
|
+
};
|
|
2482
|
+
if (row.lastStatus) source = "probe";
|
|
2483
|
+
} else if (deps.liveProbe) {
|
|
2484
|
+
const { probeEndpoint } = await import("./probe-3IK646WN.js");
|
|
2485
|
+
const r2 = await probeEndpoint(query, {
|
|
2486
|
+
fetch: deps.liveProbe.fetch,
|
|
2487
|
+
budgetMs: deps.liveProbe.budgetMs ?? LIVE_BUDGET_MS
|
|
2488
|
+
});
|
|
2489
|
+
facts = {
|
|
2490
|
+
...facts,
|
|
2491
|
+
remote: {
|
|
2492
|
+
url: r2.url,
|
|
2493
|
+
transport: r2.transport ?? "streamable-http",
|
|
2494
|
+
templated: r2.status === "templated",
|
|
2495
|
+
status: r2.status,
|
|
2496
|
+
observedAt: /* @__PURE__ */ new Date(),
|
|
2497
|
+
auth: r2.auth,
|
|
2498
|
+
violations: r2.violations,
|
|
2499
|
+
declaredHeaders: []
|
|
2500
|
+
},
|
|
2501
|
+
tools: r2.snapshot?.tools ?? null
|
|
2502
|
+
};
|
|
2503
|
+
source = "live_probe";
|
|
2504
|
+
} else {
|
|
2505
|
+
const id = endpointIdentity(query);
|
|
2506
|
+
facts = {
|
|
2507
|
+
...facts,
|
|
2508
|
+
remote: id ? {
|
|
2509
|
+
url: id.url,
|
|
2510
|
+
transport: "streamable-http",
|
|
2511
|
+
templated: id.templated,
|
|
2512
|
+
status: null,
|
|
2513
|
+
observedAt: null,
|
|
2514
|
+
auth: null,
|
|
2515
|
+
violations: [],
|
|
2516
|
+
declaredHeaders: []
|
|
2517
|
+
} : null
|
|
2518
|
+
};
|
|
2519
|
+
}
|
|
2520
|
+
} else {
|
|
2521
|
+
const [server] = await findRegistryServers(db, query, 1);
|
|
2522
|
+
if (server) {
|
|
2523
|
+
resolvedAs = "registry";
|
|
2524
|
+
registryName = server.name;
|
|
2525
|
+
source = "registry";
|
|
2526
|
+
facts = {
|
|
2527
|
+
...facts,
|
|
2528
|
+
name: server.name,
|
|
2529
|
+
alias: aliasFor(server.name),
|
|
2530
|
+
package: packageFacts(server.packages)
|
|
2531
|
+
};
|
|
2532
|
+
const linked = await getEndpointsForServer(db, server.name);
|
|
2533
|
+
const best = pickEndpoint(linked.map((l) => l.endpoint));
|
|
2534
|
+
if (best) {
|
|
2535
|
+
endpointId = best.id;
|
|
2536
|
+
const declared = (await getDeclaredHeaders(db, [best.id])).get(best.id) ?? [];
|
|
2537
|
+
facts.remote = remoteFactsFromRow(best, declared);
|
|
2538
|
+
facts.tools = await toolsFor(db, best.lastContentHash);
|
|
2539
|
+
if (best.lastStatus) source = "probe";
|
|
2540
|
+
}
|
|
2541
|
+
} else {
|
|
2542
|
+
const manifest = await fetchServerManifest(query);
|
|
2543
|
+
if (manifest) {
|
|
2544
|
+
resolvedAs = "npm";
|
|
2545
|
+
registryName = manifest.registryName;
|
|
2546
|
+
source = "registry";
|
|
2547
|
+
facts = {
|
|
2548
|
+
...facts,
|
|
2549
|
+
alias: aliasFor(query),
|
|
2550
|
+
package: manifest.remoteOnly ? null : { registryType: "npm", identifier: manifest.npmPackage, env: manifest.env }
|
|
2551
|
+
};
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
const profiles = input.client ? CLIENT_PROFILES.filter((c) => c.id === input.client) : CLIENT_PROFILES;
|
|
2556
|
+
const clients = profiles.map((profile) => {
|
|
2557
|
+
const compat = evaluateClient(facts, profile);
|
|
2558
|
+
const config = compat.via === "remote" && facts.remote && compat.verdict !== "blocked" ? renderClientConfig(profile, {
|
|
2559
|
+
name: facts.alias,
|
|
2560
|
+
url: facts.remote.url,
|
|
2561
|
+
headers: headersToRender(facts.remote)
|
|
2562
|
+
}) : [];
|
|
2563
|
+
return { ...compat, config };
|
|
2564
|
+
});
|
|
2565
|
+
const summary = {
|
|
2566
|
+
works: 0,
|
|
2567
|
+
needs_setup: 0,
|
|
2568
|
+
blocked: 0,
|
|
2569
|
+
unknown: 0
|
|
2570
|
+
};
|
|
2571
|
+
for (const c of clients) summary[c.verdict]++;
|
|
2572
|
+
const changes = endpointId ? await listEndpointChanges(db, endpointId, 5) : [];
|
|
2573
|
+
const r = facts.remote;
|
|
2574
|
+
const observedAt = r?.observedAt ? r.observedAt.toISOString() : null;
|
|
2575
|
+
return {
|
|
2576
|
+
query,
|
|
2577
|
+
resolvedAs,
|
|
2578
|
+
server: {
|
|
2579
|
+
name: facts.name,
|
|
2580
|
+
registryName,
|
|
2581
|
+
url: r?.url ?? null,
|
|
2582
|
+
package: facts.package?.identifier ?? null
|
|
2583
|
+
},
|
|
2584
|
+
evidence: {
|
|
2585
|
+
source,
|
|
2586
|
+
status: r?.status ?? null,
|
|
2587
|
+
observedAt,
|
|
2588
|
+
auth: r?.auth ? {
|
|
2589
|
+
mode: r.auth.mode,
|
|
2590
|
+
issuer: r.auth.oauth?.issuer ?? null,
|
|
2591
|
+
cimd: r.auth.oauth?.cimd ?? null,
|
|
2592
|
+
dcr: r.auth.oauth?.dcr ?? null,
|
|
2593
|
+
pkceS256: r.auth.oauth?.pkceS256 ?? null
|
|
2594
|
+
} : null,
|
|
2595
|
+
violations: r?.violations ?? [],
|
|
2596
|
+
toolCount: facts.tools?.length ?? null,
|
|
2597
|
+
recentChanges: changes.map((c) => ({
|
|
2598
|
+
kind: c.kind,
|
|
2599
|
+
severity: c.severity,
|
|
2600
|
+
summary: c.summary,
|
|
2601
|
+
at: c.createdAt.toISOString()
|
|
2602
|
+
}))
|
|
2603
|
+
},
|
|
2604
|
+
clients,
|
|
2605
|
+
summary,
|
|
2606
|
+
coverageNote: resolvedAs === null ? `lurq has no record of "${query}" in the official MCP registry or its probe index. This is NOT evidence the server does not exist or will not work.` : `Server facts: ${source === "probe" ? `credential-free probe, ${observedAt ?? "date unknown"}` : source === "live_probe" ? "a live credential-free probe just now (not stored)" : "registry declaration only, not yet probed"}. Client facts: primary sources read ${CLIENT_FACTS_READ}. "unknown" means not established, never "will not work".`
|
|
2607
|
+
};
|
|
2608
|
+
}
|
|
2609
|
+
export {
|
|
2610
|
+
aliasFor,
|
|
2611
|
+
handleConnectCheck,
|
|
2612
|
+
resolveServerEndpoint
|
|
2613
|
+
};
|