@modelcontextprotocol/server-pdf 0.4.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -45
- package/dist/index.js +23609 -28764
- package/dist/mcp-app.html +26 -29
- package/dist/server.d.ts +30 -8
- package/dist/server.js +8676 -8701
- package/package.json +3 -3
- package/dist/src/pdf-indexer.d.ts +0 -15
- package/dist/src/pdf-loader.d.ts +0 -5
- package/dist/src/types.d.ts +0 -50
package/README.md
CHANGED
|
@@ -2,26 +2,59 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
|
|
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
|
+
```
|
|
27
|
+
|
|
28
|
+
### Local Development
|
|
29
|
+
|
|
30
|
+
To test local modifications, use this configuration (replace `~/code/ext-apps` with your clone path):
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"mcpServers": {
|
|
35
|
+
"pdf": {
|
|
36
|
+
"command": "bash",
|
|
37
|
+
"args": [
|
|
38
|
+
"-c",
|
|
39
|
+
"cd ~/code/ext-apps/examples/pdf-server && npm run build >&2 && node dist/index.js --stdio"
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
6
45
|
|
|
7
46
|
## What This Example Demonstrates
|
|
8
47
|
|
|
9
48
|
### 1. Chunked Data Through Size-Limited Tool Calls
|
|
10
49
|
|
|
11
|
-
On some host platforms, tool calls have size limits, so large PDFs cannot be sent in a single response. This example
|
|
50
|
+
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
51
|
|
|
13
|
-
**Server side** (`
|
|
52
|
+
**Server side** (`server.ts`):
|
|
14
53
|
|
|
15
54
|
```typescript
|
|
16
55
|
// Returns chunks with pagination metadata
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
bytes: base64Chunk,
|
|
20
|
-
offset,
|
|
21
|
-
byteCount,
|
|
22
|
-
totalBytes,
|
|
23
|
-
hasMore: offset + byteCount < totalBytes,
|
|
24
|
-
};
|
|
56
|
+
{
|
|
57
|
+
(bytes, offset, byteCount, totalBytes, hasMore);
|
|
25
58
|
}
|
|
26
59
|
```
|
|
27
60
|
|
|
@@ -30,7 +63,7 @@ async function loadPdfBytesChunk(entry, offset, byteCount) {
|
|
|
30
63
|
```typescript
|
|
31
64
|
// Load in chunks with progress
|
|
32
65
|
while (hasMore) {
|
|
33
|
-
const chunk = await app.callServerTool("read_pdf_bytes", {
|
|
66
|
+
const chunk = await app.callServerTool("read_pdf_bytes", { url, offset });
|
|
34
67
|
chunks.push(base64ToBytes(chunk.bytes));
|
|
35
68
|
offset += chunk.byteCount;
|
|
36
69
|
hasMore = chunk.hasMore;
|
|
@@ -44,13 +77,12 @@ The viewer keeps the model informed about what the user is seeing:
|
|
|
44
77
|
|
|
45
78
|
```typescript
|
|
46
79
|
app.updateModelContext({
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
},
|
|
80
|
+
content: [
|
|
81
|
+
{
|
|
82
|
+
type: "text",
|
|
83
|
+
text: `PDF viewer | "${title}" | Current Page: ${page}/${total}\n\nPage content:\n${pageText}`,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
54
86
|
});
|
|
55
87
|
```
|
|
56
88
|
|
|
@@ -80,58 +112,75 @@ The viewer demonstrates opening external links (e.g., to the original arxiv page
|
|
|
80
112
|
titleEl.onclick = () => app.openLink(sourceUrl);
|
|
81
113
|
```
|
|
82
114
|
|
|
115
|
+
### 5. View Persistence
|
|
116
|
+
|
|
117
|
+
Page position is saved per-view using `viewUUID` and localStorage.
|
|
118
|
+
|
|
119
|
+
### 6. Dark Mode / Theming
|
|
120
|
+
|
|
121
|
+
The viewer syncs with the host's theme using CSS `light-dark()` and the SDK's theming APIs:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
app.onhostcontextchanged = (ctx) => {
|
|
125
|
+
if (ctx.theme) applyDocumentTheme(ctx.theme);
|
|
126
|
+
if (ctx.styles?.variables) applyHostStyleVariables(ctx.styles.variables);
|
|
127
|
+
};
|
|
128
|
+
```
|
|
129
|
+
|
|
83
130
|
## Usage
|
|
84
131
|
|
|
85
132
|
```bash
|
|
86
133
|
# Default: loads a sample arxiv paper
|
|
87
|
-
bun examples/pdf-server/
|
|
134
|
+
bun examples/pdf-server/main.ts
|
|
88
135
|
|
|
89
136
|
# Load local files (converted to file:// URLs)
|
|
90
|
-
bun examples/pdf-server/
|
|
137
|
+
bun examples/pdf-server/main.ts ./docs/paper.pdf /path/to/thesis.pdf
|
|
91
138
|
|
|
92
139
|
# Load from URLs
|
|
93
|
-
bun examples/pdf-server/
|
|
140
|
+
bun examples/pdf-server/main.ts https://arxiv.org/pdf/2401.00001.pdf
|
|
94
141
|
|
|
95
142
|
# Mix local and remote
|
|
96
|
-
bun examples/pdf-server/
|
|
143
|
+
bun examples/pdf-server/main.ts ./local.pdf https://arxiv.org/pdf/2401.00001.pdf
|
|
97
144
|
|
|
98
145
|
# stdio mode for MCP clients
|
|
99
|
-
bun examples/pdf-server/
|
|
146
|
+
bun examples/pdf-server/main.ts --stdio ./papers/
|
|
100
147
|
```
|
|
101
148
|
|
|
102
|
-
|
|
149
|
+
## Allowed Sources
|
|
150
|
+
|
|
151
|
+
- **Local files**: Must be passed as CLI arguments
|
|
152
|
+
- **Remote URLs**: arxiv.org, biorxiv.org, medrxiv.org, chemrxiv.org, zenodo.org, osf.io, hal.science, ssrn.com, and more
|
|
103
153
|
|
|
104
154
|
## Tools
|
|
105
155
|
|
|
106
|
-
| Tool | Visibility | Purpose
|
|
107
|
-
| ---------------- | ---------- |
|
|
108
|
-
| `list_pdfs` | Model | List
|
|
109
|
-
| `display_pdf` | Model + UI | Display interactive viewer
|
|
110
|
-
| `read_pdf_bytes` | App only |
|
|
156
|
+
| Tool | Visibility | Purpose |
|
|
157
|
+
| ---------------- | ---------- | -------------------------------------- |
|
|
158
|
+
| `list_pdfs` | Model | List available local files and origins |
|
|
159
|
+
| `display_pdf` | Model + UI | Display interactive viewer |
|
|
160
|
+
| `read_pdf_bytes` | App only | Stream PDF data in chunks |
|
|
111
161
|
|
|
112
162
|
## Architecture
|
|
113
163
|
|
|
114
164
|
```
|
|
115
|
-
server.ts
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
│ ├── pdf-loader.ts # Chunked loading (171 lines)
|
|
120
|
-
│ └── mcp-app.ts # Interactive viewer UI
|
|
165
|
+
server.ts # MCP server + tools
|
|
166
|
+
main.ts # CLI entry point
|
|
167
|
+
src/
|
|
168
|
+
└── mcp-app.ts # Interactive viewer UI (PDF.js)
|
|
121
169
|
```
|
|
122
170
|
|
|
123
171
|
## Key Patterns Shown
|
|
124
172
|
|
|
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
|
-
|
|
|
173
|
+
| Pattern | Implementation |
|
|
174
|
+
| ----------------- | ------------------------------------------- |
|
|
175
|
+
| App-only tools | `_meta: { ui: { visibility: ["app"] } }` |
|
|
176
|
+
| Chunked responses | `hasMore` + `offset` pagination |
|
|
177
|
+
| Model context | `app.updateModelContext()` |
|
|
178
|
+
| Display modes | `app.requestDisplayMode()` |
|
|
179
|
+
| External links | `app.openLink()` |
|
|
180
|
+
| View persistence | `viewUUID` + localStorage |
|
|
181
|
+
| Theming | `applyDocumentTheme()` + CSS `light-dark()` |
|
|
133
182
|
|
|
134
183
|
## Dependencies
|
|
135
184
|
|
|
136
|
-
- `pdfjs-dist`: PDF rendering
|
|
185
|
+
- `pdfjs-dist`: PDF rendering (frontend only)
|
|
137
186
|
- `@modelcontextprotocol/ext-apps`: MCP Apps SDK
|