MCP Service Account Authentication
March 12, 2026 ยท View on GitHub
Service account authentication enables headless, automated access to MCP servers using OAuth2 client credentials flow. This authentication method is designed for scenarios where interactive user authentication is not possible or desirable, such as CI/CD pipelines, backend services, and automated workflows.
When to Use Service Account Authentication
Service account authentication is ideal for:
- CI/CD Pipelines: Automated testing and deployment workflows that need to access MCP servers
- Backend Services: Server-to-server communication without user interaction
- Batch Processing: Scheduled jobs that process data from MCP servers
- Automated Workflows: Any scenario where a browser-based OAuth2 flow is not feasible
- Container Deployments: Containerized applications that need consistent, non-interactive authentication
Use interactive OAuth2 authentication (mcp_oauth2) instead when:
- Users need to authorize access to their personal data
- User-specific permissions are required
- The workflow is user-facing and can present a browser for authentication
Supported Capabilities
The mcp_service_account authentication provider implements:
- OAuth2 Client Credentials Flow: Standard RFC 6749 Section 4.4 client credentials grant
- Token Caching: Automatic token caching with configurable refresh buffer to minimize token endpoint requests
- Multi-Header Authentication: Ability to inject multiple authentication headers for services using backend system delegation patterns
Authentication Token Types
Service account authentication uses two types of tokens depending on the MCP server requirements:
-
OAuth2 Service Account Token: Obtained via OAuth2 client credentials flow. Always required for authentication and authorization.
-
Service Token: Optional service-specific token (such as Jira service token or GitLab service token) required by some MCP servers for delegating access to backend systems.
Configuring Service Account Auth Provider
The mcp_service_account provider is a built-in authentication provider in the NVIDIA NeMo Agent Toolkit. Configure it in your workflow YAML file:
authentication:
my_service_account:
_type: mcp_service_account
client_id: ${SERVICE_ACCOUNT_CLIENT_ID}
client_secret: ${SERVICE_ACCOUNT_CLIENT_SECRET}
token_url: https://auth.example.com/service_account/token
scopes:
- service-account-scope-1
- service-account-scope-2
To view all configuration options for the mcp_service_account authentication provider, run the following command:
nat info components -t auth_provider -q mcp_service_account
Required Configuration Fields
The following fields must be provided in your configuration:
| Field | Description | Example |
|---|---|---|
client_id | OAuth2 client identifier for your service account | my-service-client |
client_secret | OAuth2 client secret (keep secure, never commit to version control) | ${SERVICE_ACCOUNT_CLIENT_SECRET} |
token_url | OAuth2 token endpoint URL | https://auth.example.com/oauth/token |
scopes | List of OAuth2 scopes required for access (can also be space-delimited string) | [api.read, api.write] |
Optional Configuration Fields
Customize the authentication behavior with these optional fields:
| Field | Default | Description |
|---|---|---|
service_token | None | Nested configuration for service-specific token in dual authentication patterns. Contains: token (static token value), function (Python function path for dynamic token), and header (HTTP header name, default: X-Service-Account-Token). Either token or function must be provided, not both. |
token_cache_buffer_seconds | 300 | Seconds before token expiry to refresh the token (default: 5 minutes) |
Environment Variables
Service account credentials are typically provided through environment variables to avoid committing secrets to version control. Reference them in your configuration using the ${VARIABLE_NAME} syntax.
For scopes, you can use either a list (recommended for clarity) or a space-delimited string from an environment variable:
List format (recommended):
authentication:
my_service_account:
_type: mcp_service_account
client_id: ${SERVICE_ACCOUNT_CLIENT_ID}
client_secret: ${SERVICE_ACCOUNT_CLIENT_SECRET}
token_url: ${SERVICE_ACCOUNT_TOKEN_URL}
scopes:
- service-account-scope-1
- service-account-scope-2
String format (for environment variables):
authentication:
my_service_account:
_type: mcp_service_account
client_id: ${SERVICE_ACCOUNT_CLIENT_ID}
client_secret: ${SERVICE_ACCOUNT_CLIENT_SECRET}
token_url: ${SERVICE_ACCOUNT_TOKEN_URL}
scopes: ${SERVICE_ACCOUNT_SCOPES} # Space-delimited string
Set the environment variables in your shell:
export SERVICE_ACCOUNT_CLIENT_ID="your-client-id"
export SERVICE_ACCOUNT_CLIENT_SECRET="your-client-secret"
export SERVICE_ACCOUNT_TOKEN_URL="https://auth.example.com/oauth/token"
export SERVICE_ACCOUNT_SCOPES="service-account-scope-1 service-account-scope-2" # For string format
:::{warning} Security Best Practice: Never commit credentials to version control. Always use environment variables or a secure secret management system for storing service account credentials. :::
Referencing Auth Providers in Clients
Reference the service account authentication provider in your MCP client configuration using the auth_provider parameter:
function_groups:
mcp_tools:
_type: mcp_client
server:
transport: streamable-http
url: https://api.example.com/mcp
auth_provider: my_service_account
authentication:
my_service_account:
_type: mcp_service_account
client_id: ${SERVICE_ACCOUNT_CLIENT_ID}
client_secret: ${SERVICE_ACCOUNT_CLIENT_SECRET}
token_url: ${SERVICE_ACCOUNT_TOKEN_URL}
scopes:
- api.read
- api.write
Authentication Patterns
The service account provider supports two authentication patterns depending on MCP server requirements.
Single Authentication Pattern
Use for custom MCP servers that only require OAuth2 service account token validation. This pattern uses one authentication header.
sequenceDiagram
participant Client
participant Token Endpoint
participant MCP Server
Client->>Token Endpoint: POST /oauth/token<br/>(client_id, client_secret, scopes)
Token Endpoint-->>Client: access_token
Client->>MCP Server: Request<br/>Authorization: Bearer <access_token>
MCP Server-->>Client: Response
authentication:
custom_auth:
_type: mcp_service_account
client_id: ${CLIENT_ID}
client_secret: ${CLIENT_SECRET}
token_url: https://auth.example.com/oauth/token
scopes:
- service.scope
Produces: Authorization: Bearer <access_token>
Dual Authentication Pattern
Use for enterprise MCP servers (such as Jira, GitLab) that require both OAuth2 service account token and service token for backend system delegation. This pattern uses two authentication headers.
sequenceDiagram
participant Client
participant Token Endpoint
participant MCP Server
Client->>Token Endpoint: POST /oauth/token<br/>(client_id, client_secret, scopes)
Token Endpoint-->>Client: access_token
Client->>MCP Server: Request<br/>Authorization: Bearer <access_token><br/>X-Service-Account-Token: <service_token>
MCP Server-->>Client: Response
Option 1: Static Service Token
authentication:
dual_auth:
_type: mcp_service_account
client_id: ${CLIENT_ID}
client_secret: ${CLIENT_SECRET}
token_url: ${TOKEN_URL}
scopes:
- service.scope
service_token:
token: ${SERVICE_TOKEN} # Static token from environment
header: X-Service-Account-Token
Option 2: Dynamic Service Token (Advanced)
authentication:
dual_auth:
_type: mcp_service_account
client_id: ${CLIENT_ID}
client_secret: ${CLIENT_SECRET}
token_url: ${TOKEN_URL}
scopes:
- service.scope
service_token:
function: "my_module.get_service_token" # Python function path
header: X-Service-Account-Token
kwargs: # Optional: Pass additional arguments to the function
vault_path: "secrets_jira"
region: "us-west-2"
Both produce:
Authorization: Bearer <access_token>
X-Service-Account-Token: <service_token>
:::{tip} Dynamic Function Approach
The dynamic function approach is useful for enterprise environments where service tokens need to be fetched from secure token vaults or have complex retrieval logic. The function will be called on every request.
Function signature: async def get_service_token(**kwargs) -> str | tuple[str, str]
The function can:
- Return
strfor the token (usesheaderfrom config) - Return
tuple[str, str]for(header_name, token)to override the header - Access runtime context via
Context.get()if needed - Receive additional arguments via the
kwargsfield in configuration
Example:
async def get_service_token(vault_path: str, **kwargs) -> str:
from nat.builder.context import Context
context = Context.get()
# Fetch token from vault, use context metadata if needed
return fetch_from_vault(vault_path)
:::
Security Considerations
Credential Management
- Never commit credentials: Store credentials in environment variables or secure secret management systems
- Rotate credentials regularly: Implement a credential rotation policy
- Use minimal scopes: Request only the OAuth2 scopes required for your use case
- Monitor token usage: Track token requests and usage patterns for anomalies
Token Security
- In-memory only: Tokens are cached in memory and not persisted to disk
- Automatic refresh: Tokens are refreshed before expiration to minimize exposure window
- HTTPS required: Always use HTTPS for token endpoint communications
- Protected logs: Failed requests do not expose credentials in log messages
Token Caching
The service account provider implements intelligent token caching to minimize requests to the OAuth2 token endpoint:
- First Request: Client credentials are sent to the token endpoint, and the access token is cached
- Subsequent Requests: Cached token is reused until it nears expiration
- Automatic Refresh: Token is refreshed automatically when it reaches the buffer threshold (default: 5 minutes before expiration)
- Concurrent Requests: Thread-safe operations prevent duplicate token requests during concurrent workflow execution
You can adjust the refresh buffer with the token_cache_buffer_seconds configuration option:
authentication:
my_service_account:
_type: mcp_service_account
# ... other configuration ...
token_cache_buffer_seconds: 600 # Refresh 10 minutes before expiry
Example Workflows
The Service Account Authentication Example demonstrates both authentication patterns:
- Single Authentication:
examples/MCP/service_account_auth_mcp/configs/config-mcp-service-account-jama.yml - Dual Authentication:
examples/MCP/service_account_auth_mcp/configs/config-mcp-service-account-jira.yml - Dual Authentication with Dynamic Service Token:
examples/MCP/service_account_auth_mcp/configs/config-mcp-service-account-jira-function.yml
See examples/MCP/service_account_auth_mcp/README.md for complete instructions on running the example workflows.
Troubleshooting
Error: "client_id is required"
Ensure the client_id field is set in your configuration or the corresponding environment variable is defined.
Error: "Invalid service account credentials"
Verify your client ID and client secret are correct, the token endpoint URL is reachable, and your service account has necessary permissions.
Error: "Service account rate limit exceeded"
Wait before retrying.
Error: "SSL: CERTIFICATE_VERIFY_FAILED"
The MCP server may use certificates from an internal Certificate Authority. Install your organization's CA certificates in your system's trust store.
Authentication works locally but fails in CI/CD
Verify all environment variables are set in your CI/CD platform.
See Also
- MCP Authentication - OAuth2 interactive authentication for user-facing workflows
- MCP Client - Connecting to MCP servers
- MCP Service Account Authentication Example:
examples/MCP/service_account_auth_mcp/README.md