The Authdog MCP Swift SDK verifies a bearer token presented to your [MCP](https://modelcontextprotocol.io) 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/`](https://github.com/authdog/mcp-sdk/tree/main/swift) of [authdog/mcp-sdk](https://github.com/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](https://github.com/authdog/mcp-sdk) and add the `swift/` directory as a local package in your `Package.swift`:

```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

```swift
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

```swift
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:

```swift
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](/docs/mcp-sdk): all language token verifiers
- [MCP authentication](/docs/mcp-authentication): how Authdog authenticates MCP clients
