-
Notifications
You must be signed in to change notification settings - Fork 847
Add support for custom headers in HostedMcpServerTool #7053
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add support for custom headers in HostedMcpServerTool #7053
Conversation
Introduces a Headers property to HostedMcpServerTool for specifying additional request headers. Updates OpenAIResponsesChatClient to pass these headers when creating MCP tools and adds unit tests to verify header roundtripping.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds support for custom HTTP headers in HostedMcpServerTool, enabling users to specify additional request headers when communicating with remote MCP servers.
- Introduces a
Headersproperty of typeIDictionary<string, string>?to theHostedMcpServerToolclass - Updates
OpenAIResponsesChatClientto pass these headers to both variants ofResponseTool.CreateMcpTool() - Adds roundtrip tests to verify the Headers property can be set, retrieved, and cleared
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/Libraries/Microsoft.Extensions.AI.Abstractions/Tools/HostedMcpServerTool.cs | Adds the nullable Headers property with XML documentation |
| src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIResponsesChatClient.cs | Passes mcpTool.Headers parameter to both CreateMcpTool overloads (URI-based and connector ID-based) |
| test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Tools/HostedMcpServerToolTests.cs | Adds assertions to verify Headers can be set to a dictionary, retrieved, and reset to null |
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Tools/HostedMcpServerToolTests.cs
Show resolved
Hide resolved
|
@stephentoub any chance I can get an assignee on this one to help with getting it through? This is #7049 with the reverts. |
|
Thanks for your PR @echapmanFromBunnings. We originally pursued this API shape because OpenAI supported headers in the request, while Anthropic only supported passing an auth token. I initially took the broader approach and used a dictionary in Later, OpenAI added support for passing the token in the same way Anthropic does, which suggested to me that there might not be many use cases for headers beyond auth and that we don't need a headers dictionary in the class. For the sake of doing an informed decision, could you share more about what kinds of headers you need to pass to your MCP server? If we bring the headers dictionary back, we need to reconsider if we want to keep |
|
Hi @jozkee Since the MCP server essentially operates as an HTTP client, couldn’t we simply allow these headers to leverage the underlying protocol? In this context, headers are not being used for authentication, so encountering a header should not imply it is solely for that purpose. These are two distinct concerns and should be treated as such. |
src/Libraries/Microsoft.Extensions.AI.Abstractions/Tools/HostedMcpServerTool.cs
Outdated
Show resolved
Hide resolved
src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIResponsesChatClient.cs
Outdated
Show resolved
Hide resolved
src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIResponsesChatClient.cs
Outdated
Show resolved
Hide resolved
|
CI errors are about CompatibilitySuppression.xml since we are bringing the property back. I suspect we need to remove them from: extensions/src/Libraries/Microsoft.Extensions.AI.Abstractions/CompatibilitySuppressions.xml Lines 25 to 45 in 1ab483a
cc @joperezr |
Deleted baseline suppressions for the Headers property and its setter in HostedMcpServerTool from CompatibilitySuppressions.xml.
Expanded XML documentation for the Headers property in HostedMcpServerTool to clarify usage and behavior. Minor formatting adjustments in OpenAIResponsesChatClient for MCP tool creation calls.
|
Addressed all PR comments, and added extensive documents on that headers prop @jozkee |
Deleted baseline suppressions for get_Headers and set_Headers members of HostedMcpServerTool across net8.0, net9.0, and netstandard2.0. This likely reflects removal or resolution of related compatibility issues.
…manFromBunnings/extensions into AddHeaderToMcpToolCreation
src/Libraries/Microsoft.Extensions.AI.Abstractions/Tools/HostedMcpServerTool.cs
Show resolved
Hide resolved
| /// <para> | ||
| /// The underlying provider may respond with an error if <see cref="AuthorizationToken"/> is set and an Authorization header is included. | ||
| /// </para> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a provider would have problems with that, wouldn't it be better for it to just not send the auth header? Basically say that if AuthorizationToken is set, any Authorization header in Headers may be ignored, or alternatively say that AuthorizationToken may be ignored if Headers contains an Authorization header?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That said, what's the benefit of having both? If we only had Headers, is that not sufficient? The token can be extracted from an auth header.
It feels awkward having both, like there are two possible sources of truth. (At the same time I understand that for the common case, having the simple AuthorizationToken property is simpler.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we only had Headers, is that not sufficient?
It is sufficient but it still creates friction with Claude and OpenAI Connectors that only support Authorization Tokens. Converting the Authorization HTTP Header implies trimming the "Bearer " prefix.
If a provider would have problems with that, wouldn't it be better for it to just not send the auth header? Basically say that if AuthorizationToken is set, any Authorization header in Headers may be ignored, or alternatively say that AuthorizationToken may be ignored if Headers contains an Authorization header?
That would mean us (MEAI) deciding for users and providers in this behavior. My thinking was that is better to simply rely what was given to us to the provider. If we are going to decide how to handle this in MEAI.OpenAI I would rather choose throwing to signal there's a bug, which aligns with OpenAI's current behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it still creates friction with Claude and OpenAI Connectors that only support Authorization Tokens. Converting the Authorization HTTP Header implies trimming the "Bearer " prefix.
Having both properties introduces that friction. Implementations will need to account for both existing and the authorization token being in either the AuthorizationToken property or the Headers. A developer could put it in either and the implementation should still work. If a particular provider's API only supports one or the other, it'll need to extract the relevant data. Forcing a developer to know the details of the provider breaks the abstraction.
That would mean us (MEAI) deciding for users and providers in this behavior.
We have to provide a consistent abstraction. A developer using these APIs should not need to know the details of the exact provider being used. That's the point of an abstraction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not all authorisations are bearer tokens auth schemes also, so it's not just a trim and push into the token property solution.
What's the thoughts here? The documentation is solving this really (so agents should be satisfied) and this is really just protecting a user from doing something incorrect. Do I go further and provide valid authorisation header setter validation also? Or block authorisation from being set as a header at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could add the property representing the authorization header as bearer scheme, it would be another path to interact with the dictionary, but we could make that clear in the docs.
public string? BearerToken
{
get => Headers?["Authorization"] is string token && token.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase) ? token : null;
set
{
var headers = (Headers ??= new Dictionary<string, string>());
headers["Authorization"] = $"Bearer {value}"
}
}Or we could add a method GetAuthorizationToken(AuthScheme schene) in case bearer gets relegated by another scheme.
I think we could add this later, hence the "must".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Except that if we want to have such a property, we could just keep the public API that's there and avoid the breaking change / churn.
Let's design the final end-to-end that we really want here, factoring in everything we know about all implementers and what consumers are going go to want to do. We're at this point with these APIs where we need to stop churning them, so we want to get this next round right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would keep the headers as the source of truth while making it easier to implementors to map it to providers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a problem with this kind of set up, though... it creates an ordering issue between properties, e.g.
new HostedMcpServerTool(...)
{
AuthorizationToken = ...,
Headers = ...,
}has very different behavior from:
new HostedMcpServerTool(...)
{
Headers = ...,
AuthorizationToken = ...,
}There are various ways to address that, such as by making at least one of them non-settable, e.g. if the Headers property returns a non-null mutable collection and only has a getter. That of course can slightly complicate other use cases, though, so the tradeoffs need to be evaluated.
Otherwise, having AuthorizationToken as an accelerator into Headers makes a lot of sense. We still need to evaluate what each implementer would do with the data, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a problem with this kind of set up, though... it creates an ordering issue between properties ... if the Headers property returns a non-null mutable collection and only has a getter. That of course can slightly complicate other use cases, though, so the tradeoffs need to be evaluated.
Great catch and great suggestion. Headers as get-only allows this to work and provide better usability; the token as get-only will just force folks to walk the long path.
As discussed offline, this can encumber cases that want to [re]use an existing dictionary as the Headers, forcing them to manually iterate to copy the elements. That seems to me like an acceptable trade-off.
We still need to evaluate what each implementer would do with the data, though
If headers are supported, implementors should always send the headers, if not (Claude and OpenAI with Connector), send the token and ignore all other headers.
src/Libraries/Microsoft.Extensions.AI.Abstractions/Tools/HostedMcpServerTool.cs
Outdated
Show resolved
Hide resolved
| mcpTool.AuthorizationToken, | ||
| mcpTool.ServerDescription); | ||
| mcpTool.ServerDescription, | ||
| mcpTool.Headers); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be validating these with unit tests, ala
extensions/test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIResponseClientTests.cs
Lines 1190 to 2140 in e9b33a7
| [Theory] | |
| [InlineData("user")] | |
| [InlineData("tool")] | |
| public async Task McpToolCall_ApprovalRequired_NonStreaming(string role) | |
| { | |
| string input = """ | |
| { | |
| "model": "gpt-4o-mini", | |
| "tools": [ | |
| { | |
| "type": "mcp", | |
| "server_label": "deepwiki", | |
| "server_url": "https://mcp.deepwiki.com/mcp" | |
| } | |
| ], | |
| "tool_choice": "auto", | |
| "input": [ | |
| { | |
| "type": "message", | |
| "role": "user", | |
| "content": [ | |
| { | |
| "type": "input_text", | |
| "text": "Tell me the path to the README.md file for Microsoft.Extensions.AI.Abstractions in the dotnet/extensions repository" | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| """; | |
| string output = """ | |
| { | |
| "id": "resp_04e29d5bdd80bd9f0068e6b01f786081a29148febb92892aee", | |
| "object": "response", | |
| "created_at": 1759948831, | |
| "status": "completed", | |
| "background": false, | |
| "error": null, | |
| "incomplete_details": null, | |
| "instructions": null, | |
| "max_output_tokens": null, | |
| "max_tool_calls": null, | |
| "model": "gpt-4o-mini-2024-07-18", | |
| "output": [ | |
| { | |
| "id": "mcpr_04e29d5bdd80bd9f0068e6b022a9c081a2ae898104b7a75051", | |
| "type": "mcp_approval_request", | |
| "arguments": "{\"repoName\":\"dotnet/extensions\"}", | |
| "name": "ask_question", | |
| "server_label": "deepwiki" | |
| } | |
| ], | |
| "parallel_tool_calls": true, | |
| "previous_response_id": null, | |
| "prompt_cache_key": null, | |
| "reasoning": { | |
| "effort": null, | |
| "summary": null | |
| }, | |
| "safety_identifier": null, | |
| "service_tier": "default", | |
| "store": true, | |
| "temperature": 1.0, | |
| "text": { | |
| "format": { | |
| "type": "text" | |
| }, | |
| "verbosity": "medium" | |
| }, | |
| "tool_choice": "auto", | |
| "tools": [ | |
| { | |
| "type": "mcp", | |
| "allowed_tools": null, | |
| "headers": null, | |
| "require_approval": "always", | |
| "server_description": null, | |
| "server_label": "deepwiki", | |
| "server_url": "https://mcp.deepwiki.com/<redacted>" | |
| } | |
| ], | |
| "top_logprobs": 0, | |
| "top_p": 1.0, | |
| "truncation": "disabled", | |
| "usage": { | |
| "input_tokens": 193, | |
| "input_tokens_details": { | |
| "cached_tokens": 0 | |
| }, | |
| "output_tokens": 23, | |
| "output_tokens_details": { | |
| "reasoning_tokens": 0 | |
| }, | |
| "total_tokens": 216 | |
| }, | |
| "user": null, | |
| "metadata": {} | |
| } | |
| """; | |
| var chatOptions = new ChatOptions | |
| { | |
| Tools = [new HostedMcpServerTool("deepwiki", new Uri("https://mcp.deepwiki.com/mcp"))] | |
| }; | |
| McpServerToolApprovalRequestContent approvalRequest; | |
| using (VerbatimHttpHandler handler = new(input, output)) | |
| using (HttpClient httpClient = new(handler)) | |
| using (IChatClient client = CreateResponseClient(httpClient, "gpt-4o-mini")) | |
| { | |
| var response = await client.GetResponseAsync( | |
| "Tell me the path to the README.md file for Microsoft.Extensions.AI.Abstractions in the dotnet/extensions repository", | |
| chatOptions); | |
| approvalRequest = Assert.Single(response.Messages.SelectMany(m => m.Contents).OfType<McpServerToolApprovalRequestContent>()); | |
| chatOptions.ConversationId = response.ConversationId; | |
| } | |
| input = $$""" | |
| { | |
| "previous_response_id": "resp_04e29d5bdd80bd9f0068e6b01f786081a29148febb92892aee", | |
| "model": "gpt-4o-mini", | |
| "tools": [ | |
| { | |
| "type": "mcp", | |
| "server_label": "deepwiki", | |
| "server_url": "https://mcp.deepwiki.com/mcp" | |
| } | |
| ], | |
| "tool_choice": "auto", | |
| "input": [ | |
| { | |
| "type": "mcp_approval_response", | |
| "approval_request_id": "mcpr_04e29d5bdd80bd9f0068e6b022a9c081a2ae898104b7a75051", | |
| "approve": true | |
| } | |
| ] | |
| } | |
| """; | |
| output = """ | |
| { | |
| "id": "resp_06ee3b1962eeb8470068e6b21c377081a3a20dbf60eee7a736", | |
| "object": "response", | |
| "created_at": 1759949340, | |
| "status": "completed", | |
| "background": false, | |
| "error": null, | |
| "incomplete_details": null, | |
| "instructions": null, | |
| "max_output_tokens": null, | |
| "max_tool_calls": null, | |
| "model": "gpt-4o-mini-2024-07-18", | |
| "output": [ | |
| { | |
| "id": "mcp_06ee3b1962eeb8470068e6b21cbaa081a3b5aa2a6c989f4c6f", | |
| "type": "mcp_call", | |
| "status": "completed", | |
| "approval_request_id": "mcpr_06ee3b1962eeb8470068e6b192985c81a383a16059ecd8230e", | |
| "arguments": "{\"repoName\":\"dotnet/extensions\",\"question\":\"What is the path to the README.md file for Microsoft.Extensions.AI.Abstractions?\"}", | |
| "error": null, | |
| "name": "ask_question", | |
| "output": "The `README.md` file for `Microsoft.Extensions.AI.Abstractions` is located at `src/Libraries/Microsoft.Extensions.AI.Abstractions/README.md` within the `dotnet/extensions` repository. This file provides an overview of the package, including installation instructions and usage examples for its core interfaces like `IChatClient` and `IEmbeddingGenerator`. \n\n## Path to README.md\n\nThe specific path to the `README.md` file for the `Microsoft.Extensions.AI.Abstractions` project is `src/Libraries/Microsoft.Extensions.AI.Abstractions/README.md`. This path is also referenced in the `AI Extensions Framework` wiki page as a relevant source file. \n\n## Notes\n\nThe `Packaging.targets` file in the `eng/MSBuild` directory indicates that `README.md` files are included in packages when `IsPackable` and `IsShipping` properties are true. This suggests that the `README.md` file located at `src/Libraries/Microsoft.Extensions.AI.Abstractions/README.md` is intended to be part of the distributed NuGet package for `Microsoft.Extensions.AI.Abstractions`. \n\nWiki pages you might want to explore:\n- [AI Extensions Framework (dotnet/extensions)](/wiki/dotnet/extensions#3)\n- [Chat Completion (dotnet/extensions)](/wiki/dotnet/extensions#3.3)\n\nView this search on DeepWiki: https://deepwiki.com/search/what-is-the-path-to-the-readme_315595bd-9b39-4f04-9fa3-42dc778fa9f3\n", | |
| "server_label": "deepwiki" | |
| }, | |
| { | |
| "id": "msg_06ee3b1962eeb8470068e6b226ab0081a39fccce9aa47aedbc", | |
| "type": "message", | |
| "status": "completed", | |
| "content": [ | |
| { | |
| "type": "output_text", | |
| "annotations": [], | |
| "logprobs": [], | |
| "text": "The `README.md` file for `Microsoft.Extensions.AI.Abstractions` is located at:\n\n```\nsrc/Libraries/Microsoft.Extensions.AI.Abstractions/README.md\n```\n\nThis file provides an overview of the `Microsoft.Extensions.AI.Abstractions` package, including installation instructions and usage examples for its core interfaces like `IChatClient` and `IEmbeddingGenerator`." | |
| } | |
| ], | |
| "role": "assistant" | |
| } | |
| ], | |
| "parallel_tool_calls": true, | |
| "previous_response_id": "resp_06ee3b1962eeb8470068e6b18e0db881a3bdfd255a60327cdc", | |
| "prompt_cache_key": null, | |
| "reasoning": { | |
| "effort": null, | |
| "summary": null | |
| }, | |
| "safety_identifier": null, | |
| "service_tier": "default", | |
| "store": true, | |
| "temperature": 1.0, | |
| "text": { | |
| "format": { | |
| "type": "text" | |
| }, | |
| "verbosity": "medium" | |
| }, | |
| "tool_choice": "auto", | |
| "tools": [ | |
| { | |
| "type": "mcp", | |
| "allowed_tools": null, | |
| "headers": null, | |
| "require_approval": "always", | |
| "server_description": null, | |
| "server_label": "deepwiki", | |
| "server_url": "https://mcp.deepwiki.com/<redacted>" | |
| } | |
| ], | |
| "top_logprobs": 0, | |
| "top_p": 1.0, | |
| "truncation": "disabled", | |
| "usage": { | |
| "input_tokens": 542, | |
| "input_tokens_details": { | |
| "cached_tokens": 0 | |
| }, | |
| "output_tokens": 72, | |
| "output_tokens_details": { | |
| "reasoning_tokens": 0 | |
| }, | |
| "total_tokens": 614 | |
| }, | |
| "user": null, | |
| "metadata": {} | |
| } | |
| """; | |
| using (VerbatimHttpHandler handler = new(input, output)) | |
| using (HttpClient httpClient = new(handler)) | |
| using (IChatClient client = CreateResponseClient(httpClient, "gpt-4o-mini")) | |
| { | |
| var response = await client.GetResponseAsync( | |
| new ChatMessage(new ChatRole(role), [approvalRequest.CreateResponse(true)]), chatOptions); | |
| Assert.NotNull(response); | |
| Assert.Equal("resp_06ee3b1962eeb8470068e6b21c377081a3a20dbf60eee7a736", response.ResponseId); | |
| Assert.Equal("resp_06ee3b1962eeb8470068e6b21c377081a3a20dbf60eee7a736", response.ConversationId); | |
| Assert.Equal("gpt-4o-mini-2024-07-18", response.ModelId); | |
| Assert.Equal(DateTimeOffset.FromUnixTimeSeconds(1_759_949_340), response.CreatedAt); | |
| Assert.Null(response.FinishReason); | |
| var message = Assert.Single(response.Messages); | |
| Assert.Equal(ChatRole.Assistant, response.Messages[0].Role); | |
| Assert.Equal("The `README.md` file for `Microsoft.Extensions.AI.Abstractions` is located at:\n\n```\nsrc/Libraries/Microsoft.Extensions.AI.Abstractions/README.md\n```\n\nThis file provides an overview of the `Microsoft.Extensions.AI.Abstractions` package, including installation instructions and usage examples for its core interfaces like `IChatClient` and `IEmbeddingGenerator`.", response.Messages[0].Text); | |
| Assert.Equal(3, message.Contents.Count); | |
| var call = Assert.IsType<McpServerToolCallContent>(message.Contents[0]); | |
| Assert.Equal("mcp_06ee3b1962eeb8470068e6b21cbaa081a3b5aa2a6c989f4c6f", call.CallId); | |
| Assert.Equal("deepwiki", call.ServerName); | |
| Assert.Equal("ask_question", call.ToolName); | |
| Assert.NotNull(call.Arguments); | |
| Assert.Equal(2, call.Arguments.Count); | |
| Assert.Equal("dotnet/extensions", ((JsonElement)call.Arguments["repoName"]!).GetString()); | |
| Assert.Equal("What is the path to the README.md file for Microsoft.Extensions.AI.Abstractions?", ((JsonElement)call.Arguments["question"]!).GetString()); | |
| var result = Assert.IsType<McpServerToolResultContent>(message.Contents[1]); | |
| Assert.Equal("mcp_06ee3b1962eeb8470068e6b21cbaa081a3b5aa2a6c989f4c6f", result.CallId); | |
| Assert.NotNull(result.Output); | |
| Assert.StartsWith("The `README.md` file for `Microsoft.Extensions.AI.Abstractions` is located at", Assert.IsType<TextContent>(Assert.Single(result.Output)).Text); | |
| Assert.NotNull(response.Usage); | |
| Assert.Equal(542, response.Usage.InputTokenCount); | |
| Assert.Equal(72, response.Usage.OutputTokenCount); | |
| Assert.Equal(614, response.Usage.TotalTokenCount); | |
| } | |
| } | |
| [Theory] | |
| [InlineData(false)] | |
| [InlineData(true)] | |
| public async Task McpToolCall_ApprovalNotRequired_NonStreaming(bool rawTool) | |
| { | |
| const string Input = """ | |
| { | |
| "model": "gpt-4o-mini", | |
| "tools": [ | |
| { | |
| "type": "mcp", | |
| "server_label": "deepwiki", | |
| "server_url": "https://mcp.deepwiki.com/mcp", | |
| "require_approval": "never" | |
| } | |
| ], | |
| "tool_choice": "auto", | |
| "input": [ | |
| { | |
| "type": "message", | |
| "role": "user", | |
| "content": [ | |
| { | |
| "type": "input_text", | |
| "text": "Tell me the path to the README.md file for Microsoft.Extensions.AI.Abstractions in the dotnet/extensions repository" | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| """; | |
| const string Output = """ | |
| { | |
| "id": "resp_68be416397ec81918c48ef286530b8140384f747588fc3f5", | |
| "object": "response", | |
| "created_at": 1757299043, | |
| "status": "completed", | |
| "background": false, | |
| "error": null, | |
| "incomplete_details": null, | |
| "instructions": null, | |
| "max_output_tokens": null, | |
| "max_tool_calls": null, | |
| "model": "gpt-4o-mini-2024-07-18", | |
| "output": [ | |
| { | |
| "id": "mcpl_68be4163aa80819185e792abdcde71670384f747588fc3f5", | |
| "type": "mcp_list_tools", | |
| "server_label": "deepwiki", | |
| "tools": [ | |
| { | |
| "annotations": { | |
| "read_only": false | |
| }, | |
| "description": "Get a list of documentation topics for a GitHub repository", | |
| "input_schema": { | |
| "type": "object", | |
| "properties": { | |
| "repoName": { | |
| "type": "string", | |
| "description": "GitHub repository: owner/repo (e.g. \"facebook/react\")" | |
| } | |
| }, | |
| "required": [ | |
| "repoName" | |
| ], | |
| "additionalProperties": false, | |
| "$schema": "http://json-schema.org/draft-07/schema#" | |
| }, | |
| "name": "read_wiki_structure" | |
| }, | |
| { | |
| "annotations": { | |
| "read_only": false | |
| }, | |
| "description": "View documentation about a GitHub repository", | |
| "input_schema": { | |
| "type": "object", | |
| "properties": { | |
| "repoName": { | |
| "type": "string", | |
| "description": "GitHub repository: owner/repo (e.g. \"facebook/react\")" | |
| } | |
| }, | |
| "required": [ | |
| "repoName" | |
| ], | |
| "additionalProperties": false, | |
| "$schema": "http://json-schema.org/draft-07/schema#" | |
| }, | |
| "name": "read_wiki_contents" | |
| }, | |
| { | |
| "annotations": { | |
| "read_only": false | |
| }, | |
| "description": "Ask any question about a GitHub repository", | |
| "input_schema": { | |
| "type": "object", | |
| "properties": { | |
| "repoName": { | |
| "type": "string", | |
| "description": "GitHub repository: owner/repo (e.g. \"facebook/react\")" | |
| }, | |
| "question": { | |
| "type": "string", | |
| "description": "The question to ask about the repository" | |
| } | |
| }, | |
| "required": [ | |
| "repoName", | |
| "question" | |
| ], | |
| "additionalProperties": false, | |
| "$schema": "http://json-schema.org/draft-07/schema#" | |
| }, | |
| "name": "ask_question" | |
| } | |
| ] | |
| }, | |
| { | |
| "id": "mcp_68be4166acfc8191bc5e0a751eed358b0384f747588fc3f5", | |
| "type": "mcp_call", | |
| "approval_request_id": null, | |
| "arguments": "{\"repoName\":\"dotnet/extensions\"}", | |
| "error": null, | |
| "name": "read_wiki_structure", | |
| "output": "Available pages for dotnet/extensions:\n\n- 1 Overview\n- 2 Build System and CI/CD\n- 3 AI Extensions Framework\n - 3.1 Core Abstractions\n - 3.2 AI Function System\n - 3.3 Chat Completion\n - 3.4 Caching System\n - 3.5 Evaluation and Reporting\n- 4 HTTP Resilience and Diagnostics\n - 4.1 Standard Resilience\n - 4.2 Hedging Strategies\n- 5 Telemetry and Compliance\n- 6 Testing Infrastructure\n - 6.1 AI Service Integration Testing\n - 6.2 Time Provider Testing", | |
| "server_label": "deepwiki" | |
| }, | |
| { | |
| "id": "mcp_68be416900f88191837ae0718339a4ce0384f747588fc3f5", | |
| "type": "mcp_call", | |
| "approval_request_id": null, | |
| "arguments": "{\"repoName\":\"dotnet/extensions\",\"question\":\"What is the path to the README.md file for Microsoft.Extensions.AI.Abstractions?\"}", | |
| "error": null, | |
| "name": "ask_question", | |
| "output": "The `README.md` file for `Microsoft.Extensions.AI.Abstractions` is located at `src/Libraries/Microsoft.Extensions.AI.Abstractions/README.md` within the `dotnet/extensions` repository. This file provides an overview of the `Microsoft.Extensions.AI.Abstractions` package, including installation instructions and usage examples for its core interfaces like `IChatClient` and `IEmbeddingGenerator`. \n\n## Path to README.md\n\nThe specific path to the `README.md` file for the `Microsoft.Extensions.AI.Abstractions` project is `src/Libraries/Microsoft.Extensions.AI.Abstractions/README.md`. This path is also referenced in the `AI Extensions Framework` wiki page as a relevant source file. \n\n## Notes\n\nThe `Packaging.targets` file in the `eng/MSBuild` directory indicates that `README.md` files are included in packages when `IsPackable` and `IsShipping` properties are true. This suggests that the `README.md` file located at `src/Libraries/Microsoft.Extensions.AI.Abstractions/README.md` is intended to be part of the distributed NuGet package for `Microsoft.Extensions.AI.Abstractions`. \n\nWiki pages you might want to explore:\n- [AI Extensions Framework (dotnet/extensions)](/wiki/dotnet/extensions#3)\n- [Chat Completion (dotnet/extensions)](/wiki/dotnet/extensions#3.3)\n\nView this search on DeepWiki: https://deepwiki.com/search/what-is-the-path-to-the-readme_315595bd-9b39-4f04-9fa3-42dc778fa9f3\n", | |
| "server_label": "deepwiki" | |
| }, | |
| { | |
| "id": "msg_68be416fb43c819194a1d4ace2643a7e0384f747588fc3f5", | |
| "type": "message", | |
| "status": "completed", | |
| "content": [ | |
| { | |
| "type": "output_text", | |
| "annotations": [], | |
| "logprobs": [], | |
| "text": "The `README.md` file for `Microsoft.Extensions.AI.Abstractions` is located at:\n\n```\nsrc/Libraries/Microsoft.Extensions.AI.Abstractions/README.md\n```\n\nThis file includes an overview, installation instructions, and usage examples related to the package." | |
| } | |
| ], | |
| "role": "assistant" | |
| } | |
| ], | |
| "parallel_tool_calls": true, | |
| "previous_response_id": null, | |
| "prompt_cache_key": null, | |
| "reasoning": { | |
| "effort": null, | |
| "summary": null | |
| }, | |
| "safety_identifier": null, | |
| "service_tier": "default", | |
| "store": true, | |
| "temperature": 1, | |
| "text": { | |
| "format": { | |
| "type": "text" | |
| }, | |
| "verbosity": "medium" | |
| }, | |
| "tool_choice": "auto", | |
| "tools": [ | |
| { | |
| "type": "mcp", | |
| "allowed_tools": null, | |
| "headers": null, | |
| "require_approval": "never", | |
| "server_description": null, | |
| "server_label": "deepwiki", | |
| "server_url": "https://mcp.deepwiki.com/<redacted>" | |
| } | |
| ], | |
| "top_logprobs": 0, | |
| "top_p": 1, | |
| "truncation": "disabled", | |
| "usage": { | |
| "input_tokens": 1329, | |
| "input_tokens_details": { | |
| "cached_tokens": 0 | |
| }, | |
| "output_tokens": 123, | |
| "output_tokens_details": { | |
| "reasoning_tokens": 0 | |
| }, | |
| "total_tokens": 1452 | |
| }, | |
| "user": null, | |
| "metadata": {} | |
| } | |
| """; | |
| using VerbatimHttpHandler handler = new(Input, Output); | |
| using HttpClient httpClient = new(handler); | |
| using IChatClient client = CreateResponseClient(httpClient, "gpt-4o-mini"); | |
| AITool mcpTool = rawTool ? | |
| ResponseTool.CreateMcpTool("deepwiki", serverUri: new("https://mcp.deepwiki.com/mcp"), toolCallApprovalPolicy: new McpToolCallApprovalPolicy(GlobalMcpToolCallApprovalPolicy.NeverRequireApproval)).AsAITool() : | |
| new HostedMcpServerTool("deepwiki", new Uri("https://mcp.deepwiki.com/mcp")) | |
| { | |
| ApprovalMode = HostedMcpServerToolApprovalMode.NeverRequire, | |
| }; | |
| ChatOptions chatOptions = new() | |
| { | |
| Tools = [mcpTool], | |
| }; | |
| var response = await client.GetResponseAsync("Tell me the path to the README.md file for Microsoft.Extensions.AI.Abstractions in the dotnet/extensions repository", chatOptions); | |
| Assert.NotNull(response); | |
| Assert.Equal("resp_68be416397ec81918c48ef286530b8140384f747588fc3f5", response.ResponseId); | |
| Assert.Equal("resp_68be416397ec81918c48ef286530b8140384f747588fc3f5", response.ConversationId); | |
| Assert.Equal("gpt-4o-mini-2024-07-18", response.ModelId); | |
| Assert.Equal(DateTimeOffset.FromUnixTimeSeconds(1_757_299_043), response.CreatedAt); | |
| Assert.Null(response.FinishReason); | |
| var message = Assert.Single(response.Messages); | |
| Assert.Equal(ChatRole.Assistant, response.Messages[0].Role); | |
| Assert.Equal("The `README.md` file for `Microsoft.Extensions.AI.Abstractions` is located at:\n\n```\nsrc/Libraries/Microsoft.Extensions.AI.Abstractions/README.md\n```\n\nThis file includes an overview, installation instructions, and usage examples related to the package.", response.Messages[0].Text); | |
| Assert.Equal(6, message.Contents.Count); | |
| var firstCall = Assert.IsType<McpServerToolCallContent>(message.Contents[1]); | |
| Assert.Equal("mcp_68be4166acfc8191bc5e0a751eed358b0384f747588fc3f5", firstCall.CallId); | |
| Assert.Equal("deepwiki", firstCall.ServerName); | |
| Assert.Equal("read_wiki_structure", firstCall.ToolName); | |
| Assert.NotNull(firstCall.Arguments); | |
| Assert.Single(firstCall.Arguments); | |
| Assert.Equal("dotnet/extensions", ((JsonElement)firstCall.Arguments["repoName"]!).GetString()); | |
| var firstResult = Assert.IsType<McpServerToolResultContent>(message.Contents[2]); | |
| Assert.Equal("mcp_68be4166acfc8191bc5e0a751eed358b0384f747588fc3f5", firstResult.CallId); | |
| Assert.NotNull(firstResult.Output); | |
| Assert.StartsWith("Available pages for dotnet/extensions", Assert.IsType<TextContent>(Assert.Single(firstResult.Output)).Text); | |
| var secondCall = Assert.IsType<McpServerToolCallContent>(message.Contents[3]); | |
| Assert.Equal("mcp_68be416900f88191837ae0718339a4ce0384f747588fc3f5", secondCall.CallId); | |
| Assert.Equal("deepwiki", secondCall.ServerName); | |
| Assert.Equal("ask_question", secondCall.ToolName); | |
| Assert.NotNull(secondCall.Arguments); | |
| Assert.Equal("dotnet/extensions", ((JsonElement)secondCall.Arguments["repoName"]!).GetString()); | |
| Assert.Equal("What is the path to the README.md file for Microsoft.Extensions.AI.Abstractions?", ((JsonElement)secondCall.Arguments["question"]!).GetString()); | |
| var secondResult = Assert.IsType<McpServerToolResultContent>(message.Contents[4]); | |
| Assert.Equal("mcp_68be416900f88191837ae0718339a4ce0384f747588fc3f5", secondResult.CallId); | |
| Assert.NotNull(secondResult.Output); | |
| Assert.StartsWith("The `README.md` file for `Microsoft.Extensions.AI.Abstractions` is located at", Assert.IsType<TextContent>(Assert.Single(secondResult.Output)).Text); | |
| Assert.NotNull(response.Usage); | |
| Assert.Equal(1329, response.Usage.InputTokenCount); | |
| Assert.Equal(123, response.Usage.OutputTokenCount); | |
| Assert.Equal(1452, response.Usage.TotalTokenCount); | |
| } | |
| [Fact] | |
| public async Task McpToolCall_ApprovalNotRequired_Streaming() | |
| { | |
| const string Input = """ | |
| { | |
| "model": "gpt-4o-mini", | |
| "tools": [ | |
| { | |
| "type": "mcp", | |
| "server_label": "deepwiki", | |
| "server_url": "https://mcp.deepwiki.com/mcp", | |
| "require_approval": "never" | |
| } | |
| ], | |
| "tool_choice": "auto", | |
| "input": [ | |
| { | |
| "type": "message", | |
| "role": "user", | |
| "content": [ | |
| { | |
| "type": "input_text", | |
| "text": "Tell me the path to the README.md file for Microsoft.Extensions.AI.Abstractions in the dotnet/extensions repository" | |
| } | |
| ] | |
| } | |
| ], | |
| "stream": true | |
| } | |
| """; | |
| const string Output = """ | |
| event: response.created | |
| data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_68be44fd7298819e82fd82c8516e970d03a2537be0e84a54","object":"response","created_at":1757299965,"status":"in_progress","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"mcp","allowed_tools":null,"headers":null,"require_approval":"never","server_description":null,"server_label":"deepwiki","server_url":"https://mcp.deepwiki.com/<redacted>"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}} | |
| event: response.in_progress | |
| data: {"type":"response.in_progress","sequence_number":1,"response":{"id":"resp_68be44fd7298819e82fd82c8516e970d03a2537be0e84a54","object":"response","created_at":1757299965,"status":"in_progress","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"mcp","allowed_tools":null,"headers":null,"require_approval":"never","server_description":null,"server_label":"deepwiki","server_url":"https://mcp.deepwiki.com/<redacted>"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}}} | |
| event: response.output_item.added | |
| data: {"type":"response.output_item.added","sequence_number":2,"output_index":0,"item":{"id":"mcpl_68be44fd8f68819eba7a74a2f6d27a5a03a2537be0e84a54","type":"mcp_list_tools","server_label":"deepwiki","tools":[]}} | |
| event: response.mcp_list_tools.in_progress | |
| data: {"type":"response.mcp_list_tools.in_progress","sequence_number":3,"output_index":0,"item_id":"mcpl_68be44fd8f68819eba7a74a2f6d27a5a03a2537be0e84a54"} | |
| event: response.mcp_list_tools.completed | |
| data: {"type":"response.mcp_list_tools.completed","sequence_number":4,"output_index":0,"item_id":"mcpl_68be44fd8f68819eba7a74a2f6d27a5a03a2537be0e84a54"} | |
| event: response.output_item.done | |
| data: {"type":"response.output_item.done","sequence_number":5,"output_index":0,"item":{"id":"mcpl_68be44fd8f68819eba7a74a2f6d27a5a03a2537be0e84a54","type":"mcp_list_tools","server_label":"deepwiki","tools":[{"annotations":{"read_only":false},"description":"Get a list of documentation topics for a GitHub repository","input_schema":{"type":"object","properties":{"repoName":{"type":"string","description":"GitHub repository: owner/repo (e.g. \"facebook/react\")"}},"required":["repoName"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"name":"read_wiki_structure"},{"annotations":{"read_only":false},"description":"View documentation about a GitHub repository","input_schema":{"type":"object","properties":{"repoName":{"type":"string","description":"GitHub repository: owner/repo (e.g. \"facebook/react\")"}},"required":["repoName"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"name":"read_wiki_contents"},{"annotations":{"read_only":false},"description":"Ask any question about a GitHub repository","input_schema":{"type":"object","properties":{"repoName":{"type":"string","description":"GitHub repository: owner/repo (e.g. \"facebook/react\")"},"question":{"type":"string","description":"The question to ask about the repository"}},"required":["repoName","question"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"name":"ask_question"}]}} | |
| event: response.output_item.added | |
| data: {"type":"response.output_item.added","sequence_number":6,"output_index":1,"item":{"id":"mcp_68be4503d45c819e89cb574361c8eba003a2537be0e84a54","type":"mcp_call","approval_request_id":null,"arguments":"","error":null,"name":"read_wiki_structure","output":null,"server_label":"deepwiki"}} | |
| event: response.mcp_call.in_progress | |
| data: {"type":"response.mcp_call.in_progress","sequence_number":7,"output_index":1,"item_id":"mcp_68be4503d45c819e89cb574361c8eba003a2537be0e84a54"} | |
| event: response.mcp_call_arguments.delta | |
| data: {"type":"response.mcp_call_arguments.delta","sequence_number":8,"output_index":1,"item_id":"mcp_68be4503d45c819e89cb574361c8eba003a2537be0e84a54","delta":"{\"repoName\":\"dotnet/extensions\"}","obfuscation":""} | |
| event: response.mcp_call_arguments.done | |
| data: {"type":"response.mcp_call_arguments.done","sequence_number":9,"output_index":1,"item_id":"mcp_68be4503d45c819e89cb574361c8eba003a2537be0e84a54","arguments":"{\"repoName\":\"dotnet/extensions\"}"} | |
| event: response.mcp_call.completed | |
| data: {"type":"response.mcp_call.completed","sequence_number":10,"output_index":1,"item_id":"mcp_68be4503d45c819e89cb574361c8eba003a2537be0e84a54"} | |
| event: response.output_item.done | |
| data: {"type":"response.output_item.done","sequence_number":11,"output_index":1,"item":{"id":"mcp_68be4503d45c819e89cb574361c8eba003a2537be0e84a54","type":"mcp_call","approval_request_id":null,"arguments":"{\"repoName\":\"dotnet/extensions\"}","error":null,"name":"read_wiki_structure","output":"Available pages for dotnet/extensions:\n\n- 1 Overview\n- 2 Build System and CI/CD\n- 3 AI Extensions Framework\n - 3.1 Core Abstractions\n - 3.2 AI Function System\n - 3.3 Chat Completion\n - 3.4 Caching System\n - 3.5 Evaluation and Reporting\n- 4 HTTP Resilience and Diagnostics\n - 4.1 Standard Resilience\n - 4.2 Hedging Strategies\n- 5 Telemetry and Compliance\n- 6 Testing Infrastructure\n - 6.1 AI Service Integration Testing\n - 6.2 Time Provider Testing","server_label":"deepwiki"}} | |
| event: response.output_item.added | |
| data: {"type":"response.output_item.added","sequence_number":12,"output_index":2,"item":{"id":"mcp_68be4505f134819e806c002f27cce0c303a2537be0e84a54","type":"mcp_call","approval_request_id":null,"arguments":"","error":null,"name":"ask_question","output":null,"server_label":"deepwiki"}} | |
| event: response.mcp_call.in_progress | |
| data: {"type":"response.mcp_call.in_progress","sequence_number":13,"output_index":2,"item_id":"mcp_68be4505f134819e806c002f27cce0c303a2537be0e84a54"} | |
| event: response.mcp_call_arguments.delta | |
| data: {"type":"response.mcp_call_arguments.delta","sequence_number":14,"output_index":2,"item_id":"mcp_68be4505f134819e806c002f27cce0c303a2537be0e84a54","delta":"{\"repoName\":\"dotnet/extensions\",\"question\":\"What is the path to the README.md file for Microsoft.Extensions.AI.Abstractions?\"}","obfuscation":"IT"} | |
| event: response.mcp_call_arguments.done | |
| data: {"type":"response.mcp_call_arguments.done","sequence_number":15,"output_index":2,"item_id":"mcp_68be4505f134819e806c002f27cce0c303a2537be0e84a54","arguments":"{\"repoName\":\"dotnet/extensions\",\"question\":\"What is the path to the README.md file for Microsoft.Extensions.AI.Abstractions?\"}"} | |
| event: response.mcp_call.completed | |
| data: {"type":"response.mcp_call.completed","sequence_number":16,"output_index":2,"item_id":"mcp_68be4505f134819e806c002f27cce0c303a2537be0e84a54"} | |
| event: response.output_item.done | |
| data: {"type":"response.output_item.done","sequence_number":17,"output_index":2,"item":{"id":"mcp_68be4505f134819e806c002f27cce0c303a2537be0e84a54","type":"mcp_call","approval_request_id":null,"arguments":"{\"repoName\":\"dotnet/extensions\",\"question\":\"What is the path to the README.md file for Microsoft.Extensions.AI.Abstractions?\"}","error":null,"name":"ask_question","output":"The path to the `README.md` file for `Microsoft.Extensions.AI.Abstractions` is `src/Libraries/Microsoft.Extensions.AI.Abstractions/README.md` . This file provides an overview of the `Microsoft.Extensions.AI.Abstractions` library, including installation instructions and usage examples for its core components like `IChatClient` and `IEmbeddingGenerator` .\n\n## README.md Content Overview\nThe `README.md` file for `Microsoft.Extensions.AI.Abstractions` details the purpose of the library, which is to provide abstractions for generative AI components . It includes instructions on how to install the NuGet package `Microsoft.Extensions.AI.Abstractions` .\n\nThe document also provides usage examples for the `IChatClient` interface, which defines methods for interacting with AI services that offer \"chat\" capabilities . This includes examples for requesting both complete and streaming chat responses .\n\nFurthermore, the `README.md` explains the `IEmbeddingGenerator` interface, which is used for generating vector embeddings from input values . It demonstrates how to use `GenerateAsync` to create embeddings . The file also discusses how both `IChatClient` and `IEmbeddingGenerator` implementations can be layered to create pipelines of functionality, incorporating features like caching and telemetry .\n\nNotes:\nThe user's query specifically asked for the path to the `README.md` file for `Microsoft.Extensions.AI.Abstractions`. The provided codebase context, particularly the wiki page for \"AI Extensions Framework\", directly lists this file as a relevant source file . The content of the `README.md` file itself further confirms its relevance to the `Microsoft.Extensions.AI.Abstractions` library.\n\nWiki pages you might want to explore:\n- [AI Extensions Framework (dotnet/extensions)](/wiki/dotnet/extensions#3)\n- [Chat Completion (dotnet/extensions)](/wiki/dotnet/extensions#3.3)\n\nView this search on DeepWiki: https://deepwiki.com/search/what-is-the-path-to-the-readme_bb6bee43-3136-4b21-bc5d-02ca1611d857\n","server_label":"deepwiki"}} | |
| event: response.output_item.added | |
| data: {"type":"response.output_item.added","sequence_number":18,"output_index":3,"item":{"id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","type":"message","status":"in_progress","content":[],"role":"assistant"}} | |
| event: response.content_part.added | |
| data: {"type":"response.content_part.added","sequence_number":19,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""}} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":20,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"The","logprobs":[],"obfuscation":"a5sNdjeWpJXIK"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":21,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" path","logprobs":[],"obfuscation":"2oWbALsHrtv"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":22,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" to","logprobs":[],"obfuscation":"K8lRBCaiusvjP"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":23,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"LP7Xp4jDWA5w"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":24,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" `","logprobs":[],"obfuscation":"2rUNEj0h3wLlee"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":25,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"README","logprobs":[],"obfuscation":"PSbOrCj8y6"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":26,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":".md","logprobs":[],"obfuscation":"Do0BCY4kJ6wQW"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":27,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"`","logprobs":[],"obfuscation":"3fTPkjHu1Oq83DT"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":28,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" file","logprobs":[],"obfuscation":"CI9PXx3sH06"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":29,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" for","logprobs":[],"obfuscation":"fJuaoSPsMge8"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":30,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" `","logprobs":[],"obfuscation":"O1h4Q0T72OM4e7"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":31,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"Microsoft","logprobs":[],"obfuscation":"E2YPgfE"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":32,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":".Extensions","logprobs":[],"obfuscation":"vfVX8"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":33,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":".A","logprobs":[],"obfuscation":"EwDmSMHqymBRl1"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":34,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"I","logprobs":[],"obfuscation":"QQfjze1z7QhvcJE"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":35,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":".A","logprobs":[],"obfuscation":"7fLbFXKbxOMkBi"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":36,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"bst","logprobs":[],"obfuscation":"3p1svK7Jd1N7C"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":37,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"ractions","logprobs":[],"obfuscation":"Cl2xCwTC"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":38,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"`","logprobs":[],"obfuscation":"ObDOKE72QOlXSx9"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":39,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" in","logprobs":[],"obfuscation":"FJwPbDYgh4XjL"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":40,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"e8cV5qt7hEsz"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":41,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" `","logprobs":[],"obfuscation":"Hf8ZQDFLfImh3e"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":42,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"dot","logprobs":[],"obfuscation":"0lh2vLiYye2JI"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":43,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"net","logprobs":[],"obfuscation":"g5fzb2qtk4Piz"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":44,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"/extensions","logprobs":[],"obfuscation":"egpos"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":45,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"`","logprobs":[],"obfuscation":"gXw3bKveEVIKXux"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":46,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" repository","logprobs":[],"obfuscation":"rqhlC"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":47,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" is","logprobs":[],"obfuscation":"YZq9zsRja0g2M"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":48,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":":\n\n","logprobs":[],"obfuscation":"mhDAmaHJUvLGl"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":49,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"``","logprobs":[],"obfuscation":"3XmO5YTsWjzHHf"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":50,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"`\n","logprobs":[],"obfuscation":"4fmXZmdkPxNn8K"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":51,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"src","logprobs":[],"obfuscation":"ifGf4yLEg5pMZ"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":52,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"/L","logprobs":[],"obfuscation":"C1k1toBElpgxyW"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":53,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"ibraries","logprobs":[],"obfuscation":"fdOTYTyp"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":54,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"/M","logprobs":[],"obfuscation":"DyscJIQYaPJugC"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":55,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"icrosoft","logprobs":[],"obfuscation":"PQxU7muP"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":56,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":".Extensions","logprobs":[],"obfuscation":"RCJB8"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":57,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":".A","logprobs":[],"obfuscation":"i92CWxnAkwS4C9"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":58,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"I","logprobs":[],"obfuscation":"qfH8wVJN74vCfBM"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":59,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":".A","logprobs":[],"obfuscation":"LcuBP89lZVCCH9"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":60,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"bst","logprobs":[],"obfuscation":"I8rKDbKN0zylv"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":61,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"ractions","logprobs":[],"obfuscation":"tOgiCPs5"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":62,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"/","logprobs":[],"obfuscation":"jgJjLruTbFJGDhU"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":63,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"README","logprobs":[],"obfuscation":"D5VSEFNde7"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":64,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":".md","logprobs":[],"obfuscation":"7ZGJO5sZOTPBs"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":65,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"\n","logprobs":[],"obfuscation":"7Sv80haKTTwfEWj"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":66,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"``","logprobs":[],"obfuscation":"m1JSvZ8rrpJnH5"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":67,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"`\n\n","logprobs":[],"obfuscation":"U93PMKtCB5Pb5"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":68,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"This","logprobs":[],"obfuscation":"f5veTGedo9nM"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":69,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" file","logprobs":[],"obfuscation":"oEBwvP5FnPK"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":70,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" provides","logprobs":[],"obfuscation":"IVNCYwr"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":71,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" an","logprobs":[],"obfuscation":"3x6WquURIJ3ld"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":72,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" overview","logprobs":[],"obfuscation":"VR9yeiD"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":73,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" of","logprobs":[],"obfuscation":"z46dC1o2FC8Rs"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":74,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" the","logprobs":[],"obfuscation":"YfZGabvmgyoI"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":75,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" library","logprobs":[],"obfuscation":"TamElgEp"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":76,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":",","logprobs":[],"obfuscation":"VfVfqbnHAfsJyJn"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":77,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" installation","logprobs":[],"obfuscation":"CGR"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":78,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" instructions","logprobs":[],"obfuscation":"xst"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":79,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":",","logprobs":[],"obfuscation":"3u5wqRA2RXh2QP8"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":80,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" and","logprobs":[],"obfuscation":"tD4WZmOhepzQ"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":81,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" usage","logprobs":[],"obfuscation":"SadOK826mZ"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":82,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" examples","logprobs":[],"obfuscation":"5VpLKav"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":83,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" for","logprobs":[],"obfuscation":"xPvtjDSUic9E"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":84,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" its","logprobs":[],"obfuscation":"6duK61DX14vx"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":85,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" core","logprobs":[],"obfuscation":"Cz8trPLsCWu"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":86,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" components","logprobs":[],"obfuscation":"Gexuy"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":87,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":".","logprobs":[],"obfuscation":"HVeWkHoX1cc6hVh"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":88,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" If","logprobs":[],"obfuscation":"G1TOxxwvSEq4L"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":89,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" you","logprobs":[],"obfuscation":"xQlKeOixd1hv"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":90,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" have","logprobs":[],"obfuscation":"bX6P0qgFPnR"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":91,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" any","logprobs":[],"obfuscation":"KxH8EiMzXa1N"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":92,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" more","logprobs":[],"obfuscation":"kA0kxRPPqru"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":93,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" questions","logprobs":[],"obfuscation":"9HRCyD"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":94,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" about","logprobs":[],"obfuscation":"yYFZhtsSfc"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":95,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" it","logprobs":[],"obfuscation":"zpyEAwPWl8Ozh"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":96,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":",","logprobs":[],"obfuscation":"ivjn00lbmzDHiFU"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":97,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" feel","logprobs":[],"obfuscation":"O2edXDmkBqt"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":98,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" free","logprobs":[],"obfuscation":"MlpWh7p0P1F"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":99,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" to","logprobs":[],"obfuscation":"uMNfozGkKe6xW"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":100,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":" ask","logprobs":[],"obfuscation":"6rMOxwXhR8RY"} | |
| event: response.output_text.delta | |
| data: {"type":"response.output_text.delta","sequence_number":101,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"delta":"!","logprobs":[],"obfuscation":"QPZMdhS0e5vYuRl"} | |
| event: response.output_text.done | |
| data: {"type":"response.output_text.done","sequence_number":102,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"text":"The path to the `README.md` file for `Microsoft.Extensions.AI.Abstractions` in the `dotnet/extensions` repository is:\n\n```\nsrc/Libraries/Microsoft.Extensions.AI.Abstractions/README.md\n```\n\nThis file provides an overview of the library, installation instructions, and usage examples for its core components. If you have any more questions about it, feel free to ask!","logprobs":[]} | |
| event: response.content_part.done | |
| data: {"type":"response.content_part.done","sequence_number":103,"item_id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","output_index":3,"content_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"The path to the `README.md` file for `Microsoft.Extensions.AI.Abstractions` in the `dotnet/extensions` repository is:\n\n```\nsrc/Libraries/Microsoft.Extensions.AI.Abstractions/README.md\n```\n\nThis file provides an overview of the library, installation instructions, and usage examples for its core components. If you have any more questions about it, feel free to ask!"}} | |
| event: response.output_item.done | |
| data: {"type":"response.output_item.done","sequence_number":104,"output_index":3,"item":{"id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The path to the `README.md` file for `Microsoft.Extensions.AI.Abstractions` in the `dotnet/extensions` repository is:\n\n```\nsrc/Libraries/Microsoft.Extensions.AI.Abstractions/README.md\n```\n\nThis file provides an overview of the library, installation instructions, and usage examples for its core components. If you have any more questions about it, feel free to ask!"}],"role":"assistant"}} | |
| event: response.completed | |
| data: {"type":"response.completed","sequence_number":105,"response":{"id":"resp_68be44fd7298819e82fd82c8516e970d03a2537be0e84a54","object":"response","created_at":1757299965,"status":"completed","background":false,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[{"id":"mcpl_68be44fd8f68819eba7a74a2f6d27a5a03a2537be0e84a54","type":"mcp_list_tools","server_label":"deepwiki","tools":[{"annotations":{"read_only":false},"description":"Get a list of documentation topics for a GitHub repository","input_schema":{"type":"object","properties":{"repoName":{"type":"string","description":"GitHub repository: owner/repo (e.g. \"facebook/react\")"}},"required":["repoName"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"name":"read_wiki_structure"},{"annotations":{"read_only":false},"description":"View documentation about a GitHub repository","input_schema":{"type":"object","properties":{"repoName":{"type":"string","description":"GitHub repository: owner/repo (e.g. \"facebook/react\")"}},"required":["repoName"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"name":"read_wiki_contents"},{"annotations":{"read_only":false},"description":"Ask any question about a GitHub repository","input_schema":{"type":"object","properties":{"repoName":{"type":"string","description":"GitHub repository: owner/repo (e.g. \"facebook/react\")"},"question":{"type":"string","description":"The question to ask about the repository"}},"required":["repoName","question"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"name":"ask_question"}]},{"id":"mcp_68be4503d45c819e89cb574361c8eba003a2537be0e84a54","type":"mcp_call","approval_request_id":null,"arguments":"{\"repoName\":\"dotnet/extensions\"}","error":null,"name":"read_wiki_structure","output":"Available pages for dotnet/extensions:\n\n- 1 Overview\n- 2 Build System and CI/CD\n- 3 AI Extensions Framework\n - 3.1 Core Abstractions\n - 3.2 AI Function System\n - 3.3 Chat Completion\n - 3.4 Caching System\n - 3.5 Evaluation and Reporting\n- 4 HTTP Resilience and Diagnostics\n - 4.1 Standard Resilience\n - 4.2 Hedging Strategies\n- 5 Telemetry and Compliance\n- 6 Testing Infrastructure\n - 6.1 AI Service Integration Testing\n - 6.2 Time Provider Testing","server_label":"deepwiki"},{"id":"mcp_68be4505f134819e806c002f27cce0c303a2537be0e84a54","type":"mcp_call","approval_request_id":null,"arguments":"{\"repoName\":\"dotnet/extensions\",\"question\":\"What is the path to the README.md file for Microsoft.Extensions.AI.Abstractions?\"}","error":null,"name":"ask_question","output":"The path to the `README.md` file for `Microsoft.Extensions.AI.Abstractions` is `src/Libraries/Microsoft.Extensions.AI.Abstractions/README.md` . This file provides an overview of the `Microsoft.Extensions.AI.Abstractions` library, including installation instructions and usage examples for its core components like `IChatClient` and `IEmbeddingGenerator` .\n\n## README.md Content Overview\nThe `README.md` file for `Microsoft.Extensions.AI.Abstractions` details the purpose of the library, which is to provide abstractions for generative AI components . It includes instructions on how to install the NuGet package `Microsoft.Extensions.AI.Abstractions` .\n\nThe document also provides usage examples for the `IChatClient` interface, which defines methods for interacting with AI services that offer \"chat\" capabilities . This includes examples for requesting both complete and streaming chat responses .\n\nFurthermore, the `README.md` explains the `IEmbeddingGenerator` interface, which is used for generating vector embeddings from input values . It demonstrates how to use `GenerateAsync` to create embeddings . The file also discusses how both `IChatClient` and `IEmbeddingGenerator` implementations can be layered to create pipelines of functionality, incorporating features like caching and telemetry .\n\nNotes:\nThe user's query specifically asked for the path to the `README.md` file for `Microsoft.Extensions.AI.Abstractions`. The provided codebase context, particularly the wiki page for \"AI Extensions Framework\", directly lists this file as a relevant source file . The content of the `README.md` file itself further confirms its relevance to the `Microsoft.Extensions.AI.Abstractions` library.\n\nWiki pages you might want to explore:\n- [AI Extensions Framework (dotnet/extensions)](/wiki/dotnet/extensions#3)\n- [Chat Completion (dotnet/extensions)](/wiki/dotnet/extensions#3.3)\n\nView this search on DeepWiki: https://deepwiki.com/search/what-is-the-path-to-the-readme_bb6bee43-3136-4b21-bc5d-02ca1611d857\n","server_label":"deepwiki"},{"id":"msg_68be450c39e8819eb9bf6fcb9fd16ecb03a2537be0e84a54","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The path to the `README.md` file for `Microsoft.Extensions.AI.Abstractions` in the `dotnet/extensions` repository is:\n\n```\nsrc/Libraries/Microsoft.Extensions.AI.Abstractions/README.md\n```\n\nThis file provides an overview of the library, installation instructions, and usage examples for its core components. If you have any more questions about it, feel free to ask!"}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"mcp","allowed_tools":null,"headers":null,"require_approval":"never","server_description":null,"server_label":"deepwiki","server_url":"https://mcp.deepwiki.com/<redacted>"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":1420,"input_tokens_details":{"cached_tokens":0},"output_tokens":149,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":1569},"user":null,"metadata":{}}} | |
| """; | |
| using VerbatimHttpHandler handler = new(Input, Output); | |
| using HttpClient httpClient = new(handler); | |
| using IChatClient client = CreateResponseClient(httpClient, "gpt-4o-mini"); | |
| ChatOptions chatOptions = new() | |
| { | |
| Tools = [new HostedMcpServerTool("deepwiki", new Uri("https://mcp.deepwiki.com/mcp")) | |
| { | |
| ApprovalMode = HostedMcpServerToolApprovalMode.NeverRequire, | |
| } | |
| ], | |
| }; | |
| var response = await client.GetStreamingResponseAsync("Tell me the path to the README.md file for Microsoft.Extensions.AI.Abstractions in the dotnet/extensions repository", chatOptions) | |
| .ToChatResponseAsync(); | |
| Assert.NotNull(response); | |
| Assert.Equal("resp_68be44fd7298819e82fd82c8516e970d03a2537be0e84a54", response.ResponseId); | |
| Assert.Equal("resp_68be44fd7298819e82fd82c8516e970d03a2537be0e84a54", response.ConversationId); | |
| Assert.Equal("gpt-4o-mini-2024-07-18", response.ModelId); | |
| Assert.Equal(DateTimeOffset.FromUnixTimeSeconds(1_757_299_965), response.CreatedAt); | |
| Assert.Equal(ChatFinishReason.Stop, response.FinishReason); | |
| var message = Assert.Single(response.Messages); | |
| Assert.Equal(ChatRole.Assistant, response.Messages[0].Role); | |
| Assert.StartsWith("The path to the `README.md` file", response.Messages[0].Text); | |
| Assert.Equal(6, message.Contents.Count); | |
| var firstCall = Assert.IsType<McpServerToolCallContent>(message.Contents[1]); | |
| Assert.Equal("mcp_68be4503d45c819e89cb574361c8eba003a2537be0e84a54", firstCall.CallId); | |
| Assert.Equal("deepwiki", firstCall.ServerName); | |
| Assert.Equal("read_wiki_structure", firstCall.ToolName); | |
| Assert.NotNull(firstCall.Arguments); | |
| Assert.Single(firstCall.Arguments); | |
| Assert.Equal("dotnet/extensions", ((JsonElement)firstCall.Arguments["repoName"]!).GetString()); | |
| var firstResult = Assert.IsType<McpServerToolResultContent>(message.Contents[2]); | |
| Assert.Equal("mcp_68be4503d45c819e89cb574361c8eba003a2537be0e84a54", firstResult.CallId); | |
| Assert.NotNull(firstResult.Output); | |
| Assert.StartsWith("Available pages for dotnet/extensions", Assert.IsType<TextContent>(Assert.Single(firstResult.Output)).Text); | |
| var secondCall = Assert.IsType<McpServerToolCallContent>(message.Contents[3]); | |
| Assert.Equal("mcp_68be4505f134819e806c002f27cce0c303a2537be0e84a54", secondCall.CallId); | |
| Assert.Equal("deepwiki", secondCall.ServerName); | |
| Assert.Equal("ask_question", secondCall.ToolName); | |
| Assert.NotNull(secondCall.Arguments); | |
| Assert.Equal("dotnet/extensions", ((JsonElement)secondCall.Arguments["repoName"]!).GetString()); | |
| Assert.Equal("What is the path to the README.md file for Microsoft.Extensions.AI.Abstractions?", ((JsonElement)secondCall.Arguments["question"]!).GetString()); | |
| var secondResult = Assert.IsType<McpServerToolResultContent>(message.Contents[4]); | |
| Assert.Equal("mcp_68be4505f134819e806c002f27cce0c303a2537be0e84a54", secondResult.CallId); | |
| Assert.NotNull(secondResult.Output); | |
| Assert.StartsWith("The path to the `README.md` file", Assert.IsType<TextContent>(Assert.Single(secondResult.Output)).Text); | |
| Assert.NotNull(response.Usage); | |
| Assert.Equal(1420, response.Usage.InputTokenCount); | |
| Assert.Equal(149, response.Usage.OutputTokenCount); | |
| Assert.Equal(1569, response.Usage.TotalTokenCount); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do, but will do once I know which direction we want to take the model.
Updated the XML documentation for the HTTP headers property to clarify that these headers are included when calling the remote MCP server.
…manFromBunnings/extensions into AddHeaderToMcpToolCreation
Introduces a Headers property to HostedMcpServerTool for specifying additional request headers. Updates OpenAIResponsesChatClient to pass these headers when creating MCP tools and adds unit tests to verify header roundtripping.
Microsoft Reviewers: Open in CodeFlow