Skip to content

[Frontend] Add MCP tool streaming support to Responses API#31761

Merged
chaunceyjiang merged 1 commit intovllm-project:mainfrom
daniel-salib:pr3-mcp-streaming-v2
Jan 9, 2026
Merged

[Frontend] Add MCP tool streaming support to Responses API#31761
chaunceyjiang merged 1 commit intovllm-project:mainfrom
daniel-salib:pr3-mcp-streaming-v2

Conversation

@daniel-salib
Copy link
Copy Markdown
Contributor

@daniel-salib daniel-salib commented Jan 6, 2026

Purpose

This change enables streaming support for MCP tools when using GPT OSS. It extends the harmony utilities and response serving infrastructure to handle tool streaming, allowing tool calls and their results to be incrementally streamed back to clients rather than returned as a single batch.

This PR is taken over from #30301

Test Plan

VLLM_ENABLE_RESPONSES_API_STORE=1 VLLM_GPT_OSS_SYSTEM_TOOL_MCP_LABELS=web_search_preview,container,code_interpreter VLLM_RESPONSES_API_USE_MCP_TYPES=1 VLLM_GPT_OSS_HARMONY_SYSTEM_INSTRUCTIONS=1 CUDA_VISIBLE_DEVICES=1 HF_HUB_OFFLINE=1 vllm serve openai/gpt-oss-20b/  --served-model-name=default

curl -X POST "http://localhost:8000/v1/responses"   -H "Content-Type: application/json"   -H "Authorization: Bearer dummy-api-key"   -d '{
        "model": "openai/gpt-oss-120b",
        "input": "Multiply 64548*15151 using the python tool.",
        "tools": [
          {
            "type": "mcp",
            "server_label": "code_interpreter",
            "headers": {"test": "test"},
            "server_url": "IGNORED"
          }
        ], "stream": true
      }'

Test Result

event: response.created
data: {"response":{"id":"resp_af2c67a22acb8b7c","created_at":1767665472,"incomplete_details":null,"instructions":null,"metadata":null,"model":"default","object":"response","output":[],"parallel_tool_calls":true,"temperature":1.0,"tool_choice":"auto","tools":[{"server_label":"code_interpreter","type":"mcp","allowed_tools":null,"authorization":null,"connector_id":null,"headers":{"test":"test"},"require_approval":null,"server_description":null,"server_url":"IGNORED"}],"top_p":1.0,"background":false,"max_output_tokens":130897,"max_tool_calls":null,"previous_response_id":null,"prompt":null,"reasoning":null,"service_tier":"auto","status":"in_progress","text":null,"top_logprobs":null,"truncation":"disabled","usage":null,"user":null,"input_messages":null,"output_messages":null},"sequence_number":0,"type":"response.created"}

event: response.in_progress
data: {"response":{"id":"resp_af2c67a22acb8b7c","created_at":1767665472,"incomplete_details":null,"instructions":null,"metadata":null,"model":"default","object":"response","output":[],"parallel_tool_calls":true,"temperature":1.0,"tool_choice":"auto","tools":[{"server_label":"code_interpreter","type":"mcp","allowed_tools":null,"authorization":null,"connector_id":null,"headers":{"test":"test"},"require_approval":null,"server_description":null,"server_url":"IGNORED"}],"top_p":1.0,"background":false,"max_output_tokens":130897,"max_tool_calls":null,"previous_response_id":null,"prompt":null,"reasoning":null,"service_tier":"auto","status":"in_progress","text":null,"top_logprobs":null,"truncation":"disabled","usage":null,"user":null,"input_messages":null,"output_messages":null},"sequence_number":1,"type":"response.in_progress"}

event: response.output_item.added
data: {"item":{"id":"msg_95b6718e61a70c83","summary":[],"type":"reasoning","content":null,"encrypted_content":null,"status":"in_progress"},"output_index":0,"sequence_number":2,"type":"response.output_item.added"}

event: response.reasoning_part.added
data: {"content_index":0,"item_id":"msg_95b6718e61a70c83","output_index":0,"part":{"text":"","type":"reasoning_text"},"sequence_number":3,"type":"response.reasoning_part.added"}

event: response.reasoning_text.delta
data: {"content_index":0,"delta":"We","item_id":"msg_95b6718e61a70c83","output_index":0,"sequence_number":4,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":0,"delta":" need","item_id":"msg_95b6718e61a70c83","output_index":0,"sequence_number":5,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":0,"delta":" to","item_id":"msg_95b6718e61a70c83","output_index":0,"sequence_number":6,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":0,"delta":" compute","item_id":"msg_95b6718e61a70c83","output_index":0,"sequence_number":7,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":0,"delta":" ","item_id":"msg_95b6718e61a70c83","output_index":0,"sequence_number":8,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":0,"delta":"645","item_id":"msg_95b6718e61a70c83","output_index":0,"sequence_number":9,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":0,"delta":"48","item_id":"msg_95b6718e61a70c83","output_index":0,"sequence_number":10,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":0,"delta":" *","item_id":"msg_95b6718e61a70c83","output_index":0,"sequence_number":11,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":0,"delta":" ","item_id":"msg_95b6718e61a70c83","output_index":0,"sequence_number":12,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":0,"delta":"151","item_id":"msg_95b6718e61a70c83","output_index":0,"sequence_number":13,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":0,"delta":"51","item_id":"msg_95b6718e61a70c83","output_index":0,"sequence_number":14,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":0,"delta":".","item_id":"msg_95b6718e61a70c83","output_index":0,"sequence_number":15,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":0,"delta":" Use","item_id":"msg_95b6718e61a70c83","output_index":0,"sequence_number":16,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":0,"delta":" python","item_id":"msg_95b6718e61a70c83","output_index":0,"sequence_number":17,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":0,"delta":".","item_id":"msg_95b6718e61a70c83","output_index":0,"sequence_number":18,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.done
data: {"content_index":0,"item_id":"msg_95b6718e61a70c83","output_index":1,"sequence_number":19,"text":"We need to compute 64548 * 15151. Use python.","type":"response.reasoning_text.done"}

event: response.reasoning_part.done
data: {"content_index":0,"item_id":"msg_95b6718e61a70c83","output_index":1,"part":{"text":"We need to compute 64548 * 15151. Use python.","type":"reasoning_text"},"sequence_number":20,"type":"response.reasoning_part.done"}

event: response.output_item.done
data: {"item":{"id":"msg_95b6718e61a70c83","summary":[],"type":"reasoning","content":[{"text":"We need to compute 64548 * 15151. Use python.","type":"reasoning_text"}],"encrypted_content":null,"status":"completed"},"output_index":1,"sequence_number":21,"type":"response.output_item.done"}

event: response.output_item.added
data: {"item":{"id":"mcp_b0e91354895f7444","arguments":"","name":"python","server_label":"code_interpreter","type":"mcp_call","approval_request_id":null,"error":null,"output":null,"status":"in_progress"},"output_index":1,"sequence_number":22,"type":"response.output_item.added"}

event: response.mcp_call.in_progress
data: {"item_id":"mcp_b0e91354895f7444","output_index":1,"sequence_number":23,"type":"response.mcp_call.in_progress"}

event: response.mcp_call_arguments.delta
data: {"delta":"645","item_id":"mcp_b0e91354895f7444","output_index":1,"sequence_number":24,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.delta
data: {"delta":"48","item_id":"mcp_b0e91354895f7444","output_index":1,"sequence_number":25,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.delta
data: {"delta":"*","item_id":"mcp_b0e91354895f7444","output_index":1,"sequence_number":26,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.delta
data: {"delta":"151","item_id":"mcp_b0e91354895f7444","output_index":1,"sequence_number":27,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.delta
data: {"delta":"51","item_id":"mcp_b0e91354895f7444","output_index":1,"sequence_number":28,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.done
data: {"arguments":"64548*15151","item_id":"mcp_b0e91354895f7444","output_index":2,"sequence_number":29,"type":"response.mcp_call_arguments.done","name":"python"}

event: response.mcp_call.completed
data: {"item_id":"mcp_b0e91354895f7444","output_index":2,"sequence_number":30,"type":"response.mcp_call.completed"}

event: response.output_item.done
data: {"item":{"id":"mcp_b0e91354895f7444","arguments":"64548*15151","name":"python","server_label":"code_interpreter","type":"mcp_call","approval_request_id":null,"error":null,"output":null,"status":"completed"},"output_index":2,"sequence_number":31,"type":"response.output_item.done"}

event: response.output_item.added
data: {"item":{"id":"msg_be7ca538a6c519f9","summary":[],"type":"reasoning","content":null,"encrypted_content":null,"status":"in_progress"},"output_index":2,"sequence_number":32,"type":"response.output_item.added"}

event: response.reasoning_part.added
data: {"content_index":1,"item_id":"msg_be7ca538a6c519f9","output_index":2,"part":{"text":"","type":"reasoning_text"},"sequence_number":33,"type":"response.reasoning_part.added"}

event: response.reasoning_text.delta
data: {"content_index":1,"delta":"Result","item_id":"msg_be7ca538a6c519f9","output_index":2,"sequence_number":34,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":1,"delta":":","item_id":"msg_be7ca538a6c519f9","output_index":2,"sequence_number":35,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":1,"delta":" let's","item_id":"msg_be7ca538a6c519f9","output_index":2,"sequence_number":36,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":1,"delta":" see","item_id":"msg_be7ca538a6c519f9","output_index":2,"sequence_number":37,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":1,"delta":".","item_id":"msg_be7ca538a6c519f9","output_index":2,"sequence_number":38,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.done
data: {"content_index":1,"item_id":"msg_be7ca538a6c519f9","output_index":3,"sequence_number":39,"text":"Result: let's see.","type":"response.reasoning_text.done"}

event: response.reasoning_part.done
data: {"content_index":1,"item_id":"msg_be7ca538a6c519f9","output_index":3,"part":{"text":"Result: let's see.","type":"reasoning_text"},"sequence_number":40,"type":"response.reasoning_part.done"}

event: response.output_item.done
data: {"item":{"id":"msg_be7ca538a6c519f9","summary":[],"type":"reasoning","content":[{"text":"Result: let's see.","type":"reasoning_text"}],"encrypted_content":null,"status":"completed"},"output_index":3,"sequence_number":41,"type":"response.output_item.done"}

event: response.output_item.added
data: {"item":{"id":"msg_9de9bb6468536951","summary":[],"type":"reasoning","content":null,"encrypted_content":null,"status":"in_progress"},"output_index":3,"sequence_number":42,"type":"response.output_item.added"}

event: response.reasoning_part.added
data: {"content_index":2,"item_id":"msg_9de9bb6468536951","output_index":3,"part":{"text":"","type":"reasoning_text"},"sequence_number":43,"type":"response.reasoning_part.added"}

event: response.reasoning_text.delta
data: {"content_index":2,"delta":"It","item_id":"msg_9de9bb6468536951","output_index":3,"sequence_number":44,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":2,"delta":" didn't","item_id":"msg_9de9bb6468536951","output_index":3,"sequence_number":45,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":2,"delta":" output","item_id":"msg_9de9bb6468536951","output_index":3,"sequence_number":46,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":2,"delta":";","item_id":"msg_9de9bb6468536951","output_index":3,"sequence_number":47,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":2,"delta":" need","item_id":"msg_9de9bb6468536951","output_index":3,"sequence_number":48,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":2,"delta":" to","item_id":"msg_9de9bb6468536951","output_index":3,"sequence_number":49,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":2,"delta":" capture","item_id":"msg_9de9bb6468536951","output_index":3,"sequence_number":50,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":2,"delta":".","item_id":"msg_9de9bb6468536951","output_index":3,"sequence_number":51,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.done
data: {"content_index":2,"item_id":"msg_9de9bb6468536951","output_index":4,"sequence_number":52,"text":"It didn't output; need to capture.","type":"response.reasoning_text.done"}

event: response.reasoning_part.done
data: {"content_index":2,"item_id":"msg_9de9bb6468536951","output_index":4,"part":{"text":"It didn't output; need to capture.","type":"reasoning_text"},"sequence_number":53,"type":"response.reasoning_part.done"}

event: response.output_item.done
data: {"item":{"id":"msg_9de9bb6468536951","summary":[],"type":"reasoning","content":[{"text":"It didn't output; need to capture.","type":"reasoning_text"}],"encrypted_content":null,"status":"completed"},"output_index":4,"sequence_number":54,"type":"response.output_item.done"}

event: response.output_item.added
data: {"item":{"id":"mcp_ba0b463a02c4cb96","arguments":"","name":"python","server_label":"code_interpreter","type":"mcp_call","approval_request_id":null,"error":null,"output":null,"status":"in_progress"},"output_index":4,"sequence_number":55,"type":"response.output_item.added"}

event: response.mcp_call.in_progress
data: {"item_id":"mcp_ba0b463a02c4cb96","output_index":4,"sequence_number":56,"type":"response.mcp_call.in_progress"}

event: response.mcp_call_arguments.delta
data: {"delta":"result","item_id":"mcp_ba0b463a02c4cb96","output_index":4,"sequence_number":57,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.delta
data: {"delta":" =","item_id":"mcp_ba0b463a02c4cb96","output_index":4,"sequence_number":58,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.delta
data: {"delta":" ","item_id":"mcp_ba0b463a02c4cb96","output_index":4,"sequence_number":59,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.delta
data: {"delta":"645","item_id":"mcp_ba0b463a02c4cb96","output_index":4,"sequence_number":60,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.delta
data: {"delta":"48","item_id":"mcp_ba0b463a02c4cb96","output_index":4,"sequence_number":61,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.delta
data: {"delta":"*","item_id":"mcp_ba0b463a02c4cb96","output_index":4,"sequence_number":62,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.delta
data: {"delta":"151","item_id":"mcp_ba0b463a02c4cb96","output_index":4,"sequence_number":63,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.delta
data: {"delta":"51","item_id":"mcp_ba0b463a02c4cb96","output_index":4,"sequence_number":64,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.delta
data: {"delta":"\n","item_id":"mcp_ba0b463a02c4cb96","output_index":4,"sequence_number":65,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.delta
data: {"delta":"result","item_id":"mcp_ba0b463a02c4cb96","output_index":4,"sequence_number":66,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.done
data: {"arguments":"result = 64548*15151\nresult","item_id":"mcp_ba0b463a02c4cb96","output_index":5,"sequence_number":67,"type":"response.mcp_call_arguments.done","name":"python"}

event: response.mcp_call.completed
data: {"item_id":"mcp_ba0b463a02c4cb96","output_index":5,"sequence_number":68,"type":"response.mcp_call.completed"}

event: response.output_item.done
data: {"item":{"id":"mcp_ba0b463a02c4cb96","arguments":"result = 64548*15151\nresult","name":"python","server_label":"code_interpreter","type":"mcp_call","approval_request_id":null,"error":null,"output":null,"status":"completed"},"output_index":5,"sequence_number":69,"type":"response.output_item.done"}

event: response.output_item.added
data: {"item":{"id":"msg_a8e6b99bfde50185","summary":[],"type":"reasoning","content":null,"encrypted_content":null,"status":"in_progress"},"output_index":5,"sequence_number":70,"type":"response.output_item.added"}

event: response.reasoning_part.added
data: {"content_index":3,"item_id":"msg_a8e6b99bfde50185","output_index":5,"part":{"text":"","type":"reasoning_text"},"sequence_number":71,"type":"response.reasoning_part.added"}

event: response.reasoning_text.delta
data: {"content_index":3,"delta":"Result","item_id":"msg_a8e6b99bfde50185","output_index":5,"sequence_number":72,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":3,"delta":" is","item_id":"msg_a8e6b99bfde50185","output_index":5,"sequence_number":73,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":3,"delta":" ","item_id":"msg_a8e6b99bfde50185","output_index":5,"sequence_number":74,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":3,"delta":"977","item_id":"msg_a8e6b99bfde50185","output_index":5,"sequence_number":75,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":3,"delta":",","item_id":"msg_a8e6b99bfde50185","output_index":5,"sequence_number":76,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":3,"delta":"966","item_id":"msg_a8e6b99bfde50185","output_index":5,"sequence_number":77,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":3,"delta":",","item_id":"msg_a8e6b99bfde50185","output_index":5,"sequence_number":78,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":3,"delta":"748","item_id":"msg_a8e6b99bfde50185","output_index":5,"sequence_number":79,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":3,"delta":"?","item_id":"msg_a8e6b99bfde50185","output_index":5,"sequence_number":80,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":3,"delta":" Actually","item_id":"msg_a8e6b99bfde50185","output_index":5,"sequence_number":81,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":3,"delta":" let's","item_id":"msg_a8e6b99bfde50185","output_index":5,"sequence_number":82,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":3,"delta":" print","item_id":"msg_a8e6b99bfde50185","output_index":5,"sequence_number":83,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":3,"delta":".","item_id":"msg_a8e6b99bfde50185","output_index":5,"sequence_number":84,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.done
data: {"content_index":3,"item_id":"msg_a8e6b99bfde50185","output_index":6,"sequence_number":85,"text":"Result is 977,966,748? Actually let's print.","type":"response.reasoning_text.done"}

event: response.reasoning_part.done
data: {"content_index":3,"item_id":"msg_a8e6b99bfde50185","output_index":6,"part":{"text":"Result is 977,966,748? Actually let's print.","type":"reasoning_text"},"sequence_number":86,"type":"response.reasoning_part.done"}

event: response.output_item.done
data: {"item":{"id":"msg_a8e6b99bfde50185","summary":[],"type":"reasoning","content":[{"text":"Result is 977,966,748? Actually let's print.","type":"reasoning_text"}],"encrypted_content":null,"status":"completed"},"output_index":6,"sequence_number":87,"type":"response.output_item.done"}

event: response.output_item.added
data: {"item":{"id":"mcp_b7edd83e21dcb9ac","arguments":"","name":"python","server_label":"code_interpreter","type":"mcp_call","approval_request_id":null,"error":null,"output":null,"status":"in_progress"},"output_index":6,"sequence_number":88,"type":"response.output_item.added"}

event: response.mcp_call.in_progress
data: {"item_id":"mcp_b7edd83e21dcb9ac","output_index":6,"sequence_number":89,"type":"response.mcp_call.in_progress"}

event: response.mcp_call_arguments.delta
data: {"delta":"print","item_id":"mcp_b7edd83e21dcb9ac","output_index":6,"sequence_number":90,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.delta
data: {"delta":"(result","item_id":"mcp_b7edd83e21dcb9ac","output_index":6,"sequence_number":91,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.delta
data: {"delta":")","item_id":"mcp_b7edd83e21dcb9ac","output_index":6,"sequence_number":92,"type":"response.mcp_call_arguments.delta"}

event: response.mcp_call_arguments.done
data: {"arguments":"print(result)","item_id":"mcp_b7edd83e21dcb9ac","output_index":7,"sequence_number":93,"type":"response.mcp_call_arguments.done","name":"python"}

event: response.mcp_call.completed
data: {"item_id":"mcp_b7edd83e21dcb9ac","output_index":7,"sequence_number":94,"type":"response.mcp_call.completed"}

event: response.output_item.done
data: {"item":{"id":"mcp_b7edd83e21dcb9ac","arguments":"print(result)","name":"python","server_label":"code_interpreter","type":"mcp_call","approval_request_id":null,"error":null,"output":null,"status":"completed"},"output_index":7,"sequence_number":95,"type":"response.output_item.done"}

event: response.output_item.added
data: {"item":{"id":"msg_b9691461a75ddddf","summary":[],"type":"reasoning","content":null,"encrypted_content":null,"status":"in_progress"},"output_index":7,"sequence_number":96,"type":"response.output_item.added"}

event: response.reasoning_part.added
data: {"content_index":4,"item_id":"msg_b9691461a75ddddf","output_index":7,"part":{"text":"","type":"reasoning_text"},"sequence_number":97,"type":"response.reasoning_part.added"}

event: response.reasoning_text.delta
data: {"content_index":4,"delta":"977","item_id":"msg_b9691461a75ddddf","output_index":7,"sequence_number":98,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":4,"delta":"966","item_id":"msg_b9691461a75ddddf","output_index":7,"sequence_number":99,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.delta
data: {"content_index":4,"delta":"748","item_id":"msg_b9691461a75ddddf","output_index":7,"sequence_number":100,"type":"response.reasoning_text.delta"}

event: response.reasoning_text.done
data: {"content_index":4,"item_id":"msg_b9691461a75ddddf","output_index":8,"sequence_number":101,"text":"977966748","type":"response.reasoning_text.done"}

event: response.reasoning_part.done
data: {"content_index":4,"item_id":"msg_b9691461a75ddddf","output_index":8,"part":{"text":"977966748","type":"reasoning_text"},"sequence_number":102,"type":"response.reasoning_part.done"}

event: response.output_item.done
data: {"item":{"id":"msg_b9691461a75ddddf","summary":[],"type":"reasoning","content":[{"text":"977966748","type":"reasoning_text"}],"encrypted_content":null,"status":"completed"},"output_index":8,"sequence_number":103,"type":"response.output_item.done"}

event: response.output_item.added
data: {"item":{"id":"msg_b45216f652686d76","content":[],"role":"assistant","status":"in_progress","type":"message"},"output_index":8,"sequence_number":104,"type":"response.output_item.added"}

event: response.content_part.added
data: {"content_index":5,"item_id":"msg_b45216f652686d76","output_index":8,"part":{"annotations":[],"text":"","type":"output_text","logprobs":[]},"sequence_number":105,"type":"response.content_part.added"}

event: response.output_text.delta
data: {"content_index":5,"delta":"The","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":106,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":" product","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":107,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":" of","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":108,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":" \\(","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":109,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":"645","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":110,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":"48","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":111,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":"\\","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":112,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":")","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":113,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":" and","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":114,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":" \\(","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":115,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":"151","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":116,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":"51","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":117,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":"\\","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":118,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":")","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":119,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":" is","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":120,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":" **","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":121,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":"977","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":122,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":",","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":123,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":"966","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":124,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":",","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":125,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":"748","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":126,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":"**","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":127,"type":"response.output_text.delta"}

event: response.output_text.delta
data: {"content_index":5,"delta":".","item_id":"msg_b45216f652686d76","logprobs":[],"output_index":8,"sequence_number":128,"type":"response.output_text.delta"}

event: response.output_text.done
data: {"content_index":5,"item_id":"msg_b45216f652686d76","logprobs":[],"output_index":9,"sequence_number":129,"text":"The product of \\(64548\\) and \\(15151\\) is **977,966,748**.","type":"response.output_text.done"}

event: response.content_part.done
data: {"content_index":5,"item_id":"msg_b45216f652686d76","output_index":9,"part":{"annotations":[],"text":"The product of \\(64548\\) and \\(15151\\) is **977,966,748**.","type":"output_text","logprobs":null},"sequence_number":130,"type":"response.content_part.done"}

event: response.output_item.done
data: {"item":{"id":"msg_b45216f652686d76","content":[{"annotations":[],"text":"The product of \\(64548\\) and \\(15151\\) is **977,966,748**.","type":"output_text","logprobs":null}],"role":"assistant","status":"completed","type":"message"},"output_index":9,"sequence_number":131,"type":"response.output_item.done"}

event: response.completed
data: {"response":{"id":"resp_af2c67a22acb8b7c","created_at":1767665472,"incomplete_details":null,"instructions":null,"metadata":null,"model":"default","object":"response","output":[{"id":"rs_995bd080395ac421","summary":[],"type":"reasoning","content":[{"text":"We need to compute 64548 * 15151. Use python.","type":"reasoning_text"}],"encrypted_content":null,"status":null},{"id":"rs_90c52777a1f1f371","summary":[],"type":"reasoning","content":[{"text":"64548*15151","type":"reasoning_text"}],"encrypted_content":null,"status":null},{"id":"rs_811a66e807d97313","summary":[],"type":"reasoning","content":[{"text":"64548*15151","type":"reasoning_text"}],"encrypted_content":null,"status":null},{"id":"rs_9db52735dfa042f5","summary":[],"type":"reasoning","content":[{"text":"Result: let's see.","type":"reasoning_text"}],"encrypted_content":null,"status":null},{"id":"rs_a77b05a4ebc8787c","summary":[],"type":"reasoning","content":[{"text":"It didn't output; need to capture.","type":"reasoning_text"}],"encrypted_content":null,"status":null},{"id":"rs_9742beb1afb85031","summary":[],"type":"reasoning","content":[{"text":"result = 64548*15151\nresult","type":"reasoning_text"}],"encrypted_content":null,"status":null},{"id":"rs_abd7f6f5bb160d72","summary":[],"type":"reasoning","content":[{"text":"result = 64548*15151\nresult","type":"reasoning_text"}],"encrypted_content":null,"status":null},{"id":"rs_b4d834d77bec56d5","summary":[],"type":"reasoning","content":[{"text":"Result is 977,966,748? Actually let's print.","type":"reasoning_text"}],"encrypted_content":null,"status":null},{"id":"rs_97c1fac0f6216477","summary":[],"type":"reasoning","content":[{"text":"print(result)","type":"reasoning_text"}],"encrypted_content":null,"status":null},{"id":"rs_955867165fab5940","summary":[],"type":"reasoning","content":[{"text":"print(result)","type":"reasoning_text"}],"encrypted_content":null,"status":null},{"id":"rs_8ddddc4c88e55832","summary":[],"type":"reasoning","content":[{"text":"977966748","type":"reasoning_text"}],"encrypted_content":null,"status":null},{"id":"msg_afbbae58e91fad50","content":[{"annotations":[],"text":"The product of \\(64548\\) and \\(15151\\) is **977,966,748**.","type":"output_text","logprobs":null}],"role":"assistant","status":"completed","type":"message"}],"parallel_tool_calls":true,"temperature":1.0,"tool_choice":"auto","tools":[{"server_label":"code_interpreter","type":"mcp","allowed_tools":null,"authorization":null,"connector_id":null,"headers":{"test":"test"},"require_approval":null,"server_description":null,"server_url":"IGNORED"}],"top_p":1.0,"background":false,"max_output_tokens":130723,"max_tool_calls":null,"previous_response_id":null,"prompt":null,"reasoning":null,"service_tier":"auto","status":"completed","text":null,"top_logprobs":null,"truncation":"disabled","usage":{"input_tokens":1019,"input_tokens_details":{"cached_tokens":832,"input_tokens_per_turn":[175,211,284,349],"cached_tokens_per_turn":[160,192,208,272]},"output_tokens":143,"output_tokens_details":{"reasoning_tokens":70,"tool_output_tokens":67,"output_tokens_per_turn":[34,43,30,36],"tool_output_tokens_per_turn":[0,2,30,35]},"total_tokens":1162},"user":null,"input_messages":null,"output_messages":null},"sequence_number":132,"type":"response.completed"}

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds streaming support for MCP tools to the Responses API, which is a significant feature enhancement. The changes include extending the Harmony utilities and refactoring the response serving infrastructure to handle tool streaming events. Several new tests have been added to cover MCP tool streaming, multi-turn conversations, and to ensure event types are correctly dispatched.

The refactoring of the streaming event logic in serving_responses.py into smaller, more manageable functions is a good improvement for maintainability. However, I've identified a few critical logical errors in the new event dispatching code that could lead to incorrect behavior, such as dead code paths and duplicate event emissions for certain tool types. I've provided suggestions to fix these issues. I also found a minor issue in one of the new tests that could make it brittle.

Overall, this is a great contribution, but the identified issues should be addressed to ensure the new streaming logic is robust and correct.

Comment on lines +2029 to +2051
elif (
ctx.parser.current_channel == "commentary"
or ctx.parser.current_channel == "analysis"
) and ctx.parser.current_recipient is not None:
recipient = ctx.parser.current_recipient
# Check for function calls first - they have their own event handling
if recipient.startswith("functions."):
return self._emit_function_call_delta_events(ctx, state)
is_mcp_tool = self._is_mcp_tool_by_namespace(recipient)
if is_mcp_tool:
return self._emit_mcp_tool_delta_events(ctx, state, recipient)
else:
return self._emit_code_interpreter_delta_events(ctx, state)
elif (
(
ctx.parser.current_channel == "commentary"
or ctx.parser.current_channel == "analysis"
)
and ctx.parser.current_recipient is not None
and ctx.parser.current_recipient.startswith("mcp.")
):
return self._emit_mcp_prefix_delta_events(ctx, state)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The logic for dispatching delta events has unreachable code. The elif block checking for ctx.parser.current_recipient.startswith("mcp.") (lines 2042-2050) will never be reached because the preceding elif block (lines 2029-2041) already handles all cases where ctx.parser.current_recipient is not None.

Specifically, if recipient starts with "mcp.", _is_mcp_tool_by_namespace(recipient) will return True, causing _emit_mcp_tool_delta_events to be called and the function to return, thus never reaching the intended _emit_mcp_prefix_delta_events. Additionally, the else branch calling _emit_code_interpreter_delta_events is also unreachable because _is_mcp_tool_by_namespace will always be true if the recipient.startswith("functions.") check fails.

The logic should be restructured to correctly prioritize the "mcp." prefix and remove the dead code.

        elif (
            ctx.parser.current_channel == "commentary"
            or ctx.parser.current_channel == "analysis"
        ) and ctx.parser.current_recipient is not None:
            recipient = ctx.parser.current_recipient
            # Check for function calls first
            if recipient.startswith("functions."):
                return self._emit_function_call_delta_events(ctx, state)
            # Handle mcp. prefixed tools
            if recipient.startswith("mcp."):
                return self._emit_mcp_prefix_delta_events(ctx, state)

            # Handle other MCP-style tools (including python/code_interpreter)
            is_mcp_tool = self._is_mcp_tool_by_namespace(recipient)
            if is_mcp_tool:
                return self._emit_mcp_tool_delta_events(ctx, state, recipient)

Comment on lines +2299 to +2325
if (
self.tool_server is not None
and previous_item.recipient is not None
and state.current_item_id is not None
and state.sent_output_item_added
):
recipient = previous_item.recipient
# Handle MCP tool completion
is_mcp_tool = self._is_mcp_tool_by_namespace(
recipient
) and state.current_item_id.startswith("mcp_")
if is_mcp_tool:
events.extend(
self._emit_mcp_tool_completion_events(previous_item, state)
)
else:
events.extend(
self._emit_code_interpreter_completion_events(previous_item, state)
)

# Handle MCP prefix tool completion
if previous_item.recipient is not None and previous_item.recipient.startswith(
"mcp."
):
events.extend(self._emit_mcp_prefix_completion_events(previous_item, state))

return events
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

There is a logical flaw in how tool completion events are emitted. The code has two separate if blocks for handling tool completions. One for general MCP/code-interpreter tools, and another specifically for tools with an mcp. prefix. If a tool recipient starts with mcp., it will match the conditions for both blocks, leading to _emit_mcp_tool_completion_events and _emit_mcp_prefix_completion_events both being called. This will result in duplicate or conflicting events being sent to the client.

The logic should be restructured into a single if/elif/else chain to ensure that only one completion event handler is called for any given tool.

        # Handle tool completion
        if (
            self.tool_server is not None
            and previous_item.recipient is not None
            and state.current_item_id is not None
            and state.sent_output_item_added
        ):
            recipient = previous_item.recipient
            # Handle MCP prefix tool completion first
            if recipient.startswith("mcp."):
                events.extend(self._emit_mcp_prefix_completion_events(previous_item, state))
            else:
                # Handle other MCP tool and code interpreter completion
                is_mcp_tool = self._is_mcp_tool_by_namespace(
                    recipient
                ) and state.current_item_id.startswith("mcp_")
                if is_mcp_tool:
                    events.extend(
                        self._emit_mcp_tool_completion_events(previous_item, state)
                    )
                else:
                    events.extend(
                        self._emit_code_interpreter_completion_events(previous_item, state)
                    )

        return events

Comment on lines +252 to +256
if (
event.type.endswith("added")
or event.type == "response.mcp_call.in_progress"
):
stack_of_event_types.append(event.type)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The if statement at line 252 should be an elif. The current structure with two separate if/elif chains is confusing and less robust. A single if/elif chain for event type dispatching would make the logic clearer and prevent potential bugs if new event types are added in the future.

Suggested change
if (
event.type.endswith("added")
or event.type == "response.mcp_call.in_progress"
):
stack_of_event_types.append(event.type)
elif (
event.type.endswith("added")
or event.type == "response.mcp_call.in_progress"
):
stack_of_event_types.append(event.type)

@chaunceyjiang
Copy link
Copy Markdown
Collaborator

/cc @qandrew PTAL.

@mergify
Copy link
Copy Markdown

mergify bot commented Jan 6, 2026

Hi @daniel-salib, the pre-commit checks have failed. Please run:

uv pip install pre-commit
pre-commit install
pre-commit run --all-files

Then, commit the changes and push to your branch.

For future commits, pre-commit will run automatically on changed files before each commit.

Tip

Is mypy or markdownlint failing?
mypy and markdownlint are run differently in CI. If the failure is related to either of these checks, please use the following commands to run them locally:
# For mypy (substitute "3.10" with the failing version if needed)
pre-commit run --hook-stage manual mypy-3.10
# For markdownlint
pre-commit run --hook-stage manual markdownlint

@mergify
Copy link
Copy Markdown

mergify bot commented Jan 6, 2026

⚠️ The sha of the head commit of this PR conflicts with #30192. Mergify cannot evaluate rules on this PR. ⚠️

@qandrew
Copy link
Copy Markdown
Contributor

qandrew commented Jan 6, 2026

@chaunceyjiang could you "mark as ready" first? Daniel is taking over again from my work in #30301 and there might be some UT issues before I review

@chaunceyjiang chaunceyjiang added the ready ONLY add when PR is ready to merge/full CI is needed label Jan 6, 2026
@chaunceyjiang
Copy link
Copy Markdown
Collaborator

ok

@daniel-salib daniel-salib force-pushed the pr3-mcp-streaming-v2 branch 2 times, most recently from 1993cf3 to 99d93b5 Compare January 7, 2026 01:02
@mergify
Copy link
Copy Markdown

mergify bot commented Jan 7, 2026

Hi @daniel-salib, the pre-commit checks have failed. Please run:

uv pip install pre-commit
pre-commit install
pre-commit run --all-files

Then, commit the changes and push to your branch.

For future commits, pre-commit will run automatically on changed files before each commit.

Tip

Is mypy or markdownlint failing?
mypy and markdownlint are run differently in CI. If the failure is related to either of these checks, please use the following commands to run them locally:
# For mypy (substitute "3.10" with the failing version if needed)
pre-commit run --hook-stage manual mypy-3.10
# For markdownlint
pre-commit run --hook-stage manual markdownlint

@mergify
Copy link
Copy Markdown

mergify bot commented Jan 7, 2026

Hi @daniel-salib, the pre-commit checks have failed. Please run:

uv pip install pre-commit
pre-commit install
pre-commit run --all-files

Then, commit the changes and push to your branch.

For future commits, pre-commit will run automatically on changed files before each commit.

Tip

Is mypy or markdownlint failing?
mypy and markdownlint are run differently in CI. If the failure is related to either of these checks, please use the following commands to run them locally:
# For mypy (substitute "3.10" with the failing version if needed)
pre-commit run --hook-stage manual mypy-3.10
# For markdownlint
pre-commit run --hook-stage manual markdownlint

@daniel-salib daniel-salib force-pushed the pr3-mcp-streaming-v2 branch 2 times, most recently from afa5a58 to 7c2c258 Compare January 7, 2026 12:31
@mergify
Copy link
Copy Markdown

mergify bot commented Jan 7, 2026

Hi @daniel-salib, the pre-commit checks have failed. Please run:

uv pip install pre-commit
pre-commit install
pre-commit run --all-files

Then, commit the changes and push to your branch.

For future commits, pre-commit will run automatically on changed files before each commit.

Tip

Is mypy or markdownlint failing?
mypy and markdownlint are run differently in CI. If the failure is related to either of these checks, please use the following commands to run them locally:
# For mypy (substitute "3.10" with the failing version if needed)
pre-commit run --hook-stage manual mypy-3.10
# For markdownlint
pre-commit run --hook-stage manual markdownlint

@mergify
Copy link
Copy Markdown

mergify bot commented Jan 7, 2026

Hi @daniel-salib, the pre-commit checks have failed. Please run:

uv pip install pre-commit
pre-commit install
pre-commit run --all-files

Then, commit the changes and push to your branch.

For future commits, pre-commit will run automatically on changed files before each commit.

Tip

Is mypy or markdownlint failing?
mypy and markdownlint are run differently in CI. If the failure is related to either of these checks, please use the following commands to run them locally:
# For mypy (substitute "3.10" with the failing version if needed)
pre-commit run --hook-stage manual mypy-3.10
# For markdownlint
pre-commit run --hook-stage manual markdownlint

@daniel-salib daniel-salib force-pushed the pr3-mcp-streaming-v2 branch 2 times, most recently from d41d2d4 to 7d76b3a Compare January 7, 2026 23:47
@daniel-salib daniel-salib force-pushed the pr3-mcp-streaming-v2 branch 8 times, most recently from 3966e8b to 0ca0d27 Compare January 8, 2026 11:53
Signed-off-by: Daniel Salib <danielsalib@meta.com>
for event in self._emit_previous_item_done_events(
previous_item, state
):
yield _increment_sequence_number_and_return(event)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the refactor, this looks a lot more clean. Ideally we may want to move all the harmony streaming to another file, but i think it's fine for now.

@chaunceyjiang chaunceyjiang enabled auto-merge (squash) January 9, 2026 01:19
Copy link
Copy Markdown
Collaborator

@chaunceyjiang chaunceyjiang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chaunceyjiang chaunceyjiang merged commit a4ec0c5 into vllm-project:main Jan 9, 2026
48 checks passed
@github-project-automation github-project-automation bot moved this from To Triage to Done in gpt-oss Issues & Enhancements Jan 9, 2026
@chaunceyjiang
Copy link
Copy Markdown
Collaborator

https://buildkite.com/vllm/ci/builds/46594#019baee4-4801-4332-98f5-e9fcafc09c6f


[2026-01-11T21:27:32Z] =========================== short test summary info ============================
--
[2026-01-11T21:27:32Z] FAILED entrypoints/openai/responses/test_harmony.py::test_mcp_code_interpreter_streaming[openai/gpt-oss-20b] - AssertionError: MCP call was not added



@daniel-salib PTAL.

@daniel-salib
Copy link
Copy Markdown
Contributor Author

https://buildkite.com/vllm/ci/builds/46594#019baee4-4801-4332-98f5-e9fcafc09c6f


[2026-01-11T21:27:32Z] =========================== short test summary info ============================
--
[2026-01-11T21:27:32Z] FAILED entrypoints/openai/responses/test_harmony.py::test_mcp_code_interpreter_streaming[openai/gpt-oss-20b] - AssertionError: MCP call was not added

@daniel-salib PTAL.

thanks for flagging - I think the test is flaky because the math question is too simple to always trigger the mcp tool call with the python tool - I find a slightly larger math problem like "123 *. 456" triggers it consistently.

Opened a new PR with a fix: https://github.com/vllm-project/vllm/pull/32153/files

akh64bit pushed a commit to akh64bit/vllm that referenced this pull request Jan 16, 2026
dsuhinin pushed a commit to dsuhinin/vllm that referenced this pull request Jan 21, 2026
…ect#31761)

Signed-off-by: Daniel Salib <danielsalib@meta.com>
Signed-off-by: dsuhinin <suhinin.dmitriy@gmail.com>
ItzDEXX pushed a commit to ItzDEXX/vllm that referenced this pull request Feb 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

frontend gpt-oss Related to GPT-OSS models ready ONLY add when PR is ready to merge/full CI is needed

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants