Skip to content

Commit f93935c

Browse files
committed
Add tool name filtering to getAgentTools()
Enables filtering tools by name in addition to server-level filtering. Supports both raw tool names (e.g., 'add') and server-prefixed names (e.g., 'time__get_current_time'). - Add tools parameter to AgentToolsOptions interface - Implement filtering logic with validation for prefixed format - Add TOOL_SEPARATOR constant to eliminate magic strings - Add comprehensive unit tests (9 test cases) - Update README with examples and progressive disclosure explanation
1 parent 1d6b889 commit f93935c

File tree

4 files changed

+485
-8
lines changed

4 files changed

+485
-8
lines changed

README.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,8 +359,15 @@ if (await client.isServerHealthy("time")) {
359359

360360
Generate callable functions that work directly with AI agent frameworks. No conversion layers needed.
361361

362+
**Why filter tools?**
363+
364+
AI agents perform better with focused tool sets.
365+
Tool filtering enables progressive disclosure - operators can expose a subset of server tools via `mcpd` configuration,
366+
then agents can further narrow down to only the tools needed for their specific task.
367+
This prevents overwhelming the model's context window and improves response quality.
368+
362369
```typescript
363-
// Options: { servers?: string[], format?: 'array' | 'object' | 'map' }
370+
// Options: { servers?: string[], tools?: string[], format?: 'array' | 'object' | 'map' }
364371
// Default format is 'array' (for LangChain)
365372

366373
// Use with LangChain JS (array format is default)
@@ -397,6 +404,28 @@ const timeTools = await client.getAgentTools({
397404
format: "array",
398405
});
399406

407+
// Filter by tool names (cross-cutting across all servers)
408+
const mathTools = await client.getAgentTools({
409+
tools: ["add", "multiply"],
410+
});
411+
412+
// Filter by qualified tool names (server-specific)
413+
const specificTools = await client.getAgentTools({
414+
tools: ["time__get_current_time", "math__add"],
415+
});
416+
417+
// Combine server and tool filtering
418+
const filteredTools = await client.getAgentTools({
419+
servers: ["time", "math"],
420+
tools: ["add", "get_current_time"],
421+
});
422+
423+
// Tool filtering works with different formats
424+
const toolsObject = await client.getAgentTools({
425+
tools: ["add", "multiply"],
426+
format: "object",
427+
});
428+
400429
// Use with Map for efficient lookups
401430
const toolMap = await client.getAgentTools({ format: "map" });
402431
const timeTool = toolMap.get("time__get_current_time");

src/client.ts

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ import { API_PATHS } from "./apiPaths";
5050
*/
5151
const SERVER_HEALTH_CACHE_MAXSIZE = 100;
5252

53+
/**
54+
* Separator used between server name and tool name in qualified tool names.
55+
* Format: `{serverName}{TOOL_SEPARATOR}{toolName}`
56+
* Example: "time__get_current_time" where "time" is server and "get_current_time" is tool.
57+
*/
58+
const TOOL_SEPARATOR = "__";
59+
5360
/**
5461
* Client for interacting with MCP (Model Context Protocol) servers through an mcpd daemon.
5562
*
@@ -789,36 +796,72 @@ export class McpdClient {
789796
async getAgentTools(options?: {
790797
format?: "array";
791798
servers?: string[];
799+
tools?: string[];
792800
}): Promise<AgentFunction[]>;
793801
async getAgentTools(options: {
794802
format: "object";
795803
servers?: string[];
804+
tools?: string[];
796805
}): Promise<Record<string, AgentFunction>>;
797806
async getAgentTools(options: {
798807
format: "map";
799808
servers?: string[];
809+
tools?: string[];
800810
}): Promise<Map<string, AgentFunction>>;
801811
async getAgentTools(
802812
options: AgentToolsOptions = {},
803813
): Promise<
804814
AgentFunction[] | Record<string, AgentFunction> | Map<string, AgentFunction>
805815
> {
806-
const { servers, format = "array" } = options;
816+
const { servers, tools, format = "array" } = options;
817+
818+
const allTools = await this.agentTools(servers);
807819

808-
// Get tools in array format (default internal representation)
809-
const tools = await this.agentTools(servers);
820+
const filteredTools = tools
821+
? allTools.filter((tool) => this.#matchesToolFilter(tool, tools))
822+
: allTools;
810823

811-
// Return in requested format
812824
switch (format) {
813825
case "object":
814-
return Object.fromEntries(tools.map((tool) => [tool.name, tool]));
826+
return Object.fromEntries(
827+
filteredTools.map((tool) => [tool.name, tool]),
828+
);
815829

816830
case "map":
817-
return new Map(tools.map((tool) => [tool.name, tool]));
831+
return new Map(filteredTools.map((tool) => [tool.name, tool]));
818832

819833
case "array":
820834
default:
821-
return tools;
835+
return filteredTools;
822836
}
823837
}
838+
839+
/**
840+
* Check if a tool matches the tool filter.
841+
*
842+
* Supports two formats:
843+
* - Raw tool name: "get_current_time" (matches tool name only)
844+
* - Server-prefixed name: "time__get_current_time" (server + TOOL_SEPARATOR + tool)
845+
*
846+
* @param tool The tool to check.
847+
* @param tools List of tool names/patterns to match against.
848+
* @returns True if the tool matches any item in the filter.
849+
*/
850+
#matchesToolFilter(tool: AgentFunction, tools: string[]): boolean {
851+
return tools.some((filterItem) => {
852+
const separatorIndex = filterItem.indexOf(TOOL_SEPARATOR);
853+
if (separatorIndex !== -1) {
854+
const leftPart = filterItem.slice(0, separatorIndex).trim();
855+
const rightPart = filterItem
856+
.slice(separatorIndex + TOOL_SEPARATOR.length)
857+
.trim();
858+
859+
if (leftPart && rightPart && filterItem === tool.name) {
860+
return true;
861+
}
862+
}
863+
864+
return filterItem === tool._toolName;
865+
});
866+
}
824867
}

src/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,16 @@ export interface AgentToolsOptions {
238238
*/
239239
servers?: string[];
240240

241+
/**
242+
* Optional list of tool names to filter by. Supports both:
243+
* - Raw tool names: 'get_current_time' (matches tool across all servers)
244+
* - Server-prefixed names: 'time__get_current_time' (server + TOOL_SEPARATOR + tool)
245+
* If not specified, returns all tools from selected servers.
246+
* @example ['add', 'multiply']
247+
* @example ['time__get_current_time', 'math__add']
248+
*/
249+
tools?: string[];
250+
241251
/**
242252
* Output format for the tools.
243253
* - 'array': Returns array of functions (default, for LangChain)

0 commit comments

Comments
 (0)