feat: add the BM student MCP tools

- token twins of the student API under /_bm/api/v1/token (user.api_token,
  per-user jz.bm.token bucket, a scope on each), from one mountStudent
- pact.HasMCP with the eleven student tools over the twins
- TestBMMCP and TestTokenMirrorRoutes
This commit is contained in:
2026-10-28 14:32:03 +01:00
parent 65a9a683af
commit e106b36a9a
5 changed files with 490 additions and 21 deletions

View File

@@ -119,6 +119,19 @@ func TestRoutes(t *testing.T) {
"PUT /_bm/api/v1/calendar-feed",
"POST /_bm/api/v1/calendar-feed/rotate",
"DELETE /_bm/api/v1/calendar-feed",
"GET /_bm/api/v1/token/dashboard",
"GET /_bm/api/v1/token/courses",
"GET /_bm/api/v1/token/courses/{id}",
"GET /_bm/api/v1/token/schedule",
"GET /_bm/api/v1/token/meetings",
"GET /_bm/api/v1/token/deadlines",
"GET /_bm/api/v1/token/readings",
"PUT /_bm/api/v1/token/readings/{id}/progress",
"DELETE /_bm/api/v1/token/readings/{id}/progress",
"PUT /_bm/api/v1/token/readings/{id}/owned",
"DELETE /_bm/api/v1/token/readings/{id}/owned",
"GET /_bm/api/v1/token/materials",
"GET /_bm/api/v1/token/materials/{id}",
"GET /api/v1/bm/me",
"GET /api/v1/bm/materials",
"GET /api/v1/bm/materials/{id}",
@@ -176,7 +189,13 @@ func TestRoutes(t *testing.T) {
continue
}
got = append(got, rt.Method+" "+rt.Pattern)
if strings.HasPrefix(rt.Pattern, "/api/v1/bm/") {
if strings.HasPrefix(rt.Pattern, bm.TokenPrefix+"/") {
// The token twins of the student API: the user API token guard
// and a scope instead of jwt.auth.
if !slices.Contains(rt.Middleware, "user.api_token") || !slices.ContainsFunc(rt.Middleware, func(name string) bool { return strings.HasPrefix(name, "user.scope:") }) || slices.Contains(rt.Middleware, "jwt.auth") {
t.Errorf("%s %s is not behind the user API token and a scope: %v", rt.Method, rt.Pattern, rt.Middleware)
}
} else if strings.HasPrefix(rt.Pattern, "/api/v1/bm/") {
if !slices.Contains(rt.Middleware, "user.api_token") || !slices.ContainsFunc(rt.Middleware, func(name string) bool { return strings.HasPrefix(name, "user.scope:") }) {
t.Errorf("%s %s is not behind the shared token and a scope: %v", rt.Method, rt.Pattern, rt.Middleware)
}