# Connect Qwen Code to tools via MCP

Qwen Code can connect to external tools and data sources through the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction). MCP servers give Qwen Code access to your tools, databases, and APIs.

## What you can do with MCP

With MCP servers connected, you can ask Qwen Code to:

- Work with files and repos (read/search/write, depending on the tools you enable)
- Query databases (schema inspection, queries, reporting)
- Integrate internal services (wrap your APIs as MCP tools)
- Automate workflows (repeatable tasks exposed as tools/prompts)

> [!tip]
>
> If you’re looking for the “one command to get started”, jump to [Quick start](#quick-start).

## Quick start

Qwen Code loads MCP servers from `mcpServers` in your `settings.json`. You can configure servers either:

- By editing `settings.json` directly
- By using `qwen mcp` commands (see [CLI reference](#manage-mcp-servers-with-qwen-mcp))

### Add your first server

1. Add a server (example: remote HTTP MCP server):

```bash
qwen mcp add --transport http my-server http://localhost:3000/mcp
```

2. Start Qwen Code and open the MCP management dialog to view and manage
   servers:

```bash
qwen
```

Then enter:

```text
/mcp
```

3. If Qwen Code was already running before you added the server, restart it in
   the same project. Then ask the model to use tools from that server.

## Where configuration is stored (scopes)

Most users only need these two scopes:

- **User scope (default)**: `~/.qwen/settings.json` across all projects on your machine
- **Project scope**: `.qwen/settings.json` in your project root

Write to user scope:

```bash
qwen mcp add --scope user --transport http my-server http://localhost:3000/mcp
```

> [!tip]
>
> For advanced configuration layers (system defaults/system settings and precedence rules), see [Settings](../configuration/settings).

## Configure servers

### Choose a transport

| Transport | When to use                                                       | JSON field(s)                               |
| --------- | ----------------------------------------------------------------- | ------------------------------------------- |
| `http`    | Recommended for remote services; works well for cloud MCP servers | `httpUrl` (+ optional `headers`)            |
| `sse`     | Legacy/deprecated servers that only support Server-Sent Events    | `url` (+ optional `headers`)                |
| `stdio`   | Local process (scripts, CLIs, Docker) on your machine             | `command`, `args` (+ optional `cwd`, `env`) |

> [!note]
>
> If a server supports both, prefer **HTTP** over **SSE**.

### Configure via `settings.json` vs `qwen mcp add`

Both approaches produce the same `mcpServers` entries in your `settings.json`—use whichever you prefer.

#### Stdio server (local process)

JSON (`.qwen/settings.json`):

```json
{
  "mcpServers": {
    "pythonTools": {
      "command": "python",
      "args": ["-m", "my_mcp_server", "--port", "8080"],
      "cwd": "./mcp-servers/python",
      "env": {
        "DATABASE_URL": "$DB_CONNECTION_STRING",
        "API_KEY": "${EXTERNAL_API_KEY}"
      },
      "timeout": 15000
    }
  }
}
```

CLI (writes to user scope by default):

```bash
qwen mcp add pythonTools -e DATABASE_URL=$DB_CONNECTION_STRING -e API_KEY=$EXTERNAL_API_KEY \
  --timeout 15000 python -m my_mcp_server --port 8080
```

#### HTTP server (remote streamable HTTP)

JSON:

```json
{
  "mcpServers": {
    "httpServerWithAuth": {
      "httpUrl": "http://localhost:3000/mcp",
      "headers": {
        "Authorization": "Bearer your-api-token"
      },
      "timeout": 5000
    }
  }
}
```

CLI:

```bash
qwen mcp add --transport http httpServerWithAuth http://localhost:3000/mcp \
  --header "Authorization: Bearer your-api-token" --timeout 5000
```

#### SSE server (remote Server-Sent Events)

JSON:

```json
{
  "mcpServers": {
    "sseServer": {
      "url": "http://localhost:8080/sse",
      "timeout": 30000
    }
  }
}
```

CLI:

```bash
qwen mcp add --transport sse sseServer http://localhost:8080/sse --timeout 30000
```

## Using MCP prompts and resources

Besides tools, Qwen Code discovers and surfaces two other MCP primitives.

### Prompts (slash commands)

Any prompt a server advertises via `prompts/list` becomes an executable
**slash command**. After discovery, type `/` and you'll see the prompt
listed (labeled `MCP: <server>`); run it like any other command:

```text
/my_prompt --arg1="value" --arg2="value"
# positional form also works:
/my_prompt "value" "value"
# show the prompt's arguments:
/my_prompt help
```

The prompt's messages are sent to the model, which then acts on them.

> Discovery is lenient about the declared `prompts` capability: some
> servers implement `prompts/list` but omit `prompts` from their
> `initialize` capabilities. Qwen Code attempts `prompts/list` anyway, so
> those prompts still appear. A server that genuinely has no prompts simply
> answers `Method not found`, which is ignored.

### Resources

Resources a server advertises via `resources/list` are discovered per
server. Open the management dialog with `/mcp` and select a server to see
its **Resources** count alongside its tools and prompts. Choose **View
resources** to browse the server's resource URIs; selecting one shows its
description and MIME type along with the exact `@server:uri` reference to
paste into a message. As with prompts, the `resources` capability is not
required to be declared.

Inject a resource's contents into your message with the `@server:uri`
syntax — type `@`, then the server name, a colon, and the resource URI:

```text
summarize @myserver:file:///docs/spec.md and list the open questions
```

Typing `@myserver:` shows an autocomplete list of that server's resources;
keep typing to filter, matching (case-insensitively) either the resource URI
or its friendly name/title. You don't have to know a URI by heart — before
you reach the colon, typing part of a server name also suggests matching
servers that expose resources, so you can pick one and drill straight into
its resource list. On submit, the referenced resource is read and its contents are
appended to your message (text inline, binary blobs as attachments); the
`@server:uri` reference is preserved in the prompt so the model knows what
it is looking at. The `server` prefix must match a configured MCP server —
otherwise the token is treated as a normal file path, so existing
`@path/to/file` references are unaffected. Resource reads are disabled in
untrusted folders.

## Progressive availability and discovery timeouts

Qwen Code discovers MCP servers in the background after the UI is already
interactive. You see the cli's first prompt within a few hundred
milliseconds even when one of your MCP servers takes several seconds
(or never responds), and the model's tool list updates within roughly
one frame (~16 ms) of each server completing its discover handshake.

- **Interactive mode**: the UI appears immediately; an MCP status pill in
  the bottom-right shows `N/M MCP servers ready` while discovery is in
  flight. Sending a prompt before MCP finishes simply means the model
  sees the tools that are ready _at that moment_; subsequent prompts see
  more tools as servers come online.
- **Non-interactive mode** (`--prompt`, stream-json, ACP): the cli still
  waits for MCP discovery to settle before sending the first prompt, so
  scripted / piped invocations see the same complete tool set the
  legacy synchronous behavior produced.

### Per-server `discoveryTimeoutMs`

Each MCP server gets a discovery-only timeout that caps how long the
initial handshake (`connect` + `tools/list` + `prompts/list` +
`resources/list`) is allowed to take. Defaults:

- **stdio servers**: 30 s
- **remote HTTP / SSE servers**: 5 s (network risk is higher)

Override per server when needed:

```jsonc
{
  "mcpServers": {
    "slow-stdio": {
      "command": "node",
      "args": ["./slow-server.js"],
      "discoveryTimeoutMs": 60000,
    },
    "flaky-remote": {
      "httpUrl": "https://example.com/mcp",
      "discoveryTimeoutMs": 10000,
    },
  },
}
```

The existing `timeout` field is **tool-call** timeout (used for each
`tools/call` request, default 10 minutes) and is unaffected by
`discoveryTimeoutMs` — a long-running tool invocation is not a startup
pathology.

### Rolling back progressive MCP

If you need the old synchronous behavior (cli waits for every MCP server
before showing any UI), set `QWEN_CODE_LEGACY_MCP_BLOCKING=1` in your
environment. This is kept as an escape hatch for at least one release.

## Safety and control

### Trust (skip confirmations)

- **Server trust** (`trust: true`): bypasses confirmation prompts for that server (use sparingly).

### OAuth authentication

Qwen Code supports OAuth 2.0 authentication for MCP servers. This is useful when accessing remote servers that require authentication.

#### Basic usage

When you add an MCP server with OAuth credentials, Qwen Code will automatically handle the authentication flow:

```bash
qwen mcp add --transport sse oauth-server https://api.example.com/sse/ \
  --oauth-client-id your-client-id \
  --oauth-redirect-uri https://your-server.com/oauth/callback \
  --oauth-authorization-url https://provider.example.com/authorize \
  --oauth-token-url https://provider.example.com/token
```

#### Important: Redirect URI configuration

The OAuth flow requires a redirect URI where the authorization provider sends the authentication code.

- **Local development**: By default, Qwen Code uses `http://localhost:7777/oauth/callback`. This works when running Qwen Code on your local machine with a local browser.

- **Remote/cloud deployments**: When running Qwen Code on remote servers, cloud IDEs, or web terminals, the default `localhost` redirect will NOT work. Configure `--oauth-redirect-uri` with a public URL ending in `/oauth/callback`, then reverse-proxy that path to `http://127.0.0.1:7777/oauth/callback` on the machine running Qwen Code. Qwen Code does not terminate TLS; the proxy must do so.

Example for remote servers:

```bash
qwen mcp add --transport sse remote-server https://api.example.com/sse/ \
  --oauth-redirect-uri https://your-remote-server.example.com/oauth/callback
```

For example, a reverse proxy can forward only this callback path to the local listener:

```nginx
location = /oauth/callback {
  proxy_pass http://127.0.0.1:7777;
}
```

#### Manual configuration via settings.json

You can also configure OAuth by editing `settings.json` directly:

```json
{
  "mcpServers": {
    "oauthServer": {
      "url": "https://api.example.com/sse/",
      "oauth": {
        "enabled": true,
        "clientId": "your-client-id",
        "clientSecret": "your-client-secret",
        "authorizationUrl": "https://provider.example.com/authorize",
        "tokenUrl": "https://provider.example.com/token",
        "redirectUri": "https://your-server.com/oauth/callback",
        "scopes": ["read", "write"]
      }
    }
  }
}
```

OAuth configuration properties:

| Property           | Description                                                                                                           |
| ------------------ | --------------------------------------------------------------------------------------------------------------------- |
| `enabled`          | Enable OAuth for this server (boolean)                                                                                |
| `clientId`         | OAuth client identifier (string, optional with dynamic registration)                                                  |
| `clientSecret`     | OAuth client secret (string, optional for public clients)                                                             |
| `authorizationUrl` | OAuth authorization endpoint (string, auto-discovered if omitted)                                                     |
| `tokenUrl`         | OAuth token endpoint (string, auto-discovered if omitted)                                                             |
| `scopes`           | Required OAuth scopes (array of strings)                                                                              |
| `redirectUri`      | Custom redirect URI (string). **Critical for remote deployments**. Defaults to `http://localhost:7777/oauth/callback` |
| `tokenParamName`   | Query parameter name for tokens in SSE URLs (string)                                                                  |
| `audiences`        | Audiences the token is valid for (array of strings)                                                                   |

#### Token management

OAuth tokens are automatically:

- **Stored** in `~/.qwen/mcp-oauth-tokens.json` (plaintext, mode 0600) by default. If `QWEN_CODE_FORCE_ENCRYPTED_FILE_STORAGE=true` is set, Qwen Code uses keychain-backed storage where available, or `~/.qwen/mcp-oauth-tokens-v2.json` with AES-256-GCM encryption.
- **Refreshed** when expired (if refresh tokens are available)
- **Validated** before each connection attempt

> [!WARNING]
> By default, OAuth tokens are stored unencrypted on disk. On shared or multi-user machines, set `QWEN_CODE_FORCE_ENCRYPTED_FILE_STORAGE=true` to protect credentials.

Use the `/mcp` dialog within Qwen Code to inspect MCP servers and manage
authentication interactively.

### Tool filtering (allow/deny tools per server)

Use `includeTools` / `excludeTools` to restrict tools exposed by a server (from Qwen Code’s perspective).

Example: include only a few tools:

```json
{
  "mcpServers": {
    "filteredServer": {
      "command": "python",
      "args": ["-m", "my_mcp_server"],
      "includeTools": ["safe_tool", "file_reader", "data_processor"],
      "timeout": 30000
    }
  }
}
```

### Global allow/deny lists

The `mcp` object in your `settings.json` defines global rules for all MCP servers:

- `mcp.allowed`: allow-list of MCP server names (keys in `mcpServers`)
- `mcp.excluded`: deny-list of MCP server names

Both lists support glob patterns: `*` matches any sequence of characters and `?` matches a single character (for example, `"*puppeteer*"` matches every server whose name contains `puppeteer`). Entries without glob characters are matched exactly. When a server matches both lists, `mcp.excluded` takes precedence.

Example:

```json
{
  "mcp": {
    "allowed": ["my-trusted-server", "*-internal"],
    "excluded": ["experimental-server"]
  }
}
```

## Troubleshooting

- **Server shows “Disconnected” in `qwen mcp list`**: verify the URL/command is correct, then increase `timeout`.
- **Stdio server fails to start**: use an absolute `command` path, and double-check `cwd`/`env`.
- **Environment variables in JSON don’t resolve**: ensure they exist in the environment where Qwen Code runs (shell vs GUI app environments can differ).

## Reference

### `settings.json` structure

#### Server-specific configuration (`mcpServers`)

Add an `mcpServers` object to your `settings.json` file:

```json
// ... file contains other config objects
{
  "mcpServers": {
    "serverName": {
      "command": "path/to/server",
      "args": ["--arg1", "value1"],
      "env": {
        "API_KEY": "$MY_API_TOKEN"
      },
      "cwd": "./server-directory",
      "timeout": 30000,
      "trust": false
    }
  }
}
```

Configuration properties:

Required (one of the following):

| Property  | Description                                            |
| --------- | ------------------------------------------------------ |
| `command` | Path to the executable for Stdio transport             |
| `url`     | SSE endpoint URL (e.g., `"http://localhost:8080/sse"`) |
| `httpUrl` | HTTP streaming endpoint URL                            |

Optional:

| Property               | Type/Default                 | Description                                                                                                                                                                                                                                                       |
| ---------------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `args`                 | array                        | Command-line arguments for Stdio transport                                                                                                                                                                                                                        |
| `headers`              | object                       | Custom HTTP headers when using `url` or `httpUrl`                                                                                                                                                                                                                 |
| `env`                  | object                       | Environment variables for the server process. Values can reference environment variables using `$VAR_NAME` or `${VAR_NAME}` syntax                                                                                                                                |
| `cwd`                  | string                       | Working directory for Stdio transport                                                                                                                                                                                                                             |
| `timeout`              | number<br>(default: 600,000) | Request timeout in milliseconds (default: 600,000ms = 10 minutes)                                                                                                                                                                                                 |
| `trust`                | boolean<br>(default: false)  | When `true`, bypasses all tool call confirmations for this server (default: `false`)                                                                                                                                                                              |
| `includeTools`         | array                        | List of tool names to include from this MCP server. When specified, only the tools listed here will be available from this server (allowlist behavior). If not specified, all tools from the server are enabled by default.                                       |
| `excludeTools`         | array                        | List of tool names to exclude from this MCP server. Tools listed here will not be available to the model, even if they are exposed by the server.<br>Note: `excludeTools` takes precedence over `includeTools` - if a tool is in both lists, it will be excluded. |
| `targetAudience`       | string                       | The OAuth Client ID allowlisted on the IAP-protected application you are trying to access. Used with `authProviderType: 'service_account_impersonation'`.                                                                                                         |
| `targetServiceAccount` | string                       | The email address of the Google Cloud Service Account to impersonate. Used with `authProviderType: 'service_account_impersonation'`.                                                                                                                              |

<a id="qwen-mcp-cli"></a>

### Manage MCP servers with `qwen mcp`

You can always configure MCP servers by manually editing `settings.json`, but the CLI is usually faster.

#### Adding a server (`qwen mcp add`)

```bash
qwen mcp add [options] <name> <commandOrUrl> [args...]
```

| Argument/Option             | Description                                                         | Default                                | Example                                                            |
| --------------------------- | ------------------------------------------------------------------- | -------------------------------------- | ------------------------------------------------------------------ |
| `<name>`                    | A unique name for the server.                                       | —                                      | `example-server`                                                   |
| `<commandOrUrl>`            | The command to execute (for `stdio`) or the URL (for `http`/`sse`). | —                                      | `/usr/bin/python` or `http://localhost:8`                          |
| `[args...]`                 | Optional arguments for a `stdio` command.                           | —                                      | `--port 5000`                                                      |
| `-s`, `--scope`             | Configuration scope (user or project).                              | `user`                                 | `-s user`                                                          |
| `-t`, `--transport`         | Transport type (`stdio`, `sse`, `http`).                            | `stdio`                                | `-t sse`                                                           |
| `-e`, `--env`               | Set environment variables.                                          | —                                      | `-e KEY=value`                                                     |
| `-H`, `--header`            | Set HTTP headers for SSE and HTTP transports.                       | —                                      | `-H "X-Api-Key: abc123"`                                           |
| `--timeout`                 | Set connection timeout in milliseconds.                             | —                                      | `--timeout 30000`                                                  |
| `--trust`                   | Trust the server (bypass all tool call confirmation prompts).       | — (`false`)                            | `--trust`                                                          |
| `--description`             | Set the description for the server.                                 | —                                      | `--description "Local tools"`                                      |
| `--include-tools`           | A comma-separated list of tools to include.                         | all tools included                     | `--include-tools mytool,othertool`                                 |
| `--exclude-tools`           | A comma-separated list of tools to exclude.                         | none                                   | `--exclude-tools mytool`                                           |
| `--oauth-client-id`         | OAuth client ID for MCP server authentication.                      | —                                      | `--oauth-client-id your-client-id`                                 |
| `--oauth-client-secret`     | OAuth client secret for MCP server authentication.                  | —                                      | `--oauth-client-secret your-client-secret`                         |
| `--oauth-redirect-uri`      | OAuth redirect URI for authentication callback.                     | `http://localhost:7777/oauth/callback` | `--oauth-redirect-uri https://your-server.com/oauth/callback`      |
| `--oauth-authorization-url` | OAuth authorization URL.                                            | —                                      | `--oauth-authorization-url https://provider.example.com/authorize` |
| `--oauth-token-url`         | OAuth token URL.                                                    | —                                      | `--oauth-token-url https://provider.example.com/token`             |
| `--oauth-scopes`            | OAuth scopes (comma-separated).                                     | —                                      | `--oauth-scopes scope1,scope2`                                     |

> `--oauth-*` flags apply only to `--transport sse` and `--transport http`. Combining them with `--transport stdio` is rejected.

#### Removing a server (`qwen mcp remove`)

```bash
qwen mcp remove <name>
```
