ask-cooter

ask-cooter

MCP server that turns any scanned or digital PDF into a private knowledge expert, letting you ask questions and get cited answers with page-level sources. Provides tools for semantic search, page retrieval, torque spec lookup, and section navigation.

Category
访问服务器

README

Ask Cooter

Turn any PDF — even a scanned, text-free one — into a private, cited knowledge expert. Ask Cooter ingests a PDF, extracts clean text, tables, and figure descriptions from every page with a vision model, embeds it into a vector store, and answers questions about it with citations back to the exact source page — through a CLI, a local chat UI, or an MCP server.

Because extraction is vision-based, it works on scanned documents with no selectable text (the hard case), not just digital PDFs.

Initial use case

The proof-of-concept corpus is a Harley-Davidson Softail (1984–1999) service manual — 651 scanned pages, zero embedded text — turned into a mechanic's assistant ("Ask Cooter") that answers repair questions and cites the page to open for the original diagram or torque spec. The defaults in .env.example point at that manual; nothing in the pipeline is motorcycle-specific: set PDF_PATH to any PDF and re-run the ingest.

See DESIGN.md for the architecture and rationale.

How it works

Any PDF (scanned images or digital text)
  → render each page to PNG            (PyMuPDF)
  → vision extraction (Claude)         → clean markdown + tables + figure text + specs
  → chunk + Voyage embeddings          → stored in Postgres/pgvector
Question → embed → pgvector similarity search → cited passages → answer (CLI / chat UI / MCP)

Stack (as built)

Layer Choice Notes
Language Python 3.13
Vision extraction Anthropic Messages API, claude-opus-5 (default) structured JSON output; EXTRACT_MODEL swappable to claude-sonnet-5 for a cheaper bulk run
Embeddings Voyage voyage-3.5, 1024-dim Anthropic has no embeddings API; separate signup
Vector store PostgreSQL 18 + pgvector 0.8.6 native Windows, no Docker; pgvector compiled from source (see pgvector-build/), HNSW cosine index
PDF rendering PyMuPDF (fitz) every page → PNG at 150 DPI (scanned or digital)
Serving MCP (stdio) + CLI + local chat UI 5 MCP tools; answers cite source pages

Prerequisites

  • Python 3.11+
  • PostgreSQL 18 (installed at C:\Program Files\PostgreSQL\18, port 5433) with the pgvector extension. pgvector ships no Windows binaries, so it's compiled from source (step 1) and installed with a script. The compiled .dll is not committed; build it locally. No Docker.
  • API keys: ANTHROPIC_API_KEY (vision extraction) and VOYAGE_API_KEY (embeddings). Anthropic doesn't do embeddings, so Voyage is a separate signup.

Setup (native, no Docker)

python -m venv .venv
. .venv/Scripts/activate            # PowerShell: .venv\Scripts\Activate.ps1
pip install -r requirements.txt
cp .env.example .env                 # then fill in the two API keys

1. Build pgvector for PostgreSQL 18 (the compiled .dll isn't committed). From an x64 Native Tools Command Prompt for VS:

git clone --branch v0.8.6 https://github.com/pgvector/pgvector.git
cd pgvector
set PGROOT=C:\Program Files\PostgreSQL\18
nmake /f Makefile.win

Copy the outputs into pgvector-build\ (where the install script looks):

Copy-Item pgvector\vector.dll, pgvector\vector.control, pgvector\sql\vector--*.sql pgvector-build\

On PG18 the standard build links cleanly. (On PG17, EDB's build did not export float_to_shortest_decimal_*; a small shim defining those functions, added to OBJS in Makefile.win, is required. PG18 exports them, so no shim is needed.)

2. Install pgvector into PostgreSQL 18 (copies the files into Program Files — needs admin; the script self-elevates via UAC):

powershell -ExecutionPolicy Bypass -File scripts\install-pgvector.ps1

3. Create the database + role + extension (prompts for the postgres superuser password; PG18 is on port 5433):

& "C:\Program Files\PostgreSQL\18\bin\psql.exe" -U postgres -p 5433 -f scripts\bootstrap-db.sql

4. Create the Ask Cooter tables:

python -m askcooter.cli init-db

Ingest a PDF (one-time, costs API money)

This renders every page of the configured PDF (PDF_PATH), runs each through the vision model, embeds the text, and stores everything. It is resumable — re-run the same command to retry any pages that failed, and it auto-retries the output content-filter false positives with a model fallback.

Recommended: prototype on a small page range first to eyeball quality and cost:

python -m askcooter.cli ingest --start 88 --end 92      # example pages
python -m askcooter.cli query "primary chaincase oil"

Then run the whole document:

python -m askcooter.cli ingest

Point it at a different PDF: set PDF_PATH in .env. One PDF per database — to switch corpora, use a fresh DATABASE_URL (or truncate pages/chunks and re-init-db), since results are ranked across whatever is in the DB.

Cost note: extraction is one vision call per page (651 for the Softail manual). EXTRACT_MODEL defaults to claude-opus-5 (highest fidelity); set EXTRACT_MODEL=claude-sonnet-5 in .env for a much cheaper run — usually fine for OCR-style extraction. Embeddings (Voyage) are cents.

Ask a question (direct, cited answer)

ask retrieves the relevant passages, hands them to Claude, and returns a direct answer with source-page citations:

python -m askcooter.cli ask "how much oil does the primary chaincase hold?"

query is the raw retrieval view (top matching passages, no synthesis) — useful for debugging what the vector search finds:

python -m askcooter.cli query "how do I test the starter solenoid?"
python -m askcooter.cli sections

Chat UI

A local single-page chat with streaming answers, rendered markdown, multi-turn context, and clickable source citations that open the original page — extracted text plus the scanned image, with zoom and page-flip:

python -m askcooter.cli web            # http://127.0.0.1:8000

A bike profile (year + model, default 1986 Softail Custom) tailors answers to your machine. Questions and their answers are saved to Postgres (the user_history table, keyed by an opaque per-browser token); the history sidebar loads from the DB, and clicking a past question replays its saved answer with sources — no API call. Self-contained (no external assets), light/dark aware. Uses ANSWER_MODEL and the keys from .env. Bind elsewhere with --host / --port.

Accessing the database

The data lives in PostgreSQL 18 (askcooter DB, role cooter, port 5433).

  • psql shell: powershell -ExecutionPolicy Bypass -File scripts\db-shell.ps1 (or & "C:\Program Files\PostgreSQL\18\bin\psql.exe" -U cooter -p 5433 -d askcooter)
  • pgAdmin 4 (installed by the EDB Postgres installer): add a server → host localhost, port 5433, database askcooter, user cooter.
  • Any GUI (DBeaver, TablePlus): connect to localhost:5433 / askcooter / cooter.

Handy queries: SELECT count(*) FROM pages; · SELECT pdf_page, printed_page, section FROM pages ORDER BY pdf_page; · SELECT count(*) FROM chunks; · SELECT question, created_at FROM user_history ORDER BY id DESC;

Use as an MCP server

Run over stdio:

python -m askcooter.cli serve

Register with Claude Desktop (claude_desktop_config.json):

{
  "mcpServers": {
    "ask-cooter": {
      "command": "python",
      "args": ["-m", "askcooter.cli", "serve"],
      "cwd": "C:/app/AskCooter",
      "env": {
        "DATABASE_URL": "postgresql://cooter:cooter@localhost:5433/askcooter",
        "VOYAGE_API_KEY": "pa-..."
      }
    }
  }
}

(The server needs VOYAGE_API_KEY to embed incoming queries and DATABASE_URL to reach Postgres. It also needs ANTHROPIC_API_KEY if the ask tool is used — add it to the env block above. search_manual alone does not require it, since the MCP host does the synthesis.)

MCP tools

Tool Purpose
ask(question, limit) Direct, cited answer (retrieval + Claude synthesis)
search_manual(query, limit, component) Semantic search; returns cited passages
get_page(pdf_page) Full extracted content of one page + specs + figures
get_torque_spec(component) Structured spec lookup (torque, clearances, capacities)
list_sections() Section table of contents with page ranges

Project layout

askcooter/
  config.py            env-based settings
  db.py                pgvector connection + schema init
  schema.sql           DDL (pages, chunks, user_history)
  ingest/
    render.py          PDF page → PNG
    extract.py         Claude vision → structured JSON
    chunk.py           structure-aware chunking
    embed.py           Voyage embeddings
    pipeline.py        resumable orchestration
  retrieval.py         query embed + pgvector search + spec/section lookups
  answer.py            retrieval + Claude synthesis (the `ask` layer)
  history.py           user_history persistence (record / list / clear)
  mcp_server.py        FastMCP server (5 tools)
  cli.py               init-db / ingest / ask / query / sections / web / serve
  web/
    app.py             FastAPI backend: /api/ask, /api/page, /api/meta, /api/history
    index.html         single-page chat UI
scripts/
  install-pgvector.ps1 copy the built extension into PG18 (admin)
  bootstrap-db.sql     create role/db/extension
  db-shell.ps1         open a psql shell to the DB

Notes & limits

  • Page numbers: pdf_page is 0-based internally; the CLI and tools print the 1-based PDF page plus the manual's own printed label so you can always find the original.
  • Copyright: your source PDF may be copyrighted (the Softail manual is). Keeping Ask Cooter private/personal is the intended use — see DESIGN.md §6 before considering any public deployment.
  • User history: the chat UI writes each Q&A to the user_history table, keyed by an opaque client token (no auth). Past questions reload from the DB and replay their saved answers. GET/DELETE /api/history back the sidebar.

License

PolyForm Noncommercial License 1.0.0 — see LICENSE. Copyright 2026 SmallAxeIT. Free to use, modify, and share for noncommercial purposes only; commercial use is not granted.

The Harley-Davidson Softail service manual is not part of this repository and is not covered by this license; it is copyrighted by its publisher and excluded from version control (see .gitignore).

Known limitations & tech debt

  • Embedding dimension is hardcoded in schema.sql (VECTOR(1024)). It must match EMBED_DIM and the Voyage model's native dimension. Changing VOYAGE_MODEL to a different-dimension model requires editing the DDL and a full re-embed; there is no migration path.
  • Ingest is sequential. ~1–2s per page (≈15–25 min for 651 pages). A thread pool over pages would parallelize it. Retry is re-running the command (per-page commit + skip); the Anthropic SDK retries 429/5xx transient failures.
  • Citation fidelity is prompt-enforced, not guaranteed. ask / answer.py synthesize a cited answer server-side; search_manual returns raw passages for an MCP host to synthesize. In both paths the model is instructed to cite; there is no post-check that every claim maps to a retrieved page.
  • Chunking uses a char≈token approximation (len//4). The value is stored as metadata only; chunk boundaries are character-bounded, not token-based.
  • Windows/EDB-specific build. The pgvector build and install scripts hardcode C:\Program Files\PostgreSQL\18 and the MSVC toolchain. Not portable as-is.
  • No automated tests. Verification is manual (compile, render, live query).
  • Follow-up retrieval uses the current question only. The chat UI passes prior turns to the answer model, but retrieval embeds just the latest question, so elliptical follow-ups ("what about the front one?") can retrieve poorly. Query rewriting would address it.
  • History accumulates duplicate rows. Every ask inserts a user_history row, so re-asking a question stores it again; the sidebar dedups for display, but the table grows unbounded. Dedup-on-insert or periodic pruning would bound it.
  • History token travels in the query string. GET/DELETE /api/history?user_token=… can land in server/access logs. Harmless locally; move to a header or POST body before any networked deployment.
  • printed_page is model-read per page. OCR misreads of the printed label are possible; pdf_page is authoritative for jumping.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选