Ironman Software Forums
Continue the conversion on the Ironman Software forums. Chat with over 1000 users about PowerShell, PowerShell Universal, and PowerShell Pro Tools.
GitHub Copilot is an AI helper that can run in an editor like Visual Studio Code. It provides code completion as well as chat capabilities to analyze and generate code. GitHub Copilot also supports Agent mode, which allows for communication with additional tools to perform actions on your behalf. In this post, we’ll look at how to expose PowerShell Universal as a tool for GitHub Copilot to perform actions.
The Model Context Protocol, or MCP, is a standard for exposing tools to AI agents like Copilot. Similar to standards like OpenAPI or the Language Service Protocol in Visual Studio Code, this standard uses JSON objects and endpoints to provide details about a tool in an AI agent. By providing expected inputs and outputs and descriptions of the tool, AI models can determine whether or not to use a tool and how to use it.
MCP servers are starting to become common place for software stacks to provide AI with easy ways to interact with them. While PowerShell Universal does not (yet) support MCP directly, we can use the existing OpenAPI implementation and an open source project to host an MCP proxy server. This server translates OpenAPI spec to MCP spec and then forwards MCP requests to the accompanying APIs.
First, we need to define an API and API documentation that can be consumed by the MCP proxy. In PowerShell Universal, we’ll create a couple of APIs to return and start processes. These APIs are simple in implementation but use a couple of PSU techniques to provide information to the OpenAPI documentation generation engine within the platform. We provide some information about the output types of the endpoints as well as a description for each one that the LLM will be able to parse.
New-PSUEndpoint -Url "/process" -Description "Gets a list of processes running on the PowerShell Universal server." -Method @('GET') -Endpoint {
<#
.OUTPUTS
200:
Description: An array of process information.
Content:
application/json: ProcessInfo[]
400:
Description: Invalid input
#>
param()
Get-Process | Select-Object Name, Id
} -Documentation "Agent Docs"
New-PSUEndpoint -Url "/process/:name" -Description "Starts a process on the PowerShell Universal server." -Endpoint {
param(
[Parameter(Mandatory, HelpMessage = "The file name of the process to start.")]
$Name
)
Start-Process $Name -PassThru | Select-Object Name, Id
} -Documentation "Agent Docs"
We also need to create an API Document for these endpoints that also defines a class with the structure of our output.
New-PSUEndpointDocumentation -Name "Agent Docs" -Definition {
[Documentation()]
class ProcessInfo {
[string]$Name
[string]$Id
}
} -Url "/agent-docs" -ContactName "Adam Driscoll" -Version "1.0.0" -LicenseName "MIT"
After defining the endpoints and document, we can view the documentation at http://localhost:5000/swagger/index.html?urls.primaryName=Agent%20Docs
.
Additionally, a JSON OpenAPI spec is created.
{
"openapi": "3.0.4",
"info": {
"title": "Agent Docs",
"contact": {
"name": "Adam Driscoll"
},
"license": {
"name": "MIT"
},
"version": "1.0.0"
},
"servers": [
{
"url": "http://localhost:5000"
}
],
"paths": {
"/process": {
"get": {
"summary": "",
"description": "Gets a list of processes running on the PowerShell Universal server.",
"requestBody": {
"description": "Error processing input types. Cannot perform runtime binding on a null reference",
"content": { }
},
"responses": {
"200": {
"description": "An array of process information.",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ProcessInfo"
}
}
}
}
},
"400": {
"description": "Invalid input"
}
}
}
},
"/process/{name}": { }
},
"components": {
"schemas": {
"ProcessInfo": {
"type": "object",
"properties": {
"name": {
"type": "string",
"nullable": true
},
"id": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
}
},
"securitySchemes": {
"Bearer": {
"type": "http",
"description": "App Token for accessing the API",
"scheme": "bearer",
"bearerFormat": "JWT"
}
}
},
"security": [
{
"Bearer": [ ]
}
],
"tags": [
{
"name": "default"
}
]
}
LLMs are very good at parsing schemas like this but we can make it even easier by putting an MCP proxy in front of the API documentation so that tools like GitHub Copilot can discover tools easily.
We will use the openapi-mcp-generator
NPM package to generate a MCP proxy server for our API. This requires NodeJS to be installed. To install the package, use the following command.
npm install -g openapi-mcp-generator
Next, we need to generate the MCP server proxy based on our API. This generates Typescript files that can then be transpiled and executed in node. The following reads the OpenAPI documentation and then compiles and starts the MCP server.
openapi-mcp-generator --input http://localhost:5000/agent-docs --output .\mcp --base-url http://localhost:5000 --transport web
cd .\mcp
npm i
npm run build
npm run start:web
Now that we have our PowerShell Universal APIs defined and our MCP proxy server running, we can configure GitHub Copilot. You will need the extension installed before continuing. In VS Code, press Ctrl+Shift+P
and search for MCP: Add Server...
.
Select the HTTP option and enter the URL to the MCP server. You will need the /sse
route. The full URL, by default, is http://localhost:3000/sse
. Name the server whatever you would like.
The resulting settings.json
contents will look something like this.
"mcp": {
"servers": {
"PSU": {
"url": "http://localhost:3000/sse"
}
}
}
With everything configured, we can now use our AI agent tool. Click the Copilot icon and open a new chat.
Within the chat window, you can prompt Copilot with a question such as Can you please list all the processes as an array of strings in a new PowerShell scripts?
Copilot will call our PSU tool and retrieve the list of processes and then generate a PowerShell script in VS Code.
Because we also have an endpoint to start processes, you can also prompt Copilot to do so with a statement like: Can you start a new process in PowerShell Universal named calc?
. This will cause the calc.exe
process to start because the PSU endpoint will be called with that argument.
In the post, we looked at how to create an MCP proxy server for PowerShell Universal in order to call it from GitHub Copilot. Because MCP is a standard protocol, you could also integrate the PSU server into other LLM agents.
Continue the conversion on the Ironman Software forums. Chat with over 1000 users about PowerShell, PowerShell Universal, and PowerShell Pro Tools.
Receive once-a-month updates about Ironman Software. You'll learn about our product updates and blogs related to PowerShell.