# How the MCP Server Exposes CRM Capabilities Without Leaking Tenant Data

> Discover how the MCP server secures CRM capabilities, preventing tenant data leaks with JWT auth, scoped queries, and sanitization. Learn about DeskcommCRM's data protection.

- Repository: [Rafael Melgaço/DeskcommCRM](https://github.com/melgarafael/DeskcommCRM)
- Tags: architecture
- Published: 2026-09-13

---

**The DeskcommCRM MCP server prevents cross-tenant data leakage by enforcing JWT-based authentication, organization-scoped queries via McpContext, brute-force UUID sanitization, and comprehensive audit logging that returns only sanitized results.**

The **MCP (Model‑Context‑Protocol) server** in the DeskcommCRM repository acts as the secure gateway for exposing CRM functionality to AI agents and external callers. According to the source code, the implementation relies on a layered security model that strictly isolates tenant data while maintaining rich API capabilities. This architecture ensures that tools for managing leads, contacts, and conversations remain accessible only within the boundaries of the authenticated tenant’s organization.

## Authentication and Authorization Layer

Incoming requests first pass through strict validation in [`lib/mcp/auth.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/mcp/auth.ts). The `verifyMcpToken` function validates JWT tokens and extracts critical security metadata.

Two core guard functions enforce access control:

- **`ensureScope`**: Verifies that the JWT-derived scopes contain the tool’s required scope (e.g., `mcp:read`)
- **`ensureRole`**: Confirms the caller’s role matches the required permission (e.g., `actor:ai_agent`)

The authentication result carries the tenant’s `organizationId`, which becomes the foundation for all subsequent data filtering. This ensures that only authenticated tenants can invoke tools, and every call is explicitly tied to a single organization.

## Tenant Isolation via McpContext

Once authenticated, the server constructs a secure execution environment defined in [`lib/mcp/types.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/mcp/types.ts). The **`McpContext`** object encapsulates the tenant’s identity and database access:

- **`organizationId`**: The unique tenant identifier extracted during authentication
- **`role`** and **`actor`**: Permission metadata for the current session
- **`supabase`**: A Supabase admin client pre-configured for the tenant

All downstream queries in the tool handlers must filter on `organization_id`, guaranteeing that data retrieval is physically scoped to the caller’s tenant. The context is immutable and passed as the second argument to every tool handler, preventing accidental scope escalation.

## Input Sanitization and Hygiene

Before any tool executes, raw arguments undergo sanitization in [`lib/mcp/uuid-de-aterro.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/mcp/uuid-de-aterro.ts). The **`higienizarUuidsDeAterro`** function removes "dump" UUIDs from input parameters.

This prevents attackers from using optional UUID filters to brute-force records belonging to other tenants. By stripping potentially malicious UUID values from query parameters, the system ensures that optional filters never become vectors for cross-tenant data leakage.

## Tool Registration and Audit Trails

The `createMcpServer` function in [`lib/mcp/server.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/mcp/server.ts) registers every tool with a strict contract including name, description, input schema, required scopes, and roles. Each invocation follows a secure lifecycle:

1. **Pre-execution**: Arguments are sanitized and scope/role requirements are verified
2. **Execution**: The tool handler receives the sanitized inputs and `McpContext`
3. **Auditing**: The `auditMcpToolCall` function logs the tenant ID, tool name, sanitized arguments, execution duration, and result summary
4. **Result handling**: Successful results are serialized to JSON within a uniform `content` array, while errors are wrapped in an `isError` envelope to prevent stack trace exposure

The audit records contain tenant identification and execution metadata but never include raw database rows, ensuring that logs themselves do not become leakage channels.

## Implementation Example: Secure CRM Tool Creation

The following pattern demonstrates how the MCP server instantiates a secure environment per request in [`app/api/v1/mcp/route.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/app/api/v1/mcp/route.ts):

```typescript
import { createMcpServer } from '@/lib/mcp/server';
import { verifyMcpToken } from '@/lib/mcp/auth';

export async function GET(request: Request) {
  const token = request.headers.get('Authorization')?.replace('Bearer ', '');
  const authResult = await verifyMcpToken(token!);
  const server = createMcpServer(authResult, request.headers.get('x-request-id') ?? '');
  return server.handle(request);
}

```

Inside individual tools, such as the lead management functionality, the `organizationId` from `McpContext` enforces strict data boundaries:

```typescript
export const crmListLeads = {
  name: 'crmListLeads',
  description: 'List leads belonging to the tenant',
  inputSchema: z.object({
    status: z.string().optional(),
    limit: z.number().min(1).max(100).default(20),
  }),
  requiresScope: 'mcp:read',
  requiresRole: 'actor:ai_agent',
  async handler(args, ctx) {
    const { organizationId, supabase } = ctx;
    const q = supabase.from('leads')
      .select('*')
      .eq('organization_id', organizationId)
      .limit(args.limit);
    if (args.status) q.eq('status', args.status);
    const { data } = await q;
    return data;
  },
};

```

The sanitization and auditing occur transparently through the server wrapper:

```typescript
await auditMcpToolCall({
  ctx,
  toolName: tool.name,
  args,
  durationMs,
  success: true,
  resultSummary: summarizeResult(result),
});

```

## Summary

- **Authentication** via [`lib/mcp/auth.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/mcp/auth.ts) binds every request to a specific `organizationId` through JWT validation
- **Context isolation** through `McpContext` in [`lib/mcp/types.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/mcp/types.ts) ensures all database queries are automatically scoped to the tenant
- **Input hygiene** via `higienizarUuidsDeAterro` in [`lib/mcp/uuid-de-aterro.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/mcp/uuid-de-aterro.ts) prevents brute-force attacks using UUID parameters
- **Audit logging** in [`lib/mcp/server.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/mcp/server.ts) tracks all tool invocations without exposing raw database content
- **Result sanitization** prevents error messages and stack traces from leaking internal system details

## Frequently Asked Questions

### How does the MCP server authenticate incoming requests?

The server validates JWT tokens using `verifyMcpToken` in [`lib/mcp/auth.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/mcp/auth.ts). This function extracts the tenant’s `organizationId`, assigned scopes, and role from the token, then verifies these against the tool’s requirements using `ensureScope` and `ensureRole` before allowing execution.

### What prevents a tenant from accessing another tenant's CRM data?

Every tool handler receives a `McpContext` object containing the authenticated `organizationId`. All database queries in `lib/mcp/tools/*` must include `.eq('organization_id', organizationId)`, ensuring the Supabase client returns only records belonging to the requesting tenant.

### How does the system protect against malicious UUID inputs?

Before processing, all tool arguments pass through `higienizarUuidsDeAterro` in [`lib/mcp/uuid-de-aterro.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/mcp/uuid-de-aterro.ts). This function sanitizes UUID parameters to prevent attackers from using optional filters to scan for records across tenant boundaries.

### Where are MCP tool calls logged for security auditing?

The `auditMcpToolCall` function in [`lib/mcp/server.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/mcp/server.ts) creates audit records containing the tenant ID, tool name, sanitized arguments, execution time, and result summaries. This logging occurs within the server's try/catch wrapper, ensuring every call is tracked without exposing sensitive data in the logs.