Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,16 @@ jobs:
cache-from: type=registry,ref=ghcr.io/${{ github.repository }}/agentstack-server:cache
cache-to: type=registry,ref=ghcr.io/${{ github.repository }}/agentstack-server:cache,mode=max

- uses: docker/build-push-action@v6
with:
context: ./apps/supergateway
file: ./apps/supergateway/Dockerfile
push: true
platforms: linux/amd64,linux/arm64
tags: ghcr.io/${{ github.repository }}/supergateway:latest
cache-from: type=registry,ref=ghcr.io/${{ github.repository }}/supergateway:cache
cache-to: type=registry,ref=ghcr.io/${{ github.repository }}/supergateway:cache,mode=max

- run: mise run 'agentstack-ui:build:*'
- uses: docker/build-push-action@v6
with:
Expand Down
3 changes: 2 additions & 1 deletion apps/agentstack-server/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
FROM python:3.13-alpine3.22 AS builder
FROM python:3.13-alpine3.22
ENV UV_COMPILE_BYTECODE=1 \
HOME="/tmp" \
AGENT_REGISTRY__LOCATIONS__FILE="file:///app/registry.yaml"
RUN apk add --no-cache kubectl
RUN --mount=type=cache,target=/tmp/.cache/uv \
--mount=type=bind,source=dist/requirements.txt,target=/requirements.txt \
--mount=type=bind,from=ghcr.io/astral-sh/uv:0.9.5,source=/uv,target=/bin/uv \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from agentstack_server.service_layer.services.configurations import ConfigurationService
from agentstack_server.service_layer.services.connector import ConnectorService
from agentstack_server.service_layer.services.contexts import ContextService
from agentstack_server.service_layer.services.external_mcp_service import ExternalMcpService
from agentstack_server.service_layer.services.files import FileService
from agentstack_server.service_layer.services.mcp import McpService
from agentstack_server.service_layer.services.model_providers import ModelProviderService
Expand All @@ -50,6 +51,7 @@
AuthServiceDependency = Annotated[AuthService, Depends(lambda: di[AuthService])]
ModelProviderServiceDependency = Annotated[ModelProviderService, Depends(lambda: di[ModelProviderService])]
ConnectorServiceDependency = Annotated[ConnectorService, Depends(lambda: di[ConnectorService])]
ExternalMcpServiceDependency = Annotated[ExternalMcpService, Depends(lambda: di[ExternalMcpService])]

logger = logging.getLogger(__name__)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from agentstack_server.api.dependencies import (
ConnectorServiceDependency,
ExternalMcpServiceDependency,
RequiresPermissions,
)
from agentstack_server.api.schema.connector import (
Expand Down Expand Up @@ -96,6 +97,7 @@ async def connect_connector(
user=user.user,
redirect_url=connect_request.redirect_url,
callback_uri=str(request.url_for(oauth_callback.__name__)),
access_token=connect_request.access_token,
)
)

Expand Down Expand Up @@ -125,11 +127,16 @@ async def oauth_callback(
request: Request,
state: str,
connector_service: ConnectorServiceDependency,
external_mcp_service: ExternalMcpServiceDependency,
error: str | None = None,
error_description: str | None = None,
):
return await connector_service.oauth_callback(
callback_url=str(request.url), state=state, error=error, error_description=error_description
return await external_mcp_service.oauth_callback(
callback_url=str(request.url),
state=state,
error=error,
error_description=error_description,
probe_fn=lambda connector: connector_service.probe_connector(connector=connector),
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class ConnectorResponse(BaseModel):

class ConnectorConnectRequest(BaseModel):
redirect_url: AnyUrl | None = None
access_token: str | None = None


class ConnectorPresetResponse(BaseModel):
Expand Down
15 changes: 15 additions & 0 deletions apps/agentstack-server/src/agentstack_server/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from agentstack_server.service_layer.build_manager import IProviderBuildManager
from agentstack_server.service_layer.deployment_manager import IProviderDeploymentManager
from agentstack_server.service_layer.unit_of_work import IUnitOfWorkFactory
from agentstack_server.utils.kubectl import Kubectl
from agentstack_server.utils.utils import async_to_sync_isolated

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -89,5 +90,19 @@ def _set_di[T](service: type[T], instance: T):

_set_di(ITextExtractionBackend, DoclingTextExtractionBackend(di[Configuration].text_extraction))

# TODO: unify all services under single k8s client library (kr8s or kubectl wrapper)
_set_di(
Kubectl,
Kubectl(
kubeconfig=di[Configuration].k8s_kubeconfig,
namespace=di[Configuration].k8s_namespace
or (
p.read_text().strip()
if (p := pathlib.Path("/var/run/secrets/kubernetes.io/serviceaccount/namespace")).is_file()
else "default"
),
),
)


bootstrap_dependencies_sync = async_to_sync_isolated(bootstrap_dependencies)
19 changes: 19 additions & 0 deletions apps/agentstack-server/src/agentstack_server/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,11 +219,30 @@ class ManagedProviderConfiguration(BaseModel):
)


class ConnectorStdioPreset(BaseModel):
image: str
command: list[str] | None = None
args: list[str] | None = None
env: dict[str, str] = Field(default_factory=dict)
auth_token_env_name: str | None = None


class ConnectorPreset(BaseModel):
url: AnyUrl
client_id: str | None = None
client_secret: str | None = None
metadata: dict[str, str] | None = None
stdio: ConnectorStdioPreset | None = None

@model_validator(mode="after")
def validate_url_scheme(self):
if self.stdio is None:
if self.url.scheme not in ("http", "https"):
raise ValueError(f"Stdio is not configured, URL scheme must be http(s), got: {self.url.scheme}")
else:
if self.url.scheme != "mcp+stdio":
raise ValueError(f"Stdio is configured, URL scheme must be mcp+stdio, got: {self.url.scheme}")
return self


class ConnectorConfiguration(BaseModel):
Expand Down
Loading