Skip to main content

What Is User Management?

User Management allows enterprise applications to serve multiple users through a single MCP server instance. Instead of creating separate servers for each user, you can dynamically switch user context using the x-alloy-userid header. Think of it as a multi-tenant system where:
  • One server serves all your users
  • Each request can specify which user it’s for
  • Data and credentials remain isolated per user
  • Permissions are enforced at the user level

How User Management Works

When you make a request with the x-alloy-userid header, the system switches context to that specific user: The user management system operates at multiple levels:
  1. Authentication - Validates the enterprise API key has permission to override users
  2. Context Switching - Loads the specified user’s context, credentials, and permissions
  3. Data Isolation - Ensures each user only sees their own credentials and data
  4. Execution - Runs the request in the user’s context
  5. Response - Returns results specific to that user

Why Use User Management?

Multi-Tenant SaaS Applications

  • Single Integration: One MCP server for all your customers
  • Customer Isolation: Each customer’s data and credentials stay separate
  • Easy Scaling: Add users without creating new servers

Team Collaboration

  • Department Access: Sales uses CRM tools, Marketing uses email tools
  • Individual Credentials: Each team member has their own third-party logins
  • Centralized Control: Manage all users from one place

Enterprise Platforms

  • User Provisioning: Automatically set up new users
  • Compliance: Track actions per user for audit requirements
  • Cost Efficiency: One server infrastructure for unlimited users

Visualizing User Management

Without User Management - Multiple Servers:

With User Management - Single Server:

Quick Start

Basic User Override

curl -X POST "https://mcp.runalloy.com/mcp/YOUR_SERVER_ID/YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "x-alloy-userid: user_abc123" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "list_connectors_alloy",
      "arguments": {}
    },
    "id": "1"
  }'

How to Implement User Management

Step 1: Create an Enterprise MCP Server

Create a server with your enterprise API key:
curl -X POST https://mcp.runalloy.com/api/servers \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "enterprise-multi-user",
    "description": "Enterprise server for multiple users",
    "restrictions": {
      "permissions": [
        {
          "connector": "slack",
          "mode": "allow",
          "actions": ["*"]
        },
        {
          "connector": "notion",
          "mode": "allow",
          "actions": ["*"]
        }
      ]
    }
  }'

Step 2: Execute Requests for Different Users

For User Alice:

curl -X POST "YOUR_MCP_SERVER_URL" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "x-alloy-userid: user_alice_123" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "execute_action_alloy",
      "arguments": {
        "connectorId": "slack",
        "actionId": "chat_postMessage",
        "credentialId": "alice_slack_credential_id",
        "parameters": {
          "requestBody": {
            "channel": "C123456",
            "text": "Message from Alice"
          }
        }
      }
    },
    "id": "1"
  }'

For User Bob:

curl -X POST "YOUR_MCP_SERVER_URL" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "x-alloy-userid: user_bob_456" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "execute_action_alloy",
      "arguments": {
        "connectorId": "slack",
        "actionId": "chat_postMessage",
        "credentialId": "bob_slack_credential_id",
        "parameters": {
          "requestBody": {
            "channel": "C789012",
            "text": "Message from Bob"
          }
        }
      }
    },
    "id": "1"
  }'

Understanding the User Flow

Here’s how a typical user management request flows through the system: Each step ensures:
  1. Authentication - Only enterprise keys can override users
  2. Authorization - User must exist and have permissions
  3. Isolation - User gets only their data
  4. Execution - Actions use user’s specific credentials

Integration Examples

Node.js Client

class MCPClient {
  constructor(serverUrl, token) {
    this.serverUrl = serverUrl;
    this.token = token;
  }

  async executeForUser(userId, method, params) {
    const response = await fetch(`${this.serverUrl}/${this.token}`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json, text/event-stream',
        'x-alloy-userid': userId  // Dynamic user override
      },
      body: JSON.stringify({
        jsonrpc: '2.0',
        method: 'tools/call',
        params: {
          name: method,
          arguments: params
        },
        id: '1'
      })
    });

    return response.json();
  }
}

// Usage
const mcp = new MCPClient(
  'https://mcp.runalloy.com/mcp/enterprise-123',
  'your_token_here'
);

// Execute for different users
await mcp.executeForUser('user_alice_123', 'list_connectors_alloy', {});
await mcp.executeForUser('user_bob_456', 'list_connectors_alloy', {});

Python Client

import requests

class MCPClient:
    def __init__(self, server_url, token):
        self.server_url = server_url
        self.token = token

    def execute_for_user(self, user_id, method, params):
        url = f"{self.server_url}/{self.token}"

        headers = {
            'Content-Type': 'application/json',
            'Accept': 'application/json, text/event-stream',
            'x-alloy-userid': user_id  # Dynamic user override
        }

        payload = {
            'jsonrpc': '2.0',
            'method': 'tools/call',
            'params': {
                'name': method,
                'arguments': params
            },
            'id': '1'
        }

        response = requests.post(url, json=payload, headers=headers)
        return response.json()

# Usage
mcp = MCPClient(
    'https://mcp.runalloy.com/mcp/enterprise-123',
    'your_token_here'
)

# Execute for different users
mcp.execute_for_user('user_alice_123', 'list_connectors_alloy', {})
mcp.execute_for_user('user_bob_456', 'list_connectors_alloy', {})

Key Concepts

User Context

Each user has their own isolated context containing:
  • Credentials - OAuth tokens and API keys for third-party services
  • Permissions - What actions they can perform
  • Data - Their specific configurations and settings
  • History - Their action logs and usage

Enterprise Authentication

User management requires enterprise-level authentication:
  • Standard API Keys - Cannot override user context
  • Enterprise API Keys - Can specify any valid user ID
  • Security - Prevents unauthorized user impersonation

Common Use Cases

1. SaaS Platform Integration

Serve all your customers through one MCP server:
// When customer Alice uses your platform
await mcpClient.executeForUser(alice.userId, 'execute_action_alloy', {
  connectorId: 'slack',
  actionId: 'chat_postMessage',
  // ... other params
});

// When customer Bob uses your platform
await mcpClient.executeForUser(bob.userId, 'execute_action_alloy', {
  connectorId: 'notion',
  actionId: 'pages_create',
  // ... other params
});

2. Multi-Tenant Applications

Isolate data and credentials per tenant:
const tenantUserId = `tenant_${tenantId}_user_${userId}`;
await mcpClient.executeForUser(tenantUserId, method, params);

3. Team Collaboration Tools

Grant team members access to specific integrations:
// Sales team member
await mcpClient.executeForUser('team_sales_john', 'execute_action_alloy', {
  connectorId: 'hubspot',
  actionId: 'contacts_create'
});

// Marketing team member
await mcpClient.executeForUser('team_marketing_jane', 'execute_action_alloy', {
  connectorId: 'mailchimp',
  actionId: 'campaigns_create'
});

Security Features

  1. Enterprise-Only - Only enterprise API keys can override users
  2. Validation - User IDs are sanitized and validated
  3. Fallback - Invalid users fall back to default context
  4. Logging - All override attempts are logged
  5. Isolation - Users cannot access each other’s data

Best Practices

1. User ID Format

Use a consistent format for user IDs:
// Good patterns
`${companyId}_${userId}`
`${environment}_${tenantId}_${userId}`
`team_${department}_${username}`

2. Error Handling

Always handle cases where user override might fail:
try {
  const result = await mcpClient.executeForUser(userId, method, params);
  return result;
} catch (error) {
  // Fallback logic or error reporting
  console.error(`Failed for user ${userId}:`, error);
}

3. Rate Limiting

Implement per-user rate limiting in your application:
const userRateLimiter = new Map();

async function executeWithRateLimit(userId, method, params) {
  if (userRateLimiter.get(userId) > MAX_REQUESTS_PER_MINUTE) {
    throw new Error('Rate limit exceeded for user');
  }

  // Update rate limit counter
  userRateLimiter.set(userId, (userRateLimiter.get(userId) || 0) + 1);

  return mcpClient.executeForUser(userId, method, params);
}

4. Credential Management

Each user should have their own credentials:
// Store credentials per user
const userCredentials = {
  'user_alice_123': {
    slack: 'alice_slack_credential_id',
    notion: 'alice_notion_credential_id'
  },
  'user_bob_456': {
    slack: 'bob_slack_credential_id',
    hubspot: 'bob_hubspot_credential_id'
  }
};

Important Considerations

User Prerequisites

  • Users must exist in Alloy before you can switch to their context
  • Create users via Alloy API or dashboard first
  • Invalid user IDs fall back to default context

Permission Model

  • Users inherit the server’s restriction settings
  • Cannot override server restrictions per user
  • For different permissions, create separate servers

Technical Limits

  • HTTP header size limit (typically 8KB)
  • User ID format restrictions (alphanumeric + limited special characters)
  • Rate limits apply per user context

Troubleshooting

Common Issues

Invalid User ID

Symptom: User override is ignored
{
  "warning": "Invalid x-alloy-userid header",
  "attempted": "invalid-user-id",
  "fallback": "primary_account_id"
}
Solution: Ensure the user ID exists and is properly formatted.

Missing Credentials

Symptom: Actions fail with credential errors
{
  "error": "No credentials found for user_alice_123 on connector slack"
}
Solution: Each user needs their own credentials for each connector they use.

Permission Denied

Symptom: Actions blocked by restrictions
{
  "error": "Action chat_postMessage is not allowed for connector slack"
}
Solution: Check server restrictions - they apply to all users equally.

Additional Resources

I