answer_question
Retrieve relevant documentation chunks as RAG context with citations to answer questions about Open Finance Brasil.
Instructions
Retrieve the most relevant chunks formatted as RAG context.
The MCP itself does NOT call an LLM — it returns the raw context plus citations so the calling assistant can answer using its own context window (saving tokens vs. running a second model here).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| question | Yes | ||
| max_chunks | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- src/of_mcp/server.py:123-145 (handler)Handler function for the answer_question MCP tool. Performs BM25 search via search.search_chunks, formats results with full chunk text via search.format_hits_for_rag, and prepends instructions for the calling LLM to synthesize the answer.
@mcp.tool() def answer_question(question: str, max_chunks: int = 6) -> str: """Retrieve the most relevant chunks formatted as RAG context. The MCP itself does NOT call an LLM — it returns the raw context plus citations so the calling assistant can answer using its own context window (saving tokens vs. running a second model here). """ max_chunks = max(1, min(int(max_chunks), 12)) with closing(_conn()) as conn: hits = search.search_chunks(conn, question, limit=max_chunks) if not hits: return ( f"No relevant docs for the question. Index status: {_index_status()}.\n" "If the index is empty, run the crawler first." ) body = search.format_hits_for_rag(hits) instructions = ( "Use the context below to answer the question. Cite sources with " "the [n] markers. If the answer is not in the context, say so.\n\n" f"Question: {question}\n\nContext:\n" ) return instructions + body - src/of_mcp/server.py:123-124 (registration)Tool registration via FastMCP @mcp.tool() decorator in src/of_mcp/server.py
@mcp.tool() def answer_question(question: str, max_chunks: int = 6) -> str: - src/of_mcp/search.py:40-76 (helper)BM25 search function called by answer_question. Executes FTS5 MATCH query against the SQLite chunks_fts virtual table and returns SearchHit objects.
def search_chunks(conn: sqlite3.Connection, query: str, limit: int = 8) -> list[SearchHit]: fts_query = sanitize_query(query) if not fts_query: return [] sql = """ SELECT c.id AS chunk_id, c.page_id AS page_id, p.title AS page_title, p.url AS page_url, c.heading_path AS heading_path, snippet(chunks_fts, 1, '<<', '>>', ' … ', 18) AS snippet, c.body_md AS body_md, c.token_estimate AS token_estimate, bm25(chunks_fts) AS rank FROM chunks_fts JOIN chunks c ON c.id = chunks_fts.rowid JOIN pages p ON p.id = c.page_id WHERE chunks_fts MATCH ? ORDER BY rank LIMIT ? """ rows = conn.execute(sql, (fts_query, limit)).fetchall() return [ SearchHit( chunk_id=r["chunk_id"], page_id=r["page_id"], page_title=r["page_title"], page_url=r["page_url"], heading_path=r["heading_path"], snippet=r["snippet"], body_md=r["body_md"], token_estimate=r["token_estimate"], rank=float(r["rank"]), ) for r in rows ] - src/of_mcp/search.py:93-104 (helper)RAG formatting helper that converts search hits into verbose context blocks with full chunk text, heading path, and source citation, used exclusively by answer_question.
def format_hits_for_rag(hits: list[SearchHit]) -> str: """Verbose formatting (full chunk text) for the answer_question tool.""" if not hits: return "No results." blocks = [] for i, h in enumerate(hits, 1): blocks.append( f"### [{i}] {h.heading_path}\n" f"Source: {h.page_title} — {h.page_url}\n\n" f"{h.body_md}" ) return "\n\n---\n\n".join(blocks) - src/of_mcp/server.py:123-124 (schema)Input schema: question (str, required) and max_chunks (int, default 6, clamped to 1-12). Output: str (formatted RAG context with instructions for the LLM).
@mcp.tool() def answer_question(question: str, max_chunks: int = 6) -> str: