Official Swift SDK for the [Model Context Protocol][mcp] (MCP).
The Model Context Protocol (MCP) defines a standardized way for applications to communicate with AI and ML models. This Swift SDK implements both client and server components according to the [2025-11-25][mcp-spec-2025-11-25] (latest) version of the MCP specification.
See the Platform Availability section below for platform-specific requirements.
Add the following to your Package.swift file:
dependencies: [
.package(url: "https://github.com/modelcontextprotocol/swift-sdk.git", from: "0.11.0")
]
Then add the dependency to your target:
.target(
name: "YourTarget",
dependencies: [
.product(name: "MCP", package: "swift-sdk")
]
)
The client component allows your application to connect to MCP servers.
import MCP
// Initialize the client
let client = Client(name: "MyApp", version: "1.0.0")
// Create a transport and connect
let transport = StdioTransport()
let result = try await client.connect(transport: transport)
// Check server capabilities
if result.capabilities.tools != nil {
// Server supports tools (implicitly including tool calling if the 'tools' capability object is present)
}
[!NOTE] The
Client.connect(transport:)method returns the initialization result. This return value is discardable, so you can ignore it if you don't need to check server capabilities.
For local subprocess communication:
// Create a stdio transport (simplest option)
let transport = StdioTransport()
try await client.connect(transport: transport)
For remote server communication:
// Create a streaming HTTP transport
let transport = HTTPClientTransport(
endpoint: URL(string: "http://localhost:8080")!,
streaming: true // Enable Server-Sent Events for real-time updates
)
try await client.connect(transport: transport)
Tools represent functions that can be called by the client:
// List available tools
let (tools, cursor) = try await client.listTools()
print("Available tools: \(tools.map { $0.name }.joined(separator: ", "))")
// Call a tool with arguments and get the result
let (content, isError) = try await client.callTool(
name: "image-generator",
arguments: [
"prompt": "A serene mountain landscape at sunset",
"style": "photorealistic",
"width": 1024,
"height": 768
]
)
// Handle tool content
for item in content {
switch item {
case .text(let text):
print("Generated text: \(text)")
case .image(let data, let mimeType, let metadata):
if let width = metadata?["width"] as? Int,
let height = metadata?["height"] as? Int {
print("Generated \(width)x\(height) image of type \(mimeType)")
// Save or display the image data
}
case .audio(let data, let mimeType):
print("Received audio data of type \(mimeType)")
case .resource(let resource, _, _):
print("Received embedded resource: \(resource)")
case .resourceLink(let uri, let name, _, _, let mimeType, _):
print("Resource link: \(name) at \(uri), type: \(mimeType ?? "unknown")")
}
}
Resources represent data that can be accessed and potentially subscribed to:
// List available resources
let (resources, nextCursor) = try await client.listResources()
print("Available resources: \(resources.map { $0.uri }.joined(separator: ", "))")
// Read a resource
let contents = try await client.readResource(uri: "resource://example")
print("Resource content: \(contents)")
// Subscribe to resource updates if supported
if result.capabilities.resources?.subscribe == true {
try await client.subscribeToResource(uri: "resource://example")
// Register notification handler
await client.onNotification(ResourceUpdatedNotification.self) { message in
let uri = message.params.uri
print("Resource \(uri) updated with new content")
// Fetch the updated resource content
let updatedContents = try await client.readResource(uri: uri)
print("Updated resource content received")
}
}
Prompts represent templated conversation starters:
// List available prompts
let (prompts, nextCursor) = try await client.listPrompts()
print("Available prompts: \(prompts.map { $0.name }.joined(separator: ", "))")
// Get a prompt with arguments
let (description, messages) = try await client.getPrompt(
name: "customer-service",
arguments: [
"customerName": "Alice",
"orderNumber": "ORD-12345",
"issue": "delivery delay"
]
)
// Use the prompt messages in your application
print("Prompt description: \(description)")
for message in messages {
if case .text(text: let text) = message.content {
print("\(message.role): \(text)")
}
}
Completions allow servers to provide autocompletion suggestions for prompt and resource template arguments as users type:
// Request completions for a prompt argument
let completion = try await client.complete(
promptName: "code_review",
argumentName: "language",
argumentValue: "py"
)
// Display suggestions to the user
for value in completion.values {
print("Suggestion: \(value)")
}
if completion.hasMore == true {
print("More suggestions available (total: \(completion.total ?? 0))")
}
You can also provide context with already-resolved arguments:
// First, user selects a language
let languageCompletion = try await client.complete(
promptName: "code_review",
argumentName: "language",
argumentValue: "py"
)
// User selects "python"
// Then get framework suggestions based on the selected language
let frameworkCompletion = try await client.complete(
promptName: "code_review",
argumentName: "framework",
argumentValue: "fla",
context: ["language": .string("python")]
)
// Returns: ["flask"]
Completions work for resource templates as well:
// Get path completions for a resource URI template
let pathCompletion = try await client.complete(
resourceURI: "file:///{path}",
argumentName: "path",
argumentValue: "/usr/"
)
// Returns: ["/usr/bin", "/usr/lib", "/usr/local"]
Sampling allows servers to request LLM completions through the client, enabling agentic behaviors while maintaining human-in-the-loop control. Clients register a handler to process incoming sampling requests from servers.
[!TIP] Sampling requests flow from server to client, not client to server. This enables servers to request AI assistance while clients maintain control over model access and user approval.
// Register a sampling handler in the client
await client.withSamplingHandler { parameters in
// Review the sampling request (human-in-the-loop step 1)
print("Server requests completion for: \(parameters.messages)")
// Optionally modify the request based on user input
var messages = parameters.messages
if let systemPrompt = parameters.systemPrompt {
print("System prompt: \(systemPrompt)")
}
// Sample from your LLM (this is where you'd call your AI service)
let completion = try await callYourLLMService(
messages: messages,
maxTokens: parameters.maxTokens,
temperature: parameters.temperature
)
// Review the completion (human-in-the-loop step 2)
print("LLM generated: \(completion)")
// User can approve, modify, or reject the completion here
// Return the result to the server
return CreateSamplingMessage.Result(
model: "your-model-name",
stopReason: .endTurn,
role: .assistant,
content: .text(completion)
)
}
Elicitation allows servers to request structured information directly from users through the client. This is useful when servers need user input that wasn't provided in the original request, such as credentials, configuration choices, or approval for sensitive operations.
[!TIP] Elicitation requests flow from server to client, similar to sampling. Clients must register a handler to respond to elicitation requests from servers.
Register an elicitation handler to respond to server requests:
```swift // Register an elicitation handler in the client await client.withElicitationHandler { parameters in switch parameters { case .form(let form): // Display the request to the user print("Server requests: (form.message)")
// If a schema was provided, inspect
| 日期 | 榜单 | 排名 | 周期新增 | 总 STAR |
|---|