signupgenius-mcp 1.0.1 → 1.0.2

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 CHANGED
@@ -2,11 +2,12 @@
2
2
 
3
3
  MCP server for [SignUpGenius](https://www.signupgenius.com). 13 read tools and 1 write across profile, groups, sign-ups, and reports.
4
4
 
5
- Two auth modes:
6
- - **Session mode (recommended).** Logs in with your normal email/password to call the same web API the signupgenius.com dashboard uses. **Free accounts work.** No SSO/2FA.
7
- - **Key mode.** Uses the documented Pro API key. Required only for the slot REPORT tools (filled/available/all-participants). Pro subscription needed.
5
+ Three auth modes (tried in this priority order — first match wins):
6
+ 1. **Pro key mode.** Uses the documented Pro API key. Required only for the slot REPORT tools (filled/available/all-participants). Pro subscription needed.
7
+ 2. **Session mode.** Logs in with your normal email/password to call the same web API the signupgenius.com dashboard uses. **Free accounts work.** No SSO/2FA.
8
+ 3. **fetchproxy fallback (no env vars needed).** When no env vars are set, the server reads `accessToken` / `cfid` / `cftoken` cookies once at startup from your already-signed-in `signupgenius.com` tab via the [fetchproxy](https://github.com/chrischall/fetchproxy) browser extension. After that one read, all SignUpGenius API calls go directly from Node — the extension is **not** in the request hot path. Install the extension once, sign into SignUpGenius, and the MCP just works.
8
9
 
9
- Both modes can be configured at the same time set `SIGNUPGENIUS_USER_KEY` to force key mode, otherwise email/password takes effect.
10
+ Set `SIGNUPGENIUS_DISABLE_FETCHPROXY=1` to opt out of the fallback (turns missing credentials into a hard error useful in headless CI).
10
11
 
11
12
  ## Tools
12
13
 
@@ -45,9 +46,15 @@ SIGNUPGENIUS_NAME=PTA Org # optional
45
46
 
46
47
  Find the user key in SignUpGenius under **Pro Tools → API Management**.
47
48
 
49
+ ### fetchproxy fallback (no env vars)
50
+
51
+ Install the [fetchproxy extension](https://github.com/chrischall/fetchproxy) (Chrome Web Store / Safari `.dmg`), sign into [signupgenius.com](https://www.signupgenius.com), and remove the env block from your MCP config. The MCP reads `accessToken` / `cfid` / `cftoken` cookies once at startup and uses them like a session-mode login. No password copy-paste required.
52
+
53
+ The slot REPORT tools still require Pro key mode — `SIGNUPGENIUS_USER_KEY` is the only path that hits the documented v2/k Pro API.
54
+
48
55
  ### Both at once
49
56
 
50
- Set both. Key mode wins. Useful if you have Pro for some accounts and want reports while still using your normal login elsewhere.
57
+ Set both Pro key and email/password. Key mode wins. Useful if you have Pro for some accounts and want reports while still using your normal login elsewhere.
51
58
 
52
59
  ### Advanced overrides
53
60
 
@@ -56,6 +63,7 @@ Set both. Key mode wins. Useful if you have Pro for some accounts and want repor
56
63
  | `SIGNUPGENIUS_BASE_URL` | key: `https://api.signupgenius.com/v2/k`<br>session: `https://api.signupgenius.com/v3` | Override the JSON API base. |
57
64
  | `SIGNUPGENIUS_LEGACY_BASE_URL` | `https://www.signupgenius.com` | Override the host for `/SUGboxAPI.cfm?go=…` legacy calls. |
58
65
  | `SIGNUPGENIUS_LOGIN_URL` | `https://www.signupgenius.com` | Override the login form host. |
66
+ | `SIGNUPGENIUS_DISABLE_FETCHPROXY` | unset | Set to `1` to skip the fetchproxy fallback (missing creds become a hard error). |
59
67
 
60
68
  ## ToS caveat
61
69
 
package/dist/auth.js ADDED
@@ -0,0 +1,163 @@
1
+ // ────────────────────────────────────────────────────────────────────────────
2
+ // Auth resolution — Pattern A template
3
+ // ────────────────────────────────────────────────────────────────────────────
4
+ //
5
+ // SignUpGenius supports three auth paths. This file picks one, in priority
6
+ // order, and hands the chosen path to `SignUpGeniusClient`. It mirrors the
7
+ // Pattern A shape used by ofw-mcp/src/auth.ts so all sibling MCPs in this
8
+ // family stay structurally aligned.
9
+ //
10
+ // THE THREE PATHS, in priority order:
11
+ //
12
+ // 1. Pro API key (existing)
13
+ // SIGNUPGENIUS_USER_KEY set → stateless `Authorization: <key>` against
14
+ // the documented v2/k Pro API. The only path that can call slot reports.
15
+ // Unchanged from pre-fetchproxy behavior.
16
+ //
17
+ // 2. Session-login (existing)
18
+ // SIGNUPGENIUS_EMAIL + SIGNUPGENIUS_PASSWORD set → POST the login form,
19
+ // scrape `csrfToken`, capture `accessToken` (JWT) + `cfid`/`cftoken`
20
+ // cookies. Calls go to the v3 web API (Bearer) and the legacy
21
+ // `/SUGboxAPI.cfm` dispatcher (cookies). Unchanged from pre-fetchproxy
22
+ // behavior.
23
+ //
24
+ // 3. fetchproxy fallback (new)
25
+ // When no env vars are set, lift the user's session out of their
26
+ // already-signed-in signupgenius.com browser tab. `@fetchproxy/bootstrap`
27
+ // opens a one-shot WebSocket bridge, asks the extension for the
28
+ // `accessToken` / `MTOKEN` / `cfid` / `cftoken` cookies (all declared
29
+ // upfront — that's the security boundary), and closes the bridge.
30
+ // Subsequent SignUpGenius calls go out via plain Node `fetch()` with
31
+ // those cookies attached — fetchproxy is NOT in the request hot path.
32
+ //
33
+ // Note: `accessToken` and `MTOKEN` carry the same JWT value (verified
34
+ // via DevTools); we accept either and prefer `accessToken` if both are
35
+ // present.
36
+ //
37
+ // Users opt out with SIGNUPGENIUS_DISABLE_FETCHPROXY=1 (anyone who
38
+ // wants the old behavior of "fail loudly when creds are missing").
39
+ //
40
+ // 4. Error
41
+ // Nothing to authenticate with. We throw a message that names both
42
+ // escape hatches: set creds OR install the extension and sign in.
43
+ //
44
+ // Testability:
45
+ // - `@fetchproxy/bootstrap` is mocked at the module boundary in tests.
46
+ // - `loadAccount()` (the existing env-var resolver) is reused as-is so the
47
+ // legacy paths keep working unchanged.
48
+ import { bootstrap } from '@fetchproxy/bootstrap';
49
+ import { loadAccount } from './config.js';
50
+ import pkg from '../package.json' with { type: 'json' };
51
+ function readEnv(key) {
52
+ const raw = process.env[key];
53
+ if (typeof raw !== 'string')
54
+ return undefined;
55
+ const trimmed = raw.trim();
56
+ if (trimmed.length === 0)
57
+ return undefined;
58
+ if (trimmed === 'undefined' || trimmed === 'null')
59
+ return undefined;
60
+ if (/^\$\{[^}]*\}$/.test(trimmed))
61
+ return undefined;
62
+ return trimmed;
63
+ }
64
+ function fetchproxyDisabled() {
65
+ const raw = readEnv('SIGNUPGENIUS_DISABLE_FETCHPROXY');
66
+ if (raw === undefined)
67
+ return false;
68
+ return ['1', 'true', 'yes', 'on'].includes(raw.toLowerCase());
69
+ }
70
+ /**
71
+ * The exact error message `loadAccount()` throws when NO auth env vars are
72
+ * set. We catch this specific string so partial-config errors (which the
73
+ * user MUST fix) still propagate, but the "you didn't set anything at all"
74
+ * case falls through to fetchproxy.
75
+ */
76
+ const NO_ENV_CONFIG_MARKER = 'Missing SignUpGenius auth config';
77
+ /**
78
+ * Resolve SignUpGenius auth using the three-path priority described at the
79
+ * top of this file. Throws with an actionable message when no path succeeds.
80
+ */
81
+ export async function resolveAuth() {
82
+ // ── Paths 1 & 2: env-var credentials. loadAccount() handles precedence,
83
+ // partial-config errors, and env-var sanitization for us.
84
+ try {
85
+ const account = loadAccount();
86
+ return { account, source: 'env' };
87
+ }
88
+ catch (e) {
89
+ // `loadAccount()` only ever throws plain Error instances (validated by
90
+ // tests in config.test.ts). Partial-config errors (missing one of the
91
+ // EMAIL/PASSWORD pair, non-https override URL, etc.) are USER MISTAKES
92
+ // and should propagate. Only the "nothing set at all" case is allowed
93
+ // to fall through to fetchproxy.
94
+ if (!e.message.startsWith(NO_ENV_CONFIG_MARKER)) {
95
+ throw e;
96
+ }
97
+ }
98
+ // ── Path 3: fetchproxy fallback.
99
+ if (!fetchproxyDisabled()) {
100
+ try {
101
+ const session = await bootstrap({
102
+ serverName: pkg.name,
103
+ version: pkg.version,
104
+ domains: ['signupgenius.com'],
105
+ declare: {
106
+ // Declare ALL the cookies we might need. The 0.3.0 read_cookies
107
+ // capability uses chrome.cookies.get (HttpOnly-visible) — the
108
+ // security gate is this declared key list, not HttpOnly status.
109
+ // MTOKEN is signupgenius.com's older name for the JWT; on some
110
+ // browsers/sessions one shows up first. accessToken takes priority
111
+ // when both are present.
112
+ cookies: ['MTOKEN', 'accessToken', 'cfid', 'cftoken'],
113
+ localStorage: [],
114
+ sessionStorage: [],
115
+ captureHeaders: [],
116
+ },
117
+ });
118
+ const accessToken = session.cookies['accessToken'] ?? session.cookies['MTOKEN'];
119
+ if (!accessToken) {
120
+ throw new Error('accessToken cookie missing on signupgenius.com. ' +
121
+ 'Sign into signupgenius.com in your browser (with the fetchproxy extension installed) and retry.');
122
+ }
123
+ // Build the cookie header the legacy /SUGboxAPI.cfm dispatcher expects.
124
+ // accessToken first (the JWT also lives in this cookie jar) then the CF
125
+ // pair if present. Anything else gets ignored.
126
+ const parts = [`accessToken=${accessToken}`];
127
+ const cfid = session.cookies['cfid'];
128
+ const cftoken = session.cookies['cftoken'];
129
+ if (cfid)
130
+ parts.push(`cfid=${cfid}`);
131
+ if (cftoken)
132
+ parts.push(`cftoken=${cftoken}`);
133
+ const cookieHeader = parts.join('; ');
134
+ // Synthesize a session account with empty creds — the client will see
135
+ // `preloaded` and skip the form login. The bases match the defaults
136
+ // used by `loadAccount()` so behavior is identical from here on.
137
+ const account = {
138
+ mode: 'session',
139
+ name: 'signupgenius.com (browser)',
140
+ baseUrl: 'https://api.signupgenius.com/v3',
141
+ legacyBaseUrl: 'https://www.signupgenius.com',
142
+ loginBaseUrl: 'https://www.signupgenius.com',
143
+ email: '',
144
+ password: '',
145
+ };
146
+ return {
147
+ account,
148
+ preloaded: { accessToken, cookieHeader },
149
+ source: 'fetchproxy',
150
+ };
151
+ }
152
+ catch (e) {
153
+ const msg = e instanceof Error ? e.message : String(e);
154
+ throw new Error('SignUpGenius auth: no SIGNUPGENIUS_USER_KEY or SIGNUPGENIUS_EMAIL/PASSWORD set, ' +
155
+ `and fetchproxy fallback failed: ${msg}`);
156
+ }
157
+ }
158
+ // ── Path 4: nothing configured and fetchproxy explicitly disabled.
159
+ throw new Error('Missing SignUpGenius auth config. Set SIGNUPGENIUS_USER_KEY (Pro API), ' +
160
+ 'or SIGNUPGENIUS_EMAIL + SIGNUPGENIUS_PASSWORD (session mode, free accounts), ' +
161
+ 'or install the fetchproxy extension and sign into signupgenius.com ' +
162
+ '(unset SIGNUPGENIUS_DISABLE_FETCHPROXY if it is set).');
163
+ }