🔌 Bolagshack

Bolagshack · september 2026

Låt modellen ställa frågorna

Bygg en egen MCP-server, koppla den till en assistent och se vad som händer när modellen får riktig data bakom sig.

MCPFredag60 minuterEnsam eller i par

Välkommen

Välkommen till CoCreates andra Bolagshack!

I augusti byggde vi en dataplattform i miniatyr med DuckDB. Den här gången vänder vi på steken: istället för att vi ställer frågorna till datan låter vi en AI-agent göra det. Verktyget är MCP, Model Context Protocol, ett öppet protokoll som låter Claude och andra modeller anropa dina egna verktyg och datakällor. Under timmen bygger du en egen MCP-server, kopplar den till en agent och ser vad som händer när agenten får svara med riktig data bakom sig.

Vi kör en timme på fredagseftermiddagen direkt efter lunch, rakt in i kaklet (dvs fikat)!

Våra tre mål med dagen:

  1. Förstå MCP. Vad protokollet är och hur delarna hänger ihop.
  2. Servera riktig data. Bygg en server som ger agenten tillgång till din data.
  3. Låt agenten ställa frågorna. Koppla servern till agenten och se vad den gör med datan.

Prepp

Prepp tar tio minuter men gör att du slipper bråka med installationer under hacktimmen. Gör det innan fredag.

Du behöver uv, FastMCP och en agent att koppla servern till. Node är valfritt men trevligt. Har du kvar hack.db från augusti är den ett utmärkt underlag, men det går lika bra med CSV-filerna direkt, med Robs demo i demos/duckdb/ eller med helt egen data.

Repot

Klona repot (om du inte redan har det):

git clone git@github.com:knowit-solutions-cocreate/bolagshack.git

Se till att du har senaste ändringarna:

sh
git switch main
git pull

Gör din egen gren:

git switch -c $(whoami)

Jobba förslagsvis i hack-2026-09-mcp/people/<ditt-namn>/.

uv

Installera uv (om du inte redan har det):

curl -LsSf https://astral.sh/uv/install.sh | sh

eller med din lokala pakethanterare (brew install uv, etc).

Verifiera att det fungerar:

uv --version

FastMCP

FastMCP är Python-ramverket vi bygger servern med. Skapa en katalog för din server och gör den till ett uv-projekt med FastMCP och DuckDB som beroenden:

sh
mkdir min-egen-mcp && cd min-egen-mcp
uv init --bare
uv add fastmcp duckdb

Verifiera:

uv run fastmcp version

Varför ett projekt och inte uv tool install fastmcp? För att servern behöver duckdb (eller vad du nu vill servera) i samma miljö som FastMCP, och det är enklast när uv äger hela miljön. Det är också så Claude kommer att starta servern sen.

Node (valfritt)

MCP Inspector är ett debugverktyg: en webbsida som startar din server, listar dess verktyg, resurser och prompts och låter dig anropa dem för hand och se exakt vad som skickas fram och tillbaka. Ingen modell inblandad, bara du och protokollet. Det är där du tittar när Claude gör något oväntat med ditt verktyg. Den är ett Node-program som startas med npx, så vill du använda den behöver du Node: brew install node (eller mise use -g node@lts, nvm install --lts, etc).

Verifiera:

npx --version

Hoppar du över Node kan du ändå testa servern direkt i Claude enligt steg 3.

En agent

Servern behöver en klient som pratar MCP. Välj en av dessa (eller båda). Har du ingen Claude-inloggning räcker det med inspektorn i testet nedan för att komma igång, men det är roligare med en riktig agent.

Claude Desktop. Enklast att demonstrera. Ladda ner från claude.ai/download och logga in. Servern ansluts via en konfigurationsfil som FastMCP kan skriva åt dig, se steg 3 nedan.

Claude Code. För den som hellre bor i terminalen. Installera:

curl -fsSL https://claude.ai/install.sh | bash

Verifiera med claude --version. Servern ansluts med claude mcp add, se steg 3 nedan.

Allt grönt?!

Är allt ovanför under kontroll? Bra, då är du redo för fredagens bolagshack!

Uppgiften

Tre mål med dagen:

  1. Förstå MCP. En MCP-server erbjuder modellen tre saker: verktyg (funktioner modellen kan anropa, som query(sql)), resurser (data modellen kan läsa, som ett schema eller en fil) och prompts (färdiga instruktioner användaren kan välja). Efter idag ska du kunna förklara skillnaden och veta när du vill ha vilken.
  2. Servera riktig data. Bygg en server över CoFinder-datan från augusti eller något eget. Ett query-verktyg mot DuckDB räcker för att komma igång.
  3. Låt agenten ställa frågorna. Koppla servern till Claude och fråga på svenska. Titta på vilka verktyg den väljer, vilka frågor den skriver och var det går fel. Det är den intressanta delen.

Jobba ensam eller i par. Vi har 60 minuter. När tiden är slut:

  1. Committa det du har till din egen gren och pusha. Oavsett hur långt det kom.
  2. Skriv en rad i #bolagshack på Slack om vad du gjorde och vad du lärde dig. En skärmdump av en konversation räcker gott.

En timme är kort, så fokusera!

Datan

Datan från augusti ligger kvar i hack-2026-08-duckdb/data/:

Fil Innehåll
job_skills.csv ~3 400 konsultuppdrag från Verama, Cinode Market m.fl. Kolumnen skills är en kommaseparerad sträng.
people_skills.csv 48 konsulter i teamet. skills är kommaseparerad, skills_detailed är JSON med nivå 1–5 per skill.

Har du kvar din hack.db med rena tabeller från förra gången: använd den. Modellen får det mycket lättare med person_skill och job_skill än med kommaseparerade strängar.

Annars finns Robs demo i demos/duckdb/. Den läser in CSV-filerna till en lokal databas med två råtabeller, raw_knowit.job_skills och raw_knowit.people_skills:

sh
cd demos/duckdb
uv run ingest.py
duckdb data/bolagshack.duckdb -c "SELECT count(*) FROM raw_knowit.job_skills"

Peka sedan din server på demos/duckdb/data/bolagshack.duckdb, eller skapa en symlink till dito.

Alternativt så skapar du en egen databas-fil på trettio sekunder:

sh
duckdb hack.db
sql
CREATE TABLE jobs   AS SELECT * FROM 'hack-2026-08-duckdb/data/job_skills.csv';
CREATE TABLE people AS SELECT * FROM 'hack-2026-08-duckdb/data/people_skills.csv';

Egen data

Allt som DuckDB kan läsa går att servera: CSV, Parquet, JSON, Excel, lokalt eller via URL. Men det behöver inte vara en databas alls. Ett verktyg i en MCP-server är bara en Python-funktion, så det kan lika gärna slå mot ett API, läsa din kalender eller kolla status på ett kluster.

Sätt igång!

Ta stegen i ordning. Hinner du inte hela vägen är det helt okej, en server som svarar på ett enda verktyg i inspektorn är också ett resultat.

Steg 1: Hello world

Skapa hello.py:

python
from fastmcp import FastMCP

mcp = FastMCP("Hello")

@mcp.tool
def greet(name: str) -> str:
    """Hälsa på någon."""
    return f"Hej, {name}!"

if __name__ == "__main__":
    mcp.run()

Kör:

uv run fastmcp run hello.py

FastMCP skriver ut en banner och servern väntar sedan på en klient på stdin. Det är allt du kan se utan klient, men det räcker som kvitto på att installationen fungerar. Avsluta med Ctrl-C.

Du kan också starta den i inspektorn om du har Node installerat:

uv run fastmcp dev inspector hello.py

En webbsida öppnas med din server i listan, frånkopplad. Slå på reglaget vid Disconnected så startar inspektorn servern och du kan se verktyget greet under Tools, anropa det och läsa svaret. Fungerar det så är kedjan Python, FastMCP och MCP-protokollet på plats. Nu är det dags att faktiskt skapa ett användbart verktyg!

Steg 2: Servera datan

Skapa server.py som öppnar din DuckDB-databas och definierar ett verktyg:

python
from pathlib import Path

import duckdb
from fastmcp import FastMCP

mcp = FastMCP("Bolagshack")
db = duckdb.connect(Path(__file__).parent / "hack.db", read_only=True)

@mcp.tool
def query(sql: str) -> list[dict]:
    """Kör en SQL-fråga (DuckDB) mot uppdrags- och konsultdatan och returnera raderna."""
    rel = db.sql(sql)
    return [dict(zip(rel.columns, row)) for row in rel.fetchall()]

if __name__ == "__main__":
    mcp.run()

Sökvägen till databasen räknas ut från __file__ för att Claude Desktop startar servern från en annan katalog än din. Kör i inspektorn:

uv run fastmcp dev inspector server.py

Testa: SELECT count(*) FROM raw_knowit.job_skills eller motsvarande för din data.

Modellen vet ännu inte vilka tabeller som finns. Ge den en resurs som beskriver schemat:

python
@mcp.resource("schema://tables")
def schema() -> str:
    """Tabeller och kolumner i databasen."""
    rows = db.sql("SELECT table_name, column_name, data_type FROM information_schema.columns").fetchall()
    return "\n".join(f"{t}.{c} {d}" for t, c, d in rows)

Alternativet är att skriva schemat rakt in i docstringen på query. Testa gärna båda och se vilket modellen har lättast att använda.

Steg 3: Koppla servern till agenten

Claude Desktop. Låt FastMCP skriva konfigurationen, med ditt uv-projekt som miljö:

uv run fastmcp install claude-desktop server.py --project .

Starta om Claude Desktop helt (avsluta, starta igen). Servern dyker upp under verktygsikonen i chattrutan. Vill du se vad som skrevs så ligger det i ~/Library/Application Support/Claude/claude_desktop_config.json.

Claude Code. Registrera servern och starta en session i samma katalog:

sh
claude mcp add bolagshack -- uv run --project "$PWD" fastmcp run server.py
claude

Kör /mcp inne i sessionen för att se att den är ansluten.

Ställ sedan tre frågor på svenska, till exempel:

  • Vilka fem skills efterfrågas mest i uppdragen?
  • Vem i teamet matchar bäst mot ett uppdrag som kräver Python och Azure?
  • Var är gapet störst mellan vad marknaden vill ha och vad vi kan?

Titta på vilken SQL modellen skriver. Går det fel? Var? Ofta är svaret att den saknar information: en kolumn som heter något oväntat, ett format den inte förstår. Förbättra docstringen eller schemaresursen och fråga igen.

Extra: Typade verktyg istället för SQL

Ett query(sql)-verktyg ger modellen all makt, och allt ansvar. Prova motsatsen: ta bort det och skriv några smala verktyg med tydliga parametrar istället.

python
@mcp.tool
def top_skills(limit: int = 10) -> list[dict]:
    """De mest efterfrågade skillsen i uppdragen, flest först."""
    ...

@mcp.tool
def people_with_skill(skill: str, min_level: int = 1) -> list[dict]:
    """Konsulter som har en given skill på minst en viss nivå (1–5)."""
    ...

Ställ samma tre frågor igen. Vad blev bättre, vad blev sämre? Det här är den designfråga alla som bygger MCP-servrar brottas med, och det finns inget facit.

Extra: Prompts

Lägg till en prompt som paketerar en vanlig fråga, till exempel "matcha det här uppdraget mot teamet". I Claude Desktop dyker den upp som ett val i chattrutan.

python
@mcp.prompt
def match_assignment(description: str) -> str:
    """Hitta konsulterna som passar bäst för ett uppdrag."""
    return f"Här är en uppdragsbeskrivning:\n\n{description}\n\nVilka i teamet passar bäst, och varför?"

Bonus: Bortom din laptop

För den som blir klar snabbt eller gjort det här förut.

  • Wrappa ett API istället för en fil. Ett verktyg som slår mot Bolagsverket, SMHI, GitHub eller er egen tjänst. Samma mönster, annan datakälla.
  • HTTP-transport. mcp.run(transport="http") gör servern nåbar över nätverket istället för via stdin. Sätt den på en server och anslut den från claude.ai som en fjärrserver. Då kan hela teamet använda den.
  • Andra klienter. VS Code och Cursor pratar också MCP. Anslut samma server och se om agenterna beter sig olika.