@botbotgo/agent-harness 0.0.161 → 0.0.162

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -911,3 +911,4 @@ ACP transport notes:
911
911
  - `serveAcpStdio(runtime)` exposes newline-delimited JSON-RPC over stdio for local IDE, CLI, or subprocess clients.
912
912
  - `serveAcpHttp(runtime)` exposes JSON-RPC over HTTP plus SSE runtime events so remote operator surfaces can connect without importing the runtime in-process.
913
913
  - `exportRunPackage(...)` and `exportSessionPackage(...)` package stable runtime records, transcript, approvals, events, and artifacts for operator tooling without reaching into persistence internals.
914
+ - `runtime/default.governance.remoteMcp` can now deny or allow specific MCP servers, raise approval requirements by transport, and stamp transport-based risk tiers into runtime governance bundles.
package/README.zh.md CHANGED
@@ -870,3 +870,4 @@ ACP transport 说明:
870
870
  - `serveAcpStdio(runtime)` 提供基于 stdio 的 newline-delimited JSON-RPC,适合本地 IDE、CLI 或子进程客户端。
871
871
  - `serveAcpHttp(runtime)` 提供基于 HTTP 的 JSON-RPC 与 SSE runtime events,适合远程 operator surface 或独立控制面接入。
872
872
  - `exportRunPackage(...)` 与 `exportSessionPackage(...)` 可把稳定 runtime 记录、transcript、approvals、events 和 artifacts 打包给 operator tooling,而不必直接访问 persistence 内部实现。
873
+ - `runtime/default.governance.remoteMcp` 现在可以按 MCP server 或 transport 做 allow/deny、审批升级,并把 transport 风险等级写进 runtime governance bundles。
@@ -102,6 +102,8 @@ export type RuntimeGovernanceToolPolicy = {
102
102
  toolId: string;
103
103
  toolType: string;
104
104
  category: "local" | "backend" | "mcp" | "provider-native";
105
+ mcpServerRef?: string;
106
+ mcpTransport?: string;
105
107
  risk: RuntimeGovernanceRiskLevel;
106
108
  requiresApproval: boolean;
107
109
  approvalPolicy: "explicit-hitl" | "runtime-default" | "none";
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.160";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.161";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.160";
1
+ export const AGENT_HARNESS_VERSION = "0.0.161";
@@ -57,6 +57,25 @@ function readRisk(value) {
57
57
  function readApprovalPolicy(value) {
58
58
  return value === "explicit-hitl" || value === "runtime-default" || value === "none" ? value : undefined;
59
59
  }
60
+ function normalizeServerRef(value) {
61
+ if (typeof value !== "string" || value.trim().length === 0) {
62
+ return undefined;
63
+ }
64
+ const trimmed = value.trim();
65
+ return trimmed.startsWith("mcp/") ? trimmed : `mcp/${trimmed}`;
66
+ }
67
+ function readRemoteMcpMetadata(tool) {
68
+ const config = asObject(tool.config);
69
+ const mcpReference = asObject(config?.mcp);
70
+ const inlineServer = asObject(config?.mcpServer);
71
+ const transport = typeof inlineServer?.transport === "string" && inlineServer.transport.trim().length > 0
72
+ ? inlineServer.transport.trim()
73
+ : undefined;
74
+ return {
75
+ ...(normalizeServerRef(mcpReference?.serverRef) ? { serverRef: normalizeServerRef(mcpReference?.serverRef) } : {}),
76
+ ...(transport ? { transport } : {}),
77
+ };
78
+ }
60
79
  function matchesToolPolicy(rule, policy) {
61
80
  const match = asObject(rule.match) ?? rule;
62
81
  const toolName = typeof match.toolName === "string" ? match.toolName.trim() : undefined;
@@ -102,14 +121,49 @@ function applyGovernanceOverrides(binding, policies) {
102
121
  return merged;
103
122
  });
104
123
  }
124
+ function applyRemoteMcpGovernance(binding, policies) {
125
+ const governance = asObject(binding.harnessRuntime.governance);
126
+ const remoteMcp = asObject(governance?.remoteMcp);
127
+ if (!remoteMcp) {
128
+ return policies;
129
+ }
130
+ const requireApprovalTransports = new Set(readStringArray(remoteMcp.requireApprovalTransports));
131
+ const riskByTransport = asObject(remoteMcp.riskByTransport);
132
+ const inputRiskHintsByTransport = asObject(remoteMcp.inputRiskHintsByTransport);
133
+ return policies.map((policy) => {
134
+ if (policy.category !== "mcp") {
135
+ return policy;
136
+ }
137
+ const merged = { ...policy };
138
+ const transport = merged.mcpTransport;
139
+ if (transport && requireApprovalTransports.has(transport)) {
140
+ merged.requiresApproval = true;
141
+ if (merged.approvalPolicy === "none") {
142
+ merged.approvalPolicy = "runtime-default";
143
+ }
144
+ }
145
+ const transportRisk = transport ? readRisk(riskByTransport?.[transport]) : undefined;
146
+ if (transportRisk) {
147
+ merged.risk = transportRisk;
148
+ }
149
+ const transportHints = transport ? readStringArray(inputRiskHintsByTransport?.[transport]) : [];
150
+ if (transportHints.length > 0) {
151
+ merged.inputRiskHints = Array.from(new Set([...merged.inputRiskHints, ...transportHints]));
152
+ }
153
+ return merged;
154
+ });
155
+ }
105
156
  export function buildRuntimeGovernanceBundles(binding) {
106
- const toolPolicies = applyGovernanceOverrides(binding, getBindingPrimaryTools(binding).map((tool) => {
157
+ const toolPolicies = applyGovernanceOverrides(binding, applyRemoteMcpGovernance(binding, getBindingPrimaryTools(binding).map((tool) => {
107
158
  const requiresApproval = toolRequiresRuntimeApproval(tool);
159
+ const remoteMcp = readRemoteMcpMetadata(tool);
108
160
  return {
109
161
  toolName: tool.name,
110
162
  toolId: tool.id,
111
163
  toolType: tool.type,
112
164
  category: toCategory(tool.type),
165
+ ...(remoteMcp.serverRef ? { mcpServerRef: remoteMcp.serverRef } : {}),
166
+ ...(remoteMcp.transport ? { mcpTransport: remoteMcp.transport } : {}),
113
167
  risk: classifyRisk({
114
168
  toolType: tool.type,
115
169
  requiresApproval,
@@ -122,7 +176,7 @@ export function buildRuntimeGovernanceBundles(binding) {
122
176
  hasInputSchema: typeof tool.inputSchemaRef === "string" && tool.inputSchemaRef.trim().length > 0,
123
177
  inputRiskHints: inputHints(binding, tool),
124
178
  };
125
- }));
179
+ })));
126
180
  if (toolPolicies.length === 0) {
127
181
  return [];
128
182
  }
@@ -12,6 +12,9 @@ export class PolicyEngine {
12
12
  const governance = typeof binding.harnessRuntime.governance === "object" && binding.harnessRuntime.governance
13
13
  ? binding.harnessRuntime.governance
14
14
  : undefined;
15
+ const remoteMcp = typeof governance?.remoteMcp === "object" && governance.remoteMcp
16
+ ? governance.remoteMcp
17
+ : undefined;
15
18
  const denyConfig = typeof governance?.deny === "object" && governance.deny
16
19
  ? governance.deny
17
20
  : undefined;
@@ -38,6 +41,72 @@ export class PolicyEngine {
38
41
  reasons.push(`runtime governance denied tool access: ${blocked.map((tool) => tool.name).join(", ")}`);
39
42
  }
40
43
  }
44
+ if (remoteMcp) {
45
+ const normalizeServerRef = (value) => {
46
+ if (typeof value !== "string" || value.trim().length === 0) {
47
+ return undefined;
48
+ }
49
+ const trimmed = value.trim();
50
+ return trimmed.startsWith("mcp/") ? trimmed : `mcp/${trimmed}`;
51
+ };
52
+ const allowServerRefs = new Set(Array.isArray(remoteMcp.allowServerRefs)
53
+ ? remoteMcp.allowServerRefs
54
+ .map((item) => normalizeServerRef(item))
55
+ .filter((item) => Boolean(item))
56
+ : []);
57
+ const denyServerRefs = new Set(Array.isArray(remoteMcp.denyServerRefs)
58
+ ? remoteMcp.denyServerRefs
59
+ .map((item) => normalizeServerRef(item))
60
+ .filter((item) => Boolean(item))
61
+ : []);
62
+ const denyTransports = new Set(Array.isArray(remoteMcp.denyTransports)
63
+ ? remoteMcp.denyTransports.filter((item) => typeof item === "string" && item.trim().length > 0).map((item) => item.trim())
64
+ : []);
65
+ const tools = binding.execution?.params?.tools ?? binding.langchainAgentParams?.tools ?? binding.deepAgentParams?.tools ?? [];
66
+ const deniedRemoteTools = tools.flatMap((tool) => {
67
+ if (tool.type !== "mcp") {
68
+ return [];
69
+ }
70
+ const config = typeof tool.config === "object" && tool.config && !Array.isArray(tool.config)
71
+ ? tool.config
72
+ : undefined;
73
+ const mcpRef = typeof config?.mcp === "object" && config.mcp && !Array.isArray(config.mcp)
74
+ ? config.mcp
75
+ : undefined;
76
+ const inlineMcpServer = typeof config?.mcpServer === "object" && config.mcpServer && !Array.isArray(config.mcpServer)
77
+ ? config.mcpServer
78
+ : undefined;
79
+ const serverRef = normalizeServerRef(mcpRef?.serverRef);
80
+ const transport = typeof inlineMcpServer?.transport === "string" && inlineMcpServer.transport.trim().length > 0
81
+ ? inlineMcpServer.transport.trim()
82
+ : undefined;
83
+ const serverDenied = serverRef ? denyServerRefs.has(serverRef) || (allowServerRefs.size > 0 && !allowServerRefs.has(serverRef)) : false;
84
+ const transportDenied = transport ? denyTransports.has(transport) : false;
85
+ return serverDenied || transportDenied
86
+ ? [{
87
+ toolName: tool.name,
88
+ ...(serverRef ? { serverRef } : {}),
89
+ ...(transport ? { transport } : {}),
90
+ }]
91
+ : [];
92
+ });
93
+ if (deniedRemoteTools.length > 0) {
94
+ allowed = false;
95
+ const details = deniedRemoteTools.map((tool) => {
96
+ if (tool.serverRef && tool.transport) {
97
+ return `${tool.toolName} (${tool.serverRef}, ${tool.transport})`;
98
+ }
99
+ if (tool.serverRef) {
100
+ return `${tool.toolName} (${tool.serverRef})`;
101
+ }
102
+ if (tool.transport) {
103
+ return `${tool.toolName} (${tool.transport})`;
104
+ }
105
+ return tool.toolName;
106
+ });
107
+ reasons.push(`runtime governance denied remote MCP access: ${details.join(", ")}`);
108
+ }
109
+ }
41
110
  for (const evaluator of getPolicyEvaluators()) {
42
111
  const decision = evaluator.evaluate(binding);
43
112
  if (!decision) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.161",
3
+ "version": "0.0.162",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",