Sådan bygger du en MCP-server i Python: Komplet guide til Model Context Protocol (2026)
Komplet praktisk guide til at bygge en MCP-server i Python med FastMCP. Lær JSON-RPC-protokollen, opsætning af tools, resources og prompts, samt hvordan du forbinder din server til Claude Desktop og kører den i produktion.
En MCP-server i Python bygges hurtigst med FastMCP-biblioteket fra det officielle mcp-SDK: du definerer dine værktøjer som almindelige Python-funktioner med @mcp.tool()-dekoratoren, kører serveren over stdio, og forbinder den til Claude Desktop via en JSON-konfigurationsfil. Model Context Protocol (MCP) er Anthropics åbne standard fra november 2024. Den lader LLM-klienter som Claude tale med eksterne datakilder og værktøjer på en ensartet, sikker måde, så du slipper for at skrive integrationskode for hver enkelt model eller klient.
Jeg byggede min første MCP-server til en kunde i januar 2025 (en simpel Postgres-forespørgselsserver), og det der overraskede mig mest var hvor lidt kode der faktisk skulle til. Under 50 linjer, og Claude kunne pludselig hente live ordredata. Lad mig vise dig hvordan.
MCP er en åben JSON-RPC 2.0-baseret protokol, der standardiserer hvordan LLM'er kommunikerer med eksterne værktøjer, ressourcer og prompts.
Det officielle Python-SDK indeholder FastMCP, der gør det muligt at definere værktøjer med dekoratorer på under 20 linjer kode.
MCP understøtter to transportlag: stdio (lokale processer) og HTTP/SSE (fjernservere). Du vælger ud fra dit deployment-scenarie.
I modsætning til OpenAI function calling er MCP klient-agnostisk: én server fungerer med Claude Desktop, IDE-integrationer og custom agenter samtidig.
Sikkerhed kræver eksplicit konfiguration af roots, capability-deklarationer og brugersamtykke ved tool-kald. Protokollen håndhæver det ikke automatisk.
FastMCP 2.x understøtter strukturerede outputs via Pydantic-modeller og asynkrone værktøjer ud af boksen.
Hvad er Model Context Protocol?
Model Context Protocol (MCP) er en åben standard udviklet af Anthropic og frigivet i november 2024. Den beskriver hvordan store sprogmodeller kan kommunikere med eksterne systemer på en ensartet måde. Hvor du tidligere skulle skrive en separat integration for hver kombination af model og datakilde (én til Claude, én til GPT, én til hver database), definerer MCP et fælles JSON-RPC 2.0-lag. Resultatet: en server du bygger én gang kan bruges af enhver MCP-kompatibel klient.
Protokollen specificerer tre hovedprimitiver: tools (funktioner LLM'en kan kalde), resources (data LLM'en kan læse) og prompts (genbrugelige promptskabeloner). Klient og server forhandler om capabilities ved opstart, hvilket gør protokollen fremadkompatibel.
I løbet af 2025 voksede økosystemet markant. Officielle SDK'er findes nu til Python, TypeScript, Java, Kotlin, C# og Swift, og store IDE'er som VS Code, Cursor og Zed har indbygget MCP-understøttelse.
MCP er især nyttig når du vil give din egen LLM-assistent adgang til virksomhedsdata, interne API'er eller udviklerværktøjer, uden at uploade hele datasættet til en cloud-API. Serveren kører lokalt eller i dit eget netværk, og klienten henter kun det data, der er relevant for den aktuelle samtale. For en bredere introduktion til hvordan LLM'er bruger eksterne værktøjer, se vores guide til function calling og tool use med LLM'er i Python.
MCP vs. function calling: hvad er forskellen?
Function calling og MCP løser overlappende problemer, men på forskellige niveauer i stakken. Function calling er en API-funktion specifik for én leverandørs model. Du sender et JSON-schema med i hvert kald, og modellen svarer med argumenter til en funktion. MCP er en protokol, der eksisterer over modellen: den definerer hvordan en klient og server taler sammen, uafhængigt af hvilken model klienten bruger internt.
Egenskab
Function calling
MCP
Standardisering
Leverandørspecifik (OpenAI, Anthropic, Google)
Åben standard, ens på tværs af klienter
Genbrug
En implementering pr. model
Én server, mange klienter
Transport
HTTPS til model-API
stdio eller HTTP/SSE
Stateful sessioner
Stateless
Persistent forbindelse med capabilities
Ressourcer ud over funktioner
Kun tools
Tools, resources og prompts
Bruges typisk til
Inline tool-kald i applikationen
Eksterne integrationer og pluggable kontekst
I praksis bruger mange teams begge dele: function calling internt i agenten til hurtige, applikationsspecifikke handlinger, og MCP til at eksponere genbrugelige integrationer (databaser, filsystem, Slack, Jira), som andre teams og klienter også kan koble sig på.
Installation og projektopsætning
Vi bruger Python 3.10 eller nyere og det officielle MCP Python-SDK. Det anbefalede værktøj er uv fra Astral, da det håndterer både Python-versioner og afhængigheder hurtigere end pip. Hvis du foretrækker pip, virker det også fint.
# Opret nyt projekt med uv (anbefalet)
uv init mcp-vejr-server
cd mcp-vejr-server
uv add "mcp[cli]" httpx pydantic
# Eller med pip + venv
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install "mcp[cli]" httpx pydantic
Pakken mcp[cli] indeholder både kernebiblioteket og mcp-kommandolinjeværktøjet, som vi bruger til at teste serveren lokalt med MCP Inspector (en webbaseret debug-UI, der kører på http://localhost:6274). httpx bruger vi til at lave HTTP-kald i vores eksempel-tool, og pydantic til strukturerede outputs.
Din første MCP-server med FastMCP
Lad os bygge en simpel vejrservice, der eksponerer to værktøjer: et til at hente aktuelt vejr og et til at konvertere mellem temperaturskalaer. Opret en fil server.py:
from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel, Field
import httpx
# Initialiser serveren med et navn (vises i klienten)
mcp = FastMCP("vejr-service")
class Vejrdata(BaseModel):
"""Struktureret vejrresultat med Pydantic-validering."""
by: str
temperatur_celsius: float
beskrivelse: str
luftfugtighed: int = Field(ge=0, le=100)
@mcp.tool()
async def hent_vejr(by: str) -> Vejrdata:
"""Hent aktuelt vejr for en given by.
Args:
by: Navnet på byen, fx "København" eller "Aarhus"
"""
# Open-Meteo kræver ingen API-nøgle, perfekt til demoer
async with httpx.AsyncClient(timeout=10.0) as client:
geo = await client.get(
"https://geocoding-api.open-meteo.com/v1/search",
params={"name": by, "count": 1, "language": "da"},
)
geo.raise_for_status()
steder = geo.json().get("results", [])
if not steder:
raise ValueError(f"Byen '{by}' kunne ikke findes")
lat, lon = steder[0]["latitude"], steder[0]["longitude"]
v = await client.get(
"https://api.open-meteo.com/v1/forecast",
params={
"latitude": lat,
"longitude": lon,
"current": "temperature_2m,relative_humidity_2m,weather_code",
},
)
v.raise_for_status()
c = v.json()["current"]
return Vejrdata(
by=by,
temperatur_celsius=c["temperature_2m"],
beskrivelse=_kode_til_tekst(c["weather_code"]),
luftfugtighed=c["relative_humidity_2m"],
)
@mcp.tool()
def konverter_temperatur(vaerdi: float, fra: str, til: str) -> float:
"""Konverter mellem celsius, fahrenheit og kelvin."""
til_kelvin = {"c": lambda x: x + 273.15,
"f": lambda x: (x - 32) * 5 / 9 + 273.15,
"k": lambda x: x}
fra_kelvin = {"c": lambda x: x - 273.15,
"f": lambda x: (x - 273.15) * 9 / 5 + 32,
"k": lambda x: x}
k = til_kelvin[fra.lower()](vaerdi)
return round(fra_kelvin[til.lower()](k), 2)
def _kode_til_tekst(kode: int) -> str:
tabel = {0: "klart vejr", 1: "overvejende klart", 2: "delvist skyet",
3: "overskyet", 45: "tåge", 61: "let regn", 63: "regn",
71: "let sne", 95: "tordenvejr"}
return tabel.get(kode, "ukendt")
if __name__ == "__main__":
mcp.run(transport="stdio")
Test serveren med MCP Inspector:
uv run mcp dev server.py
# eller hvis du bruger pip:
mcp dev server.py
Inspector åbner i browseren. Klik "Connect", vælg fanen "Tools", og kald hent_vejr med {"by": "København"}. Du skulle få en JSON-respons med temperatur og beskrivelse. Det er præcis hvad en LLM-klient ser, når den kalder dit værktøj.
Tools, resources og prompts forklaret
MCP-protokollen definerer tre primitiver, og hver har sin egen brugssag. At forstå forskellen er afgørende for at designe en god server.
Tools: handlinger LLM'en kan udføre
Tools er funktioner, der har bivirkninger eller henter dynamiske data. LLM'en beslutter hvornår de skal kaldes, og brugeren bør typisk give samtykke første gang. Brug tools til API-kald, databaseskrivninger og kommandoer der ændrer tilstand. Vores hent_vejr ovenfor er et tool.
Resources: data LLM'en kan læse
Resources er statiske eller semi-statiske data identificeret med en URI. Klienten (ikke modellen) afgør hvornår de skal indlæses i konteksten. Tænk på dem som filer: file:///projekt/README.md, postgres://customers/schema. Brug dem til dokumentation, konfigurationsfiler og schema-definitioner.
Prompts er parametriserede tekstskabeloner, som brugeren eksplicit kan invokere, typisk via en slash-kommando i klienten. De er ideelle til standardiserede arbejdsgange som code review, commit-beskeder eller bug-rapporter.
@mcp.prompt()
def kodegennemgang(sprog: str, kode: str) -> str:
return f"Lav en grundig code review af følgende {sprog}-kode "\
f"med fokus på sikkerhed og ydeevne:\n\n```{sprog}\n{kode}\n```"
En velkonstrueret server kombinerer typisk alle tre: tools til handlinger, resources til kontekstdata, og prompts til standardiserede arbejdsgange.
Sådan forbinder du MCP til Claude Desktop
Claude Desktop var den første MCP-klient og er stadig den nemmeste at teste mod. Konfigurationen ligger i en JSON-fil, hvis placering afhænger af operativsystem:
Tilføj din server i mcpServers-blokken. Brug absolutte stier. Claude Desktop kører ikke kommandoen i din shell, så $PATH og aliasser virker ikke (jeg spildte en time på det i januar, så lær af min fejl):
Genstart Claude Desktop helt (Quit, ikke bare luk vinduet). Klik på værktøjsikonet i bunden af samtalefeltet, og du skulle se "vejr-service" med dine to tools. Spørg fx "Hvad er vejret i Odense?", og Claude vil bede om tilladelse til at kalde hent_vejr.
HTTP/SSE-transport til fjernservere
stdio-transport er perfekt til lokale processer. Men når du vil eksponere én MCP-server til mange klienter (fx et helt team), bruger du HTTP-transport med Server-Sent Events. FastMCP gør det til et engelskift:
if __name__ == "__main__":
mcp.run(
transport="streamable-http",
host="0.0.0.0",
port=8000,
)
Kør serveren med uv run server.py og test endpoint'et på http://localhost:8000/mcp. I produktion skal du sætte serveren bag en reverse proxy som Nginx eller Caddy med TLS, og tilføje autentificering: typisk OAuth 2.0 eller en simpel bearer-token-middleware. Den officielle MCP-specifikation beskriver de godkendte autentificeringsflows i detaljer.
Husk at HTTP-transport introducerer multi-tenancy-overvejelser: hver session skal isoleres, og du bør implementere rate limiting pr. klient. Til produktionsworkloads med komplekse agenter kobler du typisk MCP-tools ind i en agentgraf. Se vores guide til byg din første AI-agent med LangGraph i Python for et konkret eksempel.
Sikkerhed og produktion
MCP-protokollen specificerer mekanismer for sikkerhed, men håndhæver dem ikke automatisk. Det er dit ansvar som server-udvikler at:
Validere alle inputs. Pydantic-modeller på tool-parametre er et must. Stol aldrig på, at LLM'en sender velformet data.
Begrænse scope. Hvis dit værktøj læser filer, så begræns det til en specifik rod via MCP's roots-capability. Eksponér aldrig hele filsystemet.
Logge alle tool-kald. Gem hvilken klient, hvilket tool, hvilke argumenter og hvilket resultat. Det er kritisk til audit og fejlfinding.
Implementere rate limiting. Især for tools, der koster penge (LLM-kald, betalte API'er) eller belaster eksterne systemer.
Bruge eksplicit samtykke. Klienter som Claude Desktop spørger brugeren første gang, men hvis du bygger din egen klient, skal du implementere det selv.
I produktion bør du også monitorere serveren. MCP Inspector er fin til udvikling, men til produktion bruger man typisk OpenTelemetry-traces, der følger hvert tool-kald fra klient gennem server til downstream-systemer. Det officielle SDK har indbygget OTEL-instrumentering fra version 1.8.
Almindelige fejl og fejlfinding
Når MCP-serveren ikke opfører sig som forventet, er det næsten altid én af disse fem ting:
Print-statements forstyrrer JSON-RPC. stdio-transport bruger stdout til protokolbeskeder. Bruger du print() i din tool-kode, korrumperer det streamen. Brug import logging; logging.basicConfig(level=logging.INFO, stream=sys.stderr).
Forkerte stier i Claude Desktop-config. Brug absolutte stier til både command og --directory. Test først med which uv og pwd i terminalen.
Async/sync-blanding. FastMCP understøtter begge dele, men en blokerende requests.get() i et async def-tool fryser serveren. Brug httpx.AsyncClient til HTTP.
Manglende capability-deklarationer. Hvis du tilføjer resources eller prompts efter første test, så genstart klienten. Capabilities forhandles kun ved opstart.
Versionsmismatch. Tjek pip show mcp. Protokollen revideres et par gange om året, og gamle klienter kan ikke tale med nye servere uden version negotiation.
For en dybere debug-oplevelse, kør serveren med MCP_LOG_LEVEL=debug som miljøvariabel. Så ser du hvert eneste JSON-RPC-budskab på stderr.
Ofte stillede spørgsmål
Hvad er forskellen på MCP og LangChain tools?
LangChain tools er et Python-bibliotek bundet til LangChain-agenter, mens MCP er en sproguafhængig protokol, der virker på tværs af klienter og programmeringssprog. Du kan faktisk eksponere LangChain-tools via en MCP-server og dermed gøre dem tilgængelige for Claude Desktop, Cursor og andre MCP-klienter samtidigt.
Kan jeg bruge MCP med OpenAI's GPT-modeller?
Ja, men det kræver en MCP-klient, der oversætter mellem MCP-protokollen og OpenAI's API. Officielle SDK'er gør det nemt. Du skriver din MCP-server én gang, og klienten kalder GPT med tools konverteret til function calling-formatet under hætten.
Er MCP sikkert at bruge i produktion?
MCP er sikker, hvis du implementerer protokollens anbefalinger: roots til at begrænse filsystemadgang, capability-baseret tilladelser, audit-logging og eksplicit brugersamtykke ved første tool-kald. Protokollen selv håndhæver intet, så sikkerheden er serverudviklerens ansvar.
Hvor mange MCP-servere kan jeg køre samtidigt?
Der er ingen hård grænse. Claude Desktop håndterer i praksis 10-20 servere uden problemer. Hver server kører i sin egen proces ved stdio-transport, så ressourceforbruget er primært RAM (cirka 30-100 MB pr. Python-server). Til mange servere kan en delt HTTP-server med flere tools være mere effektivt.
Skal jeg bruge FastMCP eller det lavniveau-SDK?
Brug FastMCP til 95% af tilfældene. Det dækker tools, resources, prompts og begge transporter med minimal boilerplate. Det lavniveau-API er kun nødvendigt hvis du implementerer custom protokoludvidelser, alternative transporter eller har brug for fuld kontrol over JSON-RPC-laget.
Article changelog (1)
— SEO meta refreshed (title and description updated)
Lær hvordan function calling og tool use fungerer med LLM'er i Python. Komplet guide med arbejdende kodeeksempler for OpenAI og Claude, parallelle tool calls, Pydantic-validering og Model Context Protocol (MCP).
Lær at bygge en komplet RAG-pipeline med Python, LangChain og ChromaDB — fra dokumentindlæsning og chunking til vektorsøgning og svargenerering med GPT-4o eller Claude.
Lær at bygge en AI-agent med LangGraph i Python fra bunden. Denne trin-for-trin guide dækker StateGraph, værktøjsintegration, hukommelse og streaming — med kodeeksempler der virker i 2026.