Content
# MCP Azure Cloud Shell
A Model Context Protocol (MCP) server that enables developers to execute Azure CLI commands and bash scripts in your Azure Cloud Shell environment.
## Quick Links
- [Features](#features) - Available tools and capabilities
- [Installation](#installation) - Get started in 3 steps
- [Usage](#usage) - Stdio vs HTTP transport setup
- [Next Steps](#next-steps) - Production deployment and development recommendations
- [How It Works](#architecture-details) - Technical deep dive with code references
- [Cost & Billing](#cost--billing) - Pricing breakdown and what resources are created
- [Troubleshooting](#troubleshooting) - Common issues and solutions
## Features
This MCP server provides programmatic access to Azure Cloud Shell through the following tools:
### Tools
1. **azure_cli** - Execute Azure CLI commands
- Run any `az` command in your Azure environment
- Example: `az account show`, `az vm list`, `az group list`
2. **azure_shell** - Execute bash shell commands
- Run general bash commands, git operations, or scripts
- Example: `ls -la`, `git status`, `pwd`, `cat file.txt`
3. **azure_shell_reconnect** - Force reconnection to Cloud Shell
- Use if the connection seems stale or commands are timing out
## How It Works
The server connects to Azure Cloud Shell using the official Azure REST API:
1. **Authentication** - Uses `DefaultAzureCredential` from `@azure/identity`
2. **Console Provisioning** - Provisions a Cloud Shell console via Azure Management API
3. **Terminal Creation** - Creates a bash terminal session
4. **WebSocket Connection** - Establishes bidirectional communication for command execution
## Prerequisites
Before using this server, you need:
1. **Azure Account** with an active subscription
2. **Azure CLI** installed locally (for authentication setup)
3. **Node.js** 18+ installed
4. **Azure Cloud Shell** resource provider registered in your subscription
### Register Azure Cloud Shell Resource Provider
If this is your first time using Cloud Shell, register the resource provider:
```bash
az provider register --namespace Microsoft.CloudShell
```
### Authentication Setup
The server uses `DefaultAzureCredential`, which supports multiple authentication methods. Choose one:
#### Option 1: Azure CLI (Recommended for Development)
```bash
az login
```
This is the simplest method for local development. The server will automatically use your Azure CLI credentials.
#### Option 2: Environment Variables (Service Principal)
```bash
export AZURE_TENANT_ID="your-tenant-id"
export AZURE_CLIENT_ID="your-client-id"
export AZURE_CLIENT_SECRET="your-client-secret"
```
#### Option 3: Managed Identity (For Azure-hosted applications)
If running on Azure VMs, App Service, or Functions, managed identity will be used automatically.
## Installation
1. Navigate to the project directory:
```bash
cd mcp-azure-cloudshell
```
2. Install dependencies:
```bash
npm install
```
3. Build the server:
```bash
npm run build
```
## Usage
This server supports two transport mechanisms. Choose the one that best fits your use case:
### Transport Options
| Transport | Best For | Pros | Cons |
|-----------|----------|------|------|
| **Stdio** | Local development, Claude Code desktop | Low latency, simple setup | Single user, local only |
| **HTTP** | Remote access, web apps, multiple users | Network accessible, scalable | Requires server management |
### Option 1: Stdio Transport (Recommended for Claude Code)
**Start the server:**
```bash
npm start
```
**Add to Claude Code using CLI:**
```bash
claude mcp add --transport stdio azure-cloudshell -- node /path/to/mcp-azure-cloudshell/dist/index.js
```
**Or manually configure** in `.mcp.json` (local) or `~/.config/claude/mcp.json` (global):
```json
{
"mcpServers": {
"azure-cloudshell": {
"command": "node",
"args": ["/absolute/path/to/mcp-azure-cloudshell/dist/index.js"],
"transport": "stdio"
}
}
}
```
### Option 2: HTTP Transport (For Remote/Web Access)
**Start the HTTP server:**
```bash
npm run start:http
```
The server starts on `http://localhost:3000/mcp` by default. Use `PORT=8080` to customize.
**Configure in Claude Code** (`.mcp.json`):
```json
{
"mcpServers": {
"azure-cloudshell-http": {
"url": "http://localhost:3000/mcp",
"transport": "http"
}
}
}
```
**Test with curl:**
```bash
# Initialize session
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc": "2.0", "method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "curl-client", "version": "1.0.0"}}, "id": 1}'
# List tools (use session ID from response)
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Mcp-Session-Id: YOUR-SESSION-ID" \
-d '{"jsonrpc": "2.0", "method": "tools/list", "params": {}, "id": 2}'
```
For detailed HTTP setup, deployment options, and troubleshooting, see [HTTP-TRANSPORT.md](./HTTP-TRANSPORT.md).
### Example Queries
After adding the server, you can ask Claude Code questions like:
- "What Azure subscription am I using?"
- "List all my resource groups"
- "Show me all VMs in the eastus region"
- "Create a new resource group called test-rg in westus2"
- "What's my current directory in Cloud Shell?"
- "Clone the repository https://github.com/example/repo into Cloud Shell"
## Next Steps
Now that you have the server set up, here are some recommended next steps:
### For Production Deployments (HTTP Transport)
1. **Add Authentication**: Implement Bearer token or API key validation
```typescript
app.use((req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader || !validateToken(authHeader)) {
return res.status(401).json({ error: 'Unauthorized' });
}
next();
});
```
2. **Enable HTTPS**: Use valid TLS certificates for secure communication
- Deploy behind a reverse proxy (nginx, Caddy)
- Use Let's Encrypt for free certificates
- Configure HTTPS redirect
3. **Deploy to Cloud**:
- **Azure App Service**: `az webapp up --name my-mcp-server --resource-group my-rg`
- **AWS Lambda**: Use AWS Lambda Web Adapter with the HTTP server
- **Docker**: Build and deploy containerized version (see [HTTP-TRANSPORT.md](./HTTP-TRANSPORT.md))
4. **Add Monitoring**: Track usage, errors, and performance
- Application Insights (Azure)
- CloudWatch (AWS)
- Custom logging and metrics
5. **Implement Rate Limiting**: Prevent abuse with request throttling
```bash
npm install express-rate-limit
```
### For Development
1. **Test with Azure Credentials**: Ensure `az login` is configured and test actual command execution
2. **Add Custom Tools**: Extend the server with additional Azure-specific tools
3. **Improve Output Parsing**: Enhance the `cleanOutput` method for better command result formatting
4. **Add PowerShell Support**: Modify terminal creation to support PowerShell in addition to bash
### Learning More
- Read [HTTP-TRANSPORT.md](./HTTP-TRANSPORT.md) for detailed HTTP setup and deployment guides
- Explore [Azure Cloud Shell REST API](https://learn.microsoft.com/en-us/rest/api/cloudshell/) documentation
- Check out the [Model Context Protocol specification](https://modelcontextprotocol.io)
## Project Structure
```
mcp-azure-cloudshell/
├── src/
│ ├── index.ts # Main MCP server (stdio transport)
│ ├── http-server.ts # HTTP server (HTTP transport)
│ └── cloudshell.ts # Azure Cloud Shell connection manager
├── dist/ # Compiled JavaScript (generated)
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript configuration
├── README.md # This file
└── HTTP-TRANSPORT.md # HTTP transport documentation
```
## Development
### Development Mode
Run TypeScript compiler in watch mode:
```bash
npm run dev
```
### Build
```bash
npm run build
```
### Start Server (for testing)
```bash
npm start
```
## Architecture Details
### How It Works Under the Hood
The MCP server establishes a connection to Azure Cloud Shell through a three-step process:
#### Step 1: Console Provisioning (cloudshell.ts:60-93)
```typescript
PUT https://management.azure.com/providers/Microsoft.Portal/consoles/default
```
- Calls the **Microsoft.Portal** resource provider (not Microsoft.CloudShell!)
- Creates or retrieves an existing Cloud Shell console metadata object
- This is a **FREE** management resource with no compute/storage costs
- Returns a URI pointing to your Cloud Shell gateway
#### Step 2: Terminal Creation (cloudshell.ts:98-133)
```typescript
POST https://{gateway-url}/terminals?cols=160&rows=40&shell=bash
```
- Creates a bash terminal session in your Cloud Shell environment
- Specifies terminal dimensions and shell type
- Returns a WebSocket URI and terminal ID for bidirectional communication
#### Step 3: WebSocket Connection (cloudshell.ts:138-177)
```typescript
wss://{gateway-url}/terminals/{terminal-id}
```
- Opens a persistent WebSocket connection to the terminal
- All command input/output flows through this connection
- Commands are queued and output is buffered until completion
- Detects command completion by watching for shell prompts ($, #, >)
#### Command Execution Flow (cloudshell.ts:212-244)
1. Command sent via WebSocket with newline (`command\n`)
2. Output accumulated in buffer as messages arrive
3. Completion detected when prompt characters appear
4. ANSI escape codes removed and output cleaned
5. Echoed command line and final prompt stripped from result
### Authentication Flow
```
┌─────────────────┐
│ DefaultAzure │
│ Credential │
└────────┬────────┘
│
v
┌─────────────────┐
│ Azure │
│ Management API │
└────────┬────────┘
│
v
┌─────────────────┐
│ Cloud Shell │
│ Console │
└────────┬────────┘
│
v
┌─────────────────┐
│ Terminal │
│ Session │
└────────┬────────┘
│
v
┌─────────────────┐
│ WebSocket │
│ Connection │
└─────────────────┘
```
### API Endpoints Used
1. **Console Provisioning**
- `PUT https://management.azure.com/providers/Microsoft.Portal/consoles/default?api-version=2020-04-01-preview`
- Returns: `{ properties: { uri: "https://...", provisioningState: "Succeeded", osType: "linux" } }`
2. **Terminal Creation**
- `POST https://{gateway-url}/terminals?cols=160&rows=40&shell=bash`
- Returns: `{ id: "terminal-id", socketUri: "wss://..." }`
3. **WebSocket Connection**
- `wss://{gateway-url}/terminals/{terminal-id}`
- Protocol: Bidirectional text messages with terminal I/O
### Code Architecture
#### index.ts (MCP Server)
```typescript
import { AzureCloudShell } from "./cloudshell.js";
// Server maintains a singleton Cloud Shell instance
let cloudShell: AzureCloudShell | null = null;
// On first tool call, initialize connection
if (!cloudShell) {
cloudShell = new AzureCloudShell(credential);
await cloudShell.connect(); // Runs 3-step connection process
}
// Execute commands through the established connection
const result = await cloudShell.executeCommand(command, timeout);
```
The `.js` extension in the import is required because:
- TypeScript compiles to ES modules (`"type": "module"` in package.json)
- ES module imports must include file extensions
- At runtime, Node.js loads `dist/cloudshell.js` (the compiled output)
#### cloudshell.ts (Connection Manager)
```typescript
export class AzureCloudShell {
private ws: WebSocket | null = null; // WebSocket connection
private commandQueue: Array<{...}> = []; // Queue for sequential commands
private outputBuffer: string = ""; // Accumulates command output
private isReady: boolean = false; // Connection state
async connect() {
await this.provisionConsole(); // Step 1: Get console URI
await this.createTerminal(); // Step 2: Create terminal
await this.connectWebSocket(); // Step 3: Open WebSocket
}
async executeCommand(command: string, timeout: number): Promise<string> {
// Send command via WebSocket
this.ws.send(`${command}\n`);
// Wait for completion (detected by prompt characters)
return new Promise((resolve, reject) => {
this.commandQueue.push({ command, resolve, reject, timeout });
});
}
private handleMessage(data: WebSocket.Data) {
// Accumulate output until prompt detected
this.outputBuffer += data.toString();
// Resolve pending command when prompt appears
if (message includes prompt characters) {
currentCommand.resolve(this.cleanOutput(this.outputBuffer));
}
}
}
```
Key design patterns:
- **Singleton connection**: One WebSocket per server instance
- **Command queuing**: Sequential execution prevents output interleaving
- **Promise-based API**: Clean async/await interface for callers
- **Automatic cleanup**: SIGINT/SIGTERM handlers disconnect gracefully
## Command Execution
The server handles command execution with the following features:
- **Timeout Support** - Configurable timeout per command (default 30 seconds)
- **Output Cleaning** - Removes ANSI escape codes and terminal prompts
- **Queue Management** - Handles multiple commands sequentially
- **Error Handling** - Provides clear error messages for failures
## Troubleshooting
### Authentication Errors
**Error:** "Failed to get access token"
**Solutions:**
- Ensure you're logged in with Azure CLI: `az login`
- Verify your service principal credentials if using environment variables
- Check that you have appropriate permissions in your Azure subscription
### Connection Errors
**Error:** "Failed to provision console"
**Solutions:**
- Ensure Microsoft.CloudShell resource provider is registered
- Verify your subscription is active
- Check network connectivity to Azure
### Command Timeout
**Error:** "Command timed out after 30000ms"
**Solutions:**
- Increase the timeout parameter in your command
- Check if the command is actually long-running
- Use the `azure_shell_reconnect` tool to refresh the connection
### WebSocket Errors
**Error:** "WebSocket connection closed"
**Solutions:**
- Cloud Shell sessions timeout after 20 minutes of inactivity
- Use `azure_shell_reconnect` to establish a new connection
- The server will automatically reconnect on the next command
## Cost & Billing
### What Resources Are Created?
This MCP server does **NOT** create VMs, sandboxes, or resource groups. It connects to Azure's existing Cloud Shell infrastructure using REST APIs.
### Resource Providers Used
#### Microsoft.Portal Resource Provider
- **Resource Type**: `Microsoft.Portal/consoles`
- **Cost**: **FREE** - Just metadata tracking your Cloud Shell session
- **What it does**: Creates a console object that references your Cloud Shell gateway
- **Location**: Management plane only, no compute/storage resources
#### Microsoft.CloudShell Resource Provider
- **Resource Type**: Registration only (`operations`)
- **Cost**: **FREE** - Just API operation definitions
- **What it does**: Enables Cloud Shell APIs in your subscription
### What Cloud Shell Actually Provisions (First Use Only)
When you use Cloud Shell for the first time, Azure automatically provisions:
#### 1. Storage Account (~$0.05-0.10/month)
- **Resource**: Azure Files share (5 GB)
- **Purpose**: Persistent storage for your `$HOME` directory
- **Naming**: Usually `cs<randomstring>`
- **Cost**: ~$0.05-0.10/month for Standard LRS storage
- **Lifecycle**: Created once, persists across sessions
#### 2. Container Instances (Mostly FREE)
- **Resource**: Temporary Linux container for your shell session
- **Cost**:
- **First 20 hours/month**: **FREE**
- After 20 hours: ~$0.0013/second (~$4.68/hour if continuously running)
- **Lifecycle**:
- Spins up on first command
- Auto-terminates after 20 minutes of inactivity
- No charges when not running
#### 3. Network Egress (FREE within limits)
- **Resource**: WebSocket traffic and Azure API calls
- **Cost**: Typically covered by Azure free tier
- **Volume**: Minimal (just command I/O)
### Cost Comparison
| Scenario | Monthly Cost |
|----------|--------------|
| **Typical usage** (< 20 hrs/month) | ~$0.05-0.10 (storage only) |
| **Heavy usage** (40 hrs/month) | ~$0.10 + (20 hrs × $4.68) = ~$93.70 |
| **This MCP Server vs Azure Portal** | **Identical costs** - uses same infrastructure |
| **Idle (no commands running)** | ~$0.05-0.10 (storage only) |
### Important Notes
- This server uses the **same** Cloud Shell environment you access via portal.azure.com
- No additional resources are created beyond standard Cloud Shell
- The 20-minute idle timeout prevents runaway costs
- Storage costs persist but are minimal
- First 20 hours/month of compute are completely FREE
### Monitoring Costs
To check if Cloud Shell storage exists in your subscription:
```bash
az storage account list --query "[?tags.ms-resource-usage=='azure-cloud-shell']"
```
To view your Cloud Shell usage:
- Azure Portal → Cost Management → Cost Analysis
- Filter by service: "Azure Container Instances" and "Storage"
## Limitations
1. **Session Timeout** - Cloud Shell sessions timeout after 20 minutes of inactivity
2. **Single Session** - Only one active session per server instance
3. **Output Parsing** - Complex interactive commands may not work perfectly
4. **Rate Limiting** - Azure API rate limits apply
5. **Free Tier Limit** - 20 hours/month of free compute; additional hours billed at standard rates
## Security Considerations
1. **Credential Security** - Uses Azure's official credential chain
2. **No Credential Storage** - Tokens are fetched on-demand
3. **API Scope** - Limited to Azure Management API scope
4. **Command Execution** - Commands run in your Cloud Shell environment with your permissions
## Advanced Usage
### Custom Timeout
```typescript
// In Claude Code, specify longer timeout for slow commands
"List all resources with a 60 second timeout using azure_cli with command 'az resource list' and timeout 60"
```
### Chaining Commands
```bash
# Use && to chain multiple commands
"Execute in azure_shell: cd /home && ls -la && pwd"
```
### Working with Files
```bash
# Create and read files in Cloud Shell
"Create a file in Cloud Shell named test.txt with content 'Hello World'"
"Read the contents of test.txt in Cloud Shell"
```
## API Reference
### azure_cli Tool
**Parameters:**
- `command` (string, required) - Azure CLI command to execute
- `timeout` (number, optional) - Timeout in seconds (default: 30)
**Returns:**
- Command output as text
### azure_shell Tool
**Parameters:**
- `command` (string, required) - Bash command to execute
- `timeout` (number, optional) - Timeout in seconds (default: 30)
**Returns:**
- Command output as text
### azure_shell_reconnect Tool
**Parameters:**
- None
**Returns:**
- Success message
## Contributing
To extend this server:
1. **Add New Tools** - Define additional tools in `src/index.ts`
2. **Enhance Output Parsing** - Improve the `cleanOutput` method in `src/cloudshell.ts`
3. **Add PowerShell Support** - Modify terminal creation to use `pwsh` instead of `bash`
4. **Implement Resources** - Expose Azure resources as MCP resources
## Resources
- [Azure Cloud Shell Documentation](https://learn.microsoft.com/en-us/azure/cloud-shell/)
- [Azure Identity SDK](https://learn.microsoft.com/en-us/javascript/api/overview/azure/identity-readme)
- [Model Context Protocol](https://modelcontextprotocol.io)
- [Claude Code MCP Guide](https://docs.claude.com/en/docs/claude-code/mcp)
## License
MIT
## Acknowledgments
This server is built using:
- `@azure/identity` - Azure authentication
- `@modelcontextprotocol/sdk` - MCP server framework
- `ws` - WebSocket client
- Azure Cloud Shell REST API
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available