Flyte MCP server
FlyteMCPAppEnvironment exposes Union.ai operations as standardized
MCP tools, so AI assistants and LLM clients can drive your cluster programmatically: running tasks, monitoring runs, reading logs, inspecting actions, managing apps and triggers, and searching the SDK and docs.
Unlike
MCPAppEnvironment, where you supply your own tools, this environment ships a curated set of Flyte tools out of the box. You decide which of them to expose and what they’re allowed to touch.
flyte-agent-plugins ships these same
control-plane tools as a flyte-cluster MCP server, plus a hosted flyte-docs search
server. Claude Code and Codex wire up both for you; Hermes, opencode, and pi take a few
lines of config. See
Flyte agent plugins.
Build your own FlyteMCPAppEnvironment below when you need to scope the tools,
allowlist resources, or deploy a shared server.
When to use it
Use a Flyte MCP server when you want an assistant to act on your cluster on your behalf. Common scenarios:
- Agentic development loops: Let Claude Code or OpenCode run a task, wait for it, read its outputs, and iterate, without you copy-pasting commands.
- Conversational operations: Ask an assistant to list recent runs, check a run’s status, or abort a stuck run.
- Docs- and example-aware coding: Enable the
searchtools so an assistant can ground its answers in the Flyte SDK examples and Union documentation. - Self-service automation: Give a trusted internal agent a tightly-scoped server (a few allowlisted tasks, no destructive tools) to perform a narrow job.
If you only need to expose custom, non-Flyte tools, use
MCPAppEnvironment instead.
How to run it
There are two ways to run a Flyte MCP server, suited to different stages:
| Mode | When to use |
|---|---|
| Local (stdio) | One user on one machine. The client launches the server as a subprocess, using your local Flyte config and your existing login. Nothing is deployed, and no data leaves your machine. |
| Remote (HTTP) | A whole team, or a client that cannot launch a subprocess (a browser-based assistant, for example). The server runs as a deployed app with a stable, authenticated URL. |
Prefer stdio unless you need one of the two things only HTTP gives you: a single shared server that nobody has to install, or a URL for a client that has no local process.
Running locally with uvx
The flyte[mcp] extra ships a flyte-mcp CLI entrypoint. Run it with
uvx (no global install required):
uvx --from "flyte[mcp]>=2.5.18" flyte-mcp --transport stdiouvx downloads flyte[mcp] into an isolated environment, runs flyte-mcp, and exits cleanly when you’re done. The server reads your active Flyte config (the same one used by the flyte CLI or flyte.init_from_config()), so whichever project and cluster you’re pointed at is what the tools operate on.
Two parts of that command matter:
--transport stdiois required. The CLI defaults tostreamable-http, which starts an HTTP listener instead of speaking JSON-RPC on stdin and stdout. A client that launches the process expecting stdio will not connect without this flag.>=2.5.18is the minimum version. It is the first release that constrains itsmcpdependency below 2.0. Earlier versions resolvemcp2.0.0, which removed the module the server imports, and the server exits at startup reportingmcp is not installed.
Pin to an exact version instead of a floor: uvx --from "flyte[mcp]==2.5.18" flyte-mcp --transport stdio
The server starts even when no Flyte config is present. In that case the tools fail when the assistant calls them, rather than the server failing to start.
The three search_* tools grep a local copy of the Flyte SDK examples, the docs examples, and llms.txt. Enabling them makes the CLI clone roughly 120 MB into ~/.flyte/mcp on first launch. Pass --tool-groups without search to skip it:
uvx --from "flyte[mcp]>=2.5.18" flyte-mcp --transport stdio \
--tool-groups task,run,action,logs,app,trigger,project,secret,condition,identityOnce running, register it with your client as a stdio transport: the client manages the process lifetime. See the connecting a client section below.
Deploying remotely
Deploy a FlyteMCPAppEnvironment as a long-running app to get a stable, shared HTTP endpoint:
flyte.init_from_config()
handle = flyte.serve(mcp_env)
handle.activate(wait=True)
print(f"MCP endpoint: {handle.endpoint}/flyte-mcp/mcp")The app is deployed into your own tenant, so the server sits in the same trust domain as the data it reads: run metadata, logs, and task inputs and outputs stay inside your account. Set requires_auth=True and the endpoint sits behind your organization’s SSO, so each call runs as the person driving the agent, with their own permissions.
See Serve and deploy apps for how deployment, activation, and scaling work in general.
Basic example
A server with all tools enabled. The environment definition is the same for both modes — only the last step differs, since flyte.serve deploys it as an app:
# Deploy an MCP server with all tools enabled
mcp_env = FlyteMCPAppEnvironment(
name="flyte-mcp-server",
resources=flyte.Resources(cpu=1, memory="512Mi"),
transport="streamable-http",
instructions=(
"This MCP server provides tools to interact with the Flyte control plane. "
"Use the available tools to run tasks, monitor runs, manage apps, build images, "
"build and run UV scripts remotely, and search SDK/docs examples."
),
)
if __name__ == "__main__":
flyte.init_from_config()
app_handle = flyte.serve(mcp_env)
app_handle.activate(wait=True)
print(f"App is ready at {app_handle.endpoint}")
The default mount path is /flyte-mcp, so with the default streamable-http transport the MCP endpoint is /flyte-mcp/mcp.
instructionsThe instructions string is sent to the LLM as guidance on what the server is for and how to use its tools. A clear, specific instruction string measurably improves how reliably the assistant picks the right tool.
Scoping the server
A server with every tool enabled and no restrictions is convenient for trusted local use, but for anything shared you should narrow it down. There are three layers of control, from coarse to fine.
1. Tool groups
Tools are organized into groups. Pass tool_groups to enable only the groups you need:
mcp_env = FlyteMCPAppEnvironment(
name="restricted-mcp",
tool_groups=["task", "run", "logs"], # Only these groups
)| Group | Tools | Typical use |
|---|---|---|
all |
All tools (default when both tool_groups and tools are omitted) |
Trusted local development |
core |
No tools (only HTTP routes) | Health-check-only / building up explicitly |
task |
run_task, get_task, list_tasks |
Launching and inspecting tasks |
run |
get_run, get_run_io, abort_run, list_runs, wait_for_run, rerun_run |
Monitoring and controlling runs |
action |
list_actions, get_action, abort_action |
Debugging a run step by step: phases, attempts, failure details, timing |
logs |
get_logs |
Reading task logs |
app |
get_app, list_apps, activate_app, deactivate_app |
Managing deployed apps |
trigger |
list_triggers, get_trigger, activate_trigger, deactivate_trigger |
Managing triggers |
project |
list_projects, get_project |
Discovering projects and domains |
secret |
list_secrets, create_secret, delete_secret |
Managing secrets (names only — values are never returned) |
condition |
list_conditions, signal_condition |
Answering human-in-the-loop gates |
identity |
whoami |
Confirming which identity and org the server is acting as |
search |
search_flyte_sdk_examples, search_flyte_docs_examples, search_full_docs |
Grounding answers in SDK/docs |
2. Individual tools
For the tightest control, pass tools with an explicit list of tool names instead of tool_groups (pass one or the other, not both):
mcp_env = FlyteMCPAppEnvironment(
name="read-only-mcp",
tools=["get_run", "list_runs", "get_run_io"], # No run_task, no abort_run
)This is the way to build, for example, a strictly read-only server.
3. Allowlists
Even with a tool enabled, you can restrict which resources it may target. Allowlists are the safest way to expose run_task or app/trigger management to an agent:
mcp_env = FlyteMCPAppEnvironment(
name="restricted-mcp",
task_allowlist=[
"production/my-project/allowed-task", # domain/project/task
"my-project/another-task", # project/task (any domain)
"any-domain-task", # task only (any project/domain)
],
app_allowlist=["my-app", "another-app"],
trigger_allowlist=["nightly-retrain"],
)When an allowlist is set, calls targeting anything outside it are rejected. Omitting an allowlist leaves that resource type unrestricted.
Enabling the search tools
The search tools need a corpus to scan, so you must point them at filesystem paths that exist inside the app image:
sdk_examples_path: Flyte SDK examples (powerssearch_flyte_sdk_examples)docs_examples_path: Union examples (powerssearch_flyte_docs_examples)full_docs_path: the docsllms.txtindex (powerssearch_full_docs)
The default image already clones the flyte-sdk and unionai-examples repos and downloads llms.txt into /root for you. If you supply a custom image, bake the corpora in yourself and pass matching paths:
image = (
flyte.Image.from_debian_base()
.with_apt_packages("ca-certificates", "git", "curl")
.with_pip_packages("mcp", "starlette", "uvicorn")
.with_commands([
"git clone --depth 1 https://github.com/flyteorg/flyte-sdk.git /root/flyte-sdk",
"git clone --depth 1 https://github.com/unionai/unionai-examples.git /root/unionai-examples",
# Also download llms.txt to /root/llms.txt for full-docs search
])
)
mcp_env = FlyteMCPAppEnvironment(
name="search-mcp",
image=image,
tool_groups=["search"],
sdk_examples_path="/root/flyte-sdk/examples",
docs_examples_path="/root/unionai-examples/v2",
full_docs_path="/root/llms.txt",
)Putting it together: a filtered server
This example combines tool groups, an allowlist, search paths, and instructions to build a scoped, production-ready server:
mcp_env = FlyteMCPAppEnvironment(
name="restricted-mcp",
image=image,
resources=flyte.Resources(cpu=1, memory="512Mi"),
transport="streamable-http",
tool_groups=["task", "run", "script", "search"],
task_allowlist=["my-project/my-task", "another-task"],
# Search paths (see docstring): you need to clone/fetch these during image build.
sdk_examples_path="/root/flyte-sdk/examples",
docs_examples_path="/root/unionai-examples/v2",
full_docs_path="/root/llms.txt",
instructions=(
"This MCP server provides tools to run and monitor specific Flyte tasks, "
"build and run UV scripts remotely, and search Flyte SDK/docs examples. "
"Only allowlisted tasks can be accessed."
),
)
if __name__ == "__main__":
flyte.init_from_config()
app_handle = flyte.serve(mcp_env)
app_handle.activate(wait=True)
print(f"App is ready at {app_handle.endpoint}")
Connecting a client
Claude Code: local (stdio)
Registers the flyte-mcp process as a locally managed stdio server. Claude Code starts and stops the uvx process automatically:
claude mcp add --transport stdio flyte-mcp -- \
uvx --from "flyte[mcp]>=2.5.18" flyte-mcp --transport stdio--transport stdio appears twice on purpose. The first tells Claude Code how to talk to the server; the second tells the server which transport to serve. Without the second, the server starts an HTTP listener and the connection fails.
Claude Code: remote (HTTP)
For a deployed server with a public URL:
claude mcp add --transport http \
--header "Authorization: Bearer $TOKEN" \
flyte-mcp-remote https://<YOUR_HOST>/flyte-mcp/mcpOpenCode: local
OpenCode spawns the uvx command for you:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"flyte-mcp": {
"type": "local",
"command": ["uvx", "--from", "flyte[mcp]>=2.5.18", "flyte-mcp", "--transport", "stdio"],
"enabled": true
}
}
}OpenCode: remote
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"flyte-mcp-remote": {
"type": "remote",
"url": "https://<YOUR_HOST>/flyte-mcp/mcp",
"enabled": true,
"headers": {
"Authorization": "Bearer $TOKEN"
}
}
}
}Best practices
- Start broad locally, scope down for sharing: Run with
alltools viauvxwhile exploring, then enable only the groups or tools you need before deploying a shared server. - Always allowlist mutating tools: If
run_task,abort_run, or app/trigger management is enabled on a shared server, set the corresponding allowlist so an agent can’t touch arbitrary resources. - Prefer
toolsovertool_groupsfor read-only servers: An explicit allowlist of read tools is the clearest way to guarantee an agent can’t change anything. - Write specific
instructions: Describe what the server does and any constraints (e.g. “only allowlisted tasks can be run”). This guides tool selection and reduces wasted calls. - Keep auth on: Leave
requires_auth=Trueso only authenticated clients can reach a deployed server.
MCP tools reference
| Tool | Group | Description |
|---|---|---|
run_task |
task |
Run a task |
get_task |
task |
Get task details |
list_tasks |
task |
List tasks |
get_run |
run |
Get a run |
get_run_io |
run |
Get run inputs and outputs |
abort_run |
run |
Abort a run |
list_runs |
run |
List runs |
wait_for_run |
run |
Wait for a run to finish |
rerun_run |
run |
Re-run a prior run |
list_actions |
action |
List the actions of a run |
get_action |
action |
Get action details and timing |
abort_action |
action |
Abort a single action |
get_logs |
logs |
Read action logs |
get_app |
app |
Get an app |
list_apps |
app |
List apps |
activate_app |
app |
Activate an app |
deactivate_app |
app |
Deactivate an app |
list_triggers |
trigger |
List triggers |
get_trigger |
trigger |
Get a trigger |
activate_trigger |
trigger |
Activate a trigger |
deactivate_trigger |
trigger |
Deactivate a trigger |
list_projects |
project |
List projects |
get_project |
project |
Get a project |
list_secrets |
secret |
List secret names |
create_secret |
secret |
Create a secret |
delete_secret |
secret |
Delete a secret |
list_conditions |
condition |
List the conditions of a run |
signal_condition |
condition |
Signal a waiting condition |
whoami |
identity |
Show the caller’s identity |
search_flyte_sdk_examples |
search |
Search Flyte SDK examples |
search_flyte_docs_examples |
search |
Search Flyte docs examples |
search_full_docs |
search |
Search the full Flyte docs |