mcp-page-capture is a Model Context Protocol (MCP) server that orchestrates headless Chromium via Puppeteer to capture pixel-perfect screenshots of arbitrary URLs. It is optimized for Copilot/MCP-enabled environments and can be embedded into automated workflows or run as a standalone developer tool. - ๐ธ High-fidelity screenshots powered by Puppeteer and headless Chromium - โ๏ธ LLM-optimized schema
Add this skill
npx mdskills install chasesaurabh/mcp-page-captureFeature-rich MCP server with excellent LLM-optimized schema and comprehensive browser automation capabilities
1# mcp-page-capture23[](https://www.npmjs.com/package/mcp-page-capture)4[](https://github.com/chasesaurabh/mcp-page-capture)5[](LICENSE)67891011mcp-page-capture is a Model Context Protocol (MCP) server that orchestrates headless Chromium via Puppeteer to capture pixel-perfect screenshots of arbitrary URLs. It is optimized for Copilot/MCP-enabled environments and can be embedded into automated workflows or run as a standalone developer tool.1213## Features14- ๐ธ High-fidelity screenshots powered by Puppeteer and headless Chromium15- โ๏ธ **LLM-optimized schema** with minimal parameters exposed and sensible defaults16- ๐ Structured DOM extraction with optional CSS selectors for AI-friendly consumption17- ๐ฑ Device presets for mobile emulation (iPhone, iPad, Android, desktop)18- ๐ฏ **6 simplified steps** for LLM friendliness: `viewport`, `wait`, `fill`, `click`, `scroll`, `screenshot`19- ๐ค **Smart defaults** - screenshot auto-captured, field types auto-detected20- ๐ **Consistent parameters** - `target` for elements, `for` for waiting, `to` for scrolling, `device` for viewport21- ๐ Automatic retry with exponential backoff for transient failures22- ๐ Telemetry hooks for centralized observability and monitoring23- ๐พ Pluggable storage backends (local filesystem, S3, memory)24- ๐ก๏ธ Structured logging plus defensive error handling for operational visibility25- ๐ Launch via `npm start`, `npm run dev`, or as a long-lived MCP sidecar26- ๐ณ Docker image with multi-platform support (amd64, arm64)2728## How It Works291. The MCP transport boots a Node.js server and registers the `captureScreenshot` and `extractDom` tools.302. Incoming tool invocations are validated against the relevant type definitions.313. Puppeteer starts (or reuses) a Chromium instance, navigates to the requested URL, and either captures a screenshot or serializes the DOM according to the tool parameters.324. The server returns structured content (images, text, DOM trees, metadata) to the caller or downstream workflow.3334## Requirements35- Node.js โฅ 18.x36- npm โฅ 9.x37- Chromium package download permissions (first run)38- Network access to the target URLs3940## ๐ LLM-Friendly Features4142### Simplified Schema (5-Star LLM Rating)43- โ Only 4 top-level parameters: `url`, `steps`, `headers`, `validate`44- โ Exactly 6 step types (no more, no less)45- โ Consistent parameter naming across all steps46- โ Automatic screenshot if omitted47- โ Smart field type detection48- โ Actionable error messages with recovery suggestions49- โ Single source of truth for all schemas50- โ Deprecation warnings for legacy parameters51- โ **NEW**: Validate mode for pre-flight step checking52- โ **NEW**: Step order enforcement with auto-correction53- โ **NEW**: Embedded LLM reference in MCP server instructions5455### Quick Start for LLMs5657See **[LLM Quick Reference](docs/LLM_REFERENCE.md)** for the 6 primary step types and common patterns.5859For advanced features, see **[Advanced Steps](docs/ADVANCED_STEPS.md)**.6061### The 6 Step Types (Exactly 6, No More)6263| Step | Purpose | Key Parameters | Example |64|------|---------|----------------|--------|65| `viewport` | Set device | `device`, `width`, `height` | `{ "type": "viewport", "device": "mobile" }` |66| `wait` | Wait for element/time | `for` OR `duration`, `timeout` | `{ "type": "wait", "for": ".loaded" }` |67| `fill` | Fill form field | `target`, `value`, `submit` | `{ "type": "fill", "target": "#email", "value": "a@b.com" }` |68| `click` | Click element | `target`, `waitFor` | `{ "type": "click", "target": "button", "waitFor": ".result" }` |69| `scroll` | Scroll page | `to`, `y` | `{ "type": "scroll", "to": "#footer" }` |70| `screenshot` | Capture (auto-added) | `fullPage`, `element` | `{ "type": "screenshot", "fullPage": true }` |7172**Step Order**: Auto-fixed! `viewport` auto-moves to first, `screenshot` auto-added at end.7374### Composite Patterns (NEW)7576High-level patterns that auto-expand to multiple steps:7778```json79// Login pattern80{81 "type": "login",82 "email": { "selector": "#email", "value": "user@example.com" },83 "password": { "selector": "#password", "value": "secret" },84 "submit": "button[type=submit]",85 "successIndicator": ".dashboard"86}8788// Search pattern89{90 "type": "search",91 "input": "#search-box",92 "query": "MCP protocol",93 "resultsIndicator": ".search-results"94}95```9697## Installation9899### From source (recommended during development)100```powershell101git clone https://github.com/chasesaurabh/mcp-page-capture.git102npm install103```104105### From npm106```powershell107# Run without installing globally108npx mcp-page-capture109110# Or add it to your toolchain111npm install -g mcp-page-capture112mcp-page-capture113```114115## Running the server116```117npm install118npm run build119npm start120```121122For hot reload while iterating locally, run `npm run dev`.123124## Why Docker?125- Guarantees a consistent Puppeteer + Chromium environment with all system libraries when teammates or CI run the server. No more "it works on my machine" mismatches.126- Provides a ready-to-deploy container image for hosting mcp-page-capture as a sidecar/service on Kubernetes, ECS, Fly.io, etc.127128If you need those guarantees, build and run via:129```powershell130docker build -t mcp-page-capture .131docker run --rm -it mcp-page-capture132```133Otherwise you can keep using the standard npm scripts locally.134135## IDE MCP configuration136137```json138{139 "mcpServers": {140 "page-capture": {141 "command": "node",142 "args": ["dist/cli.js"]143 }144 }145}146```147148Note: You may need to use the full path to `dist/cli.js` or `node` depending on your working directory and Node.js module resolution configuration.149150## Programmatic usage151152If you want to embed the server inside another Node.js process, import the helpers exposed by the package:153154```ts155import { startMcpPageCaptureServer } from "mcp-page-capture";156157await startMcpPageCaptureServer();158// Optionally pass a custom Transport implementation if you don't want stdio.159```160161## Usage162163### Tool invocation examples164165#### Basic Screenshot166```json167{168 "tool": "captureScreenshot",169 "params": {170 "url": "https://example.com"171 }172}173```174> Note: A screenshot is automatically captured at the end if no explicit screenshot step is provided.175176#### Search with Fill Step (Recommended)177The `fill` step auto-detects field types and handles text inputs, selects, checkboxes, and radio buttons.178179```json180{181 "tool": "captureScreenshot",182 "params": {183 "url": "https://example.com",184 "steps": [185 { "type": "fill", "target": "#search", "value": "MCP protocol", "submit": true },186 { "type": "wait", "for": ".search-results" },187 { "type": "screenshot" }188 ]189 }190}191```192193#### Login Form Example194```json195{196 "tool": "captureScreenshot",197 "params": {198 "url": "https://example.com/login",199 "steps": [200 { "type": "wait", "for": "#login-form" },201 { "type": "fill", "target": "#email", "value": "user@example.com" },202 { "type": "fill", "target": "#password", "value": "secretpassword" },203 { "type": "fill", "target": "#remember-me", "value": "true" },204 { "type": "click", "target": "button[type=submit]", "waitFor": ".dashboard" },205 { "type": "screenshot" }206 ]207 }208}209```210211#### Full Page Screenshot212```json213{214 "tool": "captureScreenshot",215 "params": {216 "url": "https://docs.modelcontextprotocol.io",217 "steps": [218 { "type": "screenshot", "fullPage": true }219 ]220 }221}222```223224#### With Authentication and Cookies225```json226{227 "tool": "captureScreenshot",228 "params": {229 "url": "https://example.com/dashboard",230 "headers": {231 "authorization": "Bearer dev-token"232 },233 "steps": [234 {235 "type": "cookie",236 "action": "set",237 "name": "session",238 "value": "abc123",239 "path": "/secure"240 },241 { "type": "screenshot" }242 ]243 }244}245```246247#### Extract DOM Content248```json249{250 "tool": "extractDom",251 "params": {252 "url": "https://docs.modelcontextprotocol.io",253 "selector": "main article"254 }255}256```257258#### Mobile Device Emulation259```json260{261 "tool": "captureScreenshot",262 "params": {263 "url": "https://example.com",264 "steps": [265 { "type": "viewport", "device": "ipad-pro" },266 { "type": "scroll", "to": "#main-content" },267 { "type": "screenshot" }268 ]269 }270}271```272273### Step Types274275#### 6 Primary Steps (LLM-Exposed)276277These are the **only** steps exposed to LLMs. They cover 95%+ of use cases:278279| Step | Purpose | Parameters |280|------|---------|------------|281| `viewport` | Set device/screen size | `device`, `width`, `height` |282| `wait` | Wait for element/time | `for` OR `duration`, `timeout` |283| `fill` | Fill form field | `target`, `value`, `submit` |284| `click` | Click element | `target`, `waitFor` |285| `scroll` | Scroll page | `to` (selector), `y` (pixels) |286| `screenshot` | Capture (auto-added) | `fullPage`, `element` |287288#### Validate Mode (NEW)289290Use `validate: true` to check steps before execution:291292```json293{294 "tool": "captureScreenshot",295 "params": {296 "url": "https://example.com",297 "steps": [298 { "type": "fill", "target": "#email", "value": "test@example.com" },299 { "type": "click", "target": "button" }300 ],301 "validate": true302 }303}304```305306Returns validation analysis including:307- **Errors**: Missing required parameters308- **Warnings**: Step order issues (e.g., viewport not first)309- **Suggestions**: Recommended improvements (e.g., add `waitFor` to click)310- **Step analysis**: Per-step status and notes311312#### Deprecated Steps (Legacy Support Only)313314These work at runtime but are **not exposed in the LLM schema**. Use the 6 primary steps instead:315316| Deprecated | Use Instead |317|------------|-------------|318| `quickFill` | `fill` with `submit: true` |319| `fillForm` | Multiple `fill` steps |320| `waitForSelector` | `wait` with `for` parameter |321| `delay` | `wait` with `duration` parameter |322| `fullPage` | `screenshot` with `fullPage: true` |323324#### Internal Steps (Not Exposed)325326These are for power users only and are not documented in the tool schema:327328`type`, `hover`, `cookie`, `storage`, `evaluate`, `keypress`, `focus`, `blur`, `clear`, `upload`, `submit`329330### Legacy Parameter Support331332For backward compatibility, these parameters work at runtime but are **not exposed in the LLM schema**:333334| Legacy Parameter | Canonical | Notes |335|-----------------|-----------|-------|336| `selector` | `target` | Use `target` for element selectors |337| `awaitElement` | `for` | Use `for` in wait steps |338| `scrollTo` | `to` | Use `to` in scroll steps |339| `preset` | `device` | Use `device` in viewport steps |340| `captureElement` | `element` | Use `element` in screenshot steps |341| `waitAfter` | `wait` | Use `wait` in click steps |342343**Deprecation warnings** are logged when legacy parameters are used.344345### Example response346```json347{348 "content": [349 {350 "type": "text",351 "text": "mcp-page-capture screenshot\nURL: https://example.com\nCaptured: 2025-12-13T08:30:12.713Z\nFull page: false\nViewport: 1280x720\nDocument: 1280x2000\nScroll position: (0, 0)\nSize: 45.2 KB\nSteps executed: 5"352 },353 {354 "type": "image",355 "mimeType": "image/png",356 "data": "iVBORw0KGgoAAAANSUhEUgA..."357 }358 ]359}360361## Supported options362363### `captureScreenshot`364- `url` (string, required): Fully-qualified URL to capture365- `headers` (object, optional): Key/value map of HTTP headers to send with the initial page navigation366- `cookies` (array, optional): List of cookies to set before navigation. Each cookie supports `name`, `value`, and optional `url`, `domain`, `path`, `secure`, `httpOnly`, `sameSite`, and `expires` (Unix timestamp, seconds)367- `viewport` (object, optional): Viewport configuration368 - `preset` (string, optional): Use a predefined viewport preset (see Viewport Presets section)369 - `width` (number, optional): Custom viewport width370 - `height` (number, optional): Custom viewport height371 - `deviceScaleFactor` (number, optional): Device scale factor (e.g., 2 for Retina)372 - `isMobile` (boolean, optional): Whether to emulate mobile device373 - `hasTouch` (boolean, optional): Whether to enable touch events374 - `userAgent` (string, optional): Custom user agent string375- `retryPolicy` (object, optional): Retry configuration for transient failures376 - `maxRetries` (number, optional, default 3): Maximum number of retry attempts377 - `initialDelayMs` (number, optional, default 1000): Initial delay between retries378 - `maxDelayMs` (number, optional, default 10000): Maximum delay between retries379 - `backoffMultiplier` (number, optional, default 2): Exponential backoff multiplier380- `storageTarget` (string, optional): Storage backend name for saving captures381382### `extractDom`383- `url` (string, required): Fully-qualified URL to inspect384- `selector` (string, optional): CSS selector to scope extraction to a specific element. Defaults to the entire document385- `headers` (object, optional): Key/value map of HTTP headers sent before navigation386- `cookies` (array, optional): Same cookie structure as `captureScreenshot`, applied before navigation387- `viewport` (object, optional): Same viewport configuration as `captureScreenshot`388- `retryPolicy` (object, optional): Same retry configuration as `captureScreenshot`389- `storageTarget` (string, optional): Storage backend name for saving DOM data390391## Action Steps for captureScreenshot392393The `captureScreenshot` tool supports a comprehensive `steps` array that allows you to perform various web interactions before capturing the screenshot. Each step is executed in sequence, allowing for complex automation scenarios.394395### Fill Form (`fillForm`) - Recommended for Form Interactions396The `fillForm` step is the easiest and most LLM-friendly way to interact with forms. It auto-detects field types and handles multiple fields in a single step.397398```json399{400 "type": "fillForm",401 "fields": [402 { "selector": "#email", "value": "user@example.com" },403 { "selector": "#password", "value": "secretpassword" },404 { "selector": "#country", "value": "us" },405 { "selector": "#newsletter", "value": "true" },406 { "selector": "#plan", "value": "premium", "type": "radio" }407 ],408 "formSelector": "#signup-form",409 "submit": true,410 "submitSelector": "#submit-btn",411 "waitForNavigation": true412}413```414415#### Field Configuration416Each field in the `fields` array supports:417- `selector` (required): CSS selector for the form field418- `value` (required): Value to set. For checkboxes use `"true"` or `"false"`. For selects/radios use the value attribute.419- `type` (optional): Field type hint (`text`, `select`, `checkbox`, `radio`, `textarea`, `password`, `email`, `number`, `tel`, `url`, `date`, `file`). Auto-detected if not specified.420- `matchByText` (optional): For select fields, match by visible text instead of value attribute421- `delay` (optional): Delay between keystrokes in ms (for text inputs)422423#### Form Options424- `formSelector` (optional): CSS selector for the form container (for scoping field selectors)425- `submit` (optional): Whether to submit the form after filling (default: false)426- `submitSelector` (optional): Selector for submit button. If not specified, uses form.submit() or looks for `[type="submit"]`427- `waitForNavigation` (optional): Whether to wait for navigation after submit (default: true)428429### Text Input (`text`)430Type text into input fields:431```json432{433 "type": "text",434 "selector": "#username",435 "value": "john.doe@example.com",436 "clearFirst": true, // Clear existing text first (default: true)437 "delay": 100, // Delay between keystrokes in ms (0-1000)438 "pressEnter": false // Press Enter after typing (default: false)439}440```441442### Select Dropdown (`select`)443Select an option from a dropdown:444```json445{446 "type": "select",447 "selector": "#country",448 "value": "us" // OR "text": "United States" OR "index": 0449}450```451452### Radio Button (`radio`)453Select a radio button:454```json455{456 "type": "radio",457 "selector": "input[type='radio']",458 "value": "option1", // Value attribute of the radio button459 "name": "preference" // Name attribute to identify the radio group460}461```462463### Checkbox (`checkbox`)464Check or uncheck a checkbox:465```json466{467 "type": "checkbox",468 "selector": "#agree-terms",469 "checked": true // true to check, false to uncheck470}471```472473### Click (`click`)474Click on elements:475```json476{477 "type": "click",478 "target": "button.submit",479 "button": "left", // "left", "right", or "middle" (default: left)480 "clickCount": 1, // 1=single, 2=double, 3=triple (default: 1)481 "waitForNavigation": false, // Wait for page navigation (default: false)482 "waitForSelector": ".modal-content" // Wait for element to appear after click483}484```485486### Hover (`hover`)487Hover over elements:488```json489{490 "type": "hover",491 "selector": ".dropdown-trigger",492 "duration": 1000 // How long to maintain hover in ms (0-10000)493}494```495496### File Upload (`upload`)497Upload files:498```json499{500 "type": "upload",501 "selector": "input[type='file']",502 "filePaths": ["/path/to/file1.pdf", "/path/to/file2.jpg"]503}504```505506### Form Submit (`submit`)507Submit forms:508```json509{510 "type": "submit",511 "selector": "#contact-form", // Form element or submit button512 "waitForNavigation": true // Wait for page navigation (default: true)513}514```515516### Scroll (`scroll`)517Scroll the page:518```json519{520 "type": "scroll",521 "scrollTo": "#section-2", // Scroll to element (takes precedence)522 "x": 0, // OR horizontal scroll position in pixels523 "y": 500, // OR vertical scroll position in pixels524 "behavior": "smooth" // "auto" or "smooth" (default: auto)525}526```527528### Key Press (`keypress`)529Press keyboard keys:530```json531{532 "type": "keypress",533 "key": "Enter", // Key to press (e.g., "Enter", "Tab", "Escape", "ArrowDown")534 "modifiers": ["Control", "Shift"], // Optional modifiers535 "selector": "#search-box" // Optional element to focus first536}537```538539### Wait for Selector (`waitForSelector`) - DEPRECATED540Use `wait` step instead:541```json542{ "type": "wait", "for": ".loading-complete", "timeout": 10000 }543```544545### Delay (`delay`) - DEPRECATED546Use `wait` step with `duration` instead:547```json548{ "type": "wait", "duration": 2000 }549```550551### Focus (`focus`)552Focus an element:553```json554{555 "type": "focus",556 "selector": "#search-input"557}558```559560### Blur (`blur`)561Blur (unfocus) an element:562```json563{564 "type": "blur",565 "selector": "#search-input"566}567```568569### Clear (`clear`)570Clear input field contents:571```json572{573 "type": "clear",574 "selector": "#search-input"575}576```577578### Evaluate (`evaluate`)579Execute custom JavaScript:580```json581{582 "type": "evaluate",583 "script": "document.title = 'New Title'; return document.title;",584 "selector": "#element" // Optional element to pass to the script585}586```587588### Screenshot (`screenshot`)589Capture screenshot at any point:590```json591{592 "type": "screenshot",593 "fullPage": true, // Capture entire page (optional)594 "captureElement": ".specific-element" // Capture specific element (optional)595}596```597598### Cookie Management (`cookie`)599Set or delete browser cookies:600```json601{602 "type": "cookie",603 "action": "set",604 "name": "session_id",605 "value": "abc123",606 "domain": ".example.com",607 "path": "/",608 "secure": true609}610```611Supported actions: `set` (add/update cookie), `delete` (remove cookie).612613### Storage Management (`storage`)614Manage localStorage/sessionStorage:615```json616{617 "type": "storage",618 "storageType": "localStorage",619 "action": "set",620 "key": "user_preferences",621 "value": "{\"theme\":\"dark\"}"622}623```624Supported actions: `set` (add/update), `delete` (remove key), `clear` (remove all items).625626### Example: Complex Form Interaction with Screenshot627```json628{629 "tool": "captureScreenshot",630 "params": {631 "url": "https://example.com/signup",632 "steps": [633 { "type": "waitForSelector", "awaitElement": "#signup-form" },634 { "type": "text", "selector": "#email", "value": "user@example.com" },635 { "type": "text", "selector": "#password", "value": "SecurePass123!" },636 { "type": "select", "selector": "#country", "text": "United States" },637 { "type": "radio", "selector": "input[name='plan']", "value": "premium" },638 { "type": "checkbox", "selector": "#newsletter", "checked": true },639 { "type": "checkbox", "selector": "#terms", "checked": true },640 { "type": "hover", "selector": ".tooltip-trigger", "duration": 500 },641 { "type": "scroll", "y": 200 },642 { "type": "click", "target": "button[type='submit']", "waitForNavigation": true },643 { "type": "delay", "duration": 2000 },644 { "type": "screenshot", "fullPage": true }645 ]646 }647}648```649650## Troubleshooting651652If your capture fails, use these guidelines:653654| Error | Solution |655|-------|----------|656| "element not found" | Check CSS selector, add `waitForSelector` before `click`/`fillForm` |657| "navigation timeout" | Increase `retryPolicy.maxRetries` or add `delay` step |658| "page not loaded" | Add `waitForSelector` or `delay` before `screenshot` |659| "click failed" | Ensure element is visible, add `scroll` to bring it into view |660661## Viewport Presets662663The following viewport presets are available:664665### Desktop666- `desktop-fhd`: 1920x1080 Full HD667- `desktop-hd`: 1280x720 HD668- `desktop-4k`: 3840x2160 4K669- `macbook-pro-16`: MacBook Pro 16-inch Retina670671### Tablets672- `ipad-pro`: iPad Pro 12.9-inch673- `ipad-pro-landscape`: iPad Pro 12.9-inch (landscape)674- `ipad`: iPad 10.2-inch675- `surface-pro`: Microsoft Surface Pro676677### Mobile678- `iphone-14-pro-max`: iPhone 14 Pro Max679- `iphone-14-pro`: iPhone 14 Pro680- `iphone-se`: iPhone SE (3rd generation)681- `pixel-7-pro`: Google Pixel 7 Pro682- `galaxy-s23-ultra`: Samsung Galaxy S23 Ultra683684### Example with viewport preset:685```json686{687 "tool": "captureScreenshot",688 "params": {689 "url": "https://example.com",690 "viewport": {691 "preset": "iphone-14-pro"692 }693 }694}695```696697## Retry Policy698699The tools automatically retry on transient failures with exponential backoff. Default retryable conditions:700- HTTP status codes: 500, 502, 503, 504, 408, 429701- Network errors: ETIMEDOUT, ECONNRESET, ENOTFOUND, ECONNREFUSED702- DNS resolution failures703704### Example with custom retry policy:705```json706{707 "tool": "captureScreenshot",708 "params": {709 "url": "https://example.com",710 "retryPolicy": {711 "maxRetries": 5,712 "initialDelayMs": 2000,713 "backoffMultiplier": 1.5714 }715 }716}717```718719## Telemetry720721The server emits structured telemetry events that can be consumed for monitoring and observability:722723### Event Types724- `tool.invoked`: Tool execution started725- `tool.completed`: Tool execution succeeded726- `tool.failed`: Tool execution failed727- `navigation.started`: Page navigation initiated728- `navigation.completed`: Page navigation succeeded729- `navigation.failed`: Page navigation failed730- `retry.attempt`: Retry attempt started731- `retry.succeeded`: Retry succeeded732- `browser.launched`: Puppeteer browser started733- `browser.closed`: Puppeteer browser closed734- `screenshot.captured`: Screenshot taken735- `dom.extracted`: DOM content extracted736737### Configuring Telemetry738739You can configure telemetry hooks programmatically:740741```typescript742import { getGlobalTelemetry } from "mcp-page-capture";743744const telemetry = getGlobalTelemetry();745746// Configure HTTP sink for centralized collection747telemetry.configureHttpSink({748 url: "https://telemetry.example.com/events",749 headers: { "X-API-Key": "your-api-key" },750 batchSize: 100,751 flushIntervalMs: 5000,752});753754// Register custom hooks755telemetry.registerHook({756 name: "custom-logger",757 enabled: true,758 handler: async (event) => {759 console.log(`[${event.type}]`, event.data);760 },761});762```763764## Storage Backends765766Captures can be automatically saved to configurable storage backends:767768### Local Filesystem769```typescript770import { registerStorageTarget, LocalStorageTarget } from "mcp-page-capture";771772const localStorage = new LocalStorageTarget("/path/to/captures");773registerStorageTarget("local", localStorage);774```775776### S3-Compatible Storage777```typescript778import { registerStorageTarget, S3StorageTarget } from "mcp-page-capture";779780const s3Storage = new S3StorageTarget({781 bucket: "my-captures",782 prefix: "screenshots/",783 region: "us-west-2",784});785registerStorageTarget("s3", s3Storage);786```787788### Memory Storage789```typescript790import { registerStorageTarget, MemoryStorageTarget } from "mcp-page-capture";791792const memoryStorage = new MemoryStorageTarget();793registerStorageTarget("memory", memoryStorage);794```795796Then use the storage in tool invocations:797```json798{799 "tool": "captureScreenshot",800 "params": {801 "url": "https://example.com",802 "storageTarget": "s3"803 }804}805```806807## Known limitations808- Dynamic pages requiring complex authentication flows or user gestures are not yet automated809- Extremely long or infinite-scroll pages may exceed default Chromium memory limits810- S3 storage backend requires AWS SDK integration (placeholder implementation included)811812813## Automated Releases & Distribution814815### npm Package816- Release automation is powered by [semantic-release](https://semantic-release.gitbook.io/semantic-release/) and GitHub Actions817- Commits must follow the Conventional Commits spec (`feat:`, `fix:`, `chore:`) for automatic versioning818- Publishes to npm registry on successful builds from `main` branch819820### Docker Images821- Multi-platform images (linux/amd64, linux/arm64) are automatically built and published822- Images are pushed to:823 - Docker Hub: `<username>/mcp-page-capture`824 - GitHub Container Registry: `ghcr.io/<org>/mcp-page-capture`825- Tagged with semantic version, major, major.minor, and latest826827### Required Repository Secrets828Configure these secrets in your GitHub repository settings:829- `NPM_TOKEN`: npm access token with publish permissions830- `DOCKER_USERNAME`: Docker Hub username831- `DOCKER_PASSWORD`: Docker Hub access token832833The `GITHUB_TOKEN` is provided automatically by GitHub Actions.834835## How to contribute836Read `CONTRIBUTING.md`, open an issue describing the change, and submit a PR that includes `npm run build` output plus updated docs/tests.837838## Author / Maintainer839Maintained by **Saurabh Chase (@chasesaurabh)**. Reach out via issues or discussions for roadmap coordination.840841## License842Released under the [MIT License](LICENSE).843
Full transparency โ inspect the skill content before installing.