google-task-ifttt-webhook-mcp
by doitian
README.md
# google-task-ifttt-webhook-mcp
MCP server running on Cloudflare Workers that exposes a `create_google_task` tool.
Sends tasks to Google Tasks via the [IFTTT Maker webhook](https://ifttt.com/maker_webhooks).
Authentication is handled by **Cloudflare Access** (Managed OAuth). The worker validates the `Cf-Access-Jwt-Assertion` JWT against Access JWKS.
## How it works
```
MCP Client Cloudflare Access Cloudflare Worker IFTTT
| | | |
|-- OAuth login ------------>| | |
|<-- access granted ---------| | |
| | | |
|-- POST /mcp -------------->|-- Cf-Access-Jwt-Assertion->| |
| | |-- POST JSON ------->|
| | |<-- "Congratulations"|
|<-- result -----------------|<-------------------------| |
```
## Prerequisites
- [Node.js](https://nodejs.org/) 18+
- [Cloudflare account](https://dash.cloudflare.com/) with Workers enabled
- [Cloudflare Access](https://developers.cloudflare.com/cloudflare-one/applications/) (requires Cloudflare Zero Trust)
- IFTTT account with the [Maker Webhooks](https://ifttt.com/maker_webhooks) service connected
- A Google Tasks applet on IFTTT triggered by a JSON web request event named `task`
## Setup
### 1. Install dependencies
```pwsh
npm install
```
### 2. Get your IFTTT Maker key
1. Go to [IFTTT Maker Webhooks](https://ifttt.com/maker_webhooks)
2. Click **Documentation**
3. Your key is the last path segment of the URL shown:
`https://maker.ifttt.com/use/{YOUR_KEY}`
4. Set it as a Worker secret:
```pwsh
npx wrangler secret put IFTTT_MAKER_TASK_KEY
```
### 3. Set up the IFTTT applet
Create an applet on IFTTT:
- **If**: Maker Webhooks → Receive a web request with a JSON payload, event name `task`
- **Then**: Google Tasks → Create a task
- Title: `{{JsonPayload.title}}`
- Notes: `{{JsonPayload.notes}}`
- Due date: `{{JsonPayload.due}}`
The worker posts to:
`https://maker.ifttt.com/trigger/task/json/with/key/{KEY}`
with a JSON body like `{ "title": "...", "notes": "...", "due": "..." }`.
### 4. Configure Cloudflare Access
1. Go to [Cloudflare Zero Trust dashboard](https://one.dash.cloudflare.com/)
2. Navigate to **Access** → **Applications** → **Add an application** → **Self-hosted**
3. Configure:
- **Application name**: `google-task-mcp`
- **Application domain**: your worker hostname (custom domain or `*.workers.dev`)
- **Identity providers**: Select your preferred IdP (Google, GitHub, email OTP, etc.)
- **Policy**: Create an allow policy for the users who should access the MCP server
- **Managed OAuth (Beta)**: Must be enabled
4. Note your **team domain** (e.g. `myteam.cloudflareaccess.com`) and the **Application Audience (AUD)** tag
5. Set them as Worker secrets:
```pwsh
npx wrangler secret put CF_ACCESS_TEAM_DOMAIN
npx wrangler secret put CF_ACCESS_AUD
```
`CF_ACCESS_AUD` is optional but recommended (enables audience validation on the JWT).
### 5. Deploy
```pwsh
npm run deploy
```
### 6. Test locally (optional)
```pwsh
npm run dev
```
The worker is available at `http://localhost:8787`. When `CF_ACCESS_TEAM_DOMAIN` is unset, any present `Cf-Access-Jwt-Assertion` header is accepted (use a dummy value for local calls).
### 7. Configure the MCP client
Point the client at the worker URL (path `/` or `/mcp`). Auth is Cloudflare Access Managed OAuth — the client completes Access login; Access injects `Cf-Access-Jwt-Assertion` on requests to the worker.
#### opencode
```json
{
"mcp": {
"google-tasks": {
"type": "remote",
"url": "https://google-task-mcp.yourdomain.com/mcp",
"oauth": {}
}
}
}
```
#### Claude Desktop / other streamable HTTP clients
Use your client's remote MCP + OAuth settings with:
`https://google-task-mcp.yourdomain.com/mcp`
## Tool: `create_google_task`
| Parameter | Required | Description |
|-----------|----------|-------------|
| `title` | yes | Single-line task title |
| `notes` | no | Optional detailed notes. `\n` for line breaks |
| `due` | no | Optional due date in ISO 8601 UTC (e.g. `2026-07-25T23:59:59Z`) |
### Example MCP call
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "create_google_task",
"arguments": {
"title": "Buy groceries",
"notes": "Milk, eggs, bread",
"due": "2026-07-25T23:59:59Z"
}
}
}
```
## Secrets / env
| Name | Required | Description |
|------|----------|-------------|
| `IFTTT_MAKER_TASK_KEY` | yes | IFTTT Maker Webhooks key |
| `CF_ACCESS_TEAM_DOMAIN` | prod | Cloudflare Zero Trust team domain (e.g. `myteam.cloudflareaccess.com`). Omit locally to skip JWKS verification |
| `CF_ACCESS_AUD` | no | Access application audience tag (enables audience validation) |
| `ICON_URL` | no | Override the default server icon URL returned on `initialize` |
Set secrets via:
```pwsh
npx wrangler secret put <NAME>
```
## Endpoints
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/` or `/mcp` | MCP JSON-RPC (requires valid `Cf-Access-Jwt-Assertion`) |
| `OPTIONS` | `*` | CORS preflight |
Supported MCP methods: `initialize`, `notifications/initialized`, `ping`, `tools/list`, `tools/call`.
## Troubleshooting
**"Unauthorized: Cloudflare Access authentication required"**
Missing or invalid `Cf-Access-Jwt-Assertion`. Ensure Access protects the worker hostname, Managed OAuth is enabled, and the client completed Access login. Locally, send any non-empty `Cf-Access-Jwt-Assertion` header when `CF_ACCESS_TEAM_DOMAIN` is unset.
**"IFTTT webhook failed (HTTP 404)"**
The Maker key is invalid or no applet listens for event `task`. Verify the key and applet event name.
**"IFTTT_MAKER_TASK_KEY is not configured on the server."**
```pwsh
npx wrangler secret put IFTTT_MAKER_TASK_KEY
```
Then redeploy.
**JWT verification failures in production**
Confirm `CF_ACCESS_TEAM_DOMAIN` matches your team domain exactly (hostname only, or full `https://…` URL). If set, `CF_ACCESS_AUD` must match the Access application AUD tag.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues