Jama MCP Server
Enables LLM clients to semantically search and query metadata within Jama requirements management via RAG and native API filters.
README
Jama MCP Server
A production-grade Model Context Protocol (MCP) server for the Jama requirements management system, combining high-precision RAG retrieval with native REST API filtering. An LLM client (Claude Desktop, etc.) can autonomously choose between semantic search and structured metadata queries.
Architecture
┌──────────────────────── MCP (stdio) ────────────────────────┐
│ │
LLM ──────┤ init_jama_project get_sync_progress │
Client │ search_jama_semantics query_jama_native_metadata │
│ │
│ server.py (FastMCP + APScheduler + thread pool) │
│ │ │
│ ├── rag_pipeline.py (Multi-Query + Hybrid + RRF + │
│ │ Qwen3-Reranker) │
│ ├── jama_client.py (OAuth2 + pagination + HTML clean)│
│ └── db_setup.py (SQLite + FTS5 + sqlite-vec) │
│ │
└──────────────────────────────────────────────────────────────┘
│ │
Jama REST API Azure OpenAI embeddings
(read-only GET) (text-embedding-3-small)
Retrieval pipeline (search_jama_semantics)
- Multi-Query — the query is expanded into 3-5 sub-queries. The MCP LLM
client performs the expansion and passes the variants via the
sub_queriesparameter; when none are supplied, the server falls back to deterministic lexical variants (stopword-stripped + truncated) so RRF fusion still benefits from multiple recall angles. No server-side chat LLM is configured or called. - Hybrid recall — for each sub-query: vector recall (sqlite-vec, cosine)
- keyword recall (FTS5, BM25), each capped at
candidate_k.
- keyword recall (FTS5, BM25), each capped at
- RRF fusion — Reciprocal Rank Fusion merges all ranked lists into one
candidate pool of ≤
candidate_kunique chunks. - Rerank — local Qwen3-Reranker-0.6B (CPU, via
transformers) scores(query, chunk)pairs via theP("yes")token probability; toptop_kreturned. If the model is unavailable, the pipeline gracefully falls back to RRF scores. Model weights are fetched from the HuggingFace China mirror (HF_ENDPOINT=https://hf-mirror.com) on first use, then served from cache.
Reliability & crash recovery
The server is designed to survive crashes without losing data and to come back up consistent on restart:
- Atomic per-item indexing — each item's chunks (text + FTS5 + sqlite-vec)
are replaced in a single
write_txn(BEGIN IMMEDIATE), so a crash mid-sync never leaves a half-written item.done/progressonly advance after the commit, so the DB is consistent up to the last flushed batch. - Idempotent re-sync — upserts overwrite (never duplicate), so re-processing already-indexed items on resume is harmless.
- Startup recovery —
_resume_interrupted_syncsre-queues any project leftINITIALIZINGby a prior crash, so the server self-heals without manual action. - Concurrency guard —
init_jama_projectrefuses a duplicate concurrent sync for a project that already has a job in flight, returning the existingjob_idinstead of spawning a racing second worker. - Bounded HTTP retries — 429 rate-limit handling is a bounded loop (not
recursion), so a persistent rate-limit fails cleanly instead of overflowing
the stack;
Retry-Afterparsing tolerates non-numeric values; a 401 mid-sync refreshes the token and retries the page; malformed JSON bodies are retried. - WAL mode + write lock — SQLite runs in WAL with a process-wide write
lock, so the scheduler's writer and MCP reader threads coexist without
SQLITE_BUSYfailures.
Chunking (LlamaIndex)
Jama rich-text (Description / Test Case Steps) is cleaned to plain text with
BeautifulSoup before being wrapped in LlamaIndex Document objects. The
documents are split into TextNode chunks by LlamaIndex's SentenceSplitter
(recursive, sentence-aware; chunk_size=512, chunk_overlap=80 to preserve
context for the ~30% long-form items). The item name is prepended to each chunk
so the title is always retrievable.
Native API (query_jama_native_metadata)
Bypasses the vector store for exact-match questions (specific document key,
status, item type). Uses /abstractitems which honours itemType,
contains and documentKey server-side; status is refined client-side.
Handles pagination internally, returns up to 20 core metadata records.
Incremental sync
On startup, APScheduler registers a job (every 2h by default) that reads the
projects table for initialized project_id + last_sync_time, then walks
Jama items whose modifiedDate > last_sync_time, re-cleans/re-chunks them and
updates the FTS5 + sqlite-vec indexes. New items are added; modified items have
their old chunks replaced atomically.
Setup
# 1. Install (uses Aliyun mirror; GitHub-only deps fall back to PyPI)
pip install -r requirements.txt
# 2. Configure (interactive wizard writes .env, then validates deps + config,
# and optionally probes Jama/embedding connectivity with --self-test)
python setup_wizard.py --self-test
# 3. Run (stdio transport for an MCP client)
python server.py
First-run configuration guard
Every MCP tool runs an offline pre-flight check before doing any work:
Python dependencies, required env vars (JAMA_URL / JAMA_CLIENT_ID /
JAMA_CLIENT_SECRET / EMBEDDING_BASE_URL / EMBEDDING_API_KEY) and the SQLite
store. If anything is missing the tool returns a clear error dict with a
hint instead of failing midway through a Jama API call. Configure via the
wizard, or call the configure_jama / validate_setup tools at runtime.
MCP client config (Claude Desktop example)
{
"mcpServers": {
"jama-mcp": {
"command": "python",
"args": ["/absolute/path/to/jama-mcp-server/server.py"],
"env": { "JAMA_MCP_DB_PATH": "/absolute/path/to/jama-mcp-server/jama_mcp.db" }
}
}
}
Usage flow (for the LLM)
init_jama_project("20571")→ returnsjob_idimmediately (non-blocking).get_sync_progress(job_id)→ poll untilstatus == "DONE".search_jama_semantics("20571", "how does volume sync work", top_k=5)→ RAG.query_jama_native_metadata("20314", document_key="SA-TC-7")→ exact match.
Resilience
- Jama API: OAuth token auto-refresh on expiry + 401 retry; urllib3
Retrywith exponential backoff on 429/5xx; explicitRetry-Afterhandling; SSL connection-reset tolerated (transient on this network). - Embeddings: same retry/backoff session on the embedding endpoint.
- SQLite concurrency: WAL mode + busy timeout + a process-level write lock
so the APScheduler writer and MCP reader threads coexist without
SQLITE_BUSYerrors; chunk replacement is atomic per item. - Reranker: lazy-loaded singleton; failure degrades to RRF-only scoring instead of crashing the search.
- Read-only:
JamaClientonly issues GET requests — it cannot create, modify or delete data on the Jama instance.
Files
| File | Purpose |
|---|---|
requirements.txt |
deps + Aliyun mirror config |
config.py |
env-driven settings (dataclasses) + validation/persistence/reload |
db_setup.py |
SQLite schema, FTS5 + sqlite-vec loading, CRUD |
jama_client.py |
OAuth, paginated fetch, HTML cleaning, native query, browse API |
rag_pipeline.py |
chunking, embeddings, Multi-Query, hybrid recall, RRF, rerank |
server.py |
MCP tools, async jobs, APScheduler incremental sync, pre-flight guards |
preflight.py |
offline dependency + config + storage validation |
setup_wizard.py |
interactive configuration wizard (python setup_wizard.py) |
selftest.py |
end-to-end self-test suite (python selftest.py) |
.env.example |
template for environment configuration |
Tools
Configuration & validation
validate_setup(live=False)— offline pre-flight (+ optional live Jama/embedding probe).configure_jama(values)— apply config at runtime, persist to.env, reload.
Jama browse (read-only, gated by pre-flight)
list_jama_projects()— all visible projects.find_jama_project_by_name(name, exact?)— find projects by name → get id + info.get_jama_item(item_id)— full single item (cleaned text).get_jama_item_children(item_id)— decomposition children.get_jama_item_relationships(item_id)/list_jama_project_relationships(project_id, item_id?)— relationships (cursor-paginated/relationships).get_jama_item_comments(item_id)— item comments (cleaned body).get_jama_item_attachments(item_id)— attachment metadata (no binary).list_jama_releases(project_id)— project releases/versions.list_jama_test_runs(project_id?, test_cycle_id?)— test runs.list_jama_item_types()— tenant item types (id → name).query_jama_endpoint(path, params?, all_pages?)— generic read-only GET escape hatch.
RAG / retrieval
init_jama_project(project_id)— async background init (returnsjob_id).get_sync_progress(job_id)— poll init/sync progress.search_jama_semantics(project_id, query, ...)— Multi-Query + hybrid + RRF + Qwen3 rerank.query_jama_native_metadata(project_id, ...)— exact-match metadata via/abstractitems.
Verified
All components self-tested against the live Jama instance and embedding API: OAuth + paginated fetch, HTML→text cleaning, Test Case step rendering, item-type mapping, DB schema (FTS5 + vec0), full RAG search, async init with progress polling, incremental sync (0 new items), native metadata filters (item_type / status / keyword / document_key), APScheduler startup, MCP stdio handshake, and error paths (bad project id, unknown job, nonexistent project).
The Qwen3-Reranker-0.6B was downloaded from the HuggingFace China mirror
(hf-mirror.com) and loaded on CPU; verified it produces non-zero relevance
scores with correct ordering (related=0.55 > unrelated=0.0002) and that the
end-to-end RAG search returns strategy=rerank results. LlamaIndex is the
primary RAG framework: SentenceSplitter + Document/TextNode for chunking.
Multi-Query expansion is performed by the MCP LLM client and passed to the
pipeline via search(sub_queries=...); when omitted, deterministic lexical
variants are used.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.