@phnx-labs/agents-cli 1.14.3 → 1.14.4
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/dist/browser.d.ts +2 -0
- package/dist/browser.js +7 -0
- package/dist/commands/browser.d.ts +1 -0
- package/dist/commands/browser.js +4 -0
- package/dist/commands/teams.js +25 -3
- package/dist/commands/view.js +1 -1
- package/dist/index.js +0 -0
- package/dist/lib/migrate.js +46 -0
- package/dist/lib/resources/commands.d.ts +46 -0
- package/dist/lib/resources/commands.js +208 -0
- package/dist/lib/resources/hooks.d.ts +12 -0
- package/dist/lib/resources/hooks.js +136 -0
- package/dist/lib/resources/index.d.ts +36 -0
- package/dist/lib/resources/index.js +69 -0
- package/dist/lib/resources/mcp.d.ts +34 -0
- package/dist/lib/resources/mcp.js +483 -0
- package/dist/lib/resources/permissions.d.ts +13 -0
- package/dist/lib/resources/permissions.js +184 -0
- package/dist/lib/resources/rules.d.ts +43 -0
- package/dist/lib/resources/rules.js +146 -0
- package/dist/lib/resources/skills.d.ts +37 -0
- package/dist/lib/resources/skills.js +238 -0
- package/dist/lib/resources/subagents.d.ts +46 -0
- package/dist/lib/resources/subagents.js +198 -0
- package/dist/lib/resources/types.d.ts +82 -0
- package/dist/lib/resources/types.js +8 -0
- package/dist/lib/state.js +3 -5
- package/dist/lib/teams/registry.d.ts +4 -0
- package/dist/lib/teams/registry.js +4 -0
- package/dist/lib/versions.d.ts +2 -1
- package/dist/lib/versions.js +20 -14
- package/package.json +3 -2
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP resource handler - lists, resolves, and syncs MCP server configs across layers.
|
|
3
|
+
*
|
|
4
|
+
* MCP servers are stored as YAML files in mcp/ directories:
|
|
5
|
+
* ~/.agents-system/mcp/ (system)
|
|
6
|
+
* ~/.agents/mcp/ (user)
|
|
7
|
+
* .agents/mcp/ (project)
|
|
8
|
+
*
|
|
9
|
+
* Resolution: project > user > system (higher layer wins on name conflict).
|
|
10
|
+
* Sync writes into agent-specific config files (settings.json, config.toml, etc).
|
|
11
|
+
*/
|
|
12
|
+
import type { AgentId, ResourceHandler } from './types.js';
|
|
13
|
+
/**
|
|
14
|
+
* MCP server item as stored in mcp/*.yaml files.
|
|
15
|
+
*/
|
|
16
|
+
export interface McpItem {
|
|
17
|
+
name: string;
|
|
18
|
+
transport: 'stdio' | 'http' | 'sse';
|
|
19
|
+
command?: string;
|
|
20
|
+
args?: string[];
|
|
21
|
+
env?: Record<string, string>;
|
|
22
|
+
url?: string;
|
|
23
|
+
headers?: Record<string, string>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get the config file path for MCP for a given agent.
|
|
27
|
+
* Different agents use different config formats and locations.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getMcpConfigPath(agent: AgentId, versionHome: string): string | null;
|
|
30
|
+
/**
|
|
31
|
+
* MCP resource handler implementing ResourceHandler<McpItem>.
|
|
32
|
+
*/
|
|
33
|
+
export declare const McpHandler: ResourceHandler<McpItem>;
|
|
34
|
+
export default McpHandler;
|
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP resource handler - lists, resolves, and syncs MCP server configs across layers.
|
|
3
|
+
*
|
|
4
|
+
* MCP servers are stored as YAML files in mcp/ directories:
|
|
5
|
+
* ~/.agents-system/mcp/ (system)
|
|
6
|
+
* ~/.agents/mcp/ (user)
|
|
7
|
+
* .agents/mcp/ (project)
|
|
8
|
+
*
|
|
9
|
+
* Resolution: project > user > system (higher layer wins on name conflict).
|
|
10
|
+
* Sync writes into agent-specific config files (settings.json, config.toml, etc).
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from 'fs';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
import * as yaml from 'yaml';
|
|
15
|
+
import * as TOML from 'smol-toml';
|
|
16
|
+
import { getSystemMcpDir, getUserMcpDir, getProjectAgentsDir, getEnabledExtraRepos, } from '../state.js';
|
|
17
|
+
/** Agents from resources/types.ts that support MCP. */
|
|
18
|
+
const MCP_CAPABLE_AGENTS = ['claude', 'codex', 'gemini', 'cursor', 'opencode', 'openclaw'];
|
|
19
|
+
/**
|
|
20
|
+
* Parse an MCP YAML file into an McpItem.
|
|
21
|
+
*/
|
|
22
|
+
function parseMcpYaml(filePath) {
|
|
23
|
+
if (!fs.existsSync(filePath)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
28
|
+
const parsed = yaml.parse(content);
|
|
29
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
// Validate required fields
|
|
33
|
+
if (!parsed.name || !parsed.transport) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
// Validate transport-specific fields
|
|
37
|
+
if (parsed.transport === 'stdio' && !parsed.command) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
if ((parsed.transport === 'http' || parsed.transport === 'sse') && !parsed.url) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
name: parsed.name,
|
|
45
|
+
transport: parsed.transport,
|
|
46
|
+
command: parsed.command,
|
|
47
|
+
args: parsed.args,
|
|
48
|
+
env: parsed.env,
|
|
49
|
+
url: parsed.url,
|
|
50
|
+
headers: parsed.headers,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get layer directories for MCP resolution.
|
|
59
|
+
*/
|
|
60
|
+
function getLayerDirs(cwd) {
|
|
61
|
+
const dirs = [];
|
|
62
|
+
// Project layer (highest priority)
|
|
63
|
+
const projectDir = getProjectAgentsDir(cwd || process.cwd());
|
|
64
|
+
if (projectDir) {
|
|
65
|
+
dirs.push({ layer: 'project', dir: path.join(projectDir, 'mcp') });
|
|
66
|
+
}
|
|
67
|
+
// User layer
|
|
68
|
+
dirs.push({ layer: 'user', dir: getUserMcpDir() });
|
|
69
|
+
// Extra repos (between user and system)
|
|
70
|
+
for (const { dir } of getEnabledExtraRepos()) {
|
|
71
|
+
dirs.push({ layer: 'user', dir: path.join(dir, 'mcp') });
|
|
72
|
+
}
|
|
73
|
+
// System layer (lowest priority)
|
|
74
|
+
dirs.push({ layer: 'system', dir: getSystemMcpDir() });
|
|
75
|
+
return dirs;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Scan a directory for MCP YAML files.
|
|
79
|
+
*/
|
|
80
|
+
function scanMcpDir(dir) {
|
|
81
|
+
const results = [];
|
|
82
|
+
if (!fs.existsSync(dir)) {
|
|
83
|
+
return results;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
if (!entry.isFile())
|
|
89
|
+
continue;
|
|
90
|
+
if (!entry.name.endsWith('.yaml') && !entry.name.endsWith('.yml'))
|
|
91
|
+
continue;
|
|
92
|
+
const filePath = path.join(dir, entry.name);
|
|
93
|
+
const item = parseMcpYaml(filePath);
|
|
94
|
+
if (item) {
|
|
95
|
+
results.push({ name: item.name, path: filePath, item });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Directory read failed
|
|
101
|
+
}
|
|
102
|
+
return results;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Get the config file path for MCP for a given agent.
|
|
106
|
+
* Different agents use different config formats and locations.
|
|
107
|
+
*/
|
|
108
|
+
export function getMcpConfigPath(agent, versionHome) {
|
|
109
|
+
switch (agent) {
|
|
110
|
+
case 'claude':
|
|
111
|
+
return path.join(versionHome, '.claude', 'settings.json');
|
|
112
|
+
case 'codex':
|
|
113
|
+
return path.join(versionHome, '.codex', 'config.toml');
|
|
114
|
+
case 'opencode':
|
|
115
|
+
return path.join(versionHome, '.opencode', 'opencode.jsonc');
|
|
116
|
+
case 'cursor':
|
|
117
|
+
return path.join(versionHome, '.cursor', 'mcp.json');
|
|
118
|
+
case 'gemini':
|
|
119
|
+
return path.join(versionHome, '.gemini', 'settings.json');
|
|
120
|
+
case 'openclaw':
|
|
121
|
+
return path.join(versionHome, '.openclaw', 'openclaw.json');
|
|
122
|
+
default:
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Strip JSON comments (for JSONC files).
|
|
128
|
+
*/
|
|
129
|
+
function stripJsonComments(content) {
|
|
130
|
+
let result = '';
|
|
131
|
+
let inString = false;
|
|
132
|
+
let escape = false;
|
|
133
|
+
let i = 0;
|
|
134
|
+
while (i < content.length) {
|
|
135
|
+
const char = content[i];
|
|
136
|
+
const next = content[i + 1];
|
|
137
|
+
if (escape) {
|
|
138
|
+
result += char;
|
|
139
|
+
escape = false;
|
|
140
|
+
i++;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (char === '\\' && inString) {
|
|
144
|
+
result += char;
|
|
145
|
+
escape = true;
|
|
146
|
+
i++;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (char === '"') {
|
|
150
|
+
inString = !inString;
|
|
151
|
+
result += char;
|
|
152
|
+
i++;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (!inString) {
|
|
156
|
+
if (char === '/' && next === '/') {
|
|
157
|
+
while (i < content.length && content[i] !== '\n') {
|
|
158
|
+
i++;
|
|
159
|
+
}
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (char === '/' && next === '*') {
|
|
163
|
+
i += 2;
|
|
164
|
+
while (i < content.length && !(content[i] === '*' && content[i + 1] === '/')) {
|
|
165
|
+
i++;
|
|
166
|
+
}
|
|
167
|
+
i += 2;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
result += char;
|
|
172
|
+
i++;
|
|
173
|
+
}
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Write MCP servers to Claude settings.json format.
|
|
178
|
+
*/
|
|
179
|
+
function syncToClaudeConfig(configPath, items) {
|
|
180
|
+
let config = {};
|
|
181
|
+
if (fs.existsSync(configPath)) {
|
|
182
|
+
try {
|
|
183
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
config = {};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const mcpServers = {};
|
|
190
|
+
for (const item of items) {
|
|
191
|
+
if (item.transport === 'stdio') {
|
|
192
|
+
mcpServers[item.name] = {
|
|
193
|
+
command: item.command,
|
|
194
|
+
args: item.args || [],
|
|
195
|
+
env: item.env || {},
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
mcpServers[item.name] = {
|
|
200
|
+
url: item.url,
|
|
201
|
+
...(item.headers && { headers: item.headers }),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
config.mcpServers = mcpServers;
|
|
206
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
207
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Write MCP servers to Codex config.toml format.
|
|
211
|
+
*/
|
|
212
|
+
function syncToCodexConfig(configPath, items) {
|
|
213
|
+
let config = {};
|
|
214
|
+
if (fs.existsSync(configPath)) {
|
|
215
|
+
try {
|
|
216
|
+
config = TOML.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
config = {};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const mcpServers = {};
|
|
223
|
+
for (const item of items) {
|
|
224
|
+
if (item.transport === 'stdio') {
|
|
225
|
+
mcpServers[item.name] = {
|
|
226
|
+
command: item.command,
|
|
227
|
+
args: item.args || [],
|
|
228
|
+
...(item.env && { env: item.env }),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
// Codex may not support HTTP MCPs
|
|
232
|
+
}
|
|
233
|
+
config.mcp_servers = mcpServers;
|
|
234
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
235
|
+
fs.writeFileSync(configPath, TOML.stringify(config), 'utf-8');
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Write MCP servers to OpenCode opencode.jsonc format.
|
|
239
|
+
*/
|
|
240
|
+
function syncToOpenCodeConfig(configPath, items) {
|
|
241
|
+
let config = {};
|
|
242
|
+
if (fs.existsSync(configPath)) {
|
|
243
|
+
try {
|
|
244
|
+
const content = stripJsonComments(fs.readFileSync(configPath, 'utf-8'));
|
|
245
|
+
config = JSON.parse(content);
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
config = {};
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
const mcp = {};
|
|
252
|
+
for (const item of items) {
|
|
253
|
+
if (item.transport === 'stdio') {
|
|
254
|
+
// OpenCode uses command as array
|
|
255
|
+
const commandArray = [item.command, ...(item.args || [])];
|
|
256
|
+
mcp[item.name] = {
|
|
257
|
+
type: 'local',
|
|
258
|
+
command: commandArray,
|
|
259
|
+
...(item.env && { env: item.env }),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
mcp[item.name] = {
|
|
264
|
+
type: 'remote',
|
|
265
|
+
url: item.url,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
config.mcp = mcp;
|
|
270
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
271
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Write MCP servers to Cursor mcp.json format.
|
|
275
|
+
*/
|
|
276
|
+
function syncToCursorConfig(configPath, items) {
|
|
277
|
+
let config = {};
|
|
278
|
+
if (fs.existsSync(configPath)) {
|
|
279
|
+
try {
|
|
280
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
config = {};
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const mcpServers = {};
|
|
287
|
+
for (const item of items) {
|
|
288
|
+
if (item.transport === 'stdio') {
|
|
289
|
+
mcpServers[item.name] = {
|
|
290
|
+
command: item.command,
|
|
291
|
+
args: item.args || [],
|
|
292
|
+
env: item.env || {},
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
mcpServers[item.name] = {
|
|
297
|
+
url: item.url,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
config.mcpServers = mcpServers;
|
|
302
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
303
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Write MCP servers to Gemini settings.json format.
|
|
307
|
+
*/
|
|
308
|
+
function syncToGeminiConfig(configPath, items) {
|
|
309
|
+
let config = {};
|
|
310
|
+
if (fs.existsSync(configPath)) {
|
|
311
|
+
try {
|
|
312
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
config = {};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const mcpServers = {};
|
|
319
|
+
for (const item of items) {
|
|
320
|
+
if (item.transport === 'stdio') {
|
|
321
|
+
mcpServers[item.name] = {
|
|
322
|
+
command: item.command,
|
|
323
|
+
args: item.args || [],
|
|
324
|
+
env: item.env || {},
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
mcpServers[item.name] = {
|
|
329
|
+
url: item.url,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
config.mcpServers = mcpServers;
|
|
334
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
335
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Write MCP servers to OpenClaw openclaw.json format.
|
|
339
|
+
*/
|
|
340
|
+
function syncToOpenClawConfig(configPath, items) {
|
|
341
|
+
let config = {};
|
|
342
|
+
if (fs.existsSync(configPath)) {
|
|
343
|
+
try {
|
|
344
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
config = {};
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (!config.mcp || typeof config.mcp !== 'object') {
|
|
351
|
+
config.mcp = {};
|
|
352
|
+
}
|
|
353
|
+
const servers = {};
|
|
354
|
+
for (const item of items) {
|
|
355
|
+
if (item.transport === 'stdio') {
|
|
356
|
+
servers[item.name] = {
|
|
357
|
+
command: item.command,
|
|
358
|
+
args: item.args,
|
|
359
|
+
env: item.env,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
servers[item.name] = {
|
|
364
|
+
url: item.url,
|
|
365
|
+
transport: item.transport,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
config.mcp.servers = servers;
|
|
370
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
371
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* MCP resource handler implementing ResourceHandler<McpItem>.
|
|
375
|
+
*/
|
|
376
|
+
export const McpHandler = {
|
|
377
|
+
kind: 'mcp',
|
|
378
|
+
listAll(agent, cwd) {
|
|
379
|
+
if (!MCP_CAPABLE_AGENTS.includes(agent)) {
|
|
380
|
+
return [];
|
|
381
|
+
}
|
|
382
|
+
const results = new Map();
|
|
383
|
+
const layerDirs = getLayerDirs(cwd);
|
|
384
|
+
// Process in reverse order (system first) so higher layers override
|
|
385
|
+
for (let i = layerDirs.length - 1; i >= 0; i--) {
|
|
386
|
+
const { layer, dir } = layerDirs[i];
|
|
387
|
+
const items = scanMcpDir(dir);
|
|
388
|
+
for (const { name, path: itemPath, item } of items) {
|
|
389
|
+
results.set(name, {
|
|
390
|
+
name,
|
|
391
|
+
item,
|
|
392
|
+
layer,
|
|
393
|
+
path: itemPath,
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return Array.from(results.values());
|
|
398
|
+
},
|
|
399
|
+
resolve(agent, name, cwd) {
|
|
400
|
+
if (!MCP_CAPABLE_AGENTS.includes(agent)) {
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
const layerDirs = getLayerDirs(cwd);
|
|
404
|
+
// Check in priority order (project first)
|
|
405
|
+
for (const { layer, dir } of layerDirs) {
|
|
406
|
+
if (!fs.existsSync(dir))
|
|
407
|
+
continue;
|
|
408
|
+
try {
|
|
409
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
410
|
+
for (const entry of entries) {
|
|
411
|
+
if (!entry.isFile())
|
|
412
|
+
continue;
|
|
413
|
+
if (!entry.name.endsWith('.yaml') && !entry.name.endsWith('.yml'))
|
|
414
|
+
continue;
|
|
415
|
+
const filePath = path.join(dir, entry.name);
|
|
416
|
+
const item = parseMcpYaml(filePath);
|
|
417
|
+
if (item && item.name === name) {
|
|
418
|
+
return {
|
|
419
|
+
name,
|
|
420
|
+
item,
|
|
421
|
+
layer,
|
|
422
|
+
path: filePath,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
catch {
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return null;
|
|
432
|
+
},
|
|
433
|
+
sync(agent, versionHome, cwd) {
|
|
434
|
+
if (!MCP_CAPABLE_AGENTS.includes(agent)) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const items = this.listAll(agent, cwd);
|
|
438
|
+
if (items.length === 0) {
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
const configPath = getMcpConfigPath(agent, versionHome);
|
|
442
|
+
if (!configPath) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
const mcpItems = items.map((r) => r.item);
|
|
446
|
+
switch (agent) {
|
|
447
|
+
case 'claude':
|
|
448
|
+
syncToClaudeConfig(configPath, mcpItems);
|
|
449
|
+
break;
|
|
450
|
+
case 'codex':
|
|
451
|
+
syncToCodexConfig(configPath, mcpItems);
|
|
452
|
+
break;
|
|
453
|
+
case 'opencode':
|
|
454
|
+
syncToOpenCodeConfig(configPath, mcpItems);
|
|
455
|
+
break;
|
|
456
|
+
case 'cursor':
|
|
457
|
+
syncToCursorConfig(configPath, mcpItems);
|
|
458
|
+
break;
|
|
459
|
+
case 'gemini':
|
|
460
|
+
syncToGeminiConfig(configPath, mcpItems);
|
|
461
|
+
break;
|
|
462
|
+
case 'openclaw':
|
|
463
|
+
syncToOpenClawConfig(configPath, mcpItems);
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
format(agent) {
|
|
468
|
+
switch (agent) {
|
|
469
|
+
case 'codex':
|
|
470
|
+
return 'toml';
|
|
471
|
+
default:
|
|
472
|
+
return 'json';
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
targetDir(_agent) {
|
|
476
|
+
// MCP doesn't have a target directory - it modifies config files
|
|
477
|
+
return 'mcp';
|
|
478
|
+
},
|
|
479
|
+
configPath(agent, versionHome) {
|
|
480
|
+
return getMcpConfigPath(agent, versionHome);
|
|
481
|
+
},
|
|
482
|
+
};
|
|
483
|
+
export default McpHandler;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PermissionsHandler - ResourceHandler implementation for permissions.
|
|
3
|
+
*
|
|
4
|
+
* Permissions are stored as YAML files in permissions/ directories at each layer.
|
|
5
|
+
* Resolution: project > user > system (higher layer wins on name conflict).
|
|
6
|
+
* Unlike other resources, permissions merge into agent-specific config files
|
|
7
|
+
* (Claude: settings.json, Codex: config.toml, OpenCode: opencode.jsonc).
|
|
8
|
+
*/
|
|
9
|
+
import type { ResourceHandler } from './types.js';
|
|
10
|
+
import type { PermissionSet } from '../types.js';
|
|
11
|
+
export type PermissionItem = PermissionSet;
|
|
12
|
+
export declare const PermissionsHandler: ResourceHandler<PermissionItem>;
|
|
13
|
+
export default PermissionsHandler;
|