@jamius19/unity-ai-context-bridge-mcp 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/LICENSE.md +7 -0
- package/README.md +336 -0
- package/index.js +321 -0
- package/package.json +34 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2026 Jamius Siam
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
## Unity AI Context Bridge MCP
|
|
2
|
+
|
|
3
|
+
Node MCP server that feeds AI tools Unity GameObjects, assets, and other resources that are selected by the user. Think of it like when you drag source code in Cursor, Antigravity, Github Copilot window to add that file to context, but for Unity.
|
|
4
|
+
|
|
5
|
+
> [!IMPORTANT]
|
|
6
|
+
> The MCP server requires the `Unity AI Context Bridge` Unity package to be installed in the Unity project.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
> [!TIP]
|
|
10
|
+
> For the best experience, use Unity Official MCP server along with this.
|
|
11
|
+
|
|
12
|
+
<br>
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
Install the package globally:
|
|
17
|
+
|
|
18
|
+
```shell
|
|
19
|
+
npm install -g @jamius19/unity-ai-context-bridge-mcp
|
|
20
|
+
```
|
|
21
|
+
<br>
|
|
22
|
+
|
|
23
|
+
## MCP Client Installation
|
|
24
|
+
|
|
25
|
+
Install the NPM package globally before wiring an MCP client to it. This will enable the `uacb-mcp` command in your terminal,
|
|
26
|
+
|
|
27
|
+
Choose one transport per client entry:
|
|
28
|
+
|
|
29
|
+
1. Stdio: the MCP client launches this server as a subprocess. **(Recommended)**
|
|
30
|
+
2. Streamable HTTP: you start this server with `uacb-mcp --http`, then the MCP client connects to `http://127.0.0.1:3333/mcp`.
|
|
31
|
+
|
|
32
|
+
<br>
|
|
33
|
+
|
|
34
|
+
### GitHub Copilot (VS Code)
|
|
35
|
+
|
|
36
|
+
Create or update `.vscode/mcp.json` in a project, or run `MCP: Open User Configuration` from the Command Palette for a user-wide setup.
|
|
37
|
+
|
|
38
|
+
Stdio:
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"servers": {
|
|
43
|
+
"unity-ai-context-bridge": {
|
|
44
|
+
"type": "stdio",
|
|
45
|
+
"command": "uacb-mcp",
|
|
46
|
+
"args": ["--stdio"]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
HTTP with auth:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"servers": {
|
|
57
|
+
"unity-ai-context-bridge": {
|
|
58
|
+
"type": "http",
|
|
59
|
+
"url": "http://127.0.0.1:3333/mcp",
|
|
60
|
+
"headers": {
|
|
61
|
+
"Authorization": "Bearer change-me"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
HTTP without auth:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"servers": {
|
|
73
|
+
"unity-ai-context-bridge": {
|
|
74
|
+
"type": "http",
|
|
75
|
+
"url": "http://127.0.0.1:3333/mcp"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
After adding the server, run `MCP: List Servers` from the Command Palette and start `unity-ai-context-bridge` if VS Code does not start it automatically. Then open GitHub Copilot Chat in Agent mode and enable the server tools from the tools menu if needed.
|
|
82
|
+
|
|
83
|
+
<br>
|
|
84
|
+
|
|
85
|
+
### Rider/Other `mcp.json` clients
|
|
86
|
+
|
|
87
|
+
Many MCP clients, including Claude Desktop-style JSON configs, accept the same `mcpServers` shape.
|
|
88
|
+
|
|
89
|
+
Stdio:
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"mcpServers": {
|
|
94
|
+
"unity-ai-context-bridge": {
|
|
95
|
+
"command": "uacb-mcp",
|
|
96
|
+
"args": ["--stdio"]
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
HTTP:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"mcpServers": {
|
|
107
|
+
"unity-ai-context-bridge": {
|
|
108
|
+
"url": "http://127.0.0.1:3333/mcp",
|
|
109
|
+
"headers": {
|
|
110
|
+
"Authorization": "Bearer change-me"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
<br>
|
|
118
|
+
|
|
119
|
+
### Claude Code
|
|
120
|
+
|
|
121
|
+
Stdio:
|
|
122
|
+
|
|
123
|
+
```shell
|
|
124
|
+
claude mcp add --transport stdio unity-ai-context-bridge -- uacb-mcp --stdio
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
HTTP with auth:
|
|
128
|
+
|
|
129
|
+
```shell
|
|
130
|
+
claude mcp add --transport http --header "Authorization: Bearer change-me" unity-ai-context-bridge http://127.0.0.1:3333/mcp
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
HTTP without auth:
|
|
134
|
+
|
|
135
|
+
```shell
|
|
136
|
+
claude mcp add --transport http unity-ai-context-bridge http://127.0.0.1:3333/mcp
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
<br>
|
|
140
|
+
|
|
141
|
+
### Codex
|
|
142
|
+
|
|
143
|
+
Add one of these entries to `~/.codex/config.toml`.
|
|
144
|
+
|
|
145
|
+
Stdio:
|
|
146
|
+
|
|
147
|
+
```toml
|
|
148
|
+
[mcp_servers.unity_ai_context_bridge]
|
|
149
|
+
command = "uacb-mcp"
|
|
150
|
+
args = ["--stdio"]
|
|
151
|
+
enabled = true
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
HTTP with auth:
|
|
155
|
+
|
|
156
|
+
```toml
|
|
157
|
+
[mcp_servers.unity_ai_context_bridge]
|
|
158
|
+
url = "http://127.0.0.1:3333/mcp"
|
|
159
|
+
http_headers = { Authorization = "Bearer change-me" }
|
|
160
|
+
enabled = true
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
HTTP without auth:
|
|
164
|
+
|
|
165
|
+
```toml
|
|
166
|
+
[mcp_servers.unity_ai_context_bridge]
|
|
167
|
+
url = "http://127.0.0.1:3333/mcp"
|
|
168
|
+
enabled = true
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
<br>
|
|
172
|
+
|
|
173
|
+
### Gemini CLI
|
|
174
|
+
|
|
175
|
+
Stdio:
|
|
176
|
+
|
|
177
|
+
```shell
|
|
178
|
+
gemini mcp add unity-ai-context-bridge uacb-mcp --stdio
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
HTTP with auth:
|
|
182
|
+
|
|
183
|
+
```shell
|
|
184
|
+
gemini mcp add --transport http unity-ai-context-bridge http://127.0.0.1:3333/mcp --header "Authorization: Bearer change-me"
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
HTTP without auth:
|
|
188
|
+
|
|
189
|
+
```shell
|
|
190
|
+
gemini mcp add --transport http unity-ai-context-bridge http://127.0.0.1:3333/mcp
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
<br>
|
|
194
|
+
|
|
195
|
+
### Cursor
|
|
196
|
+
|
|
197
|
+
Create or update `.cursor/mcp.json` in a project, or `~/.cursor/mcp.json` for a user-wide setup.
|
|
198
|
+
|
|
199
|
+
Stdio:
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"mcpServers": {
|
|
204
|
+
"unity-ai-context-bridge": {
|
|
205
|
+
"type": "stdio",
|
|
206
|
+
"command": "uacb-mcp",
|
|
207
|
+
"args": ["--stdio"]
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
HTTP with auth:
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"mcpServers": {
|
|
218
|
+
"unity-ai-context-bridge": {
|
|
219
|
+
"url": "http://127.0.0.1:3333/mcp",
|
|
220
|
+
"headers": {
|
|
221
|
+
"Authorization": "Bearer change-me"
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
HTTP without auth:
|
|
229
|
+
|
|
230
|
+
```json
|
|
231
|
+
{
|
|
232
|
+
"mcpServers": {
|
|
233
|
+
"unity-ai-context-bridge": {
|
|
234
|
+
"url": "http://127.0.0.1:3333/mcp"
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
<br>
|
|
241
|
+
|
|
242
|
+
### Run over stdio
|
|
243
|
+
|
|
244
|
+
```shell
|
|
245
|
+
uacb-mcp --stdio
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Use this transport from MCP clients that launch servers as subprocesses.
|
|
249
|
+
|
|
250
|
+
<br>
|
|
251
|
+
|
|
252
|
+
### Run over HTTP
|
|
253
|
+
|
|
254
|
+
In HTTP mode it defaults to `http://127.0.0.1:3333/mcp` URL.\
|
|
255
|
+
You can customize it via the following ways,
|
|
256
|
+
|
|
257
|
+
PowerShell:
|
|
258
|
+
|
|
259
|
+
```powershell
|
|
260
|
+
$env:MCP_AUTH_TOKEN = "change-me"
|
|
261
|
+
$env:PORT = "3333"
|
|
262
|
+
uacb-mcp --http
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Bash:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
MCP_AUTH_TOKEN=change-me PORT=3333 uacb-mcp --http
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
If you do not want HTTP auth during local development, omit `MCP_AUTH_TOKEN`:
|
|
272
|
+
|
|
273
|
+
```shell
|
|
274
|
+
uacb-mcp --http
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
HTTP endpoints:
|
|
278
|
+
|
|
279
|
+
- `GET /health` does not require auth.
|
|
280
|
+
- `GET /context` returns the raw currently selected Unity objects/assets context JSON. It requires a `projectPath` query parameter to select a specific Unity project bridge.
|
|
281
|
+
- `POST /mcp` is the SDK Streamable HTTP MCP endpoint for AI tools.
|
|
282
|
+
|
|
283
|
+
Authenticated HTTP requests must include either:
|
|
284
|
+
|
|
285
|
+
```text
|
|
286
|
+
Authorization: Bearer change-me
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
or:
|
|
290
|
+
|
|
291
|
+
```text
|
|
292
|
+
x-auth-token: change-me
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
<br>
|
|
296
|
+
|
|
297
|
+
## MCP surface
|
|
298
|
+
|
|
299
|
+
Resource template:
|
|
300
|
+
|
|
301
|
+
- `unity-context://projects/items{?projectPath}`: currently selected Unity GameObjects/assets of interest for a specific project path. Listed concrete resources percent-encode the project path in the query string, so paths with spaces are represented safely, for example `unity-context://projects/items?projectPath=C%3A%5Cexample%5Cunity%5Cunity%5Cproject` for an Unity project in the `C:\example\unity\unity\project` path.
|
|
302
|
+
|
|
303
|
+
Tool:
|
|
304
|
+
|
|
305
|
+
- `get_unity_context_items`: returns the current Unity selection context for AI tools. It requires a URL-encoded `projectPath`; the server reads that project's bridge file and uses its URL.
|
|
306
|
+
|
|
307
|
+
Example JSON-RPC request:
|
|
308
|
+
|
|
309
|
+
```json
|
|
310
|
+
{
|
|
311
|
+
"jsonrpc": "2.0",
|
|
312
|
+
"id": 1,
|
|
313
|
+
"method": "tools/call",
|
|
314
|
+
"params": {
|
|
315
|
+
"name": "get_unity_context_items",
|
|
316
|
+
"arguments": {
|
|
317
|
+
"projectPath": "C%3A%5Cexample%5Cunity%5Cunity%5Cproject"
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
If you have Unity Official MCP installed, this will be done automatically, otheriwse you can update your `AGENTS.md` file with the full project path.
|
|
324
|
+
|
|
325
|
+
<br>
|
|
326
|
+
|
|
327
|
+
## How does it work?
|
|
328
|
+
It automatically discovers the live Unity selection context from the bridge file under the requested Unity project path:
|
|
329
|
+
|
|
330
|
+
```text
|
|
331
|
+
<projectPath>/Temp/unity-ai-context-bridge/bridge.json
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
The bridge file contains the local URL and auth key for that Unity editor instance. The MCP server reads that file for the requested `projectPath`, connects to its `url`, and sends the auth key to Unity AI Context Bridge.
|
|
335
|
+
|
|
336
|
+
This bridge file is auto generated by the `Unity AI Context Bridge` Unity Editor Extension.
|
package/index.js
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Copyright (c) 2026 Jamius Siam
|
|
4
|
+
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const http = require('node:http');
|
|
8
|
+
const https = require('node:https');
|
|
9
|
+
const fs = require('node:fs/promises');
|
|
10
|
+
const path = require('node:path');
|
|
11
|
+
const { z } = require('zod');
|
|
12
|
+
const { McpServer, ResourceTemplate } = require('@modelcontextprotocol/sdk/server/mcp.js');
|
|
13
|
+
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
14
|
+
const { StreamableHTTPServerTransport } = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
|
|
15
|
+
const { createMcpExpressApp } = require('@modelcontextprotocol/sdk/server/express.js');
|
|
16
|
+
|
|
17
|
+
const SERVER_INFO = {
|
|
18
|
+
name: 'unity-ai-context-bridge-mcp',
|
|
19
|
+
title: 'Unity AI Context Bridge MCP',
|
|
20
|
+
version: '1.0.0',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const RESOURCE_URI_TEMPLATE = 'unity-context://projects/items{?projectPath}';
|
|
24
|
+
const DEFAULT_UPSTREAM_TIMEOUT_MS = 5000;
|
|
25
|
+
|
|
26
|
+
function jsonText(value) {
|
|
27
|
+
return JSON.stringify(value, null, 2);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getUnityContextResourceUri(projectPath) {
|
|
31
|
+
return `unity-context://projects/items?projectPath=${encodeURIComponent(projectPath)}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function decodeProjectPath(projectPath) {
|
|
35
|
+
if (typeof projectPath !== 'string' || projectPath.length === 0) {
|
|
36
|
+
throw new Error('projectPath is required');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
return decodeURIComponent(projectPath);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
throw new Error(`projectPath must be URL encoded: ${err.message}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getBridgeInfoPath(projectPath) {
|
|
47
|
+
return path.join(projectPath, 'Temp', 'unity-ai-context-bridge', 'bridge.json');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function readBridgeInfo(projectPath) {
|
|
51
|
+
const bridgePath = getBridgeInfoPath(projectPath);
|
|
52
|
+
let bridge;
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
bridge = JSON.parse(await fs.readFile(bridgePath, 'utf8'));
|
|
56
|
+
} catch (err) {
|
|
57
|
+
throw new Error(`Failed to read Unity context bridge at ${bridgePath}: ${err.message}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!bridge || typeof bridge !== 'object' || Array.isArray(bridge)) {
|
|
61
|
+
throw new Error(`Unity context bridge at ${bridgePath} must be a JSON object`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (typeof bridge.url !== 'string' || bridge.url.length === 0) {
|
|
65
|
+
throw new Error(`Unity context bridge at ${bridgePath} must include url`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return bridge;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function validateUnityContext(payload) {
|
|
72
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
73
|
+
throw new Error('Unity context response must be a JSON object');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!Array.isArray(payload.items)) {
|
|
77
|
+
throw new Error('Unity context response must include an items array');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return payload;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function fetchJson(url, authKey, timeoutMs = DEFAULT_UPSTREAM_TIMEOUT_MS) {
|
|
84
|
+
return new Promise((resolve, reject) => {
|
|
85
|
+
const parsed = new URL(url);
|
|
86
|
+
const transport = parsed.protocol === 'https:' ? https : http;
|
|
87
|
+
const headers = { accept: 'application/json' };
|
|
88
|
+
if (authKey) {
|
|
89
|
+
headers.authorization = `Bearer ${authKey}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const req = transport.get(parsed, { headers }, (res) => {
|
|
93
|
+
const chunks = [];
|
|
94
|
+
let size = 0;
|
|
95
|
+
|
|
96
|
+
res.on('data', (chunk) => {
|
|
97
|
+
size += chunk.length;
|
|
98
|
+
if (size > 1024 * 1024) {
|
|
99
|
+
reject(new Error('Unity AI Context Bridge Editor Extension endpoint returned a response that is too large'));
|
|
100
|
+
req.destroy();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
chunks.push(chunk);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
res.on('end', () => {
|
|
107
|
+
const body = Buffer.concat(chunks).toString('utf8');
|
|
108
|
+
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
109
|
+
reject(new Error(`Unity AI Context Bridge Editor Extension endpoint returned HTTP ${res.statusCode}: ${body}`));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
resolve(JSON.parse(body));
|
|
115
|
+
} catch (err) {
|
|
116
|
+
reject(new Error(`Unity AI Context Bridge Editor Extension endpoint returned invalid JSON: ${err.message}`));
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
req.setTimeout(timeoutMs, () => {
|
|
122
|
+
req.destroy(new Error(`Unity AI Context Bridge Editor Extension endpoint timed out after ${timeoutMs}ms`));
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
req.on('error', reject);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function getUnityContext(projectPath) {
|
|
130
|
+
const bridge = await readBridgeInfo(decodeProjectPath(projectPath));
|
|
131
|
+
return validateUnityContext(await fetchJson(bridge.url, bridge.authToken));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function createUnityMcpServer() {
|
|
135
|
+
const server = new McpServer(SERVER_INFO);
|
|
136
|
+
|
|
137
|
+
server.registerResource(
|
|
138
|
+
'unity-context-items',
|
|
139
|
+
new ResourceTemplate(RESOURCE_URI_TEMPLATE, { list: undefined }),
|
|
140
|
+
{
|
|
141
|
+
title: 'Unity Context Items',
|
|
142
|
+
description: 'Returns information about the Unity GameObjects, assets, and other editor items user selected in the Unity editor that are relevant to the active AI task.',
|
|
143
|
+
mimeType: 'application/json',
|
|
144
|
+
},
|
|
145
|
+
async (uri, variables) => {
|
|
146
|
+
const projectPath = decodeProjectPath(variables.projectPath);
|
|
147
|
+
const unityContext = await getUnityContext(variables.projectPath);
|
|
148
|
+
return {
|
|
149
|
+
contents: [
|
|
150
|
+
{
|
|
151
|
+
uri: getUnityContextResourceUri(projectPath),
|
|
152
|
+
mimeType: 'application/json',
|
|
153
|
+
text: jsonText(unityContext),
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
};
|
|
157
|
+
},
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
server.registerTool(
|
|
161
|
+
'get_unity_context_items',
|
|
162
|
+
{
|
|
163
|
+
title: 'Get Unity Context Items',
|
|
164
|
+
description: 'Returns information about the Unity GameObjects, assets, and other editor items user selected in the Unity editor that are relevant to the active AI task.',
|
|
165
|
+
inputSchema: {
|
|
166
|
+
projectPath: z.string().describe('URL-encoded Unity project full path.'),
|
|
167
|
+
},
|
|
168
|
+
annotations: {
|
|
169
|
+
readOnlyHint: true,
|
|
170
|
+
openWorldHint: false,
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
async ({ projectPath }) => {
|
|
174
|
+
const unityContext = await getUnityContext(projectPath);
|
|
175
|
+
return {
|
|
176
|
+
content: [
|
|
177
|
+
{
|
|
178
|
+
type: 'text',
|
|
179
|
+
text: jsonText(unityContext),
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
structuredContent: unityContext,
|
|
183
|
+
};
|
|
184
|
+
},
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
return server;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function isAuthorized(req, authToken) {
|
|
191
|
+
if (!authToken) {
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const bearer = req.headers.authorization;
|
|
196
|
+
const token = req.headers['x-auth-token'];
|
|
197
|
+
return bearer === `Bearer ${authToken}` || token === authToken;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function sendJson(res, statusCode, value) {
|
|
201
|
+
const body = JSON.stringify(value, null, 2);
|
|
202
|
+
res.writeHead(statusCode, {
|
|
203
|
+
'content-type': 'application/json; charset=utf-8',
|
|
204
|
+
'content-length': Buffer.byteLength(body, 'utf8'),
|
|
205
|
+
});
|
|
206
|
+
res.end(body);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function authMiddleware(authToken) {
|
|
210
|
+
return (req, res, next) => {
|
|
211
|
+
if (isAuthorized(req, authToken)) {
|
|
212
|
+
next();
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
res.status(401).json({
|
|
217
|
+
error: 'Unauthorized',
|
|
218
|
+
message: 'Send Authorization: Bearer <token> or x-auth-token: <token>.',
|
|
219
|
+
});
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function startStdioServer() {
|
|
224
|
+
const server = createUnityMcpServer();
|
|
225
|
+
const transport = new StdioServerTransport();
|
|
226
|
+
await server.connect(transport);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async function startHttpServer() {
|
|
230
|
+
const host = process.env.HOST || '127.0.0.1';
|
|
231
|
+
const port = Number(process.env.PORT || process.env.MCP_PORT || 3333);
|
|
232
|
+
const authToken = process.env.MCP_AUTH_TOKEN || process.env.AUTH_TOKEN || '';
|
|
233
|
+
const app = createMcpExpressApp();
|
|
234
|
+
|
|
235
|
+
app.get('/health', (req, res) => {
|
|
236
|
+
res.json({ ok: true, serverInfo: SERVER_INFO });
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
app.get('/context', authMiddleware(authToken), async (req, res) => {
|
|
240
|
+
try {
|
|
241
|
+
res.json(await getUnityContext(req.query.projectPath));
|
|
242
|
+
} catch (err) {
|
|
243
|
+
res.status(502).json({
|
|
244
|
+
error: 'Bad Gateway',
|
|
245
|
+
message: err.message,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
app.post('/mcp', authMiddleware(authToken), async (req, res) => {
|
|
251
|
+
const server = createUnityMcpServer();
|
|
252
|
+
const transport = new StreamableHTTPServerTransport({
|
|
253
|
+
sessionIdGenerator: undefined,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
await server.connect(transport);
|
|
258
|
+
await transport.handleRequest(req, res, req.body);
|
|
259
|
+
res.on('close', () => {
|
|
260
|
+
transport.close().catch((err) => console.error('Error closing MCP HTTP transport:', err));
|
|
261
|
+
server.close().catch((err) => console.error('Error closing MCP HTTP server:', err));
|
|
262
|
+
});
|
|
263
|
+
} catch (err) {
|
|
264
|
+
console.error('Error handling MCP HTTP request:', err);
|
|
265
|
+
if (!res.headersSent) {
|
|
266
|
+
res.status(500).json({
|
|
267
|
+
jsonrpc: '2.0',
|
|
268
|
+
error: {
|
|
269
|
+
code: -32603,
|
|
270
|
+
message: 'Internal server error',
|
|
271
|
+
},
|
|
272
|
+
id: null,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
app.get('/mcp', authMiddleware(authToken), (req, res) => {
|
|
279
|
+
sendJson(res, 405, {
|
|
280
|
+
jsonrpc: '2.0',
|
|
281
|
+
error: {
|
|
282
|
+
code: -32000,
|
|
283
|
+
message: 'Method not allowed.',
|
|
284
|
+
},
|
|
285
|
+
id: null,
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
app.delete('/mcp', authMiddleware(authToken), (req, res) => {
|
|
290
|
+
sendJson(res, 405, {
|
|
291
|
+
jsonrpc: '2.0',
|
|
292
|
+
error: {
|
|
293
|
+
code: -32000,
|
|
294
|
+
message: 'Method not allowed.',
|
|
295
|
+
},
|
|
296
|
+
id: null,
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
await new Promise((resolve, reject) => {
|
|
301
|
+
const listener = app.listen(port, host, () => {
|
|
302
|
+
console.error(`${SERVER_INFO.name} Streamable HTTP server listening on http://${host}:${port}/mcp`);
|
|
303
|
+
if (!authToken) {
|
|
304
|
+
console.error('MCP_AUTH_TOKEN is not set; HTTP auth is disabled.');
|
|
305
|
+
}
|
|
306
|
+
resolve(listener);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
listener.on('error', reject);
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (require.main === module) {
|
|
314
|
+
const args = new Set(process.argv.slice(2));
|
|
315
|
+
const start = args.has('--http') ? startHttpServer : startStdioServer;
|
|
316
|
+
|
|
317
|
+
start().catch((err) => {
|
|
318
|
+
console.error(err);
|
|
319
|
+
process.exit(1);
|
|
320
|
+
});
|
|
321
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jamius19/unity-ai-context-bridge-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server that feeds AI tools the currently selected Unity GameObjects and assets of interest. Requires the \"Unity AI Context Bridge\" Unity package to be installed in the Unity project.",
|
|
5
|
+
"homepage": "https://github.com/jamius19/unity-ai-context-bridge-mcp",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/jamius19/unity-ai-context-bridge-mcp.git"
|
|
9
|
+
},
|
|
10
|
+
"main": "index.js",
|
|
11
|
+
"type": "commonjs",
|
|
12
|
+
"bin": {
|
|
13
|
+
"uacb-mcp": "index.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"index.js",
|
|
17
|
+
"README.md",
|
|
18
|
+
"package.json"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"start": "node index.js",
|
|
22
|
+
"start:stdio": "node index.js --stdio",
|
|
23
|
+
"start:http": "node index.js --http"
|
|
24
|
+
},
|
|
25
|
+
"author": "Jamius Siam",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
32
|
+
"zod": "^4.4.3"
|
|
33
|
+
}
|
|
34
|
+
}
|