Commit a14402e
feat(llmobs): [MLOB-4258] add support for OpenAI server-side MCP calls (#15057)
## Description
This PR adds support for server-side MCP calls made via the OpenAI
Responses API.
In the Responses API, LLMs can invoke MCP tools on behalf of the client.
They do this by asking the provided MCP server to list available tools
and then calling the relevant tool.
Our current support for these kinds of interactions is not great: we do
not capture any tool calls, tool results, or tool spans.
This PR provides better support by:
1. Capturing the `McpCall` output item and parsing it into a Tool Call
and Tool Result for the current active LLM span
2. Generating a Tool span to represent the server-side tool invocation
_(note that this tool span is a child of the active LLM span since it
technically happens within the LLM operation)_
3. Adding any tools returned from the MCP server to the LLM's available
tools field.
## Manual Testing
I manually tested my changes with the following script:
```
import asyncio
from ddtrace.llmobs import LLMObs
from agents import set_default_openai_client
from agents.tracing import set_tracing_disabled
from dd_internal_authentication.client import JWTDDToolAuthClientTokenManager
from openai import AsyncOpenAI
LLMObs.enable(
ml_app="nicole-test",
site="datadoghq.com"
)
set_tracing_disabled(True)
token=JWTDDToolAuthClientTokenManager.instance(name="rapid-ai-platform", datacenter='us1.staging.dog').get_token("rapid-ai-platform")
ai_gateway_client = AsyncOpenAI(base_url="https://ai-gateway.us1.staging.dog/v1", default_headers={"source": "nicole-test", "org-id": "2"}, api_key=token)
set_default_openai_client(ai_gateway_client)
async def main():
resp = await ai_gateway_client.responses.create(
model="gpt-5",
tools=[
{
"type": "mcp",
"server_label": "dice_roller",
"server_description": "Public dice-roller MCP server for testing.",
"server_url": "https://dice-rolling-mcp.vercel.app/mcp", # or use the FastMCP URL
"require_approval": "never",
},
],
input="Roll 2d4+1",
)
print(resp)
if __name__ == "__main__":
asyncio.run(main())
```
### Before
Before, our experience for these types of server-side MCP use cases was
very poor. Running the script, I get this
[trace](https://app.datadoghq.com/llm/traces?query=%40ml_app%3Anicole-test%20%40event_type%3Aspan%20%40parent_id%3Aundefined&agg_m=count&agg_m_source=base&agg_t=count&fromUser=false&sp=%5B%7B%22p%22%3A%7B%22eventId%22%3A%22AwAAAZo7t33VDIDW2gAAABhBWm83dDMzVkFBRHU0Q2I1ZDNYT0FBQUEAAAAkZjE5YTNiYmEtMGM3NC00NzVlLWFlZDQtY2ExZmVmYWU2ZDRkAABNvg%22%7D%2C%22i%22%3A%22llm-obs-panel%22%7D%5D&spanId=2741126858136455689&start=1761936196353&end=1761939796353&paused=false)
which does not parse the MCP server side calls:
<img width="2668" height="1304" alt="image"
src="https://github.com/user-attachments/assets/51426be1-d670-45ad-b2f2-d3dab8a85f52"
/>
### After
With the changes in this PR, the
[trace](https://app.datadoghq.com/llm/traces?query=%40ml_app%3Anicole-test%20%40event_type%3Aspan%20%40parent_id%3Aundefined&agg_m=count&agg_m_source=base&agg_t=count&fromUser=true&sp=%5B%7B%22p%22%3A%7B%22eventId%22%3A%22AwAAAZqSG1GZuwlyswAAABhBWnFTRzFHWkFBQy13SDVmUXVhNkFBQUEAAAAkZjE5YTkyMWMtODg0Mi00M2YwLWJlYmQtNjM5MTdiNjkzNTI0AAAckw%22%7D%2C%22i%22%3A%22llm-obs-panel%22%7D%5D&spanId=11798342076467341016&start=1763387074443&end=1763387974443&paused=false)
looks much cleaner and correctly parses all the information related to
the MCP usage.
#### Tool Calls and Tool Results are highlighted:
<img width="1988" height="442" alt="image"
src="https://github.com/user-attachments/assets/8db2cdbf-425a-426b-ab85-3e5d13dd439e"
/>
#### Available Tools from the MCP server are captured:
<img width="2030" height="882" alt="image"
src="https://github.com/user-attachments/assets/e7153191-b852-484a-817d-b5d24c687c2b"
/>
#### Separate tool span is emitted:
<img width="1376" height="612" alt="image"
src="https://github.com/user-attachments/assets/7ceaa977-61ff-4917-af53-6a97babf86e5"
/>
I also tried this out with more than one tool call in this
[trace](https://app.datadoghq.com/llm/traces?query=%40ml_app%3Anicole-test%20%40event_type%3Aspan%20%40parent_id%3Aundefined&agg_m=count&agg_m_source=base&agg_t=count&fromUser=false&sp=%5B%7B%22p%22%3A%7B%22eventId%22%3A%22AwAAAZqSHLq7ndIu6wAAABhBWnFTSExxN0FBRG1Md0RzUlVWbUFBQUEAAAAkZjE5YTkyMWMtZjcxMi00ZGU5LTg4ODItN2Q5NTdlNGU2MTliAAACpA%22%7D%2C%22i%22%3A%22llm-obs-panel%22%7D%5D&spanId=4722629383297745650&start=1763387152732&end=1763388052732&paused=false).
## Risks
<!-- Note any risks associated with this change, or "None" if no risks
-->
## Additional Notes
<!-- Any other information that would be helpful for reviewers -->
---------
Co-authored-by: Yun Kim <[email protected]>1 parent 9eb932e commit a14402e
File tree
7 files changed
+346
-21
lines changed- ddtrace
- contrib/internal/openai
- llmobs/_integrations
- releasenotes/notes
- tests/contrib/openai
7 files changed
+346
-21
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
10 | 11 | | |
| 12 | + | |
| 13 | + | |
11 | 14 | | |
12 | 15 | | |
13 | 16 | | |
| |||
520 | 523 | | |
521 | 524 | | |
522 | 525 | | |
| 526 | + | |
523 | 527 | | |
524 | 528 | | |
525 | 529 | | |
| |||
528 | 532 | | |
529 | 533 | | |
530 | 534 | | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
531 | 564 | | |
532 | 565 | | |
533 | 566 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
16 | 17 | | |
| 18 | + | |
17 | 19 | | |
18 | 20 | | |
19 | 21 | | |
| |||
27 | 29 | | |
28 | 30 | | |
29 | 31 | | |
| 32 | + | |
30 | 33 | | |
31 | 34 | | |
32 | 35 | | |
33 | 36 | | |
34 | 37 | | |
35 | 38 | | |
| 39 | + | |
| 40 | + | |
36 | 41 | | |
37 | 42 | | |
38 | 43 | | |
| |||
105 | 110 | | |
106 | 111 | | |
107 | 112 | | |
108 | | - | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
109 | 118 | | |
110 | 119 | | |
111 | 120 | | |
| |||
121 | 130 | | |
122 | 131 | | |
123 | 132 | | |
124 | | - | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
125 | 136 | | |
126 | 137 | | |
127 | 138 | | |
| |||
153 | 164 | | |
154 | 165 | | |
155 | 166 | | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
156 | 188 | | |
157 | 189 | | |
158 | 190 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
231 | 231 | | |
232 | 232 | | |
233 | 233 | | |
234 | | - | |
| 234 | + | |
235 | 235 | | |
236 | 236 | | |
237 | 237 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
628 | 628 | | |
629 | 629 | | |
630 | 630 | | |
631 | | - | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
632 | 634 | | |
633 | | - | |
| 635 | + | |
| 636 | + | |
634 | 637 | | |
635 | 638 | | |
636 | 639 | | |
637 | 640 | | |
638 | 641 | | |
639 | 642 | | |
| 643 | + | |
640 | 644 | | |
641 | 645 | | |
642 | | - | |
| 646 | + | |
643 | 647 | | |
644 | 648 | | |
645 | 649 | | |
646 | | - | |
| 650 | + | |
647 | 651 | | |
648 | | - | |
| 652 | + | |
649 | 653 | | |
650 | | - | |
| 654 | + | |
651 | 655 | | |
652 | 656 | | |
653 | | - | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
654 | 660 | | |
655 | 661 | | |
656 | | - | |
| 662 | + | |
657 | 663 | | |
658 | 664 | | |
659 | 665 | | |
| |||
664 | 670 | | |
665 | 671 | | |
666 | 672 | | |
| 673 | + | |
667 | 674 | | |
668 | 675 | | |
669 | 676 | | |
| |||
707 | 714 | | |
708 | 715 | | |
709 | 716 | | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
710 | 746 | | |
711 | 747 | | |
712 | 748 | | |
713 | 749 | | |
714 | 750 | | |
715 | | - | |
| 751 | + | |
716 | 752 | | |
717 | 753 | | |
718 | 754 | | |
| |||
802 | 838 | | |
803 | 839 | | |
804 | 840 | | |
805 | | - | |
| 841 | + | |
| 842 | + | |
| 843 | + | |
806 | 844 | | |
807 | 845 | | |
808 | 846 | | |
| |||
851 | 889 | | |
852 | 890 | | |
853 | 891 | | |
854 | | - | |
| 892 | + | |
855 | 893 | | |
856 | 894 | | |
857 | | - | |
858 | | - | |
| 895 | + | |
| 896 | + | |
859 | 897 | | |
860 | 898 | | |
861 | 899 | | |
| |||
878 | 916 | | |
879 | 917 | | |
880 | 918 | | |
881 | | - | |
| 919 | + | |
882 | 920 | | |
883 | 921 | | |
884 | 922 | | |
885 | 923 | | |
886 | | - | |
| 924 | + | |
| 925 | + | |
| 926 | + | |
887 | 927 | | |
888 | 928 | | |
889 | 929 | | |
| |||
1198 | 1238 | | |
1199 | 1239 | | |
1200 | 1240 | | |
1201 | | - | |
| 1241 | + | |
1202 | 1242 | | |
1203 | 1243 | | |
1204 | 1244 | | |
1205 | 1245 | | |
1206 | 1246 | | |
| 1247 | + | |
1207 | 1248 | | |
1208 | 1249 | | |
1209 | | - | |
| 1250 | + | |
1210 | 1251 | | |
1211 | 1252 | | |
1212 | 1253 | | |
1213 | | - | |
| 1254 | + | |
1214 | 1255 | | |
1215 | 1256 | | |
1216 | 1257 | | |
| |||
Lines changed: 4 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
0 commit comments