Documentation
The complete reference for discovering, installing, publishing, and integrating AI agent capabilities with AgentNode.
Quick Start
Go from zero to using your first AI agent capability in under five minutes. This walkthrough installs the CLI, searches the registry, installs a pack, and uses it in Python code.
1. Install the CLI
$ npm install -g agentnode-cli
added 1 package in 2.1s2. Authenticate
Log in to connect the CLI to your AgentNode account. If you do not have an account yet, register at agentnode.net/auth/register.
$ agentnode login
? Email: developer@example.com
? Password: ********
? 2FA Code: 123456
Authenticated as developer@example.com
API key stored in ~/.agentnode/credentials3. Search for a capability
$ agentnode search "pdf extraction"
Results for "pdf extraction":
pdf-reader-pack v1.0.0 trusted Extract text, tables, and metadata from PDFs
pdf-extractor-pack v1.0.0 verified High-fidelity PDF text extraction
ocr-reader-pack v1.1.0 trusted OCR-based document reading including PDFs
3 results found4. Install a pack
$ agentnode install pdf-reader-pack
Installing pdf-reader-pack@1.2.0...
Downloading package done
Verifying hash (SHA-256) done
Installing dependencies done
Writing lockfile done
Installed pdf-reader-pack@1.2.05. Run it in your code
from agentnode_sdk import run_tool
# Run with automatic subprocess isolation (all trust levels)
result = run_tool("pdf-reader-pack", file_path="quarterly-report.pdf")
print(result.result["text"])
print(result.mode_used) # "subprocess" (default) or "direct" (explicit opt-in)
# Multi-tool packs: specify the tool name
result = run_tool("csv-analyzer-pack", tool_name="describe", file_path="data.csv")That is it. You searched the registry, installed a trust-verified pack, and used it with a single function call. Read on for the full reference.
Runtime QuickStart
Build agents that discover and install capabilities at runtime — no hardcoded dependencies. Five lines from pip install to a working tool.
Install the SDK
$ pip install agentnode-sdkThe 5-line agent pattern
Describe what your agent needs. AgentNode resolves it to the best-scored, trust-verified package — downloads, verifies, and installs it locally. Then run it with automatic isolation.
from agentnode_sdk import AgentNodeClient, run_tool
client = AgentNodeClient(api_key="ank_live_...")
# Resolve capability → install best match (trust-verified)
client.resolve_and_install(["pdf_extraction"])
# Run with trust-aware isolation (auto = safe default)
result = run_tool("pdf-reader-pack", file_path="report.pdf")
print(result.result["text"])That is the complete runtime flow. resolve_and_install() handles resolution, trust verification, download, hash check, extraction, dependency install, and lockfile update. run_tool() then executes with automatic subprocess isolation for all trust levels.
The smart_run() pattern (v0.4.0)
Even simpler: wrap your logic and let AgentNode detect, install, and retry automatically when a capability is missing.
from agentnode_sdk import AgentNodeClient
client = AgentNodeClient(api_key="ank_live_...")
# If process_pdf fails because pdfplumber is missing,
# AgentNode detects the gap, installs a PDF skill, and retries
result = client.smart_run(
lambda: process_pdf("report.pdf"),
auto_upgrade_policy="safe", # only verified+ skills
)
print(result.success) # True
print(result.upgraded) # True (skill was installed)
print(result.installed_slug) # "pdf-reader-pack"Step-by-step for more control
When you need to inspect candidates, check policies, or control trust requirements before installing:
from agentnode_sdk import AgentNodeClient, run_tool
client = AgentNodeClient(api_key="ank_live_...")
# 1. Resolve: find the best package for a capability
result = client.resolve(capabilities=["pdf_extraction"])
best = result.results[0]
print(f"Best match: {best.slug} v{best.version}")
print(f" Trust: {best.trust_level} Score: {best.score}")
# 2. Pre-flight check (optional): verify trust + permissions
check = client.can_install(best.slug, require_trusted=True)
if not check.allowed:
print(f"Blocked: {check.reason}")
exit(1)
# 3. Install locally (download → verify hash → extract → pip install → lockfile)
installed = client.install(best.slug)
print(installed.message) # "Installed pdf-reader-pack@1.2.0"
# 4. Run with isolation (auto-mode routes by trust level)
data = run_tool(best.slug, file_path="report.pdf")
print(data.result["text"])
print(f"Ran in {data.mode_used} mode ({data.duration_ms}ms)")What happens under the hood
| Step | What it does |
|---|---|
| detect_gap() | Analyzes error to identify missing capability — 3 layers: ImportError (high), keywords (medium), context (low) |
| resolve() | Scores packages by capability match (40%), framework fit (20%), runtime compatibility (15%), trust level (15%), permissions (10%) |
| can_install() | Pre-flight check — verifies trust level, permissions, deprecation status without downloading anything |
| install() | Downloads artifact, verifies SHA-256 hash, extracts to ~/.agentnode/packages/, runs pip install for dependencies, writes agentnode.lock with trust metadata |
| run_tool() | Always runs in subprocess isolation by default (mode='auto'). Pass mode='direct' to opt into in-process execution. Returns RunToolResult with output, timing, and mode used |
| smart_run() | Full loop: run → detect gap → resolve → install → retry once. Returns SmartRunResult with complete transparency |
Lockfile
Every install writes to agentnode.lock in your project root. This pins exact versions and hashes for reproducible builds across environments.
{
"pdf-reader-pack": {
"version": "1.2.0",
"hash": "sha256:a1b2c3d4...",
"entrypoint": "pdf_reader.extract:run",
"tools": ["extract_pdf", "extract_tables"],
"installed_at": "2026-03-24T10:30:00Z"
}
}LLM Runtime
AgentNodeRuntime connects any OpenAI, Anthropic, or Gemini agent to AgentNode with zero configuration. It registers 5 meta-tools, injects a system prompt, and runs the tool loop automatically. The LLM discovers, installs, and runs capabilities on its own.
Quick start
$ pip install agentnode-sdkOpenAI
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)Anthropic
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"}],
)Gemini
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?"}],
)OpenRouter / any OpenAI-compatible provider
Use Mistral, DeepSeek, Qwen, Llama, and more via OpenRouter or any OpenAI-compatible endpoint:
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",
messages=[{"role": "user", "content": "Find and install a PDF reader tool"}],
)Manual tool calling
For any provider that supports tool calling, get tool definitions and dispatch calls manually with handle():
runtime = AgentNodeRuntime()
# Get tool definitions in your provider's format
tools = runtime.as_openai_tools() # OpenAI function-calling format
tools = runtime.as_anthropic_tools() # Anthropic format
tools = runtime.as_gemini_tools() # Gemini format
tools = runtime.as_generic_tools() # Generic / baseline format
# When the LLM makes a tool call, dispatch it:
result = runtime.handle("agentnode_search", {"query": "pdf extraction"})
# → {"success": true, "result": {"total": 5, "results": [...]}}Constructor
AgentNodeRuntime(
client=None, # Optional AgentNodeClient
api_key=None, # Optional API key
minimum_trust_level="verified", # "verified" | "trusted" | "curated"
)5 meta-tools
These tools are automatically registered when you create a Runtime. The LLM calls them as needed during the tool loop.
| 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 |
API reference
| 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_gemini_tools() | Tools in Google Gemini 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(name, args) | Dispatch a tool call. Returns dict. Never throws. |
| run(provider, client, ...) | Auto-loop with tool dispatch. Never throws. |
run() parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| provider | str | — | "openai", "anthropic", or "gemini" |
| client | Any | — | Provider SDK client instance |
| messages | list[dict] | — | Conversation messages |
| model | str | "" | Model name (e.g. "gpt-4o") |
| max_tool_rounds | int | 8 | Max tool call rounds before stopping |
| inject_system_prompt | bool | True | Append AgentNode prompt to system message |
Trust levels
minimum_trust_level controls which packages can be installed and run through the Runtime. Higher levels are stricter:
| Level | Accepts |
|---|---|
| "verified" | verified, trusted, curated |
| "trusted" | trusted, curated |
| "curated" | curated only |
Three surfaces
| CLI | For humans — search, install, publish |
| SDK / Client | For programmatic access — search, resolve, install, run |
| Runtime | For LLM agents — tool registration, dispatch, auto-loop |
Agents
AgentNode agents are self-describing AI agents packaged with a standardized manifest. Unlike traditional agents that hide behavior in code, every AgentNode agent declares its goal, behavior, permissions, tool access, and limits in a single agentnode.yaml.
Agent tiers
Each agent is classified by what it needs to run:
| Tier | Description | Example |
|---|---|---|
| llm_only | Pure LLM reasoning — no tools, no API calls | Blog writer, report generator |
| llm_plus_tools | LLM + AgentNode tool packs (search, extract, analyze) | Deep research, code review |
| llm_plus_credentials | LLM + tools + external API credentials | CRM enrichment, cloud cost analysis |
Agent manifest fields
The agent: section in the manifest declares the agent configuration. All fields are visible on the package detail page.
| Field | Required | Description |
|---|---|---|
| entrypoint | Yes | Python module:function to execute the agent |
| goal | Yes | What the agent does (shown in UI) |
| system_prompt | Recommended | Agent behavior description (shown as 'Agent Behavior' in UI) |
| tier | No | llm_only | llm_plus_tools | llm_plus_credentials |
| tool_access.allowed_packages | No | List of tool packs the agent may use (empty = full registry) |
| limits.max_iterations | No | Maximum reasoning iterations (1-100) |
| limits.max_tool_calls | No | Maximum tool calls (1-500) |
| limits.max_runtime_seconds | No | Maximum execution time (1-3600) |
| isolation | No | process or thread (default: thread) |
Installing and running an agent
$ agentnode install deep-research-agentfrom agentnode_sdk import run_tool
result = run_tool("deep-research-agent",
goal="Compare React vs Vue adoption in 2026")
print(result.result["report"])
print(result.result["sources"])Agent behavior transparency
Every agent shows its behavior description on the package page under “Agent Behavior”. This is a human-readable description of what the agent does — not necessarily the prompt sent to the LLM. It lets you evaluate an agent before installing it.
Combined with declared permissions, tool access, and verification results, you get full transparency into what an agent does, what it can access, and how it was tested.
See the full list of available agents at agentnode.net/agents.
Installation
System requirements
| Requirement | Details |
|---|---|
| Node.js | v18 or later (for the CLI) |
| Python | v3.10 or later (for running packs and using the SDK) |
| npm | v9 or later |
| OS | macOS, Linux, Windows (WSL recommended on Windows) |
Choose your install
| Use case | Package | Install |
|---|---|---|
| Build agents & apps in Python | agentnode-sdk | pip install agentnode-sdk |
| Install & publish from terminal | agentnode-cli | npm install -g agentnode-cli |
| Use with LangChain / LangGraph | agentnode-langchain | pip install agentnode-langchain |
| Use with MCP (Claude, Cursor) | agentnode-mcp | pip install agentnode-mcp |
Install the CLI
The AgentNode CLI is distributed as a global npm package. It provides all commands for searching, installing, publishing, and managing packs.
$ npm install -g agentnode-cliVerify the installation:
$ agentnode --version
agentnode/0.3.1
$ agentnode --help
Usage: agentnode <command> [options]
Commands:
login Authenticate with the registry
search Search for packages
resolve Resolve capabilities to packages
install Install a package
update Update a package to the latest version
rollback Roll back to a specific version
info Show package details
explain Explain capabilities, permissions, and use cases
auth Manage local credentials (own API tokens)
credentials Manage server-side credentials (OAuth)
audit View the policy decision audit trail
doctor Analyze setup and suggest improvements
list Show installed packages
publish Publish a package
validate Validate a manifest
report Generate a security report
recommend Get recommendations for missing capabilities
resolve-upgrade Find upgrade packages for capability gaps
policy-check Check policy constraints
api-keys Manage API keys
import Import tools from other frameworksAuthentication & API Keys
Who needs an account? Only publishers (people who want to upload packages) and users who install from the registry. Read-only operations like search, info, and explain work without any authentication.
| Operation | Auth Required? | Who Uses This |
|---|---|---|
| search, info, explain | No | Anyone — explore the registry freely |
| install, resolve, update | Yes (API key) | Developers integrating packages into their agents |
| publish, validate | Yes (API key + publisher profile) | Package authors publishing to the registry |
| credentials list/test | Yes (API key) | Users managing server-side OAuth credentials |
| auth (local tokens) | No | Anyone — local credentials need no account |
How to get an API key: Register an account on the website, then create an API key in your account settings or via agentnode api-keys create. The key is shown once at creation — save it immediately.
# Interactive login — stores the API key locally
$ agentnode login
Enter your API key: ********
✓ Logged in as developer (ank_****f456)
# Or set the key directly
$ agentnode api-keys set ank_live_abc123def456
# For CI/CD — use an environment variable
$ export AGENTNODE_API_KEY=ank_live_abc123def456
# Check which key is active
$ agentnode api-keys list
Source: ~/.agentnode/config.json
Key: ank_****f456API keys are stored locally in ~/.agentnode/config.json. The backend stores only a SHA-256 hash of your key — the plaintext is never persisted on the server. Keys can be revoked at any time from your account settings.
Searching & Discovery
AgentNode search is designed for AI agent developers. Instead of keyword matching against package names, search queries are matched against capability descriptions, tool declarations, tags, and metadata. Results are ranked by relevance, trust level, and framework compatibility.
Basic search
$ agentnode search "web scraping"
Results for "web scraping":
webpage-extractor-pack v1.0.0 trusted Extract clean text and metadata from any webpage
browser-automation-pack v1.1.0 verified Automate browser interactions for data extraction
web-search-pack v1.0.0 trusted Search the web and retrieve structured results
3 results foundFiltering results
Narrow results by framework, trust level, runtime, or capability ID.
# Only show packs compatible with LangChain
$ agentnode search "pdf" --framework langchain
# Only show trusted or curated packs
$ agentnode search "email" --trust trusted
# Filter by runtime
$ agentnode search "data analysis" --runtime python
# Filter by specific capability ID
$ agentnode search --capability pdf_extraction
# Combine filters
$ agentnode search "document processing" --framework crewai --trust verifiedSearch flags
| Flag | Type | Description |
|---|---|---|
| --framework | string | Filter by framework compatibility: langchain, crewai, generic |
| --trust | string | Minimum trust level: unverified, verified, trusted, curated |
| --runtime | string | Filter by runtime: python |
| --capability | string | Filter by exact capability ID from the taxonomy |
| --limit | number | Maximum number of results to return (default: 20) |
| --json | boolean | Output results as JSON for programmatic consumption |
| --publisher | string | Filter by publisher namespace |
Understanding results
Each result shows the package slug, current version, trust level, and a summary. Use agentnode info or agentnode explain for detailed information about a specific pack before installing.
$ agentnode explain pdf-reader-pack
pdf-reader-pack@1.2.0
Publisher: agentnode-official
Trust: trusted
Runtime: python >=3.10
Frameworks: langchain, crewai, generic
Capabilities:
- pdf_extraction: Extract text, tables, and metadata from PDF documents
Permissions:
Network: none
Filesystem: read (reads input PDF files)
Code Execution: none
Data Access: input_only
Use Cases:
- Extract text from PDF reports for summarization
- Parse tables from financial PDFs
- Read metadata and page counts from document archives
Install: agentnode install pdf-reader-packResolution Engine
Resolution is different from search. While search finds packages matching a text query, resolution takes a list of capability IDs your agent needs and returns the optimal packages to fill those gaps. The resolution engine scores candidates across multiple dimensions and respects policy constraints.
How resolution scoring works
Each candidate package receives a composite score from 0 to 1 based on five weighted factors:
| Factor | Weight | Description |
|---|---|---|
| Capability match | 40% | How well the pack's declared capabilities match your requested capability IDs |
| Framework compatibility | 20% | Whether the pack supports your agent's framework (LangChain, CrewAI, etc.) |
| Runtime fit | 15% | Whether the pack's runtime and version constraints match your environment |
| Trust level | 15% | Higher trust levels (curated > trusted > verified > unverified) score higher |
| Permissions safety | 10% | Packs requesting fewer permissions score higher (principle of least privilege) |
CLI resolution
$ agentnode resolve pdf_extraction web_search --framework langchain
Resolving 2 capabilities for langchain...
pdf_extraction:
1. pdf-reader-pack v1.0.0 score: 0.94 trusted
2. pdf-extractor-pack v1.0.0 score: 0.81 verified
web_search:
1. web-search-pack v1.0.0 score: 0.92 trusted
2. browser-automation-pack v1.1.0 score: 0.73 verified
Recommended: agentnode install pdf-reader-pack web-search-packSDK resolution
from agentnode_sdk import AgentNodeClient
client = AgentNodeClient(api_key="ank_live_abc123def456")
# Resolve multiple capability gaps at once
result = client.resolve(
capabilities=["pdf_extraction", "web_search", "email_sending"],
framework="langchain",
limit=5,
)
for match in result.results:
print(f"{match.matched_capabilities}: {match.slug} (score: {match.score})")
print(f" Trust: {match.trust_level}")
print()Policy constraints
The resolution engine accepts policy constraints that automatically filter out non-compliant packages. This is critical for production deployments where agents must operate within strict security boundaries.
# Only resolve packages that are trusted or curated
$ agentnode resolve pdf_extraction --trust trusted
# Only resolve packages with no network access
$ agentnode resolve pdf_extraction --policy-no-network
# Check if a specific package meets your policy
$ agentnode policy-check pdf-reader-pack --trust trusted --no-code-execution
Policy check for pdf-reader-pack@1.2.0:
Trust level: trusted PASS
Network: none PASS
Filesystem: read PASS
Code execution: none PASS
Data access: input_only PASS
Package passes all policy constraints.Installing Packs
Basic installation
$ agentnode install pdf-reader-pack
Installing pdf-reader-pack@1.2.0...
Downloading package done
Verifying hash (SHA-256) done
Installing dependencies done
Writing lockfile done
Installed pdf-reader-pack@1.2.0Install a specific version
$ agentnode install pdf-reader-pack@1.1.0Install multiple packs
$ agentnode install pdf-reader-pack web-search-pack email-drafter-packWhat happens during installation
When you run agentnode install, the following steps execute in order:
- Version resolution -- the registry resolves the latest compatible version (or the pinned version you specified).
- Download -- the package archive is downloaded from the registry.
- Hash verification -- the downloaded archive is verified against the SHA-256 hash stored in the registry. If the hash does not match, the install is aborted.
- Dependency installation -- Python dependencies declared in the pack are installed via pip.
- Lockfile update -- the pack version and hash are recorded in
agentnode.lockfor reproducible installations.
The lockfile
The agentnode.lock file records exactly which versions and hashes are installed. Commit this file to version control for reproducible builds across environments.
# Auto-generated by agentnode. Do not edit manually.
lockfile_version: 2
packages:
csv-analyzer-pack:
version: "1.1.0"
hash: "sha256:a1b2c3d4e5f6..."
installed_at: "2025-01-15T10:30:00Z"
entrypoint: "csv_analyzer_pack.tool"
tools:
- name: "describe"
entrypoint: "csv_analyzer_pack.tool:describe"
capability_id: "csv_analysis"
- name: "filter"
entrypoint: "csv_analyzer_pack.tool:filter_rows"
capability_id: "data_cleaning"
pdf-reader-pack:
version: "1.2.0"
hash: "sha256:f6e5d4c3b2a1..."
installed_at: "2025-01-15T10:31:00Z"
entrypoint: "pdf_reader_pack.tool"
tools: []Using packs in code
Every pack is loaded through the SDK's load_tool() function. v0.2 packs support multiple tools with individual entrypoints. v0.1 packs work the same way with a single default entrypoint.
from agentnode_sdk.installer import load_tool
# v0.2 multi-tool pack — load specific tools by name
describe = load_tool("csv-analyzer-pack", tool_name="describe")
filter_rows = load_tool("csv-analyzer-pack", tool_name="filter")
result = describe({"file_path": "data.csv"})
filtered = filter_rows({"file_path": "data.csv", "column": "status", "value": "active"})
# v0.1 single-tool packs — no tool_name needed
extract = load_tool("pdf-reader-pack")
pdf_result = extract({"file_path": "report.pdf"})
search = load_tool("web-search-pack")
search_result = search({"query": "AgentNode", "max_results": 5})Updating and rolling back
# Update to latest version
$ agentnode update pdf-reader-pack
Updating pdf-reader-pack 1.2.0 -> 1.3.0... done
# Roll back to a specific version
$ agentnode rollback pdf-reader-pack@1.2.0
Rolling back pdf-reader-pack 1.3.0 -> 1.2.0... done
# List all installed packs
$ agentnode list
Installed packages:
pdf-reader-pack v1.0.0 trusted
web-search-pack v1.0.0 trustedPublishing Guide
Publishing a pack to AgentNode makes your AI tool discoverable, installable, and verifiable by any agent developer. This guide walks through the full process from account creation to published pack.
Step 1: Create your publisher account
Sign up at agentnode.net/auth/register and enable two-factor authentication. Your publisher namespace (e.g., your-org) appears in every package you publish and cannot be changed later.
Step 2: Structure your project
A minimal pack has three files: the manifest, a pyproject.toml for Python packaging, and the tool module with your tool functions.
my-pack/
agentnode.yaml # ANP manifest (required)
pyproject.toml # Python package config (required)
src/
my_pack/
__init__.py
tool.py # Tool functions (required)Step 3: Write your agentnode.yaml manifest
The manifest is the source of truth for what your pack does, what it needs, and how it integrates. See the ANP Manifest Reference below for every field.
manifest_version: "0.2"
package_id: "github-integration-pack"
package_type: "toolpack"
name: "GitHub Integration Pack"
publisher: "your-namespace"
version: "1.0.0"
summary: "Interact with GitHub repos, issues, and PRs."
description: "A comprehensive toolkit for GitHub automation including issue creation, PR review, repository management, and webhook handling."
runtime: "python"
entrypoint: "github_integration_pack.tool"
install_mode: "package"
hosting_type: "agentnode_hosted"
capabilities:
tools:
- name: "create_issue"
capability_id: "github_integration"
description: "Create a new GitHub issue"
entrypoint: "github_integration_pack.tool:create_issue"
input_schema:
type: "object"
properties:
token:
type: "string"
description: "GitHub personal access token"
repo:
type: "string"
description: "Repository in owner/repo format"
title:
type: "string"
body:
type: "string"
required: ["token", "repo", "title"]
- name: "list_repos"
capability_id: "github_integration"
description: "List repositories for authenticated user"
entrypoint: "github_integration_pack.tool:list_repos"
input_schema:
type: "object"
properties:
token:
type: "string"
required: ["token"]
permissions:
network:
level: "unrestricted"
justification: "Requires access to GitHub API"
filesystem:
level: "none"
code_execution:
level: "none"
data_access:
level: "input_only"
compatibility:
frameworks: ["generic"]
python: ">=3.10"
tags: ["github", "integration", "devtools", "automation"]Step 4: Implement your tool functions
from agentnode_sdk.exceptions import AgentNodeToolError
def create_issue(inputs: dict) -> dict:
"""Create a new GitHub issue."""
token = inputs["token"]
repo = inputs["repo"]
title = inputs["title"]
body = inputs.get("body", "")
# Your implementation here
response = _github_api(token, f"/repos/{repo}/issues", {
"title": title, "body": body
})
return {"issue_number": response["number"], "url": response["html_url"]}
def list_repos(inputs: dict) -> dict:
"""List repositories for authenticated user."""
token = inputs["token"]
repos = _github_api(token, "/user/repos")
return {"repos": [{"name": r["name"], "url": r["html_url"]} for r in repos]}
# Optional: backward-compatible run() wrapper for v0.1 callers
# Not required for v0.2 — per-tool entrypoints (tool:create_issue, tool:list_repos) are used instead
def run(inputs: dict) -> dict:
operation = inputs.get("operation", "list_repos")
dispatch = {"create_issue": create_issue, "list_repos": list_repos}
handler = dispatch.get(operation)
if not handler:
raise AgentNodeToolError(f"Unknown operation: {operation}", tool_name=operation)
return handler(inputs)Step 5: Validate
$ agentnode validate .
Validating github-integration-pack...
Manifest syntax OK
Capability IDs OK (1 tool, 0 resources)
Permissions OK (network: unrestricted)
Entrypoint OK (github_integration_pack.tool)
Compatibility OK (3 frameworks)
Package is valid and ready to publish.Step 6: Publish
$ agentnode publish .
Publishing github-integration-pack@1.0.0...
Uploading package done
Security scan passed (no issues found)
Signing package done (Ed25519)
Indexing capabilities done
Published! https://agentnode.net/packages/github-integration-packTip: Use agentnode publish --dry-run . to test the full publishing pipeline without actually publishing. This runs validation, security scanning, and packaging but does not upload to the registry.
ANP Manifest Reference
The agentnode.yaml manifest is the heart of every pack. It declares identity, capabilities, permissions, and compatibility in a single human-readable file.
Identity fields
| Field | Type | Required | Description |
|---|---|---|---|
| manifest_version | string | Yes | ANP format version. "0.1" or "0.2". Use "0.2" for multi-tool packs with per-tool entrypoints. |
| package_id | string | Yes | Unique identifier for the package. Must be lowercase, hyphenated. Example: "pdf-reader-pack" |
| package_type | string | Yes | Package type. Currently "toolpack" is the primary type. |
| name | string | Yes | Human-readable display name. Example: "PDF Reader Pack" |
| publisher | string | Yes | Publisher namespace from your account. Example: "agentnode-official" |
| version | string | Yes | Semantic version. Must follow semver: "1.0.0", "2.1.3" |
| summary | string | Yes | One-line description (under 120 characters). Used in search results. |
| description | string | No | Longer description with full details. Supports plain text. |
Runtime fields
| Field | Type | Required | Description |
|---|---|---|---|
| runtime | string | Yes | Execution runtime. Currently "python". |
| entrypoint | string | Yes | Package-level Python module path. Example: "pdf_reader_pack.tool". In v0.2, individual tools can have their own entrypoints. |
| install_mode | string | Yes | How the pack is installed. Values: "package" (pip install), "standalone" (script). |
| hosting_type | string | No | Where the pack is hosted. Values: "agentnode_hosted" (default), "self_hosted", "remote". |
Capabilities
The capabilities section declares what your pack can do. Each tool in the tools array maps to a function the pack exposes.
| Field | Type | Required | Description |
|---|---|---|---|
| capabilities.tools | array | Yes | Array of tool declarations. |
| tools[].name | string | Yes | Tool name used in code. Example: "extract_pdf" |
| tools[].capability_id | string | Yes | Standardized capability ID from the taxonomy. Example: "pdf_extraction" |
| tools[].entrypoint | string | v0.2 | Per-tool entrypoint in module.path:function format. Required for multi-tool v0.2 packs. Example: "csv_analyzer_pack.tool:describe" |
| tools[].description | string | Yes | What this tool does, in plain language. |
| tools[].input_schema | object | No | JSON Schema describing the tool's input parameters. |
| tools[].output_schema | object | No | JSON Schema describing the tool's return value. |
Permissions
Every pack must explicitly declare what system resources it accesses. This is not optional -- it is enforced at publish time and surfaced to users before installation.
| Field | Values | Description |
|---|---|---|
| permissions.network.level | none | restricted | unrestricted | Network access. "none" = no outbound calls. "restricted" = specific domains only. "unrestricted" = any network access. |
| permissions.network.justification | string | Why this permission level is needed (recommended for restricted/unrestricted). |
| permissions.filesystem.level | none | temp | read | write | File system access. "none" = no FS access. "temp" = temp directory only. "read" = read files. "write" = read and write. |
| permissions.code_execution.level | none | sandboxed | full | Code execution. "none" = no code exec. "sandboxed" = restricted sandbox. "full" = unrestricted execution. |
| permissions.data_access.level | input_only | output_only | bidirectional | Data flow direction. "input_only" = reads input, does not send data out. "bidirectional" = sends and receives. |
Compatibility
| Field | Type | Required | Description |
|---|---|---|---|
| compatibility.frameworks | array | No | Auto-defaults to ["generic"]. ANP packages work across all frameworks automatically. |
| compatibility.python | string | No | Python version constraint. Example: ">=3.10" |
Tags
An array of lowercase string tags for search and categorization. Example: ["pdf", "extraction", "documents"]. Tags are free-form but should describe the domain and use case.
CLI Reference
Complete reference for all 18 commands in the AgentNode CLI.
agentnode login
Authenticate with the AgentNode registry. Stores credentials in ~/.agentnode/credentials.
$ agentnode login
? Email: developer@example.com
? Password: ********
? 2FA Code: 123456
Authenticated as developer@example.comagentnode search <query>
Search the registry for packages matching a text query.
| Flag | Description |
|---|---|
| --framework <name> | Filter by framework (langchain, crewai, generic) |
| --trust <level> | Minimum trust level (unverified, verified, trusted, curated) |
| --runtime <name> | Filter by runtime (python) |
| --capability <id> | Filter by capability ID |
| --publisher <slug> | Filter by publisher namespace |
| --limit <n> | Maximum results (default: 20) |
| --json | Output as JSON |
$ agentnode search "email automation" --framework langchain --trust verifiedagentnode resolve <capability_id...>
Resolve one or more capability IDs to ranked package recommendations using the scoring engine.
| Flag | Description |
|---|---|
| --framework <name> | Preferred framework for scoring |
| --trust <level> | Minimum trust level |
| --json | Output as JSON |
$ agentnode resolve pdf_extraction email_sending --framework crewaiagentnode install <slug> [slug...]
Install one or more packs. Supports version pinning with slug@version.
$ agentnode install pdf-reader-pack
$ agentnode install pdf-reader-pack@1.1.0
$ agentnode install pdf-reader-pack web-search-packagentnode update <slug>
Update an installed pack to its latest version.
$ agentnode update pdf-reader-pack
Updating pdf-reader-pack 1.2.0 -> 1.3.0... doneagentnode rollback <slug>@<version>
Roll back an installed pack to a specific previous version.
$ agentnode rollback pdf-reader-pack@1.2.0
Rolling back pdf-reader-pack 1.3.0 -> 1.2.0... doneagentnode info <slug>
Display detailed metadata about a package: version history, publisher, trust level, permissions, capabilities, and compatibility.
$ agentnode info pdf-reader-packagentnode explain <slug>
Explain a package in plain language: what it does, what permissions it requires, which frameworks it supports, and typical use cases. Designed for deciding whether to install.
$ agentnode explain pdf-reader-packagentnode audit
View and manage the AgentNode Guard policy decision audit trail. Every install and run decision is logged to ~/.agentnode/audit.jsonl.
| Subcommand | Description |
|---|---|
| audit show | Show recent audit entries (default: last 20) |
| audit show --limit 50 | Show more entries |
| audit show --json | Output raw JSON for scripting |
| audit stats | Summary: allow/deny/prompt counts, top packages |
| audit clear --yes | Delete the audit log |
$ agentnode audit show
TIMESTAMP EVENT SLUG ACTION SOURCE TRUST
───────────────────────────────────────────────────────────────────────────────────────────────────────────
2026-04-16 14:23:01 client_install pdf-reader-pack allow default trusted
2026-04-16 14:23:05 run_tool pdf-reader-pack allow default trusted
2026-04-16 14:25:12 client_install untrusted-pack deny trust_level unverified
$ agentnode audit stats
Total entries: 142
Period: 2026-04-10 → 2026-04-16
Actions: allow 118 (83.1%) deny 19 (13.4%) prompt 5 (3.5%)agentnode doctor
Analyze your local setup, check for outdated packs, missing dependencies, configuration issues, and suggest improvements.
$ agentnode doctor
Checking environment...
Node.js: v20.10.0 OK
Python: 3.11.5 OK
CLI: 1.0.0 OK
Auth: logged in OK
Lockfile: found OK
1 outdated pack: pdf-reader-pack (1.2.0 -> 1.3.0)
Run: agentnode update pdf-reader-packagentnode list
Show all locally installed packs with versions and trust levels.
$ agentnode list
Installed packages:
pdf-reader-pack v1.0.0 trusted
web-search-pack v1.0.0 trusted
email-drafter-pack v1.0.0 verifiedagentnode publish <directory>
Publish a pack to the registry. Runs validation, security scanning, signing, and indexing.
| Flag | Description |
|---|---|
| --dry-run | Run the full pipeline without uploading |
$ agentnode publish .
$ agentnode publish ./my-pack --dry-runagentnode validate <directory>
Validate a manifest without publishing. Checks syntax, capability IDs, permissions consistency, entrypoint resolution, and framework compatibility.
$ agentnode validate .agentnode report <slug>
Generate a full security report for a package, including trust history, scan results, dependency analysis, and permission audit.
$ agentnode report pdf-reader-packagentnode recommend
Analyze your installed packs and suggest additional capabilities that complement your current setup.
$ agentnode recommend
Based on your installed packs, you might also need:
document_summary -> document-summarizer-pack trusted
data_visualization -> data-visualizer-pack verifiedagentnode resolve-upgrade
Find higher-scored or more trusted alternatives for your currently installed packs.
$ agentnode resolve-upgrade
Checking for upgrades...
pdf-extractor-pack -> pdf-reader-pack (higher trust, better score)agentnode policy-check <slug>
Check whether a package meets specified policy constraints.
| Flag | Description |
|---|---|
| --trust <level> | Required minimum trust level |
| --no-network | Require no network access |
| --no-code-execution | Require no code execution |
| --no-filesystem-write | Require no filesystem write access |
$ agentnode policy-check pdf-reader-pack --trust trusted --no-networkagentnode auth <provider>
Store a local API token for a connector provider (e.g. GitHub, Slack). Tokens are saved to ~/.agentnode/credentials.json with 0600 permissions. No AgentNode account required.
| Subcommand / Flag | Description |
|---|---|
| auth <provider> | Store a token for the given provider (interactive prompt) |
| auth list | List locally stored credentials |
| auth remove <provider> | Remove a locally stored credential |
| --validate | Validate the token against the provider's API before saving |
$ agentnode auth github --validate
? Paste your GitHub token: ********
✓ Token validated — stored for githubagentnode credentials <subcommand>
Manage server-side OAuth credentials stored in the AgentNode backend. These are obtained via OAuth flows and proxied through the API — your tool never sees the raw token.
| Subcommand / Flag | Description |
|---|---|
| credentials list | List all server-side credentials |
| credentials test <id> | Test connectivity for a credential |
| credentials delete <id> | Revoke and delete a credential |
| --json | Output as JSON (available on all subcommands) |
$ agentnode credentials list
Credentials (2):
a1b2c3d4 github active domains=[api.github.com]
e5f6g7h8 slack active domains=[slack.com]agentnode import <file> --from <platform>
Import existing tools from other frameworks and generate an ANP manifest automatically. See Import Tools for full details.
| Flag | Description |
|---|---|
| --from <platform> | Source platform: mcp, langchain, openai, crewai, clawhub, skillssh |
| --output <dir> | Output directory for generated manifest (default: current directory) |
$ agentnode import my_tools.py --from langchainPython SDK
The Python SDK provides programmatic access to the AgentNode registry for search, resolution, trust checking, installation, tool loading, and capability gap detection. Use it to build agents that detect missing capabilities and safely acquire verified skills on demand.
Installation
$ pip install agentnode-sdkInitialization
from agentnode_sdk import AgentNodeClient
# API key authentication (recommended)
client = AgentNodeClient(api_key="ank_live_abc123def456")
# Or use a bearer token
client = AgentNodeClient(token="your_bearer_token")
# Custom base URL (for self-hosted registries)
client = AgentNodeClient(
api_key="ank_live_abc123def456",
base_url="https://api.your-registry.com/v1"
)Search
result = client.search(
query="pdf extraction",
framework="langchain",
per_page=10
)
print(f"Found {result.total} packages")
for hit in result.hits:
print(f" {hit.slug} {hit.trust_level} {hit.summary}")Resolve
Resolve finds the best package for a set of capability IDs, scoring each candidate on capability match, framework fit, runtime compatibility, trust level, and permissions.
result = client.resolve(
capabilities=["pdf_extraction", "web_search"],
framework="langchain"
)
for match in result.results:
print(f"{match.slug} v{match.version}")
print(f" Score: {match.score} Trust: {match.trust_level}")
print(f" Breakdown: cap={match.breakdown.capability} "
f"fw={match.breakdown.framework} "
f"trust={match.breakdown.trust}")Pre-flight check
Check whether a package can be installed under given trust and permission constraints — without downloading anything.
check = client.can_install(
"pdf-reader-pack",
require_trusted=True,
denied_permissions=["network", "code_execution"]
)
if check.allowed:
print(f"OK — trust: {check.trust_level}")
else:
print(f"Blocked: {check.reason}")Install
Downloads the artifact, verifies the SHA-256 hash, extracts to ~/.agentnode/packages/, installs pip dependencies, and writes agentnode.lock.
result = client.install("pdf-reader-pack", require_trusted=True)
print(result.message) # "Installed pdf-reader-pack@1.2.0"
print(result.installed) # True
print(result.hash_verified) # TrueLoad and run tools
# Load a tool from an installed package
extract = client.load_tool("pdf-reader-pack")
result = extract({"file_path": "report.pdf"})
# Multi-tool packs: load a specific tool by name
describe = client.load_tool("csv-analyzer-pack", tool_name="describe")
summary = describe({"file_path": "data.csv"})One-call autonomous install
For agents that need to self-upgrade: describe what you need and let AgentNode handle the rest.
# Resolve + trust check + install in one call
result = client.resolve_and_install(
capabilities=["pdf_extraction"],
require_trusted=True # only install trusted/curated packages
)
if result.installed:
tool = client.load_tool(result.slug)
data = tool({"file_path": "report.pdf"})
else:
print(f"Could not install: {result.message}")Capability gap detection (v0.4.0)
AgentNode can analyze runtime errors to detect missing capabilities — without any LLM. Three detection layers with confidence levels:
- High —
ImportErrorfor a known module (e.g. pdfplumber, pandas, selenium) - Medium — Error message contains technical keywords (e.g. "chromedriver", "csv parser")
- Low — Context hints like file extensions or URLs
from agentnode_sdk import detect_gap
gap = detect_gap(ImportError("No module named 'pdfplumber'"))
print(gap.capability) # "pdf_extraction"
print(gap.confidence) # "high"
print(gap.source) # "import_error"
# Context helps when the error itself isn't specific
gap = detect_gap(RuntimeError("failed"), context={"file": "report.pdf"})
print(gap.capability) # "pdf_extraction"
print(gap.confidence) # "low"detect_and_install() (v0.4.0)
The product-level API for self-upgrading agents. Detects the gap, resolves the best match, and installs it — all in one call.
try:
result = my_agent_logic()
except Exception as exc:
upgrade = client.detect_and_install(
exc,
auto_upgrade_policy="safe", # only verified+ skills
on_detect=lambda cap, conf, err: print(f"Detected: {cap} ({conf})"),
on_install=lambda slug: print(f"Installed: {slug}"),
)
if upgrade.installed:
result = my_agent_logic() # retry manually
else:
print(f"Detection: {upgrade.capability} ({upgrade.confidence})")
print(f"Error: {upgrade.error}")smart_run() (v0.4.0)
Convenience wrapper: wrap your logic and let AgentNode handle detection, installation, and exactly one retry automatically.
result = client.smart_run(
lambda: process_pdf("report.pdf"),
auto_upgrade_policy="safe",
)
if result.success:
print(result.result) # your function's return value
print(result.upgraded) # True if a skill was installed
print(result.installed_slug) # e.g. "pdf-reader-pack"
print(result.duration_ms) # total time including retry
else:
print(result.error)
print(result.original_error) # the first error, always availableAuto-upgrade policies (v0.4.0)
Named policies control what gets auto-installed. When set, the policy overrides individual parameters like require_verified.
| Policy | Behavior |
|---|---|
| "off" | Detect only, never install |
| "safe" | Auto-install verified+ skills (recommended) |
| "strict" | Auto-install trusted+ skills only |
Low-confidence detections are blocked from auto-install by default. Use allow_low_confidence=True to override.
Package metadata
# Package details
pkg = client.get_package("pdf-reader-pack")
print(f"{pkg.name} v{pkg.latest_version}")
print(f"Downloads: {pkg.download_count}")
print(f"Deprecated: {pkg.is_deprecated}")
# Install metadata (capabilities, permissions, artifact info)
meta = client.get_install_metadata("pdf-reader-pack")
print(f"Runtime: {meta.runtime}")
print(f"Entrypoint: {meta.entrypoint}")
for cap in meta.capabilities:
print(f" {cap.name} ({cap.capability_id})")
if meta.permissions:
print(f" Network: {meta.permissions.network_level}")
print(f" Filesystem: {meta.permissions.filesystem_level}")REST API
The AgentNode REST API provides direct HTTP access to all registry functionality. Base URL: https://api.agentnode.net/v1
Authentication
Include your API key in the X-API-Key header. Read-only endpoints (search, info) may work without authentication but are rate-limited.
curl -H "X-API-Key: ank_live_abc123def456" \
https://api.agentnode.net/v1/packages/pdf-reader-packSearch packages
# POST /v1/search
curl -X POST "https://api.agentnode.net/v1/search" \
-H "Content-Type: application/json" \
-d '{"q": "pdf extraction", "framework": "langchain", "trust": "verified"}'
# Response:
{
"results": [
{
"slug": "pdf-reader-pack",
"name": "PDF Reader Pack",
"version": "1.2.0",
"summary": "Extract text, tables, and metadata from PDF documents",
"trust_level": "trusted",
"publisher": "agentnode-official",
"frameworks": ["generic"],
"capabilities": ["pdf_extraction"]
}
],
"total": 1
}Get package details
# GET /v1/packages/:slug
curl "https://api.agentnode.net/v1/packages/pdf-reader-pack"
# Response:
{
"slug": "pdf-reader-pack",
"name": "PDF Reader Pack",
"version": "1.2.0",
"publisher": "agentnode-official",
"trust_level": "trusted",
"summary": "Extract text, tables, and metadata from PDF documents",
"runtime": "python",
"capabilities": [
{
"name": "extract_pdf",
"capability_id": "pdf_extraction",
"description": "Extract text, tables, and metadata from PDF documents"
}
],
"permissions": {
"network": "none",
"filesystem": "read",
"code_execution": "none",
"data_access": "input_only"
},
"compatibility": {
"frameworks": ["generic"],
"python": ">=3.10"
}
}Resolve capabilities
# POST /v1/resolve
curl -X POST "https://api.agentnode.net/v1/resolve" \
-H "X-API-Key: ank_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{
"capabilities": ["pdf_extraction", "web_search"],
"framework": "langchain",
"policy": {
"min_trust": "verified"
}
}'
# Response:
{
"results": [
{
"slug": "pdf-reader-pack",
"version": "1.2.0",
"score": 0.94,
"trust_level": "trusted",
"matched_capabilities": ["pdf_extraction"]
},
{
"slug": "web-search-pack",
"version": "1.0.0",
"score": 0.92,
"trust_level": "trusted",
"matched_capabilities": ["web_search"]
}
]
}Check policy
# POST /v1/check-policy
curl -X POST "https://api.agentnode.net/v1/check-policy" \
-H "X-API-Key: ank_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{
"package_slug": "pdf-reader-pack",
"policy": {
"min_trust": "trusted",
"max_permissions": {
"network": "none",
"code_execution": "none"
}
}
}'
# Response:
{
"passes": true,
"checks": [
{ "name": "trust_level", "passed": true, "actual": "trusted", "required": "trusted" },
{ "name": "network", "passed": true, "actual": "none", "max": "none" },
{ "name": "code_execution", "passed": true, "actual": "none", "max": "none" }
]
}Publish a package
# POST /v1/packages/publish
curl -X POST "https://api.agentnode.net/v1/packages/publish" \
-H "X-API-Key: ank_live_abc123def456" \
-H "Content-Type: multipart/form-data" \
-F "artifact=@./dist/my-pack-1.0.0.tar.gz" \
-F "manifest=@./agentnode.yaml"
# Response:
{
"slug": "my-pack",
"version": "1.0.0",
"url": "https://agentnode.net/packages/my-pack",
"trust_level": "unverified"
}List capabilities
# GET /v1/capabilities
curl "https://api.agentnode.net/v1/capabilities"
# Response:
{
"capabilities": [
{ "id": "pdf_extraction", "category": "Document Processing", "description": "Extract text and data from PDF files" },
{ "id": "web_search", "category": "Web & Browsing", "description": "Search the web and return structured results" },
{ "id": "email_sending", "category": "Communication", "description": "Compose and send emails" }
]
}Additional endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/auth/register | Create a new account |
| POST | /v1/auth/login | Authenticate and receive a session token |
| POST | /v1/auth/2fa/setup | Initialize two-factor authentication |
| POST | /v1/auth/2fa/verify | Verify a 2FA code |
| GET | /v1/packages/:slug/trust | Get trust level details and history |
| POST | /v1/packages/:slug/reviews | Submit a review for a package |
| GET | /v1/packages/:slug/reviews | List reviews for a package |
| POST | /v1/packages/:slug/report | Report a package for security or policy violations |
| GET | /v1/packages/:slug/install-info | Get install metadata (hash, URL, dependencies) |
| POST | /v1/packages/:slug/install | Record an installation event |
| POST | /v1/recommend | Get pack recommendations based on installed capabilities |
| POST | /v1/packages/validate | Validate a manifest without publishing |
MCP Integration
The Model Context Protocol (MCP) is an open standard for connecting AI models to external tools and data sources. The AgentNode MCP adapter lets you search, resolve, and browse the AgentNode registry directly from MCP-compatible editors like Claude Code and Cursor.
Installation
$ pip install agentnode-mcpTwo modes of operation
The MCP adapter runs in two modes:
- Pack server -- exposes a single installed pack as MCP tools. Use when you want to give an editor access to one specific pack.
- Platform server -- exposes the full AgentNode platform API as MCP tools. Use when you want to search, resolve, and browse the registry from your editor.
Pack server
# Expose a single pack as MCP tools
$ agentnode-mcp --pack pdf-reader-packPlatform server
# Expose the full AgentNode platform API
$ agentnode-mcp-platform --api-url https://api.agentnode.netAvailable MCP tools (platform server)
| Tool | Description |
|---|---|
| agentnode_search | Search the registry for packages by query, framework, and trust level |
| agentnode_resolve | Resolve capability IDs to ranked package recommendations |
| agentnode_explain | Get a detailed explanation of a package's capabilities and permissions |
| agentnode_capabilities | List all available capability IDs in the taxonomy |
Claude Code configuration
Add the following to your Claude Code MCP configuration file (typically claude_desktop_config.json or your project .mcp.json):
{
"mcpServers": {
"agentnode": {
"command": "agentnode-mcp-platform",
"args": ["--api-url", "https://api.agentnode.net"],
"env": {
"AGENTNODE_API_KEY": "ank_live_abc123def456"
}
}
}
}Cursor configuration
Add the same server block to your Cursor MCP settings. The exact file location depends on your OS:
{
"mcpServers": {
"agentnode": {
"command": "agentnode-mcp-platform",
"args": ["--api-url", "https://api.agentnode.net"],
"env": {
"AGENTNODE_API_KEY": "ank_live_abc123def456"
}
}
}
}Using a specific pack in your editor
To expose a specific installed pack as an MCP tool (so your editor can use it directly):
{
"mcpServers": {
"pdf-reader": {
"command": "agentnode-mcp",
"args": ["--pack", "pdf-reader-pack"]
}
}
}GitHub Action
The agentnode/publish@v1 GitHub Action automates pack publishing from your CI/CD pipeline. Push a tag or create a release, and the action validates, scans, signs, and publishes your pack automatically.
Basic workflow
name: Publish to AgentNode
on:
release:
types: [published]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: pip install -r requirements.txt
- name: Publish pack
uses: agentnode/publish@v1
with:
api-key: ${{ secrets.AGENTNODE_API_KEY }}Action inputs
| Input | Required | Default | Description |
|---|---|---|---|
| api-key | Yes | -- | Your AgentNode API key. Store as a repository secret. |
| dry-run | No | false | Run validation and scanning without publishing. Set to "true" for PR checks. |
| directory | No | . | Path to the pack directory containing agentnode.yaml. |
Dry-run on pull requests
Use dry-run mode to validate manifests on every pull request without publishing:
name: Validate AgentNode Pack
on:
pull_request:
paths:
- "agentnode.yaml"
- "src/**"
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate pack
uses: agentnode/publish@v1
with:
api-key: ${{ secrets.AGENTNODE_API_KEY }}
dry-run: trueExample output
Run agentnode/publish@v1
Validating agentnode.yaml...
Manifest syntax OK
Capability IDs OK (1 tool, 0 resources)
Permissions OK (network: unrestricted)
Entrypoint OK (github_integration_pack.tool)
Compatibility OK (3 frameworks)
Running security scan...
Bandit scan passed (0 issues)
Dependency audit passed
Publishing github-integration-pack@1.0.0...
Uploading package done
Signing package done (Ed25519)
Indexing capabilities done
Published: https://agentnode.net/packages/github-integration-packTriggering on tags
If you prefer tag-based releases instead of GitHub Releases:
name: Publish to AgentNode
on:
push:
tags:
- "v*"
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Publish pack
uses: agentnode/publish@v1
with:
api-key: ${{ secrets.AGENTNODE_API_KEY }}Package Verification
AgentNode verifies every package on publish in a real sandbox and computes a verification score from 0–100. The score is based on evidence — not self-reported badges. Scores are visible on every package page and factor into search ranking.
Verification steps
Each package goes through four checks, each contributing points to the overall score:
1. Install
15 ptsThe package is installed in a clean virtual environment. If installation fails (missing dependencies, build errors), the package is automatically quarantined.
2. Import
15 ptsAll declared tool entrypoints are imported and checked for existence and callability. If any entrypoint is missing or not callable, the package is quarantined.
3. Smoke test
25 ptsTools are called with test inputs generated from their JSON schema. Results are classified as passed (25 pts), inconclusive (0–12 pts depending on reason), or failed (0 pts). Smoke failures do not block the package but reduce the score.
4. Tests
15 ptsIf the package includes a test suite, it is executed with pytest. Real tests that pass earn 15 pts. Auto-generated tests earn 8 pts. No tests: 3 pts. Integration tests marked with @pytest.mark.integration are skipped.
Quality checks (multi-run)
After a successful smoke test, the same input is run multiple times to measure stability:
| Check | Points | What it measures |
|---|---|---|
| Reliability | 0–10 | Proportion of runs that succeed (e.g. 3/3 = 10 pts) |
| Determinism | 0–5 | Output consistency across runs (same hash = 5 pts) |
| Contract | 0–10 | Return value is serializable, non-null, structurally valid |
| Warnings | −2 each | Deprecation warnings, unsafe patterns (max −10) |
Verification tiers
The total score maps to a tier displayed on the package page and in search results:
Gold
90–100
Verified
70–89
Partial
50–69
Unverified
<50
Inconclusive reasons & partial credit
Not every tool can be fully tested in a sandbox. A tool that needs an API key is not broken — it just cannot be smoke-tested without credentials. We classify why a smoke test is inconclusive and score accordingly:
| Reason | Smoke pts | Meaning |
|---|---|---|
| needs_credentials | 12/25 | Requires API keys not available in sandbox |
| missing_system_dependency | 12/25 | Requires Chromium, FFmpeg, etc. |
| needs_binary_input | 12/25 | Requires real PDF, image, or audio files |
| external_network_blocked | 12/25 | Needs network access blocked in sandbox |
| not_implemented | 0/25 | Stub package, raises NotImplementedError |
| unknown_smoke_condition | 8/25 | Ambiguous error — may be broken or just missing data |
How to improve your score
As a publisher, there are several things you can do to maximize your verification score:
Include real tests
A passing test suite earns 15 pts (vs. 3 pts with no tests). Use pytest and place tests in a tests/ directory. Mark integration tests that need external services with @pytest.mark.integration.
Define a complete input schema
Declare input_schema with properties, required, and type for every field. Use enum for constrained fields and default values where appropriate. The more schema detail, the better our test input generation.
Add schema examples
Include an examples key in your input schema with a working example input. This is the highest-confidence test input and is tried first.
Return serializable values
Tools should return JSON-serializable values (dicts, lists, strings). Returning None or non-serializable objects reduces the contract score.
Quarantine behavior
Packages are automatically quarantined if installation or import fails. Quarantined packages are hidden from search results and cannot be installed. Other issues (smoke test, unit tests) reduce the score but do not block usage.
Continuous re-verification
Scores are not static. Every package is automatically re-verified on every publish. If a dependency update breaks something, the score drops and users see it before their agent does. Admins can also trigger targeted re-verification at any time.
What verification guarantees
Guaranteed
- Can be installed in a clean environment
- All declared entrypoints exist and are callable
- Package structure is valid
- Score reflects real sandbox execution
Not guaranteed
- Correct behavior with real-world data
- Availability of external services
- Full test coverage
- Tools requiring credentials/system deps work correctly
Verification is evidence-based, not absolute. It filters out broken packages and scores what it can prove. “Partially Verified” means “not fully testable in a sandbox” — not “broken.”
Credentials & Connectors
Some packages connect to external APIs (GitHub, Slack, etc.). These connector packages need credentials to authenticate. AgentNode provides two ways to manage credentials: local tokens you own, and server-side OAuth via the AgentNode backend.
How credential resolution works
When a connector package runs, the SDK resolves credentials through a configurable chain. By default (auto mode), it tries each source in order until it finds a match:
1. Environment Variable
AGENTNODE_CRED_GITHUB, AGENTNODE_CRED_SLACK, etc. Best for CI/CD pipelines and containers.
2. Local Credential File
~/.agentnode/credentials.json — tokens stored via agentnode auth. No account needed.
3. Server-Side (API)
OAuth2 tokens managed by the AgentNode backend. Secrets never leave the server — requests are proxied.
Local credentials (agentnode auth)
Use agentnode auth to store your own API tokens locally. No AgentNode account required. Tokens are validated against the provider's API before being saved.
# Store a GitHub token
$ agentnode auth github
Create a token at: https://github.com/settings/tokens
Recommended scopes: repo, read:user
Paste your token: ********
Validating token... valid
GitHub credential stored.
# Store a Slack token
$ agentnode auth slack
# List stored credentials
$ agentnode auth list
# Remove a credential
$ agentnode auth remove githubCredentials are stored in ~/.agentnode/credentials.json with file permissions restricted to the current user (0600 on Unix). This follows the same pattern used by gh, docker, and aws CLI tools.
Server-side credentials (agentnode credentials)
For OAuth2 flows that require a callback URL, the AgentNode backend handles the token exchange. Your secrets never leave the server — tool execution is proxied through the backend.
# List server-side credentials
$ agentnode credentials list
# Test connectivity
$ agentnode credentials test <id>
# Revoke a credential
$ agentnode credentials delete <id>Environment variables
For CI/CD and automated environments, set credentials as environment variables. The naming convention is AGENTNODE_CRED_ followed by the provider name in uppercase:
export AGENTNODE_CRED_GITHUB=ghp_xxxxxxxxxxxxx
export AGENTNODE_CRED_SLACK=xoxb-xxxxxxxxxxxxxResolution mode
You can control which sources are checked via credentials.resolve_mode in your config:
| Mode | Sources Checked | Use Case |
|---|---|---|
| auto (default) | env → local file → API | Best for development — tries everything |
| env | Environment variables only | CI/CD pipelines, containers |
| local | Local file only | Offline development, air-gapped systems |
| api | Server-side only | Managed environments, OAuth2 flows |
{
"credentials": {
"resolve_mode": "auto"
}
}Security model
- CredentialHandle — tools never see raw tokens. They receive an opaque handle that makes authenticated requests on their behalf. The handle validates target domains against the connector's allowed list before attaching credentials.
- Domain restriction — each credential is bound to specific API domains (e.g.,
api.github.com). Requests to unauthorized domains are rejected before any secret is attached. - No serialization — CredentialHandles cannot be pickled, serialized, or printed.
repr()shows only the provider name, never the secret. - Proxy mode — server-side credentials are never sent to the client. Requests are proxied through the backend, so the token stays on the server.
Trust & Security
Trust is not binary. AgentNode provides a layered trust model where every pack has a clear, auditable trust level that progresses over time through verification, community usage, and manual review.
The four trust levels
Unverified
Newly published pack. Metadata has been validated and the manifest is syntactically correct, but no further review has been performed. Use with caution in production.
Verified
Publisher identity has been confirmed. The pack passes automated security scans (Bandit), and its declared permissions are consistent with actual behavior. Publisher has 2FA enabled.
Trusted
Security scanned with zero findings, tests pass, active maintenance history, meaningful community usage, and no reported issues. The pack has demonstrated reliability over time.
Curated
Manually reviewed by the AgentNode team. Code has been audited, permissions verified against actual behavior, and the pack meets the highest quality bar. This is the highest assurance level in the registry.
How to progress through trust levels
| From | To | Requirements |
|---|---|---|
| Unverified | Verified | Confirm publisher identity, enable 2FA, pass Bandit security scan, permissions match declared behavior |
| Verified | Trusted | Zero security findings, tests pass, active maintenance, community usage, no unresolved reports |
| Trusted | Curated | Manual review by AgentNode team, code audit, permissions verification, documentation review |
Security scanning
Every pack published to the registry undergoes automated security scanning:
- Bandit analysis -- static analysis for common Python security issues (hardcoded passwords, SQL injection, insecure deserialization, etc.)
- Ed25519 signatures -- every published pack is signed with the publisher's key. Install-time verification ensures the pack has not been tampered with after publication.
- Typosquatting detection -- the registry detects package names that are suspiciously similar to popular packs (e.g.,
pdf-reeder-packvs.pdf-reader-pack) and flags them for manual review. - Hash verification -- SHA-256 hashes are computed at publish time and verified at install time. If the hash does not match, the installation is aborted.
The permission model
Every pack must explicitly declare its permissions across four dimensions. Agents and users see the full permission manifest before installation, and the resolution engine can filter by permission constraints.
| Dimension | Levels | Description |
|---|---|---|
| Network | none, restricted, unrestricted | What network access the pack requires. "none" means no outbound calls. "restricted" means specific domains only. "unrestricted" means any network access. |
| Filesystem | none, temp, read, write | What file system access the pack requires. "temp" means temporary directory only. "read" means it reads files. "write" means it reads and writes. |
| Code Execution | none, sandboxed, full | Whether the pack executes arbitrary code. "sandboxed" means restricted execution environment. "full" means unrestricted. |
| Data Access | input_only, output_only, bidirectional | The direction of data flow. "input_only" means the pack reads input but does not send data externally. "bidirectional" means it both receives and sends data. |
Inspecting a package
Use agentnode info and agentnode policy-check to review a package's trust level, permissions, and whether it meets your policy constraints before installation.
$ agentnode info pdf-reader-pack
$ agentnode policy-check pdf-reader-pack --trust trusted --no-networkData Sovereignty
AgentNode is a registry and policy engine — not a data processor. Your data never touches our servers. Tools run locally in your environment. The backend only stores what's needed for the registry catalog (package metadata, account credentials). Everything else stays on your machine.
Your data stays local
- Tool input/output data — the data your agents process never touches our servers. Tools run locally in your environment, not on ours.
- LLM prompts or responses — we have no visibility into what your agent sends to or receives from language models.
- Usage telemetry — the SDK does not phone home. No analytics, no tracking, no usage beacons. The only network calls are explicit ones you trigger (install, search, resolve).
- Local credentials — tokens stored via
agentnode authstay in~/.agentnode/credentials.jsonon your machine. They are never uploaded. - Audit logs — Guard's audit trail (
~/.agentnode/audit.jsonl) is local-only. Policy decisions stay on your machine.
Execution model
All tool execution happens locally in your Python process. The AgentNode backend is only involved in three scenarios:
Registry Operations
Search, install, resolve, publish — catalog operations that transfer package metadata and artifacts.
OAuth Proxy
Server-side credentials: the backend proxies API calls so OAuth tokens never leave the server. Optional — you can use local tokens instead.
Remote Runner
For packages that explicitly declare remote execution. The SDK marks these as remote_run in the audit trail. Most packages run locally.
Accounts & API keys
An AgentNode account is needed only for write operations (installing, publishing) and server-side credentials. Read-only operations like search, info, and explain work without any authentication.
API keys are the primary authentication mechanism for the CLI and SDK. They replace session-based login for programmatic access:
- Create an API key in your account settings on the website or via
agentnode api-keys create <label> - Store it locally with
agentnode api-keys set <key>or viaagentnode login - Use it automatically — the CLI reads from config, or set
AGENTNODE_API_KEYfor CI/CD - Revoke at any time from your account settings — revoked keys are immediately rejected
Only a SHA-256 hash of your API key is stored on the server. If the database is compromised, the plaintext key cannot be recovered. Keys are matched using constant-time comparison (hmac.compare_digest) to prevent timing attacks.
Offline-capable
Once packages are installed, the SDK works fully offline. The lockfile (agentnode.lock) contains all metadata needed to run tools without network access. Set credentials.resolve_mode: "local" to ensure credential resolution never reaches out to the API. Policy enforcement (Guard) is entirely local — no server calls, no latency.
AgentNode Guard
AgentNode Guard is the pre-execution policy gateway built into the SDK. Every install and run call passes through Guard before anything executes. Guard checks trust levels, permission boundaries, and environment context — then allows, denies, or prompts. Every decision is logged to an append-only audit trail.
How it works
Guard sits at the center of every execution path: the Python SDK, the CLI, the MCP adapter, and the agent runtime. There is no way to run or install a pack without Guard evaluating the request first.
1. Check
Guard reads your config (~/.agentnode/config.json), the package's trust level and permissions, and the runtime environment (secrets present? CI? container?).
2. Decide
Based on your policy, Guard returns one of three actions: allow, deny, or prompt. Broken or missing config defaults to deny (fail-closed).
3. Audit
Every decision is logged to ~/.agentnode/audit.jsonl with timestamp, event type, package, action, source, and environment context. The audit trail is append-only and auto-rotated.
Enforcement points
Guard is enforced at every execution path. There is no bypass.
| Path | Check | Enforcement |
|---|---|---|
| client.install() | check_install | Hard — policy crash = deny |
| runner.run_tool() | check_run | Hard — deny or prompt stops execution |
| runtime.handle() | check_run | Hard — returns policy_denied error |
| MCP call_tool() | check_run | Hard — fail-closed (non-interactive = deny) |
| agent_runner | trust check | Hard — own trust verification |
| remote_runner | dispatcher | Hard — audited as remote_run event |
Policy configuration
Guard reads your policy from ~/.agentnode/config.json. These are the key settings:
{
"trust": {
"minimum_trust_level": "verified"
},
"permissions": {
"network": "prompt",
"filesystem": "prompt",
"code_execution": "sandboxed"
},
"audit": {
"max_size_mb": 10,
"max_files": 5
}
}| Setting | Values | Description |
|---|---|---|
| trust.minimum_trust_level | unverified, verified, trusted, curated | Packages below this level are denied |
| permissions.network | allow, prompt, deny | How to handle packages requesting network access |
| permissions.filesystem | allow, prompt, deny | How to handle packages requesting filesystem access |
| permissions.code_execution | sandboxed, prompt, deny | How to handle packages requesting code execution |
| audit.max_size_mb | number (default: 10) | Rotate audit log when it exceeds this size |
| audit.max_files | number (default: 5) | Maximum number of rotated audit files to keep |
Environment-aware decisions
Guard detects your runtime environment and escalates decisions when risk is higher:
- Secrets detected — if environment variables like
AWS_*,OPENAI_*, orDATABASE_URLare present, Guard escalates to prompt for unverified packages with network access - CI mode — detected via
CI,GITHUB_ACTIONS, etc. Non-interactive environments use deny instead of prompt - Strict mode — set
AGENTNODE_GUARD_STRICT=trueto force all uncertain decisions to deny instead of prompt
Viewing the audit trail
Every Guard decision is logged. Use the CLI to inspect:
# Show recent decisions
$ agentnode audit show
# Show statistics
$ agentnode audit stats
# Export as JSON for analysis
$ agentnode audit show --limit 100 --json > decisions.jsonImport Tools
Already have tools written for another framework? The import command detects tool names, descriptions, and input schemas from your existing code and generates an ANP manifest automatically. No rewriting required.
Supported platforms
| Platform | What it detects | Command |
|---|---|---|
| MCP | @server.tool() decorated functions, tool descriptions, input schemas | agentnode import server.py --from mcp |
| LangChain | BaseTool subclasses, @tool decorated functions, schemas | agentnode import tools.py --from langchain |
| OpenAI Functions | Function definitions in JSON format | agentnode import functions.json --from openai |
| CrewAI | @tool decorated functions, tool descriptions | agentnode import tools.py --from crewai |
| ClawHub | ClawHub manifest files | agentnode import manifest.json --from clawhub |
| Skills.sh | Skills.sh skill configs | agentnode import skill.json --from skillssh |
Import from MCP
$ agentnode import mcp_server.py --from mcp
Detected 3 tools in mcp_server.py:
search_web -> capability: web_search
extract_page -> capability: webpage_extraction
send_email -> capability: email_sending
Generated agentnode.yaml with 3 tools.
Review and edit the manifest, then publish with: agentnode publish .Import from LangChain
$ agentnode import search_tool.py --from langchain
Detected 1 tool in search_tool.py:
SearchTool (BaseTool subclass)
name: "web_search"
description: "Search the web for information"
-> capability: web_search
Generated agentnode.yaml with 1 tool.Import from OpenAI Functions
$ agentnode import functions.json --from openai
Detected 2 functions in functions.json:
get_weather -> capability: weather_lookup
search_docs -> capability: document_search
Generated agentnode.yaml with 2 tools.Import from CrewAI
$ agentnode import crew_tools.py --from crewai
Detected 2 tools in crew_tools.py:
@tool search_internet -> capability: web_search
@tool analyze_data -> capability: data_analysis
Generated agentnode.yaml with 2 tools.Web import tool
Prefer a visual interface? Use the web-based import tool to paste your code or upload a file and generate a manifest in your browser.
After importing
The import command generates an agentnode.yaml manifest with auto-detected values. You should review the generated manifest and:
- Verify the detected capability IDs are correct
- Set the appropriate permission levels (the importer defaults to conservative values)
- Add your publisher namespace
- Verify the per-tool entrypoints are correct (e.g.
tool:create_issuefor multi-tool packs) - Run
agentnode validate .to confirm everything is correct - Publish with
agentnode publish .
Something missing or incorrect? Open an issue on GitHub.