creditkarma-mcp 2.0.8 → 2.0.10
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 +30 -30
- package/SKILL.md +13 -12
- package/dist/auth.js +198 -0
- package/dist/bundle.js +7822 -1975
- package/dist/client.js +1 -1
- package/dist/index.js +2 -2
- package/dist/tools/auth.js +2 -2
- package/dist/tools/sync.js +12 -1
- package/package.json +4 -7
- package/server.json +9 -3
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ 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
25
|
|
|
26
26
|
## Installation
|
|
27
27
|
|
|
@@ -80,38 +80,31 @@ Fully quit and relaunch. Then ask: *"Sync my Credit Karma transactions"*.
|
|
|
80
80
|
|
|
81
81
|
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
82
|
|
|
83
|
-
|
|
83
|
+
`creditkarma-mcp` tries three auth paths in priority order; whichever succeeds first is used. Existing setups keep working unchanged.
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
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.
|
|
86
|
+
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.
|
|
87
|
+
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
88
|
|
|
87
|
-
|
|
88
|
-
npm run auth # prints the Cookie header to the console
|
|
89
|
-
npm run auth -- .env # writes CK_COOKIES=<header> to .env
|
|
90
|
-
```
|
|
89
|
+
Set `CK_DISABLE_FETCHPROXY=1` to opt out of the fallback (turns missing credentials into a hard error — useful in headless CI).
|
|
91
90
|
|
|
92
|
-
|
|
91
|
+
### Getting your credentials (env-var path)
|
|
93
92
|
|
|
94
|
-
#### Option
|
|
93
|
+
#### Option A — fetchproxy extension (recommended)
|
|
95
94
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
```
|
|
95
|
+
1. Install the [fetchproxy 0.3.0 extension](https://github.com/chrischall/fetchproxy) (Chrome Web Store or Safari `.dmg`).
|
|
96
|
+
2. Sign into [creditkarma.com](https://www.creditkarma.com) in that browser.
|
|
97
|
+
3. Leave `CK_COOKIES` **unset** in your Claude config.
|
|
100
98
|
|
|
101
|
-
|
|
99
|
+
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
100
|
|
|
103
|
-
#### Option
|
|
101
|
+
#### Option B — manual (DevTools)
|
|
104
102
|
|
|
105
103
|
1. Log in to [creditkarma.com](https://www.creditkarma.com) in Chrome
|
|
106
104
|
2. Open DevTools → **Network** → click any request to creditkarma.com → **Request Headers**
|
|
107
105
|
3. Right-click the `cookie` header → **Copy value**
|
|
108
106
|
|
|
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
|
|
107
|
+
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
108
|
|
|
116
109
|
The server extracts the access and refresh JWTs from the `CKAT` cookie inside the header and refreshes the access token automatically as needed.
|
|
117
110
|
|
|
@@ -119,7 +112,9 @@ The server extracts the access and refresh JWTs from the `CKAT` cookie inside th
|
|
|
119
112
|
|
|
120
113
|
- **Access token**: ~15 minutes (auto-refreshed transparently)
|
|
121
114
|
- **Refresh token**: ~8 hours
|
|
122
|
-
- When the refresh token expires
|
|
115
|
+
- When the refresh token expires:
|
|
116
|
+
- **fetchproxy path:** sign back into creditkarma.com — the MCP re-reads fresh cookies on the next tool call.
|
|
117
|
+
- **env-var path:** grab a fresh Cookie header from DevTools and update `CK_COOKIES` (or call `ck_set_session`).
|
|
123
118
|
|
|
124
119
|
## Available tools
|
|
125
120
|
|
|
@@ -156,14 +151,19 @@ sync_state (key, value)
|
|
|
156
151
|
|
|
157
152
|
| Env var | Description | Default |
|
|
158
153
|
|---------|-------------|---------|
|
|
159
|
-
| `CK_COOKIES` | Full Cookie header from a signed-in creditkarma.com request | *(
|
|
154
|
+
| `CK_COOKIES` | Full Cookie header from a signed-in creditkarma.com request | *(unset — falls back to fetchproxy)* |
|
|
155
|
+
| `CK_DISABLE_FETCHPROXY` | Set to `1` to skip the fetchproxy fallback (headless / CI) | *(unset)* |
|
|
160
156
|
| `CK_DB_PATH` | Path to SQLite database file | `~/.creditkarma-mcp/transactions.db` |
|
|
161
157
|
|
|
162
158
|
## Troubleshooting
|
|
163
159
|
|
|
164
|
-
**"
|
|
160
|
+
**"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.
|
|
161
|
+
|
|
162
|
+
**"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`.
|
|
163
|
+
|
|
164
|
+
**"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
165
|
|
|
166
|
-
**Sync returns 0 transactions** — check that your
|
|
166
|
+
**Sync returns 0 transactions** — check that your auth is fresh. The refresh token inside the CKAT cookie expires after ~8 hours.
|
|
167
167
|
|
|
168
168
|
**Tools not appearing** — fully quit and relaunch Claude Desktop. In Claude Code, run `/mcp` to check server status.
|
|
169
169
|
|
|
@@ -171,9 +171,10 @@ sync_state (key, value)
|
|
|
171
171
|
|
|
172
172
|
## Security
|
|
173
173
|
|
|
174
|
-
- Credentials are stored only in your local `.env` file (gitignored) or
|
|
175
|
-
- `.env` is written at mode 0600 (owner read/write only) by
|
|
174
|
+
- Credentials are stored only in your local `.env` file (gitignored), Claude config, or your browser's cookie jar (fetchproxy path)
|
|
175
|
+
- `.env` is written at mode 0600 (owner read/write only) by `ck_set_session`
|
|
176
176
|
- `ck_set_session` refuses to save a refresh token whose JWT `exp` is already in the past — prevents stale credentials from polluting `.env`
|
|
177
|
+
- 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
178
|
- The server never logs credentials; warnings go to stderr only (stdout is reserved for the MCP JSON-RPC stream)
|
|
178
179
|
- 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
180
|
|
|
@@ -196,6 +197,7 @@ Changes land via PR, including for solo work — release notes are generated fro
|
|
|
196
197
|
|
|
197
198
|
```
|
|
198
199
|
src/
|
|
200
|
+
auth.ts resolveAuth() — three-path priority (CK_COOKIES env / ck_set_session cache / fetchproxy), plus loadAuthIntoClient()
|
|
199
201
|
client.ts Credit Karma GraphQL client (auto-refresh, JWT helpers, cookie parser)
|
|
200
202
|
index.ts MCP server entry point; bootstraps tokens from CK_COOKIES
|
|
201
203
|
db.ts SQLite schema, migrations, and upsert helpers
|
|
@@ -207,13 +209,11 @@ src/
|
|
|
207
209
|
ck_get_spending_by_category, ck_get_spending_by_merchant,
|
|
208
210
|
ck_get_account_summary
|
|
209
211
|
sql.ts ck_query_sql — SELECT-only escape hatch
|
|
210
|
-
scripts/
|
|
211
|
-
setup-auth.mjs npm run auth — Puppeteer flow + manual paste fallback
|
|
212
212
|
tests/
|
|
213
213
|
helpers.ts Shared test helpers (fakeServer, makeJwt)
|
|
214
|
+
auth.test.ts resolveAuth + loadAuthIntoClient (mocks @fetchproxy/bootstrap)
|
|
214
215
|
client.test.ts
|
|
215
216
|
db.test.ts
|
|
216
|
-
setup-auth.test.ts
|
|
217
217
|
tools/
|
|
218
218
|
auth.test.ts
|
|
219
219
|
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
|
+
}
|