@loadmill/mcp 0.0.2-rc.1

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 ADDED
@@ -0,0 +1,34 @@
1
+ # Loadmill MCP Server
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.
4
+
5
+ ## Use Cases
6
+ - Query Loadmill for information
7
+ - Execute Loadmill tests
8
+ - Write and maintain Loadmill tests (TBD)
9
+
10
+
11
+ ## Install
12
+
13
+ ### VS Code
14
+
15
+ ```
16
+ {
17
+ "servers": {
18
+ "loadmill": {
19
+ "type": "stdio",
20
+ "command": "npx",
21
+ "args": ["@loadmill/mcp"],
22
+ "env": {
23
+ "LOADMILL_API_TOKEN": "${input:loadmill-api-token}"
24
+ }
25
+ }
26
+ },
27
+ "inputs": [ {
28
+ "id": "loadmill-api-token",
29
+ "type": "promptString",
30
+ "description": "Loadmill API token",
31
+ "password": true
32
+ }]
33
+ }
34
+ ```
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ require('../dist/index').default(process.argv);
@@ -0,0 +1,10 @@
1
+ export declare const MCP_SERVER_NAME = "loadmill-mcp-server";
2
+ export declare const MCP_SERVER_VERSION = "0.0.1";
3
+ export declare const SEARCH_TEST_SUITES = "search_test_suites";
4
+ export declare const RUN_TEST_SUITE = "run_test_suite";
5
+ export declare const GET_TEST_SUITE_RUN = "get_test_suite_run";
6
+ export declare const SEARCH_TEST_PLAN = "search_test_plans";
7
+ export declare const RUN_TEST_PLAN = "run_test_plans";
8
+ export declare const GET_TEST_PLAN_RUN = "get_test_plan_run";
9
+ export declare const GET_LABELS = "get_labels";
10
+ export declare const AVALIABLE_TOOLS: string[];
@@ -0,0 +1,23 @@
1
+ "use strict";
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_RUN = exports.RUN_TEST_SUITE = exports.SEARCH_TEST_SUITES = exports.MCP_SERVER_VERSION = exports.MCP_SERVER_NAME = void 0;
4
+ exports.MCP_SERVER_NAME = 'loadmill-mcp-server';
5
+ exports.MCP_SERVER_VERSION = '0.0.1';
6
+ //tools
7
+ exports.SEARCH_TEST_SUITES = 'search_test_suites';
8
+ exports.RUN_TEST_SUITE = 'run_test_suite';
9
+ exports.GET_TEST_SUITE_RUN = 'get_test_suite_run';
10
+ exports.SEARCH_TEST_PLAN = 'search_test_plans';
11
+ exports.RUN_TEST_PLAN = 'run_test_plans';
12
+ exports.GET_TEST_PLAN_RUN = 'get_test_plan_run';
13
+ exports.GET_LABELS = 'get_labels';
14
+ exports.AVALIABLE_TOOLS = [
15
+ exports.SEARCH_TEST_SUITES,
16
+ exports.RUN_TEST_SUITE,
17
+ exports.GET_TEST_SUITE_RUN,
18
+ exports.GET_LABELS,
19
+ exports.SEARCH_TEST_PLAN,
20
+ exports.RUN_TEST_PLAN,
21
+ exports.GET_TEST_PLAN_RUN,
22
+ ];
23
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +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,cAAc,GAAG,gBAAgB,CAAC;AAClC,QAAA,kBAAkB,GAAG,oBAAoB,CAAC;AAC1C,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,sBAAc;IACd,0BAAkB;IAClB,kBAAU;IACV,wBAAgB;IAChB,qBAAa;IACb,yBAAiB;CAClB,CAAC"}
@@ -0,0 +1,15 @@
1
+ export declare const sendHttpRequest: (options: HttpRequestOptions) => Promise<any>;
2
+ declare type HttpRequestOptions = {
3
+ method?: HttpMethods;
4
+ url: string;
5
+ query?: Record<string, any>;
6
+ body?: Record<string, any>;
7
+ };
8
+ export declare enum HttpMethods {
9
+ GET = "get",
10
+ POST = "post",
11
+ PUT = "put",
12
+ DELETE = "delete",
13
+ PATCH = "patch"
14
+ }
15
+ export {};
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpMethods = exports.sendHttpRequest = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const superagent = tslib_1.__importStar(require("superagent"));
6
+ const DEFAULT_TIMEOUT_MS = process.env.LOADMILL_TIMEOUT || 30000; // 30 seconds
7
+ const RETRY_DELAY_MS = 1000; // 1 second
8
+ const LOADMILL_API_TOKEN = process.env.LOADMILL_API_TOKEN;
9
+ const sendHttpRequest = async (options) => {
10
+ try {
11
+ return await _executeRequest(options);
12
+ }
13
+ catch (err) {
14
+ // eslint-disable-next-line no-console
15
+ console.error(`Error during ${(options.method || HttpMethods.GET).toUpperCase()} request to ${options.url}:`, err);
16
+ if (_shouldRetry(err)) {
17
+ // eslint-disable-next-line no-console
18
+ console.log(`Retrying ${(options.method || HttpMethods.GET).toUpperCase()} request to ${options.url}...`);
19
+ await _delay(RETRY_DELAY_MS);
20
+ return await _executeRequest(options);
21
+ }
22
+ throw err;
23
+ }
24
+ };
25
+ exports.sendHttpRequest = sendHttpRequest;
26
+ const _executeRequest = async (options) => {
27
+ const { method = HttpMethods.GET, url, query, body } = options;
28
+ let request = superagent[method](url)
29
+ .auth(LOADMILL_API_TOKEN)
30
+ .timeout(DEFAULT_TIMEOUT_MS);
31
+ if (query) {
32
+ request = request.query(query);
33
+ }
34
+ if (body) {
35
+ request = request.send(body);
36
+ }
37
+ return await request;
38
+ };
39
+ // retry on network issues
40
+ const _shouldRetry = (err) => {
41
+ return err.timeout ||
42
+ err.code === 'ECONNREFUSED' ||
43
+ err.code === 'ECONNABORTED' ||
44
+ err.code === 'ETIMEDOUT' ||
45
+ err.code === 'ECONNRESET';
46
+ };
47
+ const _delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
48
+ var HttpMethods;
49
+ (function (HttpMethods) {
50
+ HttpMethods["GET"] = "get";
51
+ HttpMethods["POST"] = "post";
52
+ HttpMethods["PUT"] = "put";
53
+ HttpMethods["DELETE"] = "delete";
54
+ HttpMethods["PATCH"] = "patch";
55
+ })(HttpMethods = exports.HttpMethods || (exports.HttpMethods = {}));
56
+ //# sourceMappingURL=http-request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-request.js","sourceRoot":"","sources":["../src/http-request.ts"],"names":[],"mappings":";;;;AAAA,+DAAyC;AAEzC,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,KAAK,CAAC,CAAC,aAAa;AAC/E,MAAM,cAAc,GAAG,IAAI,CAAC,CAAC,WAAW;AACxC,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;AAEnD,MAAM,eAAe,GAAG,KAAK,EAAE,OAA2B,EAAE,EAAE;IACnE,IAAI;QACF,OAAO,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;KACvC;IACD,OAAO,GAAG,EAAE;QACV,sCAAsC;QACtC,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,MAAM,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,eAAe,OAAO,CAAC,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;QACnH,IAAI,YAAY,CAAC,GAAG,CAAC,EAAE;YACrB,sCAAsC;YACtC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,eAAe,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC;YAC1G,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;YAC7B,OAAO,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;SACvC;QACD,MAAM,GAAG,CAAC;KACX;AACH,CAAC,CAAC;AAfW,QAAA,eAAe,mBAe1B;AAEF,MAAM,eAAe,GAAG,KAAK,EAAE,OAA2B,EAAE,EAAE;IAC5D,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IAC/D,IAAI,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC;SAClC,IAAI,CAAC,kBAAkB,CAAC;SACxB,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAE/B,IAAI,KAAK,EAAE;QACT,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;KAChC;IAED,IAAI,IAAI,EAAE;QACR,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;KAC9B;IAED,OAAO,MAAM,OAAO,CAAC;AACvB,CAAC,CAAC;AAEF,0BAA0B;AAC1B,MAAM,YAAY,GAAG,CAAC,GAAG,EAAW,EAAE;IACpC,OAAO,GAAG,CAAC,OAAO;QAChB,GAAG,CAAC,IAAI,KAAK,cAAc;QAC3B,GAAG,CAAC,IAAI,KAAK,cAAc;QAC3B,GAAG,CAAC,IAAI,KAAK,WAAW;QACxB,GAAG,CAAC,IAAI,KAAK,YAAY,CAAC;AAC9B,CAAC,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAS/E,IAAY,WAMX;AAND,WAAY,WAAW;IACrB,0BAAW,CAAA;IACX,4BAAa,CAAA;IACb,0BAAW,CAAA;IACX,gCAAiB,CAAA;IACjB,8BAAe,CAAA;AACjB,CAAC,EANW,WAAW,GAAX,mBAAW,KAAX,mBAAW,QAMtB"}
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const node_util_1 = tslib_1.__importDefault(require("node:util"));
5
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+ const mcp_server_1 = require("./mcp-server");
7
+ const transport = new stdio_js_1.StdioServerTransport();
8
+ transport.onmessage = async (message) => {
9
+ const output = 'Loadmill MCP:\n' +
10
+ node_util_1.default.inspect(message, { depth: null, colors: false });
11
+ process.stderr.write(output + '\n'); // one write, one `[warning]` block
12
+ };
13
+ transport.onerror = async (error) => {
14
+ const output = 'Loadmill MCP Error:\n' +
15
+ node_util_1.default.inspect(error, { depth: null, colors: false });
16
+ process.stderr.write(output + '\n'); // one write, one `[warning]` block
17
+ };
18
+ exports.default = async () => {
19
+ await mcp_server_1.mcpServer.connect(transport);
20
+ };
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,kEAA6B;AAE7B,wEAAiF;AACjF,6CAAyC;AAEzC,MAAM,SAAS,GAAG,IAAI,+BAAoB,EAAE,CAAC;AAE7C,SAAS,CAAC,SAAS,GAAG,KAAK,EAAE,OAAO,EAAE,EAAE;IACtC,MAAM,MAAM,GACV,iBAAiB;QACjB,mBAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACxD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,mCAAmC;AAC1E,CAAC,CAAC;AAEF,SAAS,CAAC,OAAO,GAAG,KAAK,EAAE,KAAK,EAAE,EAAE;IAClC,MAAM,MAAM,GACV,uBAAuB;QACvB,mBAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,mCAAmC;AAC1E,CAAC,CAAC;AAGF,kBAAe,KAAK,IAAI,EAAE;IACxB,MAAM,sBAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AACrC,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare type MCPHandler = {
2
+ schema: any;
3
+ handler: (request: any) => Promise<any>;
4
+ };
5
+ export declare const handlers: MCPHandler[];
@@ -0,0 +1,157 @@
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 tools_js_1 = require("./tools.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: 'run_test_suite',
39
+ title: 'Run Test Suite',
40
+ description: 'A tool to run a Loadmill test suite. Returns the id of the running suite',
41
+ inputSchema: {
42
+ type: 'object',
43
+ properties: {
44
+ id: {
45
+ type: 'string',
46
+ description: 'ID of the test suite to run',
47
+ },
48
+ },
49
+ },
50
+ },
51
+ {
52
+ name: 'get_test_suite_run',
53
+ title: 'Get Test Suite Run',
54
+ description: 'A tool to get a Loadmill test suite run by its ID. Returns the run details.',
55
+ inputSchema: {
56
+ type: 'object',
57
+ properties: {
58
+ id: {
59
+ type: 'string',
60
+ description: 'ID of the test suite run to fetch',
61
+ },
62
+ },
63
+ },
64
+ },
65
+ {
66
+ name: constants_js_1.SEARCH_TEST_PLAN,
67
+ title: 'Search Test Plans',
68
+ description: 'A tool to search for Loadmill test plans. Returns a list of test plans with their descriptions (names) and the author\'s information.',
69
+ inputSchema: {
70
+ type: 'object',
71
+ properties: {
72
+ search: {
73
+ type: 'string',
74
+ description: 'free text filter to search for test suites',
75
+ },
76
+ page: {
77
+ type: 'integer',
78
+ description: 'page number for pagination',
79
+ },
80
+ labels: {
81
+ type: 'array',
82
+ items: {
83
+ type: 'string',
84
+ description: 'filter suites that have flows with these labels',
85
+ },
86
+ },
87
+ },
88
+ },
89
+ },
90
+ {
91
+ name: constants_js_1.RUN_TEST_PLAN,
92
+ title: 'Run a Test Plan',
93
+ description: 'A tool to run a Loadmill test plan. Returns the id of the running plan',
94
+ inputSchema: {
95
+ type: 'object',
96
+ properties: {
97
+ id: {
98
+ type: 'string',
99
+ description: 'ID of the test plan to run',
100
+ },
101
+ },
102
+ },
103
+ },
104
+ {
105
+ name: constants_js_1.GET_TEST_PLAN_RUN,
106
+ title: 'Get Test Plan Run',
107
+ description: 'A tool to get a Loadmill test plan run by its ID. Returns the run details.',
108
+ inputSchema: {
109
+ type: 'object',
110
+ properties: {
111
+ id: {
112
+ type: 'string',
113
+ description: 'ID of the test plan run to fetch',
114
+ },
115
+ },
116
+ },
117
+ },
118
+ {
119
+ name: constants_js_1.GET_LABELS,
120
+ title: 'Get Labels',
121
+ description: 'A tool to fetch all available labels in Loadmill.',
122
+ inputSchema: {
123
+ type: 'object',
124
+ properties: {},
125
+ },
126
+ },
127
+ ];
128
+ return { tools };
129
+ },
130
+ },
131
+ {
132
+ schema: types_js_1.CallToolRequestSchema,
133
+ handler: async (request) => {
134
+ if (!constants_js_1.AVALIABLE_TOOLS.includes(request.params.name)) {
135
+ throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
136
+ }
137
+ const tool = tools_js_1.toolsTasks[request.params.name];
138
+ if (!tool) {
139
+ throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
140
+ }
141
+ return await tool(request.params);
142
+ },
143
+ },
144
+ {
145
+ schema: types_js_1.ListResourcesRequestSchema,
146
+ handler: async (_request) => {
147
+ // Handle the request
148
+ },
149
+ },
150
+ {
151
+ schema: types_js_1.ReadResourceRequestSchema,
152
+ handler: async (_request) => {
153
+ // Handle the request
154
+ },
155
+ },
156
+ ];
157
+ //# sourceMappingURL=mcp-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-handler.js","sourceRoot":"","sources":["../src/mcp-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ export declare const mcpServer: Server<{
3
+ method: string;
4
+ params?: {
5
+ [x: string]: unknown;
6
+ _meta?: {
7
+ [x: string]: unknown;
8
+ progressToken?: string | number | undefined;
9
+ } | undefined;
10
+ } | undefined;
11
+ }, {
12
+ method: string;
13
+ params?: {
14
+ [x: string]: unknown;
15
+ _meta?: {
16
+ [x: string]: unknown;
17
+ } | undefined;
18
+ } | undefined;
19
+ }, {
20
+ [x: string]: unknown;
21
+ _meta?: {
22
+ [x: string]: unknown;
23
+ } | undefined;
24
+ }>;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mcpServer = void 0;
4
+ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
5
+ const constants_1 = require("./constants");
6
+ const mcp_handler_js_1 = require("./mcp-handler.js");
7
+ let server;
8
+ const getInstace = () => {
9
+ if (!server) {
10
+ server = initMcpServer();
11
+ }
12
+ return server;
13
+ };
14
+ const initMcpServer = () => {
15
+ server = new index_js_1.Server({
16
+ name: constants_1.MCP_SERVER_NAME,
17
+ version: constants_1.MCP_SERVER_VERSION,
18
+ }, {
19
+ capabilities: {
20
+ resources: {},
21
+ tools: {},
22
+ },
23
+ });
24
+ setupToolHandlers(server);
25
+ process.on('SIGINT', async () => {
26
+ await server.close();
27
+ });
28
+ return server;
29
+ };
30
+ const setupToolHandlers = (server) => {
31
+ mcp_handler_js_1.handlers.forEach((handler) => {
32
+ server.setRequestHandler(handler.schema, handler.handler);
33
+ });
34
+ };
35
+ exports.mcpServer = getInstace();
36
+ //# sourceMappingURL=mcp-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":";;;AAAA,wEAAmE;AACnE,2CAAkE;AAClE,qDAA4C;AAE5C,IAAI,MAAc,CAAC;AAGnB,MAAM,UAAU,GAAG,GAAG,EAAE;IACtB,IAAI,CAAC,MAAM,EAAE;QACX,MAAM,GAAG,aAAa,EAAE,CAAC;KAC1B;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,GAAG,EAAE;IACzB,MAAM,GAAG,IAAI,iBAAM,CACjB;QACE,IAAI,EAAE,2BAAe;QACrB,OAAO,EAAE,8BAAkB;KAC5B,EACD;QACE,YAAY,EAAE;YACZ,SAAS,EAAE,EAAE;YACb,KAAK,EAAE,EAAE;SACV;KACF,CACF,CAAC;IAGF,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAE1B,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;QAC9B,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,EAAE;IACnC,yBAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC3B,MAAM,CAAC,iBAAiB,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEW,QAAA,SAAS,GAAG,UAAU,EAAE,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare type ToolTasks = {
2
+ [name: string]: (params: any) => Promise<any>;
3
+ };
4
+ export declare const toolsTasks: ToolTasks;
package/dist/tools.js ADDED
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toolsTasks = void 0;
4
+ const http_request_js_1 = require("./http-request.js");
5
+ const constants_js_1 = require("./constants.js");
6
+ //const LOADMILL_URL = process.env.LOADMILL_URL || 'https://api.loadmill.com';
7
+ const LOADMILL_URL = process.env.LOADMILL_URL || 'http://localhost:8090';
8
+ const BASE_URL = LOADMILL_URL + '/api';
9
+ // todo make sure all return in the same allowed pattern
10
+ exports.toolsTasks = {
11
+ [constants_js_1.SEARCH_TEST_SUITES]: async (params) => {
12
+ const args = params.arguments;
13
+ // eslint-disable-next-line no-console
14
+ console.log('Searching test suites with args:', args);
15
+ const response = await (0, http_request_js_1.sendHttpRequest)({
16
+ method: http_request_js_1.HttpMethods.GET,
17
+ url: `${BASE_URL}/test-suites`,
18
+ query: {
19
+ search: args.search || '',
20
+ filter: args.filter || 'All',
21
+ fromDateFilter: args.fromDateFilter || '',
22
+ toDateFilter: args.toDateFilter || '',
23
+ labels: args.labels || [],
24
+ },
25
+ });
26
+ return {
27
+ content: [{
28
+ type: 'text',
29
+ text: JSON.stringify(response, null, 2),
30
+ }],
31
+ };
32
+ },
33
+ [constants_js_1.RUN_TEST_SUITE]: async (params) => {
34
+ const args = params.arguments;
35
+ // eslint-disable-next-line no-console
36
+ console.log('Running test suite with args:', args);
37
+ const response = await (0, http_request_js_1.sendHttpRequest)({
38
+ method: http_request_js_1.HttpMethods.POST,
39
+ url: `${BASE_URL}/test-suites/${args.id}/run`,
40
+ });
41
+ return {
42
+ content: [{
43
+ type: 'text',
44
+ text: JSON.stringify(response, null, 2),
45
+ }],
46
+ };
47
+ },
48
+ [constants_js_1.GET_TEST_SUITE_RUN]: async (params) => {
49
+ const args = params.arguments;
50
+ // eslint-disable-next-line no-console
51
+ console.log('Getting test suite run with args:', args);
52
+ const response = await (0, http_request_js_1.sendHttpRequest)({
53
+ method: http_request_js_1.HttpMethods.GET,
54
+ url: `${BASE_URL}/test-suites-runs/${args.id}`,
55
+ });
56
+ return {
57
+ content: [{
58
+ type: 'text',
59
+ text: JSON.stringify(response, null, 2),
60
+ }],
61
+ };
62
+ },
63
+ [constants_js_1.SEARCH_TEST_PLAN]: async (params) => {
64
+ const args = params.arguments;
65
+ // eslint-disable-next-line no-console
66
+ console.log('Searching test plans with args:', args);
67
+ const response = await (0, http_request_js_1.sendHttpRequest)({
68
+ method: http_request_js_1.HttpMethods.GET,
69
+ url: `${BASE_URL}/test-plans`,
70
+ query: {
71
+ search: args.search || '',
72
+ filter: args.filter || 'All',
73
+ fromDateFilter: args.fromDateFilter || '',
74
+ toDateFilter: args.toDateFilter || '',
75
+ labels: args.labels || [],
76
+ },
77
+ });
78
+ return {
79
+ content: [{
80
+ type: 'text',
81
+ text: JSON.stringify(response, null, 2),
82
+ }],
83
+ };
84
+ },
85
+ [constants_js_1.RUN_TEST_PLAN]: async (params) => {
86
+ const args = params.arguments;
87
+ // eslint-disable-next-line no-console
88
+ console.log('Running test plan with args:', args);
89
+ const response = await (0, http_request_js_1.sendHttpRequest)({
90
+ method: http_request_js_1.HttpMethods.POST,
91
+ url: `${BASE_URL}/test-plans/${args.id}/run`,
92
+ });
93
+ return {
94
+ content: [{
95
+ type: 'text',
96
+ text: JSON.stringify(response, null, 2),
97
+ }],
98
+ };
99
+ },
100
+ [constants_js_1.GET_TEST_PLAN_RUN]: async (params) => {
101
+ const args = params.arguments;
102
+ // eslint-disable-next-line no-console
103
+ console.log('Getting test plan run with args:', args);
104
+ const response = await (0, http_request_js_1.sendHttpRequest)({
105
+ method: http_request_js_1.HttpMethods.GET,
106
+ url: `${BASE_URL}/test-plans-runs/${args.id}`,
107
+ });
108
+ return {
109
+ content: [{
110
+ type: 'text',
111
+ text: JSON.stringify(response, null, 2),
112
+ }],
113
+ };
114
+ },
115
+ [constants_js_1.GET_LABELS]: async (params) => {
116
+ const args = params.arguments;
117
+ // eslint-disable-next-line no-console
118
+ console.log('Getting labels with args:', args);
119
+ const response = await (0, http_request_js_1.sendHttpRequest)({
120
+ method: http_request_js_1.HttpMethods.GET,
121
+ url: `${BASE_URL}/labels`,
122
+ });
123
+ return {
124
+ content: [{
125
+ type: 'text',
126
+ text: JSON.stringify(response, null, 2),
127
+ }],
128
+ };
129
+ },
130
+ };
131
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":";;;AAAA,uDAAiE;AACjE,iDAQwB;AAMxB,8EAA8E;AAC9E,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,uBAAuB,CAAC;AACzE,MAAM,QAAQ,GAAG,YAAY,GAAG,MAAM,CAAC;AAEvC,wDAAwD;AAC3C,QAAA,UAAU,GAAc;IACnC,CAAC,iCAAkB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC;QAC9B,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,kCAAkC,EAAE,IAAI,CAAC,CAAC;QAEtD,MAAM,QAAQ,GAAG,MAAM,IAAA,iCAAe,EAAC;YACrC,MAAM,EAAE,6BAAW,CAAC,GAAG;YACvB,GAAG,EAAE,GAAG,QAAQ,cAAc;YAC9B,KAAK,EAAE;gBACL,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE;gBACzB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK;gBAC5B,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,EAAE;gBACzC,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,EAAE;gBACrC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE;aAC1B;SACF,CAAC,CAAC;QACH,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;iBACxC,CAAC;SACH,CAAC;IACJ,CAAC;IACD,CAAC,6BAAc,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QACjC,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC;QAC9B,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,+BAA+B,EAAE,IAAI,CAAC,CAAC;QAEnD,MAAM,QAAQ,GAAG,MAAM,IAAA,iCAAe,EAAC;YACrC,MAAM,EAAE,6BAAW,CAAC,IAAI;YACxB,GAAG,EAAE,GAAG,QAAQ,gBAAgB,IAAI,CAAC,EAAE,MAAM;SAC9C,CAAC,CAAC;QACH,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;iBACxC,CAAC;SACH,CAAC;IACJ,CAAC;IACD,CAAC,iCAAkB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC;QAC9B,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,mCAAmC,EAAE,IAAI,CAAC,CAAC;QAEvD,MAAM,QAAQ,GAAG,MAAM,IAAA,iCAAe,EAAC;YACrC,MAAM,EAAE,6BAAW,CAAC,GAAG;YACvB,GAAG,EAAE,GAAG,QAAQ,qBAAqB,IAAI,CAAC,EAAE,EAAE;SAC/C,CAAC,CAAC;QACH,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;iBACxC,CAAC;SACH,CAAC;IACJ,CAAC;IACD,CAAC,+BAAgB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QACnC,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC;QAC9B,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,iCAAiC,EAAE,IAAI,CAAC,CAAC;QAErD,MAAM,QAAQ,GAAG,MAAM,IAAA,iCAAe,EAAC;YACrC,MAAM,EAAE,6BAAW,CAAC,GAAG;YACvB,GAAG,EAAE,GAAG,QAAQ,aAAa;YAC7B,KAAK,EAAE;gBACL,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE;gBACzB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK;gBAC5B,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,EAAE;gBACzC,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,EAAE;gBACrC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE;aAC1B;SACF,CAAC,CAAC;QACH,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;iBACxC,CAAC;SACH,CAAC;IACJ,CAAC;IACD,CAAC,4BAAa,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAChC,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC;QAC9B,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,8BAA8B,EAAE,IAAI,CAAC,CAAC;QAElD,MAAM,QAAQ,GAAG,MAAM,IAAA,iCAAe,EAAC;YACrC,MAAM,EAAE,6BAAW,CAAC,IAAI;YACxB,GAAG,EAAE,GAAG,QAAQ,eAAe,IAAI,CAAC,EAAE,MAAM;SAC7C,CAAC,CAAC;QACH,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;iBACxC,CAAC;SACH,CAAC;IACJ,CAAC;IACD,CAAC,gCAAiB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QACpC,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC;QAC9B,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,kCAAkC,EAAE,IAAI,CAAC,CAAC;QAEtD,MAAM,QAAQ,GAAG,MAAM,IAAA,iCAAe,EAAC;YACrC,MAAM,EAAE,6BAAW,CAAC,GAAG;YACvB,GAAG,EAAE,GAAG,QAAQ,oBAAoB,IAAI,CAAC,EAAE,EAAE;SAC9C,CAAC,CAAC;QACH,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;iBACxC,CAAC;SACH,CAAC;IACJ,CAAC;IACD,CAAC,yBAAU,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC;QAC9B,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,IAAI,CAAC,CAAC;QAE/C,MAAM,QAAQ,GAAG,MAAM,IAAA,iCAAe,EAAC;YACrC,MAAM,EAAE,6BAAW,CAAC,GAAG;YACvB,GAAG,EAAE,GAAG,QAAQ,SAAS;SAC1B,CAAC,CAAC;QACH,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;iBACxC,CAAC;SACH,CAAC;IACJ,CAAC;CACF,CAAC"}
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@loadmill/mcp",
3
+ "version": "0.0.2-rc.1",
4
+ "description": "Loadmill mcp library",
5
+ "main": "dist/",
6
+ "module": "src/",
7
+ "bin": {
8
+ "loadmill-mcp": "bin/loadmill-mcp"
9
+ },
10
+ "scripts": {
11
+ "ts-watch": "rimraf dist && tsc -w -p tsconfig.json",
12
+ "build": "rimraf dist && tsc -p tsconfig.json",
13
+ "check-types": "tsc -p tsconfig.json --noEmit",
14
+ "prepack": "yarn build"
15
+ },
16
+ "engines": {
17
+ "node": ">=18.0.0"
18
+ },
19
+ "license": "Apache-2.0",
20
+ "devDependencies": {
21
+ "@types/node": "18.11.9"
22
+ },
23
+ "dependencies": {
24
+ "@modelcontextprotocol/sdk": "^1.17.2",
25
+ "superagent": "^10.0.1"
26
+ }
27
+ }