AI bardzo szybko stało się pełnoprawnym członkiem naszego zespołu. Pisze kod, tworzy testy, refaktoryzuje komponenty i coraz częściej jest pierwszym miejscem, do którego zaglądamy, zamiast przekopywać się przez dokumentację.
Pojawia się jednak dość brutalne pytanie: Skąd model ma wiedzieć, jak wygląda nasza architektura i jak poprawnie używać naszych wewnętrznych, specyficznych rozwiązań?
W naszym przypadku odpowiedź ewoluowała przez kilka etapów. Od klasycznej dokumentacji i Storybooka, przez statyczne reguły i agentów, aż po wdrożenie Model Context Protocol (MCP). To nie była rewolucja z dnia na dzień. Raczej seria potknięć i małych kroków, z których każdy rozwiązywał jeden problem… i natychmiast ujawniał kolejny. Przyjrzymy się temu bliżej.
Punkt wyjścia: Storybook i TSDoc
W projekcie utrzymujemy wewnętrzną bibliotekę komponentów używaną przez 24 niezależne moduły. Są one rozwijane w osobnych repozytoriach i osadzane jako mikrofrontendy. Biblioteka ląduje w prywatnym rejestrze npm (Nexus), a deweloperzy korzystają z niej tak jak z każdej innej zależności.
Do dyspozycji zespołu oddaliśmy klasykę gatunku:
- Storybook: hostowany pod prywatnym adresem.
- Dokumentację techniczną: utrzymywaną w TSDoc.
Dla ludzi to rozwiązanie sprawdzało się świetnie. Programista otwierał Storybooka, kopiował przykłady użycia, a IDE grzecznie podpowiadało API. Sielanka.
AI wchodzi do gry (i zjada tokeny)
Początkowo po wpuszczeniu modeli językowych do IDE niewiele się zmieniło w naszych projektach. AI potrafiło skanować kod w node_modules, czytać deklaracje typów (.d.ts), komentarze TSDoc, a czasem nawet analizować implementację pod spodem. Kod z użyciem naszej biblioteki generował się całkiem nieźle.
Oczywiście, AI miało też swoje słabsze momenty. Czasem halucynowało nieistniejące API komponentu albo wybierało wariant oznaczony jako @deprecated. Zazwyczaj jedna lub dwie iteracje wystarczały do naprostowania modelu. Problem pojawił się na innej płaszczyźnie – z biegiem czasu tokeny zaczęły wyparowywać w coraz szybszym tempie. Coraz mocniejsze modele chętniej zasysały większy kontekst.
Przy każdym, nawet banalnym zadaniu, AI próbowało od nowa zrozumieć całą bibliotekę:
- Analizowało tysiące linii typów.
- Przeszukiwało dokumentację w kodzie.
- Porównywało implementacje na własną rękę.
Liczba tokenów kontekstu przy prostej poprawce potrafiła sięgać dziesiątek tysięcy. To trochę tak, jakby przed usmażeniem jajecznicy kucharz za każdym razem czytał całą książkę kucharską od deski do deski. Da się, ale z efektywnością ma to niewiele wspólnego.
Krok pierwszy: Rules
Zaczęliśmy od szybkiej łaty – wdrożenia plików z regułami (Rules) dla poszczególnych technologii i standardów. Opisaliśmy w nich:
- Architekturę Angulara,
- Code Style,
- Wymagania dla dokumentacji i testów (JEST).
Efekt był natychmiastowy. Model przestał zgadywać i zaczął trzymać się ustalonego rygoru.
Krok drugi: Wyspecjalizowani Agenci
Później, zamiast pakować wszystko w jeden ogromny kontekst, stworzyliśmy dedykowane profile:
- Agent ds. biblioteki komponentów: Wiedział, jak składać UI.
- Agent ds. testów: Specjalizował się w JEST oraz rozumiał nasz odpowiednik Page Objects (Component Harnes) dostarczany przez bibliotekę.
Model dostawał tylko to, czego potrzebował do danego wycinka pracy. Jakość podpowiedzi wzrosła, ale wpadliśmy w kolejną pułapkę.
Problem centralizacji (czyli powrót do piekieł)
Rozproszenie wiedzy szybko dało o sobie znać. Każde z 24 repozytoriów zaczęło żyć własnym życiem, przechowując lokalne kopie rules, promptów dla agentów oraz wycinków dokumentacji.
AI zaczęło generować różny kod w zależności od tego, w którym repozytorium zostało uruchomione. Przeniesienie tego do Confluence nie wchodziło w grę – dokumentacja żyjąca z dala od kodu umiera powoli i w samotności.
Naturalnym kierunkiem wydawało się zintegrowanie Model Context Protocol (MCP). Pierwsza, naiwna myśl: Niech serwer MCP po prostu czyta naszego hostowanego Storybooka i karmi tym model.
Szybko zderzyliśmy się z rzeczywistością, a konkretnie z wersjonowaniem. Nie wszystkie mikrofrontendy korzystają z tej samej wersji biblioteki. Starsze moduły mają starsze paczki. Gdyby MCP zawsze ciągnął dane z „najnowszego” Storybooka, AI namiętnie proponowałoby deweloperom API, którego ich wersja biblioteki jeszcze nie obsługuje.
Do tego dochodzi kwestia wydajności – kilkudziesięciu deweloperów z armią agentów odpytujących Storybooka przy każdym prompcie to prosty przepis na niezamierzony atak DDoS na naszą własną infrastrukturę.
Ostateczna architektura: MCP jako dystrybutor wiedzy
Zamiast budować zewnętrzne byty, wykorzystaliśmy MCP jako precyzyjny interfejs do wiedzy zaszytej bezpośrednio w bibliotece.
Serwer MCP jest publikowany jako osobny pakiet w wewnętrznym Nexus npm Registry. Dzięki temu każda wersja biblioteki ma odpowiadającą jej wersję serwera MCP. Projekt instaluje obie zależności i wskazuje serwer w konfiguracji klienta MCP (np. w VS Code lub innym IDE).
W praktyce oznacza to, że AI zawsze komunikuje się z serwerem powiązanym z wersją biblioteki używaną przez dany projekt.
To rozwiązuje problem rozjazdu wersji. Jeśli dwa projekty korzystają z różnych wersji biblioteki, każdy uruchamia własną instancję serwera MCP i otrzymuje dokumentację oraz API zgodne wyłącznie z lokalnie zainstalowaną wersją. Nie ma ryzyka, że model wygeneruje kod na podstawie nowszej lub starszej dokumentacji.
Nasz serwer wystawia m.in.:
- Resources: Pliki Markdown zawierające reguły projektowe i architektoniczne, w tym wcześniej przygotowane Agents oraz Rules, udostępnione modelowi jako źródło wiedzy.
- Tools: Wyszukiwarkę komponentów po słowach kluczowych.
W praktyce
Jak to działa w praktyce? AI nie analizuje już całej biblioteki typów. Gdy model rozpozna, że potrzebuje np. komponentu Modal, wywołuje narzędzie search_component(„modal”). W odpowiedzi otrzymuje ustrukturyzowane dane zawierające nazwę komponentu, ścieżkę importu, sygnaturę API (propsy, typy i dostępne warianty), przykłady użycia oraz odnośniki do powiązanych zasobów i dobrych praktyk.
Dzięki temu model operuje na precyzyjnym, aktualnym kontekście zgodnym z wersją biblioteki zainstalowaną w projekcie, zamiast analizować tysiące linii definicji TypeScript. Efektem jest bardziej trafnie generowany kod przy jednoczesnym zmniejszeniu liczby wykorzystywanych tokenów.
Wnioski: Single Source of Truth
Największym wygranym nie jest wcale oszczędność tokenów. To miły dodatek. W pierwszych testach liczba zużywanych tokenów spadła o około 30%. Prawdziwym game changerem jest jednak to, że wiedza znów jest w jednym miejscu.
Zaktualizowanie standardów albo dodanie nowej dokumentacji komponentu odbywa się raz – w repozytorium biblioteki. Serwer MCP udostępnia te zmiany natychmiast wszystkim konsumentom. W efekcie wszystkie repozytoria korzystające z tej samej wersji biblioteki automatycznie otrzymują aktualny kontekst bez konieczności ręcznego propagowania zmian (np. aktualizacji plików .cursor w wielu projektach).
Model MCP, publikowany jako osobny pakiet w wewnętrznym Nexus npm Registry, eliminuje potrzebę utrzymywania centralnego, hostowanego serwera MCP. Cała logika oraz dane są dostarczane jako część zależności projektu. AI korzysta z lokalnie uruchomionej instancji MCP, która odczytuje pliki i zasoby zainstalowane w konkretnej wersji biblioteki. To upraszcza architekturę i ogranicza koszty infrastrukturalne, ponieważ nie ma osobnego środowiska serwerowego ani pośredniczącej warstwy API.
Brak centralnego hostingu oznacza brak kosztów skalowania, utrzymania serwerów oraz obsługi ruchu API – komunikacja odbywa się lokalnie w kontekście projektu.
Czy MCP to lek na całe zło? Nie.
- Nie zastąpi dobrze przemyślanej dokumentacji.
- Nie uratuje tragicznie zaprojektowanego API.
Ale fenomenalnie rozwiązuje problem dostarczania kontekstu. MCP to po prostu kolejna ewolucja dokumentacji. Kiedyś pisaliśmy ją tylko dla ludzi. Później modele AI próbowały ją czytać przez ramię. Dzisiaj, dzięki ustrukturyzowanym protokołom, oba światy mogą w końcu optymalnie korzystać z tego samego źródła prawdy.
Zostaw komentarz