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
24 changes: 17 additions & 7 deletions src/dedalus_mcp/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ async def dispatch(
raise RuntimeError("Dispatch backend not configured")

# Resolve connection target to handle
connections = self._get_connection_handles(runtime)
connections = self._get_connections(runtime)

if connection_target is None:
# Single-connection server: use the only connection
Expand Down Expand Up @@ -401,13 +401,23 @@ def _build_resolver_context(self, operation: Mapping[str, Any] | None) -> dict[s
payload["operation"] = dict(operation)
return payload

def _get_connection_handles(self, runtime: Mapping[str, Any]) -> dict[str, str]:
"""Get connection name to handle mapping from runtime config.
def _get_connections(self, runtime: Mapping[str, Any]) -> dict[str, str]:
"""Get connection mapping from JWT claims (ddls:connections)."""
auth_context = self.auth_context
if auth_context is None:
msg = """DEDALUS_DISPATCH_URL not found.
Dispatch is only available through the Dedalus-hosted MCP servers."""
raise RuntimeError(msg)

Note: Authorization is handled by the gateway at runtime via Admin API.
The runtime config provides the name->handle mapping for dispatch routing.
"""
return dict(runtime.get("connection_handles", {}))
claims = getattr(auth_context, "claims", None)
if not isinstance(claims, dict):
raise RuntimeError("Invalid authorization claims")

connections = claims.get("ddls:connections")
if not isinstance(connections, dict):
raise RuntimeError("Missing ddls:connections claim")

return dict(connections)


def _activate_request_context() -> Token[Context | None]:
Expand Down
69 changes: 6 additions & 63 deletions src/dedalus_mcp/server/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,8 @@ def __init__(
raise ValueError(f"Duplicate connection name: '{conn.name}'")
self._connections[conn.name] = conn

# Dispatch backend and connection handles (initialized in _build_runtime_payload)
# Dispatch backend (initialized in _build_runtime_payload)
self._dispatch_backend: Any = None
self._connection_handles: dict[str, str] = {}
super().__init__(
name, version=version, instructions=instructions, website_url=website_url, icons=icons, lifespan=lifespan
)
Expand Down Expand Up @@ -867,73 +866,17 @@ def _build_runtime_payload(self) -> dict[str, Any]:
"server": self,
"resolver": self._connection_resolver,
"dispatch_backend": self._dispatch_backend,
"connection_handles": self._connection_handles,
}

def _initialize_dispatch_backend(self) -> None:
"""Initialize dispatch backend from environment and connections.
"""Initialize dispatch backend from environment.

Creates DirectDispatchBackend (OSS mode) or EnclaveDispatchBackend (production)
based on DEDALUS_DISPATCH_URL environment variable.
Requires DEDALUS_DISPATCH_URL to be set. Dispatch is only available
through Dedalus-hosted MCP servers.
"""
import os
from ..dispatch import create_dispatch_backend_from_env

from ..dispatch import DirectDispatchBackend, create_dispatch_backend_from_env

# Build connection handles map: {name: "ddls:conn:{name}"}
self._connection_handles = {name: f'ddls:conn:{name}' for name in self._connections}

# Get backend from environment (checks DEDALUS_DISPATCH_URL)
backend = create_dispatch_backend_from_env()

# For DirectDispatchBackend, configure credential resolver
if isinstance(backend, DirectDispatchBackend):

def credential_resolver(handle: str) -> tuple[str, str, str]:
"""Resolve connection handle to (base_url, header_name, header_value)."""
# Extract connection name from handle (ddls:conn:{name})
if not handle.startswith('ddls:conn:'):
raise ValueError(f'Invalid handle format: {handle}')
conn_name = handle[len('ddls:conn:') :]

conn = self._connections.get(conn_name)
if conn is None:
raise ValueError(f"Connection '{conn_name}' not found")

# Read credentials from environment using Binding entries
creds: dict[str, str] = {}
for field_name, binding in conn.secrets.entries.items():
raw = os.getenv(binding.name)
if raw is None or raw == '':
if binding.optional:
continue
raise RuntimeError(f'Environment variable {binding.name} is not set')
creds[field_name] = raw

# Extract the credential value (api_key for format string)
api_key = ''
for key, value in creds.items():
if key in ('token', 'access_token', 'key', 'apikey', 'api_key', 'service_role_key'):
api_key = value
break
else:
# Fallback: use first credential value
api_key = value
break

# Format header value using connection's auth_header_format
header_value = conn.auth_header_format.format(api_key=api_key)

return (conn.base_url or '', conn.auth_header_name, header_value)

# Replace backend with one that has credential resolver
backend = DirectDispatchBackend(credential_resolver=credential_resolver)

self._dispatch_backend = backend
self._logger.debug(
'dispatch backend initialized',
extra={'event': 'dispatch.init', 'backend_type': type(backend).__name__},
)
self._dispatch_backend = create_dispatch_backend_from_env()

# TODO: Quality check on this impl.
async def _handle_request(
Expand Down
Loading