236d1abd80
CI / push-validation (pull_request) Successful in 16s
CI / build (pull_request) Successful in 24s
CI / helm (pull_request) Successful in 28s
CI / quality (pull_request) Successful in 32s
CI / lint (pull_request) Successful in 34s
CI / typecheck (pull_request) Successful in 48s
CI / security (pull_request) Successful in 59s
CI / e2e_tests (pull_request) Successful in 3m3s
CI / integration_tests (pull_request) Successful in 3m54s
CI / unit_tests (pull_request) Successful in 5m0s
CI / docker (pull_request) Successful in 11s
CI / coverage (pull_request) Successful in 10m50s
CI / status-check (pull_request) Successful in 1s
CI / benchmark-publish (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Successful in 57m42s
597 lines
17 KiB
Markdown
597 lines
17 KiB
Markdown
# Protocols
|
|
|
|
CleverAgents deliberately adopts open, versioned protocols so that clients,
|
|
tools, and skills can interoperate without bespoke integrations.
|
|
|
|
| Protocol | Role |
|
|
|----------|------|
|
|
| [A2A](#a2a-agent-to-agent-protocol) | Versioned client-server contract (JSON-RPC 2.0) |
|
|
| [MCP](#mcp-model-context-protocol) | Tool discovery and invocation over a server boundary |
|
|
| [LSP](#lsp-language-server-protocol) | Language intelligence for actors and agents |
|
|
| [AgentSkills.io](#agentskillsio) | Packaging instruction-driven, multi-step workflows |
|
|
|
|
> **Guiding principles:**
|
|
> 1. **Prefer open protocols** — align with community standards to keep integrations portable.
|
|
> 2. **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](https://a2a-protocol.org)
|
|
**ADRs:** [ADR-026](../adr/ADR-026-agent-client-protocol.md), [ADR-047](../adr/ADR-047-acp-standard-adoption.md), [ADR-048](../adr/ADR-048-server-application-architecture.md)
|
|
**Python API:** [`cleveragents.a2a`](a2a.md)
|
|
|
|
### 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:
|
|
|
|
```json
|
|
// 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](../../CHANGELOG.md) 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 |
|
|
|
|
```python
|
|
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:
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```python
|
|
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](https://modelcontextprotocol.io)
|
|
**ADR:** [ADR-029](../adr/ADR-029-model-context-protocol.md)
|
|
**Python API:** [`cleveragents.mcp`](mcp.md)
|
|
|
|
### 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
|
|
|
|
```python
|
|
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`:
|
|
|
|
```python
|
|
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:
|
|
|
|
```python
|
|
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()`:
|
|
|
|
```python
|
|
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:
|
|
|
|
```python
|
|
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](https://microsoft.github.io/language-server-protocol/)
|
|
**ADR:** [ADR-027](../adr/ADR-027-language-server-protocol.md)
|
|
|
|
### 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:
|
|
|
|
1. **As callable tools** via `LSPToolAdapter` — actors can invoke LSP
|
|
operations (diagnostics, completions, references, rename, code actions)
|
|
as regular tool calls.
|
|
2. **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
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```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:
|
|
|
|
```yaml
|
|
lsp_binding:
|
|
mode: auto # detect languages from project resources
|
|
```
|
|
|
|
---
|
|
|
|
## AgentSkills.io
|
|
|
|
**Standard:** [AgentSkills.io](https://AgentSkills.io)
|
|
**ADRs:** [ADR-028](../adr/ADR-028-agent-skills-standard.md), [ADR-030](../adr/ADR-030-skill-abstraction-definition.md)
|
|
**Python API:** [`cleveragents.skills`](skills.md)
|
|
|
|
### 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
|
|
|
|
```markdown
|
|
# 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.
|
|
|
|
```bash
|
|
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
|
|
|
|
```python
|
|
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
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```python
|
|
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:
|
|
|
|
```python
|
|
from cleveragents.skills import InlineToolExecutor
|
|
|
|
executor = InlineToolExecutor()
|
|
result = await executor.execute(step, context)
|
|
```
|
|
|
|
### Skill Context
|
|
|
|
The `SkillContext` carries runtime state during skill execution:
|
|
|
|
```python
|
|
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.
|