{"slug": "discovery-how-an-agent-finds-your-apis", "title": "Discovery: How an Agent Finds Your APIs", "summary": "Neander, a purpose-built language for agentic systems, makes API discovery a first-class language feature through its 'discover' verb. The language unifies finding and calling APIs into a single 'submitProgram' operation, eliminating the need for separate tool catalogs or documentation scraping. This design allows agents to discover available functions at runtime using the same mechanism as executing work.", "body_md": "[Last time](https://dev.to/newadventuresinit/neander-and-grotto-beyond-code-mode-21im) I made the case for why **Neander** and **Grotto** exist at all — a purpose-built, safe-by-construction language instead of a sandboxed general-purpose one. That was the argument for the whole language. From here, **Field Notes from the Grotto** takes it apart one feature at a time, and the first one is the feature that explains why the rest exists: discovery.\n\nClassical integration had a shape we all recognize: a developer reads one system's API documentation, then writes integration code that calls it in the right order with the right data. Two steps, one human. Make the calling system agentic — let it decide at runtime what it needs — and the human in the middle vanishes. The two steps do not. They have to land somewhere.\n\nThe writing step is the half everyone talks about: the agent writes the program instead of the developer. It is the *reading* step that gets skipped over. Before you can write a line against an API you have to find out what the API even is — what exists, what it takes, what it returns. That was the developer with a browser tab open on the documentation. When the developer leaves, that finding-out does not leave with them; something has to inherit it. That something is discovery.\n\nThe eager answer is the tool catalog: hand the agent every function definition up front and let it pick. That is discovery too — just total, and paid in advance. It does not scale. Hundreds of definitions clutter the context before the agent has done anything, every intermediate result piles on top, and latency and cost climb with them.\n\nTake the pragmatic route from my post [ Source Code as the Seam Between Systems](https://dev.to/newadventuresinit/source-code-as-the-seam-between-systems-5b1p) — a general-purpose language the model already writes fluently, its APIs behind a sandbox — and you inherit a quieter awkwardness. That language and those API packages were built for human developers, and human developers read documentation: a reference site, a PDF, a README, a page of generated typedocs. But a running agent is not a human developer. It may not have unrestricted access to the web to search for and reach that reference page at all — and even where it does, the page is shaped for a person: prose, worked examples, a layout to skim, none of it meant to be consumed by an agent. So every system that takes this route has to bolt something on — a meta-tool that lists the available functions, or a search endpoint the agent queries before it writes anything. It works. But it is an appendage: out of band, and reinvented for every stack.\n\nNeander makes discovery part of the language itself. `discover`\n\nis a verb you write into a program and submit exactly the same way you submit a program that does real work — and that is the unification worth stressing. In Neander, *everything is a program*. Finding out what exists and calling it are the same kind of act, in the same language, through the same entry point. The runtime exposes one operation to the agent — `submitProgram`\n\n— and every interaction, whether the agent is asking what is available or getting something done, flows through it. With this, the finding-out moved in-band. What the developer used to do with a browser tab, the agent now does with a program.\n\nAlmost everything in Neander exists to glue two verbs together. `call`\n\ninvokes one of the registered API functions. `discover`\n\nasks the runtime what there is to call.\n\n`discover`\n\nhas six forms — three things you can look for (namespaces, functions, documents) crossed with two ways to look (search a list, or get one by exact name):\n\n```\ndiscover namespaces [\"payment\"]              // [Namespace]  — search\ndiscover namespace  \"shipping\"               // Namespace?   — exact lookup of a namespace\ndiscover functions  ns [\"estimate\", \"intl\"]  // [Function]   — search the namespace's functions\ndiscover function   ns \"estimateBatch\"       // Function?    — exact lookup of a function\ndiscover documents  ns [\"format\"]            // [Document]   — search the namespace's documents\ndiscover document   ns \"requestFormat\"       // Document?    — exact lookup of a document\n```\n\nSearch terms are case-insensitive substrings, AND-combined, matched against each candidate's name and description. The empty list matches everything. That is the entire discovery surface.\n\nA cold agent does not know your API, so it works inward. The power lies in the sequential writing and execution of code. One program to list namespaces, a second to look for functions inside a namespace, a third to make the function call — reading and learning from the response envelopes the runtime returns as the result of every program submission.\n\n``` php\nneander 1 {\n  types {}\n  main -> [Namespace] {\n    return discover namespaces []\n  }\n}\n```\n\nThe runtime answers with a standard envelope — a `success`\n\nflag, the `result`\n\n, and a `meta`\n\nblock of telemetry. The return value of type `[Namespace]`\n\nis the fully serialized list of available namespaces:\n\n```\n{\n  \"success\": true,\n  \"result\": [\n    {\n      \"name\": \"bookings\",\n      \"description\": \"Booking management — retrieve, list, confirm, and escalate reservations for the hospitality backend\"\n    },\n    {\n      \"name\": \"guests\",\n      \"description\": \"Guest profile lookup — retrieve and list guests for the hospitality backend\"\n    },\n    {\n      \"name\": \"payments\",\n      \"description\": \"Payment processing — charge bookings, issue refunds, and look up prior charges\"\n    }\n  ],\n  \"meta\": {\n    \"thalersConsumed\": 1,\n    \"memoryConsumedKb\": 1,\n    \"durationMs\": 2,\n    \"apiCalls\": []\n  }\n}\n```\n\nWith this list in its context the agent can now further inspect the namespace of interest:\n\n``` php\nneander 1 {\n  types {}\n  main -> [Function] {\n    let ns: Namespace =? discover namespace \"bookings\"\n    return discover functions ns []\n  }\n}\n```\n\nThis time the result contains the list of available functions in this namespace, each function serialized in full (only one function shown here):\n\n```\n{\n  \"success\": true,\n  \"result\": [\n    {\n      \"qualifiedName\": \"bookings.get\",\n      \"description\": \"Get a booking by its numeric id\",\n      \"params\": {\n        \"id\": \"int\"\n      },\n      \"returnType\": \"bookings.Booking\",\n      \"errors\": {\n        \"404\": \"Booking not found\"\n      },\n      \"types\": {\n        \"bookings.Booking\": {\n          \"description\": \"A reservation record with id, fare amount, state, and the guest who placed it\",\n          \"fields\": {\n            \"id\": \"int\",\n            \"fare\": \"decimal(2, half_away)\",\n            \"state\": \"string\",\n            \"guestId\": \"int\"\n          }\n        }\n      }\n    }\n  ],\n  \"meta\": {\n    \"thalersConsumed\": 2,\n    \"memoryConsumedKb\": 1,\n    \"durationMs\": 3,\n    \"apiCalls\": []\n  }\n}\n```\n\nEvery function name listed is the exact string to write next — `qualifiedName`\n\nis what you pass to `call`\n\n, and the `types`\n\nside-table is transitively complete, so the shape of `bookings.Booking`\n\narrives with the function that returns it. The agent reads it straight out of the JSON response envelope, then writes the program that does the work:\n\n``` php\nneander 1 {\n  types {}\n  main -> decimal(2, half_away) {\n    let booking: bookings.Booking =? call bookings.get(id: 8821)\n    return booking.fare\n  }\n}\n```\n\nDiscover, call, return. Everything else in the language is detail.\n\nThe design choice worth pointing out is that the values `discover`\n\nreturns — handles of type `Namespace`\n\n, `Function`\n\n, `Document`\n\n— are *opaque*. A program can hold one and return it, but it cannot read its fields. The envelope contains what the runtime serializes when the discovery handle is *returned* from `main`\n\n. Inside the program the handle is a sealed token. Only on the way out does it become readable.\n\nWhat might sound like a restriction is, in fact, a guarantee. A discovery handle can only ever come *from* `discover`\n\n— you cannot forge one out of a record literal or return it from an API function. So a discovery handle the agent sees is always a handle the runtime minted, pointing at something the runtime actually registered. Discovery is not a convention, it is the only way in.\n\nDiscovery answers *what APIs exist* — but one question remains open: how does an agent discover discovery? The disadvantage of a new, purpose-built language like Neander is that a cold-start agent has never heard of it in stark contrast to any well-established general-purpose language. So before the agent can write even its first three-line namespace discovery program, it has to learn the language itself with nothing more at its disposal than the `submitProgram`\n\noperation. If you do not know the language, what is the only program you can write which is not guesswork?\n\nThe empty program, exactly!\n\nIn the meantime, read the [Neander](https://newadventuresinit.github.io/neander/) spec, embed [Grotto](https://github.com/newadventuresinit/grotto) in your own app, and let me know what you discovered.", "url": "https://wpnews.pro/news/discovery-how-an-agent-finds-your-apis", "canonical_source": "https://dev.to/newadventuresinit/discovery-how-an-agent-finds-your-apis-hh7", "published_at": "2026-07-07 03:32:50+00:00", "updated_at": "2026-07-07 04:28:38.828243+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence", "ai-agents"], "entities": ["Neander", "Grotto"], "alternates": {"html": "https://wpnews.pro/news/discovery-how-an-agent-finds-your-apis", "markdown": "https://wpnews.pro/news/discovery-how-an-agent-finds-your-apis.md", "text": "https://wpnews.pro/news/discovery-how-an-agent-finds-your-apis.txt", "jsonld": "https://wpnews.pro/news/discovery-how-an-agent-finds-your-apis.jsonld"}}