---
title: "Built-in AI chat assistant"
description: "Movk Nuxt Docs includes a built-in AI Chat module that integrates deeply with your docs through MCP tools, supporting streaming responses, code highlighting, multi-model switching, and a floating input. Learn how to configure models, customize providers, write a system prompt, and use useAIChat to control the chat state."
canonical_url: "https://docs.mhaibaraai.cn/en/docs/getting-started/ai-chat"
---
# Built-in AI chat assistant

> Movk Nuxt Docs includes a built-in AI Chat module that integrates deeply with your docs through MCP tools, supporting streaming responses, code highlighting, multi-model switching, and a floating input. Learn how to configure models, customize providers, write a system prompt, and use useAIChat to control the chat state.

## Overview

A Nuxt module that provides an AI chat interface powered by MCP (Model Context Protocol) tools.

## Quick start

Movk Nuxt Docs already includes the AI Chat module. To enable it, apply the following configuration:

### Add the module configuration in `nuxt.config.ts`:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  extends: ['@movk/nuxt-docs'],
  aiChat: {
    model: 'zai/glm-4.7',
    models: [
      'zai/glm-4.7',
      'anthropic/claude-sonnet-4.6',
      'google/gemini-3-flash'
    ],
  }
})
```

Module configuration options:

<table>
<thead>
  <tr>
    <th>
      Option
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        apiPath
      </code>
    </td>
    
    <td>
      <code className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" language="ts-type" style="">
        <span class="sBMFI">
          string
        </span>
      </code>
    </td>
    
    <td>
      <code>
        /api/ai-chat
      </code>
    </td>
    
    <td>
      The chat API endpoint path
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mcpPath
      </code>
    </td>
    
    <td>
      <code className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" language="ts-type" style="">
        <span class="sBMFI">
          string
        </span>
      </code>
    </td>
    
    <td>
      <code>
        /mcp
      </code>
    </td>
    
    <td>
      The MCP server connection path
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        model
      </code>
    </td>
    
    <td>
      <code className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" language="ts-type" style="">
        <span class="sBMFI">
          string
        </span>
      </code>
    </td>
    
    <td>
      <code>
        -
      </code>
    </td>
    
    <td>
      The model identifier
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        models
      </code>
    </td>
    
    <td>
      <code className="language-ts-type shiki shiki-themes material-theme-lighter material-theme material-theme-palenight" language="ts-type" style="">
        <span class="sBMFI">
          string
        </span>
        
        <span class="sTEyZ">
          []
        </span>
      </code>
    </td>
    
    <td>
      <code>
        []
      </code>
    </td>
    
    <td>
      The list of available models (in "provider/model" or "model" format)
    </td>
  </tr>
</tbody>
</table>

### Configure the AI Chat features in `app/app.config.ts`:

```ts [app/app.config.ts]
export default defineAppConfig({
  aiChat: {
    floatingInput: true,
    explainWithAi: true,
    faqQuestions: [
      {
        category: 'Quick start',
        items: ['How do I install it?', 'How do I configure it?'],
      },
      {
        category: 'Advanced usage',
        items: ['How do I customize it?'],
      },
    ],
    shortcuts: {
      focusInput: 'meta_i'
    },
    texts: {
      title: 'AI Assistant',
      placeholder: 'Type your question...',
      // ... more text configuration
    }
  }
})
```

> [!TIP]
> See: /en/docs/getting-started/configuration#ai-chat
> 
> See the full AI Chat configuration options

> [!NOTE]
> 
> When multiple languages are enabled, 
> 
> faqQuestions
> 
>  can be an object grouped by locale (e.g. 
> 
> { 'zh-CN': [...], 'en': [...] }
> 
> ), and the panel switches with the current locale.

### Set your API key as an environment variable:

```bash [.env]
# Required: the module is disabled when this is empty
AI_API_KEY=your-api-key

# Optional: OpenAI-compatible endpoint. Leave empty to route through the AI Gateway.
# AI_BASE_URL=https://api.deepseek.com/v1
```

> [!TIP]
> 
> AI Chat is enabled as long as 
> 
> AI_API_KEY
> 
>  is present; if it is missing, the module is disabled and logs a message to the console.

### Recommended setup

**Default: AI Gateway.** Set only `AI_API_KEY`; a single key reaches every provider and `models` can mix vendors freely, exactly as in the quick start above. This is the least setup and fits most cases.

**Direct connection, e.g. from mainland China: Alibaba Cloud Model Studio.** Its compatible mode covers the Qwen, GLM, and DeepSeek families under one key, with no Gateway account required:

```bash [.env]
AI_API_KEY=your-dashscope-key
AI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
```

```ts [nuxt.config.ts]
aiChat: {
  model: 'alibaba/qwen3.7-plus',
  models: [
    'alibaba/qwen3.7-plus',
    'zai/glm-5.1',
    'deepseek/deepseek-v3.2'
  ]
}
```

This is what this site runs on. In direct mode the prefix is stripped before the request, so the three models are sent as `qwen3.7-plus`, `glm-5.1`, and `deepseek-v3.2`, while the prefix stays behind to render each vendor's icon in the dropdown. See [Model routing](#model-routing) for more endpoints and routing details.

## Automatic integration

> [!TIP]
> 
> The AI Chat feature is built into Movk Nuxt Docs, with no need to add components manually.

By default, the following features are enabled automatically:

- **AI Chat trigger button**: shown on the right side of the page; clicking it opens the AI assistant panel
- **Floating input**: shown at the bottom of documentation pages (controlled via `appConfig.aiChat.floatingInput`)
- **AI explain button**: shown in the documentation sidebar (controlled via `appConfig.aiChat.explainWithAi`)

### Manual integration (optional)

If you need to use the AI Chat components on a custom page:

```vue
<template>
  <div>
    <AiChat />
    <AiChatPanel />
  </div>
</template>
```

> [!NOTE]
> 
> FAQ questions are now configured in 
> 
> app/app.config.ts
> 
> , with no need to pass props in the component.

### Floating input

The floating input is integrated at the bottom of documentation pages by default. To use it on a custom page:

```vue
<template>
  <div>
    <Teleport to="body">
      <ClientOnly>
        <AiChatPanel />
        <AiChatFloatingInput />
      </ClientOnly>
    </Teleport>
  </div>
</template>
```

> [!TIP]
> 
> Use 
> 
> Teleport
> 
>  to render the floating input at the body level, ensuring it stays pinned to the bottom regardless of where it sits in the component hierarchy

### Programmatic control

Use the `useAIChat` composable to control the chat:

```vue
<script setup>
const {
  isEnabled,
  isOpen,
  messages,
  toggleChat,
  open
} = useAIChat()

// Open the chat and send an initial message
open('How do I install this module?')

// Toggle chat visibility
toggle()
</script>
```

## Customization

### Custom model providers

The layer only ships `@ai-sdk/openai`. To use a provider that does not speak the OpenAI-compatible protocol, install its AI SDK package in your own project and register it under a prefix:

> [!NOTE]
> See: https://ai-sdk.dev/providers/ai-sdk-providers/
> 
> See the list of providers supported by the AI SDK

```ts [server/plugins/modelProviders.ts]
import { modelProviderRegistry } from '#ai-chat/server/utils/modelProviders'
import { createAnthropic } from '@ai-sdk/anthropic'

export default defineNitroPlugin(() => {
  // Registers the anthropic prefix, so anthropic/claude-sonnet-4.6 resolves here
  modelProviderRegistry.register('anthropic', ({ modelId }) => {
    const anthropic = createAnthropic({
      apiKey: process.env.ANTHROPIC_API_KEY
      // Custom configuration...
    })
    return anthropic(modelId)
  })
})
```

Configure the environment variable:

```bash [.env]
ANTHROPIC_API_KEY=your_api_key
```

#### Model routing

`getModel()` resolves a model id in three steps:

1. The prefix matches a registered provider, which is used
2. `AI_BASE_URL` is set, so the endpoint is called directly over the OpenAI-compatible protocol
3. Otherwise, the request falls back to the AI Gateway

Without `AI_BASE_URL`, every model goes through the AI Gateway, where a single key proxies multiple providers and model ids keep the `provider/model` shape. With it, requests hit the endpoint directly over chat completions; the prefix then only drives the icon in the model dropdown and is stripped before the request, so `deepseek/deepseek-chat` is sent as `deepseek-chat`.

<table>
<thead>
  <tr>
    <th>
      Provider
    </th>
    
    <th>
      <code>
        AI_BASE_URL
      </code>
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      DeepSeek
    </td>
    
    <td>
      <code>
        https://api.deepseek.com/v1
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Alibaba Cloud Qwen (international)
    </td>
    
    <td>
      <code>
        https://dashscope-intl.aliyuncs.com/compatible-mode/v1
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Alibaba Cloud Qwen (China)
    </td>
    
    <td>
      <code>
        https://dashscope.aliyuncs.com/compatible-mode/v1
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Zhipu GLM
    </td>
    
    <td>
      <code>
        https://open.bigmodel.cn/api/paas/v4
      </code>
    </td>
  </tr>
</tbody>
</table>

> [!NOTE]
> 
> One 
> 
> AI_API_KEY
> 
>  + 
> 
> AI_BASE_URL
> 
>  pair maps to a single upstream, so keep 
> 
> models
> 
>  to that provider's models. To reach several providers at once, register custom providers as shown above.

### System prompt

To customize the AI's behavior, create or edit the system prompt in the following file:
`server/api/ai-chat.ts`

[](https://github.com/mhaibaraai/movk-nuxt-docs/blob/main/layer/modules/ai-chat/runtime/server/api/ai-chat.ts)

### Styling

The components use Nuxt UI and Tailwind CSS design tokens. You can customize the appearance by modifying the component files or overriding UI properties.

## API

### `AiChat`

The simplest way to integrate, showing the assistant button.

[](https://github.com/mhaibaraai/movk-nuxt-docs/blob/main/layer/modules/ai-chat/runtime/components/AiChat.vue)

### `AiChatFloatingInput`

A floating input at the bottom of the viewport. Requires no props.

**Keyboard shortcuts:**

- `⌘I` / `Ctrl+I` - focus the input
- `Escape` - blur the input
- `Enter` - submit the question

> [!NOTE]
> 
> Wrap it with 
> 
> Teleport
> 
>  and 
> 
> ClientOnly
> 
>  to ensure it renders correctly.

[](https://github.com/mhaibaraai/movk-nuxt-docs/blob/main/layer/modules/ai-chat/runtime/components/AiChatFloatingInput.vue)

### `AiChatModelSelect`

A model selection dropdown for switching AI models. Requires no props.

> [!NOTE]
> 
> This component automatically displays the 
> 
> models
> 
>  list configured in 
> 
> nuxt.config.ts
> 
>  and uses the 
> 
> useModels
> 
>  composable to manage model state.

[](https://github.com/mhaibaraai/movk-nuxt-docs/blob/main/layer/modules/ai-chat/runtime/components/AiChatModelSelect.vue)

### `AiChatPanel`

The complete AI chat panel interface.

**Features:**

- An expandable/collapsible sidebar panel
- Automatically pushes the main content area
- Built-in message history and streaming responses
- Supports code highlighting and Markdown rendering

[](https://github.com/mhaibaraai/movk-nuxt-docs/blob/main/layer/modules/ai-chat/runtime/components/AiChatPanel.vue)

### `AiChatDisabled`

The disabled-state component shown when the AI Chat feature is not enabled.

[](https://github.com/mhaibaraai/movk-nuxt-docs/blob/main/layer/modules/ai-chat/runtime/components/AiChatDisabled.vue)

## Composables

### `useAIChat()`

Conversation state management, providing state control and message management for the chat interface.

#### Return values

**isOpen** (`Ref<boolean>`): The reactive state of whether the dialog is open.

**messages** (`Ref<UIMessage[]>`): The message list, containing all conversation history.

**open** (`(text: string) => void`): Open the dialog and send an initial message.The initial message, sent automatically when the dialog opens.

**toggleChat** (`() => void`): Toggle the open/closed state of the dialog.

### `useModels()`

Model configuration management, controlling the list of available AI models and the currently selected model.

#### Return values

**models** (`Ref<string[]>`): The list of available models, read from the aiChat.models configuration in nuxt.config.ts.

**model** (`Ref<string>`): The currently selected model ID, persisted to localStorage.

**formatModelName** (`(modelId: string) => string`): Format a model ID into a readable display name (removing the prefix and suffix).The model ID, in a format such as anthropic/claude-sonnet-4.6.

### `useHighlighter()`

Asynchronously loads a Shiki code highlighter instance, used for syntax highlighting of code blocks.

#### Return value

Returns a Promise that resolves to a configured Shiki Highlighter instance.

> [!NOTE]
> 
> **Supported languages:** `vue`, `js`, `ts`, `css`, `html`, `json`, `yaml`, `markdown`, `bash`
> 
> **Supported themes:**
> 
> - `material-theme-palenight` (dark mode)
> - `material-theme-lighter` (light mode)

```ts
const highlighter = await useHighlighter()

// Usage example
const html = highlighter.codeToHtml(code, {
  lang: 'typescript',
  theme: 'material-theme-palenight'
})
```

### `useToolCall(state, toolName, input)`

An extension point for AI chat tool calls, allowing you to customize the text label and icon shown when a tool is called.

When the AI assistant calls a tool, the `AiChatPanel` component calls this composable to get the display information for that tool. The default implementation returns an empty object, and the built-in tool labels are handled by the component itself. By overriding this composable in your application layer, you can add display support for custom tools.

#### Parameters

**state** (`ToolState`): The tool call state, used to determine whether it is complete: 'output-available' means complete, other values mean in progress.

**toolName** (`string`): The tool name, corresponding to the name field in the MCP tool definition.

**input** (`Record<string, string | undefined>`): The input parameters of the tool call.

#### Return values

**toolMessage** (`Record<string, string>`): A mapping from tool name to display text. The key is the tool name and the value is the corresponding display text. Unmatched tool names fall back to the default text.

**toolIcon** (`Record<string, string>`): A mapping from tool name to icon. The key is the tool name and the value is an Iconify icon name. Unmatched tool names fall back to i-lucide-search.

#### Extending with custom tools

Create a composable of the same name in your application layer to override the default implementation:

```ts [composables/useToolCall.ts]
import type { ToolState } from '#ai-chat/types'

export function useToolCall(state: ToolState, toolName: string, input: Record<string, string | undefined>) {
  const searchVerb = state === 'output-available' ? 'Searched' : 'Searching'
  const readVerb = state === 'output-available' ? 'Read' : 'Reading'

  const toolMessage: Record<string, string> = {
    'search-api': `${searchVerb} the API docs`,
    'get-schema': `${readVerb} the ${input.name || ''} type definition`,
  }

  const toolIcon: Record<string, string> = {
    'search-api': 'i-lucide-book-open',
    'get-schema': 'i-lucide-braces',
  }

  return {
    toolMessage,
    toolIcon,
  }
}
```

#### Default handling of built-in tools

The following tools are handled internally by `AiChatPanel` and do not need to be defined in `useToolCall`. The default handling is tailored to the MCP tools built into the current project, and a single configuration generates both the display text and the icon:

<table>
<thead>
  <tr>
    <th>
      Tool name
    </th>
    
    <th>
      Display text
    </th>
    
    <th>
      Icon
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        search-documentation
      </code>
    </td>
    
    <td>
      Searching / Searched documentation pages, optionally with <code>
        section
      </code>
      
       and <code>
        search
      </code>
      
       filters
    </td>
    
    <td>
      <code>
        i-lucide-book-search
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        search-composables
      </code>
    </td>
    
    <td>
      Searching / Searched composables, optionally with a <code>
        search
      </code>
      
       filter
    </td>
    
    <td>
      <code>
        i-lucide-braces
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        search-icons
      </code>
    </td>
    
    <td>
      Searching / Searched icons, optionally with a <code>
        query
      </code>
      
       filter
    </td>
    
    <td>
      <code>
        i-lucide-search
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        list-examples
      </code>
    </td>
    
    <td>
      Searching / Searched examples
    </td>
    
    <td>
      <code>
        i-lucide-codesandbox
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        get-documentation-page
      </code>
    </td>
    
    <td>
      Reading / Read the <code>
        {path}
      </code>
      
       page
    </td>
    
    <td>
      <code>
        i-lucide-book-open
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        get-example
      </code>
    </td>
    
    <td>
      Reading / Read the <code>
        {exampleName}
      </code>
      
       example
    </td>
    
    <td>
      <code>
        i-lucide-codepen
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        get-component
      </code>
    </td>
    
    <td>
      Reading / Read the <code>
        {componentName}
      </code>
      
       component docs
    </td>
    
    <td>
      <code>
        i-lucide-box
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        get-component-metadata
      </code>
    </td>
    
    <td>
      Reading / Read the <code>
        {componentName}
      </code>
      
       component metadata
    </td>
    
    <td>
      <code>
        i-lucide-file-code
      </code>
    </td>
  </tr>
</tbody>
</table>


## Sitemap

See the full [sitemap](https://docs.mhaibaraai.cn/sitemap.md) for all pages.
