Skip to main content

A controlled Python code-mode runtime for programmable tool surfaces.

Project description

toolplane

PyPI version Python versions CI Docs License: MIT

A controlled Python code-mode runtime where CLIs, MCP tools, and libraries are normalized into one programmable tool surface.

Full documentation: https://oneryalcin.github.io/toolplane/

Why It Exists

Agents are strongest when they can use code as the control plane for real work: looping, branching, filtering, retrying, aggregating, and combining tools without bouncing through one tool call at a time.

Python already has the right orchestration model. What is missing is a clean way to expose different tool sources through one curated runtime:

  • CLI tools wrapped as Python callables.
  • MCP server tools exposed as async Python functions.
  • Regular Python libraries such as pandas, httpx, and project SDKs.
  • Host-provided domain helpers with explicit permissions and limits.

toolplane is the runtime layer for that surface. The agent writes Python; the host decides which capabilities exist, how credentials are handled, and what resource or security boundaries apply.

Relationship To cli-to-py

cli-to-py turns CLI binaries into Python APIs. In toolplane, that is one adapter in a broader adapter stack.

The goal is that agent-written code should not need to care whether a capability came from a CLI, an MCP server, or a normal Python package. It should see typed, validated Python functions with predictable return values.

Prior Art

FastMCP Code Mode is a strong reference point. It replaces a large MCP tool catalog with a smaller set of meta-tools for progressive discovery and code execution: search for relevant tools, inspect the schemas that matter, then execute Python that orchestrates tool calls in a sandbox.

toolplane follows the same basic shape:

discover capabilities -> inspect schemas -> execute Python against a curated namespace

The difference is scope. FastMCP Code Mode is centered on MCP server tools. toolplane aims to generalize that pattern across MCP tools, CLI wrappers, and regular Python libraries.

OpenAI Agents SDK sandboxes are another useful reference: they separate the sandbox session/provider from the tools exposed to the model. Toolplane follows that boundary too. Backends execute code; adapters expose capabilities; bridges let sandboxed code call host capabilities when direct local calls are not appropriate.

See Code Mode Backends for the initial backend strategy and Architecture for the code organization approach.

See ROADMAP.md for the current sequencing.

Design Goals

  • Make code-mode agents useful for multi-step tool orchestration.
  • Normalize heterogeneous tools into a Python-first API surface.
  • Keep the exposed runtime curated rather than ambiently powerful.
  • Preserve host control over credentials, authorization, filesystems, network access, timeouts, and cancellation.
  • Prefer structured return values and validation errors over raw text where practical.
  • Keep adapters small enough to be understandable and replaceable.
  • Treat JSON as a wire format, not the programming model. Agent-written code should compose normal Python values and callables.
  • Make canonical capability ids qualified, and expose friendly Python aliases only when they are unambiguous.

Docs

make docs
make docs-serve

Development

make test
make examples
make ci
make publish-check

Publishing uses the same local release surface:

PYPI_TOKEN=... make publish

See the release checklist for the full publish flow.

Status

Early implementation. Toolplane can register Python functions, explicit cli-to-py wrappers, and FastMCP-backed MCP tools, then discover them, inspect schemas, and execute agent-written Python through:

  • monty: the default; sandboxed Monty interpreter with no filesystem or network access, flat capability calls, and host bridge call_tool callbacks.
  • local_unsafe: development-only in-process execution.
  • pyodide-deno: experimental Pyodide-in-Deno sandbox execution with package loading and host bridge call_tool callbacks.
from toolplane import Toolplane

runtime = Toolplane()

@runtime.tool(tags={"math"})
def add(x: int, y: int) -> int:
    """Add two numbers."""
    return x + y

result = await runtime.execute("""
value = await call_tool("add", {"x": 2, "y": 3})
return value
""")

The current Pyodide+Deno smoke target works with pandas:

result = await runtime.execute(
    """
import pandas as pd

x = await call_tool("add", {"x": 2, "y": 3})
df = pd.DataFrame([{"value": x}])
return int(df["value"].sum())
""",
    backend="pyodide-deno",
    packages=["pandas"],
)

CLI tools are also available through ambient lazy proxies in the execution namespace. Toolplane does not parse every binary at startup; it resolves a CLI through cli-to-py only when code first calls it:

result = await runtime.execute("""
status = await git.status(short=True).text()
files = await git.diff(name_only=True, _=["HEAD~1", "HEAD"]).lines()
return {"status": status, "files": files}
""")

For binaries that are not valid Python identifiers, use the cli root:

result = await runtime.execute("""
version = await cli("docker-compose").version().text()
return version
""")

CLI tools can be exposed as capabilities during host setup:

from cli_to_py import convert
from toolplane import Toolplane

runtime = Toolplane()
python = await convert("python3", subcommands=False)

runtime.register_cli(
    "python_version",
    python,
    description="Return the Python interpreter version.",
    tags={"python", "cli"},
)

result = await runtime.execute("""
version = await call_tool("python_version", {"version": True})
return version["stdout"] + version["stderr"]
""")

MCP servers can be exposed the same way. An in-process FastMCP app:

from fastmcp import FastMCP
from toolplane import Toolplane

runtime = Toolplane()
mcp = FastMCP("Demo")

@mcp.tool
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b

await runtime.register_mcp("demo", mcp)

result = await runtime.execute("""
value = await demo.add(a=2, b=3)
return value
""")

Or a standard mcpServers config, including stdio or remote HTTP servers:

await runtime.register_mcp_config({
    "mcpServers": {
        "context7": {
            "url": "https://mcp.context7.com/mcp",
        }
    }
})

Registered MCP tools get canonical ids such as mcp:context7/get_docs and safe Python aliases such as context7_get_docs. They are also available through a scoped namespace, so agent-written code can call context7.get_docs(...) without caring that the capability came from MCP.

Host Python helpers can be grouped the same way:

from pathlib import Path
from toolplane import Toolplane

runtime = Toolplane()

def read_text(path: str) -> str:
    return Path(path).read_text()

runtime.register_python_namespace("repo", {"read_text": read_text})

result = await runtime.execute("""
text = await repo.read_text(path="README.md")
return text.splitlines()[0]
""")

See examples for executable FastMCP in-process, stdio config, and live Context7 remote MCP smokes.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

toolplane-0.2.0.tar.gz (84.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

toolplane-0.2.0-py3-none-any.whl (50.4 kB view details)

Uploaded Python 3

File details

Details for the file toolplane-0.2.0.tar.gz.

File metadata

  • Download URL: toolplane-0.2.0.tar.gz
  • Upload date:
  • Size: 84.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for toolplane-0.2.0.tar.gz
Algorithm Hash digest
SHA256 c87e579bbfe7b825dbd52fe734f1e7e3c69eb95f68c9c8ed2d215b222000c682
MD5 e6489c7be85eecb4ff7a77e23ffb1cb8
BLAKE2b-256 de72d119e873ed85e5adc104a13aba4d8519d846f23ea885c4d2a865b8181499

See more details on using hashes here.

File details

Details for the file toolplane-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: toolplane-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 50.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for toolplane-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 625a1f7a1c7a5b56328c1db65c137cc367d8f23be554ff0b2b1d7eb0c2be3b20
MD5 dcceb325770a6c96846272aad2e8dc85
BLAKE2b-256 257061883fe9ad56af942279a9235b50c34dadcc2af849eff189e346ba31f9ce

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page