# Make Your App Visible to AI

*Your app works great in Chrome and is completely invisible to every AI agent on the internet. Here are eleven fixes, all learned the hard way.*

By Brilliant Brain

We spent a week building the WellSpr.ing platform — a legal evaluation system with a public API, a revised Uniform Commercial Code, jurisdiction scorecards, and a full content library. The app worked beautifully in every browser. Then we asked Claude to read it.

It couldn't see anything. Every page returned an empty React shell. The API endpoints returned HTML instead of JSON. The llms.txt file threw a 500 error. The site was invisible to every AI agent on the internet.

We fixed all of it. Here's what we learned, distilled into eleven things every vibe coder should tell their build agent before shipping.

## 1. Your API Routes Must Resolve Before Your SPA Catch-All

This is the single most common reason AI agents can't read your app. Modern frameworks serve a single index.html for every route and let client-side JavaScript figure out what to render. Browsers execute the JavaScript and see your content. AI agents, search crawlers, and every other programmatic client get an empty div.

The fix is route order. Register all API routes and static file routes before the wildcard SPA route in your server configuration. API first, static files second, SPA catch-all last.

Test it: run curl against your API endpoint. If you get HTML back instead of JSON, your route order is wrong. We discovered this when Claude fetched our UCC document API and received the React hydration shell instead of 80KB of structured legal data.

## 2. Serve JSON When Asked for JSON

AI agents don't have browsers. They make HTTP requests and read the response body. If every endpoint returns HTML, the agent has to parse your markup to find the content — which is fragile, token-expensive, and often impossible.

Support content negotiation. When a client requests JSON via the Accept header, return JSON. But here's the critical insight we learned: many AI fetch tools can't set custom HTTP headers. They can only modify the URL. A query parameter like ?format=json is the most reliable way to ensure any client can get structured data.

This one workaround — adding ?format=json support — is what finally let Claude read our entire UCC document directly from the live API.

## 3. Add an llms.txt File

An llms.txt file sits at your site root and tells AI agents what your app does, what content is available, and how to access it programmatically. Think of it as robots.txt for AI — except instead of telling them what not to crawl, it tells them what's worth reading and how to get it.

Include a one-paragraph description of your app, a list of API endpoints with brief descriptions, links to key content, any authentication requirements, and technical notes about how to request JSON.

The critical implementation detail: serve it as a static file registered before any middleware. If your llms.txt route passes through session middleware, body parsers, or the SSR pipeline, non-browser clients will get errors. We learned this when our llms.txt returned a 500 to Claude despite working perfectly in every browser — the middleware stack was choking on requests without browser headers.

The formal spec is at llmstxt.org, but don't overthink it. A clear, useful plaintext file that actually loads is better than a perfectly formatted one blocked by middleware.

## 4. Don't Put Everything Behind Authentication

If every API endpoint requires an auth token, no AI agent can discover or sample your content. The agent's user may have credentials, but the agent itself often doesn't — especially during discovery, when it's trying to figure out what your app even offers.

Make read-only, public-facing content accessible without authentication. Save auth for write operations, private data, and premium features. A listing endpoint that returns published content should be open. A creation endpoint that writes data should require auth.

If your app is entirely private, at minimum make the llms.txt and a capabilities description publicly accessible so agents know what's behind the auth wall and can tell their users what's available.

## 5. Middleware Will Break Things You Don't Expect

Your app probably has a stack of middleware — body parsing, session management, CSRF protection, rate limiting, cookie handling, compression, error handlers. Each one assumes something about the incoming request. Non-browser clients violate those assumptions in ways that produce mysterious failures.

Session middleware tries to set a cookie, fails silently, and the request hangs. CSRF middleware rejects the request because there's no token. Body parser chokes on a request with no Content-Type header. The error handler catches an exception and returns an HTML error page instead of the actual error message.

For routes that AI agents need to reach — API endpoints, llms.txt, static data files — register them before the middleware stack. Or create a separate router with minimal middleware for machine-readable endpoints. Our llms.txt 500 error was finally resolved by making it one of the very first routes in the server, before any body parsing, session, or other middleware.

## 6. Return Structured Data, Not Prose

An AI agent can read your JSON and extract exactly what it needs in one pass. It cannot reliably extract structured information from a paragraph of narrative text embedded in an HTML page.

When designing API responses, return data with clear field names. A response with score, signal, verdict, and principle_scores as separate fields is instantly machine-readable. The same information written as a paragraph summary requires natural language parsing, which defeats the purpose of having an API.

This applies even to content-heavy responses. Our UCC document API returns every section as a structured object with citation, title, verdict, oneSentence, text, and councilAmendment as separate fields. Claude can filter, sort, and analyze 438 sections programmatically because each one is a data object, not a block of prose.

## 7. Make Your URLs Predictable and Meaningful

AI agents and their users construct URLs based on patterns they observe. If your URL structure is consistent and semantic, they can navigate your app by inference.

If an agent has seen /api/v1/scorecards/king-county-wa, it should be able to guess that /api/v1/scorecards/santa-clara-ca also exists. If it's seen /api/v1/blog/the-council-has-spoken, it should be able to guess the pattern for other posts. This only works if your slugs are human-readable and your path structure is consistent.

Opaque IDs, abbreviated slugs, and cryptic sub-paths make your API navigable only by clients that already have the full URL. Semantic URLs make it navigable by anyone — or any agent — that understands the pattern.

## 8. Your Error Responses Should Be JSON, Not HTML

When an API request fails, many frameworks render an HTML error page by default — the Express default error handler, Next.js error pages, framework-specific 500 templates. An AI agent receiving an HTML error page gets zero useful information about what went wrong.

Add a JSON error handler for your API routes. Return the HTTP status code, a message field with a meaningful description, and an error code if you have one. This seems minor but it transforms debugging from impossible to trivial — both for AI agents trying to use your API and for you trying to figure out why they can't.

Every 500 error we encountered during this build was harder to diagnose because the error responses were HTML. The moment we added JSON error responses to the API routes, the actual error messages became visible and fixable.

## 9. Add OpenAPI or JSON Schema Documentation

An AI agent arriving at your app for the first time has no idea what endpoints exist, what parameters they accept, or what responses they return — unless you tell it in a format it can parse.

Serve an OpenAPI spec at a known URL like /api/v1/openapi.json. This is a machine-readable description of your entire API — every endpoint, every parameter, every response schema. You don't have to write it by hand. Most frameworks have libraries that generate it from your route definitions, and your vibe-coding agent can generate it if you ask.

If your llms.txt references the OpenAPI spec URL, an AI agent can go from 'what does this app do' to 'how do I interact with every endpoint' in two fetches. WellSpr.ing also serves an AI plugin manifest at /.well-known/ai-plugin.json, which is an emerging standard for AI agent integration.

## 10. Test with curl, Not Just Your Browser

Your browser adds dozens of headers, handles redirects silently, executes JavaScript, manages cookies, follows CORS preflight flows, and renders error pages as visual content. None of this happens when an AI agent makes a request.

After deploying, test every public endpoint with curl. Does your API return JSON? Does your llms.txt serve plain text? Does your SSR return real content in the HTML body? What happens with a non-browser user agent? If any of these return HTML when you expected JSON, empty shells when you expected content, or errors when you expected data — that's what every AI agent sees.

We would have caught every issue in this list hours earlier if we had run curl against our endpoints before asking Claude to fetch them. The browser was lying to us — showing us a working app while the raw HTTP responses told a completely different story.

## 11. Give AI Agents Tools, Not Just Data

Everything up to this point makes your app *readable* by AI. This step makes it *usable*.

The Model Context Protocol (MCP) is an open standard that lets AI agents call functions on your server — not just fetch pages, but take actions. An agent connected to your MCP endpoint can search your database, run evaluations, pull statistics, and compose multi-step workflows without constructing URLs or parsing HTML. It's the difference between giving someone a book and giving them a workbench.

WellSpr.ing runs an MCP server at /mcp using the Streamable HTTP transport. It exposes tools for problem triage, institution search, platform statistics, and council verdict lookup. When Claude connects to it, Claude doesn't read our API documentation and then construct fetch calls — it calls named functions with typed parameters and gets structured results back. The friction between "what can this app do" and "do it" drops to zero.

The implementation is simpler than you'd expect. The MCP SDK handles the protocol layer. You define tools as functions with descriptions and parameter schemas — essentially the same work as writing API routes, but in a format that agents can discover, understand, and invoke without any documentation lookup. Streamable HTTP means it works over standard HTTPS with no special infrastructure.

The key architectural decision: MCP doesn't replace your REST API. It sits alongside it. Your API serves browsers, scripts, and agents that prefer HTTP. Your MCP server serves agents that prefer tool-calling. Same data, same logic, two interfaces. The agents that support MCP get a dramatically better experience. The ones that don't still have everything else on this list.

If you build one thing beyond the basics on this list, build an MCP endpoint. llms.txt tells agents what you have. OpenAPI tells agents how to fetch it. MCP lets agents *use* it. That's the progression — from invisible, to readable, to operational.

## The Meta-Lesson

Every item on this list comes down to one principle: your app has two audiences — humans with browsers and machines with HTTP clients. The browser audience gets JavaScript rendering, visual design, interactive elements, and friendly error pages. The machine audience gets raw HTTP responses, and whatever is in that response is all they will ever see.

Most modern web frameworks are optimized entirely for the first audience. Making your app visible to the second audience requires deliberate choices — serving JSON, registering routes before middleware, providing machine-readable documentation, exposing tool-calling interfaces, and testing without a browser.

The good news: these choices are small, specific, and can usually be implemented by telling your vibe-coding agent exactly what to do. This list is your prompt. Hand it to Replit, Cursor, Bolt, or whatever is building your app, and your app becomes visible to every AI agent on the internet.

The agents are here. They're trying to help your users. Let them in.

---

*Based on real debugging sessions between WellSpr.ing and Claude, February 2026. Every problem on this list was encountered and solved during the development of the WellSpr.ing platform.

wellspr.ing — Where noble ideas find their spring.*

— Ody the WellBuilder & Claude, MMXXVI
