Architecture
- Overview
- Separation of concerns — current and future state
- Repository layout
- The MCP server
- Content adapters
- Skills and skill packages
- The content-object model
- Access control — ODRL, checked before every task
- Work-plan priming (cross-harness)
- Deployment
Overview
The rules behind this page. Architecture describes the shape; the Skills govern the decisions. Adapters against profiles —
content-profiles. Where a new node belongs before you create it —placement. The repository layout and every graph typology —directory-conventions. Composing and verifying the MCP surface —mcp-assemblyandmcp-contract. Where this page and a Skill disagree, the Skill wins.
folio-assistant is an MCP server with a pluggable content adapter layer, a skill system, a typed content-object model, RBAC, and a deploy story. The content it operates on lives in a separate repository — the platform is content-agnostic.
flowchart TD
subgraph Harness[LLM harness · Claude Code / Antigravity / Gemini]
LLM[Agent]
end
LLM <-->|MCP stdio/http| Server[FolioServer · src/server.ts]
Server --> Tools[Core tools<br/>check-deps · skill-fetch · preview · preferences · work_plan_prime]
Server --> Adapter{Content adapter}
Adapter --> Paper[paper adapter<br/>lean · validate · render]
Adapter --> Future[other adapters …]
Server --> RBAC[Access · src/core/rbac.ts + access.ts → ODRL policies/]
Server --> Git[Git helper · src/core/git.ts]
Server --> Feedback[Feedback store · src/core/feedback.ts]
Tools --> Skills[Skill packages<br/>schemas/skills/*]
Paper --> Model[Content-object model<br/>schemas/types.ts · constraints.ts · builders.ts]
Separation of concerns — current and future state
This repo is today a Tool repo and a Content repo in one checkout. Issue #223 plans the split into five composable folio-assistant instances. Child pages carry it:
| page | what it answers |
|---|---|
| Repo taxonomy | What kinds of repository exist — Tool, Test, Content, Consumer — and what each may contain |
| Current state | What is actually in this repo today, measured, and where the mixture is |
| Future state | The five target repos and which directory lands in which |
| Migration plan | Phase 0/I/II/III, the gates, and what is still undecided |
Minimum cat-harness | What survives in the harness once “not self-documenting” is applied as a test |
| Harness instances | What an instance IS — schematics, visualisations, tools; the four directories; the default rendering |
The last two look like they disagree — the minimum says a harness produces
nothing a human looks at, and the instance page says an instance renders by
default. They do not: the requirement is a floor that rises, with
bootstrap exempt from the visualiser and owing its own .json/.jsonld
instead, and cat-harness the layer where the rest begins to apply. See
Where the requirement starts.
The rest of this page describes the architecture as it is now.
Repository layout
| Path | What lives here |
|---|---|
src/ | The MCP server (server.ts), entry point (index.ts), core (git, rbac, cache, feedback, logging), and core tools (tools/) |
adapters/ | Content adapters — paper/ (Lean + LaTeX) and the standalone mcp-server/ |
schemas/ | The content-object model (types.ts, constraints.ts, builders.ts) and per-skill JSON Schemas (schemas/skills/*) |
skills/ | Skill packages (content-lifecycle, authoring-math, authoring-who-smart-guidelines) with their Docker manifests |
content/ | Content pipeline tooling (validators, QA, render helpers) — not content itself |
ui/, viewer/, home_page/ | Web UI, interactive viewer, and the example Pages site |
deploy/ | Deployment (Caddy, docker-compose, provisioning, OAuth) |
docs/ | This documentation site |
.github/ | CI workflows and scripts (build, publish, QA, docs) |
.claude/skills/ | Local agent skills + capability hooks |
The MCP server
FolioServer (src/server.ts) registers the core tools, then asks the active
content adapter to register its tools. It supports two transports — --stdio
(what harnesses launch) and --http (a shared long-running instance). Tool calls
are logged with timing.
Content adapters
A content adapter encapsulates everything type-specific: which artifacts exist,
how to validate them, how to build/render them, and which extra MCP tools to
register. The document adapter (adapters/document/) is the base for prose
folios; the paper adapter (adapters/paper/) extends it and provides Lean lifecycle tools
(lean_setup/build/check/status), validation, and rendering
(paper_render_pdf/html, formula_render). New content types add a new
adapter — see Adding a content type.
Skills and skill packages
A skill is a documented, schema-bounded unit of work (e.g.
lean-formalization). Skills are grouped into packages that declare their
Docker/runtime dependencies via a package-manifest.json. The LLM discovers
skills with skill_list and loads instructions with skill_fetch. The full
list of skills and roles — and how they compose with the LLM (RBAC, capabilities,
requirements) — is on the Skills & roles page; each skill’s
input/output contract is published in the
Skill schema reference.
The content-object model
For papers, content is a tree of typed blocks validated at runtime with Zod:
schemas/types.ts—Block,Section,Chapter,Paper, and block kindsschemas/constraints.ts— Zod schemas and constraint rulesschemas/builders.ts— validated constructors (definition(),theorem(), …)
These are documented in the generated TypeScript API reference.
Access control — ODRL, checked before every task
There is one permission system, and it is W3C ODRL 2.2 (issue #1180): actions
in skills/permissions/permissions.json, grants in policies/*.jsonld,
evaluated by permits() / decide() in schemas/odrl.ts. Two callers ask it:
- The BPMN executor, before every task and decision
(
src/workflow/authorize.ts): is the actor authenticated, eligible for the lane’s role, permitted toperform-taskhere, and allowed to touch the content? Advisory today: adenyor a role mismatch refuses, andunknownis recorded. - The HTTP routes, through
src/core/rbac.ts: each route names the action it performs (content-authoring,review-comments,adjudication), and the auth-gateway’s sessions are declared actors whose grants arepolicies/http-gateway.jsonld. Hereunknownrefuses.
Until issue #1207 (2026-09-23), rbac.ts was a separate viewer < collaborator
< owner ladder and the executor checked nothing. The discipline is the
task-authorization
skill.
Work-plan priming (cross-harness)
The work-plan is stored in beans and surfaced three ways so any harness is
primed identically:
AGENTS.md— static discipline, read natively by every agent.SessionStarthook — each harness runs the sharedscripts/session-start-coord-sweep.shprimer.work_plan_primeMCP tool — live priming for any MCP-connected agent.
See docs/folio-assistant-migration.md for the full cross-agent design.
Deployment
deploy/ contains a Caddy reverse-proxy template, docker-compose.yml, a
provisioning script, Google OAuth setup, and a self-update script for running a
shared HTTP instance. The skill-package Docker manifests aggregate apt/pip/npm
dependencies into a single image per active package set.