@rog0x/mcp-image-tools 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.
- package/README.md +46 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +247 -0
- package/dist/tools/favicon-finder.d.ts +12 -0
- package/dist/tools/favicon-finder.js +177 -0
- package/dist/tools/image-meta.d.ts +14 -0
- package/dist/tools/image-meta.js +50 -0
- package/dist/tools/og-image.d.ts +18 -0
- package/dist/tools/og-image.js +95 -0
- package/dist/tools/placeholder-generator.d.ts +26 -0
- package/dist/tools/placeholder-generator.js +50 -0
- package/dist/tools/responsive-images.d.ts +22 -0
- package/dist/tools/responsive-images.js +101 -0
- package/package.json +37 -0
- package/src/index.ts +271 -0
- package/src/tools/favicon-finder.ts +193 -0
- package/src/tools/image-meta.ts +64 -0
- package/src/tools/og-image.ts +122 -0
- package/src/tools/placeholder-generator.ts +81 -0
- package/src/tools/responsive-images.ts +127 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface PlaceholderConfig {
|
|
2
|
+
width: number;
|
|
3
|
+
height?: number;
|
|
4
|
+
backgroundColor?: string;
|
|
5
|
+
textColor?: string;
|
|
6
|
+
text?: string;
|
|
7
|
+
format?: "png" | "jpg" | "jpeg" | "gif" | "webp" | "svg";
|
|
8
|
+
font?: string;
|
|
9
|
+
fontSize?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface PlaceholderResult {
|
|
12
|
+
url: string;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
backgroundColor: string;
|
|
16
|
+
textColor: string;
|
|
17
|
+
text: string;
|
|
18
|
+
format: string;
|
|
19
|
+
htmlImg: string;
|
|
20
|
+
markdownImg: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function generatePlaceholder(config: PlaceholderConfig): PlaceholderResult;
|
|
23
|
+
export declare function generatePlaceholderSet(sizes: Array<{
|
|
24
|
+
width: number;
|
|
25
|
+
height?: number;
|
|
26
|
+
}>, options?: Omit<PlaceholderConfig, "width" | "height">): PlaceholderResult[];
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generatePlaceholder = generatePlaceholder;
|
|
4
|
+
exports.generatePlaceholderSet = generatePlaceholderSet;
|
|
5
|
+
function generatePlaceholder(config) {
|
|
6
|
+
const width = Math.max(1, Math.min(config.width, 4000));
|
|
7
|
+
const height = config.height ? Math.max(1, Math.min(config.height, 4000)) : width;
|
|
8
|
+
const bgColor = (config.backgroundColor || "cccccc").replace(/^#/, "");
|
|
9
|
+
const txtColor = (config.textColor || "333333").replace(/^#/, "");
|
|
10
|
+
const format = config.format || "png";
|
|
11
|
+
const text = config.text || `${width}x${height}`;
|
|
12
|
+
// Build placehold.co URL
|
|
13
|
+
let url = `https://placehold.co/${width}x${height}/${bgColor}/${txtColor}`;
|
|
14
|
+
// Add format
|
|
15
|
+
url += `.${format}`;
|
|
16
|
+
// Add query params
|
|
17
|
+
const params = new URLSearchParams();
|
|
18
|
+
if (config.text) {
|
|
19
|
+
params.set("text", config.text);
|
|
20
|
+
}
|
|
21
|
+
if (config.font) {
|
|
22
|
+
params.set("font", config.font);
|
|
23
|
+
}
|
|
24
|
+
if (config.fontSize) {
|
|
25
|
+
params.set("font-size", config.fontSize.toString());
|
|
26
|
+
}
|
|
27
|
+
const queryString = params.toString();
|
|
28
|
+
if (queryString) {
|
|
29
|
+
url += `?${queryString}`;
|
|
30
|
+
}
|
|
31
|
+
const alt = `Placeholder ${width}x${height}`;
|
|
32
|
+
return {
|
|
33
|
+
url,
|
|
34
|
+
width,
|
|
35
|
+
height,
|
|
36
|
+
backgroundColor: `#${bgColor}`,
|
|
37
|
+
textColor: `#${txtColor}`,
|
|
38
|
+
text,
|
|
39
|
+
format,
|
|
40
|
+
htmlImg: `<img src="${url}" alt="${alt}" width="${width}" height="${height}" />`,
|
|
41
|
+
markdownImg: ``,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function generatePlaceholderSet(sizes, options) {
|
|
45
|
+
return sizes.map((size) => generatePlaceholder({
|
|
46
|
+
...options,
|
|
47
|
+
width: size.width,
|
|
48
|
+
height: size.height,
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface ResponsiveConfig {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
alt?: string;
|
|
4
|
+
widths?: number[];
|
|
5
|
+
sizes?: string;
|
|
6
|
+
className?: string;
|
|
7
|
+
loading?: "lazy" | "eager";
|
|
8
|
+
formats?: string[];
|
|
9
|
+
}
|
|
10
|
+
export interface ResponsiveResult {
|
|
11
|
+
baseUrl: string;
|
|
12
|
+
variants: ImageVariant[];
|
|
13
|
+
srcsetAttr: string;
|
|
14
|
+
imgTag: string;
|
|
15
|
+
pictureTag: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ImageVariant {
|
|
18
|
+
url: string;
|
|
19
|
+
width: number;
|
|
20
|
+
descriptor: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function generateResponsiveImages(config: ResponsiveConfig): ResponsiveResult;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateResponsiveImages = generateResponsiveImages;
|
|
4
|
+
function inferImageFormat(url) {
|
|
5
|
+
const pathname = new URL(url).pathname.toLowerCase();
|
|
6
|
+
if (pathname.endsWith(".webp"))
|
|
7
|
+
return "webp";
|
|
8
|
+
if (pathname.endsWith(".avif"))
|
|
9
|
+
return "avif";
|
|
10
|
+
if (pathname.endsWith(".png"))
|
|
11
|
+
return "png";
|
|
12
|
+
if (pathname.endsWith(".gif"))
|
|
13
|
+
return "gif";
|
|
14
|
+
if (pathname.endsWith(".svg"))
|
|
15
|
+
return "svg";
|
|
16
|
+
return "jpeg";
|
|
17
|
+
}
|
|
18
|
+
function buildVariantUrl(baseUrl, width) {
|
|
19
|
+
// Append width parameter - handles both existing and new query strings
|
|
20
|
+
const url = new URL(baseUrl);
|
|
21
|
+
url.searchParams.set("w", width.toString());
|
|
22
|
+
return url.href;
|
|
23
|
+
}
|
|
24
|
+
function escapeHtml(str) {
|
|
25
|
+
return str
|
|
26
|
+
.replace(/&/g, "&")
|
|
27
|
+
.replace(/"/g, """)
|
|
28
|
+
.replace(/</g, "<")
|
|
29
|
+
.replace(/>/g, ">");
|
|
30
|
+
}
|
|
31
|
+
function generateResponsiveImages(config) {
|
|
32
|
+
const defaultWidths = [320, 640, 768, 1024, 1280, 1536, 1920];
|
|
33
|
+
const widths = config.widths || defaultWidths;
|
|
34
|
+
const alt = config.alt || "";
|
|
35
|
+
const sizes = config.sizes || "(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw";
|
|
36
|
+
const loading = config.loading || "lazy";
|
|
37
|
+
const className = config.className || "";
|
|
38
|
+
const formats = config.formats || ["webp", inferImageFormat(config.baseUrl)];
|
|
39
|
+
const sortedWidths = [...widths].sort((a, b) => a - b);
|
|
40
|
+
// Generate variants
|
|
41
|
+
const variants = sortedWidths.map((width) => ({
|
|
42
|
+
url: buildVariantUrl(config.baseUrl, width),
|
|
43
|
+
width,
|
|
44
|
+
descriptor: `${width}w`,
|
|
45
|
+
}));
|
|
46
|
+
// Build srcset attribute
|
|
47
|
+
const srcsetAttr = variants
|
|
48
|
+
.map((v) => `${v.url} ${v.descriptor}`)
|
|
49
|
+
.join(",\n ");
|
|
50
|
+
// Build simple <img> tag with srcset
|
|
51
|
+
const classAttr = className ? ` class="${escapeHtml(className)}"` : "";
|
|
52
|
+
const imgTag = [
|
|
53
|
+
`<img`,
|
|
54
|
+
` src="${escapeHtml(config.baseUrl)}"`,
|
|
55
|
+
` srcset="${srcsetAttr}"`,
|
|
56
|
+
` sizes="${escapeHtml(sizes)}"`,
|
|
57
|
+
` alt="${escapeHtml(alt)}"`,
|
|
58
|
+
` loading="${loading}"`,
|
|
59
|
+
className ? ` class="${escapeHtml(className)}"` : null,
|
|
60
|
+
`/>`,
|
|
61
|
+
]
|
|
62
|
+
.filter(Boolean)
|
|
63
|
+
.join("\n");
|
|
64
|
+
// Build <picture> element with multiple formats
|
|
65
|
+
const sourceElements = formats.map((format) => {
|
|
66
|
+
const formatVariants = sortedWidths.map((width) => {
|
|
67
|
+
const url = new URL(config.baseUrl);
|
|
68
|
+
url.searchParams.set("w", width.toString());
|
|
69
|
+
url.searchParams.set("fm", format);
|
|
70
|
+
return `${url.href} ${width}w`;
|
|
71
|
+
});
|
|
72
|
+
const formatSrcset = formatVariants.join(",\n ");
|
|
73
|
+
const mimeType = format === "jpg" || format === "jpeg"
|
|
74
|
+
? "image/jpeg"
|
|
75
|
+
: format === "avif"
|
|
76
|
+
? "image/avif"
|
|
77
|
+
: format === "webp"
|
|
78
|
+
? "image/webp"
|
|
79
|
+
: format === "png"
|
|
80
|
+
? "image/png"
|
|
81
|
+
: `image/${format}`;
|
|
82
|
+
return ` <source\n type="${mimeType}"\n srcset="${formatSrcset}"\n sizes="${escapeHtml(sizes)}"\n />`;
|
|
83
|
+
});
|
|
84
|
+
const pictureTag = [
|
|
85
|
+
`<picture${classAttr}>`,
|
|
86
|
+
...sourceElements,
|
|
87
|
+
` <img`,
|
|
88
|
+
` src="${escapeHtml(config.baseUrl)}"`,
|
|
89
|
+
` alt="${escapeHtml(alt)}"`,
|
|
90
|
+
` loading="${loading}"`,
|
|
91
|
+
` />`,
|
|
92
|
+
`</picture>`,
|
|
93
|
+
].join("\n");
|
|
94
|
+
return {
|
|
95
|
+
baseUrl: config.baseUrl,
|
|
96
|
+
variants,
|
|
97
|
+
srcsetAttr,
|
|
98
|
+
imgTag,
|
|
99
|
+
pictureTag,
|
|
100
|
+
};
|
|
101
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rog0x/mcp-image-tools",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server providing image analysis tools for AI agents - metadata, favicons, OG images, placeholders, responsive markup",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mcp-image-tools": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"start": "node dist/index.js",
|
|
12
|
+
"dev": "ts-node src/index.ts"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mcp",
|
|
16
|
+
"claude",
|
|
17
|
+
"ai",
|
|
18
|
+
"image-tools",
|
|
19
|
+
"metadata",
|
|
20
|
+
"favicon",
|
|
21
|
+
"og-image",
|
|
22
|
+
"responsive-images"
|
|
23
|
+
],
|
|
24
|
+
"author": "rog0x",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/rog0x/mcp-image-tools"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^20.0.0",
|
|
35
|
+
"typescript": "^5.4.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import {
|
|
6
|
+
CallToolRequestSchema,
|
|
7
|
+
ListToolsRequestSchema,
|
|
8
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
import { getImageMetadata } from "./tools/image-meta.js";
|
|
10
|
+
import { findFavicons } from "./tools/favicon-finder.js";
|
|
11
|
+
import { extractOgImage } from "./tools/og-image.js";
|
|
12
|
+
import { generatePlaceholder, generatePlaceholderSet } from "./tools/placeholder-generator.js";
|
|
13
|
+
import { generateResponsiveImages } from "./tools/responsive-images.js";
|
|
14
|
+
|
|
15
|
+
const server = new Server(
|
|
16
|
+
{
|
|
17
|
+
name: "mcp-image-tools",
|
|
18
|
+
version: "1.0.0",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
capabilities: {
|
|
22
|
+
tools: {},
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// List available tools
|
|
28
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
29
|
+
tools: [
|
|
30
|
+
{
|
|
31
|
+
name: "image_metadata",
|
|
32
|
+
description:
|
|
33
|
+
"Read image metadata from a URL via HTTP headers: content-type, file size, last-modified, etag, cache info. No image downloading required.",
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: "object" as const,
|
|
36
|
+
properties: {
|
|
37
|
+
url: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "Direct URL to an image file",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
required: ["url"],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "find_favicons",
|
|
47
|
+
description:
|
|
48
|
+
"Find all favicons for a website. Checks /favicon.ico, parses HTML link tags, and inspects manifest.json. Returns all found favicons with sizes and types.",
|
|
49
|
+
inputSchema: {
|
|
50
|
+
type: "object" as const,
|
|
51
|
+
properties: {
|
|
52
|
+
url: {
|
|
53
|
+
type: "string",
|
|
54
|
+
description: "Website URL to find favicons for (e.g. https://example.com)",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
required: ["url"],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "extract_og_image",
|
|
62
|
+
description:
|
|
63
|
+
"Extract Open Graph image, Twitter card image, and Apple touch icon from any URL. Returns all social media preview images found on the page.",
|
|
64
|
+
inputSchema: {
|
|
65
|
+
type: "object" as const,
|
|
66
|
+
properties: {
|
|
67
|
+
url: {
|
|
68
|
+
type: "string",
|
|
69
|
+
description: "URL of the web page to extract images from",
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
required: ["url"],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "generate_placeholder",
|
|
77
|
+
description:
|
|
78
|
+
"Generate placeholder image URLs via placehold.co. Supports custom size, colors, text, format, and font. Returns URL plus ready-to-use HTML and Markdown markup.",
|
|
79
|
+
inputSchema: {
|
|
80
|
+
type: "object" as const,
|
|
81
|
+
properties: {
|
|
82
|
+
width: {
|
|
83
|
+
type: "number",
|
|
84
|
+
description: "Image width in pixels (1-4000)",
|
|
85
|
+
},
|
|
86
|
+
height: {
|
|
87
|
+
type: "number",
|
|
88
|
+
description: "Image height in pixels (1-4000). Defaults to same as width.",
|
|
89
|
+
},
|
|
90
|
+
backgroundColor: {
|
|
91
|
+
type: "string",
|
|
92
|
+
description: "Background color hex without # (e.g. 'cccccc'). Default: cccccc",
|
|
93
|
+
},
|
|
94
|
+
textColor: {
|
|
95
|
+
type: "string",
|
|
96
|
+
description: "Text color hex without # (e.g. '333333'). Default: 333333",
|
|
97
|
+
},
|
|
98
|
+
text: {
|
|
99
|
+
type: "string",
|
|
100
|
+
description: "Custom text to display on the image. Default: WIDTHxHEIGHT",
|
|
101
|
+
},
|
|
102
|
+
format: {
|
|
103
|
+
type: "string",
|
|
104
|
+
description: "Image format: png, jpg, jpeg, gif, webp, svg. Default: png",
|
|
105
|
+
enum: ["png", "jpg", "jpeg", "gif", "webp", "svg"],
|
|
106
|
+
},
|
|
107
|
+
font: {
|
|
108
|
+
type: "string",
|
|
109
|
+
description: "Font name (e.g. 'roboto', 'open-sans', 'montserrat')",
|
|
110
|
+
},
|
|
111
|
+
fontSize: {
|
|
112
|
+
type: "number",
|
|
113
|
+
description: "Font size in pixels",
|
|
114
|
+
},
|
|
115
|
+
sizes: {
|
|
116
|
+
type: "array",
|
|
117
|
+
items: {
|
|
118
|
+
type: "object",
|
|
119
|
+
properties: {
|
|
120
|
+
width: { type: "number" },
|
|
121
|
+
height: { type: "number" },
|
|
122
|
+
},
|
|
123
|
+
required: ["width"],
|
|
124
|
+
},
|
|
125
|
+
description:
|
|
126
|
+
"Generate multiple placeholders at once. If provided, width/height params are ignored.",
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
required: ["width"],
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: "responsive_images",
|
|
134
|
+
description:
|
|
135
|
+
"Generate srcset and <picture> element HTML for responsive images. Given a base image URL, produces multiple size variants with proper HTML markup for responsive design.",
|
|
136
|
+
inputSchema: {
|
|
137
|
+
type: "object" as const,
|
|
138
|
+
properties: {
|
|
139
|
+
baseUrl: {
|
|
140
|
+
type: "string",
|
|
141
|
+
description: "Base image URL that accepts width/format query parameters",
|
|
142
|
+
},
|
|
143
|
+
alt: {
|
|
144
|
+
type: "string",
|
|
145
|
+
description: "Alt text for the image",
|
|
146
|
+
},
|
|
147
|
+
widths: {
|
|
148
|
+
type: "array",
|
|
149
|
+
items: { type: "number" },
|
|
150
|
+
description:
|
|
151
|
+
"Array of widths to generate (default: [320, 640, 768, 1024, 1280, 1536, 1920])",
|
|
152
|
+
},
|
|
153
|
+
sizes: {
|
|
154
|
+
type: "string",
|
|
155
|
+
description:
|
|
156
|
+
"CSS sizes attribute (default: '(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw')",
|
|
157
|
+
},
|
|
158
|
+
className: {
|
|
159
|
+
type: "string",
|
|
160
|
+
description: "CSS class name to add to the element",
|
|
161
|
+
},
|
|
162
|
+
loading: {
|
|
163
|
+
type: "string",
|
|
164
|
+
description: "Loading strategy: 'lazy' or 'eager' (default: lazy)",
|
|
165
|
+
enum: ["lazy", "eager"],
|
|
166
|
+
},
|
|
167
|
+
formats: {
|
|
168
|
+
type: "array",
|
|
169
|
+
items: { type: "string" },
|
|
170
|
+
description:
|
|
171
|
+
"Image formats for <picture> sources (default: ['webp', auto-detected original format])",
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
required: ["baseUrl"],
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
}));
|
|
179
|
+
|
|
180
|
+
// Handle tool calls
|
|
181
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
182
|
+
const { name, arguments: args } = request.params;
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
switch (name) {
|
|
186
|
+
case "image_metadata": {
|
|
187
|
+
const result = await getImageMetadata(args?.url as string);
|
|
188
|
+
return {
|
|
189
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
case "find_favicons": {
|
|
194
|
+
const result = await findFavicons(args?.url as string);
|
|
195
|
+
return {
|
|
196
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
case "extract_og_image": {
|
|
201
|
+
const result = await extractOgImage(args?.url as string);
|
|
202
|
+
return {
|
|
203
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
case "generate_placeholder": {
|
|
208
|
+
const sizes = args?.sizes as Array<{ width: number; height?: number }> | undefined;
|
|
209
|
+
if (sizes && sizes.length > 0) {
|
|
210
|
+
const results = generatePlaceholderSet(sizes, {
|
|
211
|
+
backgroundColor: args?.backgroundColor as string | undefined,
|
|
212
|
+
textColor: args?.textColor as string | undefined,
|
|
213
|
+
text: args?.text as string | undefined,
|
|
214
|
+
format: args?.format as "png" | "jpg" | "jpeg" | "gif" | "webp" | "svg" | undefined,
|
|
215
|
+
font: args?.font as string | undefined,
|
|
216
|
+
fontSize: args?.fontSize as number | undefined,
|
|
217
|
+
});
|
|
218
|
+
return {
|
|
219
|
+
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
const result = generatePlaceholder({
|
|
223
|
+
width: args?.width as number,
|
|
224
|
+
height: args?.height as number | undefined,
|
|
225
|
+
backgroundColor: args?.backgroundColor as string | undefined,
|
|
226
|
+
textColor: args?.textColor as string | undefined,
|
|
227
|
+
text: args?.text as string | undefined,
|
|
228
|
+
format: args?.format as "png" | "jpg" | "jpeg" | "gif" | "webp" | "svg" | undefined,
|
|
229
|
+
font: args?.font as string | undefined,
|
|
230
|
+
fontSize: args?.fontSize as number | undefined,
|
|
231
|
+
});
|
|
232
|
+
return {
|
|
233
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
case "responsive_images": {
|
|
238
|
+
const result = generateResponsiveImages({
|
|
239
|
+
baseUrl: args?.baseUrl as string,
|
|
240
|
+
alt: args?.alt as string | undefined,
|
|
241
|
+
widths: args?.widths as number[] | undefined,
|
|
242
|
+
sizes: args?.sizes as string | undefined,
|
|
243
|
+
className: args?.className as string | undefined,
|
|
244
|
+
loading: args?.loading as "lazy" | "eager" | undefined,
|
|
245
|
+
formats: args?.formats as string[] | undefined,
|
|
246
|
+
});
|
|
247
|
+
return {
|
|
248
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
default:
|
|
253
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
254
|
+
}
|
|
255
|
+
} catch (error: unknown) {
|
|
256
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
257
|
+
return {
|
|
258
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
259
|
+
isError: true,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// Start server
|
|
265
|
+
async function main() {
|
|
266
|
+
const transport = new StdioServerTransport();
|
|
267
|
+
await server.connect(transport);
|
|
268
|
+
console.error("MCP Image Tools server running on stdio");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
main().catch(console.error);
|