| name | clash-routes |
| description | 查看指定进程的代理线路。通过 Mihomo API 查询当前活跃连接,显示进程匹配的规则和代理链路。用于确认某个进程(如 claude、chrome)走的是哪条订阅线路 |
| allowed-tools | Bash |
| metadata | {"argument-hint":"[进程名,默认显示全部]"} |
Clash 线路查看工具
查看当前活跃连接的代理线路信息,确认指定进程走的是哪条订阅/代理链。
用户传入的参数:$ARGUMENTS
如果用户没有传入参数,显示所有活跃连接(按进程分组)。
执行流程
第一步:获取 Mihomo API 凭证
读取 Clash Verge 配置获取 API secret:
SECRET=$(grep '^secret:' "$HOME/Library/Application Support/io.github.clash-verge-rev.clash-verge-rev/clash-verge.yaml" 2>/dev/null | awk '{print $2}')
[ -z "$SECRET" ] && SECRET=$(grep '^secret:' "$HOME/.config/clash/config.yaml" 2>/dev/null | awk '{print $2}')
echo "Secret: ${SECRET:-(未找到)}"
第二步:查询连接信息
优先使用 Unix socket,fallback 到 HTTP:
SOCKET="/var/tmp/verge/verge-mihomo.sock"
if [ -S "$SOCKET" ]; then
CONNECTIONS=$(curl -s --unix-socket "$SOCKET" "http://localhost/connections" -H "Authorization: Bearer $SECRET" 2>/dev/null)
else
CONTROLLER=$(grep '^external-controller:' "$HOME/Library/Application Support/io.github.clash-verge-rev.clash-verge-rev/clash-verge.yaml" 2>/dev/null | awk '{print $2}' | tr -d "'\"")
[ -z "$CONTROLLER" ] && CONTROLLER="127.0.0.1:9090"
CONNECTIONS=$(curl -s "http://$CONTROLLER/connections" -H "Authorization: Bearer $SECRET" 2>/dev/null)
fi
第三步:解析并展示线路
用 Python 解析 JSON,按进程分组显示:
import json, sys
data = json.loads(sys.stdin.read())
connections = data.get("connections", [])
process_filter = "参数中的进程名"
results = []
for conn in connections:
meta = conn.get("metadata", {})
process = meta.get("process", "unknown")
host = meta.get("host", "") or meta.get("destinationIP", "")
port = meta.get("destinationPort", "")
rule = conn.get("rule", "") + ("/" + conn.get("rulePayload", "") if conn.get("rulePayload") else "")
chains = conn.get("chains", [])
network = meta.get("network", "")
if process_filter and process_filter.lower() not in process.lower():
continue
results.append({
"process": process,
"host": f"{host}:{port}" if port else host,
"rule": rule,
"chains": " → ".join(reversed(chains)) if chains else "DIRECT",
"network": network.upper(),
})
collections defaultdict
grouped = defaultdict()
r results:
grouped[r[]].append(r)
process, conns (grouped.items()):
()
()
()
chain_stats = defaultdict(: {: , : ()})
c conns:
key =
chain_stats[key][] +=
chain_stats[key][].add(c[])
route, info (chain_stats.items(), key= x: -x[][]):
()
()
hosts = (info[])
(hosts) <= :
()
:
()
()
完整的一键执行命令
将上面的步骤组合成一个完整的 bash 命令执行:
SECRET=$(grep '^secret:' "$HOME/Library/Application Support/io.github.clash-verge-rev.clash-verge-rev/clash-verge.yaml" 2>/dev/null | awk '{print $2}')
SOCKET="/var/tmp/verge/verge-mihomo.sock"
FILTER="$ARGUMENTS"
if [ -S "$SOCKET" ]; then
DATA=$(curl -s --unix-socket "$SOCKET" "http://localhost/connections" -H "Authorization: Bearer $SECRET" 2>/dev/null)
else
CONTROLLER=$(grep '^external-controller:' "$HOME/Library/Application Support/io.github.clash-verge-rev.clash-verge-rev/clash-verge.yaml" 2>/dev/null | awk '{print $2}' | tr -d "'\"")
[ -z "$CONTROLLER" ] && CONTROLLER="127.0.0.1:9090"
DATA=$(curl -s "http://$CONTROLLER/connections" -H "Authorization: Bearer $SECRET" 2>/dev/null)
fi
if [ -z "$DATA" ] || echo "$DATA" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; [ $? -ne 0 ]; then
:
fi
echo "$DATA" | python3 -c "
import json, sys
from collections import defaultdict
data = json.loads(sys.stdin.read())
conns = data.get('connections', [])
filt = '$FILTER'.strip().lower()
results = []
for c in conns:
m = c.get('metadata', {})
proc = m.get('process', 'unknown')
if filt and filt not in proc.lower():
continue
host = m.get('host', '') or m.get('destinationIP', '')
port = m.get('destinationPort', '')
rule = c.get('rule', '')
rp = c.get('rulePayload', '')
if rp:
rule += '/' + rp
chains = c.get('chains', [])
chain_str = ' → '.join(reversed(chains)) if chains else 'DIRECT'
results.append({'process': proc, 'host': f'{host}:{port}' if port else host, 'rule': rule, 'chains': chain_str})
grouped = defaultdict(list)
for r in results:
grouped[r['process']].append(r)
if not grouped:
target = filt if filt else '任何进程'
print(f'未找到 {target} 的活跃连接')
sys.exit(0)
total = sum(len(v) for v in grouped.values())
print(f'共 {total} 个活跃连接,涉及 {len(grouped)} 个进程')
for proc, pconns in sorted(grouped.items()):
print(f'\n{\"=\"*60}')
print(f'进程: {proc} ({len(pconns)} 个连接)')
print(f'{\"=\"*60}')
chain_stats = defaultdict(lambda: {'count': 0, 'hosts': set()})
for c in pconns:
key = f'{c[\"rule\"]} → {c[\"chains\"]}'
chain_stats[key]['count'] += 1
chain_stats[key]['hosts'].add(c['host'])
for route, info in sorted(chain_stats.items(), key=lambda x: -x[1]['count']):
print(f' 线路: {route}')
print(f' 连接数: {info[\"count\"]}')
hosts = sorted(info['hosts'])
if len(hosts) <= 5:
print(f' 目标: {\", \".join(hosts)}')
else:
print(f' 目标: {\", \".join(hosts[:5])} ... (+{len(hosts)-5})')
print()
"
输出格式
按进程分组,每个进程显示:
- 线路:匹配规则 → 代理链路(如
ProcessName/claude → 🤖 AI → 🇯🇵 日本 东京)
- 连接数:该线路上的活跃连接数量
- 目标:连接的目标域名/IP
使用示例
/clash-routes - 查看所有进程的线路
/clash-routes claude - 只看 claude 进程
/clash-routes chrome - 只看 chrome 进程
/clash-routes telegram - 只看 telegram 进程
注意事项
- 只读操作,不修改任何配置
- 需要 Clash Verge Rev 或 mihomo 正在运行
- 通过 Unix socket 访问比 HTTP 更可靠(不受 external-controller 配置影响)
- 用中文输出所有信息