Metadata-Version: 2.4
Name: rti-mcp
Version: 0.3.2
Summary: MCP server for querying your own applications on India's RTI Online portal
Author: Goutham Ganesh M H
License-Expression: MIT
Project-URL: Homepage, https://github.com/gouthamganeshm/rti-mcp
Project-URL: Repository, https://github.com/gouthamganeshm/rti-mcp
Project-URL: Issues, https://github.com/gouthamganeshm/rti-mcp/issues
Project-URL: Changelog, https://github.com/gouthamganeshm/rti-mcp/blob/main/CHANGELOG.md
Keywords: mcp,rti,india,transparency,civic-tech,rtionline
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
Classifier: Topic :: Office/Business
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: mcp>=2.0.0
Requires-Dist: requests>=2.31.0
Requires-Dist: beautifulsoup4>=4.12.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"
Dynamic: license-file

# rti-mcp

[![CI](https://github.com/gouthamganeshm/rti-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/gouthamganeshm/rti-mcp/actions/workflows/ci.yml)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)

An [MCP](https://modelcontextprotocol.io) server for querying **your own**
applications on India's [RTI Online portal](https://rtionline.gov.in) — status,
filed text, replies and reply PDFs — without re-entering an OTP and two
captchas for every lookup.

Ask your assistant "which of my RTIs are overdue?" instead of clicking through
`View History` one application at a time.

```
> Which of my RTI requests are more than 30 days overdue?

  14 of your 146 pending requests are past the Act's 30-day deadline.
  The oldest is DGDOR/R/E/21/00133, filed 1,844 days ago…

> What happened to the NIMHANS one?

  NIMNS/R/E/26/00220 — REQUEST DISPOSED OF as on 30/07/2026. A reply PDF
  is available; want me to download it?
```

## Contents

- [Why this works](#why-this-works)
- [Install](#install)
- [Get your session URL](#get-your-session-url)
- [Register with your MCP client](#register-with-your-mcp-client)
- [Tools](#tools)
- [Configuration](#configuration)
- [How the portal behaves](#how-the-portal-behaves)
- [Troubleshooting](#troubleshooting)
- [Security](#security)
- [Disclaimer](#disclaimer)

## Why this works

Logging in to RTI Online and clicking **View History** lands you on a
`citizen_view_history.php` URL whose `emailchk`, `cellchk` and `urletoken`
parameters are server-side encrypted blobs. That URL authenticates *itself* —
it keeps working from a plain HTTP client with no cookies carried over from the
browser, and it stays valid for a long time.

This server takes that one URL and walks the rest of the portal from it. You
pay the OTP + captcha cost once, whenever the URL eventually stops working.

It is **read-only**. It only ever reads your own account, and only what the
portal already shows you when you are logged in. It cannot file, appeal, edit
or pay. Anyone holding your URL can read the same data, so treat it like a
password — see [Security](#security).

## Install

Requires **Python 3.10+**.

```bash
python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install rti-mcp
```

Install into a virtual environment rather than system Python: the next section
asks for an absolute interpreter path, and a venv is what makes that path
stable.

<details>
<summary>From source, to hack on it</summary>

```bash
git clone https://github.com/gouthamganeshm/rti-mcp.git
cd rti-mcp
python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
```

Or the unreleased `main`, without a working copy:

```bash
pip install "git+https://github.com/gouthamganeshm/rti-mcp.git"
```

</details>

> **Point your MCP config at an absolute interpreter path, never a bare
> `python`.** A bare `python` resolves to whatever environment happens to be
> active when the client spawns the server, and the server vanishes from the
> list with a `ModuleNotFoundError` the moment that differs from the
> environment holding the dependencies. Get the right path with:
>
> ```bash
> python -c "import sys; print(sys.executable)"
> ```

## Get your session URL

1. Open <https://rtionline.gov.in> and log in with your OTP and captcha.
2. Click **View History**.
3. Copy the entire URL from the address bar. It looks like:

   ```
   https://rtionline.gov.in/request/citizen_view_history.php?emailchk=…&cellchk=…&urletoken=…
   ```

Then hand it to the server, either by asking your assistant —

> "Set my RTI session URL to `https://rtionline.gov.in/request/citizen_view_history.php?emailchk=…`"

— which calls `rti_set_session_url` and stores it in `~/.rti-mcp/config.json`,
or by exporting it before the client starts:

```bash
export RTI_HISTORY_URL="https://rtionline.gov.in/request/citizen_view_history.php?emailchk=…"
```

The stored config file wins over the environment variable, so
`rti_set_session_url` can refresh an expired URL at runtime without touching
your MCP client config or restarting anything.

## Register with your MCP client

Each client keeps its own registry — registering with one does **not** populate
another's list.

<details open>
<summary><b>Claude Code</b></summary>

```bash
claude mcp add rti-online -s user -- /absolute/path/to/.venv/bin/python -m rti_mcp
```

On Windows the interpreter sits elsewhere in the venv, so pass that path instead:

```bash
claude mcp add rti-online -s user -- C:\path\to\.venv\Scripts\python.exe -m rti_mcp
```

Re-pointing an existing entry means removing it first — `claude mcp add` will not
overwrite one that is already registered:

```bash
claude mcp remove rti-online -s user
```

Or a project `.mcp.json` (key: `mcpServers`):

```json
{
  "mcpServers": {
    "rti-online": {
      "type": "stdio",
      "command": "/absolute/path/to/.venv/bin/python",
      "args": ["-m", "rti_mcp"]
    }
  }
}
```

Verify with `claude mcp list`, or `/mcp` inside a session.
</details>

<details>
<summary><b>Claude Desktop</b></summary>

Edit `claude_desktop_config.json` (macOS:
`~/Library/Application Support/Claude/`, Windows: `%APPDATA%\Claude\`):

```json
{
  "mcpServers": {
    "rti-online": {
      "command": "C:\\path\\to\\.venv\\Scripts\\python.exe",
      "args": ["-m", "rti_mcp"]
    }
  }
}
```

Restart Claude Desktop afterwards.
</details>

<details>
<summary><b>VS Code</b></summary>

`.vscode/mcp.json` — note the key is `servers`, not `mcpServers`:

```json
{
  "servers": {
    "rti-online": {
      "type": "stdio",
      "command": "/absolute/path/to/.venv/bin/python",
      "args": ["-m", "rti_mcp"]
    }
  }
}
```

The server will not appear in the Extensions sidebar; that is expected.
</details>

<details>
<summary><b>Anything else (stdio)</b></summary>

The server speaks stdio and is also installed as a console script:

```bash
rti-mcp
```

Equivalent to `python -m rti_mcp`. Point any MCP-capable client at either.
</details>

## Tools

| Tool | What it does |
| --- | --- |
| `rti_session_status` | Is the stored URL still good, and whose account is it |
| `rti_set_session_url` | Store a fresh URL after re-login; clears the cache |
| `rti_dashboard` | Registered / disposed / pending totals, requests and appeals |
| `rti_list` | List one bucket, with optional filter and paging |
| `rti_search` | Find applications by number fragment, authority code or status |
| `rti_overdue` | Pending requests past the Act's 30-day reply deadline |
| `rti_status` | Current status, remarks and reply availability for one application |
| `rti_details` | Full filed application: authority, information sought, CPIO |
| `rti_download` | Save the reply PDF or the attached request document |
| `rti_export_csv` | Export every request and appeal to CSV |
| `rti_clear_cache` | Force the next query to re-read the portal |

Things to ask for once it is wired up:

- "What's the status of NIMNS/R/E/26/00220?"
- "Which of my RTIs are more than 30 days overdue?"
- "Anything change on my applications today?"
- "Download the reply for the NIMHANS one."
- "Export everything to a spreadsheet."

## Configuration

| Variable | Default | Meaning |
| --- | --- | --- |
| `RTI_HISTORY_URL` | — | Seed View History URL (fallback if no config file) |
| `RTI_MCP_HOME` | `~/.rti-mcp` | Config + cache location |
| `RTI_MCP_DOWNLOAD_DIR` | `~/.rti-mcp/documents` | Where PDFs and CSVs land |
| `RTI_MCP_CACHE_TTL` | `900` | Seconds a fetched list stays fresh |

Every list-returning tool also takes `refresh=true` to bypass the cache for one
call.

## How the portal behaves

Worth knowing, because the constraints shaped the code:

- **No pagination.** A bucket's list page carries every row — several hundred
  registered requests in one ~300 KB response. One fetch gets everything.
- **`registered` is the superset.** Its count equals disposed + pending, so
  `rti_search` and `rti_export_csv` fetch one list per category, not three.
- **Detail links are single-use-ish.** Every list fetch mints fresh
  `regId`/`token` params, valid only while that fetch is the most recent
  *successful* navigation. Fetching a different list invalidates the previous
  page's links.
- **A 403 poisons the session.** Once one request 403s, the next one fails too,
  whatever it is. Re-walking seed → list clears it.

`client.py` encodes these rules: detail links are never cached to disk, several
details can be read off one live list, and a 403 triggers an automatic re-walk
from the seed URL. Requests are paced ~0.6 s apart to stay polite to a
government server.

One known quirk, surfaced rather than hidden: the dashboard's counts sometimes
run a row or two ahead of its own list pages (e.g. it says 146 pending while
the pending page lists 144). That is the portal's inconsistency, not a parsing
gap — the row parser matches the served table exactly, and any row it fails to
recognise is reported by `rti_session_status` instead of being dropped.

## Troubleshooting

**The server does not appear in the client's tool list.**
Almost always the interpreter path. Run
`/absolute/path/to/python -m rti_mcp` in a terminal: if it fails with
`ModuleNotFoundError: No module named 'rti_mcp'`, the package is installed into
a different environment than the one your config names.

**"The RTI Online session URL is no longer valid."**
It expired. Log in again, open View History, copy the URL, and pass it to
`rti_set_session_url`. Nothing else needs changing.

**Results look stale.**
Lists are cached for 15 minutes. Pass `refresh=true`, or call
`rti_clear_cache`.

**A tool reports `unparsed_rows`.**
The portal changed its registration-number format and some applications are
missing from results. Please [open an
issue](https://github.com/gouthamganeshm/rti-mcp/issues) with the *shape* of
the number that failed — not your real one.

**Everything 403s.**
The session got poisoned mid-walk. The client recovers automatically; if it
persists, `rti_clear_cache` then retry.

## Security

`~/.rti-mcp/config.json` holds a URL that grants read access to your entire RTI
account. Don't commit it, don't paste it into an issue, and don't put it in a
screenshot. **There is no logout** — revoking it means waiting for the portal
to expire it.

Full details, and how to report a vulnerability, in [SECURITY.md](SECURITY.md).

## Disclaimer

Not affiliated with, endorsed by, or connected to the Government of India, the
Department of Personnel and Training, or the RTI Online portal. 

This is an unofficial client that parses HTML the portal was not designed to
serve to programs, so it can break whenever the portal changes. It reads only
the account whose session URL you supply. Use it for your own applications;
don't point it at the portal at large, and don't remove the request pacing.

The 30-day figure `rti_overdue` uses is the
ordinary deadline under §7(1) of the RTI Act, 2005 — shorter and longer periods
apply in some cases (48 hours where life or liberty is concerned, 35 or 40 days
when routed through an APIO or a third party is involved). Check the Act before
relying on a date.

Use this at your own risk.

## Contributing

Issues and PRs welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). The test suite
runs entirely offline against synthetic fixtures, so you can work on the
parsers without an RTI account.

## License

[MIT](LICENSE) © Goutham Ganesh M H
