diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e3b16b5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +node_modules +.git +*.log +*.zip +dist diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..55d0779 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +FROM node:22 + +# Install dependencies and Ngrok +RUN apt-get update && \ + apt-get install -y curl unzip && \ + curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc | tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null && \ + echo "deb https://ngrok-agent.s3.amazonaws.com buster main" | tee /etc/apt/sources.list.d/ngrok.list && \ + apt-get update && apt-get install ngrok + +# Set working directory +WORKDIR /app + +# Clone your repo +RUN git clone --single-branch --branch=feat/sse-server https://github.com/algolia/mcp-node.git ./mcp-sse + +WORKDIR /app/mcp-sse + +# Install npm packages and build +RUN npm install && npm run build + +WORKDIR /app/mcp-sse/dist + +# Copy entrypoint script +COPY docker/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] + diff --git a/README.md b/README.md index 73b2ba1..360cdbd 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,14 @@ If you have feedback or ideas (even code!), we'd love to hear it. Just know that > [!NOTE] > For step-by-step instructions, follow the [installation guide](#-installation) and [configuration for Claude Desktop](#%EF%B8%8F-configuration). +## 📦 Dockerized version + +There is a basic `Dockerfile` to run a containerized version that exposes the SSE server in a Docker container using ngrok. You will need an `ngrok` API token and Algolia credentials. + +To run the container: +1. `docker build -t mcp-ngrok .` +2. `docker run -e NGROK_AUTHTOKEN=xxxxxxxxx -e ALGOLIA_CREDS=xxxxx:xxxxxxxxxxxx -it --rm mcp-ngrok` + ## 🚀 Features Algolia Node.js MCP enables natural language interactions with your Algolia data through Claude Desktop. This implementation allows you to: diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 0000000..687bee2 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Fail fast +set -e + +# Configure Ngrok if token is provided +if [[ -n "$NGROK_AUTHTOKEN" ]]; then + ngrok config add-authtoken "$NGROK_AUTHTOKEN" +else + echo "NGROK_AUTHTOKEN is not set" 1>&2 + exit 1 +fi + +# Authenticate the application +#./app authenticate + +# Start your server in background +if [[ -n "$ALGOLIA_CREDS" ]]; then + ./app start-server --credentials $ALGOLIA_CREDS --transport http & +else + echo "ALGOLIA_CREDS is not set" 1>&2 + exit 1 +fi + +# Start ngrok in background +ngrok http 4243 + +# Wait a bit for ngrok to initialize +sleep 5 + +# Extract and print the public URL +curl --silent http://localhost:4040/api/tunnels | \ + grep -o 'https://[a-z0-9]*\.ngrok\.io' | \ + head -n 1 + diff --git a/package-lock.json b/package-lock.json index 78f30ec..29f369a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "algolia-mcp", - "version": "0.0.7", + "version": "0.0.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "algolia-mcp", - "version": "0.0.7", + "version": "0.0.8", "license": "ISC", "dependencies": { "@modelcontextprotocol/sdk": "^1.11.0", diff --git a/package.json b/package.json index ae3efee..78d62e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "algolia-mcp", - "version": "0.0.7", + "version": "0.0.8", "main": "index.js", "scripts": { "start": "node --experimental-strip-types --no-warnings=ExperimentalWarnings src/app.ts", @@ -27,7 +27,7 @@ }, "devDependencies": { "@eslint/js": "^9.24.0", - "@modelcontextprotocol/inspector": "^0.9.0", + "@modelcontextprotocol/inspector": "^0.14.3", "@types/cors": "^2.8.17", "@types/express": "^5.0.1", "@types/node": "^22.14.0", diff --git a/src/DashboardApi.ts b/src/DashboardApi.ts index ff77e31..6f20245 100644 --- a/src/DashboardApi.ts +++ b/src/DashboardApi.ts @@ -155,9 +155,12 @@ export class DashboardApi { }, ); - const result = await CreateApiKeyResponse.parse(await response.json()); + const key = CreateApiKeyResponse.parse(await response.json()).data.attributes.value; - return result.data.attributes.value; + const client = algoliasearch(applicationId, key); + await client.waitForApiKey({ key, operation: "add" }); + + return key; } async #makeRequest(url: string, requestInit: RequestInit = {}): Promise { diff --git a/src/app.ts b/src/app.ts index 533b5fd..dd0bdd9 100644 --- a/src/app.ts +++ b/src/app.ts @@ -18,6 +18,8 @@ const DEFAULT_ALLOW_TOOLS = [ "multipleBatch", "partialUpdateObject", "deleteByQuery", + "getObject", + "getObjects", // Analytics "getTopSearches", "getTopHits",