snapforge-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.
Files changed (3) hide show
  1. package/README.md +47 -0
  2. package/index.js +91 -0
  3. package/package.json +18 -0
package/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # snapforge-mcp
2
+
3
+ MCP server for [SnapForge](https://snapforge.org) — gives AI agents two tools:
4
+
5
+ - **`snapforge_screenshot`** — capture a URL or HTML as PNG/JPEG (returned inline as an image)
6
+ - **`snapforge_pdf`** — render a URL or HTML to a PDF (saved to a temp file, path returned)
7
+
8
+ ## Setup
9
+
10
+ Get a free API key at <https://snapforge.org> (POST your email to `/signup`), then:
11
+
12
+ ### Claude Desktop / Claude Code
13
+
14
+ Add to your MCP config (`claude_desktop_config.json` or `.mcp.json`):
15
+
16
+ ```json
17
+ {
18
+ "mcpServers": {
19
+ "snapforge": {
20
+ "command": "npx",
21
+ "args": ["-y", "snapforge-mcp"],
22
+ "env": { "SNAPFORGE_API_KEY": "sf_your_key_here" }
23
+ }
24
+ }
25
+ }
26
+ ```
27
+
28
+ ### Any MCP client
29
+
30
+ ```bash
31
+ SNAPFORGE_API_KEY=sf_your_key npx -y snapforge-mcp
32
+ ```
33
+
34
+ ## Environment
35
+
36
+ - `SNAPFORGE_API_KEY` (required) — your SnapForge API key
37
+ - `SNAPFORGE_BASE_URL` (optional) — defaults to `https://snapforge.org`
38
+
39
+ ## Tool parameters
40
+
41
+ Both tools accept `url` **or** `html`, plus `waitUntil` (`load`|`networkidle`) and `delay` (ms).
42
+ - `snapforge_screenshot`: `fullPage`, `width`, `height`, `scale`, `format` (`png`|`jpeg`), `quality`
43
+ - `snapforge_pdf`: `pageFormat` (`A4`, `Letter`, …), `landscape`, `printBackground`, `margin`
44
+
45
+ ## License
46
+
47
+ MIT
package/index.js ADDED
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { z } from 'zod';
5
+ import { writeFile } from 'node:fs/promises';
6
+ import { tmpdir } from 'node:os';
7
+ import path from 'node:path';
8
+
9
+ const BASE = (process.env.SNAPFORGE_BASE_URL || 'https://snapforge.org').replace(/\/$/, '');
10
+ const API_KEY = process.env.SNAPFORGE_API_KEY || '';
11
+
12
+ // Call a SnapForge render endpoint and return the raw bytes + content type.
13
+ async function render(endpoint, body) {
14
+ if (!API_KEY) {
15
+ throw new Error('Set the SNAPFORGE_API_KEY environment variable (get one at https://snapforge.org).');
16
+ }
17
+ const res = await fetch(`${BASE}${endpoint}`, {
18
+ method: 'POST',
19
+ headers: { 'x-api-key': API_KEY, 'content-type': 'application/json' },
20
+ body: JSON.stringify(body)
21
+ });
22
+ if (!res.ok) {
23
+ let detail = '';
24
+ try { detail = JSON.stringify(await res.json()); } catch { detail = await res.text().catch(() => ''); }
25
+ throw new Error(`SnapForge ${endpoint} failed (HTTP ${res.status}): ${detail}`);
26
+ }
27
+ const buf = Buffer.from(await res.arrayBuffer());
28
+ return { buf, contentType: res.headers.get('content-type') || 'application/octet-stream' };
29
+ }
30
+
31
+ const server = new McpServer({ name: 'snapforge', version: '1.0.0' });
32
+
33
+ const common = {
34
+ url: z.string().url().optional().describe('Public http/https URL to render'),
35
+ html: z.string().optional().describe('Raw HTML to render instead of a URL (max 2MB)'),
36
+ waitUntil: z.enum(['load', 'networkidle']).optional().describe('When to consider the page ready'),
37
+ delay: z.number().int().min(0).max(10000).optional().describe('Extra wait in ms before capture')
38
+ };
39
+
40
+ server.registerTool(
41
+ 'snapforge_screenshot',
42
+ {
43
+ title: 'SnapForge screenshot',
44
+ description: 'Capture a screenshot of a public URL or raw HTML and return it as an image (PNG/JPEG).',
45
+ inputSchema: {
46
+ ...common,
47
+ fullPage: z.boolean().optional().describe('Capture the full scrollable page'),
48
+ width: z.number().int().min(100).max(3840).optional(),
49
+ height: z.number().int().min(100).max(2160).optional(),
50
+ scale: z.number().int().min(1).max(3).optional().describe('Device pixel ratio (high-DPI)'),
51
+ format: z.enum(['png', 'jpeg']).optional(),
52
+ quality: z.number().int().min(1).max(100).optional().describe('JPEG quality')
53
+ }
54
+ },
55
+ async (args) => {
56
+ try {
57
+ const { buf, contentType } = await render('/v1/screenshot', args);
58
+ return { content: [{ type: 'image', data: buf.toString('base64'), mimeType: contentType }] };
59
+ } catch (e) {
60
+ return { isError: true, content: [{ type: 'text', text: String(e.message || e) }] };
61
+ }
62
+ }
63
+ );
64
+
65
+ server.registerTool(
66
+ 'snapforge_pdf',
67
+ {
68
+ title: 'SnapForge PDF',
69
+ description: 'Render a public URL or raw HTML to a PDF. The PDF is written to a temp file and its path is returned.',
70
+ inputSchema: {
71
+ ...common,
72
+ pageFormat: z.enum(['Letter', 'Legal', 'Tabloid', 'A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6']).optional(),
73
+ landscape: z.boolean().optional(),
74
+ printBackground: z.boolean().optional(),
75
+ margin: z.string().optional().describe('CSS size for all margins, e.g. "1cm"')
76
+ }
77
+ },
78
+ async (args) => {
79
+ try {
80
+ const { buf } = await render('/v1/pdf', args);
81
+ const file = path.join(tmpdir(), `snapforge-${Date.now()}.pdf`);
82
+ await writeFile(file, buf);
83
+ return { content: [{ type: 'text', text: `PDF saved to ${file} (${buf.length} bytes).` }] };
84
+ } catch (e) {
85
+ return { isError: true, content: [{ type: 'text', text: String(e.message || e) }] };
86
+ }
87
+ }
88
+ );
89
+
90
+ const transport = new StdioServerTransport();
91
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "snapforge-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for SnapForge — screenshot & HTML-to-PDF API as agent tools",
5
+ "type": "module",
6
+ "bin": { "snapforge-mcp": "index.js" },
7
+ "files": ["index.js", "README.md"],
8
+ "keywords": ["mcp", "model-context-protocol", "screenshot", "pdf", "snapforge", "playwright"],
9
+ "homepage": "https://snapforge.org",
10
+ "repository": { "type": "git", "url": "git+https://github.com/sporty303/snapforge-mcp.git" },
11
+ "author": "Gerome Sportelli",
12
+ "license": "MIT",
13
+ "engines": { "node": ">=18" },
14
+ "dependencies": {
15
+ "@modelcontextprotocol/sdk": "^1.0.0",
16
+ "zod": "^3.23.8"
17
+ }
18
+ }