# How to Add a New Tool to the Auth0 MCP Server: Step-by-Step Implementation Guide

> Learn how to add a new tool to the Auth0 MCP server with this step-by-step guide. Implement new Auth0 API endpoints efficiently by defining metadata and handler functions.

- Repository: [Auth0/auth0-mcp-server](https://github.com/auth0/auth0-mcp-server)
- Tags: how-to-guide
- Published: 2026-02-25

---

**To add a new tool to the Auth0 MCP server, define a `Tool` metadata object and handler function in a new category file (e.g., `src/tools/<category>.ts`), then register both in [`src/tools/index.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/tools/index.ts) by spreading them into the `TOOLS` array and `allHandlers` object.**

The Auth0 MCP server exposes Auth0 Management API endpoints as Model Context Protocol (MCP) tools through a standardized architecture. Adding a new tool—whether for a new resource server operation or a custom endpoint—requires implementing a consistent pattern involving tool definitions, handler implementations, and centralized registration in the `auth0/auth0-mcp-server` repository.

## Architecture Overview

The MCP server uses a modular architecture with clear separation between tool definitions and their implementations:

- **Tool Definition** (`Tool[]`): Declares the tool name, description, input JSON schema, required scopes, and UI hints. Located in `src/tools/<category>.ts`.
- **Handler** (`Record<string, (req, cfg) => Promise<HandlerResponse>>`): Implements the Auth0 SDK call, validates tokens, and formats responses. Co-located with the tool definition.
- **Tool Registry** (`TOOLS`): A flat array in [`src/tools/index.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/tools/index.ts) that aggregates all tools for server discovery.
- **Handler Registry** (`HANDLERS`): Wrapped with analytics tracking and exported from [`src/tools/index.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/tools/index.ts).

When you add a new tool, you only need to create or extend a category file and plug the exported constants into [`src/tools/index.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/tools/index.ts). The server automatically discovers the tool without additional configuration.

## Step-by-Step Implementation Process

Follow this canonical workflow used by existing tool groups such as `applications`, `resource-servers`, and `logs`.

### Create a New Category File

Create a TypeScript file at `src/tools/<new-category>.ts` (for example, [`src/tools/custom-endpoint.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/tools/custom-endpoint.ts)). This file will contain both the tool metadata and the handler implementation for your new Auth0 Management API endpoint.

### Define Tool Metadata

Export a constant array of `Tool` objects that declare the interface for your new MCP tool:

```typescript
export const CUSTOM_ENDPOINT_TOOLS: Tool[] = [
  {
    name: 'auth0_custom_action',
    description: 'Perform the custom Auth0 Management-API action',
    inputSchema: {
      type: 'object',
      properties: {
        id: { type: 'string', description: 'The ID of the resource' },
        foo: { type: 'string' },
      },
      required: ['id'],
    },
    _meta: {
      requiredScopes: ['read:custom_scope'],
      readOnly: true,
    },
    annotations: {
      title: 'Custom Auth0 Action',
      readOnlyHint: true,
      destructiveHint: false,
      idempotentHint: true,
      openWorldHint: false,
    },
  },
];

```

The `_meta` field specifies the exact Auth0 scope required (e.g., `read:custom_scope`) and whether the operation is read-only. The `annotations` object provides UI hints for MCP clients regarding the tool's behavior.

### Implement the Handler Function

Export a handler map that implements the actual API call using the Auth0 Management SDK:

```typescript
export const CUSTOM_ENDPOINT_HANDLERS: Record<
  string,
  (req: HandlerRequest, cfg: HandlerConfig) => Promise<HandlerResponse>
> = {
  auth0_custom_action: async (request, config) => {
    if (!request.token) return createErrorResponse('Error: Missing authorization token');
    if (!config.domain) return createErrorResponse('Error: Auth0 domain is not configured');

    const mgmtCfg: Auth0Config = { domain: config.domain, token: request.token };
    const client = await getManagementClient(mgmtCfg);

    const { id, foo } = request.parameters;

    try {
      const result = await client.customEndpoint.perform({ id }, { foo });
      return createSuccessResponse(result);
    } catch (sdkError: any) {
      let msg = `Failed to run custom action: ${sdkError.message || 'Unknown error'}`;
      if (sdkError.statusCode === 401) {
        msg += '\nError: Unauthorized – check token and required scopes.';
      } else if (sdkError.statusCode === 404) {
        msg = `Resource with id '${id}' not found.`;
      }
      return createErrorResponse(msg);
    }
  },
};

```

Every handler must validate the `request.token` and `config.domain` before proceeding. Use `getManagementClient` from [`src/utils/auth0-client.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/utils/auth0-client.ts) to instantiate the SDK, and return responses via `createSuccessResponse` or `createErrorResponse` from [`src/utils/http-utility.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/utils/http-utility.ts).

### Register in the Global Registry

Import and spread your new constants into [`src/tools/index.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/tools/index.ts):

```typescript
import { CUSTOM_ENDPOINT_HANDLERS, CUSTOM_ENDPOINT_TOOLS } from './custom-endpoint.js';

export const TOOLS: Tool[] = [
  ...APPLICATION_TOOLS,
  ...RESOURCE_SERVER_TOOLS,
  ...ACTION_TOOLS,
  ...LOG_TOOLS,
  ...FORM_TOOLS,
  ...APPLICATION_GRANTS_TOOLS,
  ...CUSTOM_ENDPOINT_TOOLS,
];

const allHandlers = {
  ...APPLICATION_HANDLERS,
  ...RESOURCE_SERVER_HANDLERS,
  ...ACTION_HANDLERS,
  ...LOG_HANDLERS,
  ...FORM_HANDLERS,
  ...APPLICATION_GRANTS_HANDLERS,
  ...CUSTOM_ENDPOINT_HANDLERS,
};

```

The `createHandlersWithAnalytics` wrapper automatically applies analytics tracking to your new handler—no additional code required.

### Test Your Implementation

Run the test suite to ensure your tool integrates correctly:

```bash
npm test

```

If you created a new category, add unit tests under `test/tools/<category>.test.ts` that mock the Auth0 SDK call, send a synthetic `HandlerRequest`, and assert the `HandlerResponse` shape. Verify that error handling works correctly for 401 and 404 status codes.

## Concrete Example: Adding a User Blocks Tool

Below is a complete implementation adding `auth0_list_user_blocks`, which maps to the Auth0 Management API endpoint `GET /api/v2/user-blocks`.

Create [`src/tools/user-blocks.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/tools/user-blocks.ts):

```typescript
import type {
  HandlerConfig,
  HandlerRequest,
  HandlerResponse,
  Tool,
} from '../utils/types.js';
import { log } from '../utils/logger.js';
import { createErrorResponse, createSuccessResponse } from '../utils/http-utility.js';
import type { Auth0Config } from '../utils/config.js';
import { getManagementClient } from '../utils/auth0-client.js';

export const USER_BLOCK_TOOLS: Tool[] = [
  {
    name: 'auth0_list_user_blocks',
    description: 'List user blocks in the tenant (e.g. after too many failed logins).',
    inputSchema: {
      type: 'object',
      properties: {
        page: { type: 'number', description: '0-based page number' },
        per_page: { type: 'number', description: 'Items per page' },
        include_totals: { type: 'boolean', description: 'Include total count' },
      },
    },
    _meta: {
      requiredScopes: ['read:user_blocks'],
      readOnly: true,
    },
    annotations: {
      title: 'List Auth0 User Blocks',
      readOnlyHint: true,
      destructiveHint: false,
      idempotentHint: true,
      openWorldHint: false,
    },
  },
];

export const USER_BLOCK_HANDLERS: Record<
  string,
  (req: HandlerRequest, cfg: HandlerConfig) => Promise<HandlerResponse>
> = {
  auth0_list_user_blocks: async (request, config) => {
    if (!request.token) return createErrorResponse('Error: Missing authorization token');
    if (!config.domain) return createErrorResponse('Error: Auth0 domain is not configured');

    const options: Record<string, any> = {};
    if (request.parameters.page !== undefined) options.page = request.parameters.page;
    if (request.parameters.per_page !== undefined) options.per_page = request.parameters.per_page;
    if (request.parameters.include_totals !== undefined) options.include_totals = request.parameters.include_totals;

    try {
      const client = await getManagementClient({ domain: config.domain, token: request.token });
      log('Fetching user blocks...');
      const { data } = await client.userBlocks.getAll(options);
      return createSuccessResponse(data);
    } catch (sdkError: any) {
      let msg = `Failed to list user blocks: ${sdkError.message || 'Unknown error'}`;
      if (sdkError.statusCode === 401) msg += '\nError: Unauthorized – check token / scopes.';
      if (sdkError.statusCode === 429) msg += '\nError: Rate limited.';
      return createErrorResponse(msg);
    }
  },
};

export { USER_BLOCK_TOOLS, USER_BLOCK_HANDLERS };

```

Then register in [`src/tools/index.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/tools/index.ts):

```typescript
import { USER_BLOCK_HANDLERS, USER_BLOCK_TOOLS } from './user-blocks.js';

export const TOOLS: Tool[] = [
  ...USER_BLOCK_TOOLS,
];

const allHandlers = {
  ...USER_BLOCK_HANDLERS,
};

```

## Key Files and Their Roles

| File | Purpose |
|------|---------|
| `src/tools/<category>.ts` | Contains the `Tool` array and handler implementations for your new endpoint. |
| [`src/tools/index.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/tools/index.ts) | Aggregates all tools and handlers; you must import and spread your new constants here. |
| [`src/utils/auth0-client.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/utils/auth0-client.ts) | Provides `getManagementClient` for SDK instantiation. |
| [`src/utils/http-utility.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/utils/http-utility.ts) | Supplies `createSuccessResponse` and `createErrorResponse` for standardized responses. |
| [`src/utils/types.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/utils/types.ts) | Defines `HandlerRequest`, `HandlerConfig`, and `HandlerResponse` interfaces. |
| `test/tools/<category>.test.ts` | Unit tests mocking SDK calls and verifying response shapes. |

## Summary

- **Define** tool metadata in a new category file with name, description, JSON schema, and required Auth0 scopes.
- **Implement** the handler to validate tokens, instantiate the Management client, call the Auth0 SDK, and normalize errors.
- **Export** both the `Tool` array and handler map from your category file.
- **Register** the exports in [`src/tools/index.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/tools/index.ts) by spreading into `TOOLS` and `allHandlers`.
- **Test** using the existing Jest/Vitest suite and add unit tests for your specific endpoint logic.
- **Document** the new tool in the README or documentation to inform end-users of availability.

## Frequently Asked Questions

### Do I need to modify server configuration files to add a new tool?

No. Once you register your tool and handler in [`src/tools/index.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/tools/index.ts), the server automatically discovers them through the `TOOLS` and `HANDLERS` exports. The analytics wrapper and RPC endpoint require no additional configuration.

### What Auth0 scopes should I specify in the `_meta` field?

Specify the exact Management API scope required by the endpoint you are exposing. For example, use `read:user_blocks` for listing user blocks or `update:resource_servers` for modifying resource servers. These scopes are enforced by the Auth0 API, not the MCP server itself.

### How should I handle SDK errors in the handler?

Catch SDK errors and normalize them using `createErrorResponse`. Check for specific `statusCode` values (such as 401 for unauthorized or 404 for not found) to provide actionable error messages. Always include the original error message for debugging purposes.

### Can I add multiple related tools in a single category file?

Yes. Define multiple objects in your `Tool` array and multiple entries in your handler map within the same file. Follow the pattern in [`src/tools/applications.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/tools/applications.ts) or [`src/tools/resource-servers.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/tools/resource-servers.ts) where related operations are grouped logically.