Model Context Protocol server for aeman's board operations, providing a 1:1 projection of the /api/v1 resource API for Board/Card/Note resources and actions
The aeman server provides 17 well-named tools with consistently structured definitions and detailed descriptions. All tools follow clear verb_noun naming (get_board, list_cards, create_card, etc.), and most have rich, context-aware descriptions explaining WHAT the tool does, WHEN to use it, and dependencies. Input schemas are present and typed for all visible tools. However, several critical gaps exist: (1) output schemas are not documented, responses are inferred from code rather than declared in tool definitions; (2) parameter descriptions, while present, sometimes assume domain knowledge (e.g., 'aeman project', 'epic column') without explaining to an LLM unfamiliar with the aeman domain what these concepts mean; (3) no enum constraints on status/stage/zone parameters, LLMs must infer valid values from descriptions; (4) error handling guidance is absent, tools do not describe what errors may occur or how to recover; (5) parameter relationships and mutual exclusivity (e.g., parent/week, day/week) are documented in descriptions but not in formal schema constraints. The definitions would benefit from tighter LLM-optimized descriptions (many exceed 200 chars) and explicit output schemas. Despite these gaps, the tool set is well-composed, with clear responsibilities and good parameter documentation.
Add a note to the card — a per-person log entry a person leaves for themselves and the team to see (shareable context; if it is for everyone use the description). Unlike the description (which syncs to the review card), a note stays on its own card. Notes are the bulk of the feed; they pair with mutations to tell what happened and why. Edit and delete are not offered here: notes are immutable history. Timestamp and author are server-set.
Close a review (approve or request changes): delete the review card and flip the original back to In Progress (progress stays as is, stage clears). Closing the review does not delete the review's linked review card's notes — they stay in its feed, and the original's feed shows them too since it links back.
Create a card on a BOARD, and the board says what the card is: view=me (the default) files it on the person you are acting for, for the day, in their sprint and in the UNPLANNED band, which is the only one that board adds in; view=team files it for the team, in the Unassigned column unless you name an assignee, in any band (that board is where planning is done); view=triage schedules it for a WEEK and no day (pass week — a card of the week being worked may carry today's dates too); view=backlog parks it on the team's shelf; view=project files it under a column (pass epic and project). A board refuses the fields it does not own and names the field. Zones are semantic (urgent/unplanned/planned/niceToHave). A title that is just a GitHub issue/PR URL is auto-filled from that issue/PR (its real title, with the link kept in the card description — appended to an explicit one that does not already contain it). Reference links belong in the description, passed at create or later via update_card. Pass parent to create the card as a subtask of an existing one (a subtask takes its parent's band, not the board's); a card cannot be both a subtask and scheduled for a week of its own, so naming a parent and a week together is refused, since grouping hands a subtask's week to its parent.
Output schemas not documented. Tools return structured objects but no schema is declared in tool definitions, response structure must be inferred from code or runtime testing. LLMs cannot plan downstream calls or extract fields without knowing what the response contains.
No enum constraints on categorical parameters. Status values (locked, review, recurrent, refuse, done), zones (urgent, unplanned, planned, niceToHave), stages, and views (me, team, triage, backlog, project, all) are documented in descriptions but not declared as enums in the schema. LLMs must infer valid options from prose, risking invalid calls.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 66 | 2026-07-28+ | v2 |
Delete a card for real, cascading to its linked review card.
Get the board identity, its team roster (metadata.teams) and the Project board's structure: the project roster (metadata.projects), the epic columns with the project each belongs to (metadata.epics), and the weeks carrying a deadline (metadata.deadlines). "Project" here is aeman's planning entity — a group of epic columns — not the GitHub board, which is addressed by owner+board.
Get a single card by uid — the card's DETAIL PANE: the full body (description) plus everything a row shows. This is the follow-up to a list_cards row; pair with list_log for the activity feed and list_links for resolved links. A card whose parent field is set is a subtask of that card; a card can itself have subtasks (its progress then derives from them, and it cannot be done while any subtask is open).
List cards as board ROWS — title, team, zone, assignees, progress, stage, dates, link refs — the same light shape a person sees on the board. Card BODIES are not included: read the one card you act on with get_card (its detail pane), its feed with list_log, its resolved links with list_links. To find a card someone mentioned by name, pass title=<substring> (case-insensitive) — one cheap call resolves it to a uid. With no view it defaults to YOUR own Me board — your own cards in the active sprint — because that is where everyone works and you normally act only on your own cards. Pass view=team&team=X for a team lead's grid of the whole team's cards (the lead view; you usually don't need it and shouldn't edit others' cards unless you're the lead or creating one), view=triage for the weeks work is scheduled in, or view=all for every card on the board. Also filter by stage, semantic zone (urgent/unplanned/planned/niceToHave), assignee or team, and focus=true to keep only cards workable right now (drops done, on-review and locked) — the go-to way to answer "what should I pick up next". Cards can be grouped: a card with a parent field is a subtask riding under that parent (views deliver subtasks alongside their parent, and a parent's progress bar derives from its subtasks).
List the card's resolved link references — GitHub issue/PR titles and states. Links are extracted from the description and resolved on read (no stored link table — they ride the description text and GitHub answers them). Keeping open PR/issue links on the card is encouraged: it is how the team sees what the work is waiting on.
List the card's feed: notes and the mutations (creations, updates, review cycles, assignment changes, stage and date changes, progress bumps — the events that tell the card's story). The feed is ordered newest first; the log is the single source of truth for what happened and when. A review card's feed includes its original's activity — they share the story, and a person reading the review does not miss why the work exists. The identity and login of the author are stamped here.
List the notes on a card — just the per-person log entries the feed puts among mutations. Notes are the bulk of human context.
Show the card in a SECOND Project-board column ({uid, project, epic}): the same card, one file, one log, one set of dates, standing in both projects — so shared work is one card on one person, not a duplicate per project. The card must already be in a column (attach it first via update_card epic/project); the target column must exist and be in the card's own repository. Mirroring where it already stands is a no-op.
Smart-remove a card (the UI's x). A card you are CARRYING but did not create is not yours to take off the board (403, and delete_card the same): your answer to work somebody else planned for you is update_card stage=refuse, which leaves the card standing for your lead. It empties the working area — the sprint and the days the card stands on. With the WEEK it is scheduled for, or a Project-board column (the EPIC side — a bare project name is on no board), to fall back on the card goes there; with nowhere else to be it is DELETED, progress and all — ask the person first when the card carries work. A subtask with nowhere else to be is deleted outright: it has no sprint history of its own. One standing in a project column is not — that column is a home of its own, and the card is released to it. Deleting a parent frees its subtasks into standalone cards rather than destroying them. A card filed under a project column is not deleted here while that column can hold it. One exception: a SUBTASK whose column belongs to its PARENT's repository loses that column when the x pulls it out of the group — its file follows its own team — and is then answered like any other columnless card, demoted or deleted. Deleting deliberately is delete_card.
The Project board's x: remove the card from ONE column ({uid, project, epic}). A mirror simply goes away. Its home column is kept: you cannot remove a card from the only column it stands in — that column is its home, and the card must stay in it (demote/delete instead). A subtask whose home column is in its parent's repository is demoted outright, since it has no independent team to show on.
Request a code review: create a linked review card (a second card standing on its own, with ReviewOf pointing back to the original) and optionally assign it to someone. The review card stands on the same day as the original (both the day the original is on and the day it was started in the sprint, if any), in the team's no-team group, and carries the original's description so reviewers see the full context. The original's stage flips to review. An empty assignee leaves it Unassigned (a lead can pick it up). A card of today that started in a past sprint carries that sprint's start date too, so review pulls that forward — a review card opened in a live sprint for work from a past sprint gets today's dates and today's sprint.
Set a card's progress percentage (0..100). A shorthand for update_card progress=X when the card otherwise stays still. Also sets the Stage field — stage=done when progress=100, stage=review when progress=50–99 (the implicit In Progress), nothing when progress<50.
Take one mirror column away from the card ({uid, project, epic}); its home column and everything else stay.
Patch a card: only the provided fields change, an explicit empty string clears a field; zones are semantic (urgent/unplanned/planned/niceToHave). Use the description field to leave context the whole team should see on the card — it is the card body everyone sees and it live-syncs onto the linked review card. Reference links belong in the description: when the work involves GitHub PRs or issues, DO include their full URLs — free-form text is fine, links are extracted from anywhere in it, surfaced on the card, and GitHub issue/PR links resolve to live titles/states (read them with list_links). Keeping the open PR/issue links on the card is encouraged: it is how the team sees what the card is waiting on. (For shareable context prefer the description over add_note, which is a private per-person log.) Set parent to a card uid to group this card as its subtask (one level deep; the parent's progress bar derives from its subtasks and the parent cannot be done while a subtask is open); an empty parent ungroups it back to standalone. A card's COLUMN must live in the repository that holds the card's file: for a subtask (whose file rides its parent) or a review card (whose file follows its original) that is the linked card's repository, not the column's project — attaching, grouping, ungrouping, re-teaming or linking against that rule is refused. A card cannot be both a subtask and scheduled for a week of its own either: giving a subtask a week is refused (clearing one is free), so ungroup it first.
Domain jargon not explained to unfamiliar LLMs. Terms like 'epic column', 'aeman project', 'zone', 'band', and 'sprint' appear throughout descriptions but lack beginner-friendly definitions. An LLM without domain context cannot reason about these concepts.
No error handling guidance. Tools do not document what errors may occur (e.g., 'Invalid team key', 'Card not found', 'Permission denied', 'Subtask constraint violated') or how to recover. LLMs cannot self-correct when calls fail.
Parameter mutual exclusivity documented informally. Constraints like parent/week exclusivity, day/week/parent conflicts, and column-repository rules are scattered in descriptions rather than formally declared. LLMs may pass invalid parameter combinations.
Descriptions exceed 200 characters for many tools. list_cards and remove_card descriptions are 400+ chars, verbose and inefficient for token budgets. Best practice is 50 - 200 chars: state what it does, when to use it, key constraints.