# How DesktopCommanderMCP Detects Client Types (Claude Desktop, Cursor, Windsurf) and Adapts Behavior

> Learn how DesktopCommanderMCP detects Claude Desktop, Cursor, and Windsurf clients. Discover how it adapts behavior based on client type for a personalized user experience.

- Repository: [Eduard Ruzga/DesktopCommanderMCP](https://github.com/wonderwhy-er/DesktopCommanderMCP)
- Tags: internals
- Published: 2026-07-19

---

**The DesktopCommanderMCP server identifies connected clients by extracting the `clientInfo` object from the MCP protocol Initialize request, storing it in a global `currentClient` variable, and using this data to conditionally render onboarding pages, filter available tools, and tag analytics events.**

DesktopCommanderMCP is a Model Context Protocol (MCP) server that dynamically adjusts its behavior based on the specific AI client connecting to it. According to the `wonderwhy-er/DesktopCommanderMCP` source code, the server implements client type detection to distinguish between Claude Desktop, Cursor, Windsurf, and other endpoints by inspecting protocol-level initialization parameters. This capability enables the server to suppress redundant onboarding flows for MCP-native clients and hide internal tools when running inside the Desktop Commander UI itself.

## Capturing Client Information from Initialize Requests

When a client initiates a connection, the server's **Initialize** handler extracts the `clientInfo` object from the request parameters. This object contains a `name` field (such as `claude-code`, `cursor`, or `windsurf`) and an optional `version` string.

In [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts), the handler captures this information and forwards it to the client update logic:

```typescript
// src/server.ts – Initialize handler (lines 12‑15)
const clientInfo = request.params?.clientInfo;
if (clientInfo) {
    await updateCurrentClient(clientInfo);
}

```

The `clientInfo` object serves as the primary source of truth for all subsequent client-specific adaptations within the server lifecycle.

## Storing and Tracking the Current Client

The `updateCurrentClient` function updates a global `currentClient` object that persists throughout the connection session. This approach makes the client type accessible to all tool implementations and utility functions without requiring repeated parsing of the initialization request.

As implemented in [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts) (lines 86‑94):

```typescript
// src/server.ts – updateCurrentClient
currentClient = {
    name: clientInfo.name || currentClient.name,
    version: clientInfo.version || currentClient.version,
};

```

Once stored, `currentClient.name` drives conditional logic for feature flags, UI components, and analytics tracking across the entire server architecture.

## Detecting Remote and Legacy Clients

Beyond standard MCP clients, the server also identifies connections from remote devices or LLM proxies using the `isRemoteClientContext` function. This helper checks for the `DC_REMOTE_DEVICE` environment variable or a legacy client identifier (`desktop-commander-client`) to distinguish remote contexts from local IDE integrations.

From [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts) (lines 79‑81):

```typescript
function isRemoteClientContext(clientName?: string): boolean {
    return process.env.DC_REMOTE_DEVICE === 'true' ||
           clientName === 'desktop-commander-client';
}

```

Remote contexts trigger different security and UI behaviors, such as skipping local onboarding pages that require direct filesystem access.

## Client-Specific Adaptations and Feature Flags

The server uses the stored `currentClient` data to modify its behavior across three primary areas: onboarding flows, tool availability, and analytics tagging.

### Conditional Onboarding Exclusions

The server maintains a feature flag `welcome_page_excluded_clients` that lists clients like `claude-code`, `cursor`, and `windsurf`. When these clients connect, the welcome page is suppressed to avoid redundant setup prompts, as these applications handle their own configuration.

The eligibility logic in [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts) (lines 21‑24) evaluates:

```typescript
const isWelcomePageEligibleClient =
    currentClient.name !== 'desktop-commander-app' &&
    currentClient.name !== 'desktop-commander' &&
    !isRemoteClientContext(currentClient.name) &&
    !(global as any).disableOnboarding;

```

This prevents the Desktop Commander app itself, remote devices, and major MCP clients from receiving the standard onboarding URL.

### Tool Visibility Control

Certain internal tools—specifically feedback-related functions like `give_feedback_to_desktop_commander` and `get_prompts`—are hidden when the server detects it is running inside the Desktop Commander UI itself. This prevents self-referential tool loops where the app would try to submit feedback to itself.

As shown in [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts) (lines 86‑92):

```typescript
if (currentClient?.name === 'desktop-commander-app') {
    if (toolName === 'give_feedback_to_desktop_commander' ||
        toolName === 'get_prompts') {
        return false;
    }
}

```

### Analytics Context Tagging

The server records the client type for telemetry using the `capture` function, which inspects environment variables including `CLAUDE_CODE_ENTRYPOINT`, `AI_AGENT`, and `CLAUDE_PLUGIN_DATA` to distinguish between CLI, Desktop, and plugin contexts.

From [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts) (lines 45‑50):

```typescript
capture('run_server_mcp_initialized', {
    host_entrypoint: process.env.CLAUDE_CODE_ENTRYPOINT?.substring(0, 100),
    host_agent: process.env.AI_AGENT?.substring(0, 100),
    host_plugin_id: process.env.CLAUDE_PLUGIN_DATA
        ? path.basename(process.env.CLAUDE_PLUGIN_DATA).substring(0, 100)
        : undefined,
});

```

This tagging enables the maintainers to analyze usage patterns across different client ecosystems.

## Practical Implementation Examples

### Accessing the Client Name in Custom Tools

Tool implementations can import the `currentClient` object to adjust behavior for specific clients:

```typescript
import { currentClient } from '../server';

export async function myTool(_: any) {
  const client = currentClient?.name ?? 'unknown';
  if (client === 'cursor') {
    // Adjust behavior for Cursor
    return { result: 'Cursor‑specific response' };
  }
  if (client === 'windsurf') {
    // Adjust behavior for Windsurf
    return { result: 'Windsurf‑specific response' };
  }
  // Default handling for other clients
  return { result: 'Generic response' };
}

```

### Skipping Onboarding for Remote Clients

Remote detection logic can gate UI flows that require local filesystem access:

```typescript
import { isRemoteClientContext, currentClient } from '../server';

if (isRemoteClientContext(currentClient?.name)) {
  // Remote client – do not show onboarding UI
  await skipWelcomePageOnboarding();
} else {
  await handleWelcomePageOnboarding(currentClient?.name);
}

```

## Key Files Involved

| File | Role |
|------|------|
| **src/server.ts** | Core request handling, client detection via `clientInfo`, and `currentClient` state management |
| **src/utils/open-browser.ts** | Sends onboarding page URLs with `utm_source` derived from the client name |
| **src/utils/welcome-onboarding.ts** | Implements onboarding flows and respects the `welcome_page_excluded_clients` flag |
| **src/utils/feature-flags.ts** | Provides the `welcome_page_excluded_clients` flag listing `claude-code`, `cursor`, and `windsurf` |
| **src/remote-device/desktop-commander-integration.ts** | Sets `DC_REMOTE_DEVICE` for remote-device wrappers |
| **src/utils/capture.ts** | Emits analytics events containing client-type identifiers |

## Summary

- **Client detection** relies on the `clientInfo` object supplied in the MCP protocol Initialize request, specifically extracting the `name` and `version` fields.
- **Global state** stores the client type in `currentClient`, updated via `updateCurrentClient` in [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts), making it accessible across tool implementations.
- **Remote identification** uses `isRemoteClientContext` to check the `DC_REMOTE_DEVICE` environment variable and legacy client identifiers.
- **Feature adaptation** leverages the `welcome_page_excluded_clients` flag to suppress onboarding for Claude Desktop, Cursor, and Windsurf, while tool filtering hides internal utilities when running inside the Desktop Commander app.
- **Analytics tagging** captures client context through environment variables to track usage across different entry points.

## Frequently Asked Questions

### How does DesktopCommanderMCP identify which AI client is connecting?

The server reads the `clientInfo` object from the MCP Initialize request parameters, which contains a `name` field identifying the client (e.g., `claude-code`, `cursor`, `windsurf`). This information is stored in the global `currentClient` variable and used throughout the server session to adapt behavior.

### What is the difference between client detection for local versus remote connections?

Local connections are identified directly from the `clientInfo.name` field in the Initialize request, while remote connections are detected via the `isRemoteClientContext` function, which checks for the `DC_REMOTE_DEVICE` environment variable or the legacy identifier `desktop-commander-client`. Remote contexts bypass local onboarding flows and may have restricted tool access.

### How can I access the client type inside a custom tool implementation?

Import the `currentClient` object from [`src/server.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/server.ts) and access its `name` property. This allows conditionals like `if (currentClient?.name === 'cursor')` to provide client-specific responses or adjust tool behavior without modifying the core server initialization logic.

### Why are some tools hidden for certain clients?

Tools like `give_feedback_to_desktop_commander` are explicitly filtered out when `currentClient.name === 'desktop-commander-app'` to prevent the UI from invoking feedback tools on itself. Similarly, the onboarding welcome page is suppressed for clients listed in `welcome_page_excluded_clients` (including `claude-code`, `cursor`, and `windsurf`) because these applications handle their own configuration workflows.