@nbakka/mcp-appium 2.0.25 → 2.0.26
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/lib/server.js +56 -76
- package/package.json +1 -1
package/lib/server.js
CHANGED
|
@@ -141,27 +141,72 @@ const createMcpServer = () => {
|
|
|
141
141
|
|
|
142
142
|
tool(
|
|
143
143
|
"mobile_learn_current_app_context",
|
|
144
|
-
"Follow
|
|
145
|
-
{
|
|
146
|
-
|
|
144
|
+
"Follow these instructions strictly to navigate through the app. This should be the first step after launching the app and before performing any tests. Provide the Google Sheet name to fetch locatorName and androidLocator from the specified sheet.",
|
|
145
|
+
{
|
|
146
|
+
sheetName: {
|
|
147
|
+
type: "string",
|
|
148
|
+
description: "Exact name of the Google Sheets tab (sheet) to fetch locator data from.",
|
|
149
|
+
// required by zod or MCP implicitly if needed
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
async ({ sheetName }) => {
|
|
147
153
|
try {
|
|
148
|
-
// Read
|
|
149
|
-
const
|
|
150
|
-
const fileContent = await fs.readFile(
|
|
154
|
+
// Read app context notes from file
|
|
155
|
+
const notesFilePath = path.join(__dirname, 'app_context.txt');
|
|
156
|
+
const fileContent = await fs.readFile(notesFilePath, 'utf-8');
|
|
151
157
|
|
|
152
|
-
// Assuming each note is separated by a newline
|
|
153
|
-
// Trim to remove empty lines, filter out blank lines
|
|
154
158
|
const notes = fileContent
|
|
155
159
|
.split('\n')
|
|
156
160
|
.map(line => line.trim())
|
|
157
161
|
.filter(line => line.length > 0);
|
|
158
162
|
|
|
159
|
-
|
|
163
|
+
// Initialize response object
|
|
164
|
+
const context = { notes, locatorData: null };
|
|
165
|
+
|
|
166
|
+
if (sheetName && sheetName.trim() !== '') {
|
|
167
|
+
// Load Google Sheets credentials
|
|
168
|
+
const keyFile = path.join(__dirname, 'secret.json');
|
|
169
|
+
const credentials = JSON.parse(await fs.readFile(keyFile, 'utf-8'));
|
|
170
|
+
|
|
171
|
+
const auth = new google.auth.GoogleAuth({
|
|
172
|
+
credentials,
|
|
173
|
+
scopes: ['https://www.googleapis.com/auth/spreadsheets.readonly'],
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const authClient = await auth.getClient();
|
|
177
|
+
const sheets = google.sheets({ version: 'v4', auth: authClient });
|
|
178
|
+
const spreadsheetId = '1UapR81AxaztDUlPGDV-_EwHo2hWXkKCZXl8ALsvIyxA';
|
|
179
|
+
|
|
180
|
+
const range = `${sheetName}!A1:Z1000`;
|
|
181
|
+
const res = await sheets.spreadsheets.values.get({ spreadsheetId, range });
|
|
182
|
+
const rows = res.data.values;
|
|
183
|
+
|
|
184
|
+
if (!rows || rows.length === 0) {
|
|
185
|
+
return `Sheet "${sheetName}" is empty or does not exist. Notes loaded: ${JSON.stringify(notes)}`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Normalize headers
|
|
189
|
+
const header = rows[0].map(h => h.toString().toLowerCase().trim());
|
|
190
|
+
const locatorNameIdx = header.indexOf('locator name');
|
|
191
|
+
const androidLocatorIdx = header.findIndex(h => h === 'android xpath' || h === 'android locator');
|
|
192
|
+
|
|
193
|
+
if (locatorNameIdx === -1 || androidLocatorIdx === -1) {
|
|
194
|
+
return `Required columns "locator name" and/or "android xpath" not found in sheet "${sheetName}". Notes loaded: ${JSON.stringify(notes)}`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const locatorData = rows.slice(1)
|
|
198
|
+
.filter(row => row[locatorNameIdx] && row[androidLocatorIdx])
|
|
199
|
+
.map(row => ({
|
|
200
|
+
locatorName: row[locatorNameIdx],
|
|
201
|
+
androidLocator: row[androidLocatorIdx],
|
|
202
|
+
}));
|
|
203
|
+
|
|
204
|
+
context.locatorData = locatorData;
|
|
205
|
+
}
|
|
160
206
|
|
|
161
207
|
return `App context learned: ${JSON.stringify(context)}`;
|
|
162
208
|
} catch (error) {
|
|
163
|
-
|
|
164
|
-
return `Error reading app context notes: ${error.message}`;
|
|
209
|
+
return `Error reading app context notes or fetching locator data: ${error.message}`;
|
|
165
210
|
}
|
|
166
211
|
}
|
|
167
212
|
);
|
|
@@ -274,71 +319,6 @@ const createMcpServer = () => {
|
|
|
274
319
|
}
|
|
275
320
|
);
|
|
276
321
|
|
|
277
|
-
tool(
|
|
278
|
-
"fetch_google_sheet_locators",
|
|
279
|
-
"Follow this instructions strictly. MUST be invoked after mobile_learn_current_app_context tool. Provide the Google Sheet name to fetch locatorName and androidLocator from the specified sheet.",
|
|
280
|
-
{
|
|
281
|
-
sheetName: {
|
|
282
|
-
type: "string",
|
|
283
|
-
description: "Exact name of the Google Sheets tab (sheet) to fetch data from.",
|
|
284
|
-
required: true,
|
|
285
|
-
},
|
|
286
|
-
},
|
|
287
|
-
async ({ sheetName }) => {
|
|
288
|
-
if (!sheetName || sheetName.trim() === "") {
|
|
289
|
-
return "Error: sheetName is required and cannot be empty.";
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
try {
|
|
293
|
-
const fs = require('fs').promises;
|
|
294
|
-
const path = require('path');
|
|
295
|
-
const { google } = require('googleapis');
|
|
296
|
-
|
|
297
|
-
// Adjust path to your config folder as needed
|
|
298
|
-
const keyFile = path.join(__dirname, '', 'secret.json');
|
|
299
|
-
const credentials = JSON.parse(await fs.readFile(keyFile, 'utf-8'));
|
|
300
|
-
|
|
301
|
-
const auth = new google.auth.GoogleAuth({
|
|
302
|
-
credentials,
|
|
303
|
-
scopes: ['https://www.googleapis.com/auth/spreadsheets.readonly'],
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
const authClient = await auth.getClient();
|
|
307
|
-
const sheets = google.sheets({ version: 'v4', auth: authClient });
|
|
308
|
-
const spreadsheetId = '1UapR81AxaztDUlPGDV-_EwHo2hWXkKCZXl8ALsvIyxA';
|
|
309
|
-
|
|
310
|
-
const range = `${sheetName}!A1:Z1000`;
|
|
311
|
-
const res = await sheets.spreadsheets.values.get({ spreadsheetId, range });
|
|
312
|
-
const rows = res.data.values;
|
|
313
|
-
|
|
314
|
-
if (!rows || rows.length === 0) {
|
|
315
|
-
return `Sheet "${sheetName}" is empty or does not exist.`;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Normalize header keys for safe case-insensitive lookup
|
|
319
|
-
const header = rows[0].map(h => h.toString().toLowerCase().trim());
|
|
320
|
-
const locatorNameIdx = header.indexOf('locator name');
|
|
321
|
-
const androidLocatorIdx = header.findIndex(h => h === 'android xpath' || h === 'android locator');
|
|
322
|
-
|
|
323
|
-
if (locatorNameIdx === -1 || androidLocatorIdx === -1) {
|
|
324
|
-
return `Required columns "locator name" and/or "android xpath" not found in sheet "${sheetName}".`;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
const extracted = rows.slice(1)
|
|
328
|
-
.filter(row => row[locatorNameIdx] && row[androidLocatorIdx])
|
|
329
|
-
.map(row => ({
|
|
330
|
-
locatorName: row[locatorNameIdx],
|
|
331
|
-
androidLocator: row[androidLocatorIdx],
|
|
332
|
-
}));
|
|
333
|
-
|
|
334
|
-
return extracted;
|
|
335
|
-
} catch (error) {
|
|
336
|
-
return `Error fetching data from Google Sheets: ${error.message}`;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
);
|
|
340
|
-
|
|
341
|
-
|
|
342
322
|
return server;
|
|
343
323
|
};
|
|
344
324
|
|