面向 AI 助手:本页的 Markdown 版本位于 https://ark.volcengine.com/region:cn-beijing/docs/mcp-remote-mcp.md,全部文档索引见 https://ark.volcengine.com/llms.txt。

云部署 MCP / Remote MCP

核心功能#

  • 兼容丰富的 MCP 生态:已对接 智能体工具商店,支持调用市场内各类垂直领域 MCP 工具(如巨量千川、知识库),无需自行开发工具逻辑。

  • 多轮工具调用:复杂任务(例如多步数据查询+分析)支持多轮 MCP 调用,上一轮工具输出结果自动作为下一轮模型输入(例:MCP 数据查询→模型分析→二次补充查询)。

  • 灵活混合调用:可与用户自定义函数、Web Search 工具混合使用(如联合巨量千川 MCP 与 Web Search 实现“数据+热点”交叉分析),暂不限制工具组合数量。

准备工作:获取 MCP URL#

  1. 您可以访问 智能体工具商店,选择想要使用的云部署 MCP 或 Remote MCP。
  1. 在 MCP 详情页,点击生成获取带授权信息的个人 MCP 服务端 URL。

    部分 MCP 通过Authorization字段来配置授权,详情请参考2. 配置鉴权信息。

快速入门#

Bash
curl --location 'https://ark.cn-beijing.volces.com/api/v3/responses' \
--header "Authorization: Bearer <ARK_API_KEY>" \
--header 'Content-Type: application/json' \
--header 'ark-beta-mcp: true' \
--data '{
    "model": "doubao-seed-2-1-pro-260915",
    "stream": true,
    "tools": [
        {
            "type": "mcp",
            "server_label": "deepwiki",
            "server_url": "https://mcp.deepwiki.com/mcp",
            "require_approval": "never"
        }
    ],
    "input": [
        {
            "role": "user",
            "content": [
                {
                    "type": "input_text",
                    "text": "看一下volcengine/ai-app-lab这个repo的文档"
                }
            ]
        }
    ]
}'
Python
from arkruntime import Ark
import os

# 从环境变量获取API密钥(配置方法:https://ark.volcengine.com/region:cn-beijing/docs/quick-start)
api_key = os.getenv('ARK_API_KEY')

# 初始化客户端,启用MCP功能
client = Ark(
    base_url='https://ark.cn-beijing.volces.com/api/v3',
    api_key=api_key
)

# 发起基础MCP调用请求
response = client.responses.create(
    model="doubao-seed-2-1-pro-260915",
    tools=[
        {
            "type": "mcp",
            "server_label": "deepwiki",
            "server_url": "https://mcp.deepwiki.com/mcp",
            "require_approval": "never"
        }
    ],
    input=[
        {
            "role": "user",
            "content": [
                {
                    "type": "input_text",
                    "text": "看一下volcengine/ai-app-lab这个repo的文档"
                }
            ]
        }
    ],
    extra_headers={"ark-beta-mcp": "true"},
    stream=True  # 流式获取结果
)

# 打印响应结果
for chunk in response:
    if hasattr(chunk, 'delta'):
        print(chunk.delta, end="", flush=True)
Java
package com.example.arkdemo;


import com.volcengine.ark.runtime.model.responses.constant.ResponsesConstants;
import com.volcengine.ark.runtime.model.responses.event.StreamEvent;
import com.volcengine.ark.runtime.model.responses.request.ResponsesInput;
import com.volcengine.ark.runtime.model.responses.request.CreateResponsesRequest;
import com.volcengine.ark.runtime.model.responses.tool.ToolMCP;
import com.volcengine.ark.runtime.model.responses.tool.mcp.MCPRequireApproval;
import com.volcengine.ark.runtime.model.responses.event.reasoningsummary.ReasoningSummaryTextDeltaEvent;
import com.volcengine.ark.runtime.model.responses.event.outputitem.OutputItemDoneEvent;
import com.volcengine.ark.runtime.model.responses.event.outputtext.OutputTextDeltaEvent;
import com.volcengine.ark.runtime.service.ArkService;
import okhttp3.ConnectionPool;
import okhttp3.Dispatcher;
import com.volcengine.ark.runtime.model.responses.common.ResponsesThinking;
import com.volcengine.ark.runtime.model.responses.event.response.ResponseCompletedEvent;


import java.time.Duration;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;


public class mcpjava {


    // 固定使用的模型
    private static final String model = "doubao-seed-2-1-pro-260915";


    public static void main(String[] args) {
        // 1. 校验鉴权凭证(必须配置 ARK_API_KEY 环境变量)
        String apiKey = System.getenv("ARK_API_KEY");
        if (apiKey == null) {
            System.out.println("ARK_API_KEY environment variable not set");
            return;
        }


        // 2. 初始化 ArkService(配置连接池、并发、超时等)
        ConnectionPool connectionPool = new ConnectionPool(5, 1, TimeUnit.SECONDS);
        Dispatcher dispatcher = new Dispatcher();
        dispatcher.setMaxRequests(5000);
        dispatcher.setMaxRequestsPerHost(5000);
        ArkService service = ArkService.builder()
                .dispatcher(dispatcher)
                .timeout(Duration.ofHours(1))
                .connectionPool(connectionPool)
                .apiKey(apiKey)
                .build();


        // 3. 构造 MCP 工具调用请求
        CreateResponsesRequest mcpRequest = CreateResponsesRequest.builder()
                .model(model)
                .stream(true) // 流式响应,实时获取结果
                // 配置 MCP 工具:指定服务标签、服务地址、审批模式
                .tools(Collections.singletonList(
                        ToolMCP.builder()
                                .serverLabel("deepwiki-test") // MCP 服务标签
                                .serverUrl("https://mcp.deepwiki.com/mcp") // MCP 服务接口地址
                                // 审批模式:NEVER(无需审批)、ALWAYS(必须审批)、AUTO(自动审批)
                                .requireApproval(MCPRequireApproval.builder()
                                        .mode(ResponsesConstants.MCP_APPROVAL_MODE_NEVER)
                                        .build())
                                .build()
                ))
                // 输入查询指令:让 MCP 工具查询指定仓库结构
                .input(ResponsesInput.builder()
                        .stringValue("查看这个 repo的structure expressjs/express")
                        .build())
                // 启用模型思考过程(可在流式输出中查看)
                .thinking(ResponsesThinking.builder()
                        .type(ResponsesConstants.THINKING_TYPE_ENABLED)
                        .build())
                .build();


        // 4. 构造自定义 Header(设置 ark-beta-mcp:true)
        Map<String, String> customHeaders = new HashMap<>();
        customHeaders.put("ark-beta-mcp", "true");


        // 5. 执行 MCP 工具调用并处理流式响应(传入自定义 Header)
        try {
            System.out.println("----- [MCP Tool Call] 开始调用 -----");
            // 流式调用时传入 headers 参数
            service.streamResponse(mcpRequest, customHeaders)
                    .doOnError(Throwable::printStackTrace) // 异常处理
                    .blockingForEach(mcpjava::printStreamEvent); // 监听流式事件
        } catch (Exception e) {
            System.err.println("MCP Tool Call Error: " + e.getMessage());
            e.printStackTrace();
        }


        // 6. 关闭服务 executor,释放资源
        service.shutdownExecutor();
    }


    // 跟踪是否已输出思考过程前缀
    private static boolean isReasoningPrefixPrinted = false;
    // 跟踪是否已输出工具返回前缀
    private static boolean isToolPrefixPrinted = false;
    // 流式事件处理:打印思考过程、响应内容、工具调用状态
    private static void printStreamEvent(StreamEvent event) {
        // 打印思考过程增量(如模型分析如何调用 MCP 工具)
        if (event instanceof ReasoningSummaryTextDeltaEvent) {
            ReasoningSummaryTextDeltaEvent reasoningEvent = (ReasoningSummaryTextDeltaEvent) event;
            String delta = reasoningEvent.getDelta();
            // 只在思考过程开始时添加前缀
            if (!isReasoningPrefixPrinted && delta.trim().length() > 0) {
                System.out.print("[思考过程] " + delta);
                isReasoningPrefixPrinted = true;
            } else {
                System.out.print(delta);
            }
            // 如果遇到换行符,重置前缀标记
            if (delta.contains("\n")) {
                isReasoningPrefixPrinted = false;
            }
        }
        // 打印文本响应增量(MCP 工具返回结果的实时输出)
        else if (event instanceof OutputTextDeltaEvent) {
            OutputTextDeltaEvent textEvent = (OutputTextDeltaEvent) event;
            String delta = textEvent.getDelta();
            // 只在工具返回开始时添加前缀
            if (!isToolPrefixPrinted && delta.trim().length() > 0) {
                System.out.print("[工具返回] " + delta);
                isToolPrefixPrinted = true;
            } else {
                System.out.print(delta);
            }
            // 如果遇到换行符,重置前缀标记
            if (delta.contains("\n")) {
                isToolPrefixPrinted = false;
            }
        }
        // 打印输出项完成事件
        else if (event instanceof OutputItemDoneEvent) {
            OutputItemDoneEvent itemEvent = (OutputItemDoneEvent) event;
            System.out.println("\n[输出完成] " + itemEvent.getItem().getType());
            // 重置所有前缀标记
            isReasoningPrefixPrinted = false;
            isToolPrefixPrinted = false;
        }
        // 整体响应完成,打印调用用量
        else if (event instanceof ResponseCompletedEvent) {
            ResponseCompletedEvent completedEvent = (ResponseCompletedEvent) event;
            System.out.println("\n[响应完成] 调用用量:" + completedEvent.getResponse().getUsage());
            // 重置所有前缀标记
            isReasoningPrefixPrinted = false;
            isToolPrefixPrinted = false;
        }
        // 打印所有其他未处理的事件类型(用于调试)
        else {
            System.out.println("\n[未处理事件] 类型:" + event.getClass().getName());
            // 重置所有前缀标记
            isReasoningPrefixPrinted = false;
            isToolPrefixPrinted = false;
        }
    }
}
Go
package main

import (
    "context"
    "fmt"
    "io"
    "os"

    "github.com/volcengine/ark-runtime-go/arkruntime"
    "github.com/volcengine/ark-runtime-go/arkruntime/model/responses"
)

const (
    mcpServerURL  = "https://mcp.deepwiki.com/mcp"
    mcpLabel      = "deepwiki"
    mcpBetaHeader = "ark-beta-mcp"
    userPrompt    = "查一下 mark3labs/mcp-go 这个仓库的结构"
)

func main() {
    nonStream()
    fmt.Println()
    stream()
}

func newClient() *arkruntime.Client {
    return arkruntime.NewClientWithApiKey(
        os.Getenv("ARK_API_KEY"),
        arkruntime.WithBaseUrl("https://ark.cn-beijing.volces.com/api/v3"),
    )
}

func nonStream() {
    fmt.Println("===== non-streaming =====")
    client := newClient()
    ctx := context.Background()

    // Round 1: ask the model to call the MCP tool.
    req1 := &responses.ResponsesRequest{
        Model: "doubao-seed-2-1-pro-260915",
        Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{
            {OneOf: responses.NewItemEasyMessageInputItemSum(buildUserMessage())},
        }),
        Tools: []responses.Tool{mcpTool(false)},
    }
    resp1, err := client.CreateResponses(
        ctx,
        req1,
        arkruntime.WithCustomHeader(mcpBetaHeader, "true"),
    )
    if err != nil {
        fmt.Printf("round 1 error: %v\n", err)
        return
    }
    fmt.Printf("response: %+v\n", resp1)

    approvalRequestID := findApprovalRequestID(resp1.Output)
    if approvalRequestID == "" {
        fmt.Println("no approval request emitted; nothing to approve")
        return
    }

    // Round 2: approve the requested MCP call.
    req2 := &responses.ResponsesRequest{
        Model: "doubao-seed-2-1-pro-260915",
        Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{
            {OneOf: responses.NewItemFunctionMcpApprovalResponseInputItemSum(
                responses.ItemFunctionMcpApprovalResponse{
                    Type:              responses.ItemFunctionMcpApprovalResponseTypeMcpApprovalResponse,
                    ApprovalRequestID: approvalRequestID,
                    Approve:           true,
                },
            )},
        }),
        Tools:              []responses.Tool{mcpTool(false)},
        PreviousResponseID: responses.NewOptString(resp1.ID),
    }
    resp2, err := client.CreateResponses(
        ctx,
        req2,
        arkruntime.WithCustomHeader(mcpBetaHeader, "true"),
    )
    if err != nil {
        fmt.Printf("round 2 error: %v\n", err)
        return
    }
    fmt.Printf("response: %+v\n", resp2)

    // Round 3: let the model call the MCP tool without approval.
    req3 := &responses.ResponsesRequest{
        Model: "doubao-seed-2-1-pro-260915",
        Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{
            {OneOf: responses.NewItemEasyMessageInputItemSum(buildUserMessage())},
        }),
        Tools: []responses.Tool{mcpTool(true)},
    }
    resp3, err := client.CreateResponses(
        ctx,
        req3,
        arkruntime.WithCustomHeader(mcpBetaHeader, "true"),
    )
    if err != nil {
        fmt.Printf("round 3 error: %v\n", err)
        return
    }
    fmt.Printf("response: %+v\n", resp3)
}

func stream() {
    fmt.Println("===== streaming =====")
    client := newClient()
    ctx := context.Background()

    req1 := &responses.ResponsesRequest{
        Model: "doubao-seed-2-1-pro-260915",
        Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{
            {OneOf: responses.NewItemEasyMessageInputItemSum(buildUserMessage())},
        }),
        Tools: []responses.Tool{mcpTool(false)},
    }
    responseID, approvalRequestID, err := streamAndCapture(ctx, client, req1)
    if err != nil {
        fmt.Printf("round 1 stream error: %v\n", err)
        return
    }
    if approvalRequestID == "" {
        fmt.Println("no approval request emitted; nothing to approve")
        return
    }

    req2 := &responses.ResponsesRequest{
        Model: "doubao-seed-2-1-pro-260915",
        Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{
            {OneOf: responses.NewItemFunctionMcpApprovalResponseInputItemSum(
                responses.ItemFunctionMcpApprovalResponse{
                    Type:              responses.ItemFunctionMcpApprovalResponseTypeMcpApprovalResponse,
                    ApprovalRequestID: approvalRequestID,
                    Approve:           true,
                },
            )},
        }),
        Tools:              []responses.Tool{mcpTool(false)},
        PreviousResponseID: responses.NewOptString(responseID),
    }
    if _, _, err := streamAndCapture(ctx, client, req2); err != nil {
        fmt.Printf("round 2 stream error: %v\n", err)
        return
    }

    req3 := &responses.ResponsesRequest{
        Model: "doubao-seed-2-1-pro-260915",
        Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{
            {OneOf: responses.NewItemEasyMessageInputItemSum(buildUserMessage())},
        }),
        Tools: []responses.Tool{mcpTool(true)},
    }
    if _, _, err := streamAndCapture(ctx, client, req3); err != nil {
        fmt.Printf("round 3 stream error: %v\n", err)
    }
}

func buildUserMessage() responses.ItemEasyMessage {
    return responses.ItemEasyMessage{
        Role: responses.NewOptMessageRole(responses.MessageRoleUser),
        Content: responses.NewContentItemArrayMessageContent([]responses.ContentItem{
            {
                OneOf: responses.NewContentItemTextContentItemSum(responses.ContentItemText{
                    Type: responses.ContentItemTextTypeInputText,
                    Text: userPrompt,
                }),
            },
        }),
    }
}

func mcpTool(skipApproval bool) responses.Tool {
    mcp := responses.McpTool{
        Type:              responses.McpToolTypeMcp,
        ServerLabel:       mcpLabel,
        ServerURL:         mcpServerURL,
        ServerDescription: responses.NewOptString("Query a GitHub repository's documentation"),
    }
    if skipApproval {
        mcp.RequireApproval = responses.NewOptMcpRequireApproval(
            responses.NewMcpApprovalModeMcpRequireApproval(responses.McpApprovalModeNever),
        )
    }
    return responses.Tool{OneOf: responses.NewMcpToolToolSum(mcp)}
}

func findApprovalRequestID(output []responses.OutputItem) string {
    for i := len(output) - 1; i >= 0; i-- {
        if request, ok := output[i].OneOf.GetItemFunctionMcpApprovalRequest(); ok {
            return request.ID.Or("")
        }
    }
    return ""
}

func streamAndCapture(
    ctx context.Context,
    client *arkruntime.Client,
    req *responses.ResponsesRequest,
) (string, string, error) {
    stream, err := client.CreateResponsesStream(
        ctx,
        req,
        arkruntime.WithCustomHeader(mcpBetaHeader, "true"),
    )
    if err != nil {
        return "", "", err
    }

    var responseID string
    var approvalRequestID string
    for {
        event, err := stream.Recv()
        if err == io.EOF {
            return responseID, approvalRequestID, nil
        }
        if err != nil {
            return responseID, approvalRequestID, err
        }

        switch event.OneOf.Type {
        case responses.ResponseCreatedEventResponseStreamEventSum:
            responseID = event.OneOf.ResponseCreatedEvent.Response.ID
        case responses.ResponseReasoningSummaryTextDeltaEventResponseStreamEventSum:
            fmt.Print(event.OneOf.ResponseReasoningSummaryTextDeltaEvent.Delta.Or(""))
        case responses.ResponseTextDeltaEventResponseStreamEventSum:
            fmt.Print(event.OneOf.ResponseTextDeltaEvent.Delta.Or(""))
        case responses.ResponseTextDoneEventResponseStreamEventSum:
            fmt.Printf("\n[text done] %s\n", event.OneOf.ResponseTextDoneEvent.Text.Or(""))
        case responses.ResponseOutputItemDoneEventResponseStreamEventSum:
            item := event.OneOf.ResponseOutputItemDoneEvent.Item
            if request, ok := item.OneOf.GetItemFunctionMcpApprovalRequest(); ok {
                approvalRequestID = request.ID.Or("")
                fmt.Printf(
                    "\n[mcp approval request] name=%s arguments=%s\n",
                    request.Name,
                    request.Arguments,
                )
            }
        case responses.ResponseMcpCallArgumentsDeltaEventResponseStreamEventSum:
            fmt.Print(event.OneOf.ResponseMcpCallArgumentsDeltaEvent.Delta)
        case responses.ResponseMcpCallCompletedEventResponseStreamEventSum:
            fmt.Println("\n[mcp call completed]")
        case responses.ResponseMcpListToolsInProgressEventResponseStreamEventSum:
            fmt.Println("\n[listing mcp tools]")
        case responses.ResponseMcpListToolsCompletedEventResponseStreamEventSum:
            fmt.Println("\n[mcp tools listed]")
        }
    }
}

参数说明#

详情请参见创建 Response。

支持的模型列表#

参见工具调用能力。

模型调用需确保已完成 MCP 服务开通及对应模型权限申请,未开通用户无法触发 MCP 工具功能。

工作原理#

您可以通过 Responses API 调用 MCP 工具(包括云部署 MCP 和 Remote MCP)。此功能与函数调用的主要区别在于,MCP 工具完全在云端执行:大模型会连接至远程服务器来完成调用,因此不支持本地 MCP 运行调用。

使用指南#

本节介绍了常见的使用场景及其字段设置方法,以帮助您更高效地运用 MCP 工具。

1. 过滤 MCP 工具#

一个 MCP 服务器可能提供多种工具。您可以在该服务的详情页查看完整的工具列表。在 API 调用时,您可以使用 allowed_tools 参数来指定要使用的工具,从而节省时间并获得更符合预期的结果。

Python
"tools": [
    {
            "type": "mcp",
            "server_label": "mcp_tools",
            "server_url": "https://mcpserver/mcp",
            "allowed_tools": {
                "tool_names": [
                    "tool1",
                    "tool2"
                ]
            }
        }
]

2. 配置鉴权信息#

目前部分 MCP 工具需要在 header 中配置身份验证。一般通过 Authorization 字段来配置,示例参考如下:

JSON
...
"tools": [
  {
    "type": "mcp",
    "server_label": "info-quest-mcp",
    "server_url": "https://mcp.infoquest.bytepluses.com/mcp",
    "require_approval": "never",
    "headers": {
      "Authorization": "******"
    }
  }
]
...

场景示例:多轮调用#

场景:调用巨量千川 MCP 分析直播数据,需用户确认后执行,支持多轮补充查询。

Python
from arkruntime import Ark
import os

api_key = os.getenv('ARK_API_KEY')
client = Ark(
    base_url='https://ark.cn-beijing.volces.com/api/v3',
    api_key=api_key
)

# 1. 首轮请求:发起MCP调用审批
first_response = client.responses.create(
    model="doubao-seed-2-1-pro-260915",
    tools=[
        {
            "type": "mcp",
            "server_label": "qianchuan-mcp",
            "server_url": "https://mcp.qianchuan.com/mcp",
            "require_approval": "always"
        }
    ],
    input=[
        {
            "role": "user",
            "content": [{"type": "input_text", "text": "分析今日直播间竞价推广数据"}]
        }
    ],
    extra_headers={"ark-beta-mcp": "true"},
    stream=True
)

# 提取审批ID与上一轮响应ID
last_chunk = None
for chunk in first_response:
    last_chunk = chunk
previous_response_id = last_chunk.response.id
approval_id = last_chunk.response.output[-1].id
print(f"上一轮响应ID:{previous_response_id},审批ID:{approval_id}")

# 2. 二轮请求:同意审批并继续执行
second_response = client.responses.create(
    model="doubao-seed-2-1-pro-260915",
    tools=[
        {
            "type": "mcp",
            "server_label": "qianchuan-mcp",
            "server_url": "https://mcp.qianchuan.com/mcp",
            "require_approval": "always"
        }
    ],
    input=[
        {
            "type": "mcp_approval_response",
            "approve": True,  # 同意审批
            "approval_request_id": approval_id
        }
    ],
    extra_headers={"ark-beta-mcp": "true"},
    previous_response_id=previous_response_id,  # 关联上一轮请求
    stream=True
)

# 打印二轮响应结果
for chunk in second_response:
    if hasattr(chunk, 'delta'):
        print(chunk.delta, end="", flush=True)

注意事项#

  1. 目前云部署 MCP / Remote MCP 仅支持通过 Responses API 调用,同时只支持通过 Streamable HTTP 链接的 MCP。
  2. 函数命名冲突:若用户自定义函数与 MCP 内置工具(如 mcp_call)重名,由模型自动判断调用优先级,无需额外配置。
  3. 默认限流规则:账号维度默认限流为 1000 RPM(每分钟请求数),超出限流将导致请求失败,需调整可提交工单。
  4. 错误信息:暂不支持 caching 参数,使用该参数会返回 400 错误信息。

计费说明#

消耗模型基础 Tokens,不收取 MCP 工具调用附加费。

相关参考#