AutoGen Integration
Use Attestd as an AutoGen FunctionTool to give agents real-time CVE risk and supply chain integrity data. These patterns target autogen-agentchat 0.7.5, the current stable release.
In multi-agent workflows, a compromised dependency approved by one agent propagates to every downstream agent that trusts its output. The most important correctness concern: outside_coverage is not a safety signal. Catch AttestdUnsupportedProductError and return an explicit unknown-risk response.
Install
Pin autogen-agentchat to 0.7.5. You also need a model client from autogen-ext (OpenAI shown below).
pip install "autogen-agentchat==0.7.5" autogen-core autogen-ext[openai] attestdFunctionTool definition
Wrap the Attestd check in FunctionTool from autogen_core.tools. Instantiate attestd.Client once at module level and capture it in the closure.
import os
import attestd
from autogen_core.tools import FunctionTool
from attestd import AttestdUnsupportedProductError
# Instantiate once — captured in closure. Never instantiate inside the tool function.
_client = attestd.Client(api_key=os.environ["ATTESTD_API_KEY"])
def check_package_vulnerability(product: str, version: str) -> dict:
"""Check whether a software package version has known CVE vulnerabilities
or supply chain compromise. Use before deploying or recommending any
software dependency. outside_coverage=True means no data — treat as
unknown risk, not safe. typosquat_detected=True means the name itself
is suspect; use resembles to recover the intended package."""
try:
result = _client.check(product, version)
sc = result.supply_chain
return {
"outside_coverage": False,
"risk_state": result.risk_state,
"remote_exploitable": result.remote_exploitable,
"actively_exploited": result.actively_exploited,
"patch_available": result.patch_available,
"fixed_version": result.fixed_version,
"supply_chain_compromised": sc.compromised if sc is not None else False,
"supply_chain_malware_type": sc.malware_type if sc is not None else None,
"supply_chain_still_live": (
(sc.removed_at is None) if (sc is not None and sc.compromised) else None
),
}
except AttestdUnsupportedProductError as e:
if e.typosquat and e.typosquat.detected:
return {
"outside_coverage": True,
"typosquat_detected": True,
"typosquat_kind": e.typosquat.kind,
"resembles": e.typosquat.resembles,
"likely_intended": list(e.typosquat.likely_intended),
"message": (
f"'{product}' fails package name integrity "
f"(kind={e.typosquat.kind}). "
f"Likely intended: {e.typosquat.resembles}. "
f"Do not install this package."
),
}
return {
"outside_coverage": True,
"typosquat_detected": False,
"risk_state": None,
"message": f"No Attestd coverage for '{product}'. Treat as unknown risk.",
}
attestd_tool = FunctionTool(
check_package_vulnerability,
description=(
"Check CVE risk and supply chain integrity for a software dependency. "
"outside_coverage=True means unknown risk, not safe."
),
)Single-agent pattern
One AssistantAgent with the Attestd tool in a RoundRobinGroupChat. Use TextMentionTermination with sources= so termination only fires on agent output, not the initial user task.
import asyncio
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.conditions import TextMentionTermination
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.ui import Console
from autogen_ext.models.openai import OpenAIChatCompletionClient
from attestd_tool import attestd_tool
model_client = OpenAIChatCompletionClient(model="gpt-4o-mini")
security_agent = AssistantAgent(
name="security_agent",
model_client=model_client,
tools=[attestd_tool],
system_message=(
"You are a security-aware deployment assistant. "
"Before approving any software dependency, call check_package_vulnerability. "
"Block if risk_state is 'critical' or 'high', supply_chain_compromised is True, "
"or typosquat_detected is True. "
"If outside_coverage is True, state that explicitly. Do not treat it as safe. "
"End with APPROVED or BLOCKED and your reasoning."
),
)
termination = TextMentionTermination("APPROVED", sources=["security_agent"]) | TextMentionTermination(
"BLOCKED", sources=["security_agent"]
)
team = RoundRobinGroupChat([security_agent], termination_condition=termination)
async def main() -> None:
await Console(
team.run_stream(
task="Is it safe to deploy with runc 1.0.0 and litellm 1.82.7?"
)
)
asyncio.run(main())Multi-agent safety gate
A dedicated security_gate agent runs Attestd checks before a deployment_agent can proceed. Combine termination conditions with | so the team stops on either APPROVED or BLOCKED from the security gate.
import asyncio
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.conditions import TextMentionTermination
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.ui import Console
from autogen_ext.models.openai import OpenAIChatCompletionClient
from attestd_tool import attestd_tool
model_client = OpenAIChatCompletionClient(model="gpt-4o-mini")
security_gate = AssistantAgent(
name="security_gate",
model_client=model_client,
tools=[attestd_tool],
system_message=(
"You are a security gate agent. Before any software dependency is "
"deployed or recommended, check it with check_package_vulnerability. "
"Block if risk_state is 'critical' or 'high', supply_chain_compromised is True, "
"or typosquat_detected is True. "
"If outside_coverage is True, state that explicitly. Do not treat it as safe. "
"Respond with APPROVED or BLOCKED with your reasoning."
),
)
deployment_agent = AssistantAgent(
name="deployment_agent",
model_client=model_client,
system_message=(
"You handle deployment tasks. Wait for security_gate to approve "
"all dependencies before proceeding."
),
)
termination = TextMentionTermination("APPROVED", sources=["security_gate"]) | TextMentionTermination(
"BLOCKED", sources=["security_gate"]
)
team = RoundRobinGroupChat(
[security_gate, deployment_agent],
termination_condition=termination,
)
async def main() -> None:
await Console(
team.run_stream(
task=(
"Deploy a stack with runc 1.0.0, litellm 1.82.7, "
"node-ipc 9.1.6, and nginx 1.27.4."
)
)
)
asyncio.run(main())Return fields
The tool returns these fields. Design agent branching logic around them.
| Field | Semantics |
|---|---|
outside_coverage | true if Attestd has no data for this product. Not a safety signal — treat as unknown risk. Check typosquat_detected on this path. |
risk_state | "critical" | "high" | "elevated" | "low" | "none" | null (when outside_coverage). Block on critical or high. |
remote_exploitable | true if the vulnerability has a network attack vector. Drives the high vs elevated split in risk_state. |
actively_exploited | true if in the CISA KEV catalog. Block regardless of risk_state if true. |
patch_available | true if a fixed version is known. Use with fixed_version to tell the agent what to recommend. |
fixed_version | The earliest clean version, or null if no patch exists yet. |
supply_chain_compromised | true if a malicious publish was detected on npm or PyPI. Block immediately. |
supply_chain_malware_type | Malware classification if compromised, else null. e.g. "malware", "keylogger", "cryptominer". |
supply_chain_still_live | true if the compromised version has not been removed from the registry. null when not compromised. A still-live compromise is more urgent. |
typosquat_detected | Only present on outside_coverage responses. true means the package name fails integrity — do not install. |
typosquat_kind | "hallucination" (AI-invented or conflated name) or "typosquat" (misspelling of a real package). |
resembles / likely_intended | The canonical package(s) the requested name most closely matches. Use to recover the intended dependency. |
- → LangChain Integration (StructuredTool definition, same agent policy for Python LangChain)
- → MCP Server (hosted at mcp.attestd.io for Claude Code and Cursor)
- → AI Agent Integration (generic function tool, OpenAI function calling, outside-coverage policy)
- → Python SDK Reference (AsyncClient, error types, attestd.testing mock transports)
- → Response Field Reference (full semantics for every field returned by /v1/check)