Context7.py
· 3.7 KiB · Python
Raw
#!/usr/bin/env python3
"""Context7 extension - Query up-to-date documentation for any programming library"""
import json
import urllib.request
import urllib.parse
import ssl
import os
CONTEXT7_API = "https://context7.com/api"
API_KEY = os.environ.get("CONTEXT7_API_KEY") # Optional, for higher rate limits
def context7_get(endpoint, params=None):
"""Make GET request to Context7 API"""
try:
url = f"{CONTEXT7_API}{endpoint}"
if params:
url += "?" + urllib.parse.urlencode(params)
headers = {
"X-Context7-Source": "mcp",
"Accept": "application/json"
}
# Add API key if available
if API_KEY:
headers["Authorization"] = f"Bearer {API_KEY}"
# Create SSL context for mitmproxy compatibility
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
req = urllib.request.Request(url, headers=headers)
resp = urllib.request.urlopen(req, timeout=30, context=ssl_context)
content_type = resp.headers.get("Content-Type", "")
if "application/json" in content_type:
return json.loads(resp.read().decode())
else:
return resp.read().decode()
except Exception as e:
return {"error": f"{type(e).__name__}: {e}"}
@register_tool(
"context7_search",
"Search for libraries by name (e.g., 'react', 'express', 'django')",
{"query": "string"}
)
def context7_search(args):
"""Search for libraries in Context7"""
query = args.get("query", "")
if not query:
return "error: query required"
result = context7_get("/v1/search", {"query": query})
if isinstance(result, dict) and "error" in result:
return f"error: {result['error']}"
if isinstance(result, dict):
results = result.get("results", [])
else:
return f"error: Unexpected response format"
if not results:
return f"No libraries found for: {query}"
# Format results
output = f"Found {len(results)} libraries:\n\n"
for lib in results[:5]: # Top 5
output += f"• {lib.get('title', 'Unknown')}\n"
output += f" ID: {lib.get('id', 'N/A')}\n"
if lib.get('description'):
output += f" {lib['description']}\n"
output += f" Snippets: {lib.get('totalSnippets', 0)}\n"
output += f" Trust Score: {lib.get('trustScore', 0)}\n"
if lib.get('versions'):
output += f" Versions: {', '.join(lib['versions'][:3])}\n"
output += "\n"
# Suggest using the top result
if results:
top_lib = results[0]
lib_id = top_lib.get('id', '')
output += f"To get documentation:\ncontext7_docs({{\"library_id\": \"{lib_id}\", \"topic\": \"your topic\"}})"
return output
@register_tool(
"context7_docs",
"Get documentation for a library using its Context7 ID",
{"library_id": "string", "topic": "string?", "tokens": "number?"}
)
def context7_docs(args):
"""Get library documentation from Context7"""
library_id = args.get("library_id", "")
topic = args.get("topic", "")
tokens = args.get("tokens", 5000)
if not library_id:
return "error: library_id required (use context7_search first)"
# Ensure minimum tokens
if tokens < 1000:
tokens = 1000
params = {
"tokens": tokens,
"type": "txt"
}
if topic:
params["topic"] = topic
result = context7_get(f"/v1{library_id}", params)
if isinstance(result, dict) and "error" in result:
return f"error: {result['error']}"
if not result or result == "null":
return f"No documentation found for {library_id}. Try using context7_search first."
return result
| 1 | #!/usr/bin/env python3 |
| 2 | """Context7 extension - Query up-to-date documentation for any programming library""" |
| 3 | |
| 4 | import json |
| 5 | import urllib.request |
| 6 | import urllib.parse |
| 7 | import ssl |
| 8 | import os |
| 9 | |
| 10 | CONTEXT7_API = "https://context7.com/api" |
| 11 | API_KEY = os.environ.get("CONTEXT7_API_KEY") # Optional, for higher rate limits |
| 12 | |
| 13 | def context7_get(endpoint, params=None): |
| 14 | """Make GET request to Context7 API""" |
| 15 | try: |
| 16 | url = f"{CONTEXT7_API}{endpoint}" |
| 17 | if params: |
| 18 | url += "?" + urllib.parse.urlencode(params) |
| 19 | |
| 20 | headers = { |
| 21 | "X-Context7-Source": "mcp", |
| 22 | "Accept": "application/json" |
| 23 | } |
| 24 | |
| 25 | # Add API key if available |
| 26 | if API_KEY: |
| 27 | headers["Authorization"] = f"Bearer {API_KEY}" |
| 28 | |
| 29 | # Create SSL context for mitmproxy compatibility |
| 30 | ssl_context = ssl.create_default_context() |
| 31 | ssl_context.check_hostname = False |
| 32 | ssl_context.verify_mode = ssl.CERT_NONE |
| 33 | |
| 34 | req = urllib.request.Request(url, headers=headers) |
| 35 | resp = urllib.request.urlopen(req, timeout=30, context=ssl_context) |
| 36 | |
| 37 | content_type = resp.headers.get("Content-Type", "") |
| 38 | if "application/json" in content_type: |
| 39 | return json.loads(resp.read().decode()) |
| 40 | else: |
| 41 | return resp.read().decode() |
| 42 | except Exception as e: |
| 43 | return {"error": f"{type(e).__name__}: {e}"} |
| 44 | |
| 45 | @register_tool( |
| 46 | "context7_search", |
| 47 | "Search for libraries by name (e.g., 'react', 'express', 'django')", |
| 48 | {"query": "string"} |
| 49 | ) |
| 50 | def context7_search(args): |
| 51 | """Search for libraries in Context7""" |
| 52 | query = args.get("query", "") |
| 53 | if not query: |
| 54 | return "error: query required" |
| 55 | |
| 56 | result = context7_get("/v1/search", {"query": query}) |
| 57 | |
| 58 | if isinstance(result, dict) and "error" in result: |
| 59 | return f"error: {result['error']}" |
| 60 | |
| 61 | if isinstance(result, dict): |
| 62 | results = result.get("results", []) |
| 63 | else: |
| 64 | return f"error: Unexpected response format" |
| 65 | |
| 66 | if not results: |
| 67 | return f"No libraries found for: {query}" |
| 68 | |
| 69 | # Format results |
| 70 | output = f"Found {len(results)} libraries:\n\n" |
| 71 | for lib in results[:5]: # Top 5 |
| 72 | output += f"• {lib.get('title', 'Unknown')}\n" |
| 73 | output += f" ID: {lib.get('id', 'N/A')}\n" |
| 74 | if lib.get('description'): |
| 75 | output += f" {lib['description']}\n" |
| 76 | output += f" Snippets: {lib.get('totalSnippets', 0)}\n" |
| 77 | output += f" Trust Score: {lib.get('trustScore', 0)}\n" |
| 78 | if lib.get('versions'): |
| 79 | output += f" Versions: {', '.join(lib['versions'][:3])}\n" |
| 80 | output += "\n" |
| 81 | |
| 82 | # Suggest using the top result |
| 83 | if results: |
| 84 | top_lib = results[0] |
| 85 | lib_id = top_lib.get('id', '') |
| 86 | output += f"To get documentation:\ncontext7_docs({{\"library_id\": \"{lib_id}\", \"topic\": \"your topic\"}})" |
| 87 | |
| 88 | return output |
| 89 | |
| 90 | @register_tool( |
| 91 | "context7_docs", |
| 92 | "Get documentation for a library using its Context7 ID", |
| 93 | {"library_id": "string", "topic": "string?", "tokens": "number?"} |
| 94 | ) |
| 95 | def context7_docs(args): |
| 96 | """Get library documentation from Context7""" |
| 97 | library_id = args.get("library_id", "") |
| 98 | topic = args.get("topic", "") |
| 99 | tokens = args.get("tokens", 5000) |
| 100 | |
| 101 | if not library_id: |
| 102 | return "error: library_id required (use context7_search first)" |
| 103 | |
| 104 | # Ensure minimum tokens |
| 105 | if tokens < 1000: |
| 106 | tokens = 1000 |
| 107 | |
| 108 | params = { |
| 109 | "tokens": tokens, |
| 110 | "type": "txt" |
| 111 | } |
| 112 | if topic: |
| 113 | params["topic"] = topic |
| 114 | |
| 115 | result = context7_get(f"/v1{library_id}", params) |
| 116 | |
| 117 | if isinstance(result, dict) and "error" in result: |
| 118 | return f"error: {result['error']}" |
| 119 | |
| 120 | if not result or result == "null": |
| 121 | return f"No documentation found for {library_id}. Try using context7_search first." |
| 122 | |
| 123 | return result |
| 124 |