keeper.sh

general MCP Server

Calendar sync tool & universal calendar MCP server. Aggregate, sync and control calendars on Google, Outlook, Office 365, iCloud, CalDAV or ICS.

VerifiedInstall ReadyReviewed
generalgeneralgcp
8 views1,137 stars37 forksAGPL-3.0

Why This Matters

Discovered via github-seeds:mcp-hot and last synced 3mo ago.

VerifiedInstall ReadyReviewed
Source
github-seeds:mcp-hot
Stars
1,137
Last synced
3mo ago
Install
Instructions detected

Install

1. Install the package

npm install keeper.sh
54
Tools
0
Resources
0
Prompts
Standard I/O
Transport

Available Tools (54)

Web

5173

DATABASE_URL

`api`, `cron`, `worker`, `mcp`

API

3000

REDIS_URL

`api`, `cron`, `worker`

POLAR_ACCESS_TOKEN

`api`, `cron`

Redis

6379

BETTER_AUTH_SECRET

`api`, `mcp`

ENCRYPTION_KEY

`api`, `cron`, `worker`

Service

Local Port

1

∞

Postgres

5432

BETTER_AUTH_URL

`api`, `mcp`

POLAR_WEBHOOK_SECRET

`api`

MICROSOFT_CLIENT_SECRET

`api`, `cron`, `worker`

OTEL_EXPORTER_OTLP_PROTOCOL

`api`, `cron`, `worker`, `mcp`, `web`

Tag

Description

Caddy

443

Name

Description

VITE_API_URL

`web`

PASSKEY_ORIGIN

`api`

MCP_PUBLIC_URL

`api`, `mcp`

MCP

3001

WORKER_JOB_QUEUE_ENABLED

`cron`

2

∞

ENV

`web`

PASSKEY_RP_ID

`api`

PRIVATE_RESOLUTION_WHITELIST

`api`, `cron`

VITE_COMMERCIAL_MODE

Toggle commercial mode in the web UI (`true`/`false`).

Free

Pro (Cloud-Hosted)

API_PORT

`api`

RESEND_API_KEY

`api`

BLOCK_PRIVATE_RESOLUTION

`api`, `cron`

list_calendars

List all calendars connected to Keeper, including provider name and account.

PORT

`web`

PASSKEY_RP_NAME

`api`

COMMERCIAL_MODE

`api`, `cron`

GOOGLE_CLIENT_ID

`api`, `cron`, `worker`

VITE_MCP_URL

`web`

POLAR_MODE

`api`, `cron`

MICROSOFT_CLIENT_ID

`api`, `cron`, `worker`

OTEL_EXPORTER_OTLP_ENDPOINT

`api`, `cron`, `worker`, `mcp`, `web`

GOOGLE_CLIENT_SECRET

`api`, `cron`, `worker`

MCP_PORT

`mcp`

VITE_GOOGLE_ADS_ID

Optional. Google Ads conversion tracking ID (e.g., `AW-123456789`)

POSTGRES_PASSWORD

`standalone`

OTEL_EXPORTER_OTLP_HEADERS

`api`, `cron`, `worker`, `mcp`, `web`

keeper-mcp

> [!TIP] > > Pin your images to a major.minor version tag (e.g., `2.9`) rather than `latest`. This prevents breaking changes from automatically applying when you pull new images. ## Prerequisites ### Docker & Docker Compose In order to install Docker Compose, please refer to the [official Docker documentation.](https://docs.docker.com/compose/install/). ### Google OAuth Credentials > [!TIP] > > This is optional, although you will not be able to set Google Calendar as a destination without this. Reference the [official Google Cloud Platform documentation](https://support.google.com/cloud/answer/15549257) to generate valid credentials for Google OAuth. You must grant your consent screen the `calendar.events`, `calendar.calendarlist.readonly`, and `userinfo.email` scopes. Once this is configured, set the client ID and client secret as the `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` environment variables at runtime. ### Microsoft Azure Credentials > [!TIP] > > Once again, this is optional. If you do not configure this, you will not be able to configure Microsoft Outlook as a destination. Microsoft does not appear to do documentation well, the best I could find for non-legacy instructions on configuring OAuth is this [community thread.](https://learn.microsoft.com/en-us/answers/questions/4705805/how-to-set-up-oauth-2-0-for-outlook). The required scopes are `Calendars.ReadWrite`, `User.Read`, and `offline_access`. The client ID and secret for Microsoft go into the `MICROSOFT_CLIENT_ID` and `MICROSOFT_CLIENT_SECRET` environment variables respectively. ## Standalone Container While you'd typically want to run containers granularly, if you just want to get up and running, a convenience image `keeper-standalone:2.9` has been provided. This container contains the `cron`, `worker`, `web`, `api` services as well as a configured `redis`, `database`, and `caddy` instance that puts everything behind the same port. While this is the easiest way to spin up Keeper, it is not recognized as best-practice. ### Generate `keeper-standalone` Environment Variables The following will generate a `.env` file that contains the key used to generate sessions, as well as the key that is used to encrypt CalDAV credentials at rest. > [!IMPORTANT] > > If you plan on accessing Keeper from a URL _other than_ http://localhost, > you will need to set the `TRUSTED_ORIGINS` environment variable. This should > be a comma-delimited list of protocol-hostname inclusive origins you will be using. > > Here is an example where we would be accessing Keeper from the LAN IP and where we > are routing Keeper through a reverse proxy that hosts it at https://keeper.example.com/ > > ```bash > TRUSTED_ORIGINS=http://10.0.0.2,https://keeper.example.com > ``` > > Without this, you will fail CSRF checks on the `better-auth` package. ```bash cat > .env << EOF # BETTER_AUTH_SECRET and ENCRYPTION_KEY are required. # TRUSTED_ORIGINS is required if you plan on accessing Keeper from an # origin other than http://localhost/ BETTER_AUTH_SECRET=$(openssl rand -base64 32) ENCRYPTION_KEY=$(openssl rand -base64 32) TRUSTED_ORIGINS= GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= MICROSOFT_CLIENT_ID= MICROSOFT_CLIENT_SECRET= EOF ``` ### Run `keeper-standalone` with Docker If you'd like to just run using the Docker CLI, you can use the following command. I would however recommend [using a compose.yaml](#run-standalone-with-docker-compose) file. ```bash docker run -d \ -p 80:80 \ -v keeper-data:/var/lib/postgresql/data \ --env-file .env \ ghcr.io/ridafkih/keeper-standalone:2.9 ``` ### Run `keeper-standalone` with Docker Compose If you'd prefer to use a `compose.yaml` file, the following is an example. Remember to [populate your .env file first](#generate-keeper-standalone-environment-variables). ```yaml services: keeper: image: ghcr.io/ridafkih/keeper-standalone:2.9 ports: - "80:80" volumes: - keeper-data:/var/lib/postgresql/data environment: BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET} ENCRYPTION_KEY: ${ENCRYPTION_KEY} TRUSTED_ORIGINS: ${TRUSTED_ORIGINS} GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-} GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-} MICROSOFT_CLIENT_ID: ${MICROSOFT_CLIENT_ID:-} MICROSOFT_CLIENT_SECRET: ${MICROSOFT_CLIENT_SECRET:-} volumes: keeper-data: ``` Once that's configured, you can launch Keeper using the following command. ```bash docker compose up -d ``` With all said and done, you can access Keeper at http://localhost/. You can use a reverse-proxy like Nginx or Caddy to put Keeper behind a domain on your network. ## Collective Services Image If you'd like to bring your own Redis and PostgreSQL, you can use the `keeper-services` image. This contains the `cron`, `web` and `api` services in one. ### Generate `keeper-services` Environment Variables ```bash cat > .env << EOF # DATABASE_URL and REDIS_URL are required. # *_CLIENT_ID and *_CLIENT_SECRET are optional. BETTER_AUTH_SECRET=$(openssl rand -base64 32) ENCRYPTION_KEY=$(openssl rand -base64 32) DATABASE_URL=postgres://keeper:keeper@postgres:5432/keeper REDIS_URL=redis://redis:6379 BETTER_AUTH_URL=http://localhost:3000 GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= MICROSOFT_CLIENT_ID= MICROSOFT_CLIENT_SECRET= EOF ``` ### Run `keeper-services` with Docker Compose Once you've populated your environment variables, you can choose to run `redis` and `postgres` alongside the `keeper-services` image to get up and running. ```yaml services: postgres: image: postgres:17 environment: POSTGRES_USER: keeper POSTGRES_PASSWORD: keeper POSTGRES_DB: keeper volumes: - postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U keeper -d keeper"] interval: 5s timeout: 5s retries: 5 redis: image: redis:7-alpine volumes: - redis-data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 5s retries: 5 keeper: image: ghcr.io/ridafkih/keeper-services:latest environment: DATABASE_URL: ${DATABASE_URL} REDIS_URL: ${REDIS_URL} BETTER_AUTH_URL: ${BETTER_AUTH_URL} BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET} ENCRYPTION_KEY: ${ENCRYPTION_KEY} GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-} GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-} MICROSOFT_CLIENT_ID: ${MICROSOFT_CLIENT_ID:-} MICROSOFT_CLIENT_SECRET: ${MICROSOFT_CLIENT_SECRET:-} ports: - "3000:3000" depends_on: postgres: condition: service_healthy redis: condition: service_healthy volumes: postgres-data: redis-data: ``` Once that's configured, you can launch Keeper using the following command. ```bash docker compose up -d ``` ## Individual Service Images While running services individually is considered best-practice, it is verbose and more complicated to configure. Each service is hosted in its own image. ### Generate Individual Service Environment Variables ```bash cat > .env << EOF # The only optional variables are *_CLIENT_ID, *_CLIENT_SECRET BETTER_AUTH_SECRET=$(openssl rand -base64 32) ENCRYPTION_KEY=$(openssl rand -base64 32) VITE_API_URL=http://api:3001 POSTGRES_USER=keeper POSTGRES_PASSWORD=keeper POSTGRES_DB=keeper REDIS_URL=redis://redis:6379 BETTER_AUTH_URL=http://localhost:3000 GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= MICROSOFT_CLIENT_ID= MICROSOFT_CLIENT_SECRET= EOF ``` ### Configure Individual Service `compose.yaml` ```yaml services: postgres: image: postgres:17 environment: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB} volumes: - postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U keeper -d keeper"] interval: 5s timeout: 5s retries: 5 redis: image: redis:7-alpine volumes: - redis-data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 5s retries: 5 api: image: ghcr.io/ridafkih/keeper-api:latest environment: API_PORT: 3001 DATABASE_URL: postgres://keeper:keeper@postgres:5432/keeper REDIS_URL: redis://redis:6379 BETTER_AUTH_URL: ${BETTER_AUTH_URL} BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET} ENCRYPTION_KEY: ${ENCRYPTION_KEY} GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-} GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-} MICROSOFT_CLIENT_ID: ${MICROSOFT_CLIENT_ID:-} MICROSOFT_CLIENT_SECRET: ${MICROSOFT_CLIENT_SECRET:-} depends_on: postgres: condition: service_healthy redis: condition: service_healthy cron: image: ghcr.io/ridafkih/keeper-cron:latest environment: DATABASE_URL: postgres://keeper:keeper@postgres:5432/keeper REDIS_URL: redis://redis:6379 ENCRYPTION_KEY: ${ENCRYPTION_KEY} GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-} GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-} MICROSOFT_CLIENT_ID: ${MICROSOFT_CLIENT_ID:-} MICROSOFT_CLIENT_SECRET: ${MICROSOFT_CLIENT_SECRET:-} depends_on: postgres: condition: service_healthy redis: condition: service_healthy web: image: ghcr.io/ridafkih/keeper-web:latest environment: VITE_API_URL: ${VITE_API_URL} PORT: 3000 ports: - "3000:3000" depends_on: api: condition: service_started volumes: postgres-data: redis-data: ``` Once that's configured, you can launch Keeper using the following command. ```bash docker compose up -d ``` # MCP (Model Context Protocol) Keeper includes an optional MCP server that lets AI agents (such as Claude) access your calendar data through a standardized protocol. The MCP server authenticates via OAuth 2.1 with a consent flow hosted by the web application. ## Available Tools

TRUSTED_ORIGINS

`api`

POLAR_PRO_MONTHLY_PRODUCT_ID

Optional. Polar monthly product ID to power in-app upgrade links.

get_event_count

Get the total number of calendar events synced to Keeper.

POLAR_PRO_YEARLY_PRODUCT_ID

Optional. Polar yearly product ID to power in-app upgrade links.

VITE_VISITORS_NOW_TOKEN

Optional. [visitors.now](https://visitors.now) token for analytics

VITE_GOOGLE_ADS_CONVERSION_LABEL

Optional. Google Ads conversion label for purchase tracking

get_events

Get calendar events within a date range. Accepts ISO 8601 datetimes and an IANA timezone identifier.