@bradheitmann/odin-sentinel 0.4.11 → 0.4.13

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.
Files changed (42) hide show
  1. package/.claude-plugin/marketplace.json +23 -0
  2. package/README.md +20 -16
  3. package/dist/src/mcp/server.js +32 -0
  4. package/dist/src/mcp/server.js.map +1 -1
  5. package/dist/src/protocol/repository.d.ts +6 -0
  6. package/dist/src/protocol/repository.js +12 -2
  7. package/dist/src/protocol/repository.js.map +1 -1
  8. package/dist/src/protocol/service.js +17 -9
  9. package/dist/src/protocol/service.js.map +1 -1
  10. package/dist/src/protocol/version.d.ts +2 -2
  11. package/dist/src/protocol/version.js +2 -2
  12. package/docs/guides/quick-start.md +7 -7
  13. package/docs/guides/quickstart-prompts.md +4 -4
  14. package/docs/reference/client-compatibility.md +1 -1
  15. package/docs/reference/distribution.md +11 -5
  16. package/docs/reference/public-surface-audit.md +2 -2
  17. package/package.json +5 -3
  18. package/plugins/odin-scp/.claude-plugin/plugin.json +25 -0
  19. package/plugins/odin-scp/README.md +62 -0
  20. package/plugins/odin-scp/skills/odin-scp/CHANGELOG.md +25 -0
  21. package/plugins/odin-scp/skills/odin-scp/SKILL.md +1518 -0
  22. package/plugins/odin-scp/skills/odin-scp/agents/openai.yaml +4 -0
  23. package/plugins/odin-scp/skills/odin-scp/references/boot-receipt-examples.md +439 -0
  24. package/plugins/odin-scp/skills/odin-scp/references/canonical-introduction-prompt.md +118 -0
  25. package/plugins/odin-scp/skills/odin-scp/references/harness-skill-targets.md +56 -0
  26. package/plugins/odin-scp/skills/odin-scp/references/team-bootstrap-runbook.md +298 -0
  27. package/plugins/odin-scp/skills/odin-scp/scripts/sync-installations.sh +233 -0
  28. package/protocol/SCP.md +3 -3
  29. package/protocol/bootstrap-skill.md +22 -13
  30. package/protocol/closeout.yaml +1 -1
  31. package/protocol/delegation.yaml +1 -1
  32. package/protocol/model-profiles.yaml +2 -2
  33. package/protocol/receipts/team-manifest.yaml +1 -1
  34. package/protocol/roles.yaml +1 -1
  35. package/protocol/skill-references/boot-receipt-examples.md +439 -0
  36. package/protocol/skill-references/canonical-introduction-prompt.md +118 -0
  37. package/protocol/skill-references/harness-skill-targets.md +56 -0
  38. package/protocol/skill-references/team-bootstrap-runbook.md +298 -0
  39. package/protocol/topology.yaml +1 -1
  40. package/scripts/audit/public-surface.mjs +32 -3
  41. package/scripts/audit/verify-pack.mjs +175 -12
  42. package/templates/team-manifest-template.yaml +6 -6
@@ -1,5 +1,6 @@
1
1
  import { execFileSync } from "node:child_process";
2
- import { readFileSync } from "node:fs";
2
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
3
+ import { join } from "node:path";
3
4
  import { pathToFileURL } from "node:url";
4
5
 
5
6
  export const MINIMUM_COMPATIBLE_CHILD_MCP_VERSION = "0.4.5";
@@ -13,7 +14,11 @@ const requiredProtocolFiles = [
13
14
  "protocol/delegation.yaml",
14
15
  "protocol/receipts/boot-receipt.yaml",
15
16
  "protocol/receipts/team-manifest.yaml",
16
- "protocol/bootstrap-skill.md"
17
+ "protocol/bootstrap-skill.md",
18
+ "protocol/skill-references/boot-receipt-examples.md",
19
+ "protocol/skill-references/canonical-introduction-prompt.md",
20
+ "protocol/skill-references/harness-skill-targets.md",
21
+ "protocol/skill-references/team-bootstrap-runbook.md"
17
22
  ];
18
23
 
19
24
  const requiredTemplateFiles = [
@@ -24,6 +29,7 @@ const requiredTemplateFiles = [
24
29
  ];
25
30
 
26
31
  export const requiredPackageFiles = [
32
+ ".claude-plugin/marketplace.json",
27
33
  "dist/src/bin/index.js",
28
34
  "dist/src/mcp/server.js",
29
35
  "dist/src/protocol/index.js",
@@ -40,9 +46,22 @@ export const requiredPackageFiles = [
40
46
  "docs/reference/distribution.md",
41
47
  "docs/reference/public-surface-audit.md",
42
48
  ...requiredProtocolFiles,
49
+ "plugins/odin-scp/.claude-plugin/plugin.json",
50
+ "plugins/odin-scp/skills/odin-scp/SKILL.md",
51
+ "plugins/odin-scp/skills/odin-scp/CHANGELOG.md",
52
+ "plugins/odin-scp/skills/odin-scp/agents/openai.yaml",
53
+ "plugins/odin-scp/skills/odin-scp/references/boot-receipt-examples.md",
54
+ "plugins/odin-scp/skills/odin-scp/references/canonical-introduction-prompt.md",
55
+ "plugins/odin-scp/skills/odin-scp/references/harness-skill-targets.md",
56
+ "plugins/odin-scp/skills/odin-scp/references/team-bootstrap-runbook.md",
57
+ "plugins/odin-scp/skills/odin-scp/scripts/sync-installations.sh",
58
+ "plugins/odin-scp/README.md",
43
59
  ...requiredTemplateFiles,
44
60
  "scripts/audit/public-surface.mjs",
45
61
  "scripts/audit/verify-pack.mjs",
62
+ "scripts/protocol/install-activation-hooks.mjs",
63
+ "scripts/protocol/verify-governed-context.mjs",
64
+ "scripts/protocol/verify-instruction-read.mjs",
46
65
  "AGENTS.md",
47
66
  "CLAUDE.md",
48
67
  "README.md",
@@ -67,14 +86,20 @@ const protocolResourceVersionLockedFiles = new Set([
67
86
  "protocol/topology.yaml"
68
87
  ]);
69
88
 
70
- const forbiddenPackagePrefixes = ["project/" + "planning" + "/", "." + "edge-" + "agentic" + "/local/"];
89
+ const forbiddenPackagePrefixes = ["docs/handoffs/", "project/" + "planning" + "/", "." + "edge-" + "agentic" + "/local/"];
90
+ const AUDIT_SCRIPT_EXEMPTIONS = new Set(["scripts/audit/public-surface.mjs", "scripts/audit/verify-pack.mjs"]);
91
+ const INTERNAL_HANDOFF_REFERENCE_EXEMPTIONS = new Set([...AUDIT_SCRIPT_EXEMPTIONS, "docs/reference/distribution.md"]);
71
92
  const forbiddenPackagedContentRules = [
72
93
  { name: "local evidence path", pattern: new RegExp(`\\.${"edge-" + "agentic"}/local`, "i") },
73
94
  { name: "local ODIN audit path", pattern: /\.odin\/local\//i },
74
95
  { name: "private planning path", pattern: new RegExp(`project/${"planning"}/`, "i") },
96
+ { name: "internal handoff path reference", pattern: /docs\/handoffs\//i, exemptFiles: INTERNAL_HANDOFF_REFERENCE_EXEMPTIONS },
75
97
  { name: "macOS home path", pattern: new RegExp(`/${"Users"}/[A-Za-z0-9._-]+/`) },
76
98
  { name: "Linux home path", pattern: /\/home\/[A-Za-z0-9._-]+\// },
77
- { name: "secret-looking assignment", pattern: /(api[_-]?key|secret|token|password)\s*[:=]\s*["'][^"']+["']/i }
99
+ { name: "secret-looking quoted assignment", pattern: /(api[_-]?key|secret|token|password)\s*[:=]\s*["'][^"']+["']/i },
100
+ { name: "secret-looking unquoted assignment", pattern: /(api[_-]?key|secret|token|password)\s*[:=]\s*[A-Za-z0-9._~+/=-]{16,}/i },
101
+ { name: "bearer token literal", pattern: /\bBearer\s+[A-Za-z0-9._~+/=-]{20,}/i },
102
+ { name: "URI credential literal", pattern: /[a-z][a-z0-9+.-]*:\/\/[^/\s:@]+:[^/\s:@]+@/i }
78
103
  ];
79
104
 
80
105
  function asPathSet(paths) {
@@ -89,7 +114,10 @@ export function validatePackageMetadata(packageJson) {
89
114
  if (!packageJson.license) errors.push("package.json missing license");
90
115
  if (!packageJson.engines?.node) errors.push("package.json missing engines.node");
91
116
  if (!Array.isArray(packageJson.files) || packageJson.files.length === 0) errors.push("package.json missing files allowlist");
92
- for (const file of ["docs", "protocol", "templates", "AGENTS.md", "CLAUDE.md", "README.md", "LICENSE"]) {
117
+ if (packageJson.scripts?.prepublishOnly !== "pnpm run validate") {
118
+ errors.push("package.json prepublishOnly must run pnpm run validate");
119
+ }
120
+ for (const file of [".claude-plugin", "docs", "plugins", "protocol", "templates", "AGENTS.md", "CLAUDE.md", "README.md", "LICENSE"]) {
93
121
  if (!packageJson.files?.includes(file)) errors.push(`package.json files allowlist missing ${file}`);
94
122
  }
95
123
  if (packageJson.odin?.publicVersion !== packageJson.version) {
@@ -106,6 +134,30 @@ export function validatePackageMetadata(packageJson) {
106
134
  return errors;
107
135
  }
108
136
 
137
+ function walkFiles(dir) {
138
+ if (!existsSync(dir)) return [];
139
+ return readdirSync(dir).flatMap((entry) => {
140
+ const path = join(dir, entry);
141
+ return statSync(path).isDirectory() ? walkFiles(path) : [path];
142
+ });
143
+ }
144
+
145
+ function expectedGeneratedDistFiles() {
146
+ const expected = new Set();
147
+ for (const file of walkFiles("src")) {
148
+ if (!file.endsWith(".ts") || file.endsWith(".d.ts")) continue;
149
+ const jsFile = file.replace(/^src\//, "dist/src/").replace(/\.ts$/, ".js");
150
+ expected.add(jsFile);
151
+ expected.add(`${jsFile}.map`);
152
+ expected.add(jsFile.replace(/\.js$/, ".d.ts"));
153
+ }
154
+ return expected;
155
+ }
156
+
157
+ function allowedGeneratedDistFiles() {
158
+ return new Set([...requiredPackageFiles.filter((file) => file.startsWith("dist/")), ...expectedGeneratedDistFiles()]);
159
+ }
160
+
109
161
  export function validatePackFileList(pathsInput) {
110
162
  const paths = asPathSet(pathsInput);
111
163
  const errors = [];
@@ -118,6 +170,10 @@ export function validatePackFileList(pathsInput) {
118
170
  const privatePaths = Array.from(paths).filter((file) => forbiddenPackagePrefixes.some((prefix) => file.startsWith(prefix)));
119
171
  if (privatePaths.length > 0) errors.push(`Package includes private local paths: ${privatePaths.join(", ")}`);
120
172
 
173
+ const allowed = new Set([...requiredPackageFiles, ...allowedGeneratedDistFiles()]);
174
+ const unexpected = Array.from(paths).filter((file) => !allowed.has(file));
175
+ if (unexpected.length > 0) errors.push(`Package includes unexpected files: ${unexpected.join(", ")}`);
176
+
121
177
  return errors;
122
178
  }
123
179
 
@@ -125,6 +181,7 @@ export function validatePackFileContents(fileTextByPath) {
125
181
  const findings = [];
126
182
  for (const [file, text] of Object.entries(fileTextByPath)) {
127
183
  for (const rule of forbiddenPackagedContentRules) {
184
+ if (rule.exemptFiles?.has(file)) continue;
128
185
  if (rule.pattern.test(text)) findings.push(`${file}: ${rule.name}`);
129
186
  }
130
187
  }
@@ -186,6 +243,56 @@ export function findStaleVersionReferences(fileTextByPath, currentVersion, minim
186
243
  return findings;
187
244
  }
188
245
 
246
+ export function findUnpinnedInstallReferences(fileTextByPath, currentVersion) {
247
+ const findings = [];
248
+ const packageName = "@bradheitmann/odin-sentinel";
249
+ const pinned = `${packageName}@${currentVersion}`;
250
+ const commandMarkers = [
251
+ "pnpm",
252
+ "npx",
253
+ "npm",
254
+ "claude mcp",
255
+ "--package",
256
+ "installurl",
257
+ "\"args\"",
258
+ "args =",
259
+ "command"
260
+ ];
261
+
262
+ for (const [file, text] of Object.entries(fileTextByPath)) {
263
+ if (AUDIT_SCRIPT_EXEMPTIONS.has(file)) continue;
264
+ const lines = text.split("\n");
265
+ for (const [index, line] of lines.entries()) {
266
+ if (!line.includes(packageName)) continue;
267
+ const windowText = lines.slice(Math.max(0, index - 2), Math.min(lines.length, index + 3)).join(" ");
268
+ const lowerWindow = windowText.toLowerCase();
269
+ if (!commandMarkers.some((marker) => lowerWindow.includes(marker))) continue;
270
+ if (windowText.includes(pinned)) continue;
271
+ findings.push(`${file}:${index + 1}: install command must pin ${pinned}`);
272
+ }
273
+ }
274
+
275
+ return findings;
276
+ }
277
+
278
+ export function validateRuntimeVersionConstants(versionText, currentVersion, minimumCompatibleVersion = MINIMUM_COMPATIBLE_CHILD_MCP_VERSION, file = "src/protocol/version.ts") {
279
+ const errors = [];
280
+ const required = [
281
+ [`PROTOCOL_SCHEMA_VERSION`, currentVersion],
282
+ [`PUBLIC_LATEST_VERSION`, currentVersion],
283
+ [`MINIMUM_COMPATIBLE_MCP_VERSION`, minimumCompatibleVersion]
284
+ ];
285
+
286
+ for (const [constant, expected] of required) {
287
+ const pattern = new RegExp(`\\b${constant}\\s*=\\s*["']${expected.replaceAll(".", "\\.")}["']`);
288
+ if (!pattern.test(versionText)) {
289
+ errors.push(`${file}: ${constant} must be ${expected}`);
290
+ }
291
+ }
292
+
293
+ return errors;
294
+ }
295
+
189
296
  export function validatePublicProtocolSync({ scpText, bootstrapText, currentVersion, minimumCompatibleVersion = MINIMUM_COMPATIBLE_CHILD_MCP_VERSION }) {
190
297
  const errors = [];
191
298
  const requiredMarkers = [
@@ -217,6 +324,9 @@ export function validatePluginSync({ pluginManifestText, pluginSkillText, plugin
217
324
  manifest = {};
218
325
  }
219
326
 
327
+ if (manifest.name !== "odin-scp") {
328
+ errors.push(`Claude plugin manifest name ${manifest.name ?? "<missing>"} must be odin-scp`);
329
+ }
220
330
  if (manifest.version !== currentVersion) {
221
331
  errors.push(`Claude plugin manifest version ${manifest.version ?? "<missing>"} must match package version ${currentVersion}`);
222
332
  }
@@ -226,9 +336,13 @@ export function validatePluginSync({ pluginManifestText, pluginSkillText, plugin
226
336
  } else {
227
337
  if (server.command !== "pnpm") errors.push("Claude plugin odin-sentinel server must use pnpm");
228
338
  const args = Array.isArray(server.args) ? server.args : [];
229
- for (const requiredArg of ["dlx", "--package", `@bradheitmann/odin-sentinel@${currentVersion}`, "odin-sentinel-mcp"]) {
339
+ const expectedArgs = ["dlx", "--package", `@bradheitmann/odin-sentinel@${currentVersion}`, "odin-sentinel-mcp"];
340
+ for (const requiredArg of expectedArgs) {
230
341
  if (!args.includes(requiredArg)) errors.push(`Claude plugin odin-sentinel args missing ${requiredArg}`);
231
342
  }
343
+ if (JSON.stringify(args) !== JSON.stringify(expectedArgs)) {
344
+ errors.push(`Claude plugin odin-sentinel args must exactly equal ${JSON.stringify(expectedArgs)}`);
345
+ }
232
346
  }
233
347
 
234
348
  for (const marker of [`SCP_PUBLIC_VERSION: ${currentVersion}`, `MIN_COMPATIBLE_CHILD_MCP: ${minimumCompatibleVersion}`]) {
@@ -244,6 +358,39 @@ export function validatePluginSync({ pluginManifestText, pluginSkillText, plugin
244
358
  return errors;
245
359
  }
246
360
 
361
+ export function validateMarketplaceSync({ marketplaceText, currentVersion }) {
362
+ const errors = [];
363
+ let marketplace;
364
+ try {
365
+ marketplace = JSON.parse(marketplaceText);
366
+ } catch {
367
+ errors.push("Claude marketplace manifest must be valid JSON");
368
+ marketplace = {};
369
+ }
370
+
371
+ const plugins = Array.isArray(marketplace.plugins) ? marketplace.plugins : [];
372
+ const odinScp = plugins.find((plugin) => plugin?.name === "odin-scp");
373
+ if (!odinScp) {
374
+ errors.push("Claude marketplace manifest must advertise odin-scp");
375
+ } else {
376
+ if (odinScp.source !== "./plugins/odin-scp") {
377
+ errors.push(`Claude marketplace odin-scp source ${odinScp.source ?? "<missing>"} must be ./plugins/odin-scp`);
378
+ }
379
+ if (odinScp.version !== currentVersion) {
380
+ errors.push(`Claude marketplace odin-scp version ${odinScp.version ?? "<missing>"} must match package version ${currentVersion}`);
381
+ }
382
+ }
383
+
384
+ const legacyName = `sentinel-${"coordination"}-${"protocol"}`;
385
+ const legacySource = `./plugins/${legacyName}`;
386
+ const staleLegacy = plugins.find((plugin) => plugin?.name === legacyName || plugin?.source === legacySource);
387
+ if (staleLegacy) {
388
+ errors.push("Claude marketplace manifest must not advertise the legacy long-name plugin");
389
+ }
390
+
391
+ return errors;
392
+ }
393
+
247
394
  export function validatePackagedProtocolVersions(fileTextByPath, currentVersion) {
248
395
  const errors = [];
249
396
  for (const [file, text] of Object.entries(fileTextByPath)) {
@@ -280,11 +427,20 @@ function readPublicVersionFiles() {
280
427
  "docs/reference/client-compatibility.md",
281
428
  "docs/reference/distribution.md",
282
429
  "docs/reference/public-surface-audit.md",
430
+ "src/protocol/version.ts",
431
+ ".claude-plugin/marketplace.json",
283
432
  "protocol/SCP.md",
284
433
  "protocol/bootstrap-skill.md",
285
- "plugins/sentinel-coordination-protocol/.claude-plugin/plugin.json",
286
- "plugins/sentinel-coordination-protocol/skills/sentinel-coordination-protocol/SKILL.md",
287
- "plugins/sentinel-coordination-protocol/README.md"
434
+ "plugins/odin-scp/.claude-plugin/plugin.json",
435
+ "plugins/odin-scp/skills/odin-scp/SKILL.md",
436
+ "plugins/odin-scp/skills/odin-scp/CHANGELOG.md",
437
+ "plugins/odin-scp/skills/odin-scp/agents/openai.yaml",
438
+ "plugins/odin-scp/skills/odin-scp/references/boot-receipt-examples.md",
439
+ "plugins/odin-scp/skills/odin-scp/references/canonical-introduction-prompt.md",
440
+ "plugins/odin-scp/skills/odin-scp/references/harness-skill-targets.md",
441
+ "plugins/odin-scp/skills/odin-scp/references/team-bootstrap-runbook.md",
442
+ "plugins/odin-scp/skills/odin-scp/scripts/sync-installations.sh",
443
+ "plugins/odin-scp/README.md"
288
444
  ].map((file) => [file, readFileSync(file, "utf8")]));
289
445
  }
290
446
 
@@ -304,15 +460,22 @@ export function runVerifyPack({ pack, packageJson, publicVersionFiles, costPriva
304
460
  ...validatePackFileContents(packFileTexts),
305
461
  ...validatePackagedProtocolVersions(packFileTexts, packageJson.version),
306
462
  ...findStaleVersionReferences(publicVersionFiles, packageJson.version),
463
+ ...findUnpinnedInstallReferences(packFileTexts, packageJson.version),
464
+ ...validateRuntimeVersionConstants(publicVersionFiles["src/protocol/version.ts"], packageJson.version),
465
+ ...validateRuntimeVersionConstants(packFileTexts["dist/src/protocol/version.js"] ?? "", packageJson.version, MINIMUM_COMPATIBLE_CHILD_MCP_VERSION, "dist/src/protocol/version.js"),
307
466
  ...validatePublicProtocolSync({
308
467
  scpText: publicVersionFiles["protocol/SCP.md"],
309
468
  bootstrapText: publicVersionFiles["protocol/bootstrap-skill.md"],
310
469
  currentVersion: packageJson.version
311
470
  }),
471
+ ...validateMarketplaceSync({
472
+ marketplaceText: publicVersionFiles[".claude-plugin/marketplace.json"],
473
+ currentVersion: packageJson.version
474
+ }),
312
475
  ...validatePluginSync({
313
- pluginManifestText: publicVersionFiles["plugins/sentinel-coordination-protocol/.claude-plugin/plugin.json"],
314
- pluginSkillText: publicVersionFiles["plugins/sentinel-coordination-protocol/skills/sentinel-coordination-protocol/SKILL.md"],
315
- pluginReadmeText: publicVersionFiles["plugins/sentinel-coordination-protocol/README.md"],
476
+ pluginManifestText: publicVersionFiles["plugins/odin-scp/.claude-plugin/plugin.json"],
477
+ pluginSkillText: publicVersionFiles["plugins/odin-scp/skills/odin-scp/SKILL.md"],
478
+ pluginReadmeText: publicVersionFiles["plugins/odin-scp/README.md"],
316
479
  currentVersion: packageJson.version,
317
480
  expectedToolCount: extractToolCount(packageJson.description)
318
481
  }),
@@ -30,7 +30,7 @@ role_slots:
30
30
  workspace: "<workspace-ref>"
31
31
  pane: "<pane-ref>"
32
32
  surface: "<surface-ref>"
33
- scp_context_source: "native sentinel-coordination-protocol skill"
33
+ scp_context_source: "native odin-scp skill"
34
34
  mcp_available: true
35
35
  mcp_version: "0.4.5"
36
36
  scp_skill_available: true
@@ -43,7 +43,7 @@ role_slots:
43
43
  workspace: "<workspace-ref>"
44
44
  pane: "<pane-ref>"
45
45
  surface: "<surface-ref>"
46
- scp_context_source: "native sentinel-coordination-protocol skill"
46
+ scp_context_source: "native odin-scp skill"
47
47
  mcp_available: true
48
48
  mcp_version: "0.4.5"
49
49
  scp_skill_available: true
@@ -58,7 +58,7 @@ role_slots:
58
58
  workspace: "<workspace-ref>"
59
59
  pane: "<pane-ref>"
60
60
  surface: "<surface-ref>"
61
- scp_context_source: "native sentinel-coordination-protocol skill"
61
+ scp_context_source: "native odin-scp skill"
62
62
  mcp_available: true
63
63
  mcp_version: "0.4.5"
64
64
  scp_skill_available: true
@@ -73,7 +73,7 @@ role_slots:
73
73
  workspace: "<workspace-ref>"
74
74
  pane: "<pane-ref>"
75
75
  surface: "<surface-ref>"
76
- scp_context_source: "native sentinel-coordination-protocol skill"
76
+ scp_context_source: "native odin-scp skill"
77
77
  mcp_available: true
78
78
  mcp_version: "0.4.5"
79
79
  scp_skill_available: true
@@ -121,7 +121,7 @@ user_defined_criteria:
121
121
  # workspace: "workspace:1"
122
122
  # pane: "pane:1"
123
123
  # surface: "surface:1"
124
- # scp_context_source: "native sentinel-coordination-protocol skill"
124
+ # scp_context_source: "native odin-scp skill"
125
125
  # mcp_available: true
126
126
  # mcp_version: "0.4.5"
127
127
  # scp_skill_available: true
@@ -134,7 +134,7 @@ user_defined_criteria:
134
134
  # workspace: "workspace:1"
135
135
  # pane: "pane:2"
136
136
  # surface: "surface:2"
137
- # scp_context_source: "native sentinel-coordination-protocol skill"
137
+ # scp_context_source: "native odin-scp skill"
138
138
  # mcp_available: true
139
139
  # mcp_version: "0.4.5"
140
140
  # scp_skill_available: true