wopee-mcp 1.2.1 → 1.3.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 ADDED
@@ -0,0 +1,257 @@
1
+ # Wopee MCP Server
2
+
3
+ A Model Context Protocol (MCP) server for interacting with Wopee.io's autonomous testing platform. This server provides tools for managing analysis suites, generating test cases, user stories, and dispatching autonomous testing agents.
4
+
5
+ ## Setup
6
+
7
+ ### Prerequisites
8
+
9
+ - Node.js (v18 or higher recommended)
10
+ - An IDE that supports MCP (Model Context Protocol), such as Cursor or VSCode
11
+
12
+ ### MCP Server Configuration
13
+
14
+ Add this server to your MCP configuration.
15
+
16
+ ### Configuration Example
17
+
18
+ ```json
19
+ {
20
+ "mcpServers": {
21
+ "wopee": {
22
+ "command": "npx wopee-mcp",
23
+ "env": {
24
+ "WOPEE_PROJECT_UUID": "your-project-uuid-here",
25
+ "WOPEE_API_KEY": "your-api-key-here"
26
+ }
27
+ }
28
+ }
29
+ }
30
+ ```
31
+
32
+ #### Required Environment Variables
33
+
34
+ - **`WOPEE_PROJECT_UUID`** - Your Wopee project UUID. This identifies which project you're working with.
35
+ - **`WOPEE_API_KEY`** - Your Wopee API key. You can create one at [cmd.wopee.io](https://cmd.wopee.io), in your project's settings.
36
+
37
+ #### Optional Environment Variables
38
+
39
+ - **`WOPEE_API_URL`** - The Wopee API endpoint URL. Should be specified only for testing/development purposes.
40
+
41
+ ## Getting Started
42
+
43
+ Most tools in this MCP server require a `suiteUuid` to operate. You have two options to get started:
44
+
45
+ ### Option 1: Use Existing Suites
46
+
47
+ Start by fetching your existing analysis suites:
48
+
49
+ ```
50
+ Use the wopee_fetch_analysis_suites tool to retrieve all available suites for your project.
51
+ ```
52
+
53
+ This will return a list of all analysis suites with their UUIDs, which you can then use with other tools.
54
+
55
+ ### Option 2: Create a New Suite
56
+
57
+ If you don't have any suites yet, create a fresh analysis suite:
58
+
59
+ ```
60
+ Use the wopee_dispatch_analysis tool to create and dispatch a new analysis/crawling suite.
61
+ ```
62
+
63
+ This will create a new suite and return its UUID, which you can use for subsequent operations.
64
+
65
+ ## Available Tools
66
+
67
+ ### Suite Management
68
+
69
+ #### `wopee_fetch_analysis_suites`
70
+
71
+ Fetches all analysis suites for your project. This is a good starting point to see what suites are available.
72
+
73
+ - **Returns:** Array of analysis suites with their UUIDs, names, statuses, and metadata
74
+
75
+ **Example Usage:**
76
+
77
+ ```
78
+ Fetch all existing analysis suites for my project
79
+ ```
80
+
81
+ #### `wopee_dispatch_analysis`
82
+
83
+ Creates and dispatches a new analysis/crawling suite for your project. Use this to start a fresh analysis session.
84
+
85
+ - **Returns:** Success message with the created suite information
86
+
87
+ **Example Usage:**
88
+
89
+ ```
90
+ Dispatch a new analysis suite
91
+ ```
92
+
93
+ ### Generation Tools
94
+
95
+ These tools generate various artifacts for a specific suite. All require a `suiteUuid`.
96
+
97
+ #### `wopee_generate_app_context`
98
+
99
+ Generates an application context markdown file for the selected suite. This provides a comprehensive overview of the application being analyzed.
100
+
101
+ - **Parameters:**
102
+ - `suiteUuid` - The UUID of the suite to generate context for
103
+ - **Returns:** Generated output in case of successful generation
104
+
105
+ **Example Usage:**
106
+
107
+ ```
108
+ Generate app context for suite abc-123-def-456
109
+ ```
110
+
111
+ #### `wopee_generate_general_user_stories`
112
+
113
+ Generates general user stories markdown file for the selected suite.
114
+
115
+ - **Parameters:**
116
+ - `suiteUuid` - The UUID of the suite
117
+ - **Returns:** Generated output in case of successful generation
118
+
119
+ **Example Usage:**
120
+
121
+ ```
122
+ Generate general user stories for my latest suite
123
+ ```
124
+
125
+ #### `wopee_generate_user_stories`
126
+
127
+ Generates detailed user stories JSON file for the selected suite.
128
+
129
+ - **Parameters:**
130
+ - `suiteUuid` - The UUID of the suite
131
+ - **Returns:** Generated output in case of successful generation
132
+
133
+ **Example Usage:**
134
+
135
+ ```
136
+ Generate user stories for suite abc-123-def-456
137
+ ```
138
+
139
+ #### `wopee_generate_test_cases`
140
+
141
+ Generates test cases for the selected suite based on the analysis and user stories.
142
+
143
+ - **Parameters:**
144
+ - `suiteUuid` - The UUID of the suite
145
+ - **Returns:** Generated output in case of successful generation
146
+
147
+ **Example Usage:**
148
+
149
+ ```
150
+ Generate test cases for suite abc-123-def-456
151
+ ```
152
+
153
+ ### Fetch Tools
154
+
155
+ These tools retrieve generated artifacts for a specific suite. All require a `suiteUuid`.
156
+
157
+ #### `wopee_fetch_app_context`
158
+
159
+ Fetches the application context markdown file for a suite.
160
+
161
+ - **Parameters:**
162
+ - `suiteUuid` - The UUID of the suite
163
+ - **Returns:** The application context markdown content
164
+
165
+ **Example Usage:**
166
+
167
+ ```
168
+ Fetch app context for my latest suite
169
+ ```
170
+
171
+ #### `wopee_fetch_general_user_stories`
172
+
173
+ Fetches the general user stories markdown file for a suite.
174
+
175
+ - **Parameters:**
176
+ - `suiteUuid` - The UUID of the suite
177
+ - **Returns:** The general user stories markdown content
178
+
179
+ **Example Usage:**
180
+
181
+ ```
182
+ Fetch general user stories for suite abc-123-def-456
183
+ ```
184
+
185
+ #### `wopee_fetch_user_stories`
186
+
187
+ Fetches the user stories JSON file for a suite.
188
+
189
+ - **Parameters:**
190
+ - `suiteUuid` - The UUID of the suite
191
+ - **Returns:** The user stories JSON content
192
+
193
+ **Example Usage:**
194
+
195
+ ```
196
+ Fetch user stories for suite abc-123-def-456
197
+ ```
198
+
199
+ #### `wopee_fetch_test_cases`
200
+
201
+ Fetches the test cases JSON file for a suite.
202
+
203
+ - **Parameters:**
204
+ - `suiteUuid` - The UUID of the suite
205
+ - **Returns:** The test cases JSON content
206
+
207
+ **Example Usage:**
208
+
209
+ ```
210
+ Fetch test cases for suite abc-123-def-456
211
+ ```
212
+
213
+ ### Agent Testing
214
+
215
+ #### `wopee_dispatch_agent`
216
+
217
+ Dispatches an autonomous testing agent to execute test cases for a selected suite.
218
+
219
+ - **Parameters:**
220
+ - `suiteUuid` - The UUID of the suite containing the test cases
221
+ - `analysisIdentifier` - The analysis identifier for the suite
222
+ - `testCases` - Array of test case objects to execute, each containing:
223
+ - `testCaseId` - The ID of the test case
224
+ - `userStoryId` - The ID of the associated user story
225
+ - **Returns:** Success message indicating the agent has been dispatched
226
+
227
+ **Example Usage:**
228
+
229
+ ```
230
+ Dispatch agent for my latest suite's user story US001 and test case TC003
231
+ ```
232
+
233
+ ## Typical Workflow
234
+
235
+ 1. **Start with a suite:**
236
+
237
+ - Use `wopee_fetch_analysis_suites` to see existing suites, OR
238
+ - Use `wopee_dispatch_analysis` to create a new suite
239
+
240
+ 2. **Generate artifacts:**
241
+
242
+ - Generate app context: `wopee_generate_app_context`
243
+ - Generate user stories: `wopee_generate_user_stories` or `wopee_generate_general_user_stories`
244
+ - Generate test cases: `wopee_generate_test_cases`
245
+
246
+ 3. **Fetch generated content:**
247
+
248
+ - Use the fetch tools to retrieve generated markdown/JSON files
249
+
250
+ 4. **Run tests:**
251
+ - Use `wopee_dispatch_agent` to execute test cases with the autonomous testing agent
252
+
253
+ ## Notes
254
+
255
+ - Most tools require a `suiteUuid`. Always start by fetching or creating a suite.
256
+ - `wopee_dispatch_analysis` tool will go through whole cycle of processing - crawling the application and generating all of the artifacts one by one.
257
+ - It is advisable to use [cmd.wopee.io](https://cmd.wopee.io) for a convenient visual representation of the generated data and results of the agent runs.
package/build/index.js ADDED
@@ -0,0 +1,19 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { TOOLS } from "./tools/index.js";
4
+ const server = new McpServer({
5
+ name: "wopee-mcp",
6
+ version: "1.0.0",
7
+ });
8
+ for (const { name, config, handler } of TOOLS) {
9
+ server.registerTool(name, config, handler);
10
+ }
11
+ async function main() {
12
+ const transport = new StdioServerTransport();
13
+ await server.connect(transport);
14
+ console.error("Wopee MCP server running on stdio");
15
+ }
16
+ main().catch((error) => {
17
+ console.error("Error starting Wopee MCP server:", error);
18
+ process.exit(1);
19
+ });
@@ -0,0 +1,24 @@
1
+ import { wopeeDispatchAgent } from "./wopee_dispatch_agent/index.js";
2
+ import { wopeeFetchTestCases } from "./wopee_fetch_test_cases/index.js";
3
+ import { wopeeFetchAppContext } from "./wopee_fetch_app_context/index.js";
4
+ import { wopeeDispatchAnalysis } from "./wopee_dispatch_analysis/index.js";
5
+ import { wopeeFetchUserStories } from "./wopee_fetch_user_stories/index.js";
6
+ import { wopeeGenerateTestCases } from "./wopee_generate_test_cases/index.js";
7
+ import { wopeeGenerateAppContext } from "./wopee_generate_app_context/index.js";
8
+ import { wopeeFetchAnalysisSuites } from "./wopee_fetch_analysis_suites/index.js";
9
+ import { wopeeGenerateUserStories } from "./wopee_generate_user_stories/index.js";
10
+ import { wopeeFetchGeneralUserStories } from "./wopee_fetch_general_user_stories/index.js";
11
+ import { wopeeGenerateGeneralUserStories } from "./wopee_generate_general_user_stories/index.js";
12
+ export const TOOLS = [
13
+ wopeeDispatchAnalysis,
14
+ wopeeDispatchAgent,
15
+ wopeeGenerateAppContext,
16
+ wopeeGenerateGeneralUserStories,
17
+ wopeeGenerateUserStories,
18
+ wopeeGenerateTestCases,
19
+ wopeeFetchAnalysisSuites,
20
+ wopeeFetchAppContext,
21
+ wopeeFetchGeneralUserStories,
22
+ wopeeFetchUserStories,
23
+ wopeeFetchTestCases,
24
+ ];
@@ -0,0 +1,32 @@
1
+ import { getConfig } from "../../utils/getConfig.js";
2
+ export const createFetchFileInput = (input) => {
3
+ const { WOPEE_PROJECT_UUID } = getConfig();
4
+ if (!WOPEE_PROJECT_UUID)
5
+ throw new Error("WOPEE_PROJECT_UUID is not set");
6
+ return {
7
+ projectUuid: WOPEE_PROJECT_UUID,
8
+ suiteUuid: input.suiteUuid,
9
+ bucket: input.bucket,
10
+ };
11
+ };
12
+ export const createGenerateAIDataInput = (input) => {
13
+ const { WOPEE_PROJECT_UUID } = getConfig();
14
+ if (!WOPEE_PROJECT_UUID)
15
+ throw new Error("WOPEE_PROJECT_UUID is not set");
16
+ return {
17
+ projectUuid: WOPEE_PROJECT_UUID,
18
+ suiteUuid: input.suiteUuid,
19
+ suiteAnalysisConfig: {
20
+ startingUrl: null,
21
+ username: null,
22
+ password: null,
23
+ cookiesPreference: null,
24
+ additionalInstructions: null,
25
+ additionalVariables: null,
26
+ },
27
+ continueGeneration: false,
28
+ extraPrompt: null,
29
+ selectedUserStories: null,
30
+ sourceSuiteUuid: null,
31
+ };
32
+ };
@@ -0,0 +1,106 @@
1
+ export const FetchFile = `
2
+ query FetchFile($projectUuid: ID!, $suiteUuid: ID!, $bucket: String!) {
3
+ fetchFile(projectUuid: $projectUuid, suiteUuid: $suiteUuid, bucket: $bucket)
4
+ }
5
+ `;
6
+ export const DispatchAgent = `
7
+ mutation DispatchAgent(
8
+ $input: TestCasesInput!
9
+ ) {
10
+ dispatchAgent(input: $input)
11
+ }
12
+ `;
13
+ export const DispatchAnalysis = `
14
+ mutation DispatchAnalysis($input: DispatchAnalysisInput!) {
15
+ dispatchAnalysis(input: $input) {
16
+ uuid
17
+ name
18
+ createdAt
19
+ updatedAt
20
+ suiteType
21
+ analysisIdentifier
22
+ suiteRunningStatus
23
+ generatedAnalysisDataState {
24
+ suiteUuid
25
+ appContext {
26
+ status
27
+ isGenerated
28
+ }
29
+ generalUserStories {
30
+ status
31
+ isGenerated
32
+ }
33
+ userStories {
34
+ status
35
+ isGenerated
36
+ }
37
+ testCases {
38
+ status
39
+ isGenerated
40
+ }
41
+ }
42
+ }
43
+ }
44
+ `;
45
+ export const FetchAnalysisSuites = `
46
+ query FetchAnalysisSuites($projectUuid: ID!) {
47
+ fetchAnalysisSuites(projectUuid: $projectUuid) {
48
+ uuid
49
+ name
50
+ suiteType
51
+ executionStatus
52
+ analysisIdentifier
53
+ suiteRunningStatus
54
+ createdAt
55
+ updatedAt
56
+ generatedAnalysisDataState {
57
+ uuid
58
+ suiteUuid
59
+ appContext {
60
+ uuid
61
+ isGenerated
62
+ status
63
+ }
64
+ generalUserStories {
65
+ uuid
66
+ isGenerated
67
+ status
68
+ }
69
+ userStories {
70
+ uuid
71
+ isGenerated
72
+ status
73
+ }
74
+ testCases {
75
+ uuid
76
+ isGenerated
77
+ status
78
+ }
79
+ }
80
+ }
81
+ }
82
+ `;
83
+ export const GenerateAppContext = `
84
+ mutation GenerateAppContext(
85
+ $input: GenerateAIDataInput!
86
+ ) {
87
+ generateAppContext(input: $input)
88
+ }
89
+ `;
90
+ export const GenerateGeneralUserStories = `
91
+ mutation GenerateGeneralUserStories(
92
+ $input: GenerateAIDataInput!
93
+ ) {
94
+ generateGeneralUserStories(input: $input)
95
+ }
96
+ `;
97
+ export const GenerateTestCases = `
98
+ mutation GenerateTestCases($input: GenerateAIDataInput!) {
99
+ generateTestCases(input: $input)
100
+ }
101
+ `;
102
+ export const GenerateUserStories = `
103
+ mutation GenerateUserStories($input: GenerateAIDataInput!) {
104
+ generateUserStories(input: $input)
105
+ }
106
+ `;
@@ -0,0 +1,123 @@
1
+ import { z } from "zod";
2
+ import { FetchFileInputSchema, GenerateAIDataInputSchema, } from "./schemas.js";
3
+ import { FetchFile, GenerateTestCases, GenerateAppContext, GenerateUserStories, GenerateGeneralUserStories, } from "./gql-queries.js";
4
+ import { createFetchFileInput, createGenerateAIDataInput, } from "./factories.js";
5
+ import { Bucket, GenerationType } from "./types.js";
6
+ import { requestClient } from "../../utils/requestClient.js";
7
+ export function parseError(error) {
8
+ console.error(error instanceof z.ZodError ? error.issues : error);
9
+ return {
10
+ content: [
11
+ {
12
+ type: "text",
13
+ text: `Failed to parse input: ${error instanceof z.ZodError
14
+ ? error.issues
15
+ .map((issue) => `${issue.path[0] ?? "Unknown"}: ${issue.message}`)
16
+ .join("\n")
17
+ : error instanceof Error
18
+ ? error.message
19
+ : "Unknown zod validation error"}`,
20
+ },
21
+ ],
22
+ };
23
+ }
24
+ function parseGenerationType(type) {
25
+ switch (type) {
26
+ case GenerationType.APP_CONTEXT:
27
+ return {
28
+ query: GenerateAppContext,
29
+ dataKey: "generateAppContext",
30
+ bucket: Bucket.APP_CONTEXT,
31
+ description: "application's context markdown file for selected suite",
32
+ };
33
+ case GenerationType.GENERAL_USER_STORIES:
34
+ return {
35
+ query: GenerateGeneralUserStories,
36
+ dataKey: "generateGeneralUserStories",
37
+ bucket: Bucket.GENERAL_USER_STORIES,
38
+ description: "general user stories markdown file for selected suite",
39
+ };
40
+ case GenerationType.USER_STORIES:
41
+ return {
42
+ query: GenerateUserStories,
43
+ dataKey: "generateUserStories",
44
+ bucket: Bucket.USER_STORIES,
45
+ description: "user stories JSON file for selected suite",
46
+ };
47
+ case GenerationType.TEST_CASES:
48
+ return {
49
+ query: GenerateTestCases,
50
+ dataKey: "generateTestCases",
51
+ bucket: Bucket.USER_STORIES,
52
+ description: "test cases for selected suite",
53
+ };
54
+ default:
55
+ return {
56
+ query: null,
57
+ dataKey: null,
58
+ description: null,
59
+ };
60
+ }
61
+ }
62
+ export async function fetchFile(input) {
63
+ try {
64
+ const fetchFileInput = createFetchFileInput(input);
65
+ const parsedInput = FetchFileInputSchema.parse(fetchFileInput);
66
+ const result = await requestClient(FetchFile, parsedInput);
67
+ if (!result || !result.fetchFile)
68
+ return {
69
+ content: [
70
+ {
71
+ type: "text",
72
+ text: "Failed to fetch file",
73
+ },
74
+ ],
75
+ };
76
+ return {
77
+ content: [
78
+ {
79
+ type: "text",
80
+ text: result.fetchFile,
81
+ },
82
+ ],
83
+ };
84
+ }
85
+ catch (error) {
86
+ return parseError(error);
87
+ }
88
+ }
89
+ export async function generateAIDataFile(type, input) {
90
+ const { query, dataKey, bucket, description } = parseGenerationType(type);
91
+ if (!query || !dataKey || !description || !bucket)
92
+ return {
93
+ content: [
94
+ {
95
+ type: "text",
96
+ text: "Failed to parse generation type",
97
+ },
98
+ ],
99
+ };
100
+ try {
101
+ const generateAIDataInput = createGenerateAIDataInput(input);
102
+ const parsedInput = GenerateAIDataInputSchema.parse(generateAIDataInput);
103
+ const generationResult = await requestClient(query, {
104
+ input: parsedInput,
105
+ });
106
+ if (!generationResult || !generationResult[dataKey])
107
+ return {
108
+ content: [
109
+ {
110
+ type: "text",
111
+ text: `Failed to generate ${description}`,
112
+ },
113
+ ],
114
+ };
115
+ return await fetchFile({
116
+ suiteUuid: parsedInput.suiteUuid,
117
+ bucket,
118
+ });
119
+ }
120
+ catch (error) {
121
+ return parseError(error);
122
+ }
123
+ }
@@ -0,0 +1,40 @@
1
+ import { z } from "zod";
2
+ import { Bucket } from "./types.js";
3
+ var CookiesPreference;
4
+ (function (CookiesPreference) {
5
+ CookiesPreference["ACCEPT_ALL"] = "ACCEPT_ALL";
6
+ CookiesPreference["DECLINE_ALL"] = "DECLINE_ALL";
7
+ CookiesPreference["IGNORE"] = "IGNORE";
8
+ })(CookiesPreference || (CookiesPreference = {}));
9
+ export const SuiteAnalysisConfigSchema = z.object({
10
+ startingUrl: z.string().nullable().default(null),
11
+ username: z.string().nullable().default(null),
12
+ password: z.string().nullable().default(null),
13
+ cookiesPreference: z.nativeEnum(CookiesPreference).nullable().default(null),
14
+ additionalInstructions: z.string().nullable().default(null),
15
+ additionalVariables: z.string().nullable().default(null),
16
+ });
17
+ export const GenerateAIDataHandlerInputSchema = z.object({
18
+ suiteUuid: z.string().min(1, "Suite UUID is required"),
19
+ });
20
+ export const GenerateAIDataInputSchema = z.object({
21
+ projectUuid: z.string().min(1, "Project UUID is required"),
22
+ suiteUuid: z.string().min(1, "Suite UUID is required"),
23
+ extraPrompt: z.string().nullish(),
24
+ sourceSuiteUuid: z.string().nullish(),
25
+ selectedUserStories: z.array(z.string()).nullish(),
26
+ suiteAnalysisConfig: SuiteAnalysisConfigSchema,
27
+ continueGeneration: z.boolean().nullish().default(false),
28
+ });
29
+ export const FetchFileHandlerInputSchema = z.object({
30
+ suiteUuid: z.string().min(1, "Suite UUID is required"),
31
+ });
32
+ export const FetchFileFactoryInputSchema = z.object({
33
+ suiteUuid: z.string().min(1, "Suite UUID is required"),
34
+ bucket: z.nativeEnum(Bucket),
35
+ });
36
+ export const FetchFileInputSchema = z.object({
37
+ projectUuid: z.string().min(1, "Project UUID is required"),
38
+ suiteUuid: z.string().min(1, "Suite UUID is required"),
39
+ bucket: z.nativeEnum(Bucket),
40
+ });
@@ -0,0 +1,60 @@
1
+ export var ToolName;
2
+ (function (ToolName) {
3
+ ToolName["WOPEE_DISPATCH_ANALYSIS"] = "wopee_dispatch_analysis";
4
+ ToolName["WOPEE_DISPATCH_AGENT"] = "wopee_dispatch_agent";
5
+ ToolName["WOPEE_FETCH_ANALYSIS_SUITES"] = "wopee_fetch_analysis_suites";
6
+ ToolName["WOPEE_FETCH_APP_CONTEXT"] = "wopee_fetch_app_context";
7
+ ToolName["WOPEE_FETCH_GENERAL_USER_STORIES"] = "wopee_fetch_general_user_stories";
8
+ ToolName["WOPEE_FETCH_USER_STORIES"] = "wopee_fetch_user_stories";
9
+ ToolName["WOPEE_FETCH_TEST_CASES"] = "wopee_fetch_test_cases";
10
+ ToolName["WOPEE_GENERATE_APP_CONTEXT"] = "wopee_generate_app_context";
11
+ ToolName["WOPEE_GENERATE_GENERAL_USER_STORIES"] = "wopee_generate_general_user_stories";
12
+ ToolName["WOPEE_GENERATE_USER_STORIES"] = "wopee_generate_user_stories";
13
+ ToolName["WOPEE_GENERATE_TEST_CASES"] = "wopee_generate_test_cases";
14
+ })(ToolName || (ToolName = {}));
15
+ export const Bucket = {
16
+ APP_CONTEXT: "project-suite-app-context",
17
+ GENERAL_USER_STORIES: "project-suite-general-user-stories",
18
+ USER_STORIES: "project-suite-user-stories",
19
+ PAGE_CONTENT: "screen-instance-page-content",
20
+ PROMPTS: "project-prompts",
21
+ PLAYWRIGHT_CODE: "project-suite-playwright-code",
22
+ UPLOADED_PAGE_DATA: "project-uploaded-page-data",
23
+ };
24
+ export var GenerationType;
25
+ (function (GenerationType) {
26
+ GenerationType["APP_CONTEXT"] = "APP_CONTEXT";
27
+ GenerationType["GENERAL_USER_STORIES"] = "GENERAL_USER_STORIES";
28
+ GenerationType["USER_STORIES"] = "USER_STORIES";
29
+ GenerationType["TEST_CASES"] = "TEST_CASES";
30
+ })(GenerationType || (GenerationType = {}));
31
+ export var SuiteType;
32
+ (function (SuiteType) {
33
+ SuiteType["BOT"] = "BOT";
34
+ SuiteType["CODE"] = "CODE";
35
+ SuiteType["AGENT"] = "AGENT";
36
+ SuiteType["ANALYSIS"] = "ANALYSIS";
37
+ SuiteType["INTEGRATION"] = "INTEGRATION";
38
+ SuiteType["UPLOADED_PAGE_DATA"] = "UPLOADED_PAGE_DATA";
39
+ })(SuiteType || (SuiteType = {}));
40
+ export var SuiteRunningStatus;
41
+ (function (SuiteRunningStatus) {
42
+ SuiteRunningStatus["IDLE"] = "IDLE";
43
+ SuiteRunningStatus["FINISHED"] = "FINISHED";
44
+ SuiteRunningStatus["IN_PROGRESS"] = "IN_PROGRESS";
45
+ SuiteRunningStatus["IN_PROGRESS_MERGE"] = "IN_PROGRESS_MERGE";
46
+ })(SuiteRunningStatus || (SuiteRunningStatus = {}));
47
+ export var ExecutionStatus;
48
+ (function (ExecutionStatus) {
49
+ ExecutionStatus["IN_PROGRESS"] = "IN_PROGRESS";
50
+ ExecutionStatus["FINISHED"] = "FINISHED";
51
+ ExecutionStatus["FAILED"] = "FAILED";
52
+ })(ExecutionStatus || (ExecutionStatus = {}));
53
+ export var GenerationStatus;
54
+ (function (GenerationStatus) {
55
+ GenerationStatus["NOT_STARTED"] = "NOT_STARTED";
56
+ GenerationStatus["IN_QUEUE"] = "IN_QUEUE";
57
+ GenerationStatus["IN_PROGRESS"] = "IN_PROGRESS";
58
+ GenerationStatus["FINISHED"] = "FINISHED";
59
+ GenerationStatus["FAILED"] = "FAILED";
60
+ })(GenerationStatus || (GenerationStatus = {}));
@@ -0,0 +1,13 @@
1
+ import { getConfig } from "../../utils/getConfig.js";
2
+ export const createDispatchAgentInput = (input) => {
3
+ const { WOPEE_PROJECT_UUID } = getConfig();
4
+ if (!WOPEE_PROJECT_UUID)
5
+ throw new Error("WOPEE_PROJECT_UUID is not set");
6
+ return {
7
+ projectUuid: WOPEE_PROJECT_UUID,
8
+ suiteUuid: input.suiteUuid,
9
+ analysisIdentifier: input.analysisIdentifier,
10
+ testCases: input.testCases,
11
+ skipRateLimitCheck: true,
12
+ };
13
+ };
@@ -0,0 +1,43 @@
1
+ import { DispatchAgentInputSchema, WopeeDispatchAgentInputSchema, } from "./schema.js";
2
+ import { ToolName } from "../shared/types.js";
3
+ import { parseError } from "../shared/handlers.js";
4
+ import { createDispatchAgentInput } from "./factory.js";
5
+ import { DispatchAgent } from "../shared/gql-queries.js";
6
+ import { requestClient } from "../../utils/requestClient.js";
7
+ export const wopeeDispatchAgent = {
8
+ name: ToolName.WOPEE_DISPATCH_AGENT,
9
+ config: {
10
+ title: "Dispatch agent",
11
+ description: "Dispatch agent testing for selected suite's test cases",
12
+ inputSchema: WopeeDispatchAgentInputSchema.shape,
13
+ },
14
+ handler: async (input) => {
15
+ try {
16
+ const dispatchAgentInput = createDispatchAgentInput(input);
17
+ const parsedInput = DispatchAgentInputSchema.parse(dispatchAgentInput);
18
+ const result = await requestClient(DispatchAgent, {
19
+ input: parsedInput,
20
+ });
21
+ if (!result || !result.dispatchAgent)
22
+ return {
23
+ content: [
24
+ {
25
+ type: "text",
26
+ text: "Failed to dispatch agent",
27
+ },
28
+ ],
29
+ };
30
+ return {
31
+ content: [
32
+ {
33
+ type: "text",
34
+ text: "Agent dispatched successfully",
35
+ },
36
+ ],
37
+ };
38
+ }
39
+ catch (error) {
40
+ return parseError(error);
41
+ }
42
+ },
43
+ };
@@ -0,0 +1,17 @@
1
+ import { z } from "zod";
2
+ const SelectedTestCasesSchema = z.object({
3
+ testCaseId: z.string().min(1, "Test case ID is required"),
4
+ userStoryId: z.string().min(1, "User story ID is required"),
5
+ });
6
+ export const WopeeDispatchAgentInputSchema = z.object({
7
+ suiteUuid: z.string().min(1, "Suite UUID is required"),
8
+ analysisIdentifier: z.string().min(1, "Analysis identifier is required"),
9
+ testCases: z.array(SelectedTestCasesSchema),
10
+ });
11
+ export const DispatchAgentInputSchema = z.object({
12
+ projectUuid: z.string().min(1, "Project UUID is required"),
13
+ suiteUuid: z.string().min(1, "Suite UUID is required"),
14
+ analysisIdentifier: z.string().min(1, "Analysis identifier is required"),
15
+ testCases: z.array(SelectedTestCasesSchema),
16
+ skipRateLimitCheck: z.boolean().nullable().default(true),
17
+ });
@@ -0,0 +1,18 @@
1
+ import { getConfig } from "../../utils/getConfig.js";
2
+ export const createDispatchAnalysisInput = () => {
3
+ const { WOPEE_PROJECT_UUID } = getConfig();
4
+ if (!WOPEE_PROJECT_UUID)
5
+ throw new Error("WOPEE_PROJECT_UUID is not set");
6
+ return {
7
+ projectUuid: WOPEE_PROJECT_UUID,
8
+ suiteAnalysisConfig: {
9
+ startingUrl: null,
10
+ username: null,
11
+ password: null,
12
+ cookiesPreference: null,
13
+ additionalInstructions: null,
14
+ additionalVariables: null,
15
+ },
16
+ rerun: null,
17
+ };
18
+ };
@@ -0,0 +1,42 @@
1
+ import { parseError } from "../shared/handlers.js";
2
+ import { DispatchAnalysisInputSchema } from "./schema.js";
3
+ import { createDispatchAnalysisInput } from "./factory.js";
4
+ import { DispatchAnalysis } from "../shared/gql-queries.js";
5
+ import { requestClient } from "../../utils/requestClient.js";
6
+ import { ToolName } from "../shared/types.js";
7
+ export const wopeeDispatchAnalysis = {
8
+ name: ToolName.WOPEE_DISPATCH_ANALYSIS,
9
+ config: {
10
+ title: "Dispatch analysis",
11
+ description: "Create and dispatch analysis/crawling suite for a project",
12
+ },
13
+ handler: async () => {
14
+ try {
15
+ const input = createDispatchAnalysisInput();
16
+ const parsedInput = DispatchAnalysisInputSchema.parse(input);
17
+ const result = await requestClient(DispatchAnalysis, {
18
+ input: parsedInput,
19
+ });
20
+ if (!result || !result.dispatchAnalysis)
21
+ return {
22
+ content: [
23
+ {
24
+ type: "text",
25
+ text: "Failed to dispatch agent",
26
+ },
27
+ ],
28
+ };
29
+ return {
30
+ content: [
31
+ {
32
+ type: "text",
33
+ text: "Agent dispatched successfully",
34
+ },
35
+ ],
36
+ };
37
+ }
38
+ catch (error) {
39
+ return parseError(error);
40
+ }
41
+ },
42
+ };
@@ -0,0 +1,17 @@
1
+ import { z } from "zod";
2
+ import { SuiteAnalysisConfigSchema } from "../shared/schemas.js";
3
+ var RerunMode;
4
+ (function (RerunMode) {
5
+ RerunMode["FULL"] = "FULL";
6
+ RerunMode["CRAWLING"] = "CRAWLING";
7
+ })(RerunMode || (RerunMode = {}));
8
+ const RerunOptionsSchema = z.object({
9
+ suiteUuid: z.string().min(1, "Suite UUID is required"),
10
+ analysisIdentifier: z.string().min(1, "Analysis identifier is required"),
11
+ mode: z.nativeEnum(RerunMode),
12
+ });
13
+ export const DispatchAnalysisInputSchema = z.object({
14
+ projectUuid: z.string().min(1, "Project UUID is required"),
15
+ suiteAnalysisConfig: SuiteAnalysisConfigSchema,
16
+ rerun: RerunOptionsSchema.nullable().default(null),
17
+ });
@@ -0,0 +1,43 @@
1
+ import { requestClient } from "../../utils/requestClient.js";
2
+ import { ToolName } from "../shared/types.js";
3
+ import { FetchAnalysisSuites } from "../shared/gql-queries.js";
4
+ import { getConfig } from "../../utils/getConfig.js";
5
+ export const wopeeFetchAnalysisSuites = {
6
+ name: ToolName.WOPEE_FETCH_ANALYSIS_SUITES,
7
+ config: {
8
+ title: "Fetch Analysis Suites",
9
+ description: "Fetch project's analysis suites from Woopee",
10
+ },
11
+ handler: async () => {
12
+ const { WOPEE_PROJECT_UUID } = getConfig();
13
+ if (!WOPEE_PROJECT_UUID)
14
+ return {
15
+ content: [
16
+ {
17
+ type: "text",
18
+ text: "WOPEE_PROJECT_UUID is not set",
19
+ },
20
+ ],
21
+ };
22
+ const result = await requestClient(FetchAnalysisSuites, {
23
+ projectUuid: WOPEE_PROJECT_UUID,
24
+ });
25
+ if (!result || !result.fetchAnalysisSuites)
26
+ return {
27
+ content: [
28
+ {
29
+ type: "text",
30
+ text: "Failed to fetch analysis suites",
31
+ },
32
+ ],
33
+ };
34
+ return {
35
+ content: [
36
+ {
37
+ type: "text",
38
+ text: JSON.stringify(result.fetchAnalysisSuites, null, 2),
39
+ },
40
+ ],
41
+ };
42
+ },
43
+ };
@@ -0,0 +1,15 @@
1
+ import { fetchFile } from "../shared/handlers.js";
2
+ import { Bucket, ToolName } from "../shared/types.js";
3
+ import { FetchFileHandlerInputSchema, } from "../shared/schemas.js";
4
+ export const wopeeFetchAppContext = {
5
+ name: ToolName.WOPEE_FETCH_APP_CONTEXT,
6
+ config: {
7
+ title: "Fetch app context",
8
+ description: "Fetch suite's application context markdown file for selected project",
9
+ inputSchema: FetchFileHandlerInputSchema.shape,
10
+ },
11
+ handler: async (input) => await fetchFile({
12
+ ...input,
13
+ bucket: Bucket.APP_CONTEXT,
14
+ }),
15
+ };
@@ -0,0 +1,15 @@
1
+ import { FetchFileHandlerInputSchema, } from "../shared/schemas.js";
2
+ import { fetchFile } from "../shared/handlers.js";
3
+ import { Bucket, ToolName } from "../shared/types.js";
4
+ export const wopeeFetchGeneralUserStories = {
5
+ name: ToolName.WOPEE_FETCH_GENERAL_USER_STORIES,
6
+ config: {
7
+ title: "Fetch general user stories",
8
+ description: "Fetch suite's general user stories markdown file for selected project",
9
+ inputSchema: FetchFileHandlerInputSchema.shape,
10
+ },
11
+ handler: async (input) => await fetchFile({
12
+ ...input,
13
+ bucket: Bucket.GENERAL_USER_STORIES,
14
+ }),
15
+ };
@@ -0,0 +1,15 @@
1
+ import { FetchFileHandlerInputSchema, } from "../shared/schemas.js";
2
+ import { fetchFile } from "../shared/handlers.js";
3
+ import { Bucket, ToolName } from "../shared/types.js";
4
+ export const wopeeFetchTestCases = {
5
+ name: ToolName.WOPEE_FETCH_TEST_CASES,
6
+ config: {
7
+ title: "Fetch test cases",
8
+ description: "Fetch suite's test cases JSON file for selected project",
9
+ inputSchema: FetchFileHandlerInputSchema.shape,
10
+ },
11
+ handler: async (input) => await fetchFile({
12
+ ...input,
13
+ bucket: Bucket.USER_STORIES,
14
+ }),
15
+ };
@@ -0,0 +1,15 @@
1
+ import { FetchFileHandlerInputSchema, } from "../shared/schemas.js";
2
+ import { fetchFile } from "../shared/handlers.js";
3
+ import { Bucket, ToolName } from "../shared/types.js";
4
+ export const wopeeFetchUserStories = {
5
+ name: ToolName.WOPEE_FETCH_USER_STORIES,
6
+ config: {
7
+ title: "Fetch user stories",
8
+ description: "Fetch suite's user stories JSON file for selected project",
9
+ inputSchema: FetchFileHandlerInputSchema.shape,
10
+ },
11
+ handler: async (input) => await fetchFile({
12
+ ...input,
13
+ bucket: Bucket.USER_STORIES,
14
+ }),
15
+ };
@@ -0,0 +1,12 @@
1
+ import { GenerateAIDataHandlerInputSchema, } from "../shared/schemas.js";
2
+ import { generateAIDataFile } from "../shared/handlers.js";
3
+ import { GenerationType, ToolName } from "../shared/types.js";
4
+ export const wopeeGenerateAppContext = {
5
+ name: ToolName.WOPEE_GENERATE_APP_CONTEXT,
6
+ config: {
7
+ title: "Generate app context",
8
+ description: "Generate application's context markdown file for selected suite",
9
+ inputSchema: GenerateAIDataHandlerInputSchema.shape,
10
+ },
11
+ handler: async (input) => await generateAIDataFile(GenerationType.APP_CONTEXT, input),
12
+ };
@@ -0,0 +1,12 @@
1
+ import { GenerateAIDataHandlerInputSchema, } from "../shared/schemas.js";
2
+ import { generateAIDataFile } from "../shared/handlers.js";
3
+ import { GenerationType, ToolName } from "../shared/types.js";
4
+ export const wopeeGenerateGeneralUserStories = {
5
+ name: ToolName.WOPEE_GENERATE_GENERAL_USER_STORIES,
6
+ config: {
7
+ title: "Generate general user stories",
8
+ description: "Generate general user stories markdown file for selected suite",
9
+ inputSchema: GenerateAIDataHandlerInputSchema.shape,
10
+ },
11
+ handler: async (input) => await generateAIDataFile(GenerationType.GENERAL_USER_STORIES, input),
12
+ };
@@ -0,0 +1,12 @@
1
+ import { GenerateAIDataHandlerInputSchema, } from "../shared/schemas.js";
2
+ import { generateAIDataFile } from "../shared/handlers.js";
3
+ import { GenerationType, ToolName } from "../shared/types.js";
4
+ export const wopeeGenerateTestCases = {
5
+ name: ToolName.WOPEE_GENERATE_TEST_CASES,
6
+ config: {
7
+ title: "Generate test cases",
8
+ description: "Generate test cases for selected suite",
9
+ inputSchema: GenerateAIDataHandlerInputSchema.shape,
10
+ },
11
+ handler: async (input) => await generateAIDataFile(GenerationType.TEST_CASES, input),
12
+ };
@@ -0,0 +1,12 @@
1
+ import { GenerateAIDataHandlerInputSchema, } from "../shared/schemas.js";
2
+ import { generateAIDataFile } from "../shared/handlers.js";
3
+ import { GenerationType, ToolName } from "../shared/types.js";
4
+ export const wopeeGenerateUserStories = {
5
+ name: ToolName.WOPEE_GENERATE_USER_STORIES,
6
+ config: {
7
+ title: "Generate user stories",
8
+ description: "Generate user stories JSON file for selected suite",
9
+ inputSchema: GenerateAIDataHandlerInputSchema.shape,
10
+ },
11
+ handler: async (input) => await generateAIDataFile(GenerationType.USER_STORIES, input),
12
+ };
@@ -0,0 +1,10 @@
1
+ export const getConfig = () => {
2
+ const WOPEE_API_URL = process.env.WOPEE_API_URL ?? "https://api.wopee.io";
3
+ const WOPEE_API_KEY = process.env.WOPEE_API_KEY ?? null;
4
+ const WOPEE_PROJECT_UUID = process.env.WOPEE_PROJECT_UUID ?? null;
5
+ return {
6
+ WOPEE_API_URL,
7
+ WOPEE_API_KEY,
8
+ WOPEE_PROJECT_UUID,
9
+ };
10
+ };
@@ -0,0 +1,37 @@
1
+ import { getConfig } from "./getConfig.js";
2
+ export const requestClient = async (query, variables) => {
3
+ const { WOPEE_API_URL, WOPEE_API_KEY } = getConfig();
4
+ try {
5
+ if (!WOPEE_API_KEY) {
6
+ console.error("[REQUEST_CLIENT_ERROR]: WOPEE_API_KEY is not set");
7
+ return null;
8
+ }
9
+ const headers = {
10
+ api_key: WOPEE_API_KEY,
11
+ "Content-Type": "application/json",
12
+ };
13
+ const response = await fetch(`${WOPEE_API_URL}`, {
14
+ method: "POST",
15
+ headers,
16
+ body: JSON.stringify({
17
+ query,
18
+ variables,
19
+ }),
20
+ });
21
+ if (!response.ok) {
22
+ const errorText = await response.text();
23
+ console.error("[REQUEST_CLIENT_ERROR]:", errorText);
24
+ return null;
25
+ }
26
+ const result = (await response.json());
27
+ if (result.errors && result.errors.length > 0) {
28
+ console.error("GraphQL errors:", result.errors);
29
+ return null;
30
+ }
31
+ return result.data || null;
32
+ }
33
+ catch (error) {
34
+ console.error("[REQUEST_CLIENT_ERROR]:", error);
35
+ return null;
36
+ }
37
+ };
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "bin": {
5
5
  "wopee-mcp": "./build/index.js"
6
6
  },
7
- "version": "1.2.1",
7
+ "version": "1.3.0",
8
8
  "description": "Wopee.io MCP server",
9
9
  "main": "./build/index.js",
10
10
  "scripts": {