Observed since 20 Oct, 22:45 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
agentnode-sdk
Python SDK for AgentNode — the open upgrade and discovery infrastructure for AI agents.
Python SDK for AgentNode — the open upgrade and discovery infrastructure for AI agents.
pip install agentnode-sdk
Connect any LLM agent to AgentNode in three lines. The Runtime provides tool definitions, system prompt, and a tool-loop engine. Tested across 22 models — works with OpenAI, Anthropic, Gemini, Mistral, DeepSeek, Qwen, Llama, and more.
from agentnode_sdk import AgentNodeRuntime
runtime = AgentNodeRuntime()
# Get tools + system prompt for your provider
bundle = runtime.tool_bundle()
# → { "tools": [...], "system_prompt": "..." }
from openai import OpenAI
from agentnode_sdk import AgentNodeRuntime
runtime = AgentNodeRuntime()
client = OpenAI()
result = runtime.run(
provider="openai",
client=client,
model="gpt-4o",
messages=[{"role": "user", "content": "Count the words in 'Hello world'"}],
)
print(result.content)
from anthropic import Anthropic
from agentnode_sdk import AgentNodeRuntime
runtime = AgentNodeRuntime()
client = Anthropic()
result = runtime.run(
provider="anthropic",
client=client,
model="claude-sonnet-4-6",
messages=[{"role": "user", "content": "Search for PDF tools on AgentNode"}],
)
from google import genai
from agentnode_sdk import AgentNodeRuntime
runtime = AgentNodeRuntime()
client = genai.Client()
result = runtime.run(
provider="gemini",
client=client,
model="gemini-2.5-flash",
messages=[{"role": "user", "content": "What AgentNode tools are available?"}],
)
Use any OpenAI-compatible provider by passing a custom base_url:
from openai import OpenAI
from agentnode_sdk import AgentNodeRuntime
runtime = AgentNodeRuntime()
client = OpenAI(
api_key="sk-or-...",
base_url="https://openrouter.ai/api/v1",
)
result = runtime.run(
provider="openai",
client=client,
model="mistralai/mistral-large", # or deepseek/deepseek-chat, qwen/qwen-plus, etc.
messages=[{"role": "user", "content": "Find and install a PDF reader tool"}],
)
For any provider that supports tool calling, use handle() to dispatch calls manually:
runtime = AgentNodeRuntime()
# Get tool definitions in your provider's format
tools = runtime.as_openai_tools() # OpenAI format
tools = runtime.as_anthropic_tools() # Anthropic format
tools = runtime.as_gemini_tools() # Gemini format
tools = runtime.as_generic_tools() # Generic format
# When the LLM makes a tool call, dispatch it:
result = runtime.handle("agentnode_search", {"query": "pdf extraction"})
# → {"success": true, "result": {"total": 5, "results": [...]}}
The agentnode CLI is the human interface. Single commands, multi-step tasks, diagnostics.
# Search & install
agentnode search "pdf extraction"
agentnode install pdf-reader-pack
# Run a single task (natural language)
agentnode run "extract text from report.pdf"
# Multi-step: pipe output between capabilities automatically
agentnode run "extract text from report.pdf then translate to german"
# Preview without executing
agentnode run "search for AI news then summarize" --dry-run
# Show reasoning
agentnode run "search for AI news" --explain
# Diagnostics
agentnode doctor # detect missing capabilities, suggest packages
agentnode doctor --fix # auto-install suggestions
agentnode recommend # prioritized recommendations based on installed setup
agentnode audit # recent policy decisions (allow/deny/prompt)
agentnode inspect pdf-reader-pack # security report: trust, permissions, audit history
# Credentials & config
agentnode auth set openai
agentnode auth status
agentnode config list
All commands support --json for structured output.
Note: Multi-step runs respect your config:
install_confirmation: prompt will ask before installing missing packagesCLI → for humans (search, install, run, diagnose)
SDK / Client → for programmatic access (search, resolve, install, run)
Runtime → for LLM agents (tool registration, dispatch, auto-loop)
AgentNodeRuntimeZero-config LLM agent integration.
| Method | Description |
|---|---|
tool_specs() | Internal typed tool definitions (list[ToolSpec]) |
as_openai_tools() | Tools in OpenAI function-calling format |
as_anthropic_tools() | Tools in Anthropic format |
as_generic_tools() | Tools in generic/baseline format |
system_prompt() | AgentNode system prompt block (append to yours) |
tool_bundle() | Combined {"tools": [...], "system_prompt": "..."} |
handle(tool_name, arguments) | Dispatch a tool call. Returns dict. Never throws. |
run(provider, client, messages, model, ...) | Auto-loop with tool dispatch. Never throws. |
Constructor:
AgentNodeRuntime(
client=None, # Optional AgentNodeClient
api_key=None, # Optional API key
minimum_trust_level="verified", # "verified" | "trusted" | "curated"
)
5 Meta-Tools (automatically registered):
| Tool | Description |
|---|---|
agentnode_capabilities | List installed packages (local, no API call) |
agentnode_search | Search the registry (max 5 results) |
agentnode_install | Install a package by slug |
agentnode_run | Execute an installed tool |
agentnode_acquire | Search + install in one step |
AgentNodeClientThe programmatic client with typed return models.
| Method | Description |
|---|---|
search(query, ...) | Search packages by keyword or capability |
resolve(capabilities, ...) | Resolve capability gaps to ranked packages |
install(slug, ...) | Download, verify, and install locally |
resolve_and_install(capabilities, ...) | Resolve + install in one call |
run_tool(slug, tool_name=, ...) | Run a tool with trust-aware isolation |
smart_run(fn, ...) | Wrap logic with auto-detect, install, retry |
detect_and_install(error, ...) | Detect capability gap and install |
run_tool() (standalone)Top-level function for running tools with process isolation.
from agentnode_sdk import run_tool
result = run_tool("pdf-reader-pack", mode="auto", file_path="report.pdf")
# result.success, result.result, result.error, result.mode_used, result.duration_ms
# result.policy → {"action": "allow", "reason": "...", "source": "..."}
# result.to_dict() → structured output for --json
Isolation contract. mode="auto" always resolves to subprocess,
regardless of the package's trust level. This makes the isolation
guarantee true by default. If you need in-process execution (for
example, to share module-level state with the tool), pass
mode="direct" explicitly — that is an opt-in performance trade-off,
not a default.
get_risk_profile() (standalone)Usage risk assessment for installed packages. Separate from verification — risk answers "how risky is the usage?" not "does it work reliably?"
from agentnode_sdk import get_risk_profile
profile = get_risk_profile("gmail-sender-pack")
# profile.risk_level → "medium"
# profile.risk_score → 45 (0-100, higher = riskier)
# profile.signals → ["Uses external network access", "Requires credentials (oauth)"]
# profile.risk_flags → ["external_write_capable"]
Returns None if the package is not installed.
Guard classifies every tool call by action type and applies configurable policy before any code runs. 9 action types, 3 decisions (allow/prompt/deny), per-tool overrides, strict mode.
# Check what Guard would decide for a tool
agentnode guard check file-manager/delete_file
agentnode guard check web-scraper/fetch_page --json
# Show current policy
agentnode guard status
# Change policy for an action type
agentnode guard set delete deny # block all deletes
agentnode guard set write_local allow # allow local writes without prompt
# Per-tool override (granular escape hatch)
agentnode guard set delete allow --tool file-manager/delete_file
# Reset everything
agentnode guard reset
# Strict mode (CI / production)
export AGENTNODE_GUARD_STRICT=true
Default policy: read/compute/write_local/network_egress → allow. delete/write_external/execute/credential_use/unknown → prompt.
Strict mode: delete/write_external/execute/unknown → deny. write_local → prompt. Per-tool overrides ignored.
Critical risk (unverified + high-risk + secrets in env) → hard deny, no override possible.
Remote/connector hardening: Credentialed requests require HTTPS and
explicit domain binding. Empty allowed_domains is denied — no
open-proxy default. The remote runner additionally warns on
method/action-type mismatches, oversized payloads, and scope/method
inconsistencies (advisory only, never blocks).
See THREAT_MODEL.md for the full security model.
Three layers of supply-chain protection:
Integrity (v0.7.0) — every installed package entry is sealed with a SHA-256 hash over security-critical fields. Post-install tampering is detected before any code executes.
Authenticity (v0.8.0) — publishers sign packages with Ed25519 keys. Install verifies the signature before writing the lockfile. Invalid signatures block install. Missing signatures warn but don't block (gradual adoption).
Registry Authenticity (v0.10.0) — trust-critical registry API responses are verified against pinned Ed25519 registry keys. Prevents a compromised registry from serving attacker-controlled metadata (public keys, trust levels, key status).
# Verify integrity + signatures (CI-friendly, exit code 1 on mismatch/invalid)
agentnode lock verify
agentnode lock verify --strict # also fail on missing integrity
agentnode lock verify --json # structured output with signature status
# Inspect a single package
agentnode inspect pdf-reader-pack
# → Integrity verified
# → Signature valid (key ed25519:a1b2c3d4e5f6)
# Seal entries after manual lockfile edits
agentnode lock seal
New installs are sealed and signature-verified automatically. In strict
mode (AGENTNODE_GUARD_STRICT=true), tampered entries are denied at
runtime. Invalid signatures always block install regardless of mode.
Configure how the SDK reacts to computed risk flags. Uses the same
allow | log | prompt | deny values as permission policies.
# Default: log (audit only, no blocking)
agentnode config get risk_policies.external_write_capable
# Require confirmation for packages that can send data externally
agentnode config set risk_policies.external_write_capable prompt
# Reset to audit-only
agentnode config set risk_policies.external_write_capable log
Risk policies only fire after the normal permission check passes. Hard policies (trust, permissions) always have priority.
Decompose and execute multi-step tasks programmatically.
from agentnode_sdk.planner import plan_task, plan_and_run
# Plan without executing
plan = plan_task("extract text from report.pdf then summarize")
# plan.steps[0].capability == "pdf_extraction"
# plan.steps[1].capability == "text_summarization"
# plan.steps[1].uses_previous == True
# Plan and execute (each step runs through run_tool with full policy/audit)
result = plan_and_run("extract text from report.pdf then summarize")
# result.success, result.steps, result.duration_ms
# result.to_dict() → structured output
Limitations: Max 3 steps. Rule-based splitting on connectors (then,
and then, →) — no LLM decomposition.
MIT
Download counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
cryptographyEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | cryptography>=41.0 | runtime |
httpxEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | httpx>=0.25 | runtime |
keyringEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | keyring>=24 | runtime |
mcpEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | mcp<2,>=1.0.0 | runtime |
packagingEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | packaging>=21 | runtime |
pyyamlEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | pyyaml>=6.0 | runtime |
pytestEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | pytest; extra == "dev" | runtime |
pytest-asyncioEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | pytest-asyncio; extra == "dev" | runtime |
respxEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | respx; extra == "dev" | runtime |
bdist_wheel · 404.31 KB · Python py3
sdist · 845.53 KB · Python source
Observed since 20 Oct, 22:45 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:ad44b09cf616fe0b8be806cca60016eb2bcb5e610a42bb7eb870405d91f4a94b