Context is the scarce resource — and your AI still can't see your website A developer building an AI-native platform argues that context, not model quality, determines LLM output, and describes shipping two features to improve it: trimming an MCP server's tool count from 59 to 43, and adding a feature that makes existing website context visible to AI assistants. The developer notes that tool sprawl taxes context windows and that the leaner tool surface outperformed the larger one in internal tests. Part 2 of a series on building an AI-native platform. This one is about the thing that actually determines output quality — and a feature we shipped because of it. Code is deterministic. Same input, same output, every time. That property is the whole reason we can test it, cache it, and reason about it. AI is not that. Same prompt, different answer, and no amount of shouting in capital letters changes the underlying fact that you're sampling from a distribution. If you've built anything real on top of an LLM, you already know this in your bones: the demo works, the eval is flaky, and the difference between "ship it" and "delete it" often comes down to something you can't quite point at. So here's the question that matters for anyone building with these models: if the model doesn't guarantee quality, where does quality come from? The honest answer, after a couple of years of building on this stuff, is context. Not prompt-engineering folklore. Not a marginally smarter model. How much of the actual situation the model can see at the moment it answers. The clearest way I know to feel this: take the same task and run it twice. Run A — no context "Add pagination to the product list." Run B — same task, repo in context "Add pagination to the product list." ...with the actual ProductList component, the API client, and the existing useProducts hook in the window. Run A gives you a plausible, generic answer that assumes a framework you're not using and an API shape you don't have. Run B gives you a diff you can apply. Same model, same temperature, same prompt string. The only thing that changed is what it could see. That's not a subtle effect. In practice it's most of the difference between output you ship and output you throw away. And it has a property that quietly governs everything downstream: context is finite. There's a budget. Every token you spend on one thing is a token you didn't spend on another. Once you accept that, there are exactly two levers for better results. We shipped both, and the second one is the feature this post is really about. We expose an MCP server. At one point it had 59 tools. It now has 43, and we intend to keep it lean. That reduction was counterintuitive to argue for internally, because on a feature page "more tools" reads as "more capable." But watch what actually happens in a session. Every tool an MCP client loads is a schema — a name, a description, a parameter list — and all of it goes into the context window before the model does anything . Fifty-nine tools is a menu the model reads end to end on every turn, whether or not the task needs any of them. Tool sprawl looks like capability and behaves like noise. Trim the surface to the tools that carry their weight, merge the overlapping ones, and the model spends its attention on your project instead of on your API reference. We measured the effect the boring way — running the same build tasks across models before and after — and the lean surface came out ahead on every one of them. If you're shipping an MCP server, this is the lever nobody talks about: your tool count is a tax your users' models pay on every call. Design it like context is expensive, because for the thing consuming it, it is. Here's the thing that quietly annoyed me for a year. For anyone with an existing website, the single densest piece of context they own has been completely unreachable to their AI. Your information architecture. Your copy, in your voice. Your visual system. The hundred small decisions you made and then forgot you made. All of it sitting on a server, and none of it visible to the assistant you're asking for help. So the industry's advice became: start over. Open a blank prompt, describe your site from memory, and hope the result resembles what you already had. That's not a fresh start. It's a lossy re-encode of something that already existed, run through a narrow text channel. You re-litigate decisions you'd already settled, and you silently lose the ones you can't remember making. For a category of tool whose entire value proposition is use your own words , telling people to throw away their most contextful asset was backwards. So we built the importer. You give it the URL of a site you own; it produces a real, editable project on the platform, and then you build on top of it with whatever assistant you already use. The naive way to clone a site is to flatten everything into one directory and rewrite every reference to match the new layout. It works, but it's a rewrite bomb: every href , every src , every url in CSS, every path buried in a JSON blob has to be found and mutated, and each mutation is a chance to break something. We took the other road. Pages get clean URLs — about.html becomes /about , contact.php becomes /contact , trailing slashes get normalised — because those are the URLs a human will see and share. But same-origin assets keep their original paths. The stylesheet that referenced ../../uploads/2019/hero.jpg still references exactly that, because that file still lives at exactly that path. The payoff is that the rewriter shrinks to almost nothing. Instead of remapping every reference, you're doing host-level normalisation and a small set of page-slug rewrites. Three passes: https://site.com , //site.com , and same-origin absolute paths to one internal form. .html / .php → clean URL , applied longest-match-first so a longer path never gets clipped by a shorter one that's a prefix of it. robots directives, its canonical tags, platform-injected boot scripts. Fewer rewrites, fewer edge cases, fewer ways to end up with a 99%-imported i.e. broken site.The concept is a weekend. The reason it's not a weekend is that the web is thirty years of accumulated weirdness, and a site that imports 99% correctly is a broken site. A sampling of what actually decides whether this works: Escaped JSON-LD. WordPress + Yoast emits structured data with escaped slashes: {"@type":"CoffeeShop","url":"https:\/\/site.com\/"} A rewriter that only understands href="..." walks straight past that URL and leaves a dead reference inside your schema graph. You have to parse the escaped form. Extension vs. DOM context. A file can lie about what it is.