import { type EngineLookup } from "./engine-root.js"; export interface PluginInfo { name: string; contentDir: string; mountPoint: string; } export interface UeMcpConfig { /** Content roots to search by default (e.g. ["/Game/", "/GASP/", "/MyPlugin/"]) */ contentRoots?: string[]; /** Tool categories to disable (e.g. ["gas", "networking", "pcg"]) */ disable?: string[]; /** Native (Epic 5.8 ToolsetRegistry) tool surfacing. Enabled by default; * `exclude` names ue-mcp categories that should not be enriched with Epic * tools (they remain reachable via the `epic` gateway). */ nativeTools?: { enabled?: boolean; exclude?: string[]; }; /** Editor bridge WebSocket. `port` pins the bridge port instead of deriving * it from the project root path (see port.ts). Unset = derived per-worktree port. */ bridge?: { port?: number; /** Per-project equivalent of UE_MCP_HOST (#817). Unset = 127.0.0.1. */ host?: string; }; /** Per-project equivalents of UE_EDITOR_PATH and UE_BUILD_TOOL_PATH (#817). * Unset = the engine this project's EngineAssociation names. */ editor?: { path?: string; buildToolPath?: string; }; /** Per-project equivalent of UE_MCP_ENV (#817): the `ue-mcp..yml` * overlay this project merges. The env var still wins. */ env?: string; /** Per-asset exclusive locking for concurrent agents (see locking.ts). * Opt-in; disabled by default. */ locking?: { enabled?: boolean; ttlSeconds?: number; }; /** Optional HTTP surface for flow.run (#144). Disabled by default. */ http?: { enabled?: boolean; /** Default 7723. Bound to 127.0.0.1 only. */ port?: number; /** Override bind host. Defaults to 127.0.0.1 - do not expose externally. */ host?: string; }; /** Context-seeding strategy. `full` (default) lists every action inline; * `lean` keeps action names but serves descriptions on demand; `micro` * collapses everything behind one gateway tool. See lean-context.ts. */ context?: { strategy?: "full" | "lean" | "micro"; }; /** Play In Editor. `allowIgnoreBlueprintErrors` pre-authorizes * editor(play_in_editor_ignore_blueprint_errors) so it stops asking for * per-launch approval. Off by default. */ pie?: { allowIgnoreBlueprintErrors?: boolean; }; /** Per-plugin runtime config, keyed by plugin slug (package name minus * `ue-mcp-`). `groups` toggles whole flow groups (opt-out). See * plugin-groups.ts. */ pluginConfig?: Record; } & Record>; } /** * True when a path names a `.uproject` file, whatever case the extension is * written in. * * The extension is the only thing telling a project file from the directory * holding one, and every other project-keyed part of the server folds case * (the session key, the derived bridge port, the lockfile path). Reading it * case-sensitively here made `C:\PROJ\GAME.UPROJECT` resolve as a directory, * which fails as "project directory not found" on a path that exists. */ export declare function isUProjectPath(candidate: string): boolean; /** * Resolve a user-supplied path (a .uproject, or a directory holding one) to an * absolute .uproject path. Pure: it touches no state, so a caller that has to * move several things at once can validate the target first and leave * everything where it was when the path is bad. */ export declare function resolveUProjectPath(inputPath: string): string; /** * Read the merged `ue-mcp:` config for a project directory without loading the * project. Lets a caller learn a project's settings (its pinned bridge port, * say) before committing to the switch. */ /** * One `ue-mcp:` key that failed validation, and what was wrong with it. * * Kept per project directory so a reader who is already looking at the project * (project(get_status), the startup banner) can be told, rather than the user * having to find a warn() line on stderr that their MCP client writes to a log * nobody opens. */ export interface UeMcpConfigRejection { key: string; message: string; } /** Every `ue-mcp:` key that was dropped for this project, newest read wins. */ export declare function ueMcpConfigRejections(projectDir?: string | null): UeMcpConfigRejection[]; /** One line per rejected key, phrased for a person reading a terminal. */ export declare function describeConfigRejections(rejections: UeMcpConfigRejection[]): string[]; /** * Validate the merged block key by key, keeping the ones that parse. * * The whole-block safeParse this replaced was all-or-nothing: one malformed * key - `disable: gas` where a list is required - dropped the entire block, * so a pinned `bridge.port` went with it, the derived hash port was used * instead, and the client ended up on a different port from the editor, which * reads the same yaml and binds what `bridge.port` says. One typo, two * subsystems disagreeing about where the editor is. * * Each declared key is parsed against its OWN schema, so nothing reaches the * result unvalidated: a key that fails is dropped, never coerced and never * passed through. Undeclared keys pass through exactly as they did before, * because the schema is `.passthrough()` and always was. */ export declare function partitionUeMcpConfig(block: unknown): { config: UeMcpConfig; rejected: UeMcpConfigRejection[]; }; export declare function readUeMcpConfig(projectDir: string): UeMcpConfig; export declare class ProjectContext { projectPath: string | null; projectName: string | null; contentDir: string | null; engineAssociation: string | null; config: UeMcpConfig; get isLoaded(): boolean; setProject(inputPath: string): void; ensureLoaded(): void; resolveContentPath(assetPath: string): string; resolveContentDir(dirPath: string): string; getRelativeContentPath(absolutePath: string): string; get projectDir(): string | null; get configDir(): string | null; get pluginsDir(): string | null; /** * Everything the shared engine resolver needs to place this project. * * One resolver answers "which engine" for engine plugins, engine-source reads * and the build tool, so a source build beside the project cannot be visible * to one of them and invisible to the next (#959, #962). */ engineLookup(): EngineLookup; /** The engine tree this project belongs to, or null. */ resolveEngineRoot(): string | null; /** * Cache for discoverPlugins(). Engine-tree scans walk hundreds of dirs; * caching for the lifetime of the server is fine because plugin layouts * don't change while the editor is running. */ private _pluginCache; discoverPlugins(): PluginInfo[]; resolvePluginPath(mountPath: string): string | null; getRelativePluginPath(absolutePath: string): string | null; private parseUProject; private loadConfig; }