#
tokens: 3927/50000 7/7 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .env.example
├── .gitignore
├── Dockerfile
├── LICENSE
├── mcp_server_deepseek
│   ├── config.py
│   └── server.py
├── README.md
└── requirements.txt
```

# Files

--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------

```
1 | DEEPSEEK_API_KEY=your_api_key_here
```

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

```
  1 | # Byte-compiled / optimized / DLL files
  2 | __pycache__/
  3 | *.py[cod]
  4 | *$py.class
  5 | 
  6 | # C extensions
  7 | *.so
  8 | 
  9 | # Distribution / packaging
 10 | .Python
 11 | build/
 12 | develop-eggs/
 13 | dist/
 14 | downloads/
 15 | eggs/
 16 | .eggs/
 17 | lib/
 18 | lib64/
 19 | parts/
 20 | sdist/
 21 | var/
 22 | wheels/
 23 | share/python-wheels/
 24 | *.egg-info/
 25 | .installed.cfg
 26 | *.egg
 27 | MANIFEST
 28 | 
 29 | # PyInstaller
 30 | #  Usually these files are written by a python script from a template
 31 | #  before PyInstaller builds the exe, so as to inject date/other infos into it.
 32 | *.manifest
 33 | *.spec
 34 | 
 35 | # Installer logs
 36 | pip-log.txt
 37 | pip-delete-this-directory.txt
 38 | 
 39 | # Unit test / coverage reports
 40 | htmlcov/
 41 | .tox/
 42 | .nox/
 43 | .coverage
 44 | .coverage.*
 45 | .cache
 46 | nosetests.xml
 47 | coverage.xml
 48 | *.cover
 49 | *.py,cover
 50 | .hypothesis/
 51 | .pytest_cache/
 52 | cover/
 53 | 
 54 | # Translations
 55 | *.mo
 56 | *.pot
 57 | 
 58 | # Django stuff:
 59 | *.log
 60 | local_settings.py
 61 | db.sqlite3
 62 | db.sqlite3-journal
 63 | 
 64 | # Flask stuff:
 65 | instance/
 66 | .webassets-cache
 67 | 
 68 | # Scrapy stuff:
 69 | .scrapy
 70 | 
 71 | # Sphinx documentation
 72 | docs/_build/
 73 | 
 74 | # PyBuilder
 75 | .pybuilder/
 76 | target/
 77 | 
 78 | # Jupyter Notebook
 79 | .ipynb_checkpoints
 80 | 
 81 | # IPython
 82 | profile_default/
 83 | ipython_config.py
 84 | 
 85 | # pyenv
 86 | #   For a library or package, you might want to ignore these files since the code is
 87 | #   intended to run in multiple environments; otherwise, check them in:
 88 | # .python-version
 89 | 
 90 | # pipenv
 91 | #   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
 92 | #   However, in case of collaboration, if having platform-specific dependencies or dependencies
 93 | #   having no cross-platform support, pipenv may install dependencies that don't work, or not
 94 | #   install all needed dependencies.
 95 | #Pipfile.lock
 96 | 
 97 | # UV
 98 | #   Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
 99 | #   This is especially recommended for binary packages to ensure reproducibility, and is more
100 | #   commonly ignored for libraries.
101 | #uv.lock
102 | 
103 | # poetry
104 | #   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105 | #   This is especially recommended for binary packages to ensure reproducibility, and is more
106 | #   commonly ignored for libraries.
107 | #   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108 | #poetry.lock
109 | 
110 | # pdm
111 | #   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112 | #pdm.lock
113 | #   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114 | #   in version control.
115 | #   https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116 | .pdm.toml
117 | .pdm-python
118 | .pdm-build/
119 | 
120 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121 | __pypackages__/
122 | 
123 | # Celery stuff
124 | celerybeat-schedule
125 | celerybeat.pid
126 | 
127 | # SageMath parsed files
128 | *.sage.py
129 | 
130 | # Environments
131 | .env
132 | .venv
133 | env/
134 | venv/
135 | ENV/
136 | env.bak/
137 | venv.bak/
138 | 
139 | # Spyder project settings
140 | .spyderproject
141 | .spyproject
142 | 
143 | # Rope project settings
144 | .ropeproject
145 | 
146 | # mkdocs documentation
147 | /site
148 | 
149 | # mypy
150 | .mypy_cache/
151 | .dmypy.json
152 | dmypy.json
153 | 
154 | # Pyre type checker
155 | .pyre/
156 | 
157 | # pytype static type analyzer
158 | .pytype/
159 | 
160 | # Cython debug symbols
161 | cython_debug/
162 | 
163 | # PyCharm
164 | #  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165 | #  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166 | #  and can be added to the global gitignore or merged into this file.  For a more nuclear
167 | #  option (not recommended) you can uncomment the following to ignore the entire idea folder.
168 | #.idea/
169 | 
170 | # PyPI configuration file
171 | .pypirc
172 | 
173 | .vscode
```

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

```markdown
  1 | # MCP Server for Deepseek Integration
  2 | 
  3 | This repository contains a Model Control Protocol (MCP) server implementation that allows Claude Desktop to use Deepseek models running in Docker.
  4 | 
  5 | ## Prerequisites
  6 | 
  7 | - Docker
  8 | - Python 3.11 or later
  9 | - A Deepseek API key
 10 | - Claude Desktop
 11 | 
 12 | ## Installation
 13 | 
 14 | 1. Clone the repository:
 15 | 
 16 | ```bash
 17 | git clone https://github.com/vincentf305/mcp-server-deepseek.git
 18 | cd mcp-server-deepseek
 19 | ```
 20 | 
 21 | 2. Install dependencies:
 22 | 
 23 | ```bash
 24 | pip install -r requirements.txt
 25 | ```
 26 | 
 27 | ## Setup Environment Variables
 28 | 
 29 | Create a `.env` file in the root directory of the project and add the following environment variable:
 30 | 
 31 | ```
 32 | DEEPSEEK_API_KEY=your_api_key_here
 33 | ```
 34 | 
 35 | Make sure to replace `your_api_key_here` with your actual Deepseek API key.
 36 | 
 37 | ## Running the Server
 38 | 
 39 | ### Using Docker
 40 | 
 41 | 1. Build the Docker image:
 42 | 
 43 | ```bash
 44 | docker build -t mcp_server_deepseek .
 45 | ```
 46 | 
 47 | 2. Run the container:
 48 | 
 49 | ```bash
 50 | docker run -d \
 51 |   --name mcp-server-deepseek \
 52 |   -p 8765:8765 \
 53 |   -e DEEPSEEK_API_KEY=your_api_key_here \
 54 |   mcp-server-deepseek
 55 | ```
 56 | 
 57 | ### Running Locally
 58 | 
 59 | ```bash
 60 | python -m mcp_server_deepseek.server
 61 | ```
 62 | 
 63 | ## Usage with Claude Desktop
 64 | 
 65 | 1. Ensure you have a Deepseek API key
 66 | 
 67 | 2. Add the following to your Claude Desktop configuration (claude_desktop_config.json):
 68 | 
 69 | ```json
 70 | {
 71 |   "mcpServers": {
 72 |     "deepseek-server": {
 73 |       "command": "docker",
 74 |       "args": [
 75 |         "run",
 76 |         "-i",
 77 |         "--rm",
 78 |         "-e",
 79 |         "DEEPSEEK_API_KEY",
 80 |         "mcp_server_deepseek"
 81 |       ],
 82 |       "env": {
 83 |         "DEEPSEEK_API_KEY": "your_api_key_here"
 84 |       }
 85 |     }
 86 |   }
 87 | }
 88 | ```
 89 | 
 90 | 3. Restart Claude Desktop to load the new configuration
 91 | 
 92 | ## Contributing
 93 | 
 94 | 1. Fork the repository
 95 | 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
 96 | 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
 97 | 4. Push to the branch (`git push origin feature/amazing-feature`)
 98 | 5. Create a Pull Request
 99 | 
100 | ## License
101 | 
102 | MIT License - see the [LICENSE](LICENSE) file for details
103 | 
```

--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------

```
1 | requests==2.31.0
2 | pydantic==2.10.6
3 | pydantic-settings==2.7.1
4 | mcp>=0.9.1
```

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

```dockerfile
 1 | FROM python:3.13-slim
 2 | 
 3 | WORKDIR /app
 4 | 
 5 | COPY requirements.txt .
 6 | RUN pip install --no-cache-dir -r requirements.txt
 7 | 
 8 | COPY mcp_server_deepseek/ ./mcp_server_deepseek/
 9 | 
10 | EXPOSE 3000
11 | 
12 | CMD ["python", "-m", "mcp_server_deepseek.server"]
```

--------------------------------------------------------------------------------
/mcp_server_deepseek/config.py:
--------------------------------------------------------------------------------

```python
 1 | from pydantic_settings import BaseSettings
 2 | 
 3 | class Settings(BaseSettings):
 4 |     deepseek_base_url: str = "https://api.deepseek.com"
 5 |     deepseek_api_key: str = ""
 6 | 
 7 |     class Config:
 8 |         env_file = ".env"
 9 |         env_file_encoding = "utf-8"
10 | 
11 | settings = Settings()
```

--------------------------------------------------------------------------------
/mcp_server_deepseek/server.py:
--------------------------------------------------------------------------------

```python
  1 | import asyncio
  2 | import click
  3 | import json
  4 | import logging
  5 | import requests
  6 | import sys
  7 | 
  8 | import mcp
  9 | import mcp.types as types
 10 | 
 11 | from mcp.server import Server, NotificationOptions
 12 | from mcp.server.models import InitializationOptions
 13 | 
 14 | from .config import settings
 15 | 
 16 | logging.basicConfig(level=logging.DEBUG)
 17 | logger = logging.getLogger(__name__)
 18 | 
 19 | def serve() -> Server:
 20 |     server = Server("deepseek-server")
 21 | 
 22 |     @server.list_tools()
 23 |     async def handle_list_tools() -> list[types.Tool]:
 24 |         return [
 25 |             types.Tool(
 26 |                 name="ask-deepseek",
 27 |                 description="Generate responses using the Deepseek model",
 28 |                 inputSchema={
 29 |                     "type": "object",
 30 |                     "properties": {
 31 |                         "messages": {
 32 |                             "type": "array",
 33 |                             "items": {
 34 |                                 "type": "object",
 35 |                                 "properties": {
 36 |                                     "role": {"type": "string", "enum": ["user", "assistant", "system"]},
 37 |                                     "content": {"type": "string"}
 38 |                                 },
 39 |                                 "required": ["role", "content"]
 40 |                             }
 41 |                         },
 42 |                         "model": {"type": "string", "default": "deepseek-coder", "enum": ["deepseek-coder", "deepseek-chat"]},
 43 |                         "temperature": {"type": "number", "default": 0.7, "minimum": 0, "maximum": 2},
 44 |                         "max_tokens": {"type": "integer", "default": 500, "minimum": 1, "maximum": 4000},
 45 |                         "top_p": {"type": "number", "default": 1.0, "minimum": 0, "maximum": 1},
 46 |                         "stream": {"type": "boolean", "default": False}
 47 |                     },
 48 |                     "required": ["messages"]
 49 |                 }
 50 |             )
 51 |         ]
 52 | 
 53 |     @server.call_tool()
 54 |     async def handle_tool_call(name: str, arguments: dict | None) -> list[types.TextContent]:
 55 |         try:
 56 |             if not arguments:
 57 |                 raise ValueError("No arguments provided")
 58 | 
 59 |             if name == "ask-deepseek":
 60 |                 messages = arguments["messages"]
 61 |                 model = arguments.get("model", "deepseek-coder")
 62 |                 temperature = arguments.get("temperature", 0.7)
 63 |                 max_tokens = arguments.get("max_tokens", 500)
 64 |                 top_p = arguments.get("top_p", 1.0)
 65 |                 stream = arguments.get("stream", False)
 66 | 
 67 |                 deepseek_request = {
 68 |                     "model": model,
 69 |                     "messages": messages,
 70 |                     "temperature": temperature,
 71 |                     "max_tokens": max_tokens,
 72 |                     "top_p": top_p,
 73 |                     "stream": stream
 74 |                 }
 75 | 
 76 |                 json_data = json.dumps(deepseek_request)
 77 | 
 78 |                 response = requests.post(
 79 |                     f"{settings.deepseek_base_url}/v1/chat/completions",
 80 |                     headers={
 81 |                         "Authorization": f"Bearer {settings.deepseek_api_key}",
 82 |                         "Content-Type": "application/json"
 83 |                     },
 84 |                     data=json_data
 85 |                 )
 86 | 
 87 |                 response.raise_for_status()
 88 |                 data = response.json()
 89 |                 chat_response = data["choices"][0]["message"]["content"]
 90 |                 
 91 |                 return [types.TextContent(type="text", text=chat_response)]
 92 | 
 93 |             raise ValueError(f"Unknown tool: {name}")
 94 |         except Exception as e:
 95 |             logger.error(f"Tool call failed: {str(e)}")
 96 |             return [types.TextContent(type="text", text=f"Error: {str(e)}")]
 97 |         
 98 |     return server
 99 | 
100 | @click.command()
101 | def main():
102 |     try:
103 |         async def _run():
104 |             async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
105 |                 server = serve()
106 |                 await server.run(
107 |                     read_stream, write_stream,
108 |                     InitializationOptions(
109 |                         server_name="deepseek-server",
110 |                         server_version="0.1.0",
111 |                         capabilities=server.get_capabilities(
112 |                             notification_options=NotificationOptions(),
113 |                             experimental_capabilities={}
114 |                         )
115 |                     )
116 |                 )
117 |         asyncio.run(_run())
118 |     except KeyboardInterrupt:
119 |         logger.info("Server stopped by user")
120 |     except Exception as e:
121 |         logger.exception("Server failed")
122 |         sys.exit(1)
123 | 
124 | if __name__ == "__main__":
125 |     main()
```