@jbctechsolutions/mcp-office365 2.5.1 → 4.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 +66 -57
- package/dist/approval/hash.d.ts +8 -8
- package/dist/approval/token-manager.d.ts +37 -19
- package/dist/approval/token-manager.d.ts.map +1 -1
- package/dist/approval/token-manager.js +133 -37
- package/dist/approval/token-manager.js.map +1 -1
- package/dist/approval/types.d.ts +7 -3
- package/dist/approval/types.d.ts.map +1 -1
- package/dist/cli.d.ts +21 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +100 -2
- package/dist/cli.js.map +1 -1
- package/dist/database/repository.d.ts +48 -48
- package/dist/database/repository.d.ts.map +1 -1
- package/dist/database/repository.js.map +1 -1
- package/dist/delta/mirror.d.ts +67 -0
- package/dist/delta/mirror.d.ts.map +1 -0
- package/dist/delta/mirror.js +183 -0
- package/dist/delta/mirror.js.map +1 -0
- package/dist/graph/auth/account-id.d.ts +19 -0
- package/dist/graph/auth/account-id.d.ts.map +1 -0
- package/dist/graph/auth/account-id.js +55 -0
- package/dist/graph/auth/account-id.js.map +1 -0
- package/dist/graph/auth/config.d.ts +1 -1
- package/dist/graph/auth/config.d.ts.map +1 -1
- package/dist/graph/auth/config.js +13 -1
- package/dist/graph/auth/config.js.map +1 -1
- package/dist/graph/auth/device-code-flow.d.ts +3 -1
- package/dist/graph/auth/device-code-flow.d.ts.map +1 -1
- package/dist/graph/auth/device-code-flow.js +42 -14
- package/dist/graph/auth/device-code-flow.js.map +1 -1
- package/dist/graph/auth/index.d.ts +1 -0
- package/dist/graph/auth/index.d.ts.map +1 -1
- package/dist/graph/auth/index.js +1 -0
- package/dist/graph/auth/index.js.map +1 -1
- package/dist/graph/auth/token-cache.js +2 -2
- package/dist/graph/auth/token-cache.js.map +1 -1
- package/dist/graph/client/graph-client.d.ts +141 -8
- package/dist/graph/client/graph-client.d.ts.map +1 -1
- package/dist/graph/client/graph-client.js +536 -48
- package/dist/graph/client/graph-client.js.map +1 -1
- package/dist/graph/client/immutable-id-middleware.d.ts +50 -0
- package/dist/graph/client/immutable-id-middleware.d.ts.map +1 -0
- package/dist/graph/client/immutable-id-middleware.js +137 -0
- package/dist/graph/client/immutable-id-middleware.js.map +1 -0
- package/dist/graph/content-readers.d.ts +0 -12
- package/dist/graph/content-readers.d.ts.map +1 -1
- package/dist/graph/content-readers.js +0 -21
- package/dist/graph/content-readers.js.map +1 -1
- package/dist/graph/index.d.ts +5 -6
- package/dist/graph/index.d.ts.map +1 -1
- package/dist/graph/index.js +5 -6
- package/dist/graph/index.js.map +1 -1
- package/dist/graph/mailbox-adapter.d.ts +15 -15
- package/dist/graph/mappers/contact-mapper.d.ts +4 -1
- package/dist/graph/mappers/contact-mapper.d.ts.map +1 -1
- package/dist/graph/mappers/contact-mapper.js +7 -3
- package/dist/graph/mappers/contact-mapper.js.map +1 -1
- package/dist/graph/mappers/email-mapper.d.ts.map +1 -1
- package/dist/graph/mappers/email-mapper.js +7 -4
- package/dist/graph/mappers/email-mapper.js.map +1 -1
- package/dist/graph/mappers/event-mapper.d.ts.map +1 -1
- package/dist/graph/mappers/event-mapper.js +6 -3
- package/dist/graph/mappers/event-mapper.js.map +1 -1
- package/dist/graph/mappers/folder-mapper.d.ts +0 -4
- package/dist/graph/mappers/folder-mapper.d.ts.map +1 -1
- package/dist/graph/mappers/folder-mapper.js +9 -19
- package/dist/graph/mappers/folder-mapper.js.map +1 -1
- package/dist/graph/mappers/index.d.ts +2 -2
- package/dist/graph/mappers/index.d.ts.map +1 -1
- package/dist/graph/mappers/index.js +2 -2
- package/dist/graph/mappers/index.js.map +1 -1
- package/dist/graph/mappers/task-mapper.d.ts +4 -2
- package/dist/graph/mappers/task-mapper.d.ts.map +1 -1
- package/dist/graph/mappers/task-mapper.js +7 -5
- package/dist/graph/mappers/task-mapper.js.map +1 -1
- package/dist/graph/mappers/utils.d.ts +0 -10
- package/dist/graph/mappers/utils.d.ts.map +1 -1
- package/dist/graph/mappers/utils.js +0 -19
- package/dist/graph/mappers/utils.js.map +1 -1
- package/dist/graph/repository.d.ts +409 -276
- package/dist/graph/repository.d.ts.map +1 -1
- package/dist/graph/repository.js +772 -1082
- package/dist/graph/repository.js.map +1 -1
- package/dist/ids/mint.d.ts +31 -0
- package/dist/ids/mint.d.ts.map +1 -0
- package/dist/ids/mint.js +36 -0
- package/dist/ids/mint.js.map +1 -0
- package/dist/ids/next-action.d.ts +29 -0
- package/dist/ids/next-action.d.ts.map +1 -0
- package/dist/ids/next-action.js +58 -0
- package/dist/ids/next-action.js.map +1 -0
- package/dist/ids/resolver.d.ts +31 -0
- package/dist/ids/resolver.d.ts.map +1 -0
- package/dist/ids/resolver.js +52 -0
- package/dist/ids/resolver.js.map +1 -0
- package/dist/ids/schema.d.ts +91 -0
- package/dist/ids/schema.d.ts.map +1 -0
- package/dist/ids/schema.js +131 -0
- package/dist/ids/schema.js.map +1 -0
- package/dist/ids/token.d.ts +62 -0
- package/dist/ids/token.d.ts.map +1 -0
- package/dist/ids/token.js +225 -0
- package/dist/ids/token.js.map +1 -0
- package/dist/index.d.ts +10 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +255 -5925
- package/dist/index.js.map +1 -1
- package/dist/parsers/index.d.ts +0 -1
- package/dist/parsers/index.d.ts.map +1 -1
- package/dist/parsers/index.js +0 -1
- package/dist/parsers/index.js.map +1 -1
- package/dist/registry/all-tools.d.ts +14 -0
- package/dist/registry/all-tools.d.ts.map +1 -0
- package/dist/registry/all-tools.js +68 -0
- package/dist/registry/all-tools.js.map +1 -0
- package/dist/registry/context.d.ts +19 -0
- package/dist/registry/context.d.ts.map +1 -0
- package/dist/registry/context.js +20 -0
- package/dist/registry/context.js.map +1 -0
- package/dist/registry/define-tool.d.ts +15 -0
- package/dist/registry/define-tool.d.ts.map +1 -0
- package/dist/registry/define-tool.js +8 -0
- package/dist/registry/define-tool.js.map +1 -0
- package/dist/registry/elicit-links.d.ts +35 -0
- package/dist/registry/elicit-links.d.ts.map +1 -0
- package/dist/registry/elicit-links.js +102 -0
- package/dist/registry/elicit-links.js.map +1 -0
- package/dist/registry/elicitor.d.ts +24 -0
- package/dist/registry/elicitor.d.ts.map +1 -0
- package/dist/registry/elicitor.js +51 -0
- package/dist/registry/elicitor.js.map +1 -0
- package/dist/registry/index.d.ts +13 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +8 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/registry.d.ts +73 -0
- package/dist/registry/registry.d.ts.map +1 -0
- package/dist/registry/registry.js +221 -0
- package/dist/registry/registry.js.map +1 -0
- package/dist/registry/types.d.ts +146 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +6 -0
- package/dist/registry/types.js.map +1 -0
- package/dist/search/compiler.d.ts +49 -0
- package/dist/search/compiler.d.ts.map +1 -0
- package/dist/search/compiler.js +168 -0
- package/dist/search/compiler.js.map +1 -0
- package/dist/signature.js +2 -2
- package/dist/signature.js.map +1 -1
- package/dist/state/delta-store.d.ts +85 -0
- package/dist/state/delta-store.d.ts.map +1 -0
- package/dist/state/delta-store.js +124 -0
- package/dist/state/delta-store.js.map +1 -0
- package/dist/state/migrate.d.ts +23 -0
- package/dist/state/migrate.d.ts.map +1 -0
- package/dist/state/migrate.js +81 -0
- package/dist/state/migrate.js.map +1 -0
- package/dist/state/schema.d.ts +26 -0
- package/dist/state/schema.d.ts.map +1 -0
- package/dist/state/schema.js +77 -0
- package/dist/state/schema.js.map +1 -0
- package/dist/state/store.d.ts +155 -0
- package/dist/state/store.d.ts.map +1 -0
- package/dist/state/store.js +321 -0
- package/dist/state/store.js.map +1 -0
- package/dist/tools/calendar-graph.d.ts +41 -0
- package/dist/tools/calendar-graph.d.ts.map +1 -0
- package/dist/tools/calendar-graph.js +299 -0
- package/dist/tools/calendar-graph.js.map +1 -0
- package/dist/tools/calendar-permissions.d.ts +17 -7
- package/dist/tools/calendar-permissions.d.ts.map +1 -1
- package/dist/tools/calendar-permissions.js +60 -4
- package/dist/tools/calendar-permissions.js.map +1 -1
- package/dist/tools/calendar.d.ts +133 -82
- package/dist/tools/calendar.d.ts.map +1 -1
- package/dist/tools/calendar.js +249 -136
- package/dist/tools/calendar.js.map +1 -1
- package/dist/tools/categories.d.ts +14 -4
- package/dist/tools/categories.d.ts.map +1 -1
- package/dist/tools/categories.js +59 -2
- package/dist/tools/categories.js.map +1 -1
- package/dist/tools/checklist-items.d.ts +19 -9
- package/dist/tools/checklist-items.d.ts.map +1 -1
- package/dist/tools/checklist-items.js +71 -5
- package/dist/tools/checklist-items.js.map +1 -1
- package/dist/tools/contact-folders.d.ts +50 -0
- package/dist/tools/contact-folders.d.ts.map +1 -0
- package/dist/tools/contact-folders.js +136 -0
- package/dist/tools/contact-folders.js.map +1 -0
- package/dist/tools/contacts-graph.d.ts +45 -0
- package/dist/tools/contacts-graph.d.ts.map +1 -0
- package/dist/tools/contacts-graph.js +174 -0
- package/dist/tools/contacts-graph.js.map +1 -0
- package/dist/tools/contacts.d.ts +61 -30
- package/dist/tools/contacts.d.ts.map +1 -1
- package/dist/tools/contacts.js +150 -93
- package/dist/tools/contacts.js.map +1 -1
- package/dist/tools/excel.d.ts +20 -10
- package/dist/tools/excel.d.ts.map +1 -1
- package/dist/tools/excel.js +82 -6
- package/dist/tools/excel.js.map +1 -1
- package/dist/tools/focused-overrides.d.ts +14 -4
- package/dist/tools/focused-overrides.d.ts.map +1 -1
- package/dist/tools/focused-overrides.js +59 -2
- package/dist/tools/focused-overrides.js.map +1 -1
- package/dist/tools/index.d.ts +4 -5
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -6
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/linked-resources.d.ts +17 -7
- package/dist/tools/linked-resources.d.ts.map +1 -1
- package/dist/tools/linked-resources.js +60 -4
- package/dist/tools/linked-resources.js.map +1 -1
- package/dist/tools/mail-graph.d.ts +38 -0
- package/dist/tools/mail-graph.d.ts.map +1 -0
- package/dist/tools/mail-graph.js +187 -0
- package/dist/tools/mail-graph.js.map +1 -0
- package/dist/tools/mail-rules.d.ts +20 -8
- package/dist/tools/mail-rules.d.ts.map +1 -1
- package/dist/tools/mail-rules.js +63 -7
- package/dist/tools/mail-rules.js.map +1 -1
- package/dist/tools/mail-send.d.ts +39 -27
- package/dist/tools/mail-send.d.ts.map +1 -1
- package/dist/tools/mail-send.js +249 -24
- package/dist/tools/mail-send.js.map +1 -1
- package/dist/tools/mail.d.ts +91 -68
- package/dist/tools/mail.d.ts.map +1 -1
- package/dist/tools/mail.js +232 -243
- package/dist/tools/mail.js.map +1 -1
- package/dist/tools/mailbox-organization.d.ts +51 -34
- package/dist/tools/mailbox-organization.d.ts.map +1 -1
- package/dist/tools/mailbox-organization.js +318 -35
- package/dist/tools/mailbox-organization.js.map +1 -1
- package/dist/tools/mailbox-settings.d.ts +59 -0
- package/dist/tools/mailbox-settings.d.ts.map +1 -0
- package/dist/tools/mailbox-settings.js +136 -0
- package/dist/tools/mailbox-settings.js.map +1 -0
- package/dist/tools/meetings.d.ts +24 -14
- package/dist/tools/meetings.d.ts.map +1 -1
- package/dist/tools/meetings.js +82 -6
- package/dist/tools/meetings.js.map +1 -1
- package/dist/tools/onedrive.d.ts +26 -16
- package/dist/tools/onedrive.d.ts.map +1 -1
- package/dist/tools/onedrive.js +140 -11
- package/dist/tools/onedrive.js.map +1 -1
- package/dist/tools/onenote.d.ts +63 -0
- package/dist/tools/onenote.d.ts.map +1 -0
- package/dist/tools/onenote.js +221 -0
- package/dist/tools/onenote.js.map +1 -0
- package/dist/tools/people.d.ts +10 -0
- package/dist/tools/people.d.ts.map +1 -1
- package/dist/tools/people.js +94 -0
- package/dist/tools/people.js.map +1 -1
- package/dist/tools/planner-visualization.d.ts +15 -5
- package/dist/tools/planner-visualization.d.ts.map +1 -1
- package/dist/tools/planner-visualization.js +58 -4
- package/dist/tools/planner-visualization.js.map +1 -1
- package/dist/tools/planner.d.ts +69 -39
- package/dist/tools/planner.d.ts.map +1 -1
- package/dist/tools/planner.js +231 -23
- package/dist/tools/planner.js.map +1 -1
- package/dist/tools/scheduling.d.ts +12 -0
- package/dist/tools/scheduling.d.ts.map +1 -1
- package/dist/tools/scheduling.js +38 -0
- package/dist/tools/scheduling.js.map +1 -1
- package/dist/tools/shared-mailbox.d.ts +164 -0
- package/dist/tools/shared-mailbox.d.ts.map +1 -0
- package/dist/tools/shared-mailbox.js +318 -0
- package/dist/tools/shared-mailbox.js.map +1 -0
- package/dist/tools/sharepoint-lists.d.ts +131 -0
- package/dist/tools/sharepoint-lists.d.ts.map +1 -0
- package/dist/tools/sharepoint-lists.js +246 -0
- package/dist/tools/sharepoint-lists.js.map +1 -0
- package/dist/tools/sharepoint.d.ts +24 -14
- package/dist/tools/sharepoint.d.ts.map +1 -1
- package/dist/tools/sharepoint.js +85 -9
- package/dist/tools/sharepoint.js.map +1 -1
- package/dist/tools/task-attachments.d.ts +17 -7
- package/dist/tools/task-attachments.d.ts.map +1 -1
- package/dist/tools/task-attachments.js +60 -4
- package/dist/tools/task-attachments.js.map +1 -1
- package/dist/tools/task-lists.d.ts +56 -0
- package/dist/tools/task-lists.d.ts.map +1 -0
- package/dist/tools/task-lists.js +155 -0
- package/dist/tools/task-lists.js.map +1 -0
- package/dist/tools/tasks-graph.d.ts +49 -0
- package/dist/tools/tasks-graph.d.ts.map +1 -0
- package/dist/tools/tasks-graph.js +193 -0
- package/dist/tools/tasks-graph.js.map +1 -0
- package/dist/tools/tasks.d.ts +121 -29
- package/dist/tools/tasks.d.ts.map +1 -1
- package/dist/tools/tasks.js +136 -84
- package/dist/tools/tasks.js.map +1 -1
- package/dist/tools/teams.d.ts +57 -46
- package/dist/tools/teams.d.ts.map +1 -1
- package/dist/tools/teams.js +288 -26
- package/dist/tools/teams.js.map +1 -1
- package/dist/tools/what-changed.d.ts +48 -0
- package/dist/tools/what-changed.d.ts.map +1 -0
- package/dist/tools/what-changed.js +149 -0
- package/dist/tools/what-changed.js.map +1 -0
- package/dist/types/calendar.d.ts +1 -1
- package/dist/types/calendar.d.ts.map +1 -1
- package/dist/types/calendar.js.map +1 -1
- package/dist/types/contacts.d.ts +1 -1
- package/dist/types/contacts.d.ts.map +1 -1
- package/dist/types/contacts.js.map +1 -1
- package/dist/types/mail.d.ts +1 -1
- package/dist/types/mail.d.ts.map +1 -1
- package/dist/types/mail.js.map +1 -1
- package/dist/utils/errors.d.ts +110 -38
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +239 -56
- package/dist/utils/errors.js.map +1 -1
- package/dist/visualization/types.d.ts +4 -4
- package/package.json +2 -3
- package/dist/applescript/account-repository.d.ts +0 -30
- package/dist/applescript/account-repository.d.ts.map +0 -1
- package/dist/applescript/account-repository.js +0 -38
- package/dist/applescript/account-repository.js.map +0 -1
- package/dist/applescript/account-scripts.d.ts +0 -21
- package/dist/applescript/account-scripts.d.ts.map +0 -1
- package/dist/applescript/account-scripts.js +0 -180
- package/dist/applescript/account-scripts.js.map +0 -1
- package/dist/applescript/calendar-manager.d.ts +0 -44
- package/dist/applescript/calendar-manager.d.ts.map +0 -1
- package/dist/applescript/calendar-manager.js +0 -92
- package/dist/applescript/calendar-manager.js.map +0 -1
- package/dist/applescript/calendar-writer.d.ts +0 -36
- package/dist/applescript/calendar-writer.d.ts.map +0 -1
- package/dist/applescript/calendar-writer.js +0 -94
- package/dist/applescript/calendar-writer.js.map +0 -1
- package/dist/applescript/content-readers.d.ts +0 -114
- package/dist/applescript/content-readers.d.ts.map +0 -1
- package/dist/applescript/content-readers.js +0 -328
- package/dist/applescript/content-readers.js.map +0 -1
- package/dist/applescript/executor.d.ts +0 -60
- package/dist/applescript/executor.d.ts.map +0 -1
- package/dist/applescript/executor.js +0 -173
- package/dist/applescript/executor.js.map +0 -1
- package/dist/applescript/index.d.ts +0 -20
- package/dist/applescript/index.d.ts.map +0 -1
- package/dist/applescript/index.js +0 -29
- package/dist/applescript/index.js.map +0 -1
- package/dist/applescript/mail-sender.d.ts +0 -38
- package/dist/applescript/mail-sender.d.ts.map +0 -1
- package/dist/applescript/mail-sender.js +0 -67
- package/dist/applescript/mail-sender.js.map +0 -1
- package/dist/applescript/parser.d.ts +0 -235
- package/dist/applescript/parser.d.ts.map +0 -1
- package/dist/applescript/parser.js +0 -496
- package/dist/applescript/parser.js.map +0 -1
- package/dist/applescript/repository.d.ts +0 -64
- package/dist/applescript/repository.d.ts.map +0 -1
- package/dist/applescript/repository.js +0 -444
- package/dist/applescript/repository.js.map +0 -1
- package/dist/applescript/scripts.d.ts +0 -265
- package/dist/applescript/scripts.d.ts.map +0 -1
- package/dist/applescript/scripts.js +0 -1483
- package/dist/applescript/scripts.js.map +0 -1
- package/dist/parsers/olk15.d.ts +0 -87
- package/dist/parsers/olk15.d.ts.map +0 -1
- package/dist/parsers/olk15.js +0 -368
- package/dist/parsers/olk15.js.map +0 -1
- package/dist/tools/notes.d.ts +0 -74
- package/dist/tools/notes.d.ts.map +0 -1
- package/dist/tools/notes.js +0 -136
- package/dist/tools/notes.js.map +0 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
MCP server for Microsoft 365 -- mail, calendar, contacts, tasks, teams, people, and planner.
|
|
8
8
|
|
|
9
|
-
A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that provides **
|
|
9
|
+
A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that provides **229 tools** for full read/write access to Microsoft 365 via the Microsoft Graph API. Manage your emails, calendar events, contacts, tasks, OneNote notes, Teams channels and chats, people directory, and Planner boards directly through MCP.
|
|
10
10
|
|
|
11
11
|
## Features Overview
|
|
12
12
|
|
|
@@ -31,15 +31,15 @@ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that p
|
|
|
31
31
|
| Checklist Items | 5 | Subtasks on To Do tasks |
|
|
32
32
|
| Linked Resources | 4 | Linked resources on To Do tasks |
|
|
33
33
|
| Task Attachments | 4 | File attachments on To Do tasks |
|
|
34
|
-
|
|
|
34
|
+
| OneNote | 6 | Browse notebooks/sections/pages, search, and create pages |
|
|
35
35
|
| Scheduling | 2 | Free/busy availability, meeting time suggestions |
|
|
36
36
|
| Teams -- Channels | 8 | Channel CRUD, team members |
|
|
37
37
|
| Teams -- Channel Messages | 6 | Read and send channel messages with replies |
|
|
38
38
|
| Teams -- Chats | 6 | 1:1 and group chats, send messages |
|
|
39
39
|
| People & Presence | 8 | People search, org chart, presence status |
|
|
40
|
-
| Planner |
|
|
41
|
-
|
|
|
42
|
-
| **Total** | **
|
|
40
|
+
| Planner | 18 | Plans, buckets, tasks, task details with ETag |
|
|
41
|
+
| Shared Mailbox | 8 | Read another user's mailbox, calendar, and OneDrive via delegate/shared access |
|
|
42
|
+
| **Total** | **229** | |
|
|
43
43
|
|
|
44
44
|
## Quick Start
|
|
45
45
|
|
|
@@ -49,13 +49,7 @@ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that p
|
|
|
49
49
|
npx -y @jbctechsolutions/mcp-office365
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
To use the **AppleScript backend** (classic Outlook for Mac only, limited features), set the environment variable:
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
USE_APPLESCRIPT=1
|
|
58
|
-
```
|
|
52
|
+
The server connects to Microsoft 365 via the **Microsoft Graph API** (cross-platform, full read/write access).
|
|
59
53
|
|
|
60
54
|
### Pre-authenticate (optional)
|
|
61
55
|
|
|
@@ -65,32 +59,49 @@ npx @jbctechsolutions/mcp-office365 auth --status
|
|
|
65
59
|
npx @jbctechsolutions/mcp-office365 auth --logout
|
|
66
60
|
```
|
|
67
61
|
|
|
68
|
-
###
|
|
62
|
+
### Server flags
|
|
69
63
|
|
|
70
|
-
|
|
64
|
+
Pass these when launching the server (after any subcommand) to scope the exposed
|
|
65
|
+
tool surface:
|
|
66
|
+
|
|
67
|
+
| Flag | Effect |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| `--preset <names>` | Expose only the listed domains (comma-separated). Repeatable. Default (or `all`) exposes the full surface. |
|
|
70
|
+
| `--read-only` | Expose only read tools (`readOnlyHint: true`). All writes, `prepare_*`/`confirm_*`, and destructive tools are hidden; calling one returns a `READ_ONLY_MODE` error. Note: `download_*` and `get_*_photo` tools are excluded too — they write fetched bytes to local disk and so are not read-only. |
|
|
71
|
+
| `--confirm <mode>` | How a destructive `prepare_*` seeks confirmation. `token` (default) returns an approval token to pass to the matching `confirm_*` tool. `elicit` asks the user inline (MCP elicitation, ~60s) and, on approval, executes immediately; if the client can't elicit, the user cancels, or it times out, it falls back to the token flow. An explicit decline cancels and invalidates the token. |
|
|
72
|
+
|
|
73
|
+
Valid presets: `mail`, `calendar`, `contacts`, `tasks`, `notes`, `teams`,
|
|
74
|
+
`planner`, `files`, `sharepoint`, `excel`, `people`, `meetings`, `shared` (plus `all`).
|
|
75
|
+
An unknown preset name fails startup with the valid list.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Mail + calendar only, read-only
|
|
79
|
+
npx @jbctechsolutions/mcp-office365 --preset mail,calendar --read-only
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
In a client config, add them to `args`:
|
|
71
83
|
|
|
72
|
-
**Graph API backend (default):**
|
|
73
84
|
```json
|
|
74
85
|
{
|
|
75
86
|
"mcpServers": {
|
|
76
87
|
"office365": {
|
|
77
88
|
"command": "npx",
|
|
78
|
-
"args": ["-y", "@jbctechsolutions/mcp-office365"]
|
|
89
|
+
"args": ["-y", "@jbctechsolutions/mcp-office365", "--preset", "mail,calendar", "--read-only"]
|
|
79
90
|
}
|
|
80
91
|
}
|
|
81
92
|
}
|
|
82
93
|
```
|
|
83
94
|
|
|
84
|
-
|
|
95
|
+
### Claude Desktop configuration
|
|
96
|
+
|
|
97
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
98
|
+
|
|
85
99
|
```json
|
|
86
100
|
{
|
|
87
101
|
"mcpServers": {
|
|
88
102
|
"office365": {
|
|
89
103
|
"command": "npx",
|
|
90
|
-
"args": ["-y", "@jbctechsolutions/mcp-office365"]
|
|
91
|
-
"env": {
|
|
92
|
-
"USE_APPLESCRIPT": "1"
|
|
93
|
-
}
|
|
104
|
+
"args": ["-y", "@jbctechsolutions/mcp-office365"]
|
|
94
105
|
}
|
|
95
106
|
}
|
|
96
107
|
}
|
|
@@ -171,16 +182,7 @@ For production, work accounts with conditional access, or full control over the
|
|
|
171
182
|
|
|
172
183
|
## Tool Reference
|
|
173
184
|
|
|
174
|
-
All
|
|
175
|
-
|
|
176
|
-
<details>
|
|
177
|
-
<summary><strong>Accounts (1)</strong></summary>
|
|
178
|
-
|
|
179
|
-
| Tool | Description |
|
|
180
|
-
|------|-------------|
|
|
181
|
-
| `list_accounts` | List all Exchange accounts configured in Outlook |
|
|
182
|
-
|
|
183
|
-
</details>
|
|
185
|
+
All 229 tools listed below.
|
|
184
186
|
|
|
185
187
|
<details>
|
|
186
188
|
<summary><strong>Mail -- Reading (9)</strong></summary>
|
|
@@ -455,15 +457,16 @@ All 181 tools listed below. Tools marked *(Graph API)* require `USE_GRAPH_API=1`
|
|
|
455
457
|
</details>
|
|
456
458
|
|
|
457
459
|
<details>
|
|
458
|
-
<summary><strong>
|
|
460
|
+
<summary><strong>OneNote (6)</strong> <em>(Graph API)</em></summary>
|
|
459
461
|
|
|
460
462
|
| Tool | Description |
|
|
461
463
|
|------|-------------|
|
|
462
|
-
| `
|
|
463
|
-
| `
|
|
464
|
-
| `
|
|
465
|
-
|
|
466
|
-
|
|
464
|
+
| `list_notebooks` | List OneNote notebooks for the current user |
|
|
465
|
+
| `list_note_sections` | List OneNote sections, optionally scoped to a notebook |
|
|
466
|
+
| `list_note_pages` | List OneNote pages, optionally scoped to a section |
|
|
467
|
+
| `get_note_page` | Get a OneNote page's metadata and HTML content |
|
|
468
|
+
| `search_note_pages` | Search OneNote pages by keyword |
|
|
469
|
+
| `create_note_page` | Create a new OneNote page in a section |
|
|
467
470
|
|
|
468
471
|
</details>
|
|
469
472
|
|
|
@@ -548,7 +551,7 @@ All 181 tools listed below. Tools marked *(Graph API)* require `USE_GRAPH_API=1`
|
|
|
548
551
|
</details>
|
|
549
552
|
|
|
550
553
|
<details>
|
|
551
|
-
<summary><strong>Planner (
|
|
554
|
+
<summary><strong>Planner (18)</strong> <em>(Graph API)</em></summary>
|
|
552
555
|
|
|
553
556
|
| Tool | Description |
|
|
554
557
|
|------|-------------|
|
|
@@ -562,6 +565,7 @@ All 181 tools listed below. Tools marked *(Graph API)* require `USE_GRAPH_API=1`
|
|
|
562
565
|
| `prepare_delete_bucket` | Prepare to delete a Planner bucket (two-phase) |
|
|
563
566
|
| `confirm_delete_bucket` | Confirm Planner bucket deletion |
|
|
564
567
|
| `list_planner_tasks` | List all tasks in a Planner plan |
|
|
568
|
+
| `list_my_planner_tasks` | List all Planner tasks assigned to the signed-in user across every plan |
|
|
565
569
|
| `get_planner_task` | Get details for a specific Planner task |
|
|
566
570
|
| `create_planner_task` | Create a new task in a Planner plan |
|
|
567
571
|
| `update_planner_task` | Update a Planner task |
|
|
@@ -572,14 +576,27 @@ All 181 tools listed below. Tools marked *(Graph API)* require `USE_GRAPH_API=1`
|
|
|
572
576
|
|
|
573
577
|
</details>
|
|
574
578
|
|
|
575
|
-
|
|
579
|
+
<details>
|
|
580
|
+
<summary><strong>Shared Mailbox (8)</strong> <em>(Graph API)</em></summary>
|
|
576
581
|
|
|
577
|
-
|
|
582
|
+
Read another user's mailbox, calendar, or OneDrive via `/users/{upn}/...`, relying on the signed-in user having delegate/shared access (`Mail.Read.Shared` / `Calendars.Read.Shared` / `Files.Read.All`). Read-only. These return **raw Graph ids** (not durable tokens, which are scoped to your own `/me` mailbox); pass the `mailbox` plus the raw id back to the matching `get_*` tool.
|
|
578
583
|
|
|
579
|
-
|
|
584
|
+
| Tool | Description |
|
|
585
|
+
| --- | --- |
|
|
586
|
+
| `list_shared_mailbox_folders` | List mail folders in a shared/delegated mailbox |
|
|
587
|
+
| `list_shared_mailbox_emails` | List emails in a shared/delegated mailbox (optionally by folder) |
|
|
588
|
+
| `get_shared_mailbox_email` | Get a single email from a shared/delegated mailbox by raw Graph id |
|
|
589
|
+
| `search_shared_mailbox_emails` | Full-text search a shared/delegated mailbox |
|
|
590
|
+
| `list_shared_calendar_events` | List events from another user's calendar (optionally within a window) |
|
|
591
|
+
| `get_shared_calendar_event` | Get a single event from another user's calendar by raw Graph id |
|
|
592
|
+
| `list_shared_user_drive_items` | List items in another user's OneDrive (root or a folder) |
|
|
593
|
+
| `search_shared_user_drive_items` | Search another user's OneDrive |
|
|
580
594
|
|
|
581
|
-
|
|
582
|
-
|
|
595
|
+
</details>
|
|
596
|
+
|
|
597
|
+
## Architecture
|
|
598
|
+
|
|
599
|
+
The server connects to Microsoft 365 cloud services via the **Microsoft Graph API**. Full read/write access across all 229 tools. No Outlook installation required. Works on macOS, Windows, and Linux.
|
|
583
600
|
|
|
584
601
|
### Two-Phase Approval
|
|
585
602
|
|
|
@@ -587,7 +604,7 @@ Destructive operations (delete, send, move, forward, etc.) use a prepare/confirm
|
|
|
587
604
|
|
|
588
605
|
### ID Caching
|
|
589
606
|
|
|
590
|
-
The server maintains an internal ID mapping layer
|
|
607
|
+
The server maintains an internal ID mapping layer that assigns stable, durable identifier tokens for Graph API resources so tool callers do not need to track raw Graph object IDs.
|
|
591
608
|
|
|
592
609
|
### ETag Caching
|
|
593
610
|
|
|
@@ -597,8 +614,7 @@ Planner resources use ETag-based concurrency control. The server caches ETags fr
|
|
|
597
614
|
|
|
598
615
|
```
|
|
599
616
|
src/
|
|
600
|
-
|
|
601
|
-
graph/ Microsoft Graph API integration (default)
|
|
617
|
+
graph/ Microsoft Graph API integration
|
|
602
618
|
auth/ MSAL authentication, device code flow, token cache
|
|
603
619
|
client/ Graph client wrapper with response caching
|
|
604
620
|
mappers/ Graph-to-internal type mappers
|
|
@@ -609,7 +625,7 @@ src/
|
|
|
609
625
|
|
|
610
626
|
## Required Graph API Permissions
|
|
611
627
|
|
|
612
|
-
These delegated permissions are requested
|
|
628
|
+
These delegated permissions are requested via Microsoft Graph:
|
|
613
629
|
|
|
614
630
|
| Permission | Purpose |
|
|
615
631
|
|------------|---------|
|
|
@@ -629,24 +645,17 @@ These delegated permissions are requested when using the Graph API backend:
|
|
|
629
645
|
| `User.ReadBasic.All` | Read basic user profiles and photos |
|
|
630
646
|
| `Presence.Read.All` | Read user presence/availability |
|
|
631
647
|
| `Group.Read.All` | Read Microsoft 365 groups (for Planner) |
|
|
648
|
+
| `Mail.Read.Shared` | Read mail in shared/delegated mailboxes |
|
|
649
|
+
| `Calendars.Read.Shared` | Read shared/delegated calendars |
|
|
650
|
+
| `Files.Read.All` | Read another user's OneDrive files (shared/delegate access) |
|
|
632
651
|
|
|
633
652
|
## Environment Variables
|
|
634
653
|
|
|
635
654
|
| Variable | Description | Default |
|
|
636
655
|
|----------|-------------|---------|
|
|
637
|
-
| `USE_APPLESCRIPT` | Set to `1` or `true` for legacy AppleScript backend | (unset -- uses Graph API) |
|
|
638
656
|
| `OUTLOOK_MCP_CLIENT_ID` | Override the embedded Azure AD client ID | (embedded) |
|
|
639
657
|
| `OUTLOOK_MCP_TENANT_ID` | Azure AD tenant ID | `common` |
|
|
640
658
|
|
|
641
|
-
## Known Limitations
|
|
642
|
-
|
|
643
|
-
**AppleScript backend:**
|
|
644
|
-
- Google accounts in Outlook are not accessible (macOS/Outlook limitation). Use IMAP configuration or the Graph API backend instead.
|
|
645
|
-
- Write operations limited to calendar events and email sending. All other writes require Graph API.
|
|
646
|
-
|
|
647
|
-
**Graph API backend:**
|
|
648
|
-
- Outlook Notes are not available (Graph API does not expose them). Use AppleScript backend for notes.
|
|
649
|
-
|
|
650
659
|
## Contributing
|
|
651
660
|
|
|
652
661
|
We welcome contributions. Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
package/dist/approval/hash.d.ts
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
* Used to detect if the email changed between prepare and confirm.
|
|
8
8
|
*/
|
|
9
9
|
export declare function hashEmailForApproval(email: {
|
|
10
|
-
id:
|
|
10
|
+
id: string;
|
|
11
11
|
subject: string | null;
|
|
12
|
-
folderId:
|
|
12
|
+
folderId: string;
|
|
13
13
|
timeReceived: number | null;
|
|
14
14
|
}): string;
|
|
15
15
|
/**
|
|
@@ -17,7 +17,7 @@ export declare function hashEmailForApproval(email: {
|
|
|
17
17
|
* Used to detect if the folder changed between prepare and confirm.
|
|
18
18
|
*/
|
|
19
19
|
export declare function hashFolderForApproval(folder: {
|
|
20
|
-
id:
|
|
20
|
+
id: string;
|
|
21
21
|
name: string | null;
|
|
22
22
|
messageCount: number;
|
|
23
23
|
}): string;
|
|
@@ -26,7 +26,7 @@ export declare function hashFolderForApproval(folder: {
|
|
|
26
26
|
* Used to detect if the draft changed between prepare and confirm.
|
|
27
27
|
*/
|
|
28
28
|
export declare function hashDraftForSend(draft: {
|
|
29
|
-
id:
|
|
29
|
+
id: string;
|
|
30
30
|
subject: string | null;
|
|
31
31
|
recipientCount: number;
|
|
32
32
|
}): string;
|
|
@@ -45,7 +45,7 @@ export declare function hashDirectSendForApproval(params: {
|
|
|
45
45
|
* Captures the original message ID, comment length, and reply-all flag.
|
|
46
46
|
*/
|
|
47
47
|
export declare function hashReplyForApproval(params: {
|
|
48
|
-
originalId:
|
|
48
|
+
originalId: string;
|
|
49
49
|
commentLength: number;
|
|
50
50
|
replyAll: boolean;
|
|
51
51
|
}): string;
|
|
@@ -54,7 +54,7 @@ export declare function hashReplyForApproval(params: {
|
|
|
54
54
|
* Captures the original message ID and the number of forward recipients.
|
|
55
55
|
*/
|
|
56
56
|
export declare function hashForwardForApproval(params: {
|
|
57
|
-
originalId:
|
|
57
|
+
originalId: string;
|
|
58
58
|
recipientCount: number;
|
|
59
59
|
}): string;
|
|
60
60
|
/**
|
|
@@ -62,7 +62,7 @@ export declare function hashForwardForApproval(params: {
|
|
|
62
62
|
* Used to detect if the event changed between prepare and confirm.
|
|
63
63
|
*/
|
|
64
64
|
export declare function hashEventForApproval(event: {
|
|
65
|
-
id:
|
|
65
|
+
id: string;
|
|
66
66
|
subject: string | null;
|
|
67
67
|
startDateTime: string | null;
|
|
68
68
|
}): string;
|
|
@@ -71,7 +71,7 @@ export declare function hashEventForApproval(event: {
|
|
|
71
71
|
* Used to detect if the contact changed between prepare and confirm.
|
|
72
72
|
*/
|
|
73
73
|
export declare function hashContactForApproval(contact: {
|
|
74
|
-
id:
|
|
74
|
+
id: string;
|
|
75
75
|
displayName: string | null;
|
|
76
76
|
emailAddress: string | null;
|
|
77
77
|
}): string;
|
|
@@ -2,49 +2,67 @@
|
|
|
2
2
|
* Copyright (c) 2026 JBC Tech Solutions, LLC
|
|
3
3
|
* Licensed under the MIT License. See LICENSE file in the project root.
|
|
4
4
|
*/
|
|
5
|
+
import type { StateStore } from '../state/store.js';
|
|
5
6
|
import type { OperationType, TargetType, ApprovalToken, ValidationResult } from './types.js';
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
/** Options for {@link ApprovalTokenManager}. */
|
|
8
|
+
export interface ApprovalTokenManagerOptions {
|
|
9
|
+
/** Token lifetime (default 24h). */
|
|
10
|
+
ttlMs?: number;
|
|
11
|
+
/** Durable store; omit for in-memory (tests / degraded mode). */
|
|
12
|
+
store?: StateStore;
|
|
13
|
+
/**
|
|
14
|
+
* Account stamp for stored tokens (D7). May be a function, since the signed-in
|
|
15
|
+
* account (MSAL `homeAccountId`) is only known after auth — later than when
|
|
16
|
+
* this manager is constructed — and is resolved lazily at each token op.
|
|
17
|
+
*/
|
|
18
|
+
accountId?: string | (() => string);
|
|
19
|
+
/** Clock, for deterministic tests. */
|
|
20
|
+
now?: () => number;
|
|
21
|
+
}
|
|
12
22
|
export declare class ApprovalTokenManager {
|
|
13
23
|
private readonly tokens;
|
|
14
24
|
private readonly ttlMs;
|
|
15
|
-
|
|
25
|
+
private readonly store;
|
|
26
|
+
private readonly resolveAccountId;
|
|
27
|
+
private readonly now;
|
|
28
|
+
constructor(options?: ApprovalTokenManagerOptions | number);
|
|
29
|
+
/** The account scope for store ops, resolved fresh each call (D7). */
|
|
30
|
+
private get accountId();
|
|
16
31
|
/**
|
|
17
32
|
* Generates a new approval token for a destructive operation.
|
|
18
33
|
*/
|
|
19
34
|
generateToken(params: {
|
|
20
35
|
operation: OperationType;
|
|
21
36
|
targetType: TargetType;
|
|
22
|
-
targetId:
|
|
37
|
+
targetId: string;
|
|
23
38
|
targetHash: string;
|
|
24
39
|
metadata?: Record<string, unknown>;
|
|
25
40
|
}): ApprovalToken;
|
|
26
41
|
/**
|
|
27
|
-
* Looks up a token by ID without consuming or validating it.
|
|
28
|
-
*
|
|
42
|
+
* Looks up a token by ID without consuming or validating it. Returns undefined
|
|
43
|
+
* if the token does not exist (or belongs to another account, when durable).
|
|
29
44
|
*/
|
|
30
45
|
lookupToken(tokenId: string): ApprovalToken | undefined;
|
|
31
46
|
/**
|
|
32
|
-
* Validates a token without consuming it
|
|
33
|
-
*
|
|
47
|
+
* Validates a token without consuming it. Checks existence, prior redemption,
|
|
48
|
+
* expiry, operation match, and target match.
|
|
34
49
|
*/
|
|
35
|
-
validateToken(tokenId: string, operation: OperationType, targetId:
|
|
50
|
+
validateToken(tokenId: string, operation: OperationType, targetId: string): ValidationResult;
|
|
51
|
+
private check;
|
|
36
52
|
/**
|
|
37
|
-
* Validates and consumes a token (one-time use).
|
|
38
|
-
*
|
|
53
|
+
* Validates and consumes a token (one-time use). Durable stores use an atomic
|
|
54
|
+
* guarded consume so only one caller — across processes sharing the db — can
|
|
55
|
+
* redeem it; a losing/repeat caller gets `ALREADY_CONSUMED` (D8).
|
|
39
56
|
*/
|
|
40
|
-
consumeToken(tokenId: string, operation: OperationType, targetId:
|
|
57
|
+
consumeToken(tokenId: string, operation: OperationType, targetId: string): ValidationResult;
|
|
41
58
|
/**
|
|
42
|
-
* Removes
|
|
59
|
+
* Removes expired tokens from the in-memory store. Durable stores purge on
|
|
60
|
+
* boot (90-day retention), so this is a no-op there.
|
|
43
61
|
*/
|
|
44
62
|
cleanupExpiredTokens(): void;
|
|
45
63
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
64
|
+
* Number of active in-memory tokens (testing/monitoring). Durable tokens are
|
|
65
|
+
* not counted here — they live in the store.
|
|
48
66
|
*/
|
|
49
67
|
get size(): number;
|
|
50
68
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-manager.d.ts","sourceRoot":"","sources":["../../src/approval/token-manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"token-manager.d.ts","sourceRoot":"","sources":["../../src/approval/token-manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAeH,OAAO,KAAK,EAAE,UAAU,EAAoB,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAa7F,gDAAgD;AAChD,MAAM,WAAW,2BAA2B;IAC1C,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC;IACpC,sCAAsC;IACtC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoC;IAC3D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAC/C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAe;IAChD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,OAAO,GAAE,2BAA2B,GAAG,MAAW;IAU9D,sEAAsE;IACtE,OAAO,KAAK,SAAS,GAEpB;IAED;;OAEG;IACH,aAAa,CAAC,MAAM,EAAE;QACpB,SAAS,EAAE,aAAa,CAAC;QACzB,UAAU,EAAE,UAAU,CAAC;QACvB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACpC,GAAG,aAAa;IAsCjB;;;OAGG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAQvD;;;OAGG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB;IAyB5F,OAAO,CAAC,KAAK;IAeb;;;;OAIG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB;IA4B3F;;;OAGG;IACH,oBAAoB,IAAI,IAAI;IAY5B;;;OAGG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|
|
@@ -3,41 +3,43 @@
|
|
|
3
3
|
* Licensed under the MIT License. See LICENSE file in the project root.
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
|
-
* Approval token manager.
|
|
6
|
+
* Approval token manager (U9). Manages the lifecycle of two-phase approval
|
|
7
|
+
* tokens: generation, validation, and atomic single-use consumption.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
9
|
+
* When a durable {@link StateStore} is supplied (production), tokens persist to
|
|
10
|
+
* SQLite with a 24h TTL and are consumed via an atomic guarded write (D8), so an
|
|
11
|
+
* approval prepared in one process/session survives a restart and can be
|
|
12
|
+
* redeemed exactly once — even across concurrent Claude Code windows. Without a
|
|
13
|
+
* store (tests / degraded mode) it falls back to an in-memory map, preserving
|
|
14
|
+
* the original behavior and public surface.
|
|
10
15
|
*/
|
|
11
16
|
import { randomUUID } from 'node:crypto';
|
|
12
|
-
//
|
|
13
|
-
// Constants
|
|
14
|
-
// =============================================================================
|
|
15
|
-
const DEFAULT_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
17
|
+
const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000; // 24h (D8, up from 5 minutes)
|
|
16
18
|
const CLEANUP_THRESHOLD = 100;
|
|
17
|
-
// =============================================================================
|
|
18
|
-
// Token Manager
|
|
19
|
-
// =============================================================================
|
|
20
|
-
/**
|
|
21
|
-
* Manages approval tokens for destructive operations.
|
|
22
|
-
*
|
|
23
|
-
* Tokens are stored in memory and are single-use. They expire
|
|
24
|
-
* after a configurable TTL (default 5 minutes).
|
|
25
|
-
*/
|
|
26
19
|
export class ApprovalTokenManager {
|
|
27
20
|
tokens = new Map();
|
|
28
21
|
ttlMs;
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
store;
|
|
23
|
+
resolveAccountId;
|
|
24
|
+
now;
|
|
25
|
+
constructor(options = {}) {
|
|
26
|
+
// Back-compat: some call sites/tests pass a bare ttlMs number.
|
|
27
|
+
const opts = typeof options === 'number' ? { ttlMs: options } : options;
|
|
28
|
+
this.ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
|
|
29
|
+
this.store = opts.store;
|
|
30
|
+
const account = opts.accountId ?? 'default';
|
|
31
|
+
this.resolveAccountId = typeof account === 'function' ? account : (() => account);
|
|
32
|
+
this.now = opts.now ?? (() => Date.now());
|
|
33
|
+
}
|
|
34
|
+
/** The account scope for store ops, resolved fresh each call (D7). */
|
|
35
|
+
get accountId() {
|
|
36
|
+
return this.resolveAccountId();
|
|
31
37
|
}
|
|
32
38
|
/**
|
|
33
39
|
* Generates a new approval token for a destructive operation.
|
|
34
40
|
*/
|
|
35
41
|
generateToken(params) {
|
|
36
|
-
|
|
37
|
-
if (this.tokens.size > CLEANUP_THRESHOLD) {
|
|
38
|
-
this.cleanupExpiredTokens();
|
|
39
|
-
}
|
|
40
|
-
const now = Date.now();
|
|
42
|
+
const now = this.now();
|
|
41
43
|
const token = {
|
|
42
44
|
tokenId: randomUUID(),
|
|
43
45
|
operation: params.operation,
|
|
@@ -48,26 +50,73 @@ export class ApprovalTokenManager {
|
|
|
48
50
|
expiresAt: now + this.ttlMs,
|
|
49
51
|
metadata: Object.freeze({ ...params.metadata }),
|
|
50
52
|
};
|
|
51
|
-
this.
|
|
53
|
+
if (this.store != null) {
|
|
54
|
+
const target = {
|
|
55
|
+
targetType: token.targetType,
|
|
56
|
+
targetId: token.targetId,
|
|
57
|
+
targetHash: token.targetHash,
|
|
58
|
+
metadata: { ...token.metadata },
|
|
59
|
+
};
|
|
60
|
+
this.store.putApprovalToken({
|
|
61
|
+
token: token.tokenId,
|
|
62
|
+
action: token.operation,
|
|
63
|
+
targetJson: JSON.stringify(target),
|
|
64
|
+
contentHash: token.targetHash,
|
|
65
|
+
accountId: this.accountId,
|
|
66
|
+
expiresAt: token.expiresAt,
|
|
67
|
+
createdAt: token.createdAt,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
if (this.tokens.size > CLEANUP_THRESHOLD) {
|
|
72
|
+
this.cleanupExpiredTokens();
|
|
73
|
+
}
|
|
74
|
+
this.tokens.set(token.tokenId, token);
|
|
75
|
+
}
|
|
52
76
|
return token;
|
|
53
77
|
}
|
|
54
78
|
/**
|
|
55
|
-
* Looks up a token by ID without consuming or validating it.
|
|
56
|
-
*
|
|
79
|
+
* Looks up a token by ID without consuming or validating it. Returns undefined
|
|
80
|
+
* if the token does not exist (or belongs to another account, when durable).
|
|
57
81
|
*/
|
|
58
82
|
lookupToken(tokenId) {
|
|
83
|
+
if (this.store != null) {
|
|
84
|
+
const row = this.store.getApprovalToken(tokenId, this.accountId);
|
|
85
|
+
return row != null ? (rowToToken(row) ?? undefined) : undefined;
|
|
86
|
+
}
|
|
59
87
|
return this.tokens.get(tokenId);
|
|
60
88
|
}
|
|
61
89
|
/**
|
|
62
|
-
* Validates a token without consuming it
|
|
63
|
-
*
|
|
90
|
+
* Validates a token without consuming it. Checks existence, prior redemption,
|
|
91
|
+
* expiry, operation match, and target match.
|
|
64
92
|
*/
|
|
65
93
|
validateToken(tokenId, operation, targetId) {
|
|
94
|
+
if (this.store != null) {
|
|
95
|
+
const row = this.store.getApprovalToken(tokenId, this.accountId);
|
|
96
|
+
if (row == null) {
|
|
97
|
+
return { valid: false, error: 'NOT_FOUND' };
|
|
98
|
+
}
|
|
99
|
+
if (row.redeemedAt != null) {
|
|
100
|
+
return { valid: false, error: 'ALREADY_CONSUMED' };
|
|
101
|
+
}
|
|
102
|
+
const token = rowToToken(row);
|
|
103
|
+
// A corrupt/tampered target_json fails closed as NOT_FOUND rather than
|
|
104
|
+
// throwing out of validate — the token simply can't be redeemed.
|
|
105
|
+
if (token == null) {
|
|
106
|
+
return { valid: false, error: 'NOT_FOUND' };
|
|
107
|
+
}
|
|
108
|
+
return this.check(token, operation, targetId);
|
|
109
|
+
}
|
|
66
110
|
const token = this.tokens.get(tokenId);
|
|
67
111
|
if (token == null) {
|
|
68
112
|
return { valid: false, error: 'NOT_FOUND' };
|
|
69
113
|
}
|
|
70
|
-
|
|
114
|
+
return this.check(token, operation, targetId);
|
|
115
|
+
}
|
|
116
|
+
check(token, operation, targetId) {
|
|
117
|
+
// `>=` so validate agrees with the store's consume guard (expires_at > now):
|
|
118
|
+
// a token is expired at the exact expiry instant, on both paths.
|
|
119
|
+
if (this.now() >= token.expiresAt) {
|
|
71
120
|
return { valid: false, error: 'EXPIRED' };
|
|
72
121
|
}
|
|
73
122
|
if (token.operation !== operation) {
|
|
@@ -79,21 +128,44 @@ export class ApprovalTokenManager {
|
|
|
79
128
|
return { valid: true, token };
|
|
80
129
|
}
|
|
81
130
|
/**
|
|
82
|
-
* Validates and consumes a token (one-time use).
|
|
83
|
-
*
|
|
131
|
+
* Validates and consumes a token (one-time use). Durable stores use an atomic
|
|
132
|
+
* guarded consume so only one caller — across processes sharing the db — can
|
|
133
|
+
* redeem it; a losing/repeat caller gets `ALREADY_CONSUMED` (D8).
|
|
84
134
|
*/
|
|
85
135
|
consumeToken(tokenId, operation, targetId) {
|
|
86
136
|
const result = this.validateToken(tokenId, operation, targetId);
|
|
87
|
-
if (result.valid) {
|
|
88
|
-
|
|
137
|
+
if (!result.valid) {
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
if (this.store != null) {
|
|
141
|
+
const consumed = this.store.consumeApprovalToken({
|
|
142
|
+
token: tokenId,
|
|
143
|
+
accountId: this.accountId,
|
|
144
|
+
now: this.now(),
|
|
145
|
+
});
|
|
146
|
+
switch (consumed.status) {
|
|
147
|
+
case 'consumed':
|
|
148
|
+
return result;
|
|
149
|
+
case 'already_redeemed':
|
|
150
|
+
return { valid: false, error: 'ALREADY_CONSUMED' };
|
|
151
|
+
case 'expired':
|
|
152
|
+
return { valid: false, error: 'EXPIRED' };
|
|
153
|
+
default:
|
|
154
|
+
return { valid: false, error: 'NOT_FOUND' };
|
|
155
|
+
}
|
|
89
156
|
}
|
|
157
|
+
this.tokens.delete(tokenId);
|
|
90
158
|
return result;
|
|
91
159
|
}
|
|
92
160
|
/**
|
|
93
|
-
* Removes
|
|
161
|
+
* Removes expired tokens from the in-memory store. Durable stores purge on
|
|
162
|
+
* boot (90-day retention), so this is a no-op there.
|
|
94
163
|
*/
|
|
95
164
|
cleanupExpiredTokens() {
|
|
96
|
-
|
|
165
|
+
if (this.store != null) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const now = this.now();
|
|
97
169
|
for (const [tokenId, token] of this.tokens) {
|
|
98
170
|
if (now > token.expiresAt) {
|
|
99
171
|
this.tokens.delete(tokenId);
|
|
@@ -101,11 +173,35 @@ export class ApprovalTokenManager {
|
|
|
101
173
|
}
|
|
102
174
|
}
|
|
103
175
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
176
|
+
* Number of active in-memory tokens (testing/monitoring). Durable tokens are
|
|
177
|
+
* not counted here — they live in the store.
|
|
106
178
|
*/
|
|
107
179
|
get size() {
|
|
108
180
|
return this.tokens.size;
|
|
109
181
|
}
|
|
110
182
|
}
|
|
183
|
+
/** Reconstructs an {@link ApprovalToken} from a stored row, or null if the
|
|
184
|
+
* persisted target JSON is corrupt/unparseable (fail closed). */
|
|
185
|
+
function rowToToken(row) {
|
|
186
|
+
let target;
|
|
187
|
+
try {
|
|
188
|
+
target = JSON.parse(row.targetJson);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
tokenId: row.token,
|
|
195
|
+
operation: row.action,
|
|
196
|
+
targetType: target.targetType,
|
|
197
|
+
// Coerce to string: a token persisted by a pre-v4 build may carry a numeric
|
|
198
|
+
// targetId (e.g. the send_email/upload_file sentinel `0`), which would else
|
|
199
|
+
// fail the strict `!==` target check against v4's string ids within the TTL.
|
|
200
|
+
targetId: String(target.targetId),
|
|
201
|
+
targetHash: target.targetHash ?? row.contentHash ?? '',
|
|
202
|
+
createdAt: row.createdAt,
|
|
203
|
+
expiresAt: row.expiresAt,
|
|
204
|
+
metadata: Object.freeze({ ...target.metadata }),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
111
207
|
//# sourceMappingURL=token-manager.js.map
|