cursus.mcp¶
cursus.mcp — Cursus as an agentic toolset.
This module exposes the cursus pipeline-engine capabilities as framework-neutral
tools: small, typed functions that take JSON-serializable arguments and return a
JSON-serializable ToolResult. The same tool functions
are consumable by any agent framework (MCP, OpenAI function-calling, Claude tool_use,
Bedrock), and a thin optional adapter (cursus.mcp.server) mounts them on an
actual MCP server.
Design (parallel to core / steps — it does not replace the human-facing
cursus.cli):
envelope— theToolResultsuccess/error envelope every tool returns.tools/*— one file per namespace (catalog, dag, config, compile, validate,execdoc, pipeline_catalog), each a set of pure tool functions.
schemas— not a module; each tool carries its JSON schema viaToolDef.registry— the canonical name →ToolDefmapping (catalog.list_steps…).server— optional MCP server adapter (imports themcpSDK lazily).
Quick start:
from cursus.mcp import get_registry, call_tool
reg = get_registry() # name -> ToolDef
reg["catalog.list_steps"].schema # JSON schema for the tool
result = call_tool("catalog.list_steps", {}) # or {"job_type": "training"}
result.ok # True / False
result.data # JSON-serializable payload
The registry is the single source of truth; the MCP server, an OpenAI tool list, and the CLI (in future) can all be generated from it.
- class ToolResult(ok, data=None, error=None, code=None, warnings=<factory>, next_steps=<factory>, remedy=None, meta=<factory>)[source]¶
Bases:
objectUniform tool outcome.
- data¶
JSON-serializable payload on success (None on error).
- Type:
Any | None
- code¶
Machine-readable error code on failure (e.g.
"not_found","invalid_input","internal_error").- Type:
str | None
- next_steps¶
Optional in-band guidance for an agent on a successful result — a list of
{"tool", "when", "why"}("args_hint"optional) entries naming the tool(s) the agent would typically call next. Lets the golden path live on the result instead of in the agent’s prompt.- Type:
List[Dict[str, Any]]
- remedy¶
Optional in-band recovery hint on a failure —
{"suggested_tools": [...], "fix_action": str}naming the tool(s) or action that typically resolves this error, so the agent does not have to reverse-engineer a remedy from the barecode.- Type:
Dict[str, Any] | None
- exception ToolError(message, code='tool_error', details=None)[source]¶
Bases:
ExceptionRaised inside a tool to signal a handled, user-facing failure.
The registry’s invoker catches this and converts it to
ToolResult.failure(...), preservingcodeanddetails. Use this for expected failures (bad input, not-found, validation) — let genuinely unexpected exceptions propagate so the invoker can wrap them as aninternal_error.
- class ToolDef(name, description, schema, handler, destructive=False, tags=(), when='', examples=(), writes=False, exec_code=False, network=False)[source]¶
Bases:
objectDeclarative definition of one agent-callable tool.
- 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:
- when¶
One-line “call this when …” cue — the trigger condition, complementing the what in
description. Optional; surfaced by help/describe and exporters.- Type:
- 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:
- 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__— viaget_tool().
- get_registry(force_reload=False)[source]¶
Return the canonical
name -> ToolDefmap, building it once and caching.
- get_namespaces()[source]¶
Return the
namespace -> one-line descriptionmap (built with the registry).
- list_tools(namespace=None)[source]¶
List all registered tools, optionally filtered to one namespace.
- call_tool(name, args=None)[source]¶
Invoke a registered tool by name with an argument dict.
Returns a
ToolResultin all cases — unknown tool, invalid args, handledToolError, 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
whencue andexamplesare 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’smetadataso an agent can group/route tools by lifecycle phase rather than scanning every description. Thewhen/examplesguidance is folded into the description (seerender_description()) so external clients see it too.
- export_mcp_tools(namespace=None)[source]¶
Export tools in MCP
list_toolsshape (name / description / inputSchema).Includes the phase
tagsso agents can filter by planner/validator/programmer, and folds thewhen/examplesguidance into the description (seerender_description()) so external MCP clients see it too.
Modules