95 lines
3.3 KiB
Python
95 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Context7 API client for searching libraries and fetching documentation.
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import sys
|
|
import urllib.request
|
|
import urllib.parse
|
|
import urllib.error
|
|
import os
|
|
|
|
API_BASE = "https://context7.com/api/v2"
|
|
API_KEY = os.environ.get("CONTEXT7_API_KEY", "ctx7sk-d6069954-149e-4a74-ae8f-85092cbfcd6f")
|
|
|
|
|
|
def make_request(url: str) -> dict | str:
|
|
"""Make an authenticated request to Context7 API."""
|
|
headers = {"Authorization": f"Bearer {API_KEY}"}
|
|
req = urllib.request.Request(url, headers=headers)
|
|
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=30) as response:
|
|
content_type = response.headers.get("Content-Type", "")
|
|
data = response.read().decode("utf-8")
|
|
if "application/json" in content_type:
|
|
return json.loads(data)
|
|
return data
|
|
except urllib.error.HTTPError as e:
|
|
return {"error": f"HTTP {e.code}: {e.reason}", "body": e.read().decode("utf-8")}
|
|
except urllib.error.URLError as e:
|
|
return {"error": f"URL Error: {e.reason}"}
|
|
|
|
|
|
def search_libraries(library_name: str, query: str = "") -> dict:
|
|
"""Search for libraries by name and optional query."""
|
|
params = {"libraryName": library_name}
|
|
if query:
|
|
params["query"] = query
|
|
|
|
url = f"{API_BASE}/libs/search?{urllib.parse.urlencode(params)}"
|
|
return make_request(url)
|
|
|
|
|
|
def get_context(library_id: str, query: str, output_type: str = "txt", tokens: int = None) -> str | dict:
|
|
"""Fetch documentation context for a specific library."""
|
|
params = {
|
|
"libraryId": library_id,
|
|
"query": query,
|
|
"type": output_type
|
|
}
|
|
if tokens:
|
|
params["tokens"] = tokens
|
|
|
|
url = f"{API_BASE}/context?{urllib.parse.urlencode(params)}"
|
|
return make_request(url)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Context7 API client")
|
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
|
|
# Search command
|
|
search_parser = subparsers.add_parser("search", help="Search for libraries")
|
|
search_parser.add_argument("library_name", help="Library name to search for (e.g., 'react', 'next.js')")
|
|
search_parser.add_argument("--query", "-q", default="", help="Optional query to filter results")
|
|
|
|
# Context command
|
|
context_parser = subparsers.add_parser("context", help="Get documentation context")
|
|
context_parser.add_argument("library_id", help="Library ID from search results (e.g., '/vercel/next.js')")
|
|
context_parser.add_argument("query", help="Query describing what you need (e.g., 'setup ssr')")
|
|
context_parser.add_argument("--type", "-t", default="txt", choices=["txt", "json"], help="Output format (txt returns markdown-formatted text)")
|
|
context_parser.add_argument("--tokens", type=int, help="Max tokens to return")
|
|
|
|
args = parser.parse_args()
|
|
|
|
if args.command == "search":
|
|
result = search_libraries(args.library_name, args.query)
|
|
if isinstance(result, dict):
|
|
print(json.dumps(result, indent=2))
|
|
else:
|
|
print(result)
|
|
|
|
elif args.command == "context":
|
|
result = get_context(args.library_id, args.query, args.type, args.tokens)
|
|
if isinstance(result, dict):
|
|
print(json.dumps(result, indent=2))
|
|
else:
|
|
print(result)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|