Skip to content

Support stateless MCP-over-ACP #189

Open

Description

@PsiACE

The SDK still uses the connection-oriented MCP-over-ACP binding. We need to align it with the upstream stateless, request-scoped binding.

The Python SDK still implements the original connection-oriented MCP-over-ACP
prototype:

  • mcp/connect(serverId) -> connectionId
  • mcp/message(connectionId, method, params)
  • mcp/disconnect(connectionId)

The ACP RFD and the Rust and TypeScript reference implementations have since
moved to a stateless, request-scoped binding for MCP 2026-07-28:

  • mcp/message carries a serverId and a fresh logical requestId
  • there is no mcp/connect / mcp/disconnect exchange
  • there is no MCP initialize handshake; each request carries its protocol
    version and client capabilities in inner _meta
  • results and MCP errors travel inside an ACP response carrier
  • notifications are request-scoped and carry the same serverId / requestId

Because the current schema requires connectionId, a Python implementation
cannot send or receive the stateless form. The SDK validates and rejects the
new envelope before any application code sees it.

Originally posted by @xelandernt in #134 (comment)

Activity

  1. frostming commented on Nov 9, 2026

    @frostming
    Contributor

    What is the status of the eco-system now? Does Lody or T3 Code support the new MCP-over-ACP?

  2. xelandernt commented on Nov 11, 2026

    @xelandernt
    Contributor

    I looked at your most popular dependents by github stars + t3 and Lody:

    # Repo (stars) Dependent? MCP over ACP Old MCP standard New MCP standard (2026-07-28)
    1 NousResearch/hermes-agent (262,614) yes ❌ Agent-side only: the ACP adapter accepts mcpServers and connects itself (_mcp_server_config handles stdio/http/sse only) ✅ ✅ Explicit protocol=auto/stateless/legacy; server/discover fallback, detects "stateless-only" servers whose initialize is rejected (tools/mcp_tool_transport.py)
    2 bytedance/deer-flow (85,247) yes ❌ ACP client: builds mcpServers for session/new ✅ ClientSession.initialize() in its session pool ❌
    3 pingdotgg/t3code (56,466) no — uses @agentclientprotocol/sdk ✅ Yes — old binding only: AcpMcpOverAcpBridge implements mcp/connect → connectionId, mcp/message, notification, mcp/disconnect; also exposes the agent-side client methods. Proxies to HTTP MCP with mcp-session-id + mcp-protocol-version ✅ initialize-based negotiation per connection ❌ no server/discover, no request-scoped path
    4 onyx-dot-app/onyx (33,335) yes ❌ Drives OpenCode over ACP; MCP is configured as remote HTTP entries in OpenCode config ✅ pinned mcp==1.30.0, FastMCP 3.4, initialize ❌
    5 langchain-ai/deepagents (31,201) yes ❌ ACP server explicitly types MCP servers as (HttpMcpServer, SseMcpServer, McpServerStdio) — no ACP transport ✅ via langchain-mcp-adapters/FastMCP ❌
    6 jaegertracing/jaeger (23,398) yes (entry looks stale) ❌ ACP client in Go (jaegerai dials goose); no python dependency left in tree ✅ legacy initialize still answered (statelessly) ✅ mcp.NewStreamableHTTPHandler(..., Stateless: true) — "revision 2026-07-28, no sessions", GET/DELETE answered 405
    7 pydantic/pydantic-ai (21,137) yes ❌ ACP server hands mcp_servers to a user session_config; its type union includes AcpMcpServer but nothing handles it ✅ ✅ "modern session" support (SEP-2575): no handshake, no logging/setLevel, no server→client requests; warns instead of failing
    8 a2ui-project/a2ui (17,059) yes (transitive) ❌ dependency is transitive: inspect-ai → agent-client-protocol (eval only) ⚠️ Partial: MCP catalog (callMcpTool) + MCP Apps renderer; executes tools through a host-supplied client, ships no transport ❌
    9 MervinPraison/PraisonAI (9,333) yes ❌ ACP server rejects all mcpServers ("not supported yet"), advertises mcpCapabilities: {http: false, sse: false} ✅ own MCP server with initialize / notifications/initialized; client in praisonai_code ❌
    10 mistralai/mistral-vibe (5,366) yes ❌ AcpMcpServer → ConfigurationError("uses unsupported ACP transport") ✅ stdio + streamable-http, but unconditionally await session.initialize() ❌
    11 ag2ai/ag2 (5,056) yes ❌ McpCapabilities(http=False, sse=False, acp=False); ACP client only passes HttpMcpServer ✅ ✅ Explicit dual-era support ("handshake era up to 2025-11-25" vs "modern era 2026-07-28") in ag2/mcp
    12 LodyAI/Lody (1,683) no — uses @agentclientprotocol/sdk ❌ Passes mcpServers over session/new; no connectionId / mcp/* handling anywhere ✅ Full MCP host (stdio, SSE, streamable HTTP, OAuth/PKCE, credential relay) ✅ Its MCP HTTP host branches on mcp-protocol-version: 2026-07-28 → modern handler, else per-request stateless streamable HTTP server

    Pinned SDK versions (proxy for schema generation)

    Repo agent-client-protocol
    onyx-dot-app/onyx ==0.7.1
    bytedance/deer-flow >=0.4.0
    MervinPraison/PraisonAI >=0.7.0
    NousResearch/hermes-agent ==0.9.0
    langchain-ai/deepagents >=0.10.1
    mistralai/mistral-vibe ==0.11.0
    pydantic/pydantic-ai >=0.11
    ag2ai/ag2 >=0.12.1
    jaegertracing/jaeger — (Go-only ACP; python entry appears stale)
    a2ui-project/a2ui — (transitive, via inspect-ai)
    pingdotgg/t3code — (@agentclientprotocol/sdk)
    LodyAI/Lody — (@agentclientprotocol/sdk ^1.3.0, catalogs 1.5.1 || 2.0.2 || 2.5.0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions