Appearance
date: 2026-06-08 tags: [github, mcp, projects-v2, web-env, automation] status: active graduated_to:
GitHub MCP expired? $GH_TOKEN curl still writes — and Projects v2 field-sets need the issue on the board first
Symptom — mid-session the GitHub MCP server returned "requires re-authorization (token expired)", so mcp__github__* reads/writes failed. A whole backlog reshape (milestones, sub-issues, types, triage Status, labels) still needed doing.
Root cause — the MCP's OAuth and the ambient $GH_TOKEN are independent credentials. The MCP expiring doesn't revoke $GH_TOKEN, which (here) has repo admin — so REST + GraphQL via curl kept working for the whole reshape.
Fix / how — drive GitHub directly with curl -H "Authorization: Bearer $GH_TOKEN":
- milestone / native type:
PATCH /issues/{n}{"milestone":N,"type":"Task"} - sub-issue link / reparent:
POST /issues/{parent}/sub_issues {"sub_issue_id":<child DB id>}(remove:DELETE …/sub_issue) - labels:
POST /issues/{n}/labels - Projects v2 Status: GraphQL
updateProjectV2ItemFieldValue(see2026-06-06-projects-v2-status-via-graphql.md).
Gotcha that bit: an issue must be on the board before its Status can be set — only 36 of 88 Foundational issues were. addProjectV2ItemById(projectId, contentId:<node_id>) is idempotent (returns the existing item if already added), so the safe pattern is add-then-set for every issue, unconditionally.
Guard — convention, no test. When the GitHub MCP is unavailable in the web env, fall back to $GH_TOKEN curl rather than declaring GitHub unreachable; and always addProjectV2ItemById before an updateProjectV2ItemFieldValue. (Caveat: $GH_TOKEN scope can still vary — it 403'd on the checks API this session even while issues/projects writes worked.)