Python SDK#
Install a tool with the CLI, install the SDK in your Python app, load tools, and call them.
Goal#
Install the Python SDK and use load() to call a published tool from your app.
1) Install a tool#
From your project with an agent manifest:
# declare and install a specific tool version
agentpm install @zack/summarize@0.1.3
# or: edit agent.json → tools[] then run agentpm installThis resolves and prepares the tool under: .agentpm/tools/<namespace>/<name>/<version>
2) Install the Python SDK#
uv pip install agentpm
# or: pip install agentpm3) The load() function (core API)#
load() resolves the tool and returns a callable bound to a managed subprocess.
Basic usage#
from agentpm import load
summarize = load("@zack/summarize@0.1.3")
result = summarize({ "text": "Cats are elegant, enigmatic creatures." })
print(result)Type signature (overloads)#
from typing import Callable, TypedDict, Literal
ToolFunc = Callable[[JsonValue], JsonValue]
class LoadedWithMeta(TypedDict):
func: ToolFunc
meta: ToolMeta
def load(
spec: str,
with_meta: bool = False,
timeout: float | None = None,
tool_dir_override: str | None = None,
env: dict[str, str] | None = None,
) -> ToolFunc | LoadedWithMeta: ...Arguments#
spec: str— tool spec like@namespace/name@0.1.3with_meta: bool = False— include manifest metadata in the return valuetimeout: float | None = None— per-call hard cap in seconds (default:120.0)tool_dir_override: str | None = None— custom tool root (tests/local layouts)env: dict[str, str] | None = None— merged into the subprocess environment
Example with environment and timeout#
from agentpm import load
import os
capitalize = load(
"@zack/capitalize@0.1.11",
timeout=30.0,
env={ "OPENAI_API_KEY": os.environ["OPENAI_API_KEY"] },
)
out = capitalize({
"text": "Cats are some of the most amazing creatures—loving, curious, and a little wild at heart.",
"doKeywords": True,
"doSentiment": True,
"doSummary": True,
"maxSummaryChars": 200,
})
print(out)4) Loading with metadata#
Ask load() for manifest data you can hand to your agent runtime (for tool descriptions, IO schemas, etc.).
from agentpm import load
loaded = load("@zack/capitalize@0.1.11", with_meta=True)
func = loaded["func"]
meta = loaded["meta"]
out2 = func({ "text": "hi" })meta fields:
# meta: ToolMeta
# {
# "name": str,
# "version": str,
# "description": str | None,
# "inputs": JsonValue | None, # JSON Schema (Draft 2020-12)
# "outputs": JsonValue | None, # JSON Schema (Draft 2020-12)
# "runtime": { "type": "node|python", "version": "…" } | None
# }Using meta to configure an agent tool (example)#
from agentpm import load
import json
tools = [
load("@zack/capitalize@0.1.11", with_meta=True),
load("@zack/summarize@0.1.3", with_meta=True),
]
agent_tools = []
for t in tools:
func, meta = t["func"], t["meta"]
agent_tools.append({
"name": meta["name"],
"description": meta.get("description") or "AgentPM tool",
# helpful: concatenate description + IO shapes for better tool selection
"longDescription": f'{meta.get("description","")}\n\n'
f'Inputs: {json.dumps(meta.get("inputs"))}\n'
f'Outputs: {json.dumps(meta.get("outputs"))}',
"call": func, # the callable your agent runtime will invoke
})
# register agent_tools with your agent runtime’s tool loader5) Execution contract (how tools must behave)#
For a tool to work with the SDK, it must follow this process protocol:
- STDIN: SDK writes one JSON object (tool inputs) to the subprocess stdin.
- STDOUT: Tool writes exactly one JSON object (tool outputs) to stdout (typically the last line).
- STDERR: Any logs/diagnostics go to stderr.
- Exit code:
0on success; non-zero indicates failure.
(The full rationale and Node example are on the Node SDK page; the contract is identical here.)
6) What load() enforces for safety#
- Interpreter allow-list
- Allowed:
node,nodejs,python,python3, or versioned likepython3.11. - Otherwise: throws
ValueError('Unsupported agent.json.entrypoint.command …').
- Allowed:
- Interpreter on PATH
- Verifies the interpreter can be resolved (checks
PATH, akin toshutil.which).
- Verifies the interpreter can be resolved (checks
- Runtime ↔ entrypoint match (if runtime present)
- Ensures
runtime.typeagrees withentrypoint.command(e.g.,python↔python).
- Ensures
- Timeouts
- Per-call hard cap (
timeoutMs, default 120s).
- Per-call hard cap (
- Environment merging
- Merges manifest
entrypoint.env+envargument + safe base env.
- Merges manifest
7) How the subprocess is spawned (high level)#
- Working dir (
cwd):(tool_root / entrypoint.cwd or "."). - Isolated run dirs: per-call temp
run/folder with dedicatedHOMEandTMPDIR. - Python hardening flags: SDK injects
-I(isolated) and -B (no.pyc) for Python interpreters. - Node memory cap: if the interpreter is Node, injects
--max-old-space-size(default256MB). You can override via envAGENTPM_NODE_OLD_SPACE_MBor by adding the flag yourself. - Optional JITless (Node): enable with
AGENTPM_NODE_JITLESS=1or put--jitlessin args. - Environment: composed via
buildEnv(entry.env, opts.env, HOME, TMPDIR)to keep the tool isolated yet configurable.
If your tool needs more memory, you can add your own flag in entrypoint.args (e.g., --max-old-space-size=512 for Node).
8) Troubleshooting#
- Interpreter not found
- Ensure
node/pythonis onPATH. The error message prints the PATH searched.
- Ensure
- Timeouts
- Increase
timeoutMsinload()or setentrypoint.timeout_ms(SDK uses the per-call value).
- Increase
- Non-JSON output
- Make sure only one valid JSON object is written to stdout; logs go to stderr. On failure, the SDK saves
child.stdout/child.stderrunder the run dir and prints the tail of stderr.
- Make sure only one valid JSON object is written to stdout; logs go to stderr. On failure, the SDK saves
- Runtime mismatch
- Align
runtime.type(node/python) withentrypoint.command.
- Align
- Local testing / custom layouts
- Use
toolDirOverrideinload()to point to a local unpacked tool directory.
- Use
9) Best practices#
- Define strong IO schemas so agents construct correct arguments.
- Keep logs off stdout; stdout is reserved for the final JSON.
- Set sensible timeouts (
entrypoint.timeout_ms) and document required env vars in your tool README/manifest descriptions.