MCP server authoring guide
What this page is
How to add a new MCP server domain to AuroraSOC. Covers the tool surface contract, the launcher integration, the auth/audit model agents inherit, and the compose-stack integration so the new server is reachable.
Why it exists this way
The MCP plane is the boundary between agents and side-effecting infrastructure. Every privileged action an agent can take goes through an MCP tool; the tool runs in a separate process with its own policy. Adding a new domain (the WAF MCP that landed this wave is a recent example) is a recurring task; this page makes the recipe explicit.
How it works
A new domain takes four steps:
-
Define the tools. Create a module under packages/backend/aurorasoc/tools/. Each tool is a BeeAI
Toolsubclass with a Pydantic input schema. Inputs declare their fields explicitly;Anyis not acceptable. The tool docstring is the prompt the agent sees when picking tools, so write it in clear plain English. -
Register the server. Add a
server.pynext to the tool modules. The server class subclasses the existing MCP server base and lists the tools it exposes. The launcher reads the domain name fromMCP_DOMAINat startup and instantiates the matching server. Seeaurorasoc.tools.mcp_launcherfor the dispatch table. -
Wire the port and the compose service. Pick the next unused port in packages/backend/aurorasoc/config/settings.py under
MCPSettings; the unique-ports validator catches collisions at startup. Add a service stanza in infra/compose/docker-compose.yml following the existing pattern (build from the sharedagent.Dockerfile, setMCP_DOMAIN, depend on Redis). -
Update the orchestrator dependency. If the orchestrator needs the new domain to be present for a class of investigations, add it to the orchestrator's
depends_onin compose so the orchestrator fails loudly when the server is missing rather than silently degrading.
Auth and audit
Every tool call goes through require_permission (see
Permission taxonomy codegen).
The permission pair is declared at the tool level; the
launcher refuses to start a server whose tools declare an
unknown pair.
Every tool call is logged to the decisions:tools Redis
Stream by the base class, so the new domain inherits the
audit trail without writing any per-tool boilerplate.
What goes wrong
MCP_DOMAINenv var typo, the launcher exits with the list of known domains. Add the new one to the dispatch table inmcp_launcherif it is genuinely new.- Tool calls return but the orchestrator never sees them, most often the agent did not pick the tool because the docstring did not match its intent. Rewrite the docstring; agent prompts are the closest thing to a contract.
- A tool's permission check fails because the
require_permissionpair is not inpermission_taxonomy.py. Add the pair inaurora-schema, re-run the codegen, commit both in one atomic change per the codegen rules.