Authdog

MCP SDK: C#

Last updated Oct 13, 2026
View as Markdown

The C# MCP SDK (Authdog.Mcp.Sdk) is a bearer-token verifier for Model Context Protocol servers. AuthdogVerifier takes the access token an MCP client presents, calls Authdog GET /v1/userinfo with it, and returns an AuthInfo identity you attach to the request context to authorize tool and resource calls. It targets .NET 8.0 or higher.

Availability

Authdog.Mcp.Sdk is source-available in github.com/authdog/mcp-sdk (csharp/). Publication on NuGet is unverified, so dotnet add package Authdog.Mcp.Sdk may not resolve yet. Until it does, reference the project from a local checkout.

Install

Clone the repository and add a project reference:

git clone https://github.com/authdog/mcp-sdk.git
dotnet add reference ../mcp-sdk/csharp/Authdog.Mcp.Sdk.csproj

The package is version 0.1.0, MIT licensed, and depends on Microsoft.Extensions.Http and Microsoft.Extensions.DependencyInjection 7.0.0. Once it is published on NuGet, dotnet add package Authdog.Mcp.Sdk is the install command.

Configure

AuthdogVerifierOptions:

Property Type Default Description
BaseUrl string empty string Base URL of the Authdog API, for example https://api.authdog.com. A trailing / is trimmed. Set it explicitly.
Timeout TimeSpan 10 seconds Per-request timeout.
using Authdog.Mcp.Sdk;

var verifier = new AuthdogVerifier(new AuthdogVerifierOptions
{
    BaseUrl = "https://api.authdog.com",
    // Timeout = TimeSpan.FromSeconds(10), // default
});

The constructor takes an optional second argument, an HttpClient, for testing or to share a pooled client. When you pass your own client, Timeout is not applied to it; configure the timeout on that client instead. Passing null options throws ArgumentNullException.

var verifier = new AuthdogVerifier(
    new AuthdogVerifierOptions { BaseUrl = "https://api.authdog.com" },
    httpClient);

Verify a token

VerifyTokenAsync(string accessToken) sends GET {BaseUrl}/v1/userinfo with Authorization: Bearer <accessToken> and User-Agent: authdog-mcp-csharp-sdk/0.1.0:

using Authdog.Mcp.Sdk.Exceptions;

try
{
    var authInfo = await verifier.VerifyTokenAsync("the-bearer-access-token");

    Console.WriteLine($"Subject:  {authInfo.Subject}");
    Console.WriteLine($"Username: {authInfo.Username}");
    Console.WriteLine($"Email:    {authInfo.Email}");
    Console.WriteLine($"Expires:  {authInfo.ExpiresAt}");
}
catch (AuthenticationException ex)
{
    // HTTP 401: invalid or expired token
}
catch (ApiException ex)
{
    // any other non-2xx response, or a transport failure
}

AuthInfo (namespace Authdog.Mcp.Sdk.Types):

Member Type Description
Token string The bearer access token that was verified.
Subject string User.Id
Username string User.UserName
Email string? First User.Emails value, or null if none.
Scopes IReadOnlyList<string> Always empty. Userinfo does not return scopes yet.
ExpiresAt DateTimeOffset? UTC now plus Session.RemainingSeconds. null when RemainingSeconds is not greater than 0.
User User The full user record from userinfo.

Wire into an MCP server

For an HTTP MCP server on ASP.NET Core, verify the Authorization: Bearer header in middleware ahead of your MCP (Streamable HTTP) endpoint and stash the identity for your handlers:

using Authdog.Mcp.Sdk;
using Authdog.Mcp.Sdk.Exceptions;
using Authdog.Mcp.Sdk.Types;

app.Use(async (context, next) =>
{
    var header = context.Request.Headers.Authorization.ToString();
    if (!header.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
    {
        context.Response.StatusCode = StatusCodes.Status401Unauthorized;
        return;
    }

    var token = header["Bearer ".Length..].Trim();

    try
    {
        AuthInfo authInfo = await verifier.VerifyTokenAsync(token);
        context.Items["AuthInfo"] = authInfo;
        await next();
    }
    catch (AuthenticationException)
    {
        context.Response.StatusCode = StatusCodes.Status401Unauthorized;
    }
    catch (ApiException)
    {
        context.Response.StatusCode = StatusCodes.Status502BadGateway;
    }
});

Tool and resource handlers then read context.Items["AuthInfo"] to authorize the call, for example by authInfo.Subject. The SDK does not ship its own middleware; this snippet is the pattern from the SDK README.

Errors

All exceptions live in Authdog.Mcp.Sdk.Exceptions and derive from AuthdogException, which derives from Exception.

Exception When Message
AuthenticationException HTTP 401 Unauthorized - invalid or expired token
ApiException Other non-2xx response HTTP error <status>: <body>
ApiException Network failure (HttpRequestException) Request failed: <cause>
ApiException Timeout (TaskCanceledException) Request timeout: <cause>
ApiException 2xx response whose body is not valid JSON Failed to parse response: <cause>

Catch AuthdogException to handle any SDK error at once.