Skip to main content
Glama
krollchristensen

Sample Server

README.md
# MCP-demo med Python, FastMCP, Claude Desktop og Cursor

Dette repo viser et simpelt eksempel på en lokal MCP-server skrevet i Python med FastMCP.

Eksemplet udstiller tre MCP-tools:

```text
get_weather
calculate
convert_currency
```

Formålet er at vise, hvordan en AI-applikation som Claude Desktop eller Cursor kan bruge eksterne funktioner gennem MCP.

## Kort forklaring

MCP står for Model Context Protocol.

I dette eksempel er rollerne:

| Rolle | I dette eksempel |
|---|---|
| MCP host | Claude Desktop eller Cursor |
| MCP client | Den interne MCP-forbindelse i Claude/Cursor |
| MCP server | Python-programmet `server.py` |
| Tools | Funktionerne `get_weather`, `calculate` og `convert_currency` |

Claude Desktop og Cursor starter Python-programmet som en lokal MCP-server. Serveren kører via `stdio`, så den kommunikerer gennem standard input/output i stedet for HTTP.

## Hvorfor er eksemplet relevant?

Eksemplet viser, at MCP ikke bare handler om at kalde en funktion. Det handler om at standardisere forbindelsen mellem AI-applikationer og eksterne capabilities.

I stedet for at hver AI-app skal specialintegreres med hvert værktøj, kan værktøjer udstilles som MCP-servere.

Det giver en mere modulær arkitektur:

```text
AI-applikation
      |
   MCP client
      |
   MCP server
      |
 Lokale tools, filer, API'er eller databaser
```

## Projektstruktur

```text
MCP_Test/
├── server.py
├── pyproject.toml
├── uv.lock
└── .venv/
```

## Forudsætninger

Du skal have installeret:

```text
Python
uv
Claude Desktop og/eller Cursor
```

Tjek at `uv` virker:

```powershell
uv --version
```

Find eventuelt den fulde sti til `uv`:

```powershell
where.exe uv
```

Eksempel:

```text
C:\Users\mikc\.local\bin\uv.exe
```

## Opret projektet

Stå i projektmappen:

```powershell
cd C:\Users\mikc\IdeaProjects\MCP_Test
```

Hvis projektet ikke allerede har en `pyproject.toml`, så kør:

```powershell
uv init --bare
```

Installer FastMCP:

```powershell
uv add fastmcp
```

## server.py

Gem denne fil som:

```text
C:\Users\mikc\IdeaProjects\MCP_Test\server.py
```

```python
from fastmcp import FastMCP

# MCP-serveren er den del, der udstiller capabilities til en AI-applikation.
#
# I dette eksempel er rollerne:
# - Claude Desktop eller Cursor = MCP host
# - Deres interne forbindelse til denne proces = MCP client
# - Dette Python-program = MCP server
#
# Navnet "Sample Server" vises i hosten, så brug et navn der er let at genkende.
mcp = FastMCP("Sample Server")


# @mcp.tool() gør funktionen til en MCP capability af typen tool.
#
# Et tool er en handling eller funktion, som AI'en kan kalde.
#
# Discovery:
# Hosten kan opdage tool-navn, parametre og docstring.
# Derfor er tydelige funktionsnavne, simple parametre og gode docstrings vigtige.
@mcp.tool()
def get_weather(location: str) -> str:
    """Get the current weather for a specified location."""
    # Demo-tool:
    # Dette henter ikke rigtigt vejr fra et API.
    #
    # Det viser blot, hvordan et tool kan modtage input fra hosten
    # og returnere et resultat tilbage til AI'ens kontekst.
    return f"Weather in {location}: Sunny, 72°F"


# Et lille og afgrænset tool.
#
# Designpointen er:
# Lav hellere flere små tools end én stor funktion, der prøver at gøre alt.
@mcp.tool()
def calculate(expression: str) -> float | str:
    """Calculate the result of a mathematical expression."""
    try:
        # Kun til demo:
        # eval() bør ikke bruges i rigtig produktion,
        # fordi brugeren potentielt kan sende farlig kode.
        #
        # I undervisning er det dog et simpelt eksempel på:
        # prompt -> tool call -> execution -> resultat tilbage til hosten.
        return eval(expression)
    except Exception as e:
        # Fejlhåndtering er vigtig, fordi MCP-clienten skal kunne få
        # et klart svar tilbage, hvis execution fejler.
        return f"Error calculating expression: {str(e)}"


# Endnu et lille tool med tydelige parametre.
#
# AI'en kan se parameterlisten og bruge den til at kalde funktionen korrekt.
@mcp.tool()
def convert_currency(amount: float, from_currency: str, to_currency: str) -> str:
    """Convert amount from one currency to another."""
    # Demo-data:
    # I en rigtig MCP-server kunne dette være et kald til et eksternt API,
    # en database eller et internt system.
    #
    # Det viser pointen om, at MCP-serveren kan skjule kompleksitet
    # bag et simpelt tool-interface.
    rates = {
        "USD": 1.0,
        "EUR": 0.85,
        "GBP": 0.73,
        "JPY": 110.0,
        "INR": 83.0
    }

    if from_currency not in rates or to_currency not in rates:
        return f"Unsupported currency pair: {from_currency} to {to_currency}"

    converted = amount * (rates[to_currency] / rates[from_currency])
    return f"{amount} {from_currency} = {converted:.2f} {to_currency}"


# MCP lifecycle:
#
# FastMCP håndterer:
# - initialization
# - discovery
# - execution
# - termination
#
# Når Claude Desktop eller Cursor starter serveren,
# kommunikerer de med denne proces via stdio.
#
# mcp.run() uden argumenter bruger stdio som standard.
# Det passer derfor til både Claude Desktop og Cursor.
if __name__ == "__main__":
    mcp.run()
```

## Test serveren lokalt

Kør:

```powershell
cd C:\Users\mikc\IdeaProjects\MCP_Test
uv run server.py
```

Hvis terminalen bare står stille, er det normalt.

En `stdio`-MCP-server venter på, at en host, fx Claude Desktop eller Cursor, kommunikerer med den.

Stop serveren igen med:

```text
Ctrl + C
```

## Opsætning i Claude Desktop

Claude Desktop bruger filen:

```text
C:\Users\mikc\AppData\Roaming\Claude\claude_desktop_config.json
```

Du kan åbne mappen med:

```powershell
explorer $env:APPDATA\Claude
```

Hvis filen allerede findes, skal du ikke nødvendigvis slette indholdet. Du skal tilføje `mcpServers` på samme niveau som de andre hovedfelter.

Eksempel med fuld sti til `uv`:

```json
{
  "mcpServers": {
    "sample_mcp": {
      "command": "C:\\Users\\mikc\\.local\\bin\\uv.exe",
      "args": [
        "--directory",
        "C:\\Users\\mikc\\IdeaProjects\\MCP_Test",
        "run",
        "server.py"
      ]
    }
  }
}
```

Hvis din fil allerede indeholder andet indhold, fx `preferences`, kan den se sådan her ud:

```json
{
  "preferences": {
    "remoteToolsDeviceName": "z4020"
  },
  "coworkUserFilesPath": "C:\\Users\\mikc\\Claude",
  "mcpServers": {
    "sample_mcp": {
      "command": "C:\\Users\\mikc\\.local\\bin\\uv.exe",
      "args": [
        "--directory",
        "C:\\Users\\mikc\\IdeaProjects\\MCP_Test",
        "run",
        "server.py"
      ]
    }
  }
}
```

Vigtigt:

```text
mcpServers skal ikke ligge inde i preferences.
mcpServers skal ligge på øverste niveau i JSON-filen.
```

Efter ændringer:

```text
1. Gem filen
2. Luk Claude Desktop helt
3. Åbn Claude Desktop igen
```

## Test i Claude Desktop

Skriv fx:

```text
Brug get_weather med location Copenhagen
```

eller:

```text
Brug calculate til at regne 12 * 8 + 4
```

eller:

```text
Brug convert_currency til at konvertere 100 USD til EUR
```

Claude kan spørge om tilladelse til at bruge værktøjet. Godkend kaldet.

## Opsætning i Cursor

Cursor bruger typisk denne fil:

```text
C:\Users\mikc\.cursor\mcp.json
```

Du kan også finde opsætningen i Cursor under:

```text
Settings / Customize
Tools & MCPs
```

Brug denne `mcp.json`:

```json
{
  "mcpServers": {
    "sample_mcp": {
      "type": "stdio",
      "command": "C:\\Users\\mikc\\.local\\bin\\uv.exe",
      "args": [
        "--directory",
        "C:\\Users\\mikc\\IdeaProjects\\MCP_Test",
        "run",
        "server.py"
      ]
    }
  }
}
```

Gem filen.

Gå derefter til:

```text
Settings / Customize
Tools & MCPs
Home MCP Servers
sample_mcp
```

Du bør kunne se disse tools:

```text
get_weather
calculate
convert_currency
```

## Test i Cursor

Åbn Cursor-chatten.

Brug ikke kun ren Ask-mode, hvis Cursor ikke vil kalde tools. Vælg i stedet en agent-/composer-tilstand, eller tilføj MCP-serveren via:

```text
+ 
MCP Servers
sample_mcp
```

Test med:

```text
Brug MCP-tool get_weather med location Copenhagen
```

eller:

```text
Kald sample_mcp calculate med expression "12 * 8 + 4"
```

eller:

```text
Kald sample_mcp convert_currency med amount 100, from_currency USD og to_currency EUR
```

## Typiske fejl og løsninger

### Fejl: uv findes ikke

Hvis Claude eller Cursor ikke kan finde `uv`, så find den fulde sti:

```powershell
where.exe uv
```

Eksempel:

```text
C:\Users\mikc\.local\bin\uv.exe
```

Brug derefter den fulde sti i configen:

```json
"command": "C:\\Users\\mikc\\.local\\bin\\uv.exe"
```

### Fejl: pyproject.toml mangler

Hvis du får:

```text
No pyproject.toml found
```

så opret projektfilen:

```powershell
uv init --bare
```

og installer FastMCP:

```powershell
uv add fastmcp
```

### Fejl: adgang nægtet til .venv

Hvis du får en fejl om adgang til `.venv`, så kan miljøet være låst af en terminal eller IDE.

Prøv:

```powershell
deactivate
Remove-Item -Recurse -Force .venv
uv add fastmcp
```

Luk eventuelt Cursor, IntelliJ eller andre terminaler, der bruger projektet.

### Fejl: den angivne sti blev ikke fundet

Tjek at projektmappen findes:

```powershell
Test-Path C:\Users\mikc\IdeaProjects\MCP_Test
```

Tjek at `server.py` findes:

```powershell
Test-Path C:\Users\mikc\IdeaProjects\MCP_Test\server.py
```

Begge skal give:

```text
True
```

### Ændringer i server.py slår ikke igennem

Hvis du ændrer i `server.py`, skal MCP-serveren genstartes.

I Claude Desktop:

```text
Luk Claude helt og åbn igen
```

I Cursor:

```text
Slå MCP-serveren fra og til igen
```

eller genstart Cursor.

## Undervisningspointer

Dette eksempel kan bruges til at forklare flere centrale MCP-begreber.

### Host, client og server

Claude Desktop og Cursor er hosts. De har en MCP-client indbygget, som taler med MCP-serveren.

Python-filen `server.py` er MCP-serveren.

### Capabilities og tools

Funktionerne med `@mcp.tool()` bliver til capabilities af typen tools.

Tools får AI'en til at gøre noget, fx beregne, konvertere eller hente data.

### Discovery

Claude og Cursor kan opdage, hvilke tools serveren tilbyder.

Derfor er dette vigtigt:

```text
Gode funktionsnavne
Simple parametre
Klare docstrings
```

### Execution

Når brugeren skriver en prompt, kan AI'en vælge et relevant tool.

Flowet er:

```text
Bruger skriver prompt
AI vurderer behov for tool
MCP-client kalder MCP-server
Python-funktionen udføres
Resultatet sendes tilbage til AI'en
AI'en formulerer svaret
```

### Små tools frem for ét stort tool

Dette eksempel har tre små tools:

```text
get_weather
calculate
convert_currency
```

Det er bedre end én stor funktion, der prøver at gøre alt.

Små tools er lettere at forstå, teste, fejlfinde og genbruge.

## Forslag til videreudvikling

Når grundeksemplet virker, kan man udvide med:

```text
Read-only taskliste fra JSON-fil
Opslag i lokal SQLite-database
GitHub issue-helper
MongoDB read-only query
API-kald til rigtig vejrservice
Code review prompt
```

Start gerne med read-only eksempler, før der laves tools med sideeffekter som oprettelse, sletning eller afsendelse.

## Afsluttende pointe

MCP gør AI til en del af en softwarearkitektur.

Det handler derfor ikke kun om at skrive gode prompts, men om at designe:

```text
værktøjer
grænser
adgang
ansvar
fejlhåndtering
sikkerhed
```

Det gør MCP relevant for både programmering, systemudvikling, softwarearkitektur og IT-sikkerhed.

TDQS

B3.2/5.0

Scored across 3 tools

Disambiguation5/5

Each tool targets a completely different task (mathematical calculation, currency conversion, and weather retrieval), so there is no ambiguity in purpose.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern (calculate, convert_currency, get_weather), making them predictable and easy to understand.

Tool Count4/5

Three tools is a reasonable number for a utility server, though the set feels slightly sparse for a general-purpose toolkit.

Completeness2/5

The tools form a disjoint set with no clear domain; they cover only a few random utilities, leaving obvious gaps for a general-purpose server.

Maintenance

ActivityInactive
ResponsivenessNo issues