Python SDK#
Install packages with the CLI, load tools, and inspect installed agents, skills, knowledge packages, memory blueprints, instruction profiles, and loops from Python.
Goal#
Install the Python SDK and use:
load()to call a published toolload_agent()to inspect an installed agent package and its resolved packagesload_skill()to inspect an installed skill package and its resolved toolsload_knowledge()to inspect an installed knowledge package and its mode-specific metadataload_memory()to inspect an installed memory blueprint package and its generated contractsload_profile()to inspect an installed instruction profile package and its authored metadataload_loop()to inspect an installed loop package and its authored orchestration metadata
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>
You can also install a published agent package directly:
agentpm install @zack/support-agent@0.1.0That writes:
- the installed agent under
.agentpm/agents/<namespace>/<name>/<version> - its resolved knowledge packages under
.agentpm/knowledge/<namespace>/<name>/<version> - its resolved memory packages under
.agentpm/memory/<namespace>/<name>/<version> - its resolved profile packages under
.agentpm/profiles/<namespace>/<name>/<version> - its resolved loop package under
.agentpm/loops/<namespace>/<name>/<version> - its resolved skills under
.agentpm/skills/<namespace>/<name>/<version> - its resolved tools under
.agentpm/tools/<namespace>/<name>/<version>
You can also install a published skill package directly:
agentpm install @zack/incident-commander@0.1.0That writes:
- the installed skill under
.agentpm/skills/<namespace>/<name>/<version> - its resolved tools under
.agentpm/tools/<namespace>/<name>/<version>
You can also install a published knowledge package directly:
agentpm install @zack/python-docs@0.1.0That writes:
- the installed knowledge package under
.agentpm/knowledge/<namespace>/<name>/<version>
You can also install a published memory blueprint package directly:
agentpm install @zack/profile-memory@0.1.0That writes:
- the installed memory package under
.agentpm/memory/<namespace>/<name>/<version>
You can also install a published profile package directly:
agentpm install @zack/support-style@0.1.0That writes:
- the installed profile package under
.agentpm/profiles/<namespace>/<name>/<version>
You can also install a published loop package directly:
agentpm install @zack/incident-response-loop@1.0.0That writes:
- the installed loop package under
.agentpm/loops/<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) Loading an installed agent package#
Use load_agent() when you want metadata for a registry-installed agent package plus the concrete package refs it resolved to at install time.
from agentpm import load, load_agent, load_skill
agent = load_agent("@zack/support-agent@0.1.0")
first_skill = agent["resolvedSkills"][0]
skill = load_skill(f'{first_skill["name"]}@{first_skill["version"]}')
first_tool = skill["resolvedTools"][0]
tool = load(f'{first_tool["name"]}@{first_tool["version"]}')What load_agent() returns:
- the installed agent manifest
- the installed agent root path
resolvedKnowledge, which are the exact knowledge package refs represented inagent.lockresolvedMemory, which are the exact memory package refs represented inagent.lockresolvedProfiles, which are the exact profile package refs represented inagent.lockresolvedLoop, which is the exact singular loop ref represented inagent.lock- reserved refs (
knowledge,memory,profiles) from the lockfile metadata path resolvedTools, which are the exact tool package refs represented inagent.lockresolvedSkills, which are the exact skill package refs represented inagent.lock
Compatibility note:
resolvedKnowledgeis populated from the modern first-classroot.knowledgeentries inagent.lock.reserved.knowledgeis legacy pass-through metadata from older lockfile shapes. For current installs, treatresolvedKnowledgeas authoritative and expectreserved.knowledgeto usually be empty.- If your workspace still has an older lockfile shape where Knowledge refs only exist under
reserved.knowledge, rerunagentpm installto rewrite the lockfile. resolvedLoopis populated from the modern first-classroot.loopentry inagent.lock.resolvedProfilesis populated from the modern first-classroot.profilesentries inagent.lock.reserved.profilesis legacy pass-through metadata from older lockfile shapes. For current installs, treatresolvedProfilesas authoritative and expectreserved.profilesto usually be empty.manifest["loop"]andmanifest["bindings"]preserve the authored declarative metadata from the installedagent.json.
What it does not do:
- it does not execute the agent package
- it does not orchestrate the tools for you
- it does not load the local project
./agent.json
That makes load_agent() the Python mirror of the Node SDK’s loadAgent():
- inspect an installed agent package
- get the exact resolved package refs
- choose which skills and tools to load into your runtime
6) Loading an installed skill package#
Use load_skill() when you want metadata for a registry-installed skill package plus the concrete tool refs it resolved to at install time.
from agentpm import load_skill
skill = load_skill("@zack/incident-commander@0.1.0")
print(skill["entrypointPath"])
print(skill["entrypointContent"])
print(skill["references"])
print(skill["scripts"])
print(skill["resolvedTools"])load_skill() returns an inspectable Skill object. Skills are not runnable SDK objects by themselves.
7) Loading an installed knowledge package#
Use load_knowledge() when you want metadata for a registry-installed Knowledge package plus the canonical package-relative paths it declares.
from agentpm import load_knowledge
knowledge = load_knowledge("@zack/python-docs@0.1.0")
print(knowledge["knowledge"]["mode"])
print(knowledge["documentPaths"])
print(knowledge["chunksPath"])
print(knowledge["sourcesPath"])
print(knowledge["vectorsPath"])
print(knowledge["indexPaths"])load_knowledge() returns an inspectable knowledge object. Knowledge packages are not runnable SDK objects by themselves.
8) Loading an installed memory blueprint package#
Use load_memory() when you want authored memory blueprint metadata, parsed build metadata, the persisted contract index, and on-demand access to resolved contracts.
from agentpm import load_memory, load_memory_contract
memory = load_memory("@zack/profile-memory@0.1.0")
contract = load_memory_contract(
memory,
space="profile",
record_type="user_preference",
)
print(memory["memory"]["spaces"])
print(memory["build"])
print(memory["contractIndex"])
print(memory["contracts"])
print(contract)load_memory() returns an inspectable memory object. Memory packages are not runnable SDK objects by themselves and do not provide live record CRUD behavior.
9) Loading an installed instruction profile package#
Use load_profile() when you want authored Instruction Profile metadata from an installed registry package.
from agentpm import load_profile
profile = load_profile("@zack/support-style@0.1.0")
print(profile["profile"]["identity"]["role"])
print(profile["profile"]["objectives"])
print(profile["profile"]["communication"])load_profile() returns an inspectable profile object containing:
- the installed profile manifest
- the installed package root path
- parsed authored
profilemetadata
It does not compile prompts, interpret README content as instructions, enforce constraints, or execute runtime behavior.
10) Loading an installed loop package#
Use load_loop() when you want authored Loop metadata from an installed registry package.
from agentpm import load_loop
loop = load_loop("@zack/incident-response-loop@1.0.0")
print(loop["loop"]["entry_phase"])
print(loop["loop"]["phases"])
print(loop["loop"]["transitions"])
print(loop["loop"]["error_policy"])load_loop() returns an inspectable Loop object containing:
- the installed loop manifest
- the installed package root path
- parsed authored
loopmetadata
It does not execute the graph, choose a model, resolve Agent phase bindings, validate Memory selectors against installed blueprints, start MCP, read consumer_context.file, or provide a harness runtime.
load() stays tool-only#
from agentpm import load
load("@zack/incident-commander@0.1.0")
# raises: use load_skill("@zack/incident-commander@0.1.0") instead
load("@zack/python-docs@0.1.0")
# raises: use load_knowledge("@zack/python-docs@0.1.0") instead
load("@zack/profile-memory@0.1.0")
# raises: use load_memory("@zack/profile-memory@0.1.0") instead
load("@zack/support-style@0.1.0")
# raises: use load_profile("@zack/support-style@0.1.0") instead
load("@zack/incident-response-loop@1.0.0")
# raises: use load_loop("@zack/incident-response-loop@1.0.0") instead11) 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.)
12) 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
13) 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).
14) 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
- Modern package loading requires a current
agent.lockload_agent()andload_skill()read modern lockfile shapes. If the lockfile is missing or too old, rerunagentpm install.
- Agent installed on disk but not in the lockfile
load_agent()uses the installed agent directory and theagent.lockroot entry together. If the lockfile is missing the agent root, reinstall it.
- Skill installed on disk but not in the lockfile
load_skill()uses the installed skill directory and theagent.lockroot entry together. If the lockfile is missing the skill root, reinstall it.
15) 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.