Safeguard
Supply Chain Security•

Agent Communication Protocol (ACP) Explained: REST Agents and A2A Merge

ACP was IBM Research's REST protocol for agent messaging. It merged into A2A in August 2025. How runs, manifests and messages worked, and how to migrate.

Safeguard Security Team
43 min read

The Agent Communication Protocol (ACP) was an open, REST based protocol for messaging between AI agents, applications and humans. IBM Research launched it in March 2025 to power the BeeAI Platform, the project moved under Linux Foundation governance through LF AI and Data, and on 25 August 2025 the ACP team announced that ACP was merging into the Agent2Agent protocol (A2A) and winding down its own development.

If you run ACP today, you are running an archived specification. The ACP GitHub repository is read only, the last SDK release was version 1.0.3 in August 2025, and the platform it was built for now speaks A2A. This post explains how ACP worked (runs, agent manifests, multipart messages, sync, async and streaming modes), why it folded into A2A, how its concepts map onto A2A 1.0, and what changes for authentication when you migrate. It goes one level deeper than our posts on A2A for security teams, A2A versus MCP on the wire and AGNTCY and agent identity, so it does not repeat the A2A basics those posts cover.

Key takeaways

  • ACP was plain REST. An ACP server exposed a small OpenAPI 3.1 surface: GET /agents, GET /agents/{name}, POST /runs, GET and POST /runs/{run_id}, POST /runs/{run_id}/cancel, GET /runs/{run_id}/events, GET /session/{session_id} and GET /ping. Any HTTP client could call an agent with curl.
  • A run was the unit of work. Each run moved through created, in-progress, awaiting, cancelling, cancelled, completed or failed, and could be requested in sync, async or stream mode.
  • Messages were ordered MIME typed parts. Every part carried a content_type and either inline content or a content_url. A part with a name became an artifact.
  • The merge was announced on 25 August 2025. The ACP team said it would stop active development and contribute its technology to A2A under the Linux Foundation. The i-am-bee/acp repository is now archived.
  • A2A has since moved on. A2A reached version 1.0.0 on 12 March 2026 and joined the Agentic AI Foundation as a Growth Stage project on 27 August 2026. It now defines JSON-RPC, gRPC and HTTP+JSON/REST bindings, so the REST style that ACP championed survives inside A2A.
  • Protocol auth was never agent trust. ACP's docs listed TLS, Basic Auth, bearer tokens, JWTs and reverse proxies, and left identity federation as future work. A2A declares security schemes in the Agent Card and requires the server to authenticate every request, but authorization is still yours to write.
  • Migrating is mostly renaming plus auth work. Manifest becomes Agent Card, run becomes task, awaiting becomes TASK_STATE_INPUT_REQUIRED (or TASK_STATE_AUTH_REQUIRED), and a REST call to /runs becomes POST /message:send. The auth layer is where most of the real work sits.

What ACP was, where it started and who governed it

Origins in IBM Research and the BeeAI project

The ACP team's own merge announcement gives the history in one paragraph: IBM Research launched ACP in March 2025 to power the BeeAI Platform, an open source platform for discovering, running and sharing agents, and later that month the BeeAI project, and ACP with it, was donated to the Linux Foundation. A month after that, Google introduced A2A, and the two teams started looking at how to bring the efforts together.

The package history fits that story with one wrinkle. The Python SDK, acp-sdk, was first uploaded to PyPI on 18 February 2025 as version 0.0.1, so early code existed before the public launch. The dedicated i-am-bee/acp repository on GitHub was created on 3 April 2025, after the protocol had already been announced as part of BeeAI. The TypeScript client, published to npm as acp-sdk, first appeared on 16 May 2025. Treat "March 2025" as the launch date the project itself uses, and the February upload as pre-launch development.

Governance: LF AI and Data

ACP's "Mission and Team" page says the protocol was part of the Linux Foundation AI and Data foundation (LF AI and Data) and committed to open, transparent governance. LF AI and Data lists BeeAI as a hosted project, describing the BeeAI Framework as an open source framework for multi-agent systems and the BeeAI Platform as a place to test, debug and share agents. ACP was never a separate LF AI and Data project; it lived inside the BeeAI effort and shared its governance.

That detail matters for how the merge happened. Because both BeeAI and, from 23 June 2025, A2A sat under Linux Foundation umbrellas, the consolidation did not require any transfer of intellectual property between hostile parties. It was two Linux Foundation hosted efforts deciding that one specification was better than two.

What problem ACP set out to solve

The ACP README frames the problem as fragmentation: agents built in isolation across different frameworks, teams and infrastructures cannot easily work together. ACP's answer was a standard interface that let agents:

  • send and receive rich messages such as text, code, files or media;
  • respond in real time, in the background, or as a stream;
  • let others discover what they can do;
  • collaborate on long running tasks;
  • share state with each other when needed.

The ACP documentation's own comparison page is clear about what ACP was not. It describes the Model Context Protocol (MCP) as the standard that gives a model its context (tools and resources) inside the bounds of a single agent, while ACP connects agents to each other. The two were designed to work together: MCP inside each agent, ACP between them.

How ACP worked on the wire

The authoritative description of ACP is its OpenAPI specification, docs/spec/openapi.yaml in the archived repository. It is an OpenAPI 3.1.1 document titled "ACP - Agent Communication Protocol" at version 0.2.0. The documentation site at agentcommunicationprotocol.dev still serves the concept guides that explain it. Everything below comes from those two sources.

The endpoint surface

MethodPathOperationWhat it does
GET/pingpingLiveness check
GET/agentslistAgentsLists the agents this server hosts (basic discovery)
GET/agents/{name}getAgentReturns one agent's manifest
POST/runscreateRunCreates and starts a run for a named agent
GET/runs/{run_id}getRunReturns the run's current status and output
POST/runs/{run_id}resumeRunResumes a run that is in the awaiting state
POST/runs/{run_id}/cancelcancelRunRequests cancellation (returns 202 Accepted)
GET/runs/{run_id}/eventslistRunEventsLists the events a run has emitted
GET/session/{session_id}getSessionReturns a stored session

That is the whole protocol surface: nine operations, no custom RPC envelope, no required SDK. An ACP server hosts one or more agents, and an ACP client (which can itself be an agent, an application or any other service) calls them over HTTP. The architecture guide describes the server's purpose in one line: to expose agents through a REST interface.

Runs and their lifecycle

An agent run is a single execution of an agent with specific inputs. The run lifecycle page defines a state machine:

StateMeaning
createdThe run request was accepted but processing has not begun
in-progressThe agent is actively processing
awaitingThe agent paused and is waiting for more input from the client
completedThe agent finished successfully
cancellingA cancel request was received and is being processed
cancelledThe cancellation was confirmed (terminal)
failedThe run hit an error, including an await timeout (terminal)

Two transitions are worth noting for security reviews. First, cancellation is two step: the client calls the cancel endpoint, the run moves to cancelling, and only when the agent confirms does it reach cancelled. A client that treats the 202 response as proof the work stopped is making an assumption the protocol does not make. Second, a run that sits in awaiting for too long moves to failed with a timeout error, so a human approval step that is slower than the server's timeout fails closed rather than continuing.

Await: pausing for input

ACP called its pause mechanism Await. An agent that needs external data, clarification or a user's confirmation yields an await request, the run enters awaiting, and the client resumes it by posting await_resume data to POST /runs/{run_id}. The docs list three outcomes: resumed execution, cancellation while waiting, and timeout.

This is the ACP feature that most closely resembles a human in the loop control. It is also the one most worth checking in any code you inherit, because the resume payload is client supplied input arriving mid run. It needs the same schema validation as the original request.

Sync, async and streaming modes

POST /runs takes three required or common fields: agent_name, input (a list of messages) and an optional mode of sync, async or stream. The OpenAPI file defines RunMode as exactly that enum.

  • Sync waits and returns the completed run, output included, in the response body.
  • Async returns a run with its run_id immediately. The docs say plainly that it is the client's responsibility to poll GET /runs/{run_id} for the terminal state and result.
  • Stream returns a stream of events as the agent works. The SDK exposes this as run_stream.

An optional session_id ties runs together. The stateful agents guide says the SDK manages session state so an agent can see the complete history of a session, and the distributed sessions guide added session continuity across separate server instances using URI based resource sharing. The high availability guide shows centralized stores (Redis or PostgreSQL) for multi instance deployments.

Messages and message parts

The message structure page calls ACP's format minimal and multimodal. A message has a role and an ordered list of parts. Valid roles are user, agent, or agent/{name}, where the name may contain letters, digits, underscores and hyphens. Each part has:

AttributeRequiredPurpose
content_typeYesA MIME type such as text/plain or image/png
content or content_urlYes (one of)Inline content, or a URL pointing at it
content_encodingNoplain (default) or base64
nameNoTurns the part into an artifact
metadataNoCitation or trajectory metadata

Later releases added standard metadata types: citation metadata for source attribution and trajectory metadata for recording multi step reasoning and tool calls.

From a defensive point of view, two fields carry most of the risk. content_url means a message can point at content hosted anywhere, so a receiving agent that dereferences it is making an outbound request on behalf of whoever sent the message. And the MIME type is a claim by the sender, not a fact; a part labelled text/plain still needs to be treated as untrusted bytes.

Agent manifests

Every ACP agent was described by an Agent Manifest. In the OpenAPI schema, four fields are required: name, description, input_content_types and output_content_types. The name follows the RFC 1123 DNS label convention: lowercase letters, digits and hyphens, 1 to 63 characters. Content type lists accept wildcards, and if a developer did not set them the SDK defaulted both to */*.

Optional metadata covered annotations, markdown documentation, an SPDX licence identifier, programming language, framework, tags and similar fields. An optional status object carried runtime metrics supplied by whatever managed the agent: avg_run_tokens, avg_run_time_seconds and success_rate as a percentage.

That */* default is a small detail with a real consequence. An agent whose manifest says it accepts any content type gives callers no signal about what it can safely parse, and gives a gateway nothing to enforce. Explicit content types were always the better choice, and they map cleanly onto A2A's defaultInputModes and defaultOutputModes.

Four ways to discover agents

The agent discovery page described four methods:

  1. Basic discovery: call GET /agents on a running server.
  2. Open discovery: publish the manifest as YAML at https://your-domain.com/.well-known/agent.yml.
  3. Registry based discovery: query a central registry. The docs say this was not part of the official spec, but the BeeAI Platform implemented one.
  4. Embedded discovery: bake manifest metadata into the distribution package, for example as container image labels, so agents can be found offline.

Embedded discovery was ACP's most distinctive idea. The docs argued that writing the manifest at build time keeps advertised capabilities in step with the real implementation and suits disconnected or automated environments. It is also the idea with the clearest supply chain angle: if your agent's self description lives in an image label, then the integrity of that description is the integrity of your image build.

How ACP differed from A2A and from MCP

ACP versus A2A

ACP's own comparison page, written while both protocols were active, listed what it saw as ACP's advantages over A2A: Linux Foundation governance, co-development with BeeAI, REST based communication that needs no special runtime, offline agent discovery, a MIME type based message structure, support for anything from stateless serverless utilities to long running stateful agents, and a native SDK with session management.

Some of those differences have since dissolved. A2A also moved to the Linux Foundation in June 2025. A2A 1.0 now includes an HTTP+JSON/REST binding alongside JSON-RPC 2.0 and gRPC, and its parts and modes use media types too. The differences that remain are structural:

ConceptACPA2A 1.0
Wire styleREST over HTTP, OpenAPI definedJSON-RPC 2.0, gRPC or HTTP+JSON/REST, all functionally equivalent
Unit of workRun (run_id)Task (id), grouped by contextId
Self descriptionAgent Manifest (name, description, content types, metadata, status)Agent Card (name, description, interfaces, provider, version, capabilities, security schemes, skills, optional signatures)
Well known location/.well-known/agent.yml (open discovery)/.well-known/agent-card.json
Pause for inputawaiting plus POST /runs/{run_id} resumeTASK_STATE_INPUT_REQUIRED, continued by sending another message on the task
Pause for authorizationNot distinguished from inputTASK_STATE_AUTH_REQUIRED, with explicit rules about what it does and does not authorize
Async deliveryClient polls GET /runs/{run_id}Polling, streaming subscription or push notification webhooks
Auth declarationNone in the manifest; HTTP auth at deploymentsecuritySchemes and securityRequirements in the Agent Card
Card or manifest integrityNot specifiedOptional JWS signatures over a JCS canonicalized Agent Card
VersioningOpenAPI document versionA2A-Version header with Major.Minor semantics

ACP versus MCP

MCP and ACP were never competing for the same job. MCP defines how a host application connects a model to servers that expose tools, resources and prompts. ACP, like A2A, defines how one agent hands work to another agent that has its own model, its own tools and its own decision making. The ACP site even shipped an MCP adapter, which let an MCP client treat ACP agents as resources, a recognition that the two layers sit at different heights.

The A2A project uses the same framing today. Its announcement of joining the Agentic AI Foundation describes MCP as the vertical integration layer between an agent and its tools and data, and A2A as the horizontal layer for peer to peer collaboration between agents.

Why and when ACP merged into A2A

The announcement

The merge was announced in a GitHub discussion titled "ACP Joins Forces with A2A Under the Linux Foundation", posted on 25 August 2025 in the i-am-bee organization. The key commitments in it:

  • ACP would merge with A2A under the Linux Foundation umbrella.
  • The ACP team would wind down active development and contribute its technology and expertise directly to A2A.
  • Migration paths and documentation would be provided for ACP users.
  • Kate Blair, Director of Incubation for IBM Research, who had overseen ACP's development, would join the A2A Technical Steering Committee on behalf of IBM.
  • The BeeAI Platform, previously powered by ACP, would use A2A to support agents from any framework.

In the announcement, Blair said the goal was "a single, more powerful standard" for agent communication. The stated reason was alignment: both teams had approached agent to agent communication the same way, and maintaining two overlapping open specifications would split implementers and slow both.

What happened to ACP users and SDKs

The visible artefacts tell the rest of the story.

DateEventSource
18 Feb 2025acp-sdk 0.0.1 uploaded to PyPIPyPI release history
Mar 2025IBM Research launches ACP for the BeeAI Platform; BeeAI donated to the Linux Foundation later that monthACP merge announcement
3 Apr 2025i-am-bee/acp repository createdGitHub repository metadata
9 Apr 2025Google announces A2AGoogle Developers Blog; ACP comparison page
16 May 2025acp-sdk first published to npm (TypeScript client)npm registry metadata
23 Jun 2025Linux Foundation launches the A2A projectLinux Foundation press release
24 Jul 2025acp-sdk 1.0.1 on PyPIPyPI release history
30 Jul 2025A2A v0.3.0 releasedA2A GitHub releases
21 Aug 2025acp-sdk 1.0.3, the last release, on PyPI and npmPyPI and npm metadata
25 Aug 2025Merge announced; last push to the ACP repository the same dayGitHub discussion; repository metadata
9 Dec 2025Linux Foundation forms the Agentic AI Foundation with MCP, goose and AGENTS.mdLinux Foundation press release
12 Mar 2026A2A v1.0.0 releasedA2A GitHub releases
28 May 2026A2A v1.0.1 releasedA2A GitHub releases
27 Aug 2026A2A accepted as a Growth Stage project at the Agentic AI FoundationA2A project blog

For BeeAI users the announcement gave two concrete paths: agents built with the BeeAI framework could be exposed as A2A agents through an A2AServer adapter, and external A2A agents could be pulled into BeeAI applications with A2AAgent. The BeeAI Platform has since been renamed Agent Stack, and its README now says it is built on A2A and hosted by the Linux Foundation. The original ACP to A2A migration guide link in the ACP README now returns a 404 because the platform repository moved, so if you are migrating today, work from the A2A specification and the A2A SDK documentation rather than that guide.

The acp-sdk packages were not marked deprecated in the npm registry when we checked on 25 October 2026, and PyPI still serves 1.0.3. That is common for wound down projects, and it is a trap: a package manager will happily resolve an archived protocol's SDK with no warning. Nothing about the install tells a developer the spec underneath has stopped moving.

What the merge teaches about protocol consolidation

Foundations make merging cheap

Two open specifications can merge quickly when they already live under the same neutral host. ACP sat under LF AI and Data through BeeAI; A2A joined the Linux Foundation in June 2025. Neither side had to negotiate a licence change or a trademark transfer to combine efforts, and the contributor from the smaller project could take a seat on the larger project's steering committee. ACP launched in March 2025 and announced its merge five months later.

The ecosystem kept consolidating

The pattern did not stop with ACP. On 9 December 2025 the Linux Foundation formed the Agentic AI Foundation (AAIF) with three founding projects: MCP, goose and AGENTS.md. On 27 August 2026, A2A was accepted into the AAIF as a Growth Stage project, so the two protocols that most agent builders combine (MCP for tools, A2A for peers) now sit under the same foundation. Our Agentic AI Foundation explainer covers how that foundation is governed.

Not everything has consolidated. AGNTCY's directory, OASF schema, SLIM messaging layer and identity work, and the community driven Agent Network Protocol with its decentralized identifiers, remain separate efforts that solve adjacent problems (directories, transport, identity across the open web). Our posts on AGNTCY and the Agent Network Protocol go into those.

Lessons for engineering teams

  1. Watch governance, not just stars. An archived repository with a thousand stars still looks popular in search results. The status that matters is in the README banner, the archive flag and the last release date.
  2. Prefer specifications with a clear versioning contract. A2A 1.0 requires clients to send an A2A-Version header and requires servers to reject versions they do not support with a VersionNotSupportedError. ACP's OpenAPI document had a version number but no negotiation rule.
  3. Abstract the transport. Teams that wrapped ACP calls behind an internal interface could swap in A2A without touching every caller. Teams that sprinkled raw /runs calls across services have to find them all.
  4. Expect the concepts to survive even when the spec does not. Runs became tasks, manifests became cards, await became input required. The ideas are stable; the names and envelopes change.

Security design in REST based agent protocols

ACP's production guide said the protocol "aligns with modern security practices" through HTTP native mechanisms: TLS, common authentication methods such as Basic Auth, bearer tokens and JWTs, and reverse proxy integration for access control. It described identity federation for users and clients as under active development, and its "What's Next" page listed identity federation, access delegation and multi registry support as open questions. That is an honest description of where ACP stopped: it gave you a clean HTTP surface and left trust to the deployment.

A2A 1.0 goes further in the specification text, and the gap between the two is a good checklist for anyone running agent traffic over HTTP.

Authentication of agents

  • Transport. A2A says production deployments must use HTTPS or TLS and recommends TLS 1.3 or later. Clients should validate the server's certificate. ACP recommended TLS but did not require it.
  • Declared schemes. An A2A Agent Card lists securitySchemes (API key, HTTP auth, OAuth 2.0, OpenID Connect or mutual TLS) and securityRequirements. Clients obtain credentials out of band and send them with every request. The server must authenticate every incoming request. In the A2A Protocol Buffer definitions, the OAuth implicit and password flows are marked deprecated in favour of authorization code with PKCE, client credentials or device code, so a migrated agent should not offer either.
  • Authorization is still yours. A2A states that authorization logic is implementation specific and may consider the skill requested, the action attempted, data access policies and OAuth scopes. Nothing in either protocol decides whether agent A may ask agent B to do a particular thing. Your code does.
  • In-task authorization. A2A added TASK_STATE_AUTH_REQUIRED so an agent can ask its client to obtain an authorization (an OAuth token, a human approval). The spec is explicit that entering that state is not itself authorization for anything, and that a credential obtained during it must not be assumed to authorize later messages unless your implementation defines that. It also warns that passing credentials in band through a chain of agents exposes them to every agent in the chain, and recommends binding credentials to the originating agent.

Message validation

  • Validate against the schema you publish. ACP shipped an OpenAPI file; A2A ships Protocol Buffer definitions with JSON equivalents. Generate validators from them and reject requests that do not conform before any model sees them.
  • Enforce content types. If your card says text/plain and application/json, reject other media types with A2A's ContentTypeNotSupportedError rather than passing them through. Do not default to */*.
  • Treat URLs in parts as untrusted. ACP's content_url and A2A's file parts with URIs both let a sender point you at remote content. Fetch through an allow list or a proxy, set size and time limits, and never follow internal addresses.
  • Treat model bound text as untrusted input. Any text another agent sends you may end up in your model's context. Keep it in the data channel, not the instruction channel, and keep the tools that agent can trigger narrow.

Trust boundaries

  • Each agent is a separate trust domain. Even inside one company, an agent built by another team, on another framework, with its own tools, should be treated like an external API.
  • Cards and manifests are claims. An ACP manifest or an unsigned Agent Card says what an agent claims it can do. A2A lets providers sign cards with JWS over an RFC 8785 canonicalized JSON form; verify signatures where they exist and pin the keys you expect.
  • Discovery is a trust decision. ACP's open discovery file and A2A's well known card both mean that whoever controls the domain controls the description. A registry adds a second check but also a second system to protect.

Audit logging

ACP's production guide pointed out that HTTP based agent traffic can be monitored with standard tools and traced with OpenTelemetry, and that this makes detailed audit logging straightforward. A2A inherits the same property for all three bindings. A useful minimum record per request:

FieldWhy it matters
Authenticated caller identity (subject, client ID, certificate fingerprint)Who asked
Target agent and skillWhat they asked for
Task or run ID and context or session IDTies multi step work together
Protocol version (A2A-Version) and bindingSpots stale clients and downgrade attempts
State transitions with timestampsShows when work paused for input or authorization, and who resumed it
Content types and sizes of parts, URLs dereferencedDetects unexpected payloads and outbound fetches
Authorization decision and the policy that made itExplains why a request was allowed

Log message content only where your data handling rules allow it; metadata is usually enough for security review.

Comparison: ACP, A2A, MCP, AGNTCY and ANP

ACP (archived)A2A 1.0MCPAGNTCYAgent Network Protocol
Main jobAgent to agent messagingAgent to agent messaging and task delegationConnect a model host to tools, resources and promptsInfrastructure for agent directories, schemas, messaging and identityAgent identity, description and discovery on the open web
TransportREST over HTTPJSON-RPC 2.0, gRPC, HTTP+JSON/REST; SSE for streamingJSON-RPC messages over stdio or Streamable HTTPSLIM messaging layer with pub/sub, streaming and MLS encryptionHTTP with signed requests
DiscoveryGET /agents, /.well-known/agent.yml, registries, image labelsAgent Card at /.well-known/agent-card.json, registries, direct configurationConfigured by the host; server capabilities negotiated at initializationAgent Directory Service (federated) with OASF recordsAgent descriptions at well known URLs
IdentityDeployment level HTTP authSecurity schemes declared in the Agent Card; optional signed cardsOptional OAuth based authorization for HTTP transports; stdio servers take credentials from the environmentIdentity component in the AGNTCY stackW3C Decentralized Identifiers (did:wba, did:web)
GovernanceBeeAI under LF AI and Data; merged into A2ALinux Foundation project since June 2025, now in the Agentic AI FoundationAgentic AI FoundationLinux Foundation project since July 2025Open source community project
StatusArchived since August 20251.0.1 released May 2026Active; current version 2026-07-28ActiveCore specs released at v1.2; authorization, meta-protocol and payments still draft

Each project's own documentation is the authority for its row; check it before you build, because these specifications move quickly.

Step by step: map an ACP agent onto A2A and check what changes for auth

The steps below take a typical ACP deployment, a REST server hosting one named agent, and move it to A2A 1.0. They use curl and jq against your own lab hosts. Replace acp.lab.internal and agent.lab.internal with your hostnames, and keep this on a lab network until the auth steps are done.

Step 1: inventory what you have

Find every ACP server, client and SDK pin before you change anything.

# Python projects that depend on the ACP SDK
grep -rn "acp-sdk\|acp_sdk" --include="*.toml" --include="*.txt" --include="*.lock" --include="*.py" .

# Node projects that depend on the ACP TypeScript client
grep -rn "\"acp-sdk\"" --include="package.json" --include="package-lock.json" .

# Direct REST callers that bypass the SDK
grep -rn "/runs\b\|/runs/\|/agents/" --include="*.py" --include="*.ts" --include="*.js" --include="*.go" .

Record each server's agents and their manifests:

curl -s https://acp.lab.internal/agents | jq '.agents[] | {name, description, input_content_types, output_content_types}'
curl -s https://acp.lab.internal/agents/summarizer | jq .

If any manifest shows */* for input or output, write down what the agent really accepts. You will need it for the Agent Card.

Step 2: map the manifest to an Agent Card

ACP manifest fieldA2A Agent Card fieldNotes
namename, plus a skill idA2A names are display names; use the old DNS label as the skill id so callers can still find it
descriptiondescription and skill descriptionKeep the agent level description short; put task detail in skills
input_content_typesdefaultInputModes and skill inputModesList real media types; no wildcards
output_content_typesdefaultOutputModes and skill outputModesSame rule
metadata.documentationdocumentationUrlHost the markdown and link it
metadata.tagsskill tags
metadata.license, programming_language, frameworkNo direct fieldKeep in your internal catalogue or an extension
status (avg tokens, run time, success rate)No equivalentMove to monitoring; it never belonged in a trust decision
NothingsupportedInterfaces, provider, version, capabilities, securitySchemes, securityRequirements, signaturesNew fields you must decide on

A minimal card for the summarizer agent, served over the HTTP+JSON binding with OAuth 2.0 client credentials:

{
  "name": "Summarizer",
  "description": "Summarizes internal documents into short plain text briefs.",
  "supportedInterfaces": [
    {"url": "https://agent.lab.internal/a2a/json", "protocolBinding": "HTTP+JSON", "protocolVersion": "1.0"}
  ],
  "provider": {"organization": "Example Platform Team", "url": "https://platform.lab.internal"},
  "version": "2.0.0",
  "capabilities": {"streaming": true, "pushNotifications": false, "extendedAgentCard": false},
  "securitySchemes": {
    "internal-oauth": {
      "oauth2SecurityScheme": {
        "flows": {
          "clientCredentials": {
            "tokenUrl": "https://idp.lab.internal/oauth2/token",
            "scopes": {"summarize": "Submit documents for summary"}
          }
        }
      }
    }
  },
  "securityRequirements": [{"schemes": {"internal-oauth": {"list": ["summarize"]}}}],
  "defaultInputModes": ["text/plain", "application/pdf"],
  "defaultOutputModes": ["text/plain"],
  "skills": [
    {
      "id": "summarizer",
      "name": "Document summary",
      "description": "Returns a summary of up to 300 words for one document.",
      "tags": ["summary", "documents"],
      "inputModes": ["text/plain", "application/pdf"],
      "outputModes": ["text/plain"]
    }
  ]
}

Check the exact field names against the Agent Card definitions in section 4.4 and the security objects in section 4.5 of the A2A specification for the version you deploy; the sample card in section 8.5 is the best template.

Step 3: map runs to tasks and states

ACPA2A 1.0
POST /runs with mode: syncPOST /message:send (returns a task or a direct message)
POST /runs with mode: streamPOST /message:stream (Server-Sent Events)
POST /runs with mode: async, then pollPOST /message:send, then GET /tasks/{id}, or POST /tasks/{id}:subscribe, or a push notification config
GET /runs/{run_id}GET /tasks/{id}
POST /runs/{run_id}/cancelPOST /tasks/{id}:cancel
POST /runs/{run_id} with await_resumeSend another message on the same task while it is in TASK_STATE_INPUT_REQUIRED
session_idcontextId
createdTASK_STATE_SUBMITTED
in-progressTASK_STATE_WORKING
awaitingTASK_STATE_INPUT_REQUIRED, or TASK_STATE_AUTH_REQUIRED if what the agent needs is an authorization
completed, failedTASK_STATE_COMPLETED, TASK_STATE_FAILED
cancelling, cancelledTASK_STATE_CANCELED
No equivalentTASK_STATE_REJECTED (the agent declined the task)
Message part with name (artifact)Task artifacts with artifactId and name

The split of awaiting into two A2A states is the most important change for security. In ACP, a pause for "please confirm you want to delete this" and a pause for "please type the customer ID" looked the same on the wire. In A2A, the first should be TASK_STATE_AUTH_REQUIRED, which the specification ties to explicit rules about what the resulting credential does and does not authorize.

Step 4: compare the calls side by side

The ACP call your clients make today:

curl -s -X POST https://acp.lab.internal/runs \
  -H "Authorization: Bearer $ACP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "agent_name": "summarizer",
        "input": [{"role": "user", "parts": [{"content_type": "text/plain", "content": "Summarize the attached change log."}]}],
        "mode": "sync"
      }' | jq '{run_id, status, output}'

The A2A equivalent over the HTTP+JSON binding:

# 1. Fetch and inspect the card
curl -s https://agent.lab.internal/.well-known/agent-card.json | jq '{name, supportedInterfaces, securitySchemes, securityRequirements}'

# 2. Obtain a token using the scheme the card declares (client credentials here)
A2A_TOKEN=$(curl -s -X POST https://idp.lab.internal/oauth2/token \
  -d grant_type=client_credentials -d scope=summarize \
  -u "$CLIENT_ID:$CLIENT_SECRET" | jq -r .access_token)

# 3. Send the message
curl -s -X POST https://agent.lab.internal/a2a/json/message:send \
  -H "Authorization: Bearer $A2A_TOKEN" \
  -H "A2A-Version: 1.0" \
  -H "Content-Type: application/a2a+json" \
  -d '{
        "message": {
          "messageId": "6f1c2c8e-1d2a-4f0b-9a55-3b1f0e7c9d10",
          "role": "ROLE_USER",
          "parts": [{"text": "Summarize the attached change log."}]
        },
        "configuration": {"acceptedOutputModes": ["text/plain"]}
      }' | jq '{id: .task.id, state: .task.status.state, artifacts: .task.artifacts}'

Three differences stand out. A2A uses a messageId that the client generates, roles are enum values such as ROLE_USER, and the client sends A2A-Version. The specification says servers must treat a missing version header as 0.3, so a 1.0 server that receives an old client's request will apply 0.3 semantics or reject it, which you want to know about before you cut over.

Step 5: check what changes for auth

Run these checks against the new A2A endpoint before any production traffic reaches it.

# No token: expect an authentication error, not a task
curl -s -o /dev/null -w "%{http_code}\n" -X POST https://agent.lab.internal/a2a/json/message:send \
  -H "A2A-Version: 1.0" -H "Content-Type: application/a2a+json" \
  -d '{"message":{"messageId":"t1","role":"ROLE_USER","parts":[{"text":"ping"}]}}'

# Plain HTTP: expect no listener or a redirect, never a task
curl -s -o /dev/null -w "%{http_code}\n" http://agent.lab.internal/a2a/json/message:send

# Unsupported media type: expect ContentTypeNotSupportedError
curl -s -X POST https://agent.lab.internal/a2a/json/message:send \
  -H "Authorization: Bearer $A2A_TOKEN" -H "A2A-Version: 1.0" -H "Content-Type: application/a2a+json" \
  -d '{"message":{"messageId":"t2","role":"ROLE_USER","parts":[{"raw":"UEsDBA==","mediaType":"application/zip"}]}}' | jq .

# Unknown version: expect VersionNotSupportedError
curl -s -X POST https://agent.lab.internal/a2a/json/message:send \
  -H "Authorization: Bearer $A2A_TOKEN" -H "A2A-Version: 9.9" -H "Content-Type: application/a2a+json" \
  -d '{"message":{"messageId":"t3","role":"ROLE_USER","parts":[{"text":"ping"}]}}' | jq .

Then check the questions the protocol cannot answer for you:

  1. Is the token scoped to this agent? A token minted for the old ACP server should not work on the new A2A endpoint, and a token for one agent should not work on another. Check the audience claim.
  2. Can one client read another client's tasks? Create a task with client A's token and call GET /tasks/{id} with client B's token. You should get a not found or forbidden error.
  3. Does the agent pass its own credentials downstream? If this agent calls other agents, confirm it uses its own identity for those calls and does not forward the caller's bearer token.
  4. What does TASK_STATE_AUTH_REQUIRED unlock? Write down which single operation an approval covers and confirm a second destructive request triggers a second approval.
  5. Is the old ACP endpoint gone? After cutover, remove the ACP routes or put them behind a deny rule. Old endpoints with old auth are a common way back in.

Step 6: retire the ACP dependency

Remove acp-sdk from lockfiles, delete the ACP routes, and add a CI check that fails if the package comes back:

# .github/workflows/no-acp.yml
name: block-archived-acp-sdk
on: [pull_request]
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Fail if the archived ACP SDK is still referenced
        run: |
          if grep -rqE "acp-sdk|acp_sdk" --include="*.toml" --include="*.txt" --include="*.lock" --include="package.json" --include="package-lock.json" .; then
            echo "acp-sdk found; ACP merged into A2A in August 2025. Migrate to an A2A SDK."
            exit 1
          fi

If you need a Python A2A SDK, the A2A project publishes a2a-sdk on PyPI (version 1.2.3 when we checked) with documentation on a2a-protocol.org. Pin it, record it in your SBOM, and track it like any other dependency.

Maturity: what was specified, what was not

AreaACP status when archivedA2A 1.0 status
Core endpoints and run lifecycleSpecified in OpenAPI 0.2.0Core operations specified across three bindings
Open discovery fileDocumented (agent.yml)Specified (agent-card.json)
Registry discoveryNot in the spec; implemented in BeeAIMentioned as a discovery mechanism; registries are outside the core spec
Identity federation"Under active development"Out of scope for the protocol; schemes declared in the card
Access delegationListed as an open questionIn-task authorization state with explicit limits
Card or manifest signingNot specifiedOptional JWS signatures
Version negotiationNot specifiedRequired A2A-Version handling

Common mistakes

  • Building on a deprecated spec. Starting a new project on acp-sdk in 2026 because a tutorial or course still uses it. The repository is archived and the last release was August 2025. New work should start on A2A.
  • Assuming protocol auth means agent trust. A valid bearer token proves the caller holds a token. It says nothing about whether that caller should be allowed to run this skill on this data. Write the authorization policy.
  • Skipping message schema validation. Passing raw request bodies to the agent framework because "the SDK handles it". Validate against the OpenAPI or Protocol Buffer definitions at the edge.
  • Leaving content types as wildcards. ACP's SDK defaulted to */*. Carrying that over as */* input modes in an Agent Card removes the cheapest filter you have.
  • Dereferencing every URL in a message. content_url in ACP and URI file parts in A2A should go through an allow list with size and time limits.
  • Treating a 202 cancel as stopped. ACP's cancel was two step, and A2A returns TaskNotCancelableError when a task has already reached a terminal state. Read the final state before you report work as stopped.
  • Forwarding the user's token through an agent chain. A2A's specification warns that in band credentials passed across a chain are exposed to every agent in it. Use per agent credentials.
  • Leaving the old ACP endpoint running after migration. It keeps its old auth, its old logging and its archived SDK.
  • Using manifest status metrics as trust signals. Average tokens and success rate come from whatever manages the agent and are easy to misreport.

FAQ

What is the Agent Communication Protocol (ACP)?

ACP was an open protocol for communication between AI agents, applications and humans, launched by IBM Research in March 2025 for the BeeAI Platform. It defined a small REST API for discovering agents and running them, with multipart MIME typed messages and sync, async and streaming modes.

Is ACP still maintained?

No. On 25 August 2025 the ACP team announced it was merging into A2A under the Linux Foundation and winding down active development. The i-am-bee/acp repository is archived, and the last SDK release, 1.0.3, came out on 21 August 2025.

Did ACP merge into A2A?

Yes. The announcement said ACP's technology and expertise would be contributed to A2A, IBM's ACP lead would join the A2A Technical Steering Committee, and the BeeAI Platform would move from ACP to A2A.

What is the difference between ACP and A2A?

Both are agent to agent protocols. ACP was REST only, used runs, agent manifests and an awaiting state. A2A 1.0 offers JSON-RPC, gRPC and REST bindings, uses tasks and Agent Cards, declares security schemes in the card, supports signed cards and distinguishes input required from auth required.

What is the difference between ACP and MCP?

MCP connects a model host to tools, resources and prompts inside one agent. ACP connected separate agents to each other. ACP's own documentation described them as complementary layers.

Is IBM's ACP the same as the Agent Client Protocol?

No. The Agent Client Protocol, also abbreviated ACP, is a different specification for how code editors talk to coding agents. Check which one a document means before you follow it; our Agent Client Protocol explainer covers the editor one.

How do I migrate from ACP to A2A?

Inventory your ACP servers and clients, map each manifest to an Agent Card, map runs to tasks and awaiting to the input required or auth required states, switch callers to POST /message:send with an A2A-Version header, then test authentication, task isolation and version handling before removing the ACP routes.

What authentication did ACP support?

ACP's documentation listed TLS, Basic Auth, bearer tokens, JWTs and reverse proxy integration, with identity federation described as under development. The manifest had no field for declaring auth requirements, so clients learned them out of band.

Where is A2A governed now?

A2A became a Linux Foundation project on 23 June 2025 and was accepted as a Growth Stage project at the Agentic AI Foundation on 27 August 2026, alongside MCP, goose and AGENTS.md.

Can I still install acp-sdk?

Yes. PyPI and npm still serve version 1.0.3, and neither package was marked deprecated when we checked. Installing it gives you an SDK for an archived specification, so use it only to maintain an existing system while you migrate.

Where Safeguard fits

Safeguard's fit with ACP is indirect. Safeguard does not inspect agent to agent traffic, validate Agent Cards, broker OAuth tokens or decide which agent may call which skill. What it covers is the software those agents are built from. safeguard scan --dir . inventories the dependencies in an agent's repository, including an archived acp-sdk pin, an A2A SDK, HTTP frameworks and the libraries that parse untrusted message parts, and matches them against vulnerability databases. --image does the same for the container image you ship. Reachability analysis ranks the findings your code actually calls, the platform shows which projects contain a given component and version when an advisory lands, and policies and gates can stop a build that brings in a version you have decided to block.

Migrating from ACP to A2A swaps one set of SDKs, web frameworks and serialization libraries for another, and each of those is open source code that will receive security advisories of its own. An archived SDK is the extreme case: when no maintainer is shipping fixes, the gap between an advisory and a safe version is open ended.

Fix it without waiting for upstream

Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.

1. Autonomous remediation: pull requests, not tickets

When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.

2. Gold Open Source: the safe version in seconds, free

gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.

3. Gold Registry: start from components that are already fixed

registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.

When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.

Your situationWhat to use
A fixed version existsAutonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version
You need to know today whether you are affectedGold Open Source lookup and "Am I affected?" checker, free and without login
You want vulnerable versions to stop shippingThe Gold scan GitHub Action in CI, plus Safeguard policies and gates
Upstream has no fix yet, or will not ship oneA custom Gold request; fork and patch (early access)
You want to start clean on new projectsPull hardened components and images from Gold Registry

One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.

See how Safeguard would help

The first demo connects Claude to the Safeguard MCP server with OAuth and shows how you choose which tools Claude may use. It is the MCP layer this post describes, sitting inside one agent, and it shows the habit the A2A migration needs: grant an agent only the capabilities its job requires and make that choice explicit.

Click through it at your own pace, or open it full screen.

The second demo is a real Safeguard CLI session: install, open the interactive terminal with safeguard, ask it to fix the lodash CVE in a repository, and watch it read the finding and advisory, ask before any command that changes something, update package.json to lodash 4.17.21, run npm install and npm test with your approval, and push nothing. The same approve each step flow fits a migration branch where you are replacing acp-sdk with an A2A SDK and want every dependency change reviewed.

Click through it at your own pace, or open it full screen.

The third demo sets up the local runner on your own machine, approves it with its device code and scans from the terminal. Use it on the lab host from the walkthrough above to scan the old ACP service and the new A2A service side by side before cutover.

Click through it at your own pace, or open it full screen.

To try it on your own agent code, download Safeguard and follow the CLI documentation.

Quick check

An ACP agent paused in the awaiting state to ask a user to approve a deletion. Which A2A 1.0 state should the migrated agent use, and why?

TASK_STATE_AUTH_REQUIRED, because the agent needs an authorization rather than more data. The A2A specification says entering that state is not itself authorization and that a credential obtained during it must not be assumed to cover later messages unless the implementation defines that, which gives you explicit rules ACP's single awaiting state did not.

Your ACP manifest listed input_content_types as /. What should the migrated Agent Card say?

The real media types the agent can parse, such as text/plain and application/pdf, in defaultInputModes and each skill's inputModes. The server should then reject other types with ContentTypeNotSupportedError instead of passing them to the agent.

Sources

Last reviewed: 25 October 2026. ACP is archived and A2A is versioned; check the A2A specification for the version you deploy before you migrate.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.