核心功能#
-
兼容丰富的 MCP 生态:已对接 智能体工具商店,支持调用市场内各类垂直领域 MCP 工具(如巨量千川、知识库),无需自行开发工具逻辑。
-
多轮工具调用:复杂任务(例如多步数据查询+分析)支持多轮 MCP 调用,上一轮工具输出结果自动作为下一轮模型输入(例:MCP 数据查询→模型分析→二次补充查询)。
-
灵活混合调用:可与用户自定义函数、Web Search 工具混合使用(如联合巨量千川 MCP 与 Web Search 实现“数据+热点”交叉分析),暂不限制工具组合数量。
准备工作:获取 MCP URL#
- 您可以访问 智能体工具商店,选择想要使用的云部署 MCP 或 Remote MCP。
-
在 MCP 详情页,点击生成获取带授权信息的个人 MCP 服务端 URL。
部分 MCP 通过
Authorization字段来配置授权,详情请参考2. 配置鉴权信息。
快速入门#
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的文档"
}
]
}
]
}'
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)
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;
}
}
}
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 参数来指定要使用的工具,从而节省时间并获得更符合预期的结果。
"tools": [
{
"type": "mcp",
"server_label": "mcp_tools",
"server_url": "https://mcpserver/mcp",
"allowed_tools": {
"tool_names": [
"tool1",
"tool2"
]
}
}
]
2. 配置鉴权信息#
目前部分 MCP 工具需要在 header 中配置身份验证。一般通过 Authorization 字段来配置,示例参考如下:
...
"tools": [
{
"type": "mcp",
"server_label": "info-quest-mcp",
"server_url": "https://mcp.infoquest.bytepluses.com/mcp",
"require_approval": "never",
"headers": {
"Authorization": "******"
}
}
]
...
场景示例:多轮调用#
场景:调用巨量千川 MCP 分析直播数据,需用户确认后执行,支持多轮补充查询。
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)
注意事项#
- 目前云部署 MCP / Remote MCP 仅支持通过 Responses API 调用,同时只支持通过 Streamable HTTP 链接的 MCP。
- 函数命名冲突:若用户自定义函数与 MCP 内置工具(如
mcp_call)重名,由模型自动判断调用优先级,无需额外配置。 - 默认限流规则:账号维度默认限流为
1000 RPM(每分钟请求数),超出限流将导致请求失败,需调整可提交工单。 - 错误信息:暂不支持
caching参数,使用该参数会返回 400 错误信息。
计费说明#
消耗模型基础 Tokens,不收取 MCP 工具调用附加费。