Architecture

  1. Overview
  2. Separation of concerns — current and future state
  3. Repository layout
  4. The MCP server
  5. Content adapters
  6. Skills and skill packages
  7. The content-object model
  8. Access control — ODRL, checked before every task
  9. Work-plan priming (cross-harness)
  10. 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-assembly and mcp-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:

pagewhat it answers
Repo taxonomyWhat kinds of repository exist — Tool, Test, Content, Consumer — and what each may contain
Current stateWhat is actually in this repo today, measured, and where the mixture is
Future stateThe five target repos and which directory lands in which
Migration planPhase 0/I/II/III, the gates, and what is still undecided
Minimum cat-harnessWhat survives in the harness once “not self-documenting” is applied as a test
Harness instancesWhat 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

PathWhat 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 kinds
  • schemas/constraints.ts — Zod schemas and constraint rules
  • schemas/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 to perform-task here, and allowed to touch the content? Advisory today: a deny or a role mismatch refuses, and unknown is 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 are policies/http-gateway.jsonld. Here unknown refuses.

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:

  1. AGENTS.md — static discipline, read natively by every agent.
  2. SessionStart hook — each harness runs the shared scripts/session-start-coord-sweep.sh primer.
  3. work_plan_prime MCP 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.


Table of contents


folio-assistant — code under the Apache License 2.0, documentation under CC BY 3.0. Generated schema and API references are rebuilt from source on every push to main.

deployed b08c961 · 2026-10-23T23:11:09Z