Files
oh-my-opencode/src/features/skill-mcp-manager/stdio-client.ts
T
YeonGyu-Kim 1bdfd409e9 refactor(skill-mcp-manager): split manager.ts into connection and client modules
Extract MCP client lifecycle management:
- connection.ts: getOrCreateClientWithRetry logic
- stdio-client.ts, http-client.ts: transport-specific creation
- oauth-handler.ts: OAuth token management
- cleanup.ts: session and global cleanup
- connection-type.ts: connection type detection
2026-02-08 16:21:28 +09:00

70 lines
2.2 KiB
TypeScript

import { Client } from "@modelcontextprotocol/sdk/client/index.js"
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
import type { ClaudeCodeMcpServer } from "../claude-code-mcp-loader/types"
import { createCleanMcpEnvironment } from "./env-cleaner"
import { registerProcessCleanup, startCleanupTimer } from "./cleanup"
import type { ManagedClient, SkillMcpClientConnectionParams } from "./types"
function getStdioCommand(config: ClaudeCodeMcpServer, serverName: string): string {
if (!config.command) {
throw new Error(`MCP server "${serverName}" is configured for stdio but missing 'command' field.`)
}
return config.command
}
export async function createStdioClient(params: SkillMcpClientConnectionParams): Promise<Client> {
const { state, clientKey, info, config } = params
const command = getStdioCommand(config, info.serverName)
const args = config.args ?? []
const mergedEnv = createCleanMcpEnvironment(config.env)
registerProcessCleanup(state)
const transport = new StdioClientTransport({
command,
args,
env: mergedEnv,
stderr: "ignore",
})
const client = new Client(
{ name: `skill-mcp-${info.skillName}-${info.serverName}`, version: "1.0.0" },
{ capabilities: {} }
)
try {
await client.connect(transport)
} catch (error) {
// Close transport to prevent orphaned MCP process on connection failure
try {
await transport.close()
} catch {
// Process may already be terminated
}
const errorMessage = error instanceof Error ? error.message : String(error)
throw new Error(
`Failed to connect to MCP server "${info.serverName}".\n\n` +
`Command: ${command} ${args.join(" ")}\n` +
`Reason: ${errorMessage}\n\n` +
`Hints:\n` +
` - Ensure the command is installed and available in PATH\n` +
` - Check if the MCP server package exists\n` +
` - Verify the args are correct for this server`
)
}
const managedClient = {
client,
transport,
skillName: info.skillName,
lastUsedAt: Date.now(),
connectionType: "stdio",
} satisfies ManagedClient
state.clients.set(clientKey, managedClient)
startCleanupTimer(state)
return client
}