screenshotone mcp server
Connects AI assistants to ScreenshotOne.com API for capturing website screenshots with customizable options including viewport size, full-page captures, and multiple output formats.
Connects AI assistants to ScreenshotOne.com API for capturing website screenshots with customizable options including viewport size, full-page captures, and multiple output formats.
This project provides a Model Context Protocol (MCP) server that connects AI assistants to ScreenshotOne.com API to capture screenshots of websites.
Bearer <token>)# Take a screenshot of a URL
npm run dev:cli -- take-screenshot --url "https://example.com" --access-key "your-access-key"
# Take a screenshot with custom viewport
npm run dev:cli -- take-screenshot --url "https://example.com" --viewport-width 1920 --viewport-height 1080
# Capture a full page screenshot
npm run dev:cli -- take-screenshot --url "https://example.com" --full-page
# Save the screenshot to a file
npm run dev:cli -- take-screenshot --url "https://example.com" --output screenshot.png
# Block ads and trackers
npm run dev:cli -- take-screenshot --url "https://example.com" --block-ads --block-trackers --block-cookie-banners
# ----------------------------------------------
# UPLOAD SCREENSHOT TO CLOUDFLARE
# REMEMBER TO SET THE ENVIRONMENT VARIABLES
# > See example at ".env.example" file
# ----------------------------------------------
# Take a screenshot and upload it to Cloudflare
npm run dev:cli -- take-screenshot --url https://example.com --upload
# Take a screenshot with a custom filename
npm run dev:cli -- take-screenshot --url https://example.com --upload --upload-filename my-screenshot
# Take a screenshot with upload debugging enabled
npm run dev:cli -- take-screenshot --url https://example.com --upload --upload-debug
For local configuration with stdio transport:
{
"mcpServers": {
"screenshotone": {
"command": "node",
"args": ["/path/to/screenshotone-mcp-server/dist/index.js"],
"transportType": "stdio"
}
}
}
For remote HTTP configuration:
{
"mcpServers": {
"screenshotone": {
"type": "http",
"url": "http://localhost:8080/mcp"
}
}
}
Environment Variables for HTTP Transport:
You can configure the HTTP server using these environment variables:
MCP_HTTP_HOST: The host to bind to (default: 127.0.0.1)MCP_HTTP_PORT: The port to listen on (default: 8080)MCP_HTTP_PATH: The endpoint path (default: /mcp)Model Context Protocol (MCP) is an open standard that allows AI systems to securely and contextually connect with external tools and data sources.
This boilerplate implements the MCP specification with a clean, layered architecture that can be extended to build custom MCP servers for any API or data source.
Production-Ready Architecture: Follows the same pattern used in published MCP servers, with clear separation between CLI, tools, controllers, and services.
Type Safety: Built with TypeScript for improved developer experience, code quality, and maintainability.
Working Example: Includes a fully implemented IP lookup tool demonstrating the complete pattern from CLI to API integration.
Testing Framework: Comes with testing infrastructure for both unit and CLI integration tests, including coverage reporting.
Development Tooling: Includes ESLint, Prettier, TypeScript, and other quality tools preconfigured for MCP server development.
# Clone the repository
git clone https://github.com/mrgoonie/screenshotone-mcp-server.git
cd screenshotone-mcp-server
# Install dependencies
npm install
Start the server in development mode with stdio transport (default):
npm run dev:server
Or with the Streamable HTTP transport:
npm run dev:server:http
This starts the MCP server with hot-reloading and enables the MCP Inspector at http://localhost:5173.
⚙️ Proxy server listening on port 6277 ? MCP Inspector is up and running at http://127.0.0.1:6274
When using HTTP transport, the server will be available at http://127.0.0.1:8080/mcp by default.
Take a screenshot using the CLI:
# Basic screenshot
npm run dev:cli -- take-screenshot --url "https://example.com" --access-key "your-access-key"
# Advanced options
npm run dev:cli -- take-screenshot --url "https://example.com" --format png --viewport-width 1920 --viewport-height 1080 --full-page --output screenshot.png
This boilerplate follows a clean, layered architecture pattern that separates concerns and promotes maintainability.
src/
├── cli/ # Command-line interfaces
├── controllers/ # Business logic
├── resources/ # MCP resources: expose data and content from your servers to LLMs
├── services/ # External API interactions
├── tools/ # MCP tool definitions
├── types/ # Type definitions
├── utils/ # Shared utilities
└── index.ts # Entry point
src/cli/*.cli.ts)<feature>.cli.ts<feature>.cli.test.tssrc/tools/*.tool.ts)<feature>.tool.ts with types in <feature>.types.tssrc/controllers/*.controller.ts)<feature>.controller.tsControllerResponse objectssrc/services/*.service.ts)<feature>.service.tssrc/utils/*.util.ts)logger.util.ts: Structured loggingerror.util.ts: Error handling and standardizationformatter.util.ts: Markdown formatting helpers# Start server in development mode (hot-reload & inspector)
npm run dev:server
# Run CLI in development mode
npm run dev:cli -- [command] [args]
# Build the project
npm run build
# Start server in production mode
npm run start:server
# Run CLI in production mode
npm run start:cli -- [command] [args]
# Run all tests
npm test
# Run specific tests
npm test -- src/path/to/test.ts
# Generate test coverage report
npm run test:coverage
# Lint code
npm run lint
# Format code with Prettier
npm run format
# Check types
npm run typecheck
Follow these steps to add your own tools to the server:
Create a new service in src/services/ to interact with your external API:
// src/services/example.service.ts
import { Logger } from ../utils/logger.util.js ;
const logger = Logger.forContext( services/example.service.ts );
export async function getData(param: string): Promise<any> {
logger.debug( Getting data , { param });
// API interaction code here
return { result: example data };
}
Add a controller in src/controllers/ to handle business logic:
// src/controllers/example.controller.ts
import { Logger } from ../utils/logger.util.js ;
import * as exampleService from ../services/example.service.js ;
import { formatMarkdown } from ../utils/formatter.util.js ;
import { handleControllerError } from ../utils/error-handler.util.js ;
import { ControllerResponse } from ../types/common.types.js ;
const logger = Logger.forContext( controllers/example.controller.ts );
export interface GetDataOptions {
param?: string;
}
export async function getData(
options: GetDataOptions = {},
): Promise<ControllerResponse> {
try {
logger.debug( Getting data with options , options);
const data = await exampleService.getData(options.param || default );
const content = formatMarkdown(data);
return { content };
} catch (error) {
throw handleControllerError(error, {
entityType: ExampleData ,
operation: getData ,
source: controllers/example.controller.ts ,
});
}
}
Create a tool definition in src/tools/:
// src/tools/example.tool.ts
import { McpServer } from @modelcontextprotocol/sdk/server/mcp.js ;
import { z } from zod ;
import { Logger } from ../utils/logger.util.js ;
import { formatErrorForMcpTool } from ../utils/error.util.js ;
import * as exampleController from ../controllers/example.controller.js ;
const logger = Logger.forContext( tools/example.tool.ts );
const GetDataArgs = z.object({
param: z.string().optional().describe( Optional parameter ),
});
type GetDataArgsType = z.infer<typeof GetDataArgs>;
async function handleGetData(args: GetDataArgsType) {
try {
logger.debug( Tool get_data called , args);
const result = await exampleController.getData({
param: args.param,
});
return {
content: [{ type: text as const, text: result.content }],
};
} catch (error) {
logger.error( Tool get_data failed , error);
return formatErrorForMcpTool(error);
}
}
export function register(server: McpServer) {
server.tool(
get_data ,
`Gets data from the example API, optionally using `param`.
Use this to fetch example data. Returns formatted data as Markdown.`,
GetDataArgs.shape,
handleGetData,
);
}
Create a CLI command in src/cli/:
// src/cli/example.cli.ts
import { program } from commander ;
import { Logger } from ../utils/logger.util.js ;
import * as exampleController from ../controllers/example.controller.js ;
import { handleCliError } from ../utils/error-handler.util.js ;
const logger = Logger.forContext( cli/example.cli.ts );
program
.command( get-data )
.description( Get example data )
.option( --param <value> , Optional parameter )
.action(async (options) => {
try {
logger.debug( CLI get-data called , options);
const result = await exampleController.getData({
param: options.param,
});
console.log(result.content);
} catch (error) {
handleCliError(error);
}
});
Update the entry points to register your new components:
// In src/cli/index.ts
import ../cli/example.cli.js ;
// In src/index.ts (for the tool)
import exampleTool from ./tools/example.tool.js ;
// Then in registerTools function:
exampleTool.register(server);
Access the visual MCP Inspector to test your tools and view request/response details:
npm run dev:serverEnable debug logs for development:
# Set environment variable
DEBUG=true npm run dev:server
# Or configure in ~/.mcp/configs.json
When ready to publish your custom MCP server:
npm run buildnpm run start:servernpm publish{
"screenshotone": {
"environments": {
"DEBUG": "true",
"SCREENSHOTONE_API_KEY": "value"
}
}
}
Note: For backward compatibility, the server will also recognize configurations under the full package name (screenshotone-mcp-server) or the unscoped package name (screenshotone-mcp-server) if the screenshotone key is not found. However, using the short screenshotone key is recommended for new configurations.