mcpField
P
Paparats MCP: Self-Hosted Semantic Code Search

Paparats MCP: Self-Hosted Semantic Code Search

Local
github.comDeveloper Tools34Updated today

About Paparats MCP: Self-Hosted Semantic Code Search

Paparats MCP is a self-hosted semantic code-search server for teams working across multiple repositories. AST-aware indexing, symbol relationships, Git history, and agent-maintained architectural memory provide code and design context.

Key features

  1. 01AST-aware indexing across 11 languages
  2. 02Cross-chunk symbol graph
  3. 03Git history and ticket metadata
  4. 04Semantic search with query expansion
  5. 05Agent-maintained architectural memory

Use cases

  1. 01Search code across multiple repositories
  2. 02Trace symbol relationships between code chunks
  3. 03Review architectural decisions and lessons
  4. 04Search curated documentation through a scoped endpoint

MCP support

Exposes coding and support tools through separate MCP endpoints, plus an optional documentation-only endpoint. MCP clients can connect over Streamable HTTP; legacy SSE routes are also available.

Configuration

Local server config is the same for every client. Copy it into your client’s MCP config file.

mcp.json · Universal config
{
"mcpServers": {
"ibazylchuk-paparats": {
"command": "npx",
"args": [
"-y",
"@paparats/cli"
]
}
}
}
Run commandnpx -y @paparats/cli

Local servers need a runtime on your machine (e.g. Node.js 18+). Config file locations differ by client; see your client’s docs.

Packages
npm@paparats/cli2.16.3

Tools

No tool list yet

Local servers expose tools only once running on your machine, and the maintainer has not provided a tool list.

Details

From the project README

@paparats/server

The MCP server for Paparats MCP — semantic code search over multiple repos with AST-aware chunking, a cross-chunk symbol graph, git-history per chunk, and an agent-maintained architectural memory layer.

Not published to npm. This package is consumed as a library by @paparats/indexer and shipped as a Docker image: ibaz/paparats-server. End users install through @paparats/cli.

What this package does

Three jobs in one process:

  • Indexer. AST-aware chunking (tree-sitter, 11 languages), per-chunk symbol extraction, single-parse pipeline, llama-server embedding with SQLite cache, batched Qdrant upsert. Post-index passes attach git history, ticket references, and cross-chunk symbol edges (calls / called_by / references / referenced_by).
  • Searcher. Task-prefixed embedding (nl2code / code2code / techqa), query expansion for the support tools (abbreviations, case variants, plurals, filler removal), in-memory LRU query cache with per-group invalidation, confidence scoring, token-savings telemetry.
  • MCP server. Three endpoints — /mcp (coding), /support/mcp (support) and the opt-in, docs-only /knowledge/mcp (knowledge) — each with its own tool set and system instructions. Streamable HTTP for every client, with legacy SSE routes kept. Plus an agent-maintained architectural memory layer.

Endpoints

http://<host>:9876/health                  – liveness probe
http://<host>:9876/metrics                 – Prometheus (PAPARATS_METRICS=true)
http://<host>:9876/ui                      – operator console
http://<host>:9876/api/analytics/...       – analytics REST API (UI backend)

http://<host>:9876/mcp                     – MCP, coding mode (Streamable HTTP)
http://<host>:9876/sse                     – MCP, coding mode (SSE / Cursor)
http://<host>:9876/messages                – MCP, coding mode (SSE message channel)

http://<host>:9876/support/mcp             – MCP, support mode
http://<host>:9876/support/sse             – MCP, support mode (SSE)
http://<host>:9876/support/messages        – MCP, support mode (SSE message channel)

http://<host>:9876/knowledge/mcp           – MCP, knowledge mode (stateless, bearer auth,
                                             POST only; mounted only when configured)

POST /api/index registers a project; POST /api/file-changed and POST /api/file-deleted update single files. See docs/tools.md for the full tool inventory per endpoint.

Knowledge mode

A docs-only MCP endpoint for a consumer in another network — an agent backend behind a load balancer that may search curated documentation and nothing else. Off unless PAPARATS_KNOWLEDGE_TOKENS and PAPARATS_KNOWLEDGE_AUDIENCE are both set; one without the other stops the server at startup.

  • Tools: search_docs and get_document only. No glossary block, no resources, no workflow prompts. search_docs returns structuredContent with an outputSchema, so a wrapping service can build citations without parsing markdown.
  • Auth: Authorization: Bearer <token>, compared in constant time, checked before the request body is read. 401 + WWW-Authenticate: Bearer otherwise.
  • Scope: documents labelled with an audience in PAPARATS_KNOWLEDGE_AUDIENCE (never internal), in the projects of PAPARATS_KNOWLEDGE_PROJECTS when set. Both are intersected with PAPARATS_DOCS_AUDIENCE / PAPARATS_PROJECTS when those are set. Out-of-scope documents read exactly like missing ones.
  • Transport: stateless Streamable HTTP — a fresh server and transport per POST, no session id; GET/DELETE answer 405. 64 kB body limit, 1000-character query cap.

Documents get their audience at index time: frontmatter audience: first, then docs.audience from .paparats.yml / projects.yml, otherwise internal. An invalid frontmatter value is logged and stored as internal. See the documentation search guide.

Set PAPARATS_KNOWLEDGE_PROJECTS — otherwise a stray audience: in some code repository's README is enough to widen what the endpoint serves.

Expose only that path, POST only. For example with nginx:

location = /knowledge/mcp {
  limit_except POST { deny all; }
  client_max_body_size 64k;
  proxy_pass http://paparats:9876;
  proxy_http_version 1.1;
  proxy_buffering off; # responses stream as text/event-stream
}
location / {
  return 404;
}

Architectural memory

Code search tells the agent what the code does. The arch-memory layer tells it why — and the agent maintains that knowledge itself, across sessions, without you authoring a single doc.

Three card kinds in a separate Qdrant collection per group (paparats_<group>_arch), embedded with qwen3-embedding-0.6b (1024d, last-pooled):

KindFieldsIdempotency
Componentname, summary (markdown: Does / Owns / Does not / Touched when), files, neighbours, anchorsBy name (structural)
Decisiontitle, context, decision, alternatives_rejected, consequences, scope, supersedesServer-side similarity gate
Lessonrule, why, when, scope, severity, evidenceServer-side similarity gate

Similarity gate (cosine on qwen3-embedding-0.6b):

  • >= 0.85 → duplicate. Decisions are refused so the agent must reconcile or supersede. Lessons bump updatedAt on the existing card (Reflexion "rule confirmed").
  • 0.70 – 0.85 → similar. Nothing is written; the agent should refine wording or pass supersedes.
  • < 0.70 → accepted as a new card.

supersedes bypasses the gate and marks the prior decision status=superseded so it disappears from default search but stays in history.

Reading via arch_context accepts min_score (default 0.45). Each returned card carries an updated N ago stamp and the cosine score so the agent can detect stale or low-confidence hits.

Bootstrap with init_arch_memory — a workflow prompt analogous to /init in Claude Code. Walks the repo, identifies 8-20 components by domain boundary, writes the cards. Followed by audit_architecture (sweep stale cards) and record_lesson_from_correction (turn user corrections into structured lessons).

MCP resources:

  • arch://schema — full card-schema reference.
  • arch://stats/{group} — live counts (total / by kind / by status) and oldest/newest updatedAt.

Prometheus metrics (PAPARATS_METRICS=true):

  • paparats_arch_context_calls_total{group} — counter
  • paparats_arch_write_total{kind, status} — counter labelled by gate outcome
  • paparats_arch_search_score — histogram of cosine scores from arch_context
  • paparats_arch_collection_size{group, kind, status} — gauge

arch_context is read-only on both MCP endpoints. arch_record_* is support-only — recording belongs to the architectural-review workflow.

Module map

ModuleResponsibility
types.tsShared interfaces
lib.tsPublic library entry point (re-exports for programmatic use)
config.ts.paparats.yml reader, 11 language profiles, autoProjectConfig()
app.tsExpress app factory, HTTP API routes, request wiring
index.tsServer bootstrap — starts HTTP server, wires components, graceful shutdown
ast-chunker.tsAST-based chunking via tree-sitter
chunker.tsRegex chunking fallback (4 strategies)
ast-symbol-extractor.tsAST-based per-chunk symbol extraction
ast-queries.tsTree-sitter S-expression queries per language
tree-sitter-parser.tsWASM tree-sitter manager — lazy grammar loading
symbol-graph.tsCross-chunk symbol edges
embeddings.tsllama-server provider + SQLite cache + cached wrapper
indexer.tsGroup-aware Qdrant indexing — createQdrantClient(), toCollectionName(), single-parse chunkFile()
searcher.tsVector search with query expansion, cache, metrics
query-expansion.tsAbbreviation / case / plural / filler expansion
task-prefixes.tsQuery-type detection + bge-code-v1 / Qwen3 query instructions
query-cache.tsIn-memory LRU cache with TTL and per-group invalidation
metrics.tsPrometheus metrics (prom-client) + NoOpMetrics fallback
metadata.tsTag resolution + auto-detection from directory structure
metadata-db.tsSQLite store for git commits, tickets, symbol edges
git-metadata.tsGit history extraction — diff-hunk mapping to chunks
ticket-extractor.tsJira / GitHub / custom ticket parsing
mcp-handler.tsMCP protocol — coding, support and knowledge endpoints with isolated tool sets and instructions
knowledge-mode.tsKnowledge mode — env parsing, bearer auth, body limit, scope intersection
arch/types.tsArchitectural-memory types
arch/collection.tsPer-group paparats_<group>_arch collection lifecycle
arch/text-embeddings.tsqwen3-embedding-0.6b text embedder via llama-server
arch/store.tsCRUD + server-side similarity gate + stats() aggregation
arch/context.tsarch_context query — top-N across kinds with min_score and age stamps

Configuration (env vars)

VariableDefaultNotes
PORT9876HTTP port
QDRANT_URLhttp://localhost:6333Qdrant endpoint (https → port 443, http → 6333)
QDRANT_API_KEY—Qdrant Cloud API key
EMBED_URLhttp://127.0.0.1:18434Embed server endpoint (llama-swap; internal port 8080, host port 18434 in the generated stack)
EMBED_BATCH_SIZE—Embeddings per llama-server call
EMBED_TTL0Idle seconds before a model unloads; 0 = never (default)
EMBEDDING_PROVIDERllamaCode embedding provider: llama | openai | voyage
EMBEDDING_MODELbge-code-v1Code embedding model
EMBEDDING_DIMENSIONS1536Code embedding dimensions
TEXT_EMBEDDING_PROVIDERllamallama or openai for the arch layer
TEXT_EMBEDDING_MODELqwen3-embedding-0.6bArch-layer text embedding model
TEXT_EMBEDDING_DIMENSIONS1024Arch-layer text embedding dimensions
PAPARATS_METRICSfalseSet to true to expose /metrics
PAPARATS_PROJECTS—Comma-separated allow-list of project names
PAPARATS_DOCS_AUDIENCE—Comma-separated audience ceiling for search_docs / get_document on the coding and support endpoints
PAPARATS_KNOWLEDGE_TOKENS—Comma-separated bearer tokens (≥ 16 characters) for /knowledge/mcp. Set together with PAPARATS_KNOWLEDGE_AUDIENCE, or neither
PAPARATS_KNOWLEDGE_AUDIENCE—Audience ceiling of /knowledge/mcp; internal is refused
PAPARATS_KNOWLEDGE_PROJECTS—Optional project allowlist of /knowledge/mcp — recommended
PAPARATS_UI_BASIC_AUTH—user:pass for /ui and /api/analytics/...; a malformed value stops startup
PAPARATS_CORS_ORIGINS—Comma-separated exact origins (e.g. https://app.example.com) browsers may call the server from. Unset = no CORS headers, and any request whose Origin is neither this server nor listed gets 403. Requests without Origin (MCP clients, curl) are unaffected
OTEL_*—Standard OpenTelemetry env vars (Tempo / Honeycomb / Datadog / Elastic APM)

Programmatic use (library)

The package.json exports map points at dist/lib.js, not the server bootstrap. Importing @paparats/server does not start the HTTP server — @paparats/indexer uses this to embed the Indexer class as a library:

import { Indexer, createQdrantClient } from '@paparats/server';

const qdrant = createQdrantClient({ url: process.env.QDRANT_URL! });
const indexer = new Indexer({ qdrant, /* ... */ });
await indexer.indexProject(group, project);

Docker

docker pull ibaz/paparats-server:latest
docker run --rm -p 9876:9876 \
  -e QDRANT_URL=http://qdrant:6333 \
  -e EMBED_URL=http://embed:8080 \
  ibaz/paparats-server:latest

See examples/docker-compose for a complete stack (Qdrant + embed server + server + indexer).

Development

yarn install
yarn workspace @paparats/server build
yarn workspace @paparats/server test
yarn workspace @paparats/server typecheck

Related packages

PackagePurpose
@paparats/cliOne-command installer, project management, search
@paparats/indexerAutomated multi-repo indexer (uses this as a library)
@paparats/sharedShared utilities (path validation, gitignore, excludes)
ibaz/paparats-embedllama.cpp llama-server + llama-swap with pre-baked bge-code-v1 and qwen3-embedding-0.6b

License

MIT

Comments

Markdown supported

New to MCP?

From what the protocol is to installing your first server, these answers cover most first questions.

An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.