cursus.mcp.registry

The canonical tool registry for cursus.mcp.

Every tool is declared as a ToolDef (name, description, JSON schema, handler) and collected into one registry keyed by dotted name ("catalog.list_steps"). The registry is the single source of truth: the MCP server, an OpenAI/Claude tool list, and (eventually) the CLI can all be generated from it.

call_tool is the in-process invoker. It enforces the tool contract: - light JSON-schema validation of arguments (required keys, no unknown keys), - handlers may raise ToolError for handled failures (-> ToolResult.error), - any other exception is caught and wrapped as an internal_error ToolResult so a

tool call never crashes the caller.

class ToolDef(name, description, schema, handler, destructive=False, tags=(), when='', examples=(), writes=False, exec_code=False, network=False)[source]

Bases: object

Declarative definition of one agent-callable tool.

name

Dotted, namespaced, unique tool name (e.g. "compile.dag").

Type:

str

description

One/two-sentence description for the agent (what + when to call).

Type:

str

schema

JSON Schema (draft-07 style {"type": "object", "properties": {...}, "required": [...]}) describing the arguments.

Type:

Dict[str, Any]

handler

Callable taking the validated arg dict, returning a ToolResult.

Type:

Callable[[Dict[str, Any]], cursus.mcp.envelope.ToolResult]

namespace

Leading segment of name (derived; "compile").

destructive

True if the tool mutates external state (e.g. upserts/starts a SageMaker pipeline). Agents/servers may gate these behind confirmation.

Type:

bool

tags

Free-form labels (e.g. "planner", "validator") for grouping.

Type:

tuple

when

One-line “call this when …” cue — the trigger condition, complementing the what in description. Optional; surfaced by help/describe and exporters.

Type:

str

examples

Copy-paste invocation strings showing real calls, e.g. 'catalog.list_steps {"framework": "xgboost"}  # every XGBoost step'. Stored as a tuple so this frozen dataclass field is truly immutable (a list could be mutated in place); surfaced by help/describe and folded into exported tool descriptions so external MCP/OpenAI clients see them too.

Type:

tuple

name: str
description: str
schema: Dict[str, Any]
handler: Callable[[Dict[str, Any]], ToolResult]
destructive: bool = False
tags: tuple = ()
when: str = ''
examples: tuple = ()
writes: bool = False
exec_code: bool = False
network: bool = False
property namespace: str
property wire_name: str

The on-the-wire tool name for MCP hosts (dotted . -> __).

Host tool-calling APIs (Anthropic ^[a-zA-Z0-9_-]{1,128}$, OpenAI ^[a-zA-Z0-9_-]+$) reject the . in the internal dotted names, so every externally-exposed name uses __ instead. It round-trips unambiguously — no tool or namespace name contains a literal __ — via get_tool().

get_registry(force_reload=False)[source]

Return the canonical name -> ToolDef map, building it once and caching.

get_namespaces()[source]

Return the namespace -> one-line description map (built with the registry).

list_tools(namespace=None)[source]

List all registered tools, optionally filtered to one namespace.

get_tool(name)[source]

Look up a tool by its internal dotted name OR its on-the-wire __ name.

call_tool(name, args=None)[source]

Invoke a registered tool by name with an argument dict.

Returns a ToolResult in all cases — unknown tool, invalid args, handled ToolError, or unexpected exception are all converted to error envelopes.

render_description(td)[source]

Compose a tool’s full-text description: description + ‘When’ + ‘Examples’.

External MCP/OpenAI clients only receive a single description string per tool, so the when cue and examples are folded in here (they are otherwise only reachable via the in-process help/describe tools). In-process agents get the fields structured; this keeps external agents from missing the usage guidance.

export_openai_tools(namespace=None)[source]

Export tools in OpenAI / Claude function-calling shape.

The phase tags (planner/validator/programmer) are surfaced under the function’s metadata so an agent can group/route tools by lifecycle phase rather than scanning every description. The when/examples guidance is folded into the description (see render_description()) so external clients see it too.

export_mcp_tools(namespace=None)[source]

Export tools in MCP list_tools shape (name / description / inputSchema).

Includes the phase tags so agents can filter by planner/validator/programmer, and folds the when/examples guidance into the description (see render_description()) so external MCP clients see it too.