`AuthdogVerifier` from `@authdog/mcp-sdk` verifies the bearer token an MCP client presents to your Node.js MCP server. It calls `GET {baseUrl}/v1/userinfo` with the token and returns an `AuthInfo` (subject, username, email, expiry, full user). Source is [`node/`](https://github.com/authdog/mcp-sdk/tree/main/node) in [authdog/mcp-sdk](https://github.com/authdog/mcp-sdk). The package is ESM, uses `axios`, and declares `node >=16`.

This is the MCP token verifier, not the runtime package of the same name used inside the Authdog platform. See [MCP SDK](/docs/mcp-sdk) for how the language SDKs relate.

## Availability

These packages are source-available in [github.com/authdog/mcp-sdk](https://github.com/authdog/mcp-sdk) and may not resolve from registries yet. At the time of writing `@authdog/mcp-sdk` returns 404 on npm, and the repository has no release tags. The package version in `package.json` is `0.1.0`. Install from source until a release is published.

The name `@authdog/mcp-sdk` also belongs to a separate private runtime package inside the Authdog platform monorepo (trust store, AuthZEN, telemetry). That package is not this verifier. See [MCP SDK](/docs/mcp-sdk).

## Install

From source:

```bash
git clone https://github.com/authdog/mcp-sdk.git
cd mcp-sdk/node
npm install
npm run build
```

`npm run build` runs `tsc` and writes `dist/`, which is what `main` and `types` point at. Then add the built directory to your server project:

```bash
npm install /path/to/mcp-sdk/node
```

Once a release is published, the README gives these commands:

```bash
deno add npm:@authdog/mcp-sdk
npm install @authdog/mcp-sdk
```

## Configure

```ts
import { AuthdogVerifier } from "@authdog/mcp-sdk"

const verifier = new AuthdogVerifier({
  baseUrl: "https://api.authdog.com",
  timeout: 10000,
})
```

| Option | Type | Default | Behavior |
| --- | --- | --- | --- |
| `baseUrl` | `string` | required | Authdog API base URL. Trailing slashes are stripped. A missing or blank value throws `TypeError`. |
| `timeout` | `number` | `10000` | Request timeout in milliseconds. A falsy value (including `0`) falls back to `10000`. |

Each request sends `Content-Type: application/json` and `User-Agent: authdog-mcp-node-sdk/0.1.0`.

## Verify a token

```ts
import { AuthdogVerifier, AuthenticationError, APIError } from "@authdog/mcp-sdk"

const authInfo = await verifier.verifyToken("the-access-token")
console.log(authInfo.subject, authInfo.username, authInfo.email)
```

`verifyToken(accessToken: string, signal?: AbortSignal): Promise<AuthInfo>` sends `Authorization: Bearer <accessToken>`. Pass an `AbortSignal` to cancel the in-flight request, for example when the incoming MCP request is cancelled. An empty or whitespace-only token throws `AuthenticationError` before any request is made.

`AuthInfo`:

| Field | Type | Value |
| --- | --- | --- |
| `token` | `string` | The token you passed in |
| `subject` | `string` | `user.id` |
| `username` | `string` | `user.userName` |
| `email` | `string \| null` | First `user.emails[].value`, or `null` |
| `scopes` | `string[]` | Always `[]` |
| `expiresAt` | `Date \| null` | Now plus `session.remainingSeconds`, only when that value is a number greater than 0. Otherwise `null`. |
| `user` | `UserInfoResponse["user"]` | Full user object from the response |

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

Types `AuthInfo`, `AuthdogVerifierConfig`, and `UserInfoResponse` are exported alongside `AuthdogVerifier`.

## Wire into an MCP server

The MCP TypeScript SDK protects HTTP transports with `requireBearerAuth`, which delegates to a token verifier. Adapt `AuthdogVerifier` in a few lines:

```ts
import express from "express"
import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js"
import type { OAuthTokenVerifier } from "@modelcontextprotocol/sdk/server/auth/provider.js"
import type { AuthInfo as McpAuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js"
import { AuthdogVerifier } from "@authdog/mcp-sdk"

const authdog = new AuthdogVerifier({ baseUrl: "https://api.authdog.com" })

// Adapt the Authdog verifier to MCP's TokenVerifier interface
const verifier: OAuthTokenVerifier = {
  async verifyAccessToken(token: string): Promise<McpAuthInfo> {
    const info = await authdog.verifyToken(token)
    return {
      token: info.token,
      clientId: info.subject,
      scopes: info.scopes,
      expiresAt: info.expiresAt
        ? Math.floor(info.expiresAt.getTime() / 1000) // MCP expects epoch seconds
        : undefined,
      extra: { username: info.username, email: info.email },
    }
  },
}

const app = express()

// Protect your MCP HTTP endpoint with Authdog-backed bearer auth
app.use("/mcp", requireBearerAuth({ verifier }))
```

When the token is missing, invalid, or expired, `verifyToken` throws `AuthenticationError`, which `requireBearerAuth` surfaces to the client as HTTP 401.

## Errors

All three classes are exported and extend `Error`. `name` is set to the class name.

| Class | Thrown when |
| --- | --- |
| `AuthdogError` | Base class for the others |
| `AuthenticationError` | Empty or blank token, or HTTP 401 (`Unauthorized - invalid or expired token`) |
| `APIError` | Timeout (`Request timed out: ...`), other non-2xx (`HTTP error <status>: <body>`), network failure (`Request failed: ...`), or a 2xx response with no `user` (`Invalid response: missing user in userinfo payload`) |

```ts
try {
  await verifier.verifyToken(token)
} catch (error) {
  if (error instanceof AuthenticationError) {
    // reject with 401
  } else if (error instanceof APIError) {
    // upstream or transport failure
  } else {
    throw error
  }
}
```

## Related

- [MCP SDK](/docs/mcp-sdk): language overview and the name collision
- [MCP authentication](/docs/mcp-authentication): how MCP clients obtain tokens
- [Python](/docs/mcp-sdk/python) and [Go](/docs/mcp-sdk/go) verifiers
