Skip to content
💻 🧠 Code 1001 > ⚡ PowerShell Philosophy > MCP PowerShell Server > Detailed Guide to Using MCP PowerShell Server. (how-to-use.md)

Detailed Guide to Using MCP PowerShell Server. (how-to-use.md)

Installation and Setup

Prerequisites

  1. PowerShell 7.0+ # Check PowerShell version $PSVersionTable.PSVersion # Install PowerShell 7 (if necessary) # Download from https://github.com/PowerShell/PowerShell
  2. Access Rights
    • Administrator rights are required for ports < 1024.
    • Permissions to execute PowerShell scripts.
  3. Execution Policy Configuration # Check the current policy Get-ExecutionPolicy # Set the policy to allow script execution Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

Initial Setup

  1. 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
  2. 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, with gemini-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 curl or 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-cli starts mcp-powershell-stdio.ps1 as 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

CodeDescription
-32700Parse error
-32600Invalid Request
-32601Method not found
-32602Invalid params
-32603Internal error

Logging Levels

LevelDescription
DEBUGDetailed debugging information
INFOGeneral operational information
WARNINGWarnings about potential issues
ERRORErrors 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.

Leave a Reply

Your email address will not be published. Required fields are marked *