skosmos-mcp
A read-only MCP server for browsing and querying SKOS vocabularies through Skosmos.
List vocabularies and get vocabulary metadata with top concepts.
Fetch concept details, including broader, narrower, and related links.
Retrieve all labels for a concept: preferred, alternative, and hidden labels.
Get a concept’s hierarchy path to the root via broader relations.
Full-text search concepts across one or all vocabularies, with pagination.
Autocomplete labels by prefix and resolve label text to concept URIs.
BFS-traverse broader, narrower, related, or mixed relationships up to a configurable depth.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@skosmos-mcp search for 'climate' in the yso vocabulary"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
skosmos-mcp
A production-quality Model Context Protocol (MCP) server that wraps the Skosmos REST API, enabling AI assistants to navigate and query SKOS vocabularies. Also includes SPARQL query capabilities for direct RDF data access.
Features
17 MCP tools covering vocabulary browsing, concept lookup, full-text search, label resolution, BFS traversal, and schema-guided assistance
4 SPARQL tools for direct SPARQL query execution, updates, graph discovery, and query templates
3 MCP resources for direct URI-based access to vocabularies and concepts
BFS traversal engine with configurable depth cap, cycle detection, and duplicate elimination
TTL-based in-memory cache to avoid redundant API calls
Retry logic with exponential backoff for 5xx and network errors
AbortController timeout on every HTTP request
Strict TypeScript (strict mode,
noUncheckedIndexedAccess,exactOptionalPropertyTypes)Zod-validated inputs on all tools
stdio transport — reads from stdin, writes to stdout; all logging goes to stderr
StreamableHTTP transport — HTTP server at
/mcpfor remote or web-based MCP clients
Related MCP server: skill4agent MCP Server
Installation
npm install
npm run buildOr run directly with tsx:
npm run devDocker / Docker Compose
Build and run the Streamable HTTP MCP server in a container:
docker compose up --build -dThis starts the Streamable HTTP MCP server on port 3000 and uses Docker Compose's restart: unless-stopped policy so it will come back up automatically after crashes. The image defaults to the Finto endpoints, runs the HTTP MCP server on 0.0.0.0:3000, and enables alternate Skosmos/SPARQL connections by default. The container logs a warning at startup when those options are enabled because allowing other endpoints can be a security risk. The container reads the same environment variables as the local app, so copy .env.example to .env if you want to override those defaults.
Container Images from GitHub Container Registry
Releases publish two container image variants to GitHub Container Registry (GHCR):
HTTP variant (for remote access via HTTP):
docker pull ghcr.io/jsilvanus/skosmos-mcp:http
docker pull ghcr.io/jsilvanus/skosmos-mcp:http-latest
# Or use a specific release version:
docker pull ghcr.io/jsilvanus/skosmos-mcp:v0.2.1-httpStdio variant (for local stdio MCP protocol):
docker pull ghcr.io/jsilvanus/skosmos-mcp:stdio
docker pull ghcr.io/jsilvanus/skosmos-mcp:stdio-latest
# Or use a specific release version:
docker pull ghcr.io/jsilvanus/skosmos-mcp:v0.2.1-stdioEach release publishes both variants automatically. Choose the one that matches your use case:
HTTP variant: Runs an HTTP server on port 3000, suitable for remote access or web-based MCP clients
Stdio variant: Uses stdin/stdout for the MCP protocol, suitable for local integration with AI assistants or other MCP clients
Configuration
Copy .env.example to .env and fill in values:
SKOSMOS_BASE_URL=https://api.finto.fi # required
SKOSMOS_DEFAULT_VOCABULARY= # optional
SKOSMOS_DEFAULT_LANGUAGE=en
SKOSMOS_TIMEOUT=30000
SKOSMOS_USER_AGENT=skosmos-mcp/0.2.0
SKOSMOS_CACHE_TTL=300
SKOSMOS_MAX_TRAVERSAL_DEPTH=5
SKOSMOS_TOOL_SERVER_URL_ALLOWED=true
# SPARQL Configuration (optional)
SPARQL_ENDPOINT_URL=https://api.finto.fi/sparql
SPARQL_USERNAME=
SPARQL_PASSWORD=
SPARQL_ALLOW_OTHER_ENDPOINTS=trueVariable | Default | Description |
| (required) | Base URL of the Skosmos instance |
| — | Default vocabulary id when not specified in a tool call |
|
| Default language code for labels |
|
| HTTP request timeout in milliseconds |
|
| User-Agent header sent with REST and SPARQL requests. Must start with |
|
| Cache entry TTL in seconds |
|
| Hard cap on BFS traversal depth |
|
| When |
|
| Log level: debug, info, warn, error (written to stderr) |
|
| TCP port for the StreamableHTTP server |
|
| Bind address for the StreamableHTTP server |
| — | SPARQL endpoint URL (optional; enables SPARQL tools) |
| — | Username for SPARQL endpoint HTTP Basic auth (optional) |
| — | Password for SPARQL endpoint HTTP Basic auth (optional) |
|
| When |
HTTP transport only — OAuth with single sign-on (see OAuth and OIDC sign-in). All of these are ignored while OIDC_ISSUER is unset, and the stdio server never reads them.
Variable | Default | Description |
| — | Issuer URL exactly as the IdP publishes it (authentik: |
| — | Required when |
| — | Set = confidential client ( |
|
| Must contain |
|
| Text of the sign-in button |
| — | Required when |
| — | Required when |
| — | Express |
With NODE_ENV=production (the default in the Docker image) OIDC_ISSUER and MCP_PUBLIC_URL must be https:. Invalid values stop the server at startup with a clear error. OIDC_CREATE_USERS and OIDC_TRUST_EMAIL, used by other MCP apps of this family, do not apply here: there are no local users.
MCP Tools Reference
Vocabulary Tools
list_vocabularies
List all available vocabularies.
Parameter | Type | Required | Description |
| string | no | Language code for labels |
get_vocabulary
Get vocabulary metadata and top concepts.
Parameter | Type | Required | Description |
| string | yes | Vocabulary identifier (e.g. |
| string | no | Language code |
Concept Tools
get_concept
Fetch full concept details: labels, broader, narrower, related.
Parameter | Type | Required | Description |
| URL | yes | Concept URI |
| string | no | Vocabulary identifier (required if no default set) |
| string | no | Language code |
get_concept_label
Get all labels for a concept URI.
Parameter | Type | Required | Description |
| URL | yes | Concept URI |
| string | yes | Vocabulary identifier |
| string | no | Language code |
concept_path
Get the hierarchy path from a concept to its root.
Parameter | Type | Required | Description |
| URL | yes | Concept URI |
| string | yes | Vocabulary identifier |
| string | no | Language code |
Search Tools
search_concepts
Full-text search across one or all vocabularies.
Parameter | Type | Required | Description |
| string | yes | Search string (supports trailing |
| string | no | Limit to this vocabulary |
| string | no | Language code |
| integer | no | Max results |
| integer | no | Pagination offset |
autocomplete
Autocomplete concept labels by prefix.
Parameter | Type | Required | Description |
| string | yes | Label prefix |
| string | no | Limit to this vocabulary |
| string | no | Language code |
| integer | no | Max suggestions |
resolve_label
Resolve a label text to concept URIs.
Parameter | Type | Required | Description |
| string | yes | Label text to resolve |
| string | yes | Vocabulary identifier |
| string | no | Language code |
Labels Tool
labels
Get all labels (prefLabel, altLabel, hiddenLabel) for a concept URI.
Parameter | Type | Required | Description |
| URL | yes | Concept URI |
| string | yes | Vocabulary identifier |
| string | no | Language code |
Traversal Tools
All traversal tools use BFS with cycle detection. Depth is capped at Math.min(depth, SKOSMOS_MAX_TRAVERSAL_DEPTH).
broader_concepts
Traverse broader (parent) concepts.
Parameter | Type | Required | Description |
| URL | yes | Starting concept URI |
| string | yes | Vocabulary identifier |
| integer | no | Max traversal depth |
| string | no | Language code |
narrower_concepts
Traverse narrower (child) concepts.
Parameter | Type | Required | Description |
| URL | yes | Starting concept URI |
| string | yes | Vocabulary identifier |
| integer | no | Max traversal depth |
| string | no | Language code |
related_concepts
Traverse related concepts.
Parameter | Type | Required | Description |
| URL | yes | Starting concept URI |
| string | yes | Vocabulary identifier |
| integer | no | Max traversal depth |
| string | no | Language code |
traverse_concepts
BFS using a mix of relationship types.
Parameter | Type | Required | Description |
| URL | yes | Starting concept URI |
| string | yes | Vocabulary identifier |
| array | yes | One or more of: |
| integer | no | Max traversal depth |
| string | no | Language code |
Assistance Tools
vocabulary_schema_overview
Summarize a vocabulary's structure with top concepts, relationship hints, and suggested tasks for AI clients.
Parameter | Type | Required | Description |
| string | yes | Vocabulary identifier |
| string | no | Language code |
| boolean | no | Whether to include a top concept preview |
| integer | no | Maximum number of top concept previews |
query_guidance
Return task-oriented guidance for common SKOS vocabulary workflows such as exploration, hierarchy traversal, or label resolution.
Parameter | Type | Required | Description |
| string | yes | Vocabulary identifier |
| string | no | One of |
reconcile_concept
Resolve a label to one or more candidate concepts using Skosmos lookup and search.
Parameter | Type | Required | Description |
| string | yes | Label text to resolve |
| string | yes | Vocabulary identifier |
| string | no | Language code |
| string | no | Optional concept type filter |
| integer | no | Maximum number of matches |
suggest_sparql_templates
Return SKOS-oriented SPARQL templates for exploration, hierarchy tracing, labels, and related concepts.
Parameter | Type | Required | Description |
| string | no | Optional vocabulary identifier to include in the response |
| string | no | One of |
SPARQL Tools
SPARQL tools enable direct querying of RDF data. Set SPARQL_ENDPOINT_URL environment variable to enable these tools. Supports both SPARQL 1.1 Query and Update protocols, with optional HTTP Basic authentication.
See the Attribution section for licensing details about the SPARQL implementation.
execute_sparql_query
Execute a SPARQL query (SELECT, CONSTRUCT, ASK, DESCRIBE) against the configured endpoint.
Parameter | Type | Required | Description |
| string | yes | The SPARQL query to execute |
| URL | no | Optional custom SPARQL endpoint (overrides default) |
Example Query:
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
SELECT ?concept ?label
WHERE {
?concept a skos:Concept ;
skos:prefLabel ?label .
}
LIMIT 10execute_sparql_update
Execute a SPARQL update query (INSERT, DELETE, etc.) against the configured endpoint.
Parameter | Type | Required | Description |
| string | yes | The SPARQL update query to execute |
| URL | no | Optional custom SPARQL endpoint (overrides default) |
Example Update:
PREFIX ex: <http://example.org/>
INSERT DATA {
ex:subject1 ex:predicate1 "object1" .
}list_sparql_graphs
List all available named graphs in the SPARQL endpoint.
Parameter | Type | Required | Description |
| URL | no | Optional custom SPARQL endpoint (overrides default) |
Returns: JSON array of graph URIs.
sparql_query_templates
Get pre-built SPARQL query templates for common data exploration patterns.
Parameter | Type | Required | Description |
| string | yes | Template category: |
Categories:
exploration— Basic data discovery and statisticsproperty-paths— Complex graph navigation using SPARQL property pathsstatistics— Knowledge graph metrics and analysisvalidation— Data quality and consistency checksschema— Structure discovery and ontology exploration
MCP Resources
Resources are read-only and addressable by URI. skosmos://{vocid} and skosmos://{vocid}/{encodedUri} list their entries where the server can enumerate them.
URI Pattern | Description |
| JSON list of all vocabularies |
| Vocabulary metadata for |
| Concept data (labels, broader, narrower, related). |
| All labels (prefLabel, altLabel, hiddenLabel) for a concept |
| Structure summary of a vocabulary: entry points, relationship hints, suggested tasks |
| SKOS SPARQL query templates; |
| SPARQL query, update and named graph reference (Markdown) |
MCP Prompts
Prompt | Arguments | Description |
|
| Curated guidance for common SKOS tasks |
Deprecations
Several read tools are deprecated in favour of the resources and prompt above. They still work, and their descriptions start with DEPRECATED. They will be removed in 1.0:
list_vocabularies,get_vocabulary,get_concept,get_concept_label,labelsvocabulary_schema_overview,query_guidance,suggest_sparql_templates,sparql_query_templatesbroader_concepts,narrower_concepts,related_concepts(usetraverse_conceptswithrelationships)
execute_sparql_update is marked destructive (destructiveHint). Read-only tools are marked readOnlyHint.
Traversal Examples
Get all ancestors of a concept (depth 3)
{
"tool": "broader_concepts",
"args": {
"uri": "http://www.yso.fi/onto/yso/p8966",
"vocabulary": "yso",
"depth": 3,
"lang": "en"
}
}Response includes nodes (with depth), edges (directed relationships), rootUri, and maxDepth.
Mixed traversal (broader + related)
{
"tool": "traverse_concepts",
"args": {
"uri": "http://www.yso.fi/onto/yso/p8966",
"vocabulary": "yso",
"relationships": ["broader", "related"],
"depth": 2
}
}Using Optional Server URL Parameter
All 13 MCP tools support an optional server_url parameter. When SKOSMOS_TOOL_SERVER_URL_ALLOWED=true is set in the environment, you can pass a server_url parameter to any tool to make it query a different Skosmos instance instead of the configured SKOSMOS_BASE_URL.
Example: Query a different Skosmos instance
{
"tool": "get_concept",
"args": {
"uri": "http://www.yso.fi/onto/yso/p8966",
"vocabulary": "yso",
"lang": "en",
"server_url": "https://alternative-skosmos.example.org"
}
}This allows a single MCP session to interact with multiple Skosmos instances. The server_url parameter is:
Optional on all tools
Ignored unless
SKOSMOS_TOOL_SERVER_URL_ALLOWED=true(default:false)Can be any valid URL pointing to a Skosmos instance with a compatible REST API
Why use this feature?
Query multiple Skosmos instances in parallel within a single session
Test against different Skosmos servers without restarting the MCP
Support scenarios where vocabularies are distributed across multiple instances
stdio (standard MCP deployment)
SKOSMOS_BASE_URL=https://skosmos.example.org node dist/index.jsStreamableHTTP
SKOSMOS_BASE_URL=https://skosmos.example.org MCP_HTTP_PORT=3000 node dist/http.jsThe server listens on http://<MCP_HTTP_HOST>:<MCP_HTTP_PORT>/mcp (default: http://127.0.0.1:3000/mcp).
Each POST request is handled as a stateless MCP session (no session ID). The SkosmosClient and CacheManager instances are shared across requests for the lifetime of the process.
OAuth and OIDC sign-in (HTTP only)
By default the HTTP server has no authentication. Set OIDC_ISSUER (plus OIDC_CLIENT_ID, MCP_PUBLIC_URL and JWT_SECRET) to require sign-in through your identity provider (e.g. authentik). The server then acts as:
an OAuth authorization server for MCP clients, following the MCP authorization spec: CIMD client ids (the
client_idis anhttps:URL of the client's metadata document), authorization code + S256 PKCE at/oauth/authorizeand/oauth/token, refresh tokens, and JWT access tokens bound toiss=MCP_PUBLIC_URLandaud=<MCP_PUBLIC_URL>/mcp;an OAuth resource server for
/mcp: requests without a valid access token get401withWWW-Authenticate: Bearer resource_metadata="<MCP_PUBLIC_URL>/.well-known/oauth-protected-resource/mcp";an OIDC Relying Party toward the IdP. It never issues ID tokens and is not an OpenID Provider.
Discovery: /.well-known/oauth-protected-resource/mcp (RFC 9728, also at /.well-known/oauth-protected-resource), /.well-known/oauth-authorization-server and the /.well-known/openid-configuration alias of the same OAuth metadata.
Flow: the MCP client opens /oauth/authorize; the page shows a single sign-on button, which goes to /oidc/login and on to the IdP (state, nonce and PKCE; the state is also kept in a short-lived skosmos_oidc cookie against login CSRF). The IdP returns to /oidc/callback, the ID token is verified, and a consent page (Approve/Deny) finishes the authorization. There is no password sign-in.
There are no local user accounts: the signed-in identity is the IdP issuer + sub, carried in the access token (sub, idp_iss). Who may sign in is decided by the IdP, e.g. authentik's application bindings/policies; this server has no allow-list of its own.
State: pending sign-ins (10 min) and authorization codes (60 s) are kept in memory, as this server has no database; a restart only interrupts sign-ins in progress. Refresh tokens (30 days) and access tokens (1 hour) are self-contained signed JWTs, so they survive restarts; revoke them all by rotating JWT_SECRET. Run a single instance (or sticky sessions) when OIDC is on.
authentik setup
Applications → Providers → Create → OAuth2/OpenID Provider: client type Confidential, redirect URI
<MCP_PUBLIC_URL>/oidc/callback(strict), and a signing key so ID tokens are RS256.Applications → Create an application that uses this provider; bind the users/groups who may use the MCP server.
Copy the provider's OpenID Configuration Issuer (
https://auth.example.org/application/o/<slug>/) toOIDC_ISSUER, and the client ID/secret toOIDC_CLIENT_ID/OIDC_CLIENT_SECRET.
OIDC_ISSUER=https://auth.example.org/application/o/skosmos-mcp/ \
OIDC_CLIENT_ID=... OIDC_CLIENT_SECRET=... \
MCP_PUBLIC_URL=https://skosmos-mcp.example.org \
JWT_SECRET=$(openssl rand -base64 32) \
node dist/http.jsClaude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"skosmos": {
"command": "node",
"args": ["/path/to/skosmos-mcp/dist/index.js"],
"env": {
"SKOSMOS_BASE_URL": "https://skosmos.example.org",
"SKOSMOS_DEFAULT_LANGUAGE": "en"
}
}
}
}Development
npm run dev # run with tsx (no build)
npm run typecheck # check types without emitting
npm run test # run tests
npm run test:watch # watch mode
npm run build # compile to dist/
npm run lint # lint src/ and tests/Architecture
MCP Client (AI Assistant)
│ stdio (JSON-RPC)
▼
McpServer (SDK)
├── 17 Tools (Zod-validated)
└── 3 Resources
│
┌────┴────┐
│ │
TraversalEngine CacheManager
(BFS + cycle (TTL, per-type)
detection)
│
SkosmosClient
(fetch + retry
+ timeout)
│
Skosmos REST APIKey Design Decisions
No global mutable state: config, client, cache, and traversal engine are created once in
src/index.tsand passed via dependency injection.BFS traversal: uses a queue (not recursion) to ensure breadth-first ordering and avoid stack overflows.
Depth capping:
Math.min(requestedDepth, config.maxTraversalDepth)is applied in both the traversal engine and tool handlers.Cache keys include all relevant parameters:
vocabulary:${vocid}:${lang},label:${vocab}:${uri}:${lang}, etc.All logging to stderr — stdout is reserved exclusively for MCP JSON-RPC.
HTTP app:
src/http-app.tsbuilds the Express app (used bysrc/http.tsand the tests).src/auth/holds the optional OAuth authorization/resource server and OIDC Relying Party, ported from the jsilvanus/codestashmcp/api-connector-stylescaffold (Fastify) to Express; it is mounted only whenOIDC_ISSUERis set. Tests run it against a fake OIDC provider (tests/helpers/fake-oidc-provider.ts).
License
This project is licensed under the MIT License - see the LICENSE file for details.
Attribution
SPARQL functionality in this project is derived from ramuzes/mcp-jena and is used under the MIT License.
Available Tools
13 toolsautocompleteB
Autocomplete concept labels by prefix
Inputs: prefix (required), lang, maxhits, vocabulary.
broader_conceptsA
BFS traversal of broader (parent) concepts up to a specified depth
Inputs: uri (required), vocabulary (required), lang, depth.
concept_pathA
Get the hierarchy path from a concept to its root via broader transitive relations
Inputs: uri (required), vocabulary (required), lang.
get_conceptC
Get full concept details including broader, narrower, and related concepts
Inputs: uri (required), lang, vocabulary.
get_concept_labelB
Get all labels (prefLabel, altLabel, hiddenLabel) for a concept URI
Inputs: uri (required), vocabulary (required), lang.
get_vocabularyC
Get details of a specific vocabulary including top concepts
Inputs: id (required), lang.
labelsA
Get all labels for a concept URI in a vocabulary
Inputs: uri (required), vocabulary (required), lang.
list_vocabulariesC
List all available vocabularies in the Skosmos instance
Inputs: lang.
narrower_conceptsB
BFS traversal of narrower (child) concepts down to a specified depth
Inputs: uri (required), vocabulary (required), lang, depth.
resolve_labelC
Resolve a label text to concepts in a vocabulary
Inputs: text (required), vocabulary (required), lang.
search_conceptsA
Full-text search for concepts across one or all vocabularies
Inputs: query (required), lang, offset, maxhits, +1 more.
traverse_conceptsB
BFS traversal using a mix of broader, narrower, and/or related relationships
Inputs: uri (required), vocabulary (required), relationships (required), lang, +1 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 13 tools
Most tools have distinct purposes, but 'get_concept_label' and 'labels' overlap significantly, both retrieving labels for a concept URI. Additionally, 'broader_concepts' and 'traverse_concepts' can be confused since traverse supports broader traversal. This creates some ambiguity for an agent.
Tool names mostly follow a consistent verb_noun pattern (e.g., list_vocabularies, get_concept, resolve_label). However, 'autocomplete' is a single word and 'concept_path' is noun_noun, breaking the pattern. The overall structure is clear despite these minor deviations.
With 13 tools, the server is well-scoped for SKOS vocabulary browsing. It covers listing, searching, detail retrieval, and hierarchy traversal without being overwhelming. The count is appropriate for its purpose.
The tool surface covers core SKOS operations: vocabulary listing, concept details, labels, hierarchy traversal (broader, narrower, related), and search (autocomplete, full-text, label resolution). Missing are concept creation/modification (likely out of scope) and some non-core features like concept collections, but the browsing workflow is complete.
Maintenance
Related MCP Connectors
UMLS MCP — wraps the NLM UMLS Terminology Services REST API (uts-ws.nlm.nih.gov/rest)
OpenAlex MCP — wraps the OpenAlex API (scholarly works, free, no auth)
Search and read the aicoolies catalog of AI and developer tools via a remote MCP knowledge graph.
Search and discover Agent Skills from the skills.sh registry. Powered by HAPI MCP server.
Related MCP Servers
- AlicenseAqualityDmaintenanceAn MCP server that enables AI-powered exploration of RDF data and SPARQL querying via RDF4J. It provides tools for executing queries, searching knowledge graph resources, and retrieving schema summaries.131MIT
- AlicenseBqualityCmaintenanceMCP (Model Context Protocol) Server for skill4agent - Search, view, and install AI skills in AI conversations.325 npm4MIT
- AlicenseNot gradedqualityDmaintenanceBridges Web of Things (WoT) devices to AI assistants via MCP, enabling discovery, monitoring, and control of IoT devices through natural language.10GPL 3.0
- AlicenseAqualityCmaintenanceMCP server exposing SPARQL query functionalities for LLMs, enabling query execution, validation, and graph exploration across SPARQL endpoints.738 PyPIMIT