policy-rag-mcp-server
Enables natural-language question answering over Australian government policy documents with grounded, source-cited responses and a tool to list indexed documents.
README
Policy Document Q&A Assistant — RAG + MCP Server (LangChain, Azure AI Foundry, PostgreSQL/pgvector, Docker, Langfuse)
A question-answering assistant over real Australian public policy documents (Queensland Government financial/ICT policy and ATO federal tax guidance), built as a Retrieval-Augmented Generation (RAG) pipeline and exposed as an MCP (Model Context Protocol) server — so it plugs into Claude Desktop or any other MCP-compatible client as a callable tool, not just a one-off script. The whole pipeline, from PDF ingestion through to the tool itself, runs containerized with Docker and is instrumented end-to-end with Langfuse tracing.
OBJECTIVE
Built to close a specific set of AI Engineer skill gaps identified across recent job applications (Queensland Treasury, Warren and Mahoney, McCosker, CI&T, Datacom) — agent tooling via MCP, LangChain-based orchestration, a real vector database (not an embedded/toy store), containerization, a named cloud-native AI platform (Azure AI Foundry specifically, not just "an OpenAI API key"), and LLMOps observability. Rather than collecting certificates, the goal was one real, working, end-to-end system that demonstrates all six hands-on — the same approach used for the sibling lead-scoring-databricks-pipeline project.
The application itself mirrors a realistic enterprise use case: "let staff ask natural-language questions against our own policy documents, with every answer traceable back to a real source page" — thematically relevant to government-adjacent roles specifically.
DATASET USED
8 real PDF documents (162 pages total), sourced directly from official .gov.au domains — no third-party mirrors:
FAH_Volume_1_Introduction_2025.pdf— Queensland Financial Accountability Handbook, Vol. 1Overview-of-Queensland-Financial-Accountability-Framework-as-at-Jan-2020.pdfict-as-a-service_decision_framework-overview_v1_0_0.pdfpaf-policy-overview.pdf/paf-supporting-guidelines.pdf— Queensland Project Assurance Framework (incl. public-private partnerships)n75057 [DE-81739] - 2026 Tax Time toolkit for small business_DIGITAL.pdf— ATOn75127 [DE-73758] - Residency for tax purposes - factsheet_DIGITAL.pdf— ATOtr2023-001.pdf— ATO Taxation Ruling 2023/1
The corpus is deliberately split across two clusters — Queensland state policy and Australian federal tax guidance — so retrieval has to genuinely discriminate between state vs. federal sources, and between a formal ruling and a plain-language summary of a related topic, rather than trivially matching on a single obvious keyword. An initial set of 11 documents (344 pages) was trimmed down to these 8 (162 pages) to keep the corpus large enough to be a real retrieval problem while staying fast to iterate on.
ARCHITECTURE & PIPELINE
flowchart LR
subgraph Ingestion["Ingestion (run once, re-run on doc changes)"]
A[Policy PDFs] --> B[Chunking\nLangChain RecursiveCharacterTextSplitter]
B --> C[Embed\nAzure text-embedding-3-small]
C --> D[(PostgreSQL + pgvector\nvia Docker)]
end
subgraph Query["Query time"]
E[MCP Client\ne.g. Claude Desktop] --> F[MCP Server\nPython, containerized]
F --> G[LangChain RAG chain]
G --> D
G --> H[Azure AI Foundry\ngpt-5-mini]
H --> G --> F --> E
end
G -. traces .-> I[Langfuse\nobservability]
Ingestion: Each PDF is loaded page-by-page with LangChain's PyPDFLoader (preserving source filename + page number as metadata for later citations), then split with RecursiveCharacterTextSplitter at 1000 characters / 150 overlap — chosen over a smaller 500/50 split after comparing both directly on the real corpus (950 vs. 521 chunks); the larger chunk size suits these procedural, structurally dense policy documents better, giving the model more surrounding context per retrieved piece. Each of the 521 resulting chunks is embedded via Azure's text-embedding-3-small and written into a PostgreSQL database running in Docker, with the pgvector extension enabling vector similarity search directly inside the relational database rather than a separate dedicated vector store — a more production-credible pattern, since many real organizations add vector search to a Postgres instance they already run.
Query time: A question is embedded with the same model, PostgreSQL returns the top-4 most similar chunks by vector similarity, and those chunks are "stuffed" into a prompt that instructs gpt-5-mini to answer only from that retrieved context — or say "I don't know" rather than fall back on its own general knowledge. The answer is returned together with the exact source document and page number for every chunk it drew from, so every answer is independently verifiable.
Interface: The RAG chain is wrapped as an MCP server using the official Python MCP SDK, exposing two tools — query_policy_docs (the core Q&A capability) and list_indexed_documents (lets a client show what's actually covered before asking). The RAG logic and the MCP protocol layer are kept in separate files on purpose, so the underlying capability could be reused behind a different interface later without touching it. The server is containerized with Docker; because MCP's stdio transport requires the server to be attached directly to its client's stdin/stdout, it runs as a short-lived container spawned on demand per connection (docker compose run --rm -i), unlike the always-on PostgreSQL container.
Observability: Every query is traced end-to-end with Langfuse — retrieval and generation nested under one connected trace per question, showing the exact prompt, retrieved chunks, latency, and token cost.
MODEL USED
Both models are deployed via Azure AI Foundry (chosen deliberately over the classic "Azure OpenAI" resource type, since Foundry is named explicitly in several target job postings):
gpt-5-mini— generation,temperature=0for faithful, literal answers grounded in retrieved text rather than creative ones.text-embedding-3-small— embeddings, for both the document corpus and incoming questions.
This Foundry resource uses Azure's newer unified v1 API surface (.../openai/v1), so the integration uses LangChain's plain ChatOpenAI / OpenAIEmbeddings classes with base_url + api_key + model, rather than the Azure-specific AzureChatOpenAI / AzureOpenAIEmbeddings classes, which target the older dated-api-version endpoint scheme and 404 against this resource.
RESULTS & INSIGHTS
Evaluated manually with 5 real test questions run through the live system:
- "What is the Financial Accountability Handbook designed to help agencies do?" → correct, near-verbatim grounded answer with accurate citations.
- "What does the ICT-as-a-service decision framework help agencies decide?" → correct, captured the real nuance (deployment/service models, risk assessment).
- "What is the capital of France?" (deliberately out-of-scope) → correctly answered "I don't know" instead of using the model's own general knowledge — the key anti-hallucination test.
- "What is a public private partnership under Queensland's PPP policy?" → answered "I don't know." Verified independently by directly inspecting the source PDF's extracted text: the document genuinely never defines the term in plain language anywhere in its 23 pages — a real, defensible corpus gap correctly identified rather than a retrieval failure or a fabricated answer.
- "What record-keeping obligations does the small business Tax Time toolkit mention?" → correct, specific detail, accurate citation.
5/5 sensible behavior — 3 correct grounded answers and 2 correct refusals (one genuinely out-of-scope, one a real corpus gap correctly recognized rather than hallucinated). Distinguishing "retrieval missed the right chunk" from "the corpus genuinely doesn't contain the answer" is a real RAG evaluation skill, and this round demonstrated both failure modes being handled correctly — not just an eyeballed pass/fail.
Verified against a real MCP client (Claude Desktop) end-to-end, both running locally via .venv and fully containerized via Docker, with every query traced in Langfuse.
SETUP & USAGE
Requirements: Docker Desktop, Python 3.13, an Azure AI Foundry resource with gpt-5-mini and text-embedding-3-small deployed, and (optionally) a free Langfuse Cloud account for tracing.
- Copy
.env.exampleto.envand fill in your own Azure, Postgres, and (optionally) Langfuse credentials. - Start the vector store:
docker compose up -d postgres - Ingest the documents (one-off, or whenever the document set changes):
pip install -r requirements-ingest.txt python embed_and_store.py - Run the assistant directly from the CLI:
python rag_chain.py - Or connect it as an MCP tool: build the server image (
docker compose build mcp-server), then add it to your MCP client's config (e.g. Claude Desktop'sclaude_desktop_config.json):"policy-rag": { "command": "docker", "args": ["compose", "-f", "<path-to-repo>/docker-compose.yml", "run", "--rm", "-i", "mcp-server"] }
CONCLUSIONS
The pipeline is fully working end to end: real government PDFs are chunked, embedded, and stored in a real vector database; retrieval and generation are grounded and correctly refuse to answer beyond the corpus; the whole capability is exposed as a standards-compliant MCP tool, verified live against a real MCP client; the server is containerized and portable; and every query is traced with cost, latency, and full context for observability. Every layer named as a target skill gap — MCP, LangChain, a real vector database, Docker, Azure AI Foundry, and LLMOps tracing — is demonstrated with working, tested code rather than a tutorial-only implementation.
AUTHOR INFORMATION
Varun Vikas Jaiswal 2026
KEYWORDS
RAG, Retrieval-Augmented Generation, LangChain, MCP, Model Context Protocol, Azure AI Foundry, Azure OpenAI, pgvector, PostgreSQL, vector database, vector similarity search, Docker, Docker Compose, Langfuse, LLMOps, observability, Python, AI Engineer
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。