added context7 connector to extra tools

This commit is contained in:
Luxferre
2026-08-17 10:00:33 +03:00
parent b298c53406
commit e9dd44d97b
2 changed files with 232 additions and 0 deletions
+33
View File
@@ -247,6 +247,39 @@ Options include `-l/--location`, `-u/--units` (`m`/`u`/`M`), `-L/--lang`,
`https://wttr.in`) and `WEATHER_TIMEOUT` (default `20`s). `https://wttr.in`) and `WEATHER_TIMEOUT` (default `20`s).
Dependencies: `curl`, `jq` (only required for the JSON format). Dependencies: `curl`, `jq` (only required for the JSON format).
Dependencies: `curl`, `jq` (only required for the JSON format).
### `extras/context7`
A documentation lookup tool for Bantam backed by the
[Context7 public MCP server](https://mcp.context7.com/mcp). It speaks the same
JSON-RPC MCP protocol as `websearch` (`initialize` ->
`notifications/initialized` -> `tools/call`) and prints the returned text.
Context7 keeps up-to-date docs and code examples for thousands of libraries and
exposes two tools, both wrapped here:
- `resolve-library-id` — maps a library name to a Context7 ID
(`/org/project`).
- `query-docs` — fetches documentation and code examples for a resolved ID.
Usage:
```sh
extras/context7 resolve "React" "hooks" # list candidate library IDs
extras/context7 query "/reactjs/react.dev" "useEffect cleanup" # fetch docs
extras/context7 docs "Express" "middleware error handling" # resolve + query
extras/context7 --help
```
The `docs` subcommand is a convenience that resolves the library, auto-selects
the top-ranked match, and immediately queries it (the chosen ID is printed to
stderr so stdout stays clean for piping). Environment overrides:
`CONTEXT7_MCP_ENDPOINT` (default `https://mcp.context7.com/mcp`) and
`CONTEXT7_API_KEY` (optional; sent as the `X-Context7-API-Key` header for
higher rate limits / private docs).
Dependencies: `curl`, `jq`.
## FAQ ## FAQ
+199
View File
@@ -0,0 +1,199 @@
#!/bin/sh
# context7 - query library/framework documentation via the Context7 public
# MCP server (https://mcp.context7.com/mcp).
#
# Context7 keeps up-to-date docs and code examples for thousands of libraries
# and frameworks and exposes them through an MCP (Model Context Protocol)
# server. This script speaks the JSON-RPC MCP protocol over HTTP:
# 1. initialize -> obtains an Mcp-Session-Id (optional)
# 2. notifications/initialized -> no reply
# 3. tools/call -> runs a Context7 tool and prints text
#
# Context7 provides two tools:
# * resolve-library-id maps a library name to a Context7 ID (/org/project).
# * query-docs fetches docs + examples for a resolved library ID.
#
# Usage:
# context7 resolve <libraryName> [query]
# Search for a library and print the candidate Context7 IDs with their
# description, snippet count, reputation and benchmark score.
#
# context7 query <libraryId> <query>
# Fetch documentation for an explicit library ID (format /org/project
# or /org/project/version). The ID usually comes from `resolve`.
#
# context7 docs <libraryName> <query>
# Convenience: resolve the library, auto-pick the top-ranked match and
# immediately query its documentation.
#
# context7 --help
#
# Environment:
# CONTEXT7_MCP_ENDPOINT optional; default https://mcp.context7.com/mcp
# CONTEXT7_API_KEY optional; if set, sent as the X-Context7-API-Key
# request header (for higher rate limits / private
# docs). No extra headers are added when unset.
#
# Dependencies: curl, jq.
set -eu
usage() {
sed -n '/^# Usage:/,/^# Dependencies:/p' "$0" | sed 's/^# \{0,1\}//'
}
if [ "$#" -lt 1 ]; then
usage >&2
exit 1
fi
CMD="$1"
shift
case "$CMD" in
-h|--help|help)
usage
exit 0
;;
resolve|query|docs)
;;
*)
echo "context7: unknown command '$CMD' (try --help)" >&2
exit 1
;;
esac
# --- Validate argument counts per command. ---
case "$CMD" in
resolve)
if [ "$#" -lt 1 ]; then
echo "context7: resolve requires <libraryName> [query]" >&2
exit 1
fi
LIB_NAME="$1"
QUERY="${2:-$1}"
;;
query)
if [ "$#" -lt 2 ]; then
echo "context7: query requires <libraryId> <query>" >&2
exit 1
fi
LIB_ID="$1"
QUERY="$2"
;;
docs)
if [ "$#" -lt 2 ]; then
echo "context7: docs requires <libraryName> <query>" >&2
exit 1
fi
LIB_NAME="$1"
QUERY="$2"
;;
esac
ENDPOINT="${CONTEXT7_MCP_ENDPOINT:-https://mcp.context7.com/mcp}"
# Optional auth header (single token, no spaces expected in an API key).
AUTH_ARGS=""
if [ -n "${CONTEXT7_API_KEY:-}" ]; then
AUTH_ARGS="-H X-Context7-API-Key:${CONTEXT7_API_KEY}"
fi
# --- MCP handshake + tool call helper. ---
# Args: $1 = tool name, $2 = arguments JSON. Prints the parsed text content.
mcp_call() {
TOOL="$1"
ARGS="$2"
INIT_HEADERS=$(mktemp)
INIT_BODY=$(mktemp)
curl -s -D "$INIT_HEADERS" -X POST "$ENDPOINT" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
$AUTH_ARGS \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"context7","version":"1.0"}}}' \
> "$INIT_BODY"
SID=$(grep -i '^mcp-session-id:' "$INIT_HEADERS" | tr -d '\r' | awk '{print $2}')
if [ -n "$SID" ]; then
curl -s -X POST "$ENDPOINT" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Mcp-Session-Id: $SID" \
$AUTH_ARGS \
-d '{"jsonrpc":"2.0","method":"notifications/initialized"}' > /dev/null
SID_ARGS="-H Mcp-Session-Id:$SID"
else
SID_ARGS=""
fi
CALL_BODY=$(mktemp)
curl -s -X POST "$ENDPOINT" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
$SID_ARGS \
$AUTH_ARGS \
-d "{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/call\",\"params\":{\"name\":\"$TOOL\",\"arguments\":$ARGS}}" \
> "$CALL_BODY"
# Take the last "data:" SSE payload (or the whole body if plain JSON).
DATA=$(grep -E '^data:[[:space:]]' "$CALL_BODY" | tail -n1 | sed 's/^data:[[:space:]]*//')
if [ -z "$DATA" ]; then
DATA=$(cat "$CALL_BODY")
fi
rm -f "$INIT_HEADERS" "$INIT_BODY" "$CALL_BODY"
if [ -z "$DATA" ]; then
echo "context7: no response from MCP server" >&2
return 1
fi
# Surface protocol errors.
ERROR_MSG=$(printf '%s' "$DATA" | jq -r 'if .error then (.error|tostring) else empty end')
if [ -n "$ERROR_MSG" ]; then
echo "context7 error: $ERROR_MSG" >&2
return 1
fi
printf '%s' "$DATA" | jq -r '
(.result.content // [])
| map(select(.type == "text"))
| map(.text)
| .[]
'
}
# --- Dispatch. ---
case "$CMD" in
resolve)
ARGS=$(jq -n --arg n "$LIB_NAME" --arg q "$QUERY" \
'{libraryName:$n, query:$q}')
mcp_call "resolve-library-id" "$ARGS"
;;
query)
ARGS=$(jq -n --arg id "$LIB_ID" --arg q "$QUERY" \
'{libraryId:$id, query:$q}')
mcp_call "query-docs" "$ARGS"
;;
docs)
RARGS=$(jq -n --arg n "$LIB_NAME" --arg q "$QUERY" \
'{libraryName:$n, query:$q}')
RESOLVE_OUT=$(mcp_call "resolve-library-id" "$RARGS") || exit 1
# Auto-pick the first candidate library ID from the resolve output.
TOP_ID=$(printf '%s\n' "$RESOLVE_OUT" \
| grep -m1 -oE '/[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+' || true)
if [ -z "$TOP_ID" ]; then
echo "context7: could not extract a library ID from resolve output:" >&2
printf '%s\n' "$RESOLVE_OUT" >&2
exit 1
fi
echo "context7: using library ID $TOP_ID (from resolve of '$LIB_NAME')" >&2
QARGS=$(jq -n --arg id "$TOP_ID" --arg q "$QUERY" \
'{libraryId:$id, query:$q}')
mcp_call "query-docs" "$QARGS"
;;
esac
exit 0