a2a · Remote Skills as Function Tools
How to discover a remote A2A agent's advertised skills and hand each one to a local host agent as an ordinary function tool.
What this lesson demonstrates
A remote agent publishes an agent card over HTTP — a JSON document describing the skills it offers. This lesson resolves that card, wraps the remote agent behind the a2aprovider, and turns each advertised a2a.AgentSkill into a function tool. A local Foundry host agent (a travel planner) is given those tools. When the host model decides it needs a skill, calling the tool round-trips a prompt to the remote agent and returns its answer. One agent’s skills become another agent’s tools.
The core: a skill becomes a tool
createSkillTools builds one functool.MustNew per advertised skill. Each tool’s handler is a closure that forwards the caller’s query to the remote agent:
tools = append(tools, functool.MustNew(functool.Config{
Name: sanitizeToolName(cmp.Or(skill.Name, skill.ID, "a2a_skill")),
Description: formatSkillDescription(skill),
}, func(ctx context.Context, query string) (string, error) {
resp, err := remoteAgent.RunText(ctx, skillPrompt(skill, query)).Collect()
if err != nil {
return "", err
}
return resp.String(), nil
}))
From the host model’s point of view, “calling a skill” is just an ordinary function-tool call. The fact that it fans out to a network call is invisible.
What to notice
- Names and descriptions are derived, not hand-written.
sanitizeToolNamelower-cases and collapses non-alphanumeric runs to underscores, falling back to the skill’sIDand then toa2a_skill.formatSkillDescriptionflattens the skill’sDescription,ID,Tags,Examples, and I/O modes into a multi-line tool description so the host model has enough context to choose it. Both are pure functions — the offline test hammers them directly. - Two providers, composed.
a2aprovider.NewAgent(client, …)wraps the remote agent;foundryprovider.NewAgent(…, Tools: …)builds the local host. The host never knows a tool is backed by a network call. - Construction is separated from
main.buildHostAgent(...)andcreateSkillTools(...)are factored out so the test can build the identical wiring offline — fake credential, fake remote agent, no server, no model.
The gotcha worth internalizing: the skill’s human-readable Name (e.g. "Route Planner") is not a valid tool identifier. If you skip sanitizing, tool registration fails or two skills silently collide on the same name.
How it maps to the Agent Framework
This is Agent-to-Agent (A2A) composition in the Microsoft Agent Framework Go SDK. The a2aprovider package makes a remote agent look like a local *agent.Agent, so it slots into the same agent.Config{Tools: …} seam that any function tool uses. On Azure AI Foundry, the host agent runs against your deployed model while its tool calls reach out over A2A — you compose specialized remote agents into one planner without the model ever leaving the standard tool-calling contract.
Run it
export A2A_AGENT_HOST=http://127.0.0.1:5000
export FOUNDRY_PROJECT_ENDPOINT=... # + az login
go run ./tutorial/02-agents/a2a/as_function_tools
Most of the lesson builds and tests offline — the structural tests wire a fake credential and fake remote agent with no network — and the live end-to-end run is gated behind AF_LIVE=1 (it needs a running A2A server and az login).
Key takeaways
- A remote agent’s advertised skills become local function tools.
createSkillToolsbuilds onefunctool.MustNewpera2a.AgentSkill; each handler closure forwards the query to the remote agent viaremoteAgent.RunText(...).Collect(), so a network call looks like an ordinary tool call to the host model. - Names and descriptions are derived, not hand-written.
sanitizeToolNamelower-cases and collapses non-alphanumeric runs to underscores (falling back to the skillID, thena2a_skill), andformatSkillDescriptionflattens description, tags, examples, and I/O modes so the host model has enough to choose the tool. - Two providers compose.
a2aprovider.NewAgentwraps the remote agent;foundryprovider.NewAgentbuilds the local host — the host never knows a tool is backed by a network call. - The gotcha: a human-readable skill
Namelike “Route Planner” is not a valid tool identifier; skip sanitizing and registration fails or two skills silently collide.
Further reading
- A2A · Polling for Task Completion — drive a slow remote agent with a continuation token instead of one long call.
- providers · A2A (Agent2Agent) — swap the LLM behind an agent for a remote agent reached over A2A.
- step10 · Agent as an MCP Tool — the MCP counterpart to exposing an agent as a reusable capability.
Next: A2A · Polling for Task Completion