---
description:
globs:
alwaysApply: false
---
#### 5. MCP Inspector Specifics
* **URL Consistency:** The MCP Inspector URL (`http://127.0.0.1:6274`) was found to be consistent between runs, simplifying Safari targeting.
* **"Connected" State vs. iTerm Logs:** A key finding was that the Safari Inspector UI can show "Connected" (and tools subsequently work) even if detailed `DEBUG`-level logs from the launched server process (`start.sh` -> `node dist/server.js`) do not appear in the iTerm console where `npx @modelcontextprotocol/inspector` is running. The Inspector seems to show its own proxying/connection logs, but the full stdout/stderr of the child might not always be visible there. This means successful connection and tool usability are the primary indicators, and absence of detailed server logs in the iTerm console is not necessarily a showstopper for basic interaction, though it would affect deeper debugging of the server itself.
These notes capture the iterative process and key takeaways from the Safari automation for the MCP Inspector. The successful methods are now enshrined in `mcp-inspector.mdc`, while this document provides the background and context.
This contrasts with `mcp-inspector.mdc`, which is designed to be the concise, polished, and operational ruleset for future automated runs once a specific automation flow (like connecting to the MCP Inspector) has been stabilized and proven reliable. `mcp-inspector.mdc` should contain the 'final' working scripts and minimal necessary commentary, while `safari.mdc` is the space for the extended antechamber of discovery.
* **Clarification on `[WORKSPACE_PATH]` Resolution:** The placeholder `[WORKSPACE_PATH]` used in rules (e.g., for script paths like `[WORKSPACE_PATH]/start.sh`) must be dynamically replaced by the AI with the absolute path of the current project workspace. This path is typically available to the AI from its context (e.g., derived from `user_info.workspace_path` or a similar environment variable). It is crucial that the AI ensures the resolved path is correctly quoted if it's used in shell commands or script arguments, especially if the path might contain spaces or special characters. For instance, a path like `/Users/username/My Projects/project-name` should be passed as `'/Users/username/My Projects/project-name'` in a shell command.
---
### Strategies for Robust Element Selection
When automating UI interactions, the reliability of your scripts heavily depends on how you identify and select HTML elements. Here's a hierarchy of preferences and tips for making your selectors more robust:
1. **`data-testid` Attributes (Gold Standard):**
* **Why:** These are custom attributes specifically added for testing and automation. They are decoupled from styling and functional implementation details, making them the most resilient to UI changes.
* **Example (CSS):** `[data-testid='user-login-button']`
* **Example (XPath):** `//*[@data-testid='user-login-button']`
2. **Unique `id` Attributes:**
* **Why:** `id` attributes are *supposed* to be unique within a page. If developers adhere to this, they are very reliable.
* **Example (CSS):** `#submit-form`
* **Example (XPath):** `//*[@id='submit-form']`
3. **Stable `aria-label`, `aria-labelledby`, `role`, or other Accessibility Attributes:**
* **Why:** Accessibility attributes are often more stable than class names used for styling, as they relate to the element's function and purpose.
* **Example (CSS):** `button[aria-label='Open settings']`
* **Example (XPath):** `//button[@aria-label='Open settings']`
4. **Stable Class Names (Used for Structure/Function, Not Just Styling):**
* **Why:** Some class names indicate the structure or function of an element rather than just its appearance. These can be reasonably stable. Avoid classes that are purely presentational (e.g., `color-blue`, `margin-small`).
* **Example (CSS):** `.user-profile-card .username` (Contextual selection)
* **Example (XPath):** `//div[contains(@class, 'user-profile-card')]//span[contains(@class, 'username')]`
5. **Structural XPaths (Based on DOM hierarchy):**
* **Why:** Relying on the element's position within the DOM (e.g., "the second `div` inside a `section` with a specific header"). These are more brittle than attribute-based selectors because any structural change can break them. Use sparingly and keep them as simple as possible.
* **Example (XPath):** `//section[@id='main-content']/div[2]/p`
6. **Text-Based XPaths (Using visible text):**
* **Why:** Selecting elements based on their visible text content (e.g., a button with the text "Submit"). Can be useful, but prone to breakage if the text changes (e.g., for localization or wording updates).
* **Example (XPath):** `//button[text()='Submit']` or `//button[contains(text(), 'Submit')]`
* **Tip for Robustness:** Use XPath's `normalize-space()` function to handle variations in whitespace (leading, trailing, multiple internal spaces).
* `//button[normalize-space(text())='Submit']` (Matches " Submit ", "Submit", " Submit" etc.)
* `//a[contains(normalize-space(.), 'Learn More')]` (Checks within any descendant text nodes)
**General Tips for Selectors:**
* **Prefer CSS Selectors for Simplicity and Speed:** When applicable, CSS selectors are often more concise and can be faster than XPaths.
* **Use Browser Developer Tools:** Actively use the "Inspect Element" feature in your browser to test and refine your CSS selectors and XPaths. Most dev tools allow you to directly test them.
* **Avoid Generated IDs/Classes:** Be wary of IDs or class names that look auto-generated (e.g., `id="ext-gen1234"`), as these are likely to change between page loads or application versions.
* **Context is Key:** Instead of overly complex global selectors, try to select a stable parent element first, then find the target element within that parent's context. This often leads to simpler and more reliable selectors.
---
### Debugging AppleScript `do JavaScript` Execution Flow
Successfully executing JavaScript via AppleScript's `do JavaScript` command often involves navigating two potential layers of errors: AppleScript parsing errors and JavaScript runtime errors. Here's how to approach debugging:
**1. Differentiating Error Types:**
* **AppleScript Compile-Time/Parsing Errors (e.g., `-2741`):**
* **Symptom:** The AppleScript editor shows an error, or the script fails immediately when run, often with error messages like "Syntax Error," "Expected end of line but found...", or specific error codes like `-2741` (which typically means the command couldn't be parsed correctly, often due to malformed strings or incorrect quoting).
* **Cause:** The AppleScript interpreter itself cannot understand the structure of your `do JavaScript "..."` command, usually due to incorrect quoting or escaping of characters *within the AppleScript string that defines the JavaScript code*.
* **The JavaScript code itself hasn't even been sent to Safari yet.**
* **JavaScript Runtime Errors:**
* **Symptom:** The AppleScript command runs without an immediate AppleScript error, but the desired action doesn't occur in Safari, or `do JavaScript` returns an error message from the JavaScript engine (e.g., "TypeError: null is not an object" or "SyntaxError: Unexpected identifier").
* **Cause:** The JavaScript code was successfully passed to Safari, but the JavaScript engine encountered an error while trying to execute it (e.g., trying to access a property of a non-existent element, incorrect JS syntax, etc.).
**2. Debugging AppleScript Syntax/Parsing Errors:**
* **Simplify the JavaScript String:** Start with the simplest possible JavaScript that should work, e.g.:
```applescript
tell application "Safari"
do JavaScript "'test';" in front document
end tell
```
* **Log the Constructed JavaScript String:** Before the `do JavaScript` line, use AppleScript's `log` command to print the exact JavaScript string you are about to send. This helps you visually inspect it for quoting issues.
```applescript
set jsCommand to "document.getElementById(\"myButton\").click();"
log jsCommand
tell application "Safari"
do JavaScript jsCommand in front document
end tell
```
Check the logged output carefully in Script Editor's "Messages" tab.
* **Build Complex Strings Incrementally:** If your JavaScript is complex, build it in parts using AppleScript variables. This can make it easier to manage quoting for each part.
* **Master Quoting:**
* If AppleScript string is in double quotes (`"..."`): Escape internal JS double quotes as `\"`. JS single quotes usually don't need escaping.
* Use `character id 39` for single quotes if constructing JS with many internal single quotes to avoid confusion: `set sQuote to character id 39`. `set jsCommand to "var name = " & sQuote & "Pete" & sQuote & ";"`
**3. Debugging JavaScript Runtime Errors:**
* **Test in Safari's Web Inspector Console:** The most effective way to debug the JavaScript itself is to open Safari, navigate to the target page, open the Web Inspector (Develop > Show Web Inspector), and paste your JavaScript snippet directly into the Console. This provides immediate feedback, error messages, and allows for interactive debugging.
* **Use `try...catch` in Your JavaScript:** Wrap your JavaScript code in a `try...catch` block to capture and return error messages back to AppleScript. This can make it much easier to see what went wrong inside Safari.
```applescript
set jsCommand to "try { document.getElementById('nonExistentElement').value = 'test'; return 'Success'; } catch(e) { return 'JS Error: ' + e.name + ': ' + e.message; }"
tell application "Safari"
set jsResult to do JavaScript jsCommand in front document
log jsResult
end tell
```
* **Return Values for Debugging:** Have your JavaScript return intermediate values or status indicators to AppleScript to understand its state.
```applescript
set jsCommand to "var el = document.getElementById('myField'); if (el) { return 'Element found!'; } else { return 'Element NOT found.'; }"
log (do JavaScript jsCommand in front document)
```
By systematically checking for AppleScript parsing issues first, then moving to debug the JavaScript logic within Safari's environment, you can effectively troubleshoot `do JavaScript` commands.
---
### Advanced Asynchronous Handling: Polling for Conditions
Web pages load and update content asynchronously. Relying on fixed `delay` commands in AppleScript after an action (like a click or page navigation) can be unreliable because the actual time needed for the UI to update can vary due to network speed, server load, or client-side processing.
A more robust approach is to actively poll for a specific condition to be met (e.g., an element appearing, text changing, a certain JavaScript variable becoming true) before proceeding. This makes your scripts more resilient to timing variations.
**How Polling Works:**
1. Define the JavaScript code that checks for your desired condition (this should return `true` or `false`).
2. In AppleScript, create a loop that:
* Executes the JavaScript check.
* If the condition is met, exit the loop.
* If not, wait for a short interval (e.g., 0.5 seconds).
* Include a counter or timeout mechanism to prevent the loop from running indefinitely if the condition is never met.
**Example: Polling for 'Connected' Status in MCP Inspector**
This AppleScript snippet demonstrates polling for the text "Connected" to appear on the page after clicking the connect button:
```applescript
-- JavaScript to check if the page body contains the text "Connected"
set jsCheckConnected to "document.body.innerText.includes('Connected');"
set isNowConnected to false
set attempts to 0
set maxAttempts to 20 -- Set a reasonable limit, e.g., 20 attempts
set pollInterval to 0.5 -- Wait 0.5 seconds between attempts
log "Polling for 'Connected' status..."
tell application "Safari"
tell front document
repeat while isNowConnected is false and attempts < maxAttempts
try
if (do JavaScript jsCheckConnected) is true then
set isNowConnected to true
log "Status changed to 'Connected' after " & (attempts + 1) & " attempts."
else
delay pollInterval
end if
on error errMsg number errNum
log "Error during JavaScript check (attempt " & (attempts + 1) & "): " & errMsg & " (Number: " & errNum & ")"
-- Decide if you want to stop on error or just log and continue
delay pollInterval -- Still delay even if JS itself errored, maybe it's a temporary issue
end try
set attempts to attempts + 1
end repeat
end tell
end tell
if isNowConnected then
log "Successfully confirmed 'Connected' status via polling."
-- Proceed with next actions that depend on being connected
else
log "Failed to see 'Connected' status within " & (maxAttempts * pollInterval) & " seconds."
-- Handle the failure case (e.g., log error, stop script)
end if
```
**Benefits of Polling:**
* **Increased Reliability:** Scripts wait only as long as necessary, adapting to real-time conditions rather than fixed, potentially too short or too long, delays.
* **Reduced Brittleness:** Less likely to fail due to unexpected slowdowns.
* **Clearer Intent:** The script explicitly states what condition it's waiting for.
**Considerations:**
* **Timeout:** Always implement a maximum number of attempts or a total timeout to prevent infinite loops if the condition never occurs.
* **Poll Interval:** Choose a reasonable interval. Too short can be resource-intensive; too long can make the script feel sluggish.
* **Error Handling:** Include `try...on error` blocks within your loop to gracefully handle potential errors during the JavaScript execution (e.g., if the page is still transitioning and elements are not yet available).
---
### Meta-Level Collaboration & Rule Evolution Notes