The ``domain_tools.py`` module
==============================
.. py:module:: ansys.cfx.mcp.common.domain_tools
Summary
-------
.. py:currentmodule:: domain_tools
.. tab-set::
.. tab-item:: Classes
.. list-table::
:header-rows: 0
:widths: auto
* - :py:obj:`~ansys.cfx.mcp.common.domain_tools.DomainToolSpec`
- Metadata describing a domain tool to a MCP leaf.
* - :py:obj:`~ansys.cfx.mcp.common.domain_tools.DomainTool`
- A registered domain tool — spec plus typed async handler.
.. tab-item:: Functions
.. list-table::
:header-rows: 0
:widths: auto
* - :py:obj:`~schema_from_signature`
- Derive a minimal JSON schema from a typed handler signature.
.. tab-item:: Attributes
.. list-table::
:header-rows: 0
:widths: auto
* - :py:obj:`~DomainToolHandler`
-
.. toctree::
:titlesonly:
:maxdepth: 1
:hidden:
DomainToolSpec
DomainTool
Description
-----------
Domain-tool registration framework.
A *domain tool* is one that does pure backend / catalog / product
introspection work with no dependency on a plan builder, journal,
learned constraints, or recipe registry. Every domain tool lives on an
MCP leaf. When an optional higher-level agent layer is installed it
reaches these tools over the MCP wire (an MCP client pool), not by
direct Python import. This package never imports that agent layer — the
dependency direction is strictly one-way (agent → this package).
The contract is intentionally small so that migrating a handler from
the agent layer is a mechanical rewrite. A domain tool is a
**typed** async function with the signature::
async def my_tool_impl(
backend: Backend,
*,
arg1: int,
arg2: str | None = None,
) -> dict[str, Any]: ...
paired with a name, description, and (optional) live-session flag::
DomainTool(
spec=DomainToolSpec(name="my_tool", description="..."),
handler=my_tool_impl,
)
The leaf calls :func:`FluidsLeafMCP._register_domain_tools` with a
list of these. The helper synthesises a wrapper whose
``inspect.Signature`` mirrors the handler minus the ``backend``
parameter so FastMCP can extract the JSON input schema from the
wrapper. There is no parallel JSON-schema-on-the-spec to keep in
sync with the handler signature.
The handler receives the leaf's active
:class:`ansys.cfx.mcp.common.backend.Backend` plus the validated
keyword arguments; there is no loop state, no journal, no plan
builder. A tool that needs the plan builder, journal, or recipe
registry is **agent-only** by definition and lives in the optional
agent layer instead — not in this package.
..
!! processed by numpydoc !!
Module detail
-------------
.. py:function:: schema_from_signature(handler: DomainToolHandler) -> dict[str, Any]
Derive a minimal JSON schema from a typed handler signature.
Drops the leading ``backend`` parameter and emits one
``properties`` entry per keyword-only argument. The mapping is
intentionally small: ``str -> "string"``, ``int -> "integer"``,
``float -> "number"``, ``bool -> "boolean"``, ``dict -> "object"``,
``list -> "array"`` plus ``None`` for ``Optional[...]`` arms.
Anything else falls back to a parameter without a ``type``
constraint — the handler is still callable, the schema just
documents fewer expectations.
:Parameters:
**handler** : :obj:`DomainToolHandler`
Callable inspected or registered by the helper.
:Returns:
:class:`python:dict`\[:class:`python:str`, :obj:`Any`]
Mapping containing the operation result.
..
!! processed by numpydoc !!
.. py:data:: DomainToolHandler