Skip to main content
Glama
Jonvoge
by Jonvoge
README.md
# Canon MCP

The **serving layer** for Canon governed context repositories. Reads a `canon-context-accelerator`
repo at runtime and exposes its domain knowledge to AI agents over the MCP protocol, executing
DAX and SQL queries under the user's identity via Entra OBO.

```
canon-context-accelerator (context layer)
  domains/<slug>/*.yaml,*.md   ──git read (60s TTL)──►  canon-mcp (this repo)
  _compiled/_index.json                                   single container
  _compiled/bundles/<domain>.md                           6 MCP tools
  .canon-cache/<d>/<connector_id>/schema.json             OBO → Fabric
```

**Architecture invariant:** the image carries code, not content. Anything a serving path reads
must be readable from git at runtime. Content changes are a push, not a redeploy.

---

## Tools

| Tool | Description |
|---|---|
| `list_domains` | List all Canon domains from the compiled index |
| `get_domain_context` | Full grounding bundle for a domain (call before any query) |
| `query_measures` | Compose and execute SUMMARIZECOLUMNS DAX server-side (governed) |
| `run_dax` | Raw DAX escape hatch — requires justification, logged |
| `execute_sql` | Governed SQL fallback against warehouse connectors |
| `whoami` | Diagnostics: UPN, OBO health, config completeness |

---

## Environment variables

### Repo pointer (required for remote mode)

| Variable | Description |
|---|---|
| `CANON_REPO_PROVIDER` | `github` or `ado` — enables remote git reads; omit for local file mode |
| `CANON_REPO_OWNER` | GitHub org / ADO org |
| `CANON_REPO_NAME` | Repository name |
| `CANON_REPO_TOKEN` | PAT with read:contents scope |
| `CANON_REPO_BRANCH` | Branch to read from (default: `main`) |
| `CANON_REPO_ADO_ORG` | ADO org name (ADO only) |
| `CANON_REPO_ADO_PROJECT` | ADO project name (ADO only) |

### Auth (required for HTTP mode)

| Variable | Description |
|---|---|
| `CANON_AUTH_TENANT_ID` | Entra tenant ID |
| `CANON_AUTH_CLIENT_ID` | App registration client ID |
| `CANON_AUTH_CLIENT_SECRET` | Client secret (for OBO token acquisition) |
| `CANON_MCP_BASE_URL` | Public HTTPS URL of this server (used in OAuth metadata) |

### Transport / runtime

| Variable | Description |
|---|---|
| `CANON_MCP_PORT` | HTTP port (default: `8000`) |
| `CANON_MCP_REFRESH_SECRET` | Shared secret for `POST /refresh`. **Required** when `CANON_REPO_PROVIDER` is set; the server refuses to start without it. |
| `CANON_REPO_ROOT` | Local path to context repo (local mode only) |

**Transport is streamable HTTP only** (no stdio, no SSE).

**Env vars carry secrets and repo-pointer config only — never topology.**
Workspace IDs, dataset IDs, and connector topology come from `scan-config.yaml`
in the context repo, not from environment variables.

---

## Local mode

```bash
CANON_REPO_ROOT=/path/to/canon-context-accelerator \
uv run python -m serving.server
```

## HTTP / Container App mode

```bash
CANON_REPO_PROVIDER=github \
CANON_REPO_OWNER=YourOrg \
CANON_REPO_NAME=canon-context-accelerator \
CANON_REPO_TOKEN=ghp_... \
CANON_AUTH_TENANT_ID=... \
CANON_AUTH_CLIENT_ID=... \
CANON_AUTH_CLIENT_SECRET=... \
CANON_MCP_BASE_URL=https://your-container-app.azurecontainerapps.io \
CANON_MCP_REFRESH_SECRET=... \
uv run python -m serving.server
```

---

## Development

```bash
uv sync --extra dev
uv run pytest
uv run ruff check .
```

CI runs `ruff check` + `pytest` on every PR via `.github/workflows/test.yml`.

---

## Two-repo contract

canon-mcp consumes these artifacts from the context repo (never baked into the image):

| Artifact | Produced by | Used for |
|---|---|---|
| `_compiled/_index.json` | `compile.yml` | `list_domains`, domain enum |
| `_compiled/bundles/<domain>.md` | `compile.yml` | `get_domain_context` |
| `domains/<d>/models/<m>.yaml` | humans + scan | `query_measures` validation |
| `domains/<d>/examples.yaml` | humans | few-shot grounding in bundle |
| `connections/<c>.yaml` | humans | `execute_sql` allow-list |
| `scan-config.yaml` → `domains[].models[]`, `connectors[]` | humans | connector resolution |
| `.canon-cache/<d>/<connector_id>/{schema,profiles}.json` | `scan.yml` | model schema, dim profiles |

Changing any of these shapes is a coordinated two-repo migration.