본문으로 건너뛰기

Worker 스레드

mcp.worker.tsclaude 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 명령설명
listlist()없음 (파일 직접 읽기)config.json에서 서버 목록 읽기
addadd()claude mcp add서버 추가
addJsonaddJson()claude mcp add-jsonJSON에서 일괄 추가
removeremove()claude mcp remove서버 제거
testtest()claude mcp test연결 테스트
discoverdiscover()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

매개변수 구성:

  1. ['mcp', 'add', '--scope', scope, name]
  2. env를 순회하며 -e KEY=VALUE 매개변수 추가
  3. commandargs 추가

SSE/HTTP 서버

claude mcp add --scope user my-server \
--transport sse \
--header "Authorization: Bearer token" \
https://api.example.com/mcp

매개변수 구성:

  1. ['mcp', 'add', '--scope', scope, name]
  2. --transport sse|http
  3. URL
  4. 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 스레드가 충돌하지 않습니다.

관련 파일