swift-fast-mcp
The fastest way to build MCP servers in Swift.
Install / Use
claude mcp add mehmetbaykar -- npx -y github:mehmetbaykar/swift-fast-mcpIf the server publishes to npm under a different name, use that package instead — check the repo README.
MCP Server
Model Context Protocol server
Quality Score
Category
Development & EngineeringSupported Platforms
Tags
Skill content
View source on GitHubFastMCP
The fastest way to build MCP servers in Swift.
<p align="center"> If you found this helpful, you can support more open source work! <br><br> <a href="https://buymeacoffee.com/mehmetbaykar" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="60"></a> </p>try await FastMCP.builder()
.name("My Server")
.addTools([WeatherTool()])
.run()
Three lines to a working MCP server over stdio. Or serve over HTTP:
try await FastMCP.builder()
.name("My Server")
.addTools([WeatherTool()])
.transport(.http(port: 8080))
.run()
Or aggregate tools from another Streamable HTTP MCP server and expose them beside your local Swift tools:
try await FastMCP.builder()
.name("Gateway")
.addTools([WeatherTool()])
.addUpstreamMCPServers([
.streamableHTTP(
name: "firecrawl",
endpoint: URL(string: "https://mcp.firecrawl.dev/v2/mcp")!,
headers: ["Authorization": "Bearer <token>"]
)
])
.run()
Installation
Add to your Package.swift:
dependencies: [
.package(url: "https://github.com/mehmetbaykar/swift-fast-mcp", from: "2.7.0")
]
Then add "FastMCP" as a dependency of your target:
.target(
name: "MyServer",
dependencies: [
.product(name: "FastMCP", package: "swift-fast-mcp")
]
)
FastMCP pulls in these dependencies automatically:
- swift-sdk -- Official MCP Swift SDK
- swift-ai-hub --
@Tool/@Generable/@Parameter/@Guidemacros and theToolprotocol surface - swift-service-lifecycle -- Graceful shutdown
- swift-syntax -- Compiler plugin for
@MCPPrompt/@MCPResource - swift-nio -- HTTP transport (NIO is an internal dependency; not re-exported)
FastMCPAIBridge is intentionally public and re-exported by FastMCP for
advanced bridge testing and custom adapter use. Most applications should still
prefer the builder APIs.
Quick Start
Stdio transport (default)
import FastMCP
@Tool("Get weather for a location")
struct WeatherTool {
@Parameter("City or coordinates")
var location: String = ""
func execute() async throws -> String {
"Weather in \(location): 22°C, Sunny"
}
}
@main
struct MyServer {
static func main() async throws {
try await FastMCP.builder()
.name("Weather Server")
.addTools([WeatherTool()])
.run()
}
}
Connect it to Claude Desktop by adding to claude_desktop_config.json:
{
"mcpServers": {
"weather": {
"command": "/path/to/my-server"
}
}
}
HTTP transport
@main
struct MyServer {
static func main() async throws {
try await FastMCP.builder()
.name("Weather Server")
.addTools([WeatherTool()])
.transport(.http(port: 8080))
.run()
// Listening on http://127.0.0.1:8080/mcp
}
}
Transport Options
public enum Transport: Sendable {
case stdio
case inMemory
case http(
mode: HTTPMode = .stateful,
host: String = "127.0.0.1",
port: Int = 3000,
endpoint: String = "/mcp"
)
case custom(MCP.Transport)
}
| Transport | Use Case |
|-----------|----------|
| .stdio | Claude Desktop, CLI tools. Default. |
| .inMemory | Unit testing. |
| .http(...) | Remote servers, multi-client access, web deployments. |
| .custom(transport) | Provide your own MCP.Transport implementation. |
HTTPMode
public enum HTTPMode: Sendable {
case stateful
case stateless
}
Stateful (default) -- Full MCP Streamable HTTP. Each client gets a session with SSE streaming, resumability via Last-Event-ID, GET for server-initiated messages, and DELETE for session teardown.
Stateless -- Minimal HTTP. No sessions, direct JSON responses, POST only. Use when session management is handled externally or not needed.
// Stateful (default)
.transport(.http(port: 8080))
.transport(.http(mode: .stateful, host: "0.0.0.0", port: 3000, endpoint: "/mcp"))
// Stateless
.transport(.http(mode: .stateless, port: 8080))
Builder API
All builder methods return a new Builder (value semantics) and can be chained.
Server metadata
.name("My Server") // Server name (default: process name)
.version("2.7.0") // Server version (default: "1.0.0")
.title("My Display Name") // Human-readable display name for UIs
.instructions("Use this server to...") // Instructions for LLM clients
.icons([...]) // Server icons for display in UIs
Capabilities
try FastMCP.builder()
.addTools([WeatherTool(), MathTool(), StructuredSearchTool()]) // Register tool implementations
.addUpstreamMCPServers([ // Proxy Streamable HTTP upstream tools as docs_*
.streamableHTTP(name: "docs", endpoint: docsMCPURL)
])
.addResources([ConfigResource()]) // Register resource implementations
.addPrompts([GreetingPrompt()]) // Register prompt implementations
.enableCompletions() // Advertise completions capability
.enableLogging() // Advertise logging capability
Duplicate upstream server names and tool names fail early. Resources and prompts are deduplicated automatically; the first registration wins.
Deferred SwiftAIHub tool sources resolve asynchronously, so addTools(_:) is
also available as an async throws builder call:
import FastMCP
try await FastMCP.builder()
.addTools(localOrDeferredToolSource)
.run()
Use addUpstreamMCPServers(...) when you want FastMCP to act as an MCP gateway
for remote MCP tools while preserving their MCP descriptors and call results.
Transport and infrastructure
.transport(.stdio) // Transport selection (default: .stdio)
.logger(myLogger) // Custom swift-log Logger
.shutdownSignals([.sigterm, .sigint]) // Unix signals for graceful shutdown (default: both)
Lifecycle hooks
Under .stdio (the default), stdout carries JSON-RPC frames — do not use print in hooks. Route messages through the injected Logger instead (swift-log's default handler writes to stderr). Under .http, either approach is safe.
.onStart { logger.info("Server started") }
.onShutdown { logger.info("Server stopped") }
.onInitialize { clientInfo, capabilities in
// Called when a client sends an initialize request.
// Receives Client.Info and Client.Capabilities.
// Useful for auth checks, logging, per-client setup.
// Especially valuable for HTTP where multiple clients connect.
logger.info("Client: \(clientInfo.name) v\(clientInfo.version)")
}
HTTP-specific configuration
.sessionTimeout(.seconds(1800)) // Idle session timeout (default: 3600s)
// Only applies to .http with .stateful mode.
.httpValidation(
allowedOrigins: ["https://example.com"], // Allowed Origin headers (default: localhost only)
customValidators: [MyAuthValidator()] // Custom HTTPRequestValidator implementations
)
Running
.run() // Starts the server and blocks until shutdown
HTTP Transport
Multi-session model
In stateful HTTP mode, each connecting client gets its own independent Server + Transport pair. The server manages session lifecycle automatically.
Session lifecycle (stateful mode)
- Client sends POST to
/mcpwith aninitializeJSON-RPC request (no session header). - Server creates a new
StatefulHTTPServerTransport+Serverpair, registers all tools/resources/prompts. - Transport generates a session ID, returned in the
Mcp-Session-Idresponse header. - Subsequent requests include this header and are routed to the matching session.
- Client sends DELETE to terminate the session.
- A cleanup loop runs every 60 seconds, removing sessions idle longer than
sessionTimeout.
Stateless mode
- Client sends POST to
/mcpwith a JSON-RPC request. - Server responds with direct JSON. No SSE, no session headers, no session tracking.
Validation pipeline
The HTTP server validates incoming requests before processing. By default, only localhost origins are allowed.
To allow remote origins or add custom validation (e.g., bearer token auth), use .httpValidation():
.httpValidation(
allowedOrigins: ["https://myapp.com", "https://staging.myapp.com"],
customValidators: [BearerTokenValidator(expectedToken: "...")]
)
Custom validators conform to HTTPRequestValidator from the MCP SDK.
TLS
FastMCP does not handle TLS. Deploy behind a reverse proxy (nginx, Caddy) for HTTPS.
Tools
AI-callable functions with JSON Schema generated from either flat @Parameter
stored properties plus execute(), or a nested @Generable Arguments struct
plus execute(_:). The tool's name is derived from the struct name
(MathTool → math) and its return type from the body of execute:
@Generable
enum Operation: String, CaseIterable {
case add, subtract, multiply, divide
}
@Tool("Perform math operations")
struct MathTool {
@Generable
struct Arguments {
@Parameter("Operation") var operation: Operation
@Parameter("First operand") var a: Double
@Parameter("Second operand") var b: Double
}
func execute(_ arguments: Arguments) async throws -> String {
let result = switch arguments.operation {
case .add: arguments.a + arguments.b
case .subtract: arguments.a - arguments.b
case .multiply: arguments.a * arguments.b
case .divide: arguments.a / arguments.b
}
return "Result: \(result)"
}
}
Resources
Expose data to AI models:
@MCPResource(
"config://app/settings",
name: "App Settings",
description: "Application configuration",
mimeType: .applicationJSON
)
struct ConfigResource {
@ResourceContentBuilder
var content: Content {
"""
{"theme": "dark", "version": "1.0.0"}
"""
}
}
Prompts
Reusable conversation templates with typed arguments via @PromptArgument:
@MCPPrompt("A greeting template")
struct GreetingPrompt {
@PromptArgument("Who to greet")
var name: String
@PromptArgument("Use formal tone")
var formal: Bool = false
func getMessages() async throws -> Messages {
if formal {
return [
.user("You are a formal assistant helping \(name)."),
.assistant("Good day, \(name). How may I assist you?"),
]
} else {
return [
.user("You are a friendly assistant helping \(name)."),
.assistant("Hey \(name)! What can I help you with?"),
]
}
}
}
Full Example
A complete stdio server with tools, resources, prompts, and lifecycle hooks — the canonical MCP setup for Claude Desktop and CLI clients that spawn the server as a subprocess. This matches the stdio transport/lifecycle pattern used in the shipped Sources/Example/ExampleServer.swift (which wires up its own tools and metadata). For an HTTP-specific variant (with .httpValidation, .sessionTimeout, etc.), see the HTTP transport section above.
import FastMCP
import Logging
@main
struct ExampleServer {
static func main() async throws {
let logger: Logger = {
var log = Logger(label: "my-server")
Truncated for display — read the full file on GitHub.
Related Skills
Agent-Reach
84.4kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
headroom
73.4kCompress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.
ruflo
73.0k🌊 The original agent harness. Deploy intelligent multi-player swarms, coordinate autonomous workflows, and build conversational AI systems. Features adaptive memory, self-learning intelligence, federation, vector RAG integration, and native Claude Code / Codex / Hermes and many more Integrated
career-ops
72.4kOpen-source AI job search: scan job portals, evaluate listings into a structured A-H report with a global 1-5 score, tailor your CV, track applications — runs locally in your AI coding CLI (Claude Code, Codex, OpenCode, Antigravity…)
