Skip to content

Commit 4bfc20b

Browse files
committed
refactor(core): update MCP tool filtering implementation
- Moved tool filtering logic to agent/runner layer - Removed server-side filtering and context coupling - Updated test suite to reflect new behavior
1 parent 1cc56dd commit 4bfc20b

File tree

2 files changed

+103
-10
lines changed

2 files changed

+103
-10
lines changed

packages/agents-core/src/mcp.ts

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ import {
1414
JsonObjectSchemaStrict,
1515
UnknownContext,
1616
} from './types';
17-
import type { ToolFilterCallable, ToolFilterStatic } from './mcpUtil';
17+
import type {
18+
ToolFilterCallable,
19+
ToolFilterStatic,
20+
ToolFilterContext,
21+
} from './mcpUtil';
1822
import type { RunContext } from './runContext';
1923
import type { Agent } from './agent';
2024

@@ -30,6 +34,7 @@ export const DEFAULT_STREAMABLE_HTTP_MCP_CLIENT_LOGGER_NAME =
3034
*/
3135
export interface MCPServer {
3236
cacheToolsList: boolean;
37+
toolFilter?: ToolFilterCallable | ToolFilterStatic;
3338
connect(): Promise<void>;
3439
readonly name: string;
3540
close(): Promise<void>;
@@ -46,7 +51,7 @@ export interface MCPServer {
4651
export abstract class BaseMCPServerStdio implements MCPServer {
4752
public cacheToolsList: boolean;
4853
protected _cachedTools: any[] | undefined = undefined;
49-
protected toolFilter?: ToolFilterCallable | ToolFilterStatic;
54+
public toolFilter?: ToolFilterCallable | ToolFilterStatic;
5055

5156
protected logger: Logger;
5257
constructor(options: MCPServerStdioOptions) {
@@ -83,7 +88,7 @@ export abstract class BaseMCPServerStdio implements MCPServer {
8388
export abstract class BaseMCPServerStreamableHttp implements MCPServer {
8489
public cacheToolsList: boolean;
8590
protected _cachedTools: any[] | undefined = undefined;
86-
protected toolFilter?: ToolFilterCallable | ToolFilterStatic;
91+
public toolFilter?: ToolFilterCallable | ToolFilterStatic;
8792

8893
protected logger: Logger;
8994
constructor(options: MCPServerStreamableHttpOptions) {
@@ -155,13 +160,13 @@ export class MCPServerStdio extends BaseMCPServerStdio {
155160
return this.underlying.close();
156161
}
157162
async listTools(
158-
runContext?: RunContext<any>,
159-
agent?: Agent<any, any>,
163+
_runContext?: RunContext<any>,
164+
_agent?: Agent<any, any>,
160165
): Promise<MCPTool[]> {
161166
if (this.cacheToolsList && this._cachedTools) {
162167
return this._cachedTools;
163168
}
164-
const tools = await this.underlying.listTools(runContext, agent);
169+
const tools = await this.underlying.listTools();
165170
if (this.cacheToolsList) {
166171
this._cachedTools = tools;
167172
}
@@ -191,13 +196,13 @@ export class MCPServerStreamableHttp extends BaseMCPServerStreamableHttp {
191196
return this.underlying.close();
192197
}
193198
async listTools(
194-
runContext?: RunContext<any>,
195-
agent?: Agent<any, any>,
199+
_runContext?: RunContext<any>,
200+
_agent?: Agent<any, any>,
196201
): Promise<MCPTool[]> {
197202
if (this.cacheToolsList && this._cachedTools) {
198203
return this._cachedTools;
199204
}
200-
const tools = await this.underlying.listTools(runContext, agent);
205+
const tools = await this.underlying.listTools();
201206
if (this.cacheToolsList) {
202207
this._cachedTools = tools;
203208
}
@@ -268,7 +273,16 @@ async function getFunctionToolsFromServer<TContext = UnknownContext>(
268273
}
269274
return withMCPListToolsSpan(
270275
async (span) => {
271-
const mcpTools = await server.listTools(runContext, agent);
276+
let mcpTools = await server.listTools(runContext, agent);
277+
if (server.toolFilter) {
278+
mcpTools = await filterMcpTools(
279+
mcpTools,
280+
server.toolFilter as ToolFilterCallable<TContext> | ToolFilterStatic,
281+
runContext,
282+
agent,
283+
server.name,
284+
);
285+
}
272286
span.spanData.result = mcpTools.map((t) => t.name);
273287
const tools: FunctionTool<TContext, any, string>[] = mcpTools.map((t) =>
274288
mcpToFunctionTool(t, server, convertSchemasToStrict),
@@ -371,6 +385,41 @@ function ensureStrictJsonSchema(
371385
return out;
372386
}
373387

388+
async function filterMcpTools<TContext = UnknownContext>(
389+
tools: MCPTool[],
390+
filter: ToolFilterCallable<TContext> | ToolFilterStatic,
391+
runContext: RunContext<TContext> | undefined,
392+
agent: Agent<TContext, any> | undefined,
393+
serverName: string,
394+
): Promise<MCPTool[]> {
395+
if (typeof filter === 'function') {
396+
if (!runContext || !agent) {
397+
return tools;
398+
}
399+
const ctx = {
400+
runContext,
401+
agent,
402+
serverName,
403+
} as ToolFilterContext<TContext>;
404+
const result: MCPTool[] = [];
405+
for (const tool of tools) {
406+
if (await filter(ctx, tool)) {
407+
result.push(tool);
408+
}
409+
}
410+
return result;
411+
}
412+
return tools.filter((t) => {
413+
if (filter.allowedToolNames && !filter.allowedToolNames.includes(t.name)) {
414+
return false;
415+
}
416+
if (filter.blockedToolNames && filter.blockedToolNames.includes(t.name)) {
417+
return false;
418+
}
419+
return true;
420+
});
421+
}
422+
374423
/**
375424
* Abstract base class for MCP servers that use a ClientSession for communication.
376425
* Handles session management, tool listing, tool calling, and cleanup.

packages/agents-core/test/mcpToolFilter.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest';
22
import { withTrace } from '../src/tracing';
33
import { NodeMCPServerStdio } from '../src/shims/mcp-server/node';
44
import { createStaticToolFilter } from '../src/mcpUtil';
5+
import { getAllMcpTools } from '../src/mcp';
56
import { Agent } from '../src/agent';
67
import { RunContext } from '../src/runContext';
78

@@ -198,4 +199,47 @@ describe('MCP tool filtering', () => {
198199
expect(result.map((t) => t.name)).toEqual(['x']);
199200
});
200201
});
202+
203+
it('applies filter in getAllMcpTools', async () => {
204+
await withTrace('test', async () => {
205+
const tools = [
206+
{
207+
name: 'allow',
208+
description: '',
209+
inputSchema: {
210+
type: 'object',
211+
properties: {},
212+
required: [],
213+
additionalProperties: false,
214+
},
215+
},
216+
{
217+
name: 'block',
218+
description: '',
219+
inputSchema: {
220+
type: 'object',
221+
properties: {},
222+
required: [],
223+
additionalProperties: false,
224+
},
225+
},
226+
];
227+
const server = new StubServer(
228+
'filter',
229+
tools,
230+
createStaticToolFilter({ allowed: ['allow'] }),
231+
);
232+
const agent = new Agent({
233+
name: 'agent',
234+
instructions: '',
235+
model: '',
236+
modelSettings: {},
237+
tools: [],
238+
mcpServers: [server],
239+
});
240+
const runContext = new RunContext();
241+
const result = await getAllMcpTools([server], false, runContext, agent);
242+
expect(result.map((t) => t.name)).toEqual(['allow']);
243+
});
244+
});
201245
});

0 commit comments

Comments
 (0)