Skip to content

Commit b8a67ef

Browse files
address review comments
Co-authored-by: Saikrishna321 <[email protected]>
1 parent 5e524ab commit b8a67ef

15 files changed

+93
-660
lines changed

docs/CONTRIBUTING.md

Lines changed: 40 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ Welcome! This guide will help you extend MCP Appium by adding new tools and reso
66

77
- [Adding New Tools](#adding-new-tools)
88
- [Adding New Resources](#adding-new-resources)
9-
- [Tool Metadata with YAML](#tool-metadata-with-yaml)
109
- [Code Style Guidelines](#code-style-guidelines)
10+
- [Formatting Best Practices](#formatting-best-practices)
1111

1212
---
1313

@@ -250,107 +250,13 @@ export default function registerResources(server: any) {
250250

251251
---
252252

253-
## Tool Metadata with YAML
254-
255-
For better maintainability, tool metadata can be defined in YAML files.
256-
257-
### YAML File Structure
258-
259-
Create a YAML file for your tool metadata:
260-
261-
```yaml
262-
# src/tools/metadata/my-tool.yaml
263-
name: appium_my_tool
264-
description: |
265-
This is a detailed description of what the tool does.
266-
It can span multiple lines.
267-
268-
parameters:
269-
param1:
270-
type: string
271-
description: Description of the first parameter
272-
required: true
273-
param2:
274-
type: number
275-
description: Description of the second parameter
276-
required: false
277-
278-
annotations:
279-
readOnly: false
280-
openWorld: false
281-
282-
# Instructions for prompt-based tools (optional)
283-
instructions: |
284-
## Instructions for AI
285-
- Point 1
286-
- Point 2
287-
```
288-
289-
### Using YAML Metadata
290-
291-
```typescript
292-
// src/tools/my-tool.ts
293-
import { loadToolMetadata } from './metadata-loader.js';
294-
import yaml from 'js-yaml';
295-
import fs from 'fs';
296-
import { z } from 'zod';
297-
298-
export default function myTool(server: FastMCP): void {
299-
// Load YAML metadata
300-
const yamlPath = './src/tools/metadata/my-tool.yaml';
301-
const metadata = yaml.load(fs.readFileSync(yamlPath, 'utf8'));
302-
303-
// Convert YAML schema to Zod schema
304-
const parameters = buildZodSchema(metadata.parameters);
305-
306-
server.addTool({
307-
name: metadata.name,
308-
description: metadata.description,
309-
parameters,
310-
annotations: {
311-
readOnlyHint: metadata.annotations.readOnly,
312-
openWorldHint: metadata.annotations.openWorld,
313-
},
314-
execute: async (args: any, context: any): Promise<any> => {
315-
// Tool implementation
316-
// You can access metadata.instructions if needed
317-
},
318-
});
319-
}
320-
```
321-
322-
### Benefits of YAML
323-
324-
1. **Maintainability**: Metadata separated from implementation
325-
2. **Clarity**: Easier to read and understand tool definitions
326-
3. **Version Control**: Track metadata changes independently
327-
4. **Internationalization**: Easier to translate descriptions
328-
5. **Documentation**: Auto-generate docs from YAML
329-
330-
### When to Use YAML
331-
332-
**Use YAML when:**
333-
334-
- Tool has complex instructions or descriptions
335-
- Multiple tools share similar metadata
336-
- You want to version metadata separately
337-
- You need to generate documentation automatically
338-
339-
**Use inline metadata when:**
340-
341-
- Tool is simple and straightforward
342-
- Metadata is tightly coupled with implementation
343-
- You prefer keeping everything in one place
344-
345-
---
346253

347254
## Code Style Guidelines
348255

349256
### 1. File Naming
350257

351258
- Tools: `kebab-case.ts` (e.g., `boot-simulator.ts`)
352259
- Resources: `kebab-case.ts` (e.g., `java-template.ts`)
353-
- YAML files: `tool-name.yaml`
354260

355261
### 2. Function Exports
356262

@@ -438,6 +344,45 @@ After adding a new tool:
438344

439345
---
440346

347+
---
348+
349+
## Formatting Best Practices
350+
351+
### Long Descriptions
352+
353+
For better readability when descriptions are long, use template literals with proper indentation:
354+
355+
**Bad (hard to read):**
356+
```typescript
357+
description: 'REQUIRED: First ASK THE USER which mobile platform they want to use (Android or iOS) before creating a session. DO NOT assume or default to any platform. You MUST explicitly prompt the user to choose between Android or iOS. This is mandatory before proceeding to use the create_session tool.',
358+
```
359+
360+
**Good (readable):**
361+
```typescript
362+
description: `REQUIRED: First ASK THE USER which mobile platform they want to use (Android or iOS) before creating a session.
363+
DO NOT assume or default to any platform.
364+
You MUST explicitly prompt the user to choose between Android or iOS.
365+
This is mandatory before proceeding to use the create_session tool.
366+
`,
367+
```
368+
369+
### Parameter Descriptions
370+
371+
For long parameter descriptions, also use template literals:
372+
373+
```typescript
374+
parameters: z.object({
375+
platform: z
376+
.enum(['ios', 'android'])
377+
.describe(
378+
`REQUIRED: Must match the platform the user explicitly selected via the select_platform tool.
379+
DO NOT default to Android or iOS without asking the user first.`
380+
),
381+
})
382+
```
383+
384+
---
385+
441386
## Need Help?
442387

443388
- Check existing tools in `src/tools/`

package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
},
99
"scripts": {
1010
"build": "rimraf dist && tsc && chmod +x dist/index.js && npm run copy-docs",
11-
"copy-docs": "mkdir -p dist/tools/documentation/uploads && cp -f src/tools/documentation/uploads/documents.json dist/tools/documentation/uploads/documents.json 2>/dev/null || echo 'No documents.json found, run npm run index-docs first'",
11+
"copy-docs": "mkdir -p dist/tools/documentation/uploads && cp -f src/tools/documentation/uploads/documents.json dist/tools/documentation/uploads/documents.json 2>/dev/null || true",
1212
"start": "node src/index.js",
1313
"start:stdio": "node src/index.js",
1414
"start:sse": "node src/index.js --sse",
@@ -43,7 +43,6 @@
4343
"appium-xcuitest-driver": "^10.2.1",
4444
"fast-xml-parser": "^5.2.3",
4545
"fastmcp": "^1.23.2",
46-
"js-yaml": "^4.1.0",
4746
"langchain": "^0.3.27",
4847
"lodash": "^4.17.21",
4948
"node-simctl": "^8.0.4",
@@ -61,7 +60,6 @@
6160
"conventional-changelog-conventionalcommits": "^8.0.0",
6261
"@jest/globals": "^29.7.0",
6362
"@types/jest": "^29.5.12",
64-
"@types/js-yaml": "^4.0.9",
6563
"@types/lodash": "^4.17.17",
6664
"@types/node": "^22.15.18",
6765
"@typescript-eslint/eslint-plugin": "^8.34.0",

src/scripts/simple-index-documentation.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,7 @@ if (args.length > 0 && args[0]) {
4747
}
4848
} else {
4949
// Use default path to resources directory
50-
const __filename = fileURLToPath(import.meta.url);
51-
const __dirname = path.dirname(__filename);
52-
markdownPath = path.resolve(__dirname, '../resources');
50+
markdownPath = path.resolve(process.cwd(), 'src/resources');
5351
console.log(`Using default resources directory: ${markdownPath}`);
5452
}
5553

src/tools/README.md

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,6 @@ export default function registerTools(server: FastMCP): void {
107107
}
108108
```
109109

110-
## Using YAML Metadata (Optional)
111-
112-
For better maintainability, you can store tool metadata in YAML files:
113-
114-
1. Create `src/tools/metadata/my-tool.yaml`
115-
2. Use `loadToolMetadata()` to load metadata
116-
3. See `metadata/README.md` for YAML schema
117-
4. Example: `scroll-with-yaml.example.ts`
118-
119110
## Best Practices
120111

121112
1. **Always check for active session**: Use `getDriver()` and check for null

src/tools/answerAppium.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import {
1010
export default function answerAppium(server: any): void {
1111
server.addTool({
1212
name: 'appium_documentation_query',
13-
description: `Query Appium documentation using RAG (Retrieval-Augmented Generation). This tool searches through indexed Appium documentation to answer questions about Appium features, setup, configuration, drivers, and usage.`,
13+
description: `Query Appium documentation using RAG (Retrieval-Augmented Generation).
14+
This tool searches through indexed Appium documentation to answer questions about Appium features, setup, configuration, drivers, and usage.
15+
`,
1416
parameters: z.object({
1517
query: z
1618
.string()

src/tools/boot-simulator.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,13 @@ import { IOSManager } from '../devicemanager/ios-manager.js';
88
export default function bootSimulator(server: any): void {
99
server.addTool({
1010
name: 'boot_simulator',
11-
description:
12-
'Boot an iOS simulator and wait for it to be ready. This speeds up subsequent session creation by ensuring the simulator is already running.',
11+
description: `Boot an iOS simulator and wait for it to be ready.
12+
This speeds up subsequent session creation by ensuring the simulator is already running.`,
1313
parameters: z.object({
14-
udid: z
15-
.string()
16-
.describe(
17-
'The UDID of the iOS simulator to boot. Use select_platform and select_device tools first to get the UDID.'
18-
),
14+
udid: z.string().describe(
15+
`The UDID of the iOS simulator to boot.
16+
Use select_platform and select_device tools first to get the UDID.`
17+
),
1918
}),
2019
annotations: {
2120
readOnlyHint: false,

src/tools/create-session.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,16 @@ interface CapabilitiesConfig {
3636
export default function createSession(server: any): void {
3737
server.addTool({
3838
name: 'create_session',
39-
description:
40-
'Create a new mobile session with Android or iOS device (MUST use select_platform tool first to ask the user which platform they want - DO NOT assume or default to any platform)',
39+
description: `Create a new mobile session with Android or iOS device.
40+
MUST use select_platform tool first to ask the user which platform they want.
41+
DO NOT assume or default to any platform.
42+
`,
4143
parameters: z.object({
4244
platform: z
4345
.enum(['ios', 'android'])
4446
.describe(
45-
'REQUIRED: Must match the platform the user explicitly selected via the select_platform tool. DO NOT default to Android or iOS without asking the user first.'
47+
`REQUIRED: Must match the platform the user explicitly selected via the select_platform tool.
48+
DO NOT default to Android or iOS without asking the user first.`
4649
),
4750
capabilities: z
4851
.object({})

src/tools/install-wda.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,9 @@ async function isWDARunning(simulatorUdid: string): Promise<boolean> {
158158
export default function installWDA(server: any): void {
159159
server.addTool({
160160
name: 'install_wda',
161-
description:
162-
'Install and launch the WebDriverAgent (WDA) app on a booted iOS simulator using the app path from setup_wda. This tool requires WDA to be already set up using setup_wda and at least one simulator to be booted.',
161+
description: `Install and launch the WebDriverAgent (WDA) app on a booted iOS simulator using the app path from setup_wda.
162+
This tool requires WDA to be already set up using setup_wda and at least one simulator to be booted.
163+
`,
163164
parameters: z.object({
164165
simulatorUdid: z
165166
.string()
@@ -171,7 +172,8 @@ export default function installWDA(server: any): void {
171172
.string()
172173
.optional()
173174
.describe(
174-
'The path to the WDA app bundle (.app file) that be generated by setup_wda tool. If not provided, will try to find the latest cached WDA app.'
175+
`The path to the WDA app bundle (.app file) that be generated by setup_wda tool.
176+
If not provided, will try to find the latest cached WDA app.`
175177
),
176178
}),
177179
annotations: {

src/tools/interactions/screenshot.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import { FastMCP } from 'fastmcp/dist/FastMCP.js';
22
import { z } from 'zod';
33
import { getDriver } from '../sessionStore.js';
4+
import { writeFile } from 'fs/promises';
5+
import { join } from 'path';
46

57
export default function screenshot(server: FastMCP): void {
68
server.addTool({
79
name: 'appium_screenshot',
8-
description: 'Take a screenshot of the current screen in base64 format',
10+
description:
11+
'Take a screenshot of the current screen and return as PNG image',
912
annotations: {
1013
readOnlyHint: false,
1114
openWorldHint: false,
@@ -17,12 +20,24 @@ export default function screenshot(server: FastMCP): void {
1720
}
1821

1922
try {
20-
const screenshot = await driver.getScreenshot();
23+
const screenshotBase64 = await driver.getScreenshot();
24+
25+
// Convert base64 to buffer
26+
const screenshotBuffer = Buffer.from(screenshotBase64, 'base64');
27+
28+
// Generate filename with timestamp
29+
const timestamp = Date.now();
30+
const filename = `screenshot_${timestamp}.png`;
31+
const filepath = join(process.cwd(), filename);
32+
33+
// Save screenshot to disk
34+
await writeFile(filepath, screenshotBuffer);
35+
2136
return {
2237
content: [
2338
{
2439
type: 'text',
25-
text: screenshot,
40+
text: `Screenshot saved successfully to: ${filename}`,
2641
},
2742
],
2843
};

0 commit comments

Comments
 (0)