pagebolt-mcp 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/LICENSE +21 -0
- package/README.md +226 -0
- package/package.json +58 -0
- package/server.json +41 -0
- package/src/index.mjs +422 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PageBolt
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# PageBolt MCP Server
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/pagebolt-mcp)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://modelcontextprotocol.io)
|
|
6
|
+
|
|
7
|
+
Take screenshots, generate PDFs, and create OG images directly from your AI coding assistant.
|
|
8
|
+
|
|
9
|
+
**Works with Claude Desktop, Cursor, Windsurf, Cline, and any MCP-compatible client.**
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<img src="https://pagebolt.dev/og-image-default.png" alt="PageBolt" width="600" />
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## What It Does
|
|
18
|
+
|
|
19
|
+
PageBolt MCP Server connects your AI assistant to [PageBolt's web capture API](https://pagebolt.dev), giving it the ability to:
|
|
20
|
+
|
|
21
|
+
- **Take screenshots** of any URL, HTML, or Markdown (30+ parameters)
|
|
22
|
+
- **Generate PDFs** from URLs or HTML (invoices, reports, docs)
|
|
23
|
+
- **Create OG images** for social cards using templates or custom HTML
|
|
24
|
+
- **Run browser sequences** — multi-step automation (navigate, click, fill, screenshot)
|
|
25
|
+
- **List device presets** — 25+ devices (iPhone, iPad, MacBook, Galaxy, etc.)
|
|
26
|
+
- **Check usage** — monitor your API quota in real time
|
|
27
|
+
|
|
28
|
+
All results are returned inline — screenshots appear directly in your chat.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
### 1. Get a free API key
|
|
35
|
+
|
|
36
|
+
Sign up at [pagebolt.dev](https://pagebolt.dev) — the free tier includes 100 requests/month, no credit card required.
|
|
37
|
+
|
|
38
|
+
### 2. Install & configure
|
|
39
|
+
|
|
40
|
+
#### Claude Desktop
|
|
41
|
+
|
|
42
|
+
Add to `~/.claude/claude_desktop_config.json`:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"mcpServers": {
|
|
47
|
+
"pagebolt": {
|
|
48
|
+
"command": "npx",
|
|
49
|
+
"args": ["-y", "pagebolt-mcp"],
|
|
50
|
+
"env": {
|
|
51
|
+
"PAGEBOLT_API_KEY": "pf_live_your_key_here"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
#### Cursor
|
|
59
|
+
|
|
60
|
+
Add to `.cursor/mcp.json` in your project (or global config):
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"mcpServers": {
|
|
65
|
+
"pagebolt": {
|
|
66
|
+
"command": "npx",
|
|
67
|
+
"args": ["-y", "pagebolt-mcp"],
|
|
68
|
+
"env": {
|
|
69
|
+
"PAGEBOLT_API_KEY": "pf_live_your_key_here"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
#### Windsurf
|
|
77
|
+
|
|
78
|
+
Add to your Windsurf MCP settings:
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"mcpServers": {
|
|
83
|
+
"pagebolt": {
|
|
84
|
+
"command": "npx",
|
|
85
|
+
"args": ["-y", "pagebolt-mcp"],
|
|
86
|
+
"env": {
|
|
87
|
+
"PAGEBOLT_API_KEY": "pf_live_your_key_here"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### Cline / Other MCP Clients
|
|
95
|
+
|
|
96
|
+
Same config pattern — set `command` to `npx`, `args` to `["-y", "pagebolt-mcp"]`, and provide your API key in `env`.
|
|
97
|
+
|
|
98
|
+
### 3. Try it
|
|
99
|
+
|
|
100
|
+
Ask your AI assistant:
|
|
101
|
+
|
|
102
|
+
> "Take a screenshot of https://github.com in dark mode at 1920x1080"
|
|
103
|
+
|
|
104
|
+
The screenshot will appear inline in your chat.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Tools
|
|
109
|
+
|
|
110
|
+
### `take_screenshot`
|
|
111
|
+
|
|
112
|
+
Capture a pixel-perfect screenshot of any URL, HTML, or Markdown.
|
|
113
|
+
|
|
114
|
+
**Key parameters:**
|
|
115
|
+
- `url` / `html` / `markdown` — content source
|
|
116
|
+
- `width`, `height` — viewport size (default: 1280x720)
|
|
117
|
+
- `viewportDevice` — device preset (e.g. `"iphone_14_pro"`, `"macbook_pro_14"`)
|
|
118
|
+
- `fullPage` — capture the entire scrollable page
|
|
119
|
+
- `darkMode` — emulate dark color scheme
|
|
120
|
+
- `format` — `png`, `jpeg`, or `webp`
|
|
121
|
+
- `blockBanners` — hide cookie consent banners
|
|
122
|
+
- `blockAds` — block advertisements
|
|
123
|
+
- `blockChats` — remove live chat widgets
|
|
124
|
+
- `blockTrackers` — block tracking scripts
|
|
125
|
+
- `extractMetadata` — get page title, description, OG tags alongside the screenshot
|
|
126
|
+
- `selector` — capture a specific DOM element
|
|
127
|
+
- `delay` — wait before capture (for animations)
|
|
128
|
+
- `cookies`, `headers`, `authorization` — authenticated captures
|
|
129
|
+
- `geolocation`, `timeZone` — location emulation
|
|
130
|
+
- ...and 15+ more
|
|
131
|
+
|
|
132
|
+
**Example prompts:**
|
|
133
|
+
- "Screenshot https://example.com on an iPhone 14 Pro"
|
|
134
|
+
- "Take a full-page screenshot of https://news.ycombinator.com with ad blocking"
|
|
135
|
+
- "Capture this HTML in dark mode: `<h1>Hello World</h1>`"
|
|
136
|
+
|
|
137
|
+
### `generate_pdf`
|
|
138
|
+
|
|
139
|
+
Generate a PDF from any URL or HTML content.
|
|
140
|
+
|
|
141
|
+
**Parameters:** `url`/`html`, `format` (A4/Letter/Legal), `landscape`, `margin`, `scale`, `pageRanges`, `delay`, `saveTo`
|
|
142
|
+
|
|
143
|
+
**Example prompts:**
|
|
144
|
+
- "Generate a PDF of https://example.com and save it to ./report.pdf"
|
|
145
|
+
- "Create a PDF from this invoice HTML in Letter format, landscape"
|
|
146
|
+
|
|
147
|
+
### `create_og_image`
|
|
148
|
+
|
|
149
|
+
Create Open Graph / social preview images.
|
|
150
|
+
|
|
151
|
+
**Parameters:** `template` (default/minimal/gradient), `html` (custom), `title`, `subtitle`, `logo`, `bgColor`, `textColor`, `accentColor`, `width`, `height`, `format`
|
|
152
|
+
|
|
153
|
+
**Example prompts:**
|
|
154
|
+
- "Create an OG image with title 'How to Build a SaaS' using the gradient template"
|
|
155
|
+
- "Generate a social card with a dark blue background and white text"
|
|
156
|
+
|
|
157
|
+
### `run_sequence`
|
|
158
|
+
|
|
159
|
+
Execute multi-step browser automation.
|
|
160
|
+
|
|
161
|
+
**Actions:** `navigate`, `click`, `fill`, `select`, `hover`, `scroll`, `wait`, `wait_for`, `evaluate`, `screenshot`, `pdf`
|
|
162
|
+
|
|
163
|
+
**Example prompts:**
|
|
164
|
+
- "Go to https://example.com, click the pricing link, then screenshot both pages"
|
|
165
|
+
- "Navigate to the login page, fill in test credentials, submit, and screenshot the dashboard"
|
|
166
|
+
|
|
167
|
+
### `list_devices`
|
|
168
|
+
|
|
169
|
+
List all 25+ available device presets with viewport dimensions.
|
|
170
|
+
|
|
171
|
+
**Example prompt:**
|
|
172
|
+
- "What device presets are available for screenshots?"
|
|
173
|
+
|
|
174
|
+
### `check_usage`
|
|
175
|
+
|
|
176
|
+
Check your current API usage and plan limits.
|
|
177
|
+
|
|
178
|
+
**Example prompt:**
|
|
179
|
+
- "How many API requests do I have left this month?"
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Configuration
|
|
184
|
+
|
|
185
|
+
| Environment Variable | Required | Default | Description |
|
|
186
|
+
|---------------------|----------|---------|-------------|
|
|
187
|
+
| `PAGEBOLT_API_KEY` | **Yes** | — | Your PageBolt API key ([get one free](https://pagebolt.dev)) |
|
|
188
|
+
| `PAGEBOLT_BASE_URL` | No | `https://pagebolt.dev` | API base URL |
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Pricing
|
|
193
|
+
|
|
194
|
+
| Plan | Price | Requests/mo | Rate Limit |
|
|
195
|
+
|------|-------|-------------|------------|
|
|
196
|
+
| **Free** | $0 | 100 | 10 req/min |
|
|
197
|
+
| Starter | $29/mo | 5,000 | 60 req/min |
|
|
198
|
+
| Growth | $79/mo | 25,000 | 120 req/min |
|
|
199
|
+
| Scale | $199/mo | 100,000 | 300 req/min |
|
|
200
|
+
|
|
201
|
+
Free plan requires no credit card. Starter and Growth include a 14-day free trial.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Why PageBolt?
|
|
206
|
+
|
|
207
|
+
- **5 APIs, one key** — screenshot, PDF, OG image, browser automation, and MCP server. Stop paying for separate tools.
|
|
208
|
+
- **Clean captures** — automatic ad blocking, cookie banner removal, chat widget suppression, tracker blocking.
|
|
209
|
+
- **25+ device presets** — iPhone SE to Galaxy S24 Ultra, iPad Pro, MacBook, Desktop 4K.
|
|
210
|
+
- **Ship in 5 minutes** — plain HTTP, no SDKs required, works in any language.
|
|
211
|
+
- **Inline results** — screenshots and OG images appear directly in your AI chat.
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Links
|
|
216
|
+
|
|
217
|
+
- **Website:** [pagebolt.dev](https://pagebolt.dev)
|
|
218
|
+
- **API Docs:** [pagebolt.dev/docs.html](https://pagebolt.dev/docs.html)
|
|
219
|
+
- **npm:** [npmjs.com/package/pagebolt-mcp](https://www.npmjs.com/package/pagebolt-mcp)
|
|
220
|
+
- **Issues:** [github.com/Custodia-Admin/pagebolt-mcp/issues](https://github.com/Custodia-Admin/pagebolt-mcp/issues)
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## License
|
|
225
|
+
|
|
226
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pagebolt-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for PageBolt — take screenshots, generate PDFs, and create OG images from AI coding assistants like Claude, Cursor, and Windsurf.",
|
|
5
|
+
"main": "src/index.mjs",
|
|
6
|
+
"bin": {
|
|
7
|
+
"pagebolt-mcp": "src/index.mjs"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node src/index.mjs"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"mcp",
|
|
15
|
+
"mcp-server",
|
|
16
|
+
"model-context-protocol",
|
|
17
|
+
"screenshot",
|
|
18
|
+
"screenshot-api",
|
|
19
|
+
"pdf",
|
|
20
|
+
"pdf-generation",
|
|
21
|
+
"og-image",
|
|
22
|
+
"open-graph",
|
|
23
|
+
"social-card",
|
|
24
|
+
"browser-automation",
|
|
25
|
+
"puppeteer",
|
|
26
|
+
"web-capture",
|
|
27
|
+
"pagebolt",
|
|
28
|
+
"ai-tools",
|
|
29
|
+
"claude",
|
|
30
|
+
"cursor",
|
|
31
|
+
"windsurf",
|
|
32
|
+
"cline",
|
|
33
|
+
"devtools"
|
|
34
|
+
],
|
|
35
|
+
"author": "PageBolt <hello@pagebolt.dev> (https://pagebolt.dev)",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "git+https://github.com/Custodia-Admin/pagebolt-mcp.git"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://pagebolt.dev",
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/Custodia-Admin/pagebolt-mcp/issues"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18.0.0"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"src/",
|
|
50
|
+
"README.md",
|
|
51
|
+
"LICENSE",
|
|
52
|
+
"server.json"
|
|
53
|
+
],
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
56
|
+
"zod": "^4.3.6"
|
|
57
|
+
}
|
|
58
|
+
}
|
package/server.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pagebolt",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Take screenshots, generate PDFs, and create OG images from AI coding assistants.",
|
|
5
|
+
"homepage": "https://pagebolt.dev",
|
|
6
|
+
"repository": "https://github.com/Custodia-Admin/pagebolt-mcp",
|
|
7
|
+
"tools": [
|
|
8
|
+
{
|
|
9
|
+
"name": "take_screenshot",
|
|
10
|
+
"description": "Capture a screenshot of a URL, HTML, or Markdown. 30+ parameters including device emulation, ad blocking, dark mode, geolocation, and more."
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "generate_pdf",
|
|
14
|
+
"description": "Generate a PDF from a URL or HTML content. Supports A4/Letter/Legal, margins, landscape, and page ranges."
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "create_og_image",
|
|
18
|
+
"description": "Create Open Graph / social card images from templates or custom HTML."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "run_sequence",
|
|
22
|
+
"description": "Multi-step browser automation: navigate, click, fill, screenshot — all in one session."
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "list_devices",
|
|
26
|
+
"description": "List 25+ device presets for viewport emulation (iPhone, iPad, MacBook, etc.)."
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "check_usage",
|
|
30
|
+
"description": "Check current API usage and plan limits."
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"config": {
|
|
34
|
+
"required": {
|
|
35
|
+
"PAGEBOLT_API_KEY": "Your PageBolt API key (get one free at https://pagebolt.dev)"
|
|
36
|
+
},
|
|
37
|
+
"optional": {
|
|
38
|
+
"PAGEBOLT_BASE_URL": "API base URL (default: https://pagebolt.dev)"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/index.mjs
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PageBolt MCP Server
|
|
5
|
+
*
|
|
6
|
+
* A Model Context Protocol (MCP) server that exposes PageBolt's
|
|
7
|
+
* screenshot, PDF, and OG image APIs as tools for AI coding assistants
|
|
8
|
+
* (Claude Desktop, Cursor, Windsurf, Cline, etc.).
|
|
9
|
+
*
|
|
10
|
+
* Get your free API key at https://pagebolt.dev
|
|
11
|
+
*
|
|
12
|
+
* Configuration (environment variables):
|
|
13
|
+
* PAGEBOLT_API_KEY — Required. Your PageBolt API key.
|
|
14
|
+
* PAGEBOLT_BASE_URL — Optional. Defaults to https://pagebolt.dev
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* npx pagebolt-mcp
|
|
18
|
+
* # or after global install:
|
|
19
|
+
* pagebolt-mcp
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
23
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
24
|
+
import { z } from 'zod';
|
|
25
|
+
import { writeFileSync } from 'node:fs';
|
|
26
|
+
import { resolve } from 'node:path';
|
|
27
|
+
|
|
28
|
+
// ─── Configuration ───────────────────────────────────────────────
|
|
29
|
+
const API_KEY = process.env.PAGEBOLT_API_KEY;
|
|
30
|
+
const BASE_URL = (process.env.PAGEBOLT_BASE_URL || 'https://pagebolt.dev').replace(/\/$/, '');
|
|
31
|
+
|
|
32
|
+
if (!API_KEY) {
|
|
33
|
+
console.error(
|
|
34
|
+
'ERROR: PAGEBOLT_API_KEY environment variable is required.\n\n' +
|
|
35
|
+
'Get your free API key at https://pagebolt.dev\n\n' +
|
|
36
|
+
'Then set it in your MCP client config:\n\n' +
|
|
37
|
+
' Claude Desktop (~/.claude/claude_desktop_config.json):\n' +
|
|
38
|
+
' "env": { "PAGEBOLT_API_KEY": "pf_live_..." }\n\n' +
|
|
39
|
+
' Cursor (.cursor/mcp.json):\n' +
|
|
40
|
+
' "env": { "PAGEBOLT_API_KEY": "pf_live_..." }\n'
|
|
41
|
+
);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ─── HTTP helper ─────────────────────────────────────────────────
|
|
46
|
+
async function callApi(endpoint, options = {}) {
|
|
47
|
+
const url = `${BASE_URL}${endpoint}`;
|
|
48
|
+
const method = options.method || 'GET';
|
|
49
|
+
const headers = {
|
|
50
|
+
'x-api-key': API_KEY,
|
|
51
|
+
'user-agent': 'pagebolt-mcp/1.0.0',
|
|
52
|
+
...(options.body ? { 'Content-Type': 'application/json' } : {}),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const res = await fetch(url, {
|
|
56
|
+
method,
|
|
57
|
+
headers,
|
|
58
|
+
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
let errorMsg;
|
|
63
|
+
try {
|
|
64
|
+
const errJson = await res.json();
|
|
65
|
+
errorMsg = errJson.error || JSON.stringify(errJson);
|
|
66
|
+
} catch {
|
|
67
|
+
errorMsg = `HTTP ${res.status} ${res.statusText}`;
|
|
68
|
+
}
|
|
69
|
+
throw new Error(`PageBolt API error: ${errorMsg}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return res;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ─── MIME type helper ────────────────────────────────────────────
|
|
76
|
+
function imageMimeType(format) {
|
|
77
|
+
const map = { png: 'image/png', jpeg: 'image/jpeg', jpg: 'image/jpeg', webp: 'image/webp' };
|
|
78
|
+
return map[format] || 'image/png';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ─── Create MCP Server ──────────────────────────────────────────
|
|
82
|
+
const server = new McpServer({
|
|
83
|
+
name: 'pagebolt',
|
|
84
|
+
version: '1.0.0',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// ─── Tool: take_screenshot ──────────────────────────────────────
|
|
88
|
+
server.tool(
|
|
89
|
+
'take_screenshot',
|
|
90
|
+
'Capture a screenshot of a URL, HTML, or Markdown content. 30+ parameters including device emulation, ad/chat/tracker blocking, metadata extraction, geolocation, timezone, and more. Returns an image (PNG, JPEG, or WebP).',
|
|
91
|
+
{
|
|
92
|
+
// ── Source ──
|
|
93
|
+
url: z.string().url().optional().describe('URL to capture (required if no html/markdown)'),
|
|
94
|
+
html: z.string().optional().describe('Raw HTML to render (required if no url/markdown)'),
|
|
95
|
+
markdown: z.string().optional().describe('Render Markdown content as a screenshot'),
|
|
96
|
+
// ── Viewport ──
|
|
97
|
+
width: z.number().int().min(1).max(3840).optional().describe('Viewport width in pixels (default: 1280)'),
|
|
98
|
+
height: z.number().int().min(1).max(2160).optional().describe('Viewport height in pixels (default: 720)'),
|
|
99
|
+
viewportDevice: z.string().optional().describe('Device preset for viewport emulation (e.g. "iphone_14_pro", "macbook_pro_14"). Use list_devices to see all presets.'),
|
|
100
|
+
viewportMobile: z.boolean().optional().describe('Enable mobile meta viewport emulation'),
|
|
101
|
+
viewportHasTouch: z.boolean().optional().describe('Enable touch event emulation'),
|
|
102
|
+
deviceScaleFactor: z.number().min(1).max(3).optional().describe('Device pixel ratio, use 2 for retina (default: 1)'),
|
|
103
|
+
// ── Output format ──
|
|
104
|
+
format: z.enum(['png', 'jpeg', 'webp']).optional().describe('Image format (default: png)'),
|
|
105
|
+
quality: z.number().int().min(1).max(100).optional().describe('JPEG/WebP quality 1-100 (default: 80)'),
|
|
106
|
+
omitBackground: z.boolean().optional().describe('Transparent background (PNG/WebP only)'),
|
|
107
|
+
// ── Capture region ──
|
|
108
|
+
fullPage: z.boolean().optional().describe('Capture the full scrollable page (default: false)'),
|
|
109
|
+
fullPageScroll: z.boolean().optional().describe('Auto-scroll page before capture to trigger lazy-loaded images'),
|
|
110
|
+
fullPageMaxHeight: z.number().int().optional().describe('Maximum pixel height cap for full-page captures'),
|
|
111
|
+
selector: z.string().optional().describe('CSS selector to capture a specific element'),
|
|
112
|
+
clip: z.object({
|
|
113
|
+
x: z.number(),
|
|
114
|
+
y: z.number(),
|
|
115
|
+
width: z.number(),
|
|
116
|
+
height: z.number(),
|
|
117
|
+
}).optional().describe('Crop region { x, y, width, height } in pixels'),
|
|
118
|
+
// ── Timing ──
|
|
119
|
+
delay: z.number().int().min(0).max(10000).optional().describe('Milliseconds to wait before capture (default: 0)'),
|
|
120
|
+
waitUntil: z.enum(['load', 'domcontentloaded', 'networkidle0', 'networkidle2']).optional().describe('When to consider navigation finished (default: networkidle2)'),
|
|
121
|
+
waitForSelector: z.string().optional().describe('Wait for this CSS selector to appear before capturing'),
|
|
122
|
+
// ── Emulation ──
|
|
123
|
+
darkMode: z.boolean().optional().describe('Emulate dark color scheme (default: false)'),
|
|
124
|
+
reducedMotion: z.boolean().optional().describe('Emulate prefers-reduced-motion to disable animations'),
|
|
125
|
+
mediaType: z.enum(['screen', 'print']).optional().describe('Emulate CSS media type'),
|
|
126
|
+
timeZone: z.string().optional().describe('Override browser timezone (e.g. "America/New_York")'),
|
|
127
|
+
geolocation: z.object({
|
|
128
|
+
latitude: z.number(),
|
|
129
|
+
longitude: z.number(),
|
|
130
|
+
accuracy: z.number().optional(),
|
|
131
|
+
}).optional().describe('Emulate geolocation { latitude, longitude, accuracy? }'),
|
|
132
|
+
userAgent: z.string().optional().describe('Override the browser User-Agent string'),
|
|
133
|
+
// ── Auth & headers ──
|
|
134
|
+
cookies: z.array(
|
|
135
|
+
z.union([
|
|
136
|
+
z.string(),
|
|
137
|
+
z.object({
|
|
138
|
+
name: z.string(),
|
|
139
|
+
value: z.string(),
|
|
140
|
+
domain: z.string().optional(),
|
|
141
|
+
}),
|
|
142
|
+
])
|
|
143
|
+
).optional().describe('Cookies to set — array of "name=value" strings or { name, value, domain? } objects'),
|
|
144
|
+
headers: z.record(z.string()).optional().describe('Extra HTTP headers to send with the request'),
|
|
145
|
+
authorization: z.string().optional().describe('Authorization header value (e.g. "Bearer <token>")'),
|
|
146
|
+
bypassCSP: z.boolean().optional().describe('Bypass Content-Security-Policy on the page'),
|
|
147
|
+
// ── Content manipulation ──
|
|
148
|
+
hideSelectors: z.array(z.string()).optional().describe('Array of CSS selectors to hide before capture'),
|
|
149
|
+
click: z.string().optional().describe('CSS selector to click before capturing the screenshot'),
|
|
150
|
+
blockBanners: z.boolean().optional().describe('Hide cookie consent banners (default: false)'),
|
|
151
|
+
blockAds: z.boolean().optional().describe('Block advertisements on the page'),
|
|
152
|
+
blockChats: z.boolean().optional().describe('Block live chat widgets on the page'),
|
|
153
|
+
blockTrackers: z.boolean().optional().describe('Block tracking scripts on the page'),
|
|
154
|
+
// ── Extras ──
|
|
155
|
+
extractMetadata: z.boolean().optional().describe('Extract page metadata (title, description, OG tags) alongside the screenshot'),
|
|
156
|
+
},
|
|
157
|
+
async (params) => {
|
|
158
|
+
if (!params.url && !params.html && !params.markdown) {
|
|
159
|
+
return { content: [{ type: 'text', text: 'Error: One of "url", "html", or "markdown" is required.' }], isError: true };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const res = await callApi('/api/v1/screenshot', {
|
|
163
|
+
method: 'POST',
|
|
164
|
+
body: { ...params, response_type: 'json' },
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const data = await res.json();
|
|
168
|
+
const format = params.format || 'png';
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
content: [
|
|
172
|
+
{
|
|
173
|
+
type: 'image',
|
|
174
|
+
data: data.data,
|
|
175
|
+
mimeType: imageMimeType(format),
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
type: 'text',
|
|
179
|
+
text: `Screenshot captured successfully. Format: ${format}, Size: ${data.size_bytes} bytes, Duration: ${data.duration_ms}ms`,
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// ─── Tool: generate_pdf ─────────────────────────────────────────
|
|
187
|
+
server.tool(
|
|
188
|
+
'generate_pdf',
|
|
189
|
+
'Generate a PDF from a URL or HTML content. Saves the PDF to disk and returns the file path.',
|
|
190
|
+
{
|
|
191
|
+
url: z.string().url().optional().describe('URL to render as PDF (required if no html)'),
|
|
192
|
+
html: z.string().optional().describe('Raw HTML to render as PDF (required if no url)'),
|
|
193
|
+
format: z.string().optional().describe('Paper format: A4, Letter, Legal, Tabloid (default: A4)'),
|
|
194
|
+
landscape: z.boolean().optional().describe('Landscape orientation (default: false)'),
|
|
195
|
+
printBackground: z.boolean().optional().describe('Include CSS backgrounds (default: true)'),
|
|
196
|
+
margin: z.string().optional().describe('CSS margin for all sides, e.g. "1cm" or "0.5in"'),
|
|
197
|
+
scale: z.number().min(0.1).max(2).optional().describe('Rendering scale 0.1-2 (default: 1)'),
|
|
198
|
+
pageRanges: z.string().optional().describe('Page ranges to include, e.g. "1-5, 8"'),
|
|
199
|
+
delay: z.number().int().min(0).max(10000).optional().describe('Milliseconds to wait before rendering (default: 0)'),
|
|
200
|
+
saveTo: z.string().optional().describe('Output file path (default: ./output.pdf)'),
|
|
201
|
+
},
|
|
202
|
+
async (params) => {
|
|
203
|
+
if (!params.url && !params.html) {
|
|
204
|
+
return { content: [{ type: 'text', text: 'Error: Either "url" or "html" is required.' }], isError: true };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const { saveTo, ...apiParams } = params;
|
|
208
|
+
const res = await callApi('/api/v1/pdf', {
|
|
209
|
+
method: 'POST',
|
|
210
|
+
body: { ...apiParams, response_type: 'json' },
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const data = await res.json();
|
|
214
|
+
const outputPath = resolve(saveTo || './output.pdf');
|
|
215
|
+
|
|
216
|
+
// Decode base64 and write to disk
|
|
217
|
+
const buffer = Buffer.from(data.data, 'base64');
|
|
218
|
+
writeFileSync(outputPath, buffer);
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
content: [
|
|
222
|
+
{
|
|
223
|
+
type: 'text',
|
|
224
|
+
text: `PDF generated successfully.\n` +
|
|
225
|
+
` File: ${outputPath}\n` +
|
|
226
|
+
` Size: ${data.size_bytes} bytes\n` +
|
|
227
|
+
` Duration: ${data.duration_ms}ms`,
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
// ─── Tool: create_og_image ──────────────────────────────────────
|
|
235
|
+
server.tool(
|
|
236
|
+
'create_og_image',
|
|
237
|
+
'Generate an Open Graph / social card image. Returns an image using built-in templates or custom HTML.',
|
|
238
|
+
{
|
|
239
|
+
template: z.enum(['default', 'minimal', 'gradient']).optional().describe('Built-in template name (default: "default")'),
|
|
240
|
+
html: z.string().optional().describe('Custom HTML template (overrides template parameter)'),
|
|
241
|
+
title: z.string().optional().describe('Main title text (default: "Your Title Here")'),
|
|
242
|
+
subtitle: z.string().optional().describe('Subtitle text'),
|
|
243
|
+
logo: z.string().optional().describe('Logo image URL'),
|
|
244
|
+
bgColor: z.string().optional().describe('Background color as hex, e.g. "#0f172a"'),
|
|
245
|
+
textColor: z.string().optional().describe('Text color as hex, e.g. "#f8fafc"'),
|
|
246
|
+
accentColor: z.string().optional().describe('Accent color as hex, e.g. "#6366f1"'),
|
|
247
|
+
bgImage: z.string().optional().describe('Background image URL'),
|
|
248
|
+
width: z.number().int().min(1).max(2400).optional().describe('Image width in pixels (default: 1200)'),
|
|
249
|
+
height: z.number().int().min(1).max(1260).optional().describe('Image height in pixels (default: 630)'),
|
|
250
|
+
format: z.enum(['png', 'jpeg', 'webp']).optional().describe('Image format (default: png)'),
|
|
251
|
+
},
|
|
252
|
+
async (params) => {
|
|
253
|
+
const res = await callApi('/api/v1/og-image', {
|
|
254
|
+
method: 'POST',
|
|
255
|
+
body: { ...params, response_type: 'json' },
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const data = await res.json();
|
|
259
|
+
const format = params.format || 'png';
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
content: [
|
|
263
|
+
{
|
|
264
|
+
type: 'image',
|
|
265
|
+
data: data.data,
|
|
266
|
+
mimeType: imageMimeType(format),
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
type: 'text',
|
|
270
|
+
text: `OG image created successfully. Format: ${format}, Size: ${data.size_bytes} bytes, Duration: ${data.duration_ms}ms`,
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
// ─── Tool: run_sequence ─────────────────────────────────────────
|
|
278
|
+
server.tool(
|
|
279
|
+
'run_sequence',
|
|
280
|
+
'Execute a multi-step browser automation sequence. Navigate pages, interact with elements (click, fill, select), and capture multiple screenshots/PDFs in a single browser session. Each output counts as 1 API request.',
|
|
281
|
+
{
|
|
282
|
+
steps: z.array(
|
|
283
|
+
z.object({
|
|
284
|
+
action: z.enum([
|
|
285
|
+
'navigate', 'click', 'fill', 'select', 'hover',
|
|
286
|
+
'scroll', 'wait', 'wait_for', 'evaluate',
|
|
287
|
+
'screenshot', 'pdf',
|
|
288
|
+
]).describe('The action to perform'),
|
|
289
|
+
url: z.string().url().optional().describe('URL to navigate to (for navigate action)'),
|
|
290
|
+
selector: z.string().optional().describe('CSS selector for the target element'),
|
|
291
|
+
value: z.string().optional().describe('Value to type or select'),
|
|
292
|
+
ms: z.number().int().min(0).max(10000).optional().describe('Milliseconds to wait (for wait action)'),
|
|
293
|
+
timeout: z.number().int().min(0).max(15000).optional().describe('Timeout in ms for wait_for (default: 10000)'),
|
|
294
|
+
x: z.number().optional().describe('Horizontal scroll position'),
|
|
295
|
+
y: z.number().optional().describe('Vertical scroll position'),
|
|
296
|
+
script: z.string().max(5000).optional().describe('JavaScript to execute in page context (for evaluate action)'),
|
|
297
|
+
name: z.string().optional().describe('Name for the output (for screenshot/pdf actions)'),
|
|
298
|
+
format: z.string().optional().describe('Image format: png, jpeg, webp (screenshot) or A4, Letter (pdf)'),
|
|
299
|
+
fullPage: z.boolean().optional().describe('Capture full scrollable page (for screenshot action)'),
|
|
300
|
+
quality: z.number().int().min(1).max(100).optional().describe('JPEG/WebP quality (for screenshot action)'),
|
|
301
|
+
landscape: z.boolean().optional().describe('Landscape orientation (for pdf action)'),
|
|
302
|
+
printBackground: z.boolean().optional().describe('Include CSS backgrounds (for pdf action)'),
|
|
303
|
+
margin: z.string().optional().describe('CSS margin for all sides (for pdf action)'),
|
|
304
|
+
scale: z.number().min(0.1).max(2).optional().describe('Rendering scale (for pdf action)'),
|
|
305
|
+
})
|
|
306
|
+
).min(1).max(20).describe('Array of steps to execute in order. Must include at least one screenshot or pdf step. Max 20 steps, max 5 outputs.'),
|
|
307
|
+
viewport: z.object({
|
|
308
|
+
width: z.number().int().min(320).max(3840).optional().describe('Viewport width (default: 1280)'),
|
|
309
|
+
height: z.number().int().min(200).max(2160).optional().describe('Viewport height (default: 720)'),
|
|
310
|
+
}).optional().describe('Browser viewport size'),
|
|
311
|
+
darkMode: z.boolean().optional().describe('Emulate dark color scheme (default: false)'),
|
|
312
|
+
blockBanners: z.boolean().optional().describe('Hide cookie consent banners (default: false)'),
|
|
313
|
+
deviceScaleFactor: z.number().min(1).max(3).optional().describe('Device pixel ratio (default: 1)'),
|
|
314
|
+
},
|
|
315
|
+
async (params) => {
|
|
316
|
+
if (!params.steps || params.steps.length === 0) {
|
|
317
|
+
return { content: [{ type: 'text', text: 'Error: "steps" must be a non-empty array.' }], isError: true };
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
try {
|
|
321
|
+
const res = await callApi('/api/v1/sequence', {
|
|
322
|
+
method: 'POST',
|
|
323
|
+
body: params,
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
const data = await res.json();
|
|
327
|
+
const content = [];
|
|
328
|
+
|
|
329
|
+
for (const output of data.outputs) {
|
|
330
|
+
if (output.type === 'screenshot') {
|
|
331
|
+
content.push({
|
|
332
|
+
type: 'image',
|
|
333
|
+
data: output.data,
|
|
334
|
+
mimeType: output.content_type,
|
|
335
|
+
});
|
|
336
|
+
content.push({
|
|
337
|
+
type: 'text',
|
|
338
|
+
text: `[${output.name}] Screenshot — ${output.format}, ${output.size_bytes} bytes, step ${output.step_index}`,
|
|
339
|
+
});
|
|
340
|
+
} else if (output.type === 'pdf') {
|
|
341
|
+
content.push({
|
|
342
|
+
type: 'text',
|
|
343
|
+
text: `[${output.name}] PDF generated — ${output.format}, ${output.size_bytes} bytes, step ${output.step_index} (base64 data available in raw response)`,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const failedSteps = data.step_results.filter(s => s.status === 'error');
|
|
349
|
+
let summary = `Sequence complete: ${data.steps_completed}/${data.total_steps} steps, ${data.outputs.length} outputs, ${data.total_duration_ms}ms total.`;
|
|
350
|
+
if (failedSteps.length > 0) {
|
|
351
|
+
summary += `\nFailed steps: ${failedSteps.map(s => `Step ${s.step_index} (${s.action}): ${s.error}`).join('; ')}`;
|
|
352
|
+
}
|
|
353
|
+
summary += `\nUsage: ${data.usage.outputs_charged} request(s) charged, ${data.usage.remaining} remaining.`;
|
|
354
|
+
|
|
355
|
+
content.push({ type: 'text', text: summary });
|
|
356
|
+
return { content };
|
|
357
|
+
} catch (err) {
|
|
358
|
+
return { content: [{ type: 'text', text: `Sequence error: ${err.message}` }], isError: true };
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
// ─── Tool: list_devices ─────────────────────────────────────────
|
|
364
|
+
server.tool(
|
|
365
|
+
'list_devices',
|
|
366
|
+
'List all available device presets for viewport emulation (e.g. iphone_14_pro, macbook_pro_14). Use the returned device names with the viewportDevice parameter in take_screenshot.',
|
|
367
|
+
{},
|
|
368
|
+
async () => {
|
|
369
|
+
const res = await callApi('/api/v1/devices');
|
|
370
|
+
const data = await res.json();
|
|
371
|
+
|
|
372
|
+
const lines = data.devices.map((d) => {
|
|
373
|
+
const touch = d.hasTouch ? ', touch' : '';
|
|
374
|
+
const mobile = d.isMobile ? ', mobile' : '';
|
|
375
|
+
return ` ${d.name} — ${d.viewport.width}x${d.viewport.height} @${d.viewport.deviceScaleFactor}x${mobile}${touch}`;
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
content: [
|
|
380
|
+
{
|
|
381
|
+
type: 'text',
|
|
382
|
+
text:
|
|
383
|
+
`Available device presets (${data.devices.length}):\n` +
|
|
384
|
+
lines.join('\n') +
|
|
385
|
+
`\n\nUse the device name as the "viewportDevice" parameter in take_screenshot.`,
|
|
386
|
+
},
|
|
387
|
+
],
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
// ─── Tool: check_usage ──────────────────────────────────────────
|
|
393
|
+
server.tool(
|
|
394
|
+
'check_usage',
|
|
395
|
+
'Check your current PageBolt API usage and plan limits.',
|
|
396
|
+
{},
|
|
397
|
+
async () => {
|
|
398
|
+
const res = await callApi('/api/v1/usage');
|
|
399
|
+
const data = await res.json();
|
|
400
|
+
|
|
401
|
+
const { plan, usage } = data;
|
|
402
|
+
const pct = usage.limit > 0 ? Math.round((usage.current / usage.limit) * 100) : 0;
|
|
403
|
+
|
|
404
|
+
return {
|
|
405
|
+
content: [
|
|
406
|
+
{
|
|
407
|
+
type: 'text',
|
|
408
|
+
text:
|
|
409
|
+
`PageBolt Usage\n` +
|
|
410
|
+
` Plan: ${plan}\n` +
|
|
411
|
+
` Used: ${usage.current.toLocaleString()} / ${usage.limit.toLocaleString()} requests\n` +
|
|
412
|
+
` Remaining: ${usage.remaining.toLocaleString()}\n` +
|
|
413
|
+
` Usage: ${pct}%`,
|
|
414
|
+
},
|
|
415
|
+
],
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
// ─── Start ──────────────────────────────────────────────────────
|
|
421
|
+
const transport = new StdioServerTransport();
|
|
422
|
+
await server.connect(transport);
|