Tweek MCP Server
Manage your Tweek tasks and calendars directly from AI assistants — Claude, ChatGPT, Cursor, and any MCP-compatible client. The server is hosted by Tweek; there is nothing to install or self-host.

Setup
Add the server to your MCP client configuration:
{
"mcpServers": {
"tweek": {
"type": "http",
"url": "https://tweek.so/mcp"
}
}
}
Client-specific locations:
| Client | Where |
|---|---|
| Claude Code | claude mcp add --transport http tweek https://tweek.so/mcp, or add the block to .mcp.json in the project root (~/.claude/.mcp.json globally) |
| Claude Desktop / claude.ai | Settings → Connectors → Add custom connector → the URL above |
| ChatGPT | Settings → Connectors → add an MCP server with the URL above (availability depends on plan) |
| Cursor | Settings → MCP → Add new MCP server → type HTTP, the URL above |
On first use the client opens a browser window to sign in to Tweek and approve access.
Directories
Tweek is listed in these directories, so many clients can add it without pasting the URL:
| Directory | Listing |
|---|---|
| Claude connector directory | Tweek on Claude — see Connect Claude |
| ChatGPT apps | Tweek on ChatGPT |
| Cursor Directory | Tweek on Cursor Directory |
| Official MCP Registry | so.tweek/mcp — the source for VS Code, GitHub Copilot and other registry-based clients |
Authentication
Tweek uses OAuth 2.0 with PKCE (Authorization Code flow). Your client registers itself automatically (Dynamic Client Registration, RFC 7591) and drives the flow — there is no client_id or API key to configure.
- Access tokens are JWTs, valid for 1 hour.
- Refresh tokens are valid for 30 days and rotate on use; refresh is handled automatically.
- Every request is scoped to your account. The assistant acts as you and never sees other users' data.
The assistant is granted these scopes on the consent screen:
| Scope | Grants |
|---|---|
| tasks:read | Read tasks |
| tasks:write | Create, update, and delete tasks |
| calendars:read | Read calendars |
| colors:read | Read custom colors |
| offline_access | Issue a refresh token |
This is the same authorization server as the REST API. See the API reference for the token, refresh, and introspection endpoints.
Tools
Read tools require the matching read scope; write tools require tasks:write. A failed call returns an error result with a message (see Errors) rather than aborting the connection. Results are JSON.
Every tool also returns machine-readable structuredContent, advertised as the tool's output schema in tools/list, so clients get typed results without parsing text. The examples below show each tool's shape; list tools wrap their array under a key (calendars, colors) because structured content must be an object.
list_calendars
List the calendars the user can access, including their lists. Needed to obtain a calendarId. No parameters.
[
{ "id": "CALENDAR_ID", "name": "My calendar", "isDefault": true, "lists": [] }
]
list_tasks
List tasks in a calendar. With both dateFrom and dateTo set (a window of at most 92 days), recurring tasks are automatically expanded into their per-day occurrences — the result matches what the user sees in the app for those dates and comes back in full, without pagination. Computed occurrences carry "virtual": true and an id like <taskId>_yyyyMMdd; passing such an id to update_task, complete_task or delete_task acts on that single occurrence. Without a date window the tool lists stored documents, paginated.
| Parameter | Type | Notes |
|---|---|---|
calendarId | string | Required. From list_calendars |
dateFrom | string | Tasks on/after this date, YYYY-MM-DD |
dateTo | string | Tasks on/before this date, YYYY-MM-DD |
expand | boolean | Defaults to true when both dates are set; pass false to list only stored documents |
timezone | string | The user's IANA timezone (e.g. Europe/Berlin) for timezone-dependent recurrence exceptions; defaults to UTC |
startAt | string | Pagination cursor (the previous page's nextDocId) — non-expanded listing only |
{ "data": [ /* task objects */ ], "pageSize": 100, "nextDocId": "TASK_ID" }
get_task
Fetch a single task.
| Parameter | Type | Notes |
|---|---|---|
taskId | string | Required. |
create_task
Create a task. A task needs a placement to be visible — set date or listId. Field semantics match the REST task object.
| Parameter | Type | Notes |
|---|---|---|
calendarId | string | Required. |
text | string | Required. Task title |
date | string | YYYY-MM-DD — a dated task |
listId | string | List id, used instead of date |
note | string | Free text |
color | string | blank, pink, yellowish; other colors need a paid plan |
done | boolean | Defaults to false |
dtStart, notifyAt, freq, recurrence | — | Reminders & recurrence. Paid plan. See the API reference |
checklist | array | Subtasks, e.g. [{ "text": "Milk" }, { "text": "Eggs", "done": true }]. Give each item its text; the id and done default are set for you. Paid plan. |
{ "id": "GENERATED_TASK_ID" }
update_task
Apply partial updates to a task. Provide taskId plus at least one field to change. calendarId is fixed once a task exists. Sending checklist replaces the whole list — read the task first (get_task), then pass back every subtask you want to keep, each existing one with its id (new ones without), and drop the ones to delete.
For a recurring series, taskId may be a virtual-occurrence id from an expanded list_tasks (updates just that occurrence), and the optional updateType parameter picks the series mode: only_this, this_and_future or all_linked — the same choices the apps offer. With updateType set, only text, note, color, checklist, source, done and notifyAt may change. See the API reference.
{ "id": "TASK_ID", "updated": true }
complete_task
Mark a task done. Takes taskId. A shortcut for update_task with done: true. A virtual-occurrence id completes only that occurrence of a recurring series.
{ "id": "TASK_ID", "done": true }
delete_task
Delete a task. Takes taskId. For a recurring series, the optional updateType picks the mode: only_this removes a single occurrence (also what a virtual-occurrence id does by default), this_and_future ends the series from that occurrence on, all_linked permanently deletes every occurrence.
{ "id": "TASK_ID", "deleted": true }
Bulk tools
Act on many tasks in one call instead of looping a single-item tool. Each takes up to 50 items, runs them independently, and reports which succeeded and which failed — one bad item never aborts the batch. All require tasks:write.
| Tool | Input |
|---|---|
bulk_create_tasks | tasks: array of create_task objects |
bulk_update_tasks | updates: array of { taskId, …fields } |
bulk_complete_tasks | taskIds: array of task ids |
bulk_delete_tasks | taskIds: array of task ids |
{
"succeeded": [ { "index": 0, "id": "TASK_ID" } ],
"failed": [ { "index": 1, "error": "Not found" } ]
}
list_colors
List the user's custom colors. No parameters.
Plans & limits
Advanced fields — reminders (notifyAt), recurrence (freq, recurrence, dtStart) and subtasks — and custom colors require a paid plan. A tool call that uses them on a free account returns an Upgrade required error. Tool calls count against your account's API request quota — in an expanded list_tasks every returned occurrence counts as one read; see the API reference.
Errors
A tool call that fails returns an error result carrying a message, so the assistant can react without dropping the connection. Common cases:
| Message | Meaning |
|---|---|
Insufficient scope | The granted scopes don't cover this tool. Reconnect and approve the needed scopes. |
Upgrade required | A paid-only field or color was used on a free account. |
Reads / Writes quota exhausted | The account's API request quota is spent for the period. |
| Not found | No task or calendar with that id, or no access to it. |
Authorization and transport failures follow the standard OAuth error format documented in the API reference.
Back to Docs