Skip to content

Commit b4e5f0d

Browse files
kaisozaojea
authored andcommitted
sdk: expect the card of an agent behind a node rewritten for the mesh
1 parent 049d7a2 commit b4e5f0d

1 file changed

Lines changed: 51 additions & 1 deletion

File tree

‎tests/integration/sdk_mesh_test.go‎

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,16 @@ egress:
212212
// serving the MCP service "calc" from a backend this test runs.
213213
backend := httptest.NewServer(newBoundaryMCPHandler(t))
214214
t.Cleanup(backend.Close)
215+
// A stock A2A agent behind the node; its card names its own address.
216+
agentCard := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
217+
if r.URL.Path != "/.well-known/agent-card.json" {
218+
http.NotFound(w, r)
219+
return
220+
}
221+
w.Header().Set("Content-Type", "application/json")
222+
_, _ = io.WriteString(w, sdkMeshStockAgentCard)
223+
}))
224+
t.Cleanup(agentCard.Close)
215225
nodeBin := buildBinary(t, "./cmd/sam-node")
216226
nodeHome := filepath.Join(t.TempDir(), "node")
217227
if err := os.MkdirAll(nodeHome, 0o755); err != nil {
@@ -232,7 +242,9 @@ egress:
232242
"--allow-loopback",
233243
"--api-token-path", tokenPath(t, "node-token"),
234244
"--discovery-interval", "100ms",
235-
"--config", writeNodeConfig(t, nodeHome, sdkMeshLabels, svcDecl{Type: "mcp", Name: "calc", TargetURL: backend.URL}),
245+
"--config", writeNodeConfig(t, nodeHome, sdkMeshLabels,
246+
svcDecl{Type: "mcp", Name: "calc", TargetURL: backend.URL},
247+
svcDecl{Type: "a2a", Name: sdkMeshAgentName, TargetURL: agentCard.URL}),
236248
"--secrets-dir", secrets,
237249
"--log-level", "debug",
238250
)
@@ -259,6 +271,15 @@ egress:
259271
}
260272
}
261273

274+
const sdkMeshAgentName = "echo-agent"
275+
276+
const sdkMeshStockAgentCard = `{"name":"echo-agent","description":"stock agent behind a sam-node","version":"1.0.0",` +
277+
`"capabilities":{"streaming":true},` +
278+
`"supportedInterfaces":[{"url":"http://127.0.0.1:7777/","protocolBinding":"JSONRPC","protocolVersion":"1.0"},` +
279+
`{"url":"127.0.0.1:50051","protocolBinding":"GRPC","protocolVersion":"1.0"}],` +
280+
`"signatures":[{"protected":"eyJhbGciOiJFUzI1NiJ9","signature":"c3RhbGU"}],` +
281+
`"skills":[],"defaultInputModes":["text/plain"],"defaultOutputModes":["text/plain"]}`
282+
262283
// sdkMember is a running SDK conformance-join runner: a mesh member written
263284
// in another language that the test drives over stdin/stdout. Both runners
264285
// (sdk/js/src/conformance-join.ts, sdk/python/src/agent_mesh/conformance_join.py)
@@ -468,6 +489,35 @@ func TestNativeSDKsMesh(t *testing.T) {
468489
if res := m.callRaw(t, nodeRelayAddr, "mcp://no-such-service", "add", nil); res.OK {
469490
t.Fatalf("%s called a service the node does not serve: %+v", m.name, res)
470491
}
492+
493+
// A stock client bootstraps from the node's agent card, so the SDK
494+
// serves it rewritten for the mesh, as the node's egress proxy does.
495+
res := m.http(t, nodeRelayAddr, "a2a://"+sdkMeshAgentName, "/.well-known/agent-card.json")
496+
var card struct {
497+
SupportedInterfaces []struct {
498+
URL string `json:"url"`
499+
ProtocolBinding string `json:"protocolBinding"`
500+
} `json:"supportedInterfaces"`
501+
Capabilities struct {
502+
Streaming bool `json:"streaming"`
503+
} `json:"capabilities"`
504+
Signatures []json.RawMessage `json:"signatures"`
505+
}
506+
if res.Status != 200 || json.Unmarshal([]byte(res.Body), &card) != nil {
507+
t.Fatalf("%s fetching the node's agent card: %+v", m.name, res)
508+
}
509+
meshBase := "http://mesh/sam/" + samNode.peerID.String() + "/a2a/" + sdkMeshAgentName
510+
if len(card.SupportedInterfaces) != 1 || card.SupportedInterfaces[0].URL != meshBase || card.SupportedInterfaces[0].ProtocolBinding != "JSONRPC" {
511+
t.Errorf("%s got interfaces %+v, want one JSONRPC interface at %s", m.name, card.SupportedInterfaces, meshBase)
512+
}
513+
// Streaming stays as the agent declares it: the SDK's transport streams.
514+
if !card.Capabilities.Streaming || len(card.Signatures) != 0 {
515+
t.Errorf("%s got a card with streaming=%v (want true) and %d signatures (want none)", m.name, card.Capabilities.Streaming, len(card.Signatures))
516+
}
517+
// The bare service root serves the same card, as the node does for a2a-go.
518+
if root := m.http(t, nodeRelayAddr, "a2a://"+sdkMeshAgentName, "/"); root.Body != res.Body {
519+
t.Errorf("%s got a different card at the service root: %s", m.name, root.Body)
520+
}
471521
})
472522
}
473523

0 commit comments

Comments
 (0)