@rendshot/mcp 0.1.0 → 0.2.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.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +5 -5
- package/dist/server.js +5 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/generateImage.d.ts +10 -1
- package/dist/tools/generateImage.d.ts.map +1 -1
- package/dist/tools/generateImage.js +16 -2
- package/dist/tools/generateImage.js.map +1 -1
- package/dist/tools/getTemplate.d.ts +16 -0
- package/dist/tools/getTemplate.d.ts.map +1 -0
- package/dist/tools/getTemplate.js +28 -0
- package/dist/tools/getTemplate.js.map +1 -0
- package/dist/tools/listTemplates.d.ts +20 -0
- package/dist/tools/listTemplates.d.ts.map +1 -0
- package/dist/tools/listTemplates.js +30 -0
- package/dist/tools/listTemplates.js.map +1 -0
- package/package.json +2 -2
- package/src/__tests__/tools.test.ts +186 -7
- package/src/server.ts +25 -1
- package/src/tools/generateImage.ts +17 -2
- package/src/tools/getTemplate.ts +37 -0
- package/src/tools/listTemplates.ts +40 -0
package/.turbo/turbo-build.log
CHANGED
package/.turbo/turbo-test.log
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
|
|
2
|
-
> @rendshot/mcp@0.
|
|
2
|
+
> @rendshot/mcp@0.2.0 test /Users/wuzhiqiang/Documents/WWW/MySelf/rendshot/packages/mcp
|
|
3
3
|
> vitest run --passWithNoTests
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
RUN v3.2.4 /Users/wuzhiqiang/Documents/WWW/MySelf/rendshot/packages/mcp
|
|
7
7
|
|
|
8
|
-
✓ src/__tests__/tools.test.ts (
|
|
8
|
+
✓ src/__tests__/tools.test.ts (35 tests) 7ms
|
|
9
9
|
|
|
10
10
|
Test Files 1 passed (1)
|
|
11
|
-
Tests
|
|
12
|
-
Start at
|
|
13
|
-
Duration
|
|
11
|
+
Tests 35 passed (35)
|
|
12
|
+
Start at 15:23:35
|
|
13
|
+
Duration 432ms (transform 60ms, setup 0ms, collect 80ms, tests 7ms, environment 0ms, prepare 65ms)
|
|
14
14
|
|
package/dist/server.js
CHANGED
|
@@ -4,6 +4,8 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
4
4
|
import { RendshotClient } from '@rendshot/sdk';
|
|
5
5
|
import { generateImageSchema, generateImageToolMeta, handleGenerateImage, } from './tools/generateImage.js';
|
|
6
6
|
import { screenshotUrlSchema, screenshotUrlToolMeta, handleScreenshotUrl, } from './tools/screenshotUrl.js';
|
|
7
|
+
import { listTemplatesSchema, listTemplatesToolMeta, handleListTemplates, } from './tools/listTemplates.js';
|
|
8
|
+
import { getTemplateSchema, getTemplateToolMeta, handleGetTemplate, } from './tools/getTemplate.js';
|
|
7
9
|
const apiKey = process.env.RENDSHOT_API_KEY;
|
|
8
10
|
const apiUrl = process.env.RENDSHOT_API_URL || 'https://api.rendshot.ai';
|
|
9
11
|
if (!apiKey) {
|
|
@@ -13,10 +15,12 @@ if (!apiKey) {
|
|
|
13
15
|
const client = new RendshotClient({ apiKey, baseUrl: apiUrl });
|
|
14
16
|
const server = new McpServer({
|
|
15
17
|
name: 'rendshot',
|
|
16
|
-
version: '0.
|
|
18
|
+
version: '0.2.0',
|
|
17
19
|
});
|
|
18
20
|
server.tool(generateImageToolMeta.name, generateImageToolMeta.description, generateImageSchema, async (args) => handleGenerateImage(client, args));
|
|
19
21
|
server.tool(screenshotUrlToolMeta.name, screenshotUrlToolMeta.description, screenshotUrlSchema, async (args) => handleScreenshotUrl(client, args));
|
|
22
|
+
server.tool(listTemplatesToolMeta.name, listTemplatesToolMeta.description, listTemplatesSchema, async (args) => handleListTemplates(client, args));
|
|
23
|
+
server.tool(getTemplateToolMeta.name, getTemplateToolMeta.description, getTemplateSchema, async (args) => handleGetTemplate(client, args));
|
|
20
24
|
const transport = new StdioServerTransport();
|
|
21
25
|
await server.connect(transport);
|
|
22
26
|
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,wBAAwB,CAAA;AAE/B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAA;AAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,yBAAyB,CAAA;AAExE,IAAI,CAAC,MAAM,EAAE,CAAC;IACZ,OAAO,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAA;IAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC;AAED,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAA;AAE9D,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAA;AAEF,MAAM,CAAC,IAAI,CACT,qBAAqB,CAAC,IAAI,EAC1B,qBAAqB,CAAC,WAAW,EACjC,mBAAmB,EACnB,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,CAClD,CAAA;AAED,MAAM,CAAC,IAAI,CACT,qBAAqB,CAAC,IAAI,EAC1B,qBAAqB,CAAC,WAAW,EACjC,mBAAmB,EACnB,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,CAClD,CAAA;AAED,MAAM,CAAC,IAAI,CACT,qBAAqB,CAAC,IAAI,EAC1B,qBAAqB,CAAC,WAAW,EACjC,mBAAmB,EACnB,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,CAClD,CAAA;AAED,MAAM,CAAC,IAAI,CACT,mBAAmB,CAAC,IAAI,EACxB,mBAAmB,CAAC,WAAW,EAC/B,iBAAiB,EACjB,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,CAChD,CAAA;AAED,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAA;AAC5C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import type { RendshotClient } from '@rendshot/sdk';
|
|
3
3
|
export declare const generateImageSchema: {
|
|
4
|
-
html: z.ZodString
|
|
4
|
+
html: z.ZodOptional<z.ZodString>;
|
|
5
|
+
template_id: z.ZodOptional<z.ZodString>;
|
|
6
|
+
variables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
5
7
|
css: z.ZodOptional<z.ZodString>;
|
|
6
8
|
width: z.ZodOptional<z.ZodNumber>;
|
|
7
9
|
height: z.ZodOptional<z.ZodNumber>;
|
|
@@ -20,5 +22,12 @@ export declare function handleGenerateImage(client: RendshotClient, args: z.obje
|
|
|
20
22
|
type: "text";
|
|
21
23
|
text: string;
|
|
22
24
|
}[];
|
|
25
|
+
isError: boolean;
|
|
26
|
+
} | {
|
|
27
|
+
content: {
|
|
28
|
+
type: "text";
|
|
29
|
+
text: string;
|
|
30
|
+
}[];
|
|
31
|
+
isError?: undefined;
|
|
23
32
|
}>;
|
|
24
33
|
//# sourceMappingURL=generateImage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateImage.d.ts","sourceRoot":"","sources":["../../src/tools/generateImage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,eAAe,CAAA;AAEhE,eAAO,MAAM,mBAAmB
|
|
1
|
+
{"version":3,"file":"generateImage.d.ts","sourceRoot":"","sources":["../../src/tools/generateImage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,eAAe,CAAA;AAEhE,eAAO,MAAM,mBAAmB;;;;;;;;;;;;CAY/B,CAAA;AAED,eAAO,MAAM,qBAAqB;;;CAGjC,CAAA;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,CAAC,CAAC,gBAAgB,CAAC,OAAO,mBAAmB,EAAE,CAAC,CAAC,UAAU,CAAC;;;;;;;;;;;;GAwBnE"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export const generateImageSchema = {
|
|
3
|
-
html: z.string().describe('HTML content to render'),
|
|
3
|
+
html: z.string().optional().describe('HTML content to render (provide this OR template_id, not both)'),
|
|
4
|
+
template_id: z.string().optional().describe('Template ID (e.g. tpl_abc123) — use list_templates to find available templates'),
|
|
5
|
+
variables: z.record(z.string(), z.unknown()).optional().describe('Template variables as key-value pairs — use get_template to see available variables'),
|
|
4
6
|
css: z.string().optional().describe('Optional CSS styles'),
|
|
5
7
|
width: z.number().optional().describe('Image width in pixels (default 1080)'),
|
|
6
8
|
height: z.number().optional().describe('Image height in pixels (default 1080)'),
|
|
@@ -12,9 +14,21 @@ export const generateImageSchema = {
|
|
|
12
14
|
};
|
|
13
15
|
export const generateImageToolMeta = {
|
|
14
16
|
name: 'generate_image',
|
|
15
|
-
description: 'Render HTML/CSS to an image and return the URL',
|
|
17
|
+
description: 'Render HTML/CSS or a template to an image and return the URL. Provide either html OR template_id (not both). Use list_templates and get_template to discover available templates and their variables.',
|
|
16
18
|
};
|
|
17
19
|
export async function handleGenerateImage(client, args) {
|
|
20
|
+
if (!args.html && !args.template_id) {
|
|
21
|
+
return {
|
|
22
|
+
content: [{ type: 'text', text: 'Error: Provide either html or template_id' }],
|
|
23
|
+
isError: true,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (args.html && args.template_id) {
|
|
27
|
+
return {
|
|
28
|
+
content: [{ type: 'text', text: 'Error: Provide either html or template_id, not both' }],
|
|
29
|
+
isError: true,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
18
32
|
const result = await client.renderImage(args);
|
|
19
33
|
return {
|
|
20
34
|
content: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateImage.js","sourceRoot":"","sources":["../../src/tools/generateImage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"generateImage.js","sourceRoot":"","sources":["../../src/tools/generateImage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gEAAgE,CAAC;IACtG,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gFAAgF,CAAC;IAC7H,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qFAAqF,CAAC;IACvJ,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;IAC1D,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;IAC7E,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;IAC/E,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,6BAA6B,CAAC;IACjF,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oCAAoC,CAAC;IAC7E,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;IACvH,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iDAAiD,CAAC;IACjG,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;CAChF,CAAA;AAED,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,IAAI,EAAE,gBAAgB;IACtB,WAAW,EAAE,uMAAuM;CACrN,CAAA;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAsB,EACtB,IAAkE;IAElE,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACpC,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,2CAA2C,EAAE,CAAC;YACvF,OAAO,EAAE,IAAI;SACd,CAAA;IACH,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QAClC,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,qDAAqD,EAAE,CAAC;YACjG,OAAO,EAAE,IAAI;SACd,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAAgB,MAAM,MAAM,CAAC,WAAW,CAAC,IAAW,CAAC,CAAA;IACjE,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,uCAAuC,MAAM,CAAC,GAAG,WAAW,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,gBAAgB,MAAM,CAAC,IAAI,QAAQ;aACpJ;SACF;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { RendshotClient } from '@rendshot/sdk';
|
|
3
|
+
export declare const getTemplateSchema: {
|
|
4
|
+
template_id: z.ZodString;
|
|
5
|
+
};
|
|
6
|
+
export declare const getTemplateToolMeta: {
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function handleGetTemplate(client: RendshotClient, args: z.objectOutputType<typeof getTemplateSchema, z.ZodTypeAny>): Promise<{
|
|
11
|
+
content: {
|
|
12
|
+
type: "text";
|
|
13
|
+
text: string;
|
|
14
|
+
}[];
|
|
15
|
+
}>;
|
|
16
|
+
//# sourceMappingURL=getTemplate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getTemplate.d.ts","sourceRoot":"","sources":["../../src/tools/getTemplate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEnD,eAAO,MAAM,iBAAiB;;CAE7B,CAAA;AAED,eAAO,MAAM,mBAAmB;;;CAG/B,CAAA;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,CAAC,CAAC,gBAAgB,CAAC,OAAO,iBAAiB,EAAE,CAAC,CAAC,UAAU,CAAC;;;;;GAsBjE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const getTemplateSchema = {
|
|
3
|
+
template_id: z.string().describe('Template ID (e.g. tpl_abc123)'),
|
|
4
|
+
};
|
|
5
|
+
export const getTemplateToolMeta = {
|
|
6
|
+
name: 'get_template',
|
|
7
|
+
description: 'Get detailed information about a template, including its variable definitions. Use this before generate_image to know which variables to pass.',
|
|
8
|
+
};
|
|
9
|
+
export async function handleGetTemplate(client, args) {
|
|
10
|
+
const t = await client.getTemplate(args.template_id);
|
|
11
|
+
const varLines = t.variables.map((v) => {
|
|
12
|
+
const req = v.required ? ' (required)' : '';
|
|
13
|
+
const opts = v.options ? ` options: [${v.options.join(', ')}]` : '';
|
|
14
|
+
return ` - ${v.key} (${v.type})${req}: default="${v.default}"${opts}`;
|
|
15
|
+
});
|
|
16
|
+
const text = [
|
|
17
|
+
`Template: ${t.name} (${t.id})`,
|
|
18
|
+
t.description ? `Description: ${t.description}` : null,
|
|
19
|
+
`Platform: ${t.platform}`,
|
|
20
|
+
`Size: ${t.width}x${t.height}`,
|
|
21
|
+
t.tags.length > 0 ? `Tags: ${t.tags.join(', ')}` : null,
|
|
22
|
+
`Author: ${t.author.name ?? 'Unknown'}`,
|
|
23
|
+
'',
|
|
24
|
+
varLines.length > 0 ? `Variables (${varLines.length}):\n${varLines.join('\n')}` : 'No variables.',
|
|
25
|
+
].filter(Boolean).join('\n');
|
|
26
|
+
return { content: [{ type: 'text', text }] };
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=getTemplate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getTemplate.js","sourceRoot":"","sources":["../../src/tools/getTemplate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,+BAA+B,CAAC;CAClE,CAAA;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,IAAI,EAAE,cAAc;IACpB,WAAW,EAAE,gJAAgJ;CAC9J,CAAA;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAsB,EACtB,IAAgE;IAEhE,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAEpD,MAAM,QAAQ,GAAG,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACrC,MAAM,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAA;QAC3C,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;QACnE,OAAO,OAAO,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,IAAI,GAAG,cAAc,CAAC,CAAC,OAAO,IAAI,IAAI,EAAE,CAAA;IACxE,CAAC,CAAC,CAAA;IAEF,MAAM,IAAI,GAAG;QACX,aAAa,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,EAAE,GAAG;QAC/B,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI;QACtD,aAAa,CAAC,CAAC,QAAQ,EAAE;QACzB,SAAS,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,EAAE;QAC9B,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI;QACvD,WAAW,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,SAAS,EAAE;QACvC,EAAE;QACF,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,QAAQ,CAAC,MAAM,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe;KAClG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAE5B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,CAAA;AACvD,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { RendshotClient } from '@rendshot/sdk';
|
|
3
|
+
export declare const listTemplatesSchema: {
|
|
4
|
+
platform: z.ZodOptional<z.ZodString>;
|
|
5
|
+
category: z.ZodOptional<z.ZodString>;
|
|
6
|
+
q: z.ZodOptional<z.ZodString>;
|
|
7
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
8
|
+
cursor: z.ZodOptional<z.ZodString>;
|
|
9
|
+
};
|
|
10
|
+
export declare const listTemplatesToolMeta: {
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function handleListTemplates(client: RendshotClient, args: z.objectOutputType<typeof listTemplatesSchema, z.ZodTypeAny>): Promise<{
|
|
15
|
+
content: {
|
|
16
|
+
type: "text";
|
|
17
|
+
text: string;
|
|
18
|
+
}[];
|
|
19
|
+
}>;
|
|
20
|
+
//# sourceMappingURL=listTemplates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listTemplates.d.ts","sourceRoot":"","sources":["../../src/tools/listTemplates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEnD,eAAO,MAAM,mBAAmB;;;;;;CAM/B,CAAA;AAED,eAAO,MAAM,qBAAqB;;;CAGjC,CAAA;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,CAAC,CAAC,gBAAgB,CAAC,OAAO,mBAAmB,EAAE,CAAC,CAAC,UAAU,CAAC;;;;;GAqBnE"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const listTemplatesSchema = {
|
|
3
|
+
platform: z.string().optional().describe('Filter by platform (e.g. twitter, instagram, custom)'),
|
|
4
|
+
category: z.string().optional().describe('Filter by category'),
|
|
5
|
+
q: z.string().optional().describe('Search by name or description'),
|
|
6
|
+
limit: z.number().optional().describe('Max results 1-50 (default 20)'),
|
|
7
|
+
cursor: z.string().optional().describe('Pagination cursor from previous response'),
|
|
8
|
+
};
|
|
9
|
+
export const listTemplatesToolMeta = {
|
|
10
|
+
name: 'list_templates',
|
|
11
|
+
description: 'Browse available image templates. Returns template IDs, names, dimensions, and variable counts. Use get_template for full variable details.',
|
|
12
|
+
};
|
|
13
|
+
export async function handleListTemplates(client, args) {
|
|
14
|
+
const result = await client.listTemplates(args);
|
|
15
|
+
if (result.templates.length === 0) {
|
|
16
|
+
return {
|
|
17
|
+
content: [{ type: 'text', text: 'No templates found matching the criteria.' }],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const lines = result.templates.map((t) => {
|
|
21
|
+
const vars = t.variables.map((v) => `${v.key}(${v.type})`).join(', ');
|
|
22
|
+
return `- ${t.id}: ${t.name} (${t.width}x${t.height}) — variables: ${vars || 'none'}`;
|
|
23
|
+
});
|
|
24
|
+
let text = `Found ${result.templates.length} template(s):\n\n${lines.join('\n')}`;
|
|
25
|
+
if (result.nextCursor) {
|
|
26
|
+
text += `\n\nMore results available — pass cursor: "${result.nextCursor}"`;
|
|
27
|
+
}
|
|
28
|
+
return { content: [{ type: 'text', text }] };
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=listTemplates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listTemplates.js","sourceRoot":"","sources":["../../src/tools/listTemplates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sDAAsD,CAAC;IAChG,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oBAAoB,CAAC;IAC9D,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+BAA+B,CAAC;IAClE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+BAA+B,CAAC;IACtE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0CAA0C,CAAC;CACnF,CAAA;AAED,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,IAAI,EAAE,gBAAgB;IACtB,WAAW,EAAE,6IAA6I;CAC3J,CAAA;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAsB,EACtB,IAAkE;IAElE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAA;IAE/C,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,2CAA2C,EAAE,CAAC;SACxF,CAAA;IACH,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACvC,MAAM,IAAI,GAAG,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACrE,OAAO,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,kBAAkB,IAAI,IAAI,MAAM,EAAE,CAAA;IACvF,CAAC,CAAC,CAAA;IAEF,IAAI,IAAI,GAAG,SAAS,MAAM,CAAC,SAAS,CAAC,MAAM,oBAAoB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAA;IACjF,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,IAAI,IAAI,8CAA8C,MAAM,CAAC,UAAU,GAAG,CAAA;IAC5E,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,CAAA;AACvD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rendshot/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"rendshot-mcp": "./dist/server.js"
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@modelcontextprotocol/sdk": "^1",
|
|
10
10
|
"zod": "^3",
|
|
11
|
-
"@rendshot/sdk": "0.
|
|
11
|
+
"@rendshot/sdk": "0.2.0"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"tsx": "^4",
|
|
@@ -10,6 +10,16 @@ import {
|
|
|
10
10
|
screenshotUrlToolMeta,
|
|
11
11
|
handleScreenshotUrl,
|
|
12
12
|
} from '../tools/screenshotUrl.js'
|
|
13
|
+
import {
|
|
14
|
+
listTemplatesSchema,
|
|
15
|
+
listTemplatesToolMeta,
|
|
16
|
+
handleListTemplates,
|
|
17
|
+
} from '../tools/listTemplates.js'
|
|
18
|
+
import {
|
|
19
|
+
getTemplateSchema,
|
|
20
|
+
getTemplateToolMeta,
|
|
21
|
+
handleGetTemplate,
|
|
22
|
+
} from '../tools/getTemplate.js'
|
|
13
23
|
import { RendshotClient } from '@rendshot/sdk'
|
|
14
24
|
|
|
15
25
|
// ---------------------------------------------------------------------------
|
|
@@ -81,9 +91,9 @@ describe('generateImageSchema validation', () => {
|
|
|
81
91
|
expect(result.success).toBe(true)
|
|
82
92
|
})
|
|
83
93
|
|
|
84
|
-
it('
|
|
94
|
+
it('accepts input without html (html is now optional for template mode)', () => {
|
|
85
95
|
const result = schema.safeParse({ width: 800 })
|
|
86
|
-
expect(result.success).toBe(
|
|
96
|
+
expect(result.success).toBe(true)
|
|
87
97
|
})
|
|
88
98
|
|
|
89
99
|
it('rejects invalid format values', () => {
|
|
@@ -193,12 +203,11 @@ describe('handleGenerateImage', () => {
|
|
|
193
203
|
expect((response.content[0] as any).text).toContain('Image generated successfully')
|
|
194
204
|
})
|
|
195
205
|
|
|
196
|
-
it('
|
|
197
|
-
const mockFetch = makeFetchMock(400, { error: { message: 'HTML is required' } })
|
|
198
|
-
vi.stubGlobal('fetch', mockFetch)
|
|
199
|
-
|
|
206
|
+
it('returns error for empty html (treated as missing)', async () => {
|
|
200
207
|
const client = new RendshotClient({ apiKey: 'rs_test_key', baseUrl: 'https://api.rendshot.com' })
|
|
201
|
-
await
|
|
208
|
+
const result = await handleGenerateImage(client, { html: '' } as any)
|
|
209
|
+
expect((result as any).isError).toBe(true)
|
|
210
|
+
expect(result.content[0].text).toContain('Provide either html or template_id')
|
|
202
211
|
})
|
|
203
212
|
})
|
|
204
213
|
|
|
@@ -246,3 +255,173 @@ describe('handleScreenshotUrl', () => {
|
|
|
246
255
|
).rejects.toThrow('Invalid URL')
|
|
247
256
|
})
|
|
248
257
|
})
|
|
258
|
+
|
|
259
|
+
// ---------------------------------------------------------------------------
|
|
260
|
+
// generate_image with template_id
|
|
261
|
+
// ---------------------------------------------------------------------------
|
|
262
|
+
|
|
263
|
+
describe('generateImageSchema validation (template mode)', () => {
|
|
264
|
+
const schema = z.object(generateImageSchema)
|
|
265
|
+
|
|
266
|
+
it('accepts template_id without html', () => {
|
|
267
|
+
const result = schema.safeParse({ template_id: 'tpl_abc123' })
|
|
268
|
+
expect(result.success).toBe(true)
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
it('accepts template_id with variables', () => {
|
|
272
|
+
const result = schema.safeParse({
|
|
273
|
+
template_id: 'tpl_abc123',
|
|
274
|
+
variables: { title: 'Hello', count: 42 },
|
|
275
|
+
})
|
|
276
|
+
expect(result.success).toBe(true)
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it('accepts html without template_id (backward compat)', () => {
|
|
280
|
+
const result = schema.safeParse({ html: '<h1>Hello</h1>' })
|
|
281
|
+
expect(result.success).toBe(true)
|
|
282
|
+
})
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
describe('handleGenerateImage with template_id', () => {
|
|
286
|
+
it('returns success for template_id render', async () => {
|
|
287
|
+
const f = makeFetchMock(200, {
|
|
288
|
+
imageId: 'img_t1',
|
|
289
|
+
url: 'https://cdn/tpl.png',
|
|
290
|
+
width: 1200,
|
|
291
|
+
height: 630,
|
|
292
|
+
format: 'png',
|
|
293
|
+
size: 4096,
|
|
294
|
+
createdAt: '2026-03-29',
|
|
295
|
+
})
|
|
296
|
+
vi.stubGlobal('fetch', f)
|
|
297
|
+
|
|
298
|
+
const client = new RendshotClient({ apiKey: 'rs_test_key', baseUrl: 'https://api.test.com' })
|
|
299
|
+
const result = await handleGenerateImage(client, {
|
|
300
|
+
template_id: 'tpl_abc123',
|
|
301
|
+
variables: { title: 'Test' },
|
|
302
|
+
} as any)
|
|
303
|
+
|
|
304
|
+
expect(result.content[0].text).toContain('Image generated successfully')
|
|
305
|
+
expect(result.content[0].text).toContain('https://cdn/tpl.png')
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
it('returns error when neither html nor template_id provided', async () => {
|
|
309
|
+
const client = new RendshotClient({ apiKey: 'rs_test_key', baseUrl: 'https://api.test.com' })
|
|
310
|
+
const result = await handleGenerateImage(client, {} as any)
|
|
311
|
+
expect((result as any).isError).toBe(true)
|
|
312
|
+
expect(result.content[0].text).toContain('Provide either html or template_id')
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
it('returns error when both html and template_id provided', async () => {
|
|
316
|
+
const client = new RendshotClient({ apiKey: 'rs_test_key', baseUrl: 'https://api.test.com' })
|
|
317
|
+
const result = await handleGenerateImage(client, {
|
|
318
|
+
html: '<h1>x</h1>',
|
|
319
|
+
template_id: 'tpl_x',
|
|
320
|
+
} as any)
|
|
321
|
+
expect((result as any).isError).toBe(true)
|
|
322
|
+
})
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
// ---------------------------------------------------------------------------
|
|
326
|
+
// list_templates
|
|
327
|
+
// ---------------------------------------------------------------------------
|
|
328
|
+
|
|
329
|
+
describe('list_templates tool', () => {
|
|
330
|
+
it('has the correct name and description', () => {
|
|
331
|
+
expect(listTemplatesToolMeta.name).toBe('list_templates')
|
|
332
|
+
expect(listTemplatesToolMeta.description.length).toBeGreaterThan(0)
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
it('schema accepts empty object', () => {
|
|
336
|
+
const schema = z.object(listTemplatesSchema)
|
|
337
|
+
expect(schema.safeParse({}).success).toBe(true)
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
it('schema accepts all filter params', () => {
|
|
341
|
+
const schema = z.object(listTemplatesSchema)
|
|
342
|
+
expect(schema.safeParse({
|
|
343
|
+
platform: 'twitter',
|
|
344
|
+
category: 'social',
|
|
345
|
+
q: 'card',
|
|
346
|
+
limit: 10,
|
|
347
|
+
}).success).toBe(true)
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
it('returns formatted template list', async () => {
|
|
351
|
+
const f = makeFetchMock(200, {
|
|
352
|
+
templates: [
|
|
353
|
+
{
|
|
354
|
+
id: 'tpl_abc',
|
|
355
|
+
name: 'Social Card',
|
|
356
|
+
width: 1200,
|
|
357
|
+
height: 630,
|
|
358
|
+
variables: [{ key: 'title', type: 'text', label: 'Title', default: 'Hi' }],
|
|
359
|
+
author: { name: 'Jane' },
|
|
360
|
+
},
|
|
361
|
+
],
|
|
362
|
+
nextCursor: null,
|
|
363
|
+
})
|
|
364
|
+
vi.stubGlobal('fetch', f)
|
|
365
|
+
|
|
366
|
+
const client = new RendshotClient({ apiKey: 'rs_test_key', baseUrl: 'https://api.test.com' })
|
|
367
|
+
const result = await handleListTemplates(client, {})
|
|
368
|
+
|
|
369
|
+
expect(result.content[0].text).toContain('tpl_abc')
|
|
370
|
+
expect(result.content[0].text).toContain('Social Card')
|
|
371
|
+
expect(result.content[0].text).toContain('title(text)')
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
it('returns "no templates" message for empty results', async () => {
|
|
375
|
+
const f = makeFetchMock(200, { templates: [], nextCursor: null })
|
|
376
|
+
vi.stubGlobal('fetch', f)
|
|
377
|
+
|
|
378
|
+
const client = new RendshotClient({ apiKey: 'rs_test_key', baseUrl: 'https://api.test.com' })
|
|
379
|
+
const result = await handleListTemplates(client, {})
|
|
380
|
+
|
|
381
|
+
expect(result.content[0].text).toContain('No templates found')
|
|
382
|
+
})
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
// ---------------------------------------------------------------------------
|
|
386
|
+
// get_template
|
|
387
|
+
// ---------------------------------------------------------------------------
|
|
388
|
+
|
|
389
|
+
describe('get_template tool', () => {
|
|
390
|
+
it('has the correct name', () => {
|
|
391
|
+
expect(getTemplateToolMeta.name).toBe('get_template')
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
it('schema requires template_id', () => {
|
|
395
|
+
const schema = z.object(getTemplateSchema)
|
|
396
|
+
expect(schema.safeParse({}).success).toBe(false)
|
|
397
|
+
expect(schema.safeParse({ template_id: 'tpl_abc' }).success).toBe(true)
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
it('returns formatted template details', async () => {
|
|
401
|
+
const f = makeFetchMock(200, {
|
|
402
|
+
id: 'tpl_abc',
|
|
403
|
+
name: 'Social Card',
|
|
404
|
+
description: 'A card template',
|
|
405
|
+
platform: 'twitter',
|
|
406
|
+
width: 1200,
|
|
407
|
+
height: 630,
|
|
408
|
+
tags: ['social'],
|
|
409
|
+
variables: [
|
|
410
|
+
{ key: 'title', type: 'text', label: 'Title', default: 'Hello', required: true },
|
|
411
|
+
{ key: 'bgColor', type: 'color', label: 'BG', default: '#fff' },
|
|
412
|
+
],
|
|
413
|
+
author: { name: 'Jane' },
|
|
414
|
+
})
|
|
415
|
+
vi.stubGlobal('fetch', f)
|
|
416
|
+
|
|
417
|
+
const client = new RendshotClient({ apiKey: 'rs_test_key', baseUrl: 'https://api.test.com' })
|
|
418
|
+
const result = await handleGetTemplate(client, { template_id: 'tpl_abc' })
|
|
419
|
+
|
|
420
|
+
const text = result.content[0].text
|
|
421
|
+
expect(text).toContain('Social Card')
|
|
422
|
+
expect(text).toContain('title (text)')
|
|
423
|
+
expect(text).toContain('(required)')
|
|
424
|
+
expect(text).toContain('bgColor (color)')
|
|
425
|
+
expect(text).toContain('default="Hello"')
|
|
426
|
+
})
|
|
427
|
+
})
|
package/src/server.ts
CHANGED
|
@@ -12,6 +12,16 @@ import {
|
|
|
12
12
|
screenshotUrlToolMeta,
|
|
13
13
|
handleScreenshotUrl,
|
|
14
14
|
} from './tools/screenshotUrl.js'
|
|
15
|
+
import {
|
|
16
|
+
listTemplatesSchema,
|
|
17
|
+
listTemplatesToolMeta,
|
|
18
|
+
handleListTemplates,
|
|
19
|
+
} from './tools/listTemplates.js'
|
|
20
|
+
import {
|
|
21
|
+
getTemplateSchema,
|
|
22
|
+
getTemplateToolMeta,
|
|
23
|
+
handleGetTemplate,
|
|
24
|
+
} from './tools/getTemplate.js'
|
|
15
25
|
|
|
16
26
|
const apiKey = process.env.RENDSHOT_API_KEY
|
|
17
27
|
const apiUrl = process.env.RENDSHOT_API_URL || 'https://api.rendshot.ai'
|
|
@@ -25,7 +35,7 @@ const client = new RendshotClient({ apiKey, baseUrl: apiUrl })
|
|
|
25
35
|
|
|
26
36
|
const server = new McpServer({
|
|
27
37
|
name: 'rendshot',
|
|
28
|
-
version: '0.
|
|
38
|
+
version: '0.2.0',
|
|
29
39
|
})
|
|
30
40
|
|
|
31
41
|
server.tool(
|
|
@@ -42,5 +52,19 @@ server.tool(
|
|
|
42
52
|
async (args) => handleScreenshotUrl(client, args),
|
|
43
53
|
)
|
|
44
54
|
|
|
55
|
+
server.tool(
|
|
56
|
+
listTemplatesToolMeta.name,
|
|
57
|
+
listTemplatesToolMeta.description,
|
|
58
|
+
listTemplatesSchema,
|
|
59
|
+
async (args) => handleListTemplates(client, args),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
server.tool(
|
|
63
|
+
getTemplateToolMeta.name,
|
|
64
|
+
getTemplateToolMeta.description,
|
|
65
|
+
getTemplateSchema,
|
|
66
|
+
async (args) => handleGetTemplate(client, args),
|
|
67
|
+
)
|
|
68
|
+
|
|
45
69
|
const transport = new StdioServerTransport()
|
|
46
70
|
await server.connect(transport)
|
|
@@ -2,7 +2,9 @@ import { z } from 'zod'
|
|
|
2
2
|
import type { RendshotClient, ImageResult } from '@rendshot/sdk'
|
|
3
3
|
|
|
4
4
|
export const generateImageSchema = {
|
|
5
|
-
html: z.string().describe('HTML content to render'),
|
|
5
|
+
html: z.string().optional().describe('HTML content to render (provide this OR template_id, not both)'),
|
|
6
|
+
template_id: z.string().optional().describe('Template ID (e.g. tpl_abc123) — use list_templates to find available templates'),
|
|
7
|
+
variables: z.record(z.string(), z.unknown()).optional().describe('Template variables as key-value pairs — use get_template to see available variables'),
|
|
6
8
|
css: z.string().optional().describe('Optional CSS styles'),
|
|
7
9
|
width: z.number().optional().describe('Image width in pixels (default 1080)'),
|
|
8
10
|
height: z.number().optional().describe('Image height in pixels (default 1080)'),
|
|
@@ -15,13 +17,26 @@ export const generateImageSchema = {
|
|
|
15
17
|
|
|
16
18
|
export const generateImageToolMeta = {
|
|
17
19
|
name: 'generate_image',
|
|
18
|
-
description: 'Render HTML/CSS to an image and return the URL',
|
|
20
|
+
description: 'Render HTML/CSS or a template to an image and return the URL. Provide either html OR template_id (not both). Use list_templates and get_template to discover available templates and their variables.',
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
export async function handleGenerateImage(
|
|
22
24
|
client: RendshotClient,
|
|
23
25
|
args: z.objectOutputType<typeof generateImageSchema, z.ZodTypeAny>,
|
|
24
26
|
) {
|
|
27
|
+
if (!args.html && !args.template_id) {
|
|
28
|
+
return {
|
|
29
|
+
content: [{ type: 'text' as const, text: 'Error: Provide either html or template_id' }],
|
|
30
|
+
isError: true,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (args.html && args.template_id) {
|
|
34
|
+
return {
|
|
35
|
+
content: [{ type: 'text' as const, text: 'Error: Provide either html or template_id, not both' }],
|
|
36
|
+
isError: true,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
25
40
|
const result: ImageResult = await client.renderImage(args as any)
|
|
26
41
|
return {
|
|
27
42
|
content: [
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import type { RendshotClient } from '@rendshot/sdk'
|
|
3
|
+
|
|
4
|
+
export const getTemplateSchema = {
|
|
5
|
+
template_id: z.string().describe('Template ID (e.g. tpl_abc123)'),
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const getTemplateToolMeta = {
|
|
9
|
+
name: 'get_template',
|
|
10
|
+
description: 'Get detailed information about a template, including its variable definitions. Use this before generate_image to know which variables to pass.',
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function handleGetTemplate(
|
|
14
|
+
client: RendshotClient,
|
|
15
|
+
args: z.objectOutputType<typeof getTemplateSchema, z.ZodTypeAny>,
|
|
16
|
+
) {
|
|
17
|
+
const t = await client.getTemplate(args.template_id)
|
|
18
|
+
|
|
19
|
+
const varLines = t.variables.map((v) => {
|
|
20
|
+
const req = v.required ? ' (required)' : ''
|
|
21
|
+
const opts = v.options ? ` options: [${v.options.join(', ')}]` : ''
|
|
22
|
+
return ` - ${v.key} (${v.type})${req}: default="${v.default}"${opts}`
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const text = [
|
|
26
|
+
`Template: ${t.name} (${t.id})`,
|
|
27
|
+
t.description ? `Description: ${t.description}` : null,
|
|
28
|
+
`Platform: ${t.platform}`,
|
|
29
|
+
`Size: ${t.width}x${t.height}`,
|
|
30
|
+
t.tags.length > 0 ? `Tags: ${t.tags.join(', ')}` : null,
|
|
31
|
+
`Author: ${t.author.name ?? 'Unknown'}`,
|
|
32
|
+
'',
|
|
33
|
+
varLines.length > 0 ? `Variables (${varLines.length}):\n${varLines.join('\n')}` : 'No variables.',
|
|
34
|
+
].filter(Boolean).join('\n')
|
|
35
|
+
|
|
36
|
+
return { content: [{ type: 'text' as const, text }] }
|
|
37
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import type { RendshotClient } from '@rendshot/sdk'
|
|
3
|
+
|
|
4
|
+
export const listTemplatesSchema = {
|
|
5
|
+
platform: z.string().optional().describe('Filter by platform (e.g. twitter, instagram, custom)'),
|
|
6
|
+
category: z.string().optional().describe('Filter by category'),
|
|
7
|
+
q: z.string().optional().describe('Search by name or description'),
|
|
8
|
+
limit: z.number().optional().describe('Max results 1-50 (default 20)'),
|
|
9
|
+
cursor: z.string().optional().describe('Pagination cursor from previous response'),
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const listTemplatesToolMeta = {
|
|
13
|
+
name: 'list_templates',
|
|
14
|
+
description: 'Browse available image templates. Returns template IDs, names, dimensions, and variable counts. Use get_template for full variable details.',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function handleListTemplates(
|
|
18
|
+
client: RendshotClient,
|
|
19
|
+
args: z.objectOutputType<typeof listTemplatesSchema, z.ZodTypeAny>,
|
|
20
|
+
) {
|
|
21
|
+
const result = await client.listTemplates(args)
|
|
22
|
+
|
|
23
|
+
if (result.templates.length === 0) {
|
|
24
|
+
return {
|
|
25
|
+
content: [{ type: 'text' as const, text: 'No templates found matching the criteria.' }],
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const lines = result.templates.map((t) => {
|
|
30
|
+
const vars = t.variables.map((v) => `${v.key}(${v.type})`).join(', ')
|
|
31
|
+
return `- ${t.id}: ${t.name} (${t.width}x${t.height}) — variables: ${vars || 'none'}`
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
let text = `Found ${result.templates.length} template(s):\n\n${lines.join('\n')}`
|
|
35
|
+
if (result.nextCursor) {
|
|
36
|
+
text += `\n\nMore results available — pass cursor: "${result.nextCursor}"`
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return { content: [{ type: 'text' as const, text }] }
|
|
40
|
+
}
|