Skip to content

moooon

Motir

Vibe your whole project. Bring an idea — Motir's three AI layers plan it, track it, and ship it, end to end. You're looking at Motir, built in Motir.

  • Vibe Project
  • Open Source
  • AI Agent
  • AI Loop
1
requests
0
upvotes
145
planned
1,361
shipped

Motir · Roadmap

MOTIR-4194Done

(motir-core) Publish the MCP tool catalogue as an anonymous artifact — the derived grouping served at a stable path, the way the OpenAPI spec already is

Opened by Zhu Yue ·

Type · code · motir-core · ONE PR · the PRODUCER half of the tool-catalogue seam

Why now · MOTIR-4180 removed the hand-copied tool list from motir.co/docs/mcp/tools because there was nothing for a guard in that repository to check it against. Motir now renders no tool catalogue anywhere. This card supplies the artifact that lets the page come back correctly, and its consumer is the sibling card blocked_by this one.


The shape, and why it is this one

/docs/api reads ${APP_ORIGIN}/api/openapi/v1.json fresh at request time and holds no copy — the strongest form of the decision MOTIR-4046 was told to make. Do the same for the tool catalogue. Everything needed is already in the tree and already total by construction:

  • lib/mcp/toolPermissions.tsTOOL_PERMISSIONS, 55 keys, one permission per tool. The permission IS the group (a tool's group is derived from it; only the ORDER is authored — MOTIR-2581).
  • lib/apiDocs/mcp.tsTOOL_SUMMARIES, the authored one-line summary per tool plus its descriptionFingerprint. Held key-equal to TOOL_PERMISSIONS by a typecheck chain whose point is that "a tool cannot reach the server undocumented".

That module currently has no runtime reader at all (git grep -l 'apiDocs/mcp|TOOL_SUMMARIES' origin/main returns the file, its fingerprint module, two tests and three documents — no route, no component). It has been an orphan since MOTIR-3951 deleted app/(public), which is also why its truth gate could be deleted as collateral and go unnoticed for a day (MOTIR-4165). Giving it a published reader is what makes the gate guard something real.

⚠️ Why the LIVE surface cannot be the source instead

app/api/mcp/route.ts wraps every request in withMcpAuth(…, { required: true })"rejects an absent/invalid/revoked/expired token with a 401 before a tool ever executes" — and the transport layer meters "the initialize / tools/list traffic no tool callback ever sees". tools/list is 401 to an anonymous caller, so a second repository cannot read it without holding a workspace token. That is the fact that makes a published artifact the right answer rather than a convenience: it removes the credential from the seam entirely.

Scope

  1. Serve it. A route rendering the catalogue as JSON — tool name, permission, summary, and the group derived from the permission with the authored order preserved. Anonymous and cacheable — no session, no PAT, no workspace context; it describes the SURFACE, not any tenant's data.
  2. Decide and record the versioning posture, in the PR body and in the ADR the repo already keeps for public surfaces (docs/decisions/public-api-conventions.md / public-surface-hosts.md). The recommendation, and the reason: an UNVERSIONED docs artifact, not part of the v1 contract. The MCP surface already versions itself through tools/list, and putting a documentation feed under the published stability/deprecation policy buys a standing obligation without buying a reader. If you disagree, say so in the ADR — the point is that it is decided on the record rather than inherited from whichever path the route happens to sit under.
  3. Honour the self-hosting gate the way its neighbour does. MOTIR-4042 is the fixture: /api/openapi/public.json shipped a contract for a public surface a self-hosted build does not serve, and that was a bug. Read how that was resolved and apply the SAME answer here — do not invent a second policy for an adjacent route.
  4. A totality guard. The served document must carry every TOOL_PERMISSIONS key. A tool added without a summary already fails typecheck; this asserts the SERIALISATION did not drop one, which typecheck cannot see.

Acceptance criteria

(Ordering: every criterion is true at PR-open in motir-core; none reads main, a merge or a deploy. Repository: motir-core alone — the page that renders this is the sibling card.)

  1. A route serves the tool catalogue as JSON, reachable with no credential — asserted by a test that calls it with no Authorization header and gets a document, not a 401.
  2. Every key of TOOL_PERMISSIONS appears in the served document, asserted by comparing the two SETS — proved to fire by removing one entry inside the test, never proved to pass.
  3. The grouping in the document is DERIVED from each tool's permission. No group label is authored as a free string in this route; the authored ORDER is the only authored thing, and the test says which is which.
  4. The versioning posture (scope item 2) is decided and written into the relevant docs/decisions/ record, naming what a consumer may rely on and what may change without notice.
  5. The self-hosted / cloud-gating behaviour matches whatever /api/openapi/public.json does after MOTIR-4042 — the PR body names that resolution and states this route follows it.
  6. lib/apiDocs/mcp.ts's header names its new runtime reader, so the module is no longer describable as an orphan.
  7. The PR body quotes the document as fetched from the running app — the anonymous curl and its first lines — because a route that returns 401 to the consumer is the failure this card exists to prevent, and only an actual unauthenticated call proves it does not.
  8. pnpm lint · typecheck · test pass; the new route carries its own coverage.

What this does NOT do

It does not render anything. The page is the sibling motir-marketing card, blocked_by this one — ONE SUBTASK = ONE REPO = ONE PR.

It also does not restore the fingerprint truth gate: that is MOTIR-4165, which is independent and shippable now. The two are complementary — 4165 proves each SUMMARY still matches the tool text it was written against; this card proves the catalogue reaches a reader. Neither blocks the other, and neither substitutes for the other.

Discussion

No comments yet.

Adding to this discussion signs you in on app.motir.co and brings you back to this request.

Add a comment