Skip to content
This repository was archived by the owner on Mar 7, 2026. It is now read-only.

Commit 9e6f131

Browse files
author
Imran Siddique
committed
feat: add protocol integrations for A2A, MCP, and LangGraph
- A2A: CMVK-enhanced Agent Cards with trust verification - MCP: Trust-gated server and client implementations - LangGraph: Trusted graph nodes with capability checks These integrations enable AgentMesh trust layer for: - Google A2A Protocol (Agent-to-Agent) - Anthropic MCP (Model Context Protocol) - LangGraph multi-agent workflows
1 parent a19e22e commit 9e6f131

4 files changed

Lines changed: 1139 additions & 0 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"""
2+
AgentMesh Integrations
3+
======================
4+
5+
Protocol integrations for A2A, MCP, and LangGraph.
6+
"""
7+
8+
from .a2a import A2AAgentCard, A2ATrustProvider
9+
from .mcp import TrustGatedMCPServer, TrustGatedMCPClient
10+
from .langgraph import TrustedGraphNode, TrustCheckpoint
11+
12+
__all__ = [
13+
"A2AAgentCard",
14+
"A2ATrustProvider",
15+
"TrustGatedMCPServer",
16+
"TrustGatedMCPClient",
17+
"TrustedGraphNode",
18+
"TrustCheckpoint",
19+
]
Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
"""
2+
A2A Protocol Integration for AgentMesh
3+
=======================================
4+
5+
Provides CMVK-enhanced Agent Cards and trust verification for the
6+
Agent-to-Agent (A2A) Protocol developed by Google/Linux Foundation.
7+
8+
Features:
9+
- CMVK-signed Agent Cards for cryptographic identity
10+
- Trust handshake before task delegation
11+
- Capability verification for A2A tasks
12+
- Audit trail integration
13+
14+
Example:
15+
>>> from agentmesh.integrations.a2a import A2AAgentCard, A2ATrustProvider
16+
>>> from agentmesh.identity import AgentIdentity
17+
>>>
18+
>>> # Create identity and Agent Card
19+
>>> identity = AgentIdentity.create(
20+
... name="sql-agent",
21+
... sponsor_id="human@example.com",
22+
... capabilities=["execute:sql", "read:database"]
23+
... )
24+
>>> card = A2AAgentCard.from_identity(identity)
25+
>>>
26+
>>> # Publish to /.well-known/agent.json
27+
>>> card.to_json()
28+
"""
29+
30+
from __future__ import annotations
31+
32+
import json
33+
import logging
34+
import hashlib
35+
from dataclasses import dataclass, field
36+
from datetime import datetime, timedelta
37+
from typing import Any, Dict, List, Optional
38+
from enum import Enum
39+
40+
logger = logging.getLogger(__name__)
41+
42+
43+
class A2ATaskState(Enum):
44+
"""A2A Protocol task states."""
45+
SUBMITTED = "submitted"
46+
WORKING = "working"
47+
INPUT_REQUIRED = "input_required"
48+
COMPLETED = "completed"
49+
FAILED = "failed"
50+
CANCELED = "canceled"
51+
52+
53+
@dataclass
54+
class CMVKSignature:
55+
"""Cryptographic Multi-Vector Key signature for Agent Cards."""
56+
algorithm: str = "Ed25519"
57+
public_key: str = ""
58+
signature: str = ""
59+
timestamp: datetime = field(default_factory=datetime.utcnow)
60+
expires_at: Optional[datetime] = None
61+
62+
def is_valid(self) -> bool:
63+
"""Check if signature is still valid."""
64+
if self.expires_at is None:
65+
return True
66+
return datetime.utcnow() < self.expires_at
67+
68+
def to_dict(self) -> Dict[str, Any]:
69+
return {
70+
"algorithm": self.algorithm,
71+
"publicKey": self.public_key,
72+
"signature": self.signature,
73+
"timestamp": self.timestamp.isoformat(),
74+
"expiresAt": self.expires_at.isoformat() if self.expires_at else None,
75+
}
76+
77+
78+
@dataclass
79+
class A2AAgentCard:
80+
"""
81+
CMVK-enhanced A2A Agent Card.
82+
83+
Extends the standard A2A Agent Card with cryptographic identity
84+
verification using AgentMesh's CMVK infrastructure.
85+
"""
86+
# Standard A2A fields
87+
name: str
88+
description: str
89+
url: str
90+
version: str = "1.0.0"
91+
capabilities: List[str] = field(default_factory=list)
92+
skills: List[Dict[str, Any]] = field(default_factory=list)
93+
authentication: Dict[str, Any] = field(default_factory=dict)
94+
95+
# AgentMesh extensions
96+
agent_did: str = ""
97+
cmvk_signature: Optional[CMVKSignature] = None
98+
trust_score: int = 500
99+
sponsor_id: str = ""
100+
delegation_chain: List[str] = field(default_factory=list)
101+
102+
@classmethod
103+
def from_identity(
104+
cls,
105+
identity: Any, # AgentIdentity
106+
url: str,
107+
description: str = "",
108+
skills: Optional[List[Dict[str, Any]]] = None,
109+
) -> "A2AAgentCard":
110+
"""Create Agent Card from AgentMesh identity."""
111+
# Extract capabilities from identity
112+
capabilities = []
113+
if hasattr(identity, "capabilities"):
114+
capabilities = list(identity.capabilities)
115+
116+
# Build delegation chain
117+
delegation_chain = []
118+
if hasattr(identity, "delegation_chain"):
119+
delegation_chain = identity.delegation_chain
120+
121+
# Create CMVK signature
122+
signature = None
123+
if hasattr(identity, "sign"):
124+
card_data = f"{identity.did}:{url}:{datetime.utcnow().isoformat()}"
125+
sig_bytes = identity.sign(card_data.encode())
126+
signature = CMVKSignature(
127+
algorithm="Ed25519",
128+
public_key=identity.public_key if hasattr(identity, "public_key") else "",
129+
signature=sig_bytes.hex() if sig_bytes else "",
130+
timestamp=datetime.utcnow(),
131+
expires_at=datetime.utcnow() + timedelta(hours=24),
132+
)
133+
134+
return cls(
135+
name=identity.name if hasattr(identity, "name") else str(identity.did),
136+
description=description or f"AgentMesh agent: {identity.did}",
137+
url=url,
138+
capabilities=capabilities,
139+
skills=skills or [],
140+
authentication={"schemes": ["cmvk", "bearer"]},
141+
agent_did=str(identity.did) if hasattr(identity, "did") else "",
142+
cmvk_signature=signature,
143+
trust_score=identity.trust_score if hasattr(identity, "trust_score") else 500,
144+
sponsor_id=identity.sponsor_id if hasattr(identity, "sponsor_id") else "",
145+
delegation_chain=delegation_chain,
146+
)
147+
148+
def to_json(self, indent: int = 2) -> str:
149+
"""Export as A2A-compatible JSON for /.well-known/agent.json"""
150+
data = {
151+
"name": self.name,
152+
"description": self.description,
153+
"url": self.url,
154+
"version": self.version,
155+
"capabilities": {
156+
"streaming": True,
157+
"pushNotifications": False,
158+
"stateTransitionHistory": True,
159+
},
160+
"skills": self.skills,
161+
"authentication": self.authentication,
162+
# AgentMesh extensions (under x-agentmesh namespace)
163+
"x-agentmesh": {
164+
"did": self.agent_did,
165+
"trustScore": self.trust_score,
166+
"sponsorId": self.sponsor_id,
167+
"delegationChain": self.delegation_chain,
168+
"cmvkSignature": self.cmvk_signature.to_dict() if self.cmvk_signature else None,
169+
"grantedCapabilities": self.capabilities,
170+
},
171+
}
172+
return json.dumps(data, indent=indent)
173+
174+
def to_dict(self) -> Dict[str, Any]:
175+
"""Export as dictionary."""
176+
return json.loads(self.to_json())
177+
178+
def verify_signature(self, identity_registry: Any = None) -> bool:
179+
"""Verify the CMVK signature is valid."""
180+
if not self.cmvk_signature:
181+
return False
182+
183+
if not self.cmvk_signature.is_valid():
184+
logger.warning(f"Agent Card signature expired for {self.agent_did}")
185+
return False
186+
187+
# If registry provided, verify against stored identity
188+
if identity_registry and hasattr(identity_registry, "get"):
189+
stored_identity = identity_registry.get(self.agent_did)
190+
if not stored_identity:
191+
logger.warning(f"Unknown agent DID: {self.agent_did}")
192+
return False
193+
194+
# Verify public key matches
195+
if hasattr(stored_identity, "public_key"):
196+
if stored_identity.public_key != self.cmvk_signature.public_key:
197+
logger.warning(f"Public key mismatch for {self.agent_did}")
198+
return False
199+
200+
return True
201+
202+
203+
@dataclass
204+
class A2ATask:
205+
"""A2A Protocol task with trust metadata."""
206+
task_id: str
207+
session_id: str
208+
state: A2ATaskState
209+
message: Dict[str, Any]
210+
211+
# Trust metadata
212+
requester_did: str = ""
213+
executor_did: str = ""
214+
trust_verified: bool = False
215+
trust_level: str = "untrusted"
216+
217+
created_at: datetime = field(default_factory=datetime.utcnow)
218+
updated_at: datetime = field(default_factory=datetime.utcnow)
219+
220+
221+
class A2ATrustProvider:
222+
"""
223+
Trust provider for A2A Protocol interactions.
224+
225+
Verifies agent identity and trust before allowing task delegation.
226+
"""
227+
228+
def __init__(
229+
self,
230+
identity: Any, # AgentIdentity
231+
trust_bridge: Any = None, # TrustBridge
232+
min_trust_score: int = 300,
233+
):
234+
self.identity = identity
235+
self.trust_bridge = trust_bridge
236+
self.min_trust_score = min_trust_score
237+
self._verified_peers: Dict[str, datetime] = {}
238+
self._verification_cache_ttl = timedelta(minutes=15)
239+
240+
async def verify_peer(self, peer_did: str, peer_card: Optional[A2AAgentCard] = None) -> bool:
241+
"""
242+
Verify peer agent before task interaction.
243+
244+
Args:
245+
peer_did: Peer agent's DID
246+
peer_card: Optional peer's Agent Card for verification
247+
248+
Returns:
249+
True if peer is trusted
250+
"""
251+
# Check cache
252+
if peer_did in self._verified_peers:
253+
cached_time = self._verified_peers[peer_did]
254+
if datetime.utcnow() - cached_time < self._verification_cache_ttl:
255+
logger.debug(f"Using cached trust for {peer_did}")
256+
return True
257+
258+
# Verify Agent Card signature if provided
259+
if peer_card:
260+
if not peer_card.verify_signature():
261+
logger.warning(f"Invalid Agent Card signature for {peer_did}")
262+
return False
263+
264+
# Check trust score
265+
if peer_card.trust_score < self.min_trust_score:
266+
logger.warning(
267+
f"Peer {peer_did} trust score {peer_card.trust_score} "
268+
f"below minimum {self.min_trust_score}"
269+
)
270+
return False
271+
272+
# Use TrustBridge for handshake if available
273+
if self.trust_bridge and hasattr(self.trust_bridge, "verify_peer"):
274+
try:
275+
result = await self.trust_bridge.verify_peer(peer_did)
276+
if not result:
277+
logger.warning(f"TrustBridge verification failed for {peer_did}")
278+
return False
279+
except Exception as e:
280+
logger.error(f"Trust verification error: {e}")
281+
return False
282+
283+
# Cache successful verification
284+
self._verified_peers[peer_did] = datetime.utcnow()
285+
logger.info(f"Verified trust for peer {peer_did}")
286+
return True
287+
288+
async def create_task(
289+
self,
290+
peer_did: str,
291+
message: Dict[str, Any],
292+
peer_card: Optional[A2AAgentCard] = None,
293+
) -> Optional[A2ATask]:
294+
"""
295+
Create A2A task with trust verification.
296+
297+
Args:
298+
peer_did: Target agent DID
299+
message: Task message
300+
peer_card: Optional target's Agent Card
301+
302+
Returns:
303+
A2ATask if trust verified, None otherwise
304+
"""
305+
# Verify trust first
306+
if not await self.verify_peer(peer_did, peer_card):
307+
logger.warning(f"Cannot create task - peer {peer_did} not trusted")
308+
return None
309+
310+
# Create task with trust metadata
311+
task_id = hashlib.sha256(
312+
f"{self.identity.did}:{peer_did}:{datetime.utcnow().isoformat()}".encode()
313+
).hexdigest()[:16]
314+
315+
task = A2ATask(
316+
task_id=task_id,
317+
session_id=f"session-{task_id}",
318+
state=A2ATaskState.SUBMITTED,
319+
message=message,
320+
requester_did=str(self.identity.did) if hasattr(self.identity, "did") else "",
321+
executor_did=peer_did,
322+
trust_verified=True,
323+
trust_level="verified",
324+
)
325+
326+
logger.info(f"Created A2A task {task_id} for peer {peer_did}")
327+
return task
328+
329+
def get_trust_footer(self) -> Dict[str, Any]:
330+
"""
331+
Get trust verification footer for A2A messages.
332+
333+
This footer can be appended to A2A messages for audit trails.
334+
"""
335+
return {
336+
"x-agentmesh-trust": {
337+
"verifierDid": str(self.identity.did) if hasattr(self.identity, "did") else "",
338+
"timestamp": datetime.utcnow().isoformat(),
339+
"minTrustScore": self.min_trust_score,
340+
"verificationMethod": "cmvk-handshake",
341+
}
342+
}
343+
344+
345+
# Convenience exports
346+
__all__ = [
347+
"A2AAgentCard",
348+
"A2ATask",
349+
"A2ATaskState",
350+
"A2ATrustProvider",
351+
"CMVKSignature",
352+
]

0 commit comments

Comments
 (0)