ask-marcel-office-cli 2.0.0 → 2.2.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 +269 -0
- package/README.md +178 -118
- package/dist/cli.js +44841 -26996
- package/dist/commands.json +423 -51
- package/dist/composition/build-deps.d.ts +8 -0
- package/dist/composition/mcp.d.ts +20 -0
- package/dist/composition/run-registry-command.d.ts +34 -0
- package/dist/domain/tenant-id.d.ts +9 -0
- package/dist/domain/utilities/spo-tenant.d.ts +20 -0
- package/dist/index.js +2671 -1463
- package/dist/infra/auth.d.ts +55 -3
- package/dist/infra/browser-auth.d.ts +10 -3
- package/dist/infra/graph-client.d.ts +62 -0
- package/dist/presenter/graph-cursor.d.ts +2 -0
- package/dist/presenter/output.d.ts +1 -1
- package/dist/presenter/render-to-string.d.ts +15 -0
- package/dist/use-cases/commands/build-command.d.ts +19 -2
- package/dist/use-cases/commands/command-types.d.ts +16 -11
- package/dist/use-cases/commands/convert-calendar-event-attachment-to-markdown.d.ts +4 -0
- package/dist/use-cases/commands/{convert-drive-item-zip.d.ts → convert-drive-item-zip-to-markdown.d.ts} +5 -0
- package/dist/use-cases/commands/{convert-local-file.d.ts → convert-local-file-to-markdown.d.ts} +8 -0
- package/dist/use-cases/commands/convert-mail-attachment-to-markdown.d.ts +10 -2
- package/dist/use-cases/commands/{convert-mail-attachment-zip.d.ts → convert-mail-attachment-zip-to-markdown.d.ts} +5 -1
- package/dist/use-cases/commands/convert-mail-to-markdown.d.ts +35 -1
- package/dist/use-cases/commands/create-forward-draft.d.ts +16 -0
- package/dist/use-cases/commands/create-reply-draft.d.ts +5 -1
- package/dist/use-cases/commands/download-drive-item-as-markdown.d.ts +7 -2
- package/dist/use-cases/commands/download-drive-item-as-pdf.d.ts +1 -0
- package/dist/use-cases/commands/download-drive-item-content.d.ts +1 -0
- package/dist/use-cases/commands/draft-comment-splicer.d.ts +29 -0
- package/dist/use-cases/commands/draft-dedup.d.ts +18 -0
- package/dist/use-cases/commands/draft-response.d.ts +4 -0
- package/dist/use-cases/commands/extract-drive-item-images.d.ts +1 -0
- package/dist/use-cases/commands/extract-local-file-images.d.ts +1 -1
- package/dist/use-cases/commands/fetch-raw-bytes.d.ts +13 -0
- package/dist/use-cases/commands/find-mail-drafts.d.ts +9 -0
- package/dist/use-cases/commands/get-mail-signature.d.ts +8 -0
- package/dist/use-cases/commands/get-user.d.ts +10 -0
- package/dist/use-cases/commands/image-extraction.d.ts +2 -1
- package/dist/use-cases/commands/inline-image-embedder.d.ts +2 -1
- package/dist/use-cases/commands/login-status.d.ts +13 -0
- package/dist/use-cases/commands/login.d.ts +24 -1
- package/dist/use-cases/commands/mail-message-select.d.ts +1 -0
- package/dist/use-cases/commands/mail-quote-stripper.d.ts +16 -15
- package/dist/use-cases/commands/markdown-dispatch.d.ts +2 -1
- package/dist/use-cases/commands/msg-to-markdown.d.ts +10 -1
- package/dist/use-cases/commands/odata-query.d.ts +15 -1
- package/dist/use-cases/commands/office-to-markdown.d.ts +1 -0
- package/dist/use-cases/commands/parse-recipients.d.ts +14 -0
- package/dist/use-cases/commands/read-mail-attachment.d.ts +4 -0
- package/dist/use-cases/commands/resolve-command.d.ts +25 -0
- package/dist/use-cases/commands/search-all-files.d.ts +8 -0
- package/dist/use-cases/commands/search-escape.d.ts +23 -0
- package/dist/use-cases/commands/signature-extractor.d.ts +2 -0
- package/dist/use-cases/commands/tenant-option.d.ts +44 -0
- package/dist/use-cases/commands/update-mail-draft.d.ts +1 -0
- package/dist/use-cases/commands/zip-archive-to-markdown.d.ts +12 -5
- package/dist/use-cases/ports/filesystem.d.ts +1 -1
- package/docs/COMMANDS.md +34 -28
- package/docs/USAGE.md +65 -7
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,140 +1,198 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# ask-marcel-office
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
npm i -g ask-marcel-office-cli
|
|
7
|
-
ask-marcel-office login # browser opens once, token cached
|
|
8
|
-
ask-marcel-office my-quick-context # who am I + my IDs, in one round trip
|
|
9
|
-
ask-marcel-office list-mail-messages --top 5
|
|
10
|
-
```
|
|
5
|
+
### The Microsoft 365 command line built for AI agents
|
|
11
6
|
|
|
12
|
-
|
|
7
|
+
**One sign-in. 180 read-only commands. Every document as clean markdown.**
|
|
13
8
|
|
|
14
|
-
|
|
9
|
+
[](https://www.npmjs.com/package/ask-marcel-office-cli)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
[](https://nodejs.org)
|
|
12
|
+
[](https://bun.sh)
|
|
13
|
+
[](docs/USAGE.md)
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
Outlook · OneDrive · SharePoint · Calendar · Excel · Teams · Planner · To Do · OneNote · People
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
2. **Default payloads are tuned for backend services, not context windows.** Listing endpoints return every field on every item, used-range Excel calls return four redundant 2D arrays, attachment endpoints inline base64 by default. An agent that reads "what's in my inbox" without trimming burns its budget on metadata it never needed.
|
|
20
|
-
3. **Errors are opaque.** `BadRequest: Invalid filter clause` doesn't tell a model what to fix.
|
|
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 184 commands](docs/COMMANDS.md)
|
|
21
18
|
|
|
22
|
-
|
|
19
|
+
</div>
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
---
|
|
25
22
|
|
|
26
|
-
|
|
23
|
+
Your agent is smart enough to answer *"what did Contoso say about the Q3 budget, and what's in the attached deck?"* It just can't see your mailbox.
|
|
27
24
|
|
|
28
|
-
|
|
25
|
+
`ask-marcel-office` gives any tool-calling LLM (Claude Code, Cursor, Cline, an MCP server, your own loop) eyes on your entire Microsoft 365:
|
|
29
26
|
|
|
30
|
-
|
|
27
|
+
```bash
|
|
28
|
+
npm i -g ask-marcel-office-cli
|
|
29
|
+
ask-marcel-office login # your normal Microsoft sign-in, in a browser, once
|
|
30
|
+
ask-marcel-office list-mail-messages --top 5
|
|
31
|
+
```
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
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.
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
## The three walls it removes
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
- Quoted reply chains / forwarded-message blocks stripped by default so a long thread doesn't duplicate earlier messages into the model's context (the cut is replaced with a visible marker; opt out with `--keep-quoted true` to keep the full body)
|
|
38
|
-
- Inline images embedded as base64 `data:` URIs (size-capped per image — opt out with `--inline-images false` to keep raw `cid:` refs)
|
|
39
|
-
- File attachments listed below the body with id + name + size, ready for follow-up calls
|
|
40
|
-
- Pair with `extract-sharepoint-links-in-mail` to resolve every SharePoint URL in the body to its driveItem in parallel (capped at 25 unique URLs per call)
|
|
37
|
+
### 🔑 Sign in like a human, not like an app
|
|
41
38
|
|
|
42
|
-
|
|
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 with zero side effects.
|
|
43
40
|
|
|
44
|
-
|
|
41
|
+
### 🛡️ Safe to hand to an autonomous agent
|
|
45
42
|
|
|
46
|
-
|
|
47
|
-
- `download-drive-item-as-pdf` — Graph PDF conversion for anything it supports (preserves slide layout, images, charts — the right call for pptx and image-heavy docs)
|
|
48
|
-
- `convert-mail-attachment-to-markdown` / `convert-mail-attachment-to-pdf` — same pipelines but starting from an email attachment
|
|
49
|
-
- `read-mail-attachment` — one call that reads any mail attachment, auto-routing by file type (a `.zip` is unpacked and every entry converted; docx/xlsx/pptx/odf/csv/PDF/`.msg`/legacy/text → markdown; images, scanned PDFs, and legacy `.ppt` return an actionable 415 pointing at the raw-bytes / vision route) so an agent never has to choose between the `convert-mail-attachment-*` siblings
|
|
50
|
-
- `convert-local-file` — same pipelines but starting from a file **on disk** (`--path ./report.docx`); never calls Graph (works offline, no login). A `.zip` is unpacked with every contained file converted in one call. The two things it can't do locally — convert **to** PDF and Loop/Fluid/Whiteboard sources — need Graph's server-side renderer (upload to OneDrive and use the drive-item siblings)
|
|
51
|
-
- `convert-drive-item-zip` / `convert-mail-attachment-zip` — unzip an archive (OneDrive/SharePoint item, or an Outlook attachment) and convert **every** contained file in one call; legacy GBK / CP437 entry names (Chinese vendor archives from WinRAR / Windows Explorer) are decoded correctly, never mojibaked; unsupported entries are listed with a note instead of failing the archive
|
|
52
|
-
- **Outlook `.msg` files** (saved/forwarded emails) convert to markdown through every entry point above — H1 subject, From/To/Cc/Date header block, the body, and an `## Attachments` section where each attachment is itself converted recursively (depth-capped)
|
|
53
|
-
- `extract-sharepoint-links-in-documents` — the doc-side sibling of `extract-sharepoint-links-in-mail`: resolve every `*.sharepoint.com` URL embedded in a docx/xlsx/pptx (read from the package's relationship parts) or an odt/ods/odp (read from the inline `xlink:href` links in content.xml) to its driveItem, so an agent can follow references out of a document the same way it follows them out of an email
|
|
43
|
+
The 184 commands break down as 176 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.
|
|
54
44
|
|
|
55
|
-
|
|
45
|
+
### 🧠 Responses budgeted for a context window
|
|
56
46
|
|
|
57
|
-
|
|
47
|
+
Graph payloads are tuned for backend services. These are tuned for models. Listings return hand-picked fields instead of every property on every item (opt out with `--full true`). Email threads arrive with quoted reply chains stripped, which shrinks real threads by 78-90%. Inline images become `[inline image: logo.png]` placeholders instead of kilobytes of base64. Binaries over ~1 MB are refused inline and routed to disk via `--output-path`. And every failure returns `{error, hint, source, retryAfterSeconds}` so the model can repair its own call instead of retrying blind.
|
|
58
48
|
|
|
59
|
-
|
|
49
|
+
## See it work
|
|
60
50
|
|
|
61
|
-
|
|
51
|
+
Reading one email with its attachments in raw Graph: GET the message, GET the attachment list, GET each attachment's bytes, resolve every SharePoint link in the body, then run your own HTML-to-text pipeline. Six round trips minimum. Here:
|
|
62
52
|
|
|
63
|
-
|
|
53
|
+
```console
|
|
54
|
+
$ ask-marcel-office convert-mail-to-markdown --message-id "AAMkAD..."
|
|
64
55
|
|
|
65
|
-
|
|
56
|
+
# Q3 budget review: action needed
|
|
57
|
+
**From:** Robin Chen <robin.chen@contoso.com>
|
|
66
58
|
|
|
67
|
-
|
|
59
|
+
Before Friday's review, the forecast tab still shows last quarter's
|
|
60
|
+
headcount. Can you sanity-check the attached numbers?
|
|
68
61
|
|
|
69
|
-
|
|
62
|
+
## Attachments
|
|
63
|
+
- Q3-forecast.xlsx (48 KB) · id AAMkAD...
|
|
64
|
+
```
|
|
70
65
|
|
|
71
|
-
|
|
66
|
+
The quoted chain below the reply is replaced by a one-line marker (restore it with `--keep-quoted true`), inline images become named placeholders, and attachments arrive with the ids ready for the follow-up call:
|
|
72
67
|
|
|
73
|
-
```
|
|
74
|
-
ask-marcel-office
|
|
68
|
+
```console
|
|
69
|
+
$ ask-marcel-office convert-mail-attachment-to-markdown \
|
|
70
|
+
--message-id "AAMkAD..." --attachment-id "AAMkAD..."
|
|
75
71
|
```
|
|
76
72
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
Every failure — Graph, CLI parser, Zod validation, substrate — comes back as `{ok: false, error, errorCode?, hint?, source, retryAfterSeconds?}`. The `hint` field tells the model *what to do next* (e.g. "string literals MUST use single quotes; embed one by doubling it") and `source` tells it where the failure came from. Curated rules for 20+ recurring Graph errors plus cross-resolver pointers (passed a Teams URL to `resolve-mail-link`? Hint says "re-run with `resolve-teams-link`"). When Graph throttles (HTTP 429, sometimes 503) the response's `Retry-After` is surfaced as `retryAfterSeconds` — the integer seconds to wait — so a caller running tenant-scale crawls can honor the server's backoff instead of guessing.
|
|
73
|
+
That xlsx comes back as one markdown table per sheet. And when the answer lives in a deck someone shared three weeks ago:
|
|
80
74
|
|
|
81
|
-
|
|
75
|
+
```console
|
|
76
|
+
$ ask-marcel-office microsoft-search-query --query "Q3 budget filetype:pptx"
|
|
77
|
+
$ ask-marcel-office download-drive-item-as-markdown --drive-id "b!abc..." --item-id "01BYE..."
|
|
78
|
+
```
|
|
82
79
|
|
|
83
|
-
|
|
80
|
+
## The difference in practice
|
|
84
81
|
|
|
85
|
-
|
|
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
|
+
## What your agent can reach
|
|
92
|
+
|
|
93
|
+
| Surface | Commands | In practice |
|
|
94
|
+
|---|---:|---|
|
|
95
|
+
| 📧 Outlook Mail | 36 | 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
|
+
| 📁 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
|
+
| 📅 Calendar | 24 | "What's on this week" via relative dates (`today`, `start-of-week`, `+7d`), event details, free/busy lookups |
|
|
98
|
+
| 👥 People + directory | 16 | People search, user profiles, the directory around you, your own identity and IDs in one round trip |
|
|
99
|
+
| 💬 Teams | 16 | Your teams, channels, chats, and message history |
|
|
100
|
+
| ✅ Planner + To Do | 15 | Plans, buckets, tasks, checklists, due dates |
|
|
101
|
+
| 📊 Excel | 11 | Live workbook reads: worksheets, used ranges, tables (lean values, not Graph's four redundant 2D arrays) |
|
|
102
|
+
| 📓 OneNote | 11 | Notebooks, sections, page content |
|
|
103
|
+
| 🔎 Search + utilities | 6 | Federated Microsoft Search across the tenant, cursor pagination (`next-page`), token status (`scopes-check`), offline local-file conversion |
|
|
104
|
+
|
|
105
|
+
Full per-command tables with required parameters and Graph endpoints: **[docs/COMMANDS.md](docs/COMMANDS.md)**.
|
|
106
|
+
|
|
107
|
+
## Any file becomes markdown
|
|
108
|
+
|
|
109
|
+
One conversion pipeline, four entry points: a OneDrive / SharePoint item, an Outlook attachment, a `.zip` archive, or a file on disk (`convert-local-file-to-markdown` runs fully offline, no login).
|
|
110
|
+
|
|
111
|
+
| Source | What comes back |
|
|
112
|
+
|---|---|
|
|
113
|
+
| docx / docm / dotx | Clean markdown; `--include-metadata true` adds comments, tracked changes, hidden text, and a VBA-macro flag |
|
|
114
|
+
| xlsx / xlsm / csv | One markdown table per sheet, with a 50 000-cell guard so a huge sheet becomes a band-by-band read plan instead of an OOM |
|
|
115
|
+
| pptx / pptm | Per-slide text with speaker notes (`## Slide N`); switch to the PDF sibling + a vision model when layout matters |
|
|
116
|
+
| pdf | Text-layer extraction; a scanned PDF answers with a pointer to the vision route instead of silence |
|
|
117
|
+
| odt / ods / odp | Headings, lists, tables, per-slide text, comments folded inline |
|
|
118
|
+
| Legacy .doc / .xls | `.xls` reads like `.xlsx`; `.doc` extracts as plain text (`.ppt`: convert to PDF first) |
|
|
119
|
+
| Outlook .msg | The full email: headers, quote-stripped body, and every attachment converted recursively |
|
|
120
|
+
| .zip | Every entry converted in one call; GBK / CP437 entry names decoded, never mojibake |
|
|
121
|
+
| Loop / Whiteboard | Rendered through Graph's server-side converter |
|
|
122
|
+
|
|
123
|
+
Need the pictures instead of the words? `extract-drive-item-images`, `extract-mail-attachment-images`, and `extract-local-file-images` pull the embedded images out of docx / xlsx / pptx / pdf (including full-resolution originals and images on hidden slides), ready for a vision model; `--output-dir` writes them straight to disk.
|
|
124
|
+
|
|
125
|
+
## Designed for the agent loop
|
|
126
|
+
|
|
127
|
+
- **Self-teaching.** `help-json --terse` returns a slim JSON manifest built for a model's first contact; add `--category mail` to scope it. `docs <command>` prints one command's full documentation (response shape, examples, the underlying Graph endpoint). Scan, pick, read, call.
|
|
128
|
+
- **Errors that repair the call.** Every failure is `{ok: false, error, errorCode?, hint?, source, retryAfterSeconds?}`, with curated hints for 20+ recurring Graph mistakes, including wrong-resolver pointers (a Teams URL passed to `resolve-mail-link` answers with the command to use instead).
|
|
129
|
+
- **Pagination without state.** Every listing returns an opaque cursor; `next-page --url "<cursor>"` continues any of them.
|
|
130
|
+
- **Relative dates.** `--start-date-time start-of-week --end-date-time +7d`. No timestamp math before "what's on my calendar".
|
|
131
|
+
- **Binary discipline.** Multi-MB payloads never hit stdout by accident: without `--output-path`, a binary answer is a one-line summary; with it, the bytes land on disk and the envelope carries `savedTo`.
|
|
132
|
+
- **Tenant-wide reach.** `list-accessible-drives` unions every discovery vector a delegated token can hit (Teams libraries, group sites, private channels, shared-with-me, activity signals, secondary site libraries) and `search-all-accessible-sites` deep-pages the search index. Together: the practical maximum reachable without tenant-admin rights.
|
|
133
|
+
|
|
134
|
+
## Install in 60 seconds
|
|
86
135
|
|
|
87
|
-
|
|
136
|
+
```bash
|
|
137
|
+
# Bun >=1.0 or Node >=20 · macOS, Windows, Linux
|
|
138
|
+
npm i -g ask-marcel-office-cli
|
|
88
139
|
|
|
89
|
-
|
|
140
|
+
ask-marcel-office login # browser opens once, tokens cached
|
|
141
|
+
ask-marcel-office my-quick-context # who am I + my IDs, one round trip
|
|
142
|
+
ask-marcel-office list-calendar-view --start-date-time today --end-date-time +7d
|
|
143
|
+
ask-marcel-office search-onedrive-files --drive-id "b!abc..." --query "Q3 budget"
|
|
144
|
+
```
|
|
90
145
|
|
|
91
|
-
|
|
92
|
-
- **No flag, text mode** — binary commands print a one-line summary (`binary: application/pdf, 4837291 bytes — use --output-path to save`) instead of spilling base64 to stdout. The LLM sees a hint without ever pulling the bytes.
|
|
146
|
+
## Plug it into Claude Code, Cursor, Cline, or your own stack
|
|
93
147
|
|
|
94
|
-
|
|
148
|
+
Any agent that can run a shell command can use the whole surface today: point it at `help-json --terse`, let it call commands with `--output json`, and the lean defaults plus structured hints let it recover from its own mistakes. No wrapper required.
|
|
95
149
|
|
|
96
|
-
|
|
150
|
+
**No shell? Register it as an MCP server.** It speaks MCP over stdio, so any MCP client can drive it — straight off the npm registry via `npx`, nothing to clone or preinstall.
|
|
97
151
|
|
|
98
|
-
|
|
152
|
+
**Claude Code** — one command:
|
|
99
153
|
|
|
100
154
|
```bash
|
|
101
|
-
|
|
102
|
-
npm i -g ask-marcel-office-cli
|
|
103
|
-
|
|
104
|
-
# authenticate (cached → refresh → browser fallback)
|
|
105
|
-
ask-marcel-office login
|
|
106
|
-
|
|
107
|
-
# the rest is read-only (the only writes are mail drafts) and discoverable from --help
|
|
108
|
-
ask-marcel-office list-drives
|
|
109
|
-
ask-marcel-office search-onedrive-files --drive-id "b!abc..." --query "Q3 budget"
|
|
110
|
-
ask-marcel-office convert-mail-to-markdown --message-id "AAMkAD..."
|
|
111
|
-
ask-marcel-office list-calendar-view --start-date-time today --end-date-time +7d
|
|
112
|
-
ask-marcel-office convert-mail-attachment-to-pdf \
|
|
113
|
-
--message-id "AAMkAD..." --attachment-id "AAMkAD...attach1" \
|
|
114
|
-
--output-path /tmp/deck.pdf
|
|
155
|
+
claude mcp add --transport stdio --scope user ask-marcel-office -- npx -y ask-marcel-office-cli mcp
|
|
115
156
|
```
|
|
116
157
|
|
|
117
|
-
|
|
158
|
+
**Claude Desktop, Cursor, or any other MCP client** — they all take the same `mcpServers` block; only the file location differs:
|
|
159
|
+
|
|
160
|
+
| Client | Config file |
|
|
161
|
+
|:--|:--|
|
|
162
|
+
| Claude Desktop (macOS) | `~/Library/Application Support/Claude/claude_desktop_config.json` |
|
|
163
|
+
| Claude Desktop (Windows) | `%AppData%\Claude\claude_desktop_config.json` |
|
|
164
|
+
| Cursor, Cline, Windsurf, … | see your client's MCP docs — the block below is unchanged |
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"mcpServers": {
|
|
169
|
+
"ask-marcel-office": {
|
|
170
|
+
"description": "Ask Marcel Office CLI MCP server",
|
|
171
|
+
"command": "npx",
|
|
172
|
+
"args": ["-y", "ask-marcel-office-cli", "mcp"],
|
|
173
|
+
"env": {}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
118
178
|
|
|
119
|
-
|
|
179
|
+
> **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`.
|
|
120
180
|
|
|
121
|
-
|
|
122
|
-
|---|---|---|
|
|
123
|
-
| Help with a single command | `ask-marcel-office <command> --help` | Required flags, optional flags, an example, pagination notes |
|
|
124
|
-
| A scan of every command | `ask-marcel-office --help` | One-sentence summary per command, grouped by category |
|
|
125
|
-
| The slim LLM-friendly index | `ask-marcel-office help-json --terse` | JSON manifest with heavy fields (options, response shape) stripped — best first-call for an agent meeting the CLI for the first time |
|
|
126
|
-
| The slim index for one domain | `ask-marcel-office help-json --terse --category mail` | Same as above, filtered to one of 12 categories — keeps the response tiny when the agent already knows the domain |
|
|
127
|
-
| Rich docs for one command | `ask-marcel-office docs <command>` | Full Markdown to stdout (response shape, examples, the underlying Graph endpoint, Microsoft Learn link) |
|
|
181
|
+
You get **five gateway tools**, not one per command — 184 tool schemas would bloat every session, the opposite of the point:
|
|
128
182
|
|
|
129
|
-
|
|
183
|
+
| Tool | Does |
|
|
184
|
+
|:--|:--|
|
|
185
|
+
| `list-commands` | The terse manifest. Start here; `category` narrows it. |
|
|
186
|
+
| `get-command-docs` | Full docs for one command: options, endpoint, example. |
|
|
187
|
+
| `run-command` | The 180 **read** commands. `readOnlyHint: true`, so clients can auto-approve it. |
|
|
188
|
+
| `run-write-command` | The 4 mail-draft **write** commands. Separate tool so the read tool's promise stays honest. |
|
|
189
|
+
| `login` | Sign in / refresh. Opens a browser on this machine. |
|
|
130
190
|
|
|
131
|
-
|
|
191
|
+
**Sign in from a terminal first** — `npx -y ask-marcel-office-cli login`. Do this once before wiring up any client, and the reads just work.
|
|
132
192
|
|
|
133
|
-
|
|
193
|
+
## Embed it as a TypeScript library
|
|
134
194
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
**2. Embed as a library** — every command is exported. Compose it inside your own MCP server, Claude Agent, or LangChain tool:
|
|
195
|
+
Every command is exported, typed, and returns `Result<T, E>` (no thrown surprises). Compose them inside your own agent or LangChain tool:
|
|
138
196
|
|
|
139
197
|
```ts
|
|
140
198
|
import { commands, buildDeps } from 'ask-marcel-office-cli';
|
|
@@ -142,57 +200,59 @@ import { commands, buildDeps } from 'ask-marcel-office-cli';
|
|
|
142
200
|
const { graph } = buildDeps();
|
|
143
201
|
const result = await commands['list-mail-messages'].execute(graph, { top: '10' });
|
|
144
202
|
if (result.ok) {
|
|
145
|
-
// result.value is the Graph payload
|
|
203
|
+
// result.value is the Graph payload
|
|
146
204
|
}
|
|
147
205
|
```
|
|
148
206
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
**Most users — use the built-in browser-OAuth ladder:**
|
|
152
|
-
|
|
153
|
-
```ts
|
|
154
|
-
import { buildDeps } from 'ask-marcel-office-cli';
|
|
155
|
-
|
|
156
|
-
const { graph } = buildDeps();
|
|
157
|
-
// First call triggers cache → refresh → headed-Chromium fallback automatically.
|
|
158
|
-
// Tokens cached at ~/.ask-marcel/token-cache.json for subsequent calls.
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
**Agents / CI / MCP servers — bring your own token:**
|
|
207
|
+
**Bring your own token** (CI, MCP servers, vault-managed environments): swap the built-in browser auth for any token source via `createGraphClient`.
|
|
162
208
|
|
|
163
209
|
```ts
|
|
164
210
|
import { createGraphClient } from 'ask-marcel-office-cli';
|
|
165
211
|
|
|
212
|
+
const cancelled = async () => ({ ok: false as const, error: { type: 'auth_cancelled' as const } });
|
|
213
|
+
|
|
166
214
|
const graph = createGraphClient({
|
|
167
|
-
getAccessToken: async () => ({
|
|
168
|
-
ok: true,
|
|
169
|
-
value: await fetchTokenFromYourVault(),
|
|
170
|
-
}),
|
|
215
|
+
getAccessToken: async () => ({ ok: true, value: await fetchTokenFromYourVault() }),
|
|
171
216
|
logout: async () => ({ ok: true, value: undefined }),
|
|
217
|
+
// Decline the token tiers your source cannot mint; only the commands
|
|
218
|
+
// that need them will notice, and they fail with a clear message.
|
|
219
|
+
getElevatedAccessToken: cancelled, // ODSP-gated: historical-version downloads
|
|
220
|
+
getGuestAccessToken: cancelled, // partner tenants you are a guest in
|
|
221
|
+
getChatsvcaggAccessToken: cancelled, // Teams chat substrate
|
|
222
|
+
getIc3AccessToken: cancelled, // Teams chat history
|
|
223
|
+
getChatsvcaggRegion: async () => 'emea',
|
|
224
|
+
getLastElevatedOutcome: () => null,
|
|
225
|
+
getLastChatsvcaggOutcome: () => null,
|
|
172
226
|
});
|
|
173
227
|
```
|
|
174
228
|
|
|
175
|
-
|
|
229
|
+
Azure Managed Identity, an on-behalf-of flow, hand-pasted JWTs in tests: the Graph client doesn't care where the token came from. Full library guide: **[docs/USAGE.md](docs/USAGE.md)**.
|
|
176
230
|
|
|
177
231
|
## Deep docs
|
|
178
232
|
|
|
179
|
-
- **[All
|
|
180
|
-
- **[Usage guide](docs/USAGE.md)
|
|
181
|
-
- **[Machine-readable manifest](docs/commands.json)
|
|
182
|
-
- **[QA playbook](docs/QA-PLAYBOOK.md)
|
|
233
|
+
- **[All 184 commands](docs/COMMANDS.md)**: per-category tables with required params + Graph endpoint
|
|
234
|
+
- **[Usage guide](docs/USAGE.md)**: output formats, OData passthrough, `--output-path`, pagination, library API, architecture, configuration
|
|
235
|
+
- **[Machine-readable manifest](docs/commands.json)**: JSON for programmatic discovery, also importable via `import manifest from 'ask-marcel-office-cli/commands.json'`
|
|
236
|
+
- **[QA playbook](docs/QA-PLAYBOOK.md)**: the repeatable full-surface health check run before each release
|
|
183
237
|
|
|
184
238
|
## Roadmap
|
|
185
239
|
|
|
186
|
-
Read-only stays the default forever.
|
|
187
|
-
|
|
188
|
-
Suggestions, requests, and pull requests welcome — see the [issues page](https://github.com/vdelacou/ask-marcel-office-cli/issues).
|
|
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/vdelacou/ask-marcel-office-cli/issues).
|
|
189
241
|
|
|
190
242
|
## Built with
|
|
191
243
|
|
|
192
|
-
- **Bun + TypeScript
|
|
193
|
-
- **Microsoft Graph v1.0
|
|
194
|
-
- **Playwright
|
|
244
|
+
- **Bun + TypeScript**: single-package install, Node >=20 fallback, `Result<T, E>` at every IO boundary, a 100% coverage gate on every tier
|
|
245
|
+
- **Microsoft Graph v1.0**: the public API surface, no beta endpoints in production code
|
|
246
|
+
- **Playwright**: the headed browser behind the one-time sign-in
|
|
195
247
|
|
|
196
248
|
## License
|
|
197
249
|
|
|
198
250
|
MIT © Vincent Delacourt
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
<div align="center">
|
|
255
|
+
|
|
256
|
+
**If this saved your agent a few round trips, a ⭐ helps other agents' humans find it.**
|
|
257
|
+
|
|
258
|
+
</div>
|