No description
  • JavaScript 70.8%
  • TypeScript 25.2%
  • Dockerfile 4%
Find a file
@s.roertgen 380e7baa6e
All checks were successful
build-image / build (push) Successful in 54s
align build-image workflow with edufeed-app pattern
Previous run (#99) pushed image layers successfully but got
cancelled because cache-to type=gha timed out dialing the GHA
cache sidecar at 172.24.0.2:36761 — that service is not
provisioned on this Forgejo runner. Runner label ubuntu-latest
was also not a clean match for the instance.

Switch to the pattern already working in edufeed-app:

- runs-on: docker-builder (matches the actual runner label)
- Shell-based docker login + buildx build (no docker/* actions)
- Cache via type=registry (cache stored as :buildcache tag in
  the same registry, no external cache service required)

Tag strategy preserved: :sha-<7char> always, plus :main + :latest
on default branch, plus :vX.Y.Z on tag pushes.
2026-04-21 12:37:35 +02:00
.forgejo/workflows align build-image workflow with edufeed-app pattern 2026-04-21 12:37:35 +02:00
src add Streamable HTTP transport and Docker deployment 2026-04-21 09:58:59 +02:00
.dockerignore add Streamable HTTP transport and Docker deployment 2026-04-21 09:58:59 +02:00
.env.example update sachsen graph 2026-02-26 16:11:18 +01:00
.gitignore initial commit 2026-01-30 14:01:15 +01:00
.mcp.json initial commit 2026-01-30 14:01:15 +01:00
analyse-literals-bundeslaender.md add project notes and Claude Code instructions 2026-04-21 09:59:15 +02:00
CLAUDE.md add project notes and Claude Code instructions 2026-04-21 09:59:15 +02:00
docker-compose.yml build and ship container image via Forgejo Actions 2026-04-21 12:16:10 +02:00
Dockerfile add Streamable HTTP transport and Docker deployment 2026-04-21 09:58:59 +02:00
package-lock.json add Streamable HTTP transport and Docker deployment 2026-04-21 09:58:59 +02:00
package.json add Streamable HTTP transport and Docker deployment 2026-04-21 09:58:59 +02:00
README.md build and ship container image via Forgejo Actions 2026-04-21 12:16:10 +02:00
tsconfig.json initial commit 2026-01-30 14:01:15 +01:00

MEM Ontology MCP Server

An MCP (Model Context Protocol) server for querying the MEM (Metadata for Education Media) ontology, which models German school curricula across federal states (Bundesländer).

Features

The server provides the following tools:

  1. sparql_query — Execute arbitrary SPARQL SELECT queries against the MEM triple store
  2. list_bundeslaender — List all German federal states available in the ontology
  3. list_schulfaecher — List all school subjects for a given state
  4. list_schularten — List all school types for a given state
  5. find_lehrplaene — Find curricula by state, optionally filtered by subject, school type, or grade level
  6. get_lehrplan_tree — Get the hierarchical structure of a Lehrplan (bounded by depth, default 2, max 10)
  7. get_children — Get direct children of a specific node (for drilling down into a branch)
  8. search — Full-text search across all Lehrplan nodes by keyword (uses Virtuoso bif:contains), with optional Bundesland filter

Installation

npm install
npm run build

Configuration

Environment Variables

All configuration is via a .env file in the project root (loaded automatically at startup). Copy the example and edit as needed:

cp .env.example .env

The server will fail at startup if required variables are missing.

Infrastructure graphs (required):

Variable Description
SPARQL_ENDPOINT SPARQL endpoint URL
GRAPH_ONTOLOGY Ontology graph URI
GRAPH_SCHULART Schulart graph URI
GRAPH_SCHULFACH Schulfach graph URI

State graphs (optional, add one per state):

Variable Description
GRAPH_STATE_<CODE> Graph URI for a state, e.g. GRAPH_STATE_SN, GRAPH_STATE_BY

State graphs are discovered dynamically — adding a new state requires only a new GRAPH_STATE_<CODE> entry in .env.

For Claude Code

Add a .mcp.json file in the project root:

{
  "mcpServers": {
    "mem-ontology": {
      "type": "stdio",
      "command": "node",
      "args": ["build/index.js"]
    }
  }
}

For Claude Desktop

Add to your MCP settings configuration file:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Linux: ~/.config/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "mem-ontology": {
      "command": "node",
      "args": ["/absolute/path/to/mem-ontologie-mcp/build/index.js"]
    }
  }
}

Usage Examples

Find biology curricula for Gymnasium in Sachsen

Tool: find_lehrplaene
Arguments: { "bundesland": "SN", "schulfach": "Biologie", "schulart": "Gymnasium" }

Browse the curriculum tree (depth-limited)

Tool: get_lehrplan_tree
Arguments: { "lehrplanUri": "https://lp-sachsen.org/resource/522", "depth": 2 }

Drill into a specific node

Tool: get_children
Arguments: { "nodeUri": "https://lp-sachsen.org/resource/7052" }

Search for a topic across all states

Tool: search
Arguments: { "query": "Wirbeltiere" }

Search within a specific state

Tool: search
Arguments: { "query": "Fische", "bundesland": "SN" }

State Codes

  • BW — Baden-Württemberg
  • BY — Bayern
  • BE — Berlin
  • BB — Brandenburg
  • HB — Bremen
  • HH — Hamburg
  • HE — Hessen
  • MV — Mecklenburg-Vorpommern
  • NI — Niedersachsen
  • NW — Nordrhein-Westfalen
  • RP — Rheinland-Pfalz
  • SL — Saarland
  • SN — Sachsen
  • ST — Sachsen-Anhalt
  • SH — Schleswig-Holstein
  • TH — Thüringen

Note: Currently, curriculum data is available for BY (Bayern), SN (Sachsen), and RP (Rheinland-Pfalz).

Architecture

  • Transport: stdio (default) or Streamable HTTP (--http flag)
  • SDK: @modelcontextprotocol/sdk with McpServer and zod schemas
  • Data source: SPARQL endpoint (configurable via SPARQL_ENDPOINT env var)
  • Runtime: Node.js, TypeScript, ES modules

Source files:

  • src/index.ts — Main MCP server with all tool registrations
  • src/http.ts — Streamable HTTP transport (Express server)
  • src/sparql.ts — SPARQL query execution and result formatting

Development

npm run build      # Compile TypeScript
npm start          # Run the server (stdio)
npm run start:http # Run the server (HTTP)

HTTP Mode

Run with --http flag (or npm run start:http) to expose the MCP server as an HTTP service using Streamable HTTP transport.

Configuration (via .env or environment variables):

Variable Default Description
PORT 3000 HTTP listen port
API_KEY (unset) Bearer token for auth. If unset, auth is disabled.

Authentication: When API_KEY is set, all /mcp requests must include Authorization: Bearer <key>. Without API_KEY, the server is open access.

Client connection example:

# Test with MCP Inspector
npx @modelcontextprotocol/inspector --url http://localhost:3000/mcp

# With authentication
npx @modelcontextprotocol/inspector --url http://localhost:3000/mcp --header "Authorization: Bearer your-secret-key-here"

Deployment (Docker)

A Dockerfile and docker-compose.yml are included for self-hosted deployment. The container runs the server in HTTP mode behind your own reverse proxy (TLS is expected to be terminated upstream).

Container images are built by Forgejo Actions on every push to main and every vX.Y.Z tag, then published to the Forgejo registry at git.edufeed.org/laoc/mem-mcp. Available tags: :latest, :main, :sha-<short>, :vX.Y.Z.

# One-time on the deploy host: authenticate to the Forgejo registry.
# Skip this step if the package is set to public in Forgejo.
docker login git.edufeed.org

# First-time setup: configure environment.
cp .env.example .env
# Set API_KEY to a random secret for production.

# Deploy / update.
docker compose pull
docker compose up -d

# Logs
docker compose logs -f

By default the container binds to 127.0.0.1:3000 on the host, so it's only reachable via a reverse proxy on the same machine. Adjust the ports: entry in docker-compose.yml if you need LAN exposure.

Building locally (development only)

docker build -t mem-ontology-mcp:dev .
docker run --rm -p 127.0.0.1:3000:3000 --env-file .env mem-ontology-mcp:dev

Reverse proxy example (Caddy)

mcp.example.org {
    reverse_proxy 127.0.0.1:3000
}

Reverse proxy example (nginx)

location /mcp {
    proxy_pass http://127.0.0.1:3000/mcp;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    # Streamable HTTP uses SSE for server->client messages.
    proxy_buffering off;
    proxy_read_timeout 3600s;
}

Clients then connect to https://mcp.example.org/mcp with Authorization: Bearer <API_KEY>.

License

Unlicense