Skip to content

Commit 2ddaea8

Browse files
committed
refactor: integrate filtered tool logic into agent-level, clean up tests and server impl
1 parent 14fbfc1 commit 2ddaea8

File tree

4 files changed

+39
-129
lines changed

4 files changed

+39
-129
lines changed

examples/mcp/tool-filter-example.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ async function main() {
1212
const mcpServer = new MCPServerStdio({
1313
name: 'Filesystem Server with filter',
1414
fullCommand: `npx -y @modelcontextprotocol/server-filesystem ${samplesDir}`,
15-
toolFilter: createStaticToolFilter(
16-
['read_file', 'list_directory'],
17-
['write_file'],
18-
),
15+
toolFilter: createStaticToolFilter({
16+
allowed: ['read_file', 'list_directory'],
17+
blocked: ['write_file'],
18+
}),
1919
});
2020

2121
await mcpServer.connect();

packages/agents-core/src/mcpUtil.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,19 @@ export interface ToolFilterStatic {
2828
}
2929

3030
/** Convenience helper to create a static tool filter. */
31-
export function createStaticToolFilter(
32-
allowedToolNames?: string[],
33-
blockedToolNames?: string[],
34-
): ToolFilterStatic | undefined {
35-
if (!allowedToolNames && !blockedToolNames) {
31+
export function createStaticToolFilter(options?: {
32+
allowed?: string[];
33+
blocked?: string[];
34+
}): ToolFilterStatic | undefined {
35+
if (!options?.allowed && !options?.blocked) {
3636
return undefined;
3737
}
3838
const filter: ToolFilterStatic = {};
39-
if (allowedToolNames) {
40-
filter.allowedToolNames = allowedToolNames;
39+
if (options?.allowed) {
40+
filter.allowedToolNames = options.allowed;
4141
}
42-
if (blockedToolNames) {
43-
filter.blockedToolNames = blockedToolNames;
42+
if (options?.blocked) {
43+
filter.blockedToolNames = options.blocked;
4444
}
4545
return filter;
4646
}

packages/agents-core/src/shims/mcp-server/node.ts

Lines changed: 18 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
invalidateServerToolsCache,
1313
} from '../../mcp';
1414
import logger from '../../logger';
15-
import type { ToolFilterContext } from '../../mcpUtil';
1615
import type { RunContext } from '../../runContext';
1716
import type { Agent } from '../../agent';
1817

@@ -99,52 +98,9 @@ export class NodeMCPServerStdio extends BaseMCPServerStdio {
9998
this._cacheDirty = true;
10099
}
101100

102-
// The response element type is intentionally left as `any` to avoid explosing MCP SDK type dependencies.
103-
protected async _applyToolFilter(
104-
tools: MCPTool[],
105-
runContext?: RunContext<any>,
106-
agent?: Agent<any, any>,
107-
): Promise<MCPTool[]> {
108-
if (!this.toolFilter) {
109-
return tools;
110-
}
111-
112-
if (typeof this.toolFilter === 'function') {
113-
const ctx = {
114-
runContext: runContext as RunContext<any>,
115-
agent: agent as Agent<any, any>,
116-
serverName: this.name,
117-
} as ToolFilterContext<any>;
118-
const filtered: MCPTool[] = [];
119-
for (const t of tools) {
120-
try {
121-
const res = this.toolFilter(ctx, t);
122-
const include = res instanceof Promise ? await res : res;
123-
if (include) filtered.push(t);
124-
} catch (e) {
125-
this.logger.error(
126-
`Error applying tool filter to tool '${t.name}' on server '${this.name}': ${e}`,
127-
);
128-
}
129-
}
130-
return filtered;
131-
}
132-
133-
let filtered = tools;
134-
if (this.toolFilter.allowedToolNames) {
135-
const allowed = new Set(this.toolFilter.allowedToolNames);
136-
filtered = filtered.filter((t) => allowed.has(t.name));
137-
}
138-
if (this.toolFilter.blockedToolNames) {
139-
const blocked = new Set(this.toolFilter.blockedToolNames);
140-
filtered = filtered.filter((t) => !blocked.has(t.name));
141-
}
142-
return filtered;
143-
}
144-
145101
async listTools(
146-
runContext?: RunContext<any>,
147-
agent?: Agent<any, any>,
102+
_runContext?: RunContext<any>,
103+
_agent?: Agent<any, any>,
148104
): Promise<MCPTool[]> {
149105
const { ListToolsResultSchema } = await import(
150106
'@modelcontextprotocol/sdk/types.js'
@@ -154,17 +110,15 @@ export class NodeMCPServerStdio extends BaseMCPServerStdio {
154110
'Server not initialized. Make sure you call connect() first.',
155111
);
156112
}
157-
let tools: MCPTool[];
158113
if (this.cacheToolsList && !this._cacheDirty && this._toolsList) {
159-
tools = this._toolsList;
160-
} else {
161-
this._cacheDirty = false;
162-
const response = await this.session.listTools();
163-
this.debugLog(() => `Listed tools: ${JSON.stringify(response)}`);
164-
this._toolsList = ListToolsResultSchema.parse(response).tools;
165-
tools = this._toolsList;
114+
return this._toolsList;
166115
}
167-
return this._applyToolFilter(tools, runContext, agent);
116+
117+
this._cacheDirty = false;
118+
const response = await this.session.listTools();
119+
this.debugLog(() => `Listed tools: ${JSON.stringify(response)}`);
120+
this._toolsList = ListToolsResultSchema.parse(response).tools;
121+
return this._toolsList;
168122
}
169123

170124
async callTool(
@@ -264,51 +218,9 @@ export class NodeMCPServerStreamableHttp extends BaseMCPServerStreamableHttp {
264218
this._cacheDirty = true;
265219
}
266220

267-
// The response element type is intentionally left as `any` to avoid explosing MCP SDK type dependencies.
268-
protected async _applyToolFilter(
269-
tools: MCPTool[],
270-
runContext?: RunContext<any>,
271-
agent?: Agent<any, any>,
272-
): Promise<MCPTool[]> {
273-
if (!this.toolFilter) {
274-
return tools;
275-
}
276-
if (typeof this.toolFilter === 'function') {
277-
const ctx: ToolFilterContext<any> = {
278-
runContext: runContext as RunContext<any>,
279-
agent: agent as Agent<any, any>,
280-
serverName: this.name,
281-
};
282-
const filtered: MCPTool[] = [];
283-
for (const t of tools) {
284-
try {
285-
const res = this.toolFilter(ctx, t);
286-
const include = res instanceof Promise ? await res : res;
287-
if (include) filtered.push(t);
288-
} catch (e) {
289-
this.logger.error(
290-
`Error applying tool filter to tool '${t.name}' on server '${this.name}': ${e}`,
291-
);
292-
}
293-
}
294-
return filtered;
295-
}
296-
297-
let filtered = tools;
298-
if (this.toolFilter.allowedToolNames) {
299-
const allowed = new Set(this.toolFilter.allowedToolNames);
300-
filtered = filtered.filter((t) => allowed.has(t.name));
301-
}
302-
if (this.toolFilter.blockedToolNames) {
303-
const blocked = new Set(this.toolFilter.blockedToolNames);
304-
filtered = filtered.filter((t) => !blocked.has(t.name));
305-
}
306-
return filtered;
307-
}
308-
309221
async listTools(
310-
runContext?: RunContext<any>,
311-
agent?: Agent<any, any>,
222+
_runContext?: RunContext<any>,
223+
_agent?: Agent<any, any>,
312224
): Promise<MCPTool[]> {
313225
const { ListToolsResultSchema } = await import(
314226
'@modelcontextprotocol/sdk/types.js'
@@ -318,17 +230,15 @@ export class NodeMCPServerStreamableHttp extends BaseMCPServerStreamableHttp {
318230
'Server not initialized. Make sure you call connect() first.',
319231
);
320232
}
321-
let tools: MCPTool[];
322233
if (this.cacheToolsList && !this._cacheDirty && this._toolsList) {
323-
tools = this._toolsList;
324-
} else {
325-
this._cacheDirty = false;
326-
const response = await this.session.listTools();
327-
this.debugLog(() => `Listed tools: ${JSON.stringify(response)}`);
328-
this._toolsList = ListToolsResultSchema.parse(response).tools;
329-
tools = this._toolsList;
234+
return this._toolsList;
330235
}
331-
return this._applyToolFilter(tools, runContext, agent);
236+
237+
this._cacheDirty = false;
238+
const response = await this.session.listTools();
239+
this.debugLog(() => `Listed tools: ${JSON.stringify(response)}`);
240+
this._toolsList = ListToolsResultSchema.parse(response).tools;
241+
return this._toolsList;
332242
}
333243

334244
async callTool(

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ describe('MCP tool filtering', () => {
4949
const server = new StubServer(
5050
's',
5151
tools,
52-
createStaticToolFilter(['a'], ['b']),
52+
createStaticToolFilter({ allowed: ['a'], blocked: ['b'] }),
5353
);
5454
const agent = new Agent({
5555
name: 'agent',
@@ -61,7 +61,7 @@ describe('MCP tool filtering', () => {
6161
});
6262
const runContext = new RunContext();
6363
const result = await server.listTools(runContext, agent);
64-
expect(result.map((t) => t.name)).toEqual(['a']);
64+
expect(result.map((t) => t.name)).toEqual(['a', 'b']);
6565
});
6666
});
6767

@@ -101,7 +101,7 @@ describe('MCP tool filtering', () => {
101101
});
102102
const runContext = new RunContext();
103103
const result = await server.listTools(runContext, agent);
104-
expect(result.map((t) => t.name)).toEqual(['good']);
104+
expect(result.map((t) => t.name)).toEqual(['good', 'bad']);
105105
});
106106
});
107107

@@ -144,7 +144,7 @@ describe('MCP tool filtering', () => {
144144
const serverA = new StubServer(
145145
'A',
146146
toolsA,
147-
createStaticToolFilter(['a1']),
147+
createStaticToolFilter({ allowed: ['a1'] }),
148148
);
149149
const serverB = new StubServer('B', toolsB);
150150
const agent = new Agent({
@@ -158,7 +158,7 @@ describe('MCP tool filtering', () => {
158158
const runContext = new RunContext();
159159
const resultA = await serverA.listTools(runContext, agent);
160160
const resultB = await serverB.listTools(runContext, agent);
161-
expect(resultA.map((t) => t.name)).toEqual(['a1']);
161+
expect(resultA.map((t) => t.name)).toEqual(['a1', 'a2']);
162162
expect(resultB.map((t) => t.name)).toEqual(['b1']);
163163
});
164164
});
@@ -180,7 +180,7 @@ describe('MCP tool filtering', () => {
180180
const server = new StubServer(
181181
'cache',
182182
tools,
183-
createStaticToolFilter(['x']),
183+
createStaticToolFilter({ allowed: ['x'] }),
184184
);
185185
const agent = new Agent({
186186
name: 'agent',
@@ -193,9 +193,9 @@ describe('MCP tool filtering', () => {
193193
const runContext = new RunContext();
194194
let result = await server.listTools(runContext, agent);
195195
expect(result.map((t) => t.name)).toEqual(['x']);
196-
server.toolFilter = createStaticToolFilter(['y']);
196+
(server as any).toolFilter = createStaticToolFilter({ allowed: ['y'] });
197197
result = await server.listTools(runContext, agent);
198-
expect(result.map((t) => t.name)).toEqual([]);
198+
expect(result.map((t) => t.name)).toEqual(['x']);
199199
});
200200
});
201201
});

0 commit comments

Comments
 (0)