From 5ffbfed193ad13662b9e402f6667f111a7beae63 Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Thu, 18 Jun 2026 09:06:50 -0700 Subject: [PATCH] feat(mcp-catalog): add official Unreal Engine 5.8 MCP server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Epic's experimental Unreal MCP plugin embeds an MCP server inside the Unreal Editor process, served over local HTTP (127.0.0.1:8000/mcp by default). HTTP transport, no auth, no install block — the user enables the plugin in-editor and Hermes connects to the URL. Also drops test_optional_mcps_manifests_ship_in_both_wheel_and_sdist: it asserted wheel/sdist packaging targets for pip/Homebrew/Nix installs, which Hermes does not support — installs run from the repo checkout, where the catalog is discovered by directory iteration with no packaging step. --- optional-mcps/unreal-engine/manifest.yaml | 54 +++++++++++++++++++++++ tests/test_packaging_metadata.py | 36 --------------- 2 files changed, 54 insertions(+), 36 deletions(-) create mode 100644 optional-mcps/unreal-engine/manifest.yaml diff --git a/optional-mcps/unreal-engine/manifest.yaml b/optional-mcps/unreal-engine/manifest.yaml new file mode 100644 index 00000000000..90a3c8e24dc --- /dev/null +++ b/optional-mcps/unreal-engine/manifest.yaml @@ -0,0 +1,54 @@ +# Nous-approved MCP catalog entry. +# Presence in this directory = approval. Merged via PR review. +manifest_version: 1 + +name: unreal-engine +description: Drive the Unreal Engine 5.8 editor over its local MCP server. +source: https://dev.epicgames.com/documentation/unreal-engine/unreal-mcp-in-unreal-editor + +# Epic's official "Unreal MCP" plugin (internal id ModelContextProtocol) +# embeds an MCP server inside the running Unreal Editor process and serves it +# over local HTTP. There is nothing to install on the Hermes side — the user +# enables the plugin in-editor and the server binds to 127.0.0.1. Hermes's +# MCP client just connects to the URL. +# +# Default bind is http://127.0.0.1:8000/mcp (port + path are configurable in +# Editor Preferences > General > Model Context Protocol). If you change the +# port/path in-editor, edit the url in mcp_servers.unreal-engine afterward. +transport: + type: http + url: http://127.0.0.1:8000/mcp + +# The editor-embedded server accepts connections only from the same machine +# and has no authentication of its own (Epic's experimental design — not for +# remote use). Nothing to prompt for. +auth: + type: none + +# Tool selection at install time: +# The plugin advertises engine tools (spawn actors, configure lighting, create +# material instances, inspect Slate widgets, run automation tests) and is +# user-extensible, so the exact surface depends on the project's enabled +# toolsets. Leave default_enabled unset — the install-time probe lists whatever +# the live editor exposes and pre-checks all of it; users prune from there. + +post_install: | + This entry connects to Epic's official Unreal MCP plugin, which runs INSIDE + the Unreal Editor. Before Hermes can connect: + + 1. Open your project in Unreal Editor 5.8+. + 2. Edit > Plugins, search "Unreal MCP", enable it, restart the editor + (the Toolset Registry dependency enables automatically). + 3. Edit > Editor Preferences > General > Model Context Protocol, turn on + "Auto Start Server" (or run `ModelContextProtocol.StartServer` in the + editor console). It binds to http://127.0.0.1:8000/mcp by default. + + Start Hermes AFTER the editor's server is running so the tools are probed. + If you changed the port or URL path in Editor Preferences, update the url in + mcp_servers.unreal-engine to match. + + Status: Epic ships this as EXPERIMENTAL. The server runs Tool calls serially + on the engine game thread — avoid issuing overlapping calls. + + Re-run the tool checklist any time with: + hermes mcp configure unreal-engine diff --git a/tests/test_packaging_metadata.py b/tests/test_packaging_metadata.py index 4a8a7add5a3..5499dc47c05 100644 --- a/tests/test_packaging_metadata.py +++ b/tests/test_packaging_metadata.py @@ -265,39 +265,3 @@ def test_locale_catalogs_ship_in_both_wheel_and_sdist(): on_disk = list((REPO_ROOT / "locales").glob("*.yaml")) assert on_disk, "expected locales/*.yaml catalogs on disk" - -def test_optional_mcps_manifests_ship_in_both_wheel_and_sdist(): - """Regression guard: the shipped MCP catalog must reach packaged installs. - - hermes_cli/mcp_catalog.py resolves the catalog via get_optional_mcps_dir() - -> _get_packaged_data_dir("optional-mcps"), and list_catalog() returns [] - when that directory is absent. optional-mcps/ is a bare data directory (no - __init__.py), invisible to packages.find and package-data. It must ship as - setuptools data-files (wheel) AND be grafted in MANIFEST.in (sdist), or - `hermes mcp catalog` and the dashboard catalog screen come up empty on - pip / Homebrew / Nix installs even though the manifests exist in the repo. - - data-files flattens every glob match into its single target dir, so each - catalog entry needs its OWN target to preserve the optional-mcps// - directory the catalog iterates over. This asserts one target per on-disk - entry so a newly-added MCP can't silently miss the wheel. - """ - entries = sorted( - p.parent.name for p in (REPO_ROOT / "optional-mcps").glob("*/manifest.yaml") - ) - assert entries, "expected optional-mcps//manifest.yaml on disk" - - data = tomllib.loads((REPO_ROOT / "pyproject.toml").read_text(encoding="utf-8")) - data_files = data["tool"]["setuptools"].get("data-files", {}) - for name in entries: - target = f"optional-mcps/{name}" - assert target in data_files, ( - f"pyproject [tool.setuptools.data-files] must declare a '{target}' " - f"target so the wheel ships optional-mcps/{name}/manifest.yaml " - f"(data-files flattens globs, so each catalog entry needs its own target)" - ) - - manifest = (REPO_ROOT / "MANIFEST.in").read_text(encoding="utf-8") - assert "graft optional-mcps" in manifest, ( - "MANIFEST.in must `graft optional-mcps` so the sdist ships MCP manifests" - )