supermemory/apps/mcp/e2e/capture-oauth-token.ts
Dhravya Shah 19e8f06cf1
MCP Revamp (#1120) (#1380)
Co-authored-by: Prasanna <106952318+Prasanna721@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: ved015 <vedant.04.mahajan@gmail.com>
Co-authored-by: ved015 <ved015@users.noreply.github.com>
Co-authored-by: Ishaan Gupta <ishaankone@gmail.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-30 17:08:51 -07:00

121 lines
3.7 KiB
TypeScript

// One-time helper to capture a Tier D refresh token — run: bun e2e/capture-oauth-token.ts
import { createHash, randomBytes } from "node:crypto"
import { chmod, mkdir, writeFile } from "node:fs/promises"
import { createServer } from "node:http"
import { exec } from "node:child_process"
import { dirname } from "node:path"
import { fileURLToPath } from "node:url"
const API_URL = process.env.SUPERMEMORY_API_URL ?? "https://api.supermemory.ai"
const MCP_RESOURCE =
process.env.SUPERMEMORY_MCP_RESOURCE ?? "https://mcp.supermemory.ai/mcp"
const CREDENTIAL_FILE =
process.env.SUPERMEMORY_MCP_CREDENTIAL_FILE ??
fileURLToPath(new URL("../../../.context/mcp-oauth.env", import.meta.url))
const PORT = Number(process.env.SUPERMEMORY_MCP_CALLBACK_PORT ?? "8765")
const REDIRECT_URI = `http://localhost:${PORT}/callback`
const b64url = (b: Buffer) =>
b
.toString("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "")
async function main() {
const meta = (await (
await fetch(`${API_URL}/.well-known/oauth-authorization-server`)
).json()) as {
authorization_endpoint: string
token_endpoint: string
registration_endpoint: string
}
const reg = (await (
await fetch(meta.registration_endpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
client_name: "sm-mcp-e2e-capture",
redirect_uris: [REDIRECT_URI],
grant_types: ["authorization_code", "refresh_token"],
response_types: ["code"],
token_endpoint_auth_method: "none",
}),
})
).json()) as { client_id: string }
const verifier = b64url(randomBytes(32))
const challenge = b64url(createHash("sha256").update(verifier).digest())
const state = b64url(randomBytes(16))
const authUrl = new URL(meta.authorization_endpoint)
authUrl.search = new URLSearchParams({
response_type: "code",
client_id: reg.client_id,
redirect_uri: REDIRECT_URI,
code_challenge: challenge,
code_challenge_method: "S256",
scope: "openid profile email offline_access",
resource: MCP_RESOURCE,
state,
}).toString()
const code: string = await new Promise((resolve, reject) => {
const server = createServer((req, res) => {
const u = new URL(req.url ?? "", `http://localhost:${PORT}`)
if (u.pathname !== "/callback") return res.end()
if (u.searchParams.get("state") !== state) {
res.end("state mismatch")
return reject(new Error("state mismatch"))
}
const c = u.searchParams.get("code")
res.end("Done — you can close this tab.")
server.close()
c ? resolve(c) : reject(new Error("no code in callback"))
}).listen(PORT, () => {
console.log(`\nOpening browser to log in:\n${authUrl}\n`)
exec(`open "${authUrl}" || xdg-open "${authUrl}"`)
})
})
const tokenRes = (await (
await fetch(meta.token_endpoint, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "authorization_code",
code,
client_id: reg.client_id,
code_verifier: verifier,
redirect_uri: REDIRECT_URI,
resource: MCP_RESOURCE,
}),
})
).json()) as { refresh_token?: string; error?: string }
if (!tokenRes.refresh_token) {
console.error("No refresh_token returned:", tokenRes)
process.exit(1)
}
await mkdir(dirname(CREDENTIAL_FILE), { recursive: true })
await writeFile(
CREDENTIAL_FILE,
[
`SUPERMEMORY_MCP_CLIENT_ID=${JSON.stringify(reg.client_id)}`,
`SUPERMEMORY_MCP_REFRESH_TOKEN=${JSON.stringify(tokenRes.refresh_token)}`,
"",
].join("\n"),
{ mode: 0o600 },
)
await chmod(CREDENTIAL_FILE, 0o600)
console.log(`\nOAuth test credentials saved to ${CREDENTIAL_FILE}`)
}
main().catch((e) => {
console.error(e)
process.exit(1)
})