Blame

19a9ac Claude (MCP) 2026-03-13 19:48:35
[mcp] P2-4: Management API summary
1
---
2
category: reference
3
tags:
4
- phase-2
5
- management-api
6
last_updated: 2026-03-13
7
---
8
9
# P2-4: Management API
10
11
**Branch:** `feat/P2-4-management-api` (from `phase-2`)
12
**Commit:** `1e80a07`
13
**Tests:** 30 passing
14
15
## What was built
16
17
WSGI middleware (`ManagementMiddleware`) that intercepts `/admin/*` requests before they reach the otterwiki Flask app. All routes require platform JWT authentication.
18
19
## Files
20
21
| File | Purpose |
22
|------|---------|
23
| `app/management/routes.py` | WSGI middleware with 8 API endpoints |
24
| `app/management/wiki_init.py` | Git repo init + bootstrap template writer |
25
| `app/management/token.py` | MCP bearer token generation (secrets + bcrypt) |
26
| `app/models/user.py` | Added `get_by_email()` scan method |
27
| `tests/test_management_api.py` | 30 unit tests (moto + tmpdir) |
28
29
## API Endpoints
30
31
| Method | Path | Description |
32
|--------|------|-------------|
33
| POST | `/admin/wikis` | Create wiki (repo init, bootstrap, ACL, token) |
34
| GET | `/admin/wikis` | List user's wikis |
35
| GET | `/admin/wikis/{slug}` | Wiki details + MCP endpoint |
36
| DELETE | `/admin/wikis/{slug}` | Delete wiki (repo, DB records, ACLs) |
37
| POST | `/admin/wikis/{slug}/token` | Regenerate MCP bearer token |
38
| POST | `/admin/wikis/{slug}/acl` | Grant access (body: `{email, role}`) |
39
| DELETE | `/admin/wikis/{slug}/acl/{grantee_id}` | Revoke access |
40
| GET | `/admin/wikis/{slug}/acl` | List ACL entries |
41
42
## Design decisions
43
44
- **WSGI middleware pattern** — wraps the otterwiki app; if `PATH_INFO` starts with `/admin`, handle it; otherwise pass through.
45
- **Plain JSON responses**`json.dumps()` with `Content-Type: application/json`, not Flask.
46
- **Ownership enforcement** — delete, token regen, and ACL grant/revoke require `role=owner` in ACL table.
47
- **Tier limits**`wiki_count >= wiki_limit` check on create; count incremented/decremented atomically.
48
- **Token shown once** — plaintext returned only on create and regenerate; only bcrypt hash stored.
49
- **Repo cleanup on create failure** — if `init_wiki_repo` fails, DB records are rolled back.
50
- **wiki_id format**`{owner_id}:{wiki_slug}` (consistent with ACL model).
51
- **MCP endpoint** — returns `/{slug}/mcp` for now (no username mapping yet).
52
53
## Integration notes
54
55
To wire into the Lambda handler, wrap the otterwiki WSGI app:
56
57
```python
58
from app.management.routes import ManagementMiddleware
59
wrapped = ManagementMiddleware(
60
otterwiki_wsgi_app,
61
auth_middleware=auth_middleware,
62
user_model=users,
63
wiki_model=wikis,
64
acl_model=acls,
65
)
66
handler = make_lambda_handler(wrapped)
67
```
68
69
## Not included (future work)
70
71
- Lambda handler wiring (will be done when all Phase 2 components integrate)
72
- Username-to-subdomain mapping for MCP endpoint URLs
73
- Rate limiting on wiki creation