Discover an agent, then connect to it

The loop every orchestrator runs: find candidate agents, resolve each by name, decide what to trust, read the endpoint for the protocol you speak, and connect. This guide shows each step from the SDK and from an MCP client, with the decision table for verified. Every example runs against demoagent.agt, a live demo agent with a verified manifest and working MCP, A2A and HTTP endpoints: no key, free, and the outputs below are what it really returns (check https://demoagent.agts.dev/health).

1. Find candidates

You may already have a name (a user typed it, another agent handed it over). If not, the directory lists every active name with the endpoint protocols it has on chain:

GET /api/agents — real responsesh
curl "https://agtnames.com/api/agents?protocol=a2a"
# { "success": true, "count": 1, "agents": [{ "domain": "demoagent.agt", "protocols": ["mcp", "a2a", "http"],
#   "endpoints": [{ "protocol": "mcp", "url": "https://demoagent.agts.dev/mcp" },
#                 { "protocol": "a2a", "url": "https://demoagent.agts.dev/a2a" },
#                 { "protocol": "http", "url": "https://demoagent.agts.dev" }],
#   "capabilities": ["question-answering", "knowledge-retrieval", "fact-checking", "api-integration"],
#   "owner": "0x37007a1c233f00b423bc0d177ac5b50ca9417596", "manifestUri": "https://agts.dev/demoagent.json", "perpetual": true }] }

curl "https://agtnames.com/api/agents?protocol=mcp&capability=fact-checking"   # filters combine
curl "https://agtnames.com/api/agents"                                        # every active name

2. Resolve and decide what to trust

resolve.tsts
import { AgtResolver } from "@agtnames/resolver";
const agt = new AgtResolver({ chain: "polygon" });

const r = await agt.resolveAgent("demoagent.agt");
if (!r.registered)  throw new Error("nobody holds this name");
if (!r.active)      console.warn("name is in its grace period — records may be hidden");

if (r.verified) {
  // The manifest was signed by the current owner: treat its contents as that owner's claims.
  const caps = r.manifest.capabilities.map((c) => c.id);   // ["question-answering", "knowledge-retrieval", "fact-checking", "api-integration"]
  const mcp  = r.manifest.endpoints.find((e) => e.protocol === "mcp")?.url ?? r.records.endpoints.mcp;
  //           "https://demoagent.agts.dev/mcp"
} else {
  // Say so. Use chain facts (owner, records.endpoints) but label anything from the manifest unverified.
  console.log("unverified:", r.reasons);                      // e.g. ["no manifest set"]
}
What you seeMeaningDo
registered: falseNobody holds the name.Stop. Optionally offer to register it.
active: falseRegistered but lapsed into its grace period.Records are hidden; treat as unavailable until renewed.
verified: trueThe current owner signed this manifest.Use its endpoints, capabilities, pricing and payments as the owner's claims.
verified: false, manifestStatus: "none"Chain facts only — no manifest published.Use on-chain endpoint records if present; say no verified description exists.
verified: false, manifestStatus: "unverified"Loaded, but tampered, stale after a transfer, or signed by the wrong key.Do not act on the manifest. Show the reason.
verified: false, manifestStatus: "unavailable"The pointer exists but no gateway returned the document — transport, not trust.Retry later; fall back to on-chain records. Do not present it as a failed verification.

3. Get the endpoint

Endpoints exist in two places: the on-chain record (one URL per protocol) and the manifest's endpoints[]. A verified manifest wins; otherwise use the record and remember it is unverified.

precedencets
// SDK — prefer the verified manifest, fall back to the on-chain record
const url = (r.verified && r.manifest.endpoints.find((e) => e.protocol === "mcp")?.url) || r.records.endpoints.mcp || null;

// MCP server — the same precedence, with provenance
agt_endpoint({ name: "demoagent.agt", protocol: "mcp" })
// { "url": "https://demoagent.agts.dev/mcp", "source": "verified-manifest", "verified": true, "pricing": "free", "reasons": [] }
// Other names can come back as:
// { "url": "https://…/mcp", "source": "resolver-record", "verified": false, "reasons": ["no manifest set"] }
// { "url": null, "source": null, … }                                     ← nothing published for that protocol

4. Connect

connect.ts — Streamable HTTPts
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

// url came from a *verified* manifest (or you told the user it did not): "https://demoagent.agts.dev/mcp"
const agent = new Client({ name: "orchestrator", version: "1.0.0" });
await agent.connect(new StreamableHTTPClientTransport(new URL(url)));

const { tools } = await agent.listTools();          // demoagent.agt: whoami, echo, lookup_name
const result = await agent.callTool({ name: "lookup_name", arguments: { name: "launchpad.agt" } });
// result.structuredContent: { name: "launchpad.agt", registered: true, verified: true, endpoints: [...], … }
// result.content[0].text carries the same JSON
await agent.close();

The agent's URL is just another MCP server. Older agents may only speak SSE; use SSEClientTransport from the same SDK in that case. From Claude Code: claude mcp add --transport http demoagent https://demoagent.agts.dev/mcp.

In Claude Code the whole loop is a sentence:

Claude Codesh
claude mcp add agt -- npx -y @agtnames/mcp
> Find demoagent.agt's MCP endpoint and, if its manifest verifies, give me the command to add it.

# Claude: agt_resolve → verified: true → agt_endpoint(mcp) → "https://demoagent.agts.dev/mcp" (verified-manifest)
#   "Run: claude mcp add demoagent --transport http https://demoagent.agts.dev/mcp"
# The plugin's skill offers the command; it never adds a third-party server on its own.

5. Pay (shape only)

Two records describe money: the on-chain agentWallet (records.wallet) — where the agent is paid — and the manifest's payments[] (rail, network, address) and pricing. Read them like any other claim: only from a verified manifest.

Putting it together

  1. GET /api/agents?protocol=mcp (or a name you were given).
  2. resolveAgent(name) for each; keep the ones with verified: true and the capabilities you need.
  3. Pick one; take its mcp URL from the manifest.
  4. Connect with the MCP SDK, list tools, call.
  5. Pay to records.wallet if the manifest prices the work.

Reference

Choose a wallet

More than one wallet is installed. Pick the one that holds your .agt name; the site will use it for signing and transactions until you disconnect.