## Pin an MCP server's tool list (beta)
The whole hunk
from line 61, old and new numbered
/
lines
The two sides of this change are more than 400 edits apart, too far apart to line up, so this is the differ's own diff of it and the words inside a line are not marked.
from line 61
6161 -H "anthropic-version: 2023-06-01" \
6262 -H "anthropic-beta: mcp-client-2025-11-20" \
6363 -d '{
64 "model": "claude-opus-5",
64 "model": "claude-opus-5-5",
6565 "max_tokens": 1000,
6666 "messages": [{"role": "user", "content": "What tools do you have available?"}],
6767 "mcp_servers": [
from line 83
8383
8484 ```bash CLI
8585 ant beta:messages create --beta mcp-client-2025-11-20 <<'YAML'
86 model: claude-opus-5
86 model: claude-opus-5-5
8787 max_tokens: 1000
8888 messages:
8989 - role: user
from line 103
103103 client = anthropic.Anthropic()
104104
105105 response = client.beta.messages.create(
106 model="claude-opus-5",
106 model="claude-opus-5-5",
107107 max_tokens=1000,
108108 messages=[{"role": "user", "content": "What tools do you have available?"}],
109109 mcp_servers=[
from line 125
125125 const anthropic = new Anthropic();
126126
127127 const response = await anthropic.beta.messages.create({
128 model: "claude-opus-5",
128 model: "claude-opus-5-5",
129129 max_tokens: 1000,
130130 messages: [
131131 {
from line 158
158158
159159 var parameters = new MessageCreateParams
160160 {
161 Model = Model.ClaudeOpus5,
161 Model = Model.ClaudeOpus5_5,
162162 MaxTokens = 1000,
163163 Messages = new List<BetaMessageParam>
164164 {
from line 188
188188 client := anthropic.NewClient()
189189
190190 response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
191 Model: anthropic.ModelClaudeOpus5,
191 Model: anthropic.ModelClaudeOpus5_5,
192192 MaxTokens: 1000,
193193 Messages: []anthropic.BetaMessageParam{
194194 anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("What tools do you have available?")),
from line 225
225225 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
226226
227227 MessageCreateParams params = MessageCreateParams.builder()
228 .model(Model.CLAUDE_OPUS_5)
228 .model(Model.CLAUDE_OPUS_5_5)
229229 .maxTokens(1000L)
230230 .addUserMessage("What tools do you have available?")
231231 .addMcpServer(BetaRequestMcpServerUrlDefinition.builder()
from line 252
252252 messages: [
253253 ['role' => 'user', 'content' => 'What tools do you have available?']
254254 ],
255 model: 'claude-opus-5',
255 model: 'claude-opus-5-5',
256256 mcpServers: [
257257 [
258258 'type' => 'url',
from line 277
277277 client = Anthropic::Client.new
278278
279279 response = client.beta.messages.create(
280 model: "claude-opus-5",
280 model: "claude-opus-5-5",
281281 max_tokens: 1000,
282282 messages: [
283283 { role: "user", content: "What tools do you have available?" }
from line 358
358358| `configs` | object | No | Per-tool configuration overrides. Keys are tool names, values are configuration objects. |
359359| `cache_control` | object | No | [Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) cache breakpoint configuration for this toolset. |
360360
361With the `mcp-client-2026-09-15` beta header, an MCPToolset also accepts `tools`, a pinned copy of the server's tool list. See [Pin an MCP server's tool list](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector#pin-mcp-tool-list).
362
361363### Tool configuration options
362364
363365Each tool (whether configured in `default_config` or in `configs`) supports the following fields:
from line 526
524526}
525527```
526528
527## Multiple MCP servers
528
529You can connect to multiple MCP servers by including multiple server definitions in `mcp_servers` and a corresponding MCPToolset for each in the `tools` array:
529## Pin an MCP server's tool list (beta)
530
531An MCP server can change its tools at any time. The `mcp-client-2026-09-15` beta header records the tool list each server returns and lets you pin it, so a server that changes its tools doesn't change what Claude sees partway through a conversation. It includes everything `mcp-client-2025-11-20` does, so send it in place of that header. It's available on the Claude API.
532
533When the API asks an MCP server for its tools while producing a response, the response starts with an `mcp_tool_listing` block for that server, one block for each server it asked:
530534
531535```json
532536{
533 "model": "claude-opus-5",
537 "type": "mcp_tool_listing",
538 "mcp_server_name": "example-mcp",
539 "tools": [
540 {
541 "name": "echo",
542 "description": "Returns the text it receives.",
543 "input_schema": {
544 "type": "object",
545 "properties": { "text": { "type": "string" } },
546 "required": ["text"]
547 }
548 }
549 ]
550}
551```
552
553If your code reads `content[0]`, skip these blocks. Send the assistant message back unchanged, `mcp_tool_listing` blocks included, and keep sending `mcp-client-2026-09-15` on every request that carries one. Later requests then use the recorded list for that server instead of asking it again.
554
555To pin a list yourself, copy a block's `tools` into the `tools` field of that server's MCPToolset. The API then doesn't ask the server for its tools, and the toolset's tools are exactly those entries, with `default_config` and `configs` applied:
556
557```json
558{
559 "type": "mcp_toolset",
560 "mcp_server_name": "example-mcp",
561 "tools": [
562 {
563 "name": "echo",
564 "description": "Returns the text it receives.",
565 "input_schema": {
566 "type": "object",
567 "properties": { "text": { "type": "string" } },
568 "required": ["text"]
569 }
570 }
571 ]
572}
573```
574
575Each entry in `tools` holds the tool's `name` as the server lists it (without the server name), its `description`, and its `input_schema`.
576
577The following example sends one request with an unpinned toolset, copies the returned list into the toolset's `tools` field, and sends the request again. The second response has no `mcp_tool_listing` block, because the API doesn't ask the server:
578
579<CodeGroup>
580 ```bash cURL
581 BODY='{
582 "model": "claude-opus-5-5",
583 "max_tokens": 1024,
584 "mcp_servers": [
585 {
586 "type": "url",
587 "url": "https://example-server.modelcontextprotocol.io/sse",
588 "name": "example-mcp",
589 "authorization_token": "YOUR_TOKEN"
590 }
591 ],
592 "tools": [
593 {
594 "type": "mcp_toolset",
595 "mcp_server_name": "example-mcp"
596 }
597 ],
598 "messages": [
599 {
600 "role": "user",
601 "content": "What tools do you have available?"
602 }
603 ]
604 }'
605
606 # First request: the toolset isn't pinned, so the API asks the server for
607 # its tools and the response starts with an mcp_tool_listing block.
608 # tee shows the response on stderr while the variable captures it.
609 FIRST=$(curl -sS https://api.anthropic.com/v1/messages \
610 -H "content-type: application/json" \
611 -H "x-api-key: $ANTHROPIC_API_KEY" \
612 -H "anthropic-version: 2023-06-01" \
613 -H "anthropic-beta: mcp-client-2026-09-15" \
614 -d "$BODY" | tee /dev/stderr)
615
616 # Pin the list: copy the block's tools into the toolset. The API uses
617 # exactly these entries and doesn't ask the server again.
618 TOOLS=$(jq '.content[] | select(.type == "mcp_tool_listing") | .tools' \
619 <<<"$FIRST")
620 PINNED=$(jq --argjson tools "$TOOLS" '.tools[0].tools = $tools' <<<"$BODY")
621
622 # With a pinned toolset, the response has no mcp_tool_listing block.
623 curl https://api.anthropic.com/v1/messages \
624 -H "content-type: application/json" \
625 -H "x-api-key: $ANTHROPIC_API_KEY" \
626 -H "anthropic-version: 2023-06-01" \
627 -H "anthropic-beta: mcp-client-2026-09-15" \
628 -d "$PINNED"
629 ```
630
631 ```bash CLI
632 request=$(cat <<'YAML'
633 model: claude-opus-5-5
634 max_tokens: 1024
635 mcp_servers:
636 - type: url
637 url: https://example-server.modelcontextprotocol.io/sse
638 name: example-mcp
639 authorization_token: YOUR_TOKEN
640 tools:
641 - type: mcp_toolset
642 mcp_server_name: example-mcp
643 messages:
644 - role: user
645 content: What tools do you have available?
646 YAML
647 )
648
649 # First request: the toolset isn't pinned, so the API asks the server for
650 # its tools and the response starts with an mcp_tool_listing block.
651 # tee shows the response on stderr while the variable captures it.
652 first=$(ant beta:messages create --beta mcp-client-2026-09-15 --format json \
653 <<<"$request" | tee /dev/stderr)
654 tools=$(jq -c '.content[] | select(.type == "mcp_tool_listing") | .tools' \
655 <<<"$first")
656
657 # Pin the list: copy the block's tools into the toolset. The --tool flag
658 # replaces the body's tools array. The API uses exactly these entries and
659 # doesn't ask the server again, so the response has no mcp_tool_listing block.
660 ant beta:messages create --beta mcp-client-2026-09-15 \
661 --tool "{type: mcp_toolset, mcp_server_name: example-mcp, tools: $tools}" \
662 <<<"$request"
663 ```
664
665 ```python Python
666 from anthropic.types.beta import (
667 BetaMessageParam,
668 BetaRequestMCPServerURLDefinitionParam,
669 )
670
671 client = anthropic.Anthropic()
672
673 mcp_servers: list[BetaRequestMCPServerURLDefinitionParam] = [
674 {
675 "type": "url",
676 "url": "https://example-server.modelcontextprotocol.io/sse",
677 "name": "example-mcp",
678 "authorization_token": "YOUR_TOKEN",
679 },
680 ]
681 messages: list[BetaMessageParam] = [
682 {"role": "user", "content": "What tools do you have available?"},
683 ]
684
685 # First request: the toolset isn't pinned, so the API asks the server for
686 # its tools and the response starts with an mcp_tool_listing block.
687 first = client.beta.messages.create(
688 model="claude-opus-5-5",
689 max_tokens=1024,
690 betas=["mcp-client-2026-09-15"],
691 mcp_servers=mcp_servers,
692 tools=[{"type": "mcp_toolset", "mcp_server_name": "example-mcp"}],
693 messages=messages,
694 )
695
696 listing = next(block for block in first.content if block.type == "mcp_tool_listing")
697 print([tool.name for tool in listing.tools])
698
699 # Pin the list: copy the block's tools into the toolset. The API uses
700 # exactly these entries and doesn't ask the server again.
701 second = client.beta.messages.create(
702 model="claude-opus-5-5",
703 max_tokens=1024,
704 betas=["mcp-client-2026-09-15"],
705 mcp_servers=mcp_servers,
706 tools=[
707 {
708 "type": "mcp_toolset",
709 "mcp_server_name": "example-mcp",
710 "tools": [
711 {
712 "name": tool.name,
713 "description": tool.description,
714 "input_schema": tool.input_schema,
715 }
716 for tool in listing.tools
717 ],
718 },
719 ],
720 messages=messages,
721 )
722
723 # With a pinned toolset, the response has no mcp_tool_listing block.
724 print([block.type for block in second.content])
725 ```
726
727 ```typescript TypeScript
728 const client = new Anthropic();
729
730 const mcpServers: Anthropic.Beta.BetaRequestMCPServerURLDefinition[] = [
731 {
732 type: "url",
733 url: "https://example-server.modelcontextprotocol.io/sse",
734 name: "example-mcp",
735 authorization_token: "YOUR_TOKEN"
736 }
737 ];
738 const messages: Anthropic.Beta.BetaMessageParam[] = [
739 { role: "user", content: "What tools do you have available?" }
740 ];
741
742 // First request: the toolset isn't pinned, so the API asks the server for
743 // its tools and the response starts with an mcp_tool_listing block.
744 const first = await client.beta.messages.create({
745 model: "claude-opus-5-5",
746 max_tokens: 1024,
747 betas: ["mcp-client-2026-09-15"],
748 mcp_servers: mcpServers,
749 tools: [{ type: "mcp_toolset", mcp_server_name: "example-mcp" }],
750 messages
751 });
752
753 const listing = first.content.find((block) => block.type === "mcp_tool_listing");
754 if (!listing) {
755 throw new Error("The response has no mcp_tool_listing block.");
756 }
757 console.log(listing.tools.map((tool) => tool.name));
758
759 // Pin the list: copy the block's tools into the toolset. The API uses
760 // exactly these entries and doesn't ask the server again.
761 const second = await client.beta.messages.create({
762 model: "claude-opus-5-5",
763 max_tokens: 1024,
764 betas: ["mcp-client-2026-09-15"],
765 mcp_servers: mcpServers,
766 tools: [
767 {
768 type: "mcp_toolset",
769 mcp_server_name: "example-mcp",
770 tools: listing.tools
771 }
772 ],
773 messages
774 });
775
776 // With a pinned toolset, the response has no mcp_tool_listing block.
777 console.log(second.content.map((block) => block.type));
778 ```
779
780 ```csharp C#
781 using Anthropic.Models.Beta;
782 using Anthropic.Models.Beta.Messages;
783 using Messages = Anthropic.Models.Messages;
784
785 AnthropicClient client = new();
786
787 List<BetaRequestMcpServerUrlDefinition> mcpServers =
788 [
789 new()
790 {
791 Url = "https://example-server.modelcontextprotocol.io/sse",
792 Name = "example-mcp",
793 AuthorizationToken = "YOUR_TOKEN",
794 },
795 ];
796 List<BetaMessageParam> messages =
797 [
798 new() { Role = Role.User, Content = "What tools do you have available?" },
799 ];
800
801 // First request: the toolset isn't pinned, so the API asks the server for
802 // its tools and the response starts with an mcp_tool_listing block.
803 var first = await client.Beta.Messages.Create(new MessageCreateParams
804 {
805 Model = Messages::Model.ClaudeOpus5_5,
806 MaxTokens = 1024,
807 Betas = [AnthropicBeta.McpClient2026_09_15],
808 McpServers = mcpServers,
809 Tools = [new BetaMcpToolset("example-mcp")],
810 Messages = messages,
811 });
812
813 var listing = first.Content
814 .Select(block => block.Value)
815 .OfType<BetaMcpToolListingBlock>()
816 .First();
817 Console.WriteLine(string.Join(", ", listing.Tools.Select(tool => tool.Name)));
818
819 // Pin the list: copy the block's tools into the toolset. The API uses
820 // exactly these entries and doesn't ask the server again.
821 var second = await client.Beta.Messages.Create(new MessageCreateParams
822 {
823 Model = Messages::Model.ClaudeOpus5_5,
824 MaxTokens = 1024,
825 Betas = [AnthropicBeta.McpClient2026_09_15],
826 McpServers = mcpServers,
827 Tools =
828 [
829 new BetaMcpToolset("example-mcp")
830 {
831 Tools =
832 [
833 .. listing.Tools.Select(tool => new BetaMcpToolParam
834 {
835 Name = tool.Name,
836 Description = tool.Description,
837 InputSchema = tool.InputSchema,
838 }),
839 ],
840 },
841 ],
842 Messages = messages,
843 });
844
845 // With a pinned toolset, the response has no mcp_tool_listing block.
846 Console.WriteLine(string.Join(", ", second.Content.Select(block => block.Type)));
847 ```
848
849 ```go Go
850 client := anthropic.NewClient()
851
852 mcpServers := []anthropic.BetaRequestMCPServerURLDefinitionParam{
853 {
854 URL: "https://example-server.modelcontextprotocol.io/sse",
855 Name: "example-mcp",
856 AuthorizationToken: anthropic.String("YOUR_TOKEN"),
857 },
858 }
859 messages := []anthropic.BetaMessageParam{
860 anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("What tools do you have available?")),
861 }
862
863 // First request: the toolset isn't pinned, so the API asks the server for
864 // its tools and the response starts with an mcp_tool_listing block.
865 first, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
866 Model: anthropic.ModelClaudeOpus5_5,
867 MaxTokens: 1024,
868 Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaMCPClient2026_09_15},
869 MCPServers: mcpServers,
870 Tools: []anthropic.BetaToolUnionParam{
871 {OfMCPToolset: &anthropic.BetaMCPToolsetParam{MCPServerName: "example-mcp"}},
872 },
873 Messages: messages,
874 })
875 if err != nil {
876 log.Fatal(err)
877 }
878
879 var listing anthropic.BetaMCPToolListingBlock
880 for _, block := range first.Content {
881 if listingBlock, ok := block.AsAny().(anthropic.BetaMCPToolListingBlock); ok {
882 listing = listingBlock
883 break
884 }
885 }
886
887 // Pin the list: copy the block's tools into the toolset. The API uses
888 // exactly these entries and doesn't ask the server again.
889 var toolNames []string
890 var pinnedTools []anthropic.BetaMCPToolParam
891 for _, tool := range listing.Tools {
892 toolNames = append(toolNames, tool.Name)
893 pinnedTools = append(pinnedTools, anthropic.BetaMCPToolParam{
894 Name: tool.Name,
895 Description: anthropic.String(tool.Description),
896 InputSchema: tool.InputSchema,
897 })
898 }
899 fmt.Println(toolNames)
900
901 second, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
902 Model: anthropic.ModelClaudeOpus5_5,
903 MaxTokens: 1024,
904 Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaMCPClient2026_09_15},
905 MCPServers: mcpServers,
906 Tools: []anthropic.BetaToolUnionParam{
907 {OfMCPToolset: &anthropic.BetaMCPToolsetParam{
908 MCPServerName: "example-mcp",
909 Tools: pinnedTools,
910 }},
911 },
912 Messages: messages,
913 })
914 if err != nil {
915 log.Fatal(err)
916 }
917
918 // With a pinned toolset, the response has no mcp_tool_listing block.
919 var blockTypes []string
920 for _, block := range second.Content {
921 blockTypes = append(blockTypes, block.Type)
922 }
923 fmt.Println(blockTypes)
924 ```
925
926 ```java Java
927 import com.anthropic.models.beta.AnthropicBeta;
928 import com.anthropic.models.beta.messages.BetaMcpTool;
929 import com.anthropic.models.beta.messages.BetaMcpToolListingBlock;
930 import com.anthropic.models.beta.messages.BetaMcpToolset;
931 import com.anthropic.models.beta.messages.BetaMessage;
932 import com.anthropic.models.beta.messages.BetaRequestMcpServerUrlDefinition;
933 import com.anthropic.models.beta.messages.MessageCreateParams;
934 // ...
935
936 void main() {
937 AnthropicClient client = AnthropicOkHttpClient.fromEnv();
938
939 BetaRequestMcpServerUrlDefinition mcpServer = BetaRequestMcpServerUrlDefinition.builder()
940 .url("https://example-server.modelcontextprotocol.io/sse")
941 .name("example-mcp")
942 .authorizationToken("YOUR_TOKEN")
943 .build();
944
945 // First request: the toolset isn't pinned, so the API asks the server for
946 // its tools and the response starts with an mcp_tool_listing block.
947 BetaMessage first = client.beta().messages().create(MessageCreateParams.builder()
948 .model(Model.CLAUDE_OPUS_5_5)
949 .maxTokens(1024)
950 .addBeta(AnthropicBeta.MCP_CLIENT_2026_09_15)
951 .addMcpServer(mcpServer)
952 .addTool(BetaMcpToolset.builder()
953 .mcpServerName("example-mcp")
954 .build())
955 .addUserMessage("What tools do you have available?")
956 .build());
957
958 BetaMcpToolListingBlock listing = first.content().stream()
959 .flatMap(block -> block.mcpToolListing().stream())
960 .findFirst()
961 .orElseThrow();
962 IO.println(listing.tools().stream().map(BetaMcpTool::name).toList());
963
964 // Pin the list: copy the block's tools into the toolset. The API uses
965 // exactly these entries and doesn't ask the server again.
966 BetaMessage second = client.beta().messages().create(MessageCreateParams.builder()
967 .model(Model.CLAUDE_OPUS_5_5)
968 .maxTokens(1024)
969 .addBeta(AnthropicBeta.MCP_CLIENT_2026_09_15)
970 .addMcpServer(mcpServer)
971 .addTool(BetaMcpToolset.builder()
972 .mcpServerName("example-mcp")
973 .tools(listing.tools().stream().map(BetaMcpTool::toParam).toList())
974 .build())
975 .addUserMessage("What tools do you have available?")
976 .build());
977
978 // With a pinned toolset, the response has no mcp_tool_listing block.
979 IO.println(second.content().stream()
980 .map(block -> block.type().asString())
981 .toList());
982 }
983 ```
984
985 ```php PHP
986 use Anthropic\Beta\AnthropicBeta;
987 use Anthropic\Beta\Messages\BetaMCPTool;
988 use Anthropic\Beta\Messages\BetaMCPToolListingBlock;
989 // ...
990
991 $client = new Client();
992
993 $mcpServers = [
994 [
995 'type' => 'url',
996 'url' => 'https://example-server.modelcontextprotocol.io/sse',
997 'name' => 'example-mcp',
998 'authorization_token' => 'YOUR_TOKEN',
999 ],
1000 ];
1001 $messages = [['role' => 'user', 'content' => 'What tools do you have available?']];
1002
1003 // First request: the toolset isn't pinned, so the API asks the server for
1004 // its tools and the response starts with an mcp_tool_listing block.
1005 $first = $client->beta->messages->create(
1006 model: Model::CLAUDE_OPUS_5_5,
1007 maxTokens: 1024,
1008 betas: [AnthropicBeta::MCP_CLIENT_2026_09_15],
1009 mcpServers: $mcpServers,
1010 tools: [['type' => 'mcp_toolset', 'mcp_server_name' => 'example-mcp']],
1011 messages: $messages,
1012 );
1013
1014 $listing = array_find($first->content, fn ($block) => $block instanceof BetaMCPToolListingBlock);
1015 echo json_encode(array_map(fn (BetaMCPTool $tool) => $tool->name, $listing->tools)), PHP_EOL;
1016
1017 // Pin the list: copy the block's tools into the toolset. The API uses
1018 // exactly these entries and doesn't ask the server again.
1019 $second = $client->beta->messages->create(
1020 model: Model::CLAUDE_OPUS_5_5,
1021 maxTokens: 1024,
1022 betas: [AnthropicBeta::MCP_CLIENT_2026_09_15],
1023 mcpServers: $mcpServers,
1024 tools: [
1025 [
1026 'type' => 'mcp_toolset',
1027 'mcp_server_name' => 'example-mcp',
1028 'tools' => array_map(
1029 fn (BetaMCPTool $tool) => [
1030 'name' => $tool->name,
1031 'description' => $tool->description,
1032 'input_schema' => $tool->inputSchema,
1033 ],
1034 $listing->tools,
1035 ),
1036 ],
1037 ],
1038 messages: $messages,
1039 );
1040
1041 // With a pinned toolset, the response has no mcp_tool_listing block.
1042 echo json_encode(array_map(fn ($block) => $block->type, $second->content)), PHP_EOL;
1043 ```
1044
1045 ```ruby Ruby
1046 client = Anthropic::Client.new
1047
1048 mcp_servers = [
1049 {
1050 type: "url",
1051 url: "https://example-server.modelcontextprotocol.io/sse",
1052 name: "example-mcp",
1053 authorization_token: "YOUR_TOKEN"
1054 }
1055 ]
1056 messages = [{ role: "user", content: "What tools do you have available?" }]
1057
1058 # First request: the toolset isn't pinned, so the API asks the server for
1059 # its tools and the response starts with an mcp_tool_listing block.
1060 first = client.beta.messages.create(
1061 model: Anthropic::Model::CLAUDE_OPUS_5_5,
1062 max_tokens: 1024,
1063 betas: [Anthropic::AnthropicBeta::MCP_CLIENT_2026_09_15],
1064 mcp_servers:,
1065 tools: [{ type: "mcp_toolset", mcp_server_name: "example-mcp" }],
1066 messages:
1067 )
1068
1069 listing = first.content.find { it.is_a?(Anthropic::Beta::BetaMCPToolListingBlock) }
1070 puts listing.tools.map(&:name).inspect
1071
1072 # Pin the list: copy the block's tools into the toolset. The API uses
1073 # exactly these entries and doesn't ask the server again.
1074 second = client.beta.messages.create(
1075 model: Anthropic::Model::CLAUDE_OPUS_5_5,
1076 max_tokens: 1024,
1077 betas: [Anthropic::AnthropicBeta::MCP_CLIENT_2026_09_15],
1078 mcp_servers:,
1079 tools: [
1080 {
1081 type: "mcp_toolset",
1082 mcp_server_name: "example-mcp",
1083 tools: listing.tools.map(&:to_h)
1084 }
1085 ],
1086 messages:
1087 )
1088
1089 # With a pinned toolset, the response has no mcp_tool_listing block.
1090 puts second.content.map(&:type).inspect
1091 ```
1092</CodeGroup>
1093
1094With the `inline-tools-2026-09-15` beta header as well, you can add an MCP server partway through a conversation. See [Add an MCP server mid-conversation](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#add-an-mcp-server-mid-conversation-beta).
1095
1096## Multiple MCP servers
1097
1098You can connect to multiple MCP servers by including multiple server definitions in `mcp_servers` and a corresponding MCPToolset for each in the `tools` array:
1099
1100```json
1101{
1102 "model": "claude-opus-5-5",
5341103 "max_tokens": 1000,
5351104 "messages": [
5361105 {
from line 1236
6671236 <Tabs>
6681237 <Tab title="Gradle">
6691238 ```kotlin
670 implementation("com.anthropic:anthropic-java:2.63.0")
671 implementation("com.anthropic:anthropic-java-mcp:2.63.0")
1239 implementation("com.anthropic:anthropic-java:2.65.0")
1240 implementation("com.anthropic:anthropic-java-mcp:2.65.0")
6721241 ```
6731242 </Tab>
6741243
from line 1246
6771246 <dependency>
6781247 <groupId>com.anthropic</groupId>
6791248 <artifactId>anthropic-java</artifactId>
680 <version>2.63.0</version>
1249 <version>2.65.0</version>
6811250 </dependency>
6821251 <dependency>
6831252 <groupId>com.anthropic</groupId>
6841253 <artifactId>anthropic-java-mcp</artifactId>
685 <version>2.63.0</version>
1254 <version>2.65.0</version>
6861255 </dependency>
6871256 ```
6881257 </Tab>
from line 1358
7891358 # List tools and convert them for the Claude API
7901359 tools_result = await mcp_client.list_tools()
7911360 runner = client.beta.messages.tool_runner(
792 model="claude-opus-5",
1361 model="claude-opus-5-5",
7931362 max_tokens=1024,
7941363 messages=[
7951364 {"role": "user", "content": "What tools do you have available?"},
from line 1399
8301399 };
8311400
8321401 const finalMessage = await anthropic.beta.messages.toolRunner({
833 model: "claude-opus-5",
1402 model: "claude-opus-5-5",
8341403 max_tokens: 1024,
8351404 messages: [{ role: "user", content: "What tools do you have available?" }],
8361405 tools: mcpTools(tools, mcpClientForTools)
from line 1427
8581427 var runner = anthropic.Beta.Messages.ToolRunner(
8591428 new MessageCreateParams
8601429 {
861 Model = Messages::Model.ClaudeOpus5,
1430 Model = Messages::Model.ClaudeOpus5_5,
8621431 MaxTokens = 1024,
8631432 Messages =
8641433 [
from line 1478
9091478
9101479 runner := client.Beta.Messages.NewToolRunner(betaTools, anthropic.BetaToolRunnerParams{
9111480 BetaMessageNewParams: anthropic.BetaMessageNewParams{
912 Model: anthropic.ModelClaudeOpus5,
1481 Model: anthropic.ModelClaudeOpus5_5,
9131482 MaxTokens: 1024,
9141483 Messages: []anthropic.BetaMessageParam{
9151484 anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("What tools do you have available?")),
from line 1527
9581527 List<McpBetaTool> betaTools = BetaMcp.mcpTools(mcpClient.listTools().tools(), mcpClient);
9591528
9601529 MessageCreateParams params = MessageCreateParams.builder()
961 .model(Model.CLAUDE_OPUS_5)
1530 .model(Model.CLAUDE_OPUS_5_5)
9621531 .maxTokens(1024L)
9631532 .addUserMessage("What tools do you have available?")
9641533 .addTools(betaTools)
from line 1560
9911560 $runner = $anthropic->beta->messages->toolRunner(
9921561 maxTokens: 1024,
9931562 messages: [['role' => 'user', 'content' => 'What tools do you have available?']],
994 model: 'claude-opus-5',
1563 model: 'claude-opus-5-5',
9951564 tools: BetaMcp::tools($mcp->listTools()->tools, $mcp),
9961565 );
9971566
from line 1579
10101579
10111580 # List tools and convert them for the Claude API
10121581 runner = anthropic.beta.messages.tool_runner(
1013 model: "claude-opus-5",
1582 model: "claude-opus-5-5",
10141583 max_tokens: 1024,
10151584 messages: [{ role: "user", content: "What tools do you have available?" }],
10161585 tools: Anthropic::Mcp.tools(mcp_client.tools, mcp_client)
from line 1600
10311600
10321601 prompt = await mcp_client.get_prompt(name="my-prompt")
10331602 response = await client.beta.messages.create(
1034 model="claude-opus-5",
1603 model="claude-opus-5-5",
10351604 max_tokens=1024,
10361605 messages=[mcp_message(message) for message in prompt.messages],
10371606 )
from line 1613
10441613
10451614 const { messages } = await mcpClient.getPrompt({ name: "my-prompt" });
10461615 const response = await anthropic.beta.messages.create({
1047 model: "claude-opus-5",
1616 model: "claude-opus-5-5",
10481617 max_tokens: 1024,
10491618 messages: mcpMessages(messages)
10501619 });
from line 1626
10571626 var response = await anthropic.Beta.Messages.Create(
10581627 new MessageCreateParams
10591628 {
1060 Model = Messages::Model.ClaudeOpus5,
1629 Model = Messages::Model.ClaudeOpus5_5,
10611630 MaxTokens = 1024,
10621631 Messages = BetaMcp.Messages(prompt.Messages),
10631632 }
from line 1651
10821651 }
10831652
10841653 response, err := client.Beta.Messages.New(ctx, anthropic.BetaMessageNewParams{
1085 Model: anthropic.ModelClaudeOpus5,
1654 Model: anthropic.ModelClaudeOpus5_5,
10861655 MaxTokens: 1024,
10871656 Messages: messages,
10881657 })
from line 1666
10971666 new McpSchema.GetPromptRequest("my-prompt", Map.of()));
10981667
10991668 BetaMessage response = anthropic.beta().messages().create(MessageCreateParams.builder()
1100 .model(Model.CLAUDE_OPUS_5)
1669 .model(Model.CLAUDE_OPUS_5_5)
11011670 .maxTokens(1024L)
11021671 .messages(BetaMcp.mcpMessages(prompt.messages()))
11031672 .build());
from line 1680
11111680 $response = $anthropic->beta->messages->create(
11121681 maxTokens: 1024,
11131682 messages: array_map(BetaMcp::message(...), $prompt->messages),
1114 model: 'claude-opus-5',
1683 model: 'claude-opus-5-5',
11151684 );
11161685
11171686 echo $response, "\n";
from line 1690
11211690 prompt = mcp_client.get_prompt(name: "my-prompt")
11221691
11231692 response = anthropic.beta.messages.create(
1124 model: "claude-opus-5",
1693 model: "claude-opus-5-5",
11251694 max_tokens: 1024,
11261695 messages: prompt["messages"].map { |message| Anthropic::Mcp.message(message) }
11271696 )
from line 1713
11441713 # As a content block in a message
11451714 resource = await mcp_client.read_resource(uri="file:///path/to/doc.txt")
11461715 response = await client.beta.messages.create(
1147 model="claude-opus-5",
1716 model="claude-opus-5-5",
11481717 max_tokens=1024,
11491718 messages=[
11501719 {
from line 1743
11741743 // As a content block in a message
11751744 const resource = await mcpClient.readResource({ uri: "file:///path/to/doc.txt" });
11761745 const response = await anthropic.beta.messages.create({
1177 model: "claude-opus-5",
1746 model: "claude-opus-5-5",
11781747 max_tokens: 1024,
11791748 messages: [
11801749 {
from line 1769
12001769 var response = await anthropic.Beta.Messages.Create(
12011770 new MessageCreateParams
12021771 {
1203 Model = Messages::Model.ClaudeOpus5,
1772 Model = Messages::Model.ClaudeOpus5_5,
12041773 MaxTokens = 1024,
12051774 Messages =
12061775 [
from line 1817
12481817 }
12491818
12501819 response, err := client.Beta.Messages.New(ctx, anthropic.BetaMessageNewParams{
1251 Model: anthropic.ModelClaudeOpus5,
1820 Model: anthropic.ModelClaudeOpus5_5,
12521821 MaxTokens: 1024,
12531822 Messages: []anthropic.BetaMessageParam{
12541823 anthropic.NewBetaUserMessage(
from line 1865
12961865 BetaTextBlockParam.builder().text("Summarize this document").build()));
12971866
12981867 BetaMessage response = anthropic.beta().messages().create(MessageCreateParams.builder()
1299 .model(Model.CLAUDE_OPUS_5)
1868 .model(Model.CLAUDE_OPUS_5_5)
13001869 .maxTokens(1024L)
13011870 .addUserMessageOfBetaContentBlockParams(content)
13021871 .build());
from line 1909
13401909 ],
13411910 ],
13421911 ],
1343 model: 'claude-opus-5',
1912 model: 'claude-opus-5-5',
13441913 );
13451914
13461915 echo $response, "\n";
from line 1925
13561925 resource = mcp_client.read_resource(uri: "file:///path/to/doc.txt")
13571926
13581927 response = anthropic.beta.messages.create(
1359 model: "claude-opus-5",
1928 model: "claude-opus-5-5",
13601929 max_tokens: 1024,
13611930 messages: [
13621931 {
from line 1978
14091978
14101979```json
14111980{
1412 "model": "claude-opus-5",
1981 "model": "claude-opus-5-5",
14131982 "max_tokens": 1000,
14141983 "messages": [
14151984 // ...
from line 2002
14332002
14342003```json
14352004{
1436 "model": "claude-opus-5",
2005 "model": "claude-opus-5-5",
14372006 "max_tokens": 1000,
14382007 "messages": [
14392008 // ...
14402009