@loadmill/mcp 0.0.2 → 0.1.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
@@ -1,12 +1,13 @@
1
1
  # Loadmill MCP Server
2
2
 
3
- This package provides a STDIO-based MCP (Message Control Protocol) server. It is designed to be integrated into your workflow, allowing communication over standard input/output streams.
3
+ This package provides a STDIO-based MCP server. It is designed to be integrated into your workflow, allowing LLM interact with Loadmill.
4
4
 
5
5
  ## Use Cases
6
6
  - Query Loadmill for information
7
7
  - Execute Loadmill tests
8
8
  - Write and maintain Loadmill tests (TBD)
9
9
 
10
+ *Note*: As always, when working with MCPs, make sure you are on `Agent` mode
10
11
 
11
12
  ## Install
12
13
 
@@ -31,4 +32,41 @@ This package provides a STDIO-based MCP (Message Control Protocol) server. It is
31
32
  "password": true
32
33
  }]
33
34
  }
34
- ```
35
+ ```
36
+
37
+ ### Cursor
38
+
39
+ ```
40
+ {
41
+ "mcpServers": {
42
+ // other MCP servers...
43
+ "loadmill": {
44
+ "command": "npx",
45
+ "args": ["@loadmill/mcp"],
46
+ "env": {
47
+ "LOADMILL_API_TOKEN": "${env:LOADMILL_API_TOKEN}"
48
+ }
49
+ }
50
+ }
51
+ }
52
+ ```
53
+
54
+ ## MCP Capabilities
55
+
56
+ This server declares the following MCP capabilities:
57
+
58
+ - Tools: Supports `list_tools` and `call_tool`.
59
+ - Search Loadmill test suites.
60
+ - Search Loadmill test suite runs.
61
+ - Run a specific test suite.
62
+ - Fetch details of a test suite run.
63
+ - Fetch details of a specific suite flow run.
64
+ - Search Loadmill test plans.
65
+ - Run a specific test plan (with optional labels/overrides).
66
+ - Fetch details of a test plan run.
67
+ - List all available labels in Loadmill.
68
+
69
+ - Prompts: Supports `list_prompts` and `get_prompt`.
70
+ - Investigate failed test: Analyze failed test results and suggest next steps.
71
+
72
+ - Resources: (TBD) Supports `list_resources` and `read_resource` (declared; not yet implemented).
@@ -1,5 +1,8 @@
1
1
  export declare const MCP_SERVER_NAME = "loadmill-mcp-server";
2
2
  export declare const MCP_SERVER_VERSION = "0.0.1";
3
+ export declare const LOADMILL_URL: string;
4
+ export declare const ASSETS_URL: string;
5
+ export declare const API_URL: string;
3
6
  export declare const SEARCH_TEST_SUITES = "search_test_suites";
4
7
  export declare const SEARCH_TEST_SUITE_RUNS = "search_test_suite_runs";
5
8
  export declare const RUN_TEST_SUITE = "run_test_suite";
@@ -9,4 +12,11 @@ export declare const SEARCH_TEST_PLAN = "search_test_plans";
9
12
  export declare const RUN_TEST_PLAN = "run_test_plans";
10
13
  export declare const GET_TEST_PLAN_RUN = "get_test_plan_run";
11
14
  export declare const GET_LABELS = "get_labels";
15
+ export declare const GET_TEST_SUITE_SCHEMA = "get_test_suite_schema";
16
+ export declare const VALIDATE_TEST_SUITE = "validate_test_suite";
17
+ export declare const SCHEMA_TYPE: {
18
+ TEST_SUITE: string;
19
+ };
12
20
  export declare const AVALIABLE_TOOLS: string[];
21
+ export declare const AVILABLE_PROMPTS: never[];
22
+ export declare const AVILABLE_RESOURCES: string[];
package/dist/constants.js CHANGED
@@ -1,8 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AVALIABLE_TOOLS = exports.GET_LABELS = exports.GET_TEST_PLAN_RUN = exports.RUN_TEST_PLAN = exports.SEARCH_TEST_PLAN = exports.GET_TEST_SUITE_FLOW_RUN = exports.GET_TEST_SUITE_RUN = exports.RUN_TEST_SUITE = exports.SEARCH_TEST_SUITE_RUNS = exports.SEARCH_TEST_SUITES = exports.MCP_SERVER_VERSION = exports.MCP_SERVER_NAME = void 0;
3
+ exports.AVILABLE_RESOURCES = exports.AVILABLE_PROMPTS = exports.AVALIABLE_TOOLS = exports.SCHEMA_TYPE = exports.VALIDATE_TEST_SUITE = exports.GET_TEST_SUITE_SCHEMA = exports.GET_LABELS = exports.GET_TEST_PLAN_RUN = exports.RUN_TEST_PLAN = exports.SEARCH_TEST_PLAN = exports.GET_TEST_SUITE_FLOW_RUN = exports.GET_TEST_SUITE_RUN = exports.RUN_TEST_SUITE = exports.SEARCH_TEST_SUITE_RUNS = exports.SEARCH_TEST_SUITES = exports.API_URL = exports.ASSETS_URL = exports.LOADMILL_URL = exports.MCP_SERVER_VERSION = exports.MCP_SERVER_NAME = void 0;
4
4
  exports.MCP_SERVER_NAME = 'loadmill-mcp-server';
5
5
  exports.MCP_SERVER_VERSION = '0.0.1';
6
+ exports.LOADMILL_URL = process.env.LOADMILL_URL || 'https://app.loadmill.com';
7
+ exports.ASSETS_URL = exports.LOADMILL_URL + '/assets';
8
+ exports.API_URL = exports.LOADMILL_URL + '/api';
6
9
  //tools
7
10
  exports.SEARCH_TEST_SUITES = 'search_test_suites';
8
11
  exports.SEARCH_TEST_SUITE_RUNS = 'search_test_suite_runs';
@@ -13,6 +16,11 @@ exports.SEARCH_TEST_PLAN = 'search_test_plans';
13
16
  exports.RUN_TEST_PLAN = 'run_test_plans';
14
17
  exports.GET_TEST_PLAN_RUN = 'get_test_plan_run';
15
18
  exports.GET_LABELS = 'get_labels';
19
+ exports.GET_TEST_SUITE_SCHEMA = 'get_test_suite_schema';
20
+ exports.VALIDATE_TEST_SUITE = 'validate_test_suite';
21
+ exports.SCHEMA_TYPE = {
22
+ TEST_SUITE: 'loadmill://schemas/test-suite@v1',
23
+ };
16
24
  exports.AVALIABLE_TOOLS = [
17
25
  exports.SEARCH_TEST_SUITES,
18
26
  exports.SEARCH_TEST_SUITE_RUNS,
@@ -23,5 +31,12 @@ exports.AVALIABLE_TOOLS = [
23
31
  exports.SEARCH_TEST_PLAN,
24
32
  exports.RUN_TEST_PLAN,
25
33
  exports.GET_TEST_PLAN_RUN,
34
+ exports.VALIDATE_TEST_SUITE,
35
+ ];
36
+ exports.AVILABLE_PROMPTS = [
37
+ // Add prompt-related constants here
38
+ ];
39
+ exports.AVILABLE_RESOURCES = [
40
+ exports.GET_TEST_SUITE_SCHEMA,
26
41
  ];
27
42
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AACa,QAAA,eAAe,GAAG,qBAAqB,CAAC;AACxC,QAAA,kBAAkB,GAAG,OAAO,CAAC;AAE1C,OAAO;AACM,QAAA,kBAAkB,GAAG,oBAAoB,CAAC;AAC1C,QAAA,sBAAsB,GAAG,wBAAwB,CAAC;AAClD,QAAA,cAAc,GAAG,gBAAgB,CAAC;AAClC,QAAA,kBAAkB,GAAG,oBAAoB,CAAC;AAC1C,QAAA,uBAAuB,GAAG,yBAAyB,CAAC;AACpD,QAAA,gBAAgB,GAAG,mBAAmB,CAAC;AACvC,QAAA,aAAa,GAAG,gBAAgB,CAAC;AACjC,QAAA,iBAAiB,GAAG,mBAAmB,CAAC;AACxC,QAAA,UAAU,GAAG,YAAY,CAAC;AAE1B,QAAA,eAAe,GAAG;IAC7B,0BAAkB;IAClB,8BAAsB;IACtB,sBAAc;IACd,0BAAkB;IAClB,+BAAuB;IACvB,kBAAU;IACV,wBAAgB;IAChB,qBAAa;IACb,yBAAiB;CAClB,CAAC"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AACa,QAAA,eAAe,GAAG,qBAAqB,CAAC;AACxC,QAAA,kBAAkB,GAAG,OAAO,CAAC;AAG7B,QAAA,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,0BAA0B,CAAC;AACtE,QAAA,UAAU,GAAG,oBAAY,GAAG,SAAS,CAAC;AACtC,QAAA,OAAO,GAAG,oBAAY,GAAG,MAAM,CAAC;AAE7C,OAAO;AACM,QAAA,kBAAkB,GAAG,oBAAoB,CAAC;AAC1C,QAAA,sBAAsB,GAAG,wBAAwB,CAAC;AAClD,QAAA,cAAc,GAAG,gBAAgB,CAAC;AAClC,QAAA,kBAAkB,GAAG,oBAAoB,CAAC;AAC1C,QAAA,uBAAuB,GAAG,yBAAyB,CAAC;AACpD,QAAA,gBAAgB,GAAG,mBAAmB,CAAC;AACvC,QAAA,aAAa,GAAG,gBAAgB,CAAC;AACjC,QAAA,iBAAiB,GAAG,mBAAmB,CAAC;AACxC,QAAA,UAAU,GAAG,YAAY,CAAC;AAC1B,QAAA,qBAAqB,GAAG,uBAAuB,CAAC;AAChD,QAAA,mBAAmB,GAAG,qBAAqB,CAAC;AAE5C,QAAA,WAAW,GAAG;IACzB,UAAU,EAAE,kCAAkC;CAC/C,CAAC;AAEW,QAAA,eAAe,GAAG;IAC7B,0BAAkB;IAClB,8BAAsB;IACtB,sBAAc;IACd,0BAAkB;IAClB,+BAAuB;IACvB,kBAAU;IACV,wBAAgB;IAChB,qBAAa;IACb,yBAAiB;IACjB,2BAAmB;CACpB,CAAC;AAEW,QAAA,gBAAgB,GAAG;AAC9B,oCAAoC;CACrC,CAAC;AAEW,QAAA,kBAAkB,GAAG;IAChC,6BAAqB;CACtB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { MCPHandler } from '../../mcp-handler.js';
2
+ export declare const handlers: MCPHandler[];
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handlers = void 0;
4
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
5
+ const utils_js_1 = require("../../utils.js");
6
+ exports.handlers = [
7
+ {
8
+ schema: types_js_1.ListPromptsRequestSchema,
9
+ handler: async (_request) => {
10
+ return { prompts };
11
+ },
12
+ },
13
+ {
14
+ schema: types_js_1.GetPromptRequestSchema,
15
+ handler: async (req) => {
16
+ const { name, arguments: args = {} } = req.params;
17
+ const prompt = prompts.find((p) => p.name === name);
18
+ if (!prompt) {
19
+ throw new Error(`Unknown prompt: ${name}`);
20
+ }
21
+ const required = (prompt.inputSchema?.required ?? []);
22
+ const missing = required.filter((k) => args[k] === undefined);
23
+ if (missing.length) {
24
+ throw new Error(`Missing required prompt arguments: ${missing.join(', ')}`);
25
+ }
26
+ const messages = prompt.messages.map((m) => ({
27
+ role: m.role,
28
+ content: (0, utils_js_1.fillTemplate)(m.content, args),
29
+ }));
30
+ return { messages };
31
+ },
32
+ },
33
+ ];
34
+ const prompts = [
35
+ {
36
+ name: 'investigate_failed_test',
37
+ description: 'Analyze failed test results and suggest next steps',
38
+ messages: [
39
+ {
40
+ role: 'system',
41
+ content: 'You are a QA investigator. Explain failures clearly and suggest next steps.',
42
+ },
43
+ {
44
+ role: 'user',
45
+ content: `The test '{{test_description}}' has failed.\n\n
46
+ Result:\n{{result}}\n\n
47
+ RedactableResult (extended data):\n{{redactableResult}}\n\n
48
+ Please explain the cause and suggest how to fix it.`,
49
+ },
50
+ ],
51
+ inputSchema: {
52
+ type: 'object',
53
+ properties: {
54
+ test_description: { type: 'string' },
55
+ result: { type: 'string' },
56
+ redactableResult: { type: 'string' },
57
+ },
58
+ required: ['test_description', 'result', 'redactableResult'],
59
+ },
60
+ },
61
+ {
62
+ name: 'create_test_suite',
63
+ description: 'Design a Loadmill test-suite JSON using the official schema',
64
+ messages: [
65
+ {
66
+ role: 'system',
67
+ content: `You are an expert in API testing.
68
+ Produce a valid Loadmill test-suite JSON strictly conforming to the schema returned by the get_test_suite_schema resource.
69
+ Favor clarity, maintainability, and minimal assumptions.`,
70
+ },
71
+ {
72
+ role: 'user',
73
+ content: `Create a test-suite named "{{name}}" to test the following endpoints and behaviors:\n\n {{requirements}}\n\n
74
+ Constraints:\n- Include flows and request steps covering happy-path and key edge-cases.\n-
75
+ Use descriptive names for flows and steps.\n- Parameterize repeated values in suite.parameters when reasonable.\n-
76
+ Add basic assertions for status, response time, and core fields.\n- Output only JSON.`,
77
+ },
78
+ ],
79
+ inputSchema: {
80
+ type: 'object',
81
+ properties: {
82
+ name: { type: 'string' },
83
+ requirements: { type: 'string' },
84
+ },
85
+ required: ['name', 'requirements'],
86
+ },
87
+ },
88
+ ];
89
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/handlers/prompts/index.ts"],"names":[],"mappings":";;;AAAA,iEAG4C;AAG5C,6CAA8C;AAGjC,QAAA,QAAQ,GAAiB;IACpC;QACE,MAAM,EAAE,mCAAwB;QAChC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;YAC1B,OAAO,EAAE,OAAO,EAAE,CAAC;QACrB,CAAC;KACF;IACD;QACE,MAAM,EAAE,iCAAsB;QAC9B,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACrB,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;YAElD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;YACpD,IAAI,CAAC,MAAM,EAAE;gBACX,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC;aAC5C;YAED,MAAM,QAAQ,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,IAAI,EAAE,CAAa,CAAC;YAClE,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;YAC9D,IAAI,OAAO,CAAC,MAAM,EAAE;gBAClB,MAAM,IAAI,KAAK,CACb,sCAAsC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC3D,CAAC;aACH;YAED,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC3C,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,OAAO,EAAE,IAAA,uBAAY,EAAC,CAAC,CAAC,OAAO,EAAE,IAA+B,CAAC;aAClE,CAAC,CAAC,CAAC;YAEJ,OAAO,EAAE,QAAQ,EAAE,CAAC;QACtB,CAAC;KACF;CACF,CAAC;AAEF,MAAM,OAAO,GAAG;IACd;QACE,IAAI,EAAE,yBAAyB;QAC/B,WAAW,EAAE,oDAAoD;QACjE,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,QAAQ;gBACd,OAAO,EACH,6EAA6E;aAClF;YACD;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EACE;;;qEAGoD;aAC9D;SACF;QACD,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,gBAAgB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACpC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC1B,gBAAgB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;aACrC;YACD,QAAQ,EAAE,CAAC,kBAAkB,EAAE,QAAQ,EAAE,kBAAkB,CAAC;SAC7D;KACF;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,WAAW,EAAE,6DAA6D;QAC1E,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,QAAQ;gBACd,OAAO,EACL;;mEAEyD;aAC5D;YACD;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EACL;;;gGAGsF;aACzF;SACF;QACD,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACxB,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;aACjC;YACD,QAAQ,EAAE,CAAC,MAAM,EAAE,cAAc,CAAC;SACnC;KACF;CACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { MCPHandler } from '../../mcp-handler.js';
2
+ export declare const handlers: MCPHandler[];
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handlers = void 0;
4
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
5
+ const shared_js_1 = require("../shared.js");
6
+ exports.handlers = [
7
+ {
8
+ schema: types_js_1.ListResourcesRequestSchema,
9
+ handler: async (_request) => {
10
+ const resources = [
11
+ {
12
+ name: 'loadmill-test-suite-schema',
13
+ title: 'Loadmill Test-Suite Schema',
14
+ uri: 'loadmill://schemas/test-suite@v1',
15
+ description: 'JSON Schema (draft 2020-12) describing the Loadmill test-suite format.',
16
+ mimeType: 'application/schema+json',
17
+ },
18
+ ];
19
+ return { resources };
20
+ },
21
+ },
22
+ {
23
+ schema: types_js_1.ReadResourceRequestSchema,
24
+ handler: async (request) => {
25
+ const uri = request?.params?.uri;
26
+ console.log('Reading resource with URI:', uri);
27
+ if (!uri) {
28
+ return {
29
+ contents: [
30
+ {
31
+ mimeType: 'text/plain',
32
+ text: 'Missing resource URI',
33
+ },
34
+ ],
35
+ };
36
+ }
37
+ if (uri === 'loadmill://schemas/test-suite@v1') {
38
+ const schema = await (0, shared_js_1.getSchema)(uri);
39
+ console.log('Fetched schema for URI:', uri);
40
+ return {
41
+ contents: [
42
+ {
43
+ uri,
44
+ mimeType: 'application/json',
45
+ text: schema,
46
+ },
47
+ ],
48
+ };
49
+ }
50
+ // Unknown resource
51
+ return {
52
+ contents: [
53
+ {
54
+ mimeType: 'text/plain',
55
+ text: `Unknown resource: ${uri}`,
56
+ },
57
+ ],
58
+ };
59
+ },
60
+ },
61
+ ];
62
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/handlers/resources/index.ts"],"names":[],"mappings":";;;AAAA,iEAG4C;AAE5C,4CAAyC;AAG5B,QAAA,QAAQ,GAAiB;IACpC;QACE,MAAM,EAAE,qCAA0B;QAClC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;YAC1B,MAAM,SAAS,GAAG;gBAChB;oBACE,IAAI,EAAE,4BAA4B;oBAClC,KAAK,EAAE,4BAA4B;oBACnC,GAAG,EAAE,kCAAkC;oBACvC,WAAW,EACT,wEAAwE;oBAC1E,QAAQ,EAAE,yBAAyB;iBACpC;aACF,CAAC;YAEF,OAAO,EAAE,SAAS,EAAE,CAAC;QACvB,CAAC;KACF;IACD;QACE,MAAM,EAAE,oCAAyB;QACjC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACzB,MAAM,GAAG,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC;YAEjC,OAAO,CAAC,GAAG,CAAC,4BAA4B,EAAE,GAAG,CAAC,CAAC;YAC/C,IAAI,CAAC,GAAG,EAAE;gBACR,OAAO;oBACL,QAAQ,EAAE;wBACR;4BACE,QAAQ,EAAE,YAAY;4BACtB,IAAI,EAAE,sBAAsB;yBAC7B;qBACF;iBACF,CAAC;aACH;YAED,IAAI,GAAG,KAAK,kCAAkC,EAAE;gBAC9C,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAS,EAAC,GAAG,CAAC,CAAC;gBACpC,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,GAAG,CAAC,CAAC;gBAC5C,OAAO;oBACL,QAAQ,EAAE;wBACR;4BACE,GAAG;4BACH,QAAQ,EAAE,kBAAkB;4BAC5B,IAAI,EAAE,MAAM;yBACb;qBACF;iBACF,CAAC;aACH;YAED,mBAAmB;YACnB,OAAO;gBACL,QAAQ,EAAE;oBACR;wBACE,QAAQ,EAAE,YAAY;wBACtB,IAAI,EAAE,qBAAqB,GAAG,EAAE;qBACjC;iBACF;aACF,CAAC;QACJ,CAAC;KACF;CACF,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function getSchema(schemaType: string): Promise<string>;
2
+ export declare const schemaTypeToSchemaURL: {
3
+ [x: string]: string;
4
+ };
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.schemaTypeToSchemaURL = exports.getSchema = void 0;
4
+ const constants_1 = require("../constants");
5
+ const http_request_1 = require("../http-request");
6
+ const schemaCache = new Map();
7
+ async function getSchema(schemaType) {
8
+ try {
9
+ console.log('Getting schema with type:', schemaType);
10
+ const schemaUrl = getSchemaURLByType(schemaType);
11
+ const cached = schemaCache.get(schemaUrl);
12
+ if (cached) {
13
+ if (!cached.expiresAt || Date.now() < cached.expiresAt) {
14
+ return cached.text;
15
+ }
16
+ schemaCache.delete(schemaUrl);
17
+ }
18
+ const resp = await (0, http_request_1.sendHttpRequest)({ method: http_request_1.HttpMethods.GET, url: schemaUrl });
19
+ const text = resp.text;
20
+ const entry = {
21
+ text,
22
+ expiresAt: Date.now() + 60 * 60 * 1000, // Cache for 60 minutes
23
+ };
24
+ console.log('Cached schema:', entry);
25
+ schemaCache.set(schemaUrl, entry);
26
+ return entry.text;
27
+ }
28
+ catch (error) {
29
+ console.error(`Failed to fetch schema for ${schemaType}:`, error);
30
+ throw error;
31
+ }
32
+ }
33
+ exports.getSchema = getSchema;
34
+ exports.schemaTypeToSchemaURL = {
35
+ [constants_1.SCHEMA_TYPE.TEST_SUITE]: `${constants_1.ASSETS_URL}/schema/loadmill-test-suite.schema.v1.json`,
36
+ };
37
+ function getSchemaURLByType(schemaType) {
38
+ const schemaUrl = exports.schemaTypeToSchemaURL[schemaType];
39
+ //console.log('Resolved schema URL:', schemaUrl);
40
+ if (!schemaUrl) {
41
+ throw new Error(`Unknown schema type: ${schemaType}`);
42
+ }
43
+ return schemaUrl;
44
+ }
45
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/handlers/shared.ts"],"names":[],"mappings":";;;AAAA,4CAAuD;AACvD,kDAA+D;AAO/D,MAAM,WAAW,GAAG,IAAI,GAAG,EAA4B,CAAC;AAEjD,KAAK,UAAU,SAAS,CAAC,UAAkB;IAEhD,IAAI;QACF,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,UAAU,CAAC,CAAC;QACrD,MAAM,SAAS,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;QAEjD,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC1C,IAAI,MAAM,EAAE;YACV,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,SAAS,EAAE;gBACtD,OAAO,MAAM,CAAC,IAAI,CAAC;aACpB;YACD,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;SAC/B;QAED,MAAM,IAAI,GAAG,MAAM,IAAA,8BAAe,EAAC,EAAE,MAAM,EAAE,0BAAW,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;QAChF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QAEvB,MAAM,KAAK,GAAqB;YAC9B,IAAI;YACJ,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,uBAAuB;SAChE,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;QACrC,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAClC,OAAO,KAAK,CAAC,IAAI,CAAC;KACnB;IAAC,OAAO,KAAK,EAAE;QACd,OAAO,CAAC,KAAK,CAAC,8BAA8B,UAAU,GAAG,EAAE,KAAK,CAAC,CAAC;QAClE,MAAM,KAAK,CAAC;KACb;AACH,CAAC;AA5BD,8BA4BC;AAEY,QAAA,qBAAqB,GAAG;IACnC,CAAC,uBAAW,CAAC,UAAU,CAAC,EAAE,GAAG,sBAAU,4CAA4C;CACpF,CAAC;AAEF,SAAS,kBAAkB,CAAC,UAAkB;IAC5C,MAAM,SAAS,GAAG,6BAAqB,CAAC,UAAU,CAAC,CAAC;IACpD,iDAAiD;IACjD,IAAI,CAAC,SAAS,EAAE;QACd,MAAM,IAAI,KAAK,CAAC,wBAAwB,UAAU,EAAE,CAAC,CAAC;KACvD;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { MCPHandler } from '../../mcp-handler.js';
2
+ export declare const handlers: MCPHandler[];
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handlers = void 0;
4
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
5
+ const constants_js_1 = require("../../constants.js");
6
+ const tasks_js_1 = require("./tasks.js");
7
+ exports.handlers = [
8
+ {
9
+ schema: types_js_1.ListToolsRequestSchema,
10
+ handler: async () => {
11
+ const tools = [
12
+ {
13
+ name: constants_js_1.SEARCH_TEST_SUITES,
14
+ title: 'Search Test Suites',
15
+ description: 'A tool to search for Loadmill test suites. Returns a list of test suites',
16
+ inputSchema: {
17
+ type: 'object',
18
+ properties: {
19
+ search: {
20
+ type: 'string',
21
+ description: 'free text filter to search for test suites',
22
+ },
23
+ page: {
24
+ type: 'integer',
25
+ description: 'page number for pagination',
26
+ },
27
+ labels: {
28
+ type: 'array',
29
+ items: {
30
+ type: 'string',
31
+ description: 'filter suites that have flows with these labels',
32
+ },
33
+ },
34
+ },
35
+ },
36
+ },
37
+ {
38
+ name: constants_js_1.VALIDATE_TEST_SUITE,
39
+ title: 'Validate Test Suite',
40
+ description: 'Validates a test-suite file against the official JSON Schema.',
41
+ inputSchema: {
42
+ type: 'object',
43
+ properties: {
44
+ path: {
45
+ type: 'string',
46
+ description: 'Path to a JSON file containing a Loadmill test-suite',
47
+ },
48
+ },
49
+ required: ['path'],
50
+ },
51
+ },
52
+ {
53
+ name: constants_js_1.SEARCH_TEST_SUITE_RUNS,
54
+ title: 'Search Test Suite Runs',
55
+ description: 'A tool to search for Loadmill test suite runs. Returns a list of test suite runs',
56
+ inputSchema: {
57
+ type: 'object',
58
+ properties: {
59
+ search: {
60
+ type: 'string',
61
+ description: 'free text filter to search for test suites',
62
+ },
63
+ page: {
64
+ type: 'integer',
65
+ description: 'page number for pagination',
66
+ },
67
+ },
68
+ },
69
+ },
70
+ {
71
+ name: constants_js_1.RUN_TEST_SUITE,
72
+ title: 'Run Test Suite',
73
+ description: 'A tool to run a Loadmill test suite. Returns the id of the running suite',
74
+ inputSchema: {
75
+ type: 'object',
76
+ properties: {
77
+ id: {
78
+ type: 'string',
79
+ description: 'ID of the test suite to run',
80
+ },
81
+ },
82
+ },
83
+ },
84
+ {
85
+ name: constants_js_1.GET_TEST_SUITE_RUN,
86
+ title: 'Get Test Suite Run',
87
+ description: `A tool to get a Loadmill test suite run by its ID.
88
+ Returns the run details including the testSuiteFlowRuns list that can be fetched later and is useful for understanding the flow of the test run.`,
89
+ inputSchema: {
90
+ type: 'object',
91
+ properties: {
92
+ id: {
93
+ type: 'string',
94
+ description: 'ID of the test suite run to fetch',
95
+ },
96
+ },
97
+ },
98
+ },
99
+ {
100
+ name: constants_js_1.GET_TEST_SUITE_FLOW_RUN,
101
+ title: 'Get Test Suite Flow Run',
102
+ description: `A tool to get a Loadmill test suite flow run by its ID.
103
+ Returns the run details. Use this when investigating the test run results.`,
104
+ inputSchema: {
105
+ type: 'object',
106
+ properties: {
107
+ flowRunId: {
108
+ type: 'string',
109
+ description: 'ID of the test suite flow run to fetch',
110
+ },
111
+ },
112
+ },
113
+ },
114
+ {
115
+ name: constants_js_1.SEARCH_TEST_PLAN,
116
+ title: 'Search Test Plans',
117
+ description: 'A tool to search for Loadmill test plans. Returns a list of test plans with their descriptions (names) and the author\'s information.',
118
+ inputSchema: {
119
+ type: 'object',
120
+ properties: {
121
+ search: {
122
+ type: 'string',
123
+ description: 'free text filter to search for test suites',
124
+ },
125
+ page: {
126
+ type: 'integer',
127
+ description: 'page number for pagination',
128
+ },
129
+ },
130
+ },
131
+ },
132
+ {
133
+ name: constants_js_1.RUN_TEST_PLAN,
134
+ title: 'Run a Test Plan',
135
+ description: 'A tool to run a Loadmill test plan. Returns the id of the running plan',
136
+ inputSchema: {
137
+ type: 'object',
138
+ properties: {
139
+ id: {
140
+ type: 'string',
141
+ description: 'ID of the test plan to run',
142
+ },
143
+ labels: {
144
+ type: 'array',
145
+ items: {
146
+ type: 'string',
147
+ description: 'filter suites that have flows with these labels',
148
+ },
149
+ },
150
+ overrideParameters: {
151
+ type: 'string',
152
+ description: `Parameters to override in the test plan.
153
+ A string of comma separated list of key-value pairs, e.g. "param1=value1,param2=value2".`,
154
+ },
155
+ },
156
+ },
157
+ },
158
+ {
159
+ name: constants_js_1.GET_TEST_PLAN_RUN,
160
+ title: 'Get Test Plan Run',
161
+ description: 'A tool to get a Loadmill test plan run by its ID. Returns the run details.',
162
+ inputSchema: {
163
+ type: 'object',
164
+ properties: {
165
+ id: {
166
+ type: 'string',
167
+ description: 'ID of the test plan run to fetch',
168
+ },
169
+ },
170
+ },
171
+ },
172
+ {
173
+ name: constants_js_1.GET_LABELS,
174
+ title: 'Get Labels',
175
+ description: 'A tool to fetch all available labels in Loadmill.',
176
+ inputSchema: {
177
+ type: 'object',
178
+ properties: {},
179
+ },
180
+ },
181
+ ];
182
+ return { tools };
183
+ },
184
+ },
185
+ {
186
+ schema: types_js_1.CallToolRequestSchema,
187
+ handler: async (request) => {
188
+ if (!constants_js_1.AVALIABLE_TOOLS.includes(request.params.name)) {
189
+ throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
190
+ }
191
+ const tool = tasks_js_1.toolsTasks[request.params.name];
192
+ if (!tool) {
193
+ throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
194
+ }
195
+ return await tool(request.params);
196
+ },
197
+ },
198
+ ];
199
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/handlers/tools/index.ts"],"names":[],"mappings":"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"}