vantage-peers-mcp 2.17.0 → 2.18.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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.18.0] — 2026-08-11
4
+
5
+ ### Changed
6
+ - **Expose only the CORE tool surface (mission `vp-mcp-alias-cleanup-v1`, S8).** A data-driven allowlist (`mcp-server/tool-exposure.json`, `{"core":[...]}`) masks every tool whose `T2_verdict` is not `CORE` in `analysis/vantagepeers/vp-restructuring/vp-by-tool-day158.csv`. Masking ≠ deletion: non-CORE tools stay registered + handler-wired and are `disable()`d so `tools/list` does not advertise them; reverting = removing a line from the data file. Exposed = CORE ∩ registered = **66** tools; masked = **43** (registered 109). The exposure list is derived from the CSV, never typed; a name in the file matching no registered tool makes the server refuse to start, naming it. Applied at the single shared `registerTools` surface, so both stdio and HTTP transports (online clients) inherit the reduced surface. 5 CORE names from the day158 CSV are #1169-removed aliases whose CORE survivors stay exposed (zero capability lost). Tests: `test/tool-exposure.test.ts` 2/2, full suite 1071 passed / 0 failed, tsc 0.
7
+
3
8
  ## [2.17.0] — 2026-08-11
4
9
 
5
10
  ### Removed
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![MCP tools: 109+](https://img.shields.io/badge/MCP_tools-109+-green)]()
7
7
 
8
8
  > **Package:** `vantage-peers-mcp` (plain — NOT `@vantageos/vantage-peers-mcp`)
9
- > **Current version:** `2.17.0` (alias-cleanup release — 14 duplicate alias tools removed, fleet-used survivor of each pair kept)
9
+ > **Current version:** `2.18.0` (S8 CORE tool-exposure filter release — server now advertises only 66 CORE tools of 109 registered; masking is data-driven (`tool-exposure.json`) and reversible, non-CORE tools stay registered/handler-wired but are not listed)
10
10
  > **License:** FSL-1.1-Apache-2.0
11
11
  > **Repo:** https://github.com/vantageos-agency/vantage-peers (full monorepo README at `/README.md`)
12
12
  > **Docs:** https://vantagepeers.com/docs
package/dist/server.js CHANGED
@@ -57,7 +57,7 @@ const convexUrl = loadConvexUrl();
57
57
  const convex = new ConvexHttpClient(convexUrl);
58
58
  const server = new McpServer({
59
59
  name: "vantage-peers",
60
- version: "2.17.0",
60
+ version: "2.18.0",
61
61
  });
62
62
  // stdio transport has no OAuth identity → pass oauthCtx=undefined to opt into
63
63
  // the legacy bearer / system-scope code path inside registerTools (same path
package/dist/src/tools.js CHANGED
@@ -9,6 +9,9 @@
9
9
  */
10
10
  import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
11
11
  import { LEGACY_WILDCARD_CTX, scopeFilterGet, scopeFilterList, } from "@vantageos/cloud-identity";
12
+ import { readFileSync } from "node:fs";
13
+ import { dirname, resolve } from "node:path";
14
+ import { fileURLToPath } from "node:url";
12
15
  import { z } from "zod";
13
16
  import { checkFromAllowed, checkNamespaceRead, checkNamespaceWrite, isMasterScope, } from "./auth.js";
14
17
  import { FreshStateGuardError, guardFreshState } from "./fresh-state-guard.js";
@@ -1232,7 +1235,88 @@ export const validateTaskPayloadOutputSchema = z.object({
1232
1235
  // ─────────────────────────────────────────────────────────────────────────────
1233
1236
  // Main export: register all tools against a server + convex client pair
1234
1237
  // ─────────────────────────────────────────────────────────────────────────────
1238
+ // ─────────────────────────────────────────────────────────────────────────────
1239
+ // Tool exposure filter — DATA-DRIVEN CORE allowlist (S8, mission
1240
+ // vp-mcp-alias-cleanup-v1). Only tool names listed in `core` inside
1241
+ // tool-exposure.json are registered/advertised to clients; every other tool
1242
+ // stays fully present in the code + DB, just not exposed. The list lives as
1243
+ // DATA, never a code constant — reverting = editing tool-exposure.json.
1244
+ // VP_TOOL_EXPOSURE_PATH overrides the path for tests only.
1245
+ //
1246
+ // Derived from analysis/vantagepeers/vp-restructuring/vp-by-tool-day158.csv
1247
+ // (outil column where T2_verdict == "CORE"), intersected with the
1248
+ // actually-registered tool-name set — see tool-exposure.json's own header.
1249
+ // ─────────────────────────────────────────────────────────────────────────────
1250
+ function resolveToolExposurePath() {
1251
+ if (process.env.VP_TOOL_EXPOSURE_PATH) {
1252
+ return resolve(process.env.VP_TOOL_EXPOSURE_PATH);
1253
+ }
1254
+ // This module resolves at "<mcp-server>/src/tools.ts" when run from source
1255
+ // (bun run server.ts) and at "<mcp-server>/dist/src/tools.js" when run from
1256
+ // the tsc build (dist/server.js) — tool-exposure.json always lives at the
1257
+ // mcp-server package root, one directory further up in the built case.
1258
+ const here = dirname(fileURLToPath(import.meta.url));
1259
+ const fromSource = resolve(here, "../tool-exposure.json");
1260
+ const fromDist = resolve(here, "../../tool-exposure.json");
1261
+ try {
1262
+ readFileSync(fromSource, "utf-8");
1263
+ return fromSource;
1264
+ }
1265
+ catch {
1266
+ return fromDist;
1267
+ }
1268
+ }
1269
+ function loadCoreToolNames() {
1270
+ const path = resolveToolExposurePath();
1271
+ let raw;
1272
+ try {
1273
+ raw = readFileSync(path, "utf-8");
1274
+ }
1275
+ catch (err) {
1276
+ throw new Error(`tool-exposure: failed to read ${path}: ${err.message}`);
1277
+ }
1278
+ let parsed;
1279
+ try {
1280
+ parsed = JSON.parse(raw);
1281
+ }
1282
+ catch (err) {
1283
+ throw new Error(`tool-exposure: invalid JSON in ${path}: ${err.message}`);
1284
+ }
1285
+ const core = parsed?.core;
1286
+ if (!Array.isArray(core) || !core.every((n) => typeof n === "string")) {
1287
+ throw new Error(`tool-exposure: "core" must be an array of strings in ${path}`);
1288
+ }
1289
+ return core;
1290
+ }
1235
1291
  export function registerTools(server, convex, oauthCtx) {
1292
+ // Intercept EVERY server.tool(...) call made below (directly or through
1293
+ // defineTool()/registerExportOkfBundle()/registerImportOkfBundle()/
1294
+ // registerKbIngestTools()/registerValidateOkfBundle() — they all receive
1295
+ // this same `server` instance) so only CORE names are actually advertised.
1296
+ //
1297
+ // The registration itself always goes through — the tool stays fully
1298
+ // present in the code + handler wiring (masking ≠ deletion; a non-CORE
1299
+ // tool's handler is still reachable in-process, e.g. by existing unit
1300
+ // tests that capture registrations against a stub McpServer). What is
1301
+ // masked is client-facing ADVERTISEMENT: a non-CORE tool is immediately
1302
+ // `.disable()`d on its returned RegisteredTool, which is the MCP SDK's own
1303
+ // mechanism for keeping a tool out of `tools/list` (server/mcp.js filters
1304
+ // `tool.enabled` when building that list) while refusing `tools/call` on
1305
+ // it with an explicit "Tool <name> disabled" error — never a silent
1306
+ // unregistration.
1307
+ const coreToolNames = new Set(loadCoreToolNames());
1308
+ const allRegisteredNames = new Set();
1309
+ const realTool = server.tool.bind(server);
1310
+ // biome-ignore lint/suspicious/noExplicitAny: narrowing the overloaded McpServer#tool signature for interception.
1311
+ server.tool = (name, ...rest) => {
1312
+ allRegisteredNames.add(name);
1313
+ // @ts-expect-error — forwarding to the real overloaded signature.
1314
+ const registered = realTool(name, ...rest);
1315
+ if (!coreToolNames.has(name) && registered && typeof registered.disable === "function") {
1316
+ registered.disable();
1317
+ }
1318
+ return registered;
1319
+ };
1236
1320
  // ── scope guards (no-op when oauthCtx is undefined — legacy bearer path) ────
1237
1321
  const guardFrom = (from) => {
1238
1322
  const err = checkFromAllowed(oauthCtx, from);
@@ -7216,4 +7300,8 @@ export function registerTools(server, convex, oauthCtx) {
7216
7300
  return mcpConvexError(error);
7217
7301
  }
7218
7302
  });
7303
+ const missingCoreNames = [...coreToolNames].filter((name) => !allRegisteredNames.has(name));
7304
+ if (missingCoreNames.length > 0) {
7305
+ throw new Error(`tool-exposure: core name(s) not found among registered tools: ${missingCoreNames.join(", ")}`);
7306
+ }
7219
7307
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vantage-peers-mcp",
3
- "version": "2.17.0",
3
+ "version": "2.18.0",
4
4
  "description": "MCP server for VantagePeers — shared memory, messaging, and task coordination for AI agent teams",
5
5
  "type": "module",
6
6
  "main": "./dist/server.js",
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "files": [
18
18
  "dist/",
19
+ "tool-exposure.json",
19
20
  "README.md",
20
21
  "CHANGELOG.md"
21
22
  ],
@@ -0,0 +1,70 @@
1
+ {
2
+ "core": [
3
+ "add_task_dependency",
4
+ "billing_summary_by_project",
5
+ "block_task",
6
+ "bulk_complete_tasks",
7
+ "check_messages",
8
+ "checkout_task",
9
+ "complete_task",
10
+ "create_briefing_note",
11
+ "create_mission",
12
+ "create_recurring_task",
13
+ "create_task",
14
+ "delete_message",
15
+ "delete_recurring_task",
16
+ "delete_task",
17
+ "export_okf_bundle",
18
+ "get_briefing_note",
19
+ "get_diary",
20
+ "get_episode",
21
+ "get_memory",
22
+ "get_message",
23
+ "get_mission",
24
+ "get_mission_template",
25
+ "get_profile",
26
+ "get_recurring_task",
27
+ "get_task",
28
+ "hybrid_search",
29
+ "import_okf_bundle",
30
+ "improvisation_digest",
31
+ "instantiate_template_into_mission",
32
+ "list_briefing_notes",
33
+ "list_broadcast_status",
34
+ "list_diaries",
35
+ "list_episodes",
36
+ "list_memories",
37
+ "list_messages",
38
+ "list_missions",
39
+ "list_peers",
40
+ "list_recurring_tasks",
41
+ "list_tasks",
42
+ "list_tasks_by_mission",
43
+ "mark_as_read",
44
+ "pause_recurring_task",
45
+ "recall",
46
+ "resume_recurring_task",
47
+ "search_briefing_notes_by_keyword",
48
+ "search_episodes_by_keyword",
49
+ "search_episodes_by_semantic",
50
+ "search_messages_by_keyword",
51
+ "search_tasks_by_keyword",
52
+ "send_message",
53
+ "set_summary",
54
+ "soft_delete_document",
55
+ "soft_delete_memory",
56
+ "start_task",
57
+ "store_document_chunked",
58
+ "store_episode",
59
+ "store_memory",
60
+ "text_search",
61
+ "update_briefing_note",
62
+ "update_mission",
63
+ "update_mission_status",
64
+ "update_mission_template",
65
+ "update_profile",
66
+ "update_recurring_task",
67
+ "update_task",
68
+ "write_diary"
69
+ ]
70
+ }