> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mcp-use.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Elicitation

> Request user input from TypeScript MCP tools with safe form and URL flows.

Elicitation lets a tool pause and ask the client for user input. Use it when a tool cannot continue safely without a user decision, missing field, or external authorization step.

This guide focuses on when to use elicitation and how to handle the user response. Use the [Tool context API reference](/typescript/api-reference/server/tool-context#elicit) for exact overloads, result types, timeout options, validation errors, and return shapes. For enum fields, use Zod (`z.enum`, `z.array(z.enum(...))`) — see [Elicitation schemas](/typescript/api-reference/server/elicitation-schemas) if you are migrating from v1 enum helpers.

## Choose form mode or URL mode

Use form mode for non-sensitive structured input. Use URL mode for sensitive or external flows.

| Need                                                                              | Mode      |
| --------------------------------------------------------------------------------- | --------- |
| Ask for preferences, labels, confirmation fields, or non-sensitive details        | Form mode |
| Send the user through OAuth, payment, credential entry, or another sensitive flow | URL mode  |

Never collect credentials, API keys, payment details, or OAuth secrets through form mode. Form responses pass through the MCP client.

Keep form schemas flat and simple. Use top-level primitive fields, enum fields, and string-array enum selections. For nested objects, files, credentials, or richer forms, send the user through URL mode or another external flow.

## Check client support

Only use elicitation when the connected client supports the mode you need. Provide a fallback for clients that do not advertise that support.

```typescript theme={null}
import { error, text } from "mcp-use";
import { z } from "zod";

server.tool(
  {
    name: "collect_contact",
    description: "Collect non-sensitive contact details before continuing.",
  },
  async (_params, ctx) => {
    if (!ctx.client.capabilities().elicitation?.form) {
      return error("This client does not support form elicitation.");
    }

    const result = await ctx.elicit(
      "Who should receive the follow-up?",
      z.object({
        name: z.string().describe("Recipient name"),
        email: z.string().email().describe("Recipient email"),
      }),
    );

    if (result.action !== "accept") {
      return text("No contact details were provided.");
    }

    return text(
      `Follow-up will go to ${result.data.name} at ${result.data.email}.`,
    );
  },
);
```

In form mode, mcp-use validates accepted responses against your Zod schema before returning `result.data`.

## Handle every user decision

An elicitation request can be accepted, declined, or cancelled. Treat each outcome deliberately.

```typescript theme={null}
const result = await ctx.elicit(
  "Confirm the export settings.",
  z.object({
    includePrivateFields: z.boolean().default(false),
  }),
);

switch (result.action) {
  case "accept":
    return text(
      result.data.includePrivateFields
        ? "Exporting with private fields."
        : "Exporting without private fields.",
    );
  case "decline":
    return text("The user declined to provide export settings.");
  case "cancel":
    return text("The export was cancelled.");
}
```

Use `decline` for an explicit refusal and `cancel` for dismissal or interruption. Avoid continuing a risky operation after either outcome.

## Use form mode for non-sensitive data

Form mode works well when the user needs to choose or fill in ordinary application data.

```typescript theme={null}
server.tool(
  {
    name: "create_ticket",
    description: "Create a support ticket after asking for missing details.",
  },
  async (_params, ctx) => {
    if (!ctx.client.capabilities().elicitation?.form) {
      return error("This client does not support form elicitation.");
    }

    const result = await ctx.elicit(
      "Add ticket details.",
      z.object({
        title: z.string().min(3),
        priority: z.enum(["low", "normal", "high"]).default("normal"),
        description: z.string().optional(),
      }),
    );

    if (result.action !== "accept") {
      return text("Ticket creation cancelled.");
    }

    const ticket = await tickets.create(result.data);
    return text(`Created ticket ${ticket.id}.`);
  },
);
```

Use `.describe()` on fields when labels or expected values are not obvious. Use `.default()` only when the default is safe.

## Use URL mode for sensitive flows

URL mode directs the user to an external page. Use it when secrets or authorization codes must stay outside the MCP client.

```typescript theme={null}
server.tool(
  {
    name: "connect_github",
    description: "Ask the user to authorize GitHub access.",
  },
  async (_params, ctx) => {
    if (!ctx.client.capabilities().elicitation?.url) {
      return error("This client does not support URL elicitation.");
    }

    const authRequest = await githubAuth.createAuthorizationRequest();
    const result = await ctx.elicit(
      "Authorize GitHub access to continue.",
      authRequest.url,
    );

    if (result.action !== "accept") {
      return error("GitHub authorization was not completed.");
    }

    const connection = await githubAuth.waitForCallback(authRequest.state);

    if (!connection.ok) {
      return error("GitHub authorization did not finish.");
    }

    return text("GitHub authorization completed.");
  },
);
```

The `accept` action only means the user continued the URL flow. Your server must still verify the OAuth callback, exchange the code, and store tokens securely before reporting success.

## Catch validation and client errors

Wrap elicitation when the tool can recover from validation errors, unsupported clients, timeouts, or transport failures.

```typescript theme={null}
try {
  const result = await ctx.elicit(
    "Choose a deployment environment.",
    z.object({
      environment: z.enum(["staging", "production"]),
    }),
  );

  if (result.action !== "accept") {
    return text("Deployment cancelled.");
  }

  return text(`Deploying to ${result.data.environment}.`);
} catch (err) {
  return error(
    `Could not collect deployment input: ${err instanceof Error ? err.message : String(err)}`,
  );
}
```

For exact timeout behavior, validation error types, and verbose schema forms, see the [Tool context API reference](/typescript/api-reference/server/tool-context#elicit).

## Test elicitation locally

Run the elicitation example or call an elicitation tool from the Inspector.

```bash theme={null}
cd libraries/typescript/packages/mcp-use/examples/server/features/elicitation
pnpm install
pnpm dev
```

Then open the Inspector, call a tool that uses elicitation, and verify accept, decline, and cancel behavior.

## Next steps

<CardGroup cols={2}>
  <Card title="Tool context API reference" icon="terminal" href="/typescript/api-reference/server/tool-context#elicit">
    Look up `ctx.elicit()` overloads, result types, timeout options, and
    validation behavior.
  </Card>

  <Card title="Elicitation schemas (v1 migration)" icon="list-checks" href="/typescript/api-reference/server/elicitation-schemas">
    Map removed v1 enum helpers to Zod form schemas.
  </Card>

  <Card title="Sampling" icon="pipette" href="/typescript/server/sampling">
    Ask the client LLM for a completion during tool execution.
  </Card>

  <Card title="Client elicitation" icon="monitor" href="/typescript/client/elicitation">
    Handle elicitation requests in a TypeScript MCP client.
  </Card>
</CardGroup>
