get-access-token.js•3.7 kB
#!/usr/bin/env node
import crypto from 'crypto';
import { createInterface } from 'readline';
import axios from 'axios';
import open from 'open';
// ClickUp OAuth configuration
// You'll need to replace these with your actual app credentials
const CLIENT_ID = process.env.CLICKUP_CLIENT_ID || 'your_client_id_here';
const REDIRECT_URI = 'http://localhost:3000/callback'; // or your configured redirect URI
if (CLIENT_ID === 'your_client_id_here') {
console.error('Please set CLICKUP_CLIENT_ID environment variable or edit the script with your Client ID');
process.exit(1);
}
// Generate PKCE code verifier and challenge
function generatePKCE() {
const codeVerifier = crypto.randomBytes(32).toString('base64url');
const codeChallenge = crypto.createHash('sha256').update(codeVerifier).digest('base64url');
return { codeVerifier, codeChallenge };
}
// Create authorization URL
function createAuthUrl(codeChallenge) {
const params = new URLSearchParams({
client_id: CLIENT_ID,
redirect_uri: REDIRECT_URI,
response_type: 'code',
scope: 'task:read task:write', // Adjust scopes as needed
code_challenge: codeChallenge,
code_challenge_method: 'S256'
});
return `https://app.clickup.com/api?${params.toString()}`;
}
// Exchange authorization code for access token
async function exchangeCodeForToken(code, codeVerifier) {
try {
const response = await axios.post('https://api.clickup.com/api/v2/oauth/token', {
client_id: CLIENT_ID,
client_secret: process.env.CLICKUP_CLIENT_SECRET || '', // If required
code: code,
grant_type: 'authorization_code',
redirect_uri: REDIRECT_URI,
code_verifier: codeVerifier
});
return response.data;
} catch (error) {
console.error('Error exchanging code for token:', error.response?.data || error.message);
throw error;
}
}
// Main function
async function main() {
console.log('ClickUp OAuth Token Setup');
console.log('========================');
// Generate PKCE
const { codeVerifier, codeChallenge } = generatePKCE();
console.log('Generated PKCE code challenge');
// Create and open authorization URL
const authUrl = createAuthUrl(codeChallenge);
console.log('\nOpening browser for authorization...');
console.log('If browser doesn\'t open, visit this URL manually:');
console.log(authUrl);
try {
await open(authUrl);
} catch (error) {
console.log('Could not open browser automatically. Please visit the URL above manually.');
}
// Wait for user to paste the authorization code
const rl = createInterface({
input: process.stdin,
output: process.stdout
});
const code = await new Promise((resolve) => {
rl.question('\nAfter authorizing, paste the authorization code from the redirect URL: ', (answer) => {
resolve(answer.trim());
rl.close();
});
});
if (!code) {
console.error('No authorization code provided');
process.exit(1);
}
console.log('\nExchanging code for access token...');
try {
const tokenData = await exchangeCodeForToken(code, codeVerifier);
console.log('\n✅ Success! Your ClickUp access token is:');
console.log(tokenData.access_token);
if (tokenData.refresh_token) {
console.log('\nRefresh token (keep this secure):');
console.log(tokenData.refresh_token);
}
console.log('\nAdd this to your MCP settings as CLICKUP_ACCESS_TOKEN environment variable.');
console.log('Example:');
console.log(`"env": { "CLICKUP_ACCESS_TOKEN": "${tokenData.access_token}" }`);
} catch (error) {
console.error('Failed to get access token:', error.message);
process.exit(1);
}
}
main().catch(console.error);