倧åŠçã®ããæ10äžã ã£ã坿¥åå ¥ããæãæã¡ã§æ60äžãŸã§è²ã£ããšããäŒç€Ÿéœåã®è§£éã§ãã£ãã0ã«ãªããŸãããããããå幎ã§Claude CodeèªåŸç°å¢ãäžããäœãçŽããä»ã¯æå120äžãå®å®ãããŠããŸããç°å¢ãæ¢ãŸããªããã°ã売äžãæ¢ãŸããªããããåŠãã§ããã¯ãã³ãŒãããå ã«ãé害ããã£ãŠãåããªãèšèšããåªå ããããã«ãªããŸããã
ãªããã®ä»çµã¿ãå¹ãã®ã
MCPãµãŒããŒãèœã¡ããšäœãèµ·ããŠããã
Claude Codeã䜿ã£ãèªåŸåãå§ããåœåããã¡ã°ãå«ããªé害ããããŸãããå€äžã«åãããããããMCPãµãŒããŒã®ã¿ã€ã ã¢ãŠãã§äžžããšæ¢ãŸã£ãŠãããã€ã§ããæèµ·ãããšãconnection refusedãã®ãã°ã ããç©ã¿äžãã£ãŠããŠãåå€ããäžåé²ãã§ããªããSNSæçš¿ã®èªååãªããšããããã¯ã©ã€ã¢ã³ãåãã®çŽåç©çæãæ¢ãŸã£ãŠãããšãã¯æ¬åœã«çŠããŸããã
MCPãšããã®ã¯ãClaude Codeããã©ãŠã¶æäœã»DBåç §ã»å€éšAPIåŒã³åºããªã©ãããŒã«ãšããŠäœ¿ãããã®ä»çµã¿ã§ããClaudeåŽãããããšãmcp__obsidian__searchãã®ãããªååã®ããŒã«ãåŒã¶ã ãã§ããããã®è£ã§ã¯ããŒã«ã«ããã»ã¹ããªã¢ãŒãHTTPãµãŒããŒãšã®éä¿¡ãèµ°ã£ãŠããŸãããã®ãµãŒããŒãäœããã®åå ã§å¿çããªããªããšãClaude Codeã¯ããŒã«åŒã³åºãããšã©ãŒãšããŠè¿ããã»ãã·ã§ã³å šäœã®æµããè©°ãŸããŸãã
åé¡ã¯ããªãæ¢ãŸã£ãããã ãã§ã¯ãããŸããã§ããã429ïŒã¬ãŒãå¶éïŒã§æ¢ãŸã£ãŠããã®ã«ããã®ãŸãŸ30ç§åŸã«ååŒã³åºãããâãŸã429âãŸãæ¢ãŸãããšããã«ãŒããç¹°ãè¿ãã±ãŒã¹ããããŸããã503ïŒãµãŒãã¹äžæåæ¢ïŒãªã®ã«ã401ïŒèªèšŒåãïŒãšåãå€å®ãããŠåèªèšŒãããŒãç¡é§ã«èµ°ãããã±ãŒã¹ããããŸãããã¹ããŒã¿ã¹ã³ãŒãããšã«æŠç¥ãå€ããªããã°ãã©ããªã«é«éãªã¢ãã«ã䜿ã£ãŠãç¡é§ã§ãã
ãäœæ¥ãã§ãªããç°å¢ããæŽããããšã®æ¬è³ª
æå120äžã®èªåŸç°å¢ãç¶æããŠããŠæ°ã¥ããã®ã¯ã皌ããå¢ããããã«è¿œå ããã³ãŒããããããæ¢ãŸããªãããã®ä»çµã¿ãã«è²»ãããæéã®ã»ãããé·æROIãé«ããšããããšã§ãã
æå60äžã®ããã¯ãã¿ã¹ã¯ãå¢ããã°çšŒããå¢ããããšæã£ãŠããŸãããè§£éãããŠ0ã«ãªã£ãããšãããäžåºŠäœãçŽãéçšã§èãæ¹ãå€ãããŸãããã¿ã¹ã¯ãå¢ãããŠããç°å¢ãäžå®å®ã ãšã¹ã«ãŒããããäžéã«åŒµãä»ãããããã€ã³ãã©çãªåé¡ã1ã€æœ°ãã ãã§ãæ¢åã¿ã¹ã¯å šäœã®å®èµ°çãäžãããŸãã
MCPã®ãã«ã¹ãã§ãã¯ããã®å
žåäŸã§ãã~/.claude/scripts/hooks/mcp-health-check.jsãããã¯ã«çµã¿èŸŒãããšã§ãClaude CodeãããŒã«ãåŒã¶åã«HTTPãããŒããèµ°ãããå¿çã¹ããŒã¿ã¹ããšã«ãå³ãããã¯ããããã¯ãªãåŸã«å詊è¡ããreconnectã³ãã³ããå®è¡ããŠåãããŒãããæ¯ãåããŸããå€å®çµæã¯~/.claude/mcp-health-cache.jsonã«æ°žç¶åãããããã³ã³ããã¹ããå§çž®ãããŠãå¥å
šæ§ã®èšé²ã¯åŒãç¶ãããŸãã
ãªããã£ãã·ã¥ããã³ã³ããã¹ãå§çž®å¯Ÿçãã«ãªãã
Claude Codeã®ã»ãã·ã§ã³ã¯é·æéåãããšäŒè©±å±¥æŽãå§çž®ãããŸããéå»ã«ããã®ãµãŒããŒã¯èœã¡ãŠããããšããæ å ±ããã£ãŠããå§çž®åŸã®ã³ã³ããã¹ãã«ã¯ãããæ®ããŸãããçµæãšããŠããã§ã«äžå¥å šãšå€æããŠãããµãŒããŒã«å¯ŸããŠäœåºŠãããŒã«åŒã³åºãã詊ã¿ããã®ãã³ã«ãšã©ãŒãåãåããšããç¡é§ãçºçããŸãã
ãã¡ã€ã«ããŒã¹ã®ãã£ãã·ã¥ã¯ã³ã³ããã¹ããšã¯ç¬ç«ããŠããŸãã~/.claude/mcp-health-cache.jsonã¯ã»ãã·ã§ã³ãå§çž®ãããããšæ¶ããŸãããæ¬¡ã®ã¿ãŒã³ã§ãã«ã¹ãã§ãã¯ããã¯ãèµ°ã£ããšãããã¡ã€ã«ããååã®ç¶æ
ãèªã¿èŸŒã¿ãnextRetryAtãéãããŸã§ã¯åãããŒãããããªãã§å³ãããã¯ããŸããã³ã³ããã¹ããŠã£ã³ããŠã®å€åŽã«ç¶æ
ãæã€ããšããã¢ã€ãã¢ãæ¬è³ªçã«éèŠã§ãã
èªè ã®ãããã誀解
ããšã©ãŒãã³ããªã³ã°ã¯ClaudeåŽã®ããã³ããã§ããã°ïŒããšæãæ¹ãããŸããå®éã«è©ŠããŸãããããã®ããŒã«ããšã©ãŒã«ãªã£ããå¥ã®æ¹æ³ã詊ããŠããšããã·ã¹ãã ããã³ããã¯ãåçºã®ãšã©ãŒã«ã¯ãããªãã«å¹ããŸããããããMCPãµãŒããŒãããŠã³ããŠããŠé£ç¶ããŠå€±æããç¶æ³ã§ã¯ãã¢ãã«ã¯ã詊ã¿ãŠãããããšã«å€§éã®ããŒã¯ã³ãæ¶è²»ããŸãããŸããæ¬¡ã®ã¿ãŒã³ã§ãŸãåããµãŒããŒãåŒãŒããšããŸããããµãŒããŒãæ»ãã§ããããšããäºå®ãã³ã³ããã¹ãå€ã«æžãåºããŠãããã¯ã¬ãã«ã§ãããã¯ããã»ããå§åçã«ã¯ãªãŒã³ã§ãã
ããäžã€ãããã誀解ã¯ãMCPèªäœã®ãªãã©ã€èšå®ã§è¶³ããããšãããã®ã§ããMCPãããã³ã«ã«ã¯ãã©ã³ã¹ããŒãå±€ã®ãªãã©ã€ã¯ãããŸãããã¹ããŒã¿ã¹ã³ãŒããèªãã§æŠç¥ãåãæ¿ããæ©èœã¯ãããŸããã429ãš503ã§ã¯é©åãªããã¯ãªãæéãç°ãªããŸããã401/403ã¯ãªãã©ã€ã§ãªãåèªèšŒãå¿ èŠãªã±ãŒã¹ã§ãããã®æ¯ãåããã¢ããªã±ãŒã·ã§ã³å±€ã§å®è£ ããã®ãããã®ããã¯ã®åœ¹å²ã§ãã
å šäœã®æµã
ããã¯ãä»å ¥ããã¿ã€ãã³ã°
mcp-health-check.jsã¯Claude Codeã®2çš®é¡ã®ããã¯ã€ãã³ãã«å¿çããŸããã³ãŒãåé ã®ã³ã¡ã³ãïŒ7ã12è¡ç®ïŒã«ãã®ãŸãŸæžãããŠããŸãã
- PreToolUse: probe MCP server health before MCP tool execution
- PostToolUseFailure: mark unhealthy servers, attempt reconnect, and re-probe
PreToolUseã¯ããŒã«ãå®è¡ãããåã«åŒã°ããŸããããã§ãããŒããæã¡ããµãŒããŒãçããŠãããã確èªããŠããå®è¡ãèš±å¯ããããexit code 2ã§ãããã¯ããããæ±ºããŸããPostToolUseFailureã¯ããŒã«ããšã©ãŒãè¿ããããšã«åŒã°ããŸãããšã©ãŒããã¹ããè§£æããŠå€±æãã¿ãŒã³ãç¹å®ãããµãŒããŒãäžå¥å
šãšããŒã¯ããŠreconnectã詊ã¿ãŸãã
å šäœãããŒå³
Claude Code ã mcp__* ããŒã«ãåŒã¶
â
⌠PreToolUse ããã¯èµ·å
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â mcp-health-check.js â
â â
â â mcp-health-cache.json ãèªã â
â status=healthy ã〠expiresAt ãæªæ¥ïŒ â
â YES ââââââââââââââââââââââââââââââââââââââââââ â exit 0
â NO â â ïŒããŒã«å®è¡ãžïŒ
â â
â â¡ nextRetryAt ãæªæ¥ïŒunhealthy ã¯ãŒã«ããŠã³äžïŒïŒ â
â YES â ããã㯠âââââââââââââââââââââââââââââââ â exit 2
â NO â â ïŒããŒã«ãã¹ãããïŒ
â â
â ⢠ãããŒãå®è¡ â
â HTTPãµãŒã㌠â GET ãªã¯ãšã¹ãïŒ5ç§ã¿ã€ã ã¢ãŠãïŒ â
â stdioãµãŒã㌠â ããã»ã¹èµ·åïŒ5ç§çå確èªïŒ â
â â
â ã¬ã¹ãã³ã¹ã®ã¹ããŒã¿ã¹ã³ãŒãå€å® â
â ââââââââââââââââââââââââââââââââââââââââââââ â
â â ECONNREFUSED / ENOTFOUND / ã¿ã€ã ã¢ãŠã ââââ â
â â â å³ markUnhealthy & exit 2 â â
â âââââââââââââââââââââââââââââââââââââââââââ†â
â â 401 / 403 / 429 / 503 ââââ â
â â â reconnect ã³ãã³ããå®è¡ â â
â â â æåããã°åãããŒã â â
â â â åãããŒã OK â markHealthy & exit 0 â â
â â â åãããŒã NG â markUnhealthy & exit 2â â
â âââââââââââââââââââââââââââââââââââââââââââ†â
â â 200 ç³» / 400 / 401 / 403 / 405 / 406 â â
â âïŒ"å°éã§ãã"蚌æãšã㊠healthy æ±ãïŒ ââââ â exit 0
â ââââââââââââââââââââââââââââââââââââââââââââ â
â â
â â£ ç¶æ
ã mcp-health-cache.json ã«æžãåºã â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â
⌠ããŒã«å®è¡åŸã«ãšã©ãŒãåºãå Žå
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â PostToolUseFailure ãã㯠â
â ãšã©ãŒããã¹ãã FAILURE_PATTERNS ãšç
§å â
â failureCode ç¹å® â markUnhealthy â reconnectè©Šè¡ â
â â åãããŒã OK ãªã markHealthy â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
宿°ãèªãèšèšææ³
å®éã®ã³ãŒããèªããšãèšèšã®æå³ãæ°å€ã«è¡šããŠããŸãïŒ22ã26è¡ç®ïŒã
const DEFAULT_TTL_MS = 2 * 60 * 1000; // 2å
const DEFAULT_TIMEOUT_MS = 5000; // 5ç§
const DEFAULT_BACKOFF_MS = 30 * 1000; // 30ç§ïŒååããã¯ãªãïŒ
const MAX_BACKOFF_MS = 10 * 60 * 1000; // 10åïŒäžéïŒ
TTLã2åãšããå€ã¯ãããããŒããæ¯åèµ°ãããããªãããå€ãçµæãæã¡ãããããªãããšãããã¬ãŒããªãã§ããClaude Codeã§é£ç¶ããŠããŒã«ãåŒã¶å Žé¢ã§ã¯ã2å以å ã«åããµãŒããŒãžè€æ°åã¢ã¯ã»ã¹ããããšã¯çãããããŸããããã£ãã·ã¥ãããã°ããã®ãã³ã«HTTPãããŒããèµ°ãããå¿ èŠããªããã¬ã€ãã³ã·ãäžãããŸãã
ã¿ã€ã ã¢ãŠã5ç§ã¯ãããŒã«ã«ã®stdioãµãŒããŒããããã»ã¹ãšããŠèµ·åã§ãããããšã確èªããããã®éŸå€ã§ããèµ·åå®äºã§ã¯ãªãã5ç§éããã»ã¹ãçãç¶ãããããããšããã£ãŠæ£åžžãšå€æããŸãïŒ428ã472è¡ç®ã®probeCommandServerã®ã¿ã€ããŒããžãã¯ïŒãèµ·åã«5ç§ä»¥äžãããéããµãŒããŒã§ããç°å¢å€æ°ECC_MCP_HEALTH_TIMEOUT_MSã§èª¿æŽã§ããŸãã
ææ°ããã¯ãªãã®èšç®åŒ
markUnhealthy颿°ïŒ213ã229è¡ç®ïŒã®ããã¯ãªãèšç®ã¯1è¡ã«å§çž®ãããŠããŸãã
const nextRetryDelay = Math.min(
backoffBase * (2 ** Math.max(failureCount - 1, 0)),
MAX_BACKOFF_MS
);
backoffBaseã¯ããã©ã«ã30ç§ã§ããfailureCountã1ã®ãšãã¯2 ** 0 = 1ãªã®ã§30ç§ã2åç®ã¯2 ** 1 = 2ã§60ç§ã3åç®ã¯120ç§ã4åç®ã¯240ç§ãšåå¢ããæå€§600ç§ïŒ10åïŒã§é æã¡ã«ãªããŸããæåã®å€±æã§30ç§åŸ
ã¡ãããã§å埩ããªããã°1åã2åã4åãšééãåºããŠãããããèœã¡ãŠãããµãŒããŒã«å¯ŸããŠãããŒããç¡é§ã«æã¡ç¶ããäºæ
ãé¿ããããŸãã
ã¹ããŒã¿ã¹ã³ãŒãã®æ¯ãåãããžãã¯
HEALTHY_HTTP_CODESã®å®çŸ©ïŒ32è¡ç®ïŒãäžèŠããŠå¥åŠã«èŠããŸãã
const HEALTHY_HTTP_CODES = new Set([
200, 201, 202, 204,
301, 302, 303, 304, 307, 308,
400, 401, 403, 405, 406
]);
400çªå°ã®äžéšããæ£åžžãæ±ãã«ãªã£ãŠããŸããã³ãŒãã®ã³ã¡ã³ãïŒ29ã32è¡ç®ïŒã«ãã®çç±ãæžãããŠããŸãã
// The preflight HTTP probe only checks reachability; it does not have access to
// Claude Code's stored OAuth bearer token. Treat auth-gated responses as
// reachable so the real MCP client can attempt the authenticated call. A
// Streamable HTTP MCP server can also return 406 to a bare GET that omits
// Accept: text/event-stream; that still proves the endpoint is alive.
ããªãã©ã€ããããŒãã¯OAuthããŒã¯ã³ãæããªãGETãªã¯ãšã¹ãã§ããèªèšŒãå¿
èŠãªãšã³ããã€ã³ãã¯401ã403ãè¿ããŸãããããã¯ãèªèšŒãªãã®ãªã¯ãšã¹ããæ£ããæåŠããŠãããããšãæå³ãããµãŒããŒãçããŠãã蚌æ ã§ãã406ã¯Accept: text/event-streamããããªãã®ãªã¯ãšã¹ããžã®æ£åžžãªæåŠå¿çã§ãããããã¯ãã¹ãŠãå°éã§ãããããšã蚌æããã³ãŒãã§ãã
äžæ¹ãreconnectã®å¯Ÿè±¡ã«ãªãã®ã¯ãµãã»ããã§ãïŒ33è¡ç®ïŒã
const RECONNECT_STATUS_CODES = new Set([401, 403, 429, 503]);
401ãš403ã¯ãããŒãã³ã³ããã¹ãã§ã¯ãå°éã§ãããæ±ãã§ãããå®éã®ããŒã«åŒã³åºãåŸã«è¿ã£ãŠããå Žåã¯æå³ãéããŸããPostToolUseFailureã§detectFailureCode颿°ãåŒã°ãããšãããšã©ãŒã¡ãã»ãŒãžäžã®401/403ã¯ãèªèšŒå€±æããšããŠæããããreconnectã®åŒãéã«ãªããŸãããããŒãå€å®ãšå€±æåŸã®å€å®ãç°ãªãã¬ã€ã€ãŒã§åããŠããã®ããã€ã³ãã§ãã
FAILURE_PATTERNSãšããã¹ãè§£æ
MCPã®ãšã©ãŒã¯HTTPã¹ããŒã¿ã¹ã³ãŒããšããŠè¿ã£ãŠãããšã¯éããŸãããstdioãµãŒããŒã®ãšã©ãŒã¯ããã¹ãã§è¿ã£ãŠããŸããFAILURE_PATTERNSïŒ34ã40è¡ç®ïŒã¯ãã®ããã¹ããã倱æçš®å¥ãæ£èŠè¡šçŸã§ç¹å®ããä»çµã¿ã§ãã
const FAILURE_PATTERNS = [
{ code: 401, pattern: /\b401\b|unauthori[sz]ed|auth(?:entication)?\s+(?:failed|expired|invalid)/i },
{ code: 403, pattern: /\b403\b|forbidden|permission denied/i },
{ code: 429, pattern: /\b429\b|rate limit|too many requests/i },
{ code: 503, pattern: /\b503\b|service unavailable|overloaded|temporarily unavailable/i },
{ code: 'transport', pattern: /ECONNREFUSED|ENOTFOUND|EAI_AGAIN|timed? out|socket hang up|connection (?:failed|lost|reset|closed)/i }
];
ãunauthorizedããauth expiredããrate limitããECONNREFUSEDããªã©ã®æååãå«ãŸãããšã©ãŒã¡ãã»ãŒãžãããèªåçã«å€±æã³ãŒããç¹å®ããŸããtransportã³ãŒãã¯HTTPã¹ããŒã¿ã¹ã§ã¯ãªãããããã¯ãŒã¯æ¥ç¶èªäœãåããŠããã±ãŒã¹ã§ããããã¯ããµãŒããŒã«å°éããã§ããªããç¶æ
ã§ãããreconnectãããããã¯ãªããåªå
ãããŸãã
ãã£ãã·ã¥ãã¡ã€ã«ã®æ§é
~/.claude/mcp-health-cache.jsonã¯saveState颿°ïŒ95ã102è¡ç®ïŒãJSON.stringifyã§æŽåœ¢ããŠæžãåºããŸããå®éã®ãã¡ã€ã«ã¯æ¬¡ã®ãããªæ§é ã«ãªã£ãŠããŸãã
{
"version": 1,
"servers": {
"obsidian": {
"status": "healthy",
"checkedAt": 1753666200000,
"expiresAt": 1753666320000,
"failureCount": 0,
"lastError": null,
"lastFailureCode": null,
"nextRetryAt": 1753666200000,
"lastRestoredAt": 1753666200000,
"source": "~/.claude/settings.json"
},
"agentmemory": {
"status": "unhealthy",
"checkedAt": 1753665900000,
"expiresAt": 1753665900000,
"failureCount": 3,
"lastError": "ECONNREFUSED 127.0.0.1:3001",
"lastFailureCode": "transport",
"nextRetryAt": 1753666020000,
"lastRestoredAt": null
}
}
}
expiresAtãæªæ¥ã§ããã°åãããŒããªãã§ãã¹ãnextRetryAtãæªæ¥ã§ããã°å詊è¡ããããªãã§ãããã¯ããšãã2段æ§ãã®ã²ãŒãã«ãªã£ãŠããŸããfailureCountãèç©ããã»ã©ããã¯ãªãã䌞ã³ããããç¹°ãè¿ãããŠã³ããŠãããµãŒããŒã¯ã©ãã©ã詊è¡ééãåºãããŸããlastRestoredAtã¯ããã€åŸ©æ§ããããã®èšé²ã§ã皌åçã®äºåŸåæã«äœ¿ããŸãã
sourceãã£ãŒã«ãã¯èšå®ãã¡ã€ã«ã®ãã¹ã§ããconfigPaths颿°ïŒ54ã72è¡ç®ïŒãã«ã¬ã³ããã£ã¬ã¯ããªã®.claude.jsonâã«ã¬ã³ãã®.claude/settings.jsonâããŒã ã®.claude.jsonâããŒã ã®.claude/settings.jsonãšããé ã§æ€çŽ¢ããæåã«èŠã€ãã£ããã®ã䜿ãããŸãããããžã§ã¯ãããšã®MCPèšå®ãæã£ãŠããå Žåã§ãæ£ããã³ã³ãã£ã°ãåç
§ãããŸãã
reconnectã³ãã³ãã®èšå®æ¹æ³
reconnectCommand颿°ïŒ516ã527è¡ç®ïŒã¯ç°å¢å€æ°ããã³ãã³ããèªã¿ãŸãã
const key = `ECC_MCP_RECONNECT_${String(serverName).toUpperCase().replace(/[^A-Z0-9]/g, '_')}`;
const command = process.env[key] || process.env.ECC_MCP_RECONNECT_COMMAND || '';
ãµãŒããŒåãagentmemoryãªãECC_MCP_RECONNECT_AGENTMEMORYãšããç°å¢å€æ°ãæ¢ããŸããèŠã€ãããªããã°ECC_MCP_RECONNECT_COMMANDãšããã°ããŒãã«ãªãã©ãŒã«ããã¯ã䜿ããŸããã³ãã³ãæååã«{server}ãå«ãŸããŠããã°ããµãŒããŒåã«å±éãããŸãïŒ524ã526è¡ç®ïŒã
ããšãã°ããã»ã¹ãããŒãžã£ãšããŠPM2ã䜿ã£ãŠãããªããæ¬¡ã®ããã«èšå®ã§ããŸãã
export ECC_MCP_RECONNECT_COMMAND="pm2 restart {server}"
ããã§agentmemoryãµãŒããŒã401ã503ãè¿ãããšããèªåçã«pm2 restart agentmemoryãèµ°ããåãããŒãã§æ£åžžã確èªããŠããããŒã«å®è¡ãèš±å¯ããŸããreconnectãæåããå ŽåãmarkHealthyã®restoredByãã£ãŒã«ãã«reconnect-commandãšèšé²ãããŸãïŒ607ã609è¡ç®ïŒã
ãããŸã§ã§ããã®ããã¯ã®å šäœåãã€ããããšæããŸããæ¬¡ã®ã»ã¯ã·ã§ã³ã§ã¯å®éã®ã€ãŸãããã€ã³ããšãèšå®ã§é¥ããããçœ ã«ã€ããŠæãäžããŸãã
å®è£ ã®è©³çް
extractMcpTarget â ãµãŒããŒåãããŒã«åããåè§£ãã
ããã¯ãæåã«è¡ãã®ã¯ãã©ã®MCPãµãŒããŒãžã®åŒã³åºããããç¹å®ããããšã§ããextractMcpTarget颿°ïŒ133ã167è¡ç®ïŒããããæ
ããŸãã
if (!toolName.startsWith('mcp__')) {
return null;
}
const segments = toolName.slice(5).split('__');
if (segments.length < 2 || !segments[0]) {
return null;
}
return {
server: segments[0],
tool: segments.slice(1).join('__')
};
mcp__obsidian__search_notesãšããååãããã°ãslice(5)ã§obsidian__search_notesã«ãªããsplit('__')ã§['obsidian', 'search', 'notes']ã«ãªããŸããsegments[0]ããµãŒããŒåãæ®ããã¢ã³ããŒã¹ã³ã¢2ã€ã§çµåãããã®ãããŒã«åã§ãã
ãã ãããŒã«åã®è§£æã¯ãããŸã§ãã©ãŒã«ããã¯ã§ããå
ã«æç€ºçãªserverãã£ãŒã«ããæ¢ããŸãïŒ135ã145è¡ç®ïŒã
const explicitServer = input.server
|| input.mcp_server
|| input.tool_input?.server
|| input.tool_input?.mcp_server
|| input.tool_input?.connector
|| null;
è€æ°ã®ãã¹ã蟿ãã®ã¯ãããã¯ã€ãã³ãã®ã¹ããŒããClaude Codeã®ããŒãžã§ã³ãæ¥ç¶æ¹åŒïŒHTTP/stdioïŒã«ãã£ãŠãã£ãŒã«ãäœçœ®ããããããšãããããã§ããããã«JSONããŒã¹ã倱æããŠtruncatedãã©ã°ãç«ã£ãå Žåã¯ãextractMcpTargetFromRawïŒ169ã179è¡ç®ïŒãçã®æååã«æ£èŠè¡šçŸãåœãŠãŠåãæ
å ±ãåãåºããŸããããŒã¹ã§ããªããŠã諊ããªãèšèšã§ãã
handlePreToolUse ã®2段ã²ãŒã
handlePreToolUseïŒ567ã630è¡ç®ïŒã¯åçŽã«èŠããŸããã2段ã®ãã£ãã·ã¥ãã§ãã¯ãé£ç¶ããŠããŸãã
// 第1ã²ãŒã: healthy ãã€ãã£ãã·ã¥æå¹ â ãããŒããªãã§éé
if (previous.status === 'healthy' && Number(previous.expiresAt || 0) > now) {
return { rawInput, exitCode: 0, logs };
}
// 第2ã²ãŒã: unhealthy ãã€ã¯ãŒã«ããŠã³äž â ãããŒããªãã§ãããã¯
if (previous.status === 'unhealthy' && Number(previous.nextRetryAt || 0) > now) {
logs.push(
`[MCPHealthCheck] ${target.server} is marked unhealthy until ${new Date(previous.nextRetryAt).toISOString()}; skipping ${target.tool || 'tool'}`
);
return { rawInput, exitCode: shouldFailOpen() ? 0 : 2, logs };
}
ãhealthy ã〠TTL å ãã¯ééããunhealthy ãã€ããã¯ãªãäžãã¯ãããã¯ãã©ã¡ãã§ããªãå ŽåïŒunhealthyã ãããã¯ãªããåããããŸãã¯ãã£ãã·ã¥ã空ïŒã®ã¿ãããŒããå®è¡ããŸããé«é »åºŠã«ããŒã«ãåŒã¶ã»ãã·ã§ã³ã§ã¯ã»ãšãã©ã®åŒã³åºãã第1ã²ãŒãã§ééãããããHTTPãªã¯ãšã¹ãã®ã¬ã€ãã³ã·ã¯å®è³ªãŒãã§ãã
reconnectã®èµ·åæ¡ä»¶ãåæ§ã«æ³šæãå¿ èŠã§ãïŒ601ã603è¡ç®ïŒã
let reconnect = { attempted: false, success: false, reason: 'probe failed' };
if (probe.failureCode || previous.status === 'unhealthy') {
reconnect = attemptReconnect(target.server);
ããããŒãã倱æãããã ãã§ã¯reconnectã¯èµ°ããŸãããprobe.failureCodeïŒreconnectãæå¹ãªã¹ããŒã¿ã¹ã³ãŒãïŒãŸãã¯ãååããæ¢ã«unhealthyããšããæ¡ä»¶ãå¿
èŠã§ããECONNREFUSEDã§æ¥ç¶äžèœãªã®ã«reconnectã³ãã³ããæ¯åå®è¡ããã®ã¯ç¡é§ã§ããã429ã503ã®ããã«ãµãŒããŒã¯ååšããã䜿ããªãç¶æ
ã§ã¯ç©æ¥µçã«åæ¥ç¶ã詊ã¿ãããšãã倿ãããã«èŸŒããããŠããŸãã
probeCommandServer â stdioãµãŒããŒã®ã5ç§çå確èªã
HTTPãµãŒããŒã¯requestHttpã§GETãæãŠã°ããããŸããstdioãµãŒããŒã¯ã©ã確èªããããprobeCommandServerïŒ301ã481è¡ç®ïŒã®ã¢ãããŒãã¯ç¬ç¹ã§ãã
å®éã«ããã»ã¹ãèµ·åããŠ5ç§éèŠå®ããçãç¶ããŠããã°æ£åžžããã®åã«çµäºãããç°åžžã
timer = setTimeout(() => {
// ã¿ã€ã ã¢ãŠãå°é = 5ç§éããã»ã¹ãçå â æ£åžž
// ãã ã: ããŒãããããã·ã³ã§ã¯exitã€ãã³ããã¿ã€ããŒãã
// ãããã«é
ããŠå±ãããšããããé«éã¯ã©ãã·ã¥ãèŠéããªããã
// ããã»ã¹ã®ç¶æ
ãå確èªãã
if (child.exitCode !== null || child.signalCode !== null) {
attemptFinish({
ok: false,
statusCode: child.exitCode,
reason: stderr.trim() || `process exited before handshake (...)`
});
return;
}
// SIGTERMã§çµäºããã200msåŸã«SIGKILLã§ç¢ºå®ã«å§æ«ãã
child.kill('SIGTERM');
setTimeout(() => {
try { child.kill('SIGKILL'); } catch { /* ignore */ }
}, 200).unref?.();
attemptFinish({
ok: true,
statusCode: null,
reason: `${serverName} accepted a new stdio process`
});
}, timeoutMs);
ã³ã¡ã³ãã®éãããé«éã¯ã©ãã·ã¥ãµãŒããŒãã®åé¡ã¯å®éã«åä»ã§ããèµ·åçŽåŸã«ããã»ã¹ãçµäºããå Žåãexitã€ãã³ããšã¿ã€ããŒã®ã³ãŒã«ããã¯ãã»ãŒåæã«å°éããå¯èœæ§ããããŸããã¿ã€ããŒãå
ã«èµ°ãã°child.exitCodeãã§ãã¯ãªãã«ok: trueãè¿ããŠããŸããŸãããã®å¯ŸçãšããŠãã¿ã€ããŒã³ãŒã«ããã¯å
ã§child.exitCode !== nullãæ¹ããŠç¢ºèªãããã§ã«æ»ãã§ããã°ok: falseã«èœãšããŸãã
Windowsã§ã®æåãäœã蟌ãŸããŠããŸãïŒ329ã334è¡ç®ã438ã461è¡ç®ïŒãnpxã®ããã«æ¡åŒµåãªãã®ã³ãã³ãã¯Windowsã§ã¯npx.cmdãšããŠè§£æ±ºãããå¿
èŠãããããããNode 18.20以éã¯CVE-2024-27980察å¿ã§shellçµç±ã§ã®.cmdå®è¡ãå¶éãããŸãããã³ãŒãã¯candidatesé
åã«command.cmd/command.exe/command.batã®ãã©ãŒã«ããã¯ã䞊ã¹ãENOENTæã«æ¬¡ã®åè£ãé çªã«è©ŠããŸãããŸã.cmd/.batãã¡ã€ã«ã¯shellçµç±ã§å®è¡ããŸãããã³ãã³ãæååã«ã·ã§ã«ã¡ã¿ãã£ã©ã¯ã¿ïŒ&|<>^%()çïŒãå«ãŸããŠããã°shellçµç±ãæåŠããå®å
šåŒãæã£ãŠããŸãïŒ339è¡ç®ãUNSAFE_SHELL_CHARSïŒã
failureSummary â ãšã©ãŒããã¹ãã®å šæ¹äœåé
failureSummaryïŒ231ã244è¡ç®ïŒãå°å³ã«éèŠã§ãã
const pieces = [
typeof input.error === 'string' ? input.error : '',
typeof input.message === 'string' ? input.message : '',
typeof input.tool_response === 'string' ? input.tool_response : '',
typeof output === 'string' ? output : '',
typeof output?.output === 'string' ? output.output : '',
typeof output?.stderr === 'string' ? output.stderr : '',
typeof input.tool_input?.error === 'string' ? input.tool_input.error : ''
].filter(Boolean);
return pieces.join('\n');
MCPã®ãšã©ãŒããã¹ããã©ã®ãã£ãŒã«ãã«å
¥ããã¯ããµãŒããŒã®å®è£
ãšClaude Codeã®ããŒãžã§ã³ã«ãã£ãŠç°ãªããŸããerrorã«å
¥ãã±ãŒã¹ãtool_responseã«å
¥ãã±ãŒã¹ãoutput.stderrã«å
¥ãã±ãŒã¹ããã¹ãŠå®éã«ééããŸãããpieces.join('\n')ã§å
šãã£ãŒã«ããçµåããæååãdetectFailureCodeã«æž¡ãããšã§ãã©ã®ãã£ãŒã«ãã«ãšã©ãŒããã¹ããå
¥ã£ãŠããŠãæ£èŠè¡šçŸã§ãã£ããã§ããŸãã
fail-open ãšPostToolUseFailureã®exit code
shouldFailOpenïŒ557ã559è¡ç®ïŒã¯ç°å¢å€æ°äžè¡ã§ãã
function shouldFailOpen() {
return /^(1|true|yes)$/i.test(String(process.env.ECC_MCP_HEALTH_FAIL_OPEN || ''));
}
unhealthyãªãµãŒããŒãžã®ããŒã«åŒã³åºããéãèšå®ã§ããéçºæã«ãããã¯ã¯åããŠããããããã¯ããªãã§æ§åãèŠããããšãã«äœ¿ããŸããPreToolUseã§ã¯exit 2ïŒãããã¯ïŒã®ä»£ããã«exit 0ïŒééïŒãè¿ããŸãã
PostToolUseFailureã®exitCodeã¯åžžã«0ã§ãïŒ677è¡ç®ïŒãããã¯èšèšäžéèŠãªç¹ã§ã倱æåŸããã¯ã¯ããŒã«å®è¡ã®ãããã¯ã§ã¯ãªãç¶æ
ã®èšé²ãç®çã§ãããã§ã«å€±æããããŒã«åŒã³åºãã®exit codeãå€ããŠãæå³ã¯ãªãããã°ãšãã£ãã·ã¥æŽæ°ã ããè¡ã£ãŠæ¬¡ã®PreToolUseã«ç¶æ
ãåŒãæž¡ããŸãã
ç§ãè©°ãŸã£ã話
ãã®ä»çµã¿ãèªåã®ç°å¢ã«çµã¿èŸŒãã§ããå®å®ãããŸã§ã3ã€ã®å Žé¢ã§ããããŸãããçç¶ãšåå ãšçŽãæ¹ãé çªã«æžããŸãã
è©°ãŸãâ : stdioãµãŒããŒããèµ·åã§ãããã®ã«æ¯åãšã©ãŒã«ãªã£ã
çç¶: agentmemoryãµãŒããŒãstatus: healthyã§ãã£ãã·ã¥ã«æ®ã£ãŠããã®ã«ãå®éã®ããŒã«åŒã³åºãã§ã¯ECONNREFUSEDãè¿ã£ãŠããããã«ã¹ãã§ãã¯ã®æå³ããªãããã«èŠããã
åå : stdioãµãŒããŒã®ãããŒãã¯ãããã»ã¹ã5ç§éçåãããããšã確èªããã ãã§ããããã»ã¹ãèµ·åããŠ5ç§åŸã«SIGTERMã§çµäºããããã®åŸã«æ¬ç©ã®ããŒã«åŒã³åºããæ¥ããšãããµãŒããŒã¯ãŸããŒãããèµ·åããŸãããã ãæ¬ç©ã®èµ·åã§ã¯ãMCPã®ãã³ãã·ã§ã€ã¯ïŒstdioã§ã®JSONã¡ãã»ãŒãžäº€æïŒãå®äºãããŸã§ã«æéãããããŸãããã®ãã³ãã·ã§ã€ã¯äžã«å¥ã®ãšã©ãŒãèµ·ããŠããŸããã
å
·äœçã«ã¯ãç°å¢å€æ°ANTHROPIC_API_KEYãèšå®ãããŠããªããŸãŸèµ·åã詊ã¿ããµãŒããŒããèµ·åçŽåŸã«ã¯ã©ãã·ã¥ããŠããŸããããããŒãæã¯5ç§éçãç¶ããã®ã§healthyã«èšé²ãããŸããããå®éã®ãã³ãã·ã§ã€ã¯æã«ã¯æ°ç§åŸã«ã¯ã©ãã·ã¥ãããšããç¶æ
ã§ããã
çŽãæ¹: ãµãŒããŒããã»ã¹ã®ç°å¢å€æ°ãæã£ãŠããããå
ã«ç¢ºèªãããmcp-health-check.jsã¯config.envãããã»ã¹ã®ç°å¢å€æ°ã«ããŒãžããŠèµ·åããŸãïŒ306ã309è¡ç®ïŒãæ¬çªã®èµ·åãšåãç°å¢å€æ°ãèšå®ãã¡ã€ã«ã®envã»ã¯ã·ã§ã³ã«å
¥ã£ãŠãããã確èªããããšãå
決ã§ããããããŒãã§æ£åžžããšãããŒã«åŒã³åºãã§æ£åžžãã¯å³å¯ã«ã¯å¥ã®å€å®ã§ãããšçè§£ããŠããããããããããã°ã®æ¹åãæ±ºãŸããŸããã
è©°ãŸãâ¡: reconnectãªãã®503ãç©ã¿éãªã£ãŠãµãŒããŒãé·æé䜿ããªãã£ã
çç¶: Obsidianã®ãªã¢ãŒããµãŒããŒãå€éã«äžæçã«503ãè¿ãããç¿æç¢ºèªãããšããµãŒããŒèªäœã¯ãšã£ãã«åŸ©æ§ããŠããã®ã«ãmcp-health-cache.jsonã®nextRetryAtãæªæ¥ã®æå»ãæããŠããŠãã£ãšãããã¯ãããŠããã
åå : RECONNECT_STATUS_CODESã«503ãå«ãŸããŠããããã503ãæ¥ããšreconnectã詊ã¿ãŸãããããECC_MCP_RECONNECT_OBSIDIANãECC_MCP_RECONNECT_COMMANDãèšå®ããŠããŸããã§ãããreconnectã³ãã³ãããªããã°attemptReconnectã¯attempted: falseãè¿ãïŒ531è¡ç®ïŒãmarkUnhealthyã ããèµ°ã£ãŠfailureCountãèç©ããŸãã503ã3忥ããšfailureCount=3ã«ãªããããã¯ãªãã¯30 * (2 ** 2) = 120ç§ã次ã®30åã§5忥ãã°failureCount=5ã§30 * (2 ** 4) = 480ç§ãå€éã«åæ°åãšã©ãŒãç©ã¿éãªã£ãçµæãããã¯ãªããäžéã®600ç§ïŒ10åïŒã«åŒµãä»ããŠããŸããããµãŒããŒã埩æ§ããŠã10åããšã«ãããããŒããèµ°ããããã®éã®ããŒã«åŒã³åºãã¯å
šãŠãããã¯ãããŸãã
çŽãæ¹: reconnectã³ãã³ããèšå®ããããECC_MCP_HEALTH_BACKOFF_MSãå°ããå€ïŒäŸïŒ10000ïŒ10ç§ïŒã«èšå®ããŠããã¯ãªãã®åºåºå€ãäžãããã®ã©ã¡ããã§ããç§ã¯launchd plistã«ECC_MCP_RECONNECT_COMMAND=echo noopïŒäœãããªããããŒïŒãèšå®ããreconnectãè©Šè¡æžã¿ãæ±ãã«ããããšã§failureCountã®èç©ãæå¶ããŸãããæ£æ»æ³ã¯PM2ãsystemdã§ãµãŒãã¹ã管çããŠæ¬ç©ã®reconnectã³ãã³ããèšå®ããããšã§ãã
export ECC_MCP_RECONNECT_OBSIDIAN="pm2 restart obsidian-mcp"
ãã®èšå®ãå
¥ã£ãŠããã503ãæ¥ãŠãPM2ãåèµ·åâåãããŒãã§æ£åžžç¢ºèªâå³åº§ã«healthy埩垰ããšãããµã€ã¯ã«ãåãããã«ãªããŸããã
è©°ãŸãâ¢: ãããžã§ã¯ããã£ã¬ã¯ããªã®settings.jsonãããŒã ã®èšå®ãé ãã
çç¶: ç¹å®ã®ãããžã§ã¯ããã£ã¬ã¯ããªã§Claude Codeãèµ·åãããšããMCPãã«ã¹ãã§ãã¯ããNo MCP config found for obsidianããšãã°ã«åºããŠãããŒããã¹ãããããã®ã«ãããŒã«åŒã³åºãèªäœã¯èµ°ã£ããããã¯ãæ©èœããŠããããã§æ©èœããŠããªãç¶æ ã§ããã
åå : configPaths颿°ïŒ54ã72è¡ç®ïŒã¯ã«ã¬ã³ããã£ã¬ã¯ããªã®èšå®ãã¡ã€ã«ãå
ã«æ¢ããŸãã
return [
path.join(cwd, '.claude.json'),
path.join(cwd, '.claude', 'settings.json'), // â ãããå
ã«èŠã€ãã
path.join(home, '.claude.json'),
path.join(home, '.claude', 'settings.json')
];
ãã®ãããžã§ã¯ãã«ã¯.claude/settings.jsonããããããã«ã¯ãããžã§ã¯ãåºæã®MCPãµãŒããŒïŒplaywrightãªã©ïŒããå®çŸ©ãããŠããŸããã§ãããresolveServerConfig('obsidian')ã¯ãªã¹ãé ã«æ€çŽ¢ããŠæåã®ãã¡ã€ã«ãèŠã€ããæç¹ã§æ¢ãŸãããã§ã¯ãªãããã®ãã¡ã€ã«ã«obsidianããªããã°æ¬¡ã®ãã¡ã€ã«ãžé²ã¿ãŸãããããreadJsonFileã®ã«ãŒãã§data?.mcpServers?.[serverName]ãnullãªãnullãè¿ããŠã«ãŒããç¶ç¶ããã®ã§ããââå®éã«åé¡ã«ãªã£ãã®ã¯ç§ãç°å¢å€æ°ECC_MCP_CONFIG_PATHãããŒã ã®èšå®ãã¡ã€ã«ã«åããŠããããã€ãã®ãã¹ã«æã¡ééãããã£ãã±ãŒã¹ã§ããã
# 誀ïŒã¿ã€ãããïŒ
export ECC_MCP_CONFIG_PATH="/Users/~/.claude/settings.json"
# æ£
export ECC_MCP_CONFIG_PATH="${HOME}/.claude/settings.json"
path.resolveã/Users/~/.claude/settings.jsonãšããå®åšããªããã¹ãè¿ããreadJsonFileãnullãè¿ããŠèšå®ãèŠã€ãããªãæ±ãã«ãªã£ãŠããŸãããECC_MCP_CONFIG_PATHãèšå®ãããšéåžžã®æ€çŽ¢ãã¹ãå®å
šã«äžæžããããããïŒ55ã61è¡ç®ïŒãã¿ã€ããããã°å
šãŠã®ãµãŒããŒèšå®ãæ¶ããŸãã
çŽãæ¹: ECC_MCP_CONFIG_PATHã«çµ¶å¯Ÿãã¹ã䜿ãããã«ãå±éã¯shellãè¡ããã®ã§Nodeã®path.resolveã¯è¡ããŸãããlaunchd plistã«æžãå Žåã¯~ãå±éãããªãããã/Users/èªåã®ããŒã ãã£ã¬ã¯ããªã${HOME}ïŒã·ã§ã«ã¹ã¯ãªããçµç±ïŒãos.homedir()çžåœã®ãã¹ã§æžãå¿
èŠããããŸãã
<!-- launchd plist ã§ã¯ ~ ãå±éãããªã -->
<key>ECC_MCP_CONFIG_PATH</key>
<string>/Users/lily/.claude/settings.json</string>
å®éã®ããŒã ãã£ã¬ã¯ããªãã¹ãçŽæžãããã®ãæã確å®ã§ãã
è©°ãŸãâ£: ECC_MCP_HEALTH_FAIL_OPEN ãæ¬çªç°å¢ã«æ®ãã
çç¶: ããã¯ã¯æ£ããåããŠããŠããã£ãã·ã¥ã«ãunhealthyãšèšé²ãããŠããããªã®ã«ããŒã«åŒã³åºããéã£ãŠããŸãããšã©ãŒãClaudeã®ã³ã³ããã¹ãã«æµã蟌ãã§ããã1åã®ã»ãã·ã§ã³ã§åããšã©ãŒã20ååãåããšããããŒã¯ã³ã®ç¡é§ãèµ·ããŸããã
åå : ãããã°äžã«èšå®ããECC_MCP_HEALTH_FAIL_OPEN=1ãlaunchd plistããåé€ãå¿ããŠããŸããããã®èšå®ãããéããshouldFailOpen()ãtrueãè¿ããunhealthyãªãµãŒããŒãžã®ããŒã«åŒã³åºããå
šãŠexit 0ïŒééïŒã«ãªããŸããããã¯ã¯åããŠãããäœããããã¯ããªãç¶æ
ã§ãã
mcp-health-cache.jsonãèŠãã°status: unhealthyãèšé²ãããŠããã®ã«ãããŒã«åŒã³åºããéããšããäžæŽåãçºçããŠããããã§ãã
çŽãæ¹: ECC_MCP_HEALTH_FAIL_OPENã¯ãããã°å°çšã®ãã©ã°ãšããŠæ±ããåžžçšã®launchd plistã«ã¯æžããªãããããã°ãçµãã£ããããã«åé€ããéçšã«ãŒã«ãèªåã®äžã«èšããŸãããããã¯ãæå³éããããã¯ããŠãããã確èªããã«ã¯ã~/.claude/mcp-health-cache.jsonãçŽæ¥èªãã§status: unhealthyã®ãµãŒããŒãååšããç¶æ
ã§ããŒã«åŒã³åºããèµ°ã£ããšããããã¯ã®stderrãã°ã«skippingã®æèšãåºããã確èªããã®ãæã£åãæ©ãã§ãã
# ããã¯ã®ãã°ããªã¢ã«ã¿ã€ã ã§ç¢ºèªïŒstderrã¯Claude Codeã®hookãã°ã«æµããïŒ
tail -f ~/.claude/logs/hooks.log | grep MCPHealthCheck
ãã®4ã€ã®ããããã€ã³ããä¹ãè¶ããŠãããç°å¢ã¯å®å®ããŸãããä»ã¯å€äžã«èµ°ãããããæèµ·ãããš100ä»¶ã®åŠçãçµããŠããç¶æ ãæ®éã«ãªã£ãŠããŸããMCPãµãŒããŒã®ããŠã³ã§ããããæ¢ãŸã£ãã®ã¯ããã®ä»çµã¿ãå°å ¥ããŠããäžåºŠããããŸããã
ã€ãŸãããã€ã³ã
åæ®µã§4ã€ã®è©°ãŸãã詳述ããŸãããããã§ã¯å®éã«ééããããŸãã¯èŠèœãšããããèœãšã穎ãç®æ¡æžãã§ç¶²çŸ ããŸããP2ãšéè€ãããã®ã¯çããŠããŸãã
-
CLAUDE_HOOK_EVENT_NAMEãæž¡ã£ãŠããªããšããå šåŒã³åºããPreToolUseãšããŠåŠçãããã³ãŒã704è¡ç®ã«
const eventName = process.env.CLAUDE_HOOK_EVENT_NAME || 'PreToolUse';ããããŸãããã®ç°å¢å€æ°ãæ¬ ããŠããã°ã€ãã³ãå€å®ãPreToolUseã«åºå®ãããŸããPostToolUseFailureãšããŠç»é²ããã€ããã®ããã¯ãå®éã«ã¯PreToolUseãšããŠåããŠããããšããã±ãŒã¹ããããŸããClaude Codeãæ£ããENVãæž¡ããŠãããããŸãprocess.envãstderrã«dumpããŠç¢ºèªããã®ãåãåãã®ç¬¬äžæ©ã§ãã -
stdinã1MBãè¶ ãããš
truncatedãã©ã°ãç«ã¡ãfail-openã§ãªããã°å³ãããã¯ã«ãªãMAX_STDIN = 1024 * 1024ïŒ22è¡ç®ïŒããã®äžéã§ãããã¡ã€ã«å 容ãtool_inputã«äžžããšè©°ã蟌ããããªå€§ããªããŒã«åŒã³åºãã§ããã¯å ¥åããã®å€ãè¶ ãããšãããŒã¹ãäžå®å šã®ãŸãŸã¿ãŒã²ãããµãŒããŒãç¹å®ããããšããŸããã¿ãŒã²ãããç¹å®ã§ããã°ECC_HOOK_INPUT_MAX_BYTESã§äžéãåŒãäžããããšãã§ããŸãããå®å šã«ããŒã¹ã§ããªãå Žåã¯fail-openã§ãªãéãexit 2ã§ãããã¯ãããŸãïŒ695ã701è¡ç®ïŒããã®æåãç¥ããªããšãç¹å®ã®ããŒã«ã ãããã¯ã誀ãããã¯ããããšããè¬ã®çŸè±¡ã«èŠããŸãã -
saveStateã¯ãšã©ãŒãæ¡ãã€ã¶ãèšèšãªã®ã§ãæžã蟌ã¿å€±æãç¡é³ã§ç¶ç¶ãã95ã102è¡ç®ã®
saveStateã¯try/catchã§å šãšã©ãŒãææããã³ã¡ã³ãéããNever block the hook on state persistence errors.ãã§é»ã£ãŠç¶è¡ããŸãããã£ã¹ã¯æºæ¯ã»ããŒããã·ã§ã³äžè¶³ã»~/.claude/ãã£ã¬ã¯ããªãæ¶ããŠãããã©ã®ã±ãŒã¹ã§ãããã¯ã¯exit 0ãexit 2ãè¿ãç¶ããŸããããããã£ãã·ã¥ãæžãåºãããªããããhealthyå€å®ãTTLåŸã«æ¶ããŠæ¯åãããŒããèµ°ãããã«ãªããŸããããªããæ¯åHTTPãããŒããèµ°ãããšæããããdf -h ~/.claude/ã§ãã£ã¹ã¯ç¢ºèªãšls -la ~/.claude/mcp-health-cache.jsonã§ããŒããã·ã§ã³ç¢ºèªããå§ããŠãã ããã -
resolveServerConfigãã©ã®ãã¡ã€ã«ã䜿ã£ãŠããããsourceãã£ãŒã«ãã確èªããªããšããããªã
resolveServerConfigïŒ181ã197è¡ç®ïŒã¯configPaths()ã®é ã«å šãã¡ã€ã«ãèµ°æ»ãããµãŒããŒåãèŠã€ãã£ãæç¹ã§sourceãã£ãŒã«ãã«ãã®ãã¹ãèšé²ããŸãããããžã§ã¯ãåºæã®.claude/settings.jsonãšããŒã ã®~/.claude/settings.jsonã«ååãµãŒããŒãäž¡æ¹å®çŸ©ãããŠããå Žåããããžã§ã¯ãåŽãåã¡ãŸããæå³ãšéãèšå®ã䜿ãããŠããªããã¯~/.claude/mcp-health-cache.jsonã®sourceãã£ãŒã«ãã§ç¢ºèªã§ããŸãããããŒã ã®èšå®ãçŽããã®ã«åæ ãããªããçç¶ã®8å²ã¯ããã§ãã -
reconnectã³ãã³ãã¯shell:trueã§å®è¡ããããããã³ãã³ãæååã®æ§é ã«ã¯æ³šæãå¿ èŠ
attemptReconnectïŒ528ã555è¡ç®ïŒã¯spawnSync(command, { shell: true, ... })ã§å®è¡ããŸãã&&ã;ãå«ãŸããã³ãã³ãé£çµã¯æ£åžžã«åããŸããã$(date)ã®ãããªã³ãã³ã眮æãæå³ããå±éãããããšããããŸããECC_MCP_RECONNECT_COMMANDã®å€ã¯pm2 restart {server}ã®ãããªåçŽãª1ã³ãã³ãã«ãšã©ããã®ãå®å šã§ãã{server}ãã¬ãŒã¹ãã«ãïŒ524ã526è¡ç®ïŒã䜿ãã°ãµãŒããŒåããšã«å¥åã®ç°å¢å€æ°ãèšå®ããªããŠãæžã¿ãŸãã -
failureCountã¯markHealthyãåŒã°ãããŸã§ãªã»ããããããããã¯ãªããäžéã«åŒµãä»ãããŸãŸå埩ããªã
markUnhealthyã¯previous.failureCount + 1ãæ¯åèç©ããŸãïŒ215è¡ç®ïŒãäžæ¹ãmarkHealthyã¯failureCount: 0ã«åŒ·å¶ãªã»ããããŸãïŒ200ã210è¡ç®ïŒãåé¡ã¯reconnectã³ãã³ããèšå®ãããŠãããããã¯ãªããäžéïŒ600ç§ïŒã«åŒµãä»ããšã次ã®åãããŒããŸã§10ååŸ ããããç¹ã§ãããã®éã®ããŒã«åŒã³åºãã¯å šãŠãããã¯ãããŸããé·æéäžå®å®ã ã£ããµãŒããŒã埩æ§ããåŸã§ã10åããšã«ããå埩ã確èªããŸããããã®ç¶æ ãçŽ æ©ãè§£æ¶ããã«ã¯ã察象ãµãŒããŒã®ãšã³ããªããã£ãã·ã¥ãã¡ã€ã«ããæååé€ããã®ãæéã§ããpython3 -c " import json, pathlib p = pathlib.Path.home() / '.claude/mcp-health-cache.json' s = json.loads(p.read_text()) s['servers'].pop('agentmemory', None) p.write_text(json.dumps(s, indent=2)) print('reset done') " -
stdioãµãŒããŒã®config.envã«æŒããç°å¢å€æ°ããlaunchdããèµ·åãããšåããªããã®åå ã«ãªã
probeCommandServerïŒ306ã309è¡ç®ïŒã¯ããã»ã¹èµ·åæã«config.envãprocess.envã«ããŒãžããŸããã€ã³ã¿ã©ã¯ãã£ããªzshã»ãã·ã§ã³ã§ã¯ANTHROPIC_API_KEYã.zshrcããèªåçã«åŒãç¶ãããŸãããlaunchdããèµ·åããClaudeã¯loginã·ã§ã«ã®ç°å¢å€æ°ãæã¡ãŸããããæã§è©Šããšåãã»å€éãããã§å€±æããããšããçç¶ã®å€ãã¯ããã«åå ããããŸããå šãµãŒããŒã®config.envã»ã¯ã·ã§ã³ã«å¿ èŠãªAPIããŒãæç€ºçã«åæããããšãæ ¹æ¬å¯Ÿçã§ãã -
HTTPãµãŒããŒãžã®ãããŒãã429ãè¿ãç¶ããŠreconnectã«ãŒãã«å ¥ãã±ãŒã¹ããã
RECONNECT_STATUS_CODESã«429ãå«ãŸããŠããããããããŒãã§429ãè¿ããšreconnectãèµ°ããŸãïŒ33è¡ç®ïŒãreconnectã³ãã³ããå®è¡ãããŠãæ ¹æ¬çãªåé¡ïŒã¬ãŒãå¶éïŒãè§£æ¶ããªããã°åãããŒãã429ãè¿ããmarkUnhealthyãèµ°ã£ãŠfailureCountãå¢ããŸãããããŒãèªäœãã¬ãŒãå¶éãæ¶è²»ããŠããæªåŸªç°ã§ãããããé²ãã«ã¯ECC_MCP_HEALTH_TTL_MSãé·ãã«èšå®ããŠãããŒãé »åºŠãäžãããã察象ãµãŒããŒã®ãããŒãURLãèªèšŒäžèŠãªãã«ã¹ãšã³ããã€ã³ãã«å€æŽããæ¹æ³ããããŸãã -
PostToolUseFailureããã¯ã®exit codeã0ã§ããããã¯ãæ©èœããŠãããšæã蟌ã
handlePostToolUseFailureã®æ»ãå€ã¯åžžã«exitCode: 0ã§ãïŒ677è¡ç®ïŒãããã¯èšèšäžæ£ããæåã§ãããã§ã«å€±æããããŒã«åŒã³åºããã«å¯ŸããŠãããã¯ãè¿ããŠãæé ãã§ãããã®ããã¯ã®ä»äºã¯ã次ã®PreToolUseã§ãããã¯ããããã®ç¶æ æžã蟌ã¿ãã§ãããexit codeã«ã¯æå³ããããŸããããPostToolUseFailureããã¯ãèµ°ã£ãŠããã®ã«ãããã¯ãããªãããšããå ±åã®å€ãã¯ã次ã®PreToolUseã§ãã£ãã·ã¥ãæ£ããèªãŸããŠãããã確èªããããšã§è§£æ±ºããŸãã
ãã¹ããã©ã¯ãã£ã¹
å幎éãæå120äžã®èªåŸç°å¢ããã®ããã¯ã§æ¯ããŠããäžã§ã培åºããŠããããšã12ç¹ãŸãšããŸãã
1. reconnectã³ãã³ãã¯ãããŒã§ãããããå¿ ãèšå®ãã
reconnectã³ãã³ããªãã§RECONNECT_STATUS_CODESã®ãšã©ãŒãç©ã¿éãªããšãfailureCountãéªã ããŸã«ãªãããã¯ãªããäžéã«åŒµãä»ããŸãã埩æ§ããŠãããµãŒããŒãžã®ã¢ã¯ã»ã¹ãé·æéãããã¯ããç¶ããŸããPM2管çäžãªãæ¬ç©ãèšå®ããããã§ãªããã°echo noopã ãã§ãreconnectãè©Šè¡æžã¿ãæ±ãã«ãªã£ãŠfailureCountã®èç©ãæå¶ã§ããŸãã
# PM2管çäžãªãæ¬ç©ã
export ECC_MCP_RECONNECT_COMMAND="pm2 restart {server}"
# ãšããããèç©ãæ¢ããã ããªã
export ECC_MCP_RECONNECT_COMMAND="echo noop"
2. ECC_MCP_HEALTH_FAIL_OPENã¯launchd plistã«æžããªã
ãã®å€æ°ãç«ã£ãŠãããšshouldFailOpen()ïŒ557ã559è¡ç®ïŒãtrueãè¿ããunhealthyãªãµãŒããŒãžã®ããŒã«åŒã³åºããå
šãŠexit 0ïŒééïŒã«ãªããŸããããã¯ãåããŠããããã«èŠããŠäœããããã¯ããªãç¶æ
ã§ãããããã°å°çšãã©ã°ãšããŠæ±ãããããã°ãçµãã£ããããã«åé€ããéçšã«ãŒã«ãäœã£ãŠãã ããã
3. ECC_MCP_CONFIG_PATHã«ã¯çµ¶å¯Ÿãã¹ãæžã
launchd plistã§ã¯~ãå±éãããŸããã/Users/ããªãã®ããŒã ãã£ã¬ã¯ããªå/.claude/settings.jsonãšçŽæžãããã®ãæã確å®ã§ããpath.resolveã¯Nodeã®æ©èœã§ãã·ã§ã«ã®ãã«ãå±éãè¡ããŸããïŒ55ã61è¡ç®ïŒãã¿ã€ããããã°å
šãµãŒããŒã®èšå®ãæ¶ããŸãã
4. ãã£ãã·ã¥ç¢ºèªã»ãªã»ããã»ãã°ç£èŠã®aliasãç»é²ãã
åé¡ãèµ·ãããšãã«ããåããããã.zshrcã«aliasãå
¥ããŠãããŸãã
alias mcp-health="python3 -m json.tool ~/.claude/mcp-health-cache.json"
alias mcp-reset="echo '{\"version\":1,\"servers\":{}}' > ~/.claude/mcp-health-cache.json"
alias mcp-logs="tail -f ~/.claude/logs/hooks.log | grep MCPHealthCheck"
5. stdioãµãŒããŒãå¿ èŠãšããå šAPIããŒãconfig.envã«æç€ºãã
launchdããèµ·åããClaudeã¯loginã·ã§ã«ã®ç°å¢å€æ°ãæã¡ãŸããã~/.claude/settings.jsonã®åãµãŒããŒã®envã»ã¯ã·ã§ã³ã«å¿
èŠãªããŒãå
šãŠåæããããšã§ãã€ã³ã¿ã©ã¯ãã£ãåäœãšãããåäœã®æåãäžèŽãããŸãã
6. TTLã¯ã»ãã·ã§ã³ã®äœ¿çšé »åºŠã«åãããŠèª¿æŽãã
ããã©ã«ãã®2åïŒDEFAULT_TTL_MS = 2 * 60 * 1000ã23è¡ç®ïŒã¯æ±çšçãªå€ã§ããå€éãããã®ããã«1ãµãŒããŒãé«é »åºŠã§åŒã¶ç°å¢ã§ã¯5ã10åã«å»¶ã°ãããšã§ãããŒãåæ°ã倧å¹
ã«æžãã¹ã«ãŒããããäžãããŸãããããã°äžã¯30ç§ã«çž®ãããšåé¡ã®åçŸãéããªããŸãã
# å€éãããç°å¢
export ECC_MCP_HEALTH_TTL_MS=600000 # 10å
# ãããã°äž
export ECC_MCP_HEALTH_TTL_MS=30000 # 30ç§
7. ããã¯ãªãåºåºå€ã¯ãµãŒããŒã®ç¹æ§ã«åããã
ããã©ã«ãã®30ç§ã¯ããŒã«ã«ããã»ã¹ã®åèµ·åãæ³å®ããŠããŸããå€éšã®APIãµãŒããŒã§åŸ©æ§ã«1ã5åãããå Žåã¯ECC_MCP_HEALTH_BACKOFF_MS=60000ïŒ1åïŒã«äžãããšãå埩確èªã®ééã宿
ã«åããŸããéã«ããŒã«ã«ã®stdioãµãŒããŒãæ°ç§ã§åŸ©æ§ãããªã10ç§ã§ãååã§ãã
8. éèŠãµãŒããŒã¯PM2ã§ããã»ã¹ç®¡çãã
PM2ã®ãªãŒããªã¹ã¿ãŒããšãã®ããã¯ã®reconnectâåãããŒããµã€ã¯ã«ãçµã¿åããããšãäžæé害ããã®åŸ©æ§ãå®å šèªååãããŸãããµãŒããŒãã¯ã©ãã·ã¥ããâPM2ãåèµ·åããâæ¬¡ã®PreToolUseããã¯ãreconnectã³ãã³ããåŒã¶âPM2ããæ¢ã«èµ·åæžã¿ãã§æåãè¿ãâåãããŒãã§healthyã確èªâããŒã«å®è¡ãèš±å¯ããšãããµã€ã¯ã«ã§ãããã®çµã¿åããã«ããŠãããç¿æãµãŒããŒãæ»ãã§ãããã±ãŒã¹ã¯ãŒãã«ãªããŸããã
9. sourceãã£ãŒã«ãã§èšå®ãã¡ã€ã«ã®è§£æ±ºçµæã宿çã«ç¢ºèªãã
~/.claude/mcp-health-cache.jsonã®åãšã³ããªã«ããsourceãã£ãŒã«ãããã©ã®èšå®ãã¡ã€ã«ãããµãŒããŒèšå®ãèªãŸããŠãããã瀺ããŸãããããžã§ã¯ããã£ã¬ã¯ããªã«.claude/settings.jsonã远å ããåŸã¯ãæå³ããèšå®ã䜿ãããŠãããããã®ãã£ãŒã«ãã§ç¢ºèªããŠãã ããããããŒã ã®èšå®ãçŽããã®ã«åããªããã®å€§åã¯ããã§è§£æ±ºããŸãã
10. ããã¯ã®ãã°ããªã¢ã«ã¿ã€ã ã§ç¢ºèªããç¿æ £ãæã€
emitLogsïŒ561ã565è¡ç®ïŒã¯stderrã«ãã°ãåºåããŸããClaude Codeã®ããã¯ãã°ã«æµãããããtail -fã§ç¢ºèªã§ããŸããskippingã®æåãåºãŠããã°ãããã¯ãæ©èœããŠããŸããconnection restoredãåºãŠããã°reconnectãæåããŠããŸãããã°ãèŠãç¿æ
£ãããã°ãããã¯ãæå³éãã«åããŠããããæ°ç§ã§ç¢ºèªã§ããŸãã
11. ããã¯ç»é²ã¯ããŒã ã®èšå®ã«äžå åãã
ãããžã§ã¯ãã®.claude/settings.jsonã«ãhooksãæžããŸããããã«ã¹ãã§ãã¯ããã¯ã¯ããŒã ã®~/.claude/settings.jsonã®ã¿ã«æžãããšã匷ãå§ããŸãããããžã§ã¯ãããšã«åæ£ããããšããã®ãããžã§ã¯ãã§ã ãããã¯ãå¹ããªãããšãã穎ãçãŸããŸããå
šãããžã§ã¯ãã§äžæ§ã«ä¿è·ãåããããã«ããŒã ã®èšå®ãæ£ãšããŸãã
12. failureCountãç°åžžã«å€§ãããªã£ãããšã³ããªãæååé€ããŠãªã»ãããã
markHealthyãåŒã°ããã°èªåçã«failureCount: 0ã«ãªã»ãããããŸãããããã¯ãªããé·ãããŠmarkHealthyãžã®éãéããããŠãããšãã¯æååé€ãæéã§ããåè¿°ã®python3ã¯ã³ã©ã€ããŒãaliasã«ç»é²ããŠãããšãç¹å®ãµãŒããŒã®ç¶æ
ã10ç§ã§ãªã»ããã§ããŸãã
ãŸãšã
äŒç€Ÿéœåã§è§£éãããŠæå0ã«ãªã£ãããšãèªåŸç°å¢ãäžããäœãçŽãã«ããã£ãŠäžçªæããŠããã®ã¯ãå€äžã«åãããããããæãŸã§ã«å šæ» ããŠãããã·ããªãªã§ãããããããã¡ã°ã確å®ã«é²ãã®ã¯ãã¢ãã«ã匷ãããããšã§ããããã³ããã磚ãããšã§ããªããéå®³ãæ€åºããŠã³ã³ããã¹ãã®å€åŽã«èšé²ããŠããä»çµã¿ã§ããã
mcp-health-check.jsã®èšèšãäžèšã§ãŸãšãããšããã³ã³ããã¹ããå§çž®ãããŠãå¥å
šæ§ã®æ
å ±ã倱ããªããã§ããmcp-health-cache.jsonã¯ã»ãã·ã§ã³ã®å€ã«ãããŸããããã¯ãªãèšç®ã¯ã³ãŒããæ
ããŸããã¢ãã«ã¯ããã®ãµãŒããŒã¯èœã¡ãŠãããããããªãããšããæšè«ãããªããŠããããã®åã®ããŒã¯ã³ãšæ³šæåããæ¬æ¥ã®ã¿ã¹ã¯ã«åããããŸãã
æå120äžã®ç°å¢ããæå120äžãã§ããç¶ããŠããã®ã¯ãå€äžã®ããããç¿æãŸã§åããªãããã§ããåããªãã®ã¯ãã®ããã¯ãéãã«ä»äºãããŠããããã§ãã
å®è£ ã¯721è¡ãããŸãããã³ã¢ã¯3ç¹ã«éçŽãããŸãã
- ã³ã³ããã¹ãã®å€ã«ç¶æ ãæ°žç¶åãã â ã»ãã·ã§ã³å§çž®ã«é¢ãããèšé²ãæ®ã
- ã¹ããŒã¿ã¹ã³ãŒãã§æŠç¥ãåå²ããã â 429ãš503ãšECONNREFUSEDã¯å¥ç©ãšããŠæ±ã
-
ããã¯ãªãã«äžéãèšãã â
MAX_BACKOFF_MSã®10åã§å®å šæŸæ£ãé²ã
ãã®3ç¹ãçè§£ã§ããã°ããã¥ãŒãã³ã°ãåé¡ã®åãåããèªåã§ã§ããããã«ãªããŸãã
ä»çµã¿ã®å šäœåã»æ120äžã®å èš³ã»30æ¥æé ã¯æænoteã«ãŸãšããŠããŸãã
ð Claude CodeèªåŸç°å¢ã§ãå®éã©ã皌ãã â ä»çµã¿ã»å®äŸã»å§ãæ¹ã»ãµããŒã
LilyïŒ@bokuwalilyïŒâ å人éçºè ãClaude Code ã§èªåååºç€ãçµã¿ãªãããiOSã¢ããªãWebãµãŒãã¹ãéç£ããŠããŸã
- å¶äœç©ã»èšäºã¯ bokuwalily.com ã«ãŸãšããŠããŸãð¥ïž
- AIã§ãå¯ãŠãŠãåãä»çµã¿ããäœã£ãŠæ120äžã«ãã話㯠noteã®ææèšäº ã«ð°
- OSS: github.com/bokuwalily ð
- ææ°æ å ±ã»ãåãåãã㯠X @bokuwalily ãžð
çããã® â€ïž ãã·ã§ã¢ãå±ã¿ã«ãªããŸãïŒ