Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Develop and verify changes

Set up the private engine, connect an MCP client, and verify changes without starting a paid examination.

Prerequisites

  • Access to the private repository and its AGENTS.md instructions.
  • Python 3.12 or newer and uv.
  • A checkout of the repository. Run the engine commands below from its root.

Pilot credentials grant access to the hosted tools, not to the source repository. The public tester kit contains the client bridge and setup instructions only.

Configure cases

Production cases come from the operator’s ignored data/surveys.local.json, or the JSON file named by TITLE_EXAMINER_SURVEYS. Paths in it are relative to the repository root or explicitly absolute. A new checkout has no private registry and can return an empty case list. An explicitly configured missing or malformed registry fails startup.

Create data/surveys.local.json using this fictional structure, then replace its metadata and supply the PDFs:

{
  "Example Survey": {
    "county": "Example County",
    "abstract": "TEST-001",
    "state": "TX",
    "docs_dir": "data/example/Documents",
    "lrs_file": null,
    "lor_files": []
  }
}

county, abstract, and docs_dir are required strings. state defaults to TX; another value does not validate the engine for another jurisdiction. lrs_file and lor_files are optional reference answer keys for evaluation, not required source records.

Historical measured cases live in evaluation/fixtures/benchmark-surveys.json and require explicit opt-in. They are benchmark examples, not production defaults. Keep private dataset URLs in data/downloads.local.json or TITLE_EXAMINER_DOWNLOADS. Each downloader entry requires url, dest, and description strings. The destination must remain inside data/; sharing links must stay in the private configuration.

Set up the engine

Install the locked MCP dependency environment:

uv sync --frozen --extra mcp

Run the fictional complete-chain demonstration:

uv run python scripts/demo_complete_chain.py

This command makes no model calls. It writes reports under output/Hale Family (complete-demo)/. The example’s mineral fee interests are Walnut Oil Company 1/2, James Hale 1/4, and Mary Hale 1/4. Thomas Reed’s 1/8 non-participating royalty interest (NPRI) appears separately.

Check the local MCP boundary:

uv run --extra mcp python scripts/smoke_mcp.py

Success: the client initializes, discovers the six examiner tools, and prints the configured survey count. An empty registry prints Registered surveys: 0; TITLE_EXAMINER_SURVEYS selects the registry used by this check. It does not start an examination. A silent mcp_server.py process alone is not a successful tool check.

To let a local client launch the server, use this command descriptor in that client’s documented MCP configuration:

{
  "command": "uv",
  "args": ["run", "--directory", "/absolute/path/to/ai-title-examiner", "--extra", "mcp", "python", "mcp_server.py"]
}

Replace the checkout path. The local server uses standard input/output (stdio), so stdout belongs to MCP messages. Send diagnostics to stderr or a log.

Connect a Python client

Use this example when you are building a client for the hosted endpoint. It lists tools and surveys without invoking title models.

Save the code below as client_check.py in your checkout. Supply TITLE_EXAMINER_URL and MCP_HTTP_API_KEY through your process’s protected environment. The endpoint must use HTTPS and end in /mcp.

import asyncio
import os

from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client


async def main():
    endpoint = os.environ["TITLE_EXAMINER_URL"]
    key = os.environ["MCP_HTTP_API_KEY"]
    async with streamablehttp_client(
        endpoint, headers={"Authorization": f"Bearer {key}"}
    ) as (read, write, _):
        async with ClientSession(read, write) as session:
            await session.initialize()
            tools = await session.list_tools()
            print([tool.name for tool in tools.tools])
            result = await session.call_tool("list_surveys", {})
            if result.isError:
                raise RuntimeError("list_surveys failed; inspect the safe MCP error")
            for part in result.content:
                if part.type == "text":
                    print(part.text)


asyncio.run(main())

Run it in the locked environment:

uv run --extra mcp python client_check.py

Success: the output lists six tools and registered survey records. Missing environment variables fail before connection. Authentication or network failures require the connection troubleshooting steps.

Use live tools/list schemas for requests and the MCP tool reference for defaults, domain fields, and operational limits. A tool’s isError result differs from a transport failure.

Run a paid local examination

Before running the pipeline, prepare the registered case PDFs and configure the required provider credentials. Agree on spending separately from setup and smoke checks.

Replace the survey placeholder with an exact configured name:

uv run python orchestrator.py --survey "<registered survey>" --driver bfs

This command invokes paid models. Local CLI and stdio also support the experimental agent driver, which can execute model-produced Python. Hosted HTTP accepts only bfs; preserve that restriction when changing either admission or worker code.

See Documents and sourcing for case scope and provider behavior. See Deployment and operations for the hosted configuration.

Find the source that owns a behavior

SourceResponsibility
orchestrator.pyCLI and analysis/report orchestration
agents/Extraction, QA, chain analysis, curative reading, and reports
core/models.pyStructured evidence and LLM-tolerant parsing
core/config.pySurvey registry, data/output paths, and model settings
core/ownership_fold.pyExact-fraction ownership calculation
core/buying_report.pyNet mineral acres, burdens, and curative summaries
sourcing/Providers, identity checks, frontier, and acquisition ledger
mcp_server.pyTool inputs, admission, and HTTP authentication
mcp_jobs.pyDurable records, locks, reconciliation, and artifact reads
mcp_job_runner.pyDetached worker and repeated hosted safety checks
evaluation/Field scoring, ownership scoring, and faithfulness checks

The repository’s AGENTS.md records less obvious constraints and historical findings. Use current source and fresh runtime evidence to distinguish today’s behavior from an older experiment.

Configure provider credentials

Keep values in the ignored root .env or the host’s protected secret store. Never put them in public examples, screenshots, or command arguments.

VariablePurpose
GEMINI_API_KEYPrimary extraction and focused date reads
ANTHROPIC_API_KEYReasoning and vision stages
OPENROUTER_API_KEYConfigured fallback for qualifying provider failures
MISTRAL_API_KEYOptional OCR fallback
MCP_HTTP_API_KEYHosted MCP bearer authentication
EXA_API_KEYOptional web/archive sourcing
BROWSERBASE_API_KEYOptional browser sourcing
TEXASFILE_USERNAME, TEXASFILE_PASSWORDOptional records-provider login

A working primary-provider path does not test fallback availability. Browser credentials do not enable sourcing or purchases by themselves; each also requires its host and request opt-ins.

Verify a change

Run the existing lint, regression, and public-boundary checks from the repository root:

mkdir -p ci-results
uv run ruff check .
TITLE_EXAMINER_SURVEYS=evaluation/fixtures/benchmark-surveys.json uv run pytest -q --junitxml=ci-results/regressions.xml
uv run python scripts/check_ownership_boundaries.py --output ci-results/ownership-boundaries.json
uv run python scripts/check_sourcing_boundaries.py --output ci-results/sourcing-boundaries.json
uv run --extra mcp python scripts/smoke_mcp.py > ci-results/local-mcp.log

Success: all commands exit zero, and the named reports exist. Inspect failures before attributing them to model reasoning; missing dependencies, routing, and provider quota are different causes.

For extraction or benchmark changes, compare the same cases with the same answer keys and evaluator. Retain the sample size and source set. One successful synthetic chain does not replace the benchmark in Accuracy and known limits.

Preserve these boundaries when editing:

  • Keep mineral fee, NPRI, and life estates separate. Use estate-share fractions for probate.
  • Keep unresolved cited records and unreadable scans visible as gaps.
  • Preserve original OCR text for grounding and risk notes through analysis/checkpoint loads.
  • Keep data/, output/, and curated results/ at their configured locations.
  • Preserve hosted driver and spending gates at admission and execution.
  • Test provider-tolerant coercions and output-token changes against actual outputs before tightening them.

Update and preview documentation

Use mdBook 0.5.4, mdbook-mermaid 0.17.1, Python 3.11 or newer, and Node.js 22 or newer. From the repository root:

cd docs-site
npm ci
npm run build
npm run deploy:dry-run
npm run dev

Open the local address Wrangler prints. Check the changed chapters, links, search, diagrams, and generated /llms.txt. Rebuild after editing; restart the preview after a rebuild if its asset manifest becomes stale.

Success: the build and packaging check pass, and the rendered guides match the same Markdown served to agents. Keep the default mdBook theme.

Update affected guides in the same change as behavior, tools, examples, or deployment. The documentation contributor guide is available to repository collaborators. Follow Deployment and operations to verify the published revision.

Keep private information out of Git

Use ignored .env files or a protected secret store for credentials. Commit variable names and obvious placeholders only. Keep private case registries, sharing URLs, raw records, correspondence, and operator run logs outside tracked source.

Use fictional examples in general instructions. Real names belong only in labeled, approved benchmark or test examples with stated provenance. Do not copy a measured case into production defaults.

Before committing, inspect the staged file list and run uv run python scripts/check_no_secrets.py. Inspect Office files, PDFs, ZIPs, screenshots, and generated reports, including nested archives. The text guard cannot detect every password hidden in prose or binary contents.

Before making the repository public, scan current tracked contents and all reachable Git history with a secret scanner. If a credential enters Git, revoke or rotate it first, remove all historical copies, and coordinate cleanup of hosted references and other clones. Deleting the latest copy does not erase older commits.