@nbakka/mcp-appium 2.0.25 → 2.0.27

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.
Files changed (2) hide show
  1. package/lib/server.js +52 -76
  2. package/package.json +1 -1
package/lib/server.js CHANGED
@@ -141,27 +141,68 @@ const createMcpServer = () => {
141
141
 
142
142
  tool(
143
143
  "mobile_learn_current_app_context",
144
- "Follow this instructions strictly to navigate through the app. This should be the first step after launching the app and before performing any tests.",
145
- {},
146
- async () => {
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: zod_1.z.string().describe("The name of the Google Sheet to fetch locator data from"),
147
+ },
148
+ async ({ sheetName }) => {
147
149
  try {
148
- // Read file from config folder
149
- const filePath = path.join(__dirname, '', 'app_context.txt');
150
- const fileContent = await fs.readFile(filePath, 'utf-8');
150
+ // Read app context notes from file
151
+ const notesFilePath = path.join(__dirname, 'app_context.txt');
152
+ const fileContent = await fs.readFile(notesFilePath, 'utf-8');
151
153
 
152
- // Assuming each note is separated by a newline
153
- // Trim to remove empty lines, filter out blank lines
154
154
  const notes = fileContent
155
155
  .split('\n')
156
156
  .map(line => line.trim())
157
157
  .filter(line => line.length > 0);
158
158
 
159
- const context = { notes };
159
+ // Initialize response object
160
+ const context = { notes, locatorData: null };
161
+
162
+ if (sheetName && sheetName.trim() !== '') {
163
+ // Load Google Sheets credentials
164
+ const keyFile = path.join(__dirname, 'secret.json');
165
+ const credentials = JSON.parse(await fs.readFile(keyFile, 'utf-8'));
166
+
167
+ const auth = new google.auth.GoogleAuth({
168
+ credentials,
169
+ scopes: ['https://www.googleapis.com/auth/spreadsheets.readonly'],
170
+ });
171
+
172
+ const authClient = await auth.getClient();
173
+ const sheets = google.sheets({ version: 'v4', auth: authClient });
174
+ const spreadsheetId = '1UapR81AxaztDUlPGDV-_EwHo2hWXkKCZXl8ALsvIyxA';
175
+
176
+ const range = `${sheetName}!A1:Z1000`;
177
+ const res = await sheets.spreadsheets.values.get({ spreadsheetId, range });
178
+ const rows = res.data.values;
179
+
180
+ if (!rows || rows.length === 0) {
181
+ return `Sheet "${sheetName}" is empty or does not exist. Notes loaded: ${JSON.stringify(notes)}`;
182
+ }
183
+
184
+ // Normalize headers
185
+ const header = rows[0].map(h => h.toString().toLowerCase().trim());
186
+ const locatorNameIdx = header.indexOf('locatorName');
187
+ const androidLocatorIdx = header.findIndex(h => h === 'androidXpath' || h === 'androidLocator');
188
+
189
+ if (locatorNameIdx === -1 || androidLocatorIdx === -1) {
190
+ return `Required columns "locator name" and/or "android xpath" not found in sheet "${sheetName}". Notes loaded: ${JSON.stringify(notes)}`;
191
+ }
192
+
193
+ const locatorData = rows.slice(1)
194
+ .filter(row => row[locatorNameIdx] && row[androidLocatorIdx])
195
+ .map(row => ({
196
+ locatorName: row[locatorNameIdx],
197
+ androidLocator: row[androidLocatorIdx],
198
+ }));
199
+
200
+ context.locatorData = locatorData;
201
+ }
160
202
 
161
203
  return `App context learned: ${JSON.stringify(context)}`;
162
204
  } catch (error) {
163
- // Handle file read errors gracefully
164
- return `Error reading app context notes: ${error.message}`;
205
+ return `Error reading app context notes or fetching locator data: ${error.message}`;
165
206
  }
166
207
  }
167
208
  );
@@ -274,71 +315,6 @@ const createMcpServer = () => {
274
315
  }
275
316
  );
276
317
 
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
318
  return server;
343
319
  };
344
320
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nbakka/mcp-appium",
3
- "version": "2.0.25",
3
+ "version": "2.0.27",
4
4
  "description": "Appium MCP",
5
5
  "engines": {
6
6
  "node": ">=18"