mcp-squared 0.4.0 → 0.7.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 +61 -21
- package/dist/index.js +1629 -467
- package/dist/tui/config.js +390 -23
- package/package.json +9 -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,6 +703,18 @@ 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
|
+
});
|
|
437
718
|
var PreferredNamespacesByIntentSchema = z.object({
|
|
438
719
|
codeSearch: z.array(z.string().min(1)).default([])
|
|
439
720
|
});
|
|
@@ -455,6 +736,14 @@ var LoggingSchema = z.object({
|
|
|
455
736
|
var EmbeddingsSchema = z.object({
|
|
456
737
|
enabled: z.boolean().default(false)
|
|
457
738
|
});
|
|
739
|
+
var ResponseResourceSchema = z.object({
|
|
740
|
+
enabled: z.boolean().default(false),
|
|
741
|
+
thresholdBytes: z.number().int().min(1024).default(51200),
|
|
742
|
+
maxInlineLines: z.number().int().min(1).default(20),
|
|
743
|
+
maxResources: z.number().int().min(1).default(100),
|
|
744
|
+
ttlMs: z.number().int().min(0).default(600000)
|
|
745
|
+
});
|
|
746
|
+
var DEFAULT_RESPONSE_RESOURCE_CONFIG = ResponseResourceSchema.parse({});
|
|
458
747
|
var SelectionCacheSchema = z.object({
|
|
459
748
|
enabled: z.boolean().default(true),
|
|
460
749
|
minCooccurrenceThreshold: z.number().int().min(1).default(2),
|
|
@@ -471,10 +760,17 @@ var OperationsSchema = z.object({
|
|
|
471
760
|
index: IndexSchema.default({ refreshIntervalMs: 30000 }),
|
|
472
761
|
logging: LoggingSchema.default({ level: "info" }),
|
|
473
762
|
embeddings: EmbeddingsSchema.default({ enabled: false }),
|
|
763
|
+
responseResource: ResponseResourceSchema.default(DEFAULT_RESPONSE_RESOURCE_CONFIG),
|
|
474
764
|
selectionCache: SelectionCacheSchema.default({
|
|
475
765
|
enabled: true,
|
|
476
766
|
minCooccurrenceThreshold: 2,
|
|
477
767
|
maxBundleSuggestions: 3
|
|
768
|
+
}),
|
|
769
|
+
dynamicToolSurface: DynamicToolSurfaceSchema.default({
|
|
770
|
+
inference: "heuristic_with_overrides",
|
|
771
|
+
refresh: "on_connect",
|
|
772
|
+
capabilityOverrides: {},
|
|
773
|
+
semanticConfidenceThreshold: 0.45
|
|
478
774
|
})
|
|
479
775
|
}).default({
|
|
480
776
|
findTools: {
|
|
@@ -487,10 +783,17 @@ var OperationsSchema = z.object({
|
|
|
487
783
|
index: { refreshIntervalMs: 30000 },
|
|
488
784
|
logging: { level: "info" },
|
|
489
785
|
embeddings: { enabled: false },
|
|
786
|
+
responseResource: DEFAULT_RESPONSE_RESOURCE_CONFIG,
|
|
490
787
|
selectionCache: {
|
|
491
788
|
enabled: true,
|
|
492
789
|
minCooccurrenceThreshold: 2,
|
|
493
790
|
maxBundleSuggestions: 3
|
|
791
|
+
},
|
|
792
|
+
dynamicToolSurface: {
|
|
793
|
+
inference: "heuristic_with_overrides",
|
|
794
|
+
refresh: "on_connect",
|
|
795
|
+
capabilityOverrides: {},
|
|
796
|
+
semanticConfidenceThreshold: 0.45
|
|
494
797
|
}
|
|
495
798
|
});
|
|
496
799
|
var ConfigSchema = z.object({
|
|
@@ -544,6 +847,34 @@ function migrateV0ToV1(config) {
|
|
|
544
847
|
|
|
545
848
|
// src/config/load.ts
|
|
546
849
|
init_paths();
|
|
850
|
+
function isRecord(value) {
|
|
851
|
+
return typeof value === "object" && value !== null;
|
|
852
|
+
}
|
|
853
|
+
function getDeprecatedDynamicToolSurfaceKeys(raw) {
|
|
854
|
+
const operations = raw["operations"];
|
|
855
|
+
if (!isRecord(operations)) {
|
|
856
|
+
return [];
|
|
857
|
+
}
|
|
858
|
+
const dynamicToolSurface = operations["dynamicToolSurface"];
|
|
859
|
+
if (!isRecord(dynamicToolSurface)) {
|
|
860
|
+
return [];
|
|
861
|
+
}
|
|
862
|
+
const deprecated = [];
|
|
863
|
+
if ("mode" in dynamicToolSurface) {
|
|
864
|
+
deprecated.push("operations.dynamicToolSurface.mode");
|
|
865
|
+
}
|
|
866
|
+
if ("naming" in dynamicToolSurface) {
|
|
867
|
+
deprecated.push("operations.dynamicToolSurface.naming");
|
|
868
|
+
}
|
|
869
|
+
return deprecated;
|
|
870
|
+
}
|
|
871
|
+
function warnDeprecatedDynamicToolSurfaceKeys(filePath, keys) {
|
|
872
|
+
if (keys.length === 0) {
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
console.warn(`[mcp\xB2] Deprecated config keys in ${filePath}: ${keys.join(", ")}. Run 'mcp-squared migrate' to clean up legacy settings.`);
|
|
876
|
+
}
|
|
877
|
+
|
|
547
878
|
class ConfigError extends Error {
|
|
548
879
|
cause;
|
|
549
880
|
constructor(message, cause) {
|
|
@@ -611,6 +942,7 @@ async function loadConfigFromPath(filePath, source) {
|
|
|
611
942
|
} catch (err) {
|
|
612
943
|
throw new ConfigParseError(filePath, err);
|
|
613
944
|
}
|
|
945
|
+
warnDeprecatedDynamicToolSurfaceKeys(filePath, getDeprecatedDynamicToolSurfaceKeys(rawConfig));
|
|
614
946
|
const migrated = migrateConfig(rawConfig);
|
|
615
947
|
let config;
|
|
616
948
|
try {
|
|
@@ -1473,60 +1805,68 @@ function cleanupExpiredTokens() {
|
|
|
1473
1805
|
}
|
|
1474
1806
|
}
|
|
1475
1807
|
}
|
|
1476
|
-
function validateConfirmationToken(token,
|
|
1808
|
+
function validateConfirmationToken(token, capability, action) {
|
|
1477
1809
|
cleanupExpiredTokens();
|
|
1478
1810
|
const confirmation = pendingConfirmations.get(token);
|
|
1479
1811
|
if (!confirmation) {
|
|
1480
1812
|
return false;
|
|
1481
1813
|
}
|
|
1482
|
-
if (confirmation.
|
|
1814
|
+
if (confirmation.capability !== capability || confirmation.action !== action) {
|
|
1483
1815
|
return false;
|
|
1484
1816
|
}
|
|
1485
1817
|
pendingConfirmations.delete(token);
|
|
1486
1818
|
return true;
|
|
1487
1819
|
}
|
|
1488
|
-
function createConfirmationToken(
|
|
1820
|
+
function createConfirmationToken(capability, action) {
|
|
1489
1821
|
cleanupExpiredTokens();
|
|
1490
1822
|
const token = generateToken();
|
|
1491
1823
|
pendingConfirmations.set(token, {
|
|
1492
|
-
|
|
1493
|
-
|
|
1824
|
+
capability,
|
|
1825
|
+
action,
|
|
1494
1826
|
createdAt: Date.now()
|
|
1495
1827
|
});
|
|
1496
1828
|
return token;
|
|
1497
1829
|
}
|
|
1498
1830
|
function evaluatePolicy(context, config) {
|
|
1499
|
-
const
|
|
1831
|
+
const capability = context.capability ?? context.serverKey;
|
|
1832
|
+
const action = context.action ?? context.toolName;
|
|
1833
|
+
const confirmationToken = context.confirmationToken;
|
|
1500
1834
|
const { block, confirm, allow } = config.security.tools;
|
|
1501
|
-
if (
|
|
1835
|
+
if (!capability || !action) {
|
|
1502
1836
|
return {
|
|
1503
1837
|
decision: "block",
|
|
1504
|
-
reason:
|
|
1838
|
+
reason: "Missing capability/action in security policy context"
|
|
1505
1839
|
};
|
|
1506
1840
|
}
|
|
1507
|
-
if (matchesAnyPattern(
|
|
1841
|
+
if (matchesAnyPattern(block, capability, action)) {
|
|
1842
|
+
return {
|
|
1843
|
+
decision: "block",
|
|
1844
|
+
reason: `Action "${action}" in capability "${capability}" is blocked by security policy`
|
|
1845
|
+
};
|
|
1846
|
+
}
|
|
1847
|
+
if (matchesAnyPattern(allow, capability, action)) {
|
|
1508
1848
|
return {
|
|
1509
1849
|
decision: "allow",
|
|
1510
|
-
reason: `
|
|
1850
|
+
reason: `Action "${action}" is allowed by security policy`
|
|
1511
1851
|
};
|
|
1512
1852
|
}
|
|
1513
|
-
if (matchesAnyPattern(confirm,
|
|
1514
|
-
if (confirmationToken && validateConfirmationToken(confirmationToken,
|
|
1853
|
+
if (matchesAnyPattern(confirm, capability, action)) {
|
|
1854
|
+
if (confirmationToken && validateConfirmationToken(confirmationToken, capability, action)) {
|
|
1515
1855
|
return {
|
|
1516
1856
|
decision: "allow",
|
|
1517
|
-
reason: `
|
|
1857
|
+
reason: `Action "${action}" confirmed with valid token`
|
|
1518
1858
|
};
|
|
1519
1859
|
}
|
|
1520
|
-
const token = createConfirmationToken(
|
|
1860
|
+
const token = createConfirmationToken(capability, action);
|
|
1521
1861
|
return {
|
|
1522
1862
|
decision: "confirm",
|
|
1523
|
-
reason: `
|
|
1863
|
+
reason: `Action "${action}" in capability "${capability}" requires confirmation`,
|
|
1524
1864
|
confirmationToken: token
|
|
1525
1865
|
};
|
|
1526
1866
|
}
|
|
1527
1867
|
return {
|
|
1528
1868
|
decision: "block",
|
|
1529
|
-
reason: `
|
|
1869
|
+
reason: `Action "${action}" in capability "${capability}" is not in the allow or confirm list`
|
|
1530
1870
|
};
|
|
1531
1871
|
}
|
|
1532
1872
|
function compilePolicy(config) {
|
|
@@ -1685,6 +2025,26 @@ async function waitForProcessExit(proc, timeoutMs) {
|
|
|
1685
2025
|
}
|
|
1686
2026
|
|
|
1687
2027
|
// src/upstream/cataloger.ts
|
|
2028
|
+
var AUTH_ERROR_PATTERNS = [
|
|
2029
|
+
"invalid_token",
|
|
2030
|
+
"invalid token",
|
|
2031
|
+
"unauthorized",
|
|
2032
|
+
"no token provided",
|
|
2033
|
+
"no authorization",
|
|
2034
|
+
"api key",
|
|
2035
|
+
"api_key",
|
|
2036
|
+
"authentication required",
|
|
2037
|
+
"authentication failed",
|
|
2038
|
+
"invalid credentials",
|
|
2039
|
+
"invalid api",
|
|
2040
|
+
"forbidden",
|
|
2041
|
+
"access denied",
|
|
2042
|
+
"not authenticated"
|
|
2043
|
+
];
|
|
2044
|
+
function isAuthError(message) {
|
|
2045
|
+
const lower = message.toLowerCase();
|
|
2046
|
+
return AUTH_ERROR_PATTERNS.some((pattern) => lower.includes(pattern));
|
|
2047
|
+
}
|
|
1688
2048
|
function resolveEnvVars(env) {
|
|
1689
2049
|
const resolved = {};
|
|
1690
2050
|
for (const [key, value] of Object.entries(env)) {
|
|
@@ -1765,7 +2125,7 @@ class Cataloger {
|
|
|
1765
2125
|
if (err instanceof UnauthorizedError2 && connection.authProvider) {
|
|
1766
2126
|
if (connection.authProvider.isNonInteractive()) {
|
|
1767
2127
|
connection.authPending = true;
|
|
1768
|
-
connection.status = "
|
|
2128
|
+
connection.status = "needs_auth";
|
|
1769
2129
|
connection.error = `OAuth authorization required. Run: mcp-squared auth ${key}`;
|
|
1770
2130
|
return;
|
|
1771
2131
|
}
|
|
@@ -1789,8 +2149,9 @@ class Cataloger {
|
|
|
1789
2149
|
}));
|
|
1790
2150
|
connection.status = "connected";
|
|
1791
2151
|
} catch (err) {
|
|
1792
|
-
|
|
1793
|
-
connection.
|
|
2152
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
2153
|
+
connection.status = isAuthError(errorMsg) ? "needs_auth" : "error";
|
|
2154
|
+
connection.error = errorMsg;
|
|
1794
2155
|
try {
|
|
1795
2156
|
await this.cleanupConnection(connection);
|
|
1796
2157
|
} catch (_cleanupErr) {}
|
|
@@ -1924,10 +2285,15 @@ class Cataloger {
|
|
|
1924
2285
|
name: bareToolName,
|
|
1925
2286
|
arguments: args
|
|
1926
2287
|
});
|
|
1927
|
-
|
|
2288
|
+
const response = {
|
|
1928
2289
|
content: callResult.content,
|
|
1929
2290
|
isError: callResult.isError
|
|
1930
2291
|
};
|
|
2292
|
+
const sc = callResult["structuredContent"];
|
|
2293
|
+
if (sc != null && typeof sc === "object" && !Array.isArray(sc)) {
|
|
2294
|
+
response.structuredContent = sc;
|
|
2295
|
+
}
|
|
2296
|
+
return response;
|
|
1931
2297
|
}
|
|
1932
2298
|
async refreshTools(key) {
|
|
1933
2299
|
const connection = this.connections.get(key);
|
|
@@ -1952,13 +2318,14 @@ class Cataloger {
|
|
|
1952
2318
|
if (err instanceof UnauthorizedError2 && connection.authProvider) {
|
|
1953
2319
|
if (connection.authProvider.isNonInteractive()) {
|
|
1954
2320
|
connection.authPending = true;
|
|
1955
|
-
connection.status = "
|
|
2321
|
+
connection.status = "needs_auth";
|
|
1956
2322
|
connection.error = `OAuth authorization required. Run: mcp-squared auth ${key}`;
|
|
1957
2323
|
return;
|
|
1958
2324
|
}
|
|
1959
2325
|
}
|
|
1960
|
-
|
|
1961
|
-
connection.
|
|
2326
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
2327
|
+
connection.status = isAuthError(errorMsg) ? "needs_auth" : "error";
|
|
2328
|
+
connection.error = errorMsg;
|
|
1962
2329
|
}
|
|
1963
2330
|
}
|
|
1964
2331
|
async refreshAllTools() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-squared",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.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,7 +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
|
+
"coverage:check": "bun run scripts/check-line-coverage.ts coverage/coverage-summary.txt coverage/lcov.info 80",
|
|
32
32
|
"typecheck": "tsc --noEmit",
|
|
33
33
|
"lint": "biome check src tests scripts AGENTS.md CLAUDE.md WARP.md README.md CHANGELOG.md package.json biome.json tsconfig.json",
|
|
34
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",
|
|
@@ -60,10 +60,13 @@
|
|
|
60
60
|
"url": "https://github.com/aditzel/mcp-squared"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
+
"@hono/node-server": "^1.19.11",
|
|
63
64
|
"@huggingface/transformers": "^3.8.1",
|
|
64
65
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
65
66
|
"@opentui/core": "^0.1.82",
|
|
67
|
+
"hono": "^4.12.5",
|
|
66
68
|
"smol-toml": "^1.6.0",
|
|
69
|
+
"tar": "^7.5.10",
|
|
67
70
|
"yaml": "^2.8.2",
|
|
68
71
|
"zod": "^4.3.6"
|
|
69
72
|
},
|
|
@@ -72,10 +75,12 @@
|
|
|
72
75
|
"@opentelemetry/sdk-trace-base": "^1.30.1"
|
|
73
76
|
},
|
|
74
77
|
"overrides": {
|
|
78
|
+
"@hono/node-server": "^1.19.11",
|
|
75
79
|
"ajv": "^8.18.0",
|
|
76
80
|
"diff": "8.0.3",
|
|
77
|
-
"
|
|
81
|
+
"express-rate-limit": "8.2.2",
|
|
82
|
+
"hono": "^4.12.5",
|
|
78
83
|
"qs": "6.15.0",
|
|
79
|
-
"tar": "7.5.
|
|
84
|
+
"tar": "^7.5.10"
|
|
80
85
|
}
|
|
81
86
|
}
|