Skip to content

Commit 2518772

Browse files
authored
feat: allow tool selection (#135)
1 parent 41ff4c2 commit 2518772

2 files changed

Lines changed: 100 additions & 46 deletions

File tree

src/deepset_mcp/main.py

Lines changed: 27 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,7 @@
55

66
from mcp.server.fastmcp import FastMCP
77

8-
from deepset_mcp.api.client import AsyncDeepsetClient
9-
from deepset_mcp.tool_factory import WorkspaceMode, register_all_tools
10-
from deepset_mcp.tools.doc_search import (
11-
get_docs_config,
12-
search_docs as search_docs_tool,
13-
)
8+
from deepset_mcp.tool_factory import WorkspaceMode, register_tools
149

1510
# Initialize MCP Server
1611
mcp = FastMCP("Deepset Cloud MCP", settings={"log_level": "ERROR"})
@@ -31,41 +26,6 @@ async def deepset_copilot() -> str:
3126
return prompt_path.read_text()
3227

3328

34-
# Check if docs search should be enabled
35-
docs_config = get_docs_config()
36-
if docs_config:
37-
docs_workspace, docs_pipeline_name, docs_api_key = docs_config
38-
39-
async def search_docs(query: str) -> str:
40-
"""Search the deepset platform documentation.
41-
42-
This tool allows you to search through deepset's official documentation to find
43-
information about features, API usage, best practices, and troubleshooting guides.
44-
Use this when you need to look up specific deepset functionality or help users
45-
understand how to use deepset features.
46-
47-
:param query: The search query to execute against the documentation.
48-
:returns: The formatted search results from the documentation.
49-
"""
50-
async with AsyncDeepsetClient(api_key=docs_api_key) as client:
51-
response = await search_docs_tool(
52-
client=client,
53-
workspace=docs_workspace,
54-
pipeline_name=docs_pipeline_name,
55-
query=query,
56-
)
57-
return response
58-
59-
# Add the tool to the server
60-
mcp.add_tool(search_docs)
61-
62-
else:
63-
logging.warning(
64-
"Documentation search tool not enabled. To enable, set the following environment variables: "
65-
"DEEPSET_DOCS_WORKSPACE, DEEPSET_DOCS_PIPELINE_NAME, DEEPSET_DOCS_API_KEY"
66-
)
67-
68-
6929
def main() -> None:
7030
"""Entrypoint for the deepset MCP server."""
7131
parser = argparse.ArgumentParser(description="Run the Deepset MCP server.")
@@ -97,8 +57,27 @@ def main() -> None:
9757
default="implicit",
9858
help="Whether workspace is implicit (from env) or explicit (as parameter). Default: implicit",
9959
)
60+
parser.add_argument(
61+
"--tools",
62+
nargs="*",
63+
help="Space-separated list of tools to register (default: all)",
64+
)
65+
parser.add_argument(
66+
"--list-tools",
67+
action="store_true",
68+
help="List all available tools and exit",
69+
)
10070
args = parser.parse_args()
10171

72+
# Handle --list-tools flag early
73+
if args.list_tools:
74+
from deepset_mcp.tool_factory import TOOL_REGISTRY
75+
76+
print("Available tools:")
77+
for tool_name in sorted(TOOL_REGISTRY.keys()):
78+
print(f" {tool_name}")
79+
return
80+
10281
# prefer flags, fallback to env
10382
workspace = args.workspace or os.getenv("DEEPSET_WORKSPACE")
10483
api_key = args.api_key or os.getenv("DEEPSET_API_KEY")
@@ -130,8 +109,13 @@ def main() -> None:
130109
if docs_api_key:
131110
os.environ["DEEPSET_DOCS_API_KEY"] = docs_api_key
132111

133-
# Register all tools based on configuration
134-
register_all_tools(mcp, workspace_mode, workspace)
112+
# Parse tool names if provided
113+
tool_names = None
114+
if args.tools:
115+
tool_names = set(args.tools)
116+
117+
# Register tools based on configuration
118+
register_tools(mcp, workspace_mode, workspace, tool_names)
135119

136120
# run with SSE transport (HTTP+Server-Sent Events)
137121
mcp.run(transport="stdio")

src/deepset_mcp/tool_factory.py

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import functools
44
import inspect
5+
import logging
56
import os
67
from collections.abc import Awaitable, Callable
78
from dataclasses import dataclass
@@ -17,6 +18,10 @@
1718
get_latest_custom_component_installation_logs as get_latest_custom_component_installation_logs_tool,
1819
list_custom_component_installations as list_custom_component_installations_tool,
1920
)
21+
from deepset_mcp.tools.doc_search import (
22+
get_docs_config,
23+
search_docs as search_docs_tool,
24+
)
2025
from deepset_mcp.tools.haystack_service import (
2126
get_component_definition as get_component_definition_tool,
2227
get_custom_components as get_custom_components_tool,
@@ -92,6 +97,32 @@ def get_slice_from_object_store(
9297
return EXPLORER.slice(obj_id=object_id, start=start, end=end, path=path)
9398

9499

100+
async def search_docs(query: str) -> str:
101+
"""Search the deepset platform documentation.
102+
103+
This tool allows you to search through deepset's official documentation to find
104+
information about features, API usage, best practices, and troubleshooting guides.
105+
Use this when you need to look up specific deepset functionality or help users
106+
understand how to use deepset features.
107+
108+
:param query: The search query to execute against the documentation.
109+
:returns: The formatted search results from the documentation.
110+
"""
111+
docs_config = get_docs_config()
112+
if not docs_config:
113+
raise RuntimeError("Documentation search configuration not available")
114+
115+
docs_workspace, docs_pipeline_name, docs_api_key = docs_config
116+
async with AsyncDeepsetClient(api_key=docs_api_key) as client:
117+
response = await search_docs_tool(
118+
client=client,
119+
workspace=docs_workspace,
120+
pipeline_name=docs_pipeline_name,
121+
query=query,
122+
)
123+
return response
124+
125+
95126
class WorkspaceMode(StrEnum):
96127
"""Configuration for how workspace is provided to tools."""
97128

@@ -236,6 +267,7 @@ def get_workspace_from_env() -> str:
236267
"create_workspace": (create_workspace_tool, ToolConfig(needs_client=True, memory_type=MemoryType.EXPLORABLE)),
237268
"get_from_object_store": (get_from_object_store, ToolConfig(memory_type=MemoryType.NO_MEMORY)),
238269
"get_slice_from_object_store": (get_slice_from_object_store, ToolConfig(memory_type=MemoryType.NO_MEMORY)),
270+
"search_docs": (search_docs, ToolConfig(memory_type=MemoryType.NO_MEMORY)),
239271
}
240272

241273

@@ -388,15 +420,53 @@ async def async_wrapper(**kwargs: Any) -> Any:
388420
return wrapper
389421

390422

391-
def register_all_tools(mcp: FastMCP, workspace_mode: WorkspaceMode, workspace: str | None = None) -> None:
392-
"""Register all tools with unified configuration.
423+
def register_tools(
424+
mcp: FastMCP, workspace_mode: WorkspaceMode, workspace: str | None = None, tool_names: set[str] | None = None
425+
) -> None:
426+
"""Register tools with unified configuration.
393427
394428
Args:
395429
mcp: FastMCP server instance
396430
workspace_mode: How workspace should be handled
397431
workspace: Workspace to use for implicit mode (if None, reads from env)
432+
tool_names: Set of tool names to register (if None, registers all tools)
398433
"""
399-
for tool_name, (base_func, config) in TOOL_REGISTRY.items():
434+
# Check if docs search is available
435+
docs_available = get_docs_config() is not None
436+
437+
# Validate tool names if provided
438+
if tool_names is not None:
439+
all_tools = set(TOOL_REGISTRY.keys())
440+
invalid_tools = tool_names - all_tools
441+
if invalid_tools:
442+
sorted_invalid = sorted(invalid_tools)
443+
sorted_all = sorted(all_tools)
444+
raise ValueError(f"Unknown tools: {', '.join(sorted_invalid)}\nAvailable tools: {', '.join(sorted_all)}")
445+
446+
# Warn if search_docs was requested but config is missing
447+
if "search_docs" in tool_names and not docs_available:
448+
logging.warning(
449+
"Documentation search tool requested but not available. To enable, set the following environment "
450+
"variables: DEEPSET_DOCS_WORKSPACE, DEEPSET_DOCS_PIPELINE_NAME, DEEPSET_DOCS_API_KEY"
451+
)
452+
453+
tools_to_register = tool_names.copy()
454+
else:
455+
tools_to_register = set(TOOL_REGISTRY.keys())
456+
457+
# Warn if search_docs would be skipped in "all tools" mode
458+
if not docs_available:
459+
logging.warning(
460+
"Documentation search tool not enabled. To enable, set the following environment "
461+
"variables: DEEPSET_DOCS_WORKSPACE, DEEPSET_DOCS_PIPELINE_NAME, DEEPSET_DOCS_API_KEY"
462+
)
463+
464+
# Remove search_docs if config is not available
465+
if not docs_available:
466+
tools_to_register.discard("search_docs")
467+
468+
for tool_name in tools_to_register:
469+
base_func, config = TOOL_REGISTRY[tool_name]
400470
# Create enhanced tool
401471
enhanced_tool = create_enhanced_tool(base_func, config, workspace_mode, workspace)
402472

0 commit comments

Comments
 (0)