Skip to content

Commit 96fb975

Browse files
authored
Merge pull request #18 from cloudflare/auth-improvements
automatically login on server start
2 parents d4b871f + e3a3d63 commit 96fb975

File tree

4 files changed

+61
-39
lines changed

4 files changed

+61
-39
lines changed

README.md

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,9 @@ This lets you use Claude Desktop, or any MCP Client, to use natural language to
1818

1919
## Setup
2020

21-
1. Make sure you are logged in to Cloudflare via Wrangler:
22-
```bash
23-
npx wrangler login
24-
```
25-
This authentication is required for the MCP server to access your Cloudflare resources when testing locally.
21+
1. Run `npx @cloudflare/mcp-server-cloudflare init`
2622

27-
2. Run `npx @cloudflare/mcp-server-cloudflare init`
23+
> **Note:** The MCP server will automatically run `npx wrangler login` if you're not already authenticated with Cloudflare. You'll be prompted to complete the authentication process in your browser if needed.
2824
2925
<div align="left">
3026
<img src="https://github.com/user-attachments/assets/163bed75-ec0c-478a-94b2-179969a90923" alt="Example console output" width="300"/>
@@ -178,7 +174,7 @@ Then, in a second terminal:
178174
node dist/index.js init
179175
```
180176

181-
This will link Claude Desktop against your locally-installed version for you to test.
177+
This will link Claude Desktop against your locally-installed version for you to test. If you're not already authenticated with Wrangler, the server will automatically prompt you to complete the authentication process in your browser.
182178

183179
## Testing
184180

src/index.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { init } from './init'
33
import { config, log } from './utils/helpers'
44
import { main } from './main'
5-
import { getAuthTokens, isAccessTokenExpired, LocalState, refreshToken } from './utils/wrangler'
5+
import { getAuthTokens, isAccessTokenExpired, LocalState, refreshToken, ensureWranglerAuthentication } from './utils/wrangler'
66

77
// Handle process events
88
process.on('uncaughtException', (error) => {
@@ -32,15 +32,13 @@ if (cmd === 'init') {
3232
config.accountId = accountId
3333

3434
if (!config.accountId || !config.apiToken) {
35-
getAuthTokens()
36-
37-
if (isAccessTokenExpired()) {
38-
if (await refreshToken()) {
39-
console.log('Successfully refreshed access token')
40-
} else {
41-
console.log('Failed to refresh access token')
42-
}
35+
// Ensure the user is authenticated with Wrangler
36+
const isAuthenticated = await ensureWranglerAuthentication()
37+
if (!isAuthenticated) {
38+
throw new Error('Failed to authenticate with Wrangler. Please run `npx wrangler login` manually and try again.')
4339
}
40+
41+
// Set the API token from the authenticated state
4442
config.apiToken = LocalState.accessToken?.value
4543
}
4644

src/init.ts

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@ import { exec } from 'child_process'
33
import { promisify } from 'util'
44
import {
55
AccountInfo,
6+
ensureWranglerAuthentication,
67
fetchInternal,
78
FetchResult,
8-
getAuthTokens,
9-
isAccessTokenExpired,
109
isDirectory,
11-
refreshToken,
1210
} from './utils/wrangler'
1311
import chalk from 'chalk'
1412
import os from 'node:os'
@@ -37,29 +35,12 @@ export async function init(accountTag: string | undefined) {
3735
startSection(`Checking for existing Wrangler auth info`, `Step 1 of 3`)
3836
updateStatus(chalk.gray(`If anything goes wrong, try running 'npx wrangler@latest login' manually and retrying.`))
3937

40-
try {
41-
getAuthTokens()
42-
} catch (e: any) {
43-
updateStatus(`${chalk.underline.red('Warning:')} ${chalk.gray(e.message)}`, false)
44-
updateStatus(`Running '${chalk.yellow('npx wrangler login')}' and retrying...`, false)
45-
46-
const { stderr, stdout } = await execAsync('npx wrangler@latest login')
47-
if (stderr) updateStatus(chalk.gray(stderr))
48-
49-
getAuthTokens()
38+
const authenticated = await ensureWranglerAuthentication()
39+
if (!authenticated) {
40+
throw new Error('Failed to authenticate with Wrangler. Please try running npx wrangler@latest login manually and then retry.')
5041
}
5142

5243
updateStatus(`Wrangler auth info loaded!`)
53-
54-
if (isAccessTokenExpired()) {
55-
updateStatus(`Access token expired, refreshing...`, false)
56-
if (await refreshToken()) {
57-
updateStatus('Successfully refreshed access token')
58-
} else {
59-
throw new Error('Failed to refresh access token')
60-
}
61-
}
62-
6344
endSection('Done')
6445
startSection(`Fetching account info`, `Step 2 of 3`)
6546

src/utils/wrangler.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import TOML from '@iarna/toml'
1111
import assert from 'node:assert'
1212
import { mcpCloudflareVersion } from './helpers'
1313
import { fetch, Headers, Response, RequestInit, HeadersInit } from 'undici'
14+
import { exec } from 'child_process'
15+
import { promisify } from 'util'
1416

1517
export function isDirectory(configPath: string) {
1618
try {
@@ -449,3 +451,48 @@ export interface FetchResult<ResponseType = unknown> {
449451
}
450452

451453
export type AccountInfo = { name: string; id: string }
454+
455+
const execAsync = promisify(exec)
456+
457+
/**
458+
* Checks if the user is authenticated with Wrangler and runs the login command if needed.
459+
* @returns A promise that resolves to true if the user is authenticated, false otherwise
460+
*/
461+
export async function ensureWranglerAuthentication(): Promise<boolean> {
462+
try {
463+
// Try to get auth tokens
464+
getAuthTokens()
465+
466+
// If tokens are expired, try to refresh them
467+
if (isAccessTokenExpired()) {
468+
console.log('Wrangler access token expired, attempting to refresh...')
469+
if (await refreshToken()) {
470+
console.log('Successfully refreshed Wrangler access token')
471+
return true
472+
} else {
473+
console.log('Failed to refresh Wrangler access token, running wrangler login...')
474+
}
475+
} else {
476+
// Tokens exist and are not expired
477+
return true
478+
}
479+
} catch (e) {
480+
// If we can't get auth tokens, we need to run wrangler login
481+
console.log('No Wrangler authentication found, running wrangler login...')
482+
}
483+
484+
try {
485+
// Run wrangler login
486+
console.log('Running npx wrangler login. Please follow the prompts in your browser...')
487+
const { stderr, stdout } = await execAsync('npx wrangler@latest login')
488+
if (stderr) console.error(stderr)
489+
if (stdout) console.log(stdout)
490+
491+
// Try to get auth tokens again
492+
getAuthTokens()
493+
return true
494+
} catch (error) {
495+
console.error('Failed to run wrangler login:', error)
496+
return false
497+
}
498+
}

0 commit comments

Comments
 (0)