Skip to main content
Glama
felix-e

maester-mcp

by felix-e
README.md
# Access receipt for MCP

A reference lab for enforcing identity-aware tool access in an MCP server.

This is a distilled version of a disposable-tenant lab. The lab question was practical: if an AI agent can reach enterprise security evidence, what proves the caller is allowed to use each tool? The answer here is deliberately plain:

1. verify the caller or agent route,
2. map identity evidence to local permissions,
3. expose only the matching MCP tools,
4. test the refusal cases.

This is not a production platform. It is a runnable pattern for access receipts: identity in, scoped tools out, and denial paths that do useful work.

## Architecture

```text
identity route -> token / authorization evidence -> verifier -> permissions -> MCP tool tags -> read-only data
```

The same permission ladder is used for all routes so the interesting variable is the identity plumbing, not the downstream tool contract.

Design constraints:

- the server exposes read-side evidence only,
- no arbitrary SQL tool is published,
- raw evidence is behind `maester.raw`,
- analysis tools return summary-shaped fields, not raw result payloads,
- Auditor is powerful, not magical; mutation remains denied.

## Permission model

| Role | Permissions | Typical tools | Explicit refusals |
| --- | --- | --- | --- |
| Reader | `maester.read` | `whoami`, `list_runs`, `get_run_summary` | failed-test analysis, raw detail |
| Analyst | `maester.read`, `maester.analyse` | Reader tools plus `get_failed_tests`, `get_test_history`, `compare_runs` | raw result detail |
| Auditor | `maester.read`, `maester.analyse`, `maester.raw` | Analyst tools plus `get_result_detail` | mutation, arbitrary SQL |

Negative tests are part of the design, not decoration. If Reader can analyse, Analyst can read raw evidence, or Auditor can mutate tests, the architecture has become a suggestion box.

## Routes covered

- **Entra RBAC**: app roles become local Maester permissions.
- **Copilot Studio / Agent ID**: the agent route still resolves to backend permissions.
- **Okta XAA / ID-JAG**: authorization evidence travels to the MCP server and is verified there.

## Raw-ish examples

`examples/` contains a deliberately small set of sanitised raw lab artefacts. These are the files that help a reader understand the access pattern; the rest of the lab trail was noise with better timestamps.

```text
examples/entra-rbac/
  resource-app-create-body.raw.example.json       # resource app manifest shape: scopes, app roles, token version
  resource-app-current-summary.raw.example.json   # resulting resource/service-principal settings
  delegated-rbac-setup.raw.example.json           # delegated client, consent and role-assignment shape

examples/agent-id/
  agent-blueprint-created.raw.example.json        # Agent ID / blueprint object shape
  connector-oauth-client-created.raw.example.json # OAuth client used by the connector/runtime path
  redirect-and-user-role-assignments.raw.example.json
  copilot-mcp-connector-created.raw.example.json  # Copilot Studio MCP connector shape

examples/okta-xaa/
  custom-as-idjag-contract.raw.example.json       # authorization-server and ID-JAG contract shape
  role-matrix-passed.raw.example.json             # Reader / Analyst / Auditor validation result
  runtime-real-idjag-reader-smoke.raw.example.json
```

The examples are not copy-paste deployment recipes. They are shape references: what objects existed, which fields mattered, and where authorization evidence showed up.

## Run locally

```bash
python -m venv .venv
. .venv/bin/activate
pip install -e '.[dev]'
maester-ingest --source data/source --db data/maester.duckdb
python -m pytest tests -q
python scripts/duckdb/verify_demo.py
```

Start the MCP server with auth disabled:

```bash
MAESTER_MCP_AUTH_PROFILE=none maester-mcp
```

Or with synthetic local demo tokens:

```bash
MAESTER_MCP_AUTH_PROFILE=synthetic maester-mcp
# reader-token, analyst-token, auditor-token are local examples only
```

## Sanitisation

The included data is a sanitised Maester-shaped demo set. The original private seed database is not included. `data/maester.duckdb` is generated locally from `data/source/` and ignored by git.

The sanitisation pass replaces organisation names, user-like values, GUIDs, email-shaped values, local paths and policy names with deterministic demo values. Run the verification gate:

```bash
python scripts/duckdb/verify_demo.py
```

Publishing rule: configuration shape is useful; raw tenant history is not.

## What is intentionally absent

- no live tenant credentials,
- no private keys,
- no production evidence,
- no arbitrary SQL tool,
- no write-side Maester controls,
- no historical lab logs, agent transcripts, deployment state, BOX notes or tenant-specific handoffs.

The useful part is the boundary, not the plumbing souvenirs.

## Repository map

```text
src/maester_mcp/        FastMCP server, auth verifiers, read-only DuckDB access
tests/                  permission, transport, ingestion and denial tests
sql/                    schema, views and verification SQL for demo evidence
data/source/            sanitised Maester-shaped source JSON
data/maester.duckdb     generated local demo database, ignored by git
examples/               sanitised raw-ish config and lab artefact shapes
```

## Related notes and videos

- Project note: https://iam.felixelliott.com/posts/mcp-reference-architecture/
- Intro: https://youtu.be/mSMquGtJ2Tk
- Entra RBAC: https://youtu.be/8XjDbdLgbRw
- Agent ID: https://youtu.be/vh8lkvPZx0k
- Okta XAA: https://youtu.be/Z7I1Y72CXOc
- Code walkthrough: https://youtu.be/IWNy2_jZ0VQ

TDQS

A4/5.0

Scored across 7 tools

Disambiguation5/5

Each tool targets a distinct aspect of the Maester run data: identity, run listing, run summaries, failed tests, test history, run comparison, and raw detail. No overlap in purpose or ambiguity in selecting the appropriate tool.

Naming Consistency5/5

All tools use snake_case with a consistent verb_noun pattern (e.g., list_runs, get_run_summary, compare_runs), with whoami as a minor but acceptable deviation. The naming is predictable and easy to infer.

Tool Count5/5

With 7 tools, the server is well-scoped for a read-only analysis tool covering identity, runs, summaries, failures, history, comparison, and details. Each tool earns its place without excess.

Completeness3/5

The server lacks a tool to enumerate all tests or retrieve complete test results beyond failures, leaving a significant gap in understanding overall test outcomes. Compare_runs provides summary changes but not per-test detail, so agents cannot fully assess test coverage.

Maintenance

ActivitySlowing
ResponsivenessNo issues