One file, 29 tools, every one acting with the caller's own Gitea token. Parameters are typed (patterns, ranges, enums), so FastMCP refuses a bad argument before Gitea is asked anything and publishes the limits in each tool's schema. New: tea_pull_get/create/review/merge, tea_labels and tea_label, tea_commit (up to 50 files, text or base64), tea_branch_create, tea_repo_edit (including archived), tea_hook_create (one per URL), tea_secret_set and tea_variable_set. Fixed: - unknown labels are refused instead of silently dropped - a file update is chosen from a 404 on the path, not from matching "404" in an error message; paths with spaces are percent-encoded - tea_org_add_member names its team; there is no default - org repositories are private by default, like user repositories - "more" and "truncated" come from X-Total-Count, so they are true - tea_issue_comments pages client-side: Gitea's endpoint is unpaged - tea_actions reads /actions/tasks: 1.24 has no /actions/runs, so the old tool always got a 404 - a Gitea that does not answer is named as such instead of a masked error Tests run against a fake Gitea (27). The same code was run against teahub.xyz with a temporary token in scratch repositories that were then deleted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CYHXFvWMCs1GUmRmpySqQ6
Teahub MCP
Teahub's MCP (Fly app green-tea-mcp, https://mcp.teahub.xyz/mcp) against the Gitea at GITEA_URL. Sign-in is Gitea's OAuth (Gitea signs people in with GitHub or nostr), and every tool acts with the caller's own Gitea token: no service token, no allowlist, nothing that outruns the caller's own permissions. Reads and writes are bounded, and no tool deletes a repository, branch, file, issue or comment.
Env: GITEA_URL, PUBLIC_URL, ALLOWED_HOSTS; secrets GITEA_OAUTH_CLIENT_ID, GITEA_OAUTH_CLIENT_SECRET (an OAuth2 application on the Gitea with redirect PUBLIC_URL/auth/callback), JWT_SIGNING_KEY. State: /data (FastMCP's OAuth store).
Tools (29), all acting as you. Their parameters are typed, so a bad argument is refused before Gitea is asked anything.
- Issues and pull requests:
tea_issues,tea_issue_get/create/edit,tea_issue_comments/comment,tea_labels,tea_label(add or remove by name),tea_pull_get(branches, mergeability, checks, diff),tea_pull_create,tea_pull_review,tea_pull_merge(keeps the head branch). - Repositories and organisations:
tea_whoami,tea_repo,tea_repo_edit(description, website, visibility, default branch, archived),tea_repo_create,tea_org_create,tea_org_repo_create,tea_org_add_member(name the team),tea_hook_create(one hook per URL). - Files:
tea_tree,tea_file_get,tea_file_change,tea_commit(up to 50 files, text or base64, 3 MB in all),tea_branch_create. - Actions:
tea_actions(recent jobs, each linking its run),tea_workflow_dispatch,tea_secret_set(never echoed),tea_variable_set.
New repositories are private unless asked otherwise, and labels must already exist. Gitea 1.24 has no API that lists a run's jobs, so there is no log tool; tea_actions links each run's page, where the logs are.
Tests: from mcp/, python -m unittest discover -s tests (a fake Gitea; needs requirements.txt).