Add: gate ibmi-server MCP on required env vars

opencode's config schema types mcp.<name>.enabled as a static boolean and only interpolates {env:VAR} for string values, so conditional enablement is not expressible in opencode.jsonc alone.

Add a config-hook plugin that flips ibmi-server.enabled to true only when DB2i_HOST, DB2i_USER, and DB2i_PASS are all set. The config retains enabled: false as a fallback if the plugin fails to load.
This commit is contained in:
2026-07-29 20:39:24 -05:00
parent bfb6dcd4df
commit 28582d56c0
2 changed files with 147 additions and 111 deletions
+112 -111
View File
@@ -1,116 +1,117 @@
{ {
"$schema": "https://opencode.ai/config.json", "$schema": "https://opencode.ai/config.json",
"permission": { "permission": {
"bash": { "bash": {
"*": "ask", "*": "ask",
"git log *": "allow", "git log *": "allow",
"git commit *": "ask", "git commit *": "ask",
"git status *": "allow", "git status *": "allow",
"git diff *": "allow", "git diff *": "allow",
"git show *": "allow", "git show *": "allow",
"git push *": "ask", "git push *": "ask",
"sudo *": "deny", "sudo *": "deny",
"curl *": "ask", "curl *": "ask",
"wget *": "ask", "wget *": "ask",
"diff *": "allow", "diff *": "allow",
"npm *": "allow", "npm *": "allow",
"rm -rf *": "deny", "rm -rf *": "deny",
"rm *": "ask", "rm *": "ask",
"grep *": "allow", "grep *": "allow",
"sort *": "allow", "sort *": "allow",
"head *": "allow", "head *": "allow",
"tail *": "allow", "tail *": "allow",
"ls *": "allow", "echo *": "allow",
"wc *": "allow", "ls *": "allow",
"go build *": "allow", "wc *": "allow",
"go test *": "allow", "go build *": "allow",
"go vet *": "allow", "go test *": "allow",
"go mod tidy": "allow", "go vet *": "allow",
"go mod download": "allow", "go mod tidy": "allow",
"dotnet *": "allow", "go mod download": "allow",
"docker ps *": "allow", "dotnet *": "allow",
"docker logs *": "allow", "docker ps *": "allow",
"docker compose logs *": "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",
}, },
"mcp": { "read": {
"zai-mcp-server": { ".env": "deny",
"type": "local", ".secrets": "deny",
"command": ["npx", "-y", "@z_ai/mcp-server"], "*.key": "deny",
"environment": { "*.pem": "deny",
"Z_AI_API_KEY": "{env:ZAI_API_KEY}", "id_rsa": "deny",
"Z_AI_MODE": "ZAI", "id_ed25519": "deny",
}, ".aws/credentials": "deny",
}, "opencode.jsonc": "deny",
"web-search-prime": { "~/.ssh/known_hosts": "allow",
"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",
},
},
}, },
"lsp": true, "grep": "allow",
"formatter": true, "glob": "allow",
"model": "zai-coding-plan/glm-5.2", "question": "allow",
"small_model": "zai-coding-plan/glm-4.5-air", "lsp": "allow",
"agent": { "websearch": "allow",
"explore": { },
"model": "zai-coding-plan/glm-4.5-air", "mcp": {
}, "zai-mcp-server": {
"scout": { "type": "local",
"model": "zai-coding-plan/glm-4.5-air", "command": ["npx", "-y", "@z_ai/mcp-server"],
}, "environment": {
"Z_AI_API_KEY": "{env:ZAI_API_KEY}",
"Z_AI_MODE": "ZAI",
},
}, },
"share": "disabled", "web-search-prime": {
"snapshot": true, "type": "remote",
"plugin": ["opencode-glm-quota"], "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"],
} }
+35
View File
@@ -0,0 +1,35 @@
/*
Gate the ibmi-mcp-server on its required environment variables.
opencode's config schema types `mcp.<name>.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;