Skip to content

Use Mock Server

The a2a/mock package provides a lightweight, in-process A2A server backed by httptest.Server. Use it for fast, deterministic tests without needing a real agent.


  • Fast — no network overhead, no LLM calls
  • Deterministic — canned responses for reproducible tests
  • No external dependencies — runs entirely in-process
  • Configurable — skill routing, input matching, latency/error injection

package main
import (
"context"
"fmt"
"log"
"github.com/AltairaLabs/PromptKit/runtime/a2a"
"github.com/AltairaLabs/PromptKit/runtime/a2a/mock"
)
func main() {
ctx := context.Background()
text := "Found 3 papers on quantum computing"
server := mock.NewA2AServer(
&a2a.AgentCard{
Name: "Research Agent",
Skills: []a2a.AgentSkill{
{ID: "search_papers", Name: "Search Papers"},
},
},
mock.WithSkillResponse("search_papers", mock.Response{
Parts: []a2a.Part{{Text: &text}},
}),
)
url, err := server.Start()
if err != nil {
log.Fatal(err)
}
defer server.Close()
// Use the mock like any A2A server.
client := a2a.NewClient(url)
card, _ := client.Discover(ctx)
fmt.Printf("Agent: %s\n", card.Name)
}

Rules are evaluated in order — the first match wins. Each rule targets a skill ID and optionally applies a matcher to the message content.

The mock extracts the skill ID from message or request metadata (metadata.skillId) and matches it against rules:

mock.WithSkillResponse("search_papers", mock.Response{
Parts: []a2a.Part{{Text: &searchResult}},
})
mock.WithSkillResponse("summarize", mock.Response{
Parts: []a2a.Part{{Text: &summary}},
})

Use WithInputMatcher for content-based routing:

quantumResult := "Papers about quantum computing..."
generalResult := "General research results..."
mock.WithInputMatcher("search_papers",
func(msg a2a.Message) bool {
for _, p := range msg.Parts {
if p.Text != nil && strings.Contains(*p.Text, "quantum") {
return true
}
}
return false
},
mock.Response{Parts: []a2a.Part{{Text: &quantumResult}}},
)
// Fallback: matches any message to search_papers.
mock.WithSkillResponse("search_papers", mock.Response{
Parts: []a2a.Part{{Text: &generalResult}},
})

More specific matchers should come before general fallbacks since first match wins.


Add a delay before each response to test timeout handling:

server := mock.NewA2AServer(card,
mock.WithLatency(2 * time.Second),
mock.WithSkillResponse("search_papers", response),
)

Return a failed task for a specific skill:

server := mock.NewA2AServer(card,
mock.WithSkillError("search_papers", "Service unavailable"),
)

The mock returns a task with status.state: "failed" and the error message in the status message.


For Arena integration or file-based configuration, define rules as AgentConfig:

import "github.com/AltairaLabs/PromptKit/runtime/a2a/mock"
cfg := &mock.AgentConfig{
Name: "research_agent",
Card: a2a.AgentCard{
Name: "Research Agent",
Skills: []a2a.AgentSkill{
{ID: "search_papers", Name: "Search Papers"},
},
},
Responses: []mock.RuleConfig{
{
Skill: "search_papers",
Match: &mock.MatchConfig{Contains: "quantum"},
Response: &mock.ResponseConfig{
Parts: []mock.PartConfig{{Text: "Quantum papers found"}},
},
},
{
Skill: "search_papers",
Response: &mock.ResponseConfig{
Parts: []mock.PartConfig{{Text: "General papers found"}},
},
},
},
}
opts := mock.OptionsFromConfig(cfg)
server := mock.NewA2AServer(&cfg.Card, opts...)
FieldDescription
containsCase-insensitive substring match on message text
regexRegular expression match on message text

Both conditions must match if both are specified.


The same config works in YAML (used by Arena):

a2a_agents:
- name: research_agent
card:
name: Research Agent
description: Mock research agent
skills:
- id: search_papers
name: Search Papers
description: Search for academic papers
tags: [research]
responses:
- skill: search_papers
match:
contains: quantum
response:
parts:
- text: "Found 3 papers on quantum computing"
- skill: search_papers
response:
parts:
- text: "Found 1 general paper"