Bau dir deinen eigenen MCP-Server: Wenn dein Agent eigene Werkzeuge bekommt
In meinem letzten Post ging es darum, fremde MCP-Server an ein LLM anzuschließen – Git, Drive, Mail. Die logische nächste Frage kam prompt: „Und wie baue ich selbst einen?"
Kurze Antwort: erstaunlich einfach. Ich hatte den ersten eigenen Server in unter einer Stunde laufen. Die längste Zeit habe ich damit verbracht, mir zu überlegen, was der Server überhaupt können soll – nicht damit, ihn zu programmieren.
Hier zeige ich dir das kleinste sinnvolle Beispiel: ein Server, der meinem Agenten Zugriff auf mein eigenes Blog gibt. Danach kann Claude Fragen beantworten wie „Worüber habe ich zuletzt geschrieben?" – ohne dass ich irgendwas kopiere. Und das Beste: Eine ausgebaute Version genau dieses Servers läuft live – am Ende des Posts zeige ich dir, wie du sie in dein Claude einbindest.
Was ein MCP-Server wirklich ist
Man kann sich von dem Begriff einschüchtern lassen. Muss man aber nicht.
Ein MCP-Server ist im Kern nur ein Programm, das eine Liste von Tools anbietet. Ein Tool ist eine Funktion mit einem Namen, einer Beschreibung und einem Eingabeschema. Das LLM liest die Beschreibungen, entscheidet selbst, wann ein Tool passt, ruft es mit den passenden Argumenten auf und bekommt ein Ergebnis zurück.
Die Kommunikation läuft über einen Transport. Für lokale Server ist das schlicht stdin/stdout – dein Server liest JSON von der Standardeingabe und schreibt JSON zurück. Kein HTTP-Server, kein Port, kein Deployment. Das Modell startet dein Programm einfach als Subprozess. Für Server, die im Netz laufen sollen, gibt es zusätzlich einen HTTP-Transport – das Prinzip bleibt dasselbe.
Das war's im Prinzip. Tools plus Transport. MCP kennt daneben noch Resources und Prompts, aber für den Einstieg reichen Tools völlig. Der Rest ist Beschreibungsarbeit.
Das kleinste sinnvolle Beispiel
Neues Projekt, offizielles SDK installieren:
npm init -y
npm install @modelcontextprotocol/sdk zod
Und dann die index.js. Das Herzstück ist ein Tool, das einen Blog-Post holt:
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { readFile } from 'node:fs/promises'
import { z } from 'zod'
const server = new McpServer({ name: 'blog', version: '1.0.0' })
server.tool(
'get_post',
'Liefert den vollständigen Markdown-Inhalt eines Blog-Posts.',
{ slug: z.string().describe('Der Slug, z. B. "vibe-coding"') },
async ({ slug }) => {
const md = await readFile(`./content/blog/${slug}.md`, 'utf8')
return { content: [{ type: 'text', text: md }] }
}
)
await server.connect(new StdioServerTransport())
Das läuft wirklich – kein Pseudocode. Ein zweites Tool list_posts, das alle Posts auflistet, sieht genauso aus, nur ohne Parameter. Die Beschreibung jedes Tools ist dabei kein Beiwerk, sondern die eigentliche Schnittstelle zum Modell: Sie entscheidet, ob der Agent das Tool im richtigen Moment greift. Ich schreibe die Beschreibung inzwischen so sorgfältig wie einen Funktionskommentar für einen Kollegen.
Wie dein Agent den Server findet
Der Server läuft nicht von allein – ein Client muss ihn starten. Bei Claude (Desktop oder Code) trägst du ihn in die Konfiguration ein:
{
"mcpServers": {
"blog": {
"command": "node",
"args": ["/pfad/zu/blog-server/index.js"]
}
}
}
Mehr nicht. Beim nächsten Start fährt Claude deinen Server als Subprozess hoch, fragt „welche Tools hast du?" und blendet list_posts und get_post ein. Ab jetzt kann der Agent sie aufrufen, wann immer er sie für sinnvoll hält.
Zum Debuggen gibt es den MCP Inspector (npx @modelcontextprotocol/inspector node index.js) – eine kleine Web-UI, in der du deine Tools von Hand aufrufst, bevor du sie ans Modell hängst. Den Schritt würde ich nie überspringen.
Was ich beim Bauen gelernt habe
Ein paar Dinge, die ich beim zweiten Server schon anders gemacht habe:
- Klein anfangen. Mein erster Reflex war, gleich zehn Tools zu bauen. Falsch. Ein Tool, das eine Sache gut kann, ist mehr wert als fünf halbgare. Und jedes Tool, das du dem Modell gibst, ist eins mehr, das es falsch aufrufen kann.
- Lesend zuerst. Meine Blog-Tools lesen nur. Sobald ein Tool schreibt, löscht oder verschickt, ändert sich alles – siehe unten.
- Beschreibungen sind Code. Ein vage beschriebenes Tool wird zur falschen Zeit gerufen. Eine präzise Beschreibung ist der Unterschied zwischen „nützlich" und „nervig".
- Fehler zurückgeben, nicht crashen. Wenn ein Slug nicht existiert, will der Agent eine verwertbare Fehlermeldung, keinen abgestürzten Subprozess. Gib Fehler als Text-Content zurück, damit das Modell reagieren kann.
Und der Punkt, den ich nicht oft genug wiederholen kann: Sobald dein Server schreibt, baust du keine Spielerei mehr, sondern ein Tor in deine Systeme. Alles, was ich im Post zu Prompt Injection geschrieben habe, gilt dann für deinen eigenen Code. Ein Tool namens delete_post klingt bequem – bis ein manipulierter Text im Kontext das Modell dazu bringt, es aufzurufen.
Brauchst du überhaupt einen eigenen Server?
Ehrliche Frage – denn oft ist die Antwort: nein. Wenn du einmal am Tag Daten von A nach B schieben willst, reicht ein Skript. Cron-Job, fertig. Ein MCP-Server ist die falsche Antwort auf ein Automatisierungsproblem.
Interessant wird es, wenn du vorher nicht weißt, wann die Daten gebraucht werden. Mein Blog-Server ist genau dafür da: Ich frage Claude irgendetwas – zu einem Artikel, einer Idee, einem Thema, das ich schon einmal behandelt habe – und der Agent entscheidet selbst, ob er dafür in meine Posts schauen muss. Diese Entscheidung kann ich nicht vorab in ein Skript gießen.
Das ist für mich die Faustregel: Wenn du den Aufruf selbst planen kannst, schreib ein Skript. Wenn der Agent den Aufruf planen soll, bau ein Tool.
Probier's aus: Mein Server läuft live
Das Beispiel aus diesem Post ist kein Theorie-Code. Eine ausgebaute Version läuft live unter https://finkbeiner.me/v1/mcp – mit dem HTTP-Transport von oben, sonst gleiches Prinzip. Neben den Blog-Tools liefert sie auch meine Projekte, Skills und Kontaktdaten.
In Claude Code reicht ein Befehl:
claude mcp add --transport http finkbeiner https://finkbeiner.me/v1/mcp
In Claude Desktop geht es über Einstellungen → Connectors → „Add custom connector" mit derselben URL.
Danach frag Claude einfach: „Worüber schreibt Patrick so?" Oder, wenn du gerade einen Freelancer suchst: „Passt Patrick zu meinem Nuxt-Projekt?" – und schau zu, welche Tools der Agent greift.
Alles read-only, versteht sich. Ich halte mich an meine eigenen Regeln.
Fazit: Ein Nachmittag, kein Hexenwerk
Einen eigenen MCP-Server zu bauen ist kein großes Projekt. Es ist ein Nachmittag. Das SDK nimmt dir die Protokoll-Details ab, du schreibst im Grunde nur Funktionen und beschreibst sie gut.
Der Reiz ist echt: Zum ersten Mal gibst du deinem Agenten Werkzeuge, die du definiert hast, zugeschnitten auf deinen Workflow. Das fühlt sich anders an als ein weiterer fertiger Server aus dem Verzeichnis.
Genau deshalb gilt hier dasselbe wie beim Anschließen fremder Server: Jedes Tool ist eine Entscheidung. Fang bei den Dingen an, die nur lesen. Baue das Schreiben erst, wenn du sicher bist, dass du die Konsequenzen willst. Und dann lass dich fragen, bevor etwas passiert – nicht informieren, nachdem es passiert ist.
Ein MCP-Server gibt deinem Agenten Hände. Es liegt an dir, wie weit sie reichen.