pagebolt-mcp 1.6.0 → 1.7.1

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.
Files changed (3) hide show
  1. package/README.md +2 -0
  2. package/package.json +1 -1
  3. package/src/index.mjs +105 -16
package/README.md CHANGED
@@ -8,6 +8,8 @@ Take screenshots, generate PDFs, create OG images, inspect pages, and record dem
8
8
 
9
9
  **Works with Claude Desktop, Cursor, Windsurf, Cline, and any MCP-compatible client.**
10
10
 
11
+ <img width="1280" height="1279" alt="pagebolt-screenshot_1" src="https://github.com/user-attachments/assets/fd21a372-df4d-41cd-baf4-5b6dd6a9a685" />
12
+
11
13
  ---
12
14
 
13
15
  ## What It Does
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pagebolt-mcp",
3
- "version": "1.6.0",
3
+ "version": "1.7.1",
4
4
  "description": "MCP server for PageBolt — take screenshots, generate PDFs, create OG images, inspect pages, record demo videos with Audio Guide narration, from AI coding assistants like Claude, Cursor, and Windsurf.",
5
5
  "main": "src/index.mjs",
6
6
  "module": "src/index.mjs",
package/src/index.mjs CHANGED
@@ -57,7 +57,7 @@ async function callApi(endpoint, options = {}) {
57
57
  const method = options.method || 'GET';
58
58
  const headers = {
59
59
  'x-api-key': API_KEY,
60
- 'user-agent': 'pagebolt-mcp/1.5.2',
60
+ 'user-agent': 'pagebolt-mcp/1.7.0',
61
61
  ...(options.body ? { 'Content-Type': 'application/json' } : {}),
62
62
  };
63
63
 
@@ -146,6 +146,8 @@ PageBolt gives you 8 tools for web capture and browser automation. All tools use
146
146
  | inspect_page | Get structured map of page elements with CSS selectors | 1 request |
147
147
  | list_devices | List 25+ device presets (iPhone, iPad, MacBook, etc.) | 0 (free) |
148
148
  | check_usage | Check current API usage and plan limits | 0 (free) |
149
+ | create_session | Create a persistent browser session (Starter+ only) | 0 (free to create) |
150
+ | destroy_session | Destroy a persistent browser session | 0 (free) |
149
151
 
150
152
  ## Key Workflow: Inspect Before You Interact
151
153
 
@@ -168,13 +170,17 @@ record_video supports polished video output:
168
170
  - frame: { enabled: true, style: "macos" } — browser chrome around the video
169
171
  - background: { enabled: true, type: "gradient", gradient: "ocean" } — gradient/glass background with padding
170
172
  - cursor: { style: "classic", persist: true } — always-visible cursor
171
- - Per-step zoom: add zoom: { enabled: true } on click steps
172
173
  - **Step notes (IMPORTANT)**: Add a "note" field to EVERY action step for guided-tour-style tooltip annotations. Notes appear as beautiful styled tooltips near the element being interacted with. Example: { action: "click", selector: "#btn", note: "Click here to open settings" }. The only steps that should NOT have notes are wait/wait_for pauses.
173
174
  - **Audio Guide**: Add audioGuide: { enabled: true, script: "Welcome. {{1}} Click here. {{2}} Done." } for AI voice narration. Two modes: (1) Per-step — add "narration" text to individual steps. (2) Script — provide a single "script" with {{N}} markers for continuous narration synchronized to steps.
174
175
  - Audio Guide voices: ava, andrew, emma, brian, aria, guy, jenny, davis, christopher, michelle (Azure) or alloy, echo, fable, nova, onyx, shimmer (OpenAI).
175
- - **Live wait steps**: Add live: true to wait steps to capture animated content (transitions, loading spinners) instead of freezing the last frame.
176
176
  - **Variables**: Pass variables: { "base_url": "https://example.com" } and use {{base_url}} in step URLs/values for reusable recordings.
177
177
 
178
+ ## IMPORTANT: Video Step Best Practices
179
+
180
+ - **Do NOT add wait steps between every action.** The "pace" parameter already adds natural pauses between steps. Only use wait when: (1) the page needs time to load after navigation, or (2) you want to hold on a view for narration. A typical video should have very few wait steps.
181
+ - **Do NOT use zoom unless the user explicitly asks for it.** Zoom adds visual complexity and encoding time. Omit zoom entirely by default.
182
+ - **Keep videos concise.** A good demo has 5-15 action steps (navigate, click, fill, hover, scroll). More steps = longer encoding time and larger files.
183
+
178
184
  ## Common Parameters (available on most tools)
179
185
 
180
186
  - blockBanners: true — hides cookie consent banners (GDPR popups, OneTrust, CookieBot, etc.)
@@ -213,7 +219,7 @@ Use blockBanners on almost every request to get clean captures. Combine blockAds
213
219
  function createConfiguredServer() {
214
220
  const srv = new McpServer({
215
221
  name: 'pagebolt',
216
- version: '1.5.0',
222
+ version: '1.7.0',
217
223
  }, {
218
224
  instructions: SERVER_INSTRUCTIONS,
219
225
  });
@@ -300,6 +306,8 @@ server.tool(
300
306
  extractMetadata: z.boolean().optional().describe('Extract page metadata (title, description, OG tags) alongside the screenshot'),
301
307
  // ── Styling ──
302
308
  style: styleSchema,
309
+ // ── Session ──
310
+ session_id: z.string().optional().describe('Persistent session ID (Starter+ only). Reuse a live browser page created with create_session — browser state (cookies, localStorage, auth) carries over from previous requests in this session.'),
303
311
  },
304
312
  async (params) => {
305
313
  if (!params.url && !params.html && !params.markdown) {
@@ -479,7 +487,7 @@ server.tool(
479
487
  'screenshot', 'pdf',
480
488
  ]).describe('The action to perform'),
481
489
  url: z.string().url().optional().describe('URL to navigate to (for navigate action)'),
482
- selector: z.string().optional().describe('CSS selector for the target element'),
490
+ selector: z.string().optional().describe('CSS selector for the target element (also used for element screenshots)'),
483
491
  value: z.string().optional().describe('Value to type or select'),
484
492
  ms: z.number().int().min(0).max(10000).optional().describe('Milliseconds to wait (for wait action)'),
485
493
  timeout: z.number().int().min(0).max(15000).optional().describe('Timeout in ms for wait_for (default: 10000)'),
@@ -491,7 +499,6 @@ server.tool(
491
499
  fullPage: z.boolean().optional().describe('Capture full scrollable page (for screenshot action)'),
492
500
  fullPageScroll: z.boolean().optional().describe('Auto-scroll for lazy images (for screenshot action)'),
493
501
  quality: z.number().int().min(1).max(100).optional().describe('JPEG/WebP quality (for screenshot action)'),
494
- selector: z.string().optional().describe('Element selector (for screenshot action)'),
495
502
  omitBackground: z.boolean().optional().describe('Transparent background (for screenshot action)'),
496
503
  delay: z.number().int().min(0).max(10000).optional().describe('Pre-capture delay in ms (for screenshot action)'),
497
504
  landscape: z.boolean().optional().describe('Landscape orientation (for pdf action)'),
@@ -511,6 +518,7 @@ server.tool(
511
518
  blockChats: z.boolean().optional().describe('Block live chat widgets'),
512
519
  blockTrackers: z.boolean().optional().describe('Block tracking scripts'),
513
520
  deviceScaleFactor: z.number().min(1).max(3).optional().describe('Device pixel ratio (default: 1)'),
521
+ session_id: z.string().optional().describe('Persistent session ID (Starter+ only). Reuse a live browser page created with create_session — browser state (cookies, localStorage, auth) carries over from previous requests in this session.'),
514
522
  },
515
523
  async (params) => {
516
524
  if (!params.steps || params.steps.length === 0) {
@@ -565,7 +573,7 @@ server.tool(
565
573
  // ═══════════════════════════════════════════════════════════════════
566
574
  server.tool(
567
575
  'record_video',
568
- 'Record a professional demo video of a multi-step browser automation sequence. Produces MP4/WebM/GIF with cursor highlighting, click effects, smooth movement, per-step zoom, step notes, browser frame (macOS/Windows), gradient/glass backgrounds, and more. Costs 3 API requests. Saves to disk.',
576
+ 'Record a professional demo video of a multi-step browser automation sequence. Produces MP4/WebM/GIF with cursor highlighting, click effects, smooth movement, step notes, browser frame (macOS/Windows), gradient/glass backgrounds, and more. Costs 3 API requests. Saves to disk. BEST PRACTICE: Keep videos concise (5-15 action steps). Do NOT add wait steps between every action — the pace parameter handles timing. Only use wait for page loads or narration holds. Do NOT use zoom unless the user explicitly asks for it.',
569
577
  {
570
578
  steps: z.array(
571
579
  z.object({
@@ -576,7 +584,7 @@ server.tool(
576
584
  url: z.string().url().optional().describe('URL to navigate to (for navigate action)'),
577
585
  selector: z.string().optional().describe('CSS selector for the target element'),
578
586
  value: z.string().optional().describe('Value to type or select'),
579
- ms: z.number().int().min(0).max(10000).optional().describe('Milliseconds to wait (for wait action)'),
587
+ ms: z.number().int().min(0).max(10000).optional().describe('Milliseconds to wait (for wait action). Only use wait steps when the page needs loading time or to hold for narration — the pace parameter handles inter-step timing automatically.'),
580
588
  timeout: z.number().int().min(0).max(15000).optional().describe('Timeout in ms for wait_for (default: 10000)'),
581
589
  x: z.number().optional().describe('Horizontal scroll position'),
582
590
  y: z.number().optional().describe('Vertical scroll position'),
@@ -585,11 +593,11 @@ server.tool(
585
593
  narration: z.string().max(500).optional().describe('Text to speak at this step (max 500 chars, requires audioGuide.enabled). Used in per-step mode.'),
586
594
  live: z.boolean().optional().describe('For wait steps: true captures animated content in real-time, false freezes a single frame (default: false)'),
587
595
  zoom: z.object({
588
- enabled: z.boolean().optional().describe('Enable zoom on this step (default: false)'),
596
+ enabled: z.boolean().optional().describe('Enable zoom on this step (default: false). Only use when user explicitly requests zoom.'),
589
597
  level: z.number().min(1.2).max(4).optional().describe('Zoom magnification (inherits from global zoom.level if not set)'),
590
- }).optional().describe('Per-step zoom override (for click/dblclick steps). Overrides global zoom settings.'),
598
+ }).optional().describe('Per-step zoom override. Do NOT add zoom unless the user specifically requests it — it adds encoding time and visual complexity.'),
591
599
  })
592
- ).min(1).max(50).describe('Array of steps to execute and record. Max steps depends on plan (10-50).'),
600
+ ).min(1).max(50).describe('Array of action steps to record. Keep concise: 5-15 steps is ideal. Do NOT pad with wait steps pace handles timing.'),
593
601
  viewport: z.object({
594
602
  width: z.number().int().min(320).max(3840).optional().describe('Viewport width (default: 1280)'),
595
603
  height: z.number().int().min(200).max(2160).optional().describe('Viewport height (default: 720)'),
@@ -612,8 +620,8 @@ server.tool(
612
620
  level: z.number().min(1.2).max(4).optional().describe('Default zoom magnification (default: 1.5)'),
613
621
  duration: z.number().int().min(400).max(3000).optional().describe('Zoom animation duration in ms (default: 1200)'),
614
622
  easing: z.enum(['ease-in-out', 'linear', 'ease']).optional().describe('Zoom animation easing (default: ease-in-out)'),
615
- }).optional().describe('Global zoom settings. Per-step zoom on click/dblclick steps overrides these.'),
616
- autoZoom: z.boolean().optional().describe('Shorthand: set to true to enable auto-zoom with defaults (same as zoom.enabled=true)'),
623
+ }).optional().describe('Global zoom settings. Only use when the user explicitly requests zoom. Do NOT enable by default.'),
624
+ autoZoom: z.boolean().optional().describe('Enable auto-zoom on all clicks (default: false). Only use when user explicitly requests zoom.'),
617
625
  // ── Click effects ──
618
626
  clickEffect: z.object({
619
627
  enabled: z.boolean().optional().describe('Show click ripple effects (default: true)'),
@@ -935,6 +943,87 @@ server.tool(
935
943
  }
936
944
  );
937
945
 
946
+ // ═══════════════════════════════════════════════════════════════════
947
+ // Tool: create_session — Persistent browser session (Starter+ only)
948
+ // ═══════════════════════════════════════════════════════════════════
949
+ server.tool(
950
+ 'create_session',
951
+ 'Create a persistent browser session (Starter+ plan required). The session keeps a live browser page open so you can reuse cookies, localStorage, and auth state across multiple take_screenshot or run_sequence calls. Pass the returned session_id to those tools. Sessions expire after 10 minutes of inactivity (hard cap: 30 minutes). Useful for AI agent workflows that log in once and then take multiple screenshots of authenticated pages.',
952
+ {
953
+ cookies: z.array(cookieSchema).optional().describe('Cookies to pre-load into the session browser page'),
954
+ viewport: z.object({
955
+ width: z.number().int().optional(),
956
+ height: z.number().int().optional(),
957
+ }).optional().describe('Viewport dimensions for the session browser page'),
958
+ stealth: z.boolean().optional().describe('Launch this session with stealth mode (bypasses bot detection). Note: stealth sessions use a dedicated browser and consume more memory.'),
959
+ },
960
+ async (params) => {
961
+ const res = await callApi('/api/v1/sessions', {
962
+ method: 'POST',
963
+ body: params,
964
+ });
965
+ const data = await res.json();
966
+ return {
967
+ content: [
968
+ {
969
+ type: 'text',
970
+ text:
971
+ `Session created.\n` +
972
+ ` session_id: ${data.session_id}\n` +
973
+ ` expires_at: ${data.expires_at}\n\n` +
974
+ `Pass session_id to take_screenshot or run_sequence to reuse this browser page.\n` +
975
+ `Note: ${data.note || 'Sessions do not persist across server restarts.'}`,
976
+ },
977
+ ],
978
+ };
979
+ }
980
+ );
981
+
982
+ // ═══════════════════════════════════════════════════════════════════
983
+ // Tool: list_sessions — List active persistent browser sessions
984
+ // ═══════════════════════════════════════════════════════════════════
985
+ server.tool(
986
+ 'list_sessions',
987
+ 'List all active persistent browser sessions for your API key. Returns session IDs, creation times, and expiry times. Useful for checking which sessions are still alive before reusing them.',
988
+ {},
989
+ async () => {
990
+ const data = await callApi('/api/v1/sessions', { method: 'GET' });
991
+ const sessions = data.sessions || [];
992
+ if (sessions.length === 0) {
993
+ return { content: [{ type: 'text', text: 'No active sessions.' }] };
994
+ }
995
+ const lines = sessions.map(s =>
996
+ `• ${s.session_id} expires: ${s.expires_at} created: ${s.created_at}`
997
+ );
998
+ return {
999
+ content: [{ type: 'text', text: `Active sessions (${sessions.length}):\n${lines.join('\n')}` }],
1000
+ };
1001
+ }
1002
+ );
1003
+
1004
+ // Tool: destroy_session — Explicitly close a persistent session
1005
+ // ═══════════════════════════════════════════════════════════════════
1006
+ server.tool(
1007
+ 'destroy_session',
1008
+ 'Explicitly destroy a persistent browser session before it expires. Frees the browser page immediately. Use this when you are done with a session to free up capacity.',
1009
+ {
1010
+ session_id: z.string().describe('The session ID to destroy (returned by create_session)'),
1011
+ },
1012
+ async (params) => {
1013
+ await callApi(`/api/v1/sessions/${encodeURIComponent(params.session_id)}`, {
1014
+ method: 'DELETE',
1015
+ });
1016
+ return {
1017
+ content: [
1018
+ {
1019
+ type: 'text',
1020
+ text: `Session ${params.session_id} destroyed successfully.`,
1021
+ },
1022
+ ],
1023
+ };
1024
+ }
1025
+ );
1026
+
938
1027
  } // end registerTools
939
1028
 
940
1029
  // ─── Prompts ────────────────────────────────────────────────────
@@ -1039,15 +1128,15 @@ Please follow this workflow:
1039
1128
 
1040
1129
  Important tips:
1041
1130
  - Use selectors from the inspect_page results — never guess selectors
1042
- - Add wait steps (ms: 800-1200) between interactions for visual clarity
1043
- - Use wait_for after navigation to ensure the page loads
1131
+ - Do NOT add wait steps between every action — the pace parameter already handles timing between steps. Only use wait when: (1) the page needs time to load new content after navigation, or (2) you need to hold on a view for narration.
1132
+ - Do NOT use zoom unless I specifically ask for it
1044
1133
  - **ALWAYS add a "note" field on every meaningful step** — notes render as styled tooltip annotations that explain what's happening, creating a guided tour experience. Examples:
1045
1134
  - navigate: note: "Opening the dashboard"
1046
1135
  - click: note: "This button creates a new project"
1047
1136
  - fill: note: "Enter your email to get started"
1048
1137
  - hover: note: "Hover to reveal the dropdown menu"
1049
1138
  - The ONLY steps without notes should be wait/wait_for (pauses)
1050
- - Keep to 15 steps or fewer for best results
1139
+ - Keep to 5-15 action steps for best results. Fewer steps = faster encoding and smaller files.
1051
1140
  - Each video costs 3 API requests`,
1052
1141
  },
1053
1142
  },