Initial commit
This commit is contained in:
12
.claude-plugin/plugin.json
Normal file
12
.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "debugging-tools",
|
||||||
|
"description": "Tools for debugging and troubleshooting applications",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "Josh Nichols",
|
||||||
|
"email": "josh@technicalpickles.com"
|
||||||
|
},
|
||||||
|
"skills": [
|
||||||
|
"./skills"
|
||||||
|
]
|
||||||
|
}
|
||||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# debugging-tools
|
||||||
|
|
||||||
|
Tools for debugging and troubleshooting applications
|
||||||
101
plugin.lock.json
Normal file
101
plugin.lock.json
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
{
|
||||||
|
"$schema": "internal://schemas/plugin.lock.v1.json",
|
||||||
|
"pluginId": "gh:technicalpickles/pickled-claude-plugins:plugins/debugging-tools",
|
||||||
|
"normalized": {
|
||||||
|
"repo": null,
|
||||||
|
"ref": "refs/tags/v20251128.0",
|
||||||
|
"commit": "b559c19d4e9ae7210b346e27219a1ba7d8cc239d",
|
||||||
|
"treeHash": "6cefdf4c9050098367915640e8b821a8531a148f8a4cce5a8ccb8fb7720e4516",
|
||||||
|
"generatedAt": "2025-11-28T10:28:36.428027Z",
|
||||||
|
"toolVersion": "publish_plugins.py@0.2.0"
|
||||||
|
},
|
||||||
|
"origin": {
|
||||||
|
"remote": "git@github.com:zhongweili/42plugin-data.git",
|
||||||
|
"branch": "master",
|
||||||
|
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
|
||||||
|
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
|
||||||
|
},
|
||||||
|
"manifest": {
|
||||||
|
"name": "debugging-tools",
|
||||||
|
"description": "Tools for debugging and troubleshooting applications",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "README.md",
|
||||||
|
"sha256": "7ceb07ee7fa1b409a47cb61082a378f52868618a3e02e0983f8f6b92014beef4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": ".claude-plugin/plugin.json",
|
||||||
|
"sha256": "4ea2f1a286bd580931104420f852786111e921430d549627356dd2f97e9f9823"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/scope/SKILL.md",
|
||||||
|
"sha256": "dae97cfc7057270f7f3fe257de033ada158e2d21a3d93373fea1ec57fa53f3af"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/scope/references/quick-reference.md",
|
||||||
|
"sha256": "6017925aa9add68d19b084bb8bd930475f072976224039cdfb959b426623c2f4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/scope/references/real-world-examples.md",
|
||||||
|
"sha256": "0c92b088ed38ebe77692267014166f5bbd446bbc886eb80addb81c31dbfad899"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/scope/references/testing-guide.md",
|
||||||
|
"sha256": "017a5c2736b6d5852026bc8488e7a19e813a7ab4eacd8683177b823e8e3738c9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/scope/scripts/create-doctor-group.sh",
|
||||||
|
"sha256": "8f0b6b690d90d1d791780fb2344df3c64e1d536dbe21a39245d8a8cde9b7554a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/scope/scripts/create-known-error.sh",
|
||||||
|
"sha256": "18e5a161735cf2df5c9b4008530f0943484ee30641f804f43ff4ece33fd46961"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/mcpproxy-debug/SKILL.md",
|
||||||
|
"sha256": "c0e5fce1924e27efe4cff8f119a4002fcbf917a46be8d4e1bd5240f4c773b9e2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/mcpproxy-debug/references/docker-isolation-guide.md",
|
||||||
|
"sha256": "7ed366fa44acd824657fedbe61711d0cbf3385edf34acd7f7dc61360413f4363"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/mcpproxy-debug/references/configuration-patterns.md",
|
||||||
|
"sha256": "76438d32a438936a936e7ef81205ee823933fadfacd57bdcafed336285b2a111"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/mcpproxy-debug/references/debugging-examples.md",
|
||||||
|
"sha256": "32f59228425a7694d383f07d1fcf87ae88c644e0b990500eda3283eb5e5a72b8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/mcpproxy-debug/references/connection-failures.md",
|
||||||
|
"sha256": "c718f61799fe934320e89d9452755b1166852e3d55acea746f294f775d2fec6f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/mcpproxy-debug/scripts/check_status.sh",
|
||||||
|
"sha256": "f8ac448e8e7c7c754708d9f5b02ed82a0f0b8d4f0ade3e786eecd859dc3027ec"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/mcpproxy-debug/scripts/diagnose_server.sh",
|
||||||
|
"sha256": "2fc619f5b70005bc3ff6b9485213f24afc8c67d28f179362f00bac9194d995e0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/mcpproxy-debug/scripts/compare_servers.sh",
|
||||||
|
"sha256": "408ab05045070cf6faecdf1ccf892ad0ce97c2889c6da34ad9b2bb5acbe6aeab"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/mcpproxy-debug/scripts/get_api_key.sh",
|
||||||
|
"sha256": "53f1b44ae45ae87f1565e33849eb165e721e8c5a12ca3d604b5b6b3e0cfca0ea"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dirSha256": "6cefdf4c9050098367915640e8b821a8531a148f8a4cce5a8ccb8fb7720e4516"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"scannedAt": null,
|
||||||
|
"scannerVersion": null,
|
||||||
|
"flags": []
|
||||||
|
}
|
||||||
|
}
|
||||||
221
skills/mcpproxy-debug/SKILL.md
Normal file
221
skills/mcpproxy-debug/SKILL.md
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
---
|
||||||
|
name: mcpproxy-debug
|
||||||
|
description: This skill should be used when debugging, configuring, or troubleshooting MCPProxy (smart-mcp-proxy/mcpproxy-go). It provides workflows for checking server status, diagnosing connection failures, fixing configuration issues, and understanding Docker isolation behavior.
|
||||||
|
---
|
||||||
|
|
||||||
|
# MCPProxy Debug
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
MCPProxy is a Go-based smart proxy for Model Context Protocol (MCP) servers. This skill provides the capability to diagnose and fix MCPProxy issues systematically.
|
||||||
|
|
||||||
|
**Use this skill when:**
|
||||||
|
- MCPProxy server won't start or connect
|
||||||
|
- MCP servers show `connected: false` in status
|
||||||
|
- Docker isolation is causing failures
|
||||||
|
- Configuration changes aren't working
|
||||||
|
- Need to verify MCPProxy is running correctly
|
||||||
|
|
||||||
|
**Do NOT use this skill for:**
|
||||||
|
- Using MCPProxy tools normally → Use `dev-tools:using-mcpproxy-tools` instead
|
||||||
|
|
||||||
|
## Core Architecture (Quick Reference)
|
||||||
|
|
||||||
|
- **Core Server**: `mcpproxy` process at `127.0.0.1:8080` (default)
|
||||||
|
- **Config**: `~/.mcpproxy/mcp_config.json`
|
||||||
|
- **Logs**: `~/Library/Logs/mcpproxy/` (macOS) or `~/.mcpproxy/logs/` (Linux)
|
||||||
|
- **Database**: `~/.mcpproxy/config.db` (locked when running)
|
||||||
|
|
||||||
|
## Navigation Map - Start Here
|
||||||
|
|
||||||
|
Use this decision tree to find the right reference:
|
||||||
|
|
||||||
|
```
|
||||||
|
Problem: Where do I look?
|
||||||
|
├─ Server won't connect / shows errors
|
||||||
|
│ └─ Read: references/connection-failures.md
|
||||||
|
│
|
||||||
|
├─ Docker isolation issues / "not a TTY" errors
|
||||||
|
│ └─ Read: references/docker-isolation-guide.md
|
||||||
|
│
|
||||||
|
├─ Adding/configuring servers / config file questions
|
||||||
|
│ └─ Read: references/configuration-patterns.md
|
||||||
|
│
|
||||||
|
└─ Need detailed examples from real debugging sessions
|
||||||
|
└─ Read: references/debugging-examples.md (already exists)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Progressive disclosure principle**: Load only what you need. Start with the quick checks below. If those don't solve it, read the specific reference file.
|
||||||
|
|
||||||
|
## Quick Health Check (Do This First)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Is mcpproxy running?
|
||||||
|
ps aux | grep mcpproxy | grep -v grep
|
||||||
|
|
||||||
|
# 2. Get API key
|
||||||
|
grep '"api_key"' ~/.mcpproxy/mcp_config.json
|
||||||
|
|
||||||
|
# 3. Check server status
|
||||||
|
curl -s "http://127.0.0.1:8080/api/v1/servers?apikey=YOUR_KEY" | python3 -m json.tool
|
||||||
|
|
||||||
|
# 4. Check for recent errors
|
||||||
|
tail -50 ~/Library/Logs/mcpproxy/main.log | grep -i error
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key status fields to check:**
|
||||||
|
- `connected`: Boolean - is the server connected?
|
||||||
|
- `status`: String - current state (connecting, ready, error)
|
||||||
|
- `last_error`: String - most recent error message
|
||||||
|
- `tool_count`: Number - how many tools available
|
||||||
|
|
||||||
|
**If status looks bad** → Read the appropriate reference file from Navigation Map above.
|
||||||
|
|
||||||
|
## Top 3 Common Issues (One-Line Fixes)
|
||||||
|
|
||||||
|
### 1. "the input device is not a TTY" (Docker servers)
|
||||||
|
|
||||||
|
**Fix:** Add `"isolation": {"enabled": false}` to the Docker-based server config.
|
||||||
|
|
||||||
|
**Why:** Docker isolation wraps Docker commands in Docker (Docker-in-Docker). See `references/docker-isolation-guide.md` for details.
|
||||||
|
|
||||||
|
### 2. "unexpected argument found" (uvx/npx servers)
|
||||||
|
|
||||||
|
**Fix:** Put package name as first arg: `["mcp-server-name", "--arg", "value"]`
|
||||||
|
|
||||||
|
**Why:** Package managers need package name first, then its arguments. See `references/configuration-patterns.md` for examples.
|
||||||
|
|
||||||
|
### 3. "Invalid or missing API key" after restart
|
||||||
|
|
||||||
|
**Fix:** Check `echo $MCPPROXY_API_KEY` - environment variable overrides config file.
|
||||||
|
|
||||||
|
**Why:** Tray app may set `MCPPROXY_API_KEY`. Get current key from logs: `grep "api_key_prefix" ~/Library/Logs/mcpproxy/main.log | tail -1`
|
||||||
|
|
||||||
|
## Essential Commands
|
||||||
|
|
||||||
|
### Restart MCPProxy
|
||||||
|
```bash
|
||||||
|
pkill mcpproxy
|
||||||
|
sleep 2
|
||||||
|
open /Applications/mcpproxy.app # macOS
|
||||||
|
# OR
|
||||||
|
mcpproxy & # Linux/headless
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Specific Server Logs
|
||||||
|
```bash
|
||||||
|
# See server-specific log (most revealing for connection issues)
|
||||||
|
tail -50 ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
|
||||||
|
|
||||||
|
# Find errors in server log
|
||||||
|
grep -i "error\|stderr" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -20
|
||||||
|
```
|
||||||
|
|
||||||
|
### Trigger Config Reload
|
||||||
|
```bash
|
||||||
|
# Touch config to trigger file watcher
|
||||||
|
touch ~/.mcpproxy/mcp_config.json
|
||||||
|
|
||||||
|
# Verify reload happened
|
||||||
|
grep -i "config.*reload" ~/Library/Logs/mcpproxy/main.log | tail -3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Container Check
|
||||||
|
```bash
|
||||||
|
# List MCPProxy containers
|
||||||
|
docker ps | grep mcpproxy
|
||||||
|
|
||||||
|
# Check container logs
|
||||||
|
docker logs CONTAINER_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
## Helper Scripts
|
||||||
|
|
||||||
|
Located in `scripts/` directory:
|
||||||
|
|
||||||
|
- **`check_status.sh`** - Quick health check of mcpproxy and all servers
|
||||||
|
- **`get_api_key.sh`** - Extract current API key from config or logs
|
||||||
|
- **`diagnose_server.sh SERVER_NAME`** - Comprehensive server diagnosis with error pattern detection
|
||||||
|
- **`compare_servers.sh WORKING BROKEN`** - Compare configs and status to find differences
|
||||||
|
|
||||||
|
Usage: `~/.claude/skills/mcpproxy-debug/scripts/SCRIPT_NAME.sh`
|
||||||
|
|
||||||
|
## References - Deep Dives
|
||||||
|
|
||||||
|
Load these on-demand when quick fixes don't solve the problem:
|
||||||
|
|
||||||
|
### `references/connection-failures.md`
|
||||||
|
**When to read:** Server shows `connected: false` or connection errors
|
||||||
|
|
||||||
|
**Contains:**
|
||||||
|
- Step-by-step connection diagnosis workflow
|
||||||
|
- Common error patterns with detailed explanations
|
||||||
|
- "context deadline exceeded" root cause analysis
|
||||||
|
- Server-specific log investigation techniques
|
||||||
|
- Real patterns: uvx args, Docker TTY, missing packages
|
||||||
|
|
||||||
|
### `references/docker-isolation-guide.md`
|
||||||
|
**When to read:** Docker isolation enabled and servers failing, or "not a TTY" errors
|
||||||
|
|
||||||
|
**Contains:**
|
||||||
|
- How Docker isolation works in MCPProxy
|
||||||
|
- Configuration options and defaults
|
||||||
|
- Docker-in-Docker prevention strategies
|
||||||
|
- Colima context support
|
||||||
|
- When to disable isolation (with examples)
|
||||||
|
- Volume mounting for file access
|
||||||
|
|
||||||
|
### `references/configuration-patterns.md`
|
||||||
|
**When to read:** Adding new servers or fixing config issues
|
||||||
|
|
||||||
|
**Contains:**
|
||||||
|
- Complete config structures for all server types
|
||||||
|
- uvx/npx package manager patterns
|
||||||
|
- Docker-based server patterns
|
||||||
|
- HTTP/SSE server patterns
|
||||||
|
- Common configuration mistakes with fixes
|
||||||
|
- Environment variable handling
|
||||||
|
- Quarantine settings
|
||||||
|
|
||||||
|
### `references/debugging-examples.md` (already exists)
|
||||||
|
**When to read:** Need detailed walkthroughs from real debugging sessions
|
||||||
|
|
||||||
|
**Contains:**
|
||||||
|
- Real-world case studies
|
||||||
|
- Complete diagnostic workflows with root cause analysis
|
||||||
|
- Pattern recognition guides for complex failure modes
|
||||||
|
|
||||||
|
## When to Load References
|
||||||
|
|
||||||
|
**Don't load references preemptively.** Use this workflow:
|
||||||
|
|
||||||
|
1. Run Quick Health Check (above)
|
||||||
|
2. Identify the problem category
|
||||||
|
3. Check Top 3 Common Issues for one-line fix
|
||||||
|
4. If not solved → Read the specific reference file from Navigation Map
|
||||||
|
5. Follow the detailed workflow in that reference
|
||||||
|
|
||||||
|
**Why:** Loading all references upfront = 4,000+ tokens of mostly irrelevant content. Progressive disclosure keeps context lean and relevant.
|
||||||
|
|
||||||
|
## Environment Variables Reference
|
||||||
|
|
||||||
|
- `MCPPROXY_API_KEY` - Set API key (overrides config file)
|
||||||
|
- `MCPPROXY_LISTEN` - Override bind address (e.g., `:8080`)
|
||||||
|
- `MCPPROXY_DEBUG` - Enable debug mode
|
||||||
|
- `HEADLESS` - Run without launching browser
|
||||||
|
|
||||||
|
**API Key Priority:** ENV var > config file > auto-generated
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
This skill is a **navigation map**, not a documentation dump.
|
||||||
|
|
||||||
|
**Start with:**
|
||||||
|
1. Quick Health Check
|
||||||
|
2. Top 3 Common Issues
|
||||||
|
|
||||||
|
**If not solved:**
|
||||||
|
3. Use Navigation Map to find the right reference
|
||||||
|
4. Read only that reference file
|
||||||
|
|
||||||
|
**Result:** Fast diagnosis with minimal context overhead. Load ~1,400 tokens initially, then ~1,600 more only when needed for specific problems.
|
||||||
641
skills/mcpproxy-debug/references/configuration-patterns.md
Normal file
641
skills/mcpproxy-debug/references/configuration-patterns.md
Normal file
@@ -0,0 +1,641 @@
|
|||||||
|
# Configuration Patterns - Complete Reference
|
||||||
|
|
||||||
|
**When to use this reference:** Adding new MCP servers to MCPProxy or fixing configuration issues.
|
||||||
|
|
||||||
|
This reference provides complete configuration structures for all server types with detailed explanations.
|
||||||
|
|
||||||
|
## Configuration File Location
|
||||||
|
|
||||||
|
`~/.mcpproxy/mcp_config.json`
|
||||||
|
|
||||||
|
**Important:** After editing, trigger reload:
|
||||||
|
```bash
|
||||||
|
touch ~/.mcpproxy/mcp_config.json
|
||||||
|
# OR restart mcpproxy
|
||||||
|
pkill mcpproxy && open /Applications/mcpproxy.app
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complete Configuration Patterns
|
||||||
|
|
||||||
|
### Pattern 1: uvx/npx Servers (Package Managers)
|
||||||
|
|
||||||
|
**Key rule:** Package name MUST be first argument.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"server-name": {
|
||||||
|
"protocol": "stdio",
|
||||||
|
"command": "uvx", // or "npx"
|
||||||
|
"args": [
|
||||||
|
"package-name", // CRITICAL: Package name FIRST
|
||||||
|
"--arg1", // Then package arguments
|
||||||
|
"value1",
|
||||||
|
"--arg2",
|
||||||
|
"value2"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
// Optional environment variables
|
||||||
|
"API_KEY": "secret",
|
||||||
|
"DEBUG": "true"
|
||||||
|
},
|
||||||
|
"working_dir": "/path/to/dir", // Optional working directory
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Real examples:**
|
||||||
|
|
||||||
|
#### Time Server with Timezone
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"time": {
|
||||||
|
"protocol": "stdio",
|
||||||
|
"command": "uvx",
|
||||||
|
"args": [
|
||||||
|
"mcp-server-time",
|
||||||
|
"--local-timezone",
|
||||||
|
"America/New_York"
|
||||||
|
],
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### SQLite Server with Database Path
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sqlite": {
|
||||||
|
"protocol": "stdio",
|
||||||
|
"command": "uvx",
|
||||||
|
"args": [
|
||||||
|
"mcp-server-sqlite",
|
||||||
|
"--db-path",
|
||||||
|
"/Users/username/data/mydb.sqlite"
|
||||||
|
],
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Filesystem Server with Paths
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"filesystem": {
|
||||||
|
"protocol": "stdio",
|
||||||
|
"command": "npx",
|
||||||
|
"args": [
|
||||||
|
"@modelcontextprotocol/server-filesystem",
|
||||||
|
"/Users/username/workspace",
|
||||||
|
"/Users/username/Documents"
|
||||||
|
],
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Common mistakes:**
|
||||||
|
```json
|
||||||
|
// ❌ WRONG - Arguments before package name
|
||||||
|
{
|
||||||
|
"command": "uvx",
|
||||||
|
"args": ["--local-timezone", "America/New_York"]
|
||||||
|
// Error: "unexpected argument '--local-timezone' found"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ CORRECT - Package name first
|
||||||
|
{
|
||||||
|
"command": "uvx",
|
||||||
|
"args": ["mcp-server-time", "--local-timezone", "America/New_York"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 2: Docker-Based Servers
|
||||||
|
|
||||||
|
**Key rules:**
|
||||||
|
1. Use `-i` (NOT `-it`) for stdin pipe
|
||||||
|
2. ALWAYS disable isolation to prevent Docker-in-Docker
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"docker-server": {
|
||||||
|
"protocol": "stdio",
|
||||||
|
"command": "docker",
|
||||||
|
"args": [
|
||||||
|
"run",
|
||||||
|
"-i", // CRITICAL: -i only, not -it
|
||||||
|
"--rm", // Clean up container after exit
|
||||||
|
"-e", "VAR_NAME", // Pass environment variables
|
||||||
|
"image:tag", // Docker image
|
||||||
|
"subcommand" // Optional: server entrypoint
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"VAR_NAME": "value" // Values for -e flags
|
||||||
|
},
|
||||||
|
"isolation": {
|
||||||
|
"enabled": false // CRITICAL: Disable to prevent Docker-in-Docker
|
||||||
|
},
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Real examples:**
|
||||||
|
|
||||||
|
#### Basic Docker MCP Server
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"docker-mcp": {
|
||||||
|
"protocol": "stdio",
|
||||||
|
"command": "docker",
|
||||||
|
"args": [
|
||||||
|
"run",
|
||||||
|
"-i",
|
||||||
|
"--rm",
|
||||||
|
"ghcr.io/example/mcp-server:latest"
|
||||||
|
],
|
||||||
|
"isolation": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Docker Server with Environment Variables
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"api-server": {
|
||||||
|
"protocol": "stdio",
|
||||||
|
"command": "docker",
|
||||||
|
"args": [
|
||||||
|
"run",
|
||||||
|
"-i",
|
||||||
|
"--rm",
|
||||||
|
"-e", "API_KEY",
|
||||||
|
"-e", "API_URL",
|
||||||
|
"mycompany/api-mcp:v1.0"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"API_KEY": "sk-xxxx",
|
||||||
|
"API_URL": "https://api.example.com"
|
||||||
|
},
|
||||||
|
"isolation": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Docker Server with Volume Mounts
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"file-processor": {
|
||||||
|
"protocol": "stdio",
|
||||||
|
"command": "docker",
|
||||||
|
"args": [
|
||||||
|
"run",
|
||||||
|
"-i",
|
||||||
|
"--rm",
|
||||||
|
"-v", "/Users/username/data:/data:ro",
|
||||||
|
"mycompany/file-mcp:latest",
|
||||||
|
"--data-dir", "/data"
|
||||||
|
],
|
||||||
|
"isolation": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Common mistakes:**
|
||||||
|
```json
|
||||||
|
// ❌ WRONG - Using -it flags
|
||||||
|
{
|
||||||
|
"command": "docker",
|
||||||
|
"args": ["run", "-it", "--rm", "image:tag"]
|
||||||
|
// Error: "the input device is not a TTY"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ WRONG - Isolation enabled
|
||||||
|
{
|
||||||
|
"command": "docker",
|
||||||
|
"args": ["run", "-i", "--rm", "image:tag"],
|
||||||
|
"isolation": {"enabled": true}
|
||||||
|
// Error: Docker-in-Docker, "not a TTY"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ CORRECT - Use -i only, disable isolation
|
||||||
|
{
|
||||||
|
"command": "docker",
|
||||||
|
"args": ["run", "-i", "--rm", "image:tag"],
|
||||||
|
"isolation": {"enabled": false}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 3: HTTP/SSE Servers
|
||||||
|
|
||||||
|
For servers accessed via HTTP or Server-Sent Events.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"http-server": {
|
||||||
|
"protocol": "http", // or "sse"
|
||||||
|
"url": "https://api.example.com/mcp",
|
||||||
|
"headers": {
|
||||||
|
// Optional authentication headers
|
||||||
|
"Authorization": "Bearer token",
|
||||||
|
"X-API-Key": "secret"
|
||||||
|
},
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Real examples:**
|
||||||
|
|
||||||
|
#### HTTP Server with Bearer Token
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"remote-api": {
|
||||||
|
"protocol": "http",
|
||||||
|
"url": "https://mcp.example.com/v1",
|
||||||
|
"headers": {
|
||||||
|
"Authorization": "Bearer sk-xxxxxxxxxxxx"
|
||||||
|
},
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### SSE Server with API Key
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"streaming-api": {
|
||||||
|
"protocol": "sse",
|
||||||
|
"url": "https://stream.example.com/mcp",
|
||||||
|
"headers": {
|
||||||
|
"X-API-Key": "api-key-here"
|
||||||
|
},
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### HTTP Server Without Authentication
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"public-api": {
|
||||||
|
"protocol": "http",
|
||||||
|
"url": "http://localhost:3000/mcp",
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 4: Local Command Servers
|
||||||
|
|
||||||
|
For locally installed commands (not uvx/npx).
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"local-server": {
|
||||||
|
"protocol": "stdio",
|
||||||
|
"command": "/usr/local/bin/mcp-server", // Full path or command in PATH
|
||||||
|
"args": [
|
||||||
|
"--config", "/path/to/config.json",
|
||||||
|
"--verbose"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"HOME": "/Users/username",
|
||||||
|
"PYTHONPATH": "/path/to/modules"
|
||||||
|
},
|
||||||
|
"working_dir": "/Users/username/workspace",
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Real examples:**
|
||||||
|
|
||||||
|
#### Python Script as Server
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"custom-python": {
|
||||||
|
"protocol": "stdio",
|
||||||
|
"command": "python",
|
||||||
|
"args": [
|
||||||
|
"/Users/username/mcp-servers/my_server.py",
|
||||||
|
"--config", "production"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"PYTHONUNBUFFERED": "1"
|
||||||
|
},
|
||||||
|
"working_dir": "/Users/username/mcp-servers",
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Node.js Script as Server
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"custom-node": {
|
||||||
|
"protocol": "stdio",
|
||||||
|
"command": "node",
|
||||||
|
"args": [
|
||||||
|
"/Users/username/mcp-servers/server.js"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"NODE_ENV": "production"
|
||||||
|
},
|
||||||
|
"working_dir": "/Users/username/mcp-servers",
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complete Configuration File Structure
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"api_key": "mcpproxy-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||||
|
"listen": "127.0.0.1:8080",
|
||||||
|
"docker_isolation": {
|
||||||
|
"enabled": true,
|
||||||
|
"memory_limit": "512m",
|
||||||
|
"cpu_limit": "1.0",
|
||||||
|
"timeout": "30s",
|
||||||
|
"network_mode": "bridge",
|
||||||
|
"default_images": {
|
||||||
|
"uvx": "python:3.11",
|
||||||
|
"npx": "node:20",
|
||||||
|
"python": "python:3.11",
|
||||||
|
"node": "node:20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mcpServers": {
|
||||||
|
"server1": {
|
||||||
|
"protocol": "stdio",
|
||||||
|
"command": "uvx",
|
||||||
|
"args": ["package-name"],
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
},
|
||||||
|
"server2": {
|
||||||
|
"protocol": "http",
|
||||||
|
"url": "https://api.example.com",
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Configuration Mistakes
|
||||||
|
|
||||||
|
### 1. Missing Package Name (uvx/npx)
|
||||||
|
```json
|
||||||
|
// ❌ WRONG
|
||||||
|
{"command": "uvx", "args": ["--arg", "value"]}
|
||||||
|
|
||||||
|
// ✅ CORRECT
|
||||||
|
{"command": "uvx", "args": ["package-name", "--arg", "value"]}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Wrong Docker Flags
|
||||||
|
```json
|
||||||
|
// ❌ WRONG - Using -it
|
||||||
|
{"command": "docker", "args": ["run", "-it", "--rm", "image"]}
|
||||||
|
|
||||||
|
// ✅ CORRECT - Using -i only
|
||||||
|
{"command": "docker", "args": ["run", "-i", "--rm", "image"]}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Docker Isolation Not Disabled
|
||||||
|
```json
|
||||||
|
// ❌ WRONG - Isolation enabled for Docker
|
||||||
|
{
|
||||||
|
"command": "docker",
|
||||||
|
"args": ["run", "-i", "--rm", "image"],
|
||||||
|
"isolation": {"enabled": true} // Docker-in-Docker!
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ CORRECT - Isolation disabled
|
||||||
|
{
|
||||||
|
"command": "docker",
|
||||||
|
"args": ["run", "-i", "--rm", "image"],
|
||||||
|
"isolation": {"enabled": false}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Wrong Protocol for Server Type
|
||||||
|
```json
|
||||||
|
// ❌ WRONG - Using stdio for URL
|
||||||
|
{"protocol": "stdio", "url": "https://api.example.com"}
|
||||||
|
|
||||||
|
// ❌ WRONG - Using http for command
|
||||||
|
{"protocol": "http", "command": "uvx", "args": ["package"]}
|
||||||
|
|
||||||
|
// ✅ CORRECT - Match protocol to type
|
||||||
|
{"protocol": "http", "url": "https://api.example.com"}
|
||||||
|
{"protocol": "stdio", "command": "uvx", "args": ["package"]}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Server Still Quarantined
|
||||||
|
```json
|
||||||
|
// ❌ WRONG - Quarantine prevents execution
|
||||||
|
{
|
||||||
|
"command": "uvx",
|
||||||
|
"args": ["package"],
|
||||||
|
"quarantined": true // Tools return security analysis!
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ CORRECT - Set to false to use tools
|
||||||
|
{
|
||||||
|
"command": "uvx",
|
||||||
|
"args": ["package"],
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Invalid JSON
|
||||||
|
```json
|
||||||
|
// ❌ WRONG - Trailing comma
|
||||||
|
{
|
||||||
|
"command": "uvx",
|
||||||
|
"args": ["package"],
|
||||||
|
"enabled": true, // Trailing comma breaks JSON
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ CORRECT - No trailing comma
|
||||||
|
{
|
||||||
|
"command": "uvx",
|
||||||
|
"args": ["package"],
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
### Built-in Environment Variables
|
||||||
|
|
||||||
|
MCPProxy automatically provides:
|
||||||
|
- `HOME` - User's home directory
|
||||||
|
- `PATH` - System PATH
|
||||||
|
|
||||||
|
### Custom Environment Variables
|
||||||
|
|
||||||
|
Add via `env` field:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"API_KEY": "secret",
|
||||||
|
"DEBUG": "true",
|
||||||
|
"CUSTOM_VAR": "value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Variable Priority
|
||||||
|
|
||||||
|
For Docker servers:
|
||||||
|
1. Variables in Docker args (`-e VAR=value`)
|
||||||
|
2. Variables in `env` field passed via `-e VAR` (value from env)
|
||||||
|
3. Host environment (if not overridden)
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "docker",
|
||||||
|
"args": [
|
||||||
|
"run", "-i", "--rm",
|
||||||
|
"-e", "API_KEY", // Value from env field
|
||||||
|
"-e", "DEBUG=true", // Value in args
|
||||||
|
"image:tag"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"API_KEY": "secret" // Used by -e API_KEY above
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quarantine Settings
|
||||||
|
|
||||||
|
New servers are automatically quarantined for security. To use them:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"quarantined": false // Set to false to enable tool execution
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**What quarantine does:**
|
||||||
|
- `quarantined: true` - Tools return security analysis instead of executing
|
||||||
|
- `quarantined: false` - Tools execute normally
|
||||||
|
|
||||||
|
**Best practice:** Review new servers before setting `quarantined: false`.
|
||||||
|
|
||||||
|
## Docker Isolation Configuration
|
||||||
|
|
||||||
|
See `references/docker-isolation-guide.md` for detailed Docker isolation configuration.
|
||||||
|
|
||||||
|
**Quick reference:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
// Global settings
|
||||||
|
"docker_isolation": {
|
||||||
|
"enabled": true, // Master switch
|
||||||
|
"memory_limit": "512m", // Default memory per container
|
||||||
|
"cpu_limit": "1.0", // Default CPU per container
|
||||||
|
"network_mode": "bridge" // Default network mode
|
||||||
|
},
|
||||||
|
|
||||||
|
// Per-server override
|
||||||
|
"mcpServers": {
|
||||||
|
"my-server": {
|
||||||
|
"isolation": {
|
||||||
|
"enabled": false, // Disable isolation
|
||||||
|
// OR customize:
|
||||||
|
"enabled": true,
|
||||||
|
"image": "python:3.12",
|
||||||
|
"memory_limit": "1g",
|
||||||
|
"cpu_limit": "2.0",
|
||||||
|
"network_mode": "host"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verification After Configuration
|
||||||
|
|
||||||
|
After adding or modifying server configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Trigger reload
|
||||||
|
touch ~/.mcpproxy/mcp_config.json
|
||||||
|
|
||||||
|
# 2. Wait for connection (30 seconds)
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
# 3. Check server status
|
||||||
|
curl -s "http://127.0.0.1:8080/api/v1/servers?apikey=YOUR_KEY" | \
|
||||||
|
python3 -m json.tool | grep -A 10 '"name": "SERVER_NAME"'
|
||||||
|
|
||||||
|
# 4. Verify connection
|
||||||
|
# Look for:
|
||||||
|
# "connected": true
|
||||||
|
# "status": "ready"
|
||||||
|
# "tool_count": > 0
|
||||||
|
|
||||||
|
# 5. If not connected, check logs
|
||||||
|
tail -50 ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Checklist
|
||||||
|
|
||||||
|
When adding a new server, verify:
|
||||||
|
|
||||||
|
- [ ] JSON is valid (no trailing commas, quotes matched)
|
||||||
|
- [ ] Protocol matches server type (stdio/http/sse)
|
||||||
|
- [ ] For uvx/npx: Package name is first argument
|
||||||
|
- [ ] For Docker: Using `-i` not `-it`
|
||||||
|
- [ ] For Docker: Isolation disabled (`"enabled": false`)
|
||||||
|
- [ ] `enabled` is set to `true`
|
||||||
|
- [ ] `quarantined` is set to `false` (after review)
|
||||||
|
- [ ] Environment variables defined if needed
|
||||||
|
- [ ] Config file reloaded (touch or restart)
|
||||||
|
- [ ] Server status shows `connected: true` (wait 30s)
|
||||||
|
|
||||||
|
If server won't connect, see `references/connection-failures.md` for debugging.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Configuration patterns vary by server type. The most common mistakes are:
|
||||||
|
1. Missing package name for uvx/npx
|
||||||
|
2. Using `-it` instead of `-i` for Docker
|
||||||
|
3. Leaving isolation enabled for Docker-based servers
|
||||||
|
4. Leaving servers quarantined
|
||||||
|
|
||||||
|
Follow the pattern for your server type, verify with the checklist, and check logs if it doesn't connect.
|
||||||
372
skills/mcpproxy-debug/references/connection-failures.md
Normal file
372
skills/mcpproxy-debug/references/connection-failures.md
Normal file
@@ -0,0 +1,372 @@
|
|||||||
|
# Connection Failures - Detailed Diagnosis
|
||||||
|
|
||||||
|
**When to use this reference:** Server shows `connected: false` or has connection errors in status API.
|
||||||
|
|
||||||
|
This reference provides step-by-step workflows for diagnosing why an MCP server won't connect through MCPProxy.
|
||||||
|
|
||||||
|
## Systematic Diagnosis Workflow
|
||||||
|
|
||||||
|
### Step 1: Check Server-Specific Logs
|
||||||
|
|
||||||
|
Server-specific logs contain the most revealing information, especially stderr output from the failing server.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List all server logs (most recent first)
|
||||||
|
ls -lhrt ~/Library/Logs/mcpproxy/server-*.log | tail -10
|
||||||
|
|
||||||
|
# Check specific server log (last 50 lines)
|
||||||
|
tail -50 ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
|
||||||
|
|
||||||
|
# Find errors in server log
|
||||||
|
grep -i "error\|failed\|stderr" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -20
|
||||||
|
|
||||||
|
# Focus on stderr (most revealing)
|
||||||
|
grep "stderr" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -20
|
||||||
|
```
|
||||||
|
|
||||||
|
**What to look for:**
|
||||||
|
- Error messages in stderr output
|
||||||
|
- Stack traces indicating crashes
|
||||||
|
- Missing dependencies or packages
|
||||||
|
- Permission errors
|
||||||
|
- Network connection failures
|
||||||
|
|
||||||
|
### Step 2: Identify Error Patterns
|
||||||
|
|
||||||
|
Most connection failures follow recognizable patterns. Match the error message to the pattern below.
|
||||||
|
|
||||||
|
## Common Error Patterns
|
||||||
|
|
||||||
|
### Pattern: "unexpected argument found"
|
||||||
|
|
||||||
|
**Example stderr:**
|
||||||
|
```
|
||||||
|
error: unexpected argument '--local-timezone' found
|
||||||
|
```
|
||||||
|
|
||||||
|
**Root cause:** Package manager (uvx/npx) received server arguments before package name.
|
||||||
|
|
||||||
|
**Commands affected:** `uvx`, `npx`, `yarn dlx`, `bunx`
|
||||||
|
|
||||||
|
**How it happens:**
|
||||||
|
```json
|
||||||
|
// WRONG - Arguments passed to uvx instead of the package
|
||||||
|
{
|
||||||
|
"command": "uvx",
|
||||||
|
"args": ["--local-timezone", "America/New_York"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package manager sees: uvx --local-timezone America/New_York
|
||||||
|
// And thinks: "--local-timezone is not a uvx option!"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fix:**
|
||||||
|
```json
|
||||||
|
// CORRECT - Package name first, then its arguments
|
||||||
|
{
|
||||||
|
"command": "uvx",
|
||||||
|
"args": [
|
||||||
|
"mcp-server-time", // Package name FIRST
|
||||||
|
"--local-timezone", // Then package's arguments
|
||||||
|
"America/New_York"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verification:**
|
||||||
|
```bash
|
||||||
|
# After fixing config, check server log for successful connection
|
||||||
|
tail -20 ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | grep -i "connected\|ready"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern: "the input device is not a TTY"
|
||||||
|
|
||||||
|
**Example stderr:**
|
||||||
|
```
|
||||||
|
the input device is not a TTY
|
||||||
|
```
|
||||||
|
|
||||||
|
**Root causes:**
|
||||||
|
|
||||||
|
#### Cause 1: Using `-it` with Docker
|
||||||
|
|
||||||
|
MCPProxy communicates via stdio pipes, not terminal TTY sessions. Using `-it` with Docker tries to allocate a TTY, which fails in pipe mode.
|
||||||
|
|
||||||
|
```json
|
||||||
|
// WRONG - Docker with -it flags
|
||||||
|
{
|
||||||
|
"command": "docker",
|
||||||
|
"args": ["run", "-it", "--rm", "image:tag"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fix:**
|
||||||
|
```json
|
||||||
|
// CORRECT - Only -i for stdin pipe
|
||||||
|
{
|
||||||
|
"command": "docker",
|
||||||
|
"args": ["run", "-i", "--rm", "image:tag"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Cause 2: Docker Isolation on Docker-based Server
|
||||||
|
|
||||||
|
MCPProxy's Docker isolation feature wraps commands in Docker containers. When the command itself is Docker, you get Docker-in-Docker, which causes TTY issues.
|
||||||
|
|
||||||
|
```json
|
||||||
|
// WRONG - Docker isolation wrapping a Docker command
|
||||||
|
{
|
||||||
|
"command": "docker",
|
||||||
|
"args": ["run", "-i", "--rm", "image:tag"],
|
||||||
|
"isolation": {
|
||||||
|
"enabled": true // This wraps Docker in Docker!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fix:**
|
||||||
|
```json
|
||||||
|
// CORRECT - Disable isolation for Docker commands
|
||||||
|
{
|
||||||
|
"command": "docker",
|
||||||
|
"args": ["run", "-i", "--rm", "image:tag"],
|
||||||
|
"isolation": {
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why this happens:** MCPProxy auto-detects Docker commands and should skip isolation, but explicit configuration is more reliable.
|
||||||
|
|
||||||
|
**Verification:**
|
||||||
|
```bash
|
||||||
|
# Check if isolation was applied
|
||||||
|
grep -i "docker isolation" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -5
|
||||||
|
|
||||||
|
# Verify no Docker containers running for this server
|
||||||
|
docker ps | grep mcpproxy-SERVER_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern: "context deadline exceeded"
|
||||||
|
|
||||||
|
**Example error:**
|
||||||
|
```
|
||||||
|
last_error: "context deadline exceeded"
|
||||||
|
status: "error"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Root cause:** Server failed to initialize within timeout (usually 30 seconds).
|
||||||
|
|
||||||
|
**Common reasons:**
|
||||||
|
|
||||||
|
1. **Underlying error hidden** - Check stderr for the real error:
|
||||||
|
```bash
|
||||||
|
grep "stderr" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -20
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Missing package name** (uvx/npx) - Look for "unexpected argument":
|
||||||
|
```bash
|
||||||
|
grep "unexpected argument" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Docker TTY issue** - Look for "not a TTY":
|
||||||
|
```bash
|
||||||
|
grep "TTY" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Docker image pull failed**:
|
||||||
|
```bash
|
||||||
|
grep -i "pull\|not found" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Server crashed during startup**:
|
||||||
|
```bash
|
||||||
|
grep -i "stack trace\|panic\|exception" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Missing environment variables**:
|
||||||
|
```bash
|
||||||
|
grep -i "missing\|required.*variable" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
|
||||||
|
```
|
||||||
|
|
||||||
|
7. **Network issues** (HTTP servers):
|
||||||
|
```bash
|
||||||
|
grep -i "connection refused\|timeout" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
|
||||||
|
```
|
||||||
|
|
||||||
|
**Investigation workflow:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Check if process even started
|
||||||
|
grep "Starting connection\|Docker isolation setup" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -5
|
||||||
|
|
||||||
|
# 2. Check stderr for the real error
|
||||||
|
grep "stderr" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -20
|
||||||
|
|
||||||
|
# 3. If Docker isolation enabled, check container logs
|
||||||
|
docker ps -a | grep mcpproxy-SERVER_NAME
|
||||||
|
docker logs CONTAINER_ID
|
||||||
|
|
||||||
|
# 4. Check Docker command being executed
|
||||||
|
grep "docker_run_args" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -3
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fix:** Address the underlying error found in stderr.
|
||||||
|
|
||||||
|
### Pattern: "database is locked"
|
||||||
|
|
||||||
|
**Example error:**
|
||||||
|
```
|
||||||
|
ERROR: database is locked
|
||||||
|
```
|
||||||
|
|
||||||
|
**Root cause:** Another mcpproxy instance is running and has locked `~/.mcpproxy/config.db`.
|
||||||
|
|
||||||
|
**Fix:**
|
||||||
|
```bash
|
||||||
|
# Kill all mcpproxy instances
|
||||||
|
pkill mcpproxy
|
||||||
|
|
||||||
|
# Wait for processes to clean up
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# Verify no mcpproxy processes remain
|
||||||
|
ps aux | grep mcpproxy | grep -v grep
|
||||||
|
|
||||||
|
# Restart mcpproxy
|
||||||
|
open /Applications/mcpproxy.app # macOS
|
||||||
|
# OR
|
||||||
|
mcpproxy & # Linux/headless
|
||||||
|
```
|
||||||
|
|
||||||
|
**Prevention:** Always use `pkill mcpproxy` before starting a new instance.
|
||||||
|
|
||||||
|
### Pattern: "transport error" / "EOF"
|
||||||
|
|
||||||
|
**Example error:**
|
||||||
|
```
|
||||||
|
last_error: "transport error: EOF"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Root cause:** Server process exited unexpectedly or closed stdio pipes.
|
||||||
|
|
||||||
|
**Investigation:**
|
||||||
|
```bash
|
||||||
|
# Check if server crashed
|
||||||
|
grep -i "exit\|crash\|terminated" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -10
|
||||||
|
|
||||||
|
# Look for error messages before EOF
|
||||||
|
grep -B 20 "EOF" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -30
|
||||||
|
|
||||||
|
# Check stderr for crash details
|
||||||
|
grep "stderr" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -20
|
||||||
|
```
|
||||||
|
|
||||||
|
**Common causes:**
|
||||||
|
- Missing dependencies in Docker image
|
||||||
|
- Server code bug causing crash
|
||||||
|
- Memory/resource limits hit (Docker isolation)
|
||||||
|
- Permission errors accessing files
|
||||||
|
|
||||||
|
**Fix:** Depends on stderr output. Often requires fixing server code or Docker image.
|
||||||
|
|
||||||
|
## Step 3: Docker Isolation Checks
|
||||||
|
|
||||||
|
If Docker isolation is enabled and server is failing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check if isolation is active for this server
|
||||||
|
grep -i "docker isolation enabled\|docker isolation setup" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -5
|
||||||
|
|
||||||
|
# List running containers
|
||||||
|
docker ps | grep mcpproxy
|
||||||
|
|
||||||
|
# Check container status
|
||||||
|
docker ps -a | grep mcpproxy-SERVER_NAME
|
||||||
|
|
||||||
|
# View container logs
|
||||||
|
docker logs CONTAINER_NAME
|
||||||
|
|
||||||
|
# Check Docker context (for Colima users)
|
||||||
|
docker context show
|
||||||
|
```
|
||||||
|
|
||||||
|
**If Docker isolation is interfering:**
|
||||||
|
1. See `references/docker-isolation-guide.md` for detailed Docker troubleshooting
|
||||||
|
2. Consider disabling isolation for this server (add `"isolation": {"enabled": false}`)
|
||||||
|
|
||||||
|
## Step 4: Configuration Verification
|
||||||
|
|
||||||
|
Verify the server configuration is correct:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# View server config (pretty-printed)
|
||||||
|
cat ~/.mcpproxy/mcp_config.json | python3 -m json.tool | grep -A 20 '"name": "SERVER_NAME"'
|
||||||
|
|
||||||
|
# Check for common mistakes:
|
||||||
|
# - uvx/npx: Package name is first argument?
|
||||||
|
# - Docker: Using -i not -it?
|
||||||
|
# - Docker servers: isolation disabled?
|
||||||
|
# - Quarantined: "quarantined": false?
|
||||||
|
```
|
||||||
|
|
||||||
|
For detailed configuration patterns, see `references/configuration-patterns.md`.
|
||||||
|
|
||||||
|
## Step 5: Test Server Command Manually
|
||||||
|
|
||||||
|
Test if the server command works outside MCPProxy:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# For uvx servers
|
||||||
|
uvx mcp-server-name --arg value
|
||||||
|
|
||||||
|
# For Docker servers (remove -i for manual testing)
|
||||||
|
docker run --rm -it image:tag
|
||||||
|
|
||||||
|
# For npx servers
|
||||||
|
npx mcp-server-name --arg value
|
||||||
|
```
|
||||||
|
|
||||||
|
**What to look for:**
|
||||||
|
- Does the command fail with same error?
|
||||||
|
- Is the package/image available?
|
||||||
|
- Are arguments correct?
|
||||||
|
|
||||||
|
## Debugging Checklist
|
||||||
|
|
||||||
|
Use this checklist systematically:
|
||||||
|
|
||||||
|
- [ ] Checked server-specific log for errors
|
||||||
|
- [ ] Examined stderr output (most revealing)
|
||||||
|
- [ ] Matched error to common patterns above
|
||||||
|
- [ ] Verified command syntax (uvx: package name first, Docker: -i not -it)
|
||||||
|
- [ ] Checked Docker isolation status (disabled for Docker commands?)
|
||||||
|
- [ ] Verified configuration file is valid JSON
|
||||||
|
- [ ] Tested command manually outside MCPProxy
|
||||||
|
- [ ] Triggered config reload (touch config file or restart mcpproxy)
|
||||||
|
- [ ] Waited 30 seconds for connection to initialize
|
||||||
|
- [ ] Checked main.log for global errors
|
||||||
|
|
||||||
|
If all checks pass and server still won't connect, see `references/debugging-examples.md` for real-world case studies.
|
||||||
|
|
||||||
|
## Success Verification
|
||||||
|
|
||||||
|
After fixing the issue:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Check server status via API
|
||||||
|
curl -s "http://127.0.0.1:8080/api/v1/servers?apikey=YOUR_KEY" | python3 -m json.tool | grep -A 10 '"name": "SERVER_NAME"'
|
||||||
|
|
||||||
|
# Look for:
|
||||||
|
# "connected": true
|
||||||
|
# "status": "ready"
|
||||||
|
# "tool_count": > 0
|
||||||
|
|
||||||
|
# 2. Check server log for successful connection
|
||||||
|
tail -20 ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | grep -i "connected\|ready\|discovered"
|
||||||
|
|
||||||
|
# 3. Verify tools are available
|
||||||
|
curl -s "http://127.0.0.1:8080/api/v1/tools?apikey=YOUR_KEY" | python3 -m json.tool | grep "SERVER_NAME:"
|
||||||
|
```
|
||||||
|
|
||||||
|
If `connected: true` and `tool_count > 0`, the issue is resolved.
|
||||||
258
skills/mcpproxy-debug/references/debugging-examples.md
Normal file
258
skills/mcpproxy-debug/references/debugging-examples.md
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
# Real-World MCPProxy Debugging Examples
|
||||||
|
|
||||||
|
This file contains detailed walkthroughs of actual debugging sessions, providing concrete examples of how to diagnose and fix MCPProxy issues.
|
||||||
|
|
||||||
|
## Example 1: Buildkite Server - Docker Isolation Conflict
|
||||||
|
|
||||||
|
**Date**: October 2025
|
||||||
|
**Symptom**: Buildkite MCP server failing to connect with "the input device is not a TTY" error
|
||||||
|
**Server Type**: Docker-based MCP server (`ghcr.io/buildkite/buildkite-mcp-server:latest`)
|
||||||
|
|
||||||
|
### Initial Configuration
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "buildkite",
|
||||||
|
"protocol": "stdio",
|
||||||
|
"command": "docker",
|
||||||
|
"args": [
|
||||||
|
"run",
|
||||||
|
"--pull=always",
|
||||||
|
"-q",
|
||||||
|
"-i",
|
||||||
|
"--rm",
|
||||||
|
"-e",
|
||||||
|
"BUILDKITE_API_TOKEN",
|
||||||
|
"ghcr.io/buildkite/buildkite-mcp-server:latest",
|
||||||
|
"stdio"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"BUILDKITE_API_TOKEN": "${keyring:buildkite_token}"
|
||||||
|
},
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Diagnostic Process
|
||||||
|
|
||||||
|
1. **Ran diagnostic script**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~/.claude/skills/mcpproxy-debug/scripts/diagnose_server.sh buildkite
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Key findings from stderr**:
|
||||||
|
|
||||||
|
```
|
||||||
|
"the input device is not a TTY"
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Pattern recognition**: This error typically indicates:
|
||||||
|
|
||||||
|
- Either `-it` flags being used (but config showed `-i` only)
|
||||||
|
- OR Docker isolation wrapping a Docker command (Docker-in-Docker)
|
||||||
|
|
||||||
|
4. **Checked Docker isolation status**:
|
||||||
|
- Global `docker_isolation.enabled` was `true`
|
||||||
|
- Server had no explicit isolation override
|
||||||
|
- MCPProxy was wrapping the Docker command in another container
|
||||||
|
|
||||||
|
### Root Cause
|
||||||
|
|
||||||
|
Docker isolation was enabled globally, and MCPProxy was attempting to run:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run [isolation-wrapper-args] docker run [server-args] ...
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a Docker-in-Docker situation where the inner Docker command can't allocate a TTY because it's running inside a container.
|
||||||
|
|
||||||
|
### Solution
|
||||||
|
|
||||||
|
Added explicit isolation disable to the server configuration:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "buildkite",
|
||||||
|
"protocol": "stdio",
|
||||||
|
"command": "docker",
|
||||||
|
"args": [
|
||||||
|
"run",
|
||||||
|
"--pull=always",
|
||||||
|
"-q",
|
||||||
|
"-i",
|
||||||
|
"--rm",
|
||||||
|
"-e",
|
||||||
|
"BUILDKITE_API_TOKEN",
|
||||||
|
"ghcr.io/buildkite/buildkite-mcp-server:latest",
|
||||||
|
"stdio"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"BUILDKITE_API_TOKEN": "${keyring:buildkite_token}"
|
||||||
|
},
|
||||||
|
"isolation": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"enabled": true,
|
||||||
|
"quarantined": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verification
|
||||||
|
|
||||||
|
After restarting mcpproxy:
|
||||||
|
|
||||||
|
1. **Process inspection**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ps aux | grep mcpproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
Confirmed the buildkite server was now running directly:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run --cidfile ... --pull=always -q -i --rm -e BUILDKITE_API_TOKEN ghcr.io/buildkite/buildkite-mcp-server:latest stdio
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Connection verification**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~/.claude/skills/mcpproxy-debug/scripts/diagnose_server.sh buildkite
|
||||||
|
```
|
||||||
|
|
||||||
|
Output showed:
|
||||||
|
|
||||||
|
```
|
||||||
|
Successfully connected and initialized
|
||||||
|
server_name: "buildkite-mcp-server"
|
||||||
|
server_version: "0.7.2"
|
||||||
|
tool_count: 28
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **No more TTY errors** in recent logs
|
||||||
|
|
||||||
|
### Key Lessons
|
||||||
|
|
||||||
|
1. **Docker isolation auto-skip may fail**: The intended automatic skip for Docker commands didn't work in this case
|
||||||
|
2. **Always explicitly disable isolation for Docker commands**: Best practice to prevent Docker-in-Docker issues
|
||||||
|
3. **Stderr is the most revealing**: The "TTY" error in stderr immediately pointed to the issue
|
||||||
|
4. **The diagnostic script is highly effective**: Automated pattern detection caught the problem immediately
|
||||||
|
|
||||||
|
### Applicable to Other Servers
|
||||||
|
|
||||||
|
This same issue and solution applies to any Docker-based MCP server:
|
||||||
|
|
||||||
|
- `ghcr.io/github/github-mcp-server`
|
||||||
|
- `ghcr.io/sooperset/mcp-atlassian:latest`
|
||||||
|
- Any custom Docker image serving MCP
|
||||||
|
|
||||||
|
**Template solution**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "any-docker-mcp-server",
|
||||||
|
"command": "docker",
|
||||||
|
"args": ["run", "-i", "--rm", "-e", "ENV_VAR", "image:tag"],
|
||||||
|
"isolation": {
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example 2: API Key Mismatch After Restart
|
||||||
|
|
||||||
|
**Symptom**: API calls return "Invalid or missing API key" after restarting mcpproxy
|
||||||
|
|
||||||
|
### Diagnostic Process
|
||||||
|
|
||||||
|
1. **Check config file API key**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep '"api_key"' ~/.mcpproxy/mcp_config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Result: `"api_key": "d380462e333f25a1c61bad1d5d3f673277d5167dcdba18954effc7d6f0401c37"`
|
||||||
|
|
||||||
|
2. **API call with config key fails**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "X-API-Key: d380462e333f25a1c61bad1d5d3f673277d5167dcdba18954effc7d6f0401c37" \
|
||||||
|
http://127.0.0.1:8080/api/v1/servers
|
||||||
|
```
|
||||||
|
|
||||||
|
Result: `{"success": false, "error": "Invalid or missing API key"}`
|
||||||
|
|
||||||
|
3. **Check logs for API key source**:
|
||||||
|
```bash
|
||||||
|
grep -i "api.*key" ~/Library/Logs/mcpproxy/main.log | tail -10
|
||||||
|
```
|
||||||
|
Result: `API key authentication enabled | {"source": "environment variable", "api_key_prefix": "9fc1****ee1c"}`
|
||||||
|
|
||||||
|
### Root Cause
|
||||||
|
|
||||||
|
The `MCPPROXY_API_KEY` environment variable was set (likely by the tray app), which takes precedence over the config file. The actual API key was `9fc15eb0...` (different from config).
|
||||||
|
|
||||||
|
### Solution
|
||||||
|
|
||||||
|
Use the environment variable key or unset the environment variable:
|
||||||
|
|
||||||
|
**Option 1**: Use the actual key from logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Extract from log prefix
|
||||||
|
grep "api_key_prefix" ~/Library/Logs/mcpproxy/main.log | tail -1
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option 2**: Unset environment variable to use config file
|
||||||
|
|
||||||
|
```bash
|
||||||
|
unset MCPPROXY_API_KEY
|
||||||
|
pkill mcpproxy
|
||||||
|
open /Applications/mcpproxy.app
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Lesson
|
||||||
|
|
||||||
|
Always check both environment variables and config file when debugging API authentication. Environment variables take precedence.
|
||||||
|
|
||||||
|
## Pattern: Missing Package Name in uvx/npx
|
||||||
|
|
||||||
|
**Error**: `unexpected argument '--some-flag' found`
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
// WRONG
|
||||||
|
{"command": "uvx", "args": ["--local-timezone", "America/New_York"]}
|
||||||
|
|
||||||
|
// CORRECT
|
||||||
|
{"command": "uvx", "args": ["mcp-server-time", "--local-timezone", "America/New_York"]}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Diagnostic**: Check stderr in server logs for "unexpected argument" errors, then verify args array has package name first.
|
||||||
|
|
||||||
|
## Pattern: Context Deadline Exceeded
|
||||||
|
|
||||||
|
**Error**: `MCP initialize failed: transport error: context deadline exceeded`
|
||||||
|
|
||||||
|
**Common Causes**:
|
||||||
|
|
||||||
|
1. Missing package name (uvx/npx) - check stderr for "unexpected argument"
|
||||||
|
2. Docker TTY issue - check stderr for "not a TTY"
|
||||||
|
3. Server crashed on startup - check stderr for stack traces
|
||||||
|
4. Environment variables missing - check stderr for "missing" or "required"
|
||||||
|
|
||||||
|
**Diagnostic Workflow**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Check stderr for actual error
|
||||||
|
grep "stderr" ~/Library/Logs/mcpproxy/server-{SERVER_NAME}.log | tail -20
|
||||||
|
|
||||||
|
# 2. Check if server started at all
|
||||||
|
grep "Starting connection" ~/Library/Logs/mcpproxy/server-{SERVER_NAME}.log | tail -5
|
||||||
|
|
||||||
|
# 3. Check Docker container if isolation enabled
|
||||||
|
docker ps | grep mcpproxy
|
||||||
|
docker logs CONTAINER_ID
|
||||||
|
```
|
||||||
458
skills/mcpproxy-debug/references/docker-isolation-guide.md
Normal file
458
skills/mcpproxy-debug/references/docker-isolation-guide.md
Normal file
@@ -0,0 +1,458 @@
|
|||||||
|
# Docker Isolation - Complete Guide
|
||||||
|
|
||||||
|
**When to use this reference:** Docker isolation is enabled and servers are failing, or you see "not a TTY" errors.
|
||||||
|
|
||||||
|
This reference explains how MCPProxy's Docker isolation feature works and how to debug issues with it.
|
||||||
|
|
||||||
|
## What is Docker Isolation?
|
||||||
|
|
||||||
|
MCPProxy can run stdio MCP servers inside Docker containers for security isolation. This prevents untrusted servers from accessing your filesystem or network.
|
||||||
|
|
||||||
|
**Key concept:** MCPProxy wraps the server command in `docker run`, manages the container lifecycle, and handles stdio communication between Claude Code and the containerized server.
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
### Normal Flow (No Isolation)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# User configures:
|
||||||
|
command: "uvx"
|
||||||
|
args: ["mcp-server-sqlite", "--db-path", "/path/to/db"]
|
||||||
|
|
||||||
|
# MCPProxy runs directly:
|
||||||
|
uvx mcp-server-sqlite --db-path /path/to/db
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Docker Isolation Enabled
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# User configures same as above
|
||||||
|
# MCPProxy detects runtime (uvx → Python) and wraps in Docker:
|
||||||
|
|
||||||
|
docker run -i --rm \
|
||||||
|
--memory="512m" \
|
||||||
|
--cpus="1.0" \
|
||||||
|
--network=bridge \
|
||||||
|
python:3.11 \
|
||||||
|
uvx mcp-server-sqlite --db-path /path/to/db
|
||||||
|
```
|
||||||
|
|
||||||
|
**MCPProxy automatically:**
|
||||||
|
1. Detects the runtime from the command (uvx→Python, npx→Node.js)
|
||||||
|
2. Selects appropriate Docker image
|
||||||
|
3. Wraps command in `docker run` with resource limits
|
||||||
|
4. Tracks container lifecycle
|
||||||
|
5. Cleans up container on exit
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Global Docker Isolation Settings
|
||||||
|
|
||||||
|
Located in `~/.mcpproxy/mcp_config.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"docker_isolation": {
|
||||||
|
"enabled": true, // Master switch
|
||||||
|
"memory_limit": "512m", // Per-container memory limit
|
||||||
|
"cpu_limit": "1.0", // CPU cores (1.0 = 1 core)
|
||||||
|
"timeout": "30s", // Container startup timeout
|
||||||
|
"network_mode": "bridge", // Docker network mode
|
||||||
|
"default_images": {
|
||||||
|
"uvx": "python:3.11", // Image for uvx commands
|
||||||
|
"npx": "node:20", // Image for npx commands
|
||||||
|
"python": "python:3.11", // Image for python commands
|
||||||
|
"node": "node:20" // Image for node commands
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Per-Server Isolation Override
|
||||||
|
|
||||||
|
You can override isolation settings for specific servers:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "my-server",
|
||||||
|
"command": "uvx",
|
||||||
|
"args": ["mcp-server-name"],
|
||||||
|
"isolation": {
|
||||||
|
"enabled": false, // Disable isolation for this server
|
||||||
|
// OR customize settings:
|
||||||
|
"enabled": true,
|
||||||
|
"image": "python:3.12-slim", // Custom image
|
||||||
|
"memory_limit": "1g", // More memory
|
||||||
|
"cpu_limit": "2.0", // More CPU
|
||||||
|
"network_mode": "none", // No network access
|
||||||
|
"working_dir": "/workspace" // Working directory in container
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Checking Docker Isolation Status
|
||||||
|
|
||||||
|
### Is Isolation Enabled for a Server?
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check server log for isolation setup
|
||||||
|
grep -i "docker isolation enabled\|docker isolation setup" \
|
||||||
|
~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -5
|
||||||
|
|
||||||
|
# Look for:
|
||||||
|
# "Docker isolation enabled for server: SERVER_NAME"
|
||||||
|
# "Docker isolation setup successful"
|
||||||
|
```
|
||||||
|
|
||||||
|
### View the Docker Command Being Used
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# See actual docker run command
|
||||||
|
grep "docker_run_args" ~/Library/Logs/mcpproxy/main.log | tail -3
|
||||||
|
|
||||||
|
# Example output:
|
||||||
|
# docker_run_args: ["run", "-i", "--rm", "--memory=512m", "--cpus=1.0",
|
||||||
|
# "--network=bridge", "python:3.11", "uvx", "mcp-server-name"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### List Running Containers
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List all MCPProxy containers
|
||||||
|
docker ps | grep mcpproxy
|
||||||
|
|
||||||
|
# Check specific server container
|
||||||
|
docker ps | grep mcpproxy-SERVER_NAME
|
||||||
|
|
||||||
|
# Check container health
|
||||||
|
docker inspect --format='{{.State.Status}}' CONTAINER_ID
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Container Logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# If container is running
|
||||||
|
docker logs CONTAINER_NAME
|
||||||
|
|
||||||
|
# If container exited, find it in history
|
||||||
|
docker ps -a | grep mcpproxy-SERVER_NAME
|
||||||
|
docker logs CONTAINER_ID
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Docker Isolation Issues
|
||||||
|
|
||||||
|
### Issue 1: Docker-in-Docker (Most Common)
|
||||||
|
|
||||||
|
**Symptom:** "the input device is not a TTY" error for Docker-based servers
|
||||||
|
|
||||||
|
**Root cause:** MCPProxy's Docker isolation wraps a Docker command in Docker, creating nested containers.
|
||||||
|
|
||||||
|
```json
|
||||||
|
// Server config uses Docker directly
|
||||||
|
{
|
||||||
|
"command": "docker",
|
||||||
|
"args": ["run", "-i", "--rm", "mcp-image:latest"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MCPProxy wraps it in another Docker (if isolation enabled):
|
||||||
|
docker run ... python:3.11 docker run -i --rm mcp-image:latest
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
This fails: Docker-in-Docker
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution:** Explicitly disable isolation for Docker-based servers:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "docker-server",
|
||||||
|
"command": "docker",
|
||||||
|
"args": ["run", "-i", "--rm", "mcp-image:latest"],
|
||||||
|
"isolation": {
|
||||||
|
"enabled": false // CRITICAL: Always disable for Docker commands
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why explicit is better:** MCPProxy should auto-detect Docker commands and skip isolation, but explicit configuration is more reliable and prevents edge cases.
|
||||||
|
|
||||||
|
### Issue 2: File Access Problems
|
||||||
|
|
||||||
|
**Symptom:** Server can't access local files, permission errors
|
||||||
|
|
||||||
|
**Root cause:** Container can't access host filesystem by default.
|
||||||
|
|
||||||
|
**Solution 1: Disable isolation** (if file access is required):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"isolation": {
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution 2: Mount volumes** (more secure):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"isolation": {
|
||||||
|
"enabled": true,
|
||||||
|
"volumes": [
|
||||||
|
"/host/path:/container/path:ro" // Read-only mount
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** Volume mounting requires MCPProxy support. Check documentation for current status.
|
||||||
|
|
||||||
|
### Issue 3: Network Access Blocked
|
||||||
|
|
||||||
|
**Symptom:** Server can't make API calls, network timeouts
|
||||||
|
|
||||||
|
**Root cause:** Container network mode blocks external access.
|
||||||
|
|
||||||
|
**Check current network mode:**
|
||||||
|
```bash
|
||||||
|
grep "network_mode" ~/.mcpproxy/mcp_config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution:** Change network mode:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"isolation": {
|
||||||
|
"enabled": true,
|
||||||
|
"network_mode": "bridge" // Default, allows external access
|
||||||
|
// OR
|
||||||
|
"network_mode": "host" // Full host network access
|
||||||
|
// OR
|
||||||
|
"network_mode": "none" // No network (most secure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue 4: Container Image Not Found
|
||||||
|
|
||||||
|
**Symptom:** "context deadline exceeded", stderr shows "pull" or "not found"
|
||||||
|
|
||||||
|
**Investigation:**
|
||||||
|
```bash
|
||||||
|
# Check stderr for pull errors
|
||||||
|
grep -i "pull\|not found" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
|
||||||
|
|
||||||
|
# Try pulling image manually
|
||||||
|
docker pull python:3.11
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution:** Ensure Docker image exists locally or can be pulled:
|
||||||
|
```bash
|
||||||
|
# Pull image before starting mcpproxy
|
||||||
|
docker pull python:3.11
|
||||||
|
|
||||||
|
# Or specify a custom image that exists
|
||||||
|
{
|
||||||
|
"isolation": {
|
||||||
|
"image": "python:3.12-slim" // Different image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue 5: Resource Limits Too Low
|
||||||
|
|
||||||
|
**Symptom:** Container exits unexpectedly, out of memory errors
|
||||||
|
|
||||||
|
**Investigation:**
|
||||||
|
```bash
|
||||||
|
# Check container exit status
|
||||||
|
docker ps -a | grep mcpproxy-SERVER_NAME
|
||||||
|
|
||||||
|
# Look for OOMKilled status
|
||||||
|
docker inspect CONTAINER_ID | grep -i "oom"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution:** Increase resource limits:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"isolation": {
|
||||||
|
"enabled": true,
|
||||||
|
"memory_limit": "2g", // Increase from default 512m
|
||||||
|
"cpu_limit": "2.0" // Increase from default 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker Context Support (Colima)
|
||||||
|
|
||||||
|
MCPProxy uses your system's default Docker context. For Colima users:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check current Docker context
|
||||||
|
docker context show
|
||||||
|
|
||||||
|
# List all contexts
|
||||||
|
docker context ls
|
||||||
|
|
||||||
|
# Example output:
|
||||||
|
# NAME DESCRIPTION DOCKER ENDPOINT
|
||||||
|
# default Current DOCKER_HOST based configuration unix:///var/run/docker.sock
|
||||||
|
# colima * colima unix:///Users/.../.colima/docker.sock
|
||||||
|
|
||||||
|
# MCPProxy uses whichever has the asterisk (*)
|
||||||
|
```
|
||||||
|
|
||||||
|
**To change context:**
|
||||||
|
```bash
|
||||||
|
docker context use colima
|
||||||
|
docker context use default
|
||||||
|
```
|
||||||
|
|
||||||
|
**After changing context:** Restart mcpproxy for changes to take effect.
|
||||||
|
|
||||||
|
## Disabling Docker Isolation
|
||||||
|
|
||||||
|
### Globally (All Servers)
|
||||||
|
|
||||||
|
Edit `~/.mcpproxy/mcp_config.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"docker_isolation": {
|
||||||
|
"enabled": false // Disables for all servers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Per-Server (Recommended)
|
||||||
|
|
||||||
|
Only disable for servers that need it:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "special-server",
|
||||||
|
"command": "docker", // Or any command that needs no isolation
|
||||||
|
"args": ["..."],
|
||||||
|
"isolation": {
|
||||||
|
"enabled": false // Only this server runs without isolation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Best practice:** Keep isolation enabled globally, disable per-server only when necessary.
|
||||||
|
|
||||||
|
## Debugging Workflow for Docker Isolation Issues
|
||||||
|
|
||||||
|
Use this systematic workflow:
|
||||||
|
|
||||||
|
### Step 1: Verify Isolation Status
|
||||||
|
```bash
|
||||||
|
# Is isolation enabled for this server?
|
||||||
|
grep -i "docker isolation" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -5
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Check Container Status
|
||||||
|
```bash
|
||||||
|
# Is container running?
|
||||||
|
docker ps | grep mcpproxy-SERVER_NAME
|
||||||
|
|
||||||
|
# Did container exit?
|
||||||
|
docker ps -a | grep mcpproxy-SERVER_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: View Container Logs
|
||||||
|
```bash
|
||||||
|
# Get container ID
|
||||||
|
CONTAINER_ID=$(docker ps -a | grep mcpproxy-SERVER_NAME | awk '{print $1}')
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker logs $CONTAINER_ID
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Check Docker Command
|
||||||
|
```bash
|
||||||
|
# See exact command being used
|
||||||
|
grep "docker_run_args" ~/Library/Logs/mcpproxy/main.log | tail -3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 5: Test Manually
|
||||||
|
```bash
|
||||||
|
# Extract docker run command from logs and test it manually
|
||||||
|
# Example:
|
||||||
|
docker run -i --rm --memory=512m --cpus=1.0 python:3.11 uvx mcp-server-name
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 6: Apply Fix
|
||||||
|
|
||||||
|
Based on the issue:
|
||||||
|
- Docker-in-Docker → Disable isolation
|
||||||
|
- File access → Disable isolation or mount volumes
|
||||||
|
- Network issues → Change network_mode
|
||||||
|
- Resource limits → Increase memory/CPU
|
||||||
|
- Image not found → Pull image or change image
|
||||||
|
|
||||||
|
### Step 7: Reload and Verify
|
||||||
|
```bash
|
||||||
|
# Trigger config reload
|
||||||
|
touch ~/.mcpproxy/mcp_config.json
|
||||||
|
|
||||||
|
# Or restart
|
||||||
|
pkill mcpproxy && open /Applications/mcpproxy.app
|
||||||
|
|
||||||
|
# Verify fix
|
||||||
|
curl -s "http://127.0.0.1:8080/api/v1/servers?apikey=YOUR_KEY" | \
|
||||||
|
python3 -m json.tool | grep -A 10 '"name": "SERVER_NAME"'
|
||||||
|
```
|
||||||
|
|
||||||
|
## When to Use Docker Isolation
|
||||||
|
|
||||||
|
### ✅ Use Docker Isolation When:
|
||||||
|
- Running untrusted third-party MCP servers
|
||||||
|
- Need security boundaries between servers
|
||||||
|
- Want resource limits per server
|
||||||
|
- Servers don't need host filesystem access
|
||||||
|
- Servers don't need special network configurations
|
||||||
|
|
||||||
|
### ❌ Disable Docker Isolation When:
|
||||||
|
- Server needs to access local files
|
||||||
|
- Server command is already Docker (prevents Docker-in-Docker)
|
||||||
|
- Server needs host network access
|
||||||
|
- Troubleshooting connection issues
|
||||||
|
- Performance is critical (isolation adds overhead)
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
### Check if isolation is active
|
||||||
|
```bash
|
||||||
|
grep -i "docker isolation" ~/Library/Logs/mcpproxy/server-NAME.log | tail -5
|
||||||
|
```
|
||||||
|
|
||||||
|
### List containers
|
||||||
|
```bash
|
||||||
|
docker ps | grep mcpproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
### View container logs
|
||||||
|
```bash
|
||||||
|
docker logs CONTAINER_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disable isolation for server
|
||||||
|
```json
|
||||||
|
{"isolation": {"enabled": false}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Increase resources
|
||||||
|
```json
|
||||||
|
{"isolation": {"memory_limit": "2g", "cpu_limit": "2.0"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Change network mode
|
||||||
|
```json
|
||||||
|
{"isolation": {"network_mode": "host"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use custom image
|
||||||
|
```json
|
||||||
|
{"isolation": {"image": "python:3.12-slim"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Docker isolation provides security but adds complexity. Start with isolation disabled while getting servers working, then enable it once everything is stable. Always disable isolation explicitly for Docker-based servers to prevent Docker-in-Docker issues.
|
||||||
89
skills/mcpproxy-debug/scripts/check_status.sh
Executable file
89
skills/mcpproxy-debug/scripts/check_status.sh
Executable file
@@ -0,0 +1,89 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Quick health check for MCPProxy
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo "=== MCPProxy Health Check ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Check if mcpproxy is running
|
||||||
|
echo -n "Process Status: "
|
||||||
|
if pgrep -x "mcpproxy" > /dev/null; then
|
||||||
|
echo -e "${GREEN}Running${NC}"
|
||||||
|
ps aux | grep mcpproxy | grep -v grep | head -1
|
||||||
|
else
|
||||||
|
echo -e "${RED}Not Running${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Get API key from config
|
||||||
|
API_KEY=$(grep '"api_key"' ~/.mcpproxy/mcp_config.json 2> /dev/null | cut -d'"' -f4 || echo "")
|
||||||
|
|
||||||
|
if [ -z "$API_KEY" ]; then
|
||||||
|
echo -e "${YELLOW}Warning: No API key found in config${NC}"
|
||||||
|
echo "Checking logs for auto-generated key..."
|
||||||
|
API_KEY=$(grep "api_key_prefix" ~/Library/Logs/mcpproxy/main.log 2> /dev/null | tail -1 | grep -o '"api_key":"[^"]*"' | cut -d'"' -f4 || echo "")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$API_KEY" ]; then
|
||||||
|
echo -e "${RED}Error: Could not find API key${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check server status via API
|
||||||
|
echo "Server Status:"
|
||||||
|
RESPONSE=$(curl -s -H "X-API-Key: $API_KEY" "http://127.0.0.1:8080/api/v1/servers" 2> /dev/null)
|
||||||
|
|
||||||
|
if [ -z "$RESPONSE" ]; then
|
||||||
|
echo -e "${RED}Error: API returned empty response${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$RESPONSE" | python3 -c "import sys, json; data=json.load(sys.stdin); exit(0 if data.get('success', False) else 1)" 2> /dev/null; then
|
||||||
|
# Parse and display server info
|
||||||
|
echo "$RESPONSE" | python3 << 'PYTHON'
|
||||||
|
import sys, json
|
||||||
|
|
||||||
|
data = json.load(sys.stdin)
|
||||||
|
stats = data.get('data', {}).get('stats', {})
|
||||||
|
servers = data.get('data', {}).get('servers', [])
|
||||||
|
|
||||||
|
print(f" Total Servers: {stats.get('total_servers', 0)}")
|
||||||
|
print(f" Connected: {stats.get('connected_servers', 0)}")
|
||||||
|
print(f" Quarantined: {stats.get('quarantined_servers', 0)}")
|
||||||
|
print(f" Total Tools: {stats.get('total_tools', 0)}")
|
||||||
|
print(f" Docker Containers: {stats.get('docker_containers', 0)}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
if servers:
|
||||||
|
print("Servers:")
|
||||||
|
for server in servers:
|
||||||
|
status_icon = "✓" if server.get('connected') else "✗"
|
||||||
|
name = server.get('name', 'unknown')
|
||||||
|
status = server.get('status', 'unknown')
|
||||||
|
tools = server.get('tool_count', 0)
|
||||||
|
error = server.get('last_error', '')
|
||||||
|
|
||||||
|
print(f" {status_icon} {name}: {status} ({tools} tools)")
|
||||||
|
if error:
|
||||||
|
print(f" Error: {error}")
|
||||||
|
PYTHON
|
||||||
|
else
|
||||||
|
echo -e "${RED}API Error:${NC}"
|
||||||
|
echo "$RESPONSE" | python3 -m json.tool 2> /dev/null || echo "$RESPONSE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Docker Containers ==="
|
||||||
|
docker ps --filter "name=mcpproxy" --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" || echo "None running"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo -e "${GREEN}Health check complete!${NC}"
|
||||||
74
skills/mcpproxy-debug/scripts/compare_servers.sh
Executable file
74
skills/mcpproxy-debug/scripts/compare_servers.sh
Executable file
@@ -0,0 +1,74 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Compare two servers (one working, one broken) to find differences
|
||||||
|
# Usage: ./compare_servers.sh <working-server> <broken-server>
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [ $# -lt 2 ]; then
|
||||||
|
echo "Usage: $0 <working-server-name> <broken-server-name>"
|
||||||
|
echo "Example: $0 glean buildkite"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
WORKING="$1"
|
||||||
|
BROKEN="$2"
|
||||||
|
|
||||||
|
echo "=== Comparing Servers: $WORKING (working) vs $BROKEN (broken) ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Compare configurations
|
||||||
|
echo "Configuration Comparison"
|
||||||
|
echo "------------------------"
|
||||||
|
echo "Working server ($WORKING):"
|
||||||
|
grep -A 15 "\"name\": \"$WORKING\"" ~/.mcpproxy/mcp_config.json | head -20
|
||||||
|
echo
|
||||||
|
echo "Broken server ($BROKEN):"
|
||||||
|
grep -A 15 "\"name\": \"$BROKEN\"" ~/.mcpproxy/mcp_config.json | head -20
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Compare connection status
|
||||||
|
echo "Connection Status Comparison"
|
||||||
|
echo "----------------------------"
|
||||||
|
echo "$WORKING status:"
|
||||||
|
grep "$WORKING" ~/Library/Logs/mcpproxy/main.log 2> /dev/null | grep -i "connected\|initialized" | tail -3 \
|
||||||
|
|| grep "$WORKING" ~/.mcpproxy/logs/main.log 2> /dev/null | grep -i "connected\|initialized" | tail -3 \
|
||||||
|
|| echo "No connection info found"
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "$BROKEN status:"
|
||||||
|
grep "$BROKEN" ~/Library/Logs/mcpproxy/main.log 2> /dev/null | grep -i "error\|failed" | tail -3 \
|
||||||
|
|| grep "$BROKEN" ~/.mcpproxy/logs/main.log 2> /dev/null | grep -i "error\|failed" | tail -3 \
|
||||||
|
|| echo "No error info found"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Compare stderr output
|
||||||
|
echo "Stderr Comparison"
|
||||||
|
echo "-----------------"
|
||||||
|
echo "$WORKING recent stderr:"
|
||||||
|
grep "stderr" ~/Library/Logs/mcpproxy/server-${WORKING}.log 2> /dev/null | tail -5 \
|
||||||
|
|| grep "stderr" ~/.mcpproxy/logs/server-${WORKING}.log 2> /dev/null | tail -5 \
|
||||||
|
|| echo "No stderr output"
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "$BROKEN recent stderr:"
|
||||||
|
grep "stderr" ~/Library/Logs/mcpproxy/server-${BROKEN}.log 2> /dev/null | tail -5 \
|
||||||
|
|| grep "stderr" ~/.mcpproxy/logs/server-${BROKEN}.log 2> /dev/null | tail -5 \
|
||||||
|
|| echo "No stderr output"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Docker container comparison
|
||||||
|
echo "Docker Container Comparison"
|
||||||
|
echo "---------------------------"
|
||||||
|
echo "$WORKING container:"
|
||||||
|
docker ps --filter "name=mcpproxy-${WORKING}" --format "{{.Names}}\t{{.Status}}\t{{.Image}}" 2> /dev/null || echo "No container"
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "$BROKEN container:"
|
||||||
|
docker ps --filter "name=mcpproxy-${BROKEN}" --format "{{.Names}}\t{{.Status}}\t{{.Image}}" 2> /dev/null || echo "No container"
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "=== Key Differences to Investigate ==="
|
||||||
|
echo "1. Check 'command' and 'args' differences in config"
|
||||||
|
echo "2. Check if error patterns match known issues (uvx package, Docker -it)"
|
||||||
|
echo "3. Check stderr for specific error messages"
|
||||||
|
echo "4. Check if Docker container is running for working but not broken"
|
||||||
93
skills/mcpproxy-debug/scripts/diagnose_server.sh
Executable file
93
skills/mcpproxy-debug/scripts/diagnose_server.sh
Executable file
@@ -0,0 +1,93 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Diagnose a specific MCP server following mcpproxy-debug skill workflow
|
||||||
|
# Usage: ./diagnose_server.sh <server-name>
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [ $# -lt 1 ]; then
|
||||||
|
echo "Usage: $0 <server-name>"
|
||||||
|
echo "Example: $0 buildkite"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SERVER_NAME="$1"
|
||||||
|
LOG_FILE="$HOME/Library/Logs/mcpproxy/server-${SERVER_NAME}.log"
|
||||||
|
|
||||||
|
# Check if log file exists (Linux fallback)
|
||||||
|
if [ ! -f "$LOG_FILE" ]; then
|
||||||
|
LOG_FILE="$HOME/.mcpproxy/logs/server-${SERVER_NAME}.log"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$LOG_FILE" ]; then
|
||||||
|
echo "Error: Log file not found for server '$SERVER_NAME'"
|
||||||
|
echo "Checked locations:"
|
||||||
|
echo " - ~/Library/Logs/mcpproxy/server-${SERVER_NAME}.log (macOS)"
|
||||||
|
echo " - ~/.mcpproxy/logs/server-${SERVER_NAME}.log (Linux)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== MCPProxy Server Diagnosis: $SERVER_NAME ==="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Step 1: Check connection status
|
||||||
|
echo "Step 1: Connection Status"
|
||||||
|
echo "-------------------------"
|
||||||
|
grep -i "connected\|error\|failed" ~/Library/Logs/mcpproxy/main.log 2> /dev/null | grep "$SERVER_NAME" | tail -5 \
|
||||||
|
|| grep -i "connected\|error\|failed" ~/.mcpproxy/logs/main.log 2> /dev/null | grep "$SERVER_NAME" | tail -5 \
|
||||||
|
|| echo "No recent connection status found in main log"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Step 2: Check for stderr messages (most revealing)
|
||||||
|
echo "Step 2: Recent stderr output (shows actual errors)"
|
||||||
|
echo "---------------------------------------------------"
|
||||||
|
grep "stderr" "$LOG_FILE" | tail -10
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Step 3: Check for error patterns
|
||||||
|
echo "Step 3: Error Pattern Detection"
|
||||||
|
echo "--------------------------------"
|
||||||
|
|
||||||
|
if grep -q "unexpected argument" "$LOG_FILE"; then
|
||||||
|
echo "❌ Found: 'unexpected argument' error"
|
||||||
|
echo " → Likely cause: Missing package name for uvx/npx"
|
||||||
|
echo " → Fix: Add package name as first argument"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -q "not a TTY" "$LOG_FILE"; then
|
||||||
|
echo "❌ Found: 'input device is not a TTY' error"
|
||||||
|
echo " → Likely cause: Using -it flag with Docker"
|
||||||
|
echo " → Fix: Use -i only (not -it) for stdin pipe"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -q "context deadline exceeded" "$LOG_FILE"; then
|
||||||
|
echo "❌ Found: 'context deadline exceeded' error"
|
||||||
|
echo " → Likely cause: Server failed to initialize within timeout"
|
||||||
|
echo " → Check stderr above for specific reason"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -q "Successfully connected" "$LOG_FILE"; then
|
||||||
|
echo "✅ Server successfully connected at some point"
|
||||||
|
grep "Successfully connected" "$LOG_FILE" | tail -1
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 4: Check Docker container status
|
||||||
|
echo "Step 4: Docker Container Status"
|
||||||
|
echo "--------------------------------"
|
||||||
|
if docker ps --filter "name=mcpproxy-${SERVER_NAME}" --format "{{.Names}}\t{{.Status}}\t{{.Image}}" 2> /dev/null | grep -q .; then
|
||||||
|
docker ps --filter "name=mcpproxy-${SERVER_NAME}" --format "{{.Names}}\t{{.Status}}\t{{.Image}}"
|
||||||
|
else
|
||||||
|
echo "No running Docker container found for $SERVER_NAME"
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Step 5: Recent log activity
|
||||||
|
echo "Step 5: Last 10 log entries"
|
||||||
|
echo "----------------------------"
|
||||||
|
tail -10 "$LOG_FILE"
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "=== End of Diagnosis ==="
|
||||||
24
skills/mcpproxy-debug/scripts/get_api_key.sh
Executable file
24
skills/mcpproxy-debug/scripts/get_api_key.sh
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Extract current API key from config or logs
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Try config file first
|
||||||
|
API_KEY=$(grep '"api_key"' ~/.mcpproxy/mcp_config.json 2> /dev/null | cut -d'"' -f4 || echo "")
|
||||||
|
|
||||||
|
if [ -n "$API_KEY" ]; then
|
||||||
|
echo "$API_KEY"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try logs for auto-generated key
|
||||||
|
echo "No API key in config, checking logs..." >&2
|
||||||
|
API_KEY=$(grep -i "api key" ~/Library/Logs/mcpproxy/main.log 2> /dev/null | grep -o '"api_key":"[^"]*"' | tail -1 | cut -d'"' -f4 || echo "")
|
||||||
|
|
||||||
|
if [ -n "$API_KEY" ]; then
|
||||||
|
echo "$API_KEY"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Error: Could not find API key" >&2
|
||||||
|
exit 1
|
||||||
643
skills/scope/SKILL.md
Normal file
643
skills/scope/SKILL.md
Normal file
@@ -0,0 +1,643 @@
|
|||||||
|
---
|
||||||
|
name: scope
|
||||||
|
description: Guide for working with Scope, a developer environment management tool that automates environment checks, detects known errors, and provides automated fixes. Use when creating Scope configurations (ScopeKnownError, ScopeDoctorGroup, ScopeReportLocation), debugging environment issues, or writing rules for error detection and remediation.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Scope - Developer Environment Management
|
||||||
|
|
||||||
|
Scope is a DevEx tool that helps maintain consistent development environments through automated checks, error detection, and fixes.
|
||||||
|
|
||||||
|
## Core Commands
|
||||||
|
|
||||||
|
### scope doctor
|
||||||
|
|
||||||
|
Run automated environment checks and fixes.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scope doctor run # Run all checks
|
||||||
|
scope doctor run --only group-name # Run specific group
|
||||||
|
scope doctor run --fix=false # Check only, no fixes
|
||||||
|
scope doctor run --no-cache # Disable caching
|
||||||
|
scope doctor list # List available checks
|
||||||
|
```
|
||||||
|
|
||||||
|
### scope analyze
|
||||||
|
|
||||||
|
Detect known errors in logs or command output.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scope analyze logs file.log # Analyze log file
|
||||||
|
scope analyze command -- cmd args # Analyze command output
|
||||||
|
scope analyze --extra-config path file.log # Use additional configs
|
||||||
|
```
|
||||||
|
|
||||||
|
### scope report
|
||||||
|
|
||||||
|
Generate bug reports with automatic secret redaction.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scope report ./script.sh # Run and report on script
|
||||||
|
scope report -- command args # Run and report on command
|
||||||
|
```
|
||||||
|
|
||||||
|
### scope-intercept
|
||||||
|
|
||||||
|
Monitor script execution in real-time (used as shebang).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env scope-intercept bash
|
||||||
|
# Script content here
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Resources
|
||||||
|
|
||||||
|
All Scope configurations use Kubernetes-inspired YAML format:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: <ResourceType>
|
||||||
|
metadata:
|
||||||
|
name: unique-identifier
|
||||||
|
description: Human-readable description
|
||||||
|
spec:
|
||||||
|
# Resource-specific configuration
|
||||||
|
```
|
||||||
|
|
||||||
|
## ScopeKnownError
|
||||||
|
|
||||||
|
Define error patterns and provide automated help/fixes for common issues.
|
||||||
|
|
||||||
|
### Structure
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeKnownError
|
||||||
|
metadata:
|
||||||
|
name: descriptive-error-name
|
||||||
|
description: Brief description of what this error means
|
||||||
|
spec:
|
||||||
|
pattern: 'regex pattern to match the error'
|
||||||
|
help: |
|
||||||
|
Clear explanation of the issue.
|
||||||
|
|
||||||
|
Steps to resolve:
|
||||||
|
1. First step
|
||||||
|
2. Second step
|
||||||
|
3. Where to get help if needed
|
||||||
|
fix:
|
||||||
|
prompt:
|
||||||
|
text: User-friendly prompt asking permission
|
||||||
|
commands:
|
||||||
|
- first-fix-command
|
||||||
|
- second-fix-command
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Fields
|
||||||
|
|
||||||
|
- **pattern**: Regex pattern using ripgrep syntax to match error text
|
||||||
|
- **help**: Multi-line markdown explanation with resolution steps
|
||||||
|
- **fix** (optional): Automated remediation configuration
|
||||||
|
- **commands**: List of commands to run to fix the error (required)
|
||||||
|
- **helpText**: Descriptive text shown if the fix fails (optional)
|
||||||
|
- **helpUrl**: Documentation link for manual resolution (optional)
|
||||||
|
- **prompt**: User approval before running fix (optional)
|
||||||
|
- **text**: Question asking for user permission
|
||||||
|
- **extraContext**: Additional context about what the fix does and why approval is needed
|
||||||
|
|
||||||
|
**Note:** When a fix is defined, it only runs when the error pattern is detected. The fix is optional - if not provided, only the help text is shown.
|
||||||
|
|
||||||
|
### File Organization
|
||||||
|
|
||||||
|
Place files in categorized directories:
|
||||||
|
|
||||||
|
- Location: `{config-root}/known-errors/{category}/{error-name}.yaml`
|
||||||
|
- Common categories: `docker/`, `ruby/`, `git/`, `github/`, `mysql/`, `postgres/`, `dns/`, `aws/`
|
||||||
|
- Include matching `.txt` file with example error for testing
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeKnownError
|
||||||
|
metadata:
|
||||||
|
name: gem-missing-file
|
||||||
|
description: A gem source file is missing and fails to be loaded
|
||||||
|
spec:
|
||||||
|
pattern: "/lib/ruby/.* `require': cannot load such file --.*/lib/ruby/gems/.*(LoadError)"
|
||||||
|
help: |
|
||||||
|
A gem source file is missing. The solution is to reinstall the gems:
|
||||||
|
1. Run `bundle pristine`
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- bundle pristine
|
||||||
|
helpText: |
|
||||||
|
Bundle pristine failed. Try these steps:
|
||||||
|
1. Check your bundle config: bundle config list
|
||||||
|
2. Reinstall bundler: gem install bundler
|
||||||
|
3. Contact #help-ruby if issues persist
|
||||||
|
helpUrl: https://bundler.io/man/bundle-pristine.1.html
|
||||||
|
prompt:
|
||||||
|
text: |-
|
||||||
|
This will reinstall all gems from your Gemfile.
|
||||||
|
Do you wish to continue?
|
||||||
|
extraContext: >-
|
||||||
|
bundle pristine reinstalls gems without changing versions,
|
||||||
|
which resolves missing file errors but preserves your lock file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Validation
|
||||||
|
|
||||||
|
Validate schema structure:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install sourcemeta/apps/jsonschema
|
||||||
|
jsonschema validate schema.json known-error.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Test pattern matching:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scope analyze logs --extra-config config-dir/ test-error.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Additional Resources
|
||||||
|
|
||||||
|
- [Complete example with fix and prompt](https://github.com/oscope-dev/scope/blob/main/examples/.scope/known-error-with-fix.yaml)
|
||||||
|
- [ScopeKnownError model documentation](https://oscope-dev.github.io/scope/docs/models/ScopeKnownError)
|
||||||
|
|
||||||
|
### Pattern Writing Tips
|
||||||
|
|
||||||
|
- Use ripgrep regex syntax (similar to PCRE)
|
||||||
|
- Escape special characters: `\\.`, `\\[`, `\\(`
|
||||||
|
- Use character classes: `[[:digit:]]`, `[[:alpha:]]`
|
||||||
|
- Test patterns: `echo "error text" | rg "pattern"`
|
||||||
|
- Balance specificity vs flexibility to avoid false positives/negatives
|
||||||
|
|
||||||
|
## ScopeDoctorGroup
|
||||||
|
|
||||||
|
Define environment setup and maintenance checks with automated fixes.
|
||||||
|
|
||||||
|
### Structure
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeDoctorGroup
|
||||||
|
metadata:
|
||||||
|
name: tool-name
|
||||||
|
description: Brief description of what this group does
|
||||||
|
spec:
|
||||||
|
include: when-required # or "by-default"
|
||||||
|
needs:
|
||||||
|
- dependency-1
|
||||||
|
- dependency-2
|
||||||
|
actions:
|
||||||
|
- name: action-name
|
||||||
|
description: What this action checks/fixes
|
||||||
|
required: true # default true; false for optional checks
|
||||||
|
check:
|
||||||
|
paths:
|
||||||
|
- 'file-to-watch.txt'
|
||||||
|
- '**/*.config'
|
||||||
|
commands:
|
||||||
|
- ./bin/check-script.sh
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./bin/fix-script.sh
|
||||||
|
helpText: |
|
||||||
|
What to do if the fix fails.
|
||||||
|
Where to get help.
|
||||||
|
helpUrl: https://docs.example.com/help
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Components
|
||||||
|
|
||||||
|
#### Include Modes
|
||||||
|
|
||||||
|
- **by-default**: Always runs with `scope doctor run`
|
||||||
|
- **when-required**: Only runs when explicitly specified or as dependency
|
||||||
|
|
||||||
|
#### Dependencies
|
||||||
|
|
||||||
|
List other groups that must run first in `needs` array. Creates execution graph.
|
||||||
|
|
||||||
|
#### Actions
|
||||||
|
|
||||||
|
Each action is an atomic check/fix operation that runs in order.
|
||||||
|
|
||||||
|
#### Check Logic
|
||||||
|
|
||||||
|
Determines if fix should run. Fix runs when:
|
||||||
|
|
||||||
|
- **No check defined**: Fix always runs
|
||||||
|
- **paths specified**: Any file changed or no files match globs
|
||||||
|
- **commands specified**: Any command exits non-zero
|
||||||
|
- Both can be combined (OR logic)
|
||||||
|
|
||||||
|
#### Fix Section
|
||||||
|
|
||||||
|
- **commands**: List of commands to run in order
|
||||||
|
- **helpText**: Shown if fix fails (markdown supported)
|
||||||
|
- **helpUrl**: Optional link to documentation
|
||||||
|
|
||||||
|
### File Organization
|
||||||
|
|
||||||
|
- Application setup: `{config-root}/application/`
|
||||||
|
- Environment setup: `{config-root}/environment/`
|
||||||
|
- Project-specific: `.scope/` in project root
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeDoctorGroup
|
||||||
|
metadata:
|
||||||
|
name: ruby-version
|
||||||
|
description: Set up Ruby with correct version
|
||||||
|
spec:
|
||||||
|
include: when-required
|
||||||
|
needs:
|
||||||
|
- ruby-manager
|
||||||
|
- homebrew
|
||||||
|
actions:
|
||||||
|
- name: install
|
||||||
|
description: Ensures correct version of ruby is installed
|
||||||
|
check:
|
||||||
|
paths:
|
||||||
|
- '{{ working_dir }}/.ruby-version'
|
||||||
|
commands:
|
||||||
|
- ./bin/ruby-version.sh verify
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./bin/ruby-version.sh install
|
||||||
|
helpText: |
|
||||||
|
Ruby installation failed.
|
||||||
|
Contact: #help-channel
|
||||||
|
```
|
||||||
|
|
||||||
|
### Script Conventions
|
||||||
|
|
||||||
|
Create helper scripts following check/fix pattern:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ACTION="${1:-check}"
|
||||||
|
|
||||||
|
check() {
|
||||||
|
# Return 0 if check passes, non-zero if fix needed
|
||||||
|
if [[ condition ]]; then
|
||||||
|
echo "Check passed"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "Check failed" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
fix() {
|
||||||
|
# Perform fix, return 0 on success
|
||||||
|
echo "Running fix..."
|
||||||
|
# Fix commands here
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$ACTION" in
|
||||||
|
check) check ;;
|
||||||
|
fix) fix ;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 [check|fix]" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
```
|
||||||
|
|
||||||
|
Script best practices:
|
||||||
|
|
||||||
|
- Use relative paths with `./` prefix (relative to YAML file)
|
||||||
|
- Make scripts idempotent (safe to run multiple times)
|
||||||
|
- Exit 0 for success, non-zero for failure
|
||||||
|
- Write errors to stderr
|
||||||
|
- Include helpful error messages
|
||||||
|
|
||||||
|
### Caching
|
||||||
|
|
||||||
|
File-based checks use content hashing:
|
||||||
|
|
||||||
|
- Cache stores file path and content hash
|
||||||
|
- Only re-runs if file contents change
|
||||||
|
- Persists between runs
|
||||||
|
- Disable with `--no-cache`
|
||||||
|
|
||||||
|
## ScopeReportLocation
|
||||||
|
|
||||||
|
Configure where and how bug reports are uploaded.
|
||||||
|
|
||||||
|
### GitHub Issues
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeReportLocation
|
||||||
|
metadata:
|
||||||
|
name: github
|
||||||
|
spec:
|
||||||
|
destination:
|
||||||
|
githubIssue:
|
||||||
|
owner: org-name
|
||||||
|
repo: repo-name
|
||||||
|
additionalData:
|
||||||
|
nodeVersion: node -v
|
||||||
|
rubyPath: which ruby
|
||||||
|
```
|
||||||
|
|
||||||
|
Authentication via environment variables:
|
||||||
|
|
||||||
|
- GitHub App: `SCOPE_GH_APP_ID` + `SCOPE_GH_APP_KEY`
|
||||||
|
- Personal Access Token: `SCOPE_GH_TOKEN`
|
||||||
|
|
||||||
|
### Local File System
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeReportLocation
|
||||||
|
metadata:
|
||||||
|
name: local
|
||||||
|
spec:
|
||||||
|
destination:
|
||||||
|
local:
|
||||||
|
directory: /tmp/scope-reports
|
||||||
|
additionalData:
|
||||||
|
pwd: pwd
|
||||||
|
username: id -un
|
||||||
|
scopeVersion: scope version
|
||||||
|
```
|
||||||
|
|
||||||
|
### RustyPaste
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeReportLocation
|
||||||
|
metadata:
|
||||||
|
name: rustypaste
|
||||||
|
spec:
|
||||||
|
destination:
|
||||||
|
rustyPaste:
|
||||||
|
url: http://localhost:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Additional Data
|
||||||
|
|
||||||
|
Capture environment context with arbitrary commands:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
additionalData:
|
||||||
|
diskSpace: df -h
|
||||||
|
gitBranch: git branch --show-current
|
||||||
|
envVars: env | sort
|
||||||
|
```
|
||||||
|
|
||||||
|
Commands execute at report generation time. Scope automatically redacts secrets.
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Version Check Pattern
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
actions:
|
||||||
|
- name: minimum-version
|
||||||
|
description: Ensure minimum version installed
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- ./bin/check-version.sh check tool 1.2.3
|
||||||
|
fix:
|
||||||
|
helpText: |
|
||||||
|
You don't have the minimum version installed.
|
||||||
|
Check Managed Software Center for updates.
|
||||||
|
```
|
||||||
|
|
||||||
|
### File-Based Check Pattern
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
actions:
|
||||||
|
- name: config-file
|
||||||
|
description: Verify config file exists and is valid
|
||||||
|
check:
|
||||||
|
paths:
|
||||||
|
- .config-file
|
||||||
|
commands:
|
||||||
|
- test -s .config-file
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./bin/setup-config.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service Health Pattern
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
actions:
|
||||||
|
- name: service-running
|
||||||
|
description: Ensure service is running
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- ./bin/check-service.sh
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./bin/start-service.sh
|
||||||
|
helpText: |
|
||||||
|
Service failed to start.
|
||||||
|
Check logs: tail "$(brew --prefix)/var/log/service.log"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dependency Install Pattern
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
actions:
|
||||||
|
- name: install-packages
|
||||||
|
description: Install project dependencies
|
||||||
|
check:
|
||||||
|
paths:
|
||||||
|
- package.json
|
||||||
|
- yarn.lock
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Orchestration Pattern
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Main group that just coordinates dependencies
|
||||||
|
spec:
|
||||||
|
needs:
|
||||||
|
- environment-setup
|
||||||
|
- database-setup
|
||||||
|
- language-runtime
|
||||||
|
actions: [] # No actions, just orchestration
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflow: Creating Known Error
|
||||||
|
|
||||||
|
1. **Identify error pattern**
|
||||||
|
|
||||||
|
- Capture actual error output
|
||||||
|
- Find unique identifying text
|
||||||
|
- Create regex pattern
|
||||||
|
|
||||||
|
2. **Create files**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p {config-root}/known-errors/category
|
||||||
|
touch {config-root}/known-errors/category/error-name.yaml
|
||||||
|
touch {config-root}/known-errors/category/error-name.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Write YAML definition**
|
||||||
|
|
||||||
|
- Use template structure
|
||||||
|
- Write clear help text with numbered steps
|
||||||
|
- Add fix commands if automation is possible
|
||||||
|
|
||||||
|
4. **Create test file**
|
||||||
|
|
||||||
|
- Put actual error output in `.txt` file
|
||||||
|
- Include enough context for pattern matching
|
||||||
|
|
||||||
|
5. **Test pattern**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scope analyze logs \
|
||||||
|
--extra-config {config-root} \
|
||||||
|
{config-root}/known-errors/category/error-name.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Validate schema**
|
||||||
|
```bash
|
||||||
|
jsonschema validate schema.json error-name.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflow: Creating Doctor Group
|
||||||
|
|
||||||
|
1. **Define problem domain**
|
||||||
|
|
||||||
|
- What needs checking/fixing?
|
||||||
|
- What are the dependencies?
|
||||||
|
- Application or environment level?
|
||||||
|
|
||||||
|
2. **Create group file**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Application-level
|
||||||
|
touch {config-root}/application/tool.yaml
|
||||||
|
|
||||||
|
# Environment-level
|
||||||
|
touch {config-root}/environment/tool.yaml
|
||||||
|
|
||||||
|
# Project-specific
|
||||||
|
touch .scope/tool.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Create helper scripts**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p .scope/bin
|
||||||
|
touch .scope/bin/tool.sh
|
||||||
|
chmod +x .scope/bin/tool.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Write group definition**
|
||||||
|
|
||||||
|
- Define metadata (name, description)
|
||||||
|
- List dependencies in `needs`
|
||||||
|
- Create actions for each discrete check
|
||||||
|
- Reference scripts with relative paths
|
||||||
|
|
||||||
|
5. **Test the group**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scope doctor list # Verify detected
|
||||||
|
scope doctor run --only tool # Test execution
|
||||||
|
scope doctor run --only tool --no-cache # Test without cache
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Add to parent group**
|
||||||
|
Update parent group's `needs` list if this is a new dependency
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
### Known Error Not Matching
|
||||||
|
|
||||||
|
- Test regex: `rg "pattern" test-file.txt`
|
||||||
|
- Check special character escaping
|
||||||
|
- Verify pattern exists in test file
|
||||||
|
- Use `scope analyze logs --extra-config` with test file
|
||||||
|
|
||||||
|
### Doctor Check Always Running
|
||||||
|
|
||||||
|
- Verify `paths` globs match files
|
||||||
|
- Check commands exit 0 on success
|
||||||
|
- Try `--no-cache` to rule out caching
|
||||||
|
- Verify script permissions (executable)
|
||||||
|
|
||||||
|
### Dependency Issues
|
||||||
|
|
||||||
|
- Run `scope doctor list` to see execution order
|
||||||
|
- Verify all `needs` items exist
|
||||||
|
- Check for circular dependencies
|
||||||
|
- Use `--only` to test individual groups
|
||||||
|
|
||||||
|
### Script Path Issues
|
||||||
|
|
||||||
|
- Use `./` prefix for relative paths (relative to YAML)
|
||||||
|
- Ensure scripts have execute permissions
|
||||||
|
- Use absolute paths or PATH for system commands
|
||||||
|
- Verify working directory
|
||||||
|
|
||||||
|
## Security Features
|
||||||
|
|
||||||
|
### Automatic Redaction
|
||||||
|
|
||||||
|
Scope automatically redacts sensitive information:
|
||||||
|
|
||||||
|
- GitHub API tokens
|
||||||
|
- AWS credentials
|
||||||
|
- SSH keys
|
||||||
|
- Environment variable values containing secrets
|
||||||
|
- Uses patterns from [ripsecrets](https://github.com/sirwart/ripsecrets)
|
||||||
|
|
||||||
|
This makes it safe to capture full environment in reports and share debug output publicly.
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
### Configuration Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
config-repository/
|
||||||
|
├── {config-root}/
|
||||||
|
│ ├── application/ # App-level (ruby, node, postgres)
|
||||||
|
│ ├── environment/ # System-level (homebrew, docker, github)
|
||||||
|
│ ├── known-errors/ # Error definitions by category
|
||||||
|
│ │ ├── docker/
|
||||||
|
│ │ ├── ruby/
|
||||||
|
│ │ ├── git/
|
||||||
|
│ │ └── ...
|
||||||
|
│ └── reports/ # Report locations
|
||||||
|
```
|
||||||
|
|
||||||
|
### At Scale
|
||||||
|
|
||||||
|
Real-world implementations include:
|
||||||
|
|
||||||
|
- 70+ known error definitions
|
||||||
|
- 30+ doctor groups
|
||||||
|
- Categories: Docker, Ruby, Git, GitHub, MySQL, Postgres, DNS, AWS
|
||||||
|
- Reduces time-to-resolution for common issues
|
||||||
|
- Self-service troubleshooting
|
||||||
|
- Consistent environments across teams
|
||||||
|
|
||||||
|
## Additional Resources
|
||||||
|
|
||||||
|
See references directory for:
|
||||||
|
|
||||||
|
- `quick-reference.md` - Command cheat sheet and pattern examples
|
||||||
|
- `real-world-examples.md` - Production-tested configurations
|
||||||
|
- `testing-guide.md` - Comprehensive validation workflows
|
||||||
375
skills/scope/references/quick-reference.md
Normal file
375
skills/scope/references/quick-reference.md
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
# Scope Quick Reference
|
||||||
|
|
||||||
|
Condensed reference for common Scope operations and patterns.
|
||||||
|
|
||||||
|
## Command Cheat Sheet
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Doctor
|
||||||
|
scope doctor run # Run all checks
|
||||||
|
scope doctor run --only group-name # Run specific group
|
||||||
|
scope doctor run --fix=false # Check only
|
||||||
|
scope doctor run --no-cache # Force re-check
|
||||||
|
scope doctor list # List all checks
|
||||||
|
|
||||||
|
# Analyze
|
||||||
|
scope analyze logs file.log # Check log for errors
|
||||||
|
scope analyze command -- cmd args # Check command output
|
||||||
|
scope analyze --extra-config dir/ file # Use extra configs
|
||||||
|
|
||||||
|
# Report
|
||||||
|
scope report ./script.sh # Run & report script
|
||||||
|
scope report -- command args # Run & report command
|
||||||
|
|
||||||
|
# Version
|
||||||
|
scope version # Show version
|
||||||
|
```
|
||||||
|
|
||||||
|
## YAML Templates
|
||||||
|
|
||||||
|
### Known Error Template
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeKnownError
|
||||||
|
metadata:
|
||||||
|
name: error-name
|
||||||
|
description: What this error means
|
||||||
|
spec:
|
||||||
|
pattern: 'regex pattern'
|
||||||
|
help: |
|
||||||
|
Explanation and steps to fix:
|
||||||
|
1. First step
|
||||||
|
2. Second step
|
||||||
|
fix:
|
||||||
|
prompt:
|
||||||
|
text: Permission prompt
|
||||||
|
commands:
|
||||||
|
- fix-command
|
||||||
|
```
|
||||||
|
|
||||||
|
### Doctor Group Template
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeDoctorGroup
|
||||||
|
metadata:
|
||||||
|
name: group-name
|
||||||
|
description: What this group does
|
||||||
|
spec:
|
||||||
|
include: when-required
|
||||||
|
needs:
|
||||||
|
- dependency-1
|
||||||
|
actions:
|
||||||
|
- name: action-name
|
||||||
|
description: What this checks
|
||||||
|
required: true
|
||||||
|
check:
|
||||||
|
paths:
|
||||||
|
- 'file.txt'
|
||||||
|
commands:
|
||||||
|
- ./bin/check.sh
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./bin/fix.sh
|
||||||
|
helpText: |
|
||||||
|
Help if fix fails
|
||||||
|
```
|
||||||
|
|
||||||
|
### Report Location Template
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeReportLocation
|
||||||
|
metadata:
|
||||||
|
name: location-name
|
||||||
|
spec:
|
||||||
|
destination:
|
||||||
|
local:
|
||||||
|
directory: /tmp/reports
|
||||||
|
# OR githubIssue:
|
||||||
|
# owner: org
|
||||||
|
# repo: repo
|
||||||
|
# OR rustyPaste:
|
||||||
|
# url: http://localhost:8000
|
||||||
|
additionalData:
|
||||||
|
command1: command-to-run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Regex Pattern Examples
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Match specific version error
|
||||||
|
pattern: "ruby [0-9]+\\.[0-9]+\\.[0-9]+ is not installed"
|
||||||
|
|
||||||
|
# Match file not found
|
||||||
|
pattern: "cannot load such file -- .*/([^/]+)\\.(rb|so)"
|
||||||
|
|
||||||
|
# Match Docker daemon not running
|
||||||
|
pattern: "\\.colima/[^/]+/docker\\.sock.*Is the docker daemon running\\?"
|
||||||
|
|
||||||
|
# Match Git lock error
|
||||||
|
pattern: "Unable to create '.*\\.git/refs/heads/.*\\.lock'"
|
||||||
|
|
||||||
|
# Match DNS resolution failure
|
||||||
|
pattern: "Could not resolve host: ([a-zA-Z0-9.-]+)"
|
||||||
|
|
||||||
|
# Match permission denied
|
||||||
|
pattern: "Permission denied.*(/[^:]+)"
|
||||||
|
|
||||||
|
# Character classes
|
||||||
|
[[:digit:]] # 0-9
|
||||||
|
[[:alpha:]] # a-z, A-Z
|
||||||
|
[[:alnum:]] # alphanumeric
|
||||||
|
[[:space:]] # whitespace
|
||||||
|
|
||||||
|
# Quantifiers
|
||||||
|
* # 0 or more
|
||||||
|
+ # 1 or more
|
||||||
|
? # 0 or 1
|
||||||
|
{n,m} # between n and m
|
||||||
|
|
||||||
|
# Escaping
|
||||||
|
\\. # literal dot
|
||||||
|
\\[ # literal [
|
||||||
|
\\( # literal (
|
||||||
|
```
|
||||||
|
|
||||||
|
## Check/Fix Script Template
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ACTION="${1:-check}"
|
||||||
|
|
||||||
|
check() {
|
||||||
|
# Exit 0 if OK, non-zero if needs fix
|
||||||
|
if [[ condition ]]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "Check failed: reason" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
fix() {
|
||||||
|
# Perform fix, exit 0 on success
|
||||||
|
echo "Fixing..."
|
||||||
|
# commands
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$ACTION" in
|
||||||
|
check) check ;;
|
||||||
|
fix) fix ;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 [check|fix]" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Version Check
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: min-version
|
||||||
|
description: Check minimum version
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- test "$(tool --version | cut -d' ' -f2)" = "1.2.3"
|
||||||
|
fix:
|
||||||
|
helpText: Update tool via Managed Software Center
|
||||||
|
```
|
||||||
|
|
||||||
|
### File Exists
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: config-exists
|
||||||
|
description: Config file exists
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- test -f .config
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./bin/create-config.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service Running
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: service-up
|
||||||
|
description: Service is running
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- pgrep -x service-name
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- brew services restart service-name
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dependencies Installed
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: deps
|
||||||
|
description: Dependencies installed
|
||||||
|
check:
|
||||||
|
paths:
|
||||||
|
- package.json
|
||||||
|
- yarn.lock
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Path-Based Auto-Run
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Runs fix when file changes
|
||||||
|
- name: auto-update
|
||||||
|
check:
|
||||||
|
paths:
|
||||||
|
- config.yaml
|
||||||
|
- '**/*.conf'
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./bin/reload.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation Workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Create error file
|
||||||
|
mkdir -p {config-root}/known-errors/category
|
||||||
|
cat > {config-root}/known-errors/category/error.yaml << 'EOF'
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeKnownError
|
||||||
|
metadata:
|
||||||
|
name: my-error
|
||||||
|
spec:
|
||||||
|
pattern: "error pattern"
|
||||||
|
help: How to fix
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 2. Create test file with actual error
|
||||||
|
cat > {config-root}/known-errors/category/error.txt << 'EOF'
|
||||||
|
Actual error output goes here
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 3. Test pattern
|
||||||
|
scope analyze logs \
|
||||||
|
--extra-config {config-root} \
|
||||||
|
{config-root}/known-errors/category/error.txt
|
||||||
|
|
||||||
|
# 4. Validate schema (if available)
|
||||||
|
jsonschema validate schema.json error.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Organization
|
||||||
|
|
||||||
|
```
|
||||||
|
# Gusto shared configs
|
||||||
|
{config-root}/
|
||||||
|
├── application/ # App-level (ruby, node, db)
|
||||||
|
├── environment/ # System-level (homebrew, git)
|
||||||
|
├── known-errors/
|
||||||
|
│ ├── docker/
|
||||||
|
│ ├── ruby/
|
||||||
|
│ ├── git/
|
||||||
|
│ └── {category}/
|
||||||
|
│ ├── error-name.yaml
|
||||||
|
│ └── error-name.txt # Test file
|
||||||
|
└── reports/
|
||||||
|
|
||||||
|
# Project-specific
|
||||||
|
.scope/
|
||||||
|
├── project-name.yaml # Main orchestrator
|
||||||
|
├── db.yaml # Database setup
|
||||||
|
├── ruby.yaml # Language setup
|
||||||
|
└── bin/ # Helper scripts
|
||||||
|
├── check-*.sh
|
||||||
|
└── fix-*.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging Checklist
|
||||||
|
|
||||||
|
### Known Error Not Matching
|
||||||
|
|
||||||
|
- [ ] Test regex: `echo "error" | rg "pattern"`
|
||||||
|
- [ ] Check escaping of special chars
|
||||||
|
- [ ] Verify test file has actual error
|
||||||
|
- [ ] Try broader pattern first
|
||||||
|
|
||||||
|
### Doctor Always Runs
|
||||||
|
|
||||||
|
- [ ] Check path globs match: `ls -la path/pattern`
|
||||||
|
- [ ] Verify check command exits 0: `./bin/check.sh; echo $?`
|
||||||
|
- [ ] Try `--no-cache`
|
||||||
|
- [ ] Check script is executable: `ls -l script.sh`
|
||||||
|
|
||||||
|
### Dependencies Not Working
|
||||||
|
|
||||||
|
- [ ] Run `scope doctor list` - see order
|
||||||
|
- [ ] Verify `needs` names match exactly
|
||||||
|
- [ ] Check for circular deps
|
||||||
|
- [ ] Test with `--only group-name`
|
||||||
|
|
||||||
|
### Script Issues
|
||||||
|
|
||||||
|
- [ ] Add `set -euo pipefail` to scripts
|
||||||
|
- [ ] Check relative path has `./` prefix
|
||||||
|
- [ ] Make executable: `chmod +x script.sh`
|
||||||
|
- [ ] Test standalone: `./bin/script.sh check`
|
||||||
|
|
||||||
|
## Testing Tips
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test regex patterns
|
||||||
|
echo "error text here" | rg "pattern"
|
||||||
|
|
||||||
|
# Test check command
|
||||||
|
./bin/check.sh check
|
||||||
|
echo "Exit code: $?"
|
||||||
|
|
||||||
|
# Test doctor group in isolation
|
||||||
|
scope doctor run --only group-name --no-cache
|
||||||
|
|
||||||
|
# See what would run
|
||||||
|
scope doctor list | grep group-name
|
||||||
|
|
||||||
|
# Test with extra config
|
||||||
|
scope analyze --extra-config /path/to/config file.log
|
||||||
|
|
||||||
|
# Validate YAML syntax
|
||||||
|
yamllint file.yaml
|
||||||
|
|
||||||
|
# Check file matching
|
||||||
|
ls -la path/to/files/**/*.pattern
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Report authentication
|
||||||
|
SCOPE_GH_TOKEN=ghp_xxx # GitHub PAT
|
||||||
|
SCOPE_GH_APP_ID=123 # GitHub App
|
||||||
|
SCOPE_GH_APP_KEY=/path/to/key # GitHub App key
|
||||||
|
|
||||||
|
# Template variables (in YAML)
|
||||||
|
{{ working_dir }} # Current working directory
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Gotchas
|
||||||
|
|
||||||
|
1. **Regex escaping**: Use `\\.` for literal dot, not `.`
|
||||||
|
2. **Relative paths**: Must start with `./` (relative to YAML file)
|
||||||
|
3. **Check exit codes**: 0 = pass, non-zero = needs fix
|
||||||
|
4. **Cache persistence**: Use `--no-cache` when testing
|
||||||
|
5. **Pattern specificity**: Too broad = false positives, too narrow = misses errors
|
||||||
|
6. **Script permissions**: Must be executable (`chmod +x`)
|
||||||
|
7. **YAML indentation**: Use 2 spaces, not tabs
|
||||||
|
8. **Action order**: Actions run in order defined
|
||||||
|
9. **Dependency order**: Use `scope doctor list` to verify
|
||||||
|
10. **Help text**: Use `|` for multi-line strings in YAML
|
||||||
579
skills/scope/references/real-world-examples.md
Normal file
579
skills/scope/references/real-world-examples.md
Normal file
@@ -0,0 +1,579 @@
|
|||||||
|
# Real-World Scope Examples
|
||||||
|
|
||||||
|
Curated examples from production Scope configurations showing battle-tested patterns.
|
||||||
|
|
||||||
|
## Known Errors
|
||||||
|
|
||||||
|
### Docker: Colima Not Running
|
||||||
|
|
||||||
|
**File**: `known-errors/docker/default-colima-not-running.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeKnownError
|
||||||
|
metadata:
|
||||||
|
name: default-colima-not-running
|
||||||
|
description: The default instance of Colima is not running
|
||||||
|
spec:
|
||||||
|
pattern: '.colima/default/docker.sock. Is the docker daemon running?'
|
||||||
|
help: |
|
||||||
|
Colima is not running. Start it by:
|
||||||
|
1. `scope doctor run --only company/docker@v1`
|
||||||
|
|
||||||
|
If that doesn't resolve the issue, reach out to us at @team
|
||||||
|
in the #help-channel channel in Slack for help.
|
||||||
|
fix:
|
||||||
|
prompt:
|
||||||
|
text: Run scope doctor?
|
||||||
|
commands:
|
||||||
|
- scope doctor run --only company/docker@v1
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Patterns**:
|
||||||
|
|
||||||
|
- Uses escaped dot in path pattern: `\\.colima/`
|
||||||
|
- Provides clear escalation path
|
||||||
|
- Fix delegates to doctor group for complex multi-step resolution
|
||||||
|
- Includes Slack channel for human help
|
||||||
|
|
||||||
|
### Ruby: Gem Missing File
|
||||||
|
|
||||||
|
**File**: `known-errors/ruby/gem-missing-file.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeKnownError
|
||||||
|
metadata:
|
||||||
|
name: gem-missing-file
|
||||||
|
description: A gem source file is missing, and fails to be loaded
|
||||||
|
spec:
|
||||||
|
pattern: "/lib/ruby/([[:digit:]]\\.[[:digit:]]\\.[[:digit:]]|gems)/.* `(require|require_relative)': cannot load such file --.*/lib/ruby/gems/.*(LoadError)"
|
||||||
|
help: |
|
||||||
|
A gem source file is missing and fails to be loaded. The cause of this is
|
||||||
|
unknown and still being investigated (TICKET-123).
|
||||||
|
|
||||||
|
The solution is to reinstall the gems to fix the missing file:
|
||||||
|
1. Run `bundle pristine`
|
||||||
|
fix:
|
||||||
|
prompt:
|
||||||
|
text: Run bundle pristine?
|
||||||
|
commands:
|
||||||
|
- bundle pristine
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Patterns**:
|
||||||
|
|
||||||
|
- Complex regex with alternation: `([[:digit:]]\\.[[:digit:]]\\.[[:digit:]]|gems)`
|
||||||
|
- Uses character classes: `[[:digit:]]`
|
||||||
|
- Multiple escaped characters in paths
|
||||||
|
- References tracking issue in help text
|
||||||
|
- Simple, safe fix command
|
||||||
|
|
||||||
|
### Git: Cannot Lock Ref
|
||||||
|
|
||||||
|
**File**: `known-errors/git/cannot-lock-ref.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeKnownError
|
||||||
|
metadata:
|
||||||
|
name: cannot-lock-ref
|
||||||
|
description: Git cannot create lock file for ref
|
||||||
|
spec:
|
||||||
|
pattern: "error: cannot lock ref '[^']+': Unable to create '[^']+\\.lock': File exists"
|
||||||
|
help: |
|
||||||
|
Another git process is running or crashed leaving a lock file.
|
||||||
|
|
||||||
|
To resolve:
|
||||||
|
1. Check for running git processes: `ps aux | grep git`
|
||||||
|
2. If none running, remove the lock file mentioned in the error
|
||||||
|
3. Example: `rm .git/refs/heads/branch-name.lock`
|
||||||
|
fix:
|
||||||
|
prompt:
|
||||||
|
text: This requires manual intervention. Proceed with caution?
|
||||||
|
commands:
|
||||||
|
- echo "Check for git processes: ps aux | grep git"
|
||||||
|
- echo "If safe, manually remove the .lock file mentioned above"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Patterns**:
|
||||||
|
|
||||||
|
- Uses character class negation: `[^']+` (anything except single quote)
|
||||||
|
- Escaped special characters: `\\.lock`
|
||||||
|
- Fix provides diagnostic commands rather than automated fix
|
||||||
|
- Warns user about manual intervention
|
||||||
|
|
||||||
|
### MySQL: Connection Refused
|
||||||
|
|
||||||
|
**File**: `known-errors/mysql/trilogy-connection-refused.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeKnownError
|
||||||
|
metadata:
|
||||||
|
name: trilogy-connection-refused
|
||||||
|
description: MySQL connection refused, service may not be running
|
||||||
|
spec:
|
||||||
|
pattern: "Trilogy::ConnectionRefusedError.*Connection refused - connect\\(2\\)"
|
||||||
|
help: |
|
||||||
|
MySQL/MariaDB is not running or not accepting connections.
|
||||||
|
|
||||||
|
To fix:
|
||||||
|
1. Check if service is running: `brew services list | grep mysql`
|
||||||
|
2. Start the service: `brew services start mysql@8.0`
|
||||||
|
3. Or run: `scope doctor run --only database`
|
||||||
|
fix:
|
||||||
|
prompt:
|
||||||
|
text: Attempt to start MySQL service?
|
||||||
|
commands:
|
||||||
|
- brew services restart mysql@8.0
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Patterns**:
|
||||||
|
|
||||||
|
- Escaped parentheses in regex: `\\(2\\)`
|
||||||
|
- Provides multiple resolution paths
|
||||||
|
- Delegates to doctor group for comprehensive fix
|
||||||
|
- Uses `restart` instead of `start` (idempotent)
|
||||||
|
|
||||||
|
## Doctor Groups
|
||||||
|
|
||||||
|
### Ruby Version Management
|
||||||
|
|
||||||
|
**File**: `application/ruby-version.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeDoctorGroup
|
||||||
|
metadata:
|
||||||
|
name: ruby-version
|
||||||
|
description: Set up Ruby with accurate version
|
||||||
|
spec:
|
||||||
|
include: when-required
|
||||||
|
needs:
|
||||||
|
- ruby-manager
|
||||||
|
actions:
|
||||||
|
- name: .ruby-version
|
||||||
|
description: Verify a valid .ruby-version file is present.
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- test -s .ruby-version
|
||||||
|
fix:
|
||||||
|
helpText: |
|
||||||
|
The .ruby-version file must exist and not be blank.
|
||||||
|
- name: install
|
||||||
|
description: Ensures correct version of ruby is installed
|
||||||
|
check:
|
||||||
|
paths:
|
||||||
|
- '{{ working_dir }}/.ruby-version'
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./bin/ruby-version.sh install
|
||||||
|
- name: verify
|
||||||
|
description: Verify the desired ruby version and current version are the same
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- ./bin/ruby-version.sh verify
|
||||||
|
fix:
|
||||||
|
helpText: |
|
||||||
|
Something went wrong.
|
||||||
|
The ruby version was installed, but is not the version available in your current shell.
|
||||||
|
See error messages above for additional details and possible solutions.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Patterns**:
|
||||||
|
|
||||||
|
- Multiple sequential actions building on each other
|
||||||
|
- First action has no fix commands, only helpText (manual intervention)
|
||||||
|
- Second action watches file changes with `paths`
|
||||||
|
- Third action validates end state
|
||||||
|
- Uses template variable: `{{ working_dir }}`
|
||||||
|
- Delegates complex logic to external script
|
||||||
|
|
||||||
|
### Colima (Docker) Setup
|
||||||
|
|
||||||
|
**File**: `environment/colima.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeDoctorGroup
|
||||||
|
metadata:
|
||||||
|
name: company/docker@v1
|
||||||
|
description: Colima
|
||||||
|
spec:
|
||||||
|
include: when-required
|
||||||
|
needs:
|
||||||
|
- homebrew
|
||||||
|
actions:
|
||||||
|
- name: install
|
||||||
|
description: company-docker is installed
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- ./bin/colima.sh check install
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./bin/colima.sh fix install
|
||||||
|
- name: profile
|
||||||
|
description: The gusto profile exists
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- ./bin/colima.sh check profile
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./bin/colima.sh fix profile
|
||||||
|
helpText: |-
|
||||||
|
The ~/.colima/company.yaml file still doesn't exist after running `sudo config-management`.
|
||||||
|
Please contact #ops-channel in slack.
|
||||||
|
- name: running
|
||||||
|
description: service and vm are running
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- ./bin/colima.sh check running
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./bin/colima.sh fix running
|
||||||
|
helpText: |-
|
||||||
|
We were unable to start the company-docker service and/or the colima vm.
|
||||||
|
Please review the logs.
|
||||||
|
|
||||||
|
tail "$(brew --prefix)/var/log/service.log"
|
||||||
|
|
||||||
|
If you are not able to resolve the issue,
|
||||||
|
please contact #help-channel in slack.
|
||||||
|
- name: docker-context
|
||||||
|
description: docker context is set to gusto
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- ./bin/colima.sh check context
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./bin/colima.sh fix context
|
||||||
|
- name: default-service
|
||||||
|
description: The default colima brew service is stopped
|
||||||
|
required: false
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- ./bin/colima.sh check default-service
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./bin/colima.sh fix default-service
|
||||||
|
- name: default-profile
|
||||||
|
description: The default colima profile is stopped
|
||||||
|
required: false
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- ./bin/colima.sh check default-profile
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./bin/colima.sh fix default-profile
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Patterns**:
|
||||||
|
|
||||||
|
- Versioned name: `company/docker@v1` (allows breaking changes)
|
||||||
|
- All actions delegate to same script with subcommands
|
||||||
|
- Mix of required and optional actions
|
||||||
|
- Complex multi-step setup
|
||||||
|
- Detailed helpText with log locations
|
||||||
|
- Shell command expansion in helpText: `$(brew --prefix)`
|
||||||
|
- Last two actions are optional cleanup (`required: false`)
|
||||||
|
|
||||||
|
### Brewfile Package Management
|
||||||
|
|
||||||
|
**File**: `application/brewfile.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeDoctorGroup
|
||||||
|
metadata:
|
||||||
|
name: brewfile
|
||||||
|
description: Homebrew managed packages
|
||||||
|
spec:
|
||||||
|
include: when-required
|
||||||
|
needs:
|
||||||
|
- github-cli
|
||||||
|
- homebrew
|
||||||
|
actions:
|
||||||
|
- name: brew-bundle
|
||||||
|
description: Install Homebrew packages from Brewfile
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- ./bin/brew-bundle.sh check
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./bin/brew-bundle.sh fix
|
||||||
|
helpText: |
|
||||||
|
brew dependencies cannot be satisfied
|
||||||
|
|
||||||
|
Please review the output above for errors and possible solutions.
|
||||||
|
If you need assistance, please contact #help-channel in slack.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Patterns**:
|
||||||
|
|
||||||
|
- Multiple dependencies ensure prerequisites installed first
|
||||||
|
- Single action with simple check/fix delegation
|
||||||
|
- Generic helpText directs to previous output
|
||||||
|
- Minimal but effective
|
||||||
|
|
||||||
|
### Version Requirements Check
|
||||||
|
|
||||||
|
**File**: `.scope/scope.yaml` (project-specific)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeDoctorGroup
|
||||||
|
metadata:
|
||||||
|
name: scope
|
||||||
|
spec:
|
||||||
|
include: when-required
|
||||||
|
needs: []
|
||||||
|
actions:
|
||||||
|
- name: minimum-scope-version
|
||||||
|
description: Ensures we have at least the minimum version of scope installed
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- ./bin/check-scope-version.sh check scope 2024.2.68
|
||||||
|
fix:
|
||||||
|
helpText: |
|
||||||
|
You don't have the minimum version of scope installed.
|
||||||
|
Check the Managed Software Center for updates.
|
||||||
|
|
||||||
|
If that doesn't work, please contact #help-channel in slack.
|
||||||
|
- name: minimum-gusto-scope-version
|
||||||
|
description: Ensures we have at least the minimum version of scope installed
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- ./bin/check-scope-version.sh check gusto 2025.05.15.0001
|
||||||
|
fix:
|
||||||
|
helpText: |
|
||||||
|
You don't have the minimum version of scope_config installed.
|
||||||
|
Check the Managed Software Center for updates.
|
||||||
|
|
||||||
|
If that doesn't work, please contact #help-channel in slack.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Patterns**:
|
||||||
|
|
||||||
|
- No dependencies (runs first)
|
||||||
|
- No automated fix (requires external tool)
|
||||||
|
- Passes version as argument to script
|
||||||
|
- Consistent helpText pattern across actions
|
||||||
|
|
||||||
|
### Orchestrator Pattern
|
||||||
|
|
||||||
|
**File**: `.scope/project.yaml` (project-specific)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeDoctorGroup
|
||||||
|
metadata:
|
||||||
|
name: project
|
||||||
|
description: Application setup
|
||||||
|
spec:
|
||||||
|
needs:
|
||||||
|
- scope
|
||||||
|
- company/environment@v1
|
||||||
|
- brewfile
|
||||||
|
- company/ruby@v1
|
||||||
|
- company/javascript@v1
|
||||||
|
- gitconfig
|
||||||
|
- lefthook
|
||||||
|
- db
|
||||||
|
- rubymine
|
||||||
|
- ruby-next
|
||||||
|
- kafka
|
||||||
|
actions: []
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Patterns**:
|
||||||
|
|
||||||
|
- No actions, only dependencies
|
||||||
|
- Orchestrates entire setup in correct order
|
||||||
|
- Acts as entrypoint for `scope doctor run`
|
||||||
|
- Clear dependency chain
|
||||||
|
|
||||||
|
## Report Location
|
||||||
|
|
||||||
|
**File**: `reports/report-location.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeReportLocation
|
||||||
|
metadata:
|
||||||
|
name: local
|
||||||
|
spec:
|
||||||
|
destination:
|
||||||
|
local:
|
||||||
|
directory: /tmp/scope-reports
|
||||||
|
additionalData:
|
||||||
|
pwd: pwd
|
||||||
|
username: id -un
|
||||||
|
ruby: which ruby
|
||||||
|
node: which node
|
||||||
|
nodeVersion: node -v
|
||||||
|
scopeVersion: scope version
|
||||||
|
configVersion: config-tool --version
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Patterns**:
|
||||||
|
|
||||||
|
- Local filesystem destination (no auth required)
|
||||||
|
- Captures environment context
|
||||||
|
- Uses simple shell commands
|
||||||
|
- Platform-specific command: `pkgutil` (macOS)
|
||||||
|
- Mix of path commands (`which`) and version commands
|
||||||
|
|
||||||
|
## Helper Script Examples
|
||||||
|
|
||||||
|
### Check/Fix Pattern
|
||||||
|
|
||||||
|
**Example**: `bin/ruby-version.sh`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ACTION="${1:-}"
|
||||||
|
COMMAND="${2:-}"
|
||||||
|
|
||||||
|
check_file_exists() {
|
||||||
|
test -s .ruby-version
|
||||||
|
}
|
||||||
|
|
||||||
|
install_version() {
|
||||||
|
local desired_version
|
||||||
|
desired_version=$(cat .ruby-version)
|
||||||
|
|
||||||
|
if mise which ruby &> /dev/null; then
|
||||||
|
echo "Ruby is already available"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
mise install ruby@"${desired_version}"
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_version() {
|
||||||
|
local desired_version current_version
|
||||||
|
desired_version=$(cat .ruby-version)
|
||||||
|
current_version=$(ruby --version | awk '{print $2}')
|
||||||
|
|
||||||
|
if [[ "$desired_version" == "$current_version" ]]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "Desired: $desired_version, Current: $current_version" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$ACTION" in
|
||||||
|
check)
|
||||||
|
check_file_exists
|
||||||
|
;;
|
||||||
|
install)
|
||||||
|
install_version
|
||||||
|
;;
|
||||||
|
verify)
|
||||||
|
verify_version
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 [check|install|verify]" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Patterns**:
|
||||||
|
|
||||||
|
- Supports multiple subcommands
|
||||||
|
- Extracts values from files
|
||||||
|
- Uses command substitution
|
||||||
|
- Provides clear error messages to stderr
|
||||||
|
- Returns appropriate exit codes
|
||||||
|
|
||||||
|
### Version Comparison
|
||||||
|
|
||||||
|
**Example**: `bin/check-scope-version.sh`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ACTION="${1:-}"
|
||||||
|
PACKAGE="${2:-}"
|
||||||
|
MIN_VERSION="${3:-}"
|
||||||
|
|
||||||
|
check_version() {
|
||||||
|
local current_version
|
||||||
|
|
||||||
|
case "$PACKAGE" in
|
||||||
|
scope)
|
||||||
|
current_version=$(scope version 2>&1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
|
||||||
|
;;
|
||||||
|
gusto)
|
||||||
|
current_version=$(config-tool --version 2>&1 | awk '/version:/ {print $2}')
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown package: $PACKAGE" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ "$(printf '%s\n' "$MIN_VERSION" "$current_version" | sort -V | head -1)" == "$MIN_VERSION" ]]; then
|
||||||
|
echo "Version $current_version meets minimum $MIN_VERSION"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "Version $current_version does not meet minimum $MIN_VERSION" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$ACTION" in
|
||||||
|
check)
|
||||||
|
check_version
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 check <package> <min-version>" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Patterns**:
|
||||||
|
|
||||||
|
- Semantic version comparison using `sort -V`
|
||||||
|
- Multiple package sources
|
||||||
|
- Regex extraction of version numbers
|
||||||
|
- Parameter validation
|
||||||
|
|
||||||
|
## Lessons from Production
|
||||||
|
|
||||||
|
### What Works Well
|
||||||
|
|
||||||
|
1. **Versioned group names** (`company/docker@v1`) allow non-breaking changes
|
||||||
|
2. **Orchestrator groups** with no actions simplify complex setups
|
||||||
|
3. **Optional actions** (`required: false`) for nice-to-haves
|
||||||
|
4. **Delegating to scripts** keeps YAML simple, logic testable
|
||||||
|
5. **Consistent naming** (category/tool pattern) aids discovery
|
||||||
|
6. **Rich helpText** with log locations and Slack channels
|
||||||
|
7. **Multiple fix strategies** in help text (auto, manual, escalate)
|
||||||
|
|
||||||
|
### Common Pitfalls
|
||||||
|
|
||||||
|
1. **Overly broad patterns** catch unrelated errors
|
||||||
|
2. **Missing escaping** in regex patterns
|
||||||
|
3. **Hardcoded paths** instead of variables
|
||||||
|
4. **Complex logic in YAML** instead of scripts
|
||||||
|
5. **Missing error messages** when checks fail
|
||||||
|
6. **No test files** make pattern validation harder
|
||||||
|
7. **Circular dependencies** between groups
|
||||||
|
|
||||||
|
### Scale Insights
|
||||||
|
|
||||||
|
At 70+ known errors and 30+ doctor groups:
|
||||||
|
|
||||||
|
- Categorization prevents overwhelming users
|
||||||
|
- Consistent patterns make contribution easier
|
||||||
|
- Test files are essential for maintenance
|
||||||
|
- Versioning enables evolution without breaking changes
|
||||||
|
- Clear ownership (Slack channels) reduces support burden
|
||||||
661
skills/scope/references/testing-guide.md
Normal file
661
skills/scope/references/testing-guide.md
Normal file
@@ -0,0 +1,661 @@
|
|||||||
|
# Scope Testing Guide
|
||||||
|
|
||||||
|
Comprehensive guide for testing Scope configurations before deployment.
|
||||||
|
|
||||||
|
## Testing Philosophy
|
||||||
|
|
||||||
|
1. **Test patterns in isolation** before adding to YAML
|
||||||
|
2. **Validate schema** before testing functionality
|
||||||
|
3. **Test with real errors** using `.txt` files
|
||||||
|
4. **Test incrementally** (pattern → YAML → integration)
|
||||||
|
5. **Automate regression tests** for known errors
|
||||||
|
|
||||||
|
## Known Error Testing
|
||||||
|
|
||||||
|
### 1. Test Regex Pattern in Isolation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test pattern matches expected text
|
||||||
|
echo "error: cannot lock ref 'refs/heads/main'" | rg "cannot lock ref"
|
||||||
|
# Output: error: cannot lock ref 'refs/heads/main'
|
||||||
|
|
||||||
|
# Test pattern doesn't match unrelated text
|
||||||
|
echo "error: something else" | rg "cannot lock ref"
|
||||||
|
# Output: (nothing - no match)
|
||||||
|
|
||||||
|
# Test with actual error file
|
||||||
|
rg "pattern" path/to/error-output.log
|
||||||
|
```
|
||||||
|
|
||||||
|
**Common Issues**:
|
||||||
|
|
||||||
|
- Pattern too broad: matches too many things
|
||||||
|
- Pattern too specific: misses variations
|
||||||
|
- Missing escapes: special chars break regex
|
||||||
|
- Wrong quantifiers: `*` vs `+` vs `?`
|
||||||
|
|
||||||
|
### 2. Create Test File
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create directory
|
||||||
|
mkdir -p {config-root}/known-errors/category
|
||||||
|
|
||||||
|
# Create test file with ACTUAL error output
|
||||||
|
cat > {config-root}/known-errors/category/error-name.txt << 'EOF'
|
||||||
|
[Paste actual error output here]
|
||||||
|
This should be the real error text that users see
|
||||||
|
Including all the context around it
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
**Best Practices**:
|
||||||
|
|
||||||
|
- Use real error output, not synthetic examples
|
||||||
|
- Include surrounding context (lines before/after)
|
||||||
|
- Test multiple variations if error has variants
|
||||||
|
- Keep file size reasonable (< 100 lines)
|
||||||
|
|
||||||
|
### 3. Create YAML Definition
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat > {config-root}/known-errors/category/error-name.yaml << 'EOF'
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeKnownError
|
||||||
|
metadata:
|
||||||
|
name: error-name
|
||||||
|
description: Brief description
|
||||||
|
spec:
|
||||||
|
pattern: "your pattern here"
|
||||||
|
help: |
|
||||||
|
How to fix this error
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Test Pattern Matching
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test that pattern matches the test file
|
||||||
|
scope analyze logs \
|
||||||
|
--extra-config {config-root} \
|
||||||
|
{config-root}/known-errors/category/error-name.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected Output** (pattern matches):
|
||||||
|
|
||||||
|
```
|
||||||
|
Known Error: error-name
|
||||||
|
Brief description
|
||||||
|
|
||||||
|
How to fix this error
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected Output** (pattern doesn't match):
|
||||||
|
|
||||||
|
```
|
||||||
|
No known errors found
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Validate Schema
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install validator
|
||||||
|
brew install sourcemeta/apps/jsonschema
|
||||||
|
|
||||||
|
# Get schema (one-time setup)
|
||||||
|
curl -o /tmp/ScopeKnownError.json \
|
||||||
|
https://github.com/oscope-dev/scope/raw/main/scope/schema/v1alpha.com.github.scope.ScopeKnownError.json
|
||||||
|
|
||||||
|
# Validate
|
||||||
|
jsonschema validate \
|
||||||
|
/tmp/ScopeKnownError.json \
|
||||||
|
{config-root}/known-errors/category/error-name.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected Output** (valid):
|
||||||
|
|
||||||
|
```
|
||||||
|
ok: {config-root}/known-errors/category/error-name.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Test in Real Scenario
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run the actual failing command and pipe to scope
|
||||||
|
failing-command 2>&1 | scope analyze command
|
||||||
|
|
||||||
|
# Or analyze existing log file
|
||||||
|
scope analyze logs /path/to/error.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Doctor Group Testing
|
||||||
|
|
||||||
|
### 1. Test Check Script Standalone
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create test script
|
||||||
|
cat > .scope/bin/test-check.sh << 'EOF'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Your check logic here
|
||||||
|
if [[ -f .required-file ]]; then
|
||||||
|
echo "Check passed"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "Check failed: .required-file missing" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod +x .scope/bin/test-check.sh
|
||||||
|
|
||||||
|
# Test success case
|
||||||
|
touch .required-file
|
||||||
|
./.scope/bin/test-check.sh
|
||||||
|
echo "Exit code: $?"
|
||||||
|
# Expected: Check passed, Exit code: 0
|
||||||
|
|
||||||
|
# Test failure case
|
||||||
|
rm .required-file
|
||||||
|
./.scope/bin/test-check.sh
|
||||||
|
echo "Exit code: $?"
|
||||||
|
# Expected: Check failed: .required-file missing, Exit code: 1
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Test Fix Script Standalone
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create fix script
|
||||||
|
cat > .scope/bin/test-fix.sh << 'EOF'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "Creating .required-file..."
|
||||||
|
touch .required-file
|
||||||
|
echo "Done"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod +x .scope/bin/test-fix.sh
|
||||||
|
|
||||||
|
# Test fix
|
||||||
|
rm -f .required-file
|
||||||
|
./.scope/bin/test-fix.sh
|
||||||
|
ls -la .required-file
|
||||||
|
# Expected: File created
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Create Doctor Group YAML
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat > .scope/test-group.yaml << 'EOF'
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeDoctorGroup
|
||||||
|
metadata:
|
||||||
|
name: test-group
|
||||||
|
description: Test group for validation
|
||||||
|
spec:
|
||||||
|
include: when-required
|
||||||
|
needs: []
|
||||||
|
actions:
|
||||||
|
- name: test-action
|
||||||
|
description: Test action
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- ./bin/test-check.sh
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./bin/test-fix.sh
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Verify Group is Detected
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List all groups
|
||||||
|
scope doctor list | grep test-group
|
||||||
|
|
||||||
|
# Expected output includes:
|
||||||
|
# ScopeDoctorGroup/test-group Test group for validation .scope/test-group.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Test Check-Only Mode
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Remove file to trigger check failure
|
||||||
|
rm -f .required-file
|
||||||
|
|
||||||
|
# Run in check-only mode
|
||||||
|
scope doctor run --only test-group --fix=false
|
||||||
|
|
||||||
|
# Expected: Shows check failed, but doesn't run fix
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Test With Fix
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Remove file to trigger check failure
|
||||||
|
rm -f .required-file
|
||||||
|
|
||||||
|
# Run with fix enabled
|
||||||
|
scope doctor run --only test-group
|
||||||
|
|
||||||
|
# Expected: Check fails, fix runs, file created
|
||||||
|
ls -la .required-file
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Test Caching
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# First run (should execute check)
|
||||||
|
scope doctor run --only test-group
|
||||||
|
# Expected: Runs check and fix if needed
|
||||||
|
|
||||||
|
# Second run (should use cache if file-based)
|
||||||
|
scope doctor run --only test-group
|
||||||
|
# Expected: Skips if using path-based checks
|
||||||
|
|
||||||
|
# Force re-run
|
||||||
|
scope doctor run --only test-group --no-cache
|
||||||
|
# Expected: Runs check again
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. Test Dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create dependency group
|
||||||
|
cat > .scope/dependency.yaml << 'EOF'
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeDoctorGroup
|
||||||
|
metadata:
|
||||||
|
name: dependency
|
||||||
|
spec:
|
||||||
|
include: when-required
|
||||||
|
needs: []
|
||||||
|
actions:
|
||||||
|
- name: setup
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- test -f .dependency-marker
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- touch .dependency-marker
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Update test group to depend on it
|
||||||
|
# Add to test-group.yaml:
|
||||||
|
# needs:
|
||||||
|
# - dependency
|
||||||
|
|
||||||
|
# Test dependency resolution
|
||||||
|
rm -f .dependency-marker .required-file
|
||||||
|
scope doctor run --only test-group
|
||||||
|
|
||||||
|
# Expected: Runs dependency first, then test-group
|
||||||
|
ls -la .dependency-marker .required-file
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9. Validate Schema
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get schema
|
||||||
|
curl -o /tmp/ScopeDoctorGroup.json \
|
||||||
|
https://github.com/oscope-dev/scope/raw/main/scope/schema/v1alpha.com.github.scope.ScopeDoctorGroup.json
|
||||||
|
|
||||||
|
# Validate
|
||||||
|
jsonschema validate \
|
||||||
|
/tmp/ScopeDoctorGroup.json \
|
||||||
|
.scope/test-group.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration Testing
|
||||||
|
|
||||||
|
### Test Complete Workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Clean slate
|
||||||
|
rm -rf /tmp/scope-test
|
||||||
|
mkdir -p /tmp/scope-test/.scope/bin
|
||||||
|
|
||||||
|
# 2. Create complete setup
|
||||||
|
cd /tmp/scope-test
|
||||||
|
|
||||||
|
# Create check script
|
||||||
|
cat > .scope/bin/check.sh << 'EOF'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
test -f .setup-complete
|
||||||
|
EOF
|
||||||
|
chmod +x .scope/bin/check.sh
|
||||||
|
|
||||||
|
# Create fix script
|
||||||
|
cat > .scope/bin/fix.sh << 'EOF'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
echo "Setting up..."
|
||||||
|
sleep 1
|
||||||
|
touch .setup-complete
|
||||||
|
echo "Done"
|
||||||
|
EOF
|
||||||
|
chmod +x .scope/bin/fix.sh
|
||||||
|
|
||||||
|
# Create doctor group
|
||||||
|
cat > .scope/setup.yaml << 'EOF'
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeDoctorGroup
|
||||||
|
metadata:
|
||||||
|
name: setup
|
||||||
|
spec:
|
||||||
|
include: by-default
|
||||||
|
needs: []
|
||||||
|
actions:
|
||||||
|
- name: initialize
|
||||||
|
description: Initialize project
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- ./.scope/bin/check.sh
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./.scope/bin/fix.sh
|
||||||
|
helpText: |
|
||||||
|
Setup failed. Try running manually:
|
||||||
|
./.scope/bin/fix.sh
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 3. Test end-to-end
|
||||||
|
scope doctor run
|
||||||
|
|
||||||
|
# 4. Verify result
|
||||||
|
test -f .setup-complete && echo "SUCCESS" || echo "FAILED"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Regression Testing
|
||||||
|
|
||||||
|
### Create Test Suite
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create test runner
|
||||||
|
cat > test-scope.sh << 'EOF'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
PASSED=0
|
||||||
|
FAILED=0
|
||||||
|
|
||||||
|
test_known_error() {
|
||||||
|
local name=$1
|
||||||
|
local yaml=$2
|
||||||
|
local test_file=$3
|
||||||
|
|
||||||
|
echo "Testing: $name"
|
||||||
|
|
||||||
|
if scope analyze logs --extra-config {config-root} "$test_file" | grep -q "$name"; then
|
||||||
|
echo " ✓ Pattern matches"
|
||||||
|
((PASSED++))
|
||||||
|
else
|
||||||
|
echo " ✗ Pattern does not match"
|
||||||
|
((FAILED++))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test all known errors
|
||||||
|
for yaml in {config-root}/known-errors/*/*.yaml; do
|
||||||
|
name=$(basename "$yaml" .yaml)
|
||||||
|
txt="${yaml%.yaml}.txt"
|
||||||
|
|
||||||
|
if [[ -f "$txt" ]]; then
|
||||||
|
test_known_error "$name" "$yaml" "$txt"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Results: $PASSED passed, $FAILED failed"
|
||||||
|
|
||||||
|
exit $FAILED
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod +x test-scope.sh
|
||||||
|
|
||||||
|
# Run test suite
|
||||||
|
./test-scope.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Continuous Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Watch for changes and re-test
|
||||||
|
while true; do
|
||||||
|
inotifywait -r -e modify {config-root}/
|
||||||
|
./test-scope.sh
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Testing
|
||||||
|
|
||||||
|
### Measure Doctor Run Time
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Time complete run
|
||||||
|
time scope doctor run
|
||||||
|
|
||||||
|
# Time specific group
|
||||||
|
time scope doctor run --only group-name
|
||||||
|
|
||||||
|
# Compare cached vs uncached
|
||||||
|
time scope doctor run # With cache
|
||||||
|
time scope doctor run --no-cache # Without cache
|
||||||
|
```
|
||||||
|
|
||||||
|
### Profile Cache Effectiveness
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# First run (cold cache)
|
||||||
|
scope doctor run > /tmp/run1.log 2>&1
|
||||||
|
|
||||||
|
# Second run (warm cache)
|
||||||
|
scope doctor run > /tmp/run2.log 2>&1
|
||||||
|
|
||||||
|
# Compare
|
||||||
|
diff /tmp/run1.log /tmp/run2.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging Tests
|
||||||
|
|
||||||
|
### Enable Verbose Output
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Scope doesn't have --verbose, but you can debug scripts
|
||||||
|
# Add to scripts:
|
||||||
|
set -x # Print commands before execution
|
||||||
|
|
||||||
|
# Example:
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euxo pipefail # Added 'x' for debug output
|
||||||
|
```
|
||||||
|
|
||||||
|
### Capture Full Output
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Capture stdout and stderr
|
||||||
|
scope doctor run --only group-name > /tmp/stdout.log 2> /tmp/stderr.log
|
||||||
|
|
||||||
|
# Capture combined
|
||||||
|
scope doctor run --only group-name &> /tmp/combined.log
|
||||||
|
|
||||||
|
# Capture and display
|
||||||
|
scope doctor run --only group-name 2>&1 | tee /tmp/output.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Specific Action
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Doctor groups run all actions in sequence
|
||||||
|
# To test just one action, you can run the script directly:
|
||||||
|
./.scope/bin/script.sh check
|
||||||
|
./.scope/bin/script.sh fix
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Test Scenarios
|
||||||
|
|
||||||
|
### Test: Pattern with Special Characters
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pattern: "error: cannot lock ref 'refs/heads/main'"
|
||||||
|
# Test: Does it escape properly?
|
||||||
|
|
||||||
|
echo "error: cannot lock ref 'refs/heads/main'" > /tmp/test.txt
|
||||||
|
|
||||||
|
# This should match:
|
||||||
|
rg "cannot lock ref '[^']+'" /tmp/test.txt
|
||||||
|
|
||||||
|
# This should NOT match (missing escape):
|
||||||
|
rg "cannot lock ref 'refs/heads/main'" /tmp/test.txt # Literal string
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test: Path-Based Check Triggers on Change
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Setup group with path check
|
||||||
|
cat > .scope/path-test.yaml << 'EOF'
|
||||||
|
spec:
|
||||||
|
actions:
|
||||||
|
- name: test
|
||||||
|
check:
|
||||||
|
paths:
|
||||||
|
- config.yaml
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- echo "Config changed"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# First run - config doesn't exist, should trigger
|
||||||
|
scope doctor run --only path-test
|
||||||
|
|
||||||
|
# Create config
|
||||||
|
echo "version: 1" > config.yaml
|
||||||
|
|
||||||
|
# Second run - config exists, should trigger (first time seeing it)
|
||||||
|
scope doctor run --only path-test
|
||||||
|
|
||||||
|
# Third run - config unchanged, should NOT trigger
|
||||||
|
scope doctor run --only path-test
|
||||||
|
|
||||||
|
# Change config
|
||||||
|
echo "version: 2" > config.yaml
|
||||||
|
|
||||||
|
# Fourth run - config changed, should trigger
|
||||||
|
scope doctor run --only path-test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test: Dependency Order
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create test that verifies order
|
||||||
|
cat > .scope/dep-order-test.yaml << 'EOF'
|
||||||
|
spec:
|
||||||
|
needs:
|
||||||
|
- first
|
||||||
|
- second
|
||||||
|
actions:
|
||||||
|
- name: verify
|
||||||
|
check:
|
||||||
|
commands:
|
||||||
|
- test -f .first-ran
|
||||||
|
- test -f .second-ran
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Run and check log order
|
||||||
|
scope doctor run --only dep-order-test 2>&1 | grep -E "(first|second|verify)"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### DO
|
||||||
|
|
||||||
|
✓ Test regex patterns with `rg` before adding to YAML
|
||||||
|
✓ Create `.txt` test files with real error output
|
||||||
|
✓ Validate schema before testing functionality
|
||||||
|
✓ Test scripts standalone before integrating
|
||||||
|
✓ Use `--no-cache` when testing changes
|
||||||
|
✓ Test both success and failure paths
|
||||||
|
✓ Test dependency resolution
|
||||||
|
✓ Keep test files small and focused
|
||||||
|
✓ Use version control to track test changes
|
||||||
|
|
||||||
|
### DON'T
|
||||||
|
|
||||||
|
✗ Skip testing regex patterns in isolation
|
||||||
|
✗ Use synthetic error examples
|
||||||
|
✗ Assume patterns work without testing
|
||||||
|
✗ Test only the happy path
|
||||||
|
✗ Forget to make scripts executable
|
||||||
|
✗ Hard-code paths in tests
|
||||||
|
✗ Test in production first
|
||||||
|
✗ Commit without validation
|
||||||
|
|
||||||
|
## Troubleshooting Tests
|
||||||
|
|
||||||
|
### Pattern doesn't match test file
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Debug steps:
|
||||||
|
# 1. Check file encoding
|
||||||
|
file -I test-file.txt
|
||||||
|
|
||||||
|
# 2. Check for hidden characters
|
||||||
|
cat -A test-file.txt
|
||||||
|
|
||||||
|
# 3. Test pattern piece by piece
|
||||||
|
rg "simple" test-file.txt
|
||||||
|
rg "simple.*pattern" test-file.txt
|
||||||
|
rg "full.*complex.*pattern" test-file.txt
|
||||||
|
|
||||||
|
# 4. Check escaping
|
||||||
|
rg "pattern with \. dot" test-file.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check always fails
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Debug steps:
|
||||||
|
# 1. Run script manually
|
||||||
|
./.scope/bin/check.sh
|
||||||
|
echo "Exit code: $?"
|
||||||
|
|
||||||
|
# 2. Check for syntax errors
|
||||||
|
bash -n ./.scope/bin/check.sh
|
||||||
|
|
||||||
|
# 3. Add debug output
|
||||||
|
set -x in script
|
||||||
|
|
||||||
|
# 4. Check permissions
|
||||||
|
ls -l ./.scope/bin/check.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fix runs but doesn't work
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Debug steps:
|
||||||
|
# 1. Check fix script exit code
|
||||||
|
./.scope/bin/fix.sh
|
||||||
|
echo "Exit code: $?"
|
||||||
|
|
||||||
|
# 2. Check what fix actually does
|
||||||
|
./.scope/bin/fix.sh
|
||||||
|
ls -la # Check if files created
|
||||||
|
|
||||||
|
# 3. Run check after fix
|
||||||
|
./.scope/bin/fix.sh && ./.scope/bin/check.sh
|
||||||
|
echo "Check exit code: $?"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cache causes issues
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Solutions:
|
||||||
|
# 1. Always use --no-cache when testing
|
||||||
|
scope doctor run --only test --no-cache
|
||||||
|
|
||||||
|
# 2. Clear cache manually (implementation-specific)
|
||||||
|
# Check scope docs for cache location
|
||||||
|
|
||||||
|
# 3. Use command-based checks instead of path-based
|
||||||
|
# Commands don't use cache
|
||||||
|
```
|
||||||
205
skills/scope/scripts/create-doctor-group.sh
Executable file
205
skills/scope/scripts/create-doctor-group.sh
Executable file
@@ -0,0 +1,205 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Create a new Scope doctor group with helper scripts
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat << EOF
|
||||||
|
Usage: $0 <type> <group-name>
|
||||||
|
|
||||||
|
Create a new Scope doctor group with helper scripts.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
type Group type: 'application' or 'environment' or 'project'
|
||||||
|
group-name Descriptive name (e.g., ruby-version, colima)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$0 application ruby-version
|
||||||
|
$0 environment colima
|
||||||
|
$0 project database-setup
|
||||||
|
|
||||||
|
The script will create:
|
||||||
|
For application/environment:
|
||||||
|
- {config-root}/{type}/{group-name}.yaml
|
||||||
|
- {config-root}/{type}/bin/{group-name}.sh
|
||||||
|
|
||||||
|
For project:
|
||||||
|
- .scope/{group-name}.yaml
|
||||||
|
- .scope/bin/{group-name}.sh
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check arguments
|
||||||
|
if [[ $# -ne 2 ]]; then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
TYPE="$1"
|
||||||
|
GROUP_NAME="$2"
|
||||||
|
|
||||||
|
# Validate type and determine base directory
|
||||||
|
case "$TYPE" in
|
||||||
|
application | environment)
|
||||||
|
# Try common config directory structures
|
||||||
|
if [[ -d "config/${TYPE}" ]]; then
|
||||||
|
BASE_DIR="config/${TYPE}"
|
||||||
|
elif [[ -d "scope-config/${TYPE}" ]]; then
|
||||||
|
BASE_DIR="scope-config/${TYPE}"
|
||||||
|
elif [[ -d "${TYPE}" ]]; then
|
||||||
|
BASE_DIR="${TYPE}"
|
||||||
|
else
|
||||||
|
echo "Error: Cannot find ${TYPE} directory" >&2
|
||||||
|
echo "Run this from config root directory" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
BIN_DIR="${BASE_DIR}/bin"
|
||||||
|
;;
|
||||||
|
project)
|
||||||
|
BASE_DIR=".scope"
|
||||||
|
BIN_DIR="${BASE_DIR}/bin"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Error: type must be 'application', 'environment', or 'project'" >&2
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Create directories
|
||||||
|
mkdir -p "$BASE_DIR" "$BIN_DIR"
|
||||||
|
|
||||||
|
# Create YAML file
|
||||||
|
YAML_FILE="${BASE_DIR}/${GROUP_NAME}.yaml"
|
||||||
|
if [[ -f "$YAML_FILE" ]]; then
|
||||||
|
echo "Error: $YAML_FILE already exists" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat > "$YAML_FILE" << EOF
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeDoctorGroup
|
||||||
|
metadata:
|
||||||
|
name: ${GROUP_NAME}
|
||||||
|
description: TODO: Brief description of what this group does
|
||||||
|
spec:
|
||||||
|
include: when-required # or 'by-default'
|
||||||
|
needs:
|
||||||
|
# TODO: Add dependencies here
|
||||||
|
# - dependency-1
|
||||||
|
# - dependency-2
|
||||||
|
actions:
|
||||||
|
- name: check-and-fix
|
||||||
|
description: TODO: What this action checks/fixes
|
||||||
|
required: true
|
||||||
|
check:
|
||||||
|
# Option 1: Command-based check
|
||||||
|
commands:
|
||||||
|
- ./bin/${GROUP_NAME}.sh check
|
||||||
|
# Option 2: Path-based check (watches files for changes)
|
||||||
|
# paths:
|
||||||
|
# - "file-to-watch.txt"
|
||||||
|
# - "**/*.config"
|
||||||
|
fix:
|
||||||
|
commands:
|
||||||
|
- ./bin/${GROUP_NAME}.sh fix
|
||||||
|
helpText: |
|
||||||
|
TODO: What to do if the fix fails.
|
||||||
|
|
||||||
|
Common issues:
|
||||||
|
1. Issue 1 and resolution
|
||||||
|
2. Issue 2 and resolution
|
||||||
|
|
||||||
|
If you need assistance, contact #help-channel in Slack.
|
||||||
|
# Optional: helpUrl: https://docs.example.com/troubleshooting
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create helper script
|
||||||
|
SCRIPT_FILE="${BIN_DIR}/${GROUP_NAME}.sh"
|
||||||
|
if [[ -f "$SCRIPT_FILE" ]]; then
|
||||||
|
echo "Error: $SCRIPT_FILE already exists" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat > "$SCRIPT_FILE" << 'SCRIPTEOF'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Helper script for GROUP_NAME doctor group
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ACTION="${1:-}"
|
||||||
|
|
||||||
|
check() {
|
||||||
|
# TODO: Implement check logic
|
||||||
|
# Return 0 if check passes (nothing to fix)
|
||||||
|
# Return non-zero if check fails (fix needed)
|
||||||
|
|
||||||
|
echo "Checking..." >&2
|
||||||
|
|
||||||
|
# Example: Check if a file exists
|
||||||
|
# if [[ -f .required-file ]]; then
|
||||||
|
# echo "Check passed" >&2
|
||||||
|
# return 0
|
||||||
|
# else
|
||||||
|
# echo "Check failed: .required-file missing" >&2
|
||||||
|
# return 1
|
||||||
|
# fi
|
||||||
|
|
||||||
|
echo "TODO: Implement check logic" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fix() {
|
||||||
|
# TODO: Implement fix logic
|
||||||
|
# Return 0 on success
|
||||||
|
# Return non-zero on failure
|
||||||
|
|
||||||
|
echo "Fixing..." >&2
|
||||||
|
|
||||||
|
# Example: Create required file
|
||||||
|
# touch .required-file
|
||||||
|
|
||||||
|
echo "TODO: Implement fix logic" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$ACTION" in
|
||||||
|
check)
|
||||||
|
check
|
||||||
|
;;
|
||||||
|
fix)
|
||||||
|
fix
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 [check|fix]" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
SCRIPTEOF
|
||||||
|
|
||||||
|
# Replace GROUP_NAME placeholder
|
||||||
|
sed -i.bak "s/GROUP_NAME/${GROUP_NAME}/g" "$SCRIPT_FILE" && rm "${SCRIPT_FILE}.bak"
|
||||||
|
|
||||||
|
# Make script executable
|
||||||
|
chmod +x "$SCRIPT_FILE"
|
||||||
|
|
||||||
|
echo "Created files:"
|
||||||
|
echo " - $YAML_FILE"
|
||||||
|
echo " - $SCRIPT_FILE"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo "1. Edit $SCRIPT_FILE:"
|
||||||
|
echo " - Implement check() logic"
|
||||||
|
echo " - Implement fix() logic"
|
||||||
|
echo "2. Test script standalone:"
|
||||||
|
echo " - $SCRIPT_FILE check"
|
||||||
|
echo " - $SCRIPT_FILE fix"
|
||||||
|
echo "3. Edit $YAML_FILE:"
|
||||||
|
echo " - Update description"
|
||||||
|
echo " - Add dependencies in 'needs'"
|
||||||
|
echo " - Adjust include mode (when-required vs by-default)"
|
||||||
|
echo " - Update helpText"
|
||||||
|
echo "4. Test group:"
|
||||||
|
echo " - scope doctor list | grep ${GROUP_NAME}"
|
||||||
|
echo " - scope doctor run --only ${GROUP_NAME} --no-cache"
|
||||||
|
echo "5. Validate schema:"
|
||||||
|
echo " - jsonschema validate schema.json $YAML_FILE"
|
||||||
117
skills/scope/scripts/create-known-error.sh
Executable file
117
skills/scope/scripts/create-known-error.sh
Executable file
@@ -0,0 +1,117 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Create a new Scope known error with proper structure
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat << EOF
|
||||||
|
Usage: $0 <category> <error-name>
|
||||||
|
|
||||||
|
Create a new Scope known error definition with test file.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
category Error category (e.g., docker, ruby, git)
|
||||||
|
error-name Descriptive error name (e.g., colima-not-running)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$0 docker colima-not-running
|
||||||
|
$0 ruby gem-missing-file
|
||||||
|
$0 git cannot-lock-ref
|
||||||
|
|
||||||
|
The script will create:
|
||||||
|
- {config-root}/known-errors/{category}/{error-name}.yaml
|
||||||
|
- {config-root}/known-errors/{category}/{error-name}.txt
|
||||||
|
|
||||||
|
You will need to edit both files with actual content.
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check arguments
|
||||||
|
if [[ $# -ne 2 ]]; then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
CATEGORY="$1"
|
||||||
|
ERROR_NAME="$2"
|
||||||
|
|
||||||
|
# Determine base directory
|
||||||
|
if [[ -d "known-errors" ]]; then
|
||||||
|
BASE_DIR="known-errors"
|
||||||
|
elif [[ -d "config/known-errors" ]]; then
|
||||||
|
BASE_DIR="config/known-errors"
|
||||||
|
elif [[ -d "scope-config/known-errors" ]]; then
|
||||||
|
BASE_DIR="scope-config/known-errors"
|
||||||
|
else
|
||||||
|
echo "Error: Cannot find known-errors directory" >&2
|
||||||
|
echo "Run this from config root or known-errors parent directory" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create directory
|
||||||
|
DIR="${BASE_DIR}/${CATEGORY}"
|
||||||
|
mkdir -p "$DIR"
|
||||||
|
|
||||||
|
# Create YAML file
|
||||||
|
YAML_FILE="${DIR}/${ERROR_NAME}.yaml"
|
||||||
|
if [[ -f "$YAML_FILE" ]]; then
|
||||||
|
echo "Error: $YAML_FILE already exists" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat > "$YAML_FILE" << EOF
|
||||||
|
apiVersion: scope.github.com/v1alpha
|
||||||
|
kind: ScopeKnownError
|
||||||
|
metadata:
|
||||||
|
name: ${ERROR_NAME}
|
||||||
|
description: TODO: Brief description of what this error means
|
||||||
|
spec:
|
||||||
|
pattern: "TODO: regex pattern to match the error"
|
||||||
|
help: |
|
||||||
|
TODO: Clear explanation of the issue.
|
||||||
|
|
||||||
|
Steps to resolve:
|
||||||
|
1. First step
|
||||||
|
2. Second step
|
||||||
|
3. Where to get help if needed
|
||||||
|
|
||||||
|
If you need assistance, contact #help-channel in Slack.
|
||||||
|
# Uncomment to add automated fix:
|
||||||
|
# fix:
|
||||||
|
# prompt:
|
||||||
|
# text: TODO: User-friendly prompt asking permission
|
||||||
|
# commands:
|
||||||
|
# - TODO: command-to-fix
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create test file
|
||||||
|
TXT_FILE="${DIR}/${ERROR_NAME}.txt"
|
||||||
|
cat > "$TXT_FILE" << EOF
|
||||||
|
TODO: Paste actual error output here.
|
||||||
|
|
||||||
|
This should be the real error text that users see, including:
|
||||||
|
- The error message itself
|
||||||
|
- Surrounding context (lines before/after)
|
||||||
|
- Stack traces if applicable
|
||||||
|
- Command that failed
|
||||||
|
|
||||||
|
Example:
|
||||||
|
$ some-command that failed
|
||||||
|
Error: something went wrong
|
||||||
|
Details about the error here
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "Created files:"
|
||||||
|
echo " - $YAML_FILE"
|
||||||
|
echo " - $TXT_FILE"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo "1. Edit $TXT_FILE with actual error output"
|
||||||
|
echo "2. Test pattern: rg 'your-pattern' $TXT_FILE"
|
||||||
|
echo "3. Edit $YAML_FILE with:"
|
||||||
|
echo " - Proper description"
|
||||||
|
echo " - Correct pattern"
|
||||||
|
echo " - Clear help text"
|
||||||
|
echo " - Optional fix commands"
|
||||||
|
echo "4. Test: scope analyze logs --extra-config ${BASE_DIR%/known-errors*} $TXT_FILE"
|
||||||
|
echo "5. Validate: jsonschema validate schema.json $YAML_FILE"
|
||||||
Reference in New Issue
Block a user