toolplane 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- toolplane-0.1.0/.github/workflows/ci.yml +36 -0
- toolplane-0.1.0/.github/workflows/pages.yml +59 -0
- toolplane-0.1.0/.gitignore +13 -0
- toolplane-0.1.0/AGENTS.md +63 -0
- toolplane-0.1.0/Makefile +52 -0
- toolplane-0.1.0/PKG-INFO +279 -0
- toolplane-0.1.0/README.md +255 -0
- toolplane-0.1.0/ROADMAP.md +105 -0
- toolplane-0.1.0/docs/architecture.md +224 -0
- toolplane-0.1.0/docs/code-mode-backends.md +252 -0
- toolplane-0.1.0/docs/development/release-checklist.md +31 -0
- toolplane-0.1.0/docs/index.md +180 -0
- toolplane-0.1.0/examples/README.md +39 -0
- toolplane-0.1.0/examples/ambient_cli_git.py +25 -0
- toolplane-0.1.0/examples/context7_remote.py +52 -0
- toolplane-0.1.0/examples/fastmcp_in_process.py +36 -0
- toolplane-0.1.0/examples/mcp_stdio_config.py +40 -0
- toolplane-0.1.0/examples/mcp_stdio_server.py +17 -0
- toolplane-0.1.0/examples/mixed_capability_report.py +142 -0
- toolplane-0.1.0/examples/scoped_namespaces_context7.py +153 -0
- toolplane-0.1.0/mkdocs.yml +13 -0
- toolplane-0.1.0/pyproject.toml +41 -0
- toolplane-0.1.0/src/toolplane/__init__.py +46 -0
- toolplane-0.1.0/src/toolplane/adapters/__init__.py +13 -0
- toolplane-0.1.0/src/toolplane/adapters/ambient_cli.py +329 -0
- toolplane-0.1.0/src/toolplane/adapters/cli_to_py.py +223 -0
- toolplane-0.1.0/src/toolplane/adapters/mcp.py +208 -0
- toolplane-0.1.0/src/toolplane/adapters/python.py +46 -0
- toolplane-0.1.0/src/toolplane/backends/__init__.py +7 -0
- toolplane-0.1.0/src/toolplane/backends/_python.py +12 -0
- toolplane-0.1.0/src/toolplane/backends/base.py +27 -0
- toolplane-0.1.0/src/toolplane/backends/local.py +178 -0
- toolplane-0.1.0/src/toolplane/backends/pyodide_deno.py +519 -0
- toolplane-0.1.0/src/toolplane/bridges/__init__.py +14 -0
- toolplane-0.1.0/src/toolplane/bridges/base.py +49 -0
- toolplane-0.1.0/src/toolplane/bridges/in_process.py +38 -0
- toolplane-0.1.0/src/toolplane/bridges/rpc.py +116 -0
- toolplane-0.1.0/src/toolplane/capabilities.py +190 -0
- toolplane-0.1.0/src/toolplane/discovery.py +91 -0
- toolplane-0.1.0/src/toolplane/errors.py +27 -0
- toolplane-0.1.0/src/toolplane/execution.py +50 -0
- toolplane-0.1.0/src/toolplane/registry.py +245 -0
- toolplane-0.1.0/src/toolplane/runtime.py +194 -0
- toolplane-0.1.0/tests/test_ambient_cli.py +97 -0
- toolplane-0.1.0/tests/test_bridges.py +154 -0
- toolplane-0.1.0/tests/test_cli_to_py_adapter.py +198 -0
- toolplane-0.1.0/tests/test_mcp_adapter.py +330 -0
- toolplane-0.1.0/tests/test_pyodide_deno_backend.py +132 -0
- toolplane-0.1.0/tests/test_toolplane.py +188 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Python ${{ matrix.python-version }}
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
python-version:
|
|
17
|
+
- "3.11"
|
|
18
|
+
- "3.12"
|
|
19
|
+
- "3.13"
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- name: Check out repository
|
|
23
|
+
uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- name: Set up Python
|
|
26
|
+
uses: actions/setup-python@v5
|
|
27
|
+
with:
|
|
28
|
+
python-version: ${{ matrix.python-version }}
|
|
29
|
+
|
|
30
|
+
- name: Set up uv
|
|
31
|
+
uses: astral-sh/setup-uv@v5
|
|
32
|
+
with:
|
|
33
|
+
enable-cache: true
|
|
34
|
+
|
|
35
|
+
- name: Run tests, examples, and docs
|
|
36
|
+
run: make ci
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
pages: write
|
|
13
|
+
id-token: write
|
|
14
|
+
|
|
15
|
+
concurrency:
|
|
16
|
+
group: pages
|
|
17
|
+
cancel-in-progress: false
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
build:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- name: Check out repository
|
|
24
|
+
uses: actions/checkout@v4
|
|
25
|
+
|
|
26
|
+
- name: Set up Python
|
|
27
|
+
uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.12"
|
|
30
|
+
|
|
31
|
+
- name: Set up uv
|
|
32
|
+
uses: astral-sh/setup-uv@v5
|
|
33
|
+
with:
|
|
34
|
+
enable-cache: true
|
|
35
|
+
|
|
36
|
+
- name: Build docs
|
|
37
|
+
run: make docs SITE_DIR=site
|
|
38
|
+
|
|
39
|
+
- name: Configure Pages
|
|
40
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
41
|
+
uses: actions/configure-pages@v5
|
|
42
|
+
|
|
43
|
+
- name: Upload Pages artifact
|
|
44
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
45
|
+
uses: actions/upload-pages-artifact@v3
|
|
46
|
+
with:
|
|
47
|
+
path: site
|
|
48
|
+
|
|
49
|
+
deploy:
|
|
50
|
+
needs: build
|
|
51
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
environment:
|
|
54
|
+
name: github-pages
|
|
55
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
56
|
+
steps:
|
|
57
|
+
- name: Deploy to GitHub Pages
|
|
58
|
+
id: deployment
|
|
59
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# toolplane Agent Guidance
|
|
2
|
+
|
|
3
|
+
This repo exists to build a controlled Python code-mode runtime where CLIs, MCP
|
|
4
|
+
tools, and libraries are normalized into one programmable tool surface.
|
|
5
|
+
|
|
6
|
+
## Product Direction
|
|
7
|
+
|
|
8
|
+
- Keep `toolplane` focused on the programmable tool surface, not a full agent
|
|
9
|
+
framework.
|
|
10
|
+
- Preserve the stable flow:
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
discover capabilities -> inspect schemas -> execute Python against a curated namespace
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- Keep discovery and execution separate. Backends execute code; registries
|
|
17
|
+
describe and dispatch capabilities.
|
|
18
|
+
- Treat MCP tools, CLI wrappers, Python functions, and host helpers as
|
|
19
|
+
capability adapters, not execution backends.
|
|
20
|
+
|
|
21
|
+
## Implementation Bias
|
|
22
|
+
|
|
23
|
+
- Build small vertical slices and validate them empirically.
|
|
24
|
+
- Avoid speculative plugin frameworks or broad abstractions before concrete
|
|
25
|
+
backends force them.
|
|
26
|
+
- Let each piece of code earn its place through behavior, tests, or an imminent
|
|
27
|
+
integration need.
|
|
28
|
+
- Keep tests focused on contracts and empirical behavior. No ceremonial testing.
|
|
29
|
+
|
|
30
|
+
## Pydantic vs Dataclass
|
|
31
|
+
|
|
32
|
+
Dataclasses are fine for simple internal value carriers.
|
|
33
|
+
|
|
34
|
+
Prefer Pydantic earlier when a model is likely to cross a boundary soon, not only
|
|
35
|
+
in a distant future. Boundary-crossing models include:
|
|
36
|
+
|
|
37
|
+
- execution results and structured errors
|
|
38
|
+
- backend capability/config objects
|
|
39
|
+
- capability manifests loaded from config or remote registries
|
|
40
|
+
- data sent over sandbox RPC
|
|
41
|
+
- adapter inputs/outputs that need validation or JSON schema export
|
|
42
|
+
|
|
43
|
+
If a dataclass is starting to need manual validation, serialization,
|
|
44
|
+
deserialization, or schema generation, move it to Pydantic instead of layering
|
|
45
|
+
more custom code around it.
|
|
46
|
+
|
|
47
|
+
## Backend Direction
|
|
48
|
+
|
|
49
|
+
- `local_unsafe` is only for development and shape validation.
|
|
50
|
+
- Monty is useful for safe small-tool orchestration, but it is not the default
|
|
51
|
+
answer for package-heavy code.
|
|
52
|
+
- Pyodide+Deno is the next likely default sandbox for package-capable snippets,
|
|
53
|
+
especially pandas/NumPy-style workflows.
|
|
54
|
+
- Docker, Modal, E2B, and Blaxel are needed for arbitrary CPython packages,
|
|
55
|
+
native system dependencies, subprocesses, GPUs, remote isolation, or
|
|
56
|
+
long-running jobs.
|
|
57
|
+
|
|
58
|
+
## Verification
|
|
59
|
+
|
|
60
|
+
- Prefer empirical smoke tests alongside unit tests.
|
|
61
|
+
- For backend work, prove the real execution path works, not just config
|
|
62
|
+
construction.
|
|
63
|
+
- Keep artifacts out of the repo unless they are intentional source files.
|
toolplane-0.1.0/Makefile
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
.PHONY: help test examples docs docs-serve ci build clean publish-check publish
|
|
2
|
+
|
|
3
|
+
UV ?= uv --no-config
|
|
4
|
+
PYTEST ?= pytest
|
|
5
|
+
SITE_DIR ?= /tmp/toolplane-site
|
|
6
|
+
PORT ?= 8000
|
|
7
|
+
PYPI_CHECK_URL ?= https://pypi.org/simple/
|
|
8
|
+
|
|
9
|
+
help:
|
|
10
|
+
@printf "toolplane commands\n"
|
|
11
|
+
@printf " make test Run the pytest suite\n"
|
|
12
|
+
@printf " make examples Run deterministic example smokes\n"
|
|
13
|
+
@printf " make docs Build MkDocs in strict mode\n"
|
|
14
|
+
@printf " make docs-serve Serve docs locally on PORT=%s\n" "$(PORT)"
|
|
15
|
+
@printf " make ci Run tests and strict docs build\n"
|
|
16
|
+
@printf " make build Build package artifacts\n"
|
|
17
|
+
@printf " make publish-check Dry-run publish built artifacts\n"
|
|
18
|
+
@printf " make publish Publish built artifacts to PyPI\n"
|
|
19
|
+
@printf " make clean Remove local generated artifacts\n"
|
|
20
|
+
|
|
21
|
+
test:
|
|
22
|
+
$(UV) run --no-project --with-editable ".[dev]" python -m $(PYTEST)
|
|
23
|
+
|
|
24
|
+
examples:
|
|
25
|
+
$(UV) run --no-project --with-editable . python examples/ambient_cli_git.py
|
|
26
|
+
$(UV) run --no-project --with-editable . python examples/fastmcp_in_process.py
|
|
27
|
+
$(UV) run --no-project --with-editable . python examples/mcp_stdio_config.py
|
|
28
|
+
|
|
29
|
+
docs:
|
|
30
|
+
$(UV) run --no-project --with-editable ".[docs]" mkdocs build --strict --site-dir $(SITE_DIR)
|
|
31
|
+
|
|
32
|
+
docs-serve:
|
|
33
|
+
$(UV) run --no-project --with-editable ".[docs]" mkdocs serve -a 127.0.0.1:$(PORT)
|
|
34
|
+
|
|
35
|
+
ci: test examples docs
|
|
36
|
+
|
|
37
|
+
build:
|
|
38
|
+
$(UV) build
|
|
39
|
+
|
|
40
|
+
publish-check: build
|
|
41
|
+
@if [ -n "$$PYPI_TOKEN" ]; then \
|
|
42
|
+
UV_PUBLISH_TOKEN="$$PYPI_TOKEN" $(UV) publish --dry-run --check-url $(PYPI_CHECK_URL) dist/*; \
|
|
43
|
+
else \
|
|
44
|
+
$(UV) publish --dry-run --check-url $(PYPI_CHECK_URL) dist/*; \
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
publish: build
|
|
48
|
+
@test -n "$(PYPI_TOKEN)" || (printf '%s\n' 'PYPI_TOKEN is required' >&2; exit 1)
|
|
49
|
+
@UV_PUBLISH_TOKEN="$(PYPI_TOKEN)" $(UV) publish --check-url $(PYPI_CHECK_URL) dist/*
|
|
50
|
+
|
|
51
|
+
clean:
|
|
52
|
+
rm -rf .pytest_cache build dist site *.egg-info
|
toolplane-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: toolplane
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A controlled Python code-mode runtime for programmable tool surfaces.
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: agents,cli,code-mode,mcp,runtime,tools
|
|
7
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Requires-Dist: cli-to-py>=0.1
|
|
17
|
+
Requires-Dist: fastmcp>=3.1
|
|
18
|
+
Requires-Dist: pydantic>=2.0
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
21
|
+
Provides-Extra: docs
|
|
22
|
+
Requires-Dist: mkdocs>=1.6; extra == 'docs'
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# toolplane
|
|
26
|
+
|
|
27
|
+
[](https://pypi.org/project/toolplane/)
|
|
28
|
+
[](https://pypi.org/project/toolplane/)
|
|
29
|
+
[](https://github.com/oneryalcin/toolplane/actions/workflows/ci.yml)
|
|
30
|
+
[](https://github.com/oneryalcin/toolplane/actions/workflows/pages.yml)
|
|
31
|
+
[](LICENSE)
|
|
32
|
+
|
|
33
|
+
A controlled Python code-mode runtime where CLIs, MCP tools, and libraries are
|
|
34
|
+
normalized into one programmable tool surface.
|
|
35
|
+
|
|
36
|
+
Full documentation: https://oneryalcin.github.io/toolplane/
|
|
37
|
+
|
|
38
|
+
## Why It Exists
|
|
39
|
+
|
|
40
|
+
Agents are strongest when they can use code as the control plane for real work:
|
|
41
|
+
looping, branching, filtering, retrying, aggregating, and combining tools
|
|
42
|
+
without bouncing through one tool call at a time.
|
|
43
|
+
|
|
44
|
+
Python already has the right orchestration model. What is missing is a clean
|
|
45
|
+
way to expose different tool sources through one curated runtime:
|
|
46
|
+
|
|
47
|
+
- CLI tools wrapped as Python callables.
|
|
48
|
+
- MCP server tools exposed as async Python functions.
|
|
49
|
+
- Regular Python libraries such as `pandas`, `httpx`, and project SDKs.
|
|
50
|
+
- Host-provided domain helpers with explicit permissions and limits.
|
|
51
|
+
|
|
52
|
+
`toolplane` is the runtime layer for that surface. The agent writes Python; the
|
|
53
|
+
host decides which capabilities exist, how credentials are handled, and what
|
|
54
|
+
resource or security boundaries apply.
|
|
55
|
+
|
|
56
|
+
## Relationship To cli-to-py
|
|
57
|
+
|
|
58
|
+
[`cli-to-py`](https://github.com/oneryalcin/cli-to-py) turns CLI binaries into
|
|
59
|
+
Python APIs. In `toolplane`, that is one adapter in a broader adapter stack.
|
|
60
|
+
|
|
61
|
+
The goal is that agent-written code should not need to care whether a capability
|
|
62
|
+
came from a CLI, an MCP server, or a normal Python package. It should see typed,
|
|
63
|
+
validated Python functions with predictable return values.
|
|
64
|
+
|
|
65
|
+
## Prior Art
|
|
66
|
+
|
|
67
|
+
[FastMCP Code Mode](https://gofastmcp.com/servers/transforms/code-mode) is a
|
|
68
|
+
strong reference point. It replaces a large MCP tool catalog with a smaller set
|
|
69
|
+
of meta-tools for progressive discovery and code execution: search for relevant
|
|
70
|
+
tools, inspect the schemas that matter, then execute Python that orchestrates
|
|
71
|
+
tool calls in a sandbox.
|
|
72
|
+
|
|
73
|
+
`toolplane` follows the same basic shape:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
discover capabilities -> inspect schemas -> execute Python against a curated namespace
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The difference is scope. FastMCP Code Mode is centered on MCP server tools.
|
|
80
|
+
`toolplane` aims to generalize that pattern across MCP tools, CLI wrappers, and
|
|
81
|
+
regular Python libraries.
|
|
82
|
+
|
|
83
|
+
[OpenAI Agents SDK sandboxes](https://developers.openai.com/api/docs/guides/agents/sandboxes)
|
|
84
|
+
are another useful reference: they separate the sandbox session/provider from
|
|
85
|
+
the tools exposed to the model. Toolplane follows that boundary too. Backends
|
|
86
|
+
execute code; adapters expose capabilities; bridges let sandboxed code call host
|
|
87
|
+
capabilities when direct local calls are not appropriate.
|
|
88
|
+
|
|
89
|
+
See [Code Mode Backends](docs/code-mode-backends.md) for the initial backend
|
|
90
|
+
strategy and [Architecture](docs/architecture.md) for the code organization
|
|
91
|
+
approach.
|
|
92
|
+
|
|
93
|
+
See [ROADMAP.md](ROADMAP.md) for the current sequencing.
|
|
94
|
+
|
|
95
|
+
## Design Goals
|
|
96
|
+
|
|
97
|
+
- Make code-mode agents useful for multi-step tool orchestration.
|
|
98
|
+
- Normalize heterogeneous tools into a Python-first API surface.
|
|
99
|
+
- Keep the exposed runtime curated rather than ambiently powerful.
|
|
100
|
+
- Preserve host control over credentials, authorization, filesystems, network
|
|
101
|
+
access, timeouts, and cancellation.
|
|
102
|
+
- Prefer structured return values and validation errors over raw text where
|
|
103
|
+
practical.
|
|
104
|
+
- Keep adapters small enough to be understandable and replaceable.
|
|
105
|
+
- Treat JSON as a wire format, not the programming model. Agent-written code
|
|
106
|
+
should compose normal Python values and callables.
|
|
107
|
+
- Make canonical capability ids qualified, and expose friendly Python aliases
|
|
108
|
+
only when they are unambiguous.
|
|
109
|
+
|
|
110
|
+
## Docs
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
make docs
|
|
114
|
+
make docs-serve
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Development
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
make test
|
|
121
|
+
make examples
|
|
122
|
+
make ci
|
|
123
|
+
make publish-check
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Publishing uses the same local release surface:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
PYPI_TOKEN=... make publish
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
See the [release checklist](docs/development/release-checklist.md) for the full
|
|
133
|
+
publish flow.
|
|
134
|
+
|
|
135
|
+
## Status
|
|
136
|
+
|
|
137
|
+
Early implementation. Toolplane can register Python functions, explicit
|
|
138
|
+
`cli-to-py` wrappers, and FastMCP-backed MCP tools, then discover them, inspect
|
|
139
|
+
schemas, and execute agent-written Python through:
|
|
140
|
+
|
|
141
|
+
- `local_unsafe`: development-only in-process execution.
|
|
142
|
+
- `pyodide-deno`: experimental Pyodide-in-Deno sandbox execution with package
|
|
143
|
+
loading and host bridge `call_tool` callbacks.
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
from toolplane import Toolplane
|
|
147
|
+
|
|
148
|
+
runtime = Toolplane()
|
|
149
|
+
|
|
150
|
+
@runtime.tool(tags={"math"})
|
|
151
|
+
def add(x: int, y: int) -> int:
|
|
152
|
+
"""Add two numbers."""
|
|
153
|
+
return x + y
|
|
154
|
+
|
|
155
|
+
result = await runtime.execute("""
|
|
156
|
+
value = await call_tool("add", {"x": 2, "y": 3})
|
|
157
|
+
return value
|
|
158
|
+
""")
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The current Pyodide+Deno smoke target works with pandas:
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
result = await runtime.execute(
|
|
165
|
+
"""
|
|
166
|
+
import pandas as pd
|
|
167
|
+
|
|
168
|
+
x = await call_tool("add", {"x": 2, "y": 3})
|
|
169
|
+
df = pd.DataFrame([{"value": x}])
|
|
170
|
+
return int(df["value"].sum())
|
|
171
|
+
""",
|
|
172
|
+
backend="pyodide-deno",
|
|
173
|
+
packages=["pandas"],
|
|
174
|
+
)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
CLI tools are also available through ambient lazy proxies in the execution
|
|
178
|
+
namespace. Toolplane does not parse every binary at startup; it resolves a CLI
|
|
179
|
+
through `cli-to-py` only when code first calls it:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
result = await runtime.execute("""
|
|
183
|
+
status = await git.status(short=True).text()
|
|
184
|
+
files = await git.diff(name_only=True, _=["HEAD~1", "HEAD"]).lines()
|
|
185
|
+
return {"status": status, "files": files}
|
|
186
|
+
""")
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
For binaries that are not valid Python identifiers, use the `cli` root:
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
result = await runtime.execute("""
|
|
193
|
+
version = await cli("docker-compose").version().text()
|
|
194
|
+
return version
|
|
195
|
+
""")
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
CLI tools can be exposed as capabilities during host setup:
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from cli_to_py import convert
|
|
202
|
+
from toolplane import Toolplane
|
|
203
|
+
|
|
204
|
+
runtime = Toolplane()
|
|
205
|
+
python = await convert("python3", subcommands=False)
|
|
206
|
+
|
|
207
|
+
runtime.register_cli(
|
|
208
|
+
"python_version",
|
|
209
|
+
python,
|
|
210
|
+
description="Return the Python interpreter version.",
|
|
211
|
+
tags={"python", "cli"},
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
result = await runtime.execute("""
|
|
215
|
+
version = await call_tool("python_version", {"version": True})
|
|
216
|
+
return version["stdout"] + version["stderr"]
|
|
217
|
+
""")
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
MCP servers can be exposed the same way. An in-process FastMCP app:
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
from fastmcp import FastMCP
|
|
224
|
+
from toolplane import Toolplane
|
|
225
|
+
|
|
226
|
+
runtime = Toolplane()
|
|
227
|
+
mcp = FastMCP("Demo")
|
|
228
|
+
|
|
229
|
+
@mcp.tool
|
|
230
|
+
def add(a: int, b: int) -> int:
|
|
231
|
+
"""Add two numbers."""
|
|
232
|
+
return a + b
|
|
233
|
+
|
|
234
|
+
await runtime.register_mcp("demo", mcp)
|
|
235
|
+
|
|
236
|
+
result = await runtime.execute("""
|
|
237
|
+
value = await demo.add(a=2, b=3)
|
|
238
|
+
return value
|
|
239
|
+
""")
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Or a standard `mcpServers` config, including stdio or remote HTTP servers:
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
await runtime.register_mcp_config({
|
|
246
|
+
"mcpServers": {
|
|
247
|
+
"context7": {
|
|
248
|
+
"url": "https://mcp.context7.com/mcp",
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Registered MCP tools get canonical ids such as `mcp:context7/get_docs` and safe
|
|
255
|
+
Python aliases such as `context7_get_docs`. They are also available through a
|
|
256
|
+
scoped namespace, so agent-written code can call `context7.get_docs(...)`
|
|
257
|
+
without caring that the capability came from MCP.
|
|
258
|
+
|
|
259
|
+
Host Python helpers can be grouped the same way:
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
from pathlib import Path
|
|
263
|
+
from toolplane import Toolplane
|
|
264
|
+
|
|
265
|
+
runtime = Toolplane()
|
|
266
|
+
|
|
267
|
+
def read_text(path: str) -> str:
|
|
268
|
+
return Path(path).read_text()
|
|
269
|
+
|
|
270
|
+
runtime.register_python_namespace("repo", {"read_text": read_text})
|
|
271
|
+
|
|
272
|
+
result = await runtime.execute("""
|
|
273
|
+
text = await repo.read_text(path="README.md")
|
|
274
|
+
return text.splitlines()[0]
|
|
275
|
+
""")
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
See [examples](examples/README.md) for executable FastMCP in-process, stdio
|
|
279
|
+
config, and live Context7 remote MCP smokes.
|