Skip to content

Core Engine

Pedro Gomes Branquinho edited this page Aug 5, 2026 · 1 revision

The Core Engine

Status: Stable See also: Creating-Addons | ADR-0007-hive-addons-architecture | Addon-Classpath-Discovery | Interfaces-and-Protocols

What hive-mcp actually is

hive-mcp is a host: a runtime that other things mount into. It is not a library you depend on, and it is not the thing you type into.

Three words get used for systems in this space, and they are not synonyms:

Term What it names Is hive-mcp this?
Harness The scaffolding that drives a model: prompt loop, tool dispatch, context management. Claude Code is a harness. Partly. hive-mcp is a harness for the agents it spawns — lings and drones get their loop, presets, budget and context from it. It is not the harness you talk to; that's your MCP client.
MCP server The wire protocol surface. Yes, but that's the transport, not the architecture.
Host The runtime addons mount into and are amalgamated by. Yes — this is the load-bearing one.

So: your harness (Claude Code, or any MCP client) talks to hive-mcp over MCP; hive-mcp hosts the addons that supply the actual capabilities; and hive-mcp is itself a harness for the sub-agents it runs. If you want one sentence: hive-mcp is an addon host that doubles as an agent harness.

┌────────────────────────────────────────────────────────────┐
│  Your harness — Claude Code, or any MCP client             │
└───────────────────────────┬────────────────────────────────┘
                            │  MCP protocol
┌───────────────────────────▼────────────────────────────────┐
│  hive-mcp — THE HOST (AGPL-3.0)                            │
│                                                            │
│   protocols  ·  registries  ·  orchestrators  ·  server    │
│   memory CRUD  ·  KG edges  ·  swarm  ·  session ritual    │
│                                                            │
│   ┌──── extension registry ────┐   ┌──── tool roots ────┐  │
│   │  generic ext-keys, noop    │   │  addon tools become │  │
│   │  defaults, addon-owned     │   │  new top-level      │  │
│   │  registrations             │   │  tools, no core edit│  │
│   └────────────────────────────┘   └─────────────────────┘  │
└───────────────────────────┬────────────────────────────────┘
                            │  IAddon
      ┌─────────────────────┼─────────────────────┐
      ▼                     ▼                     ▼
  addon (tools)        library (backend)     addon (tools)

The rule that shapes everything: closed for modification

hive-mcp is CLOSED for modification, OPEN for extension via IAddon.

When an addon needs a new capability — an extension key, a predicate, a handler, a schema, a compression strategy — all of that code goes in the addon. Zero changes to core.

This is not style advice; it is the boundary that makes the open-core split possible. Core is AGPL and public. The intelligence layers are private addons. If core had to grow a line every time an addon gained a capability, the boundary would leak on every release.

What core owns

  • Protocols and contracts
  • Registries and the loader
  • Orchestrators, the MCP server, transport
  • Memory CRUD, basic KG edges, swarm coordination, the session ritual
  • Noop defaults for every extension point

What core must never contain

  • Behaviour belonging to a named addon
  • A requiring-resolve of a concrete addon namespace — this is the smell; dependencies and knowledge flow addon → core, never the reverse
  • Addon-specific parameters bolted onto a core tool's input schema
  • Proprietary hooks, or comments describing them

How the seam actually works

Core defines a generic extension key and applies whatever is registered under it:

;; in core — addon-agnostic, this is the only legitimate core change
(ext/get-extension :catchup/wrap)

;; in the addon's IAddon/hooks — registered at initialize!, removed at shutdown!
{:catchup/wrap my-addon.catchup/wrap-fn}

Core never learns that my-addon exists. Ownership is tracked per-addon, so shutdown! deregisters cleanly.

Tool roots: extension without registration ceremony

The tool surface is assembled at advertisement time from three sources:

  1. Domain roots shipped by core — code, swarm, memory, project, fs, git, emacs, preset, web, events, multi, migrate-kanban
  2. Channel tools
  3. Addon-registered tools — filtered for collisions with domain names and for anything explicitly absorbed via config

Anything an addon registers that doesn't collide becomes a new top-level tool root automatically. No core edit, no allowlist entry, no release. A visibility gate (config.edn[:tool-roots :visible]) can then shrink the advertised surface without breaking callers: hidden tools stay dispatchable by name, they just leave tools/list.

The addon/library distinction

Manifests (META-INF/hive-addons/*.edn) carry :addon/kind:

Kind Meaning Examples
:addon Contributes user-facing tools knowledge, lsp-mcp, clj-kondo-mcp, basic-tools-mcp, emacs, ingestor
:library Pure backend or infrastructure — vector search, memory store, terminal, instrumentation qdrant, milvus, proximum, tmux, claude

The test is simple: anything contributing user-facing :tools is an :addon; pure backends are :library. This is deliberately not the same axis as :addon/type (native | mcp-bridge | external), which describes the integration mechanism instead.

Depending on the host without depending on the host

An addon must not :require any hive-mcp.* namespace. The host is a runtime, not a dependency. What the addon needs is expressed as a port:

  • Contracts to implementhive-addon (for IAddon itself) and hive-contracts
  • Host services consumed at runtime → soft resolution (requiring-resolve) behind a var-map, so the addon loads and degrades gracefully when the host is absent

A load-time require on the host is the violation. A soft runtime resolve is not.

The same discipline runs one level down: hive's own libraries depend on no concrete store — not datahike, not datalevin, not datascript. They ask hive-contracts for a datalog surface, and whichever backend addon is mounted answers. "It's only in-memory" is not an exemption: the rule is about the dependency edge, not about durability.

Why this is enforced so hard

One hard :require on the host makes a published addon artifact unloadable from a plain Maven fetch. One addon in this ecosystem had ~35 soft resolves and exactly one hard require — that single line cost the entire drop-in-addon story.

Open core, concretely

Layer Licence Home
hive-mcp (this repo) AGPL-3.0 GitHub
FOSS addons and libraries MIT GitHub
Intelligence / product addons Proprietary Private

Core ships the protocol and a working noop fallback for every extension point, so the FOSS stack alone is a complete, usable system — see FOSS-Quickstart. Private addons add ranking, learning, and domain intelligence by registering into the same seams any third-party addon uses.

Extending it

  1. Want addon behaviour at a core code path? Identify or add a generic ext-key that the core path consumes via ext/get-extension. That registry seam is the only legitimate core change.
  2. Put the logic in the addon; expose it through the addon's hooks map.
  3. Need new tools? Ship whole tool-defs from the addon — don't edit a core tool's schema.
  4. Never requiring-resolve a concrete addon namespace from core.

Full walkthrough with a scaffolded project: Creating-Addons.

Clone this wiki locally