MCP ServerAuthentication

MCP authentication

Authenticate with the Nomadfiling MCP server using API keys or OAuth 2.1 with PKCE — token formats, lifetimes, and the full authorization flow.

POST /tools/call HTTP/1.1
Host: mcp.nomadfiling.com
Content-Type: application/json
Authorization: Bearer nf_invalid_token_value

{
  "jsonrpc": "2.0",
  "id": "req_unauth_01",
  "method": "tools/call",
  "params": {
    "name": "get_filing_status",
    "arguments": {
      "filingId": "fil_9m2x8q1p"
    }
  }
}
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="mcp", resource_metadata="https://mcp.nomadfiling.com/.well-known/oauth-protected-resource"
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32001,
    "message": "Unauthorized"
  },
  "id": "req_unauth_01"
}
POST /register HTTP/1.1
Host: mcp.nomadfiling.com
Content-Type: application/json

{
  "redirect_uris": [
    "https://studio.acme.dev/oauth/callback"
  ],
  "client_name": "Acme MCP Studio",
  "grant_types": [
    "authorization_code",
    "refresh_token"
  ],
  "token_endpoint_auth_method": "none"
}
{
  "client_id": "mcp_client_example_1234567890abcdef",
  "client_id_issued_at": 1734567890,
  "redirect_uris": [
    "https://studio.acme.dev/oauth/callback"
  ],
  "grant_types": [
    "authorization_code",
    "refresh_token"
  ],
  "response_types": [
    "code"
  ],
  "token_endpoint_auth_method": "none",
  "client_name": "Acme MCP Studio"
}
POST /token HTTP/1.1
Host: mcp.nomadfiling.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=mcp_code_example_1234567890abcdef&redirect_uri=https%3A%2F%2Fstudio.acme.dev%2Foauth%2Fcallback&client_id=mcp_client_example_1234567890abcdef&code_verifier=example_code_verifier_value
{
  "access_token": "mcp_at_example_7jd3p4n8q2r6s1t5v9w0x3y6",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "mcp_rt_example_1234567890abcdef",
  "scope": "mcp"
}
POST /token HTTP/1.1
Host: mcp.nomadfiling.com
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=mcp_rt_example_1234567890abcdef
{
  "access_token": "mcp_at_example_rotated_7jd3p4n8q2",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "mcp_rt_example_rotated_abcdef1234567890",
  "scope": "mcp"
}

Choose an authentication method

Authenticate MCP requests with either a NomadFiling API key or an OAuth 2.1 access token. Use API keys for server-to-server access you control directly, and use OAuth when you are building a client that signs users in and needs delegated access.

initialize, tools/list, and ping do not require authentication. tools/call requires a valid bearer credential and returns 401 Unauthorized when authentication fails.

Use an API key when your MCP client runs in a trusted environment and you do not need an end-user consent flow.

Header format

Authorization: Bearer nf_example_9xmk7q2r4b7m8n2p6t1v5c8a

Server verification

  • Checks that the token starts with nf_
  • Computes the SHA-256 hash of the raw token
  • Compares that hash to api_keys.key_hash
  • Verifies the row exists in api_keys
  • Verifies expires_at is either null or in the future
  • Updates api_keys.last_used_at asynchronously
  • Returns user_id on success, or null on failure

Send authenticated requests

Pass the credential in the Authorization header as a bearer token. The MCP server accepts the same header pattern for both authentication methods.

curl https://mcp.nomadfiling.com/tools/call \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer nf_example_9xmk7q2r4b7m8n2p6t1v5c8a" \
  -d '{
    "jsonrpc": "2.0",
    "id": "req_7aK2mP",
    "method": "tools/call",
    "params": {
      "name": "get_filing_status",
      "arguments": {
        "filingId": "fil_9m2x8q1p"
      }
    }
  }'

If authentication succeeds, the request continues to the selected tool with the authenticated user_id in context. If authentication fails and the method is tools/call, the server returns an authentication challenge.

Use a NomadFiling API key

API keys are the simplest option for backend services, scripts, and private automations. Each key is created once in the NomadFiling app and only its hash is stored on the server.

The plaintext API key is shown once when you create it. Store it in your secret manager before you leave the creation screen.

Create an API key

Generate API keys from the NomadFiling app at /account/api-keys. Enter a name for the key, generate it, then copy the plaintext value before closing the dialog.

A valid API key has the format nf_ followed by 64 hexadecimal characters. The full string is 67 characters long, including the prefix.

nf_example_9xmk7q2r4b7m8n2p6t1v5c8a

API key format and validation

header
Authorizationstring
Required

Bearer token in the form Bearer nf_.... The server authenticates only the raw token value after the Bearer prefix.

raw token lengthstring

32 random bytes encoded as 64 hexadecimal characters after the prefix.

stored valuestring

SHA-256 hash of the full raw token, stored in api_keys.key_hash.

expiry checktimestamp or null

Authentication succeeds only when expires_at is null or later than the current time.

usage trackingtimestamp

The server updates api_keys.last_used_at asynchronously after successful authentication.

API key authentication result

user_idstring
Required

User identifier associated with the matching api_keys row.

When validation fails, the API key authenticator returns null. For tools/call, the request then fails with the 401 Unauthorized response shown earlier.

Use OAuth 2.1 with PKCE

OAuth 2.1 adds client registration, user consent, short-lived access tokens, and rotating refresh tokens. NomadFiling supports the authorization code grant and refresh token grant.

Use PKCE for every public client. The server supports both S256 and plain, but S256 is the safer choice.

OAuth endpoints

EndpointMethodPurpose
/registerPOSTRegister an OAuth client
/authorizeGETStart the authorization flow
/approvePOSTCreate an authorization code after user approval
/tokenPOSTExchange an authorization code or refresh token

Complete OAuth flow

Register your client

Send a request to /register with at least one redirect URI. The server creates a client ID with the mcp_client_ prefix and, if needed, a client secret with the mcp_secret_ prefix.

Your client is ready when you receive a 201 response containing client_id. Save that value because you need it for every later step.

Redirect the user to authorize

Send the user to /authorize with the client ID, redirect URI, response type, and PKCE challenge. The server validates the client and then issues a 302 redirect to https://app.nomadfiling.com/oauth/authorize, where the user sees the consent screen.

https://mcp.nomadfiling.com/authorize?client_id=mcp_client_example_1234567890abcdef&redirect_uri=https%3A%2F%2Fstudio.acme.dev%2Foauth%2Fcallback&response_type=code&state=example_state_value&code_challenge=example_code_challenge_value&code_challenge_method=S256&scope=mcp

After approval, the app posts to /approve, creates an authorization code with the mcp_code_ prefix, and redirects the user back to your redirect_uri with code and state query parameters.

Exchange the authorization code for tokens

Call /token with grant_type=authorization_code, the authorization code, the same redirect URI, your client ID, and the PKCE verifier when the flow used PKCE. The server validates the code, marks it as used, and returns an access token plus a refresh token.

Your client is authenticated when you can send the returned access_token as Authorization: Bearer mcp_at_... and the MCP server accepts a tools/call request.

Refresh expired access tokens

Call /token again with grant_type=refresh_token and the current refresh token. The server revokes the old token row and returns a new access token and refresh token pair.

Store the new refresh token immediately. Token rotation is enforced, so the previous refresh token no longer remains valid after a successful refresh.

Dynamic client registration reference

Use /register to create a client before starting the OAuth flow.

Request body

body
redirect_urisstring[]
Required

List of allowed redirect URIs for the client. The server rejects the request with invalid_redirect_uri when a redirect URI is missing or invalid.

body
client_namestring

Human-readable client name stored with the client record.

body
grant_typesstring[]

Grant types for the client. Defaults to authorization_code and refresh_token.

body
token_endpoint_auth_methodstring

Client authentication method for the token endpoint. Defaults to none. When set to client_secret_post, the server generates a client secret and stores only its SHA-256 hash.

Allowed values:noneclient_secret_post

Response fields

client_idstring
Required

Client identifier with the mcp_client_ prefix.

client_secretstring

Client secret with the mcp_secret_ prefix. Returned only when token_endpoint_auth_method is not none.

client_id_issued_atinteger
Required

Unix timestamp for when the client ID was issued.

redirect_urisstring[]
Required

Registered redirect URIs for the client.

grant_typesstring[]
Required

Grant types assigned to the client.

response_typesstring[]
Required

Always returns code.

token_endpoint_auth_methodstring
Required

Configured token endpoint authentication method.

client_namestring

Human-readable client name if provided at registration time.

Authorization endpoint reference

Use /authorize to validate the client request and forward the user into the NomadFiling consent flow.

Query parameters

query
client_idstring
Required

Registered OAuth client ID.

query
redirect_uristring
Required

Redirect URI that exactly matches one of the client's registered redirect URIs.

query
response_typestring
Required

Must be code.

Allowed values:code
query
statestring

Opaque value returned to your redirect URI unchanged. Use it to prevent CSRF and correlate browser state.

query
code_challengestring

PKCE code challenge. When present, the token exchange requires a matching code_verifier.

query
code_challenge_methodstring

PKCE verification method. Defaults to plain when omitted.

Allowed values:S256plain
query
scopestring

Requested scope. Defaults to mcp.

Behavior

If the request is valid, the server returns 302 Found and redirects the browser to https://app.nomadfiling.com/oauth/authorize with the request parameters forwarded. After the user approves access, the app calls /approve, generates an authorization code, and redirects the browser to your redirect_uri with code and state.

Token endpoint reference

Use /token for both authorization code exchange and refresh token rotation.

Authorization code grant

grant_typestring
Required

Must be authorization_code.

Allowed values:authorization_code
codestring
Required

Authorization code with the mcp_code_ prefix.

redirect_uristring
Required

Must match the redirect URI used during authorization and stored with the authorization code.

client_idstring
Required

OAuth client ID that requested the code.

code_verifierstring

Required when the authorization request included code_challenge. For S256, the server hashes the verifier with SHA-256 and compares the base64url output to code_challenge. For plain, the verifier must exactly equal code_challenge.

Refresh token grant

grant_typestring
Required

Must be refresh_token.

Allowed values:refresh_token
refresh_tokenstring
Required

Refresh token with the mcp_rt_ prefix.

Token response fields

access_tokenstring
Required

Bearer access token with the mcp_at_ prefix.

token_typestring
Required

Always Bearer.

expires_ininteger
Required

Access token lifetime in seconds. The current value is 3600.

refresh_tokenstring
Required

Refresh token with the mcp_rt_ prefix.

scopestring
Required

Granted scope. The current value is mcp.

Token endpoint errors

HTTP statusErrorMeaning
400invalid_grantAuthorization code or refresh token is missing, invalid, expired, revoked, already used, or does not match the client request
400unsupported_grant_typegrant_type is not supported
400invalid_requestRequest is malformed or missing required parameters
500server_errorServer failed while processing the request

Token formats and lifetimes

Each credential type has a fixed prefix and storage pattern. The server hashes secrets before lookup except for client IDs and authorization codes.

Token typePrefixRaw sizeLifetimeStorage
API keynf_32 bytes, hex encodedConfigurableSHA-256 in api_keys.key_hash
OAuth client IDmcp_client_16 bytes, base64url encodedPermanentPlaintext in oauth_clients
OAuth client secretmcp_secret_32 bytes, base64url encodedPermanentSHA-256 in oauth_clients.client_secret_hash
Authorization codemcp_code_24 bytes, base64url encoded5 minutesPlaintext in oauth_auth_codes
Access tokenmcp_at_32 bytes, base64url encoded1 hourSHA-256 in oauth_tokens.access_token_hash
Refresh tokenmcp_rt_32 bytes, base64url encoded30 daysSHA-256 in oauth_tokens.refresh_token_hash

All tokens are generated with crypto.getRandomValues and base64url encoding, except API keys, which are shown as hexadecimal after the nf_ prefix. Refresh token rotation is enforced, so each successful refresh revokes the previous token row and issues a new token pair.

Security notes

Do not log raw bearer tokens, authorization codes, or client secrets. Log token prefixes, request IDs, and user IDs instead.

When you build a public client, treat client_id as public metadata and treat code_verifier, access tokens, and refresh tokens as secrets. Store refresh tokens in encrypted storage and rotate them immediately after refresh.

FAQ

tools/call requires authentication. initialize, tools/list, and ping are allowed without authentication.