From 96daa46a1d5e90796e22f9165545b9d0702a1e1e Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Wed, 14 Jan 2026 14:25:24 +0000 Subject: [PATCH] examples: add PDF viewer w/ chunked data loading, full-screen, model context updates, private tool (#267) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(pdf-server): Interactive PDF viewer example PDF viewer with PDF.js featuring: - Chunked binary loading with progress bar - Text extraction for AI context - arXiv paper support (fetch by ID) - Page navigation with keyboard shortcuts - Zoom controls (including Ctrl+0 reset) - Fullscreen mode support - Horizontal swipe for page changes (disabled when zoomed) - Page persistence in localStorage - Text selection via PDF.js TextLayer - Clickable title link to source URL - Rounded corners and subtle border styling * chore: Add pdf-server to screenshot generation list * refactor(pdf-server): Simplify and generalize PDF loading - Accept any HTTP(s) URLs instead of ArXiv-only - Use HTTP Range requests for chunked binary loading - Remove ArXiv-specific code (arxiv.ts, metadata fetching) - Remove CLAUDE.md index generation - Flatten hierarchical folder structure to simple entries list - Remove dead code: getPdfSummary, httpFileSizes - Simplify base64 encoding using Buffer - Simplify chunk extraction using slice() - Consolidate DEFAULT_PDF_URL constant The server now works with any PDF URL, not just arXiv papers. HTTP Range requests stream chunks on-demand when supported. * feat(pdf-server): Include title and selection in model context - Add pdfTitle to updateModelContext structuredContent - Include selection position (text, start, end) when text is selected - Add debounced selectionchange listener to update context on selection * fix(pdf-server): Restore default URL in view_pdf schema The UI needs the default value in the schema to show it properly. * refactor(pdf-server): Further simplifications - Remove hard-coded test paths from main() - Remove unused resources: pdfs://metadata/{pdfId}, pdfs://content/{pdfId} - Remove unused metadata fields: subject, creator, producer, creationDate, modDate - Remove unused entry fields: relativePath, estimatedTextSize - Remove filterEntriesByFolder and folder filter from list_pdfs - Remove redundant output schema validation (trust typed returns) - Simplify scanDirectory and createLocalEntry signatures Total: 1836 → 1666 lines (-170 lines, -9%) * refactor(pdf-server): Major simplification for didactic focus Simplified the example to focus on key MCP Apps SDK patterns: - Chunked data through size-limited tool calls - Model context updates (page text + selection) - Display modes (fullscreen vs inline) - External links (openLink) Changes: - Remove local file support (HTTP URLs only) - Restrict dynamic URLs to arxiv.org for security - Simplify types: url instead of sourcePath/sourceType - Simplify indexer: 168 → 44 lines - Simplify loader: 318 → 171 lines - Simplify server: 337 → 233 lines - Fix selection text normalization - Rewrite README with didactic focus Total: 1836 → 1236 lines (-33%) * feat(pdf-server): Add file:// URL support for local files - Local paths are converted to file:// URLs on startup - file:// URLs must be in the initial list (strict validation) - Dynamic URLs still restricted to arxiv.org only - Updated README with local file examples * fix(pdf-server): Improve selection detection with logging - Add logging to selectionchange handler to verify it fires - Add fallback matching without spaces (TextLayer spans may lack spaces) - Log selection detection success/failure for debugging The issue: PDF.js TextLayer renders text as positioned spans without space characters between them. When selecting across spans: - pageText has spaces (items joined with ' ') - sel.toString() may not have spaces - indexOf fails to match The fix tries exact match first, then falls back to spaceless matching. * feat(pdf-server): Format model context as markdown with front matter Model context now looks like: ```markdown --- url: https://arxiv.org/pdf/... page: 5/144 --- Page text with selected text inline. ``` This is cleaner for the model to parse and includes the source URL. * refactor(pdf-server): Extract smart truncation helpers Added two well-designed helpers: formatPageContent(text, maxLength, selection?) - Centers truncation window around selection if present - Adds markers at elision points - Wraps selection in tags - Allocates 60% context before, 40% after for readability findSelectionInText(pageText, selectedText) - Tries exact match first - Falls back to spaceless match for TextLayer quirks - Returns { start, end } or undefined Example output with selection: ``` ...context before... selected text ...context after... ``` * fix(pdf-server): Truncate inside selection tags when selection too long When selection is too large for the budget: start...end This keeps the selection structure intact while showing beginning and end. * refactor(pdf-server): Remove unused read_pdf_text, use Attention paper as default - Remove read_pdf_text tool (viewer extracts text client-side with pdfjs) - Remove PdfTextChunk and ReadPdfTextInput types - Remove loadPdfTextChunk from pdf-loader - Change default PDF to 'Attention Is All You Need' (1706.03762) - Update README with modest language * refactor(pdf-server): Simplify to use URL as ID, rename view_pdf to display_pdf Major simplifications: - Use URL directly as identifier (no hashing) - Remove displayName - show elided URL with full URL as tooltip - Rename view_pdf to display_pdf with better description - Update all references from pdfId to url - Simplify storage key and model context The tool description now explains it displays an interactive viewer in the chat. * feat(pdf-server): Normalize arxiv URLs to PDF format arxiv.org/abs/... -> arxiv.org/pdf/... Applied both at startup and when loading dynamic URLs. * docs(pdf-server): Add prompt engineering to display_pdf description * fix(pdf-server): Sharp rendering on retina displays Account for devicePixelRatio when rendering canvas: - Scale canvas dimensions by dpr - Scale context by dpr - Keep CSS size at logical pixels * fix(pdf-server): Normalize arxiv URLs in read_pdf_bytes too * add to e2e spec * add to e2e spec * add to e2e spec * add to e2e spec * regen * chore: regenerate package-lock.json and fix hono vulnerability * docs: add pdf-server screenshot to READMEs * regen * ci: add missing examples to pkg-pr-new publish * ci: add pdf-server to npm publish examples * Update README.md * pdf-server: improve tool response text for better model context * revert unrelated screenshot changes * pdf-server: dynamically add arxiv URLs in read_pdf_bytes Fixes 'PDF not found' error when server restarts between display_pdf (which adds the entry) and read_pdf_bytes (which previously only looked up existing entries). Now read_pdf_bytes mirrors display_pdf's logic and dynamically adds arxiv URLs to the index. --- .github/workflows/npm-publish.yml | 1 + .github/workflows/publish.yml | 6 + README.md | 2 + examples/pdf-server/README.md | 137 +++ examples/pdf-server/grid-cell.png | Bin 0 -> 30605 bytes examples/pdf-server/mcp-app.html | 78 ++ examples/pdf-server/package.json | 45 + examples/pdf-server/screenshot.png | Bin 0 -> 68443 bytes examples/pdf-server/server-utils.ts | 72 ++ examples/pdf-server/server.ts | 250 ++++++ examples/pdf-server/src/global.css | 12 + examples/pdf-server/src/mcp-app.css | 292 +++++++ examples/pdf-server/src/mcp-app.ts | 795 ++++++++++++++++++ examples/pdf-server/src/pdf-indexer.ts | 57 ++ examples/pdf-server/src/pdf-loader.ts | 136 +++ examples/pdf-server/src/types.ts | 51 ++ examples/pdf-server/tsconfig.json | 19 + examples/pdf-server/vite.config.ts | 27 + package-lock.json | 400 ++++++++- package.json | 4 +- tests/e2e/generate-grid-screenshots.spec.ts | 2 + tests/e2e/servers.spec.ts | 1 + .../servers.spec.ts-snapshots/pdf-server.png | Bin 0 -> 79541 bytes 23 files changed, 2358 insertions(+), 29 deletions(-) create mode 100644 examples/pdf-server/README.md create mode 100644 examples/pdf-server/grid-cell.png create mode 100644 examples/pdf-server/mcp-app.html create mode 100644 examples/pdf-server/package.json create mode 100644 examples/pdf-server/screenshot.png create mode 100644 examples/pdf-server/server-utils.ts create mode 100644 examples/pdf-server/server.ts create mode 100644 examples/pdf-server/src/global.css create mode 100644 examples/pdf-server/src/mcp-app.css create mode 100644 examples/pdf-server/src/mcp-app.ts create mode 100644 examples/pdf-server/src/pdf-indexer.ts create mode 100644 examples/pdf-server/src/pdf-loader.ts create mode 100644 examples/pdf-server/src/types.ts create mode 100644 examples/pdf-server/tsconfig.json create mode 100644 examples/pdf-server/vite.config.ts create mode 100644 tests/e2e/servers.spec.ts-snapshots/pdf-server.png diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 5c4f770..3b237cb 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -108,6 +108,7 @@ jobs: - cohort-heatmap-server - customer-segmentation-server - map-server + - pdf-server - scenario-modeler-server - shadertoy-server - sheet-music-server diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4174e6f..283577b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -29,7 +29,13 @@ jobs: ./examples/budget-allocator-server \ ./examples/cohort-heatmap-server \ ./examples/customer-segmentation-server \ + ./examples/map-server \ + ./examples/pdf-server \ ./examples/scenario-modeler-server \ + ./examples/shadertoy-server \ + ./examples/sheet-music-server \ ./examples/system-monitor-server \ ./examples/threejs-server \ + ./examples/transcript-server \ + ./examples/video-resource-server \ ./examples/wiki-explorer-server diff --git a/README.md b/README.md index 55f6904..d9a2406 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,8 @@ Or edit your `package.json` manually: | [**Scenario Modeler**](examples/scenario-modeler-server) | [**Budget Allocator**](examples/budget-allocator-server) | [**Customer Segmentation**](examples/customer-segmentation-server) | | [![System Monitor](examples/system-monitor-server/grid-cell.png "Real-time OS metrics")](examples/system-monitor-server) | [![Transcript](examples/transcript-server/grid-cell.png "Live speech transcription")](examples/transcript-server) | [![Video Resource](examples/video-resource-server/grid-cell.png "Binary video via MCP resources")](examples/video-resource-server) | | [**System Monitor**](examples/system-monitor-server) | [**Transcript**](examples/transcript-server) | [**Video Resource**](examples/video-resource-server) | +| [![PDF Server](examples/pdf-server/grid-cell.png "Interactive PDF viewer with chunked loading")](examples/pdf-server) | | | +| [**PDF Server**](examples/pdf-server) | | | ### Starter Templates diff --git a/examples/pdf-server/README.md b/examples/pdf-server/README.md new file mode 100644 index 0000000..48964a8 --- /dev/null +++ b/examples/pdf-server/README.md @@ -0,0 +1,137 @@ +# PDF Server + +![Screenshot](screenshot.png) + +A simple interactive PDF viewer that uses [PDF.js](https://mozilla.github.io/pdf.js/). Launch it w/ a few PDF files and/or URLs as CLI args (+ support loading any additional pdf from arxiv.org). + +## What This Example Demonstrates + +### 1. Chunked Data Through Size-Limited Tool Calls + +On some host platforms, tool calls have size limits, so large PDFs cannot be sent in a single response. This example shows a possible workaround: + +**Server side** (`pdf-loader.ts`): + +```typescript +// Returns chunks with pagination metadata +async function loadPdfBytesChunk(entry, offset, byteCount) { + return { + bytes: base64Chunk, + offset, + byteCount, + totalBytes, + hasMore: offset + byteCount < totalBytes, + }; +} +``` + +**Client side** (`mcp-app.ts`): + +```typescript +// Load in chunks with progress +while (hasMore) { + const chunk = await app.callServerTool("read_pdf_bytes", { pdfId, offset }); + chunks.push(base64ToBytes(chunk.bytes)); + offset += chunk.byteCount; + hasMore = chunk.hasMore; + updateProgress(offset, chunk.totalBytes); +} +``` + +### 2. Model Context Updates + +The viewer keeps the model informed about what the user is seeing: + +```typescript +app.updateModelContext({ + structuredContent: { + title: pdfTitle, + currentPage, + totalPages, + pageText: pageText.slice(0, 5000), + selection: selectedText ? { text, start, end } : undefined, + }, +}); +``` + +This enables the model to answer questions about the current page or selected text. + +### 3. Display Modes: Fullscreen vs Inline + +- **Inline mode**: App requests height changes to fit content +- **Fullscreen mode**: App fills the screen with internal scrolling + +```typescript +// Request fullscreen +app.requestDisplayMode({ mode: "fullscreen" }); + +// Listen for mode changes +app.ondisplaymodechange = (mode) => { + if (mode === "fullscreen") enableScrolling(); + else disableScrolling(); +}; +``` + +### 4. External Links (openLink) + +The viewer demonstrates opening external links (e.g., to the original arxiv page): + +```typescript +titleEl.onclick = () => app.openLink(sourceUrl); +``` + +## Usage + +```bash +# Default: loads a sample arxiv paper +bun examples/pdf-server/server.ts + +# Load local files (converted to file:// URLs) +bun examples/pdf-server/server.ts ./docs/paper.pdf /path/to/thesis.pdf + +# Load from URLs +bun examples/pdf-server/server.ts https://arxiv.org/pdf/2401.00001.pdf + +# Mix local and remote +bun examples/pdf-server/server.ts ./local.pdf https://arxiv.org/pdf/2401.00001.pdf + +# stdio mode for MCP clients +bun examples/pdf-server/server.ts --stdio ./papers/ +``` + +**Security**: Dynamic URLs (via `view_pdf` tool) are restricted to arxiv.org. Local files must be in the initial list. + +## Tools + +| Tool | Visibility | Purpose | +| ---------------- | ---------- | ---------------------------------- | +| `list_pdfs` | Model | List indexed PDFs | +| `display_pdf` | Model + UI | Display interactive viewer in chat | +| `read_pdf_bytes` | App only | Chunked binary loading | + +## Architecture + +``` +server.ts # MCP server (233 lines) +├── src/ +│ ├── types.ts # Zod schemas (75 lines) +│ ├── pdf-indexer.ts # URL-based indexing (44 lines) +│ ├── pdf-loader.ts # Chunked loading (171 lines) +│ └── mcp-app.ts # Interactive viewer UI +``` + +## Key Patterns Shown + +| Pattern | Implementation | +| ----------------- | ---------------------------------------- | +| App-only tools | `_meta: { ui: { visibility: ["app"] } }` | +| Chunked responses | `hasMore` + `offset` pagination | +| Model context | `app.updateModelContext()` | +| Display modes | `app.requestDisplayMode()` | +| External links | `app.openLink()` | +| Size negotiation | `app.sendSizeChanged()` | + +## Dependencies + +- `pdfjs-dist`: PDF rendering +- `@modelcontextprotocol/ext-apps`: MCP Apps SDK diff --git a/examples/pdf-server/grid-cell.png b/examples/pdf-server/grid-cell.png new file mode 100644 index 0000000000000000000000000000000000000000..5acd222724ad01d430cf5eec34fc050f816e9c08 GIT binary patch literal 30605 zcmdpeWmr~Gv?g6r0@5KNAxMXGsEB}ofOLbjl$1#G(Jd`4B@H6dT_P6Hv3Gwp!+ePP1ZatNi<)@6a!p6Y~%L|gt zp`f4`**f9~l%Y@ROKOycpHjs1t>6Uu-;3-0qVwSF!=r*xtKHiRiz_u6#V_wC#GQl) zYdi`!SPJmdDQ(tFNswPr6j^t!M|bv-xJ_EB!5-JE1&jE-g&WYuEg+C*K$xGOf9N58 zW^U}{q{hBRkHb=XHWzv$rN=Z}Bc7;ePPxS%YpK6`b#|+xZag$p|s)b{U^e1ZBZAQu3O95?T zWAW^l1qZhrGpQGe=%LK3`3 zr5XxM@6ji7R10%eH`42G%4KD8R96ok?j-aQDgRJVuan6(lC$(Ml#FbUkci{UFKK3Z zMo#qW{dQZNgOig}TeL!+>Y-6BX*ydA+o-v>Sw%9_*w&F-(W|!a0#%{tTt8N&+v^^u zot=LBhUIFF9Je6G@@-^fM3~gQ%6j}TO8d3jJ$g0Og3$qS15*0zrQ(T_(GBu<9nld# zPaX4RXvMJCLc2~7`5$;M7D|u~Mz&*x<=+RIC=u}ztp|%z-hWFo%0hzsMp8mdOw8vk zE=Gq~I<6Y+TZtR5ZSetTbEd~_lvGrI{`|qj#H2`aFTkM_!^S&cUc1cvK!ClLxK_rL zmYlq(jOs@E=+PrFS^|MabtVojt{z6ghXlsH$TL#fBc#v0n7z$*hV*Cz1O$xNac`-= zeEGokwmSr6Nwkw4Z&N+isOGh>tgrLd6g^3%9A zIBQS=&zn(6cI>xe^Fuz@2V{nxJQ5NaR*n-A5=KP72ClEKv$N?NbJXvT{`i4&>(HOS7S$T+Evn;uE zgES72D)_qHnc;)dDL)oHHa51jv~+}-BC@Q1igkMs8j4?2lQ^TJg~b}gA9sXcT+^G2 zlsd6&gSiErRBB?{Z}>PSRdEU*NMjLNvZWfB7#KU#)ixP}4$RmoUn_$A{E*>RaKq8^ z&s9|c0Rj8wAfNa5=%~qggUCZ>Ojbif({UTe#Jw3 z6hEXj=TZ$S5)yE|F7v8NvDAS*ssCC=1~uCJ0e~MR9)Q}?V1&cDTBkK zBS;3kt6{>WfB*iy$F%f!r4O8ZW@e_wet|PqZgzHd_`u`cyG%)+48r%x$+ezmfBpRY ze0`B%Tl}Q<=HKleot$W@tB;M15z0KI##fp_#K2y8;;@s2K3*Hjv}kP1%%szBKiYgL zBlqlCZwjBq4)<`k_lIY=H*8Pk)jl?QUHW&lcXS}N`T2z_$Y9_Enk;um-nvU~kd&AR zY5i+W%{vbdS65f&d-u-H&Uh5L$#Hp8N*WqOA3h}SE6B?`J3amV`!^aI+TqE`&?vFQ z$?O}+i>V>+{cuCEi|s09MO6LbsS+U{BI|0MY?Yrse*6%LOiU!Ls@}g$$`KY84h#%L z#C-oQj~^Uh==d=0(pc37`I2-tvY zMbSxmFSh#iC2@s!ndEzKS2Q$SlpS-92Q8zbL}j)1!(n`xDha7!@9!O2%>021;KDcL_~yy zzNwEiH8p3cGelg%2!D)^SHo^Y)`#8N-*?p1oD_d|oNS$4Rdw*ID_n8-t-Jfr`T1+_ z_mgjApB@oE(tnbIUBQTpCC9nKg^oRLeFT?Y6;`Txufj?~Bm-e_|Neb_x7S)sbp z^XE@wWpU8_qoSg+vhL=oro_awF7H2PcwuC8moy|QYG-#>Szg}S#-^{YFI5uzc7%*l zhFD$23hRW5o}R4-!;lfTW%V><!)@MabccJ>B`rFI3bCg(rX0uq@%n$HKBjP>=?;hM(A#zsb2 z2IX9woJ#WYCUT!fEGa1|iHM20^lDX9Re5=N-)Cg({Q0A=uTMix9-?s7@V=tC`TFLn z<)+5_>Kp=wnOwBkoq&TofNsUA8+QYW3gw<5Kat71jg5_oiHWDtwBqi2i=o6UQPjdtdy8$O1))?1C4 z$ug$g@Jcc6!*{Ft%ZAZ&OZ+~AO=3QgbUPCh|IjB(3Uz$?Td3lqMXH6X2S385`gFqo z$}V^kI4oobpZJ(yyk0M3XENS#j67C$Xz3tWA8#M?79au#yJ<>Wf%uqHnEh%y;) z_i6nwwbA|bhGc_sp{tO3?yK4gZ_dR+VOql>;t>S2;Yu>AuXAGW`A+H1gyRRww7F-z>(Uor zLb-3|-S$m2W4PjOWlECc#s%WQU#HYyZype&SBJOt%bXF8(?}K693++Ez6+F_F2S zpfoGm-Q8_|H76b@Q@Mz^NHv5WHd5Zjevc~ZiQ#?8G@IQ9lgdtmupKobRTVArcmxRv0Uq`{Kq_pktQ?5> zc>@hMH#ahL^uWO3xw(N&aH0IHtO`O84-ZysHdYR7c^Mp>TLJH5{M*~J;Mx`3|9(u1 zLgWfxQ&myX!ixF04w*RX-FMd75-lnI{=fVJhSuC%T|YxD!+`3*foN`)IG3PD z@9NkmMZe~}hmPKbihBJ8$EzyCzuhEjm-xSyM~ZQe9#$j$OitR^Sml4M`v2b_?+-9% zYbZF2oEA(jG*wtpq}v_yiqL55VejHJ(dsnDiRHgq4Qg{}c~6B~^U|3mcE_3SsdRDr zq?A##`2*q1j6wP!bYt$3`Tla+iWlN4MBb6D5uM^zi#FM1Z;5uZ1KainNKM(Gm5BRo z*1i;z%k*Q!uash!?@iH0dJG}2u$8AEbI>=l?x)Fr5iy;Q8Az#i`G!6y4OMB_xn8|Q zpS@n#mYByS2qqP#c3^ZLJ-nT^Yijz;=jOFl3h6#wSbX~P%}qN6%ZaeUn0$f{Uw_U$ zCnAaPwe~<@})!n{Pa+ACFs_k0ft!r}P?evzebSmT)IrD!oshr=Vtf@nZG7XRBW1eX7lN ze9E+iKtxx@WMTy3q1@I)tZ9#&6`OX`2f4iN@^QPUTDyYqj4<=-hph)h(4x7S{rk8Zq44Odn|a%FEgkd@6Bd>VOawe1o9xic^u`J5 z;&1cRHBchK`7!pmyL)lD6Y)YR9ZFkdho|MAtSDMPI1lQ{DR+`J#N@hP$jD%*mED<(a9g@6|bd2o(-&equbepo6*t1`B#@%sd%69@F?!QPqK(_tROVM7yIoeo2KTkEd`lJ*$Ks( z&!mtD3md8H?-Kf&tEy&I;o;>r(foc<+(!1~X&jAcR?r7Z%TEndW$gD`=e&+nUPxO0 z>gwXo;fa&+k^EFsf=ePQh+#rT7H~jKfgFp2BSq_nMA_top0T-$nVA@Cy!}Lvy7Dta zMh3V4I`^YT(rdJ|oqgf_LIKQEwklm^`oE_IRHi%8JoD83vrmpGx2$ZLgoTF_Rpk5w zmK^ahbwZ?PXXM4W77xI0qbxF#PU2~C{J~tPwJFgIX6Bn1s*m&=? z3Sqy*(o$Ag>}@SsJbeG(ckW1i{h{jaYKfI`p94|1I`Kf|oi3H9vl}KwMXDu1gIj=x z{T#|YvO5$n&js!-Rhk;H8yIlc>wfjEWM=a1#-^Y7+SY%#k~D0LkI7u>vbESgfY&_t z86Bgy3te`kJIL9!-B}3>_x0Pl%1{9tDKaIc&Z(5`8O-p=ZCj@^UQd@rB;2*laqezR z0u%!KS)}v~@mrl0HJF2ZQBfU!7fVarwojg@Bolu25+!`b96*uY!>C*1kDDs=h#l_y zn^4&R1PdM zk?#zpqZWQh%~l&9=RWq7(85i+^*tgSRg@6R7q_l%c-9X!YrKTxRGyhJ)AMhFfQ$;^ z)R$h?N9rVEE~~gI3=Alqb6w$!<-dOUa!;1|eOVinHgs~5(qCR)Jei&Aoo|x$dnA)3 zr9v?#a^>-haU&_U?bPt8H%LdUUJ77DEd2qvXq zCnml}saIuf)o}FC$ED@u!G3KU5zmMwvM`5NHP;)rH`0c@9vs%4uvTfhNE}5QxYhKo z&0{bl&?XekIg2D;7siJ!Su4EAr|00X*EnX-Yi}RU%wr&X3H@ zMqe&3?YIDL8vlLcfoC51W$hr_B>)+xv&-*o;kZ=Qpwg*EHj)k2 zEmZaq>uf+VCD!N#&nC%c%sv3-%Gi|bfb z81)0A1=5Y%w!=UDP+O&#>eO+nlpVMavBa}wO}|7>>u3cnVu%XdNhE)F;xAuZEbXVE z!HqsP?&tlv%DCu0tM7Yk>{iS#ax$&P_emUXQ{u>5+F=yCI3Ouoa{G?hFW@g^2%GAC z-8wLT?Q{H4=LZO*#E4WuzL0G1o=F5}NYxGU#QDoI#z02NB;se6x6`xTG z^!4#Z>5^>5EK~>;UGLu!Ykp1TZd6LTjeL)do$>3}y9XmV-0QBc+~G80Sfv+de#VMQ z?F=Rd=2~tfB)-Rk=>g&Uj;z!aR(iJ=*Rz(JT=C9n36basXc3rn)85k2~6#{fPVH%va<< zjZ&0=pN$lr==8(r&e3=7a1(im#|+OfILioRB|qmRoH%B6xrfbsW!rt%j2{FFqD_t8-@Wb^K*`4L6; zhqc}ck43-RVAqVW!xYlFeS5Ku(*RN0IbF@YQ2j+}>mjKs+u0(jLoS^s(vIU0t>u?o zv@Zxg#vebsF}jPtkaVv^3i&oskqm<$>1{FEpC5Jb0v&r`yBK&d{GOxwej<#Zha!op zpPoMa6d{ME+-tVS#j?ik7&xJ^emxeypCe}$@qrM9KSoZ)u|lPvR^o{5ZgkTu0XeRz3#I^*|!$v8{;ooIhF8(Wsan5r^C z#O`7ik6AmbnHkzSdt94l1lQ4#K-o>@+^YFH$%hmr%pnsbj2lhXMU)2)My;P@ZErQ9 z&l`_P2)Hz)Dv~!g%xJx|-<@O<@mRxHtAA7Q>C1+fEZf=;73z*TrLpY!7+X%=P|@eW zBj=35&9{kc>;ZY(B{Yp{;^vzlj&#az8pEqt+-j_xp8*RQ#$ck}ViLZ6R5=GF+gO;N55M9S!s9_bIYDPAq)dm@xYud_?`H3UcM%C z?*8`y#(ERtE4Fydyg8eSafn;3;&=<BawgUVVN%JH&6CIAKA{pFrogrI`Pulr&Km6}4MFxj^y4L?@uoNU<`T zJ}12Oy*Z7ANcagRM#P%|ML!Eyi-{_{DtsKZR$HEatWb~CmrEivtjI}M{rlKe^n&76 z+;7bAMsLEqWKmMXEb{627*nh?^RHySncbx?d-t4xY4Ptn=R@`)bEEvBF5OrOLS=VN zvQFQPneNE$h~fSz$9(qTEn}*>SABZVxo7AvZ`uQAkTZU2naUlP(e^5u^IydfF27eT z57r*v>V3sf6|Bt;sW+NrXoVu8ZLWQN zkrzURB(BHBnjQIcYwoj{ZZ6Uk>X9n$qLd@!Pw?r2^sk`;-07@LNkfpPTm|Xj^VED5 zBrN<)v~zm#=o+~(P^AmS+PAHUX+P$gl<)p zbaE6vxk3Q)2X;1O9t_NELt>o<<}Yt)o|klT#O!AW$grU@HGg>@F`4vdLl<0QY23 z4DZdY$epI@ara>+Au^O8gw!Iff`(=QcTP^XQTMkH9`DhgSZ`S8ZS4eiFhrzMBjU7Q((s2o16axiTw3S7_JKbNHh+-P+DM>D`Y1`;bToGFcIF14)NV;lMd<9Z^R0H+WDIa?PO+#{v*vD z^pO@{BrJza5p<=JqVIC;zs(#>8r(XlDabG%nE1Zn#I&cN#4-6fB~5s<{^FsJnl|=d zG_=;|j}2(eq|s3xv2oF^3kn`7DYGJE`}*jA(oo$R2n_U-sjpvTU_`C;E1Idk z^_PL+_S>xoDpoyt+mBV*pPvO<3jEI@Wu=dbGl>qWf4f z22D6`I*VQ|=0p9Zl9xxh%9JG7{#?nEe;O1N;TWGyPhR`iiFJ3EU|Ql028XB^HAB6C zK)0>&i`x-7diXff&Mwl|vW1A;7y2}pbkw+Pr%mtZ-}wHl!d#stX36rVqe~WlR}hUS z`&i2VcKF5-x3z$Pg9-CPawP%o7b}l~Bc45p#d5-U@+8ZllANie1Ytjv9^k53hH&}* zL152F21PEa;Q07YfZt3N=8yKB9VwYY5+Y#<2?tEI9BpkW85Wl1K@?(oFSX(mag5y@?lq`7nL6S! zq1Py)rh~}S)0E2IF0Sn~mE>9?!dCr1emO?kOlEa`WRScN$o(%aK+bcU>MX}X657x( ze`3Lm45@%6;}{lkntmlFe{J8MfDi2K#^0rKI>*)D@TpS~BIP6}Vj$`qkUcj}AN~~T z{=UCAw$jJ#Gc(h1)t8iAz{aMsw$gXEs9NAMnynTU)5!b*2byX^q70qmV0VcSZNzj1 z^7YL&pXted8s_NK>lrEEj$prIPEPIuZu6yGf}42xMZQ&*C_15h;}_#wlK}(c9BmA|q$@^gbgOwy-!&917Num0cu}dv)*a#p~o~ z$#X3Av$M55Bjcsl0;T%5PENM{gpWG0KeY`Jh$(xD#aPb>?)})BK&J2eZY1)1KcZ*U z!K?ZW3)bQyQU~>I>DA#*gB;dg&3>Hit~+w37tcgY=HUE`M@Ra$3B=m-uJELl%=#l9fI!v|8OS>7EZ z)m)tWex#+{yXlR~64FB=Mwu%wLb;%zL{a`mo#^D`pFt~%k%o;Qlw!AQkW6#DjXYWZ zPN<{>3%MyWvQ7GHF4J9Q6~bgXdWQmlqe4t*X`L&5S$~qr$!EXt$2>1aUvYRS^$wS- ztJC+gsfl~4tCM`=N+phv{h2~QJkQSJ$um+wxc{UUj;ovNPloTYr z4h-&~PK!ge{yq>`u9VI@F|lA@g6O>qO-dpoqhGJIH1{9)h$}jax;cAhGFEk7L5GVP zF?VSxUgzRLr1XbhCX6pBsf_(P*Qi8!g|UzQ%2Dp!Lk^dhZ&eQKa;RlvSv!`G7z#!a z7ay)}mr2&(Gz<#-^caQD&iZ~b7H8dvmfLR7pmp_d+jn^*Mr*G7I6AStt)3Sb*;W10 z4uVvAn4CQA9c@pZJ`MP-$En`_*wR0sO?KZ5h991t#6PHn{G+=DDl+LFBKslnccgvm zdgMPozKDGX=k7CaiDaios9)GgJ9IjR2);~DU?`X<4lloZ`Kc^}mX%usNBJHL)sX+I zS99L_SOf!~ryN#g?;`cq={c0TfA8<;ATQClZ_Mu~Ju`}`-(AYlJ)=>f$sJsz&OPhpb=OScmS<`T_GE3{r_w;Hyo%wU^@NfaSC*7(NGpNBFefNI)tfR|S{S=K6Jzy2eC_o_zfSw`ZCOl>2H4v`y&7I}2F zf_YQ{2>xEEntM%k!1SR2GY-Af{+W2YFMaWCi)E7YCl` zgh)6j*MmHI@CUO+b*X%Y7jb8HTgW zWx`==xq6D6chCR2dGZed#X5nwHf4X`J)FROz2vH@{Zd!GF}8$mGG=*&NQztiynNe4 zSa;=nOD2-L3a0#1&NLX#zI*TrO(yTPzVVC@BO$(T^xvZjd1g z-FN9cbY3)&My{P&xBN*rJ@d%#r&frVn`^tK3D34E`+f5>0kW`rsQ0p;JkwG|(GL46 z7-AOoIL1CX+1D8*!%o-{1!c?Kx%)fL$2}OA&9;0c{rua?DyG!h4&(EmIh)JZ_c$kO zzdojGaA7~~9WP$G`JtLMHD0_o-s~0AcCpQO*XQ+v#$y8myG!JRxdy)&!xO`2mpXp$ zhd(AKBV~Sl%U6NU=$+hbT17wVvKq0E$qU_}m8!1p@-DA_Gp;w0kDjUf&r^0@dU~@i z98(=Svb44~2PPzCu0?`8YGD*I%`zzr`$^7PQk-7H8yuyV%&0w7#HdzG%>MF~hgnQt z2#3dEN)iwj;(KSghmI=lIx4Dy^Z7GBK5Tvc)|y&0$5w{CVm&U$6TPF+e(hbicNcCU6e=c(V?ojbz_ z8RX}SOHT4#_b~2d=kW-dNAw(?P;n*m=TA}DoxaH7X)foM@D|K#rpH9AV z=KAYkxz66=)MwVuP0N@-Ik&i1adxYjL;+R4Js9at?y9+0p{F^;r8p~BxpvfQ{{cx~ zQ@$}9DFGZL;;s7l+C^3Agu6%hsqRbr!66%(!i@`kSfJm3J0Ih~{cp!+r_=9;rKF{K zSQ69V1db%!KQKl`(o4$`j_}J{uFrwfG@qB1#oY#fG4Q!Mx*GLiMC$!~$~5-8h56t+ zdwGRciM`Lo3tY{r`&wTBjk^9+MS;>AtYJh(!-ydnk{dc^k2Fp+gyvITh2XP;L|L^Pn;#<$gswn;Jq2bqL zbMGCtTb*OB<;q@>?h=`%-$~q1?k#OoYCp!Ng_0{ zvANi3*!}3Yc}?(>LBih+xSEf2WWJ4!scUKd47a5-SUzWy|1>~W@b!kwS|RNTN9k`1xEG zHy*Em7S=4Vtm#xYJ27#5b+Qm08QHb^_u$}UXHHjBv&S+Z<#xYwU>X4ymKI#`(W8zQ zs2UzJZ}h*d{4L1v*o0I2{2HQ*_}hDm67#HfLjJXVu}M9%m9EL#!MF0|T(d!otIW+jntsflYsObab;%ce6ij zQ=X8R==3n{@bIv3L-O;?%Vs0DPoR z50(+Xxjf(?Ev~M9$wX9FS9e>c)5H7|*Wkz*+oT&pl+uJ~uz{ADu9OTU{Dl40B ze0$*5axr&vIdrqVz0HKst9Q0CIp>;*Pe{ni%>_Q3v0;6E{X=S{moM+Q7S+}!cPzQP zyF;8dHj2s1c(@)O+mg`IMqT?2nDZr8T8|g?(D|ednA=oO1KUgD?>9!E$31xPfUD_H z)zNVuNRK0)nLgKNbeD^m1_lP@O~=!_J~!`}mQI>anuHcSs|pJ@R+0?8|Ms#32M2eV z?DuiCP?M2ayncOkc4&wn5glz&skPg5l97}&$rvr>1jHUSpO^aj116PVtcSwF0Ho=F zJrooe2%MpmdXohCJ9qA^{QA{iE6S7|5P%xq1-nKX-o-(>^}9Dtu^^`2+uNIi)TS@- z0azLMVZe(lZ~Xg@t`^JqbfVlaT0xIAd}U)}YI^!R+{Av~wjR45m^DK~Eso0=;a%r5 z3)lG?#pQ}OrS?G5f>c%8*mxQ3bD85#=>whzZZTP=zqPxY85ef|l$6g?w$5W?#6%vx zsg7*GrvVb^{e;btr*vLtD@hdW3qUl47-b{D!KMf11YFJ?D64_D$eOSI*c0I5mH>$? zkj&sNqC~ImZ&J(UJ=1gG2*DLtB+syUUjtWA^5$&l6>mypBwl#ehwEDt8}O(TrMken zj21s8=lvbZlA%>;5?J`Atgz6yQcJhS4k$)Gzd~7DAwuWpCHVQxHe*FZMaA=Pu$Ibw zuD0lIPJ4ii;_ib_WP+R^e>B;0ozP{nv>Z%8Cp!h1K$}AY_VC?1(eSQ%;j5ZjpS=J& z+nKKe@d%(3QsZO3>oKWpKK&JLGg%h;_@hOpmn+Z%xf`mqUc7h#l&)y8zad&21})xK zx40TBY^Of|W1%5phKE%)vGEQc;EV{Kfckf6A$gGkD`@gK0xuMokux@?5AVVuAlTj6F|X9J7)+x_T&_Qr)TwjI zW5ae-J_Rz-(%-+8J+v2tZp|IG^A!<5%7e>`i;2y9Y&{6#L&O}c4J~J=FcDR1l@PNi zC39ZxwNFeWc9}rCKn&BCy$%SIStIWFq@*gX5+FeNN%>~P5dK}yi5@&eu=cL6ucz8K z_nK6K;U7f)RwDfpqsT@W?wwfi(r;!$63olrGX`QptU{ZS4S? z9%z_1DVb;Zl}3%PlV6`jMPV16-L#cavC-(Vx7Tyv^X@hw6qizal^a7CGQ34 zCcFHjzipMb44PC*Km`EkP^f->JY)av?OPg2Z)z6w6vg8;D@0)670^)in>+-{5V4m) zu@-gRj_EafXb6O1&-0A})^0wa!~Rg<(guvYT>&P~ygn>|Bj{&PK!Q8oZddl;D=)({ zxHvk}-?V3C3}b=jgD4c}g=5y$ZARqaFd!+RrlwB6RvWjj{?NB_tayXxR;r=<@+Hs> z11BRRBY~KWN)I~>d6qZD*38Vz(h`f<4zd$)eR)$9?i1s){}Bk>t*uhP$Oke%u)Ll<3$^O>F``U9HMb_GrJbrUX(W`QJ9q@UCA# z*KZ66>@wjIq)(79E-K1ZWy>*^)%*`0H*K_#pkR>8=FQ6k;&{ZZF*1Mb!-XxJc77{! zo!$;#?IDc0EiQrNSCj*P5d^wP5AZj!TRz@WTjsv7v=+%{8QC$b&Yx7||55V0f7Sfz z?fVqNl&O8{_d{!~b0tm5)$O9af}^=RI)A9It~62q&-}2i$wY*^Igns~DQP(Ybxef= zhg3#}qhraTv8f4S02P>MiiG6k>wo@4awYX=s@$SCmX{9$-m8iVVXD7>I~oWapxgm9 zRY4AjZ(+grxH~R&ZUa4z!qO_z>+nDkF9LT(fn?nyavpSz6hlN%o(%=e?edF??*H)37guLf>S%jn?NXz_`vbMJ2v3U2|43h6_7pniFiJ$$#Wo~UO@=Al=fJ@$xpAs*J zZnT-(T8QRhsYbCzF@^YYO+Jp$sF)fmqcC6ot%*i&407W3+s5$$4+cF68?5frCt6@Q z=C`LXD~f-=(i#h3Ui$sp42aaD&t&9GO_#sEp4s!Tw*Edh=jG(|p76)yWR2BmKEyS( zxs05p<$BS};@?hh9UTGOt%^SjzX$3Ah#(++1L%U@X{V~?v_wAd{PJK3_{>sR*!cKB zn>GK$d$Md@T@Ol;qN48;6JM*U-pqV`4eoM47P|Dv+1dH=<4&(Zwo`X$>9&RIa?lL*`}+$D+3tytq6&$QQG$RJ$eW9-Yh5M!0Ln0G`zVF|!w_2`?L3Ti>3bK{d)V>ub zL+B-enF&;65FtQ20hu3A@87da;I!{T73^%jErW*A_D@0tOiOOzj0^)Utxu|j z@8jY?U!ou{4_LfbkJFwKDBHlzmj^2Uz`z4`_VADp2%{CJy7~Ee=#2oAmzC{YUxY8O zZEQe+{+N@atE&sBAJh@n*489APrSUm^z`%&_@O0u|Ni~v))wR;M{_HC`Ls8uLdn;DXE-w4-jUeXv>n;Yy$HWYV<$?5pO2}^Up^mQZ?p(v<(9kYG`*f+h zAk=|!wYk3DYKIfp4r$}dmoKn7@B?^eunq!&2n)lh)Jg+s3K`ktVn5sDNtd5{7{vVQ&WqRy1Tc(uifnV*26G()=AIIyeW{p+W3*DfQWh3mw0x54ixyN zo9hcsPEI+a07r`@tn!|Ii?O#g-0tvvZoS8$>p`mXN285zP#<98SvGP3uR9zjx z1fc$bH8sP-zypMfT07M>=H{9Kmi^akfNt+(cVWo93SgmTnO;g-8n|Mk=b6*?U+8A8 z&xU-URzp7u4-OIm5TtSjaP=kMO0rLC>!TDqHq@9f;%WQjI0EiD9mBVYh9`oET9 zZ7`h}C-8*p47T<*2?-$AHTZwfKV4p40?fJsr4$$qsywu@keHx{3QI^x069;y*Tr68 zTbuMD8dw+fHQ>x3UxE*c(e6eH%{#cddc%ETogf{7p#l{hozMDkHUP6pgLeZxJuK|( zTf^CpBqSstS5;M4KUY@1fiA#1vn>i!z8z@S5G~;N3zw@x5NNP_<;~~T;Dg^2*ws{3 z=e;jp$I2aU7MIu8)&f%V2@MH3T?`-qfycc2G9HA~5j+X>0Hffc;DDNd3@`rFym|Ac z1v-{$n@Rb+A254Z1FQQ041B^6tHsgmzW-~fBiPVmBM)R0bI?JDb3vB;blM|%ZT$67 zEnU`={M_76>i3^M?Ezog2e}R=0WB^ru3nXSWOVcpXuhCVT?f6#>(^djS3ZkDknJr_ zS3`I-G&EFKA3=i$$`7xbtCP{u(FFjwa3zTB#{F(8FsyykQDRNa3E)sWQ&T<(i9z!! z=$KlLkB%y*wqa-zLvhg+h6mEt!$W64#(jp@KR^cs)ck>gfw{RW0I&;H7Rq*ZJJ4c; z4&Jo-p#sFiRw{w$0V9J(Y)Dm24OA#m$A=TT_OPf@tx5(S9!FRzY%g?d;GcHJ#!LT7 z1dwdra!&7}dG3KJjjE`Nc333+?&;G%Xq%zoUB--2T_3QKx zAL^Y}64s-!n^%0GMEcd)6o1g3k)9?u6Ep@Gx;}Rsx*1x z?R^dIsQjO;b~54n4(}(E_89h!o&o{hmW4#_+WK_k&#iW0~CB+;bhqKAlHGe za;CwJD|H|$Jp7G|%RK~06G=cC)7klHd=ml+5=gp)*Leqlq}ZF)h=PKDGEq>Y{!@5) z>2Y+tEFOn_gWy0A!m8o(uv$X^;UH#%bns8@0@(%xJwxfD5K_?PI$dAB<>KO+ZLMu+ zsQL1RiG_s#3i#CLdlC}!-@jW!_YAG~e1ls#ctQU2RH!nZUS9wF6_k&K>h)e1u7@>H z8bD7GE?}nh?Dgx?mKI6++%I4FIXQPAEg>T#gRLR)2NxK>dL_WaBaI~tf*VM90+f-e zg?g~?e;qfCNIWPUjXOXLBEZf4FNvL=p3XJ6E#HW`?TA4K36ngCBWQzWMn)of{7ZMIn!aQmmoiHYus=Vk@$t;R19!1Ox;*zdyYN4Oax?;_^c(7|TKU=H};v z5oDh{c|H9F&pLdhih^JaIMyG}A#F%PIcuI8YG%GQh z1v6#-WYyPdqkeTpHObAe*c1x%QU3Q+RS_P~pT=#VqvrQ3KAB@tJhJ_u&rOx2{b#nM zg)fdh|BOIQ^wMJu8=!${!N}yKMG=jfsF55Xm16b#&n+yoRkYO97zweG3HgYtdww$3 zPhl?OR$`|pCcb}9L_rant;zQ_`PkCIfiR9DpBl>GFQ+<~;yv_9ohZ>LrlO)6u;UEt z2ebks9H{QKgZ#w3>Z=fI|5VOOR@G3UrdLlUPv-pig~I_12AL(M*^tMENo|C5e#Ch5G;PR1o?YO zeJT9QAl{eFG2ZkLak8Dr!ru`oD@pV=OONxoR;`bWpd z=O%$&6W&{}x}PAQr^r0I;anU2!@4@3M4!8&3&g%4cm|Jz1{7BlI!V1;81V-s;snZ+ zPlAKfiF?Fvpd5P)d=Stno#^hyIAcrda}i0`Deb0+8EKKtiIvZ@9EDF8j4p+V!)zXc z(%>XGXe0j~QSbHD1y4C44NauBCCuG*WcZA7ON)!EYHFSaE_=<^63HCz%!L#CDKhhs z;{uqeo$jQ$yNT8t^v(!A{$YP_5A>jxaDV5GG9Ul%R&B#s|;~gPtR(*?>4s z;vYN?1UbLR!-qyHWOoc;1`hXMSQr9){Qmui{M%$?YfDS?Bsc`LVh?$F|3!{X7)W-Z z;*{s+{sw>wr37?}@Dc>L1$5g05Mco_+1wl)B{elQ&{{zvmXuuR>tlcLptP)vQ$$4P zE}_v+W+o=H7fN1Qz6g*$MumkjAUeCdVH=>UfixM~Wx~$R4zZo5`u1pZd~tCRl>gw3 z@XzDzsdQ1du*VF{Jb%{L$?51aV`3b3W`$$roR``IVGAWPcel4^r=}>N@z@+Igvk$7 z3V>`9NJLQ2(*8qw3`dfpc+d{S$A2*&_yC&suU`d0Ne9^<0CI9Nd78lG)fM~w`(R3P zqUotA0YO39hYqdK*F1QD=Vh*_s3<4*ZKW>>zzjS*%wS%)oKQr?^{oIHdSYY4PT$$q z24{7xGcyg)UjlmlGhGdafT57ZP)1ige+K>;Ks3OWFsy~7P*L=H<|~b;D{e2Sc>@8* z`1_;43l*T@1KbA!SE#7a@Ii^ay!3?10@EP`0?k-JcW3L~ zAYvdhQBkEX_e4Wkh0B!z`=p|xqOR@?Y9o9F-m3uGUGQk1vpz$3n?zA@@yztJ!_$hl z2dk)<_-L4z#4>{!;xll*5F6_VYllsQ(Gu)USXdZTEhu}?%tK2ChS}TOgH_?4RZML+ zHa0@xUs(G%X2Jv>5AF8$*4DxPKK89!Hum-{&Ks(L{#8_B-~|t|vKXlYs|RQJ`1s&4 z9!=goQ8ertCD3OX3;My(4htV2!bZaT(p^EJ4}iVAyga}qa6%ZV`)yRi&H!G5-iOP? z|5^z&Uk(lqp9%|mhlb!_sr8c^gl9`jOG5*+5+cDJy?uQR&Ko~q2>t5S%1F*rIF^E8 zd%vw43e(rTw9Z2v{7fQS$lo-~JRna@m%lHksF*Jw4FvzcDge zgi{p=B#1PKAIP*Y<%EO-XE9JIpdWyd0*apzOfXv5i}4y1yQz^s3;zaO5l$@R~NxVX`sxd!NqG(cBwV*}L)x>K0Fx3{+s zW=b0B>x;%rBAP3a8@4)LXwzoUI^x?FGa}#C< zBlz$(33$Cko!-vyFyX{qLM#yx5h#}6Qk|WTZEUsxmH1E+h31x*mseK4U?OsHbIbXW zB(fLY5CSz8y7<`G+X?dHQ&XWGqi~(kQN~y~6I0W;_wV8AkRIzB8t&jcf%Ix&!6Ly) z6bk)&Lw!Aj%z3^sH#tlSAW>IVL7J-F?C2c(_$?y15kBHrH3#Eb;UTadz(mN6|FND< z7`8fFLKgw=HK-kFe~wTsq^6>R(E>a+SPNPclS*=-;95dJ_n@#{IyeK&;3;8PsRFWy3P)SIHN(dnd zk10tKGAEhm3Lzw!G8{66BC`zN^1Rpk=X-tq)s>v{8}57WwbxpE-}p_$aC8P6XULm+ zVBbzm*t@yO2i5_n$fkB*= zw$MCG?R2&oT|h0e#6J$8?JPzy7Ku9pP_Y zx($ySe^-QiO)ae(S8jD>_4=KA(yT8Rd*OM}`@EKl&kKxv=~UB2Iitb@AvmR{56a?7*zzoS+!IYG-u>K3`Ed?kdy zj(mjbkfX=Sn0QH+^MRP&i4$=hB_Ofz0|F}$HjvKQ6~hYYzmDGCs?b0lwS3{eUCb-( z&>7_r`>Z9l?5JOj5!O*mjABxM|L|Jt2O%PqbRQpdGN7bK`UEeA1qW^dPkYvzGo}e! z5mOcLW-+tlb$0gdU~MdvSj+2}&21dC}I^eGf5H6wEvV*m*AATY39 z1SH$Vi-IBn_(VB5IhpHI6B9wS(T#St`LUkA*^WYkAi9)vU-}+msIQOe9v$3*H^+_4 zu#L5}c2u7A_u|hsJEWjsW@VMzF^Lbw^>6|PUQ92! zejU;-yCGZgv08l}WR%;t*D&q?2ttFe+NWt~=y_Z}LV{#WNJ2W+T!gNF*%b1?xp30> z7bu@gN;v^9UWkU+;tzrRRT04Fh+%W9Z3td4AN_~9%j@a+WIU^z`pVggRGuC zlVqh8W;u?0**i5f#K-VLGkM6R1R)i967<{lOM8GP7kcPYX-9wm1yfT*7}RMPr6DRR ziWwZ(=9H9_W8K-$uObU0>bkk1Jel<<$Jd4FfV4V)I1C93G2MG}b)15N0=w`omw5C; ztf7+oIS$r|z$qxAv$E(rIEwu^xVhgyd1YxR8H4AzytL$4*!%ILPJm`Rbny}3{2^0cJ_KgLCT94@0|M0EiE67SIxD&Ma~Cyr60XEP|oRU zk4OP3E|yN&YiWB1j$j(-8NfbFNP1Kh9uAVj2`Q=2ft-S(e^OO)08cOf-8+VzJFhu9 zJ`4&1B*fdqM2h#pyJ93S=ouIk78PN+VBm%X>QHD*MVx{H0zf;rW_nTW;2i;Uhdg|U z!6T7rGRn%F%*-I}F;0SCGYB=d?9c9eOi^+N$=ST{{p;85s5$D={!1nW6!JFJ0{{>h z5YWFb8qi!+6sQ9G0;9)(&;hQVi+<0)NHtc550{CxICBP0fJ)3&LZ$&LfOLgB2h0Ff zbm08EsHiAwdlf}RB&NTstErH%E?;(4;zs4ts=mcN*wfP5T0gOc0XCRaU~n=Oy8vV} zu_*}w9l01=8u$TR7g+XND#RB5Zxt04;9RgAhDSyidDXZ%IWtg@WoG7DRe=Ts^JoFn ziohQvnEu`pM5 z_844079B$%a&Y25{~$`c&kY}K=9N^^D2)}$di@&oqmt(zJ5NtfgdLD>7{%h^>e~PP zJAM)V2?Qf7AJi2z`~X7d3mv!1PEdT1NOLVpjLprtI5+@xtxQaWqV+Jh48@kV09Jffw-NkZ(}=V5?($OZxCw$u+Q0vV7%R^qcL~HzU{xU@@)#Dmdgd}Po-FvY zd$%e0mW~cTDO1esL6R3-2A>}>kH~>#{-n_kAO#aJz@Pw6nR|P$BhX?(kcTQHk+WF; zy1Ke}+CYs^&QXvR6)9FK`g6vB{wT3k>nB8XydHFYT|Mpt?uZ6&&*!oq+D4=|Yq zNdtv73OJr{_FcQ=q@}k3>GZTjWFv;q%;_3oU{7Y|6@&%Kup2tit#K>J0HEICQbL|W zK*S@D^#=3iC8pcm(NRYzzz`_R<$+lY1Q~pHF->qjmBY3ZScg)Xnx0;J1Lf3(GIv-< zW&q-_JMiL;bj)IO)~8PxnB)SyEaY|ZVhUmj${G!gd(g~a57}JN#NYK&fhG`Qa658x zz=3LrmMJM(;?bq3C015+g`NQO10@cBk(ZWcVhqBvvMhC@87~3W7UFT)hkra{cI%J7 zu&^*hDku+l9^l@v8vuTQd~g}db8|Ot-rPw?M~A{>=T3;~fReaIKumlwertVe=7NL% z`7^zkO_Xpdbye=Q&PMw=Q`6{IdkYQHKDoU<)e)CYb1m(-h{HDmzrhzKM_uaJmRKf* zJ)!wK6$o<=!el&Z=qUHh>NQu!?Y-z;Zhf`-V^Y)^*PEB9&+Unv^tr=#anS3R^7ymm zXRd?G<*gmxz9}3$XliCgd4A@lvBRlT?O<10$04A>PWB5I{`+^;t>2btTWcCcZg0UT zMe}T;bF98d-R41_@MA(OhmbnM?m^6DF=7V!WxUcLG_>JkNxerid%7z?;PrOt-bT@dsL92kgusPifczT%T6 z-EPB@hhUFgdgDgEiTL8}3QPcO8kLP}M*W;&Vs2qUPq)iggQO-HYqi3Xm!8r=-WVytGSrpb511Ri$u!%p@_$#>LQm>L+Q!Oq>*-Ax^GP4{@1UVzn0X3eIeckh^3 zy|$e+Km8?aK3w)UN@tpt}l!-J&{dng)ymmU+L5VjRYrc^{eW02Q+kl4OzRf zCy!9IF$LDa(q^qiZ7l2J;Ox|MRlz!5>xI;WF1<C-#yFK(^@(iU?n5?{vPozV=S|6HPa<)ogh-JC|^!~O8Qfj1b+@` zZO;GxV8vDYYvLb{r9|dmKPr5l#H*UFd73=vPjT5=dc>wr zfZDrAF4m2Xi>*6<@0g|&T(|llcWW$1wDNI(p3$4GserKQ?wJ9`ph{*+GGDCyfB=f( z1%O^Kc38Shc{)s-e4VZ(7RY0dyG!R|6Tgl+74316w-R}r_)sm0F^7kqa(sN8f0Xe+ zO^7_U5KU|rEHq#N+IL~{7pRnYSWnH(H5EP6^yUmdFQJHwj^6!497(}VWrOppvXumg z(1iv07l+<#3{KvLP1HC}P+|F(3I{wptZ$IPWn_rUexRIQa>sC zYwJFFRBp*=Us2yreEXALOqW*Q6k(?J(JQ z=+GY7(8b$_pD?&WbU~<0H1InOC;?~qO}3E2ckgskjUzTX9y`$~w_z)R>pj^}OA-dm z3kcJ2FX8D^)?n>cLDP+=8c$9@qe}!vqGnq|5Qo_M>$R7krMm#zwow|$AI?c+3?WmX zPG)La}0rX`Xcj4yN@4SE;NUctKTU`R|w@T z@-=nmAOai&P*UX;;|#EyMeyf-u*E0-`sI|{wLe;qzaY4d*gdmEB-$jjEXnRFyk9d! z1;HEIkj5W71jW7?NZu-5OIN{sXeQr#D-x_-ODF)F$yX!5&0tmHGFsSW391a_3%CMZ zk-78ND<~%O4AbP8kgw<)?A}02{r52xnD?JpTx(UU|>xS$3p(1VSoX$kAk@tehMafe2p-E%2@?a~pbjm9^Wy zG!mB73L&^ zKlf*t+#YM@6o~Nn^P>^k0k8{f5nwml6v+AiVq6{>>)!TW7s(j9prVw6JNVX5Rc$Wj z9tpu)f|NMq43}^Di5SVfDsB^Hzbg|!vX%A2-U7}SV?X6fp*b}Z=KHQ!?KFUS0fXvp z!|%etqgwp#9iBKR{YTLxZ-Hn%2dkW(cewIdXFrVl$j34;i0tT=H*Y z-q!VjU&mcagjkUDe_<}3iFoIg+zNM)y`V=SeOOf7hTj6@EiIdiWo}Vb)fT2X`T&H3 z&O0j54nMUgl-=J|TWb9Lq=9v)%eW7@V+B5~BW}A6oD-}JReTQ>^njCg}Z>?k_zD(?_2X(~jm8)a50d&X+ z&z23-=4XlqVZ%cPuUh#a)l6^&e1b{wjFrz#$f>U5pP7T-hp+YjW=_?zi3z%nB)jUD6^bJX{@?%4DrTH8?Ht-at;_(-PJ1 zZi2H{N&JNt1w1M%pT*9}j+MCu2d4A_JD)qp8Cb_mO+G(AkH>da0Yvw+`jHC#lYB31rC4c` zUst>D_;Hk+3^e%AK!ulwsv3+az#yfi=0Ys+0^O6}oo-Jumu!{^WLw=uc6=CPnjH&k zPej#Niyb(C^40PBbzD>iyh~YGKU_-wjpotVK89%pZ{J3>5HdS|ZdA+VO&#KgdrLeT zpoEXl_3zR(GG7gW2ozO?<_CN=CZ?uN=<7p+!q%6Kqn9s7D?+OAzPuy^h=J0ZCz6ej zh^^2zf(3}@k71w5Mo_+MpF3W?`nGQd6>1JH;PvbK%kw_AzU?xaZxw&%AJYk1y@rm%{Vl3;fSC^D+zql}&#UZ!~)&PLk!`)1#lAm(IIQ6H>hqqYAuo@m@Br8l;8T%TF2 zDB@EcmG&j(czV{Zpj?)p;fQhO5831f_GyI3^V(S6vt|BBwV)HUAKZmVf^ zZ_UFu1^XWtC@3nnoAk8NG!$zp8MR%MST8uJ`M$m0PL_ExH^_j|{=jf!<8f^xnqb)- zgFGuVx1t~Pd-WNBn00lWnC@2r_b9f>Z=*j%chsY;El6o^M19XWC6~Ou83hh{#Bi`I z8doyg?V&3-vv^H&d0rjYW=6F^Pbr8Bs5l|w9?$y2JE3Un0i~itzZ?BPW|;;eyGoab z(pFbJBBnA-BMw_4EY9m)u=oLpRby1FFy zFRu*DxI=@K=VricU2cxzM8nI{92OTYP!ue9s4`i>{=mu08%VNTTZh9Q2yjH%GWVDiR?JZ+N^|(n$|o919XJ3qd{c?rdI_Rc+X0PBVg3crw9^6+m>!^vigj1wM!YEKo4Km6=cP>-q_2&I)4f{v##R6uu>RpN{OY&}ISp6kVO6(huixY0RWRx&qEJ0U-tVV*v z*jkU0C9S7xAH()A6;1#8^^dNQO5$g0Y3Vrq$VR(D^EmxiosJmk&qsh!30mH?yBF5# zsZ-2R7{SXRRHdQNGBO9I#$S^^yncs)sk425)m`2-SF4slsa9%YT$*tPNCQfz>f+*J zG)Xr6gL^8^$@xrlhNA;>pHX*1S+tg38CTzmYqWcWmT^@|+E#4=8B}%L4D}yA zETY0)T9V!FI%z^{zygLCF}b4JyId%BppI z9z;^H#V`CbwAR{o$^~^_jprSc(!yvVIfzcX-^=(=US$`U*5St)8z1)@9tWdqo-KLc zK+C63T-@AH*#iu94Gk#|2Vbw+P*7LD>*n@#WX{dq{U0!UF)^nzO!)fHI#_*pL++p^ zIVeu-H}K7~<_&n9-gi!%po`OIqb5frG0#?jbPUr`jUN>8ZAF)lhLhDy!`2{?X;BfOrSl@<4k#EwQ0Be6Rx#6WcP z;p~Z^Q+~EG_D8TT;0~e=&=m7lZ}vw@MZx+!49fpG3AUIfWJTGyP>eSE`jsT<7COtI zvV!!TW`Y#@^VhEjEWn1oudtQAm)^{Tc2-ulC$LT!%!9D-ZdO(>GfW1jHKo}QeP-#>nQ&^1L-#p_&) z$yK5AI9^WB>I>~ZRF8@tVpRKIH1@$`x77mY%iP(?c-_P*lVVpieOUn<3Rp2X< z=7)y`Lb*APswKg*putKN>wTCfyld*H6R9;X<@Gx*g4$Eo0+oM)no`uU(*rb5oj+f8 z>`Pq^kNZNvi~FKIzRbjhzP=_$X%6?W-02(NJ~NiNv#!<6-{Oi-Svxg2?RzZjVcXqE z(V?DCL}%2P?)`2&%&$G)scdE4YSw-)>Q0#J}x*SywC3%Y|WAKHKT?AkuEa%!XGSomSr!0A()IaJf@G+{wyyOI~nZcViX zU+`iK4RYa$w%=4u5q8vp<>E;~!ql7x>QlcOl2#ir*87Zyc)Qy)F`YaririC!nM(V+ z`H$Q>Vnb`|pkV#@s^Xhza}nE{C~uC3ozCnGH`b;hCo>U0h+6b`?Duo~e;Cjtnyhw@ zRZ|u9?WE!KiVB6E0!ypfIOVOBc3qLk_E9Mb35+dY$O_=8Z%8HX#ccXxKOQ~U%zRI+ z^6XG+7)fB=aRURNsDI{jc0z2Xs_z^K35Z#`zqE^szj-By{bhbW^BZBz9ftHtG)K0= zF(?ueV2B})qDs|2c~Xue6x(Ka&Lgl626)s3>F)P(cK;B=K`YKmP~ae#>I!{XTT_8h zG_`2G3XE7%=zO}uG8c!mWSHPpFEAOVMYfKESzTOOQtKQhcT_ukcqgY;o+T(Trt#_D zzfq2%sRoKrN_%m?Q)XvxTN?@g#mL4ec&kyXIxBHMUc7_R+b9u{7NEAN_~v2iJ026c zGg|rrYJ0|WSyBWKRU7^Wp8C0#D)36gl(Xa{I(g>SXA#O(pMDkNl= zl*HAK+;n$GQhD%zGXHFOUfy%#4D0l(PEG=3{a`R~5DcN@eME!iJ_^g+zdN*6ze z75o?)vD{PwwwHEXLPE#XBA-uJUmpM&pDfNAX(BpLxZM_GMbIESP!IkK4jgG+ZN|41 z6&FMz;m4u$I})ZNr=*0HRy#fq26=*5ZmXkK)o1Z?tO>Q8O$H#MVWVn|lez1*eAsAHyE^MGhf*6c;fEkw(ge#P_ zN2X~VSUtwEMa?oXIR&!Vz3ROIni;QOYtj{}v+0sKt!q_RefaPJwqR7b$wnIGIcPmZ z)rE?XP~h9@3V)DD$mKzb(PH{nho6o3uo4AXIAp%KeLs{;O-Y-BIZ z0WPA`G1W*+C6R$l`Q#OUqBF;v^MG663=s3MKv2uW2|Gjs(H=6cFk1)=C-^Rq*$BkE zsvgc$-wt??qj#eV2oZP=XbT+7-m67Ja~{ZSfK%z|D4t;_1P_KL1?&op;~tqqKR%;7 zJRjhW!1u|<0d#q&R;H#>yuXc4L2{O0J7HkZGCXVx?22B(APss>f|7MYJLOh|AAS?Vys+Y5RT>5)sqqv_wL%&(blGs z(hj3!zY_{#d9Lj8a&JdR+GoRjHYY?Pu@##}Uvx~WBtEJiDSZ2OXl@17Enp>dAT)1M ztAihc`UX)2T;$*7-{^b$WG9Q_-iNL<{c%ieVq$+=+cuxV66~hZ4?Y0FUySt={4xCV zvl)3fFb5Pn>@g@P;81CJj7C(vUMAMLRd3)wC~oMisUHEIu8`2*-VPTP4rmx`Z;yzK z+@bOeN;Le>;40p6!*GXqh+2n(5-?A*hBn{@bY5X$ic%jNn`D^m&>-%AjRSrg)Uq9p zg{nL!Q;qX`uCj%6qSXf|rlMk7c!hNsl`NHC4JaGzzmO02>qlT~E?yXaT*eOIf~!`i6%8+FG)W#GZ+}GY0m|xI-6VqJ|8H%?$FvJR4}k zJo_CiXIT3vMPLnrggEK~!)vsjjqlf<9>}+_wgGzr+XE+|U)xz}_A5Fx1JZzC0<>LX zZxS`3s!9`g)Wafai32P$JI%Argjj04C1aYfs=;o;{c0f@*kj7a1j8DRpy=xCyisCN z)K`J;J9CEW3tHU@%gZfb$w(3tmda^lzv;CUE`?o?SxuxaTRR#sYyu|lPkl*0bw&MO;BNZtowwjpj;9&ZV?}uexiM)=^`ATqLqayB7}`ia8LZii(~POADdktORBw+!KxO zn!1M8w}PG_19U{^?X}44L@7|(?^HD?#S#pP4jrK=oH9H0QeC<>Dso@mV!hyZAs1a3 z!mJqAfkOwh*17GizWwKR%V{w<6WkD@Wrv?3_(tPNxS;WOnErz zXSw}rNd9IDsH))E;+AmpLM${q`d(2b%SWvwwgJ~0m&a}_6I|LCc1IjL8q)A0+6WaR z^XoT>0=e*FAtvPM44QJWeLCiNUw27Iw@yu~E`n8DXo&5`;)SC@g5*}oyluO6Os)%( z2S-?HPObck>OGni@Jq%`^!ZeI0a>eaNV|E-L~}|ILvS(o+9Rq{`yA|L`h#rzKI>|Z zpRFdHs9|bPYO}=MC@a&R%ZFw zhyPFkQ^X9HeV@k@^3w}T3=5N9LVs;N8mmK{tuFrd?hsYp%&(tb`;FKscG#M*%5sm> zCQ3$H`ng#qo)kQ7sF6D+pC%-PG>9=$`e6-XJ!E#)Zu+cL!q=FVMzwk?jsC&lZ}`QW z_4t_DrWUIU=p8uicNs-u&kxR|;Mybp|gzxV6C*|M?L;W${W8PP)6S}LmA%fq9F zW(YKBrJFdS&U&&II=!lPchegLCVB$lwyY+Aibm@_ItK!EfW&54B z(Ch?uvTAef3O55gJRax_;$s2_1VZ0+VWJ(yF`P|z(W-@AuzecKcJn1#$9-3b=$)b< ztHGD3!8L%`49=aKAto}?bz|+Of9oFpO;7PkM*7z63#0VEH;y3}qj1tfVOW5c!^Xx2 zp<~#&7~P8)vTzq=W1hXL&)NcBn6;@Vh*(?$JWVj9q7Uz(PD+2J4@y7GGMQan$1n>k z2)a-~l(@H#VuEQ?4ZSy@;@=b(uizA&N@mzA{;^6{~P|7&5>|*wC>T$)CNWP z5`{_RYQ=6n9Iaw)3>Jd+-Y#Q7A1RQ>U`lS*cZ->v=>@wDiA8)^gvMqx!Sa9ddq(cvn(o zp7yMxk$QX)^x8ZeEsk?0J9`d32#%YQ!{xKL`5?-mGIe!D!X*29&}etcz6U%_d;NK8 z?o{#a`*~{Z&FoIyj9Ntyn0$OYvW^8wOaHm$;DA%01cY?G6t_|Kdpn3trRm4!_-w|O zo2x@-J;8P(nEO7&kQffPB@7UOH;0T0+%if2^w`*|ey3xH__dOmlvE?StKsUHWhFhi zFG?MC$xE#hCr&`RM)YF{yy)rKLv)6(1!elZI~Q-PNPl zt#H5|YI0DKun_3K0!)EI*V1AT(?r4D!gL*;qWfpsP~aQcYE2|-m^xy}K(DN#B4{>b zP#mLXA<3?3_kc*_%SoJH>IYN@Of)ip$7<}{O;VwzxN4J5gUCL$D1@$oFQ0kw$i~fZ z27Utt9U&Iqqb!C;s^)-z{n`fMb%tLU^|}}<-a~jj2&Z`u)jUu7lhn=BNp!OcpaN;L zDpeA5VBdR{3sUgMjO3w1eWNZtSKonjgq7!PMlFv_gzncbUuy=D<22bgbC?v-DAgs6ZXTjeWrb zH#IHH%R@1QT?m#%gAZ0Bv__KHLN)xf{pIdx97GS9jTv}5h(k=pfpItjj4n1!82e`G z`qitzwKgfJ}o2yzKVk zYt7{D-d@;_N%=`aK4=O=fXW4m>32eZwF3DR&dK_Q8rSPc!J9YlZS#SBjl@6(KZ}ny z7LP_}ib!MznB@Zp!t_#mXWYZd)rrZ!+=uVq2Hy zRzUo{iq(xfV1N}8m=8<4onj7qm@Zf9#B8AR$&)#8O%4I)KNbg_Ez0tB>OHp|eHvpv v=I#3b)BOK(N&IhMK&quwsu4Q7MH*_&d#`Iv?jz3EB{_c7P&-G{`u_g`2X*cg literal 0 HcmV?d00001 diff --git a/examples/pdf-server/mcp-app.html b/examples/pdf-server/mcp-app.html new file mode 100644 index 0000000..cf21723 --- /dev/null +++ b/examples/pdf-server/mcp-app.html @@ -0,0 +1,78 @@ + + + + + + PDF Viewer + + +
+ +
+
+

Loading PDF...

+ +

+
+ + + + + + +
+ + + + diff --git a/examples/pdf-server/package.json b/examples/pdf-server/package.json new file mode 100644 index 0000000..40a3d7c --- /dev/null +++ b/examples/pdf-server/package.json @@ -0,0 +1,45 @@ +{ + "name": "@modelcontextprotocol/server-pdf", + "version": "0.4.0", + "type": "module", + "description": "MCP server for loading and extracting text from PDF files with chunked pagination and interactive viewer", + "repository": { + "type": "git", + "url": "https://github.com/modelcontextprotocol/ext-apps", + "directory": "examples/pdf-server" + }, + "license": "MIT", + "main": "server.ts", + "files": [ + "server.ts", + "server-utils.ts", + "src", + "dist" + ], + "scripts": { + "build": "tsc --noEmit && cross-env INPUT=mcp-app.html vite build", + "watch": "cross-env NODE_ENV=development INPUT=mcp-app.html vite build --watch", + "serve": "bun --watch server.ts", + "serve:http": "bun --watch server.ts", + "dev": "cross-env NODE_ENV=development concurrently 'npm run watch' 'npm run serve:http'", + "start": "npm run serve" + }, + "dependencies": { + "@modelcontextprotocol/ext-apps": "^0.4.0", + "@modelcontextprotocol/sdk": "^1.24.0", + "pdfjs-dist": "^5.0.0", + "zod": "^4.1.13" + }, + "devDependencies": { + "@types/cors": "^2.8.19", + "@types/express": "^5.0.0", + "@types/node": "^22.0.0", + "concurrently": "^9.2.1", + "cors": "^2.8.5", + "cross-env": "^10.1.0", + "express": "^5.1.0", + "typescript": "^5.9.3", + "vite": "^6.0.0", + "vite-plugin-singlefile": "^2.3.0" + } +} diff --git a/examples/pdf-server/screenshot.png b/examples/pdf-server/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..bdb8f17600c0d211fd32bb60de5aceeb8f394f95 GIT binary patch literal 68443 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 McpServer, + options: ServerOptions, +): Promise { + const { port, name = "MCP Server" } = options; + + const app = createMcpExpressApp({ host: "0.0.0.0" }); + app.use(cors()); + + app.all("/mcp", async (req: Request, res: Response) => { + const server = createServer(); + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: undefined, + }); + + res.on("close", () => { + transport.close().catch(() => {}); + server.close().catch(() => {}); + }); + + try { + await server.connect(transport); + await transport.handleRequest(req, res, req.body); + } catch (error) { + console.error("MCP error:", error); + if (!res.headersSent) { + res.status(500).json({ + jsonrpc: "2.0", + error: { code: -32603, message: "Internal server error" }, + id: null, + }); + } + } + }); + + const httpServer = app.listen(port, (err) => { + if (err) { + console.error("Failed to start server:", err); + process.exit(1); + } + console.log(`${name} listening on http://localhost:${port}/mcp`); + }); + + const shutdown = () => { + console.log("\nShutting down..."); + httpServer.close(() => process.exit(0)); + }; + + process.on("SIGINT", shutdown); + process.on("SIGTERM", shutdown); +} diff --git a/examples/pdf-server/server.ts b/examples/pdf-server/server.ts new file mode 100644 index 0000000..964c1b0 --- /dev/null +++ b/examples/pdf-server/server.ts @@ -0,0 +1,250 @@ +/** + * PDF MCP Server - Didactic Example + * + * Demonstrates: + * - Chunked data through size-limited tool responses + * - Model context updates (current page text + selection) + * - Display modes: fullscreen with scrolling vs inline with resize + * - External link opening (openLink) + */ +import { + registerAppResource, + registerAppTool, + RESOURCE_MIME_TYPE, +} from "@modelcontextprotocol/ext-apps/server"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import type { + CallToolResult, + ReadResourceResult, +} from "@modelcontextprotocol/sdk/types.js"; +import fs from "node:fs/promises"; +import path from "node:path"; +import { z } from "zod"; + +import { + buildPdfIndex, + findEntryByUrl, + createEntry, + isArxivUrl, + isFileUrl, + toFileUrl, + normalizeArxivUrl, +} from "./src/pdf-indexer.js"; +import { loadPdfBytesChunk, populatePdfMetadata } from "./src/pdf-loader.js"; +import { + ReadPdfBytesInputSchema, + PdfBytesChunkSchema, + type PdfIndex, +} from "./src/types.js"; +import { startServer } from "./server-utils.js"; + +const DIST_DIR = path.join(import.meta.dirname, "dist"); +const RESOURCE_URI = "ui://pdf-viewer/mcp-app.html"; +const DEFAULT_PDF = "https://arxiv.org/pdf/1706.03762"; // Attention Is All You Need + +let pdfIndex: PdfIndex | null = null; + +export function createServer(): McpServer { + const server = new McpServer({ name: "PDF Server", version: "1.0.0" }); + + // Tool: list_pdfs + server.tool( + "list_pdfs", + "List indexed PDFs", + {}, + async (): Promise => { + if (!pdfIndex) throw new Error("Not initialized"); + return { + content: [ + { type: "text", text: JSON.stringify(pdfIndex.entries, null, 2) }, + ], + structuredContent: { entries: pdfIndex.entries }, + }; + }, + ); + + // Tool: read_pdf_bytes (app-only) - Chunked binary loading + registerAppTool( + server, + "read_pdf_bytes", + { + title: "Read PDF Bytes", + description: "Load binary data in chunks", + inputSchema: ReadPdfBytesInputSchema.shape, + outputSchema: PdfBytesChunkSchema, + _meta: { ui: { visibility: ["app"] } }, + }, + async (args: unknown): Promise => { + if (!pdfIndex) throw new Error("Not initialized"); + const { + url: rawUrl, + offset, + byteCount, + } = ReadPdfBytesInputSchema.parse(args); + const url = isArxivUrl(rawUrl) ? normalizeArxivUrl(rawUrl) : rawUrl; + let entry = findEntryByUrl(pdfIndex, url); + + // Dynamically add arxiv URLs (handles server restart between display_pdf and read_pdf_bytes) + if (!entry) { + if (isFileUrl(url)) { + throw new Error("File URLs must be in the initial list"); + } + if (!isArxivUrl(url)) { + throw new Error(`PDF not found: ${url}`); + } + entry = createEntry(url); + await populatePdfMetadata(entry); + pdfIndex.entries.push(entry); + } + + const chunk = await loadPdfBytesChunk(entry, offset, byteCount); + return { + content: [ + { + type: "text", + text: `${chunk.byteCount} bytes at ${chunk.offset}/${chunk.totalBytes}`, + }, + ], + structuredContent: chunk, + }; + }, + ); + + // Tool: display_pdf - Interactive viewer with UI + registerAppTool( + server, + "display_pdf", + { + title: "Display PDF", + description: `Display an interactive PDF viewer in the chat. + +Use this tool when the user asks to view, display, read, or open a PDF. Accepts: +- URLs from list_pdfs (preloaded PDFs) +- Any arxiv.org URL (loaded dynamically) + +The viewer supports zoom, navigation, text selection, and fullscreen mode.`, + inputSchema: { + url: z + .string() + .default(DEFAULT_PDF) + .describe("PDF URL (arxiv.org for dynamic loading)"), + page: z.number().min(1).default(1).describe("Initial page"), + }, + outputSchema: z.object({ + url: z.string(), + title: z.string().optional(), + pageCount: z.number(), + initialPage: z.number(), + }), + _meta: { ui: { resourceUri: RESOURCE_URI } }, + }, + async ({ url: rawUrl, page }): Promise => { + if (!pdfIndex) throw new Error("Not initialized"); + + // Normalize arxiv URLs to PDF format + const url = isArxivUrl(rawUrl) ? normalizeArxivUrl(rawUrl) : rawUrl; + + let entry = findEntryByUrl(pdfIndex, url); + + if (!entry) { + if (isFileUrl(url)) { + throw new Error("File URLs must be in the initial list"); + } + if (!isArxivUrl(url)) { + throw new Error(`Only arxiv.org URLs can be loaded dynamically`); + } + + entry = createEntry(url); + await populatePdfMetadata(entry); + pdfIndex.entries.push(entry); + } + + const result = { + url: entry.url, + title: entry.metadata.title, + pageCount: entry.metadata.pageCount, + initialPage: Math.min(page, entry.metadata.pageCount), + }; + + return { + content: [ + { + type: "text", + text: `Displaying interactive PDF viewer${entry.metadata.title ? ` for "${entry.metadata.title}"` : ""} (${entry.url}, ${entry.metadata.pageCount} pages)`, + }, + ], + structuredContent: result, + }; + }, + ); + + // Resource: UI HTML + registerAppResource( + server, + RESOURCE_URI, + RESOURCE_URI, + { mimeType: RESOURCE_MIME_TYPE }, + async (): Promise => { + const html = await fs.readFile( + path.join(DIST_DIR, "mcp-app.html"), + "utf-8", + ); + return { + contents: [ + { uri: RESOURCE_URI, mimeType: RESOURCE_MIME_TYPE, text: html }, + ], + }; + }, + ); + + return server; +} + +// CLI +function parseArgs(): { urls: string[]; stdio: boolean } { + const args = process.argv.slice(2); + const urls: string[] = []; + let stdio = false; + + for (const arg of args) { + if (arg === "--stdio") { + stdio = true; + } else if (!arg.startsWith("-")) { + // Convert local paths to file:// URLs, normalize arxiv URLs + let url = arg; + if ( + !arg.startsWith("http://") && + !arg.startsWith("https://") && + !arg.startsWith("file://") + ) { + url = toFileUrl(arg); + } else if (isArxivUrl(arg)) { + url = normalizeArxivUrl(arg); + } + urls.push(url); + } + } + + return { urls: urls.length > 0 ? urls : [DEFAULT_PDF], stdio }; +} + +async function main() { + const { urls, stdio } = parseArgs(); + + console.error(`[pdf-server] Initializing with ${urls.length} PDF(s)...`); + pdfIndex = await buildPdfIndex(urls); + console.error(`[pdf-server] Ready`); + + if (stdio) { + await createServer().connect(new StdioServerTransport()); + } else { + const port = parseInt(process.env.PORT ?? "3001", 10); + await startServer(createServer, { port, name: "PDF Server" }); + } +} + +main().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/examples/pdf-server/src/global.css b/examples/pdf-server/src/global.css new file mode 100644 index 0000000..44f8c67 --- /dev/null +++ b/examples/pdf-server/src/global.css @@ -0,0 +1,12 @@ +* { + box-sizing: border-box; +} + +html, body { + font-family: system-ui, -apple-system, sans-serif; + font-size: 1rem; + margin: 0; + padding: 0; + height: 100%; + width: 100%; +} diff --git a/examples/pdf-server/src/mcp-app.css b/examples/pdf-server/src/mcp-app.css new file mode 100644 index 0000000..94093b2 --- /dev/null +++ b/examples/pdf-server/src/mcp-app.css @@ -0,0 +1,292 @@ +body { + overscroll-behavior-x: none; +} + +.main { + display: flex; + flex-direction: column; + height: 100%; + width: 100%; + background: var(--bg100, #f5f5f5); + color: var(--text000, #1a1a1a); + overflow: hidden; /* Prevent scrollbars in inline mode - we request exact size */ + border-radius: 0.75rem; + border: 1px solid var(--bg200, rgba(0, 0, 0, 0.08)); +} + +/* Loading State */ +.loading { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + flex: 1; + gap: 0.75rem; + padding: 2rem; +} + +.spinner { + width: 24px; + height: 24px; + border: 2px solid var(--bg200, #e0e0e0); + border-top-color: var(--text100, #888); + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +#loading-text { + font-size: 0.8rem; + color: var(--text100, #888); +} + +/* Progress Bar */ +.progress-container { + width: 100%; + max-width: 200px; + height: 3px; + background: var(--bg200, #e0e0e0); + border-radius: 2px; + overflow: hidden; +} + +.progress-bar { + height: 100%; + background: var(--text100, #888); + width: 0%; + transition: width 0.15s ease-out; +} + +.progress-text { + font-size: 0.7rem; + color: var(--text200, #aaa); +} + +/* Error State */ +.error { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + gap: 1rem; + padding: 2rem; + text-align: center; +} + +.error-icon { + font-size: 3rem; +} + +#error-message { + color: var(--text200, #999); + max-width: 400px; +} + +/* Viewer Container */ +.viewer { + display: flex; + flex-direction: column; + flex: 1; + overflow: visible; +} + +/* Toolbar */ +.toolbar { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.5rem 1rem; + background: var(--bg000, #ffffff); + border-bottom: 1px solid var(--bg200, #e0e0e0); + flex-shrink: 0; + gap: 0.5rem; + height: 48px; + box-sizing: border-box; +} + +.toolbar-left { + flex: 1; + min-width: 0; + overflow: hidden; +} + +.pdf-title { + font-weight: 500; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 0.9rem; + display: block; + max-width: 100%; +} + +.toolbar-center { + display: flex; + align-items: center; + gap: 0.5rem; + flex-shrink: 0; +} + +.toolbar-right { + display: flex; + align-items: center; + gap: 0.25rem; + flex-shrink: 0; +} + +/* Page Navigation */ +.page-nav { + display: flex; + align-items: center; + gap: 0.25rem; +} + +.page-input { + width: 50px; + padding: 0.25rem 0.5rem; + border: 1px solid var(--bg200, #e0e0e0); + border-radius: 4px; + font-size: 0.85rem; + text-align: center; + background: var(--bg000, #ffffff); + color: var(--text000, #1a1a1a); +} + +.page-input:focus { + outline: none; + border-color: var(--text100, #666); +} + +.page-input::-webkit-outer-spin-button, +.page-input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} +.page-input[type="number"] { + -moz-appearance: textfield; +} + +.total-pages { + font-size: 0.85rem; + color: var(--text100, #666); + white-space: nowrap; +} + +.nav-btn, +.zoom-btn, +.fullscreen-btn { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border: 1px solid var(--bg200, #e0e0e0); + border-radius: 4px; + background: var(--bg000, #ffffff); + color: var(--text000, #1a1a1a); + cursor: pointer; + font-size: 1rem; + transition: all 0.15s ease; +} + +.nav-btn:hover:not(:disabled), +.zoom-btn:hover:not(:disabled), +.fullscreen-btn:hover { + background: var(--bg100, #f5f5f5); + border-color: var(--bg300, #ccc); +} + +.nav-btn:disabled, +.zoom-btn:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.zoom-level { + font-size: 0.8rem; + color: var(--text100, #666); + min-width: 50px; + text-align: center; +} + +/* Single Page Canvas Container */ +.canvas-container { + flex: 1; + overflow: visible; + display: flex; + justify-content: center; + align-items: flex-start; + padding: 1rem; + background: var(--bg200, #e0e0e0); +} + +.page-wrapper { + position: relative; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + background: white; +} + +#pdf-canvas { + display: block; +} + +/* Text Layer for Selection */ +.text-layer { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + overflow: hidden; + opacity: 0.2; + line-height: 1; + text-size-adjust: none; + forced-color-adjust: none; + transform-origin: 0 0; + z-index: 2; +} + +.text-layer span, +.text-layer br { + color: transparent; + position: absolute; + white-space: pre; + cursor: text; + transform-origin: 0% 0%; +} + +.text-layer ::selection { + background: rgba(0, 0, 255, 0.3); +} + +.text-layer > span { + pointer-events: all; +} + +/* Fullscreen mode */ +.main.fullscreen { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 1000; + overflow: hidden; /* No scrolling on main - only canvas-container scrolls */ + border-radius: 0; + border: none; +} + +.main.fullscreen .viewer { + min-height: 0; /* Allow flex item to shrink below content size */ +} + +.main.fullscreen .canvas-container { + min-height: 0; /* Allow flex item to shrink below content size */ + overflow: auto; /* Scroll within the document area only */ +} diff --git a/examples/pdf-server/src/mcp-app.ts b/examples/pdf-server/src/mcp-app.ts new file mode 100644 index 0000000..d56c3d8 --- /dev/null +++ b/examples/pdf-server/src/mcp-app.ts @@ -0,0 +1,795 @@ +/** + * PDF Viewer MCP App + * + * Interactive PDF viewer with single-page display. + * - Fixed height (no auto-resize) + * - Text selection via PDF.js TextLayer + * - Page navigation, zoom + */ +import { App, type McpUiHostContext } from "@modelcontextprotocol/ext-apps"; +import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import * as pdfjsLib from "pdfjs-dist"; +import { TextLayer } from "pdfjs-dist"; +import "./global.css"; +import "./mcp-app.css"; + +// Configure PDF.js worker +pdfjsLib.GlobalWorkerOptions.workerSrc = new URL( + "pdfjs-dist/build/pdf.worker.mjs", + import.meta.url, +).href; + +const log = { + info: console.log.bind(console, "[PDF-VIEWER]"), + error: console.error.bind(console, "[PDF-VIEWER]"), +}; + +// State +let pdfDocument: pdfjsLib.PDFDocumentProxy | null = null; +let pdfBytes: Uint8Array | null = null; +let currentPage = 1; +let totalPages = 0; +let scale = 1.0; +let pdfUrl = ""; +let pdfTitle: string | undefined; +let currentRenderTask: { cancel: () => void } | null = null; + +// DOM Elements +const mainEl = document.querySelector(".main") as HTMLElement; +const loadingEl = document.getElementById("loading")!; +const loadingTextEl = document.getElementById("loading-text")!; +const errorEl = document.getElementById("error")!; +const errorMessageEl = document.getElementById("error-message")!; +const viewerEl = document.getElementById("viewer")!; +const canvasContainerEl = document.querySelector(".canvas-container")!; +const canvasEl = document.getElementById("pdf-canvas") as HTMLCanvasElement; +const textLayerEl = document.getElementById("text-layer")!; +const titleEl = document.getElementById("pdf-title")!; +const pageInputEl = document.getElementById("page-input") as HTMLInputElement; +const totalPagesEl = document.getElementById("total-pages")!; +const prevBtn = document.getElementById("prev-btn") as HTMLButtonElement; +const nextBtn = document.getElementById("next-btn") as HTMLButtonElement; +const zoomOutBtn = document.getElementById("zoom-out-btn") as HTMLButtonElement; +const zoomInBtn = document.getElementById("zoom-in-btn") as HTMLButtonElement; +const zoomLevelEl = document.getElementById("zoom-level")!; +const fullscreenBtn = document.getElementById( + "fullscreen-btn", +) as HTMLButtonElement; +const progressContainerEl = document.getElementById("progress-container")!; +const progressBarEl = document.getElementById("progress-bar")!; +const progressTextEl = document.getElementById("progress-text")!; + +// Track current display mode +let currentDisplayMode: "inline" | "fullscreen" = "inline"; + +// Layout constants are no longer used - we calculate dynamically from actual element dimensions + +/** + * Request the host to resize the app to fit the current PDF page. + * Only applies in inline mode - fullscreen mode uses scrolling. + */ +function requestFitToContent() { + if (currentDisplayMode === "fullscreen") { + return; // Fullscreen uses scrolling + } + + const canvasHeight = canvasEl.height; + if (canvasHeight <= 0) { + return; // No content yet + } + + // Get actual element dimensions + const canvasContainerEl = document.querySelector( + ".canvas-container", + ) as HTMLElement; + const pageWrapperEl = document.querySelector(".page-wrapper") as HTMLElement; + const toolbarEl = document.querySelector(".toolbar") as HTMLElement; + + if (!canvasContainerEl || !toolbarEl || !pageWrapperEl) { + return; + } + + // Get computed styles + const containerStyle = getComputedStyle(canvasContainerEl); + const paddingTop = parseFloat(containerStyle.paddingTop); + const paddingBottom = parseFloat(containerStyle.paddingBottom); + + // Calculate required height: + // toolbar + padding-top + page-wrapper height + padding-bottom + buffer + const toolbarHeight = toolbarEl.offsetHeight; + const pageWrapperHeight = pageWrapperEl.offsetHeight; + const BUFFER = 10; // Buffer for sub-pixel rounding and browser quirks + const totalHeight = + toolbarHeight + paddingTop + pageWrapperHeight + paddingBottom + BUFFER; + + app.sendSizeChanged({ height: totalHeight }); +} + +// Create app instance +// autoResize disabled - app fills its container, doesn't request size changes +const app = new App( + { name: "PDF Viewer", version: "1.0.0" }, + {}, + { autoResize: false }, +); + +// UI State functions +function showLoading(text: string) { + loadingTextEl.textContent = text; + loadingEl.style.display = "flex"; + errorEl.style.display = "none"; + viewerEl.style.display = "none"; +} + +function showError(message: string) { + errorMessageEl.textContent = message; + loadingEl.style.display = "none"; + errorEl.style.display = "block"; + viewerEl.style.display = "none"; +} + +function showViewer() { + loadingEl.style.display = "none"; + errorEl.style.display = "none"; + viewerEl.style.display = "flex"; +} + +function updateControls() { + // Show URL with CSS ellipsis, full URL as tooltip, clickable to open + titleEl.textContent = pdfUrl; + titleEl.title = pdfUrl; + titleEl.style.textDecoration = "underline"; + titleEl.style.cursor = "pointer"; + titleEl.onclick = () => app.openLink({ url: pdfUrl }); + pageInputEl.value = String(currentPage); + pageInputEl.max = String(totalPages); + totalPagesEl.textContent = `of ${totalPages}`; + prevBtn.disabled = currentPage <= 1; + nextBtn.disabled = currentPage >= totalPages; + zoomLevelEl.textContent = `${Math.round(scale * 100)}%`; +} + +/** + * Format page text with optional selection, truncating intelligently. + * - Centers window around selection when truncating + * - Adds markers where text is elided + * - If selection itself is too long, truncates inside: ... + */ +function formatPageContent( + text: string, + maxLength: number, + selection?: { start: number; end: number }, +): string { + const T = ""; + + // No truncation needed + if (text.length <= maxLength) { + if (!selection) return text; + return ( + text.slice(0, selection.start) + + `${text.slice(selection.start, selection.end)}` + + text.slice(selection.end) + ); + } + + // Truncation needed, no selection - just truncate end + if (!selection) { + return text.slice(0, maxLength) + "\n" + T; + } + + // Calculate budgets + const selLen = selection.end - selection.start; + const overhead = "".length + T.length * 2 + 4; + const contextBudget = maxLength - overhead; + + // Selection too long - truncate inside the selection tags + if (selLen > contextBudget) { + const keepLen = Math.max(100, contextBudget); + const halfKeep = Math.floor(keepLen / 2); + const selStart = text.slice(selection.start, selection.start + halfKeep); + const selEnd = text.slice(selection.end - halfKeep, selection.end); + return ( + T + `${T}${selStart}...${selEnd}${T}` + T + ); + } + + // Selection fits - center it with context + const remainingBudget = contextBudget - selLen; + const beforeBudget = Math.floor(remainingBudget / 2); + const afterBudget = remainingBudget - beforeBudget; + + const windowStart = Math.max(0, selection.start - beforeBudget); + const windowEnd = Math.min(text.length, selection.end + afterBudget); + + const adjStart = selection.start - windowStart; + const adjEnd = selection.end - windowStart; + const windowText = text.slice(windowStart, windowEnd); + + return ( + (windowStart > 0 ? T + "\n" : "") + + windowText.slice(0, adjStart) + + `${windowText.slice(adjStart, adjEnd)}` + + windowText.slice(adjEnd) + + (windowEnd < text.length ? "\n" + T : "") + ); +} + +/** + * Find selection position in page text using fuzzy matching. + * TextLayer spans may lack spaces between them, so we try both exact and spaceless match. + */ +function findSelectionInText( + pageText: string, + selectedText: string, +): { start: number; end: number } | undefined { + if (!selectedText || selectedText.length <= 2) return undefined; + + // Try exact match + let start = pageText.indexOf(selectedText); + if (start >= 0) { + return { start, end: start + selectedText.length }; + } + + // Try spaceless match (TextLayer spans may not have spaces) + const noSpaceSel = selectedText.replace(/\s+/g, ""); + const noSpaceText = pageText.replace(/\s+/g, ""); + const noSpaceStart = noSpaceText.indexOf(noSpaceSel); + if (noSpaceStart >= 0) { + // Map back to approximate position in original + start = Math.floor((noSpaceStart / noSpaceText.length) * pageText.length); + return { start, end: start + selectedText.length }; + } + + return undefined; +} + +// Extract text from current page and update model context as markdown +async function updatePageContext() { + if (!pdfDocument) return; + + try { + const page = await pdfDocument.getPage(currentPage); + const textContent = await page.getTextContent(); + const pageText = (textContent.items as Array<{ str?: string }>) + .map((item) => item.str || "") + .join(" ") + .replace(/\s+/g, " ") + .trim(); + + // Find selection position + const sel = window.getSelection(); + const selectedText = sel?.toString().replace(/\s+/g, " ").trim(); + const selection = selectedText + ? findSelectionInText(pageText, selectedText) + : undefined; + + if (selection) { + log.info( + "Selection found:", + selectedText?.slice(0, 30), + "at", + selection.start, + ); + } + + // Format content with selection and truncation + const content = formatPageContent(pageText, 5000, selection); + + const markdown = `--- +title: ${pdfTitle || ""} +url: ${pdfUrl} +current-page: ${currentPage}/${totalPages} +--- + +${content}`; + + app.updateModelContext({ content: [{ type: "text", text: markdown }] }); + } catch (err) { + log.error("Error updating context:", err); + } +} + +// Render state - prevents concurrent renders +let isRendering = false; +let pendingPage: number | null = null; + +// Render current page with text layer for selection +async function renderPage() { + if (!pdfDocument) return; + + // If already rendering, queue this page for later + if (isRendering) { + pendingPage = currentPage; + // Cancel current render to speed up + if (currentRenderTask) { + currentRenderTask.cancel(); + } + return; + } + + isRendering = true; + pendingPage = null; + + try { + const pageToRender = currentPage; + const page = await pdfDocument.getPage(pageToRender); + const viewport = page.getViewport({ scale }); + + // Account for retina displays + const dpr = window.devicePixelRatio || 1; + const ctx = canvasEl.getContext("2d")!; + + // Set canvas size in pixels (scaled for retina) + canvasEl.width = viewport.width * dpr; + canvasEl.height = viewport.height * dpr; + + // Set display size in CSS pixels + canvasEl.style.width = `${viewport.width}px`; + canvasEl.style.height = `${viewport.height}px`; + + // Scale context for retina + ctx.scale(dpr, dpr); + + // Clear and setup text layer + textLayerEl.innerHTML = ""; + textLayerEl.style.width = `${viewport.width}px`; + textLayerEl.style.height = `${viewport.height}px`; + + // Render canvas - track the task so we can cancel it + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const renderTask = (page.render as any)({ + canvasContext: ctx, + viewport, + }); + currentRenderTask = renderTask; + + try { + await renderTask.promise; + } catch (renderErr) { + // Ignore RenderingCancelledException - it's expected when we cancel + if ( + renderErr instanceof Error && + renderErr.name === "RenderingCancelledException" + ) { + log.info("Render cancelled"); + return; + } + throw renderErr; + } finally { + currentRenderTask = null; + } + + // Only continue if this is still the page we want + if (pageToRender !== currentPage) { + return; + } + + // Render text layer for selection + const textContent = await page.getTextContent(); + const textLayer = new TextLayer({ + textContentSource: textContent, + container: textLayerEl, + viewport, + }); + await textLayer.render(); + + updateControls(); + updatePageContext(); + + // Request host to resize app to fit content (inline mode only) + requestFitToContent(); + } catch (err) { + log.error("Error rendering page:", err); + showError(`Failed to render page ${currentPage}`); + } finally { + isRendering = false; + + // If there's a pending page, render it now + if (pendingPage !== null && pendingPage !== currentPage) { + currentPage = pendingPage; + renderPage(); + } else if (pendingPage === currentPage) { + // Re-render the same page (e.g., after zoom change during render) + renderPage(); + } + } +} + +// Page persistence +function getStorageKey(): string | null { + if (!pdfUrl) return null; + const ctx = app.getHostContext(); + const toolId = ctx?.toolInfo?.id ?? pdfUrl; + return `pdf:${pdfUrl}:${toolId}`; +} + +function saveCurrentPage() { + const key = getStorageKey(); + log.info("saveCurrentPage: key=", key, "page=", currentPage); + if (key) { + try { + localStorage.setItem(key, String(currentPage)); + log.info("saveCurrentPage: saved successfully"); + } catch (err) { + log.error("saveCurrentPage: error", err); + } + } +} + +function loadSavedPage(): number | null { + const key = getStorageKey(); + log.info("loadSavedPage: key=", key); + if (!key) return null; + try { + const saved = localStorage.getItem(key); + log.info("loadSavedPage: saved value=", saved); + if (saved) { + const page = parseInt(saved, 10); + if (!isNaN(page) && page >= 1) { + log.info("loadSavedPage: returning page=", page); + return page; + } + } + } catch (err) { + log.error("loadSavedPage: error", err); + } + log.info("loadSavedPage: returning null"); + return null; +} + +// Navigation +function goToPage(page: number) { + const targetPage = Math.max(1, Math.min(page, totalPages)); + if (targetPage !== currentPage) { + currentPage = targetPage; + saveCurrentPage(); + renderPage(); + } + pageInputEl.value = String(currentPage); +} + +function prevPage() { + goToPage(currentPage - 1); +} + +function nextPage() { + goToPage(currentPage + 1); +} + +function zoomIn() { + scale = Math.min(scale + 0.25, 3.0); + renderPage(); +} + +function zoomOut() { + scale = Math.max(scale - 0.25, 0.5); + renderPage(); +} + +function resetZoom() { + scale = 1.0; + renderPage(); +} + +async function toggleFullscreen() { + const ctx = app.getHostContext(); + if (!ctx?.availableDisplayModes?.includes("fullscreen")) { + log.info("Fullscreen not available"); + return; + } + + const newMode = currentDisplayMode === "fullscreen" ? "inline" : "fullscreen"; + log.info("Requesting display mode:", newMode); + + try { + const result = await app.requestDisplayMode({ mode: newMode }); + log.info("Display mode result:", result); + currentDisplayMode = result.mode as "inline" | "fullscreen"; + updateFullscreenButton(); + } catch (err) { + log.error("Failed to change display mode:", err); + } +} + +function updateFullscreenButton() { + fullscreenBtn.textContent = currentDisplayMode === "fullscreen" ? "⛶" : "⛶"; + fullscreenBtn.title = + currentDisplayMode === "fullscreen" ? "Exit fullscreen" : "Fullscreen"; +} + +// Event listeners +prevBtn.addEventListener("click", prevPage); +nextBtn.addEventListener("click", nextPage); +zoomOutBtn.addEventListener("click", zoomOut); +zoomInBtn.addEventListener("click", zoomIn); +fullscreenBtn.addEventListener("click", toggleFullscreen); + +pageInputEl.addEventListener("change", () => { + const page = parseInt(pageInputEl.value, 10); + if (!isNaN(page)) { + goToPage(page); + } else { + pageInputEl.value = String(currentPage); + } +}); + +pageInputEl.addEventListener("keydown", (e) => { + if (e.key === "Enter") { + pageInputEl.blur(); + } +}); + +// Keyboard navigation +document.addEventListener("keydown", (e) => { + if (document.activeElement === pageInputEl) return; + + // Ctrl/Cmd+0 to reset zoom + if ((e.ctrlKey || e.metaKey) && e.key === "0") { + resetZoom(); + e.preventDefault(); + return; + } + + switch (e.key) { + case "Escape": + if (currentDisplayMode === "fullscreen") { + toggleFullscreen(); + e.preventDefault(); + } + break; + case "ArrowLeft": + case "PageUp": + prevPage(); + e.preventDefault(); + break; + case "ArrowRight": + case "PageDown": + case " ": + nextPage(); + e.preventDefault(); + break; + case "+": + case "=": + zoomIn(); + e.preventDefault(); + break; + case "-": + zoomOut(); + e.preventDefault(); + break; + } +}); + +// Update context when text selection changes (debounced) +let selectionUpdateTimeout: ReturnType | null = null; +document.addEventListener("selectionchange", () => { + if (selectionUpdateTimeout) clearTimeout(selectionUpdateTimeout); + selectionUpdateTimeout = setTimeout(() => { + const sel = window.getSelection(); + const text = sel?.toString().trim(); + if (text && text.length > 2) { + log.info("Selection changed:", text.slice(0, 50)); + updatePageContext(); + } + }, 300); +}); + +// Horizontal scroll/swipe to change pages (disabled when zoomed) +let horizontalScrollAccumulator = 0; +const SCROLL_THRESHOLD = 50; + +canvasContainerEl.addEventListener( + "wheel", + (event) => { + const e = event as WheelEvent; + + // Only intercept horizontal scroll, let vertical scroll through + if (Math.abs(e.deltaX) <= Math.abs(e.deltaY)) return; + + // When zoomed, let natural panning happen (no page changes) + if (scale > 1.0) return; + + // At 100% zoom, handle page navigation + e.preventDefault(); + horizontalScrollAccumulator += e.deltaX; + if (horizontalScrollAccumulator > SCROLL_THRESHOLD) { + nextPage(); + horizontalScrollAccumulator = 0; + } else if (horizontalScrollAccumulator < -SCROLL_THRESHOLD) { + prevPage(); + horizontalScrollAccumulator = 0; + } + }, + { passive: false }, +); + +// Parse tool result +function parseToolResult(result: CallToolResult): { + url: string; + title?: string; + pageCount: number; + initialPage: number; +} | null { + return result.structuredContent as { + url: string; + title?: string; + pageCount: number; + initialPage: number; + } | null; +} + +// Chunked binary loading types +interface PdfBytesChunk { + url: string; + bytes: string; + offset: number; + byteCount: number; + totalBytes: number; + hasMore: boolean; +} + +// Update progress bar +function updateProgress(loaded: number, total: number) { + const percent = Math.round((loaded / total) * 100); + progressBarEl.style.width = `${percent}%`; + progressTextEl.textContent = `${(loaded / 1024).toFixed(0)} KB / ${(total / 1024).toFixed(0)} KB (${percent}%)`; +} + +// Load PDF in chunks with progress +async function loadPdfInChunks(urlToLoad: string): Promise { + const CHUNK_SIZE = 500 * 1024; // 500KB chunks + const chunks: Uint8Array[] = []; + let offset = 0; + let totalBytes = 0; + let hasMore = true; + + // Show progress UI + progressContainerEl.style.display = "block"; + updateProgress(0, 1); + + while (hasMore) { + const result = await app.callServerTool({ + name: "read_pdf_bytes", + arguments: { url: urlToLoad, offset, byteCount: CHUNK_SIZE }, + }); + + // Check for errors + if (result.isError) { + const errorText = result.content + ?.map((c) => ("text" in c ? c.text : "")) + .join(" "); + throw new Error(`Tool error: ${errorText}`); + } + + if (!result.structuredContent) { + throw new Error("No structuredContent in tool response"); + } + + const chunk = result.structuredContent as unknown as PdfBytesChunk; + totalBytes = chunk.totalBytes; + hasMore = chunk.hasMore; + + // Decode base64 chunk + const binaryString = atob(chunk.bytes); + const bytes = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + chunks.push(bytes); + + offset += chunk.byteCount; + updateProgress(offset, totalBytes); + } + + // Combine all chunks + const fullPdf = new Uint8Array(totalBytes); + let pos = 0; + for (const chunk of chunks) { + fullPdf.set(chunk, pos); + pos += chunk.length; + } + + log.info( + `PDF loaded: ${(totalBytes / 1024).toFixed(0)} KB in ${chunks.length} chunks`, + ); + return fullPdf; +} + +// Handle tool result +app.ontoolresult = async (result) => { + log.info("Received tool result:", result); + + const parsed = parseToolResult(result); + if (!parsed) { + showError("Invalid tool result"); + return; + } + + pdfUrl = parsed.url; + pdfTitle = parsed.title; + totalPages = parsed.pageCount; + + // Restore saved page or use initial page + const savedPage = loadSavedPage(); + currentPage = + savedPage && savedPage <= parsed.pageCount ? savedPage : parsed.initialPage; + + log.info( + "URL:", + pdfUrl, + "Pages:", + parsed.pageCount, + "Starting:", + currentPage, + ); + + showLoading("Loading PDF..."); + + try { + pdfBytes = await loadPdfInChunks(pdfUrl); + + showLoading("Rendering PDF..."); + + pdfDocument = await pdfjsLib.getDocument({ data: pdfBytes }).promise; + totalPages = pdfDocument.numPages; + + log.info("PDF loaded, pages:", totalPages); + + showViewer(); + renderPage(); + } catch (err) { + log.error("Error loading PDF:", err); + showError(err instanceof Error ? err.message : String(err)); + } +}; + +app.onerror = (err) => { + log.error("App error:", err); + showError(err instanceof Error ? err.message : String(err)); +}; + +function handleHostContextChanged(ctx: McpUiHostContext) { + log.info("Host context changed:", ctx); + + // Apply safe area insets + if (ctx.safeAreaInsets) { + mainEl.style.paddingTop = `${ctx.safeAreaInsets.top}px`; + mainEl.style.paddingRight = `${ctx.safeAreaInsets.right}px`; + mainEl.style.paddingBottom = `${ctx.safeAreaInsets.bottom}px`; + mainEl.style.paddingLeft = `${ctx.safeAreaInsets.left}px`; + } + + // Log containerDimensions for debugging + if (ctx.containerDimensions) { + log.info("Container dimensions:", ctx.containerDimensions); + } + + // Handle display mode changes + if (ctx.displayMode) { + const wasFullscreen = currentDisplayMode === "fullscreen"; + currentDisplayMode = ctx.displayMode as "inline" | "fullscreen"; + if (ctx.displayMode === "fullscreen") { + mainEl.classList.add("fullscreen"); + log.info("Fullscreen mode enabled"); + } else { + mainEl.classList.remove("fullscreen"); + log.info("Inline mode"); + // When exiting fullscreen, request resize to fit content + if (wasFullscreen && pdfDocument) { + requestFitToContent(); + } + } + updateFullscreenButton(); + } +} + +app.onhostcontextchanged = handleHostContextChanged; + +// Connect to host +app.connect().then(() => { + log.info("Connected to host"); + const ctx = app.getHostContext(); + if (ctx) { + handleHostContextChanged(ctx); + } +}); diff --git a/examples/pdf-server/src/pdf-indexer.ts b/examples/pdf-server/src/pdf-indexer.ts new file mode 100644 index 0000000..9dc1f99 --- /dev/null +++ b/examples/pdf-server/src/pdf-indexer.ts @@ -0,0 +1,57 @@ +/** + * PDF Indexer + */ +import path from "node:path"; +import type { PdfIndex, PdfEntry } from "./types.js"; +import { populatePdfMetadata } from "./pdf-loader.js"; + +/** Check if URL is from arxiv.org */ +export function isArxivUrl(url: string): boolean { + return /^https?:\/\/arxiv\.org\//.test(url); +} + +/** Normalize arxiv URL to PDF format */ +export function normalizeArxivUrl(url: string): string { + return url.replace(/arxiv\.org\/abs\//, "arxiv.org/pdf/"); +} + +/** Check if URL is a file:// URL */ +export function isFileUrl(url: string): boolean { + return url.startsWith("file://"); +} + +/** Convert local path to file:// URL */ +export function toFileUrl(filePath: string): string { + return `file://${path.resolve(filePath)}`; +} + +/** Create a PdfEntry from a URL */ +export function createEntry(url: string): PdfEntry { + return { + url, + metadata: { pageCount: 0, fileSizeBytes: 0 }, + }; +} + +/** Build index from a list of URLs */ +export async function buildPdfIndex(urls: string[]): Promise { + const entries: PdfEntry[] = []; + + for (const url of urls) { + console.error(`[indexer] Loading: ${url}`); + const entry = createEntry(url); + await populatePdfMetadata(entry); + entries.push(entry); + } + + console.error(`[indexer] Indexed ${entries.length} PDFs`); + return { entries }; +} + +/** Find entry by URL */ +export function findEntryByUrl( + index: PdfIndex, + url: string, +): PdfEntry | undefined { + return index.entries.find((e) => e.url === url); +} diff --git a/examples/pdf-server/src/pdf-loader.ts b/examples/pdf-server/src/pdf-loader.ts new file mode 100644 index 0000000..c5c7000 --- /dev/null +++ b/examples/pdf-server/src/pdf-loader.ts @@ -0,0 +1,136 @@ +/** + * PDF Loader - Loads PDFs and extracts content in chunks + * + * Demonstrates: + * - Chunked data loading with size limits + * - HTTP Range requests for streaming + * - Caching for repeated requests + */ +import fs from "node:fs/promises"; +import type { PdfEntry, PdfBytesChunk } from "./types.js"; +import { MAX_CHUNK_BYTES } from "./types.js"; +import { isFileUrl } from "./pdf-indexer.js"; + +// Cache for loaded PDFs +const pdfCache = new Map(); + +// Lazy-load pdfjs +let pdfjs: typeof import("pdfjs-dist"); +async function getPdfjs() { + if (!pdfjs) { + pdfjs = await import("pdfjs-dist/legacy/build/pdf.mjs"); + } + return pdfjs; +} + +// ============================================================================ +// PDF Data Loading +// ============================================================================ + +/** Fetch PDF data (with caching) */ +export async function loadPdfData(entry: PdfEntry): Promise { + const cached = pdfCache.get(entry.url); + if (cached) return cached; + + console.error(`[loader] Fetching: ${entry.url}`); + + let data: Uint8Array; + if (isFileUrl(entry.url)) { + const filePath = entry.url.replace("file://", ""); + data = new Uint8Array(await fs.readFile(filePath)); + } else { + const response = await fetch(entry.url); + if (!response.ok) { + throw new Error(`Failed to fetch: ${response.status}`); + } + data = new Uint8Array(await response.arrayBuffer()); + } + + pdfCache.set(entry.url, data); + return data; +} + +/** Try HTTP Range request for partial content */ +async function fetchRange( + url: string, + start: number, + end: number, +): Promise<{ data: Uint8Array; total: number } | null> { + try { + const res = await fetch(url, { + headers: { Range: `bytes=${start}-${end}` }, + }); + if (res.status !== 206) return null; + + const total = parseInt( + res.headers.get("Content-Range")?.split("/")[1] || "0", + ); + return { data: new Uint8Array(await res.arrayBuffer()), total }; + } catch { + return null; + } +} + +// ============================================================================ +// Chunked Binary Loading (demonstrates size-limited responses) +// ============================================================================ + +export async function loadPdfBytesChunk( + entry: PdfEntry, + offset = 0, + byteCount = MAX_CHUNK_BYTES, +): Promise { + // Try Range request first (streaming without full download) + if (!pdfCache.has(entry.url)) { + const range = await fetchRange(entry.url, offset, offset + byteCount - 1); + if (range) { + return { + url: entry.url, + bytes: Buffer.from(range.data).toString("base64"), + offset, + byteCount: range.data.length, + totalBytes: range.total, + hasMore: offset + range.data.length < range.total, + }; + } + } + + // Fallback: load full PDF and slice + const data = await loadPdfData(entry); + const chunk = data.slice(offset, offset + byteCount); + + return { + url: entry.url, + bytes: Buffer.from(chunk).toString("base64"), + offset, + byteCount: chunk.length, + totalBytes: data.length, + hasMore: offset + chunk.length < data.length, + }; +} + +// ============================================================================ +// Metadata Extraction +// ============================================================================ + +export async function populatePdfMetadata(entry: PdfEntry): Promise { + try { + const lib = await getPdfjs(); + const data = await loadPdfData(entry); + + entry.metadata.fileSizeBytes = data.length; + + const pdf = await lib.getDocument({ data: new Uint8Array(data) }).promise; + entry.metadata.pageCount = pdf.numPages; + + const info = (await pdf.getMetadata()).info as + | Record + | undefined; + if (info?.Title) entry.metadata.title = String(info.Title); + if (info?.Author) entry.metadata.author = String(info.Author); + + await pdf.destroy(); + } catch (err) { + console.error(`[loader] Metadata error: ${err}`); + } +} diff --git a/examples/pdf-server/src/types.ts b/examples/pdf-server/src/types.ts new file mode 100644 index 0000000..efbfdbd --- /dev/null +++ b/examples/pdf-server/src/types.ts @@ -0,0 +1,51 @@ +/** + * PDF Server Types - Simplified for didactic purposes + */ +import { z } from "zod"; + +// ============================================================================ +// Core Types +// ============================================================================ + +export const PdfMetadataSchema = z.object({ + title: z.string().optional(), + author: z.string().optional(), + pageCount: z.number(), + fileSizeBytes: z.number(), +}); +export type PdfMetadata = z.infer; + +export const PdfEntrySchema = z.object({ + url: z.string(), // Also serves as unique ID + metadata: PdfMetadataSchema, +}); +export type PdfEntry = z.infer; + +export const PdfIndexSchema = z.object({ + entries: z.array(PdfEntrySchema), +}); +export type PdfIndex = z.infer; + +// ============================================================================ +// Chunked Binary Loading +// ============================================================================ + +/** Max bytes per response chunk */ +export const MAX_CHUNK_BYTES = 500 * 1024; // 500KB + +export const PdfBytesChunkSchema = z.object({ + url: z.string(), + bytes: z.string(), // base64 + offset: z.number(), + byteCount: z.number(), + totalBytes: z.number(), + hasMore: z.boolean(), +}); +export type PdfBytesChunk = z.infer; + +export const ReadPdfBytesInputSchema = z.object({ + url: z.string().describe("PDF URL"), + offset: z.number().min(0).default(0).describe("Byte offset"), + byteCount: z.number().default(MAX_CHUNK_BYTES).describe("Bytes to read"), +}); +export type ReadPdfBytesInput = z.infer; diff --git a/examples/pdf-server/tsconfig.json b/examples/pdf-server/tsconfig.json new file mode 100644 index 0000000..52166b7 --- /dev/null +++ b/examples/pdf-server/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "strict": true, + "skipLibCheck": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src", "server.ts", "server-utils.ts"] +} diff --git a/examples/pdf-server/vite.config.ts b/examples/pdf-server/vite.config.ts new file mode 100644 index 0000000..2a24d21 --- /dev/null +++ b/examples/pdf-server/vite.config.ts @@ -0,0 +1,27 @@ +import { defineConfig } from "vite"; +import { viteSingleFile } from "vite-plugin-singlefile"; + +const INPUT = process.env.INPUT; +if (!INPUT) { + throw new Error("INPUT environment variable is not set"); +} + +const isDevelopment = process.env.NODE_ENV === "development"; + +export default defineConfig({ + plugins: [viteSingleFile()], + build: { + sourcemap: isDevelopment ? "inline" : undefined, + cssMinify: !isDevelopment, + minify: !isDevelopment, + + rollupOptions: { + input: INPUT, + }, + outDir: "dist", + emptyOutDir: false, + }, + optimizeDeps: { + include: ["pdfjs-dist"], + }, +}); diff --git a/package-lock.json b/package-lock.json index e3f14e0..9f548c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -105,6 +105,8 @@ }, "examples/basic-host/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -142,6 +144,8 @@ }, "examples/basic-server-preact/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -182,6 +186,8 @@ }, "examples/basic-server-react/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -219,6 +225,8 @@ }, "examples/basic-server-solid/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -256,6 +264,8 @@ }, "examples/basic-server-svelte/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -291,6 +301,8 @@ }, "examples/basic-server-vanillajs/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -328,6 +340,8 @@ }, "examples/basic-server-vue/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -364,6 +378,8 @@ }, "examples/budget-allocator-server/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -404,6 +420,8 @@ }, "examples/cohort-heatmap-server/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -440,6 +458,8 @@ }, "examples/customer-segmentation-server/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -477,6 +497,8 @@ }, "examples/integration-server/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -512,6 +534,46 @@ }, "examples/map-server/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "examples/pdf-server": { + "name": "@modelcontextprotocol/server-pdf", + "version": "0.4.0", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/ext-apps": "^0.4.0", + "@modelcontextprotocol/sdk": "^1.24.0", + "pdfjs-dist": "^5.0.0", + "zod": "^4.1.13" + }, + "devDependencies": { + "@types/cors": "^2.8.19", + "@types/express": "^5.0.0", + "@types/node": "^22.0.0", + "concurrently": "^9.2.1", + "cors": "^2.8.5", + "cross-env": "^10.1.0", + "express": "^5.1.0", + "typescript": "^5.9.3", + "vite": "^6.0.0", + "vite-plugin-singlefile": "^2.3.0" + } + }, + "examples/pdf-server/node_modules/@types/node": { + "version": "22.19.6", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "examples/pdf-server/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -553,6 +615,8 @@ }, "examples/scenario-modeler-server/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -588,6 +652,8 @@ }, "examples/shadertoy-server/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -624,6 +690,8 @@ }, "examples/sheet-music-server/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -661,6 +729,8 @@ }, "examples/system-monitor-server/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -703,6 +773,8 @@ }, "examples/threejs-server/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -739,11 +811,15 @@ }, "examples/transcript-server/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, "examples/transcript-server/node_modules/zod": { "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -781,6 +857,8 @@ }, "examples/video-resource-server/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -818,6 +896,8 @@ }, "examples/wiki-explorer-server/node_modules/undici-types": { "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -2328,6 +2408,10 @@ "resolved": "examples/map-server", "link": true }, + "node_modules/@modelcontextprotocol/server-pdf": { + "resolved": "examples/pdf-server", + "link": true + }, "node_modules/@modelcontextprotocol/server-scenario-modeler": { "resolved": "examples/scenario-modeler-server", "link": true @@ -2360,6 +2444,256 @@ "resolved": "examples/wiki-explorer-server", "link": true }, + "node_modules/@napi-rs/canvas": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.88.tgz", + "integrity": "sha512-/p08f93LEbsL5mDZFQ3DBxcPv/I4QG9EDYRRq1WNlCOXVfAHBTHMSVMwxlqG/AtnSfUr9+vgfN7MKiyDo0+Weg==", + "license": "MIT", + "optional": true, + "workspaces": [ + "e2e/*" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/canvas-android-arm64": "0.1.88", + "@napi-rs/canvas-darwin-arm64": "0.1.88", + "@napi-rs/canvas-darwin-x64": "0.1.88", + "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.88", + "@napi-rs/canvas-linux-arm64-gnu": "0.1.88", + "@napi-rs/canvas-linux-arm64-musl": "0.1.88", + "@napi-rs/canvas-linux-riscv64-gnu": "0.1.88", + "@napi-rs/canvas-linux-x64-gnu": "0.1.88", + "@napi-rs/canvas-linux-x64-musl": "0.1.88", + "@napi-rs/canvas-win32-arm64-msvc": "0.1.88", + "@napi-rs/canvas-win32-x64-msvc": "0.1.88" + } + }, + "node_modules/@napi-rs/canvas-android-arm64": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.88.tgz", + "integrity": "sha512-KEaClPnZuVxJ8smUWjV1wWFkByBO/D+vy4lN+Dm5DFH514oqwukxKGeck9xcKJhaWJGjfruGmYGiwRe//+/zQQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-arm64": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.88.tgz", + "integrity": "sha512-Xgywz0dDxOKSgx3eZnK85WgGMmGrQEW7ZLA/E7raZdlEE+xXCozobgqz2ZvYigpB6DJFYkqnwHjqCOTSDGlFdg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-x64": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.88.tgz", + "integrity": "sha512-Yz4wSCIQOUgNucgk+8NFtQxQxZV5NO8VKRl9ePKE6XoNyNVC8JDqtvhh3b3TPqKK8W5p2EQpAr1rjjm0mfBxdg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.88.tgz", + "integrity": "sha512-9gQM2SlTo76hYhxHi2XxWTAqpTOb+JtxMPEIr+H5nAhHhyEtNmTSDRtz93SP7mGd2G3Ojf2oF5tP9OdgtgXyKg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-gnu": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.88.tgz", + "integrity": "sha512-7qgaOBMXuVRk9Fzztzr3BchQKXDxGbY+nwsovD3I/Sx81e+sX0ReEDYHTItNb0Je4NHbAl7D0MKyd4SvUc04sg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-musl": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.88.tgz", + "integrity": "sha512-kYyNrUsHLkoGHBc77u4Unh067GrfiCUMbGHC2+OTxbeWfZkPt2o32UOQkhnSswKd9Fko/wSqqGkY956bIUzruA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.88.tgz", + "integrity": "sha512-HVuH7QgzB0yavYdNZDRyAsn/ejoXB0hn8twwFnOqUbCCdkV+REna7RXjSR7+PdfW0qMQ2YYWsLvVBT5iL/mGpw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-gnu": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.88.tgz", + "integrity": "sha512-hvcvKIcPEQrvvJtJnwD35B3qk6umFJ8dFIr8bSymfrSMem0EQsfn1ztys8ETIFndTwdNWJKWluvxztA41ivsEw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-musl": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.88.tgz", + "integrity": "sha512-eSMpGYY2xnZSQ6UxYJ6plDboxq4KeJ4zT5HaVkUnbObNN6DlbJe0Mclh3wifAmquXfrlgTZt6zhHsUgz++AK6g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-win32-arm64-msvc": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.88.tgz", + "integrity": "sha512-qcIFfEgHrchyYqRrxsCeTQgpJZ/GqHiqPcU/Fvw/ARVlQeDX1VyFH+X+0gCR2tca6UJrq96vnW+5o7buCq+erA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-win32-x64-msvc": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.88.tgz", + "integrity": "sha512-ROVqbfS4QyZxYkqmaIBBpbz/BQvAR+05FXM5PAtTYVc0uyY8Y4BHJSMdGAaMf6TdIVRsQsiq+FG/dH9XhvWCFQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, "node_modules/@oclif/core": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.8.0.tgz", @@ -2417,9 +2751,9 @@ } }, "node_modules/@oven/bun-darwin-aarch64": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@oven/bun-darwin-aarch64/-/bun-darwin-aarch64-1.3.5.tgz", - "integrity": "sha512-8GvNtMo0NINM7Emk9cNAviCG3teEgr3BUX9be0+GD029zIagx2Sf54jMui1Eu1IpFm7nWHODuLEefGOQNaJ0gQ==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-aarch64/-/bun-darwin-aarch64-1.3.6.tgz", + "integrity": "sha512-27rypIapNkYboOSylkf1tD9UW9Ado2I+P1NBL46Qz29KmOjTL6WuJ7mHDC5O66CYxlOkF5r93NPDAC3lFHYBXw==", "cpu": [ "arm64" ], @@ -2430,9 +2764,9 @@ ] }, "node_modules/@oven/bun-darwin-x64": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64/-/bun-darwin-x64-1.3.5.tgz", - "integrity": "sha512-r33eHQOHAwkuiBJIwmkXIyqONQOQMnd1GMTpDzaxx9vf9+svby80LZO9Hcm1ns6KT/TBRFyODC/0loA7FAaffg==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64/-/bun-darwin-x64-1.3.6.tgz", + "integrity": "sha512-I82xGzPkBxzBKgbl8DsA0RfMQCWTWjNmLjIEkW1ECiv3qK02kHGQ5FGUr/29L/SuvnGsULW4tBTRNZiMzL37nA==", "cpu": [ "x64" ], @@ -2443,9 +2777,9 @@ ] }, "node_modules/@oven/bun-darwin-x64-baseline": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64-baseline/-/bun-darwin-x64-baseline-1.3.5.tgz", - "integrity": "sha512-p5q3rJk48qhLuLBOFehVc+kqCE03YrswTc6NCxbwsxiwfySXwcAvpF2KWKF/ZZObvvR8hCCvqe1F81b2p5r2dg==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64-baseline/-/bun-darwin-x64-baseline-1.3.6.tgz", + "integrity": "sha512-nqtr+pTsHqusYpG2OZc6s+AmpWDB/FmBvstrK0y5zkti4OqnCuu7Ev2xNjS7uyb47NrAFF40pWqkpaio5XEd7w==", "cpu": [ "x64" ], @@ -2482,9 +2816,9 @@ ] }, "node_modules/@oven/bun-linux-x64": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64/-/bun-linux-x64-1.3.5.tgz", - "integrity": "sha512-n7zhKTSDZS0yOYg5Rq8easZu5Y/o47sv0c7yGr2ciFdcie9uYV55fZ7QMqhWMGK33ezCSikh5EDkUMCIvfWpjA==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64/-/bun-linux-x64-1.3.6.tgz", + "integrity": "sha512-egfngj0dfJ868cf30E7B+ye9KUWSebYxOG4l9YP5eWeMXCtenpenx0zdKtAn9qxJgEJym5AN6trtlk+J6x8Lig==", "cpu": [ "x64" ], @@ -2495,9 +2829,9 @@ ] }, "node_modules/@oven/bun-linux-x64-baseline": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-baseline/-/bun-linux-x64-baseline-1.3.5.tgz", - "integrity": "sha512-FeCQyBU62DMuB0nn01vPnf3McXrKOsrK9p7sHaBFYycw0mmoU8kCq/WkBkGMnLuvQljJSyen8QBTx+fXdNupWg==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-baseline/-/bun-linux-x64-baseline-1.3.6.tgz", + "integrity": "sha512-jRmnX18ak8WzqLrex3siw0PoVKyIeI5AiCv4wJLgSs7VKfOqrPycfHIWfIX2jdn7ngqbHFPzI09VBKANZ4Pckg==", "cpu": [ "x64" ], @@ -2508,9 +2842,9 @@ ] }, "node_modules/@oven/bun-linux-x64-musl": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-musl/-/bun-linux-x64-musl-1.3.5.tgz", - "integrity": "sha512-XkCCHkByYn8BIDvoxnny898znju4xnW2kvFE8FT5+0Y62cWdcBGMZ9RdsEUTeRz16k8hHtJpaSfLcEmNTFIwRQ==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-musl/-/bun-linux-x64-musl-1.3.6.tgz", + "integrity": "sha512-YeXcJ9K6vJAt1zSkeA21J6pTe7PgDMLTHKGI3nQBiMYnYf7Ob3K+b/ChSCznrJG7No5PCPiQPg4zTgA+BOTmSA==", "cpu": [ "x64" ], @@ -2521,9 +2855,9 @@ ] }, "node_modules/@oven/bun-linux-x64-musl-baseline": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-musl-baseline/-/bun-linux-x64-musl-baseline-1.3.5.tgz", - "integrity": "sha512-TJiYC7KCr0XxFTsxgwQOeE7dncrEL/RSyL0EzSL3xRkrxJMWBCvCSjQn7LV1i6T7hFst0+3KoN3VWvD5BinqHA==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-musl-baseline/-/bun-linux-x64-musl-baseline-1.3.6.tgz", + "integrity": "sha512-7FjVnxnRTp/AgWqSQRT/Vt9TYmvnZ+4M+d9QOKh/Lf++wIFXFGSeAgD6bV1X/yr2UPVmZDk+xdhr2XkU7l2v3w==", "cpu": [ "x64" ], @@ -2534,9 +2868,9 @@ ] }, "node_modules/@oven/bun-windows-x64": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@oven/bun-windows-x64/-/bun-windows-x64-1.3.5.tgz", - "integrity": "sha512-T3xkODItb/0ftQPFsZDc7EAX2D6A4TEazQ2YZyofZToO8Q7y8YT8ooWdhd0BQiTCd66uEvgE1DCZetynwg2IoA==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@oven/bun-windows-x64/-/bun-windows-x64-1.3.6.tgz", + "integrity": "sha512-Sr1KwUcbB0SEpnSPO22tNJppku2khjFluEst+mTGhxHzAGQTQncNeJxDnt3F15n+p9Q+mlcorxehd68n1siikQ==", "cpu": [ "x64" ], @@ -2547,9 +2881,9 @@ ] }, "node_modules/@oven/bun-windows-x64-baseline": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@oven/bun-windows-x64-baseline/-/bun-windows-x64-baseline-1.3.5.tgz", - "integrity": "sha512-rtVQB9/1XK8FWJgFtsOthbPifRMYypgJwxu+pK3NHx8WvFKmq7HcPDqNr8xLzGULjQEO7eAo2aOZfONOwYz+5g==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@oven/bun-windows-x64-baseline/-/bun-windows-x64-baseline-1.3.6.tgz", + "integrity": "sha512-PFUa7JL4lGoyyppeS4zqfuoXXih+gSE0XxhDMrCPVEUev0yhGNd/tbWBvcdpYnUth80owENoGjc8s5Knopv9wA==", "cpu": [ "x64" ], @@ -6652,6 +6986,18 @@ "node": ">= 14.16" } }, + "node_modules/pdfjs-dist": { + "version": "5.4.530", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.530.tgz", + "integrity": "sha512-r1hWsSIGGmyYUAHR26zSXkxYWLXLMd6AwqcaFYG9YUZ0GBf5GvcjJSeo512tabM4GYFhxhl5pMCmPr7Q72Rq2Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=20.16.0 || >=22.3.0" + }, + "optionalDependencies": { + "@napi-rs/canvas": "^0.1.84" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", diff --git a/package.json b/package.json index e0b4dbb..ee47f11 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "test:e2e:update": "playwright test --update-snapshots", "test:e2e:ui": "playwright test --ui", "test:e2e:docker": "docker run --rm -v $(pwd):/work -w /work -it mcr.microsoft.com/playwright:v1.57.0-noble sh -c 'npm i -g bun && npm ci && npx playwright test'", - "test:e2e:docker:update": "docker run --rm -v $(pwd):/work -w /work -it mcr.microsoft.com/playwright:v1.57.0-noble sh -c 'npm i -g bun && npm ci && npx playwright test --update-snapshots'", + "test:e2e:docker:update": "npm run build:all && docker run --rm -v $(pwd):/work -w /work -it mcr.microsoft.com/playwright:v1.57.0-noble sh -c 'npm i -g bun && npm ci && npx playwright test --update-snapshots'", "preexamples:build": "npm run build", "examples:build": "bun examples/run-all.ts build", "examples:start": "NODE_ENV=development npm run build && bun examples/run-all.ts start", @@ -64,7 +64,7 @@ "prepare": "node scripts/setup-bun.mjs && npm run build && husky", "docs": "typedoc", "docs:watch": "typedoc --watch", - "generate:screenshots": "docker run --rm -v $(pwd):/work -w /work mcr.microsoft.com/playwright:v1.57.0-noble sh -c 'npm i -g bun && npm ci && npx playwright test tests/e2e/generate-grid-screenshots.spec.ts'", + "generate:screenshots": "npm run build:all && docker run --rm -v $(pwd):/work -w /work mcr.microsoft.com/playwright:v1.57.0-noble sh -c 'npm i -g bun && npm ci && npx playwright test tests/e2e/generate-grid-screenshots.spec.ts'", "prettier": "prettier -u \"**/*.{js,jsx,ts,tsx,mjs,json,md,yml,yaml}\" --check", "prettier:fix": "prettier -u \"**/*.{js,jsx,ts,tsx,mjs,json,md,yml,yaml}\" --write", "check:versions": "node scripts/check-versions.mjs" diff --git a/tests/e2e/generate-grid-screenshots.spec.ts b/tests/e2e/generate-grid-screenshots.spec.ts index c686304..ce1336c 100644 --- a/tests/e2e/generate-grid-screenshots.spec.ts +++ b/tests/e2e/generate-grid-screenshots.spec.ts @@ -22,6 +22,7 @@ const DEFAULT_WAIT_MS = 5000; // Extra wait time for slow-loading servers (tiles, etc.) const EXTRA_WAIT_MS: Record = { "map-server": 45000, // CesiumJS needs time for map tiles + "pdf-server": 45000, // Chunked loading of file }; // Servers to skip (screenshots maintained manually) @@ -52,6 +53,7 @@ const SERVERS = [ dir: "customer-segmentation-server", }, { key: "map-server", name: "CesiumJS Map Server", dir: "map-server" }, + { key: "pdf-server", name: "PDF Server", dir: "pdf-server" }, { key: "scenario-modeler", name: "SaaS Scenario Modeler", diff --git a/tests/e2e/servers.spec.ts b/tests/e2e/servers.spec.ts index 5856485..bf94be8 100644 --- a/tests/e2e/servers.spec.ts +++ b/tests/e2e/servers.spec.ts @@ -48,6 +48,7 @@ const SERVERS = [ { key: "cohort-heatmap", name: "Cohort Heatmap Server" }, { key: "customer-segmentation", name: "Customer Segmentation Server" }, { key: "map-server", name: "CesiumJS Map Server" }, + { key: "pdf-server", name: "PDF Server" }, { key: "scenario-modeler", name: "SaaS Scenario Modeler" }, { key: "shadertoy", name: "ShaderToy Server" }, { key: "sheet-music", name: "Sheet Music Server" }, diff --git a/tests/e2e/servers.spec.ts-snapshots/pdf-server.png b/tests/e2e/servers.spec.ts-snapshots/pdf-server.png new file mode 100644 index 0000000000000000000000000000000000000000..98643966dcca9cc9f1db9d0aa5b28dcd3e1fddff GIT binary patch literal 79541 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 Jn