# Directory Structure
```
├── .gitignore
├── debug_lmstudio.py
├── debug_server.py
├── install.bat
├── install.sh
├── INSTALLATION.md
├── prepare_for_claude.bat
├── prepare_for_claude.sh
├── README.md
├── requirements.txt
├── run_server.bat
├── run_server.sh
├── server.py
├── setup.bat
├── setup.sh
├── test_mcp.py
└── verify_setup.py
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | venv/
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 | .env
6 | .venv
7 | env/
8 | ENV/
9 | .DS_Store
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Claude-LMStudio Bridge
2 |
3 | An MCP server that bridges Claude with local LLMs running in LM Studio.
4 |
5 | ## Overview
6 |
7 | This tool allows Claude to interact with your local LLMs running in LM Studio, providing:
8 |
9 | - Access to list all available models in LM Studio
10 | - The ability to generate text using your local LLMs
11 | - Support for chat completions through your local models
12 | - A health check tool to verify connectivity with LM Studio
13 |
14 | ## Prerequisites
15 |
16 | - [Claude Desktop](https://claude.ai/desktop) with MCP support
17 | - [LM Studio](https://lmstudio.ai/) installed and running locally with API server enabled
18 | - Python 3.8+ installed
19 |
20 | ## Quick Start (Recommended)
21 |
22 | ### For macOS/Linux:
23 |
24 | 1. Clone the repository
25 | ```bash
26 | git clone https://github.com/infinitimeless/claude-lmstudio-bridge.git
27 | cd claude-lmstudio-bridge
28 | ```
29 |
30 | 2. Run the setup script
31 | ```bash
32 | chmod +x setup.sh
33 | ./setup.sh
34 | ```
35 |
36 | 3. Follow the setup script's instructions to configure Claude Desktop
37 |
38 | ### For Windows:
39 |
40 | 1. Clone the repository
41 | ```cmd
42 | git clone https://github.com/infinitimeless/claude-lmstudio-bridge.git
43 | cd claude-lmstudio-bridge
44 | ```
45 |
46 | 2. Run the setup script
47 | ```cmd
48 | setup.bat
49 | ```
50 |
51 | 3. Follow the setup script's instructions to configure Claude Desktop
52 |
53 | ## Manual Setup
54 |
55 | If you prefer to set things up manually:
56 |
57 | 1. Create a virtual environment (optional but recommended)
58 | ```bash
59 | python -m venv venv
60 | source venv/bin/activate # On Windows: venv\Scripts\activate
61 | ```
62 |
63 | 2. Install the required packages
64 | ```bash
65 | pip install -r requirements.txt
66 | ```
67 |
68 | 3. Configure Claude Desktop:
69 | - Open Claude Desktop preferences
70 | - Navigate to the 'MCP Servers' section
71 | - Add a new MCP server with the following configuration:
72 | - **Name**: lmstudio-bridge
73 | - **Command**: /bin/bash (on macOS/Linux) or cmd.exe (on Windows)
74 | - **Arguments**:
75 | - macOS/Linux: /path/to/claude-lmstudio-bridge/run_server.sh
76 | - Windows: /c C:\path\to\claude-lmstudio-bridge\run_server.bat
77 |
78 | ## Usage with Claude
79 |
80 | After setting up the bridge, you can use the following commands in Claude:
81 |
82 | 1. Check the connection to LM Studio:
83 | ```
84 | Can you check if my LM Studio server is running?
85 | ```
86 |
87 | 2. List available models:
88 | ```
89 | List the available models in my local LM Studio
90 | ```
91 |
92 | 3. Generate text with a local model:
93 | ```
94 | Generate a short poem about spring using my local LLM
95 | ```
96 |
97 | 4. Send a chat completion:
98 | ```
99 | Ask my local LLM: "What are the main features of transformers in machine learning?"
100 | ```
101 |
102 | ## Troubleshooting
103 |
104 | ### Diagnosing LM Studio Connection Issues
105 |
106 | Use the included debugging tool to check your LM Studio connection:
107 |
108 | ```bash
109 | python debug_lmstudio.py
110 | ```
111 |
112 | For more detailed tests:
113 | ```bash
114 | python debug_lmstudio.py --test-chat --verbose
115 | ```
116 |
117 | ### Common Issues
118 |
119 | **"Cannot connect to LM Studio API"**
120 | - Make sure LM Studio is running
121 | - Verify the API server is enabled in LM Studio (Settings > API Server)
122 | - Check that the port (default: 1234) matches what's in your .env file
123 |
124 | **"No models are loaded"**
125 | - Open LM Studio and load a model
126 | - Verify the model is running successfully
127 |
128 | **"MCP package not found"**
129 | - Try reinstalling: `pip install "mcp[cli]" httpx python-dotenv`
130 | - Make sure you're using Python 3.8 or later
131 |
132 | **"Claude can't find the bridge"**
133 | - Check Claude Desktop configuration
134 | - Make sure the path to run_server.sh or run_server.bat is correct and absolute
135 | - Verify the server script is executable: `chmod +x run_server.sh` (on macOS/Linux)
136 |
137 | ## Advanced Configuration
138 |
139 | You can customize the bridge behavior by creating a `.env` file with these settings:
140 |
141 | ```
142 | LMSTUDIO_HOST=127.0.0.1
143 | LMSTUDIO_PORT=1234
144 | DEBUG=false
145 | ```
146 |
147 | Set `DEBUG=true` to enable verbose logging for troubleshooting.
148 |
149 | ## License
150 |
151 | MIT
152 |
```
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
```
1 | # Core dependencies
2 | mcp[cli]>=0.1.0
3 | httpx>=0.24.0
4 | python-dotenv>=1.0.0
5 |
6 | # For development and testing
7 | pytest>=7.0.0
8 |
```
--------------------------------------------------------------------------------
/debug_server.py:
--------------------------------------------------------------------------------
```python
1 | import sys
2 | import traceback
3 | from mcp.server.fastmcp import FastMCP
4 |
5 | # Print startup message to stderr for debugging
6 | print("Starting debug server...", file=sys.stderr)
7 |
8 | try:
9 | # Initialize FastMCP server
10 | print("Initializing FastMCP server...", file=sys.stderr)
11 | mcp = FastMCP("lmstudio-bridge")
12 |
13 | @mcp.tool()
14 | async def debug_test() -> str:
15 | """Basic test function to verify MCP server is working.
16 |
17 | Returns:
18 | A simple confirmation message
19 | """
20 | print("debug_test function called", file=sys.stderr)
21 | return "MCP server is working correctly!"
22 |
23 | if __name__ == "__main__":
24 | print("Starting server with stdio transport...", file=sys.stderr)
25 | # Initialize and run the server
26 | mcp.run(transport='stdio')
27 | except Exception as e:
28 | print(f"ERROR: {str(e)}", file=sys.stderr)
29 | print("Traceback:", file=sys.stderr)
30 | traceback.print_exc(file=sys.stderr)
31 |
```
--------------------------------------------------------------------------------
/prepare_for_claude.bat:
--------------------------------------------------------------------------------
```
1 | @echo off
2 | SETLOCAL
3 |
4 | echo === Claude-LMStudio Bridge Setup ===
5 | echo This script will prepare the environment for use with Claude Desktop
6 | echo.
7 |
8 | :: Try to install MCP globally to ensure it's available
9 | echo Installing MCP package globally...
10 | python -m pip install "mcp[cli]" httpx
11 |
12 | :: Check if installation was successful
13 | python -c "import mcp" >nul 2>&1
14 | IF %ERRORLEVEL% NEQ 0 (
15 | echo X Failed to install MCP package. Please check your Python installation.
16 | EXIT /B 1
17 | ) ELSE (
18 | echo ✓ MCP package installed successfully
19 | )
20 |
21 | :: Create virtual environment if it doesn't exist
22 | IF NOT EXIST venv (
23 | echo Creating virtual environment...
24 | python -m venv venv
25 | echo ✓ Created virtual environment
26 |
27 | :: Activate and install dependencies
28 | CALL venv\Scripts\activate.bat
29 | python -m pip install -r requirements.txt
30 | echo ✓ Installed dependencies in virtual environment
31 | ) ELSE (
32 | echo ✓ Virtual environment already exists
33 | )
34 |
35 | :: Display configuration instructions
36 | echo.
37 | echo === Configuration Instructions ===
38 | echo 1. Open Claude Desktop preferences
39 | echo 2. Navigate to the 'MCP Servers' section
40 | echo 3. Add a new MCP server with the following configuration:
41 | echo.
42 | echo Name: lmstudio-bridge
43 | echo Command: cmd.exe
44 | echo Arguments: /c %CD%\run_server.bat
45 | echo.
46 | echo 4. Start LM Studio and ensure the API server is running
47 | echo 5. Restart Claude Desktop
48 | echo.
49 | echo Setup complete! You can now use the LMStudio bridge with Claude Desktop.
50 |
51 | ENDLOCAL
```
--------------------------------------------------------------------------------
/test_mcp.py:
--------------------------------------------------------------------------------
```python
1 | #!/usr/bin/env python3
2 | """
3 | Simple script to test if MCP package is working correctly.
4 | Run this script to verify the MCP installation before attempting to run the full server.
5 | """
6 | import sys
7 | import traceback
8 |
9 | print("Testing MCP installation...")
10 |
11 | try:
12 | print("Importing mcp package...")
13 | from mcp.server.fastmcp import FastMCP
14 | print("✅ Successfully imported FastMCP")
15 |
16 | print("Creating FastMCP instance...")
17 | mcp = FastMCP("test-server")
18 | print("✅ Successfully created FastMCP instance")
19 |
20 | print("Registering simple tool...")
21 | @mcp.tool()
22 | async def hello() -> str:
23 | return "Hello, world!"
24 | print("✅ Successfully registered tool")
25 |
26 | print("All tests passed! MCP appears to be correctly installed.")
27 | print("\nNext steps:")
28 | print("1. First try running the debug_server.py script")
29 | print("2. Then try running the main server.py script if debug_server works")
30 |
31 | except ImportError as e:
32 | print(f"❌ Error importing MCP: {str(e)}")
33 | print("\nTry reinstalling the MCP package with:")
34 | print("pip uninstall mcp")
35 | print("pip install 'mcp[cli]'")
36 |
37 | except Exception as e:
38 | print(f"❌ Unexpected error: {str(e)}")
39 | traceback.print_exc()
40 |
41 | print("\nTroubleshooting tips:")
42 | print("1. Make sure you're using Python 3.8 or newer")
43 | print("2. Check that you're in the correct virtual environment")
44 | print("3. Try reinstalling dependencies: pip install -r requirements.txt")
45 |
```
--------------------------------------------------------------------------------
/prepare_for_claude.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash
2 | #
3 | # This script prepares the Claude-LMStudio Bridge for use with Claude Desktop
4 | # It installs required packages and ensures everything is ready to run
5 | #
6 |
7 | echo "=== Claude-LMStudio Bridge Setup ==="
8 | echo "This script will prepare the environment for use with Claude Desktop"
9 | echo
10 |
11 | # Make the run script executable
12 | chmod +x run_server.sh
13 | echo "✅ Made run_server.sh executable"
14 |
15 | # Try to install MCP globally to ensure it's available
16 | echo "Installing MCP package globally..."
17 | python -m pip install "mcp[cli]" httpx
18 |
19 | # Check if installation was successful
20 | if python -c "import mcp" 2>/dev/null; then
21 | echo "✅ MCP package installed successfully"
22 | else
23 | echo "❌ Failed to install MCP package. Please check your Python installation."
24 | exit 1
25 | fi
26 |
27 | # Create virtual environment if it doesn't exist
28 | if [ ! -d "venv" ]; then
29 | echo "Creating virtual environment..."
30 | python -m venv venv
31 | echo "✅ Created virtual environment"
32 |
33 | # Activate and install dependencies
34 | source venv/bin/activate
35 | python -m pip install -r requirements.txt
36 | echo "✅ Installed dependencies in virtual environment"
37 | else
38 | echo "✅ Virtual environment already exists"
39 | fi
40 |
41 | # Display configuration instructions
42 | echo
43 | echo "=== Configuration Instructions ==="
44 | echo "1. Open Claude Desktop preferences"
45 | echo "2. Navigate to the 'MCP Servers' section"
46 | echo "3. Add a new MCP server with the following configuration:"
47 | echo
48 | echo " Name: lmstudio-bridge"
49 | echo " Command: /bin/bash"
50 | echo " Arguments: $(pwd)/run_server.sh"
51 | echo
52 | echo "4. Start LM Studio and ensure the API server is running"
53 | echo "5. Restart Claude Desktop"
54 | echo
55 | echo "Setup complete! You can now use the LMStudio bridge with Claude Desktop."
56 |
```
--------------------------------------------------------------------------------
/setup.bat:
--------------------------------------------------------------------------------
```
1 | @echo off
2 | REM setup.bat - Simplified setup script for Claude-LMStudio Bridge
3 |
4 | echo === Claude-LMStudio Bridge Setup ===
5 | echo.
6 |
7 | REM Create and activate virtual environment
8 | if not exist venv (
9 | echo Creating virtual environment...
10 | python -m venv venv
11 | echo ✓ Created virtual environment
12 | ) else (
13 | echo ✓ Virtual environment already exists
14 | )
15 |
16 | REM Activate the virtual environment
17 | call venv\Scripts\activate.bat
18 |
19 | REM Install dependencies
20 | echo Installing dependencies...
21 | pip install -r requirements.txt
22 | echo ✓ Installed dependencies
23 |
24 | REM Create default configuration
25 | if not exist .env (
26 | echo Creating default configuration...
27 | (
28 | echo LMSTUDIO_HOST=127.0.0.1
29 | echo LMSTUDIO_PORT=1234
30 | echo DEBUG=false
31 | ) > .env
32 | echo ✓ Created .env configuration file
33 | ) else (
34 | echo ✓ Configuration file already exists
35 | )
36 |
37 | REM Check if LM Studio is running
38 | set PORT_CHECK=0
39 | netstat -an | findstr "127.0.0.1:1234" > nul && set PORT_CHECK=1
40 | if %PORT_CHECK%==1 (
41 | echo ✓ LM Studio is running on port 1234
42 | ) else (
43 | echo ⚠ LM Studio does not appear to be running on port 1234
44 | echo Please start LM Studio and enable the API server (Settings ^> API Server)
45 | )
46 |
47 | echo.
48 | echo ✓ Setup complete!
49 | echo.
50 | echo To start the bridge, run:
51 | echo venv\Scripts\activate.bat ^&^& python server.py
52 | echo.
53 | echo To configure with Claude Desktop:
54 | echo 1. Open Claude Desktop preferences
55 | echo 2. Navigate to the 'MCP Servers' section
56 | echo 3. Add a new MCP server with the following configuration:
57 | echo - Name: lmstudio-bridge
58 | echo - Command: cmd.exe
59 | echo - Arguments: /c %CD%\run_server.bat
60 | echo.
61 | echo Make sure LM Studio is running with API server enabled on port 1234.
62 |
63 | REM Keep the window open
64 | pause
65 |
```
--------------------------------------------------------------------------------
/setup.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash
2 | # setup.sh - Simplified setup script for Claude-LMStudio Bridge
3 |
4 | echo "=== Claude-LMStudio Bridge Setup ==="
5 |
6 | # Create and activate virtual environment
7 | if [ ! -d "venv" ]; then
8 | echo "Creating virtual environment..."
9 | python -m venv venv
10 | echo "✅ Created virtual environment"
11 | else
12 | echo "✅ Virtual environment already exists"
13 | fi
14 |
15 | # Activate the virtual environment
16 | source venv/bin/activate
17 |
18 | # Install dependencies
19 | echo "Installing dependencies..."
20 | pip install -r requirements.txt
21 | echo "✅ Installed dependencies"
22 |
23 | # Create default configuration
24 | if [ ! -f ".env" ]; then
25 | echo "Creating default configuration..."
26 | cat > .env << EOL
27 | LMSTUDIO_HOST=127.0.0.1
28 | LMSTUDIO_PORT=1234
29 | DEBUG=false
30 | EOL
31 | echo "✅ Created .env configuration file"
32 | else
33 | echo "✅ Configuration file already exists"
34 | fi
35 |
36 | # Make run_server.sh executable
37 | chmod +x run_server.sh
38 | echo "✅ Made run_server.sh executable"
39 |
40 | # Check if LM Studio is running
41 | if nc -z localhost 1234 2>/dev/null; then
42 | echo "✅ LM Studio is running on port 1234"
43 | else
44 | echo "⚠️ LM Studio does not appear to be running on port 1234"
45 | echo " Please start LM Studio and enable the API server (Settings > API Server)"
46 | fi
47 |
48 | echo
49 | echo "✅ Setup complete!"
50 | echo
51 | echo "To start the bridge, run:"
52 | echo " source venv/bin/activate && python server.py"
53 | echo
54 | echo "To configure with Claude Desktop:"
55 | echo "1. Open Claude Desktop preferences"
56 | echo "2. Navigate to the 'MCP Servers' section"
57 | echo "3. Add a new MCP server with the following configuration:"
58 | echo " - Name: lmstudio-bridge"
59 | echo " - Command: /bin/bash"
60 | echo " - Arguments: $(pwd)/run_server.sh"
61 | echo
62 | echo "Make sure LM Studio is running with API server enabled on port 1234."
63 |
```
--------------------------------------------------------------------------------
/INSTALLATION.md:
--------------------------------------------------------------------------------
```markdown
1 | # Installation Guide for Claude-LMStudio Bridge
2 |
3 | This guide provides detailed instructions for setting up the Claude-LMStudio Bridge MCP server.
4 |
5 | ## Installing the MCP Python SDK
6 |
7 | The primary issue users face is not having the MCP module installed properly. Here are different ways to install it:
8 |
9 | ### Using uv (Recommended)
10 |
11 | `uv` is a modern Python package installer that's recommended for MCP development:
12 |
13 | ```bash
14 | # Install uv if you don't have it
15 | pip install uv
16 |
17 | # Install the MCP SDK with CLI support
18 | uv add "mcp[cli]"
19 | ```
20 |
21 | ### Using pip
22 |
23 | Alternatively, you can use pip:
24 |
25 | ```bash
26 | pip install "mcp[cli]"
27 | ```
28 |
29 | ## Verifying Installation
30 |
31 | After installation, verify that the module is correctly installed:
32 |
33 | ```bash
34 | python -c "import mcp; print(mcp.__version__)"
35 | ```
36 |
37 | This should print the version of the MCP SDK if it's installed correctly.
38 |
39 | ## Ensuring the Correct Environment
40 |
41 | Make sure you're using the correct Python environment:
42 |
43 | 1. If using a virtual environment, activate it before running your script:
44 |
45 | ```bash
46 | # Activate virtual environment
47 | source venv/bin/activate # For Mac/Linux
48 | # or
49 | venv\Scripts\activate # For Windows
50 | ```
51 |
52 | 2. Verify the Python path to ensure you're using the expected Python interpreter:
53 |
54 | ```bash
55 | which python # On Mac/Linux
56 | where python # On Windows
57 | ```
58 |
59 | ## Testing the Installation
60 |
61 | Run the test script to verify your setup:
62 |
63 | ```bash
64 | python test_mcp.py
65 | ```
66 |
67 | If this works successfully, you should be ready to run the server.
68 |
69 | ## Common Issues and Solutions
70 |
71 | 1. **ModuleNotFoundError: No module named 'mcp'**
72 | - The MCP module isn't installed in your current Python environment
73 | - Solution: Install the MCP SDK as described above
74 |
75 | 2. **MCP installed but still getting import errors**
76 | - You might be running Python from a different environment
77 | - Solution: Check which Python is being used with `which python` and make sure your virtual environment is activated
78 |
79 | 3. **Error loading the server in Claude**
80 | - Make sure you're using absolute paths in your Claude Desktop configuration
81 | - Check that the server is executable and that Python has permission to access it
82 |
```
--------------------------------------------------------------------------------
/run_server.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash
2 |
3 | # Configuration - Auto-detect Python path
4 | if [ -z "$PYTHON_PATH" ]; then
5 | PYTHON_PATH=$(which python3 2>/dev/null || which python 2>/dev/null)
6 | if [ -z "$PYTHON_PATH" ]; then
7 | echo "ERROR: Python not found. Please install Python 3." >&2
8 | exit 1
9 | fi
10 | fi
11 |
12 | # Print current environment details
13 | echo "Current directory: $(pwd)" >&2
14 | echo "Using Python at: $PYTHON_PATH" >&2
15 |
16 | # Check if Python exists at the specified path
17 | if [ ! -f "$PYTHON_PATH" ]; then
18 | echo "ERROR: Python not found at $PYTHON_PATH" >&2
19 | echo "Please install Python or set the correct path in this script." >&2
20 | exit 1
21 | fi
22 |
23 | # Check if mcp is installed, if not, try to install it
24 | if ! $PYTHON_PATH -c "import mcp" 2>/dev/null; then
25 | echo "MCP package not found, attempting to install..." >&2
26 |
27 | # Try to install using python -m pip
28 | $PYTHON_PATH -m pip install "mcp[cli]" httpx || {
29 | echo "Failed to install MCP package. Please install manually with:" >&2
30 | echo "$PYTHON_PATH -m pip install \"mcp[cli]\" httpx" >&2
31 | exit 1
32 | }
33 |
34 | # Check if installation was successful
35 | if ! $PYTHON_PATH -c "import mcp" 2>/dev/null; then
36 | echo "MCP package was installed but still can't be imported." >&2
37 | echo "This might be due to a Python path issue." >&2
38 | exit 1
39 | fi
40 | fi
41 |
42 | # Check if httpx is installed
43 | if ! $PYTHON_PATH -c "import httpx" 2>/dev/null; then
44 | echo "httpx package not found, attempting to install..." >&2
45 | $PYTHON_PATH -m pip install httpx || {
46 | echo "Failed to install httpx package." >&2
47 | exit 1
48 | }
49 | fi
50 |
51 | # Check if dotenv is installed (for .env file support)
52 | if ! $PYTHON_PATH -c "import dotenv" 2>/dev/null; then
53 | echo "python-dotenv package not found, attempting to install..." >&2
54 | $PYTHON_PATH -m pip install python-dotenv || {
55 | echo "Failed to install python-dotenv package." >&2
56 | exit 1
57 | }
58 | fi
59 |
60 | # Check if virtual environment exists and use it if it does
61 | if [ -d "venv" ] && [ -f "venv/bin/python" ]; then
62 | echo "Using Python from virtual environment" >&2
63 | PYTHON_PATH=$(pwd)/venv/bin/python
64 | echo "Updated Python path to: $PYTHON_PATH" >&2
65 | fi
66 |
67 | # Attempt to check if LM Studio is running before starting
68 | if command -v nc &> /dev/null; then
69 | if ! nc -z localhost 1234 2>/dev/null; then
70 | echo "WARNING: LM Studio does not appear to be running on port 1234" >&2
71 | echo "Please make sure LM Studio is running with the API server enabled" >&2
72 | else
73 | echo "✓ LM Studio API server appears to be running on port 1234" >&2
74 | fi
75 | fi
76 |
77 | # Run the server script
78 | echo "Starting server.py with $PYTHON_PATH..." >&2
79 | $PYTHON_PATH server.py
80 |
```
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash
2 |
3 | # Claude-LMStudio Bridge Installer
4 | # This script will set up the Claude-LMStudio Bridge for use with Claude Desktop
5 |
6 | echo "===== Claude-LMStudio Bridge Installer ====="
7 | echo "This will configure the bridge to work with Claude Desktop"
8 | echo
9 |
10 | # Find Python location
11 | PYTHON_PATH=$(which python3)
12 | if [ -z "$PYTHON_PATH" ]; then
13 | echo "❌ ERROR: Python 3 not found in your PATH"
14 | echo "Please install Python 3 first and try again"
15 | exit 1
16 | fi
17 |
18 | echo "✅ Found Python at: $PYTHON_PATH"
19 |
20 | # Update the run_server.sh script with the correct Python path
21 | echo "Updating run_server.sh with Python path..."
22 | sed -i '' "s|PYTHON_PATH=.*|PYTHON_PATH=\"$PYTHON_PATH\"|g" run_server.sh
23 | chmod +x run_server.sh
24 |
25 | # Install required packages
26 | echo "Installing required Python packages..."
27 | "$PYTHON_PATH" -m pip install "mcp[cli]" httpx
28 |
29 | # Check if installation was successful
30 | if ! "$PYTHON_PATH" -c "import mcp" 2>/dev/null; then
31 | echo "❌ ERROR: Failed to install MCP package"
32 | echo "Try running manually: $PYTHON_PATH -m pip install \"mcp[cli]\" httpx"
33 | exit 1
34 | fi
35 |
36 | echo "✅ MCP package installed successfully"
37 |
38 | # Get full path to the run_server.sh script
39 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
40 | SCRIPT_PATH="$SCRIPT_DIR/run_server.sh"
41 |
42 | # Create or update Claude Desktop config
43 | CONFIG_DIR="$HOME/Library/Application Support/Claude"
44 | CONFIG_FILE="$CONFIG_DIR/claude_desktop_config.json"
45 |
46 | mkdir -p "$CONFIG_DIR"
47 |
48 | if [ -f "$CONFIG_FILE" ]; then
49 | # Backup existing config
50 | cp "$CONFIG_FILE" "$CONFIG_FILE.backup"
51 | echo "Created backup of existing config at $CONFIG_FILE.backup"
52 |
53 | # Check if JSON is valid and has mcpServers property
54 | if grep -q "\"mcpServers\"" "$CONFIG_FILE"; then
55 | # Add or update lmstudio-bridge entry
56 | TMP_FILE=$(mktemp)
57 | jq --arg path "$SCRIPT_PATH" '.mcpServers["lmstudio-bridge"] = {"command": "/bin/bash", "args": [$path]}' "$CONFIG_FILE" > "$TMP_FILE"
58 | mv "$TMP_FILE" "$CONFIG_FILE"
59 | else
60 | # Create mcpServers section
61 | TMP_FILE=$(mktemp)
62 | jq --arg path "$SCRIPT_PATH" '. + {"mcpServers": {"lmstudio-bridge": {"command": "/bin/bash", "args": [$path]}}}' "$CONFIG_FILE" > "$TMP_FILE"
63 | mv "$TMP_FILE" "$CONFIG_FILE"
64 | fi
65 | else
66 | # Create new config file
67 | echo "{
68 | \"mcpServers\": {
69 | \"lmstudio-bridge\": {
70 | \"command\": \"/bin/bash\",
71 | \"args\": [
72 | \"$SCRIPT_PATH\"
73 | ]
74 | }
75 | }
76 | }" > "$CONFIG_FILE"
77 | fi
78 |
79 | echo "✅ Updated Claude Desktop configuration at $CONFIG_FILE"
80 |
81 | echo
82 | echo "✅ Installation complete!"
83 | echo "Please restart Claude Desktop to use the LMStudio bridge"
84 | echo
85 | echo "If you encounter any issues, edit run_server.sh to check settings"
86 | echo "or refer to the README.md for troubleshooting steps."
87 |
```
--------------------------------------------------------------------------------
/run_server.bat:
--------------------------------------------------------------------------------
```
1 | @echo off
2 | SETLOCAL
3 |
4 | REM Configuration - Auto-detect Python path
5 | IF "%PYTHON_PATH%"=="" (
6 | FOR /F "tokens=*" %%i IN ('where python') DO (
7 | SET PYTHON_PATH=%%i
8 | GOTO :found_python
9 | )
10 |
11 | echo ERROR: Python not found in your PATH 1>&2
12 | echo Please install Python first and make sure it's in your PATH 1>&2
13 | EXIT /B 1
14 |
15 | :found_python
16 | )
17 |
18 | REM Print current environment details
19 | echo Current directory: %CD% 1>&2
20 | echo Using Python at: %PYTHON_PATH% 1>&2
21 |
22 | REM Check if Python exists at the specified path
23 | IF NOT EXIST "%PYTHON_PATH%" (
24 | echo ERROR: Python not found at %PYTHON_PATH% 1>&2
25 | echo Please install Python or set the correct path in this script. 1>&2
26 | EXIT /B 1
27 | )
28 |
29 | REM Check if mcp is installed, if not, try to install it
30 | "%PYTHON_PATH%" -c "import mcp" >nul 2>&1
31 | IF %ERRORLEVEL% NEQ 0 (
32 | echo MCP package not found, attempting to install... 1>&2
33 |
34 | REM Try to install using python -m pip
35 | "%PYTHON_PATH%" -m pip install "mcp[cli]" httpx
36 | IF %ERRORLEVEL% NEQ 0 (
37 | echo Failed to install MCP package. Please install manually with: 1>&2
38 | echo "%PYTHON_PATH%" -m pip install "mcp[cli]" httpx 1>&2
39 | EXIT /B 1
40 | )
41 |
42 | REM Check if installation was successful
43 | "%PYTHON_PATH%" -c "import mcp" >nul 2>&1
44 | IF %ERRORLEVEL% NEQ 0 (
45 | echo MCP package was installed but still can't be imported. 1>&2
46 | echo This might be due to a Python path issue. 1>&2
47 | EXIT /B 1
48 | )
49 | )
50 |
51 | REM Check if httpx is installed
52 | "%PYTHON_PATH%" -c "import httpx" >nul 2>&1
53 | IF %ERRORLEVEL% NEQ 0 (
54 | echo httpx package not found, attempting to install... 1>&2
55 | "%PYTHON_PATH%" -m pip install httpx
56 | IF %ERRORLEVEL% NEQ 0 (
57 | echo Failed to install httpx package. 1>&2
58 | EXIT /B 1
59 | )
60 | )
61 |
62 | REM Check if dotenv is installed (for .env file support)
63 | "%PYTHON_PATH%" -c "import dotenv" >nul 2>&1
64 | IF %ERRORLEVEL% NEQ 0 (
65 | echo python-dotenv package not found, attempting to install... 1>&2
66 | "%PYTHON_PATH%" -m pip install python-dotenv
67 | IF %ERRORLEVEL% NEQ 0 (
68 | echo Failed to install python-dotenv package. 1>&2
69 | EXIT /B 1
70 | )
71 | )
72 |
73 | REM Check if virtual environment exists and use it if it does
74 | IF EXIST "venv\Scripts\python.exe" (
75 | echo Using Python from virtual environment 1>&2
76 | SET PYTHON_PATH=%CD%\venv\Scripts\python.exe
77 | echo Updated Python path to: %PYTHON_PATH% 1>&2
78 | )
79 |
80 | REM Attempt to check if LM Studio is running before starting
81 | netstat -an | findstr "127.0.0.1:1234" >nul
82 | IF %ERRORLEVEL% NEQ 0 (
83 | echo WARNING: LM Studio does not appear to be running on port 1234 1>&2
84 | echo Please make sure LM Studio is running with the API server enabled 1>&2
85 | ) ELSE (
86 | echo ✓ LM Studio API server appears to be running on port 1234 1>&2
87 | )
88 |
89 | REM Run the server script
90 | echo Starting server.py with %PYTHON_PATH%... 1>&2
91 | "%PYTHON_PATH%" server.py
92 |
93 | ENDLOCAL
94 |
```
--------------------------------------------------------------------------------
/install.bat:
--------------------------------------------------------------------------------
```
1 | @echo off
2 | echo ===== Claude-LMStudio Bridge Installer =====
3 | echo This will configure the bridge to work with Claude Desktop
4 | echo.
5 |
6 | :: Find Python location
7 | for /f "tokens=*" %%i in ('where python') do (
8 | set PYTHON_PATH=%%i
9 | goto :found_python
10 | )
11 |
12 | echo X ERROR: Python not found in your PATH
13 | echo Please install Python first and try again
14 | exit /b 1
15 |
16 | :found_python
17 | echo v Found Python at: %PYTHON_PATH%
18 |
19 | :: Update the run_server.bat script with the correct Python path
20 | echo Updating run_server.bat with Python path...
21 | powershell -Command "(Get-Content run_server.bat) -replace 'SET PYTHON_PATH=.*', 'SET PYTHON_PATH=%PYTHON_PATH%' | Set-Content run_server.bat"
22 |
23 | :: Install required packages
24 | echo Installing required Python packages...
25 | "%PYTHON_PATH%" -m pip install "mcp[cli]" httpx
26 |
27 | :: Check if installation was successful
28 | "%PYTHON_PATH%" -c "import mcp" >nul 2>&1
29 | if %ERRORLEVEL% NEQ 0 (
30 | echo X ERROR: Failed to install MCP package
31 | echo Try running manually: "%PYTHON_PATH%" -m pip install "mcp[cli]" httpx
32 | exit /b 1
33 | )
34 |
35 | echo v MCP package installed successfully
36 |
37 | :: Get full path to the run_server.bat script
38 | set SCRIPT_DIR=%~dp0
39 | set SCRIPT_PATH=%SCRIPT_DIR%run_server.bat
40 | echo Script path: %SCRIPT_PATH%
41 |
42 | :: Create or update Claude Desktop config
43 | set CONFIG_DIR=%APPDATA%\Claude
44 | set CONFIG_FILE=%CONFIG_DIR%\claude_desktop_config.json
45 |
46 | if not exist "%CONFIG_DIR%" mkdir "%CONFIG_DIR%"
47 |
48 | if exist "%CONFIG_FILE%" (
49 | :: Backup existing config
50 | copy "%CONFIG_FILE%" "%CONFIG_FILE%.backup" >nul
51 | echo Created backup of existing config at %CONFIG_FILE%.backup
52 |
53 | :: Create new config file - we'll use a simple approach for Windows
54 | echo {> "%CONFIG_FILE%"
55 | echo "mcpServers": {>> "%CONFIG_FILE%"
56 | echo "lmstudio-bridge": {>> "%CONFIG_FILE%"
57 | echo "command": "cmd.exe",>> "%CONFIG_FILE%"
58 | echo "args": [>> "%CONFIG_FILE%"
59 | echo "/c",>> "%CONFIG_FILE%"
60 | echo "%SCRIPT_PATH:\=\\%">> "%CONFIG_FILE%"
61 | echo ]>> "%CONFIG_FILE%"
62 | echo }>> "%CONFIG_FILE%"
63 | echo }>> "%CONFIG_FILE%"
64 | echo }>> "%CONFIG_FILE%"
65 | ) else (
66 | :: Create new config file
67 | echo {> "%CONFIG_FILE%"
68 | echo "mcpServers": {>> "%CONFIG_FILE%"
69 | echo "lmstudio-bridge": {>> "%CONFIG_FILE%"
70 | echo "command": "cmd.exe",>> "%CONFIG_FILE%"
71 | echo "args": [>> "%CONFIG_FILE%"
72 | echo "/c",>> "%CONFIG_FILE%"
73 | echo "%SCRIPT_PATH:\=\\%">> "%CONFIG_FILE%"
74 | echo ]>> "%CONFIG_FILE%"
75 | echo }>> "%CONFIG_FILE%"
76 | echo }>> "%CONFIG_FILE%"
77 | echo }>> "%CONFIG_FILE%"
78 | )
79 |
80 | echo v Updated Claude Desktop configuration at %CONFIG_FILE%
81 |
82 | echo.
83 | echo v Installation complete!
84 | echo Please restart Claude Desktop to use the LMStudio bridge
85 | echo.
86 | echo If you encounter any issues, edit run_server.bat to check settings
87 | echo or refer to the README.md for troubleshooting steps.
88 |
89 | pause
```
--------------------------------------------------------------------------------
/verify_setup.py:
--------------------------------------------------------------------------------
```python
1 | #!/usr/bin/env python3
2 | """
3 | Verification script to check if all required packages are installed.
4 | This script will check for the presence of essential packages and their versions.
5 | """
6 | import sys
7 | import subprocess
8 | import platform
9 |
10 | def check_python_version():
11 | """Check if Python version is 3.8 or higher."""
12 | version = sys.version_info
13 | if version.major < 3 or (version.major == 3 and version.minor < 8):
14 | print(f"❌ Python version too old: {platform.python_version()}")
15 | print(" MCP requires Python 3.8 or higher.")
16 | return False
17 | else:
18 | print(f"✅ Python version: {platform.python_version()}")
19 | return True
20 |
21 | def check_package(package_name):
22 | """Check if a package is installed and get its version."""
23 | try:
24 | if package_name == "mcp":
25 | # Special handling for mcp to test import
26 | module = __import__(package_name)
27 | version = getattr(module, "__version__", "unknown")
28 | print(f"✅ {package_name} is installed (version: {version})")
29 | return True
30 | else:
31 | # Use pip to check other packages
32 | result = subprocess.run(
33 | [sys.executable, "-m", "pip", "show", package_name],
34 | capture_output=True,
35 | text=True
36 | )
37 | if result.returncode == 0:
38 | for line in result.stdout.splitlines():
39 | if line.startswith("Version:"):
40 | version = line.split(":", 1)[1].strip()
41 | print(f"✅ {package_name} is installed (version: {version})")
42 | return True
43 | print(f"❌ {package_name} is not installed")
44 | return False
45 | except ImportError:
46 | print(f"❌ {package_name} is not installed")
47 | return False
48 | except Exception as e:
49 | print(f"❌ Error checking {package_name}: {str(e)}")
50 | return False
51 |
52 | def check_environment():
53 | """Check if running in a virtual environment."""
54 | in_venv = hasattr(sys, "real_prefix") or (
55 | hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix
56 | )
57 | if in_venv:
58 | print(f"✅ Running in virtual environment: {sys.prefix}")
59 | return True
60 | else:
61 | print("⚠️ Not running in a virtual environment")
62 | print(" It's recommended to use a virtual environment for this project")
63 | return True # Not critical
64 |
65 | def main():
66 | """Run all checks."""
67 | print("🔍 Checking environment setup for Claude-LMStudio Bridge...")
68 | print("-" * 60)
69 |
70 | success = True
71 |
72 | # Check Python version
73 | if not check_python_version():
74 | success = False
75 |
76 | # Check virtual environment
77 | check_environment()
78 |
79 | # Check essential packages
80 | required_packages = ["mcp", "httpx"]
81 | for package in required_packages:
82 | if not check_package(package):
83 | success = False
84 |
85 | print("-" * 60)
86 | if success:
87 | print("✅ All essential checks passed! Your environment is ready.")
88 | print("\nNext steps:")
89 | print("1. Run 'python test_mcp.py' to test MCP functionality")
90 | print("2. Run 'python debug_server.py' to test a simple MCP server")
91 | print("3. Run 'python server.py' to start the full bridge server")
92 | else:
93 | print("❌ Some checks failed. Please address the issues above.")
94 | print("\nCommon solutions:")
95 | print("1. Install MCP: pip install 'mcp[cli]'")
96 | print("2. Install httpx: pip install httpx")
97 | print("3. Upgrade Python to 3.8+: https://www.python.org/downloads/")
98 |
99 | return 0 if success else 1
100 |
101 | if __name__ == "__main__":
102 | sys.exit(main())
103 |
```
--------------------------------------------------------------------------------
/debug_lmstudio.py:
--------------------------------------------------------------------------------
```python
1 | #!/usr/bin/env python3
2 | """
3 | debug_lmstudio.py - Simple diagnostic tool for LM Studio connectivity
4 |
5 | This script tests the connection to LM Studio's API server and helps identify
6 | issues with the connection or API calls.
7 | """
8 | import sys
9 | import json
10 | import traceback
11 | import argparse
12 | import os
13 | import httpx
14 | import asyncio
15 |
16 | # Set up command-line arguments
17 | parser = argparse.ArgumentParser(description="Test connection to LM Studio API")
18 | parser.add_argument("--host", default="127.0.0.1", help="LM Studio API host (default: 127.0.0.1)")
19 | parser.add_argument("--port", default="1234", help="LM Studio API port (default: 1234)")
20 | parser.add_argument("--test-prompt", action="store_true", help="Test with a simple prompt")
21 | parser.add_argument("--test-chat", action="store_true", help="Test with a simple chat message")
22 | parser.add_argument("--verbose", "-v", action="store_true", help="Show verbose output")
23 | args = parser.parse_args()
24 |
25 | # Configure API URL
26 | API_URL = f"http://{args.host}:{args.port}/v1"
27 | print(f"Testing connection to LM Studio API at {API_URL}")
28 |
29 | async def test_connection():
30 | """Test basic connectivity to the LM Studio API server"""
31 | try:
32 | print("\n=== Testing basic connectivity ===")
33 | async with httpx.AsyncClient() as client:
34 | response = await client.get(f"{API_URL}/models", timeout=5.0)
35 |
36 | if response.status_code == 200:
37 | print("✅ Connection successful!")
38 |
39 | # Check for available models
40 | data = response.json()
41 | if "data" in data and isinstance(data["data"], list):
42 | if len(data["data"]) > 0:
43 | models = [model.get("id", "Unknown") for model in data["data"]]
44 | print(f"✅ Found {len(models)} available model(s): {', '.join(models)}")
45 | else:
46 | print("⚠️ No models are currently loaded in LM Studio")
47 | else:
48 | print("⚠️ Unexpected response format from models endpoint")
49 |
50 | if args.verbose:
51 | print("\nResponse data:")
52 | print(json.dumps(data, indent=2))
53 |
54 | return True
55 | else:
56 | print(f"❌ Connection failed with status code: {response.status_code}")
57 | print(f"Response: {response.text[:200]}")
58 | return False
59 | except Exception as e:
60 | print(f"❌ Connection error: {str(e)}")
61 | if args.verbose:
62 | traceback.print_exc()
63 | return False
64 |
65 | async def test_completion():
66 | """Test text completion API with a simple prompt"""
67 | if not await test_connection():
68 | return False
69 |
70 | print("\n=== Testing text completion API ===")
71 | try:
72 | # Simple test prompt
73 | payload = {
74 | "prompt": "Hello, my name is",
75 | "max_tokens": 50,
76 | "temperature": 0.7,
77 | "stream": False
78 | }
79 |
80 | print("Sending test prompt: 'Hello, my name is'")
81 |
82 | async with httpx.AsyncClient() as client:
83 | response = await client.post(
84 | f"{API_URL}/completions",
85 | json=payload,
86 | timeout=10.0
87 | )
88 |
89 | if response.status_code == 200:
90 | data = response.json()
91 | if "choices" in data and len(data["choices"]) > 0:
92 | completion = data["choices"][0].get("text", "")
93 | print(f"✅ Received completion response: '{completion[:50]}...'")
94 |
95 | if args.verbose:
96 | print("\nFull response data:")
97 | print(json.dumps(data, indent=2))
98 |
99 | return True
100 | else:
101 | print("❌ No completion text received in the response")
102 | print(f"Response: {json.dumps(data, indent=2)}")
103 | return False
104 | else:
105 | print(f"❌ Completion request failed with status code: {response.status_code}")
106 | print(f"Response: {response.text[:200]}")
107 | return False
108 | except Exception as e:
109 | print(f"❌ Error during completion test: {str(e)}")
110 | if args.verbose:
111 | traceback.print_exc()
112 | return False
113 |
114 | async def test_chat():
115 | """Test chat completion API with a simple message"""
116 | if not await test_connection():
117 | return False
118 |
119 | print("\n=== Testing chat completion API ===")
120 | try:
121 | # Simple test chat message
122 | payload = {
123 | "messages": [
124 | {"role": "user", "content": "What is the capital of France?"}
125 | ],
126 | "max_tokens": 50,
127 | "temperature": 0.7,
128 | "stream": False
129 | }
130 |
131 | print("Sending test chat message: 'What is the capital of France?'")
132 |
133 | async with httpx.AsyncClient() as client:
134 | response = await client.post(
135 | f"{API_URL}/chat/completions",
136 | json=payload,
137 | timeout=10.0
138 | )
139 |
140 | if response.status_code == 200:
141 | data = response.json()
142 | if "choices" in data and len(data["choices"]) > 0:
143 | if "message" in data["choices"][0] and "content" in data["choices"][0]["message"]:
144 | message = data["choices"][0]["message"]["content"]
145 | print(f"✅ Received chat response: '{message[:50]}...'")
146 |
147 | if args.verbose:
148 | print("\nFull response data:")
149 | print(json.dumps(data, indent=2))
150 |
151 | return True
152 | else:
153 | print("❌ No message content received in the response")
154 | print(f"Response: {json.dumps(data, indent=2)}")
155 | return False
156 | else:
157 | print("❌ No choices received in the response")
158 | print(f"Response: {json.dumps(data, indent=2)}")
159 | return False
160 | else:
161 | print(f"❌ Chat request failed with status code: {response.status_code}")
162 | print(f"Response: {response.text[:200]}")
163 | return False
164 | except Exception as e:
165 | print(f"❌ Error during chat test: {str(e)}")
166 | if args.verbose:
167 | traceback.print_exc()
168 | return False
169 |
170 | async def run_tests():
171 | """Run all selected tests"""
172 | try:
173 | connection_ok = await test_connection()
174 |
175 | if args.test_prompt and connection_ok:
176 | await test_completion()
177 |
178 | if args.test_chat and connection_ok:
179 | await test_chat()
180 |
181 | if not args.test_prompt and not args.test_chat and connection_ok:
182 | # If no specific tests are requested, but connection is OK,
183 | # give a helpful message about next steps
184 | print("\n=== Next Steps ===")
185 | print("Connection to LM Studio API is working.")
186 | print("Try these additional tests:")
187 | print(" python debug_lmstudio.py --test-prompt # Test text completion")
188 | print(" python debug_lmstudio.py --test-chat # Test chat completion")
189 | print(" python debug_lmstudio.py -v --test-chat # Verbose test output")
190 |
191 | except Exception as e:
192 | print(f"❌ Unexpected error: {str(e)}")
193 | traceback.print_exc()
194 |
195 | # Run the tests
196 | if __name__ == "__main__":
197 | try:
198 | asyncio.run(run_tests())
199 | except KeyboardInterrupt:
200 | print("\nTests interrupted.")
201 | sys.exit(1)
202 |
```
--------------------------------------------------------------------------------
/server.py:
--------------------------------------------------------------------------------
```python
1 | import sys
2 | import traceback
3 | import os
4 | import json
5 | import logging
6 | from typing import Any, Dict, List, Optional, Union
7 | from mcp.server.fastmcp import FastMCP
8 | import httpx
9 |
10 | # Configure logging
11 | logging.basicConfig(
12 | level=logging.INFO,
13 | format="%(asctime)s - %(levelname)s - %(message)s",
14 | handlers=[
15 | logging.StreamHandler(sys.stderr)
16 | ]
17 | )
18 |
19 | # Print startup message
20 | logging.info("Starting LMStudio bridge server...")
21 |
22 | try:
23 | # ===== Configuration =====
24 | # Load from environment variables with defaults
25 | LMSTUDIO_HOST = os.getenv("LMSTUDIO_HOST", "127.0.0.1")
26 | LMSTUDIO_PORT = os.getenv("LMSTUDIO_PORT", "1234")
27 | LMSTUDIO_API_URL = f"http://{LMSTUDIO_HOST}:{LMSTUDIO_PORT}/v1"
28 | DEBUG = os.getenv("DEBUG", "false").lower() in ("true", "1", "yes")
29 |
30 | # Set more verbose logging if debug mode is enabled
31 | if DEBUG:
32 | logging.getLogger().setLevel(logging.DEBUG)
33 | logging.debug(f"Debug mode enabled")
34 |
35 | logging.info(f"Configured LM Studio API URL: {LMSTUDIO_API_URL}")
36 |
37 | # Initialize FastMCP server
38 | mcp = FastMCP("lmstudio-bridge")
39 |
40 | # ===== Helper Functions =====
41 | async def call_lmstudio_api(endpoint: str, payload: Dict[str, Any], timeout: float = 60.0) -> Dict[str, Any]:
42 | """Unified API communication function with better error handling"""
43 | headers = {
44 | "Content-Type": "application/json",
45 | "User-Agent": "claude-lmstudio-bridge/1.0"
46 | }
47 |
48 | url = f"{LMSTUDIO_API_URL}/{endpoint}"
49 | logging.debug(f"Making request to {url}")
50 | logging.debug(f"Payload: {json.dumps(payload, indent=2)}")
51 |
52 | try:
53 | async with httpx.AsyncClient() as client:
54 | response = await client.post(
55 | url,
56 | json=payload,
57 | headers=headers,
58 | timeout=timeout
59 | )
60 |
61 | # Better error handling with specific error messages
62 | if response.status_code != 200:
63 | error_message = f"LM Studio API error: {response.status_code}"
64 | try:
65 | error_json = response.json()
66 | if "error" in error_json:
67 | if isinstance(error_json["error"], dict) and "message" in error_json["error"]:
68 | error_message += f" - {error_json['error']['message']}"
69 | else:
70 | error_message += f" - {error_json['error']}"
71 | except:
72 | error_message += f" - {response.text[:100]}"
73 |
74 | logging.error(f"Error response: {error_message}")
75 | return {"error": error_message}
76 |
77 | result = response.json()
78 | logging.debug(f"Response received: {json.dumps(result, indent=2, default=str)[:200]}...")
79 | return result
80 | except httpx.RequestError as e:
81 | logging.error(f"Request error: {str(e)}")
82 | return {"error": f"Connection error: {str(e)}"}
83 | except Exception as e:
84 | logging.error(f"Unexpected error: {str(e)}")
85 | return {"error": f"Unexpected error: {str(e)}"}
86 |
87 | def prepare_chat_messages(messages_input: Union[str, List, Dict]) -> List[Dict[str, str]]:
88 | """Convert various input formats to what LMStudio expects"""
89 | try:
90 | # If messages_input is a string
91 | if isinstance(messages_input, str):
92 | # Try to parse it as JSON
93 | try:
94 | parsed = json.loads(messages_input)
95 | if isinstance(parsed, list):
96 | return parsed
97 | else:
98 | # If it's parsed but not a list, make it a user message
99 | return [{"role": "user", "content": messages_input}]
100 | except json.JSONDecodeError:
101 | # If not valid JSON, assume it's a simple message
102 | return [{"role": "user", "content": messages_input}]
103 |
104 | # If it's a list already
105 | elif isinstance(messages_input, list):
106 | return messages_input
107 |
108 | # If it's a dict, assume it's a single message
109 | elif isinstance(messages_input, dict) and "content" in messages_input:
110 | if "role" not in messages_input:
111 | messages_input["role"] = "user"
112 | return [messages_input]
113 |
114 | # If it's some other format, convert to string and make it a user message
115 | else:
116 | return [{"role": "user", "content": str(messages_input)}]
117 | except Exception as e:
118 | logging.error(f"Error preparing chat messages: {str(e)}")
119 | # Fallback to simplest format
120 | return [{"role": "user", "content": str(messages_input)}]
121 |
122 | # ===== MCP Tools =====
123 | @mcp.tool()
124 | async def check_lmstudio_connection() -> str:
125 | """Check if the LM Studio server is running and accessible.
126 |
127 | Returns:
128 | Connection status and model information
129 | """
130 | try:
131 | # Try to get the server status via models endpoint
132 | async with httpx.AsyncClient() as client:
133 | response = await client.get(f"{LMSTUDIO_API_URL}/models", timeout=5.0)
134 |
135 | if response.status_code == 200:
136 | models_data = response.json()
137 | if "data" in models_data and len(models_data["data"]) > 0:
138 | active_model = models_data["data"][0]["id"]
139 | return f"✅ Connected to LM Studio. Active model: {active_model}"
140 | else:
141 | return "✅ Connected to LM Studio but no models are currently loaded"
142 | else:
143 | return f"❌ LM Studio returned an error: {response.status_code}"
144 | except Exception as e:
145 | return f"❌ Failed to connect to LM Studio: {str(e)}"
146 |
147 | @mcp.tool()
148 | async def list_lmstudio_models() -> str:
149 | """List available LLM models in LM Studio.
150 |
151 | Returns:
152 | A formatted list of available models with their details.
153 | """
154 | logging.info("list_lmstudio_models function called")
155 | try:
156 | # Use the API helper function
157 | models_response = await call_lmstudio_api("models", {}, timeout=10.0)
158 |
159 | # Check for errors from the API helper
160 | if "error" in models_response:
161 | return f"Error listing models: {models_response['error']}"
162 |
163 | if not models_response or "data" not in models_response:
164 | return "No models found or unexpected response format."
165 |
166 | models = models_response["data"]
167 | model_info = []
168 |
169 | for model in models:
170 | model_info.append(f"ID: {model.get('id', 'Unknown')}")
171 | model_info.append(f"Name: {model.get('name', 'Unknown')}")
172 | if model.get('description'):
173 | model_info.append(f"Description: {model.get('description')}")
174 | model_info.append("---")
175 |
176 | if not model_info:
177 | return "No models available in LM Studio."
178 |
179 | return "\n".join(model_info)
180 | except Exception as e:
181 | logging.error(f"Unexpected error in list_lmstudio_models: {str(e)}")
182 | traceback.print_exc(file=sys.stderr)
183 | return f"Unexpected error: {str(e)}"
184 |
185 | @mcp.tool()
186 | async def generate_text(
187 | prompt: str,
188 | model_id: str = "",
189 | max_tokens: int = 1000,
190 | temperature: float = 0.7
191 | ) -> str:
192 | """Generate text using a local LLM in LM Studio.
193 |
194 | Args:
195 | prompt: The text prompt to send to the model
196 | model_id: ID of the model to use (leave empty for default model)
197 | max_tokens: Maximum number of tokens in the response (default: 1000)
198 | temperature: Randomness of the output (0-1, default: 0.7)
199 |
200 | Returns:
201 | The generated text from the local LLM
202 | """
203 | logging.info("generate_text function called")
204 | try:
205 | # Validate inputs
206 | if not prompt or not prompt.strip():
207 | return "Error: Prompt cannot be empty."
208 |
209 | if max_tokens < 1:
210 | return "Error: max_tokens must be a positive integer."
211 |
212 | if temperature < 0 or temperature > 1:
213 | return "Error: temperature must be between 0 and 1."
214 |
215 | # Prepare payload
216 | payload = {
217 | "prompt": prompt,
218 | "max_tokens": max_tokens,
219 | "temperature": temperature,
220 | "stream": False
221 | }
222 |
223 | # Add model if specified
224 | if model_id and model_id.strip():
225 | payload["model"] = model_id.strip()
226 |
227 | # Make request to LM Studio API using the helper function
228 | response = await call_lmstudio_api("completions", payload)
229 |
230 | # Check for errors from the API helper
231 | if "error" in response:
232 | return f"Error generating text: {response['error']}"
233 |
234 | # Extract and return the generated text
235 | if "choices" in response and len(response["choices"]) > 0:
236 | return response["choices"][0].get("text", "")
237 |
238 | return "No response generated."
239 | except Exception as e:
240 | logging.error(f"Unexpected error in generate_text: {str(e)}")
241 | traceback.print_exc(file=sys.stderr)
242 | return f"Unexpected error: {str(e)}"
243 |
244 | @mcp.tool()
245 | async def chat_completion(
246 | messages: str,
247 | model_id: str = "",
248 | max_tokens: int = 1000,
249 | temperature: float = 0.7
250 | ) -> str:
251 | """Generate a chat completion using a local LLM in LM Studio.
252 |
253 | Args:
254 | messages: JSON string of messages in the format [{"role": "user", "content": "Hello"}, ...]
255 | or a simple text string which will be treated as a user message
256 | model_id: ID of the model to use (leave empty for default model)
257 | max_tokens: Maximum number of tokens in the response (default: 1000)
258 | temperature: Randomness of the output (0-1, default: 0.7)
259 |
260 | Returns:
261 | The generated text from the local LLM
262 | """
263 | logging.info("chat_completion function called")
264 | try:
265 | # Standardize message format using the helper function
266 | messages_formatted = prepare_chat_messages(messages)
267 |
268 | logging.debug(f"Formatted messages: {json.dumps(messages_formatted, indent=2)}")
269 |
270 | # Validate inputs
271 | if not messages_formatted:
272 | return "Error: At least one message is required."
273 |
274 | if max_tokens < 1:
275 | return "Error: max_tokens must be a positive integer."
276 |
277 | if temperature < 0 or temperature > 1:
278 | return "Error: temperature must be between 0 and 1."
279 |
280 | # Prepare payload
281 | payload = {
282 | "messages": messages_formatted,
283 | "max_tokens": max_tokens,
284 | "temperature": temperature,
285 | "stream": False
286 | }
287 |
288 | # Add model if specified
289 | if model_id and model_id.strip():
290 | payload["model"] = model_id.strip()
291 |
292 | # Make request to LM Studio API using the helper function
293 | response = await call_lmstudio_api("chat/completions", payload)
294 |
295 | # Check for errors from the API helper
296 | if "error" in response:
297 | return f"Error generating chat completion: {response['error']}"
298 |
299 | # Extract and return the generated text
300 | if "choices" in response and len(response["choices"]) > 0:
301 | choice = response["choices"][0]
302 | if "message" in choice and "content" in choice["message"]:
303 | return choice["message"]["content"]
304 |
305 | return "No response generated."
306 | except Exception as e:
307 | logging.error(f"Unexpected error in chat_completion: {str(e)}")
308 | traceback.print_exc(file=sys.stderr)
309 | return f"Unexpected error: {str(e)}"
310 |
311 | if __name__ == "__main__":
312 | logging.info("Starting server with stdio transport...")
313 | # Initialize and run the server
314 | mcp.run(transport='stdio')
315 | except Exception as e:
316 | logging.critical(f"CRITICAL ERROR: {str(e)}")
317 | logging.critical("Traceback:")
318 | traceback.print_exc(file=sys.stderr)
319 | sys.exit(1)
320 |
```