Skip to main content
Glama

mesa-clm

Calibrated, ontology-grounded metadata annotation for the MESA stack with Contrastive LM.

mesa-clm proposes OBO-grounded AVUs for a dataset the way its sibling mesa-anyjev does, but asks Contrastive LM (CLM) instead of AnyJev: every fit question is one rank over the candidate terms plus a fixed "none of these" anchor, scored by a frozen Qwen3-8B encoder and CLM's projection heads behind a loopback HTTP server. A reasoning model only plans (which ontologies are in play, what to search); deterministic code drives mesa-mcp's OBO/OLS tools; every decision is recorded in a provenance sidecar next to the AVU history kept by mesa-ducklake. The core is a torch-free client; the encoder and clm-serve run as separate processes.

Status: pre-alpha, milestone M2 done (2026-10-03). The registered framing experiment (bench/results/2026-10-03/) chose the F9 framing on clm-latest for column.ontology_fits and found no qualifying framing for term.fits (K1), so term.fits proposals are the OLS top-1 (ols_rank) by default (DESIGN A1; every number in RESEARCH.md, "M2 results"). The same run measured CLM's zero-shot answers to the closed choices (annotate, aspect, value kind) below their majority class, so, by the user's decision after the results (DESIGN A6, product safety, not a scientific claim), rules answer them by default and CLM's answers are kept for audit only. Everything is proposed-only. M1 delivered the serving stack on sparky-1 and the hermetic rank-first pipeline (annotate, explain, review, feedback). See DESIGN.md for the decisions (U1–U4, D0–D33, the pre-registered experiments, amendments A1–A6), RESEARCH.md for the verified facts the design rests on, design/plan-2026-09-28.md for the plan, and CLAUDE.md for how to work here.

Why

mesa-anyjev's AnyJev readout works through next-token logprobs: a top-20 logprob cap, one slow prompt per candidate, K>8 choices split into yes/no twins. CLM scores candidate texts against a state with cacheable candidate vectors and no option limit. Its released head is weak zero-shot on typed questions (state-independent Score answers, suffix collapse, inverted calibration, and a logistic regression on the frozen embeddings that beats the fine-tuned head; upstream issues #3, #13, #15), and a no-model lookup that copies labels across cards already scores leave-one-card-out accuracy 0.772 on term.fits, above AnyJev L2's 0.765. So mesa-clm asks rank-first questions with an anchor, fingerprints everything that depends on the encoder, earns trust only through pre-registered, leakage-aware, nested-selection bench cells that beat that lookup on novel keys, and ships proposed-only first.

Related MCP server: Universal Ontology MCP

Install

uv sync --all-extras                      # dev install; nothing here pulls torch or vllm
uv run pytest -q                          # hermetic suite
sha256sum -c vendored.sha256              # vendored CLM and AnyJev files are byte-identical

Requires Python 3.11+. mesa-mcp and mesa-ducklake are installed from pinned git commits (neither is on PyPI). Serving (a vLLM pooling container plus a patched clm-serve) runs on the GPU host; see docs/concepts/serving.md and the runbook docs/deploy/serving.md.

Command line

uv run mesa-clm doctor                    # pins, versions, mesa-mcp plugin API, policy, stores (--quick, --json)
export MESA_CLM_OLS__FIXTURES=replay MESA_CLM_PROVENANCE__DSN=duckdb:///labels.duckdb
uv run mesa-clm labels ingest-neon-eval --eval-root tests/fixtures/neon-avu-eval   # silver labels
uv run mesa-clm labels stats                                                         # counts per task, source, label
uv run mesa-clm labels snapshot --out bench/snapshots/today.parquet                  # frozen labels + labels_sha256
uv run mesa-clm bench baselines --snapshot bench/snapshots/today.parquet             # lookup_prob, novel-key, LOPO
uv run mesa-clm bench mde --snapshot bench/snapshots/today.parquet                   # minimum detectable effect

labels import-anyjev --dsn <sidecar> reads a mesa-anyjev sidecar read-only (its curator rows arrive as weight-0 agent_pick unless --trust-curator is given at a terminal).

Annotation (milestone M1; every outcome is proposed-only until evidence-cited autos land in M4):

export MESA_CLM_OLS__FIXTURES=replay MESA_CLM_OLS__FIXTURES_DIR=tests/fixtures/ols
uv run mesa-clm --provenance duckdb:////tmp/clm.duckdb annotate \
    --card tests/fixtures/cards/DP1.10003.001.brd_countdata.md --provider fake --out run.json
uv run mesa-clm --provenance duckdb:////tmp/clm.duckdb explain --run-id <id or prefix>
uv run mesa-clm --provenance duckdb:////tmp/clm.duckdb review --run-id <id or prefix>   # interactive
uv run mesa-clm framings --check          # framing keys match framings.lock.json

By default term.fits groups are decided by ols_rank (the OLS top-1, proposed-only; K1, DESIGN A1) and column.ontology_fits is asked with F9; --ols-rank-tasks none asks CLM for term.fits too, an audit run. The closed choices are answered by rules (DESIGN A6: every non-identifier column annotated whatever the planner says; aspects from the planner's hint and the M0 lookup over a table frozen from the registered labels snapshot and shipped in the package, else the first two in the lookup's prior order; value kind "the term label"), CLM's answers to them recorded audit-only; --closed-choice clm runs the M2 behaviour. Answers given at an interactive terminal are curator labels; review --pick and feedback run without one (a script, an agent's shell) are recorded as an agent's answers at weight 0 and say so (DESIGN A2). On the serving host, --provider clm (the default) talks to the loopback encoder and clm-serve with the keys mesa-clm serve keys --init writes, ~/.mesa/clm/secrets/clm.key and encoder.key, read by default (MESA_CLM_CLM__API_KEY_FILE and MESA_CLM_ENCODER__API_KEY_FILE name others); mesa-clm doctor --serve checks the stack first. features build|export-npz|project|stats keep the per-encoder feature cache, and provenance migrate|export|import|prune maintain the sidecar. plan, apply, revert and the mesa_clm_* MCP tools arrive with M3; mesa-clm --help lists what exists.

License

MIT, Copyright (c) 2026 The Regents of the University of New Mexico. CLM and its vendored schema.py are Apache-2.0 and not affiliated with TypeSafe AI or Jev; NEON metadata in the fixtures is CC BY 4.0; see THIRD_PARTY.md.

Available Tools

50 tools
ds_add_avuC

Add a new AVU (attribute-value-unit) to a file (data-object), directory (collection), resource, or user.

Inputs: value (required), target (required), attribute (required), target_type (required), +1 more.

ds_add_avusA

Add MULTIPLE AVUs to one data-object/collection in a single call and mirror them all into DuckLake as ONE snapshot (efficient bulk tagging).

Inputs: avus (required), target (required), target_type.

ds_copy_fileB

Copy a file (data-object) or directory (collection) to a new location.

Inputs: source_path (required), destination_path (required).

ds_create_ticketA

Create a read or write iRODS ticket on a data object or collection. Returns the ticket string plus the restrictions that were applied. Anonymous users may not create tickets.

Inputs: mode (required), path (required), expiry, uses_allowed, +3 more.

ds_delete_avuB

Delete an AVU (attribute-value-unit) from a file, directory, resource, or user.

Inputs: target (required), target_type (required), id, unit, +2 more.

ds_delete_fileC

Delete a file (data-object) or directory (collection).

Inputs: path (required).

ds_delete_ticketA

Revoke an existing iRODS ticket. Issuer or admin only — the server enforces this. Anonymous callers are rejected at the tool layer.

Inputs: ticket (required).

ds_directory_treeB

Get a recursive tree view of files (data-objects) and directories (collections). The specified path must be an iRODS path. The output is in JSON format. The output contains all entries in the given directory (collection) path.

Inputs: path (required), depth.

ds_download_fileA

Returns how to download the full contgent of a file (data-object) with the specified path. The specified path must be an iRODS path. Returns how to download the file using WebDAV, GoCommands (gocmd), and iCommands.

Inputs: irods_path (required), local_path (required).

ds_execute_ruleA

Run an iRODS rule. Supply either rule_name (server-installed) or rule_text (inline iRL). Output parameters are returned as a dict; iRODS stdout/stderr are returned when output_parameters includes 'ruleExecOut'. Path-typed input parameters are checked against the caller's accessible paths before the rule fires.

Inputs: rule_name, rule_text, instance_name, input_parameters, +1 more.

ds_get_file_infoC

Retrieve detailed metadata about a file or directory.

Inputs: path (required).

ds_get_metadataA

Fetch the full AVU metadata bundle for an iRODS path (data object or collection). Returns the resolved target type alongside the AVU list.

Inputs: path (required).

ds_get_policy_configA

Return the configuration of a named Policy Composition Framework policy. iRODS does not expose PCF config through PRC; this is a stub that returns config=None and a note documenting the limitation.

Inputs: name (required).

ds_get_rule_definitionA

Return the source of a named iRODS rule. iRODS does not expose rule sources through PRC; this is a best-effort tool whose definition field is None on most servers.

Inputs: name (required).

ds_get_ticket_infoA

Get information about a specific iRODS ticket, such as its ID and expiration time, in JSON format. Anonymous users are not allowed to get ticket information.

Inputs: name (required).

ds_list_allowed_directoriesB

Get a list of directories (collections) that this server is allowed to access. The output also contains API names that can be requested to each directory (collection).

Inputs: no named parameters.

ds_list_avusB

List AVUs (attribute-value-unit) from a file (data-object), directory (collection), resource, or user.

Inputs: target (required), target_type (required).

ds_list_directoryB

Get a list of files (data-objects) and directories (collections) in a specified path. The specified path must be an iRODS path. The output is in JSON format. The output contains entries in the given directory (collection) path. Use offset and limit parameters to paginate through large directories.

Inputs: path (required), limit, offset.

ds_list_directory_detailsA

Get a list of files (data-objects) and directories (collections) in a specified path with full detailed info. The specified path must be an iRODS path. The output is in JSON format. The output contains entries in the given directory (collection) path, and users or groups who can access the files (data-ojects). Files (data-objects) will also have replica information. Use offset and limit parameters to paginate through large directories.

Inputs: path (required), limit, offset.

ds_list_policiesA

List policies in effect for the Data Store. With project_path, returns the MESA policy AVUs (mesa.policy.*) set on that project root — the policies mesa-mcp itself honours. Always includes the server-side Policy Composition Framework envelope; iRODS does not expose PCF state through PRC, so that part is a documented stub.

Inputs: project_path.

ds_list_rulesA

List iRODS rules visible to the caller. Returns a best-effort list of delayed rules; the static rule base is not exposed by PRC and the note field documents that limitation.

Inputs: no named parameters.

ds_list_ticketsA

Get a list of iRODS tickets. Return information about the tickets, such as their IDs and expiration times, in JSON format. Anonymous users are not allowed to list tickets.

Inputs: no named parameters.

ds_make_directoryC

Make a new directory (collection).

Inputs: path (required).

ds_modify_accessC

Modify data access of a user or group to a file (data-object) or directory (collection).

Inputs: path (required), access_level (required), user_or_group (required), recurse.

ds_modify_access_inheritanceC

Modify data access inheritance flag of a file or directory.

Inputs: path (required), inherit (required), recurse.

ds_modify_ticketA

Modify restrictions on an existing iRODS ticket. Cannot change the ticket's mode — that is set at issuance time. Returns the applied restrictions. Anonymous users are not allowed to modify tickets.

Inputs: ticket (required), uses, expiry, host_restriction, +2 more.

ds_move_fileC

Move a file (data-object) or directory (collection) to a new location.

Inputs: new_path (required), old_path (required).

ds_pingA

Liveness check. Echoes back the supplied message (or 'ok') and the running mesa-mcp version. No iRODS access required.

Inputs: message. Returns: Output schema for ``ds_ping``. Reference implementation of the opt-in ``output_…

ds_read_fileB

Read the partial content of a file (data-object) with the specified path and offset. The specified path must be an iRODS path. If the file is too large to be displayed inline, use the WebDAV URI to access it.

Inputs: path (required), length, offset.

ds_search_filesA

Recursively search for files (data-objects) and directories (collections) matching a pattern. The specified search root path must be an iRODS path. Use unix wildcards, such as '?' and '*', for the search pattern. The matching entries are returned in JSON format.

Inputs: path (required).

ds_search_files_by_avuC

Search for files (data-objects) and directories (collections) matching iRODS AVU (attribute-value-units) using specified attribute and value. The matching entries are returned in JSON format.

Inputs: value (required), attribute (required).

ds_search_metadataA

Search iRODS by AVU attribute/value/unit. More permissive than ds_search_files_by_avu: any combination of attribute, value, and unit can be supplied and you can restrict the result type to data objects, collections, or both.

Inputs: unit, value, target, attribute.

ds_upload_fileB

Returns how to upload the full contgent of a file (data-object) to the specified path. The specified path must be an iRODS path. Returns how to upload the file using WebDAV, GoCommands (gocmd), and iCommands.

Inputs: irods_path (required), local_path (required), is_dir.

ds_use_ticketA

Supply an iRODS ticket to the caller's iRODS session. Subsequent operations by the same caller run with the ticket applied, and their AVU writes record the ticket id in DuckLake's via_ticket column. Other callers' sessions are unaffected.

Inputs: ticket (required).

ds_write_fileC

Write the partial content to a file (data-object) with the specified path and offset. The specified path must be an iRODS path. If the file is too large to be displayed inline, use the WebDAV URI to access it.

Inputs: path (required), content (required), offset.

mesa_avu_apply_dataciteA

Validate a DataCite 4.x record and write it to an iRODS path as AVUs (canonical and/or CyVerse-template naming), mirrored to DuckLake as one snapshot.

Inputs: record (required), target (required), naming.

mesa_avu_apply_termA

Resolve an OLS term, build the canonical AVU triple (.<snake_label>, , ), write it to the iRODS path, and record the change in the project's DuckLake. Composite of mesa_avu_from_term + ds_add_avu, in one call.

Inputs: path (required), value (required), ontology_id (required), iri, +2 more.

mesa_avu_from_termA

Pure transformation: turn an OLS term + user value into the canonical AVU triple (attribute='.<snake_case_label>', value=, unit=) without writing to iRODS.

Inputs: value (required), ontology_id (required), iri, curie, +1 more.

mesa_datacite_exportB

Export DataCite AVUs as a DataCite 4.x XML (for DOI registration) or REST JSON document.

Inputs: avus (required), format.

mesa_datacite_templateA

Return the DataCite 4.x field scaffold (field, required?, repeatable?, controlled vocabulary, canonical AVU key) to drive a complete, valid record.

Inputs: no named parameters.

mesa_datacite_validateA

Validate DataCite AVUs against the 4.x kernel: report missing mandatory fields, invalid controlled-vocabulary values, and whether the record is DOI-ready.

Inputs: avus (required).

mesa_ducklake_init_projectA

Enroll an iRODS collection as a MESA project. Creates the <irods_path>/.mesa/ducklake/ sub-collection (or the configured ducklake.data_collection), sets the mesa.enabled=true AVU on the project root, and registers the project in the DuckLake catalog. Idempotent — safe to re-run.

Inputs: irods_path (required).

mesa_ols_generate_templateC

Generate a SCHEMAS-compatible template (prefix + top-level term fields) for an ontology. This is the function that drives the esiil-portal auto-generated AVU forms.

Inputs: ontology_id (required).

mesa_ols_get_ontologyA

Fetch metadata for a single OLS ontology (term count, version, homepage, preferred prefix).

Inputs: ontology_id (required).

mesa_ols_get_termA

Get the full record (label, CURIE, synonyms, definition, parents/children) for a single OLS term by IRI.

Inputs: iri (required), ontology_id (required).

mesa_ols_get_term_hierarchyA

List the direct children of an OLS term. Use this to walk an ontology's class hierarchy one level at a time.

Inputs: iri (required), ontology_id (required), size.

mesa_ols_list_ontologiesA

List ontologies available in the EMBL-EBI Ontology Lookup Service (OLS4), paginated. Returns 266+ ontologies (ENVO, GO, CHEBI, etc.).

Inputs: page, size.

mesa_ols_search_termsB

Search OLS terms across all ontologies or scoped to one. Optionally restrict to descendants of a parent IRI (hierarchy walk).

Inputs: query (required), size, ontology_id, descendants_of.

mesa_policy_disableA

Disable a MESA policy on a project root collection by removing the mesa.policy.<policy_name> AVU. Records the change into the project's DuckLake when MESA-enabled.

Inputs: policy_name (required), project_path (required).

mesa_policy_enableA

Enable a MESA policy on a project root collection by writing the mesa.policy.<policy_name>=true AVU. Records the change into the project's DuckLake when MESA-enabled.

Inputs: policy_name (required), project_path (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.2/5.0

    Scored across 50 tools

    Disambiguation3/5

    Most tools have distinct resource+action targets, but several pairs overlap: ds_list_directory vs ds_list_directory_details, ds_search_files_by_avu vs ds_search_metadata (both AVU search), ds_list_avus vs ds_get_metadata, and ds_add_avu vs ds_add_avus. The detailed descriptions do help an agent choose, and the mesa_* vs ds_* split separates higher-level workflows from raw iRODS ops, but the boundaries are not crisp.

    Naming Consistency4/5

    A consistent verb_noun snake_case pattern (ds_add_avu, ds_delete_file, ds_make_directory, ds_list_rules) holds across the dominant ds_ family, with namespace prefixes (mesa_ols_, mesa_avu_, mesa_datacite_, mesa_policy_) grouping related tools predictably. Minor deviations exist, e.g. singular/plural drift (ds_add_avu vs ds_add_avus, ds_list_avus) and redundant qualifiers (ds_search_files_by_avu vs ds_search_metadata).

    Tool Count2/5

    50 tools is very heavy and near the extreme end of the scale. While the surface genuinely spans multiple subsystems (iRODS CRUD, AVUs, tickets, policies, OLS ontologies, DataCite, DuckLake), the count suggests feature bloat and several entries are documented stubs (ds_get_policy_config, ds_get_rule_definition, ds_list_rules) that dilute the set.

    Completeness4/5

    Coverage is broad and near-complete: file/directory CRUD, AVU CRUD and search, ticket lifecycle, access and inheritance management, ontology lookup/hierarchy, DataCite validation/export, and project enrollment all exist. Gaps are minor (some PCF/rule inspection is a documented stub), and agents have viable workarounds via rules and search tools.

    Maintenance

    ActivityMaintained
    ResponsivenessLimited data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables natural language search and discovery of open-access scientific datasets through the EOSC Data Commons OpenSearch service. Provides tools to search datasets and retrieve file metadata using LLM-assisted queries.
      15
      MIT
    • A
      license
      Not graded
      quality
      Not graded
      maintenance
      Enables AI assistants to navigate and populate complex ontologies by transforming unstructured data into structured, SHACL-validated knowledge graphs. It features semantic hybrid search and proactive schema guidance to help users build deeply linked, reasoning-ready data.
      -
    • A
      license
      A
      quality
      C
      maintenance
      Enables AI assistants to query, modify, and reason over OWL/TTL/RDF ontology files using natural language, without needing SPARQL or OWL syntax.
      14
      6
      MIT