Documentation Index
Fetch the complete documentation index at: https://mintlify.com/QwenLM/qwen-code/llms.txt
Use this file to discover all available pages before exploring further.
MCP Server Tools
Model Context Protocol (MCP) servers extend Qwen Code’s capabilities by providing access to external tools, APIs, and data sources.
Overview
MCP servers act as bridges between Qwen Code and external systems, allowing the AI to:
- Discover tools: List available tools with schemas
- Execute tools: Call tools with structured parameters
- Access resources: Read data from external sources
- Extend functionality: Add custom capabilities without modifying Qwen Code
Architecture
Integration Flow
Qwen Code
│
v
┌──────────────────────────┐
│ MCP Client │
│ (mcp-client.ts) │
│ │
│ 1. Discover servers │
│ 2. Connect via transport │
│ 3. Fetch tool schemas │
│ 4. Register in registry │
└──────────┬───────────────┘
│
v
┌──────────────────────────┐
│ Tool Registry │
│ │
│ - Built-in tools │
│ - MCP tools (dynamic) │
└──────────┬───────────────┘
│
v
┌──────────────────────────┐
│ MCP Tool Wrapper │
│ (mcp-tool.ts) │
│ │
│ - Confirmation logic │
│ - Parameter validation │
│ - Execution & response │
└──────────┬───────────────┘
│
v
┌──────────────────────────┐
│ MCP Server │
│ (External Process) │
│ │
│ - Custom tools │
│ - API integrations │
│ - Database access │
└──────────────────────────┘
Transport Mechanisms
Qwen Code supports three transport types:
1. Stdio Transport
Communicates via standard input/output:
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["path/to/server.js"],
"transport": "stdio"
}
}
}
Use for:
- Local scripts
- Command-line tools
- Simple integrations
2. SSE Transport
Server-Sent Events over HTTP:
{
"mcpServers": {
"remote-server": {
"url": "https://api.example.com/mcp",
"transport": "sse",
"headers": {
"Authorization": "Bearer $API_TOKEN"
}
}
}
}
Use for:
- Remote services
- Cloud APIs
- Webhooks
3. Streamable HTTP
HTTP streaming:
{
"mcpServers": {
"http-server": {
"url": "https://api.example.com/mcp",
"transport": "streamable-http"
}
}
}
Use for:
- RESTful APIs
- Modern web services
Configuration
Basic Setup
Add to settings.json:
{
"mcpServers": {
"serverName": {
"command": "path/to/server",
"args": ["--arg1", "value1"],
"env": {
"API_KEY": "$MY_API_TOKEN"
},
"cwd": "./server-directory",
"timeout": 30000,
"trust": false
}
}
}
Configuration Properties
interface MCPServerConfig {
// Execution
command?: string; // Command to run server
args?: string[]; // Command arguments
env?: Record<string, string>; // Environment variables
cwd?: string; // Working directory
// Connection
url?: string; // For SSE/HTTP transport
transport?: 'stdio' | 'sse' | 'streamable-http';
headers?: Record<string, string>; // HTTP headers
// Behavior
timeout?: number; // Timeout in ms
trust?: boolean; // Skip confirmation
disabled?: boolean; // Disable this server
}
Environment Variables
Use $VAR_NAME syntax for environment variables:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "$GITHUB_PERSONAL_ACCESS_TOKEN"
}
}
}
}
Variables are resolved from:
- Process environment
- User’s shell environment
.env files (if supported)
Global MCP Settings
{
"mcp": {
"allowed": ["trusted-server"], // Allowlist
"excluded": ["experimental-server"], // Blocklist
"serverCommand": "node" // Default command
}
}
Discovery Process
// packages/core/src/tools/mcp-client.ts
export async function discoverMcpTools(
config: Config,
): Promise<void> {
const serverConfigs = config.getMcpServers();
for (const [name, serverConfig] of Object.entries(serverConfigs)) {
// 1. Create transport
const transport = createTransport(serverConfig);
// 2. Connect to server
const client = new Client({ name, version: '1.0' });
await client.connect(transport);
// 3. List available tools
const toolsList = await client.listTools();
// 4. Register each tool
for (const toolDef of toolsList.tools) {
const mcpTool = new DiscoveredMCPTool(
toolDef,
name,
serverConfig.trust,
);
config.getToolRegistry().registerTool(mcpTool);
}
}
}
MCP tools are registered with unique names:
Original: get_user
Registered: mcp_github_get_user
Original: list_repos
Registered: mcp_github_list_repos
Naming pattern: mcp_{serverName}_{toolName}
This prevents conflicts between:
- Built-in tools
- Different MCP servers
- Tools with same names
Execution Flow
// User/Model requests tool
mcp_github_get_user({ username: "octocat" });
// 1. Tool validation
const tool = registry.getTool("mcp_github_get_user");
// 2. Confirmation (if needed)
if (requiresConfirmation) {
await confirmWithUser();
}
// 3. Call MCP server
const result = await mcpClient.callTool({
name: "get_user",
arguments: { username: "octocat" },
});
// 4. Process response
return {
llmContent: result.content,
returnDisplay: formatForUI(result),
};
Confirmation
MCP tools require confirmation unless:
-
Server is trusted:
{
"mcpServers": {
"my-server": {
"trust": true // Skip confirmations
}
}
}
-
Tool is read-only:
// MCP tool annotations
{
readOnlyHint: true, // Safe, no confirmation
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
}
-
User allowlisted it:
- Choose “Always allow” in confirmation dialog
- Applies to current session
MCP tools can provide hints about their behavior:
interface McpToolAnnotations {
readOnlyHint?: boolean; // Safe read operation
destructiveHint?: boolean; // Modifies or deletes data
idempotentHint?: boolean; // Same result on repeated calls
openWorldHint?: boolean; // Accesses external network
}
These help Qwen Code make informed decisions about confirmation and execution.
Examples
GitHub Server
{
"mcpServers": {
"github": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-github"
],
"env": {
"GITHUB_TOKEN": "$GITHUB_PERSONAL_ACCESS_TOKEN"
}
}
}
}
Available Tools:
mcp_github_create_or_update_file
mcp_github_search_repositories
mcp_github_create_repository
mcp_github_get_file_contents
mcp_github_push_files
mcp_github_create_issue
mcp_github_create_pull_request
mcp_github_fork_repository
mcp_github_create_branch
Filesystem Server
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/allowed/path"
]
}
}
}
Available Tools:
mcp_filesystem_read_file
mcp_filesystem_read_multiple_files
mcp_filesystem_write_file
mcp_filesystem_create_directory
mcp_filesystem_list_directory
mcp_filesystem_move_file
mcp_filesystem_search_files
mcp_filesystem_get_file_info
Database Server
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres"
],
"env": {
"POSTGRES_CONNECTION_STRING": "$DATABASE_URL"
}
}
}
}
Available Tools:
mcp_postgres_query
mcp_postgres_list_tables
mcp_postgres_describe_table
mcp_postgres_insert
mcp_postgres_update
Custom Server
{
"mcpServers": {
"my-custom-server": {
"command": "node",
"args": ["./mcp-servers/custom.js"],
"cwd": "./",
"timeout": 60000,
"trust": false
}
}
}
Creating MCP Servers
Server Structure
MCP servers follow a standard protocol:
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const server = new Server(
{
name: 'my-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
},
);
// Register tools
server.setRequestHandler('tools/list', async () => ({
tools: [
{
name: 'my_tool',
description: 'Does something useful',
inputSchema: {
type: 'object',
properties: {
param: {
type: 'string',
description: 'A parameter',
},
},
required: ['param'],
},
},
],
}));
server.setRequestHandler('tools/call', async (request) => {
const { name, arguments: args } = request.params;
if (name === 'my_tool') {
const result = await doSomething(args.param);
return {
content: [
{
type: 'text',
text: `Result: ${result}`,
},
],
};
}
throw new Error(`Unknown tool: ${name}`);
});
// Start server
const transport = new StdioServerTransport();
await server.connect(transport);
Define tools with JSON Schema:
{
name: 'search_code',
description: 'Search for code patterns in the repository',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Search query (regex supported)',
},
file_pattern: {
type: 'string',
description: 'File pattern to search in (glob)',
},
case_sensitive: {
type: 'boolean',
description: 'Whether search is case-sensitive',
default: false,
},
},
required: ['query'],
},
annotations: {
readOnlyHint: true, // Safe read operation
},
}
Return results in MCP format:
return {
content: [
// Text content
{
type: 'text',
text: 'Search found 5 results',
},
// Image content
{
type: 'image',
data: base64Data,
mimeType: 'image/png',
},
// Resource link
{
type: 'resource',
resource: {
uri: 'file:///path/to/file',
text: 'Content...',
},
},
],
isError: false,
};
Security
Trust Levels
Untrusted (default):
- Requires confirmation for each tool use
- Shows tool name and parameters
- User can allowlist specific tools
Trusted:
- No confirmation required
- Use only for verified servers
- Set
trust: true in config
Best Practices
-
Review Server Code:
- Inspect server implementation
- Verify it’s from trusted source
- Check for suspicious behavior
-
Limit Permissions:
- Give minimum necessary access
- Use environment variables for secrets
- Don’t store secrets in config
-
Use Allowlists:
{
"mcp": {
"allowed": ["github", "filesystem"]
}
}
-
Monitor Activity:
- Review confirmation prompts
- Check logs for suspicious calls
- Disable unused servers
-
Sandbox if Possible:
export QWEN_SANDBOX=docker
Environment Variable Security
Store sensitive data in environment:
# .env (never commit this)
export GITHUB_TOKEN="ghp_..."
export DATABASE_URL="postgresql://..."
export API_KEY="sk-..."
Reference in config:
{
"env": {
"GITHUB_TOKEN": "$GITHUB_TOKEN",
"API_KEY": "$API_KEY"
}
}
Troubleshooting
Server Not Found
Error: MCP server "my-server" not found
Solutions:
- Check server name in config
- Verify command is in PATH
- Use absolute path:
/usr/local/bin/server
- Check
disabled: false
Connection Timeout
Error: Connection to MCP server timed out
Solutions:
- Increase timeout:
"timeout": 60000
- Check server starts quickly
- Verify network connectivity (for remote servers)
- Check server logs
Error: Tool "mcp_server_tool" not found
Solutions:
- Restart Qwen Code (tools discovered on startup)
- Check server is enabled
- Verify server implements tool
- Check for name conflicts
Permission Denied
Error: Permission denied: /path/to/server
Solutions:
- Make server executable:
chmod +x /path/to/server
- Check file permissions
- Run with correct user
Implementation
Key Files:
packages/core/src/tools/mcp-client.ts - Discovery and client
packages/core/src/tools/mcp-tool.ts - Tool wrapper
packages/core/src/tools/mcp-client-manager.ts - Connection management
Client Manager
export class McpClientManager {
private clients = new Map<string, McpDirectClient>();
async getOrCreateClient(
serverName: string,
config: MCPServerConfig,
): Promise<McpDirectClient> {
if (this.clients.has(serverName)) {
return this.clients.get(serverName)!;
}
const transport = createTransport(config);
const client = new Client({ name: serverName, version: '1.0' });
await client.connect(transport);
this.clients.set(serverName, client);
return client;
}
}
Next Steps