Problem statement
The MCP CLI bridge currently prevents valid zero-argument custom safe-output tools from being called. Every safeoutputs invocation whose parsed arguments are {} is treated as a schema-discovery probe, so the bridge prints per-tool help and returns before issuing MCP tools/call.
This is a runtime regression: the compiler intentionally supports custom safe-output jobs with no inputs and generates an empty object schema for them.
{
"type": "object",
"properties": {},
"additionalProperties": false
}
User impact and use case
Downstream repository: https://github.com/elastic/terraform-provider-elasticstack
The affected workflow uses a custom safe-output tool named dispatch_code_factory. Calling the tool records a safe-output item; a later dispatch_code_factory GitHub Actions job checks for that output type before dispatching code-factory runs for issues created by the agent.
Because the bridge exits after showing help, no item is recorded. The later job's if: condition evaluates false and silently skips the intended dispatches.
Evidence:
The downstream repository is adding a required dummy boolean input as a temporary mitigation. That avoids {} but does not resolve the general runtime contract violation.
Reproduction
Given a generated tool named dispatch_code_factory with the empty schema above, both supported zero-argument forms fail:
$ safeoutputs dispatch_code_factory
[warning] [safeoutputs] No arguments provided for 'dispatch_code_factory'; showing command help instead of calling the tool
$ printf '{}' | safeoutputs dispatch_code_factory .
[warning] [safeoutputs] No arguments provided for 'dispatch_code_factory'; showing command help instead of calling the tool
Expected behavior
Both invocations should proceed through MCP initialization and issue:
{
"method": "tools/call",
"params": {
"name": "dispatch_code_factory",
"arguments": {}
}
}
The safe-output item should then be recorded and the downstream custom job should be eligible to run.
Actual behavior
The bridge invokes showToolHelp, returns with a successful exit, and never sends tools/call.
Agent analysis and execution trace
The failure is in actions/setup/js/mcp_cli_bridge.cjs, not in custom-job schema generation or the MCP server:
main() loads the cached tool definitions.
- It resolves
matchedTool, including matchedTool.inputSchema.
parseToolArgs correctly produces {} for both a bare invocation and piped {} with the . sentinel.
shouldShowToolHelpForEmptyArgs(serverName, toolArgs) currently ignores matchedTool and returns true for every safeoutputs call with {}.
main() displays help and returns before mcpInitialize, mcpNotifyInitialized, and mcpToolsCall.
Current guard:
function shouldShowToolHelpForEmptyArgs(serverName, toolArgs) {
return serverName === SAFEOUTPUTS_SERVER_NAME && Object.keys(toolArgs).length === 0;
}
The server path in actions/setup/js/mcp_server_core.cjs is already schema-aware: empty-argument probe rejection is only relevant when the tool declares required fields. It accepts {} for a schema with no required inputs.
The compiler path in pkg/workflow/safe_outputs_config_generation.go also behaves correctly. generateCustomJobToolDefinition always emits an object schema, populates properties from configured inputs, and only adds required when at least one input is required. A custom job with omitted inputs therefore intentionally produces properties: {} and no required field.
Regression source and affected versions
The behavior was introduced by #43566 and merge commit 1ac3557.
The original change was intended to stop agents repeatedly probing write-once safe-output tools with {}, receiving -32602, and retrying until timeout. During review, schema-aware detection was simplified based on the assertion that safe-output tools always require arguments: #43566 (comment)
That assertion does not hold for custom safe-output jobs.
Version evidence:
| Version |
Status |
setup v0.81.6 / AWF v0.27.11 |
Known good |
v0.82.3 |
First affected release containing the merge commit |
v0.83.4 |
Affected |
v0.84.0 |
Affected |
The known-good lock file already generated an empty schema, confirming this is bridge behavior drift rather than a downstream schema change.
Similar-issue research
Before filing, searches were performed for the regression PR number, shouldShowToolHelpForEmptyArgs, zero-argument safeoutputs, empty-argument safeoutputs, and dispatch_code_factory. No existing gh-aw issue or fix PR describing this exact regression was found. The downstream issue linked above is the only identified report.
Implementation plan
Please have a core-team coding agent implement the following steps in order. Each step identifies the files, behavior, tests, and validation needed for a complete fix.
1. Restore schema-aware probe detection
Update shouldShowToolHelpForEmptyArgs in actions/setup/js/mcp_cli_bridge.cjs to receive the already-resolved matchedTool.
For an empty parsed argument object:
- return false for servers other than
safeoutputs;
- inspect
matchedTool.inputSchema.required;
- show help only when
required is a non-empty array;
- allow zero-input and optional-only tools to proceed with
{}.
Using required rather than the number of properties follows JSON Schema semantics: an optional-only tool can also be validly invoked with {}. Missing or malformed cached schema should not be misclassified as a required-input probe; the MCP server remains authoritative and can return the appropriate protocol error.
Pass matchedTool at the call site in main() and update the JSDoc. Do not remove the existing -32602 non-retry hint added by #43566; the fix should preserve that protection for invalid calls.
2. Add focused bridge tests
Update actions/setup/js/mcp_cli_bridge.test.cjs:
- empty args + explicit empty object schema returns false from the help guard;
- empty args + optional-only schema returns false;
- empty args + non-empty
required returns true;
- non-empty calls and non-
safeoutputs servers remain unchanged;
printf '{}' | safeoutputs dispatch_code_factory . reaches MCP tools/call with arguments: {};
- bare
safeoutputs dispatch_code_factory reaches the same call;
- a normal required-input tool still prints help and does not call MCP.
Use a local in-process HTTP server for the bridge-level tests so the assertions cover the actual initialize → initialized notification → tools/call route without a live external API.
3. Preserve the compiler/runtime contract
Retain the existing no inputs unit case in pkg/workflow/safe_outputs_config_generation_test.go.
Extend pkg/workflow/safe_outputs_tools_meta_integration_test.go with a custom safe-output job whose inputs are omitted. After compilation, parse GH_AW_TOOLS_META_JSON and assert its dynamic_tools entry contains:
- normalized tool name;
type: object;
- empty
properties;
- no
required key;
additionalProperties: false.
Note: explicit inputs: {} is currently rejected by the workflow schema's minProperties: 1; omitted inputs are the supported equivalent and generate the empty MCP schema at issue here.
4. Release metadata
Add a patch changeset describing restoration of valid zero-argument custom safe-output calls. This is a backward-compatible bug fix restoring the compiler/runtime contract, not a CLI breaking change.
No user documentation change should be required: the public configuration is already valid and the defect is in runtime routing. If maintainers want to clarify zero-input custom jobs explicitly, that can be handled as a separate documentation improvement.
5. Validation
The implementing core-team agent should work in the supported Dev Container or Codespace and run the repository-prescribed checks, including make agent-finish, before completing its PR.
Focused checks:
cd actions/setup/js
npm run typecheck
vitest run mcp_cli_bridge.test.cjs --no-file-parallelism
cd ../../..
go test ./pkg/workflow -run TestGenerateCustomJobToolDefinition
go test -tags=integration ./pkg/workflow -run TestToolsMetaJSONCompiledWorkflowEmbedsMeta
Reference implementation and local verification
A reference implementation was built locally at tobio/gh-aw@c159b95877. The issue is intentionally self-contained so a core-team agent can implement or adapt it according to the repository's contribution model.
Checks already completed against that reference implementation:
make build
make lint-cjs
npm run typecheck
- focused bridge suite: 108 tests passed
go test ./pkg/workflow -run TestGenerateCustomJobToolDefinition: 11 tests passed
go test -tags=integration ./pkg/workflow -run TestToolsMetaJSONCompiledWorkflowEmbedsMeta: passed
make agent-report-progress: passed after installing the repository-pinned golangci-lint version
This local verification supplements, but does not replace, the required supported-environment validation by the implementing core-team agent.
Acceptance criteria
Problem statement
The MCP CLI bridge currently prevents valid zero-argument custom safe-output tools from being called. Every
safeoutputsinvocation whose parsed arguments are{}is treated as a schema-discovery probe, so the bridge prints per-tool help and returns before issuing MCPtools/call.This is a runtime regression: the compiler intentionally supports custom safe-output jobs with no inputs and generates an empty object schema for them.
{ "type": "object", "properties": {}, "additionalProperties": false }User impact and use case
Downstream repository: https://github.com/elastic/terraform-provider-elasticstack
The affected workflow uses a custom safe-output tool named
dispatch_code_factory. Calling the tool records a safe-output item; a laterdispatch_code_factoryGitHub Actions job checks for that output type before dispatching code-factory runs for issues created by the agent.Because the bridge exits after showing help, no item is recorded. The later job's
if:condition evaluates false and silently skips the intended dispatches.Evidence:
v0.81.6tov0.83.1: elastic/terraform-provider-elasticstack@6425763The downstream repository is adding a required dummy boolean input as a temporary mitigation. That avoids
{}but does not resolve the general runtime contract violation.Reproduction
Given a generated tool named
dispatch_code_factorywith the empty schema above, both supported zero-argument forms fail:Expected behavior
Both invocations should proceed through MCP initialization and issue:
{ "method": "tools/call", "params": { "name": "dispatch_code_factory", "arguments": {} } }The safe-output item should then be recorded and the downstream custom job should be eligible to run.
Actual behavior
The bridge invokes
showToolHelp, returns with a successful exit, and never sendstools/call.Agent analysis and execution trace
The failure is in
actions/setup/js/mcp_cli_bridge.cjs, not in custom-job schema generation or the MCP server:main()loads the cached tool definitions.matchedTool, includingmatchedTool.inputSchema.parseToolArgscorrectly produces{}for both a bare invocation and piped{}with the.sentinel.shouldShowToolHelpForEmptyArgs(serverName, toolArgs)currently ignoresmatchedTooland returns true for everysafeoutputscall with{}.main()displays help and returns beforemcpInitialize,mcpNotifyInitialized, andmcpToolsCall.Current guard:
The server path in
actions/setup/js/mcp_server_core.cjsis already schema-aware: empty-argument probe rejection is only relevant when the tool declares required fields. It accepts{}for a schema with no required inputs.The compiler path in
pkg/workflow/safe_outputs_config_generation.goalso behaves correctly.generateCustomJobToolDefinitionalways emits an object schema, populatespropertiesfrom configured inputs, and only addsrequiredwhen at least one input is required. A custom job with omitted inputs therefore intentionally producesproperties: {}and norequiredfield.Regression source and affected versions
The behavior was introduced by #43566 and merge commit 1ac3557.
The original change was intended to stop agents repeatedly probing write-once safe-output tools with
{}, receiving-32602, and retrying until timeout. During review, schema-aware detection was simplified based on the assertion that safe-output tools always require arguments: #43566 (comment)That assertion does not hold for custom safe-output jobs.
Version evidence:
v0.81.6/ AWFv0.27.11v0.82.3v0.83.4v0.84.0The known-good lock file already generated an empty schema, confirming this is bridge behavior drift rather than a downstream schema change.
Similar-issue research
Before filing, searches were performed for the regression PR number,
shouldShowToolHelpForEmptyArgs, zero-argument safeoutputs, empty-argument safeoutputs, anddispatch_code_factory. No existing gh-aw issue or fix PR describing this exact regression was found. The downstream issue linked above is the only identified report.Implementation plan
Please have a core-team coding agent implement the following steps in order. Each step identifies the files, behavior, tests, and validation needed for a complete fix.
1. Restore schema-aware probe detection
Update
shouldShowToolHelpForEmptyArgsinactions/setup/js/mcp_cli_bridge.cjsto receive the already-resolvedmatchedTool.For an empty parsed argument object:
safeoutputs;matchedTool.inputSchema.required;requiredis a non-empty array;{}.Using
requiredrather than the number of properties follows JSON Schema semantics: an optional-only tool can also be validly invoked with{}. Missing or malformed cached schema should not be misclassified as a required-input probe; the MCP server remains authoritative and can return the appropriate protocol error.Pass
matchedToolat the call site inmain()and update the JSDoc. Do not remove the existing-32602non-retry hint added by #43566; the fix should preserve that protection for invalid calls.2. Add focused bridge tests
Update
actions/setup/js/mcp_cli_bridge.test.cjs:requiredreturns true;safeoutputsservers remain unchanged;printf '{}' | safeoutputs dispatch_code_factory .reaches MCPtools/callwitharguments: {};safeoutputs dispatch_code_factoryreaches the same call;Use a local in-process HTTP server for the bridge-level tests so the assertions cover the actual initialize → initialized notification →
tools/callroute without a live external API.3. Preserve the compiler/runtime contract
Retain the existing
no inputsunit case inpkg/workflow/safe_outputs_config_generation_test.go.Extend
pkg/workflow/safe_outputs_tools_meta_integration_test.gowith a custom safe-output job whose inputs are omitted. After compilation, parseGH_AW_TOOLS_META_JSONand assert itsdynamic_toolsentry contains:type: object;properties;requiredkey;additionalProperties: false.Note: explicit
inputs: {}is currently rejected by the workflow schema'sminProperties: 1; omitted inputs are the supported equivalent and generate the empty MCP schema at issue here.4. Release metadata
Add a patch changeset describing restoration of valid zero-argument custom safe-output calls. This is a backward-compatible bug fix restoring the compiler/runtime contract, not a CLI breaking change.
No user documentation change should be required: the public configuration is already valid and the defect is in runtime routing. If maintainers want to clarify zero-input custom jobs explicitly, that can be handled as a separate documentation improvement.
5. Validation
The implementing core-team agent should work in the supported Dev Container or Codespace and run the repository-prescribed checks, including
make agent-finish, before completing its PR.Focused checks:
Reference implementation and local verification
A reference implementation was built locally at
tobio/gh-aw@c159b95877. The issue is intentionally self-contained so a core-team agent can implement or adapt it according to the repository's contribution model.Checks already completed against that reference implementation:
make buildmake lint-cjsnpm run typecheckgo test ./pkg/workflow -run TestGenerateCustomJobToolDefinition: 11 tests passedgo test -tags=integration ./pkg/workflow -run TestToolsMetaJSONCompiledWorkflowEmbedsMeta: passedmake agent-report-progress: passed after installing the repository-pinnedgolangci-lintversionThis local verification supplements, but does not replace, the required supported-environment validation by the implementing core-team agent.
Acceptance criteria
tools/callwith{}.{}invocation work for a zero-input custom tool.safeoutputsbehavior remains unchanged.-32602non-retry hint remains intact.make agent-finishpass in the supported development environment.