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

```
├── Dockerfile
├── LICENSE
├── README.md
└── server.py
```

# Files

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

```markdown
  1 | 🧠 Ask ChatGPT - MCP Server (Stdio)
  2 | 
  3 | This is a Model Context Protocol (MCP) stdio server that forwards prompts to OpenAI’s ChatGPT (GPT-4o). It is designed to run inside LangGraph-based assistants and enables advanced summarization, analysis, and reasoning by accessing an external LLM.
  4 | 
  5 | 📌 What It Does
  6 | 
  7 | This server exposes a single tool:
  8 | 
  9 | ```json
 10 | {
 11 |   "name": "ask_chatgpt",
 12 |   "description": "Sends the provided text ('content') to an external ChatGPT (gpt-4o) model for advanced reasoning or summarization.",
 13 |   "parameters": {
 14 |     "type": "object",
 15 |     "properties": {
 16 |       "content": {
 17 |         "type": "string",
 18 |         "description": "The text to analyze, summarize, compare, or reason about."
 19 |       }
 20 |     },
 21 |     "required": ["content"]
 22 |   }
 23 | }
 24 | ```
 25 | 
 26 | Use this when your assistant needs to:
 27 | 
 28 | Summarize long documents
 29 | 
 30 | Analyze configuration files
 31 | 
 32 | Compare options
 33 | 
 34 | Perform advanced natural language reasoning
 35 | 
 36 | 🐳 Docker Usage
 37 | 
 38 | Build and run the container:
 39 | 
 40 | ```bash
 41 | 
 42 | docker build -t ask-chatgpt-mcp .
 43 | 
 44 | docker run -e OPENAI_API_KEY=your-openai-key -i ask-chatgpt-mcp
 45 | 
 46 | ```
 47 | 
 48 | 🧪 Manual Test
 49 | 
 50 | Test the server locally using a one-shot request:
 51 | 
 52 | ```bash
 53 | 
 54 | echo '{"method":"tools/call","params":{"name":"ask_chatgpt","arguments":{"content":"Summarize this config..."}}}' | \
 55 |   OPENAI_API_KEY=your-openai-key python3 server.py --oneshot
 56 | 
 57 | ```
 58 | 
 59 | 🧩 LangGraph Integration
 60 | 
 61 | To connect this MCP server to your LangGraph pipeline, configure it like this:
 62 | 
 63 | ```python
 64 | 
 65 | ("chatgpt-mcp", ["python3", "server.py", "--oneshot"], "tools/discover", "tools/call")
 66 | 
 67 | ```
 68 | 
 69 | ⚙️ MCP Server Config Example
 70 | 
 71 | Here’s how to configure the server using an mcpServers JSON config:
 72 | 
 73 | ```json
 74 | 
 75 | {
 76 |   "mcpServers": {
 77 |     "chatgpt": {
 78 |       "command": "python3",
 79 |       "args": [
 80 |         "server.py",
 81 |         "--oneshot"
 82 |       ],
 83 |       "env": {
 84 |         "OPENAI_API_KEY": "<YOUR_OPENAI_API_KEY>"
 85 |       }
 86 |     }
 87 |   }
 88 | }
 89 | 
 90 | ```
 91 | 
 92 | 🔍 Explanation
 93 | 
 94 | "command": Runs the script with Python
 95 | 
 96 | "args": Enables one-shot stdin/stdout mode
 97 | 
 98 | "env": Injects your OpenAI key securely
 99 | 
100 | 🌍 Environment Setup
101 | 
102 | Create a .env file (auto-loaded with python-dotenv) or export the key manually:
103 | 
104 | ```env
105 | 
106 | OPENAI_API_KEY=your-openai-key
107 | 
108 | ```
109 | 
110 | Or:
111 | 
112 | ```bash
113 | 
114 | export OPENAI_API_KEY=your-openai-key
115 | 
116 | ```
117 | 
118 | 📦 Dependencies
119 | 
120 | Installed during the Docker build:
121 | 
122 | openai
123 | 
124 | requests
125 | 
126 | python-dotenv
127 | 
128 | 📁 Project Structure
129 | 
130 | ```bash
131 | .
132 | ├── Dockerfile        # Docker build for the MCP server
133 | ├── server.py         # Main stdio server implementation
134 | └── README.md         # You're reading it!
135 | 
136 | ```
137 | 
138 | 🔐 Security Notes
139 | 
140 | Never commit .env files or API keys.
141 | 
142 | Store secrets in secure environment variables or secret managers.
```

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

```dockerfile
 1 | FROM python:3.11-slim
 2 | 
 3 | WORKDIR /app
 4 | 
 5 | RUN pip install requests
 6 | RUN pip install python-dotenv
 7 | RUN pip install openai 
 8 | 
 9 | COPY . .
10 | 
11 | CMD ["python", "-u", "server.py"]
```

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

```python
  1 | import os
  2 | import json
  3 | import time
  4 | import logging
  5 | import asyncio
  6 | import sys
  7 | import threading
  8 | from typing import Dict, Any
  9 | from openai import OpenAI
 10 | from openai.types.chat import ChatCompletion
 11 | 
 12 | # Setup logging
 13 | logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s')
 14 | logger = logging.getLogger("chatgpt_fastmcp")
 15 | 
 16 | # Load environment variable
 17 | OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
 18 | if not OPENAI_API_KEY:
 19 |     raise ValueError("Missing OPENAI_API_KEY environment variable")
 20 | 
 21 | # Initialize OpenAI Client
 22 | client = OpenAI(api_key=OPENAI_API_KEY)
 23 | logger.info("OpenAI client initialized")
 24 | 
 25 | # ChatGPT Wrapper Class
 26 | class ChatGPTClient:
 27 |     def __init__(self, model: str = "gpt-4o"):
 28 |         self.model = model
 29 | 
 30 |     async def ask(self, content: str) -> Dict[str, Any]:
 31 |         logger.info(f"Sending to ChatGPT: '{content}'")
 32 |         try:
 33 |             response: ChatCompletion = client.chat.completions.create(
 34 |                 model=self.model,
 35 |                 messages=[{"role": "user", "content": content}],
 36 |             )
 37 |             output = response.choices[0].message.content
 38 |             logger.info("Received response from ChatGPT")
 39 |             return {"output": output}
 40 |         except Exception as e:
 41 |             logger.error(f"OpenAI API error: {e}")
 42 |             return {"error": str(e)}
 43 | 
 44 | # Initialize the client
 45 | chatgpt = ChatGPTClient()
 46 | 
 47 | # Output helper
 48 | def send_response(response_data: Dict[str, Any]):
 49 |     response = json.dumps(response_data) + "\n"
 50 |     sys.stdout.write(response)
 51 |     sys.stdout.flush()
 52 | 
 53 | # stdin monitor loop for server mode
 54 | def monitor_stdin():
 55 |     while True:
 56 |         try:
 57 |             line = sys.stdin.readline().strip()
 58 |             if not line:
 59 |                 time.sleep(0.1)
 60 |                 continue
 61 | 
 62 |             try:
 63 |                 data = json.loads(line)
 64 |                 handle_request(data)
 65 |             except json.JSONDecodeError as e:
 66 |                 logger.error(f"JSON decode error: {e}")
 67 | 
 68 |         except Exception as e:
 69 |             logger.error(f"Exception in monitor_stdin: {e}")
 70 |             time.sleep(0.1)
 71 | 
 72 | # Request router
 73 | def handle_request(data: Dict[str, Any]):
 74 |     if not isinstance(data, dict):
 75 |         send_response({"error": "Invalid request format"})
 76 |         return
 77 | 
 78 |     method = data.get("method")
 79 |     if method == "tools/call":
 80 |         tool_name = data.get("params", {}).get("name")
 81 |         arguments = data.get("params", {}).get("arguments", {})
 82 |         if tool_name == "ask_chatgpt":
 83 |             content = arguments.get("content", "")
 84 |             result = asyncio.run(chatgpt.ask(content))
 85 |             send_response({"result": result})
 86 |         else:
 87 |             send_response({"error": "tool not found"})
 88 | 
 89 |     elif method == "tools/discover":
 90 |         send_response({
 91 |             "result": [
 92 |                 {
 93 |                     "name": "ask_chatgpt",
 94 |                     # --- MODIFIED DESCRIPTION ---
 95 |                     "description": (
 96 |                         "Sends the provided text ('content') to an external ChatGPT (gpt-4o) model "
 97 |                         "for analysis, summarization, comparison, or generation tasks. Use this "
 98 |                         "ONLY when specifically asked to get ChatGPT's perspective or perform complex "
 99 |                         "analysis beyond the primary assistant's capabilities (e.g., detailed security review "
100 |                         "of a config, summarizing a large document). Use this tool to augment your own AI capabilities as access to another external Large Language Model."
101 |                     ),
102 |                     # --- END MODIFIED DESCRIPTION ---
103 |                     "parameters": {
104 |                         "type": "object",
105 |                         "properties": {
106 |                             "content": {
107 |                                 "type": "string",
108 |                                 # You could optionally add a description for the parameter too
109 |                                 "description": "The text content (e.g., configuration, question, document snippet) to send to ChatGPT for processing."
110 |                                 }
111 |                         },
112 |                         "required": ["content"]
113 |                     }
114 |                 }
115 |             ]
116 |         })
117 | 
118 |     else:
119 |         send_response({"error": "unknown method"})
120 | 
121 | # Entry point
122 | if __name__ == "__main__":
123 |     logger.info("Starting Ask ChatGPT MCP Server")
124 | 
125 |     if "--oneshot" in sys.argv:
126 |         try:
127 |             line = sys.stdin.readline().strip()
128 |             data = json.loads(line)
129 |             handle_request(data)
130 |         except Exception as e:
131 |             logger.error(f"Oneshot error: {e}")
132 |             send_response({"error": str(e)})
133 | 
134 |     else:
135 |         stdin_thread = threading.Thread(target=monitor_stdin, daemon=True)
136 |         stdin_thread.start()
137 |         try:
138 |             while True:
139 |                 time.sleep(1)
140 |         except KeyboardInterrupt:
141 |             logger.info("Shutting down")
142 | 
```