It’s 5 months because the launch of SwiftMCP 1.0 and I’ve been sluggish cooking some enhancements for it. It was rewarding to see a little bit of on this bundle, judging by points and forks I might see on GitHub. Right this moment, I’m revealing the work for the client-side I’ve accomplished throughout this time.
The primary model of SwiftMCP naturally focussed totally on the server-side. You may annotate any class or actor with @MCPServer and that will expose its features with the @MCPTool attribute through MCP. Metadata is gleaned from the documentation feedback for the operate and parameters. This implies – as a developer – you don’t should spend any additional effort to show instruments on your app through MCP. You solely should assume what API floor you want to expose after which design the features such that they’ve simple to know and documented prototypes.
SwiftMCP takes the metadata it derives straight out of your code through macros and generates API responses. The initialize response accommodates all MCP device descriptions, and their enter schemas. Due to the place MCP comes from – offering textual content enter to LLMs – no thought appears to have been given to the concept that there might be structured responses. Due to this fact MCP spec is lacking output schemas.
In distinction the OpenAPI spec does have output schemas in addition to a level of help for error responses. It will turn out to be related additional down.
The Case for the Shopper
Regardless of this limitation MCP has turn out to be the de issue commonplace for JSON RPC operate calling. In case you are designing an API for an LLM, why not additionally name these instruments from your personal packages?
Earlier than at the moment, in the event you needed to name into any MCP Server you would need to assemble JSON messages and ship these off to your server. Perhaps some open supply framework would make this barely much less bothersome, however it’s nonetheless not very “Swift-y”.
Having stated that, it’s price mentioning that SwiftMCP additionally will serve the instruments through OpenAPI JSON RPC operate calling, in the event you allow this characteristic. You would then use a proxy generator what builds you Swift varieties and features. Then you definately might conveniently work together with this native code.
That bought me pondering: I do have all meta data throughout the @MCPServer macro. So at the least for the code that you simply management you possibly can generate the client-side proxy. For arbitrary different MCP Servers you possibly can at the least generate shopper code with the JSON varieties from the enter schemas.
A number of widespread varieties get mapped to JSON strings which a further format specifier. For instance Date will get mapped to String with format date-time. So for MCP Servers that inform us the format we will derive the native Swift varieties from this. Different varieties with related therapy are URL, UUID and Information.
At the moment the one solution to get the native output/return varieties is to have an identical OpenAPI spec for the MCP Server and “steal” the categories from there. Static varieties for customized errors are a lot tougher to tug off, in order that was out-of-scope for now.
Situation 1 – You Management the Code for Server and Shopper
In the event you management either side of the equation, then you might be most fortunate. The @MCPServer macro has gained a brand new capacity to generat client-code for you. You awake the sleeper agent by including `generateClient: true` to the macro. This causes it to generated a nested Shopper sort throughout the sort.
@MCPServer(generateClient: true)
actor Calculator {
@MCPTool
func add(a: Int, b: Int) -> Int {
a + b
}
}
Then to hook up with the shopper proxy, you specify a configuration for the transport and instantiate a proxy.
let url = URL(string: "http://localhost:8080/sse")!
let config = MCPServerConfig.sse(config: MCPServerSseConfig(url: url))
let proxy = MCPServerProxy(config: config)
attempt await proxy.join()
let shopper = Calculator.Shopper(proxy: proxy)
let outcome = attempt await shopper.add(a: 2, b: 3)
The shopper API floor will precisely match the unique features with one exception: All features are actually throws as a result of an issue might happen on the shopper with speaking with the server.
This path will retain every type, together with Int and Double (which each get despatched as quantity in JSON) and likewise struct. Every kind which can be Codable and Sendable can be utilized.
This opens up the use case that you’ve your MCP code in a shared bundle to permit you importing the MCPServer each out of your servers and shoppers, together with structs you wish to use as enter or output.
Situation 2 – You Management Solely the Shopper Code
That is tougher, however I bought you coated however. To generate native proxy code for any arbitrary MCP Server there’s now a CLI device. This connects to a server (over community or STDIO) and will get the instruments schemas. You may compile the utility and name it as proven, or alternatively you possibly can precede it with swift run from the challenge folder.
SwiftMCPUtility generate-proxy --sse http://localhost:8080/sse -o ToolsProxy.swift
It will generate the code for the native proxy. As said above the limitation right here is that sure varieties can solely be modeled as native varieties if the inputSchema for string values has format data. With out additional data return varieties all should be string.
If the server additionally serves an OpenAPI spec, then this could complement the sort data for output varieties.
SwiftMCPUtility generate-proxy
--sse http://localhost:8080/sse
--openapi http://localhost:8080/openapi.json
-o ToolsProxy.swift
The proxy generator additionally creates nested struct declarations. Let’s take a look at a kind of generated proxy features within the ProxyDemoCLI demo.
/**
Customized description: Performs addition of two numbers
- Parameter a: First quantity so as to add
- Parameter b: Second quantity so as to add
- Returns: The sum of a and b
*/
public func add(a: Double, b: Double) async throws -> Double {
var arguments: [String: any Sendable] = [:]
arguments["a"] = a
arguments["b"] = b
let textual content = attempt await proxy.callTool("add", arguments: arguments)
return attempt MCPClientResultDecoder.decode(Double.self, from: textual content)
}
You may see how the arguments are put into the arguments dictionary after which the proxy’s callTool features is named for the “add” command. This takes care of the sort conversion in addition to the JSON-RPC messaging. The response then will get transformed again to the native sort of Double.
Right here’s one other instance from the demo that returns a customized struct.
public actor SwiftMCPDemoProxy {
/// A useful resource content material implementation for recordsdata within the file system
public struct RandomFileResponseItem: Codable, Sendable {
/// The binary content material of the useful resource (if it is a binary useful resource)
public let blob: Information?
/// The MIME sort of the useful resource
public let mimeType: String?
/// The textual content content material of the useful resource (if it is a textual content useful resource)
public let textual content: String?
/// The URI of the useful resource
public let uri: URL?
}
/**
A operate returning a random file
- Returns: A a number of easy textual content recordsdata
*/
public func randomFile() async throws -> [RandomFileResponseItem] {
let textual content = attempt await proxy.callTool("randomFile")
return attempt MCPClientResultDecoder.decode([RandomFileResponseItem].self, from: textual content)
}
Right here you possibly can see that the proxy generator created a struct for the return sort. The operate randomFile decodes an array of this sort from the JSON response. This manner you get a statically typed Swift struct as a substitute of a dictionary of arbitrary worth varieties.
Since that is generated code you possibly can really additional customise it and for instance use your personal struct declarations. You simply have to interchange the generated sort with your personal and this can work so long as it matches the JSON.
Against this the identical operate as talked about in state of affairs 1 is ready to reference a public sort and due to this fact no additional sort declaration is important.
Conclusion
Please observe that there would possibly nonetheless be some tough edges as this performance is model new. In the event you discover an issue or have a suggestion please let me know within the GitHub points. How do you employ SwiftMCP?
Associated
Classes: Administrative

