dsh-unity-mcp 0.0.1 → 0.0.2

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 CHANGED
@@ -34,9 +34,17 @@
34
34
 
35
35
  配置为空时插件仅提供加载入口,并提示如何连接。它不会启动 Unity / Tuanjie 编辑器。连接配置完成后,DSH 启动上游 Node stdio 桥接进程,由桥接进程连接引擎端服务。
36
36
 
37
+ ## 通过对话配置工程(0.0.2 起)
38
+
39
+ 可以直接告诉 Agent:“把 Unity MCP 连接到 `C:/Projects/MyUnityProject`。”Agent 先调用 `unity_load_tools`,再调用 `unity_configure_project`,传入工程绝对路径 `projectPath`;只有自动发现失败时才需要补充 `serverPath`。未连接引擎时也能加载配置工具。
40
+
41
+ 工具检查工程的 `Assets`、`ProjectSettings` 和已构建的 CoderGamester 服务器入口,再通过 DSH 官方配置服务保存并重载插件。**保存影响当前 profile 的所有会话;会清空旧的 host、port、authTokenPath 覆盖项,改用新工程设置。** 其他配置保留。路径校验失败不会保存;并发修改连接字段时会要求重试。保存成功仅表示配置已应用,仍需重新加载工具并做只读场景查询,确认引擎在线。
42
+
43
+ 工具不会安装引擎包、构建服务器或启动编辑器。也可以继续在插件配置中手工设置路径。
44
+
37
45
  ## 工具懒加载
38
46
 
39
- 每个 Agent 初始只看到 `unity_load_tools`。需要编辑器能力时,调用一次即可把当前服务器提供的 `mcp__unity__*` 工具加载到该 Agent;其他 Agent 仍保持未加载。场景、GameObject、组件、材质、资源、控制台和测试等工具的参数与结果沿用上游,插件不维护另一套工具清单。
47
+ 每个 Agent 初始只看到 `unity_load_tools`。调用后先获得 `unity_configure_project`,并加载当前服务器提供的 `mcp__unity__*` 工具;未连接时只有配置工具。其他 Agent 仍保持未加载。场景、GameObject、组件、材质、资源、控制台和测试等工具的参数与结果沿用上游,插件不维护另一套工具清单。
40
48
 
41
49
  加载动作不会修改工程。**加载后的编辑器操作可能创建、删除资源,修改场景、执行菜单或安装包。请先提交或备份工程,并检查每次调用的范围。** 如需立即暴露全部工具,把 `lazyTools` 设为 `false`。实际工具数量随上游版本变化。
42
50
 
package/index.mjs CHANGED
@@ -2,8 +2,9 @@ import z from '@deepseek-ai/schemastery';
2
2
  import * as mcp from '@deepseek-ai/dsh-mcp-client';
3
3
  import { connectionConfig } from './lib/connection.mjs';
4
4
  import { installLazyTools } from './lib/lazy-tools.mjs';
5
+ import { configurationTool } from './lib/configure-project.mjs';
5
6
  export const name = 'dsh-unity-mcp';
6
- export const inject = ['tools', 'agents'];
7
+ export const inject = ['tools', 'agents', 'configEditor'];
7
8
  export const Config = z.object({
8
9
  projectPath: z.string().default('').description('Unity / Tuanjie 项目绝对路径 / Absolute project directory'),
9
10
  serverPath: z.string().default('').description('CoderGamester Server~/build/index.js 绝对路径;留空从项目发现 / Built server path; blank discovers in project'),
@@ -15,7 +16,7 @@ export const Config = z.object({
15
16
  });
16
17
  export async function apply(ctx, config) {
17
18
  // Install the loader even before configuration, so discovery explains setup.
18
- installLazyTools(ctx, config.lazyTools);
19
+ installLazyTools(ctx, config.lazyTools, [configurationTool(ctx)]);
19
20
  if (!config.projectPath && !config.serverPath) {
20
21
  ctx.logger.info('Unity MCP: configure projectPath or serverPath to connect CoderGamester/mcp-unity.');
21
22
  return;
@@ -0,0 +1,36 @@
1
+ import path from 'node:path';
2
+ import { stat } from 'node:fs/promises';
3
+ import { connectionConfig } from './connection.mjs';
4
+
5
+ export function configurationTool(ctx) {
6
+ return {
7
+ name: 'unity_configure_project',
8
+ description: 'Save the Unity/Tuanjie project connection for this DSH profile and reconnect. Affects all sessions and clears host/port/auth-file overrides to use the selected project settings. Validates local paths before saving; does not start or modify the engine project. Requires CoderGamester/mcp-unity in the engine.',
9
+ parameters: { type: 'object', properties: {
10
+ projectPath: { type: 'string', description: 'Absolute Unity/Tuanjie project directory containing Assets and ProjectSettings.' },
11
+ serverPath: { type: 'string', description: 'Optional absolute built CoderGamester Server~/build/index.js. Omit or empty to auto-discover in the selected project.' }
12
+ }, required: ['projectPath'], additionalProperties: false },
13
+ output: { schema: { type: 'object', properties: { projectPath: { type: 'string' }, serverPath: { type: 'string' }, saved: { type: 'boolean' }, message: { type: 'string' } }, required: ['projectPath', 'serverPath', 'saved', 'message'], additionalProperties: false }, render: (_, value) => [{ type: 'text', text: JSON.stringify(value) }] },
14
+ execute: async ({ projectPath, serverPath = '' }) => {
15
+ if (typeof projectPath !== 'string' || !path.isAbsolute(projectPath)) throw Error('projectPath must be an absolute Unity/Tuanjie project path.');
16
+ for (const directory of ['Assets', 'ProjectSettings']) {
17
+ if (!(await stat(path.join(projectPath, directory))).isDirectory()) throw Error('Not a Unity/Tuanjie project: missing ' + directory);
18
+ }
19
+ const entries = ctx.configEditor.entries().filter(entry => entry.options.name === 'dsh-unity-mcp');
20
+ if (entries.length !== 1) throw Error('Expected exactly one dsh-unity-mcp configuration entry; resolve duplicate/missing plugin entries first.');
21
+ const entry = entries[0];
22
+ const snapshot = ctx.configEditor.configuration().find(row => row.entry === entry);
23
+ if (!snapshot) throw Error('Unity configuration changed; retry.');
24
+ const patch = { projectPath, serverPath, host: '', port: 0, authTokenPath: '' };
25
+ const current = { ...snapshot.inherited, ...snapshot.override };
26
+ await connectionConfig({ ...current, ...patch });
27
+ await ctx.configEditor.edit(entry, raw => {
28
+ for (const key of ['projectPath', 'serverPath', 'host', 'port', 'authTokenPath']) {
29
+ if (JSON.stringify(raw[key]) !== JSON.stringify(current[key])) throw Error('Unity connection configuration changed during validation; retry.');
30
+ }
31
+ return { ...raw, ...patch };
32
+ });
33
+ return { projectPath, serverPath, saved: true, message: 'Configuration saved and Loader reconciliation completed. Host/port/auth overrides cleared to use this project settings. Load Unity tools again; tool discovery alone does not prove the editor is online.' };
34
+ }
35
+ };
36
+ }
@@ -1,8 +1,9 @@
1
1
  const PREFIX = 'mcp__unity__';
2
2
  const empty = { type: 'object', properties: {}, additionalProperties: false };
3
- export function installLazyTools(ctx, lazy = true) {
3
+ export function installLazyTools(ctx, lazy = true, setupTools = []) {
4
4
  const states = new Map();
5
- const definitions = () => ctx.tools.schemas().filter(tool => tool.name.startsWith(PREFIX)).map(tool => ctx.tools.get(tool.name)).filter(Boolean);
5
+ const definitions = () => [...setupTools, ...ctx.tools.schemas().filter(tool => tool.name.startsWith(PREFIX)).map(tool => ctx.tools.get(tool.name)).filter(Boolean)];
6
+ if (!lazy) for (const tool of setupTools) ctx.tools.register(tool);
6
7
  function attach(agent) {
7
8
  if (states.has(agent)) return;
8
9
  const state = { loaded: !lazy, signature: '', disposers: [], tools: [] };
@@ -11,7 +12,8 @@ export function installLazyTools(ctx, lazy = true) {
11
12
  if (signature === state.signature) return;
12
13
  for (const dispose of state.tools.splice(0).reverse()) dispose();
13
14
  if (state.mask) { state.mask(); state.mask = undefined; }
14
- if (lazy && defs.length) state.mask = agent.ctx.tools.restrict({ deny: defs.map(tool => tool.name) });
15
+ const engineNames = defs.filter(tool => tool.name.startsWith(PREFIX)).map(tool => tool.name);
16
+ if (lazy && engineNames.length) state.mask = agent.ctx.tools.restrict({ deny: engineNames });
15
17
  if (state.loaded && lazy) {
16
18
  const installed = [];
17
19
  try { for (const definition of defs) installed.push(agent.ctx.tools.register({ ...definition })); }
@@ -30,7 +32,7 @@ export function installLazyTools(ctx, lazy = true) {
30
32
  states.set(agent, state);
31
33
  if (lazy) state.disposers.push(agent.ctx.tools.register({
32
34
  name: 'unity_load_tools',
33
- description: 'Load Unity and Tuanjie editor tools for this agent: scenes, GameObjects, components, assets, materials, tests and console. Requires CoderGamester/mcp-unity installed and running in the engine. Loading does not modify the project; subsequent editor actions may.',
35
+ description: 'Load Unity/Tuanjie tools for this agent, including project connection setup even when disconnected. Editor tools cover scenes, GameObjects, assets and console; they require CoderGamester/mcp-unity in the engine. Loading itself changes no configuration or project.',
34
36
  parameters: empty,
35
37
  output: { schema: { type: 'object', properties: { tools: { type: 'array', items: { type: 'string' } } }, required: ['tools'], additionalProperties: false }, render: (_, value) => [{ type: 'text', text: JSON.stringify(value) }] },
36
38
  execute: async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-unity-mcp",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Unity and Tuanjie editor tools for DSH, powered by CoderGamester/mcp-unity with per-agent lazy loading.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -38,7 +38,7 @@
38
38
  }
39
39
  },
40
40
  "scripts": {
41
- "check": "node --check index.mjs && node --check lib/connection.mjs && node --check lib/lazy-tools.mjs",
41
+ "check": "node --check index.mjs && node --check lib/connection.mjs && node --check lib/lazy-tools.mjs && node --check lib/configure-project.mjs",
42
42
  "test": "node --test test/*.test.mjs",
43
43
  "build": "npm run check",
44
44
  "pack:check": "npm pack --dry-run --json"
@@ -53,6 +53,7 @@
53
53
  },
54
54
  "devDependencies": {
55
55
  "@deepseek-ai/cordis": "^4.0.5",
56
+ "@deepseek-ai/dsh-config-editor": "^0.2.1-rc.3",
56
57
  "@deepseek-ai/dsh-scope": "^0.2.1-rc.3",
57
58
  "@deepseek-ai/dsh-tools": "^0.2.1-rc.3"
58
59
  }