Protocol Integration¶
Duration: 60 minutes
Level: Advanced
Module: fcc.protocols
This tutorial teaches you how to expose FCC personas as interoperable agents using the A2A (Agent-to-Agent) protocol and the MCP (Model Context Protocol). You will generate Agent Cards from R.I.S.C.E.A.R. specs, set up an MCP server with tools, resources, and prompts, route messages across protocols with the ProtocolBridge, and generate an AGENTS.md manifest.
Prerequisites¶
- Completed beginner/intermediate tutorials
- Working FCC development environment
- Familiarity with
PersonaRegistryandWorkflowActionRegistry
A2A Agent Card Generation¶
The A2A protocol allows agents to discover and communicate with each other. The CardBuilder class translates FCC persona specifications into A2A-compatible Agent Cards.
Each R.I.S.C.E.A.R. field maps to an Agent Card field:
persona.namebecomescard.namepersona.riscear.rolebecomescard.descriptionpersona.riscear.archetypebecomes a capabilitypersona.riscear.role_skillsbecome additional capabilities- Workflow actions become skill definitions
from fcc.personas.registry import PersonaRegistry
from fcc.protocols.a2a.card_builder import CardBuilder
# Load the persona registry
registry = PersonaRegistry.from_yaml_directory("src/fcc/data/personas")
# Create a card builder with a custom base URL
builder = CardBuilder(base_url="https://agents.example.com")
# Build a card for a single persona
persona = registry.get("RC")
card = builder.build_card(persona)
print(f"Agent: {card.name}")
print(f"ID: {card.id}")
print(f"Description: {card.description}")
print(f"Endpoint: {card.url}")
print(f"Capabilities: {len(card.capabilities)}")
# Serialize to JSON
json_str = CardBuilder.to_json(card, indent=2)
print(json_str)
Building Cards for the Entire Registry¶
You can generate Agent Cards for every persona at once. When you pass an action_registry, the builder maps workflow actions to A2A skill definitions:
from fcc.workflow.actions import WorkflowActionRegistry
action_registry = WorkflowActionRegistry.from_yaml_directory(
"src/fcc/data/personas/actions"
)
# Build cards for all 102 personas
cards = builder.build_all_cards(registry, action_registry)
print(f"Generated {len(cards)} Agent Cards")
# Write a single card as .well-known/agent.json
from pathlib import Path
path = CardBuilder.write_well_known(cards[0], Path("output"))
print(f"Written to: {path}")
MCP Server Setup¶
The FccMcpServer class provides an MCP-compatible server with 14 tools, 14 resources, and 6 prompts out of the box. It integrates with the FCC event bus to publish protocol lifecycle events.
from fcc.messaging.bus import EventBus
from fcc.protocols.mcp.server import FccMcpServer
# Create a server with event bus integration
bus = EventBus()
server = FccMcpServer(event_bus=bus)
print(f"Tools: {server.tool_count}") # 14
print(f"Resources: {server.resource_count}") # 14
print(f"Prompts: {server.prompt_count}") # 6
# List all available tools
for tool in server.list_tools():
print(f" - {tool['name']}: {tool['description']}")
Registering Tool Handlers¶
Tools require registered handler functions before they can be invoked. Each handler receives keyword arguments and returns a result dictionary:
def persona_lookup_handler(persona_id: str = "RC") -> dict:
persona = registry.get(persona_id)
return {
"id": persona.id,
"name": persona.name,
"role": persona.riscear.role,
"category": persona.category,
}
# Register the handler for a built-in tool
server.register_tool_handler("persona_lookup", persona_lookup_handler)
# Call the tool
result = server.call_tool("persona_lookup", {"persona_id": "BC"})
print(result)
# {"status": "ok", "result": {"id": "BC", "name": "Blueprint Crafter", ...}}
Reading Resources and Rendering Prompts¶
Resources expose data via URI patterns. Prompts provide reusable templates:
# Read a resource
resource_data = server.read_resource("fcc://personas")
print(resource_data["status"]) # "ok"
# Render a prompt with arguments
prompt_result = server.get_prompt(
"persona_review",
{"persona_id": "RC", "content": "Draft research plan"}
)
print(prompt_result["description"])
for message in prompt_result["messages"]:
print(message)
Protocol Bridge¶
The ProtocolBridge routes messages between A2A and MCP protocols through the event bus. It publishes protocol-specific lifecycle events and delegates to registered handlers:
from fcc.protocols.bridge import ProtocolBridge
bus = EventBus()
bridge = ProtocolBridge.create_default(bus)
# Default routes are registered for "a2a" and "mcp"
print(bridge.list_routes()) # ["a2a", "mcp"]
# Route an A2A message
result = bridge.route("a2a", {
"task_id": "task-001",
"skill": "review",
"payload": {"document": "research_plan.md"},
})
print(result)
# {"status": "accepted", "protocol": "a2a", "message_id": "task-001"}
# Route an MCP message
result = bridge.route("mcp", {
"tool": "persona_lookup",
"arguments": {"persona_id": "DE"},
})
print(result)
# {"status": "accepted", "protocol": "mcp", "tool": "persona_lookup"}
Custom Protocol Handlers¶
Replace the default stubs with real implementations that wire into your agent infrastructure:
def custom_a2a_handler(message: dict) -> dict:
task_id = message.get("task_id", "unknown")
# Dispatch to the appropriate persona agent
return {"status": "completed", "task_id": task_id, "result": "..."}
bridge.register_route("a2a", custom_a2a_handler)
AGENTS.md Generation¶
The AgentsMdGenerator produces a standard AGENTS.md file listing every persona as an A2A-compatible agent entry:
from fcc.protocols.agents_md import AgentsMdGenerator
# Build from the persona registry
gen = AgentsMdGenerator.from_registry(registry, action_registry)
print(f"Agents listed: {len(gen)}")
# Preview the rendered markdown
print(gen.render()[:500])
# Write to disk
gen.write(Path("AGENTS.md"))
The output follows the Google A2A standard format, with each agent listing its ID, description, endpoint, capabilities, and skills.
Event Bus Integration¶
All protocol operations publish events to the FCC event bus, enabling observability and auditing:
PROTOCOL_A2A_TASK_RECEIVED-- when an A2A task arrivesPROTOCOL_MCP_TOOL_CALLED-- when an MCP tool is invokedPROTOCOL_MCP_TOOL_COMPLETED-- when a tool call succeedsPROTOCOL_MCP_TOOL_FAILED-- when a tool call errorsPROTOCOL_MCP_RESOURCE_READ-- when a resource is accessedPROTOCOL_BRIDGE_ERROR-- when routing fails
Subscribe to these events for monitoring and debugging:
from fcc.messaging.events import EventType
bus.subscribe(
EventType.PROTOCOL_MCP_TOOL_CALLED,
lambda event: print(f"Tool called: {event.payload['tool']}")
)
Summary¶
In this tutorial you learned how to:
- Generate A2A Agent Cards from R.I.S.C.E.A.R. persona specs with
CardBuilder - Set up an MCP server with 14 tools, 14 resources, and 6 prompts
- Route cross-protocol messages with
ProtocolBridge - Generate an AGENTS.md manifest with
AgentsMdGenerator - Monitor protocol operations through the event bus
Next Steps¶
- Knowledge Graphs -- Build and query knowledge graphs
- Semantic Search -- Search personas with natural language