@modelcontextprotocol/server-pdf 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,26 +2,41 @@
2
2
 
3
3
  ![Screenshot](screenshot.png)
4
4
 
5
- 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).
5
+ An interactive PDF viewer using [PDF.js](https://mozilla.github.io/pdf.js/). Supports local files and remote URLs from academic sources (arxiv, biorxiv, zenodo, etc).
6
+
7
+ ## MCP Client Configuration
8
+
9
+ Add to your MCP client configuration (stdio transport):
10
+
11
+ ```json
12
+ {
13
+ "mcpServers": {
14
+ "pdf": {
15
+ "command": "npx",
16
+ "args": [
17
+ "-y",
18
+ "--silent",
19
+ "--registry=https://registry.npmjs.org/",
20
+ "@modelcontextprotocol/server-pdf",
21
+ "--stdio"
22
+ ]
23
+ }
24
+ }
25
+ }
26
+ ```
6
27
 
7
28
  ## What This Example Demonstrates
8
29
 
9
30
  ### 1. Chunked Data Through Size-Limited Tool Calls
10
31
 
11
- 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:
32
+ On some host platforms, tool calls have size limits, so large PDFs cannot be sent in a single response. This example streams PDFs in chunks using HTTP Range requests:
12
33
 
13
- **Server side** (`pdf-loader.ts`):
34
+ **Server side** (`server.ts`):
14
35
 
15
36
  ```typescript
16
37
  // Returns chunks with pagination metadata
17
- async function loadPdfBytesChunk(entry, offset, byteCount) {
18
- return {
19
- bytes: base64Chunk,
20
- offset,
21
- byteCount,
22
- totalBytes,
23
- hasMore: offset + byteCount < totalBytes,
24
- };
38
+ {
39
+ (bytes, offset, byteCount, totalBytes, hasMore);
25
40
  }
26
41
  ```
27
42
 
@@ -30,7 +45,7 @@ async function loadPdfBytesChunk(entry, offset, byteCount) {
30
45
  ```typescript
31
46
  // Load in chunks with progress
32
47
  while (hasMore) {
33
- const chunk = await app.callServerTool("read_pdf_bytes", { pdfId, offset });
48
+ const chunk = await app.callServerTool("read_pdf_bytes", { url, offset });
34
49
  chunks.push(base64ToBytes(chunk.bytes));
35
50
  offset += chunk.byteCount;
36
51
  hasMore = chunk.hasMore;
@@ -44,13 +59,12 @@ The viewer keeps the model informed about what the user is seeing:
44
59
 
45
60
  ```typescript
46
61
  app.updateModelContext({
47
- structuredContent: {
48
- title: pdfTitle,
49
- currentPage,
50
- totalPages,
51
- pageText: pageText.slice(0, 5000),
52
- selection: selectedText ? { text, start, end } : undefined,
53
- },
62
+ content: [
63
+ {
64
+ type: "text",
65
+ text: `PDF viewer | "${title}" | Current Page: ${page}/${total}\n\nPage content:\n${pageText}`,
66
+ },
67
+ ],
54
68
  });
55
69
  ```
56
70
 
@@ -80,58 +94,75 @@ The viewer demonstrates opening external links (e.g., to the original arxiv page
80
94
  titleEl.onclick = () => app.openLink(sourceUrl);
81
95
  ```
82
96
 
97
+ ### 5. View Persistence
98
+
99
+ Page position is saved per-view using `viewUUID` and localStorage.
100
+
101
+ ### 6. Dark Mode / Theming
102
+
103
+ The viewer syncs with the host's theme using CSS `light-dark()` and the SDK's theming APIs:
104
+
105
+ ```typescript
106
+ app.onhostcontextchanged = (ctx) => {
107
+ if (ctx.theme) applyDocumentTheme(ctx.theme);
108
+ if (ctx.styles?.variables) applyHostStyleVariables(ctx.styles.variables);
109
+ };
110
+ ```
111
+
83
112
  ## Usage
84
113
 
85
114
  ```bash
86
115
  # Default: loads a sample arxiv paper
87
- bun examples/pdf-server/server.ts
116
+ bun examples/pdf-server/main.ts
88
117
 
89
118
  # Load local files (converted to file:// URLs)
90
- bun examples/pdf-server/server.ts ./docs/paper.pdf /path/to/thesis.pdf
119
+ bun examples/pdf-server/main.ts ./docs/paper.pdf /path/to/thesis.pdf
91
120
 
92
121
  # Load from URLs
93
- bun examples/pdf-server/server.ts https://arxiv.org/pdf/2401.00001.pdf
122
+ bun examples/pdf-server/main.ts https://arxiv.org/pdf/2401.00001.pdf
94
123
 
95
124
  # Mix local and remote
96
- bun examples/pdf-server/server.ts ./local.pdf https://arxiv.org/pdf/2401.00001.pdf
125
+ bun examples/pdf-server/main.ts ./local.pdf https://arxiv.org/pdf/2401.00001.pdf
97
126
 
98
127
  # stdio mode for MCP clients
99
- bun examples/pdf-server/server.ts --stdio ./papers/
128
+ bun examples/pdf-server/main.ts --stdio ./papers/
100
129
  ```
101
130
 
102
- **Security**: Dynamic URLs (via `view_pdf` tool) are restricted to arxiv.org. Local files must be in the initial list.
131
+ ## Allowed Sources
132
+
133
+ - **Local files**: Must be passed as CLI arguments
134
+ - **Remote URLs**: arxiv.org, biorxiv.org, medrxiv.org, chemrxiv.org, zenodo.org, osf.io, hal.science, ssrn.com, and more
103
135
 
104
136
  ## Tools
105
137
 
106
- | Tool | Visibility | Purpose |
107
- | ---------------- | ---------- | ---------------------------------- |
108
- | `list_pdfs` | Model | List indexed PDFs |
109
- | `display_pdf` | Model + UI | Display interactive viewer in chat |
110
- | `read_pdf_bytes` | App only | Chunked binary loading |
138
+ | Tool | Visibility | Purpose |
139
+ | ---------------- | ---------- | -------------------------------------- |
140
+ | `list_pdfs` | Model | List available local files and origins |
141
+ | `display_pdf` | Model + UI | Display interactive viewer |
142
+ | `read_pdf_bytes` | App only | Stream PDF data in chunks |
111
143
 
112
144
  ## Architecture
113
145
 
114
146
  ```
115
- server.ts # MCP server (233 lines)
116
- ├── src/
117
- │ ├── types.ts # Zod schemas (75 lines)
118
- │ ├── pdf-indexer.ts # URL-based indexing (44 lines)
119
- │ ├── pdf-loader.ts # Chunked loading (171 lines)
120
- │ └── mcp-app.ts # Interactive viewer UI
147
+ server.ts # MCP server + tools
148
+ main.ts # CLI entry point
149
+ src/
150
+ └── mcp-app.ts # Interactive viewer UI (PDF.js)
121
151
  ```
122
152
 
123
153
  ## Key Patterns Shown
124
154
 
125
- | Pattern | Implementation |
126
- | ----------------- | ---------------------------------------- |
127
- | App-only tools | `_meta: { ui: { visibility: ["app"] } }` |
128
- | Chunked responses | `hasMore` + `offset` pagination |
129
- | Model context | `app.updateModelContext()` |
130
- | Display modes | `app.requestDisplayMode()` |
131
- | External links | `app.openLink()` |
132
- | Size negotiation | `app.sendSizeChanged()` |
155
+ | Pattern | Implementation |
156
+ | ----------------- | ------------------------------------------- |
157
+ | App-only tools | `_meta: { ui: { visibility: ["app"] } }` |
158
+ | Chunked responses | `hasMore` + `offset` pagination |
159
+ | Model context | `app.updateModelContext()` |
160
+ | Display modes | `app.requestDisplayMode()` |
161
+ | External links | `app.openLink()` |
162
+ | View persistence | `viewUUID` + localStorage |
163
+ | Theming | `applyDocumentTheme()` + CSS `light-dark()` |
133
164
 
134
165
  ## Dependencies
135
166
 
136
- - `pdfjs-dist`: PDF rendering
167
+ - `pdfjs-dist`: PDF rendering (frontend only)
137
168
  - `@modelcontextprotocol/ext-apps`: MCP Apps SDK