#
tokens: 31055/50000 37/37 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .claude
│   └── settings.local.json
├── .github
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows
│       ├── checks.yml
│       ├── ci.yml
│       ├── codeql-analysis.yml
│       └── npm-publish.yml
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── docs
│   └── adr
│       ├── 001-command-security-levels.md
│       ├── 002-mcp-for-shell-commands.md
│       ├── 003-command-approval-workflow.md
│       └── 004-cross-platform-support.md
├── eslint.config.js
├── examples
│   └── client-example.js
├── jest.config.cjs
├── jest.setup.cjs
├── LICENSE
├── logs
│   └── .gitkeep
├── manifest.json
├── mcp-settings-example.json
├── package-lock.json
├── package.json
├── README.md
├── run.sh
├── scripts
│   └── make-executable.js
├── smithery.yaml
├── src
│   ├── index.ts
│   ├── services
│   │   └── command-service.ts
│   └── utils
│       ├── command-whitelist-utils.ts
│       ├── logger.ts
│       └── platform-utils.ts
├── super-shell-mcp.dxt
├── tests
│   ├── command-service.platform.test.js
│   └── command-service.test.js
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/logs/.gitkeep:
--------------------------------------------------------------------------------

```
# This file ensures the logs directory is tracked by Git
# Log files themselves are ignored via .gitignore
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
yarn.lock

# Build output
build/
dist/
*.tsbuildinfo

# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# IDE and editor files
.idea/
.vscode/
*.swp
*.swo
.DS_Store

# Logs
logs/*.log
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
[![MseeP.ai Security Assessment Badge](https://mseep.net/pr/cfdude-super-shell-mcp-badge.png)](https://mseep.ai/app/cfdude-super-shell-mcp)

# Super Shell MCP Server

[![smithery badge](https://smithery.ai/badge/@cfdude/super-shell-mcp)](https://smithery.ai/package/@cfdude/super-shell-mcp)

An MCP (Model Context Protocol) server for executing shell commands across multiple platforms (Windows, macOS, Linux). This server provides a secure way to execute shell commands with built-in whitelisting and approval mechanisms.

> 🎉 **Now available as a Claude Desktop Extension!** Install with one click using the `.dxt` package - no developer tools or configuration required.

## Features

- Execute shell commands through MCP on Windows, macOS, and Linux
- Automatic platform detection and shell selection
- Support for multiple shells:
  - **Windows**: cmd.exe, PowerShell
  - **macOS**: zsh, bash, sh
  - **Linux**: bash, sh, zsh
- Command whitelisting with security levels:
  - **Safe**: Commands that can be executed without approval
  - **Requires Approval**: Commands that need explicit approval before execution
  - **Forbidden**: Commands that are explicitly blocked
- Platform-specific command whitelists
- Non-blocking approval workflow for potentially dangerous commands
- Comprehensive logging system with file-based logs
- Comprehensive command management tools
- Platform information tool for diagnostics

## Installation

### Option 1: Claude Desktop Extension (.dxt) - Recommended

**One-Click Installation for Claude Desktop:**

1. **Download** the `super-shell-mcp.dxt` file from the [latest release](https://github.com/cfdude/super-shell-mcp/releases)
2. **Quick Install**: Double-click the `.dxt` file while Claude Desktop is open
   
   **OR**
   
   **Manual Install**: 
   - Open Claude Desktop
   - Go to **Settings** > **Extensions**
   - Click **"Add Extension"**
   - Select the downloaded `super-shell-mcp.dxt` file

3. **Configure** (optional): Set custom shell path if needed
4. **Start using** - The extension is ready to use immediately!

✅ **Benefits of DXT Installation:**
- No developer tools required (Node.js, Python, etc.)
- No manual configuration files
- Automatic dependency management
- One-click installation and updates
- Secure credential storage in OS keychain

### Option 2: Installing via Smithery

To install Super Shell MCP Server for Claude Desktop automatically via [Smithery](https://smithery.ai/package/@cfdude/super-shell-mcp):

```bash
npx -y @smithery/cli install @cfdude/super-shell-mcp --client claude
```

### Option 3: Installing Manually

```bash
# Clone the repository
git clone https://github.com/cfdude/super-shell-mcp.git
cd super-shell-mcp

# Install dependencies
npm install

# Build the project
npm run build
```

## Usage

### For Claude Desktop Extension Users (.dxt)

If you installed using the `.dxt` extension (Option 1), **you're ready to go!** No additional configuration needed. The extension handles everything automatically:

- ✅ **Automatic startup** when Claude Desktop launches
- ✅ **Platform detection** and appropriate shell selection  
- ✅ **Built-in security** with command whitelisting and approval workflows
- ✅ **Optional configuration** via Claude Desktop's extension settings

### For Manual Installation Users

If you installed manually (Option 2 or 3), you'll need to configure Claude Desktop or your MCP client:

#### Starting the Server Manually

```bash
npm start
```

Or directly:

```bash
node build/index.js
```

#### Manual Configuration for MCP Clients

For manual installations, both Roo Code and Claude Desktop use a similar configuration format for MCP servers:

##### Using NPX (Recommended for Manual Setup)

The easiest way to use Super Shell MCP is with NPX, which automatically installs and runs the package from npm without requiring manual setup. The package is available on NPM at [https://www.npmjs.com/package/super-shell-mcp](https://www.npmjs.com/package/super-shell-mcp).

##### Roo Code Configuration with NPX

```json
"super-shell": {
  "command": "npx",
  "args": [
    "-y",
    "super-shell-mcp"
  ],
  "alwaysAllow": [],
  "disabled": false
}
```

##### Claude Desktop Configuration with NPX

```json
"super-shell": {
  "command": "npx",
  "args": [
    "-y",
    "super-shell-mcp"
  ],
  "alwaysAllow": false,
  "disabled": false
}
```

#### Option 2: Using Local Installation

If you prefer to use a local installation, add the following to your Roo Code MCP settings configuration file (located at `~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`):

```json
"super-shell": {
  "command": "node",
  "args": [
    "/path/to/super-shell-mcp/build/index.js"
  ],
  "alwaysAllow": [],
  "disabled": false
}
```

You can optionally specify a custom shell by adding a shell parameter:

```json
"super-shell": {
  "command": "node",
  "args": [
    "/path/to/super-shell-mcp/build/index.js",
    "--shell=/usr/bin/bash"
  ],
  "alwaysAllow": [],
  "disabled": false
}
```
Windows 11 example
```json
"super-shell": {
      "command": "C:\\Program Files\\nodejs\\node.exe",
      "args": [
        "C:\\Program Files\\nodejs\\node_modules\\npm\\bin\\npx-cli.js",
        "-y",
        "super-shell-mcp",
        "C:\\Users\\username"
      ],
      "alwaysAllow": [],
      "disabled": false
    }
```

#### Claude Desktop Configuration

Add the following to your Claude Desktop configuration file (located at `~/Library/Application Support/Claude/claude_desktop_config.json`):

```json
"super-shell": {
  "command": "node",
  "args": [
    "/path/to/super-shell-mcp/build/index.js"
  ],
  "alwaysAllow": false,
  "disabled": false
}
```
For Windows users, the configuration file is typically located at `%APPDATA%\Claude\claude_desktop_config.json`.

### Platform-Specific Configuration

#### Windows
- Default shell: cmd.exe (or PowerShell if available)
- Configuration paths:
  - Roo Code: `%APPDATA%\Code\User\globalStorage\rooveterinaryinc.roo-cline\settings\cline_mcp_settings.json`
  - Claude Desktop: `%APPDATA%\Claude\claude_desktop_config.json`
- Shell path examples:
  - cmd.exe: `C:\\Windows\\System32\\cmd.exe`
  - PowerShell: `C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`
  - PowerShell Core: `C:\\Program Files\\PowerShell\\7\\pwsh.exe`

#### macOS
- Default shell: /bin/zsh
- Configuration paths:
  - Roo Code: `~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`
  - Claude Desktop: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Shell path examples:
  - zsh: `/bin/zsh`
  - bash: `/bin/bash`
  - sh: `/bin/sh`

#### Linux
- Default shell: /bin/bash (or $SHELL environment variable)
- Configuration paths:
  - Roo Code: `~/.config/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`
  - Claude Desktop: `~/.config/Claude/claude_desktop_config.json`
- Shell path examples:
  - bash: `/bin/bash`
  - sh: `/bin/sh`
  - zsh: `/usr/bin/zsh`


You can optionally specify a custom shell:

```json
"super-shell": {
  "command": "node",
  "args": [
    "/path/to/super-shell-mcp/build/index.js",
    "--shell=C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"
  ],
  "alwaysAllow": false,
  "disabled": false
}
```

Replace `/path/to/super-shell-mcp` with the actual path where you cloned the repository.

> **Note**:
> - For Roo Code: Setting `alwaysAllow` to an empty array `[]` is recommended for security reasons, as it will prompt for approval before executing any commands. If you want to allow specific commands without prompting, you can add their names to the array, for example: `"alwaysAllow": ["execute_command", "get_whitelist"]`.
> - For Claude Desktop: Setting `alwaysAllow` to `false` is recommended for security reasons. Claude Desktop uses a boolean value instead of an array, where `false` means all commands require approval and `true` means all commands are allowed without prompting.
>
> **Important**: The `alwaysAllow` parameter is processed by the MCP client (Roo Code or Claude Desktop), not by the Super Shell MCP server itself. The server will work correctly with either format, as the client handles the approval process before sending requests to the server.

### Available Tools
The server exposes the following MCP tools:

#### `get_platform_info`

Get information about the current platform and shell.

```json
{}
```


#### `execute_command`

Execute a shell command on the current platform.

```json
{
  "command": "ls",
  "args": ["-la"]
}
```

#### `get_whitelist`

Get the list of whitelisted commands.

```json
{}
```

#### `add_to_whitelist`

Add a command to the whitelist.

```json
{
  "command": "python3",
  "securityLevel": "safe",
  "description": "Run Python 3 scripts"
}
```

#### `update_security_level`

Update the security level of a whitelisted command.

```json
{
  "command": "python3",
  "securityLevel": "requires_approval"
}
```

#### `remove_from_whitelist`

Remove a command from the whitelist.

```json
{
  "command": "python3"
}
```

#### `get_pending_commands`

Get the list of commands pending approval.

```json
{}
```

#### `approve_command`

Approve a pending command.

```json
{
  "commandId": "command-uuid-here"
}
```

#### `deny_command`

Deny a pending command.

```json
{
  "commandId": "command-uuid-here",
  "reason": "This command is potentially dangerous"
}
```

## Default Whitelisted Commands

The server includes platform-specific command whitelists that are automatically selected based on the detected platform.

### Common Safe Commands (All Platforms)

- `echo` - Print text to standard output

### Unix-like Safe Commands (macOS/Linux)

- `ls` - List directory contents
- `pwd` - Print working directory
- `echo` - Print text to standard output
- `cat` - Concatenate and print files
- `grep` - Search for patterns in files
- `find` - Find files in a directory hierarchy
- `cd` - Change directory
- `head` - Output the first part of files
- `tail` - Output the last part of files
- `wc` - Print newline, word, and byte counts

### Windows-specific Safe Commands

- `dir` - List directory contents
- `type` - Display the contents of a text file
- `findstr` - Search for strings in files
- `where` - Locate programs
- `whoami` - Display current user
- `hostname` - Display computer name
- `ver` - Display operating system version
### Commands Requiring Approval

#### Windows Commands Requiring Approval

- `copy` - Copy files
- `move` - Move files
- `mkdir` - Create directories
- `rmdir` - Remove directories
- `rename` - Rename files
- `attrib` - Change file attributes

#### Unix Commands Requiring Approval


- `mv` - Move (rename) files
- `cp` - Copy files and directories
- `mkdir` - Create directories
- `touch` - Change file timestamps or create empty files
- `chmod` - Change file mode bits
- `chown` - Change file owner and group

### Forbidden Commands

#### Windows Forbidden Commands

- `del` - Delete files
- `erase` - Delete files
- `format` - Format a disk
- `runas` - Execute a program as another user

#### Unix Forbidden Commands

- `rm` - Remove files or directories
- `sudo` - Execute a command as another user

## Security Considerations

- All commands are executed with the permissions of the user running the MCP server
- Commands requiring approval are held in a queue until explicitly approved
- Forbidden commands are never executed
- The server uses Node.js's `execFile` instead of `exec` to prevent shell injection
- Arguments are validated against allowed patterns when specified

## Extending the Whitelist

You can extend the whitelist by using the `add_to_whitelist` tool. For example:

```json
{
  "command": "npm",
  "securityLevel": "requires_approval",
  "description": "Node.js package manager"
}
```

## NPM Package Information

Super Shell MCP is available as an npm package at [https://www.npmjs.com/package/super-shell-mcp](https://www.npmjs.com/package/super-shell-mcp).

### Benefits of Using NPX

Using the NPX method (as shown in Option 1 of the Configuration section) offers several advantages:

1. **No Manual Setup**: No need to clone the repository, install dependencies, or build the project
2. **Automatic Updates**: Always uses the latest published version
3. **Cross-Platform Compatibility**: Works the same way on Windows, macOS, and Linux
4. **Simplified Configuration**: Shorter configuration with no absolute paths
5. **Reduced Maintenance**: No local files to manage or update

### Using from GitHub

If you prefer to use the latest development version directly from GitHub:

```json
"super-shell": {
  "command": "npx",
  "args": [
    "-y",
    "github:cfdude/super-shell-mcp"
  ],
  "alwaysAllow": [],  // For Roo Code
  "disabled": false
}
```

### Publishing Your Own Version

If you want to publish your own modified version to npm:

1. Update the package.json with your details
2. Ensure the "bin" field is properly configured:
   ```json
   "bin": {
     "super-shell-mcp": "./build/index.js"
   }
   ```
3. Publish to npm:
   ```bash
   npm publish
   ```

## NPX Best Practices

For optimal integration with MCP clients using NPX, this project follows these best practices:

1. **Executable Entry Point**: The main file includes a shebang line (`#!/usr/bin/env node`) and is made executable during build.

2. **Package Configuration**:
   - `"type": "module"` - Ensures ES Modules are used
   - `"bin"` field - Maps the command name to the entry point
   - `"files"` field - Specifies which files to include when publishing
   - `"prepare"` script - Ensures compilation happens on install

3. **TypeScript Configuration**:
   - `"module": "NodeNext"` - Proper ES Modules support
   - `"moduleResolution": "NodeNext"` - Consistent with ES Modules

4. **Automatic Installation and Execution**:
   - The MCP client configuration uses `npx -y` to automatically install and run the package
   - No terminal window is tied up as the process runs in the background

5. **Publishing Process**:
   ```bash
   # Update version in package.json
   npm version patch  # or minor/major as appropriate
   
   # Build and publish
   npm publish
   ```

These practices ensure the MCP server can be started automatically by the MCP client without requiring a separate terminal window, improving user experience and operational efficiency.

## Troubleshooting

### Cross-Platform Issues

#### Windows-Specific Issues

1. **PowerShell Script Execution Policy**
   - **Issue**: PowerShell may block script execution with the error "Execution of scripts is disabled on this system"
   - **Solution**: Run PowerShell as Administrator and execute `Set-ExecutionPolicy RemoteSigned` or use the `-ExecutionPolicy Bypass` parameter when configuring the shell

2. **Path Separators**
   - **Issue**: Windows uses backslashes (`\`) in paths, which need to be escaped in JSON
   - **Solution**: Use double backslashes (`\\`) in JSON configuration files, e.g., `C:\\Windows\\System32\\cmd.exe`

3. **Command Not Found**
   - **Issue**: Windows doesn't have Unix commands like `ls`, `grep`, etc.
   - **Solution**: Use Windows equivalents (`dir` instead of `ls`, `findstr` instead of `grep`)

#### macOS/Linux-Specific Issues

1. **Shell Permissions**
   - **Issue**: Permission denied when executing commands
   - **Solution**: Ensure the shell has appropriate permissions with `chmod +x /path/to/shell`

2. **Environment Variables**
   - **Issue**: Environment variables not available in MCP server
   - **Solution**: Set environment variables in the shell's profile file (`.zshrc`, `.bashrc`, etc.)

### General Troubleshooting

1. **Shell Detection Issues**
   - **Issue**: Server fails to detect the correct shell
   - **Solution**: Explicitly specify the shell path in the configuration

2. **Command Execution Timeout**
   - **Issue**: Commands taking too long and timing out
   - **Solution**: Increase the timeout value in the command service constructor

### Logging System

The server includes a comprehensive logging system that writes logs to a file for easier debugging and monitoring:

1. **Log File Location**
   - Default: `logs/super-shell-mcp.log` in the server's directory
   - The logs directory is created automatically and tracked by Git (with a .gitkeep file)
   - Log files themselves are excluded from Git via .gitignore
   - Contains detailed information about server operations, command execution, and approval workflow

2. **Log Levels**
   - **INFO**: General operational information
   - **DEBUG**: Detailed debugging information
   - **ERROR**: Error conditions and exceptions

3. **Viewing Logs**
   - Use standard file viewing commands to check logs:
     ```bash
     # View the entire log
     cat logs/super-shell-mcp.log
     
     # Follow log updates in real-time
     tail -f logs/super-shell-mcp.log
     ```

4. **Log Content**
   - Server startup and configuration
   - Command execution requests and results
   - Approval workflow events (pending, approved, denied)
   - Error conditions and troubleshooting information

3. **Whitelist Management**
   - **Issue**: Need to add custom commands to whitelist
   - **Solution**: Use the `add_to_whitelist` tool to add commands specific to your environment

## License

This MCP server is licensed under the MIT License. This means you are free to use, modify, and distribute the software, subject to the terms and conditions of the MIT License. For more details, please see the LICENSE file in the project repository.

```

--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------

```markdown

# Contributing to This Project

Thank you for your interest in contributing! We welcome pull requests and contributions from the community. Please take a moment to read the guidelines below to ensure a smooth collaboration process.

## 📦 Getting Started

1. **Fork the Repository**  
   Click the **Fork** button at the top of the repository page to create your own copy of the project.

2. **Clone Your Fork**  
   ```bash
   git clone https://github.com/your-username/your-fork.git
   cd your-fork
   ```
   
	
3.	Create a Feature Branch
Create a new branch based on main for your changes:
```bash
git checkout -b feature/your-feature-name
```


🚀 Submitting a Pull Request

Once you’re ready to share your changes:
	1.	Ensure Code Quality
	•	Run all linting checks.
	•	Use Prettier to format your code.
	•	Confirm all tests pass (if applicable).
	2.	Document Your Work
	•	Add meaningful comments to your code.
	•	Update or add documentation where necessary (e.g., README, inline comments, or docs folder).
	3.	Submit Your PR
	•	Push your branch to your fork:
```bash
git push origin feature/your-feature-name
```

  * Open a Pull Request against the main branch of the original repository.
  * Include a clear summary of your changes and why they are beneficial.

✅ Code Standards
	* Follow the existing code style and formatting conventions.
  * All code must pass linting and Prettier formatting before submission.
  * Keep your changes focused and limited to the scope of your feature or fix.

❤️ Be Kind
  * Be respectful and constructive in code reviews and discussions.
  * Ask questions if you’re unsure—collaboration is key.

We appreciate your help in improving the project for everyone. Thank you for contributing!

```

--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------

```markdown
# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
  and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
  overall community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or
  advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
  address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
.
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series
of actions.

**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.

### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior,  harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within
the community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.

Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

```

--------------------------------------------------------------------------------
/mcp-settings-example.json:
--------------------------------------------------------------------------------

```json
{
  "mcpServers": {
    "mac-shell": {
      "command": "node",
      "args": ["/path/to/mac-shell-mcp/build/index.js"],
      "disabled": false,
      "alwaysAllow": []
    }
  }
}
```

--------------------------------------------------------------------------------
/run.sh:
--------------------------------------------------------------------------------

```bash
#!/bin/bash

# Build the project if needed
if [ ! -d "./build" ] || [ ! -f "./build/index.js" ]; then
  echo "Building project..."
  npm run build
fi

# Run the MCP server
echo "Starting Mac Shell MCP Server..."
node build/index.js
```

--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------

```yaml
name: CI

on: 
  push:
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm test

```

--------------------------------------------------------------------------------
/.claude/settings.local.json:
--------------------------------------------------------------------------------

```json
{
  "permissions": {
    "allow": [
      "Bash(gh pr create:*)",
      "Bash(gh browse:*)",
      "Bash(docker build:*)",
      "Bash(docker run:*)",
      "mcp__super-shell__execute_command",
      "mcp__git__git_status",
      "mcp__git__git_add",
      "mcp__git__git_commit"
    ],
    "deny": []
  }
}
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "types": ["node"],
    "esModuleInterop": true,
    "strict": true,
    "outDir": "./build",
    "rootDir": "./src",
    "declaration": true,
    "sourceMap": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "build"]
}
```

--------------------------------------------------------------------------------
/jest.config.cjs:
--------------------------------------------------------------------------------

```
module.exports = {
  transform: {},
  testEnvironment: 'node',
  testMatch: ['**/tests/**/*.test.js'],
  verbose: true,
  testRunner: 'jest-circus/runner',
  transformIgnorePatterns: [
    'node_modules/(?!(.*\\.mjs$))'
  ],
  setupFiles: ['./jest.setup.cjs'],
  moduleNameMapper: {
    '^../build/services/command-service.js$': '<rootDir>/jest.setup.cjs',
    '^../build/utils/platform-utils.js$': '<rootDir>/jest.setup.cjs'
  }
};
```

--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------

```yaml
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml

startCommand:
  type: stdio
  configSchema:
    # JSON Schema defining the configuration options for the MCP.
    type: object
    properties: {}
    default: {}
  commandFunction:
    # A JS function that produces the CLI command based on the given config to start the MCP on stdio.
    |-
    (config) => ({ command: 'npm', args: ['start'] })
  exampleConfig: {}

```

--------------------------------------------------------------------------------
/.github/workflows/checks.yml:
--------------------------------------------------------------------------------

```yaml
name: Checks

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  lint-and-build:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        
    - name: Install dependencies
      run: npm ci
      
    - name: Run Linter
      run: npm run lint
      
    - name: Build Verification
      run: npm run build
```

--------------------------------------------------------------------------------
/scripts/make-executable.js:
--------------------------------------------------------------------------------

```javascript
import fs from 'fs';
import { platform } from 'os';

const isWindows = platform() === 'win32';
const indexPath = './build/index.js';

if (isWindows) {
  console.log('Windows detected, skipping chmod operation');
} else {
  try {
    // On Unix-like systems, make the file executable
    fs.chmodSync(indexPath, '755');
    console.log(`Made ${indexPath} executable`);
  } catch (error) {
    console.error(`Error making ${indexPath} executable:`, error);
    process.exit(1);
  }
}
```

--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------

```markdown
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.

```

--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------

```yaml
# These are supported funding model platforms

github: [cfdude]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: cfdude
thanks_dev: # Replace with a single thanks.dev username
custom: ['paypal.me/cfdude', 'https://account.venmo.com/u/cfdude', 'https://cash.app/$cfdude']

```

--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------

```markdown
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
 - OS: [e.g. iOS]
 - Browser [e.g. chrome, safari]
 - Version [e.g. 22]

**Smartphone (please complete the following information):**
 - Device: [e.g. iPhone6]
 - OS: [e.g. iOS8.1]
 - Browser [e.g. stock browser, safari]
 - Version [e.g. 22]

**Additional context**
Add any other context about the problem here.

```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
# Use Node.js 18 Alpine for smaller image size
FROM node:18-alpine

# Set working directory
WORKDIR /app

# Create logs directory
RUN mkdir -p logs

# Copy package files first for better caching
COPY package*.json ./

# Copy TypeScript configuration and source code
COPY tsconfig.json ./
COPY src/ ./src/

# Install all dependencies (including dev dependencies for build)
RUN npm ci

# Build the project (compiles TypeScript and sets executable permissions)
RUN npm run build

# Remove dev dependencies to reduce image size
RUN npm prune --omit=dev && npm cache clean --force

# Ensure the built file is executable
RUN chmod +x build/index.js

# Expose stdio for MCP communication
# Note: MCP servers typically communicate via stdio, not network ports

# Set the entrypoint to the built application
ENTRYPOINT ["node", "build/index.js"]

```

--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------

```yaml
name: "CodeQL Analysis"

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
  schedule:
    - cron: '0 0 * * 0'  # Run once a week at midnight on Sunday

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write

    strategy:
      fail-fast: false
      matrix:
        language: [ 'javascript' ]  # Add other languages as needed: python, java, go, cpp, etc.

    steps:
    - name: Checkout repository
      uses: actions/checkout@v4

    - name: Initialize CodeQL
      uses: github/codeql-action/init@v3
      with:
        languages: ${{ matrix.language }}

    # Autobuild attempts to build any compiled languages
    - name: Autobuild
      uses: github/codeql-action/autobuild@v3

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v3
      with:
        category: "/language:${{matrix.language}}"

```

--------------------------------------------------------------------------------
/.github/workflows/npm-publish.yml:
--------------------------------------------------------------------------------

```yaml
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages

name: Publish Package to npmjs

on:
  release:
    types: [created]

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm test

  publish-npm:
    needs: build
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          registry-url: https://registry.npmjs.org/
      - run: npm ci
      - run: npm publish --provenance
        env:
          NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "super-shell-mcp",
  "version": "2.0.13",
  "description": "MCP server for executing shell commands across multiple platforms",
  "type": "module",
  "main": "build/index.js",
  "bin": {
    "super-shell-mcp": "./build/index.js"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/cfdude/super-shell-mcp.git"
  },
  "files": [
    "build"
  ],
  "scripts": {
    "build": "tsc && chmod +x build/index.js",
    "prepare": "npm run build",
    "start": "node build/index.js",
    "dev": "ts-node --esm src/index.ts",
    "lint": "eslint .",
    "test": "jest --config=jest.config.cjs",
    "test:quiet": "jest --config=jest.config.cjs --silent"
  },
  "keywords": [
    "mcp",
    "shell",
    "macos",
    "windows",
    "linux",
    "zsh",
    "bash",
    "powershell",
    "cmd",
    "terminal",
    "cross-platform"
  ],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.6.1",
    "zod": "^3.22.4"
  },
  "devDependencies": {
    "@eslint/js": "^9.30.1",
    "@types/jest": "^29.5.11",
    "@types/node": "^20.17.24",
    "@typescript-eslint/eslint-plugin": "^8.0.0",
    "@typescript-eslint/parser": "^8.0.0",
    "eslint": "^9.30.1",
    "jest": "^29.7.0",
    "jest-circus": "^29.7.0",
    "ts-node": "^10.9.2",
    "typescript": "^5.3.3"
  }
}
```

--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------

```json
{
  "dxt_version": "0.1",
  "name": "super-shell-mcp",
  "version": "2.0.13",
  "description": "Execute shell commands across multiple platforms with built-in security controls",
  "long_description": "An MCP server for executing shell commands on Windows, macOS, and Linux with automatic platform detection, command whitelisting, and approval workflows for security.",
  "author": {
    "name": "cfdude",
    "url": "https://github.com/cfdude/super-shell-mcp"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/cfdude/super-shell-mcp"
  },
  "license": "MIT",
  "keywords": ["shell", "commands", "security", "cross-platform", "terminal"],
  "server": {
    "type": "node",
    "entry_point": "server/index.js",
    "mcp_config": {
      "command": "node",
      "args": ["${__dirname}/server/index.js"],
      "env": {
        "CUSTOM_SHELL": "${user_config.shell_path}"
      }
    }
  },
  "tools": [
    {
      "name": "execute_command",
      "description": "Execute shell commands with security controls"
    },
    {
      "name": "get_whitelist",
      "description": "Get list of whitelisted commands"
    },
    {
      "name": "add_to_whitelist",
      "description": "Add commands to the security whitelist"
    },
    {
      "name": "get_platform_info",
      "description": "Get current platform and shell information"
    }
  ],
  "user_config": {
    "shell_path": {
      "type": "string",
      "title": "Custom Shell Path",
      "description": "Optional: Specify a custom shell path (e.g., /bin/zsh, C:\\Windows\\System32\\cmd.exe)",
      "required": false
    }
  }
}
```

--------------------------------------------------------------------------------
/docs/adr/001-command-security-levels.md:
--------------------------------------------------------------------------------

```markdown
# ADR 001: Command Security Levels

## Status

Accepted

## Context

When executing shell commands through an MCP server, there's a significant security risk if all commands are allowed without restrictions. Different commands have varying levels of potential impact on the system:

1. Some commands are relatively safe (e.g., `ls`, `pwd`, `echo`)
2. Some commands can modify the system but in limited ways (e.g., `mkdir`, `cp`, `mv`)
3. Some commands can cause significant damage (e.g., `rm -rf`, `sudo`)

We need a mechanism to categorize commands based on their potential risk and handle them accordingly.

## Decision

We will implement a three-tier security level system for commands:

1. **Safe Commands**: These commands can be executed immediately without approval. They are read-only or have minimal impact on the system.

2. **Commands Requiring Approval**: These commands can modify the system but are not inherently dangerous. They will be queued for explicit approval before execution.

3. **Forbidden Commands**: These commands are considered too dangerous and will be rejected outright.

Each command will be categorized in a whitelist, and the security level will determine how the command is handled when execution is requested.

## Consequences

### Positive

- Provides a clear security model for command execution
- Allows safe commands to be executed without friction
- Creates an approval workflow for potentially dangerous commands
- Completely blocks high-risk commands
- Makes the security policy explicit and configurable

### Negative

- Requires maintaining a whitelist of commands
- May introduce friction for legitimate use cases of commands requiring approval
- Initial categorization may not be perfect and could require adjustment

## Implementation

The security levels will be implemented as an enum in the `CommandService` class:

```typescript
export enum CommandSecurityLevel {
  SAFE = 'safe',
  REQUIRES_APPROVAL = 'requires_approval',
  FORBIDDEN = 'forbidden'
}
```

Commands will be stored in a whitelist with their security level:

```typescript
export interface CommandWhitelistEntry {
  command: string;
  securityLevel: CommandSecurityLevel;
  allowedArgs?: Array<string | RegExp>;
  description?: string;
}
```

When a command is executed, its security level will determine the behavior:
- Safe commands are executed immediately
- Commands requiring approval are queued for explicit approval
- Forbidden commands are rejected with an error
```

--------------------------------------------------------------------------------
/src/utils/logger.ts:
--------------------------------------------------------------------------------

```typescript
import * as fs from 'fs';
import * as path from 'path';

/**
 * Simple logging utility that writes to a file
 */
export class Logger {
  private logFile: string;
  private enabled: boolean;
  private fileStream: fs.WriteStream | null = null;

  /**
   * Create a new logger
   * @param logFile Path to the log file
   * @param enabled Whether logging is enabled
   */
  constructor(logFile: string, enabled = true) {
    this.logFile = logFile;
    this.enabled = enabled;
    
    if (this.enabled) {
      // Create the directory if it doesn't exist
      const logDir = path.dirname(this.logFile);
      console.error(`Creating log directory: ${logDir}`);
      try {
        if (!fs.existsSync(logDir)) {
          fs.mkdirSync(logDir, { recursive: true });
        }
      } catch (error) {
        console.error(`Error creating log directory: ${error}`);
        // Fall back to a directory we know exists
        this.logFile = './super-shell-mcp.log';
        console.error(`Falling back to log file: ${this.logFile}`);
      }
      
      // Create or truncate the log file
      this.fileStream = fs.createWriteStream(this.logFile, { flags: 'w' });
      
      // Write a header to the log file
      this.log('INFO', `Logging started at ${new Date().toISOString()}`);
    }
  }

  /**
   * Log a message
   * @param level Log level (INFO, DEBUG, ERROR, etc.)
   * @param message Message to log
   */
  public log(level: string, message: string): void {
    if (!this.enabled || !this.fileStream) {
      return;
    }
    
    const timestamp = new Date().toISOString();
    const logMessage = `[${timestamp}] [${level}] ${message}\n`;
    
    this.fileStream.write(logMessage);
  }

  /**
   * Log an info message
   * @param message Message to log
   */
  public info(message: string): void {
    this.log('INFO', message);
  }

  /**
   * Log a debug message
   * @param message Message to log
   */
  public debug(message: string): void {
    this.log('DEBUG', message);
  }

  /**
   * Log an error message
   * @param message Message to log
   */
  public error(message: string): void {
    this.log('ERROR', message);
  }

  /**
   * Close the logger
   */
  public close(): void {
    if (this.fileStream) {
      this.fileStream.end();
      this.fileStream = null;
    }
  }
}

// Create a singleton logger instance
let loggerInstance: Logger | null = null;

/**
 * Get the logger instance
 * @param logFile Path to the log file
 * @param enabled Whether logging is enabled
 * @returns Logger instance
 */
export function getLogger(logFile?: string, enabled?: boolean): Logger {
  if (!loggerInstance && logFile) {
    loggerInstance = new Logger(logFile, enabled);
  }
  
  if (!loggerInstance) {
    throw new Error('Logger not initialized');
  }
  
  return loggerInstance;
}
```

--------------------------------------------------------------------------------
/docs/adr/002-mcp-for-shell-commands.md:
--------------------------------------------------------------------------------

```markdown
# ADR 002: Using MCP for Shell Command Execution

## Status

Accepted

## Context

There are several ways to provide shell command execution capabilities to AI assistants:

1. Custom API endpoints
2. Direct integration with specific AI platforms
3. Standardized protocols like MCP (Model Context Protocol)

Each approach has different tradeoffs in terms of flexibility, security, and integration complexity. We need to decide on the most appropriate approach for our shell command execution service.

## Decision

We will implement shell command execution as an MCP server for the following reasons:

1. **Standardization**: MCP is an emerging standard for AI tool integration, supported by major AI platforms like Anthropic's Claude.

2. **Discoverability**: MCP provides built-in tool discovery, allowing AI assistants to automatically learn about available commands and their parameters.

3. **Security**: MCP's structured approach allows for clear security boundaries and validation of inputs.

4. **Flexibility**: MCP servers can be used with any MCP-compatible client, not just specific AI platforms.

5. **Future-proofing**: As more AI platforms adopt MCP, our implementation will be compatible without changes.

## Consequences

### Positive

- Works with any MCP-compatible client (Claude Desktop, etc.)
- Provides structured tool definitions with clear parameter schemas
- Enables dynamic tool discovery
- Follows an emerging industry standard
- Separates concerns between command execution and AI integration

### Negative

- MCP is still an evolving standard
- Requires understanding of MCP concepts and implementation details
- May have more overhead than a direct, custom integration

## Implementation

We will implement the MCP server using the official TypeScript SDK:

```typescript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
```

The server will expose tools for command execution and whitelist management:

```typescript
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: 'execute_command',
      description: 'Execute a shell command on macOS',
      inputSchema: {
        type: 'object',
        properties: {
          command: {
            type: 'string',
            description: 'The command to execute',
          },
          args: {
            type: 'array',
            items: {
              type: 'string',
            },
            description: 'Command arguments',
          },
        },
        required: ['command'],
      },
    },
    // Additional tools...
  ],
}));
```

The server will use stdio transport for compatibility with Claude Desktop and other MCP clients:

```typescript
const transport = new StdioServerTransport();
await this.server.connect(transport);
```

--------------------------------------------------------------------------------
/docs/adr/003-command-approval-workflow.md:
--------------------------------------------------------------------------------

```markdown
# ADR 003: Command Approval Workflow

## Status

Accepted

## Context

When executing shell commands, there's a middle ground between completely safe commands and forbidden commands. Some commands can modify the system in potentially harmful ways but are still necessary for legitimate use cases. We need a mechanism to handle these commands safely.

Options considered:
1. Reject all potentially dangerous commands
2. Allow all commands with appropriate warnings
3. Implement an approval workflow for commands that require additional verification

## Decision

We will implement an approval workflow for commands that are potentially dangerous but still necessary. This workflow will:

1. Queue commands marked as requiring approval
2. Provide tools to list pending commands
3. Allow explicit approval or denial of pending commands
4. Execute approved commands and reject denied commands

This approach balances security with usability by allowing potentially dangerous commands to be executed after explicit approval.

## Consequences

### Positive

- Provides a middle ground between allowing and forbidding commands
- Creates an audit trail of command approvals
- Allows for human judgment in borderline cases
- Enables safe use of necessary system-modifying commands
- Prevents accidental execution of dangerous commands

### Negative

- Introduces asynchronous workflow for command execution
- Requires additional user interaction for approval
- May create confusion if approvals are delayed or forgotten

## Implementation

The approval workflow will be implemented using a queue of pending commands:

```typescript
interface PendingCommand {
  id: string;
  command: string;
  args: string[];
  requestedAt: Date;
  requestedBy?: string;
  resolve: (value: CommandResult) => void;
  reject: (reason: Error) => void;
}
```

When a command requiring approval is executed, it will be added to the queue:

```typescript
private queueCommandForApproval(
  command: string,
  args: string[] = [],
  requestedBy?: string
): Promise<CommandResult> {
  return new Promise((resolve, reject) => {
    const id = randomUUID();
    const pendingCommand: PendingCommand = {
      id,
      command,
      args,
      requestedAt: new Date(),
      requestedBy,
      resolve,
      reject
    };

    this.pendingCommands.set(id, pendingCommand);
    this.emit('command:pending', pendingCommand);
  });
}
```

The MCP server will expose tools to list, approve, and deny pending commands:

```typescript
// Get pending commands
const pendingCommands = await client.callTool('get_pending_commands', {});

// Approve a command
await client.callTool('approve_command', { commandId });

// Deny a command
await client.callTool('deny_command', { commandId, reason: 'Not allowed' });
```

This workflow ensures that potentially dangerous commands are only executed after explicit approval, providing an additional layer of security.
```

--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------

```javascript
import js from '@eslint/js';
import tsEslint from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';

export default [
  js.configs.recommended,
  {
    files: ['**/*.js'],
    languageOptions: {
      ecmaVersion: 2022,
      sourceType: 'module',
      globals: {
        console: 'readonly',
        process: 'readonly',
        Buffer: 'readonly',
        __dirname: 'readonly',
        __filename: 'readonly',
        global: 'readonly',
        module: 'readonly',
        require: 'readonly',
        exports: 'readonly',
        setTimeout: 'readonly',
        clearTimeout: 'readonly',
        setInterval: 'readonly',
        clearInterval: 'readonly',
        setImmediate: 'readonly',
        clearImmediate: 'readonly'
      }
    },
    rules: {
      'no-unused-vars': 'warn',
      'no-console': 'off',
      'prefer-const': 'error',
      'no-var': 'error'
    }
  },
  {
    files: ['**/*.ts'],
    languageOptions: {
      parser: tsParser,
      ecmaVersion: 2022,
      sourceType: 'module',
      globals: {
        console: 'readonly',
        process: 'readonly',
        Buffer: 'readonly',
        __dirname: 'readonly',
        __filename: 'readonly',
        global: 'readonly',
        module: 'readonly',
        require: 'readonly',
        exports: 'readonly',
        setTimeout: 'readonly',
        clearTimeout: 'readonly',
        setInterval: 'readonly',
        clearInterval: 'readonly',
        setImmediate: 'readonly',
        clearImmediate: 'readonly'
      }
    },
    plugins: {
      '@typescript-eslint': tsEslint
    },
    rules: {
      'no-unused-vars': 'off',
      '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
      'no-console': 'off',
      'prefer-const': 'error',
      'no-var': 'error'
    }
  },
  {
    files: ['**/*.cjs'],
    languageOptions: {
      ecmaVersion: 2022,
      sourceType: 'commonjs',
      globals: {
        console: 'readonly',
        process: 'readonly',
        Buffer: 'readonly',
        __dirname: 'readonly',
        __filename: 'readonly',
        global: 'readonly',
        module: 'readonly',
        require: 'readonly',
        exports: 'readonly',
        setTimeout: 'readonly',
        clearTimeout: 'readonly',
        setInterval: 'readonly',
        clearInterval: 'readonly',
        setImmediate: 'readonly',
        clearImmediate: 'readonly'
      }
    },
    rules: {
      'no-unused-vars': 'warn',
      'no-console': 'off',
      'prefer-const': 'error',
      'no-var': 'error'
    }
  },
  {
    files: ['**/*.test.js', '**/*.test.ts', '**/tests/**/*.js', '**/tests/**/*.ts'],
    languageOptions: {
      globals: {
        describe: 'readonly',
        test: 'readonly',
        it: 'readonly',
        expect: 'readonly',
        beforeAll: 'readonly',
        afterAll: 'readonly',
        beforeEach: 'readonly',
        afterEach: 'readonly',
        jest: 'readonly'
      }
    }
  },
  {
    ignores: ['build/**', 'node_modules/**', '*.config.js', '*.config.cjs']
  }
];
```

--------------------------------------------------------------------------------
/src/utils/platform-utils.ts:
--------------------------------------------------------------------------------

```typescript
import * as os from 'os';
import * as path from 'path';
import * as fs from 'fs';

/**
 * Supported platform types
 */
export enum PlatformType {
  WINDOWS = 'windows',
  MACOS = 'macos',
  LINUX = 'linux',
  UNKNOWN = 'unknown'
}

/**
 * Detect the current platform
 * @returns The detected platform type
 */
export function detectPlatform(): PlatformType {
  const platform = process.platform;
  
  if (platform === 'win32') return PlatformType.WINDOWS;
  if (platform === 'darwin') return PlatformType.MACOS;
  if (platform === 'linux') return PlatformType.LINUX;
  
  return PlatformType.UNKNOWN;
}

/**
 * Get the default shell for the current platform
 * @returns Path to the default shell
 */
export function getDefaultShell(): string {
  const platform = detectPlatform();
  
  switch (platform) {
    case PlatformType.WINDOWS:
      return process.env.COMSPEC || 'cmd.exe';
    case PlatformType.MACOS:
      return '/bin/zsh';
    case PlatformType.LINUX:
      return process.env.SHELL || '/bin/bash';
    default:
      return process.env.SHELL || '/bin/sh';
  }
}

/**
 * Validate if a shell path exists and is executable
 * @param shellPath Path to the shell
 * @returns True if the shell is valid
 */
export function validateShellPath(shellPath: string): boolean {
  try {
    return fs.existsSync(shellPath) && fs.statSync(shellPath).isFile();
  } catch (error) {
    return false;
  }
}

/**
 * Get shell suggestions for each platform
 * @returns Record of platform types to array of suggested shells
 */
export function getShellSuggestions(): Record<PlatformType, string[]> {
  return {
    [PlatformType.WINDOWS]: ['cmd.exe', 'powershell.exe', 'pwsh.exe'],
    [PlatformType.MACOS]: ['/bin/zsh', '/bin/bash', '/bin/sh'],
    [PlatformType.LINUX]: ['/bin/bash', '/bin/sh', '/bin/zsh'],
    [PlatformType.UNKNOWN]: ['/bin/sh']
  };
}

/**
 * Get common locations for shells on the current platform
 * @returns Array of common shell locations
 */
export function getCommonShellLocations(): string[] {
  const platform = detectPlatform();
  
  switch (platform) {
    case PlatformType.WINDOWS:
      return [
        process.env.COMSPEC || 'C:\\Windows\\System32\\cmd.exe',
        'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe',
        'C:\\Program Files\\PowerShell\\7\\pwsh.exe'
      ];
    case PlatformType.MACOS:
      return ['/bin/zsh', '/bin/bash', '/bin/sh'];
    case PlatformType.LINUX:
      return ['/bin/bash', '/bin/sh', '/usr/bin/bash', '/usr/bin/zsh'];
    default:
      return ['/bin/sh'];
  }
}

/**
 * Get helpful message for shell configuration
 * @returns A helpful message with shell configuration guidance
 */
export function getShellConfigurationHelp(): string {
  const platform = detectPlatform();
  const suggestions = getShellSuggestions()[platform];
  const locations = getCommonShellLocations();
  
  let message = 'Shell Configuration Help:\n\n';
  
  message += `Detected platform: ${platform}\n\n`;
  message += 'Suggested shells for this platform:\n';
  suggestions.forEach(shell => {
    message += `- ${shell}\n`;
  });
  
  message += '\nCommon shell locations on this platform:\n';
  locations.forEach(location => {
    message += `- ${location}\n`;
  });
  
  message += '\nTo configure a custom shell, provide the full path to the shell executable.';
  
  return message;
}

```

--------------------------------------------------------------------------------
/examples/client-example.js:
--------------------------------------------------------------------------------

```javascript
#!/usr/bin/env node
import { McpClient } from '@modelcontextprotocol/sdk/client/index.js';
import { ChildProcessClientTransport } from '@modelcontextprotocol/sdk/client/child-process.js';

/**
 * Example client for the Mac Shell MCP Server
 * This demonstrates how to connect to the server and use its tools
 */
async function main() {
  // Create a client transport that connects to the server
  const transport = new ChildProcessClientTransport({
    command: 'node',
    args: ['../build/index.js'],
  });

  // Create the MCP client
  const client = new McpClient();
  
  try {
    // Connect to the server
    await client.connect(transport);
    console.log('Connected to Mac Shell MCP Server');

    // List available tools
    const tools = await client.listTools();
    console.log('Available tools:');
    tools.forEach(tool => {
      console.log(`- ${tool.name}: ${tool.description}`);
    });

    // Get the whitelist
    console.log('\nWhitelisted commands:');
    const whitelist = await client.callTool('get_whitelist', {});
    console.log(whitelist.content[0].text);

    // Execute a safe command
    console.log('\nExecuting a safe command (ls -la):');
    const lsResult = await client.callTool('execute_command', {
      command: 'ls',
      args: ['-la'],
    });
    console.log(lsResult.content[0].text);

    // Add a command to the whitelist
    console.log('\nAdding a command to the whitelist:');
    const addResult = await client.callTool('add_to_whitelist', {
      command: 'node',
      securityLevel: 'safe',
      description: 'Execute Node.js scripts',
    });
    console.log(addResult.content[0].text);

    // Try executing a command that requires approval
    console.log('\nExecuting a command that requires approval (mkdir test-dir):');
    try {
      const mkdirResult = await client.callTool('execute_command', {
        command: 'mkdir',
        args: ['test-dir'],
      });
      console.log(mkdirResult.content[0].text);
    } catch (error) {
      console.log('Command requires approval. Getting pending commands...');
      
      // Get pending commands
      const pendingCommands = await client.callTool('get_pending_commands', {});
      const pendingCommandsObj = JSON.parse(pendingCommands.content[0].text);
      
      if (pendingCommandsObj.length > 0) {
        const commandId = pendingCommandsObj[0].id;
        console.log(`Approving command with ID: ${commandId}`);
        
        // Approve the command
        const approveResult = await client.callTool('approve_command', {
          commandId,
        });
        console.log(approveResult.content[0].text);
      }
    }

    // Clean up - remove the test directory
    console.log('\nCleaning up:');
    try {
      // This will fail because 'rm' is forbidden
      const rmResult = await client.callTool('execute_command', {
        command: 'rm',
        args: ['-rf', 'test-dir'],
      });
      console.log(rmResult.content[0].text);
    } catch (error) {
      console.log(`Clean-up failed: ${error.message}`);
      console.log('Note: This is expected because "rm" is a forbidden command');
    }

  } catch (error) {
    console.error('Error:', error);
  } finally {
    // Disconnect from the server
    await client.disconnect();
    console.log('\nDisconnected from Mac Shell MCP Server');
  }
}

main().catch(console.error);
```

--------------------------------------------------------------------------------
/docs/adr/004-cross-platform-support.md:
--------------------------------------------------------------------------------

```markdown
# ADR 004: Cross-Platform Shell Support

## Status

Accepted

## Context

The original Mac Shell MCP server was designed specifically for macOS with ZSH shell. However, there's a need to support multiple platforms (Windows, macOS, Linux) and various shells (Bash, ZSH, PowerShell, CMD, etc.) to make the tool more widely usable.

Key limitations in the original implementation:

1. **Shell Path Hardcoding**: The server was hardcoded to use `/bin/zsh` as the default shell
2. **Command Set Assumptions**: The default whitelist included macOS/Unix commands that don't exist natively on Windows
3. **Path Handling**: Command validation extracted the base command by splitting on '/' which doesn't work for Windows backslash paths
4. **Naming and Documentation**: The server was explicitly named "mac-shell-mcp" and documented for macOS

## Decision

We will refactor the server to be platform-agnostic with the following changes:

1. **Platform Detection**: Implement platform detection using `process.platform` to identify the current operating system
2. **Shell Selection**: Select appropriate default shell based on platform and allow shell path to be configurable
3. **Path Normalization**: Use Node.js `path` module for cross-platform path handling
4. **Platform-Specific Command Whitelists**: Implement separate command whitelists for each supported platform
5. **Rename and Rebrand**: Rename to "super-shell-mcp" and update documentation to reflect cross-platform support

## Consequences

### Positive

- Works across Windows, macOS, and Linux
- Supports various shells based on user preference
- Maintains the same security model across platforms
- Provides consistent experience regardless of platform
- Increases the potential user base by supporting multiple platforms

### Negative

- Increased complexity in command handling
- Need to maintain separate command whitelists for each platform
- Some commands may behave differently across platforms
- Testing becomes more complex, requiring validation on multiple platforms

## Implementation

The implementation uses a platform detection utility:

```typescript
export function detectPlatform(): PlatformType {
  const platform = process.platform;
  
  if (platform === 'win32') return PlatformType.WINDOWS;
  if (platform === 'darwin') return PlatformType.MACOS;
  if (platform === 'linux') return PlatformType.LINUX;
  
  return PlatformType.UNKNOWN;
}
```

Platform-specific shell detection:

```typescript
export function getDefaultShell(): string {
  const platform = detectPlatform();
  
  switch (platform) {
    case PlatformType.WINDOWS:
      return process.env.COMSPEC || 'cmd.exe';
    case PlatformType.MACOS:
      return '/bin/zsh';
    case PlatformType.LINUX:
      return process.env.SHELL || '/bin/bash';
    default:
      return process.env.SHELL || '/bin/sh';
  }
}
```

Platform-specific command whitelists:

```typescript
private initializeDefaultWhitelist(): void {
  const platformCommands = getPlatformSpecificCommands();
  
  platformCommands.forEach(entry => {
    this.whitelist.set(entry.command, entry);
  });
}
```

Cross-platform path handling:

```typescript
private validateCommand(command: string, args: string[]): CommandSecurityLevel | null {
  // Extract the base command (without path) using path.basename
  const baseCommand = path.basename(command);
  
  // Check if the command is in the whitelist
  const entry = this.whitelist.get(baseCommand);
  if (!entry) {
    return null;
  }
  
  // Rest of validation...
}
```

--------------------------------------------------------------------------------
/tests/command-service.platform.test.js:
--------------------------------------------------------------------------------

```javascript
const { CommandService, CommandSecurityLevel } = require('../build/services/command-service.js');
const { detectPlatform, PlatformType } = require('../build/utils/platform-utils.js');

describe('CommandService Platform Tests', () => {
  let commandService;
  const currentPlatform = detectPlatform();

  beforeEach(() => {
    // Create a new CommandService instance for each test with auto-detected shell
    commandService = new CommandService();
  });

  test('should initialize with platform-specific whitelist', () => {
    const whitelist = commandService.getWhitelist();
    expect(whitelist).toBeDefined();
    expect(whitelist.length).toBeGreaterThan(0);
    
    // Check for common command across all platforms
    const echoCommand = whitelist.find(entry => entry.command === 'echo');
    expect(echoCommand).toBeDefined();
    expect(echoCommand.securityLevel).toBe(CommandSecurityLevel.SAFE);
    
    // Platform-specific command checks
    if (currentPlatform === PlatformType.WINDOWS) {
      // Windows-specific commands
      const dirCommand = whitelist.find(entry => entry.command === 'dir');
      expect(dirCommand).toBeDefined();
      expect(dirCommand.securityLevel).toBe(CommandSecurityLevel.SAFE);
      
      const delCommand = whitelist.find(entry => entry.command === 'del');
      expect(delCommand).toBeDefined();
      expect(delCommand.securityLevel).toBe(CommandSecurityLevel.FORBIDDEN);
    } else {
      // Unix-like platforms (macOS, Linux)
      const lsCommand = whitelist.find(entry => entry.command === 'ls');
      expect(lsCommand).toBeDefined();
      expect(lsCommand.securityLevel).toBe(CommandSecurityLevel.SAFE);
      
      const rmCommand = whitelist.find(entry => entry.command === 'rm');
      expect(rmCommand).toBeDefined();
      expect(rmCommand.securityLevel).toBe(CommandSecurityLevel.FORBIDDEN);
    }
  });

  test('should execute platform-specific safe command', async () => {
    // Choose a command based on platform
    const command = currentPlatform === PlatformType.WINDOWS ? 'echo' : 'echo';
    const args = ['test'];
    
    const result = await commandService.executeCommand(command, args);
    
    expect(result).toBeDefined();
    expect(result.stdout.trim()).toBe('test');
  });

  test('should reject platform-specific forbidden command', async () => {
    // Choose a forbidden command based on platform
    const command = currentPlatform === PlatformType.WINDOWS ? 'del' : 'rm';
    const args = currentPlatform === PlatformType.WINDOWS ? ['test.txt'] : ['-rf', 'test'];
    
    await expect(commandService.executeCommand(command, args)).rejects.toThrow();
  });

  test('should queue platform-specific command requiring approval', async () => {
    // Set up event listener to capture pending command
    let pendingCommandId = null;
    commandService.on('command:pending', (pendingCommand) => {
      pendingCommandId = pendingCommand.id;
    });
    
    // Choose a command requiring approval based on platform
    // Use a command that doesn't actually create anything to avoid test failures
    const command = currentPlatform === PlatformType.WINDOWS ? 'copy' : 'cp';
    const args = ['nonexistent-file', 'nonexistent-copy'];
    
    // Execute a command that requires approval
    const executePromise = commandService.executeCommand(command, args);
    
    // Wait a bit for the event to fire
    await new Promise(resolve => setTimeout(resolve, 100));
    
    // Check if we got a pending command
    expect(pendingCommandId).not.toBeNull();
    
    // Get pending commands
    const pendingCommands = commandService.getPendingCommands();
    expect(pendingCommands.length).toBe(1);
    expect(pendingCommands[0].id).toBe(pendingCommandId);
    
    // Approve the command
    const approvePromise = commandService.approveCommand(pendingCommandId);
    
    try {
      // Wait for both promises to resolve
      await Promise.all([executePromise, approvePromise]);
    } catch (error) {
      // Expect an error since we're trying to copy a non-existent file
      // This is expected and we can ignore it
    }
    
    // Check that there are no more pending commands
    expect(commandService.getPendingCommands().length).toBe(0);
  });

  test('should deny platform-specific command requiring approval', async () => {
    // Set up event listener to capture pending command
    let pendingCommandId = null;
    commandService.on('command:pending', (pendingCommand) => {
      pendingCommandId = pendingCommand.id;
    });
    
    // Choose a command requiring approval based on platform
    const command = currentPlatform === PlatformType.WINDOWS ? 'mkdir' : 'mkdir';
    const args = ['test-dir'];
    
    // Execute a command that requires approval
    const executePromise = commandService.executeCommand(command, args);
    
    // Wait a bit for the event to fire
    await new Promise(resolve => setTimeout(resolve, 100));
    
    // Check if we got a pending command
    expect(pendingCommandId).not.toBeNull();
    
    // Deny the command
    commandService.denyCommand(pendingCommandId, 'Test denial');
    
    // The execute promise should be rejected
    await expect(executePromise).rejects.toThrow('Test denial');
    
    // Check that there are no more pending commands
    expect(commandService.getPendingCommands().length).toBe(0);
  });
});
```

--------------------------------------------------------------------------------
/tests/command-service.test.js:
--------------------------------------------------------------------------------

```javascript
const { CommandService, CommandSecurityLevel } = require('../build/services/command-service.js');
const { getDefaultShell } = require('../build/utils/platform-utils.js');

describe('CommandService', () => {
  let commandService;

  beforeEach(() => {
    // Create a new CommandService instance for each test with auto-detected shell
    commandService = new CommandService();
  });

  test('should initialize with default whitelist', () => {
    const whitelist = commandService.getWhitelist();
    expect(whitelist).toBeDefined();
    expect(whitelist.length).toBeGreaterThan(0);
    
    // Check if common commands are in the whitelist
    const lsCommand = whitelist.find(entry => entry.command === 'ls');
    expect(lsCommand).toBeDefined();
    expect(lsCommand.securityLevel).toBe(CommandSecurityLevel.SAFE);
    
    const rmCommand = whitelist.find(entry => entry.command === 'rm');
    expect(rmCommand).toBeDefined();
    expect(rmCommand.securityLevel).toBe(CommandSecurityLevel.FORBIDDEN);
  });

  test('should add command to whitelist', () => {
    const testCommand = {
      command: 'test-command',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Test command'
    };
    
    commandService.addToWhitelist(testCommand);
    
    const whitelist = commandService.getWhitelist();
    const addedCommand = whitelist.find(entry => entry.command === 'test-command');
    
    expect(addedCommand).toBeDefined();
    expect(addedCommand.securityLevel).toBe(CommandSecurityLevel.SAFE);
    expect(addedCommand.description).toBe('Test command');
  });

  test('should update command security level', () => {
    // First add a command
    const testCommand = {
      command: 'test-command',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Test command'
    };
    
    commandService.addToWhitelist(testCommand);
    
    // Then update its security level
    commandService.updateSecurityLevel('test-command', CommandSecurityLevel.REQUIRES_APPROVAL);
    
    const whitelist = commandService.getWhitelist();
    const updatedCommand = whitelist.find(entry => entry.command === 'test-command');
    
    expect(updatedCommand).toBeDefined();
    expect(updatedCommand.securityLevel).toBe(CommandSecurityLevel.REQUIRES_APPROVAL);
  });

  test('should remove command from whitelist', () => {
    // First add a command
    const testCommand = {
      command: 'test-command',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Test command'
    };
    
    commandService.addToWhitelist(testCommand);
    
    // Then remove it
    commandService.removeFromWhitelist('test-command');
    
    const whitelist = commandService.getWhitelist();
    const removedCommand = whitelist.find(entry => entry.command === 'test-command');
    
    expect(removedCommand).toBeUndefined();
  });

  test('should execute safe command', async () => {
    // Execute a safe command (echo)
    const result = await commandService.executeCommand('echo', ['test']);
    
    expect(result).toBeDefined();
    expect(result.stdout.trim()).toBe('test');
  });

  test('should reject forbidden command', async () => {
    // Try to execute a forbidden command (rm)
    await expect(commandService.executeCommand('rm', ['-rf', 'test'])).rejects.toThrow();
  });

  test('should queue command requiring approval', async () => {
    // Set up event listener to capture pending command
    let pendingCommandId = null;
    commandService.on('command:pending', (pendingCommand) => {
      pendingCommandId = pendingCommand.id;
    });
    
    // Execute a command that requires approval
    // Use a command that doesn't actually create anything to avoid test failures
    const executePromise = commandService.executeCommand('cp', ['nonexistent-file', 'nonexistent-copy']);
    
    // Wait a bit for the event to fire
    await new Promise(resolve => setTimeout(resolve, 100));
    
    // Check if we got a pending command
    expect(pendingCommandId).not.toBeNull();
    
    // Get pending commands
    const pendingCommands = commandService.getPendingCommands();
    expect(pendingCommands.length).toBe(1);
    expect(pendingCommands[0].id).toBe(pendingCommandId);
    
    // Approve the command
    const approvePromise = commandService.approveCommand(pendingCommandId);
    
    try {
      // Wait for both promises to resolve
      await Promise.all([executePromise, approvePromise]);
    } catch (error) {
      // Expect an error since we're trying to copy a non-existent file
      // This is expected and we can ignore it
    }
    
    // Check that there are no more pending commands
    expect(commandService.getPendingCommands().length).toBe(0);
    
    // Clean up
    try {
      await commandService.executeCommand('rmdir', ['test-dir']);
    } catch (error) {
      // Ignore cleanup errors
    }
  });

  test('should deny command requiring approval', async () => {
    // Set up event listener to capture pending command
    let pendingCommandId = null;
    commandService.on('command:pending', (pendingCommand) => {
      pendingCommandId = pendingCommand.id;
    });
    
    // Execute a command that requires approval (mkdir)
    const executePromise = commandService.executeCommand('mkdir', ['test-dir']);
    
    // Wait a bit for the event to fire
    await new Promise(resolve => setTimeout(resolve, 100));
    
    // Check if we got a pending command
    expect(pendingCommandId).not.toBeNull();
    
    // Deny the command
    commandService.denyCommand(pendingCommandId, 'Test denial');
    
    // The execute promise should be rejected
    await expect(executePromise).rejects.toThrow('Test denial');
    
    // Check that there are no more pending commands
    expect(commandService.getPendingCommands().length).toBe(0);
  });
});
```

--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------

```markdown
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.0.13] - 2025-03-14

### Fixed
 - Updated the package.json file to include the proper github repo information

## [2.0.12] - 2025-03-14

### Changed
- Converted project from CommonJS to ESM module system
- Updated TypeScript configuration to use NodeNext module system
- Added ESM-compatible workaround for `__dirname` in ESM context

### Fixed
- Fixed Jest test suite to work with ESM modules
- Added CommonJS-compatible mock modules for testing
- Updated module imports to use ESM syntax with .js extensions

## [2.0.11] - 2025-03-14

### Added
- Published package to npm at https://www.npmjs.com/package/super-shell-mcp
- Updated README.md to highlight NPX installation method as the recommended approach
- Added benefits of using NPX in documentation
- Enhanced configuration examples for easier setup

### Changed
- Reorganized documentation to prioritize NPX installation method
- Simplified GitHub installation instructions
- Improved package publishing documentation

## [2.0.10] - 2025-03-14

### Added
- Added logs directory with .gitkeep file to ensure logs directory is tracked by Git
- Enhanced error handling for command approval process
- Improved logging for command execution and approval workflow

### Fixed
- Fixed version inconsistencies across package.json, package-lock.json, and src/index.ts
- Improved documentation for logging system
- Enhanced cross-platform compatibility for log file paths

## [2.0.9] - 2025-03-14

### Added
- Added additional logging for command approval workflow
- Improved error handling for command execution
- Enhanced debugging capabilities with more detailed logs

### Fixed
- Fixed minor issues with command approval workflow
- Improved reliability of command execution across platforms
- Enhanced error messages for better troubleshooting

## [2.0.8] - 2025-03-13

### Added
- Added comprehensive logging system with file-based logs
- Implemented non-blocking command approval workflow
- Added new `queueCommandForApprovalNonBlocking` method to CommandService

### Fixed
- Fixed timeout issue with commands requiring approval by implementing non-blocking approval workflow
- Improved user experience by providing immediate feedback for commands requiring approval
- Enhanced error handling for commands requiring approval
- Fixed issue where pending commands would cause client timeouts

## [2.0.7] - 2025-03-13

### Fixed
- Fixed timeout issue when using the "Approve" button in Roo Code client
- Improved error handling in `handleApproveCommand` method to bypass Promise resolution mechanism
- Added detailed logging for command approval process to aid debugging
- Enhanced direct command execution in approval workflow to prevent timeouts
- Fixed TypeScript errors related to error handling in command execution

## [2.0.6] - 2025-03-13

### Fixed
- Fixed command approval workflow to prevent timeout errors
- Added immediate detection of commands requiring approval
- Improved error messages for commands requiring approval with clear instructions
- Added direct guidance to use get_pending_commands and approve_command functions

## [2.0.5] - 2025-03-13

### Added
- Improved command approval workflow with timeout detection
- Added guidance for AI assistants when command approval times out
- Enhanced error messages for commands requiring approval

### Fixed
- Module compatibility issues between ES Modules and CommonJS in tests
- Updated TypeScript configuration to use CommonJS module system for better test compatibility
- Removed unnecessary CommonJS compatibility code from source files
- Changed package.json "type" from "module" to "commonjs" for consistent module system

## [2.0.4] - 2025-03-13

### Fixed
- Module compatibility issues between ES Modules and CommonJS in tests
- Updated TypeScript configuration to use CommonJS module system for better test compatibility
- Removed unnecessary CommonJS compatibility code from source files
- Changed package.json "type" from "module" to "commonjs" for consistent module system

## [2.0.3] - 2025-03-12

### Added
- NPX best practices documentation in README.md
- Improved package.json configuration for NPX compatibility

### Changed
- Updated TypeScript configuration to use NodeNext module system for better ES Modules support
- Added prepare script to ensure compilation happens on install
- Added files field to package.json to specify which files to include when publishing
- Simplified build script to use chmod directly instead of custom script

## [2.0.2] - 2025-03-12

### Fixed
- Test suite compatibility with cross-platform environments
- Module system compatibility between ESM and CommonJS
- Fixed platform-specific test cases to work on all operating systems
- Updated TypeScript configuration for better CommonJS compatibility
- Improved test reliability by using non-filesystem-modifying commands

## [2.0.1] - 2025-03-12

### Added
- Platform-aware test suite that adapts to the current operating system
- Cross-platform build script that works on Windows, macOS, and Linux
- Enhanced platform-specific documentation with configuration examples
- Troubleshooting guide for common cross-platform issues
- Detailed shell path examples for each supported platform

### Fixed
- Build script compatibility with Windows (removed Unix-specific chmod)
- Test suite compatibility with Windows command sets
- Path handling in shell validation for Windows paths

## [2.0.0] - 2025-03-12

### Added
- Cross-platform support for Windows, macOS, and Linux
- Platform detection using `process.platform`
- Auto-detection of appropriate shell based on platform
- Platform-specific command whitelists
- New `get_platform_info` tool to retrieve platform and shell information
- Support for Windows shells (cmd.exe, PowerShell)
- Support for Linux shells (bash, sh)
- New ADR for cross-platform support

### Changed
- Renamed from "mac-shell-mcp" to "super-shell-mcp"
- Updated path handling to use Node.js path module for cross-platform compatibility
- Modified command validation to work with Windows paths
- Updated documentation to reflect cross-platform support
- Refactored code to be platform-agnostic
- Made shell configurable with auto-detection as fallback

## [1.0.3] - 2025-03-12

### Fixed

- Improved documentation for Claude Desktop configuration which uses boolean value for `alwaysAllow`
- Added separate configuration examples for Roo Code and Claude Desktop
- Clarified that Roo Code uses array format while Claude Desktop uses boolean format
- Added explicit note that the `alwaysAllow` parameter is processed by the MCP client, not the server

## [1.0.2] - 2025-03-12

### Fixed

- Fixed MCP configuration format to use an empty array `[]` for `alwaysAllow` instead of `false`
- Updated all configuration examples in README.md to use the correct format
- Fixed error "Invalid config: missing or invalid parameters" when adding to MCP settings

## [1.0.1] - 2025-03-12

### Added

- Support for using the server as an npm package with npx
- Added bin field to package.json for CLI usage
- Improved MCP configuration instructions for Roo Code and Claude Desktop
- Added examples for using with npx directly from GitHub

## [1.0.0] - 2025-03-12

### Added

- Initial release of the Mac Shell MCP Server
- Command execution service with ZSH shell support
- Command whitelisting system with three security levels:
  - Safe commands (no approval required)
  - Commands requiring approval
  - Forbidden commands
- Pre-configured whitelist with common safe commands
- Approval workflow for potentially dangerous commands
- MCP tools for command execution and whitelist management:
  - `execute_command`: Execute shell commands
  - `get_whitelist`: Get the list of whitelisted commands
  - `add_to_whitelist`: Add a command to the whitelist
  - `update_security_level`: Update a command's security level
  - `remove_from_whitelist`: Remove a command from the whitelist
  - `get_pending_commands`: Get commands pending approval
  - `approve_command`: Approve a pending command
  - `deny_command`: Deny a pending command
- Comprehensive test suite for the command service
- Example client implementation
- Documentation and configuration examples
```

--------------------------------------------------------------------------------
/src/utils/command-whitelist-utils.ts:
--------------------------------------------------------------------------------

```typescript
import { CommandSecurityLevel, CommandWhitelistEntry } from '../services/command-service.js';
import { PlatformType, detectPlatform } from './platform-utils.js';

/**
 * Get common safe commands that work across all platforms
 * @returns Array of common safe command whitelist entries
 */
export function getCommonSafeCommands(): CommandWhitelistEntry[] {
  return [
    {
      command: 'echo',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Print text to standard output'
    }
  ];
}

/**
 * Get Windows-specific safe commands
 * @returns Array of Windows safe command whitelist entries
 */
export function getWindowsSafeCommands(): CommandWhitelistEntry[] {
  return [
    {
      command: 'dir',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'List directory contents'
    },
    {
      command: 'type',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Display the contents of a text file'
    },
    {
      command: 'cd',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Change directory'
    },
    {
      command: 'findstr',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Search for strings in files'
    },
    {
      command: 'where',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Locate programs'
    },
    {
      command: 'whoami',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Display current user'
    },
    {
      command: 'hostname',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Display computer name'
    },
    {
      command: 'ver',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Display operating system version'
    }
  ];
}

/**
 * Get macOS-specific safe commands
 * @returns Array of macOS safe command whitelist entries
 */
export function getMacOSSafeCommands(): CommandWhitelistEntry[] {
  return [
    {
      command: 'ls',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'List directory contents'
    },
    {
      command: 'pwd',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Print working directory'
    },
    {
      command: 'cat',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Concatenate and print files'
    },
    {
      command: 'grep',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Search for patterns in files'
    },
    {
      command: 'find',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Find files in a directory hierarchy'
    },
    {
      command: 'cd',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Change directory'
    },
    {
      command: 'head',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Output the first part of files'
    },
    {
      command: 'tail',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Output the last part of files'
    },
    {
      command: 'wc',
      securityLevel: CommandSecurityLevel.SAFE,
      description: 'Print newline, word, and byte counts'
    }
  ];
}

/**
 * Get Linux-specific safe commands
 * @returns Array of Linux safe command whitelist entries
 */
export function getLinuxSafeCommands(): CommandWhitelistEntry[] {
  // Linux safe commands are similar to macOS
  return getMacOSSafeCommands();
}

/**
 * Get Windows-specific commands requiring approval
 * @returns Array of Windows command whitelist entries requiring approval
 */
export function getWindowsApprovalCommands(): CommandWhitelistEntry[] {
  return [
    {
      command: 'copy',
      securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL,
      description: 'Copy files'
    },
    {
      command: 'move',
      securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL,
      description: 'Move files'
    },
    {
      command: 'mkdir',
      securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL,
      description: 'Create directories'
    },
    {
      command: 'rmdir',
      securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL,
      description: 'Remove directories'
    },
    {
      command: 'rename',
      securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL,
      description: 'Rename files'
    },
    {
      command: 'attrib',
      securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL,
      description: 'Change file attributes'
    }
  ];
}

/**
 * Get macOS-specific commands requiring approval
 * @returns Array of macOS command whitelist entries requiring approval
 */
export function getMacOSApprovalCommands(): CommandWhitelistEntry[] {
  return [
    {
      command: 'mv',
      securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL,
      description: 'Move (rename) files'
    },
    {
      command: 'cp',
      securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL,
      description: 'Copy files and directories'
    },
    {
      command: 'mkdir',
      securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL,
      description: 'Create directories'
    },
    {
      command: 'touch',
      securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL,
      description: 'Change file timestamps or create empty files'
    },
    {
      command: 'chmod',
      securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL,
      description: 'Change file mode bits'
    },
    {
      command: 'chown',
      securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL,
      description: 'Change file owner and group'
    }
  ];
}

/**
 * Get Linux-specific commands requiring approval
 * @returns Array of Linux command whitelist entries requiring approval
 */
export function getLinuxApprovalCommands(): CommandWhitelistEntry[] {
  // Linux approval commands are similar to macOS
  return getMacOSApprovalCommands();
}

/**
 * Get Windows-specific forbidden commands
 * @returns Array of Windows forbidden command whitelist entries
 */
export function getWindowsForbiddenCommands(): CommandWhitelistEntry[] {
  return [
    {
      command: 'del',
      securityLevel: CommandSecurityLevel.FORBIDDEN,
      description: 'Delete files'
    },
    {
      command: 'erase',
      securityLevel: CommandSecurityLevel.FORBIDDEN,
      description: 'Delete files'
    },
    {
      command: 'format',
      securityLevel: CommandSecurityLevel.FORBIDDEN,
      description: 'Format a disk'
    },
    {
      command: 'runas',
      securityLevel: CommandSecurityLevel.FORBIDDEN,
      description: 'Execute a program as another user'
    }
  ];
}

/**
 * Get macOS-specific forbidden commands
 * @returns Array of macOS forbidden command whitelist entries
 */
export function getMacOSForbiddenCommands(): CommandWhitelistEntry[] {
  return [
    {
      command: 'rm',
      securityLevel: CommandSecurityLevel.FORBIDDEN,
      description: 'Remove files or directories'
    },
    {
      command: 'sudo',
      securityLevel: CommandSecurityLevel.FORBIDDEN,
      description: 'Execute a command as another user'
    }
  ];
}

/**
 * Get Linux-specific forbidden commands
 * @returns Array of Linux forbidden command whitelist entries
 */
export function getLinuxForbiddenCommands(): CommandWhitelistEntry[] {
  // Linux forbidden commands are similar to macOS
  return getMacOSForbiddenCommands();
}

/**
 * Get platform-specific command whitelist entries
 * @returns Array of command whitelist entries for the current platform
 */
export function getPlatformSpecificCommands(): CommandWhitelistEntry[] {
  const platform = detectPlatform();
  
  let safeCommands: CommandWhitelistEntry[] = [];
  let approvalCommands: CommandWhitelistEntry[] = [];
  let forbiddenCommands: CommandWhitelistEntry[] = [];
  
  // Add common safe commands that work across all platforms
  const commonSafeCommands = getCommonSafeCommands();
  
  // Add platform-specific commands
  switch (platform) {
    case PlatformType.WINDOWS:
      safeCommands = getWindowsSafeCommands();
      approvalCommands = getWindowsApprovalCommands();
      forbiddenCommands = getWindowsForbiddenCommands();
      break;
    case PlatformType.MACOS:
      safeCommands = getMacOSSafeCommands();
      approvalCommands = getMacOSApprovalCommands();
      forbiddenCommands = getMacOSForbiddenCommands();
      break;
    case PlatformType.LINUX:
      safeCommands = getLinuxSafeCommands();
      approvalCommands = getLinuxApprovalCommands();
      forbiddenCommands = getLinuxForbiddenCommands();
      break;
    default:
      // Use Unix-like defaults for unknown platforms
      safeCommands = getLinuxSafeCommands();
      approvalCommands = getLinuxApprovalCommands();
      forbiddenCommands = getLinuxForbiddenCommands();
  }
  
  // Combine all commands
  return [...commonSafeCommands, ...safeCommands, ...approvalCommands, ...forbiddenCommands];
}

```

--------------------------------------------------------------------------------
/jest.setup.cjs:
--------------------------------------------------------------------------------

```
// Mock the ESM modules with CommonJS equivalents for Jest
const fs = require('fs');
const path = require('path');
const { EventEmitter } = require('events');
const { execFile } = require('child_process');
const { promisify } = require('util');
const { randomUUID } = require('crypto');

// Define the CommandSecurityLevel enum
const CommandSecurityLevel = {
  SAFE: 'safe',
  REQUIRES_APPROVAL: 'requires_approval',
  FORBIDDEN: 'forbidden'
};

// Define the PlatformType enum
const PlatformType = {
  WINDOWS: 'windows',
  MACOS: 'macos',
  LINUX: 'linux',
  UNKNOWN: 'unknown'
};

// Mock the platform-utils module
const detectPlatform = () => {
  const platform = process.platform;
  if (platform === 'win32') return PlatformType.WINDOWS;
  if (platform === 'darwin') return PlatformType.MACOS;
  if (platform === 'linux') return PlatformType.LINUX;
  return PlatformType.UNKNOWN;
};

const getDefaultShell = () => {
  const platform = detectPlatform();
  switch (platform) {
    case PlatformType.WINDOWS:
      return process.env.COMSPEC || 'cmd.exe';
    case PlatformType.MACOS:
      return '/bin/zsh';
    case PlatformType.LINUX:
      return process.env.SHELL || '/bin/bash';
    default:
      return process.env.SHELL || '/bin/sh';
  }
};

const validateShellPath = (shellPath) => {
  try {
    return fs.existsSync(shellPath) && fs.statSync(shellPath).isFile();
  } catch (error) {
    return false;
  }
};

const getShellSuggestions = () => ({
  [PlatformType.WINDOWS]: ['cmd.exe', 'powershell.exe', 'pwsh.exe'],
  [PlatformType.MACOS]: ['/bin/zsh', '/bin/bash', '/bin/sh'],
  [PlatformType.LINUX]: ['/bin/bash', '/bin/sh', '/bin/zsh'],
  [PlatformType.UNKNOWN]: ['/bin/sh']
});

const getCommonShellLocations = () => {
  const platform = detectPlatform();
  switch (platform) {
    case PlatformType.WINDOWS:
      return [
        process.env.COMSPEC || 'C:\\Windows\\System32\\cmd.exe',
        'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe',
        'C:\\Program Files\\PowerShell\\7\\pwsh.exe'
      ];
    case PlatformType.MACOS:
      return ['/bin/zsh', '/bin/bash', '/bin/sh'];
    case PlatformType.LINUX:
      return ['/bin/bash', '/bin/sh', '/usr/bin/bash', '/usr/bin/zsh'];
    default:
      return ['/bin/sh'];
  }
};

const getShellConfigurationHelp = () => {
  const platform = detectPlatform();
  const suggestions = getShellSuggestions()[platform];
  const locations = getCommonShellLocations();
  
  let message = 'Shell Configuration Help:\n\n';
  message += `Detected platform: ${platform}\n\n`;
  message += 'Suggested shells for this platform:\n';
  suggestions.forEach(shell => {
    message += `- ${shell}\n`;
  });
  
  message += '\nCommon shell locations on this platform:\n';
  locations.forEach(location => {
    message += `- ${location}\n`;
  });
  
  message += '\nTo configure a custom shell, provide the full path to the shell executable.';
  
  return message;
};

// Mock the CommandService class
class CommandService extends EventEmitter {
  constructor(shell, defaultTimeout = 30000) {
    super();
    this.shell = shell || getDefaultShell();
    this.whitelist = new Map();
    this.pendingCommands = new Map();
    this.defaultTimeout = defaultTimeout;
    this.initializeDefaultWhitelist();
  }

  getShell() {
    return this.shell;
  }

  initializeDefaultWhitelist() {
    const platform = detectPlatform();
    const commands = [];
    
    // Common commands for all platforms
    commands.push({ command: 'echo', securityLevel: CommandSecurityLevel.SAFE, description: 'Print text to standard output' });
    
    // Platform-specific commands
    if (platform === PlatformType.WINDOWS) {
      commands.push({ command: 'dir', securityLevel: CommandSecurityLevel.SAFE, description: 'List directory contents' });
      commands.push({ command: 'copy', securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL, description: 'Copy files' });
      commands.push({ command: 'mkdir', securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL, description: 'Create directories' });
      commands.push({ command: 'del', securityLevel: CommandSecurityLevel.FORBIDDEN, description: 'Delete files' });
    } else {
      commands.push({ command: 'ls', securityLevel: CommandSecurityLevel.SAFE, description: 'List directory contents' });
      commands.push({ command: 'cat', securityLevel: CommandSecurityLevel.SAFE, description: 'Concatenate and print files' });
      commands.push({ command: 'grep', securityLevel: CommandSecurityLevel.SAFE, description: 'Search for patterns in files' });
      commands.push({ command: 'find', securityLevel: CommandSecurityLevel.SAFE, description: 'Find files in a directory hierarchy' });
      commands.push({ command: 'cd', securityLevel: CommandSecurityLevel.SAFE, description: 'Change directory' });
      commands.push({ command: 'head', securityLevel: CommandSecurityLevel.SAFE, description: 'Output the first part of files' });
      commands.push({ command: 'tail', securityLevel: CommandSecurityLevel.SAFE, description: 'Output the last part of files' });
      commands.push({ command: 'wc', securityLevel: CommandSecurityLevel.SAFE, description: 'Print newline, word, and byte counts' });
      commands.push({ command: 'mv', securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL, description: 'Move (rename) files' });
      commands.push({ command: 'cp', securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL, description: 'Copy files and directories' });
      commands.push({ command: 'mkdir', securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL, description: 'Create directories' });
      commands.push({ command: 'touch', securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL, description: 'Change file timestamps or create empty files' });
      commands.push({ command: 'chmod', securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL, description: 'Change file mode bits' });
      commands.push({ command: 'chown', securityLevel: CommandSecurityLevel.REQUIRES_APPROVAL, description: 'Change file owner and group' });
      commands.push({ command: 'rm', securityLevel: CommandSecurityLevel.FORBIDDEN, description: 'Remove files or directories' });
      commands.push({ command: 'sudo', securityLevel: CommandSecurityLevel.FORBIDDEN, description: 'Execute a command as another user' });
    }
    
    commands.forEach(entry => {
      this.whitelist.set(entry.command, entry);
    });
  }

  addToWhitelist(entry) {
    this.whitelist.set(entry.command, entry);
  }

  removeFromWhitelist(command) {
    this.whitelist.delete(command);
  }

  updateSecurityLevel(command, securityLevel) {
    const entry = this.whitelist.get(command);
    if (entry) {
      entry.securityLevel = securityLevel;
      this.whitelist.set(command, entry);
    }
  }

  getWhitelist() {
    return Array.from(this.whitelist.values());
  }

  getPendingCommands() {
    return Array.from(this.pendingCommands.values());
  }

  validateCommand(command, args) {
    const baseCommand = path.basename(command);
    const entry = this.whitelist.get(baseCommand);
    
    if (!entry) {
      return null;
    }
    
    if (entry.securityLevel === CommandSecurityLevel.FORBIDDEN) {
      return CommandSecurityLevel.FORBIDDEN;
    }
    
    if (entry.allowedArgs && entry.allowedArgs.length > 0) {
      const allArgsValid = args.every((arg, index) => {
        if (index >= (entry.allowedArgs?.length || 0)) {
          return false;
        }
        
        const pattern = entry.allowedArgs?.[index];
        if (!pattern) {
          return false;
        }
        
        if (typeof pattern === 'string') {
          return arg === pattern;
        } else {
          return pattern.test(arg);
        }
      });
      
      if (!allArgsValid) {
        return CommandSecurityLevel.REQUIRES_APPROVAL;
      }
    }
    
    return entry.securityLevel;
  }

  async executeCommand(command, args = [], options = {}) {
    const securityLevel = this.validateCommand(command, args);
    
    if (securityLevel === null) {
      throw new Error(`Command not whitelisted: ${command}`);
    }
    
    if (securityLevel === CommandSecurityLevel.FORBIDDEN) {
      throw new Error(`Command is forbidden: ${command}`);
    }
    
    if (securityLevel === CommandSecurityLevel.REQUIRES_APPROVAL) {
      return this.queueCommandForApproval(command, args, options.requestedBy);
    }
    
    try {
      const timeout = options.timeout || this.defaultTimeout;
      const execFileAsync = promisify(execFile);
      const { stdout, stderr } = await execFileAsync(command, args, {
        timeout,
        shell: this.shell
      });
      
      return { stdout, stderr };
    } catch (error) {
      if (error instanceof Error) {
        throw new Error(`Command execution failed: ${error.message}`);
      }
      throw error;
    }
  }

  queueCommandForApproval(command, args = [], requestedBy) {
    return new Promise((resolve, reject) => {
      const id = randomUUID();
      const pendingCommand = {
        id,
        command,
        args,
        requestedAt: new Date(),
        requestedBy,
        resolve: (result) => resolve(result),
        reject: (error) => reject(error)
      };
      
      this.pendingCommands.set(id, pendingCommand);
      this.emit('command:pending', pendingCommand);
      
      setTimeout(() => {
        if (this.pendingCommands.has(id)) {
          this.emit('command:approval_timeout', {
            commandId: id,
            message: 'Command approval timed out. If you approved this command in the UI, please use get_pending_commands and approve_command to complete the process.'
          });
        }
      }, 5000);
    });
  }

  queueCommandForApprovalNonBlocking(command, args = [], requestedBy) {
    const id = randomUUID();
    const pendingCommand = {
      id,
      command,
      args,
      requestedAt: new Date(),
      requestedBy,
      resolve: () => {},
      reject: () => {}
    };
    
    this.pendingCommands.set(id, pendingCommand);
    this.emit('command:pending', pendingCommand);
    
    setTimeout(() => {
      if (this.pendingCommands.has(id)) {
        this.emit('command:approval_timeout', {
          commandId: id,
          message: 'Command approval timed out. If you approved this command in the UI, please use get_pending_commands and approve_command to complete the process.'
        });
      }
    }, 5000);
    
    return id;
  }

  async approveCommand(commandId) {
    const pendingCommand = this.pendingCommands.get(commandId);
    if (!pendingCommand) {
      throw new Error(`No pending command with ID: ${commandId}`);
    }
    
    try {
      const execFileAsync = promisify(execFile);
      const { stdout, stderr } = await execFileAsync(
        pendingCommand.command,
        pendingCommand.args,
        { shell: this.shell }
      );
      
      this.pendingCommands.delete(commandId);
      this.emit('command:approved', { commandId, stdout, stderr });
      pendingCommand.resolve({ stdout, stderr });
      
      return { stdout, stderr };
    } catch (error) {
      this.pendingCommands.delete(commandId);
      this.emit('command:failed', { commandId, error });
      
      if (error instanceof Error) {
        pendingCommand.reject(error);
        throw error;
      }
      
      const genericError = new Error('Command execution failed');
      pendingCommand.reject(genericError);
      throw genericError;
    }
  }

  denyCommand(commandId, reason = 'Command denied') {
    const pendingCommand = this.pendingCommands.get(commandId);
    if (!pendingCommand) {
      throw new Error(`No pending command with ID: ${commandId}`);
    }
    
    this.pendingCommands.delete(commandId);
    this.emit('command:denied', { commandId, reason });
    pendingCommand.reject(new Error(reason));
  }
}

// Export the mocked modules
module.exports = {
  CommandService,
  CommandSecurityLevel,
  detectPlatform,
  PlatformType,
  getDefaultShell,
  validateShellPath,
  getShellSuggestions,
  getCommonShellLocations,
  getShellConfigurationHelp
};
```

--------------------------------------------------------------------------------
/src/services/command-service.ts:
--------------------------------------------------------------------------------

```typescript
import { execFile } from 'child_process';
import { promisify } from 'util';
import { randomUUID } from 'crypto';
import { EventEmitter } from 'events';
import * as path from 'path';
import { getDefaultShell, validateShellPath, getShellConfigurationHelp } from '../utils/platform-utils.js';
import { getPlatformSpecificCommands } from '../utils/command-whitelist-utils.js';

const execFileAsync = promisify(execFile);

/**
 * Command security level classification
 */
export enum CommandSecurityLevel {
  /** Safe commands that can be executed without approval */
  SAFE = 'safe',
  /** Commands that require approval before execution */
  REQUIRES_APPROVAL = 'requires_approval',
  /** Commands that are explicitly forbidden */
  FORBIDDEN = 'forbidden'
}

/**
 * Command whitelist entry
 */
export interface CommandWhitelistEntry {
  /** The command path or name */
  command: string;
  /** Security level of the command */
  securityLevel: CommandSecurityLevel;
  /** Allowed arguments (string for exact match, RegExp for pattern match) */
  allowedArgs?: Array<string | RegExp>;
  /** Description of the command for documentation */
  description?: string;
}

/**
 * Pending command awaiting approval
 */
export interface PendingCommand {
  /** Unique ID for the command */
  id: string;
  /** The command to execute */
  command: string;
  /** Arguments for the command */
  args: string[];
  /** When the command was requested */
  requestedAt: Date;
  /** Who requested the command */
  requestedBy?: string;
  /** Resolve function to call when approved */
  resolve: (value: { stdout: string; stderr: string }) => void;
  /** Reject function to call when denied */
  reject: (reason: Error) => void;
}

/**
 * Result of command execution
 */
export interface CommandResult {
  /** Standard output from the command */
  stdout: string;
  /** Standard error from the command */
  stderr: string;
}

/**
 * Service for securely executing shell commands
 */
export class CommandService extends EventEmitter {
  /** Shell to use for commands */
  private shell: string;
  /** Command whitelist */
  private whitelist: Map<string, CommandWhitelistEntry>;
  /** Pending commands awaiting approval */
  private pendingCommands: Map<string, PendingCommand>;
  /** Default timeout for command execution in milliseconds */
  private defaultTimeout: number;

  /**
   * Create a new CommandService
   * @param shell The shell to use for commands (default: auto-detected based on platform)
   * @param defaultTimeout Default timeout for command execution in milliseconds (default: 30000)
   */
  constructor(shell?: string, defaultTimeout = 30000) {
    super();
    this.shell = shell || getDefaultShell();
    this.whitelist = new Map();
    this.pendingCommands = new Map();
    this.defaultTimeout = defaultTimeout;

    // Initialize with platform-specific commands
    this.initializeDefaultWhitelist();
  }

  /**
   * Get the current shell being used
   * @returns The shell path
   */
  public getShell(): string {
    return this.shell;
  }

  /**
   * Initialize the default command whitelist based on the current platform
   */
  private initializeDefaultWhitelist(): void {
    // Get platform-specific commands
    const platformCommands = getPlatformSpecificCommands();
    
    // Add all commands to the whitelist
    platformCommands.forEach(entry => {
      this.whitelist.set(entry.command, entry);
    });
  }

  /**
   * Add a command to the whitelist
   * @param entry The command whitelist entry
   */
  public addToWhitelist(entry: CommandWhitelistEntry): void {
    this.whitelist.set(entry.command, entry);
  }

  /**
   * Remove a command from the whitelist
   * @param command The command to remove
   */
  public removeFromWhitelist(command: string): void {
    this.whitelist.delete(command);
  }

  /**
   * Update a command's security level
   * @param command The command to update
   * @param securityLevel The new security level
   */
  public updateSecurityLevel(command: string, securityLevel: CommandSecurityLevel): void {
    const entry = this.whitelist.get(command);
    if (entry) {
      entry.securityLevel = securityLevel;
      this.whitelist.set(command, entry);
    }
  }

  /**
   * Get all whitelisted commands
   * @returns Array of command whitelist entries
   */
  public getWhitelist(): CommandWhitelistEntry[] {
    return Array.from(this.whitelist.values());
  }

  /**
   * Get all pending commands awaiting approval
   * @returns Array of pending commands
   */
  public getPendingCommands(): PendingCommand[] {
    return Array.from(this.pendingCommands.values());
  }

  /**
   * Validate if a command and its arguments are allowed
   * @param command The command to validate
   * @param args The command arguments
   * @returns The security level of the command or null if not whitelisted
   */
  private validateCommand(command: string, args: string[]): CommandSecurityLevel | null {
    // Extract the base command (without path) using path.basename
    const baseCommand = path.basename(command);
    
    // Check if the command is in the whitelist
    const entry = this.whitelist.get(baseCommand);
    if (!entry) {
      return null;
    }

    // If the command is forbidden, return immediately
    if (entry.securityLevel === CommandSecurityLevel.FORBIDDEN) {
      return CommandSecurityLevel.FORBIDDEN;
    }

    // If there are allowed arguments defined, validate them
    if (entry.allowedArgs && entry.allowedArgs.length > 0) {
      // Check if all arguments are allowed
      const allArgsValid = args.every((arg, index) => {
        // If we have more args than allowed patterns, reject
        if (index >= (entry.allowedArgs?.length || 0)) {
          return false;
        }

        const pattern = entry.allowedArgs?.[index];
        if (!pattern) {
          return false;
        }

        // Check if the argument matches the pattern
        if (typeof pattern === 'string') {
          return arg === pattern;
        } else {
          return pattern.test(arg);
        }
      });

      if (!allArgsValid) {
        return CommandSecurityLevel.REQUIRES_APPROVAL;
      }
    }

    return entry.securityLevel;
  }

  /**
   * Execute a shell command
   * @param command The command to execute
   * @param args Command arguments
   * @param options Additional options
   * @returns Promise resolving to command output
   */
  public async executeCommand(
    command: string,
    args: string[] = [],
    options: {
      timeout?: number;
      requestedBy?: string;
    } = {}
  ): Promise<CommandResult> {
    const securityLevel = this.validateCommand(command, args);

    // If command is not whitelisted, reject
    if (securityLevel === null) {
      throw new Error(`Command not whitelisted: ${command}`);
    }

    // If command is forbidden, reject
    if (securityLevel === CommandSecurityLevel.FORBIDDEN) {
      throw new Error(`Command is forbidden: ${command}`);
    }

    // If command requires approval, add to pending queue
    if (securityLevel === CommandSecurityLevel.REQUIRES_APPROVAL) {
      return this.queueCommandForApproval(command, args, options.requestedBy);
    }

    // For safe commands, execute immediately
    try {
      const timeout = options.timeout || this.defaultTimeout;
      const { stdout, stderr } = await execFileAsync(command, args, {
        timeout,
        shell: this.shell
      });

      return { stdout, stderr };
    } catch (error) {
      if (error instanceof Error) {
        throw new Error(`Command execution failed: ${error.message}`);
      }
      throw error;
    }
  }

  /**
   * Queue a command for approval
   * @param command The command to queue
   * @param args Command arguments
   * @param requestedBy Who requested the command
   * @returns Promise resolving when command is approved and executed
   */
  private queueCommandForApproval(
    command: string,
    args: string[] = [],
    requestedBy?: string
  ): Promise<CommandResult> {
    return new Promise((resolve, reject) => {
      const id = randomUUID();
      const pendingCommand: PendingCommand = {
        id,
        command,
        args,
        requestedAt: new Date(),
        requestedBy,
        resolve: (result: CommandResult) => resolve(result),
        reject: (error: Error) => reject(error)
      };

      this.pendingCommands.set(id, pendingCommand);
      
      // Emit event for pending command
      this.emit('command:pending', pendingCommand);
      
      // Set a timeout to check if the command is still pending after a while
      // This helps detect if the UI approval didn't properly trigger the approveCommand method
      setTimeout(() => {
        // If the command is still pending after the timeout
        if (this.pendingCommands.has(id)) {
          // Emit a warning event that can be handled by the client
          this.emit('command:approval_timeout', {
            commandId: id,
            message: 'Command approval timed out. If you approved this command in the UI, please use get_pending_commands and approve_command to complete the process.'
          });
        }
      }, 5000); // 5 second timeout to detect UI approval issues
    });
  }

  /**
   * Queue a command for approval without waiting for the Promise to resolve
   * @param command The command to queue
   * @param args Command arguments
   * @param requestedBy Who requested the command
   * @returns The ID of the queued command
   */
  public queueCommandForApprovalNonBlocking(
    command: string,
    args: string[] = [],
    requestedBy?: string
  ): string {
    const id = randomUUID();
    const pendingCommand: PendingCommand = {
      id,
      command,
      args,
      requestedAt: new Date(),
      requestedBy,
      resolve: () => {}, // No-op resolve function
      reject: () => {}   // No-op reject function
    };

    this.pendingCommands.set(id, pendingCommand);
    
    // Emit event for pending command
    this.emit('command:pending', pendingCommand);
    
    // Set a timeout to check if the command is still pending after a while
    setTimeout(() => {
      // If the command is still pending after the timeout
      if (this.pendingCommands.has(id)) {
        // Emit a warning event that can be handled by the client
        this.emit('command:approval_timeout', {
          commandId: id,
          message: 'Command approval timed out. If you approved this command in the UI, please use get_pending_commands and approve_command to complete the process.'
        });
      }
    }, 5000); // 5 second timeout to detect UI approval issues
    
    return id;
  }

  /**
   * Approve a pending command
   * @param commandId ID of the command to approve
   * @returns Promise resolving to command output
   */
  public async approveCommand(commandId: string): Promise<CommandResult> {
    const pendingCommand = this.pendingCommands.get(commandId);
    if (!pendingCommand) {
      throw new Error(`No pending command with ID: ${commandId}`);
    }

    try {
      const { stdout, stderr } = await execFileAsync(
        pendingCommand.command,
        pendingCommand.args,
        { shell: this.shell }
      );

      // Remove from pending queue
      this.pendingCommands.delete(commandId);
      
      // Emit event for approved command
      this.emit('command:approved', { commandId, stdout, stderr });
      
      // Resolve the original promise
      pendingCommand.resolve({ stdout, stderr });
      
      return { stdout, stderr };
    } catch (error) {
      // Remove from pending queue
      this.pendingCommands.delete(commandId);
      
      // Emit event for failed command
      this.emit('command:failed', { commandId, error });
      
      if (error instanceof Error) {
        // Reject the original promise
        pendingCommand.reject(error);
        throw error;
      }
      
      const genericError = new Error('Command execution failed');
      pendingCommand.reject(genericError);
      throw genericError;
    }
  }

  /**
   * Deny a pending command
   * @param commandId ID of the command to deny
   * @param reason Reason for denial
   */
  public denyCommand(commandId: string, reason: string = 'Command denied'): void {
    const pendingCommand = this.pendingCommands.get(commandId);
    if (!pendingCommand) {
      throw new Error(`No pending command with ID: ${commandId}`);
    }

    // Remove from pending queue
    this.pendingCommands.delete(commandId);
    
    // Emit event for denied command
    this.emit('command:denied', { commandId, reason });
    
    // Reject the original promise
    pendingCommand.reject(new Error(reason));
  }
}

```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ErrorCode,
  ListToolsRequestSchema,
  McpError,
} from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';
import * as path from 'path';
import * as fs from 'fs';
import { execFile } from 'child_process';
import { promisify } from 'util';
import { randomUUID } from 'crypto';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import { CommandService, CommandSecurityLevel } from './services/command-service.js';
import { getLogger, Logger } from './utils/logger.js';

// In ESM, __dirname is not available directly, so we create it
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const execFileAsync = promisify(execFile);
// Initialize the logger
// Use __dirname to get the directory of the current file
const LOG_FILE = path.join(__dirname, '../logs/super-shell-mcp.log');
console.error(`Log file path: ${LOG_FILE}`);
const logger = getLogger(LOG_FILE, true);

/**
 * SuperShellMcpServer - MCP server for executing shell commands across multiple platforms
 */
class SuperShellMcpServer {
  private server: Server;
  private commandService: CommandService;
  private pendingApprovals: Map<string, { command: string; args: string[] }>;

  constructor(options?: { shell?: string }) {
    // Initialize the command service with auto-detected or specified shell
    this.commandService = new CommandService(options?.shell);
    this.pendingApprovals = new Map();

    // Initialize the MCP server
    this.server = new Server(
      {
        name: 'super-shell-mcp',
        version: '2.0.13',
      },
      {
        capabilities: {
          tools: {},
        },
      }
    );

    // Set up event handlers for command service
    this.setupCommandServiceEvents();

    // Set up MCP request handlers
    this.setupRequestHandlers();
    
    // Error handling
    this.server.onerror = (error) => {
      logger.error(`[MCP Error] ${error}`);
      console.error('[MCP Error]', error);
    };
    
    process.on('SIGINT', async () => {
      logger.info('Received SIGINT signal, shutting down');
      await this.server.close();
      logger.info('Server closed, exiting process');
      logger.close();
      process.exit(0);
    });
  }

  /**
   * Set up event handlers for the command service
   */
  private setupCommandServiceEvents(): void {
    this.commandService.on('command:pending', (pendingCommand) => {
      logger.info(`[Pending Command] ID: ${pendingCommand.id}, Command: ${pendingCommand.command} ${pendingCommand.args.join(' ')}`);
      console.error(`[Pending Command] ID: ${pendingCommand.id}, Command: ${pendingCommand.command} ${pendingCommand.args.join(' ')}`);
      this.pendingApprovals.set(pendingCommand.id, {
        command: pendingCommand.command,
        args: pendingCommand.args,
      });
    });

    this.commandService.on('command:approved', (data) => {
      logger.info(`[Approved Command] ID: ${data.commandId}`);
      console.error(`[Approved Command] ID: ${data.commandId}`);
      this.pendingApprovals.delete(data.commandId);
    });

    this.commandService.on('command:denied', (data) => {
      logger.info(`[Denied Command] ID: ${data.commandId}, Reason: ${data.reason}`);
      console.error(`[Denied Command] ID: ${data.commandId}, Reason: ${data.reason}`);
      this.pendingApprovals.delete(data.commandId);
    });

    this.commandService.on('command:failed', (data) => {
      logger.error(`[Failed Command] ID: ${data.commandId}, Error: ${data.error.message}`);
      console.error(`[Failed Command] ID: ${data.commandId}, Error: ${data.error.message}`);
      this.pendingApprovals.delete(data.commandId);
    });
    
    // Handle approval timeout events
    this.commandService.on('command:approval_timeout', (data) => {
      logger.error(`[Approval Timeout] ID: ${data.commandId}, Message: ${data.message}`);
      console.error(`[Approval Timeout] ID: ${data.commandId}, Message: ${data.message}`);
      // Log the timeout but keep the command in the pending queue
      // The AI assistant will need to use get_pending_commands and approve_command to proceed
    });
  }

  /**
   * Set up MCP request handlers
   */
  private setupRequestHandlers(): void {
    // List available tools
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'get_platform_info',
          description: 'Get information about the current platform and shell',
          inputSchema: {
            type: 'object',
            properties: {},
          },
        },
        {
          name: 'execute_command',
          description: 'Execute a shell command on the current platform',
          inputSchema: {
            type: 'object',
            properties: {
              command: {
                type: 'string',
                description: 'The command to execute',
              },
              args: {
                type: 'array',
                items: {
                  type: 'string',
                },
                description: 'Command arguments',
              },
            },
            required: ['command'],
          },
        },
        {
          name: 'get_whitelist',
          description: 'Get the list of whitelisted commands',
          inputSchema: {
            type: 'object',
            properties: {},
          },
        },
        {
          name: 'add_to_whitelist',
          description: 'Add a command to the whitelist',
          inputSchema: {
            type: 'object',
            properties: {
              command: {
                type: 'string',
                description: 'The command to whitelist',
              },
              securityLevel: {
                type: 'string',
                enum: ['safe', 'requires_approval', 'forbidden'],
                description: 'Security level for the command',
              },
              description: {
                type: 'string',
                description: 'Description of the command',
              },
            },
            required: ['command', 'securityLevel'],
          },
        },
        {
          name: 'update_security_level',
          description: 'Update the security level of a whitelisted command',
          inputSchema: {
            type: 'object',
            properties: {
              command: {
                type: 'string',
                description: 'The command to update',
              },
              securityLevel: {
                type: 'string',
                enum: ['safe', 'requires_approval', 'forbidden'],
                description: 'New security level for the command',
              },
            },
            required: ['command', 'securityLevel'],
          },
        },
        {
          name: 'remove_from_whitelist',
          description: 'Remove a command from the whitelist',
          inputSchema: {
            type: 'object',
            properties: {
              command: {
                type: 'string',
                description: 'The command to remove from whitelist',
              },
            },
            required: ['command'],
          },
        },
        {
          name: 'get_pending_commands',
          description: 'Get the list of commands pending approval',
          inputSchema: {
            type: 'object',
            properties: {},
          },
        },
        {
          name: 'approve_command',
          description: 'Approve a pending command',
          inputSchema: {
            type: 'object',
            properties: {
              commandId: {
                type: 'string',
                description: 'ID of the command to approve',
              },
            },
            required: ['commandId'],
          },
        },
        {
          name: 'deny_command',
          description: 'Deny a pending command',
          inputSchema: {
            type: 'object',
            properties: {
              commandId: {
                type: 'string',
                description: 'ID of the command to deny',
              },
              reason: {
                type: 'string',
                description: 'Reason for denial',
              },
            },
            required: ['commandId'],
          },
        },
      ],
    }));

    // Handle tool calls
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const { name, arguments: args } = request.params;

      try {
        switch (name) {
          case 'get_platform_info':
            return await this.handleGetPlatformInfo();
          case 'execute_command':
            return await this.handleExecuteCommand(args);
          case 'get_whitelist':
            return await this.handleGetWhitelist();
          case 'add_to_whitelist':
            return await this.handleAddToWhitelist(args);
          case 'update_security_level':
            return await this.handleUpdateSecurityLevel(args);
          case 'remove_from_whitelist':
            return await this.handleRemoveFromWhitelist(args);
          case 'get_pending_commands':
            return await this.handleGetPendingCommands();
          case 'approve_command':
            return await this.handleApproveCommand(args);
          case 'deny_command':
            return await this.handleDenyCommand(args);
          default:
            throw new McpError(
              ErrorCode.MethodNotFound,
              `Unknown tool: ${name}`
            );
        }
      } catch (error) {
        if (error instanceof McpError) {
          throw error;
        }
        
        if (error instanceof Error) {
          return {
            content: [
              {
                type: 'text',
                text: `Error: ${error.message}`,
              },
            ],
            isError: true,
          };
        }
        
        throw new McpError(
          ErrorCode.InternalError,
          'An unexpected error occurred'
        );
      }
    });
  }

  /**
   * Handle execute_command tool
   */
  private async handleExecuteCommand(args: any) {
    const schema = z.object({
      command: z.string(),
      args: z.array(z.string()).optional(),
    });

    // Log the start of command execution
    logger.debug(`handleExecuteCommand called with args: ${JSON.stringify(args)}`);

    const { command, args: commandArgs = [] } = schema.parse(args);

    // Extract the base command (without path)
    const baseCommand = path.basename(command);
    
    logger.debug(`[Executing Command] Command: ${command} ${commandArgs.join(' ')}`);
    logger.debug(`Base command: ${baseCommand}`);
    
    // Check if the command requires approval before attempting execution
    const whitelist = this.commandService.getWhitelist();
    logger.debug(`Whitelist entries: ${whitelist.length}`);
    
    const whitelistEntry = whitelist.find(entry => entry.command === baseCommand);
    logger.debug(`Whitelist entry found: ${whitelistEntry ? 'yes' : 'no'}`);
    
    if (whitelistEntry) {
      logger.debug(`Security level: ${whitelistEntry.securityLevel}`);
    }
    
    if (whitelistEntry && whitelistEntry.securityLevel === CommandSecurityLevel.REQUIRES_APPROVAL) {
      logger.debug(`[Command Requires Approval] Command: ${command} ${commandArgs.join(' ')}`);
      
      // Use the non-blocking method to queue the command for approval
      const commandId = this.commandService.queueCommandForApprovalNonBlocking(command, commandArgs);
      logger.debug(`Command queued for approval with ID: ${commandId}`);
      
      // Return immediately with instructions for approval
      logger.debug(`Returning response to client`);
      return {
        content: [
          {
            type: 'text',
            text: `This command requires approval. It has been queued with ID: ${commandId}\n\nPlease approve this command in the UI or use the 'approve_command' function with this command ID.`,
          },
        ],
        isError: false, // Not an error, just needs approval
      };
    }
    
    // For safe commands or forbidden commands, use the normal execution path
    try {
      // Use the CommandService's executeCommand method
      const result = await this.commandService.executeCommand(command, commandArgs);
      
      return {
        content: [
          {
            type: 'text',
            text: result.stdout,
          },
          {
            type: 'text',
            text: result.stderr ? `Error output: ${result.stderr}` : '',
          },
        ],
      };
    } catch (error: unknown) {
      const errorMessage = error instanceof Error
        ? error.message
        : 'Unknown error occurred';
      
      console.error(`[Command Execution Failed] Error: ${errorMessage}`);
      
      return {
        content: [
          {
            type: 'text',
            text: `Command execution failed: ${errorMessage}`,
          },
        ],
        isError: true,
      };
    }
  }

  /**
   * Handle get_whitelist tool
   */
  private async handleGetWhitelist() {
    const whitelist = this.commandService.getWhitelist();
    
    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify(whitelist, null, 2),
        },
      ],
    };
  }

  /**
   * Handle add_to_whitelist tool
   */
  private async handleAddToWhitelist(args: any) {
    const schema = z.object({
      command: z.string(),
      securityLevel: z.enum(['safe', 'requires_approval', 'forbidden']),
      description: z.string().optional(),
    });

    const { command, securityLevel, description } = schema.parse(args);

    // Map string security level to enum
    const securityLevelEnum = securityLevel === 'safe'
      ? CommandSecurityLevel.SAFE
      : securityLevel === 'requires_approval'
        ? CommandSecurityLevel.REQUIRES_APPROVAL
        : CommandSecurityLevel.FORBIDDEN;

    this.commandService.addToWhitelist({
      command,
      securityLevel: securityLevelEnum,
      description,
    });

    return {
      content: [
        {
          type: 'text',
          text: `Command '${command}' added to whitelist with security level '${securityLevel}'`,
        },
      ],
    };
  }

  /**
   * Handle update_security_level tool
   */
  private async handleUpdateSecurityLevel(args: any) {
    const schema = z.object({
      command: z.string(),
      securityLevel: z.enum(['safe', 'requires_approval', 'forbidden']),
    });

    const { command, securityLevel } = schema.parse(args);

    // Map string security level to enum
    const securityLevelEnum = securityLevel === 'safe'
      ? CommandSecurityLevel.SAFE
      : securityLevel === 'requires_approval'
        ? CommandSecurityLevel.REQUIRES_APPROVAL
        : CommandSecurityLevel.FORBIDDEN;

    this.commandService.updateSecurityLevel(command, securityLevelEnum);

    return {
      content: [
        {
          type: 'text',
          text: `Security level for command '${command}' updated to '${securityLevel}'`,
        },
      ],
    };
  }

  /**
   * Handle remove_from_whitelist tool
   */
  private async handleRemoveFromWhitelist(args: any) {
    const schema = z.object({
      command: z.string(),
    });

    const { command } = schema.parse(args);

    this.commandService.removeFromWhitelist(command);

    return {
      content: [
        {
          type: 'text',
          text: `Command '${command}' removed from whitelist`,
        },
      ],
    };
  }

  /**
   * Handle get_platform_info tool
   */
  private async handleGetPlatformInfo() {
    const { detectPlatform, getDefaultShell, getShellSuggestions, getCommonShellLocations } = await import('./utils/platform-utils.js');
    
    const platform = detectPlatform();
    const currentShell = this.commandService.getShell();
    const suggestedShells = getShellSuggestions()[platform];
    const commonLocations = getCommonShellLocations();
    
    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify({
            platform,
            currentShell,
            suggestedShells,
            commonLocations,
            helpMessage: `Super Shell MCP is running on ${platform} using ${currentShell}`
          }, null, 2),
        },
      ],
    };
  }

  /**
   * Handle get_pending_commands tool
   */
  private async handleGetPendingCommands() {
    const pendingCommands = this.commandService.getPendingCommands();
    
    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify(pendingCommands.map(cmd => ({
            id: cmd.id,
            command: cmd.command,
            args: cmd.args,
            requestedAt: cmd.requestedAt,
            requestedBy: cmd.requestedBy,
          })), null, 2),
        },
      ],
    };
  }

  /**
   * Handle approve_command tool
   */
  private async handleApproveCommand(args: any) {
    const schema = z.object({
      commandId: z.string(),
    });

    logger.debug(`handleApproveCommand called with args: ${JSON.stringify(args)}`);

    const { commandId } = schema.parse(args);

    // Log the approval attempt
    logger.debug(`[Approval Attempt] ID: ${commandId}`);

    // Check if the command exists in our local pending approvals map
    const localPending = this.pendingApprovals.has(commandId);
    logger.debug(`Command found in local pendingApprovals: ${localPending ? 'yes' : 'no'}`);

    // Check if the command exists in the CommandService's pending queue
    const pendingCommands = this.commandService.getPendingCommands();
    logger.debug(`CommandService pending commands: ${pendingCommands.length}`);
    const pendingCommand = pendingCommands.find(cmd => cmd.id === commandId);
    logger.debug(`Command found in CommandService pending queue: ${pendingCommand ? 'yes' : 'no'}`);
    
    if (pendingCommand) {
      logger.debug(`Pending command details: ${JSON.stringify({
        id: pendingCommand.id,
        command: pendingCommand.command,
        args: pendingCommand.args,
        requestedAt: pendingCommand.requestedAt
      })}`);
    }

    try {
      logger.debug(`Calling CommandService.approveCommand with ID: ${commandId}`);
      // Use the CommandService's approveCommand method directly
      const result = await this.commandService.approveCommand(commandId);
      
      logger.debug(`[Command Approved] ID: ${commandId}, Output length: ${result.stdout.length}`);
      logger.debug(`Command output: ${result.stdout.substring(0, 100)}${result.stdout.length > 100 ? '...' : ''}`);
      
      return {
        content: [
          {
            type: 'text',
            text: `Command approved and executed successfully.\nOutput: ${result.stdout}`,
          },
          {
            type: 'text',
            text: result.stderr ? `Error output: ${result.stderr}` : '',
          },
        ],
      };
    } catch (error) {
      logger.error(`[Approval Error] ID: ${commandId}, Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
      
      if (error instanceof Error) {
        return {
          content: [
            {
              type: 'text',
              text: `Command approval failed: ${error.message}`,
            },
          ],
          isError: true,
        };
      }
      throw error;
    }
  }

  /**
   * Handle deny_command tool
   */
  private async handleDenyCommand(args: any) {
    const schema = z.object({
      commandId: z.string(),
      reason: z.string().optional(),
    });

    logger.debug(`handleDenyCommand called with args: ${JSON.stringify(args)}`);

    const { commandId, reason } = schema.parse(args);

    logger.debug(`[Denial Attempt] ID: ${commandId}, Reason: ${reason || 'none provided'}`);

    try {
      this.commandService.denyCommand(commandId, reason);
      logger.info(`Command denied: ID: ${commandId}, Reason: ${reason || 'none provided'}`);
      
      return {
        content: [
          {
            type: 'text',
            text: `Command denied${reason ? `: ${reason}` : ''}`,
          },
        ],
      };
    } catch (error) {
      logger.error(`[Denial Error] ID: ${commandId}, Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
      
      if (error instanceof Error) {
        return {
          content: [
            {
              type: 'text',
              text: `Command denial failed: ${error.message}`,
            },
          ],
          isError: true,
        };
      }
      throw error;
    }
  }

  /**
   * Run the MCP server
   */
  async run() {
    logger.info('Starting Super Shell MCP server');
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    logger.info('Super Shell MCP server running on stdio');
    console.error('Super Shell MCP server running on stdio');
    console.error(`Log file: ${LOG_FILE}`);
    logger.info(`Log file: ${LOG_FILE}`);
  }
}

// Create and run the server
const server = new SuperShellMcpServer();
server.run().catch(console.error);
```