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).

Metadata

Release files for glossary-mcp 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for glossary-mcp 0.3.0
File Size Uploaded
glossary_mcp-0.3.0.tar.gz 98.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for glossary-mcp 0.3.0
File Interpreter ABI Platform
glossary_mcp-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 118.9 kB

Release files / glossary_mcp-0.3.0.tar.gz

Download URL glossary_mcp-0.3.0.tar.gz
Size 98.8 kB
Tags Source
SHA-256 checksum
How to use checksums
03df8bdb196bea8c8358faab7b01e121f11e3bf900b76813000fe652dd68aa9b
BLAKE2b-256 checksum
How to use checksums
980d61e43a3514c6a104782a3ef7eb55cc613fc1ee1ec7aa346cc58d69cb19bc
Upload date
Uploaded using Trusted Publishing?
What is 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}

Release files / glossary_mcp-0.3.0-py3-none-any.whl

Download URL glossary_mcp-0.3.0-py3-none-any.whl
Size 20.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1066456f00d282a8493e7f8e6f919f59293d956f7076180ee2ff82874ff3260a
BLAKE2b-256 checksum
How to use checksums
592dbea8bea57a757b7f6925862a4d58d3b898513142240072e21df4a13515ba
Upload date
Uploaded using Trusted Publishing?
What is 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}

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page