[safeoutputs] Strengthen anti-probing guidance for add_comment and create_issue#33433
Conversation
…nd create_issue Agent-Logs-Url: https://github.com/github/gh-aw/sessions/d13e7327-53d9-468b-98e0-5befd6f65c9e Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR strengthens “write-once / no schema-probing” guidance for safe-outputs tools and adds an explicit MCP-server error path for completely empty {} tool calls (to discourage schema discovery probes).
Changes:
- Hoists “WRITE-ONCE: don’t probe with empty/placeholder args” to the first sentence of
create_issue/add_commenttool descriptions (both tool JSON copies), and clarifies thattitle/bodymust be final intended values. - Adds a distinct
-32602error response for tool calls that provide a completely empty{}arguments object when required fields exist, plus updates/extends unit tests for this behavior. - Modifies the Datadog MCP headers interpolation in
.github/workflows/smoke-otel-backends.lock.yml(appears unrelated to the PR goal).
Show a summary per file
| File | Description |
|---|---|
| pkg/workflow/js/safe_outputs_tools.json | Strengthens write-once / anti-probing messaging and field guidance for create_issue and add_comment. |
| actions/setup/js/safe_outputs_tools.json | Same safe-outputs tool description updates in the setup copy. |
| actions/setup/js/mcp_server_core.cjs | Adds special-case error messaging for empty {} arguments when required fields exist. |
| actions/setup/js/mcp_server_core.test.cjs | Updates/adds tests for the new empty-args probe-detection error path and the partial-args enhanced-error path. |
| .github/workflows/smoke-otel-backends.lock.yml | Changes Datadog header value interpolation (scope mismatch with PR description). |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comments suppressed due to low confidence (1)
actions/setup/js/mcp_server_core.cjs:769
- Same issue as the HTTP path: probe-detection currently applies to every tool with required fields and emits a write-once/
noop-specific message. SincehandleMessageis shared by servers beyond safe-outputs, please gate this behavior to explicitly write-once tools (or detect presence of anooptool) to avoid misleading errors for other MCP servers.
const missing = validateRequiredFields(args, tool.inputSchema);
if (missing.length) {
const hasRequiredFields = tool.inputSchema && Array.isArray(tool.inputSchema.required) && tool.inputSchema.required.length > 0;
if (hasRequiredFields && Object.keys(args).length === 0) {
server.replyError(
id,
-32602,
`Empty arguments are not allowed — this tool is write-once, not a discovery probe. To inspect the schema, use the tools/list MCP method. To signal that no action is needed, call \`noop\` with a \`message\`.`
);
return;
}
- Files reviewed: 5/5 changed files
- Comments generated: 2
| "url": "https://mcp.datadoghq.com/api/unstable/mcp-server/mcp?toolsets=core", | ||
| "headers": { | ||
| "DD_API_KEY": "\${DD_API_KEY}", | ||
| "DD_APPLICATION_KEY": "\${DD_APPLICATION_KEY}", | ||
| "DD_APPLICATION_KEY": "\${DD_APP_KEY}", | ||
| "DD_SITE": "\${DD_SITE}" |
| const missing = validateRequiredFields(args, tool.inputSchema); | ||
| if (missing.length) { | ||
| const hasRequiredFields = tool.inputSchema && Array.isArray(tool.inputSchema.required) && tool.inputSchema.required.length > 0; | ||
| if (hasRequiredFields && Object.keys(args).length === 0) { | ||
| throw { | ||
| code: -32602, | ||
| message: `Empty arguments are not allowed — this tool is write-once, not a discovery probe. To inspect the schema, use the tools/list MCP method. To signal that no action is needed, call \`noop\` with a \`message\`.`, | ||
| }; | ||
| } |
…tection Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Fixed in the latest commit. |
Gemini was calling
add_commentandcreate_issuewith empty{}arguments as schema discovery probes before making real calls — wasting turns and violating the explicit write-once contract. The anti-probing warning existed but was buried mid-description.Tool description changes (
safe_outputs_tools.json, both copies)WRITE-ONCE:warning to first sentence ofcreate_issueandadd_commentdescriptions so agents encounter it before the purpose blurbtitleandbodyfield descriptionsBefore:
After:
Probe detection in MCP server (
mcp_server_core.cjs)Added a distinct error path in both
handleRequestandhandleMessagewhen a tool with required fields receives a completely empty{}argument object:Previously, an empty call fell through to the generic field-level "missing or empty
title,body" error, which gave no signal that probing was the problem. Partial args (some fields present but invalid) still follow the existing enhanced-error path.Tests
mcp_server_coretest for empty-args case to assert the new probe-detection messagemissing or emptyenhanced errorWarning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
https://api.github.com/graphql/usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw GOMOD GOMODCACHE go /vit�� -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go(http block)/usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go(http block)/usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw GOMOD GOMODCACHE go env -json GO111MODULE _modules/.bin/sh GOINSECURE GOMOD GOMODCACHE go(http block)https://api.github.com/repos/actions/checkout/git/ref/tags/v6/usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq [.object.sha, .object.type] | @tsv rror GO111MODULE de GOINSECURE GOMOD GOMODCACHE go /vit�� -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go(http block)/usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq [.object.sha, .object.type] | @tsv .js' --ignore-paname GO111MODULE ache/node/24.14.1/x64/bin/node GOINSECURE GOMOD GOMODCACHE ache/node/24.14.-f ndle�� -json GO111MODULE n-advisor.lock.y-f GOINSECURE GOMOD GOMODCACHE node(http block)https://api.github.com/repos/actions/download-artifact/git/ref/tags/v8/usr/bin/gh gh api /repos/actions/download-artifact/git/ref/tags/v8 --jq [.object.sha, .object.type] | @tsv quest tions/setup/js/mcp_server_core.cjs tions/setup/node_modules/.bin/node GOINSECURE GOMOD GOMODCACHE go tion�� -json GO111MODULE l GOINSECURE GOMOD GOMODCACHE go(http block)https://api.github.com/repos/actions/github-script/git/ref/tags/v9/usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v9 --jq [.object.sha, .object.type] | @tsv rror GO111MODULE /vitest GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go(http block)/usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v9 --jq [.object.sha, .object.type] | @tsv rror GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go /vit�� -json GO111MODULE node GOINSECURE GOMOD GOMODCACHE go(http block)https://api.github.com/repos/actions/setup-go/git/ref/tags/-/usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/- --jq [.object.sha, .object.type] | @tsv -json GO111MODULE bin/bash GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE repository(owner: $owner, name:-f GOINSECURE GOMOD GOMODCACHE go(http block)https://api.github.com/repos/actions/setup-node/git/ref/tags/v6/usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v6 --jq [.object.sha, .object.type] | @tsv rror GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go /vit�� -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go(http block)/usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v6 --jq [.object.sha, .object.type] | @tsv k && vitest run name GO111MODULE ache/node/24.14.1/x64/bin/node GOINSECURE GOMOD GOMODCACHE ache/node/24.14.config ndle�� --noprofile GOPROXY k/gh-aw/gh-aw/node_modules/.bin/node GOSUMDB GOWORK 64/bin/go node(http block)https://api.github.com/repos/docker/metadata-action/git/ref/tags/v6/usr/bin/gh gh api /repos/docker/metadata-action/git/ref/tags/v6 --jq [.object.sha, .object.type] | @tsv -json GO111MODULE repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } GOINSECURE GOMOD GOMODCACHE go env '**/*.ts' '**/*.json' --ignore-premote.upstream.url GO111MODULE ndlers-ChiiVk/env_handler.sh GOINSECURE GOMOD GOMODCACHE ndlers-ChiiVk/enshow(http block)https://api.github.com/repos/docker/setup-buildx-action/git/ref/tags/v4/usr/bin/gh gh api /repos/docker/setup-buildx-action/git/ref/tags/v4 --jq [.object.sha, .object.type] | @tsv -json GO111MODULE 64/bin/node GOINSECURE GOMOD GOMODCACHE go 1/x6�� k && vitest run remote.upstream.url GO111MODULE n-dir/node GOINSECURE GOMOD GOMODCACHE ndlers-ExvlRq/muconfig(http block)https://api.github.com/repos/github/gh-aw/usr/bin/gh gh api /repos/github/gh-aw --jq .default_branch json GO111MODULE rgo/bin/bash GOINSECURE GOMOD GOMODCACHE head -20 ithub/workflows GOPROXY mance.lock.yml GOSUMDB GOWORK 64/bin/go sh(http block)If you need me to access, download, or install something from one of these locations, you can either: