Skip to main content
Version: 2.5

A2A Script Binding

An A2A Script Binding publishes a certified tSM Script as an Agent Skill in the Agent2Agent Protocol (A2A). The Script's parameter and result Forms become its typed protocol contract. An external agent discovers the Skill through an Agent Card and invokes it through the existing tSM Gateway.

The binding supports two tSM execution modes:

tSM binding modeUse it whenA2A result
DIRECTThe Script produces the complete result within the invocation transactionAn A2A Message containing the validated Script result
TASKThe Script hands work to an implementation that can outlive the invocationA durable A2A Task; subsequent Scripts publish progress and an explicit terminal result

A TASK binding can hand work to a Kafka consumer, an integration, a ticket workflow, another Script, or a BPMN process. Each implementation preserves the opaque replyRef and completes the same inbound A2A Task through @a2a.reply.

This article describes an inbound A2A request handled by tSM. To call a remote agent from a BPMN process, use an A2A Agent Task.

MCP or A2A?

Use an MCP binding when an agent invokes a typed tSM tool and receives the result in that tool call. Use an A2A Script Binding when tSM participates as an agent with Agent Card discovery, Messages, durable Tasks, progress, interrupted states, Artifacts, streaming, or push notifications.

Execution Flow

Direct Mode

DIRECT executes the target Script in one transaction. The last Script value becomes the response. A timeout or exception fails that invocation according to the binding policy.

Task Mode

The Gateway atomically creates the inbound Task and queues its initializer before the binding Script starts. With returnImmediately: true, it returns the SUBMITTED Task as soon as that durable creation commits. The initializer later receives #a2aTask, commits a hand-off, and publishes WORKING or a terminal reply through @a2a.reply.

With returnImmediately: false, the same queued execution is used, while the HTTP operation waits on the Ledger until the Task reaches an interrupted or terminal state. It never keeps the Script transaction open while waiting.

The work can finish in another transaction or service. An authorized Script calls @a2a.reply, which records a local outbox intent. The Gateway reply processor validates that intent and updates the inbound Task Ledger. This flow supports both immediate A2A Task responses and callers that wait according to their A2A request policy.

Configure the Binding

A2A Script Bindings are configured in Scripts / Bindings / A2A Invocations (Scripts.Bindings.A2aInvocations).

FieldTypeRequiredDefaultDescription
fullNamestringYesStable Agent Skill ID, for example tsm.customer.balance.v1
scriptstringYesScript that handles the initial A2A Message
serverstringNodefaultGateway agent profile and Agent Card in which the Skill is published
descriptionstringNoScript descriptionPurpose, effects, expected result, and usage boundary shown in the Agent Card
executionModeDIRECT | TASKYesDIRECTtSM execution mode for a direct Message or durable Task lifecycle
replyPolicystringTASK onlyReply privilege, allowed service identities and Script codes, permitted states, and tenant scope
idempotencyPolicystringYesserver policyScope, key derivation, retention, and duplicate behavior for invocation and initializer retry
continuationScriptstringNoScript invoked for an authorized follow-up Message on an existing Task
cancellationScriptstringNoScript that forwards an accepted cancellation request to the underlying work
invocationTimeoutdurationYesserver policyMaximum duration of a direct result or one asynchronous TASK initializer attempt
taskTimeoutdurationTASK onlyserver policyMaximum lifetime before an unfinished Task receives its governed timeout result
availabilitystringNoenabledTenant rollout and kill-switch policy

The target Script remains the source of paramsFormCode, resultFormCode, target microservice, transaction behavior, and execPrivilege. Incoming Messages are validated against that certified configuration.

The Skill ID is a public integration contract. Breaking input, result, authorization, lifecycle, or side-effect changes use a new major Skill ID. A display-name edit preserves the same identity.

tSM Gateway and State Ownership

The existing tSM Gateway provides inbound Script protocol adapters:

ProtocolGateway responsibilityExample endpoint
MCPInbound tool discovery, tool calls, authentication, and routing to the Script's microservice/api/v2/mcp/<server>
A2AInbound Agent Cards, Messages, Tasks, streaming, push, authentication, and routing to the Script's microserviceURL from the Agent Card, for example /api/v2/a2a/operations

For an inbound TASK binding, the Gateway owns the authoritative Inbound A2A Task Ledger and its reply processor. Domain microservices own their business state and local outbox events and store only the opaque replyRef needed for correlation. A DIRECT binding completes in its Script transaction and therefore has no Ledger entry.

An outbound A2A Agent Task has different state ownership and does not traverse Gateway. The remote A2A server owns its Task, tsm-ai owns the local Outbound A2A Invocation and client transport, and the embedded Process Engine owns the BPMN activity and mapped business state.

Agent Card Discovery and tSM Governance

The A2A standard defines Agent Card discovery. An Agent Card describes the agent, its Skills, supported interfaces, protocol versions, capabilities, and security requirements. Its standard well-known location is:

https://operations-agent.tsm.example.com/.well-known/agent-card.json

The client reads the card, selects a compatible entry from supportedInterfaces, uses its exact url and protocolBinding, sends A2A-Version: 1.0, and applies the declared security requirements. When the interface contains tenant, every request carries the same opaque value.

One tSM Gateway can publish several agent profiles. Each public profile has a configured Agent Card base URL or hostname that routes to the same Gateway deployment. Private profiles can be distributed through direct configuration or an enterprise catalogue.

The tSM Agent Registry complements standard Agent Card discovery with enterprise governance. It approves and pins remote Agent Cards, Skills, interfaces, credentials, tenant availability, and rollout policy used by outbound A2A integrations.

Deterministic Skill Selection

Each A2A Script Binding declares this tSM protocol extension:

https://tsm.datalite.cz/a2a/extensions/script-binding/v1

The Agent Card advertises it in capabilities.extensions. The extension gives an inbound Message a deterministic mapping to the binding's stable fullName. A client:

  1. includes the extension URI in A2A-Extensions and message.extensions;
  2. stores skillId under a metadata object named by that extension URI;
  3. sends Script parameters as a structured Data Part.
{
"metadata": {
"https://tsm.datalite.cz/a2a/extensions/script-binding/v1": {
"skillId": "tsm.customer.balance.v1"
}
},
"extensions": [
"https://tsm.datalite.cz/a2a/extensions/script-binding/v1"
]
}

The URI namespace keeps tSM routing metadata distinct from the core A2A schema and from other extensions. The Gateway validates the Skill ID, version, availability, and authorization before routing the request.

Because this extension changes request routing, every tSM Agent Card publishes it as required:

{
"capabilities": {
"extensions": [
{
"uri": "https://tsm.datalite.cz/a2a/extensions/script-binding/v1",
"description": "Selects a published tSM Script Binding by stable Skill ID.",
"required": true
}
]
}
}

The client activates the URI in the A2A-Extensions header and message.extensions. Gateway rejects a request that omits this required extension or its valid namespaced skillId metadata.

Register tSM in an External A2A Client

An external client registers or resolves the tSM Agent Card URL, selects a compatible interface, configures the security scheme declared by the card, and optionally allow-lists Skills. Client configuration file syntax is implementation-specific; the protocol bootstrap remains the same.

Client Configuration Example

Client settingValue
Agent Card URLhttps://operations-agent.tsm.example.com/.well-known/agent-card.json
Interface URLExact url from a compatible supportedInterfaces entry
ProtocolHTTP+JSON, version 1.0
Media typeapplication/a2a+json
AuthenticationOAuth 2 bearer token or another scheme declared by the Agent Card
Required extensionhttps://tsm.datalite.cz/a2a/extensions/script-binding/v1
Allowed SkillStable binding fullName, for example tsm.customer.balance.v1

An illustrative client registration is:

{
"agents": {
"tsm-operations": {
"agentCardUrl": "https://operations-agent.tsm.example.com/.well-known/agent-card.json",
"headers": {
"Authorization": "Bearer ${TSM_TOKEN}"
},
"mediaType": "application/a2a+json",
"allowedSkills": ["tsm.customer.balance.v1"]
}
}
}

Clients cache the Agent Card using its HTTP cache headers, revalidate its ETag, and obtain credentials through the configured identity flow.

First Call over HTTP+JSON

First retrieve the card and select an interface URL:

curl --fail-with-body \
-H 'Authorization: Bearer <access-token>' \
https://operations-agent.tsm.example.com/.well-known/agent-card.json

If the selected interface URL is https://tsm.example.com/api/v2/a2a/operations, a non-blocking TASK invocation is:

POST /api/v2/a2a/operations/message:send HTTP/1.1
Host: tsm.example.com
Authorization: Bearer <access-token>
Content-Type: application/a2a+json
Accept: application/a2a+json
A2A-Version: 1.0
A2A-Extensions: https://tsm.datalite.cz/a2a/extensions/script-binding/v1

{
"message": {
"messageId": "019fc72c-6cab-7f52-a6b6-31b31eb67021",
"role": "ROLE_USER",
"parts": [
{
"data": {"customerId": "9ba8a665-4ef5-41eb-90af-ff29df9a8e65"},
"mediaType": "application/json"
}
],
"metadata": {
"https://tsm.datalite.cz/a2a/extensions/script-binding/v1": {
"skillId": "tsm.customer.balance.v1"
}
},
"extensions": [
"https://tsm.datalite.cz/a2a/extensions/script-binding/v1"
]
},
"configuration": {
"returnImmediately": true
}
}

A non-blocking TASK client sets returnImmediately to true and follows the returned Task using GetTask, streaming, or push. A client that intentionally waits for an interrupted or terminal state sets it to false or omits it. A DIRECT binding returns its Message as soon as the Script transaction completes. When the selected interface declares tenant, the request includes that value in its top-level tenant field.

Parameter and Result Forms

Script fieldA2A contract
paramsFormCodeGenerates the structured input schema and validates incoming Data Parts
resultFormCodeValidates the direct result or the data published with terminal COMPLETED

The Gateway validates field names, types, media types, and size before Script execution. Governed document references carry large files instead of copying their content into Script or process variables.

In TASK mode, the initial Script result is the internal hand-off receipt. The public business result is the payload later published as COMPLETED and validated against resultFormCode.

The #a2aTask Context Variable

TASK mode adds an invocation-specific, typed #a2aTask variable to the initial Script:

PropertyMeaning
taskIdOpaque inbound A2A Task ID visible to the caller
contextIdOpaque A2A interaction context, when present
messageIdID of the Message that created this Task
skillIdBound Agent Skill ID and version
replyRefOpaque, serializable correlation handle used by @a2a.reply
expiresAtDeadline after which replies follow the binding's timeout and reconciliation policy

The variable is scoped to one inbound TASK invocation. The initializer forwards #a2aTask.replyRef through one protected business field or message property. Every reply is authorized from trusted runtime identity, tenant, Script code, and replyPolicy; possession of the opaque value supplies correlation only.

DIRECT mode executes without an inbound Task context.

Publish Task Replies from SpEL

@a2a is a client SpEL binding available to tSM microservices. It gives Scripts a typed way to publish an inbound Task reply while preserving their local transaction boundary:

@a2a.reply({
'replyRef': #a2aReplyRef,
'state': 'WORKING',
'payload': {'message': 'Work started'},
'eventId': #eventId
})

In the initializer, the handle is #a2aTask.replyRef. A later Script receives the same opaque value through an explicitly named parameter in its Params Form, for example #a2aReplyRef.

Local Reply Intent: A2aReplyRequested

@a2a.reply validates the named parameter map and records an A2aReplyRequested intent in the calling microservice's transactional outbox. The function returns an A2aReplySubmission with the eventId and durable local submission state.

FieldRequiredTypeMeaning
replyRefYesstringOpaque handle copied from #a2aTask.replyRef
eventIdNostringStable idempotency ID for retries and at-least-once delivery
stateYesstringRequested A2A Task state without the TASK_STATE_ prefix
payloadYesmapState-specific status, result, Artifact, input, or error data
occurredAtNoinstantBusiness-event time; the binding supplies the current instant when omitted

A caller-provided eventId keeps retries deterministic. Repeating the ID with the same canonical state and payload returns the original submission receipt; reusing it for different content is a conflict. The Ledger serializes accepted events per Task in commit order and validates each transition against its current authoritative state.

The local submission confirms that the reply intent committed with the business change. The Gateway reply processor performs the authoritative Task transition asynchronously.

Gateway Ledger Event: A2aTaskEvent

The Gateway consumes A2aReplyRequested, resolves its protected replyRef, authenticates the trusted source envelope, applies replyPolicy, validates the state and payload, and appends an enriched A2aTaskEvent to the inbound Task Ledger:

FieldSupplied byMeaning
taskIdGatewayResolved inbound A2A Task identity
bindingIdGatewayCertified A2A Script Binding identity and version
tenantGatewayTrusted tenant scope
sequenceGatewayMonotonic event order within the Task
versionGatewayResulting optimistic-lock version
stateValidated requestAccepted A2A Task state
payloadRefGatewayGoverned reference to the validated payload or Artifact data

This separation keeps protocol state in the Gateway and business transactions in domain microservices. Accepted versions and classified rejections are visible in Task operations and audit.

Reply States

The Gateway assigns SUBMITTED when it creates an inbound TASK. Reply authors publish the first business update as WORKING, an interrupted state, or a terminal state. @a2a.reply accepts:

stateTerminalTypical payload
WORKINGNoMessage, bounded progress value, safe metadata, or incremental Artifact
INPUT_REQUIREDNoMessage and a versioned input Form reference
AUTH_REQUIREDNoMessage and an authorization-flow reference; never a credential
COMPLETEDYesResult data validated against resultFormCode and optional Artifacts
REJECTEDYesStable business reason and safe remediation
FAILEDYesStable error class, sanitized message, and optional operator reference
CANCELEDYesConfirmed cancellation or compensation outcome

SUBMITTED is Gateway-created lifecycle state and is therefore excluded from user-authored @a2a.reply calls.

The payload uses one stable envelope:

FieldMapping
messageSafe text or structured Parts in TaskStatus.message
progressBounded progress metadata for WORKING
inputFormCodeVersioned Form reference for INPUT_REQUIRED
authorizationRefSafe broker or approval reference for AUTH_REQUIRED
dataPrimary COMPLETED result, validated against resultFormCode and published as an Artifact Data Part
artifactsGoverned document references with media type, checksum, access, and retention metadata
errorStable public code and sanitized details for REJECTED or FAILED

For example:

// Progress from a later Script
@a2a.reply({
'replyRef': #a2aReplyRef,
'state': 'WORKING',
'payload': {
'message': 'Customer data validated',
'progress': 40
},
'eventId': #kafkaMessageId + ':validated',
'occurredAt': #validatedAt
})
// Explicit terminal business result
@a2a.reply({
'replyRef': #a2aReplyRef,
'state': 'COMPLETED',
'payload': {
'data': {
'ticketId': #ticket.id,
'status': #ticket.status
}
},
'eventId': #ticket.id + ':completed'
})

The Gateway atomically assigns the next version and enforces valid transitions, idempotency, and exactly one terminal state. The automation owner explicitly publishes COMPLETED, REJECTED, FAILED, or CANCELED; completion of the initializer or underlying business work is a separate event.

Starting Different Kinds of Work

The same inbound binding can initiate any implementation that preserves replyRef.

Kafka or an Integration

The initial Script sends a durable command containing business input and #a2aTask.replyRef. For example, a project-specific Script-to-Script binding can publish the command through a configured integration:

@script.customerCare.startAsync({
'customerId': #customerId,
'a2aReplyRef': #a2aTask.replyRef
})

The consumer handles the business work and invokes a configured reply Script with #a2aReplyRef. A transactional outbox preserves an atomic business change and hand-off intent.

Ticket or Other Business Object

The initial Script creates the object and stores replyRef in a protected internal correlation field. Event-bound Scripts publish milestones and the terminal business outcome. End users work with the business object rather than the correlation handle.

BPMN Process

The initial Script starts an ordinary tSM process and maps the typed Task context into a protected process variable:

#runtimeService.startProcessInstanceByKey(
'CustomerCare.Resolve',
#businessKey,
{
'customerId': #customerId,
'a2aTask': #a2aTask
}
)

Script and Service Tasks can use #a2aTask.replyRef with @a2a.reply. The process author models which milestones publish WORKING, when a Task requests input or authorization, and which end paths publish COMPLETED, REJECTED, FAILED, or CANCELED. This explicit reply keeps the A2A lifecycle aligned with the intended business outcome.

Continuation and Cancellation

When an existing Task receives another Message, the Gateway authenticates the caller, verifies its taskId and contextId, validates the Parts, and invokes continuationScript with #a2aTask and the new parameters. The Script correlates the data to the underlying work.

For CancelTask, a configured cancellationScript forwards the accepted request to the worker, ticket, integration, or process. It publishes CANCELED after cancellation or compensation is confirmed. A binding without a cancellation Script reports TaskNotCancelableError and preserves the current Task state.

The Ledger serializes a confirmed CANCELED reply with concurrent COMPLETED, REJECTED, or FAILED replies. The first valid terminal transition becomes authoritative and later transitions are rejected with the current Task state.

Transaction and Delivery Guarantees

  • A DIRECT invocation runs in its own Script transaction.
  • An inbound TASK and its idempotent initializer job are persisted atomically before the initial Script executes.
  • With returnImmediately: true, Gateway returns the created Task immediately after that commit. With blocking mode, the protocol request observes the Ledger until an interrupted or terminal state.
  • A successful initializer commits a durable hand-off or local outbox intent.
  • Reconciliation retries an initializer job or resolves a SUBMITTED Task whose initializer did not record a hand-off or reply before its deadline.
  • Local database changes and outgoing commands share a transactional outbox where atomic hand-off is required.
  • A2aReplyRequested events are delivered at least once and deduplicated by eventId.
  • The Gateway-owned Inbound A2A Task Ledger is authoritative; GetTask, streaming, and push are projections of the same persisted state.
  • The Gateway classifies stale versions, conflicting event IDs, invalid transitions, schema failures, and reply-policy violations while preserving the current Task state.
  • Reconciliation finds Tasks with no accepted hand-off, progress deadline, or terminal reply and applies the configured recovery policy.

An initializer exception rolls back its local changes and moves the inbound Task to a safe FAILED state. External effects use their own idempotency or compensation policy.

Security and Operations

  • Agent Card discovery publishes enabled Skills according to their visibility policy.
  • Invocation checks the binding privilege and access to referenced business objects.
  • replyRef provides opaque correlation; identity, tenant, Script code, and replyPolicy provide authorization.
  • Reply, continuation, read, subscribe, and cancellation operations recheck tenant and resource authorization.
  • Parameters, replies, progress, and Artifacts are schema-, size-, classification-, and retention-limited.
  • Credentials stay in managed identity and secret stores rather than Messages, Task metadata, replyRef, process variables, or logs.
  • Operations expose orphaned Tasks, reply failures, timeouts, duplicate or out-of-order events, outbox lag, and Tasks awaiting an explicit terminal reply.

See Also