Skip to content

Commit 6795e34

Browse files
committed
Merge branch 'async'
2 parents 3b11f91 + be387b9 commit 6795e34

31 files changed

+976
-584
lines changed

.swiftpm/xcode/xcshareddata/xcschemes/SwiftMCPDemo.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
</CommandLineArgument>
7070
<CommandLineArgument
7171
argument = "--token secret"
72-
isEnabled = "YES">
72+
isEnabled = "NO">
7373
</CommandLineArgument>
7474
</CommandLineArguments>
7575
<EnvironmentVariables>

Demos/SwiftMCPDemo/Calculator.swift

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,33 @@ class Calculator {
5757
/// Returns a greeting message with the provided name
5858
/// - Parameter name: Name of the person to greet
5959
/// - Returns: The greeting message
60-
@MCPTool(description: "Shows a greeting message")
61-
func greet(name: String) -> String {
62-
return "Hello, \(name)!"
63-
}
60+
@MCPTool(description: "Shows a greeting message")
61+
func greet(name: String) async throws -> String {
62+
// Validate name length
63+
if name.count < 2 {
64+
throw DemoError.nameTooShort(name: name)
65+
}
66+
67+
// Validate name contains only letters and spaces
68+
if !name.allSatisfy({ $0.isLetter || $0.isWhitespace }) {
69+
throw DemoError.invalidName(name: name)
70+
}
71+
72+
return "Hello, \(name)!"
73+
}
74+
6475

6576
/** A simple ping function that returns 'pong' */
6677
@MCPTool
6778
func ping() -> String {
6879
return "pong"
6980
}
81+
82+
/** A function to test doing nothing, not returning anything*/
83+
@MCPTool
84+
func noop() {
85+
86+
}
7087

7188
/// Returns an array of all MCP resources defined in this type
7289
var mcpResources: [MCPResource] {

Demos/SwiftMCPDemo/Commands/HTTPSSECommand.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@ struct HTTPSSECommand: ParsableCommand {
7676
#endif
7777

7878
let calculator = Calculator()
79+
80+
81+
let mirror = Mirror(reflecting: calculator)
82+
var metadataArray: [MCPToolMetadata] = []
83+
84+
for child in mirror.children {
85+
if let metadata = child.value as? MCPToolMetadata,
86+
child.label?.hasPrefix("__mcpMetadata_") == true {
87+
metadataArray.append(metadata)
88+
}
89+
}
90+
91+
print(metadataArray.description)
92+
93+
7994
let host = String.localHostname
8095
print("MCP Server \(calculator.serverName) (\(calculator.serverVersion)) started with HTTP+SSE transport on http://\(host):\(port)/sse")
8196

Demos/SwiftMCPDemo/Commands/StdioCommand.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ struct StdioCommand: ParsableCommand {
3939
"""
4040
)
4141

42-
func run() throws {
42+
func run() async throws {
4343
#if canImport(OSLog)
4444
LoggingSystem.bootstrapWithOSLog()
4545
#endif
@@ -51,7 +51,7 @@ struct StdioCommand: ParsableCommand {
5151
fputs("MCP Server \(calculator.serverName) (\(calculator.serverVersion)) started with Stdio transport\n", stderr)
5252

5353
let transport = StdioTransport(server: calculator)
54-
try transport.run()
54+
try await transport.run()
5555
}
5656
catch let error as IOError {
5757
// Handle specific IO errors with more detail

Demos/SwiftMCPDemo/DemoError.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import Foundation
2+
3+
/// Custom errors for the demo app
4+
public enum DemoError: LocalizedError {
5+
/// When a greeting name is too short
6+
case nameTooShort(name: String)
7+
8+
/// When a greeting name contains invalid characters
9+
case invalidName(name: String)
10+
11+
/// When the greeting service is temporarily unavailable
12+
case serviceUnavailable
13+
14+
public var errorDescription: String? {
15+
switch self {
16+
case .nameTooShort(let name):
17+
return "Name '\(name)' is too short. Names must be at least 2 characters long."
18+
case .invalidName(let name):
19+
return "Name '\(name)' contains invalid characters. Only letters and spaces are allowed."
20+
case .serviceUnavailable:
21+
return "The greeting service is temporarily unavailable. Please try again later."
22+
}
23+
}
24+
}

Sources/SwiftMCP/Extensions/Array+MCPTool.swift

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,34 +12,8 @@ extension Array where Element == MCPToolMetadata {
1212
return self.map { meta in
1313
// Create properties for the JSON schema
1414
let properties = Dictionary(uniqueKeysWithValues: meta.parameters.map { param in
15-
// Create the appropriate schema based on the parameter type
16-
let schema: JSONSchema
17-
let jsonSchemaType = param.type.JSONSchemaType
18-
19-
if jsonSchemaType == "array" {
20-
// This is an array type
21-
let elementType: JSONSchema
22-
if let arrayElementType = param.type.arrayElementType {
23-
if arrayElementType.JSONSchemaType == "number" {
24-
elementType = JSONSchema.number()
25-
} else if arrayElementType.JSONSchemaType == "boolean" {
26-
elementType = JSONSchema.boolean()
27-
} else {
28-
elementType = JSONSchema.string()
29-
}
30-
} else {
31-
elementType = JSONSchema.string()
32-
}
33-
schema = JSONSchema.array(items: elementType, description: param.description)
34-
} else if jsonSchemaType == "number" {
35-
schema = JSONSchema.number(description: param.description)
36-
} else if jsonSchemaType == "boolean" {
37-
schema = JSONSchema.boolean(description: param.description)
38-
} else {
39-
schema = JSONSchema.string(description: param.description)
40-
}
41-
42-
return (param.name, schema)
15+
16+
return (param.name, param.jsonSchema)
4317
})
4418

4519
// Determine which parameters are required (those without default values)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//
2+
// MCPToolParameterInfo+JSONSchema.swift
3+
// SwiftMCP
4+
//
5+
// Created by Oliver Drobnik on 18.03.25.
6+
//
7+
8+
import Foundation
9+
10+
extension MCPToolParameterInfo {
11+
12+
var jsonSchema: JSONSchema {
13+
14+
switch type.JSONSchemaType {
15+
16+
case "array":
17+
18+
// This is an array type
19+
let elementType: JSONSchema
20+
if let arrayElementType = type.arrayElementType {
21+
if arrayElementType.JSONSchemaType == "number" {
22+
elementType = JSONSchema.number()
23+
} else if arrayElementType.JSONSchemaType == "boolean" {
24+
elementType = JSONSchema.boolean()
25+
} else {
26+
elementType = JSONSchema.string()
27+
}
28+
} else {
29+
elementType = JSONSchema.string()
30+
}
31+
32+
return JSONSchema.array(items: elementType, description: description)
33+
34+
case "number":
35+
36+
return JSONSchema.number(description: description)
37+
38+
case "boolean":
39+
40+
return JSONSchema.boolean(description: description)
41+
42+
default:
43+
44+
return JSONSchema.string(description: description)
45+
}
46+
}
47+
}

Sources/SwiftMCP/MCPMacros.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,6 @@ public macro MCPTool(description: String? = nil) = #externalMacro(module: "Swift
4646
/// }
4747
/// }
4848
/// ```
49-
@attached(member, names: named(mcpTools), named(callTool), named(__mcpServerName), named(__mcpServerVersion), named(__mcpServerDescription))
49+
@attached(member, names: named(callTool), named(__mcpServerName), named(__mcpServerVersion), named(__mcpServerDescription))
5050
@attached(extension, conformances: MCPServer)
5151
public macro MCPServer(name: String? = nil, version: String? = nil) = #externalMacro(module: "SwiftMCPMacros", type: "MCPServerMacro")

Sources/SwiftMCP/Models/JSONRPC/JSONRPCRequest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public struct JSONRPCRequest: Codable {
88
public let method: String?
99
public let params: [String: AnyCodable]?
1010

11-
public init(jsonrpc: String, id: Int?, method: String, params: [String: AnyCodable]? = nil) {
11+
public init(jsonrpc: String, id: Int?, method: String?, params: [String: AnyCodable]? = nil) {
1212
self.jsonrpc = jsonrpc
1313
self.id = id
1414
self.method = method

Sources/SwiftMCP/Models/MCPRequestResponse.swift

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,3 @@ public struct MCPErrorResponse: Codable {
117117
self.error = error
118118
}
119119
}
120-
121-
/// Process MCP requests and generate appropriate responses
122-
/// - Parameter request: The MCPRequest to handle
123-
/// - Returns: An optional MCPResponse if the request can be handled
124-
public func handleRequest(_ request: MCPRequest) -> MCPResponse? {
125-
if request.method == "hello" {
126-
let name = request.params?["name"] ?? "World"
127-
return MCPResponse(id: request.id, result: ["message": "Hello, \(name)!"])
128-
}
129-
return nil
130-
}

0 commit comments

Comments
 (0)