Swift CLI MCP
用于构建基于stdio的轻量级Swift库 模型上下文协议(MCP) 服务器。
特性
- 类型安全工具 使用Codable参数验证、自动生成的模式,以及
@InputProperty注释 - 资源 用于公开文件和数据,支持URI模板
- 提示 用于具有类型化参数的可重用提示模板
- 日志记录 具有客户端控制的日志级别(
logging/setLevel) - 并发请求处理 背压和请求取消
- 平滑关闭 信号/信号情报
- 完全符合JSON-RPC 2.0标准
安装
添加到您的 Package.swift:
dependencies: [
.package(url: "https://github.com/alexmx/swift-cli-mcp.git", from: "1.0.0")
]快速开始
import SwiftMCP
struct EchoArgs: MCPToolInput {
@InputProperty("The message to echo")
var message: String
}
let server = MCPServer(
name: "my-tools",
version: "1.0.0",
tools: [
.tool(name: "echo", description: "Echo a message") { (args: EchoArgs) in
.text("Echo: \(args.message)")
}
],
resources: [
.textResource(uri: "config://version", name: "Version", mimeType: "text/plain") { _ in
"1.0.0"
}
],
prompts: [
.prompt(name: "greet", description: "Generate a greeting", arguments: [
.required(name: "name", description: "Name to greet")
]) { args in
.userMessage("Say hello to \(args["name"]!)")
}
]
)
await server.run()架构是从自动生成的 EchoArgs --属性类型、必填字段和描述都是从结构定义中推断出来的。
工具
键入参数 @InputProperty
使用 @InputProperty 将描述与您的房产放在同一位置。模式是自动生成的——属性类型是推断出来的(String → "string", Int → "integer", Bool → "boolean", Double → "number")非可选属性标记为必填项:
struct ListFilesArgs: MCPToolInput {
@InputProperty("Directory path")
var path: String
@InputProperty("Include subdirectories")
var recursive: Bool?
}
.tool(name: "list_files", description: "List files in a directory") { (args: ListFilesArgs) in
let files = try FileManager.default.contentsOfDirectory(atPath: args.path)
return .text(files.joined(separator: "\n"))
}简单工具
对于没有参数或只有一个字符串参数的工具:
// No arguments
.tool(name: "ping", description: "Check server status") {
.text("pong")
}
// Single string argument
.tool(name: "echo", description: "Echo a message", argumentName: "message", argumentDescription: "The message to echo") { message in
.text("Echo: \(message)")
}手动模式
覆盖自动生成以实现完全控制:
.tool(
name: "list_files",
description: "List files in a directory",
schema: MCPSchema(
properties: [
"path": .string("Directory path"),
"recursive": .boolean("Include subdirectories")
],
required: ["path"]
)
) { (args: ListFilesArgs) in
let files = try FileManager.default.contentsOfDirectory(atPath: args.path)
return .text(files.joined(separator: "\n"))
}多个内容块
在单个响应中返回多个内容项:
.tool(name: "report", description: "Generate report") { (args: ReportArgs) in
.content([
.text("# Report\n\nGenerated at \(Date())"),
.text("Status: Complete"),
.image(data: chartData, mimeType: "image/png")
])
}错误处理
错误会被自动捕获并返回给客户端:
.tool(name: "divide", description: "Divide two numbers") { (args: DivideArgs) in
guard args.b != 0 else {
throw NSError(domain: "math", code: 1, userInfo: [NSLocalizedDescriptionKey: "Division by zero"])
}
return .text("Result: \(args.a / args.b)")
}类型不匹配和缺少必填字段将自动验证。
资源
公开文件、日志或动态数据:
// Text resource — handler returns String, URI plumbed automatically
.textResource(uri: "file:///logs/app.log", name: "Application Log", mimeType: "text/plain") { _ in
try String(contentsOfFile: "/var/log/app.log")
}
// Binary resource — handler returns Data, URI plumbed automatically
.blobResource(uri: "img://logo", name: "Logo", mimeType: "image/png") { _ in
try Data(contentsOf: URL(fileURLWithPath: "/assets/logo.png"))
}
// Full handler when you need custom MCPResourceContents
.resource(uri: "system://stats", name: "System Stats", mimeType: "application/json") {
let stats = """
{"cpu": \(ProcessInfo.processInfo.processorCount)}
"""
return .text(uri: "system://stats", stats, mimeType: "application/json")
}资源模板
通告客户端可以填写的URI模式(RFC 6570):
resourceTemplates: [
.template(uriTemplate: "file:///{path}", name: "Project Files", mimeType: "text/plain"),
.template(uriTemplate: "db:///{table}/{id}", name: "Database Records")
]提示
定义具有类型化参数的可重用提示模板:
.prompt(
name: "code_review",
description: "Review code for issues",
arguments: [
.required(name: "code", description: "The code to review"),
.optional(name: "language", description: "Programming language")
]
) { args in
let code = args["code"] ?? ""
let lang = args["language"] ?? "unknown"
return .userMessage(
"Review this \(lang) code for bugs and improvements:\n\n```\(lang)\n\(code)\n```",
description: "Code review prompt"
)
}多消息提示
.prompt(name: "interview", description: "Technical interview") { _ in
.result(messages: [
.user("Ask me a technical question about Swift concurrency."),
.assistant("I'll ask you about structured concurrency and actors.")
])
}日志记录
向客户端发送日志
await server.sendLog(level: .info, message: "Processing started")
await server.sendLog(level: .warning, message: "Resource usage high", logger: "monitor")客户端可以通过以下方式控制最低日志级别 logging/setLevel。低于最低值的邮件将自动过滤。
可用级别(按严重程度): debug, info, notice, warning, error, critical, alert, emergency
自定义服务器日志记录
控制内部服务器日志的位置:
let server = MCPServer(
name: "my-server",
version: "1.0.0",
tools: [...],
logHandler: { message in
print("[\(Date())] \(message)")
}
)并发
请求是并发分派的,因此缓慢的工具处理程序不会阻止其他请求。服务器应用具有最大并发限制(16)的背压,并支持通过以下方式取消请求 notifications/cancelled.
模式
使用类型化属性定义架构:
MCPSchema(
properties: [
"name": .string("User's name"),
"age": .integer("User's age"),
"active": .boolean("Account status"),
"score": .number("Performance score")
],
required: ["name"]
)合并可重用属性的架构:
let base = MCPSchema(properties: ["apiKey": .string("API key")], required: ["apiKey"])
let extended = base.merging(MCPSchema(properties: ["timeout": .integer("Timeout")]))支持的MCP方法
| 方法 | 说明 |
|---|---|
initialize | 服务器信息和功能 |
ping | 健康检查 |
tools/list | 列出可用工具 |
tools/call | 执行工具 |
resources/list | 列出可用资源 |
resources/read | 阅读资源 |
resources/templates/list | 列出URI模板 |
prompts/list | 列出可用提示 |
prompts/get | 获取渲染提示 |
logging/setLevel | 设置最低日志级别 |
notifications/cancelled | 取消飞行中的请求 |
需求
- Swift 6.0+
- macOS 15.0+
资源
许可证
MIT许可证-请参阅 许可证 文件以获取详细信息。
