The Authdog MCP Swift SDK verifies a bearer token presented to your MCP server. AuthdogVerifier.verifyToken calls GET {baseURL}/v1/userinfo with the token and returns an AuthInfo. It needs Swift 5.9+ and targets iOS 13, macOS 10.15, watchOS 6, and tvOS 13 or later. It is a token verifier only, not an OAuth client or an MCP server framework.
Availability
The Swift SDK is source-available in `swift/` of authdog/mcp-sdk. The README shows a SwiftPM dependency on the repository URL from: "0.1.0", but no release tag was verified, and Package.swift lives in the swift/ subdirectory rather than at the repository root. A URL dependency may not resolve yet. Install from source until it does.
Install
Check out authdog/mcp-sdk and add the swift/ directory as a local package in your Package.swift:
dependencies: [
.package(path: "../mcp-sdk/swift")
],
targets: [
.target(
name: "MyMCPServer",
dependencies: [
.product(name: "AuthdogMCP", package: "swift")
]
)
]The package name is AuthdogMCP in its manifest, but SwiftPM identifies a local path package by its directory name, so the package: value above is swift. If you rename the checkout directory, change it to match. The library has no third-party dependencies.
Configure
import AuthdogMCP
var config = AuthdogVerifierConfig(baseURL: "https://api.authdog.com")
config.timeout = 30
let verifier = AuthdogVerifier(config: config)| Field | Type | Default | Notes |
|---|---|---|---|
baseURL |
String |
required | Authdog API base URL. Leading and trailing / characters are trimmed. |
timeout |
TimeInterval |
10 |
Seconds. Applied to the request and, on the default session, as both request and resource timeout. |
AuthdogVerifier(config:session:) also accepts an optional URLSession, mainly for tests. A supplied session replaces the default one. Every request sends User-Agent: authdog-mcp-swift-sdk/0.1.0.
Verify a token
do {
let auth = try await verifier.verifyToken("the-bearer-token")
print(auth.subject, auth.username, auth.email ?? "n/a")
} catch AuthdogError.authentication(let message) {
// HTTP 401
} catch AuthdogError.api(let message) {
// any other failure
}It sends Authorization: Bearer <token> and, on a 2xx response, maps the body to AuthInfo:
| Field | Type | Source |
|---|---|---|
token |
String |
the verified access token |
subject |
String |
user.id |
username |
String |
user.userName |
email |
String? |
first user.emails value, else nil |
scopes |
[String] |
always [] (userinfo returns no scopes) |
expiresAt |
Date? |
now + session.remainingSeconds |
user |
User |
full decoded user record |
Do not use scopes for authorization yet. The verifier does not cache results; each call is one HTTP request.
The response is decoded with a strict Codable model. User.phoneNumbers and User.addresses are typed [String], so a userinfo response where those arrays contain objects would fail to decode and surface as AuthdogError.api.
Wire into an MCP server
Use the verifier in your transport's authorization hook. Extract the bearer token, verify it, and reject on failure:
func authenticate(authorizationHeader: String?) async throws -> AuthInfo {
guard let header = authorizationHeader,
header.hasPrefix("Bearer ") else {
throw AuthdogError.authentication("Missing bearer token")
}
let token = String(header.dropFirst("Bearer ".count))
return try await verifier.verifyToken(token)
}The prefix check is case-sensitive.
Errors
AuthdogError is an enum conforming to Error and LocalizedError.
| Case | When |
|---|---|
.authentication(String) |
HTTP 401. Message: Unauthorized - invalid or expired token. |
.api(String) |
Invalid base URL, transport failure, non-HTTP response, any other non-2xx status (includes the body's error string when present), or a 2xx body that fails to decode. |
Related
- MCP SDKs: all language token verifiers
- MCP authentication: how Authdog authenticates MCP clients