@chalksurf/cli 0.3.1 → 0.3.4

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/agents.md CHANGED
@@ -64,7 +64,11 @@ The CLI and MCP surface are backed by the same agent resource contracts in `pack
64
64
  - Write tools return a `receipt` with `resource`, `operation`, `changedPaths`, `precondition`, `warnings`, and `details`.
65
65
  - Patch-style writes require `expectedUpdatedAt` from a fresh `get` call.
66
66
  - Exercise updates require shared-usage care. Use `exercise usage` first when the exercise might appear outside the target sheet.
67
- - Validate proposed LaTeX with `exercise validate-latex` before applying content updates.
67
+ - Agent exercise create/update writes are stricter than the human UI: invalid LaTeX is rejected with `agentErrorCode: "invalid_latex"` instead of being saved with a warning. Validate proposed LaTeX with `exercise validate-latex` before applying content updates.
68
+ - For exercise creates, the server validates all submitted normalized translation fields. For exercise updates, the server validates only translation fields explicitly present in `patch.translations`; omitted invalid fields do not block unrelated partial updates.
69
+ - Successful exercise create/update receipts include `details.latexValidation`. The scope is `all_translation_fields`, `changed_translation_fields`, or `none` when no translation content changed.
70
+ - Failed invalid-LaTeX writes include `error.agentErrorDetails.results[].patchPath`, `validation.issues[].code`, `message`, `snippet`, offsets, and optional `hint` values so agents can repair and retry without guessing.
71
+ - Use figure tools to create and attach figures to exercises. Use `figure capabilities` before drafting, visually review `figure render-draft`, then upload and attach only after the figure looks correct.
68
72
  - Use `exercise labels` / `list_exercise_labels` before writing labels. Exercise create and update reject labels outside the canonical taxonomy.
69
73
  - Delete and visibility tools are exposed, but destructive writes require exact resource-ID confirmation and current `expectedUpdatedAt` values where applicable.
70
74
  - No transfer-between-organization agent primitive is intentionally exposed.
@@ -75,37 +79,47 @@ Write receipts are the agent audit log. Preserve them in run notes or user-visib
75
79
 
76
80
  Use these commands after authentication and organization selection:
77
81
 
78
- | Goal | CLI command |
79
- | --- | --- |
80
- | Fetch one sheet | `chalksurf sheet get SHEET_ID --json` |
81
- | List owned sheet versions | `chalksurf sheet versions SHEET_ID --json` |
82
- | Fetch one owned sheet version | `chalksurf sheet version SHEET_ID VERSION_ID --json` |
83
- | Get a sheet repair queue | `chalksurf sheet issues SHEET_ID --json` |
84
- | Create a sheet | `chalksurf sheet create --input-json JSON --json` |
85
- | Copy a sheet | `chalksurf sheet copy SHEET_ID "New name" --exercise-copy-mode keep_references --json` |
86
- | Create an editable variant sheet | `chalksurf sheet copy SHEET_ID "Variant name" --exercise-copy-mode copy_all_exercises --json` |
87
- | Append existing exercises to a sheet | `chalksurf sheet append SHEET_ID EXERCISE_ID... --json` |
88
- | Patch sheet metadata | `chalksurf sheet update SHEET_ID --expected-updated-at ISO --patch-json JSON --json` |
89
- | Set sheet visibility | `chalksurf sheet set-visibility SHEET_ID --public --expected-updated-at ISO --confirm-make-public-resource-id SHEET_ID --json` |
90
- | Delete a sheet | `chalksurf sheet delete SHEET_ID --expected-updated-at ISO --confirm-resource-id SHEET_ID --json` |
91
- | List folders | `chalksurf sheet folder list --json` |
92
- | Create a folder | `chalksurf sheet folder create "Folder name" --parent-id FOLDER_ID --json` |
93
- | Rename a folder | `chalksurf sheet folder rename FOLDER_ID "New name" --json` |
94
- | Delete an empty folder | `chalksurf sheet folder delete FOLDER_ID --confirm-resource-id FOLDER_ID --json` |
95
- | Fetch one exercise | `chalksurf exercise get EXERCISE_ID --json` |
96
- | List valid exercise labels | `chalksurf exercise labels --json` |
97
- | Inspect exercise usage | `chalksurf exercise usage EXERCISE_ID --json` |
98
- | Create an exercise | `chalksurf exercise create --input-json JSON --json` |
99
- | Copy one exercise | `chalksurf exercise copy EXERCISE_ID --json` |
100
- | Patch exercise fields | `chalksurf exercise update EXERCISE_ID --expected-updated-at ISO --patch-json JSON --json` |
101
- | Set exercise visibility | `chalksurf exercise set-visibility EXERCISE_ID --public --expected-updated-at ISO --confirm-make-public-resource-id EXERCISE_ID --json` |
102
- | Delete an unused exercise | `chalksurf exercise delete EXERCISE_ID --expected-updated-at ISO --confirm-resource-id EXERCISE_ID --json` |
103
- | Validate LaTeX snippets | `chalksurf exercise validate-latex --snippet "path=value" --json` |
104
- | Send user-consented feedback | `chalksurf feedback user --message "..." --user-consent --json` |
105
- | Send agent-observed feedback | `chalksurf feedback agent --message "..." --json` |
82
+ | Goal | CLI command |
83
+ | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
84
+ | List organization-owned sheets | `chalksurf sheet list --limit 100 --json` |
85
+ | Fetch one sheet | `chalksurf sheet get SHEET_ID --json` |
86
+ | List owned sheet versions | `chalksurf sheet versions SHEET_ID --json` |
87
+ | Fetch one owned sheet version | `chalksurf sheet version SHEET_ID VERSION_ID --json` |
88
+ | Get a sheet repair queue | `chalksurf sheet issues SHEET_ID --json` |
89
+ | Create a sheet | `chalksurf sheet create --input-json JSON --json` |
90
+ | Copy a sheet | `chalksurf sheet copy SHEET_ID "New name" --exercise-copy-mode keep_references --json` |
91
+ | Create an editable variant sheet | `chalksurf sheet copy SHEET_ID "Variant name" --exercise-copy-mode copy_all_exercises --json` |
92
+ | Append existing exercises to a sheet | `chalksurf sheet append SHEET_ID EXERCISE_ID... --json` |
93
+ | Patch sheet metadata | `chalksurf sheet update SHEET_ID --expected-updated-at ISO --patch-json JSON --json` |
94
+ | List valid sheet-series paths | `chalksurf sheet series list --subject math --json` |
95
+ | Validate/apply bulk sheet patches | `chalksurf sheet bulk-update --input batch.json --json` |
96
+ | Set sheet visibility | `chalksurf sheet set-visibility SHEET_ID --public --expected-updated-at ISO --confirm-make-public-resource-id SHEET_ID --json` |
97
+ | Delete a sheet | `chalksurf sheet delete SHEET_ID --expected-updated-at ISO --confirm-resource-id SHEET_ID --json` |
98
+ | List folders | `chalksurf sheet folder list --json` |
99
+ | Create a folder | `chalksurf sheet folder create "Folder name" --parent-id FOLDER_ID --json` |
100
+ | Rename a folder | `chalksurf sheet folder rename FOLDER_ID "New name" --json` |
101
+ | Delete an empty folder | `chalksurf sheet folder delete FOLDER_ID --confirm-resource-id FOLDER_ID --json` |
102
+ | Fetch one exercise | `chalksurf exercise get EXERCISE_ID --json` |
103
+ | List valid exercise labels | `chalksurf exercise labels --json` |
104
+ | Inspect exercise usage | `chalksurf exercise usage EXERCISE_ID --json` |
105
+ | Inspect figure building blocks | `chalksurf figure capabilities --json` |
106
+ | Render a draft figure | `chalksurf figure render-draft --input-json JSON --output figure.png --json` |
107
+ | Upload and attach a figure image | `chalksurf exercise figure upload EXERCISE_ID ./figure.png --figure-type text --json` |
108
+ | Create an exercise | `chalksurf exercise create --input-json JSON --json` |
109
+ | Copy one exercise | `chalksurf exercise copy EXERCISE_ID --json` |
110
+ | Patch exercise fields | `chalksurf exercise update EXERCISE_ID --expected-updated-at ISO --patch-json JSON --json` |
111
+ | Set exercise visibility | `chalksurf exercise set-visibility EXERCISE_ID --public --expected-updated-at ISO --confirm-make-public-resource-id EXERCISE_ID --json` |
112
+ | Delete an unused exercise | `chalksurf exercise delete EXERCISE_ID --expected-updated-at ISO --confirm-resource-id EXERCISE_ID --json` |
113
+ | Validate LaTeX snippets | `chalksurf exercise validate-latex --snippet "path=value" --json` |
114
+ | Send user-consented feedback | `chalksurf feedback user --message "..." --user-consent --json` |
115
+ | Send agent-observed feedback | `chalksurf feedback agent --message "..." --json` |
116
+
117
+ Pass `folderId` in the `sheet create --input-json` payload to place a new sheet in a target folder. Omit it or pass `null` to create the sheet at the organization root.
106
118
 
107
119
  When appending private exercises to a public sheet, the CLI requires `--confirm-make-exercises-public` because the append makes those exercises public.
108
120
 
121
+ For reviewed bulk sheet updates, prefer `--input batch.json` or pipe the same object through `--input -`. Keep `--input-json` for short inline payloads. Submit the unchanged update list in dry-run and apply modes, copy the returned `confirmationDigest` into the apply object, and preserve both outputs and the apply receipt.
122
+
109
123
  For `--private`, visibility commands do not require `--confirm-make-public-resource-id`. If visibility controls are disabled for the selected organization/user, the CLI and MCP return `feature_disabled`.
110
124
 
111
125
  For deletes, fetch the resource immediately before deleting, use that `updated_at` as `--expected-updated-at`, and pass the target ID again as `--confirm-resource-id`. Exercise deletes fail while the exercise is used by any active sheet. Folder deletes fail unless the folder has no child folders and no active child sheets.
@@ -128,7 +142,7 @@ chalksurf --profile prod-codex exercise validate-latex \
128
142
  --json
129
143
  ```
130
144
 
131
- 4. Patch the owning exercise or sheet with the `updated_at` value from the latest `get` result:
145
+ 4. Patch the owning exercise or sheet with the `updated_at` value from the latest `get` result. Exercise update validation is scoped to the translation fields explicitly present in `patch.translations`:
132
146
 
133
147
  ```bash
134
148
  chalksurf --profile prod-codex exercise update EXERCISE_ID \
@@ -142,6 +156,31 @@ chalksurf --profile prod-codex exercise update EXERCISE_ID \
142
156
 
143
157
  Do not pass `--allow-shared-exercise-update` unless the user has acknowledged that the same exercise is used outside the target sheet.
144
158
 
159
+ ## Figure Workflow: Draft, Review, Attach
160
+
161
+ 1. Inspect available building blocks:
162
+
163
+ ```bash
164
+ chalksurf --profile prod-codex figure capabilities --json
165
+ ```
166
+
167
+ 2. Render a semantic draft and save the PNG for visual inspection:
168
+
169
+ ```bash
170
+ chalksurf --profile prod-codex figure render-draft \
171
+ --input-json '{"canvas":{"width":320,"height":240},"objects":[{"id":"A","type":"point","at":{"x":0,"y":0}}]}' \
172
+ --output figure.png \
173
+ --json
174
+ ```
175
+
176
+ 3. Upload and attach the reviewed image to its exercise:
177
+
178
+ ```bash
179
+ chalksurf --profile prod-codex exercise figure upload EXERCISE_ID figure.png \
180
+ --figure-type text \
181
+ --json
182
+ ```
183
+
145
184
  ## Variant Workflow: Create A Slightly Easier Sheet
146
185
 
147
186
  Use `copy_all_exercises` when the target is an editable variant. Shared public exercises often cannot be edited by the selected organization, so keeping references usually blocks variant edits.
@@ -160,48 +199,56 @@ Use `keep_references` only when the user wants a new sheet arrangement that stil
160
199
 
161
200
  MCP clients should use tool discovery from the client. The ChalkSurf MCP endpoint exposes the same resource primitives under snake_case names:
162
201
 
163
- | CLI command | MCP tool |
164
- | --- | --- |
165
- | `auth status` | `get_auth_status` |
166
- | `org list` | `list_organizations` |
167
- | `exercise search` | `search_exercises` |
168
- | `exercise get` | `get_exercise` |
169
- | `exercise labels` | `list_exercise_labels` |
170
- | `exercise usage` | `get_exercise_usage` |
171
- | `exercise create` | `create_exercise` |
172
- | `exercise copy` | `copy_exercise` |
173
- | `exercise update` | `update_exercise` |
174
- | `exercise set-visibility` | `set_exercise_visibility` |
175
- | `exercise translate` | `generate_exercise_translation_job` |
176
- | `exercise delete` | `delete_exercise` |
177
- | `exercise validate-latex` | `validate_latex_snippets` |
178
- | `exercise import` | `import_exercise` |
179
- | `exercise import-solution` | `import_exercise_solution` |
180
- | `sheet search` | `search_sheets` |
181
- | `sheet get` | `get_sheet` |
182
- | `sheet versions` | `list_sheet_versions` |
183
- | `sheet version` | `get_sheet_version` |
184
- | `sheet issues` | `get_sheet_issues` |
185
- | `sheet create` | `create_sheet` |
186
- | `sheet copy` | `copy_sheet` |
187
- | `sheet append` | `append_exercises_to_sheet` |
188
- | `sheet update` | `update_sheet` |
189
- | `sheet set-visibility` | `set_sheet_visibility` |
190
- | `sheet translate` | `generate_sheet_translation_job` |
191
- | `sheet delete` | `delete_sheet` |
192
- | `sheet import` | `import_sheet` |
193
- | `sheet import-solutions` | `import_sheet_solutions` |
194
- | `sheet folder list` | `list_folders` |
195
- | `sheet folder create` | `create_folder` |
196
- | `sheet folder rename` | `rename_folder` |
197
- | `sheet folder delete` | `delete_folder` |
198
- | `job list` | `list_jobs` |
199
- | `job get` | `get_job` |
200
- | `job wait` | `wait_for_jobs` |
201
- | `feedback user` | `send_user_feedback` |
202
- | `feedback agent` | `send_agent_feedback` |
203
-
204
- MCP calls authenticate through ChalkSurf OAuth, not CLI tokens. Read tools require `chalksurf:read`; write tools require `chalksurf:write` plus a write grant for the requested `organizationId`. Each organization-scoped MCP call must pass `organizationId`.
202
+ | CLI command | MCP tool |
203
+ | -------------------------- | ----------------------------------- |
204
+ | `auth status` | `get_auth_status` |
205
+ | `org list` | `list_organizations` |
206
+ | `exercise search` | `search_exercises` |
207
+ | `exercise get` | `get_exercise` |
208
+ | `exercise labels` | `list_exercise_labels` |
209
+ | `exercise usage` | `get_exercise_usage` |
210
+ | `figure capabilities` | `figure_get_capabilities` |
211
+ | `figure render-draft` | `figure_render_draft` |
212
+ | `exercise figure upload` | `upload_exercise_figure` |
213
+ | `exercise create` | `create_exercise` |
214
+ | `exercise copy` | `copy_exercise` |
215
+ | `exercise update` | `update_exercise` |
216
+ | `exercise set-visibility` | `set_exercise_visibility` |
217
+ | `exercise translate` | `generate_exercise_translation_job` |
218
+ | `exercise delete` | `delete_exercise` |
219
+ | `exercise validate-latex` | `validate_latex_snippets` |
220
+ | `exercise import` | `import_exercise` |
221
+ | `exercise import-solution` | `import_exercise_solution` |
222
+ | `sheet list` | `list_sheets` |
223
+ | `sheet search` | `search_sheets` |
224
+ | `sheet get` | `get_sheet` |
225
+ | `sheet versions` | `list_sheet_versions` |
226
+ | `sheet version` | `get_sheet_version` |
227
+ | `sheet issues` | `get_sheet_issues` |
228
+ | `sheet create` | `create_sheet` |
229
+ | `sheet copy` | `copy_sheet` |
230
+ | `sheet append` | `append_exercises_to_sheet` |
231
+ | `sheet update` | `update_sheet` |
232
+ | `sheet series list` | `list_sheet_series` |
233
+ | `sheet bulk-update` | `bulk_update_sheets` |
234
+ | `sheet set-visibility` | `set_sheet_visibility` |
235
+ | `sheet translate` | `generate_sheet_translation_job` |
236
+ | `sheet delete` | `delete_sheet` |
237
+ | `sheet import` | `import_sheet` |
238
+ | `sheet import-solutions` | `import_sheet_solutions` |
239
+ | `sheet folder list` | `list_folders` |
240
+ | `sheet folder create` | `create_folder` |
241
+ | `sheet folder rename` | `rename_folder` |
242
+ | `sheet folder delete` | `delete_folder` |
243
+ | `job list` | `list_jobs` |
244
+ | `job get` | `get_job` |
245
+ | `job wait` | `wait_for_jobs` |
246
+ | `feedback user` | `send_user_feedback` |
247
+ | `feedback agent` | `send_agent_feedback` |
248
+
249
+ `sheet bulk-update` file/stdin handling is a CLI transport convenience. MCP clients pass the same strict object directly to `bulk_update_sheets`; the dry-run, confirmation-digest, batch limit, and atomic-apply contract is identical.
250
+
251
+ MCP calls authenticate through ChalkSurf OAuth, not CLI tokens. Tools advertise `chalksurf:read` or `chalksurf:write` during authorization, while the editable MCP grant's current per-organization permission is the authoritative runtime read/write boundary. For normal execution, each organization-scoped MCP call must pass a full `organizationId` from `get_auth_status` or `list_organizations`. If it is unknown, pass `organizationId: null`; no operation runs, and the response returns the complete current organization catalog for retry. `organization_selection_required` means a UUID must be selected, while `organization_access_denied` means the supplied UUID is not currently usable. Grant and membership edits take effect on the next HTTP request without reloading tools or issuing a new access token. Never infer the organization from a resource ID. Organization-independent tools omit the field, though `list_exercise_labels`, `list_sheet_series`, and `validate_latex_snippets` temporarily ignore an optional legacy UUID from cached tool definitions. ChalkSurf serves modern MCP `2026-07-28` and stateless legacy `2025-11-25` on the same endpoint.
205
252
 
206
253
  ## Feedback
207
254
 
@@ -17,7 +17,10 @@ The CLI uses stable exit codes so automation can branch without parsing human te
17
17
 
18
18
  These same codes are mirrored in `error.exitCode` and `error.code` inside the JSON envelope.
19
19
 
20
- API errors may also include `error.agentErrorCode`, a stable ChalkSurf agent error code such as `precondition_failed`, `confirmation_required`, `invalid_label`, or `resource_in_use`.
20
+ API errors may also include:
21
+
22
+ - `error.agentErrorCode`: a stable ChalkSurf agent error code such as `precondition_failed`, `confirmation_required`, `invalid_label`, `invalid_latex`, or `resource_in_use`.
23
+ - `error.agentErrorDetails`: structured repair details for errors that need more context. For `invalid_latex`, this includes a summary, affected patch paths, issue codes, snippets, offsets, messages, and hints when available.
21
24
 
22
25
  ## JSON Envelope
23
26
 
@@ -51,6 +54,8 @@ Known failures also return JSON on stdout:
51
54
  }
52
55
  ```
53
56
 
57
+ Invalid agent-written exercise LaTeX returns exit code `5` with `error.agentErrorCode = "invalid_latex"`. In JSON mode, inspect `error.agentErrorDetails.results[].patchPath` and `error.agentErrorDetails.results[].validation.issues[]` to repair the submitted fields and retry.
58
+
54
59
  ## Contract Notes
55
60
 
56
61
  - `warnings` contains informational messages that would otherwise be printed for humans.
package/docs/manual.md CHANGED
@@ -175,12 +175,31 @@ Add `--wait` to either translate command to wait for the queued translation job
175
175
 
176
176
  Search commands default to `--ownership own`, which means resources in the selected organization.
177
177
 
178
+ List every sheet owned by the selected organization, including folder paths and current series assignments:
179
+
180
+ ```bash
181
+ chalksurf sheet list --limit 100 --json
182
+ chalksurf sheet list --folder-id FOLDER_ID --include-descendants --json
183
+ ```
184
+
185
+ `sheet list` is for folder-hierarchy inventory. Use `sheet search` for text, subject, ownership, readiness, series, or edition-year predicates.
186
+
178
187
  Find exercise sheets by title:
179
188
 
180
189
  ```bash
181
190
  chalksurf sheet search --text "OKTV 2014"
182
191
  ```
183
192
 
193
+ Find sheets from one series and edition-year range:
194
+
195
+ ```bash
196
+ chalksurf sheet search \
197
+ --series-path hungary.oktv.mathematics \
198
+ --from-year 1980 \
199
+ --to-year 1990 \
200
+ --json
201
+ ```
202
+
184
203
  Find exercises by text:
185
204
 
186
205
  ```bash
@@ -234,6 +253,16 @@ chalksurf exercise update 00000000-0000-4000-8000-000000000002 \
234
253
 
235
254
  Write commands return a receipt with the affected resource, changed paths, warnings, and operation-specific details. Fetch the resource first and use its current `updated_at` value for patch commands.
236
255
 
256
+ Admin-only sheet-series labeling supports a two-step bulk flow:
257
+
258
+ ```bash
259
+ chalksurf sheet series list --subject math --json
260
+ chalksurf sheet bulk-update --input batch-001.dry-run.json --json
261
+ chalksurf sheet bulk-update --input batch-001.apply.json --json
262
+ ```
263
+
264
+ Use `--input -` to read the object from stdin, or `--input-json` for short inline JSON. Exactly one input option is required. The exact same update list must be submitted in both calls; only change `mode` and add the returned `confirmationDigest`. A batch contains at most 100 sheets, applies atomically, and cannot update sheets outside the selected organization.
265
+
237
266
  Manage folders:
238
267
 
239
268
  ```bash
package/docs/mcp.md CHANGED
@@ -5,11 +5,23 @@ ChalkSurf exposes a remote MCP endpoint for agent workflows that need the same c
5
5
  ## Endpoint And Auth
6
6
 
7
7
  - Endpoint: `/mcp` on the ChalkSurf API host.
8
+ - Protocols: modern `2026-07-28` and stateless legacy `2025-11-25` are both supported on the same endpoint.
8
9
  - Authentication: OAuth access tokens issued through the ChalkSurf MCP authorization flow.
9
10
  - CLI tokens and browser session tokens are rejected on `/mcp`.
10
- - Read tools require the `chalksurf:read` scope.
11
- - Write tools require the `chalksurf:write` scope and a write grant for the requested organization.
12
- - Organization-scoped tools require an explicit `organizationId` argument.
11
+ - Tools advertise `chalksurf:read` or `chalksurf:write` during OAuth authorization. The editable MCP grant's current
12
+ per-organization permission is the authoritative runtime read/write boundary, so changing the grant does not require
13
+ issuing a new access token.
14
+ - Organization-scoped tools require an explicit `organizationId` argument for normal execution.
15
+
16
+ Pass the full organization UUID returned by `get_auth_status` or `list_organizations`. If the organization is unknown,
17
+ pass `organizationId: null`; the tool performs no operation and returns `organization_selection_required` with the
18
+ complete current organization catalog. A supplied UUID that is no longer usable returns `organization_access_denied`
19
+ with the same catalog. Retry the same tool with the selected UUID. Grant and membership changes are loaded on the next
20
+ HTTP request, so clients do not need to reload tool definitions. Never infer an organization from a resource ID.
21
+
22
+ `get_auth_status`, `list_organizations`, `list_exercise_labels`, `list_sheet_series`, `validate_latex_snippets`, and the
23
+ figure capability/rendering tools are organization-independent. The three newly global tools temporarily accept and
24
+ ignore an optional UUID from cached legacy tool definitions.
13
25
 
14
26
  MCP clients should use their built-in tool listing/search. ChalkSurf does not expose a second search abstraction just to rediscover tools.
15
27
 
@@ -28,53 +40,123 @@ Read tools return structured resource data. Write tools return a structured `rec
28
40
 
29
41
  Treat write receipts as an agent audit log. They are suitable for user-visible summaries after an agent edits a sheet.
30
42
 
43
+ Exercise content writes are stricter for agents than for the human UI. `create_exercise` validates all submitted normalized exercise translation fields before writing. `update_exercise` validates only translation fields explicitly present in `patch.translations`; omitted invalid fields do not block unrelated partial updates.
44
+
45
+ Successful exercise create/update receipts include `details.latexValidation` with one of these scopes:
46
+
47
+ - `all_translation_fields` for exercise creates.
48
+ - `changed_translation_fields` for exercise updates that submitted translation fields.
49
+ - `none` when an exercise update changed no translation content.
50
+
51
+ If `create_exercise` or `update_exercise` rejects invalid LaTeX, the tool result has `isError: true` and structured content shaped like:
52
+
53
+ ```json
54
+ {
55
+ "isError": true,
56
+ "error": {
57
+ "code": "invalid_latex",
58
+ "message": "invalid_latex: translations.english.exercise_text",
59
+ "agentErrorDetails": {
60
+ "type": "invalid_latex",
61
+ "summary": {
62
+ "checkedSnippetCount": 1,
63
+ "invalidSnippetCount": 1,
64
+ "affectedPathCount": 1
65
+ },
66
+ "results": []
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ Use `error.agentErrorDetails.results[].patchPath` and `validation.issues[]` to repair the submitted fields and retry. The text content also starts with `[invalid_latex]` and names affected paths for clients that only display text.
73
+
31
74
  ## Tool Surface
32
75
 
33
- | Tool | Permission | Purpose |
34
- | --- | --- | --- |
35
- | `get_auth_status` | read | Confirm the authenticated ChalkSurf user and selected organization. |
36
- | `list_organizations` | read | List organizations granted to the MCP client. |
37
- | `search_exercises` | read | Search exercises by text, labels, status, ownership, age, difficulty, or semantic query. |
38
- | `get_exercise` | read | Fetch full details for one visible exercise. |
39
- | `list_exercise_labels` | read | List the canonical exercise label taxonomy agents may write to exercises. |
40
- | `get_exercise_usage` | read | Inspect which sheets use one visible exercise. |
41
- | `create_exercise` | write | Create one exercise in the selected organization. |
42
- | `copy_exercise` | write | Copy one visible exercise into the selected organization. |
43
- | `delete_exercise` | write | Soft-delete one unused writable exercise after `updated_at` and exact-ID confirmation checks. |
44
- | `update_exercise` | write | Patch exercise fields with an `updated_at` precondition and shared-usage acknowledgement. |
45
- | `set_exercise_visibility` | write | Make one writable exercise public or private when visibility controls are enabled. |
46
- | `generate_exercise_translation_job` | write | Queue translation generation for one writable exercise. |
47
- | `import_exercise` | write | Queue an exercise import from MCP files or HTTPS URLs. |
48
- | `import_exercise_solution` | write | Queue a solution import for one exercise. |
49
- | `search_sheets` | read | Search visible sheets, optionally by readiness issues. |
50
- | `get_sheet` | read | Fetch full details for one visible sheet. |
51
- | `list_sheet_versions` | read | List version history for one sheet owned by the granted organization. |
52
- | `get_sheet_version` | read | Fetch one owned sheet version snapshot with referenced exercise details. |
53
- | `get_sheet_issues` | read | Fetch actionable sheet issues: invalid LaTeX, missing translations, missing solutions, and missing scores. |
54
- | `create_sheet` | write | Create one exercise sheet in the selected organization. |
55
- | `copy_sheet` | write | Copy a visible sheet, either keeping exercise references or copying all visible exercises. |
56
- | `delete_sheet` | write | Soft-delete one writable sheet after `updated_at` and exact-ID confirmation checks. |
57
- | `update_sheet` | write | Patch sheet metadata with an `updated_at` precondition. |
58
- | `set_sheet_visibility` | write | Make one writable sheet public or private when visibility controls are enabled. |
59
- | `append_exercises_to_sheet` | write | Append existing visible exercises to the end of one writable sheet. |
60
- | `generate_sheet_translation_job` | write | Queue translation generation for one writable exercise sheet. |
61
- | `list_folders` | read | List sheet folders in the selected organization. |
62
- | `create_folder` | write | Create a sheet folder. |
63
- | `rename_folder` | write | Rename a sheet folder. |
64
- | `delete_folder` | write | Delete one empty sheet folder after exact-ID confirmation. |
65
- | `validate_latex_snippets` | read | Validate proposed LaTeX snippets before writing. |
66
- | `import_sheet` | write | Queue sheet imports from MCP files or HTTPS URLs. |
67
- | `import_sheet_solutions` | write | Queue solution import for an existing sheet. |
68
- | `list_jobs` | read | List jobs in the granted organization. |
69
- | `get_job` | read | Fetch one job. |
70
- | `wait_for_jobs` | read | Wait for one or more jobs to complete. |
71
- | `send_user_feedback` | read | Send user-consented product feedback, bug reports, or blocked-request reports. |
72
- | `send_agent_feedback` | read | Send agent-observed API, MCP, CLI, or documentation feedback. |
76
+ | Tool | Permission | Purpose |
77
+ | ----------------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------- |
78
+ | `get_auth_status` | read | Confirm the authenticated user and return the complete current organization catalog. |
79
+ | `list_organizations` | read | Return the complete current organizations usable through the MCP grant and membership. |
80
+ | `search_exercises` | read | Search exercises by text, labels, status, ownership, age, difficulty, or semantic query. |
81
+ | `get_exercise` | read | Fetch full details for one visible exercise. |
82
+ | `list_exercise_labels` | read | List the canonical exercise label taxonomy agents may write to exercises. |
83
+ | `get_exercise_usage` | read | Inspect which sheets use one visible exercise. |
84
+ | `figure_get_capabilities` | read | Inspect supported semantic 2D figure objects, construction definitions, assertions, limits, and snippets. |
85
+ | `figure_render_draft` | read | Render a semantic figure draft to SVG/PNG metadata, PNG image content, diagnostics, assertions, and review hints. |
86
+ | `upload_exercise_figure` | write | Upload and attach one reviewed image from a ChatGPT file parameter, HTTPS image, base64 image, or rendered figure to an exercise. |
87
+ | `create_exercise` | write | Create one exercise in the selected organization. |
88
+ | `copy_exercise` | write | Copy one visible exercise into the selected organization. |
89
+ | `delete_exercise` | write | Soft-delete one unused writable exercise after `updated_at` and exact-ID confirmation checks. |
90
+ | `update_exercise` | write | Patch exercise fields with an `updated_at` precondition and shared-usage acknowledgement. |
91
+ | `set_exercise_visibility` | write | Make one writable exercise public or private when visibility controls are enabled. |
92
+ | `generate_exercise_translation_job` | write | Queue translation generation for one writable exercise. |
93
+ | `import_exercise` | write | Queue an exercise import from ChatGPT file parameters or HTTPS URLs. |
94
+ | `import_exercise_solution` | write | Queue a solution import for one exercise. |
95
+ | `search_sheets` | read | Search visible sheets by text, subject, ownership, readiness, series, or edition year. |
96
+ | `list_sheets` | read | List sheets owned by the selected organization, including folder paths and current series assignments. |
97
+ | `get_sheet` | read | Fetch full details for one visible sheet. |
98
+ | `list_sheet_series` | read | List the authoritative sheet-series hierarchy and valid assignment paths. |
99
+ | `list_sheet_versions` | read | List version history for one sheet owned by the granted organization. |
100
+ | `get_sheet_version` | read | Fetch one owned sheet version snapshot with referenced exercise details. |
101
+ | `get_sheet_issues` | read | Fetch actionable sheet issues: invalid LaTeX, missing translations, missing solutions, and missing scores. |
102
+ | `create_sheet` | write | Create one exercise sheet in the selected organization. |
103
+ | `copy_sheet` | write | Copy a visible sheet, either keeping exercise references or copying all visible exercises. |
104
+ | `delete_sheet` | write | Soft-delete one writable sheet after `updated_at` and exact-ID confirmation checks. |
105
+ | `update_sheet` | write | Patch sheet metadata with an `updated_at` precondition. |
106
+ | `bulk_update_sheets` | write | Dry-run or atomically apply up to 100 sheet patches using a confirmation digest. |
107
+ | `set_sheet_visibility` | write | Make one writable sheet public or private when visibility controls are enabled. |
108
+ | `append_exercises_to_sheet` | write | Append existing visible exercises to the end of one writable sheet. |
109
+ | `generate_sheet_translation_job` | write | Queue translation generation for one writable exercise sheet. |
110
+ | `list_folders` | read | List sheet folders in the selected organization. |
111
+ | `create_folder` | write | Create a sheet folder. |
112
+ | `rename_folder` | write | Rename a sheet folder. |
113
+ | `delete_folder` | write | Delete one empty sheet folder after exact-ID confirmation. |
114
+ | `validate_latex_snippets` | read | Validate proposed LaTeX snippets before writing. |
115
+ | `import_sheet` | write | Queue sheet imports from ChatGPT file parameters or HTTPS URLs. |
116
+ | `import_sheet_solutions` | write | Queue solution import for an existing sheet. |
117
+ | `list_jobs` | read | List jobs in the granted organization. |
118
+ | `get_job` | read | Fetch one job. |
119
+ | `wait_for_jobs` | read | Wait for one or more jobs to complete. |
120
+ | `send_user_feedback` | read | Send user-consented product feedback, bug reports, or blocked-request reports. |
121
+ | `send_agent_feedback` | read | Send agent-observed API, MCP, CLI, or documentation feedback. |
122
+
123
+ For `create_sheet`, pass `folderId` to create the sheet in a target folder. Omit it or pass `null` to create the sheet at the organization root.
124
+
125
+ Sheet-series writes are currently restricted to admin users and sheets owned by the selected organization. Use `list_sheet_series`, then call `bulk_update_sheets` with `mode: "dry_run"`. Apply the unchanged batch with the returned `confirmationDigest`; no idempotency key or separate idempotency table is used. CLI `--input` and stdin support are transport conveniences only: MCP clients pass the same strict bulk object directly.
73
126
 
74
127
  Delete tools require exact `confirmResourceId` matching the target ID. Exercise and sheet deletes also require the latest `expectedUpdatedAt`. Exercise deletes fail while the exercise is used by an active sheet, and folder deletes fail unless the folder is empty.
75
128
 
76
129
  Visibility tools respect the existing ChalkSurf visibility feature flag. If visibility controls are disabled, they return `feature_disabled`. Publicization requires `confirmMakePublicResourceId`; private visibility changes do not.
77
130
 
131
+ Figure tools are available for agents to create and attach figures to exercises. The recommended flow is `figure_get_capabilities`, then one or more `figure_render_draft` calls with visual review, then `upload_exercise_figure` with the target `exerciseId`. Upload and attachment are one operation. Figure drafts support semantic 2D constructions such as midpoints, projections, selected intersections, triangle centers, parallel/perpendicular lines, angle bisectors, incircles, diameter circles, and tangent circles; use assertions such as `angleBisected`, `tangent`, `pointOnSegment`, and `concurrent` to verify the intended relationships before upload.
132
+
133
+ ### File Parameters
134
+
135
+ ChatGPT file uploads are advertised with `_meta["openai/fileParams"]`. For `import_exercise`, `import_exercise_solution`, `import_sheet`, and `import_sheet_solutions`, ChatGPT should pass uploaded files in top-level `file_id_N` arguments. The field names are model-facing: ChatGPT receives file IDs for those fields, then the harness passes ChalkSurf resolved file objects:
136
+
137
+ ```json
138
+ {
139
+ "organizationId": "00000000-0000-0000-0000-000000000000",
140
+ "sourceUrls": ["https://example.com/worksheet-part-1.pdf"],
141
+ "file_id_1": {
142
+ "download_url": "https://...",
143
+ "file_id": "file_...",
144
+ "mime_type": "application/pdf",
145
+ "file_name": "worksheet.pdf"
146
+ },
147
+ "file_id_2": {
148
+ "download_url": "https://...",
149
+ "file_id": "file_...",
150
+ "mime_type": "application/pdf",
151
+ "file_name": "worksheet-solution.pdf"
152
+ }
153
+ }
154
+ ```
155
+
156
+ `download_url` and `file_id` are required by ChatGPT. `mime_type` and `file_name` are optional. The import tools declare `file_id_1` through `file_id_20`; `upload_exercise_figure` declares only `file_id_1`. `sourceUrls` and `file_id_N` fields can be used together in one import call. ChalkSurf downloads every temporary `download_url` immediately and queues the existing import job with copied storage files; ChatGPT download URLs are not persisted for later job processing.
157
+
158
+ Generic MCP clients should pass absolute HTTPS URLs in `sourceUrls` for import tools, or use `source.kind = "https_url"`, `"base64_image"`, or `"rendered_figure"` for `upload_exercise_figure`. The older import `sources` object-array shape is accepted temporarily for compatibility, but it is no longer the advertised MCP input shape.
159
+
78
160
  No MCP tool currently transfers resources between organizations.
79
161
 
80
162
  `send_user_feedback` and `send_agent_feedback` are not read-only operations, but they require only the `chalksurf:read` OAuth scope because they do not mutate ChalkSurf exercises, sheets, or folders. Use `send_user_feedback` only after the user explicitly agrees to send feedback. Use `send_agent_feedback` when the agent itself observes inconsistent behavior, inaccurate docs, or missing agent-facing API coverage.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chalksurf/cli",
3
- "version": "0.3.1",
3
+ "version": "0.3.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "engines": {