@atoms-tech/atoms-mcp 0.1.0

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.
Files changed (110) hide show
  1. package/bin/atoms-mcp.js +2 -0
  2. package/dist/auth/login.d.ts +23 -0
  3. package/dist/auth/login.d.ts.map +1 -0
  4. package/dist/auth/login.js +246 -0
  5. package/dist/auth/login.js.map +1 -0
  6. package/dist/auth/refresh.d.ts +17 -0
  7. package/dist/auth/refresh.d.ts.map +1 -0
  8. package/dist/auth/refresh.js +82 -0
  9. package/dist/auth/refresh.js.map +1 -0
  10. package/dist/auth/token-store.d.ts +34 -0
  11. package/dist/auth/token-store.d.ts.map +1 -0
  12. package/dist/auth/token-store.js +61 -0
  13. package/dist/auth/token-store.js.map +1 -0
  14. package/dist/config.d.ts +17 -0
  15. package/dist/config.d.ts.map +1 -0
  16. package/dist/config.js +17 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/db/client.d.ts +30 -0
  19. package/dist/db/client.d.ts.map +1 -0
  20. package/dist/db/client.js +110 -0
  21. package/dist/db/client.js.map +1 -0
  22. package/dist/db/graph.d.ts +8 -0
  23. package/dist/db/graph.d.ts.map +1 -0
  24. package/dist/db/graph.js +8 -0
  25. package/dist/db/graph.js.map +1 -0
  26. package/dist/db/queries.d.ts +77 -0
  27. package/dist/db/queries.d.ts.map +1 -0
  28. package/dist/db/queries.js +210 -0
  29. package/dist/db/queries.js.map +1 -0
  30. package/dist/index.d.ts +12 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +92 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/middleware/audit.d.ts +26 -0
  35. package/dist/middleware/audit.d.ts.map +1 -0
  36. package/dist/middleware/audit.js +44 -0
  37. package/dist/middleware/audit.js.map +1 -0
  38. package/dist/middleware/rate-limiter.d.ts +21 -0
  39. package/dist/middleware/rate-limiter.d.ts.map +1 -0
  40. package/dist/middleware/rate-limiter.js +43 -0
  41. package/dist/middleware/rate-limiter.js.map +1 -0
  42. package/dist/middleware/validator.d.ts +22 -0
  43. package/dist/middleware/validator.d.ts.map +1 -0
  44. package/dist/middleware/validator.js +91 -0
  45. package/dist/middleware/validator.js.map +1 -0
  46. package/dist/server.d.ts +14 -0
  47. package/dist/server.d.ts.map +1 -0
  48. package/dist/server.js +511 -0
  49. package/dist/server.js.map +1 -0
  50. package/dist/tools/_base.d.ts +58 -0
  51. package/dist/tools/_base.d.ts.map +1 -0
  52. package/dist/tools/_base.js +109 -0
  53. package/dist/tools/_base.js.map +1 -0
  54. package/dist/tools/create-item.d.ts +43 -0
  55. package/dist/tools/create-item.d.ts.map +1 -0
  56. package/dist/tools/create-item.js +118 -0
  57. package/dist/tools/create-item.js.map +1 -0
  58. package/dist/tools/delete-item.d.ts +38 -0
  59. package/dist/tools/delete-item.d.ts.map +1 -0
  60. package/dist/tools/delete-item.js +69 -0
  61. package/dist/tools/delete-item.js.map +1 -0
  62. package/dist/tools/export-mermaid.d.ts +36 -0
  63. package/dist/tools/export-mermaid.d.ts.map +1 -0
  64. package/dist/tools/export-mermaid.js +125 -0
  65. package/dist/tools/export-mermaid.js.map +1 -0
  66. package/dist/tools/get-coverage.d.ts +34 -0
  67. package/dist/tools/get-coverage.d.ts.map +1 -0
  68. package/dist/tools/get-coverage.js +36 -0
  69. package/dist/tools/get-coverage.js.map +1 -0
  70. package/dist/tools/get-history.d.ts +34 -0
  71. package/dist/tools/get-history.d.ts.map +1 -0
  72. package/dist/tools/get-history.js +53 -0
  73. package/dist/tools/get-history.js.map +1 -0
  74. package/dist/tools/get-item.d.ts +62 -0
  75. package/dist/tools/get-item.d.ts.map +1 -0
  76. package/dist/tools/get-item.js +93 -0
  77. package/dist/tools/get-item.js.map +1 -0
  78. package/dist/tools/link-items.d.ts +41 -0
  79. package/dist/tools/link-items.d.ts.map +1 -0
  80. package/dist/tools/link-items.js +150 -0
  81. package/dist/tools/link-items.js.map +1 -0
  82. package/dist/tools/list-items.d.ts +37 -0
  83. package/dist/tools/list-items.d.ts.map +1 -0
  84. package/dist/tools/list-items.js +36 -0
  85. package/dist/tools/list-items.js.map +1 -0
  86. package/dist/tools/list-projects.d.ts +38 -0
  87. package/dist/tools/list-projects.d.ts.map +1 -0
  88. package/dist/tools/list-projects.js +28 -0
  89. package/dist/tools/list-projects.js.map +1 -0
  90. package/dist/tools/record-test-result.d.ts +41 -0
  91. package/dist/tools/record-test-result.d.ts.map +1 -0
  92. package/dist/tools/record-test-result.js +80 -0
  93. package/dist/tools/record-test-result.js.map +1 -0
  94. package/dist/tools/search.d.ts +34 -0
  95. package/dist/tools/search.d.ts.map +1 -0
  96. package/dist/tools/search.js +28 -0
  97. package/dist/tools/search.js.map +1 -0
  98. package/dist/tools/update-item.d.ts +43 -0
  99. package/dist/tools/update-item.d.ts.map +1 -0
  100. package/dist/tools/update-item.js +98 -0
  101. package/dist/tools/update-item.js.map +1 -0
  102. package/dist/types/responses.d.ts +58 -0
  103. package/dist/types/responses.d.ts.map +1 -0
  104. package/dist/types/responses.js +6 -0
  105. package/dist/types/responses.js.map +1 -0
  106. package/dist/types/work-item.d.ts +69 -0
  107. package/dist/types/work-item.d.ts.map +1 -0
  108. package/dist/types/work-item.js +8 -0
  109. package/dist/types/work-item.js.map +1 -0
  110. package/package.json +50 -0
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import("../dist/index.js");
@@ -0,0 +1,23 @@
1
+ /**
2
+ * OAuth 2.1 PKCE login flow for the MCP CLI.
3
+ *
4
+ * Flow:
5
+ * 1. Generate PKCE code_verifier + code_challenge
6
+ * 2. Start HTTP server on localhost:19275
7
+ * 3. Open browser to x.atoms.tech/auth/mcp-login (or Supabase auth directly)
8
+ * 4. User authenticates → redirect to localhost with tokens
9
+ * 5. Store tokens in ~/.atoms/credentials.json
10
+ */
11
+ /**
12
+ * Run the interactive login flow.
13
+ * Opens browser, waits for callback, stores tokens.
14
+ *
15
+ * Uses OAuth 2.1 PKCE flow:
16
+ * 1. Redirect to Supabase with code_challenge
17
+ * 2. Supabase returns authorization code to localhost callback
18
+ * 3. Exchange code + code_verifier for session tokens
19
+ */
20
+ export declare function login(): Promise<{
21
+ email: string;
22
+ }>;
23
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/auth/login.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAWH;;;;;;;;GAQG;AACH,wBAAsB,KAAK,IAAI,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAyLxD"}
@@ -0,0 +1,246 @@
1
+ /**
2
+ * OAuth 2.1 PKCE login flow for the MCP CLI.
3
+ *
4
+ * Flow:
5
+ * 1. Generate PKCE code_verifier + code_challenge
6
+ * 2. Start HTTP server on localhost:19275
7
+ * 3. Open browser to x.atoms.tech/auth/mcp-login (or Supabase auth directly)
8
+ * 4. User authenticates → redirect to localhost with tokens
9
+ * 5. Store tokens in ~/.atoms/credentials.json
10
+ */
11
+ import { createServer } from "node:http";
12
+ import { randomBytes, createHash } from "node:crypto";
13
+ import { URL } from "node:url";
14
+ import { writeCredentials } from "./token-store.js";
15
+ import { ATOMS_SUPABASE_URL, ATOMS_SUPABASE_ANON_KEY } from "../config.js";
16
+ const CALLBACK_PORT = 19275;
17
+ const CALLBACK_PATH = "/callback";
18
+ /**
19
+ * Run the interactive login flow.
20
+ * Opens browser, waits for callback, stores tokens.
21
+ *
22
+ * Uses OAuth 2.1 PKCE flow:
23
+ * 1. Redirect to Supabase with code_challenge
24
+ * 2. Supabase returns authorization code to localhost callback
25
+ * 3. Exchange code + code_verifier for session tokens
26
+ */
27
+ export async function login() {
28
+ // Generate PKCE challenge
29
+ const codeVerifier = randomBytes(32).toString("base64url");
30
+ const codeChallenge = createHash("sha256")
31
+ .update(codeVerifier)
32
+ .digest("base64url");
33
+ const redirectTo = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;
34
+ // Build auth URL
35
+ const authUrl = new URL(`${ATOMS_SUPABASE_URL}/auth/v1/authorize`);
36
+ authUrl.searchParams.set("provider", "google");
37
+ authUrl.searchParams.set("redirect_to", redirectTo);
38
+ authUrl.searchParams.set("code_challenge", codeChallenge);
39
+ authUrl.searchParams.set("code_challenge_method", "S256");
40
+ return new Promise((resolve, reject) => {
41
+ const server = createServer(async (req, res) => {
42
+ const url = new URL(req.url ?? "/", `http://localhost:${CALLBACK_PORT}`);
43
+ if (url.pathname !== CALLBACK_PATH) {
44
+ res.writeHead(404);
45
+ res.end("Not found");
46
+ return;
47
+ }
48
+ try {
49
+ // PKCE flow: Supabase redirects with ?code=xxx in query params
50
+ const code = url.searchParams.get("code");
51
+ if (code) {
52
+ // Exchange authorization code + code_verifier for tokens via REST API
53
+ const tokenRes = await fetch(`${ATOMS_SUPABASE_URL}/auth/v1/token?grant_type=pkce`, {
54
+ method: "POST",
55
+ headers: {
56
+ "Content-Type": "application/json",
57
+ "apikey": ATOMS_SUPABASE_ANON_KEY,
58
+ },
59
+ body: JSON.stringify({
60
+ auth_code: code,
61
+ code_verifier: codeVerifier,
62
+ }),
63
+ });
64
+ const tokenData = await tokenRes.json();
65
+ if (!tokenRes.ok || !tokenData.access_token) {
66
+ const errMsg = tokenData.error_description ?? tokenData.msg ?? "Unknown error";
67
+ res.writeHead(200, { "Content-Type": "text/html" });
68
+ res.end(failurePage(`Code exchange failed: ${errMsg}`));
69
+ server.close();
70
+ reject(new Error(`Code exchange failed: ${errMsg}`));
71
+ return;
72
+ }
73
+ // Decode JWT to get email
74
+ let email = "authenticated";
75
+ try {
76
+ const payload = JSON.parse(Buffer.from(tokenData.access_token.split(".")[1], "base64").toString());
77
+ email = payload.email ?? email;
78
+ }
79
+ catch { /* ignore decode errors */ }
80
+ // Verify user has an ATOMS account (belongs to at least one org)
81
+ const { createClient } = await import("@supabase/supabase-js");
82
+ const verifyClient = createClient(ATOMS_SUPABASE_URL, ATOMS_SUPABASE_ANON_KEY, {
83
+ global: {
84
+ headers: { Authorization: `Bearer ${tokenData.access_token}` },
85
+ },
86
+ });
87
+ const { data: memberships, error: memErr } = await verifyClient
88
+ .from("org_members")
89
+ .select("org_id")
90
+ .limit(1);
91
+ if (memErr || !memberships || memberships.length === 0) {
92
+ // User authenticated with Google but has no ATOMS account
93
+ res.writeHead(200, { "Content-Type": "text/html" });
94
+ res.end(failurePage("No ATOMS account found for this Google account.<br><br>" +
95
+ "Please sign up at <a href=\"https://x.atoms.tech\">x.atoms.tech</a> first, " +
96
+ "then run this login command again."));
97
+ server.close();
98
+ reject(new Error("No ATOMS account found. Sign up at x.atoms.tech first."));
99
+ return;
100
+ }
101
+ await writeCredentials({
102
+ access_token: tokenData.access_token,
103
+ refresh_token: tokenData.refresh_token,
104
+ expires_at: Date.now() + (tokenData.expires_in ?? 3600) * 1000,
105
+ user_email: email,
106
+ });
107
+ res.writeHead(200, { "Content-Type": "text/html" });
108
+ res.end(successPage(email));
109
+ server.close();
110
+ resolve({ email });
111
+ return;
112
+ }
113
+ // Fallback: check for tokens in hash fragment via extractor page
114
+ const accessToken = url.searchParams.get("access_token");
115
+ const refreshToken = url.searchParams.get("refresh_token");
116
+ const expiresIn = parseInt(url.searchParams.get("expires_in") ?? "3600", 10);
117
+ if (accessToken && refreshToken) {
118
+ await writeCredentials({
119
+ access_token: accessToken,
120
+ refresh_token: refreshToken,
121
+ expires_at: Date.now() + expiresIn * 1000,
122
+ });
123
+ res.writeHead(200, { "Content-Type": "text/html" });
124
+ res.end(successPage("authenticated"));
125
+ server.close();
126
+ resolve({ email: "authenticated" });
127
+ }
128
+ else {
129
+ // Serve a page that extracts hash fragment and POSTs back
130
+ res.writeHead(200, { "Content-Type": "text/html" });
131
+ res.end(extractorPage());
132
+ }
133
+ }
134
+ catch (err) {
135
+ res.writeHead(500);
136
+ res.end("Authentication failed");
137
+ server.close();
138
+ reject(err);
139
+ }
140
+ });
141
+ // Handle POST from the extractor page (hash fragment fallback)
142
+ server.on("request", async (req, res) => {
143
+ if (req.method === "POST" && req.url === "/token") {
144
+ let body = "";
145
+ req.on("data", (chunk) => { body += chunk.toString(); });
146
+ req.on("end", async () => {
147
+ try {
148
+ const data = JSON.parse(body);
149
+ await writeCredentials({
150
+ access_token: data.access_token,
151
+ refresh_token: data.refresh_token,
152
+ expires_at: Date.now() + (data.expires_in ?? 3600) * 1000,
153
+ user_email: data.user_email,
154
+ });
155
+ res.writeHead(200, { "Content-Type": "application/json" });
156
+ res.end(JSON.stringify({ ok: true }));
157
+ server.close();
158
+ resolve({ email: data.user_email ?? "authenticated" });
159
+ }
160
+ catch (err) {
161
+ res.writeHead(500);
162
+ res.end("Failed to store credentials");
163
+ server.close();
164
+ reject(err);
165
+ }
166
+ });
167
+ }
168
+ });
169
+ server.listen(CALLBACK_PORT, "127.0.0.1", async () => {
170
+ process.stderr.write(`\nOpening browser for ATOMS login...\n`);
171
+ process.stderr.write(`If the browser doesn't open, visit:\n${authUrl.toString()}\n\n`);
172
+ try {
173
+ const { default: openBrowser } = await import("open");
174
+ await openBrowser(authUrl.toString());
175
+ }
176
+ catch {
177
+ process.stderr.write("Could not open browser automatically. Please open the URL above.\n");
178
+ }
179
+ });
180
+ // Timeout after 5 minutes
181
+ setTimeout(() => {
182
+ server.close();
183
+ reject(new Error("Login timed out after 5 minutes"));
184
+ }, 5 * 60_000);
185
+ });
186
+ }
187
+ function extractorPage() {
188
+ return `<!DOCTYPE html>
189
+ <html>
190
+ <head><title>ATOMS MCP Login</title></head>
191
+ <body>
192
+ <h1>Completing login...</h1>
193
+ <script>
194
+ const hash = window.location.hash.substring(1);
195
+ const params = new URLSearchParams(hash);
196
+ const data = {
197
+ access_token: params.get('access_token'),
198
+ refresh_token: params.get('refresh_token'),
199
+ expires_in: params.get('expires_in'),
200
+ user_email: ''
201
+ };
202
+ if (data.access_token) {
203
+ // Decode JWT to get email
204
+ try {
205
+ const payload = JSON.parse(atob(data.access_token.split('.')[1]));
206
+ data.user_email = payload.email || '';
207
+ } catch(e) {}
208
+ fetch('/token', {
209
+ method: 'POST',
210
+ headers: { 'Content-Type': 'application/json' },
211
+ body: JSON.stringify(data)
212
+ }).then(() => {
213
+ document.body.innerHTML = '<h1>Login successful! You can close this tab.</h1>';
214
+ }).catch(() => {
215
+ document.body.innerHTML = '<h1>Login failed. Please try again.</h1>';
216
+ });
217
+ } else {
218
+ document.body.innerHTML = '<h1>Login failed — no tokens received.</h1>';
219
+ }
220
+ </script>
221
+ </body>
222
+ </html>`;
223
+ }
224
+ function successPage(email) {
225
+ return `<!DOCTYPE html>
226
+ <html>
227
+ <head><title>ATOMS MCP Login</title></head>
228
+ <body style="font-family:system-ui;text-align:center;padding:60px">
229
+ <h1 style="color:#22c55e">Login successful!</h1>
230
+ <p>Authenticated as <strong>${email}</strong></p>
231
+ <p>You can close this tab and return to your terminal.</p>
232
+ </body>
233
+ </html>`;
234
+ }
235
+ function failurePage(reason) {
236
+ return `<!DOCTYPE html>
237
+ <html>
238
+ <head><title>ATOMS MCP Login</title></head>
239
+ <body style="font-family:system-ui;text-align:center;padding:60px">
240
+ <h1 style="color:#ef4444">Login failed</h1>
241
+ <p>${reason}</p>
242
+ <p>Please close this tab and try again.</p>
243
+ </body>
244
+ </html>`;
245
+ }
246
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/auth/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * JWT refresh logic for MCP server startup.
3
+ *
4
+ * On each MCP session start, we check if the access_token is expired
5
+ * and use the refresh_token to get a new one from Supabase.
6
+ */
7
+ /**
8
+ * Get a valid access token, refreshing if needed.
9
+ * Returns { access_token, user_id, email } or throws.
10
+ */
11
+ export declare function getValidToken(): Promise<{
12
+ access_token: string;
13
+ refresh_token: string;
14
+ user_id: string;
15
+ email: string;
16
+ }>;
17
+ //# sourceMappingURL=refresh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../../src/auth/refresh.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CA+DD"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * JWT refresh logic for MCP server startup.
3
+ *
4
+ * On each MCP session start, we check if the access_token is expired
5
+ * and use the refresh_token to get a new one from Supabase.
6
+ */
7
+ import { createClient } from "@supabase/supabase-js";
8
+ import { readCredentials, writeCredentials } from "./token-store.js";
9
+ import { ATOMS_SUPABASE_URL, ATOMS_SUPABASE_ANON_KEY } from "../config.js";
10
+ /**
11
+ * Get a valid access token, refreshing if needed.
12
+ * Returns { access_token, user_id, email } or throws.
13
+ */
14
+ export async function getValidToken() {
15
+ // Priority 1: Environment variable (for CI/CD, headless)
16
+ const envToken = process.env.ATOMS_ACCESS_TOKEN;
17
+ if (envToken) {
18
+ const payload = decodeJwtPayload(envToken);
19
+ return {
20
+ access_token: envToken,
21
+ refresh_token: "",
22
+ user_id: payload.sub,
23
+ email: payload.email ?? "",
24
+ };
25
+ }
26
+ // Priority 2: Stored credentials
27
+ const creds = await readCredentials();
28
+ if (!creds) {
29
+ throw new Error("Not authenticated. Run 'npx @atoms-tech/atoms-mcp login' to connect your ATOMS account.");
30
+ }
31
+ // Check if token is still valid (with 60s buffer)
32
+ if (creds.expires_at > Date.now() + 60_000) {
33
+ const payload = decodeJwtPayload(creds.access_token);
34
+ return {
35
+ access_token: creds.access_token,
36
+ refresh_token: creds.refresh_token,
37
+ user_id: payload.sub,
38
+ email: payload.email ?? creds.user_email ?? "",
39
+ };
40
+ }
41
+ // Token expired — refresh it
42
+ process.stderr.write("[atoms-mcp] Refreshing access token...\n");
43
+ const supabase = createClient(ATOMS_SUPABASE_URL, ATOMS_SUPABASE_ANON_KEY);
44
+ const { data, error } = await supabase.auth.refreshSession({
45
+ refresh_token: creds.refresh_token,
46
+ });
47
+ if (error || !data.session) {
48
+ throw new Error(`Token refresh failed: ${error?.message ?? "No session returned"}. ` +
49
+ "Re-run 'npx @atoms-tech/atoms-mcp login'.");
50
+ }
51
+ const session = data.session;
52
+ // Update stored credentials
53
+ await writeCredentials({
54
+ access_token: session.access_token,
55
+ refresh_token: session.refresh_token,
56
+ expires_at: Date.now() + (session.expires_in ?? 3600) * 1000,
57
+ user_email: session.user?.email,
58
+ });
59
+ return {
60
+ access_token: session.access_token,
61
+ refresh_token: session.refresh_token,
62
+ user_id: session.user?.id ?? "",
63
+ email: session.user?.email ?? "",
64
+ };
65
+ }
66
+ /**
67
+ * Decode JWT payload without verification (Supabase already verified it).
68
+ */
69
+ function decodeJwtPayload(jwt) {
70
+ const parts = jwt.split(".");
71
+ if (parts.length !== 3)
72
+ throw new Error("Invalid JWT format");
73
+ const padded = parts[1]
74
+ .replace(/-/g, "+")
75
+ .replace(/_/g, "/")
76
+ .padEnd(parts[1].length + ((4 - (parts[1].length % 4)) % 4), "=");
77
+ const payload = JSON.parse(Buffer.from(padded, "base64").toString("utf-8"));
78
+ if (!payload.sub)
79
+ throw new Error("JWT missing 'sub' claim");
80
+ return payload;
81
+ }
82
+ //# sourceMappingURL=refresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../src/auth/refresh.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Token storage for MCP auth credentials.
3
+ *
4
+ * Stores access_token + refresh_token in ~/.atoms/credentials.json
5
+ * with file permissions 0600 (owner read/write only).
6
+ */
7
+ export interface StoredCredentials {
8
+ access_token: string;
9
+ refresh_token: string;
10
+ expires_at: number;
11
+ user_email?: string;
12
+ supabase_url?: string;
13
+ }
14
+ /**
15
+ * Read stored credentials. Returns null if not found or invalid.
16
+ */
17
+ export declare function readCredentials(): Promise<StoredCredentials | null>;
18
+ /**
19
+ * Write credentials to disk with secure permissions.
20
+ */
21
+ export declare function writeCredentials(creds: StoredCredentials): Promise<void>;
22
+ /**
23
+ * Check if stored credentials exist and are not expired.
24
+ */
25
+ export declare function hasValidCredentials(): Promise<boolean>;
26
+ /**
27
+ * Delete stored credentials (logout).
28
+ */
29
+ export declare function clearCredentials(): Promise<void>;
30
+ /**
31
+ * Get the credentials file path (for user-facing messages).
32
+ */
33
+ export declare function getCredentialsPath(): string;
34
+ //# sourceMappingURL=token-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CASzE;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAO9E;AAED;;GAEG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAK5D;AAED;;GAEG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAMtD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Token storage for MCP auth credentials.
3
+ *
4
+ * Stores access_token + refresh_token in ~/.atoms/credentials.json
5
+ * with file permissions 0600 (owner read/write only).
6
+ */
7
+ import { readFile, writeFile, mkdir, unlink } from "node:fs/promises";
8
+ import { homedir } from "node:os";
9
+ import { join } from "node:path";
10
+ const ATOMS_DIR = join(homedir(), ".atoms");
11
+ const CREDENTIALS_FILE = join(ATOMS_DIR, "credentials.json");
12
+ /**
13
+ * Read stored credentials. Returns null if not found or invalid.
14
+ */
15
+ export async function readCredentials() {
16
+ try {
17
+ const content = await readFile(CREDENTIALS_FILE, "utf-8");
18
+ const creds = JSON.parse(content);
19
+ if (!creds.access_token || !creds.refresh_token)
20
+ return null;
21
+ return creds;
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ /**
28
+ * Write credentials to disk with secure permissions.
29
+ */
30
+ export async function writeCredentials(creds) {
31
+ await mkdir(ATOMS_DIR, { recursive: true, mode: 0o700 });
32
+ await writeFile(CREDENTIALS_FILE, JSON.stringify(creds, null, 2), { mode: 0o600 });
33
+ }
34
+ /**
35
+ * Check if stored credentials exist and are not expired.
36
+ */
37
+ export async function hasValidCredentials() {
38
+ const creds = await readCredentials();
39
+ if (!creds)
40
+ return false;
41
+ // Consider expired if less than 60 seconds remaining
42
+ return creds.expires_at > Date.now() + 60_000;
43
+ }
44
+ /**
45
+ * Delete stored credentials (logout).
46
+ */
47
+ export async function clearCredentials() {
48
+ try {
49
+ await unlink(CREDENTIALS_FILE);
50
+ }
51
+ catch {
52
+ // File doesn't exist — already logged out
53
+ }
54
+ }
55
+ /**
56
+ * Get the credentials file path (for user-facing messages).
57
+ */
58
+ export function getCredentialsPath() {
59
+ return CREDENTIALS_FILE;
60
+ }
61
+ //# sourceMappingURL=token-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-store.js","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AAC5C,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC;AAU7D;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAsB,CAAC;QACvD,IAAI,CAAC,KAAK,CAAC,YAAY,IAAI,CAAC,KAAK,CAAC,aAAa;YAAE,OAAO,IAAI,CAAC;QAC7D,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAwB;IAC7D,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzD,MAAM,SAAS,CACb,gBAAgB,EAChB,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAC9B,EAAE,IAAI,EAAE,KAAK,EAAE,CAChB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,MAAM,KAAK,GAAG,MAAM,eAAe,EAAE,CAAC;IACtC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,qDAAqD;IACrD,OAAO,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC;AAChD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,0CAA0C;IAC5C,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO,gBAAgB,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * ATOMS MCP Server — Public client configuration.
3
+ *
4
+ * These are PUBLISHABLE values — identical to the ones embedded in the
5
+ * ATOMS.tech React frontend bundle (NEXT_PUBLIC_SUPABASE_*).
6
+ *
7
+ * The anon key is NOT a secret. It only grants access to Supabase's API
8
+ * gateway. All data access is gated by Row Level Security (RLS) policies
9
+ * which require a valid user JWT. Without authentication, these values
10
+ * grant zero data access.
11
+ *
12
+ * This is the standard pattern used by Supabase, Firebase, and Clerk
13
+ * for client-side applications.
14
+ */
15
+ export declare const ATOMS_SUPABASE_URL = "https://gmebjyhomsbvhrxffzre.supabase.co";
16
+ export declare const ATOMS_SUPABASE_ANON_KEY = "sb_publishable_b49MUAB8XCrQiF7x5b9lUA_w1aplSBH";
17
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,eAAO,MAAM,kBAAkB,6CAA6C,CAAC;AAC7E,eAAO,MAAM,uBAAuB,mDACc,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * ATOMS MCP Server — Public client configuration.
3
+ *
4
+ * These are PUBLISHABLE values — identical to the ones embedded in the
5
+ * ATOMS.tech React frontend bundle (NEXT_PUBLIC_SUPABASE_*).
6
+ *
7
+ * The anon key is NOT a secret. It only grants access to Supabase's API
8
+ * gateway. All data access is gated by Row Level Security (RLS) policies
9
+ * which require a valid user JWT. Without authentication, these values
10
+ * grant zero data access.
11
+ *
12
+ * This is the standard pattern used by Supabase, Firebase, and Clerk
13
+ * for client-side applications.
14
+ */
15
+ export const ATOMS_SUPABASE_URL = "https://gmebjyhomsbvhrxffzre.supabase.co";
16
+ export const ATOMS_SUPABASE_ANON_KEY = "sb_publishable_b49MUAB8XCrQiF7x5b9lUA_w1aplSBH";
17
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG,0CAA0C,CAAC;AAC7E,MAAM,CAAC,MAAM,uBAAuB,GAClC,gDAAgD,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Supabase client factory for the MCP server.
3
+ *
4
+ * Creates a user-scoped client with JWT — RLS enforced automatically.
5
+ * Never uses service role key (all queries respect user permissions).
6
+ */
7
+ import { type SupabaseClient } from "@supabase/supabase-js";
8
+ /**
9
+ * Get or create an authenticated Supabase client.
10
+ * Auto-refreshes JWT when expired. All queries run with user's RLS permissions.
11
+ */
12
+ export declare function getClient(): Promise<SupabaseClient>;
13
+ /**
14
+ * Get the authenticated user's ID. Must call getClient() first.
15
+ */
16
+ export declare function getUserId(): string;
17
+ /**
18
+ * Get the authenticated user's email.
19
+ */
20
+ export declare function getUserEmail(): string;
21
+ /**
22
+ * Reset the cached client (for token refresh).
23
+ */
24
+ export declare function resetClient(): void;
25
+ /**
26
+ * Check if the user has write access to a project.
27
+ * Returns the user's org role, or throws a descriptive error.
28
+ */
29
+ export declare function requireWriteAccess(client: SupabaseClient, projectId: string): Promise<string>;
30
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAS1E;;;GAGG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,cAAc,CAAC,CAuCzD;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAKlC;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAKlC;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAoCjB"}