Atlas / MCP servers / db-lyon / ue-mcp

ue-mcpBLOCK

mcp/db-lyon/ue-mcp

Complete Unreal Engine development toolkit exposed as MCP tools.

Verdict
BLOCK
Grade
F
Trust score
54 /100
Exposed tools
4 4r · 0w · 0d
Transport
stdio
License
MIT
Stars
407
01

Overview

From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.

[](https://www.npmjs.com/package/ue-mcp) [](https://www.npmjs.com/package/ue-mcp) [](https://github.com/db-lyon/ue-mcp/actions/workflows/ci.yml) [](https://github.com/db-lyon/ue-mcp/stargazers) [](LICENSE)

Unreal Engine Model Context Protocol Server - gives AI assistants deep read/write access to the Unreal Editor through 26 category tools covering 1975+ actions, plus a YAML flow engine for multi-step workflows and an npm plugin system for extending the surface.

That total is 1145+ actions of ue-mcp's own plus Epic's entire native AI Toolset Registry: on UE 5.8+ its 830 official Unreal tools are called in-process and surfaced as epic_* actions in the matching category, Sequencer in animation, PCG in pcg, static meshes in asset.

flowchart LR
AI[AI Assistant] -->|stdio| MCP[MCP ServerTypeScript / Node.js]
MCP -->|WebSocketJSON-RPC| Plugin[C++ Bridge Plugininside Unreal Editor]
Plugin -->|UE API| Editor[Editor Subsystems]
MCP -->|direct fs| FS[Config INIC++ HeadersAsset Listing]

Blueprints, materials, levels, actors, animation, VFX, landscape, PCG, foliage, audio, UI, physics, navigation, AI, StateTree, GAS, networking, sequencer, build pipeline - all programmable through natural language.

Filesystem reads (INI config, C++ headers, asset listing) work with the editor closed, so an agent can

Read from source at commit cd7f9ff300a5OBSERVED · 2026-10-11
02

Connect

Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control. Replace the environment placeholders with a token scoped to the least it needs.

claude-code
claude mcp add ue-mcp --env ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} --env REGISTRY_PUBLISH_TOKEN=${REGISTRY_PUBLISH_TOKEN} --env UE_MCP_AUTH_DIR=${UE_MCP_AUTH_DIR} -- npx -y [email protected]
claude-desktop
{
  "mcpServers": {
    "ue-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "[email protected]"
      ],
      "env": {
        "ANTHROPIC_API_KEY": "${ANTHROPIC_API_KEY}",
        "REGISTRY_PUBLISH_TOKEN": "${REGISTRY_PUBLISH_TOKEN}",
        "UE_MCP_AUTH_DIR": "${UE_MCP_AUTH_DIR}"
      }
    }
  }
}
03

Exposed tools (4)

4 read · 0 write · 0 destructive.

ToolRiskDescription
afterread
beforereadflat
context_probereadasks the context what it is
flowread
04

Trust audit

BLOCKgrade F · trust 54/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.

LayerWhat it checksResult
L0Provenance & inventoryPASS
L1Static analysis of the codeFAIL
L2Instruction surface (what it tells the agent)PASS
L3Class-specific surfacePASS
L4Behavioural (sandbox)SKIPPED

What the source does

Filesystem
declared (10 observation(s))
Network
declared (13 observation(s))
Shell
declared (11 observation(s))
Dependencies
not all pinned
Secrets in source
none-found

Findings (25)

HIGHCode injection · code.deserialize · CWE-78, CWE-94, CWE-95
scripts/check-tracked-config.mjs:80
doc = yaml.load(text);
Why it matters. deserialises untrusted bytes into live objects
Fix. use json or yaml.safe_load
HIGHCode injection · code.deserialize · CWE-78, CWE-94, CWE-95
src/config/ue-mcp-config.ts:59
const raw = yaml.load(fs.readFileSync(file, "utf-8"), { filename: file });
Why it matters. deserialises untrusted bytes into live objects
Fix. use json or yaml.safe_load
HIGHCode injection · code.deserialize · CWE-78, CWE-94, CWE-95
src/extensions/manifest.ts:270
const raw = yaml.load(fs.readFileSync(manifestPath, "utf-8")) as unknown;
Why it matters. deserialises untrusted bytes into live objects
Fix. use json or yaml.safe_load
HIGHCode injection · code.deserialize · CWE-78, CWE-94, CWE-95
src/extensions/plugin-scaffold.ts:506
const m = yaml.load(fs.readFileSync(manifestPath, "utf8")) ?? {};
Why it matters. deserialises untrusted bytes into live objects
Fix. use json or yaml.safe_load
HIGHCode injection · code.deserialize · CWE-78, CWE-94, CWE-95
src/extensions/skills.ts:390
const doc = yaml.load(m[1]);
Why it matters. deserialises untrusted bytes into live objects
Fix. use json or yaml.safe_load
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
scripts/build.js:19
const proc = exec(fullCommand, {
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
MEDIUMNetwork egress · net.beacon_words · CWE-200, CWE-319
scripts/check-flows.mjs:23
export const DEMO_FLOWS = new Set(["beacon", "neon_shrine", "neon_shrine_cleanup"]);
MEDIUMNetwork egress · net.beacon_words · CWE-200, CWE-319
scripts/check-flows.mjs:34
export const DEMO_PATHS = new Set(["/Game/Flows/Beacon", "/Game/MCP_Home"]);
MEDIUMNetwork egress · net.beacon_words · CWE-200, CWE-319
src/flow/loader.ts:46
const PKG = "/Game/Flows/Beacon";
MEDIUMNetwork egress · net.beacon_words · CWE-200, CWE-319
src/flow/loader.ts:389
beacon: {
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
src/cli/daemon-cli.ts:62
const url = `http://127.0.0.1:${live.port}/ui/?token=${live.token}`;
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
src/cli/daemon-cli.ts:78
await fetch(`http://127.0.0.1:${d.port}/v1/shutdown`, { method: "POST", headers: auth }).catch(() => undefined);
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
src/cli/daemon-cli.ts:85
const res = await fetch(`http://127.0.0.1:${d.port}/v1/health`, { headers: auth });
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
src/cli/extension-cli.ts:32
await fetch(`http://127.0.0.1:${d.port}/v1/shutdown`, { method: "POST", headers: { authorization: `Bearer ${d.token}` } }).catch(() => undefined);
LOWInventory / provenance · inv.hidden_file · CWE-1104
tests/ue_mcp/.vsconfig
.vsconfig
Why it matters. hidden member outside the usual dotfiles
Fix. review its purpose
LOWCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
src/tools/specs/material.generated.ts:754
"description": "Folder for the new function (default /Game/Materials/Functions)"
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
LOWCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
src/tools/specs/material.generated.ts:2294
packagePath: z.string().optional().describe("Folder for a new material (default /Game/Materials) (build_material). Folder for the new material (default /Game/Materials) (create_material, create_materi
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
LOWCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
tests/unit/handler-spec-format.test.ts:75
return new Function("z", `return ${expr};`)(z) as z.ZodTypeAny;
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
LOWCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
tests/unit/handler-specs.test.ts:175
const generated = new Function("z", `return ${zodExpression(param)}`)(zodRuntime) as z.ZodTypeAny;
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
LOWInsecure crypto · crypto.weak_hash · CWE-327, CWE-338
scripts/bridge-target.mjs:59
const h = crypto.createHash("sha1")
LOWInsecure crypto · crypto.weak_hash · CWE-327, CWE-338
src/bridge/port.ts:43
const h = crypto.createHash("sha1").update(norm, "utf8").digest();
LOWInsecure crypto · crypto.weak_hash · CWE-327, CWE-338
src/daemon/discovery.ts:34
return crypto.createHash("sha1").update(normalizeProjectRoot(projectDir)).digest("hex").slice(0, 16);
LOWInsecure crypto · crypto.weak_hash · CWE-327, CWE-338
tests/unit/port.test.ts:52
const h = createHash("sha1").update(normalized, "utf8").digest();
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
scripts/generate-epic-actions.mjs:312
import type { ActionSpec } from "../../core/types.js";
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
scripts/generate-epic-actions.mjs:313
import { bp } from "../../surface/category-tool.js";

Gates applied: no_behavioural_pass.

Audited 2026-10-11 · audit v0.4.1 · source sha cd7f9ff300a5full audit observations/trust-audit/mcp-server/db-lyon__ue-mcp.json · Report an issue / request a re-scan
05

Audit history

Every audit this server has had. A grade with a past is a grade somebody is still checking.

DateSourceVerdictGradeScoreChange
2026-10-11cd7f9ff300a5BLOCKF54first audit
06

Questions

What is the ue-mcp MCP server?

Complete Unreal Engine development toolkit exposed as MCP tools.

What tools does ue-mcp expose?

4 in total: 4 read-only, 0 that write, and 0 that can delete or overwrite. Every one is listed on this page with its risk.

Is ue-mcp safe to connect to an agent?

No — not without reading the findings first. The audit graded it F (54/100) and found 6 critical or high issues in the source. Each one is listed on this page with the file and line it is on.

What credentials does ue-mcp need?

It reads ANTHROPIC_API_KEY, REGISTRY_PUBLISH_TOKEN and UE_MCP_AUTH_DIR from the environment. Give it a token scoped to the least it needs — an agent that can be talked into calling a tool can be talked into calling it with your credentials.

How does ue-mcp run?

It speaks stdio, so it runs as a local process your client starts. It is published on npm as ue-mcp at 1.4.0-beta.1.

How current is this page?

The grade is for one exact copy of the source (cd7f9ff300a5), read on 2026-10-11. The repository is watched and re-audited when it changes.

Advertisement