labanalyze
v0.1.6
Published
MCP server for Azure Local lab DVMs and cluster nodes (PowerShell remoting via long-lived pwsh child)
Downloads
76
Maintainers
Readme
labanalyze — Azure Local Lab MCP Server
labanalyze is an MCP server that gives AI assistants (Copilot CLI, Claude, etc.) direct access to Azure Local lab Deployment Virtual Machines (DVMs) and cluster nodes via PowerShell remoting (WinRM). It exposes 15 tools covering connection management, cluster health, log retrieval, and arbitrary PowerShell execution — no credentials are ever stored on disk.
Architecture
┌──────────────┐ stdio ┌──────────────┐ WinRM/PS ┌─────────┐
│ Copilot CLI │ ◄────────────► │ labanalyze │ ──────────────► │ DVM │ (discovery only)
│ (or other │ (MCP proto) │ MCP server │ (Administrator)└─────────┘
│ MCP client) │ │ │ WinRM/PS ┌──────────────┐
└──────────────┘ │ (Node.js, │ ──────────────► │ Cluster Node │ (direct access)
│ spawns │ (domain\hci- │ ⭐ Seed Node │ (deploy logs)
│ pwsh) │ deploymentuser)│ │
└──────────────┘ └──────────────┘The server runs as a Node.js process started by the MCP client. It manages a single long-lived pwsh (or Windows powershell.exe) child process that holds persistent PSSession connections — one to the DVM (for AD/cluster discovery, opened as Administrator) and one per cluster node (opened lazily as <domain>\hcideploymentuser for direct queries). The WinRM client lives inside the pwsh child; Node communicates with it over stdin/stdout via a delimiter-framed protocol. Credentials live only in process memory.
Quick start (recommended)
Add the following snippet to your MCP configuration (e.g. ~/.copilot/mcp-config.json):
{
"mcpServers": {
"labanalyze": {
"type": "stdio",
"command": "npx",
"args": ["-y", "labanalyze"]
}
}
}Note: This works once the package is published to npm. For local development from the tarball, see Build from source.
Prerequisites
- Node.js 18+
- PowerShell 7+ (
pwsh) recommended; on Windows the server falls back topowershell.exeautomatically - Network access to the lab (VPN connected)
- DVM password (provided at runtime via
connect_to_dvm— never stored)
Optional environment variables
LABMCP_TIMEOUT_MS— per-command timeout in milliseconds for the internal pwsh host. Defaults to300000(5 minutes). Increase if your lab WinRM/AD queries are slow.
Lab conventions
- DVM is used for discovery only — finding the cluster name/IP and node names/IPs. Accessed as
Administrator(no domain prefix). - Cluster nodes are accessed directly via WinRM using
<domain>\hcideploymentuserwith the same password as the DVM. - Seed node is the first node alphabetically — Azure Local deployment logs are always at
C:\CloudDeployment\Logson this node. - Cluster objects in AD have names like
*-clors-cluster*.
Usage
1. Connect to DVM (just IP + password)
User: Connect to the DVM at 10.57.64.5 with password <secret>
→ connect_to_dvm(ip="10.57.64.5", password="...")Domain is auto-discovered. Username defaults to Administrator.
2. List cluster nodes
User: List the cluster nodes
→ list_cluster_nodes()Returns cluster name, node IPs, and identifies the seed node.
3. Check cluster status (defaults to seed node)
User: What's the cluster status?
→ get_cluster_status() ← automatically uses seed node4. Get ECE deployment logs (from seed node)
User: Show the last 100 lines of ECE logs
→ get_ece_logs(tail_lines=100) ← automatically targets seed node5. Run arbitrary commands
User: Run Get-Process on node hci-node-1
→ run_command_on_node(node="hci-node-1", command="Get-Process")6. Disconnect
User: Disconnect from the lab
→ disconnect_from_dvm()Available tools
| Category | Tool | Description |
|----------|------|-------------|
| Connection | connect_to_dvm | Connect to DVM (IP + password, username defaults to Administrator) |
| | disconnect_from_dvm | Disconnect and clear credentials from memory |
| | get_connection_status | Show connection state, cluster info, seed node |
| | list_cluster_nodes | Discover cluster + nodes from DVM (marks seed node) |
| Cluster | get_cluster_status | Overall cluster health (defaults to seed node) |
| | get_cluster_resources | List cluster resources and status |
| | get_cluster_network_info | Cluster networks and interfaces |
| | get_node_info | Node details — OS, uptime, hardware |
| | get_storage_info | Storage pools and virtual disks |
| Logs | get_event_logs | Windows Event Log entries (defaults to seed node) |
| | get_ece_logs | ECE deployment logs from C:\CloudDeployment\Logs |
| | get_deployment_status | Azure Local deployment action plan status |
| | get_file_content | Read any file from a cluster node |
| Commands | run_command_on_dvm | Execute arbitrary PowerShell on DVM |
| | run_command_on_node | Execute arbitrary PowerShell on cluster node (direct) |
Security
- No hardcoded credentials — password passed as tool parameter at runtime
- In-memory only — credentials and sessions exist only in process memory
- No config file secrets — MCP config contains only the command to start the server
- Lab/test use only — the general-purpose command tools execute arbitrary PowerShell by design
Troubleshooting
"Not connected to a DVM"
Call connect_to_dvm first with the DVM IP and password.
WinRM connection fails
- Ensure you are connected to the lab VPN
- Verify the DVM IP is correct and reachable (
ping <ip>) - Check that WinRM is enabled on the DVM (port 5985 for HTTP)
"No seed node discovered"
Call list_cluster_nodes after connecting to the DVM. This discovers the cluster and its nodes.
"Access denied" on cluster nodes
- Cluster nodes are accessed directly as
<domain>\hcideploymentuser - Domain is auto-discovered from the DVM — ensure the DVM is domain-joined
- The password is the same as the DVM password
"PowerShell host not found"
Install PowerShell 7 (pwsh) from https://github.com/PowerShell/PowerShell/releases, or run on Windows where powershell.exe is available (the server falls back automatically).
Build from source
cd labanalyzemcp
npm install
npm run build
node dist/index.jsTests run with npm test (53 tests across formatting, state, powershellHost, and connectionManager).
Project structure
labanalyzemcp/
├── package.json # name=labanalyze, bin → dist/index.js
├── tsconfig.json
├── README.md
├── .gitignore
├── .npmignore
├── src/
│ ├── index.ts # bin entry: #!/usr/bin/env node + main()
│ ├── server.ts # MCP server bootstrap, registers tools, stdio transport
│ ├── state.ts # ServerState, DvmConnectionInfo, CaseInsensitiveMap
│ ├── connection/
│ │ ├── powershellHost.ts # long-lived pwsh child + delimiter protocol
│ │ └── connectionManager.ts # DVM/node PSSession lifecycle, AD discovery
│ ├── helpers/
│ │ └── formatting.ts # markdown output helpers
│ └── tools/
│ ├── _helpers.ts # requireConnected / resolveNode (shared)
│ ├── connectionTools.ts # connect/disconnect/status/list_cluster_nodes (4)
│ ├── clusterTools.ts # cluster status/resources/network/node/storage (5)
│ ├── logTools.ts # event/ECE/deployment-status/file-content (4)
│ └── commandTools.ts # arbitrary command on DVM / node (2)
└── tests/
├── state.test.ts
├── formatting.test.ts
├── powershellHost.test.ts
├── connectionManager.test.ts
└── fixtures/
└── mock-pwsh.mjs # Node-based pwsh stand-in for unit tests