Upload NDAs, MSAs, or vendor contracts. The agent extracts key terms, flags non-standard clauses, compares against your template, and outputs a risk summary with recommended changes.
This agent assists with contract review and risk identification — it is not a substitute for qualified legal advice. The output should be reviewed by a lawyer before taking any action based on it. High-value or high-risk contracts must always go through qualified legal counsel.
Net payment days, late payment penalties, payment milestones
Notice periods, termination for convenience, cure periods
Limitation of liability clauses, exclusions, indemnification
Who owns work-for-hire, IP assignment, licensing terms
NDA scope, duration, permitted disclosures, carve-outs
Auto-renew clauses, opt-out windows, price escalation
Jurisdiction, governing law, arbitration vs. litigation
Non-compete, non-solicitation, exclusivity obligations
Claude's long context window (200k tokens) means it can read entire contracts without chunking. This is the key advantage over other approaches. Get a key at console.anthropic.com.
Before building, write down your standard positions on the key clauses: e.g. "We always want Net 30 payment terms", "We never accept unlimited liability", "We require governing law to be English law". This becomes the comparison baseline.
Gather 2–3 sample contracts (NDAs, MSAs, vendor agreements). You can find publicly available template contracts on sites like LegalTemplates.net if you don't have real ones to test with.
mkdir contract-review && cd contract-review
python3 -m venv venv
source venv/bin/activate
pip install anthropic pypdf python-dotenv
ANTHROPIC_API_KEY=sk-ant-...
import os
import sys
from pathlib import Path
from datetime import datetime
from dotenv import load_dotenv
import anthropic
from pypdf import PdfReader
load_dotenv()
claude = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
# ── Your standard positions (the "playbook") ──────────────────────────────────
# Edit this to match your company's standard contract positions.
PLAYBOOK = """
OUR STANDARD CONTRACT POSITIONS:
PAYMENT TERMS:
- Standard: Net 30 days from invoice
- Acceptable: Net 45 days
- Red flag: Net 60+ days, no late payment clause, payment milestones with unclear triggers
TERMINATION:
- We require: Either party can terminate for convenience with 30 days notice
- Acceptable: 60 days notice
- Red flag: Termination for convenience requires 90+ days notice,
or only "for cause" termination allowed
LIABILITY:
- Our cap: Limited to fees paid in the last 12 months
- Acceptable: 2x annual fees
- Red flag: Unlimited liability, no cap, or cap includes consequential damages
- We always exclude: Indirect, special, punitive, consequential damages
IP OWNERSHIP:
- We retain all IP we bring to the project
- Work-for-hire output: ownership negotiable, we prefer dual license
- Red flag: Assignment of all IP including background IP to the other party
CONFIDENTIALITY:
- Standard duration: 3 years post-termination
- Acceptable: 5 years
- Red flag: Perpetual confidentiality, no carve-outs for publicly available info
AUTO-RENEWAL:
- We prefer: No auto-renewal, or 30-day opt-out window
- Red flag: Auto-renewal with less than 30 days opt-out notice
GOVERNING LAW:
- Preferred: Laws of England and Wales
- Acceptable: Laws of any common-law jurisdiction
- Red flag: Exclusive jurisdiction in a foreign/unfamiliar court
EXCLUSIVITY:
- We do not accept exclusivity obligations
- We do not accept non-compete clauses broader than 6 months post-termination
"""
# ── PDF extraction ─────────────────────────────────────────────────────────────
def extract_pdf_text(pdf_path: str) -> str:
"""Extract all text from a PDF file."""
reader = PdfReader(pdf_path)
text = ""
for page in reader.pages:
text += page.extract_text() + "\n"
return text.strip()
# ── Contract review with Claude ───────────────────────────────────────────────
def review_contract(contract_text: str, filename: str) -> str:
"""Send the contract to Claude for review against the playbook."""
prompt = f"""You are a senior commercial lawyer reviewing a contract on
behalf of our company. Compare the contract against our standard positions
(the "playbook") and produce a structured risk assessment.
OUR PLAYBOOK:
{PLAYBOOK}
CONTRACT TO REVIEW (filename: {filename}):
{contract_text}
Produce the following structured review:
## CONTRACT OVERVIEW
- Document type (NDA, MSA, SOW, etc.)
- Parties involved
- Effective date and duration
- Total contract value if stated
## KEY TERMS EXTRACTED
For each clause category below, state: what the contract says, whether it
matches our playbook, and risk level (✅ OK | ⚠️ Review | 🔴 Red Flag):
1. Payment Terms
2. Termination Rights
3. Liability Cap
4. IP Ownership
5. Confidentiality
6. Auto-Renewal
7. Governing Law
8. Exclusivity / Non-compete
9. Any other unusual or noteworthy clauses
## RISK SUMMARY
List all flagged items in priority order:
| Risk | Clause | Current Language | Our Standard | Severity |
|------|--------|-----------------|--------------|----------|
## RECOMMENDED CHANGES
For each red flag, write the specific language change we should request:
"Change [current clause] to [our preferred language]."
## OVERALL RISK RATING
- Green (proceed): No material issues
- Amber (proceed with changes): Issues present but manageable with amendments
- Red (legal review required): Significant risk requiring lawyer involvement
State your overall risk rating and a 2-sentence summary of the most
important issues."""
response = claude.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=4000,
messages=[{"role": "user", "content": prompt}]
)
return response.content[0].text
# ── Save output ───────────────────────────────────────────────────────────────
def save_review(review: str, original_filename: str):
"""Save the review to a text file."""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
base_name = Path(original_filename).stem
output_file = f"review_{base_name}_{timestamp}.md"
with open(output_file, 'w') as f:
f.write(f"# Contract Review: {original_filename}\n")
f.write(f"Reviewed: {datetime.now().strftime('%d %B %Y at %H:%M')}\n\n")
f.write(review)
return output_file
# ── Main ──────────────────────────────────────────────────────────────────────
def main():
if len(sys.argv) < 2:
print("Usage: python review_agent.py contract.pdf")
print(" python review_agent.py *.pdf (review multiple)")
sys.exit(1)
pdf_paths = sys.argv[1:]
for pdf_path in pdf_paths:
if not os.path.exists(pdf_path):
print(f"File not found: {pdf_path}")
continue
print(f"\nReviewing: {pdf_path}")
print("Extracting text from PDF...")
contract_text = extract_pdf_text(pdf_path)
word_count = len(contract_text.split())
print(f"Extracted {word_count} words")
if word_count < 100:
print("⚠️ Very short document — check that the PDF contains readable text (not scanned images)")
print("Sending to Claude for review (this takes 15–30 seconds)...")
review = review_contract(contract_text, pdf_path)
# Display the review
print("\n" + "="*60)
print(review)
print("="*60)
# Save to file
output_file = save_review(review, pdf_path)
print(f"\n✓ Review saved to: {output_file}")
if __name__ == "__main__":
main()
source venv/bin/activate
# Review a single contract
python review_agent.py vendor-nda.pdf
# Review multiple contracts at once
python review_agent.py contracts/vendor-nda.pdf contracts/msa-draft.pdf
Sample output for a vendor NDA:
## CONTRACT OVERVIEW
- Document type: Mutual Non-Disclosure Agreement
- Parties: Acme Corp ("Discloser") and Our Company ("Recipient")
- Effective date: 1 March 2025
- Duration: 2 years
## KEY TERMS EXTRACTED
**1. Payment Terms**
N/A — NDA only. ✅ OK
**2. Termination Rights**
Either party may terminate with 30 days written notice. ✅ OK — matches our standard.
**3. Liability Cap**
Clause 8.3: "Liability shall not exceed £5,000 in aggregate."
⚠️ REVIEW — Cap of £5,000 is very low. Our standard is fees paid in last 12 months (if applicable) or at minimum a reasonable cap tied to the purpose of the agreement.
**4. Confidentiality Duration**
Clause 5: "Obligations survive for 7 years post-termination."
🔴 RED FLAG — 7 years exceeds our standard of 3–5 years. Recommend requesting 3 years.
**6. Auto-Renewal**
Clause 9: "Agreement automatically renews annually unless either party gives 90 days notice."
🔴 RED FLAG — 90-day opt-out window is excessive. Our standard is 30 days.
## OVERALL RISK RATING
**🟡 Amber — Proceed with changes**
Two clauses require amendment before signing: the 7-year confidentiality
duration (request 3 years) and the 90-day auto-renewal opt-out window
(request 30 days). The £5,000 liability cap should also be reviewed.
Pass two versions of the same contract (original + redlined version) and ask Claude to identify every change made, whether each change is acceptable against the playbook, and which changes still need further negotiation.
Use the Gmail API to watch for email attachments with "NDA", "MSA", or "Agreement" in the subject line. Automatically extract and review them, then email the review summary back to the sender.
For scanned PDF contracts (image-based, not text-based), add pytesseract for OCR, or use Claude's vision API to pass the PDF pages as images — Claude can read and review both.
Log every review to a database. After 50+ reviews, analyse patterns: which clauses do vendors most commonly push back on? Which clauses most often get flagged red? Use this to improve your playbook.