Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,40 @@ Generate a FastMCP server from YAML configuration.
shellmcp generate my-server.yml --output-dir ./output --verbose
```

### `shellmcp mcp-config`
Generate MCP server configuration JSON.

```bash
shellmcp mcp-config my-server.yml
shellmcp mcp-config my-server.yml --output-file mcp.json
shellmcp mcp-config my-server.yml --allow-auto-confirm
```

## MCP Configuration

ShellMCP can generate MCP server configuration JSON:

```bash
# Complete workflow: create → generate → get MCP config
shellmcp new --name "my-tools" --desc "My custom tools"
shellmcp add-tool my_tools.yml
shellmcp generate my_tools.yml
shellmcp mcp-config my_tools.yml
# Copy the JSON to your MCP client configuration!
```

The MCP config generator:
- Auto-detects your generated server file
- Creates proper MCP server configuration
- Outputs to stdout or file
- Uses templates for consistent formatting

See [MCP Integration Guide](docs/mcp-integration.md) for detailed documentation.

## Documentation

- [YAML Specification](docs/yml-specification.md)
- [MCP Integration](docs/mcp-integration.md)

## License

Expand Down
146 changes: 146 additions & 0 deletions docs/mcp-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# MCP Integration

ShellMCP can generate MCP server configuration JSON. This allows you to easily add your generated servers to MCP client configuration files.

## Quick Start

1. **Create and generate your ShellMCP server:**
```bash
# Create a new server configuration
shellmcp new --name "my-tools" --desc "My custom tools"

# Add some tools
shellmcp add-tool my_tools.yml

# Generate the server
shellmcp generate my_tools.yml
```

2. **Generate MCP configuration:**
```bash
# Output JSON to stdout
shellmcp mcp-config my_tools.yml

# Or save to file
shellmcp mcp-config my_tools.yml --output-file mcp.json
```

3. **Add to your MCP client** by copying the JSON to your configuration file

## Command Usage

```bash
# Generate JSON and output to stdout
shellmcp mcp-config my_tools.yml

# Generate JSON and save to file
shellmcp mcp-config my_tools.yml --output-file mcp.json

# Use specific server path
shellmcp mcp-config my_tools.yml --server-path ./output/my_tools_server.py

# Use different Python executable
shellmcp mcp-config my_tools.yml --python-executable python3.11

# Enable auto-trusting for tools
shellmcp mcp-config my_tools.yml --allow-auto-confirm
```


## MCP Configuration Locations

MCP clients typically look for configuration in these locations:

- **Global**: `~/.config/mcp/config.json`
- **Local**: `./mcp.json`
- **User Config**: `~/.mcp/config.json`

## Manual Integration

1. **Generate the configuration:**
```bash
shellmcp mcp-config my_tools.yml --output-file mcp_config.json
```

2. **Copy to your MCP client config:**
```bash
# For local project
cp mcp_config.json ./mcp.json

# Or merge with existing config
# (manually edit your existing config to add the new server)
```

3. **Restart your MCP client** to load your new MCP server

## Auto-Trusting Tools

You can enable auto-trusting for your tools using the `--allow-auto-confirm` flag. This adds the `--no-confirm-dangerous` argument to the server command, allowing tools to run without user confirmation:

```bash
shellmcp mcp-config my_tools.yml --allow-auto-confirm
```

This generates:

```json
{
"mcpServers": {
"my-tools": {
"command": "python3",
"args": ["--no-confirm-dangerous", "/path/to/my_tools_server.py"],
"env": {
"PYTHONPATH": "/path/to/server/directory"
}
}
}
}
```

**Security Note**: Only enable auto-trusting for tools you fully trust, as this allows them to run without user confirmation.

## Template Customization

The MCP JSON is generated using a Jinja2 template located at `shellmcp/templates/mcp_config.json.j2`:

```json
{
"mcpServers": {
"{{ server_name }}": {
"command": "{{ python_executable }}",
"args": [{% if allow_auto_confirm %}"--no-confirm-dangerous", {% endif %}"{{ server_path }}"],
"env": {
"PYTHONPATH": "{{ server_dir }}"
}
}
}
}
```

You can modify this template to customize the generated configuration format.

## Troubleshooting

### Server Not Found

If you get a "server file not found" error:

1. Ensure you've run `shellmcp generate` first
2. Check the server path is correct
3. Use `--server-path` to specify the exact path

### JSON Format Issues

If the generated JSON is invalid:

1. Check that your YAML configuration is valid
2. Verify the server file exists at the specified path
3. Ensure file paths don't contain special characters

## Best Practices

1. **Use descriptive server names** - They become identifiers in your MCP client
2. **Test your tools locally** - Validate your YAML configuration before generating JSON
3. **Use absolute paths** - The generator automatically converts relative paths to absolute
4. **Version control** - Keep your YAML configurations in version control
5. **Document your tools** - Add clear descriptions for better MCP integration
38 changes: 37 additions & 1 deletion shellmcp/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
)
from .parser import YMLParser
from .utils import get_choice, get_input, get_yes_no, load_or_create_config, save_config
from .mcp_config import generate_mcp_config


def _handle_error(error_msg: str, verbose: bool = False, exception: Exception = None) -> int:
Expand Down Expand Up @@ -536,6 +537,40 @@ def add_prompt(config_file: str, name: str = None, prompt_name: str = None, desc
return _handle_error(f"Error adding prompt: {e}", exception=e)


def mcp_config(config_file: str, server_path: str = None, python_executable: str = "python3",
output_file: str = None, allow_auto_confirm: bool = False) -> int:
"""
Generate MCP server configuration JSON.

Args:
config_file: Path to the YAML configuration file
server_path: Path to the generated server.py file (auto-detected if not provided)
python_executable: Python executable to use (default: python3)
output_file: Optional output file path (defaults to stdout)
allow_auto_confirm: Enable auto-trusting for tools (default: False)

Returns:
Exit code (0 for success, 1 for failure)
"""
try:
if not _check_file_exists(config_file):
return _handle_error(f"File '{config_file}' not found")

result = generate_mcp_config(
config_file, server_path, python_executable, output_file, allow_auto_confirm
)

if output_file:
print(result)
else:
print(result)

return 0

except Exception as e:
return _handle_error(f"Error generating MCP config: {e}", exception=e)


def main():
"""Main CLI entry point using Fire."""
fire.Fire({
Expand All @@ -544,5 +579,6 @@ def main():
'new': new,
'add-tool': add_tool,
'add-resource': add_resource,
'add-prompt': add_prompt
'add-prompt': add_prompt,
'mcp-config': mcp_config
})
79 changes: 79 additions & 0 deletions shellmcp/mcp_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""Generate MCP server configuration JSON."""

import json
from pathlib import Path

from jinja2 import Environment, FileSystemLoader

from .parser import YMLParser


def generate_mcp_config(yml_file: str, server_path: str = None,
python_executable: str = "python3", output_file: str = None,
allow_auto_confirm: bool = False) -> str:
"""
Generate MCP server configuration JSON.

Args:
yml_file: Path to ShellMCP YAML configuration file
server_path: Path to the generated server.py file (auto-detected if not provided)
python_executable: Python executable to use (default: python3)
output_file: Optional output file path (defaults to stdout)
allow_auto_confirm: Enable auto-trusting for tools (default: False)

Returns:
Generated JSON configuration
"""
# Load ShellMCP configuration
if not Path(yml_file).exists():
raise FileNotFoundError(f"YAML configuration file not found: {yml_file}")

parser = YMLParser()
yml_config = parser.load_from_file(yml_file)
server_name = yml_config.server.name.replace(' ', '-').replace('_', '-').lower()

# Auto-detect server path if not provided
if server_path is None:
config_dir = Path(yml_file).parent
server_name_dir = yml_config.server.name.replace('-', '_').replace(' ', '_').lower()
server_path = config_dir / server_name_dir / f"{yml_config.server.name.replace('-', '_')}_server.py"

# Ensure server path is absolute
if not Path(server_path).is_absolute():
server_path = str(Path(server_path).resolve())
else:
server_path = str(server_path)

# Get server directory for PYTHONPATH
server_dir = str(Path(server_path).parent)

# Set up Jinja2 environment and generate JSON using template
template_dir = Path(__file__).parent / "templates"
jinja_env = Environment(
loader=FileSystemLoader(str(template_dir)),
trim_blocks=True,
lstrip_blocks=True
)

template = jinja_env.get_template('mcp_config.json.j2')
json_config = template.render(
server_name=server_name,
python_executable=python_executable,
server_path=server_path,
server_dir=server_dir,
allow_auto_confirm=allow_auto_confirm
)

# Parse and pretty-print JSON
parsed_config = json.loads(json_config)
formatted_json = json.dumps(parsed_config, indent=2)

# Write to file or return string
if output_file:
output_path = Path(output_file)
output_path.parent.mkdir(parents=True, exist_ok=True)
with open(output_file, 'w', encoding='utf-8') as f:
f.write(formatted_json)
return f"✅ MCP configuration written to {output_file}"
else:
return formatted_json
11 changes: 11 additions & 0 deletions shellmcp/templates/mcp_config.json.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"mcpServers": {
"{{ server_name }}": {
"command": "{{ python_executable }}",
"args": [{% if allow_auto_confirm %}"--no-confirm-dangerous", {% endif %}"{{ server_path }}"],
"env": {
"PYTHONPATH": "{{ server_dir }}"
}
}
}
}