┌─────────────────┐
│ MCP Client │
│ (ChatGPT) │
└────────┬────────┘
│ MCP Protocol (HTTP)
▼
┌─────────────────────────────────────────┐
│ server-http.ts │
│ ┌───────────────────────────────────┐ │
│ │ OAuth Handler (oauth/) │ │
│ │ /authorize, /callback, /token │ │
│ └───────────────────────────────────┘ │
│ ┌───────────────────────────────────┐ │
│ │ MCP Transport │ │
│ │ WebStandardStreamableHTTP │ │
│ └───────────────────────────────────┘ │
└────────────────┬────────────────────────┘
│
┌────────────────▼────────────────────────┐
│ createMcpServer.ts │
│ ┌───────────────────────────────────┐ │
│ │ Tool Handlers (tools/) │ │
│ │ tasklists, task_create, etc. │ │
│ └───────────────────────────────────┘ │
└────────────────┬────────────────────────┘
│
┌────────────────▼────────────────────────┐
│ GoogleTasksService.ts │
│ Google Tasks API wrapper │
└────────────────┬────────────────────────┘
│ OAuth 2.0 / REST
▼
┌─────────────────────────┐
│ Google Tasks API │
└─────────────────────────┘
src/
├── server-http.ts # HTTP entry point
├── server-stdio.ts # stdio entry point (not working with OAuth yet)
├── mcp/
│ ├── createMcpServer.ts # MCP server factory
│ ├── schemas/
│ │ └── task.ts # Zod validation schemas
│ └── tools/ # MCP tool handlers
├── oauth/ # OAuth 2.0 implementation
│ ├── handlers/ # Endpoint handlers
│ └── ...
├── services/
│ └── GoogleTasksService.ts
└── utils/
├── createSuccessResponse.ts
└── createErrorResponse.ts
HTTP server using Bun's native Bun.serve(). Handles:
- OAuth request routing (delegates to
oauth/router.ts) - MCP session management (one McpServer + Transport per session)
- Access token extraction from Authorization header
- Accept header normalization for compatibility
stdio transport for local MCP clients (Cursor, Claude Desktop). Currently requires environment variables for auth - OAuth not yet supported.
Factory function that creates configured McpServer instance:
- Registers all tools
- Creates GoogleTasksService with appropriate auth mode (access token or refresh token)
- Exports
SERVER_INFOfor version identification
Shared Zod schemas:
taskSchema- Google Task object structuredueDateSchema- RFC3339 date format validation
Each file exports:
inputSchema- Zod schema for tool parametersoutputSchema- Zod schema for response (documentation only)create*Handler(service)- Factory returning tool callback
Pattern:
export function createSomeHandler(
service: GoogleTasksService
): ToolCallback<typeof inputSchema> {
return async (args) => {
try {
const result = await service.someMethod(args);
return createSuccessResponse(result);
} catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
return createErrorResponse(message);
}
};
}Routes requests to endpoint handlers:
/.well-known/*→wellKnown.ts- OAuth/OIDC discovery metadata/authorize→authorize.ts- Redirects to Google OAuth/callback→callback.ts- Receives Google auth code, redirects to client/token→token.ts- Exchanges codes for tokens, handles refresh/register→register.ts- Dynamic Client Registration (RFC 7591)
In-memory storage for:
- Pending authorizations (state → redirect_uri mapping)
- Token data (access_token → refresh_token mapping for auto-refresh)
- Registered clients (DCR)
TypeScript interfaces for OAuth entities.
Creates OAuthConfig from environment variables.
Utility functions: generateRandomString, jsonResponse.
Google Tasks API wrapper using googleapis library. Supports two auth modes:
- Refresh token mode: Has client credentials, can auto-refresh tokens
- Access token mode: Token only, expires after ~1h
Methods:
getTaskLists()- List all task listsgetTasks(listId)- List tasks in a listcreateTask(task, listId)- Create taskupdateTask(listId, taskId, updates)- Update taskdeleteTask(listId, taskId)- Delete taskcheckConnection()- Health check
Creates MCP CallToolResult with both content (text) and structuredContent (object).
Creates MCP CallToolResult with isError: true.
Client Server Google
│ │ │
│ GET /authorize │ │
│ ?redirect_uri=... │ │
│ &state=... │ │
│────────────────────────>│ │
│ │ │
│ 302 Redirect │ │
│<────────────────────────│ │
│ │ │
│ GET accounts.google.com/o/oauth2/auth │
│─────────────────────────────────────────────────────>
│ │ │
│ [User logs in] │
│ │ │
│ 302 Redirect to /callback?code=... │
│<─────────────────────────────────────────────────────
│ │ │
│ GET /callback?code=... │ │
│────────────────────────>│ │
│ │ │
│ 302 Redirect │ │
│ ?code=our_code │ │
│<────────────────────────│ │
│ │ │
│ POST /token │ │
│ code=our_code │ │
│────────────────────────>│ │
│ │ POST /token │
│ │ code=google_code │
│ │──────────────────────────>│
│ │ │
│ │ {access_token, refresh} │
│ │<──────────────────────────│
│ │ │
│ {access_token} │ │
│<────────────────────────│ │
Each MCP session gets its own:
McpServerinstanceWebStandardStreamableHTTPServerTransportinstanceGoogleTasksServiceinstance (with user's tokens)
This is required because McpServer can only be connected to one transport at a time.
Sessions are stored in a Map keyed by session ID (UUID generated on first request).
- Tokens stored in memory only (not persisted)
- Tokens never reach the LLM - only tool results
- HTTPS required in production
- PKCE (S256) supported for OAuth
- Scope limited to
https://www.googleapis.com/auth/tasks