Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions src/agents/mcp-writer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ export interface McpResolvedTarget {

export type McpTargetResolver = (agentId: string, spec: McpConfigSpec) => McpResolvedTarget;

export interface McpWriteWarning {
agent: string;
message: string;
}

/**
* Convert McpConfig entries (from agents.toml) to universal McpDeclarations.
*/
Expand Down Expand Up @@ -41,13 +46,15 @@ export function projectMcpResolver(projectRoot: string): McpTargetResolver {
* Write MCP config files for each agent.
* - Dedicated files (shared=false): written fresh each time.
* - Shared files (shared=true): read existing, merge dotagents servers under the root key, write back.
* - Agents that don't support MCP: collected as warnings.
*/
export async function writeMcpConfigs(
agentIds: string[],
servers: McpDeclaration[],
resolveTarget: McpTargetResolver,
): Promise<void> {
if (servers.length === 0) return;
): Promise<McpWriteWarning[]> {
const warnings: McpWriteWarning[] = [];
if (servers.length === 0) return warnings;

// Deduplicate by resolved filePath so shared files aren't written twice
const seen = new Set<string>();
Expand All @@ -56,6 +63,11 @@ export async function writeMcpConfigs(
const agent = getAgent(id);
if (!agent) continue;

if (!agent.mcp) {
warnings.push({ agent: id, message: `Agent "${agent.displayName}" does not support MCP` });
continue;
}

const { mcp } = agent;
const { filePath, shared } = resolveTarget(id, mcp);
if (seen.has(filePath)) continue;
Expand All @@ -75,6 +87,8 @@ export async function writeMcpConfigs(
await freshWrite(filePath, mcp, serialized);
}
}

return warnings;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sync command silently discards MCP warnings from write

Medium Severity

writeMcpConfigs now returns McpWriteWarning[] instead of void, but the caller in sync.ts (line 197) still uses await writeMcpConfigs(...) without capturing the return value. When the sync command repairs MCP configs, warnings about agents that don't support MCP are silently discarded, unlike in install.ts where they're properly captured and displayed to the user.

Fix in Cursor Fix in Web

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was following the existing pattern in writeHookConfigs on line 216 which has the same silent discard on main. Can fix if needed though.

}

/**
Expand All @@ -95,6 +109,9 @@ export async function verifyMcpConfigs(
const agent = getAgent(id);
if (!agent) continue;

// Skip agents that don't support MCP
if (!agent.mcp) continue;

const { mcp } = agent;
const { filePath } = resolveTarget(id, mcp);
if (seen.has(filePath)) continue;
Expand Down
4 changes: 2 additions & 2 deletions src/agents/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ export interface AgentDefinition {
* Undefined if the agent reads ~/.agents/skills/ natively (no symlink needed).
*/
userSkillsParentDirs?: string[];
/** MCP config file specification */
mcp: McpConfigSpec;
/** MCP config file specification (undefined if agent doesn't support MCP) */
mcp?: McpConfigSpec;
/** Transforms universal MCP declaration to agent-specific format */
serializeServer: McpSerializer;
/** Hook config file specification (undefined if agent doesn't support hooks) */
Expand Down
7 changes: 4 additions & 3 deletions src/cli/commands/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export interface InstallOptions {
export interface InstallResult {
installed: string[];
skipped: string[];
mcpWarnings: { agent: string; message: string }[];
hookWarnings: { agent: string; message: string }[];
}

Expand Down Expand Up @@ -276,7 +277,7 @@ export async function runInstall(opts: InstallOptions): Promise<InstallResult> {

// 5. Write MCP config files
const mcpResolver = scope.scope === "user" ? userMcpResolver() : projectMcpResolver(scope.root);
await writeMcpConfigs(config.agents, toMcpDeclarations(config.mcp), mcpResolver);
const mcpWarnings = await writeMcpConfigs(config.agents, toMcpDeclarations(config.mcp), mcpResolver);

// 6. Write hook config files (skip for user scope)
let hookWarnings: { agent: string; message: string }[] = [];
Expand All @@ -288,7 +289,7 @@ export async function runInstall(opts: InstallOptions): Promise<InstallResult> {
);
}

return { installed, skipped, hookWarnings };
return { installed, skipped, mcpWarnings, hookWarnings };
}

export default async function install(args: string[], flags?: { user?: boolean }): Promise<void> {
Expand All @@ -314,7 +315,7 @@ export default async function install(args: string[], flags?: { user?: boolean }
chalk.green(`Installed ${result.installed.length} skill(s): ${result.installed.join(", ")}`),
);
}
for (const w of result.hookWarnings) {
for (const w of [...result.mcpWarnings, ...result.hookWarnings]) {
console.log(chalk.yellow(` warn: ${w.message}`));
}
} catch (err) {
Expand Down
Loading