Last active 1 month ago

Query up-to-date, version-specific documentation for any programming library. Search for libraries and get relevant code examples and documentation.

Revision 139a9609b5034298bdee060f6d0411b654013534

Context7.py Raw
1#!/usr/bin/env python3
2"""Context7 extension - Query up-to-date documentation for any programming library"""
3
4import json
5import urllib.request
6import urllib.parse
7import ssl
8import os
9
10CONTEXT7_API = "https://context7.com/api"
11API_KEY = os.environ.get("CONTEXT7_API_KEY") # Optional, for higher rate limits
12
13def 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)
50def 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)
95def 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