funplay-unreal-mcp 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/README.md ADDED
@@ -0,0 +1,49 @@
1
+ <!-- mcp-name: io.github.FunplayAI/funplay-unreal-mcp -->
2
+
3
+ # funplay-unreal-mcp (stdio bridge)
4
+
5
+ A tiny, dependency-free stdio ⇄ HTTP bridge that connects MCP clients (Claude
6
+ Code, Cursor, VS Code, Codex, …) to the **Funplay MCP server running inside the
7
+ Unreal Editor**. All tools and logic live in the editor plugin; this package
8
+ just forwards JSON-RPC messages.
9
+
10
+ ## Usage
11
+
12
+ ```bash
13
+ funplay-unreal-mcp --url http://127.0.0.1:8765/ --token <token>
14
+ ```
15
+
16
+ or via environment variables:
17
+
18
+ ```bash
19
+ FUNPLAY_UNREAL_MCP_URL=http://127.0.0.1:8765/ \
20
+ FUNPLAY_UNREAL_MCP_TOKEN=<token> \
21
+ funplay-unreal-mcp
22
+ ```
23
+
24
+ Get the endpoint and token from the Unreal editor: **Tools → Funplay MCP → Log
25
+ Endpoint + Token** (printed to the Output Log).
26
+
27
+ ## MCP client config
28
+
29
+ ```json
30
+ {
31
+ "mcpServers": {
32
+ "funplay-unreal": {
33
+ "command": "npx",
34
+ "args": ["-y", "funplay-unreal-mcp"],
35
+ "env": {
36
+ "FUNPLAY_UNREAL_MCP_URL": "http://127.0.0.1:8765/",
37
+ "FUNPLAY_UNREAL_MCP_TOKEN": "<token from the Funplay MCP menu>"
38
+ }
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ The client launches this bridge over **stdio**; the bridge talks **HTTP** to the
45
+ editor. Requires Node.js ≥ 18.
46
+
47
+ ## License
48
+
49
+ MIT — see [LICENSE](https://github.com/FunplayAI/funplay-unreal-mcp/blob/main/LICENSE).
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env node
2
+ // Funplay MCP for Unreal -- thin stdio <-> HTTP bridge.
3
+ //
4
+ // AI clients speak MCP over stdio; this process forwards each JSON-RPC message
5
+ // verbatim to the Unreal editor plugin's local HTTP endpoint and pipes the
6
+ // response back to stdout. All MCP intelligence (tools, profiles, safety) lives
7
+ // in the editor plugin -- this bridge holds none of it. Zero dependencies.
8
+
9
+ import { argv, env, exit, stderr, stdin, stdout } from "node:process";
10
+
11
+ const VERSION = "0.2.0";
12
+ const DEFAULT_URL = "http://127.0.0.1:8765/";
13
+
14
+ function parseArgs(args) {
15
+ const out = { url: null, token: null, help: false, version: false };
16
+ for (let i = 0; i < args.length; i += 1) {
17
+ const arg = args[i];
18
+ if (arg === "--help" || arg === "-h") out.help = true;
19
+ else if (arg === "--version" || arg === "-v") out.version = true;
20
+ else if (arg === "--url") out.url = args[++i];
21
+ else if (arg.startsWith("--url=")) out.url = arg.slice("--url=".length);
22
+ else if (arg === "--token") out.token = args[++i];
23
+ else if (arg.startsWith("--token=")) out.token = arg.slice("--token=".length);
24
+ }
25
+ return out;
26
+ }
27
+
28
+ const opts = parseArgs(argv.slice(2));
29
+
30
+ if (opts.help) {
31
+ stdout.write(
32
+ [
33
+ "funplay-unreal-mcp -- stdio bridge to a local Funplay MCP server for Unreal",
34
+ "",
35
+ "Usage: funplay-unreal-mcp [--url <endpoint>] [--token <token>]",
36
+ "",
37
+ "Environment:",
38
+ " FUNPLAY_UNREAL_MCP_URL HTTP endpoint (default http://127.0.0.1:8765/)",
39
+ " FUNPLAY_UNREAL_MCP_TOKEN Auth token from the Funplay MCP editor menu",
40
+ "",
41
+ ].join("\n"),
42
+ );
43
+ exit(0);
44
+ }
45
+ if (opts.version) {
46
+ stdout.write(VERSION + "\n");
47
+ exit(0);
48
+ }
49
+
50
+ const endpoint =
51
+ opts.url || env.FUNPLAY_UNREAL_MCP_URL || env.UNREAL_MCP_URL || DEFAULT_URL;
52
+ const authToken =
53
+ opts.token || env.FUNPLAY_UNREAL_MCP_TOKEN || env.UNREAL_MCP_TOKEN || "";
54
+
55
+ function isJsonRpcRequest(message) {
56
+ return message && typeof message === "object" &&
57
+ Object.prototype.hasOwnProperty.call(message, "id");
58
+ }
59
+
60
+ function writeMessage(message) {
61
+ if (message === null || message === undefined) return;
62
+ stdout.write(JSON.stringify(message) + "\n");
63
+ }
64
+
65
+ function buildErrorFor(message, code, errMessage) {
66
+ // Only respond to requests (which carry an id); notifications stay silent.
67
+ const list = Array.isArray(message) ? message : [message];
68
+ const responses = list
69
+ .filter(isJsonRpcRequest)
70
+ .map((m) => ({
71
+ jsonrpc: "2.0",
72
+ id: m.id,
73
+ error: { code, message: errMessage },
74
+ }));
75
+ if (!responses.length) return;
76
+ writeMessage(Array.isArray(message) ? responses : responses[0]);
77
+ }
78
+
79
+ async function forwardMessage(message) {
80
+ let response;
81
+ try {
82
+ const headers = {
83
+ accept: "application/json",
84
+ "content-type": "application/json",
85
+ };
86
+ if (authToken) headers["x-funplay-mcp-token"] = authToken;
87
+ response = await fetch(endpoint, {
88
+ method: "POST",
89
+ headers,
90
+ body: JSON.stringify(message),
91
+ });
92
+ } catch (err) {
93
+ buildErrorFor(
94
+ message,
95
+ -32000,
96
+ `Failed to reach Funplay MCP for Unreal at ${endpoint}: ${err.message}. ` +
97
+ "Is the editor open with the MCP server running?",
98
+ );
99
+ return;
100
+ }
101
+
102
+ if (response.status === 204) return; // notification accepted, no body
103
+ const text = await response.text();
104
+ if (!text) {
105
+ buildErrorFor(message, -32603, "Empty response from Unreal MCP server");
106
+ return;
107
+ }
108
+ let parsed;
109
+ try {
110
+ parsed = JSON.parse(text);
111
+ } catch {
112
+ buildErrorFor(message, -32603, "Invalid JSON from Unreal MCP server");
113
+ return;
114
+ }
115
+ writeMessage(parsed);
116
+ }
117
+
118
+ // Serialize message handling so responses keep input order.
119
+ let queue = Promise.resolve();
120
+ function enqueue(line) {
121
+ const trimmed = line.trim();
122
+ if (!trimmed) return;
123
+ let message;
124
+ try {
125
+ message = JSON.parse(trimmed);
126
+ } catch {
127
+ writeMessage({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error" } });
128
+ return;
129
+ }
130
+ queue = queue.then(() => forwardMessage(message)).catch((err) => {
131
+ stderr.write(`[funplay-unreal-mcp] ${err && err.stack ? err.stack : err}\n`);
132
+ });
133
+ }
134
+
135
+ let buffer = "";
136
+ stdin.setEncoding("utf8");
137
+ stdin.on("data", (chunk) => {
138
+ buffer += chunk;
139
+ let index = buffer.indexOf("\n");
140
+ while (index !== -1) {
141
+ const line = buffer.slice(0, index);
142
+ buffer = buffer.slice(index + 1);
143
+ enqueue(line);
144
+ index = buffer.indexOf("\n");
145
+ }
146
+ });
147
+ stdin.on("end", () => {
148
+ if (buffer.trim()) enqueue(buffer);
149
+ });
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "funplay-unreal-mcp",
3
+ "version": "0.2.0",
4
+ "description": "stdio bridge for the local Unreal Editor MCP server from FunplayAI/funplay-unreal-mcp.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "mcpName": "io.github.FunplayAI/funplay-unreal-mcp",
8
+ "bin": {
9
+ "funplay-unreal-mcp": "bin/funplay-unreal-mcp.js"
10
+ },
11
+ "files": [
12
+ "bin",
13
+ "README.md",
14
+ "package.json"
15
+ ],
16
+ "engines": {
17
+ "node": ">=18"
18
+ },
19
+ "keywords": [
20
+ "unreal",
21
+ "unreal-engine",
22
+ "mcp",
23
+ "model-context-protocol",
24
+ "funplay",
25
+ "gamedev"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/FunplayAI/funplay-unreal-mcp.git",
30
+ "directory": "stdio-wrapper"
31
+ },
32
+ "homepage": "https://github.com/FunplayAI/funplay-unreal-mcp",
33
+ "bugs": {
34
+ "url": "https://github.com/FunplayAI/funplay-unreal-mcp/issues"
35
+ }
36
+ }