Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ConceptRouter

A zero-infra MCP server that turns an Open Knowledge Format (OKF) bundle — a directory of markdown "concept" files with YAML frontmatter — into fast, lexical-first concept discovery for LLM agents. No vector DB, no embeddings, no GPU: discovery runs entirely in Python memory via BM25F + Token-Set Jaccard + Levenshtein, fused with Reciprocal Rank Fusion (RRF).

ConceptRouter is a sibling project to SpecRouter (which does the same thing for OpenAPI specs -> callable tools). It reuses SpecRouter's retrieval core, but most OKF concepts are knowledge documents, not callable actions — there's no generic execute_tool that pretends every discovered concept can be "called" (see CLAUDE.md for why that would be a category error). The one deliberate exception is execute_concept, described below.

How it works

ConceptRouter exposes three MCP tools and one MCP resource:

  • discover_concepts(query) — natural-language search over the bundle. Returns up to top_k concept summaries (id, type, title, description, tags, resource, score, read_uri, executable).
  • concept://{path} (MCP resource) — the full markdown (frontmatter + body) for one concept, addressed by the id/read_uri a discovery hit gave you.
  • execute_concept(concept_id, arguments) — for concepts whose executable field is true (they declare their own method/path/params frontmatter — producer-defined extra fields OKF explicitly allows), makes the live HTTP call and returns {status, contentType, content}. Concepts without those fields (the vast majority) return a structured 400 — read them via concept:// instead. Requires CONCEPTROUTER_BASE_URL (concepts carry relative paths, not a base URL).
  • refresh_index() — force a re-fetch (git pull, for a git-hosted bundle) + rebuild + persist.

The intended agent flow: call discover_concepts, then either fetch read_uri (knowledge) or call execute_concept with the hit's id (when executable is true) — never both blended into one tool.

Quickstart

pip install -e ".[dev,fast]"     # dev = pytest, fast = rapidfuzz accelerator (optional)

export CONCEPTROUTER_BUNDLE_SOURCE=sample/okf-sample
conceptrouter index               # prebuild + persist the index
conceptrouter serve                # start the MCP server over stdio

Point an MCP client at it (e.g. in mcp.json):

{
  "mcpServers": {
    "conceptrouter": {
      "command": "conceptrouter",
      "args": ["serve"],
      "env": { "CONCEPTROUTER_BUNDLE_SOURCE": "sample/okf-sample" }
    }
  }
}

Bundle sources

CONCEPTROUTER_BUNDLE_SOURCE is either:

  • a local directory path — any directory of OKF concept .md files, walked recursively; or

  • a git repogit+<url>[#ref][:subdir], cloned (shallow) into the cache dir and re-pulled on refresh_index. For example, to point at the okf-viewer example bundle:

    export CONCEPTROUTER_BUNDLE_SOURCE=git+https://github.com/saschb2b/okf-viewer.git#main:docs

Sample data

  • sample/okf-sample/ — a small hand-written bundle (tables/metrics/runbooks/one API concept) used by the test suite.

  • sample/petstore-api/ — a larger, generated bundle: 29 endpoints across 7 categories (accounts/, finance/, pets/, monitoring/, orders/, notifications/, auth/), each concept carrying method/path/params frontmatter and directly callable via execute_concept. Generated from sample/petstore_openapi.json (an OpenAPI spec, in the same spirit as SpecRouter's sample/petstore.json) via:

    python scripts/openapi_to_okf.py sample/petstore_openapi.json sample/petstore-api

    scripts/openapi_to_okf.py works on any OpenAPI spec, not just this sample — it groups operations into category folders by their first tag and emits one concept per operation.

Retrieval

Same fielded BM25F + Jaccard + Levenshtein + RRF stack as SpecRouter, retargeted to concept fields instead of endpoint fields:

Field Role
title, type High-weight, low length-normalization — short, high-signal identity fields. Also the two fields Levenshtein fuzzy-matches against.
tags, headings Mid-weight structural signal.
description, body Lower weight, high length-normalization — long free text.

Typo tolerance comes from BM25F's fuzzy term expansion (CONCEPTROUTER_FUZZY_*) plus a down-weighted standalone Levenshtein vote in RRF (CONCEPTROUTER_RRF_WEIGHT_LEVENSHTEIN=0.5 by default) — the same tuning SpecRouter arrived at, since it's the same fusion math over a different field set.

Config reference

Required: CONCEPTROUTER_BUNDLE_SOURCE. Common: CONCEPTROUTER_CACHE_DIR, CONCEPTROUTER_TOP_K, CONCEPTROUTER_RRF_K, CONCEPTROUTER_RRF_WEIGHT_{BM25F,JACCARD,LEVENSHTEIN}. Fuzzy expansion: CONCEPTROUTER_FUZZY_EXPAND|MAX_RATIO|MIN_TOKEN_LEN|WEIGHT_POWER. Logging (rotating file, never stdout): CONCEPTROUTER_LOG_DIR|LEVEL|MAX_BYTES|BACKUP_COUNT|CONSOLE|ARGS. execute_concept bridge: CONCEPTROUTER_BASE_URL (required for it to work — concepts carry relative paths) and CONCEPTROUTER_AUTH_MODE + matching credential vars — same shape as SpecRouter's.

config.load_settings auto-loads a .env file (./.env or $CONCEPTROUTER_ENV_FILE); real env vars override file values.

Tests

pytest   # no network — conftest.py builds an in-memory bundle from sample/okf-sample

Status

All three tools are live: discover_concepts, execute_concept, refresh_index, plus the concept://{path} resource. execute_concept only works for concepts that declare themselves callable (producer-defined method/path/params frontmatter — see ConceptRecord.is_executable); everything else — the vast majority of OKF concepts — stays read-only via concept://, by design.

About

A zero-infra MCP server that turns an Open Knowledge Format (OKF) bundle into fast, lexical-first knowledge discovery for LLM agents — no vector DB, no embeddings by default.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages