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
feat(mcp): filter SafeOutputs tools based on front matter config (#156)
* feat(mcp): filter SafeOutputs tools based on front matter config
Only expose safe output tools that are configured in the front matter's
safe-outputs section. This reduces agent confusion by hiding tools that
aren't configured for the current pipeline.
Implementation:
- Add --enabled-tools repeatable CLI arg to mcp and mcp-http commands
- SafeOutputs::new() accepts optional enabled tools list and uses
ToolRouter::remove_route() to remove unconfigured tools at startup
- Compiler derives tool list from safe-outputs keys and emits
--enabled-tools args in the pipeline template
- Always-on diagnostic tools (noop, missing-data, missing-tool,
report-incomplete) are never filtered out
Backward compatible: if --enabled-tools is not passed (empty
safe-outputs or omitted), all tools remain available.
Note: MCPG has a tools field in its config schema but does not enforce
it at runtime. This change filters at the SafeOutputs server level
instead, making it self-contained.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: address PR review feedback for SafeOutputs tool filtering
- Validate tool names against safe regex (ASCII alphanumeric + hyphens)
to prevent shell injection from malicious YAML keys
- Fix dangling backslash in base.yml when enabled_tools_args is empty
- Replace fragile exact-count assertion in test_tool_filtering_multiple_tools
with explicit presence/absence checks
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: trailing space for enabled_tools_args and move ALWAYS_ON_TOOLS
- Add trailing space to generate_enabled_tools_args output when non-empty,
preventing the last --enabled-tools value from concatenating with the
next positional argument in the shell command
- Move ALWAYS_ON_TOOLS constant from mcp.rs to tools/mod.rs to avoid
compile→mcp coupling (common.rs now imports from tools directly)
- Reduce list_all() calls in tool filtering to a single collect pass
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: warn on unrecognized safe-output keys at compile time
- Add ALL_KNOWN_SAFE_OUTPUTS constant in tools/mod.rs enumerating every
valid safe-output key (MCP tools + always-on diagnostics + memory)
- Emit compile-time warning when a safe-outputs key doesn't match any
known tool, catching typos like 'crate-pull-request' early
- Use HashSet for O(1) deduplication when merging always-on tools
- Rename misleading test to test_generate_enabled_tools_args_warns_on_unknown_tool
and exercise the typo path (crate-pull-request)
- Document {{ enabled_tools_args }} template marker in AGENTS.md
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: skip unrecognized safe-output tool names in enabled-tools args
Previously, unrecognized tool names (e.g. typos like "crate-pull-request")
were warned about but still appended to --enabled-tools. This caused the
real tool (create-pull-request) to be silently filtered out at runtime
because it was absent from the enabled list.
Now unrecognized names are skipped entirely, making the warning and
behavior consistent. The warning message is also updated to be clearer.
Additional improvements:
- Add test asserting ALL_KNOWN_SAFE_OUTPUTS covers every router-registered
tool, preventing the list from drifting when new tools are added
- Add integration test verifying --enabled-tools flags appear in compiled
pipeline YAML (end-to-end: standalone.rs + generate_enabled_tools_args +
template substitution)
- Document is_safe_tool_name newline-safety requirement
- Add explanatory comment in base.yml template for the inline substitution
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: skip non-MCP keys and warn on all-unrecognized safe-outputs
Address PR review feedback:
- Skip non-MCP safe-output keys (e.g. `memory`) from --enabled-tools
generation. These keys have no MCP route, so including them would
cause real MCP tools to be filtered out at runtime.
- Add prominent warning when all user-specified safe-output keys are
invalid/unrecognized/non-MCP, since the agent would be restricted
to diagnostic tools only.
- Remove unreachable `args.is_empty()` branch — ALWAYS_ON_TOOLS
guarantees the args string is never empty when safe-outputs is
configured.
- Add tests for memory key skipping and memory-only configuration.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: memory-only safe-outputs should not restrict MCP tools
When safe-outputs contains only non-MCP keys (e.g. just `memory`) or
only unrecognized names (typos), return empty args so all tools remain
available — matching backward-compatible behavior. Previously this
path would emit only ALWAYS_ON_TOOLS in --enabled-tools, silently
restricting the agent to 4 diagnostic tools.
Also:
- Add debug! log in server-side filtering for enabled-tools entries
that have no matching route, aiding troubleshooting
- Add template comment documenting positional arg ordering requirement
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* refactor: derive safe-output tool lists from types at compile time
Replace hand-maintained string arrays with compile-time type-derived
lists using two new macros:
- tool_names![Type1, Type2, ...] → extracts ToolResult::NAME from
each type into a &[&str] array
- all_safe_output_names![types...; "extra"] → combines type names
with non-MCP string keys
Changes:
- Add ToolResult::REQUIRES_WRITE associated constant (default false)
- Extend tool_result! macro with `write = true` parameter (4 arms)
- Annotate all 17 write-requiring tools with write = true
- CreatePrResult (manual impl) also gets REQUIRES_WRITE = true
- Move WRITE_REQUIRING_SAFE_OUTPUTS from common.rs to tools/mod.rs
- Derive ALWAYS_ON_TOOLS, WRITE_REQUIRING_SAFE_OUTPUTS, and
ALL_KNOWN_SAFE_OUTPUTS from type lists — no more string duplication
- Add NON_MCP_SAFE_OUTPUT_KEYS as public const in mod.rs
- Add 5 subset/consistency tests:
- WRITE_REQUIRING ⊆ ALL_KNOWN
- ALWAYS_ON ⊆ ALL_KNOWN
- NON_MCP ⊆ ALL_KNOWN
- REQUIRES_WRITE consistency (true for write tools, false for diag)
- ALL_KNOWN count = write + diagnostic + non-MCP
Adding a new tool now requires adding its type to the list in mod.rs;
the name string is derived automatically from ToolResult::NAME,
eliminating the risk of typo drift between lists.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore: document 1ES safe-outputs filtering gap and clarify HashSet purpose
- Add note in onees.rs explaining that 1ES target does not support
--enabled-tools filtering (uses service connections, not mcp-http)
- Clarify HashSet comment in generate_enabled_tools_args — it deduplicates
across user keys and ALWAYS_ON_TOOLS, not within HashMap keys
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: correct all_safe_output_names! doc comment and rename variable
- Fix doc comment: example showed two semicolons but macro only accepts
one. Clarify that all types go before the semicolon, string keys after.
- Rename user_tool_count → effective_mcp_tool_count for clarity — it
counts recognized, valid, non-MCP keys, not all user-specified keys.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: promote unmatched enabled-tools log to warn and add disjointness checks
- Promote debug! to warn! for --enabled-tools entries with no matching
route, matching the compile-time warning severity. A typo like
"crate-pull-request" would otherwise be invisible in production logs.
- Add explicit disjointness assertions in test_all_known_completeness
so overlapping lists produce a clear "tool X in both lists" message
rather than a confusing count mismatch.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy file name to clipboardExpand all lines: AGENTS.md
+10Lines changed: 10 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -605,6 +605,16 @@ Should be replaced with the comma-separated domain list for AWF's `--allow-domai
605
605
606
606
The output is formatted as a comma-separated string (e.g., `github.com,*.dev.azure.com,api.github.com`).
607
607
608
+
## {{ enabled_tools_args }}
609
+
610
+
Should be replaced with `--enabled-tools <name>` CLI arguments for the SafeOutputs MCP HTTP server. The tool list is derived from `safe-outputs:` front matter keys plus always-on diagnostic tools (`noop`, `missing-data`, `missing-tool`, `report-incomplete`).
611
+
612
+
When `safe-outputs:` is empty (or omitted), this is replaced with an empty string and all tools remain available (backward compatibility). When non-empty, the replacement includes a trailing space to prevent concatenation with the next positional argument in the shell command.
613
+
614
+
Tool names are validated at compile time:
615
+
- Names must contain only ASCII alphanumerics and hyphens (shell injection prevention)
616
+
- Unrecognized names (not in `ALL_KNOWN_SAFE_OUTPUTS`) emit a warning to catch typos
617
+
608
618
## {{ cancel_previous_builds }}
609
619
610
620
When `triggers.pipeline` is configured, this generates a bash step that cancels any previously queued or in-progress builds of the same pipeline definition. This prevents multiple builds from accumulating when the upstream pipeline triggers rapidly (e.g., multiple PRs merged in quick succession).
0 commit comments