[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
],