Coordination without language models
These examples show ordinary ASTRA agents coordinating across HTTP services. No language model, provider key or model call is needed. The agents use goals, beliefs and rules to choose partners, allocate work and handle failure; ANP/OpenAPI or A2A carries their requests and responses.
The example repository contains nine coordination patterns, each with four runnable Maven projects. Despite the repository name, these examples do not require MCP. The example release is coordination-1.0.1, using ASTRA 2.7.10 and MAMS 2.5.7. All 36 coordination cases were exercised as separate peer and client processes.
Choose a pattern
Start with book trading to understand request correlation and partner selection, then explore the other patterns. Each link opens a project README with startup instructions and code snippets.
Pattern |
What happens |
ANP |
MAMS ANP |
A2A |
MAMS A2A |
|---|---|---|---|---|---|
Book trading |
Concurrent quotes, stale stock, fallback seller and duplicate purchase receipt |
||||
Contract Net style allocation |
Proposals, accepted award, known execution failure and reassignment |
||||
Auctions |
Highest bidder, first-price and second-price payment in independent trials |
||||
Blackboard |
Two native workers compete to claim and complete one remote task |
||||
Workflow |
Reserve, pay and ship; compensate and check state after failure |
||||
Publish–subscribe |
Peer callbacks, unsubscribe and duplicate-event handling |
||||
Negotiation |
Offers, counteroffers, agreement and budget-limited withdrawal |
||||
Organizational commitments |
Roles, activation, discharge and logical deadline violation |
||||
Distributed action planning |
Discover action models, compose applicable steps and execute across peers |
For a smaller starting point, use the repository’s ping-pong examples: each performs three request/reply rounds in the same four transport variants.
Run book trading
Use Java 17 or later and Maven. Clone the repository and build its projects once:
git clone https://gitlab.com/astra-language/examples/llm-examples/mcp.git
cd mcp
mvn clean install
Start three participants, each in a separate terminal, from the coordination-anp directory:
mvn astra:run -Ddemo.mainClass=coordination.anp.Alpha
mvn astra:run -Ddemo.mainClass=coordination.anp.Beta
mvn astra:run -Ddemo.mainClass=coordination.anp.Gamma
These commands use the ASTRA Maven plugin. Each selected startup agent supplies its role and port to the shared Node agent through an ordinary subgoal; Alpha uses 8120, Beta 8121 and Gamma 8122. Wait for NODE READY from all three. In another terminal, run the buyer from book-trading-anp:
mvn astra:run
The buyer collects quotes of 10, 15 and 20. Alpha’s stock deliberately becomes unavailable when the buyer tries to purchase. The buyer removes that proposal, chooses beta and retries the same purchase identifier to check that the receipt is stable. A successful client prints BOOK-TRADING CHECK PASSED and exits. Stop the participant processes with Ctrl-C.
The other variants use the corresponding support directory and three consecutive ports:
Variant |
Participant directory |
Ports |
Buyer directory |
|---|---|---|---|
ANP |
|
8120–8122 |
|
MAMS ANP |
|
8130–8132 |
|
A2A |
|
8140–8142 |
|
MAMS A2A |
|
8150–8152 |
|
For the other transports, select the same startup classes in coordination.mams_anp, coordination.a2a or coordination.mams_a2a, respectively. Each scenario project configures its Main agent, so its client command remains mvn astra:run.
Restart participants before each new scenario: their beliefs retain stock, ownership, receipts and deliberate one-time failures. To run the checks automatically from the repository root:
python3 tests/test_coordination.py --pattern book-trading --variant anp
python3 tests/test_coordination.py
The runner starts and stops its own processes; the second command checks all 36 cases.
Separate the scenario from its transport
Each startup agent combines a coordination scenario with a transport client. The ANP buyer is:
agent Main extends coordination.BookTrading, coordination.anp.Client { }
The scenario inherits the common client goals. The transport implements !connect, !sendRequest and !close, and turns completion events into remoteReply beliefs. Changing to coordination.a2a.Client gives the same buyer policy an A2A transport.
ANP discovers a public agent description and selects its OpenAPI operation. A2A discovers an Agent Card, submits a skill request and obtains the answer from a completed task artifact. MAMS variants use CArtAgO HTTP resources for these exchanges. The adapters handle transport; native ASTRA rules retain the business decisions and state.
The ANP adapter remains an experimental public discovery/OpenAPI profile in the example repository. It does not implement DID authentication, proof validation or encrypted messaging. The A2A examples use fresh tasks and polling; they do not demonstrate streaming or input-required continuation.
Keep the response in the calling intention
The common client builds an application request with an operation, business identifier, value and a fresh wire identifier. This excerpt comes from coordination.Client:
rule +!rpc(string peer,string operation,string id,string value,string answer) {
JsonNode packet = json.with(json.object(),"action",operation);
packet=json.with(packet,"id",id);packet=json.with(packet,"value",value);packet=json.with(packet,"wire",wire.id());
!sendRequest(peer,json.toJsonString(packet),string call);
wait(remoteReply(call,boolean failed,string payload));-remoteReply(call,failed,payload);
if(failed){C.println("Protocol failure: " + payload);sys.fail();}
answer=payload;
}
call correlates the transport response. id identifies the business operation, such as a purchase, and can remain unchanged when retrying. The fresh wire identifier distinguishes individual exchanges.
wait suspends this intention while the agent continues handling other events. It preserves the caller’s local variables and subgoal chain, allowing answer to return through !rpc. Consuming the response belief prevents it being reused accidentally. A transport failure fails the goal so the caller can handle it through ASTRA recovery or a failed-goal event.
A separate +remoteReply rule could process the response instead, but would need a pending belief that records the call’s business context, plus an explicit continuation and failure path. The examples retain wait because the interaction is naturally a subgoal that returns an answer. See Agent communication for the corresponding native messaging concepts.
Collect proposals concurrently
The buyer launches separate intentions and joins their completion beliefs:
rule +!gather(string operation,string id) {
!!solicit("alpha",operation,id);!!solicit("beta",operation,id);!!solicit("gamma",operation,id);
wait(responded("alpha") & responded("beta") & responded("gamma"));
}
Each !!solicit runs independently, stores a usable proposal(peer, price) and records responded(peer) even when that solicitation fails. The join therefore waits for completion, rather than requiring every peer to offer a proposal. The buyer selects the least available quote and handles a refusal in its purchase goal:
rule +!purchase(list peers,string seller) {
!least(peers,"",10000,string chosen,int price);
C.println("Selected quote: " + chosen + " price " + price);
if(chosen==""){sys.fail();}
!rpc(chosen,"buy","book-1","ASTRA",string answer);
if(json.valueAsString(answer,"/status")=="purchased"){seller=chosen;}
else {C.println("Quote could not be fulfilled by " + chosen);-proposal(chosen,price);!purchase(peers,string fallback);seller=fallback;}
}
Partner selection and fallback are ordinary ASTRA reasoning. The transport does not choose a seller.
Preserve state and coordinate concurrent updates
Each participant has a persistent native service agent. Application beliefs record inventory, task ownership, subscriptions, agreements and commitments. Business updates use a named business lock and check the current state inside that lock. This lets two blackboard workers compete without both becoming the owner. See Synchronization for the locking rules.
Network waits occur after releasing the lock. Publishers snapshot their subscribers, then create native courier agents to deliver callbacks. Commitment members similarly create native participant agents to submit fulfilment attempts. Waiting under the business lock would prevent other requests from updating the state needed to finish the operation.
A2A creates a fresh worker for each task. That worker consults the persistent service agent through local ASTRA messages, then completes the A2A task with the answer. Those local messages stay within the participant process: communication between peers, including the returned result, uses A2A.
What the larger patterns add
The workflow makes failure handling visible: shipping fails before producing an effect, so the coordinator refunds payment, releases the reservation and queries the resulting state. These are explicit compensating operations. A timeout does not establish that an operation had no effect, so the examples do not blindly reassign work after an unknown transport outcome.
Publish–subscribe delivers actual peer callbacks. Subscribers deduplicate events and reject conflicting content with the same identifier. Unsubscription affects future publication snapshots. This is an application protocol, rather than WebSub or A2A streaming, and it has no durable delivery queue.
Negotiation gives the buyer a budget and the seller an independent reservation price and concession policy. Agreements and withdrawals remain terminal when requests repeat. Commitments add a role-based registry with conditional, active, discharged and violated states, using a logical clock. Its configured identities and condition events are fixtures, rather than authenticated membership or verified payment evidence.
Distributed planning discovers preconditions and effects from different peers. The coordinator checks its world beliefs, selects an applicable action, executes it remotely and applies effects only after success. Planning is centralized, while action descriptions and execution are distributed. The bounded greedy planner is neither complete nor optimal.
All service state and deduplication records last only for the process lifetime. The suite teaches coordination policy and transport separation; durable recovery, production authorization and stronger delivery guarantees require additional infrastructure. The Contract Net example uses application-level JSON operations and does not claim FIPA ACL wire conformance.