Skip to content
Merged
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
2 changes: 2 additions & 0 deletions packages/agent/src/core/mcp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export interface McpConfig {
servers?: McpServerConfig[];
/** Default resources to load automatically */
defaultResources?: string[];
/** Default tools to make available */
defaultTools?: string[];
}

/**
Expand Down
17 changes: 14 additions & 3 deletions packages/agent/src/tools/browser/filterPageContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { Readability } from '@mozilla/readability';
import { JSDOM } from 'jsdom';
import { Page } from 'playwright';

const OUTPUT_LIMIT = 11 * 1024; // 10KB limit

/**
* Returns the raw HTML content of the page without any processing
*/
Expand Down Expand Up @@ -93,13 +95,22 @@ export async function filterPageContent(
page: Page,
pageFilter: 'simple' | 'none' | 'readability',
): Promise<string> {
let result: string = '';
switch (pageFilter) {
case 'none':
return getNoneProcessedDOM(page);
result = await getNoneProcessedDOM(page);
break;
case 'readability':
return getReadabilityProcessedDOM(page);
result = await getReadabilityProcessedDOM(page);
break;
case 'simple':
default:
return getSimpleProcessedDOM(page);
result = await getSimpleProcessedDOM(page);
break;
}

if (result.length > OUTPUT_LIMIT) {
return result.slice(0, OUTPUT_LIMIT) + '...(truncated)';
}
return result;
}
147 changes: 140 additions & 7 deletions packages/agent/src/tools/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,25 @@ const getResourceSchema = z.object({
.describe('URI of the resource to fetch in the format "scheme://path"'),
});

// Parameters for listTools method
const listToolsSchema = z.object({
server: z
.string()
.optional()
.describe('Optional server name to filter tools by'),
});

// Parameters for executeTool method
const executeToolSchema = z.object({
uri: z
.string()
.describe('URI of the tool to execute in the format "scheme://path"'),
params: z
.record(z.unknown())
.optional()
.describe('Parameters to pass to the tool'),
});

// Return type for listResources
const listResourcesReturnSchema = z.array(
z.object({
Expand All @@ -39,6 +58,20 @@ const listResourcesReturnSchema = z.array(
// Return type for getResource
const getResourceReturnSchema = z.string();

// Return type for listTools
const listToolsReturnSchema = z.array(
z.object({
uri: z.string(),
name: z.string(),
description: z.string().optional(),
parameters: z.record(z.unknown()).optional(),
returns: z.record(z.unknown()).optional(),
}),
);

// Return type for executeTool - can be any JSON value
const executeToolReturnSchema = z.unknown();

// Map to store MCP clients
const mcpClients = new Map<string, any>();

Expand Down Expand Up @@ -87,7 +120,7 @@ export function createMcpTool(config: McpConfig): Tool {
return {
name: 'mcp',
description:
'Interact with Model Context Protocol (MCP) servers to retrieve resources',
'Interact with Model Context Protocol (MCP) servers to retrieve resources and execute tools',
parameters: z.discriminatedUnion('method', [
z.object({
method: z.literal('listResources'),
Expand All @@ -97,6 +130,14 @@ export function createMcpTool(config: McpConfig): Tool {
method: z.literal('getResource'),
params: getResourceSchema,
}),
z.object({
method: z.literal('listTools'),
params: listToolsSchema.optional(),
}),
z.object({
method: z.literal('executeTool'),
params: executeToolSchema,
}),
]),
parametersJsonSchema: zodToJsonSchema(
z.discriminatedUnion('method', [
Expand All @@ -108,15 +149,33 @@ export function createMcpTool(config: McpConfig): Tool {
method: z.literal('getResource'),
params: getResourceSchema,
}),
z.object({
method: z.literal('listTools'),
params: listToolsSchema.optional(),
}),
z.object({
method: z.literal('executeTool'),
params: executeToolSchema,
}),
]),
),
returns: z.union([listResourcesReturnSchema, getResourceReturnSchema]),
returns: z.union([
listResourcesReturnSchema,
getResourceReturnSchema,
listToolsReturnSchema,
executeToolReturnSchema,
]),
returnsJsonSchema: zodToJsonSchema(
z.union([listResourcesReturnSchema, getResourceReturnSchema]),
z.union([
listResourcesReturnSchema,
getResourceReturnSchema,
listToolsReturnSchema,
executeToolReturnSchema,
]),
),

execute: async ({ method, params }, { logger }) => {
// Extract the server name from a resource URI
// Extract the server name from a URI (resource or tool)
function getServerNameFromUri(uri: string): string | undefined {
const match = uri.match(/^([^:]+):\/\//);
return match ? match[1] : undefined;
Expand Down Expand Up @@ -180,6 +239,64 @@ export function createMcpTool(config: McpConfig): Tool {
logger.verbose(`Fetching resource: ${uri}`);
const resource = await client.resource(uri);
return resource.content;
} else if (method === 'listTools') {
// List available tools from MCP servers
const tools: any[] = [];
const serverFilter = params?.server;

// If a specific server is requested, only check that server
if (serverFilter) {
const client = mcpClients.get(serverFilter);
if (client) {
try {
logger.verbose(`Fetching tools from server: ${serverFilter}`);
const serverTools = await client.tools();
tools.push(...(serverTools as any[]));
} catch (error) {
logger.error(
`Failed to fetch tools from server ${serverFilter}:`,
error,
);
}
} else {
logger.warn(`Server not found: ${serverFilter}`);
}
} else {
// Otherwise, check all servers
for (const [serverName, client] of mcpClients.entries()) {
try {
logger.verbose(`Fetching tools from server: ${serverName}`);
const serverTools = await client.tools();
tools.push(...(serverTools as any[]));
} catch (error) {
logger.error(
`Failed to fetch tools from server ${serverName}:`,
error,
);
}
}
}

return tools;
} else if (method === 'executeTool') {
// Execute a tool from an MCP server
const { uri, params: toolParams = {} } = params;

// Parse the URI to determine which server to use
const serverName = getServerNameFromUri(uri);
if (!serverName) {
throw new Error(`Could not determine server from URI: ${uri}`);
}

const client = mcpClients.get(serverName);
if (!client) {
throw new Error(`Server not found: ${serverName}`);
}

// Use the MCP SDK to execute the tool
logger.verbose(`Executing tool: ${uri} with params:`, toolParams);
const result = await client.tool(uri, toolParams);
return result;
}

throw new Error(`Unknown method: ${method}`);
Expand All @@ -188,20 +305,36 @@ export function createMcpTool(config: McpConfig): Tool {
logParameters: (params, { logger }) => {
if (params.method === 'listResources') {
logger.verbose(
`Listing MCP resources${params.params?.server ? ` from server: ${params.params.server}` : ''}`,
`Listing MCP resources${
params.params?.server ? ` from server: ${params.params.server}` : ''
}`,
);
} else if (params.method === 'getResource') {
logger.verbose(`Fetching MCP resource: ${params.params.uri}`);
} else if (params.method === 'listTools') {
logger.verbose(
`Listing MCP tools${
params.params?.server ? ` from server: ${params.params.server}` : ''
}`,
);
} else if (params.method === 'executeTool') {
logger.verbose(`Executing MCP tool: ${params.params.uri}`);
}
},

logReturns: (result, { logger }) => {
if (Array.isArray(result)) {
logger.verbose(`Found ${result.length} MCP resources`);
} else {
if (result.length > 0 && 'description' in result[0]) {
logger.verbose(`Found ${result.length} MCP tools`);
} else {
logger.verbose(`Found ${result.length} MCP resources`);
}
} else if (typeof result === 'string') {
logger.verbose(
`Retrieved MCP resource content (${result.length} characters)`,
);
} else {
logger.verbose(`Executed MCP tool and received result`);
}
},
};
Expand Down
22 changes: 22 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ export default {

// Optional: Default context resources to load
defaultResources: ['company-docs://api/reference'],

// Optional: Default tools to make available
defaultTools: ['company-docs://tools/search'],
},
};
```
Expand All @@ -212,6 +215,25 @@ When MCP is configured, the agent will have access to a new `mcp` tool that allo

- List available resources from configured MCP servers
- Fetch resources to use as context for its work
- List available tools from configured MCP servers
- Execute tools provided by MCP servers

#### Using MCP Tools

MCP tools allow the agent to execute functions provided by external services through the Model Context Protocol. The agent can:

1. Discover available tools using `mcp.listTools()`
2. Execute a tool using `mcp.executeTool({ uri: 'server-name://path/to/tool', params: { ... } })`

Tools can provide various capabilities like:

- Searching documentation
- Accessing databases
- Interacting with APIs
- Performing specialized calculations
- Accessing proprietary services

Each tool has a URI that identifies it, along with parameters it accepts and the type of result it returns.

### CLI-Only Options

Expand Down
Loading