Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/contracts/mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
"property-priority-parity": "MCP default output follows the shared UI Model Property Priority contract: keep primary identity/task fields and useful disambiguators, compact repeated nested objects, and omit low-priority raw/audit fields unless full mode is explicitly requested.",
"actionable-errors": "Validation and common not-found payloads prefer plain-language messages and may include a hint that points to the next useful tool or query to recover.",
"resource-bound-access-token": "MCP transport requests must present a resource-bound Bearer token for /api/mcp. JWT access tokens minted with the canonical MCP resource are accepted; opaque tokens without a resource indicator are rejected. Omitted-resource refresh grants are normalized to MCP only when the stored grant includes MCP-compatible feature scopes and the canonical MCP resource approval.",
"complete-batch-scope-enforcement": "Every feature scope declared by each requested tool is required across the complete JSON-RPC batch. A feature:write grant also satisfies feature:read for the same feature, while the legacy mcp:tools scope retains compatibility access.",
"transport-observability": "MCP transport handling emits production-safe structured request/response logs and Cloudflare Analytics Engine datapoints with JSON-RPC method summaries, tool names, argument keys, auth/origin phase, status, duration, and registered tool count; it never logs bearer tokens, cookies, or tool argument values.",
"mutation-rate-limits": "After MCP bearer authentication and before SDK tool handling, every actual mutation tools/call entry is checked against the same per-host, per-user, canonical feature-action Cloudflare budgets as REST: 60 standard writes per minute or 10 batch writes per minute. A JSON-RPC batch is rejected as a whole before any tool side effect when any check exceeds the budget (429) or the Cloudflare binding is unavailable (503); both responses include Retry-After: 60.",
"rate-limit-accuracy-boundary": "MCP mutation throttles are per-Cloudflare-location, permissive, eventually consistent abuse controls rather than global exact quotas. The protocol does not promise remaining/reset counters, and read-only tools do not consume mutation budgets.",
"flexible-date-inputs": "Date and datetime parameters on MCP tools accept ISO 8601 with timezone offset (2026-05-01T08:00:00+08:00), bare date strings (2026-05-01, treated as UTC midnight for @db.Date columns), or timezone-less datetimes (2026-05-01T08:00:00, interpreted as Asia/Shanghai). Invalid strings produce a descriptive error response rather than a validation rejection.",
"time-override": "Time-sensitive tools (get_my_7days_timeline, get_upcoming_deadlines, get_my_overview, get_next_buses) accept an optional atTime parameter to anchor their internal clock to a caller-supplied moment instead of the server clock, enabling reproducible queries and future-scenario planning.",
"openai-compatible-descriptors": "Every registered tool advertises a title, a closed top-level object outputSchema with a required success boolean and typed known fields, MCP annotations for read-only/destructive/open-world hints, top-level securitySchemes, and _meta.securitySchemes with the OAuth feature scopes required for that tool. Default and full results keep the same top-level key and collection types.",
Expand All @@ -36,7 +39,7 @@
"returns": "MCP JSON-RPC response",
"notes": [
"All tool calls are handled through this endpoint and require a Bearer token bound to the MCP protected resource /api/mcp; transport handling writes Cloudflare Analytics Engine datapoints for HTTP phase/status/duration, JSON-RPC method counts, and tool-call counts by tool name.",
"Trusted browser-origin Bearer-token clients receive CORS headers, including exposed MCP-Session-Id and WWW-Authenticate response headers for browser transports.",
"Trusted browser-origin Bearer-token clients receive CORS headers, including exposed MCP-Session-Id, Retry-After, and WWW-Authenticate response headers for browser transports.",
"Answers browser CORS preflight requests for the MCP transport headers used by POST/DELETE browser clients.",
"Requests with a foreign Origin header are rejected before MCP auth/tool handling."
]
Expand Down
3 changes: 3 additions & 0 deletions docs/contracts/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
"generated-client-required": "CLI and Bot business calls must use generated OpenAPI clients and generated request/response types whenever the server OpenAPI covers the endpoint. Allowed exceptions are explicit raw request escape hatches, presigned upload object PUTs, OAuth protocol plumbing, external school/QQ/NapCat APIs, and documented non-OpenAPI endpoints such as OAuth userinfo fallback.",
"openapi-drift-gate": "After server OpenAPI changes, regenerate public/openapi.generated.json and sync api/openapi.json plus generated Go clients in CLI and Bot before publishing. Cross-repo CI should fail if copied OpenAPI files drift from the server-generated contract.",
"rest-observability": "REST API handling records production-safe structured request-start/request-finish logs with request ID, method, status, auth mode, duration, and normalized route template; when the Cloudflare Analytics Engine binding is present it also writes sanitized request finish/error datapoints with normalized route, method, status class, auth mode, and duration; it does not log request bodies, credentials, raw query strings, or high-cardinality resource IDs.",
"authenticated-mutation-rate-limits": "Authenticated end-user and admin REST mutations use Cloudflare rate-limit bindings keyed by deployment host, user, and canonical feature action. Standard writes allow 60 checks per minute; batch writes allow 10. REST and MCP share the same feature-action keys. The gate runs after authentication and before mutation. Exceeded budgets return 429; missing or failing Cloudflare bindings fail closed with 503. Both responses include Retry-After: 60. Host-native development bypasses the gate explicitly.",
"dashboard-rate-limit-fallbacks": "For the dashboard pin HTML form, rate-limit rejection skips the write and redirects with an error marker; JSON/API callers receive 429 or 503 directly. Dashboard link visits are best-effort analytics writes: rejection skips click recording but preserves the target redirect.",
"rate-limit-accuracy-boundary": "Mutation throttles are abuse controls, not exact accounting: Cloudflare counters are per-location, permissive, and eventually consistent. Contracts do not promise a global exact quota, remaining count, or reset timestamp. OAuth/provider endpoints retain Better Auth's separate limiter, and anonymous/read-only POST utilities are outside the authenticated mutation budget.",
"unknown-api-json-errors": "Unknown paths under /api return the standard JSON { error } envelope with status 404 instead of falling through to the HTML application error page.",
"pagination-query-parameter": "Paginated REST endpoints use pageSize as the canonical request parameter. The deprecated limit alias remains accepted during migration, and pageSize takes precedence when both are supplied.",
"paginated-list-envelope": "Paginated REST list endpoints return { data, pagination } with page, pageSize, total, and totalPages. Resource-specific list metadata is nested under meta instead of introducing alternate collection keys; REST and equivalent MCP list tools share this core envelope.",
Expand Down
3 changes: 2 additions & 1 deletion docs/contracts/subscription.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
},
"rules": {
"subscription-language": "Copy may use 'subscribe to section' or 'follow section'; must not write 'enroll in course'.",
"user-scoped": "Subscription relationships belong only to the current user and determine the display scope for home page, calendar, homework, and exam."
"user-scoped": "Subscription relationships belong only to the current user and determine the display scope for home page, calendar, homework, and exam.",
"bounded-batch-input": "Calendar subscription query and mutation requests accept at most 500 section IDs and at most 500 lookup codes per request, in addition to the per-user batch request budget."
},
"capabilities": {
"subscribe-from-section": {
Expand Down
Loading