After 4 months of intensive improvement, I’m thrilled to announce that SwiftMCP 1.0 is feature-complete and prepared so that you can use.
For these simply becoming a member of, SwiftMCP is a local Swift implementation of the Mannequin Context Protocol (MCP). The purpose is to supply a dead-simple means for any developer to make their app, or components of it, obtainable as a robust server for AI brokers and Massive Language Fashions. You’ll be able to learn the official specification at modelcontextprotocol.io.
I did a SwiftMCP 1.0 Characteristic Pace Run on YouTube, if that’s what you like.
The Core Thought: Your Documentation is the API
Earlier than diving into options, it’s essential to grasp the philosophy of SwiftMCP. The framework is constructed on the precept that your current documentation must be the first supply of fact for an AI. Through the use of normal Swift documentation feedback, you present all of the context an AI wants to grasp and use your server’s capabilities.
/**
Provides two numbers and returns their sum.
- Parameter a: The primary quantity so as to add
- Parameter b: The second quantity so as to add
- Returns: The sum of a and b
*/
@MCPTool
func add(a: Int, b: Int) -> Int {
a + b
}
This code reveals the only use case. The @MCPTool
macro inspects the add
operate and its documentation remark. It robotically extracts the primary description (“Provides two numbers…”), the descriptions for parameters a
and b
, and the outline of the return worth, making all of this info obtainable to an AI shopper with none additional work.
Server Options: Exposing Your App’s Logic
These are the capabilities your Swift utility (the server) exposes to a shopper.
Instruments: The Basis of Motion
Instruments are the first strategy to expose your app’s performance. By adorning any operate with @MCPTool
, you make it a callable motion for an AI. A great instrument is well-documented, handles potential errors, and offers clear performance.
// Outline a easy error and enum for the instrument
enum TaskError: Error { case invalidName }
enum Precedence: String, Codable, CaseIterable { case low, medium, excessive }
/**
Schedules a activity with a given precedence.
- Parameter identify: The identify of the duty. Can't be empty.
- Parameter precedence: The execution precedence.
- Parameter delay: The delay in seconds earlier than the duty runs. Defaults to 0.
- Returns: A affirmation message.
- Throws: `TaskError.invalidName` if the identify is empty.
*/
@MCPTool
func scheduleTask(identify: String, precedence: Precedence, delay: Double = 0) async throws -> String {
guard !identify.isEmpty else {
throw TaskError.invalidName
}
// Simulate async work
strive await Job.sleep(for: .seconds(delay))
return "Job '(identify)' scheduled with (precedence.rawValue) precedence."
}
This instance demonstrates a number of key options without delay. The operate is async
to carry out work that takes time, and it throws
a customized TaskError
for invalid enter. It makes use of a CaseIterable
enum, Precedence
, as a parameter, which SwiftMCP can use to supply auto-completion to purchasers. Lastly, the delay
parameter has a default worth, making it optionally available for the caller.
Assets: Publishing Learn-Solely Information
Assets mean you can publish information that purchasers can question by URI. SwiftMCP gives a versatile system for this, which might be damaged down into two important classes: function-backed assets and provider-based assets.
Perform-Backed Assets
These assets are outlined by particular person features adorned with the @MCPResource
macro. If a operate has no parameters, it acts as a static endpoint. If it has parameters, they have to be represented as placeholders within the URI template.
/// Static Useful resource: Returns a server data string
@MCPResource("server://data")
func getServerInfo() -> String {
"SwiftMCP Demo Server v1.0"
}
/// Dynamic Useful resource: Returns a greeting for a person by ID
/// - Parameter user_id: The person's distinctive identifier
@MCPResource("customers://{user_id}/greeting")
func getUserGreeting(user_id: Int) -> String {
"Hiya, person #(user_id)!"
}
The getServerInfo
operate is a static useful resource; a shopper can request the URI server://data
and can at all times get the identical string again. The getUserGreeting
operate is dynamic; the {user_id}
placeholder within the URI tells SwiftMCP to count on a worth. When a shopper requests customers://123/greeting
, the framework robotically extracts “123”, converts it to an Int
, and passes it to the user_id
parameter.
Supplier-Based mostly Assets (like information)
For exposing a dynamic assortment of assets, like information in a listing, you possibly can conform your server to MCPResourceProviding
. This requires implementing a property to find the assets and a operate to supply their content material on request.
extension DemoServer: MCPResourceProviding {
// Announce obtainable file assets
var mcpResources: [any MCPResource] {
let docURL = URL(fileURLWithPath: "/Customers/Shared/doc.pdf")
return [FileResource(uri: docURL, name: "Shared Document")]
}
// Present the file's content material when its URI is requested
func getNonTemplateResource(uri: URL) async throws ->
[MCPResourceContent] {
guard FileManager.default.fileExists(atPath: uri.path) else {
return []
}
return strive [FileResourceContent.from(fileURL: uri)]
}
}
This code reveals the two-part mechanism. First, the mcpResources
property known as by the framework to find what assets can be found. Right here, we announce a single PDF file. Second, when a shopper really requests the content material of that file’s URI, the getNonTemplateResource(uri:)
operate known as. It verifies the file exists after which returns its contents.
Prompts: Reusable Templates for LLMs
For reusable immediate templates, the @MCPPrompt
macro works identical to @MCPTool
. It exposes a operate that returns a string or PromptMessage
objects, making its parameters obtainable for the AI to fill in.
/// A immediate for saying Hiya
@MCPPrompt()
func helloPrompt(identify: String) -> [PromptMessage] {
let message = PromptMessage(function: .assistant,
content material: .init(textual content: "Hiya (identify)!"))
return [message]
}
This instance defines a easy immediate template. An AI shopper can uncover this immediate and see that it requires a identify
parameter. The shopper can then name the immediate with a selected identify, and the server will execute the operate to assemble and return the absolutely shaped immediate message, able to be despatched to an LLM.
Progress Reporting: Dealing with Lengthy-Working Duties
For duties that take time, you possibly can report progress again to the shopper utilizing RequestContext.present
, which prevents the shopper from being left at midnight.
@MCPTool
func countdown() async -> String {
for i in (0...30).reversed() {
let finished = Double(30 - i) / 30
await RequestContext.present?.reportProgress(finished,
whole: 1.0, message: "(i)s left")
strive? await Job.sleep(nanoseconds: 1_000_000_000)
}
return "Countdown accomplished!"
}
On this operate, the server loops for 30 seconds. Contained in the loop, reportProgress
known as on the RequestContext.present
. This sends a notification again to the unique shopper that made the request, which may then use the progress worth and message to replace a UI factor like a progress bar.
Shopper Options: The Shopper is in Management
Whereas SwiftMCP is a server framework, it absolutely helps the highly effective capabilities a shopper can supply. The shopper holds a substantial amount of management, and your server can adapt its conduct by checking Session.present?.clientCapabilities
.
Roots: Managing File Entry
The shopper is in full management of what native information the server can see. When a shopper provides or removes a root listing, your server is notified and may react by implementing handleRootsListChanged()
.
func handleRootsListChanged() async {
guard let session = Session.present else { return }
do {
let updatedRoots = strive await session.listRoots()
await session.sendLogNotification(LogMessage(
degree: .data,
information: [ "message": "Roots list updated", "roots": updatedRoots ]
))
} catch {
// Deal with error...
}
}
This operate is a notification handler. When a shopper modifies its listing of shared directories (or “roots”), it sends a notification to the server. SwiftMCP robotically calls this operate, which may then use session.listRoots()
to get the up to date listing and react accordingly, for instance, by refreshing its personal listing of accessible information.
Cancellation: Stopping Duties Gracefully
If the shopper is exhibiting a progress bar for that countdown, it must also have a cancel button. The shopper can ship a cancellation notification, and your server code have to be a great citizen and verify for it with strive Job.checkCancellation()
.
Elicitation: Asking the Person for Enter
Elicitation is a robust interplay the place the server determines it wants particular, structured info. It sends a JSON schema to the shopper, and the shopper is liable for rendering a kind to “elicit” that information.
@MCPTool
func requestContactInfo() async throws -> String {
// Outline the information you want with a JSON schema
let schema = JSONSchema.object(JSONSchema.Object(
properties: [
"name": .string(description: "Your full name"),
"email": .string(description: "Your email address",
format: "email")
],
required: ["name", "email"]
))
// Elicit the knowledge from the shopper
let response = strive await RequestContext.present?.elicit(
message: "Please present your contact info",
schema: schema
)
// Deal with the person's response
change response?.motion {
case .settle for:
let identify = response?.content material?["name"]?.worth as? String ?? "Person"
return "Thanks, (identify)!"
case .decline:
return "Person declined to supply info."
case .cancel, .none:
return "Person cancelled the request."
}
}
This instrument demonstrates the three steps of elicitation. First, it defines a JSONSchema
that specifies the required fields (identify
and e-mail
). Second, it calls elicit
on the present request context, sending the schema and a message to the shopper. Third, it waits for the person’s response and makes use of a change
assertion to deal with the totally different outcomes: the person accepting, declining, or canceling the request.
Sampling: Utilizing the Shopper’s LLM
Maybe probably the most fascinating function is Sampling, which flips the script. The server can request that the shopper carry out a generative activity utilizing its personal LLM. This permits your server to be light-weight and delegate AI-heavy lifting.
@MCPTool
func sampleFromClient(immediate: String) async throws -> String {
// Test if the shopper helps sampling
guard await Session.present?.clientCapabilities?.sampling != nil else {
throw MCPServerError.clientHasNoSamplingSupport
}
// Request the era
return strive await RequestContext.present?.pattern(immediate: immediate) ?? "No response from shopper"
}
This code reveals how a server can leverage a shopper’s personal generative capabilities. It first checks if the shopper has marketed assist for sampling. If that’s the case, it calls pattern(immediate:)
, which sends the immediate to the shopper. The shopper is then liable for working the immediate via its personal LLM and returning the generated textual content, which the server receives as the results of the await
name.
What’s Subsequent?
My imaginative and prescient is for builders to combine MCP servers immediately into their Mac apps. My API.me
personal app does precisely this, exposing a person’s native emails, contacts, and calendar via a neighborhood server that an LLM can securely work together with. I’m pondering if I ought to put this on the app retailer or presumably open supply it. What do you assume?
It has been lots of work, and it’s lastly prepared. SwiftMCP 1.0 is right here.
I’m very a lot wanting ahead to your suggestions. Please give it a strive, try the examples on GitHub, and let me know what you assume. I hope to see you construct some superb issues with it.
Oh and should you haven’t watched it but, I actually suggest watching my demonstration of all the brand new options:
Associated
Classes: Updates