@myvillage/cli 1.65.0 → 1.66.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myvillage/cli",
3
- "version": "1.65.0",
3
+ "version": "1.66.0",
4
4
  "description": "MyVillageOS CLI for community developers",
5
5
  "type": "module",
6
6
  "bin": {
@@ -156,7 +156,12 @@ export async function getMCPTools(agentDir, agentConfig) {
156
156
  }
157
157
 
158
158
  activeClients.push(client);
159
- const tools = await client.tools();
159
+ // Per-server tool allowlist (least privilege). When `allowed_tools` is a
160
+ // non-empty array in tools.yaml, expose ONLY those tools to the agent and
161
+ // withhold everything else this server offers (e.g. grant `network_feed`
162
+ // but not `wallet_send`). Absent/empty → all tools, the backward-compatible
163
+ // default.
164
+ const tools = applyToolAllowlist(await client.tools(), server.allowed_tools, name);
160
165
  const toolNames = Object.keys(tools);
161
166
  Object.assign(allTools, tools);
162
167
  servers.push({
@@ -182,6 +187,29 @@ export async function getMCPTools(agentDir, agentConfig) {
182
187
  return { tools: allTools, servers };
183
188
  }
184
189
 
190
+ /**
191
+ * Restrict a server's tools to an allowlist. `allowed` is the `allowed_tools`
192
+ * array from a tools.yaml server entry; when it's missing or empty, all tools
193
+ * pass through unchanged (backward-compatible). Allowlisted names that the
194
+ * server doesn't actually expose are logged so a typo or a removed/renamed tool
195
+ * is visible rather than silently dropping the scope to nothing.
196
+ */
197
+ export function applyToolAllowlist(tools, allowed, serverName) {
198
+ if (!Array.isArray(allowed) || allowed.length === 0) return tools;
199
+ const allow = new Set(allowed);
200
+ const filtered = {};
201
+ for (const [name, tool] of Object.entries(tools)) {
202
+ if (allow.has(name)) filtered[name] = tool;
203
+ }
204
+ const unknown = allowed.filter((n) => !(n in tools));
205
+ if (unknown.length > 0) {
206
+ console.error(
207
+ `[mcp-client] ${serverName}: allowed_tools lists ${unknown.length} tool(s) this server doesn't expose: ${unknown.join(', ')}`,
208
+ );
209
+ }
210
+ return filtered;
211
+ }
212
+
185
213
  export async function cleanupMCPClients() {
186
214
  for (const client of activeClients) {
187
215
  try { await client.close(); } catch { /* ignore */ }