Django-based MCP server for student CRM operations with capability-based access control
dj-mcp demonstrates solid fundamentals: all 4 tools have clear verb-noun naming (lookup_, get_, list_, add_), comprehensive descriptions (100-200 chars, well above 20-char minimum), and complete input schemas with type definitions and constraints. Pydantic validation is present. However, output schemas are not formally documented in the code, only inferred from ToolResult.json() calls. Error handling is basic (returns ToolResult.error() with messages) but lacks recovery guidance or error classification. No tool annotations (readOnlyHint/destructiveHint) despite clear read/write semantics. Tenant isolation is correctly enforced server-side, preventing agent bypass. Parameter descriptions are solid (e.g., 'exact' vs 'partial' matching, health_score_below threshold), but some lack format hints (e.g., date ranges, ISO 8601 expectations). Overall: good naming and descriptions, complete schemas, but missing output documentation and error recovery patterns.
Add a note to a student's record. Use to log context from a conversation, flag a risk, or record an action taken.
Retrieve usage metrics for a specific student over a date range. Returns API calls, feature adoption, active users, and trend. Use for health assessments and renewal conversations.
Returns students with low health scores or declining usage. Results sorted by risk severity — highest risk first. Use to identify accounts needing proactive outreach.
Look up a student by name or email. Returns student info, enrollment date, and academic status. Call this before making decisions about a student.
Output schemas not formally documented. ToolResult.json() calls exist but return structure is inferred, not declared. LLMs cannot plan downstream tool calls without knowing what fields to expect.
No tool annotations (readOnlyHint, destructiveHint, idempotentHint). add_student_note is clearly destructive (WRITE risk), but this is not signaled in the schema. Agents cannot distinguish safe retry candidates from irreversible operations.
Error responses lack recovery guidance. ToolResult.error() returns plain messages ('Student not found in your account') but does not suggest next steps (e.g., 'Try lookup_student() first') or classify errors as retryable vs user-fixable.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | B | 79 | 2026-07-28+ | v2 |
Parameter descriptions lack format hints. 'Days to look back' does not specify ISO 8601 or natural-language date format expectations. 'Note content' does not clarify if markdown, plain text, or structured format is expected.
No batch variants. If an agent needs to add notes to 10 at-risk students, it must call add_student_note 10 times sequentially, wasting tokens and latency. A batch_add_student_notes(student_ids: list, note: str) would be more efficient.