c118996746
- SwiftUI macOS app with C++17 code analysis engine (ObjC++ bridge) - Agentic AI loop: LLM plans → tool calls → execution → feedback loop - 15 agent tools: file ops, terminal, git, xcode build, code intel - 7 persistent terminal tools with background session management - Chat sidebar with agent step rendering and auto-apply - NVIDIA NIM API integration (Llama 3.3 70B default) - OpenAI tool_calls format with prompt-based fallback - Code editor with syntax highlighting and multi-tab support - File tree, console view, terminal view - Git integration and workspace management
179 lines
5.3 KiB
Swift
179 lines
5.3 KiB
Swift
// MCPTypes.swift
|
|
// CxSwiftAgent — MCP Protocol Types
|
|
//
|
|
// JSON-RPC 2.0 + MCP 2025-03-26 data types.
|
|
// Zero external dependencies.
|
|
|
|
import Foundation
|
|
|
|
// MARK: - JSON-RPC 2.0
|
|
|
|
struct JsonRpcRequest: Codable, Sendable {
|
|
let jsonrpc: String
|
|
let method: String
|
|
let params: [String: AnyCodable]?
|
|
let id: AnyCodable?
|
|
|
|
var isNotification: Bool { id == nil }
|
|
}
|
|
|
|
struct JsonRpcResponse: Codable, Sendable {
|
|
let jsonrpc: String
|
|
let result: AnyCodable?
|
|
let error: JsonRpcError?
|
|
let id: AnyCodable?
|
|
|
|
static func success(id: AnyCodable?, result: Any) -> JsonRpcResponse {
|
|
JsonRpcResponse(jsonrpc: "2.0", result: AnyCodable(result), error: nil, id: id)
|
|
}
|
|
|
|
static func error(id: AnyCodable?, code: Int, message: String, data: Any? = nil) -> JsonRpcResponse {
|
|
let errData = data.map { AnyCodable($0) }
|
|
return JsonRpcResponse(jsonrpc: "2.0", result: nil,
|
|
error: JsonRpcError(code: code, message: message, data: errData), id: id)
|
|
}
|
|
}
|
|
|
|
struct JsonRpcError: Codable, Sendable {
|
|
let code: Int
|
|
let message: String
|
|
let data: AnyCodable?
|
|
}
|
|
|
|
// MARK: - MCP Protocol Types
|
|
|
|
struct MCPToolDefinition: Sendable {
|
|
let name: String
|
|
let description: String
|
|
nonisolated(unsafe) let inputSchema: [String: Any]
|
|
let annotations: ToolAnnotations
|
|
let handler: @Sendable ([String: Any]) async -> [[String: Any]]
|
|
}
|
|
|
|
struct ToolAnnotations: Codable, Sendable {
|
|
var readOnlyHint: Bool = false
|
|
var destructiveHint: Bool = false
|
|
var idempotentHint: Bool = false
|
|
var openWorldHint: Bool = false
|
|
}
|
|
|
|
struct MCPResourceDefinition: Sendable {
|
|
let uri: String
|
|
let name: String
|
|
let mimeType: String
|
|
let handler: @Sendable () async -> String
|
|
}
|
|
|
|
struct MCPPromptDefinition: Sendable {
|
|
let name: String
|
|
let description: String
|
|
let arguments: [[String: String]]
|
|
let handler: @Sendable ([String: String]) -> [[String: Any]]
|
|
}
|
|
|
|
struct MCPCapabilities: Codable, Sendable {
|
|
let tools: ToolsCap?
|
|
let resources: ResourcesCap?
|
|
let prompts: PromptsCap?
|
|
let logging: LoggingCap?
|
|
|
|
struct ToolsCap: Codable, Sendable { let listChanged: Bool? }
|
|
struct ResourcesCap: Codable, Sendable { let subscribe: Bool?; let listChanged: Bool? }
|
|
struct PromptsCap: Codable, Sendable { let listChanged: Bool? }
|
|
struct LoggingCap: Codable, Sendable {}
|
|
}
|
|
|
|
// MARK: - AnyCodable (JSON bridge)
|
|
|
|
struct AnyCodable: Codable, Sendable, CustomStringConvertible {
|
|
nonisolated(unsafe) let value: Any
|
|
|
|
init(_ value: Any) {
|
|
self.value = value
|
|
}
|
|
|
|
init(from decoder: Decoder) throws {
|
|
let container = try decoder.singleValueContainer()
|
|
if container.decodeNil() {
|
|
value = NSNull()
|
|
} else if let b = try? container.decode(Bool.self) {
|
|
value = b
|
|
} else if let i = try? container.decode(Int.self) {
|
|
value = i
|
|
} else if let d = try? container.decode(Double.self) {
|
|
value = d
|
|
} else if let s = try? container.decode(String.self) {
|
|
value = s
|
|
} else if let arr = try? container.decode([AnyCodable].self) {
|
|
value = arr.map(\.value)
|
|
} else if let dict = try? container.decode([String: AnyCodable].self) {
|
|
value = dict.mapValues(\.value)
|
|
} else {
|
|
value = NSNull()
|
|
}
|
|
}
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
var container = encoder.singleValueContainer()
|
|
switch value {
|
|
case is NSNull:
|
|
try container.encodeNil()
|
|
case let b as Bool:
|
|
try container.encode(b)
|
|
case let i as Int:
|
|
try container.encode(i)
|
|
case let d as Double:
|
|
try container.encode(d)
|
|
case let s as String:
|
|
try container.encode(s)
|
|
case let arr as [Any]:
|
|
try container.encode(arr.map { AnyCodable($0) })
|
|
case let dict as [String: Any]:
|
|
try container.encode(dict.mapValues { AnyCodable($0) })
|
|
default:
|
|
try container.encode("\(value)")
|
|
}
|
|
}
|
|
|
|
var description: String { "\(value)" }
|
|
}
|
|
|
|
// MARK: - JSON Serialization Helpers
|
|
|
|
enum JSON {
|
|
private static let encoder: JSONEncoder = {
|
|
let e = JSONEncoder()
|
|
e.outputFormatting = [.sortedKeys]
|
|
return e
|
|
}()
|
|
|
|
private static let prettyEncoder: JSONEncoder = {
|
|
let e = JSONEncoder()
|
|
e.outputFormatting = [.prettyPrinted, .sortedKeys]
|
|
return e
|
|
}()
|
|
|
|
static func serialize(_ value: Any, pretty: Bool = false) -> String {
|
|
let enc = pretty ? prettyEncoder : encoder
|
|
if let codable = value as? Encodable {
|
|
if let data = try? enc.encode(AnyCodable(codable)) {
|
|
return String(data: data, encoding: .utf8) ?? "{}"
|
|
}
|
|
}
|
|
// Fallback to JSONSerialization
|
|
if let data = try? JSONSerialization.data(withJSONObject: value, options: pretty ? .prettyPrinted : .sortedKeys) {
|
|
return String(data: data, encoding: .utf8) ?? "{}"
|
|
}
|
|
return "{}"
|
|
}
|
|
|
|
static func parse(_ string: String) -> Any? {
|
|
guard let data = string.data(using: .utf8) else { return nil }
|
|
return try? JSONSerialization.jsonObject(with: data)
|
|
}
|
|
|
|
static func parseDict(_ string: String) -> [String: Any]? {
|
|
parse(string) as? [String: Any]
|
|
}
|
|
}
|