# Tools that come and go: handing agents the map

> Dynamic WebMCP tools keep the runtime tidy but hide the longer journey. Skills can hand agents a map - and Agentic Resource Discovery might help them find it.

*Published 2026-07-20 · 13 min read*

---

In <a href="/blog/agents-talking-to-each-other" target="_blank" rel="noopener">my last post</a> I dropped a little side note: *"I might experiment with exposing skills for WebMCP 🤔"*.

Because the more I play with WebMCP tools, the more I keep running into the same gap:

> A website knows its whole journey. A visiting agent only sees the tools registered right now. So how does it learn the rest?

Today I want to walk through that problem, a few ideas I've been testing against it, and a freshly announced **discovery proposal** that might hand us a useful piece of the answer 👇.

To make this less abstract, I've built **Forge Titan** - a small experiment where an agent assembles a robot using WebMCP tools. The whole demo is on GitHub: <RepoLink href="https://github.com/Kulikowski/webmcp-ard-exploration">Kulikowski/webmcp-ard-exploration</RepoLink> - so you can run every condition yourself. I'll use it as the running example and reveal the journey as we go 🤖.

<Note>
  **This is a simple experiment, not a benchmark.** A handful of runs per condition, few models, two predefined failures, simple agent harness. Treat everything below as field notes - In the real production scenario I would need to run every scenario many times, with different models and different harnesses to properly evaluate it. The discovered patterns were consistent enough that I think it's worth sharing anyway, and you can rerun everything yourself from the repository mentioned above. Always properly evaluate your tools!🙂
</Note>

<PostImage
  src="/forge-titan-screenshot.png"
  alt="The Forge Titan experiment running: on the left, the agent panel in 'Dynamic tools + catalog skill' mode with Anthropic Haiku 4.5, showing run stats - 31 model calls, 30 tool calls, 0 errors, 0 off-path, 0 unavailable, 48.5s - and a final agent message summarizing all five stations completed, ending with 'Final State: DEPLOYED'. On the right, a 3D view of the assembled robot standing on a grid, with a LIVE ROBOT STATE badge, a SHIPPED tag, and a 'Station 06 / 06 - Deployment' card underneath."
  width={1912}
  height={873}
  quality={90}
  caption="A completed 'Dynamic tools + catalog skill' run: the agent transcript with run stats on the left, the assembled and deployed robot on the right."
/>

## Tools that are always there, tools that come and go

<Note>
  **In this post I'm focusing on WebMCP's imperative API.** There's a declarative side too - the browser can synthesize tool definitions straight from `<form>` elements, no JavaScript needed (see the [declarative API explainer](https://github.com/webmachinelearning/webmcp/blob/main/declarative-api-explainer.md)).
</Note>

WebMCP tools are registered imperatively from page JavaScript - `document.modelContext.registerTool({...})` - and unregistered by aborting a signal you passed at registration time. That tiny API surface hides a real architectural choice:

1️⃣ **Static tools.** You register the same toolset on every page load, no matter where the user navigates or what state the assembly is in. `list_parts`, `mount_torso`, `deploy_titan` - always there, like a global API the page happens to host. Simple to build, easy for the agent to enumerate.

2️⃣ **Dynamic tools.** You register and unregister tools as the app moves - by **route**, or by **application state**. In Forge Titan, `reserve_part` appears after inventory inspection and `deploy_titan` only after the tests pass. The toolset reflects the UI state for agents.

Forge Titan makes the difference easy to see. Its assembly journey crosses six stations and more than twenty tools. In static mode the agent sees everything from `list_parts` to `deploy_titan` before the torso is even mounted. Dynamic mode opens the journey with just `list_parts` and `check_stock`; after the parts are inspected, `reserve_part` appears. Complete inventory and that surface is replaced by the structural assembly tools.

To be clear, this is not an either/or choice - it's per tool, not per site. In practice you'd *probably* mix the two: a small **stable core** that makes sense everywhere, with **state-gated tools** appearing and disappearing on top of it as the journey unfolds. Forge Titan does exactly that: a handful of workshop tools stay registered in every state, and the assembly tools come and go with the build.

Dynamic feels right, doesn't it? The tool list stays small, and each tool is intended to be useful in the current state. It mirrors how I think about building UIs - I don't expect a deploy button before the robot passes testing 🤷‍♂️.

There is an important engineering detail here: registration is **not** enforcement. State can change between discovery and invocation, so every tool still needs to validate permissions and preconditions when it runs. Hiding a tool keeps the surface relevant; it is not an authorization boundary.

But there is a catch - **with dynamic tools, the agent's visibility into tools shrinks to whatever is registered right now.**

## Planning without a map

Agents that work step by step usually run some flavor of the **ReAct** pattern (from [the paper that named it](https://arxiv.org/abs/2210.03629)): *reason* about the goal, *act* by calling a tool, *observe* the result, repeat. Each loop the agent picks its **next best action** from whatever tools it can currently see.

With static tools, the agent can at least attempt a plan upfront - all the puzzle pieces are on the table from step one.

With dynamic tools, it cannot see the full journey **from the tool list alone**. The tools for step five may not appear until steps one through four are done. Imagine a car navigation that only ever shows you the next turn - never the whole route 😅. The agent can still learn from the page, previous knowledge or tool results, but the current tool list gives it less information to plan with.

For a two-step flow? The agent will probably figure the route out as it goes. But for a really long journey - configure, validate, order, assemble, test - it starts to matter a lot. An agent *with the full route* can start with a plan. An agent *without it* discovers the route turn by turn, which I suspect means more model calls and more detours. That's exactly what I ended up testing - the results are near the end of this post 😀.

And Forge Titan hits an even harder version of this at the first station. `reserve_part` fails because there is no shoulder actuator bracket. The page tools describe the current assembly state correctly, but they cannot supply the missing part. That comes from a separate supplier outside the page - and no matter how far the agent progresses, the supplier will never show up in the page's tool list. That stop isn't just further down the route - it's not on this map at all 🤔.

## Could we publish a sitemap for tools?

My first instinct: if tools are keyed to routes, maybe we should publish a tools map? "On `/inventory` you'll find these tools, on `/bench` those." A *sitemap.xml for tools*. For route-keyed registration it might just work 😀.

But the moment tools depend on **application state** rather than navigation, the map breaks down. "This tool exists once the configuration is valid" isn't an address - it's a condition. You'd end up publishing a state machine, and at that point you're not writing a sitemap anymore, you're writing... documentation of the workflow.

And what about just going **static**? No map needed then - every tool is already sitting there, enumerable from the start. But that has a cost too. Take `deploy_titan` in static mode: it's registered from station one, long before the robot is even assembled. Call it early and nothing crashes - it just isn't relevant yet, there's nothing to deploy. The real cost isn't that call though, it's that most agent hosts send every registered tool definition to the model on every single turn, whether it's useful right now or not. So `deploy_titan` sits in the model's context from the very first station, tempting an early dead-end call, and adding one more option the agent has to sort through to find what's actually useful at this moment 😅.

So: dynamic tools starve the agent of the map, static tools fill its map with turns it can't take yet. Time to test a few ideas 🤔.

## A skill vs. a mega-tool

Here's the comparison I keep coming back to. Two very different ways to package the Forge Titan journey:

1️⃣ **One workflow tool.** `assemble_and_deploy_forge_titan()` - the site keeps orchestration under its own control. From the agent's perspective it starts with one call, and code can branch, retry, expose progress and recover from known failures very reliably. But the assembly still has to represent a missing part, an asynchronous supplier order, a failed test, partial completion and any moment where the operator needs to step in. The complexity doesn't disappear - it moves behind the tool boundary.

2️⃣ **A skill.** A document that the agent loads and follows using the smaller assembly tools. Now the agent has the **recipe upfront**: reserve the modules, assemble the frame, connect power, calibrate, test and deploy. If the bracket is missing, the Forge Titan skill tells it to stop retrying local tools, find the external supplier and wait for delivery before trying `reserve_part` again.

The useful knowledge looks roughly like this:

```md
1. Call `list_parts`, then `reserve_part`.
2. If the shoulder actuator bracket is unavailable, stop retrying local tools.
3. Discover the site's external capabilities.
4. Find the supplier and order the missing bracket.
5. Wait until the order is delivered.
6. Retry `reserve_part`.
```

None of that changes what `reserve_part` does. The skill gives the agent knowledge about what to do **around** the tool.

So this isn't *rigid code versus clever agent*. A workflow tool gives the site more control and can be the safer choice for tightly controlled work. A skill gives the visiting agent more orchestration responsibility and can be useful when the journey has many variations. One encapsulates the workflow; the other transfers knowledge about it 💡.

And remember where the sitemap idea ended: once the map needs to describe state gates, it starts turning into **workflow documentation** - which is exactly what a skill is. A state map describes **when capabilities become available**. A skill turns those conditions into **a procedure the agent can follow**, including what to do when the happy path breaks 💡.

## How does a skill reach the agent?

**André Bandarra** shows a clever pattern in his post [WebMCP tools as skills](https://bandarra.me/posts/webmcp-tools-as-skills): register a zero-argument tool whose *description* is the one-line summary and whose *return value* is the full recipe. Progressive disclosure through the tool channel 😎. The caveat he names himself: it's a convention, not a spec.

The first-class version of that ask is already sitting in the spec repo. In [issue #161 on WebMCP](https://github.com/webmachinelearning/webmcp/issues/161), **Idan Levin** proposes a `registerSkill()` API, framed as *"Tools can tell agents what a site can do, but Skills tell agents how to do it well"* - great framing in my opinion. Both links are worth your time.

The workflow knowledge stays the same while its delivery changes: a tool can load it on demand, a future first-class API could make its role explicit, and a catalog can hand it over upfront too - more on that next 👇.

## Agentic Resource Discovery - and the catalog that's already there

The Forge Titan skill can tell the agent *"find the supplier."* But where does that supplier live? I could hardcode an MCP URL into the recipe, but then workflow knowledge owns deployment information too. This is where the new ingredient becomes interesting.

The recently [announced Agentic Resource Discovery (ARD)](https://developers.googleblog.com/announcing-the-agentic-resource-discovery-specification/) is an open **v0.9 draft proposal** for answering: *where does the right capability live, who published it, and what trust information is available before I connect?* The full spec lives at [agenticresourcediscovery.org](https://agenticresourcediscovery.org) - here I'll stick to the two pieces this experiment needs.

A site publishes an `ai-catalog.json` - a manifest deployed at `https://{domain}/.well-known/ai-catalog.json`. **Registries** can ingest these catalogs and expose search APIs, so an agent can ask *"who can do X?"* across the ecosystem.

<Idea>
  ARD's headline story is the registry - a search engine for the agent that *hasn't found you yet*. But there's a second path too: **an agent can skip the registry and read a site's catalog directly**. Now connect that to **co-browsing - browsing a site together with my agent**. We're already on the website, and a catalog may be sitting at `/.well-known/ai-catalog.json`. One fetch can tell a compatible agent which resources the site advertises 😀.
</Idea>

Forge Titan's catalog currently advertises five resources: the supplier's MCP server card, three skills (the complete assembly journey, plus coolant maintenance and paint work the mission never needs), and an experimental description of the site's WebMCP surfaces. The skill explains **when** the supplier is needed. The catalog explains **where** to find it.

The catalog only provides pointers - following one still needs a matching client, network access and authentication.

The same file that makes a site *findable* by registries makes it *readable* for whoever already arrived. And the direct read isn't some hack - [the announcement](https://developers.googleblog.com/announcing-the-agentic-resource-discovery-specification/) itself says an agent can *"completely bypass search and directly fetch a catalog"* from a domain it already knows 💡.

Here is the mental model I find easiest to keep in my head:

- **WebMCP:** What can I do on this page right now?
- **Skill:** How do I complete the whole journey?
- **ARD:** Where do other capabilities I need live?

Page tools operate the assembly process, the skill carries the recipe, and ARD points to the supplier. Different layers, different jobs.

## What if WebMCP lived in the catalog too?

Which leads to the speculative bit - very much *me thinking out loud*, not anything the spec says today.

ARD's catalog entries are typed with media types, and the spec's examples already include MCP server cards, A2A agent cards and even an `application/ai-skill` type - so skills have a seat at this table today. The envelope appears able to carry an experimental newcomer too - call it `application/webmcp+json` - saying *"this site registers WebMCP tools; here are the surfaces, here's the skill that ties them together."* Adding a string is the easy part; real interoperability would still need a media type, schema, versioning and security rules.

**The catalog advertises that a WebMCP surface exists. The running page still decides which tools are available now.**

Two things could then fall out. For the **visiting agent**: one fetch answers "is this site worth driving with tools at all?", *before* committing to drive it turn by turn. For the **ecosystem**: a registry that understood this entry type could become **a searchable index of WebMCP-enabled websites** - runtime inspection would still tell the agent what is actually available now.

And I'm clearly not the only one whose thoughts drift toward a well-known path - that same [issue #161](https://github.com/webmachinelearning/webmcp/issues/161) floats a `.well-known/webmcp` manifest for a site's skills. Do we really want each protocol minting its *own* well-known file? My take is that this is the job ARD's typed catalog is aiming at - one `ai-catalog.json`, many entry types, each pointing at its protocol-native resource 🗺️.

There is one more hard problem hiding here: **version drift**. A catalog skill can outlive the tool names, routes or state model it describes. A real design needs a way to say which tool-surface version the skill understands, and agents need to treat a mismatch as a reason to stop and refresh rather than follow an old recipe.

## The Forge Titan experiment

<Idea>
The experiment records model calls, tool calls, rejected calls, off-path side calls, calls to tools that were not in the offered surface at all (hallucinated, or gone since the model last saw them), elapsed time, the tool surface sent on every iteration and the final assembly state.
</Idea>

Forge Titan compares **static or dynamic tools**, with **skill or no skill**.

One branch from earlier is missing on purpose: there is no `assemble_and_deploy_forge_titan()` mega-tool condition. I wanted to isolate what workflow *knowledge* does - the workflow *tool* deserves an experiment of its own 🙂.

1️⃣ **Static tools, no skill.** Every page tool is visible from the start - including the tools that are completely not needed for the current mission. 

2️⃣ **Dynamic tools, no skill.** The agent discovers the journey turn by turn.

3️⃣ **Static tools + catalog skill.** The same tools surface, but the recipe arrives before the first model call.

4️⃣ **Dynamic tools + catalog skill.** The live surface stays small while the full recipe arrives upfront.

The mission and two predefined failures stay the same in every run. Only the tool surface and the presence of workflow knowledge change.

One more twist keeps the comparison fair: the workshop can do more than one job. Two working **side paths** - coolant maintenance and paint work - are always registered, in both modes. Their calls succeed for real, each has its own skill in the catalog, and none of them advance the mission. Nothing in a tool description says *"you won't need this today"*. The assembly skill does 😀.

The pairs make the comparison more useful. The two static runs isolate what the recipe adds when the tool surface is noisy. The two dynamic runs isolate what the upfront recipe adds when the live surface stays small - and the side paths make sure even the small surface contains turns the agent should not take.

In catalog modes, the **agent harness** reads `/.well-known/ai-catalog.json` and resolves the assembly skill before the first model call.

<Note>
  **A skill is untrusted input.** In catalog mode the harness fetches a document from the web and hands it to the model as instructions to follow 🙃. In Forge Titan the skill happens to come from the same origin as the tools - the site describing itself - but that's not a reason to relax. It's still fetched live on every run, and an origin you trust today can get compromised tomorrow. A skill resolved through a third-party registry deserves the exact same treatment: know where it came from, that's useful for the audit trail, but keep the real protection at the tool boundary either way. Registration is not enforcement - and neither is a recipe.
</Note>

## Takeaways

Same mission, same predefined failures, four experiments. Here's what my runs looked like:

1️⃣ **Static tools, no skill.** Very hard to get through, and it took real steering from me to finish the journey at all. Every tool is on the table from the start, side paths included, and for a journey this long the agent simply doesn't know how to progress on its own. It calls stations that haven't unlocked, collects *"tool cannot be called in this state"* rejections, and burns its iterations recovering 😅.

2️⃣ **Dynamic tools, no skill.** Almost there. The small surface keeps it on rails, and most of the journey just works. But it needed my help up to two times per run - typed straight into the harness chat, the message box from the screenshot at the top: when `reserve_part` failed and it had no idea a supplier existed (that one happened almost every run), and sometimes again when the load test failed and it couldn't work out the recovery sequence.

3️⃣ **Static tools + catalog skill.** Very high success rate and clean transcripts. Notably, zero calls to unavailable tools - every name the model can reach for is registered, so there is nothing to hallucinate against.

4️⃣ **Dynamic tools + catalog skill.** Also very high success rate - with one interesting phenomenon 🤔. The model sometimes calls a tool that was registered a station ago and has since disappeared. The call bounces off, the agent reads the error, and the journey still completes.

Whether the recipe (skill) was present mattered far more than how the tools were registered. The two skill conditions both finished reliably; the two no-skill conditions split into "needed heavy steering to finish" and "needed an occasional nudge".

One fairness note: the recipe isn't free either. The skill text takes up context on every model call - the same cost I complained about for unused static tool definitions. In my runs it more than paid for itself, but it belongs in the same context budget 😀.

If you hand the agent no skill at all, in this case **dynamic registration seems to be the stronger default**: fewer tools, each one targeted at the current state, so the next best action is usually somewhere on the short list. The static no-skill runs had every capability in view and still couldn't find the route without me steering most of it by hand.

<Note>
It doesn't automatically mean that static tools without skills are worse. For simpler states and fewer dependencies between tools they work just fine! This blog registers static WebMCP tools 😀
</Note>

Dynamic registration shrinks the space for error - fewer tools, fewer wrong turns - but it introduces a failure mode of its own: the model's picture of the available tools goes **stale**, and it keeps reaching for a tool that is no longer there. In my runs that was recoverable noise, not a blocker. I suspect a **stronger agent harness** could deal with it better - it already refreshes definitions between model calls, and it could also catch a call that misses the current surface and tell the model what changed instead of returning a bare error.

In static nothing ever goes stale, so those hallucinated-availability calls didn't happen in my tests - but the surface answers *"what exists"* and never *"what's next"*, and without the recipe the agent stalls on a journey this size.

## Bonus: what about tool search?

One more pattern still matters outside this experiment: **tool search**. If a site keeps a large static toolset, deferred loading can keep most definitions out of the model's context until they are needed - roughly the pattern Anthropic describes in its [Tool Search Tool](https://www.anthropic.com/engineering/advanced-tool-use). That can reduce context cost and improve tool selection, but it doesn't teach the agent that ordering a missing part belongs before assembling the robot. Search helps the agent find a capability; a skill explains how that capability fits into the journey.

## Summary

So how does the visiting agent learn the rest?

The live **WebMCP tools tell the agent what it can do** now. A **skill gives the recipe** on how to handle complex workflows. And when a journey cannot be fulfilled by using the page tools alone, **ARD can point to what's possible!**

Exciting!

More soon. ✨
