Install
$ agentstack add mcp-hogu-dev-madosho ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
madosho
madosho is a composable, document-centric RAG platform. It is three things in one codebase: a kernel library for building queryable corpora from documents (PDF, office, web, and more), a service platform (control plane on :8000 + query plane on :8001 + a background worker), and a web workbench for managing documents and tuning retrieval pipelines. The focus is the storage and retrieval side of RAG. Corpora hold both frozen source documents and editable, agent-readable knowledge bases.
Why: what is the best way to do RAG? The honest answer is "it depends" - on the documents, the questions, and the tools. madosho is built to let you find out instead of guessing: make any technique a component (docling vs pypdfium2, one chunker vs another, this embedder vs that one), build it into a named pipeline, and compare the pipelines side by side on your own documents. Every built pipeline keeps its index and stays queryable; comparison is the product, not an afterthought.
Agents: RAG is useful, and RAG used by agents is more useful. madosho is backend-first (backend-only if you want; the frontend is there to visualize the RAG process and organize documents). Agents drive it directly via madosho-cli and MCP, or through the bundled portable skills for Claude Code, Codex, and opencode. Four external interfaces expose the same retrieval core: the native HTTP API + OpenAI shim, chat frontends (Open WebUI), the CLI + agent skills, and an MCP server. See "External interfaces" below, or jump straight to examples/demo/README.md for the full demo trail.
Comparison is the product: a single document carries multiple named pipelines - each a full extract -> chunk -> index build with its own stored index and its own per-step rating - all live and queryable at once.
Status
Runs via Docker Compose on any machine that has Docker - a laptop, a home server, a cloud VM. Clients do not have to sit on the same machine as the stack: every interface takes the stack's address by env var or config (see docs/HEADLESS.md, "Reach the stack from another machine"). Licensed under Apache-2.0. API-key / user auth is built in and on by default. Not yet packaged for PyPI and not production-hardened. Use it to explore, benchmark, and tune retrieval pipelines.
Where the pieces run, and how clients reach them:
flowchart LR
subgraph clients["Clients - any machine"]
BR["Browser"]
AGT["Agents / CLI / MCP"]
end
CADDY["Caddy TLS proxy - opt-incompose.tls.yaml:8443 workbench, :8444 control:8445 query, :8446 toolserverlocal-CA for LAN, Let's Encrypt for public"]
subgraph host["madosho host - docker compose up"]
UI2["Web workbench :8080"]
CP2["Control plane :8000"]
QP2["Query plane :8001"]
TS2["Tool server :8088"]
end
BR ==>|HTTPS - remote / prod| CADDY
AGT ==>|HTTPS - remote / prod| CADDY
CADDY --> UI2
CADDY --> CP2
CADDY --> QP2
CADDY --> TS2
BR -.->|plain HTTP - localhost / dev| UI2
AGT -.->|env var MADOSHO_CONTROL_URL / QUERY_URL| CP2
Quickstart (service + web workbench)
git clone https://github.com/hogu-dev/madosho.git && cd madosho
cp .env.example .env
docker compose up
Auth is on by default. The example .env seeds a first login of admin / admin, so once the stack is up, open the workbench at http://localhost:8080, log in, and change the password immediately on the Users page (use the reset button on your own row). Prefer your own credentials? Set MADOSHO_BOOTSTRAP_ADMIN_USER / MADOSHO_BOOTSTRAP_ADMIN_PASSWORD in .env before the first up, or create the account once the stack is running:
docker compose exec app python -m madosho_server.users_cli create --name admin --scope admin
# prompts for a password
Running the stack on a remote box (a home server, a cloud VM)? Start it with the TLS overlay instead and open https://madosho-host:8443 - see examples/tls/README.md. (Dev-only alternative: plain HTTP at http://madosho-host:8080 with MADOSHO_COOKIE_INSECURE=1 in .env, because the login cookie is Secure-only by default and browsers drop it over plain HTTP from any address except localhost - see docs/AUTH.md.) Upload a PDF, watch it index through the default pipeline, build extra pipelines with different tools from the DocumentDetail page, and compare them side by side. The Scrying tab lets you ask questions and see the retrieved chunks and the assembled prompt.
Indexing, search, and compare all work out of the box. Generated answers (the Scrying answer, research reports, the contextual chunker) are off until you wire an LLM: set MADOSHO_LLM_API_BASE in .env to any OpenAI-compatible endpoint - a bundled services/llm-endpoint / services/agent-server, your own GPU box, or a hosted provider. It is blank by default; see .env.example.
Beyond the env default, named endpoints are registered on the Settings page (or POST /llm-endpoints): provider, model, API base, a key env var, text and vision capability flags, and an API flavor - chat (standard chat-completions, the default) or responses (the OpenAI Responses API). The responses flavor is what subscription relays and local proxy servers typically speak: join the proxy to the compose network (or publish it on a host port), register it with API base http://your-proxy:PORT/v1 and flavor responses, and point the key env var at any placeholder value if the proxy handles authentication itself - the client insists a key exists, the proxy ignores it. An endpoint flagged vision can also transcribe scanned pages for the vision parser.
The service uses per-pipeline Qdrant collections - each named pipeline has its own stored index. All are live at once and queryable. "Selecting" a pipeline for a document just chooses which one answers queries by default; nothing rebuilds.
For headless / API / agent access you will also want an API key:
docker compose exec app python -m madosho_server.keys_cli create --name me --scope write
export MADOSHO_API_KEY= # printed once
See docs/AUTH.md for scopes, browser login, and user accounts. Both humans and agents authenticate into the same scope gate that guards both planes:
flowchart TB
HUM["Humanusername + password"]
AGENT["Agent / CLI / MCPAPI key"]
HUM -->|POST /auth/login| COOKIE["Signed session cookiehttpOnly, HMAC-SHA256carries scope, never the key"]
AGENT -->|Bearer token| GATE
COOKIE --> GATE
PROXY["Tool server / proxieshold no key, forward caller bearer"] -.-> GATE
GATE["Shared auth gate - on by defaultread guards BOTH planes"]
GATE --> CP3["Control plane :8000"]
GATE --> QP3["Query plane :8001"]
Use it as a library
Write a madosho.yaml next to your data:
corpus: contracts
source: ./pdfs
ingest:
parser: router
chunker: docling-hybrid
embedder: granite-embedding-english-r2
store: lancedb
indexes: [bm25, dense]
query:
- keyword_search: {k: 50}
- semantic_search: {k: 50}
- fuse: {method: rrf}
- rerank: {model: granite-reranker-english-r2, top_k: 8}
Then ingest and query (--config path/to/madosho.yaml if you are not in the same directory):
$ madosho ingest
processed: 2 skipped: 0 failed: 0 (2.5s)
$ madosho query "What does the termination clause require?"
[1] ./pdfs/contract_a.pdf p.1 (score 0.910, via rerank)
... The termination clause requires ninety days written notice ...
Or as a Python library:
import madosho
corpus = madosho.open("madosho.yaml")
corpus.ingest() # idempotent: unchanged files are skipped by content hash
for hit in corpus.query("What does the termination clause require?"):
print(hit.citation, hit.score, hit.text)
Where things live:
- A relative
source:resolves against the config file's directory, never
the process cwd.
- Corpus state (LanceDB tables, ingest manifest) lives in
.madosho/next to
the config file.
- Models download once into the standard Hugging Face cache
(~/.cache/huggingface), shared across projects.
Using a Qdrant server instead
Swap the store in madosho.yaml to point at a running Qdrant (docker run -p 6333:6333 qdrant/qdrant):
ingest:
store:
qdrant:
url: http://localhost:6333
Server API keys are read from the QDRANT_API_KEY env var (configurable via the api_key_env option) -- never placed in the config file. Unlike the LanceDB store, the Qdrant store preserves all chunk-metadata keys and exposes multi-vector/MaxSim retrieval through the MultiVectorSearch extension.
External interfaces
Four doors, each covering a different consumer:
flowchart LR
API["Native HTTP/query + OpenAI shim"]
CHAT["Chat frontendsOpen WebUI"]
CLI["CLI + agent skills"]
MCP["MCP server"]
MAN["agent-tools manifestone shared tool schema"]
CORE["Query core :8001"]
QD[("Qdrant")]
LLM["Your LLM"]
API --> CORE
CHAT --> CORE
CLI --> MAN
MCP --> MAN
MAN --> CORE
CORE -->|retrieve| QD
CORE -.->|optional answer| LLM
| Door | What it is | Guide | |------|------------|-------| | Native HTTP + OpenAI shim | /query (cited chunks, or a proxied answer) + /v1/chat/completions shim on :8001 | examples/api-contract/README.md | | Chat frontends | Open WebUI via the shim (proxy) or via the OpenAPI tool server on :8088 (context source) | examples/chat-frontends/README.md | | CLI + agent skills | madosho-cli + two portable SKILL.md skills you copy into any project | skills/README.md | | MCP server | madosho-mcp (stdio default, --http optional) for Claude Desktop, Cursor, IDE agents | examples/mcp/README.md |
All four share the same tool schemas, derived from the one agent-tools manifest (madosho_cli/manifest.py) -- they cannot drift from each other.
Here is an agent (Claude Code) answering over a corpus through the MCP search tool: the retrieval and citations come from madosho, the reasoning from the agent.
The chat-frontends door wires up two ways -- Mode A where madosho produces the answer, Mode B where your own chat model does and madosho is just a retrieval tool:
flowchart TB
OWUI["Open WebUIbrings its own chat model"]
OWUI -->|"Mode A - OpenAI APIbearer = madosho key"| SHIM["Query plane :8001/v1 OpenAI shim"]
OWUI -->|"Mode B - tool callbearer set in OWUI"| TS["Tool server :8088OpenAPI - republishes the agent-tools manifest"]
SHIM -.->|madosho calls your LLM| LLM["Your LLM"]
SHIM ==>|"proxied answer - Shape A"| OWUI
TS -->|forwards caller bearer| CP["Control plane :8000list corpora / documents / pipelines, get doc"]
TS -->|forwards caller bearer| QP["Query plane :8001search"]
QP ==>|"cited chunks - Shape B"| TS
TS ==>|context for OWUI's own model| OWUI
To run all four headless demos in sequence: python examples/demo/demo_all.py.
Full demo trail: examples/demo/README.md. Headless write access (create corpora, upload, build pipelines over HTTP): docs/HEADLESS.md.
Install
madosho ships as three PyPI packages so you install only what you need:
| Package | pip install ... | Use it when you want to... | |---|---|---| | madosho-cli | madosho-cli | drive a running madosho from the shell or an agent (zero dependencies) | | madosho-mcp | madosho-mcp | expose a madosho corpus to an MCP host (Claude Desktop, IDEs, agents) | | madosho | madosho[server] | run the full server/framework yourself |
madosho-cli and madosho-mcp are lightweight HTTP clients - no server, no Postgres, no model stack. The server package pulls madosho-cli in automatically.
To run the full server or develop from source (Python >= 3.11):
pip install ./packaging/madosho-cli # the client dep; only a 0.0.1 placeholder is on PyPI so far
pip install -e ".[local]"
The local extra pulls the full default stack. Extras are independent -- install only what your pipeline uses:
| Extra | Pulls | Used by | |-------|-------|---------| | docling | docling (layout + TableFormer), pypdfium2, onnxruntime | router, docling, pypdfium2 parsers, docling-hybrid chunker; onnxruntime powers the rapidocr OCR engine | | ocr-easyocr | easyocr | the easyocr OCR engine (opt-in; see compose.ocr.yaml) | | models | sentence-transformers | granite embedder and reranker | | lancedb | lancedb, pyarrow | lancedb store | | qdrant | qdrant-client | qdrant store (server; in-process local mode for tests) | | server | fastapi, uvicorn, sqlalchemy, psycopg, procrastinate, any-llm-sdk, python-multipart | the service platform (control + query planes + worker) | | research | any-llm-sdk | the standalone research_agent package | | local | all of docling, lancedb, models | the default library pipeline | | dev | pytest, fpdf2, build, packaging, testcontainers, httpx, openai | running the test suite |
Core itself depends only on pydantic and pyyaml. Dependency floors are tested floors -- the suite runs against every declared minimum (enforced by tests/unit/test_scaffold.py).
> Dev layout: madosho_cli/ and madosho_mcp/ stay at the repo root; their > standalone PyPI build configs live under packaging/. The test suite imports every > package straight from the source tree (pythonpath in pyproject.toml), so a plain > pytest run needs no editable install of the split-out clients.
How it works
flowchart TB
UI["Web workbench:8080"]
AG["Agents and API clientsCLI / MCP / OpenAI shim"]
subgraph planes["madosho service platform - one image, three roles"]
CP["Control plane :8000corpus / document / pipeline CRUD + uploads"]
QP["Query plane :8001/query + /v1 OpenAI shim - stateless"]
WK["Workerbuilds / ratings / eval"]
end
subgraph store["Backing services"]
PG[("Postgresmetadata + job queue")]
QD[("Qdrantper-pipeline vector collections")]
FS[("Filestoreuploaded PDFs")]
MC[("Model cacheembedders + rerankers")]
end
LLM["Your LLMbring-your-own endpoint"]
UI -->|manage| CP
UI -->|ask| QP
AG -->|write| CP
AG -->|search / answer| QP
CP -->|store upload| FS
CP -->|rows + enqueue| PG
WK -->|dequeue| PG
WK -->|read files| FS
WK -->|write index| QD
WK -->|embed| MC
WK -.->|research / index-time| LLM
QP -->|retrieve| QD
QP -->|rerank| MC
QP -.->|answer| LLM
A Corpus is source documents + a default recipe + the indexes built from them. It can also hold knowledge bases - curated, editable page collections that live alongside the frozen source documents (see "Knowledge bases" below).
Ingest is fixed slots: parser -> chunker -> embedder -> store, plus which indexes to build (bm25, dense). In the library path the store is LanceDB (local, no server). In the service path the worker builds per-pipeline Qdrant collections -- each named pipeline gets its own collection that stays live independently.
Query is a composable operator stack, run top to bottom. Search operators (keyword_search, semantic_search) each emit a candidate pool; fuse merges all pools (reciprocal-rank fusion); downstream operators (rerank, chunk_read) transform the list. A stack whose search operators are never followed by fuse yields no hits. The service's query plane (:8001) resolves, per document, its effective pipeline (your saved pick, else the highest-rated), queries that pipeline's Qdrant collection, and RRF-merges across pipelines when multiple are in play.
The build-time slots and the read-time operator stack, side by side:
flowchart TB
subgraph ingest["Ingest - fixed slots, build time"]
direction LR
P["parserrouter / docling / pypdfium2 / vision"] --> C["chunkerdocling-hybrid / semantic / recurs
…
## Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [hogu-dev](https://github.com/hogu-dev)
- **Source:** [hogu-dev/madosho](https://github.com/hogu-dev/madosho)
- **License:** Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.