Worker 스레드
mcp.worker.ts는 claude CLI를 통해 MCP 서버를 관리하는 브리지 레이어를 제공하는 독립적인 Worker 스레드입니다. McpService (SDK 직접 연결 방식)를 보완하며, Claude 구성 파일에서 MCP 서버 레코드를 읽고 CLI를 통해 추가/제거/테스트 작업을 수행하는 것을 지원합니다.
파일 경로: packages/desktop/app/main/workers/mcp.worker.ts
아키텍처 다이어그램
graph LR
subgraph Main["메인 프로세스"]
Router["McpRouter / 호출자"]
end
subgraph Worker["Worker 스레드"]
Port["MessagePort"]
McpWorkerClass["McpWorker"]
end
subgraph External["외부"]
ClaudeCLI["claude CLI"]
ConfigFile["~/.claude/config.json"]
end
Router -->|postMessage| Port
Port -->|message event| McpWorkerClass
McpWorkerClass -->|spawn| ClaudeCLI
McpWorkerClass -->|readFile| ConfigFile
McpWorkerClass -->|postMessage| Port
Port -->|result/error| Router
통신 프로토콜
요청 형식
type WorkerAction =
| { id: number; action: 'list' }
| { id: number; action: 'add'; payload: McpServerInput }
| { id: number; action: 'addJson'; payload: McpServerJsonInput }
| { id: number; action: 'remove'; payload: McpServerRemoveInput }
| { id: number; action: 'test'; payload: McpServerRemoveInput }
| { id: number; action: 'discover'; payload: McpServerRemoveInput };
응답 형식
type WorkerResponse =
| { id: number; result: unknown }
| { id: number; error: string };
각 요청은 id 필드를 통해 응답과 쌍을 이룹니다.
지원되는 작업
| Action | 메서드 | CLI 명령 | 설명 |
|---|---|---|---|
list | list() | 없음 (파일 직접 읽기) | config.json에서 서버 목록 읽기 |
add | add() | claude mcp add | 서버 추가 |
addJson | addJson() | claude mcp add-json | JSON에서 일괄 추가 |
remove | remove() | claude mcp remove | 서버 제거 |
test | test() | claude mcp test | 연결 테스트 |
discover | discover() | claude mcp inspect | 도구/리소스/프롬프트 검색 |
list — 구성 파일 읽기
list()는 CLI를 호출하지 않고 ~/.claude/config.json 파일을 직접 읽습니다:
private async readClaudeConfig(): Promise<ClaudeConfig | null> {
const configPath = path.join(os.homedir(), '.claude', 'config.json');
const content = await fsp.readFile(configPath, 'utf8');
return JSON.parse(content);
}
구성 파일 구조
interface ClaudeConfig {
mcpServers?: Record<string, McpServerConfig & Record<string, unknown>>;
projects?: Record<string, {
mcpServers?: Record<string, McpServerConfig & Record<string, unknown>>;
}>;
}
서버 레코드 매핑
config.json에서 읽은 레코드는 내부 형식으로 변환해야 합니다:
private toServerRecord(input): McpServerRecord {
// Transport type inference: transport > type > default 'stdio'
const transport = config.transport ?? config.type ?? 'stdio';
return {
id: `${scope}:${projectPath ?? 'global'}:${name}`,
name,
scope,
type: transport,
projectPath,
status: 'configured',
config
};
}
ID 형식: {scope}:{projectPath|global}:{name}
스코프:
user— 전역으로 구성된 서버 (최상위mcpServers)local— 프로젝트 수준에서 구성된 서버 (projects[path].mcpServers)
add — 서버 추가
claude mcp add CLI 명령을 통해 서버를 추가합니다:
Stdio 서버
claude mcp add --scope user my-server \
-e API_KEY=xxx \
npx -y @example/mcp-server
매개변수 구성:
['mcp', 'add', '--scope', scope, name]env를 순회하며-e KEY=VALUE매개변수 추가command및args추가
SSE/HTTP 서버
claude mcp add --scope user my-server \
--transport sse \
--header "Authorization: Bearer token" \
https://api.example.com/mcp
매개변수 구성:
['mcp', 'add', '--scope', scope, name]--transport sse|http- URL
headers를 순회하며--header "Key: Value"매개변수 추가
discover — 기능 검색
claude mcp inspect 명령을 사용하여 JSON 형식의 기능 목록을 가져옵니다:
claude mcp inspect --scope user my-server --format json
JSON 추출
CLI 출력에서 JSON 객체를 추출합니다:
private extractJson(output: string) {
const match = /\{[\s\S]*\}/.exec(output);
if (!match) return null;
return JSON.parse(match[0]);
}
결과 정규화
도구/리소스/프롬프트 목록은 McpToolInfo[] 형식으로 표준화됩니다:
private normalizeToolList(value: any): McpToolInfo[] {
// Support string arrays and object arrays
// string → { name: string }
// { name, description } → McpToolInfo
}
서버 식별자 파싱
Worker의 서버 식별자는 scope:name 형식을 사용합니다 (McpService의 UUID 형식과 다름):
private parseServerIdentifier(id?: string, scope?: string) {
const [prefix, ...rest] = id.split(':');
const name = rest.length ? rest.join(':') : prefix;
const normalizedScope = rest.length
? prefix as 'user' | 'local'
: scope ?? 'user';
return { scope: normalizedScope, name };
}
예시:
user:my-server→{ scope: 'user', name: 'my-server' }local:/path/to/project:db-server→{ scope: 'local', name: '/path/to/project:db-server' }my-server(접두사 없음) →{ scope: 'user', name: 'my-server' }
CLI 실행
모든 CLI 작업은 runClaude() 메서드를 통해 실행됩니다:
private async runClaude(
args: string[],
options: { cwd?: string } = {}
): Promise<{ success: boolean; stdout: string; stderr: string }>
child_process.spawn을 사용하여claude프로세스 시작- stdout 및 stderr 수집
- 프로세스 종료 코드를 통해 성공/실패 판단
- local 스코프 작업을 위한 작업 디렉터리(
cwd) 지정 지원
Worker와 McpService의 관계
| 기능 | McpService (SDK 직접 연결) | mcp.worker (CLI 브리지) |
|---|---|---|
| 연결 방식 | MCP SDK 직접 사용 | claude CLI를 통해 |
| 프로세스 모델 | 메인 프로세스 내 | 독립 Worker 스레드 |
| 구성 소스 | Electron Store (mcp-config) | ~/.claude/config.json |
| 도구 호출 | 지원됨 | 지원되지 않음 |
| 목적 | 런타임 연결 및 도구 호출 | 구성 관리 및 CLI 호환성 |
| 실시간성 | 실시간 연결 | 요청 시 작업 |
참고: 현재 버전에서 McpService (SDK 직접 연결)가 주요 런타임 구현입니다. Worker는 보완적인 역할로, Claude CLI와의 구성 상호 운용성을 제공합니다. 두 방식이 관리하는 구성은 독립적입니다 (각각 Electron Store와 ~/.claude/config.json에 별도로 저장됨).
오류 처리
Worker 스레드의 오류는 메시지 프로토콜을 통해 전달됩니다:
port.on('message', async (message: WorkerAction) => {
try {
// Handle request...
port.postMessage({ id: message.id, result: ... });
} catch (error) {
port.postMessage({
id: message.id,
error: error instanceof Error ? error.message : String(error)
});
}
});
모든 예외는 캐치되어 오류 응답으로 변환되며, Worker 스레드가 충돌하지 않습니다.