mcp-gif 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 +72 -0
  2. package/build/index.js +83 -0
  3. package/package.json +38 -0
package/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # mcp-gif
2
+
3
+ MCP server for GIF animation analysis. Provides metadata extraction, individual frame export, and per-frame timing information.
4
+
5
+ ## Setup
6
+
7
+ ### Claude Code
8
+
9
+ ```bash
10
+ claude mcp add -s user gif npx mcp-gif
11
+ ```
12
+
13
+ ### Claude Desktop
14
+
15
+ Add to your `claude_desktop_config.json`:
16
+
17
+ ```json
18
+ {
19
+ "mcpServers": {
20
+ "gif": {
21
+ "command": "npx",
22
+ "args": ["mcp-gif"]
23
+ }
24
+ }
25
+ }
26
+ ```
27
+
28
+ ## Tools
29
+
30
+ ### `gif_info`
31
+
32
+ Get GIF metadata: dimensions, frame count, delays, loop count, and average FPS.
33
+
34
+ **Input**: `path` — path to a GIF file
35
+
36
+ **Output**:
37
+ ```json
38
+ {
39
+ "width": 320,
40
+ "height": 240,
41
+ "frames": 12,
42
+ "delays": [100, 100, 100],
43
+ "loop": 0,
44
+ "avgFps": 10
45
+ }
46
+ ```
47
+
48
+ ### `gif_frame`
49
+
50
+ Extract a single frame as a PNG image.
51
+
52
+ **Input**: `path` — path to a GIF file, `frame` — 0-based frame index
53
+
54
+ **Output**: PNG image (base64)
55
+
56
+ ### `gif_frames_list`
57
+
58
+ List all frames with their index, delay (ms), and FPS.
59
+
60
+ **Input**: `path` — path to a GIF file
61
+
62
+ **Output**:
63
+ ```json
64
+ [
65
+ { "index": 0, "delay": 100, "fps": 10 },
66
+ { "index": 1, "delay": 200, "fps": 5 }
67
+ ]
68
+ ```
69
+
70
+ ## License
71
+
72
+ BSD-2-Clause
package/build/index.js ADDED
@@ -0,0 +1,83 @@
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 sharp from "sharp";
5
+ import { z } from "zod";
6
+ const server = new McpServer({
7
+ name: "mcp-gif",
8
+ version: "1.0.0",
9
+ });
10
+ server.tool("gif_info", "Get metadata of a GIF file: dimensions, frame count, delays, loop count, and average FPS", { path: z.string().describe("Path to the GIF file") }, async ({ path }) => {
11
+ const metadata = await sharp(path, { animated: true }).metadata();
12
+ const width = metadata.width ?? 0;
13
+ const height = metadata.pageHeight ?? metadata.height ?? 0;
14
+ const frames = metadata.pages ?? 1;
15
+ const delays = metadata.delay ?? [];
16
+ const loop = metadata.loop ?? 0;
17
+ const avgDelay = delays.length > 0
18
+ ? delays.reduce((a, b) => a + b, 0) / delays.length
19
+ : 100;
20
+ const avgFps = avgDelay > 0 ? Math.round((1000 / avgDelay) * 100) / 100 : 0;
21
+ return {
22
+ content: [
23
+ {
24
+ type: "text",
25
+ text: JSON.stringify({ width, height, frames, delays, loop, avgFps }, null, 2),
26
+ },
27
+ ],
28
+ };
29
+ });
30
+ server.tool("gif_frame", "Extract a single frame from a GIF as a PNG image", {
31
+ path: z.string().describe("Path to the GIF file"),
32
+ frame: z.number().int().min(0).describe("Frame index (0-based)"),
33
+ }, async ({ path, frame }) => {
34
+ const metadata = await sharp(path, { animated: true }).metadata();
35
+ const totalFrames = metadata.pages ?? 1;
36
+ if (frame >= totalFrames) {
37
+ return {
38
+ content: [
39
+ {
40
+ type: "text",
41
+ text: `Error: frame index ${frame} out of range (0-${totalFrames - 1})`,
42
+ },
43
+ ],
44
+ isError: true,
45
+ };
46
+ }
47
+ const buffer = await sharp(path, { page: frame }).png().toBuffer();
48
+ return {
49
+ content: [
50
+ {
51
+ type: "image",
52
+ data: buffer.toString("base64"),
53
+ mimeType: "image/png",
54
+ },
55
+ ],
56
+ };
57
+ });
58
+ server.tool("gif_frames_list", "List all frames of a GIF with their index, delay (ms), and FPS", { path: z.string().describe("Path to the GIF file") }, async ({ path }) => {
59
+ const metadata = await sharp(path, { animated: true }).metadata();
60
+ const frames = metadata.pages ?? 1;
61
+ const delays = metadata.delay ?? [];
62
+ const list = Array.from({ length: frames }, (_, i) => {
63
+ const delay = delays[i] ?? 100;
64
+ const fps = delay > 0 ? Math.round((1000 / delay) * 100) / 100 : 0;
65
+ return { index: i, delay, fps };
66
+ });
67
+ return {
68
+ content: [
69
+ {
70
+ type: "text",
71
+ text: JSON.stringify(list, null, 2),
72
+ },
73
+ ],
74
+ };
75
+ });
76
+ async function main() {
77
+ const transport = new StdioServerTransport();
78
+ await server.connect(transport);
79
+ }
80
+ main().catch((error) => {
81
+ console.error("Fatal error:", error);
82
+ process.exit(1);
83
+ });
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "mcp-gif",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for GIF animation analysis — metadata, frame extraction, and frame listing",
5
+ "type": "module",
6
+ "bin": {
7
+ "mcp-gif": "./build/index.js"
8
+ },
9
+ "files": [
10
+ "build"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "prepublishOnly": "npm run build",
15
+ "start": "node build/index.js"
16
+ },
17
+ "keywords": [
18
+ "mcp",
19
+ "gif",
20
+ "animation",
21
+ "sharp",
22
+ "model-context-protocol"
23
+ ],
24
+ "license": "BSD-2-Clause",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/signal-slot/mcp-gif.git"
28
+ },
29
+ "dependencies": {
30
+ "@modelcontextprotocol/sdk": "^1.27.1",
31
+ "sharp": "^0.33.5",
32
+ "zod": "^3.24.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^22.0.0",
36
+ "typescript": "^5.7.0"
37
+ }
38
+ }