Production update workflow, widget URIs, cache keys - best practices

Hi folks!

My app was approved last week and I hit publish on Monday - unfortunately it’s not working :(. Getting the classic “failed to fetch template” although its clear the data is loading as its referenced in the chat.

To make a short story long, as part of my build I bump the ui:// URIs to bust caches between deploys, not fully appreciating that those URIs are part of the locked submission metadata.

Now i have a version waiting for Review with zero functional changes, but am left wondering how i’m going to make this work long term.

The MCP server doc says to treat the ui:// URI as a cache key and version it on breaking changes.

The submission doc says all submitted info is locked once published; changes require resubmission.

The implied steady state seems to be one MCP server/URL simultaneously serving both approved and in development, as well as old/cached versions after approval.

I haven’t built this yet but… does this sound right? Is this what others are doing?

A few specifics:

1. Is “one server, multiple coexisting widget URI sets” the intended pattern? During a transition, would my server register resources for both the approved build’s URIs and the new build’s, resources/read resolves either, and only tools/list differentiates which is advertised?

2. The tools/list gate during review. If both versions coexist server-side, I assume I shouldn’t advertise both in tools/list simultaneously. Is the expected pattern to flip an env var around the “Scan Tools” click, or is there a documented way for the server to identify reviewer requests vs. live traffic?

3. Anyone gotten Next.js to work here? My server is Next.js 16. Widget HTML returned from resources/read references /_next/static/... chunks, which get regenerated on every next build - exactly the failure mode that broke my app. The only path I can see is inlining all CSS/JS into a self-contained HTML blob built with esbuild outside Next.js, at which point Next.js is only serving the MCP route and probably doesn’t belong here. And at which point i’ll trash NextJS from this equation because, … why use it? :slight_smile:

4. History rehydration on every deploy. Because my server only registers the current build’s URIs, every deploy retires the previous URIs for all users - old messages immediately start showing “Failed to fetch template” (gopi.ranganathan’s thread). Is this acceptable breakage on routine deploys, or is there a recommended retention window for retired URIs and their assets?

Really hoping I’m missing something fundamental, please point me in any direction you see me floundering, I’d appreciate it!!!

Bob

My build process versions the widget HTML but I keep all of the old widget versions around (well, at least for now - eventually will purge). That said, there were some other posts here about OAI relaxing the widget change process after feedback (cached for ~hours but not locked forever).

My build has widget.html which is what is advertised as the resource URI but it’s just a shell with the React mount root and then the CSS and JS are versioned so if you load widget.html, you always get the most recent version but since the old builds are kept, if you cached the contents, it would still render the older version.

Hope that helps.

In addition to the very helpful reply from @hunter_h here’s the link to the announcement of the updated submission flow for ChatGPT Apps.

Edit: Forgot to add link

Thanks for these responses.

Here is what worked for me. A stable URI in tools/list going forward, plus a ResourceTemplate catching legacy versioned URIs, all resolving to current widget HTML.

server.registerResource('show_venues', 'ui://widget/show-venues.html', {}, handler);

server.registerResource(

'show_venues-legacy',

new ResourceTemplate('ui://widget/show-venues-{version}.html', { list: undefined }),

{},

handler,

);

The template absorbs cached clients still pinned to old versioned URIs - this immediately fixed my production app.

Worth noting: my previous setup did bake a per-build version into every advertised URI. The clean version of that pattern — retain all old widget HTML and the static chunks each version references — is hard in Next.js (which is what I’m in). Every next build regenerates the _next/static/ hashes, so old HTML pointing at old chunks 404s after the next deploy. Stable URI + template catch was the pragmatic path.

My plan is when I make changes that aren’t backward-compatible (parameter shape, behavior), I’ll bump to a new URI, keep the old URI registered against the old handler, and only advertise the new one in tools/list. Old chats pinned to the old URI keep working with the old code; new chats get the new contract.

This is assuming tools/list is snapshotted at submission and only updated on approved resubmission… which I’m pretty sure is the case :wink: but if anyone knows otherwise please share!

Thanks again!