feat(agent): add external MCP client support

Reuse OpenCode 2 HTTP/stdio clients with per-session read-only grants,
native approval and sanitized owner-scoped MCP status. Narrow the
environment inherited by trusted local MCP processes.

Validated in an isolated checkout with bun run check and 20 focused tests.
This commit is contained in:
2026-10-08 18:36:07 +08:00
parent 7383ed6d13
commit 75d8d526f9
19 changed files with 728 additions and 4 deletions
+14
View File
@@ -10,6 +10,7 @@ import { Plugin, Rpc } from "@opencode/plugin";
import { z } from "zod";
import { resolve } from "node:path";
import { registerSessionSandbox } from "../session-sandbox.js";
import { registerMcpPolicy } from "../mcp-policy.js";
import add_task_human_evidence from "../tools/add_task_human_evidence.js";
import clear_scada_analysis from "../tools/clear_scada_analysis.js";
@@ -99,6 +100,7 @@ export default Plugin.define({
},
});
});
const mcpReport = await registerMcpPolicy(ctx);
await ctx.rpc.register(Rpc.define({
id: "tjwater.tools",
methods: {
@@ -106,9 +108,21 @@ export default Plugin.define({
input: z.object({}),
output: z.array(z.object({ id: z.string(), direct: z.boolean(), sandboxed: z.boolean(), workspaceRoot: z.string().optional() })),
},
mcp: {
input: z.object({}),
output: z.object({
configuration_errors: z.array(z.string()),
servers: z.array(z.object({
name: z.string(), type: z.enum(["local", "remote"]),
status: z.enum(["connected", "pending", "disabled", "failed", "needs_auth"]),
configured_protocol: z.enum(["legacy", "auto", "2026-07-28"]), tools: z.array(z.string()),
})),
}),
},
},
events: {},
}), {
mcp: mcpReport,
list: async () => (await ctx.tool.list()).map((tool) => ({
id: tool.id,
direct: tool.options?.codemode === false,