The MCP server uses a separate OAuth 2.0 token endpoint at https://mcp.dynamosql.com/token. This is distinct from the REST API token endpoint at https://api.dynamosql.com/v1/auth/token, but both accept the same API client credentials.
Use the same API client credentials, but not the same bearer token. The REST API accessToken from https://api.dynamosql.com/v1/auth/token is not accepted on POST /mcp. The MCP server only accepts bearer tokens minted by https://mcp.dynamosql.com/token.
OAuth 2.0 discovery
MCP clients that support OAuth discovery can find the token endpoint automatically:
| Endpoint | URL |
|---|
| Protected resource metadata | https://mcp.dynamosql.com/.well-known/oauth-protected-resource |
| Authorization server metadata | https://mcp.dynamosql.com/.well-known/oauth-authorization-server |
The protected resource metadata response:
{
"resource": "https://mcp.dynamosql.com/mcp",
"authorization_servers": ["https://mcp.dynamosql.com"],
"bearer_methods_supported": ["header"],
"scopes_supported": ["query", "schemas:read"]
}
The authorization server metadata response:
{
"issuer": "https://mcp.dynamosql.com",
"token_endpoint": "https://mcp.dynamosql.com/token",
"grant_types_supported": ["client_credentials"],
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post"
],
"scopes_supported": ["query", "schemas:read"]
}
Obtaining a token
Exchange your API client credentials for a bearer token using the client_credentials grant type.
Using HTTP Basic authentication (recommended)
curl -X POST https://mcp.dynamosql.com/token \
-u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
-d "grant_type=client_credentials&scope=query%20schemas:read"
Using form body credentials
curl -X POST https://mcp.dynamosql.com/token \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "scope=query%20schemas:read"
Token response
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 600,
"scope": "query schemas:read"
}
| Field | Description |
|---|
access_token | Bearer token for MCP requests |
token_type | Always Bearer |
expires_in | Token lifetime in seconds (600 = 10 minutes) |
scope | Granted scopes (space-delimited) |
Scopes
The scope parameter is optional. If omitted, the token is granted all MCP-supported scopes that the API client is allowed. If provided, it must be a subset of the client’s allowed scopes.
| Scope | Enables |
|---|
query | run_sql tool |
schemas:read | list_tables and describe_table tools |
The MCP surface only recognizes the MCP-supported scopes query and schemas:read. If you omit the scope parameter, the issued token includes all MCP-supported scopes configured on the API client. Other API-client scopes are ignored for MCP token issuance.
Using the bearer token
Include the token in the Authorization header on POST /mcp requests:
Authorization: Bearer YOUR_ACCESS_TOKEN
When the token is missing or invalid, the server returns 401 with a WWW-Authenticate challenge header pointing to the protected resource metadata URL.
Token errors
| HTTP status | error code | Description |
|---|
| 400 | unsupported_grant_type | Only client_credentials is supported |
| 400 | invalid_scope | Requested scope exceeds client grants |
| 401 | invalid_client | Wrong credentials or inactive API client |
| 429 | temporarily_unavailable | Authentication service rate limited |
| 500 | server_error | Internal failure |
Token lifecycle
- Tokens expire after 10 minutes (
expires_in: 600)
- There is no refresh token — request a new token when the current one expires
- MCP clients that support OAuth discovery handle token refresh automatically
- For static configurations, schedule token renewal before the 10-minute expiry