@juvantlabs/m365-graph-mcp-server 0.2.0 → 0.3.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/ARCHITECTURE.md +9 -2
- package/CHANGELOG.md +112 -1
- package/README.md +27 -12
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -2
- package/dist/index.js.map +1 -1
- package/dist/tools/get_transcript.d.ts +21 -5
- package/dist/tools/get_transcript.d.ts.map +1 -1
- package/dist/tools/get_transcript.js +111 -17
- package/dist/tools/get_transcript.js.map +1 -1
- package/package.json +1 -1
package/ARCHITECTURE.md
CHANGED
|
@@ -39,7 +39,12 @@ the auth path consolidated.
|
|
|
39
39
|
**Meeting transcripts** (v0.2.0+)
|
|
40
40
|
|
|
41
41
|
- List available transcripts for a Teams meeting (from a calendar event ID).
|
|
42
|
-
- Fetch transcript content (VTT parsed to clean text
|
|
42
|
+
- Fetch transcript content (VTT parsed to clean text). Long transcripts page
|
|
43
|
+
client-side via `offset` + `next_offset` — the Graph content endpoint
|
|
44
|
+
returns the whole VTT in one blob, so paging is implemented in this server
|
|
45
|
+
by slicing the parsed text. Per-call cap (`M365_TRANSCRIPT_MAX_CHARS`,
|
|
46
|
+
default 200 000) and upstream byte cap (`M365_TRANSCRIPT_MAX_BYTES`,
|
|
47
|
+
default 10 MB) are configurable.
|
|
43
48
|
- Post-meeting only: live transcription during active calls requires a media bot
|
|
44
49
|
(separate threat model; explicitly out of scope — see below).
|
|
45
50
|
|
|
@@ -163,7 +168,7 @@ that informs the [handbook MCP server spec](https://github.com/juvantlabs/handbo
|
|
|
163
168
|
| README env-var lies (audit S2) | CI README env-var accuracy check. |
|
|
164
169
|
| OData / URL injection (audit S3) | All Graph queries built via the SDK or with explicit `encodeURIComponent`. |
|
|
165
170
|
| Token storage (audit S5) | `@napi-rs/keyring`, never `keytar`. |
|
|
166
|
-
| Whole-file buffering (audit S7) | Downloads stream; max file size capped at 200 MB (configurable). |
|
|
171
|
+
| Whole-file buffering (audit S7) | Downloads stream; max file size capped at 200 MB (configurable). Transcript reads cap raw VTT at `M365_TRANSCRIPT_MAX_BYTES` (default 10 MB) and parsed-text per response at `M365_TRANSCRIPT_MAX_CHARS` (default 200 000 chars). |
|
|
167
172
|
| No async-op polling (audit S8) | `copy` / `move` poll the monitor URL until completion; never return "initiated successfully" as the final result. |
|
|
168
173
|
|
|
169
174
|
### Universal Boundaries (per `SYSTEM_INVARIANTS.md` §4)
|
|
@@ -217,6 +222,8 @@ Each row is also reflected in [`README.md`](README.md) § Tools.
|
|
|
217
222
|
| `m365-graph:cancel_event` | Phase 1: `GET /events/{id}` → preview. Phase 2: `POST /events/{id}/cancel { Comment }` after token consume. | `event_id` (required), `comment?`, `confirmation_token?` | preview or `{ cancelled }` | `Calendars.ReadWrite` | Same two-phase pattern as delete_file. The `comment` is part of the spec — changing it between preview and execute fails `spec_mismatch`. |
|
|
218
223
|
| `m365-graph:decline_event` | Phase 1: `GET /events/{id}` → preview. Phase 2: `POST /events/{id}/decline { sendResponse, comment? }` after token consume. | `event_id`, `comment?`, `send_response?`, `confirmation_token?` | preview or `{ declined }` | `Calendars.ReadWrite` | For events the user is invited to (attendee). Cancel_event vs decline_event reflect the Graph distinction (organizer vs attendee). `send_response` is part of the spec hash so changing it between preview/execute fails `spec_mismatch`. Default true = organizer notified; false = silent decline. |
|
|
219
224
|
| `m365-graph:search_events_content` | `POST /search/query` with `entityTypes: ["event"]` + queryString | `query`, `limit?`, `from?` | `{ count, total, results: [<event summary>] }` | `Calendars.Read` | Subject + body search via the Microsoft Search API (separate from `$filter` on /me/events used by `search_events`). Search API hit shape `{ hitId, summary, resource }` mapped back to summarizeEvent's shape via `resource`. Returns recurrence series masters. |
|
|
225
|
+
| `m365-graph:list_meeting_transcripts` | 3-step: `GET /me/events/{id}` (resolve `onlineMeeting.joinUrl`) → `GET /me/onlineMeetings?$filter=JoinWebUrl eq '…'` (resolve onlineMeeting id) → `GET /me/onlineMeetings/{meeting-id}/transcripts` | `event_id` | `{ event_id, meeting_id, count, transcripts: [{ id, meeting_id, created_at, end_at }] }` | `Calendars.Read`, `OnlineMeetings.Read`, `OnlineMeetingTranscript.Read.All` (latter two delegated, **admin consent required**) | Non-online-meeting events return `{ error: "not_an_online_meeting" }`; missing join URL or unresolved onlineMeeting returns `{ error: "meeting_id_unavailable" }` (both as text content, not thrown). joinUrl is single-quote-escaped before OData filter interpolation. Empty `transcripts` list (not an error) when recording was disabled or transcript is still processing. |
|
|
226
|
+
| `m365-graph:get_transcript` | `GET /me/onlineMeetings/{meeting-id}/transcripts/{transcript-id}/content?$format=text/vtt` (returns raw WebVTT) | `meeting_id`, `transcript_id`, `offset?` (0..2_000_000_000, default 0), `max_chars?` (1..`M365_TRANSCRIPT_MAX_CHARS`, default = cap) | `{ meeting_id, transcript_id, offset, char_count, next_offset, total_char_count, truncated, vtt_truncated, transcript }` | `OnlineMeetingTranscript.Read.All` (delegated, **admin consent required**) | **VTT timing-marker stripping**: `WEBVTT` header, `NOTE` blocks, `HH:MM:SS.mmm --> …` timestamp lines, numeric cue-sequence lines, and blank lines are stripped; cue text (speaker names + spoken content) is kept and joined with `\n`. **Two caps** (defense-in-depth, audit-S7 whole-file-buffering bound): raw VTT bytes read from Graph capped at `M365_TRANSCRIPT_MAX_BYTES` (default 10 MB ≈ 8h+ of speech) — surplus is dropped and `vtt_truncated: true` is flagged on the response; parsed-text characters returned per call capped at `M365_TRANSCRIPT_MAX_CHARS` (default 200 000 ≈ 2h of speech). **Client-side paging**: long transcripts are paged by slicing the parsed text — caller passes the previous response's `next_offset` (or `null` if done) back as `offset` on the next call. `truncated` is `true` iff `next_offset !== null` (i.e. the returned slice does not reach the end of the parsed text); distinct from `vtt_truncated`, which signals upstream byte-cap loss. |
|
|
220
227
|
|
|
221
228
|
## Spec/approval confirmation-token pattern
|
|
222
229
|
|
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,109 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
+
## [Unreleased]
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## [0.3.0] - 2026-06-22 — Long Teams transcript paging + cap fix
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **`m365-graph:get_transcript`** silently truncated long Teams meeting
|
|
21
|
+
transcripts at 30 000 chars (parsed text) and 500 KB (raw VTT). Defaults
|
|
22
|
+
are now 200 000 chars / 10 MB — generous enough for multi-hour meetings —
|
|
23
|
+
and the byte cap loss is now flagged on the response (`vtt_truncated`).
|
|
24
|
+
Issue [#2](https://github.com/juvantlabs/m365-graph-mcp-server/issues/2).
|
|
25
|
+
- **Server version reporting** — the MCP `Server` constructor previously
|
|
26
|
+
advertised a hardcoded `version: "0.1.4"` to every MCP client while
|
|
27
|
+
`package.json` had moved on. The version is now read from `package.json`
|
|
28
|
+
at runtime via `createRequire(import.meta.url)`, so it always matches the
|
|
29
|
+
shipped package (and is also surfaced in the stdio startup log line). A
|
|
30
|
+
unit test asserts `PACKAGE_VERSION === package.json#version` to prevent
|
|
31
|
+
silent drift from recurring.
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- **`m365-graph:get_transcript`** — paging support. New optional inputs
|
|
36
|
+
`offset` (0..2_000_000_000) and `max_chars`. New response fields `offset`,
|
|
37
|
+
`next_offset`, `total_char_count`, and `vtt_truncated`. Existing fields
|
|
38
|
+
(`meeting_id`, `transcript_id`, `char_count`, `truncated`, `transcript`)
|
|
39
|
+
remain. Backward-compatible: prior callers passing only `meeting_id` +
|
|
40
|
+
`transcript_id` keep working; `truncated` now means "this slice does not
|
|
41
|
+
reach the end of the parsed text" (previously meant "30k cap hit", which
|
|
42
|
+
was always at offset 0 so the semantics align).
|
|
43
|
+
- **`M365_TRANSCRIPT_MAX_BYTES`** env var — overrides the upstream VTT
|
|
44
|
+
byte cap (default 10 000 000).
|
|
45
|
+
- **`M365_TRANSCRIPT_MAX_CHARS`** env var — overrides the per-call parsed-
|
|
46
|
+
text cap (default 200 000).
|
|
47
|
+
|
|
48
|
+
### Security
|
|
49
|
+
|
|
50
|
+
- `npm audit fix` to clear moderate+ advisories that were blocking CI. No
|
|
51
|
+
direct-dependency major bumps; transitive lockfile updates only.
|
|
52
|
+
|
|
53
|
+
### Documentation
|
|
54
|
+
|
|
55
|
+
- Accuracy pass across `ARCHITECTURE.md`, `README.md`, and `## Status` ahead
|
|
56
|
+
of the v0.3.0 tag. `ARCHITECTURE.md § Tool catalog` gains rows for
|
|
57
|
+
`list_meeting_transcripts` and `get_transcript` (Graph call chain, inputs,
|
|
58
|
+
outputs, scopes, VTT-stripping + paging + cap notes). README Tools-table
|
|
59
|
+
rows for `list_calendars`, `list_events`, and `get_event` reconciled with
|
|
60
|
+
the actual handler shapes (owner_name/owner_email split, body_preview /
|
|
61
|
+
is_all_day / is_online_meeting / organizer_name / organizer_email / webLink,
|
|
62
|
+
online_meeting_join_url). `## What's new` is now evergreen (no version
|
|
63
|
+
suffix); the `get_transcript` bullet drops the stale "30 000 chars" cap
|
|
64
|
+
reference — cap and paging detail live in the Tools table and the
|
|
65
|
+
`## Environment variables` table only. `## Status` reads v0.3.0; the
|
|
66
|
+
`mcp_server` pin in the binding example is bumped to `0.3.0`. No code
|
|
67
|
+
changes.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## [0.2.1] - 2026-06-15
|
|
72
|
+
|
|
73
|
+
### Documentation
|
|
74
|
+
|
|
75
|
+
- README `## What's new in v0.2.0` section added retroactively for the v0.2.0
|
|
76
|
+
ship (transcript tools + new delegated scopes + `npm run setup` reminder).
|
|
77
|
+
No behavior change; package metadata-only release.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## [0.2.0] - 2026-05-15
|
|
82
|
+
|
|
83
|
+
### Added
|
|
84
|
+
|
|
85
|
+
- **`m365-graph:list_meeting_transcripts`** — list available transcripts for a
|
|
86
|
+
Teams meeting identified by a calendar event ID. Resolves the event's
|
|
87
|
+
`onlineMeeting.joinUrl` → filters `/me/onlineMeetings` by JoinWebUrl →
|
|
88
|
+
lists transcripts via `/me/onlineMeetings/{id}/transcripts`. Returns an
|
|
89
|
+
empty list (not an error) when no transcript is available yet. Requires
|
|
90
|
+
`OnlineMeetings.Read` + `OnlineMeetingTranscript.Read.All` (both delegated,
|
|
91
|
+
admin consent required — see README § Tools).
|
|
92
|
+
- **`m365-graph:get_transcript`** — fetch the text content of a Teams meeting
|
|
93
|
+
transcript. The Graph API returns VTT (WebVTT subtitle format); this tool
|
|
94
|
+
strips timing markers, sequence numbers, and NOTE blocks, returning clean
|
|
95
|
+
readable text capped at 30 000 chars. Handles both ReadableStream and string
|
|
96
|
+
responses from the Graph SDK. Requires `OnlineMeetingTranscript.Read.All`.
|
|
97
|
+
- **`m365-graph:list_events`** — new field `is_online_meeting` (boolean) in
|
|
98
|
+
every event summary. Lets callers identify Teams meetings without a separate
|
|
99
|
+
`get_event` call.
|
|
100
|
+
- **`m365-graph:get_event`** — new field `online_meeting_join_url` (string or
|
|
101
|
+
null) sourced from `onlineMeeting.joinUrl`.
|
|
102
|
+
|
|
103
|
+
### Changed
|
|
104
|
+
|
|
105
|
+
- `DELEGATED_SCOPES` now includes `OnlineMeetings.Read` and
|
|
106
|
+
`OnlineMeetingTranscript.Read.All`. **Re-run `npm run setup`** (or
|
|
107
|
+
`npx @juvantlabs/m365-graph-mcp-server setup`) after upgrading to acquire
|
|
108
|
+
the new scopes. Both require admin consent in the Entra app registration.
|
|
109
|
+
|
|
110
|
+
### Fixed
|
|
111
|
+
|
|
112
|
+
- OData injection: `joinUrl` is now single-quote-escaped before embedding in
|
|
113
|
+
the `JoinWebUrl eq '...'` OData filter string.
|
|
114
|
+
|
|
12
115
|
## [0.1.4] - 2026-05-05
|
|
13
116
|
|
|
14
117
|
### Added
|
|
@@ -265,4 +368,12 @@ the Juvant OS instance level — see juvant-os-pm FEAT-018 / 019 /
|
|
|
265
368
|
|
|
266
369
|
---
|
|
267
370
|
|
|
268
|
-
[Unreleased]: https://github.com/juvantlabs/m365-graph-mcp-server/compare/HEAD
|
|
371
|
+
[Unreleased]: https://github.com/juvantlabs/m365-graph-mcp-server/compare/v0.3.0...HEAD
|
|
372
|
+
[0.3.0]: https://github.com/juvantlabs/m365-graph-mcp-server/compare/v0.2.1...v0.3.0
|
|
373
|
+
[0.2.1]: https://github.com/juvantlabs/m365-graph-mcp-server/compare/v0.2.0...v0.2.1
|
|
374
|
+
[0.2.0]: https://github.com/juvantlabs/m365-graph-mcp-server/compare/v0.1.4...v0.2.0
|
|
375
|
+
[0.1.4]: https://github.com/juvantlabs/m365-graph-mcp-server/compare/v0.1.3...v0.1.4
|
|
376
|
+
[0.1.3]: https://github.com/juvantlabs/m365-graph-mcp-server/compare/v0.1.2...v0.1.3
|
|
377
|
+
[0.1.2]: https://github.com/juvantlabs/m365-graph-mcp-server/compare/v0.1.1...v0.1.2
|
|
378
|
+
[0.1.1]: https://github.com/juvantlabs/m365-graph-mcp-server/compare/v0.1.0...v0.1.1
|
|
379
|
+
[0.1.0]: https://github.com/juvantlabs/m365-graph-mcp-server/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -17,15 +17,28 @@ real Juvant OS need surfaces; outbound Teams notifications go through
|
|
|
17
17
|
webhooks (Adaptive Cards), not MCP. Per-company instance config binds
|
|
18
18
|
this server in `.juvant/config.json`.
|
|
19
19
|
|
|
20
|
+
## What's new
|
|
21
|
+
|
|
22
|
+
**Teams meeting transcript support.** Two new read-only tools:
|
|
23
|
+
|
|
24
|
+
- **`m365-graph:list_meeting_transcripts`** — given a calendar event ID, lists available post-meeting transcripts. Returns transcript IDs to pass to `get_transcript`.
|
|
25
|
+
- **`m365-graph:get_transcript`** — fetches the transcript content, strips VTT timing markers, and returns clean readable text.
|
|
26
|
+
|
|
27
|
+
Two new delegated scopes required (admin consent — see § Tools):
|
|
28
|
+
`OnlineMeetings.Read` and `OnlineMeetingTranscript.Read.All`.
|
|
29
|
+
**Re-run `npm run setup` after upgrading** to acquire them.
|
|
30
|
+
|
|
31
|
+
See [CHANGELOG.md](CHANGELOG.md) for the full change list.
|
|
32
|
+
|
|
20
33
|
## Status
|
|
21
34
|
|
|
22
|
-
**Published.** v0.
|
|
23
|
-
([`@juvantlabs/m365-graph-mcp-server`](https://www.npmjs.com/package/@juvantlabs/m365-graph-mcp-server))
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
35
|
+
**Published.** v0.3.0 on npm
|
|
36
|
+
([`@juvantlabs/m365-graph-mcp-server`](https://www.npmjs.com/package/@juvantlabs/m365-graph-mcp-server)).
|
|
37
|
+
19 tools across files (OneDrive + SharePoint), Outlook Calendar, and
|
|
38
|
+
Teams meeting transcripts. Published via npm **Trusted Publishing**
|
|
39
|
+
(OIDC-based auth from GitHub Actions; no static `NPM_TOKEN`) with
|
|
40
|
+
provenance attestation; manual approval gate on the `production`
|
|
41
|
+
GitHub Environment guards the publish step.
|
|
29
42
|
|
|
30
43
|
Originally generated by
|
|
31
44
|
[`juvantlabs/juvant-tools`](https://github.com/juvantlabs/juvant-tools)
|
|
@@ -63,6 +76,8 @@ Optional:
|
|
|
63
76
|
|---|---|
|
|
64
77
|
| `MCP_SERVER_LOG_LEVEL` | Log level for diagnostics on stderr (default `info`). |
|
|
65
78
|
| `M365_DOWNLOAD_DIR` | Override the per-tenant sandbox directory used by `download_file`. Default: `$XDG_CACHE_HOME/m365-graph-mcp-server/<tenant-id>` or `~/.cache/m365-graph-mcp-server/<tenant-id>`. |
|
|
79
|
+
| `M365_TRANSCRIPT_MAX_BYTES` | Max raw VTT bytes read from Graph per `get_transcript` call. Defense-in-depth bound against the audit-S7 whole-file-buffering pattern. Default `10000000` (10 MB ≈ 8 h+ of speech). |
|
|
80
|
+
| `M365_TRANSCRIPT_MAX_CHARS` | Max parsed-text characters returned in a single `get_transcript` response. Long transcripts page via the tool's `offset` + `next_offset` fields. Default `200000` (~2 h of speech). |
|
|
66
81
|
|
|
67
82
|
> CI enforces that every variable documented in this section is actually
|
|
68
83
|
> read from `process.env.<NAME>` somewhere in `src/` — placeholder names
|
|
@@ -81,7 +96,7 @@ The Juvant OS adopter binds this server in `.juvant/config.json`:
|
|
|
81
96
|
{
|
|
82
97
|
"m365-graph": {
|
|
83
98
|
"provider": "microsoft",
|
|
84
|
-
"mcp_server": "npx @juvantlabs/m365-graph-mcp-server@0.
|
|
99
|
+
"mcp_server": "npx @juvantlabs/m365-graph-mcp-server@0.3.0",
|
|
85
100
|
"scope": "rw"
|
|
86
101
|
}
|
|
87
102
|
}
|
|
@@ -102,10 +117,10 @@ Step 8.5 cross-check semantics.
|
|
|
102
117
|
| `m365-graph:list_items` | Lists immediate children (files + folders) of a folder. Defaults to the drive root. | `drive_id?`, `item_id?`, `limit?` (1–100, default 50) | `{ count, items: [] }` with id / name / type / size / child_count / lastModified / webUrl. | `Files.Read` |
|
|
103
118
|
| `m365-graph:search_files` | Searches files by name and content within a drive. | `query` (required), `drive_id?`, `limit?` (1–50, default 20) | `{ count, results: [] }` with id / name / path / size / is_folder / lastModified / webUrl. | `Files.Read` |
|
|
104
119
|
| `m365-graph:download_file` | Downloads a file to a per-tenant local sandbox. Returns the local path; agent reads via a filesystem-aware tool. Streams, capped at 200 MB. | `item_id` (required), `drive_id?` | `{ local_path, size_bytes, name, content_type }` | `Files.Read` |
|
|
105
|
-
| `m365-graph:list_calendars` | Lists the user's calendars (primary + group / shared). | `limit?` (1–100, default 50) | `{ count, calendars: [] }` with id / name / color /
|
|
106
|
-
| `m365-graph:list_events` | Lists events in a date window. Recurrences are expanded — each occurrence is its own event. | `start` + `end` (ISO 8601, required), `calendar_id?`, `limit?` (1–200, default 100) | `{ window, count, events: [] }` with id / subject / start / end / location /
|
|
120
|
+
| `m365-graph:list_calendars` | Lists the user's calendars (primary + group / shared). | `limit?` (1–100, default 50) | `{ count, calendars: [] }` with id / name / color / owner_name / owner_email / is_default / can_edit / can_share. | `Calendars.Read` |
|
|
121
|
+
| `m365-graph:list_events` | Lists events in a date window. Recurrences are expanded — each occurrence is its own event. | `start` + `end` (ISO 8601, required), `calendar_id?`, `limit?` (1–200, default 100) | `{ calendar_id, window, count, events: [] }` with id / subject / body_preview / start / end / is_all_day / is_online_meeting / location / organizer_name / organizer_email / attendees / webLink. | `Calendars.Read` |
|
|
107
122
|
| `m365-graph:search_events` | Searches events by subject substring (Graph $search isn't supported on Events; subject-only via `contains()`). Returns recurrence series masters, not occurrences. | `query` (required), `limit?` (1–50, default 20) | `{ count, results: [] }` (same event shape). | `Calendars.Read` |
|
|
108
|
-
| `m365-graph:get_event` | Fetches full details for a single event — body (capped at 8000 chars), attendees with response statuses, location, recurrence rule. | `event_id` (required) | event summary + `body` / `body_content_type` / `body_truncated` / `recurrence`. | `Calendars.Read` |
|
|
123
|
+
| `m365-graph:get_event` | Fetches full details for a single event — body (capped at 8000 chars), attendees with response statuses, location, recurrence rule, online-meeting join URL when applicable. | `event_id` (required) | event summary + `body` / `body_content_type` / `body_truncated` / `recurrence` / `online_meeting_join_url`. | `Calendars.Read` |
|
|
109
124
|
| `m365-graph:upload_file` | Uploads a local file to a drive. Auto-routes between single PUT (≤ 4 MB) and resumable upload session (> 4 MB, 10 MB chunks). 200 MB hard cap. | `local_path` (required), `drive_id?`, `parent_item_id?`, `name?`, `conflict_behavior?` (`fail`/`replace`/`rename`, default `fail`) | `{ uploaded: { id, name, size, webUrl, upload_path } }` | `Files.ReadWrite` |
|
|
110
125
|
| `m365-graph:create_event` | Creates a new event on the user's primary calendar (or a specified calendar). Sends invitations to attendees by Graph default. | `subject` + `start` + `end` (required), `timezone?` (default UTC), `body?`, `body_content_type?` (`text`/`html`), `location?`, `attendees?`, `is_all_day?`, `calendar_id?` | `{ created: <event summary> }` | `Calendars.ReadWrite` |
|
|
111
126
|
| `m365-graph:update_event` | Updates an existing event. All fields except `event_id` are optional; only provided fields are PATCHed. **Attendees: full replacement, not merge** — pass the full intended list. | `event_id` (required), then any subset of `subject`/`start`+`end`+`timezone`/`body`+`body_content_type`/`location`/`attendees`/`is_all_day` | `{ updated: <event summary> }` | `Calendars.ReadWrite` |
|
|
@@ -116,7 +131,7 @@ Step 8.5 cross-check semantics.
|
|
|
116
131
|
| `m365-graph:decline_event` | **Two-phase**. Declines an event the user is invited to (as attendee — distinct from cancel which is for events the user organizes). Sends a decline RSVP unless `send_response: false`. | `event_id` (required), `comment?`, `send_response?` (default `true`), `confirmation_token?` | preview or `{ declined: { event_id, send_response } }` | `Calendars.ReadWrite` |
|
|
117
132
|
| `m365-graph:search_events_content` | Subject + **body** content search via the Microsoft Search API (POST `/search/query`). Distinct from `search_events` (subject-only via `$filter`). Returns recurrence series masters; for occurrences in a window use `list_events`. | `query` (required), `limit?` (1–50, default 25), `from?` (pagination offset, default 0) | `{ count, total, results: [<event summary>] }` | `Calendars.Read` |
|
|
118
133
|
| `m365-graph:list_meeting_transcripts` | List available transcripts for a Teams meeting identified by its calendar event ID. Transcripts are post-meeting only and require recording to have been enabled by the organizer. | `event_id` (required) | `{ event_id, meeting_id, count, transcripts: [{ id, meeting_id, created_at, end_at }] }` | `Calendars.Read`, `OnlineMeetings.Read` ¹, `OnlineMeetingTranscript.Read.All` ¹ |
|
|
119
|
-
| `m365-graph:get_transcript` | Fetch the text content of a Teams meeting transcript. VTT timing markers are stripped; returns clean readable text
|
|
134
|
+
| `m365-graph:get_transcript` | Fetch the text content of a Teams meeting transcript. VTT timing markers are stripped; returns clean readable text. Long transcripts page via `offset` + `next_offset`; per-call cap and upstream byte cap configurable via `M365_TRANSCRIPT_MAX_CHARS` / `M365_TRANSCRIPT_MAX_BYTES` (defaults 200 000 chars / 10 MB). | `meeting_id` + `transcript_id` (required, from `list_meeting_transcripts`); `offset?` (0..2_000_000_000), `max_chars?` | `{ meeting_id, transcript_id, offset, char_count, next_offset, total_char_count, truncated, vtt_truncated, transcript }` | `OnlineMeetingTranscript.Read.All` ¹ |
|
|
120
135
|
|
|
121
136
|
¹ **Admin consent required.** `OnlineMeetings.Read` and `OnlineMeetingTranscript.Read.All` must be granted in the Entra app registration under **API permissions → Add a permission → Microsoft Graph → Delegated → Grant admin consent**. Without admin consent these tools return 403 Forbidden.
|
|
122
137
|
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,29 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { ALL_TOOLS } from "./tools/index.js";
|
|
17
17
|
export declare const TENANT_ID_RE: RegExp;
|
|
18
|
+
/**
|
|
19
|
+
* Read the package version from `package.json` at runtime, so the MCP
|
|
20
|
+
* server advertises the real shipped version instead of a hardcoded
|
|
21
|
+
* literal that silently drifts away from `package.json` on each bump.
|
|
22
|
+
*
|
|
23
|
+
* Resolution strategy: use `createRequire(import.meta.url)` against
|
|
24
|
+
* `../package.json`. This relies on `package.json` sitting one level
|
|
25
|
+
* above the runtime file, which holds in BOTH layouts the build emits:
|
|
26
|
+
*
|
|
27
|
+
* - Built: dist/index.js → ../package.json = <pkg-root>/package.json
|
|
28
|
+
* - Dev: src/index.ts → ../package.json = <pkg-root>/package.json
|
|
29
|
+
* - Tests: src/index.ts → ../package.json = <pkg-root>/package.json
|
|
30
|
+
*
|
|
31
|
+
* `createRequire` is preferred over a bare JSON import-assertion here
|
|
32
|
+
* because `tsconfig.json` has `rootDir: "src"`, which forbids importing
|
|
33
|
+
* `../package.json` from `src/index.ts`. Going through `createRequire`
|
|
34
|
+
* sidesteps the rootDir constraint without copying or generating files
|
|
35
|
+
* at build time, and stays dependency-free.
|
|
36
|
+
*
|
|
37
|
+
* Exported for unit testing.
|
|
38
|
+
*/
|
|
39
|
+
export declare function readPackageVersion(): string;
|
|
40
|
+
export declare const PACKAGE_VERSION: string;
|
|
18
41
|
/**
|
|
19
42
|
* Validate the env vars the server needs at startup. Throws with a
|
|
20
43
|
* specific message on failure; main() catches + exits with stderr +
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;GAaG;AAgBH,OAAO,EAAE,SAAS,EAAmB,MAAM,kBAAkB,CAAC;AAE9D,eAAO,MAAM,YAAY,QAC0E,CAAC;AAEpG;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAI3C;AAED,eAAO,MAAM,eAAe,QAAuB,CAAC;AAEpD;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,IAAI,CAgBnE;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,OAAO,mCAAmC,EAAE,MAAM,EACzD,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,EAC5D,OAAO,EAAE;IAAE,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GACzD,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAmBhF;AAiCD;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE;IACvD,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhB"}
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* rules enforce this in `juvantlabs/*-mcp-server` repos.
|
|
15
15
|
*/
|
|
16
16
|
import { realpathSync } from "node:fs";
|
|
17
|
+
import { createRequire } from "node:module";
|
|
17
18
|
import { pathToFileURL } from "node:url";
|
|
18
19
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
19
20
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -23,6 +24,33 @@ import { runSetup } from "./auth/setup.js";
|
|
|
23
24
|
import { makeGraphClient } from "./client/graph.js";
|
|
24
25
|
import { ALL_TOOLS, buildHandlerMap } from "./tools/index.js";
|
|
25
26
|
export const TENANT_ID_RE = /^(common|organizations|consumers|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/;
|
|
27
|
+
/**
|
|
28
|
+
* Read the package version from `package.json` at runtime, so the MCP
|
|
29
|
+
* server advertises the real shipped version instead of a hardcoded
|
|
30
|
+
* literal that silently drifts away from `package.json` on each bump.
|
|
31
|
+
*
|
|
32
|
+
* Resolution strategy: use `createRequire(import.meta.url)` against
|
|
33
|
+
* `../package.json`. This relies on `package.json` sitting one level
|
|
34
|
+
* above the runtime file, which holds in BOTH layouts the build emits:
|
|
35
|
+
*
|
|
36
|
+
* - Built: dist/index.js → ../package.json = <pkg-root>/package.json
|
|
37
|
+
* - Dev: src/index.ts → ../package.json = <pkg-root>/package.json
|
|
38
|
+
* - Tests: src/index.ts → ../package.json = <pkg-root>/package.json
|
|
39
|
+
*
|
|
40
|
+
* `createRequire` is preferred over a bare JSON import-assertion here
|
|
41
|
+
* because `tsconfig.json` has `rootDir: "src"`, which forbids importing
|
|
42
|
+
* `../package.json` from `src/index.ts`. Going through `createRequire`
|
|
43
|
+
* sidesteps the rootDir constraint without copying or generating files
|
|
44
|
+
* at build time, and stays dependency-free.
|
|
45
|
+
*
|
|
46
|
+
* Exported for unit testing.
|
|
47
|
+
*/
|
|
48
|
+
export function readPackageVersion() {
|
|
49
|
+
const require = createRequire(import.meta.url);
|
|
50
|
+
const pkg = require("../package.json");
|
|
51
|
+
return pkg.version;
|
|
52
|
+
}
|
|
53
|
+
export const PACKAGE_VERSION = readPackageVersion();
|
|
26
54
|
/**
|
|
27
55
|
* Validate the env vars the server needs at startup. Throws with a
|
|
28
56
|
* specific message on failure; main() catches + exits with stderr +
|
|
@@ -79,7 +107,7 @@ async function runMcpServer() {
|
|
|
79
107
|
const logLevel = process.env.MCP_SERVER_LOG_LEVEL ?? "info";
|
|
80
108
|
const server = new Server({
|
|
81
109
|
name: "@juvantlabs/m365-graph-mcp-server",
|
|
82
|
-
version:
|
|
110
|
+
version: PACKAGE_VERSION,
|
|
83
111
|
}, {
|
|
84
112
|
capabilities: { tools: {} },
|
|
85
113
|
});
|
|
@@ -89,7 +117,7 @@ async function runMcpServer() {
|
|
|
89
117
|
server.setRequestHandler(CallToolRequestSchema, async (request) => dispatchToolCall(graph, handlers, request));
|
|
90
118
|
const transport = new StdioServerTransport();
|
|
91
119
|
await server.connect(transport);
|
|
92
|
-
console.error(`[m365-graph-mcp-server] running on stdio (log level: ${logLevel}, tenant: ${process.env.M365_TENANT_ID}, tools: ${ALL_TOOLS.length})`);
|
|
120
|
+
console.error(`[m365-graph-mcp-server] v${PACKAGE_VERSION} running on stdio (log level: ${logLevel}, tenant: ${process.env.M365_TENANT_ID}, tools: ${ALL_TOOLS.length})`);
|
|
93
121
|
}
|
|
94
122
|
/**
|
|
95
123
|
* Subcommand dispatcher. Exported so tests can verify routing without
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,19 +3,35 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Fetch the text content of a Teams meeting transcript. The Graph API
|
|
5
5
|
* returns VTT (WebVTT subtitle format); this tool strips the timing
|
|
6
|
-
* markers and returns clean readable text
|
|
6
|
+
* markers and returns clean readable text.
|
|
7
7
|
*
|
|
8
8
|
* Required Graph scope: OnlineMeetingTranscript.Read.All (delegated,
|
|
9
9
|
* admin consent required).
|
|
10
10
|
*
|
|
11
|
+
* Caps (defense-in-depth bounds, configurable via env):
|
|
12
|
+
* - Raw VTT stream: M365_TRANSCRIPT_MAX_BYTES (default 10 000 000 / 10 MB)
|
|
13
|
+
* - Parsed-text page: M365_TRANSCRIPT_MAX_CHARS (default 200 000 chars)
|
|
14
|
+
*
|
|
15
|
+
* Paging: when a transcript exceeds the per-call char cap, callers
|
|
16
|
+
* iterate by passing the returned `next_offset` back as `offset` on
|
|
17
|
+
* the next call. The Graph content endpoint returns the whole VTT in
|
|
18
|
+
* one blob — there is no server-side paging primitive — so paging is
|
|
19
|
+
* implemented client-side by slicing the parsed text.
|
|
20
|
+
*
|
|
11
21
|
* Input:
|
|
12
|
-
* meeting_id (string, required)
|
|
22
|
+
* meeting_id (string, required) — onlineMeeting id from
|
|
13
23
|
* list_meeting_transcripts
|
|
14
|
-
* transcript_id (string, required)
|
|
24
|
+
* transcript_id (string, required) — transcript id from
|
|
15
25
|
* list_meeting_transcripts
|
|
26
|
+
* offset (integer, optional, 0..2_000_000_000, default 0)
|
|
27
|
+
* — character offset into the parsed transcript text
|
|
28
|
+
* max_chars (integer, optional, 1..M365_TRANSCRIPT_MAX_CHARS,
|
|
29
|
+
* default M365_TRANSCRIPT_MAX_CHARS) — max chars to
|
|
30
|
+
* return in this response
|
|
16
31
|
*
|
|
17
|
-
* Output: plain
|
|
18
|
-
* metadata (meeting_id, transcript_id, char_count,
|
|
32
|
+
* Output: plain-text transcript slice (VTT markup removed) plus
|
|
33
|
+
* metadata (meeting_id, transcript_id, offset, char_count,
|
|
34
|
+
* next_offset, total_char_count, truncated, vtt_truncated).
|
|
19
35
|
*/
|
|
20
36
|
import type { Tool } from "../types/tool.js";
|
|
21
37
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get_transcript.d.ts","sourceRoot":"","sources":["../../src/tools/get_transcript.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"get_transcript.d.ts","sourceRoot":"","sources":["../../src/tools/get_transcript.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAKH,OAAO,KAAK,EAAE,IAAI,EAA6C,MAAM,kBAAkB,CAAC;AAmExF;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAkB5C;AA6GD,eAAO,MAAM,iBAAiB,EAAE,IAAgD,CAAC"}
|
|
@@ -3,26 +3,66 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Fetch the text content of a Teams meeting transcript. The Graph API
|
|
5
5
|
* returns VTT (WebVTT subtitle format); this tool strips the timing
|
|
6
|
-
* markers and returns clean readable text
|
|
6
|
+
* markers and returns clean readable text.
|
|
7
7
|
*
|
|
8
8
|
* Required Graph scope: OnlineMeetingTranscript.Read.All (delegated,
|
|
9
9
|
* admin consent required).
|
|
10
10
|
*
|
|
11
|
+
* Caps (defense-in-depth bounds, configurable via env):
|
|
12
|
+
* - Raw VTT stream: M365_TRANSCRIPT_MAX_BYTES (default 10 000 000 / 10 MB)
|
|
13
|
+
* - Parsed-text page: M365_TRANSCRIPT_MAX_CHARS (default 200 000 chars)
|
|
14
|
+
*
|
|
15
|
+
* Paging: when a transcript exceeds the per-call char cap, callers
|
|
16
|
+
* iterate by passing the returned `next_offset` back as `offset` on
|
|
17
|
+
* the next call. The Graph content endpoint returns the whole VTT in
|
|
18
|
+
* one blob — there is no server-side paging primitive — so paging is
|
|
19
|
+
* implemented client-side by slicing the parsed text.
|
|
20
|
+
*
|
|
11
21
|
* Input:
|
|
12
|
-
* meeting_id (string, required)
|
|
22
|
+
* meeting_id (string, required) — onlineMeeting id from
|
|
13
23
|
* list_meeting_transcripts
|
|
14
|
-
* transcript_id (string, required)
|
|
24
|
+
* transcript_id (string, required) — transcript id from
|
|
15
25
|
* list_meeting_transcripts
|
|
26
|
+
* offset (integer, optional, 0..2_000_000_000, default 0)
|
|
27
|
+
* — character offset into the parsed transcript text
|
|
28
|
+
* max_chars (integer, optional, 1..M365_TRANSCRIPT_MAX_CHARS,
|
|
29
|
+
* default M365_TRANSCRIPT_MAX_CHARS) — max chars to
|
|
30
|
+
* return in this response
|
|
16
31
|
*
|
|
17
|
-
* Output: plain
|
|
18
|
-
* metadata (meeting_id, transcript_id, char_count,
|
|
32
|
+
* Output: plain-text transcript slice (VTT markup removed) plus
|
|
33
|
+
* metadata (meeting_id, transcript_id, offset, char_count,
|
|
34
|
+
* next_offset, total_char_count, truncated, vtt_truncated).
|
|
19
35
|
*/
|
|
20
|
-
import { validateRequiredString } from "../types/validators.js";
|
|
21
|
-
|
|
36
|
+
import { validateRequiredString, validateOptionalInteger } from "../types/validators.js";
|
|
37
|
+
// Defaults are generous enough for multi-hour meetings but still bounded
|
|
38
|
+
// against the audit-S7 whole-file-buffering anti-pattern. Both are
|
|
39
|
+
// overridable via env vars at process start (per-tenant subprocess, so
|
|
40
|
+
// the env is the right knob — no per-call override of the absolute cap).
|
|
41
|
+
const DEFAULT_MAX_VTT_BYTES = 10_000_000; // 10 MB raw VTT (~8h+ of speech)
|
|
42
|
+
const DEFAULT_CONTENT_CHAR_CAP = 200_000; // 200k chars parsed (~2h of speech)
|
|
43
|
+
function parsePositiveInt(raw, fallback) {
|
|
44
|
+
if (raw === undefined || raw === "")
|
|
45
|
+
return fallback;
|
|
46
|
+
const n = Number(raw);
|
|
47
|
+
if (!Number.isInteger(n) || n <= 0)
|
|
48
|
+
return fallback;
|
|
49
|
+
return n;
|
|
50
|
+
}
|
|
51
|
+
// Env reads are intentionally written as literal `process.env.<NAME>` (not
|
|
52
|
+
// via a dynamic-key helper) so the README env-var-accuracy CI gate
|
|
53
|
+
// (handbook anti-pattern S2) can grep them and confirm the documented names
|
|
54
|
+
// are actually wired. See .github/workflows/ci.yml § "README env-var accuracy".
|
|
55
|
+
function getMaxVttBytes() {
|
|
56
|
+
return parsePositiveInt(process.env.M365_TRANSCRIPT_MAX_BYTES, DEFAULT_MAX_VTT_BYTES);
|
|
57
|
+
}
|
|
58
|
+
function getMaxChars() {
|
|
59
|
+
return parsePositiveInt(process.env.M365_TRANSCRIPT_MAX_CHARS, DEFAULT_CONTENT_CHAR_CAP);
|
|
60
|
+
}
|
|
22
61
|
const definition = {
|
|
23
62
|
name: "m365-graph:get_transcript",
|
|
24
63
|
description: "Fetch the text content of a Teams meeting transcript. VTT timing markers " +
|
|
25
|
-
"are stripped; returns clean readable text
|
|
64
|
+
"are stripped; returns clean readable text. Long transcripts can be paged " +
|
|
65
|
+
"via the offset + max_chars inputs (see next_offset in the response). " +
|
|
26
66
|
"Use list_meeting_transcripts to get the meeting_id and transcript_id. Read-only.",
|
|
27
67
|
inputSchema: {
|
|
28
68
|
type: "object",
|
|
@@ -35,6 +75,20 @@ const definition = {
|
|
|
35
75
|
type: "string",
|
|
36
76
|
description: "Transcript ID from list_meeting_transcripts.",
|
|
37
77
|
},
|
|
78
|
+
offset: {
|
|
79
|
+
type: "integer",
|
|
80
|
+
minimum: 0,
|
|
81
|
+
maximum: 2_000_000_000,
|
|
82
|
+
description: "Character offset into the parsed transcript text (default 0). " +
|
|
83
|
+
"Pass the previous response's next_offset to fetch the next page.",
|
|
84
|
+
},
|
|
85
|
+
max_chars: {
|
|
86
|
+
type: "integer",
|
|
87
|
+
minimum: 1,
|
|
88
|
+
description: "Maximum number of transcript characters to return in this response. " +
|
|
89
|
+
"Defaults to and is capped by M365_TRANSCRIPT_MAX_CHARS " +
|
|
90
|
+
"(default 200 000).",
|
|
91
|
+
},
|
|
38
92
|
},
|
|
39
93
|
required: ["meeting_id", "transcript_id"],
|
|
40
94
|
},
|
|
@@ -79,6 +133,18 @@ export function parseVtt(vtt) {
|
|
|
79
133
|
const handler = async (graph, args) => {
|
|
80
134
|
const meetingId = validateRequiredString(args.meeting_id, "meeting_id");
|
|
81
135
|
const transcriptId = validateRequiredString(args.transcript_id, "transcript_id");
|
|
136
|
+
const maxVttBytes = getMaxVttBytes();
|
|
137
|
+
const maxChars = getMaxChars();
|
|
138
|
+
const offset = validateOptionalInteger(args.offset, "offset", {
|
|
139
|
+
min: 0,
|
|
140
|
+
max: 2_000_000_000,
|
|
141
|
+
default: 0,
|
|
142
|
+
});
|
|
143
|
+
const pageChars = validateOptionalInteger(args.max_chars, "max_chars", {
|
|
144
|
+
min: 1,
|
|
145
|
+
max: maxChars,
|
|
146
|
+
default: maxChars,
|
|
147
|
+
});
|
|
82
148
|
const endpoint = `/me/onlineMeetings/${encodeURIComponent(meetingId)}` +
|
|
83
149
|
`/transcripts/${encodeURIComponent(transcriptId)}/content`;
|
|
84
150
|
const rawResponse = await graph
|
|
@@ -86,15 +152,17 @@ const handler = async (graph, args) => {
|
|
|
86
152
|
.query({ $format: "text/vtt" })
|
|
87
153
|
.get();
|
|
88
154
|
// Graph SDK may return a ReadableStream for binary/text content types.
|
|
89
|
-
// Cap raw VTT at
|
|
90
|
-
|
|
155
|
+
// Cap raw VTT at maxVttBytes before parsing (audit S7: whole-file
|
|
156
|
+
// buffering bound). We flag `vtt_truncated` so callers know the tail
|
|
157
|
+
// of the *upstream* VTT was not read (distinct from output paging).
|
|
91
158
|
let vttString;
|
|
159
|
+
let vttTruncated = false;
|
|
92
160
|
if (rawResponse instanceof ReadableStream) {
|
|
93
161
|
const reader = rawResponse.getReader();
|
|
94
162
|
const chunks = [];
|
|
95
163
|
let totalBytes = 0;
|
|
96
164
|
let done = false;
|
|
97
|
-
while (!done && totalBytes <
|
|
165
|
+
while (!done && totalBytes < maxVttBytes) {
|
|
98
166
|
const { done: d, value } = await reader.read();
|
|
99
167
|
done = d;
|
|
100
168
|
if (value) {
|
|
@@ -103,16 +171,38 @@ const handler = async (graph, args) => {
|
|
|
103
171
|
totalBytes += buf.byteLength;
|
|
104
172
|
}
|
|
105
173
|
}
|
|
106
|
-
if (!done)
|
|
174
|
+
if (!done) {
|
|
175
|
+
vttTruncated = true;
|
|
107
176
|
reader.cancel().catch(() => undefined);
|
|
108
|
-
|
|
177
|
+
}
|
|
178
|
+
vttString = Buffer.concat(chunks).toString("utf-8");
|
|
179
|
+
if (vttString.length > maxVttBytes) {
|
|
180
|
+
vttString = vttString.slice(0, maxVttBytes);
|
|
181
|
+
vttTruncated = true;
|
|
182
|
+
}
|
|
109
183
|
}
|
|
110
184
|
else {
|
|
111
|
-
|
|
185
|
+
const s = String(rawResponse ?? "");
|
|
186
|
+
if (s.length > maxVttBytes) {
|
|
187
|
+
vttString = s.slice(0, maxVttBytes);
|
|
188
|
+
vttTruncated = true;
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
vttString = s;
|
|
192
|
+
}
|
|
112
193
|
}
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
|
|
194
|
+
const fullText = parseVtt(vttString);
|
|
195
|
+
const totalChars = fullText.length;
|
|
196
|
+
// Slice the requested page out of the parsed text.
|
|
197
|
+
const sliceStart = Math.min(offset, totalChars);
|
|
198
|
+
const sliceEnd = Math.min(sliceStart + pageChars, totalChars);
|
|
199
|
+
const content = fullText.slice(sliceStart, sliceEnd);
|
|
200
|
+
const nextOffset = sliceEnd < totalChars ? sliceEnd : null;
|
|
201
|
+
// `truncated` retained for backward-compat with v0.2.x callers: true when
|
|
202
|
+
// the returned slice does not reach the end of the (in-memory) parsed
|
|
203
|
+
// text. Combined with `vtt_truncated`, callers can distinguish a
|
|
204
|
+
// page-of-many response from a hard upstream-byte loss.
|
|
205
|
+
const truncated = nextOffset !== null;
|
|
116
206
|
return {
|
|
117
207
|
content: [
|
|
118
208
|
{
|
|
@@ -120,8 +210,12 @@ const handler = async (graph, args) => {
|
|
|
120
210
|
text: JSON.stringify({
|
|
121
211
|
meeting_id: meetingId,
|
|
122
212
|
transcript_id: transcriptId,
|
|
213
|
+
offset: sliceStart,
|
|
123
214
|
char_count: content.length,
|
|
215
|
+
next_offset: nextOffset,
|
|
216
|
+
total_char_count: totalChars,
|
|
124
217
|
truncated,
|
|
218
|
+
vtt_truncated: vttTruncated,
|
|
125
219
|
transcript: content,
|
|
126
220
|
}, null, 2),
|
|
127
221
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get_transcript.js","sourceRoot":"","sources":["../../src/tools/get_transcript.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"get_transcript.js","sourceRoot":"","sources":["../../src/tools/get_transcript.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED