Skip to main content
Glama
MCPCommand+Call.swift4.92 kB
// // MCPCommand+Call.swift // PeekabooCLI // import Algorithms import Commander import Foundation import Logging import MCP import PeekabooCore import TachikomaMCP extension MCPCommand { /// Call tool on MCP server @MainActor struct Call { static let commandDescription = CommandDescription( commandName: "call", abstract: "Call a tool on another MCP server", discussion: """ Connect to another MCP server and execute a tool. This allows Peekaboo to consume services from other MCP servers. EXAMPLE: peekaboo mcp call claude-code edit_file --args '{"path": "main.swift"}' """ ) private static let connectionTimeoutSeconds: TimeInterval = 15 @Argument(help: "MCP server to connect to") var server: String? @Argument(help: "Tool to call") var tool: String? @Option(help: "Tool arguments as JSON") var args: String = "{}" var service: any MCPClientService = DefaultMCPClientService.shared @MainActor mutating func run(using runtime: CommandRuntime) async throws { let context = MCPCommandContext(runtime: runtime, service: self.service) do { guard let server = self.server, !server.isEmpty else { throw MCPCommandError.invalidArguments("Specify the target MCP server via --server.") } guard let tool = self.tool, !tool.isEmpty else { throw MCPCommandError.invalidArguments("Specify the MCP tool to call using --tool.") } let arguments = try MCPArgumentParsing.parseJSONObject(self.args) try await self.ensureServerReady(serverName: server, context: context) let response = try await context.service.execute(server: server, tool: tool, args: arguments) AutomationEventLogger.log( .mcp, "call server=\(server) tool=\(tool) error=\(response.isError)" ) if context.wantsJSON { MCPCallFormatter.outputJSON( response: response, serverName: server, toolName: tool, logger: context.logger ) } else { MCPCallFormatter.outputHumanReadable(response: response, server: server, toolName: tool) } if response.isError { throw ExitCode.failure } } catch let error as MCPCommandError { MCPCallFormatter.emitError( message: error.localizedDescription, code: error.errorCode, wantsJSON: context.wantsJSON, logger: context.logger ) throw ExitCode.failure } catch let exit as ExitCode { throw exit } catch { MCPCallFormatter.emitError( message: "Failed to call MCP tool: \(error.localizedDescription)", code: .UNKNOWN_ERROR, wantsJSON: context.wantsJSON, logger: context.logger ) throw ExitCode.failure } } private func ensureServerReady(serverName: String, context: MCPCommandContext) async throws { await context.service.bootstrap(connect: false) guard let info = await context.service.serverInfo(name: serverName) else { throw MCPCommandError.serverNotConfigured(serverName) } guard info.config.enabled else { throw MCPCommandError.serverDisabled(serverName) } let probe = await context.service.probe( name: serverName, timeoutMs: Int(Self.connectionTimeoutSeconds * 1000) ) guard case .connected = probe else { let reason: String? = { if case let .disconnected(error) = probe { return error } return nil }() throw MCPCommandError.connectionFailed(server: serverName, reason: reason) } } } } @MainActor extension MCPCommand.Call: ParsableCommand {} extension MCPCommand.Call: AsyncRuntimeCommand {} @MainActor extension MCPCommand.Call: CommanderBindableCommand { mutating func applyCommanderValues(_ values: CommanderBindableValues) throws { self.server = try values.requiredPositional(0, label: "server") self.tool = try values.requiredPositional(1, label: "tool") if let argsValue = values.singleOption("args") { self.args = argsValue } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/steipete/Peekaboo'

If you have feedback or need assistance with the MCP directory API, please join our Discord server