jeevesagent.mcp
===============

.. py:module:: jeevesagent.mcp

.. autoapi-nested-parse::

   Model Context Protocol (MCP) integration.

   * :class:`MCPServerSpec` — declarative description of an MCP server
     (transport + connection details).
   * :class:`MCPClient` — wraps a single ``mcp.ClientSession``. The
     ``mcp`` SDK is imported lazily inside :meth:`MCPClient.connect` so
     the module loads without the ``mcp`` extra installed; the import
     fires only when actually connecting to a real server.
   * :class:`MCPRegistry` — implements
     :class:`~jeevesagent.core.protocols.ToolHost` over N MCP servers.
     Connects all servers in parallel through an
     :func:`anyio.create_task_group`, builds a tool name index with
     auto-disambiguation, and routes ``call(tool, args)`` to the right
     session.



Submodules
----------

.. toctree::
   :maxdepth: 1

   /api/jeevesagent/mcp/client/index
   /api/jeevesagent/mcp/registry/index
   /api/jeevesagent/mcp/spec/index


Classes
-------

.. autoapisummary::

   jeevesagent.mcp.MCPClient
   jeevesagent.mcp.MCPRegistry
   jeevesagent.mcp.MCPServerSpec


Package Contents
----------------

.. py:class:: MCPClient(spec: jeevesagent.mcp.spec.MCPServerSpec, *, session: Any | None = None)

   One client per MCP server. Holds the live ``ClientSession``.


   .. py:method:: aclose() -> None
      :async:


      Tear down the session and underlying transport.



   .. py:method:: call_tool(name: str, args: dict[str, Any]) -> Any
      :async:


      Invoke ``name`` with ``args``. Returns the SDK's CallToolResult.



   .. py:method:: connect() -> None
      :async:


      Open the transport and initialise the session.

      No-op if already connected (or a fake session was injected at
      construction time).



   .. py:method:: list_tools() -> list[Any]
      :async:


      Return whatever the SDK gave us — a list of tool descriptors.

      Each descriptor has ``name``, ``description``, ``inputSchema``.
      We don't translate to :class:`ToolDef` here — the registry does
      that, since it also assigns names with disambiguation.



   .. py:property:: is_connected
      :type: bool



   .. py:property:: name
      :type: str



   .. py:property:: spec
      :type: jeevesagent.mcp.spec.MCPServerSpec



.. py:class:: MCPRegistry(items: list[jeevesagent.mcp.spec.MCPServerSpec | jeevesagent.mcp.client.MCPClient] | None = None)

   Aggregates many :class:`MCPClient` instances into a single ``ToolHost``.


   .. py:method:: aclose() -> None
      :async:



   .. py:method:: call(tool: str, args: collections.abc.Mapping[str, Any], *, call_id: str = '') -> jeevesagent.core.types.ToolResult
      :async:



   .. py:method:: connect() -> None
      :async:


      Connect every client in parallel and rebuild the index.



   .. py:method:: list_tools(*, query: str | None = None) -> list[jeevesagent.core.types.ToolDef]
      :async:



   .. py:method:: refresh() -> None
      :async:


      Re-pull tool lists from every client and rebuild the index.



   .. py:method:: watch() -> collections.abc.AsyncIterator[jeevesagent.core.types.ToolEvent]
      :async:


      ``listChanged`` notifications. Not yet implemented; yields nothing.



   .. py:property:: server_names
      :type: list[str]



.. py:class:: MCPServerSpec

   How to find and talk to a single MCP server.

   Construct via the class methods :meth:`stdio` or :meth:`http` rather
   than the bare constructor — they enforce the right combination of
   fields per transport.


   .. py:method:: http(name: str, url: str, headers: dict[str, str] | None = None, *, description: str = '') -> MCPServerSpec
      :classmethod:


      Connect to ``url`` via Streamable HTTP transport.



   .. py:method:: stdio(name: str, command: str, args: list[str] | tuple[str, Ellipsis] | None = None, env: dict[str, str] | None = None, *, description: str = '') -> MCPServerSpec
      :classmethod:


      Spawn ``command`` as a subprocess and speak JSON-RPC over its stdio.



   .. py:attribute:: args
      :type:  tuple[str, Ellipsis]
      :value: ()



   .. py:attribute:: command
      :type:  str | None
      :value: None



   .. py:attribute:: description
      :type:  str
      :value: ''



   .. py:attribute:: env
      :type:  tuple[tuple[str, str], Ellipsis]
      :value: ()



   .. py:attribute:: headers
      :type:  tuple[tuple[str, str], Ellipsis]
      :value: ()



   .. py:attribute:: name
      :type:  str


   .. py:attribute:: transport
      :type:  Literal['stdio', 'http']


   .. py:attribute:: url
      :type:  str | None
      :value: None



