FastMCP server for Kanban board management projected live from ~/cortex/docs/tickets/*.md files. Provides read/write tools for ticket management across multiple columns (backlog, new, inprogress, testing, done) and SYSTEMSCANN board management. Includes REST API via FastAPI and Ionic Angular web UI.
Cortex Board has clear, action-oriented tool names (get_board, add_ticket, move_ticket) and comprehensive descriptions (avg 180 chars, well above 10-char minimum). All 9 tools have descriptions. However, input schemas are incomplete: most parameters lack explicit type definitions in the visible code. Parameter descriptions are present but generic (e.g., 'Ticket ID (e.g., T-NN)' without format constraints). Output schemas are not documented, LLMs cannot predict return structure. Error handling is minimal (basic try/except with generic error dicts). No tool annotations (readOnlyHint, destructiveHint) despite clear read/write/destructive risk profiles. Composition is sound: tools are single-responsibility, names are verb-noun, and chaining is feasible (e.g., add_ticket returns id for move_ticket). Security: no secrets in params, but no audit trail or permission gates visible.
Create a new SYSTEMSCANN ticket as SC-NN_slug.md in ~/cortex/docs/scan-tickets. The file gets `# SC-NN — <title>`, `**Status:** new`, and optional sections `## Kontext` and `## Next`. The id is the lowest free SC-NN (SC-00 reserved for INDEX). Returns {id, title, path, column}.
Create a new ticket as a fresh T-NN_slug.md in ~/cortex/docs/tickets. The new file gets `# T-NN — <title>`, `**Status:** new`, and the body sections `## Kontext` (from `description`) and `## Next` (from `next_step`) if those are non-empty. The id is the lowest free T-NN across active and archive. Returns {id, title, path, column}.
Read the whole Kanban board, projected live from ~/cortex/docs/tickets. Returns every column (backlog, new, inprogress, testing, done) with its current `rev` (content hash), ticket count and tickets. Each card carries `id` (T-NN / WD-NN) and `title` (`<id> — <heading title>`). Read-only.
Read one column -> {column, rev, count, tickets}. Live from docs/tickets. `column` must be one of: backlog, new, inprogress, testing, done.
Read the whole SYSTEMSCANN board, projected live from ~/cortex/docs/scan-tickets. Returns all 3 columns (new, open, resolved) with rev hash, count and tickets. Each SC-NN card carries `id`, `title`, `description`, and `next_step`. Read-only.
Input schemas lack explicit type definitions. Parameters are declared but JSON Schema types (string, integer, boolean, enum) are not visible in the provided code. This forces LLMs to infer types from descriptions alone, increasing hallucination risk.
Output schemas are not documented. Tools return dicts (e.g., get_board returns {column, rev, count, tickets}) but the structure of nested fields is not specified. LLMs cannot plan downstream calls or extract data reliably.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 54 | <=2025-11-25 | v2 |
Move a SYSTEMSCANN ticket to another column by rewriting its `**Status:**` line. `ticket_id` must be a SC-NN id. `to_column` is one of: new, open, resolved.
Move a ticket to another column by rewriting its `**Status:**` line. `to_column` is one of: backlog (parks the ticket), new, inprogress, testing, done. The canonical status word written into the file is `new` / `in_progress` / `testing` / `done` / `parked`.
Delete a ticket .md file. For done items prefer `git mv` to archive/<YYYY-MM>/ (Cortex Daily convention) — this tool removes outright.
Edit an existing ticket's H1 heading (the title). Description and next_step are kept as the long-form body — for those, hand-edit the .md (that is what the .md is for).
No tool annotations (readOnlyHint, destructiveHint, idempotentHint). Tools are marked with Risk labels in the spec (READ_ONLY, WRITE, DESTRUCTIVE) but these are not exposed to the MCP protocol. LLMs cannot distinguish safe reads from destructive writes without reading descriptions.
Parameter descriptions lack format constraints. 'column' accepts one of {backlog, new, inprogress, testing, done} but this is stated in prose, not as an enum. LLMs may hallucinate invalid values like 'pending' or 'archived'.
Error handling is generic. Errors return {ok: False, error: str(e)} with no recovery guidance. E.g., 'unknown column' does not suggest valid columns or next steps. Per pattern:recovery-guide, errors should guide the LLM toward resolution.