ultimate-unreal-engine-mcp 0.1.6 → 0.1.7

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.
@@ -55,6 +55,15 @@ export class PluginBridgeClient {
55
55
  * Entries are added before socket.write() and removed on response or timeout.
56
56
  */
57
57
  pendingCommands = new Map();
58
+ /** Shared singleton instance — all tool modules use the same TCP connection. */
59
+ static _instance = null;
60
+ /** Get the shared singleton bridge client. Creates one on first call. */
61
+ static shared() {
62
+ if (!PluginBridgeClient._instance) {
63
+ PluginBridgeClient._instance = new PluginBridgeClient();
64
+ }
65
+ return PluginBridgeClient._instance;
66
+ }
58
67
  constructor(port = PLUGIN_PORT) {
59
68
  this.port = port;
60
69
  this.backoff = new ExponentialBackoff();
@@ -98,13 +107,13 @@ export class PluginBridgeClient {
98
107
  const cmdWithId = { ...cmd, correlationId };
99
108
  return new Promise((resolve, reject) => {
100
109
  this.pendingCommands.set(correlationId, { resolve, reject });
101
- // 10-second timeout — T-07-11 mitigation
110
+ // 30-second timeout — T-07-11 mitigation
102
111
  const timeoutHandle = setTimeout(() => {
103
112
  if (this.pendingCommands.has(correlationId)) {
104
113
  this.pendingCommands.delete(correlationId);
105
- reject(new Error(`MCP command '${cmd.type}' timed out after 10 seconds`));
114
+ reject(new Error(`MCP command '${cmd.type}' timed out after 30 seconds`));
106
115
  }
107
- }, 10_000);
116
+ }, 30_000);
108
117
  // Write JSON-newline framed message to the socket.
109
118
  // The write callback fires on flush; reject immediately on error.
110
119
  this.socket.write(JSON.stringify(cmdWithId) + '\n', 'utf8', (err) => {
@@ -15,7 +15,7 @@ import { z } from 'zod';
15
15
  import { withKnownIssues } from '../known-issues/middleware.js';
16
16
  import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
17
17
  // Module-level bridge instance — injected in tests via exported handler signatures.
18
- const bridge = new PluginBridgeClient();
18
+ const bridge = PluginBridgeClient.shared();
19
19
  // ---------------------------------------------------------------------------
20
20
  // sendOrDisconnect helper
21
21
  // ---------------------------------------------------------------------------
@@ -154,7 +154,7 @@ export async function handleQueryNavmesh(args, b = bridge) {
154
154
  * accept bridge injection via their exported function signatures).
155
155
  */
156
156
  export function registerAISystemsTools(server, _bridge) {
157
- const b = _bridge ?? new PluginBridgeClient();
157
+ const b = _bridge ?? PluginBridgeClient.shared();
158
158
  // --------------------------------------------------------------------------
159
159
  // ue_inspect_behavior_tree (AI-01)
160
160
  // --------------------------------------------------------------------------
@@ -15,7 +15,7 @@ import { z } from 'zod';
15
15
  import { withKnownIssues } from '../known-issues/middleware.js';
16
16
  import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
17
17
  // Module-level bridge instance — injected in tests via exported handler signatures.
18
- const bridge = new PluginBridgeClient();
18
+ const bridge = PluginBridgeClient.shared();
19
19
  // ---------------------------------------------------------------------------
20
20
  // sendOrDisconnect helper
21
21
  // ---------------------------------------------------------------------------
@@ -152,7 +152,7 @@ export async function handleReadRetargetMappings(args, b = bridge) {
152
152
  * accept bridge injection via their exported function signatures).
153
153
  */
154
154
  export function registerAnimationTools(server, _bridge) {
155
- const b = _bridge ?? new PluginBridgeClient();
155
+ const b = _bridge ?? PluginBridgeClient.shared();
156
156
  // --------------------------------------------------------------------------
157
157
  // ue_list_animation_assets (ANIM-01)
158
158
  // --------------------------------------------------------------------------
@@ -61,7 +61,7 @@ async function sendOrDisconnect(b, cmd) {
61
61
  * @param b PluginBridgeClient instance (defaults to bridge created in registerAudioTools).
62
62
  */
63
63
  export async function handleListSoundAssets(args, b) {
64
- const bridge = b ?? new PluginBridgeClient();
64
+ const bridge = b ?? PluginBridgeClient.shared();
65
65
  // Response data shape: SoundAssetListResult
66
66
  const payload = {};
67
67
  if (args.type_filter !== undefined) {
@@ -80,7 +80,7 @@ export async function handleListSoundAssets(args, b) {
80
80
  * @param b PluginBridgeClient instance (defaults to bridge created in registerAudioTools).
81
81
  */
82
82
  export async function handleInspectMetasound(args, b) {
83
- const bridge = b ?? new PluginBridgeClient();
83
+ const bridge = b ?? PluginBridgeClient.shared();
84
84
  // Response data shape: MetaSoundInspectResult
85
85
  return sendOrDisconnect(bridge, {
86
86
  type: 'audio.metasound',
@@ -95,7 +95,7 @@ export async function handleInspectMetasound(args, b) {
95
95
  * @param b PluginBridgeClient instance (defaults to bridge created in registerAudioTools).
96
96
  */
97
97
  export async function handleInspectSoundCue(args, b) {
98
- const bridge = b ?? new PluginBridgeClient();
98
+ const bridge = b ?? PluginBridgeClient.shared();
99
99
  // Response data shape: SoundCueInspectResult
100
100
  return sendOrDisconnect(bridge, {
101
101
  type: 'audio.soundcue',
@@ -110,7 +110,7 @@ export async function handleInspectSoundCue(args, b) {
110
110
  * @param b PluginBridgeClient instance (defaults to bridge created in registerAudioTools).
111
111
  */
112
112
  export async function handleQueryAudioInsights(args, b) {
113
- const bridge = b ?? new PluginBridgeClient();
113
+ const bridge = b ?? PluginBridgeClient.shared();
114
114
  // Response data shape: AudioInsightsResult
115
115
  return sendOrDisconnect(bridge, {
116
116
  type: 'audio.insights',
@@ -137,7 +137,7 @@ export async function handleQueryAudioInsights(args, b) {
137
137
  * @param bridge Optional PluginBridgeClient for testing (injected into handler calls).
138
138
  */
139
139
  export function registerAudioTools(server, bridge) {
140
- const b = bridge ?? new PluginBridgeClient();
140
+ const b = bridge ?? PluginBridgeClient.shared();
141
141
  // --------------------------------------------------------------------------
142
142
  // ue_list_sound_assets (AUD-01)
143
143
  // --------------------------------------------------------------------------
@@ -14,7 +14,7 @@ import { validatePath } from '../../utils/path-guard.js';
14
14
  import { PROJECT_ROOT } from '../../config.js';
15
15
  // Module-level bridge instance — Phase 7 will introduce a shared singleton.
16
16
  // Two instances (blueprint + editor) is acceptable for Phase 1 stub behaviour.
17
- const bridge = new PluginBridgeClient();
17
+ const bridge = PluginBridgeClient.shared();
18
18
  /**
19
19
  * ue_read_blueprint handler — reads Blueprint structure via plugin bridge.
20
20
  * BPR-01: returns parentClass, variables, and components.
@@ -292,9 +292,9 @@ export async function handleFindBlueprintSubclasses(args) {
292
292
  * call or access a specific C++ member via the UE Editor plugin bridge.
293
293
  * BPC-03: requires plugin; returns plugin_not_connected when editor is not running.
294
294
  */
295
- export async function handleTraceCppInBlueprints(args) {
295
+ export async function handleTraceCppInBlueprints(args, b = bridge) {
296
296
  try {
297
- const response = await bridge.sendCommand({
297
+ const response = await b.sendCommand({
298
298
  type: 'blueprint.cppUsage',
299
299
  correlationId: '',
300
300
  payload: { class_name: args.class_name, member_name: args.member_name },
@@ -14,7 +14,7 @@ import { z } from 'zod';
14
14
  import { withKnownIssues } from '../known-issues/middleware.js';
15
15
  import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
16
16
  // Module-level bridge instance — injected in tests via exported handler signatures.
17
- const _defaultBridge = new PluginBridgeClient();
17
+ const _defaultBridge = PluginBridgeClient.shared();
18
18
  // ---------------------------------------------------------------------------
19
19
  // sendOrDisconnect helper
20
20
  // ---------------------------------------------------------------------------
@@ -148,7 +148,7 @@ export async function handleManagePhysicsCache(args, b = _defaultBridge) {
148
148
  * @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
149
149
  */
150
150
  export function registerChaosTools(server, bridge) {
151
- const b = bridge ?? new PluginBridgeClient();
151
+ const b = bridge ?? PluginBridgeClient.shared();
152
152
  // --------------------------------------------------------------------------
153
153
  // ue_inspect_geometry_collection (CHAOS-01)
154
154
  // --------------------------------------------------------------------------
@@ -70,7 +70,7 @@ async function sendOrDisconnect(bridge, cmd) {
70
70
  * @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
71
71
  */
72
72
  export function registerCollisionPhysicsTools(server, bridge) {
73
- const _bridge = bridge ?? new PluginBridgeClient();
73
+ const _bridge = bridge ?? PluginBridgeClient.shared();
74
74
  // --------------------------------------------------------------------------
75
75
  // ue_read_collision (PHY-01)
76
76
  // --------------------------------------------------------------------------
@@ -65,7 +65,7 @@ async function sendOrDisconnect(bridge, cmd) {
65
65
  * @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
66
66
  */
67
67
  export function registerEditorTools(server, bridge) {
68
- const _bridge = bridge ?? new PluginBridgeClient();
68
+ const _bridge = bridge ?? PluginBridgeClient.shared();
69
69
  // --------------------------------------------------------------------------
70
70
  // ue_list_actors
71
71
  // --------------------------------------------------------------------------
@@ -14,7 +14,7 @@ import { z } from 'zod';
14
14
  import { withKnownIssues } from '../known-issues/middleware.js';
15
15
  import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
16
16
  // Module-level bridge instance — injected in tests via exported handler signatures.
17
- const bridge = new PluginBridgeClient();
17
+ const bridge = PluginBridgeClient.shared();
18
18
  // ---------------------------------------------------------------------------
19
19
  // sendOrDisconnect helper
20
20
  // ---------------------------------------------------------------------------
@@ -146,7 +146,7 @@ export async function handleQueryGameplayTags(args, b = bridge) {
146
146
  * accept bridge injection via their exported function signatures).
147
147
  */
148
148
  export function registerGASTools(server, _bridge) {
149
- const b = _bridge ?? new PluginBridgeClient();
149
+ const b = _bridge ?? PluginBridgeClient.shared();
150
150
  // --------------------------------------------------------------------------
151
151
  // ue_list_abilities (GAS-01)
152
152
  // --------------------------------------------------------------------------
@@ -76,7 +76,7 @@ async function sendOrDisconnect(b, cmd) {
76
76
  * @param bridge Optional PluginBridgeClient for testing (injected bridge replaces module singleton).
77
77
  */
78
78
  export function registerImportExportTools(server, bridge) {
79
- const _bridge = bridge ?? new PluginBridgeClient();
79
+ const _bridge = bridge ?? PluginBridgeClient.shared();
80
80
  // --------------------------------------------------------------------------
81
81
  // ue_import_fbx (IMP-01)
82
82
  // --------------------------------------------------------------------------
@@ -61,7 +61,7 @@ async function sendOrDisconnect(bridge, cmd) {
61
61
  * @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
62
62
  */
63
63
  export function registerInputTools(server, bridge) {
64
- const _bridge = bridge ?? new PluginBridgeClient();
64
+ const _bridge = bridge ?? PluginBridgeClient.shared();
65
65
  // --------------------------------------------------------------------------
66
66
  // ue_list_input_actions
67
67
  // --------------------------------------------------------------------------
@@ -14,7 +14,7 @@ import { z } from 'zod';
14
14
  import { withKnownIssues } from '../known-issues/middleware.js';
15
15
  import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
16
16
  // Module-level bridge instance — injected in tests via exported handler signatures.
17
- const bridge = new PluginBridgeClient();
17
+ const bridge = PluginBridgeClient.shared();
18
18
  // ---------------------------------------------------------------------------
19
19
  // sendOrDisconnect helper
20
20
  // ---------------------------------------------------------------------------
@@ -138,7 +138,7 @@ export async function handlePreviewLiveLinkData(args, b = bridge) {
138
138
  * accept bridge injection via their exported function signatures).
139
139
  */
140
140
  export function registerLiveLinkTools(server, _bridge) {
141
- const b = _bridge ?? new PluginBridgeClient();
141
+ const b = _bridge ?? PluginBridgeClient.shared();
142
142
  // --------------------------------------------------------------------------
143
143
  // ue_list_livelink_sources (LL-01)
144
144
  // --------------------------------------------------------------------------
@@ -62,7 +62,7 @@ async function sendOrDisconnect(bridge, cmd) {
62
62
  * @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
63
63
  */
64
64
  export function registerMaterialTools(server, bridge) {
65
- const _bridge = bridge ?? new PluginBridgeClient();
65
+ const _bridge = bridge ?? PluginBridgeClient.shared();
66
66
  // --------------------------------------------------------------------------
67
67
  // ue_material_params
68
68
  // --------------------------------------------------------------------------
@@ -14,7 +14,7 @@ import { z } from 'zod';
14
14
  import { withKnownIssues } from '../known-issues/middleware.js';
15
15
  import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
16
16
  // Module-level bridge instance — injected in tests via exported handler signatures.
17
- const bridge = new PluginBridgeClient();
17
+ const bridge = PluginBridgeClient.shared();
18
18
  // ---------------------------------------------------------------------------
19
19
  // sendOrDisconnect helper
20
20
  // ---------------------------------------------------------------------------
@@ -152,7 +152,7 @@ export async function handleManageRemoteControl(args, b = bridge) {
152
152
  * accept bridge injection via their exported function signatures).
153
153
  */
154
154
  export function registerMotionDesignTools(server, _bridge) {
155
- const b = _bridge ?? new PluginBridgeClient();
155
+ const b = _bridge ?? PluginBridgeClient.shared();
156
156
  // --------------------------------------------------------------------------
157
157
  // ue_list_scene_states (MD-01)
158
158
  // --------------------------------------------------------------------------
@@ -71,7 +71,7 @@ async function sendOrDisconnect(bridge, cmd) {
71
71
  * @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
72
72
  */
73
73
  export function registerMovieRenderTools(server, bridge) {
74
- const _bridge = bridge ?? new PluginBridgeClient();
74
+ const _bridge = bridge ?? PluginBridgeClient.shared();
75
75
  // --------------------------------------------------------------------------
76
76
  // ue_list_render_queue (MRP-01)
77
77
  // --------------------------------------------------------------------------
@@ -15,7 +15,7 @@ import { z } from 'zod';
15
15
  import { withKnownIssues } from '../known-issues/middleware.js';
16
16
  import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
17
17
  // Module-level bridge instance — injected in tests via exported function parameter.
18
- const bridge = new PluginBridgeClient();
18
+ const bridge = PluginBridgeClient.shared();
19
19
  // ---------------------------------------------------------------------------
20
20
  // sendOrDisconnect helper
21
21
  // ---------------------------------------------------------------------------
@@ -70,7 +70,7 @@ async function sendOrDisconnect(bridge, cmd) {
70
70
  * @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
71
71
  */
72
72
  export function registerPCGTools(server, bridge) {
73
- const _bridge = bridge ?? new PluginBridgeClient();
73
+ const _bridge = bridge ?? PluginBridgeClient.shared();
74
74
  // --------------------------------------------------------------------------
75
75
  // ue_list_pcg_graphs
76
76
  // --------------------------------------------------------------------------
@@ -70,7 +70,7 @@ async function sendOrDisconnect(bridge, cmd) {
70
70
  * @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
71
71
  */
72
72
  export function registerSelectionTools(server, bridge) {
73
- const _bridge = bridge ?? new PluginBridgeClient();
73
+ const _bridge = bridge ?? PluginBridgeClient.shared();
74
74
  // --------------------------------------------------------------------------
75
75
  // ue_select_actors (SEL-01)
76
76
  // --------------------------------------------------------------------------
@@ -72,7 +72,7 @@ async function sendOrDisconnect(bridge, cmd) {
72
72
  * @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
73
73
  */
74
74
  export function registerSequencerTools(server, bridge) {
75
- const _bridge = bridge ?? new PluginBridgeClient();
75
+ const _bridge = bridge ?? PluginBridgeClient.shared();
76
76
  // --------------------------------------------------------------------------
77
77
  // ue_create_sequence
78
78
  // --------------------------------------------------------------------------
@@ -6,7 +6,7 @@ import { z } from 'zod';
6
6
  import { withKnownIssues } from '../known-issues/middleware.js';
7
7
  import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
8
8
  // Module-level bridge instance — injected in tests via exported handler signatures.
9
- const bridge = new PluginBridgeClient();
9
+ const bridge = PluginBridgeClient.shared();
10
10
  // ---------------------------------------------------------------------------
11
11
  // sendOrDisconnect helper
12
12
  // ---------------------------------------------------------------------------
@@ -119,7 +119,7 @@ export async function handleCheckBlueprint(args, b = bridge) {
119
119
  * @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
120
120
  */
121
121
  export function registerValidationTools(server, bridge) {
122
- const _bridge = bridge ?? new PluginBridgeClient();
122
+ const _bridge = bridge ?? PluginBridgeClient.shared();
123
123
  // --------------------------------------------------------------------------
124
124
  // ue_validate_asset (VAL-01)
125
125
  // --------------------------------------------------------------------------
@@ -183,7 +183,7 @@ const targetSchema = z.union([
183
183
  * @param bridge Optional PluginBridgeClient for testing (defaults to a new instance).
184
184
  */
185
185
  export function registerViewportTools(server, bridge) {
186
- const _bridge = bridge ?? new PluginBridgeClient();
186
+ const _bridge = bridge ?? PluginBridgeClient.shared();
187
187
  // --------------------------------------------------------------------------
188
188
  // ue_editor_state
189
189
  // --------------------------------------------------------------------------
@@ -14,7 +14,7 @@ import { z } from 'zod';
14
14
  import { withKnownIssues } from '../known-issues/middleware.js';
15
15
  import { PluginBridgeClient, PluginNotConnectedError } from '../../plugin-bridge/client.js';
16
16
  // Module-level bridge instance — injected in tests via exported handler signatures.
17
- const bridge = new PluginBridgeClient();
17
+ const bridge = PluginBridgeClient.shared();
18
18
  // ---------------------------------------------------------------------------
19
19
  // sendOrDisconnect helper
20
20
  // ---------------------------------------------------------------------------
@@ -145,7 +145,7 @@ export async function handleTriggerHlodGeneration(args, b = bridge) {
145
145
  * accept bridge injection via their exported function signatures).
146
146
  */
147
147
  export function registerWorldPartitionTools(server, _bridge) {
148
- const b = _bridge ?? new PluginBridgeClient();
148
+ const b = _bridge ?? PluginBridgeClient.shared();
149
149
  // --------------------------------------------------------------------------
150
150
  // ue_read_world_partition (WP-01)
151
151
  // --------------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate-unreal-engine-mcp",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "MCP server giving AI assistants full access to Unreal Engine 5.7 projects",
5
5
  "type": "module",
6
6
  "engines": {