Installation and Setup
Prerequisites
- PowerShell 7.0+
# Check PowerShell version $PSVersionTable.PSVersion # Install PowerShell 7 (if necessary) # Download from https://github.com/PowerShell/PowerShell - Access Rights
- Administrator rights are required for ports < 1024.
- Permissions to execute PowerShell scripts.
- Execution Policy Configuration
# Check the current policy Get-ExecutionPolicy # Set the policy to allow script execution Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Initial Setup
- Navigate to the servers directory
# Navigate to the module's root cd C:\powershell\modules\mcp-powershell-server # Navigate to the servers cd src\servers - Check the files
powershell # Ensure all necessary files are present Get-ChildItem *.ps1 | Select-Object Name
Choosing an Operating Mode: HTTP vs. STDIO
Before diving into the details, it’s important to understand which of the two server operating modes is right for you. The choice depends on how and from where you plan to send commands.
- HTTP mode (
mcp-powershell-http.ps1): Works like a web service. It accepts commands over the network (HTTP) and can be accessed from other computers or web applications. This is a versatile method for network integrations. - STDIO mode (
mcp-powershell-stdio.ps1): Works like a console application controlled by another process. It receives commands through the standard input stream and returns results through the standard output stream. This method is ideal for local integration, for example, withgemini-cli.
When to use HTTP mode?
Choose HTTP if you require network accessibility:
- Remote Management: The client application (e.g., a Python script) is on a different computer.
- Web Integration: You want to call PowerShell from a web panel by sending requests with JavaScript.
- Microservice Architecture: Different services on your network need to exchange commands.
- Simple Testing: You want to send commands using tools like
curlor Postman.
Key Scenario: The client and server are on a network and communicate using standard web protocols.
When to use STDIO mode?
Choose STDIO for local and more secure integration:
- Integration with Gemini CLI: This is the primary and most common scenario.
gemini-clistartsmcp-powershell-stdio.ps1as a child process and communicates with it directly. - Local Wrapper Scripts: Your application in another language (e.g., Node.js) starts the PowerShell server as a child process and manages it.
- Enhanced Security: This mode does not open any network ports, which eliminates an entire class of network threats.
Key Scenario: The client and server are running on the same machine, and the client manages the server’s lifecycle itself.
Now that you have decided on the mode, proceed to the corresponding section below for detailed instructions on launching and using it.
STDIO Mode
The STDIO mode is designed for integration with MCP clients like gemini-cli.
Starting the STDIO Server
# Start the server directly (from the src/servers folder)
.\mcp-powershell-stdio.ps1
# Or from the project root
.\src\servers\mcp-powershell-stdio.ps1
STDIO Mode Features
- Protocol: JSON-RPC over standard input/output streams.
- Logging: To the file
%TEMP%\mcp-powershell-server.log. - Encoding: UTF-8 for correct handling of various character sets.
- Compatibility: Works with any MCP client.
Testing STDIO Mode
# Start the test server for verification (from the src/servers folder)
.\test-mcp.ps1
# Or from the project root
.\src\servers\test-mcp.ps1
Example of manual testing:
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05"}}
{"jsonrpc":"2.0","id":2,"method":"tools/list"}
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"run-script","arguments":{"script":"Get-Date"}}}
HTTP Mode
The HTTP mode is designed for web integrations and REST APIs.
Starting the HTTP Server
# Basic start (localhost:8090) from the src/servers folder
.\mcp-powershell-http.ps1
# Start on a different port
.\mcp-powershell-http.ps1 -Port 9090
# Start on all interfaces
.\mcp-powershell-http.ps1 -ServerHost "0.0.0.0" -Port 8080
# Start with a configuration file
.\mcp-powershell-http.ps1 -ConfigFile "config.json"
# Or from the project root
.\src\servers\mcp-powershell-http.ps1 -Port 8090
HTTP API Endpoints
All requests are sent as POST to the server’s root URL.
URL: http://localhost:8090/
Method: POST
Content-Type: application/json
Testing HTTP Mode
# Test using Invoke-RestMethod
$body = @{
jsonrpc = "2.0"
id = 1
method = "tools/list"
} | ConvertTo-Json
Invoke-RestMethod -Uri "http://localhost:8090/" -Method POST -Body $body -ContentType "application/json"```
bash
Test using curl
curl -X POST http://localhost:8090/ \
-H “Content-Type: application/json” \
-d ‘{“jsonrpc”:”2.0″,”id”:1,”method”:”tools/list”}’
## Integration with Gemini CLI
### Automatic Setup
powershell
Start with automatic Gemini CLI setup
.\start-mcp-with-gemini.ps1 -ApiKey “your-gemini-api-key”
With additional parameters
.\start-mcp-with-gemini.ps1 -ApiKey “your-key” -ServerPort 9090 -Wait 15
### Manual Setup
1. **Create MCP Configuration**
```powershell
# Create the configuration directory
$configDir = "$env:USERPROFILE\.config\gemini"
New-Item -Path $configDir -ItemType Directory -Force
# Create the MCP configuration file
$config = @{
mcpServers = @{
powershell = @{
command = "pwsh"
args = @("-File", "C:\path\to\mcp-powershell-stdio.ps1")
env = @{}
}
}
} | ConvertTo-Json -Depth 5
$config | Set-Content "$configDir\mcp_servers.json" -Encoding UTF8
```
2. **Usage with gemini-cli**
```bash
# Interactive mode
gemini --mcp-config "path/to/mcp_servers.json" -i
# Single request
gemini --mcp-config "path/to/mcp_servers.json" -m gemini-2.5-pro -p "Execute the command Get-Process | Select-Object -First 5"
```
## Usage Examples
### Basic PowerShell Commands
json
{
“jsonrpc”: “2.0”,
“id”: 1,
“method”: “tools/call”,
“params”: {
“name”: “run-script”,
“arguments”: {
“script”: “Get-ComputerInfo | Select-Object WindowsProductName, TotalPhysicalMemory”
}
}
}
### Working with Files
json
{
“jsonrpc”: “2.0”,
“id”: 2,
“method”: “tools/call”,
“params”: {
“name”: “run-script”,
“arguments”: {
“script”: “Get-ChildItem C:\ -Directory | Select-Object Name, CreationTime | Format-Table”,
“workingDirectory”: “C:\”,
“timeoutSeconds”: 30
}
}
}
### Scripts with Parameters
json
{
“jsonrpc”: “2.0”,
“id”: 3,
“method”: “tools/call”,
“params”: {
“name”: “run-script”,
“arguments”: {
“script”: “param($ProcessName) Get-Process -Name $ProcessName -ErrorAction SilentlyContinue”,
“parameters”: {
“ProcessName”: “notepad”
}
}
}
}
### System Monitoring
json
{
“jsonrpc”: “2.0”,
“id”: 4,
“method”: “tools/call”,
“params”: {
“name”: “run-script”,
“arguments”: {
“script”: “$cpu = Get-Counter ‘\Processor(_Total)\% Processor Time’ | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue; $memory = Get-Counter ‘\Memory\Available MBytes’ | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue; Write-Output \”CPU: $([math]::Round($cpu, 2))%, Available Memory: $memory MB\””
}
}
}
## Configuration
### config.json File
json
{
“Port”: 8090,
“Host”: “localhost”,
“MaxConcurrentRequests”: 10,
“TimeoutSeconds”: 300,
“LogLevel”: “INFO”,
“AllowedPaths”: [
“C:\Scripts\”,
“C:\Tools\”,
“C:\Temp\”
],
“Security”: {
“EnableScriptValidation”: true,
“BlockDangerousCommands”: true,
“RestrictedCommands”: [
“Remove-Item”,
“Format-Volume”,
“Stop-Computer”,
“Restart-Computer”,
“New-ItemProperty -Path ‘HKLM:‘”, “Remove-ItemProperty -Path ‘HKLM:‘”
],
“AllowedModules”: [
“Microsoft.PowerShell.*”,
“PackageManagement”,
“PowerShellGet”
]
},
“Logging”: {
“LogFile”: “%TEMP%\mcp-powershell-server.log”,
“MaxLogSize”: “10MB”,
“LogRotation”: true
}
}
### Environment Variables
powershell
Configuration via environment variables
$env:MCP_PS_PORT = “8090”
$env:MCP_PS_HOST = “localhost”
$env:MCP_PS_TIMEOUT = “300”
$env:MCP_PS_LOG_LEVEL = “INFO”
## Security
### Security Recommendations
1. **Command Restriction**
```json
"RestrictedCommands": [
"Remove-Item",
"Format-Volume",
"Stop-Computer",
"Restart-Computer",
"Invoke-Expression",
"iex",
"& *"
]
```
2. **Path Restriction**
```json
"AllowedPaths": [
"C:\\Scripts\\",
"C:\\Tools\\",
"C:\\Temp\\"
]
```
3. **Network Restrictions**
```powershell
# Restrict access to localhost only
.\start-mcp-server.ps1 -ServerHost "127.0.0.1"
```
4. **Timeouts**
```json
"TimeoutSeconds": 60 // Limit execution time
```
### Auditing and Monitoring
powershell
Monitor logs in real-time
Get-Content “$env:TEMP\mcp-powershell-server.log” -Wait -Tail 10
Analyze executed commands
Select-String -Path “$env:TEMP\mcp-powershell-server.log” -Pattern “Executing PowerShell script”
## Extending Functionality
### Adding New MCP Tools
1. **Tool Structure**
```powershell
# In the Invoke-MCPMethod function, add a new case
"my-custom-tool" {
# Validate parameters
if (-not $arguments.ContainsKey("required_param")) {
return New-MCPResponse -Id $Id -Error @{
code = -32602
message = "Missing required parameter 'required_param'"
}
}
# Execution logic
$result = Invoke-MyCustomFunction -Param $arguments.required_param
# Return the result
return New-MCPResponse -Id $Id -Result @{
content = @(
@{
type = "text"
text = "Result: $result"
}
)
}
}
```
2. **Registration in tools/list**
```powershell
# Add the tool description to the tools/list method
@{
name = "my-custom-tool"
description = "Description of my custom tool"
inputSchema = @{
type = "object"
properties = @{
required_param = @{
type = "string"
description = "A required parameter"
}
}
required = @("required_param")
}
}
```
### Example of a Custom Tool
powershell
Adding a tool to work with the registry
“registry-query” {
if (-not $arguments.ContainsKey(“path”)) {
return New-MCPResponse -Id $Id -Error @{
code = -32602
message = “Missing required parameter ‘path'”
}
}
try {
$regPath = $arguments.path
$regKey = Get-ItemProperty -Path $regPath -ErrorAction Stop
$result = $regKey | Format-List | Out-String
return New-MCPResponse -Id $Id -Result @{
content = @(
@{
type = "text"
text = "Registry values at ${regPath}:`n$result"
}
)
}
}
catch {
return New-MCPResponse -Id $Id -Error @{
code = -32603
message = "Registry query failed: $($_.Exception.Message)"
}
}
}
## Troubleshooting
### Diagnostic Commands
powershell
Check PowerShell version
$PSVersionTable.PSVersion
Check port availability
Test-NetConnection -ComputerName localhost -Port 8090
Check logs
Get-Content “$env:TEMP\mcp-powershell-server.log” -Tail 50
Check PowerShell processes
Get-Process -Name pwsh*
### Common Problems
1. **"Port is already in use"**
```powershell
# Find the process using the port
Get-NetTCPConnection -LocalPort 8090 | Get-Process
# Or use a different port
.\start-mcp-server.ps1 -Port 9090
```
2. **"Access denied"**
```powershell
# Run with administrator rights for ports < 1024
Start-Process pwsh -Verb RunAs -ArgumentList "-File", "start-mcp-server.ps1"
```
3. **"Encoding issues"**
```powershell
# Check console encoding
[Console]::OutputEncoding
[Console]::InputEncoding
# Force UTF-8
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
[Console]::InputEncoding = [System.Text.Encoding]::UTF8
```
4. **"Script does not execute"**
```powershell
# Check the execution policy
Get-ExecutionPolicy -List
# Temporarily bypass
powershell.exe -ExecutionPolicy Bypass -File "script.ps1"
```
### Debugging
powershell
Enable detailed logging
$DebugPreference = “Continue”
Trace script execution
Set-PSDebug -Trace 1
Turn off tracing
Set-PSDebug -Off
## API Reference
### MCP Methods
#### initialize
Initializes the MCP server.
**Request:**
json
{
“jsonrpc”: “2.0”,
“id”: 1,
“method”: “initialize”,
“params”: {
“protocolVersion”: “2024-11-05”
}
}
**Response:**
json
{
“jsonrpc”: “2.0”,
“id”: 1,
“result”: {
“protocolVersion”: “2024-11-05”,
“capabilities”: {
“tools”: {
“listChanged”: true
}
},
“serverInfo”: {
“name”: “PowerShell Script Runner”,
“version”: “1.0.0”,
“description”: “Executes PowerShell scripts via MCP”
}
}
}
#### tools/list
Gets the list of available tools.
**Request:**
json
{
“jsonrpc”: “2.0”,
“id”: 2,
“method”: “tools/list”
}
**Response:**
json
{
“jsonrpc”: “2.0”,
“id”: 2,
“result”: {
“tools”: [
{
“name”: “run-script”,
“description”: “Executes a PowerShell script with specified parameters”,
“inputSchema”: {
“type”: “object”,
“properties”: {
“script”: {
“type”: “string”,
“description”: “PowerShell code to execute”
},
“parameters”: {
“type”: “object”,
“description”: “Parameters for the script (optional)”
},
“workingDirectory”: {
“type”: “string”,
“description”: “Working directory for execution”
},
“timeoutSeconds”: {
“type”: “integer”,
“description”: “Execution timeout in seconds”,
“default”: 300,
“minimum”: 1,
“maximum”: 3600
}
},
“required”: [“script”]
}
}
]
}
}“`
tools/call
Executes a tool.
Request:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "run-script",
"arguments": {
"script": "Get-Date",
"timeoutSeconds": 30
}
}
}
Response:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "Command output:\n```\nTuesday, September 25, 2025 2:30:45 PM\n```"
}
],
"isError": false,
"_meta": {
"executionTime": "2025-09-25 14:30:45",
"success": true,
"errorCount": 0,
"warningCount": 0
}
}
}
Error Codes
| Code | Description |
|---|---|
| -32700 | Parse error |
| -32600 | Invalid Request |
| -32601 | Method not found |
| -32602 | Invalid params |
| -32603 | Internal error |
Logging Levels
| Level | Description |
|---|---|
| DEBUG | Detailed debugging information |
| INFO | General operational information |
| WARNING | Warnings about potential issues |
| ERROR | Errors that require attention |
Conclusion
The MCP PowerShell Server provides a powerful and secure way to integrate PowerShell with AI assistants and other applications through the standardized MCP protocol. Follow the security recommendations and use logging to monitor the server’s operation.