[Bugfix] Support MCP SDK 2.x tool input schemas (#53870)

Signed-off-by: chaunceyjiang <chaunceyjiang@gmail.com>

Chauncey committed Sep 1, 2026 at 23:02 UTC 339e16cbb687680a401afc4aca5b19cac54a5fe3
7 files changed +118 -37
requirements/common.txt
+1 -1
@@ -51,7 +51,7 @@ setproctitle # Used to set process names for better debugging and monitoring
51 openai-harmony >= 0.0.3 # Required for gpt-oss
52 anthropic >= 0.71.0
53 model-hosting-container-standards >= 0.1.14, < 1.0.0
54 -mcp
54 +mcp >= 2.0.0, < 3.0.0
55 opentelemetry-sdk >= 1.27.0
56 opentelemetry-api >= 1.27.0
57 opentelemetry-exporter-otlp >= 1.27.0
requirements/test/cpu.txt
+21 -9
@@ -37,6 +37,7 @@ anyio==4.14.1
37 # via
38 # anthropic
39 # httpx
40 + # httpx2
41 # mcp
42 # openai
43 # sse-starlette
@@ -304,9 +305,10 @@ grpcio==1.78.0
305 # ray
306 grpcio-reflection==1.78.0
307 # via -r requirements/test/cuda.in
307 -h11==0.14.0
308 +h11==0.16.0
309 # via
310 # httpcore
311 + # httpcore2
312 # uvicorn
313 h2==4.3.0
314 # via httpx
@@ -320,8 +322,10 @@ hpack==4.1.0
322 # via h2
323 html2text==2025.4.15
324 # via gpt-oss
323 -httpcore==1.0.6
325 +httpcore==1.0.9
326 # via httpx
327 +httpcore2==2.12.0
328 + # via httpx2
329 httptools==0.8.0
330 # via uvicorn
331 httpx==0.27.2
@@ -332,12 +336,11 @@ httpx==0.27.2
336 # fastapi
337 # fastapi-cloud-cli
338 # huggingface-hub
335 - # mcp
339 # model-hosting-container-standards
340 # openai
341 # perceptron
342 # schemathesis
340 -httpx-sse==0.4.3
343 +httpx2==2.12.0
344 # via mcp
345 huggingface-hub==1.28.0
346 # via
@@ -366,11 +369,12 @@ hypothesis-graphql==0.13.0
369 # via schemathesis
370 hypothesis-jsonschema==0.23.1
371 # via schemathesis
369 -idna==3.10
372 +idna==3.19
373 # via
374 # anyio
375 # email-validator
376 # httpx
377 + # httpx2
378 # requests
379 # yarl
380 ijson==3.5.0
@@ -476,8 +480,10 @@ mbstrdecoder==1.1.3
480 # dataproperty
481 # pytablewriter
482 # typepy
479 -mcp==1.28.1
483 +mcp==2.1.1
484 # via -r requirements/test/../common.txt
485 +mcp-types==2.1.1
486 + # via mcp
487 mdurl==0.1.2
488 # via markdown-it-py
489 mistral-common==1.11.6
@@ -596,6 +602,7 @@ openpyxl==3.1.5
602 opentelemetry-api==1.35.0
603 # via
604 # -r requirements/test/../common.txt
605 + # mcp
606 # opentelemetry-exporter-otlp-proto-grpc
607 # opentelemetry-exporter-otlp-proto-http
608 # opentelemetry-exporter-prometheus
@@ -782,6 +789,7 @@ pydantic==2.12.0
789 # gpt-oss
790 # lm-format-enforcer
791 # mcp
792 + # mcp-types
793 # mistral-common
794 # model-hosting-container-standards
795 # mteb
@@ -800,9 +808,7 @@ pydantic-extra-types==2.10.5
808 # fastapi
809 # mistral-common
810 pydantic-settings==2.14.2
803 - # via
804 - # fastapi
805 - # mcp
811 + # via fastapi
812 pygments==2.18.0
813 # via
814 # pytest
@@ -1185,6 +1191,10 @@ triton==3.6.0
1191 # via xgrammar
1192 tritonclient==2.64.0
1193 # via -r requirements/test/cuda.in
1194 +truststore==0.10.4
1195 + # via
1196 + # httpcore2
1197 + # httpx2
1198 typepy==1.3.2
1199 # via
1200 # dataproperty
@@ -1215,10 +1225,12 @@ typing-extensions==4.15.0
1225 # cohere
1226 # fastapi
1227 # grpcio
1228 + # httpx2
1229 # huggingface-hub
1230 # librosa
1231 # lm-eval
1232 # mcp
1233 + # mcp-types
1234 # mistral-common
1235 # mteb
1236 # openai
requirements/test/cuda.txt
+24 -10
@@ -40,6 +40,7 @@ anyio==4.14.1
40 # via
41 # anthropic
42 # httpx
43 + # httpx2
44 # mcp
45 # openai
46 # sse-starlette
@@ -323,9 +324,10 @@ grpcio==1.78.0
324 # ray
325 grpcio-reflection==1.78.0
326 # via -r requirements/test/cuda.in
326 -h11==0.14.0
327 +h11==0.16.0
328 # via
329 # httpcore
330 + # httpcore2
331 # uvicorn
332 h2==4.3.0
333 # via httpx
@@ -339,8 +341,10 @@ hpack==4.1.0
341 # via h2
342 html2text==2025.4.15
343 # via gpt-oss
342 -httpcore==1.0.6
344 +httpcore==1.0.9
345 # via httpx
346 +httpcore2==2.12.0
347 + # via httpx2
348 httptools==0.8.0
349 # via uvicorn
350 httpx==0.27.2
@@ -351,12 +355,11 @@ httpx==0.27.2
355 # fastapi
356 # fastapi-cloud-cli
357 # huggingface-hub
354 - # mcp
358 # model-hosting-container-standards
359 # openai
360 # perceptron
361 # schemathesis
359 -httpx-sse==0.4.3
362 +httpx2==2.12.0
363 # via mcp
364 huggingface-hub==1.28.0
365 # via
@@ -386,11 +389,12 @@ hypothesis-graphql==0.13.0
389 # via schemathesis
390 hypothesis-jsonschema==0.23.1
391 # via schemathesis
389 -idna==3.10
392 +idna==3.19
393 # via
394 # anyio
395 # email-validator
396 # httpx
397 + # httpx2
398 # requests
399 # yarl
400 ijson==3.5.0
@@ -505,8 +509,12 @@ mbstrdecoder==1.1.3
509 # dataproperty
510 # pytablewriter
511 # typepy
508 -mcp==1.28.1
509 - # via -r requirements/test/../common.txt
512 +mcp==2.1.1
513 + # via
514 + # -c requirements/common.txt
515 + # -r requirements/test/../common.txt
516 +mcp-types==2.1.1
517 + # via mcp
518 mdurl==0.1.2
519 # via markdown-it-py
520 mistral-common==1.11.6
@@ -675,6 +683,7 @@ opentelemetry-api==1.35.0
683 # via
684 # -c requirements/common.txt
685 # -r requirements/test/../common.txt
686 + # mcp
687 # opentelemetry-exporter-otlp-proto-grpc
688 # opentelemetry-exporter-otlp-proto-http
689 # opentelemetry-exporter-prometheus
@@ -873,6 +882,7 @@ pydantic==2.12.0
882 # gpt-oss
883 # lm-format-enforcer
884 # mcp
885 + # mcp-types
886 # mistral-common
887 # model-hosting-container-standards
888 # mteb
@@ -891,9 +901,7 @@ pydantic-extra-types==2.10.5
901 # fastapi
902 # mistral-common
903 pydantic-settings==2.14.2
894 - # via
895 - # fastapi
896 - # mcp
904 + # via fastapi
905 pygments==2.18.0
906 # via
907 # pytest
@@ -1293,6 +1301,10 @@ triton==3.7.1
1301 # xgrammar
1302 tritonclient==2.64.0
1303 # via -r requirements/test/cuda.in
1304 +truststore==0.10.4
1305 + # via
1306 + # httpcore2
1307 + # httpx2
1308 typepy==1.3.2
1309 # via
1310 # dataproperty
@@ -1324,10 +1336,12 @@ typing-extensions==4.15.0
1336 # cohere
1337 # fastapi
1338 # grpcio
1339 + # httpx2
1340 # huggingface-hub
1341 # librosa
1342 # lm-eval
1343 # mcp
1344 + # mcp-types
1345 # mistral-common
1346 # mteb
1347 # openai
requirements/test/rocm.txt
+22 -8
@@ -37,6 +37,7 @@ anyio==4.13.0
37 # via
38 # anthropic
39 # httpx
40 + # httpx2
41 # mcp
42 # openai
43 # sse-starlette
@@ -319,6 +320,7 @@ grpcio-reflection==1.78.0
320 h11==0.16.0
321 # via
322 # httpcore
323 + # httpcore2
324 # uvicorn
325 h2==4.3.0
326 # via httpx
@@ -334,6 +336,8 @@ html2text==2025.4.15
336 # via gpt-oss
337 httpcore==1.0.9
338 # via httpx
339 +httpcore2==2.12.0
340 + # via httpx2
341 httptools==0.7.1
342 # via uvicorn
343 httpx==0.27.2
@@ -344,12 +348,11 @@ httpx==0.27.2
348 # fastapi
349 # fastapi-cloud-cli
350 # huggingface-hub
347 - # mcp
351 # model-hosting-container-standards
352 # openai
353 # perceptron
354 # schemathesis
352 -httpx-sse==0.4.3
355 +httpx2==2.12.0
356 # via mcp
357 huggingface-hub==1.28.0
358 # via
@@ -379,11 +382,12 @@ hypothesis-graphql==0.13.0
382 # via schemathesis
383 hypothesis-jsonschema==0.23.1
384 # via schemathesis
382 -idna==3.11
385 +idna==3.19
386 # via
387 # anyio
388 # email-validator
389 # httpx
390 + # httpx2
391 # requests
392 # yarl
393 ijson==3.5.0
@@ -500,8 +504,12 @@ mbstrdecoder==1.1.4
504 # dataproperty
505 # pytablewriter
506 # typepy
503 -mcp==1.27.0
504 - # via -r requirements/test/../common.txt
507 +mcp==2.1.1
508 + # via
509 + # -c requirements/common.txt
510 + # -r requirements/test/../common.txt
511 +mcp-types==2.1.1
512 + # via mcp
513 mdurl==0.1.2
514 # via markdown-it-py
515 mistral-common==1.11.6
@@ -642,6 +650,7 @@ opentelemetry-api==1.40.0
650 # via
651 # -c requirements/common.txt
652 # -r requirements/test/../common.txt
653 + # mcp
654 # opentelemetry-exporter-otlp-proto-grpc
655 # opentelemetry-exporter-otlp-proto-http
656 # opentelemetry-exporter-prometheus
@@ -847,6 +856,7 @@ pydantic==2.12.5
856 # gpt-oss
857 # lm-format-enforcer
858 # mcp
859 + # mcp-types
860 # mistral-common
861 # model-hosting-container-standards
862 # mteb
@@ -865,9 +875,7 @@ pydantic-extra-types==2.11.1
875 # fastapi
876 # mistral-common
877 pydantic-settings==2.13.1
868 - # via
869 - # fastapi
870 - # mcp
878 + # via fastapi
879 pygments==2.19.2
880 # via
881 # pytest
@@ -1241,6 +1249,10 @@ transformers-stream-generator==0.0.5
1249 # via -r requirements/test/rocm.in
1250 tritonclient==2.66.0
1251 # via -r requirements/test/rocm.in
1252 +truststore==0.10.4
1253 + # via
1254 + # httpcore2
1255 + # httpx2
1256 typeguard==4.5.1
1257 # via inflect
1258 typepy==1.3.4
@@ -1274,10 +1286,12 @@ typing-extensions==4.15.0
1286 # cohere
1287 # fastapi
1288 # grpcio
1289 + # httpx2
1290 # huggingface-hub
1291 # librosa
1292 # lm-eval
1293 # mcp
1294 + # mcp-types
1295 # mistral-common
1296 # mteb
1297 # openai
requirements/test/xpu.txt
+5 -3
@@ -375,9 +375,11 @@ mbstrdecoder==1.1.5
375 # dataproperty
376 # pytablewriter
377 # typepy
378 -mcp==2.0.0
379 - # via -r requirements/test/../common.txt
380 -mcp-types==2.0.0
378 +mcp==2.1.1
379 + # via
380 + # -c requirements/common.txt
381 + # -r requirements/test/../common.txt
382 +mcp-types==2.1.1
383 # via mcp
384 mdurl==0.1.2
385 # via markdown-it-py
tests/entrypoints/openai/responses/test_mcp_tools.py
+41 -2
@@ -10,7 +10,10 @@ from openai import OpenAI
10 from openai_harmony import Message, ToolDescription, ToolNamespaceConfig
11
12 from tests.utils import RemoteOpenAIServer
13 -from vllm.entrypoints.mcp.tool_server import MCPToolServer
13 +from vllm.entrypoints.mcp.tool_server import (
14 + MCPToolServer,
15 + post_process_tools_description,
16 +)
17
18 from .conftest import (
19 BASE_TEST_ENV,
@@ -36,8 +39,9 @@ _PYTHON_TOOL_INSTRUCTION = (
39 )
40
41
42 +@pytest.mark.skip_global_cleanup
43 class TestMCPToolServerUnit:
40 - """Test MCPToolServer.get_tool_description filtering logic.
44 + """Test MCP tool description processing and filtering.
45
46 Note: The wildcard "*" is normalized to None by
47 _extract_allowed_tools_from_mcp_requests before reaching this layer,
@@ -96,6 +100,41 @@ class TestMCPToolServerUnit:
100 # Empty list - returns None
101 assert server.get_tool_description("test_server", allowed_tools=[]) is None
102
103 + def test_post_process_tools_description_uses_trim_schema_result(self, monkeypatch):
104 + mcp_types = pytest.importorskip("mcp.types")
105 + tool = mcp_types.Tool(
106 + name="tool",
107 + input_schema={"type": "object"},
108 + )
109 + result = mcp_types.ListToolsResult(tools=[tool])
110 + trimmed_schema = {"type": "object", "properties": {}}
111 +
112 + monkeypatch.setattr(
113 + "vllm.entrypoints.mcp.tool_server.trim_schema",
114 + lambda _schema: trimmed_schema,
115 + )
116 +
117 + post_process_tools_description(result)
118 +
119 + assert tool.input_schema is trimmed_schema
120 +
121 + def test_post_process_tools_description_honors_meta_opt_out(self):
122 + mcp_types = pytest.importorskip("mcp.types")
123 + included = mcp_types.Tool(
124 + name="included",
125 + input_schema={"type": "object"},
126 + )
127 + excluded = mcp_types.Tool(
128 + name="excluded",
129 + input_schema={"type": "object"},
130 + meta={"include_in_prompt": False},
131 + )
132 + result = mcp_types.ListToolsResult(tools=[included, excluded])
133 +
134 + post_process_tools_description(result)
135 +
136 + assert result.tools == [included]
137 +
138 def test_builtin_tools_consistency(self):
139 """MCP_BUILTIN_TOOLS must match BUILTIN_TOOL_TO_MCP_SERVER_LABEL values."""
140 from vllm.entrypoints.openai.parser.harmony_utils import (
vllm/entrypoints/mcp/tool_server.py
+4 -4
@@ -58,14 +58,14 @@ def post_process_tools_description(
58 ) -> "ListToolsResult":
59 # Adapt the MCP tool result for Harmony
60 for tool in list_tools_result.tools:
61 - tool.inputSchema = trim_schema(tool.inputSchema)
61 + tool.input_schema = trim_schema(tool.input_schema)
62
63 # Some tools schema don't need to be part of the prompt (e.g. simple text
64 # in text out for Python)
65 list_tools_result.tools = [
66 tool
67 for tool in list_tools_result.tools
68 - if getattr(tool.annotations, "include_in_prompt", True)
68 + if (tool.meta or {}).get("include_in_prompt", True)
69 ]
70
71 return list_tools_result
@@ -121,13 +121,13 @@ class MCPToolServer(ToolServer):
121 list_tools_response = post_process_tools_description(list_tools_response)
122
123 tool_from_mcp = ToolNamespaceConfig(
124 - name=initialize_response.serverInfo.name,
124 + name=initialize_response.server_info.name,
125 description=initialize_response.instructions,
126 tools=[
127 ToolDescription.new(
128 name=tool.name,
129 description=tool.description,
130 - parameters=tool.inputSchema,
130 + parameters=tool.input_schema,
131 )
132 for tool in list_tools_response.tools
133 ],