MCP-Server — Doku-Lookup im Editor

MCP-Server — Doku-Lookup im Editor

Artikel 7 · Serie: Agentic Coding mit Claude Code

v0.6 funktioniert. Drei Visualisierungen, ein Datensatz, ein Switcher. An diesem Punkt entsteht eine andere Art von Problem: jede Erweiterung bringt Libraries ins Spiel, die Claude Code aus dem Gedächtnis kennt, aber vielleicht nicht in ihrer aktuellen Version. Das ist kein Vorwurf, sondern ein strukturelles Problem. Wer shadcn/ui einführen will, bekommt von einem Sprachmodell mit Trainings-Cutoff möglicherweise Prop-Signaturen aus einer älteren Version. Wer Tailwind 4 nutzt, bekommt vielleicht Tailwind-3-Syntax. Das Werkzeug dagegen heißt MCP.

Was MCP ist und was es nicht ist

MCP steht für Model Context Protocol. Ein MCP-Server ist ein externer Prozess, den Claude Code zur Laufzeit ansprechen kann, um Daten zu lesen, APIs aufzurufen oder Werkzeuge auszuführen. Im Unterschied zu einem Skill definiert ein MCP-Server nicht, wie eine Aufgabe anzugehen ist, sondern was für externe Fähigkeiten verfügbar sind.

Der Unterschied in der Praxis: ein Skill sagt Claude Code „bei einer Parser-Aufgabe diese Schritte ausführen". Ein MCP-Server sagt Claude Code „wenn du aktuelle Doku zur D3-Library brauchst, frag mich". Der Skill beschreibt Ablauf und Strategie. Der MCP-Server liefert Daten oder führt Aktionen aus.

Zwei MCP-Server kommen in Art 7 zum Einsatz:

  • Context7: holt aktuelle Library-Dokumentation zu Tausenden von Packages direkt in den Editor-Kontext. Claude kann fragen, ohne zu raten.
  • Playwright: steuert einen echten Browser. Für Art 8 vorbereitet, hier nur installiert.

Plan-File für Art 7

Task 1 (sequenziell): .mcp.json einrichten
  Context7 und Playwright als stdio-Server konfigurieren.
  Die Datei kommt ins Repo, damit alle, die das Projekt klonen, dieselbe MCP-Konfiguration bekommen.

Task 2 (nach Task 1): shadcn/ui einführen
  npx shadcn init, dann Tabs, Button und Card hinzufügen.
  App.tsx migrieren: selbstgebauten Switcher durch shadcn-Tabs ersetzen.

Task 3 (parallel zu Task 2): chart-builder Skill
  Wissen aus den drei bestehenden Charts verdichten.
  Skill ruft Context7 für aktuelle D3-Doku auf.

Task 1: .mcp.json ins Repo

Der erste Schritt ist die einfachste Datei:

Erstelle eine .mcp.json im Repo-Root.
Zwei Server: Context7 für Library-Doku und Playwright für Browser-Steuerung.
Die Datei soll ins Repo, nicht in eine globale User-Konfiguration.

Das Ergebnis:

{
  "mcpServers": {
    "context7": {
      "command": "npx",
      "args": ["-y", "@upstash/context7-mcp"]
    },
    "playwright": {
      "command": "npx",
      "args": ["-y", "@playwright/mcp@latest"]
    }
  }
}

Wichtig: die Datei gehört ins Repo. Wer MCP-Server nur in ~/.claude.json einträgt, hat sie auf seinem eigenen Rechner, aber andere Projektmitglieder, neue Entwicklungsumgebungen oder CI sehen nichts davon. .mcp.json im Repo-Root ist die richtige Stelle.

.playwright-mcp/ kommt in .gitignore, da der Playwright-Server dort Session-Artefakte ablegt, die nicht ins Versionssystem gehören.

Was Context7 konkret macht

Vor der shadcn/ui-Migration stellen wir Context7 kurz unter Beweis. Der Prompt:

Ich will shadcn/ui in ein Vite-Projekt mit Tailwind 4 einführen.
Bitte hol dir über Context7 die aktuelle Installationsanleitung.

Claude Code ruft dann mcp__context7__resolve-library-id auf, sucht die aktuelle shadcn-Library-ID, und holt anschließend mit mcp__context7__query-docs die relevanten Abschnitte. Was zurückkommt, ist die Doku der tatsächlich installierten Version, nicht das, was zum Trainings-Zeitpunkt gültig war.

Das Ergebnis in unserem Fall: shadcn läuft unter Tailwind 4 mit dem -b radix-Flag, nicht mit einer Legacy-Konfiguration. Ohne Context7-Lookup wäre hier Raten angesagt gewesen.

Context7 ist kein Ersatz für IDE-Autocomplete bei stabilen APIs. Für Array.prototype.map oder useState ist es Overkill. Sinnvoll wird es dort, wo Claude erfahrungsgemäß rät: neue Major-Versionen, Libraries mit häufigen API-Brüchen, Plugin-spezifische Konfiguration.

Task 2: shadcn/ui einführen

shadcn/ui ist keine NPM-Library im üblichen Sinne. Es gibt kein npm install @shadcn/ui. Stattdessen kopiert eine CLI die Komponenten-Quellen direkt ins Projekt. Die Komponenten werden mit-versioniert, lassen sich anpassen und gehören dem Projekt.

Führe shadcn/ui in web/ ein: npx shadcn init.
Füge dann Tabs, Button und Card hinzu.
Migriere App.tsx: der selbstgebaute Switcher mit Tailwind-Buttons kommt raus,
stattdessen kommen shadcn-Tabs rein.

Nach npx shadcn@latest init und dem Hinzufügen der Komponenten:

npx shadcn@latest add button
npx shadcn@latest add tabs
npx shadcn@latest add card

liegen in web/src/components/ui/ drei fertige Komponenten, die sofort importiert werden können. shadcn legt außerdem src/lib/utils.ts mit dem cn()-Hilfs-Helper an, der clsx und tailwind-merge zusammenführt.

Die Migration von App.tsx ist ein direkter Austausch. Die bisherigen manuell gestylten Buttons:

<nav role="tablist" className="inline-flex rounded-md border ...">
  {VARIANTS.map((v) => (
    <button role="tab" aria-selected={variant === v.key}
      className={`px-4 py-1.5 ${variant === v.key ? "bg-stone-900 text-white" : "..."}`}
    >
      {v.label}
    </button>
  ))}
</nav>

werden durch shadcn-Komponenten ersetzt:

<Tabs value={variant} onValueChange={(v) => setVariant(v as Variant)}>
  <TabsList>
    <TabsTrigger value="sunburst" data-testid="switch-sunburst">Sunburst</TabsTrigger>
    <TabsTrigger value="sankey"   data-testid="switch-sankey">Sankey</TabsTrigger>
    <TabsTrigger value="treemap"  data-testid="switch-treemap">Treemap</TabsTrigger>
  </TabsList>
  <TabsContent value="sunburst"><Sunburst titel={data.titel} /></TabsContent>
  <TabsContent value="sankey"><Sankey titel={data.titel} /></TabsContent>
  <TabsContent value="treemap"><Treemap titel={data.titel} /></TabsContent>
</Tabs>

Der Unterschied: shadcn-Tabs bringen Keyboard-Navigation, korrekte ARIA-Attribute und konsistentes Styling ohne zusätzliche Konfiguration. Der Overhead ist minimal, der Gewinn in Accessibility sofort da.

Task 3: chart-builder Skill

Nach drei Visualisierungen steckt im Repo implizites Wissen, das sich nicht wiederholen sollte. Welche Farbkonventionen gelten, wie ein Drill-Down mit focusPath funktioniert, warum <foreignObject> für Text-Umbruch in SVG nötig ist, wie Smoke-Tests für D3-Komponenten aussehen. Dieses Wissen in einen Skill zu verdichten bedeutet: beim nächsten Chart muss niemand die bestehenden Dateien auseinandersuchen.

Erstelle .claude/skills/chart-builder/SKILL.md.
Der Skill soll bei Chart-Anfragen triggern und folgende Konventionen festhalten:
Props-Struktur, Farbschema, shortLabel-Pattern, focusPath-Mechanik, Smoke-Test.
Außerdem: bei D3-API-Fragen soll der Skill Context7 aufrufen.

Der Kern-Workflow im Skill:

  1. Welcher Chart-Typ? Ist buildHierarchy() der richtige Dateneinstieg, oder braucht der Chart eine eigene Aggregation?
  2. Context7 für D3-API konsultieren, wenn Unsicherheit besteht.
  3. Komponente nach Projektkonvention aufbauen, Smoke-Test gleich mit.
  4. Im App.tsx-Switcher integrieren.

Der Skill referenziert Sunburst.tsx als Referenz für Drill-Down, Treemap.tsx für <foreignObject> und Sankey.tsx für Highlight-Verhalten. Wer das nächste Chart baut, findet dort alle Muster.

Tests anpassen

Die Migration auf shadcn-Tabs bringt eine unerwartete Konsequenz für die Tests. Radix Tabs, auf dem shadcn aufbaut, rendert <TabsContent> inaktiver Tabs standardmäßig nicht ins DOM. Das bedeutet: ein Test, der nach dem Klick auf den Sankey-Tab nach data-testid="sankey" sucht, findet nichts — nicht weil etwas kaputt ist, sondern weil Radix den Inhalt erst beim Aktivieren mounted.

Das ist eigentlich gutes Verhalten: unnötiger DOM-Ballast bleibt draußen. Für Tests heißt es aber: die bisherige Strategie „klick auf Tab, prüf ob Inhalt da ist" greift zu tief in die Rendering-Details ein. Sie testet die shadcn-Implementierung, nicht das eigentliche Verhalten der App.

Die robustere Alternative ist, das zu testen was semantisch entscheidend ist: sind die richtigen Tab-Trigger vorhanden, und ist der richtige als aktiv markiert?

// Default-Tab: Sunburst aktiv
expect(screen.getByTestId("switch-sunburst")).toHaveAttribute("aria-selected", "true");
expect(screen.queryByTestId("sunburst")).toBeInTheDocument();

Das ist kein Kompromiss, sondern der bessere Test: aria-selected ist genau das, was ein Screenreader oder ein Accessibility-Audit prüfen würde. Wenn der falsche Tab aktiv ist, schlägt dieser Test an. Wenn die Visualisierung selbst kaputt ist, haben die Smoke-Tests der Chart-Komponenten ihren Auftritt.

3 Tests grün, Build sauber.

Playwright-MCP als Vorbereitung

Playwright-MCP ist in .mcp.json konfiguriert, aber in Art 7 noch nicht genutzt. Der MCP-Server startet bei Bedarf einen Chromium-Browser, über den Claude Code navigieren, klicken und Screenshots machen kann.

Die Vorkonfiguration macht Sinn, weil der erste Start von Playwright eine Chromium-Installation auslöst. Das besser jetzt zu klären als mitten in einer End-to-End-Test-Session in Art 8.

Stand am Ende dieses Artikels

git clone https://codeberg.org/rotecodefraktion/byhaushalt.git
cd byhaushalt
git checkout v0.7
cd parser && uv run python -m parser.normalize
cd ../web && npm install && npm run dev

Vollständiger Stand unter byhaushalt @ v0.7.

v0.7 enthält: .mcp.json mit Context7 + Playwright, shadcn/ui mit Tabs, Button und Card, App.tsx auf shadcn-Tabs migriert, .claude/skills/chart-builder/SKILL.md. 22 Tests grün, 3 xfail.

cd parser && uv run pytest -v
cd web && npm run test
# 22 passed, 3 xfailed

Wie es weitergeht

Artikel 8 behandelt End-to-End-Tests mit Playwright-MCP. Claude Code steuert den Browser direkt, findet Selektoren, prüft ob Visualisierungen korrekt rendern, und schreibt die E2E-Specs aus einer Markdown-Beschreibung. Die Grundlage dafür ist der Playwright-MCP-Server, der in Art 7 schon konfiguriert wurde.

Wie shadcn/ui mit Context7 eingeführt wurde und was der chart-builder-Skill leistet, steht hier. Den Aufbau des Worktree-Setups zeigt Artikel 6. Subagents und das Datenmodell beschreibt Artikel 5.