fix: repo_create fails — gitea-mcp token missing write:user scope #33

Closed
opened 2026-05-27 20:23:08 +00:00 by mathias · 1 comment
Owner

Problem

repo_create tool fails with "Token scope doesn't include write:user for repo creation" when called from claude.ai via the gitea-mcp connector.

Root cause

The Gitea API token used by gitea-mcp (stored as GITEA_MCP_DEFAULT_TOKEN in gitea-mcp-secrets) is missing the write:user scope. The Gitea API requires this scope for POST /api/v1/user/repos.

Current token scopes (gitea-mcp token)

From the Applications page (2026-05-14):

  • write:issue
  • write:repository
  • read:user

Missing: write:user

Fix

Option A — add write:user to existing gitea-mcp token (quickest)

  1. gitea.d-ma.be → Settings → Applications → find gitea-mcp token → Delete
  2. Generate new token named gitea-mcp with scopes:
    • write:issue
    • write:repository
    • read:user
    • write:user ← add this
  3. Update in 1Password HomeLab: op item edit GITEA_MCP_DEFAULT_TOKEN --vault HomeLab password=<new-token>
  4. Force ESO sync:
    kubectl annotate externalsecret gitea-mcp-default-token -n gitea-mcp \
      force-sync=$(date +%s) --overwrite
    kubectl rollout restart deployment/gitea-mcp -n gitea-mcp
    

Option B — verify if write:user is actually needed

Check Gitea API docs — POST /api/v1/user/repos may only need write:repository.
The error message may be misleading. Test with a token that has write:repository only.

Recommendation: verify Option B first — if write:repository is sufficient, no token rotation needed.

# Test with current token directly
curl -X POST https://gitea.d-ma.be/api/v1/user/repos \
  -H "Authorization: token $DMABE_GITEA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"test-scope-check","private":true,"auto_init":false}' | jq .message
# Then delete if created:
curl -X DELETE https://gitea.d-ma.be/api/v1/repos/mathias/test-scope-check \
  -H "Authorization: token $DMABE_GITEA_API_TOKEN"

If this works → the gitea-mcp token just needs write:repository added.
If this fails → write:user is genuinely needed.

Impact

Without repo_create working, project_create from claude.ai is broken — the user must manually create repos in Gitea UI before claude.ai can seed files. This defeats the purpose of the automated project initiation flow.

Acceptance criteria

  • Correct required scopes identified (write:repository or write:user)
  • gitea-mcp token updated with correct scopes
  • repo_create callable from claude.ai without error
  • project_create end-to-end works without manual repo creation

Related

  • hyperguild #31 (routing-mcp connector — same workflow gap)
  • gitea-mcp #13 (repo_create original implementation)
  • local-dev #9, #10 (new-project.sh fixes)
## Problem `repo_create` tool fails with "Token scope doesn't include `write:user` for repo creation" when called from claude.ai via the gitea-mcp connector. ## Root cause The Gitea API token used by gitea-mcp (stored as `GITEA_MCP_DEFAULT_TOKEN` in `gitea-mcp-secrets`) is missing the `write:user` scope. The Gitea API requires this scope for `POST /api/v1/user/repos`. ## Current token scopes (gitea-mcp token) From the Applications page (2026-05-14): - `write:issue` - `write:repository` - `read:user` Missing: `write:user` ## Fix ### Option A — add write:user to existing gitea-mcp token (quickest) 1. `gitea.d-ma.be` → Settings → Applications → find `gitea-mcp` token → Delete 2. Generate new token named `gitea-mcp` with scopes: - `write:issue` - `write:repository` - `read:user` - `write:user` ← add this 3. Update in 1Password HomeLab: `op item edit GITEA_MCP_DEFAULT_TOKEN --vault HomeLab password=<new-token>` 4. Force ESO sync: ```bash kubectl annotate externalsecret gitea-mcp-default-token -n gitea-mcp \ force-sync=$(date +%s) --overwrite kubectl rollout restart deployment/gitea-mcp -n gitea-mcp ``` ### Option B — verify if write:user is actually needed Check Gitea API docs — `POST /api/v1/user/repos` may only need `write:repository`. The error message may be misleading. Test with a token that has `write:repository` only. **Recommendation: verify Option B first** — if `write:repository` is sufficient, no token rotation needed. ```bash # Test with current token directly curl -X POST https://gitea.d-ma.be/api/v1/user/repos \ -H "Authorization: token $DMABE_GITEA_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"test-scope-check","private":true,"auto_init":false}' | jq .message # Then delete if created: curl -X DELETE https://gitea.d-ma.be/api/v1/repos/mathias/test-scope-check \ -H "Authorization: token $DMABE_GITEA_API_TOKEN" ``` If this works → the gitea-mcp token just needs `write:repository` added. If this fails → `write:user` is genuinely needed. ## Impact Without `repo_create` working, `project_create` from claude.ai is broken — the user must manually create repos in Gitea UI before claude.ai can seed files. This defeats the purpose of the automated project initiation flow. ## Acceptance criteria - [ ] Correct required scopes identified (write:repository or write:user) - [ ] gitea-mcp token updated with correct scopes - [ ] `repo_create` callable from claude.ai without error - [ ] `project_create` end-to-end works without manual repo creation ## Related - hyperguild #31 (routing-mcp connector — same workflow gap) - gitea-mcp #13 (repo_create original implementation) - local-dev #9, #10 (new-project.sh fixes)
Author
Owner

Verified resolved 2026-07-03 — no longer reproduces. Closing.

Tested the live gitea-mcp connector token end-to-end with throwaway repos (created + deleted):

  • repo_create(mathias/scope-check-tmp, private, auto_init)success (POST /api/v1/user/repos, the exact path that used to 403 on write:user). No scope error.
  • create_project_from_template(mathias/scope-check-tmpl, private)success (POST /api/v1/repos/{tmpl}/generate). Repo created, no scope error.

Both throwaways deleted afterward. So the gitea-mcp token now carries whatever scope POST /user/repos requires (write:user was added per Option A, or the endpoint only needed write:repository which it already had — either way it works now). No code change needed; MapStatus already surfaces the 401/403 body verbatim, so any future scope gap returns the actionable "Token scope doesn't include X" message.

Separate observation (not this issue): the server-configured default template returned partial_failure: "no placeholders substituted ... verify the scaffold is not left templated" — i.e. the default template repo has no __PROJECT_NAME__/__MODULE_PATH__ placeholders, so the loud-check fires on it. Worth a look at what the deployed default template is (or pass template_name=template-go-agent explicitly). Filing separately if it's a real gap.

Verified resolved 2026-07-03 — no longer reproduces. Closing. Tested the live gitea-mcp connector token end-to-end with throwaway repos (created + deleted): - `repo_create(mathias/scope-check-tmp, private, auto_init)` → **success** (`POST /api/v1/user/repos`, the exact path that used to 403 on `write:user`). No scope error. - `create_project_from_template(mathias/scope-check-tmpl, private)` → **success** (`POST /api/v1/repos/{tmpl}/generate`). Repo created, no scope error. Both throwaways deleted afterward. So the gitea-mcp token now carries whatever scope `POST /user/repos` requires (`write:user` was added per Option A, or the endpoint only needed `write:repository` which it already had — either way it works now). No code change needed; `MapStatus` already surfaces the 401/403 body verbatim, so any future scope gap returns the actionable "Token scope doesn't include X" message. **Separate observation (not this issue):** the server-configured *default* template returned `partial_failure: "no placeholders substituted ... verify the scaffold is not left templated"` — i.e. the default template repo has no `__PROJECT_NAME__`/`__MODULE_PATH__` placeholders, so the loud-check fires on it. Worth a look at what the deployed default template is (or pass `template_name=template-go-agent` explicitly). Filing separately if it's a real gap.
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mathias/gitea-mcp#33