Bygg er första MCP-server
Använd Python MCP SDK för att skapa en server som exponerar resurser, verktyg och prompts, och anslut den sedan till Claude Desktop för att se hela flödet fungera.
Bygg er första MCP-server är en gratis lektion i AI Engineering Academy på CoddyKit. Detta är lektion 2 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för AI Engineering Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i AI Engineering Academy innehåller totalt 4 lektioner.
Projektkonfiguration för en MCP-server
För att bygga en MCP-server i Python behöver du mcp-SDK:t och en Python-miljö. Du ska skapa en server som Claude Desktop eller vilken MCP-klient som helst kan ansluta till via stdio. Börja med att installera paketet och skapa serverfilen.
# Create a project directory
# mkdir my_mcp_server && cd my_mcp_server
# Create a virtual environment
# python -m venv venv && source venv/bin/activate
# Install the MCP SDK
# pip install mcp httpx
# Project structure:
# my_mcp_server/
# server.py <- Your MCP server
# requirements.txt
# README.mdSkapa serverobjektet
Importera från paketet mcp och skapa en instans av Server med serverns namn. Namnet visas för klienterna i deras lista över MCP-servrar – välj något beskrivande. Serverobjektet är startpunkten för att registrera alla dina verktyg, resurser och prompter.
# server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
import asyncio
import httpx
# Create the server — name is shown in Claude Desktop
app = Server('weather-server')
# --- Tool registrations go here ---
# Entry point
if __name__ == '__main__':
asyncio.run(stdio_server(app))Registrera verktyg med @app.list_tools()
Dekoratören @app.list_tools() registrerar en hanterare som returnerar listan över tillgängliga verktyg när klienten frågar efter den. Varje verktyg definieras med ett namn, en beskrivning och ett inputSchema – JSON Schema som beskriver parametrarna. Klienten skickar listan till LLM-modellen så att den vet vilka verktyg den kan anropa.
@app.list_tools()
async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name='get_weather',
description='Get current weather for a city. Use when the user asks about weather in a specific location.',
inputSchema={
'type': 'object',
'properties': {
'city': {
'type': 'string',
'description': 'City name, e.g. London or New York'
},
'units': {
'type': 'string',
'enum': ['metric', 'imperial'],
'description': 'Temperature units. Default is metric.'
}
},
'required': ['city']
}
),
types.Tool(
name='list_cities',
description='Return a list of major cities the user can query weather for.',
inputSchema={'type': 'object', 'properties': {}, 'required': []}
)
]Implementera verktygskörning
Decoratorn @app.call_tool() hanterar körning av verktyg. När klienten anropar ett verktyg tar denna hanterare emot verktygets namn och argument. Kör den logik som behövs och returnera en lista med TextContent-objekt som innehåller resultatsträngen.
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
if name == 'list_cities':
cities = ['London', 'New York', 'Tokyo', 'Paris', 'Sydney']
return [types.TextContent(type='text', text=', '.join(cities))]
if name == 'get_weather':
city = arguments['city']
units = arguments.get('units', 'metric')
unit_symbol = 'C' if units == 'metric' else 'F'
# Call real weather API (stub here)
async with httpx.AsyncClient() as client:
# Replace with actual API call
result = f'{city}: 18{chr(176)}{unit_symbol}, partly cloudy, humidity 65%'
return [types.TextContent(type='text', text=result)]
raise ValueError(f'Unknown tool: {name}')Exponera resurser
Resurser är statiska eller dynamiska data som modellen kan läsa – tänk på dem som filer. Definiera resurs-URI:er och implementera en resursläsare. Resurser visas i klienten som objekt som AI:n kan referera till och är användbara för konfiguration, dokumentation eller ofta använda ögonblicksbilder av data.
@app.list_resources()
async def list_resources() -> list[types.Resource]:
return [
types.Resource(
uri='weather://supported-cities',
name='Supported Cities List',
description='Complete list of cities available in this weather server.',
mimeType='text/plain'
)
]
@app.read_resource()
async def read_resource(uri: str) -> str:
if uri == 'weather://supported-cities':
cities = ['London', 'New York', 'Tokyo', 'Paris', 'Sydney', 'Dubai', 'Singapore']
return '\n'.join(cities)
raise ValueError(f'Unknown resource URI: {uri}')Registrera frågemallar
Frågemallar är återanvändbara meddelandemallar som klienter visar för användare som snedstreckskommandon eller snabbåtgärder. De tar emot parametrar och returnerar en lista med meddelanden som blir den inledande kontexten för en konversation. Frågemallar passar bra för att kapsla in komplexa instruktioner som användare kan aktivera med ett enda kommando.
@app.list_prompts()
async def list_prompts() -> list[types.Prompt]:
return [
types.Prompt(
name='weather-report',
description='Generate a formatted weather report for a city.',
arguments=[
types.PromptArgument(name='city', description='City name', required=True)
]
)
]
@app.get_prompt()
async def get_prompt(name: str, arguments: dict) -> types.GetPromptResult:
if name == 'weather-report':
city = arguments.get('city', 'London')
return types.GetPromptResult(
description=f'Weather report for {city}',
messages=[
types.PromptMessage(
role='user',
content=types.TextContent(
type='text',
text=f'Use the get_weather tool to look up {city} and give me a detailed weather report including what clothing I should wear.'
)
)
]
)
raise ValueError(f'Unknown prompt: {name}')Anslut till Claude Desktop
Om du vill använda MCP-servern med Claude Desktop lägger du till den i konfigurationsfilen för Claude Desktop. På macOS finns den på ~/Library/Application Support/Claude/claude_desktop_config.json. Ange kommandot som startar servern samt eventuella miljövariabler som den behöver.
# ~/Library/Application Support/Claude/claude_desktop_config.json
# Add this JSON configuration:
# {
# "mcpServers": {
# "weather-server": {
# "command": "/path/to/venv/bin/python",
# "args": ["/path/to/my_mcp_server/server.py"],
# "env": {
# "WEATHER_API_KEY": "your_api_key_here"
# }
# }
# }
# }
# After saving, restart Claude Desktop.
# Your server's tools will appear in Claude's tool list.Testa servern med MCP CLI
Testa servern med MCP-inspektören eller CLI-verktyg innan du ansluter till Claude Desktop. Kommandot mcp dev startar servern och öppnar en webbläsarbaserad inspektör där du kan anropa verktyg manuellt och se råa protokollmeddelanden. Det gör det enkelt att felsöka problem.
# Install the MCP development tools
# pip install 'mcp[cli]'
# Start the inspector with your server
# mcp dev server.py
# The inspector opens at http://localhost:5173
# You can:
# - See all registered tools and their schemas
# - Call tools with custom arguments
# - Browse available resources
# - Test prompt templates
# - View the full JSON-RPC message exchangeFelhantering i MCP-servrar
MCP-servrar ska aldrig krascha på grund av felaktiga indata. Omge all körning av verktyg med try/except och returnera felmeddelanden som TextContent i stället för att kasta undantag. För kritiska fel, till exempel konfigurationsproblem eller saknade API-nycklar, ska du logga dem vid uppstart och kasta ett undantag innan servern går in i sin huvudloop.
@app.call_tool()
async def call_tool_safe(name: str, arguments: dict) -> list[types.TextContent]:
try:
if name == 'get_weather':
city = arguments.get('city')
if not city:
return [types.TextContent(type='text', text='Error: city argument is required.')]
result = await fetch_weather(city, arguments.get('units', 'metric'))
return [types.TextContent(type='text', text=result)]
raise ValueError(f'Unknown tool: {name}')
except httpx.TimeoutException:
return [types.TextContent(type='text', text='Error: Weather API timed out. Try again.')]
except Exception as e:
return [types.TextContent(type='text', text=f'Error: {str(e)}')]Loggning för MCP-servrar
Eftersom MCP-servrar kommunicerar med klienten via stdio kommer utskrifter med print-satser att förstöra protokollet. Använd alltid stderr för loggning – den går till en separat ström som inte stör utbytet av MCP-meddelanden. Konfigurera Pythons loggningsmodul så att den skriver till stderr.
import logging
import sys
# Configure logging to stderr (NOT stdout — that's the MCP channel)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
stream=sys.stderr
)
logger = logging.getLogger('weather-server')
# In your tool handler:
# logger.info(f'Getting weather for {city}')
# logger.error(f'API call failed: {e}')
# Claude Desktop captures stderr to a log file:
# ~/Library/Logs/Claude/mcp-server-weather-server.logPaketera MCP-servern
Dela MCP-servern genom att paketera den med en pyproject.toml-fil och publicera den på PyPI, eller distribuera den som en Docker-container för team. Använd miljövariabler för alla hemligheter – API-nycklar, databas-URL:er och liknande – så att serverkonfigurationen hålls åtskild från koden. Dokumentera nödvändiga miljövariabler och en exempelkonfiguration i en tydlig README.
Snabbtest
Testa dina kunskaper om att bygga en MCP-server i Python.
Sammanfattning av lektionen
I den här lektionen har du lärt dig att MCP-servrar exponerar verktyg via dekoratorerna @app.list_tools() och @app.call_tool(), att resurser och frågemallar utökar servern med läsbara data och återanvändbara mallar samt att loggning måste använda stderr för att undvika att stdio-kanalen för MCP-protokollet förvanskas. Nästa steg är att ansluta en MCP-server till en databas och exponera dynamiska resurser med paginering.
Lär dig Python med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 30
- Lektioner
- 120
Vanliga frågor
Är lektionen ”Bygg er första MCP-server” gratis?
Ja – hela texten till ”Bygg er första MCP-server” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i AI Engineering Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i AI Engineering Academy innehåller totalt 4 lektioner.
Vad lär jag mig i ”Bygg er första MCP-server”?
Använd Python MCP SDK för att skapa en server som exponerar resurser, verktyg och prompts, och anslut den sedan till Claude Desktop för att se hela flödet fungera. Ni övar på AI Engineering Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig AI Engineering Academy?
Du behöver inga förkunskaper. Utbildningen i AI Engineering Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.
Hur lång tid tar lektionen ”Bygg er första MCP-server”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här AI Engineering Academy-lektionen?
Ja. Varje AI Engineering Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- Vad är MCP och varför är det viktigt
- Bygg er första MCP-server
- Exponera databaseresurser via MCP
- MCP-säkerhet och autentisering