ClauseLens MCP
README.md
# ClauseLens MCP



**ClauseLens turns any MCP-capable AI (Claude, Cursor, etc.) into a contract analyst.** Point it at a lease, NDA, employment offer, SaaS agreement, or Terms of Service and it walks the AI through a disciplined workflow: fetch the document, split it into clauses, classify each against a risk taxonomy, score severity from *your* side of the deal, and β critically β verify every quote is real before showing it to you.
It's a remote [Model Context Protocol](https://modelcontextprotocol.io) server. The connecting AI does all the reasoning; the server just gives it sharp, safe tools and a rigorous process to follow.
> **π Live server:** `https://clauselens-mcp-production.up.railway.app/mcp`
> No sign-up, no API key β add the URL to Claude and start analyzing.
---
## Why it's built this way
Three design decisions make ClauseLens different from "just paste your contract into ChatGPT":
1. **Zero server-side LLM calls.** The server never talks to an LLM and needs no `ANTHROPIC_API_KEY`. All intelligence lives in the AI that connects to it. The server's job is to be a *trustworthy instrument* β fetch, segment, verify β not a second brain. This makes it cheap, fast, stateless, and model-agnostic.
2. **Grounding guardrail against hallucination.** AIs love to paraphrase and occasionally invent contract language. ClauseLens forces the model to call `verify_spans` and prove every quote it intends to show appears *verbatim* at the exact character offsets in the source. Anything that doesn't verify gets dropped. You never see a fabricated clause.
3. **No document retention.** Contracts are sensitive. Every byte is processed in memory during the request and then gone β nothing is stored, logged, or persisted. Ever.
---
## How it works
### The analysis flow
When you ask Claude to "analyze this contract," the `analyze_contract` prompt injects a 7-step workflow that drives the tools in order:
```
You: "Analyze this freelance agreement from the contractor's side: <url>"
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 1. fetch_document(url) β clean text (SSRF-hardened) β
β 2. segment_clauses(text) β clauses w/ exact char offsets β
β 3. read clauselens://taxonomy + severity-rubric resources β
β 4. classify each clause against the 15 risk categories β
β 5. judge risk FROM THE CHOSEN SIDE (contractor here) β
β 6. score severity (lowβcritical) + confidence (0.0β1.0) β
β 7. verify_spans(text, quotes) β drop anything not verbatim β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
You get: an overall risk score, a severity breakdown, and per-clause
findings β each with a verbatim quote, plain-English meaning, why it
matters to you, and what you might do about it.
```
The AI is told to pick a **side** first (tenant vs. landlord, contractor vs. client), because the same indemnification clause that endangers a freelancer protects the company. Risk is always relative to *who's signing*.
### System architecture
```
βββββββββββββββββββββββ
β Claude / Cursor β does all reasoning Β· zero server-side LLM calls
βββββββββββ¬ββββββββββββ
β HTTPS Β· Streamable HTTP (MCP)
βΌ
βββββββββββββββββββββββ
β Railway β TLS termination Β· public HTTPS URL
βββββββββββ¬ββββββββββββ
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FastMCP 3.x (Streamable HTTP transport) β
β β
β Middleware β
β β’ IP rate limiting (60 req/hr, returns 429) β
β β’ /health bypass β
β β
β Tools β
β fetch_document β SSRF-hardened, streaming, capped fetch β
β segment_clauses β offset-exact clause splitter β
β verify_spans β verbatim-quote grounding guardrail β
β get_risk_taxonomyβ the 15-category risk reference β
β β
β Resources β
β clauselens://taxonomy (15 risk categories) β
β clauselens://severity-rubric (low / med / high / crit) β
β β
β Prompt β
β analyze_contract (injects the 7-step workflow) β
β β
β No document retention β everything in-memory β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
---
## Integrating with AI tools
### Claude.ai (web)
1. Go to **Settings β Connectors β Add custom connector**
2. Name it `ClauseLens` and paste the URL:
`https://clauselens-mcp-production.up.railway.app/mcp`
3. Click **Add** / **Connect** β no sign-in required.
4. Start a chat and try: *"Use ClauseLens to analyze this Terms of Service from the customer's side: https://example.com/terms"*
### Claude Desktop
Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
```json
{
"mcpServers": {
"clauselens": {
"url": "https://clauselens-mcp-production.up.railway.app/mcp"
}
}
}
```
Restart Claude Desktop. ClauseLens tools appear in the π menu.
### Cursor
In **Settings β MCP β Add new MCP server**, choose transport `HTTP` and enter the same URL. ClauseLens tools become available to the agent.
### Any MCP client
ClauseLens speaks standard MCP over Streamable HTTP at `/mcp`. Any compliant client can connect with just the URL β there are no auth headers to configure.
---
## MCP surface reference
### Tools
| Tool | Description |
|------|-------------|
| `fetch_document(url)` | Fetches a public URL and returns cleaned readable text (via trafilatura). SSRF-hardened, streams the body with a 2 MB cap, 10s timeout, 100k-char text cap. Returns `DocumentText`. |
| `segment_clauses(text)` | Splits contract text into clauses with exact character offsets. Guarantees the invariant `text[c.char_start:c.char_end] == c.text`. Returns `list[Clause]`. |
| `verify_spans(text, spans)` | Grounding guardrail. Confirms each quoted span matches the source verbatim at its offsets. The AI must drop any clause that fails. Returns `VerificationResult`. |
| `get_risk_taxonomy()` | Returns the 15 risk categories with definitions and signal language. Returns `dict`. |
### Resources
| URI | Description |
|-----|-------------|
| `clauselens://taxonomy` | The 15 risk categories in markdown |
| `clauselens://severity-rubric` | The 4-level severity scale and confidence calibration guidance |
### Prompt
**`analyze_contract(document, is_url=False, perspective=None)`** β injects the full 7-step analysis workflow.
| Parameter | Type | Description |
|-----------|------|-------------|
| `document` | `str` | Contract text, or a URL if `is_url=True` |
| `is_url` | `bool` | Whether `document` is a URL to fetch |
| `perspective` | `str` | The side you're on (e.g. `"tenant"`, `"freelancer"`). If omitted, the AI assumes you're the weaker party being asked to sign. |
### The 15 risk categories
`auto_renewal` Β· `unilateral_change` Β· `liability_limitation` Β· `indemnification` Β· `arbitration_or_class_waiver` Β· `data_sharing_or_privacy` Β· `ip_assignment` Β· `non_compete_or_non_solicit` Β· `termination_terms` Β· `payment_fees_penalties` Β· `confidentiality` Β· `governing_law_jurisdiction` Β· `warranty_disclaimer` Β· `assignment_or_transfer` Β· `other`
---
## Security
**SSRF hardening.** `fetch_document` blocks requests to private IP ranges (RFC 1918, loopback, link-local, and cloud metadata endpoints like `169.254.169.254`), allows only `http`/`https`, and enforces a 10-second timeout. It defends against **DNS-rebinding (TOCTOU)** by resolving the hostname once and pinning the TCP connection to that validated IP β while preserving the original hostname for TLS SNI and certificate verification. The URL is **re-validated on every redirect hop**, so a public URL can't 302 you into the internal network.
**Memory-exhaustion protection.** The response body is *streamed* and capped at 2 MB β the transfer aborts the instant it crosses the limit, so a hostile server can't push an unbounded payload into memory. Extracted text is further capped at 100k characters.
**No document retention.** All processing happens in memory during the request lifecycle. No contract text, extracted content, or analysis is stored, logged, or persisted.
**Rate limiting.** IP-based fixed-window limiter runs in middleware before any tool logic. Default 60 req/hour per IP; returns HTTP 429 when exceeded. Tunable via `RATELIMIT_REQUESTS` / `RATELIMIT_WINDOW_SECONDS`.
> **Scaling note:** the rate limiter is in-process, so counters are per-instance and reset on restart β correct for the current single-instance deployment. Multiple replicas would need a shared store (e.g. Redis).
**Typed I/O.** All tool inputs and outputs are validated through Pydantic models.
---
## Local development
```bash
git clone https://github.com/Jaydatta-Bade/clauselens-mcp.git
cd clauselens-mcp
pip install -r requirements.txt
python server.py # serves http://localhost:8000/mcp
```
No configuration is required β every environment variable is optional (see `.env.example`).
### Running tests
```bash
pip install -r requirements-dev.txt
pytest -q
```
44 tests cover SSRF hardening, the streaming size cap, clause segmentation offset-exactness, the grounding guardrail, rate limiting, and schema validation. CI runs them on Python 3.12 and 3.13 on every push.
---
## Deploy your own instance
ClauseLens runs anywhere that serves a Python ASGI app. On [Railway](https://railway.app):
```bash
npm install -g @railway/cli
railway login
railway init
railway up
```
Railway assigns a public HTTPS URL; append `/mcp` to get your MCP endpoint. Optional env vars:
| Variable | Description | Default |
|----------|-------------|---------|
| `RATELIMIT_REQUESTS` | Requests per window per IP | `60` |
| `RATELIMIT_WINDOW_SECONDS` | Window length in seconds | `3600` |
| `PORT` | Server port (Railway sets this automatically) | `8000` |
---
## Project structure
```
server.py FastMCP app: tools, resources, prompt, middleware
ratelimit.py in-process IP rate limiter
schemas.py Pydantic models (DocumentText, Clause, Span, β¦)
taxonomy.py the 15 risk categories + severity rubric text
prompts.py the analyze_contract workflow prompt
tools/
fetch.py SSRF-hardened streaming document fetch
segment.py offset-exact clause segmentation
verify.py verbatim-quote grounding guardrail
tests/ 44 tests (pytest)
```
---
## Tech stack
[FastMCP 3.x](https://github.com/jlowin/fastmcp) Β· Starlette/ASGI Β· Pydantic v2 Β· httpx Β· trafilatura Β· Railway Β· GitHub Actions
---
## Disclaimer
ClauseLens provides automated information, **not legal advice**. It can miss issues or misread context. For decisions that matter, consult a qualified lawyer.
---
## License
[MIT](LICENSE)
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues