Yichus / Reference / API MCP tools

api_report

Build the program-report-2.0 map from the typed IR for whatever style of program the sources are (CRUD, flow, dist, escrow, unrouted IO). overview.surfaces names the present IR surfaces; needed names missing declarations with snippets. Pass format html for a self-contained HTML document.

Kind: Report. Origin: Yichus/Mcp::catalog.

CLI: yichus api report — the program-map report with the same detail levels.

Parameters

Example

This response was produced by calling this tool with the displayed request when these docs were generated. Analysis examples use the owner-scoped notes specimen; scaffold examples supply a resource specification.

Request

{
  "sources": [
    {
      "fileName": "notes-api.bosatsu",
      "source": "package Demo/Service/NotesApi\n\n# Owner-scoped notes API. One row per caller, keyed by Principal.user_id.\n# `yichus api verify` / api_access_check prove the scoping from Matchless IR.\n\nfrom Yichus/IO import IO, flat_map\nfrom Yichus/Access import Principal, AccessRule, AccessSpec, OwnerScoped\nfrom Yichus/Data import Db, Table, table, db_read, db_write, db_delete\nfrom Yichus/Service import handler, route, service_def, ReadPerm, WritePerm, DeletePerm\n\nexport (notes_api, access_rules, Note())\nexposes (Yichus/Access, Yichus/Service)\n\nstruct Note(title: String)\n\naccess_rules = AccessSpec([\n  AccessRule(\"notes\", OwnerScoped),\n])\n\ndef notes_table(db: Db) -> Table[List[Note]]:\n  table(db, \"notes\")\n\ndef list_notes(db: Db, p: Principal) -> IO[List[Note]]:\n  Principal(uid, _) = p\n  db_read(notes_table(db), uid)\n\ndef put_notes(db: Db, p: Principal, items: List[Note]) -> IO[List[Note]]:\n  Principal(uid, _) = p\n  db_write(notes_table(db), uid, items)\n\ndef add_note(db: Db, p: Principal, item: Note) -> IO[List[Note]]:\n  Principal(uid, _) = p\n  items <- flat_map(db_read(notes_table(db), uid))\n  db_write(notes_table(db), uid, [item, *items])\n\ndef clear_notes(db: Db, p: Principal) -> IO[Unit]:\n  Principal(uid, _) = p\n  db_delete(notes_table(db), uid)\n\nnotes_api = service_def(\n  \"notes\",\n  [\n    route(\"/notes\", handler(\"list_notes\", list_notes), [ReadPerm(\"notes\")]),\n    route(\"/notes/put\", handler(\"put_notes\", put_notes), [WritePerm(\"notes\")]),\n    route(\"/notes/add\", handler(\"add_note\", add_note), [ReadPerm(\"notes\"), WritePerm(\"notes\")]),\n    route(\"/notes/clear\", handler(\"clear_notes\", clear_notes), [DeletePerm(\"notes\")]),\n  ]\n)\n"
    }
  ],
  "instances": "1"
}

Response

{
  "ok": true,
  "tool": "api_report",
  "artifactClass": "program-report-2.0",
  "topology": "single-instance",
  "detail": "summary",
  "headline": {
    "class": "crud-api",
    "counts": {
      "routes": 4,
      "bindings": 4,
      "tables": 1,
      "cells": 0,
      "flows": 0,
      "abstractions": 1,
      "packages": 1
    },
    "liminal": {
      "unrouted": 0,
      "undeclared": 0,
      "needed": 1
    }
  },
  "proven": true,
  "overview": {
    "surfaces": [
      "tables",
      "routes",
      "shared-cells"
    ],
    "routeCount": 4,
    "handlerCount": 4,
    "tableCount": 1,
    "effectCount": 0,
    "tables": [
      {
        "name": "notes",
        "policy": "OwnerScoped",
        "status": "holds",
        "rowType": "Note",
        "writerCount": 3
      }
    ],
    "concurrencySurface": [
      "notes"
    ],
    "facts": [
      "surfaces: tables, routes, shared-cells",
      "4 route(s), 4 handler(s)",
      "1 table(s)",
      "1 shared cell(s)",
      "notes: OwnerScoped (holds, row Note)",
      "add_note: read-modify-write on notes",
      "every op on notes keys by the caller",
      "1 missing declaration(s); call api_add_artifact"
    ]
  },
  "map": {
    "nodes": [
      {
        "id": "needed:FrontendSpec",
        "zone": "surface",
        "size": 0,
        "status": "needed"
      },
      {
        "id": "route:/notes",
        "zone": "surface",
        "size": 1
      },
      {
        "id": "route:/notes/add",
        "zone": "surface",
        "size": 1
      },
      {
        "id": "route:/notes/clear",
        "zone": "surface",
        "size": 1
      },
      {
        "id": "route:/notes/put",
        "zone": "surface",
        "size": 1
      },
      {
        "id": "abstraction:notes_table",
        "zone": "work",
        "size": 5
      },
      {
        "id": "binding:add_note",
        "zone": "work",
        "size": 2,
        "cond": [
          "guarantee:rmw-transactions"
        ]
      },
      {
        "id": "binding:clear_notes",
        "zone": "work",
        "size": 1
      },
      {
        "id": "binding:list_notes",
        "zone": "work",
        "size": 1
      },
      {
        "id": "binding:put_notes",
        "zone": "work",
        "size": 1
      },
      {
        "id": "table:notes",
        "zone": "state",
        "size": 4,
        "status": "violated",
        "cond": [
          "invariant:race-free:notes"
        ]
      }
    ],
    "edges": {
      "routes_to": [
        [
          "route:/notes",
          "binding:list_notes"
        ],
        [
          "route:/notes/add",
          "binding:add_note"
        ],
        [
          "route:/notes/clear",
          "binding:clear_notes"
        ],
        [
          "route:/notes/put",
          "binding:put_notes"
        ]
      ],
      "reads": [
        [
          "binding:add_note",
          "table:notes"
        ],
        [
          "binding:list_notes",
          "table:notes"
        ]
      ],
      "writes": [
        [
          "binding:add_note",
          "table:notes"
        ],
        [
          "binding:put_notes",
          "table:notes"
        ]
      ],
      "deletes": [
        [
          "binding:clear_notes",
          "table:notes"
        ]
      ],
      "covers": [
        [
          "needed:FrontendSpec",
          "route:/notes"
        ],
        [
          "needed:FrontendSpec",
          "route:/notes/add"
        ],
        [
          "needed:FrontendSpec",
          "route:/notes/clear"
        ],
        [
          "needed:FrontendSpec",
          "route:/notes/put"
        ]
      ]
    }
  },
  "needed": [
    {
      "artifact": "FrontendSpec",
      "kind": "frontend",
      "because": "4 route(s) are declared but no FrontendSpec binding selects them",
      "unlocks": "api_frontend",
      "tool": "api_add_artifact",
      "snippet": "from Yichus/Frontend import View, FrontendSpec, ListView, Form, Action\n\napp = FrontendSpec(\n  \"App\",\n  [\n    View(\"/notes\", \"list_notes\", ListView),\n    View(\"/notes/add\", \"add_note\", Form),\n    View(\"/notes/clear\", \"clear_notes\", Action),\n    View(\"/notes/put\", \"put_notes\", Form),\n  ]\n)"
    }
  ],
  "dataModel": {
    "tables": [
      {
        "name": "notes",
        "policy": "OwnerScoped",
        "status": "holds",
        "rowType": "Note",
        "fields": [
          {
            "name": "title",
            "type": "String",
            "read": false
          }
        ],
        "readers": [
          "add_note",
          "list_notes"
        ],
        "writers": [
          "add_note",
          "put_notes"
        ],
        "deleters": [
          "clear_notes"
        ],
        "keyDiscipline": "every DB operation keys by the caller's Principal.user_id",
        "operations": [
          {
            "binding": "list_notes",
            "operation": "db_read",
            "status": "holds",
            "detail": "key derives from the authenticated Principal (user_id or owner_key)"
          },
          {
            "binding": "put_notes",
            "operation": "db_write",
            "status": "holds",
            "detail": "key derives from the authenticated Principal (user_id or owner_key)"
          },
          {
            "binding": "add_note",
            "operation": "db_read",
            "status": "holds",
            "detail": "key derives from the authenticated Principal (user_id or owner_key)"
          },
          {
            "binding": "add_note",
            "operation": "db_write",
            "status": "holds",
            "detail": "key derives from the authenticated Principal (user_id or owner_key)"
          },
          {
            "binding": "clear_notes",
            "operation": "db_delete",
            "status": "holds",
            "detail": "key derives from the authenticated Principal (user_id or owner_key)"
          }
        ]
      }
    ]
  },
  "surface": {
    "routes": [
      {
        "path": "/notes",
        "handler": "list_notes",
        "handlerBinding": "list_notes",
        "authority": "authenticated",
        "declared": [
          "Read(notes)"
        ],
        "inferred": [
          "Read(notes)"
        ],
        "status": "match",
        "tables": [
          "notes"
        ],
        "effect": "pure-read",
        "narrative": [
          "reads notes"
        ],
        "params": [
          "db: return-data",
          "p: return-data"
        ]
      },
      {
        "path": "/notes/add",
        "handler": "add_note",
        "handlerBinding": "add_note",
        "authority": "authenticated",
        "declared": [
          "Read(notes)",
          "Write(notes)"
        ],
        "inferred": [
          "Read(notes)",
          "Write(notes)"
        ],
        "status": "match",
        "tables": [
          "notes"
        ],
        "effect": "read-modify-write",
        "narrative": [
          "reads notes → writes notes"
        ],
        "rmw": [
          {
            "resource": "notes",
            "readAt": "notes_api:33:21",
            "writeAt": "notes_api:34:3"
          }
        ],
        "params": [
          "db: return-data",
          "item: return-data",
          "p: return-data"
        ]
      },
      {
        "path": "/notes/clear",
        "handler": "clear_notes",
        "handlerBinding": "clear_notes",
        "authority": "authenticated",
        "declared": [
          "Delete(notes)"
        ],
        "inferred": [
          "Delete(notes)"
        ],
        "status": "match",
        "tables": [
          "notes"
        ],
        "effect": "delete",
        "narrative": [
          "deletes notes"
        ],
        "params": [
          "db: return-data",
          "p: return-data"
        ]
      },
      {
        "path": "/notes/put",
        "handler": "put_notes",
        "handlerBinding": "put_notes",
        "authority": "authenticated",
        "declared": [
          "Write(notes)"
        ],
        "inferred": [
          "Write(notes)"
        ],
        "status": "match",
        "tables": [
          "notes"
        ],
        "effect": "blind-write",
        "narrative": [
          "writes notes"
        ],
        "params": [
          "db: return-data",
          "items: return-data",
          "p: return-data"
        ]
      }
    ]
  },
  "concurrency": {
    "bound": 8,
    "truncatedOps": 0,
    "interleavingsExplored": 60,
    "uniqueOutcomes": 25,
    "complete": true,
    "causalConsistency": {
      "id": "causal-consistency",
      "status": "holds",
      "kind": "built-in",
      "origin": "complete-causal-graph",
      "detail": "the complete causal graph over 5 effect operations admits an ordering; schedule exploration remains separately bounded"
    },
    "cells": [
      {
        "stateRef": "notes",
        "writers": [
          "add_note",
          "clear_notes",
          "put_notes"
        ],
        "readers": [
          "add_note",
          "list_notes"
        ],
        "invariants": [
          {
            "id": "race-free:notes",
            "status": "violated",
            "kind": "built-in",
            "origin": "built-in-interleaving",
            "detail": "6 valid orderings over 'notes' produce differing effect sequences",
            "counterexample": [
              [
                {
                  "index": 0,
                  "binding": "list_notes",
                  "primitive": "db_read",
                  "stateRef": "notes",
                  "location": "notes_api:25:3"
                },
                {
                  "index": 1,
                  "binding": "put_notes",
                  "primitive": "db_write",
                  "stateRef": "notes",
                  "location": "notes_api:29:3"
                },
                {
                  "index": 2,
                  "binding": "add_note",
                  "primitive": "db_read",
                  "stateRef": "notes",
                  "location": "notes_api:33:21"
                },
                {
                  "index": 3,
                  "binding": "add_note",
                  "primitive": "db_write",
                  "stateRef": "notes",
                  "location": "notes_api:34:3"
                },
                {
                  "index": 4,
                  "binding": "clear_notes",
                  "primitive": "db_delete",
                  "stateRef": "notes",
                  "location": "notes_api:38:3"
                }
              ]
            ]
          }
        ]
      }
    ],
    "topologySensitivity": {
      "declared": "single-instance",
      "other": "multi-instance",
      "flips": [
        {
          "id": "integrity",
          "atDeclared": "warning",
          "atOther": "violated"
        },
        {
          "id": "rmw-transactions",
          "atDeclared": "warning",
          "atOther": "violated"
        }
      ]
    }
  },
  "shape": {
    "packages": [
      {
        "name": "Demo/Service/NotesApi",
        "bindingCount": 7,
        "ioBindingCount": 5,
        "structCount": 0,
        "bindings": [
          {
            "name": "add_note",
            "type": "(Yichus/Data::Db, Yichus/Access::Principal, Note) -> Yichus/IO::IO[List[Note]]",
            "role": "terminal-io",
            "params": [
              "db: return-data",
              "item: return-data",
              "p: return-data"
            ],
            "reads": [
              "DbRead"
            ],
            "writes": [
              "DbWrite"
            ],
            "touches": [
              "notes"
            ]
          },
          {
            "name": "clear_notes",
            "type": "(Yichus/Data::Db, Yichus/Access::Principal) -> Yichus/IO::IO[()]",
            "role": "terminal-io",
            "params": [
              "db: return-data",
              "p: return-data"
            ],
            "writes": [
              "DbDelete"
            ],
            "touches": [
              "notes"
            ]
          },
          {
            "name": "list_notes",
            "type": "(Yichus/Data::Db, Yichus/Access::Principal) -> Yichus/IO::IO[List[Note]]",
            "role": "terminal-io",
            "params": [
              "db: return-data",
              "p: return-data"
            ],
            "reads": [
              "DbRead"
            ],
            "touches": [
              "notes"
            ]
          },
          {
            "name": "notes_table",
            "type": "Yichus/Data::Db -> Yichus/Data::Table[List[Note]]",
            "role": "pure",
            "params": [
              "db: return-data"
            ],
            "signals": [
              "literal-root"
            ]
          },
          {
            "name": "put_notes",
            "type": "(Yichus/Data::Db, Yichus/Access::Principal, List[Note]) -> Yichus/IO::IO[List[Note]]",
            "role": "terminal-io",
            "params": [
              "db: return-data",
              "items: return-data",
              "p: return-data"
            ],
            "writes": [
              "DbWrite"
            ],
            "touches": [
              "notes"
            ]
          }
        ]
      }
    ]
  },
  "abstractions": {
    "shared": [
      {
        "name": "notes_table",
        "type": "Yichus/Data::Db -> Yichus/Data::Table[List[Note]]",
        "role": "pure",
        "useCount": 5,
        "callerCount": 4,
        "useSites": [
          "add_note applied/1 @notes_api:33:29",
          "add_note applied/1 @notes_api:34:12",
          "clear_notes applied/1 @notes_api:38:13",
          "list_notes applied/1 @notes_api:25:11",
          "put_notes applied/1 @notes_api:29:12"
        ],
        "routes": [
          "/notes",
          "/notes/add",
          "/notes/clear",
          "/notes/put"
        ],
        "tables": [
          "notes"
        ]
      }
    ]
  },
  "guarantees": [
    {
      "id": "compiles",
      "claim": "parses and type-checks",
      "status": "proven",
      "evidence": "guarantees",
      "degree": {
        "topology": "single-instance"
      }
    },
    {
      "id": "access-spec-declared",
      "claim": "an AccessSpec binding declares every table's policy",
      "status": "proven",
      "evidence": "dataModel.tables",
      "degree": {
        "topology": "single-instance"
      },
      "details": [
        "notes: OwnerScoped"
      ]
    },
    {
      "id": "access-coverage",
      "claim": "every DB operation touches a declared resource",
      "status": "proven",
      "evidence": "dataModel.tables",
      "degree": {
        "topology": "single-instance"
      }
    },
    {
      "id": "access:notes",
      "claim": "every DB operation on \"notes\" keys by the caller's Principal.user_id",
      "status": "proven",
      "evidence": "dataModel.tables.notes",
      "degree": {
        "topology": "single-instance"
      },
      "assumptions": [
        "engine binds Principal; handlers never construct it"
      ]
    },
    {
      "id": "route-permissions",
      "claim": "declared route permissions equal handler effects recovered from the IR",
      "status": "proven",
      "evidence": "surface.routes",
      "degree": {
        "topology": "single-instance"
      }
    },
    {
      "id": "route-authority",
      "claim": "Route authority declarations are enforceable",
      "status": "proven",
      "evidence": "guarantees",
      "degree": {
        "topology": "single-instance"
      },
      "details": [
        "/notes: authenticated",
        "/notes/add: authenticated",
        "/notes/clear: authenticated",
        "/notes/put: authenticated"
      ]
    },
    {
      "id": "rmw-transactions",
      "claim": "add_note reads then writes \"notes\"",
      "status": "warning",
      "evidence": "surface.routes",
      "degree": {
        "topology": "single-instance",
        "bound": 8,
        "truncatedOps": 0,
        "complete": true,
        "interleavingsExplored": 60
      },
      "assumptions": [
        "engine binds Principal; handlers never construct it",
        "single engine instance runs each request's IO plan atomically"
      ],
      "details": [
        "Demo/Service/NotesApi::add_note reads then writes \"notes\"; with multiple instances on one DB, run this handler in a transaction (or a single-writer queue) to avoid lost updates (accepted: instances=1 declared; one instance runs each request's IO plan atomically)"
      ]
    },
    {
      "id": "integrity",
      "claim": "bounded interleaving over shared cells is race-free",
      "status": "warning",
      "evidence": "concurrency.cells",
      "degree": {
        "topology": "single-instance",
        "bound": 8,
        "truncatedOps": 0,
        "complete": true,
        "interleavingsExplored": 60
      },
      "assumptions": [
        "engine binds Principal; handlers never construct it",
        "single engine instance runs each request's IO plan atomically"
      ],
      "details": [
        "race-free:notes: 6 valid orderings over 'notes' produce differing effect sequences (accepted: instances=1 declared; one instance runs each request's IO plan atomically)"
      ]
    }
  ]
}

Run tools in the browser: api-mcp.html?webmcp=1.

Read the result according to this tool’s scope: static checks, bounded execution checks, and descriptive diagrams answer different questions. A successful call is not a general approval of the program. The safety and permissions guide compares the checks and provides editable ownership, guard, and role examples.