Skip to main content
Glama
dwin-gharibi

roshan-baaz-mcp

by dwin-gharibi
README.md
<div align="center">

<img src="assets/banner.svg" alt="roshan-baaz-mcp" width="100%" />

<img src="assets/icons/baaz.svg" height="100" alt="Baaz icon"/>

# roshan-baaz-mcp

**A self-hostable [Model Context Protocol](https://modelcontextprotocol.io) server
for [Roshan AI](https://www.roshan-ai.ir)'s Baaz (باز) Persian semantic-search service.**

<sub>Built from the public API documentation at
<a href="https://docs.roshan-ai.ir">docs.roshan-ai.ir</a>. Unofficial community integration.</sub>

</div>

---

## What is this?

**Baaz (باز)** is Roshan AI's Persian-native **semantic search and
retrieval** service. You give it documents; it chunks them, computes embeddings,
and stores them across an Elasticsearch + Weaviate backend. You can then run
**semantic queries**, find **similar documents**, check document status, fetch
full documents, and manage indices — all tuned for Persian (فارسی) content.

`roshan-baaz-mcp` wraps that HTTP API as MCP tools so Claude — or any
MCP-compatible client — can call Baaz as first-class tools.

Baaz is **self-hostable**, and a single deployment is rarely enough: teams run
**many independent Baaz instances** (per region, per tenant, per environment).
This server treats **named instances** as a core concept — one MCP process can
route every tool call to the right Baaz deployment via an optional `instance`
argument.

### Highlights

- **100% endpoint coverage** — every documented Baaz endpoint is a tool.
- **Multi-instance / self-hosting first** — route by `instance`; tokens are
  never exposed by `list_instances`.
- **Guardrails** — http(s) URL validation, ~10MB body cap, pagination clamps,
  and token redaction in every error path.
- **Offline docs tool** — `roshan_baaz_docs` describes the service and tools
  without any network call.

## Tool reference

Every tool accepts an optional `instance: str | None` selecting which configured
Baaz deployment to call (defaults to `default_instance`).

| Tool | Method & endpoint | Purpose |
|------|-------------------|---------|
| `baaz_index` | `POST /{index}/index` | Index/update a batch of documents (chunked + embedded). |
| `baaz_semantic_search` | `POST /{index}/document/semantic_query` | Semantic query → ranked documents with matching chunks. |
| `baaz_document_status` | `POST /{index}/document_status` | Check whether documents (by URL) already exist. |
| `baaz_delete_index` | `DELETE /{index}/delete_index` | Delete an entire index and all its documents. |
| `baaz_delete_documents` | `DELETE /{index}/delete_index` (body `{type}`) | Delete only documents of one `type`. |
| `baaz_stats` | `GET /{index}/stats` | Index statistics keyed by `{index}_{type}`. |
| `baaz_similar_documents` | `POST /{index}/similar/documents` | Find documents similar to a given `document_id`. |
| `baaz_view_document` | `GET /{index}/document/view?id=` | Fetch a full document by id. |
| `healthcheck` | `GET /healthcheck` | Check that an instance is up and ready. |
| `list_instances` | _(local)_ | List configured instances — **names + base URLs only, never tokens**. |
| `roshan_baaz_docs` | _(local)_ | Offline docs about Baaz and these tools. |

See the [Baaz API guide](https://docs.roshan-ai.ir) for full request/response
field semantics.

## Install

Requires **Python 3.10+** (developed and tested on 3.11).

```bash
git clone https://github.com/roshan-research/roshan-baaz-mcp.git
cd roshan-baaz-mcp
python -m venv .venv && source .venv/bin/activate
pip install -e .            # add ".[dev]" for the test/lint toolchain
```

Run it (stdio transport by default):

```bash
python -m roshan_baaz_mcp            # or: roshan-baaz-mcp
python -m roshan_baaz_mcp --help     # transports, host/port, log level
```

## Configuration (multi-instance)

Configuration is read from environment variables via `pydantic-settings`.

### Shorthand (single instance)

The fastest way to point at one Baaz deployment. This synthesizes an instance
named `default`:

| Variable | Default | Description |
|----------|---------|-------------|
| `ROSHAN_BAAZ_BASE_URL` | `https://baaz.roshan-ai.ir` | Base URL of the Baaz deployment. |
| `ROSHAN_BAAZ_TOKEN` | _(none)_ | Token sent as `Authorization: Token <token>`. |

```bash
export ROSHAN_BAAZ_BASE_URL=https://baaz.roshan-ai.ir
export ROSHAN_BAAZ_TOKEN=your-secret-token
```

### Nested (one or many named instances)

Prefix `ROSHAN_BAAZ__`, nested delimiter `__`. Each instance under
`INSTANCES__<NAME>__`:

| Variable | Default | Description |
|----------|---------|-------------|
| `ROSHAN_BAAZ__INSTANCES__<NAME>__BASE_URL` | `https://baaz.roshan-ai.ir` | Base URL of that instance. |
| `ROSHAN_BAAZ__INSTANCES__<NAME>__TOKEN` | _(none)_ | Auth token for that instance. |
| `ROSHAN_BAAZ__INSTANCES__<NAME>__VERIFY_SSL` | `true` | Verify the TLS certificate. |
| `ROSHAN_BAAZ__INSTANCES__<NAME>__TIMEOUT` | `60` | Per-request timeout (seconds). |
| `ROSHAN_BAAZ__DEFAULT_INSTANCE` | `default` | Instance used when a tool omits `instance`. |
| `ROSHAN_BAAZ__LOG_LEVEL` | `INFO` | Logging level. |

```bash
# Two self-hosted regions behind one MCP server
export ROSHAN_BAAZ__INSTANCES__TEHRAN__BASE_URL=https://baaz.tehran.example.ir
export ROSHAN_BAAZ__INSTANCES__TEHRAN__TOKEN=tehran-token
export ROSHAN_BAAZ__INSTANCES__MASHHAD__BASE_URL=https://baaz.mashhad.example.ir
export ROSHAN_BAAZ__INSTANCES__MASHHAD__TOKEN=mashhad-token
export ROSHAN_BAAZ__DEFAULT_INSTANCE=tehran
```

Tools then target a deployment with `instance="mashhad"`; omit it to use the
default. Call `list_instances` to discover configured names (tokens are never
returned).

## Use with an MCP client

Add the server to your MCP client (e.g. Claude Desktop) config:

```json
{
  "mcpServers": {
    "roshan-baaz": {
      "command": "python",
      "args": ["-m", "roshan_baaz_mcp"],
      "env": {
        "ROSHAN_BAAZ_BASE_URL": "https://baaz.roshan-ai.ir",
        "ROSHAN_BAAZ_TOKEN": "your-secret-token"
      }
    }
  }
}
```

## Architecture

The MCP client talks to `roshan-baaz-mcp` over a transport (stdio by default).
The server resolves the requested `instance`, applies guardrails, and calls the
Baaz HTTP API.

![Architecture](assets/diagrams/architecture.png)

### Request flow

A typical retrieval session: index documents, run a semantic query, read the
ranked results, then optionally expand with similar documents.

![Request flow](assets/diagrams/request-flow.png)

## Self-hosting & scaling

Because Baaz is self-hosted, one MCP process can front **many** Baaz deployments,
routing each tool call by its `instance` argument (region / tenant / environment).

![Self-hosting multi-instance](assets/diagrams/self-hosting-multi-instance.png)

Scaling notes:

- **Stateless** — the server holds no per-request state; run as many replicas as
  you need behind your scheduler. Configuration comes entirely from the
  environment.
- **One process, many instances** — add instances by setting more
  `ROSHAN_BAAZ__INSTANCES__<NAME>__*` variables; no code changes.
- **Rate limits** — Baaz limits to ~100 requests/minute per deployment; spread
  heavy load across instances and back off on `429`.
- **Body cap** — requests are capped at ~10MB (enforced client-side before the
  call); batch large indexing jobs accordingly.
- **Horizontal autoscaling** — a Kubernetes HPA manifest and Helm chart ship in
  [`deploy/`](./deploy).

## Deployment

Ready-to-use manifests live in [`deploy/`](./deploy):

- **Docker** — [`Dockerfile`](./Dockerfile) and a two-instance
  [`docker-compose.yml`](./docker-compose.yml).
- **Kubernetes** — raw manifests in [`deploy/kubernetes/`](./deploy/kubernetes)
  (namespace, configmap, secret, deployment, service, ingress, hpa, kustomization).
- **Helm** — chart in
  [`deploy/helm/roshan-baaz-mcp/`](./deploy/helm/roshan-baaz-mcp).
- **Terraform** — example in [`deploy/terraform/`](./deploy/terraform).

See [`deploy/README.md`](./deploy/README.md) for details.

## Testing

```bash
pip install -e ".[dev]"
python -m pytest -q          # all HTTP is mocked with respx; no network
ruff check src tests
```

Optional **live** tests run against a real Baaz deployment when you opt in:

```bash
export ROSHAN_BAAZ_LIVE=1
export ROSHAN_BAAZ_BASE_URL=https://baaz.roshan-ai.ir
export ROSHAN_BAAZ_TOKEN=your-secret-token
python -m pytest tests/live -q
```

There is also a no-network smoke test that asserts every tool has a non-empty
description and an `instance` parameter:

```bash
python scripts/smoke_test.py
```

## Examples

[`examples/inspect_server.py`](./examples/inspect_server.py) prints the
registered tools and their schemas without any network call. See
[`examples/README.md`](./examples/README.md).

## License

[MIT](./LICENSE). Baaz (باز) and Roshan AI are products/trademarks of Roshan AI;
this is an unofficial, community-maintained integration built from public docs.

<div align="center">

<img src="assets/icons/roshan.svg" alt="roshan-logo" width="40%"/>

</div>