AI very quickly became a full-fledged member of our team. It writes code, creates tests, refactors components, and increasingly, it is the first place we look instead of digging through documentation.
However, a rather brutal question arises: How is the model supposed to know what our architecture looks like and how to use our internal, domain-specific solutions correctly?
In our case, the answer evolved through several stages. From classic documentation and Storybook, through static rules and agents, all the way to implementing the Model Context Protocol (MCP). This wasn’t an overnight revolution. Rather, it was a series of stumbles and small steps, where each one solved a single problem… and immediately revealed the next one.
Starting Point: Storybook and TSDoc
In our project, we maintain an internal component library used by 24 independent modules. They are developed in separate repositories and embedded as microfrontends. The library lands in a private npm registry (Nexus), and developers use it like any other dependency.
We provided the team with the industry classics:
- Storybook: hosted at a private address.
- Technical documentation: maintained in TSDoc.
For humans, this solution worked perfectly. A developer would open Storybook, copy usage examples, and the IDE would politely suggest the API. Pure bliss.
AI enters the game (and eats up tokens)
Initially, after letting language models into our IDEs, not much changed in our projects. AI could scan the code in node_modules, read type declarations (.d.ts), TSDoc comments, and sometimes even analyze the underlying implementation. Code using our library was generated quite well.
Of course, AI had its weaker moments, too. Sometimes it hallucinated a non-existent component API or selected a variant marked as @deprecated. Usually, one or two iterations were enough to set the model straight.
The problem arose on a different level – over time, tokens started evaporating at an increasing rate. More powerful models eagerly sucked in larger contexts. With every task, even trivial ones, AI tried to understand the entire library from scratch:
- It analyzed thousands of lines of types.
- It searched through the documentation within the code.
- It compared implementations on its own.
The context token count for a simple fix could reach tens of thousands. It’s a bit like a chef reading the entire cookbook cover to cover every single time before frying scrambled eggs. It’s possible, but it has little to do with efficiency.
Step One: Rules
We started with a quick patch – implementing files with rules (Rules) for specific technologies and standards. In them, we described:
- Angular architecture,
- Code Style,
- Requirements for documentation and tests (JEST).
The effect was immediate. The model stopped guessing and started sticking to the established rigor.
Step Two: Specialized Agents
Later, instead of packing everything into one massive context, we created dedicated profiles:
- Component Library Agent: knew how to assemble the UI.
- Testing Agent: specialized in JEST and understood our equivalent of Page Objects (Component Harness) provided by the library.
The model only received what it needed for a specific slice of work. The quality of suggestions improved, but we fell into another trap.
The centralization problem (or a return to hell)
The dispersion of knowledge quickly made itself known. Each of the 24 repositories began living its own life, storing local copies of rules, agent prompts, and documentation snippets.
AI started generating different code depending on which repository it was run in. Moving this to Confluence was out of the question – documentation living far from the code dies slowly and in solitude.
Integrating the Model Context Protocol (MCP) seemed like the natural direction to take. The first, naive thought: Let the MCP server simply read our hosted Storybook and feed that to the model.
We quickly collided with reality, specifically with versioning. Not all microfrontends use the same library version. Older modules have older packages. If MCP always pulled data from the “latest” Storybook, AI would passionately suggest an API to developers that their library version didn’t support yet.
Add to that the performance issue – dozens of developers with an army of agents querying Storybook at every prompt is a simple recipe for an unintentional DDoS attack on our own infrastructure.
The final architecture: MCP as a knowledge distributor
Instead of building external entities, we used MCP as a precise interface to the knowledge embedded directly within the library.
The MCP server is published as a separate package in the internal Nexus npm Registry. As a result, each library version has a corresponding MCP server version. The project installs both dependencies and points to the server in the MCP client configuration (e.g., in VS Code or another IDE). In practice, this means AI always communicates with the server associated with the library version used by that specific project.
This solves the version mismatch problem. If two projects use different library versions, each runs its own instance of the MCP server and receives documentation and APIs strictly matching the locally installed version. There is no risk of the model generating code based on newer or older documentation.
Our server exposes, among other things:
- Resources: Markdown files containing project and architectural rules, including previously prepared Agents and Rules, provided to the model as a source of knowledge.
- Tools: A component search engine based on keywords.
In practice
How does it work in practice? AI no longer analyzes the entire type library. When the model recognizes that it needs, for example, a Modal component, it calls the search_component(“modal”) tool. In response, it receives structured data containing the component name, import path, API signature (props, types, and available variants), usage examples, and links to related resources and best practices.
As a result, the model operates on a precise, up-to-date context that matches the library version installed in the project, rather than analyzing thousands of lines of TypeScript definitions. The result is more accurately generated code while simultaneously reducing the number of tokens used.
Conclusions: Single Source of Truth
The biggest winner isn’t token savings at all. That’s a nice bonus. In our initial tests, the number of tokens consumed dropped by about 30%. The real game-changer, however, is that knowledge is back in one place.
Updating standards or adding new component documentation happens once – in the library repository. The MCP server makes these changes available immediately to all consumers. As a result, all repositories that use the same library version automatically receive the updated context without needing to manually propagate changes (e.g., updating .cursor files across multiple projects).
The MCP model, published as a separate package in the internal Nexus npm Registry, eliminates the need to maintain a central, hosted MCP server. All logic and data are delivered as part of the project dependencies. AI uses a locally run MCP instance that reads files and resources installed in a specific library version. This simplifies the architecture and reduces infrastructural costs because there is no separate server environment or intermediary API layer.
No central hosting means no costs for scaling, server maintenance, or handling API traffic – communication happens locally within the project’s context.
Is MCP a silver bullet? No.
- It won’t replace well-thought-out documentation.
- It won’t save a tragically designed API.
But it solves the problem of context delivery phenomenally. MCP is simply the next evolution of documentation. We used to write it only for humans. Later, AI models tried to read it over our shoulders. Today, thanks to structured protocols, both worlds can finally make optimal use of the same source of truth.
Leave a comment