Repo governance as tests. Point a YAML rondo at a GitHub repository and assert it meets policy: it ships a license, it is documented, it has real history. Read-only, no LLM.
This drives the GitHub MCP server with Ocarina. Each check is a declarative assertion you can commit, reuse across repos, and run in CI.
Each rule is a tool call and an expect, reading structured JSON straight from the GitHub MCP server. The rondo fails the run the moment a rule fails, and behaves the same on your laptop and in CI. It is the policy itself: declarative, reviewable, versioned next to your code, and run like any other test.
- Ocarina:
go install github.com/msradam/ocarina@latest - Node.js (for
npx). - A GitHub token with read access:
export GITHUB_PERSONAL_ACCESS_TOKEN=$(gh auth token) # or a fine-grained read-only PATgit clone https://github.com/msradam/github-mcp-ocarina
cd github-mcp-ocarina
ocarina play rondos/governance.yamlPoint it at your own repo with -e:
ocarina play rondos/governance.yaml -e owner=your-org -e repo=your-repoOr use the Makefile: make smoke, make governance, make audit, make test.
Ocarina has a --safe flag that refuses any tool the server does not mark read-only (readOnlyHint), so pointing a read-only rondo at a real organization cannot mutate it. It is a guardrail, not a hard security boundary: MCP annotations are advisory, so --safe protects you from your own mistakes, not from a server that lies about its tools.
It only helps when the server annotates its tools. The npx reference server used here does not set readOnlyHint on get_file_contents or list_commits, so --safe would refuse them. Check any server before relying on it:
ocarina docs npx -y @modelcontextprotocol/server-github # look for [readonly] under each toolGitHub's official github-mcp-server and servers like the filesystem reference server do annotate, and that is where --safe earns its place.
rondos/motifs/repo-checks.yaml is a motif: a reusable, parameterized fragment that holds the policy once. rondos/repo-audit.yaml includes it for each repository, passing owner and repo with with:. The checks live in one place; the audit just lists the targets.
rondo:
- name: audit modelcontextprotocol/servers
motif: motifs/repo-checks.yaml
with: { owner: modelcontextprotocol, repo: servers }
- name: audit modelcontextprotocol/python-sdk
motif: motifs/repo-checks.yaml
with: { owner: modelcontextprotocol, repo: python-sdk }ocarina play rondos/repo-audit.yamlAdd a repo by adding two lines, not by copying the checks.
| Rondo | What it asserts |
|---|---|
rondos/smoke.yaml |
The token works and the server can read a file. |
rondos/governance.yaml |
The target repo ships a LICENSE, has a README.md, and has commit history. |
rondos/repo-audit.yaml |
The same policy, applied to several repos by reusing one motif. |
This repo runs the npx reference server (@modelcontextprotocol/server-github) because it needs no Docker, which keeps clone-and-run simple. GitHub's official github-mcp-server is the production server and exposes the same idea with a wider tool set; it runs via Docker. The rondos here target the reference server's tool names.
.github/workflows/ci.yml uses the workflow's built-in GITHUB_TOKEN, so the checks run on every push against a public repo with no extra secrets. CI runs the governance check and the multi-repo audit, pinned to Ocarina v0.3.0.
No model in the loop. Each step calls a GitHub MCP tool (get_file_contents, list_commits), grabs a value from the JSON with a gjson path, and asserts it with expect. See the Ocarina docs.
MIT.