Skip to content

Instantly share code, notes, and snippets.

@Melvynx
Created January 22, 2026 00:23
Show Gist options
  • Select an option

  • Save Melvynx/bfe45de5405c7f3be04bd90df20ed573 to your computer and use it in GitHub Desktop.

Select an option

Save Melvynx/bfe45de5405c7f3be04bd90df20ed573 to your computer and use it in GitHub Desktop.
Use Claude models with local API keys (OAuth tokens from Claude Code)
# Claude Code AI - Use Claude Models with Local OAuth Tokens
Utility scripts to access Claude API using Claude Code's OAuth credentials. No need for a separate API key!
## Why?
When you authenticate with Claude Code (the official CLI), it stores OAuth tokens locally. These scripts let you reuse those tokens to make API calls to Claude models programmatically.
## Installation
```bash
# Clone or copy the scripts
bun add @ai-sdk/anthropic ai
```
## Quick Start
### CLI Usage
```bash
bun run cli.ts "What is the capital of France?" -m haiku
bun run cli.ts "Explain quantum computing" -m sonnet
bun run cli.ts "Write a poem" -m opus -s "You are a creative poet"
```
### Programmatic Usage
```typescript
import { generateTextCC } from "./claude";
const response = await generateTextCC({
prompt: "Your prompt here",
model: "haiku", // "haiku" | "sonnet" | "opus"
system: "Optional system prompt",
});
console.log(response);
```
### Get OAuth Token Directly
```typescript
import { getClaudeCodeToken, getClaudeCodeTokenSafe } from "./helper/credentials";
const token = await getClaudeCodeToken(); // Throws on error
const token = await getClaudeCodeTokenSafe(); // Returns null on error
```
## Available Models
| Alias | Model ID |
|-------|----------|
| `haiku` | `claude-haiku-4-5-20251001` |
| `sonnet` | `claude-sonnet-4-5-20250929` |
| `opus` | `claude-opus-4-5-20251101` |
## Where Are Credentials Stored?
| Platform | Location |
|----------|----------|
| macOS | Keychain (`security find-generic-password -s "Claude Code-credentials" -w`) |
| Linux | `~/.claude/.credentials.json` |
| Windows | WSL required, uses Linux path |
## Core Files
### `claude.ts` - Main API
```typescript
import { createAnthropic } from "@ai-sdk/anthropic";
import { generateText } from "ai";
import { getClaudeCodeToken } from "./helper/credentials";
export type Model = "haiku" | "sonnet" | "opus";
const MODEL_MAP: Record<Model, string> = {
haiku: "claude-haiku-4-5-20251001",
sonnet: "claude-sonnet-4-5-20250929",
opus: "claude-opus-4-5-20251101",
};
export async function generateTextCC({
prompt,
system,
model = "sonnet",
}: {
prompt: string;
system?: string;
model?: Model;
}): Promise<string> {
const token = await getClaudeCodeToken();
const anthropic = createAnthropic({
apiKey: "oauth-token",
headers: {
"anthropic-beta": "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14",
"anthropic-dangerous-direct-browser-access": "true",
"x-app": "cli",
"User-Agent": "claude-cli/2.0.76 (external, cli)",
},
fetch: (async (url: RequestInfo | URL, options?: RequestInit) => {
const headers = new Headers(options?.headers);
headers.delete("x-api-key");
headers.set("Authorization", `Bearer ${token}`);
return fetch(url, { ...options, headers });
}) as typeof fetch,
});
const result = await generateText({
model: anthropic(MODEL_MAP[model]),
system,
prompt,
});
return result.text;
}
```
### `helper/credentials.ts` - Token Retrieval
```typescript
import { existsSync } from "node:fs";
import { readFile } from "node:fs/promises";
import { homedir } from "node:os";
import { join } from "node:path";
import { $ } from "bun";
interface Credentials {
claudeAiOauth: {
accessToken: string;
refreshToken: string;
expiresAt: number;
scopes: string[];
subscriptionType: string;
};
}
function getCredentialsFilePath(): string {
return join(homedir(), ".claude", ".credentials.json");
}
async function getMacOSKeychainCredentials(): Promise<string> {
const result = await $`security find-generic-password -s "Claude Code-credentials" -w`
.quiet()
.text();
const creds: Credentials = JSON.parse(result.trim());
return creds.claudeAiOauth.accessToken;
}
async function getFileCredentials(): Promise<string> {
const credentialsPath = getCredentialsFilePath();
if (!existsSync(credentialsPath)) {
throw new Error(`Credentials file not found at ${credentialsPath}`);
}
const content = await readFile(credentialsPath, "utf-8");
const creds: Credentials = JSON.parse(content);
return creds.claudeAiOauth.accessToken;
}
export async function getClaudeCodeToken(): Promise<string> {
const platform = process.platform;
if (platform === "darwin") {
try {
return await getMacOSKeychainCredentials();
} catch {
return await getFileCredentials();
}
}
return await getFileCredentials();
}
export async function getClaudeCodeTokenSafe(): Promise<string | null> {
try {
return await getClaudeCodeToken();
} catch {
return null;
}
}
```
### `cli.ts` - Command Line Interface
```typescript
#!/usr/bin/env bun
import { parseArgs } from "util";
import { generateTextCC, type Model } from "./claude";
const { values, positionals } = parseArgs({
args: Bun.argv.slice(2),
options: {
model: { type: "string", short: "m", default: "sonnet" },
system: { type: "string", short: "s" },
},
allowPositionals: true,
});
const prompt = positionals.join(" ");
if (!prompt) {
console.error("Usage: bun run cli.ts <prompt> [-m opus|sonnet|haiku] [-s system_prompt]");
process.exit(1);
}
const model = values.model as Model;
if (!["haiku", "sonnet", "opus"].includes(model)) {
console.error(`Invalid model: ${model}. Use: opus, sonnet, or haiku`);
process.exit(1);
}
const response = await generateTextCC({ prompt, model, system: values.system });
console.log(response);
```
## Prerequisites
1. Install and authenticate [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code)
2. Install [Bun](https://bun.sh) runtime
3. Install dependencies: `bun add @ai-sdk/anthropic ai`
## Notes
- Tokens are automatically retrieved from the local credential store
- On macOS, tokens are stored securely in Keychain
- On Linux/Windows (WSL), tokens are stored in `~/.claude/.credentials.json`
- The script handles cross-platform differences automatically
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment