Final production deployment

#13
Files changed (5) hide show
  1. Dockerfile +15 -9
  2. app/__init__.py +0 -0
  3. app/main.py +7 -7
  4. pyproject.toml +6 -6
  5. uv.lock +0 -0
Dockerfile CHANGED
@@ -1,11 +1,6 @@
1
  # Use an official Python runtime as a parent image
2
  FROM python:3.12-slim
3
 
4
- # Set environment variables
5
- ENV PYTHONUNBUFFERED=1 \
6
- PYTHONDONTWRITEBYTECODE=1 \
7
- PATH="/home/user/.local/bin:$PATH"
8
-
9
  # Install system dependencies
10
  RUN apt-get update && apt-get install -y --no-install-recommends \
11
  curl \
@@ -14,18 +9,29 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
14
 
15
  # Create a non-root user
16
  RUN useradd -m -u 1000 user
17
- USER user
 
18
  WORKDIR /home/user/app
19
 
 
 
 
 
 
 
 
 
 
 
 
20
  # Install uv
21
  RUN curl -LsSf https://astral.sh/uv/install.sh | sh
22
 
23
  # Copy the application code
24
  COPY --chown=user . .
25
 
26
- # Install dependencies using uv
27
- # We use --system because we want to install into the container's python environment
28
- RUN uv pip install --system .
29
 
30
  # Expose the port Hugging Face expects
31
  EXPOSE 7860
 
1
  # Use an official Python runtime as a parent image
2
  FROM python:3.12-slim
3
 
 
 
 
 
 
4
  # Install system dependencies
5
  RUN apt-get update && apt-get install -y --no-install-recommends \
6
  curl \
 
9
 
10
  # Create a non-root user
11
  RUN useradd -m -u 1000 user
12
+
13
+ # Set up working directory
14
  WORKDIR /home/user/app
15
 
16
+ # Change ownership of the app directory
17
+ RUN chown user:user /home/user/app
18
+
19
+ # Switch to non-root user
20
+ USER user
21
+
22
+ # Set environment variables
23
+ ENV PATH="/home/user/.local/bin:/home/user/app/.venv/bin:$PATH" \
24
+ PYTHONUNBUFFERED=1 \
25
+ PYTHONDONTWRITEBYTECODE=1
26
+
27
  # Install uv
28
  RUN curl -LsSf https://astral.sh/uv/install.sh | sh
29
 
30
  # Copy the application code
31
  COPY --chown=user . .
32
 
33
+ # Remove existing venv if any and install dependencies
34
+ RUN rm -rf .venv && uv venv && uv pip install .
 
35
 
36
  # Expose the port Hugging Face expects
37
  EXPOSE 7860
app/__init__.py ADDED
File without changes
app/main.py CHANGED
@@ -68,19 +68,17 @@ async def verify_token(request: Request):
68
  if token != TOKEN:
69
  raise HTTPException(status_code=401, detail="Unauthorized")
70
 
71
- # Create the FastMCP Starlette app
72
  mcp_app = mcp.sse_app()
73
 
74
- # Mount the MCP app with token verification for /sse
75
- # Note: /messages is usually called by the client as a POST
76
- # We can wrap the whole mcp_app if we want, or just the /sse route.
77
- # For simplicity and according to requirements "enable the mcp server to be connected to via /sse endpoint ... and integrate a token"
78
-
79
  @app.get("/sse")
80
  async def sse(request: Request, _ = Depends(verify_token)):
81
  # Find the sse route in mcp_app
82
  for route in mcp_app.routes:
83
  if route.path == "/sse":
 
 
 
84
  return await route.endpoint(request)
85
  raise HTTPException(status_code=404)
86
 
@@ -89,7 +87,9 @@ async def messages(request: Request):
89
  # Find the messages route/mount in mcp_app
90
  for route in mcp_app.routes:
91
  if route.path == "/messages":
92
- # This is likely a Mount
 
 
93
  return await route.app(request.scope, request.receive, request._send)
94
  raise HTTPException(status_code=404)
95
 
 
68
  if token != TOKEN:
69
  raise HTTPException(status_code=401, detail="Unauthorized")
70
 
71
+ # FastMCP Starlette app
72
  mcp_app = mcp.sse_app()
73
 
 
 
 
 
 
74
  @app.get("/sse")
75
  async def sse(request: Request, _ = Depends(verify_token)):
76
  # Find the sse route in mcp_app
77
  for route in mcp_app.routes:
78
  if route.path == "/sse":
79
+ # Override host header for FastMCP transport security
80
+ request.scope["headers"] = [(k, v) for k, v in request.scope["headers"] if k.lower() != b"host"]
81
+ request.scope["headers"].append((b"host", b"localhost:7860"))
82
  return await route.endpoint(request)
83
  raise HTTPException(status_code=404)
84
 
 
87
  # Find the messages route/mount in mcp_app
88
  for route in mcp_app.routes:
89
  if route.path == "/messages":
90
+ # Override host header
91
+ request.scope["headers"] = [(k, v) for k, v in request.scope["headers"] if k.lower() != b"host"]
92
+ request.scope["headers"].append((b"host", b"localhost:7860"))
93
  return await route.app(request.scope, request.receive, request._send)
94
  raise HTTPException(status_code=404)
95
 
pyproject.toml CHANGED
@@ -1,13 +1,13 @@
1
  [project]
2
  name = "clickup-mcp"
3
  version = "0.1.0"
4
- description = "Add your description here"
5
  readme = "README.md"
6
  requires-python = ">=3.12"
7
  dependencies = [
8
- "fastapi>=0.136.1",
9
- "httpx>=0.28.1",
10
- "huggingface-hub>=1.13.0",
11
- "mcp[cli]>=1.27.0",
12
- "uvicorn>=0.46.0",
13
  ]
 
1
  [project]
2
  name = "clickup-mcp"
3
  version = "0.1.0"
4
+ description = "ClickUp MCP Server"
5
  readme = "README.md"
6
  requires-python = ">=3.12"
7
  dependencies = [
8
+ "fastapi",
9
+ "httpx",
10
+ "mcp[cli]",
11
+ "uvicorn",
12
+ "sse-starlette",
13
  ]
uv.lock ADDED
The diff for this file is too large to render. See raw diff