Overview

MCPcat allows you to identify users and associate their sessions with specific user information. This enables you to track usage patterns, understand user behavior, and provide personalized experiences.

User identification happens automatically on the first tool invocation in a session and persists throughout the entire session.

Basic Implementation

To identify users, provide an identify callback function when tracking your MCP server:

mcpcat.track(mcpServer, "proj_abc123xyz", {
  identify: async (request, extra) => {
    // Get token from request arguments or auth info
    const token = request.params?.arguments?.token || 
                  extra.authInfo?.token;
    
    // Validate token and fetch user
    const user = await validateTokenAndGetUser(token);
    
    return {
      userId: user.id,
      userName: user.email,
      userData: { 
        role: user.role,
        organization: user.orgId 
      }
    };
  }
});

User Identity Object

The identify function should return a UserIdentity object with the following structure:

interface UserIdentity {
  userId: string;                    // Required: Unique identifier
  userName?: string;                 // Optional: Display name
  userData?: Record<string, any>;    // Optional: Additional metadata
}

Common Patterns

Token-Based Authentication

Extract user information from authentication tokens:

mcpcat.track(mcpServer, "proj_abc123xyz", {
  identify: async (request, extra) => {
    // Get token from request arguments or auth info
    const token = request.params?.arguments?.token || 
                  extra.authInfo?.token;
    
    // Validate token and fetch user
    const user = await validateTokenAndGetUser(token);
    
    return {
      userId: user.id,
      userName: user.email,
      userData: { 
        role: user.role,
        organization: user.orgId 
      }
    };
  }
});

API Key Identification

Identify users based on API keys:

mcpcat.track(mcpServer, "proj_abc123xyz", {
  identify: async (request, extra) => {
    const apiKey = request.params?.arguments?.apiKey;
    
    const account = await getAccountByApiKey(apiKey);
    
    return {
      userId: account.id,
      userName: account.organizationName,
      userData: { 
        tier: account.tier,
        monthlyUsage: account.currentUsage,
        region: account.region
      }
    };
  }
});

MCP Authentication

For servers using MCP’s built-in authentication:

mcpcat.track(mcpServer, "proj_abc123xyz", {
  identify: async (request, extra) => {
    // Access auth token from extra parameter
    const authToken = extra.authInfo?.token;
    
    if (!authToken) {
      return null; // Anonymous session
    }
    
    // Use the MCP auth token to identify user
    const user = await getUserFromMCPToken(authToken);
    
    return {
      userId: user.id,
      userName: user.email,
      userData: { 
        authProvider: 'mcp',
        scopes: extra.authInfo?.scopes
      }
    };
  }
});

Error Handling

Handle identification failures gracefully:

mcpcat.track(mcpServer, "proj_abc123xyz", {
  identify: async (request, extra) => {
    try {
      const user = await fetchUserInfo(request);
      return {
        userId: user.id,
        userName: user.name
      };
    } catch (error) {
      console.error('User identification failed:', error);
      // Return null to continue with anonymous tracking
      return null;
    }
  }
});

Function Parameters

The identify function receives parameters that vary between the TypeScript and Python SDKs:

The request structure follows the MCP (Model Context Protocol) specification. The specific types and fields available depend on your SDK:

// TypeScript SDK parameters
identify: async (request, extra) => {
  // request: MCP request object
  // extra: RequestHandlerExtra object
}

request

The MCP request object following the JSON-RPC format:

{
  method: string;              // The tool/method being called (e.g., "tools/call")
  params?: {                   // Optional parameters
    _meta?: {                  // Optional metadata
      progressToken?: string | number;  // Token for progress notifications
    };
    name?: string;             // Tool name being invoked
    arguments?: {              // Tool-specific arguments passed by the client
      // Your custom arguments like userId, apiKey, token, etc. passed into tool calls
      [key: string]: any;
    };
  };
}

TypeScript: extra

The MCP RequestHandlerExtra object providing additional context:

{
  signal: AbortSignal;         // Abort signal for request cancellation
  authInfo?: AuthInfo;         // Validated access token information
  sessionId?: string;          // Session ID from the transport
  _meta?: RequestMeta;         // Metadata from the original request
  requestId: RequestId;        // JSON-RPC ID of the request (string | number)
  
  // Functions for sending related messages (not typically used in identify)
  sendNotification: (notification: Notification) => Promise<void>;
  sendRequest: <U>(request: Request, resultSchema: U, options?: RequestOptions) => Promise<z.infer<U>>;
}

Python: context

The MCP RequestContext object providing session and client information:

# Python context object
context.session           # ServerSession object
context.session.client_params.clientInfo.name     # Client name
context.session.client_params.clientInfo.version  # Client version
context.lifespan_context  # Application-specific context storage

Python Authentication Access: In Python MCP servers, authentication tokens are accessed via the get_access_token() function from mcp.server.auth.middleware.auth_context, not through the context parameter. This is different from the TypeScript SDK where auth info is available in the extra parameter.

Common Usage Patterns

identify: async (request, extra) => {
  // Access tool arguments
  const userId = request.params?.arguments?.userId;
  const apiKey = request.params?.arguments?.apiKey;
  
  // Access auth information (if using MCP auth)
  const authToken = extra.authInfo?.token;
  
  // Tool name being called
  const toolName = request.params?.name;
  
  // Check if request was cancelled
  if (extra.signal.aborted) {
    return null;
  }
  
  // Your identification logic here...
}

Important Considerations

The identify function is called only once per session on the first tool invocation. This minimizes overhead while ensuring consistent user tracking throughout the session.

Anonymous Sessions

If you don’t provide an identify function or if it returns null, sessions will be tracked anonymously. This is useful for:

  • Public APIs where user identification isn’t required
  • Development and testing environments
  • Respecting user privacy preferences