The Authdog MCP Kotlin SDK verifies a bearer token presented to your MCP 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/` of 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:
cd mcp-sdk/kotlin
./gradlew publishToMavenLocalThen add mavenLocal() to your repositories and depend on the artifact:
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:
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:
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:
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.
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: all language token verifiers
- MCP authentication: how Authdog authenticates MCP clients