Skip to main content

Python SDK and MCP server for self-hosted PageIndex RAG service

Project description

PageServe

Python SDK and MCP server for PageIndex — the self-hosted, reasoning-based RAG engine.

PyPI version Python versions License Checked with mypy Ruff

Installation · Quick Start · MCP Server · CLI · Documentation


PageServe is a typed, batteries-included client for PageIndex, a reasoning-based RAG engine that navigates document structure instead of doing vector similarity search. It reads the table of contents, picks the right sections, and synthesizes answers with page-level citations — no embeddings, no vector database, no chunking heuristics.

One install gives you three ways to talk to your PageIndex deployment:

  • 🐍 A Python SDK — synchronous and asynchronous clients, fully typed with Pydantic models.
  • 🤖 An MCP server — drop your documents into Claude Desktop, Cursor, or any MCP-compatible agent.
  • ⌨️ A CLI — query, upload, and manage documents straight from the terminal.

Features

  • Vector-free retrieval — answers come with exact page references (Employment Contract p.5, 6), not opaque similarity scores.
  • Query or retrievequery() synthesizes an answer; retrieve() returns the raw section content (cheaper, ideal for your own prompts).
  • Sync & asyncPageServeClient and AsyncPageServeClient, including concurrent query_many.
  • Streaming — token-by-token responses over Server-Sent Events.
  • Model Context Protocol — expose your corpus to LLM agents with keys kept out of the model's context.
  • Fully typed — Pydantic v2 models and strict, descriptive exceptions for every failure mode.
  • Minimal core — only httpx and pydantic required; MCP and CLI are opt-in extras.

Installation

pip install pageserve              # SDK only (sync + async)
pip install "pageserve[mcp]"       # + MCP server for agent frameworks
pip install "pageserve[cli]"       # + CLI commands
pip install "pageserve[all]"       # everything

Requires Python 3.10+.

Quick Start

from pageserve import PageServeClient

client = PageServeClient(
    base_url   = "https://pageindex.company.com",
    public_key = "<your-public-key>",
    secret_key = "<your-secret-key>",
)

# List indexed documents
docs = client.list_documents()

# Ask a question
result = client.query(docs[0].doc_id, "What are the probation terms?")
print(result.answer)
print(result.citation)   # "Employment Contract p.5, 6"
print(result.page_refs)  # [5, 6]

# Or retrieve the raw source sections without synthesizing an answer (cheaper)
retrieved = client.retrieve(docs[0].doc_id, "What are the probation terms?")
print(retrieved.text)    # all relevant section text, ready to drop into a prompt

# Upload and wait for indexing to complete
upload = client.upload("./contract.pdf", wait=True)

# Read specific pages (no LLM, instant)
pages = client.get_pages(docs[0].doc_id, "22-24")

Async Usage

import asyncio
from pageserve import AsyncPageServeClient

async def main():
    async with AsyncPageServeClient(
        base_url   = "https://pageindex.company.com",
        public_key = "<your-public-key>",
        secret_key = "<your-secret-key>",
    ) as client:
        docs = await client.list_documents()

        # Query multiple documents concurrently
        results = await client.query_many([
            (docs[0].doc_id, "What are the probation terms?"),
            (docs[1].doc_id, "What does the law say about probation?"),
        ])
        for r in results:
            print(r.answer)

asyncio.run(main())

Streaming

for event in client.query_stream(doc_id, "What are the key clauses?"):
    if event.type == "token":
        print(event.content, end="", flush=True)
    elif event.type == "sources":
        for src in event.sources:
            print(f"\nSource: {src.citation}")
    elif event.type == "done":
        break

MCP Server

Run the MCP server so Claude Desktop, Cursor, or any MCP-compatible agent can query your documents:

{
  "mcpServers": {
    "pageindex": {
      "command": "pageserve",
      "args": ["mcp"],
      "env": {
        "PAGESERVE_URL":        "https://pageindex.company.com",
        "PAGESERVE_PUBLIC_KEY": "<your-public-key>",
        "PAGESERVE_SECRET_KEY": "<your-secret-key>"
      }
    }
  }
}

The server exposes seven tools:

Tool Description
list_documents See what documents are available
query_document Ask a question against one document
query_multiple_documents Cross-reference multiple documents
retrieve_document Return raw section content without synthesizing an answer
get_page_content Read raw page text (no LLM, instant)
get_document_structure Browse the table of contents
get_service_health Check service status and queue

Keys live in environment variables and never appear in tool arguments or the model's context window.

CLI

export PAGESERVE_URL=https://pageindex.company.com
export PAGESERVE_PUBLIC_KEY=<your-public-key>
export PAGESERVE_SECRET_KEY=<your-secret-key>

pageserve list                                      # list documents
pageserve query <doc_id> "question"                 # ask a question
pageserve query <doc_id> "question" --stream        # streaming output
pageserve retrieve <doc_id> "question"              # raw sections, no answer
pageserve upload ./report.pdf --watch               # upload + progress bar
pageserve health                                    # service status
pageserve keys list                                 # list API keys
pageserve keys create "My App"                      # create a key
pageserve mcp                                       # run MCP server (stdio)
pageserve mcp --transport sse --port 3000           # MCP over SSE

Error Handling

Every failure mode maps to a specific, catchable exception:

from pageserve import (
    AuthError,
    NotFoundError,
    DocumentNotReadyError,
    FileTooLargeError,
    RateLimitError,
    ServiceUnavailableError,
    ServiceError,
)

try:
    result = client.query(doc_id, "question")
except AuthError:
    print("Invalid or expired API key")
except NotFoundError:
    print("Document not found")
except RateLimitError as e:
    import time; time.sleep(e.retry_after)
except ServiceError as e:
    print(f"Server error [{e.status_code}]")

Documentation

Guide
Getting Started Install, configure, and run your first query
Authentication API keys and credential handling
Sync Client Reference PageServeClient API
Async Client Reference AsyncPageServeClient API
Streaming (SSE) Token-by-token responses
MCP Server Tools, transports, and agent setup
CLI Reference All commands and flags
Data Models Pydantic response models
Error Handling Exception hierarchy

Contributing

Contributions are welcome! To set up a development environment:

git clone https://github.com/pageserve/pageserve.git
cd pageserve
pip install -e ".[all,dev]"

pytest          # run the test suite
ruff check .    # lint
mypy pageserve  # type-check

Please open an issue to discuss substantial changes before submitting a pull request.

License

Licensed under the Apache License 2.0. © 2026 PageServe.

Related

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pageserve-0.1.1.tar.gz (26.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pageserve-0.1.1-py3-none-any.whl (31.4 kB view details)

Uploaded Python 3

File details

Details for the file pageserve-0.1.1.tar.gz.

File metadata

  • Download URL: pageserve-0.1.1.tar.gz
  • Upload date:
  • Size: 26.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pageserve-0.1.1.tar.gz
Algorithm Hash digest
SHA256 55273e0eb794e1729ea3ae3105b38da4eaeca41e286b490cf6a6624d2ac1a01c
MD5 23d5f299ad93cdfe14ae1c14b4c9207d
BLAKE2b-256 cceb3619d8e9bec70a9359dc41632017196c37af3ec00b62cfdcc691d23bea0f

See more details on using hashes here.

Provenance

The following attestation bundles were made for pageserve-0.1.1.tar.gz:

Publisher: publish.yml on pageserve/pageserve

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pageserve-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: pageserve-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 31.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pageserve-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d74a8984c200fee9061126f0ed4053cf2591fa20117f50435a15d55880896225
MD5 4085b247e49049c82f6dc6f130c4bb0d
BLAKE2b-256 274504c666e574d6d9985b55ac49f5ba9448c5dfe15f39b5c4ed21463aef340f

See more details on using hashes here.

Provenance

The following attestation bundles were made for pageserve-0.1.1-py3-none-any.whl:

Publisher: publish.yml on pageserve/pageserve

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page