Microsoft Agent Framework (MAF)
Il backend di OpenData AI usa già il agent-framework Microsoft sotto al cofano. Questo significa che puoi costruire un agente locale che riusa esattamente gli stessi MCP server (CKAN, ISTAT, OSM) con poche righe di codice.
Setup
# agent-framework è pubblicato come pre-release: serve --pre pip install --pre agent-framework anthropic # Avvia i tre MCP server in altre shell (vedi /docs/mcp): # TRANSPORT=streamable-http PORT=8080 ckan-mcp # TRANSPORT=streamable-http PORT=8081 istat-mcp # TRANSPORT=streamable-http PORT=8082 osm-mcp
Agente con un singolo MCP (CKAN)
Il pattern minimo: un ChatAgent che usa Claude come modello e MCPStreamableHTTPTool per collegarsi al server CKAN.
import asyncio
from agent_framework import ChatAgent
from agent_framework.anthropic import AnthropicChatClient
from agent_framework.mcp import MCPStreamableHTTPTool
CKAN_MCP_URL = "http://localhost:8080/mcp"
SYSTEM = (
"Sei un assistente specializzato nei portali CKAN italiani. "
"Usa il tool package_search per cercare dataset su dati.gov.it. "
"Rispondi in italiano con un elenco numerato delle risorse trovate."
)
async def main() -> None:
async with MCPStreamableHTTPTool(name="ckan", url=CKAN_MCP_URL) as ckan:
agent = ChatAgent(
chat_client=AnthropicChatClient(model="claude-haiku-4-5"),
instructions=SYSTEM,
tools=[ckan],
)
reply = await agent.run("Cerca dataset sulla qualità dell'aria a Milano")
print(reply.text)
asyncio.run(main())Multi-MCP: CKAN + ISTAT + OSM
Stesso pattern, più tool. L'agente decide a quale server chiedere in base al system prompt e al contenuto della domanda.
import asyncio
from contextlib import AsyncExitStack
from agent_framework import ChatAgent
from agent_framework.anthropic import AnthropicChatClient
from agent_framework.mcp import MCPStreamableHTTPTool
MCPS = {
"ckan": "http://localhost:8080/mcp",
"istat": "http://localhost:8081/mcp",
"osm": "http://localhost:8082/mcp",
}
async def main() -> None:
async with AsyncExitStack() as stack:
tools = [
await stack.enter_async_context(
MCPStreamableHTTPTool(name=name, url=url)
)
for name, url in MCPS.items()
]
agent = ChatAgent(
chat_client=AnthropicChatClient(model="claude-sonnet-4-6"),
instructions=(
"Sei un agente per open data italiani ed europei. "
"Usa il tool 'ckan' per portali CKAN, 'istat' per SDMX "
"(ISTAT/Eurostat/OCSE) e 'osm' per dati geografici. "
"Includi sempre il portale/agency di origine quando citi una risorsa."
),
tools=tools,
)
reply = await agent.run(
"Confronta la popolazione di Milano nel 2024 tra ISTAT ed Eurostat, "
"e disegna i confini comunali su una mappa."
)
print(reply.text)
asyncio.run(main())Streaming dei tool
Per rispondere in streaming (utile in app o CLI interattive) usa agent.run_streaming(...):
async for chunk in agent.run_streaming("Quali dataset ISTAT sul mercato del lavoro?"):
if chunk.delta_text:
print(chunk.delta_text, end="", flush=True)
if chunk.tool_calls:
for tc in chunk.tool_calls:
print(f"\n ↳ tool {tc.name}({tc.arguments})")Note operative
- Pre-release pinning:
agent-frameworkè pubblicato come pre-release.pip install --preè necessario, e in CI conviene fissare la versione esatta. - Hostnames in Docker: dentro al docker-compose usa i nomi servizio (
http://ckan-mcp:8080/mcp); da host usalocalhost. - Provider alternativo: se non hai una chiave Anthropic, sostituisci
AnthropicChatClientcon il client Azure Foundry o Ollama (l'SDK supporta i tre). - Rate limit del backend: questo esempio chiama direttamente i server MCP, quindi non passa per i rate limit del backend OpenData AI. Se invece consumi l'endpoint REST
/datasets/searchil limite è 60 req/min/utente — vedi /docs/rate-limits.