mmt-mcp 1.44.1-pre → 1.44.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.
- package/dist/examples/basic/05_api_test_suite/README.md +2 -2
- package/dist/examples/basic/05_api_test_suite/echo.mmt +0 -2
- package/dist/examples/professional/07_mock_server_timeout/README.md +1 -1
- package/dist/examples/professional/07_mock_server_timeout/test/slow_timeout_test.mmt +1 -1
- package/dist/examples/professional/11_token_resolution/README.md +2 -0
- package/dist/examples/professional/11_token_resolution/api/current_xml_body.mmt +0 -1
- package/dist/examples/professional/11_token_resolution/api/env_xml_body.mmt +0 -1
- package/dist/examples/professional/11_token_resolution/api/input_json_body.mmt +8 -0
- package/dist/examples/professional/11_token_resolution/api/input_urlencoded_body.mmt +8 -1
- package/dist/examples/professional/11_token_resolution/api/input_xml_body.mmt +28 -0
- package/dist/examples/professional/11_token_resolution/api/random_urlencoded_body.mmt +0 -1
- package/dist/examples/professional/11_token_resolution/api/random_xml_body.mmt +0 -1
- package/dist/examples/professional/11_token_resolution/test/current_xml_body_test.mmt +0 -1
- package/dist/examples/professional/11_token_resolution/test/env_xml_body_test.mmt +0 -1
- package/dist/examples/professional/11_token_resolution/test/input_json_body_test.mmt +2 -0
- package/dist/examples/professional/11_token_resolution/test/input_urlencoded_body_test.mmt +2 -1
- package/dist/examples/professional/11_token_resolution/test/input_xml_body_test.mmt +8 -0
- package/dist/examples/professional/11_token_resolution/test/random_urlencoded_body_test.mmt +0 -1
- package/dist/examples/professional/11_token_resolution/test/random_xml_body_test.mmt +0 -1
- package/dist/server.cjs +52 -6
- package/dist/server.js +52 -6
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# API example `expect`
|
|
1
|
+
# API example `expect` in a suite
|
|
2
2
|
|
|
3
|
-
This example shows a `type: api` file with per-example `expect
|
|
3
|
+
This example shows a `type: api` file with per-example `expect`, listed in a suite.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npx testlight run suite.mmt
|
|
@@ -23,4 +23,4 @@ This professional example demonstrates a request timeout against a delayed local
|
|
|
23
23
|
npx testlight run examples/professional/07_mock_server_timeout/test/slow_timeout_test.mmt
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
This example
|
|
26
|
+
This example should pass: the request times out (`status: -1`) because the request timeout is shorter than the mock response delay.
|
|
@@ -22,6 +22,8 @@ Each request also sends the other spellings of one token:
|
|
|
22
22
|
|
|
23
23
|
A whole-field example expect uses the same token (`e:account_age`, `i:username`, `c:day`, `r:uuid`, including a slice such as `i:city[0:3]`). `e:`, `i:`, and `c:` resolve to the value that was sent. Text formats still match when a number or boolean was sent as text. `r:` is checked as a regex of that generator, because each draw is new. `c:epoch` changes every second, so that expect stays a range. Text wrapped around a token (`asda42`, `asda15`) stays a concrete string or a regex.
|
|
24
24
|
|
|
25
|
+
The `i:` examples also send `version` (`"1.0"`) and `price` (`9.5`). `input_xml_body.mmt` puts numeric, boolean, and decimal tokens in XML attributes (`<item number="100" flag="true" …>`). Attribute values are always quoted, so the request stays valid XML.
|
|
26
|
+
|
|
25
27
|
## Environment
|
|
26
28
|
|
|
27
29
|
`multimeter.mmt` is loaded as the workspace environment. The active values are:
|
|
@@ -6,6 +6,8 @@ inputs:
|
|
|
6
6
|
age: 100
|
|
7
7
|
active: true
|
|
8
8
|
city: Berlin
|
|
9
|
+
version: "1.0"
|
|
10
|
+
price: 9.5
|
|
9
11
|
outputs:
|
|
10
12
|
username: body.body.username
|
|
11
13
|
age: body.body.age
|
|
@@ -24,6 +26,8 @@ outputs:
|
|
|
24
26
|
curly_whole: body.body.curly_whole
|
|
25
27
|
text_slice: body.body.text_slice
|
|
26
28
|
missing_embed: body.body.missing_embed
|
|
29
|
+
version_text: body.body.version
|
|
30
|
+
price_value: body.body.price
|
|
27
31
|
status_code: status
|
|
28
32
|
url: https://test.mmt.dev/echo
|
|
29
33
|
method: post
|
|
@@ -46,6 +50,8 @@ body:
|
|
|
46
50
|
curly_whole: {{i:city}}
|
|
47
51
|
text_slice: <<i:city[0:3]>>
|
|
48
52
|
missing_embed: "x<<i:nickname>>y"
|
|
53
|
+
version: i:version
|
|
54
|
+
price: i:price
|
|
49
55
|
examples:
|
|
50
56
|
- id: defaults
|
|
51
57
|
title: Check outputs
|
|
@@ -68,3 +74,5 @@ examples:
|
|
|
68
74
|
curly_whole: i:city
|
|
69
75
|
text_slice: i:city[0:3]
|
|
70
76
|
missing_embed: "xi:nicknamey"
|
|
77
|
+
version_text: "1.0"
|
|
78
|
+
price_value: 9.5
|
|
@@ -6,6 +6,8 @@ inputs:
|
|
|
6
6
|
age: 100
|
|
7
7
|
active: true
|
|
8
8
|
city: Berlin
|
|
9
|
+
version: "1.0"
|
|
10
|
+
price: 9.5
|
|
9
11
|
outputs:
|
|
10
12
|
username: body.body.username
|
|
11
13
|
age: body.body.age
|
|
@@ -22,7 +24,8 @@ outputs:
|
|
|
22
24
|
quoted_curly: body.body.quoted_curly
|
|
23
25
|
curly_whole: body.body.curly_whole
|
|
24
26
|
text_slice: body.body.text_slice
|
|
25
|
-
|
|
27
|
+
version_text: body.body.version
|
|
28
|
+
price_value: body.body.price
|
|
26
29
|
status_code: status
|
|
27
30
|
url: https://test.mmt.dev/echo
|
|
28
31
|
method: post
|
|
@@ -43,6 +46,8 @@ body:
|
|
|
43
46
|
quoted_curly: "{{i:username}}"
|
|
44
47
|
curly_whole: {{i:city}}
|
|
45
48
|
text_slice: <<i:city[0:3]>>
|
|
49
|
+
version: i:version
|
|
50
|
+
price: i:price
|
|
46
51
|
examples:
|
|
47
52
|
- id: defaults
|
|
48
53
|
title: Check outputs
|
|
@@ -63,3 +68,5 @@ examples:
|
|
|
63
68
|
quoted_curly: i:username
|
|
64
69
|
curly_whole: i:city
|
|
65
70
|
text_slice: i:city[0:3]
|
|
71
|
+
version_text: "1.0"
|
|
72
|
+
price_value: "9.5"
|
|
@@ -6,6 +6,8 @@ inputs:
|
|
|
6
6
|
age: 100
|
|
7
7
|
active: true
|
|
8
8
|
city: Berlin
|
|
9
|
+
version: "1.0"
|
|
10
|
+
price: 9.5
|
|
9
11
|
outputs:
|
|
10
12
|
username: body.body.username
|
|
11
13
|
age: body.body.age
|
|
@@ -23,6 +25,14 @@ outputs:
|
|
|
23
25
|
curly_whole: body.body.curly_whole
|
|
24
26
|
text_slice: body.body.text_slice
|
|
25
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
|
|
26
36
|
status_code: status
|
|
27
37
|
url: https://test.mmt.dev/echo
|
|
28
38
|
method: post
|
|
@@ -45,6 +55,16 @@ body:
|
|
|
45
55
|
curly_whole: {{i:city}}
|
|
46
56
|
text_slice: <<i:city[0:3]>>
|
|
47
57
|
missing_embed: "x<<i:nickname>>y"
|
|
58
|
+
item:
|
|
59
|
+
_attributes:
|
|
60
|
+
number: i:age
|
|
61
|
+
flag: i:active
|
|
62
|
+
version: i:version
|
|
63
|
+
angle: <<i:age>>
|
|
64
|
+
embedded: "n<<i:age>>"
|
|
65
|
+
literal: "1.0"
|
|
66
|
+
decimal: i:price
|
|
67
|
+
price: i:price
|
|
48
68
|
examples:
|
|
49
69
|
- id: defaults
|
|
50
70
|
title: Check outputs
|
|
@@ -66,3 +86,11 @@ examples:
|
|
|
66
86
|
curly_whole: i:city
|
|
67
87
|
text_slice: i:city[0:3]
|
|
68
88
|
missing_embed: "xi:nicknamey"
|
|
89
|
+
attr_number: "100"
|
|
90
|
+
attr_bool: "true"
|
|
91
|
+
attr_text: "1.0"
|
|
92
|
+
attr_angle: "100"
|
|
93
|
+
attr_embedded: "n100"
|
|
94
|
+
attr_literal: "1.0"
|
|
95
|
+
attr_decimal: "9.5"
|
|
96
|
+
price_text: "9.5"
|
|
@@ -26,3 +26,11 @@ steps:
|
|
|
26
26
|
curly_whole: "Berlin"
|
|
27
27
|
text_slice: "Ber"
|
|
28
28
|
missing_embed: "xi:nicknamey"
|
|
29
|
+
attr_number: "100"
|
|
30
|
+
attr_bool: "true"
|
|
31
|
+
attr_text: "1.0"
|
|
32
|
+
attr_angle: "100"
|
|
33
|
+
attr_embedded: "n100"
|
|
34
|
+
attr_literal: "1.0"
|
|
35
|
+
attr_decimal: "9.5"
|
|
36
|
+
price_text: "9.5"
|
package/dist/server.cjs
CHANGED
|
@@ -14766,6 +14766,46 @@ function yamlValueToInputBox(val) {
|
|
|
14766
14766
|
}
|
|
14767
14767
|
return String(val);
|
|
14768
14768
|
}
|
|
14769
|
+
function inputBoxToYamlValue(val) {
|
|
14770
|
+
if (val === null || val === void 0) {
|
|
14771
|
+
return "";
|
|
14772
|
+
}
|
|
14773
|
+
if (typeof val !== "string") {
|
|
14774
|
+
return val;
|
|
14775
|
+
}
|
|
14776
|
+
const t = val.trim();
|
|
14777
|
+
if (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'")) {
|
|
14778
|
+
const inner = t.slice(1, -1);
|
|
14779
|
+
if (isPlainTokenScalar(inner)) {
|
|
14780
|
+
return wrapLiteralToken(inner);
|
|
14781
|
+
}
|
|
14782
|
+
return inner;
|
|
14783
|
+
}
|
|
14784
|
+
if (t === "omit") {
|
|
14785
|
+
return OMIT_SENTINEL2;
|
|
14786
|
+
}
|
|
14787
|
+
if (t === "null") {
|
|
14788
|
+
return null;
|
|
14789
|
+
}
|
|
14790
|
+
if (t.toLowerCase() === "true") {
|
|
14791
|
+
return true;
|
|
14792
|
+
}
|
|
14793
|
+
if (t.toLowerCase() === "false") {
|
|
14794
|
+
return false;
|
|
14795
|
+
}
|
|
14796
|
+
const num = Number(t);
|
|
14797
|
+
if (!Number.isNaN(num) && t !== "") {
|
|
14798
|
+
return num;
|
|
14799
|
+
}
|
|
14800
|
+
if (t.startsWith("{") && t.endsWith("}") || t.startsWith("[") && t.endsWith("]")) {
|
|
14801
|
+
try {
|
|
14802
|
+
return JSON.parse(t);
|
|
14803
|
+
} catch {
|
|
14804
|
+
return val;
|
|
14805
|
+
}
|
|
14806
|
+
}
|
|
14807
|
+
return val;
|
|
14808
|
+
}
|
|
14769
14809
|
|
|
14770
14810
|
// ../core/src/yamlAstMerge.ts
|
|
14771
14811
|
var import_yaml = require("yaml");
|
|
@@ -15282,7 +15322,17 @@ function formValueToString(value, keepLiteralMarkers = false) {
|
|
|
15282
15322
|
}
|
|
15283
15323
|
return stringifyJsonWithRuntimeTokens(value, false);
|
|
15284
15324
|
}
|
|
15325
|
+
function isNonCanonicalNumberText(text) {
|
|
15326
|
+
if (isLiteralTokenValue(text) || isTokenLikeScalar(text)) {
|
|
15327
|
+
return false;
|
|
15328
|
+
}
|
|
15329
|
+
const typed = inputBoxToYamlValue(text);
|
|
15330
|
+
return typeof typed === "number" && String(typed) !== text;
|
|
15331
|
+
}
|
|
15285
15332
|
function editorFormValue(value) {
|
|
15333
|
+
if (typeof value === "string" && isNonCanonicalNumberText(value)) {
|
|
15334
|
+
return value;
|
|
15335
|
+
}
|
|
15286
15336
|
if (value === null || typeof value === "number" || typeof value === "boolean" || typeof value === "string") {
|
|
15287
15337
|
return yamlValueToInputBox(value);
|
|
15288
15338
|
}
|
|
@@ -15916,18 +15966,14 @@ var apiToJSfunc = async (ctx) => {
|
|
|
15916
15966
|
"${JSON.stringify($1)}"
|
|
15917
15967
|
);
|
|
15918
15968
|
}
|
|
15919
|
-
const entries = Object.entries(ctx.api.inputs ?? {});
|
|
15969
|
+
const entries = reqFormat2 === "json" ? Object.entries(ctx.api.inputs ?? {}) : [];
|
|
15920
15970
|
for (const [name, value] of entries) {
|
|
15921
15971
|
const quoted = new RegExp(`"\\$\\{${name}\\}"`, "g");
|
|
15922
|
-
if (
|
|
15972
|
+
if (typeof value !== "string") {
|
|
15923
15973
|
formattedBody = formattedBody.replace(
|
|
15924
15974
|
quoted,
|
|
15925
15975
|
() => "${JSON.stringify(" + name + ")}"
|
|
15926
15976
|
);
|
|
15927
|
-
} else if (typeof value === "string") {
|
|
15928
|
-
formattedBody = formattedBody.replace(quoted, '"${' + name + '}"');
|
|
15929
|
-
} else {
|
|
15930
|
-
formattedBody = formattedBody.replace(quoted, "${" + name + "}");
|
|
15931
15977
|
}
|
|
15932
15978
|
}
|
|
15933
15979
|
if (reqFormat2 === "json") {
|
package/dist/server.js
CHANGED
|
@@ -14766,6 +14766,46 @@ function yamlValueToInputBox(val) {
|
|
|
14766
14766
|
}
|
|
14767
14767
|
return String(val);
|
|
14768
14768
|
}
|
|
14769
|
+
function inputBoxToYamlValue(val) {
|
|
14770
|
+
if (val === null || val === void 0) {
|
|
14771
|
+
return "";
|
|
14772
|
+
}
|
|
14773
|
+
if (typeof val !== "string") {
|
|
14774
|
+
return val;
|
|
14775
|
+
}
|
|
14776
|
+
const t = val.trim();
|
|
14777
|
+
if (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'")) {
|
|
14778
|
+
const inner = t.slice(1, -1);
|
|
14779
|
+
if (isPlainTokenScalar(inner)) {
|
|
14780
|
+
return wrapLiteralToken(inner);
|
|
14781
|
+
}
|
|
14782
|
+
return inner;
|
|
14783
|
+
}
|
|
14784
|
+
if (t === "omit") {
|
|
14785
|
+
return OMIT_SENTINEL2;
|
|
14786
|
+
}
|
|
14787
|
+
if (t === "null") {
|
|
14788
|
+
return null;
|
|
14789
|
+
}
|
|
14790
|
+
if (t.toLowerCase() === "true") {
|
|
14791
|
+
return true;
|
|
14792
|
+
}
|
|
14793
|
+
if (t.toLowerCase() === "false") {
|
|
14794
|
+
return false;
|
|
14795
|
+
}
|
|
14796
|
+
const num = Number(t);
|
|
14797
|
+
if (!Number.isNaN(num) && t !== "") {
|
|
14798
|
+
return num;
|
|
14799
|
+
}
|
|
14800
|
+
if (t.startsWith("{") && t.endsWith("}") || t.startsWith("[") && t.endsWith("]")) {
|
|
14801
|
+
try {
|
|
14802
|
+
return JSON.parse(t);
|
|
14803
|
+
} catch {
|
|
14804
|
+
return val;
|
|
14805
|
+
}
|
|
14806
|
+
}
|
|
14807
|
+
return val;
|
|
14808
|
+
}
|
|
14769
14809
|
|
|
14770
14810
|
// ../core/src/yamlAstMerge.ts
|
|
14771
14811
|
var import_yaml = require("yaml");
|
|
@@ -15282,7 +15322,17 @@ function formValueToString(value, keepLiteralMarkers = false) {
|
|
|
15282
15322
|
}
|
|
15283
15323
|
return stringifyJsonWithRuntimeTokens(value, false);
|
|
15284
15324
|
}
|
|
15325
|
+
function isNonCanonicalNumberText(text) {
|
|
15326
|
+
if (isLiteralTokenValue(text) || isTokenLikeScalar(text)) {
|
|
15327
|
+
return false;
|
|
15328
|
+
}
|
|
15329
|
+
const typed = inputBoxToYamlValue(text);
|
|
15330
|
+
return typeof typed === "number" && String(typed) !== text;
|
|
15331
|
+
}
|
|
15285
15332
|
function editorFormValue(value) {
|
|
15333
|
+
if (typeof value === "string" && isNonCanonicalNumberText(value)) {
|
|
15334
|
+
return value;
|
|
15335
|
+
}
|
|
15286
15336
|
if (value === null || typeof value === "number" || typeof value === "boolean" || typeof value === "string") {
|
|
15287
15337
|
return yamlValueToInputBox(value);
|
|
15288
15338
|
}
|
|
@@ -15916,18 +15966,14 @@ var apiToJSfunc = async (ctx) => {
|
|
|
15916
15966
|
"${JSON.stringify($1)}"
|
|
15917
15967
|
);
|
|
15918
15968
|
}
|
|
15919
|
-
const entries = Object.entries(ctx.api.inputs ?? {});
|
|
15969
|
+
const entries = reqFormat2 === "json" ? Object.entries(ctx.api.inputs ?? {}) : [];
|
|
15920
15970
|
for (const [name, value] of entries) {
|
|
15921
15971
|
const quoted = new RegExp(`"\\$\\{${name}\\}"`, "g");
|
|
15922
|
-
if (
|
|
15972
|
+
if (typeof value !== "string") {
|
|
15923
15973
|
formattedBody = formattedBody.replace(
|
|
15924
15974
|
quoted,
|
|
15925
15975
|
() => "${JSON.stringify(" + name + ")}"
|
|
15926
15976
|
);
|
|
15927
|
-
} else if (typeof value === "string") {
|
|
15928
|
-
formattedBody = formattedBody.replace(quoted, '"${' + name + '}"');
|
|
15929
|
-
} else {
|
|
15930
|
-
formattedBody = formattedBody.replace(quoted, "${" + name + "}");
|
|
15931
15977
|
}
|
|
15932
15978
|
}
|
|
15933
15979
|
if (reqFormat2 === "json") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mmt-mcp",
|
|
3
|
-
"version": "1.44.
|
|
3
|
+
"version": "1.44.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",
|