@jamestesting888/tum-mcp 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/tum-mcp.md CHANGED
@@ -1,52 +1,50 @@
1
1
  # TUM MCP guide
2
2
 
3
3
  Package: `@jamestesting888/tum-mcp`, CLI: `tum-mcp`, backend: Python 3.13.
4
- Version **0.5.0** provides interactive service and browser selection, local-file
5
- credential keys, richer diagnostics, Cursor/Antigravity adapters, and WSL-backed
6
- Windows client setup. The package supports campus reads and local assignment
7
- assistance; remote-action tools remain disabled.
4
+ Development version **0.6.1 (unreleased)** adds npm package management to the
5
+ read-only course retrieval and five local study workflows in 0.6.0.
8
6
 
9
7
  ## Current capabilities
10
8
 
11
- The campus server preserves all sixteen `moodle_*` tools and the existing Moodle
12
- session/keychain identity. It adds scoped Zulip channel search/topic reads,
13
- released Artemis programming statements, exact-revision visible feedback,
14
- participation-bound workspace preparation, approved container test recipes,
15
- submission previews, and seven portable workflow skills.
16
-
17
- Remote start, push, submit, rebuild, Zulip posting, and DMs are unavailable.
18
- An internal, release-gated executor covers exact-action fingerprints, encrypted
19
- expiring one-use grants, account/scope/credential revalidation, and durable
20
- unknown-outcome receipts. Synthetic tests cover one POST or exact-commit push,
21
- copied/fabricated approvals, changed targets, and lost responses. No MCP write
22
- tool, approval issuer, or preference can open this version's gate.
23
- The internal executor is groundwork for Phase 5; no live-action certification is claimed.
24
- Revision 3 Phase 5 needs a demonstrated trusted approval/credential boundary and
25
- safe practice-exercise verification before those tools can be exposed. A
26
- submission preview never performs a commit or remote action. Native Git/browser
27
- tools available to an unrestricted host remain outside the MCP enforcement
28
- boundary; the server does not claim to prevent every host-level push.
29
-
30
- The verified public origins are `https://artemis.tum.de` and
31
- `https://zulip.cit.tum.de`. Public discovery on 24 October 2026 reported Artemis
32
- 10.4, source revision `d8594cc5fb2f2ec276bd123861be7ff20c1b3cc0`, and Zulip 12.0,
33
- feature level 498, with TUM Shibboleth. No account permission follows from that
34
- public discovery. Provider routes were checked against that Artemis revision.
9
+ The sixteen `moodle_*` tools provide materials, announcements, forums and
10
+ explicitly scoped private-record reads. Artemis adds
11
+ available-course metadata, lectures and learning units, student PDF attachments,
12
+ released non-quiz statements and permitted exact-commit feedback. Zulip provides
13
+ scoped channel/topic search and cited discussion reads.
14
+
15
+ Website access is read-only. There are no public exercise workspace, test-run,
16
+ submission, start, deployment, rebuild or push workflows.
17
+ Local downloads, source inventories, study-site builds and checkpoint storage
18
+ are local operations; they do not post, submit or mark campus content as read.
19
+
20
+ Artemis lecture reads use the student overview/detail endpoints, verify the
21
+ selected course and omit unreleased units and personal completion records.
22
+ `artemis_get_attachment` retrieves a bounded page range from the student PDF,
23
+ which excludes hidden slides, and optionally saves that copy locally. Original
24
+ staff links are never fetched. Video/online links remain cited references;
25
+ unsupported or external content is reported as a retrieval gap.
26
+
27
+ Lecture routes were checked against the
28
+ [Artemis source](https://github.com/ls1intum/Artemis/blob/88824427f7ea38512606824c58b8858e539eb2eb/src/main/java/de/tum/cit/aet/artemis/lecture/web/LectureResource.java)
29
+ and the
30
+ [student file boundary](https://github.com/ls1intum/Artemis/blob/88824427f7ea38512606824c58b8858e539eb2eb/src/main/java/de/tum/cit/aet/artemis/core/web/FileResource.java).
31
+ Synthetic fixtures are the default. Live checks remain private and use explicit scopes.
35
32
 
36
33
  ## Build and test the local package
37
34
 
38
- For normal installation, use `npm install --global @jamestesting888/tum-mcp@0.5.0`
35
+ For normal installation, use `npm install --global @jamestesting888/tum-mcp@latest`
39
36
  and `tum-mcp setup --client codex`. The following commands are for maintainers.
40
37
 
41
38
  ```sh
42
39
  uv sync --locked
43
- uv run ruff check src tests plugin/scripts scripts
44
- uv run ruff format --check src tests plugin/scripts scripts
40
+ uv run ruff check src tests scripts
41
+ uv run ruff format --check src tests scripts
45
42
  uv run pytest -q -m 'not live'
46
43
  node npm/build.mjs
47
44
  npm test
48
45
  npm pack --ignore-scripts --pack-destination dist
49
- npm run test:consumer -- dist/jamestesting888-tum-mcp-0.5.0.tgz
46
+ npm run test:consumer
47
+ npm run test:upgrade
50
48
  ```
51
49
 
52
50
  The npm artifact has an explicit file allowlist, a bundled backend wheel,
@@ -69,20 +67,19 @@ and Python 3.13.15; an existing matching uv, including the verified cached downl
69
67
  is reused. Source checkouts and system
70
68
  Python are unnecessary for consumers. Supported bootstrap targets are macOS
71
69
  arm64/x64, Windows arm64/x64, and glibc Linux arm64/x64; hands-on verification is
72
- recorded separately. New campus sessions use permission-restricted local keys and need no Secret Service.
73
- Clean consumer bootstrap, idempotent setup, diagnostics, and skill validation
74
- passed on macOS ARM64 and in Linux ARM64 and x86_64 containers. The x86_64 check
75
- ran through Docker amd64 emulation on an ARM64 Mac; packaged MCP protocol checks
76
- and an encrypted credential round trip through a real GNOME Secret Service in an
77
- isolated D-Bus session also passed. Real Linux desktop browser sign-in, macOS x64,
78
- and Windows hands-on checks remain open. New storage and terminal behavior need
79
- the current checkout’s test evidence; earlier native-store results do not certify them. The installer targets glibc Linux
80
- x86_64/amd64 and aarch64/ARM64.
81
-
82
- `update` provisions the backend bundled in the invoked package and preserves
83
- older runtime directories. To obtain a newer public package after publication,
84
- invoke its reviewed version explicitly. It does not publish a release or replace
85
- customized client skills silently. If interrupted setup leaves `setup.lock`,
70
+ recorded separately. Campus sessions use permission-restricted local keys.
71
+ CI checks clean installation, updates, removal, MCP discovery and synthetic
72
+ browser workflows on macOS, Windows, Linux and both WSL engines. See the
73
+ [development guide](development.md#checks) for the verification layers.
74
+
75
+ `update` downloads and validates the latest release, replaces the invoked npm
76
+ installation and refreshes previously configured clients. `update --local`
77
+ refreshes the installed version without npm. Retained runtimes keep pinned
78
+ clients working, and newer installed versions are not silently downgraded.
79
+ [Update and removal behavior](setup.md#update-and-remove) covers ownership
80
+ receipts, conflict handling and explicit cleanup. `uninstall` removes managed
81
+ clients and npm; `--client NAME` or `--keep-package` retains the package.
82
+ Customized files and saved data are preserved. If interrupted setup leaves `setup.lock`,
86
83
  confirm that no setup process is running before removing that directory locally.
87
84
  An invalid or incomplete runtime is never selected for serving. Failed validation
88
85
  clears its ready marker so explicit setup can rebuild it; older runtimes remain
@@ -92,7 +89,7 @@ rerun its setup to restore that runtime, reviewing any client configuration diff
92
89
  For platforms outside the bootstrap targets, the source installation needs
93
90
  Python 3.13+, uv, and Git: run `uv sync --locked`, then
94
91
  `uv run tum-mcp setup --client generic` from the checkout. An installed Chromium browser is needed for
95
- browser onboarding. See [setup](setup.md) for browser choices and WSL. Docker is needed only for isolated assignment tests.
92
+ browser onboarding. See [setup](setup.md) for browser choices and WSL.
96
93
 
97
94
  ## Connections and scopes
98
95
 
@@ -101,17 +98,16 @@ tum-mcp connect
101
98
  tum-mcp status
102
99
  tum-mcp discover artemis ACCOUNT_ID
103
100
  tum-mcp discover zulip ACCOUNT_ID
104
- tum-mcp scope artemis ACCOUNT_ID --course COURSE_ID --sensitive --workspace
101
+ tum-mcp scope artemis ACCOUNT_ID --course COURSE_ID --sensitive
105
102
  tum-mcp scope zulip ACCOUNT_ID --channel CHANNEL_ID
106
103
  ```
107
104
 
108
105
  `connect` takes no arguments or connection options. Move with up/down, toggle
109
106
  services with Space, and press Enter. Choose a browser when more than one is
110
107
  installed. Only selected tabs open in one temporary browser context. Moodle,
111
- Artemis and Zulip all follow this flow; no separate Moodle login is needed.
108
+ Artemis and Zulip all follow this flow.
112
109
  Sign-in/MFA stays in the browser. `TERM=dumb` uses numbered prompts. Ctrl+C
113
- cancels and retains completed connections. Old service/campus arguments and
114
- `--service`/`--auth` flags are rejected before any browser opens.
110
+ cancels and retains completed connections.
115
111
 
116
112
  Detection covers Chrome, Edge, Brave, Chromium, Vivaldi, Opera and selected
117
113
  preview channels. Set `TUM_MCP_BROWSER` to an executable path for another
@@ -121,48 +117,30 @@ Only service-owned cookies are saved; identity-provider cookies are discarded
121
117
  when the temporary browser closes. Existing browser profiles are never imported.
122
118
 
123
119
  Zulip uses its read-only `/json/` routes with the browser session; no API key is
124
- required. Reconnect through the same selector when a session expires. Existing
125
- API-key records remain readable for compatibility, but key entry is not part of
126
- onboarding. See [Zulip's dispatcher](https://github.com/zulip/zulip/blob/12.0/zerver/lib/rest.py).
127
- Browser login and Artemis Git access remain
128
- separate capabilities. Existing scopes survive reconnection; new identities start
129
- with empty scopes. Private channels additionally require `--private-channel ID` while
130
- remaining in `--channel ID`. `--sensitive` enables own Artemis participation and
131
- feedback reads; `--workspace` separately enables preparation under the configured
132
- workspace root. Neither authorizes submission.
133
-
134
- On 24 October 2026, both saved browser-session credentials passed identity
135
- verification through the actual MCP process. Scoped Artemis exercise listing and
136
- statement retrieval, plus a bounded Zulip channel search, passed. No participation
137
- was started, code pushed, message sent, or read flag changed by these checks.
120
+ required. Reconnect through the same selector when a session expires.
121
+ See [Zulip's dispatcher](https://github.com/zulip/zulip/blob/12.0/zerver/lib/rest.py).
122
+ Existing scopes survive reconnection; new identities start with empty scopes.
123
+ Private Zulip channels require `--private-channel ID` as well as `--channel ID`.
124
+ `--sensitive` enables own Artemis participation/feedback reads. It grants no
125
+ website mutation or local execution permission.
138
126
 
139
127
  Settings live outside the installed package in `TUMMCP/config/preferences.json`.
140
- The default data directory is beside the legacy `TUMMoodle` directory on each OS.
141
- Existing Moodle data and its native-store identity are retained in place. New
142
- campus connections deliberately do not read or migrate native-store secrets;
143
- reconnect each service once to use local-file storage. The combined MCP process
144
- uses that storage for all `moodle_*` compatibility tools. The separate Moodle-only
145
- installer keeps its native-store default.
146
- Previously imported Artemis Git tokens also need re-importing into the new store;
147
- browser sign-in does not import them or open the old native store.
148
-
149
- New Moodle sessions live under `TUMMoodle/local-session/`; campus credentials live
150
- under `TUMMCP/credentials/local/`. Each directory has an independent `session.key`.
128
+ Moodle sessions live under `TUMMoodle/local-session/`; Artemis and Zulip credentials
129
+ live under `TUMMCP/credentials/local/`. Each directory has an independent `session.key`.
151
130
  Encrypted records and keys have owner-only permissions: 0700 directories / 0600
152
131
  keys on POSIX; protected current-user DACLs on Windows through PowerShell. No
153
- administrator privileges are requested. This avoids Keychain/Secret Service
132
+ administrator privileges are requested. This avoids native credential-store
154
133
  prompts but protects less than a native store. A copy of both ciphertext and key
155
134
  is sufficient to decrypt a session. Do not share, sync publicly, or package them.
156
135
 
157
136
  Missing or damaged keys fail closed. Restore the matching key from a private
158
137
  backup. To start fresh, stop clients and move the affected **local storage folder**
159
- to a private backup before reconnecting; this also resets its stored Git tokens.
160
- Never remove the legacy Moodle directory or downloads to repair a login.
161
- Native-store records remain available to the older installation for rollback.
138
+ to a private backup before reconnecting. Keep downloaded course materials when
139
+ repairing a login.
162
140
 
163
141
  Zulip message text is retrieved live and is not persisted. Retrieved excerpts may
164
- be transmitted to the coding agent's configured AI provider. Moodle's existing
165
- document cache retains its documented plaintext/expiry behavior.
142
+ be transmitted to the coding agent's configured AI provider. Moodle caches
143
+ documents and extracted text locally in plaintext.
166
144
 
167
145
  Course nicknames can be mapped in `mappings` using explicit per-service IDs.
168
146
  The tools resolve one exact mapping; ambiguity never grants broader access.
@@ -171,75 +149,64 @@ The tools resolve one exact mapping; ambiguity never grants broader access.
171
149
 
172
150
  `doctor` reads local configuration and checks runtime prerequisites. It does not
173
151
  open a browser, contact campus services, decrypt sessions, or read native stores.
174
- Saved sessions are **Not verified** until checked. Missing optional Git/Docker
175
- capabilities do not prevent course reads. Client registration is distinguished
152
+ Saved sessions are **Not verified** until checked. Git and Docker are not
153
+ prerequisites. Client registration is distinguished
176
154
  from activation; the host must confirm a running MCP connection after setup.
177
155
 
178
156
  Use `doctor --live` for bounded authenticated identity checks on saved local
179
- sessions. This reads Moodle's course overview and Artemis/Zulip identity only;
157
+ sessions and optional GitHub CLI authentication. Campus checks read Moodle's
158
+ course overview and Artemis/Zulip identity only;
180
159
  it does not broaden content scopes. `doctor --json` emits schema-versioned JSON
181
160
  without terminal styling; combine both flags for live automation. Paths appear
182
161
  in JSON for troubleshooting. Default diagnostics do not create state folders.
183
162
 
184
- ## Assignment workspaces and tests
163
+ ## Study workflows
185
164
 
186
- The user requests `artemis_prepare_workspace` with explicit account/course/
187
- exercise IDs and graded/practice mode. Missing participation returns
188
- `ACTION_REQUIRED`; preparation never starts it. Offline IDE access must be
189
- affirmatively enabled. Import an existing participation-scoped Git token locally:
165
+ `read-content` retrieves course information across the three websites. `sync-study`
166
+ uses read-only local status to initialize an explicitly chosen available course or
167
+ reconcile an existing project. Incomplete state is preserved for recovery.
168
+ `prepare-study` creates or extends its local interactive site. See the
169
+ [study guide](study.md) for examples, state structure and helper commands.
190
170
 
191
- ```sh
192
- tum-mcp workspace connect-git ACCOUNT_ID COURSE_ID EXERCISE_ID
193
- ```
194
-
195
- The hidden clone URL must match the server-reported participation repository.
196
- It is sanitized before storage/output. A narrow Git credential helper supplies
197
- the token only to that HTTPS repository. Clone uses no inherited credential
198
- helpers, external Git protocols, hooks, submodules, or dependency installers.
199
- Existing unrelated/dirty work is preserved. Local receipt files contain IDs,
200
- workspace, mode, branch, and revision, never tokens.
201
-
202
- Configure `test_recipes` locally with a unique `name`, exact `account_id`,
203
- `course_id`, an image pinned as `registry/image@sha256:DIGEST`, an `argv` array,
204
- `timeout_seconds`, and `verification_level` (`targeted` by default, or `full`
205
- after reviewing the complete test command). Submission preflight requires a
206
- passing `full` recipe for the exact content. Review the recipe and obtain its
207
- image/dependencies as a separate step. The runner uses an already-present image (`--pull=never`), no
208
- network, no home/keychain/SSH mounts, a read-only container filesystem, dropped
209
- capabilities, and CPU/memory/process/time/output limits. Only the assignment
210
- workspace is mounted read/write, with its Git metadata mounted read-only.
211
- Receipts record exit status, source fingerprints, recipe, image, timestamps,
212
- and bounded output. A missing engine or recipe fails
213
- closed. Evidence is encrypted/authenticated outside the assignment directory;
214
- editing a workspace JSON file cannot fabricate a passing receipt. Ordinary
215
- host-native testing does not acquire these isolation guarantees.
171
+ One project holds one course. Source revisions, coverage and course knowledge
172
+ avoid unnecessary rereads. Edited files and customized site templates are kept;
173
+ changed sources invalidate dependent units. Study projects, originals and learner
174
+ progress are preserved during package updates/removal.
216
175
 
217
176
  ## Client and skill setup
218
177
 
219
178
  `setup --client codex` prepares a local plugin/marketplace entry using the
220
- portable Agent Plugins schemas. Enable it in
221
- Codex Plugins, restart, and verify real tool and skill loading. Disable the legacy
222
- TUM Moodle plugin when switching so duplicate tools/skills are not active.
223
- The old installation remains available for rollback. Setup reports required UI
224
- steps and never equates writing files with activation.
179
+ portable Agent Plugins schemas. Enable it in Codex Plugins, restart, and verify
180
+ real tool and skill loading. Setup reports required UI steps and never equates
181
+ writing files with activation.
225
182
 
226
183
  The Codex adapter uses a contained Node launcher to pin the managed Python in
227
184
  isolated mode; portable stdio manifests require a bare or contained executable.
228
- Disposable-profile checks with Codex CLI 0.164.0 discovered all seven campus
229
- skills and 36 tools with no discovery error. Claude Code 2.1.304 connected to the
230
- stdio process. `scripts/verify_campus_clients.py` reproduces those checks without
231
- running another coding agent or changing real user profiles. After a user restart,
232
- an active Codex desktop chat also loaded all 36 tools, verified both provider
233
- identities, and passed scoped Artemis exercise and Zulip message reads.
234
- Host/model activation trials remain a separate gate.
235
-
236
- `setup --client claude` preserves unrelated user configuration, registers the
237
- local MCP server, and installs the skill pack. Codex's submission invocation
238
- policy and Claude's `disable-model-invocation` metadata are handled independently.
239
- Generic clients receive configuration plus optional manual skill export.
240
-
241
- `setup --client cursor` writes `~/.cursor/mcp.json`.
242
- `setup --client antigravity` writes `~/.gemini/config/mcp_config.json`; an existing
185
+ The shipped pack contains five skills: `read-content`, `sync-study`,
186
+ `prepare-study`, `audio-notes` and `publish-study`. Website-specific retrieval
187
+ guidance is loaded from references within read-content. Teaching, schema and design contracts live
188
+ with prepare-study; the renderer and synthetic example ship as reusable assets.
189
+ Audio Notes converts prepared content into category tracks and maintains coverage,
190
+ scripts, voice settings, candidates and archives under the course's `audio/` root.
191
+ Publish Study uses host GitHub CLI authentication to create a private repository
192
+ and a public Pages website after explicit publication consent. It chooses a course
193
+ and semester name, updates the same site after successful study builds, and shows
194
+ the hosted URL alongside the local launcher URL. No tokens or original course
195
+ downloads are uploaded. See [publishing](study.md#publish-a-study-website).
196
+ Old seven-skill, three-site and separate init/update packs are retired only when
197
+ managed copies remain unchanged. Personal overrides are preserved and require a deliberate merge into
198
+ the consolidated workflows before export.
199
+
200
+ `scripts/verify_campus_clients.py` checks real host discovery in disposable
201
+ profiles without running a model or changing actual user profiles. Host/model
202
+ routing quality is a separate evaluation from structural and discovery checks.
203
+ Claude's invocation metadata follows each workflow's selected policy; automatic
204
+ selection is enabled by default and never grants permission for a mutation.
205
+
206
+ `setup --client cursor` writes `~/.cursor/mcp.json` and installs all five skills
207
+ under `~/.cursor/skills/`.
208
+ `setup --client antigravity` installs the same skills under `~/.gemini/config/skills/`
209
+ and writes `~/.gemini/config/mcp_config.json`; an existing
243
210
  legacy `~/.gemini/antigravity/mcp_config.json` is reused when no current file exists.
244
211
  Both merge the `tum` stdio entry and preserve unrelated settings.
245
212
  From WSL, append `--host windows` to any client setup command to target its
@@ -253,18 +220,25 @@ in the host after setup; actual host activation is not certified by writing file
253
220
  See the official [Cursor MCP documentation](https://cursor.com/docs/mcp) and
254
221
  [Antigravity MCP documentation](https://antigravity.google/docs/mcp).
255
222
 
256
- These two adapters install MCP configuration only. Export the workflow skills
257
- below and install them using the host's documented skill mechanism after review.
258
- This avoids claiming equivalent invocation controls across clients. No new
259
- Cursor/Antigravity model-routing or native desktop activation result is claimed.
260
- `uninstall --client cursor` and `uninstall --client antigravity` remove only an
261
- unchanged managed entry; sessions and personal work stay intact.
223
+ Both adapters install complete skill bundles, including references and website
224
+ assets. Existing MCP-only registrations gain skills through setup or `tum-mcp update`.
225
+ Ownership receipts track installed files: updates replace only unchanged managed
226
+ files, and uninstall removes only unchanged managed files and the MCP entry.
227
+ An edited managed file blocks that client's update or uninstall for review.
228
+ Unrelated skills, extra user files, sessions and personal work stay intact.
229
+ Without an ownership receipt, uninstall preserves manually installed skills.
230
+ Restart the client and verify that its skills and MCP tools appear; writing files
231
+ does not establish model-routing quality or native desktop activation.
232
+ See the official [Cursor skills guide](https://cursor.com/help/customization/skills)
233
+ and [Antigravity skills guide](https://antigravity.google/docs/skills?app=antigravity-ide).
234
+
235
+ Manual export remains available for other clients or a separate reviewed copy:
262
236
 
263
237
  ```sh
264
238
  tum-mcp skills list
265
- tum-mcp skills inspect tum-zulip-research
266
- tum-mcp skills customize tum-zulip-research
267
- tum-mcp skills diff tum-zulip-research
239
+ tum-mcp skills inspect read-content
240
+ tum-mcp skills customize read-content
241
+ tum-mcp skills diff read-content
268
242
  tum-mcp skills validate
269
243
  tum-mcp skills export /path/to/new/export-folder
270
244
  ```
@@ -278,28 +252,27 @@ trials, and two review passes have not been demonstrated for this pilot.
278
252
  Export refuses symbolic links and
279
253
  does not overwrite an existing destination.
280
254
 
281
- The bundled activation corpus has 140 cases (16 development and four held-out
282
- cases per skill), including explicit/implicit requests, adjacent negatives, and
283
- missing credentials. `skills evaluate TRIALS.jsonl` scores supplied fresh-session
255
+ The activation corpus covers the reading, study and audio workflows, with
256
+ development and held-out partitions. It covers explicit/implicit requests, unrelated
257
+ negatives, missing credentials and unsupported-action negatives. Old trial
258
+ results must not be reused against the consolidated skill labels. `skills evaluate TRIALS.jsonl` scores supplied fresh-session
284
259
  records separately by host/version/model. Each record follows `Trial` in
285
260
  `skill_validation.py` and references its retained raw trace by SHA-256. The scorer
286
261
  does not launch other agents or certify the supplied traces, summary quality,
287
262
  or platform acceptance. Inspect traces and backend effects before approving a
288
263
  release; no measured activation accuracy is claimed by the structural checks.
289
264
 
290
- ## Release scope
291
-
292
- Version 0.5.0 has automated installation, browser transport, storage and MCP
293
- checks on macOS, Windows, Linux, and both distribution engines on WSL 3.0.3.
294
- These checks use synthetic pages and credentials. Fresh human SSO/MFA and
295
- desktop-client activation on every target remain separate manual checks, as do
296
- host/model skill-selection trials. Remote workflows also need a
297
- trusted approval/host credential boundary and safe practice verification before
298
- they can be exposed. Browser sign-in alone does not establish a saved backend connection.
299
-
300
- Maintainer checks include the relevant tests, a reviewed tarball inventory,
301
- integrity checks, and an isolated consumer install. Never publish or stage npm
302
- packages as a test. Review and authorization of a registry upload are separate
303
- from a GitHub source push.
304
- `tum-mcp status` reports registry state as `not_checked`, since it inspects local
305
- configuration without making registry requests.
265
+ ## Verification and release scope
266
+
267
+ Local source validation, real MCP protocol discovery, installation/migration
268
+ checks and browser study-site QA are separate gates. Model-routing accuracy and
269
+ human SSO/MFA on every desktop are not established by schema tests or generated
270
+ configuration. No measured skill-selection accuracy is claimed.
271
+
272
+ The existing desktop/WSL CI matrix must pass on the exact pushed revision before
273
+ it is described as verified. Test data and public fixtures are synthetic. Real
274
+ course material, local agent notes and screenshots stay under ignored local paths.
275
+ Maintainer checks include a reviewed tarball inventory, integrity checks and an
276
+ isolated consumer install. Never publish or stage npm packages as a test. Registry
277
+ publication is an explicit maintainer action.
278
+ `tum-mcp status` reports registry state as `not_checked`; it reads local configuration.
@@ -1,5 +1,5 @@
1
1
  {
2
- "wheel": "tum_moodle_integration-0.5.0-py3-none-any.whl",
3
- "wheelSha256": "ec23f7335cd579eb96dabb62ee835c8d35c8e17f2900d194162bb6887d878b7e",
4
- "requirementsSha256": "af80954b16be4f432ad3cdc56a0354236703a852b3f50206ba688355c723a118"
2
+ "wheel": "tum_mcp-0.6.1-py3-none-any.whl",
3
+ "wheelSha256": "59ecf435716e6c0ab68ed9720f2c137b3236f64dd5e962db4c3bf82b76c45ba2",
4
+ "requirementsSha256": "25f9efa7a4ffb0b97abcfeb9d4706d04808273ab1b35abcfaa1ef0b66e87591b"
5
5
  }
@@ -48,7 +48,7 @@ backports-zstd==1.8.0 ; python_full_version < '3.14' \
48
48
  beautifulsoup4==4.15.0 \
49
49
  --hash=sha256:288e3ca7d54b06f2ac191970bc275c1939cb46d450b255bf6718b04aa37ab4f7 \
50
50
  --hash=sha256:d6f88de62e1d4e38ecb1077eb9724cd0eff29d2a08ca16a401e9b9e93f117cf9
51
- # via tum-moodle-integration
51
+ # via tum-mcp
52
52
  brotli==1.2.0 ; platform_python_implementation == 'CPython' \
53
53
  --hash=sha256:0cf8c3b8ba93d496b2fae778039e2f5ecc7cff99df84df337ca31d8f2252896c \
54
54
  --hash=sha256:1b1d6a4efedd53671c793be6dd760fcf2107da3a52331ad9ea429edf0902f27a \
@@ -207,15 +207,15 @@ cryptography==47.0.0 \
207
207
  # via
208
208
  # pyjwt
209
209
  # secretstorage
210
- # tum-moodle-integration
210
+ # tum-mcp
211
211
  defusedxml==0.7.1 \
212
212
  --hash=sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69 \
213
213
  --hash=sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61
214
- # via tum-moodle-integration
214
+ # via tum-mcp
215
215
  filelock==3.32.7 \
216
216
  --hash=sha256:37b8a3d9811b0f9aef7e5ec5c71bb320de52df51e6ca9bcd6f5ad81187660da7 \
217
217
  --hash=sha256:65ff0d0190ea42038b32bda4b77834fb05be2cad4c5b9b01aa4dfb3614536e52
218
- # via tum-moodle-integration
218
+ # via tum-mcp
219
219
  greenlet==3.5.6 \
220
220
  --hash=sha256:06c0e933290fba8ffe53ead4ae1b8044b0e9754b75cebf381aa2bc3e50d82fac \
221
221
  --hash=sha256:2ab5f42ac6c238eb71770715e6e909ad9a1a92b6c681ccb64cd5a0f07edb953f \
@@ -291,7 +291,7 @@ httpx2==2.15.1 \
291
291
  --hash=sha256:ff75485308fbe441e75b1218546e4e78d884df41e1a9741c1f79335ed8fcfef7
292
292
  # via
293
293
  # mcp
294
- # tum-moodle-integration
294
+ # tum-mcp
295
295
  httpx2-jsfetch==1.0 ; sys_platform == 'emscripten' \
296
296
  --hash=sha256:70a0e3eabfef7cce5ad9c629f7d01ca05e418f586646f4ddf14782e4c1454c60 \
297
297
  --hash=sha256:cb916b707601e69a07721aabc8f3f6659be3a6893bc1ff5c6f9e02241df2da32
@@ -329,7 +329,7 @@ jsonschema==4.26.0 \
329
329
  --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce
330
330
  # via
331
331
  # mcp
332
- # tum-moodle-integration
332
+ # tum-mcp
333
333
  jsonschema-specifications==2025.9.1 \
334
334
  --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \
335
335
  --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d
@@ -337,7 +337,11 @@ jsonschema-specifications==2025.9.1 \
337
337
  keyring==25.7.0 \
338
338
  --hash=sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f \
339
339
  --hash=sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b
340
- # via tum-moodle-integration
340
+ # via tum-mcp
341
+ latex2mathml==3.81.1 \
342
+ --hash=sha256:c337668441b71c819b6733905a8058ba9a9d767bae11a0c5fdacb3aff31361bd \
343
+ --hash=sha256:c95add0c0fcdecad2d70567e0643050d5ea1149fb2e98a5d5792fb1c8eea2ed5
344
+ # via tum-mcp
341
345
  markdown-it-py==4.2.0 \
342
346
  --hash=sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49 \
343
347
  --hash=sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a
@@ -345,7 +349,7 @@ markdown-it-py==4.2.0 \
345
349
  mcp==2.3.0 \
346
350
  --hash=sha256:8b147a50441cf059dc88c684e0aeed3687f0aa0f39c6cde7b90330effd2b34d8 \
347
351
  --hash=sha256:dd0c44c089d16453e8ae31a3877a0054d7a2314caaa81f5e0541b9b1734b2377
348
- # via tum-moodle-integration
352
+ # via tum-mcp
349
353
  mcp-types==2.3.0 \
350
354
  --hash=sha256:968efdbdaedfab06adae40d378a34395f1090c5921d4be3c9cde283aaf76d91d \
351
355
  --hash=sha256:d1e46549edb35ee19a94940fcee6d1addd7e589ab7ea92dda83f5d84781fc362
@@ -420,7 +424,7 @@ pillow==12.3.0 \
420
424
  --hash=sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5 \
421
425
  --hash=sha256:fdafc9cce40277e0f7a0feabce0ee50dd2fa1800f3b38015e51296b5e814048d \
422
426
  --hash=sha256:fe3cca2e4e8a592be0f269a1ca4835c25199d9f3ce815c8491048f785b0a0198
423
- # via tum-moodle-integration
427
+ # via tum-mcp
424
428
  playwright==1.64.0 \
425
429
  --hash=sha256:2c14d105548876b15bea5e7eca77bf0d8ff4ba0c607c1ae931067f3d1b010369 \
426
430
  --hash=sha256:5a59af1b230b234008524a5d42b613b233d4256f73bc1dd25bf3f11db0c81b75 \
@@ -430,7 +434,7 @@ playwright==1.64.0 \
430
434
  --hash=sha256:8de42430e9a7c8b04ec963856d484d36ffd452882318ebad2df3e6fb49a8197a \
431
435
  --hash=sha256:97a5c247f1130f3343f097caf3bb1e79358d6b6cfa3550d97ecd721d1905911a \
432
436
  --hash=sha256:d76a501c9930b5a097b00e2448cda2200122a1e8e4be762ff535c1b076277737
433
- # via tum-moodle-integration
437
+ # via tum-mcp
434
438
  prompt-toolkit==3.0.53 \
435
439
  --hash=sha256:01c0891d7f9237d5e339f7d3e42cdae80b7534abb1c7c0e3352efba6231492f2 \
436
440
  --hash=sha256:9ec8a0ad96d5c56148b3f914aa79c1564c3fde5d2e6b876e7bc327e353cf8fa6
@@ -457,7 +461,7 @@ psutil==7.2.2 \
457
461
  --hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \
458
462
  --hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \
459
463
  --hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8
460
- # via tum-moodle-integration
464
+ # via tum-mcp
461
465
  pycparser==3.11 ; implementation_name != 'PyPy' \
462
466
  --hash=sha256:51d5a8ba2be0bbe440b99d2112604c95bbbc3c2748a64260186c541e1729cd80 \
463
467
  --hash=sha256:d875f09c3507d00e1aba0eecc6dcadc1352f30fff09dc6bff2f1c2935e97c2bc
@@ -468,7 +472,7 @@ pydantic==2.14.1 \
468
472
  # via
469
473
  # mcp
470
474
  # mcp-types
471
- # tum-moodle-integration
475
+ # tum-mcp
472
476
  pydantic-core==2.50.1 \
473
477
  --hash=sha256:0036473f5583e6a60e50b8b21651511564277a3f05cc5dab8cf579f552cd5f6c \
474
478
  --hash=sha256:009634b83993777ddcd69cad0ffcace43dabde692109528e35f0fde91e386a8b \
@@ -563,7 +567,7 @@ pyjwt==2.15.1 \
563
567
  pypdf==6.21.1 \
564
568
  --hash=sha256:2d62c383acd6e0dff0a96322ba8b1135229343b084d73c6f6cfd947fd291afcb \
565
569
  --hash=sha256:553e2d42f5bb3bb2a785a7d3de8ef90bb7df74b5413eff5a7b4388007ea9254b
566
- # via tum-moodle-integration
570
+ # via tum-mcp
567
571
  pypdfium2==5.14.0 \
568
572
  --hash=sha256:09b99c8f0cb427eb17fec13c0862ed598bba34b4843df153f70fff806a2820bc \
569
573
  --hash=sha256:11f281613fa22313d9c7ab89947665e84eccf8ebe40e1198a84a88352305648d \
@@ -588,7 +592,7 @@ pypdfium2==5.14.0 \
588
592
  --hash=sha256:eb8aeca157808f323e39ea298cc6d6c8e080c192ea2efb1ca81daa0f0ff4d095 \
589
593
  --hash=sha256:f1b696e6901e16f114a2ec6332e5e3f8f5033a901614ead28499ab18ca6024f5 \
590
594
  --hash=sha256:f6f13bbcc5f4adabc2676e52f662c6cb375de86b314790b0ae08f3ab62eb116a
591
- # via tum-moodle-integration
595
+ # via tum-mcp
592
596
  python-multipart==0.0.32 \
593
597
  --hash=sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e \
594
598
  --hash=sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23
@@ -638,11 +642,11 @@ pyyaml==6.0.3 \
638
642
  --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \
639
643
  --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \
640
644
  --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6
641
- # via tum-moodle-integration
645
+ # via tum-mcp
642
646
  questionary==2.1.1 \
643
647
  --hash=sha256:3d7e980292bb0107abaa79c68dd3eee3c561b83a0f89ae482860b181c8bd412d \
644
648
  --hash=sha256:a51af13f345f1cdea62347589fbb6df3b290306ab8930713bfae4d475a7d4a59
645
- # via tum-moodle-integration
649
+ # via tum-mcp
646
650
  referencing==0.37.0 \
647
651
  --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \
648
652
  --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8
@@ -653,7 +657,7 @@ rich==15.0.0 \
653
657
  --hash=sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb \
654
658
  --hash=sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36
655
659
  # via
656
- # tum-moodle-integration
660
+ # tum-mcp
657
661
  # typer
658
662
  rpds-py==2026.9.1 \
659
663
  --hash=sha256:00ba2d8c7dd4ee537978ddf4b3fbd712bef2d8751603f7f3146b3f4287768e25 \
@@ -764,7 +768,7 @@ truststore==0.10.5 ; sys_platform != 'emscripten' \
764
768
  typer==0.27.3 \
765
769
  --hash=sha256:d0396f770a560ab1b0a8504e13b5f254b728cedb05c61cf0359e944e50ce8901 \
766
770
  --hash=sha256:e50022f28b82a86313e54501317a1db64bf8f8d036ff8cfe5ca7e47675454aff
767
- # via tum-moodle-integration
771
+ # via tum-mcp
768
772
  typing-extensions==4.16.0 \
769
773
  --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8 \
770
774
  --hash=sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5
package/npm/lock.mjs ADDED
@@ -0,0 +1,43 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { readFileSync } from 'node:fs';
3
+ import { mkdir, rename, rm, rmdir, writeFile } from 'node:fs/promises';
4
+ import { hostname } from 'node:os';
5
+ import { join } from 'node:path';
6
+
7
+ export async function acquireLock(root, name = 'setup') {
8
+ await mkdir(root, { recursive: true, mode: 0o700 });
9
+ const lock = join(root, `${name}.lock`);
10
+ const owner = { pid: process.pid, host: hostname(), nonce: randomUUID() };
11
+ const guard = join(root, `${name}.claim`);
12
+ try { await mkdir(guard, { mode: 0o700 }); }
13
+ catch { throw new Error(`Another ${name} is claiming the lock. Retry; an interrupted ${name}.claim needs local review.`); }
14
+ try {
15
+ try { await mkdir(lock, { mode: 0o700 }); }
16
+ catch (error) {
17
+ if (error.code !== 'EEXIST') throw error;
18
+ let previous;
19
+ try { previous = JSON.parse(readFileSync(join(lock, 'owner.json'), 'utf8')); }
20
+ catch { throw new Error(`${name} lock has no readable owner. Confirm no operation is running before removing ${name}.lock.`); }
21
+ if (!Number.isSafeInteger(previous.pid) || previous.pid <= 0 || previous.host !== hostname()) {
22
+ throw new Error(`${name} lock belongs to an unknown owner; preserve it for local review.`);
23
+ }
24
+ let alive = true;
25
+ try { process.kill(previous.pid, 0); }
26
+ catch (failure) { if (failure.code === 'ESRCH') alive = false; }
27
+ if (alive) throw new Error(`Another ${name} is running; retry when it finishes.`);
28
+ const abandoned = `${lock}.${randomUUID()}.abandoned`;
29
+ await rename(lock, abandoned);
30
+ await rm(abandoned, { recursive: true, force: true });
31
+ try { await mkdir(lock, { mode: 0o700 }); }
32
+ catch { throw new Error(`Another ${name} acquired the lock; retry when it finishes.`); }
33
+ }
34
+ const temporary = join(lock, `${owner.nonce}.pending`);
35
+ await writeFile(temporary, JSON.stringify(owner), { mode: 0o600, flag: 'wx' });
36
+ await rename(temporary, join(lock, 'owner.json'));
37
+ } finally { await rmdir(guard); }
38
+ return async () => {
39
+ const current = JSON.parse(readFileSync(join(lock, 'owner.json'), 'utf8'));
40
+ if (current.nonce !== owner.nonce) throw new Error(`${name} lock ownership changed; it was preserved.`);
41
+ await rm(lock, { recursive: true, force: true });
42
+ };
43
+ }