Skip to content

Add batched update_project_items writes via GraphQL#2903

Draft
veralizeth wants to merge 9 commits into
mainfrom
veralizeth/mcp-batch-bulk-field-value
Draft

Add batched update_project_items writes via GraphQL#2903
veralizeth wants to merge 9 commits into
mainfrom
veralizeth/mcp-batch-bulk-field-value

Conversation

@veralizeth

@veralizeth veralizeth commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds batched field-value writes to update_project_items using chunked, aliased GraphQL mutations with input-ordered succeeded, failed, or unknown results. One required top-level updated_field applies to every item in the batch.

Why

Implements the MCP portion of github/planning-tracking#3331. It builds on the issue-membership pagination merged in #2914; CLI support will follow separately.

What changed

  • Models items[] as three closed oneOf reference variants: node_id, numeric item_id, or item_owner + item_repo + issue_number. The top-level field is a closed ID-or-name variant; null clears it for all items.
  • Resolves the project, shared field, and deduplicated item references before writing. Converts TEXT, NUMBER, DATE, SINGLE_SELECT, and ITERATION values once and rejects duplicate resolved items.
  • Sends one mutation kind in sequential chunks of 20 through the existing authenticated githubv4.Client, with a 100-item call cap and no automatic retries or per-item fallbacks.
  • Preserves confirmed partial successes and marks unconfirmed writes unknown when mutation outcomes are ambiguous.

Review guide

  1. Aliased mutation builder: pkg/github/projects_batch_mutation.go
  2. Field and item resolution: pkg/github/projects_batch_resolve.go and pkg/github/projects_resolver.go
  3. Orchestration and result semantics: pkg/github/projects_batch.go
  4. Handler contract coverage: pkg/github/projects_batch_e2e_test.go

Automated tests are 1,630 of 2,736 added lines; production code is 1,008 and generated output is 98.

MCP impact

  • No tool or API changes
  • Tool schema or behavior changed
  • New tool added

update_project_items adds reference-only batch items, one shared field/value, node_id references, and structured tri-state results. Existing single-item runtime behavior is unchanged.

Prompts tested (tool changes only)

No live prompts were run. Automated handler tests cover equivalent scenarios:

  • Apply one shared update or clear across node, numeric, and paginated issue references, including deduplicated lookups and duplicate-target handling.
  • Update 20 and 21 project items, verifying one and two sequential mutation requests.
  • Preserve partial successes and ordered mixed outcomes across GraphQL errors, transport failures, cancellation, and GHES client wiring.

Security / limits

  • No security or limits impact
  • Auth / permissions considered
  • Data exposure, filtering, or token/size limits considered

The implementation uses the existing injected REST and GraphQL clients and their permissions. The tool remains capped at 100 items; numeric reads use at most five concurrent lookups, and writes remain sequential in chunks of 20 with no automatic retry.

Tool renaming

  • I am renaming tools as part of this PR (e.g. a part of a consolidation effort)
    • I have added the new tool aliases in deprecated_tool_aliases.go
  • I am not renaming tools as part of this PR

Note: if you're renaming tools, you must add the tool aliases. For more information on how to do so, please refer to the official docs.

Lint & tests

  • Linted locally with ./script/lint
  • Tested locally with ./script/test

Focused schema, field conversion/resolution, item resolution, mutation execution, and handler tests also pass.

Docs

  • Not needed
  • Updated (README / docs / examples)

@Swastiksrijan

Copy link
Copy Markdown

go

@zwick zwick self-assigned this Jul 20, 2026
@zwick
zwick force-pushed the veralizeth/mcp-batch-bulk-field-value branch from 15ca2ee to b8f5ca9 Compare July 20, 2026 19:43
@zwick
zwick changed the base branch from main to zwick-paginate-project-item-lookup July 20, 2026 19:43
@zwick zwick changed the title Adding an initial draft of batch/bulk field-value writes Adding an initial draft of batch/bulk field-value writes Jul 20, 2026
@zwick zwick changed the title Adding an initial draft of batch/bulk field-value writes Add update_project_items batch/bulk field-value writes via aliased GraphQL mutations Jul 20, 2026
@zwick
zwick force-pushed the veralizeth/mcp-batch-bulk-field-value branch from 983d5f4 to 047505f Compare July 20, 2026 20:26
Base automatically changed from zwick-paginate-project-item-lookup to main July 21, 2026 08:25
veralizeth and others added 5 commits July 21, 2026 08:48
Extend ResolvedField with the GraphQL node ID alongside the existing
numeric databaseId, and extend the #2914 paginated issue-item resolver
to also return the item's node ID (resolveProjectItemByIssueNumber),
keeping resolveProjectItemIDByIssueNumber as a numeric-only wrapper for
existing callers. These node IDs are what the upcoming aliased
GraphQL mutations need as ItemID/FieldID inputs, in place of the
numeric IDs the REST API takes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d7dc302d-e6f2-41e9-a2c8-ed598de47067
Add projects_batch_mutation.go: builds a runtime struct type
(reflect.StructOf) for sending up to 20 aliased
updateProjectV2ItemFieldValue / clearProjectV2ItemFieldValue mutations
in a single GraphQL request, still via the existing authenticated
*githubv4.Client.

Fields and aliases are purely positional (Item0/item0, Item1/item1,
...); per the pinned githubv4.Client.Mutate signature, the first
input is always sent under the $input variable, so aliases 1+
reference $input1, $input2, ... via the variables map. Reflected
types are cached by (operation kind, chunk size) only, never by
request data.

This file is not yet wired up to any caller.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d7dc302d-e6f2-41e9-a2c8-ed598de47067
Rewire update_project_items to resolve the project, every distinct
field, and every item once up front, then write via the
projects_batch_mutation.go aliased-mutation builder, chunked into
sequential requests of 20, instead of one REST PATCH per item.

- projects_batch_resolve.go: field/item resolution. Adds node_id as a
  third item-reference form (alongside item_id and
  item_owner+item_repo+issue_number, exactly one required per item),
  bypassing REST lookup entirely. Numeric item_id values are
  deduplicated and resolved to node IDs via bounded-concurrency REST
  GETs; issue refs are deduplicated and resolved via the paginated
  resolver from the previous commit. updated_field.value is converted
  to the matching ProjectV2FieldValue member for TEXT, NUMBER, DATE,
  SINGLE_SELECT, and ITERATION; null routes to the clear mutation.
- projects_batch.go: orchestration. Rejects duplicate item+field
  targets before any writes, partitions into update/clear chunks, and
  tracks tri-state per-item results (succeeded/failed/unknown). After
  an ambiguous transport-level failure or context cancellation, no
  further chunks are sent.
- projects.go: removed the old REST-loop implementation and its
  helpers; added node_id to the update_project_items item schema.
- Removed the stale comment claiming the pinned client couldn't build
  dynamic aliases.
- Updated/added tests accordingly, including toolsnap and README
  regeneration for the schema change.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d7dc302d-e6f2-41e9-a2c8-ed598de47067
Distinguish GraphQL response errors from ambiguous transport or missing-data responses, validate numeric references before narrowing them, reuse the resolved project ID for issue lookups, and cache field-load failures. Add focused regression coverage and align the tool schema with the supported issue-reference contract.\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>\nCopilot-Session: d7dc302d-e6f2-41e9-a2c8-ed598de47067
@zwick
zwick force-pushed the veralizeth/mcp-batch-bulk-field-value branch from 5ff24d6 to 669974c Compare July 21, 2026 12:50
Cover paginated node-ID capture, deduplicated issue resolution, post-resolution duplicate detection, ambiguous field names, and top-level guards.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7ae767ff-c1d0-46a9-b126-2e91403993a0
@zwick zwick changed the title Add update_project_items batch/bulk field-value writes via aliased GraphQL mutations Add batched update_project_items writes via GraphQL Jul 21, 2026
Model node, numeric, and issue references as closed schema variants while retaining runtime validation.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7ae767ff-c1d0-46a9-b126-2e91403993a0
@zwick
zwick force-pushed the veralizeth/mcp-batch-bulk-field-value branch 2 times, most recently from 38df97f to ad9f0e4 Compare July 21, 2026 14:00
Remove redundant implementation narration while retaining comments for non-obvious client and failure semantics.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: d7dc302d-e6f2-41e9-a2c8-ed598de47067
@zwick
zwick force-pushed the veralizeth/mcp-batch-bulk-field-value branch from ad9f0e4 to 85ec9ef Compare July 21, 2026 14:04
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7ae767ff-c1d0-46a9-b126-2e91403993a0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants