<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Viren Tanti</title>
    <description>The latest articles on DEV Community by Viren Tanti (@virentanti).</description>
    <link>https://dev.to/virentanti</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4014501%2Ff51e1f38-0206-46b7-b4ed-d3bc8769095b.jpg</url>
      <title>DEV Community: Viren Tanti</title>
      <link>https://dev.to/virentanti</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/virentanti"/>
    <language>en</language>
    <item>
      <title>The spec required a dot. Every tool-calling API rejects it.</title>
      <dc:creator>Viren Tanti</dc:creator>
      <pubDate>Wed, 05 Aug 2026 18:59:15 +0000</pubDate>
      <link>https://dev.to/virentanti/the-spec-required-a-dot-every-tool-calling-api-rejects-it-452</link>
      <guid>https://dev.to/virentanti/the-spec-required-a-dot-every-tool-calling-api-rejects-it-452</guid>
      <description>&lt;p&gt;We shipped a specification with a rule that made it unusable for the single thing&lt;br&gt;
almost everyone would want to do with it. It took an HTTP 400 to notice.&lt;/p&gt;

&lt;p&gt;Here is the error, from a request built directly out of a conformant manifest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;HTTP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"invalid_request_error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"tool function names must match ^[a-zA-Z0-9_-]{1,64}$"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"tools[0].function.name"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing was malformed. The manifest validated. The capability name was exactly&lt;br&gt;
what the specification required it to be. That was the problem.&lt;/p&gt;
&lt;h2&gt;
  
  
  Two rules that cannot both be satisfied
&lt;/h2&gt;

&lt;p&gt;The Agent Discovery Specification defines capability names as &lt;code&gt;namespacedName&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;^[A-Za-z][A-Za-z0-9-]{0,63}(\.[A-Za-z][A-Za-z0-9-]{0,63})+$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trailing &lt;code&gt;+&lt;/code&gt; is the whole story. It makes at least one dot &lt;strong&gt;mandatory&lt;/strong&gt;.&lt;br&gt;
&lt;code&gt;com.example.lookupOrder&lt;/code&gt; is conformant. &lt;code&gt;lookupOrder&lt;/code&gt; is not.&lt;/p&gt;

&lt;p&gt;OpenAI's Chat Completions API constrains &lt;code&gt;tools[].function.name&lt;/code&gt; to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;^[a-zA-Z0-9_-]{1,64}$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No dot. Anthropic's &lt;code&gt;tools[].name&lt;/code&gt; and Google's &lt;code&gt;functionDeclarations[].name&lt;/code&gt;&lt;br&gt;
impose equivalent restrictions, and every OpenAI-compatible endpoint inherits&lt;br&gt;
the first one — in practice that means OpenRouter, Together, Groq, and most&lt;br&gt;
self-hosted gateways.&lt;/p&gt;

&lt;p&gt;These rules are not merely awkward together. They are mutually exclusive. The&lt;br&gt;
specification &lt;em&gt;requires&lt;/em&gt; the character the tool APIs &lt;em&gt;forbid&lt;/em&gt;. &lt;strong&gt;No conformant&lt;br&gt;
capability name could ever be passed to a tool-calling API.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.cheelalabs.com%2Fblog%2Fcapability-naming%2Fimages%2F01-two-rules-collide.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.cheelalabs.com%2Fblog%2Fcapability-naming%2Fimages%2F01-two-rules-collide.svg" alt="The ADS capability name pattern, whose trailing plus makes at least one dot mandatory, shown above the tool-calling API pattern which permits only letters, digits, underscore and hyphen. com.example.lookupOrder is conformant under the first and rejected by the second."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is not one vendor's quirk, and it is not something to wait out. It is the&lt;br&gt;
industry's settled convention, and we wrote a spec that could not meet it.&lt;/p&gt;
&lt;h2&gt;
  
  
  The part that is worse than the 400
&lt;/h2&gt;

&lt;p&gt;An error at the boundary is the good case. You see it immediately and you fix it.&lt;/p&gt;

&lt;p&gt;The real damage was quieter. Any client &lt;em&gt;can&lt;/em&gt; cope — stripping a namespace is&lt;br&gt;
trivial. But the specification never said how, so every client invented its own&lt;br&gt;
mapping, and clients that agreed perfectly on the manifest stopped agreeing the&lt;br&gt;
moment they built tools from it.&lt;/p&gt;

&lt;p&gt;Given &lt;code&gt;com.example.orders.get&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;th&gt;Resulting tool name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Take the last segment&lt;/td&gt;
&lt;td&gt;&lt;code&gt;get&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replace dots with hyphens&lt;/td&gt;
&lt;td&gt;&lt;code&gt;com-example-orders-get&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Take the last two segments&lt;/td&gt;
&lt;td&gt;&lt;code&gt;orders-get&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three clients, one manifest, three different tool names. A model trained or&lt;br&gt;
prompted against one client's names does not transfer to another's.&lt;/p&gt;

&lt;p&gt;Then it gets sharper. Consider a manifest advertising both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;com.example.orders.get&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;com.example.refunds.get&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Under the last-segment strategy, both become &lt;code&gt;get&lt;/code&gt;. The two capabilities collapse&lt;br&gt;
into one identifier, &lt;strong&gt;silently&lt;/strong&gt;. The model sees a single tool where two were&lt;br&gt;
advertised, and which one actually executes depends on how the client happened to&lt;br&gt;
build its dictionary. No error is raised. Nothing validates as broken. A refund&lt;br&gt;
runs where an order lookup was intended.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.cheelalabs.com%2Fblog%2Fcapability-naming%2Fimages%2F02-derivation-divergence.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogs.cheelalabs.com%2Fblog%2Fcapability-naming%2Fimages%2F02-derivation-divergence.svg" alt="One manifest advertising two capabilities that share a leaf segment, read by three clients. The client that takes the last segment maps both to the single name get, collapsing them silently. The client that replaces dots with hyphens keeps them distinct, which is what ADS-2 now requires. The client that takes the last two segments keeps them distinct here but is still truncating, which ADS-2 prohibits."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is also a length trap that has nothing to do with dots. &lt;code&gt;namespacedName&lt;/code&gt;&lt;br&gt;
permits 64-character segments with no limit on segment count, so a perfectly&lt;br&gt;
conformant name can exceed the 64-character tool-name ceiling on its own, before&lt;br&gt;
any transformation is applied.&lt;/p&gt;
&lt;h2&gt;
  
  
  What ADS-2 changed
&lt;/h2&gt;

&lt;p&gt;The fix is deliberately small. &lt;a href="https://github.com/Cheela-Labs/agent-discovery-spec/blob/main/proposals/0002-tool-invocable-capability-names.md" rel="noopener noreferrer"&gt;ADS-2&lt;/a&gt;&lt;br&gt;
adds one optional field, &lt;code&gt;invocationName&lt;/code&gt;, and — more importantly — makes the&lt;br&gt;
derivation rule normative so clients stop diverging.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.example.shop.orders.get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"invocationName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"orders-get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;invocationName&lt;/code&gt; must match &lt;code&gt;^[A-Za-z][A-Za-z0-9_-]{0,63}$&lt;/code&gt; and must be unique&lt;br&gt;
across the manifest. Note that underscores are permitted here and prohibited in&lt;br&gt;
&lt;code&gt;name&lt;/code&gt; — the two fields have genuinely different jobs, and no single string could&lt;br&gt;
have done both.&lt;/p&gt;

&lt;p&gt;When it is absent, a client that needs a constrained identifier &lt;strong&gt;must&lt;/strong&gt; derive&lt;br&gt;
one by replacing every &lt;code&gt;.&lt;/code&gt; with &lt;code&gt;-&lt;/code&gt;, and &lt;strong&gt;must not&lt;/strong&gt; derive one by truncating to&lt;br&gt;
a subset of segments.&lt;/p&gt;

&lt;p&gt;Truncation is prohibited rather than discouraged, and that distinction was the&lt;br&gt;
main thing worth arguing about. Discouraging it would have left the &lt;code&gt;orders.get&lt;/code&gt;&lt;br&gt;
/ &lt;code&gt;refunds.get&lt;/code&gt; collision legal. The failure it produces is silent, produces a&lt;br&gt;
wrong action rather than an error, and is invisible in the manifest — so it has&lt;br&gt;
to be a MUST NOT.&lt;/p&gt;

&lt;p&gt;If the derived identifier still exceeds 64 characters, the client must treat the&lt;br&gt;
capability as not invocable through that interface and should say why, rather&lt;br&gt;
than truncating to fit. Failing loudly beats guessing.&lt;/p&gt;

&lt;p&gt;One decision we are fairly confident about: &lt;code&gt;invocationName&lt;/code&gt; carries no identity.&lt;br&gt;
&lt;code&gt;name&lt;/code&gt; remains the sole identifier. Two manifests describing the same capability&lt;br&gt;
must agree on &lt;code&gt;name&lt;/code&gt;; they need not agree on &lt;code&gt;invocationName&lt;/code&gt;. Presentation&lt;br&gt;
concerns should not become identity, or you get two competing identifiers and the&lt;br&gt;
same divergence problem one layer up.&lt;/p&gt;

&lt;p&gt;Every manifest valid under 0.2.0 remains valid under 0.3.0. The field is&lt;br&gt;
optional, and the derivation rule describes what careful clients were already&lt;br&gt;
doing.&lt;/p&gt;
&lt;h2&gt;
  
  
  What this looks like in practice
&lt;/h2&gt;

&lt;p&gt;Our reference implementation serves 14 capabilities, each carrying both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;com.example.cheelashop.cart-add-item     →  cart-add-item
com.example.cheelashop.cart-clear        →  cart-clear
com.example.cheelashop.cart-remove-item  →  cart-remove-item
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The left column is identity, stable and namespaced. The right column is what&lt;br&gt;
gets handed to a model. They are related by an explicit rule rather than by each&lt;br&gt;
client's guess.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lesson we would like to have had earlier
&lt;/h2&gt;

&lt;p&gt;A discovery format is not consumed in isolation. It is consumed &lt;em&gt;by something&lt;/em&gt;,&lt;br&gt;
and that something has its own constraints — which are not negotiable just&lt;br&gt;
because your specification is elegant.&lt;/p&gt;

&lt;p&gt;We validated our names against our own schema. We did not validate them against&lt;br&gt;
the API that the overwhelmingly common use of a discovered capability leads&lt;br&gt;
straight to. The schema was correct and the design was wrong, and no amount of&lt;br&gt;
internal validation would have caught it. It took building a client and getting&lt;br&gt;
a 400.&lt;/p&gt;

&lt;p&gt;If you are writing a spec, the question is not only "is this internally&lt;br&gt;
consistent." It is "what will the first person to use this pipe it into, and what&lt;br&gt;
will that thing say."&lt;/p&gt;




&lt;p&gt;ADS is MIT-licensed and open to&lt;br&gt;
&lt;a href="https://github.com/Cheela-Labs/agent-discovery-spec/tree/main/proposals" rel="noopener noreferrer"&gt;proposals&lt;/a&gt;.&lt;br&gt;
If you think the derivation rule is wrong, or you have a case where prohibiting&lt;br&gt;
truncation causes more harm than it prevents, that is exactly the kind of&lt;br&gt;
disagreement the process exists for.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you want to try the spec end to end, &lt;a href="https://blogs.cheelalabs.com/agent-discovery-quickstart" rel="noopener noreferrer"&gt;implementing agent discovery takes about&lt;br&gt;
ten minutes&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agentdiscovery</category>
      <category>toolcalling</category>
      <category>openstandards</category>
      <category>ads</category>
    </item>
    <item>
      <title>Implement agent discovery in 10 minutes</title>
      <dc:creator>Viren Tanti</dc:creator>
      <pubDate>Wed, 05 Aug 2026 18:59:01 +0000</pubDate>
      <link>https://dev.to/virentanti/implement-agent-discovery-in-10-minutes-2cc3</link>
      <guid>https://dev.to/virentanti/implement-agent-discovery-in-10-minutes-2cc3</guid>
      <description>&lt;h1&gt;
  
  
  Implement agent discovery in 10 minutes
&lt;/h1&gt;

&lt;p&gt;By the end of this page you will have served a valid ADS manifest, validated it&lt;br&gt;
against the normative schema, and made one capability callable from an LLM&lt;br&gt;
tool-calling API. Ten minutes, a text editor, and Python — which you almost&lt;br&gt;
certainly already have.&lt;/p&gt;

&lt;p&gt;This is a quickstart for the &lt;a href="https://github.com/Cheela-Labs/agent-discovery-spec" rel="noopener noreferrer"&gt;Agent Discovery&lt;br&gt;
Specification&lt;/a&gt;, an open,&lt;br&gt;
MIT-licensed, vendor-neutral spec. Nothing here requires a product, an account,&lt;br&gt;
or a signup. If you want the reasoning behind any of it, the two deep dives&lt;br&gt;
linked at the bottom are where it lives — but read them &lt;em&gt;after&lt;/em&gt; this works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Current spec version: 0.3.0&lt;/strong&gt; (released as &lt;code&gt;v0.3.1&lt;/code&gt;). If you have seen an ADS&lt;br&gt;
example declaring &lt;code&gt;0.1.0&lt;/code&gt;, it predates two releases. Copy from here instead.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 1 — serve the smallest legal manifest
&lt;/h2&gt;

&lt;p&gt;Five fields. That is the entire requirement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ads-demo/.well-known &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;ads-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;.well-known/agent-discovery.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"specVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.3.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.example.bookshop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Example Bookshop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Example Inc."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"capabilities"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An empty &lt;code&gt;capabilities&lt;/code&gt; array is valid. "I speak ADS and I currently expose&lt;br&gt;
nothing" is a real, useful answer — it is how a client tells the difference&lt;br&gt;
between a system that has no capabilities and a system that has never heard of&lt;br&gt;
discovery.&lt;/p&gt;

&lt;p&gt;Serve it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; http.server 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a second terminal, fetch it the way a client would:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; http://localhost:8000/.well-known/agent-discovery.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;Content-type: application/json&lt;/code&gt; in the response headers. The spec&lt;br&gt;
requires it, and Python's dev server gets it right for free.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;http://&lt;/code&gt; and not &lt;code&gt;https://&lt;/code&gt;?&lt;/strong&gt; The spec says a client &lt;strong&gt;MUST NOT&lt;/strong&gt; fetch&lt;br&gt;
a manifest over plaintext HTTP — a manifest names endpoints an agent will&lt;br&gt;
subsequently call and the auth it will present, so over a rewritable channel it&lt;br&gt;
is a redirection primitive. The single exception is loopback (&lt;code&gt;localhost&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;127.0.0.0/8&lt;/code&gt;, &lt;code&gt;[::1]&lt;/code&gt;) during local development, where there is no network&lt;br&gt;
attacker. You are inside that exception. Everywhere else, use TLS.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;You have just performed agent discovery.&lt;/strong&gt; A GET to a predictable path. That is&lt;br&gt;
genuinely the whole mechanism.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 2 — validate it against the real schema
&lt;/h2&gt;

&lt;p&gt;This is where you find out whether your file is correct instead of hoping.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;jsonschema requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save as &lt;code&gt;validate.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;jsonschema&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Draft202012Validator&lt;/span&gt;

&lt;span class="n"&gt;SCHEMA_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://raw.githubusercontent.com/Cheela-Labs/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent-discovery-spec/v0.3.1/spec/schema/manifest.schema.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SCHEMA_URL&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;manifest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.well-known/agent-discovery.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Draft202012Validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;iter_errors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ Valid manifest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; → &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(root)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 validate.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That URL is pinned to a tag on purpose. Pointing a validator at &lt;code&gt;main&lt;/code&gt; means the&lt;br&gt;
thing you validate against can change under you between two runs; pin the&lt;br&gt;
version you are targeting and upgrade deliberately.&lt;/p&gt;

&lt;p&gt;There is no official &lt;code&gt;ads-validate&lt;/code&gt; CLI yet. Thirty lines of &lt;code&gt;jsonschema&lt;/code&gt; is the&lt;br&gt;
whole tool, which is roughly the point of keeping the schema small.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 3 — add a capability that does something
&lt;/h2&gt;

&lt;p&gt;An empty manifest is legal but boring. A capability needs three things: &lt;code&gt;name&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;version&lt;/code&gt;, &lt;code&gt;endpoint&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Replace the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"specVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.3.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.example.bookshop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Example Bookshop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Search the catalogue and check stock."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Example Inc."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"capabilities"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.example.searchBooks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"invocationName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"search_books"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Search the catalogue by title or author."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"inputSchema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"endpoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"transport"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.example.com/v1/books/search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"auth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"discovery"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"cacheTtlSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 validate.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;✅ Valid manifest&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Three fields there are worth understanding, because they are the ones people get&lt;br&gt;
wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;name&lt;/code&gt; must contain a dot.&lt;/strong&gt; It is a reverse-DNS identifier, and the schema&lt;br&gt;
enforces it. Drop the namespace and you get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ capabilities → 0 → name: 'searchBooks' does not match
   '^[A-Za-z][A-Za-z0-9-]{0,63}(\.[A-Za-z][A-Za-z0-9-]{0,63})+$'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The namespace is what stops your &lt;code&gt;searchBooks&lt;/code&gt; and someone else's &lt;code&gt;searchBooks&lt;/code&gt;&lt;br&gt;
from colliding the moment two manifests are merged into one agent's tool list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;invocationName&lt;/code&gt; must &lt;em&gt;not&lt;/em&gt; contain a dot.&lt;/strong&gt; This is the newest part of the&lt;br&gt;
spec (&lt;a href="https://github.com/Cheela-Labs/agent-discovery-spec/blob/main/proposals/0002-tool-invocable-capability-names.md" rel="noopener noreferrer"&gt;ADS-2&lt;/a&gt;,&lt;br&gt;
landed in 0.3.0) and it exists because of a genuine collision: OpenAI, Anthropic,&lt;br&gt;
Google and every OpenAI-compatible endpoint constrain tool function names to&lt;br&gt;
&lt;code&gt;^[a-zA-Z0-9_-]{1,64}$&lt;/code&gt;. A dot is rejected outright. So a conformant ADS &lt;code&gt;name&lt;/code&gt;&lt;br&gt;
could never be passed to any of them.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;invocationName&lt;/code&gt; is the identifier to use where &lt;code&gt;name&lt;/code&gt; cannot be. It is&lt;br&gt;
presentation only — &lt;code&gt;name&lt;/code&gt; remains the sole identity. If you omit it, a client&lt;br&gt;
that needs a constrained identifier must derive one by &lt;strong&gt;replacing dots with&lt;br&gt;
hyphens&lt;/strong&gt;, and is now forbidden from truncating to a subset of segments. That&lt;br&gt;
prohibition is not pedantry: two capabilities in one manifest can share a leaf&lt;br&gt;
segment, and a client that truncates maps both to the same tool name silently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;endpoint.auth&lt;/code&gt; is required&lt;/strong&gt;, even when it is &lt;code&gt;"none"&lt;/code&gt;. Delete the line and&lt;br&gt;
the validator says so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ capabilities → 0 → endpoint: 'auth' is a required property
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Making "no auth" an explicit statement rather than an omission is deliberate — a&lt;br&gt;
missing field is indistinguishable from a forgotten one, and an agent should&lt;br&gt;
never have to guess whether it needs a credential.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Break each of those three on purpose and run the validator.&lt;/strong&gt; Ninety seconds,&lt;br&gt;
and you will remember the rules for good. Then put them back.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 4 — hand it to an LLM
&lt;/h2&gt;

&lt;p&gt;Here is the payoff, and the reason &lt;code&gt;invocationName&lt;/code&gt; exists. This turns a manifest&lt;br&gt;
into a tool list an LLM API will actually accept:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;manifest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.well-known/agent-discovery.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# ADS-2: prefer invocationName; otherwise dots → hyphens. Never truncate.
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;invocationName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_schema&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;inputSchema&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cap&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;capabilities&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;transport&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;      &lt;span class="c1"&gt;# skip what you can't speak
&lt;/span&gt;    &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deprecated&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"search_books"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Search the catalogue by title or author."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"input_schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That array can be passed straight to a tool-calling API. You went from a URL to a&lt;br&gt;
usable tool list without knowing anything in advance about the system behind it.&lt;/p&gt;

&lt;p&gt;Note the &lt;code&gt;transport&lt;/code&gt; filter. The rule is &lt;strong&gt;skip what you do not understand, never&lt;br&gt;
reject the whole manifest&lt;/strong&gt;. A client that raises on an unrecognised transport&lt;br&gt;
breaks the first time anyone adds a capability it does not support — and per the&lt;br&gt;
spec, that is the client's bug, not the manifest's.&lt;/p&gt;




&lt;h2&gt;
  
  
  You are done
&lt;/h2&gt;

&lt;p&gt;You have a valid, current, tool-callable manifest. To put it in production:&lt;br&gt;
serve the same document at &lt;code&gt;/.well-known/agent-discovery.json&lt;/code&gt; over TLS, send&lt;br&gt;
&lt;code&gt;Access-Control-Allow-Origin: *&lt;/code&gt; if browser clients should see it, and keep&lt;br&gt;
&lt;code&gt;specVersion&lt;/code&gt; honest when you upgrade.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where to go next
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The reasoning&lt;/strong&gt; — &lt;a href="https://blogs.cheelalabs.com/agent-discovery-specification-ads-guide" rel="noopener noreferrer"&gt;Agent Discovery Spec: a .well-known for AI
agents&lt;/a&gt; is the deep dive:
worked examples, the versioning model, how ADS differs from MCP and OpenAPI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The gentler route&lt;/strong&gt; — &lt;a href="https://blogs.cheelalabs.com/agent-discovery-specification-explained" rel="noopener noreferrer"&gt;How AI agents find out what other systems can
do&lt;/a&gt; covers the same ground
assuming less.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The normative text&lt;/strong&gt; — &lt;a href="https://github.com/Cheela-Labs/agent-discovery-spec/tree/main/spec" rel="noopener noreferrer"&gt;the spec
itself&lt;/a&gt;,
seven documents, all short.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Now change it
&lt;/h3&gt;

&lt;p&gt;The most useful thing you can do next is disagree with something here.&lt;/p&gt;

&lt;p&gt;ADS is a 0.x draft. It is small, young, MIT-licensed with no CLA, and governed&lt;br&gt;
through a public proposal process modelled on Ethereum's EIPs — which makes it an&lt;br&gt;
unusually good first standards contribution. &lt;code&gt;invocationName&lt;/code&gt; exists because&lt;br&gt;
someone hit the dot problem and wrote it up. Ambiguity in the spec is a&lt;br&gt;
&lt;a href="https://github.com/Cheela-Labs/agent-discovery-spec/issues/new/choose" rel="noopener noreferrer"&gt;spec-bug issue&lt;/a&gt;.&lt;br&gt;
A change to how it works is a&lt;br&gt;
&lt;a href="https://github.com/Cheela-Labs/agent-discovery-spec/blob/main/proposals/0000-process.md" rel="noopener noreferrer"&gt;proposal&lt;/a&gt;.&lt;br&gt;
Both doors are open, and the second one is less intimidating than it sounds.&lt;/p&gt;

&lt;p&gt;If you serve a manifest anywhere public, open an issue and say so. A spec with&lt;br&gt;
one implementer is a design document; the implementations are what make it a&lt;br&gt;
standard.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://cheelalabs.com" rel="noopener noreferrer"&gt;Cheela&lt;/a&gt; is the first production implementer of ADS —&lt;br&gt;
its runtime registry publishes conformant manifests for every registered&lt;br&gt;
runtime. The spec does not depend on it, and nothing in this quickstart used it.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agentdiscovery</category>
      <category>ads</category>
      <category>capabilitymanifest</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
