MCP 커넥터
MCP 커넥터 (MCP connector)
Claude의 Model Context Protocol(MCP) 커넥터 기능을 사용하면 별도의 MCP 클라이언트 없이 Messages API에서 직접 원격 MCP 서버에 연결할 수 있어요. 서버의 모든 도구를 활성화하거나, 특정 도구만 허용 목록에 넣거나, 원하지 않는 도구를 차단 목록에 넣고, 도구별로 커스텀 설정을 적용할 수 있어요.
출처: 문서
본문
Claude의 Model Context Protocol(MCP) 커넥터 기능은 별도의 MCP 클라이언트 없이 Messages API에서 직접 원격 MCP 서버에 연결할 수 있게 해줘요.
참고 (Note) 이 기능의 이전 버전(
mcp-client-2025-04-04)은 폐지됐어요. 폐지된 버전: mcp-client-2025-04-04를 참고하세요.
주요 기능 (Key features)
- 직접 API 통합: MCP 클라이언트를 구현하지 않고 MCP 서버에 연결
- 도구 호출 지원: Messages API를 통해 MCP 도구에 접근
- 유연한 도구 구성: 모든 도구 활성화, 특정 도구 허용 목록, 원치 않는 도구 차단 목록
- 도구별 구성: 개별 도구를 커스텀 설정으로 구성
- OAuth 인증: 인증된 서버를 위한 OAuth Bearer 토큰 지원
- 다중 서버: 단일 요청에서 여러 MCP 서버에 연결
Claude가 MCP 도구를 사용하는 시점 (When Claude uses MCP tools)
MCP 서버가 연결되면 사용자의 요청이 도구의 설명된 기능과 매핑될 때(명시적으로 "Jira에서 열린 버그 검색" 또는 암시적으로 Jira 서버가 연결된 상태에서 "릴리스를 막는 게 뭐지?") Claude가 그 도구를 호출해요.
Claude는 연결된 서비스에 대한 일반 상식 질문에는 MCP 도구를 호출하지 않아요. Notion 서버가 연결된 상태에서 "Notion 데이터베이스는 어떻게 작동하지?"라고 물으면 바로 답이 나오고, "내 Projects 데이터베이스에 뭐가 있지?"라고 물으면 도구가 촉발돼요.
시스템 프롬프트를 통해 Claude가 MCP 도구를 얼마나 쉽게 호출하는지 조절할 수 있어요. 일반 지침과 예시 표현은 Claude가 도구를 사용하는 시점을 참고하세요.
제한 사항 (Limitations)
- MCP 사양의 기능 중 현재는 도구 호출만 지원돼요.
- 서버는 HTTP로 공개적으로 노출되어야 해요(Streamable HTTP와 SSE 전송 모두 지원). 로컬 STDIO 서버는 직접 연결할 수 없어요.
Messages API에서 MCP 커넥터 사용하기 (Using the MCP connector in the Messages API)
MCP 커넥터는 두 구성 요소를 사용해요:
- MCP 서버 정의 (
mcp_servers배열): 서버 연결 세부 정보(URL, 인증)를 정의해요. - MCP 도구셋 (
tools배열): 어떤 도구를 활성화하고 어떻게 구성할지 설정해요.
기본 예시 (Basic example)
이 예시는 기본 구성으로 MCP 서버의 모든 도구를 활성화해요:
ant beta:messages create --beta mcp-client-2025-11-20 <<'YAML'
model: claude-opus-5-5
max_tokens: 1000
messages:
- role: user
content: What tools do you have available?
mcp_servers:
- type: url
url: https://example-server.modelcontextprotocol.io/sse
name: example-mcp
authorization_token: YOUR_TOKEN
tools:
- type: mcp_toolset
mcp_server_name: example-mcp
YAML
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=1000,
messages=[{"role": "user", "content": "What tools do you have available?"}],
mcp_servers=[
{
"type": "url",
"url": "https://example-server.modelcontextprotocol.io/sse",
"name": "example-mcp",
"authorization_token": "YOUR_TOKEN",
}
],
tools=[{"type": "mcp_toolset", "mcp_server_name": "example-mcp"}],
betas=["mcp-client-2025-11-20"],
)
print(response)
const anthropic = new Anthropic();
const response = await anthropic.beta.messages.create({
model: "claude-opus-5-5",
max_tokens: 1000,
messages: [
{
role: "user",
content: "What tools do you have available?"
}
],
mcp_servers: [
{
type: "url",
url: "https://example-server.modelcontextprotocol.io/sse",
name: "example-mcp",
authorization_token: "YOUR_TOKEN"
}
],
tools: [
{
type: "mcp_toolset",
mcp_server_name: "example-mcp"
}
],
betas: ["mcp-client-2025-11-20"]
});
console.log(response);
AnthropicClient client = new();
var parameters = new MessageCreateParams
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1000,
Messages = new List<BetaMessageParam>
{
new() { Role = Role.User, Content = "What tools do you have available?" }
},
McpServers = new List<BetaRequestMcpServerUrlDefinition>
{
new()
{
Url = "https://example-server.modelcontextprotocol.io/sse",
Name = "example-mcp",
AuthorizationToken = "YOUR_TOKEN"
}
},
Tools = new List<BetaToolUnion>
{
new BetaMcpToolset("example-mcp")
},
Betas = [AnthropicBeta.McpClient2025_11_20]
};
var message = await client.Beta.Messages.Create(parameters);
Console.WriteLine(message);
client := anthropic.NewClient()
response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1000,
Messages: []anthropic.BetaMessageParam{
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("What tools do you have available?")),
},
MCPServers: []anthropic.BetaRequestMCPServerURLDefinitionParam{
{
URL: "https://example-server.modelcontextprotocol.io/sse",
Name: "example-mcp",
AuthorizationToken: anthropic.String("YOUR_TOKEN"),
},
},
Tools: []anthropic.BetaToolUnionParam{
{OfMCPToolset: &anthropic.BetaMCPToolsetParam{
MCPServerName: "example-mcp",
}},
},
Betas: []anthropic.AnthropicBeta{
anthropic.AnthropicBetaMCPClient2025_11_20,
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response)
import com.anthropic.models.beta.messages.BetaMcpToolset;
// ...
import com.anthropic.models.beta.messages.BetaRequestMcpServerUrlDefinition;
// ...
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1000L)
.addUserMessage("What tools do you have available?")
.addMcpServer(BetaRequestMcpServerUrlDefinition.builder()
.url("https://example-server.modelcontextprotocol.io/sse")
.name("example-mcp")
.authorizationToken("YOUR_TOKEN")
.build())
.addTool(BetaMcpToolset.builder()
.mcpServerName("example-mcp")
.build())
.addBeta(AnthropicBeta.MCP_CLIENT_2025_11_20)
.build();
BetaMessage response = client.beta().messages().create(params);
IO.println(response);
}
$client = new Client();
$message = $client->beta->messages->create(
maxTokens: 1000,
messages: [
['role' => 'user', 'content' => 'What tools do you have available?']
],
model: 'claude-opus-5-5',
mcpServers: [
[
'type' => 'url',
'url' => 'https://example-server.modelcontextprotocol.io/sse',
'name' => 'example-mcp',
'authorization_token' => 'YOUR_TOKEN',
],
],
tools: [
[
'type' => 'mcp_toolset',
'mcp_server_name' => 'example-mcp',
],
],
betas: ['mcp-client-2025-11-20'],
);
echo $message;
client = Anthropic::Client.new
response = client.beta.messages.create(
model: "claude-opus-5-5",
max_tokens: 1000,
messages: [
{ role: "user", content: "What tools do you have available?" }
],
mcp_servers: [
{
type: "url",
url: "https://example-server.modelcontextprotocol.io/sse",
name: "example-mcp",
authorization_token: "YOUR_TOKEN"
}
],
tools: [
{
type: "mcp_toolset",
mcp_server_name: "example-mcp"
}
],
betas: ["mcp-client-2025-11-20"]
)
puts response
MCP 서버 설정 (MCP server configuration)
mcp_servers 배열의 각 MCP 서버는 연결 세부 정보를 정의해요:
{
"type": "url",
"url": "https://example-server.modelcontextprotocol.io/sse",
"name": "example-mcp",
"authorization_token": "YOUR_TOKEN"
}
필드 설명 (Field descriptions)
| 속성 | 타입 | 필수 | 설명 |
|---|---|---|---|
type |
string | 예 | 현재는 "url"만 지원돼요. |
url |
string | 예 | MCP 서버의 URL. https://로 시작해야 해요. |
name |
string | 예 | 이 MCP 서버의 고유 식별자. tools 배열의 정확히 하나의 MCPToolset에서 참조해야 해요. |
authorization_token |
string | 아니요 | MCP 서버가 요구하면 OAuth 인증 토큰. 얻는 방법은 인증을, 프로토콜 세부 정보는 MCP 사양을 참고하세요. |
MCP 도구셋 설정 (MCP toolset configuration)
MCPToolset는 tools 배열에 있으며, MCP 서버의 어떤 도구가 활성화되고 어떻게 구성될지 설정해요.
기본 구조 (Basic structure)
{
"type": "mcp_toolset",
"mcp_server_name": "example-mcp",
"default_config": {
"enabled": true,
"defer_loading": false
},
"configs": {
"specific_tool_name": {
"enabled": true,
"defer_loading": true
}
}
}
필드 설명 (Field descriptions)
| 속성 | 타입 | 필수 | 설명 |
|---|---|---|---|
type |
string | 예 | "mcp\_toolset"이어야 해요. |
mcp_server_name |
string | 예 | mcp_servers 배열에 정의된 서버 이름과 일치해야 해요. |
default_config |
object | 아니요 | 이 세트의 모든 도구에 적용되는 기본 구성. configs의 개별 도구 구성이 이 기본값을 덮어써요. |
configs |
object | 아니요 | 도구별 구성 재정의. 키는 도구 이름, 값은 구성 객체. |
cache_control |
object | 아니요 | 이 도구셋에 대한 프롬프트 캐싱 캐시 중단점 구성. |
mcp-client-2026-09-15 베타 헤더를 쓰면 MCPToolset는 서버의 도구 목록의 고정 복사본인 tools도 받아요. MCP 서버의 도구 목록 고정하기를 참고하세요.
도구 구성 옵션 (Tool configuration options)
각 도구(default_config나 configs에서 구성하든)는 다음 필드를 지원해요:
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
enabled |
boolean | true |
이 도구가 활성화되는지 여부. |
defer_loading |
boolean | false |
true면 도구 설명이 처음에 모델로 보내지 않아요. Tool search 도구와 함께 사용해요. |
Anthropic 제공 도구 전체 디렉터리와 defer_loading 같은 선택 속성은 도구 레퍼런스를 참고하세요. 큰 도구셋을 검색하려면 Tool search 도구를 참고하세요.
구성 병합 (Configuration merging)
구성 값은 이 우선순위(높을수록 위)로 병합돼요:
configs의 도구별 설정- 세트 수준
default_config - 시스템 기본값
예시:
{
"type": "mcp_toolset",
"mcp_server_name": "google-calendar-mcp",
"default_config": {
"defer_loading": true
},
"configs": {
"search_events": {
"enabled": false
}
}
}
결과:
search_events:enabled: false(configs에서),defer_loading: true(default_config에서)- 다른 모든 도구:
enabled: true(시스템 기본값),defer_loading: true(default_config에서)
일반적인 구성 패턴 (Common configuration patterns)
기본 구성으로 모든 도구 활성화 (Enable all tools with default configuration)
가장 간단한 패턴: 서버의 모든 도구 활성화:
{
"type": "mcp_toolset",
"mcp_server_name": "google-calendar-mcp"
}
허용 목록: 특정 도구만 활성화 (Allowlist: enable only specific tools)
기본값을 enabled: false로 설정한 다음 특정 도구를 명시적으로 활성화:
{
"type": "mcp_toolset",
"mcp_server_name": "google-calendar-mcp",
"default_config": {
"enabled": false
},
"configs": {
"search_events": {
"enabled": true
},
"create_event": {
"enabled": true
}
}
}
차단 목록: 특정 도구 비활성화 (Denylist: disable specific tools)
기본적으로 모든 도구를 활성화한 다음 원치 않는 도구를 명시적으로 비활성화해요. 읽기 전용 어시스턴트를 만들 때나 상태 변경 전에 사람 확인 단계를 원할 때 쓰기·파괴적 도구를 차단 목록에 넣는 것을 권장해요:
{
"type": "mcp_toolset",
"mcp_server_name": "google-calendar-mcp",
"configs": {
"delete_all_events": {
"enabled": false
},
"share_calendar_publicly": {
"enabled": false
}
}
}
혼합: 도구별 구성이 있는 허용 목록 (Mixed: allowlist with per-tool configuration)
허용 목록을 각 도구의 커스텀 구성과 결합:
{
"type": "mcp_toolset",
"mcp_server_name": "google-calendar-mcp",
"default_config": {
"enabled": false,
"defer_loading": true
},
"configs": {
"search_events": {
"enabled": true,
"defer_loading": false
},
"list_events": {
"enabled": true
}
}
}
이 예시에서:
search_events는defer_loading: false로 활성화list_events는defer_loading: true로 활성화(default_config에서 상속)- 다른 모든 도구는 비활성화
검증 규칙 (Validation rules)
API는 다음 검증 규칙을 적용해요:
- 서버가 있어야 함: MCPToolset의
mcp_server_name은mcp_servers배열에 정의된 서버와 일치해야 해요. - 서버가 사용되어야 함:
mcp_servers에 정의된 모든 MCP 서버는 정확히 하나의 MCPToolset에서 참조되어야 해요. - 서버당 고유 도구셋: 각 MCP 서버는 하나의 MCPToolset에서만 참조될 수 있어요.
- 알 수 없는 도구 이름:
configs의 도구 이름이 MCP 서버에 없으면 백엔드 경고가 로그되지만 오류는 반환되지 않아요 (MCP 서버는 동적 도구 가용성을 가질 수 있어요).
응답 콘텐츠 유형 (Response content types)
Claude가 MCP 도구를 사용하면 응답에 두 가지 새 콘텐츠 블록 유형이 포함돼요:
MCP 도구 사용 블록 (MCP tool use block)
{
"type": "mcp_tool_use",
"id": "mcptoolu_014Q35RayjACSWkSj4X2yov1",
"name": "echo",
"server_name": "example-mcp",
"input": { "param1": "value1", "param2": "value2" }
}
MCP 도구 결과 블록 (MCP tool result block)
{
"type": "mcp_tool_result",
"tool_use_id": "mcptoolu_014Q35RayjACSWkSj4X2yov1",
"is_error": false,
"content": [
{
"type": "text",
"text": "Hello"
}
]
}
MCP 서버의 도구 목록 고정하기 (Pin an MCP server's tool list) (beta)
MCP 서버는 언제든 도구를 바꿀 수 있어요. mcp-client-2026-09-15 베타 헤더는 각 서버가 반환하는 도구 목록을 기록하고 고정할 수 있게 해줘서, 서버가 도구를 바꿔도 대화 도중 Claude가 보는 것이 바뀌지 않아요. 여기에는 mcp-client-2025-11-20이 하는 모든 것이 포함되므로 그 헤더 대신 보내세요. Claude API에서 사용할 수 있어요.
API가 응답을 만드는 동안 MCP 서버에 도구를 요청하면, 그 서버마다 하나씩 응답이 mcp_tool_listing 블록으로 시작해요:
{
"type": "mcp_tool_listing",
"mcp_server_name": "example-mcp",
"tools": [
{
"name": "echo",
"description": "Returns the text it receives.",
"input_schema": {
"type": "object",
"properties": { "text": { "type": "string" } },
"required": ["text"]
}
}
]
}
코드가 content[0]을 읽는다면 이 블록들은 건너뛰세요. mcp_tool_listing 블록을 포함해 어시스턴트 메시지를 변경 없이 다시 보내고, 그것을 담은 모든 요청에 계속 mcp-client-2026-09-15를 보내세요. 이후 요청은 그 서버에 다시 묻는 대신 기록된 목록을 사용해요.
직접 목록을 고정하려면 블록의 tools를 그 서버의 MCPToolset의 tools 필드에 복사하세요. 그러면 API는 서버에 도구를 묻지 않고, 도구셋의 도구는 정확히 그 항목들이 되며 default_config와 configs가 적용돼요:
{
"type": "mcp_toolset",
"mcp_server_name": "example-mcp",
"tools": [
{
"name": "echo",
"description": "Returns the text it receives.",
"input_schema": {
"type": "object",
"properties": { "text": { "type": "string" } },
"required": ["text"]
}
}
]
}
tools의 각 항목은 서버가 나열하는 대로 도구의 name(서버 이름 제외), description, input_schema를 담아요.
다음 예시는 고정되지 않은 도구셋으로 요청을 하나 보내고, 반환된 목록을 도구셋의 tools 필드에 복사한 다음 다시 요청을 보내요. API가 서버에 묻지 않으므로 두 번째 응답에는 mcp_tool_listing 블록이 없어요:
First request: the toolset isn't pinned, so the API asks the server for
its tools and the response starts with an mcp_tool_listing block.
tee shows the response on stderr while the variable captures it.
FIRST=$(curl -sS https://api.anthropic.com/v1/messages
-H "content-type: application/json"
-H "x-api-key: $ANTHR...KEY"
-H "anthropic-version: 2023-06-01"
-H "anthropic-beta: mcp-client-2026-09-15"
-d "$BODY" | tee /dev/stderr)
Pin the list: copy the block's tools into the toolset. The API uses
exactly these entries and doesn't ask the server again.
TOOLS=$(jq '.content[] | select(.type == "mcp_tool_listing") | .tools'
<<<"$FIRST")
PINNED=$(jq --argjson tools "$TOOLS" '.tools[0].tools = $tools' <<<"$BODY")
With a pinned toolset, the response has no mcp_tool_listing block.
curl https://api.anthropic.com/v1/messages
-H "content-type: application/json"
-H "x-api-key: $ANTHR...KEY"
-H "anthropic-version: 2023-06-01"
-H "anthropic-beta: mcp-client-2026-09-15"
-d "$PINNED"
```bash CLI
request=$(cat <<'YAML'
model: claude-opus-5-5
max_tokens: 1024
mcp_servers:
- type: url
url: https://example-server.modelcontextprotocol.io/sse
name: example-mcp
authorization_token: YOUR_TOKEN
tools:
- type: mcp_toolset
mcp_server_name: example-mcp
messages:
- role: user
content: What tools do you have available?
YAML
)
# First request: the toolset isn't pinned, so the API asks the server for
# its tools and the response starts with an mcp_tool_listing block.
# tee shows the response on stderr while the variable captures it.
first=$(ant beta:messages create --beta mcp-client-2026-09-15 --format json \
<<<"$request" | tee /dev/stderr)
tools=$(jq -c '.content[] | select(.type == "mcp_tool_listing") | .tools' \
<<<"$first")
# Pin the list: copy the block's tools into the toolset. The --tool flag
# replaces the body's tools array. The API uses exactly these entries and
# doesn't ask the server again, so the response has no mcp_tool_listing block.
ant beta:messages create --beta mcp-client-2026-09-15 \
--tool "{type: mcp_toolset, mcp_server_name: example-mcp, tools: $tools}" \
<<<"$request"
from anthropic.types.beta import (
BetaMessageParam,
BetaRequestMCPServerURLDefinitionParam,
)
client = anthropic.Anthropic()
mcp_servers: list[BetaRequestMCPServerURLDefinitionParam] = [
{
"type": "url",
"url": "https://example-server.modelcontextprotocol.io/sse",
"name": "example-mcp",
"authorization_token": "YOUR_TOKEN",
},
]
messages: list[BetaMessageParam] = [
{"role": "user", "content": "What tools do you have available?"},
]
# First request: the toolset isn't pinned, so the API asks the server for
# its tools and the response starts with an mcp_tool_listing block.
first = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
betas=["mcp-client-2026-09-15"],
mcp_servers=mcp_servers,
tools=[{"type": "mcp_toolset", "mcp_server_name": "example-mcp"}],
messages=messages,
)
listing = next(block for block in first.content if block.type == "mcp_tool_listing")
print([tool.name for tool in listing.tools])
# Pin the list: copy the block's tools into the toolset. The API uses
# exactly these entries and doesn't ask the server again.
second = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
betas=["mcp-client-2026-09-15"],
mcp_servers=mcp_servers,
tools=[
{
"type": "mcp_toolset",
"mcp_server_name": "example-mcp",
"tools": [
{
"name": tool.name,
"description": tool.description,
"input_schema": tool.input_schema,
}
for tool in listing.tools
],
},
],
messages=messages,
)
# With a pinned toolset, the response has no mcp_tool_listing block.
print([block.type for block in second.content])
const client = new Anthropic();
const mcpServers: Anthropic.Beta.BetaRequestMCPServerURLDefinition[] = [
{
type: "url",
url: "https://example-server.modelcontextprotocol.io/sse",
name: "example-mcp",
authorization_token: "YOUR_TOKEN"
}
];
const messages: Anthropic.Beta.BetaMessageParam[] = [
{ role: "user", content: "What tools do you have available?" }
];
// First request: the toolset isn't pinned, so the API asks the server for
// its tools and the response starts with an mcp_tool_listing block.
const first = await client.beta.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
betas: ["mcp-client-2026-09-15"],
mcp_servers: mcpServers,
tools: [{ type: "mcp_toolset", mcp_server_name: "example-mcp" }],
messages
});
const listing = first.content.find((block) => block.type === "mcp_tool_listing");
if (!listing) {
throw new Error("The response has no mcp_tool_listing block.");
}
console.log(listing.tools.map((tool) => tool.name));
// Pin the list: copy the block's tools into the toolset. The API uses
// exactly these entries and doesn't ask the server again.
const second = await client.beta.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
betas: ["mcp-client-2026-09-15"],
mcp_servers: mcpServers,
tools: [
{
type: "mcp_toolset",
mcp_server_name: "example-mcp",
tools: listing.tools
}
],
messages
});
// With a pinned toolset, the response has no mcp_tool_listing block.
console.log(second.content.map((block) => block.type));
using Anthropic.Models.Beta;
using Anthropic.Models.Beta.Messages;
using Messages = Anthropic.Models.Messages;
AnthropicClient client = new();
List<BetaRequestMcpServerUrlDefinition> mcpServers =
[
new()
{
Url = "https://example-server.modelcontextprotocol.io/sse",
Name = "example-mcp",
AuthorizationToken = "YOUR_TOKEN",
},
];
List<BetaMessageParam> messages =
[
new() { Role = Role.User, Content = "What tools do you have available?" },
];
// First request: the toolset isn't pinned, so the API asks the server for
// its tools and the response starts with an mcp_tool_listing block.
var first = await client.Beta.Messages.Create(new MessageCreateParams
{
Model = Messages::Model.ClaudeOpus5_5,
MaxTokens = 1024,
Betas = [AnthropicBeta.McpClient2026_09_15],
McpServers = mcpServers,
Tools = [new BetaMcpToolset("example-mcp")],
Messages = messages,
});
var listing = first.Content
.Select(block => block.Value)
.OfType<BetaMcpToolListingBlock>()
.First();
Console.WriteLine(string.Join(", ", listing.Tools.Select(tool => tool.Name)));
// Pin the list: copy the block's tools into the toolset. The API uses
// exactly these entries and doesn't ask the server again.
var second = await client.Beta.Messages.Create(new MessageCreateParams
{
Model = Messages::Model.ClaudeOpus5_5,
MaxTokens = 1024,
Betas = [AnthropicBeta.McpClient2026_09_15],
McpServers = mcpServers,
Tools =
[
new BetaMcpToolset("example-mcp")
{
Tools =
[
.. listing.Tools.Select(tool => new BetaMcpToolParam
{
Name = tool.Name,
Description = tool.Description,
InputSchema = tool.InputSchema,
}),
],
},
],
Messages = messages,
});
// With a pinned toolset, the response has no mcp_tool_listing block.
Console.WriteLine(string.Join(", ", second.Content.Select(block => block.Type)));
client := anthropic.NewClient()
mcpServers := []anthropic.BetaRequestMCPServerURLDefinitionParam{
{
URL: "https://example-server.modelcontextprotocol.io/sse",
Name: "example-mcp",
AuthorizationToken: anthropic.String("YOUR_TOKEN"),
},
}
messages := []anthropic.BetaMessageParam{
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("What tools do you have available?")),
}
// First request: the toolset isn't pinned, so the API asks the server for
// its tools and the response starts with an mcp_tool_listing block.
first, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaMCPClient2026_09_15},
MCPServers: mcpServers,
Tools: []anthropic.BetaToolUnionParam{
{OfMCPToolset: &anthropic.BetaMCPToolsetParam{MCPServerName: "example-mcp"}},
},
Messages: messages,
})
if err != nil {
log.Fatal(err)
}
var listing anthropic.BetaMCPToolListingBlock
for _, block := range first.Content {
if listingBlock, ok := block.AsAny().(anthropic.BetaMCPToolListingBlock); ok {
listing = listingBlock
break
}
}
// Pin the list: copy the block's tools into the toolset. The API uses
// exactly these entries and doesn't ask the server again.
var toolNames []string
var pinnedTools []anthropic.BetaMCPToolParam
for _, tool := range listing.Tools {
toolNames = append(toolNames, tool.Name)
pinnedTools = append(pinnedTools, anthropic.BetaMCPToolParam{
Name: tool.Name,
Description: anthropic.String(tool.Description),
InputSchema: tool.InputSchema,
})
}
fmt.Println(toolNames)
second, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaMCPClient2026_09_15},
MCPServers: mcpServers,
Tools: []anthropic.BetaToolUnionParam{
{OfMCPToolset: &anthropic.BetaMCPToolsetParam{
MCPServerName: "example-mcp",
Tools: pinnedTools,
}},
},
Messages: messages,
})
if err != nil {
log.Fatal(err)
}
// With a pinned toolset, the response has no mcp_tool_listing block.
var blockTypes []string
for _, block := range second.Content {
blockTypes = append(blockTypes, block.Type)
}
fmt.Println(blockTypes)
import com.anthropic.models.beta.AnthropicBeta;
import com.anthropic.models.beta.messages.BetaMcpTool;
import com.anthropic.models.beta.messages.BetaMcpToolListingBlock;
import com.anthropic.models.beta.messages.BetaMcpToolset;
import com.anthropic.models.beta.messages.BetaMessage;
import com.anthropic.models.beta.messages.BetaRequestMcpServerUrlDefinition;
import com.anthropic.models.beta.messages.MessageCreateParams;
// ...
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
BetaRequestMcpServerUrlDefinition mcpServer = BetaRequestMcpServerUrlDefinition.builder()
.url("https://example-server.modelcontextprotocol.io/sse")
.name("example-mcp")
.authorizationToken("YOUR_TOKEN")
.build();
// First request: the toolset isn't pinned, so the API asks the server for
// its tools and the response starts with an mcp_tool_listing block.
BetaMessage first = client.beta().messages().create(MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024)
.addBeta(AnthropicBeta.MCP_CLIENT_2026_09_15)
.addMcpServer(mcpServer)
.addTool(BetaMcpToolset.builder()
.mcpServerName("example-mcp")
.build())
.addUserMessage("What tools do you have available?")
.build());
BetaMcpToolListingBlock listing = first.content().stream()
.flatMap(block -> block.mcpToolListing().stream())
.findFirst()
.orElseThrow();
IO.println(listing.tools().stream().map(BetaMcpTool::name).toList());
// Pin the list: copy the block's tools into the toolset. The API uses
// exactly these entries and doesn't ask the server again.
BetaMessage second = client.beta().messages().create(MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024)
.addBeta(AnthropicBeta.MCP_CLIENT_2026_09_15)
.addMcpServer(mcpServer)
.addTool(BetaMcpToolset.builder()
.mcpServerName("example-mcp")
.tools(listing.tools().stream().map(BetaMcpTool::toParam).toList())
.build())
.addUserMessage("What tools do you have available?")
.build());
// With a pinned toolset, the response has no mcp_tool_listing block.
IO.println(second.content().stream()
.map(block -> block.type().asString())
.toList());
}
use Anthropic\Beta\AnthropicBeta;
use Anthropic\Beta\Messages\BetaMCPTool;
use Anthropic\Beta\Messages\BetaMCPToolListingBlock;
// ...
$client = new Client();
$mcpServers = [
[
'type' => 'url',
'url' => 'https://example-server.modelcontextprotocol.io/sse',
'name' => 'example-mcp',
'authorization_token' => 'YOUR_TOKEN',
],
];
$messages = [['role' => 'user', 'content' => 'What tools do you have available?']];
// First request: the toolset isn't pinned, so the API asks the server for
// its tools and the response starts with an mcp_tool_listing block.
$first = $client->beta->messages->create(
model: Model::CLAUDE_OPUS_5_5,
maxTokens: 1024,
betas: [AnthropicBeta::MCP_CLIENT_2026_09_15],
mcpServers: $mcpServers,
tools: [['type' => 'mcp_toolset', 'mcp_server_name' => 'example-mcp']],
messages: $messages,
);
$listing = array_find($first->content, fn ($block) => $block instanceof BetaMCPToolListingBlock);
echo json_encode(array_map(fn (BetaMCPTool $tool) => $tool->name, $listing->tools)), PHP_EOL;
// Pin the list: copy the block's tools into the toolset. The API uses
// exactly these entries and doesn't ask the server again.
$second = $client->beta->messages->create(
model: Model::CLAUDE_OPUS_5_5,
maxTokens: 1024,
betas: [AnthropicBeta::MCP_CLIENT_2026_09_15],
mcpServers: $mcpServers,
tools: [
[
'type' => 'mcp_toolset',
'mcp_server_name' => 'example-mcp',
'tools' => array_map(
fn (BetaMCPTool $tool) => [
'name' => $tool->name,
'description' => $tool->description,
'input_schema' => $tool->inputSchema,
],
$listing->tools,
),
],
],
messages: $messages,
);
// With a pinned toolset, the response has no mcp_tool_listing block.
echo json_encode(array_map(fn ($block) => $block->type, $second->content)), PHP_EOL;
client = Anthropic::Client.new
mcp_servers = [
{
type: "url",
url: "https://example-server.modelcontextprotocol.io/sse",
name: "example-mcp",
authorization_token: "YOUR_TOKEN"
}
]
messages = [{ role: "user", content: "What tools do you have available?" }]
# First request: the toolset isn't pinned, so the API asks the server for
# its tools and the response starts with an mcp_tool_listing block.
first = client.beta.messages.create(
model: Anthropic::Model::CLAUDE_OPUS_5_5,
max_tokens: 1024,
betas: [Anthropic::AnthropicBeta::MCP_CLIENT_2026_09_15],
mcp_servers:,
tools: [{ type: "mcp_toolset", mcp_server_name: "example-mcp" }],
messages:
)
listing = first.content.find { it.is_a?(Anthropic::Beta::BetaMCPToolListingBlock) }
puts listing.tools.map(&:name).inspect
# Pin the list: copy the block's tools into the toolset. The API uses
# exactly these entries and doesn't ask the server again.
second = client.beta.messages.create(
model: Anthropic::Model::CLAUDE_OPUS_5_5,
max_tokens: 1024,
betas: [Anthropic::AnthropicBeta::MCP_CLIENT_2026_09_15],
mcp_servers:,
tools: [
{
type: "mcp_toolset",
mcp_server_name: "example-mcp",
tools: listing.tools.map(&:to_h)
}
],
messages:
)
# With a pinned toolset, the response has no mcp_tool_listing block.
puts second.content.map(&:type).inspect
inline-tools-2026-09-15 베타 헤더도 함께 쓰면 대화 도중 MCP 서버를 추가할 수 있어요. 대화 중 MCP 서버 추가하기를 참고하세요.
여러 MCP 서버 (Multiple MCP servers)
mcp_servers에 여러 서버 정의를 포함하고 tools 배열에 각각에 해당하는 MCPToolset를 포함하면 여러 MCP 서버에 연결할 수 있어요:
{
"model": "claude-opus-5-5",
"max_tokens": 1000,
"messages": [
{
"role": "user",
"content": "Use tools from both mcp-server-1 and mcp-server-2 to complete this task"
}
],
"mcp_servers": [
{
"type": "url",
"url": "https://mcp.example1.com/sse",
"name": "mcp-server-1",
"authorization_token": "TOKEN1"
},
{
"type": "url",
"url": "https://mcp.example2.com/sse",
"name": "mcp-server-2",
"authorization_token": "TOKEN2"
}
],
"tools": [
{
"type": "mcp_toolset",
"mcp_server_name": "mcp-server-1"
},
{
"type": "mcp_toolset",
"mcp_server_name": "mcp-server-2",
"default_config": {
"defer_loading": true
}
}
]
}
도구가 많으면 Claude는 도구 이름과 설명을 기준으로 선택해요. 명확하고 구체적인 도구 설명이 선택 정확도를 높여요. 큰 도구셋(여러 서버에 걸친 수십 개의 도구)에서는 Tool search 도구와 함께 defer_loading을 활성화해서 쿼리마다 관련 도구만 노출되게 하는 것을 고려하세요.
인증 (Authentication)
OAuth 인증을 요구하는 MCP 서버의 경우 액세스 토큰을 얻어야 해요. MCP 커넥터 베타는 MCP 서버 정의에서 authorization_token 파라미터 전달을 지원해요. API 소비자는 OAuth 흐름을 처리하고 API 호출 전에 액세스 토큰을 얻고, 필요에 따라 토큰을 갱신해야 해요.
테스트용 액세스 토큰 얻기 (Obtaining an access token for testing)
MCP inspector가 테스트용 액세스 토큰을 얻는 과정을 안내해줘요.
- 다음 명령으로 inspector를 실행해요. 머신에 Node.js가 설치되어 있어야 해요.
npx @modelcontextprotocol/inspector - 왼쪽 사이드바의 Transport type에서 SSE 또는 Streamable HTTP를 선택해요.
- MCP 서버의 URL을 입력해요.
- 오른쪽 영역에서 Need to configure authentication? 뒤의 Open Auth Settings를 클릭해요.
- Quick OAuth Flow를 클릭하고 OAuth 화면에서 승인해요.
- inspector의 OAuth Flow Progress 섹션의 단계를 따라 Authentication complete에 도달할 때까지 Continue를 클릭해요.
access_token값을 복사해요.- MCP 서버 설정의
authorization_token필드에 붙여넣어요.
액세스 토큰 사용하기 (Using the access token)
앞선 OAuth 흐름 중 하나로 액세스 토큰을 얻었으면 MCP 서버 설정에서 사용할 수 있어요:
{
"mcp_servers": [
{
"type": "url",
"url": "https://example-server.modelcontextprotocol.io/sse",
"name": "authenticated-server",
"authorization_token": "YOUR_ACCESS_TOKEN_HERE"
}
]
}
OAuth 흐름에 대한 자세한 설명은 MCP 사양의 Authorization 섹션을 참고하세요.
클라이언트 측 MCP 헬퍼 (Client-side MCP helpers)
자체 MCP 클라이언트 연결을 관리한다면(예: 로컬 stdio 서버, MCP 프롬프트, MCP 리소스 사용), SDK가 MCP 타입과 Claude API 타입을 변환하는 헬퍼 함수를 제공해요. 여러분의 언어용 MCP SDK(예: TypeScript MCP SDK)를 Anthropic SDK와 함께 쓸 때 수동 변환 코드를 없애줘요.
참고 (Note) URL로 접근 가능한 원격 서버가 있고 도구 지원만 필요할 때는
mcp_serversAPI 파라미터를 사용하세요. 로컬 서버, 프롬프트, 리소스가 필요하거나 기본 SDK로 연결을 더 제어하고 싶을 때는 클라이언트 측 헬퍼를 사용하세요.
설치 (Installation)
Anthropic SDK와 MCP SDK를 모두 설치해요:
- Python — MCP 헬퍼는
mcp엑스트라에 포함되어 있으며 Python 3.10 이상이 필요해요:pip install "anthropic[mcp]" - TypeScript
npm install @anthropic-ai/sdk @modelcontextprotocol/sdk - C# — 헬퍼는 별도
Anthropic.Mcp패키지에 있고, MCP 클라이언트 자체는 공식 ModelContextProtocol 패키지에서 나와요:dotnet add package Anthropic.Mcp dotnet add package ModelContextProtocol - Go — 헬퍼는 MCP Go SDK 위에 구축된 Go SDK의
mcp하위 패키지에 있어요:go get github.com/anthropics/anthropic-sdk-go/mcp - Java — 헬퍼는 별도
anthropic-java-mcp아티팩트에 있으며 Java 17 이상이 필요해요 (기본 SDK는 Java 8 지원). 기본anthropic-java의존성과 함께 추가하세요:- Gradle:
implementation("com.anthropic:anthropic-java:2.65.0") implementation("com.anthropic:anthropic-java-mcp:2.65.0") - Maven:
<dependency> <groupId>com.anthropic</groupId> <artifactId>anthropic-java</artifactId> <version>2.65.0</version> </dependency> <dependency> <groupId>com.anthropic</groupId> <artifactId>anthropic-java-mcp</artifactId> <version>2.65.0</version> </dependency>
- Gradle:
- PHP — 헬퍼는 공식 MCP PHP SDK를 사용해요:
composer require "anthropic-ai/sdk" "guzzlehttp/guzzle:^7" "mcp/sdk" - Ruby — 헬퍼는 공식
mcpgem을 사용해요:bundle add anthropic mcp
사용 가능한 헬퍼 (Available helpers)
여러분의 언어용 헬퍼를 임포트해요:
import {
mcpTools,
mcpMessages,
mcpResourceToContent,
mcpResourceToFile
} from "@anthropic-ai/sdk/helpers/beta/mcp";
using Anthropic.Helpers.Beta;
using Anthropic.Helpers.Beta.Mcp;
import (
"github.com/anthropics/anthropic-sdk-go/mcp"
)
import com.anthropic.helpers.McpBetaTool;
import com.anthropic.mcp.BetaMcp;
use Anthropic\Lib\Tools\BetaMcp;
require "anthropic"
# The helpers are exposed on the Anthropic::Mcp module
헬퍼 이름과 정확한 시그니처는 각 언어의 규칙을 따르며, 이 표는 TypeScript 형태를 보여줘요:
| 헬퍼 | 설명 |
|---|---|
mcpTools(tools, mcpClient) |
MCP 도구를 client.beta.messages.toolRunner()에서 쓸 Claude API 도구로 변환 |
mcpMessages(messages) |
MCP 프롬프트 메시지를 Claude API 메시지 형식으로 변환 |
mcpResourceToContent(resource) |
MCP 리소스를 Claude API 콘텐츠 블록으로 변환 |
mcpResourceToFile(resource) |
MCP 리소스를 업로드용 파일 객체로 변환 |
MCP 도구 사용하기 (Use MCP tools)
도구 실행을 자동 처리하는 SDK의 tool runner와 함께 쓰도록 MCP 도구를 변환해요:
client = AsyncAnthropic()
async def main() -> None: # Connect to an MCP server server_params = StdioServerParameters(command="mcp-server") async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as mcp_client: await mcp_client.initialize()
# List tools and convert them for the Claude API
tools_result = await mcp_client.list_tools()
runner = client.beta.messages.tool_runner(
model="claude-opus-5-5",
max_tokens=1024,
messages=[
{"role": "user", "content": "What tools do you have available?"},
],
tools=[async_mcp_tool(tool, mcp_client) for tool in tools_result.tools],
)
final_message = await runner.until_done()
print(final_message)
asyncio.run(main())
```typescript TypeScript
import {
mcpTools,
type MCPCallToolResultLike,
type MCPClientLike
} from "@anthropic-ai/sdk/helpers/beta/mcp";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const anthropic = new Anthropic();
// Connect to an MCP server
const transport = new StdioClientTransport({ command: "mcp-server", args: [] });
const mcpClient = new Client({ name: "my-client", version: "1.0.0" });
await mcpClient.connect(transport);
// List tools and convert them for the Claude API
const { tools } = await mcpClient.listTools();
// The MCP SDK's callTool return type still includes a legacy result shape that
// mcpTools does not accept; narrow it. Drop this once MCPClientLike widens.
const mcpClientForTools: MCPClientLike = {
callTool: (params) => mcpClient.callTool(params) as Promise<MCPCallToolResultLike>
};
const finalMessage = await anthropic.beta.messages.toolRunner({
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [{ role: "user", content: "What tools do you have available?" }],
tools: mcpTools(tools, mcpClientForTools)
});
console.log(finalMessage);
using Anthropic.Helpers.Beta;
using Anthropic.Helpers.Beta.Mcp;
using Anthropic.Models.Beta.Messages;
using ModelContextProtocol.Client;
using Messages = Anthropic.Models.Messages;
var anthropic = new AnthropicClient();
// Connect to an MCP server
await using var mcpClient = await McpClient.CreateAsync(
new StdioClientTransport(new StdioClientTransportOptions { Command = "mcp-server" })
);
// List tools and convert them for the Claude API
var tools = await BetaMcp.ListToolsAsync(mcpClient);
var runner = anthropic.Beta.Messages.ToolRunner(
new MessageCreateParams
{
Model = Messages::Model.ClaudeOpus5_5,
MaxTokens = 1024,
Messages =
[
new BetaMessageParam
{
Role = Role.User,
Content = "What tools do you have available?",
},
],
},
tools
);
var finalMessage = await runner.RunUntilDoneAsync();
Console.WriteLine(finalMessage);
import (
// ...
// ...
"github.com/anthropics/anthropic-sdk-go/mcp"
mcpsdk "github.com/modelcontextprotocol/go-sdk/mcp"
)
func main() {
client := anthropic.NewClient()
ctx := context.Background()
// Connect to an MCP server
mcpClient := mcpsdk.NewClient(&mcpsdk.Implementation{Name: "my-client", Version: "1.0.0"}, nil)
session, err := mcpClient.Connect(ctx, &mcpsdk.CommandTransport{Command: exec.Command("mcp-server")}, nil)
if err != nil {
log.Fatal(err)
}
defer session.Close()
// List tools and convert them for the Claude API
toolsResult, err := session.ListTools(ctx, nil)
if err != nil {
log.Fatal(err)
}
betaTools, err := mcp.NewBetaTools(toolsResult.Tools, session)
if err != nil {
log.Fatal(err)
}
runner := client.Beta.Messages.NewToolRunner(betaTools, anthropic.BetaToolRunnerParams{
BetaMessageNewParams: anthropic.BetaMessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Messages: []anthropic.BetaMessageParam{
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("What tools do you have available?")),
},
},
})
finalMessage, err := runner.RunToCompletion(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Println(finalMessage.RawJSON())
}
import com.anthropic.helpers.BetaToolRunner;
import com.anthropic.helpers.McpBetaTool;
import com.anthropic.mcp.BetaMcp;
import com.anthropic.models.beta.messages.BetaMessage;
import com.anthropic.models.beta.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import io.modelcontextprotocol.json.McpJsonDefaults;
import io.modelcontextprotocol.spec.McpSchema;
// ...
void main() throws Exception {
AnthropicClient anthropic = AnthropicOkHttpClient.fromEnv();
// Connect to an MCP server
StdioClientTransport transport = new StdioClientTransport(
ServerParameters.builder("mcp-server").build(), McpJsonDefaults.getMapper());
try (McpSyncClient mcpClient = McpClient.sync(transport)
.clientInfo(new McpSchema.Implementation("my-client", "1.0.0"))
.build()) {
mcpClient.initialize();
// List tools and convert them for the Claude API
List<McpBetaTool> betaTools = BetaMcp.mcpTools(mcpClient.listTools().tools(), mcpClient);
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024L)
.addUserMessage("What tools do you have available?")
.addTools(betaTools)
.build();
// The runner yields one message per assistant turn; the last is the final response
BetaToolRunner runner = anthropic.beta().messages().toolRunner(params);
BetaMessage finalMessage = null;
for (BetaMessage message : runner) {
finalMessage = message;
}
IO.println(finalMessage);
}
}
use Anthropic\Lib\Tools\BetaMcp;
use Mcp\Client;
use Mcp\Client\Transport\HttpTransport;
$anthropic = new Anthropic();
// Connect to an MCP server. The PHP MCP client connects over HTTP; point this
// at your server's endpoint.
$mcp = Client::builder()->build();
$mcp->connect(new HttpTransport('http://localhost:8000/mcp'));
// List tools and convert them for the Claude API
$runner = $anthropic->beta->messages->toolRunner(
maxTokens: 1024,
messages: [['role' => 'user', 'content' => 'What tools do you have available?']],
model: 'claude-opus-5-5',
tools: BetaMcp::tools($mcp->listTools()->tools, $mcp),
);
echo $runner->runUntilDone(), "\n";
require "mcp"
anthropic = Anthropic::Client.new
# Connect to an MCP server
transport = MCP::Client::Stdio.new(command: "mcp-server")
mcp_client = MCP::Client.new(transport: transport)
mcp_client.connect
# List tools and convert them for the Claude API
runner = anthropic.beta.messages.tool_runner(
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [{ role: "user", content: "What tools do you have available?" }],
tools: Anthropic::Mcp.tools(mcp_client.tools, mcp_client)
)
final_message = runner.run_until_finished.last
puts final_message
MCP 프롬프트 사용하기 (Use MCP prompts)
MCP 프롬프트 메시지를 Claude API 메시지 형식으로 변환해요:
prompt = await mcp_client.get_prompt(name="my-prompt") response = await client.beta.messages.create( model="claude-opus-5-5", max_tokens=1024, messages=[mcp_message(message) for message in prompt.messages], )
print(response)
```typescript TypeScript
import { mcpMessages } from "@anthropic-ai/sdk/helpers/beta/mcp";
const { messages } = await mcpClient.getPrompt({ name: "my-prompt" });
const response = await anthropic.beta.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
messages: mcpMessages(messages)
});
console.log(response);
var prompt = await mcpClient.GetPromptAsync("my-prompt");
var response = await anthropic.Beta.Messages.Create(
new MessageCreateParams
{
Model = Messages::Model.ClaudeOpus5_5,
MaxTokens = 1024,
Messages = BetaMcp.Messages(prompt.Messages),
}
);
Console.WriteLine(response);
prompt, err := session.GetPrompt(ctx, &mcpsdk.GetPromptParams{Name: "my-prompt"})
if err != nil {
log.Fatal(err)
}
messages := make([]anthropic.BetaMessageParam, 0, len(prompt.Messages))
for _, promptMessage := range prompt.Messages {
message, err := mcp.ToMessage(promptMessage)
if err != nil {
log.Fatal(err)
}
messages = append(messages, message)
}
response, err := client.Beta.Messages.New(ctx, anthropic.BetaMessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Messages: messages,
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response.RawJSON())
McpSchema.GetPromptResult prompt = mcpClient.getPrompt(
new McpSchema.GetPromptRequest("my-prompt", Map.of()));
BetaMessage response = anthropic.beta().messages().create(MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024L)
.messages(BetaMcp.mcpMessages(prompt.messages()))
.build());
IO.println(response);
$prompt = $mcp->getPrompt('my-prompt');
$response = $anthropic->beta->messages->create(
maxTokens: 1024,
messages: array_map(BetaMcp::message(...), $prompt->messages),
model: 'claude-opus-5-5',
);
echo $response, "\n";
prompt = mcp_client.get_prompt(name: "my-prompt")
response = anthropic.beta.messages.create(
model: "claude-opus-5-5",
max_tokens: 1024,
messages: prompt["messages"].map { |message| Anthropic::Mcp.message(message) }
)
puts response
MCP 리소스 사용하기 (Use MCP resources)
MCP 리소스를 메시지에 포함할 콘텐츠 블록으로, 또는 업로드용 파일 객체로 변환해요:
As a content block in a message
resource = await mcp_client.read_resource(uri="file:///path/to/doc.txt") response = await client.beta.messages.create( model="claude-opus-5-5", max_tokens=1024, messages=[ { "role": "user", "content": [ mcp_resource_to_content(resource), {"type": "text", "text": "Summarize this document"}, ], } ], ) print(response)
As a file upload
file_resource = await mcp_client.read_resource( uri="file:///path/to/data.json", ) uploaded = await client.files.upload( file=mcp_resource_to_file(file_resource), ) print(uploaded.id)
```typescript TypeScript
import { mcpResourceToContent, mcpResourceToFile } from "@anthropic-ai/sdk/helpers/beta/mcp";
// As a content block in a message
const resource = await mcpClient.readResource({ uri: "file:///path/to/doc.txt" });
const response = await anthropic.beta.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
mcpResourceToContent(resource),
{ type: "text", text: "Summarize this document" }
]
}
]
});
console.log(response);
// As a file upload
const fileResource = await mcpClient.readResource({ uri: "file:///path/to/data.json" });
const uploaded = await anthropic.files.upload({ file: mcpResourceToFile(fileResource) });
console.log(uploaded.id);
// As a content block in a message
var resource = await mcpClient.ReadResourceAsync("file:///path/to/doc.txt");
var response = await anthropic.Beta.Messages.Create(
new MessageCreateParams
{
Model = Messages::Model.ClaudeOpus5_5,
MaxTokens = 1024,
Messages =
[
new BetaMessageParam
{
Role = Role.User,
Content = new BetaMessageParamContent(
[
BetaMcp.ResourceToContent(resource),
new BetaTextBlockParam { Text = "Summarize this document" },
]
),
},
],
}
);
Console.WriteLine(response);
// As a file upload
var fileResource = await mcpClient.ReadResourceAsync("file:///path/to/data.json");
var (filename, data, mediaType) = BetaMcp.ResourceToFile(fileResource);
// Build the file part explicitly so the resource's filename and MIME type
// carry through to the upload.
var file = new BinaryContent { Stream = new MemoryStream(data), FileName = filename };
if (mediaType is not null)
{
file.ContentType = new(mediaType);
}
var uploaded = await anthropic.Files.Upload(new FileUploadParams { File = file });
Console.WriteLine(uploaded.ID);
// As a content block in a message
resource, err := session.ReadResource(ctx, &mcpsdk.ReadResourceParams{URI: "file:///path/to/doc.txt"})
if err != nil {
log.Fatal(err)
}
block, err := mcp.ResourceToBlock(resource)
if err != nil {
log.Fatal(err)
}
response, err := client.Beta.Messages.New(ctx, anthropic.BetaMessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Messages: []anthropic.BetaMessageParam{
anthropic.NewBetaUserMessage(
// ResourceToBlock returns the tool-result content union; message
// content is a separate union type, so re-wrap the shared variants
// (mcp.ToMessage does the same internally).
anthropic.BetaContentBlockParamUnion{
OfText: block.OfText,
OfImage: block.OfImage,
OfDocument: block.OfDocument,
},
anthropic.NewBetaTextBlock("Summarize this document"),
),
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response.RawJSON())
// As a file upload
fileResult, err := session.ReadResource(ctx, &mcpsdk.ReadResourceParams{URI: "file:///path/to/data.json"})
if err != nil {
log.Fatal(err)
}
fileReader, err := mcp.ResourceToFile(fileResult)
if err != nil {
log.Fatal(err)
}
uploaded, err := client.Files.Upload(ctx, anthropic.FileUploadParams{File: fileReader})
if err != nil {
log.Fatal(err)
}
fmt.Println(uploaded.ID)
// As a content block in a message
McpSchema.ReadResourceResult resource = mcpClient.readResource(
new McpSchema.ReadResourceRequest("file:///path/to/doc.txt"));
List<BetaContentBlockParam> content =
new ArrayList<>(BetaMcp.mcpResourceContents(resource));
content.add(BetaContentBlockParam.ofText(
BetaTextBlockParam.builder().text("Summarize this document").build()));
BetaMessage response = anthropic.beta().messages().create(MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024L)
.addUserMessageOfBetaContentBlockParams(content)
.build());
IO.println(response);
// As a file upload
McpSchema.ReadResourceResult fileResource = mcpClient.readResource(
new McpSchema.ReadResourceRequest("file:///path/to/data.json"));
McpResourceFile resourceFile = BetaMcp.mcpResourceFiles(fileResource).getFirst();
// Build the file part explicitly so the resource's filename and MIME type
// carry through to the upload.
MultipartField.Builder<InputStream> fileField = MultipartField.<InputStream>builder()
.value(new ByteArrayInputStream(resourceFile.content()))
.filename(resourceFile.filename());
if (resourceFile.mimeType() != null) {
fileField.contentType(resourceFile.mimeType());
}
var uploaded = anthropic.files().upload(FileUploadParams.builder()
.file(fileField.build())
.build());
IO.println(uploaded.id());
// As a content block in a message
$resource = $mcp->readResource('file:///path/to/doc.txt');
$response = $anthropic->beta->messages->create(
maxTokens: 1024,
messages: [
[
'role' => 'user',
'content' => [
BetaMcp::resourceToContent($resource),
['type' => 'text', 'text' => 'Summarize this document'],
],
],
],
model: 'claude-opus-5-5',
);
echo $response, "\n";
// As a file upload
$fileResource = $mcp->readResource('file:///path/to/data.json');
$file = $anthropic->files->upload(file: BetaMcp::resourceToFile($fileResource));
echo $file->id, "\n";
# As a content block in a message
resource = mcp_client.read_resource(uri: "file:///path/to/doc.txt")
response = anthropic.beta.messages.create(
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
*Anthropic::Mcp.resource_to_contents(resource),
{ type: "text", text: "Summarize this document" }
]
}
]
)
puts response
# As a file upload
file_resource = mcp_client.read_resource(uri: "file:///path/to/data.json")
file = Anthropic::Mcp.resource_to_files(file_resource).first
uploaded_file = anthropic.files.upload(file: file)
puts uploaded_file.id
오류 처리 (Error handling)
변환 함수는 MCP 값이 Claude API에서 지원되지 않으면 UnsupportedMCPValueError를 던져요 (Go에서는 헬퍼가 UnsupportedValueError를 반환하고, Java와 C#에서는 AnthropicInvalidDataException을 던져요). 지원되지 않는 콘텐츠 타입, MIME 타입, 리소스 링크에서 발생할 수 있어요 (변환 전에 MCP 클라이언트로 리소스 링크를 해석하세요).
배치 요청 (Batch requests)
mcp_servers를 Message Batches API 요청에 포함할 수 있어요. Batches API를 통한 MCP 도구 호출은 일반 Messages API 요청과 동일하게 가격이 책정돼요.
데이터 보존 (Data retention)
MCP 커넥터는 ZDR 협정에 포함되지 않아요. MCP 서버와 교환되는 데이터(도구 정의와 실행 결과 포함)는 Anthropic의 표준 데이터 보존 정책에 따라 보존돼요.
모든 기능의 ZDR 자격에 대해서는 API 및 데이터 보존을 참고하세요.
마이그레이션 가이드 (Migration guide)
폐지된 mcp-client-2025-04-04 베타 헤더를 사용 중이라면 이 가이드에 따라 새 버전으로 마이그레이션하세요.
주요 변경 사항 (Key changes)
- 새 베타 헤더:
mcp-client-2025-04-04에서mcp-client-2025-11-20으로 변경 - 도구 구성 이동: 도구 구성은 이제 MCP 서버 정의가 아니라
tools배열의 MCPToolset 객체에 있어요. - 더 유연한 구성: 허용 목록, 차단 목록, 도구별 구성을 지원하는 새 패턴
마이그레이션 단계 (Migration steps)
이전 (폐지됨):
{
"model": "claude-opus-5-5",
"max_tokens": 1000,
"messages": [
// ...
],
"mcp_servers": [
{
"type": "url",
"url": "https://mcp.example.com/sse",
"name": "example-mcp",
"authorization_token": "YOUR_TOKEN",
"tool_configuration": {
"enabled": true,
"allowed_tools": ["tool1", "tool2"]
}
}
]
}
이후 (현재):
{
"model": "claude-opus-5-5",
"max_tokens": 1000,
"messages": [
// ...
],
"mcp_servers": [
{
"type": "url",
"url": "https://mcp.example.com/sse",
"name": "example-mcp",
"authorization_token": "YOUR_TOKEN"
}
],
"tools": [
{
"type": "mcp_toolset",
"mcp_server_name": "example-mcp",
"default_config": {
"enabled": false
},
"configs": {
"tool1": {
"enabled": true
},
"tool2": {
"enabled": true
}
}
}
]
}
일반적인 마이그레이션 패턴 (Common migration patterns)
| 이전 패턴 | 새 패턴 |
|---|---|
tool_configuration 없음 (모든 도구 활성화) |
default_config나 configs 없는 MCPToolset |
tool_configuration.enabled: false |
default_config.enabled: false인 MCPToolset |
tool_configuration.allowed_tools: [...] |
default_config.enabled: false이고 특정 도구가 configs에서 활성화된 MCPToolset |
폐지된 버전: mcp-client-2025-04-04 (Deprecated version: mcp-client-2025-04-04)
경고 (Warning) 이 버전은 폐지됐어요. 앞선 마이그레이션 가이드를 사용해
mcp-client-2025-11-20으로 마이그레이션하세요.
MCP 커넥터의 이전 버전은 도구 구성을 MCP 서버 정의에 직접 포함했어요:
{
"mcp_servers": [
{
"type": "url",
"url": "https://example-server.modelcontextprotocol.io/sse",
"name": "example-mcp",
"authorization_token": "YOUR_TOKEN",
"tool_configuration": {
"enabled": true,
"allowed_tools": ["example_tool_1", "example_tool_2"]
}
}
]
}
폐지된 필드 설명 (Deprecated field descriptions)
| 속성 | 타입 | 설명 |
|---|---|---|
tool_configuration |
object | 폐지됨: tools 배열의 MCPToolset를 사용하세요. |
tool_configuration.enabled |
boolean | 폐지됨: MCPToolset에서 default_config.enabled를 사용하세요. |
tool_configuration.allowed_tools |
array | 폐지됨: MCPToolset에서 configs로 허용 목록 패턴을 사용하세요. |
더 알아보기 (Learn more)
- MCP 커넥터 마이그레이션 — 이전 MCP 커넥터 버전에서 마이그레이션하는 방법
- 대화 중 MCP 서버 추가하기 — 대화 도중 MCP 서버 추가 (beta)
- 원격 MCP 서버 (Remote MCP servers) — 연결할 수 있는 원격 MCP 서버 예시