mirror of
https://github.com/steipete/agent-rules.git
synced 2026-05-26 11:41:39 +02:00
Add Cursor rules collection from various projects
- Added cursor-rules/ directory with 4 valuable rule sets - MCP development guidelines with security patterns - Cursor rules meta-guide for creating rules - Safari automation patterns and techniques - MCP Inspector debugging workflows These rules demonstrate proven patterns for development, automation, testing, and debugging across different project types. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -28,3 +28,51 @@ The `llms/` directory contains reference documentation for working with Large La
|
||||
- **[swift-testing-playbook.md](./llms/swift-testing-playbook.md)** - Best practices and patterns for Swift Testing
|
||||
|
||||
These documents serve as knowledge references that can be included in CLAUDE.md files to provide AI assistants with up-to-date information about Swift frameworks and APIs.
|
||||
|
||||
## Cursor Rules
|
||||
|
||||
The `cursor-rules/` directory contains reusable Cursor AI rules and patterns collected from various projects:
|
||||
|
||||
### Development Guidelines
|
||||
|
||||
- **[mcp-development-guidelines.md](./cursor-rules/mcp-development-guidelines.md)** - Comprehensive security-first development guidelines for MCP servers
|
||||
- Path validation and security patterns
|
||||
- Error handling standards
|
||||
- Testing strategies
|
||||
- TypeScript best practices
|
||||
|
||||
### Meta-Rules
|
||||
|
||||
- **[cursor-rules-meta-guide.md](./cursor-rules/cursor-rules-meta-guide.md)** - Guidelines for creating and maintaining Cursor rules
|
||||
- Rule structure and formatting
|
||||
- File reference syntax
|
||||
- Best practices for rule documentation
|
||||
|
||||
### Automation Patterns
|
||||
|
||||
- **[safari-automation.md](./cursor-rules/safari-automation.md)** - Advanced Safari browser automation techniques
|
||||
- AppleScript patterns for window/tab management
|
||||
- JavaScript execution strategies
|
||||
- Shadow DOM interaction
|
||||
- Timing and synchronization patterns
|
||||
|
||||
- **[mcp-inspector-debugging.md](./cursor-rules/mcp-inspector-debugging.md)** - Debugging MCP servers with Inspector UI
|
||||
- Multi-tool orchestration (Playwright, iTerm, Claude Code)
|
||||
- Phase-based debugging approach
|
||||
- Troubleshooting strategies
|
||||
|
||||
These rules demonstrate proven patterns for project development, automation, testing, and debugging that can be adapted for your own projects.
|
||||
|
||||
## Claude Code Plan Mode
|
||||
|
||||
**New in Claude Code**: Plan mode allows you to review implementation plans before making changes. This feature is perfect for complex changes where you want to nail the approach before diving in.
|
||||
|
||||
Plan mode enables Claude to create comprehensive plans for code changes, breaking down tasks into clear steps with file locations and specific modifications. This helps ensure accuracy and allows you to review the approach before any code is written.
|
||||
|
||||
**Learn more**: [Announcement tweet by @_catwu](https://x.com/_catwu/status/1932857816131547453)
|
||||
|
||||
## Resources
|
||||
|
||||
### Blog Posts
|
||||
- [How I Use Claude Code](https://spiess.dev/blog/how-i-use-claude-code) - Practical tips and workflows for using Claude Code effectively
|
||||
- [Claude Code is My Computer](https://steipete.me/posts/2025/claude-code-is-my-computer) - Deep dive into Claude Code as a development environment
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
description: Guidelines for creating and maintaining Cursor rules to ensure consistency and effectiveness.
|
||||
globs: .cursor/rules/*.mdc
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
- **Required Rule Structure:**
|
||||
```markdown
|
||||
---
|
||||
description: Clear, one-line description of what the rule enforces
|
||||
globs: path/to/files/*.ext, other/path/**/*
|
||||
alwaysApply: boolean
|
||||
---
|
||||
|
||||
- **Main Points in Bold**
|
||||
- Sub-points with details
|
||||
- Examples and explanations
|
||||
```
|
||||
|
||||
- **File References:**
|
||||
- Use `[filename](mdc:path/to/file)` ([filename](mdc:filename)) to reference files
|
||||
- Example: [prisma.mdc](mdc:.cursor/rules/prisma.mdc) for rule references
|
||||
- Example: [schema.prisma](mdc:prisma/schema.prisma) for code references
|
||||
|
||||
- **Code Examples:**
|
||||
- Use language-specific code blocks
|
||||
```typescript
|
||||
// ✅ DO: Show good examples
|
||||
const goodExample = true;
|
||||
|
||||
// ❌ DON'T: Show anti-patterns
|
||||
const badExample = false;
|
||||
```
|
||||
|
||||
- **Rule Content Guidelines:**
|
||||
- Start with high-level overview
|
||||
- Include specific, actionable requirements
|
||||
- Show examples of correct implementation
|
||||
- Reference existing code when possible
|
||||
- Keep rules DRY by referencing other rules
|
||||
|
||||
- **Rule Maintenance:**
|
||||
- Update rules when new patterns emerge
|
||||
- Add examples from actual codebase
|
||||
- Remove outdated patterns
|
||||
- Cross-reference related rules
|
||||
|
||||
- **Best Practices:**
|
||||
- Use bullet points for clarity
|
||||
- Keep descriptions concise
|
||||
- Include both DO and DON'T examples
|
||||
- Reference actual code over theoretical examples
|
||||
- Use consistent formatting across rules
|
||||
@@ -0,0 +1,369 @@
|
||||
# Conduit MCP Development Guidelines
|
||||
|
||||
This document provides comprehensive guidelines for developing and maintaining the `conduit-mcp` project, an MCP (Model Context Protocol) server for rich file system operations, web content fetching, image processing, search, diff, and archives.
|
||||
|
||||
## Project Overview & Structure
|
||||
|
||||
### Purpose
|
||||
|
||||
`conduit-mcp` is an MCP server that provides secure, validated file system operations and content processing through a JSON-RPC interface. It serves as a bridge between AI agents and system resources with built-in security controls.
|
||||
|
||||
### Key Directories Structure
|
||||
|
||||
```
|
||||
conduit-mcp/
|
||||
├── src/ # Main source code
|
||||
│ ├── core/ # Core functionality
|
||||
│ ├── operations/ # Business logic operations
|
||||
│ ├── tools/ # MCP tool handlers
|
||||
│ ├── types/ # TypeScript type definitions
|
||||
│ ├── utils/ # Utility functions
|
||||
│ └── server.ts # Main server entry point
|
||||
├── tests/ # Unit tests (mirrors src/ structure)
|
||||
├── e2e/ # End-to-end tests
|
||||
│ ├── scenarios/ # JSON test scenario definitions
|
||||
│ ├── utils/ # E2E testing utilities
|
||||
│ └── *.e2e.test.ts # E2E test files
|
||||
└── docs/ # Documentation
|
||||
└── spec.md # Primary specification
|
||||
```
|
||||
|
||||
### Server Entry Point
|
||||
|
||||
- **Source**: `src/server.ts`
|
||||
- **Compiled**: `dist/src/server.js` (after `npm run build`)
|
||||
- **Startup**: `./start.sh` script
|
||||
|
||||
## Security First - Core Principle
|
||||
|
||||
### Mandatory Path Validation
|
||||
|
||||
**CRITICAL**: ALL file/path operations MUST use `validateAndResolvePath` from `src/core/securityHandler.ts:37`. This is the single most important security requirement.
|
||||
|
||||
```typescript
|
||||
import { validateAndResolvePath } from '@/core/securityHandler';
|
||||
|
||||
// Required usage pattern:
|
||||
const validatedPath = await validateAndResolvePath(userInputPath, {
|
||||
isExistenceRequired: true, // true if file must exist
|
||||
checkAllowed: true, // true (default) to check against allowed paths
|
||||
forCreation: false, // true if creating new file/directory
|
||||
});
|
||||
```
|
||||
|
||||
#### Path Validation Options
|
||||
|
||||
- `isExistenceRequired`: Throws `ERR_FS_NOT_FOUND` if path doesn't exist
|
||||
- `checkAllowed`: Validates path is within `CONDUIT_ALLOWED_PATHS` (default: true)
|
||||
- `forCreation`: Validates parent directory for file/directory creation operations
|
||||
|
||||
### Configuration Security
|
||||
|
||||
Environment variables control security boundaries:
|
||||
|
||||
- `CONDUIT_ALLOWED_PATHS`: Colon-separated list of allowed path prefixes (default: `~:/tmp`)
|
||||
- `CONDUIT_TEMP_DIR`: Temporary directory for operations
|
||||
- `CONDUIT_MAX_PAYLOAD_SIZE`: Maximum request payload size
|
||||
|
||||
## Error Handling Standards
|
||||
|
||||
### Consistent JSON Response Structure
|
||||
|
||||
ALL tool responses MUST follow this structure:
|
||||
|
||||
```typescript
|
||||
{
|
||||
tool_name: string,
|
||||
status: 'success' | 'error',
|
||||
results?: any, // Present on success
|
||||
error_code?: string, // Present on error
|
||||
error_message?: string // Present on error
|
||||
}
|
||||
```
|
||||
|
||||
### Server Exit Code Convention
|
||||
|
||||
**CRITICAL**: Server always exits with code 0 if it can produce a JSON response. Error conditions are signaled _within_ the JSON response, not through process exit codes.
|
||||
|
||||
This means:
|
||||
|
||||
- E2E tests check `response.status` for logical success/error
|
||||
- Process `exitCode` is used only for startup/parsing failures
|
||||
- Tool errors are communicated via JSON `error_code` and `error_message`
|
||||
|
||||
## Development Workflow & Conventions
|
||||
|
||||
### TypeScript Guidelines
|
||||
|
||||
- Use strict TypeScript configurations (`strict: true`)
|
||||
- Prefer specific types over `any` (ESLint warns on `@typescript-eslint/no-explicit-any`)
|
||||
- Prefix intentionally unused variables with `_` (e.g., `_unusedParam`)
|
||||
- Import types from `src/types/` for consistency
|
||||
|
||||
### Module Resolution
|
||||
|
||||
- Use path alias `@/*` for `src/*` imports
|
||||
- Module system: CommonJS output (`"module": "commonjs"` in tsconfig)
|
||||
- Remove `"type": "module"` from package.json to avoid conflicts
|
||||
|
||||
### Code Style
|
||||
|
||||
- Follow existing patterns in the codebase
|
||||
- Use consistent naming conventions
|
||||
- Leverage existing utilities (e.g., `fs-extra` for file operations)
|
||||
- Always check imports and dependencies before using libraries
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- **Location**: `tests/` directory (mirrors `src/` structure)
|
||||
- **Command**: `npm test`
|
||||
- **Framework**: Vitest with TypeScript support
|
||||
- **Coverage**: `npm run coverage`
|
||||
|
||||
#### Common Mock Patterns
|
||||
|
||||
```typescript
|
||||
import { vi } from 'vitest';
|
||||
|
||||
// Mock Node.js modules
|
||||
vi.mock('fs', () => ({
|
||||
/* mock implementation */
|
||||
}));
|
||||
vi.mock('fs-extra', () => ({
|
||||
/* mock implementation */
|
||||
}));
|
||||
|
||||
// Mock internal modules
|
||||
vi.mock('@/core/securityHandler', () => ({
|
||||
validateAndResolvePath: vi.fn(),
|
||||
}));
|
||||
|
||||
// Reset mocks in test lifecycle
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks(); // or vi.clearAllMocks()
|
||||
});
|
||||
```
|
||||
|
||||
#### Frequently Mocked Modules
|
||||
|
||||
- `fs`, `fs-extra`, `os` (Node.js modules)
|
||||
- `@/core/securityHandler`, `@/core/configLoader`, `@/core/fileSystemOps` (internal)
|
||||
|
||||
### End-to-End (E2E) Tests
|
||||
|
||||
- **Location**: `e2e/` directory
|
||||
- **Command**: `npm run test:e2e`
|
||||
- **Framework**: Vitest with custom runner (`e2e/utils/e2eTestRunner.ts:17`)
|
||||
|
||||
#### Scenario-Driven Testing
|
||||
|
||||
E2E tests use JSON scenario files in `e2e/scenarios/`:
|
||||
|
||||
```json
|
||||
{
|
||||
"scenarios": [
|
||||
{
|
||||
"name": "scenario_identifier",
|
||||
"description": "Human-readable description",
|
||||
"request_payload": {
|
||||
"tool_name": "find",
|
||||
"params": {
|
||||
/* tool parameters */
|
||||
}
|
||||
},
|
||||
"expected_exit_code": 0,
|
||||
"expected_stdout": {
|
||||
"tool_name": "find",
|
||||
"status": "success",
|
||||
"results": {
|
||||
/* expected results */
|
||||
}
|
||||
},
|
||||
"filesystem_effects": {
|
||||
"created_files": [],
|
||||
"modified_files": [],
|
||||
"deleted_files": []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### E2E Test Structure
|
||||
|
||||
```typescript
|
||||
import { runConduitMCPScript, createMCPRequest } from '@/e2e/utils/e2eTestRunner';
|
||||
|
||||
test('scenario description', async () => {
|
||||
const request = createMCPRequest('tools/call', {
|
||||
name: 'find',
|
||||
arguments: {
|
||||
/* params */
|
||||
},
|
||||
});
|
||||
|
||||
const result = await runConduitMCPScript(request, envVars);
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.response.status).toBe('success');
|
||||
// Validate specific response fields
|
||||
});
|
||||
```
|
||||
|
||||
#### Test Environment Management
|
||||
|
||||
- Use `e2e/utils/tempFs.ts` for test workspace management
|
||||
- Functions: `setupTestEnvironment()`, `cleanupTestEnvironment()`, `testWorkspaceDir`
|
||||
- Temporary files created in `e2e/test-workspace/` during tests
|
||||
|
||||
## Configuration Management
|
||||
|
||||
### Core Configuration (`src/core/configLoader.ts:46`)
|
||||
|
||||
Key environment variables and defaults:
|
||||
|
||||
```typescript
|
||||
// Security
|
||||
CONDUIT_ALLOWED_PATHS = '~:/tmp'; // Allowed filesystem access
|
||||
CONDUIT_TEMP_DIR; // Temporary directory
|
||||
|
||||
// Limits
|
||||
CONDUIT_MAX_PAYLOAD_SIZE_BYTES = 10485760; // 10MB request limit
|
||||
CONDUIT_MAX_FILE_READ_BYTES = 52428800; // 50MB file read limit
|
||||
CONDUIT_HTTP_TIMEOUT_MS = 30000; // 30s HTTP timeout
|
||||
|
||||
// Image Processing
|
||||
CONDUIT_IMAGE_COMPRESSION_THRESHOLD_BYTES = 1048576; // 1MB
|
||||
CONDUIT_IMAGE_COMPRESSION_QUALITY = 75; // 1-100
|
||||
|
||||
// Logging
|
||||
LOG_LEVEL = 'INFO'; // TRACE,DEBUG,INFO,WARN,ERROR,FATAL
|
||||
```
|
||||
|
||||
## Linting and Formatting
|
||||
|
||||
### Commands
|
||||
|
||||
- `npm run lint`: Run ESLint checks
|
||||
- `npm run format`: Format code with Prettier
|
||||
|
||||
### ESLint Configuration (`eslint.config.js`)
|
||||
|
||||
- **Format**: Flat config (ESLint 9+)
|
||||
- **Parser**: `@typescript-eslint/parser`
|
||||
- **Key Rules**:
|
||||
- `@typescript-eslint/no-unused-vars`: Error (prefix with `_` for intentional)
|
||||
- `@typescript-eslint/no-explicit-any`: Warning (prefer specific types)
|
||||
- `import/no-unresolved`: Error (ensure imports resolve correctly)
|
||||
|
||||
### Type-Aware Linting
|
||||
|
||||
- `parserOptions.project`: Points to `./tsconfig.json`
|
||||
- Covers `src/**/*.ts`, `tests/**/*.ts`, `e2e/**/*.ts`
|
||||
|
||||
## Build Process
|
||||
|
||||
### Commands
|
||||
|
||||
- `npm run build`: Compile TypeScript to `dist/`
|
||||
- `npm run dev`: Development mode with file watching
|
||||
|
||||
### Output Structure
|
||||
|
||||
- Source: `src/` → Compiled: `dist/src/`
|
||||
- Main entry: `dist/src/server.js`
|
||||
- Declarations: Generated with source maps
|
||||
|
||||
## Documentation Standards
|
||||
|
||||
### Primary Documents
|
||||
|
||||
- `docs/spec.md`: Complete tool specification (MUST stay aligned with implementation)
|
||||
- `DEVELOPMENT.md`: Developer setup and testing guide
|
||||
- `README.md`: Project overview and quick start
|
||||
|
||||
### Code Documentation
|
||||
|
||||
- Use TSDoc comments for public APIs
|
||||
- Document complex algorithms and security considerations
|
||||
- Include examples for non-obvious usage patterns
|
||||
|
||||
## Key Learnings & Pitfalls
|
||||
|
||||
### TypeScript Module Resolution
|
||||
|
||||
**Issue**: Conflicts between `package.json` `"type": "module"` and `tsconfig.json` `"module": "commonjs"`
|
||||
**Resolution**: Remove `"type": "module"` from package.json; let TypeScript handle module aspects
|
||||
|
||||
### ESLint Flat Configuration
|
||||
|
||||
**Issue**: Migration from legacy `.eslintrc` to flat config
|
||||
**Resolution**: Use `eslint.config.js` with proper `parserOptions.project` setup for type-aware linting
|
||||
|
||||
### Server Exit Code Convention
|
||||
|
||||
**Issue**: E2E tests failing due to exit code expectations
|
||||
**Resolution**: Server exits 0 for JSON responses; check `response.status` for tool success/failure
|
||||
|
||||
### File System Utilities
|
||||
|
||||
**Preference**: Use `fs-extra` over standard `fs` for:
|
||||
|
||||
- `emptyDirSync()`, `ensureDirSync()`, `removeSync()` in tests
|
||||
- More reliable setup/teardown operations
|
||||
- Better error handling
|
||||
|
||||
### Mock Management in Tests
|
||||
|
||||
**Best Practice**: Always reset mocks in `beforeEach`/`afterEach`:
|
||||
|
||||
```typescript
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks(); // Clears call history and reset implementations
|
||||
});
|
||||
```
|
||||
|
||||
## Agent Usage Notes (Meta)
|
||||
|
||||
When using Claude Code MCP for file operations:
|
||||
|
||||
1. **Always specify `workFolder`**: Set to project root (`/Users/steipete/Projects/conduit-mcp`)
|
||||
2. **Refer to these guidelines**: Before proposing changes or writing new code
|
||||
3. **Follow security patterns**: Use `validateAndResolvePath` for all file operations
|
||||
4. **Maintain test coverage**: Write both unit and E2E tests for new features
|
||||
5. **Check lint/typecheck**: Run `npm run lint` and build before committing
|
||||
|
||||
### Development Commands Quick Reference
|
||||
|
||||
```bash
|
||||
# Development
|
||||
npm run dev # Watch mode development
|
||||
npm run build # Compile TypeScript
|
||||
npm run start # Start compiled server
|
||||
|
||||
# Testing
|
||||
npm test # Unit tests
|
||||
npm run test:e2e # E2E tests
|
||||
npm run coverage # Coverage report
|
||||
|
||||
# Code Quality
|
||||
npm run lint # ESLint check
|
||||
npm run format # Prettier format
|
||||
```
|
||||
|
||||
## Security Checklist
|
||||
|
||||
Before adding new file operations:
|
||||
|
||||
- [ ] Uses `validateAndResolvePath` with appropriate options
|
||||
- [ ] Handles all error cases with proper `ConduitError` types
|
||||
- [ ] Respects configuration limits (file size, timeout, etc.)
|
||||
- [ ] Returns consistent JSON response structure
|
||||
- [ ] Includes comprehensive test coverage
|
||||
- [ ] Documents security implications
|
||||
|
||||
---
|
||||
|
||||
_This document should be updated as the project evolves to reflect new patterns, conventions, and lessons learned._
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
description:
|
||||
globs: e2e,tests
|
||||
alwaysApply: false
|
||||
---
|
||||
Rule Name: mcp-inspector
|
||||
Description: Debugging and verifying the Notarium MCP server (`notarium-mcp`) via the MCP Inspector UI. This rule uses Playwright for UI automation, iTerm for server management, and Claude Code for log inspection.
|
||||
|
||||
This project IS notarium-mcp, you find the source code in the src folder.
|
||||
|
||||
Simplenote test account (credentials currently hardcoded in `src/config.ts` for this testing phase):
|
||||
steipete@protonmail.com
|
||||
MAbVuzegRZ2U9dz7wJHi
|
||||
|
||||
To read the log, use `tail -n 100 /path/to/logfile.log`
|
||||
|
||||
DO NOT RUN BLOCKING TERMINAL COMMANDS VIA YOUR TERMINAL TOOL. INSTEAD USE Terminal app via AppleScript.
|
||||
Blocking operations include:
|
||||
- `node dist/index.js`
|
||||
- `npm run dev`
|
||||
- `npm start`
|
||||
|
||||
This is ok:
|
||||
Grab the last X lines: `tail -n 100 server.log`
|
||||
|
||||
Run `npm run build` to verify build.
|
||||
|
||||
Check iTerm log when the MCP Inspector fails, as it might show errors from our service.
|
||||
|
||||
**Required Tools:**
|
||||
- `run_terminal_cmd` (for global pkill, and as fallback for Inspector start)
|
||||
- `mcp_iterm-mcp_send_control_character`
|
||||
- `mcp_iterm-mcp_write_to_terminal`
|
||||
- `mcp_iterm-mcp_read_terminal_output`
|
||||
- `mcp_playwright_browser_navigate`
|
||||
- `mcp_playwright_browser_type`
|
||||
- `mcp_playwright_browser_click`
|
||||
- `mcp_playwright_browser_snapshot`
|
||||
- `mcp_playwright_browser_console_messages`
|
||||
- `mcp_playwright_browser_wait_for`
|
||||
- `mcp_claude-code_claude_code`
|
||||
|
||||
**User Workspace Path Placeholder:**
|
||||
- `[WORKSPACE_PATH]` in this rule **MUST** be replaced by the AI assistant with the absolute path to the user's current project workspace (e.g., from `<user_info>`).
|
||||
|
||||
---
|
||||
|
||||
**Phase 1: Start MCP Inspector Server (via iTerm)**
|
||||
1. **Clear iTerm & Kill Existing Inspector Processes:**
|
||||
* Action: `mcp_iterm-mcp_send_control_character`, `letter`: `"C"`. (Stop any current iTerm command).
|
||||
2. **Start New Inspector Process in iTerm:**
|
||||
* Action: `mcp_iterm-mcp_write_to_terminal`, `command`: `SIMPLENOTE_USERNAME=steipete@protonmail.com SIMPLENOTE_PASSWORD=MAbVuzegRZ2U9dz7wJHi npx @modelcontextprotocol/inspector ./start.sh`.
|
||||
* Action: `mcp_iterm-mcp_read_terminal_output`, `linesOfOutput`: `7` (or enough to see startup messages clearly).
|
||||
* Verify: Look for "MCP Inspector is up and running at http://127.0.0.1:6274". Note the proxy port.
|
||||
3. **Wait for Inspector UI:** `mcp_playwright_browser_wait_for`, `time`: `3` (seconds).
|
||||
|
||||
**Phase 2: Connect to Notarium MCP Server via Playwright**
|
||||
1. **Navigate/Refresh Inspector Page:**
|
||||
* Action: `mcp_playwright_browser_navigate`, `url`: `http://127.0.0.1:6274`.
|
||||
* Expected: Ensures a fresh state of the Inspector UI.
|
||||
* Snapshot: Take a snapshot.
|
||||
2. **Fill Connection Form:**
|
||||
* **Set Command:** (Obtain `ref` from snapshot for "Command textbox")
|
||||
* Action: `mcp_playwright_browser_type`, `text`: `[WORKSPACE_PATH]/start.sh`.
|
||||
* **Set Arguments:** (Obtain `ref` for "Arguments textbox")
|
||||
* Action: `mcp_playwright_browser_type`, `text`: `""`.
|
||||
* **Environment Variables: (prefilled if you add it in the inspector call)**
|
||||
* Action: Click "Environment Variables" button (obtain `ref`) to view current. Snapshot.
|
||||
`src/config.ts`. No UI entry is strictly needed unless overriding these for a specific test run.
|
||||
* (If needing to override, e.g., `LOG_FILE_PATH=/tmp/custom-notarium.log`):
|
||||
* Click "Add Environment Variable" button (obtain `ref`).
|
||||
* Type key into the new key textbox (obtain `ref`).
|
||||
* Type value into the new value textbox (obtain `ref`). Repeat as needed.
|
||||
3. **Click "Connect":** (Obtain `ref` from snapshot for "Connect button")
|
||||
* Action: `mcp_playwright_browser_click`.
|
||||
* Snapshot: Take a snapshot.
|
||||
4. **Verify Connection:**
|
||||
* Examine snapshot: Check for connected status (e.g., service `mcp_notarium` appears).
|
||||
* If connection fails, examine "Error output from MCP server" panel in Inspector UI. This shows `stderr` from `start.sh`.
|
||||
* Crucially, also check the `start.sh` debug log: `mcp_claude-code_claude_code`, prompt: `"Read /tmp/notarium_start_debug.log"`.
|
||||
* If `start.sh` log shows it tried to run `notarium-mcp` but Inspector still failed to connect, then check `notarium-mcp`'s own log: `mcp_claude-code_claude_code`, prompt: `"Read [WORKSPACE_PATH]/notarium-server-debug.log"`.
|
||||
|
||||
**Phase 3: Interact with `mcp_notarium.list` Tool** (Assuming successful connection)
|
||||
1. **List/Select Service:** Click `mcp_notarium` service name. Snapshot.
|
||||
2. **Select `mcp_notarium.list` Tool:** Click tool in list. Snapshot.
|
||||
3. **Execute Tool (Default Params):** Parameters field should be empty or `{}`. Click "Run Tool". Snapshot.
|
||||
|
||||
**Phase 4: Verify Tool Execution and Get Output**
|
||||
1. **Check Results in UI:** Examine snapshot for JSON result with `items` array.
|
||||
2. **Report Output:** Clearly state the content of the `items` array.
|
||||
3. **Check Notarium Server Logs:**
|
||||
* Action: `mcp_claude-code_claude_code`, `prompt`: `"Read last 50 lines of [WORKSPACE_PATH]/notarium-server-debug.log"` (or the configured path).
|
||||
* Look for: Logs related to authentication, sync activity, and `mcp_notarium.list` tool execution.
|
||||
|
||||
**Troubleshooting Notes (Consolidated):**
|
||||
- **Workspace Path:** Ensure `[WORKSPACE_PATH]` is correctly replaced with the absolute path.
|
||||
- **`start.sh`:** Must be executable. It logs its own debug trace to `/tmp/notarium_start_debug.log`. It **must not** output to `stdout` or `stderr` itself before the actual Notarium server starts sending JSON-RPC, as this will break Inspector parsing.
|
||||
- **`notarium-mcp` Logs:** The primary logs for the Notarium server itself are in the file specified by `LOG_FILE_PATH` (default `./notarium-server-debug.log` in the project root, configured in `src/config.ts`). Use `mcp_claude-code_claude_code` with `tail -n 100 <path_to_log>` or `Read <path_to_log>`.
|
||||
- **Inspector UI Errors:** The "Error output from MCP server" panel in the Inspector shows `stderr` from the launched `start.sh` process. This is useful if `start.sh` itself fails loudly.
|
||||
- **Playwright Refs:** Always use refs from the *latest* snapshot for interactions.
|
||||
@@ -0,0 +1,216 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
### Meta Note
|
||||
|
||||
This file, `safari.mdc`, serves as a repository for detailed working notes, observations, and learnings acquired during the process of automating Safari interactions, particularly for the MCP Inspector UI. It's intended to capture the nuances of trial-and-error, debugging steps, and insights into what worked, what didn't, and why.
|
||||
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
### Key Learnings and Observations from Safari Automation (MCP Inspector)
|
||||
|
||||
#### 1. Managing Safari Windows and Tabs for the Inspector
|
||||
|
||||
* **Objective:** Reliably direct Safari to the MCP Inspector URL (`http://127.0.0.1:6274`) in a predictable way, preferably using a single, consistent browser window and tab to avoid disrupting the user's workspace or losing context.
|
||||
* **Initial Challenges & Evolution:
|
||||
* Simply using `make new document with properties {URL:"..."}` could lead to multiple windows/tabs if not managed.
|
||||
* Attempts to close all existing Inspector tabs first (`repeat with w in windows... close t...`) were functional but could be overly aggressive if the user had other work in Safari.
|
||||
* Identifying and reusing an *existing specific tab* for the Inspector requires careful targeting (e.g., `first tab whose URL starts with "..."`). If this tab was from a previous, unconfigured session, just switching to it wasn't enough; it needed to be reloaded/reset.
|
||||
* **Refined & Recommended Approach (as implemented in `mcp-inspector.mdc`):
|
||||
```applescript
|
||||
tell application "Safari"
|
||||
activate
|
||||
delay 0.2 -- Allow Safari to become the frontmost application
|
||||
if (count of windows) is 0 then
|
||||
-- No Safari windows are open, so create a new one.
|
||||
make new document with properties {URL:"http://127.0.0.1:6274"}
|
||||
else
|
||||
-- Safari has windows open; use the frontmost one.
|
||||
tell front window
|
||||
set inspectorTab to missing value
|
||||
try
|
||||
-- Check if a tab for the Inspector is already open in this window.
|
||||
set inspectorTab to (first tab whose URL starts with "http://127.0.0.1:6274")
|
||||
end try
|
||||
|
||||
if inspectorTab is not missing value then
|
||||
-- An Inspector tab exists: set its URL again (to refresh/reset) and make it active.
|
||||
set URL of inspectorTab to "http://127.0.0.1:6274"
|
||||
set current tab to inspectorTab
|
||||
else
|
||||
-- No specific Inspector tab found: set the URL of the *current active tab*.
|
||||
set URL of current tab to "http://127.0.0.1:6274"
|
||||
end if
|
||||
end tell
|
||||
end if
|
||||
delay 1 -- Pause to allow the page to begin loading.
|
||||
end tell
|
||||
```
|
||||
This logic aims to use the existing front window and either reuse/refresh an Inspector tab or repurpose the current active tab, falling back to creating a new window only if Safari isn't open.
|
||||
|
||||
#### 2. Clicking Elements Programmatically (The "Connect" Button Saga)
|
||||
|
||||
* **The Core Challenge:** Programmatically clicking the "Connect" button in the MCP Inspector UI to initiate the server connection.
|
||||
* **Strategies Explored & Lessons:
|
||||
* **CSS Selectors (`querySelector`):**
|
||||
* Simple selectors like `[data-testid='env-vars-button']` worked for some buttons but required escaping single quotes in AppleScript: `do JavaScript "document.querySelector('[data-testid=\\\'env-vars-button\\']').click();"`.
|
||||
* A complex `querySelector` for the "Connect" button (e.g., `'button[data-testid*=connect-button], button:not([disabled])... > span:contains(Connect)...'.click()`) ran without JS error but didn't reliably establish the connection, suggesting it might not have found the exact interactable element or the click wasn't registering correctly.
|
||||
* **XPath (`document.evaluate`):**
|
||||
* **Highly Specific XPaths:** An initial XPath based on the rule (`//button[contains(., 'Connect') and .//svg[.//polygon[@points='6 3 20 12 6 21 6 3']]]`) was very difficult to embed correctly in AppleScript due to nested single quotes requiring complex escaping (`\'`). This often led to AppleScript parsing errors (`-2741`).
|
||||
* **`character id 39` for AppleScript String Construction:** To combat escaping issues, building the JavaScript string in AppleScript using `set sQuote to character id 39` for internal single quotes was effective for getting the AppleScript parser to accept the command. Example:
|
||||
```applescript
|
||||
set sQuote to character id 39
|
||||
set jsConnectText to "Connect"
|
||||
set specificXPath to "//button[contains(., " & sQuote & jsConnectText & sQuote & ") and .//svg[.//polygon[@points=" & sQuote & "6 3 20 12 6 21 6 3" & sQuote & "]]]"
|
||||
set jsCommand to "document.evaluate(" & sQuote & specificXPath & sQuote & ", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.click();"
|
||||
```
|
||||
While this made the AppleScript runnable, this very specific XPath still didn't reliably trigger the connection.
|
||||
* **Successful XPath:** The breakthrough came with a slightly less specific but more robust XPath: `//button[.//text()='Connect']`. This finds a button that *contains* a text node exactly matching "Connect".
|
||||
* JavaScript: `document.evaluate("//button[.//text()='Connect']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.click();`
|
||||
* AppleScript embedding (note `\"` for JS string quotes):
|
||||
```applescript
|
||||
set jsCommand to "document.evaluate(\"//button[.//text()='Connect']\", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.click();"
|
||||
do JavaScript jsCommand in front document
|
||||
```
|
||||
This method proved successful in clicking the button and establishing the connection.
|
||||
* **`dispatchEvent(new MouseEvent('click', ...))`:** This was tried as an alternative to `.click()` but did not yield a different outcome for the "Connect" button in this specific scenario.
|
||||
|
||||
#### 3. JavaScript Construction and Execution in AppleScript
|
||||
|
||||
* **`do JavaScript "..."`:** This is the fundamental command.
|
||||
* **String Literals and Escaping:**
|
||||
* If the AppleScript command itself is enclosed in double quotes (`"..."`), then any literal double quotes *within the JavaScript code* must be escaped as `\\"`.
|
||||
* Single quotes (`'`) within the JavaScript code usually do not need escaping in this context.
|
||||
* Example: `do JavaScript "var el = document.getElementById(\"myId\"); el.value = 'Hello\';"`
|
||||
* **Long/Multiline JavaScript:**
|
||||
* Concatenating multiple AppleScript string literals using `&` (and optionally `¬` for line continuation) can build up a long JavaScript command. However, this can be fragile if not every part is perfectly quoted and escaped. Often, AppleScript parsing errors (`-2741`) occur before the JS is even attempted.
|
||||
* For complex JS, it's often more robust to ensure the entire JavaScript code is a single, well-formed string literal from AppleScript's perspective. If the JS itself is very complex, pre-constructing parts of it in AppleScript variables (especially strings that need careful quoting, like XPaths) can help.
|
||||
* **Returning Values:** The `do JavaScript` command returns the result of the last JavaScript statement executed. This can be invaluable for debugging, e.g., `return 'Found element';` or `return element !== null;`.
|
||||
|
||||
#### 4. Asynchronicity and Delays
|
||||
|
||||
* **Essential `delay` commands (Strategic vs. Tactical):**
|
||||
* **Strategic Delay (Crucial):** A critical lesson was the necessity of a significant delay (e.g., ~5 seconds) *after* an external process like the MCP Inspector is launched (e.g., via `npx` in iTerm) and *before* Safari automation attempts to interact with its web UI. This allows the external process and its web server to fully initialize. Without this, Safari automation might target a page that isn't ready or fully functional, leading to failures.
|
||||
* **Tactical Delays (Within Safari UI Automation - Often Avoidable):** Initially, small `delay` commands were used within Safari AppleScripts after actions like clicks or page loads (e.g., `delay 0.25`, `delay 1`). While these can sometimes help ensure the DOM is updated, the latest successful runs showed that if the backend/server (Inspector) is fully ready (due to the strategic delay), rapid Safari UI interactions (form filling, sequential clicks) can often be performed reliably *without* these internal micro-delays. Removing them can speed up the automation if the underlying application is responsive enough.
|
||||
* **Context is Key:** The need for tactical delays depends on how quickly the web application updates its DOM and responds to JavaScript events. For the MCP Inspector, once it's running, its UI seems to respond quickly enough to handle a sequence of JavaScript commands without interspersed AppleScript delays, provided the commands themselves are valid and target the correct elements.
|
||||
|
||||
* **Checking for Results:** When verifying an action (e.g., checking if `document.body.innerText.includes('Connected')`), it's vital that this check happens *after* the action has had a chance to complete and the UI to reflect the change. If running without tactical delays, this check should still be performed after the relevant JavaScript action that's supposed to cause the change.
|
||||
|
||||
#### 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.
|
||||
* **Server Logs in the Inspector UI:** It was confirmed that after the `macos-automator-mcp` server connects via the MCP Inspector, its startup and operational logs (e.g., `[macos_automator_server] [INFO] Starting...`) are displayed directly within the MCP Inspector's web interface in Safari. This is the primary place to check for these server-specific logs, rather than the iTerm console running the `npx @modelcontextprotocol/inspector` command (which shows the Inspector's own proxy/connection logs). The Safari UI shows "Connected" status, and the server logs within the UI provide detailed confirmation of the server's state.
|
||||
|
||||
#### 6. Automating iTerm via AppleScript and Advanced Timing Considerations
|
||||
|
||||
* **Full iTerm Automation via AppleScript:** Due to persistent issues with iTerm-specific MCP tools (e.g., `mcp_iterm_send_control_character`, `mcp_iterm_write_to_terminal` consistently failing with "Tool not found" errors), a robust AppleScript workaround was developed and successfully implemented to manage the iTerm portion of the MCP Inspector setup. This script handles:
|
||||
* Activating iTerm.
|
||||
* Ensuring a window is available.
|
||||
* Sending a Control-C command to the current session using `System Events` (for reliability, targeting the iTerm process) to terminate any running commands.
|
||||
* Writing the `npx @modelcontextprotocol/inspector` command to the iTerm session to start the inspector.
|
||||
* The successful AppleScript structure is as follows (and now part of `mcp-inspector.mdc`):
|
||||
```applescript
|
||||
tell application "iTerm"
|
||||
activate
|
||||
if (count of windows) is 0 then
|
||||
create window with default profile
|
||||
delay 0.5 # Brief delay for window creation
|
||||
end if
|
||||
end tell
|
||||
delay 0.2 # Ensure iTerm is frontmost
|
||||
|
||||
tell application "System Events"
|
||||
# Note: 'iTerm' process name might need to be 'iTerm2' for iTerm3+.
|
||||
tell process "iTerm"
|
||||
keystroke "c" using control down
|
||||
end tell
|
||||
end tell
|
||||
delay 0.2 # Pause after Ctrl-C
|
||||
|
||||
tell application "iTerm"
|
||||
tell current window
|
||||
tell current session
|
||||
write text "npx @modelcontextprotocol/inspector"
|
||||
end tell
|
||||
end tell
|
||||
end tell
|
||||
```
|
||||
|
||||
* **iTerm Process Name in System Events:** When using `System Events` to control iTerm (e.g., for `keystroke`), the `tell process "iTerm"` command might need to be `tell process "iTerm2"` if using iTerm version 3 or later, as the application's registered process name can vary.
|
||||
|
||||
* **Reinforcing the Strategic Delay:** The success of running Safari UI automation steps *without* internal (tactical) delays is highly dependent on the *strategic* delay implemented *after* initiating the MCP Inspector in iTerm and *before* beginning any Safari interaction. A delay of approximately 5 seconds was found to be effective, allowing `npx` and the Inspector server to fully initialize. Attempting Safari automation too soon, especially without tactical delays, will likely result in failures as the web UI won't be ready or responsive.
|
||||
|
||||
#### 7. Interacting with Shadow DOM (Advanced)
|
||||
|
||||
* **Identifying Shadow DOM:** Some web UIs, including potentially parts of the MCP Inspector (especially complex, self-contained components like the tool details and results panels), may use Shadow DOM to encapsulate their structure and styles. Standard `document.querySelector` or `document.evaluate` calls from the main document context will *not* pierce these shadow boundaries.
|
||||
* **Symptoms of Shadow DOM:** If `document.body.innerText` seems to miss details of an active UI component, or if standard selectors fail for visible elements that are clearly part of a specific component, Shadow DOM may be in use.
|
||||
* **Accessing Elements within Shadow DOM (Conceptual JavaScript Approach):**
|
||||
To interact with elements inside a shadow root, you first need a reference to the host element, then access its `shadowRoot` property, and then query within that root.
|
||||
```javascript
|
||||
// 1. Find the host element (custom element tag name, e.g., 'tool-details-panel')
|
||||
const hostElement = document.querySelector('your-shadow-host-tag-name');
|
||||
|
||||
if (hostElement && hostElement.shadowRoot) {
|
||||
const shadowRoot = hostElement.shadowRoot;
|
||||
|
||||
// 2. Query within the shadowRoot for target elements
|
||||
const targetElementInShadow = shadowRoot.querySelector('#some-element-inside-shadow');
|
||||
if (targetElementInShadow) {
|
||||
// targetElementInShadow.click();
|
||||
// return targetElementInShadow.textContent;
|
||||
} else {
|
||||
// return 'Element not found within shadowRoot';
|
||||
}
|
||||
} else {
|
||||
// return 'Shadow host not found or no shadowRoot attached';
|
||||
}
|
||||
```
|
||||
* **Recursive Deep Query Helper (Conceptual):** For nested shadow DOMs or when the exact host is unknown, a recursive or iterative deep query function can be useful. This function would traverse the DOM, checking each element for a `shadowRoot` and searching within it.
|
||||
```javascript
|
||||
function $deep(selector, rootNode = document) {
|
||||
const stack = [rootNode];
|
||||
while (stack.length) {
|
||||
const currentNode = stack.shift();
|
||||
if (currentNode.nodeType === Node.ELEMENT_NODE && currentNode.matches(selector)) {
|
||||
return currentNode;
|
||||
}
|
||||
if (currentNode.shadowRoot) {
|
||||
stack.push(currentNode.shadowRoot);
|
||||
}
|
||||
// Check children only if it's an Element or DocumentFragment (like a shadowRoot)
|
||||
if (currentNode.nodeType === Node.ELEMENT_NODE || currentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
||||
if (currentNode.children) { // Ensure children property exists
|
||||
stack.push(...currentNode.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// Usage: const someButton = $deep('button.some-class-in-shadow');
|
||||
```
|
||||
* **Challenges with AppleScript `do JavaScript`:**
|
||||
* **Return Value Limitations:** Complex objects (like DOM elements) or very large strings (like extensive `outerHTML`) returned from `do JavaScript` can sometimes result in `missing value` or empty strings in AppleScript, making debugging difficult.
|
||||
* **Debugging:** Direct console logging from `do JavaScript` is not visible to the AppleScript environment, complicating troubleshooting of JavaScript execution within Safari.
|
||||
* **Reliability:** For highly dynamic UIs with extensive Shadow DOM, the AppleScript `do JavaScript` bridge may not always be reliable enough for complex, multi-step interactions, especially when precise timing or access to nuanced DOM states is required. Direct API/tool calls, if available, are often more robust for verification in such cases.
|
||||
* **Discovering Shadow Host Tag Names:** If the specific tag name of a shadow host is unknown, one might attempt to list all elements that have a `shadowRoot`:
|
||||
```javascript
|
||||
// JavaScript to be executed via AppleScript to list shadow host tag names
|
||||
// (Note: Return value handling by AppleScript needs to be robust, e.g., JSON stringify)
|
||||
// let hosts = [...document.querySelectorAll('*')]\
|
||||
// .filter(el => el.shadowRoot)\
|
||||
// .map(el => el.tagName);\
|
||||
// return JSON.stringify(hosts);\
|
||||
```
|
||||
However, successful execution and return of this data via AppleScript `do JavaScript` can be unreliable, as experienced in attempts to automate the MCP Inspector.
|
||||
|
||||
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.
|
||||
|
||||
---
|
||||
### Meta-Level Collaboration & Rule Evolution Notes
|
||||
|
||||
* **Rule Refinement for Readability (User Feedback):** Based on user feedback, the main operational rule file (`mcp-inspector.mdc`) was refactored to move lengthy scripts (like the Safari tab setup AppleScript) into an Appendix section (e.g., `[Setup Safari Tab for Inspector]`). This keeps the main flow of the rule concise and readable for both humans and models, while still providing the full implementation details in a structured way. The `safari.mdc` file is designated for the more verbose, evolutionary notes and debugging narratives.
|
||||
* **Tool Usage Preferences (User Feedback):** User indicated a preference for using the `edit_file` tool for modifying rule files (like `.mdc` files) rather than `claude_code`. This allows the user to review the diff in their IDE before the change is effectively applied by the AI. This preference will be honored for future rule file modifications.
|
||||
Reference in New Issue
Block a user