rti-mcp 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rti_mcp-0.3.2/.gitignore +47 -0
- rti_mcp-0.3.2/CHANGELOG.md +77 -0
- rti_mcp-0.3.2/CONTRIBUTING.md +137 -0
- rti_mcp-0.3.2/LICENSE +21 -0
- rti_mcp-0.3.2/MANIFEST.in +13 -0
- rti_mcp-0.3.2/PKG-INFO +373 -0
- rti_mcp-0.3.2/README.md +341 -0
- rti_mcp-0.3.2/SECURITY.md +60 -0
- rti_mcp-0.3.2/pyproject.toml +74 -0
- rti_mcp-0.3.2/requirements.txt +3 -0
- rti_mcp-0.3.2/rti_mcp/__init__.py +18 -0
- rti_mcp-0.3.2/rti_mcp/__main__.py +3 -0
- rti_mcp-0.3.2/rti_mcp/_version.py +24 -0
- rti_mcp-0.3.2/rti_mcp/client.py +636 -0
- rti_mcp-0.3.2/rti_mcp/config.py +49 -0
- rti_mcp-0.3.2/rti_mcp/server.py +420 -0
- rti_mcp-0.3.2/rti_mcp.egg-info/PKG-INFO +373 -0
- rti_mcp-0.3.2/rti_mcp.egg-info/SOURCES.txt +29 -0
- rti_mcp-0.3.2/rti_mcp.egg-info/dependency_links.txt +1 -0
- rti_mcp-0.3.2/rti_mcp.egg-info/entry_points.txt +2 -0
- rti_mcp-0.3.2/rti_mcp.egg-info/requires.txt +7 -0
- rti_mcp-0.3.2/rti_mcp.egg-info/scm_file_list.json +30 -0
- rti_mcp-0.3.2/rti_mcp.egg-info/scm_version.json +8 -0
- rti_mcp-0.3.2/rti_mcp.egg-info/top_level.txt +1 -0
- rti_mcp-0.3.2/setup.cfg +4 -0
- rti_mcp-0.3.2/tests/conftest.py +52 -0
- rti_mcp-0.3.2/tests/fixtures/dashboard.html +57 -0
- rti_mcp-0.3.2/tests/fixtures/list_pending.html +83 -0
- rti_mcp-0.3.2/tests/fixtures/status_detail.html +37 -0
- rti_mcp-0.3.2/tests/test_parsing.py +196 -0
- rti_mcp-0.3.2/tests/test_search.py +71 -0
rti_mcp-0.3.2/.gitignore
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
*.egg
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.eggs/
|
|
9
|
+
|
|
10
|
+
# Written by setuptools-scm at build time; the git tag is the real source.
|
|
11
|
+
rti_mcp/_version.py
|
|
12
|
+
|
|
13
|
+
# Virtualenvs
|
|
14
|
+
.venv/
|
|
15
|
+
venv/
|
|
16
|
+
env/
|
|
17
|
+
ENV/
|
|
18
|
+
|
|
19
|
+
# Tooling caches
|
|
20
|
+
.pytest_cache/
|
|
21
|
+
.ruff_cache/
|
|
22
|
+
.mypy_cache/
|
|
23
|
+
.coverage
|
|
24
|
+
htmlcov/
|
|
25
|
+
.tox/
|
|
26
|
+
|
|
27
|
+
# Editors / OS
|
|
28
|
+
.vscode/
|
|
29
|
+
.idea/
|
|
30
|
+
*.swp
|
|
31
|
+
.DS_Store
|
|
32
|
+
Thumbs.db
|
|
33
|
+
|
|
34
|
+
# --- Never commit these -------------------------------------------------
|
|
35
|
+
# The server's own state directory, if anyone points RTI_MCP_HOME at the
|
|
36
|
+
# repo. config.json holds a session URL that reads your RTI account.
|
|
37
|
+
.rti-mcp/
|
|
38
|
+
config.json
|
|
39
|
+
rti_config.json
|
|
40
|
+
|
|
41
|
+
# Downloaded replies, exported spreadsheets, saved portal pages: these are
|
|
42
|
+
# your real applications and your real name.
|
|
43
|
+
*.pdf
|
|
44
|
+
*.csv
|
|
45
|
+
*.xlsx
|
|
46
|
+
documents/
|
|
47
|
+
!tests/fixtures/*.html
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
|
+
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.3.1] — 2026-08-07
|
|
10
|
+
|
|
11
|
+
First release published to PyPI: `pip install rti-mcp`.
|
|
12
|
+
|
|
13
|
+
0.1.1, 0.2.0, 0.2.1 and 0.3.0 were never released. Each of those tags was cut
|
|
14
|
+
against a tree that still declared an older version in its source, so the
|
|
15
|
+
number on the tag and the number in the package disagreed. Versions now come
|
|
16
|
+
from the tag itself and cannot drift again.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- `rti_list(search=...)` and `rti_search` now match on a row's status date, and
|
|
21
|
+
accept a date in either the ISO or the dd/mm/yyyy form. Both tools advertised
|
|
22
|
+
searching "by date", but the filter only ever saw the ISO filing date: asking
|
|
23
|
+
what moved on `07/08/2026` returned nothing, with no hint the query had been
|
|
24
|
+
understood as a literal string rather than a date.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- `rti_list` and `rti_search` descriptions now name every searchable field and
|
|
29
|
+
both accepted date formats, rather than the vaguer "or date".
|
|
30
|
+
- README spells out the Windows interpreter path for `claude mcp add`, and notes
|
|
31
|
+
that re-pointing an existing server needs `claude mcp remove` first.
|
|
32
|
+
- The version is derived from the git tag by setuptools-scm. No file in the tree
|
|
33
|
+
records it, so `rti_mcp.__version__`, the distribution metadata and the version
|
|
34
|
+
the MCP client sees are always the tag that was released. Building off an
|
|
35
|
+
untagged commit yields a `.devN` version that PyPI refuses, by design.
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- `pip install rti-mcp` — the project is on PyPI, so installing no longer needs
|
|
40
|
+
git or a clone.
|
|
41
|
+
- Tag-driven release workflow publishing via PyPI Trusted Publishing (OIDC), so
|
|
42
|
+
no API token is stored in the repository. It re-runs the full test matrix and
|
|
43
|
+
refuses to publish anything that is not built from a tagged commit.
|
|
44
|
+
|
|
45
|
+
## [0.1.0] — 2026-08-07
|
|
46
|
+
|
|
47
|
+
First public release.
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
|
|
51
|
+
- Eleven MCP tools over the RTI Online citizen "View History" area:
|
|
52
|
+
`rti_session_status`, `rti_set_session_url`, `rti_dashboard`, `rti_list`,
|
|
53
|
+
`rti_search`, `rti_overdue`, `rti_status`, `rti_details`, `rti_download`,
|
|
54
|
+
`rti_export_csv` and `rti_clear_cache`.
|
|
55
|
+
- Session URL stored in `~/.rti-mcp/config.json` and replaceable at runtime via
|
|
56
|
+
`rti_set_session_url`, so an expired portal session never requires editing an
|
|
57
|
+
MCP client config or restarting the server.
|
|
58
|
+
- Disk cache per (category, bucket) with a configurable TTL, keyed on the
|
|
59
|
+
session URL so a new login cannot serve another account's cached rows.
|
|
60
|
+
- Automatic recovery from the portal's navigation rules: single-use detail
|
|
61
|
+
tokens are never cached, several detail pages are read off one live list, and
|
|
62
|
+
a 403 triggers a re-walk from the seed URL.
|
|
63
|
+
- `unparsed_rows()` reporting, surfaced by `rti_session_status`, so a
|
|
64
|
+
registration-number format change shows up as a warning instead of silently
|
|
65
|
+
missing applications.
|
|
66
|
+
- Offline parser test suite over synthetic fixtures, and CI across Python
|
|
67
|
+
3.10–3.13.
|
|
68
|
+
|
|
69
|
+
### Known issues
|
|
70
|
+
|
|
71
|
+
- The portal's dashboard counts sometimes run a row or two ahead of its own
|
|
72
|
+
list pages. This is the portal's inconsistency; the row parser matches the
|
|
73
|
+
served table exactly, and `rti_dashboard` says so in its output.
|
|
74
|
+
|
|
75
|
+
[Unreleased]: https://github.com/gouthamganeshm/rti-mcp/compare/v0.3.1...HEAD
|
|
76
|
+
[0.3.1]: https://github.com/gouthamganeshm/rti-mcp/compare/v0.1.0...v0.3.1
|
|
77
|
+
[0.1.0]: https://github.com/gouthamganeshm/rti-mcp/releases/tag/v0.1.0
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for looking. This is a small, focused tool: it reads one citizen's own
|
|
4
|
+
RTI Online history and exposes it to an MCP client. Contributions that keep it
|
|
5
|
+
that size are the easiest to merge.
|
|
6
|
+
|
|
7
|
+
## Before anything else
|
|
8
|
+
|
|
9
|
+
**Never include a live session URL, a real registration number you would rather
|
|
10
|
+
keep private, or a real name and email in an issue, a PR, a test fixture or a
|
|
11
|
+
commit.** See [SECURITY.md](SECURITY.md). Fixtures in `tests/fixtures/` are
|
|
12
|
+
synthetic on purpose — keep them that way.
|
|
13
|
+
|
|
14
|
+
## Setting up
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
git clone https://github.com/gouthamganeshm/rti-mcp.git
|
|
18
|
+
cd rti-mcp
|
|
19
|
+
python -m venv .venv
|
|
20
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
21
|
+
pip install -e ".[dev]"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Run the checks:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
ruff check .
|
|
28
|
+
pytest -q
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Both run offline. You do not need an RTI account, a session URL, or network
|
|
32
|
+
access to work on the parsers — which is the point of the fixture suite.
|
|
33
|
+
|
|
34
|
+
## The thing that will break: the portal's HTML
|
|
35
|
+
|
|
36
|
+
This project has no API. It parses pages that a government portal can change
|
|
37
|
+
without warning, and the navigation rules it follows were established
|
|
38
|
+
empirically, not from documentation. `rti_mcp/client.py` opens with a map of
|
|
39
|
+
the page graph and the three rules the portal enforces with bare 403s. Read
|
|
40
|
+
that docstring before changing anything in the fetch path.
|
|
41
|
+
|
|
42
|
+
If the portal changes and something stops parsing:
|
|
43
|
+
|
|
44
|
+
1. Save the page that broke, **by hand, with your own data stripped out** —
|
|
45
|
+
replace the name, email, registration numbers and tokens with obvious
|
|
46
|
+
fakes.
|
|
47
|
+
2. Add it to `tests/fixtures/` and write a failing test.
|
|
48
|
+
3. Fix the parser.
|
|
49
|
+
|
|
50
|
+
A PR with a fixture reproducing the breakage is worth far more than one
|
|
51
|
+
without, because nobody else can see your account.
|
|
52
|
+
|
|
53
|
+
## House style
|
|
54
|
+
|
|
55
|
+
- Match the surrounding code. It favours small functions, explicit names, and
|
|
56
|
+
comments that explain *why* rather than what.
|
|
57
|
+
- Comment the portal's oddities where they bite. Several of the strange-looking
|
|
58
|
+
branches in `client.py` exist because the portal does something unreasonable;
|
|
59
|
+
a comment saying which is more valuable than a tidier-looking line.
|
|
60
|
+
- Surface anomalies rather than swallowing them. `unparsed_rows()` exists
|
|
61
|
+
because a silently dropped row is indistinguishable from an application that
|
|
62
|
+
does not exist. Please keep that instinct.
|
|
63
|
+
- Stay polite to the server. `MIN_REQUEST_GAP` paces requests ~0.6 s apart and
|
|
64
|
+
lists are cached; do not add features that hammer the portal, and do not add
|
|
65
|
+
anything that writes to it.
|
|
66
|
+
|
|
67
|
+
## Scope
|
|
68
|
+
|
|
69
|
+
Happily accepted:
|
|
70
|
+
|
|
71
|
+
- Parser fixes for portal changes, with fixtures.
|
|
72
|
+
- Better error messages, especially around session expiry.
|
|
73
|
+
- New read-only tools over pages the portal already shows the account holder.
|
|
74
|
+
- Documentation, packaging and CI improvements.
|
|
75
|
+
|
|
76
|
+
Out of scope:
|
|
77
|
+
|
|
78
|
+
- **Anything that files, edits, appeals or pays.** This stays read-only. An
|
|
79
|
+
RTI application is a legal act; it should be a deliberate human one.
|
|
80
|
+
- Anything that automates login, solves the captcha, or intercepts the OTP.
|
|
81
|
+
The one-time manual login is a feature, not an obstacle to route around.
|
|
82
|
+
- Bulk scraping of other people's applications, or of the portal at large.
|
|
83
|
+
- Vendoring or committing real account data.
|
|
84
|
+
|
|
85
|
+
## Pull requests
|
|
86
|
+
|
|
87
|
+
Keep them focused, explain what the portal was doing that made the change
|
|
88
|
+
necessary, and make sure `ruff check .` and `pytest -q` pass. Small PRs get
|
|
89
|
+
reviewed faster.
|
|
90
|
+
|
|
91
|
+
By contributing you agree your work is licensed under the MIT License, the same
|
|
92
|
+
as the rest of the project.
|
|
93
|
+
|
|
94
|
+
## Releasing
|
|
95
|
+
|
|
96
|
+
Maintainers only. Releases are cut by pushing a version tag; the `Release`
|
|
97
|
+
workflow runs the full matrix, builds, and uploads to PyPI.
|
|
98
|
+
|
|
99
|
+
One-time setup on PyPI — add a **Trusted Publisher** under the project's
|
|
100
|
+
*Publishing* settings (for the very first upload, use *Publishing* → *Add a
|
|
101
|
+
pending publisher*, since the project does not exist yet):
|
|
102
|
+
|
|
103
|
+
| Field | Value |
|
|
104
|
+
|-------|-------|
|
|
105
|
+
| Owner | `gouthamganeshm` |
|
|
106
|
+
| Repository | `rti-mcp` |
|
|
107
|
+
| Workflow | `release.yml` |
|
|
108
|
+
| Environment | `pypi` |
|
|
109
|
+
|
|
110
|
+
This uses OIDC, so there is no API token to store or rotate. Then create a
|
|
111
|
+
`pypi` environment in the repository's settings if you want a manual approval
|
|
112
|
+
gate before the upload step runs.
|
|
113
|
+
|
|
114
|
+
To release:
|
|
115
|
+
|
|
116
|
+
1. Move the `Unreleased` changelog entries under the new version, and add the
|
|
117
|
+
comparison links at the bottom.
|
|
118
|
+
2. Commit that.
|
|
119
|
+
3. Tag the commit and push:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
git tag v0.3.1
|
|
123
|
+
git push origin main --tags
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**There is no version to bump.** setuptools-scm reads it from the tag, so
|
|
127
|
+
`rti_mcp.__version__`, the distribution metadata and the version the MCP client
|
|
128
|
+
reports are all whatever you tagged. Tag `v0.3.1` and you publish `0.3.1`.
|
|
129
|
+
|
|
130
|
+
Two consequences worth knowing:
|
|
131
|
+
|
|
132
|
+
- A build from an untagged commit is a `.devN` version, not a release. PyPI
|
|
133
|
+
refuses those and the workflow fails early rather than uploading. Tag first.
|
|
134
|
+
- Tag the commit you actually want published. Retagging after a successful
|
|
135
|
+
upload does not help: PyPI never allows a version to be replaced or reused,
|
|
136
|
+
even after deletion. Dry-run anything uncertain against
|
|
137
|
+
[TestPyPI](https://test.pypi.org) first.
|
rti_mcp-0.3.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Goutham Ganesh M H
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
include LICENSE
|
|
2
|
+
include README.md
|
|
3
|
+
include CHANGELOG.md
|
|
4
|
+
include CONTRIBUTING.md
|
|
5
|
+
include SECURITY.md
|
|
6
|
+
include requirements.txt
|
|
7
|
+
|
|
8
|
+
# Ship the test suite complete -- fixtures and conftest included -- so
|
|
9
|
+
# `pytest` works from an unpacked sdist, not just from a git checkout.
|
|
10
|
+
recursive-include tests *.py *.html
|
|
11
|
+
|
|
12
|
+
prune .github
|
|
13
|
+
global-exclude __pycache__ *.py[cod]
|
rti_mcp-0.3.2/PKG-INFO
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rti-mcp
|
|
3
|
+
Version: 0.3.2
|
|
4
|
+
Summary: MCP server for querying your own applications on India's RTI Online portal
|
|
5
|
+
Author: Goutham Ganesh M H
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/gouthamganeshm/rti-mcp
|
|
8
|
+
Project-URL: Repository, https://github.com/gouthamganeshm/rti-mcp
|
|
9
|
+
Project-URL: Issues, https://github.com/gouthamganeshm/rti-mcp/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/gouthamganeshm/rti-mcp/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: mcp,rti,india,transparency,civic-tech,rtionline
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
|
|
21
|
+
Classifier: Topic :: Office/Business
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: mcp>=2.0.0
|
|
26
|
+
Requires-Dist: requests>=2.31.0
|
|
27
|
+
Requires-Dist: beautifulsoup4>=4.12.0
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
30
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# rti-mcp
|
|
34
|
+
|
|
35
|
+
[](https://github.com/gouthamganeshm/rti-mcp/actions/workflows/ci.yml)
|
|
36
|
+
[](https://www.python.org/downloads/)
|
|
37
|
+
[](LICENSE)
|
|
38
|
+
|
|
39
|
+
An [MCP](https://modelcontextprotocol.io) server for querying **your own**
|
|
40
|
+
applications on India's [RTI Online portal](https://rtionline.gov.in) — status,
|
|
41
|
+
filed text, replies and reply PDFs — without re-entering an OTP and two
|
|
42
|
+
captchas for every lookup.
|
|
43
|
+
|
|
44
|
+
Ask your assistant "which of my RTIs are overdue?" instead of clicking through
|
|
45
|
+
`View History` one application at a time.
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
> Which of my RTI requests are more than 30 days overdue?
|
|
49
|
+
|
|
50
|
+
14 of your 146 pending requests are past the Act's 30-day deadline.
|
|
51
|
+
The oldest is DGDOR/R/E/21/00133, filed 1,844 days ago…
|
|
52
|
+
|
|
53
|
+
> What happened to the NIMHANS one?
|
|
54
|
+
|
|
55
|
+
NIMNS/R/E/26/00220 — REQUEST DISPOSED OF as on 30/07/2026. A reply PDF
|
|
56
|
+
is available; want me to download it?
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Contents
|
|
60
|
+
|
|
61
|
+
- [Why this works](#why-this-works)
|
|
62
|
+
- [Install](#install)
|
|
63
|
+
- [Get your session URL](#get-your-session-url)
|
|
64
|
+
- [Register with your MCP client](#register-with-your-mcp-client)
|
|
65
|
+
- [Tools](#tools)
|
|
66
|
+
- [Configuration](#configuration)
|
|
67
|
+
- [How the portal behaves](#how-the-portal-behaves)
|
|
68
|
+
- [Troubleshooting](#troubleshooting)
|
|
69
|
+
- [Security](#security)
|
|
70
|
+
- [Disclaimer](#disclaimer)
|
|
71
|
+
|
|
72
|
+
## Why this works
|
|
73
|
+
|
|
74
|
+
Logging in to RTI Online and clicking **View History** lands you on a
|
|
75
|
+
`citizen_view_history.php` URL whose `emailchk`, `cellchk` and `urletoken`
|
|
76
|
+
parameters are server-side encrypted blobs. That URL authenticates *itself* —
|
|
77
|
+
it keeps working from a plain HTTP client with no cookies carried over from the
|
|
78
|
+
browser, and it stays valid for a long time.
|
|
79
|
+
|
|
80
|
+
This server takes that one URL and walks the rest of the portal from it. You
|
|
81
|
+
pay the OTP + captcha cost once, whenever the URL eventually stops working.
|
|
82
|
+
|
|
83
|
+
It is **read-only**. It only ever reads your own account, and only what the
|
|
84
|
+
portal already shows you when you are logged in. It cannot file, appeal, edit
|
|
85
|
+
or pay. Anyone holding your URL can read the same data, so treat it like a
|
|
86
|
+
password — see [Security](#security).
|
|
87
|
+
|
|
88
|
+
## Install
|
|
89
|
+
|
|
90
|
+
Requires **Python 3.10+**.
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
python -m venv .venv
|
|
94
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
95
|
+
pip install rti-mcp
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Install into a virtual environment rather than system Python: the next section
|
|
99
|
+
asks for an absolute interpreter path, and a venv is what makes that path
|
|
100
|
+
stable.
|
|
101
|
+
|
|
102
|
+
<details>
|
|
103
|
+
<summary>From source, to hack on it</summary>
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
git clone https://github.com/gouthamganeshm/rti-mcp.git
|
|
107
|
+
cd rti-mcp
|
|
108
|
+
python -m venv .venv
|
|
109
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
110
|
+
pip install -e ".[dev]"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Or the unreleased `main`, without a working copy:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
pip install "git+https://github.com/gouthamganeshm/rti-mcp.git"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
</details>
|
|
120
|
+
|
|
121
|
+
> **Point your MCP config at an absolute interpreter path, never a bare
|
|
122
|
+
> `python`.** A bare `python` resolves to whatever environment happens to be
|
|
123
|
+
> active when the client spawns the server, and the server vanishes from the
|
|
124
|
+
> list with a `ModuleNotFoundError` the moment that differs from the
|
|
125
|
+
> environment holding the dependencies. Get the right path with:
|
|
126
|
+
>
|
|
127
|
+
> ```bash
|
|
128
|
+
> python -c "import sys; print(sys.executable)"
|
|
129
|
+
> ```
|
|
130
|
+
|
|
131
|
+
## Get your session URL
|
|
132
|
+
|
|
133
|
+
1. Open <https://rtionline.gov.in> and log in with your OTP and captcha.
|
|
134
|
+
2. Click **View History**.
|
|
135
|
+
3. Copy the entire URL from the address bar. It looks like:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
https://rtionline.gov.in/request/citizen_view_history.php?emailchk=…&cellchk=…&urletoken=…
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Then hand it to the server, either by asking your assistant —
|
|
142
|
+
|
|
143
|
+
> "Set my RTI session URL to `https://rtionline.gov.in/request/citizen_view_history.php?emailchk=…`"
|
|
144
|
+
|
|
145
|
+
— which calls `rti_set_session_url` and stores it in `~/.rti-mcp/config.json`,
|
|
146
|
+
or by exporting it before the client starts:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
export RTI_HISTORY_URL="https://rtionline.gov.in/request/citizen_view_history.php?emailchk=…"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The stored config file wins over the environment variable, so
|
|
153
|
+
`rti_set_session_url` can refresh an expired URL at runtime without touching
|
|
154
|
+
your MCP client config or restarting anything.
|
|
155
|
+
|
|
156
|
+
## Register with your MCP client
|
|
157
|
+
|
|
158
|
+
Each client keeps its own registry — registering with one does **not** populate
|
|
159
|
+
another's list.
|
|
160
|
+
|
|
161
|
+
<details open>
|
|
162
|
+
<summary><b>Claude Code</b></summary>
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
claude mcp add rti-online -s user -- /absolute/path/to/.venv/bin/python -m rti_mcp
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
On Windows the interpreter sits elsewhere in the venv, so pass that path instead:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
claude mcp add rti-online -s user -- C:\path\to\.venv\Scripts\python.exe -m rti_mcp
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Re-pointing an existing entry means removing it first — `claude mcp add` will not
|
|
175
|
+
overwrite one that is already registered:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
claude mcp remove rti-online -s user
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Or a project `.mcp.json` (key: `mcpServers`):
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"mcpServers": {
|
|
186
|
+
"rti-online": {
|
|
187
|
+
"type": "stdio",
|
|
188
|
+
"command": "/absolute/path/to/.venv/bin/python",
|
|
189
|
+
"args": ["-m", "rti_mcp"]
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Verify with `claude mcp list`, or `/mcp` inside a session.
|
|
196
|
+
</details>
|
|
197
|
+
|
|
198
|
+
<details>
|
|
199
|
+
<summary><b>Claude Desktop</b></summary>
|
|
200
|
+
|
|
201
|
+
Edit `claude_desktop_config.json` (macOS:
|
|
202
|
+
`~/Library/Application Support/Claude/`, Windows: `%APPDATA%\Claude\`):
|
|
203
|
+
|
|
204
|
+
```json
|
|
205
|
+
{
|
|
206
|
+
"mcpServers": {
|
|
207
|
+
"rti-online": {
|
|
208
|
+
"command": "C:\\path\\to\\.venv\\Scripts\\python.exe",
|
|
209
|
+
"args": ["-m", "rti_mcp"]
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Restart Claude Desktop afterwards.
|
|
216
|
+
</details>
|
|
217
|
+
|
|
218
|
+
<details>
|
|
219
|
+
<summary><b>VS Code</b></summary>
|
|
220
|
+
|
|
221
|
+
`.vscode/mcp.json` — note the key is `servers`, not `mcpServers`:
|
|
222
|
+
|
|
223
|
+
```json
|
|
224
|
+
{
|
|
225
|
+
"servers": {
|
|
226
|
+
"rti-online": {
|
|
227
|
+
"type": "stdio",
|
|
228
|
+
"command": "/absolute/path/to/.venv/bin/python",
|
|
229
|
+
"args": ["-m", "rti_mcp"]
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
The server will not appear in the Extensions sidebar; that is expected.
|
|
236
|
+
</details>
|
|
237
|
+
|
|
238
|
+
<details>
|
|
239
|
+
<summary><b>Anything else (stdio)</b></summary>
|
|
240
|
+
|
|
241
|
+
The server speaks stdio and is also installed as a console script:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
rti-mcp
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Equivalent to `python -m rti_mcp`. Point any MCP-capable client at either.
|
|
248
|
+
</details>
|
|
249
|
+
|
|
250
|
+
## Tools
|
|
251
|
+
|
|
252
|
+
| Tool | What it does |
|
|
253
|
+
| --- | --- |
|
|
254
|
+
| `rti_session_status` | Is the stored URL still good, and whose account is it |
|
|
255
|
+
| `rti_set_session_url` | Store a fresh URL after re-login; clears the cache |
|
|
256
|
+
| `rti_dashboard` | Registered / disposed / pending totals, requests and appeals |
|
|
257
|
+
| `rti_list` | List one bucket, with optional filter and paging |
|
|
258
|
+
| `rti_search` | Find applications by number fragment, authority code or status |
|
|
259
|
+
| `rti_overdue` | Pending requests past the Act's 30-day reply deadline |
|
|
260
|
+
| `rti_status` | Current status, remarks and reply availability for one application |
|
|
261
|
+
| `rti_details` | Full filed application: authority, information sought, CPIO |
|
|
262
|
+
| `rti_download` | Save the reply PDF or the attached request document |
|
|
263
|
+
| `rti_export_csv` | Export every request and appeal to CSV |
|
|
264
|
+
| `rti_clear_cache` | Force the next query to re-read the portal |
|
|
265
|
+
|
|
266
|
+
Things to ask for once it is wired up:
|
|
267
|
+
|
|
268
|
+
- "What's the status of NIMNS/R/E/26/00220?"
|
|
269
|
+
- "Which of my RTIs are more than 30 days overdue?"
|
|
270
|
+
- "Anything change on my applications today?"
|
|
271
|
+
- "Download the reply for the NIMHANS one."
|
|
272
|
+
- "Export everything to a spreadsheet."
|
|
273
|
+
|
|
274
|
+
## Configuration
|
|
275
|
+
|
|
276
|
+
| Variable | Default | Meaning |
|
|
277
|
+
| --- | --- | --- |
|
|
278
|
+
| `RTI_HISTORY_URL` | — | Seed View History URL (fallback if no config file) |
|
|
279
|
+
| `RTI_MCP_HOME` | `~/.rti-mcp` | Config + cache location |
|
|
280
|
+
| `RTI_MCP_DOWNLOAD_DIR` | `~/.rti-mcp/documents` | Where PDFs and CSVs land |
|
|
281
|
+
| `RTI_MCP_CACHE_TTL` | `900` | Seconds a fetched list stays fresh |
|
|
282
|
+
|
|
283
|
+
Every list-returning tool also takes `refresh=true` to bypass the cache for one
|
|
284
|
+
call.
|
|
285
|
+
|
|
286
|
+
## How the portal behaves
|
|
287
|
+
|
|
288
|
+
Worth knowing, because the constraints shaped the code:
|
|
289
|
+
|
|
290
|
+
- **No pagination.** A bucket's list page carries every row — several hundred
|
|
291
|
+
registered requests in one ~300 KB response. One fetch gets everything.
|
|
292
|
+
- **`registered` is the superset.** Its count equals disposed + pending, so
|
|
293
|
+
`rti_search` and `rti_export_csv` fetch one list per category, not three.
|
|
294
|
+
- **Detail links are single-use-ish.** Every list fetch mints fresh
|
|
295
|
+
`regId`/`token` params, valid only while that fetch is the most recent
|
|
296
|
+
*successful* navigation. Fetching a different list invalidates the previous
|
|
297
|
+
page's links.
|
|
298
|
+
- **A 403 poisons the session.** Once one request 403s, the next one fails too,
|
|
299
|
+
whatever it is. Re-walking seed → list clears it.
|
|
300
|
+
|
|
301
|
+
`client.py` encodes these rules: detail links are never cached to disk, several
|
|
302
|
+
details can be read off one live list, and a 403 triggers an automatic re-walk
|
|
303
|
+
from the seed URL. Requests are paced ~0.6 s apart to stay polite to a
|
|
304
|
+
government server.
|
|
305
|
+
|
|
306
|
+
One known quirk, surfaced rather than hidden: the dashboard's counts sometimes
|
|
307
|
+
run a row or two ahead of its own list pages (e.g. it says 146 pending while
|
|
308
|
+
the pending page lists 144). That is the portal's inconsistency, not a parsing
|
|
309
|
+
gap — the row parser matches the served table exactly, and any row it fails to
|
|
310
|
+
recognise is reported by `rti_session_status` instead of being dropped.
|
|
311
|
+
|
|
312
|
+
## Troubleshooting
|
|
313
|
+
|
|
314
|
+
**The server does not appear in the client's tool list.**
|
|
315
|
+
Almost always the interpreter path. Run
|
|
316
|
+
`/absolute/path/to/python -m rti_mcp` in a terminal: if it fails with
|
|
317
|
+
`ModuleNotFoundError: No module named 'rti_mcp'`, the package is installed into
|
|
318
|
+
a different environment than the one your config names.
|
|
319
|
+
|
|
320
|
+
**"The RTI Online session URL is no longer valid."**
|
|
321
|
+
It expired. Log in again, open View History, copy the URL, and pass it to
|
|
322
|
+
`rti_set_session_url`. Nothing else needs changing.
|
|
323
|
+
|
|
324
|
+
**Results look stale.**
|
|
325
|
+
Lists are cached for 15 minutes. Pass `refresh=true`, or call
|
|
326
|
+
`rti_clear_cache`.
|
|
327
|
+
|
|
328
|
+
**A tool reports `unparsed_rows`.**
|
|
329
|
+
The portal changed its registration-number format and some applications are
|
|
330
|
+
missing from results. Please [open an
|
|
331
|
+
issue](https://github.com/gouthamganeshm/rti-mcp/issues) with the *shape* of
|
|
332
|
+
the number that failed — not your real one.
|
|
333
|
+
|
|
334
|
+
**Everything 403s.**
|
|
335
|
+
The session got poisoned mid-walk. The client recovers automatically; if it
|
|
336
|
+
persists, `rti_clear_cache` then retry.
|
|
337
|
+
|
|
338
|
+
## Security
|
|
339
|
+
|
|
340
|
+
`~/.rti-mcp/config.json` holds a URL that grants read access to your entire RTI
|
|
341
|
+
account. Don't commit it, don't paste it into an issue, and don't put it in a
|
|
342
|
+
screenshot. **There is no logout** — revoking it means waiting for the portal
|
|
343
|
+
to expire it.
|
|
344
|
+
|
|
345
|
+
Full details, and how to report a vulnerability, in [SECURITY.md](SECURITY.md).
|
|
346
|
+
|
|
347
|
+
## Disclaimer
|
|
348
|
+
|
|
349
|
+
Not affiliated with, endorsed by, or connected to the Government of India, the
|
|
350
|
+
Department of Personnel and Training, or the RTI Online portal.
|
|
351
|
+
|
|
352
|
+
This is an unofficial client that parses HTML the portal was not designed to
|
|
353
|
+
serve to programs, so it can break whenever the portal changes. It reads only
|
|
354
|
+
the account whose session URL you supply. Use it for your own applications;
|
|
355
|
+
don't point it at the portal at large, and don't remove the request pacing.
|
|
356
|
+
|
|
357
|
+
The 30-day figure `rti_overdue` uses is the
|
|
358
|
+
ordinary deadline under §7(1) of the RTI Act, 2005 — shorter and longer periods
|
|
359
|
+
apply in some cases (48 hours where life or liberty is concerned, 35 or 40 days
|
|
360
|
+
when routed through an APIO or a third party is involved). Check the Act before
|
|
361
|
+
relying on a date.
|
|
362
|
+
|
|
363
|
+
Use this at your own risk.
|
|
364
|
+
|
|
365
|
+
## Contributing
|
|
366
|
+
|
|
367
|
+
Issues and PRs welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). The test suite
|
|
368
|
+
runs entirely offline against synthetic fixtures, so you can work on the
|
|
369
|
+
parsers without an RTI account.
|
|
370
|
+
|
|
371
|
+
## License
|
|
372
|
+
|
|
373
|
+
[MIT](LICENSE) © Goutham Ganesh M H
|