Legal AI OS — Model Context Protocol
The MCP Server
Three tools, one resource, one wire. How legal-os exposes its functions to any AI client — and the three walls it hit getting there.
FastMCP · Python 3.12 · Streamable HTTP · Fly.io
01
What it is
A distribution surface, not a feature.
- One client, one server, three tools, one resource.
- Deterministic — no LLM, no database, no external API. Each tool is a plain function.
- The tool’s docstring is its description. Its signature is its schema.
02
The three tools
Structured in, structured out.
clause_risk_check
Clause risk, scored
clause_type + clause_text in. GREEN / YELLOW / RED out, with the flagged terms and a one-line rationale. Keyword rules per clause type — liability, indemnification, IP, confidentiality, termination.nda_triage
NDA carve-out check
NDA text in. Pass / needs-review out, with which standard protections are present, which are missing, and any red flags. Checks confidentiality definition, compelled disclosure, return-or-destroy, survival, and term.
NDA text in. Pass / needs-review out, with which standard protections are present, which are missing, and any red flags. Checks confidentiality definition, compelled disclosure, return-or-destroy, survival, and term.
risk_matrix
Severity × likelihood
Two integers, 1–5, in. A 1–25 score out, mapped to a band — LOW to CRITICAL — with a recommended action. The same programmatic-scoring instinct as the rest of legal-os, in miniature.
Two integers, 1–5, in. A 1–25 score out, mapped to a band — LOW to CRITICAL — with a recommended action. The same programmatic-scoring instinct as the rest of legal-os, in miniature.
legal://playbook
A static resource
Not a tool — a resource. The default review playbook served as plain text at a stable URI. Shows the second MCP primitive alongside the tools: something to read, not something to run.
Not a tool — a resource. The default review playbook served as plain text at a stable URI. Shows the second MCP primitive alongside the tools: something to read, not something to run.
03
The build
Three walls. Each one was a real wall, not a typo.
Wall 1 — the import vanished
ModuleNotFoundError: No module named 'mcp.server.fastmcp'Symptom: crash loop on boot.
mcp 2.0 shipped and deleted the import path. Fix: pin mcp[cli]>=1.28,<2.0. Lesson: a version range that looks safe resolves to a major you never tested.Wall 2 — run() hides its knobs
TypeError: FastMCP.run() got an unexpected keyword argument 'host'Symptom: can’t control the port Fly injects. Fix: build the Starlette app, serve it yourself with uvicorn. Lesson: the high-level path hides the knobs you need the moment you deploy.
Wall 3 — the public host got rejected
Invalid Host header — HTTP 421Symptom: works on
localhost:8000, dies on Fly. Fix: bind host="0.0.0.0". Lesson: transport security is the gap between your laptop and the internet.04
The migration path
The migration path is one line. These tools are toys with production shapes. Swap an inline keyword rule for a call to backend/app/api/routes/, and the MCP becomes the front door to the real platform — the same typed schema, the same structured output, the same audit trail. Connect it with:
claude mcp add --transport http legal-os https://legalos-mcp.fly.dev/mcp