Skip to main content
Glama
Pardesco

codecite

by Pardesco

codecite

Bring-your-own-corpus retrieval for building codes: section-faithful chunking, hybrid search in Postgres, and an MCP server so Claude Code or Codex can answer with §1004.5-style citations from your licensed copy of a code. The repo ships code, schema, tests, an eval harness, and an original synthetic code. It never ships, fetches, or redistributes ICC text.

flowchart LR
  A[PDF / HTML / DOCX / MD] --> B[parse blocks]
  B --> C[profile grammar<br/>ibc · oac · generic]
  C --> D[section tree]
  D --> E[chunks<br/>body · exception · table · definition]
  E --> F[(Postgres 17<br/>pgvector + FTS)]
  F --> G[hybrid search<br/>RRF + amendment overlay]
  G --> H[MCP server<br/>stdio / http]
  H --> I[Claude Code · Codex]

Quickstart (no API key)

docker compose up -d
uv sync --extra local                 # local nomic-embed-text-v1.5; or set CODECITE_EMBED_PROVIDER=fake for CI-style runs
uv run codecite init
uv run codecite ingest samples/sample-building-code/model-building-code-2026.md --corpus sample-bc-2026 --layer base --title "Model Building Code" --version 2026 --corpus-title "Model Building Code 2026"
uv run codecite ingest samples/sample-building-code/local-amendments-2026.md --corpus sample-bc-2026 --layer amendment --title "Local Amendments" --version 2026
uv run codecite search "occupant load factor for business areas"

Then wire it into a client. The repo ships a .mcp.json, so Claude Code picks the server up automatically when run inside the folder (project scope, nothing global, nothing loaded in other sessions). For a global registration instead:

claude mcp add codecite --scope user -- uv run --directory /path/to/codecite codecite serve
# ~/.codex/config.toml
[mcp_servers.codecite]
command = "uv"
args = ["run", "--directory", "/path/to/codecite", "codecite", "serve"]
env = { CODECITE_DATABASE_URL = "postgresql://codecite:codecite@127.0.0.1:5432/codecite" }

Related MCP server: MCP Context Manager

What the MCP server exposes

Tool

Use

search_code

hybrid search; returns governing sections with full text, matched chunk kinds, amendment overlay, cross-refs, and a ready-to-paste citation

get_section

exact lookup (1004.5, Section 1004.5, §1004.5) with exceptions, tables, definitions, children

get_context

parent chain, siblings, children, referenced-by

resolve_reference

which section/table/chapter references in a piece of text actually exist

list_corpora, list_chapters

what is indexed

ingest_document

off unless CODECITE_ENABLE_INGEST_TOOL=1; path-restricted

Resources: codecite://{corpus}/toc, codecite://{corpus}/section/{number}, codecite://{corpus}/document/{id}. Prompts: code_question, compare_to_standard (cite-or-abstain rules baked in).

Verified in both clients (2026-09-07):

$ claude mcp get codecite
codecite:
  Scope: User config (available in all your projects)
  Status: ✔ Connected
  Type: stdio
  Command: uv
  Args: run --directory <path-to-repo> codecite serve

$ codex mcp list
codecite  uv  run --directory <path-to-repo> codecite serve  enabled

demo

How retrieval works

  • Chunk by section, not by window. Each chunk is one section body, prefixed with its breadcrumb (Model Building Code 2026 > 10 Means Of Egress > 1004 Occupant Load > 1004.5 …) so the vocabulary the body omits is still embedded. Exceptions, tables, and definitions are separate chunks because that is where the answer usually is.

  • Hybrid. pgvector HNSW cosine candidates plus Postgres full-text (strict AND pass, then OR pass), fused with Reciprocal Rank Fusion (k = 60), collapsed to sections.

  • Amendment overlay. A same-numbered section on the amendment layer is surfaced directly above its base section and named in amended_by and in the citation.

  • One embedding model per database. init records it; mixing models is refused.

Eval

uv run codecite eval evals/sample-bc.yaml --modes naive,vector,lexical,hybrid --report docs/EVALS.md runs 64 questions (lookup, paraphrase, exception, table, definition, cross-reference, not-in-corpus) against the synthetic corpus. Results with the default local model (nomic-embed-text-v1.5, CPU), 2026-09-07:

mode

hit@1

hit@3

hit@5

MRR

kind@5

abstain

false abstain

naive (512-token windows, no sections)

0.275

0.442

0.442

0.353

0.000

1.000

0.033

vector only

0.817

0.950

0.983

0.888

0.957

1.000

0.017

lexical only

0.717

0.883

0.917

0.808

0.957

0.000

0.000

hybrid (default)

0.817

0.950

0.983

0.880

0.957

1.000

0.000

hit@k: the expected section (or a descendant, half credit) is in the top k. kind@5: the matched chunk was the expected kind (table, exception, definition). abstain: the four not-in-corpus questions were flagged low-confidence; false abstain: in-corpus questions wrongly flagged. Full per-tag breakdown in docs/EVALS.md. CI runs the same gate with a dependency-free hashed-bag-of-words embedder (hybrid hit@5 0.95).

Ohio profile

codecite fetch-oac 4101:1 downloads the Ohio Administrative Code rule PDFs (state law, free) from codes.ohio.gov, politely (robots.txt, 1 req/s, identifies itself). Ingest them with --profile oac --layer amendment over your own licensed IBC 2021 PDF on the base layer. The tool never touches any ICC domain.

uv run codecite fetch-oac 4101:1 --out corpus/oac
uv run codecite ingest corpus/oac --corpus ohio-bc --layer amendment --profile oac --corpus-title "Ohio Building Code (OAC 4101:1)"
uv run codecite ingest /path/to/your/IBC-2021.pdf --corpus ohio-bc --layer base --profile ibc --title "IBC 2021" --version 2021
uv run codecite eval evals/ohio-bc.yaml --modes hybrid

evals/ohio-bc.yaml ships questions and expected rule numbers only; it contains no code text.

Run locally on 2026-09-07 against the 35 OAC 4101:1 rules alone (amendment layer, 291 sections, no IBC base layer indexed), 30 questions, nomic-embed-text-v1.5:

mode

hit@1

hit@3

hit@5

MRR

hybrid

0.679

0.893

0.893

0.749

vector only

0.696

0.929

0.929

0.796

lexical only

0.625

0.786

0.804

0.694

The misses are mostly one-line "modify exception #1" instructions with almost no text of their own, which a base-layer IBC index would give context to. Report in docs/EVALS-ohio.md.

Full-pipeline test on a real code: New York City

The IBC itself is sold by ICC, but New York City publishes its own IBC-derived Building Code as free chapter PDFs (2014 edition based on IBC 2009; 2022 edition based on IBC 2015), and city law is a government edict. That makes it the cleanest way to exercise the whole pipeline on genuine ICC-layout PDFs. The chapter files are linked from the 2022 Construction Codes page and served from /assets/buildings/codes-pdf/cons_codes_2022/.

uv run codecite ingest corpus/nyc2022 --corpus nyc-bc-2022 --layer base --profile ibc --version 2022 --corpus-title "New York City Building Code 2022"
uv run codecite eval evals/nyc-bc-2022.yaml --modes hybrid,vector,lexical

Indexed 2026-09-07: 33 chapters, about 5,700 sections and 6,600 chunks (400+ tables, 500+ exceptions). Egress golden set (24 questions), nomic-embed-text-v1.5 on CPU:

mode

hit@1

hit@3

hit@5

MRR

hybrid

0.705

0.977

0.977

0.803

vector only

0.818

0.886

0.886

0.841

lexical only

0.614

0.750

0.750

0.676

Known parser limits on these PDFs: NYC prints chapter and section titles as running page headers, which the parser strips, so those two levels of the breadcrumb lose their titles (subsections keep theirs). Report in docs/EVALS-nyc.md.

Operators index documents they already have the right to use. Nothing indexed leaves the machine unless a remote embedding provider is explicitly enabled. See docs/LEGAL.md for the cases and the reasoning, docs/ARCHITECTURE.md for the design, and docs/WRITEUP.md for the short version.

Layout

src/codecite/   config · db (migrations, queries) · parse · profiles · tree · chunk · extract · embed · retrieve · evaluate · mcp_server · cli · fetch
samples/         synthetic Model Building Code 2026 (+ local amendments) · Acme design standards (generic profile)
evals/           golden question set
tests/           grammar, tree, chunker, extractors, retrieval (real Postgres), MCP over stdio

MIT.

Available Tools

6 tools
get_contextA

What surrounds a section: parent chain up to the chapter, previous/next siblings, children, and the sections that reference it. Use for 'where does this sit' or 'what else applies' questions.

ParametersJSON Schema
NameRequiredDescriptionDefault
corpusNo
numberYes

TDQS

A4.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description explains what kind of information is surfaced and implies a read-only lookup, but with no annotations it does not explicitly state side effects, permissions, or that no modifications are made.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single focused sentence followed by a short use-case clause, with no redundant or extraneous content.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple context lookup with no output schema, the description enumerates the returned context categories and typical use cases. It could be more complete by defining the parameters, but the core behavior is understandable.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema provides no parameter descriptions, and the description does not clarify what 'number' refers to or how 'corpus' is used. The words 'section' and 'corpus' offer only weak inference.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states what the tool does: it returns surrounding context for a section, including parent chain, siblings, children, and referencing sections. It also names concrete question types it supports.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly tells when to use the tool ('where does this sit' or 'what else applies' questions), giving an agent clear selection criteria.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_sectionD
ParametersJSON Schema
NameRequiredDescriptionDefault
corpusNo
numberYes
include_tablesNo
include_childrenNo

TDQS

D1/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Tool has no description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness1/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Tool has no description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Tool has no description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Tool has no description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose1/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tool has no description.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Tool has no description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_chaptersC

Table of contents: chapter numbers and titles for one corpus.

ParametersJSON Schema
NameRequiredDescriptionDefault
corpusYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

C2.9/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations to indicate side effects or safety, the description carries the full burden. It does not state that the operation is read-only, nor does it describe any potential errors or limitations. The brief mention of output content ('chapter numbers and titles') is helpful but insufficient for full transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise, using a single sentence that conveys the essential purpose without any redundant or extraneous information. It is well-structured for a simple listing tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so the lack of return-format details is acceptable. However, the description omits usage context, edge cases, and any relationship to sibling tools, making it only partially complete for an agent deciding when and how to invoke it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The only parameter, 'corpus', is not described in the schema. The description's phrase 'for one corpus' hints that it is a corpus identifier, but it does not clarify expected format, allowed values, or how to obtain such an identifier. Since schema coverage is 0%, the description does not sufficiently compensate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly identifies the resource (chapters) and the scope (one corpus), and the noun phrase 'Table of contents' effectively conveys the listing intent. However, it lacks an explicit verb and does not directly contrast with sibling tools like get_section or list_corpora, so it falls short of a perfect score.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives such as get_section or search_code. It does not mention any preconditions or typical use cases, leaving the agent to infer the appropriate context from the name and parameters.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_corporaA

List indexed corpora with their documents (layer, version), section/chunk counts, and the embedding model. Call this to tell the user what is actually indexed before answering.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description reveals what the tool returns (documents, counts, embedding model) and implies a non-mutating read operation. While it doesn't explicitly state side effects, listing is inherently safe.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences, concise, and to the point, with no redundant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the lack of parameters and presence of an output schema, the description sufficiently explains the tool's function and output. It provides enough context for an agent to call it appropriately.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are no parameters, so the description fully covers them. No additional explanation is needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (list indexed corpora) and the resource (corpora), distinguishing it from sibling tools like get_context and search_code.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly advises calling this tool before answering to tell the user what is indexed, giving clear guidance on when to use it. It doesn't mention when not to use it, but the context is straightforward.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

resolve_referenceA

Parse free text for section/table/chapter references and report which ones exist in the index.

ParametersJSON Schema
NameRequiredDescriptionDefault
textYes
corpusNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the burden. It communicates a read-only parse-and-report behavior, but does not mention side effects, required index/corpus state, or error behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single, direct sentence that front-loads the action and avoids redundant wording.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is simple and an output schema exists, so return values need not be described. However, the 'corpus' parameter is unexplained and the relationship between 'index' and 'corpus' is ambiguous.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%. The 'text' parameter is loosely tied to 'free text', but the optional 'corpus' parameter is never mentioned or explained, leaving a significant parameter-semantics gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific action ('parse'), a specific resource ('section/table/chapter references'), and the outcome ('report which ones exist'), making it clearly distinct from sibling tools like search_code or get_context.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies the use case—validating references in free text—but it does not explicitly state when to prefer this tool over alternatives or give exclusion criteria.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_codeD
ParametersJSON Schema
NameRequiredDescriptionDefault
kNo
kindsNo
layerNo
queryYes
chapterNo
corporaNo

TDQS

D1/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Tool has no description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness1/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Tool has no description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Tool has no description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Tool has no description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose1/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tool has no description.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Tool has no description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

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

  1. 6 tool updatesv0.1.0
    • First observedget_context
    • First observedget_section
    • First observedlist_chapters
    • First observedlist_corpora
    • First observedresolve_reference
    • First observedsearch_code

TDQS

C2.8/5.0

Scored across 6 tools

Disambiguation4/5

Tool names are distinct and suggest different purposes (context, reference resolution, corpus listing, code search, section retrieval, chapter listing). However, two tools (search_code, get_section) have empty descriptions, which slightly reduces clarity and could cause ambiguity in edge cases.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern (get_context, resolve_reference, list_corpora, search_code, get_section, list_chapters). The verbs are varied but the structure is uniform and predictable.

Tool Count5/5

With 6 tools, the set is well-scoped for a code citation/reference server. It covers lookups, searches, and structural navigation without being overly granular or excessive.

Completeness4/5

The tool set appears to cover core operations: searching code, retrieving sections and chapters, resolving references, and listing corpora. Missing operations like creating or updating references are likely outside the server's purpose, but the empty descriptions for two tools leave some uncertainty about full coverage.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides semantic code intelligence to help users search, navigate, and analyze entire codebases using plain English. It enables Claude to perform architectural overviews, bug detection, and refactor suggestions through local semantic search and keyword indexing.
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Enables efficient code navigation and retrieval through natural language search, BM25 ranking, and fuzzy matching across multiple programming languages. It drastically reduces token usage by allowing Claude to query specific code symbols and logic instead of reading entire files.
    13
    70 npm
    13
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Enables semantic search over codebases using natural language queries, returning relevant code snippets with source locations. Integrates with Claude Code for automatic codebase exploration.
    1
    1
    MIT