heroshot 0.18.0 → 0.19.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.
package/dist/cli/cli.js CHANGED
@@ -770,8 +770,9 @@ function getVersion() {
770
770
  }
771
771
  const version = getVersion();
772
772
  const program = new Command();
773
- program.name("heroshot").description("Define your screenshots once, update them forever with one command").version(version).option("-v, --verbose", "Show detailed output").option("-c, --config <path>", "Path to config file").option("-s, --session-key <key>", "Session key for encrypted auth (or set HEROSHOT_SESSION_KEY)").hook("preAction", () => {
773
+ program.name("heroshot").description("Define your screenshots once, update them forever with one command").version(version).option("-v, --verbose", "Show detailed output").option("-c, --config <path>", "Path to config file").option("-s, --session-key <key>", "Session key for encrypted auth (or set HEROSHOT_SESSION_KEY)").hook("preAction", (_thisCommand, actionCommand) => {
774
774
  setVerbose(program.opts().verbose ?? false);
775
+ if (actionCommand.name() === "mcp") return;
775
776
  intro(version);
776
777
  });
777
778
  program.command("oneshot [url]", { isDefault: true }).description("Capture URL directly, or sync all screenshots from config").option("--selector <selector...>", "CSS selector(s) to capture").option("-o, --output <file>", "Output filename").option("-p, --padding <pixels>", "Padding around element", Number.parseInt).option("-w, --width <pixels>", "Viewport width", Number.parseInt).option("--height <pixels>", "Viewport height", Number.parseInt).option("--mobile", "Use mobile viewport (430x932)").option("--tablet", "Use tablet viewport (768x1024)").option("--desktop", "Use desktop viewport (1280x800)").option("--dark", "Force dark color scheme").option("--light", "Force light color scheme").option("--scale <factor>", "Device scale factor (1, 2, 3)", Number.parseInt).option("--retina", "Use retina scale (2x)").option("-q, --quality <percent>", "JPEG quality (1-100), outputs JPEG", Number.parseInt).option("--viewport-only", "Capture only viewport (not full page)").option("--reduced-motion", "Emulate prefers-reduced-motion: reduce (disables animations)").option("--user-agent <string>", "Custom user agent string").option("--ignore-https-errors", "Ignore TLS certificate errors (self-signed certs, dev only)").option("--save", "Save screenshot definition to config").option("--clean", "Delete stale files in output directory").option("--workers <count>", "Number of parallel capture workers", Number.parseInt).option("--headed", "Run browser in headed mode (visible window) for debugging").action(async (url, options) => {
@@ -787,6 +788,10 @@ program.command("snippet [pattern]").description("Generate markdown/HTML snippet
787
788
  const globalOptions = program.opts();
788
789
  if (!snippetAction(pattern, options ?? {}, globalOptions.config)) process.exitCode = 1;
789
790
  });
791
+ program.command("mcp").description("Start the MCP (Model Context Protocol) server over stdio").action(async () => {
792
+ const { startMcpServer } = await import("../server-CqRfIFJa.js");
793
+ await startMcpServer();
794
+ });
790
795
  program.command("list").description("List all configured screenshots").option("--json", "Output as JSON").action((options) => {
791
796
  if (!listAction(options, program.opts())) process.exitCode = 1;
792
797
  });
package/dist/mcp/index.js CHANGED
@@ -1,317 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { _ as loadConfig, b as screenshotSchema, g as getConfigPath, i as filterScreenshots, r as sync, t as generateSnippets, v as saveConfig, x as generateUid } from "../snippet-ZcZmuryj.js";
3
- import { z } from "zod";
4
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
- //#region src/mcp/schemas/add.ts
7
- /**
8
- * Add tool input schema.
9
- * Wraps the existing screenshotSchema from heroshot.
10
- */
11
- const screenshotInputSchema = screenshotSchema.omit({ id: true });
12
- const addOptionsSchema = z.object({
13
- configPath: z.string().optional().describe("Path to config file (default: .heroshot/config.json in cwd)"),
14
- screenshot: screenshotInputSchema.describe("Screenshot definition to add")
15
- });
16
- //#endregion
17
- //#region src/mcp/schemas/list.ts
18
- /**
19
- * List tool input schema.
20
- */
21
- const listOptionsSchema = z.object({
22
- configPath: z.string().optional().describe("Path to config file (default: .heroshot/config.json in cwd)"),
23
- filter: z.string().optional().describe("Filter screenshots by id, name, or filename (case-insensitive substring match)")
24
- });
25
- //#endregion
26
- //#region src/mcp/schemas/remove.ts
27
- /**
28
- * Remove tool input schema.
29
- */
30
- const removeOptionsSchema = z.object({
31
- configPath: z.string().optional().describe("Path to config file (default: .heroshot/config.json in cwd)"),
32
- id: z.string().min(1).describe("ID of the screenshot to remove")
33
- });
34
- //#endregion
35
- //#region src/mcp/schemas/snippet.ts
36
- /**
37
- * Snippet tool input schema.
38
- */
39
- const snippetOptionsSchema = z.object({
40
- configPath: z.string().optional().describe("Path to config file (default: .heroshot/config.json in cwd)"),
41
- filter: z.string().optional().describe("Filter screenshots by id, name, or filename (case-insensitive substring match)"),
42
- pathPrefix: z.string().optional().describe("Custom path prefix for images (default: ./heroshots/)")
43
- });
44
- //#endregion
45
- //#region src/mcp/schemas/sync.ts
46
- /**
47
- * Sync tool input schema.
48
- */
49
- const syncOptionsSchema = z.object({
50
- configPath: z.string().optional().describe("Path to config file (default: .heroshot/config.json in cwd)"),
51
- filter: z.string().optional().describe("Filter screenshots by id, name, or filename (case-insensitive substring match)"),
52
- clean: z.boolean().optional().describe("Delete stale files in output directory"),
53
- workers: z.number().int().min(1).optional().describe("Number of parallel capture workers"),
54
- sessionKey: z.string().optional().describe("Encrypted session key for authenticated screenshots")
55
- });
56
- //#endregion
57
- //#region src/mcp/tools/add.ts
58
- /**
59
- * Add tool handler.
60
- * Adds a new screenshot definition to config.
61
- */
62
- function addHandler(input) {
63
- try {
64
- const configPath = input.configPath ?? getConfigPath();
65
- const config = loadConfig(configPath);
66
- const screenshotInput = {
67
- ...input.screenshot,
68
- id: generateUid()
69
- };
70
- const screenshot = screenshotSchema.parse(screenshotInput);
71
- const existingByName = config.screenshots.find((s) => s.name.toLowerCase() === screenshot.name.toLowerCase());
72
- if (existingByName) return {
73
- success: false,
74
- error: `Screenshot with name "${screenshot.name}" already exists (id: ${existingByName.id})`
75
- };
76
- config.screenshots.push(screenshot);
77
- saveConfig(configPath, config);
78
- return {
79
- success: true,
80
- id: screenshot.id,
81
- screenshot: {
82
- id: screenshot.id,
83
- name: screenshot.name,
84
- url: screenshot.url
85
- }
86
- };
87
- } catch (error) {
88
- return {
89
- success: false,
90
- error: error instanceof Error ? error.message : String(error)
91
- };
92
- }
93
- }
94
- //#endregion
95
- //#region src/mcp/tools/list.ts
96
- /**
97
- * List tool handler.
98
- * Lists screenshots defined in config.
99
- */
100
- function listHandler(input) {
101
- try {
102
- const screenshots = filterScreenshots(loadConfig(input.configPath ?? getConfigPath()).screenshots, input.filter);
103
- return {
104
- success: true,
105
- count: screenshots.length,
106
- screenshots: screenshots.map((s) => ({
107
- id: s.id,
108
- name: s.name,
109
- url: s.url,
110
- selector: s.selector
111
- }))
112
- };
113
- } catch {
114
- return {
115
- success: false,
116
- count: 0,
117
- screenshots: [],
118
- error: "Failed to load config"
119
- };
120
- }
121
- }
122
- //#endregion
123
- //#region src/mcp/tools/remove.ts
124
- /**
125
- * Remove tool handler.
126
- * Removes a screenshot definition from config.
127
- */
128
- function removeHandler(input) {
129
- try {
130
- const configPath = input.configPath ?? getConfigPath();
131
- const config = loadConfig(configPath);
132
- const index = config.screenshots.findIndex((s) => s.id === input.id);
133
- if (index === -1) return {
134
- success: false,
135
- error: `Screenshot with id "${input.id}" not found`
136
- };
137
- const [removed] = config.screenshots.splice(index, 1);
138
- if (!removed) return {
139
- success: false,
140
- error: `Screenshot with id "${input.id}" not found`
141
- };
142
- saveConfig(configPath, config);
143
- return {
144
- success: true,
145
- removed: {
146
- id: removed.id,
147
- name: removed.name
148
- }
149
- };
150
- } catch (error) {
151
- return {
152
- success: false,
153
- error: error instanceof Error ? error.message : String(error)
154
- };
155
- }
156
- }
157
- //#endregion
158
- //#region src/mcp/tools/snippet.ts
159
- /**
160
- * Snippet tool handler.
161
- * Generates markdown/HTML snippets for screenshots.
162
- */
163
- function snippetHandler(input) {
164
- try {
165
- const results = generateSnippets(loadConfig(input.configPath ?? getConfigPath()), input.filter, { pathPrefix: input.pathPrefix });
166
- return {
167
- success: true,
168
- count: results.length,
169
- snippets: results.map((r) => ({
170
- id: r.screenshot.id,
171
- name: r.screenshot.name,
172
- snippet: r.snippet
173
- }))
174
- };
175
- } catch (error) {
176
- return {
177
- success: false,
178
- count: 0,
179
- snippets: [],
180
- error: error instanceof Error ? error.message : String(error)
181
- };
182
- }
183
- }
184
- //#endregion
185
- //#region src/mcp/tools/sync.ts
186
- /**
187
- * Sync tool handler.
188
- * Captures screenshots defined in config.
189
- */
190
- function resolveSessionKey(input) {
191
- return input.sessionKey ?? process.env["HEROSHOT_SESSION_KEY"];
192
- }
193
- async function syncHandler(input) {
194
- try {
195
- const configPath = input.configPath ?? getConfigPath();
196
- const sessionKey = resolveSessionKey(input);
197
- const result = await sync({
198
- configPath,
199
- filter: input.filter,
200
- clean: input.clean,
201
- workers: input.workers,
202
- sessionKey
203
- });
204
- return {
205
- success: result.failed === 0,
206
- total: result.total,
207
- captured: result.success,
208
- failed: result.failed,
209
- results: result.results,
210
- staleFiles: result.staleFiles,
211
- deletedFiles: result.deletedFiles
212
- };
213
- } catch (error) {
214
- return {
215
- success: false,
216
- total: 0,
217
- captured: 0,
218
- failed: 0,
219
- results: [],
220
- error: error instanceof Error ? error.message : String(error)
221
- };
222
- }
223
- }
224
- //#endregion
225
- //#region src/mcp/tools/definitions.ts
226
- function formatResult(result) {
227
- return {
228
- content: [{
229
- type: "text",
230
- text: JSON.stringify(result, null, 2)
231
- }],
232
- isError: !result.success
233
- };
234
- }
235
- async function syncToolHandler(input) {
236
- return formatResult(await syncHandler(input));
237
- }
238
- async function addToolHandler(input) {
239
- return formatResult(addHandler(input));
240
- }
241
- async function listToolHandler(input) {
242
- return formatResult(listHandler(input));
243
- }
244
- async function snippetToolHandler(input) {
245
- return formatResult(snippetHandler(input));
246
- }
247
- async function removeToolHandler(input) {
248
- return formatResult(removeHandler(input));
249
- }
250
- const tools = [
251
- {
252
- name: "heroshot_sync",
253
- description: "Capture all screenshots defined in the heroshot config. Optionally filter by id/name/filename pattern, delete stale files, or run with multiple workers.",
254
- inputSchema: syncOptionsSchema,
255
- handler: syncToolHandler
256
- },
257
- {
258
- name: "heroshot_add",
259
- description: "Add a new screenshot definition to the heroshot config. Requires at least name and url. Optional: selector for element capture, actions for pre-capture steps.",
260
- inputSchema: addOptionsSchema,
261
- handler: addToolHandler
262
- },
263
- {
264
- name: "heroshot_list",
265
- description: "List all screenshots defined in the heroshot config. Returns id, name, url, and selector for each screenshot.",
266
- inputSchema: listOptionsSchema,
267
- handler: listToolHandler
268
- },
269
- {
270
- name: "heroshot_snippet",
271
- description: "Generate markdown/HTML snippets for embedding screenshots in documentation. Supports light/dark mode with <picture> elements.",
272
- inputSchema: snippetOptionsSchema,
273
- handler: snippetToolHandler
274
- },
275
- {
276
- name: "heroshot_remove",
277
- description: "Remove a screenshot definition from the heroshot config by its ID.",
278
- inputSchema: removeOptionsSchema,
279
- handler: removeToolHandler
280
- }
281
- ];
282
- //#endregion
283
- //#region src/mcp/utils/zodToMcp.ts
284
- /**
285
- * Utilities for converting Zod schemas to MCP tool definitions.
286
- */
287
- /**
288
- * Convert a ToolDefinition to MCP tool format.
289
- * Uses z.toJSONSchema() for automatic schema conversion.
290
- */
291
- function toMcpTool(definition) {
292
- return {
293
- name: definition.name,
294
- description: definition.description,
295
- inputSchema: z.toJSONSchema(definition.inputSchema)
296
- };
297
- }
298
- //#endregion
2
+ import { t as startMcpServer } from "../server-BklhdmjN.js";
299
3
  //#region src/mcp/index.ts
300
4
  /**
301
- * Heroshot MCP Server
302
- *
303
- * MCP server for screenshot automation. Tools are auto-derived from Zod schemas
304
- * using z.toJSONSchema() for low maintenance.
5
+ * Entry point for the `heroshot-mcp` bin.
6
+ * Also reachable via the `heroshot mcp` subcommand (see src/cli/cli.ts).
305
7
  */
306
- const server = new McpServer({
307
- name: "heroshot",
308
- version: "0.1.0"
309
- });
310
- for (const tool of tools) {
311
- const mcpTool = toMcpTool(tool);
312
- server.registerTool(tool.name, mcpTool.inputSchema, async (input) => tool.handler(input));
313
- }
314
- const transport = new StdioServerTransport();
315
- await server.connect(transport);
8
+ await startMcpServer();
316
9
  //#endregion
317
10
  export {};
@@ -0,0 +1,319 @@
1
+ #!/usr/bin/env node
2
+ import { _ as loadConfig, b as screenshotSchema, g as getConfigPath, i as filterScreenshots, r as sync, t as generateSnippets, v as saveConfig, x as generateUid } from "./snippet-ZcZmuryj.js";
3
+ import { z } from "zod";
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ //#region src/mcp/schemas/add.ts
7
+ /**
8
+ * Add tool input schema.
9
+ * Wraps the existing screenshotSchema from heroshot.
10
+ */
11
+ const screenshotInputSchema = screenshotSchema.omit({ id: true });
12
+ const addOptionsSchema = z.object({
13
+ configPath: z.string().optional().describe("Path to config file (default: .heroshot/config.json in cwd)"),
14
+ screenshot: screenshotInputSchema.describe("Screenshot definition to add")
15
+ });
16
+ //#endregion
17
+ //#region src/mcp/schemas/list.ts
18
+ /**
19
+ * List tool input schema.
20
+ */
21
+ const listOptionsSchema = z.object({
22
+ configPath: z.string().optional().describe("Path to config file (default: .heroshot/config.json in cwd)"),
23
+ filter: z.string().optional().describe("Filter screenshots by id, name, or filename (case-insensitive substring match)")
24
+ });
25
+ //#endregion
26
+ //#region src/mcp/schemas/remove.ts
27
+ /**
28
+ * Remove tool input schema.
29
+ */
30
+ const removeOptionsSchema = z.object({
31
+ configPath: z.string().optional().describe("Path to config file (default: .heroshot/config.json in cwd)"),
32
+ id: z.string().min(1).describe("ID of the screenshot to remove")
33
+ });
34
+ //#endregion
35
+ //#region src/mcp/schemas/snippet.ts
36
+ /**
37
+ * Snippet tool input schema.
38
+ */
39
+ const snippetOptionsSchema = z.object({
40
+ configPath: z.string().optional().describe("Path to config file (default: .heroshot/config.json in cwd)"),
41
+ filter: z.string().optional().describe("Filter screenshots by id, name, or filename (case-insensitive substring match)"),
42
+ pathPrefix: z.string().optional().describe("Custom path prefix for images (default: ./heroshots/)")
43
+ });
44
+ //#endregion
45
+ //#region src/mcp/schemas/sync.ts
46
+ /**
47
+ * Sync tool input schema.
48
+ */
49
+ const syncOptionsSchema = z.object({
50
+ configPath: z.string().optional().describe("Path to config file (default: .heroshot/config.json in cwd)"),
51
+ filter: z.string().optional().describe("Filter screenshots by id, name, or filename (case-insensitive substring match)"),
52
+ clean: z.boolean().optional().describe("Delete stale files in output directory"),
53
+ workers: z.number().int().min(1).optional().describe("Number of parallel capture workers"),
54
+ sessionKey: z.string().optional().describe("Encrypted session key for authenticated screenshots")
55
+ });
56
+ //#endregion
57
+ //#region src/mcp/tools/add.ts
58
+ /**
59
+ * Add tool handler.
60
+ * Adds a new screenshot definition to config.
61
+ */
62
+ function addHandler(input) {
63
+ try {
64
+ const configPath = input.configPath ?? getConfigPath();
65
+ const config = loadConfig(configPath);
66
+ const screenshotInput = {
67
+ ...input.screenshot,
68
+ id: generateUid()
69
+ };
70
+ const screenshot = screenshotSchema.parse(screenshotInput);
71
+ const existingByName = config.screenshots.find((s) => s.name.toLowerCase() === screenshot.name.toLowerCase());
72
+ if (existingByName) return {
73
+ success: false,
74
+ error: `Screenshot with name "${screenshot.name}" already exists (id: ${existingByName.id})`
75
+ };
76
+ config.screenshots.push(screenshot);
77
+ saveConfig(configPath, config);
78
+ return {
79
+ success: true,
80
+ id: screenshot.id,
81
+ screenshot: {
82
+ id: screenshot.id,
83
+ name: screenshot.name,
84
+ url: screenshot.url
85
+ }
86
+ };
87
+ } catch (error) {
88
+ return {
89
+ success: false,
90
+ error: error instanceof Error ? error.message : String(error)
91
+ };
92
+ }
93
+ }
94
+ //#endregion
95
+ //#region src/mcp/tools/list.ts
96
+ /**
97
+ * List tool handler.
98
+ * Lists screenshots defined in config.
99
+ */
100
+ function listHandler(input) {
101
+ try {
102
+ const screenshots = filterScreenshots(loadConfig(input.configPath ?? getConfigPath()).screenshots, input.filter);
103
+ return {
104
+ success: true,
105
+ count: screenshots.length,
106
+ screenshots: screenshots.map((s) => ({
107
+ id: s.id,
108
+ name: s.name,
109
+ url: s.url,
110
+ selector: s.selector
111
+ }))
112
+ };
113
+ } catch {
114
+ return {
115
+ success: false,
116
+ count: 0,
117
+ screenshots: [],
118
+ error: "Failed to load config"
119
+ };
120
+ }
121
+ }
122
+ //#endregion
123
+ //#region src/mcp/tools/remove.ts
124
+ /**
125
+ * Remove tool handler.
126
+ * Removes a screenshot definition from config.
127
+ */
128
+ function removeHandler(input) {
129
+ try {
130
+ const configPath = input.configPath ?? getConfigPath();
131
+ const config = loadConfig(configPath);
132
+ const index = config.screenshots.findIndex((s) => s.id === input.id);
133
+ if (index === -1) return {
134
+ success: false,
135
+ error: `Screenshot with id "${input.id}" not found`
136
+ };
137
+ const [removed] = config.screenshots.splice(index, 1);
138
+ if (!removed) return {
139
+ success: false,
140
+ error: `Screenshot with id "${input.id}" not found`
141
+ };
142
+ saveConfig(configPath, config);
143
+ return {
144
+ success: true,
145
+ removed: {
146
+ id: removed.id,
147
+ name: removed.name
148
+ }
149
+ };
150
+ } catch (error) {
151
+ return {
152
+ success: false,
153
+ error: error instanceof Error ? error.message : String(error)
154
+ };
155
+ }
156
+ }
157
+ //#endregion
158
+ //#region src/mcp/tools/snippet.ts
159
+ /**
160
+ * Snippet tool handler.
161
+ * Generates markdown/HTML snippets for screenshots.
162
+ */
163
+ function snippetHandler(input) {
164
+ try {
165
+ const results = generateSnippets(loadConfig(input.configPath ?? getConfigPath()), input.filter, { pathPrefix: input.pathPrefix });
166
+ return {
167
+ success: true,
168
+ count: results.length,
169
+ snippets: results.map((r) => ({
170
+ id: r.screenshot.id,
171
+ name: r.screenshot.name,
172
+ snippet: r.snippet
173
+ }))
174
+ };
175
+ } catch (error) {
176
+ return {
177
+ success: false,
178
+ count: 0,
179
+ snippets: [],
180
+ error: error instanceof Error ? error.message : String(error)
181
+ };
182
+ }
183
+ }
184
+ //#endregion
185
+ //#region src/mcp/tools/sync.ts
186
+ /**
187
+ * Sync tool handler.
188
+ * Captures screenshots defined in config.
189
+ */
190
+ function resolveSessionKey(input) {
191
+ return input.sessionKey ?? process.env["HEROSHOT_SESSION_KEY"];
192
+ }
193
+ async function syncHandler(input) {
194
+ try {
195
+ const configPath = input.configPath ?? getConfigPath();
196
+ const sessionKey = resolveSessionKey(input);
197
+ const result = await sync({
198
+ configPath,
199
+ filter: input.filter,
200
+ clean: input.clean,
201
+ workers: input.workers,
202
+ sessionKey
203
+ });
204
+ return {
205
+ success: result.failed === 0,
206
+ total: result.total,
207
+ captured: result.success,
208
+ failed: result.failed,
209
+ results: result.results,
210
+ staleFiles: result.staleFiles,
211
+ deletedFiles: result.deletedFiles
212
+ };
213
+ } catch (error) {
214
+ return {
215
+ success: false,
216
+ total: 0,
217
+ captured: 0,
218
+ failed: 0,
219
+ results: [],
220
+ error: error instanceof Error ? error.message : String(error)
221
+ };
222
+ }
223
+ }
224
+ //#endregion
225
+ //#region src/mcp/tools/definitions.ts
226
+ function formatResult(result) {
227
+ return {
228
+ content: [{
229
+ type: "text",
230
+ text: JSON.stringify(result, null, 2)
231
+ }],
232
+ isError: !result.success
233
+ };
234
+ }
235
+ async function syncToolHandler(input) {
236
+ return formatResult(await syncHandler(input));
237
+ }
238
+ async function addToolHandler(input) {
239
+ return formatResult(addHandler(input));
240
+ }
241
+ async function listToolHandler(input) {
242
+ return formatResult(listHandler(input));
243
+ }
244
+ async function snippetToolHandler(input) {
245
+ return formatResult(snippetHandler(input));
246
+ }
247
+ async function removeToolHandler(input) {
248
+ return formatResult(removeHandler(input));
249
+ }
250
+ const tools = [
251
+ {
252
+ name: "heroshot_sync",
253
+ description: "Capture all screenshots defined in the heroshot config. Optionally filter by id/name/filename pattern, delete stale files, or run with multiple workers.",
254
+ inputSchema: syncOptionsSchema,
255
+ handler: syncToolHandler
256
+ },
257
+ {
258
+ name: "heroshot_add",
259
+ description: "Add a new screenshot definition to the heroshot config. Requires at least name and url. Optional: selector for element capture, actions for pre-capture steps.",
260
+ inputSchema: addOptionsSchema,
261
+ handler: addToolHandler
262
+ },
263
+ {
264
+ name: "heroshot_list",
265
+ description: "List all screenshots defined in the heroshot config. Returns id, name, url, and selector for each screenshot.",
266
+ inputSchema: listOptionsSchema,
267
+ handler: listToolHandler
268
+ },
269
+ {
270
+ name: "heroshot_snippet",
271
+ description: "Generate markdown/HTML snippets for embedding screenshots in documentation. Supports light/dark mode with <picture> elements.",
272
+ inputSchema: snippetOptionsSchema,
273
+ handler: snippetToolHandler
274
+ },
275
+ {
276
+ name: "heroshot_remove",
277
+ description: "Remove a screenshot definition from the heroshot config by its ID.",
278
+ inputSchema: removeOptionsSchema,
279
+ handler: removeToolHandler
280
+ }
281
+ ];
282
+ //#endregion
283
+ //#region src/mcp/utils/zodToMcp.ts
284
+ /**
285
+ * Utilities for converting Zod schemas to MCP tool definitions.
286
+ */
287
+ /**
288
+ * Convert a ToolDefinition to MCP tool format.
289
+ * Uses z.toJSONSchema() for automatic schema conversion.
290
+ */
291
+ function toMcpTool(definition) {
292
+ return {
293
+ name: definition.name,
294
+ description: definition.description,
295
+ inputSchema: z.toJSONSchema(definition.inputSchema)
296
+ };
297
+ }
298
+ //#endregion
299
+ //#region src/mcp/server.ts
300
+ /**
301
+ * Heroshot MCP Server
302
+ *
303
+ * MCP server for screenshot automation. Tools are auto-derived from Zod schemas
304
+ * using z.toJSONSchema() for low maintenance.
305
+ */
306
+ async function startMcpServer() {
307
+ const server = new McpServer({
308
+ name: "heroshot",
309
+ version: "0.1.0"
310
+ });
311
+ for (const tool of tools) {
312
+ const mcpTool = toMcpTool(tool);
313
+ server.registerTool(tool.name, mcpTool.inputSchema, async (input) => tool.handler(input));
314
+ }
315
+ const transport = new StdioServerTransport();
316
+ await server.connect(transport);
317
+ }
318
+ //#endregion
319
+ export { startMcpServer as t };
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { t as startMcpServer } from "./server-BklhdmjN.js";
3
+ export { startMcpServer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "heroshot",
3
- "version": "0.18.0",
3
+ "version": "0.19.1",
4
4
  "description": "Define your screenshots once, update them forever with one command",
5
5
  "type": "module",
6
6
  "author": "Ondrej Machala",