# Umtri [Health: Active]

**Category:** 💻 Developer Tools  
**Repository:** https://github.com/bepuljang/umtri-mcp  
**GitHub Stars:** 0  
**npm Downloads (last month):** 1506  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/umtri

## Description
Give your AI agent a persistent map of your project's structure, dependencies, and bugs.

## Tools
Capabilities this server exposes over MCP:

- **list_projects** — Returns all grounds the authenticated user can access, with latest activity timestamp. Use this first to discover slugs for other tools. By default returns a summary view — each ground carries slug, name, isActive, transplanting, nodeCount, openBugs, seasonCount, nowSeasonLabel, latestActivityAt, plus a 200-char description excerpt. A ground that belongs to a team also carries org (the team slug) and role (owner/editor/reporter/viewer) — a viewer can read but every write tool returns 403 there; a reporter can only create_bug and update_bug. A ground with wiki pages also carries wikiCount — prose the tree cannot hold (conventions, decisions, glossary); read it with list_wiki. Pass view="full" for all fields (seedMeta — the ground's human-written intent: goal, endDate, budget, audience — plus typeCounts, raw metadata, timestamps); the icon base64 in metadata is always stripped (UI-only).
- **get_graph** — Returns the tree(nodes), edges, apis, seasons, and bugs for one ground. The tree is the project's information-structure (IA) diagram — each node is a structural element (an object, or an action it performs), not a work-log entry. Node types follow the plant metaphor: trunk → limb → twig → leaf → vein. See resource umtri://rules/vocabulary. By default returns a summary view — nodes carry id/label/type/parent/season, plus a 200-char description excerpt, metadata.implements, placeholder/dormant flags (soft-deleted nodes are omitted). Pass view="full" only when you need timestamps, full descriptions, all metadata keys, or tags — that response is ~3× larger. For a large tree, read it in slices instead of all at once: rootId (+depth) for one trunk or limb; maxType for a layer (e.g. maxType="twig" = the skeleton without leaves/veins — a cheap overview); role for a cross-section; season for what was born in one season. Filters combine. Node ids come from the graph itself, so the standard drill-down is two calls: first get_graph with maxType="trunk" (or "limb") for a cheap skeleton, find the id you want, then call again with rootId set to it to get just that trunk/limb and its subtree. Control the heaviest field with descriptions: "none" drops descriptions for a pure structural overview, "excerpt" (default) gives a 200-char preview, "full" returns them verbatim when you drill into a limb. The summary response also carries shape (nodeCount, maxDepth, nodes-per-level, over-wide branches) so you can judge whether the tree is too deep or too wide without rebuilding it, a childCount on each branch node, and iaHints flagging structural smells. A bushy tree (mass at mid-levels) is healthy — don't over-nest sparse parents into twigs. Bugs default to active (open + in_progress) — pass bugStatus="all" to also see healed (resolved/closed) ones. Nodes may carry plan:true — these are the human's node-based brief (intent drawn as structure, not a prompt); read them as instructions and realize them (see umtri://rules/plan). When you slice (rootId/maxType/role/season), a connection with only ONE endpoint inside the slice is still returned, marked boundary:true, and its outside endpoint appears as a lightweight stub in externalNodes (id/label/type/role, external:true) — so cross-branch dependencies and calls never silently vanish from a slice. To follow one, call get_graph again with rootId set to that external id. The response also carries project.transplanting — when true the ground is still being transplanted (see umtri://rules/transplant): you may freely add/edit nodes in any season incl. past, and hard-delete import mistakes. project.seedMeta is the ground's intent when the human filled it in — goal, endDate, budget, audience. No tool writes it, so it is the human's own brief for what this tree is for: read it before you propose or reshape structure, and let it settle what belongs in the tree and what does not.
- **get_impact** — Walks the connection graph (edges + apis) from a start node — or a bug's target — to find which other nodes a problem would reach. This is the tool for "if this breaks / I change this, what else do I need to check?" and for QA scoping. Impact does NOT always flow with the arrow. direction="affected" (default) answers "if the start breaks, who is hurt?" and follows: dependency edges backward (target→source), data_flow edges forward (source→target), apis backward (callee→caller). direction="dependsOn" is the reverse ("what does the start rely on?"). direction="both" unions them. See umtri://rules/system-structure (How problems propagate). Provide exactly one start: node (a node id) or bug (a bug id — starts from its target node, or both endpoints if the bug is on an api; ground-level bugs are rejected). Traversal is over CURRENT (live) structure only. Returns reached[] (each with hops distance, the node `from` which it was reached, and the connection `via` it came through — so you can reconstruct the chain), bugs[] (active bugs sitting on any reached node/api, worst score first), and coverage. IMPORTANT: the result is a list of nodes to CHECK, not a proven failure set — it is only as complete as the connections recorded. coverage.startsWithoutConnections flags when the start has no connections at all: an empty result then means "nothing recorded," not "nothing affected" — record edges/apis first.
- **list_bugs** — Scans the bugs (issues eroding the ground) of one ground. Looking at ONE bug you already know the number/id of? Call get_bug instead — no need to list. Bugs default to status="active" (wild + chasing): a healed bug is history, and history is not what a scan is for. Ask for "resolved" or "all" when you actually want it. Status speaks the product's own words — wild (found, nobody on it) → chasing (someone is fixing it) → resolved (it landed) — the same words the UI shows. Each bug has a score (0–8 change-risk; 8 = riskiest), and any bug whose solution carries a checklist also carries solutionProgress ("2/5" — ticked/total), so you can see what is left without opening a single body. Targets: a node, an api, or the ground itself. By default returns a summary view — each bug carries id, seq (the number a human says, "#14"), target, title, score, status, createdAt/resolvedAt, plus a 200-char description excerpt. Each node/api bug also carries impactCount — how many other nodes its target reaches by blast radius (affected direction), so you can spot wide-blast bugs at a glance; call get_impact on that bug for the full reached list. Pass view="full" for full descriptions and metadata. Use limit + order to bound a scan (e.g. the 5 newest) instead of pulling every bug.
- **get_bug** — Returns a single bug by reference — the cheap path when you already know which bug you mean. `ref` accepts either the human-facing number shown in the UI (seq — "14" or #14) or the internal id (bug-<uuid>), so a human saying "#8" needs no listing at all. Unlike list_bugs this ignores status: a resolved bug is still readable by number, which is how you review what a past fix actually did. Use list_bugs only to scan for bugs you cannot yet name. The response carries the full description and metadata, plus impact — the blast radius reached from the bug's target (reachedCount, the reached nodes with hop distance, and other active bugs sitting in that radius). Numbers are per-ground and never reused, so a deleted bug leaves a gap rather than shifting the others. Returns an error if no bug matches.
- **list_seasons** — Returns the seasons (time epochs) of a ground in chronological order. A season is the human's declaration that a chapter closed. You may create one with create_season, but only after the user confirms it — see umtri://rules/seasons. By default returns a summary view (id, label, state, startedAt, grownAt). Pass view="full" to also include metadata.
- **list_events** — Returns the append-only change history (most recent first) of a ground: node/edge/api/bug create, update (with a field-level diff {from,to}), delete, and plan_commit. Each event carries the actor ("user:<id>" for humans, "token:<id>" for agents/automation), a summary label, and for updates a diff. Use it to answer "what changed, when, and by whom" — e.g. a bug's status transitions or a node's edits over time. Filter with entityType/entityId to get a single entity's timeline. Note: git commits recorded via record_commit live in a node's metadata.commits, not here.
- **create_bug** — Creates a new bug (issue eroding the target). target.kind = "node" or "api" requires target.id of an existing node/api in the ground. target.kind = "ground" attaches the bug to the project itself (no id). score = change risk on a 0–8 scale (8 = riskiest). It blends functional impact with fix difficulty (a harder fix is likelier to break things, so it ranks higher within a tier). 0: no risk — idea / memo. 1: no risk — copy / wording edit. 2: no functional error, but may change usability. 3: minor functional issue possible — simple fix. 4: minor — complex fix. 5: significant functional issue possible — simple fix. 6: significant — complex fix. 7: critical functional issue possible — simple fix. 8: critical — complex fix. Defaults to 4. Urgency is intentionally NOT part of this score. status defaults to "open". solution is the fix, and it belongs to agents — the app gives people no way to edit it, so a human's own proposed fix arrives in description instead. At report time solution holds the plan, and by the time the bug is resolved it should describe what was actually applied. Same field — overwrite it as understanding changes; project_events keeps the diff. Write what remains as "- [ ] item" lines so the app can render them as cards and later moves cost one small op (update_bug patch:{solutionItem}). Leave it empty rather than guessing. For a node/api bug, the response auto-attaches impact (the affected blast radius from the target): reachedCount, the reached nodes with hop distance and the connection each was reached through, other active bugs sitting in that radius, and a coverage note. Use it to scope what else to check/QA. If the target has no recorded connections the radius is empty — that means nothing is recorded, not that nothing is affected (record edges/apis). Requires a write-scope token. See umtri://rules/vocabulary for bug semantics.
- **create_project** — Creates a new ground. Slug must match /^[a-z0-9][a-z0-9-]{0,49}$/ and be unique. By default it goes into the authenticated user's personal space; pass org (a team slug, as list_projects shows it) to create it in a team — only the team's owner can. Requires a write-scope token.
- **create_season** — Creates a new season and makes it the "now" season. This is not an additive act: the ground's current now-season is sealed as past and every node in it is marked grown, which locks that node's parent/season/type/sproutedAt. CONFIRMATION GATE: call once WITHOUT confirm to get back what would be sealed, put that to the user in your own words, and call again with confirm:true only after they say yes. Never pass confirm:true on your own judgment — a season is the human's declaration that one chapter closed and the next began, and inventing one makes the ground's timeline meaningless. Unsure which season a node belongs to? Attach it to the existing now-season instead of opening a new one. See umtri://rules/seasons.
- **create_node** — Creates a node. Follows the plant vocabulary protocol — see umtri://rules/vocabulary. type ∈ trunk · limb · twig · leaf · vein. Role is derived (structure / object / action). Build leaf-first: when adding a leaf/vein, create only the missing trunk/limb/twig ancestors on its path, then the leaf — do NOT pre-build every trunk, then every limb, then leaves (see Build order in umtri://rules/vocabulary). Aim for a faithful, COMPLETE map of the project, not a summary: every meaningful module/screen/endpoint/table/integration should become a node (grouped at the information-unit grain, not one-per-file). A real project yields many leaves — under-capturing to a few nodes is the more common mistake. See Completeness in umtri://rules/system-structure. parent ∈ existing node id; omit to create a root-level node. season ∈ existing season id; omit to use the active "now" season. Past seasons are normally rejected, but allowed while the ground is transplanting (project.transplanting=true) — nodes added then are auto-stamped metadata.transplanted=true for audit. See umtri://rules/transplant. The tool validates against protocol policies. Hierarchy violations are rejected. Soft issues (reserved-domain labels, leaf↔vein heuristic, trunk naming) come back as warnings in the response — reconsider before continuing if warnings appear. A trunk carries metadata.kind ∈ front · server · database · library · device, and its limbs follow that kind's axis: same domain name across trunks with no suffix ("예약", not "예약 API"), no technical-layer limbs, and at most one common limb (metadata.common=true). Read umtri://rules/trunk-kinds before creating a trunk or a limb under one. After creating a leaf/vein, consider its connections: if it calls/feeds another node add an api (create_api), if it depends on/is built on another add an edge (create_edge). The response carries a connectionCheck reminder. See umtri://rules/system-structure (Connections). Do not open a new season just because none fits — attach to the now-season, or ask the user. create_season needs their confirmation (umtri://rules/seasons). When realizing a human-drawn plan brief, any detail nodes you add should carry metadata.plan=true and the realized node needs metadata.implements — see umtri://rules/plan. A trunk is never plan: metadata.plan on a trunk is rejected.
- **update_node** — Partial update of a node. Patchable fields: label, type, parent, season, description, tags, metadata, sproutedAt. metadata is merged key by key: only the keys you send change, keys you leave out (commits that record_commit accumulated, transplanted, and so on) are kept, and a key sent as null is removed. So to change implements, send just { metadata: { implements: [...] } }. Type change reclassifies role; parent change recomputes the ltree path automatically. On a grown (past-season) node, only the tree's shape and timeline are locked — parent, season, type and sproutedAt are rejected. Content fields (label, description, metadata, tags) stay editable, so you can keep metadata.implements current when code moves without reopening transplanting. Moving any node into a `past` season is rejected. Both restrictions lift while the ground is transplanting (project.transplanting=true), so historical structure can be reconstructed (see umtri://rules/transplant). Same protocol validation as create_node — reject on hierarchy violations, warn on soft issues.
- **delete_node** — Sets removed_at on the node. By default this tool rejects deletion if the node has any active descendant, so a subtree is never lost silently. To soft-delete the node together with all its active descendants, pass cascade=true — only when the human asked to remove the whole branch; soft-deleted nodes cannot be restored. The response lists the cascaded ids. EXCEPTION — while the ground is transplanting (project.transplanting=true), the active-descendant guard is lifted (subtree cascade allowed) and you may pass hard=true to permanently remove import mistakes, including grown (past-season) nodes. Once the human roots the ground, normal guards return.
- **commit_plan** — Promotes a plan node (metadata.plan=true) to the real tree by clearing the plan flag. VERIFICATION GATE: the node must already carry metadata.implements (the source path(s) you wrote) — without it the commit is rejected, because an uncommitted plan node with no implements is not considered realized. Call this only after you have actually written the code and recorded implements via update_node. Commit from the top down: a node whose parent is still a plan is rejected — commit the parent first. See umtri://rules/plan.
- **record_commit** — Configuration record (CI/CD). Given a commit SHA and the files it changed, finds nodes whose metadata.implements include any of those files and appends the commit (sha + timestamp + optional message) to their metadata.commits (deduped by sha). Returns the matched node ids. When one commit touches several nodes that are NOT yet connected, the response also carries coChangeCandidates[] ({a, aLabel, b, bLabel}) — nodes that change together are dependency candidates; review them and add a create_edge/create_api where a real relation exists (not auto-created; suppressed for large multi-node commits). Umtri does not run jobs or read git itself — a GitHub Action / CI step or an agent supplies the sha+files. Recording a commit is the LAST step: first make sure the change is in the tree (new unit → create_node with metadata.implements; moved file → update_node), otherwise the sha lands on the nodes that happen to exist and the new ones stay invisible. A repo that keeps forgetting should write the habit down — see umtri://rules/commit-sync. See also umtri://rules/plan.
- **create_edge** — Creates a directed edge (source → target) for a STRUCTURAL relation: type "dependency" (A is built on / needs B) or "data_flow" (data moves A → B outside a request). Use for module/library deps, a route depending on the data store, a job writing a table. For a runtime request/call/integration use create_api instead, not an edge. Reserve connections for relations a maintainer would trace — don't wire everything. type ∈ project.edge_types; same source and target is rejected. See umtri://rules/system-structure (Connections).
- **delete_edge** — Removes an edge from the ground. Use to retire dependencies/flows that no longer reflect reality.
- **create_api** — Records an API flow (start → end) for a RUNTIME request / call / data flow: a screen calling an endpoint, an endpoint hitting a table, an external integration (payment, SMS, webhook). Direction is caller → callee. APIs are first-class (apis table), not generic edges — for a build-time/structural reliance use create_edge (dependency) instead. Add the flows a maintainer would trace; don't wire everything. start/end must be existing node ids. See umtri://rules/system-structure (Connections).
- **update_api** — Partial update of an API. Patchable fields: label, description, metadata, start, end.
- **delete_api** — Sets removed_at on the API. History is preserved — the API can still be seen in past season views via the time slider.
- **update_bug** — Partial update of a bug. Most common use: status transition. Send only the fields that actually change. A status transition is patch:{status} alone — do not re-send an unchanged solution, description, or metadata. Those fields carry prose, and re-emitting them costs far more time than the update itself and risks a malformed-argument retry. Follow the lifecycle one step at a time: open (wild) → in_progress (chasing) → resolved. Set status="in_progress" the moment you start the fix, not after it lands — it is the only marker that someone is already on this bug, so a parallel agent can see the work in flight instead of duplicating it. Then set "resolved" once it ships. Skipping straight from open to resolved returns a warning (not a rejection) — acceptable when the fix was genuinely instant. Other patchable fields: title, description, solution, score (0–8 change-risk), metadata. solution is yours to maintain — the app does not let people edit it, so what you write is what the human reads. Their own proposed fix arrives in description instead. Keep solution as a checklist: prose for what you learned, "- [ ] item" lines for what remains. To move one item use patch:{solutionItem:{…}} instead of re-sending the whole field — add: "text", or check/uncheck/remove: <1-based item number>. Pass expect:"<start of that item text>" alongside a number so a shifted list fails loudly instead of ticking the wrong line. This is the cheap path: one short op, not a re-emitted body. When resolving, rewrite solution to what you actually applied — at report time it held the plan, and leaving a stale plan there is worse than leaving it empty. To mark a bug as fixed, prefer status="resolved" over delete — that preserves the history of what eroded the tree. When resolving, you may record the shipped release in metadata.resolvedVersion (e.g. "v2.3.1"); metadata is replaced wholesale, so include existing keys you want to keep.
- **delete_bug** — Permanently removes a bug record. Bugs do not have history — once deleted, the record is gone. For "I resolved this", use update_bug with status="resolved" instead, so the tree remembers what was eroded.
- **send_feedback** — Files a suggestion or a defect report about **Umtri the product** — this MCP server, its tools, the app, the docs. This is NOT for issues in the user's own project: those are bugs on a ground (create_bug). Call this ONLY when the human explicitly asks to leave feedback ("건의해줘", "report this to Umtri"). Do not file feedback on your own initiative — an inbox filled by agents stops being read. kind: "bug" = something in Umtri is broken or wrong; "improvement" = a suggestion / wish (default). source: where it surfaced — the ground slug you were working in, the tool name that felt wrong, the screen. It is free text and is not validated, but it is what makes a report actionable later. One-way: this files the report, and there is no reading it back — do not promise the human a status thread. Requires a write-scope token.
- **list_feedback** — Lists feedback filed about **Umtri the product** (see send_feedback) — not the bugs of any ground. Umtri operators only: every other account gets 403, so do not call this to check on feedback you filed. Newest first. Status: open (untouched) → done (handled) / wontfix (declined, with the reason in note). Defaults to every status; pass status to narrow. Use limit to bound a scan.
- **update_feedback** — Updates one feedback record about **Umtri the product** — typically closing it: status="done" once it shipped, or "wontfix" when it was declined. Put what was actually done (or why not) in note; that is the part worth reading a year later. Umtri operators only: every other account gets 403. Send only the fields that change. Requires a write-scope token.
- **list_wiki** — Lists the ground's wiki — the prose the tree cannot hold: conventions, decisions and why they were made, glossary, onboarding. Reference entries describing how things work, not a log of what happened: the change record is list_events, the page's own past is in its revisions. Not the structure itself (get_graph) and not defects (list_bugs). Bodies come back as 200-char excerpts by default so a scan stays cheap; pass body="full" only when you mean to read them, or body="none" for a pure index. A page may hang off a node (nodeId) or float free — pass node=<id> for one node's pages, or node="none" for the pages not yet attached to anything, which is the list worth reviewing when the tree has grown. The wiki is a tree: each page has a parent page and a position among its siblings, starting from an overview page. order="tree" returns the whole table of contents in reading order (depth-first, with depth) — use it to read the wiki front to back or to see where a new page belongs. Every page also has a kind (overview / concept / component / rule / glossary); kind="none" lists the pages not yet classified.
- **get_wiki** — Returns one wiki page in full. `ref` takes either the page slug (what a human says) or the internal id (wiki-<uuid>) — renaming a page changes its slug but never its id, so ids stay safe to store. Read the page before rewriting it: write_wiki with mode="replace" overwrites the body wholesale. Pass rev=<n> to read an older revision of the page instead of the current one (list them with list_wiki_revisions); to put an old revision back, do not paste its body into a write — use write_wiki mode="restore", which cannot mangle the prose in transit.
- **list_wiki_revisions** — Returns what this page said before, newest first — rev number, who wrote it, when, and the body length. Bodies are not included (a history of twenty full documents is expensive to skim); read one with get_wiki rev=<n>. Use it to answer "what did this say last week", to see whether a write of yours dropped something, and to find the rev to hand back to write_wiki mode="restore". Consecutive writes by the same actor within 30 minutes fold into one revision rather than piling up, so the list stays readable — a working session is one entry, not eight. The practical guarantee: whatever an agent does to a page in one session, the body from before that session is still a separate revision. Revisions start at the moment the ground gained this feature; edits older than that were never recorded and cannot be recovered.
- **write_wiki** — Writes one wiki page. Creates it if the page slug is new, updates it otherwise — you do not need to check first. **The wiki is the project's encyclopedia: one page is one entry, and its body is the current spec and rules of that thing.** It is a tree read front to back: an overview page at the top, chapter pages under it, entries under those — place every page with parent (a page slug or id) and position (its order among siblings), and check list_wiki order="tree" to see where it belongs. For a new wiki, follow the table of contents in umtri://templates/wiki. The title is a plain noun for the thing in the reader's language (조직, 배포, Billing) — no dash, colon, parenthetical or sentence; the description goes in the lead. Give every page a kind — overview (the top page and chapter pages that introduce what is under them), concept (a product/domain concept or model), component (how one part of the system works), rule (a convention that must be kept), glossary (the project's vocabulary). Write it to this skeleton: a lead paragraph first (1–3 sentences saying what the thing is — not a heading, quote or list), then "## 스펙" / "## Spec" (how it works now; break it down with ###), then "## 규칙" / "## Rules" (invariants, prohibitions, required order). concept and component need Spec; rule needs Rules; overview and glossary are free-form after the lead. No other top-level (##) sections. Background, history, rejected alternatives and side notes go into footnotes — [^name] in the text, [^name]: … at the end — not into the body. A reason stays in the body only if someone changing the code would get it wrong without it. Write what the tree cannot hold and what a maintainer would otherwise have to reconstruct: why a decision went the way it did, the vocabulary this project uses, what an investigation concluded. Do not restate the structure — that is what nodes are for. **A page is a description, not a log.** Write it in the present tense, as a reference entry for the thing: how it works now, and the reasons that still constrain it. Do not append what you just did, do not add dated entries or a change-history section, do not keep progress checklists here — those go to bugs, plan nodes, list_events, and record_commit, and the page's own past is already kept in its revisions. When something changes, rewrite the sentence that is now wrong rather than adding a sentence saying it changed. mode="replace" (default) overwrites the whole body — read the page first, or you will drop what was there; this is the normal way to update a page. mode="append" is the narrow case: adding a section the page genuinely lacked, never tacking on this session's summary. mode="restore" puts an earlier revision back: pass rev=<n> and nothing else, and the page returns to what it said then. node attaches the page to a node; omit it and the page floats free, which is fine and common early on. Attach it later once the tree has a place for it. Deleting that node detaches the page rather than deleting it. Every write is kept as a revision, so a replace that drops prose can be undone (list_wiki_revisions, then mode="restore"). Consecutive writes by the same actor within 30 minutes fold into one revision instead of piling up, which means the body from before your session stays recoverable as its own revision no matter how many times you rewrite the page now. Pass newRevision=true when the page moves to a new stage rather than being polished further — most importantly when you first write back what you actually built over a page that until now held the plan. Folding is judged by clock and author, which cannot tell "still drafting" from "the plan became the thing"; only you know that, and without the flag the plan is quietly overwritten by its own outcome. rename moves the page to a new slug, keeping its id and its whole revision history — do not create a new page and delete the old one, that throws the history away. The response tells you what the write did: rev is the revision it landed in, coalesced says whether it folded into your previous one, and previousBodyChars is how long the body was before you wrote. If previousBodyChars is much larger than what you just wrote, you replaced someone's work — check list_wiki_revisions before moving on. baseRev is optional and is how you avoid clobbering a concurrent edit: pass the rev you read, and the write is refused if the page moved on since. Use it when you read, think, then write. The response carries warnings[] when the page strays from the skeleton or reads like a log (status banners, dated headings, checklists, change-history sections, unmatched footnotes). The write is saved either way — fix what they point at and write again. The change also lands in the ground's history (list_events, entityType="wiki"), where the body is recorded as a length delta rather than a copy of the prose — the prose itself lives in the revisions. Requires a write-scope token.
- **delete_wiki** — Permanently removes a wiki page **and its whole revision history** — unlike an overwrite, this cannot be undone, and only the history entry (list_events) remains to say it happened. Prefer rewriting a page that went stale over deleting it: a page that records a decision you have since reversed is worth keeping with the reversal written in. Deleting is for a page that should never have existed, not for one that is out of date.

## Claude Desktop Quick Installation
Install path detected from listing signals. Uses `npx` (confidence: high):

```json
"mcpServers": {
  "umtri": {
    "command": "npx",
    "args": ["-y","umtri-mcp"],
    "env": {
      "UMTRI_API_TOKEN": "",
      "UMTRI_API_BASE": ""
    }
  }
}
```

**Requires environment variables:** `UMTRI_API_TOKEN`, `UMTRI_API_BASE` — the values above are empty placeholders; fill in real credentials before running (see the repository for what each one is for).

## Documentation

## What the Umtri MCP server does

The Umtri MCP server connects an AI coding agent to Umtri, a system that records a project's structure, the relationships between its parts, and its bugs. Instead of requiring an agent to reconstruct the codebase from source during every session, it can retrieve information already recorded in the project map.

The stored model includes a tree of project components and connections between them. The server also supports impact questions: an agent can ask what may be affected by changing a component and receive a result based on recorded relationships rather than an inferred text search. Project, bug, season, event, and wiki information are available through separate tools.

## How it works

The server exposes Umtri through MCP over either a hosted HTTP endpoint or a local stdio process. The hosted option uses `https://mcp.umtri.io` and an authorization bearer token. The local option runs the `umtri-mcp` npm package and can target the default Umtri API or another API base.

Graph retrieval is scoped rather than returned as one unrestricted dump. An agent can select a subtree with `rootId`, restrict results by layer with `maxType`, filter by role or season, and control how much description data is included. Ten read-only MCP resources provide rules and templates covering vocabulary, system structure, seasons, plans, commits, transplantation, and wiki organization.

## Setup and configuration

For the hosted service, create an API token in the Umtri application under Settings and API Tokens. Configure an MCP client with the HTTP URL and an `Authorization: Bearer ...` header. The repository shows a Claude setup command and a generic MCP configuration object using the same endpoint and header format.

For local stdio execution, run the package with `npx -y umtri-mcp` and set `UMTRI_API_TOKEN`. `UMTRI_API_BASE` is optional and defaults to `https://api.umtri.io`; set it when using a different or self-hosted API endpoint. The token is not checked during setup. A connection can therefore appear successful while calls fail because the token is invalid or lacks the required scope. Read-only tokens cannot invoke write tools.

## Tools and capabilities

The Umtri MCP server groups its tools by the kind of project work they support:

- Read project data with `get_graph`, `get_bug`, `get_impact`, `list_projects`, `list_bugs`, `list_seasons`, and `list_events`.
- Change the recorded model with tools for projects, nodes, edges, APIs, bugs, and seasons. Node deletion removes a subtree only when `cascade: true` is supplied.
- Manage plan execution with `commit_plan` and `record_commit`.
- Browse and maintain the wiki with `list_wiki`, `get_wiki`, `list_wiki_revisions`, `write_wiki`, and `delete_wiki`. Wiki pages have a parent and position, a kind, and a place in the documentation structure; writes return warnings when a page does not fit that structure.
- Send a suggestion or defect about Umtri with `send_feedback`.

The write surface includes project and node creation, node updates and deletion, relationship management, API records, bug records, and season creation. Season creation takes effect only after user confirmation.

## Limitations and notes

The server depends on data that has been recorded in Umtri. Its impact results follow stored connections, so incomplete project maps may limit what can be traced. Graph results are intentionally slices controlled by query options, not complete exports.

API tokens require appropriate permissions. A token with only read access will not work for mutations, and authentication problems may not be visible until the first tool call. The available setup material documents Claude usage and a generic MCP configuration format; client-specific behavior beyond those formats is not specified.

_Full upstream README: https://allmcps.com/mcp/umtri/readme_

