Source code for klea_utils.mcp.schemas

#!/usr/bin/env python3
"""
Schemas shared by MCP servers and clients.

File: klea_utils/mcp/schemas.py

Copyright 2026 Ankur Sinha
Author: Ankur Sinha <sanjay DOT ankur AT gmail DOT com>
"""

from typing import Any

from pydantic import BaseModel, Field


[docs] class ToolInfo(BaseModel): """Metadata used to describe an MCP tool to clients and models. The ``read_only`` / ``destructive`` / ``idempotent`` / ``open_world`` fields map 1:1 to the standard MCP ``ToolAnnotations`` hints (``readOnlyHint`` / ``destructiveHint`` / ``idempotentHint`` / ``openWorldHint``); see https://fastmcp.wiki/en/servers/tools#mcp-annotations for what each hint means and how a client is expected to act on it. :func:`klea_utils.mcp.registry.register_tools` folds them onto the registered tool. """ # Detailed tool documentation for the LLM; falls back to the function docstring. description: str | None = None # Docstring-only form (no parameter list), for planner-style nodes that # reason about available tools without argument detail. short_description: str | None = None # The tool's input parameter schema (MCP ``inputSchema``). Used by the # tools picker to build a strict-safe per-tool call schema. input_schema: dict[str, Any] | None = None # Short human-facing label for UI and MCP clients. title: str | None = None # Categories used to group and filter tools. tags: set[str] | None = None # Argument names that are filesystem paths and must pass # check_path_access before the tool is invoked. Read client-side by the # tool caller node to gate tool calls before they reach the MCP server. checkpaths: list[str] | None = None # Additional application-specific metadata. meta: dict[str, Any] | None = None # Standard MCP ToolAnnotations hints (see the docstring link above). # Each is only set when declared (None = server does not express it). read_only: bool | None = None destructive: bool | None = None idempotent: bool | None = None open_world: bool | None = None
[docs] class ToolCallSchema(BaseModel): # One bound tool call (arguments filled in for a plan step). The picker # binds arguments only; tool choice is the planner's. tool: str = "" args: dict[str, Any] = Field(default_factory=dict) reason: str = "" #: Filesystem paths the picker declared the call will touch, used by the #: client-side permission gate as the ``expected`` discovery tier #: (ADR-0007 update 2026-10-08). Empty when the picker declared none. paths: list[str] = Field(default_factory=list) #: 1-based plan step this call belongs to (0 when there is no plan, e.g. #: RAG). Set by the agent's picker so a batch of calls can be attributed #: back to their originating step (ADR-0041). step: int = 0
[docs] class ToolCallsSchema(BaseModel): # The picker's structured output: the calls bound for the batch. The # per-run schema is built dynamically from the disclosed tools; this is # the shape it mirrors. tool_calls: list[ToolCallSchema] = Field(default_factory=list)