Skip to main content

A Python library for building NANDA-compatible AI agent registries

Project description

SM Bridge

PyPI Python License

A Python library for building NANDA-compatible AI agent registries.

NANDA (Network of AI Agents in Decentralized Architecture) is the protocol for federated AI agent discovery and communication. This library provides the primitives needed to make your agent registry interoperable with the NANDA ecosystem.

Features

  • NANDA AgentFacts Models - Pydantic models implementing the projnanda/agentfacts-format specification
  • FastAPI Router - Drop-in endpoints for /nanda/index, /nanda/resolve, /nanda/deltas
  • Delta Store - Change tracking for registry synchronization
  • Converter Interface - Abstract pattern for integrating with your existing registry
  • Trust profiles (v0.4) - Verify heterogeneous sources (ed25519 agent-card, ANS/SCITT, DNS-AID, delegation) and normalize to one proof block (docs/trust-profiles.md)
  • Quilt onboarding (v0.4) - Entry mode (pointer-only, quilt-safe) vs hosting mode (docs/quilt-onboarding.md)
  • Transparency log (v0.4) - RFC 6962 Merkle + signed checkpoint; pass your own conformance auditor

Universal onboarding + verification (v0.4) — 5-minute path

pip install "sm-bridge[trust]"     # verification adapters (crypto in extras; core stays fastapi+pydantic)
from sm_bridge import SmBridge, SimpleAgent, SimpleAgentConverter, ANSEntryConverter, TrustRegistry
from sm_bridge.trust.ed25519_agentcard import Ed25519AgentCardProfile
from sm_bridge.trust.ans_scitt import AnsScittProfile

# Hosting mode (a source with no registry of its own) + entry mode (a registry-scale source)
conv = SimpleAgentConverter(registry_id="quilt", provider_name="Q", provider_url="https://q.example")
conv.register(SimpleAgent(id="finance", name="Finance Agent", description="does finance"))

bridge = SmBridge(
    registry_id="quilt", provider_name="Q", provider_url="https://q.example",
    converter=conv,
    trust_registry=TrustRegistry([Ed25519AgentCardProfile(), AnsScittProfile()]),
    entries=[ANSEntryConverter(registry_name="acme-ans", resolver_endpoint="https://ans.acme.example")],
)
# /nanda/resolve → SmAgentFacts + a normalized proof block
# /nanda/registries/acme-ans/resolve → a delegation pointer (the quilt never mirrors ANS's agents)

Every VERIFIED is a real signature / DNS / Merkle check; absent live infra you get an honest NOT_VERIFIED(reason), never a mocked pass. See docs/trust-profiles.md and docs/quilt-onboarding.md.

Command line

The same verification is available from the terminal ([trust] extra):

sm-bridge verify ans-scitt   --receipt receipt.cbor --root-keys root-keys.txt
sm-bridge verify jws-catalog --catalog ai-catalog.json --signature sig.jws --jwks jwks.json
sm-bridge verify agent-card  --card card.json --signature-b64 <b64> --pubkey key.pem
sm-bridge verify dns-aid     --fqdn agent.example.com [--dane]
sm-bridge verify delegation  --evidence delegation-bundle.json

Exit code 0 = VERIFIED, 1 = FAILED, 2 = NOT_VERIFIED.

Runnable demos

Two offline, self-contained scenarios (see examples/):

python examples/demo1_switchboard.py            # one query → an ANS registry (delegated) + a non-ANS catalog (verified)
python examples/demo2_domainless_delegation.py  # a domainless did:key earns scoped, revocable delegation

Installation

pip install sm-bridge

The delta sync client's default HTTP transport is an optional extra:

pip install "sm-bridge[federation]"

Or install from source:

git clone https://github.com/Sharathvc23/sm-bridge
cd sm-bridge
pip install -e .

Quick Start

Basic Usage

from fastapi import FastAPI
from sm_bridge import SmBridge, SimpleAgent

# Create the bridge
bridge = SmBridge(
    registry_id="my-registry",
    provider_name="My Company",
    provider_url="https://example.com",
    base_url="https://registry.example.com"
)

# Register agents
bridge.register_agent(SimpleAgent(
    id="my-agent",
    name="My Agent",
    description="An agent that does things",
    namespace="production",
    labels=["chat", "tool-use"],
    skills=[
        {"id": "summarize", "description": "Summarizes text"},
        {"id": "translate", "description": "Translates between languages"}
    ]
))

# Mount routers
app = FastAPI()
app.include_router(bridge.router)            # /nanda/* endpoints
app.include_router(bridge.wellknown_router)   # /.well-known/nanda.json (RFC 8615)

This gives you:

  • GET /nanda/index - List all public agents (with correct total_count for pagination)
  • GET /nanda/resolve?agent=my-agent - Resolve a single agent
  • GET /nanda/deltas?since=0 - Get changes for sync
  • GET /.well-known/nanda.json - Registry discovery (RFC 8615 compliant)

Custom Registry Integration

For existing registries with their own data models:

from sm_bridge import (
    AbstractAgentConverter,
    SmAgentFacts,
    SmProvider,
    SmEndpoints,
    SmCapabilities,
    SmSkill,
    DeltaStore,
    create_sm_router,
)
from typing import Iterator

class MyRegistryConverter(AbstractAgentConverter):
    def __init__(self, db_connection):
        super().__init__(
            registry_id="my-registry",
            provider_name="My Company",
            provider_url="https://example.com"
        )
        self.db = db_connection
    
    def to_sm(self, agent) -> SmAgentFacts:
        return SmAgentFacts(
            id=f"did:web:example.com:agents:{agent.id}",
            handle=self.build_handle(agent.namespace, agent.id),
            agent_name=agent.display_name,
            label=agent.category,
            description=agent.description,
            version=agent.version,
            provider=self.build_provider(),
            endpoints=SmEndpoints(static=[agent.endpoint_url]),
            capabilities=SmCapabilities(modalities=agent.capabilities),
            skills=[SmSkill(id=s.id, description=s.desc) for s in agent.skills],
            metadata={
                "x_my_registry": {
                    "internal_id": agent.internal_id,
                    "created_at": agent.created_at.isoformat(),
                }
            }
        )
    
    def list_agents(self, limit: int, offset: int) -> Iterator:
        return self.db.query_agents(limit=limit, offset=offset)
    
    def get_agent(self, agent_id: str):
        return self.db.get_agent(agent_id)
    
    def is_public(self, agent) -> bool:
        return agent.visibility == "public"

# Create router with custom converter
converter = MyRegistryConverter(db_connection)
delta_store = DeltaStore()

nanda_router, wellknown_router = create_sm_router(
    converter=converter,
    delta_store=delta_store,
    registry_id="my-registry",
    base_url="https://registry.example.com",
    provider_name="My Company",
    provider_url="https://example.com"
)

app = FastAPI()
app.include_router(nanda_router)
app.include_router(wellknown_router)

Models

SmAgentFacts

The core data structure for agent metadata:

from sm_bridge import SmAgentFacts

facts = SmAgentFacts(
    id="did:web:example.com:agents:my-agent",
    handle="@my-registry:production/my-agent",
    agent_name="My Agent",
    label="assistant",
    description="An AI assistant",
    version="1.0.0",
    provider=SmProvider(
        name="My Company",
        url="https://example.com"
    ),
    endpoints=SmEndpoints(
        static=["https://api.example.com/agents/my-agent"]
    ),
    capabilities=SmCapabilities(
        modalities=["text", "tool-use"],
        authentication=SmAuthentication(methods=["did-auth"])
    ),
    skills=[
        SmSkill(
            id="urn:my-registry:cap:summarize:v1",
            description="Summarizes long documents",
            inputModes=["text"],
            outputModes=["text"]
        )
    ],
    metadata={
        "x_my_registry": {
            "custom_field": "custom_value"
        }
    }
)

Handle Format

NANDA handles follow the format @registry:namespace/agent-id:

handle = SmAgentFacts.create_handle(
    registry="my-registry",
    namespace="production", 
    agent_id="my-agent"
)
# Returns: "@my-registry:production/my-agent"

Delta Store

Track changes for registry synchronization:

from sm_bridge import DeltaStore, SmAgentFacts

store = DeltaStore()

# Record an agent creation/update
delta = store.add("upsert", agent_facts)
print(f"Recorded delta with seq={delta.seq}")

# Get all changes since seq 0
deltas = store.since(0)

# Get next sequence number for polling
next_seq = store.next_seq

For production, extend PersistentDeltaStore to persist to a database:

from sm_bridge import PersistentDeltaStore

class PostgresDeltaStore(PersistentDeltaStore):
    def __init__(self, dsn: str):
        super().__init__()
        self.conn = psycopg2.connect(dsn)
    
    def _persist(self, delta):
        # INSERT INTO nanda_deltas ...
        pass
    
    def _load_since(self, seq):
        # SELECT * FROM nanda_deltas WHERE seq > ...
        pass

MCP Tools

Advertise MCP tools that agents can use:

from sm_bridge import SmBridge, SmTool

bridge = SmBridge(
    registry_id="my-registry",
    provider_name="My Company",
    provider_url="https://example.com",
    tools=[
        SmTool(
            tool_id="search",
            description="Search the web",
            endpoint="https://api.example.com/mcp/search",
            params=["query", "limit"]
        ),
        SmTool(
            tool_id="calculate",
            description="Perform calculations",
            endpoint="https://api.example.com/mcp/calculate",
            params=["expression"]
        )
    ]
)

Registry Discovery

The library serves /.well-known/nanda.json at the domain root (RFC 8615) via the separate wellknown_router:

{
  "registry_id": "my-registry",
  "registry_did": "did:web:registry.example.com",
  "namespaces": ["did:web:example.com:*"],
  "index_url": "https://registry.example.com/nanda/index",
  "resolve_url": "https://registry.example.com/nanda/resolve",
  "deltas_url": "https://registry.example.com/nanda/deltas",
  "tools_url": "https://registry.example.com/nanda/tools",
  "provider": {
    "name": "My Company",
    "url": "https://example.com"
  },
  "capabilities": ["agentfacts", "deltas", "mcp-tools"]
}

Federating with NANDA

To join the NANDA network:

  1. Deploy your registry with the NANDA bridge endpoints
  2. Ensure /.well-known/nanda.json is accessible
  3. Contact the MIT NANDA team to register as a federated peer
  4. (Optional) Implement incremental sync or gossip mechanisms for real-time or near-real-time federation

AI Catalog gateway

create_gateway_router serves the AI Catalog discovery endpoints for the agents in a DeltaStore, alongside the /nanda/* router (the two surfaces are independent):

from fastapi import FastAPI
from sm_bridge import DeltaStore, create_gateway_router

delta_store = DeltaStore()

app = FastAPI()
app.include_router(
    create_gateway_router(delta_store, base_url="https://reg.example.com", domain="example.com")
)
Endpoint Returns
GET /.well-known/ai-catalog.json CatalogDocument — all agents
GET /agents/{slug} CatalogEntry — a pointer to the agent's card
GET /cards/{slug}.json A2A AgentCard (full SmAgentFacts included under _meta)

Each SmAgentFacts is translated into a CatalogEntry and an A2A AgentCard whose url is the agent's runtime. Point a NANDA index at base_url (as a hosting_path=registry entry) to make the agents discoverable through the AI Catalog flow.

Related Packages

Package Question it answers
sm-model-provenance "Where did this model come from?" (identity, versioning, provider, NANDA serialization)
sm-model-card "What is this model?" (unified metadata schema — type, status, risk level, metrics, weights hash)
sm-model-integrity-layer "Does this model's metadata meet policy?" (rule-based checks)
sm-model-governance "Has this model been cryptographically approved for deployment?" (approval flow with signatures, quorum, scoping, revocation)
sm-bridge (this package) "How do I expose this to the NANDA network?" (FastAPI router, AgentFacts models, delta sync)

License

MIT


Personal research contributions aligned with Project NANDA standards. Stellarminds.ai

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

sm_bridge-0.4.0.tar.gz (57.6 kB view details)

Uploaded Source

Built Distribution

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

sm_bridge-0.4.0-py3-none-any.whl (70.2 kB view details)

Uploaded Python 3

File details

Details for the file sm_bridge-0.4.0.tar.gz.

File metadata

  • Download URL: sm_bridge-0.4.0.tar.gz
  • Upload date:
  • Size: 57.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for sm_bridge-0.4.0.tar.gz
Algorithm Hash digest
SHA256 89b14b0921f8bbf7bfe58d1233402e33b7cd6d847acef70cd091b691263004c6
MD5 0f053b44dcbfd682be25ac4069ecdf2c
BLAKE2b-256 d2363e8cd4b91dabe04fcefcfbf96d288c261cc73ab6864af827ed7b06910ac6

See more details on using hashes here.

Provenance

The following attestation bundles were made for sm_bridge-0.4.0.tar.gz:

Publisher: release.yml on Sharathvc23/sm-bridge

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sm_bridge-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: sm_bridge-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 70.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for sm_bridge-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 af8b76538d9302872ce0a0164e36e8e19145d7f97a7c5a4e882bb1c99e6178a5
MD5 763fac5d6548f99b5bb5c411453491c7
BLAKE2b-256 a509d090fe12dd85bc03bf86e878a14e6c202e0c3f4c0a7218f05a0fdb18befb

See more details on using hashes here.

Provenance

The following attestation bundles were made for sm_bridge-0.4.0-py3-none-any.whl:

Publisher: release.yml on Sharathvc23/sm-bridge

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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