Add Context7 remote MCP server to auto-generated config
Context7 provides up-to-date library documentation for LLMs via a remote endpoint — no local binary needed. Always registered since it has no PATH dependency. Also switches generated config from .json to .jsonc so we can include a comment about the optional API key for higher rate limits. The existing-config check now detects both file extensions.
This commit is contained in:
@@ -96,6 +96,14 @@ def register_mcp_servers(config: dict) -> list[str]:
|
||||
"enabled": False,
|
||||
}
|
||||
|
||||
# Context7 — up-to-date library documentation for LLMs (remote).
|
||||
# Free tier works without an API key; set CONTEXT7_API_KEY for higher
|
||||
# rate limits. No local binary needed — purely a remote MCP endpoint.
|
||||
servers["context7"] = {
|
||||
"type": "remote",
|
||||
"url": "https://mcp.context7.com/mcp",
|
||||
}
|
||||
|
||||
if servers:
|
||||
config["mcp"] = servers
|
||||
|
||||
@@ -110,14 +118,17 @@ def main() -> int:
|
||||
|
||||
home = Path(os.environ.get("HOME", "/home/developer"))
|
||||
config_dir = home / ".config" / "opencode"
|
||||
config_file = config_dir / "opencode.json"
|
||||
config_file = config_dir / "opencode.jsonc"
|
||||
config_file_legacy = config_dir / "opencode.json"
|
||||
|
||||
# CRITICAL: never overwrite an existing config. Users may have
|
||||
# bind-mounted their host config directory, or their config may be
|
||||
# persisted in a named volume from a previous run.
|
||||
if config_file.exists():
|
||||
# Check both .json and .jsonc variants.
|
||||
if config_file.exists() or config_file_legacy.exists():
|
||||
existing = config_file if config_file.exists() else config_file_legacy
|
||||
print(
|
||||
f"Existing opencode.json found at {config_file} — "
|
||||
f"Existing config found at {existing} — "
|
||||
"skipping generation.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
@@ -140,8 +151,23 @@ def main() -> int:
|
||||
added = register_mcp_servers(config)
|
||||
|
||||
config_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Write as JSONC so we can include helpful comments.
|
||||
content = json.dumps(config, indent=2)
|
||||
|
||||
# Insert a comment about Context7 API key after the context7 url line.
|
||||
context7_comment = (
|
||||
' "url": "https://mcp.context7.com/mcp"\n'
|
||||
" // For higher rate limits, sign up at https://context7.com/dashboard\n"
|
||||
' // and add: "headers": { "CONTEXT7_API_KEY": "{env:CONTEXT7_API_KEY}" }'
|
||||
)
|
||||
content = content.replace(
|
||||
' "url": "https://mcp.context7.com/mcp"',
|
||||
context7_comment,
|
||||
)
|
||||
|
||||
with config_file.open("w") as f:
|
||||
json.dump(config, f, indent=2)
|
||||
f.write(content)
|
||||
f.write("\n")
|
||||
|
||||
if added:
|
||||
|
||||
+11
-7
@@ -160,11 +160,11 @@ else
|
||||
fi
|
||||
rm -f "$tmpout"
|
||||
|
||||
# Config generation with anthropic provider writes valid JSON with the
|
||||
# Config generation with anthropic provider writes valid JSONC with the
|
||||
# expected shape. The script's log message goes to stderr (line 1 of
|
||||
# generate-config.py uses file=sys.stderr) so capturing only stdout
|
||||
# gives us clean JSON.
|
||||
label="generate-config produces valid opencode.json"
|
||||
# gives us clean JSONC. We strip // comments before validating JSON.
|
||||
label="generate-config produces valid opencode.jsonc"
|
||||
tmp=$(mktemp -d)
|
||||
if docker run --rm \
|
||||
-e OPENCODE_PROVIDER=anthropic \
|
||||
@@ -173,24 +173,28 @@ if docker run --rm \
|
||||
"$IMAGE" sh -c '
|
||||
mkdir -p /tmp/home
|
||||
python3 /usr/local/lib/opencode-devbox/generate-config.py 2>/dev/null
|
||||
cat /tmp/home/.config/opencode/opencode.json
|
||||
' > "$tmp/out.json" 2>/dev/null; then
|
||||
cat /tmp/home/.config/opencode/opencode.jsonc
|
||||
' > "$tmp/out.jsonc" 2>/dev/null; then
|
||||
# Strip single-line // comments for JSON validation
|
||||
sed 's|//.*$||' "$tmp/out.jsonc" > "$tmp/out.json"
|
||||
if python3 -c "
|
||||
import json, sys
|
||||
c = json.load(open('$tmp/out.json'))
|
||||
assert c['model'].startswith('anthropic/'), c
|
||||
assert c['autoupdate'] is False
|
||||
assert c['share'] == 'disabled'
|
||||
assert 'context7' in c.get('mcp', {}), 'context7 MCP not registered'
|
||||
" 2>&1; then
|
||||
pass "$label"
|
||||
else
|
||||
fail "$label: output doesn't match expected shape: $(cat "$tmp/out.json")"
|
||||
fail "$label: output doesn't match expected shape: $(cat "$tmp/out.jsonc")"
|
||||
fi
|
||||
else
|
||||
fail "$label: container failed: $(cat "$tmp/out.json")"
|
||||
fail "$label: container failed: $(cat "$tmp/out.jsonc")"
|
||||
fi
|
||||
|
||||
# Config generation is idempotent — running twice must not overwrite.
|
||||
# Tests both legacy .json and new .jsonc detection.
|
||||
label="generate-config never overwrites existing config"
|
||||
if docker run --rm \
|
||||
-e OPENCODE_PROVIDER=anthropic \
|
||||
|
||||
Reference in New Issue
Block a user