17 KiB
Protocols
CleverAgents deliberately adopts open, versioned protocols so that clients, tools, and skills can interoperate without bespoke integrations.
| Protocol | Role |
|---|---|
| A2A | Versioned client-server contract (JSON-RPC 2.0) |
| MCP | Tool discovery and invocation over a server boundary |
| LSP | Language intelligence for actors and agents |
| AgentSkills.io | Packaging instruction-driven, multi-step workflows |
Guiding principles:
- Prefer open protocols — align with community standards to keep integrations portable.
- Keep adapters at the edge — standards map into stable internal domain models so core logic remains protocol-agnostic.
A2A — Agent-to-Agent Protocol
Standard: a2a-protocol.org
ADRs: ADR-026, ADR-047, ADR-048
Python API: cleveragents.a2a
Overview
A2A is the sole communication protocol for all client-server interaction in CleverAgents. It is built on JSON-RPC 2.0 and defines the fundamental boundary between the Presentation and Application layers. Every CLI command, TUI action, and IDE plugin operation flows through A2A regardless of deployment mode.
A2A is the successor to the Agent Client Protocol (ACP), which is now deprecated. A2A retains backward compatibility with ACP's JSON-RPC 2.0 foundation.
Wire Format
All A2A messages follow the JSON-RPC 2.0 specification:
// Request
{
"jsonrpc": "2.0",
"id": "req-001",
"method": "session.create",
"params": {
"actor": "openai/gpt-4o"
}
}
// Success response
{
"jsonrpc": "2.0",
"id": "req-001",
"result": {
"session_id": "sess-abc123",
"actor": "openai/gpt-4o",
"created_at": "2026-04-13T10:00:00Z"
}
}
// Error response
{
"jsonrpc": "2.0",
"id": "req-001",
"error": {
"code": -32000,
"message": "Actor not found: openai/gpt-4o",
"data": {"actor": "openai/gpt-4o"}
}
}
Breaking change (v3.7.0+): Fields were renamed to comply with JSON-RPC 2.0.
a2a_version→jsonrpc,request_id→id,operation→method,data→result. See the CHANGELOG for the full migration table.
Standard A2A Operations
| Method | Description |
|---|---|
message/send |
Send a message to an agent |
message/stream |
Stream a message response via SSE |
tasks/get |
Get task status |
tasks/cancel |
Cancel a running task |
agent/authenticatedExtendedCard |
Retrieve the Agent Card (capability discovery) |
CleverAgents Extension Methods
CleverAgents extends A2A with _cleveragents/-prefixed methods (declared via
the A2A extension mechanism):
| Method | Description |
|---|---|
session.create |
Create a new conversation session |
session.close |
Close an existing session |
plan.create |
Instantiate a plan from an action |
plan.execute |
Execute a plan |
plan.status |
Query plan status |
plan.diff |
Retrieve the sandbox diff |
plan.apply |
Apply the sandbox changeset |
plan.correct |
Correct a decision |
registry.list_tools |
List available tools |
registry.list_resources |
List available resources |
registry.list_actors |
List available actors |
registry.list_skills |
List available skills |
event.subscribe |
Subscribe to the event stream |
diagnostics.run |
Run diagnostics |
Deployment Modes
| Mode | Transport | Description |
|---|---|---|
| Local | stdio (JSON-RPC) | Agent runs as a subprocess; A2A flows in-process via A2aLocalFacade |
| Server | HTTP | A2A flows over HTTP to the CleverAgents server |
from cleveragents.a2a import A2aLocalFacade, A2aRequest
# Local mode — no network, no serialization overhead
facade = A2aLocalFacade(container)
response = await facade.dispatch(
A2aRequest(method="session.create", params={"actor": "openai/gpt-4o"})
)
session_id = response.result["session_id"]
Agent Card Discovery
The A2A Agent Card (agent/authenticatedExtendedCard) exposes the platform's
capabilities for ecosystem interoperability:
{
"name": "CleverAgents",
"version": "1.0.0",
"description": "Unified AI agent orchestration platform",
"capabilities": {
"streaming": true,
"pushNotifications": true,
"stateTransitionHistory": true
},
"extensions": [
{
"uri": "https://cleverthis.com/a2a/extensions/plan-lifecycle",
"methods": ["plan.create", "plan.execute", "plan.apply", "plan.diff", "plan.correct"]
},
{
"uri": "https://cleverthis.com/a2a/extensions/registry",
"methods": ["registry.list_tools", "registry.list_resources", "registry.list_actors"]
}
]
}
Event Streaming
Subscribe to plan and session events via the A2A event stream:
from cleveragents.a2a import A2aEventQueue
queue = A2aEventQueue()
async for event in queue.subscribe("plan-01JXYZ..."):
print(event.type, event.payload)
if event.type == "plan.applied":
break
queue.close() # release resources on shutdown
Error Codes
| Code | Name | Description |
|---|---|---|
-32700 |
Parse error | Invalid JSON |
-32600 |
Invalid request | Not a valid JSON-RPC 2.0 request |
-32601 |
Method not found | Unknown A2A method |
-32602 |
Invalid params | Invalid method parameters |
-32603 |
Internal error | Internal server error |
-32000 |
Application error | CleverAgents domain error |
-32001 |
Not available | Operation not available in current mode |
-32002 |
Version mismatch | Incompatible A2A protocol versions |
MCP — Model Context Protocol
Standard: modelcontextprotocol.io
ADR: ADR-029
Python API: cleveragents.mcp
Overview
MCP is the standard for discovering and invoking external tools over a server
boundary via JSON-RPC. CleverAgents bridges MCP servers into the Tool Registry
via the MCPToolAdapter, giving actors plug-and-play access to a growing
ecosystem of tool providers.
Supported Transports
| Transport | Description |
|---|---|
stdio |
Spawn a subprocess and communicate over stdin/stdout |
sse |
Server-Sent Events over HTTP |
streamable-http |
Streamable HTTP (MCP 1.1+) |
Connecting an MCP Server
from cleveragents.mcp import MCPToolAdapter, MCPServerConfig
from cleveragents.tool import ToolRegistry
# Configure the MCP server
config = MCPServerConfig(
name="bash-tools",
transport="stdio",
command="uvx",
args=["mcp-server-bash"],
)
# Connect and discover tools
adapter = MCPToolAdapter(config)
await adapter.connect()
tools = await adapter.discover_tools()
# Register tools into the CleverAgents Tool Registry
registry = ToolRegistry()
adapter.register_tools(registry)
# Invoke a tool
result = await adapter.invoke("bash", {"command": "ls -la"})
print(result.content)
await adapter.disconnect()
MCP Server Configuration Reference
| Field | Type | Description |
|---|---|---|
name |
str |
Server identifier (used as tool namespace prefix) |
transport |
str |
"stdio" | "sse" | "streamable-http" |
command |
str | None |
Spawn command (stdio only) |
args |
list[str] |
Command-line arguments |
env |
dict[str, str] |
Environment variables passed to the server process |
url |
str | None |
Server URL (sse/http only) |
headers |
dict[str, str] |
HTTP headers (sse/http only) |
Tool Discovery
MCP tools are discovered via the tools/list method and registered in the
Tool Registry with source="mcp". Each discovered tool becomes a
MCPToolDescriptor:
class MCPToolDescriptor(BaseModel):
name: str
description: str
input_schema: dict[str, Any] # JSON Schema
annotations: dict[str, Any]
Sandbox Path Rewriting
When running tools inside a sandbox, the SandboxPathRewriter rewrites
absolute host paths in tool arguments and results to sandbox-relative paths,
preventing path traversal outside the sandbox root:
from cleveragents.mcp import SandboxPathRewriter, SandboxPathRewriterConfig
rewriter = SandboxPathRewriter(
SandboxPathRewriterConfig(
sandbox_root="/sandbox",
host_root="/home/user/project",
)
)
rewritten_args = rewriter.rewrite_args(tool_args)
Skill Refresh Notifications
When an MCP server's tool list changes at runtime, MCPRefreshHook wires
notifications/tools/list_changed events to SkillRegistry.refresh_all():
from cleveragents.mcp import MCPRefreshHook
hook = MCPRefreshHook(skill_registry)
adapter.on_tools_changed(hook.on_tools_changed)
MCP Client Lifecycle
STOPPED → STARTING → RUNNING → STOPPING → STOPPED
↘ ERROR
The McpClient manages lazy start (server is only started when first needed)
and auto-stop after a configurable idle timeout:
from cleveragents.mcp import McpClient, McpClientConfig
client_config = McpClientConfig(server=server_config, auto_stop_idle_secs=60)
client = McpClient(client_config)
async with client:
tools = await client.list_tools()
result = await client.call_tool("bash", {"command": "echo hello"})
LSP — Language Server Protocol
Standard: microsoft.github.io/language-server-protocol
ADR: ADR-027
Overview
LSP is the standard for attaching language intelligence to actors and agents. CleverAgents registers LSP servers in the global LSP Registry and binds them to actor graph nodes via YAML configuration. When an actor activates, the LSP Runtime starts the appropriate language servers for the actor's bound languages and workspace resources.
Capabilities Exposed to Actors
LSP capabilities are exposed to actors in two ways:
- As callable tools via
LSPToolAdapter— actors can invoke LSP operations (diagnostics, completions, references, rename, code actions) as regular tool calls. - As automatic context enrichment — diagnostics and type annotations are injected into the ACMS hot context automatically.
| LSP Capability | Tool Name | Description |
|---|---|---|
textDocument/diagnostic |
lsp.diagnostics |
Get diagnostics for a file |
textDocument/hover |
lsp.hover |
Get type info and documentation at a position |
textDocument/definition |
lsp.definition |
Go to definition |
textDocument/references |
lsp.references |
Find all references |
textDocument/completion |
lsp.completion |
Get completions at a position |
textDocument/rename |
lsp.rename |
Rename a symbol |
textDocument/codeAction |
lsp.code_action |
Get available code actions |
textDocument/signatureHelp |
lsp.signature_help |
Get function signature help |
Registering an LSP Server
# Register a Python LSP server
agents lsp add --config examples/lsp/pyright.yaml
# List registered LSP servers
agents lsp list --language python
# Show LSP server details
agents lsp show local/pyright
LSP server YAML configuration:
name: local/pyright
language: python
command: pyright-langserver
args: ["--stdio"]
env: {}
workspace_mapping: auto # auto | explicit
Actor YAML Binding
Bind LSP servers to actor graph nodes in the actor YAML:
name: local/my-python-actor
entry_node: analyze
nodes:
analyze:
type: llm
lsp_binding:
language: python # bind by language (auto-selects server)
# or:
server: local/pyright # bind by explicit server name
Different nodes in an actor's graph can have different LSP bindings, enabling fine-grained control over which agents receive which language intelligence.
LSP Runtime
The LSP Runtime in the Infrastructure layer manages:
- Server lifecycle — start, stop, health monitoring
- Workspace mapping — maps project resources to LSP workspace folders
- File synchronization — keeps LSP server in sync with resource changes
- Capability negotiation — negotiates supported capabilities during initialization
Auto-Discovery
Actors can bind LSP servers automatically based on the languages detected in their project's resources:
lsp_binding:
mode: auto # detect languages from project resources
AgentSkills.io
Standard: AgentSkills.io
ADRs: ADR-028, ADR-030
Python API: cleveragents.skills
Overview
AgentSkills.io is the standard for packaging instruction-driven, multi-step
workflows as SKILL.md files with progressive disclosure. Agent Skills
complement MCP tools by teaching agents how to accomplish complex tasks
rather than simply exposing callable functions.
Skill Bundle Structure
An AgentSkills-compatible skill bundle is a directory with the following layout:
my-skill/
├── SKILL.md # Required: skill definition with progressive disclosure
├── scripts/ # Optional: executable scripts referenced by steps
│ └── deploy.sh
├── references/ # Optional: reference documents
│ └── api-spec.md
└── assets/ # Optional: static assets
└── schema.json
SKILL.md Format
# Deploy to Staging
## Description
Deploys the application to the staging environment.
## Steps
### 1. Run tests
Execute the test suite and verify all tests pass.
```bash
scripts/run-tests.sh
2. Build artifacts
Build the deployment artifacts.
3. Deploy
Deploy to the staging environment using the deployment script.
scripts/deploy.sh --env staging
Definition of Done
- All tests pass
- Application is accessible at the staging URL
- Health check returns 200
### Loading a Skill Bundle
```python
from pathlib import Path
from cleveragents.skills import AgentSkillLoader
loader = AgentSkillLoader.from_folder(Path("./skills/deploy-to-staging"))
spec = loader.load()
print(spec.name) # "deploy-to-staging"
print(spec.version) # "1.0.0"
for step in spec.steps:
print(f" Step: {step.title}")
Discovering Skills
from cleveragents.skills import discover_agent_skills, register_discovered_skills
result = discover_agent_skills([Path("./skills/")])
for skill in result.skills:
print(f"Found: {skill.name} v{skill.version}")
# Register all discovered skills
register_discovered_skills(result, skill_registry)
Registering via CLI
# Register a skill from a YAML config
agents skill add --config examples/skills/deploy.yaml
# Or discover from a directory (set CLEVERAGENTS_SKILLS_PATH)
export CLEVERAGENTS_SKILLS_PATH=/path/to/skills:/another/path
agents skill list # shows all discovered skills
Progressive Disclosure
AgentSkills.io uses a three-tier progressive disclosure model:
| Tier | Description |
|---|---|
| Discover | Actor learns the skill exists and its high-level description |
| Activate | Actor loads the full SKILL.md and step details |
| Deactivate | Actor releases the skill when no longer needed |
This keeps the actor's context window lean — skills are only fully loaded when the actor decides to use them.
AgentSkillSpec
The parsed representation of a skill bundle:
class AgentSkillSpec(BaseModel):
name: str
version: str
steps: list[SkillStep]
tools: list[AgentSkillToolDescriptor]
resource_slots: list[AgentSkillResourceSlot]
Inline Executor
Skills can define steps as inline Python callables rather than external tool
calls. The InlineToolExecutor handles these:
from cleveragents.skills import InlineToolExecutor
executor = InlineToolExecutor()
result = await executor.execute(step, context)
Skill Context
The SkillContext carries runtime state during skill execution:
class SkillContext:
session_id: str
plan_id: str
bound_resources: list[BoundResource]
cancellation_token: CancellationToken
invocation_history: list[ToolInvocationRecord]
Protocol Interoperability
The four protocols work together in a layered fashion:
User / IDE / CI
↓
A2A (JSON-RPC 2.0) ← all client-server communication
↓
CleverAgents Application
↓
┌───────────────────────────────────────┐
│ MCP Adapter LSP Runtime Skills │ ← tool/intelligence/skill sources
└───────────────────────────────────────┘
↓
Tool Registry ← all tools unified here
↓
Actor (LangGraph) ← executes plans using tools
- A2A is the outer boundary — all clients speak A2A.
- MCP brings external tools into the Tool Registry.
- LSP brings language intelligence into actor context and as tools.
- AgentSkills.io brings instruction-driven workflows as activatable skills.
All four integrate into the same internal domain model, so actors see a unified surface regardless of where a tool or skill originated.