mmt-mcp 1.42.2-pre → 1.42.4

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.
@@ -42,5 +42,5 @@ testlight docs api --pack full
42
42
  `testlight docs` reads from, in order:
43
43
 
44
44
  1. `MMT_GUIDES_DIR`
45
- 2. Guides bundled next to the CLI (`guides/` beside `cli.js`)
45
+ 2. Guides bundled next to the CLI (`guides/` beside `cli.js` or inside the standalone binary)
46
46
  3. Repo `docs/AI` when developing from source
package/dist/server.cjs CHANGED
@@ -8299,8 +8299,41 @@ var init_grpcCore = __esm({
8299
8299
  });
8300
8300
 
8301
8301
  // src/index.ts
8302
+ var import_module = require("module");
8302
8303
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
8303
8304
 
8305
+ // ../core/src/testlightHelp.ts
8306
+ var TESTLIGHT_NAME = "testlight";
8307
+ var TESTLIGHT_DESCRIPTION = "Multimeter CLI \u2014 run .mmt API tests, suites, and docs";
8308
+ var TESTLIGHT_HELP_AFTER = [
8309
+ "",
8310
+ "Run options:",
8311
+ " -q, --quiet Minimal output",
8312
+ " -o, --out <file> Write result JSON to file",
8313
+ " -i, --input <k=v...> Input variables (repeatable)",
8314
+ " -e, --env <k=v...> Environment variables (repeatable)",
8315
+ " -F, --env-file <path> Environment file (.mmt/.yaml)",
8316
+ " -P, --preset <name> Preset from env file (repeatable)",
8317
+ " -x, --example <name|#n> Named example or index (#1 is first)",
8318
+ " -p, --print-js Print generated JS before executing",
8319
+ " -r, --report <format> junit | mmt | html | md | md-detailed",
8320
+ " -R, --report-file <path> Report output path",
8321
+ "",
8322
+ "Examples:",
8323
+ " testlight run path/to/test.mmt",
8324
+ " testlight run path/to/test.mmt -F env.mmt -P runner.dev -P custom.prod",
8325
+ " testlight run path/to/suite.mmt --report html",
8326
+ " testlight scaffold test --from path/to/api.mmt",
8327
+ " testlight scaffold test --from path/to/api.mmt -o tests/api-smoke.mmt",
8328
+ " testlight docs test",
8329
+ " testlight validate path/to/test.mmt",
8330
+ " testlight suggest asserts --from path/to/api.mmt",
8331
+ " testlight update",
8332
+ " testlight update --check",
8333
+ "",
8334
+ "Run `testlight <command> --help` for command-specific options."
8335
+ ].join("\n");
8336
+
8304
8337
  // src/server.ts
8305
8338
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
8306
8339
  var import_zod = require("zod");
@@ -20707,9 +20740,6 @@ function matchesSource(filePath, src) {
20707
20740
  if (!fp || !s) {
20708
20741
  return false;
20709
20742
  }
20710
- if (s === ".") {
20711
- return true;
20712
- }
20713
20743
  if (/\.mmt$/i.test(s)) {
20714
20744
  return fp.endsWith(s) || fp === s;
20715
20745
  }
@@ -21102,9 +21132,6 @@ function matchesSource2(filePath, src) {
21102
21132
  if (!fp || !s) {
21103
21133
  return false;
21104
21134
  }
21105
- if (s === ".") {
21106
- return true;
21107
- }
21108
21135
  if (/\.mmt$/i.test(s)) {
21109
21136
  return fp.endsWith(s) || fp === s;
21110
21137
  }
@@ -24327,6 +24354,88 @@ function parseDurationMs(duration) {
24327
24354
  return void 0;
24328
24355
  }
24329
24356
 
24357
+ // ../core/src/agentDocs.ts
24358
+ var FULL_TOPIC_FILES = {
24359
+ overview: ["agent-workflow.md", "general.md", "generate.md"],
24360
+ workflow: ["agent-workflow.md"],
24361
+ test: ["generate-test.md"],
24362
+ api: ["generate-api.md"],
24363
+ loadtest: ["generate-loadtest.md"],
24364
+ suite: ["generate-suite.md"],
24365
+ env: ["generate-env.md"],
24366
+ doc: ["generate-doc.md"],
24367
+ constraints: ["generate-test-skill.md"]
24368
+ };
24369
+ var MIN_TOPIC_FILES = {
24370
+ overview: ["min/overview.md"],
24371
+ workflow: ["min/workflow.md"],
24372
+ test: ["min/test.md"],
24373
+ api: ["min/api.md"],
24374
+ loadtest: ["min/loadtest.md"],
24375
+ suite: ["min/suite.md"],
24376
+ env: ["min/env.md"],
24377
+ doc: ["min/doc.md"],
24378
+ constraints: ["min/constraints.md"]
24379
+ };
24380
+ var AGENT_DOC_FILE_TOPICS = Object.keys(MIN_TOPIC_FILES);
24381
+ function filesForAgentDocTopic(topic, pack = "min") {
24382
+ if (topic === "offline") {
24383
+ return ["offline-agent.md"];
24384
+ }
24385
+ const table = pack === "full" ? FULL_TOPIC_FILES : MIN_TOPIC_FILES;
24386
+ if (topic === "all") {
24387
+ return Array.from(new Set(Object.values(table).flat()));
24388
+ }
24389
+ return table[topic];
24390
+ }
24391
+ function loadAgentDocSections(opts) {
24392
+ const topic = opts.topic || "overview";
24393
+ const pack = opts.pack || "min";
24394
+ const files = filesForAgentDocTopic(topic, pack);
24395
+ return {
24396
+ topic,
24397
+ pack,
24398
+ sections: files.map((fileName) => ({
24399
+ fileName,
24400
+ content: opts.readText(fileName)
24401
+ }))
24402
+ };
24403
+ }
24404
+
24405
+ // ../core/src/validateMmt.ts
24406
+ function validateMmtContent(content, filePath, expectedType) {
24407
+ const detectedType = fileType(filePath || "", content);
24408
+ if (expectedType && detectedType && detectedType !== expectedType) {
24409
+ return {
24410
+ valid: false,
24411
+ detectedType,
24412
+ errors: [`Expected type "${expectedType}" but detected "${detectedType}"`]
24413
+ };
24414
+ }
24415
+ const type = expectedType || detectedType;
24416
+ try {
24417
+ if (type === "api") {
24418
+ yamlToAPIStrict(content);
24419
+ } else if (type === "test" || !type) {
24420
+ yamlToTestStrict(content);
24421
+ } else {
24422
+ return {
24423
+ valid: false,
24424
+ detectedType,
24425
+ errors: [`Validation for type "${type}" is not implemented yet`]
24426
+ };
24427
+ }
24428
+ return { valid: true, detectedType: type || detectedType, errors: [] };
24429
+ } catch (error) {
24430
+ const message = error?.message || String(error);
24431
+ return {
24432
+ valid: false,
24433
+ detectedType,
24434
+ errors: message.split("\n").filter(Boolean)
24435
+ };
24436
+ }
24437
+ }
24438
+
24330
24439
  // ../core/src/mmtFormatAst.ts
24331
24440
  var import_yaml3 = __toESM(require("yaml"));
24332
24441
  var ROOT_KEY_ORDER = {
@@ -26634,42 +26743,23 @@ function toWorkspaceRelative(workspaceRoot, fullPath) {
26634
26743
  // src/resources/documentation.ts
26635
26744
  var import_fs3 = __toESM(require("fs"), 1);
26636
26745
  var import_path3 = __toESM(require("path"), 1);
26637
- var FULL_TOPIC_FILES = {
26638
- overview: ["agent-workflow.md", "general.md", "generate.md"],
26639
- workflow: ["agent-workflow.md"],
26640
- test: ["generate-test.md"],
26641
- api: ["generate-api.md"],
26642
- loadtest: ["generate-loadtest.md"],
26643
- suite: ["generate-suite.md"],
26644
- env: ["generate-env.md"],
26645
- doc: ["generate-doc.md"],
26646
- constraints: ["generate-test-skill.md"]
26647
- };
26648
- var MIN_TOPIC_FILES = {
26649
- overview: ["min/overview.md"],
26650
- workflow: ["min/workflow.md"],
26651
- test: ["min/test.md"],
26652
- api: ["min/api.md"],
26653
- loadtest: ["min/loadtest.md"],
26654
- suite: ["min/suite.md"],
26655
- env: ["min/env.md"],
26656
- doc: ["min/doc.md"],
26657
- constraints: ["min/constraints.md"]
26658
- };
26659
26746
  function readDocumentation(topic = "overview", pack = "min") {
26660
- const table = pack === "full" ? FULL_TOPIC_FILES : MIN_TOPIC_FILES;
26661
- const files = topic === "all" ? Array.from(new Set(Object.values(table).flat())) : table[topic];
26662
- const sections = files.map((fileName) => {
26663
- const resource = GUIDE_RESOURCES.find((item) => item.fileName === fileName);
26747
+ const loaded = loadAgentDocSections({
26748
+ topic,
26749
+ pack,
26750
+ readText: readGuideContent
26751
+ });
26752
+ const sections = loaded.sections.map((section) => {
26753
+ const resource = GUIDE_RESOURCES.find((item) => item.fileName === section.fileName);
26664
26754
  return {
26665
- name: resource?.name || fileName.replace(/\.md$/, "").replace(/^min\//, ""),
26666
- fileName,
26667
- content: readGuideContent(fileName)
26755
+ name: resource?.name || section.fileName.replace(/\.md$/, "").replace(/^min\//, ""),
26756
+ fileName: section.fileName,
26757
+ content: section.content
26668
26758
  };
26669
26759
  });
26670
26760
  return {
26671
- topic,
26672
- pack,
26761
+ topic: loaded.topic,
26762
+ pack: loaded.pack,
26673
26763
  sections,
26674
26764
  usage: pack === "min" ? 'Default min pack. Request pack: "full" only when you need rare syntax.' : 'Full documentation pack. Prefer pack: "min" for routine generate/modify.'
26675
26765
  };
@@ -26769,33 +26859,6 @@ function listExamples(options) {
26769
26859
  }
26770
26860
 
26771
26861
  // src/tools/handlers.ts
26772
- function detectType(content, filePath) {
26773
- if (filePath) {
26774
- return JSer_exports.fileType(filePath, content);
26775
- }
26776
- if (content.includes("type: api")) {
26777
- return "api";
26778
- }
26779
- if (content.includes("type: test")) {
26780
- return "test";
26781
- }
26782
- if (content.includes("type: env")) {
26783
- return "env";
26784
- }
26785
- if (content.includes("type: suite")) {
26786
- return "suite";
26787
- }
26788
- if (content.includes("type: doc")) {
26789
- return "doc";
26790
- }
26791
- if (content.includes("type: server")) {
26792
- return "server";
26793
- }
26794
- if (content.includes("type: loadtest")) {
26795
- return "loadtest";
26796
- }
26797
- return null;
26798
- }
26799
26862
  function buildValidationSuggestions(errors) {
26800
26863
  const suggestions = /* @__PURE__ */ new Set();
26801
26864
  for (const error of errors) {
@@ -26824,37 +26887,11 @@ function buildValidationSuggestions(errors) {
26824
26887
  return Array.from(suggestions);
26825
26888
  }
26826
26889
  function validateContent(content, filePath, expectedType) {
26827
- const detectedType = detectType(content, filePath);
26828
- if (expectedType && detectedType && detectedType !== expectedType) {
26829
- const errors = [`Expected type "${expectedType}" but detected "${detectedType}"`];
26830
- return {
26831
- valid: false,
26832
- detectedType,
26833
- errors,
26834
- suggestions: buildValidationSuggestions(errors)
26835
- };
26836
- }
26837
- try {
26838
- const type = expectedType || detectedType;
26839
- if (type === "api") {
26840
- apiParsePack_exports.yamlToAPIStrict(content);
26841
- } else if (type === "test" || !type) {
26842
- testParsePack_exports.yamlToTestStrict(content);
26843
- } else {
26844
- const errors = [`Validation for type "${type}" is not implemented yet`];
26845
- return { valid: false, detectedType, errors, suggestions: buildValidationSuggestions(errors) };
26846
- }
26847
- return { valid: true, detectedType: type || detectedType, errors: [], suggestions: [] };
26848
- } catch (error) {
26849
- const message = error?.message || String(error);
26850
- const errors = message.split("\n").filter(Boolean);
26851
- return {
26852
- valid: false,
26853
- detectedType,
26854
- errors,
26855
- suggestions: buildValidationSuggestions(errors)
26856
- };
26857
- }
26890
+ const result = validateMmtContent(content, filePath, expectedType);
26891
+ return {
26892
+ ...result,
26893
+ suggestions: result.valid ? [] : buildValidationSuggestions(result.errors)
26894
+ };
26858
26895
  }
26859
26896
  async function handleReadDocumentation(args) {
26860
26897
  try {
@@ -27484,7 +27521,46 @@ function createMmtMcpServer() {
27484
27521
  }
27485
27522
 
27486
27523
  // src/index.ts
27524
+ var requireFromHere = (0, import_module.createRequire)(__filename);
27525
+ function resolveMcpVersion() {
27526
+ try {
27527
+ const version = requireFromHere("../package.json").version;
27528
+ if (typeof version === "string" && version) {
27529
+ return version;
27530
+ }
27531
+ } catch {
27532
+ }
27533
+ return "0.0.0";
27534
+ }
27535
+ function printHelp() {
27536
+ const version = resolveMcpVersion();
27537
+ process.stdout.write([
27538
+ `mmt-mcp ${version}`,
27539
+ "",
27540
+ "Multimeter MCP server over stdio.",
27541
+ "Tools: read_documentation, list_examples, discover_api, api_card,",
27542
+ " scaffold_test, suggest_assertions, validate, format, run",
27543
+ "",
27544
+ "Usage: npx -y mmt-mcp",
27545
+ "Docs: https://mmt.dev/docs/features/mcp",
27546
+ "",
27547
+ `${TESTLIGHT_NAME} \u2014 ${TESTLIGHT_DESCRIPTION}`,
27548
+ "npm (mmt-testlight) and GitHub/Homebrew binaries share this CLI:",
27549
+ TESTLIGHT_HELP_AFTER,
27550
+ ""
27551
+ ].join("\n"));
27552
+ }
27487
27553
  async function main() {
27554
+ const args = process.argv.slice(2);
27555
+ if (args.includes("--help") || args.includes("-h")) {
27556
+ printHelp();
27557
+ return;
27558
+ }
27559
+ if (args.includes("--version") || args.includes("-v")) {
27560
+ process.stdout.write(`${resolveMcpVersion()}
27561
+ `);
27562
+ return;
27563
+ }
27488
27564
  const server = createMmtMcpServer();
27489
27565
  const transport = new import_stdio.StdioServerTransport();
27490
27566
  await server.connect(transport);
package/dist/server.js CHANGED
@@ -8299,8 +8299,41 @@ var init_grpcCore = __esm({
8299
8299
  });
8300
8300
 
8301
8301
  // src/index.ts
8302
+ var import_module = require("module");
8302
8303
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
8303
8304
 
8305
+ // ../core/src/testlightHelp.ts
8306
+ var TESTLIGHT_NAME = "testlight";
8307
+ var TESTLIGHT_DESCRIPTION = "Multimeter CLI \u2014 run .mmt API tests, suites, and docs";
8308
+ var TESTLIGHT_HELP_AFTER = [
8309
+ "",
8310
+ "Run options:",
8311
+ " -q, --quiet Minimal output",
8312
+ " -o, --out <file> Write result JSON to file",
8313
+ " -i, --input <k=v...> Input variables (repeatable)",
8314
+ " -e, --env <k=v...> Environment variables (repeatable)",
8315
+ " -F, --env-file <path> Environment file (.mmt/.yaml)",
8316
+ " -P, --preset <name> Preset from env file (repeatable)",
8317
+ " -x, --example <name|#n> Named example or index (#1 is first)",
8318
+ " -p, --print-js Print generated JS before executing",
8319
+ " -r, --report <format> junit | mmt | html | md | md-detailed",
8320
+ " -R, --report-file <path> Report output path",
8321
+ "",
8322
+ "Examples:",
8323
+ " testlight run path/to/test.mmt",
8324
+ " testlight run path/to/test.mmt -F env.mmt -P runner.dev -P custom.prod",
8325
+ " testlight run path/to/suite.mmt --report html",
8326
+ " testlight scaffold test --from path/to/api.mmt",
8327
+ " testlight scaffold test --from path/to/api.mmt -o tests/api-smoke.mmt",
8328
+ " testlight docs test",
8329
+ " testlight validate path/to/test.mmt",
8330
+ " testlight suggest asserts --from path/to/api.mmt",
8331
+ " testlight update",
8332
+ " testlight update --check",
8333
+ "",
8334
+ "Run `testlight <command> --help` for command-specific options."
8335
+ ].join("\n");
8336
+
8304
8337
  // src/server.ts
8305
8338
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
8306
8339
  var import_zod = require("zod");
@@ -20707,9 +20740,6 @@ function matchesSource(filePath, src) {
20707
20740
  if (!fp || !s) {
20708
20741
  return false;
20709
20742
  }
20710
- if (s === ".") {
20711
- return true;
20712
- }
20713
20743
  if (/\.mmt$/i.test(s)) {
20714
20744
  return fp.endsWith(s) || fp === s;
20715
20745
  }
@@ -21102,9 +21132,6 @@ function matchesSource2(filePath, src) {
21102
21132
  if (!fp || !s) {
21103
21133
  return false;
21104
21134
  }
21105
- if (s === ".") {
21106
- return true;
21107
- }
21108
21135
  if (/\.mmt$/i.test(s)) {
21109
21136
  return fp.endsWith(s) || fp === s;
21110
21137
  }
@@ -24327,6 +24354,88 @@ function parseDurationMs(duration) {
24327
24354
  return void 0;
24328
24355
  }
24329
24356
 
24357
+ // ../core/src/agentDocs.ts
24358
+ var FULL_TOPIC_FILES = {
24359
+ overview: ["agent-workflow.md", "general.md", "generate.md"],
24360
+ workflow: ["agent-workflow.md"],
24361
+ test: ["generate-test.md"],
24362
+ api: ["generate-api.md"],
24363
+ loadtest: ["generate-loadtest.md"],
24364
+ suite: ["generate-suite.md"],
24365
+ env: ["generate-env.md"],
24366
+ doc: ["generate-doc.md"],
24367
+ constraints: ["generate-test-skill.md"]
24368
+ };
24369
+ var MIN_TOPIC_FILES = {
24370
+ overview: ["min/overview.md"],
24371
+ workflow: ["min/workflow.md"],
24372
+ test: ["min/test.md"],
24373
+ api: ["min/api.md"],
24374
+ loadtest: ["min/loadtest.md"],
24375
+ suite: ["min/suite.md"],
24376
+ env: ["min/env.md"],
24377
+ doc: ["min/doc.md"],
24378
+ constraints: ["min/constraints.md"]
24379
+ };
24380
+ var AGENT_DOC_FILE_TOPICS = Object.keys(MIN_TOPIC_FILES);
24381
+ function filesForAgentDocTopic(topic, pack = "min") {
24382
+ if (topic === "offline") {
24383
+ return ["offline-agent.md"];
24384
+ }
24385
+ const table = pack === "full" ? FULL_TOPIC_FILES : MIN_TOPIC_FILES;
24386
+ if (topic === "all") {
24387
+ return Array.from(new Set(Object.values(table).flat()));
24388
+ }
24389
+ return table[topic];
24390
+ }
24391
+ function loadAgentDocSections(opts) {
24392
+ const topic = opts.topic || "overview";
24393
+ const pack = opts.pack || "min";
24394
+ const files = filesForAgentDocTopic(topic, pack);
24395
+ return {
24396
+ topic,
24397
+ pack,
24398
+ sections: files.map((fileName) => ({
24399
+ fileName,
24400
+ content: opts.readText(fileName)
24401
+ }))
24402
+ };
24403
+ }
24404
+
24405
+ // ../core/src/validateMmt.ts
24406
+ function validateMmtContent(content, filePath, expectedType) {
24407
+ const detectedType = fileType(filePath || "", content);
24408
+ if (expectedType && detectedType && detectedType !== expectedType) {
24409
+ return {
24410
+ valid: false,
24411
+ detectedType,
24412
+ errors: [`Expected type "${expectedType}" but detected "${detectedType}"`]
24413
+ };
24414
+ }
24415
+ const type = expectedType || detectedType;
24416
+ try {
24417
+ if (type === "api") {
24418
+ yamlToAPIStrict(content);
24419
+ } else if (type === "test" || !type) {
24420
+ yamlToTestStrict(content);
24421
+ } else {
24422
+ return {
24423
+ valid: false,
24424
+ detectedType,
24425
+ errors: [`Validation for type "${type}" is not implemented yet`]
24426
+ };
24427
+ }
24428
+ return { valid: true, detectedType: type || detectedType, errors: [] };
24429
+ } catch (error) {
24430
+ const message = error?.message || String(error);
24431
+ return {
24432
+ valid: false,
24433
+ detectedType,
24434
+ errors: message.split("\n").filter(Boolean)
24435
+ };
24436
+ }
24437
+ }
24438
+
24330
24439
  // ../core/src/mmtFormatAst.ts
24331
24440
  var import_yaml3 = __toESM(require("yaml"));
24332
24441
  var ROOT_KEY_ORDER = {
@@ -26634,42 +26743,23 @@ function toWorkspaceRelative(workspaceRoot, fullPath) {
26634
26743
  // src/resources/documentation.ts
26635
26744
  var import_fs3 = __toESM(require("fs"), 1);
26636
26745
  var import_path3 = __toESM(require("path"), 1);
26637
- var FULL_TOPIC_FILES = {
26638
- overview: ["agent-workflow.md", "general.md", "generate.md"],
26639
- workflow: ["agent-workflow.md"],
26640
- test: ["generate-test.md"],
26641
- api: ["generate-api.md"],
26642
- loadtest: ["generate-loadtest.md"],
26643
- suite: ["generate-suite.md"],
26644
- env: ["generate-env.md"],
26645
- doc: ["generate-doc.md"],
26646
- constraints: ["generate-test-skill.md"]
26647
- };
26648
- var MIN_TOPIC_FILES = {
26649
- overview: ["min/overview.md"],
26650
- workflow: ["min/workflow.md"],
26651
- test: ["min/test.md"],
26652
- api: ["min/api.md"],
26653
- loadtest: ["min/loadtest.md"],
26654
- suite: ["min/suite.md"],
26655
- env: ["min/env.md"],
26656
- doc: ["min/doc.md"],
26657
- constraints: ["min/constraints.md"]
26658
- };
26659
26746
  function readDocumentation(topic = "overview", pack = "min") {
26660
- const table = pack === "full" ? FULL_TOPIC_FILES : MIN_TOPIC_FILES;
26661
- const files = topic === "all" ? Array.from(new Set(Object.values(table).flat())) : table[topic];
26662
- const sections = files.map((fileName) => {
26663
- const resource = GUIDE_RESOURCES.find((item) => item.fileName === fileName);
26747
+ const loaded = loadAgentDocSections({
26748
+ topic,
26749
+ pack,
26750
+ readText: readGuideContent
26751
+ });
26752
+ const sections = loaded.sections.map((section) => {
26753
+ const resource = GUIDE_RESOURCES.find((item) => item.fileName === section.fileName);
26664
26754
  return {
26665
- name: resource?.name || fileName.replace(/\.md$/, "").replace(/^min\//, ""),
26666
- fileName,
26667
- content: readGuideContent(fileName)
26755
+ name: resource?.name || section.fileName.replace(/\.md$/, "").replace(/^min\//, ""),
26756
+ fileName: section.fileName,
26757
+ content: section.content
26668
26758
  };
26669
26759
  });
26670
26760
  return {
26671
- topic,
26672
- pack,
26761
+ topic: loaded.topic,
26762
+ pack: loaded.pack,
26673
26763
  sections,
26674
26764
  usage: pack === "min" ? 'Default min pack. Request pack: "full" only when you need rare syntax.' : 'Full documentation pack. Prefer pack: "min" for routine generate/modify.'
26675
26765
  };
@@ -26769,33 +26859,6 @@ function listExamples(options) {
26769
26859
  }
26770
26860
 
26771
26861
  // src/tools/handlers.ts
26772
- function detectType(content, filePath) {
26773
- if (filePath) {
26774
- return JSer_exports.fileType(filePath, content);
26775
- }
26776
- if (content.includes("type: api")) {
26777
- return "api";
26778
- }
26779
- if (content.includes("type: test")) {
26780
- return "test";
26781
- }
26782
- if (content.includes("type: env")) {
26783
- return "env";
26784
- }
26785
- if (content.includes("type: suite")) {
26786
- return "suite";
26787
- }
26788
- if (content.includes("type: doc")) {
26789
- return "doc";
26790
- }
26791
- if (content.includes("type: server")) {
26792
- return "server";
26793
- }
26794
- if (content.includes("type: loadtest")) {
26795
- return "loadtest";
26796
- }
26797
- return null;
26798
- }
26799
26862
  function buildValidationSuggestions(errors) {
26800
26863
  const suggestions = /* @__PURE__ */ new Set();
26801
26864
  for (const error of errors) {
@@ -26824,37 +26887,11 @@ function buildValidationSuggestions(errors) {
26824
26887
  return Array.from(suggestions);
26825
26888
  }
26826
26889
  function validateContent(content, filePath, expectedType) {
26827
- const detectedType = detectType(content, filePath);
26828
- if (expectedType && detectedType && detectedType !== expectedType) {
26829
- const errors = [`Expected type "${expectedType}" but detected "${detectedType}"`];
26830
- return {
26831
- valid: false,
26832
- detectedType,
26833
- errors,
26834
- suggestions: buildValidationSuggestions(errors)
26835
- };
26836
- }
26837
- try {
26838
- const type = expectedType || detectedType;
26839
- if (type === "api") {
26840
- apiParsePack_exports.yamlToAPIStrict(content);
26841
- } else if (type === "test" || !type) {
26842
- testParsePack_exports.yamlToTestStrict(content);
26843
- } else {
26844
- const errors = [`Validation for type "${type}" is not implemented yet`];
26845
- return { valid: false, detectedType, errors, suggestions: buildValidationSuggestions(errors) };
26846
- }
26847
- return { valid: true, detectedType: type || detectedType, errors: [], suggestions: [] };
26848
- } catch (error) {
26849
- const message = error?.message || String(error);
26850
- const errors = message.split("\n").filter(Boolean);
26851
- return {
26852
- valid: false,
26853
- detectedType,
26854
- errors,
26855
- suggestions: buildValidationSuggestions(errors)
26856
- };
26857
- }
26890
+ const result = validateMmtContent(content, filePath, expectedType);
26891
+ return {
26892
+ ...result,
26893
+ suggestions: result.valid ? [] : buildValidationSuggestions(result.errors)
26894
+ };
26858
26895
  }
26859
26896
  async function handleReadDocumentation(args) {
26860
26897
  try {
@@ -27484,7 +27521,46 @@ function createMmtMcpServer() {
27484
27521
  }
27485
27522
 
27486
27523
  // src/index.ts
27524
+ var requireFromHere = (0, import_module.createRequire)(__filename);
27525
+ function resolveMcpVersion() {
27526
+ try {
27527
+ const version = requireFromHere("../package.json").version;
27528
+ if (typeof version === "string" && version) {
27529
+ return version;
27530
+ }
27531
+ } catch {
27532
+ }
27533
+ return "0.0.0";
27534
+ }
27535
+ function printHelp() {
27536
+ const version = resolveMcpVersion();
27537
+ process.stdout.write([
27538
+ `mmt-mcp ${version}`,
27539
+ "",
27540
+ "Multimeter MCP server over stdio.",
27541
+ "Tools: read_documentation, list_examples, discover_api, api_card,",
27542
+ " scaffold_test, suggest_assertions, validate, format, run",
27543
+ "",
27544
+ "Usage: npx -y mmt-mcp",
27545
+ "Docs: https://mmt.dev/docs/features/mcp",
27546
+ "",
27547
+ `${TESTLIGHT_NAME} \u2014 ${TESTLIGHT_DESCRIPTION}`,
27548
+ "npm (mmt-testlight) and GitHub/Homebrew binaries share this CLI:",
27549
+ TESTLIGHT_HELP_AFTER,
27550
+ ""
27551
+ ].join("\n"));
27552
+ }
27487
27553
  async function main() {
27554
+ const args = process.argv.slice(2);
27555
+ if (args.includes("--help") || args.includes("-h")) {
27556
+ printHelp();
27557
+ return;
27558
+ }
27559
+ if (args.includes("--version") || args.includes("-v")) {
27560
+ process.stdout.write(`${resolveMcpVersion()}
27561
+ `);
27562
+ return;
27563
+ }
27488
27564
  const server = createMmtMcpServer();
27489
27565
  const transport = new import_stdio.StdioServerTransport();
27490
27566
  await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mmt-mcp",
3
- "version": "1.42.2-pre",
3
+ "version": "1.42.4",
4
4
  "description": "Multimeter MCP server for Cursor, Copilot, and Claude. Scaffold, validate, format, and run YAML .mmt API tests. Do not invent .mmt syntax.",
5
5
  "mcpName": "io.github.mshobeyri/multimeter",
6
6
  "type": "module",