forgejo-mcp/openspec/changes/add-org-management/design.md
Christoph Görn 2d540a5b60
docs: ✨ add OpenSpec artifacts for organization management feature
Proposal, design, specs, and tasks for adding 15 MCP tools covering
org CRUD, membership, and team operations. Tracks issue #92.
2026-03-18 16:07:35 +01:00

69 lines
4.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## Context
Forgejo MCP currently supports six operational domains: user, repo, issue, pull, search, actions, and version. Each domain follows the same pattern:
1. A package under `operation/<domain>/` defines tools via `mcp.NewTool()` and handler functions
2. A `RegisterTool(s *server.MCPServer)` function wires them to the MCP server
3. `operation/operation.go` calls each domain's registration
4. `cmd/cli.go` mirrors the same registration with domain tagging for `--cli list` grouping
The Forgejo Go SDK (`codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2`) provides full organization API coverage. The singleton client in `pkg/forgejo/` is already used by all domains.
Shared parameter descriptions live in `operation/params/params.go` — the `Org` and `User` constants already exist.
## Goals / Non-Goals
**Goals:**
- Add 15 MCP tools for organization management (CRUD, membership, teams)
- Follow existing domain patterns exactly — no new abstractions
- Available via both MCP and CLI with zero extra CLI wiring (CLI dispatches to MCP handlers)
- Consistent parameter naming and response formatting with existing tools
**Non-Goals:**
- Admin-only endpoints (`AdminCreateOrg`, `AdminListOrgs`) — these require admin tokens and are a different use case
- Organization webhooks — complex configuration, separate feature
- Organization Actions secrets/variables — already partially covered by the actions domain
- Organization avatar management — low priority, not in SDK as structured API
- Organization labels — blocked on SDK support
## Decisions
### 1. Single `operation/org/` package with file-per-concern
Three files: `org.go` (CRUD), `membership.go` (membership), `teams.go` (teams). One `RegisterTool()` entry point.
**Why over a flat single file**: 15 tools × ~50 lines each = ~750 lines. Splitting by concern matches `operation/repo/` which splits into `repo.go`, `file.go`, `branch.go`, `commit.go`.
### 2. Tool naming: `{verb}_{noun}` with `org` prefix
Names: `create_org`, `get_org`, `list_my_orgs`, `list_user_orgs`, `edit_org`, `delete_org`, `list_org_members`, `check_org_membership`, `remove_org_member`, `list_org_teams`, `create_org_team`, `add_team_member`, `remove_team_member`, `add_team_repo`, `remove_team_repo`.
**Why**: Matches existing conventions (`create_repo`, `fork_repo`, `list_my_repos`). The `org` prefix disambiguates from user-level operations.
### 3. Team operations use team ID (int64) for mutations, org name for listing
`list_org_teams` takes `org` (string). `add_team_member`, `remove_team_member`, `add_team_repo`, `remove_team_repo` take `team_id` (number) — this matches the SDK which uses `int64` team IDs for these operations.
**Why over name-based lookup**: The SDK methods (`AddTeamMember(id, user)`, `AddTeamRepository(id, org, repo)`) use numeric IDs directly. Adding a name→ID lookup would require an extra API call and introduce ambiguity (team names aren't unique across orgs).
### 4. Reuse `pkg/to` for response formatting
All handlers return `to.TextResult(obj)` for success and `to.ErrorResult(err)` for errors, matching every existing tool.
### 5. Pagination on list endpoints
`list_my_orgs`, `list_user_orgs`, `list_org_members`, `list_org_teams` accept `page` and `limit` parameters using the existing `params.Page` / `params.Limit` descriptions and the same defaults (page=1, limit=100).
### 6. Visibility as string enum
`create_org` and `edit_org` accept `visibility` as a string: `"public"`, `"limited"`, or `"private"`. The SDK's `VisibleType` is a string alias, so no conversion is needed.
## Risks / Trade-offs
**[Risk] Delete org is destructive** → Tool description will include a clear warning. No confirmation mechanism exists in MCP protocol, so the tool description must convey severity. This matches the existing `delete_branch` and `delete_file` patterns.
**[Risk] Team ID discovery requires two steps** → Users must call `list_org_teams` to find a team ID before using team mutation tools. This is inherent to the SDK design and documented in tool descriptions. Alternative (name-based lookup) was rejected per Decision 3.
**[Trade-off] No admin endpoints** → Users with admin tokens cannot create orgs on behalf of other users. This keeps the scope focused and avoids requiring admin-level permissions for the basic use case. Can be added later as a separate feature.
**[Trade-off] No webhook/actions/label management** → Keeps the initial scope manageable (15 tools). These can be added incrementally in future changes.