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
58 changes: 48 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

A plugin marketplace for working with [Flyte](https://flyte.org) — in
[Claude Code](https://docs.claude.com/en/docs/claude-code),
[OpenAI Codex](https://developers.openai.com/plugins), or any agent harness that
supports [Agent Skills](https://agentskills.io).
[OpenAI Codex](https://developers.openai.com/plugins),
[Google Antigravity](https://antigravity.google), or any agent harness that supports
[Agent Skills](https://agentskills.io).

21 skills, plus two **MCP servers** that let Claude search Flyte docs and act on your own
cluster. `uvx flyte-skills install` gets you the skills in any harness;
Expand Down Expand Up @@ -46,7 +47,7 @@ uvx flyte-skills list # list the bundled ski
> [!NOTE]
> **This installs the skills, not the MCP servers.** The two servers that let Claude search
> Flyte docs and act on your cluster ship with the **plugin**, not the CLI — see
> [Install as a plugin](#install-as-a-plugin-claude-code-and-codex) if you want them, or
> [Install as a plugin](#install-as-a-plugin-claude-code-codex-and-antigravity) if you want them, or
> [Adding the MCP servers elsewhere](#adding-the-mcp-servers-elsewhere) to wire them up by
> hand.

Expand Down Expand Up @@ -85,7 +86,7 @@ every `install` command above works under either name. They differ in one thing:
servers too. To pin a version,
`uvx --from flyte-skills==<version> flyte-skills install`.

## Install as a plugin (Claude Code and Codex)
## Install as a plugin (Claude Code, Codex, and Antigravity)

Installing the plugin instead of the skills gets you the same 21 skills **plus** both MCP
servers, which are declared in `plugins/flyte/.mcp.json`.
Expand Down Expand Up @@ -132,22 +133,56 @@ the `mcp_servers` the Codex docs show — the manifest struct is `camelCase`,
path expanded, so `${CLAUDE_PLUGIN_ROOT}` — which Codex does not expand,
[openai/codex#22842](https://github.com/openai/codex/issues/22842) — never comes up.

### Google Antigravity

Antigravity's docs don't describe adding a third-party marketplace, so install the plugin
from a local checkout. `plugins/flyte/` is an Antigravity plugin as-is — it carries the
`plugin.json` marker Antigravity looks for at the plugin root:

```
git clone https://github.com/flyteorg/flyte-agent-plugins.git # add --branch <tag> to pin
agy plugin install ./flyte-agent-plugins/plugins/flyte
```

`/plugin install <path>` inside an Antigravity session does the same. The install copies
the plugin into `~/.gemini/config/plugins/flyte/`, so the clone can go afterwards — and to
update, pull (or check out a newer tag) and run `agy plugin install` again. `agy plugin list`,
`agy plugin disable flyte`, and `agy plugin uninstall flyte` manage it afterwards.

Antigravity namespaces plugin MCP servers by plugin name, so they appear as
`flyte_flyte-docs` and `flyte_flyte-cluster`. They don't show up in `agy mcp list`, which
lists only servers configured outside a plugin. When changing either manifest,
`agy plugin validate ./plugins/flyte` checks the plugin without installing it.

Both MCP servers come with it, but from `plugins/flyte/mcp_config.json` rather than
`.mcp.json`: Antigravity reads that file name, and it rejects the `url`/`type` fields on
remote servers in favour of `serverUrl`. The two files otherwise declare the same servers,
and `packaging/verify.py` fails if they drift. Antigravity's `plugin.json` carries only
`name` and `description` — the fields its documented schema defines — so the release
version still lives in `.claude-plugin/plugin.json`.

## Harness-native installs

The skills are plain [Agent Skills](https://agentskills.io) (`SKILL.md` + YAML
frontmatter), so they work in any harness that supports the standard. `flyte-agent-plugins
install` above covers all five; each harness also has its own installer, which is what you
install` above covers all of them; each harness also has its own installer, which is what you
want when you would rather track the repo than a release, or to install a single skill
rather than all 21.

| Harness | Skills | MCP servers | `flyte-agent-plugins install` |
|---|---|---|---|
| Claude Code | all 21 | both, automatically | `--target claude` |
| Codex CLI | all 21 | both, automatically | `--target agents` |
| Antigravity | all 21 | both, via the plugin | `--target agents --project` |
| Hermes | per-skill | none — add manually | `--target hermes` |
| opencode | all 21 | none — add manually | `--target opencode` |
| pi | all 21 | none — add manually | `--target pi` |

Antigravity reads `<workspace>/.agents/skills/`, which is why its row needs `--project`.
Its global skills directory is not `~/.agents/skills/` — it is `~/.gemini/config/skills/`
for the app and IDE, `~/.gemini/antigravity-cli/skills/` for `agy` — so for a skills-only
global install pass that directory with `--dir`, or install the plugin instead.

### Hermes

`flyte-agent-plugins install --target hermes` writes all 21 into `~/.hermes/skills/`, which Hermes
Expand Down Expand Up @@ -292,7 +327,7 @@ the plugin [README](plugins/flyte/README.md).
### Adding the MCP servers elsewhere

Hermes, opencode, and pi all support MCP — this plugin just doesn't configure it for them.
(Claude Code and Codex get both servers from the plugin; use these snippets only if you
(Claude Code, Codex, and Antigravity get both servers from the plugin; use these snippets only if you
want them configured globally rather than per-plugin.) Wiring it up yourself is a few
lines.

Expand Down Expand Up @@ -368,19 +403,22 @@ package.json # pi package manifest (pi.skills)
plugins/flyte/.claude-plugin/plugin.json # Claude Code plugin manifest
plugins/flyte/.codex-plugin/plugin.json # Codex plugin manifest (points at .mcp.json)
plugins/flyte/.mcp.json # the two bundled MCP servers
plugins/flyte/plugin.json # Antigravity plugin manifest
plugins/flyte/mcp_config.json # the same servers, in Antigravity's schema
plugins/flyte/skills/<skill>/SKILL.md
packaging/build.py # fans plugins/flyte out into the npm + PyPI packages
packaging/verify.py # builds every distribution and proves it installs
scripts/smoke_test_mcp.py # end-to-end check of the local MCP server
```

Each harness consumes a different part of this. Claude Code and Codex read the plugin
manifests, so the **plugin name** matters to them. Hermes, opencode, and pi install skills
Each harness consumes a different part of this. Claude Code, Codex, and Antigravity read
the plugin manifests, so the **plugin name** matters to them. Hermes, opencode, and pi install skills
by **directory path**, so `plugins/flyte/skills/…` is their interface.

`.mcp.json` is shared by Claude Code (which finds it by convention) and Codex (which is
pointed at it by `.codex-plugin/plugin.json`); the skills themselves stay portable across
every harness.
pointed at it by `.codex-plugin/plugin.json`). Antigravity needs its own copy in
`mcp_config.json`, kept in step by `packaging/verify.py`; the skills themselves stay
portable across every harness.

The PyPI packages are a fourth consumer: `packaging/build.py` vendors the whole
`plugins/flyte/` tree into each one, which is how `flyte-agent-plugins install` can write the
Expand Down
9 changes: 7 additions & 2 deletions packaging/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def has_mcp(dist: str) -> bool:
"claude-code",
"claude-code-plugin",
"codex",
"antigravity",
"mcp",
"workflow-orchestration",
]
Expand Down Expand Up @@ -116,11 +117,13 @@ def skill_names() -> list[str]:


def copy_plugin(dest: Path) -> None:
"""Copy the plugin payload (manifests + .mcp.json + skills) into dest."""
"""Copy the plugin payload (manifests + MCP configs + skills) into dest."""
dest.mkdir(parents=True, exist_ok=True)
for name in (".claude-plugin", ".codex-plugin", "skills"):
shutil.copytree(PLUGIN_SRC / name, dest / name)
shutil.copy2(PLUGIN_SRC / ".mcp.json", dest / ".mcp.json")
# Antigravity reads its manifest and MCP config from the plugin root.
for name in (".mcp.json", "plugin.json", "mcp_config.json"):
shutil.copy2(PLUGIN_SRC / name, dest / name)


def build_npm(dist: str, ver: str, outdir: Path) -> Path:
Expand Down Expand Up @@ -157,6 +160,8 @@ def build_npm(dist: str, ver: str, outdir: Path) -> Path:
".claude-plugin/",
".codex-plugin/",
".mcp.json",
"plugin.json",
"mcp_config.json",
"skills/",
"bin/",

Expand Down
8 changes: 4 additions & 4 deletions packaging/templates/README.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ editing config files — so it needs one of those CLIs on your PATH:
These are tools the agent can call, not inert markdown: `flyte-docs` sends search
queries to a Union-operated endpoint, and `flyte-cluster` gets control-plane access
to your cluster (which also needs `uv` and a Flyte login). Harnesses that consume
the whole plugin (Claude Code, Codex) pick the servers up from `.mcp.json`
automatically and need none of this.
the whole plugin (Claude Code, Codex, Antigravity) pick the servers up from the
bundled config automatically and need none of this.
<!-- mcp-only:end -->

## Native install paths
Expand All @@ -93,8 +93,8 @@ marketplace in its own right:
/plugin install flyte@flyte-agent-plugins
```

See [the repository README]({{REPO_URL}}#readme) for Codex, Hermes, opencode, and
pi instructions, the full skill list, and MCP server details.
See [the repository README]({{REPO_URL}}#readme) for Codex, Antigravity, Hermes,
opencode, and pi instructions, the full skill list, and MCP server details.

## License

Expand Down
44 changes: 42 additions & 2 deletions packaging/verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
Checks, for both names on both registries:
* every manifest agrees on one version;
* every skill has frontmatter with a name matching its directory;
* the Antigravity manifests agree with the Claude Code ones they mirror;
* the npm tarball carries the dot-directories Claude Code needs at the package
root (npm's default file selection is not reliable about those);
* the built wheel carries the skills as package data;
Expand Down Expand Up @@ -58,6 +59,36 @@ def check_versions() -> None:
check(len(set(versions.values())) == 1, f"one version across manifests: {versions}")


def check_antigravity() -> None:
"""Antigravity reads its own manifest (``plugin.json``) and its own MCP config
(``mcp_config.json``) from the plugin root. Both are hand-maintained mirrors of
the Claude Code files, so drift between them is the failure to catch.

The one intended difference: Antigravity rejects ``url``/``type`` on remote
servers and requires ``serverUrl`` instead.
"""
print("antigravity")
plugin = builder.PLUGIN_SRC
claude = json.loads((plugin / ".claude-plugin" / "plugin.json").read_text())
manifest = json.loads((plugin / "plugin.json").read_text())
check(
manifest == {"name": claude["name"], "description": claude["description"]},
"plugin.json carries the Claude Code manifest's name and description",
)

def as_antigravity(server: dict) -> dict:
if "url" not in server:
return server
return {"serverUrl": server["url"], **{k: v for k, v in server.items() if k not in ("url", "type")}}

expected = {
name: as_antigravity(server)
for name, server in json.loads((plugin / ".mcp.json").read_text())["mcpServers"].items()
}
actual = json.loads((plugin / "mcp_config.json").read_text())["mcpServers"]
check(actual == expected, f"mcp_config.json mirrors .mcp.json: {sorted(actual)}")


def check_targets(workdir: Path, npm_pkg: Path, py_exe: str) -> None:
"""The Python and Node CLIs declare harness targets independently, so a
target added to one and forgotten in the other is a real drift risk."""
Expand Down Expand Up @@ -150,7 +181,14 @@ def check_npm(dist: str, pkg: Path, workdir: Path) -> None:
print(f"npm {dist}")
out = run(["npm", "pack", "--dry-run", "--json"], cwd=pkg).stdout
files = {f["path"] for f in json.loads(out)[0]["files"]}
for required in (".claude-plugin/plugin.json", ".codex-plugin/plugin.json", ".mcp.json", "bin/cli.mjs"):
for required in (
".claude-plugin/plugin.json",
".codex-plugin/plugin.json",
".mcp.json",
"plugin.json",
"mcp_config.json",
"bin/cli.mjs",
):
check(required in files, f"tarball contains {required}")
packed = {f.split("/")[1] for f in files if f.startswith("skills/")}
check(packed == set(builder.skill_names()), f"tarball contains all {len(builder.skill_names())} skills")
Expand Down Expand Up @@ -186,7 +224,8 @@ def check_pypi(dist: str, pkg: Path, workdir: Path) -> None:
names = set(zipfile.ZipFile(wheel).namelist())
mod = builder.module_name(dist)
check(f"{mod}/plugin/.claude-plugin/plugin.json" in names, "wheel contains .claude-plugin/plugin.json")
check(f"{mod}/plugin/.mcp.json" in names, "wheel contains .mcp.json")
for required in (".mcp.json", "plugin.json", "mcp_config.json"):
check(f"{mod}/plugin/{required}" in names, f"wheel contains {required}")
packed = {n.split("/")[3] for n in names if n.startswith(f"{mod}/plugin/skills/")}
check(packed == set(builder.skill_names()), f"wheel contains all {len(builder.skill_names())} skills")

Expand Down Expand Up @@ -218,6 +257,7 @@ def check_pypi(dist: str, pkg: Path, workdir: Path) -> None:
def main() -> int:
check_versions()
check_skills()
check_antigravity()

with tempfile.TemporaryDirectory() as tmp:
workdir = Path(tmp)
Expand Down
23 changes: 19 additions & 4 deletions plugins/flyte/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
A single plugin for [Flyte](https://flyte.org): cluster deployment and SDK / workflow
authoring skills, plus two bundled MCP servers.
[Claude Code](https://docs.claude.com/en/docs/claude-code) and
[OpenAI Codex](https://developers.openai.com/plugins) install it as a full plugin — skills
[OpenAI Codex](https://developers.openai.com/plugins), and
[Google Antigravity](https://antigravity.google) install it as a full plugin — skills
**and** MCP servers — and any harness that supports
[Agent Skills](https://agentskills.io) (Hermes, opencode, pi) can install the skills.

Expand Down Expand Up @@ -67,9 +68,11 @@ Convert existing Flyte 1 (`flytekit`) code to Flyte 2, distilled from the offici
## Bundled MCP servers

The servers live in `.mcp.json`. Claude Code reads that file by convention; Codex picks it
up through the `mcpServers` entry in `.codex-plugin/plugin.json`. Hermes, opencode, and pi
install the skills and nothing else — they all support MCP, so you can add these by hand,
see [Adding the MCP servers elsewhere](../../README.md#adding-the-mcp-servers-elsewhere).
up through the `mcpServers` entry in `.codex-plugin/plugin.json`; Antigravity reads the same
servers from `mcp_config.json`, which uses its `serverUrl` field for the remote one.
Hermes, opencode, and pi install the skills and nothing else — they all support MCP, so you
can add these by hand, see
[Adding the MCP servers elsewhere](../../README.md#adding-the-mcp-servers-elsewhere).

`.mcp.json` declares **two MCP servers**, split so nothing is duplicated:

Expand Down Expand Up @@ -169,6 +172,18 @@ MCP servers come with it:
codex plugin marketplace add flyteorg/flyte-agent-plugins # or --ref <tag-or-branch> to pin
```

## Install (Google Antigravity)

Install from a local checkout — this directory carries the `plugin.json` marker
Antigravity looks for, and the skills and both MCP servers come with it:

```
git clone https://github.com/flyteorg/flyte-agent-plugins.git # add --branch <tag> to pin
agy plugin install ./flyte-agent-plugins/plugins/flyte
```

The install is a copy, so re-run it after pulling to update.

## Install (other agent harnesses)

The skills are standard [Agent Skills](https://agentskills.io) (`SKILL.md`), so they also
Expand Down
20 changes: 20 additions & 0 deletions plugins/flyte/mcp_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"mcpServers": {
"flyte-docs": {
"serverUrl": "https://flyte-mcp.apps.demo.hosted.unionai.cloud/flyte-mcp/mcp"
},
"flyte-cluster": {
"command": "uvx",
"args": [
"--from",
"flyte[mcp]==2.6.10",
"flyte-mcp",
"--transport",
"stdio",
"--tool-groups",
"task,run,action,logs,app,trigger,project,secret,condition,identity",
"--no-init-from-config"
]
}
}
}
4 changes: 4 additions & 0 deletions plugins/flyte/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "flyte",
"description": "Work with Flyte 2. Deploy Flyte on AWS or kind, author and run workflows, apps, agents, data, and ML workloads with the SDK, and operate your cluster through bundled MCP servers."
}
Loading