creditkarma-mcp 2.0.9 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -30
- package/SKILL.md +13 -12
- package/dist/auth.js +198 -0
- package/dist/bundle.js +9379 -3241
- package/dist/client.js +1 -1
- package/dist/index.js +3 -2
- package/dist/tools/auth.js +2 -2
- package/dist/tools/sync.js +12 -1
- package/package.json +9 -12
- package/server.json +9 -3
package/README.md
CHANGED
|
@@ -21,7 +21,29 @@ Ask Claude things like:
|
|
|
21
21
|
- [Claude Desktop](https://claude.ai/download) or [Claude Code](https://claude.ai/code)
|
|
22
22
|
- [Node.js](https://nodejs.org) 18 or later
|
|
23
23
|
- A Credit Karma account
|
|
24
|
-
- [
|
|
24
|
+
- For the no-env-var path: the [fetchproxy 0.3.0 Chrome / Safari extension](https://github.com/chrischall/fetchproxy)
|
|
25
|
+
|
|
26
|
+
## Acknowledgement of Terms
|
|
27
|
+
|
|
28
|
+
By using this MCP server, you acknowledge and agree to the following:
|
|
29
|
+
|
|
30
|
+
**1. This server accesses your own Credit Karma account.** Every request is dispatched through your own signed-in browser tab via the fetchproxy extension. **You** are the one logged in. It does not — and cannot — access anyone else's account.
|
|
31
|
+
|
|
32
|
+
**2. [Credit Karma's Terms](https://www.creditkarma.com/about/terms) govern your use of this server**, just as they govern your direct use of creditkarma.com. The clauses most relevant here:
|
|
33
|
+
|
|
34
|
+
> You must not sell, transfer, or assign your account to anyone else… you may not allow anyone else to log into our Services as you.
|
|
35
|
+
|
|
36
|
+
CK does contemplate third-party data retrieval at the user's direction (Section 3.7). There is no explicit anti-scraping clause in the membership agreement; Section 4.1 restricts copying or distributing CK content without express prior written consent.
|
|
37
|
+
|
|
38
|
+
You are agreeing to those terms — read by the maintainer 2026-05-23 — every time you invoke a tool in this server. Critically: this server runs **as you**, not as a third party logging in on your behalf. You direct the tool.
|
|
39
|
+
|
|
40
|
+
**3. Personal, non-commercial use only.** This project is not affiliated with, endorsed by, sponsored by, or in partnership with Intuit, Credit Karma, or any financial institution. It is a personal automation tool that reads your transaction history, spending categories, and account snapshots — the same data Credit Karma already shows you in their app. Do not use it on someone else's account, do not redistribute their content, and do not use it to make trading or lending decisions on behalf of others.
|
|
41
|
+
|
|
42
|
+
**4. This server may break.** Credit Karma rotates its internal endpoints; what works today may 404 tomorrow. This is the nature of unofficial integrations.
|
|
43
|
+
|
|
44
|
+
**5. You accept full responsibility** for any consequences of using this server in connection with your Credit Karma account — rate limiting, account warnings, suspension, or any enforcement action Intuit takes. If Credit Karma objects to your use, stop using this server. **Do not commit your `.env` to git** — your CK session/auth artifacts are credentials, and the Membership Agreement holds you responsible for their confidentiality.
|
|
45
|
+
|
|
46
|
+
This section is the maintainer's good-faith summary of the terms — it is not legal advice and does not modify or supersede Credit Karma's actual Membership Agreement.
|
|
25
47
|
|
|
26
48
|
## Installation
|
|
27
49
|
|
|
@@ -80,38 +102,31 @@ Fully quit and relaunch. Then ask: *"Sync my Credit Karma transactions"*.
|
|
|
80
102
|
|
|
81
103
|
Credit Karma uses short-lived JWTs. This server handles automatic token refresh — you only need to set up credentials once (or when your session expires).
|
|
82
104
|
|
|
83
|
-
|
|
105
|
+
`creditkarma-mcp` tries three auth paths in priority order; whichever succeeds first is used. Existing setups keep working unchanged.
|
|
84
106
|
|
|
85
|
-
|
|
107
|
+
1. **`CK_COOKIES` env var (legacy).** Set the full Cookie header in your Claude Desktop config or `.env`. This is the path shown in the config above.
|
|
108
|
+
2. **Cached session from `ck_set_session`.** Once called, the tool persists the Cookie header to `.env` as `CK_COOKIES` — so subsequent runs collapse into path 1.
|
|
109
|
+
3. **fetchproxy fallback (no env vars needed — easiest onboarding).** When neither is configured, the server reads `CKAT` + `CKTRKID` cookies once at startup from your already-signed-in `creditkarma.com` tab via the [fetchproxy](https://github.com/chrischall/fetchproxy) browser extension. After that one read, all CK API calls go directly from Node — the extension is **not** in the request hot path. Install the fetchproxy extension (Chrome Web Store / Safari `.dmg`), sign into [creditkarma.com](https://www.creditkarma.com), and the MCP just works.
|
|
86
110
|
|
|
87
|
-
|
|
88
|
-
npm run auth # prints the Cookie header to the console
|
|
89
|
-
npm run auth -- .env # writes CK_COOKIES=<header> to .env
|
|
90
|
-
```
|
|
111
|
+
Set `CK_DISABLE_FETCHPROXY=1` to opt out of the fallback (turns missing credentials into a hard error — useful in headless CI).
|
|
91
112
|
|
|
92
|
-
|
|
113
|
+
### Getting your credentials (env-var path)
|
|
93
114
|
|
|
94
|
-
#### Option
|
|
115
|
+
#### Option A — fetchproxy extension (recommended)
|
|
95
116
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
```
|
|
117
|
+
1. Install the [fetchproxy 0.3.0 extension](https://github.com/chrischall/fetchproxy) (Chrome Web Store or Safari `.dmg`).
|
|
118
|
+
2. Sign into [creditkarma.com](https://www.creditkarma.com) in that browser.
|
|
119
|
+
3. Leave `CK_COOKIES` **unset** in your Claude config.
|
|
100
120
|
|
|
101
|
-
|
|
121
|
+
The MCP reads the HttpOnly `CKAT` + `CKTRKID` cookies via `chrome.cookies.get` on the first tool call, then operates direct-to-API from Node. To re-auth (e.g. after Credit Karma signs you out), just sign back in to creditkarma.com.
|
|
102
122
|
|
|
103
|
-
#### Option
|
|
123
|
+
#### Option B — manual (DevTools)
|
|
104
124
|
|
|
105
125
|
1. Log in to [creditkarma.com](https://www.creditkarma.com) in Chrome
|
|
106
126
|
2. Open DevTools → **Network** → click any request to creditkarma.com → **Request Headers**
|
|
107
127
|
3. Right-click the `cookie` header → **Copy value**
|
|
108
128
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
Either of these works:
|
|
112
|
-
|
|
113
|
-
- Paste the value from `npm run auth` into `CK_COOKIES` in your `.env` or Claude config
|
|
114
|
-
- Or call `ck_set_session` from within Claude with the Cookie header value
|
|
129
|
+
Then either paste into `CK_COOKIES` in your Claude config / `.env`, or call `ck_set_session` from within Claude with the Cookie header value.
|
|
115
130
|
|
|
116
131
|
The server extracts the access and refresh JWTs from the `CKAT` cookie inside the header and refreshes the access token automatically as needed.
|
|
117
132
|
|
|
@@ -119,7 +134,9 @@ The server extracts the access and refresh JWTs from the `CKAT` cookie inside th
|
|
|
119
134
|
|
|
120
135
|
- **Access token**: ~15 minutes (auto-refreshed transparently)
|
|
121
136
|
- **Refresh token**: ~8 hours
|
|
122
|
-
- When the refresh token expires
|
|
137
|
+
- When the refresh token expires:
|
|
138
|
+
- **fetchproxy path:** sign back into creditkarma.com — the MCP re-reads fresh cookies on the next tool call.
|
|
139
|
+
- **env-var path:** grab a fresh Cookie header from DevTools and update `CK_COOKIES` (or call `ck_set_session`).
|
|
123
140
|
|
|
124
141
|
## Available tools
|
|
125
142
|
|
|
@@ -156,14 +173,19 @@ sync_state (key, value)
|
|
|
156
173
|
|
|
157
174
|
| Env var | Description | Default |
|
|
158
175
|
|---------|-------------|---------|
|
|
159
|
-
| `CK_COOKIES` | Full Cookie header from a signed-in creditkarma.com request | *(
|
|
176
|
+
| `CK_COOKIES` | Full Cookie header from a signed-in creditkarma.com request | *(unset — falls back to fetchproxy)* |
|
|
177
|
+
| `CK_DISABLE_FETCHPROXY` | Set to `1` to skip the fetchproxy fallback (headless / CI) | *(unset)* |
|
|
160
178
|
| `CK_DB_PATH` | Path to SQLite database file | `~/.creditkarma-mcp/transactions.db` |
|
|
161
179
|
|
|
162
180
|
## Troubleshooting
|
|
163
181
|
|
|
164
|
-
**"
|
|
182
|
+
**"CK auth: set CK_COOKIES, or call the ck_set_session MCP tool, or install the fetchproxy extension…"** — neither auth path is configured. Either fill in `CK_COOKIES` in your Claude config, or install the [fetchproxy extension](https://github.com/chrischall/fetchproxy) and sign into `creditkarma.com` in your browser.
|
|
183
|
+
|
|
184
|
+
**"TOKEN_EXPIRED"** — your refresh token has expired. Sign back into creditkarma.com (fetchproxy path) or grab a fresh Cookie header from DevTools and update `CK_COOKIES` / call `ck_set_session`.
|
|
185
|
+
|
|
186
|
+
**"fetchproxy fallback failed"** — the env-var path wasn't configured and the extension couldn't be reached. Confirm the fetchproxy extension is installed, signed into Credit Karma, and that it's running (open the extension popup). To disable the fallback, set `CK_DISABLE_FETCHPROXY=1`.
|
|
165
187
|
|
|
166
|
-
**Sync returns 0 transactions** — check that your
|
|
188
|
+
**Sync returns 0 transactions** — check that your auth is fresh. The refresh token inside the CKAT cookie expires after ~8 hours.
|
|
167
189
|
|
|
168
190
|
**Tools not appearing** — fully quit and relaunch Claude Desktop. In Claude Code, run `/mcp` to check server status.
|
|
169
191
|
|
|
@@ -171,9 +193,10 @@ sync_state (key, value)
|
|
|
171
193
|
|
|
172
194
|
## Security
|
|
173
195
|
|
|
174
|
-
- Credentials are stored only in your local `.env` file (gitignored) or
|
|
175
|
-
- `.env` is written at mode 0600 (owner read/write only) by
|
|
196
|
+
- Credentials are stored only in your local `.env` file (gitignored), Claude config, or your browser's cookie jar (fetchproxy path)
|
|
197
|
+
- `.env` is written at mode 0600 (owner read/write only) by `ck_set_session`
|
|
176
198
|
- `ck_set_session` refuses to save a refresh token whose JWT `exp` is already in the past — prevents stale credentials from polluting `.env`
|
|
199
|
+
- The fetchproxy path doesn't persist anything to disk — cookies are read into memory once per MCP run, directly from the user's browser via `chrome.cookies.get`
|
|
177
200
|
- The server never logs credentials; warnings go to stderr only (stdout is reserved for the MCP JSON-RPC stream)
|
|
178
201
|
- Only `SELECT` queries are permitted via `ck_query_sql` — no writes to Credit Karma; the underlying `node:sqlite` `prepare()` also rejects multi-statement input
|
|
179
202
|
|
|
@@ -196,6 +219,7 @@ Changes land via PR, including for solo work — release notes are generated fro
|
|
|
196
219
|
|
|
197
220
|
```
|
|
198
221
|
src/
|
|
222
|
+
auth.ts resolveAuth() — three-path priority (CK_COOKIES env / ck_set_session cache / fetchproxy), plus loadAuthIntoClient()
|
|
199
223
|
client.ts Credit Karma GraphQL client (auto-refresh, JWT helpers, cookie parser)
|
|
200
224
|
index.ts MCP server entry point; bootstraps tokens from CK_COOKIES
|
|
201
225
|
db.ts SQLite schema, migrations, and upsert helpers
|
|
@@ -207,13 +231,11 @@ src/
|
|
|
207
231
|
ck_get_spending_by_category, ck_get_spending_by_merchant,
|
|
208
232
|
ck_get_account_summary
|
|
209
233
|
sql.ts ck_query_sql — SELECT-only escape hatch
|
|
210
|
-
scripts/
|
|
211
|
-
setup-auth.mjs npm run auth — Puppeteer flow + manual paste fallback
|
|
212
234
|
tests/
|
|
213
235
|
helpers.ts Shared test helpers (fakeServer, makeJwt)
|
|
236
|
+
auth.test.ts resolveAuth + loadAuthIntoClient (mocks @fetchproxy/bootstrap)
|
|
214
237
|
client.test.ts
|
|
215
238
|
db.test.ts
|
|
216
|
-
setup-auth.test.ts
|
|
217
239
|
tools/
|
|
218
240
|
auth.test.ts
|
|
219
241
|
sync.test.ts
|
package/SKILL.md
CHANGED
|
@@ -56,28 +56,29 @@ Then add to `.mcp.json`:
|
|
|
56
56
|
|
|
57
57
|
Or use a `.env` file in the project directory with `CK_COOKIES=<value>`.
|
|
58
58
|
|
|
59
|
-
### Getting CK_COOKIES
|
|
59
|
+
### Getting CK_COOKIES (optional)
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
npm run auth -- .env # writes CK_COOKIES=<header> to .env
|
|
65
|
-
```
|
|
61
|
+
Three onboarding paths, in priority order:
|
|
62
|
+
|
|
63
|
+
**1. fetchproxy extension (easiest — no env vars):** Install the [fetchproxy 0.3.0 extension](https://github.com/chrischall/fetchproxy), sign into creditkarma.com once, and leave `CK_COOKIES` **unset**. The MCP reads HttpOnly `CKAT` + `CKTRKID` cookies on the first tool call via `chrome.cookies.get`, then operates direct-to-API from Node.
|
|
66
64
|
|
|
67
|
-
|
|
65
|
+
**2. ck_set_session MCP tool:** From within Claude, call `ck_set_session` with a Cookie header you copied from DevTools (see below). The tool persists it to `.env`.
|
|
68
66
|
|
|
69
|
-
**Manual (DevTools):**
|
|
67
|
+
**3. Manual (DevTools):**
|
|
70
68
|
1. Log in to [creditkarma.com](https://www.creditkarma.com) in Chrome
|
|
71
69
|
2. DevTools → **Network** → any creditkarma.com request → **Request Headers**
|
|
72
70
|
3. Right-click the `cookie` header → **Copy value**
|
|
71
|
+
4. Paste into `CK_COOKIES` in your Claude config
|
|
73
72
|
|
|
74
73
|
## Authentication
|
|
75
74
|
|
|
76
|
-
|
|
75
|
+
The MCP handles auth automatically once any of the three paths is configured.
|
|
77
76
|
|
|
78
77
|
- Access token: ~15 min TTL, auto-refreshed transparently
|
|
79
78
|
- Refresh token: ~8 hours TTL
|
|
80
|
-
- When expired:
|
|
79
|
+
- When expired:
|
|
80
|
+
- **fetchproxy path:** sign back into creditkarma.com — the MCP reads fresh cookies on the next tool call
|
|
81
|
+
- **env-var / ck_set_session path:** grab a fresh Cookie header from DevTools and update `CK_COOKIES` (or call `ck_set_session` again)
|
|
81
82
|
|
|
82
83
|
## Tools
|
|
83
84
|
|
|
@@ -104,8 +105,8 @@ Call `ck_set_session` with your Cookie header to store credentials and enable au
|
|
|
104
105
|
## Workflows
|
|
105
106
|
|
|
106
107
|
**First-time setup:**
|
|
107
|
-
1.
|
|
108
|
-
2.
|
|
108
|
+
1. Easiest: install the [fetchproxy extension](https://github.com/chrischall/fetchproxy), sign into creditkarma.com, leave `CK_COOKIES` unset.
|
|
109
|
+
2. Or: copy the Cookie header from DevTools and either set `CK_COOKIES` in your config or call `ck_set_session(cookies)` from within Claude.
|
|
109
110
|
3. `ck_sync_transactions` → initial full sync
|
|
110
111
|
|
|
111
112
|
**Regular use:**
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
// Auth resolution — Pattern A template
|
|
3
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// Mirrors the canonical "browser-bootstrap + Node-direct" shape from
|
|
6
|
+
// ofw-mcp/src/auth.ts. Other MCPs in this family (resy-mcp, opentable-mcp,
|
|
7
|
+
// signupgenius-mcp, zola-mcp, …) use the same selector — keep the structure
|
|
8
|
+
// flat, the path-selection explicit, and the error messages actionable.
|
|
9
|
+
//
|
|
10
|
+
// THE PATHS, in priority order:
|
|
11
|
+
//
|
|
12
|
+
// 1. CK_COOKIES env var (existing behavior)
|
|
13
|
+
// A full Cookie header (e.g. `CKTRKID=...; CKAT=eyJ...%3BeyJ...; ...`)
|
|
14
|
+
// from a signed-in creditkarma.com request. The CKAT cookie contains
|
|
15
|
+
// `<accessJWT>%3B<refreshJWT>` URL-encoded, which the caller parses.
|
|
16
|
+
// Legacy users keep working without action.
|
|
17
|
+
//
|
|
18
|
+
// 2. Cached session from `ck_set_session` (existing behavior)
|
|
19
|
+
// The MCP tool `ck_set_session` accepts a pasted Cookie header and
|
|
20
|
+
// persists it to .env as CK_COOKIES — so once it's been called, this
|
|
21
|
+
// path collapses into path 1 on subsequent runs.
|
|
22
|
+
//
|
|
23
|
+
// 3. fetchproxy fallback (new)
|
|
24
|
+
// When no Cookie header is set, lift the user's session out of their
|
|
25
|
+
// signed-in creditkarma.com browser tab via the fetchproxy 0.3.0
|
|
26
|
+
// extension. `@fetchproxy/bootstrap` spins up a one-shot WebSocket
|
|
27
|
+
// bridge, asks the extension for the `CKAT` and `CKTRKID` cookies via
|
|
28
|
+
// `chrome.cookies.get`, then closes the bridge. The synthesized
|
|
29
|
+
// Cookie header has the same shape that ck_set_session produces, so
|
|
30
|
+
// the rest of the stack consumes it without branching.
|
|
31
|
+
//
|
|
32
|
+
// All subsequent API calls go out via plain Node `fetch()` —
|
|
33
|
+
// fetchproxy is NOT in the request hot path. Token refresh
|
|
34
|
+
// (`POST /member/oauth2/refresh`) is also a plain Node fetch.
|
|
35
|
+
//
|
|
36
|
+
// Users opt out with CK_DISABLE_FETCHPROXY=1 (anyone who wants the
|
|
37
|
+
// old behavior of "fail loudly when creds are missing").
|
|
38
|
+
//
|
|
39
|
+
// 4. Error
|
|
40
|
+
// Nothing to authenticate with. We throw a message that names all
|
|
41
|
+
// three onboarding paths so the user can pick whichever fits.
|
|
42
|
+
//
|
|
43
|
+
// Why fetchproxy is only a one-shot read:
|
|
44
|
+
// The bootstrap call snapshots the CKAT + CKTRKID cookies and returns.
|
|
45
|
+
// The MCP then operates from Node with direct fetch — latency and
|
|
46
|
+
// reliability are not coupled to the browser bridge for normal tool
|
|
47
|
+
// calls. If the access JWT inside CKAT expires, the refresh flow runs
|
|
48
|
+
// in pure Node against `creditkarma.com/member/oauth2/refresh`. If
|
|
49
|
+
// that 403s (Akamai gate / expired refresh JWT), the user re-signs into
|
|
50
|
+
// creditkarma.com in the browser and the next MCP run re-reads the
|
|
51
|
+
// fresh cookies.
|
|
52
|
+
//
|
|
53
|
+
// Testability:
|
|
54
|
+
// - `@fetchproxy/bootstrap` is mocked at the module boundary in tests.
|
|
55
|
+
// - This module exposes a single async `resolveAuth()` that returns a
|
|
56
|
+
// Cookie header string + a source label. Callers treat the cookies
|
|
57
|
+
// value as opaque — the existing parser in `src/index.ts` /
|
|
58
|
+
// `src/tools/auth.ts` extracts the CKAT JWTs the same way it does
|
|
59
|
+
// today.
|
|
60
|
+
import { bootstrap } from '@fetchproxy/bootstrap';
|
|
61
|
+
import pkg from '../package.json' with { type: 'json' };
|
|
62
|
+
import { extractCookieValue } from './client.js';
|
|
63
|
+
/**
|
|
64
|
+
* Read an env var, trim, and treat blank / `${UNEXPANDED}` placeholders as
|
|
65
|
+
* unset. Defends against MCP hosts that pass `.mcp.json` env blocks through
|
|
66
|
+
* without variable expansion.
|
|
67
|
+
*/
|
|
68
|
+
function readEnv(key) {
|
|
69
|
+
const raw = process.env[key];
|
|
70
|
+
if (typeof raw !== 'string')
|
|
71
|
+
return undefined;
|
|
72
|
+
const trimmed = raw.trim();
|
|
73
|
+
if (trimmed.length === 0)
|
|
74
|
+
return undefined;
|
|
75
|
+
if (trimmed === 'undefined' || trimmed === 'null')
|
|
76
|
+
return undefined;
|
|
77
|
+
if (/^\$\{[^}]*\}$/.test(trimmed))
|
|
78
|
+
return undefined;
|
|
79
|
+
return trimmed;
|
|
80
|
+
}
|
|
81
|
+
/** True if the user has explicitly disabled the fetchproxy fallback. */
|
|
82
|
+
function fetchproxyDisabled() {
|
|
83
|
+
const raw = readEnv('CK_DISABLE_FETCHPROXY');
|
|
84
|
+
if (raw === undefined)
|
|
85
|
+
return false;
|
|
86
|
+
return ['1', 'true', 'yes', 'on'].includes(raw.toLowerCase());
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Resolve CK auth using the path priority described at the top of this
|
|
90
|
+
* file. Throws with an actionable error message when no path succeeds.
|
|
91
|
+
*
|
|
92
|
+
* Callers should treat the return value as opaque credentials — they
|
|
93
|
+
* should not branch on `source`. The field exists for logging / future
|
|
94
|
+
* cache-keying only.
|
|
95
|
+
*/
|
|
96
|
+
export async function resolveAuth() {
|
|
97
|
+
// ── Path 1: CK_COOKIES env var (unchanged from pre-fetchproxy behavior).
|
|
98
|
+
const envCookies = readEnv('CK_COOKIES');
|
|
99
|
+
if (envCookies) {
|
|
100
|
+
return { cookies: envCookies, source: 'env' };
|
|
101
|
+
}
|
|
102
|
+
// ── Path 2: fetchproxy fallback (new).
|
|
103
|
+
// (Path 2 — cached session from ck_set_session — also lands here at the
|
|
104
|
+
// env-var step on subsequent runs, since that tool writes CK_COOKIES
|
|
105
|
+
// to .env. So this branch only fires when neither env var nor cache
|
|
106
|
+
// has been seeded.)
|
|
107
|
+
if (!fetchproxyDisabled()) {
|
|
108
|
+
try {
|
|
109
|
+
const session = await bootstrap({
|
|
110
|
+
serverName: pkg.name,
|
|
111
|
+
version: pkg.version,
|
|
112
|
+
// CK serves www.creditkarma.com (web) and api.creditkarma.com
|
|
113
|
+
// (GraphQL). Both share the apex domain; the extension matches on
|
|
114
|
+
// suffix so listing the apex covers any subdomain.
|
|
115
|
+
domains: ['creditkarma.com'],
|
|
116
|
+
declare: {
|
|
117
|
+
// CKAT contains the access + refresh JWTs joined by `%3B`. CKTRKID
|
|
118
|
+
// is sent as the `ck-cookie-id` header on refresh requests; without
|
|
119
|
+
// it the refresh endpoint 403s. Both are HttpOnly — invisible to
|
|
120
|
+
// page JS — but fetchproxy 0.3.0's `read_cookies` uses
|
|
121
|
+
// `chrome.cookies.get` which sees HttpOnly cookies.
|
|
122
|
+
cookies: ['CKAT', 'CKTRKID'],
|
|
123
|
+
localStorage: [],
|
|
124
|
+
sessionStorage: [],
|
|
125
|
+
captureHeaders: [],
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
const ckat = session.cookies['CKAT'];
|
|
129
|
+
const cktrkid = session.cookies['CKTRKID'];
|
|
130
|
+
if (!ckat) {
|
|
131
|
+
throw new Error('CKAT cookie missing on creditkarma.com. ' +
|
|
132
|
+
'Sign into creditkarma.com in your browser (with the fetchproxy extension installed) and retry.');
|
|
133
|
+
}
|
|
134
|
+
if (!cktrkid) {
|
|
135
|
+
throw new Error('CKTRKID cookie missing on creditkarma.com. ' +
|
|
136
|
+
'Sign into creditkarma.com in your browser (with the fetchproxy extension installed) and retry.');
|
|
137
|
+
}
|
|
138
|
+
// Synthesize a Cookie header identical in shape to what `ck_set_session`
|
|
139
|
+
// accepts. The existing parser in `src/index.ts` / `src/tools/auth.ts`
|
|
140
|
+
// extracts CKAT and splits its `<accessJWT>%3B<refreshJWT>` payload
|
|
141
|
+
// without caring how the header was assembled.
|
|
142
|
+
const cookies = `CKTRKID=${cktrkid}; CKAT=${ckat}`;
|
|
143
|
+
return { cookies, source: 'fetchproxy' };
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
147
|
+
throw new Error(`CK auth: no CK_COOKIES set, and fetchproxy fallback failed: ${msg}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// ── Path 4: nothing configured. Surface all three fixes side-by-side so
|
|
151
|
+
// the user can pick whichever fits their setup.
|
|
152
|
+
throw new Error('CK auth: set CK_COOKIES, ' +
|
|
153
|
+
'or call the ck_set_session MCP tool with a Cookie header, ' +
|
|
154
|
+
'or install the fetchproxy extension and sign into creditkarma.com ' +
|
|
155
|
+
'(unset CK_DISABLE_FETCHPROXY if it is set).');
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Parse a Cookie header into the CK_COOKIES → (accessToken, refreshToken)
|
|
159
|
+
* shape, mirroring `src/index.ts` and `src/tools/auth.ts`. The CKAT cookie
|
|
160
|
+
* value is `<accessJWT>%3B<refreshJWT>` URL-encoded; we split on either
|
|
161
|
+
* the encoded or literal semicolon.
|
|
162
|
+
*
|
|
163
|
+
* Exported so both `src/index.ts` (startup) and `loadAuthIntoClient()`
|
|
164
|
+
* (lazy bootstrap) can share one parser. Returns nulls (not errors) when
|
|
165
|
+
* the input doesn't contain a CKAT — the caller decides whether absence
|
|
166
|
+
* is fatal.
|
|
167
|
+
*/
|
|
168
|
+
export function parseCookieHeader(cookies) {
|
|
169
|
+
const ckat = extractCookieValue(cookies, 'CKAT') ?? cookies.trim();
|
|
170
|
+
const parts = ckat.replace('%3B', ';').split(';');
|
|
171
|
+
const accessToken = parts[0]?.trim() || null;
|
|
172
|
+
const refreshToken = parts[1]?.trim() || null;
|
|
173
|
+
return { accessToken, refreshToken };
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Resolve CK auth via `resolveAuth()` and apply the result to a client.
|
|
177
|
+
*
|
|
178
|
+
* Used by tool handlers on the first request that needs auth but finds no
|
|
179
|
+
* credentials on the client — i.e. the user didn't set CK_COOKIES, didn't
|
|
180
|
+
* call ck_set_session, and the fetchproxy extension is the last hope.
|
|
181
|
+
*
|
|
182
|
+
* If `resolveAuth()` lands on the env-var path (path 1) the cookies are
|
|
183
|
+
* applied with no network round-trip. If it lands on fetchproxy (path 3)
|
|
184
|
+
* the bootstrap call snapshots the browser session once; afterwards the
|
|
185
|
+
* client has fresh CKAT + CKTRKID and the normal `refreshAccessToken()`
|
|
186
|
+
* flow takes over.
|
|
187
|
+
*/
|
|
188
|
+
export async function loadAuthIntoClient(client) {
|
|
189
|
+
const { cookies } = await resolveAuth();
|
|
190
|
+
const { accessToken, refreshToken } = parseCookieHeader(cookies);
|
|
191
|
+
if (!accessToken) {
|
|
192
|
+
throw new Error('CK auth: resolved cookies did not contain a CKAT token.');
|
|
193
|
+
}
|
|
194
|
+
client.setToken(accessToken);
|
|
195
|
+
if (refreshToken)
|
|
196
|
+
client.setRefreshToken(refreshToken);
|
|
197
|
+
client.setCookies(cookies);
|
|
198
|
+
}
|