Skip to main content
Glama
krollchristensen

mcp-node-tasks-02-primitives

README.md
# MCP node tasks 02 - primitives

Dette projekt er anden del af vores praktiske introduktion til Model Context Protocol, forkortet MCP.

I del 1 havde vi en almindelig Node.js-applikation, der læste tasks fra en JSON-fil.

I denne del laver vi projektet om til en MCP-server.

Serveren udstiller:

| Type | Navn | Funktion |
|---|---|---|
| Resource | `tasks://all` | Læser alle tasks |
| Tool | `add_task` | Opretter en ny task |
| Prompt | `analyze_tasks` | Giver en fast struktur til task-analyse |

## Formål

Efter denne del skal du kunne forklare:

- Hvad resources, tools og prompts er
- Hvorfor resources normalt er read-only
- Hvorfor tools kræver mere omtanke
- Hvordan en lokal MCP-server kan køre med stdio
- Hvorfor `console.log()` kan give problemer i en stdio-baseret MCP-server
- Hvordan serveren kan testes med MCP Inspector
- Hvordan serveren kan kobles på Claude Desktop eller Cursor

## Projektstruktur

```text
mcp-node-tasks-02-primitives/
├── data/
│   └── tasks.json
├── docs/
│   └── images/
│       ├── Screenshot-2025-05-28-182048-1.png
│       └── lifecyclemcp-1.svg
├── src/
│   ├── server.js
│   └── taskStore.js
├── package.json
└── README.md
```

## Billeder

Kopier disse billeder fra zip-filen til projektet:

```text
docs/images/Screenshot-2025-05-28-182048-1.png
docs/images/lifecyclemcp-1.svg
```

## MCP primitives

En MCP-server kan udstille capabilities.

I dette projekt bruger vi tre centrale capabilities:

![MCP primitives](docs/images/Screenshot-2025-05-28-182048-1.png)

| Capability | Forklaring | Eksempel i projektet |
|---|---|---|
| Resource | Read-only data | `tasks://all` |
| Tool | En handling | `add_task` |
| Prompt | En arbejdsgang | `analyze_tasks` |

Den vigtigste huskeregel er:

```text
Resources giver AI'en noget at vide.
Tools får AI'en til at gøre noget.
Prompts giver AI'en en arbejdsform.
```

## Resource: tasks://all

En resource er data, som kan læses uden at ændre noget.

I dette projekt er resource:

```text
tasks://all
```

Den læser alle tasks fra:

```text
data/tasks.json
```

Resource-koden ændrer ikke filen. Den giver kun AI’en kontekst.

## Tool: add_task

Et tool er en funktion, der udfører en handling.

I dette projekt er tool:

```text
add_task
```

Det opretter en ny task i:

```text
data/tasks.json
```

Tool’et tager to input:

| Parameter | Forklaring |
|---|---|
| `title` | Titlen på den nye task |
| `priority` | `low`, `medium` eller `high` |

Eksempel på input:

```json
{
  "title": "Lav demo til undervisning",
  "priority": "high"
}
```

Fordi tool’et ændrer data, er det mere følsomt end en resource.

## Prompt: analyze_tasks

En prompt er en genbrugelig promptskabelon eller arbejdsgang.

I dette projekt er prompten:

```text
analyze_tasks
```

Den beder AI’en om at læse tasklisten og analysere, hvilke opgaver der bør prioriteres først.

Prompten ændrer ikke data. Den giver modellen en arbejdsform.

## Resource, tool og prompt i samme case

| Behov | MCP-type | Hvorfor |
|---|---|---|
| AI’en skal kende tasklisten | Resource | Det er viden |
| AI’en skal oprette en ny task | Tool | Det er en handling |
| AI’en skal analysere opgaverne | Prompt | Det er en arbejdsgang |

## Hvordan hænger Claude, MCP og koden sammen?

```mermaid
flowchart TD
    A["Claude Desktop<br>MCP host"]
    B["claude_desktop_config.json<br>Starter serveren"]
    C["src/server.js<br>MCP-server"]
    D["Resource: tasks://all<br>Læs alle tasks"]
    E["Tool: add_task<br>Opret ny task"]
    F["Prompt: analyze_tasks<br>Fast analyse"]
    G["src/taskStore.js<br>Node.js fil-logik"]
    H["data/tasks.json<br>Projektets data"]

    A --> B
    B -->|"node src/server.js"| C
    C --> D
    C --> E
    C --> F
    D --> G
    E --> G
    G --> H
    F --> A
```

Diagrammet viser, at Claude ikke læser `tasks.json` direkte.

Claude starter først vores MCP-server ud fra `claude_desktop_config.json`.

`src/server.js` er MCP-laget. Her definerer vi, hvad Claude må bruge:

- `tasks://all` som resource
- `add_task` som tool
- `analyze_tasks` som prompt

`src/taskStore.js` indeholder den almindelige Node.js-logik til at læse og skrive tasks.

`data/tasks.json` er kun datafilen.

Det vigtige er opdelingen af ansvar:

| Del | Ansvar |
|---|---|
| Claude Desktop | Host, hvor brugeren arbejder |
| Claude config | Starter MCP-serveren |
| `server.js` | Udstiller MCP capabilities |
| `taskStore.js` | Håndterer fil-logik |
| `tasks.json` | Indeholder data |

Claude taler altså med MCP-serveren. MCP-serveren bruger derefter vores Node.js-kode til at læse eller ændre data.

## Lifecycle

Et typisk MCP-flow kan forklares i fire faser:

![MCP lifecycle](docs/images/lifecyclemcp-1.svg)

| Fase | Hvad sker der? |
|---|---|
| Initialization | Client og server etablerer forbindelse |
| Discovery | Clienten spørger serveren, hvilke capabilities der findes |
| Execution | Clienten kalder en resource, et tool eller en prompt |
| Termination | Forbindelsen lukkes pænt |

Discovery er vigtig, fordi clienten kan spørge serveren, hvad den kan, i stedet for at alt er hardcoded.

## Installation

Kør:

```bash
npm install
```

## Vigtig pointe om npm start

Du kan godt køre:

```bash
npm start
```

Men serveren vil se ud som om, den ikke gør noget.

Det er normalt.

En MCP-server med stdio venter på, at en MCP-client taler med den. Derfor tester vi den med MCP Inspector, Claude Desktop eller Cursor.

## Test med MCP Inspector

Kør:

```bash
npm run inspect
```

I Inspector kan du teste:

1. Resource: `tasks://all`
2. Tool: `add_task`
3. Prompt: `analyze_tasks`

## Denne server bruger stdio

Stdio betyder, at MCP-clienten og MCP-serveren kommunikerer via standard input og standard output.

Derfor må serveren ikke skrive almindelige logs med:

```js
console.log("Server startet");
```

Det kan ødelægge MCP-kommunikationen, fordi stdout bruges til MCP-beskeder.

Brug i stedet:

```js
console.error("Server startet");
```

`console.error()` skriver til stderr og forstyrrer ikke MCP-kommunikationen.

## Claude eller Cursor konfiguration

Når projektet virker i MCP Inspector, kan det kobles på Claude Desktop eller Cursor.

Eksempel:

```json
{
  "mcpServers": {
    "mcp-node-tasks-02": {
      "command": "node",
      "args": [
        "C:\\Users\\mikc\\IdeaProjects\\mcp-node-tasks-02-primitives\\src\\server.js"
      ]
    }
  }
}
```

Ret stien, så den passer til din egen computer.

På Windows skal du enten bruge dobbelt backslash:

```text
C:\\Users\\mikc\\IdeaProjects\\mcp-node-tasks-02-primitives\\src\\server.js
```

eller forward slashes:

```text
C:/Users/mikc/IdeaProjects/mcp-node-tasks-02-primitives/src/server.js
```

## Miniøvelse

Åbn `src/server.js`.

Find:

1. Resource-koden
2. Tool-koden
3. Prompt-koden

Svar derefter på:

1. Hvad er resource i projektet?
2. Hvad er tool i projektet?
3. Hvad er prompt i projektet?
4. Hvorfor er `add_task` mere risikabel end `tasks://all`?
5. Hvorfor er det en god idé at starte med stdio?

## Forslag til svar

`tasks://all` er en resource, fordi den kun læser data.

`add_task` er et tool, fordi det ændrer data.

`analyze_tasks` er en prompt, fordi den giver AI’en en fast arbejdsgang.

`add_task` er mere risikabel end `tasks://all`, fordi den skriver til `data/tasks.json`.

Stdio er godt til lokal undervisning, fordi det ikke kræver port, Express-server eller deployment.

## Ekstra øvelse

Tilføj en ny task med MCP Inspector.

Kontroller bagefter, at filen er ændret:

```text
data/tasks.json
```

Spørgsmål:

- Hvor kan du se side effect?
- Hvorfor er det vigtigt, at tool’et har tydelige input?
- Hvad kunne gå galt, hvis `priority` ikke blev valideret?

## Næste trin

I næste del bygger vi en custom MCP-client i Node.js.

Den skal kunne:

1. Starte MCP-serveren via stdio
2. Lave discovery
3. Liste tools, resources og prompts
4. Kalde `tasks://all`
5. Kalde `add_task`
6. Hente prompten `analyze_tasks`

TDQS

A3.6/5.0

Scored across 1 tool

Disambiguation5/5

Only one tool exists, so there is no risk of ambiguity.

Naming Consistency5/5

With a single tool, naming consistency is trivially perfect. The name 'add_task' follows a clear verb_noun pattern.

Tool Count2/5

A single tool for task management is too few for reasonable functionality, making the server feel underdeveloped.

Completeness1/5

Only a create operation is provided, with no tools for listing, updating, deleting, or reading tasks, severely limiting usability.

Maintenance

ActivityInactive
ResponsivenessNo issues