$ workshop --start mcp-agents

AI Agent Workshop
Mastering MCP

Bygg, utvid og deploy intelligente agenter med Model Context Protocol

Lars Søraas & Leif Terje Fonnes | Januar 2026
Kom i gang

// Arkitektur

MCP gir agenter tilgang til den virkelige verden gjennom standardisert verktøyoppdagelse

Web Grensesnitt :8080 AI Agent :8001 MCP Server :8000 HTTP JSON-RPC 2.0 tools/list • tools/call get_weather • get_news • ...
Agent (OpenAI)
MCP Protokoll
Verktøy

$ Hurtigstart

Sett opp utviklingsmiljøet ditt på under 5 minutter

1

Fork repository

Gå til GitHub og lag en fork av workshop-repositoryet. Husk å velge "Copy the main branch only".

zral/mcp-lab03
2

Start Codespace

I din fork, klikk på CodeCodespacesCreate codespace on main

3

Konfigurer miljøvariabler

Kopier eksempelfilen og legg til dine API-nøkler:

~/.env setup
# 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?

Start systemet!

Nå er du klar til å starte alle tjenestene:

docker compose
# 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
docker compose up -d

2. List tilgjengelige verktøy

curl - tools/list
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 - 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"}
    }
  }'

4. Test via agenten

curl - agent query
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:

app.py
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:

app.py - tools manifest
{
    "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:

app.py - routing
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

docker & 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

app.py
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_news i handle_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

manifest hint
{
    "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"}
    }
  }'