wlo-mcp

janschachtschabel/wlo-mcp

3.1

If you are the rightful owner of wlo-mcp and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.

The WLO MCP Server is a TypeScript-based scaffold designed to query content from WirLernenOnline (WLO) using ngsearch. It provides a deterministic core logic and resources for efficient content retrieval.

Tools
2
Resources
0
Prompts
0

WLO MCP Server (TypeScript) – Scaffold

Deterministische Kernlogik und Ressourcen für einen MCP-Server, der Inhalte von WirLernenOnline (WLO) via ngsearch abfragt. Dieses Scaffold enthält:

  • MCP-Tool search (Freitext → Trefferliste von Bildungs- und Unterrichtsmaterial; nutzt intern searchContent und parseQuery)
  • MCP-Tool fetch (vollständiger Bildungsressourcen-Abruf via buildDocumentFromMetadata)
  • MCP-Tool search_content (deterministische Parameter → vollständiges Bildungsressourcen-Ergebnis)
  • MCP-Tool parse_query (Freitext → Parameterheuristik für Bildungsinhalte, Unterrichtsideen, Arbeitsblätter)
  • Ressourcen (Subjects, Bildungsstufen, Inhaltstypen, Licenses) als JSON
  • Prompt-Ressource resource://prompts/map_nl_to_filters

Der Streamable-HTTP MCP-Transport für Vercel ist enthalten (api/server.ts). SSE/Sessions sind in diesem Scaffold nicht aktiv (stateless Mode), können aber bei Bedarf ergänzt werden.

Quickstart

  1. Abhängigkeiten installieren:
npm i
  1. Demo ausführen (führt eine Beispielsuche aus und gibt die Anzahl der Treffer aus):
npm run dev
# oder
npm run demo

Optional: Umgebung anpassen (Standard ist das öffentliche WLO):

# .env
WLO_BASE_URL=https://redaktion.openeduhub.net

Kern-API (Programmatic)

Die deterministische Suchfunktion befindet sich in src/tools/search_content.ts und steht als MCP-Tool search_content bereit. Das gehostete Tool search ruft diese Funktion intern nach heuristischer Parameterermittlung auf, um Bildungs-, Unterrichts- oder Lernmaterial aller Art zu finden:

import { searchContent } from './tools/search_content';

const res = await searchContent({
  q: 'Klexikon',
  subject: 'Biologie',
  educational_context: 'Sekundarstufe I',
  media_type: 'Arbeitsblatt',
  page: 1,
  per_page: 10,
  content_type: 'FILES'
});

console.log(res.resolved_filters.subject?.uri); // → https://w3id.org/openeduhub/vocabs/subject/science
console.log(res.criteria);

Die Freitext-Parsing-Funktion (optional) in src/tools/parse_query.ts ist als MCP-Tool parse_query verfügbar und unterstützt search beim Ableiten von Bildungsressourcen-Filtern:

import { parseQuery } from './tools/parse_query';

const out = parseQuery({ query_text: 'Mathe 5. Klasse Arbeitsblatt von Klexikon' });
console.log(out.suggested_params, out.confidence, out.notes);

Ressourcen

  • resources/filters/subjects.json – deutsche Labels -> URIs (virtual:taxonid)
  • resources/filters/educational_contexts.json – Labels -> URIs (ccm:educationalcontext)
  • resources/filters/media_types.json – Labels -> URIs (ccm:oeh_lrt_aggregated)
  • resources/filters/licenses.json – Codes (noch nicht im search_content genutzt)
  • resources/prompts/map_nl_to_filters.txt

Nächste Schritte (MCP + Vercel)

  • MCP-Tools registrieren: search, fetch, search_content, parse_query (alle auf Bildungsinhalte ausgelegt).
  • Resource-Pfade als resource://filters/*.json und resource://prompts/map_nl_to_filters veröffentlichen.
  • HTTP/SSE-Transport für Serverless (Vercel) hinzufügen (API-Route api/server.ts).
  • CORS/Security und optional Auth.

Wenn du möchtest, setze ich das im nächsten Commit direkt um und liefere dir einen lauffähigen Remote-MCP-Endpoint für Vercel.

MCP Nutzung

Lokal per stdio

  1. Build
npm run build
  1. Start (stdio)
npm run stdio

Nutze z. B. den MCP Inspector, um dich via stdio zu verbinden.

Konvention: Quelle (source)
  • Setze den Parameter source nur, wenn die Quelle vom Nutzer explizit verlangt wird (z. B. „nur Klexikon“, „Quelle: Klexikon“, „von Klexikon“).
  • Im Regelfall nur q, subject, educational_context, media_type setzen.

Remote (Vercel, Streamable HTTP)

  • Deploye dieses Repo nach Vercel. Die Funktion api/server.ts exponiert einen Streamable HTTP Endpoint.
  • Endpoint (Rewrite): POST https://<dein-projekt>.vercel.app/mcp
  • Direkt: POST https://<dein-projekt>.vercel.app/api/server
  • Für Browser-basierte Clients werden CORS-Header gesetzt und Mcp-Session-Id exponiert.
  • In stateless Mode wird pro Request ein neuer Server/Transport erzeugt (SSE-Notifications nicht aktiv). Für die meisten Tool-Aufrufe ausreichend.

MCP-Client-Konfiguration (z. B. in einem Host, der Remote-HTTP unterstützt):

  • URL: https://<dein-projekt>.vercel.app/mcp
  • Direkt: https://<dein-projekt>.vercel.app/api/server
  • Transport: Streamable HTTP

Test mit MCP Inspector

  1. Inspector starten und Verbindung konfigurieren:
    • Modus „Remote HTTP“
    • URL: https://<dein-projekt>.vercel.app/mcp (oder direkt https://<dein-projekt>.vercel.app/api/server)
  2. Nach dem Connect sollten die Resources, Tools und der Prompt sichtbar sein:
    • Tools: search, fetch, search_content, parse_query
    • Resources: resource://filters/*, resource://prompts/map_nl_to_filters
  3. Beispiel-Toolaufruf:
{
  "q": "Klexikon",
  "subject": "Biologie",
  "media_type": "Arbeitsblatt",
  "educational_context": "Sekundarstufe I",
  "page": 1,
  "per_page": 10,
  "content_type": "FILES"
}

Bei unbekannten Labels bekommst du eine hilfreiche Fehlermeldung inklusive Liste erlaubter Werte.

Auth & Access Control

  • Default: Ohne OAuth-Konfiguration ist der Endpoint öffentlich zugänglich (kompatibel mit OpenAI „None“ und Claude „Unauthenticated“).
  • Optional OAuth 2.0 (Bearer Tokens):
    • Setze folgende Variablen (z. B. in den Vercel Project Settings):
      • OAUTH_ISSUER
      • OAUTH_TOKEN_ENDPOINT
      • OAUTH_JWKS_URL
      • Optional: OAUTH_AUDIENCE (expected aud Claim) und OAUTH_AUTHORIZATION_SERVERS (Komma-separiert für die Metadata-Ausgabe)
    • Der Server validiert jedes Authorization: Bearer <token> gegen das entfernte JWKS. Ungültige Tokens führen zu 401 plus WWW-Authenticate Header.
    • Metadata Endpoint: /.well-known/oauth-protected-resource (per vercel.json Rewrite → api/.well-known/oauth-protected-resource.ts).
    • Unterstützte Signaturen: RS256/RS384/RS512 sowie PS256/PS384/PS512 (RSA-basierte Tokens gängiger OAuth-Anbieter).
  • Ein dedizierter API-Key-Header ist nicht nötig und würde von einigen Hosts ignoriert werden.

OpenAI Connectors / Deep Research Kompatibilität

  • search

    • Input: { "query": string }
    • Output: Content mit genau einem Text-Item. Der Text ist ein JSON-String mit Struktur: { "results": [{ "id": string, "title": string, "url": string }] }
    • Verhalten in diesem Server: Freitext wird intern mit parse_query heuristisch interpretiert (Fach, Bildungsstufe, Inhaltstyp – Quelle nur bei explizitem Wunsch) und dann deterministisch über search_content bei WLO gesucht. Trigger dieses Tool, sobald im Gespräch Begriffe rund um Unterricht, Lernmaterial, Klassenarbeiten, Arbeitsblätter, Experimente, Lehrplan-Themen, Prüfungsvorbereitung, Projektideen oder ähnliche Bildungsbedarfe fallen – selbst wenn das Wort „Bildung“ nicht vorkommt.
    • Zusätzlich wird das Feld resolved_filters ausgegeben, welches zeigt, welche Label-zu-URI-Mappings verwendet wurden.
  • fetch

    • Input: { "id": string }
    • Output: Content mit genau einem Text-Item. Der Text ist ein JSON-String mit Struktur: { "id": string, "title": string, "text": string, "url": string, "metadata": object }
    • Verhalten in diesem Server: Lädt Metadaten für einen WLO-Knoten, nutzt sys:node-uuid als stabile ID, bevorzugt die offizielle Permalink-/www-URL und ergänzt das Metadatenobjekt um resolved:*-Felder (z. B. resolved:node-uuid, resolved:permalink). Nutze dieses Tool, wenn nach tieferen Details zu einer konkreten Ressource gefragt wird (z. B. „Zeig mir mehr über die Klexikon-Ressource zum Thema Regenwald“).
    • Verhalten in diesem Server: Lädt Metadaten für einen WLO-Knoten, nutzt sys:node-uuid als stabile ID, bevorzugt die offizielle Permalink-/www-URL und ergänzt das Metadatenobjekt um resolved:*-Felder (z. B. resolved:node-uuid, resolved:permalink).
  • search_content

    • Input: { "q"?: string, "subject"?: string, ... } (siehe src/tools/search_content.ts)
    • Output: Vollständige ngsearch-Antwort inkl. resolved_filters, criteria, Paginierung.
    • Verwendung: Für deterministische Aufrufe (z. B. Claude-Workflows, Power-User oder Tests).
  • parse_query

    • Input: { "query_text": string }
    • Output: { "suggested_params": object, "confidence": number, "notes": string }
    • Verwendung: Direkter Zugriff auf die Freitext-Heuristiken; wird von search intern bereits genutzt und eignet sich für Hosts, die eigenständig Unterrichts-/Lernanfragen (Arbeitsblätter, Lehrplan-Themen, Projektideen) in strukturierte Filter überführen wollen.

Beispiel (search → Ergebnisform):

{
  "results": [
    { "id": "abc-123", "title": "Kambodscha", "url": "https://redaktion.openeduhub.net/edu-sharing/components/render?nodeId=abc-123" }
  ]
}

Beispiel (fetch → Dokumentform):

{
  "id": "abc-123",
  "title": "Kambodscha",
  "text": "Beschreibung: ...\nFächer: ...\nLizenz: ...",
  "url": "https://redaktion.openeduhub.net/edu-sharing/components/render?nodeId=abc-123",
  "metadata": { "cclom:title": ["Kambodscha"], "ccm:license": ["CC-BY"], "...": ["..."] }
}