aeat.entrypoints.mcp._server module

MCP server shell: the thin protocol wiring over the SDK-independent core.

The Model Context Protocol runtime is an optional dependency behind the aeat-cli[agent] extra. serve() imports it lazily and, when it is absent, refuses with the install hint and a non-zero exit instead of raising a raw ModuleNotFoundError - the same graceful-degradation contract the Google, browser, and Anthropic integrations follow. The tool list, annotations, and the forbidden-live-write block are sourced from the SDK-independent core in this package; call_tool runs the deterministic CLI in a subprocess and returns its JSON envelope as structured content. Alongside the per-verb tools the server advertises the search / execute meta-tools and the harness.load floor tool (the universal operating-layer channel of ADR R4), and serves the operating layer through real resources handlers - the concrete aeat:// skill / rule / persona set, the three aeat://<kind>/{name} templates, and a read resolver - and through real prompts handlers: the guided-workflow catalogue and each prompt’s embedded skill / rules, derived from _prompts.py. build_server() owns that registration and is unit-tested against the real SDK without the stdio transport.

Per D1 of 2026-07-01-agent-harness-adr, the server also enforces the persona-scoped tool boundary declared in _persona_scope.py: serve() resolves the active persona once, at startup, from the AEAT_MCP_PERSONA environment variable via active_persona(). When a persona is active, _list_tools advertises only that persona’s in-scope tools (filter_descriptors_for_persona()) and _call_tool refuses an out-of-scope call (persona_scope_refusal()) before the global HITL confirmation_for_tool gate runs. An unset/blank env var preserves the full, unscoped tool surface - pre-D1 behaviour - for any un-personified session. See _persona_scope.py’s module docstring for the known family-granularity limitation (the three modelo-lifecycle personas share one manifest family and are not distinguished by this gate).

emit_missing_sdk_refusal()[source]

Write the agent-extra install hint to stderr and exit non-zero.

The graceful-degradation path taken when the MCP SDK is absent. Exposed so the refusal contract is unit-tested directly, in any environment, rather than relying on the SDK being absent at test time.

Return type:

None

serve()[source]

Run the aeat-mcp stdio server, or refuse if the SDK is not installed.

Resolves the active persona from AEAT_MCP_PERSONA before touching the SDK, so an invalid persona value fails with the instructive active_persona() error regardless of whether the optional SDK is installed.

Return type:

None

filter_descriptors_for_persona(descriptors, *, persona)[source]

Narrow descriptors to the tools in persona’s live-manifest scope.

persona=None (no active persona) returns descriptors unchanged - the full, unscoped surface. This is the _list_tools-side half of D1; it is SDK-independent and pure so it is unit-tested directly.

Return type:

tuple[McpToolDescriptor, ...]

Returns:

The subset of McpToolDescriptor entries in scope for persona.

Parameters:
persona_scope_refusal(*, persona, command_key)[source]

Return a refusal message when command_key is outside persona’s scope.

Returns None when the call may proceed to the global HITL gate: either no persona is active, or the command is in the active persona’s declared scope. This is the _call_tool-side half of D1; it runs BEFORE confirmation_for_tool() so an out-of-scope call is refused before HITL policy is even consulted.

Return type:

str | None

Parameters:
build_sdk_tools(descriptors)[source]

Adapt the SDK-independent descriptors into MCP SDK Tool objects.

Lazily imports the SDK types so the module still imports (and serve still refuses gracefully) when the aeat-cli[agent] extra is absent. Exposed at module level so the adaptation - including the mutability-to-annotation projection - is unit-tested against the real SDK types when they are installed.

Return type:

list[Tool]

Parameters:

descriptors (tuple[McpToolDescriptor, ...])

build_meta_sdk_tools()[source]

Build the SDK Tool objects for the core-surface meta-tools.

Lazily imports the SDK Tool type so the module still imports when the aeat-cli[agent] extra is absent. Exposed at module level so the meta-tool surface is unit-tested against the real SDK types when they are installed.

Return type:

list[Tool]

Returns:

The search, execute, toolsets, and describe mcp.types.Tool objects.

build_server(descriptors, *, persona=None, telemetry=None, surface_mode=SurfaceMode.CORE)[source]

Build the MCP Server with the tool, prompt, and resource handlers.

Registers the persona-scoped per-verb tools plus the search / execute meta-tools and the harness.load floor tool, and the operating-layer prompts and resources handlers so the server advertises those capabilities during negotiation. Extracted from the stdio runner so the handler registration and capability negotiation are unit-tested against the real SDK. persona scopes the per-verb tool list and the direct call-tool refusal per D1; the meta-tools and the floor tool are always advertised and execute applies the persona gate internally.

Return type:

Server

Returns:

The configured mcp.server.Server.

Parameters:
server_initialization_options(server)[source]

Build the negotiated initialization options for server.

Declares tools.listChanged because the console emits tools/list_changed when a toolset is activated (ADR mcp-progressive-discovery P3). Centralised so production (_run_server()) and the capability-posture conformance test negotiate the SAME capability set and cannot drift.

Return type:

object

Parameters:

server (Server)