pi-mcp-adapter 1.2.2 → 1.3.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/CHANGELOG.md +6 -0
- package/README.md +0 -1
- package/index.ts +100 -65
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.3.0] - 2026-01-19
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **Parallel server connections** - All MCP servers now connect in parallel on startup instead of sequentially, significantly faster with many servers
|
|
13
|
+
|
|
8
14
|
## [1.2.2] - 2026-01-19
|
|
9
15
|
|
|
10
16
|
### Fixed
|
package/README.md
CHANGED
package/index.ts
CHANGED
|
@@ -24,6 +24,27 @@ interface McpExtensionState {
|
|
|
24
24
|
config: McpConfig;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/** Run async tasks with concurrency limit */
|
|
28
|
+
async function parallelLimit<T, R>(
|
|
29
|
+
items: T[],
|
|
30
|
+
limit: number,
|
|
31
|
+
fn: (item: T) => Promise<R>
|
|
32
|
+
): Promise<R[]> {
|
|
33
|
+
const results: R[] = [];
|
|
34
|
+
let index = 0;
|
|
35
|
+
|
|
36
|
+
async function worker() {
|
|
37
|
+
while (index < items.length) {
|
|
38
|
+
const i = index++;
|
|
39
|
+
results[i] = await fn(items[i]);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const workers = Array(Math.min(limit, items.length)).fill(null).map(() => worker());
|
|
44
|
+
await Promise.all(workers);
|
|
45
|
+
return results;
|
|
46
|
+
}
|
|
47
|
+
|
|
27
48
|
export default function mcpAdapter(pi: ExtensionAPI) {
|
|
28
49
|
let state: McpExtensionState | null = null;
|
|
29
50
|
let initPromise: Promise<McpExtensionState> | null = null;
|
|
@@ -586,78 +607,92 @@ async function initializeMcp(
|
|
|
586
607
|
return { manager, lifecycle, registeredTools, toolMetadata, config };
|
|
587
608
|
}
|
|
588
609
|
|
|
589
|
-
|
|
610
|
+
if (ctx.hasUI) {
|
|
611
|
+
ctx.ui.setStatus("mcp", `Connecting to ${serverEntries.length} servers...`);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Connect to all servers in parallel (max 10 concurrent)
|
|
615
|
+
const results = await parallelLimit(serverEntries, 10, async ([name, definition]) => {
|
|
590
616
|
try {
|
|
591
|
-
if (ctx.hasUI) {
|
|
592
|
-
ctx.ui.setStatus("mcp", `Connecting to ${name}...`);
|
|
593
|
-
}
|
|
594
|
-
|
|
595
617
|
const connection = await manager.connect(name, definition);
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
// Collect tool names (NOT registered with Pi - only mcp proxy is registered)
|
|
599
|
-
const { collected: toolNames, failed: failedTools } = collectToolNames(
|
|
600
|
-
connection.tools,
|
|
601
|
-
{ serverName: name, prefix }
|
|
602
|
-
);
|
|
603
|
-
|
|
604
|
-
// Collect resource tool names (if enabled)
|
|
605
|
-
if (definition.exposeResources !== false && connection.resources.length > 0) {
|
|
606
|
-
const resourceToolNames = collectResourceToolNames(
|
|
607
|
-
connection.resources,
|
|
608
|
-
{ serverName: name, prefix }
|
|
609
|
-
);
|
|
610
|
-
toolNames.push(...resourceToolNames);
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
registeredTools.set(name, toolNames);
|
|
614
|
-
|
|
615
|
-
// Build tool metadata for searching (include inputSchema for describe/errors)
|
|
616
|
-
const metadata: ToolMetadata[] = connection.tools.map(tool => ({
|
|
617
|
-
name: formatToolName(tool.name, name, prefix),
|
|
618
|
-
originalName: tool.name,
|
|
619
|
-
description: tool.description ?? "",
|
|
620
|
-
inputSchema: tool.inputSchema,
|
|
621
|
-
}));
|
|
622
|
-
// Add resource tools to metadata
|
|
623
|
-
for (const resource of connection.resources) {
|
|
624
|
-
if (definition.exposeResources !== false) {
|
|
625
|
-
const baseName = `get_${resourceNameToToolName(resource.name)}`;
|
|
626
|
-
metadata.push({
|
|
627
|
-
name: formatToolName(baseName, name, prefix),
|
|
628
|
-
originalName: baseName,
|
|
629
|
-
description: resource.description ?? `Read resource: ${resource.uri}`,
|
|
630
|
-
resourceUri: resource.uri,
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
toolMetadata.set(name, metadata);
|
|
635
|
-
|
|
636
|
-
// Mark keep-alive servers
|
|
637
|
-
if (definition.lifecycle === "keep-alive") {
|
|
638
|
-
lifecycle.markKeepAlive(name, definition);
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
if (failedTools.length > 0 && ctx.hasUI) {
|
|
642
|
-
ctx.ui.notify(
|
|
643
|
-
`MCP: ${name} - ${failedTools.length} tools skipped`,
|
|
644
|
-
"warning"
|
|
645
|
-
);
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
if (ctx.hasUI) {
|
|
649
|
-
ctx.ui.notify(
|
|
650
|
-
`MCP: ${name} connected (${connection.tools.length} tools, ${connection.resources.length} resources)`,
|
|
651
|
-
"info"
|
|
652
|
-
);
|
|
653
|
-
}
|
|
618
|
+
return { name, definition, connection, error: null };
|
|
654
619
|
} catch (error) {
|
|
655
620
|
const message = error instanceof Error ? error.message : String(error);
|
|
621
|
+
return { name, definition, connection: null, error: message };
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
const prefix = config.settings?.toolPrefix ?? "server";
|
|
625
|
+
|
|
626
|
+
// Process results
|
|
627
|
+
for (const { name, definition, connection, error } of results) {
|
|
628
|
+
if (error || !connection) {
|
|
656
629
|
if (ctx.hasUI) {
|
|
657
|
-
ctx.ui.notify(`MCP: Failed to connect to ${name}: ${
|
|
630
|
+
ctx.ui.notify(`MCP: Failed to connect to ${name}: ${error}`, "error");
|
|
658
631
|
}
|
|
659
|
-
console.error(`MCP: Failed to connect to ${name}: ${
|
|
632
|
+
console.error(`MCP: Failed to connect to ${name}: ${error}`);
|
|
633
|
+
continue;
|
|
660
634
|
}
|
|
635
|
+
|
|
636
|
+
// Collect tool names (NOT registered with Pi - only mcp proxy is registered)
|
|
637
|
+
const { collected: toolNames, failed: failedTools } = collectToolNames(
|
|
638
|
+
connection.tools,
|
|
639
|
+
{ serverName: name, prefix }
|
|
640
|
+
);
|
|
641
|
+
|
|
642
|
+
// Collect resource tool names (if enabled)
|
|
643
|
+
if (definition.exposeResources !== false && connection.resources.length > 0) {
|
|
644
|
+
const resourceToolNames = collectResourceToolNames(
|
|
645
|
+
connection.resources,
|
|
646
|
+
{ serverName: name, prefix }
|
|
647
|
+
);
|
|
648
|
+
toolNames.push(...resourceToolNames);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
registeredTools.set(name, toolNames);
|
|
652
|
+
|
|
653
|
+
// Build tool metadata for searching (include inputSchema for describe/errors)
|
|
654
|
+
const metadata: ToolMetadata[] = connection.tools.map(tool => ({
|
|
655
|
+
name: formatToolName(tool.name, name, prefix),
|
|
656
|
+
originalName: tool.name,
|
|
657
|
+
description: tool.description ?? "",
|
|
658
|
+
inputSchema: tool.inputSchema,
|
|
659
|
+
}));
|
|
660
|
+
// Add resource tools to metadata
|
|
661
|
+
for (const resource of connection.resources) {
|
|
662
|
+
if (definition.exposeResources !== false) {
|
|
663
|
+
const baseName = `get_${resourceNameToToolName(resource.name)}`;
|
|
664
|
+
metadata.push({
|
|
665
|
+
name: formatToolName(baseName, name, prefix),
|
|
666
|
+
originalName: baseName,
|
|
667
|
+
description: resource.description ?? `Read resource: ${resource.uri}`,
|
|
668
|
+
resourceUri: resource.uri,
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
toolMetadata.set(name, metadata);
|
|
673
|
+
|
|
674
|
+
// Mark keep-alive servers
|
|
675
|
+
if (definition.lifecycle === "keep-alive") {
|
|
676
|
+
lifecycle.markKeepAlive(name, definition);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (failedTools.length > 0 && ctx.hasUI) {
|
|
680
|
+
ctx.ui.notify(
|
|
681
|
+
`MCP: ${name} - ${failedTools.length} tools skipped`,
|
|
682
|
+
"warning"
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Summary notification
|
|
688
|
+
const connectedCount = results.filter(r => r.connection).length;
|
|
689
|
+
const failedCount = results.filter(r => r.error).length;
|
|
690
|
+
if (ctx.hasUI && connectedCount > 0) {
|
|
691
|
+
const totalTools = [...registeredTools.values()].flat().length;
|
|
692
|
+
const msg = failedCount > 0
|
|
693
|
+
? `MCP: ${connectedCount}/${serverEntries.length} servers connected (${totalTools} tools)`
|
|
694
|
+
: `MCP: ${connectedCount} servers connected (${totalTools} tools)`;
|
|
695
|
+
ctx.ui.notify(msg, "info");
|
|
661
696
|
}
|
|
662
697
|
|
|
663
698
|
// Start health checks for keep-alive servers
|