mmt-mcp 1.45.0-pre → 1.45.2-pre

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.
@@ -11,7 +11,7 @@ Use {{btn:list-tree}} in the header to pick **All** (one test) or a single reque
11
11
  - `get_profile.bru`: a simple Bruno GET request with variables, headers, bearer auth, query params, and assertions.
12
12
  - `create_user_json.bru`: a POST request with a JSON body and bearer auth.
13
13
  - `update_profile_form.bru`: a PUT request with a form-urlencoded body.
14
- - `xml_echo.bru`: a POST request with an XML text body.
14
+ - `xml_echo.bru`: a POST request with an XML text body to `test.mmt.dev/echo/xml`.
15
15
  - `import_bruno_in_test.mmt`: a Multimeter test that imports a `.bru` file and calls it like any other test import.
16
16
 
17
17
  ## Importing Bruno Files From MMT
@@ -2,7 +2,7 @@
2
2
 
3
3
  Intermediate example covering every HTTP request `format:` — `json`, `xml`, `xmle`, `text`, `urlencoded`, `binary`, and `multipart`.
4
4
 
5
- All requests POST to [test.mmt.dev/echo](https://test.mmt.dev/echo) and read fields back from the echoed payload.
5
+ Most requests POST to [test.mmt.dev/echo](https://test.mmt.dev/echo). XML formats (`post_xml.mmt`, `post_xmle.mmt`) use [test.mmt.dev/echo/xml](https://test.mmt.dev/echo/xml) so the response is `application/xml`.
6
6
 
7
7
  ## Files
8
8
 
@@ -6,7 +6,7 @@ inputs:
6
6
  outputs:
7
7
  echoed_name: body[/<name>(.*?)</name>/]
8
8
  status_code: status
9
- url: https://test.mmt.dev/echo
9
+ url: https://test.mmt.dev/echo/xml
10
10
  method: post
11
11
  format: xml
12
12
  body:
@@ -7,7 +7,7 @@ outputs:
7
7
  echoed_name: body[/<name>(.*?)</name>/]
8
8
  has_expanded_meta: body[/<meta><\/meta>/]
9
9
  status_code: status
10
- url: https://test.mmt.dev/echo
10
+ url: https://test.mmt.dev/echo/xml
11
11
  method: post
12
12
  format: xmle
13
13
  body:
@@ -1,6 +1,6 @@
1
1
  # Token resolution
2
2
 
3
- Professional samples of `e:`, `i:`, `r:`, and `c:` against [test.mmt.dev/echo](https://test.mmt.dev/echo).
3
+ Professional samples of `e:`, `i:`, `r:`, and `c:` against [test.mmt.dev/echo](https://test.mmt.dev/echo). XML body samples use [test.mmt.dev/echo/xml](https://test.mmt.dev/echo/xml) and read fields from the XML echo (`body.echo.body.body…`).
4
4
 
5
5
  A whole field in a **JSON** body keeps the value's type. **XML**, **urlencoded**, **headers**, and **query** always send text. A quoted token such as `"e:account_age"` is the literal text. `omit` removes the field. `"omit"` sends the word omit. A missing `e:`, `i:`, `r:`, or `c:` token is sent as its own text, for example `e:region` or `i:nickname`.
6
6
 
@@ -2,22 +2,22 @@ type: api
2
2
  title: Current tokens in an XML body
3
3
  description: "XML element text is always a string, including c:epoch."
4
4
  outputs:
5
- day_name: body.body.day_name
6
- epoch_seconds: body.body.epoch_seconds
7
- city: body.body.city
8
- missing_token: body.body.missing_token
9
- literal_token: body.body.literal_token
10
- literal_omit: body.body.literal_omit
11
- angle_whole: body.body.angle_whole
12
- quoted_angle: body.body.quoted_angle
13
- angle_prefix: body.body.angle_prefix
14
- angle_suffix: body.body.angle_suffix
15
- angle_both: body.body.angle_both
16
- quoted_curly: body.body.quoted_curly
17
- curly_whole: body.body.curly_whole
18
- text_slice: body.body.text_slice
5
+ day_name: body.echo.body.body.day_name
6
+ epoch_seconds: body.echo.body.body.epoch_seconds
7
+ city: body.echo.body.body.city
8
+ missing_token: body.echo.body.body.missing_token
9
+ literal_token: body.echo.body.body.literal_token
10
+ literal_omit: body.echo.body.body.literal_omit
11
+ angle_whole: body.echo.body.body.angle_whole
12
+ quoted_angle: body.echo.body.body.quoted_angle
13
+ angle_prefix: body.echo.body.body.angle_prefix
14
+ angle_suffix: body.echo.body.body.angle_suffix
15
+ angle_both: body.echo.body.body.angle_both
16
+ quoted_curly: body.echo.body.body.quoted_curly
17
+ curly_whole: body.echo.body.body.curly_whole
18
+ text_slice: body.echo.body.body.text_slice
19
19
  status_code: status
20
- url: https://test.mmt.dev/echo
20
+ url: https://test.mmt.dev/echo/xml
21
21
  method: post
22
22
  format: xml
23
23
  body:
@@ -2,24 +2,24 @@ type: api
2
2
  title: Env tokens in an XML body
3
3
  description: "XML element text is always a string, including numbers and booleans from e:."
4
4
  outputs:
5
- account_age: body.body.account_age
6
- feature_enabled: body.body.feature_enabled
7
- plan_code: body.body.plan_code
8
- plan_label: body.body.plan_label
9
- service_url: body.body.service_url
10
- missing_region: body.body.missing_region
11
- literal_token: body.body.literal_token
12
- literal_omit: body.body.literal_omit
13
- angle_whole: body.body.angle_whole
14
- quoted_angle: body.body.quoted_angle
15
- angle_prefix: body.body.angle_prefix
16
- angle_suffix: body.body.angle_suffix
17
- angle_both: body.body.angle_both
18
- quoted_curly: body.body.quoted_curly
19
- curly_whole: body.body.curly_whole
20
- text_slice: body.body.text_slice
5
+ account_age: body.echo.body.body.account_age
6
+ feature_enabled: body.echo.body.body.feature_enabled
7
+ plan_code: body.echo.body.body.plan_code
8
+ plan_label: body.echo.body.body.plan_label
9
+ service_url: body.echo.body.body.service_url
10
+ missing_region: body.echo.body.body.missing_region
11
+ literal_token: body.echo.body.body.literal_token
12
+ literal_omit: body.echo.body.body.literal_omit
13
+ angle_whole: body.echo.body.body.angle_whole
14
+ quoted_angle: body.echo.body.body.quoted_angle
15
+ angle_prefix: body.echo.body.body.angle_prefix
16
+ angle_suffix: body.echo.body.body.angle_suffix
17
+ angle_both: body.echo.body.body.angle_both
18
+ quoted_curly: body.echo.body.body.quoted_curly
19
+ curly_whole: body.echo.body.body.curly_whole
20
+ text_slice: body.echo.body.body.text_slice
21
21
  status_code: status
22
- url: https://test.mmt.dev/echo
22
+ url: https://test.mmt.dev/echo/xml
23
23
  method: post
24
24
  format: xml
25
25
  body:
@@ -9,32 +9,32 @@ inputs:
9
9
  version: "1.0"
10
10
  price: 9.5
11
11
  outputs:
12
- username: body.body.username
13
- age: body.body.age
14
- active: body.body.active
15
- city: body.body.city
16
- missing_nickname: body.body.missing_nickname
17
- literal_token: body.body.literal_token
18
- literal_omit: body.body.literal_omit
19
- angle_whole: body.body.angle_whole
20
- quoted_angle: body.body.quoted_angle
21
- angle_prefix: body.body.angle_prefix
22
- angle_suffix: body.body.angle_suffix
23
- angle_both: body.body.angle_both
24
- quoted_curly: body.body.quoted_curly
25
- curly_whole: body.body.curly_whole
26
- text_slice: body.body.text_slice
27
- missing_embed: body.body.missing_embed
28
- attr_number: body.body.item.number
29
- attr_bool: body.body.item.flag
30
- attr_text: body.body.item.version
31
- attr_angle: body.body.item.angle
32
- attr_embedded: body.body.item.embedded
33
- attr_literal: body.body.item.literal
34
- attr_decimal: body.body.item.decimal
35
- price_text: body.body.price
12
+ username: body.echo.body.body.username
13
+ age: body.echo.body.body.age
14
+ active: body.echo.body.body.active
15
+ city: body.echo.body.body.city
16
+ missing_nickname: body.echo.body.body.missing_nickname
17
+ literal_token: body.echo.body.body.literal_token
18
+ literal_omit: body.echo.body.body.literal_omit
19
+ angle_whole: body.echo.body.body.angle_whole
20
+ quoted_angle: body.echo.body.body.quoted_angle
21
+ angle_prefix: body.echo.body.body.angle_prefix
22
+ angle_suffix: body.echo.body.body.angle_suffix
23
+ angle_both: body.echo.body.body.angle_both
24
+ quoted_curly: body.echo.body.body.quoted_curly
25
+ curly_whole: body.echo.body.body.curly_whole
26
+ text_slice: body.echo.body.body.text_slice
27
+ missing_embed: body.echo.body.body.missing_embed
28
+ attr_number: body.echo.body.body.item.number
29
+ attr_bool: body.echo.body.body.item.flag
30
+ attr_text: body.echo.body.body.item.version
31
+ attr_angle: body.echo.body.body.item.angle
32
+ attr_embedded: body.echo.body.body.item.embedded
33
+ attr_literal: body.echo.body.body.item.literal
34
+ attr_decimal: body.echo.body.body.item.decimal
35
+ price_text: body.echo.body.body.price
36
36
  status_code: status
37
- url: https://test.mmt.dev/echo
37
+ url: https://test.mmt.dev/echo/xml
38
38
  method: post
39
39
  format: xml
40
40
  body:
@@ -2,21 +2,21 @@ type: api
2
2
  title: Random tokens in an XML body
3
3
  description: "XML element text is always a string, including r:int and r:bool."
4
4
  outputs:
5
- request_id: body.body.request_id
6
- lucky_number: body.body.lucky_number
7
- active: body.body.active
8
- missing_token: body.body.missing_token
9
- literal_token: body.body.literal_token
10
- literal_omit: body.body.literal_omit
11
- angle_whole: body.body.angle_whole
12
- quoted_angle: body.body.quoted_angle
13
- angle_prefix: body.body.angle_prefix
14
- angle_suffix: body.body.angle_suffix
15
- angle_both: body.body.angle_both
16
- quoted_curly: body.body.quoted_curly
17
- curly_whole: body.body.curly_whole
5
+ request_id: body.echo.body.body.request_id
6
+ lucky_number: body.echo.body.body.lucky_number
7
+ active: body.echo.body.body.active
8
+ missing_token: body.echo.body.body.missing_token
9
+ literal_token: body.echo.body.body.literal_token
10
+ literal_omit: body.echo.body.body.literal_omit
11
+ angle_whole: body.echo.body.body.angle_whole
12
+ quoted_angle: body.echo.body.body.quoted_angle
13
+ angle_prefix: body.echo.body.body.angle_prefix
14
+ angle_suffix: body.echo.body.body.angle_suffix
15
+ angle_both: body.echo.body.body.angle_both
16
+ quoted_curly: body.echo.body.body.quoted_curly
17
+ curly_whole: body.echo.body.body.curly_whole
18
18
  status_code: status
19
- url: https://test.mmt.dev/echo
19
+ url: https://test.mmt.dev/echo/xml
20
20
  method: post
21
21
  format: xml
22
22
  body:
package/dist/server.cjs CHANGED
@@ -13865,7 +13865,9 @@ function formatHtmlBody(html) {
13865
13865
 
13866
13866
  // ../core/src/expectOperatorYaml.ts
13867
13867
  var YAML_UNSAFE_OPS = opsList.filter((op) => op.startsWith("!") || op.startsWith(">")).sort((a, b) => b.length - a.length);
13868
+ var EMIT_UNQUOTE_OPS = [...opsList].sort((a, b) => b.length - a.length);
13868
13869
  var FUZZY_PERCENT_OP_RE = /^[>](?:0|[1-9][0-9]?|100)%(?:\s|$)/;
13870
+ var EMIT_FUZZY_PERCENT_OP_RE = /^[<>](?:0|[1-9][0-9]?|100)%(?:\s|$)/;
13869
13871
  var TIME_TILDE_OP_RE = new RegExp(`^${TIME_OPERATOR_PATTERN}(?:\\s|$)`);
13870
13872
  var EXACT_TIME_TILDE_OP_RE = new RegExp(`^${TIME_OPERATOR_PATTERN}$`);
13871
13873
  var MAP_ENTRY_RE = /^([^:]+?:\s+)(.+)$/;
@@ -13899,7 +13901,6 @@ function quoteExpectOperators(yaml) {
13899
13901
  }
13900
13902
  return joinLines(lines, eol);
13901
13903
  }
13902
- var YAML_BANG_UNSAFE_OPS = YAML_UNSAFE_OPS.filter((op) => op.startsWith("!"));
13903
13904
  var QUOTED_SCALAR_RE = /^"((?:\\.|[^"\\])*)"$/;
13904
13905
  function emitUnquotedOperators(yaml) {
13905
13906
  const eol = detectNewline(yaml);
@@ -13936,14 +13937,14 @@ function unescapeQuotedScalar(value) {
13936
13937
  }
13937
13938
  return match[1].replace(/\\"/g, '"').replace(/\\\\/g, "\\");
13938
13939
  }
13939
- function needsBangOperatorQuoting(value) {
13940
+ function canEmitUnquotedOperatorValue(value) {
13940
13941
  if (/^["']/.test(value)) {
13941
13942
  return false;
13942
13943
  }
13943
- if (FUZZY_PERCENT_OP_RE.test(value) || TIME_TILDE_OP_RE.test(value)) {
13944
+ if (EMIT_FUZZY_PERCENT_OP_RE.test(value) || TIME_TILDE_OP_RE.test(value)) {
13944
13945
  return true;
13945
13946
  }
13946
- for (const op of YAML_BANG_UNSAFE_OPS) {
13947
+ for (const op of EMIT_UNQUOTE_OPS) {
13947
13948
  if (value === op || value.startsWith(op + " ")) {
13948
13949
  return true;
13949
13950
  }
@@ -13954,10 +13955,10 @@ function canEmitUnquotedExactOperator(value) {
13954
13955
  if (/^["']/.test(value)) {
13955
13956
  return false;
13956
13957
  }
13957
- if (EXACT_FUZZY_PERCENT_OP_RE.test(value) || EXACT_TIME_TILDE_OP_RE.test(value)) {
13958
+ if (EXACT_EMIT_FUZZY_PERCENT_OP_RE.test(value) || EXACT_TIME_TILDE_OP_RE.test(value)) {
13958
13959
  return true;
13959
13960
  }
13960
- return YAML_BANG_UNSAFE_OPS.some((op) => op === value);
13961
+ return EMIT_UNQUOTE_OPS.some((op) => op === value);
13961
13962
  }
13962
13963
  function unquoteExpectLineIfNeeded(line) {
13963
13964
  const trimmed = line.trimStart();
@@ -13966,7 +13967,7 @@ function unquoteExpectLineIfNeeded(line) {
13966
13967
  if (mapMatch) {
13967
13968
  const [, prefix, value] = mapMatch;
13968
13969
  const inner = unescapeQuotedScalar(value.trim());
13969
- if (inner !== void 0 && needsBangOperatorQuoting(inner)) {
13970
+ if (inner !== void 0 && canEmitUnquotedOperatorValue(inner)) {
13970
13971
  return leadingWS + prefix + inner;
13971
13972
  }
13972
13973
  return line;
@@ -13975,7 +13976,7 @@ function unquoteExpectLineIfNeeded(line) {
13975
13976
  if (arrayMatch) {
13976
13977
  const [, prefix, value] = arrayMatch;
13977
13978
  const inner = unescapeQuotedScalar(value.trim());
13978
- if (inner !== void 0 && needsBangOperatorQuoting(inner)) {
13979
+ if (inner !== void 0 && canEmitUnquotedOperatorValue(inner)) {
13979
13980
  return leadingWS + prefix + inner;
13980
13981
  }
13981
13982
  }
@@ -14034,6 +14035,7 @@ function needsOperatorQuoting(value) {
14034
14035
  return false;
14035
14036
  }
14036
14037
  var EXACT_FUZZY_PERCENT_OP_RE = /^[>](?:0|[1-9][0-9]?|100)%$/;
14038
+ var EXACT_EMIT_FUZZY_PERCENT_OP_RE = /^[<>](?:0|[1-9][0-9]?|100)%$/;
14037
14039
  function needsExactOperatorQuoting(value) {
14038
14040
  if (/^["']/.test(value)) {
14039
14041
  return false;
@@ -26656,7 +26658,18 @@ ${helperFactorySource}
26656
26658
  const __mmt_exampleLabel = ${exampleLiteral};
26657
26659
  ` + helperDestructure + "\n" + (opts.apiFunctionSource ? `${indentMultiline(opts.apiFunctionSource, " ")}
26658
26660
 
26659
- ` : "") + ` const __mmt_originalSend = send_;
26661
+ ` : "") + // Direct API runs only (this wrapper). Report wire request/response for
26662
+ // History; imported call: from tests does not use this wrap.
26663
+ ` const __mmt_reportNetwork = (scope, request, response) => {
26664
+ if (typeof __reporter !== 'function') { return; }
26665
+ try {
26666
+ const payload = { scope: scope, request: request || {} };
26667
+ if (response !== undefined) { payload.response = response; }
26668
+ __reporter(payload);
26669
+ } catch (_e) {}
26670
+ };
26671
+ const __mmt_originalSend = send_;
26672
+ const __mmt_originalSendGrpc = typeof sendGrpc_ === 'function' ? sendGrpc_ : null;
26660
26673
  send_ = async function(req) {
26661
26674
  await checkAbort_();
26662
26675
  const __req = req || {};
@@ -26687,6 +26700,7 @@ ${helperFactorySource}
26687
26700
  body: __mmt_formatBodyValue(__req.body)
26688
26701
  };
26689
26702
  console.debug(__mmt_formatSection('Request:', __reqLog));
26703
+ __mmt_reportNetwork('network-request', __req);
26690
26704
  try {
26691
26705
  const __res = await __mmt_originalSend(req);
26692
26706
  // Stop before Response logging when the user cancelled mid-request.
@@ -26708,6 +26722,9 @@ ${helperFactorySource}
26708
26722
  if (__warning) {
26709
26723
  console.warn(__warning);
26710
26724
  }
26725
+ __mmt_reportNetwork(
26726
+ (typeof __status === 'number' && __status < 0) ? 'network-error' : 'network-response',
26727
+ __req, __res);
26711
26728
  // Return network failures to the API function so outputs (and the
26712
26729
  // tester Response panel) still receive the response; the wrapper
26713
26730
  // marks the run failed after outputs are built.
@@ -26736,6 +26753,8 @@ ${helperFactorySource}
26736
26753
  };
26737
26754
  console.log(__mmt_formatSection('Response:', __resLog));
26738
26755
  console.warn(__warning);
26756
+ __mmt_reportNetwork(
26757
+ __res.status < 0 ? 'network-error' : 'network-response', __req, __res);
26739
26758
  return __res;
26740
26759
  }
26741
26760
  const __errorLog = {
@@ -26750,9 +26769,41 @@ ${helperFactorySource}
26750
26769
  }
26751
26770
  }
26752
26771
  console.error(__mmt_formatSection('Response:', __errorLog));
26772
+ __mmt_reportNetwork('network-error', __req, {
26773
+ status: err && typeof err.status === 'number' ? err.status : -1,
26774
+ statusText: err && err.message ? String(err.message) : String(err),
26775
+ body: err && err.body,
26776
+ headers: err && err.headers || {},
26777
+ duration: err && typeof err.duration === 'number' ? err.duration : -1
26778
+ });
26753
26779
  throw err;
26754
26780
  }
26755
26781
  };
26782
+ if (__mmt_originalSendGrpc) {
26783
+ sendGrpc_ = async function(req) {
26784
+ await checkAbort_();
26785
+ const __req = req || {};
26786
+ __mmt_reportNetwork('network-request', __req);
26787
+ try {
26788
+ const __res = await __mmt_originalSendGrpc(req);
26789
+ await checkAbort_();
26790
+ __mmt_reportNetwork('network-response', __req, __res);
26791
+ return __res;
26792
+ } catch (err) {
26793
+ if (err && (err.kind === 'test-abort' || (typeof isTestAbortError === 'function' && isTestAbortError(err)))) {
26794
+ throw err;
26795
+ }
26796
+ __mmt_reportNetwork('network-error', __req, {
26797
+ status: err && typeof err.status === 'number' ? err.status : -1,
26798
+ statusText: err && err.message ? String(err.message) : String(err),
26799
+ body: err && err.body,
26800
+ headers: err && err.headers || {},
26801
+ duration: err && typeof err.duration === 'number' ? err.duration : -1
26802
+ });
26803
+ throw err;
26804
+ }
26805
+ };
26806
+ }
26756
26807
 
26757
26808
  try {
26758
26809
  if (__mmt_exampleLabel) {
@@ -26814,6 +26865,7 @@ ${helperFactorySource}
26814
26865
  return result;
26815
26866
  } finally {
26816
26867
  send_ = __mmt_originalSend;
26868
+ if (__mmt_originalSendGrpc) { sendGrpc_ = __mmt_originalSendGrpc; }
26817
26869
  }
26818
26870
  })();`;
26819
26871
  }
@@ -26866,10 +26918,15 @@ async function executeApi(prepared, options, preLogs) {
26866
26918
  exampleOutputs,
26867
26919
  checkTitle: fileDisplayName
26868
26920
  });
26869
- const apiSetenvReporter = options.__mmtIsSuiteBundleChildRun === true && typeof options.reporter === "function" ? (event) => {
26870
- if (event && event.scope === "setenv") {
26871
- options.reporter({ ...event, id: event.id || options.id });
26921
+ const apiReporter = typeof options.reporter === "function" ? (event) => {
26922
+ const isSuiteChild = options.__mmtIsSuiteBundleChildRun === true;
26923
+ if (isSuiteChild && event && event.scope !== "setenv") {
26924
+ return;
26872
26925
  }
26926
+ options.reporter({
26927
+ ...event,
26928
+ id: event.id || options.id
26929
+ });
26873
26930
  } : void 0;
26874
26931
  const result = await runGeneratedJs(
26875
26932
  "run-api",
@@ -26880,7 +26937,7 @@ async function executeApi(prepared, options, preLogs) {
26880
26937
  void 0,
26881
26938
  options.id,
26882
26939
  fileLoader,
26883
- apiSetenvReporter,
26940
+ apiReporter,
26884
26941
  options.abortSignal,
26885
26942
  void 0,
26886
26943
  prepared.filePath ? prepared.filePath.split(/[/\\]/).slice(0, -1).join("/") : void 0,
package/dist/server.js CHANGED
@@ -13865,7 +13865,9 @@ function formatHtmlBody(html) {
13865
13865
 
13866
13866
  // ../core/src/expectOperatorYaml.ts
13867
13867
  var YAML_UNSAFE_OPS = opsList.filter((op) => op.startsWith("!") || op.startsWith(">")).sort((a, b) => b.length - a.length);
13868
+ var EMIT_UNQUOTE_OPS = [...opsList].sort((a, b) => b.length - a.length);
13868
13869
  var FUZZY_PERCENT_OP_RE = /^[>](?:0|[1-9][0-9]?|100)%(?:\s|$)/;
13870
+ var EMIT_FUZZY_PERCENT_OP_RE = /^[<>](?:0|[1-9][0-9]?|100)%(?:\s|$)/;
13869
13871
  var TIME_TILDE_OP_RE = new RegExp(`^${TIME_OPERATOR_PATTERN}(?:\\s|$)`);
13870
13872
  var EXACT_TIME_TILDE_OP_RE = new RegExp(`^${TIME_OPERATOR_PATTERN}$`);
13871
13873
  var MAP_ENTRY_RE = /^([^:]+?:\s+)(.+)$/;
@@ -13899,7 +13901,6 @@ function quoteExpectOperators(yaml) {
13899
13901
  }
13900
13902
  return joinLines(lines, eol);
13901
13903
  }
13902
- var YAML_BANG_UNSAFE_OPS = YAML_UNSAFE_OPS.filter((op) => op.startsWith("!"));
13903
13904
  var QUOTED_SCALAR_RE = /^"((?:\\.|[^"\\])*)"$/;
13904
13905
  function emitUnquotedOperators(yaml) {
13905
13906
  const eol = detectNewline(yaml);
@@ -13936,14 +13937,14 @@ function unescapeQuotedScalar(value) {
13936
13937
  }
13937
13938
  return match[1].replace(/\\"/g, '"').replace(/\\\\/g, "\\");
13938
13939
  }
13939
- function needsBangOperatorQuoting(value) {
13940
+ function canEmitUnquotedOperatorValue(value) {
13940
13941
  if (/^["']/.test(value)) {
13941
13942
  return false;
13942
13943
  }
13943
- if (FUZZY_PERCENT_OP_RE.test(value) || TIME_TILDE_OP_RE.test(value)) {
13944
+ if (EMIT_FUZZY_PERCENT_OP_RE.test(value) || TIME_TILDE_OP_RE.test(value)) {
13944
13945
  return true;
13945
13946
  }
13946
- for (const op of YAML_BANG_UNSAFE_OPS) {
13947
+ for (const op of EMIT_UNQUOTE_OPS) {
13947
13948
  if (value === op || value.startsWith(op + " ")) {
13948
13949
  return true;
13949
13950
  }
@@ -13954,10 +13955,10 @@ function canEmitUnquotedExactOperator(value) {
13954
13955
  if (/^["']/.test(value)) {
13955
13956
  return false;
13956
13957
  }
13957
- if (EXACT_FUZZY_PERCENT_OP_RE.test(value) || EXACT_TIME_TILDE_OP_RE.test(value)) {
13958
+ if (EXACT_EMIT_FUZZY_PERCENT_OP_RE.test(value) || EXACT_TIME_TILDE_OP_RE.test(value)) {
13958
13959
  return true;
13959
13960
  }
13960
- return YAML_BANG_UNSAFE_OPS.some((op) => op === value);
13961
+ return EMIT_UNQUOTE_OPS.some((op) => op === value);
13961
13962
  }
13962
13963
  function unquoteExpectLineIfNeeded(line) {
13963
13964
  const trimmed = line.trimStart();
@@ -13966,7 +13967,7 @@ function unquoteExpectLineIfNeeded(line) {
13966
13967
  if (mapMatch) {
13967
13968
  const [, prefix, value] = mapMatch;
13968
13969
  const inner = unescapeQuotedScalar(value.trim());
13969
- if (inner !== void 0 && needsBangOperatorQuoting(inner)) {
13970
+ if (inner !== void 0 && canEmitUnquotedOperatorValue(inner)) {
13970
13971
  return leadingWS + prefix + inner;
13971
13972
  }
13972
13973
  return line;
@@ -13975,7 +13976,7 @@ function unquoteExpectLineIfNeeded(line) {
13975
13976
  if (arrayMatch) {
13976
13977
  const [, prefix, value] = arrayMatch;
13977
13978
  const inner = unescapeQuotedScalar(value.trim());
13978
- if (inner !== void 0 && needsBangOperatorQuoting(inner)) {
13979
+ if (inner !== void 0 && canEmitUnquotedOperatorValue(inner)) {
13979
13980
  return leadingWS + prefix + inner;
13980
13981
  }
13981
13982
  }
@@ -14034,6 +14035,7 @@ function needsOperatorQuoting(value) {
14034
14035
  return false;
14035
14036
  }
14036
14037
  var EXACT_FUZZY_PERCENT_OP_RE = /^[>](?:0|[1-9][0-9]?|100)%$/;
14038
+ var EXACT_EMIT_FUZZY_PERCENT_OP_RE = /^[<>](?:0|[1-9][0-9]?|100)%$/;
14037
14039
  function needsExactOperatorQuoting(value) {
14038
14040
  if (/^["']/.test(value)) {
14039
14041
  return false;
@@ -26656,7 +26658,18 @@ ${helperFactorySource}
26656
26658
  const __mmt_exampleLabel = ${exampleLiteral};
26657
26659
  ` + helperDestructure + "\n" + (opts.apiFunctionSource ? `${indentMultiline(opts.apiFunctionSource, " ")}
26658
26660
 
26659
- ` : "") + ` const __mmt_originalSend = send_;
26661
+ ` : "") + // Direct API runs only (this wrapper). Report wire request/response for
26662
+ // History; imported call: from tests does not use this wrap.
26663
+ ` const __mmt_reportNetwork = (scope, request, response) => {
26664
+ if (typeof __reporter !== 'function') { return; }
26665
+ try {
26666
+ const payload = { scope: scope, request: request || {} };
26667
+ if (response !== undefined) { payload.response = response; }
26668
+ __reporter(payload);
26669
+ } catch (_e) {}
26670
+ };
26671
+ const __mmt_originalSend = send_;
26672
+ const __mmt_originalSendGrpc = typeof sendGrpc_ === 'function' ? sendGrpc_ : null;
26660
26673
  send_ = async function(req) {
26661
26674
  await checkAbort_();
26662
26675
  const __req = req || {};
@@ -26687,6 +26700,7 @@ ${helperFactorySource}
26687
26700
  body: __mmt_formatBodyValue(__req.body)
26688
26701
  };
26689
26702
  console.debug(__mmt_formatSection('Request:', __reqLog));
26703
+ __mmt_reportNetwork('network-request', __req);
26690
26704
  try {
26691
26705
  const __res = await __mmt_originalSend(req);
26692
26706
  // Stop before Response logging when the user cancelled mid-request.
@@ -26708,6 +26722,9 @@ ${helperFactorySource}
26708
26722
  if (__warning) {
26709
26723
  console.warn(__warning);
26710
26724
  }
26725
+ __mmt_reportNetwork(
26726
+ (typeof __status === 'number' && __status < 0) ? 'network-error' : 'network-response',
26727
+ __req, __res);
26711
26728
  // Return network failures to the API function so outputs (and the
26712
26729
  // tester Response panel) still receive the response; the wrapper
26713
26730
  // marks the run failed after outputs are built.
@@ -26736,6 +26753,8 @@ ${helperFactorySource}
26736
26753
  };
26737
26754
  console.log(__mmt_formatSection('Response:', __resLog));
26738
26755
  console.warn(__warning);
26756
+ __mmt_reportNetwork(
26757
+ __res.status < 0 ? 'network-error' : 'network-response', __req, __res);
26739
26758
  return __res;
26740
26759
  }
26741
26760
  const __errorLog = {
@@ -26750,9 +26769,41 @@ ${helperFactorySource}
26750
26769
  }
26751
26770
  }
26752
26771
  console.error(__mmt_formatSection('Response:', __errorLog));
26772
+ __mmt_reportNetwork('network-error', __req, {
26773
+ status: err && typeof err.status === 'number' ? err.status : -1,
26774
+ statusText: err && err.message ? String(err.message) : String(err),
26775
+ body: err && err.body,
26776
+ headers: err && err.headers || {},
26777
+ duration: err && typeof err.duration === 'number' ? err.duration : -1
26778
+ });
26753
26779
  throw err;
26754
26780
  }
26755
26781
  };
26782
+ if (__mmt_originalSendGrpc) {
26783
+ sendGrpc_ = async function(req) {
26784
+ await checkAbort_();
26785
+ const __req = req || {};
26786
+ __mmt_reportNetwork('network-request', __req);
26787
+ try {
26788
+ const __res = await __mmt_originalSendGrpc(req);
26789
+ await checkAbort_();
26790
+ __mmt_reportNetwork('network-response', __req, __res);
26791
+ return __res;
26792
+ } catch (err) {
26793
+ if (err && (err.kind === 'test-abort' || (typeof isTestAbortError === 'function' && isTestAbortError(err)))) {
26794
+ throw err;
26795
+ }
26796
+ __mmt_reportNetwork('network-error', __req, {
26797
+ status: err && typeof err.status === 'number' ? err.status : -1,
26798
+ statusText: err && err.message ? String(err.message) : String(err),
26799
+ body: err && err.body,
26800
+ headers: err && err.headers || {},
26801
+ duration: err && typeof err.duration === 'number' ? err.duration : -1
26802
+ });
26803
+ throw err;
26804
+ }
26805
+ };
26806
+ }
26756
26807
 
26757
26808
  try {
26758
26809
  if (__mmt_exampleLabel) {
@@ -26814,6 +26865,7 @@ ${helperFactorySource}
26814
26865
  return result;
26815
26866
  } finally {
26816
26867
  send_ = __mmt_originalSend;
26868
+ if (__mmt_originalSendGrpc) { sendGrpc_ = __mmt_originalSendGrpc; }
26817
26869
  }
26818
26870
  })();`;
26819
26871
  }
@@ -26866,10 +26918,15 @@ async function executeApi(prepared, options, preLogs) {
26866
26918
  exampleOutputs,
26867
26919
  checkTitle: fileDisplayName
26868
26920
  });
26869
- const apiSetenvReporter = options.__mmtIsSuiteBundleChildRun === true && typeof options.reporter === "function" ? (event) => {
26870
- if (event && event.scope === "setenv") {
26871
- options.reporter({ ...event, id: event.id || options.id });
26921
+ const apiReporter = typeof options.reporter === "function" ? (event) => {
26922
+ const isSuiteChild = options.__mmtIsSuiteBundleChildRun === true;
26923
+ if (isSuiteChild && event && event.scope !== "setenv") {
26924
+ return;
26872
26925
  }
26926
+ options.reporter({
26927
+ ...event,
26928
+ id: event.id || options.id
26929
+ });
26873
26930
  } : void 0;
26874
26931
  const result = await runGeneratedJs(
26875
26932
  "run-api",
@@ -26880,7 +26937,7 @@ async function executeApi(prepared, options, preLogs) {
26880
26937
  void 0,
26881
26938
  options.id,
26882
26939
  fileLoader,
26883
- apiSetenvReporter,
26940
+ apiReporter,
26884
26941
  options.abortSignal,
26885
26942
  void 0,
26886
26943
  prepared.filePath ? prepared.filePath.split(/[/\\]/).slice(0, -1).join("/") : void 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mmt-mcp",
3
- "version": "1.45.0-pre",
3
+ "version": "1.45.2-pre",
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",