Skip to content

Commit 940ac41

Browse files
authored
feat: use memory with custom component tools (#115)
1 parent 77f310e commit 940ac41

4 files changed

Lines changed: 66 additions & 77 deletions

File tree

src/deepset_mcp/api/custom_components/models.py

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

33
from pydantic import BaseModel
44

5+
from deepset_mcp.api.shared_models import DeepsetUser
6+
57

68
class CustomComponentInstallation(BaseModel):
79
"""Model representing a custom component installation."""
@@ -12,6 +14,7 @@ class CustomComponentInstallation(BaseModel):
1214
created_by_user_id: str
1315
logs: list[dict[str, Any]]
1416
organization_id: str
17+
user_info: DeepsetUser | None = None
1518

1619

1720
class CustomComponentInstallationList(BaseModel):

src/deepset_mcp/tool_factory.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,11 @@ def get_workspace_from_env() -> str:
195195
),
196196
"list_custom_component_installations": (
197197
list_custom_component_installations_tool,
198-
ToolConfig(needs_client=True, needs_workspace=True),
198+
ToolConfig(needs_client=True, needs_workspace=True, memory_type=MemoryType.EXPLORABLE),
199199
),
200200
"get_latest_custom_component_installation_logs": (
201201
get_latest_custom_component_installation_logs_tool,
202-
ToolConfig(needs_client=True, needs_workspace=True),
202+
ToolConfig(needs_client=True, needs_workspace=True, memory_type=MemoryType.EXPLORABLE),
203203
),
204204
# Non-workspace tools
205205
"list_component_families": (
Lines changed: 21 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1+
from deepset_mcp.api.custom_components.models import CustomComponentInstallationList
12
from deepset_mcp.api.protocols import AsyncClientProtocol
23

34

4-
async def list_custom_component_installations(client: AsyncClientProtocol, workspace: str) -> str:
5+
async def list_custom_component_installations(
6+
client: AsyncClientProtocol, workspace: str
7+
) -> CustomComponentInstallationList | str:
58
"""List custom component installations.
69
710
:param client: The API client to use.
811
:param workspace: The workspace to operate in.
912
10-
:returns: A formatted string containing installation information.
13+
:returns: Custom component installations or error message.
1114
"""
1215
custom_components = client.custom_components(workspace)
1316
users = client.users()
@@ -17,58 +20,17 @@ async def list_custom_component_installations(client: AsyncClientProtocol, works
1720
except Exception as e:
1821
return f"Failed to retrieve custom component installations: {e}"
1922

20-
if not installations.data:
21-
return "No custom component installations found."
22-
23-
# Format the response
24-
formatted_output = [
25-
f"# Custom Component Installations (showing {len(installations.data)} of {installations.total})\n"
26-
]
27-
28-
for install in installations.data:
29-
# Try to fetch user information
30-
user_info = "Unknown"
31-
if install.created_by_user_id:
23+
# Enrich installations with user information
24+
for installation in installations.data:
25+
if installation.created_by_user_id:
3226
try:
33-
user = await users.get(install.created_by_user_id)
34-
given_name = user.given_name or ""
35-
family_name = user.family_name or ""
36-
email = user.email or ""
37-
user_info = f"{given_name} {family_name} ({email})" if email else f"{given_name} {family_name}"
38-
user_info = user_info.strip()
39-
if not user_info:
40-
user_info = "Unknown"
27+
user = await users.get(installation.created_by_user_id)
28+
installation.user_info = user
4129
except Exception:
42-
user_info = "Unknown"
43-
44-
# Format installation details
45-
install_details = [
46-
f"## Installation {install.custom_component_id[:8]}...",
47-
f"- **Status**: {install.status}",
48-
f"- **Version**: {install.version}",
49-
f"- **Installed by**: {user_info}",
50-
]
51-
52-
# Add logs if available
53-
if install.logs:
54-
install_details.append("\n### Recent Logs:")
55-
for log in install.logs[:5]: # Show only the first 5 logs
56-
level = log.get("level", "INFO")
57-
msg = log.get("msg", "No message")
58-
install_details.append(f"- [{level}] {msg}")
59-
60-
if len(install.logs) > 5:
61-
install_details.append(f"- ... and {len(install.logs) - 5} more log entries")
62-
63-
formatted_output.append("\n".join(install_details) + "\n")
30+
# If user fetch fails, user_info remains None
31+
pass
6432

65-
if installations.has_more:
66-
formatted_output.append(
67-
"*Note: There are more installations available. This listing shows only the most recent.*"
68-
)
69-
70-
# Join all sections and return
71-
return "\n".join(formatted_output)
33+
return installations
7234

7335

7436
async def get_latest_custom_component_installation_logs(client: AsyncClientProtocol, workspace: str) -> str:
@@ -77,13 +39,14 @@ async def get_latest_custom_component_installation_logs(client: AsyncClientProto
7739
:param client: The API client to use.
7840
:param workspace: The workspace to operate in.
7941
80-
:returns: A formatted string containing the latest installation logs.
42+
:returns: The latest installation logs or error message.
8143
"""
8244
custom_components = client.custom_components(workspace)
8345

84-
logs = await custom_components.get_latest_installation_logs()
85-
86-
if not logs:
87-
return "No installation logs found."
88-
89-
return f"Latest custom component installation logs:\n\n{logs}"
46+
try:
47+
logs = await custom_components.get_latest_installation_logs()
48+
if not logs:
49+
return "No installation logs found."
50+
return logs
51+
except Exception as e:
52+
return f"Failed to retrieve latest installation logs: {e}"

test/unit/tools/test_custom_components.py

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -130,18 +130,35 @@ async def test_list_custom_component_installations() -> None:
130130

131131
result = await list_custom_component_installations(client, "test-workspace")
132132

133-
assert "# Custom Component Installations (showing 2 of 2)" in result
134-
assert "## Installation comp_123..." in result
135-
assert "## Installation comp_456..." in result
136-
assert "**Status**: installed" in result
137-
assert "**Status**: failed" in result
138-
assert "**Version**: 1.0.0" in result
139-
assert "**Version**: 0.9.0" in result
140-
assert "**Installed by**: John Doe (john.doe@example.com)" in result
141-
assert "**Installed by**: Jane Smith (jane.smith@example.com)" in result
142-
assert "[INFO] Installation complete" in result
143-
assert "[ERROR] Installation failed" in result
144-
assert "[DEBUG] Debug info" in result
133+
assert isinstance(result, CustomComponentInstallationList)
134+
assert len(result.data) == 2
135+
assert result.total == 2
136+
assert result.has_more is False
137+
138+
# Check first installation
139+
first_install = result.data[0]
140+
assert first_install.custom_component_id == "comp_123"
141+
assert first_install.status == "installed"
142+
assert first_install.version == "1.0.0"
143+
assert first_install.created_by_user_id == "user_123"
144+
assert len(first_install.logs) == 1
145+
assert first_install.logs[0]["level"] == "INFO"
146+
assert first_install.user_info is not None
147+
assert first_install.user_info.given_name == "John"
148+
assert first_install.user_info.family_name == "Doe"
149+
assert first_install.user_info.email == "john.doe@example.com"
150+
151+
# Check second installation
152+
second_install = result.data[1]
153+
assert second_install.custom_component_id == "comp_456"
154+
assert second_install.status == "failed"
155+
assert second_install.version == "0.9.0"
156+
assert second_install.created_by_user_id == "user_456"
157+
assert len(second_install.logs) == 2
158+
assert second_install.user_info is not None
159+
assert second_install.user_info.given_name == "Jane"
160+
assert second_install.user_info.family_name == "Smith"
161+
assert second_install.user_info.email == "jane.smith@example.com"
145162

146163

147164
@pytest.mark.asyncio
@@ -162,7 +179,10 @@ async def test_list_custom_component_installations_empty() -> None:
162179

163180
result = await list_custom_component_installations(client, "test-workspace")
164181

165-
assert result == "No custom component installations found."
182+
assert isinstance(result, CustomComponentInstallationList)
183+
assert len(result.data) == 0
184+
assert result.total == 0
185+
assert result.has_more is False
166186

167187

168188
@pytest.mark.asyncio
@@ -192,7 +212,10 @@ async def test_list_custom_component_installations_user_fetch_error() -> None:
192212

193213
result = await list_custom_component_installations(client, "test-workspace")
194214

195-
assert "**Installed by**: Unknown" in result
215+
assert isinstance(result, CustomComponentInstallationList)
216+
assert len(result.data) == 1
217+
assert result.data[0].created_by_user_id == "user_unknown"
218+
assert result.data[0].user_info is None # User fetch failed, so user_info should be None
196219

197220

198221
@pytest.mark.asyncio
@@ -220,7 +243,7 @@ async def test_get_latest_custom_component_installation_logs() -> None:
220243

221244
result = await get_latest_custom_component_installation_logs(client, "test-workspace")
222245

223-
assert result == f"Latest custom component installation logs:\n\n{mock_logs}"
246+
assert result == mock_logs
224247

225248

226249
@pytest.mark.asyncio
@@ -242,5 +265,5 @@ async def test_get_latest_custom_component_installation_logs_api_error() -> None
242265
)
243266
client = FakeClient(custom_components_resource=custom_components_resource)
244267

245-
with pytest.raises(UnexpectedAPIError):
246-
await get_latest_custom_component_installation_logs(client, "test-workspace")
268+
result = await get_latest_custom_component_installation_logs(client, "test-workspace")
269+
assert result == "Failed to retrieve latest installation logs: API Error (Status Code: 500)"

0 commit comments

Comments
 (0)