Skip to main content

Dynamic MCP Server Registration

Report an Issue

Did you know? Dynamic MCP server registration is part of a broader Agentic Identity Framework.

The Agentic Identity Framework is a standards-driven set of designs, SDKs, and reference implementations for deploying AI agents securely.

Explore framework

Dynamic MCP server registration allows Teleport administrators to register new MCP servers (or update/unregister existing ones) without having to update the static configuration files read by Teleport Application Service instances.

The MCP server resources are registered as app resources in the Teleport backend. Application Service instances periodically query the Teleport Auth Service for app resources, each of which includes the information that the Application Service needs to proxy an application.

This page describes the operations involved in managing MCP server registration dynamically.

Required permissions​

In order to interact with dynamically registered applications, a user must have a Teleport role with permissions to manage app resources.

In the following example, a role allows a user to perform all possible operations against app resources:

allow:
  rules:
    - resources:
        - app
      verbs: [list, create, read, update, delete]

Enabling dynamic registration​

To enable dynamic registration, include a resources section in your Application Service configuration with a list of resource label selectors you'd like this service to monitor for registering:

app_service:
  enabled: true
  resources:
  - labels:
      "*": "*"

You can use a wildcard selector to register all dynamic app resources in the cluster on the Application Service or provide a specific set of labels for a subset:

resources:
- labels:
    "env": "prod"
- labels:
    "env": "test"

Creating a MCP server​

The following example configures Teleport to proxy the "Everything" MCP server by launching it through Docker. This example requires Docker to be installed on the Application Service host.

kind: app
version: v3
metadata:
  name: everything
  description: The Everything MCP server
  labels:
    env: dev
spec:
  mcp:
    # Command to launch stdio-based MCP servers.
    command: "docker"
    # Args to execute with the command.
    args: ["run", "-i", "--rm", "mcp/everything"]
    # Name of the host user account under which the command will be
    # executed. Required for stdio-based MCP servers.
    run_as_host_user: "teleport-mcp"

The run_as_host_user field is required for stdio-based MCP servers. The specified user must exist on the Application Service host before you create the resource. If the host user does not exist, the Application Service logs an error and the MCP server fails to start.

Create a dedicated system user for running MCP servers:

sudo useradd -r -s /bin/bash teleport-mcp
Treat this permission as host access

Anyone who can create or update MCP server app resources can run arbitrary commands on the Application Service host, with up to the privileges of the account running the Teleport process. Read Security considerations before you enable dynamic registration.

See the full resource spec reference.

To create the resource, run:

Log in to your cluster with tsh so you can use tctl from your local machine.

You can also run tctl on your Auth Service host without running "tsh login"

first.

tsh login --proxy=teleport.example.com --user=myuser
tctl create mcp_server.yaml

After the resource has been created, it will appear among the list of available MCP servers (in tsh mcp ls or UI) as long as at least one Application Service instance picks it up according to its label selectors.

To update an existing application resource, run:

tctl create -f mcp_server.yaml

If the updated resource's labels no longer match a particular app agent, it will unregister and stop proxying it.

To delete the resource, run:

tctl rm app/everything

Security considerations​

Creating app resources grants command execution on the host​

Anyone who can create or update an app resource can execute arbitrary commands on the Application Service hosts that proxy it. For MCP servers, Teleport runs the command and args fields as the account set in run_as_host_user. This risk isn't new with MCP servers. Dynamic labels on app and db resources also run commands on the host that proxies the resource.

These commands run with up to the same privileges as the account running the Teleport process. If Teleport runs as root, a resource editor can set run_as_host_user to any account on the host, including root. If Teleport runs as an unprivileged account that can't switch users, commands can't gain more privileges than that account has.

Grant permission to create or update app resources only to users you would trust with direct access to the Application Service hosts that could run them.

Limiting the impact​

The most effective way to limit what a resource editor can reach is to limit what the Application Service host can reach. Run the Application Service in a dedicated, isolated environment, such as its own virtual machine or Kubernetes pod. Don't run it on the same host as the Auth Service or Proxy Service.

For further isolation, run the Teleport process as an unprivileged account instead of root. This caps the privileges available to any command an app resource launches.

Using a dedicated, low-privilege account for run_as_host_user, as this guide does above, keeps MCP server processes separate from other workloads on the host. It doesn't stop a resource editor from specifying a more privileged account when Teleport runs as root.

Docker group membership​

Membership in the host's docker group grants effective root access, because it allows mounting the host filesystem into a container. Whether this changes your exposure depends on how you run Teleport.

If Teleport runs as root, which is common for systemd deployments, a resource editor can already run commands as root or as a docker group member, so adding the run_as_host_user account to the docker group doesn't expand access.

If Teleport runs as an unprivileged account with no access to the docker group, adding the run_as_host_user account to that group gives resource editors a path to root. In that setup, consider configuring Rootless Docker for the account or using a pre-approved wrapper script instead of exposing raw Docker execution.