The Authdog MCP Kotlin 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 a normalized `AuthInfo`. It requires JDK 17 (the build uses a Kotlin JVM toolchain of 17) and Kotlin 1.9.10+. It is a token verifier only, not an OAuth client or an MCP server framework.

## Availability

The Kotlin SDK is source-available in [`kotlin/`](https://github.com/authdog/mcp-sdk/tree/main/kotlin) of [authdog/mcp-sdk](https://github.com/authdog/mcp-sdk). The README lists the coordinates `com.authdog:authdog-mcp-sdk:0.1.0`, but publication to Maven Central is unverified and the artifact may not resolve from the registry yet. Install from source until it does.

## Install

Build and publish to your local Maven repository from a checkout of [authdog/mcp-sdk](https://github.com/authdog/mcp-sdk):

```bash
cd mcp-sdk/kotlin
./gradlew publishToMavenLocal
```

Then add `mavenLocal()` to your repositories and depend on the artifact:

```kotlin
repositories {
    mavenLocal()
    mavenCentral()
}

dependencies {
    implementation("com.authdog:authdog-mcp-sdk:0.1.0")
}
```

The SDK uses Kotlinx Serialization, Kotlinx Coroutines, and the JDK built-in `java.net.http.HttpClient`.

## Configure

Construct one `AuthdogVerifier` with an `AuthdogVerifierConfig`:

```kotlin
import com.authdog.mcp.*

val verifier = AuthdogVerifier(
    AuthdogVerifierConfig(
        baseUrl = "https://api.authdog.com",
        timeoutMillis = 10_000,
    )
)
```

| Field | Type | Default | Notes |
| --- | --- | --- | --- |
| `baseUrl` | `String` | required | Authdog API base URL. A trailing `/` is removed. |
| `timeoutMillis` | `Long` | `10_000` | Connect timeout of the default client and per-request timeout. |
| `httpClient` | `HttpClient?` | `null` | Custom `java.net.http.HttpClient`, mainly for tests. When supplied, `timeoutMillis` still applies per request but not as its connect timeout. |

Every request sends `User-Agent: authdog-mcp-kotlin-sdk/0.1.0`.

## Verify a token

`verifyToken` is a `suspend` function and runs the request on `Dispatchers.IO`:

```kotlin
try {
    val info: AuthInfo = verifier.verifyToken("incoming-bearer-token")
    println(info.subject)
    println(info.email ?: "(none)")
} catch (e: AuthenticationException) {
    // HTTP 401: invalid or expired token
} catch (e: ApiException) {
    // any other failure
}
```

It sends `Authorization: Bearer <token>` and, on a 2xx response, returns:

```kotlin
data class AuthInfo(
    val token: String,        // the verified access token
    val subject: String,      // user.id
    val username: String,     // user.userName
    val email: String?,       // first user.emails value, or null
    val scopes: List<String>, // always empty today
    val expiresAt: Instant?,  // now + session.remainingSeconds
    val user: User,           // full user record
)
```

`scopes` is always an empty list because userinfo does not return scopes, so do not use it for authorization yet. `expiresAt` is computed from the clock at verification time. The verifier does not cache results; each call is one HTTP request.

## Wire into an MCP server

Hook choice depends on your MCP server transport. The pattern is: read the `Authorization` header, strip the `Bearer ` prefix (case-insensitive), verify, then attach the `AuthInfo` to your request context.

```kotlin
class McpAuth(private val verifier: AuthdogVerifier) {
    suspend fun authenticate(authorizationHeader: String?): AuthInfo {
        val token = authorizationHeader
            ?.takeIf { it.startsWith("Bearer ", ignoreCase = true) }
            ?.substring(7)
            ?.trim()
            ?: throw AuthenticationException("Missing or malformed Authorization header")

        return verifier.verifyToken(token)
    }
}
```

## Errors

All errors extend `AuthdogException`.

| Exception | When |
| --- | --- |
| `AuthenticationException` | HTTP 401 from userinfo. Message: `Unauthorized - invalid or expired token`. |
| `ApiException` | Any other non-2xx status (message includes the status and body), a transport failure, or a 2xx body that cannot be parsed as the userinfo shape. |
| `AuthdogException` | Base type of both. |

## Related

- [MCP SDKs](/docs/mcp-sdk): all language token verifiers
- [MCP authentication](/docs/mcp-authentication): how Authdog authenticates MCP clients
