@manudota/artist-mcp 2.4.1-staging.129 → 3.0.0

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/README.md CHANGED
@@ -115,12 +115,14 @@ npx @manudota/artist-mcp init --allow-writes onenote-create,calendar-create
115
115
  | Capability | What it allows |
116
116
  | --- | --- |
117
117
  | `calendar-create` | Add a single event to a Google Calendar |
118
- | `calendar-delete` | Remove an event this tool itself created; it cannot touch one you made |
118
+ | `calendar-delete` | Remove an event. One you made is shown first and removed only after you confirm |
119
119
  | `onenote-create` | Add a new page to a section |
120
120
  | `onenote-edit` | Change a page this tool itself created — append, or replace part of one. Microsoft enforces this: the scope cannot reach a page you wrote |
121
121
 
122
- Granting `calendar-create` and `calendar-delete` together also allows
123
- rescheduling an event this tool created.
122
+ Granting `calendar-create` and `calendar-delete` together also allows editing
123
+ any event in place (title, times, location, notes, colour) after you confirm the
124
+ change, and rescheduling an event this tool created. Invitations from others and
125
+ whole recurring series are refused, and nobody is emailed.
124
126
 
125
127
  Three things hold for all of them. A capability you did not grant registers no
126
128
  tool at all, so it cannot be invoked, argued for, or reached by mistake. Every
@@ -1,142 +1,74 @@
1
1
  # Answering
2
2
 
3
- Answer the question that was asked, in as few words as it takes, and keep the
4
- machinery out of it.
3
+ Answer the question, in as few words as it takes. This policy decides what is
4
+ said, and every other playbook decides what is true. Where another playbook
5
+ says to "say", "name", "report", "offer" or "mention" something, it is said only
6
+ if this policy allows it.
7
+
8
+ ## Say
9
+
10
+ - **Facts that answer the question.** Who, what, when, where, how much. Nothing
11
+ around them.
12
+ - **Failures.** One short statement, such as "Couldn't read the festival page." No
13
+ reason, and no account of retries.
14
+ - **Contradictions.** The two values, briefly: "Start: 19:30 (Contract) /
15
+ 20:00 (Run sheet)." No proposed fix.
16
+ - **Gaps the question runs into.** "Rehearsal schedule: not recorded." Absent means
17
+ unrecorded, not done, and silence reads as done.
18
+ - **A partial answer.** One line where part of what the question needs was not
19
+ covered: "Only the 2026 notebook checked."
20
+
21
+ ## Do not say
22
+
23
+ - **Machinery.** Tools, calls, playbooks, searches, reading order, fallbacks,
24
+ ids, what a tool asks for.
25
+ - **Success.** "I read the page", "found it", "nothing was changed", "the
26
+ register agrees", "the source exists".
27
+ - **Facts beside the question.** Other fields of the same page, background,
28
+ context that was not asked for.
29
+ - **Advice, next steps and offers**, unless asked. That covers offers to fix,
30
+ check, patch, convert, or look in email or a calendar.
31
+ - **Sources.** Which page or notebook a fact came from, except in a
32
+ contradiction, where the source is what tells the two values apart.
5
33
 
6
- Reading a page costs several tool calls, a fallback, a truncation, a choice
7
- between sources. All of that is useful to you and almost none of it is useful to
8
- the musician. Reported by habit, it buries the one sentence they asked for, and
9
- a chat they have to mine is a chat that has put project management back in their
10
- head rather than taken it out.
11
-
12
- ## The line
13
-
14
- Keep in context, do not report:
15
-
16
- - Which tool ran, how many calls it took, or in what order.
17
- - Why a fallback or a source was chosen, when the result is sound either way.
18
- - Page ids, part numbers, preview-versus-full-read, retries, sizes.
19
- - Reasoning you worked through and discarded.
20
-
21
- Always report, however long the answer already is:
22
-
23
- - That the answer is partial: a page read in part, a notebook sketched rather
24
- than read, a scope not covered.
25
- - That something is unrecorded rather than settled. Absent means unrecorded, not
26
- handled, and silence reads as handled.
27
- - A comparison, check or source that was **not** consulted, where a reader would
28
- otherwise assume it had been.
29
- - A contradiction, a hedged value, or a fact that rests on evidence rather than
30
- on the page.
31
- - Rules that are not in force.
32
- - The page title behind a material fact. One title is not overhead.
33
-
34
- The test is not length, it is consequence: **does this change what the musician
35
- should believe or do?** If it does, say it, briefly. If it only explains how the
36
- answer was reached, keep it.
37
-
38
- ## Answer anyway, and say what it cost
39
-
40
- The workflow is a recommendation, not a gate. Survey, classify, settle the
41
- contradictions, then work — but nobody is obliged to, and a musician may ask
42
- what is due on a notebook nothing has been done to. Answer them. A limited
43
- answer is worth having.
44
-
45
- What is not worth having is a limited answer that does not look limited. The
46
- harm is never that the work was skipped; it is an answer carrying no mark of it.
47
- A list of deadlines built from pages that contradict each other is useful when
48
- it says which dates are disputed, and misleading when it picks one and mentions
49
- the conflict lower down.
50
-
51
- So where earlier work would have made the answer better, say so once, in a line:
52
- what is unsettled, what that costs this answer, and what would fix it. Then give
53
- the answer. Do not refuse, do not insist on the proper order first, and do not
54
- quietly hand back something that reads more settled than it is.
55
-
56
- ## Say it as firmly as you actually know it
57
-
58
- Three kinds of claim, and they are not stated alike.
59
-
60
- - **What a page says.** State it plainly and name the page.
61
- - **What you worked out** — a weekday, a duration, a total. It has no page
62
- behind it and arithmetic in passing is where a confident mistake hides. Say it
63
- was worked out, and never let it retire evidence: where your calculation
64
- contradicts what the pages support, doubt the calculation first and give both.
65
- - **What you think it means.** Give your reading and mark it as one — *might
66
- indicate*, *could mean*, *worth checking* — and name what would settle it.
34
+ ## When asked
67
35
 
68
- This is not hedging everything. It is the step from fact to explanation that
69
- carries the doubt, and that step is where certainty gets borrowed against
70
- evidence that does not cover it.
36
+ When the musician asks how, where from, why, or what next, answer that and only
37
+ that, as briefly as the rest. The detail is withheld by default, and never
38
+ denied on request.
71
39
 
72
- **Whatever is read first carries the claim, so it carries the doubt too.** A
73
- heading may not be more certain than what sits under it: "Duplicate pairs — pick
74
- one, delete the other" is a verdict on everything beneath it however well the
75
- third item is hedged. The same goes for a bolded lead, a table header, the first
76
- clause of a bullet, and a closing recommendation.
40
+ ## Firmness
77
41
 
78
- Some words assert more than the evidence ever supports. *Safe*, *harmless*, *no
79
- information lost*, *the losing page*, *stale*: each states a conclusion about
80
- something you compared partially, and none of them may stand in for saying what
81
- you compared and what you would check.
42
+ Say something only as firmly as you know it, in the fewest words that carry it.
82
43
 
83
- ## Ask when the answer changes the work
44
+ - A page fact: state it.
45
+ - Something worked out, like a weekday, a total or a derived deadline: mark it
46
+ with one word, such as "derived".
47
+ - A reading of what something means: mark it with one word, such as
48
+ "likely".
49
+ - A hedged value is not a fact. "Might be 0204" is not a phone number.
84
50
 
85
- A question costs the musician a turn and hands them back the thinking they came
86
- here to put down. Spend that only where their answer changes what you do.
51
+ Brevity never drops a gap, a failure, a contradiction or a partial answer. If a
52
+ short answer would mislead, it is not short enough to be right.
87
53
 
88
- Ask, always:
54
+ ## Still answer
89
55
 
90
- - Which notebook, which page, which of two conflicting values. These decide the
91
- work and may never be guessed.
92
- - Permission to widen past the notebook, or to any other source.
56
+ A notebook nobody has sorted still gets an answer. Where that makes the answer
57
+ unreliable, that is a partial answer: one line, then the answer.
93
58
 
94
- Do not ask, decide:
59
+ ## Ask only when the answer changes the work
95
60
 
96
- - How the answer should be formatted, how long it should be, what order it comes
97
- in. Pick the sensible default and deliver. They can redirect after, and they
98
- cannot get back the turn spent choosing.
99
- - Anything you would do the same way whatever they answered.
61
+ Ask which notebook, which page, or which of two conflicting values, when the
62
+ work depends on it and it has not been said. Ask before widening past the
63
+ notebook or into any other source. Never ask about format, length or order:
64
+ decide.
100
65
 
101
66
  ## Handing something over
102
67
 
103
- **Never ask which format they want.** They are choosing before they have seen
104
- anything, it costs them a turn, and it is a decision you are better placed to
105
- make. Decide, deliver, and let them redirect.
106
-
107
68
  An ordinary answer goes in chat. Something meant to be pasted into OneNote is a
108
- file, and `policy:intake` says what that file is — follow it rather than
109
- deciding again here.
110
-
111
- Whatever the form, **never a source format**: Markdown, JSON and the like are a
112
- developer's file, unreadable double-clicked and worse pasted, and `.md` means
113
- nothing to most people who will use this. Not producing one is not enough —
114
- naming it as an option is the same choice they have no way to judge.
115
-
116
- **One recommendation beats a menu.** Having read the notebook you know which
117
- item is most urgent; say so and offer it. "How would you like to proceed?" hands
118
- back a decision you are better placed to shape, and reads as work rather than
119
- help. Offer the next step, name why it is that one, and let them override.
69
+ file, as `policy:intake` describes. Never offer or produce a source format such
70
+ as Markdown or JSON.
120
71
 
121
72
  ## What they said this session outranks all of this
122
73
 
123
- Told to flag rather than decide, do not close by asking them to decide. Told to
124
- keep it short, do not hand back a survey. Told which notebook, do not wander.
125
-
126
- An instruction given ten lines ago is not a preference to be worked around on
127
- the way to being helpful, and none of these rules is a reason to override one.
128
-
129
- ## When asked
130
-
131
- If they ask how something was determined, why a value is missing, or what was
132
- searched, answer in full. The detail is withheld by default, never unavailable
133
- on request, and never denied.
134
-
135
- ## Boundaries
136
-
137
- - Never shorten by dropping a limit, a gap, or a caveat. Cut the narration, not
138
- the warranty.
139
- - Never state more confidence than the evidence carries in order to be brief. If
140
- a short answer would be misleading, the answer is not short.
141
- - Never present a mechanical detail as the finding. "Read in full because the
142
- preview was thin" is not a result.
74
+ An instruction the musician gave in this conversation beats every rule here.
@@ -71,14 +71,6 @@ From the moment it is suspected, not from when it is confirmed:
71
71
  - **Show conflicts side by side**, with both page titles, and leave them
72
72
  standing. A date confirmed on one page and questioned on the other is two
73
73
  facts, not one fact and one error.
74
- - **Offer the cross-reference**, in the same answer that reports the suspicion.
75
- One line to paste onto each page, each naming the other as a record of the
76
- same event. It states the link, not a ranking, and it is the only thing here
77
- that outlives the session: nothing remembers, so a twin found today is
78
- unknown tomorrow and the next answer is built from one page again. Do not
79
- wait for the pair to be confirmed, for the fields to be settled, or for them
80
- to ask what to do about it. `policy:patch` governs the form and where it
81
- goes; it is two fragments, one per page, never one block covering both.
82
74
 
83
75
  One line means one line. This is the whole of it, for a wedding recorded twice:
84
76
 
@@ -37,7 +37,8 @@ Widening has rings, and each needs more permission than the last.
37
37
 
38
38
  1. **The rest of the page.** Ordinary. Do it without asking.
39
39
  2. **Other pages in the chosen notebook.** Ordinary when the question is about
40
- the notebook; say which pages were read.
40
+ the notebook. Say which pages were not read only where that leaves the
41
+ answer partial.
41
42
  3. **Another notebook.** Only when the musician asks. Scope was chosen for a
42
43
  reason.
43
44
  4. **Another source — Gmail, Calendar, an attachment.** Only when the musician
@@ -49,14 +50,13 @@ account looks like an invitation. It is not one.
49
50
  - **A connection is not a standing instruction to search it.** The musician
50
51
  signed in once; that is not permission to read their mail whenever an answer
51
52
  would be improved by it.
52
- - **Ask before the first read.** Where the page cannot answer something and a
53
- source plausibly could, say what is missing and offer to look — name the
54
- source and the search you intend. Then wait.
53
+ - **Wait to be asked.** Where the page cannot answer something, say it is not
54
+ recorded. Read another source only when the musician asks for that look.
55
55
  - **One yes is one look, not a licence.** A later question starts over unless
56
56
  they said to keep using it.
57
57
  - **A tie is not an emergency.** Two pages disagreeing, a missing owner, an
58
58
  unconfirmed date: none of these justify reaching for a mailbox unasked. Report
59
- the tie. Offer the look. Let them decide.
59
+ the tie.
60
60
  - **Evidence never becomes the working unit.** It corroborates a page; it never
61
61
  replaces one and never becomes what a playbook operates on.
62
62
  - **Never report a source as empty when it was not searched, and never search it
@@ -26,7 +26,8 @@ named. That is ordinary Orchestrator work.
26
26
  never one you know of from outside this conversation, from saved context or
27
27
  an earlier session. A notebook the musician has not named in front of you is
28
28
  a guess, however good, and the answer it produces is correct about the wrong
29
- pages. Say which notebook every answer covers.
29
+ pages. Where the musician has not named the notebook, say which one the
30
+ answer covers.
30
31
  3. Use `map_notes` on the chosen notebook to see the opening of every page at
31
32
  once, and let it decide reading order. It is cheap where reading everything
32
33
  is not, so a large notebook can be triaged before any page is opened. Run it
@@ -97,9 +98,9 @@ template's shape drift.
97
98
  what identifies a template; where it sits is a hint.
98
99
  - A title naming a template is a hint too, and neither more nor less than that.
99
100
  A musician who paste-names their pages differently still has templates.
100
- - Reuse what you find: their shape, their headings, their fields. Say which
101
- template governed, its version, and when it last changed, so the transcript
102
- shows whether the shape came from them or from a playbook.
101
+ - Reuse what you find: their shape, their headings, their fields. Where no
102
+ template of theirs was found and the shape came from a playbook, say so.
103
+ Name the template, its version and when it last changed only when asked.
103
104
  - Two stamps for one project type: ask which. Never take the newer, the fuller
104
105
  or the tidier.
105
106
  - A stamp naming a project type no playbook covers: ask what it is for. Do not
@@ -11,9 +11,8 @@ musician or supplier contacts.
11
11
  1. Verify the headline facts and find contradictory dates.
12
12
  2. Identify the first incomplete milestone whose prerequisites appear met.
13
13
  3. Find the corresponding task, owner, deadline, and relevant contact.
14
- 4. Recommend one next action.
15
- 5. If outreach is useful, Registrar extracts the contact, Envoy drafts, and
16
- Auditor verifies before the result is shown.
14
+ 4. When the musician asks for outreach, Registrar extracts the contact, Envoy
15
+ drafts, and Auditor verifies before the result is shown.
17
16
 
18
17
  ## Checks
19
18
 
@@ -10,10 +10,9 @@ Turn the user's request and live OneNote evidence into one useful next action.
10
10
  2. Read that page before selecting a workflow.
11
11
  3. Load the closest project type and the smallest necessary set of roles.
12
12
  4. Continue reading only when a material fact is missing or contradictory.
13
- 5. When the missing fact is not in OneNote at all, say so and offer to check
14
- email or calendar — name the source and the search. Do not run it until the
15
- musician agrees, and do not offer when the page already answers the question.
16
- 6. Present one recommendation or one finished draft in chat, under
13
+ 5. When the missing fact is not in OneNote, say it is not recorded. Check email
14
+ or calendar only when the musician asks.
15
+ 6. Present the answer, or the finished draft asked for, under
17
16
  `policy:answering`.
18
17
 
19
18
  ## Boundaries
@@ -23,9 +22,9 @@ Turn the user's request and live OneNote evidence into one useful next action.
23
22
  recommended action is the point, and a notebook-wide "what is due" asked of
24
23
  the Project Manager, where a list across pages is the answer rather than a
25
24
  dump.
26
- - Never remove anything the musician made. Where an install can delete at all,
27
- it can only remove what this tool itself created, and that is a correction of
28
- its own mistake rather than a way to tidy a calendar.
25
+ - Never change or remove anything the musician made unless they asked for that
26
+ specific change. Where an install can edit or delete their calendar events,
27
+ the tool shows the change first; wait for their yes before confirming it.
29
28
  - Never write to OneNote. That holds whatever else an install may do, and it is
30
29
  not a limitation of the tools alone: a page has no undo, and a working unit is
31
30
  the musician's own record.
@@ -2,16 +2,16 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- Compare one working-unit page with its project-type playbook and identify the
6
- single most useful next project-management action. Or, across the chosen
7
- notebook, say what is due and what is overdue.
5
+ Compare one working-unit page with its project-type playbook and find what is
6
+ overdue, contradictory or missing. Or, across the chosen notebook, say what is
7
+ due and what is overdue.
8
8
 
9
9
  ## Method
10
10
 
11
11
  1. Parse the page's facts, milestones, tasks, owners, dates, and statuses.
12
12
  2. Respect completed work and current dependencies.
13
13
  3. Flag overdue, contradictory, unowned, or unscheduled work.
14
- 4. Recommend one next action with rationale and source evidence.
14
+ 4. Recommend a next action only when the musician asks for one.
15
15
 
16
16
  ## Across the notebook
17
17
 
@@ -35,9 +35,9 @@ about, which is the burden this exists to remove.
35
35
  4. Separate overdue from upcoming, and both from work with no date at all —
36
36
  undated work is unscheduled, not distant.
37
37
 
38
- Say which pages were read and which were not. A calendar of what is due is
39
- trusted as complete, so a partial sweep that does not say so is worse than no
40
- sweep.
38
+ Where a page was not read, say which. A calendar of what is due is trusted as
39
+ complete, so a partial sweep that does not say so is worse than no sweep. A
40
+ complete sweep needs no list of what it read.
41
41
 
42
42
  Where two pages describe one event, `policy:divergence` holds: their milestones
43
43
  are not pooled into one list as though they were one page.
@@ -5,9 +5,9 @@
5
5
  "id": "policy:answering",
6
6
  "kind": "policy",
7
7
  "name": "Answering",
8
- "description": "Answer the question that was asked, in as few words as it takes, and keep the machinery out of it.",
8
+ "description": "Answer the question, in as few words as it takes. This policy decides what is said, and every other playbook decides what is true. Where another playbook says to \"say\", \"name\", \"report\", \"offer\" or \"mention\" something, it is said only if this policy allows it.",
9
9
  "file": ".artist/policies/ANSWERING.md",
10
- "sha256": "cd24def1d3946ee602aa592767361e86791415ea78f01a0631c577e75e869e95"
10
+ "sha256": "7886eff2dfdf061cb631c0967117f6b74292a7fcffa4581e5c42d38909d3478c"
11
11
  },
12
12
  {
13
13
  "id": "policy:divergence",
@@ -15,7 +15,7 @@
15
15
  "name": "Divergence",
16
16
  "description": "When two pages in the notebook describe the same event, say so, and let the musician settle each fact the two disagree on. Neither page is the project — the event is, and the pages are two records of it.",
17
17
  "file": ".artist/policies/DIVERGENCE.md",
18
- "sha256": "af0e1139bd842a3adcc551f9c8cf7b6fce89fd0a7133025a56ff4eac6909d6ef"
18
+ "sha256": "a14cacc20c0f9f48c5233b14097ae869e440eb0864ea6e300f0b7e9153fe1deb"
19
19
  },
20
20
  {
21
21
  "id": "policy:evidence",
@@ -23,7 +23,7 @@
23
23
  "name": "Evidence",
24
24
  "description": "Assume the likely shape of a page, then verify it. When the assumption does not hold, widen the search — never report the assumption's failure as a finding.",
25
25
  "file": ".artist/policies/EVIDENCE.md",
26
- "sha256": "69025e82df3ef07176a292b894bb9a6e5c06bcf21be5cbdc0eb5377eb9153eb8"
26
+ "sha256": "cc417be662a86fc4ddc8fdfd5342c688703b069dccbb3e50d3ae9e314a596919"
27
27
  },
28
28
  {
29
29
  "id": "policy:intake",
@@ -31,7 +31,7 @@
31
31
  "name": "Intake",
32
32
  "description": "Turn an unorganized notebook into working units without inventing anything.",
33
33
  "file": ".artist/policies/INTAKE.md",
34
- "sha256": "21c65ade15b4350b284eb04315a8f4750629926d45a436beab404fee2d31e2dd"
34
+ "sha256": "3442ea49c9072e3e9c84257b50c0ce0782169c3f3ed605c545c3401552d3f092"
35
35
  },
36
36
  {
37
37
  "id": "policy:local-state",
@@ -55,7 +55,7 @@
55
55
  "name": "Concert",
56
56
  "description": "One OneNote page describing one concert. Expected fields include name, date, venue, audience, project lead, budget, description, milestones, tasks, and musician or supplier contacts.",
57
57
  "file": ".artist/project-types/CONCERT.md",
58
- "sha256": "84f45b97be57cc8335e460b2095eb3759bc354d62d50a691317351e93cf6faec"
58
+ "sha256": "be42919b0e73c2f828a16e5e8bdad08f43a456d42ea58d7ed9d366c42df818e5"
59
59
  },
60
60
  {
61
61
  "id": "project-type:large-concert",
@@ -119,15 +119,15 @@
119
119
  "name": "Orchestrator",
120
120
  "description": "Turn the user's request and live OneNote evidence into one useful next action.",
121
121
  "file": ".artist/roles/ORCHESTRATOR.md",
122
- "sha256": "c7496baa5d5e86cdc905ed37b3a72265bb6e982d7084091ed8fe024fddf50c2b"
122
+ "sha256": "ce07931b46caddbbf3c909dbcb203f3c386af987168370a4385f1390ba644b4e"
123
123
  },
124
124
  {
125
125
  "id": "role:project-manager",
126
126
  "kind": "role",
127
127
  "name": "Project Manager",
128
- "description": "Compare one working-unit page with its project-type playbook and identify the single most useful next project-management action. Or, across the chosen notebook, say what is due and what is overdue.",
128
+ "description": "Compare one working-unit page with its project-type playbook and find what is overdue, contradictory or missing. Or, across the chosen notebook, say what is due and what is overdue.",
129
129
  "file": ".artist/roles/PROJECT_MANAGER.md",
130
- "sha256": "f2c3d4a767b97d8f33f778257d23adcb61ac737ffdf58265cb84d16dbcdc01c9"
130
+ "sha256": "f670aedf05d120c6518db692e668ede235d6ab5cc4b84e773d969f20fe5e7782"
131
131
  },
132
132
  {
133
133
  "id": "role:registrar",
package/dist/api.d.ts CHANGED
@@ -122,6 +122,15 @@ export declare const calendarInsertEvent: (calendarId: string, body: unknown, to
122
122
  * only an event this tool created can be removed.
123
123
  */
124
124
  export declare const calendarDeleteEvent: (calendarId: string, eventId: string, token: string) => Promise<void>;
125
+ /**
126
+ * Change one event in place. The only PATCH to Google, added by 0013.
127
+ *
128
+ * `If-Match` carries the etag the confirmation was made against, so an event
129
+ * that changed after the preview is refused by Google (412) rather than
130
+ * overwritten. Not retried, like every write here. sendUpdates=none: nobody is
131
+ * emailed. See docs/decisions/0013-editing-any-calendar-event.md.
132
+ */
133
+ export declare const calendarPatchEvent: (calendarId: string, eventId: string, body: Record<string, unknown>, etag: string, token: string) => Promise<Response>;
125
134
  export declare const CALENDAR_LIST_NEED: ScopeNeed;
126
135
  /**
127
136
  * Create one page in one OneNote section.
@@ -135,7 +144,17 @@ export declare const CALENDAR_LIST_NEED: ScopeNeed;
135
144
  *
136
145
  * See docs/decisions/0003-onenote-writes.md.
137
146
  */
138
- export declare const onenoteCreatePage: (sectionId: string, xhtml: string, token: string) => Promise<Response>;
147
+ export declare const onenoteCreatePage: (sectionId: string, xhtml: string, token: string,
148
+ /**
149
+ * Images the page refers to as `name:<name>`. With any, the page goes as
150
+ * multipart: the document is the part named `Presentation`, and each image
151
+ * is a part of its own. See docs/decisions/0012-images-on-a-new-page.md.
152
+ */
153
+ parts?: {
154
+ name: string;
155
+ media_type: string;
156
+ bytes: Uint8Array;
157
+ }[]) => Promise<Response>;
139
158
  /**
140
159
  * Read a page back with the ids a patch can aim at.
141
160
  *
@@ -153,6 +172,14 @@ export declare const onenotePageContent: (pageId: string, token: string) => Prom
153
172
  html: string;
154
173
  etag: string | null;
155
174
  }>;
175
+ /**
176
+ * Create one section in a notebook.
177
+ *
178
+ * Narrow like `onenoteCreatePage`: one notebook id, one name, one POST, and no
179
+ * retry, because a 5xx does not say whether the section landed and a second
180
+ * attempt would make a second section. See docs/decisions/0011-creating-sections.md.
181
+ */
182
+ export declare const onenoteCreateSection: (notebookId: string, displayName: string, token: string) => Promise<Response>;
156
183
  /**
157
184
  * Apply patch commands to one page.
158
185
  *
package/dist/api.js CHANGED
@@ -415,8 +415,9 @@ export const calendarInsertEvent = async (calendarId, body, token) => {
415
415
  * only an event this tool created can be removed.
416
416
  */
417
417
  export const calendarDeleteEvent = async (calendarId, eventId, token) => {
418
+ // sendUpdates=none: nobody is emailed. Sending messages stays out (0013).
418
419
  const url = `${CALENDAR}/calendars/${encodeURIComponent(calendarId)}` +
419
- `/events/${encodeURIComponent(eventId)}`;
420
+ `/events/${encodeURIComponent(eventId)}?sendUpdates=none`;
420
421
  const res = await fetch(url, {
421
422
  method: 'DELETE',
422
423
  headers: { authorization: `Bearer ${token}` },
@@ -436,6 +437,39 @@ export const calendarDeleteEvent = async (calendarId, eventId, token) => {
436
437
  }
437
438
  throw new GraphError(`Google Calendar refused to delete the event (${res.status}). ${detail}`, false);
438
439
  };
440
+ /**
441
+ * Change one event in place. The only PATCH to Google, added by 0013.
442
+ *
443
+ * `If-Match` carries the etag the confirmation was made against, so an event
444
+ * that changed after the preview is refused by Google (412) rather than
445
+ * overwritten. Not retried, like every write here. sendUpdates=none: nobody is
446
+ * emailed. See docs/decisions/0013-editing-any-calendar-event.md.
447
+ */
448
+ export const calendarPatchEvent = async (calendarId, eventId, body, etag, token) => {
449
+ const url = `${CALENDAR}/calendars/${encodeURIComponent(calendarId)}` +
450
+ `/events/${encodeURIComponent(eventId)}?sendUpdates=none`;
451
+ const res = await fetch(url, {
452
+ method: 'PATCH',
453
+ headers: {
454
+ authorization: `Bearer ${token}`,
455
+ 'content-type': 'application/json',
456
+ 'if-match': etag,
457
+ },
458
+ body: JSON.stringify(body),
459
+ });
460
+ if (res.ok)
461
+ return res;
462
+ const detail = (await res.text().catch(() => '')).slice(0, 300);
463
+ if (res.status === 403 && /insufficient|ACCESS_TOKEN_SCOPE/i.test(detail)) {
464
+ throw new ScopeError('This Google connection cannot change calendar events. Reconnect with ' +
465
+ '`artist-mcp connect google` to grant it.', 'change calendar events', false);
466
+ }
467
+ if (res.status === 412) {
468
+ throw new GraphError('The event changed after it was shown to you, so nothing was written. Ask ' +
469
+ 'for the change again to see it as it is now.', false);
470
+ }
471
+ throw new GraphError(`Google Calendar refused to change the event (${res.status}). ${detail}`, false);
472
+ };
439
473
  export const CALENDAR_LIST_NEED = {
440
474
  capability: 'see which calendars you have',
441
475
  optional: true,
@@ -452,18 +486,32 @@ export const CALENDAR_LIST_NEED = {
452
486
  *
453
487
  * See docs/decisions/0003-onenote-writes.md.
454
488
  */
455
- export const onenoteCreatePage = async (sectionId, xhtml, token) => {
489
+ export const onenoteCreatePage = async (sectionId, xhtml, token,
490
+ /**
491
+ * Images the page refers to as `name:<name>`. With any, the page goes as
492
+ * multipart: the document is the part named `Presentation`, and each image
493
+ * is a part of its own. See docs/decisions/0012-images-on-a-new-page.md.
494
+ */
495
+ parts = []) => {
456
496
  const url = `${GRAPH}/me/onenote/sections/${encodeURIComponent(sectionId)}/pages`;
457
- const res = await fetch(url, {
458
- method: 'POST',
459
- headers: {
460
- authorization: `Bearer ${token}`,
461
- // Not application/json. OneNote takes the page as an XHTML document, and
462
- // sending JSON is answered with a 400 that names neither problem.
463
- 'content-type': 'application/xhtml+xml',
464
- },
465
- body: xhtml,
466
- });
497
+ let body = xhtml;
498
+ // Not application/json. OneNote takes the page as an XHTML document, and
499
+ // sending JSON is answered with a 400 that names neither problem.
500
+ const headers = {
501
+ authorization: `Bearer ${token}`,
502
+ 'content-type': 'application/xhtml+xml',
503
+ };
504
+ if (parts.length > 0) {
505
+ const form = new FormData();
506
+ form.append('Presentation', new Blob([xhtml], { type: 'text/html' }));
507
+ for (const part of parts) {
508
+ form.append(part.name, new Blob([new Uint8Array(part.bytes)], { type: part.media_type }));
509
+ }
510
+ body = form;
511
+ // fetch writes the multipart content-type itself, with the boundary in it.
512
+ delete headers['content-type'];
513
+ }
514
+ const res = await fetch(url, { method: 'POST', headers, body });
467
515
  // Not retried, exactly as the calendar insert is not: a repeated create makes
468
516
  // a second page, and a 5xx does not say whether the first one landed. OneNote
469
517
  // accepts no client-set id, so there is no collision to rely on either — which
@@ -501,6 +549,29 @@ export const onenotePageContent = async (pageId, token) => {
501
549
  const res = await graphGet(`/me/onenote/pages/${encodeURIComponent(pageId)}/content?includeIDs=true`, token);
502
550
  return { html: await res.text(), etag: res.headers.get('etag') };
503
551
  };
552
+ /**
553
+ * Create one section in a notebook.
554
+ *
555
+ * Narrow like `onenoteCreatePage`: one notebook id, one name, one POST, and no
556
+ * retry, because a 5xx does not say whether the section landed and a second
557
+ * attempt would make a second section. See docs/decisions/0011-creating-sections.md.
558
+ */
559
+ export const onenoteCreateSection = async (notebookId, displayName, token) => {
560
+ const url = `${GRAPH}/me/onenote/notebooks/${encodeURIComponent(notebookId)}/sections`;
561
+ const res = await fetch(url, {
562
+ method: 'POST',
563
+ headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },
564
+ body: JSON.stringify({ displayName }),
565
+ });
566
+ if (res.ok)
567
+ return res;
568
+ const detail = (await res.text().catch(() => '')).slice(0, 300);
569
+ if ((res.status === 401 || res.status === 403) && /40004|scope/i.test(detail)) {
570
+ throw new ScopeError('This Microsoft connection cannot create OneNote sections. Reconnect with ' +
571
+ '`artist-mcp connect microsoft`.', 'create OneNote sections', false);
572
+ }
573
+ throw new GraphError(`OneNote refused to create the section (${res.status}). ${detail}`, false);
574
+ };
504
575
  /**
505
576
  * Apply patch commands to one page.
506
577
  *