|
1 | 1 | # SwiftMCP |
2 | 2 |
|
3 | | -SwiftMCP is a Swift package that provides a way to generate JSON descriptions of functions for use in a Multi-Call Protocol (MCP) system. It uses Swift macros to extract function metadata at compile time. |
| 3 | +SwiftMCP is a Swift library that makes supporting the Model Context Protocol (MCP) easy. It uses Swift macros to automatically extract function metadata and generate the necessary JSON-RPC interface for MCP communication. |
| 4 | + |
| 5 | +## What is MCP? |
| 6 | + |
| 7 | +The Model Context Protocol (MCP) is a standardized way for AI models to interact with external tools and services. SwiftMCP makes it simple to expose your Swift functions as MCP-compatible tools that can be called by AI models. |
| 8 | + |
| 9 | +## MCP Transport Modes |
| 10 | + |
| 11 | +MCP supports two transport modes: |
| 12 | + |
| 13 | +- **stdio mode** ✅ - Fully implemented in SwiftMCP |
| 14 | + - Communication happens over standard input/output |
| 15 | + - Simple to implement and use for command-line tools |
| 16 | + - Perfect for local development and testing |
| 17 | + |
| 18 | +- **HTTP+SSE mode** ⏳ - Not yet implemented |
| 19 | + - Communication over HTTP with Server-Sent Events |
| 20 | + - Better for networked applications and services |
| 21 | + - Coming in a future release |
4 | 22 |
|
5 | 23 | ## Features |
6 | 24 |
|
7 | | -- Automatically extracts function parameter types and return types |
8 | | -- Generates JSON descriptions of functions with proper type information |
9 | | -- Supports parameters with default values |
10 | | -- Handles numeric default values correctly in JSON output |
11 | | -- Simple to use with Swift macros |
12 | | -- Command-line interface for one-off testing and interactive mode |
| 25 | +- **Simple Macro-Based API**: Just add `@MCPServer` and `@MCPTool` annotations to your code |
| 26 | +- **Automatic Documentation Extraction**: Parameter names, types, and descriptions are extracted from your Swift documentation |
| 27 | +- **JSON-RPC Interface**: Fully compliant with the MCP specification |
| 28 | +- **Type Safety**: Leverages Swift's type system for safe parameter handling |
| 29 | +- **Default Values Support**: Handles parameters with default values |
| 30 | +- **Command-Line Interface**: Ready-to-use CLI for testing and integration |
| 31 | + |
| 32 | +## Quick Start |
13 | 33 |
|
14 | | -## Usage |
| 34 | +Here's how to create an MCP-compatible server in just a few lines of code: |
15 | 35 |
|
16 | 36 | ```swift |
17 | 37 | import SwiftMCP |
18 | 38 |
|
19 | | -@MCPServer |
| 39 | +// 1. Annotate your class with @MCPServer |
| 40 | +@MCPServer(name: "MyCalculator", version: "1.0.0") |
20 | 41 | class Calculator { |
| 42 | + // 2. Add documentation comments that describe your function and parameters |
21 | 43 | /// Adds two integers and returns their sum |
22 | 44 | /// - Parameter a: First number to add |
23 | 45 | /// - Parameter b: Second number to add |
24 | 46 | /// - Returns: The sum of a and b |
| 47 | + // 3. Annotate your function with @MCPTool |
25 | 48 | @MCPTool |
26 | 49 | func add(a: Int, b: Int) -> Int { |
27 | 50 | return a + b |
28 | 51 | } |
29 | 52 |
|
30 | | - /// Subtracts the second integer from the first and returns the difference |
31 | | - /// - Parameter a: Number to subtract from |
32 | | - /// - Parameter b: Number to subtract (defaults to 3) |
33 | | - /// - Returns: The difference between a and b |
| 53 | + /// Divides the numerator by the denominator |
| 54 | + /// - Parameter numerator: Number to be divided |
| 55 | + /// - Parameter denominator: Number to divide by (defaults to 1.0) |
| 56 | + /// - Returns: The quotient of numerator divided by denominator |
34 | 57 | @MCPTool |
35 | | - func subtract(a: Int, b: Int = 3) -> Int { |
36 | | - return a - b |
| 58 | + func divide(numerator: Double, denominator: Double = 1.0) -> Double { |
| 59 | + return numerator / denominator |
37 | 60 | } |
38 | 61 | } |
39 | 62 |
|
40 | | -// Get JSON descriptions of all functions |
41 | | -let tools = calculator.mcpTools |
42 | | -let json = MCPTool.encodeToJSON(tools) |
43 | | -print(json) |
| 63 | +// 4. That's it! Your class now has MCP capabilities |
| 64 | +let calculator = Calculator() |
| 65 | + |
| 66 | +// Process MCP requests |
| 67 | +let request = JSONRPCRequest( |
| 68 | + jsonrpc: "2.0", |
| 69 | + id: 1, |
| 70 | + method: "tools/call", |
| 71 | + params: [ |
| 72 | + "name": AnyCodable("add"), |
| 73 | + "arguments": AnyCodable(["a": 5, "b": 3]) |
| 74 | + ] |
| 75 | +) |
| 76 | + |
| 77 | +// The response will be a properly formatted MCP response |
| 78 | +let response = calculator.handleRequest(request) |
44 | 79 | ``` |
45 | 80 |
|
46 | | -The `MCPTool` macro automatically: |
47 | | -- Extracts parameter names and types from the function declaration |
48 | | -- Captures documentation comments for descriptions |
49 | | -- Detects default parameter values |
50 | | -- Generates metadata at compile time |
51 | | - |
52 | | -The `@MCPTool` macro adds a `mcpTools` computed property that collects all the function metadata and converts it to a format suitable for JSON encoding. |
53 | | - |
54 | | -## JSON Output |
55 | | - |
56 | | -The generated JSON includes detailed information about each function, including parameter types, descriptions, and default values: |
57 | | - |
58 | | -```json |
59 | | -[ |
60 | | - { |
61 | | - "description": "Adds two integers and returns their sum", |
62 | | - "inputSchema": { |
63 | | - "properties": { |
64 | | - "a": { |
65 | | - "description": "First number to add", |
66 | | - "type": "number" |
67 | | - }, |
68 | | - "b": { |
69 | | - "description": "Second number to add", |
70 | | - "type": "number" |
71 | | - } |
72 | | - }, |
73 | | - "required": [ |
74 | | - "a", |
75 | | - "b" |
76 | | - ], |
77 | | - "type": "object" |
78 | | - }, |
79 | | - "name": "add" |
80 | | - }, |
81 | | - { |
82 | | - "description": "Subtracts the second integer from the first and returns the difference", |
83 | | - "inputSchema": { |
84 | | - "properties": { |
85 | | - "a": { |
86 | | - "description": "Number to subtract from", |
87 | | - "type": "number" |
88 | | - }, |
89 | | - "b": { |
90 | | - "default": 3, |
91 | | - "description": "Number to subtract (defaults to 3)", |
92 | | - "type": "number" |
93 | | - } |
94 | | - }, |
95 | | - "required": [ |
96 | | - "a" |
97 | | - ], |
98 | | - "type": "object" |
99 | | - }, |
100 | | - "name": "subtract" |
101 | | - } |
102 | | -] |
103 | | -``` |
| 81 | +## How It Works |
104 | 82 |
|
105 | | -## Command-Line Interface |
| 83 | +SwiftMCP uses Swift macros to analyze your code at compile time: |
106 | 84 |
|
107 | | -SwiftMCP includes a command-line interface for testing and interacting with your MCP tools. The CLI supports one-off requests, interactive mode, and continuous mode. |
| 85 | +1. **Documentation Extraction**: The `@MCPTool` macro extracts parameter names, types, and descriptions from your documentation comments |
| 86 | +2. **Schema Generation**: It automatically generates JSON Schema for your function parameters |
| 87 | +3. **Server Configuration**: The `@MCPServer` macro adds the necessary infrastructure to handle JSON-RPC requests |
108 | 88 |
|
109 | | -### One-Off Mode |
| 89 | +## JSON-RPC Interface |
110 | 90 |
|
111 | | -Process a single JSON-RPC request and exit: |
| 91 | +SwiftMCP implements the standard MCP JSON-RPC interface: |
112 | 92 |
|
113 | | -```bash |
114 | | -# Process a single request |
115 | | -echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "divide", "arguments": {"numerator": "10"}}}' | ./.build/debug/SwiftMCPDemo |
116 | | - |
117 | | -# With verbose logging |
118 | | -echo '{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "add", "arguments": {"a": "5", "b": "7"}}}' | ./.build/debug/SwiftMCPDemo -v |
119 | | -``` |
| 93 | +- `initialize`: Sets up the connection and returns server capabilities |
| 94 | +- `tools/list`: Returns a list of available tools with their schemas |
| 95 | +- `tools/call`: Calls a specific tool with the provided arguments |
120 | 96 |
|
121 | | -### Interactive Mode |
| 97 | +## Command-Line Interface |
122 | 98 |
|
123 | | -Process multiple requests until EOF: |
| 99 | +SwiftMCP includes a ready-to-use command-line interface for stdio mode: |
124 | 100 |
|
125 | 101 | ```bash |
126 | | -# Start in interactive mode |
127 | | -./.build/debug/SwiftMCPDemo --interactive |
128 | | - |
129 | | -# Then input JSON-RPC requests one per line |
130 | | -{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "divide", "arguments": {"numerator": "10"}}} |
131 | | -{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "add", "arguments": {"a": "5", "b": "7"}}} |
| 102 | +# Process a single request |
| 103 | +echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "divide", "arguments": {"numerator": 10}}}' | swift run SwiftMCPDemo |
132 | 104 | ``` |
133 | 105 |
|
134 | | -### Continuous Mode |
135 | | - |
136 | | -Run indefinitely, processing requests as they arrive: |
137 | | - |
138 | | -```bash |
139 | | -# Start in continuous mode |
140 | | -./.build/debug/SwiftMCPDemo --continuous |
141 | | - |
142 | | -# The server will run indefinitely, waiting for input |
143 | | -# You can send requests to it from another terminal or process |
144 | | -``` |
| 106 | +## Advanced Usage |
145 | 107 |
|
146 | | -For more reliable communication in continuous mode, you can use a named pipe: |
| 108 | +### Custom Tool Descriptions |
147 | 109 |
|
148 | | -```bash |
149 | | -# In terminal 1 (create a named pipe and start the server) |
150 | | -mkfifo /tmp/mcp_pipe |
151 | | -cat /tmp/mcp_pipe | ./.build/debug/SwiftMCPDemo --continuous --verbose |
| 110 | +You can provide a custom description for a tool: |
152 | 111 |
|
153 | | -# In terminal 2 (send requests to the pipe) |
154 | | -echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "divide", "arguments": {"numerator": "10"}}}' > /tmp/mcp_pipe |
| 112 | +```swift |
| 113 | +@MCPTool(description: "Custom description for this tool") |
| 114 | +func myFunction(param: String) -> String { |
| 115 | + // ... |
| 116 | +} |
155 | 117 | ``` |
156 | 118 |
|
157 | | -This mode is useful for long-running services that need to process requests continuously without exiting. |
| 119 | +### Server Name and Version |
158 | 120 |
|
159 | | -### Command-Line Options |
| 121 | +Customize your server's name and version: |
160 | 122 |
|
161 | | -``` |
162 | | -USAGE: mcp [--interactive] [--continuous] [--input-file <input-file>] [--output-file <output-file>] [--verbose] |
163 | | -
|
164 | | -OPTIONS: |
165 | | - --interactive Run in interactive mode, processing multiple requests until EOF |
166 | | - --continuous Run in continuous mode, processing requests indefinitely without exiting |
167 | | - -i, --input-file <input-file> |
168 | | - The input file to read from (defaults to stdin) |
169 | | - -o, --output-file <output-file> |
170 | | - The output file to write to (defaults to stdout) |
171 | | - -v, --verbose Enable verbose logging |
172 | | - -h, --help Show help information. |
| 123 | +```swift |
| 124 | +@MCPServer(name: "MyCustomServer", version: "2.5.0") |
| 125 | +class MyServer { |
| 126 | + // ... |
| 127 | +} |
173 | 128 | ``` |
174 | 129 |
|
175 | 130 | ## Requirements |
|
0 commit comments