Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Python cache
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# Virtual environments
.venv/
venv/
ENV/
env/

# Testing
.pytest_cache/
.coverage
htmlcov/
.tox/
.hypothesis/

# IDE
.vscode/
.idea/
*.swp
*.swo
*~

# Git
.git/
.gitignore

# Documentation
docs/
*.md
!README.md

# CI/CD
.github/

# Development files
.env
.env.*
CLAUDE.md

# Logs
logs/
*.log

# macOS
.DS_Store

# Test data
tests/
data.ms/
46 changes: 43 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Publish to PyPI
name: Publish to PyPI and Docker Hub

on:
push:
Expand All @@ -24,7 +24,7 @@ jobs:
echo "version_changed=true" >> $GITHUB_OUTPUT
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT

build-and-publish:
build-and-publish-pypi:
needs: check-version
if: needs.check-version.outputs.version_changed == 'true'
runs-on: ubuntu-latest
Expand Down Expand Up @@ -54,4 +54,44 @@ jobs:
uses: pypa/gh-action-pypi-publish@release/v1
with:
verbose: true
print-hash: true
print-hash: true

build-and-publish-docker:
needs: check-version
if: needs.check-version.outputs.version_changed == 'true'
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: meilisearch/meilisearch-mcp
tags: |
type=raw,value=${{ needs.check-version.outputs.new_version }}
type=raw,value=latest

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
28 changes: 28 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Use Python 3.12 slim image for smaller size
FROM python:3.12-slim

# Set working directory
WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
&& rm -rf /var/lib/apt/lists/*

# Install uv for faster Python package management
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
ENV PATH="/root/.local/bin:${PATH}"

# Copy project files
COPY pyproject.toml README.md ./
COPY src/ ./src/

# Install the package
RUN uv pip install --system .

# Set default environment variables
ENV MEILI_HTTP_ADDR=http://meilisearch:7700
ENV MEILI_MASTER_KEY=""

# Run the MCP server
CMD ["python", "-m", "src.meilisearch_mcp"]
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,48 @@ source .venv/bin/activate # On Windows: .venv\Scripts\activate
uv pip install -e .
```

### Using Docker

Perfect for containerized environments like n8n workflows!

#### From Docker Hub (Recommended)

```bash
# Pull the latest image
docker pull meilisearch/meilisearch-mcp:latest

# Or a specific version
docker pull meilisearch/meilisearch-mcp:0.5.0

# Run the container
docker run -it \
-e MEILI_HTTP_ADDR=http://your-meilisearch:7700 \
-e MEILI_MASTER_KEY=your-master-key \
meilisearch/meilisearch-mcp:latest
```

#### Build from Source

```bash
# Build your own image
docker build -t meilisearch-mcp .
docker run -it \
-e MEILI_HTTP_ADDR=http://your-meilisearch:7700 \
-e MEILI_MASTER_KEY=your-master-key \
meilisearch-mcp
```

#### Integration with n8n

For n8n workflows, you can use the Docker image directly in your setup:
```yaml
meilisearch-mcp:
image: meilisearch/meilisearch-mcp:latest
environment:
- MEILI_HTTP_ADDR=http://meilisearch:7700
- MEILI_MASTER_KEY=masterKey
```

## 🛠️ What Can You Do?

<details>
Expand Down
77 changes: 77 additions & 0 deletions tests/test_docker_integration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""
Integration tests for Docker image build.

These tests verify that the Docker image can be built successfully.
"""
import subprocess
import pytest
import shutil


# Check if Docker is available
def docker_available():
"""Check if Docker is available on the system."""
if not shutil.which("docker"):
return False
# Try to run docker version to ensure it's working
try:
result = subprocess.run(
["docker", "version"],
capture_output=True,
text=True,
timeout=5
)
return result.returncode == 0
except (subprocess.TimeoutExpired, FileNotFoundError):
return False


# Skip all tests in this module if Docker is not available
pytestmark = pytest.mark.skipif(
not docker_available(),
reason="Docker not available on this system"
)


def test_docker_build():
"""Test that the Docker image can be built successfully."""
result = subprocess.run(
["docker", "build", "-t", "meilisearch-mcp-test", "."],
capture_output=True,
text=True
)
assert result.returncode == 0, f"Docker build failed: {result.stderr}"


def test_docker_image_runs():
"""Test that the Docker image can run and show help."""
# First build the image
build_result = subprocess.run(
["docker", "build", "-t", "meilisearch-mcp-test", "."],
capture_output=True,
text=True
)
if build_result.returncode != 0:
pytest.skip(f"Docker build failed: {build_result.stderr}")

# Try to run the container and check it starts
result = subprocess.run(
[
"docker", "run", "--rm",
"-e", "MEILI_HTTP_ADDR=http://localhost:7700",
"-e", "MEILI_MASTER_KEY=test",
"meilisearch-mcp-test",
"python", "-c", "import src.meilisearch_mcp; print('MCP module loaded successfully')"
],
capture_output=True,
text=True,
timeout=30
)

assert result.returncode == 0, f"Docker run failed: {result.stderr}"
assert "MCP module loaded successfully" in result.stdout


if __name__ == "__main__":
# Run tests
pytest.main([__file__, "-v"])