Skip to content

feat(mcp): read-only Business Graph tools (nil_graph/cycles/overview/instances/activity)#58

Open
Basheirkh wants to merge 3 commits into
mainfrom
feat/mcp-brain-tools
Open

feat(mcp): read-only Business Graph tools (nil_graph/cycles/overview/instances/activity)#58
Basheirkh wants to merge 3 commits into
mainfrom
feat/mcp-brain-tools

Conversation

@Basheirkh

Copy link
Copy Markdown
Contributor

Why

An agent asked "show my policies" was answering inconsistently — sometimes reading the brain (correct), sometimes asking the kernel "is there a policy.* verb?", finding none, and wrongly concluding "no policies exist." Same data, different answers by phrasing. Root cause: the NIL MCP exposed only kernel/action tools (nil_describe/propose/commit + automations) — there was no tool to READ the Business Graph (cycles, policies, entities, instances), so the agent improvised (curl, file search) with non-deterministic results.

What

Five read-only tools that relay to the brain read-model's stable /api/graph/* GET endpoints:

  • nil_graph(kind?) — graph nodes; kind='policy' is the deterministic "show my policies" (policies are nodes, not verbs)
  • nil_cycles() — cycles + goal/metrics/members
  • nil_overview() — graph summary counts
  • nil_instances() — live tallies incl. overdue / awaiting_approval
  • nil_activity() — what changed (latest diff)

BrainTools is a thin HTTP relay — the kernel stays decoupled from the brain (relays HTTP, never imports it). Env-gated by NIL_BRAIN_URL (+ NIL_BRAIN_TOKEN/NIL_BRAIN_TENANT); when unset the tools simply aren't registered (no behavior change for existing deploys).

Tests

11 new (test_mcp_brain_tools.py, MockTransport): endpoint routing, tenant application, kind filtering, error degradation, registration. Full MCP suite green (69 passed).

Verified live

Deployed to wosool.ai: Hermes now discovers 17 tools, and nil_graph(kind='policy') returns the Payment-Approval policy (count 1) — the exact query that previously returned "no policies."

Stacks on #57.

AI Bot added 3 commits June 24, 2026 17:22
…se form is reachable

The panel was hidden when zero automations existed — but the '+ New cross-system
automation' button and operator-token field live inside it, so a first automation
could never be created. Always render it, with an empty state.
…ing allowlist

FastMCP only honors transport_security as a constructor kwarg and otherwise
auto-enables a localhost-only allowlist, so the remote MCP 421s ('Invalid Host
header') when reached by its container/service name or a public mcp.* host
behind a reverse proxy. build_server now accepts allowed_hosts and threads a
TransportSecuritySettings; build_asgi_app reads NIL_MCP_ALLOWED_HOSTS (JSON or
comma-separated, host:* wildcard ok). /mcp stays bearer-gated.
…instances/activity)

Adds deterministic brain-read tools so an agent answers 'show my policies / cycles /
what changed / what's overdue' from the brain read-model instead of improvising (curl,
file search) or mistaking a graph question for a missing kernel verb. BrainTools is a
thin HTTP relay to the brain's /api/graph/* GET endpoints (kernel stays decoupled — it
relays, never imports the brain). Env-gated by NIL_BRAIN_URL (+ NIL_BRAIN_TOKEN/TENANT);
policies are surfaced as nodes via nil_graph(kind='policy'). 11 new tests; full MCP suite green.

Stacks on #57 (fix/mcp-allowed-hosts).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant