ue-mcp 1.0.70 → 1.0.71

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
@@ -1,6 +1,6 @@
1
1
  # UE-MCP
2
2
 
3
- **Unreal Engine Model Context Protocol Server** - gives AI assistants deep read/write access to the Unreal Editor through <!-- count:tools -->21<!-- /count --> category tools covering <!-- count:actions -->545+<!-- /count --> actions, plus a YAML flow engine for multi-step workflows.
3
+ **Unreal Engine Model Context Protocol Server** - gives AI assistants deep read/write access to the Unreal Editor through <!-- count:tools -->21<!-- /count --> category tools covering <!-- count:actions -->547+<!-- /count --> actions, plus a YAML flow engine for multi-step workflows.
4
4
 
5
5
  ```mermaid
6
6
  flowchart LR
@@ -57,7 +57,7 @@ If you prefer to configure manually, add to your MCP client config:
57
57
 
58
58
  - [Getting Started](https://db-lyon.github.io/ue-mcp/getting-started/) — Installation, configuration, first run
59
59
  - [Architecture](https://db-lyon.github.io/ue-mcp/architecture/) — How the pieces fit together
60
- - [Tool Reference](https://db-lyon.github.io/ue-mcp/tool-reference/) - All <!-- count:tools -->21<!-- /count --> tools with <!-- count:actions -->545+<!-- /count --> actions
60
+ - [Tool Reference](https://db-lyon.github.io/ue-mcp/tool-reference/) - All <!-- count:tools -->21<!-- /count --> tools with <!-- count:actions -->547+<!-- /count --> actions
61
61
  - [Flows](https://db-lyon.github.io/ue-mcp/flows/) - YAML flow engine, custom tasks, rollback, hooks
62
62
  - [Configuration](https://db-lyon.github.io/ue-mcp/configuration/) — `.ue-mcp.json` and MCP client config
63
63
  - [Neon Shrine Demo](https://db-lyon.github.io/ue-mcp/neon-shrine-demo/) — Interactive guided demo
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "tools": 21,
3
- "actions": 545,
4
- "bridgeActions": 517,
3
+ "actions": 547,
4
+ "bridgeActions": 519,
5
5
  "localActions": 28,
6
6
  "perTool": {
7
7
  "project": 25,
@@ -19,13 +19,13 @@
19
19
  "editor": 54,
20
20
  "reflection": 8,
21
21
  "gameplay": 53,
22
- "gas": 12,
22
+ "gas": 14,
23
23
  "networking": 11,
24
24
  "demo": 3,
25
25
  "feedback": 1,
26
26
  "statetree": 35,
27
27
  "plugins": 2
28
28
  },
29
- "generatedAt": "2026-05-29T20:36:26.666Z",
30
- "version": "1.0.70"
29
+ "generatedAt": "2026-05-29T20:48:42.832Z",
30
+ "version": "1.0.71"
31
31
  }
package/dist/tools/gas.js CHANGED
@@ -10,9 +10,11 @@ export const gasTool = categoryTool("gas", "Gameplay Ability System: abilities,
10
10
  set_effect_modifier: bp("Add modifier. Params: effectPath, attribute, operation?, magnitude?", "set_effect_modifier"),
11
11
  create_cue: bp("Create GameplayCue. Params: name, packagePath?, cueType?", "create_gameplay_cue"),
12
12
  get_info: bp("Inspect GAS setup. Params: blueprintPath", "get_gas_info"),
13
+ set_asc_defaults: bp("Wire an AttributeSet onto a Blueprint's ASC component (DefaultStartingData) so attributes exist at runtime. Params: blueprintPath, attributeSet (content path or class name), componentName?, initDataTable? (starting values). Run add_ability_system_component first.", "set_asc_defaults"),
13
14
  apply_effect: bp("Apply a GameplayEffect to a live actor's ASC (agnostic stat/damage stimulus - uses the game's own effect). Params: actorLabel, effectClass (content path or class name), level?, setByCaller? ({tag-or-name: magnitude}), world? (auto|pie|editor, default auto)", "apply_effect"),
14
15
  set_attribute: bp("Set a gameplay attribute's base value on a live actor's ASC (recalculates CurrentValue through the aggregator). Params: actorLabel, attribute (Health | SetName.Health), value, world?", "set_attribute"),
15
16
  get_attribute: bp("Read gameplay attribute base + current values on a live actor's ASC. Omit attribute to list all. Params: actorLabel, attribute?, world?", "get_attribute"),
17
+ init_asc: bp("Initialize a live actor's ASC (InitAbilityActorInfo) and optionally instantiate an AttributeSet so attributes are live - the runtime setup step for testing a bridge-authored GAS actor. Params: actorLabel, attributeSet? (content path or class name), world?", "init_asc"),
16
18
  }, undefined, {
17
19
  blueprintPath: z.string().optional(),
18
20
  name: z.string().optional(),
@@ -41,5 +43,7 @@ export const gasTool = categoryTool("gas", "Gameplay Ability System: abilities,
41
43
  setByCaller: z.record(z.number()).optional().describe("apply_effect: SetByCaller magnitudes keyed by gameplay tag or name"),
42
44
  value: z.number().optional().describe("set_attribute: new base value"),
43
45
  world: z.string().optional().describe("Runtime world scope: auto (default) | pie | editor"),
46
+ attributeSet: z.string().optional().describe("set_asc_defaults / init_asc: AttributeSet content path or class name"),
47
+ initDataTable: z.string().optional().describe("set_asc_defaults: optional DataTable of starting attribute values"),
44
48
  });
45
49
  //# sourceMappingURL=gas.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gas.js","sourceRoot":"","sources":["../../src/tools/gas.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gas.js","sourceRoot":"","sources":["../../src/tools/gas.ts"],"names":[],"mappings":"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"}
@@ -2927,6 +2927,12 @@ tasks:
2927
2927
  description: "Inspect GAS setup. Params: blueprintPath"
2928
2928
  options:
2929
2929
  method: get_gas_info
2930
+ gas.set_asc_defaults:
2931
+ class_path: ue-mcp.bridge
2932
+ group: gas
2933
+ description: "Wire an AttributeSet onto a Blueprint's ASC component (DefaultStartingData) so attributes exist at runtime. Params: blueprintPath, attributeSet (content path or class name), componentName?, initDataTable? (starting values). Run add_ability_system_component first."
2934
+ options:
2935
+ method: set_asc_defaults
2930
2936
  gas.apply_effect:
2931
2937
  class_path: ue-mcp.bridge
2932
2938
  group: gas
@@ -2945,6 +2951,12 @@ tasks:
2945
2951
  description: "Read gameplay attribute base + current values on a live actor's ASC. Omit attribute to list all. Params: actorLabel, attribute?, world?"
2946
2952
  options:
2947
2953
  method: get_attribute
2954
+ gas.init_asc:
2955
+ class_path: ue-mcp.bridge
2956
+ group: gas
2957
+ description: "Initialize a live actor's ASC (InitAbilityActorInfo) and optionally instantiate an AttributeSet so attributes are live - the runtime setup step for testing a bridge-authored GAS actor. Params: actorLabel, attributeSet? (content path or class name), world?"
2958
+ options:
2959
+ method: init_asc
2948
2960
 
2949
2961
  # ── networking ──
2950
2962
  networking.set_replicates:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ue-mcp",
3
- "version": "1.0.70",
4
- "description": "Unreal Engine MCP server - 21 tools, 545+ actions for AI-driven editor control",
3
+ "version": "1.0.71",
4
+ "description": "Unreal Engine MCP server - 21 tools, 547+ actions for AI-driven editor control",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "exports": {
@@ -21,6 +21,9 @@
21
21
  #include "Engine/SimpleConstructionScript.h"
22
22
  #include "Engine/SCS_Node.h"
23
23
  #include "EdGraphSchema_K2.h"
24
+ #include "AbilitySystemComponent.h"
25
+ #include "AttributeSet.h"
26
+ #include "Engine/DataTable.h"
24
27
 
25
28
  void FGasHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
26
29
  {
@@ -33,9 +36,11 @@ void FGasHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
33
36
  Registry.RegisterHandler(TEXT("add_attribute"), &AddAttribute);
34
37
  Registry.RegisterHandler(TEXT("set_ability_tags"), &SetAbilityTags);
35
38
  Registry.RegisterHandler(TEXT("set_effect_modifier"), &SetEffectModifier);
39
+ Registry.RegisterHandler(TEXT("set_asc_defaults"), &SetAscDefaults);
36
40
  Registry.RegisterHandler(TEXT("apply_effect"), &ApplyEffect);
37
41
  Registry.RegisterHandler(TEXT("set_attribute"), &SetAttribute);
38
42
  Registry.RegisterHandler(TEXT("get_attribute"), &GetAttribute);
43
+ Registry.RegisterHandler(TEXT("init_asc"), &InitAsc);
39
44
  }
40
45
 
41
46
  TSharedPtr<FJsonValue> FGasHandlers::CreateGasBlueprint(
@@ -376,3 +381,109 @@ TSharedPtr<FJsonValue> FGasHandlers::SetEffectModifier(const TSharedPtr<FJsonObj
376
381
  Result->SetStringField(TEXT("note"), TEXT("GameplayEffect modifier configuration set. Use execute_python for full GE modifier array manipulation."));
377
382
  return MCPResult(Result);
378
383
  }
384
+
385
+ TSharedPtr<FJsonValue> FGasHandlers::SetAscDefaults(const TSharedPtr<FJsonObject>& Params)
386
+ {
387
+ FString BPPath;
388
+ if (auto Err = RequireString(Params, TEXT("blueprintPath"), BPPath)) return Err;
389
+
390
+ FString AttrSetSpec;
391
+ if (auto Err = RequireStringAlt(Params, TEXT("attributeSet"), TEXT("attributeSetPath"), AttrSetSpec)) return Err;
392
+
393
+ UBlueprint* BP = Cast<UBlueprint>(UEditorAssetLibrary::LoadAsset(BPPath));
394
+ if (!BP) return MCPError(FString::Printf(TEXT("Blueprint not found: %s"), *BPPath));
395
+
396
+ UClass* ASCClass = FindObject<UClass>(nullptr, TEXT("/Script/GameplayAbilities.AbilitySystemComponent"));
397
+ if (!ASCClass) return MCPError(TEXT("AbilitySystemComponent not found. Enable GameplayAbilities plugin."));
398
+
399
+ // Resolve the AttributeSet class from a content path (BP generated class) or
400
+ // a native short name.
401
+ UClass* AttrSetClass = nullptr;
402
+ {
403
+ UClass* AttrBase = UAttributeSet::StaticClass();
404
+ auto Ok = [AttrBase](UClass* C) { return C && C->IsChildOf(AttrBase); };
405
+ if (AttrSetSpec.Contains(TEXT("/")))
406
+ {
407
+ if (UClass* C = LoadObject<UClass>(nullptr, *AttrSetSpec); Ok(C)) AttrSetClass = C;
408
+ if (!AttrSetClass)
409
+ {
410
+ FString AssetName;
411
+ AttrSetSpec.Split(TEXT("/"), nullptr, &AssetName, ESearchCase::CaseSensitive, ESearchDir::FromEnd);
412
+ if (UClass* C = LoadObject<UClass>(nullptr, *(AttrSetSpec + TEXT(".") + AssetName + TEXT("_C"))); Ok(C)) AttrSetClass = C;
413
+ }
414
+ if (!AttrSetClass)
415
+ {
416
+ if (UBlueprint* SetBP = Cast<UBlueprint>(UEditorAssetLibrary::LoadAsset(AttrSetSpec)))
417
+ {
418
+ if (Ok(SetBP->GeneratedClass)) AttrSetClass = SetBP->GeneratedClass;
419
+ }
420
+ }
421
+ }
422
+ else if (UClass* C = FindClassByShortName(AttrSetSpec); Ok(C))
423
+ {
424
+ AttrSetClass = C;
425
+ }
426
+ }
427
+ if (!AttrSetClass) return MCPError(FString::Printf(TEXT("AttributeSet class not found: %s"), *AttrSetSpec));
428
+
429
+ // Find the ASC component template on the blueprint's construction script.
430
+ const FString CompName = OptionalString(Params, TEXT("componentName"));
431
+ UAbilitySystemComponent* ASCTemplate = nullptr;
432
+ FString ResolvedComp;
433
+ if (BP->SimpleConstructionScript)
434
+ {
435
+ for (USCS_Node* N : BP->SimpleConstructionScript->GetAllNodes())
436
+ {
437
+ if (!N || !N->ComponentTemplate || !N->ComponentTemplate->IsA(ASCClass)) continue;
438
+ if (!CompName.IsEmpty() && N->GetVariableName() != FName(*CompName)) continue;
439
+ ASCTemplate = Cast<UAbilitySystemComponent>(N->ComponentTemplate);
440
+ ResolvedComp = N->GetVariableName().ToString();
441
+ break;
442
+ }
443
+ }
444
+ if (!ASCTemplate)
445
+ {
446
+ return MCPError(TEXT("No AbilitySystemComponent on the blueprint - run add_ability_system_component first"));
447
+ }
448
+
449
+ // Optional init DataTable (production path: starting values at ASC init).
450
+ UDataTable* InitTable = nullptr;
451
+ const FString TablePath = OptionalString(Params, TEXT("initDataTable"));
452
+ if (!TablePath.IsEmpty())
453
+ {
454
+ InitTable = LoadObject<UDataTable>(nullptr, *TablePath);
455
+ if (!InitTable) return MCPError(FString::Printf(TEXT("initDataTable not found: %s"), *TablePath));
456
+ }
457
+
458
+ // Idempotency: already wired for this attribute set?
459
+ for (const FAttributeDefaults& D : ASCTemplate->DefaultStartingData)
460
+ {
461
+ if (D.Attributes == AttrSetClass)
462
+ {
463
+ auto Existed = MCPSuccess();
464
+ MCPSetExisted(Existed);
465
+ Existed->SetStringField(TEXT("blueprintPath"), BPPath);
466
+ Existed->SetStringField(TEXT("component"), ResolvedComp);
467
+ Existed->SetStringField(TEXT("attributeSet"), AttrSetClass->GetName());
468
+ return MCPResult(Existed);
469
+ }
470
+ }
471
+
472
+ FAttributeDefaults Def;
473
+ Def.Attributes = AttrSetClass;
474
+ Def.DefaultStartingTable = InitTable;
475
+ ASCTemplate->DefaultStartingData.Add(Def);
476
+
477
+ FKismetEditorUtilities::CompileBlueprint(BP);
478
+ SaveAssetPackage(BP);
479
+
480
+ auto Result = MCPSuccess();
481
+ MCPSetCreated(Result);
482
+ Result->SetStringField(TEXT("blueprintPath"), BPPath);
483
+ Result->SetStringField(TEXT("component"), ResolvedComp);
484
+ Result->SetStringField(TEXT("attributeSet"), AttrSetClass->GetName());
485
+ if (InitTable) Result->SetStringField(TEXT("initDataTable"), InitTable->GetPathName());
486
+ Result->SetStringField(TEXT("note"),
487
+ TEXT("Attribute set wired to the ASC's DefaultStartingData. If attributes aren't live at runtime, call gas(action=\"init_asc\", attributeSet=...) after PIE starts."));
488
+ return MCPResult(Result);
489
+ }
@@ -33,9 +33,16 @@ private:
33
33
  static TSharedPtr<FJsonValue> SetAbilityTags(const TSharedPtr<FJsonObject>& Params);
34
34
  static TSharedPtr<FJsonValue> SetEffectModifier(const TSharedPtr<FJsonObject>& Params);
35
35
 
36
+ // Wire an AttributeSet (with optional init DataTable) onto a Blueprint's ASC
37
+ // component template via DefaultStartingData. Authoring; in GasHandlers.cpp.
38
+ static TSharedPtr<FJsonValue> SetAscDefaults(const TSharedPtr<FJsonObject>& Params);
39
+
36
40
  // Runtime GAS control (operates on a live actor's AbilitySystemComponent,
37
41
  // PIE by default). Implemented in GasHandlers_Runtime.cpp.
38
42
  static TSharedPtr<FJsonValue> ApplyEffect(const TSharedPtr<FJsonObject>& Params);
39
43
  static TSharedPtr<FJsonValue> SetAttribute(const TSharedPtr<FJsonObject>& Params);
40
44
  static TSharedPtr<FJsonValue> GetAttribute(const TSharedPtr<FJsonObject>& Params);
45
+ // InitAbilityActorInfo + optionally GetOrCreateAttributeSubobject on a live
46
+ // actor, so a bridge-authored GAS actor has live attributes to test against.
47
+ static TSharedPtr<FJsonValue> InitAsc(const TSharedPtr<FJsonObject>& Params);
41
48
  };
@@ -109,30 +109,29 @@ namespace
109
109
  Obj->SetNumberField(TEXT("currentValue"), ASC->GetNumericAttribute(Attr));
110
110
  }
111
111
 
112
- // Resolve a UGameplayEffect subclass from a content path or short class name.
113
- UClass* ResolveEffectClass(const FString& Spec)
112
+ // Resolve a UClass deriving from Base from a content path or short class name.
113
+ // Handles native classes, Blueprint generated classes (path + "_C"), and a
114
+ // Blueprint-asset fallback. Returns nullptr unless the result is a Base subclass.
115
+ UClass* ResolveClassDeriving(const FString& Spec, UClass* Base)
114
116
  {
115
- auto IsEffect = [](UClass* C) { return C && C->IsChildOf(UGameplayEffect::StaticClass()); };
117
+ auto Ok = [Base](UClass* C) { return C && Base && C->IsChildOf(Base); };
116
118
 
117
119
  if (Spec.Contains(TEXT("/")))
118
120
  {
119
- // Direct class load (native or already-_C class path).
120
- if (UClass* C = LoadObject<UClass>(nullptr, *Spec); IsEffect(C)) return C;
121
- // Blueprint generated class: "/Game/Foo/GE_Bar" -> ".../GE_Bar.GE_Bar_C".
121
+ if (UClass* C = LoadObject<UClass>(nullptr, *Spec); Ok(C)) return C;
122
122
  FString AssetName;
123
123
  Spec.Split(TEXT("/"), nullptr, &AssetName, ESearchCase::CaseSensitive, ESearchDir::FromEnd);
124
124
  const FString ClassPath = Spec + TEXT(".") + AssetName + TEXT("_C");
125
- if (UClass* C = LoadObject<UClass>(nullptr, *ClassPath); IsEffect(C)) return C;
126
- // Fall back to loading the Blueprint and taking its generated class.
125
+ if (UClass* C = LoadObject<UClass>(nullptr, *ClassPath); Ok(C)) return C;
127
126
  if (UBlueprint* BP = LoadAssetByPath<UBlueprint>(Spec))
128
127
  {
129
- if (IsEffect(BP->GeneratedClass)) return BP->GeneratedClass;
128
+ if (Ok(BP->GeneratedClass)) return BP->GeneratedClass;
130
129
  }
131
130
  return nullptr;
132
131
  }
133
132
 
134
133
  UClass* C = FindClassByShortName(Spec);
135
- return IsEffect(C) ? C : nullptr;
134
+ return Ok(C) ? C : nullptr;
136
135
  }
137
136
  }
138
137
 
@@ -148,7 +147,7 @@ TSharedPtr<FJsonValue> FGasHandlers::ApplyEffect(const TSharedPtr<FJsonObject>&
148
147
  UAbilitySystemComponent* ASC = ResolveASC(Params, Actor, Err);
149
148
  if (!ASC) return Err;
150
149
 
151
- UClass* EffectClass = ResolveEffectClass(EffectSpec);
150
+ UClass* EffectClass = ResolveClassDeriving(EffectSpec, UGameplayEffect::StaticClass());
152
151
  if (!EffectClass)
153
152
  {
154
153
  return MCPError(FString::Printf(
@@ -298,3 +297,64 @@ TSharedPtr<FJsonValue> FGasHandlers::GetAttribute(const TSharedPtr<FJsonObject>&
298
297
  Result->SetNumberField(TEXT("count"), Rows.Num());
299
298
  return MCPResult(Result);
300
299
  }
300
+
301
+ TSharedPtr<FJsonValue> FGasHandlers::InitAsc(const TSharedPtr<FJsonObject>& Params)
302
+ {
303
+ MCP_CHECK_GAME_THREAD();
304
+
305
+ AActor* Actor = nullptr;
306
+ TSharedPtr<FJsonValue> Err;
307
+ UAbilitySystemComponent* ASC = ResolveASC(Params, Actor, Err);
308
+ if (!ASC) return Err;
309
+
310
+ // Establish owner/avatar so abilities activate and effect contexts target
311
+ // correctly. Safe to call again; a game's own pawn may also init the ASC.
312
+ ASC->InitAbilityActorInfo(Actor, Actor);
313
+
314
+ // Optionally guarantee an attribute set exists on the ASC. This is what lets
315
+ // a bridge-authored test actor have live attributes without shipping an init
316
+ // DataTable: spawn the set (with its default values) and register it if it
317
+ // isn't already present. GetOrCreateAttributeSubobject is protected, so use
318
+ // the public GetAttributeSet + AddSpawnedAttribute pair.
319
+ FString CreatedSet;
320
+ const FString AttrSetSpec = OptionalString(Params, TEXT("attributeSet"));
321
+ if (!AttrSetSpec.IsEmpty())
322
+ {
323
+ UClass* AttrSetClass = ResolveClassDeriving(AttrSetSpec, UAttributeSet::StaticClass());
324
+ if (!AttrSetClass)
325
+ {
326
+ return MCPError(FString::Printf(
327
+ TEXT("AttributeSet class not found: %s (pass a content path or class name)"), *AttrSetSpec));
328
+ }
329
+ const UAttributeSet* Existing = ASC->GetAttributeSet(AttrSetClass);
330
+ if (!Existing)
331
+ {
332
+ UAttributeSet* NewSet = NewObject<UAttributeSet>(Actor, AttrSetClass);
333
+ ASC->AddSpawnedAttribute(NewSet);
334
+ CreatedSet = NewSet->GetClass()->GetName();
335
+ }
336
+ else
337
+ {
338
+ CreatedSet = Existing->GetClass()->GetName();
339
+ }
340
+ }
341
+
342
+ // Count attributes now live across all spawned sets.
343
+ int32 AttrCount = 0;
344
+ for (const UAttributeSet* Set : ASC->GetSpawnedAttributes())
345
+ {
346
+ if (!Set) continue;
347
+ for (TFieldIterator<FProperty> It(Set->GetClass()); It; ++It)
348
+ {
349
+ FStructProperty* SProp = CastField<FStructProperty>(*It);
350
+ if (SProp && SProp->Struct == FGameplayAttributeData::StaticStruct()) ++AttrCount;
351
+ }
352
+ }
353
+
354
+ auto Result = MCPSuccess();
355
+ Result->SetStringField(TEXT("actorLabel"), Actor->GetActorLabel());
356
+ Result->SetBoolField(TEXT("initialized"), true);
357
+ if (!CreatedSet.IsEmpty()) Result->SetStringField(TEXT("attributeSet"), CreatedSet);
358
+ Result->SetNumberField(TEXT("attributeCount"), AttrCount);
359
+ return MCPResult(Result);
360
+ }