@gripforgeai/mcp 0.1.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 +60 -0
  2. package/dist/server.js +116 -0
  3. package/package.json +41 -0
package/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # @gripforgeai/mcp
2
+
3
+ Attach weapons & props to rigged characters — from Claude Code, Cursor or any
4
+ MCP client. Thin client for the [GripForge](https://gripforge.ai) API.
5
+
6
+ ## Install (Claude Code)
7
+
8
+ ```bash
9
+ claude mcp add gripforge -e GRIPFORGE_API_KEY=gf_... -- npx -y @gripforgeai/mcp
10
+ ```
11
+
12
+ Or in `.mcp.json`:
13
+
14
+ ```json
15
+ {
16
+ "mcpServers": {
17
+ "gripforge": {
18
+ "command": "npx",
19
+ "args": ["-y", "@gripforgeai/mcp"],
20
+ "env": { "GRIPFORGE_API_KEY": "gf_..." }
21
+ }
22
+ }
23
+ }
24
+ ```
25
+
26
+ Get a free API key (3 attaches / month) at https://gripforge.ai/login.
27
+
28
+ ## Install (Grok)
29
+
30
+ ```bash
31
+ grok mcp add gripforge --env GRIPFORGE_API_KEY=gf_... -- npx -y @gripforgeai/mcp
32
+ ```
33
+
34
+ Or in `~/.grok/config.toml`:
35
+
36
+ ```toml
37
+ [mcp_servers.gripforge]
38
+ command = "npx"
39
+ args = ["-y", "@gripforgeai/mcp"]
40
+ enabled = true
41
+ startup_timeout_sec = 45
42
+
43
+ [mcp_servers.gripforge.env]
44
+ GRIPFORGE_API_KEY = "gf_..."
45
+ ```
46
+
47
+ Also works with Cursor, Windsurf and any MCP-compatible client — same
48
+ `command` / `args` / `env` triple.
49
+
50
+
51
+ ## Tools
52
+
53
+ - `gripforge_attach` — character + prop in, bone-local bind + Three.js / Unity /
54
+ Godot snippets out. Styles: melee, gun, shield, staff (scythe/polearm).
55
+ - `gripforge_formats` — supported formats & options.
56
+
57
+ ## Env
58
+
59
+ - `GRIPFORGE_API_KEY` (required) — 1 credit = 1 successful attach
60
+ - `GRIPFORGE_API_URL` (optional) — defaults to https://gripforge.ai
package/dist/server.js ADDED
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * GripForge MCP — thin client.
4
+ *
5
+ * Exposes gripforge_attach to any MCP client (Claude Code, Cursor, Grok…) and
6
+ * delegates the compute to the hosted GripForge API. Requires an API key:
7
+ * create one at https://gripforge.ai/login and set GRIPFORGE_API_KEY in the
8
+ * server env. 1 credit = 1 successful attach.
9
+ */
10
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
11
+ import { basename, extname, join, resolve } from 'node:path';
12
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
13
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
14
+ import { z } from 'zod';
15
+ const API_URL = process.env.GRIPFORGE_API_URL ?? 'https://gripforge.ai';
16
+ const API_KEY = process.env.GRIPFORGE_API_KEY;
17
+ const SUPPORTED = ['.glb', '.gltf', '.fbx', '.obj'];
18
+ const server = new McpServer({ name: 'gripforge', version: '0.1.0' });
19
+ const err = (text) => ({ content: [{ type: 'text', text }], isError: true });
20
+ server.tool('gripforge_attach', 'Attach a prop (sword, shield, gun, staff/scythe…) onto a rigged character. ' +
21
+ 'Finds the hand bone across naming schemes, scales to body height, seats the grip, ' +
22
+ 'and returns a bind + ready-to-paste Three.js / Unity / Godot snippets.', {
23
+ character_path: z.string().describe('Absolute path to the rigged character (.glb .gltf .fbx .obj)'),
24
+ prop_path: z.string().describe('Absolute path to the prop mesh'),
25
+ style: z.enum(['melee', 'gun', 'shield', 'staff']).optional().describe('Grip style (default: guessed from the filename)'),
26
+ hand: z.enum(['right', 'left']).optional().describe('Hand side (default right)'),
27
+ height_ratio: z.number().min(0.05).max(1.5).optional().describe('Prop size as a fraction of body height'),
28
+ fist: z.number().min(0).max(1).optional().describe('Fist closing amount, 0 open → 1 closed (default 1)'),
29
+ out_dir: z.string().optional().describe('Write bind.json + engine snippets into this folder'),
30
+ }, async ({ character_path, prop_path, style, hand, height_ratio, fist, out_dir }) => {
31
+ if (!API_KEY) {
32
+ return err('GRIPFORGE_API_KEY missing. Create a free account at ' +
33
+ API_URL +
34
+ '/login then set the key in this MCP server env:\n' +
35
+ '"env": { "GRIPFORGE_API_KEY": "gf_..." }');
36
+ }
37
+ const charPath = resolve(character_path);
38
+ const propPath = resolve(prop_path);
39
+ for (const p of [charPath, propPath]) {
40
+ if (!SUPPORTED.includes(extname(p).toLowerCase())) {
41
+ return err(`Unsupported format: ${p} — use ${SUPPORTED.join(' ')}`);
42
+ }
43
+ }
44
+ const form = new FormData();
45
+ form.append('character', new Blob([await readFile(charPath)]), basename(charPath));
46
+ form.append('prop', new Blob([await readFile(propPath)]), basename(propPath));
47
+ if (style)
48
+ form.append('style', style);
49
+ if (hand)
50
+ form.append('hand', hand);
51
+ if (height_ratio != null)
52
+ form.append('ratio', String(height_ratio));
53
+ if (fist != null)
54
+ form.append('fist', String(fist));
55
+ form.append('fingers', '1');
56
+ let res;
57
+ try {
58
+ res = await fetch(`${API_URL}/api/v1/attach`, {
59
+ method: 'POST',
60
+ headers: { 'x-api-key': API_KEY },
61
+ body: form,
62
+ });
63
+ }
64
+ catch {
65
+ return err(`GripForge API unreachable at ${API_URL} — check GRIPFORGE_API_URL.`);
66
+ }
67
+ const data = (await res.json().catch(() => ({})));
68
+ if (res.status === 401)
69
+ return err('Invalid GRIPFORGE_API_KEY — check it on ' + API_URL + '/account#api');
70
+ if (res.status === 402) {
71
+ const u = data;
72
+ return err(`Quota exceeded (${u.used}/${u.limit}, plan ${u.plan}) — upgrade at ${API_URL}/#pricing`);
73
+ }
74
+ if (!res.ok)
75
+ return err(`Attach failed (${res.status}): ${String(data.error ?? 'unknown error')}`);
76
+ const exportsObj = (data.exports ?? {});
77
+ let wrote = null;
78
+ if (out_dir) {
79
+ const dir = resolve(out_dir);
80
+ await mkdir(dir, { recursive: true });
81
+ await writeFile(join(dir, 'bind.json'), exportsObj.json ?? JSON.stringify(data.bind, null, 2));
82
+ if (exportsObj.three)
83
+ await writeFile(join(dir, 'three.js.txt'), exportsObj.three);
84
+ if (exportsObj.unity)
85
+ await writeFile(join(dir, 'unity.cs.txt'), exportsObj.unity);
86
+ if (exportsObj.godot)
87
+ await writeFile(join(dir, 'godot.gd.txt'), exportsObj.godot);
88
+ wrote = dir;
89
+ }
90
+ const credits = data.credits;
91
+ return {
92
+ content: [
93
+ {
94
+ type: 'text',
95
+ text: JSON.stringify({ bind: data.bind, confidence: data.confidence, exports: exportsObj, wrote }, null, 2) +
96
+ (credits ? `\ncredits: ${credits.remaining}/${credits.limit} remaining (${credits.plan})` : ''),
97
+ },
98
+ ],
99
+ };
100
+ });
101
+ server.tool('gripforge_formats', 'List supported mesh formats, grip styles and hands.', {}, async () => ({
102
+ content: [
103
+ {
104
+ type: 'text',
105
+ text: JSON.stringify({
106
+ formats: SUPPORTED,
107
+ styles: ['melee', 'gun', 'shield', 'staff'],
108
+ hands: ['right', 'left'],
109
+ api: API_URL,
110
+ docs: API_URL + '/docs',
111
+ }, null, 2),
112
+ },
113
+ ],
114
+ }));
115
+ const transport = new StdioServerTransport();
116
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@gripforgeai/mcp",
3
+ "version": "0.1.0",
4
+ "description": "GripForge MCP server — attach weapons & props to rigged characters from Claude Code, Cursor or any MCP client. Calls the GripForge API.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "gripforge-mcp": "dist/server.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc -p tsconfig.json && node -e \"require('fs').chmodSync('dist/server.js', 0o755)\"",
16
+ "prepare": "tsc -p tsconfig.json && node -e \"require('fs').chmodSync('dist/server.js', 0o755)\"",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "mcp",
21
+ "model-context-protocol",
22
+ "3d",
23
+ "gamedev",
24
+ "rigging",
25
+ "weapon",
26
+ "bone-attach",
27
+ "claude"
28
+ ],
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/gripforgeai/mcp"
32
+ },
33
+ "dependencies": {
34
+ "@modelcontextprotocol/sdk": "^1.12.1",
35
+ "zod": "^3.24.0"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^20",
39
+ "typescript": "^5"
40
+ }
41
+ }