Skip to main content

glossary-mcp

CI

A small, general-purpose MCP server that lets an AI read, search, and safely edit a glossary file in the conlangkit glossary format — for any glossary, not tied to any one project.

The glossary is a Markdown pipe-table with four columns:

lemma | tags | definition | notes
----- | ---- | ---------- | -----
core  | tier | the constitutive tier-1 material of a party | never shared

Writes go through conlangkit, which owns the file: it keeps entries sorted (by byte order) and re-emits the glossary on save. On an already-sorted file that yields a minimal diff — only the touched row moves. The file is re-read from disk before each write (so a long-lived server picks up external hand-edits) and the write is atomic (temp file + os.replace).

Prerequisites

  • Python 3.11+
  • uv
  • A glossary directory or file, pointed to by GLOSSARY_PATH

conlangkit (the glossary format library) is a normal PyPI dependency, pulled in by uv sync.

MCP SDK compatibility

glossary-mcp mcp SDK
0.2.0+ 2.x
0.1.1 1.x (pinned <2)
0.1.0 broken — do not use

0.1.0 declared only mcp>=1.0, so once mcp 2.0 was released a fresh resolve pulled it in — and 2.0 removed mcp.server.fastmcp. The server then died at import with ModuleNotFoundError, which a client sees only as a server offering no tools at all (a crashed stdio server cannot report why). If you are pinned to 0.1.0, upgrade — restarting will not help. 0.1.1 pinned mcp<2 as a stopgap; 0.2.0 ports to the 2.x API proper.

Installation

uv sync   # creates .venv, installs glossary-mcp (editable) + conlangkit + dev deps

Running tests

uv run pytest   # runs the suite with branch coverage (gate: 100%)

The smoke tests run without GLOSSARY_PATH. Integration tests run against a fixture glossary.

Running the server

export GLOSSARY_PATH=/path/to/glossary        # a directory containing glossary.md …
export GLOSSARY_PATH=/path/to/glossary.md     # … or a direct path to the file
uv run glossary-mcp

GLOSSARY_PATH may be a directory (the server looks for glossary.md inside it) or a file. The server uses stdio transport. For a local checkout, set command to a resolvable path, e.g. the project's /path/to/glossary-mcp/.venv/bin/glossary-mcp:

{
  "mcpServers": {
    "glossary": {
      "command": "glossary-mcp",
      "env": {
        "GLOSSARY_PATH": "/path/to/glossary"
      }
    }
  }
}

For consuming the published package from another repo without a local checkout or absolute paths, use uvx — see the next section.

Consuming from another repo (portable .mcp.json)

glossary-mcp is published to PyPI, so any other repo can wire it into its MCP configuration with no local checkout, no venv, and no absolute paths. uvx fetches and caches the package (and its conlangkit / mcp dependencies) on demand:

{
  "mcpServers": {
    "glossary": {
      "type": "stdio",
      "command": "uvx",
      "args": ["glossary-mcp==0.3.0"],
      "env": {
        "GLOSSARY_PATH": "${CLAUDE_PROJECT_DIR:-.}/../glossary"
      }
    }
  }
}
  • command: "uvx", args: ["glossary-mcp"] — the console-script name equals the package name, so no --from flag is needed. uvx resolves the package from PyPI into an ephemeral, cached environment on first launch.
  • GLOSSARY_PATH is the only required environment variable. It may point at a directory (the server looks for glossary.md inside) or directly at a file. The ${CLAUDE_PROJECT_DIR:-.}/../glossary value assumes the standard sibling layout where the consuming repo and its glossary repo are checked out side by side. ${CLAUDE_PROJECT_DIR} resolves the project root and requires Claude Code ≥ v2.1.195; the :-. fallback keeps it working (relative to the current directory) on older versions and other MCP clients.
  • Pin the version while pre-1.0. Because the tool surface may still change before 1.0, pin so a new release can't reach every consuming repo at once and break them in lockstep: "args": ["glossary-mcp==0.3.0"]. Bump deliberately (repo by repo, or via your propagation tooling). Float the version (bare "glossary-mcp", always latest) only once the surface stabilizes at ≥ 1.0.

Upgrading 0.2.0 → 0.3.0 is a breaking change. search_by_definition and search_notes are gone, replaced by a single search (see Searching). search("verdict") covers what search_by_definition("verdict") did, and search("n:verdict") covers search_notes. Before bumping the pin, grep your prompts, skills, and agent docs for the old tool names — a doc that tells a model to call search_by_definition will send it after a tool that no longer exists.

Tools

Tool Description
lookup_term Exact lemma lookup → tags, definition, notes
search Search lemmas, tags, definitions, and notes (see below)
glossary_stats Entry count, tag histogram, unique-tag count
list_terms All lemmas, optionally filtered by tag
add_term Append a new entry (refuses a duplicate lemma)
update_term Edit an existing entry's tags/definition/notes by lemma
delete_term Remove an entry by lemma; returns the deleted entry so it can be restored
deprecate_term Retire a term in place, keeping the entry so older prose stays readable

Searching

search is a thin pass-through to conlangkit's Glossary.find(), which owns the query language and the relevance model. A bare query searches lemmas and definitions together; a field prefix narrows it:

Query Finds
verdict any term whose lemma or definition mentions a verdict
l:core lemma only
t:artifact tags only (a tag matches whole, not as a substring)
d:verdict definition only
n:seed notes only

Wildcards work inside any of these: * any run of characters, ? a single character, ! a word boundary — so l:wor* finds worker.

Results are ordered by relevance: conlangkit runs a non-fuzzy pass first and appends fuzzy matches after it, so closer matches lead. Deprecated entries are included and carry deprecated: true rather than being hidden — a retired term should stay findable, but a caller should see that it is retired.

A query that carries no search terms ("", "d:") comes back with an error and no hits instead of matching everything, since a criteria-less expression would otherwise return an arbitrary page of the glossary that reads like a genuine result.

Retiring a term: deprecate or delete

Prefer deprecate_term when the term has been used anywhere. It keeps the entry — lemma and definition intact — and prefixes its notes with a Deprecated marker, so prose written before the change is still interpretable and every read of that entry reports deprecated: true. Name the successor with superseded_by whenever one exists; it is strongly encouraged but never required, since a term can be withdrawn with nothing to replace it. The successor must already be in the glossary, so a deprecation cannot leave a dangling reference behind.

Use delete_term only when the entry should leave no trace — a typo, a duplicate, or a term no prose ever used. It is the one destructive tool, and it returns the deleted entry so a caller can restore it verbatim with add_term.

Deprecation lives in the notes rather than in a tag or a column of its own, because the conlangkit format reserves the definition column for glosses — what the term means — and the notes column for cross-references to other lemmas. The successor is written as an italic lemma reference (use *charter*), following the notation in conlangkit's writing-about-language.md.

A note on slashes in definitions

In the conlangkit format, / separates alternative senses in the definition column. A definition that contains a literal slash (e.g. allow/ask/deny) is stored escaped on disk as allow\/ask\/deny — which renders as a plain / on GitHub (CommonMark/GFM). You never type the escape: pass ordinary prose (with plain slashes) to add_term/update_term, and read ordinary prose back — the server escapes and unescapes transparently.

Case sensitivity

Lemma lookup, insertion, and sorting are case-sensitive (byte order) — conlangkit targets writing systems where case-folding is not meaningful, so core and Core are distinct entries. Use each term's canonical case: lowercase for ordinary terms, uppercase for acronyms (AID, GCD). This keeps you from creating near-duplicates.

search is the exception: it folds case, because definitions and notes are ordinary prose where a capitalized query would otherwise miss silently. So when lookup_term comes back empty and you suspect the term exists under a different capitalization, search is what will find it.

Methodology

Non-trivial work in this repo runs under the cc craft methodology (~/code/me/cc); read constitution.md first. Follow strict TDD (see AGENTS.md).

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

glossary_mcp-0.3.0.tar.gz (98.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

glossary_mcp-0.3.0-py3-none-any.whl (20.1 kB view details)

Uploaded Python 3

File details

Details for the file glossary_mcp-0.3.0.tar.gz.

File metadata

  • Download URL: glossary_mcp-0.3.0.tar.gz
  • Upload date:
  • Size: 98.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for glossary_mcp-0.3.0.tar.gz
Algorithm Hash digest
SHA256 03df8bdb196bea8c8358faab7b01e121f11e3bf900b76813000fe652dd68aa9b
MD5 8d93375fcb926da8833815e7886fb2d4
BLAKE2b-256 980d61e43a3514c6a104782a3ef7eb55cc613fc1ee1ec7aa346cc58d69cb19bc

See more details on using hashes here.

File details

Details for the file glossary_mcp-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: glossary_mcp-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 20.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for glossary_mcp-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1066456f00d282a8493e7f8e6f919f59293d956f7076180ee2ff82874ff3260a
MD5 20667e3ae071290921a0bc0cc92cdefe
BLAKE2b-256 592dbea8bea57a757b7f6925862a4d58d3b898513142240072e21df4a13515ba

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page