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,
|
"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:
|
if servers:
|
||||||
config["mcp"] = servers
|
config["mcp"] = servers
|
||||||
|
|
||||||
@@ -110,14 +118,17 @@ def main() -> int:
|
|||||||
|
|
||||||
home = Path(os.environ.get("HOME", "/home/developer"))
|
home = Path(os.environ.get("HOME", "/home/developer"))
|
||||||
config_dir = home / ".config" / "opencode"
|
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
|
# CRITICAL: never overwrite an existing config. Users may have
|
||||||
# bind-mounted their host config directory, or their config may be
|
# bind-mounted their host config directory, or their config may be
|
||||||
# persisted in a named volume from a previous run.
|
# 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(
|
print(
|
||||||
f"Existing opencode.json found at {config_file} — "
|
f"Existing config found at {existing} — "
|
||||||
"skipping generation.",
|
"skipping generation.",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
@@ -140,8 +151,23 @@ def main() -> int:
|
|||||||
added = register_mcp_servers(config)
|
added = register_mcp_servers(config)
|
||||||
|
|
||||||
config_dir.mkdir(parents=True, exist_ok=True)
|
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:
|
with config_file.open("w") as f:
|
||||||
json.dump(config, f, indent=2)
|
f.write(content)
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
|
|
||||||
if added:
|
if added:
|
||||||
|
|||||||
+11
-7
@@ -160,11 +160,11 @@ else
|
|||||||
fi
|
fi
|
||||||
rm -f "$tmpout"
|
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
|
# expected shape. The script's log message goes to stderr (line 1 of
|
||||||
# generate-config.py uses file=sys.stderr) so capturing only stdout
|
# generate-config.py uses file=sys.stderr) so capturing only stdout
|
||||||
# gives us clean JSON.
|
# gives us clean JSONC. We strip // comments before validating JSON.
|
||||||
label="generate-config produces valid opencode.json"
|
label="generate-config produces valid opencode.jsonc"
|
||||||
tmp=$(mktemp -d)
|
tmp=$(mktemp -d)
|
||||||
if docker run --rm \
|
if docker run --rm \
|
||||||
-e OPENCODE_PROVIDER=anthropic \
|
-e OPENCODE_PROVIDER=anthropic \
|
||||||
@@ -173,24 +173,28 @@ if docker run --rm \
|
|||||||
"$IMAGE" sh -c '
|
"$IMAGE" sh -c '
|
||||||
mkdir -p /tmp/home
|
mkdir -p /tmp/home
|
||||||
python3 /usr/local/lib/opencode-devbox/generate-config.py 2>/dev/null
|
python3 /usr/local/lib/opencode-devbox/generate-config.py 2>/dev/null
|
||||||
cat /tmp/home/.config/opencode/opencode.json
|
cat /tmp/home/.config/opencode/opencode.jsonc
|
||||||
' > "$tmp/out.json" 2>/dev/null; then
|
' > "$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 "
|
if python3 -c "
|
||||||
import json, sys
|
import json, sys
|
||||||
c = json.load(open('$tmp/out.json'))
|
c = json.load(open('$tmp/out.json'))
|
||||||
assert c['model'].startswith('anthropic/'), c
|
assert c['model'].startswith('anthropic/'), c
|
||||||
assert c['autoupdate'] is False
|
assert c['autoupdate'] is False
|
||||||
assert c['share'] == 'disabled'
|
assert c['share'] == 'disabled'
|
||||||
|
assert 'context7' in c.get('mcp', {}), 'context7 MCP not registered'
|
||||||
" 2>&1; then
|
" 2>&1; then
|
||||||
pass "$label"
|
pass "$label"
|
||||||
else
|
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
|
fi
|
||||||
else
|
else
|
||||||
fail "$label: container failed: $(cat "$tmp/out.json")"
|
fail "$label: container failed: $(cat "$tmp/out.jsonc")"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Config generation is idempotent — running twice must not overwrite.
|
# Config generation is idempotent — running twice must not overwrite.
|
||||||
|
# Tests both legacy .json and new .jsonc detection.
|
||||||
label="generate-config never overwrites existing config"
|
label="generate-config never overwrites existing config"
|
||||||
if docker run --rm \
|
if docker run --rm \
|
||||||
-e OPENCODE_PROVIDER=anthropic \
|
-e OPENCODE_PROVIDER=anthropic \
|
||||||
|
|||||||
Reference in New Issue
Block a user