CLAUDE.md · diff
git:20260930.1192099 to git:20261002.14fb51d
8 added, 6 removed. Audit A to A.
# CLAUDE.md
Guidance for developing the Canvas MCP server. Agents *using* the server: see [AGENTS.md](./AGENTS.md).
Design: [internal/architecture.md](internal/architecture.md). Long-form rules and the reasons behind them: [internal/dev-reference.md](internal/dev-reference.md). Completed work: [internal/project-history.md](internal/project-history.md), `CHANGELOG.md`.
Codex reads `AGENTS.md`, not this file; its "Developing this server" section points here, so keep development rules in this file only. Paths, flags, symbols and issue states below were checked on 2026-09-30.
## Commands
- Install `uv pip install -e .`; run `canvas-mcp-server` (`--test`, `--config`); `.env` holds `CANVAS_API_TOKEN` and `CANVAS_API_URL`.
- Before committing: `uv run python -m pytest tests/ -v -rf`. TypeScript changes: `npm test` and `npm run build`.
## Git Workflow - ASK FIRST
- Before a feature or significant change, ask: feature branch or `main`? Defaults: new tool `feature/`, bug `fix/`, docs or typo may go straight to `main`. Prefixes: `feature/`, `fix/`, `docs/`, `refactor/`.
- The primary checkout stays on `main`, clean and read-only. One PR = one sibling worktree `canvas-mcp-<slug>` from `origin/main`; never reuse it for another issue; remove it and the branch (local and remote) after merge.
- After a sibling PR merges, rebase surviving worktree branches and rerun tests. Before tagging, run the suite in the primary checkout on the merged commit (its `.env` sets a token that worktrees and CI lack).
- `main` requires a PR plus `test-enhancements` and `lint`; the admin can bypass the review requirement.
- Run `./scripts/install-hooks.sh` once per clone. `fixes|closes|resolves #N` mid-sentence in a commit or PR body closes the issue on merge: rephrase (`closed [issue 172]`) or set `ALLOW_CLOSING_KEYWORD=1`. A trailer that opens a line (`Closes #173`) is allowed.
- Release steps and publish-race fixes: [internal/release-checklist.md](internal/release-checklist.md). Breaking changes need a minor bump.
## Coding Standards
- Type hints on every function; PEP 604 unions (`X | Y`), enforced by ruff UP.
- Tools use `@mcp.tool()` with `@validate_params`, and every tool carries annotations (`tests/test_tool_metadata.py` fails a bare decorator).
- All Canvas calls are async and go through `make_canvas_request()`; paginate every list endpoint.
- Course identifiers are `str | int` resolved with `get_course_id()`; dates go out through `format_date()`.
- Canvas POST/PUT needs `use_form_data=True`, `/conversations` included.
- Errors: dict tools return an `"error"` key; string tools return `"Error ..."`. `modules.py` and `accessibility.py` return JSON-stringified errors; keep the local convention.
- Privacy: student IDs preserved, names anonymized at the client layer (`_should_anonymize_endpoint()`); a new endpoint must be checked against the tier rules.
- Show course codes, not IDs, in user-facing output; handle published and unpublished states.
## Testing and behavioral evidence
- New tools need meaningful automated coverage: success, failure, boundary and safety behavior.
- A reproducible bug gets a failing regression first; watch it fail for the intended reason.
- Assert outgoing request contracts and forbidden side effects, from an independent requirement, not from the implementation's own output.
- Use a real client with controlled HTTP transport when the risk is serialization, retries, pagination or error classification.
- Never send live Canvas writes to verify a patch. Never weaken an assertion to get a green run.
- Lean/TLA+ (`verify/`) only for consequential state, ownership or termination invariants; pair with implementation regressions.
- Report what was verified, what failed and what was skipped.
## Documentation Maintenance
- Source of truth: agents `AGENTS.md`; humans `tools/README.md`; machine `tools/TOOL_MANIFEST.json`; `README.md` is the entry point.
- New tool: update `tools/README.md`, then `AGENTS.md`, then `TOOL_MANIFEST.json`. Touch `README.md` only for a major feature. No tool usage docs in this file.
- `docs/` is the public Cloudflare Pages root and deploys manually (`wrangler pages deploy docs/`); nothing internal goes there.
- `internal/` is deny-by-default in `.gitignore`; un-ignore only a file meant to be public. `internal/session-history.md` stays untracked.
- Triage briefs are tracked: never record a collaborator's affiliation, evaluation status, timeline or competing products. Name the person and the technical issue only.
## Hosted Deployment
- A private, Entra-gated Azure instance serves Gies course staff. Its URL, app IDs, deploy details and key holders stay out of this repo; they live in the gitignored `internal/ops-hosted.local.md`.
- HTTP mode: each caller sends `X-Canvas-Token`; `CANVAS_API_TOKEN` must never be set (startup guard); read-only unless `ALLOWED_WRITE_TOOLS` is set.
- Merging to `main` does not deploy production. It deploys on a `v*` tag or `gh workflow run deploy-prod.yml --ref main`; `staging` deploys on push.
## Adoption numbers
- Never print a PyPI download count. Quote stars, forks and contributors, re-pulled from the GitHub API that day, with the date.
- Do not repeat "over 18,000 clones" or "UMich selected it as sole candidate" without a primary source.
- Do not imply a campus security review: the only Illinois artifact is Adam King's LRA, and the public hosted server is retired.
## Current Focus
- [ ] **#157** sandbox egress is mitigated, not closed (self-hosted only; `execute_typescript` is disabled on hosted). Needs an egress proxy or network namespace.
- [ ] **#236** OAuth2 developer-key flow: additive only, blocked on admin access to pilot a scoped key.
- [ ] **#172** Canvas Quizzes tools: blocked on a New-Quizzes-enabled sandbox (PR #191 was closed as unverifiable).
- - [ ] **#418 to #421** (raw dates, guarded edits, honest pagination, anonymous discussions); #420 includes an `assign_peer_review` placeholder write past 100 submissions.
+ - [x] **#418 to #420** shipped to `main` 2026-10-01 (#438, #437, #436); **#421** part 1 shipped (#435), still open for part 2 (GraphQL read path) and an `update_discussion_topic` refusal on anonymous topics.
+ - [ ] **Release v1.14.0** (minor): `CHANGELOG.md` Unreleased covers #433, #435, #436, #437, #438. `assign_peer_review` now requires a numeric `reviewee_id`; guarded edits are mock-verified only, so try one on a real page first.
- [ ] GHSA-hmr8: publish and request a CVE. GHSA-7pp5: decide (in triage since 07-04).
- [ ] Watch, no owner: Agent Plugins packaging (blocked on credential delivery; triggers in project-history).
## Roadmap
- [ ] Grok/xAI integration research and pilot.
## Backlog
- [ ] Module templates; bulk module creation from JSON/YAML; module duplication across courses.
- [ ] Page templates; bulk page creation from markdown; page versioning tools.
- [ ] 2026-09-06 security review follow-ups: check `docs/superpowers/plans/2026-09-06-security-review-followups.md` for items PR #360 did not cover.
## Session Log
> Full history: `internal/session-history.md`, local-only and untracked since 2026-08-20. Do not re-add it to git.
- ### 2026-09-27 — v1.13.0 released: GHSA-hmr8 fixed, production pinned to releases, hosted service read-only
- - **Merged:** #395, #387, #390, #417 (production deploys only on a tag or manual run), #422 (`mcp-remote --header-file`), #425.
- - **GHSA-hmr8 fix** (`3f23ed0`): `core/tool_policy.py` + `ALLOWED_WRITE_TOOLS`; `send_conversation` always previews; `get_conversation_details` never marks read. Codex security review clean at round 3.
- - **v1.13.0** tagged on `c3207d3`; all five channels verified. Advisory patched version set to 1.13.0, still a draft.
- - **Next:** publish GHSA-hmr8; decide GHSA-7pp5; fix the #420 placeholder write; `scripts/rotate-canvas-token.mjs` does not know `--header-file`.
+ ### 2026-09-30 06:44 → 2026-10-01 22:49 CDT — six PRs merged, CLAUDE.md trimmed, issue agents
+ - **Merged:** #432 (`pyjwt` 2.15.1) and #434 (`urllib3` 2.8.0), both lockfile-only after new advisories turned the dependency scan red; #423 (@lindsay-cheng, `missing` flag); Dependabot #427 to #429; #433 (@papatistos, group discussions, plus a numeric `group_id` check); and agent PRs #435 (#421 part 1), #436 (#420), #437 (#419), #438 (#418). Codex gated #436 (2 rounds) and #437 (3 rounds); 7 real defects fixed before merge.
+ - **Mistake:** #435 merged with red tests (merge not gated on checks); fixed on `main` in `8c3ff15`. Run the suite as CI does: `CANVAS_API_URL= CANVAS_API_TOKEN= uv run --all-extras pytest tests/ -q` (bare `pytest`, no credentials).
+ - **Docs:** CLAUDE.md 497 → 78 lines (`internal/dev-reference.md`, `internal/project-history.md`); AGENTS.md "Developing this server" pointer for Codex; reference claims checked and dated.
+ - **Housekeeping:** 74 stale remote branches deleted (10 with local backups under `refs/backup/`); fleet hooks hidden locally (`info/exclude` + skip-worktree on `.githooks/commit-msg`); closed #374, #375, #384. MCP events assessment in `internal/research/2026-09-30-openai-mcp-events.md` (recommendation: wait).
+ - **Next:** review #439 (@papatistos, #421 part 2 via GraphQL, opened 10-01 evening, fork CI needs approval); release v1.14.0; publish GHSA-hmr8 + CVE; decide GHSA-7pp5; #421 part 2; `fastmcp` 4.0.3 → 4.0.10 patch and the `mcp` 2.2.0 review (#416); #391 Windows `uv` design.