AI Agent Workshop
Mastering MCP
Bygg, utvid og deploy intelligente agenter med Model Context Protocol
// Arkitektur
MCP gir agenter tilgang til den virkelige verden gjennom standardisert verktøyoppdagelse
$ Hurtigstart
Sett opp utviklingsmiljøet ditt på under 5 minutter
Fork repository
Gå til GitHub og lag en fork av workshop-repositoryet. Husk å velge "Copy the main branch only".
zral/mcp-lab03Start Codespace
I din fork, klikk på Code →
Codespaces →
Create codespace on main
Konfigurer miljøvariabler
Kopier eksempelfilen og legg til dine API-nøkler:
# Kopier miljøfil
cp .env.example .env
# Rediger .env og legg til:
OPENAI_API_KEY=din-github-token-her
OPENWEATHER_API_KEY=din-openweather-key-her Hvor får du API-nøkler?
- OpenAI: github.com/marketplace/models → GPT-4o-mini → Create Personal Access Token
- OpenWeather: openweathermap.org/api → Registrer gratis konto
Start systemet!
Nå er du klar til å starte alle tjenestene:
# Start alle containere
docker compose up -d
# Sjekk at alt kjører
docker compose ps
# Åpne web-grensesnittet
# http://localhost:8080 > Labøvelser
Hands-on øvelser for å bygge og utvide MCP-verktøy
La oss starte med å utforske værverktøyet og forstå hvordan MCP-protokollen fungerer.
1. Start systemet
docker compose up -d 2. List tilgjengelige verktøy
curl -X POST "http://localhost:8000/message" \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}' \
| python3 -m json.tool 3. Test værverktøyet direkte
curl -X POST "http://localhost:8000/message" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_weather_forecast",
"arguments": {"location": "Oslo"}
}
}' 4. Test via agenten
curl -X POST "http://localhost:8001/query" \
-H "Content-Type: application/json" \
-d '{"query": "Hva er været i Bergen?"}' Når du ser JSON-respons med værdata, er Lab 1 fullført!
Nå skal du legge til et helt nytt verktøy i MCP-serveren: get_random_fact
Steg 1: Legg til funksjonen
Åpne services/mcp-server/app.py og legg til:
async def get_random_fact(category: str = "general") -> Dict[str, Any]:
"""Få et tilfeldig interessant faktum."""
facts = {
"general": [
"Honningbien produserer mat spist av mennesker.",
"Bananer er bær, men jordbær er ikke det."
],
"space": [
"En dag på Venus er lengre enn året sitt.",
"Saturn ville flyte i vann."
]
}
import random
fact = random.choice(facts.get(category, facts["general"]))
return {
"category": category,
"fact": fact,
"timestamp": datetime.now().isoformat()
} Steg 2: Oppdater tools manifest
I handle_tools_list(), legg til:
{
"name": "get_random_fact",
"title": "Random Fact Provider",
"description": "Få et tilfeldig interessant faktum",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"category": {
"type": "string",
"description": "Faktakategori (general, space)",
"enum": ["general", "space"]
}
},
"required": ["category"],
"additionalProperties": False
}
} Steg 3: Legg til routing
I handle_tools_call(), legg til:
elif tool_name == "get_random_fact":
category = arguments.get("category", "general")
result = await get_random_fact(category)
return {
"content": [{"type": "text", "text": json.dumps(result, ensure_ascii=False, indent=2)}],
"isError": False
} Steg 4: Rebuild og test
# Rebuild containers
docker compose build mcp-server travel-agent
docker compose up -d
# Test det nye verktøyet
curl -X POST "http://localhost:8000/message" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_random_fact",
"arguments": {"category": "space"}
}
}' Test også via agent: "Fortell meg et faktum om verdensrommet"
Nå skal du integrere et ekte eksternt API. Du må bruke alt du har lært i Lab 1 og 2!
Forutsetning
Du trenger en gratis API-nøkkel fra newsapi.org.
Legg den til i .env som NEWS_API_KEY.
Implementer get_news funksjonen
async def get_news(topic: str, language: str = "no") -> Dict[str, Any]:
"""Få nylige nyheter om et emne via NewsAPI."""
api_key = os.getenv("NEWS_API_KEY")
if not api_key:
return {
"isError": True,
"content": [{"type": "text", "text": "NEWS_API_KEY ikke konfigurert"}]
}
try:
async with httpx.AsyncClient() as client:
response = await client.get(
"https://newsapi.org/v2/everything",
params={
"q": topic,
"language": language,
"apiKey": api_key
},
timeout=10.0
)
news_data = response.json()
articles = [
{"title": article["title"], "url": article["url"]}
for article in news_data.get("articles", [])[:3]
]
return {
"isError": False,
"content": [{
"type": "text",
"text": f"Nyeste nyheter om '{topic}':\n\n" +
"\n".join([f"- {a['title']}\n {a['url']}" for a in articles])
}]
}
except Exception as e:
return {
"isError": True,
"content": [{"type": "text", "text": f"Feil: {str(e)}"}]
} Din oppgave
- 1. Legg til tool manifest for
get_newsihandle_tools_list() - 2. Legg til routing i
handle_tools_call() - 3. Rebuild og test med JSON-RPC
- 4. Test via agent: "Hva er de siste nyhetene om AI?"
Hint: inputSchema
{
"name": "get_news",
"description": "Hent nyeste nyheter om et emne",
"inputSchema": {
"type": "object",
"properties": {
"topic": {"type": "string", "description": "Emne å søke etter"},
"language": {"type": "string", "description": "Språkkode (f.eks. 'no', 'en')"}
},
"required": ["topic"]
}
} Bonus: Kombiner alle tre verktøy i ett spørsmål!
{} Teknisk Referanse
Oversikt over porter, endepunkter og JSON-RPC metoder
// Tjeneste-porter
| Tjeneste | Port | Beskrivelse |
|---|---|---|
| Web | 8080 | HTML frontend |
| Agent | 8001 | AI Agent API |
| MCP | 8000 | MCP Server |
// JSON-RPC 2.0 Metoder
| Metode | Beskrivelse |
|---|---|
tools/list | List tilgjengelige verktøy |
tools/call | Kjør et spesifikt verktøy |
// Eksempel-kall
GET /message (tools/list)
curl -X POST "http://localhost:8000/message" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}' POST /message (tools/call)
curl -X POST "http://localhost:8000/message" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_weather_forecast",
"arguments": {"location": "Oslo"}
}
}'