You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(workflows): expose workflow catalog sources through MCP - #5239
Adds specify_workflow_catalog_list for #4913, as proposed in the issue. specify workflow catalog list and the new MCP tool now share a typed, read-only workflow.catalog.list operation, following the structure of #5115. Only this command changes to available in the inventory.
_validate_catalog_url accepts URLs such as https://user:token@host/catalog.json. The MCP result removes userinfo, query strings and fragments, and MCP errors use fixed messages, because config errors can quote a configured URL. The CLI still prints URLs as configured. A secret placed in the URL path isn't detected, which the docs state.
Please check: differences from my proposal
The CLI rejects more unsafe paths. I wrote "CLI output unchanged", and that holds for normal listings and config errors. However, the shared operation now applies validate_workflow_storage and the project file policy, so the CLI rejects unsafe workflow storage and a symlinked .specify/workflow-catalogs.yml unless allow_symlinks is set. It used to follow them. I chose this for adapter parity and because workflow list behaves this way. If you'd rather keep the old CLI behavior, it's a small change.
Fragments are removed too, in addition to userinfo and the query string, because they can also carry secrets.
The tool takes limit (1-1000, default 100) and reports truncated, matching specify_workflow_list, to keep responses bounded. The CLI still lists everything.
The environment override and user-level config come from the server process rather than the selected project, the same resolution order as the CLI. The docs state this.
Testing
Tested locally with specify --help
Ran existing tests with uv sync && uv run pytest (partial on Windows, see below)
Tested with a sample project
All results are from Windows 11 with Python 3.13, using the project's .venv.
The same 35 tests fail in both runs. I also compared 65 other tests that failed in a full run, and they fail in both too.
My full run stopped early at a failure limit I had set: 9,142 passed, 95 failed, 602 skipped. All 95 failures came with WinError 1314, because this host can't create symlinks. So the full suite hasn't completed on my machine, and I'm relying on CI for the rest.
test_symlinked_project_config_is_not_followed is skipped here for the same reason, so a real symlink hasn't been exercised locally. test_project_config_read_goes_through_the_file_policy covers the error mapping by simulating the denial.
CLI output is byte-identical to main in 6 cases: defaults, a configured stack with brackets and credentials, and 4 invalid configs, including exit codes.
Disabling the URL stripping makes 10 tests fail, which confirms they protect it.
uvx ruff@0.15.0 check src tests and markdownlint-cli2 on the changed docs pass.
No slash command templates or scripts changed, so I didn't run manual slash-command tests.
AI Disclosure
I did not use AI assistance for this contribution
I did use AI assistance (fill in the disclosure below)
AI disclosure: Posted by @franciscogdeveloper. Claude Code (model: Claude Opus 5.5, auto mode, autonomous) inspected the source and wrote the code, tests, docs and this description. I reviewed the change before submitting it.
Add a typed, read-only workflow.catalog.list operation shared by the CLI
and a new specify_workflow_catalog_list MCP tool, and mark only this
command as available in the workflow inventory.
The MCP result removes userinfo, query strings and fragments from catalog
URLs, because they can carry credentials, and its errors never quote
configured values. The CLI keeps printing URLs as configured.
Reading through the shared operation means the CLI now also rejects unsafe
workflow storage and a symlinked project catalog config unless the project
policy allows symlinks, matching workflow list.
Refs github#4913
Assisted-by: Claude Code (model: Claude Opus 5.5, autonomous)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Nested YAML aliases in catalog text fields are stringified before request or response limits apply, allowing a small configuration to exhaust MCP server memory.
- Measure catalog configs with aliases unfolded before constructing them,
covering nested aliases, merge keys, recursive aliases and deep nesting
- Keep the shared invalid_catalog_config message free of configured values;
the CLI keeps its local diagnostics through the exception cause
- Load the project policy before classifying .specify, matching workflow.list
- Declare warning_types on the workflow.catalog.list descriptor
- Document the symlink policy difference between catalog list and other
catalog readers, and qualify the MCP error wording
- Cover contained config symlinks under both policies and tighten the
file-policy and default-read tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Empty-scalar aliases bypass the expansion limit, allowing a small catalog configuration to cause excessive memory allocation before MCP response-size checks.
With allow_symlinks: true, a project catalog config linked to a missing file inside the project raises FileNotFoundError for the target. This handler treats it as an absent configuration, so get_active_catalogs() silently returns user or built-in sources instead. The FileHelper contract (design/file-helper.md, “Boundary and policy”) requires existing regular-file targets. Please preserve the failure for a present dangling link and add a regression test; only a genuinely absent config should select the next layer.
AI-generated review for the requester by GitHub Copilot (model unavailable, autonomous); no human validation claimed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
triage-nice-to-haveVerdict: evidence-backed fix or greenlit feature — land after review
3 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds
specify_workflow_catalog_listfor #4913, as proposed in the issue.specify workflow catalog listand the new MCP tool now share a typed, read-onlyworkflow.catalog.listoperation, following the structure of #5115. Only this command changes toavailablein the inventory.Refs #4913
Credentials in catalog URLs
_validate_catalog_urlaccepts URLs such ashttps://user:token@host/catalog.json. The MCP result removes userinfo, query strings and fragments, and MCP errors use fixed messages, because config errors can quote a configured URL. The CLI still prints URLs as configured. A secret placed in the URL path isn't detected, which the docs state.Please check: differences from my proposal
validate_workflow_storageand the project file policy, so the CLI rejects unsafe workflow storage and a symlinked.specify/workflow-catalogs.ymlunlessallow_symlinksis set. It used to follow them. I chose this for adapter parity and becauseworkflow listbehaves this way. If you'd rather keep the old CLI behavior, it's a small change.limit(1-1000, default 100) and reportstruncated, matchingspecify_workflow_list, to keep responses bounded. The CLI still lists everything.The environment override and user-level config come from the server process rather than the selected project, the same resolution order as the CLI. The docs state this.
Testing
specify --helpuv sync && uv run pytest(partial on Windows, see below)All results are from Windows 11 with Python 3.13, using the project's
.venv.tests/specify_cli/workflows,tests/specify_cli/mcp_server,tests/test_workflows.pyWinError 1314, because this host can't create symlinks. So the full suite hasn't completed on my machine, and I'm relying on CI for the rest.test_symlinked_project_config_is_not_followedis skipped here for the same reason, so a real symlink hasn't been exercised locally.test_project_config_read_goes_through_the_file_policycovers the error mapping by simulating the denial.mainin 6 cases: defaults, a configured stack with brackets and credentials, and 4 invalid configs, including exit codes.uvx ruff@0.15.0 check src testsandmarkdownlint-cli2on the changed docs pass.No slash command templates or scripts changed, so I didn't run manual slash-command tests.
AI Disclosure
AI disclosure: Posted by @franciscogdeveloper. Claude Code (model: Claude Opus 5.5, auto mode, autonomous) inspected the source and wrote the code, tests, docs and this description. I reviewed the change before submitting it.