Skip to main content
The createSdkMcpServer() function creates an MCP server instance that runs in the same process as your SDK application.

Import

import { createSdkMcpServer } from '@qwen-code/sdk';
import type { CreateSdkMcpServerOptions, McpSdkServerConfigWithInstance } from '@qwen-code/sdk';

Signature

function createSdkMcpServer(
  options: CreateSdkMcpServerOptions
): McpSdkServerConfigWithInstance

Parameters

options
CreateSdkMcpServerOptions
required
Configuration options for the MCP server.

CreateSdkMcpServerOptions

name
string
required
Unique name for the MCP server. Used to identify the server in logs and error messages.
createSdkMcpServer({
  name: 'calculator',
  // ...
})
version
string
default:"'1.0.0'"
Server version string. Used for compatibility and debugging.
createSdkMcpServer({
  name: 'my-server',
  version: '2.1.0',
  // ...
})
tools
SdkMcpToolDefinition[]
Array of tools created with the tool() function.
createSdkMcpServer({
  name: 'utilities',
  tools: [timeTool, weatherTool, calculatorTool],
})

Return Value

McpSdkServerConfigWithInstance
object
A server configuration object that can be passed directly to the mcpServers option in query().
{
  type: 'sdk';
  name: string;
  instance: McpServer;
}

Examples

Basic Server with One Tool

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

const addTool = tool(
  'add',
  'Add two numbers',
  { a: z.number(), b: z.number() },
  async (args) => ({
    content: [{ type: 'text', text: String(args.a + args.b) }],
  })
);

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

const result = query({
  prompt: 'What is 5 + 3?',
  options: {
    permissionMode: 'yolo',
    mcpServers: { calculator: server },
  },
});

Server with Multiple Tools

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

const addTool = tool(
  'add',
  'Add two numbers',
  { a: z.number(), b: z.number() },
  async (args) => ({
    content: [{ type: 'text', text: String(args.a + args.b) }],
  })
);

const subtractTool = tool(
  'subtract',
  'Subtract two numbers',
  { a: z.number(), b: z.number() },
  async (args) => ({
    content: [{ type: 'text', text: String(args.a - args.b) }],
  })
);

const multiplyTool = tool(
  'multiply',
  'Multiply two numbers',
  { a: z.number(), b: z.number() },
  async (args) => ({
    content: [{ type: 'text', text: String(args.a * args.b) }],
  })
);

const server = createSdkMcpServer({
  name: 'calculator',
  version: '1.0.0',
  tools: [addTool, subtractTool, multiplyTool],
});

const result = query({
  prompt: 'Calculate (5 + 3) * 2',
  options: {
    permissionMode: 'yolo',
    mcpServers: { calculator: server },
  },
});

Multiple Servers

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

// Calculator server
const calculatorServer = createSdkMcpServer({
  name: 'calculator',
  tools: [
    tool('add', 'Add numbers', {
      a: z.number(), b: z.number()
    }, async (args) => ({
      content: [{ type: 'text', text: String(args.a + args.b) }],
    })),
  ],
});

// Weather server
const weatherServer = createSdkMcpServer({
  name: 'weather',
  tools: [
    tool('get_weather', 'Get weather for a city', {
      city: z.string()
    }, async (args) => ({
      content: [{ type: 'text', text: `Weather in ${args.city}: Sunny` }],
    })),
  ],
});

// Time server
const timeServer = createSdkMcpServer({
  name: 'time',
  tools: [
    tool('get_time', 'Get current time', {}, async () => ({
      content: [{ type: 'text', text: new Date().toISOString() }],
    })),
  ],
});

const result = query({
  prompt: 'What time is it and what is the weather in Paris?',
  options: {
    permissionMode: 'yolo',
    mcpServers: {
      calculator: calculatorServer,
      weather: weatherServer,
      time: timeServer,
    },
  },
});

Server with Stateful Tools

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

// State shared between tools
const storage = new Map<string, string>();

const setValueTool = tool(
  'set_value',
  'Store a key-value pair',
  { key: z.string(), value: z.string() },
  async (args) => {
    storage.set(args.key, args.value);
    return {
      content: [{ type: 'text', text: `Stored: ${args.key} = ${args.value}` }],
    };
  }
);

const getValueTool = tool(
  'get_value',
  'Retrieve a value by key',
  { key: z.string() },
  async (args) => {
    const value = storage.get(args.key);
    if (value === undefined) {
      return {
        content: [{ type: 'text', text: `Key not found: ${args.key}` }],
        isError: true,
      };
    }
    return {
      content: [{ type: 'text', text: value }],
    };
  }
);

const storageServer = createSdkMcpServer({
  name: 'storage',
  tools: [setValueTool, getValueTool],
});

const result = query({
  prompt: 'Store my name as John, then retrieve it',
  options: {
    permissionMode: 'yolo',
    mcpServers: { storage: storageServer },
  },
});

Server with Async Operations

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

const fetchTool = tool(
  'fetch_url',
  'Fetch content from a URL',
  { url: z.string().url() },
  async (args) => {
    try {
      const response = await fetch(args.url);
      const content = await response.text();
      
      return {
        content: [{
          type: 'text',
          text: `Content from ${args.url}:\n${content.slice(0, 500)}...`,
        }],
      };
    } catch (error) {
      return {
        content: [{ type: 'text', text: `Failed to fetch: ${error.message}` }],
        isError: true,
      };
    }
  }
);

const httpServer = createSdkMcpServer({
  name: 'http',
  tools: [fetchTool],
});

Server with Environment Access

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

const apiKeyTool = tool(
  'check_api_key',
  'Check if API key is configured',
  { service: z.string() },
  async (args) => {
    const envVar = `${args.service.toUpperCase()}_API_KEY`;
    const hasKey = !!process.env[envVar];
    
    return {
      content: [{
        type: 'text',
        text: hasKey
          ? `API key for ${args.service} is configured`
          : `API key for ${args.service} is NOT configured`,
      }],
    };
  }
);

const envServer = createSdkMcpServer({
  name: 'environment',
  tools: [apiKeyTool],
});

Validation

Tool Name Validation

The function validates that all tools have unique names:
import { tool, createSdkMcpServer } from '@qwen-code/sdk';

const tool1 = tool('duplicate', 'First tool', {}, async () => ({
  content: [{ type: 'text', text: 'First' }],
}));

const tool2 = tool('duplicate', 'Second tool', {}, async () => ({
  content: [{ type: 'text', text: 'Second' }],
}));

// This will throw an error:
// "Duplicate tool name 'duplicate' in MCP server 'my-server'"
const server = createSdkMcpServer({
  name: 'my-server',
  tools: [tool1, tool2],
});

Server Name Validation

Server name must be a non-empty string:
// ✅ Valid
createS dkMcpServer({ name: 'my-server', tools: [] })
createSdkMcpServer({ name: 'calculator-v2', tools: [] })

// ❌ Invalid - throws error
createS dkMcpServer({ name: '', tools: [] })
createSdkMcpServer({ name: null, tools: [] })

Error Handling

Creation Errors

try {
  const server = createSdkMcpServer({
    name: 'my-server',
    tools: [/* invalid tools */],
  });
} catch (error) {
  console.error('Failed to create server:', error.message);
}

Runtime Tool Errors

Tool errors are caught and returned to the AI:
const errorTool = tool(
  'risky_operation',
  'Perform a risky operation',
  { input: z.string() },
  async (args) => {
    // If this throws, it's caught and returned as an error
    const result = await riskyOperation(args.input);
    return { content: [{ type: 'text', text: result }] };
  }
);

const server = createSdkMcpServer({
  name: 'risky',
  tools: [errorTool],
});
Better approach - handle errors explicitly:
const errorTool = tool(
  'risky_operation',
  'Perform a risky operation',
  { input: z.string() },
  async (args) => {
    try {
      const result = await riskyOperation(args.input);
      return { content: [{ type: 'text', text: result }] };
    } catch (error) {
      return {
        content: [{ type: 'text', text: `Error: ${error.message}` }],
        isError: true,
      };
    }
  }
);

Server Capabilities

The MCP server automatically advertises its capabilities based on provided tools:
const server = createSdkMcpServer({
  name: 'my-server',
  tools: [tool1, tool2, tool3],
});

// Server instance has:
// - capabilities.tools = {} (indicates tool support)
// - Registered tool handlers for tool1, tool2, tool3

Usage in Queries

Direct Usage

const server = createSdkMcpServer({
  name: 'utilities',
  tools: [/* ... */],
});

const result = query({
  prompt: 'Use the utility tools',
  options: {
    mcpServers: {
      utilities: server,  // Direct reference
    },
  },
});

Reuse Across Queries

const sharedServer = createSdkMcpServer({
  name: 'shared',
  tools: [/* ... */],
});

// Use in multiple queries
const query1 = query({
  prompt: 'First task',
  options: { mcpServers: { shared: sharedServer } },
});

const query2 = query({
  prompt: 'Second task',
  options: { mcpServers: { shared: sharedServer } },
});

Mixed with External Servers

const sdkServer = createSdkMcpServer({
  name: 'sdk-tools',
  tools: [/* SDK tools */],
});

const result = query({
  prompt: 'Use both SDK and external tools',
  options: {
    mcpServers: {
      // SDK-embedded server
      'sdk-tools': sdkServer,
      
      // External server
      'external-db': {
        command: 'node',
        args: ['db-server.js'],
      },
    },
  },
});

Best Practices

// ✅ Good - related tools in one server
const mathServer = createSdkMcpServer({
  name: 'math',
  tools: [addTool, subtractTool, multiplyTool, divideTool],
});

// ❌ Bad - unrelated tools mixed
const mixedServer = createSdkMcpServer({
  name: 'utilities',
  tools: [addTool, weatherTool, databaseTool, timeTool],
});

2. Use Descriptive Names

// ✅ Good
createS dkMcpServer({ name: 'weather-api', tools: [/* ... */] })
createS dkMcpServer({ name: 'database-queries', tools: [/* ... */] })

// ❌ Bad
createS dkMcpServer({ name: 'tools', tools: [/* ... */] })
createS dkMcpServer({ name: 'server1', tools: [/* ... */] })

3. Specify Versions

const server = createSdkMcpServer({
  name: 'my-api',
  version: '2.1.0',  // Track version changes
  tools: [/* ... */],
});

4. Keep Servers Focused

// ✅ Good - focused servers
const authServer = createSdkMcpServer({
  name: 'auth',
  tools: [loginTool, logoutTool, validateTokenTool],
});

const dataServer = createSdkMcpServer({
  name: 'data',
  tools: [queryTool, createTool, updateTool, deleteTool],
});

// ❌ Bad - monolithic server
const everythingServer = createSdkMcpServer({
  name: 'everything',
  tools: [
    loginTool, logoutTool, queryTool, createTool,
    weatherTool, calculatorTool, /* ... 20 more tools */
  ],
});

5. Initialize Once, Reuse

// ✅ Good - create once, use many times
const utilities = createSdkMcpServer({
  name: 'utilities',
  tools: [/* ... */],
});

function runQuery(prompt: string) {
  return query({
    prompt,
    options: { mcpServers: { utilities } },
  });
}

// ❌ Bad - recreate on every call
function runQuery(prompt: string) {
  const utilities = createSdkMcpServer({  // Wasteful
    name: 'utilities',
    tools: [/* ... */],
  });
  
  return query({
    prompt,
    options: { mcpServers: { utilities } },
  });
}

See Also