@digestseo/mcp-gsc 0.3.1

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/src/storage.ts ADDED
@@ -0,0 +1,98 @@
1
+ import { encryptToken, decryptToken } from './crypto';
2
+
3
+ interface StorageEnv {
4
+ OAUTH_KV: KVNamespace;
5
+ USER_KV: KVNamespace;
6
+ TOKEN_ENCRYPTION_KEY: string;
7
+ }
8
+
9
+ const PENDING_TTL_SECONDS = 600;
10
+
11
+ export interface PendingAuthRequest {
12
+ claudeAuthRequest: unknown;
13
+ created_at: number;
14
+ }
15
+
16
+ export async function stashPendingAuth(
17
+ env: StorageEnv,
18
+ nonce: string,
19
+ claudeAuthRequest: unknown,
20
+ ): Promise<void> {
21
+ const record: PendingAuthRequest = {
22
+ claudeAuthRequest,
23
+ created_at: Date.now(),
24
+ };
25
+ await env.OAUTH_KV.put(`pending:${nonce}`, JSON.stringify(record), {
26
+ expirationTtl: PENDING_TTL_SECONDS,
27
+ });
28
+ }
29
+
30
+ export async function popPendingAuth(
31
+ env: StorageEnv,
32
+ nonce: string,
33
+ ): Promise<PendingAuthRequest | null> {
34
+ const key = `pending:${nonce}`;
35
+ const raw = await env.OAUTH_KV.get(key);
36
+ if (!raw) return null;
37
+ await env.OAUTH_KV.delete(key);
38
+ return JSON.parse(raw) as PendingAuthRequest;
39
+ }
40
+
41
+ export interface UserRecord {
42
+ email: string;
43
+ encrypted_refresh_token: string;
44
+ iv: string;
45
+ created_at: number;
46
+ updated_at: number;
47
+ }
48
+
49
+ export async function saveUser(
50
+ env: StorageEnv,
51
+ googleId: string,
52
+ email: string,
53
+ refreshToken: string,
54
+ ): Promise<void> {
55
+ const { ciphertext, iv } = await encryptToken(
56
+ refreshToken,
57
+ env.TOKEN_ENCRYPTION_KEY,
58
+ );
59
+ const existing = await getUser(env, googleId);
60
+ const now = Date.now();
61
+ const record: UserRecord = {
62
+ email,
63
+ encrypted_refresh_token: ciphertext,
64
+ iv,
65
+ created_at: existing?.created_at ?? now,
66
+ updated_at: now,
67
+ };
68
+ await env.USER_KV.put(`user:${googleId}`, JSON.stringify(record));
69
+ }
70
+
71
+ export async function getUser(
72
+ env: StorageEnv,
73
+ googleId: string,
74
+ ): Promise<UserRecord | null> {
75
+ const raw = await env.USER_KV.get(`user:${googleId}`);
76
+ if (!raw) return null;
77
+ return JSON.parse(raw) as UserRecord;
78
+ }
79
+
80
+ export async function deleteUser(
81
+ env: StorageEnv,
82
+ googleId: string,
83
+ ): Promise<void> {
84
+ await env.USER_KV.delete(`user:${googleId}`);
85
+ }
86
+
87
+ export async function getDecryptedRefreshToken(
88
+ env: StorageEnv,
89
+ googleId: string,
90
+ ): Promise<string | null> {
91
+ const user = await getUser(env, googleId);
92
+ if (!user) return null;
93
+ return await decryptToken(
94
+ user.encrypted_refresh_token,
95
+ user.iv,
96
+ env.TOKEN_ENCRYPTION_KEY,
97
+ );
98
+ }
@@ -0,0 +1,35 @@
1
+ // Cloudflare Worker configuration for mcp-gsc.
2
+ //
3
+ // Copy this file to `wrangler.jsonc` (gitignored) and fill in your own
4
+ // values before deploying. See SETUP.md for the full walkthrough.
5
+ {
6
+ "name": "mcp-gsc",
7
+ "main": "src/index.ts",
8
+ "compatibility_date": "2026-05-19",
9
+ "compatibility_flags": ["nodejs_compat"],
10
+ "observability": { "enabled": true },
11
+ "kv_namespaces": [
12
+ {
13
+ "binding": "OAUTH_KV",
14
+ "id": "YOUR_OAUTH_KV_ID"
15
+ },
16
+ {
17
+ "binding": "USER_KV",
18
+ "id": "YOUR_USER_KV_ID"
19
+ }
20
+ ],
21
+ "durable_objects": {
22
+ "bindings": [
23
+ {
24
+ "name": "MCP_OBJECT",
25
+ "class_name": "GscMcpAgent"
26
+ }
27
+ ]
28
+ },
29
+ "migrations": [
30
+ {
31
+ "tag": "v1",
32
+ "new_sqlite_classes": ["GscMcpAgent"]
33
+ }
34
+ ]
35
+ }