Skip to main content
The SDK provides built-in support for the Model Context Protocol (MCP), allowing you to create custom tools that extend the AI’s capabilities.

What is MCP?

The Model Context Protocol is an open standard for connecting AI models to external tools and data sources. It enables:
  • Custom Tools: Create application-specific functionality
  • External APIs: Connect to third-party services
  • Data Sources: Access databases, file systems, and more
  • Process Isolation: Run tools in separate processes or in-process

MCP Server Types

The SDK supports two types of MCP servers: Run in the same process as your application. No separate server process needed. Benefits:
  • Simple setup - no process management
  • Direct access to your application’s state
  • Type-safe with Zod schema validation
  • Lower latency - no IPC overhead
Use cases:
  • Application-specific tools
  • Tools that need access to app state
  • Simple utility functions
import { z } from 'zod';
import { tool, createSdkMcpServer, query } from '@qwen-code/sdk';

const weatherTool = tool(
  'get_weather',
  'Get current weather for a city',
  { city: z.string() },
  async (args) => ({
    content: [{ type: 'text', text: `Weather in ${args.city}: Sunny, 72°F` }],
  })
);

const server = createSdkMcpServer({
  name: 'weather',
  tools: [weatherTool],
});

const result = query({
  prompt: 'What is the weather in San Francisco?',
  options: {
    mcpServers: { weather: server },
  },
});

External Servers

Run as separate processes, connected via stdio, SSE, HTTP, or WebSocket. Benefits:
  • Process isolation and security
  • Language agnostic - write servers in any language
  • Share servers across multiple SDK instances
  • Standard MCP protocol compliance
Use cases:
  • Complex server implementations
  • Shared services across applications
  • Third-party MCP servers
  • Security-sensitive operations
import { query } from '@qwen-code/sdk';

const result = query({
  prompt: 'Use the database tool',
  options: {
    mcpServers: {
      database: {
        command: 'node',
        args: ['./mcp-servers/database-server.js'],
        env: { DB_CONNECTION: 'postgresql://...' },
      },
    },
  },
});

Quick Start

Create your first SDK-embedded MCP server in 3 steps:

Step 1: Install Dependencies

npm install @qwen-code/sdk zod

Step 2: Create a Tool

example.ts
import { z } from 'zod';
import { tool, createSdkMcpServer, query } from '@qwen-code/sdk';

// Define a calculator tool
const calculatorTool = tool(
  'calculate_sum',
  'Add two numbers together',
  {
    a: z.number().describe('First number'),
    b: z.number().describe('Second number'),
  },
  async (args) => {
    const result = args.a + args.b;
    return {
      content: [{
        type: 'text',
        text: `${args.a} + ${args.b} = ${result}`,
      }],
    };
  }
);

// Create MCP server
const server = createSdkMcpServer({
  name: 'calculator',
  version: '1.0.0',
  tools: [calculatorTool],
});

// Use in query
const result = query({
  prompt: 'What is 42 + 17?',
  options: {
    permissionMode: 'yolo',
    mcpServers: {
      calculator: server,
    },
  },
});

for await (const message of result) {
  if (message.type === 'assistant') {
    console.log(message.message.content);
  }
}

Step 3: Run It

npx tsx example.ts

Architecture

SDK-Embedded Servers

┌─────────────────────────────────┐
│         Your Application           │
│  ┌────────────────────────────┐  │
│  │  SDK Query Instance      │  │
│  │                            │  │
│  │  ┌─────────────────────┐  │  │
│  │  │ SDK MCP Server     │  │  │
│  │  │ (In-process)       │  │  │
│  │  │ - Tool handlers    │  │  │
│  │  │ - Zod validation   │  │  │
│  │  └─────────────────────┘  │  │
│  └────────────────────────────┘  │
└─────────────────────────────────┘

         │ JSON Protocol

┌─────────────────────────────────┐
│       Qwen CLI Process          │
└─────────────────────────────────┘

External Servers

┌─────────────────────────────────┐
│         Your Application           │
│  ┌────────────────────────────┐  │
│  │  SDK Query Instance      │  │
│  └────────────────────────────┘  │
└─────────────────────────────────┘

         │ JSON Protocol

┌─────────────────────────────────┐
│       Qwen CLI Process          │
└─────────────────────────────────┘

         │ MCP Protocol
         │ (stdio/SSE/HTTP)

┌─────────────────────────────────┐
│   External MCP Server          │
│   (Separate Process)           │
└─────────────────────────────────┘

Communication Flow

When the AI calls an MCP tool:
  1. CLI requests tool execution: AI decides to use a tool and sends request to CLI
  2. CLI routes to SDK: CLI identifies SDK-embedded server and sends control request
  3. SDK invokes handler: SDK validates input and calls your tool handler
  4. Handler returns result: Your function returns MCP-formatted result
  5. SDK sends response: Result is sent back to CLI as control response
  6. CLI forwards to AI: Tool result is included in the AI’s context

Tool Result Format

All tool handlers must return a CallToolResult object:
type CallToolResult = {
  content: Array<
    | { type: 'text'; text: string }
    | { type: 'image'; data: string; mimeType: string }
    | { type: 'resource'; uri: string; mimeType?: string; text?: string }
  >;
  isError?: boolean;
};

Text Response

return {
  content: [{ type: 'text', text: 'Operation successful' }],
};

Image Response

return {
  content: [{
    type: 'image',
    data: base64ImageData,
    mimeType: 'image/png',
  }],
};

Multiple Content Blocks

return {
  content: [
    { type: 'text', text: 'Here is the chart:' },
    { type: 'image', data: chartData, mimeType: 'image/png' },
  ],
};

Error Response

return {
  content: [{ type: 'text', text: 'Failed to process request' }],
  isError: true,
};

Configuration Options

When connecting MCP servers to a query:
import { query, createSdkMcpServer } from '@qwen-code/sdk';

const result = query({
  prompt: 'Use my tools',
  options: {
    mcpServers: {
      // SDK-embedded server
      'my-server': createSdkMcpServer({
        name: 'my-server',
        tools: [/* ... */],
      }),
      
      // External server (stdio)
      'external-stdio': {
        command: 'node',
        args: ['server.js'],
        env: { API_KEY: 'xxx' },
      },
      
      // External server (SSE)
      'external-sse': {
        url: 'http://localhost:3000/sse',
      },
      
      // External server (HTTP)
      'external-http': {
        httpUrl: 'http://localhost:3000/mcp',
        headers: { 'Authorization': 'Bearer token' },
      },
    },
  },
});

Timeout Configuration

Configure timeouts for MCP operations:
options: {
  mcpServers: { /* ... */ },
  timeout: {
    mcpRequest: 600000, // 10 minutes for long-running tools
  },
}

Best Practices

1. Use SDK-Embedded Servers for Simple Tools

const simpleTool = tool(
  'get_time',
  'Get current time',
  {},
  async () => ({
    content: [{ type: 'text', text: new Date().toISOString() }],
  })
);

2. Use External Servers for Complex Operations

options: {
  mcpServers: {
    database: {
      command: 'node',
      args: ['database-server.js'],
    },
  },
}

3. Validate Input with Zod

const tool = tool(
  'create_user',
  'Create a new user',
  {
    email: z.string().email(),
    age: z.number().min(0).max(120),
  },
  async (args) => {
    // args.email is validated email
    // args.age is validated number
  }
);

4. Handle Errors Gracefully

const tool = tool(
  'risky_operation',
  'Perform a risky operation',
  { input: z.string() },
  async (args) => {
    try {
      const result = await performOperation(args.input);
      return {
        content: [{ type: 'text', text: result }],
      };
    } catch (error) {
      return {
        content: [{
          type: 'text',
          text: `Error: ${error.message}`,
        }],
        isError: true,
      };
    }
  }
);

5. Provide Clear Descriptions

const tool = tool(
  'search_docs',
  'Search technical documentation using semantic search. Returns relevant excerpts.',
  {
    query: z.string().describe('Search query (natural language)'),
    limit: z.number().default(5).describe('Maximum number of results'),
  },
  async (args) => { /* ... */ }
);

Next Steps

tool() Function

Learn how to create tools

createSdkMcpServer()

Create MCP server instances

MCP Examples

Complete working examples

External MCP Servers

Connect to external servers