Skip to content

The index can only discover toolsets through the Kubernetes API #115

Description

@ciaransweet

The index can only discover toolsets through the Kubernetes API. Every path into mcp_index — the token it authenticates with, the host it queries, the shape it parses — assumes a pod in a cluster, so an identical deployment anywhere else serves an empty directory or nothing at all.

Where this came from

mcp-toolsets has requests to deploy on AWS without EKS: ECS on Fargate, with the infrastructure managed by CDK. The template would offer the choice at bootstrap, keep whichever target the user picks, and behave the same either way. Everything else in that port is a translation — a Deployment becomes a Fargate service, an Ingress rule becomes a listener rule, cert-manager becomes ACM. The index is the one piece with no translation available, because the discovery is not configuration, it is the module.

SERVICE_ACCOUNT_DIR = Path("/var/run/secrets/kubernetes.io/serviceaccount")
TOOLSET_LABEL = "mcp-toolsets/toolset"


async def fetch_toolset_services(client: httpx.AsyncClient) -> list[ToolsetService]:
    """List toolset Services in this pod's namespace via the Kubernetes API."""
    namespace = (SERVICE_ACCOUNT_DIR / "namespace").read_text().strip()
    token = (SERVICE_ACCOUNT_DIR / "token").read_text().strip()
    response = await client.get(
        f"https://kubernetes.default.svc/api/v1/namespaces/{namespace}/services",
        ...
    )

Off-cluster, the token file is absent and the read raises before any request is made. There is no fallback and no way to supply the answer from outside.

What discovery is actually for

Worth naming, because it decides the fix. The index does not just need a list of URLs — it needs a list of what is running, which is why it queries the platform rather than reading a manifest. A static list injected at deploy time would drift the moment a service failed to start, and the directory would report a toolset that isn't there. Whatever replaces this has to keep asking the platform.

Proposed

Split the lookup behind a setting, with today's behaviour as the default:

MCP_INDEX_DISCOVERY=kubernetes   # default, unchanged
MCP_INDEX_DISCOVERY=ecs
MCP_ECS_CLUSTER=<cluster>        # ecs only

The ECS backend lists services on the named cluster, keeps the ones tagged mcp-toolsets/toolset (the same selector, a different mechanism), and builds each internal base URL from the service's Cloud Map registration. It needs ecs:ListServices, ecs:DescribeServices, servicediscovery:ListServices and servicediscovery:DiscoverInstances on the task role.

boto3 goes behind an optional extra — mcp-toolsets-runtime[aws] — imported only when that backend is selected, alongside the extras already published for agent, web, state and checkpointing-postgres. A cluster deployment installs nothing new. Selecting ecs without the extra should fail at startup with a message naming it, not at the first request.

toolset_services() is already a pure function over a payload, so the parsing half is testable per backend without either platform.

Not in scope

The public URL shape ({public_url}/{toolset}/mcp) stays exactly as it is. Making a load balancer able to serve that shape is a separate change — see the path prefix issue.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions