A Model Context Protocol (MCP) server that integrates with the Tweek API to manage calendars, tasks, and custom colors. Built with TypeScript and ESM modules.
- Secure Authentication: Interactive sign-in with automatic token refresh
- Calendar Management: List calendars with role-based access
- Task CRUD: Create, read, update, and delete tasks with validation
- Custom Colors: Fetch user-specific color preferences
- Pagination Support: Forward-only pagination with
nextDocId - Error Handling: Structured error mapping with retry logic for transient failures
- Token Security: Encrypted token storage with file permissions (mode 600)
- Node 20+
- PNPM 10+
- Tweek API key (obtain from Tweek)
# Install dependencies
pnpm install
# Build the project
pnpm buildSet your Tweek API key (required):
export TWEEK_API_KEY="your-api-key-here"See Configuration for all available environment variables.
Before starting the server, you need to authenticate once:
pnpm auth:signinYou'll be prompted to enter your Tweek email and password:
Enter your Tweek email: user@example.com
Enter your Tweek password: ********
✅ Authentication successful! Tokens saved.
Your credentials are used only in memory and never persisted. The command exchanges them for tokens and securely saves them to ~/.tweek-mcp/tokens.json (or your configured TWEEK_TOKENS_PATH).
Non-interactive (for automation/CI):
echo "your-password" | pnpm auth:signin --email user@example.com --password-stdinImport existing refresh token:
pnpm auth:import --refresh-token "your-refresh-token"pnpm startThe server will:
- Load tokens from the token file
- Automatically refresh the
idTokenif needed - Start listening for MCP requests
For development with auto-reload:
pnpm devConfigure via environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
TWEEK_API_KEY |
✅ Yes | - | Your Tweek API key |
TWEEK_API_BASE |
No | https://tweek.so/api/v1 |
Tweek API base URL |
TWEEK_TOKENS_PATH |
No | ~/.tweek-mcp/tokens.json |
Path to token storage file |
TWEEK_REQUEST_TIMEOUT_MS |
No | 30000 |
HTTP request timeout (milliseconds) |
TWEEK_TOKEN_REFRESH_BUFFER_SEC |
No | 60 |
Proactive token refresh window (seconds) |
TWEEK_ENCRYPTION_KEY |
No | - | 32-byte key for AES-GCM token encryption |
export TWEEK_API_KEY="ak_live_..."
export TWEEK_TOKENS_PATH="/secure/volume/tweek-tokens.json"
export TWEEK_ENCRYPTION_KEY="your-32-byte-encryption-key-here"
export TWEEK_REQUEST_TIMEOUT_MS="45000"
export TWEEK_TOKEN_REFRESH_BUFFER_SEC="120"The server exposes the following MCP tools:
listCalendars()
List all calendars accessible to the authenticated user.
Returns:
interface ListCalendarsResponse {
calendars: Calendar[]
}
interface Calendar {
id: string
name: string
role: 'ROLE_OWNER' | 'ROLE_EDITOR' | 'ROLE_VIEWER'
color?: string
// ... other calendar fields
}listTasks({ calendarId, startAt?, dateFrom?, dateTo? })
List tasks with optional pagination and date filtering.
Parameters:
calendarId(required): Calendar IDstartAt(optional): Pagination cursor from previous responsedateFrom(optional): ISO 8601 date to filter tasks fromdateTo(optional): ISO 8601 date to filter tasks to
Returns:
interface ListTasksResponse {
pageSize: number
nextDocId?: string
data: Task[]
}getTask({ taskId })
Retrieve a single task by ID.
createTask({ task })
Create a new task.
Parameters:
task.calendarId(required): Calendar IDtask.text(required): Task titletask.date(optional): ISO 8601 datetask.freq(optional): Recurrence frequency (0-7)task.checklist(optional): Array of checklist items- ... see Tweek API docs for all fields
Returns:
interface CreateTaskResponse {
id: string
}updateTask({ taskId, patch })
Update an existing task.
Returns: Full updated task object
deleteTask({ taskId })
Delete a task.
Returns:
interface DeleteTaskResponse {
success: true
}getCustomColors()
Fetch custom colors for the authenticated user. The userId is automatically extracted from the idToken.
Returns:
interface GetCustomColorsResponse {
colors: Color[]
}
interface Color {
id: string
hex: string
name?: string
}The server validates task inputs:
freq: Must be integer 0-7 (Tweek recurrence enum)notifyAt,date,isoDate,dtStart: Must be valid ISO 8601 date/datetimecalendarId: Required non-empty string on creationchecklist: Each item must have non-emptytextfield
Add to your Claude Desktop configuration (claude_desktop_config.json):
{
"mcpServers": {
"tweek": {
"command": "node",
"args": ["/path/to/tweek-mcp/dist/index.js"],
"env": {
"TWEEK_API_KEY": "your-api-key-here"
}
}
}
}The server uses standard MCP JSON-RPC over stdio. Configure your client to:
- Run
node dist/index.js(orpnpm start) - Pass
TWEEK_API_KEYin environment - Ensure token file exists at configured path
# Install dependencies
pnpm install
# Run tests
pnpm test
# Run linter
pnpm lint
# Build TypeScript
pnpm build
# Development mode (auto-reload)
pnpm devsrc/
├── auth/ # Authentication (AuthManager, TokenStore, IdentityClient)
├── cli/ # CLI commands (auth signin/import)
├── config/ # Configuration loading
├── http/ # HTTP client with retry logic
├── logging/ # Structured logging
├── tests/ # Test suite
├── tools/ # MCP tools (calendars, tasks, colors, validation)
├── tweek/ # Tweek API client and types
└── index.ts # Server bootstrap
Problem: Server cannot find the token file.
Solution: Run authentication first:
pnpm auth:signinProblem: Tokens are expired or invalid.
Solution: Re-authenticate:
pnpm auth:signinProblem: Token file permissions are incorrect.
Solution: The server automatically sets mode 600. If issues persist, manually fix:
chmod 600 ~/.tweek-mcp/tokens.jsonProblem: Token refresh fails silently.
Solution: Check logs for refresh errors. Verify your refresh token is still valid by re-authenticating.
Problem: Requests timing out to Tweek API.
Solution: Increase timeout:
export TWEEK_REQUEST_TIMEOUT_MS="60000"For Docker or containerized environments:
-
Mount a persistent volume for tokens:
docker run -v /secure/volume:/tokens \ -e TWEEK_TOKENS_PATH=/tokens/tweek.json \ -e TWEEK_API_KEY=your-key \ tweek-mcp
-
Consider using
TWEEK_ENCRYPTION_KEYfor additional security -
Pre-authenticate using
auth:importwith a refresh token
- Tokens stored with file permissions 600 (user-only access)
- Optional AES-GCM encryption for token file
- Credentials never logged or persisted
- Sensitive headers redacted from logs
- Automatic token refresh before expiry
Comprehensive test suite with:
- Unit tests for validation, retry logic, encryption
- Integration tests for HTTP client and auth flows
- CLI end-to-end tests
- Contract tests for MCP tools
# Run all tests
pnpm test
# Run specific test file
pnpm test src/tests/validation.test.ts
# Run tests matching pattern
pnpm test -- -t "should validate task input"- Follow the existing code style (enforced by ESLint)
- Add tests for new features
- Ensure
pnpm lintandpnpm testpass - Update documentation as needed
MIT