From 7b1dda7bf3088be717ab9eeabc14c745f7ec41c2 Mon Sep 17 00:00:00 2001 From: Timothy Date: Thu, 30 Apr 2026 15:52:16 -0700 Subject: [PATCH] fix: mcp registry initialization --- core/framework/loader/mcp_registry.py | 50 +++++++++++++++---- .../framework/pipeline/stages/mcp_registry.py | 15 ++---- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/core/framework/loader/mcp_registry.py b/core/framework/loader/mcp_registry.py index 21b51b9c..1fc54fcd 100644 --- a/core/framework/loader/mcp_registry.py +++ b/core/framework/loader/mcp_registry.py @@ -81,7 +81,30 @@ class MCPRegistry: # ── Initialization ────────────────────────────────────────────── def initialize(self) -> None: - """Create directory structure and default files if missing.""" + """Create directory structure, default files, and seed bundled servers. + + Every read path (queen orchestrator, pipeline stage, CLI, routes) + calls this — keeping the seeding here means a fresh ``HIVE_HOME`` + (e.g. the desktop's per-user dir under ``~/.config/Hive/users//`` + or ``~/Library/Application Support/Hive/users//``) is always + populated with ``hive_tools`` / ``gcu-tools`` / ``files-tools`` / + ``shell-tools`` before any agent code reads ``installed.json``. + Without this, ``load_agent_selection()`` resolves an empty registry + and emits "Server X requested but not installed" warnings even + though the server is bundled. + + Idempotent — already-installed entries are left untouched. + """ + self._bootstrap_io() + self._seed_defaults() + + def _bootstrap_io(self) -> None: + """Create the registry directory + empty config/installed files. + + Split out from ``initialize()`` so ``_seed_defaults()`` can call it + without re-entering the seeding logic (which would recurse via + ``_read_installed()`` → ``initialize()``). + """ self._base.mkdir(parents=True, exist_ok=True) self._cache_dir.mkdir(parents=True, exist_ok=True) @@ -92,21 +115,26 @@ class MCPRegistry: self._write_json(self._installed_path, {"servers": {}}) def ensure_defaults(self) -> list[str]: - """Seed the built-in local MCP servers (hive-tools, gcu-tools, files-tools). + """Public alias kept for the ``hive mcp init`` CLI command. - Idempotent — servers already present are left untouched. Skips seeding - entirely when the source-tree ``tools/`` directory cannot be located - (e.g. when Hive is installed from a wheel rather than a checkout). - - Returns the list of names that were newly registered. + Returns the list of newly-registered server names so the CLI can + print them. Same idempotent seeding logic as ``initialize()``. """ - self.initialize() + self._bootstrap_io() + return self._seed_defaults() + def _seed_defaults(self) -> list[str]: + """Idempotently register the bundled default local servers. + + Skips entirely when the source-tree ``tools/`` directory cannot + be located (e.g. wheel installs). Returns the list of names that + were newly registered. + """ # parents: [0]=loader, [1]=framework, [2]=core, [3]=repo root tools_dir = Path(__file__).resolve().parents[3] / "tools" if not tools_dir.is_dir(): logger.debug( - "MCPRegistry.ensure_defaults: tools dir %s missing; skipping default seed", + "MCPRegistry._seed_defaults: tools dir %s missing; skipping default seed", tools_dir, ) return [] @@ -123,7 +151,7 @@ class MCPRegistry: for canonical, stale in _STALE_DEFAULT_ALIASES.items(): if stale in existing and canonical not in existing: logger.info( - "MCPRegistry.ensure_defaults: removing stale alias '%s' (canonical: '%s')", + "MCPRegistry._seed_defaults: removing stale alias '%s' (canonical: '%s')", stale, canonical, ) @@ -146,7 +174,7 @@ class MCPRegistry: ) added.append(name) except MCPError as exc: - logger.warning("MCPRegistry.ensure_defaults: failed to seed '%s': %s", name, exc) + logger.warning("MCPRegistry._seed_defaults: failed to seed '%s': %s", name, exc) if added: logger.info("MCPRegistry: seeded default local servers: %s", added) diff --git a/core/framework/pipeline/stages/mcp_registry.py b/core/framework/pipeline/stages/mcp_registry.py index 922e50f6..386342c5 100644 --- a/core/framework/pipeline/stages/mcp_registry.py +++ b/core/framework/pipeline/stages/mcp_registry.py @@ -44,19 +44,10 @@ class McpRegistryStage(PipelineStage): from framework.loader.mcp_registry import MCPRegistry from framework.orchestrator.files import FILES_MCP_SERVER_NAME + # Bundled defaults (hive_tools / gcu-tools / files-tools / shell-tools) + # are seeded inside MCPRegistry.initialize(); resolve_for_agent below + # will find them even on a fresh HIVE_HOME. registry = MCPRegistry() - # Seed the bundled default servers (hive_tools, gcu-tools, files-tools, - # shell-tools) into the active HIVE_HOME's installed.json on first - # encounter. Idempotent — already-installed entries are left alone. - # Without this, a fresh HIVE_HOME (e.g. the desktop app's per-user - # dir under ~/.config/Hive/users/) only gets defaults via the - # `hive mcp` CLI, never via the runtime that consumes them. - try: - seeded = registry.ensure_defaults() - if seeded: - logger.info("[pipeline] McpRegistryStage: seeded defaults: %s", seeded) - except Exception as exc: - logger.warning("[pipeline] McpRegistryStage: ensure_defaults failed: %s", exc) mcp_loaded = False # 1. From agent.json mcp_servers refs