Rails-native integration for exposing Rails applications as A2A v1.0 agents.
Release status (2026-10-08):
0.2.0is published as a stable release on RubyGems and GitHub. It includes the Rails/Zeitwerk and migration-generator fixes after rc2. The public Gem matches the verified artifact byte-for-byte; see the publication record.
-
RubyGems: https://rubygems.org/gems/a2a-rails
-
Published stable release: https://github.com/cuichangquan/a2a-rails/releases/tag/v0.2.0
-
Previous stable release: https://github.com/cuichangquan/a2a-rails/releases/tag/v0.1.0
-
Changelog: CHANGELOG.md
-
Release record: v0.2.0 publication record
-
Release readiness: current release/deployment checklist · v0.1.0 → v0.2.0 upgrade guide. The old rc1 record is historical evidence only.
-
Roadmap / 次にやること: ROADMAP.md — Step 26 / Issue #62 records the
0.2.0stable release gates; production Issue #11 remains open. See stable-readiness record. -
Official A2A TCK results: Pinned JSON-RPC MUST report and reproduction — after Step 17-4: 63 passed / 1 failed / 171 skipped / 30 deselected (pytest). The remaining
CORE-SEND-003mismatch is tracked upstream in #202. The TCK workflow is informational, not an A2A conformance certificate. -
A2Aの全体像(日本語・A4 1枚PDF) — 登場人物・依頼の流れ・主要用語・MCPとの違いをまとめた学習資料。
-
A2A at a glance (English, A4 one-page PDF) — Roles, workflow, key terms, and how MCP fits.
a2a-rails-demo — independent Rails 8 Echo Agent
Run a complete standalone Rails Agent using the published RubyGems a2a-rails = 0.2.0 (not a Git source checkout). The separate Demo covers Agent Card discovery, JSON-RPC v1.0 SendMessage Task and direct Message, GetTask, ListTasks and error handling.
- Demo Quick Start, code and example curl requests.
- Demo GitHub Actions smoke — independent Rails 8.1.0 / Ruby 3.4.10 HTTP smoke 16/16 checks passed against published Gem
0.2.0(Step 28 Demo PR #2, merged, passing PR CI). The CI also confirmsA2A::Rails::VERSION == "0.2.0", RubyGems as the dependency source, and the demo's production-startup refusal. - Local-only development/test demo: no trusted verifier or durable Task store, not an internet-facing production template. See production security.
a2a-rails lets a Rails application expose an A2A-compatible Agent without making application code depend directly on SDK-specific request and response objects.
A2A Protocol
↓
Ruby A2A SDK
↓
a2a-rails
↓
Rails Application
↓
Business Logic
The Gem provides:
- a Rails-native Agent / Skill DSL;
- A2A Agent Card generation;
- automatically mounted A2A HTTP endpoints;
- synchronous Task execution by default, plus opt-in ActiveJob-backed async Task execution in published
0.2.0; - SDK-independent Handler inputs;
- a process-local MemoryStore by default, plus an optional durable ActiveRecordStore in
0.2.0; - Rails generators for initial setup;
- an internal Protocol Adapter boundary around the upstream SDK.
v0.1 is intentionally server-first and non-streaming.
Warning
Security / production use: v0.1.0 does not provide built-in authentication or per-caller Task authorization. Do not expose POST /a2a to untrusted clients. Before public deployment, protect the endpoint at your application's or network's security boundary. Security hardening is tracked in Issue #11.
- Ruby
>= 3.3 - Rails
>= 8.0, < 8.2 - A2A protocol version
1.0 agent2agent ~> 2.0.0
The original v0.1.0 baseline was verified against:
- Ruby 3.3 / 3.4 / 4.0
- Rails 8.0 / 8.1
Add the Gem to an existing Rails application:
bundle add a2a-railsThen generate the initializer and an Agent scaffold:
bin/rails generate a2a:rails:install
bin/rails generate a2a:rails:agent echoNo explicit Engine mount or host config/routes.rb change is required.
The following Echo flow is verified against the published a2a-rails 0.1.0 Gem in a clean Rails 8.1 application.
bin/rails generate a2a:rails:install
bin/rails generate a2a:rails:agent echo
mkdir -p app/services/echoThe install generator creates:
config/initializers/a2a_rails.rb
The Agent generator creates:
app/agents/echo_agent.rb
The generators deliberately do not create application business logic, jobs, migrations, Task Stores, or routing side effects.
Create app/services/echo/reply.rb:
class Echo::Reply
def self.call(message:, context:)
text = message[:parts]
.filter_map { |part| part[:text] }
.join("\n")
"Echo: #{text}"
end
endHandlers receive SDK-independent Ruby Hashes for message and context.
Replace app/agents/echo_agent.rb with:
class EchoAgent < A2A::Rails::Agent
name "Echo Agent"
description "Echo messages"
version "1.0"
skill :reply,
description: "Echo a message",
tags: %w[echo],
handler: Echo::Reply
endA single Skill is selected automatically. No Router is required.
Replace config/initializers/a2a_rails.rb with:
A2A::Rails.configure do |config|
config.agent = "EchoAgent"
config.public_base_url = ENV["A2A_PUBLIC_BASE_URL"]
endFor localhost, leave A2A_PUBLIC_BASE_URL unset or set it to http://localhost:3000.
The Agent class name remains a String until an A2A endpoint resolves it, preserving Rails autoload / reload behavior.
Start Rails:
bin/rails serverThen request the Agent Card:
curl -sS http://localhost:3000/.well-known/agent-card.json \
-H "A2A-Version: 1.0"Expected essentials:
- HTTP 200
- Agent name
Echo Agent - Skill ID
reply - A2A interface URL ending in
/a2a
curl -sS -X POST http://localhost:3000/a2a \
-H "Content-Type: application/json" \
-H "A2A-Version: 1.0" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "SendMessage",
"params": {
"message": {
"messageId": "msg-1",
"role": "ROLE_USER",
"parts": [{"text": "Hello"}]
}
}
}'A successful response reaches:
result.task.status.state == TASK_STATE_COMPLETED
Artifact Text Part == "Echo: Hello"
See docs/design/quick-start.md for the design and verification notes behind this flow.
A minimal Agent looks like this:
class ShoppingAgent < A2A::Rails::Agent
name "Shopping Agent"
description "Search and purchase products"
version "1.0"
skill :search_products,
description: "Search products",
tags: %w[shopping search],
handler: Shopping::SearchProducts
endHandler:
class Shopping::SearchProducts
def self.call(message:, context:)
# Rails business logic
end
endRegistration:
A2A::Rails.configure do |config|
config.agent = "ShoppingAgent"
config.public_base_url = ENV["A2A_PUBLIC_BASE_URL"]
endv0.1 targets one public A2A Agent per Rails application.
For multiple Skills, the application supplies a Router. The Router receives skills: as a frozen Array<Symbol> of declared Skill IDs and may return a matching Symbol or String. Unknown selections raise A2A::Rails::UnknownSkillError; the Dispatcher does not silently choose the first Skill.
The Gem automatically exposes:
GET /.well-known/agent-card.json
POST /a2a
The Rails Engine is mounted automatically.
Published 0.2.0 includes a host-provided config.authenticate_request callback for POST /a2a, plus per-principal Task ownership checks. Without an authenticator, production and other non-development/test environments fail closed; the local development/test Quick Start remains available. See Authentication guide.
Important: These protections are absent from RubyGems 0.1.0 but included in the published
0.2.0.rc2pre-release; stable0.2.0includes additional fixes. Neither pre-release nor stable source automatically makes a deployed endpoint production-secure. Distributed rate limits, business authorization and the deployment review remain tracked in Issue #11.
Published 0.2.0 includes a bounded JSON-RPC body (config.max_request_bytes, default 1 MiB), Content-Type validation, stricter parameter checks, a pagination snapshot cap, and reduced exception logging. See Request Hardening Guide.
Rate limiting, application-specific authorization and production deployment safeguards remain the host's responsibility or future work. Step 16-5 adds explicit A2A Agent Card Bearer authentication advertisement in published 0.2.0; it must match the host verifier. These improvements are not included in the published v0.1.0 Gem; see the published stable 0.2.0 release.
Current verdict: NO-GO by default for open public production. The default MemoryStore remains process-local. Step 21 provides an optional durable ActiveRecordStore, and Step 22 provides optional ActiveJob Task execution, but production async operation requires both a shared/durable Task Store and a durable queue backend operated by the host. A real deployment must also provide credential verification, business authorization, TLS/proxy restrictions, distributed rate limits, execution budgets, observability and deployment-specific verification.
- Production security & deployment guide — responsibilities, sample configuration, security checks and current blockers.
- Security release checklist — release/upgrade decision, acceptance criteria and artifact verification.
Do not interpret successful source-tree CI or a merge to main as publication of a new RubyGems release.
Agent Cards are generated from the Agent / Skill DSL.
Behavior in v0.1:
- Skill IDs and default display names come from Skill declarations;
- optional
examples,input_modes, andoutput_modesmap to A2A Agent Card fields; - Handler internals are not exposed;
public_base_urlwins when configured, otherwise the request base URL is used;- default input/output mode is
text/plain; - Streaming, Push Notifications, and Extended Agent Cards are disabled;
- generated Cards pass the
agent2agent 2.0.0Agent Card schema.
Internally, a2a-rails keeps SDK-independent Task states:
SUBMITTED
↓
WORKING
├──→ COMPLETED
├──→ FAILED
└──→ REJECTED
Cancellation can move a non-terminal Task to CANCELED.
Handler result mapping:
normal return → COMPLETED
A2A::Rails::RejectedTask → REJECTED
unexpected exception → FAILED
Artifact mapping:
String → Text Part
Hash / Array → Data Part
nil → no Artifact
other object → ArtifactMappingError
Included since v0.2.0 (not in v0.1.0):
A2A::Rails::FileArtifact.bytes(...) → File Part with base64 raw bytes
A2A::Rails::FileArtifact.url(...) → File Part with an HTTPS URL
File Artifact output is included in v0.2.0. Rails Handlers can explicitly return a FileArtifact.bytes(data:, filename:, media_type:) or FileArtifact.url(url:, filename:, media_type:). See the File Artifact output guide. Input file Parts, downloading remote URLs and production file authorization are not supplied by the Gem.
Included in v0.2.0 (Step 17-4): A2A v1.0 also permits a direct Message from SendMessage. The host Agent can opt into response_mode :message or select the mode using a callable; the default remains :task. Direct replies do not create Task records. See the Direct Message response guide for the contract and safety implications.
Supported Task operations:
SendMessageGetTaskListTasksCancelTask
ListTasks supports context/state/timestamp filters, stable newest-first ordering, page sizes 1–100, and opaque snapshot pagination.
The default Task::MemoryStore is thread-safe but process-local. Tasks and pagination cursors are not durable across process restarts and are not shared between processes.
Included in v0.2.0 (Step 21): applications that need durable, multi-worker Task state can opt into config.task_store = :active_record. The ActiveRecordStore uses owner-scoped SQL access, row-locked transitions, signed keyset cursors, terminal retention, bounded pruning and maintenance limits. See ActiveRecord Task Store. PostgreSQL 16 persistence/locking smoke is verified in CI.
Included in v0.2.0 (Step 22): Task execution remains synchronous by default. Hosts can opt into ActiveJob-backed async execution globally, per Agent, or per Skill; precedence is Skill > Agent > global.
A2A::Rails.configure do |config|
config.task_store = :active_record
config.task_execution_mode = :async
end
class ReportsAgent < A2A::Rails::Agent
execution_mode :sync
skill :build_report,
description: "Build a report",
tags: %w[report],
handler: Reports::Build,
execution_mode: :async
endAsync SendMessage persists and returns a SUBMITTED Task, then a Gem-owned ActiveJob worker claims it atomically and executes the already-selected Skill. Direct-Message responses remain synchronous because they do not create a persisted Task to poll.
Production async use requires a shared/durable Task Store plus a durable ActiveJob backend. The Gem does not provide a distributed transaction between Task persistence and queue enqueue: a small crash window remains after Task commit and before queue acknowledgement. Generic Handler retries are intentionally disabled; duplicate Job delivery is suppressed at Task claim, but exactly-once external side effects are not guaranteed. Running cancellation is logical/best-effort, and ambiguous WORKING Tasks are not automatically replayed after a worker crash. See ActiveJob Task Execution and queue adapter / HTTP async verification.
Cancellation changes Task state atomically, but does not stop already-running Handler code or reverse application side effects.
Key boundaries:
- Rails Engine / controllers / routes form the Rails integration layer;
- SDK-specific behavior stays behind
Protocol::Adapter/Protocol::Agent2AgentAdapter; - A2A camelCase fields,
TASK_STATE_*, SDK schema objects, and SDK errors stay in the Protocol layer; - Agent / Handler application constants are resolved lazily through Rails;
- ActiveRecord is optional and loaded only when ActiveRecordStore is selected; ActiveJob is not a runtime requirement.
Gem structure:
lib/a2a/rails/
├── agent.rb
├── skill.rb
├── dispatcher.rb
├── configuration.rb
├── runtime.rb
├── engine.rb
├── agent_card/
├── task/
└── protocol/
lib/generators/a2a/rails/
├── install_generator.rb
├── agent_generator.rb
└── templates/
Included:
- Rails integration
A2A::Rails::Agent- Skill DSL and Handler dispatch
- Agent Card generation
/.well-known/agent-card.jsonPOST /a2a- synchronous Task lifecycle
SendMessage,GetTask,ListTasks,CancelTaskA2A-Version: 1.0validation- in-memory Task Store
- Rails Engine / Routes
- Configuration
install/agentgenerators- Rails logging boundary
Not included in v0.1:
- A2A Client
- ActiveRecord Task Store
- ActiveJob Task execution
- SSE / BiDi Streaming
- Push Notifications
- gRPC
- Human-in-the-loop flows
INPUT_REQUIRED/AUTH_REQUIRED- OAuth Server
- Agent Registry / Marketplace
- Authorization Engine
- ActingFor integration
- Admin UI
- LLM Agent Framework
- Orchestration Framework
The v0.1.0 release candidate completed:
13 / 13 CI jobs green
70 tests
240 assertions
0 failures
0 errors
0 skips
After publication, a2a-rails 0.1.0 was fetched back from RubyGems and its SHA256 matched the exact artifact that was pushed.
Final published artifact SHA256:
23d34bde6f436723bf01735f3a507cf8529975b1dfed5d5da9b063fc0480b19f
A fresh Rails 8.1 application then installed the published Gem from RubyGems and verified:
Agent Card HTTP: 200
Agent name: Echo Agent
Skill: reply
SendMessage HTTP: 200
Task state: TASK_STATE_COMPLETED
Artifact: Echo: Hello
See docs/release/v0.1.0-record.md for the complete release evidence.
Step 18 / PR #27 verified the official Python a2a-sdk==1.2.2 and Go a2a-go/v2 v2.6.0 clients against a loopback-only Rails JSON-RPC A2A v1.0 Agent. Both discovered Agent Cards, decoded Task and direct Message responses, queried Tasks and checked terminal cancellation and version errors.
- Final official Python/Go interoperability CI — both PASS.
- Final Ruby/Rails regression CI — 13/13 PASS.
- Interop test code, reproduction and limitations.
This evidence applies to the later source/pre-release line, not RubyGems v0.1.0; these tests do not certify full A2A interoperability or public-production safety.
v0.1 uses Minitest with four layers:
4. Protocol E2E / Smoke Tests
3. Rails Integration Tests
2. Adapter Contract Tests
1. Core Unit Tests
The CI matrix covers:
- Gem: Ruby 3.3 / 3.4 / 4.0
- SDK spike: Ruby 3.3 / 3.4 / 4.0
- Rails: Ruby 3.3 / 3.4 / 4.0 × Rails 8.0 / 8.1
- Packaged Gem: Ruby 3.4 + clean Rails 8.1 application
Known warning-enabled output from upstream agent2agent 2.0.0 can include circular-require, indentation, and URI-parser warnings. Those warnings are distinct from the Rack-environment INFO logging that a2a-rails suppresses at the Rails-facing adapter boundary.
- v0.1 Design Decisions
- v0.1 Test Strategy
- v0.1 Gem Structure
- v0.1 Quick Start Design
- SDK compatibility findings
The Gem is available as open source under the terms of the MIT License. See LICENSE.
- A2A Protocol overview / A2Aの全体像(日本語・A4 1枚PDF) — 公式資料をもとに作成した学習資料(2026-10-07)。
- A2A at a glance (English, A4 one-page PDF) — Learning guide based on the official documentation (2026-10-07).
- A2A Protocol documentation (Latest) / 公式ドキュメント
- A2A Protocol v1.0.0 documentation / v1.0.0ドキュメント
- A2A v1.0.0 Specification / v1.0.0仕様書
- Official GitHub / 公式GitHub: a2aproject/A2A
- Official samples / 公式サンプル: a2aproject/a2a-samples

