deepset-mcp 0.0.2rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. deepset_mcp/__init__.py +0 -0
  2. deepset_mcp/agents/__init__.py +0 -0
  3. deepset_mcp/agents/debugging/__init__.py +0 -0
  4. deepset_mcp/agents/debugging/debugging_agent.py +37 -0
  5. deepset_mcp/agents/debugging/system_prompt.md +214 -0
  6. deepset_mcp/agents/generalist/__init__.py +0 -0
  7. deepset_mcp/agents/generalist/generalist_agent.py +38 -0
  8. deepset_mcp/agents/generalist/system_prompt.md +241 -0
  9. deepset_mcp/api/README.md +536 -0
  10. deepset_mcp/api/__init__.py +0 -0
  11. deepset_mcp/api/client.py +277 -0
  12. deepset_mcp/api/custom_components/__init__.py +0 -0
  13. deepset_mcp/api/custom_components/models.py +25 -0
  14. deepset_mcp/api/custom_components/protocols.py +17 -0
  15. deepset_mcp/api/custom_components/resource.py +56 -0
  16. deepset_mcp/api/exceptions.py +70 -0
  17. deepset_mcp/api/haystack_service/__init__.py +0 -0
  18. deepset_mcp/api/haystack_service/protocols.py +13 -0
  19. deepset_mcp/api/haystack_service/resource.py +55 -0
  20. deepset_mcp/api/indexes/__init__.py +0 -0
  21. deepset_mcp/api/indexes/models.py +63 -0
  22. deepset_mcp/api/indexes/protocols.py +53 -0
  23. deepset_mcp/api/indexes/resource.py +138 -0
  24. deepset_mcp/api/integrations/__init__.py +1 -0
  25. deepset_mcp/api/integrations/models.py +49 -0
  26. deepset_mcp/api/integrations/protocols.py +27 -0
  27. deepset_mcp/api/integrations/resource.py +57 -0
  28. deepset_mcp/api/pipeline/__init__.py +17 -0
  29. deepset_mcp/api/pipeline/log_level.py +9 -0
  30. deepset_mcp/api/pipeline/models.py +235 -0
  31. deepset_mcp/api/pipeline/protocols.py +83 -0
  32. deepset_mcp/api/pipeline/resource.py +378 -0
  33. deepset_mcp/api/pipeline_template/__init__.py +0 -0
  34. deepset_mcp/api/pipeline_template/models.py +56 -0
  35. deepset_mcp/api/pipeline_template/protocols.py +17 -0
  36. deepset_mcp/api/pipeline_template/resource.py +88 -0
  37. deepset_mcp/api/protocols.py +122 -0
  38. deepset_mcp/api/secrets/__init__.py +0 -0
  39. deepset_mcp/api/secrets/models.py +16 -0
  40. deepset_mcp/api/secrets/protocols.py +29 -0
  41. deepset_mcp/api/secrets/resource.py +112 -0
  42. deepset_mcp/api/shared_models.py +17 -0
  43. deepset_mcp/api/transport.py +336 -0
  44. deepset_mcp/api/user/__init__.py +0 -0
  45. deepset_mcp/api/user/protocols.py +11 -0
  46. deepset_mcp/api/user/resource.py +38 -0
  47. deepset_mcp/api/workspace/__init__.py +7 -0
  48. deepset_mcp/api/workspace/models.py +23 -0
  49. deepset_mcp/api/workspace/protocols.py +41 -0
  50. deepset_mcp/api/workspace/resource.py +94 -0
  51. deepset_mcp/benchmark/README.md +425 -0
  52. deepset_mcp/benchmark/__init__.py +1 -0
  53. deepset_mcp/benchmark/agent_configs/debugging_agent.yml +10 -0
  54. deepset_mcp/benchmark/agent_configs/generalist_agent.yml +6 -0
  55. deepset_mcp/benchmark/dp_validation_error_analysis/__init__.py +0 -0
  56. deepset_mcp/benchmark/dp_validation_error_analysis/eda.ipynb +757 -0
  57. deepset_mcp/benchmark/dp_validation_error_analysis/prepare_interaction_data.ipynb +167 -0
  58. deepset_mcp/benchmark/dp_validation_error_analysis/preprocessing_utils.py +213 -0
  59. deepset_mcp/benchmark/runner/__init__.py +0 -0
  60. deepset_mcp/benchmark/runner/agent_benchmark_runner.py +561 -0
  61. deepset_mcp/benchmark/runner/agent_loader.py +110 -0
  62. deepset_mcp/benchmark/runner/cli.py +39 -0
  63. deepset_mcp/benchmark/runner/cli_agent.py +373 -0
  64. deepset_mcp/benchmark/runner/cli_index.py +71 -0
  65. deepset_mcp/benchmark/runner/cli_pipeline.py +73 -0
  66. deepset_mcp/benchmark/runner/cli_tests.py +226 -0
  67. deepset_mcp/benchmark/runner/cli_utils.py +61 -0
  68. deepset_mcp/benchmark/runner/config.py +73 -0
  69. deepset_mcp/benchmark/runner/config_loader.py +64 -0
  70. deepset_mcp/benchmark/runner/interactive.py +140 -0
  71. deepset_mcp/benchmark/runner/models.py +203 -0
  72. deepset_mcp/benchmark/runner/repl.py +67 -0
  73. deepset_mcp/benchmark/runner/setup_actions.py +238 -0
  74. deepset_mcp/benchmark/runner/streaming.py +360 -0
  75. deepset_mcp/benchmark/runner/teardown_actions.py +196 -0
  76. deepset_mcp/benchmark/runner/tracing.py +21 -0
  77. deepset_mcp/benchmark/tasks/chat_rag_answers_wrong_format.yml +16 -0
  78. deepset_mcp/benchmark/tasks/documents_output_wrong.yml +13 -0
  79. deepset_mcp/benchmark/tasks/jinja_str_instead_of_complex_type.yml +11 -0
  80. deepset_mcp/benchmark/tasks/jinja_syntax_error.yml +11 -0
  81. deepset_mcp/benchmark/tasks/missing_output_mapping.yml +14 -0
  82. deepset_mcp/benchmark/tasks/no_query_input.yml +13 -0
  83. deepset_mcp/benchmark/tasks/pipelines/chat_agent_jinja_str.yml +141 -0
  84. deepset_mcp/benchmark/tasks/pipelines/chat_agent_jinja_syntax.yml +141 -0
  85. deepset_mcp/benchmark/tasks/pipelines/chat_rag_answers_wrong_format.yml +181 -0
  86. deepset_mcp/benchmark/tasks/pipelines/chat_rag_missing_output_mapping.yml +189 -0
  87. deepset_mcp/benchmark/tasks/pipelines/rag_documents_wrong_format.yml +193 -0
  88. deepset_mcp/benchmark/tasks/pipelines/rag_no_query_input.yml +191 -0
  89. deepset_mcp/benchmark/tasks/pipelines/standard_index.yml +167 -0
  90. deepset_mcp/initialize_embedding_model.py +12 -0
  91. deepset_mcp/main.py +133 -0
  92. deepset_mcp/prompts/deepset_copilot_prompt.md +271 -0
  93. deepset_mcp/prompts/deepset_debugging_agent.md +214 -0
  94. deepset_mcp/store.py +5 -0
  95. deepset_mcp/tool_factory.py +473 -0
  96. deepset_mcp/tools/__init__.py +0 -0
  97. deepset_mcp/tools/custom_components.py +52 -0
  98. deepset_mcp/tools/doc_search.py +83 -0
  99. deepset_mcp/tools/haystack_service.py +358 -0
  100. deepset_mcp/tools/haystack_service_models.py +97 -0
  101. deepset_mcp/tools/indexes.py +129 -0
  102. deepset_mcp/tools/model_protocol.py +16 -0
  103. deepset_mcp/tools/pipeline.py +335 -0
  104. deepset_mcp/tools/pipeline_template.py +116 -0
  105. deepset_mcp/tools/secrets.py +45 -0
  106. deepset_mcp/tools/tokonomics/__init__.py +73 -0
  107. deepset_mcp/tools/tokonomics/decorators.py +396 -0
  108. deepset_mcp/tools/tokonomics/explorer.py +347 -0
  109. deepset_mcp/tools/tokonomics/object_store.py +177 -0
  110. deepset_mcp/tools/workspace.py +61 -0
  111. deepset_mcp-0.0.2rc1.dist-info/METADATA +292 -0
  112. deepset_mcp-0.0.2rc1.dist-info/RECORD +114 -0
  113. deepset_mcp-0.0.2rc1.dist-info/WHEEL +4 -0
  114. deepset_mcp-0.0.2rc1.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,277 @@
1
+ import os
2
+ from collections.abc import AsyncIterator
3
+ from contextlib import AbstractAsyncContextManager, asynccontextmanager
4
+ from types import TracebackType
5
+ from typing import Any, Literal, Self, TypeVar, overload
6
+
7
+ from deepset_mcp.api.custom_components.resource import CustomComponentsResource
8
+ from deepset_mcp.api.haystack_service.resource import HaystackServiceResource
9
+ from deepset_mcp.api.indexes.resource import IndexResource
10
+ from deepset_mcp.api.integrations.resource import IntegrationResource
11
+ from deepset_mcp.api.pipeline.resource import PipelineResource
12
+ from deepset_mcp.api.pipeline_template.resource import PipelineTemplateResource
13
+ from deepset_mcp.api.protocols import AsyncClientProtocol
14
+ from deepset_mcp.api.secrets.resource import SecretResource
15
+ from deepset_mcp.api.transport import (
16
+ AsyncTransport,
17
+ StreamingResponse,
18
+ TransportProtocol,
19
+ TransportResponse,
20
+ )
21
+ from deepset_mcp.api.user.resource import UserResource
22
+ from deepset_mcp.api.workspace.resource import WorkspaceResource
23
+
24
+ T = TypeVar("T")
25
+
26
+
27
+ class AsyncDeepsetClient(AsyncClientProtocol):
28
+ """Async Client for interacting with the deepset API."""
29
+
30
+ def __init__(
31
+ self,
32
+ api_key: str | None = None,
33
+ base_url: str = "https://api.cloud.deepset.ai/api",
34
+ transport: TransportProtocol | None = None,
35
+ transport_config: dict[str, Any] | None = None,
36
+ ) -> None:
37
+ """
38
+ Initialize an instance of the AsyncDeepsetClient.
39
+
40
+ Parameters
41
+ ----------
42
+ api_key : str, optional
43
+ API key or token. Falls back to DEEPSET_API_KEY env var.
44
+ base_url : str, optional
45
+ Base URL for the deepset API.
46
+ transport : TransportProtocol, optional
47
+ Custom transport implementation.
48
+ transport_config : dict, optional
49
+ Configuration for default transport (e.g. timeout).
50
+ """
51
+ self.api_key = api_key or os.environ.get("DEEPSET_API_KEY")
52
+ if not self.api_key:
53
+ raise ValueError("API key not provided and DEEPSET_API_KEY environment variable not set")
54
+ self.base_url = base_url
55
+ if transport is not None:
56
+ self._transport = transport
57
+ else:
58
+ self._transport = AsyncTransport(
59
+ base_url=self.base_url,
60
+ api_key=self.api_key,
61
+ config=transport_config,
62
+ )
63
+
64
+ @overload
65
+ async def request(
66
+ self,
67
+ endpoint: str,
68
+ *,
69
+ response_type: type[T],
70
+ method: str = "GET",
71
+ data: dict[str, Any] | None = None,
72
+ headers: dict[str, str] | None = None,
73
+ timeout: float | None | Literal["config"] = "config",
74
+ **kwargs: Any,
75
+ ) -> TransportResponse[T]: ...
76
+
77
+ @overload
78
+ async def request(
79
+ self,
80
+ endpoint: str,
81
+ *,
82
+ response_type: None = None,
83
+ method: str = "GET",
84
+ data: dict[str, Any] | None = None,
85
+ headers: dict[str, str] | None = None,
86
+ timeout: float | None | Literal["config"] = "config",
87
+ **kwargs: Any,
88
+ ) -> TransportResponse[Any]: ...
89
+
90
+ async def request(
91
+ self,
92
+ endpoint: str,
93
+ *,
94
+ method: str = "GET",
95
+ data: dict[str, Any] | None = None,
96
+ headers: dict[str, str] | None = None,
97
+ response_type: type[T] | None = None,
98
+ timeout: float | None | Literal["config"] = "config",
99
+ **kwargs: Any,
100
+ ) -> TransportResponse[Any]:
101
+ """
102
+ Make a regular (non-streaming) request to the deepset API.
103
+
104
+ Parameters
105
+ ----------
106
+ endpoint : str
107
+ API endpoint path
108
+ method : str, default="GET"
109
+ HTTP method
110
+ data : dict, optional
111
+ JSON data to send in request body
112
+ headers : dict, optional
113
+ Additional headers to include
114
+ response_type : type[T], optional
115
+ Expected response type for type checking
116
+ timeout : float | None | Literal["config"], optional
117
+ Request timeout in seconds. If "config", uses transport config timeout.
118
+ If None, disables timeout. If float, uses specific timeout.
119
+ **kwargs : Any
120
+ Additional arguments to pass to transport
121
+
122
+ Returns
123
+ -------
124
+ TransportResponse[T]
125
+ Response with parsed JSON if available
126
+ """
127
+ if not endpoint.startswith("/"):
128
+ endpoint = f"/{endpoint}"
129
+ url = self.base_url + endpoint
130
+
131
+ # Default headers
132
+ request_headers: dict[str, str] = {
133
+ "Authorization": f"Bearer {self.api_key}",
134
+ "Accept": "application/json,text/plain,*/*",
135
+ }
136
+ if data is not None:
137
+ request_headers["Content-Type"] = "application/json"
138
+ # Merge custom headers
139
+ if headers:
140
+ headers.setdefault("Authorization", request_headers["Authorization"])
141
+ request_headers.update(headers)
142
+
143
+ return await self._transport.request(
144
+ method,
145
+ url,
146
+ json=data,
147
+ headers=request_headers,
148
+ response_type=response_type,
149
+ timeout=timeout,
150
+ **kwargs,
151
+ )
152
+
153
+ def stream_request(
154
+ self,
155
+ endpoint: str,
156
+ *,
157
+ method: str = "POST",
158
+ data: dict[str, Any] | None = None,
159
+ headers: dict[str, str] | None = None,
160
+ **kwargs: Any,
161
+ ) -> AbstractAsyncContextManager[StreamingResponse]:
162
+ """
163
+ Make a streaming request to the deepset API.
164
+
165
+ Must be used as an async context manager to ensure proper cleanup.
166
+
167
+ Parameters
168
+ ----------
169
+ endpoint : str
170
+ API endpoint path
171
+ method : str, default="POST"
172
+ HTTP method (usually POST for streaming)
173
+ data : dict, optional
174
+ JSON data to send in request body
175
+ headers : dict, optional
176
+ Additional headers to include
177
+ **kwargs : Any
178
+ Additional arguments to pass to transport
179
+
180
+ Yields
181
+ ------
182
+ StreamingResponse
183
+ Response object with streaming capabilities
184
+
185
+ Examples
186
+ --------
187
+ async with client.stream_request("/pipelines/search-stream", data={"query": "AI"}) as response:
188
+ if response.success:
189
+ async for line in response.iter_lines():
190
+ # Process each line of the stream
191
+ data = json.loads(line)
192
+ print(data)
193
+ else:
194
+ # Handle error
195
+ error_body = await response.read_body()
196
+ print(f"Error {response.status_code}: {error_body}")
197
+ """
198
+
199
+ @asynccontextmanager
200
+ async def _stream() -> AsyncIterator[StreamingResponse]:
201
+ if not endpoint.startswith("/"):
202
+ full_endpoint = f"/{endpoint}"
203
+ url = self.base_url + full_endpoint
204
+
205
+ # Default headers for streaming
206
+ request_headers: dict[str, str] = {
207
+ "Authorization": f"Bearer {self.api_key}",
208
+ "Accept": "text/event-stream,application/json,text/plain,*/*",
209
+ }
210
+ if data is not None:
211
+ request_headers["Content-Type"] = "application/json"
212
+ # Merge custom headers
213
+ if headers:
214
+ headers.setdefault("Authorization", request_headers["Authorization"])
215
+ request_headers.update(headers)
216
+
217
+ async with self._transport.stream(
218
+ method,
219
+ url,
220
+ json=data,
221
+ headers=request_headers,
222
+ **kwargs,
223
+ ) as response:
224
+ yield response
225
+
226
+ return _stream()
227
+
228
+ async def close(self) -> None:
229
+ """Close underlying transport resources."""
230
+ await self._transport.close()
231
+
232
+ async def __aenter__(self) -> Self:
233
+ """Enter the AsyncContextManager."""
234
+ return self
235
+
236
+ async def __aexit__(
237
+ self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
238
+ ) -> bool:
239
+ """Exit the AsyncContextmanager and clean up resources."""
240
+ await self.close()
241
+ return False
242
+
243
+ def pipelines(self, workspace: str) -> PipelineResource:
244
+ """Resource to interact with pipelines in the specified workspace."""
245
+ return PipelineResource(client=self, workspace=workspace)
246
+
247
+ def haystack_service(self) -> HaystackServiceResource:
248
+ """Resource to interact with the Haystack service API."""
249
+ return HaystackServiceResource(client=self)
250
+
251
+ def pipeline_templates(self, workspace: str) -> PipelineTemplateResource:
252
+ """Resource to interact with pipeline templates in the specified workspace."""
253
+ return PipelineTemplateResource(client=self, workspace=workspace)
254
+
255
+ def indexes(self, workspace: str) -> IndexResource:
256
+ """Resource to interact with indexes in the specified workspace."""
257
+ return IndexResource(client=self, workspace=workspace)
258
+
259
+ def custom_components(self, workspace: str) -> CustomComponentsResource:
260
+ """Resource to interact with custom components in the specified workspace."""
261
+ return CustomComponentsResource(client=self)
262
+
263
+ def users(self) -> UserResource:
264
+ """Resource to interact with users."""
265
+ return UserResource(client=self)
266
+
267
+ def secrets(self) -> SecretResource:
268
+ """Resource to interact with secrets."""
269
+ return SecretResource(client=self)
270
+
271
+ def workspaces(self) -> WorkspaceResource:
272
+ """Resource to interact with workspaces."""
273
+ return WorkspaceResource(client=self)
274
+
275
+ def integrations(self) -> IntegrationResource:
276
+ """Resource to interact with integrations."""
277
+ return IntegrationResource(client=self)
File without changes
@@ -0,0 +1,25 @@
1
+ from typing import Any
2
+
3
+ from pydantic import BaseModel
4
+
5
+ from deepset_mcp.api.shared_models import DeepsetUser
6
+
7
+
8
+ class CustomComponentInstallation(BaseModel):
9
+ """Model representing a custom component installation."""
10
+
11
+ custom_component_id: str
12
+ status: str
13
+ version: str
14
+ created_by_user_id: str
15
+ logs: list[dict[str, Any]]
16
+ organization_id: str
17
+ user_info: DeepsetUser | None = None
18
+
19
+
20
+ class CustomComponentInstallationList(BaseModel):
21
+ """Model representing a list of custom component installations."""
22
+
23
+ data: list[CustomComponentInstallation]
24
+ total: int
25
+ has_more: bool
@@ -0,0 +1,17 @@
1
+ from typing import Protocol
2
+
3
+ from deepset_mcp.api.custom_components.models import CustomComponentInstallationList
4
+
5
+
6
+ class CustomComponentsProtocol(Protocol):
7
+ """Protocol defining the implementation for CustomComponentsResource."""
8
+
9
+ async def list_installations(
10
+ self, limit: int = 20, page_number: int = 1, field: str = "created_at", order: str = "DESC"
11
+ ) -> CustomComponentInstallationList:
12
+ """List custom component installations."""
13
+ ...
14
+
15
+ async def get_latest_installation_logs(self) -> str | None:
16
+ """Get the logs from the latest custom component installation."""
17
+ ...
@@ -0,0 +1,56 @@
1
+ from typing import Any
2
+
3
+ from deepset_mcp.api.custom_components.models import CustomComponentInstallationList
4
+ from deepset_mcp.api.custom_components.protocols import CustomComponentsProtocol
5
+ from deepset_mcp.api.protocols import AsyncClientProtocol
6
+ from deepset_mcp.api.transport import raise_for_status
7
+
8
+
9
+ class CustomComponentsResource(CustomComponentsProtocol):
10
+ """Resource for managing custom components in deepset."""
11
+
12
+ def __init__(self, client: AsyncClientProtocol) -> None:
13
+ """Initialize a CustomComponentsResource.
14
+
15
+ :param client: The API client to use for requests.
16
+ """
17
+ self._client = client
18
+
19
+ async def list_installations(
20
+ self, limit: int = 20, page_number: int = 1, field: str = "created_at", order: str = "DESC"
21
+ ) -> CustomComponentInstallationList:
22
+ """List custom component installations.
23
+
24
+ :param limit: Maximum number of installations to return.
25
+ :param page_number: Page number for pagination.
26
+ :param field: Field to sort by.
27
+ :param order: Sort order (ASC or DESC).
28
+
29
+ :returns: List of custom component installations.
30
+ """
31
+ resp = await self._client.request(
32
+ endpoint=f"v2/custom_components?limit={limit}&page_number={page_number}&field={field}&order={order}",
33
+ method="GET",
34
+ response_type=dict[str, Any],
35
+ )
36
+
37
+ raise_for_status(resp)
38
+
39
+ if resp.json is None:
40
+ return CustomComponentInstallationList(data=[], total=0, has_more=False)
41
+
42
+ return CustomComponentInstallationList(**resp.json)
43
+
44
+ async def get_latest_installation_logs(self) -> str | None:
45
+ """Get the logs from the latest custom component installation.
46
+
47
+ :returns: Latest installation logs.
48
+ """
49
+ resp = await self._client.request(
50
+ endpoint="v2/custom_components/logs",
51
+ method="GET",
52
+ )
53
+
54
+ raise_for_status(resp)
55
+
56
+ return resp.text
@@ -0,0 +1,70 @@
1
+ from typing import Any
2
+
3
+
4
+ class DeepsetAPIError(Exception):
5
+ """Base exception for all deepset API errors."""
6
+
7
+ def __init__(self, status_code: int | None = None, message: Any | None = None, detail: Any | None = None) -> None:
8
+ """Initialize the exception."""
9
+ self.status_code = status_code
10
+ self.message = message
11
+ self.detail = detail
12
+ super().__init__(self.message)
13
+
14
+ def __str__(self) -> str:
15
+ """Return a string representation of the exception."""
16
+ return f"{self.message} (Status Code: {self.status_code})"
17
+
18
+
19
+ class ResourceNotFoundError(DeepsetAPIError):
20
+ """Exception raised when a resource is not found (HTTP 404)."""
21
+
22
+ def __init__(self, message: Any = "Resource not found", detail: Any | None = None) -> None:
23
+ """Initialize the exception."""
24
+ super().__init__(status_code=404, message=message, detail=detail)
25
+
26
+
27
+ class BadRequestError(DeepsetAPIError):
28
+ """Exception raised for invalid requests (HTTP 400)."""
29
+
30
+ def __init__(self, message: Any = "Bad request", detail: Any | None = None) -> None:
31
+ """Initialize the exception."""
32
+ super().__init__(status_code=400, message=message, detail=detail)
33
+
34
+
35
+ class RequestTimeoutError(Exception):
36
+ """Exception raised when a request times out."""
37
+
38
+ def __init__(
39
+ self,
40
+ method: str,
41
+ url: str,
42
+ timeout: float | None | str,
43
+ duration: float | None = None,
44
+ detail: str | None = None,
45
+ ):
46
+ """Initialize the timeout exception with request context."""
47
+ self.method = method
48
+ self.url = url
49
+ self.timeout = timeout
50
+ self.duration = duration
51
+ self.detail = detail
52
+
53
+ timeout_display = f"{timeout}s" if isinstance(timeout, int | float) else str(timeout)
54
+
55
+ if duration is not None:
56
+ message = f"Request timed out after {duration:.2f}s (limit: {timeout_display}): {method} {url}"
57
+ else:
58
+ message = f"Request timed out (limit: {timeout_display}): {method} {url}"
59
+
60
+ super().__init__(message)
61
+
62
+
63
+ class UnexpectedAPIError(DeepsetAPIError):
64
+ """Catch-all exception for unexpected API errors."""
65
+
66
+ def __init__(
67
+ self, status_code: int | None = None, message: Any = "Unexpected API error", detail: Any | None = None
68
+ ):
69
+ """Initialize the exception."""
70
+ super().__init__(status_code=status_code, message=message, detail=detail)
File without changes
@@ -0,0 +1,13 @@
1
+ from typing import Any, Protocol
2
+
3
+
4
+ class HaystackServiceProtocol(Protocol):
5
+ """Protocol defining the implementation for HaystackService."""
6
+
7
+ async def get_component_schemas(self) -> dict[str, Any]:
8
+ """Fetch the component schema from the API."""
9
+ ...
10
+
11
+ async def get_component_input_output(self, component_name: str) -> dict[str, Any]:
12
+ """Fetch input and output schema for a component from the API."""
13
+ ...
@@ -0,0 +1,55 @@
1
+ from typing import Any
2
+
3
+ from deepset_mcp.api.exceptions import ResourceNotFoundError
4
+ from deepset_mcp.api.haystack_service.protocols import HaystackServiceProtocol
5
+ from deepset_mcp.api.protocols import AsyncClientProtocol
6
+ from deepset_mcp.api.transport import raise_for_status
7
+
8
+
9
+ class HaystackServiceResource(HaystackServiceProtocol):
10
+ """Manages interactions with the deepset Haystack service API."""
11
+
12
+ def __init__(self, client: AsyncClientProtocol) -> None:
13
+ """Initializes a HaystackServiceResource instance."""
14
+ self._client = client
15
+
16
+ async def get_component_schemas(self) -> dict[str, Any]:
17
+ """Fetch the component schema from the API.
18
+
19
+ Returns:
20
+ The component schema as a dictionary
21
+ """
22
+ resp = await self._client.request(
23
+ endpoint="v1/haystack/components",
24
+ method="GET",
25
+ headers={"accept": "application/json"},
26
+ data={"domain": "deepset-cloud"},
27
+ )
28
+
29
+ raise_for_status(resp)
30
+
31
+ return resp.json if resp.json is not None else {}
32
+
33
+ async def get_component_input_output(self, component_name: str) -> dict[str, Any]:
34
+ """Fetch the component input and output schema from the API.
35
+
36
+ Args:
37
+ component_name: The name of the component to fetch the input/output schema for
38
+
39
+ Returns:
40
+ The component input/output schema as a dictionary
41
+ """
42
+ resp = await self._client.request(
43
+ endpoint="v1/haystack/components/input-output",
44
+ method="GET",
45
+ headers={"accept": "application/json"},
46
+ params={"domain": "deepset-cloud", "names": component_name},
47
+ response_type=list[dict[str, Any]],
48
+ )
49
+
50
+ raise_for_status(resp)
51
+
52
+ if resp.json is None or len(resp.json) == 0:
53
+ raise ResourceNotFoundError(f"Component '{component_name}' not found.")
54
+
55
+ return resp.json[0] if resp.json is not None else {}
File without changes
@@ -0,0 +1,63 @@
1
+ from datetime import datetime
2
+ from typing import Any
3
+
4
+ from pydantic import BaseModel
5
+ from rich.repr import Result
6
+
7
+ from deepset_mcp.api.shared_models import DeepsetUser
8
+
9
+
10
+ class IndexStatus(BaseModel):
11
+ """Status information about documents in an index."""
12
+
13
+ pending_file_count: int
14
+ failed_file_count: int
15
+ indexed_no_documents_file_count: int
16
+ indexed_file_count: int
17
+ total_file_count: int
18
+
19
+
20
+ class Index(BaseModel):
21
+ """A deepset index."""
22
+
23
+ pipeline_index_id: str
24
+ name: str
25
+ description: str | None = None
26
+ config_yaml: str
27
+ workspace_id: str
28
+ settings: dict[str, Any]
29
+ desired_status: str
30
+ deployed_at: datetime | None = None
31
+ last_edited_at: datetime | None = None
32
+ max_index_replica_count: int
33
+ created_at: datetime
34
+ updated_at: datetime | None = None
35
+ created_by: DeepsetUser
36
+ last_edited_by: DeepsetUser | None = None
37
+ status: IndexStatus
38
+
39
+ def __rich_repr__(self) -> Result:
40
+ """Used to display the model in an LLM friendly way."""
41
+ yield "name", self.name
42
+ yield "description", self.description, None
43
+ yield "desired_status", self.desired_status
44
+ yield "status", self.status
45
+ yield "status", self.status
46
+ yield "created_by", f"{self.created_by.given_name} {self.created_by.family_name} ({self.created_by.id})"
47
+ yield "created_at", self.created_at.strftime("%m/%d/%Y %I:%M:%S %p")
48
+ yield (
49
+ "last_edited_by",
50
+ f"{self.last_edited_by.given_name} {self.last_edited_by.family_name} ({self.last_edited_by.id})"
51
+ if self.last_edited_by
52
+ else None,
53
+ )
54
+ yield "last_edited_at", self.last_edited_at.strftime("%m/%d/%Y %I:%M:%S %p") if self.last_edited_at else None
55
+ yield "config_yaml", self.config_yaml if self.config_yaml is not None else "Get full index to see config."
56
+
57
+
58
+ class IndexList(BaseModel):
59
+ """Response model for listing indexes."""
60
+
61
+ data: list[Index]
62
+ has_more: bool
63
+ total: int
@@ -0,0 +1,53 @@
1
+ from typing import Protocol
2
+
3
+ from deepset_mcp.api.indexes.models import Index, IndexList
4
+ from deepset_mcp.api.pipeline.models import PipelineValidationResult
5
+
6
+
7
+ class IndexResourceProtocol(Protocol):
8
+ """Protocol defining the implementation for IndexResource."""
9
+
10
+ async def list(self, limit: int = 10, page_number: int = 1) -> IndexList:
11
+ """List indexes in the configured workspace."""
12
+ ...
13
+
14
+ async def get(self, index_name: str) -> Index:
15
+ """Fetch a single index by its name."""
16
+ ...
17
+
18
+ async def create(self, name: str, yaml_config: str, description: str | None = None) -> Index:
19
+ """Create a new index with the given name and configuration.
20
+
21
+ :param name: Name of the index
22
+ :param yaml_config: YAML configuration for the index
23
+ :param description: Optional description for the index
24
+ :returns: Created index details
25
+ """
26
+ ...
27
+
28
+ async def update(
29
+ self, index_name: str, updated_index_name: str | None = None, yaml_config: str | None = None
30
+ ) -> Index:
31
+ """Update name and/or configuration of an existing index.
32
+
33
+ :param index_name: Name of the index to update
34
+ :param updated_index_name: Optional new name for the index
35
+ :param yaml_config: Optional new YAML configuration
36
+ :returns: Updated index details
37
+ """
38
+ ...
39
+
40
+ async def deploy(self, index_name: str) -> PipelineValidationResult:
41
+ """Deploy an index to production.
42
+
43
+ :param index_name: Name of the index to deploy.
44
+ :returns: PipelineValidationResult containing deployment status and any errors.
45
+ """
46
+ ...
47
+
48
+ async def delete(self, index_name: str) -> None:
49
+ """Delete an index.
50
+
51
+ :param index_name: Name of the index to delete.
52
+ """
53
+ ...