Skip to main content

Official Python SDK for the Amorce Agent Transaction Protocol (AATP)

Project description

Amorce Python SDK

PyPI GitHub License: MIT Python 3.9+ Demo

The Standard for Secure AI Agent Transactions

See it in action: Agent Marketplace Demo - Watch AI agents negotiate with cryptographic security

Protect your AI Agent's API with cryptographic verification. The Amorce SDK handles Ed25519 signatures, zero-trust identity, and transaction validationโ€”so you can focus on building.


๐Ÿš€ Quick Start

Installation

pip install amorce-sdk

Identity Setup

Every AI Agent needs an identity (Ed25519 keypair):

from amorce import IdentityManager

# Generate a new identity (ephemeral for dev/testing)
identity = IdentityManager.generate_ephemeral()

print(f"Agent ID: {identity.agent_id}")
print(f"Public Key:\\n{identity.public_key_pem}")

๐Ÿ“ For Production: Save your identity securely

# Save for reuse
with open("my_agent_key.pem", "w") as f:
    f.write(identity.private_key_pem)

# Load next time
from amorce import LocalFileProvider
identity = IdentityManager(LocalFileProvider("my_agent_key.pem"))

๐Ÿ›ก๏ธ For Builders: Protect Your API

Are you building an AI Agent? Use the SDK to verify incoming requests on your server.

Why This Matters

  • โœ… Cryptographic proof of sender identity (Ed25519 signatures)
  • โœ… Zero-trust by default - every request is verified
  • โœ… Intent whitelisting - only allow specific actions
  • โœ… Automatic key revocation - invalid agents rejected instantly
  • โœ… No maintenance burden - public keys auto-fetched from Trust Directory

How to Verify Requests

from amorce import verify_request, AmorceSecurityError
from flask import Flask, request

app = Flask(__name__)

@app.route('/api/v1/webhook', methods=['POST'])
def handle_incoming():
    try:
        # โœ… AUTOMATIC VERIFICATION
        # SDK fetches public key from Trust Directory and verifies signature
        verified = verify_request(
            headers=request.headers,
            body=request.get_data(),
            allowed_intents=['book_table', 'check_availability', 'cancel']
        )
        
        print(f"โœ… Verified request from: {verified.agent_id}")
        print(f"Intent: {verified.payload['payload']['intent']}")
        
        # Your business logic here - 100% sure it's legitimate
        if verified.payload['payload']['intent'] == 'book_table':
            return {"status": "confirmed", "table": "A5", "time": "19:00"}
        
    except AmorceSecurityError as e:
        # Invalid signature, unknown agent, or unauthorized intent
        print(f"โŒ Rejected: {e}")
        return {"error": "Unauthorized"}, 401

That's it! Your API is now protected by cryptographic verification.

Advanced: Manual Public Key (Offline/Testing)

For testing or private networks, you can skip the Trust Directory lookup:

# Provide public key directly (no network call)
verified = verify_request(
    headers=request.headers,
    body=request.get_data(),
    public_key="-----BEGIN PUBLIC KEY-----\\n...\\n-----END PUBLIC KEY-----"
)

๐Ÿ“‹ Register Your Agent (Optional)

Want to list your service in the Amorce Network? Generate your manifest:

identity = IdentityManager.generate_ephemeral()

# ๐Ÿ–จ๏ธ  Generate manifest JSON
manifest = identity.to_manifest_json(
    name="My Restaurant Bot",
    endpoint="https://my-api.example.com/api/v1/webhook",
    capabilities=["book_table", "check_availability", "cancel_reservation"],
    description="Fine dining reservations with real-time availability"
)

# Save it
with open("agent-manifest.json", "w") as f:
    f.write(manifest)

print("โœ… Manifest created! Submit it to the Trust Directory to get listed.")

What you get:

  • ๐ŸŒ Discoverable by other agents in the network
  • ๐Ÿ” Your public key automatically distributed
  • ๐Ÿ“Š Trust score based on transaction history

๐Ÿ“ค Sending Transactions (For Clients/Testing)

Need to call another agent? The SDK makes it simple:

from amorce import AmorceClient, IdentityManager

# Setup identity
identity = IdentityManager.generate_ephemeral()

# Initialize client (zero-config - uses production mainnet)
client = AmorceClient(identity=identity)

# Send a transaction
response = client.transact(
    service_contract={"service_id": "srv_restaurant_123"},
    payload={
        "intent": "book_table",
        "params": {"date": "2025-12-01", "guests": 4, "time": "19:00"}
    }
)

if response.get("status") == "success":
    print(f"โœ… Booking confirmed: {response['data']}")

Custom Endpoints (Development/Testing)

client = AmorceClient(
    identity=identity,
    directory_url="http://localhost:8080",
    orchestrator_url="http://localhost:8081"
)

โšก Async Support (NEW in v0.2.0)

For high-performance applications:

from amorce import AsyncAmorceClient

async with AsyncAmorceClient(identity=identity) as client:
    response = await client.transact(
        service_contract={"service_id": "srv_restaurant_123"},
        payload={"intent": "book_table", "params": {...}}
    )

Features:

  • HTTP/2 multiplexing
  • Exponential backoff + jitter
  • Auto-generated idempotency keys
  • 3x faster for concurrent requests

๐Ÿค Human-in-the-Loop (HITL) Support

Enable human oversight for critical agent decisions with built-in approval workflows.

When to Use HITL

  • High-value transactions - Booking reservations, making purchases
  • Data sharing - Before sending personal information to third parties
  • Irreversible actions - Cancellations, deletions, confirmations
  • Regulatory compliance - Finance, healthcare, legal industries

Basic HITL Workflow

from amorce import AmorceClient, IdentityManager

identity = IdentityManager.generate_ephemeral()
client = AmorceClient(identity=identity)

# 1. Agent negotiates with service
response = client.transact(
    service_contract={"service_id": "srv_restaurant_123"},
    payload={"intent": "book_table", "guests": 4, "date": "2025-12-05"}
)

# 2. Request human approval before finalizing
approval_id = client.request_approval(
    transaction_id=response['transaction_id'],
    summary=f"Book table for 4 guests at {response['restaurant']['name']}",
    details=response['result'],
    timeout_seconds=300  # 5 minute timeout
)

print(f"Awaiting approval: {approval_id}")

# 3. Human reviews and approves (via SMS, email, app, etc.)
# ... your notification logic here ...

# 4. Check approval status
status = client.check_approval(approval_id)
if status['status'] == 'approved':
    # 5. Finalize the transaction
    final_response = client.transact(
        service_contract={"service_id": "srv_restaurant_123"},
        payload={"intent": "confirm_booking", "booking_id": response['booking_id']}
    )
    print("โœ… Booking confirmed!")

Submitting Approval Decisions

Your application collects human input and submits the decision:

# Human approved via your UI/SMS/voice interface
client.submit_approval(
    approval_id=approval_id,
    decision="approve",  # or "reject"
    approved_by="user@example.com",
    comments="Looks good for the business lunch"
)

LLM-Interpreted Approvals

Use AI to interpret natural language responses:

import google.generativeai as genai

# Human responds: "yes sounds perfect"
human_response = "yes sounds perfect"

# LLM interprets the intent
interpretation = genai.GenerativeModel('gemini-pro').generate_content(
    f'Is this approving or rejecting? "{human_response}" Answer: APPROVE or REJECT'
).text

decision = "approve" if "APPROVE" in interpretation.upper() else "reject"

client.submit_approval(
    approval_id=approval_id,
    decision=decision,
    approved_by="user@example.com",
    comments=f"Original response: {human_response}"
)

Channel-Agnostic Notifications

HITL is protocol-level - you choose how to notify humans:

  • SMS (Twilio): "Sarah wants to book Le Petit Bistro for 4. Reply YES/NO"
  • Email: Send approval link with one-click approve/reject
  • Voice (Vapi.ai): "Your assistant needs approval. Say approve or decline"
  • Push notification: Mobile app notification
  • Slack/Teams: Bot message with buttons

Example with Twilio:

from twilio.rest import Client

# Create approval
approval_id = client.request_approval(...)

# Send SMS
twilio = Client(account_sid, auth_token)
twilio.messages.create(
    to="+1234567890",
    from_="+0987654321",
    body=f"Sarah needs approval: Book table for 4 at Le Petit Bistro tomorrow 7pm. Reply YES or NO"
)

# Poll for response or use webhook
# When you receive "YES", submit approval
client.submit_approval(approval_id, decision="approve", approved_by="sms:+1234567890")

Advanced: Approval Timeouts

Approvals automatically expire after the timeout period:

approval_id = client.request_approval(
    transaction_id=tx_id,
    summary="High-value purchase: $5,000",
    timeout_seconds=600  # 10 minutes
)

# Later...
status = client.check_approval(approval_id)
if status['status'] == 'expired':
    print("โฑ๏ธ Approval request timed out - transaction cancelled")

Best Practices

  1. Clear summaries - Make approval requests easy to understand
  2. Appropriate timeouts - Balance urgency vs. convenience
  3. Audit trail - All approvals are logged with timestamps and user IDs
  4. Fallback handling - Handle expired/rejected approvals gracefully
  5. Security - Verify human identity before submitting approvals

๐Ÿ”’ Security Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   Client    โ”‚                  โ”‚  Orchestrator    โ”‚                  โ”‚   Service   โ”‚
โ”‚   Agent     โ”‚                  โ”‚  (Amorce)        โ”‚                  โ”‚   Provider  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚                                  โ”‚                                    โ”‚
       โ”‚ 1. Sign request                  โ”‚                                    โ”‚
       โ”‚    (Ed25519)                     โ”‚                                    โ”‚
       โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚                                    โ”‚
       โ”‚                                  โ”‚ 2. Verify signature                โ”‚
       โ”‚                                  โ”‚    (fetch public key from          โ”‚
       โ”‚                                  โ”‚     Trust Directory)               โ”‚
       โ”‚                                  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚
       โ”‚                                  โ”‚ 3. Forward verified request        โ”‚
       โ”‚                                  โ”‚    (with sender identity)          โ”‚
       โ”‚                                  โ”‚                                    โ”‚
       โ”‚                                  โ”‚ 4. Service verifies again          โ”‚
       โ”‚                                  โ”‚    (optional double-check)         โ”‚
       โ”‚                                  โ”‚<โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
       โ”‚<โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ 5. Return signed response          โ”‚
       โ”‚                                  โ”‚                                    โ”‚

Zero-Trust Principles:

  1. Every request is signed by sender
  2. Every signature is verified before processing
  3. Public keys are immutable (tied to agent_id)
  4. Revoked agents are immediately rejected

๐Ÿ“š API Reference

IdentityManager

Factory Methods:

  • generate_ephemeral() - Create new Ed25519 identity (in-memory)
  • IdentityManager(provider) - Load from file/env/secret manager

Properties:

  • agent_id - SHA-256 hash of public key (deterministic)
  • public_key_pem - Public key in PEM format
  • private_key_pem - Private key in PEM format (โš ๏ธ handle carefully)

Methods:

  • sign_data(bytes) - Sign raw bytes, return base64 signature
  • to_manifest_json(...) - Generate agent manifest for registration
  • verify_signature(public_key, data, signature) - Static verification method

verify_request()

For Builders - Verify incoming signed requests

verify_request(
    headers: Dict[str, str],
    body: bytes,
    allowed_intents: Optional[List[str]] = None,
    public_key: Optional[str] = None,
    directory_url: str = "https://directory.amorce.io"
) -> VerifiedRequest

Returns: VerifiedRequest with .agent_id, .payload, .signature
Raises: AmorceSecurityError if verification fails

AmorceClient

Constructor:

AmorceClient(
    identity: IdentityManager,
    directory_url: str = "https://directory.amorce.io",    # Zero-config!
    orchestrator_url: str = "https://orchestrator.amorce.io",
    agent_id: Optional[str] = None,
    api_key: Optional[str] = None
)

Methods:

  • discover(service_type) - Find services in Trust Directory
  • transact(service_contract, payload, priority) - Execute transaction
  • request_approval(transaction_id, summary, details, timeout_seconds) - Create HITL approval request
  • check_approval(approval_id) - Get approval status
  • submit_approval(approval_id, decision, approved_by, comments) - Submit approval decision

Exceptions

  • AmorceError - Base exception
  • AmorceConfigError - Invalid configuration
  • AmorceNetworkError - Network/connection errors
  • AmorceAPIError - API errors (4xx, 5xx)
  • AmorceSecurityError - Signature/verification failures โš ๏ธ
  • AmorceValidationError - Data validation errors

๐Ÿ› ๏ธ Development

# Clone and install
git clone https://github.com/trebortGolin/amorce_py_sdk.git
cd amorce_py_sdk
pip install -e ".[dev]"

# Run tests
pytest --cov=amorce

# Type checking
mypy amorce/

๐Ÿ“„ License

MIT License - See LICENSE for details


๐Ÿ”— Related Projects


๐Ÿ“ Changelog

v0.2.2 (2025-12-15) ๐Ÿ†•

  • [NEW] serve_well_known() - Auto-serve A2A manifest for agent discoverability
  • [NEW] fetch_manifest() - Fetch A2A manifest from Amorce Directory
  • [NEW] generate_manifest_file() - Generate static manifest file for deployment

v0.2.1 (2025-11-30)

  • [NEW - FOR BUILDERS] verify_request() - Verify incoming signed requests
  • [NEW] to_manifest_json() - Generate agent manifest for registration
  • [ENHANCEMENT] Zero-config defaults (production mainnet URLs)
  • [DOCS] Restructured README for Builders-first messaging

v0.2.0 (2025-11-30)

  • [FEATURE] AsyncAmorceClient with HTTP/2 support
  • [FEATURE] Exponential backoff + jitter retry logic
  • [FEATURE] Auto-generated idempotency keys
  • [FEATURE] Structured AmorceResponse model
  • [BREAKING] Requires httpx and tenacity dependencies

v0.1.7

  • Initial stable release
  • Ed25519 signature support
  • Trust Directory integration
  • Sync AmorceClient

๐ŸŒ A2A Discovery: Make Your Agent Discoverable (NEW in v0.2.2)

Register your agent and instantly make it discoverable in the A2A ecosystem.

When you register your agent at amorce.io/register, you can easily serve the required /.well-known/agent.json manifest using our SDK helpers.

Option 1: One-Liner Integration (Recommended)

from amorce import serve_well_known
from fastapi import FastAPI

app = FastAPI()

# Add /.well-known/agent.json route with one line!
app = serve_well_known(app, agent_id="your-registered-agent-id")

Works with Flask too:

from flask import Flask
from amorce import serve_well_known

app = Flask(__name__)
app = serve_well_known(app, agent_id="your-agent-id", framework="flask")

Option 2: Fetch Manifest Programmatically

from amorce import fetch_manifest_sync

# Get your manifest from Amorce Directory
manifest = fetch_manifest_sync("your-agent-id")
print(manifest)
# {
#   "name": "My Agent",
#   "url": "https://my-agent.com",
#   "protocol_version": "A2A/1.0",
#   "authentication": { "type": "amorce", "public_key": "..." }
# }

Option 3: Generate Static File

from amorce import generate_manifest_file

# Generate .well-known/agent.json file for deployment
generate_manifest_file("your-agent-id", ".well-known/agent.json")
# โœ… Generated .well-known/agent.json
# Host this file at: https://your-agent.com/.well-known/agent.json

Why A2A Discovery Matters

  • ๐Ÿ” Discoverable - Other agents can find and verify your agent
  • ๐Ÿ” Trusted - Public key distributed via trusted directory
  • ๐Ÿ”— Interoperable - Works with Google A2A, MCP, and Amorce protocols
  • โšก Zero Maintenance - Manifest auto-updated from your registration

Built with โค๏ธ for the Agent Economy

๐Ÿ”Œ MCP Integration - Production Ready โœ…

Use Model Context Protocol tools through Amorce with cryptographic security and human oversight.

The Amorce SDK provides production-ready integration with Model Context Protocol servers, adding Ed25519 signatures and human-in-the-loop approvals to all tool calls.

๐Ÿš€ Quick Start

from amorce import IdentityManager, MCPToolClient

# 1. Create your agent identity
identity = IdentityManager.generate_ephemeral()

# 2. Connect to MCP wrapper
mcp = MCPToolClient(identity, wrapper_url="http://localhost:5001")

# 3. Discover available tools
tools = mcp.list_tools()
for tool in tools:
    hitl = "๐Ÿ”’" if tool.requires_approval else "โœ“"
    print(f"{hitl} {tool.name}: {tool.description}")

# 4. Call tools (read operations)
result = mcp.call_tool('filesystem', 'read_file', {'path': '/tmp/data.txt'})
print(result)

# 5. Call tools requiring approval (write operations)
try:
    mcp.call_tool('filesystem', 'write_file', {
        'path': '/tmp/output.txt',
        'content': 'Hello from Amorce!'
    })
except ValueError as e:
    print("Approval required!")  
    # Request approval through orchestrator
    approval_id = request_approval(...)  
    result = mcp.call_tool('filesystem', 'write_file', {...}, approval_id=approval_id)

โœ… Production Features

Feature Status Description
Cryptographic Signatures โœ… Production Ed25519 on every request
HITL Approvals โœ… Production Required for write/delete/move
Rate Limiting โœ… Production 20 req/min, configurable
Load Tested โœ… Verified 50 concurrent, 5ms avg response
Error Handling โœ… Comprehensive Timeouts, connection errors, validation
Trust Directory โœ… 95% Ready Public key distribution

๐Ÿ“– Complete Example with HITL

from amorce import IdentityManager, MCPToolClient, AmorceClient

# Setup
identity = IdentityManager.generate_ephemeral()
mcp = MCPToolClient(identity, "http://localhost:5001")
client = AmorceClient(identity, orchestrator_url="http://localhost:8080")

# List tools and check HITL requirements
tools = mcp.list_tools()
write_tool = next(t for t in tools if t.name == 'write_file')
print(f"Write file requires approval: {write_tool.requires_approval}")  # True

# Attempt without approval (will fail)
try:
    result = mcp.call_tool('filesystem', 'write_file', {
        'path': '/tmp/important.txt',
        'content': 'Critical data'
    })
except ValueError as e:
    print(f"Blocked: {e}")  # "Tool requires approval"
    
# Request approval
approval = client.request_approval(
    tool_name='write_file',
    tool_args={'path': '/tmp/important.txt', 'content': 'Critical data'},
    reason='Writing ML model output'
)

# Human reviews and approves (via UI or API)
# ... approval.approve() ...

# Execute with approval
result = mcp.call_tool('filesystem', 'write_file', {
    'path': '/tmp/important.txt',
    'content': 'Critical data'
}, approval_id=approval.id)

print(f"File written successfully: {result}")

๐ŸŽฏ Tool Categories

Category Examples HITL Required
Read Operations read_file, list_directory, search โŒ No
Write Operations write_file, edit_file โœ… Yes
Destructive Operations delete_file, move_file โœ… Yes
Search/Query brave_search, database_query โŒ No (read-only)

๐Ÿ“ˆ Performance

Production-Tested Metrics:

  • Response Time: 3-9ms average
  • Concurrent: 50 requests in 40ms
  • Rate Limit: 20 req/min (enforced)
  • Uptime: 100% under load testing

๐Ÿ”— Available MCP Servers

Access 80+ production MCP servers through Amorce:

# Filesystem operations
mcp.call_tool('filesystem', 'read_file', {'path': '/data/input.json'})

# Web search
mcp.call_tool('search', 'brave_search', {'query': 'AI agents 2024'})

# Database access (with HITL)
mcp.call_tool('postgres', 'execute_query', {'sql': 'SELECT * FROM users'}, approval_id)

# Git operations (with HITL)
mcp.call_tool('git', 'commit', {'message': 'Update config'}, approval_id)

View all 80+ MCP servers โ†’

๐Ÿ“š Additional Resources


Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

amorce_sdk-0.2.2.tar.gz (40.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

amorce_sdk-0.2.2-py3-none-any.whl (28.7 kB view details)

Uploaded Python 3

File details

Details for the file amorce_sdk-0.2.2.tar.gz.

File metadata

  • Download URL: amorce_sdk-0.2.2.tar.gz
  • Upload date:
  • Size: 40.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for amorce_sdk-0.2.2.tar.gz
Algorithm Hash digest
SHA256 0d1386e2edef139565e56580ccce709fdce21eff58bb86826fa4ad88462e2e64
MD5 a20f25706b91b1f62cb4ded65aabb8b9
BLAKE2b-256 2197db1d556bd1bc88fee8f83326cbfaf10375db6f7f042ee17a53cf033cd851

See more details on using hashes here.

File details

Details for the file amorce_sdk-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: amorce_sdk-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 28.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for amorce_sdk-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 cf9d67188fa690b3476fa71cd857e1efe67609cc5514026a4e49945ef3537a93
MD5 f775b9ef0dcc944a7b1c3a8d3b8b0941
BLAKE2b-256 494fb2d360668819caf883ea20d90c67181314a614f5d0b03f97eeb1535f816c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page