feat(mcp-catalog): add official Unreal Engine 5.8 MCP server

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.
This commit is contained in:
teknium1 2026-06-18 09:06:50 -07:00 committed by Teknium
parent 58ad6942d9
commit 5ffbfed193
2 changed files with 54 additions and 36 deletions

View file

@ -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

View file

@ -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/<name>/
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/<name>/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"
)