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:
- Förstå MCP. Vad protokollet är och hur delarna hänger ihop.
- Servera riktig data. Bygg en server som ger agenten tillgång till din data.
- 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:
git switch main
git pullGö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:
mkdir min-egen-mcp && cd min-egen-mcp
uv init --bare
uv add fastmcp duckdbVerifiera:
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:
- 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. - 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. - 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:
- Committa det du har till din egen gren och pusha. Oavsett hur långt det kom.
- Skriv en rad i
#bolagshackpå 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:
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:
duckdb hack.dbCREATE 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:
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:
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:
@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:
claude mcp add bolagshack -- uv run --project "$PWD" fastmcp run server.py
claudeKö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.
@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.
@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.