Skip to content

Commit 41ff4c2

Browse files
authored
feat: add resource for integrations (#134)
* feat: create integrations package * feat: add integration models * feat: add integration resource protocol * feat: add integration resource implementation * feat: add integration resource protocol import * feat: add integrations method to AsyncClientProtocol * feat: add integration resource import to client * feat: add integrations method to AsyncDeepsetClient * feat: add integration tools * feat: add integration tool imports * feat: add integration tools to registry * feat: add integration protocol import to test conftest * feat: add integrations method to BaseFakeClient * feat: create integrations unit test package * feat: add integration resource unit tests * feat: add integration tools unit tests * feat: add integration resource integration tests * refactor: use Iterator type annotation instead of List * refactor: improve type annotations and docstrings for IntegrationList * chore: remove tools; fix tests, types * chore: docs
1 parent 78eb1f0 commit 41ff4c2

11 files changed

Lines changed: 440 additions & 1 deletion

File tree

src/deepset_mcp/api/README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ The client automatically handles authentication, request management, and connect
2828

2929
## Core Resources
3030

31-
The SDK provides access to eight main resources, each designed for specific aspects of the deepset platform:
31+
The SDK provides access to nine main resources, each designed for specific aspects of the deepset platform:
3232

3333
### Pipelines
3434

@@ -272,6 +272,29 @@ async with AsyncDeepsetClient() as client:
272272
print(f"Version: {installation.version}")
273273
```
274274

275+
### Integrations
276+
277+
Manage external service integrations:
278+
279+
```python
280+
from deepset_mcp.api.client import AsyncDeepsetClient
281+
from deepset_mcp.api.integrations.models import IntegrationProvider
282+
283+
async with AsyncDeepsetClient() as client:
284+
integrations = client.integrations()
285+
286+
# List all integrations
287+
integration_list = await integrations.list()
288+
289+
for integration in integration_list.integrations:
290+
print(f"Provider: {integration.provider}")
291+
print(f"Domain: {integration.provider_domain}")
292+
293+
# Get a specific integration by provider
294+
openai_integration = await integrations.get(IntegrationProvider.OPENAI)
295+
print(f"OpenAI Integration: {openai_integration.provider_domain}")
296+
```
297+
275298
### Secrets
276299

277300
Manage secrets for secure configuration and sensitive data:
@@ -500,6 +523,7 @@ Main client class for API access.
500523
- `indexes(workspace: str)`: Returns IndexResource for the specified workspace
501524
- `pipeline_templates(workspace: str)`: Returns PipelineTemplateResource for the specified workspace
502525
- `custom_components(workspace: str)`: Returns CustomComponentsResource for the specified workspace
526+
- `integrations()`: Returns IntegrationResource
503527
- `haystack_service()`: Returns HaystackServiceResource
504528
- `secrets()`: Returns SecretResource
505529
- `users()`: Returns UserResource

src/deepset_mcp/api/client.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from deepset_mcp.api.custom_components.resource import CustomComponentsResource
88
from deepset_mcp.api.haystack_service.resource import HaystackServiceResource
99
from deepset_mcp.api.indexes.resource import IndexResource
10+
from deepset_mcp.api.integrations.resource import IntegrationResource
1011
from deepset_mcp.api.pipeline.resource import PipelineResource
1112
from deepset_mcp.api.pipeline_template.resource import PipelineTemplateResource
1213
from deepset_mcp.api.protocols import AsyncClientProtocol
@@ -270,3 +271,7 @@ def secrets(self) -> SecretResource:
270271
def workspaces(self) -> WorkspaceResource:
271272
"""Resource to interact with workspaces."""
272273
return WorkspaceResource(client=self)
274+
275+
def integrations(self) -> IntegrationResource:
276+
"""Resource to interact with integrations."""
277+
return IntegrationResource(client=self)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Integration API resources and models."""
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""Models for the integrations API."""
2+
3+
from enum import StrEnum
4+
from uuid import UUID
5+
6+
from pydantic import BaseModel
7+
8+
9+
class IntegrationProvider(StrEnum):
10+
"""Supported integration providers."""
11+
12+
AWS_BEDROCK = "aws-bedrock"
13+
AZURE_DOCUMENT_INTELLIGENCE = "azure-document-intelligence"
14+
AZURE_OPENAI = "azure-openai"
15+
COHERE = "cohere"
16+
DEEPL = "deepl"
17+
GOOGLE = "google"
18+
HUGGINGFACE = "huggingface"
19+
NVIDIA = "nvidia"
20+
OPENAI = "openai"
21+
SEARCHAPI = "searchapi"
22+
SNOWFLAKE = "snowflake"
23+
UNSTRUCTURED = "unstructured"
24+
VOYAGE_AI = "voyage-ai"
25+
WANDB_AI = "wandb-ai"
26+
MONGODB = "mongodb"
27+
TOGETHER_AI = "together-ai"
28+
29+
30+
class Integration(BaseModel):
31+
"""Model representing an integration."""
32+
33+
invalid: bool
34+
model_registry_token_id: UUID
35+
provider: IntegrationProvider
36+
provider_domain: str
37+
38+
39+
class IntegrationList(BaseModel):
40+
"""Model representing a list of integrations."""
41+
42+
integrations: list[Integration]
43+
44+
def __len__(self) -> int:
45+
"""Return the length of the list.
46+
47+
:returns: Number of integrations.
48+
"""
49+
return len(self.integrations)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"""Protocol definitions for integrations resource."""
2+
3+
from typing import TYPE_CHECKING, Protocol
4+
5+
from deepset_mcp.api.integrations.models import Integration, IntegrationList, IntegrationProvider
6+
7+
if TYPE_CHECKING:
8+
pass
9+
10+
11+
class IntegrationResourceProtocol(Protocol):
12+
"""Protocol for integration resource operations."""
13+
14+
async def list(self) -> IntegrationList:
15+
"""Retrieve all integrations.
16+
17+
:returns: IntegrationList containing all available integrations.
18+
"""
19+
...
20+
21+
async def get(self, provider: IntegrationProvider) -> Integration:
22+
"""Retrieve a specific integration by provider.
23+
24+
:param provider: The integration provider to retrieve.
25+
:returns: Integration instance for the specified provider.
26+
"""
27+
...
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"""Resource implementation for integrations API."""
2+
3+
import logging
4+
from typing import TYPE_CHECKING
5+
6+
from deepset_mcp.api.integrations.models import Integration, IntegrationList, IntegrationProvider
7+
from deepset_mcp.api.integrations.protocols import IntegrationResourceProtocol
8+
from deepset_mcp.api.transport import raise_for_status
9+
10+
logger = logging.getLogger(__name__)
11+
12+
if TYPE_CHECKING:
13+
from deepset_mcp.api.protocols import AsyncClientProtocol
14+
15+
16+
class IntegrationResource(IntegrationResourceProtocol):
17+
"""Manages interactions with the deepset integrations API."""
18+
19+
def __init__(self, client: "AsyncClientProtocol") -> None:
20+
"""Initialize an IntegrationResource instance.
21+
22+
:param client: The async client protocol instance.
23+
"""
24+
self._client = client
25+
26+
async def list(self) -> IntegrationList:
27+
"""Retrieve all integrations.
28+
29+
:returns: IntegrationList containing all available integrations.
30+
"""
31+
resp = await self._client.request(
32+
endpoint="v1/model_registry_tokens",
33+
method="GET",
34+
)
35+
36+
raise_for_status(resp)
37+
38+
if resp.json is not None:
39+
integrations = [Integration.model_validate(item) for item in resp.json]
40+
return IntegrationList(integrations=integrations)
41+
else:
42+
return IntegrationList(integrations=[])
43+
44+
async def get(self, provider: IntegrationProvider) -> Integration:
45+
"""Retrieve a specific integration by provider.
46+
47+
:param provider: The integration provider to retrieve.
48+
:returns: Integration instance for the specified provider.
49+
"""
50+
resp = await self._client.request(
51+
endpoint=f"v1/model_registry_tokens/{provider.value}",
52+
method="GET",
53+
)
54+
55+
raise_for_status(resp)
56+
57+
return Integration.model_validate(resp.json)

src/deepset_mcp/api/protocols.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from deepset_mcp.api.custom_components.protocols import CustomComponentsProtocol
66
from deepset_mcp.api.haystack_service.protocols import HaystackServiceProtocol
77
from deepset_mcp.api.indexes.protocols import IndexResourceProtocol
8+
from deepset_mcp.api.integrations.protocols import IntegrationResourceProtocol
89
from deepset_mcp.api.pipeline.protocols import PipelineResourceProtocol
910
from deepset_mcp.api.pipeline_template.protocols import PipelineTemplateResourceProtocol
1011
from deepset_mcp.api.secrets.protocols import SecretResourceProtocol
@@ -115,3 +116,7 @@ def secrets(self) -> "SecretResourceProtocol":
115116
def workspaces(self) -> "WorkspaceResourceProtocol":
116117
"""Access workspaces."""
117118
...
119+
120+
def integrations(self) -> "IntegrationResourceProtocol":
121+
"""Access integrations."""
122+
...
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
"""Integration tests for IntegrationResource."""
2+
3+
import os
4+
5+
import pytest
6+
7+
from deepset_mcp.api.client import AsyncDeepsetClient
8+
from deepset_mcp.api.integrations.models import IntegrationProvider
9+
10+
11+
@pytest.mark.asyncio
12+
@pytest.mark.integration
13+
class TestIntegrationResourceIntegration:
14+
"""Integration tests for IntegrationResource.
15+
16+
These tests run against the actual deepset API and require:
17+
- DEEPSET_API_KEY environment variable to be set
18+
- Valid API access
19+
"""
20+
21+
@pytest.fixture(autouse=True)
22+
def check_api_key(self) -> None:
23+
"""Ensure API key is available for integration tests."""
24+
if not os.environ.get("DEEPSET_API_KEY"):
25+
pytest.skip("DEEPSET_API_KEY not set, skipping integration tests")
26+
27+
async def test_list_integrations_real_api(self) -> None:
28+
"""Test listing integrations against real API."""
29+
async with AsyncDeepsetClient() as client:
30+
# Act
31+
result = await client.integrations().list()
32+
33+
# Assert
34+
# We can't assert specific content since it depends on the account configuration
35+
# but we can verify the structure is correct
36+
assert hasattr(result, "integrations")
37+
assert isinstance(result.integrations, list)
38+
39+
# If there are integrations, verify their structure
40+
for integration in result.integrations:
41+
assert hasattr(integration, "invalid")
42+
assert hasattr(integration, "model_registry_token_id")
43+
assert hasattr(integration, "provider")
44+
assert hasattr(integration, "provider_domain")
45+
assert isinstance(integration.invalid, bool)
46+
assert isinstance(integration.provider, IntegrationProvider)
47+
assert isinstance(integration.provider_domain, str)
48+
49+
async def test_get_integration_real_api(self) -> None:
50+
"""Test getting a specific integration against real API.
51+
52+
This test attempts to get an AWS Bedrock integration.
53+
If it doesn't exist, the test will expect a 404 error.
54+
"""
55+
async with AsyncDeepsetClient() as client:
56+
try:
57+
# Act
58+
result = await client.integrations().get(IntegrationProvider.AWS_BEDROCK)
59+
60+
# Assert - if we get a result, verify its structure
61+
assert hasattr(result, "invalid")
62+
assert hasattr(result, "model_registry_token_id")
63+
assert hasattr(result, "provider")
64+
assert hasattr(result, "provider_domain")
65+
assert isinstance(result.invalid, bool)
66+
assert result.provider == IntegrationProvider.AWS_BEDROCK
67+
assert isinstance(result.provider_domain, str)
68+
69+
except Exception as e:
70+
# If the integration doesn't exist, we expect a 404-like error
71+
# The exact error type depends on the API implementation
72+
# This is acceptable for integration tests
73+
assert "404" in str(e) or "not found" in str(e).lower() or "Not Found" in str(e)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Unit tests for integrations API."""

0 commit comments

Comments
 (0)