Agent skill
google-oauth-integration
Complete Google OAuth integration architecture including token storage and debugging
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/google-oauth-integration
SKILL.md
Google OAuth Integration
This skill documents the complete Google OAuth integration architecture in Orient, including token storage, tool registration, and debugging procedures.
Triggers
Use this skill when:
- Setting up Google OAuth for a new package or service
- Debugging "Unknown tool" errors for Google tools (Calendar, Gmail, Tasks)
- Understanding token storage and refresh mechanics
- Adding new Google service tools to the ToolExecutorRegistry
Token Storage Architecture
Token File Location
Google OAuth tokens are stored in JSON files at:
{cwd}/data/oauth-tokens/google-oauth.json
CRITICAL: The path is resolved using process.cwd(), meaning different packages look in different locations:
- Dashboard:
packages/dashboard/data/oauth-tokens/google-oauth.json - Slack bot:
packages/bot-slack/data/oauth-tokens/google-oauth.json - WhatsApp bot:
packages/bot-whatsapp/data/oauth-tokens/google-oauth.json - Project root:
data/oauth-tokens/google-oauth.json
Symlink Setup (Required for Multi-Package Access)
Since the dashboard is typically where users connect their Google accounts, other packages need symlinks to access the same tokens:
# For bot-slack
mkdir -p packages/bot-slack/data/oauth-tokens
ln -sf $(pwd)/packages/dashboard/data/oauth-tokens/google-oauth.json \
packages/bot-slack/data/oauth-tokens/google-oauth.json
# For bot-whatsapp
mkdir -p packages/bot-whatsapp/data/oauth-tokens
ln -sf $(pwd)/packages/dashboard/data/oauth-tokens/google-oauth.json \
packages/bot-whatsapp/data/oauth-tokens/google-oauth.json
# For project root (used by MCP servers)
mkdir -p data/oauth-tokens
ln -sf $(pwd)/packages/dashboard/data/oauth-tokens/google-oauth.json \
data/oauth-tokens/google-oauth.json
Token File Structure
{
"accounts": {
"user@gmail.com": {
"email": "user@gmail.com",
"displayName": "User Name",
"accessToken": "ya29...",
"refreshToken": "1//...",
"expiresAt": 1768578677699,
"scopes": [
"https://www.googleapis.com/auth/calendar",
"https://www.googleapis.com/auth/gmail.modify"
],
"connectedAt": 1768552923717,
"lastRefreshAt": 1768575078699
}
},
"pendingStates": {}
}
Environment Variables
Required environment variables in .env:
# OAuth Client Credentials (from Google Cloud Console)
GOOGLE_OAUTH_CLIENT_ID=<your-client-id>.apps.googleusercontent.com
GOOGLE_OAUTH_CLIENT_SECRET=GOCSPX-<your-secret>
# Callback URL (must match Google Cloud Console)
GOOGLE_OAUTH_CALLBACK_URL=http://localhost/api/auth/google/callback
The OAuth service loads credentials in this priority order:
- Secrets database (
GOOGLE_OAUTH_CLIENT_ID,GOOGLE_OAUTH_CLIENT_SECRET) - Environment variables
- Credentials file:
credentials/client_secret_*.json
Tool Registration Architecture
Two Registration Systems
Orient has two tool registration systems:
-
ToolRegistry (Discovery only)
- Location:
packages/agents/src/services/toolRegistry.ts - Used by:
discover_toolsfor listing available tools - Does NOT execute tools
- Location:
-
ToolExecutorRegistry (Execution)
- Location:
packages/agents/src/services/toolRegistry.ts - Used by:
executeToolCallFromRegistry()inpackages/mcp-servers/src/tool-executor.ts - Actually runs the tool code
- Location:
Registering New Google Tools
To add a new Google tool, register it in the registerGoogleToolHandlers() function:
// In packages/agents/src/services/toolRegistry.ts
function registerGoogleToolHandlers(registry: ToolExecutorRegistry): void {
const registerHandlers = async () => {
const { getCalendarService, getGoogleOAuthService } =
await import('@orientbot/integrations/google');
// Register handler
registry.registerHandler('google_calendar_list_events', async (args) => {
const calendar = getCalendarService();
const events = await calendar.listEvents(options, accountEmail);
return createToolResult(JSON.stringify(events, null, 2));
});
};
void registerHandlers();
}
Legacy vs Modern Registration
| Aspect | Legacy (mcp-server.ts) | Modern (ToolExecutorRegistry) |
|---|---|---|
| Location | packages/mcp-servers/src/mcp-server.ts |
packages/agents/src/services/toolRegistry.ts |
| Pattern | Giant switch statement | Handler registration |
| Used by | Direct MCP server | assistant-server via base-server |
| Status | Deprecated | Preferred |
Important: The assistant-server (used by Slack/WhatsApp bots) uses base-server.ts which ONLY uses ToolExecutorRegistry. It does NOT fall back to the legacy switch statement unless setLegacyExecutor() is called.
Token Refresh Mechanics
The OAuth service in packages/integrations/src/google/oauth.ts handles automatic token refresh:
async getAuthClient(email: string): Promise<Auth.OAuth2Client> {
const account = this.data.accounts[email];
// Check if token needs refresh (expired or expiring in < 5 min)
if (Date.now() > account.expiresAt - 5 * 60 * 1000) {
const { credentials } = await client.refreshAccessToken();
account.accessToken = credentials.access_token;
account.expiresAt = credentials.expiry_date;
this.saveData();
}
return client;
}
Key points:
- Tokens are refreshed automatically 5 minutes before expiration
- Requires valid
refresh_tokenfrom initial OAuth consent - Requires OAuth credentials (client ID/secret) to be accessible
Debugging "Unknown Tool" Errors
When Google tools return {"error":"Unknown tool: google_calendar_list_events"}:
Step 1: Check Tool Discovery
# Tool should appear in discover_tools output
curl -X POST http://localhost:4099/... -d '{"tool":"discover_tools","args":{"mode":"search","query":"calendar"}}'
If tool appears in discovery but not execution, it's registered in ToolRegistry but not ToolExecutorRegistry.
Step 2: Check ToolExecutorRegistry Registration
Look for the tool in packages/agents/src/services/toolRegistry.ts:
// Should see this in registerGoogleToolHandlers():
registry.registerHandler('google_calendar_list_events', async (args) => { ... });
Step 3: Verify Build and Restart
# Rebuild affected packages
pnpm --filter @orientbot/agents build
pnpm --filter @orientbot/mcp-servers build
# Copy to root dist (used by OpenCode)
cp packages/mcp-servers/dist/*.js dist/mcp-servers/
# Kill existing MCP server processes
ps aux | grep "assistant-server\|coding-server" | grep -v grep | awk '{print $2}' | xargs kill
# OpenCode will spawn fresh processes on next tool call
Step 4: Check MCP Server Logs
tail -f logs/mcp-debug-*.log | grep -i "calendar\|google\|unknown"
Look for:
"Registered tool"- Tool was registered"Unknown tool"- Tool not found in any executor- Error messages during handler registration
Common Issues
Issue: Tokens not found
Symptom: "No Google account connected" Cause: Token file not found at CWD path Fix: Create symlinks as documented above
Issue: Token refresh fails
Symptom: "Failed to refresh token" Cause: OAuth credentials not accessible Fix: Ensure GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET are set
Issue: Tool returns "Unknown tool"
Symptom: {"error":"Unknown tool: google_calendar_list_events"}
Cause: Tool not registered in ToolExecutorRegistry
Fix: Add handler in registerGoogleToolHandlers() and rebuild
Issue: Stale MCP server
Symptom: Changes not taking effect Cause: Old MCP server process still running Fix: Kill old processes, OpenCode will spawn new ones
Files Reference
| File | Purpose |
|---|---|
packages/integrations/src/google/oauth.ts |
OAuth service, token storage |
packages/integrations/src/google/calendar.ts |
Calendar service implementation |
packages/agents/src/services/toolRegistry.ts |
Tool discovery + executor registration |
packages/mcp-servers/src/tool-executor.ts |
Tool execution entry point |
packages/mcp-servers/src/base-server.ts |
MCP server using executor registry |
packages/mcp-servers/src/mcp-server.ts |
Legacy MCP server with switch statement |
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?