Skip to content

Commit f50746b

Browse files
authored
feat: adds run_component tool (#172)
1 parent d293ed1 commit f50746b

3 files changed

Lines changed: 115 additions & 0 deletions

File tree

src/deepset_mcp/tool_registry.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
get_component_definition as get_component_definition_tool,
1919
get_custom_components as get_custom_components_tool,
2020
list_component_families as list_component_families_tool,
21+
run_component as run_component_tool,
2122
search_component_definition as search_component_definition_tool,
2223
)
2324
from deepset_mcp.tools.indexes import (
@@ -195,6 +196,10 @@ async def search_docs(query: str) -> str:
195196
get_custom_components_tool,
196197
ToolConfig(needs_client=True, memory_type=MemoryType.EXPLORABLE),
197198
),
199+
"run_component": (
200+
run_component_tool,
201+
ToolConfig(needs_client=True, memory_type=MemoryType.BOTH),
202+
),
198203
"list_secrets": (list_secrets_tool, ToolConfig(needs_client=True, memory_type=MemoryType.EXPLORABLE)),
199204
"get_secret": (get_secret_tool, ToolConfig(needs_client=True, memory_type=MemoryType.EXPLORABLE)),
200205
"list_workspaces": (list_workspaces_tool, ToolConfig(needs_client=True, memory_type=MemoryType.EXPLORABLE)),

src/deepset_mcp/tools/haystack_service.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,3 +360,41 @@ async def build_single_component(schema: dict[str, Any]) -> ComponentDefinition
360360
component_definitions = [comp for comp in results if comp is not None]
361361

362362
return ComponentDefinitionList(components=component_definitions, total_count=len(component_definitions))
363+
364+
365+
async def run_component(
366+
*,
367+
client: AsyncClientProtocol,
368+
component_type: str,
369+
init_params: dict[str, Any] | None = None,
370+
input_data: dict[str, Any] | None = None,
371+
input_types: dict[str, str] | None = None,
372+
) -> dict[str, Any] | str:
373+
"""Run a Haystack component with the given parameters.
374+
375+
This tool allows you to execute a Haystack component by providing its type
376+
and initialization parameters, then passing input data to get results.
377+
Use this to test components and see how they would work in your pipeline.
378+
379+
:param client: The API client to use
380+
:param component_type: The type of component to run
381+
(e.g., "haystack.components.builders.prompt_builder.PromptBuilder")
382+
:param init_params: Initialization parameters for the component
383+
:param input_data: Input data for the component
384+
:param input_types: Optional type information for inputs (inferred if not provided). For custom types use the full
385+
import path (e.g. haystack.dataclasses.document.Document for Document)
386+
387+
:returns: Dictionary containing the component's outputs or error message string
388+
"""
389+
haystack_service = client.haystack_service()
390+
391+
try:
392+
result = await haystack_service.run_component(
393+
component_type=component_type,
394+
init_params=init_params,
395+
input_data=input_data,
396+
input_types=input_types,
397+
)
398+
return result
399+
except Exception as e:
400+
return f"Failed to run component: {str(e)}"

test/unit/tools/test_haystack_service.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
get_component_definition,
1414
get_custom_components,
1515
list_component_families,
16+
run_component,
1617
search_component_definition,
1718
)
1819
from deepset_mcp.tools.haystack_service_models import (
@@ -546,3 +547,74 @@ async def test_get_custom_components_api_error() -> None:
546547
assert isinstance(result, str)
547548
assert "Error retrieving component schemas" in result
548549
assert "API Error" in result
550+
551+
552+
@pytest.mark.asyncio
553+
async def test_run_component_success() -> None:
554+
run_response = {
555+
"output": {
556+
"prompt": "Hello, world! This is a test prompt.",
557+
}
558+
}
559+
resource = FakeHaystackServiceResource(run_component_response=run_response)
560+
client = FakeClient(resource=resource)
561+
562+
result = await run_component(
563+
client=client,
564+
component_type="haystack.components.builders.PromptBuilder",
565+
init_params={"template": "Hello, {{name}}! This is a {{type}} prompt."},
566+
input_data={"name": "world", "type": "test"},
567+
)
568+
569+
assert isinstance(result, dict)
570+
assert "output" in result
571+
assert result["output"]["prompt"] == "Hello, world! This is a test prompt."
572+
573+
574+
@pytest.mark.asyncio
575+
async def test_run_component_with_input_types() -> None:
576+
run_response = {"output": {"documents": [{"content": "Test document", "meta": {}}]}}
577+
resource = FakeHaystackServiceResource(run_component_response=run_response)
578+
client = FakeClient(resource=resource)
579+
580+
result = await run_component(
581+
client=client,
582+
component_type="haystack.components.readers.TextFileReader",
583+
init_params={},
584+
input_data={"sources": ["/path/to/file.txt"]},
585+
input_types={"sources": "List[str]"},
586+
)
587+
588+
assert isinstance(result, dict)
589+
assert "output" in result
590+
assert "documents" in result["output"]
591+
assert len(result["output"]["documents"]) == 1
592+
assert result["output"]["documents"][0]["content"] == "Test document"
593+
594+
595+
@pytest.mark.asyncio
596+
async def test_run_component_minimal_params() -> None:
597+
run_response = {"output": {"result": "success"}}
598+
resource = FakeHaystackServiceResource(run_component_response=run_response)
599+
client = FakeClient(resource=resource)
600+
601+
result = await run_component(client=client, component_type="haystack.components.readers.HTMLReader")
602+
603+
assert isinstance(result, dict)
604+
assert result["output"]["result"] == "success"
605+
606+
607+
@pytest.mark.asyncio
608+
async def test_run_component_api_error() -> None:
609+
resource = FakeHaystackServiceResource(exception=UnexpectedAPIError(status_code=500, message="API Error"))
610+
client = FakeClient(resource=resource)
611+
612+
result = await run_component(
613+
client=client,
614+
component_type="haystack.components.builders.PromptBuilder",
615+
init_params={"template": "Hello, {{name}}!"},
616+
)
617+
618+
assert isinstance(result, str)
619+
assert "Failed to run component" in result
620+
assert "API Error" in result

0 commit comments

Comments
 (0)