Offer courses and rooms as MCP resources, with two German prompts
A course or room can now be attached to a message rather than fetched: schulcloud://courses/<id> and schulcloud://rooms/<id> carry exactly what get_course and get_room return. Deliberately coarse — a picker lists every resource at once, which suits some twenty courses and not a thousand files. Two prompts, in German because the school is: zusammenfassung summarises a course or room, and pruefungsvorbereitung prepares for an exam with practice questions and a study plan. Each embeds the overview and says where material hides and what cannot be read. Claude Code shaped the details, read from its bundle rather than its docs. It splits prompt arguments on whitespace and drops extra words, so words arrive joined with "_", and courses match by fragments, whole words first, so LF1 is not ambiguous with LF10. Its @ autocomplete shows a resource's description, so the description carries the name. Errors are ProtocolError, because McpError's message prefix is doubled by the client. Verified in interactive Claude Code: @-mention, autocomplete and the prompt commands. 157 tests. Smoke 67/67 live; 69/69 and 67/67 on the local instance. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
30
CLAUDE.md
30
CLAUDE.md
@@ -39,8 +39,8 @@ index.
|
||||
read-only with respect to Schulcloud. Run `smoke` after touching `src/core/`,
|
||||
`src/mcp/` or `src/http/` — the unit tests cover only pure functions.
|
||||
|
||||
Run smoke **both ways**: with `DATABASE_URL` set (57 checks, index-backed) and
|
||||
without (55 checks, live-only). The degradation path is a supported mode, not a
|
||||
Run smoke **both ways**: with `DATABASE_URL` set (69 checks, index-backed) and
|
||||
without (67 checks, live-only). The degradation path is a supported mode, not a
|
||||
fallback nobody exercises.
|
||||
|
||||
Store tests need a database and skip without one:
|
||||
@@ -53,7 +53,7 @@ once put fixtures into real data.
|
||||
## Architecture
|
||||
|
||||
```
|
||||
bin/{http,stdio}.ts ─┬─ mcp/server.ts ── mcp/tools/*
|
||||
bin/{http,stdio}.ts ─┬─ mcp/server.ts ── mcp/tools/*, mcp/{resources,prompts}.ts
|
||||
└─ http/{server,api,auth}.ts /mcp and /api
|
||||
│
|
||||
bin/cli.ts ──HTTP──────────┘ cli/{config,client,sync}.ts
|
||||
@@ -86,6 +86,10 @@ bin/cli.ts ──HTTP──────────┘ cli/{config,client,sync
|
||||
- **`mcp/tools/*.ts`** — tool descriptions are prompts: they are how Claude picks
|
||||
a tool, so they carry the German domain terms (Kurse, Themen, Aufgaben) and say
|
||||
when *not* to use the tool.
|
||||
- **`mcp/resources.ts`, `mcp/prompts.ts`** — courses and rooms as resources a
|
||||
person attaches, carrying exactly `readCourse`/`readRoom`, the functions behind
|
||||
`get_course`/`get_room`; and two prompts. What people read in a picker (labels,
|
||||
prompt texts) is German; what the model reads stays English.
|
||||
- **`context.ts`** — per-session state. Only `/me` is cached, because the school
|
||||
id is on every files-storage path and cannot change for a token.
|
||||
|
||||
@@ -290,6 +294,26 @@ These cost real time to discover; `docs/API.md` has the full list with evidence.
|
||||
saying what is unavailable and what still works.
|
||||
5. Add a check to `scripts/smoke.mjs` and run `npm run smoke` both ways.
|
||||
|
||||
## Resources and prompts
|
||||
|
||||
Claude Code is the client these are tested in, and it shapes them. Read from its
|
||||
bundle (2.1.272), not its docs:
|
||||
|
||||
- **A prompt command's arguments are split on whitespace, and extra words are
|
||||
dropped** (`zipObject(argNames, input.split(/\s+/))`). A value of several words
|
||||
can only arrive joined, so `argumentText` turns `_` back into spaces and reads
|
||||
`-` as "skipped"; course names match by fragments (`resolveTarget`). Any new
|
||||
free-text argument needs the same treatment.
|
||||
- **The @ autocomplete fuzzy-matches `name` but displays `description`**, falling
|
||||
back to the name only when there is none — a description must carry the name.
|
||||
- **An @-mention resolves only URIs from `resources/list`**; a template alone
|
||||
cannot be mentioned. `McpServer` returns every template's listing in one reply
|
||||
and ignores cursors, which suits a few dozen entries and not the file manager.
|
||||
- **Throw `ProtocolError`, not `McpError`**: McpError prefixes its message with
|
||||
"MCP error <code>:", the client prefixes it again, and people read both.
|
||||
- A resource listing that fails yields no entries rather than an error: one
|
||||
refused kind would otherwise cost the whole reply.
|
||||
|
||||
## Environment
|
||||
|
||||
`.env` holds `TSC_URL`, `TSC_JWT_COOKIE`, `MCP_AUTH_TOKEN`. See `.env.example`
|
||||
|
||||
Reference in New Issue
Block a user