@vantageos/vantage-registry-mcp 1.4.0 → 1.6.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/README.md CHANGED
@@ -53,7 +53,7 @@ Or set `CONVEX_URL` in `.env.local` at the repo root.
53
53
 
54
54
  | Tool | Description |
55
55
  |------|-------------|
56
- | `upsert_skill` | Create or update a skill (upsert by name) |
56
+ | `upsert_skill` | Create or update a skill (upsert by name). **New in 1.6.0:** optional `vrBody` writes the full SKILL.md body (vrContent/hash/version) in the same call |
57
57
  | `list_skills` | List skills — optional `status` / `team` / `category` filters |
58
58
  | `list_skills_by_team` | List skills for a specific team |
59
59
  | `list_skills_by_category` | List skills for a specific category |
@@ -71,7 +71,7 @@ Or set `CONVEX_URL` in `.env.local` at the repo root.
71
71
 
72
72
  | Tool | Description |
73
73
  |------|-------------|
74
- | `upsert_hook` | Create or update a hook (upsert by name) |
74
+ | `upsert_hook` | Create or update a hook (upsert by name). **New in 1.6.0:** optional `tests` array of hook test file paths |
75
75
  | `list_hooks` | List hooks — optional `status` / `event` / `scope` filters |
76
76
  | `get_hook` | Get a hook by Convex document ID |
77
77
 
@@ -113,6 +113,7 @@ list_templates(team="core", template_type="checklist")
113
113
  | `list_runbooks_by_category` | List runbooks for a specific category (uses byCategory index) |
114
114
  | `list_runbooks_by_team` | List runbooks for a specific team (uses byTeam index) |
115
115
  | `delete_runbook` | Soft-delete a runbook — sets status to `deprecated` |
116
+ | `detect_runbook_drift` | Return per-runbook `vrHash` (sha256 of content) + category + version for drift comparison (added in 1.6.0) |
116
117
 
117
118
  **Runbook status values:** `draft | published | deprecated`
118
119
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vantageos/vantage-registry-mcp",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "MCP server exposing VantageRegistry Convex functions as Claude Code tools",
5
5
  "type": "module",
6
6
  "bin": {
package/server.js CHANGED
@@ -56,7 +56,7 @@ var hookEventSchema = z.enum([
56
56
  "UserPromptSubmit"
57
57
  ]).describe("Hook lifecycle event");
58
58
  var runbookStatusSchema = z.enum(["draft", "published", "deprecated"]).describe("Runbook status");
59
- var templateTypeSchema = z.enum(["mission", "document", "checklist"]).describe("Template type");
59
+ var templateTypeSchema = z.enum(["standard", "mission", "brief", "runbook", "document", "checklist"]).describe("Template type");
60
60
  var linkTypeSchema = z.enum(["uses", "produces", "references"]).describe(
61
61
  "Link type \u2014 uses: template consumed during execution, produces: output document generated, references: informational cross-reference"
62
62
  );
@@ -64,7 +64,7 @@ var convexUrl = loadConvexUrl();
64
64
  var convex = new ConvexHttpClient(convexUrl);
65
65
  var server = new McpServer({
66
66
  name: "vantage-registry",
67
- version: "1.4.0"
67
+ version: "1.5.0"
68
68
  });
69
69
  server.tool(
70
70
  "upsert_team",
@@ -206,7 +206,7 @@ server.tool(
206
206
  );
207
207
  server.tool(
208
208
  "upsert_skill",
209
- "Create or update a skill in VantageRegistry. Upserts by name+team \u2014 if a skill with the same name and team exists, it is updated.",
209
+ "Create or update a skill in VantageRegistry. Upserts by name+team \u2014 if a skill with the same name and team exists, it is updated. Pass vrBody to write the full SKILL.md body (vrContent/vrContentHash/vrContentVersion) in the SAME call \u2014 no separate upsert_skill_content needed (that tool remains for back-compat). Omit vrBody for metadata-only upsert.",
210
210
  {
211
211
  name: z.string().describe("Skill name \u2014 e.g. 'social-post', 'competitor-watch'"),
212
212
  team: z.string().describe("Team this skill belongs to"),
@@ -222,7 +222,10 @@ server.tool(
222
222
  price: z.number().optional().describe("Price in EUR (if paid)"),
223
223
  license: z.string().optional().describe("License \u2014 e.g. 'MIT', 'proprietary'"),
224
224
  publisherId: z.string().optional().describe("Publisher identifier"),
225
- categories: z.array(z.string()).optional().describe("Categories beyond team name")
225
+ categories: z.array(z.string()).optional().describe("Categories beyond team name"),
226
+ vrBody: z.string().optional().describe(
227
+ "Full SKILL.md body. When passed, writes vrContent/vrContentHash/vrContentVersion in the same call. Omitted \u2192 metadata-only (unchanged)."
228
+ )
226
229
  },
227
230
  async (args) => {
228
231
  const id = await convex.mutation(api.skills.upsert, args);
@@ -666,7 +669,10 @@ server.tool(
666
669
  filePath: z.string().describe("File path relative to project root"),
667
670
  registered: z.boolean().describe("Whether this hook is registered in settings.json"),
668
671
  status: hookStatusSchema,
669
- version: z.string().optional().describe("Semantic version \u2014 e.g. '1.0.0'")
672
+ version: z.string().optional().describe("Semantic version \u2014 e.g. '1.0.0'"),
673
+ tests: z.array(z.string()).optional().describe(
674
+ "Hook test file paths \u2014 e.g. ['hooks/tests/check-pii.test.py']. Omitted \u2192 not sent (back-compat)."
675
+ )
670
676
  },
671
677
  async (args) => {
672
678
  const id = await convex.mutation(api.hooks.upsert, args);
@@ -770,13 +776,20 @@ server.tool(
770
776
  );
771
777
  server.tool(
772
778
  "upsert_template",
773
- "Create or update a template in VantageRegistry. Upserts by name.",
779
+ "Create or update a template in VantageRegistry. Upserts by name. contentHash is auto-computed server-side (sha256 of content) when omitted.",
774
780
  {
775
781
  name: z.string().describe("Template name \u2014 e.g. 'agent-brief', 'skill-md'"),
776
782
  team: z.string().optional().describe("Team this template belongs to \u2014 defaults to 'global'"),
777
783
  purpose: z.string().describe("What this template is used for"),
778
784
  content: z.string().describe("Full template content"),
779
- version: z.string().optional().describe("Semantic version \u2014 e.g. '1.0.0'")
785
+ version: z.string().optional().describe("Semantic version \u2014 e.g. '1.0.0'"),
786
+ template_type: templateTypeSchema.optional().describe(
787
+ "Template type: standard, mission, brief, runbook, document, or checklist"
788
+ ),
789
+ category: z.string().optional().describe("Category \u2014 e.g. 'standards/fleet-shared'"),
790
+ contentHash: z.string().optional().describe("sha256 of content \u2014 auto-computed server-side when omitted"),
791
+ sourceCommit: z.string().optional().describe("Git commit SHA the template content was sourced from"),
792
+ sourceRepo: z.string().optional().describe("Git repo the template content was sourced from")
780
793
  },
781
794
  async (args) => {
782
795
  const id = await convex.mutation(api.templates.upsert, args);
@@ -820,6 +833,36 @@ server.tool(
820
833
  };
821
834
  }
822
835
  );
836
+ server.tool(
837
+ "detect_template_drift",
838
+ "Return the VR-side sha256 contentHash + provenance (sourceCommit, sourceRepo) for each template in scope. IMPORTANT: Convex queries have no filesystem access. This tool returns the VR canonical contentHash so the caller can read the source file, compute its SHA256, and compare. name=<slug> filters to a single template; omit name to return every template.",
839
+ {
840
+ name: z.string().optional().describe("Template slug \u2014 filter to a single template")
841
+ },
842
+ async ({ name }) => {
843
+ const result = await convex.query(api.templates.detectTemplateDrift, {
844
+ name
845
+ });
846
+ return {
847
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
848
+ };
849
+ }
850
+ );
851
+ server.tool(
852
+ "list_templates_by_category",
853
+ "List templates for a specific category using the byCategory index.",
854
+ {
855
+ category: z.string().describe("Category to filter by \u2014 e.g. 'standards/fleet-shared'")
856
+ },
857
+ async ({ category }) => {
858
+ const result = await convex.query(api.templates.listTemplatesByCategory, {
859
+ category
860
+ });
861
+ return {
862
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
863
+ };
864
+ }
865
+ );
823
866
  server.tool(
824
867
  "upsert_test_run",
825
868
  "Insert a skill quality test run and update denormalized quality fields on the parent skill (lastTestedAt, lastReviewerScore, lastEvalDelta, testStatus). reviewerScore format is 'X/Y' e.g. '37/45'. evalDelta is pp delta vs without-skill baseline.",
@@ -1224,6 +1267,21 @@ server.tool(
1224
1267
  }
1225
1268
  }
1226
1269
  );
1270
+ server.tool(
1271
+ "detect_runbook_drift",
1272
+ "Return the VR-side sha256 hash (vrHash) plus category and version for each runbook in scope. IMPORTANT: Convex queries have no filesystem access. This tool computes sha256(runbook.content) server-side and returns it as vrHash so the caller can compute disk SHA256 and compare. name=<slug> filters to a single runbook; omit name to return every runbook (bounded to 500).",
1273
+ {
1274
+ name: z.string().optional().describe("Runbook slug \u2014 filter to a single runbook")
1275
+ },
1276
+ async ({ name }) => {
1277
+ const result = await convex.query(api.runbooks.detectRunbookDrift, {
1278
+ name
1279
+ });
1280
+ return {
1281
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
1282
+ };
1283
+ }
1284
+ );
1227
1285
  server.tool(
1228
1286
  "link_runbook_template",
1229
1287
  "Create or update a typed link between a runbook and a template in the runbook_template_links junction table. Upserts by (runbookId, templateId) pair \u2014 at-most-one link per pair. linkType controls the relationship semantics: 'uses' = template consumed during execution, 'produces' = output document generated by this runbook, 'references' = informational cross-reference. Returns {linkId, created: true} on insert or {linkId, created: false} on update.",
@@ -1400,7 +1458,16 @@ server.tool(
1400
1458
  }
1401
1459
  }
1402
1460
  );
1403
- var componentKindSchema = z.enum(["skill", "agent", "hook", "plugin", "prompt", "runbook", "template"]).describe("Component kind");
1461
+ var componentKindSchema = z.enum([
1462
+ "skill",
1463
+ "agent",
1464
+ "hook",
1465
+ "plugin",
1466
+ "prompt",
1467
+ "runbook",
1468
+ "template",
1469
+ "ui-component"
1470
+ ]).describe("Component kind");
1404
1471
  var componentStatusSchema = z.enum(["active", "deprecated", "experimental"]).describe("Component status");
1405
1472
  server.tool(
1406
1473
  "register_component",
package/server.ts CHANGED
@@ -10,8 +10,8 @@
10
10
  * upsert_plugin, list_plugins, get_plugin
11
11
  * upsert_hook, list_hooks, get_hook
12
12
  * upsert_prompt, list_prompts, get_prompt
13
- * upsert_template, list_templates, get_template
14
- * upsert_runbook, get_runbook, list_runbooks, list_runbooks_by_category, list_runbooks_by_team, delete_runbook
13
+ * upsert_template, list_templates, get_template, detect_template_drift, list_templates_by_category
14
+ * upsert_runbook, get_runbook, list_runbooks, list_runbooks_by_category, list_runbooks_by_team, delete_runbook, detect_runbook_drift
15
15
  * link_runbook_template, unlink_runbook_template, list_templates_for_runbook, list_runbooks_for_template
16
16
  * register_component, list_components, get_component, search_components, update_component, delete_component
17
17
  * get_stats
@@ -112,7 +112,7 @@ const runbookStatusSchema = z
112
112
  .describe("Runbook status");
113
113
 
114
114
  const templateTypeSchema = z
115
- .enum(["mission", "document", "checklist"])
115
+ .enum(["standard", "mission", "brief", "runbook", "document", "checklist"])
116
116
  .describe("Template type");
117
117
 
118
118
  const linkTypeSchema = z
@@ -130,7 +130,7 @@ const convex = new ConvexHttpClient(convexUrl);
130
130
 
131
131
  const server = new McpServer({
132
132
  name: "vantage-registry",
133
- version: "1.4.0",
133
+ version: "1.5.0",
134
134
  });
135
135
 
136
136
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -317,7 +317,8 @@ server.tool(
317
317
 
318
318
  server.tool(
319
319
  "upsert_skill",
320
- "Create or update a skill in VantageRegistry. Upserts by name+team — if a skill with the same name and team exists, it is updated.",
320
+ "Create or update a skill in VantageRegistry. Upserts by name+team — if a skill with the same name and team exists, it is updated. " +
321
+ "Pass vrBody to write the full SKILL.md body (vrContent/vrContentHash/vrContentVersion) in the SAME call — no separate upsert_skill_content needed (that tool remains for back-compat). Omit vrBody for metadata-only upsert.",
321
322
  {
322
323
  name: z
323
324
  .string()
@@ -344,6 +345,12 @@ server.tool(
344
345
  .array(z.string())
345
346
  .optional()
346
347
  .describe("Categories beyond team name"),
348
+ vrBody: z
349
+ .string()
350
+ .optional()
351
+ .describe(
352
+ "Full SKILL.md body. When passed, writes vrContent/vrContentHash/vrContentVersion in the same call. Omitted → metadata-only (unchanged).",
353
+ ),
347
354
  },
348
355
  async (args) => {
349
356
  const id = await convex.mutation(api.skills.upsert, args);
@@ -985,6 +992,12 @@ server.tool(
985
992
  .describe("Whether this hook is registered in settings.json"),
986
993
  status: hookStatusSchema,
987
994
  version: z.string().optional().describe("Semantic version — e.g. '1.0.0'"),
995
+ tests: z
996
+ .array(z.string())
997
+ .optional()
998
+ .describe(
999
+ "Hook test file paths — e.g. ['hooks/tests/check-pii.test.py']. Omitted → not sent (back-compat).",
1000
+ ),
988
1001
  },
989
1002
  async (args) => {
990
1003
  const id = await convex.mutation(api.hooks.upsert, args);
@@ -1115,7 +1128,8 @@ server.tool(
1115
1128
 
1116
1129
  server.tool(
1117
1130
  "upsert_template",
1118
- "Create or update a template in VantageRegistry. Upserts by name.",
1131
+ "Create or update a template in VantageRegistry. Upserts by name. " +
1132
+ "contentHash is auto-computed server-side (sha256 of content) when omitted.",
1119
1133
  {
1120
1134
  name: z.string().describe("Template name — e.g. 'agent-brief', 'skill-md'"),
1121
1135
  team: z
@@ -1125,6 +1139,27 @@ server.tool(
1125
1139
  purpose: z.string().describe("What this template is used for"),
1126
1140
  content: z.string().describe("Full template content"),
1127
1141
  version: z.string().optional().describe("Semantic version — e.g. '1.0.0'"),
1142
+ template_type: templateTypeSchema
1143
+ .optional()
1144
+ .describe(
1145
+ "Template type: standard, mission, brief, runbook, document, or checklist",
1146
+ ),
1147
+ category: z
1148
+ .string()
1149
+ .optional()
1150
+ .describe("Category — e.g. 'standards/fleet-shared'"),
1151
+ contentHash: z
1152
+ .string()
1153
+ .optional()
1154
+ .describe("sha256 of content — auto-computed server-side when omitted"),
1155
+ sourceCommit: z
1156
+ .string()
1157
+ .optional()
1158
+ .describe("Git commit SHA the template content was sourced from"),
1159
+ sourceRepo: z
1160
+ .string()
1161
+ .optional()
1162
+ .describe("Git repo the template content was sourced from"),
1128
1163
  },
1129
1164
  async (args) => {
1130
1165
  const id = await convex.mutation(api.templates.upsert, args);
@@ -1173,6 +1208,47 @@ server.tool(
1173
1208
  },
1174
1209
  );
1175
1210
 
1211
+ server.tool(
1212
+ "detect_template_drift",
1213
+ "Return the VR-side sha256 contentHash + provenance (sourceCommit, sourceRepo) " +
1214
+ "for each template in scope. IMPORTANT: Convex queries have no filesystem access. " +
1215
+ "This tool returns the VR canonical contentHash so the caller can read the source " +
1216
+ "file, compute its SHA256, and compare. name=<slug> filters to a single template; " +
1217
+ "omit name to return every template.",
1218
+ {
1219
+ name: z
1220
+ .string()
1221
+ .optional()
1222
+ .describe("Template slug — filter to a single template"),
1223
+ },
1224
+ async ({ name }) => {
1225
+ const result = await convex.query(api.templates.detectTemplateDrift, {
1226
+ name,
1227
+ });
1228
+ return {
1229
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1230
+ };
1231
+ },
1232
+ );
1233
+
1234
+ server.tool(
1235
+ "list_templates_by_category",
1236
+ "List templates for a specific category using the byCategory index.",
1237
+ {
1238
+ category: z
1239
+ .string()
1240
+ .describe("Category to filter by — e.g. 'standards/fleet-shared'"),
1241
+ },
1242
+ async ({ category }) => {
1243
+ const result = await convex.query(api.templates.listTemplatesByCategory, {
1244
+ category,
1245
+ });
1246
+ return {
1247
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1248
+ };
1249
+ },
1250
+ );
1251
+
1176
1252
  // ═══════════════════════════════════════════════════════════════════════════════
1177
1253
  // SKILL TEST RUNS
1178
1254
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -1729,6 +1805,28 @@ server.tool(
1729
1805
  },
1730
1806
  );
1731
1807
 
1808
+ server.tool(
1809
+ "detect_runbook_drift",
1810
+ "Return the VR-side sha256 hash (vrHash) plus category and version for each runbook in scope. " +
1811
+ "IMPORTANT: Convex queries have no filesystem access. This tool computes sha256(runbook.content) " +
1812
+ "server-side and returns it as vrHash so the caller can compute disk SHA256 and compare. " +
1813
+ "name=<slug> filters to a single runbook; omit name to return every runbook (bounded to 500).",
1814
+ {
1815
+ name: z
1816
+ .string()
1817
+ .optional()
1818
+ .describe("Runbook slug — filter to a single runbook"),
1819
+ },
1820
+ async ({ name }) => {
1821
+ const result = await convex.query(api.runbooks.detectRunbookDrift, {
1822
+ name,
1823
+ });
1824
+ return {
1825
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1826
+ };
1827
+ },
1828
+ );
1829
+
1732
1830
  // ═══════════════════════════════════════════════════════════════════════════════
1733
1831
  // RUNBOOK ↔ TEMPLATE LINKS (Sprint B T4 — junction table typed links)
1734
1832
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -1946,7 +2044,16 @@ server.tool(
1946
2044
  // ═══════════════════════════════════════════════════════════════════════════════
1947
2045
 
1948
2046
  const componentKindSchema = z
1949
- .enum(["skill", "agent", "hook", "plugin", "prompt", "runbook", "template"])
2047
+ .enum([
2048
+ "skill",
2049
+ "agent",
2050
+ "hook",
2051
+ "plugin",
2052
+ "prompt",
2053
+ "runbook",
2054
+ "template",
2055
+ "ui-component",
2056
+ ])
1950
2057
  .describe("Component kind");
1951
2058
 
1952
2059
  const componentStatusSchema = z