ask-marcel-office-cli 2.2.0 → 2.4.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/CHANGELOG.md +201 -0
- package/README.md +45 -26
- package/dist/cli.js +2964 -2446
- package/dist/commands.json +254 -727
- package/dist/composition/run-registry-command.d.ts +12 -3
- package/dist/domain/utilities/base64.d.ts +11 -0
- package/dist/index.js +2121 -1737
- package/dist/infra/auth.d.ts +30 -4
- package/dist/infra/browser-auth.d.ts +21 -1
- package/dist/presenter/output-text.d.ts +1 -1
- package/dist/presenter/output.d.ts +2 -2
- package/dist/presenter/render-to-string.d.ts +42 -2
- package/dist/use-cases/commands/command-types.d.ts +1 -20
- package/dist/use-cases/commands/create-forward-draft.d.ts +1 -1
- package/dist/use-cases/commands/create-reply-draft.d.ts +1 -1
- package/dist/use-cases/commands/docs-render.d.ts +0 -1
- package/dist/use-cases/commands/docx-metadata.d.ts +32 -2
- package/dist/use-cases/commands/draft-comment-splicer.d.ts +8 -1
- package/dist/use-cases/commands/include-hidden-folders.d.ts +2 -0
- package/dist/use-cases/commands/list-mail-folder-messages-delta.d.ts +10 -2
- package/dist/use-cases/commands/list-shared-mailbox-child-folders.d.ts +4 -0
- package/dist/use-cases/commands/list-shared-mailbox-folders.d.ts +4 -0
- package/dist/use-cases/commands/login-status.d.ts +2 -0
- package/dist/use-cases/commands/login.d.ts +18 -8
- package/dist/use-cases/commands/next-page.d.ts +1 -0
- package/dist/use-cases/commands/ooxml-xml-walker.d.ts +31 -6
- package/dist/use-cases/commands/pptx-comments.d.ts +8 -5
- package/dist/use-cases/commands/reject-unknown-params.d.ts +16 -0
- package/dist/use-cases/commands/resolve-command.d.ts +7 -9
- package/dist/use-cases/commands/search-onenote-pages.d.ts +1 -1
- package/dist/use-cases/commands/token-tier-capability.d.ts +5 -0
- package/dist/use-cases/commands/update-mail-draft.d.ts +4 -0
- package/docs/COMMANDS.md +15 -13
- package/docs/USAGE.md +2 -2
- package/package.json +25 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,207 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `ask-marcel-office-cli` are documented here.
|
|
4
4
|
|
|
5
|
+
## 2.4.0
|
|
6
|
+
|
|
7
|
+
Two community fixes from [@a-tokyo](https://github.com/a-tokyo). Nothing breaks:
|
|
8
|
+
the new flag is optional, and every existing invocation routes exactly as before.
|
|
9
|
+
|
|
10
|
+
### Fixed — share links whose filename is not ASCII
|
|
11
|
+
|
|
12
|
+
`buildShareToken` fed the raw URL string to `btoa`, which only accepts Latin-1.
|
|
13
|
+
A sharing URL with an accented filename minted a token that resolved to nothing,
|
|
14
|
+
and one with a CJK or emoji filename threw `InvalidCharacterError`, which crashed
|
|
15
|
+
the command from inside `Promise.all`. The URL's UTF-8 bytes are now encoded
|
|
16
|
+
before base64url, which is what Graph's `/shares/{token}` resolver expects.
|
|
17
|
+
|
|
18
|
+
Six commands build that token and all six were affected:
|
|
19
|
+
|
|
20
|
+
| Command | How it reaches the token |
|
|
21
|
+
|---------|--------------------------|
|
|
22
|
+
| `resolve-drive-share-link` | encodes the sharing URL directly |
|
|
23
|
+
| `convert-mail-attachment-to-markdown` | resolves an attachment's source URL |
|
|
24
|
+
| `convert-mail-attachment-to-pdf` | resolves an attachment's source URL |
|
|
25
|
+
| `extract-mail-attachment-images` | resolves an attachment's source URL |
|
|
26
|
+
| `extract-sharepoint-links-in-mail` | one resolve per link found in the body |
|
|
27
|
+
| `extract-sharepoint-links-in-documents` | one resolve per link found in the text |
|
|
28
|
+
|
|
29
|
+
The byte-to-base64 step moved into a shared `bytesToBase64` codec under
|
|
30
|
+
`domain/utilities`, replacing the private copy that lived in `fetch-raw-bytes`.
|
|
31
|
+
|
|
32
|
+
### Added — `next-page --tenant-id`
|
|
33
|
+
|
|
34
|
+
`next-page` re-signs a cursor to match the identity its originating command used,
|
|
35
|
+
but it knew only two: the home basic token and the elevated chat token. A
|
|
36
|
+
partner-tenant drive listing signs page 1 with a guest token, and the
|
|
37
|
+
`@odata.nextLink` it emits carries no tenant, so page 2 was re-signed with the
|
|
38
|
+
home token and Graph answered `401 invalidAudienceUri`. Anything longer than one
|
|
39
|
+
page in a partner tenant was unreadable past page 1.
|
|
40
|
+
|
|
41
|
+
Pass the same `--tenant-id` you gave the originating command (ultimately from
|
|
42
|
+
`resolve-drive-share-link`) and the cursor is re-signed with a guest token for
|
|
43
|
+
that tenant. The flag reuses the existing `tenant-option` branding, so a
|
|
44
|
+
malformed GUID is rejected at the boundary before any Graph call.
|
|
45
|
+
|
|
46
|
+
The footer carries the flag for you. `next:` and `delta:` both render
|
|
47
|
+
`--tenant-id <guid>` whenever the call that produced the cursor used one, so the
|
|
48
|
+
line stays copy-pasteable rather than becoming a 401 one step later. This is the
|
|
49
|
+
line an MCP agent reads to build its next call, not just a terminal convenience.
|
|
50
|
+
|
|
51
|
+
### Added — docx tracked changes report replacements as one edit
|
|
52
|
+
|
|
53
|
+
OOXML has no "replace" revision: Word records replacing a span as a deletion
|
|
54
|
+
sitting next to an insertion. Reported as two loose halves, one edit read as an
|
|
55
|
+
unrelated cut plus an unrelated addition somewhere else, with nothing linking
|
|
56
|
+
them. A deletion and an insertion that are adjacent siblings by the same author
|
|
57
|
+
now come back as a single `replacements` entry carrying `before` and `after`,
|
|
58
|
+
and the two halves leave `insertions` / `deletions` so nothing is counted twice.
|
|
59
|
+
The markdown output gains a `### Tracked changes — replacements` section above
|
|
60
|
+
the two existing ones.
|
|
61
|
+
|
|
62
|
+
Adjacency is real document order, not a guess. The default XML parse groups
|
|
63
|
+
same-named siblings per tag, which cannot distinguish del, ins, del, ins from
|
|
64
|
+
del, del, ins, ins, so revisions are re-read through a second order-preserving
|
|
65
|
+
parser. Pairing is scoped to siblings of one parent, requires the same author on
|
|
66
|
+
both halves, and treats Word's glyph-free markers (`w:proofErr`, bookmark and
|
|
67
|
+
comment anchors) as not separating a pair, while any untouched run of prose does.
|
|
68
|
+
|
|
69
|
+
Still unreported, unchanged by this: `w:moveFrom` / `w:moveTo`, `w:rPrChange`,
|
|
70
|
+
`w:pPrChange`, and the table-structure revisions. A document whose only tracked
|
|
71
|
+
edit is a formatting change still reports no tracked changes at all.
|
|
72
|
+
|
|
73
|
+
### Added — docx moves and formatting changes are reported at all
|
|
74
|
+
|
|
75
|
+
A docx whose only tracked edits were a moved paragraph or a reformat reported
|
|
76
|
+
no tracked changes whatsoever, which reads as a clean document rather than an
|
|
77
|
+
unreviewed one. `w:moveFrom` / `w:moveTo` and `w:rPrChange` / `w:pPrChange` are
|
|
78
|
+
now extracted into two new sections.
|
|
79
|
+
|
|
80
|
+
`moves` joins the two ends of a move through the `w:name` on the range-start
|
|
81
|
+
markers that bracket them, not by matching their text: Word does guarantee the
|
|
82
|
+
halves carry identical content, so text-matching looks free right up to a
|
|
83
|
+
document that moves the same sentence twice and silently cross-pairs four halves
|
|
84
|
+
into two moves that never happened. A half whose partner is missing still gets a
|
|
85
|
+
row, marked `from-only` or `to-only`.
|
|
86
|
+
|
|
87
|
+
`formatChanges` names which properties a reviewer altered, comparing attributes
|
|
88
|
+
as well as tag names. `w:color` sits on both sides of a recolour with only its
|
|
89
|
+
`w:val` moving, so a comparison of tag names alone calls that run unchanged.
|
|
90
|
+
Run-level and paragraph-level changes share one list, separated by `scope`.
|
|
91
|
+
|
|
92
|
+
Table-structure revisions (cell insert / delete / merge, numbering, section and
|
|
93
|
+
table properties) remain unreported, and the `--include-metadata` docs now say
|
|
94
|
+
so rather than leaving it to be discovered.
|
|
95
|
+
|
|
96
|
+
### Added — `get-mail-signature` empty states carry an `errorCode`
|
|
97
|
+
|
|
98
|
+
Finding no signature used to come back as a validation error with a good
|
|
99
|
+
message and no code, so an agent routing on `errorCode` saw nothing and the
|
|
100
|
+
live QA sweep logged it as `ERR:err`. Two codes now name the two distinct
|
|
101
|
+
misses: `signature_not_found` when the scanned or pinned message carries no
|
|
102
|
+
OWA signature block, and `no_sent_messages` when there is nothing to scan at
|
|
103
|
+
all. The messages are unchanged.
|
|
104
|
+
|
|
105
|
+
### Fixed — `scopes-check` never opens a browser
|
|
106
|
+
|
|
107
|
+
`scopes-check` obtained the basic token through the same acquiring getter as
|
|
108
|
+
every other command, and in a terminal that getter heals an expired session by
|
|
109
|
+
opening a browser; when the persistent profile is already signed in it also
|
|
110
|
+
clears the session so the OAuth grant can re-fire, which drops the 90-day
|
|
111
|
+
"Stay signed in" cookie. A diagnostic did all of that on 2026-09-02. The basic
|
|
112
|
+
tier is now read straight off the cache like the elevated and Teams tiers
|
|
113
|
+
already were: an expired token reports a negative `expiresInSeconds`, an empty
|
|
114
|
+
cache reports not-signed-in, and `login` remains the command that refreshes.
|
|
115
|
+
Bring-your-own-token managers built with `createGraphClient` are unaffected.
|
|
116
|
+
|
|
117
|
+
### Added — an `invalidAudienceUri` error hint
|
|
118
|
+
|
|
119
|
+
The one 401 that re-authenticating cannot fix now says so. Graph cannot mint a
|
|
120
|
+
SharePoint token for a tenant you are only a guest in, so an agent told to run
|
|
121
|
+
`login` here loops forever. The hint names `--tenant-id`, points at
|
|
122
|
+
`resolve-drive-share-link` for the GUID, and covers the paginated case where the
|
|
123
|
+
cursor carries no tenant of its own. It sits above the token-expiry rule, which
|
|
124
|
+
would otherwise claim the same 401 and give the wrong advice.
|
|
125
|
+
|
|
126
|
+
## 2.3.0
|
|
127
|
+
|
|
128
|
+
> **Read this before upgrading.** This release contains breaking changes to the
|
|
129
|
+
> flag surface despite the minor version number. If you pin `^2.2.0` you will
|
|
130
|
+
> receive it automatically. Anything calling the CLI, the MCP gateway, or the
|
|
131
|
+
> library with an old flag spelling will stop working.
|
|
132
|
+
|
|
133
|
+
### Removed — every flag alias and every deprecated command name
|
|
134
|
+
|
|
135
|
+
One name per flag, one name per command. The alias system is gone: 77 flag
|
|
136
|
+
aliases and 4 deprecated command names. Each alias already pointed at a more
|
|
137
|
+
specific canonical, so nothing was renamed to accommodate the removal.
|
|
138
|
+
|
|
139
|
+
| Gone | Use instead |
|
|
140
|
+
|------|-------------|
|
|
141
|
+
| `--id` (51 commands) | the command's specific id flag (`--message-id`, `--site-id`, …) |
|
|
142
|
+
| `--start` / `--end` (7 date-window commands) | `--start-date-time` / `--end-date-time` |
|
|
143
|
+
| `--body-content` on `create-reply-draft` / `create-forward-draft` | `--comment` |
|
|
144
|
+
| `--task-id` | `--planner-task-id` or `--todo-task-id` |
|
|
145
|
+
| `--folder-id` | `--item-id` |
|
|
146
|
+
| `--item-id` on the SharePoint list commands | `--list-item-id` |
|
|
147
|
+
| `--page-id`, `--section-id` | `--onenote-page-id`, `--onenote-section-id` |
|
|
148
|
+
| `--emails` on `get-schedule` | `--schedules` |
|
|
149
|
+
| `--query` on `search-onenote-pages` | *(now the canonical name, see below)* |
|
|
150
|
+
| `download-onedrive-file-content` | `download-drive-item-content` |
|
|
151
|
+
| `convert-local-file` | `convert-local-file-to-markdown` |
|
|
152
|
+
| `convert-drive-item-zip` | `convert-drive-item-zip-to-markdown` |
|
|
153
|
+
| `convert-mail-attachment-zip` | `convert-mail-attachment-zip-to-markdown` |
|
|
154
|
+
|
|
155
|
+
Why: the same spelling meant different things on sibling commands. That is how
|
|
156
|
+
`--body-content` came to mean "the text above the quote" on the create-draft
|
|
157
|
+
commands and "replace the ENTIRE body, quote included" on `update-mail-draft`,
|
|
158
|
+
a silent data-loss trap fixed in this same release.
|
|
159
|
+
|
|
160
|
+
### Changed — `search-onenote-pages --title-substring` is now `--query`
|
|
161
|
+
|
|
162
|
+
The last flag naming a shared concept differently from its siblings. All eight
|
|
163
|
+
search commands now take `--query`. (`--query` had already been bolted onto this
|
|
164
|
+
command as an alias for exactly this reason; it is now the only name.) The flag
|
|
165
|
+
still matches page TITLES only, which its description states explicitly.
|
|
166
|
+
|
|
167
|
+
### Changed — a parameter a command does not declare is now refused
|
|
168
|
+
|
|
169
|
+
Previously the three surfaces disagreed. Commander rejected unknown `--flags` on
|
|
170
|
+
the CLI, but Zod strips unknown keys, so the MCP gateway's `run-command` params
|
|
171
|
+
and direct library `commands[x].execute(...)` calls silently swallowed them and
|
|
172
|
+
returned data that looked like it had obeyed. A live audit found this on 5 of 7
|
|
173
|
+
delta commands. All three surfaces now refuse identically, with
|
|
174
|
+
`errorCode: "unknown_parameter"` and the supported flags named in the message.
|
|
175
|
+
|
|
176
|
+
### Fixed — `--top` no longer terminates a mail-folder delta sync
|
|
177
|
+
|
|
178
|
+
`list-mail-folder-messages-delta --top 2` on a 67-message Inbox returned 2
|
|
179
|
+
messages and an `@odata.deltaLink` rather than a `nextLink`. Graph reads a
|
|
180
|
+
*satisfied* `$top` as "this sync is complete", so the other 65 messages were
|
|
181
|
+
never delivered and the caller banked a delta token certifying a sync that never
|
|
182
|
+
happened. `--top` is now sent as `Prefer: odata.maxpagesize`, which pages
|
|
183
|
+
normally. `--skip` and `--orderby` are no longer advertised there (Graph ignores
|
|
184
|
+
the first and rejects the second), and `--filter` / `--orderby` are gone from
|
|
185
|
+
the two drive delta commands for the same reason.
|
|
186
|
+
|
|
187
|
+
### Fixed — a whole-body draft update can no longer drop quoted history
|
|
188
|
+
|
|
189
|
+
`update-mail-draft --body-content` on a threaded draft replaced the entire body
|
|
190
|
+
including the quoted thread, with nothing warning at call time. It now reads the
|
|
191
|
+
draft first and refuses when a quote is present, pointing at `--comment`. Pass
|
|
192
|
+
`--replace-quoted-history true` for the deliberate case. The same read refuses
|
|
193
|
+
non-drafts before the write.
|
|
194
|
+
|
|
195
|
+
### Fixed — the oversized-response banner only names remedies that exist
|
|
196
|
+
|
|
197
|
+
The `sizeHint` banner called `--output-path` a "universal remedy (works on every
|
|
198
|
+
command)". It is not: plain-JSON commands refuse the flag, and the two commands
|
|
199
|
+
that trip the banner most often (`search-all-files`, `microsoft-search-query`)
|
|
200
|
+
advertise only `--query`, so every remedy it named was a dead end. The banner is
|
|
201
|
+
now derived from the command that produced the payload and the surface asking:
|
|
202
|
+
byte-producing commands keep the `--output-path` wording (`outputPath` on MCP),
|
|
203
|
+
plain-JSON commands on the CLI get a shell redirect, and the same commands on
|
|
204
|
+
MCP get neither, since an MCP client has no shell.
|
|
205
|
+
|
|
5
206
|
## 2.2.0
|
|
6
207
|
|
|
7
208
|
### Added
|
package/README.md
CHANGED
|
@@ -4,17 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
### The Microsoft 365 command line built for AI agents
|
|
6
6
|
|
|
7
|
-
**One sign-in.
|
|
7
|
+
**One sign-in. No Azure app registration. Every document as clean markdown.**
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/ask-marcel-office-cli)
|
|
10
10
|
[](LICENSE)
|
|
11
|
-
[](https://nodejs.org)
|
|
12
|
-
[](https://bun.sh)
|
|
13
11
|
[](docs/USAGE.md)
|
|
14
12
|
|
|
15
13
|
Outlook · OneDrive · SharePoint · Calendar · Excel · Teams · Planner · To Do · OneNote · People
|
|
16
14
|
|
|
17
|
-
[Install](#install-in-60-seconds) · [See it work](#see-it-work) · [What it reaches](#what-your-agent-can-reach) · [Files to markdown](#any-file-becomes-markdown) · [Library](#embed-it-as-a-typescript-library) · [All
|
|
15
|
+
[Install](#install-in-60-seconds) · [See it work](#see-it-work) · [What it reaches](#what-your-agent-can-reach) · [Files to markdown](#any-file-becomes-markdown) · [Library](#embed-it-as-a-typescript-library) · [All 186 commands](docs/COMMANDS.md)
|
|
18
16
|
|
|
19
17
|
</div>
|
|
20
18
|
|
|
@@ -32,15 +30,38 @@ ask-marcel-office list-mail-messages --top 5
|
|
|
32
30
|
|
|
33
31
|
That is the whole setup. **No Azure app registration. No tenant-admin consent. No client secrets.** And nothing for a runaway agent to break: the command surface is read-only by design.
|
|
34
32
|
|
|
33
|
+
<p align="center">
|
|
34
|
+
<img src="https://raw.githubusercontent.com/ask-marcel/ask-marcel-office-cli/main/docs/demo.gif" alt="ask-marcel-office converting an Outlook .msg file into clean markdown in one command, offline" width="880">
|
|
35
|
+
</p>
|
|
36
|
+
|
|
37
|
+
## The difference in practice
|
|
38
|
+
|
|
39
|
+
| You need | Raw Microsoft Graph | ask-marcel-office |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| Access | App registration, admin consent, secret rotation | `login`: one browser sign-in with your own account |
|
|
42
|
+
| An email + attachments | 6+ round trips, HTML-to-text is your problem | `convert-mail-to-markdown`: one call, markdown out |
|
|
43
|
+
| A pptx / pdf / legacy .doc as text | Raw bytes, bring your own converter | `download-drive-item-as-markdown`: one call |
|
|
44
|
+
| A useful error | `BadRequest: Invalid filter clause` | `hint: "string literals MUST use single quotes; embed one by doubling it"` |
|
|
45
|
+
| A 5 MB PDF | Base64 flooding the context window | `--output-path` writes it to disk; the model reads 3 lines |
|
|
46
|
+
| Throttling | HTTP 429, guess the backoff | `retryAfterSeconds` surfaced in the error envelope |
|
|
47
|
+
|
|
48
|
+
And against the tools you already know:
|
|
49
|
+
|
|
50
|
+
| | Microsoft Graph SDKs | CLI for Microsoft 365 (PnP) | ask-marcel-office |
|
|
51
|
+
|---|---|---|---|
|
|
52
|
+
| Sign-in | An Entra app registration, always: every auth flow needs a client id | An Entra app registration; its `setup` command creates one, and the permissions it requests can need admin consent | Your own account, in a browser, once. No app registration |
|
|
53
|
+
| Built for | Backend services | Admins and scripts | Tool-calling LLMs: markdown out, lean fields, a repair hint on every error |
|
|
54
|
+
| Can it break things | Whatever your app's permissions allow | Yes: it creates, changes, and deletes across the tenant | No: read-only by design; the four writes only leave unsent drafts |
|
|
55
|
+
|
|
35
56
|
## The three walls it removes
|
|
36
57
|
|
|
37
58
|
### 🔑 Sign in like a human, not like an app
|
|
38
59
|
|
|
39
|
-
Microsoft Graph normally means registering an Azure app, chasing tenant-admin consent, and rotating client secrets before the first API call. Here, `login` drives a real browser window through the standard Microsoft sign-in (Playwright under the hood) and captures the same token the Teams web client already uses. Any Microsoft 365 account works, personal or enterprise. Tokens are cached at `~/.ask-marcel/token-cache.json` (0600) and refresh themselves headlessly; `scopes-check` reports per-token scopes and expiry
|
|
60
|
+
Microsoft Graph normally means registering an Azure app, chasing tenant-admin consent, and rotating client secrets before the first API call. Here, `login` drives a real browser window through the standard Microsoft sign-in (Playwright under the hood) and captures the same token the Teams web client already uses. Any Microsoft 365 account works, personal or enterprise. Tokens are cached at `~/.ask-marcel/token-cache.json` (0600) and refresh themselves headlessly; `scopes-check` reports per-token scopes and expiry without a Graph call and without ever opening a browser: an expired session is reported as expired, not re-captured.
|
|
40
61
|
|
|
41
62
|
### 🛡️ Safe to hand to an autonomous agent
|
|
42
63
|
|
|
43
|
-
The
|
|
64
|
+
The 186 commands break down as 178 GET, 4 read-only POST (three searches and a free/busy lookup), and 4 mail-draft operations. No `send-mail`. No `create-event`. No `upload-file`. No `delete-anything`. The worst a hallucinated tool call can do is leave an unsent draft in your Drafts folder. That is the entire blast radius, which is why you can let an agent explore a mailbox without reviewing every call. No analytics, either: the only outbound traffic is Microsoft Graph and a periodic npm version check.
|
|
44
65
|
|
|
45
66
|
### 🧠 Responses budgeted for a context window
|
|
46
67
|
|
|
@@ -53,17 +74,26 @@ Reading one email with its attachments in raw Graph: GET the message, GET the at
|
|
|
53
74
|
```console
|
|
54
75
|
$ ask-marcel-office convert-mail-to-markdown --message-id "AAMkAD..."
|
|
55
76
|
|
|
56
|
-
|
|
77
|
+
**Subject:** Q3 budget review: action needed
|
|
57
78
|
**From:** Robin Chen <robin.chen@contoso.com>
|
|
79
|
+
**To:** Alex Kim <alex.kim@contoso.com>
|
|
80
|
+
**Date:** 2026-09-02T08:14:22Z
|
|
58
81
|
|
|
59
82
|
Before Friday's review, the forecast tab still shows last quarter's
|
|
60
83
|
headcount. Can you sanity-check the attached numbers?
|
|
61
84
|
|
|
62
|
-
|
|
63
|
-
|
|
85
|
+
Robin
|
|
86
|
+
|
|
87
|
+
\[inline image: contoso-signature.png\]
|
|
88
|
+
|
|
89
|
+
_\[Quoted reply chain removed — pass --keep-quoted true to include it\]_
|
|
90
|
+
|
|
91
|
+
**Attachments:**
|
|
92
|
+
- Q3-forecast.xlsx (49.2 KB, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, id: AAMkAD...)
|
|
93
|
+
_Use `convert-mail-attachment-to-pdf` or `get-mail-attachment` with the attachment id to fetch._
|
|
64
94
|
```
|
|
65
95
|
|
|
66
|
-
|
|
96
|
+
Those two bracketed lines are the entire cost of a 40-message thread and a signature logo. Attachments arrive with their ids ready for the follow-up call:
|
|
67
97
|
|
|
68
98
|
```console
|
|
69
99
|
$ ask-marcel-office convert-mail-attachment-to-markdown \
|
|
@@ -77,22 +107,11 @@ $ ask-marcel-office microsoft-search-query --query "Q3 budget filetype:pptx"
|
|
|
77
107
|
$ ask-marcel-office download-drive-item-as-markdown --drive-id "b!abc..." --item-id "01BYE..."
|
|
78
108
|
```
|
|
79
109
|
|
|
80
|
-
## The difference in practice
|
|
81
|
-
|
|
82
|
-
| You need | Raw Microsoft Graph | ask-marcel-office |
|
|
83
|
-
|---|---|---|
|
|
84
|
-
| Access | App registration, admin consent, secret rotation | `login`: one browser sign-in with your own account |
|
|
85
|
-
| An email + attachments | 6+ round trips, HTML-to-text is your problem | `convert-mail-to-markdown`: one call, markdown out |
|
|
86
|
-
| A pptx / pdf / legacy .doc as text | Raw bytes, bring your own converter | `download-drive-item-as-markdown`: one call |
|
|
87
|
-
| A useful error | `BadRequest: Invalid filter clause` | `hint: "string literals MUST use single quotes; embed one by doubling it"` |
|
|
88
|
-
| A 5 MB PDF | Base64 flooding the context window | `--output-path` writes it to disk; the model reads 3 lines |
|
|
89
|
-
| Throttling | HTTP 429, guess the backoff | `retryAfterSeconds` surfaced in the error envelope |
|
|
90
|
-
|
|
91
110
|
## What your agent can reach
|
|
92
111
|
|
|
93
112
|
| Surface | Commands | In practice |
|
|
94
113
|
|---|---:|---|
|
|
95
|
-
| 📧 Outlook Mail |
|
|
114
|
+
| 📧 Outlook Mail | 38 | Search and read mail as markdown, convert any attachment (down to nested `.zip` and `.msg`), resolve SharePoint links in bodies, extract your signature, find existing drafts on a thread, prepare reply / forward drafts (the only writes) |
|
|
96
115
|
| 📁 OneDrive + SharePoint | 49 | Discover every drive and site your token can reach, search files, read any document as markdown or PDF, version history, share links resolved even into partner tenants where you're a guest |
|
|
97
116
|
| 📅 Calendar | 24 | "What's on this week" via relative dates (`today`, `start-of-week`, `+7d`), event details, free/busy lookups |
|
|
98
117
|
| 👥 People + directory | 16 | People search, user profiles, the directory around you, your own identity and IDs in one round trip |
|
|
@@ -178,13 +197,13 @@ claude mcp add --transport stdio --scope user ask-marcel-office -- npx -y ask-ma
|
|
|
178
197
|
|
|
179
198
|
> **Two things to know.** The first launch downloads the package into npx's cache, so allow it one slow start; every start after that is instant. And GUI apps don't inherit your shell's `PATH`: terminal-launched clients resolve `"npx"` fine, but a client launched from the Dock or Start menu (Claude Desktop) may not find it — if the server won't start, replace `"command": "npx"` with the absolute path `which npx` prints. Bun users: `bunx` works in place of `npx -y`.
|
|
180
199
|
|
|
181
|
-
You get **five gateway tools**, not one per command —
|
|
200
|
+
You get **five gateway tools**, not one per command — a schema per command would bloat every session, the opposite of the point:
|
|
182
201
|
|
|
183
202
|
| Tool | Does |
|
|
184
203
|
|:--|:--|
|
|
185
204
|
| `list-commands` | The terse manifest. Start here; `category` narrows it. |
|
|
186
205
|
| `get-command-docs` | Full docs for one command: options, endpoint, example. |
|
|
187
|
-
| `run-command` | The
|
|
206
|
+
| `run-command` | The 182 **read** commands. `readOnlyHint: true`, so clients can auto-approve it. |
|
|
188
207
|
| `run-write-command` | The 4 mail-draft **write** commands. Separate tool so the read tool's promise stays honest. |
|
|
189
208
|
| `login` | Sign in / refresh. Opens a browser on this machine. |
|
|
190
209
|
|
|
@@ -230,14 +249,14 @@ Azure Managed Identity, an on-behalf-of flow, hand-pasted JWTs in tests: the Gra
|
|
|
230
249
|
|
|
231
250
|
## Deep docs
|
|
232
251
|
|
|
233
|
-
- **[All
|
|
252
|
+
- **[All 186 commands](docs/COMMANDS.md)**: per-category tables with required params + Graph endpoint
|
|
234
253
|
- **[Usage guide](docs/USAGE.md)**: output formats, OData passthrough, `--output-path`, pagination, library API, architecture, configuration
|
|
235
254
|
- **[Machine-readable manifest](docs/commands.json)**: JSON for programmatic discovery, also importable via `import manifest from 'ask-marcel-office-cli/commands.json'`
|
|
236
255
|
- **[QA playbook](docs/QA-PLAYBOOK.md)**: the repeatable full-surface health check run before each release
|
|
237
256
|
|
|
238
257
|
## Roadmap
|
|
239
258
|
|
|
240
|
-
Read-only stays the default forever. Coverage grows out of real LLM workflows as they come up: suggestions, requests, and pull requests welcome on the [issues page](https://github.com/
|
|
259
|
+
Read-only stays the default forever. Coverage grows out of real LLM workflows as they come up: suggestions, requests, and pull requests welcome on the [issues page](https://github.com/ask-marcel/ask-marcel-office-cli/issues).
|
|
241
260
|
|
|
242
261
|
## Built with
|
|
243
262
|
|