Skip to content
Merged
27 changes: 1 addition & 26 deletions package-lock.json
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why was package-lock changed?

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions packages/cli/src/ui/components/NewAgentsNotification.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,25 @@ describe('NewAgentsNotification', () => {
{
name: 'Agent B',
description: 'Description B',
kind: 'local' as const,
inputConfig: { inputSchema: {} },
promptConfig: {},
modelConfig: {},
runConfig: {},
mcpServers: {
github: {
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-github'],
},
postgres: {
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-postgres'],
},
},
},
{
name: 'Agent C',
description: 'Description C',
kind: 'remote' as const,
agentCardUrl: '',
inputConfig: { inputSchema: {} },
Expand Down
37 changes: 28 additions & 9 deletions packages/cli/src/ui/components/NewAgentsNotification.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,35 @@ export const NewAgentsNotification = ({
borderStyle="single"
padding={1}
>
{displayAgents.map((agent) => (
<Box key={agent.name}>
<Box flexShrink={0}>
<Text bold color={theme.text.primary}>
- {agent.name}:{' '}
</Text>
{displayAgents.map((agent) => {
const mcpServers =
agent.kind === 'local' ? agent.mcpServers : undefined;
const hasMcpServers =
mcpServers && Object.keys(mcpServers).length > 0;
return (
<Box key={agent.name} flexDirection="column">
<Box>
<Box flexShrink={0}>
<Text bold color={theme.text.primary}>
- {agent.name}:{' '}
</Text>
</Box>
<Text color={theme.text.secondary}>
{' '}
{agent.description}
</Text>
</Box>
{hasMcpServers && (
<Box marginLeft={2}>
<Text color={theme.text.secondary}>
(Includes MCP servers:{' '}
{Object.keys(mcpServers).join(', ')})
</Text>
</Box>
)}
</Box>
<Text color={theme.text.secondary}> {agent.description}</Text>
</Box>
))}
);
})}
{remaining > 0 && (
<Text color={theme.text.secondary}>
... and {remaining} more.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ exports[`NewAgentsNotification > renders agent list 1`] = `
│ │ │ │
│ │ - Agent A: Description A │ │
│ │ - Agent B: Description B │ │
│ │ (Includes MCP servers: github, postgres) │ │
│ │ - Agent C: Description C │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────────────────────────────┘ │
│ │
Expand Down
30 changes: 28 additions & 2 deletions packages/core/src/agents/agent-scheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import type {
CompletedToolCall,
} from '../scheduler/types.js';
import type { ToolRegistry } from '../tools/tool-registry.js';
import type { PromptRegistry } from '../prompts/prompt-registry.js';
import type { ResourceRegistry } from '../resources/resource-registry.js';
import type { EditorType } from '../utils/editor.js';

/**
Expand All @@ -25,6 +27,10 @@ export interface AgentSchedulingOptions {
parentCallId?: string;
/** The tool registry specific to this agent. */
toolRegistry: ToolRegistry;
/** The prompt registry specific to this agent. */
promptRegistry?: PromptRegistry;
/** The resource registry specific to this agent. */
resourceRegistry?: ResourceRegistry;
/** AbortSignal for cancellation. */
signal: AbortSignal;
/** Optional function to get the preferred editor for tool modifications. */
Expand All @@ -51,14 +57,34 @@ export async function scheduleAgentTools(
subagent,
parentCallId,
toolRegistry,
promptRegistry,
resourceRegistry,
signal,
getPreferredEditor,
onWaitingForConfirmation,
} = options;

// Create a proxy/override of the config to provide the agent-specific tool registry.
// Create a proxy/override of the config to provide the agent-specific registries.
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, no-restricted-syntax
const agentConfig: Config = Object.create(config);
agentConfig.getToolRegistry = () => toolRegistry;
agentConfig.getMessageBus = () => toolRegistry.messageBus;

if (promptRegistry) {
agentConfig.getPromptRegistry = () => promptRegistry;
}
if (resourceRegistry) {
agentConfig.getResourceRegistry = () => resourceRegistry;
}

// Override toolRegistry property so AgentLoopContext reads the agent-specific registry.
Object.defineProperty(agentConfig, 'toolRegistry', {
get: () => toolRegistry,
configurable: true,
});

const schedulerContext = {
config,
config: agentConfig,
promptId: config.promptId,
toolRegistry,
messageBus: toolRegistry.messageBus,
Expand Down
54 changes: 54 additions & 0 deletions packages/core/src/agents/agentLoader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,33 @@ System prompt content.`);
});
});

it('should parse frontmatter with mcp_servers', async () => {
const filePath = await writeAgentMarkdown(`---
name: mcp-agent
description: An agent with MCP servers
mcp_servers:
test-server:
command: node
args: [server.js]
include_tools: [tool1, tool2]
---
System prompt content.`);

const result = await parseAgentMarkdown(filePath);
expect(result).toHaveLength(1);
expect(result[0]).toMatchObject({
name: 'mcp-agent',
description: 'An agent with MCP servers',
mcp_servers: {
'test-server': {
command: 'node',
args: ['server.js'],
include_tools: ['tool1', 'tool2'],
},
},
});
});

it('should throw AgentLoadError if frontmatter is missing', async () => {
const filePath = await writeAgentMarkdown(`Just some markdown content.`);
await expect(parseAgentMarkdown(filePath)).rejects.toThrow(
Expand Down Expand Up @@ -274,6 +301,33 @@ Body`);
expect(result.modelConfig.model).toBe(GEMINI_MODEL_ALIAS_PRO);
});

it('should convert mcp_servers in local agent', () => {
const markdown = {
kind: 'local' as const,
name: 'mcp-agent',
description: 'An agent with MCP servers',
mcp_servers: {
'test-server': {
command: 'node',
args: ['server.js'],
include_tools: ['tool1'],
},
},
system_prompt: 'prompt',
};

const result = markdownToAgentDefinition(
markdown,
) as LocalAgentDefinition;
expect(result.kind).toBe('local');
expect(result.mcpServers).toBeDefined();
expect(result.mcpServers!['test-server']).toMatchObject({
command: 'node',
args: ['server.js'],
includeTools: ['tool1'],
});
});

it('should pass through unknown model names (e.g. auto)', () => {
const markdown = {
kind: 'local' as const,
Expand Down
Loading
Loading