This feature allows you to pass different GitLab API URLs via custom request headers, enabling the MCP server to connect to multiple GitLab instances simultaneously using a connection pool architecture.
When enabled, the server can handle requests to different GitLab instances (e.g., gitlab.com, self-hosted GitLab, etc.) within the same session by accepting a custom header that specifies the GitLab API URL for each request.
The server uses a connection pool (GitLabClientPool) to manage HTTP/HTTPS agents for multiple GitLab instances:
- Automatic Client Creation: When a new GitLab API URL is encountered, a new client configuration is created with appropriate proxy settings and SSL options
- Connection Reuse: Subsequent requests to the same API URL reuse the existing client configuration
- Automatic Cleanup: Idle connections are automatically cleaned up after a configurable timeout
- LRU Eviction: When the pool reaches maximum capacity, the least recently used client is evicted
In remote authorization mode, each session can have its own GitLab API URL:
- The API URL is extracted from the
X-GitLab-API-URLheader - It's stored alongside the authentication token in the session context
- All API calls within that session use the specified API URL
Add these environment variables to enable and configure the feature:
# Enable dynamic API URL support (required)
ENABLE_DYNAMIC_API_URL=true
# Remote authorization must be enabled for this feature
REMOTE_AUTHORIZATION=true
STREAMABLE_HTTP=true
# Connection pool configuration (optional)
GITLAB_POOL_MAX_SIZE=100 # Maximum number of GitLab instances in pool (default: 100)
GITLAB_POOL_IDLE_TIMEOUT=300000 # Idle timeout in milliseconds (default: 300000 = 5 minutes)
GITLAB_POOL_CLEANUP_INTERVAL=60000 # Cleanup interval in milliseconds (default: 60000 = 1 minute)
# Proxy settings (optional, applied to all pooled clients)
HTTP_PROXY=http://proxy.example.com:8080
HTTPS_PROXY=https://proxy.example.com:8443
NODE_TLS_REJECT_UNAUTHORIZED=0 # Set to 0 to disable SSL verification
GITLAB_CA_CERT_PATH=/path/to/ca.crt # Path to custom CA certificate- Remote Authorization Mode: This feature requires
REMOTE_AUTHORIZATION=trueandSTREAMABLE_HTTP=true - Authentication: Each request must include either
AuthorizationorPrivate-Tokenheader - Custom API URL: Include the
X-GitLab-API-URLheader with the GitLab instance URL
When making requests to the MCP server, include these headers:
POST /mcp HTTP/1.1
Host: localhost:3002
Content-Type: application/json
MCP-Session-ID: your-session-id
Authorization: Bearer glpat-xxxxxxxxxxxxxxxxxxxx
X-GitLab-API-URL: https://gitlab.example.com
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "get_project",
"arguments": {
"project_id": "my-group/my-project"
}
},
"id": 1
}| Header | Required | Description |
|---|---|---|
Authorization or Private-Token |
Yes | GitLab authentication token |
X-GitLab-API-URL |
Yes* | Full URL to GitLab API (e.g., https://gitlab.example.com) |
MCP-Session-ID |
Yes** | Session identifier for maintaining state |
* Required when ENABLE_DYNAMIC_API_URL=true
** Required for subsequent requests in the same session
The X-GitLab-API-URL header accepts various URL formats:
# Full API URL (recommended)
X-GitLab-API-URL: https://gitlab.example.com/api/v4
# Base URL (will be normalized to include /api/v4)
X-GitLab-API-URL: https://gitlab.example.com
# Self-hosted GitLab with custom path
X-GitLab-API-URL: https://git.company.com/gitlab/api/v4The server automatically normalizes URLs to ensure they end with /api/v4.
// First request to gitlab.com
const response1 = await fetch('http://localhost:3002/mcp', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer glpat-token-for-gitlab-com',
'X-GitLab-API-URL': 'https://gitlab.com'
},
body: JSON.stringify({
jsonrpc: '2.0',
method: 'tools/call',
params: {
name: 'list_projects',
arguments: {}
},
id: 1
})
});
const session1 = response1.headers.get('mcp-session-id');
// Second request to self-hosted GitLab (different session)
const response2 = await fetch('http://localhost:3002/mcp', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Private-Token': 'glpat-token-for-self-hosted',
'X-GitLab-API-URL': 'https://gitlab.company.com'
},
body: JSON.stringify({
jsonrpc: '2.0',
method: 'tools/call',
params: {
name: 'list_projects',
arguments: {}
},
id: 2
})
});
const session2 = response2.headers.get('mcp-session-id');
// Subsequent requests use the session ID
const response3 = await fetch('http://localhost:3002/mcp', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'MCP-Session-ID': session1,
'Authorization': 'Bearer glpat-token-for-gitlab-com'
},
body: JSON.stringify({
jsonrpc: '2.0',
method: 'tools/call',
params: {
name: 'get_project',
arguments: { project_id: 'my-project' }
},
id: 3
})
});# First request - creates session
curl -X POST http://localhost:3002/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer glpat-xxxxxxxxxxxxxxxxxxxx" \
-H "X-GitLab-API-URL: https://gitlab.example.com" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "list_projects",
"arguments": {}
},
"id": 1
}' \
-i # Include headers to see MCP-Session-ID
# Subsequent request - reuses session
curl -X POST http://localhost:3002/mcp \
-H "Content-Type: application/json" \
-H "MCP-Session-ID: <session-id-from-previous-response>" \
-H "Authorization: Bearer glpat-xxxxxxxxxxxxxxxxxxxx" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "get_project",
"arguments": {
"project_id": "my-group/my-project"
}
},
"id": 2
}'You can monitor the connection pool via the metrics endpoint:
curl http://localhost:3002/metricsResponse:
{
"activeSessions": 5,
"totalSessions": 127,
"expiredSessions": 12,
"authFailures": 3,
"requestsProcessed": 1543,
"rejectedByRateLimit": 2,
"rejectedByCapacity": 0,
"authenticatedSessions": 5,
"uptime": 3600.5,
"memoryUsage": {
"rss": 52428800,
"heapTotal": 20971520,
"heapUsed": 15728640,
"external": 1048576
},
"config": {
"maxSessions": 1000,
"maxRequestsPerMinute": 60,
"sessionTimeoutSeconds": 3600,
"remoteAuthEnabled": true
}
}- The server validates that the provided URL is a valid HTTP/HTTPS URL
- Invalid URLs are rejected and logged
- The original
GITLAB_API_URLfrom environment variables is used as fallback
- Each session maintains its own authentication token
- Tokens are validated for format and length
- Authentication tokens expire after
SESSION_TIMEOUT_SECONDSof inactivity
- Each session is subject to rate limiting (
MAX_REQUESTS_PER_MINUTE) - The connection pool has a maximum size (
GITLAB_POOL_MAX_SIZE) - Requests are rejected when limits are exceeded
- Each pooled client respects the global SSL settings
- Custom CA certificates can be configured via
GITLAB_CA_CERT_PATH - SSL verification can be disabled with
NODE_TLS_REJECT_UNAUTHORIZED=0(not recommended for production)
-
"Missing Authorization or Private-Token header"
- Ensure you're sending either
Authorization: Bearer <token>orPrivate-Token: <token>header - Verify the token format is correct (minimum 20 characters)
- Ensure you're sending either
-
"Invalid custom API URL provided"
- Check that the URL is properly formatted (e.g.,
https://gitlab.example.com) - Ensure the URL is accessible from the server
- Check that the URL is properly formatted (e.g.,
-
"Server capacity reached"
- The connection pool is full (
GITLAB_POOL_MAX_SIZEreached) - Wait for idle connections to be cleaned up or increase
GITLAB_POOL_MAX_SIZE
- The connection pool is full (
-
"Rate limit exceeded"
- You've exceeded
MAX_REQUESTS_PER_MINUTEfor your session - Wait for the rate limit window to reset (1 minute)
- You've exceeded
Enable debug logging to troubleshoot issues:
LOG_LEVEL=debug npm startThis will show:
- Custom API URL detection
- Connection pool operations
- Session creation and cleanup
- Authentication token handling
- Small deployments (1-10 GitLab instances):
GITLAB_POOL_MAX_SIZE=10-20 - Medium deployments (10-50 instances):
GITLAB_POOL_MAX_SIZE=50-100 - Large deployments (50+ instances):
GITLAB_POOL_MAX_SIZE=100-200
- Frequent access:
GITLAB_POOL_IDLE_TIMEOUT=600000(10 minutes) - Moderate access:
GITLAB_POOL_IDLE_TIMEOUT=300000(5 minutes, default) - Infrequent access:
GITLAB_POOL_IDLE_TIMEOUT=60000(1 minute)
Each pooled client consumes approximately:
- HTTP Agent: ~1-2 KB
- HTTPS Agent: ~2-4 KB
- Metadata: ~1 KB
Total per client: ~5-10 KB
For 100 clients: ~500 KB - 1 MB
-
Enable the feature:
ENABLE_DYNAMIC_API_URL=true REMOTE_AUTHORIZATION=true STREAMABLE_HTTP=true
-
Update client code to include
X-GitLab-API-URLheader -
Test with a single instance first to ensure compatibility
-
Gradually add more instances while monitoring the metrics endpoint
When ENABLE_DYNAMIC_API_URL=false (default):
- The server behaves exactly as before
- Only the
GITLAB_API_URLenvironment variable is used - No connection pooling overhead
- Custom API URL headers are ignored
Specifies the GitLab API URL for the current request.
Format: X-GitLab-API-URL: <url>
Example: X-GitLab-API-URL: https://gitlab.example.com
Validation:
- Must be a valid HTTP/HTTPS URL
- Automatically normalized to include
/api/v4suffix - Logged as warning if invalid
| Variable | Type | Default | Description |
|---|---|---|---|
ENABLE_DYNAMIC_API_URL |
boolean | false |
Enable dynamic API URL support |
GITLAB_POOL_MAX_SIZE |
number | 100 |
Maximum number of GitLab instances in pool |
GITLAB_POOL_IDLE_TIMEOUT |
number | 300000 |
Idle timeout in milliseconds |
GITLAB_POOL_CLEANUP_INTERVAL |
number | 60000 |
Cleanup interval in milliseconds |
This feature is part of the GitLab MCP Server and follows the same license terms.