Package `authdogmcp` verifies the bearer token an MCP client presents to your Go MCP server. `Verifier.VerifyToken` calls `GET {BaseURL}/v1/userinfo` with the token and returns an `*AuthInfo`. Source is [`go/`](https://github.com/authdog/mcp-sdk/tree/main/go) in [authdog/mcp-sdk](https://github.com/authdog/mcp-sdk). It needs Go 1.21+ and uses only the standard library.

This is the MCP token verifier. For the management and userinfo client, see the [Go SDK](/docs/sdks/go).

## Availability

This module is source-available in [github.com/authdog/mcp-sdk](https://github.com/authdog/mcp-sdk) and may not resolve from the Go module proxy yet. The repository has no release tags, so `go get` cannot select a version. Use a `replace` directive against a local checkout until a release is published.

## Install

The module path is `github.com/authdog/mcp-sdk/go` (the module lives in the `go/` subdirectory). From source:

```bash
git clone https://github.com/authdog/mcp-sdk.git
```

In your `go.mod`:

```
require github.com/authdog/mcp-sdk/go v0.0.0

replace github.com/authdog/mcp-sdk/go => ../mcp-sdk/go
```

Once a release is published, the README gives:

```bash
go get github.com/authdog/mcp-sdk/go
```

```go
import authdogmcp "github.com/authdog/mcp-sdk/go"
```

## Configure

```go
verifier := authdogmcp.NewVerifier(authdogmcp.VerifierConfig{
    BaseURL: "https://api.authdog.com",
    Timeout: 10 * time.Second,
})
```

| Field | Type | Default | Behavior |
| --- | --- | --- | --- |
| `BaseURL` | `string` | none | Authdog API base URL. Used as given: a trailing slash is not stripped and the value is not validated. |
| `Timeout` | `time.Duration` | `10s` | Per-request timeout on the internal `http.Client`. Zero means 10 seconds. |

`NewVerifier` returns `*Verifier`. Each request sends `Content-Type: application/json`, `User-Agent: authdog-mcp-go-sdk/0.1.0`, and `Authorization: Bearer <token>`. You cannot inject your own `http.Client`.

## Verify a token

```go
info, err := verifier.VerifyToken(ctx, "the-bearer-access-token")
if err != nil {
    if authdogmcp.IsAuthenticationError(err) {
        log.Printf("token rejected: %v", err)
    } else if authdogmcp.IsAPIError(err) {
        log.Printf("verification failed: %v", err)
    } else {
        log.Printf("unexpected error: %v", err)
    }
    return
}

fmt.Printf("subject:  %s\n", info.Subject)
fmt.Printf("username: %s\n", info.Username)
fmt.Printf("email:    %s\n", info.Email)
if info.ExpiresAt != nil {
    fmt.Printf("expires:  %s\n", info.ExpiresAt.Format(time.RFC3339))
}
```

`VerifyToken(ctx context.Context, accessToken string) (*AuthInfo, error)` uses `ctx` for cancellation. It does not pre-check for an empty token.

| `AuthInfo` field | Type | Value |
| --- | --- | --- |
| `Token` | `string` | The token you passed in |
| `Subject` | `string` | `User.ID` |
| `Username` | `string` | `User.UserName` |
| `Email` | `string` | First `User.Emails` value, or `""` |
| `Scopes` | `[]string` | Never populated (nil/empty) |
| `ExpiresAt` | `*time.Time` | Now plus `Session.RemainingSeconds` when that is greater than 0, else `nil` |
| `User` | `User` | Full user record |

`Scopes` is empty because the `userinfo` endpoint does not return scopes; the SDK never sets it. Do not use it for scope-based authorization. Authentication identifies the caller; apply [authorization](/docs/concepts/authorization) separately.

## Wire into an MCP server

Streamable HTTP MCP servers receive the token in the `Authorization: Bearer ...` header. Verify once in middleware and put the `*AuthInfo` on the request context:

```go
package main

import (
    "context"
    "net/http"
    "strings"

    authdogmcp "github.com/authdog/mcp-sdk/go"
)

type ctxKey struct{}

// AuthInfoFromContext returns the verified identity attached by the middleware.
func AuthInfoFromContext(ctx context.Context) (*authdogmcp.AuthInfo, bool) {
    info, ok := ctx.Value(ctxKey{}).(*authdogmcp.AuthInfo)
    return info, ok
}

// RequireAuth verifies the bearer token on each request and attaches the
// resolved AuthInfo to the context before invoking the MCP handler.
func RequireAuth(verifier *authdogmcp.Verifier, next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
        if token == "" {
            w.Header().Set("WWW-Authenticate", "Bearer")
            http.Error(w, "missing bearer token", http.StatusUnauthorized)
            return
        }

        info, err := verifier.VerifyToken(r.Context(), token)
        if err != nil {
            if authdogmcp.IsAuthenticationError(err) {
                w.Header().Set("WWW-Authenticate", "Bearer")
                http.Error(w, "invalid token", http.StatusUnauthorized)
                return
            }
            http.Error(w, "auth backend error", http.StatusBadGateway)
            return
        }

        // Attach the verified identity; handlers read it via AuthInfoFromContext.
        ctx := context.WithValue(r.Context(), ctxKey{}, info)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

func main() {
    verifier := authdogmcp.NewVerifier(authdogmcp.VerifierConfig{
        BaseURL: "https://api.authdog.com",
    })

    // mcpHandler is your MCP server's Streamable HTTP handler.
    var mcpHandler http.Handler = http.NewServeMux()

    http.Handle("/mcp", RequireAuth(verifier, mcpHandler))
    _ = http.ListenAndServe(":8080", nil)
}
```

## Errors

Each error type is a struct with a `Message` field, returned as a pointer.

| Type | Check | When |
| --- | --- | --- |
| `*AuthenticationError` | `IsAuthenticationError` | HTTP 401 (`Unauthorized - invalid or expired token`) |
| `*APIError` | `IsAPIError` | Other non-2xx (`HTTP error <status>: <body>`), request build, transport, body-read, or JSON decode failure |
| `*AuthdogError` | `IsAuthdogError` | Base type; `VerifyToken` itself never returns it |

The `Is*` helpers use a direct type assertion. They do not unwrap, and `IsAuthdogError` does not match `*AuthenticationError` or `*APIError`. Check the specific types first.

## Related

- [MCP SDK](/docs/mcp-sdk): language overview
- [MCP authentication](/docs/mcp-authentication): how MCP clients obtain tokens
- [Node.js](/docs/mcp-sdk/node) and [Python](/docs/mcp-sdk/python) verifiers
- [Go SDK](/docs/sdks/go): management and userinfo client
