A Python library for building NANDA-compatible AI agent registries
Project description
SM Bridge
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
Installation
pip install git+https://github.com/Sharathvc23/sm-bridge.git
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 correcttotal_countfor pagination)GET /nanda/resolve?agent=my-agent- Resolve a single agentGET /nanda/deltas?since=0- Get changes for syncGET /.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:
- Deploy your registry with the NANDA bridge endpoints
- Ensure
/.well-known/nanda.jsonis accessible - Contact the MIT NANDA team to register as a federated peer
- (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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sm_bridge-0.3.1.tar.gz.
File metadata
- Download URL: sm_bridge-0.3.1.tar.gz
- Upload date:
- Size: 20.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
798b761c0c9443280b39a441e0bbd7736d02fca540bbdf81450d8fb2e1f0838b
|
|
| MD5 |
03b35e177e9876d5348170b5e8e29c61
|
|
| BLAKE2b-256 |
a83389238689e5cf8ac13e72c446e432bf54dd7f5ef9a8dabd42dd3eb9abb1c3
|
Provenance
The following attestation bundles were made for sm_bridge-0.3.1.tar.gz:
Publisher:
release.yml on Sharathvc23/sm-bridge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sm_bridge-0.3.1.tar.gz -
Subject digest:
798b761c0c9443280b39a441e0bbd7736d02fca540bbdf81450d8fb2e1f0838b - Sigstore transparency entry: 1949302063
- Sigstore integration time:
-
Permalink:
Sharathvc23/sm-bridge@208ebfe0fa54d9e56f54f16d8b75119f49268978 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Sharathvc23
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@208ebfe0fa54d9e56f54f16d8b75119f49268978 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file sm_bridge-0.3.1-py3-none-any.whl.
File metadata
- Download URL: sm_bridge-0.3.1-py3-none-any.whl
- Upload date:
- Size: 24.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2a30f91c19dd09ed36d5ceae6a954045c6b6a595f0fc7aa636cf3f8be3235ee
|
|
| MD5 |
efbb8c24aad4ac08eb5ed05b154dfb03
|
|
| BLAKE2b-256 |
bc1fe0980e8eb6aa182951e1e28086ce13bfbbeabe6438334a800a153a105f30
|
Provenance
The following attestation bundles were made for sm_bridge-0.3.1-py3-none-any.whl:
Publisher:
release.yml on Sharathvc23/sm-bridge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sm_bridge-0.3.1-py3-none-any.whl -
Subject digest:
a2a30f91c19dd09ed36d5ceae6a954045c6b6a595f0fc7aa636cf3f8be3235ee - Sigstore transparency entry: 1949302253
- Sigstore integration time:
-
Permalink:
Sharathvc23/sm-bridge@208ebfe0fa54d9e56f54f16d8b75119f49268978 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Sharathvc23
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@208ebfe0fa54d9e56f54f16d8b75119f49268978 -
Trigger Event:
workflow_dispatch
-
Statement type: