MCP-RAGAnything
Multi-modal RAG service exposing a REST API and MCP server for document indexing and knowledge-base querying, powered by RAGAnything and LightRAG. Two retrieval pathways are available: a graph-based LightRAG pipeline and a classical RAG pipeline using multi-query generation with LLM-as-judge scoring. Files are retrieved from MinIO object storage and indexed into a PostgreSQL-backed knowledge graph. Each project is isolated via its own working_dir.
The service also hosts the MCP server registry — a CRUD API for registering external MCP servers and generating new MCP servers on the fly from OpenAPI/Swagger documents. The registry is persisted in PostgreSQL and rehydrated at startup, so composable-agents and other clients can discover all available MCP servers from a single endpoint. See MCP Server Registry.
Architecture
Clients
(REST / MCP / Claude)
|
+-------------+-------------+
| FastAPI App |
+-------------+-------------+
|
+---------------+---------------+
| |
Application Layer MCP Servers (FastMCP)
+------------------------------+ |
| api/ | +---+--------+ +--+-----------+ +--+-------------+ +--+----------+
| indexing_routes.py | | RAGAnything | | RAGAnything | | RAGAnything | | RAGAnything |
| query_routes.py | | Query | | Files | | Classical | | Bricks |
| file_routes.py | | /rag/mcp | | /files/mcp | | /classical/mcp| | /bricks/mcp|
| health_routes.py | +---+--------+ +--+-----------+ +--+-------------+ +--+----------+
| classical_indexing_routes | | | | |
| classical_query_routes | | | classical_index_file list_bricks_documents
| use_cases/ | | | classical_index_folder read_bricks_document
| IndexFileUseCase | | | classical_query publish_section_version
| IndexFolderUseCase |
| QueryUseCase |
| ClassicalIndexFileUseCase |
| ClassicalIndexFolderUseCase |
| ClassicalQueryUseCase |
| ListFilesUseCase |
| ListFoldersUseCase |
| ReadFileUseCase |
| UploadFileUseCase |
| CreateFolderUseCase |
| DeleteFileUseCase |
| DeleteFolderUseCase |
| ListBricksDocumentsUseCase |
| ReadBricksDocumentUseCase |
| PublishSectionVersionUseCase|
| requests/ responses/ |
+------------------------------+
| | |
v v v
Domain Layer (ports)
+----------------------------------------------------------+
| RAGEnginePort StoragePort BM25EnginePort |
| DocumentReaderPort VectorStorePort LLMPort |
| BricksApiPort |
+----------------------------------------------------------+
| | | | |
v v v v v
Infrastructure Layer (adapters)
+----------------------------------------------------------+
| LightRAGAdapter MinioAdapter |
| (RAGAnything/ (minio-py) |
| KreuzbergParser) |
| |
| PostgresBM25Adapter RRFCombiner |
| (pg_textsearch) (hybrid+ fusion) |
| |
| KreuzbergAdapter LangchainPgvectorAdapter |
| (kreuzberg - 91 formats) (langchain-postgres PGVector) |
| |
| LangchainOpenAIAdapter BricksApiAdapter |
| (langchain-openai ChatOpenAI) (httpx, Bricks REST API) |
+----------------------------------------------------------+
| | | | |
v v v v v
PostgreSQL MinIO Kreuzberg OpenAI-compatible Bricks API
(pgvector + (object (document (LLM API) (analyse.bricks.co
Apache AGE storage) extraction) + section-versions)
pg_textsearch)
Prerequisites
- Python 3.13+
- Docker and Docker Compose
- An OpenRouter API key (or any OpenAI-compatible provider)
- The
soludev-compose-apps/bricks/stack for production deployment (provides PostgreSQL, MinIO, and this service)
Quick Start
Production runs from the shared compose stack at soludev-compose-apps/bricks/. The docker-compose.yml in this repository is for local development only.
Local development
# 1. Install dependencies
uv sync
# 2. Start PostgreSQL and MinIO (docker-compose.yml provides Postgres;
# MinIO must be available separately or added to the compose file)
docker compose up -d postgres
# 3. Configure environment
cp .env.example .env
# Edit .env: set OPEN_ROUTER_API_KEY and adjust MINIO_HOST / POSTGRES_HOST
# 4. Run the server
uv run python src/main.py
The API is available at http://localhost:8000. Swagger UI at http://localhost:8000/docs.
Production (soludev-compose-apps)
cd soludev-compose-apps/bricks/
docker compose up -d
This starts all brick services including raganything-api, postgres, and minio.
API Key Authentication
The service supports optional API key authentication for both REST and MCP endpoints. A single API_KEY environment variable controls all layers.
Enabling authentication
Set API_KEY to a non-empty value in .env:
API_KEY=your-secret-key-here
When enabled, all REST endpoints (except health) and all MCP tool calls require the X-API-Key header. Requests without a valid key receive 401 Unauthorized (REST) or a ToolError (MCP).
Disabling authentication
Leave API_KEY empty (default) to disable authentication — useful for local development and testing:
API_KEY=
REST usage
Include the X-API-Key header in every request:
curl -H "X-API-Key: your-secret-key-here" \
-H "Content-Type: application/json" \
-X POST http://localhost:8000/api/v1/classical/query \
-d '{"working_dir": "project-alpha", "query": "test"}'
Health endpoints (/api/v1/health, /api/v1/health/live) remain public regardless of the API_KEY setting.
MCP usage
MCP clients must send the X-API-Key header in their HTTP transport configuration. For composable-agents, add it to the headers field of each MCP server config:
mcp_servers:
- name: bricks
transport: http
url: https://raganything.soludev.tech/bricks/mcp
headers:
X-API-Key: "${MCP_RAGANYTHING_API_KEY}"
- name: files
transport: http
url: https://raganything.soludev.tech/files/mcp
headers:
X-API-Key: "${MCP_RAGANYTHING_API_KEY}"
- name: classical
transport: http
url: https://raganything.soludev.tech/classical/mcp
headers:
X-API-Key: "${MCP_RAGANYTHING_API_KEY}"
The ${MCP_RAGANYTHING_API_KEY} placeholder is resolved from the environment variable of the same name. Set it to the same value as the server's API_KEY.
Protected endpoints
| Layer | Protected | Public |
|---|---|---|
| REST | /api/v1/files/*, /api/v1/files, /api/v1/classical/*, /api/v1/file/*, /api/v1/folder/*, /api/v1/query |
/api/v1/health, /api/v1/health/live |
| MCP | tools/call, tools/list (all 3 servers) |
initialize |
API Reference
Base path: /api/v1
Health
# Health check
curl http://localhost:8000/api/v1/health
Response:
{"message": "RAG Anything API is running"}
Indexing
Both indexing endpoints accept JSON bodies and run processing in the background. Files are downloaded from MinIO, not uploaded directly.
Index a single file
Downloads the file identified by file_name from the configured MinIO bucket, then indexes it into the RAG knowledge graph scoped to working_dir.
curl -X POST http://localhost:8000/api/v1/file/index \
-H "Content-Type: application/json" \
-d '{
"file_name": "project-alpha/report.pdf",
"working_dir": "project-alpha"
}'
Response (202 Accepted):
{"status": "accepted", "message": "File indexing started in background"}
| Field | Type | Required | Description |
|---|---|---|---|
file_name |
string | yes | Object path in the MinIO bucket |
working_dir |
string | yes | RAG workspace directory (project isolation) |
Index a folder
Lists all objects under the working_dir prefix in MinIO, downloads them, then indexes the entire folder.
curl -X POST http://localhost:8000/api/v1/folder/index \
-H "Content-Type: application/json" \
-d '{
"working_dir": "project-alpha",
"recursive": true,
"file_extensions": [".pdf", ".docx", ".txt"]
}'
Response (202 Accepted):
{"status": "accepted", "message": "Folder indexing started in background"}
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
working_dir |
string | yes | -- | RAG workspace directory, also used as the MinIO prefix |
recursive |
boolean | no | true |
Process subdirectories recursively |
file_extensions |
list[string] | no | null (all files) |
Filter by extensions, e.g. [".pdf", ".docx", ".txt"] |
Supported Document Formats
The service automatically detects and processes the following document formats through the RAGAnything parser:
| Format | Extensions | Notes |
|---|---|---|
.pdf |
Includes OCR support (English + French via Tesseract) | |
| Microsoft Word | .docx |
|
| Microsoft PowerPoint | .pptx |
|
| Microsoft Excel | .xlsx |
|
| HTML | .html, .htm |
|
| Plain Text | .txt, .text, .md |
UTF-8, UTF-16, ASCII supported; converted to PDF via ReportLab |
| Quarto Markdown | .qmd |
Quarto documents |
| R Markdown | .Rmd, .rmd |
R Markdown files |
| Images | .png, .jpg, .jpeg, .gif, .webp, .bmp, .tiff, .tif |
Vision model processing (if enabled) |
Note: File format detection is automatic. No configuration is required to specify the document type. The service will process any supported format when indexed. All document and image formats are supported out-of-the-box when installed with raganything[all].
File Browsing & Reading
Browse and read files directly from MinIO without indexing them into the RAG knowledge base. Powered by Kreuzberg for document text extraction (91 file formats).
List files
# List all files in the bucket
curl http://localhost:8000/api/v1/files/list
# List files under a specific prefix
curl "http://localhost:8000/api/v1/files/list?prefix=documents/&recursive=true"
Response (200 OK):
[
{"object_name": "documents/report.pdf", "size": 1024, "last_modified": "2026-01-01 00:00:00+00:00"},
{"object_name": "documents/notes.txt", "size": 512, "last_modified": "2026-01-02 00:00:00+00:00"}
]
| Parameter | Type | Default | Description |
|---|---|---|---|
prefix |
string | "" |
MinIO prefix to filter files by |
recursive |
boolean | true |
List files in subdirectories |
Upload a file
Uploads a file directly to the MinIO bucket. The file is stored at {prefix}{filename}. This endpoint does not index the file — use the POST /file/index endpoint after uploading to add it to the RAG knowledge base.
Allowed file types: .pdf, .txt, .docx, .xlsx, .pptx, .md, .csv, .png, .jpg, .jpeg, .gif, .webp, .svg, .bmp, .html, .xml, .json, .rtf, .odt, .ods
Maximum file size: 50 MB
curl -X POST http://localhost:8000/api/v1/files/upload \
-F "[email protected]" \
-F "prefix=documents/"
Response (201 Created):
{"object_name": "documents/report.pdf", "size": 2048, "message": "File uploaded successfully"}
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
file |
file | yes | -- | The file to upload (multipart form) |
prefix |
string | no | "" |
MinIO prefix (folder path). Must be a relative path |
Error responses:
| Status | Condition |
|---|---|
413 |
File exceeds 50 MB limit |
422 |
Invalid prefix (path traversal/absolute), disallowed file type, or missing file |
Read a file
Downloads the file from MinIO, extracts its text content using Kreuzberg, and returns the result. Supports 91 file formats including PDF, Office documents, images, and HTML.
curl -X POST http://localhost:8000/api/v1/files/read \
-H "Content-Type: application/json" \
-d '{"file_path": "documents/report.pdf"}'
Response (200 OK):
{
"content": "Extracted text from the document...",
"metadata": {"format_type": "pdf", "mime_type": "application/pdf"},
"tables": [{"markdown": "| Header | Value |\n|---|---|\n| A | 1 |"}]
}
| Field | Type | Description |
|---|---|---|
file_path |
string | Required. File path in the MinIO bucket (relative, no .. or absolute paths) |
Error responses:
| Status | Condition |
|---|---|
404 |
File not found in MinIO |
422 |
Unsupported file format or invalid path (path traversal, absolute path) |
Create a folder
Creates a folder marker in the MinIO bucket by writing a 0-byte object whose object name ends with a trailing /. The folder is purely a prefix — MinIO does not have a real folder concept. This endpoint does not index anything.
curl -X POST http://localhost:8000/api/v1/files/folders \
-H "Content-Type: application/json" \
-d '{"prefix": "documents/reports/"}'
Response (201 Created):
{"message": "Folder created", "prefix": "documents/reports/"}
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
prefix |
string | yes | -- | Folder prefix to create. Must be a relative path; a trailing / is preserved |
Error responses:
| Status | Condition |
|---|---|
422 |
Missing, absolute, or path-traversing prefix |
Delete a file (cascade)
Deletes a single object from the MinIO bucket and removes its corresponding vectors from the pgvector store. The object_name and working_dir are both required query parameters.
Deletion is performed in two steps, strictly in order:
- MinIO first — the object is removed from the storage bucket.
- pgvector second — vectors associated with the file (scoped to
working_dir) are deleted from the vector store.
If the MinIO deletion fails, the pgvector store is not touched, ensuring no orphaned vectors are created from a failed storage operation.
curl -X DELETE "http://localhost:8000/api/v1/files?object_name=documents/report.pdf&working_dir=project-alpha"
Response (200 OK):
{"message": "File deleted", "object_name": "documents/report.pdf"}
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
object_name |
string | yes | -- | Object path to delete. Must be a relative path within the bucket |
working_dir |
string | yes | -- | RAG workspace directory (project isolation). Used to scope the pgvector deletion |
Error responses:
| Status | Condition |
|---|---|
422 |
Missing, absolute, or path-traversing object_name or working_dir |
Delete a folder (recursive, cascade)
Deletes all objects whose object names start with prefix from the MinIO bucket and removes all corresponding vectors from the pgvector store. The deletion is recursive and permanent — there is no confirmation beyond the API call. A trailing / is preserved so that a prefix like documents/ does not match documents-archive/.
The prefix is automatically used as the working_dir for the pgvector deletion — no separate working_dir parameter is needed on this endpoint.
Deletion is performed in two steps, strictly in order:
- MinIO first — all objects under the prefix are removed from the storage bucket.
- pgvector second — all vectors matching the prefix are deleted from the vector store.
If the MinIO deletion fails, the pgvector store is not touched.
curl -X DELETE "http://localhost:8000/api/v1/files/folders?prefix=documents/reports/"
Response (200 OK):
{"message": "Folder deleted", "prefix": "documents/reports/"}
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
prefix |
string | yes | -- | Folder prefix to delete. Must be a relative path; trailing / is preserved. Also used as the working_dir for pgvector cleanup |
Error responses:
| Status | Condition |
|---|---|
422 |
Missing, absolute, or path-traversing prefix |
Path traversal protection
All delete routes (DELETE /files, DELETE /files/folders) and the create-folder route (POST /files/folders) validate the supplied path before touching storage. The validation rules are shared with the existing read/upload/list endpoints:
- The value must not be empty.
- It must be a relative path (no leading
/, no drive prefixes). - After normalization it must not resolve to
.,.., start with../, or contain a/../segment.
Any violation returns 422 Unprocessable Entity with a FileValidationError body. The object_name / prefix is normalized (backslashes converted to /, trailing slashes preserved) before being forwarded to the StoragePort.
Query
Query the indexed knowledge base. The RAG engine is initialized for the given working_dir before executing the query.
curl -X POST http://localhost:8000/api/v1/query \
-H "Content-Type: application/json" \
-d '{
"working_dir": "project-alpha",
"query": "What are the main findings of the report?",
"mode": "naive",
"top_k": 10
}'
Response (200 OK):
{
"status": "success",
"message": "",
"data": {
"entities": [],
"relationships": [],
"chunks": [
{
"reference_id": "...",
"content": "...",
"file_path": "...",
"chunk_id": "..."
}
],
"references": []
},
"metadata": {
"query_mode": "naive",
"keywords": null,
"processing_info": null
}
}
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
working_dir |
string | yes | -- | RAG workspace directory for this project |
query |
string | yes | -- | The search query |
mode |
string | no | "naive" |
Search mode: naive, local, global, hybrid, hybrid+, mix, bm25, bypass |
BM25 query mode
Returns results ranked by PostgreSQL full-text search using pg_textsearch. Each chunk includes a score field with the BM25 relevance score.
curl -X POST http://localhost:8000/api/v1/query \
-H "Content-Type: application/json" \
-d '{
"working_dir": "project-alpha",
"query": "quarterly revenue growth",
"mode": "bm25",
"top_k": 10
}'
Response (200 OK):
{
"status": "success",
"message": "",
"data": {
"entities": [],
"relationships": [],
"chunks": [
{
"chunk_id": "abc123",
"content": "Quarterly revenue grew 12% year-over-year...",
"file_path": "reports/financials-q4.pdf",
"score": 3.456,
"metadata": {}
}
],
"references": []
},
"metadata": {
"query_mode": "bm25",
"total_results": 10
}
}
Hybrid+ query mode
Runs BM25 and vector search in parallel, then merges results using Reciprocal Rank Fusion (RRF). Each chunk includes bm25_rank, vector_rank, and combined_score fields.
curl -X POST http://localhost:8000/api/v1/query \
-H "Content-Type: application/json" \
-d '{
"working_dir": "project-alpha",
"query": "quarterly revenue growth",
"mode": "hybrid+",
"top_k": 10
}'
Response (200 OK):
{
"status": "success",
"message": "",
"data": {
"entities": [],
"relationships": [],
"chunks": [
{
"chunk_id": "abc123",
"content": "Quarterly revenue grew 12% year-over-year...",
"file_path": "reports/financials-q4.pdf",
"score": 0.0328,
"bm25_rank": 1,
"vector_rank": 3,
"combined_score": 0.0328,
"metadata": {}
}
],
"references": []
},
"metadata": {
"query_mode": "hybrid+",
"total_results": 10,
"rrf_k": 60
}
}
The combined_score is the sum of bm25_score and vector_score, each computed as 1 / (k + rank). Results are sorted by combined_score descending. A chunk that appears in both result sets will have a higher combined score than one that appears in only one.
Classical RAG Pipeline
A second retrieval pathway alongside the graph-based LightRAG. Classical RAG uses a straightforward chunk → embed → retrieve flow with two quality-enhancing techniques: multi-query generation and LLM-as-judge relevance scoring. It stores chunks in dedicated PGVector tables (one per working_dir) and does not build a knowledge graph.
How it works
- Indexing — A file is downloaded from MinIO, text is extracted via Kreuzberg (with chunking), and each chunk is embedded and stored in a PGVector table.
- Querying — The LLM generates N alternative phrasings of the user query (multi-query), similarity search runs for each variation, results are deduplicated by
chunk_id, then an LLM judge scores each chunk's relevance on a 0–10 scale. Chunks below the relevance threshold are discarded; the rest are returned sorted by score.
Classical Indexing
Both classical indexing endpoints accept JSON bodies and run processing in the background.
Index a single file (classical)
Downloads the file from MinIO, extracts text with Kreuzberg chunking, and embeds the chunks into a PGVector table scoped to working_dir.
curl -X POST http://localhost:8000/api/v1/classical/file/index \
-H "Content-Type: application/json" \
-d '{
"file_name": "project-alpha/report.pdf",
"working_dir": "project-alpha",
"chunk_size": 1000,
"chunk_overlap": 200
}'
Response (202 Accepted):
{"status": "accepted", "message": "File indexing started in background"}
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
file_name |
string | yes | -- | Object path in the MinIO bucket |
working_dir |
string | yes | -- | RAG workspace directory (project isolation) |
chunk_size |
integer | no | 1000 |
Max characters per chunk (100–10000) |
chunk_overlap |
integer | no | 200 |
Overlap characters between chunks (0–2000) |
Index a folder (classical)
Lists all objects under the working_dir prefix in MinIO, downloads them, and indexes each file into the PGVector table.
curl -X POST http://localhost:8000/api/v1/classical/folder/index \
-H "Content-Type: application/json" \
-d '{
"working_dir": "project-alpha",
"recursive": true,
"file_extensions": [".pdf", ".docx", ".txt"],
"chunk_size": 1000,
"chunk_overlap": 200
}'
Response (202 Accepted):
{"status": "accepted", "message": "Folder indexing started in background"}
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
working_dir |
string | yes | -- | RAG workspace directory, also used as the MinIO prefix |
recursive |
boolean | no | true |
Process subdirectories recursively |
file_extensions |
list[string] | no | null (all files) |
Filter by extensions, e.g. [".pdf", ".docx", ".txt"] |
chunk_size |
integer | no | 1000 |
Max characters per chunk (100–10000) |
chunk_overlap |
integer | no | 200 |
Overlap characters between chunks (0–2000) |
Classical Query
Query the classical RAG pipeline. Supports two modes: vector (default) and hybrid (BM25 + vector via Reciprocal Rank Fusion).
Vector mode (default)
The LLM generates query variations, runs vector similarity search for each, deduplicates results, then scores and filters them with an LLM judge.
curl -X POST http://localhost:8000/api/v1/classical/query \
-H "Content-Type: application/json" \
-d '{
"working_dir": "project-alpha",
"query": "What are the main findings of the report?",
"top_k": 10,
"num_variations": 3,
"relevance_threshold": 5.0,
"mode": "vector"
}'
Hybrid mode
Runs BM25 full-text search and multi-query vector search in parallel, merges results using Reciprocal Rank Fusion (RRF), then scores with an LLM judge. Chunks include bm25_score, vector_score, and combined_score fields.
curl -X POST http://localhost:8000/api/v1/classical/query \
-H "Content-Type: application/json" \
-d '{
"working_dir": "project-alpha",
"query": "What are the main findings of the report?",
"mode": "hybrid"
}'
Response (200 OK):
{
"status": "success",
"message": "",
"queries": [
"What are the main findings of the report?",
"What key results does the report present?",
"Summarize the primary conclusions from the report"
],
"chunks": [
{
"chunk_id": "a1b2c3d4-...",
"content": "The primary finding indicates that...",
"file_path": "project-alpha/report.pdf",
"relevance_score": 8.5,
"metadata": {"chunk_index": 0},
"bm25_score": 0.0164,
"vector_score": 0.0164,
"combined_score": 0.0328
}
],
"mode": "hybrid"
}
If BM25 is unavailable (BM25_ENABLED=false or pg_textsearch extension missing), hybrid mode falls back to vector mode and logs a warning.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
working_dir |
string | yes | -- | RAG workspace directory for this project |
query |
string | yes | -- | The search query |
top_k |
integer | no | 10 |
Maximum chunks to retrieve per query variation (1–100) |
num_variations |
integer | no | 3 |
Number of LLM-generated query variations (1–10) |
relevance_threshold |
float | no | 5.0 |
Minimum LLM judge score (0–10) to include a chunk |
mode |
string | no | "vector" |
Query mode: vector (vector-only) or hybrid (BM25+vector RRF) |
LightRAG vs Classical RAG
| Aspect | LightRAG (graph-based) | Classical RAG |
|---|---|---|
| Storage | Apache AGE knowledge graph + pgvector | PGVector tables only |
| Indexing | Builds entity/relationship graph | Chunk + embed only |
| Query modes | naive, local, global, hybrid, hybrid+, mix, bm25, bypass |
vector (multi-query + LLM judge), hybrid (BM25+vector RRF) |
| Project isolation | Shared graph per working_dir |
Separate PG table per working_dir |
| Best for | Complex reasoning, relationship traversal | Straightforward document Q&A, simpler setup |
MCP Servers
The service exposes four MCP servers, all using streamable HTTP transport:
RAGAnythingQuery — /rag/mcp
Query-focused tools for searching the indexed knowledge base.
Tool: query_knowledge_base
| Parameter | Type | Default | Description |
|---|---|---|---|
working_dir |
string | required | RAG workspace directory for this project |
query |
string | required | The search query |
mode |
string | "hybrid" |
Search mode: naive, local, global, hybrid, hybrid+, mix, bm25, bypass |
top_k |
integer | 5 |
Number of chunks to retrieve |
Tool: query_knowledge_base_multimodal
| Parameter | Type | Default | Description |
|---|---|---|---|
working_dir |
string | required | RAG workspace directory for this project |
query |
string | required | The search query |
multimodal_content |
list | required | List of multimodal content items |
mode |
string | "hybrid" |
Search mode |
top_k |
integer | 5 |
Number of chunks to retrieve |
RAGAnythingFiles — /files/mcp
File browsing tools for listing and reading files from MinIO storage.
Tool: list_files
| Parameter | Type | Default | Description |
|---|---|---|---|
prefix |
string | "" |
MinIO prefix to filter files by |
recursive |
boolean | true |
List files in subdirectories |
Tool: read_file
| Parameter | Type | Default | Description |
|---|---|---|---|
file_path |
string | required | File path in MinIO bucket (e.g. documents/report.pdf) |
Downloads the file from MinIO, extracts its text content using Kreuzberg, and returns the extracted text along with metadata and any detected tables.
RAGAnythingBricks — /bricks/mcp
Bricks integration tools for accessing project documents from the Bricks platform and publishing structured section versions.
Tool: list_bricks_documents
| Parameter | Type | Default | Description |
|---|---|---|---|
project_unique_id |
string | required | Bricks project unique identifier |
Returns a list of documents for the specified Bricks project, including metadata like file name, MIME type, size, status, and presigned download URLs.
Tool: read_bricks_document
| Parameter | Type | Default | Description |
|---|---|---|---|
file_url |
string | required | Presigned S3 URL from list_bricks_documents |
Downloads the document from the presigned S3 URL, extracts its text content using Kreuzberg, and returns the extracted text, metadata, and detected tables. No authentication is required — the URL is already signed.
Tool: publish_section_version
| Parameter | Type | Default | Description |
|---|---|---|---|
project_unique_id |
string | required | Bricks project unique identifier |
section_key |
string | required | Section key to publish (e.g. "summary", "analysis") |
content |
dict | required | Structured content for the section |
workflow_id |
string | "agent-haiku-files-v1" |
Workflow identifier |
workflow_name |
string | "haiku-files" |
Workflow display name |
workflow_metadata |
dict | null |
Additional workflow metadata |
Publishes a structured section version back to the Bricks platform. When BRICKS_PUBLISH_DRY_RUN=true (default), the tool returns a preview of the payload without making an API call. Set BRICKS_PUBLISH_DRY_RUN=false to enable real publishing.
Dry-run response example:
{
"success": true,
"message": "DRY RUN — no API call made",
"dry_run": true,
"payload_preview": {
"project_unique_id": "abc-123",
"section_key": "summary",
"content": {"title": "Analysis Summary"},
"workflow_id": "agent-haiku-files-v1",
"workflow_name": "haiku-files",
"workflow_metadata": {}
}
}
RAGAnythingClassical — /classical/mcp
Classical RAG tools for indexing and querying without a knowledge graph.
Tool: classical_index_file
| Parameter | Type | Default | Description |
|---|---|---|---|
file_name |
string | required | Object path in the MinIO bucket |
working_dir |
string | required | RAG workspace directory (project isolation) |
chunk_size |
integer | 1000 |
Max characters per chunk (100–10000) |
chunk_overlap |
integer | 200 |
Overlap characters between chunks (0–2000) |
Tool: classical_index_folder
| Parameter | Type | Default | Description |
|---|---|---|---|
working_dir |
string | required | RAG workspace directory, also used as MinIO prefix |
recursive |
boolean | true |
Process subdirectories recursively |
file_extensions |
list[string] | null (all files) |
Filter by extensions, e.g. [".pdf", ".docx"] |
chunk_size |
integer | 1000 |
Max characters per chunk (100–10000) |
chunk_overlap |
integer | 200 |
Overlap characters between chunks (0–2000) |
Tool: classical_query
| Parameter | Type | Default | Description |
|---|---|---|---|
working_dir |
string | required | RAG workspace directory for this project |
query |
string | required | The search query |
top_k |
integer | 10 |
Maximum chunks to retrieve per query variation |
num_variations |
integer | 3 |
Number of LLM-generated query variations (1–10) |
relevance_threshold |
float | 5.0 |
Minimum LLM judge score (0–10) to include a chunk |
mode |
string | "vector" |
Query mode: vector (vector-only) or hybrid (BM25+vector RRF) |
Transport
All MCP servers use streamable HTTP transport exclusively. Connect MCP clients to the mount paths:
http://localhost:8000/rag/mcp # RAGAnythingQuery
http://localhost:8000/files/mcp # RAGAnythingFiles
http://localhost:8000/classical/mcp # RAGAnythingClassical
http://localhost:8000/bricks/mcp # RAGAnythingBricks
MCP Server Registry
In addition to the four built-in MCP servers above, mcp-raganything owns the MCP server registry — a CRUD service that lets you register external MCP servers (by URL) or generate new MCP servers on the fly from any OpenAPI/Swagger document. Registered servers are persisted in the mcp_servers PostgreSQL table (shared with composable-agents) and rehydrated at startup, so composable-agents (and other clients) only need to know the registry URL to discover and connect to every available MCP server.
This registry was previously hosted in the composable-agents brick; it has been migrated here so that the RAG service is the single owner of MCP server lifecycle (registration, generation, mounting, crash recovery) and the mcp_servers table schema.
What it does
- Register external MCP servers — store a name, transport URL, optional headers and an encrypted auth token. On create/update, the service connects to the remote server via
fastmcp.Client, validates reachability, and records the discoveredtool_count. Composable-agents reads these entries and connects to the URL at agent build time. - Generate MCP servers from OpenAPI specs — point the registry at any OpenAPI 3.0 (or Swagger 2.0) document URL; the service fetches the spec, builds an in-process FastMCP server exposing one tool per operation, and mounts it under
/generated/{name}/mcp. - Encrypt secrets at rest — auth tokens and sensitive header values are encrypted with Fernet using the shared
SECRET_ENCRYPTION_KEYbefore being written tomcp_servers. They are only decrypted on the/revealendpoint or when the server is mounted. - Crash recovery — on startup the service reads every
openapirow frommcp_servers, re-fetches the spec, rebuilds the FastMCP server, and remounts it. Externalhttpservers do not need rehydration (the client connects to them lazily), so onlyopenapirows are rebuilt.
Endpoints
All registry endpoints live under /api/v1/mcp/servers and are protected by the global API_KEY middleware when API_KEY is set.
| Method | Path | Description | Success Status |
|---|---|---|---|
POST |
/api/v1/mcp/servers |
Create a registered MCP server (external or openapi). Returns the masked entry (secrets hidden). | 201 |
POST |
/api/v1/mcp/servers/validate |
Dry-run validation of a server config without persisting anything. For source_type="external", connects to the remote MCP server and returns the discovered tool count. For source_type="openapi", fetches the spec, builds an in-process FastMCP server, and counts its tools. |
200 |
GET |
/api/v1/mcp/servers |
List all registered servers (masked). | 200 |
GET |
/api/v1/mcp/servers/{name} |
Get a single server (masked). | 200 |
GET |
/api/v1/mcp/servers/{name}/reveal |
Get a single server with plaintext secrets. Use with care. | 200 |
PUT |
/api/v1/mcp/servers/{name} |
Update a server (URL, headers, auth token, openapi spec). Re-mounts openapi servers. | 200 |
DELETE |
/api/v1/mcp/servers/{name} |
Delete a server and unmount it if openapi. | 204 |
The request body for POST and PUT extends the McpServerConfig shape with two fields:
| Field | Type | Default | Description |
|---|---|---|---|
name |
string | required | Unique server name (1–100 chars). |
url |
string | null |
Server URL. Required for source_type="external", omitted for source_type="openapi" (the mounted URL is generated). |
headers |
dict | {} |
HTTP headers sent to the server (upstream auth for openapi). |
env |
dict | {} |
Environment variables for stdio transport. |
auth_token |
string | null |
Bearer auth token (external only). Encrypted at rest. |
source_type |
"external" | "openapi" |
"external" |
Origin of the server. openapi triggers spec fetch + FastMCP generation. |
openapi_url |
string | null |
URL of the OpenAPI document. Required when source_type="openapi". |
Swagger 2.0 → OpenAPI 3.0 conversion
When source_type="openapi" and the fetched document is a Swagger 2.0 spec (swagger: "2.0"), the service converts it to OpenAPI 3.0 in-process, in pure Python, with no external service call (offline). The converter handles:
swagger: "2.0"→openapi: "3.0.x"host+basePath+schemes→servers[].urldefinitions→components.schemasresponses/parameters/securityDefinitions→components.*produces/consumes→ per-operationrequestBody.content/responses.*.contentx-...vendor extensions are preserved
After conversion, the resulting OpenAPI 3.0 document is fed to FastMCP to build the generated server. If the document is already OpenAPI 3.0, no conversion is performed. Validation errors (malformed spec, unreachable URL, unsupported version) are returned as 422 responses on /validate and POST.
Generated server mounting & lifecycle
For source_type="openapi" servers, the service:
- Fetches the OpenAPI/Swagger document from
openapi_url(with optionalheadersfor upstream auth). - Converts Swagger 2.0 → OpenAPI 3.0 if needed (see above).
- Builds a FastMCP server exposing one MCP tool per OpenAPI operation (operationId or method+path as the tool name).
- Mounts the server at
/generated/{name}/mcpusing streamable HTTP transport, with proper lifespan management via anAsyncExitStackso that HTTP clients and sessions opened by FastMCP
No comments yet
Be the first to share your take.