@jmylchreest/aide-plugin 0.0.56 → 0.0.57
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/package.json +1 -1
- package/src/core/mcp-sync.ts +92 -5
package/package.json
CHANGED
package/src/core/mcp-sync.ts
CHANGED
|
@@ -17,9 +17,11 @@
|
|
|
17
17
|
import {
|
|
18
18
|
existsSync,
|
|
19
19
|
readFileSync,
|
|
20
|
+
readdirSync,
|
|
20
21
|
writeFileSync,
|
|
21
22
|
mkdirSync,
|
|
22
23
|
statSync,
|
|
24
|
+
unlinkSync,
|
|
23
25
|
} from "fs";
|
|
24
26
|
import { join, dirname } from "path";
|
|
25
27
|
import { homedir } from "os";
|
|
@@ -31,6 +33,69 @@ import { homedir } from "os";
|
|
|
31
33
|
/** Platform identifier for the current assistant. */
|
|
32
34
|
export type McpPlatform = "claude-code" | "opencode";
|
|
33
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Discover MCP server names managed by installed Claude Code plugins.
|
|
38
|
+
*
|
|
39
|
+
* Scans plugin cache and marketplace directories for plugin.json files
|
|
40
|
+
* and returns the set of MCP server names they define. These servers
|
|
41
|
+
* must not be synced from assistant configs — doing so would override
|
|
42
|
+
* the plugin's own definition and bypass CLAUDE_PLUGIN_ROOT.
|
|
43
|
+
*/
|
|
44
|
+
function getPluginManagedServers(): Set<string> {
|
|
45
|
+
const names = new Set<string>();
|
|
46
|
+
const pluginsDir = join(homedir(), ".claude", "plugins");
|
|
47
|
+
|
|
48
|
+
// Scan both cache (installed plugins) and marketplaces (git-cloned plugins)
|
|
49
|
+
const searchDirs = [
|
|
50
|
+
join(pluginsDir, "cache"),
|
|
51
|
+
join(pluginsDir, "marketplaces"),
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
for (const baseDir of searchDirs) {
|
|
55
|
+
if (!existsSync(baseDir)) continue;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
// Walk up to 3 levels deep to find .claude-plugin/plugin.json
|
|
59
|
+
// cache: <marketplace>/<plugin>/<version>/.claude-plugin/plugin.json
|
|
60
|
+
// marketplaces: <name>/.claude-plugin/plugin.json
|
|
61
|
+
const findPluginJsons = (dir: string, depth: number): string[] => {
|
|
62
|
+
if (depth > 3) return [];
|
|
63
|
+
const results: string[] = [];
|
|
64
|
+
const pluginJson = join(dir, ".claude-plugin", "plugin.json");
|
|
65
|
+
if (existsSync(pluginJson)) results.push(pluginJson);
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
69
|
+
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
70
|
+
results.push(...findPluginJsons(join(dir, entry.name), depth + 1));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
// Permission or read error — skip
|
|
75
|
+
}
|
|
76
|
+
return results;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
for (const pluginJsonPath of findPluginJsons(baseDir, 0)) {
|
|
80
|
+
try {
|
|
81
|
+
const content = JSON.parse(readFileSync(pluginJsonPath, "utf-8"));
|
|
82
|
+
if (content.mcpServers && typeof content.mcpServers === "object") {
|
|
83
|
+
for (const serverName of Object.keys(content.mcpServers)) {
|
|
84
|
+
names.add(serverName);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
} catch {
|
|
88
|
+
// Skip unparseable plugin.json files
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
// Directory read error — skip
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return names;
|
|
97
|
+
}
|
|
98
|
+
|
|
34
99
|
/** Scope level for config files. */
|
|
35
100
|
export type McpScope = "user" | "project";
|
|
36
101
|
|
|
@@ -782,7 +847,7 @@ function syncScope(
|
|
|
782
847
|
if (!bestPresent) result.skipped++;
|
|
783
848
|
}
|
|
784
849
|
|
|
785
|
-
// Write to aide canonical config (if changed)
|
|
850
|
+
// Write to aide canonical config (if changed) — includes ALL servers
|
|
786
851
|
const sortedStringify = (obj: object) =>
|
|
787
852
|
JSON.stringify(obj, Object.keys(obj).sort());
|
|
788
853
|
const aideChanged =
|
|
@@ -793,21 +858,43 @@ function syncScope(
|
|
|
793
858
|
result.modified = true;
|
|
794
859
|
}
|
|
795
860
|
|
|
861
|
+
// For Claude Code, exclude servers managed by plugins — these are
|
|
862
|
+
// defined in plugin.json and must not be written to assistant configs
|
|
863
|
+
// (doing so overrides the plugin's definition and bypasses
|
|
864
|
+
// CLAUDE_PLUGIN_ROOT). The aide canonical config keeps them so they
|
|
865
|
+
// can still sync to non-plugin assistants like OpenCode.
|
|
866
|
+
let assistantServers = finalServers;
|
|
867
|
+
if (platform === "claude-code") {
|
|
868
|
+
const pluginManaged = getPluginManagedServers();
|
|
869
|
+
if (pluginManaged.size > 0) {
|
|
870
|
+
assistantServers = { ...finalServers };
|
|
871
|
+
for (const name of pluginManaged) {
|
|
872
|
+
delete assistantServers[name];
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
796
877
|
// Write to current assistant's config
|
|
797
878
|
const assistantPaths = getAssistantWritePaths(platform, scope, cwd);
|
|
798
879
|
for (const p of assistantPaths) {
|
|
799
880
|
const existingAssistant = readAssistantConfig(platform, p);
|
|
800
881
|
const assistantChanged =
|
|
801
|
-
sortedStringify(existingAssistant) !== sortedStringify(
|
|
882
|
+
sortedStringify(existingAssistant) !== sortedStringify(assistantServers);
|
|
802
883
|
|
|
803
884
|
if (assistantChanged) {
|
|
804
|
-
|
|
885
|
+
if (Object.keys(assistantServers).length === 0 && existsSync(p)) {
|
|
886
|
+
// No servers to write — remove the file rather than leaving
|
|
887
|
+
// an empty mcpServers block that the assistant still loads.
|
|
888
|
+
try { unlinkSync(p); } catch { /* ignore */ }
|
|
889
|
+
} else if (Object.keys(assistantServers).length > 0) {
|
|
890
|
+
writeAssistantConfig(platform, p, assistantServers);
|
|
891
|
+
}
|
|
805
892
|
result.modified = true;
|
|
806
893
|
}
|
|
807
894
|
}
|
|
808
895
|
|
|
809
|
-
result.serversWritten = Object.keys(
|
|
810
|
-
result.serverNames = Object.keys(
|
|
896
|
+
result.serversWritten = Object.keys(assistantServers).length;
|
|
897
|
+
result.serverNames = Object.keys(assistantServers);
|
|
811
898
|
|
|
812
899
|
return result;
|
|
813
900
|
}
|