API & MCP access

The corpus is reachable two ways: a deployed Model Context Protocol server (Streamable HTTP) exposing 11 tools, and a plain REST shim at /api/v1/* for clients that don't speak MCP. Both are public and require no authentication.

MCP endpoint

https://project-ggqu9.vercel.app/mcp

Transport: MCP Streamable HTTP. POSTs require the standard MCP headers; spec-compliant clients set these automatically.

Content-Type: application/json
Accept: application/json, text/event-stream

Tools exposed

ask_research_question(query: string, mode_override?: 'lookup' | 'claim_history' | 'qa' | null)
Synthesised, cited answer drawn from voynich.ninja threads, researcher blogs (Pelling, O'Donovan), academic papers, and reference sites (voynich.nu). Auto-routes; start here for most questions.
search_sources(query: string, max_results?: number = 5, similarity_threshold?: number = 0.5)
Raw hybrid retrieval (vector + FTS) over the citation ledger. Returns ranked passages with similarity, passage_year, passage_author.
trace_claim_history(claim: string, include_passages?: boolean = false)
Historiographic evolution of a tracked claim — earliest advocacy, modern advocates/rejecters, verification gate. 30–45s.
verify_quotation(quoted_text: string, source_id: string, paragraph_index?: number | null)
Fuzzy-match a quotation against a named source. Use before presenting any direct quote.
cite_passage(source_id: string, paragraph_index?: number = 0)
Canonical text of a specific paragraph plus a citation string.
list_sources(limit?: number = 50, offset?: number = 0)
Paginate the full set of primary sources in the ledger.
source_status(source_id: string)
Metadata for one source — type, URL, title, fetch time, word count, sha256.
list_claims()
Every hypothesis tracked in the citation graph's claim vocabulary.
query_edges(claim_id?, source_id?, passage_author_id?, nested_author_id?, stance?, min_confidence?, year_min?, year_max?, extraction_version?, include_text?: boolean = false, limit?: number = 50, offset?: number = 0)
Filter the passage_claim_edges graph by structural criteria — claim, source, author, stance, confidence, year range.
author_cooccurrence(seed_claim_id?, seed_author_id?, co_with?: 'author' | 'claim' = 'author', min_stance?, limit?: number = 20)
Authors or claims that co-occur with a seed in the edge graph.
find_convergence(seed_source_id: string, seed_paragraph_index: number, min_similarity?: number = 0.85, limit?: number = 10, cross_source_only?: boolean = true, cross_author_only?: boolean = false)
Independent corroborations — passages with high semantic similarity to a seed, restricted to different sources (or authors).

Claude — remote MCP

Claude Desktop's mcpServers config currently only spawns local stdio servers. Bridge to the remote URL with mcp-remote:

{
  "mcpServers": {
    "voynich-research": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://project-ggqu9.vercel.app/mcp"]
    }
  }
}

On claude.ai (web), add it directly under Settings → Integrations → Add custom integration, pointing at the URL above. No bridge needed.

OpenAI — Responses API with MCP

The Responses API accepts MCP servers as a tool type. The model can then call any of the 11 tools above without further wiring:

const resp = await openai.responses.create({
  model: "gpt-5",
  tools: [{
    type: "mcp",
    server_label: "voynich-research",
    server_url: "https://project-ggqu9.vercel.app/mcp",
    require_approval: "never"
  }],
  input: "Who first proposed the Voynich slot grammar?"
});

For the Chat Completions API (no MCP), call the REST shim directly from a function-calling tool — see below.

Google Gemini — MCP via SDK

The Gen AI SDK can attach an MCP client session as a tool. Connect once via Streamable HTTP, then pass the session to generateContent:

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport }
  from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const mcpClient = new Client({ name: "gemini-client", version: "1.0.0" });
await mcpClient.connect(
  new StreamableHTTPClientTransport(new URL("https://project-ggqu9.vercel.app/mcp"))
);

const response = await ai.models.generateContent({
  model: "gemini-2.5-pro",
  contents: "Trace the forgery hypothesis through time.",
  config: { tools: [mcpClient] }
});

Raw HTTP — REST shim

Every MCP tool is also exposed as a plain JSON endpoint for clients that don't speak MCP. Public, 5–25s latency on synthesis tools, occasional cold-start 503 (retry after 2s).

GET  https://project-ggqu9.vercel.app/api/v1/tools
POST https://project-ggqu9.vercel.app/api/v1/{tool_name}
Content-Type: application/json

# body is the named-argument JSON for that tool, e.g.
{
  "query": "Was Hartlieb involved in the Voynich Manuscript?",
  "mode_override": null
}

GET /tools returns the live index with each tool's parameter schema. Response from POST /{tool_name} is the tool's native return shape — no envelope.

Try it (curl)

curl -X POST 'https://project-ggqu9.vercel.app/api/v1/ask_research_question' \
  -H 'Content-Type: application/json' \
  -d '{"query": "Who first proposed Voynich was written by Roger Bacon?"}'