Files
Termind/TermindMCPBridge/main.swift
T
Kaede Akino ba46e90e30 Format with swift-format
`swift-format lint --strict --recursive Termind TermindTests
TermindMCPBridge` is in the contribution instructions but had never been
run to completion: 4133 violations across 141 files, most of them
indentation and line breaking the pretty-printer would have settled on
its own.

`swift-format format -i` accounts for all but five. The rest are
lint-only rules it will not rewrite for you:

- `SSHKeyMaterial` declared its libssh2 entry point in the C symbol's
  own spelling. `@_silgen_name` already carries the linkage name, so the
  Swift side is free to be `libssh2PublicKeyFromPrivateMemory`.
- Four `.forEach { ... }` calls become for-in loops, two in
  `PortForwardingController` and two in tests.

Formatting only, and checked as such rather than assumed: all 5409
string literals under `Termind` are byte-identical across the change, so
no localization key moved. Comparing whitespace-stripped token streams
leaves 43 files with any real change, and each is one of the rules
above, a dropped semicolon, a `case a, b` split onto separate lines with
its raw values kept explicit, or a file-scope `private` respelled
`fileprivate` — which is the same access level in that position.

Builds, and the suite stays at 975 of 975.
2026-09-02 18:09:30 +08:00

137 lines
5.5 KiB
Swift

import Darwin
import Foundation
@main
struct TermindMCPBridge {
static func main() async {
let configuration = Configuration(arguments: Array(CommandLine.arguments.dropFirst()))
guard let url = URL(string: configuration.url), !configuration.token.isEmpty else {
fail("Usage: termind-mcp [--url URL] [--token TOKEN]")
exit(2)
}
let session = URLSession(configuration: .ephemeral)
var sessionID: String?
var protocolVersion: String?
while let line = readLine(strippingNewline: true) {
guard let input = line.data(using: .utf8),
let object = try? JSONSerialization.jsonObject(with: input) as? [String: Any]
else {
fail("Ignoring a stdin line that is not a JSON-RPC object.")
continue
}
let id = object["id"]
let method = object["method"] as? String
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = input
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("application/json, text/event-stream", forHTTPHeaderField: "Accept")
request.setValue("Bearer \(configuration.token)", forHTTPHeaderField: "Authorization")
if let sessionID { request.setValue(sessionID, forHTTPHeaderField: "Mcp-Session-Id") }
if let protocolVersion {
request.setValue(protocolVersion, forHTTPHeaderField: "MCP-Protocol-Version")
}
do {
let (data, rawResponse) = try await session.data(for: request)
guard let response = rawResponse as? HTTPURLResponse else {
throw BridgeError.notHTTP
}
if let assigned = response.value(forHTTPHeaderField: "Mcp-Session-Id"),
!assigned.isEmpty
{
sessionID = assigned
}
guard (200..<300).contains(response.statusCode) else {
if id != nil {
write(errorResponse(id: id, message: "Termind HTTP server returned \(response.statusCode)."))
} else {
fail("Termind HTTP server returned \(response.statusCode) for \(method ?? "notification").")
}
continue
}
guard response.statusCode != 202, !data.isEmpty else { continue }
guard let responseObject = try? JSONSerialization.jsonObject(with: data),
JSONSerialization.isValidJSONObject(responseObject)
else {
if id != nil { write(errorResponse(id: id, message: "Termind returned invalid JSON.")) }
continue
}
if method == "initialize",
let envelope = responseObject as? [String: Any],
let result = envelope["result"] as? [String: Any],
let negotiated = result["protocolVersion"] as? String
{
protocolVersion = negotiated
}
write(try JSONSerialization.data(withJSONObject: responseObject))
} catch {
if id != nil {
write(errorResponse(id: id, message: "Cannot reach Termind: \(error.localizedDescription)"))
} else {
fail("Cannot deliver \(method ?? "notification"): \(error.localizedDescription)")
}
}
}
if let sessionID {
var request = URLRequest(url: url)
request.httpMethod = "DELETE"
request.setValue("Bearer \(configuration.token)", forHTTPHeaderField: "Authorization")
request.setValue(sessionID, forHTTPHeaderField: "Mcp-Session-Id")
if let protocolVersion {
request.setValue(protocolVersion, forHTTPHeaderField: "MCP-Protocol-Version")
}
_ = try? await session.data(for: request)
}
}
private static func errorResponse(id: Any?, message: String) -> Data {
let object: [String: Any] = [
"jsonrpc": "2.0", "id": id ?? NSNull(),
"error": ["code": -32000, "message": message],
]
return (try? JSONSerialization.data(withJSONObject: object)) ?? Data()
}
private static func write(_ data: Data) {
FileHandle.standardOutput.write(data)
FileHandle.standardOutput.write(Data([0x0A]))
}
private static func fail(_ value: String) {
FileHandle.standardError.write(Data((value + "\n").utf8))
}
}
private struct Configuration {
var url: String
var token: String
init(arguments: [String], environment: [String: String] = ProcessInfo.processInfo.environment) {
url = environment["TERMIND_MCP_URL"] ?? "http://127.0.0.1:7391/mcp"
token = environment["TERMIND_MCP_TOKEN"] ?? ""
var index = 0
while index < arguments.count {
switch arguments[index] {
case "--url" where index + 1 < arguments.count:
index += 1
url = arguments[index]
case "--token" where index + 1 < arguments.count:
index += 1
token = arguments[index]
default:
break
}
index += 1
}
}
}
private enum BridgeError: LocalizedError {
case notHTTP
var errorDescription: String? { "Termind returned a non-HTTP response." }
}