Skip to content

feat: gflow capabilities and MCP capabilities://self (#1197) - #1206

Merged
ffroliva merged 8 commits into
developfrom
feature/1197-capabilities
Nov 11, 2026
Merged

ffroliva merged 8 commits into
developfrom
feature/1197-capabilities

Conversation

@ffroliva

@ffroliva ffroliva commented Nov 9, 2026 •

Copy link
Copy Markdown
Owner

Closes #1197
Part of #1191
Follow-up: #1212 (per-profile host resolution)

Summary

  • gflow capabilities [--json] and the MCP resource capabilities://self return one document: schema, adapter (flow-browser), host (the GFLOW_CLI_FLOW_HOST setting), capabilities (one of yes|no|partial|unknown per key) and notes (always present for partial, saying what is missing).
  • One source of truth: src/gflow_cli/cli_capabilities.py::build_capabilities, serialized by json_output.dumps (the one --json serializer, also used by emit). The one deliberate difference is a no-spend MCP server: it registers no generate tool, so generate.image, generate.video and submit.no_wait are no there (docs(mcp): remove false safety claims (credit budgets, pre-flight auth) — MCP doc-truth sweep #495: a resource names only registered tools).
  • Same document, tested twice: a unit test compares CliRunner stdout with the in-memory MCP read_resource. The e2e test tests/e2e/test_capabilities_e2e.py runs the real python -m gflow_cli capabilities --json in a subprocess for each host setting and compares the parsed JSON with the resource.
  • Coverage gate: COMMAND_CAPABILITY in tests/mcp/test_capabilities.py maps every CLI leaf to a key or None. The Click walk is shared with test_cli_parity.py (tests/mcp/_cli_tree.py). It fails on a new command, on stale entries and on unknown keys, and a probe test proves it bites.
  • Staleness guards: a test fails when status, wait, cancel, reconcile, a top-level upload or a --no-wait option appears, so the "not yet" notes cannot go stale silently when CLI: --no-wait, gflow status <id> and gflow wait <id> #1194/CLI: gflow cancel <id> (local abort; honest about server-side cancel) #1198/CLI: gflow reconcile <id> settles one unknown outcome #1199/CLI: gflow upload <file> returns a reusable media id #1200 land. Another test keeps the JSON block in docs/CAPABILITIES.md equal to the code's default output.
  • MCP parity: capabilities sits in _MCP_EXEMPT with the reason "twin is the MCP resource capabilities://self". CLI_TO_MCP requires a registered tool.
  • Docs: docs/CAPABILITIES.md (every key and its meaning), linked from INDEX and the website nav; USAGE, MCP, the skill (+ plugin copy), llms.txt, README, the AGENTS.md command surface and module list, CHANGELOG.

Values today, and why

Key Value Why
generate.image partial (auto, flow.google.com), unknown (labs.google), no (no-spend MCP) flow.google.com: Agent instructions, image4, image batch, Flow media UUIDs the catalog does not record in this --project (#913/#995), mixed refs and a character id without a name exit 36 (migrated_composer._unported_image_form, ui_automation image_batch_unported); agent-only composer t2i only (#799). labs.google: whether Flow serves the account labs is not checkable offline.
generate.video same pattern flow.google.com: frames/refs by UUID or @Name, characters on t2v/i2v, a character id without a name, and extend chains/--scene/--seed exit 36 (_unported_form).
submit.no_wait, status, wait partial MCP wait=false + tasks/get (needs a client with the SEP-2663 Tasks extension); the CLI verbs are #1194.
download partial data download / gflow_download_media recover video only; an image id is refused (exit 11, #877).
upload partial, host-dependent note image upload is CLI-only (#1200). On flow.google.com an uploaded id is not taken as a video frame/reference (exit 36), and as an image ref only when the catalog records it in the same --project (#995).
abort.local partial Ctrl-C and MCP tasks/cancel; no cancel <id> (#1198).
cancel.server, idempotent_submit no An accepted generation runs and may still be charged; a retried submit is a new one.
reconcile partial data sync --names only (#543); no reconcile <id> (#1199).
credits.balance partial Veo video-credit balance only; image quotas are not read; tier fields null.

Real CLI run (worktree install, one per host setting)

The e2e test does this run and compares each result with the resource:

auto            generate.*: partial; download: partial; cancel.server/idempotent_submit: no; rest partial
flow.google.com same values as auto (notes differ: no auto prefix)
labs.google     generate.*: unknown; rest as auto

Review

Two independent reviews (/code-review xhigh, ponytail-review xhigh) came in on the first version.

Fixed

  1. No-spend: the resource now reports generate.image, generate.video and submit.no_wait as no on a no-spend server and names no unregistered tool (test with monkeypatch; CLI unaffected, also tested). Docs say "same document, except on a no-spend server".
  2. download changed from yes to partial (video-only recovery, gflow data download accepts an image media id, waits 45s, then blames the project, the trash, and your prompt #877).
  3. upload is host-dependent and says what flow.google.com does not take.
  4. The partial generate.* notes now name what is not driven (exit 36), checked against _unported_image_form / _unported_form / the image-batch refusal. gflow run is not listed as refused: it generates row by row through the single-image path, which flow.google.com serves; only image batch raises image_batch_unported.
  5. "is charged" became "may still be charged" (a test bans the old wording).
  6. Guard test for the not-yet-built verbs and --no-wait.
  7. COMMAND_CAPABILITY moved into the test, the walker is shared with the parity test, scene create → None (credit-free composition), character create → generate.image, and the comment lists every None category.
  8. "No network call" / "offline" became "makes no call to Flow", and docs/CAPABILITIES.md notes the update check and telemetry upload the process may still run.
  9. The "byte for byte" claim became "the same JSON document", and the subprocess e2e test was added (Iron Law).
  10. to_json was removed; json_output.dumps is the one serializer.
  11. The submit.no_wait / status / abort.local notes name the SEP-2663 Tasks extension.
  12. The manual reset_settings() try/finally was dropped from the host test (the autouse fixture isolates it).
  13. Per-profile resolution is filed as capabilities: resolve the served host per profile (--probe or a recorded observation) #1212 (--probe, or a recorded served-host observation), linked here and from docs/CAPABILITIES.md, which also says host reports the setting. Closes #1197 is kept: its Acceptance list is met.

Hand-copied docs values: I chose the cheaper option, a test that the JSON block in docs/CAPABILITIES.md equals build_capabilities("auto"). It already caught the stale download row during this round. The prose in llms.txt and the skill names keys, not values, apart from cancel.server / idempotent_submit = no.

Not changed, and why

Open questions

Gates (committed tree, UV_LINK_MODE=copy PYTHONUTF8=1)

Gate Result
check_repo_hygiene 1468 tracked files, no violations
check_doc_links all resolved (262 files)
check_website_docs_pii clean (28 files)
generate_website_docs --check in sync (23), nav complete
check_council_memory 52 files, all resolving
generate_plugin_skills --check in sync
ruff check clean
ruff format --check 619 files formatted
uv run pyright src 0 errors
pytest (offline, -n 8 --dist worksteal --cov) 6087 passed, 0 failed, 30 skipped, coverage 93%
e2e tests/e2e/test_capabilities_e2e.py (-m e2e) 3 passed

TDD for this round: the rewritten tests ran red first (13 failed: missing no_spend, download still yes, upload not host-dependent, notes not naming what is missing, "is charged", no SEP-2663 mention, no-spend resource). After the implementation, the docs JSON-block guard went red on the stale download row, and the docs update fixed it. All green afterwards.

Step 1b (six mirror axes)

  • A. Execution paths: N/A. Only --json; no request is built and nothing is queued.
  • B. MCP surface truth: the resource description, docs/MCP.md and the no-spend behaviour agree; the parity file records the decision.
  • C. Agent-facing: the skill, the AGENTS.md command surface, README and INDEX are updated.
  • D. Errors / exit codes: N/A. No new error class or exit code.
  • E. Capability-reporting: this command; gflow models / gflow doctor unchanged.
  • F. Docs + mirror: USAGE, the website mirror + nav, CHANGELOG ### Added.

E2E scope

The command makes no call to Flow and opens no browser, so it touches no Flow surface. The Iron Law still wants an e2e test for a new command, so one was added: tests/e2e/test_capabilities_e2e.py (e2e_auth, zero credits, no browser) runs the real binary path once per host setting and compares the result with capabilities://self. Result: 3 passed.

…//self (#1197)

One source of truth in cli_capabilities.py: the CLI --json output and the
MCP resource serialize the same function, and a test holds them to the
same bytes. Values are yes/no/partial/unknown with a note on every partial.
Offline: the only input is GFLOW_CLI_FLOW_HOST. A test fails when a CLI
command is added without a COMMAND_CAPABILITY entry.
…s, host-neutral notes (#1197)

labs.google: gflow's labs driver takes every mode, but whether Flow serves
the account labs is not checkable offline, so generation is unknown, not
yes. credits.balance is the Veo balance only, so partial. The auto note
now says what gflow cannot drive yet instead of what flow.google.com
cannot take.
@coderabbitai

coderabbitai Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 13 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6c7d94e7-0560-4ad8-9cd1-854e1f90ab31
Commits

Reviewing files that changed from the base of the PR and between c3bd669 and e4d9ee5.

Files selected for processing: 22

  • AGENTS.md
  • CHANGELOG.md
  • README.md
  • docs/CAPABILITIES.md
  • docs/INDEX.md
  • docs/MCP.md
  • docs/USAGE.md
  • llms.txt
  • plugins/gflow/skills/gflow-cli/SKILL.md
  • skills/gflow-cli/SKILL.md
  • src/gflow_cli/cli.py
  • src/gflow_cli/cli_capabilities.py
  • src/gflow_cli/json_output.py
  • src/gflow_cli/mcp/resources.py
  • tests/e2e/test_capabilities_e2e.py
  • tests/mcp/_cli_tree.py
  • tests/mcp/test_capabilities.py
  • tests/mcp/test_cli_parity.py
  • website/docs/CAPABILITIES.md
  • website/docs/MCP.md
  • website/docs/USAGE.md
  • website/mkdocs.yml
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

…ad, honest notes (#1197)

Review fixes. A no-spend MCP server registers no generate tool, so the
resource reports generate.image, generate.video and submit.no_wait as no
(#495 rule). download is partial: recovery refuses image ids (#877).
upload depends on the host: flow.google.com does not take an uploaded id
as a video frame or reference. Partial generation notes now name what is
not driven (exit 36), billing wording says may still be charged, and the
tasks notes name the SEP-2663 Tasks extension.

json_output.dumps is the one serializer. COMMAND_CAPABILITY moves into
the test (with scene create and character create remapped), the Click
walk is shared with the parity test, a guard fails when a not-yet-built
lifecycle verb lands, a test keeps the docs JSON block true, and an e2e
test runs the real command in a subprocess against the resource.
…ilities

# Conflicts:
#	AGENTS.md
#	CHANGELOG.md
#	README.md
@ffroliva

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

…ilities

# Conflicts:
#	AGENTS.md
#	CHANGELOG.md
#	docs/USAGE.md
#	website/docs/USAGE.md
Merging develop brought in #1204's `models list` and `models show`. Both read
the local model catalog, outside the generation-lifecycle vocabulary, so they
map to None like the bare `models` entry.
@ffroliva
ffroliva merged commit 1a3df74 into develop Nov 11, 2026
22 checks passed
@ffroliva
ffroliva deleted the feature/1197-capabilities branch November 11, 2026 03:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLI + MCP: gflow capabilities and capabilities://self

1 participant