diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..f37173d --- /dev/null +++ b/.env.sample @@ -0,0 +1,2 @@ +# change if required +MCP_SERVER_PORT=8050 diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 25d81b7..3f46dab 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -7,6 +7,12 @@ on: jobs: build: runs-on: ubuntu-latest + services: + docker: + image: docker:dind + options: --privileged + volumes: + - /var/run/docker.sock:/var/run/docker.sock env: MCP_SERVER_PORT: ${{ secrets.MCP_SERVER_PORT }} YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY }} @@ -16,11 +22,17 @@ jobs: steps: - uses: actions/checkout@v4.2.2 + - name: Build production and development docker images + run: | + ./community/youtube/build.sh + docker compose build + - name: Run docker compose uses: hoverkraft-tech/compose-action@v2.0.1 with: compose-file: "compose-dev.yaml" - - - name: Build production and development docker images - run: | - docker compose build + env: + MCP_SERVER_PORT: ${{ secrets.MCP_SERVER_PORT }} + YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + POSTGRES_DSN: ${{ secrets.POSTGRES_DSN }} diff --git a/README.md b/README.md index 3779476..ff48182 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,11 @@ - [Core Features](#core-features) - [Technology Stack and Features](#technology-stack-and-features) - [Planned Features](#planned-features) + - [Getting Started](#getting-started) + - [Development](#development) + - [VSCode Devcontainer](#vscode-devcontainer) + - [Without VSCode Devcontainer](#without-vscode-devcontainer) + - [Refactored Markdown Files](#refactored-markdown-files) ## Core Features @@ -39,3 +44,86 @@ - :dollar: Deploy live demo to [![Fargate](https://img.shields.io/badge/Fargate-white.svg?logo=awsfargate)](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AWS_Fargate.html) - Provision with [![Terraform](https://img.shields.io/github/stars/hashicorp/terraform?logo=terraform&label=Terraform)](https://github.com/hashicorp/terraform) IaC - Push built images to ECR and Dockerhub + +## Getting Started + +Build community youtube MCP image with: + +```bash +./community/youtube/build.sh +``` + +> [!TIP] +> Instead of cloning or submoduling the repository locally, then building the image, this script builds the Docker image inside a temporary Docker-in-Docker container. This approach avoids polluting your local environment with throwaway files by cleaning up everything once the container exits. + +Then build the other images with: + +```bash +docker compose -f compose-dev.yaml build +``` + +Copy environment file: + +```bash +cp .env.sample .env +``` + +Add your following API keys and value to the respective file: `./envs/backend.env`, `./envs/youtube.env` and `.env`. + +```bash +OPENAI_API_KEY=sk-proj-... +POSTGRES_DSN=postgresql://postgres... +YOUTUBE_API_KEY=... +``` + +Set environment variables in shell: (compatible with `bash` and `zsh`) + +```bash +set -a; for env_file in ./envs/*; do source $env_file; done; set +a +``` + +Start production containers: + +```bash +docker compose up -d +``` + +## Development + +### VSCode Devcontainer + +> [!WARNING] +> Only replace the following if you plan to start debugger for FastAPI server in VSCode. + +Replace `./compose-dev.yaml` entrypoint to allow debugging FastAPI server: + +```yaml +# ... + api: + # ... + # entrypoint: uv run fastapi run api/main.py --root-path=/api --reload + # replace above with: + entrypoint: bash -c "sleep infinity" + # ... +``` + +```bash +code --no-sandbox . +``` + +Press `F1` and type `Dev Containers: Rebuild and Reopen in Container` to open containerized environment with IntelliSense and Debugger for FastAPI. + +### Without VSCode Devcontainer + +Run development environment with: + +```bash +docker compose -f compose-dev.yaml up -d +``` + +## Refactored Markdown Files + +The following markdown files provide additional details on other features: + +- [`./docs/mcp.md`](./docs/mcp.md) +- [`./docs/supabase.md`](./docs/supabase.md) diff --git a/backend/api/.vscode/launch.json b/backend/api/.vscode/launch.json index e756e17..89d6854 100644 --- a/backend/api/.vscode/launch.json +++ b/backend/api/.vscode/launch.json @@ -12,6 +12,7 @@ "args": [ "run", "api/main.py", + "--root-path=/api", "--reload" ] } diff --git a/backend/api/core/config.py b/backend/api/core/config.py index 8fa4842..2ba0ca3 100644 --- a/backend/api/core/config.py +++ b/backend/api/core/config.py @@ -14,20 +14,24 @@ class Settings(BaseSettings): mcp_server_port: int = 8050 postgres_dsn: PostgresDsn = ( - "postgresql+psycopg://postgres:password@example.supabase.com:6543/postgres" + "postgresql://postgres:password@example.supabase.com:6543/postgres" ) @computed_field @property def orm_conn_str(self) -> str: - return self.postgres_dsn.encoded_string() + # NOTE: Explicitly follow LangGraph AsyncPostgresSaver + # and use psycopg driver for ORM + return self.postgres_dsn.encoded_string().replace( + "postgresql://", "postgresql+psycopg://" + ) @computed_field @property def checkpoint_conn_str(self) -> str: # NOTE: LangGraph AsyncPostgresSaver has some issues # with specifying psycopg driver explicitly - return self.postgres_dsn.encoded_string().replace("+psycopg", "") + return self.postgres_dsn.encoded_string() settings = Settings() diff --git a/backend/mcp/Dockerfile b/backend/mcp/Dockerfile index af1aaab..0eeeddc 100644 --- a/backend/mcp/Dockerfile +++ b/backend/mcp/Dockerfile @@ -3,10 +3,8 @@ FROM ghcr.io/astral-sh/uv:python3.13-bookworm WORKDIR /app COPY ./backend/mcp/uv.lock ./backend/mcp/pyproject.toml . RUN uv sync --frozen && rm ./uv.lock ./pyproject.toml -RUN apt-get update && apt-get install -y --no-install-recommends \ - curl +RUN apt-get update && apt-get install -y --no-install-recommends curl COPY ./backend/mcp ./mcp COPY ./backend/shared_mcp ./shared_mcp ENV PYTHONPATH /app:$PYTHONPATH -ENV PATH /app:$PATH ENTRYPOINT ["uv", "run", "mcp/main.py"] diff --git a/backend/shared_mcp/tools.py b/backend/shared_mcp/tools.py index b082adf..b9bd389 100644 --- a/backend/shared_mcp/tools.py +++ b/backend/shared_mcp/tools.py @@ -4,7 +4,6 @@ mcp = FastMCP( "MCP Server", - host=os.environ["MCP_SERVER_HOST"], port=os.environ["MCP_SERVER_PORT"], ) diff --git a/community/youtube/build.sh b/community/youtube/build.sh new file mode 100755 index 0000000..953bc66 --- /dev/null +++ b/community/youtube/build.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +docker run --rm --entrypoint sh \ + --volume /var/run/docker.sock:/var/run/docker.sock \ + --workdir /app \ + docker:dind \ + -c " + git clone https://github.com/Klavis-AI/klavis.git . && \ + touch mcp_servers/youtube/.env && \ + docker build -t youtube-mcp-server -f mcp_servers/youtube/Dockerfile . + " diff --git a/compose-dev.yaml b/compose-dev.yaml index 9bd9907..3c67bec 100644 --- a/compose-dev.yaml +++ b/compose-dev.yaml @@ -2,11 +2,10 @@ services: api: image: api:prod build: - context: . dockerfile: ./backend/api/Dockerfile entrypoint: uv run fastapi run api/main.py --root-path=/api --reload env_file: - - ./envs/shared_mcp.env + - ./envs/backend.env ports: - 8000:8000 volumes: @@ -17,16 +16,41 @@ services: mcp: image: mcp:prod build: - context: . dockerfile: ./backend/mcp/Dockerfile - env_file: - - ./envs/shared_mcp.env + environment: + - MCP_SERVER_PORT=${MCP_SERVER_PORT} ports: - - 8050:8050 + - ${MCP_SERVER_PORT}:${MCP_SERVER_PORT} volumes: - ./backend/mcp:/app/mcp - ./backend/shared_mcp:/app/shared_mcp + youtube: + image: youtube-mcp-server + env_file: + - ./envs/youtube.env + environment: + - YOUTUBE_MCP_SERVER_PORT=${MCP_SERVER_PORT} + ports: + - 5000:${MCP_SERVER_PORT} + + dbhub: + image: bytebase/dbhub:0.3.3 + ports: + - 8080:${MCP_SERVER_PORT} + command: > + --transport sse + --port ${MCP_SERVER_PORT} + --dsn ${POSTGRES_DSN} + + inspector: + image: inspector:prod + build: + dockerfile: ./inspector/Dockerfile + ports: + - 6274:6274 + - 6277:6277 + nginx: image: nginx:1.26.3-alpine ports: diff --git a/docs/mcp.md b/docs/mcp.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/supabase.md b/docs/supabase.md new file mode 100644 index 0000000..e69de29 diff --git a/envs/backend.env b/envs/backend.env index c439a50..4fa624a 100644 --- a/envs/backend.env +++ b/envs/backend.env @@ -1,2 +1,3 @@ OPENAI_API_KEY= +# do not specify driver (do not specify `+psycopg`) POSTGRES_DSN= diff --git a/envs/shared_mcp.env b/envs/shared_mcp.env deleted file mode 100644 index 4e2eb5e..0000000 --- a/envs/shared_mcp.env +++ /dev/null @@ -1,2 +0,0 @@ -MCP_SERVER_HOST=0.0.0.0 -MCP_SERVER_PORT=8050 diff --git a/envs/youtube.env b/envs/youtube.env new file mode 100644 index 0000000..b267e8e --- /dev/null +++ b/envs/youtube.env @@ -0,0 +1 @@ +YOUTUBE_API_KEY= diff --git a/inspector/Dockerfile b/inspector/Dockerfile new file mode 100644 index 0000000..12c0cb1 --- /dev/null +++ b/inspector/Dockerfile @@ -0,0 +1,6 @@ +FROM node:22.15.0-alpine + +WORKDIR /app +RUN apk update && apk add curl && \ + npm install -g @modelcontextprotocol/inspector +ENTRYPOINT ["npx", "@modelcontextprotocol/inspector"]