funplay-godot-mcp 0.7.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 ADDED
@@ -0,0 +1,40 @@
1
+ # Funplay Godot MCP stdio wrapper
2
+
3
+ <!-- mcp-name: io.github.FunplayAI/funplay-godot-mcp -->
4
+
5
+ This package bridges MCP stdio clients to the local HTTP server started by the Funplay MCP for Godot editor addon.
6
+
7
+ ## Usage
8
+
9
+ Start Godot, enable the addon, and make sure the Funplay MCP server is running. For local development, link the wrapper first:
10
+
11
+ ```bash
12
+ npm link
13
+ ```
14
+
15
+ Then run:
16
+
17
+ ```bash
18
+ funplay-godot-mcp --url http://127.0.0.1:8765/
19
+ ```
20
+
21
+ You can also configure the endpoint through an environment variable:
22
+
23
+ ```bash
24
+ FUNPLAY_GODOT_MCP_URL=http://127.0.0.1:8765/ funplay-godot-mcp
25
+ ```
26
+
27
+ ## MCP client config
28
+
29
+ ```json
30
+ {
31
+ "mcpServers": {
32
+ "funplay": {
33
+ "command": "funplay-godot-mcp",
34
+ "env": {
35
+ "FUNPLAY_GODOT_MCP_URL": "http://127.0.0.1:8765/"
36
+ }
37
+ }
38
+ }
39
+ }
40
+ ```
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { argv, env, exit, stderr, stdin, stdout } from "node:process";
4
+
5
+ const VERSION = "0.7.0";
6
+ const DEFAULT_URL = "http://127.0.0.1:8765/";
7
+
8
+ const options = parseArgs(argv.slice(2));
9
+ if (options.help) {
10
+ stdout.write(buildHelp());
11
+ exit(0);
12
+ }
13
+ if (options.version) {
14
+ stdout.write(`${VERSION}\n`);
15
+ exit(0);
16
+ }
17
+
18
+ const endpoint = normalizeEndpoint(
19
+ options.url || env.FUNPLAY_GODOT_MCP_URL || env.GODOT_MCP_URL || DEFAULT_URL,
20
+ );
21
+
22
+ let buffer = "";
23
+ let queue = Promise.resolve();
24
+
25
+ stdin.setEncoding("utf8");
26
+ stdin.on("data", (chunk) => {
27
+ buffer += chunk;
28
+ drainBuffer(false);
29
+ });
30
+
31
+ stdin.on("end", () => {
32
+ drainBuffer(true);
33
+ });
34
+
35
+ stdin.on("error", (error) => {
36
+ stderr.write(`[funplay-godot-mcp] stdin error: ${error.message}\n`);
37
+ });
38
+
39
+ function drainBuffer(flush) {
40
+ while (true) {
41
+ const index = buffer.indexOf("\n");
42
+ if (index < 0) {
43
+ break;
44
+ }
45
+ const line = buffer.slice(0, index);
46
+ buffer = buffer.slice(index + 1);
47
+ enqueueLine(line);
48
+ }
49
+
50
+ if (flush && buffer.trim() !== "") {
51
+ enqueueLine(buffer);
52
+ buffer = "";
53
+ }
54
+ }
55
+
56
+ function enqueueLine(line) {
57
+ queue = queue
58
+ .then(() => handleLine(line))
59
+ .catch((error) => {
60
+ stderr.write(`[funplay-godot-mcp] bridge error: ${error.stack || error.message}\n`);
61
+ });
62
+ }
63
+
64
+ async function handleLine(line) {
65
+ const trimmed = line.trim();
66
+ if (trimmed === "") {
67
+ return;
68
+ }
69
+
70
+ let message;
71
+ try {
72
+ message = JSON.parse(trimmed);
73
+ } catch {
74
+ writeMessage({
75
+ jsonrpc: "2.0",
76
+ id: null,
77
+ error: {
78
+ code: -32700,
79
+ message: "Parse error",
80
+ },
81
+ });
82
+ return;
83
+ }
84
+
85
+ if (Array.isArray(message)) {
86
+ const responses = [];
87
+ for (const item of message) {
88
+ const response = await forwardMessage(item);
89
+ if (response !== null) {
90
+ responses.push(response);
91
+ }
92
+ }
93
+ if (responses.length > 0) {
94
+ writeMessage(responses);
95
+ }
96
+ return;
97
+ }
98
+
99
+ const response = await forwardMessage(message);
100
+ if (response !== null) {
101
+ writeMessage(response);
102
+ }
103
+ }
104
+
105
+ async function forwardMessage(message) {
106
+ try {
107
+ const response = await fetch(endpoint, {
108
+ method: "POST",
109
+ headers: {
110
+ "accept": "application/json",
111
+ "content-type": "application/json",
112
+ },
113
+ body: JSON.stringify(message),
114
+ });
115
+
116
+ if (response.status === 204) {
117
+ return null;
118
+ }
119
+
120
+ const text = await response.text();
121
+ if (text.trim() === "") {
122
+ return buildErrorFor(message, -32603, `Godot MCP returned HTTP ${response.status} with an empty body`);
123
+ }
124
+
125
+ try {
126
+ return JSON.parse(text);
127
+ } catch {
128
+ return buildErrorFor(message, -32603, `Godot MCP returned invalid JSON over HTTP ${response.status}`);
129
+ }
130
+ } catch (error) {
131
+ return buildErrorFor(
132
+ message,
133
+ -32000,
134
+ `Failed to reach Godot MCP at ${endpoint}: ${error.message}`,
135
+ );
136
+ }
137
+ }
138
+
139
+ function buildErrorFor(message, code, messageText) {
140
+ if (!isJsonRpcRequest(message)) {
141
+ stderr.write(`[funplay-godot-mcp] ${messageText}\n`);
142
+ return null;
143
+ }
144
+
145
+ return {
146
+ jsonrpc: "2.0",
147
+ id: message.id,
148
+ error: {
149
+ code,
150
+ message: messageText,
151
+ },
152
+ };
153
+ }
154
+
155
+ function isJsonRpcRequest(value) {
156
+ return value !== null
157
+ && typeof value === "object"
158
+ && !Array.isArray(value)
159
+ && Object.prototype.hasOwnProperty.call(value, "id");
160
+ }
161
+
162
+ function writeMessage(message) {
163
+ stdout.write(`${JSON.stringify(message)}\n`);
164
+ }
165
+
166
+ function normalizeEndpoint(value) {
167
+ const trimmed = String(value || "").trim();
168
+ return trimmed === "" ? DEFAULT_URL : trimmed;
169
+ }
170
+
171
+ function parseArgs(args) {
172
+ const parsed = {
173
+ help: false,
174
+ version: false,
175
+ url: "",
176
+ };
177
+
178
+ for (let i = 0; i < args.length; i += 1) {
179
+ const arg = args[i];
180
+ if (arg === "--help" || arg === "-h") {
181
+ parsed.help = true;
182
+ } else if (arg === "--version" || arg === "-v") {
183
+ parsed.version = true;
184
+ } else if (arg === "--url") {
185
+ parsed.url = args[i + 1] || "";
186
+ i += 1;
187
+ } else if (arg.startsWith("--url=")) {
188
+ parsed.url = arg.slice("--url=".length);
189
+ } else {
190
+ stderr.write(`[funplay-godot-mcp] Unknown argument ignored: ${arg}\n`);
191
+ }
192
+ }
193
+
194
+ return parsed;
195
+ }
196
+
197
+ function buildHelp() {
198
+ return `Funplay MCP for Godot stdio bridge ${VERSION}
199
+
200
+ Usage:
201
+ funplay-godot-mcp [--url http://127.0.0.1:8765/]
202
+
203
+ Environment:
204
+ FUNPLAY_GODOT_MCP_URL Godot MCP HTTP endpoint
205
+ GODOT_MCP_URL Compatibility endpoint fallback
206
+
207
+ The Godot editor addon must be enabled and its MCP server must be running.
208
+ `;
209
+ }
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "funplay-godot-mcp",
3
+ "version": "0.7.0",
4
+ "description": "stdio bridge for the local Funplay MCP for Godot editor addon.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "mcpName": "io.github.FunplayAI/funplay-godot-mcp",
8
+ "bin": {
9
+ "funplay-godot-mcp": "bin/funplay-godot-mcp.js"
10
+ },
11
+ "files": [
12
+ "bin",
13
+ "README.md",
14
+ "package.json"
15
+ ],
16
+ "engines": {
17
+ "node": ">=18"
18
+ },
19
+ "keywords": [
20
+ "godot",
21
+ "mcp",
22
+ "model-context-protocol",
23
+ "funplay"
24
+ ],
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/FunplayAI/funplay-godot-mcp.git",
28
+ "directory": "stdio-wrapper"
29
+ }
30
+ }