Skip to content

Make AIShell an MCP client to expose MCP tools to its agents #392

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

Merged
merged 6 commits into from
Jun 24, 2025

Conversation

daxian-dbw
Copy link
Member

@daxian-dbw daxian-dbw commented Jun 23, 2025

PR Summary

This PR enables MCP client support in AIShell and also updates the openai-gpt agent to consume tools.
The detailed changes are:

  1. Updated the AIShell.Abstraction.IShell interface to include the following 2 new methods

    • Task<List<AIFunction>> GetAIFunctions() -- get available tools for AI model to use.
    • Task<FunctionResultContent> CallAIFunction(...) -- allow an agent to call a tool.

    We use types like AIFunction, FunctionCallContent, and FunctionResultContent from the NuGet package Microsoft.Extensions.AI.Abstractions as the general representation of a "tool", a "tool call request", and a "tool call result". This allows an agent to leverage the framework library Microsoft.Extensions.AI easily. And for agents that doesn't use that library, they can still use the exposed tools by calling CallAIFunction(...) directly.

  2. Supported the $HOME/mcp.json file for configuring local or remote MCP servers. Here is an example of the configuration format:

    {
      "servers": {
        "Everything": {
          "type": "stdio",
          "command": "npx",
          "args": ["-y", "@modelcontextprotocol/server-everything"]
        },
        "GitHub": {
          "type": "http",
          "url": "https://api.githubcopilot.com/mcp/",
          "headers": {
            "Authorization": "Bearer <github-pat>"
          }
        }
      }
    }

    The configuration format is based on that of the VS Code MCP, but not 100% the same -- no envFile key and no inputs support.

    • Server configuration for stdio type connection:

      Field Description Examples
      type Server connection type. "stdio"
      command Command to start the server executable. The command needs to be available on your system path or full path. If you use docker, don't use the detach option. "npx", "node", "python", "docker"
      args Array of arguments passed to the command. ["server.py", "--port", "3000"]
      env Environment variables for the server. {"API_KEY": "<key>"}
    • Server configuration for see or http type connections:

      Field Description Examples
      type Server connection type. VS Code first tries the streamable HTTP transport and falls back to SSE if HTTP is not supported. "sse", "http"
      url URL of the server. "http://localhost:3000"
      headers HTTP headers for the server. {"API_KEY": "${input:api-key}"}
  3. The new MCP components are:

    • McpConfig: it deserializes the mcp.json configuration utilizing source generation.
    • McpManager: it manages all configured MCP servers and expose methods to get all available tools, make a tool call, and stop/start a given server.
    • McpServer: it represents an MCP server. It connects to the server in asynchronous way, reports the server status, and return the available tools from the server.
    • McpTool: it represents an MCP tool. It implements AIFunction to hook up to the Microsoft.Extensions.AI framework and also make sure calls to MCP tools all go through the "tool call request" prompt for the user to approve.
    • /mcp command: it shows a table of all servers and their available tools, including the status of the server, and error message if a server fails.

    The MCP components depend on the official .NET MCP SDK ModelContextProtocol.Core to handle MCP servers under the hoods. Internally, our McpServer and McpTool wrap IMcpClient and McpClientTool respectively.

  4. The openai-gpt agent was refactored to use the framework library Microsoft.Extensions.AI.OpenAI. This makes it extremely intuitive to consume exposed AIFunction instances from AIShell. The estimated token count tracking code was removed from the agent, because after switching to Microsoft.Extensions.AI.OpenAI, the framework library will make the subsequent round of chat requests automatically after a tool call result is back, and this will certainly not go through our existing token count tracking code. It was an estimate anyways, so if needed, we can use the token usage information from the response to help shrink the chat history.

  5. Two new methods were added to Host

    • RenderToolCallRequest -- used by McpTool to display a tool call request.
    • RenderMcpServersAndTools -- used by /mcp command to display all servers and tools.
  6. Updated FancyStreamRender to work properly when the host writes output while a FancyStreamRender is still active. It's very possible that an AI model could return some text before making a tool call. In such a case, the tool call request will be rendered while a FancyStreamRender instance is still active. A FancyStreamRender instance needs to track the initial cursor position to refresh the whole output when there are new text chunks coming in, so we need to make it aware of the tool call request output, so that it can adjust its cursor position and refresh its state correctly.

  7. Updated LoadContext.cs to make sure for System.CommandLine.dll and Microsoft.Extensions.AI.Abstractions.dll, all agents share the same assembly loaded in the default load context. This is because the interfaces exposed in AIShell.Abstraction depend on types from those 2 assemblies, so if an agent loads its own version of those assemblies into its custom load context, the contracts will break.

  8. Simplified the package references.

Follow-up work

  • Use the token usage information to shrink chat history
  • GetAIFunctions() should only update the returned list when tools are changed.
  • Built-in tools support -- run command and return results
  • /mcp command should support the following sub commands:
    • /mcp stop <server> -- stop a server.
    • /mcp start <server> -- start a server.
    • /mcp refresh -- refresh all servers if MCP configuration changed.

@daxian-dbw
Copy link
Member Author

GIFs for demonstration:

stdio connection server

mcp

http connection server

mcp2

{
this.SetHandler(ShowMCPData);

//var start = new Command("start", "Start an MCP server.");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should these commends be removed?

Copy link
Member Author

Choose a reason for hiding this comment

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

The code was left intentionally. The code was for adding /mcp start and /mcp stop, which are follow-up work listed in the PR description.

Copy link
Collaborator

@StevenBucher98 StevenBucher98 left a comment

Choose a reason for hiding this comment

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

can't give advise on the code but gave it a read through and only one comment about some potentially unnecessary comments 😄

@StevenBucher98 StevenBucher98 self-requested a review June 24, 2025 06:26
@daxian-dbw daxian-dbw merged commit f932833 into PowerShell:main Jun 24, 2025
4 checks passed
@daxian-dbw daxian-dbw deleted the mcp branch June 24, 2025 18:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants