diff --git a/.config/opencode/opencode.jsonc b/.config/opencode/opencode.jsonc index 0ea6f3e..5818593 100644 --- a/.config/opencode/opencode.jsonc +++ b/.config/opencode/opencode.jsonc @@ -1,116 +1,117 @@ { - "$schema": "https://opencode.ai/config.json", - "permission": { - "bash": { - "*": "ask", - "git log *": "allow", - "git commit *": "ask", - "git status *": "allow", - "git diff *": "allow", - "git show *": "allow", - "git push *": "ask", - "sudo *": "deny", - "curl *": "ask", - "wget *": "ask", - "diff *": "allow", - "npm *": "allow", - "rm -rf *": "deny", - "rm *": "ask", - "grep *": "allow", - "sort *": "allow", - "head *": "allow", - "tail *": "allow", - "ls *": "allow", - "wc *": "allow", - "go build *": "allow", - "go test *": "allow", - "go vet *": "allow", - "go mod tidy": "allow", - "go mod download": "allow", - "dotnet *": "allow", - "docker ps *": "allow", - "docker logs *": "allow", - "docker compose logs *": "allow", - }, - "read": { - ".env": "deny", - ".secrets": "deny", - "*.key": "deny", - "*.pem": "deny", - "id_rsa": "deny", - "id_ed25519": "deny", - ".aws/credentials": "deny", - "opencode.jsonc": "deny", - "~/.ssh/known_hosts": "allow", - }, - "grep": "allow", - "glob": "allow", - "question": "allow", - "lsp": "allow", - "websearch": "allow", + "$schema": "https://opencode.ai/config.json", + "permission": { + "bash": { + "*": "ask", + "git log *": "allow", + "git commit *": "ask", + "git status *": "allow", + "git diff *": "allow", + "git show *": "allow", + "git push *": "ask", + "sudo *": "deny", + "curl *": "ask", + "wget *": "ask", + "diff *": "allow", + "npm *": "allow", + "rm -rf *": "deny", + "rm *": "ask", + "grep *": "allow", + "sort *": "allow", + "head *": "allow", + "tail *": "allow", + "echo *": "allow", + "ls *": "allow", + "wc *": "allow", + "go build *": "allow", + "go test *": "allow", + "go vet *": "allow", + "go mod tidy": "allow", + "go mod download": "allow", + "dotnet *": "allow", + "docker ps *": "allow", + "docker logs *": "allow", + "docker compose logs *": "allow", }, - "mcp": { - "zai-mcp-server": { - "type": "local", - "command": ["npx", "-y", "@z_ai/mcp-server"], - "environment": { - "Z_AI_API_KEY": "{env:ZAI_API_KEY}", - "Z_AI_MODE": "ZAI", - }, - }, - "web-search-prime": { - "type": "remote", - "url": "https://api.z.ai/api/mcp/web_search_prime/mcp", - "headers": { - "Authorization": "Bearer {env:ZAI_API_KEY}", - }, - }, - "web-reader": { - "type": "remote", - "url": "https://api.z.ai/api/mcp/web_reader/mcp", - "headers": { - "Authorization": "Bearer {env:ZAI_API_KEY}", - }, - }, - "zread": { - "type": "remote", - "url": "https://api.z.ai/api/mcp/zread/mcp", - "headers": { - "Authorization": "Bearer {env:ZAI_API_KEY}", - }, - }, - "ibmi-server": { - "type": "local", - "command": [ - "npx", - "-y", - "@ibm/ibmi-mcp-server@latest", - "--tools", - "/home/colem/mcp/ibmi-mcp-server/tools/custom-tools.yaml", - ], - "enabled": true, - "environment": { - "DB2i_HOST": "{env:DB2i_HOST}", - "DB2i_USER": "{env:DB2i_USER}", - "DB2i_PASS": "{env:DB2i_PASS}", - "DB2i_PORT": "8076", - "DB2i_IGNORE_UNAUTHORIZED": "true", - }, - }, + "read": { + ".env": "deny", + ".secrets": "deny", + "*.key": "deny", + "*.pem": "deny", + "id_rsa": "deny", + "id_ed25519": "deny", + ".aws/credentials": "deny", + "opencode.jsonc": "deny", + "~/.ssh/known_hosts": "allow", }, - "lsp": true, - "formatter": true, - "model": "zai-coding-plan/glm-5.2", - "small_model": "zai-coding-plan/glm-4.5-air", - "agent": { - "explore": { - "model": "zai-coding-plan/glm-4.5-air", - }, - "scout": { - "model": "zai-coding-plan/glm-4.5-air", - }, + "grep": "allow", + "glob": "allow", + "question": "allow", + "lsp": "allow", + "websearch": "allow", + }, + "mcp": { + "zai-mcp-server": { + "type": "local", + "command": ["npx", "-y", "@z_ai/mcp-server"], + "environment": { + "Z_AI_API_KEY": "{env:ZAI_API_KEY}", + "Z_AI_MODE": "ZAI", + }, }, - "share": "disabled", - "snapshot": true, - "plugin": ["opencode-glm-quota"], + "web-search-prime": { + "type": "remote", + "url": "https://api.z.ai/api/mcp/web_search_prime/mcp", + "headers": { + "Authorization": "Bearer {env:ZAI_API_KEY}", + }, + }, + "web-reader": { + "type": "remote", + "url": "https://api.z.ai/api/mcp/web_reader/mcp", + "headers": { + "Authorization": "Bearer {env:ZAI_API_KEY}", + }, + }, + "zread": { + "type": "remote", + "url": "https://api.z.ai/api/mcp/zread/mcp", + "headers": { + "Authorization": "Bearer {env:ZAI_API_KEY}", + }, + }, + "ibmi-server": { + "type": "local", + "command": [ + "npx", + "-y", + "@ibm/ibmi-mcp-server@latest", + "--tools", + "/home/colem/mcp/ibmi-mcp-server/tools/custom-tools.yaml", + ], + "enabled": false, + "environment": { + "DB2i_HOST": "{env:DB2i_HOST}", + "DB2i_USER": "{env:DB2i_USER}", + "DB2i_PASS": "{env:DB2i_PASS}", + "DB2i_PORT": "8076", + "DB2i_IGNORE_UNAUTHORIZED": "true", + }, + }, + }, + "lsp": true, + "formatter": true, + "model": "zai-coding-plan/glm-5.2", + "small_model": "zai-coding-plan/glm-4.5-air", + "agent": { + "explore": { + "model": "zai-coding-plan/glm-4.5-air", + }, + "scout": { + "model": "zai-coding-plan/glm-4.5-air", + }, + }, + "share": "disabled", + "snapshot": true, + "plugin": ["opencode-glm-quota", "./plugin/ibmi-gate.ts"], } diff --git a/.config/opencode/plugin/ibmi-gate.ts b/.config/opencode/plugin/ibmi-gate.ts new file mode 100644 index 0000000..84e9851 --- /dev/null +++ b/.config/opencode/plugin/ibmi-gate.ts @@ -0,0 +1,35 @@ +/* +Gate the ibmi-mcp-server on its required environment variables. + +opencode's config schema types `mcp..enabled` as a static boolean and +only interpolates `{env:VAR}` for string values (headers, environment +entries), never for the `enabled` flag. Conditionally enabling a server is +therefore not expressible in opencode.jsonc alone. + +This plugin uses the `config` hook, which runs once at startup with the live +merged config (before MCP servers spawn), to flip `ibmi-server.enabled` to +`true` only when every required credential is present. The config keeps +`enabled: false` as a fallback default, so the server stays off if this +plugin fails to load. + */ + +import type { Plugin } from "@opencode-ai/plugin"; + +// Credentials the ibmi-server MCP needs to connect. All must be non-empty. +const REQUIRED_ENV = ["DB2i_HOST", "DB2i_USER", "DB2i_PASS"] as const; +const SERVER = "ibmi-server"; + +export default (async () => { + return { + config: (cfg) => { + const ready = REQUIRED_ENV.every((name) => { + const value = process.env[name]; + return typeof value === "string" && value.length > 0; + }); + const server = cfg.mcp?.[SERVER]; + if (server && typeof server === "object") { + (server as { enabled?: boolean }).enabled = ready; + } + }, + }; +}) satisfies Plugin;