mcp-squared 0.3.4 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -14
- package/dist/index.js +1419 -383
- package/dist/tui/config.js +385 -25
- package/package.json +10 -4
package/dist/tui/config.js
CHANGED
|
@@ -123,6 +123,274 @@ function ensureConfigDir(configPath) {
|
|
|
123
123
|
var SOCKET_FILENAME = "mcp-squared.sock", INSTANCE_DIR_NAME = "instances", SOCKET_DIR_NAME = "sockets", DAEMON_DIR_NAME = "daemon", DAEMON_REGISTRY_FILENAME = "daemon.json", DAEMON_SOCKET_FILENAME = "daemon.sock", CONFIG_FILENAME = "mcp-squared.toml", CONFIG_DIR_NAME = ".mcp-squared", APP_NAME = "mcp-squared";
|
|
124
124
|
var init_paths = () => {};
|
|
125
125
|
|
|
126
|
+
// src/capabilities/inference.ts
|
|
127
|
+
function createEmptyScores() {
|
|
128
|
+
return CAPABILITY_IDS.reduce((acc, capability) => {
|
|
129
|
+
acc[capability] = 0;
|
|
130
|
+
return acc;
|
|
131
|
+
}, {});
|
|
132
|
+
}
|
|
133
|
+
function extractSchemaSignal(schema) {
|
|
134
|
+
if (!schema || schema.type !== "object") {
|
|
135
|
+
return "";
|
|
136
|
+
}
|
|
137
|
+
const keys = Object.keys(schema.properties ?? {});
|
|
138
|
+
const required = schema.required ?? [];
|
|
139
|
+
return `${keys.join(" ")} ${required.join(" ")}`;
|
|
140
|
+
}
|
|
141
|
+
function scoreTextSignals(scores, text) {
|
|
142
|
+
for (const capability of CAPABILITY_IDS) {
|
|
143
|
+
for (const pattern of CAPABILITY_PATTERNS[capability]) {
|
|
144
|
+
if (pattern.test(text)) {
|
|
145
|
+
scores[capability] += 4;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function getHighestScoringCapability(scores) {
|
|
151
|
+
const bestScore = Math.max(...Object.values(scores));
|
|
152
|
+
if (bestScore <= 0) {
|
|
153
|
+
return "general";
|
|
154
|
+
}
|
|
155
|
+
for (const capability of CAPABILITY_PRIORITY) {
|
|
156
|
+
if (scores[capability] === bestScore) {
|
|
157
|
+
return capability;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return "general";
|
|
161
|
+
}
|
|
162
|
+
function inferNamespaceCapability(namespace, tools, capabilityOverrides = {}) {
|
|
163
|
+
const override = capabilityOverrides[namespace];
|
|
164
|
+
if (override) {
|
|
165
|
+
return override;
|
|
166
|
+
}
|
|
167
|
+
const scores = createEmptyScores();
|
|
168
|
+
const namespaceText = namespace.toLowerCase();
|
|
169
|
+
for (const hint of NAMESPACE_HINTS) {
|
|
170
|
+
if (hint.pattern.test(namespaceText)) {
|
|
171
|
+
scores[hint.capability] += hint.score;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
for (const tool of tools) {
|
|
175
|
+
const signal = [
|
|
176
|
+
tool.name,
|
|
177
|
+
tool.description ?? "",
|
|
178
|
+
extractSchemaSignal(tool.inputSchema)
|
|
179
|
+
].join(" ").toLowerCase();
|
|
180
|
+
scoreTextSignals(scores, signal);
|
|
181
|
+
}
|
|
182
|
+
return getHighestScoringCapability(scores);
|
|
183
|
+
}
|
|
184
|
+
function groupNamespacesByCapability(inventories, capabilityOverrides = {}) {
|
|
185
|
+
const grouped = CAPABILITY_IDS.reduce((acc, capability) => {
|
|
186
|
+
acc[capability] = [];
|
|
187
|
+
return acc;
|
|
188
|
+
}, {});
|
|
189
|
+
const byNamespace = {};
|
|
190
|
+
const sorted = [...inventories].sort((a, b) => a.namespace.localeCompare(b.namespace));
|
|
191
|
+
for (const inventory of sorted) {
|
|
192
|
+
const capability = inferNamespaceCapability(inventory.namespace, inventory.tools, capabilityOverrides);
|
|
193
|
+
byNamespace[inventory.namespace] = capability;
|
|
194
|
+
grouped[capability].push(inventory.namespace);
|
|
195
|
+
}
|
|
196
|
+
return { byNamespace, grouped };
|
|
197
|
+
}
|
|
198
|
+
var CAPABILITY_IDS, CAPABILITY_PRIORITY, NAMESPACE_HINTS, CAPABILITY_PATTERNS;
|
|
199
|
+
var init_inference = __esm(() => {
|
|
200
|
+
CAPABILITY_IDS = [
|
|
201
|
+
"code_search",
|
|
202
|
+
"docs",
|
|
203
|
+
"browser_automation",
|
|
204
|
+
"issue_tracking",
|
|
205
|
+
"cms_content",
|
|
206
|
+
"design",
|
|
207
|
+
"ai_media_generation",
|
|
208
|
+
"hosting_deploy",
|
|
209
|
+
"time_util",
|
|
210
|
+
"research",
|
|
211
|
+
"general"
|
|
212
|
+
];
|
|
213
|
+
CAPABILITY_PRIORITY = [
|
|
214
|
+
"code_search",
|
|
215
|
+
"docs",
|
|
216
|
+
"browser_automation",
|
|
217
|
+
"issue_tracking",
|
|
218
|
+
"cms_content",
|
|
219
|
+
"design",
|
|
220
|
+
"ai_media_generation",
|
|
221
|
+
"hosting_deploy",
|
|
222
|
+
"time_util",
|
|
223
|
+
"research",
|
|
224
|
+
"general"
|
|
225
|
+
];
|
|
226
|
+
NAMESPACE_HINTS = [
|
|
227
|
+
{
|
|
228
|
+
capability: "code_search",
|
|
229
|
+
pattern: /(auggie|augment|ctxdb|code|source|repo|symbol|search)/i,
|
|
230
|
+
score: 24
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
capability: "docs",
|
|
234
|
+
pattern: /(context7|ref|docs?|documentation|shadcn)/i,
|
|
235
|
+
score: 18
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
capability: "browser_automation",
|
|
239
|
+
pattern: /(chrome|devtools|browser|playwright|puppeteer|webdriver)/i,
|
|
240
|
+
score: 24
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
capability: "issue_tracking",
|
|
244
|
+
pattern: /(linear|jira|issue|ticket|project|milestone)/i,
|
|
245
|
+
score: 20
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
capability: "cms_content",
|
|
249
|
+
pattern: /(sanity|content|cms|dataset|schema|studio)/i,
|
|
250
|
+
score: 20
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
capability: "design",
|
|
254
|
+
pattern: /(pencil|figma|ui|design|artifact|visual)/i,
|
|
255
|
+
score: 20
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
capability: "hosting_deploy",
|
|
259
|
+
pattern: /(vercel|host|hosting|domain|dns|vps|deploy|infra)/i,
|
|
260
|
+
score: 22
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
capability: "time_util",
|
|
264
|
+
pattern: /(time|timezone|clock|date|utc)/i,
|
|
265
|
+
score: 22
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
capability: "ai_media_generation",
|
|
269
|
+
pattern: /(wavespeed|stability|replicate|midjourney|dall.?e|runway|imagen|flux|fal\.ai|dreamstudio)/i,
|
|
270
|
+
score: 24
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
capability: "research",
|
|
274
|
+
pattern: /(exa|perplexity|firecrawl|crawl|scrape|research|search)/i,
|
|
275
|
+
score: 16
|
|
276
|
+
}
|
|
277
|
+
];
|
|
278
|
+
CAPABILITY_PATTERNS = {
|
|
279
|
+
code_search: [
|
|
280
|
+
/\bcodebase\b/i,
|
|
281
|
+
/\bsource\b/i,
|
|
282
|
+
/\brepo(?:sitory)?\b/i,
|
|
283
|
+
/\bsymbol(?:s)?\b/i,
|
|
284
|
+
/\bdefinition(?:s)?\b/i,
|
|
285
|
+
/\breference(?:s)?\b/i,
|
|
286
|
+
/\busage(?:s)?\b/i,
|
|
287
|
+
/\bsearch_context\b/i,
|
|
288
|
+
/\bcodebase-retrieval\b/i,
|
|
289
|
+
/\bdirectory_path\b/i
|
|
290
|
+
],
|
|
291
|
+
docs: [
|
|
292
|
+
/\bdoc(?:s|umentation)?\b/i,
|
|
293
|
+
/\bread_docs\b/i,
|
|
294
|
+
/\bquery-docs\b/i,
|
|
295
|
+
/\breference\b/i,
|
|
296
|
+
/\bmanual\b/i,
|
|
297
|
+
/\bknowledge\b/i,
|
|
298
|
+
/\bregist(?:ry|ries)\b/i,
|
|
299
|
+
/\bcomponent(?:s)?\b/i,
|
|
300
|
+
/\bexample(?:s)?\b/i
|
|
301
|
+
],
|
|
302
|
+
browser_automation: [
|
|
303
|
+
/\bbrowser\b/i,
|
|
304
|
+
/\bdevtools\b/i,
|
|
305
|
+
/\bnavigate\b/i,
|
|
306
|
+
/\bclick\b/i,
|
|
307
|
+
/\bhover\b/i,
|
|
308
|
+
/\bnetwork\b/i,
|
|
309
|
+
/\bconsole\b/i,
|
|
310
|
+
/\bscreenshot\b/i,
|
|
311
|
+
/\bpage\b/i
|
|
312
|
+
],
|
|
313
|
+
issue_tracking: [
|
|
314
|
+
/\bissue(?:s)?\b/i,
|
|
315
|
+
/\bticket(?:s)?\b/i,
|
|
316
|
+
/\bmilestone(?:s)?\b/i,
|
|
317
|
+
/\bcycle(?:s)?\b/i,
|
|
318
|
+
/\bproject(?:s)?\b/i,
|
|
319
|
+
/\bcomment(?:s)?\b/i,
|
|
320
|
+
/\blinear\b/i
|
|
321
|
+
],
|
|
322
|
+
cms_content: [
|
|
323
|
+
/\bcms\b/i,
|
|
324
|
+
/\bcontent\b/i,
|
|
325
|
+
/\bdocument(?:s)?\b/i,
|
|
326
|
+
/\bdataset(?:s)?\b/i,
|
|
327
|
+
/\bschema\b/i,
|
|
328
|
+
/\bpublish\b/i,
|
|
329
|
+
/\bdraft(?:s)?\b/i,
|
|
330
|
+
/\bsanity\b/i,
|
|
331
|
+
/\bmigration\b/i
|
|
332
|
+
],
|
|
333
|
+
design: [
|
|
334
|
+
/\bdesign\b/i,
|
|
335
|
+
/\bui\b/i,
|
|
336
|
+
/\bartifact\b/i,
|
|
337
|
+
/\bstyle_guide\b/i,
|
|
338
|
+
/\bscreenshot\b/i,
|
|
339
|
+
/\bdiagram\b/i,
|
|
340
|
+
/\bimage\b/i,
|
|
341
|
+
/\blayout\b/i,
|
|
342
|
+
/\bframe\b/i
|
|
343
|
+
],
|
|
344
|
+
ai_media_generation: [
|
|
345
|
+
/\btext.to.image\b/i,
|
|
346
|
+
/\bimage.to.image\b/i,
|
|
347
|
+
/\btext.to.video\b/i,
|
|
348
|
+
/\binpaint(?:ing)?\b/i,
|
|
349
|
+
/\bupscal(?:e|ing)\b/i,
|
|
350
|
+
/\bgenerat(?:e|ion)\b.*\bimage/i,
|
|
351
|
+
/\bimage\b.*\bgenerat(?:e|ion)\b/i,
|
|
352
|
+
/\bai\b.*\b(?:image|photo|video)\b/i,
|
|
353
|
+
/\bstable.diffusion\b/i,
|
|
354
|
+
/\bdiffusion\b/i,
|
|
355
|
+
/\bprompt\b.*\b(?:image|visual)\b/i
|
|
356
|
+
],
|
|
357
|
+
hosting_deploy: [
|
|
358
|
+
/\bdeploy(?:ment)?\b/i,
|
|
359
|
+
/\bhosting\b/i,
|
|
360
|
+
/\bdomain(?:s)?\b/i,
|
|
361
|
+
/\bdns\b/i,
|
|
362
|
+
/\bvps\b/i,
|
|
363
|
+
/\bvirtual_machine(?:s)?\b/i,
|
|
364
|
+
/\bfirewall\b/i,
|
|
365
|
+
/\bwebsite\b/i,
|
|
366
|
+
/\bbilling\b/i,
|
|
367
|
+
/\bnameserver(?:s)?\b/i
|
|
368
|
+
],
|
|
369
|
+
time_util: [
|
|
370
|
+
/\btime\b/i,
|
|
371
|
+
/\btimezone\b/i,
|
|
372
|
+
/\butc\b/i,
|
|
373
|
+
/\bclock\b/i,
|
|
374
|
+
/\bdate\b/i,
|
|
375
|
+
/\bconvert_time\b/i,
|
|
376
|
+
/\bget_current_time\b/i
|
|
377
|
+
],
|
|
378
|
+
research: [
|
|
379
|
+
/\bresearch\b/i,
|
|
380
|
+
/\bcrawl\b/i,
|
|
381
|
+
/\bscrape\b/i,
|
|
382
|
+
/\bextract\b/i,
|
|
383
|
+
/\bsemantic_search\b/i,
|
|
384
|
+
/\bweb_search\b/i,
|
|
385
|
+
/\bask\b/i,
|
|
386
|
+
/\bperplexity\b/i,
|
|
387
|
+
/\bfirecrawl\b/i,
|
|
388
|
+
/\bexa\b/i
|
|
389
|
+
],
|
|
390
|
+
general: []
|
|
391
|
+
};
|
|
392
|
+
});
|
|
393
|
+
|
|
126
394
|
// src/tui/config.ts
|
|
127
395
|
import {
|
|
128
396
|
ASCIIFontRenderable,
|
|
@@ -378,6 +646,7 @@ import { parse as parseToml } from "smol-toml";
|
|
|
378
646
|
import { ZodError } from "zod";
|
|
379
647
|
|
|
380
648
|
// src/config/schema.ts
|
|
649
|
+
init_inference();
|
|
381
650
|
import { z } from "zod";
|
|
382
651
|
var LATEST_SCHEMA_VERSION = 1;
|
|
383
652
|
var LogLevelSchema = z.enum([
|
|
@@ -434,11 +703,29 @@ var SecuritySchema = z.object({
|
|
|
434
703
|
});
|
|
435
704
|
var SearchModeSchema = z.enum(["fast", "semantic", "hybrid"]);
|
|
436
705
|
var DetailLevelSchema = z.enum(["L0", "L1", "L2"]);
|
|
706
|
+
var CapabilityIdSchema = z.enum(CAPABILITY_IDS);
|
|
707
|
+
var DynamicToolSurfaceInferenceSchema = z.enum([
|
|
708
|
+
"heuristic_with_overrides",
|
|
709
|
+
"hybrid"
|
|
710
|
+
]);
|
|
711
|
+
var DynamicToolSurfaceRefreshSchema = z.enum(["on_connect"]);
|
|
712
|
+
var DynamicToolSurfaceSchema = z.object({
|
|
713
|
+
inference: DynamicToolSurfaceInferenceSchema.default("heuristic_with_overrides"),
|
|
714
|
+
refresh: DynamicToolSurfaceRefreshSchema.default("on_connect"),
|
|
715
|
+
capabilityOverrides: z.record(z.string().min(1), CapabilityIdSchema).default({}),
|
|
716
|
+
semanticConfidenceThreshold: z.number().min(0).max(1).default(0.45)
|
|
717
|
+
});
|
|
718
|
+
var PreferredNamespacesByIntentSchema = z.object({
|
|
719
|
+
codeSearch: z.array(z.string().min(1)).default([])
|
|
720
|
+
});
|
|
437
721
|
var FindToolsSchema = z.object({
|
|
438
722
|
defaultLimit: z.number().int().min(1).default(5),
|
|
439
723
|
maxLimit: z.number().int().min(1).max(200).default(50),
|
|
440
724
|
defaultMode: SearchModeSchema.default("fast"),
|
|
441
|
-
defaultDetailLevel: DetailLevelSchema.default("L1")
|
|
725
|
+
defaultDetailLevel: DetailLevelSchema.default("L1"),
|
|
726
|
+
preferredNamespacesByIntent: PreferredNamespacesByIntentSchema.default({
|
|
727
|
+
codeSearch: []
|
|
728
|
+
})
|
|
442
729
|
});
|
|
443
730
|
var IndexSchema = z.object({
|
|
444
731
|
refreshIntervalMs: z.number().int().min(1000).default(30000)
|
|
@@ -459,7 +746,8 @@ var OperationsSchema = z.object({
|
|
|
459
746
|
defaultLimit: 5,
|
|
460
747
|
maxLimit: 50,
|
|
461
748
|
defaultMode: "fast",
|
|
462
|
-
defaultDetailLevel: "L1"
|
|
749
|
+
defaultDetailLevel: "L1",
|
|
750
|
+
preferredNamespacesByIntent: { codeSearch: [] }
|
|
463
751
|
}),
|
|
464
752
|
index: IndexSchema.default({ refreshIntervalMs: 30000 }),
|
|
465
753
|
logging: LoggingSchema.default({ level: "info" }),
|
|
@@ -468,13 +756,20 @@ var OperationsSchema = z.object({
|
|
|
468
756
|
enabled: true,
|
|
469
757
|
minCooccurrenceThreshold: 2,
|
|
470
758
|
maxBundleSuggestions: 3
|
|
759
|
+
}),
|
|
760
|
+
dynamicToolSurface: DynamicToolSurfaceSchema.default({
|
|
761
|
+
inference: "heuristic_with_overrides",
|
|
762
|
+
refresh: "on_connect",
|
|
763
|
+
capabilityOverrides: {},
|
|
764
|
+
semanticConfidenceThreshold: 0.45
|
|
471
765
|
})
|
|
472
766
|
}).default({
|
|
473
767
|
findTools: {
|
|
474
768
|
defaultLimit: 5,
|
|
475
769
|
maxLimit: 50,
|
|
476
770
|
defaultMode: "fast",
|
|
477
|
-
defaultDetailLevel: "L1"
|
|
771
|
+
defaultDetailLevel: "L1",
|
|
772
|
+
preferredNamespacesByIntent: { codeSearch: [] }
|
|
478
773
|
},
|
|
479
774
|
index: { refreshIntervalMs: 30000 },
|
|
480
775
|
logging: { level: "info" },
|
|
@@ -483,6 +778,12 @@ var OperationsSchema = z.object({
|
|
|
483
778
|
enabled: true,
|
|
484
779
|
minCooccurrenceThreshold: 2,
|
|
485
780
|
maxBundleSuggestions: 3
|
|
781
|
+
},
|
|
782
|
+
dynamicToolSurface: {
|
|
783
|
+
inference: "heuristic_with_overrides",
|
|
784
|
+
refresh: "on_connect",
|
|
785
|
+
capabilityOverrides: {},
|
|
786
|
+
semanticConfidenceThreshold: 0.45
|
|
486
787
|
}
|
|
487
788
|
});
|
|
488
789
|
var ConfigSchema = z.object({
|
|
@@ -536,6 +837,34 @@ function migrateV0ToV1(config) {
|
|
|
536
837
|
|
|
537
838
|
// src/config/load.ts
|
|
538
839
|
init_paths();
|
|
840
|
+
function isRecord(value) {
|
|
841
|
+
return typeof value === "object" && value !== null;
|
|
842
|
+
}
|
|
843
|
+
function getDeprecatedDynamicToolSurfaceKeys(raw) {
|
|
844
|
+
const operations = raw["operations"];
|
|
845
|
+
if (!isRecord(operations)) {
|
|
846
|
+
return [];
|
|
847
|
+
}
|
|
848
|
+
const dynamicToolSurface = operations["dynamicToolSurface"];
|
|
849
|
+
if (!isRecord(dynamicToolSurface)) {
|
|
850
|
+
return [];
|
|
851
|
+
}
|
|
852
|
+
const deprecated = [];
|
|
853
|
+
if ("mode" in dynamicToolSurface) {
|
|
854
|
+
deprecated.push("operations.dynamicToolSurface.mode");
|
|
855
|
+
}
|
|
856
|
+
if ("naming" in dynamicToolSurface) {
|
|
857
|
+
deprecated.push("operations.dynamicToolSurface.naming");
|
|
858
|
+
}
|
|
859
|
+
return deprecated;
|
|
860
|
+
}
|
|
861
|
+
function warnDeprecatedDynamicToolSurfaceKeys(filePath, keys) {
|
|
862
|
+
if (keys.length === 0) {
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
console.warn(`[mcp\xB2] Deprecated config keys in ${filePath}: ${keys.join(", ")}. Run 'mcp-squared migrate' to clean up legacy settings.`);
|
|
866
|
+
}
|
|
867
|
+
|
|
539
868
|
class ConfigError extends Error {
|
|
540
869
|
cause;
|
|
541
870
|
constructor(message, cause) {
|
|
@@ -603,6 +932,7 @@ async function loadConfigFromPath(filePath, source) {
|
|
|
603
932
|
} catch (err) {
|
|
604
933
|
throw new ConfigParseError(filePath, err);
|
|
605
934
|
}
|
|
935
|
+
warnDeprecatedDynamicToolSurfaceKeys(filePath, getDeprecatedDynamicToolSurfaceKeys(rawConfig));
|
|
606
936
|
const migrated = migrateConfig(rawConfig);
|
|
607
937
|
let config;
|
|
608
938
|
try {
|
|
@@ -1465,60 +1795,68 @@ function cleanupExpiredTokens() {
|
|
|
1465
1795
|
}
|
|
1466
1796
|
}
|
|
1467
1797
|
}
|
|
1468
|
-
function validateConfirmationToken(token,
|
|
1798
|
+
function validateConfirmationToken(token, capability, action) {
|
|
1469
1799
|
cleanupExpiredTokens();
|
|
1470
1800
|
const confirmation = pendingConfirmations.get(token);
|
|
1471
1801
|
if (!confirmation) {
|
|
1472
1802
|
return false;
|
|
1473
1803
|
}
|
|
1474
|
-
if (confirmation.
|
|
1804
|
+
if (confirmation.capability !== capability || confirmation.action !== action) {
|
|
1475
1805
|
return false;
|
|
1476
1806
|
}
|
|
1477
1807
|
pendingConfirmations.delete(token);
|
|
1478
1808
|
return true;
|
|
1479
1809
|
}
|
|
1480
|
-
function createConfirmationToken(
|
|
1810
|
+
function createConfirmationToken(capability, action) {
|
|
1481
1811
|
cleanupExpiredTokens();
|
|
1482
1812
|
const token = generateToken();
|
|
1483
1813
|
pendingConfirmations.set(token, {
|
|
1484
|
-
|
|
1485
|
-
|
|
1814
|
+
capability,
|
|
1815
|
+
action,
|
|
1486
1816
|
createdAt: Date.now()
|
|
1487
1817
|
});
|
|
1488
1818
|
return token;
|
|
1489
1819
|
}
|
|
1490
1820
|
function evaluatePolicy(context, config) {
|
|
1491
|
-
const
|
|
1821
|
+
const capability = context.capability ?? context.serverKey;
|
|
1822
|
+
const action = context.action ?? context.toolName;
|
|
1823
|
+
const confirmationToken = context.confirmationToken;
|
|
1492
1824
|
const { block, confirm, allow } = config.security.tools;
|
|
1493
|
-
if (
|
|
1825
|
+
if (!capability || !action) {
|
|
1494
1826
|
return {
|
|
1495
1827
|
decision: "block",
|
|
1496
|
-
reason:
|
|
1828
|
+
reason: "Missing capability/action in security policy context"
|
|
1497
1829
|
};
|
|
1498
1830
|
}
|
|
1499
|
-
if (matchesAnyPattern(
|
|
1831
|
+
if (matchesAnyPattern(block, capability, action)) {
|
|
1832
|
+
return {
|
|
1833
|
+
decision: "block",
|
|
1834
|
+
reason: `Action "${action}" in capability "${capability}" is blocked by security policy`
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1837
|
+
if (matchesAnyPattern(allow, capability, action)) {
|
|
1500
1838
|
return {
|
|
1501
1839
|
decision: "allow",
|
|
1502
|
-
reason: `
|
|
1840
|
+
reason: `Action "${action}" is allowed by security policy`
|
|
1503
1841
|
};
|
|
1504
1842
|
}
|
|
1505
|
-
if (matchesAnyPattern(confirm,
|
|
1506
|
-
if (confirmationToken && validateConfirmationToken(confirmationToken,
|
|
1843
|
+
if (matchesAnyPattern(confirm, capability, action)) {
|
|
1844
|
+
if (confirmationToken && validateConfirmationToken(confirmationToken, capability, action)) {
|
|
1507
1845
|
return {
|
|
1508
1846
|
decision: "allow",
|
|
1509
|
-
reason: `
|
|
1847
|
+
reason: `Action "${action}" confirmed with valid token`
|
|
1510
1848
|
};
|
|
1511
1849
|
}
|
|
1512
|
-
const token = createConfirmationToken(
|
|
1850
|
+
const token = createConfirmationToken(capability, action);
|
|
1513
1851
|
return {
|
|
1514
1852
|
decision: "confirm",
|
|
1515
|
-
reason: `
|
|
1853
|
+
reason: `Action "${action}" in capability "${capability}" requires confirmation`,
|
|
1516
1854
|
confirmationToken: token
|
|
1517
1855
|
};
|
|
1518
1856
|
}
|
|
1519
1857
|
return {
|
|
1520
1858
|
decision: "block",
|
|
1521
|
-
reason: `
|
|
1859
|
+
reason: `Action "${action}" in capability "${capability}" is not in the allow or confirm list`
|
|
1522
1860
|
};
|
|
1523
1861
|
}
|
|
1524
1862
|
function compilePolicy(config) {
|
|
@@ -1677,6 +2015,26 @@ async function waitForProcessExit(proc, timeoutMs) {
|
|
|
1677
2015
|
}
|
|
1678
2016
|
|
|
1679
2017
|
// src/upstream/cataloger.ts
|
|
2018
|
+
var AUTH_ERROR_PATTERNS = [
|
|
2019
|
+
"invalid_token",
|
|
2020
|
+
"invalid token",
|
|
2021
|
+
"unauthorized",
|
|
2022
|
+
"no token provided",
|
|
2023
|
+
"no authorization",
|
|
2024
|
+
"api key",
|
|
2025
|
+
"api_key",
|
|
2026
|
+
"authentication required",
|
|
2027
|
+
"authentication failed",
|
|
2028
|
+
"invalid credentials",
|
|
2029
|
+
"invalid api",
|
|
2030
|
+
"forbidden",
|
|
2031
|
+
"access denied",
|
|
2032
|
+
"not authenticated"
|
|
2033
|
+
];
|
|
2034
|
+
function isAuthError(message) {
|
|
2035
|
+
const lower = message.toLowerCase();
|
|
2036
|
+
return AUTH_ERROR_PATTERNS.some((pattern) => lower.includes(pattern));
|
|
2037
|
+
}
|
|
1680
2038
|
function resolveEnvVars(env) {
|
|
1681
2039
|
const resolved = {};
|
|
1682
2040
|
for (const [key, value] of Object.entries(env)) {
|
|
@@ -1757,7 +2115,7 @@ class Cataloger {
|
|
|
1757
2115
|
if (err instanceof UnauthorizedError2 && connection.authProvider) {
|
|
1758
2116
|
if (connection.authProvider.isNonInteractive()) {
|
|
1759
2117
|
connection.authPending = true;
|
|
1760
|
-
connection.status = "
|
|
2118
|
+
connection.status = "needs_auth";
|
|
1761
2119
|
connection.error = `OAuth authorization required. Run: mcp-squared auth ${key}`;
|
|
1762
2120
|
return;
|
|
1763
2121
|
}
|
|
@@ -1781,8 +2139,9 @@ class Cataloger {
|
|
|
1781
2139
|
}));
|
|
1782
2140
|
connection.status = "connected";
|
|
1783
2141
|
} catch (err) {
|
|
1784
|
-
|
|
1785
|
-
connection.
|
|
2142
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
2143
|
+
connection.status = isAuthError(errorMsg) ? "needs_auth" : "error";
|
|
2144
|
+
connection.error = errorMsg;
|
|
1786
2145
|
try {
|
|
1787
2146
|
await this.cleanupConnection(connection);
|
|
1788
2147
|
} catch (_cleanupErr) {}
|
|
@@ -1944,13 +2303,14 @@ class Cataloger {
|
|
|
1944
2303
|
if (err instanceof UnauthorizedError2 && connection.authProvider) {
|
|
1945
2304
|
if (connection.authProvider.isNonInteractive()) {
|
|
1946
2305
|
connection.authPending = true;
|
|
1947
|
-
connection.status = "
|
|
2306
|
+
connection.status = "needs_auth";
|
|
1948
2307
|
connection.error = `OAuth authorization required. Run: mcp-squared auth ${key}`;
|
|
1949
2308
|
return;
|
|
1950
2309
|
}
|
|
1951
2310
|
}
|
|
1952
|
-
|
|
1953
|
-
connection.
|
|
2311
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
2312
|
+
connection.status = isAuthError(errorMsg) ? "needs_auth" : "error";
|
|
2313
|
+
connection.error = errorMsg;
|
|
1954
2314
|
}
|
|
1955
2315
|
}
|
|
1956
2316
|
async refreshAllTools() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-squared",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "MCP² (Mercury Control Plane) - A local-first meta-server and proxy for the Model Context Protocol",
|
|
5
5
|
"author": "aditzel",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"test": "bun test",
|
|
29
29
|
"test:fast": "SKIP_SLOW_TESTS=true bun test",
|
|
30
30
|
"test:watch": "bun test --watch",
|
|
31
|
+
"coverage:check": "bun run scripts/check-line-coverage.ts coverage/coverage-summary.txt 80",
|
|
31
32
|
"typecheck": "tsc --noEmit",
|
|
32
33
|
"lint": "biome check src tests scripts AGENTS.md CLAUDE.md WARP.md README.md CHANGELOG.md package.json biome.json tsconfig.json",
|
|
33
34
|
"release:check": "bun run audit && bun test && bun run build && bun run build:verify && bun run lint && bun run typecheck && bun pm pack --dry-run",
|
|
@@ -35,7 +36,8 @@
|
|
|
35
36
|
"lint:fix": "biome check --write .",
|
|
36
37
|
"format": "biome format --write .",
|
|
37
38
|
"clean": "rm -rf dist",
|
|
38
|
-
"safety:sim": "bun run agent_safety_kit/cost_model/simulate.ts --tasks agent_safety_kit/cost_model/tasks.csv --pricing agent_safety_kit/cost_model/pricing.csv --out agent_safety_kit/cost_model/report.md"
|
|
39
|
+
"safety:sim": "bun run agent_safety_kit/cost_model/simulate.ts --tasks agent_safety_kit/cost_model/tasks.csv --pricing agent_safety_kit/cost_model/pricing.csv --out agent_safety_kit/cost_model/report.md",
|
|
40
|
+
"eval:routing": "bun run scripts/eval-tool-routing.ts"
|
|
39
41
|
},
|
|
40
42
|
"devDependencies": {
|
|
41
43
|
"@biomejs/biome": "^2.4.4",
|
|
@@ -58,10 +60,13 @@
|
|
|
58
60
|
"url": "https://github.com/aditzel/mcp-squared"
|
|
59
61
|
},
|
|
60
62
|
"dependencies": {
|
|
63
|
+
"@hono/node-server": "^1.19.11",
|
|
61
64
|
"@huggingface/transformers": "^3.8.1",
|
|
62
65
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
63
66
|
"@opentui/core": "^0.1.82",
|
|
67
|
+
"hono": "^4.12.5",
|
|
64
68
|
"smol-toml": "^1.6.0",
|
|
69
|
+
"tar": "^7.5.10",
|
|
65
70
|
"yaml": "^2.8.2",
|
|
66
71
|
"zod": "^4.3.6"
|
|
67
72
|
},
|
|
@@ -70,10 +75,11 @@
|
|
|
70
75
|
"@opentelemetry/sdk-trace-base": "^1.30.1"
|
|
71
76
|
},
|
|
72
77
|
"overrides": {
|
|
78
|
+
"@hono/node-server": "^1.19.11",
|
|
73
79
|
"ajv": "^8.18.0",
|
|
74
80
|
"diff": "8.0.3",
|
|
75
|
-
"hono": "^4.
|
|
81
|
+
"hono": "^4.12.5",
|
|
76
82
|
"qs": "6.15.0",
|
|
77
|
-
"tar": "7.5.
|
|
83
|
+
"tar": "^7.5.10"
|
|
78
84
|
}
|
|
79
85
|
}
|