Skip to main content
Glama
horatio8

Kitchen MCP Server

by horatio8

Kitchen MCP Server

A hosted, multi-tenant Model Context Protocol server that exposes the Kitchen.co client-portal API to MCP-compatible AI clients (Claude, ChatGPT, Cursor, etc.) over secure Streamable HTTP.

  • Per-request Kitchen credentials passed via HTTP headers — one deployment can serve many tenants without ever storing their API keys.

  • Dedicated typed tools for tasks, docs, clients, companies, invoices, conversations, messages, boards, lists, folders, milestones, labels, webhooks, files, and members.

  • kitchen_request escape hatch for any endpoint not yet wrapped.

  • Defence-in-depth: helmet, CORS allow-list, DNS-rebinding protection, per-tenant rate-limit, payload caps, log redaction, optional gate token.

Quick start (local)

npm install
cp .env.example .env       # configure as needed
npm run build
npm start
# server is now listening on http://127.0.0.1:3000/mcp

Related MCP server: k8scortex-mcp

Quick start (Docker)

docker build -t kitchen-mcp .
docker run --rm -p 3000:3000 \
  -e ALLOWED_ORIGINS=https://claude.ai \
  -e MCP_GATE_TOKEN=$(openssl rand -hex 32) \
  kitchen-mcp

How clients connect

Point any MCP client at https://<your-host>/mcp (Streamable HTTP transport). Send the following headers on every request:

Header

Required

Description

X-Kitchen-API-Key

yes (per-tenant)

Kitchen bearer token from Settings → API & Webhooks → API Tokens

X-Kitchen-Workspace

yes (per-tenant)

Workspace subdomain (e.g. acme for acme.kitchen.co)

Authorization: Bearer <MCP_GATE_TOKEN>

required if MCP_GATE_TOKEN is set

Optional shared bearer that gates access to the MCP endpoint itself

Mcp-Session-Id

after initialise

Session ID returned by the server on initialize

Both Kitchen headers may instead be supplied via the KITCHEN_API_KEY / KITCHEN_WORKSPACE env vars for single-tenant deployments.

Claude Desktop example

{
  "mcpServers": {
    "kitchen": {
      "transport": {
        "type": "http",
        "url": "https://kitchen-mcp.example.com/mcp",
        "headers": {
          "Authorization": "Bearer YOUR_MCP_GATE_TOKEN",
          "X-Kitchen-API-Key": "kc_live_xxxxxxxxxxxxxxxx",
          "X-Kitchen-Workspace": "acme"
        }
      }
    }
  }
}

Security model

Layer

Control

Transport

HTTPS terminated upstream; the server speaks plain HTTP behind your load balancer.

Endpoint access

Optional shared bearer gate (MCP_GATE_TOKEN) compared in constant time.

Tenancy

Kitchen API key + workspace are per request; never persisted; redacted from logs.

Origin

DNS-rebinding protection enforced by the MCP SDK transport; CORS allow-list configurable.

Rate limiting

60 req/min default (matches Kitchen's published limit); per-tenant fingerprint to keep tenants isolated.

Payload

JSON body capped at 1 MiB; Kitchen response bodies capped at 2 MiB before parsing.

Path safety

kitchen_request passthrough rejects paths outside /api/... and disallows //.

Process

Container runs as non-root with healthcheck.

Note: You still need to terminate TLS upstream (Cloud Run, Fly.io, Vercel, nginx, ALB, etc.). The server is designed to sit behind a proxy.

Available tools

Tool names follow the pattern kitchen_<verb>_<resource>. A non-exhaustive overview:

  • Tasks: kitchen_list_tasks, kitchen_get_task, kitchen_create_task, kitchen_update_task, kitchen_delete_task, kitchen_toggle_task_completion, kitchen_move_tasks

  • Subtasks: kitchen_list_subtasks, kitchen_create_subtask, kitchen_update_subtask, kitchen_delete_subtask

  • Task labels/members/comments: kitchen_add_task_label, kitchen_remove_task_label, kitchen_add_task_member, kitchen_remove_task_member, kitchen_list_task_comments, kitchen_create_task_comment

  • Docs: kitchen_list_docs, kitchen_get_doc, kitchen_create_doc, kitchen_update_doc, kitchen_archive_doc, kitchen_restore_doc, kitchen_move_doc, kitchen_delete_doc + doc memberships

  • Clients & companies: full CRUD

  • Invoices & recurring invoices: list/get/create/update/archive/restore/delete

  • Conversations & messages: full CRUD + archive/restore

  • Structure: boards, lists, folders, milestones, labels, members, templates

  • Webhooks: CRUD

  • Files: kitchen_create_file_upload, kitchen_complete_file, kitchen_get_file, kitchen_delete_file

  • Low-level: kitchen_request — any /api/... path

Call tools/list from your MCP client for the full, schema-rich catalogue.

Webhook receiver

This server also runs a verified Kitchen-webhook ingress at POST /webhooks/kitchen. It is off by default — enable it by setting KITCHEN_WEBHOOK_SECRETS.

Set up

  1. In Kitchen, create a webhook (Settings → API & Webhooks → Webhooks) pointed at https://<your-host>/webhooks/kitchen, subscribe to the events you want, and copy the generated secret.

  2. Set KITCHEN_WEBHOOK_SECRETS=<secret> on the server. Multiple secrets (comma-separated) are supported so you can run several Kitchen workspaces or rotate without downtime — the receiver tries each in constant time.

  3. (Optional) Set KITCHEN_WEBHOOK_FORWARD_URL if your actual handler lives elsewhere. The verifier POSTs the verified event JSON to that URL with an optional X-Forward-Token header (KITCHEN_WEBHOOK_FORWARD_TOKEN).

Guarantees

  • Signature verification. HMAC-SHA256 over the raw request bytes as received, compared in constant time. Matches Kitchen's best-practices doc exactly. Re-encoding the body is not used — that would be fragile across JSON serializers.

  • Idempotency. Each event.id is processed at most once in a 24-hour window (Kitchen retries up to 3 times with backoff). Duplicates ack 200 so Kitchen stops retrying.

  • Asynchronous dispatch. The receiver acks Kitchen as soon as the signature and shape are verified; downstream forwarding is fire-and-forget so a slow consumer can't time out the webhook.

  • Body cap. 1 MiB hard cap.

  • No rate-limit on the webhook path. Kitchen's retries should never be dropped at the edge.

Response codes

Code

Meaning

200 {"ok":true}

Verified, accepted, dispatching now.

200 {"ok":true,"duplicate":true}

Replay of an event we already processed.

401 {"error":"invalid_signature"}

Missing or wrong Signature header.

400 {"error":"invalid_json"} / invalid_event_shape / empty_body

Malformed payload.

503 {"error":"webhook_receiver_not_configured"}

KITCHEN_WEBHOOK_SECRETS is empty.

Extending the dispatcher

By default the dispatcher just logs the event and (optionally) forwards it. To wire in your own handler, edit src/webhooks/dispatcher.tsWebhookDispatcher.dispatch(event) is the single integration point. Switch on event.type (e.g. task.created, invoice.paid, client.updated) and call your code from there.

async dispatch(event: KitchenWebhookEvent): Promise<void> {
  switch (event.type) {
    case "invoice.paid":     await onInvoicePaid(event.data); break;
    case "task.created":     await onTaskCreated(event.data); break;
    case "client.updated":   await onClientUpdated(event.data, event.previous_attributes); break;
    default: logger.debug({ type: event.type }, "unhandled webhook");
  }
}

Verifying locally

SECRET='whsec_...your_secret...'
PAYLOAD='{"id":"evt_test","type":"task.created","created":1719322973,"data":{}}'
SIG=$(node -e "const c=require('crypto');process.stdout.write(c.createHmac('sha256','$SECRET').update('$PAYLOAD').digest('hex'))")
curl -X POST http://127.0.0.1:3000/webhooks/kitchen \
  -H "Content-Type: application/json" \
  -H "Signature: $SIG" \
  --data "$PAYLOAD"

Deployment recipes

Fly.io

fly launch --no-deploy --copy-config --name kitchen-mcp
fly secrets set MCP_GATE_TOKEN=$(openssl rand -hex 32) \
                ALLOWED_ORIGINS=https://claude.ai
fly deploy

Google Cloud Run

gcloud run deploy kitchen-mcp \
  --source . \
  --region us-central1 \
  --port 3000 \
  --set-env-vars=ALLOWED_ORIGINS=https://claude.ai \
  --set-env-vars=TRUST_PROXY=1 \
  --set-secrets=MCP_GATE_TOKEN=kitchen-mcp-gate:latest

Render / Railway / Heroku

Standard Node web service — set PORT, ALLOWED_ORIGINS, MCP_GATE_TOKEN, run npm start.

Development

npm install
npm run dev        # tsx watch
npm run typecheck

Rate limits

Kitchen itself caps API usage at 60 requests/minute/user (200 with a raised limit) and 5 file uploads/minute. The MCP server applies its own per-tenant 120 req/min default at the HTTP edge; tune via HTTP_RATE_LIMIT_PER_MINUTE. Retries on 429/5xx use the Retry-After header when present.

License

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    D
    maintenance
    A production-grade MCP server providing a secure, natural-language interface to Kubernetes for developers and AI agents, with multi-cluster routing, OIDC authentication, RBAC, and audit logging.
    75
    27 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    An MCP server that acts as a secure, multi-tenant proxy to the InvoiceShelf API, enabling AI assistants to manage invoices, estimates, customers, payments, expenses, and more with 109 tools and TOON compression for efficient token usage.
    GPL 3.0
  • A
    license
    Not graded
    quality
    C
    maintenance
    A self-hosted MCP server that provides AI assistants with secure access to multiple QuickBooks Online company files through a single connection, featuring a company registry, OAuth-based authorization, and comprehensive read/write tools.
    1
    MIT