@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.
Files changed (2) hide show
  1. package/lib/server.js +56 -76
  2. 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 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: {
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 file from config folder
149
- const filePath = path.join(__dirname, '', 'app_context.txt');
150
- const fileContent = await fs.readFile(filePath, 'utf-8');
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
- const context = { notes };
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
- // Handle file read errors gracefully
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
 
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.26",
4
4
  "description": "Appium MCP",
5
5
  "engines": {
6
6
  "node": ">=18"