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 build
Set 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:signin
You'll be prompted to enter your Tweek email and password:
Enter your Tweek email: [email protected]
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 [email protected] --password-stdin
Import existing refresh token:
pnpm auth:import --refresh-token "your-refresh-token"
pnpm start
The server will:
- Load tokens from the token file
- Automatically refresh the
idToken
if needed - Start listening for MCP requests
For development with auto-reload:
pnpm dev
Configure 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-emptytext
field
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_KEY
in 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 dev
src/
├── 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:signin
Problem: Tokens are expired or invalid.
Solution: Re-authenticate:
pnpm auth:signin
Problem: Token file permissions are incorrect.
Solution: The server automatically sets mode 600. If issues persist, manually fix:
chmod 600 ~/.tweek-mcp/tokens.json
Problem: 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_KEY
for additional security -
Pre-authenticate using
auth:import
with 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 lint
andpnpm test
pass - Update documentation as needed
MIT