@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.
- package/bin/atoms-mcp.js +2 -0
- package/dist/auth/login.d.ts +23 -0
- package/dist/auth/login.d.ts.map +1 -0
- package/dist/auth/login.js +246 -0
- package/dist/auth/login.js.map +1 -0
- package/dist/auth/refresh.d.ts +17 -0
- package/dist/auth/refresh.d.ts.map +1 -0
- package/dist/auth/refresh.js +82 -0
- package/dist/auth/refresh.js.map +1 -0
- package/dist/auth/token-store.d.ts +34 -0
- package/dist/auth/token-store.d.ts.map +1 -0
- package/dist/auth/token-store.js +61 -0
- package/dist/auth/token-store.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +17 -0
- package/dist/config.js.map +1 -0
- package/dist/db/client.d.ts +30 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +110 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/graph.d.ts +8 -0
- package/dist/db/graph.d.ts.map +1 -0
- package/dist/db/graph.js +8 -0
- package/dist/db/graph.js.map +1 -0
- package/dist/db/queries.d.ts +77 -0
- package/dist/db/queries.d.ts.map +1 -0
- package/dist/db/queries.js +210 -0
- package/dist/db/queries.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +92 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/audit.d.ts +26 -0
- package/dist/middleware/audit.d.ts.map +1 -0
- package/dist/middleware/audit.js +44 -0
- package/dist/middleware/audit.js.map +1 -0
- package/dist/middleware/rate-limiter.d.ts +21 -0
- package/dist/middleware/rate-limiter.d.ts.map +1 -0
- package/dist/middleware/rate-limiter.js +43 -0
- package/dist/middleware/rate-limiter.js.map +1 -0
- package/dist/middleware/validator.d.ts +22 -0
- package/dist/middleware/validator.d.ts.map +1 -0
- package/dist/middleware/validator.js +91 -0
- package/dist/middleware/validator.js.map +1 -0
- package/dist/server.d.ts +14 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +511 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/_base.d.ts +58 -0
- package/dist/tools/_base.d.ts.map +1 -0
- package/dist/tools/_base.js +109 -0
- package/dist/tools/_base.js.map +1 -0
- package/dist/tools/create-item.d.ts +43 -0
- package/dist/tools/create-item.d.ts.map +1 -0
- package/dist/tools/create-item.js +118 -0
- package/dist/tools/create-item.js.map +1 -0
- package/dist/tools/delete-item.d.ts +38 -0
- package/dist/tools/delete-item.d.ts.map +1 -0
- package/dist/tools/delete-item.js +69 -0
- package/dist/tools/delete-item.js.map +1 -0
- package/dist/tools/export-mermaid.d.ts +36 -0
- package/dist/tools/export-mermaid.d.ts.map +1 -0
- package/dist/tools/export-mermaid.js +125 -0
- package/dist/tools/export-mermaid.js.map +1 -0
- package/dist/tools/get-coverage.d.ts +34 -0
- package/dist/tools/get-coverage.d.ts.map +1 -0
- package/dist/tools/get-coverage.js +36 -0
- package/dist/tools/get-coverage.js.map +1 -0
- package/dist/tools/get-history.d.ts +34 -0
- package/dist/tools/get-history.d.ts.map +1 -0
- package/dist/tools/get-history.js +53 -0
- package/dist/tools/get-history.js.map +1 -0
- package/dist/tools/get-item.d.ts +62 -0
- package/dist/tools/get-item.d.ts.map +1 -0
- package/dist/tools/get-item.js +93 -0
- package/dist/tools/get-item.js.map +1 -0
- package/dist/tools/link-items.d.ts +41 -0
- package/dist/tools/link-items.d.ts.map +1 -0
- package/dist/tools/link-items.js +150 -0
- package/dist/tools/link-items.js.map +1 -0
- package/dist/tools/list-items.d.ts +37 -0
- package/dist/tools/list-items.d.ts.map +1 -0
- package/dist/tools/list-items.js +36 -0
- package/dist/tools/list-items.js.map +1 -0
- package/dist/tools/list-projects.d.ts +38 -0
- package/dist/tools/list-projects.d.ts.map +1 -0
- package/dist/tools/list-projects.js +28 -0
- package/dist/tools/list-projects.js.map +1 -0
- package/dist/tools/record-test-result.d.ts +41 -0
- package/dist/tools/record-test-result.d.ts.map +1 -0
- package/dist/tools/record-test-result.js +80 -0
- package/dist/tools/record-test-result.js.map +1 -0
- package/dist/tools/search.d.ts +34 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +28 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/update-item.d.ts +43 -0
- package/dist/tools/update-item.d.ts.map +1 -0
- package/dist/tools/update-item.js +98 -0
- package/dist/tools/update-item.js.map +1 -0
- package/dist/types/responses.d.ts +58 -0
- package/dist/types/responses.d.ts.map +1 -0
- package/dist/types/responses.js +6 -0
- package/dist/types/responses.js.map +1 -0
- package/dist/types/work-item.d.ts +69 -0
- package/dist/types/work-item.d.ts.map +1 -0
- package/dist/types/work-item.js +8 -0
- package/dist/types/work-item.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,110 @@
|
|
|
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 { createClient } from "@supabase/supabase-js";
|
|
8
|
+
import { getValidToken } from "../auth/refresh.js";
|
|
9
|
+
import { ATOMS_SUPABASE_URL, ATOMS_SUPABASE_ANON_KEY } from "../config.js";
|
|
10
|
+
let cachedClient = null;
|
|
11
|
+
let cachedUserId = null;
|
|
12
|
+
let cachedEmail = null;
|
|
13
|
+
let tokenExpiresAt = 0;
|
|
14
|
+
/**
|
|
15
|
+
* Get or create an authenticated Supabase client.
|
|
16
|
+
* Auto-refreshes JWT when expired. All queries run with user's RLS permissions.
|
|
17
|
+
*/
|
|
18
|
+
export async function getClient() {
|
|
19
|
+
// If token is within 60s of expiry, force refresh
|
|
20
|
+
if (cachedClient && tokenExpiresAt > Date.now() + 60_000) {
|
|
21
|
+
return cachedClient;
|
|
22
|
+
}
|
|
23
|
+
// Token expired or no client — get a fresh token
|
|
24
|
+
if (cachedClient) {
|
|
25
|
+
process.stderr.write("[atoms-mcp] Token expiring, refreshing client...\n");
|
|
26
|
+
}
|
|
27
|
+
const { access_token, user_id, email } = await getValidToken();
|
|
28
|
+
// Decode expiry from JWT
|
|
29
|
+
const parts = access_token.split(".");
|
|
30
|
+
if (parts.length === 3) {
|
|
31
|
+
try {
|
|
32
|
+
const payload = JSON.parse(Buffer.from(parts[1].replace(/-/g, "+").replace(/_/g, "/"), "base64").toString());
|
|
33
|
+
tokenExpiresAt = (payload.exp ?? 0) * 1000; // convert to ms
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Fallback: assume 1hr from now
|
|
37
|
+
tokenExpiresAt = Date.now() + 3600_000;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Use global Authorization header — works with all Supabase key formats
|
|
41
|
+
const client = createClient(ATOMS_SUPABASE_URL, ATOMS_SUPABASE_ANON_KEY, {
|
|
42
|
+
global: {
|
|
43
|
+
headers: { Authorization: `Bearer ${access_token}` },
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
cachedClient = client;
|
|
47
|
+
cachedUserId = user_id;
|
|
48
|
+
cachedEmail = email;
|
|
49
|
+
return cachedClient;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get the authenticated user's ID. Must call getClient() first.
|
|
53
|
+
*/
|
|
54
|
+
export function getUserId() {
|
|
55
|
+
if (!cachedUserId) {
|
|
56
|
+
throw new Error("Not authenticated. Call getClient() first.");
|
|
57
|
+
}
|
|
58
|
+
return cachedUserId;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get the authenticated user's email.
|
|
62
|
+
*/
|
|
63
|
+
export function getUserEmail() {
|
|
64
|
+
return cachedEmail ?? "";
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Reset the cached client (for token refresh).
|
|
68
|
+
*/
|
|
69
|
+
export function resetClient() {
|
|
70
|
+
cachedClient = null;
|
|
71
|
+
cachedUserId = null;
|
|
72
|
+
cachedEmail = null;
|
|
73
|
+
tokenExpiresAt = 0;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Check if the user has write access to a project.
|
|
77
|
+
* Returns the user's org role, or throws a descriptive error.
|
|
78
|
+
*/
|
|
79
|
+
export async function requireWriteAccess(client, projectId) {
|
|
80
|
+
const userId = getUserId();
|
|
81
|
+
// Get the project's org
|
|
82
|
+
const { data: project, error: projErr } = await client
|
|
83
|
+
.from("projects")
|
|
84
|
+
.select("org_id")
|
|
85
|
+
.eq("id", projectId)
|
|
86
|
+
.maybeSingle();
|
|
87
|
+
if (projErr || !project) {
|
|
88
|
+
throw new Error(`Project '${projectId}' not found`);
|
|
89
|
+
}
|
|
90
|
+
// Get user's role in that org
|
|
91
|
+
const { data: membership, error: memErr } = await client
|
|
92
|
+
.from("org_members")
|
|
93
|
+
.select("role")
|
|
94
|
+
.eq("user_id", userId)
|
|
95
|
+
.eq("org_id", project.org_id)
|
|
96
|
+
.maybeSingle();
|
|
97
|
+
if (memErr || !membership) {
|
|
98
|
+
// Check if user is a platform admin (can access all orgs)
|
|
99
|
+
const { data: adminCheck } = await client.rpc("is_platform_admin");
|
|
100
|
+
if (adminCheck === true) {
|
|
101
|
+
return "admin";
|
|
102
|
+
}
|
|
103
|
+
throw new Error("Not a member of this project's organization");
|
|
104
|
+
}
|
|
105
|
+
if (membership.role === "viewer") {
|
|
106
|
+
throw new Error("VIEWER_ROLE");
|
|
107
|
+
}
|
|
108
|
+
return membership.role;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/db/graph.ts"],"names":[],"mappings":";AAAA;;;;;GAKG"}
|
package/dist/db/graph.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/db/graph.ts"],"names":[],"mappings":";AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Supabase query helpers for MCP tools.
|
|
3
|
+
*
|
|
4
|
+
* All queries use the user's JWT via RLS — no service role key.
|
|
5
|
+
* Query patterns adapted from supabase/functions/.../db.ts.
|
|
6
|
+
*/
|
|
7
|
+
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
8
|
+
import type { ItemRow } from "../types/work-item.js";
|
|
9
|
+
/**
|
|
10
|
+
* List projects the authenticated user has access to (via org membership).
|
|
11
|
+
*/
|
|
12
|
+
export declare function listProjects(client: SupabaseClient): Promise<Array<{
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
description: string | null;
|
|
16
|
+
org_id: string;
|
|
17
|
+
org_name: string;
|
|
18
|
+
created_at: string;
|
|
19
|
+
}>>;
|
|
20
|
+
/**
|
|
21
|
+
* List items in a project with optional filters.
|
|
22
|
+
* Returns rows ordered by created_at, respects soft deletes.
|
|
23
|
+
*/
|
|
24
|
+
export declare function listItems(client: SupabaseClient, projectId: string, opts: {
|
|
25
|
+
type?: string;
|
|
26
|
+
domain?: string;
|
|
27
|
+
level?: string;
|
|
28
|
+
limit: number;
|
|
29
|
+
offset: number;
|
|
30
|
+
}): Promise<{
|
|
31
|
+
items: ItemRow[];
|
|
32
|
+
totalCount: number;
|
|
33
|
+
}>;
|
|
34
|
+
/**
|
|
35
|
+
* Get a single item by ID (excludes soft-deleted).
|
|
36
|
+
*/
|
|
37
|
+
export declare function getItemById(client: SupabaseClient, projectId: string, itemId: string): Promise<ItemRow | null>;
|
|
38
|
+
/**
|
|
39
|
+
* Full-text search across items in a project.
|
|
40
|
+
* Uses ilike on title, body, and summary for broad matching.
|
|
41
|
+
*/
|
|
42
|
+
export declare function searchItems(client: SupabaseClient, projectId: string, query: string, opts: {
|
|
43
|
+
type?: string;
|
|
44
|
+
limit: number;
|
|
45
|
+
}): Promise<{
|
|
46
|
+
items: ItemRow[];
|
|
47
|
+
totalCount: number;
|
|
48
|
+
}>;
|
|
49
|
+
/**
|
|
50
|
+
* Get all relationships for an item (both directions).
|
|
51
|
+
*/
|
|
52
|
+
export declare function getItemRelationships(client: SupabaseClient, projectId: string, itemId: string): Promise<Array<{
|
|
53
|
+
from_id: string;
|
|
54
|
+
to_id: string;
|
|
55
|
+
type: string;
|
|
56
|
+
}>>;
|
|
57
|
+
/**
|
|
58
|
+
* Get test coverage report: requirements with/without linked test cases.
|
|
59
|
+
*/
|
|
60
|
+
export declare function getCoverageReport(client: SupabaseClient, projectId: string, opts: {
|
|
61
|
+
domain?: string;
|
|
62
|
+
level?: string;
|
|
63
|
+
}): Promise<{
|
|
64
|
+
covered: ItemRow[];
|
|
65
|
+
uncovered: ItemRow[];
|
|
66
|
+
total: number;
|
|
67
|
+
}>;
|
|
68
|
+
/**
|
|
69
|
+
* Get change history for an item, newest first.
|
|
70
|
+
*/
|
|
71
|
+
export declare function getChangeHistory(client: SupabaseClient, projectId: string, itemId: string, limit: number): Promise<Array<Record<string, unknown>>>;
|
|
72
|
+
/**
|
|
73
|
+
* Generate the next item ID for a given type in a project.
|
|
74
|
+
* Format: PREFIX-NNNNN (e.g., REQ-00058)
|
|
75
|
+
*/
|
|
76
|
+
export declare function generateItemId(client: SupabaseClient, projectId: string, type: string): Promise<string>;
|
|
77
|
+
//# sourceMappingURL=queries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../src/db/queries.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAMrD;;GAEG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAiBhI;AAMD;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE;IACJ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,GACA,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAkCnD;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAWzB;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACrC,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CA2BnD;AAMD;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CASlE;AAMD;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACxC,OAAO,CAAC;IACT,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,SAAS,EAAE,OAAO,EAAE,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAwCD;AAMD;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAWzC;AAMD;;;GAGG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,CAejB"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Supabase query helpers for MCP tools.
|
|
3
|
+
*
|
|
4
|
+
* All queries use the user's JWT via RLS — no service role key.
|
|
5
|
+
* Query patterns adapted from supabase/functions/.../db.ts.
|
|
6
|
+
*/
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Project queries
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
/**
|
|
11
|
+
* List projects the authenticated user has access to (via org membership).
|
|
12
|
+
*/
|
|
13
|
+
export async function listProjects(client) {
|
|
14
|
+
const { data, error } = await client
|
|
15
|
+
.from("projects")
|
|
16
|
+
.select("id, name, description, org_id, created_at, organizations(name)")
|
|
17
|
+
.is("deleted_at", null)
|
|
18
|
+
.order("created_at", { ascending: false });
|
|
19
|
+
if (error)
|
|
20
|
+
throw new Error(error.message);
|
|
21
|
+
return (data ?? []).map((p) => ({
|
|
22
|
+
id: p.id,
|
|
23
|
+
name: p.name,
|
|
24
|
+
description: p.description,
|
|
25
|
+
org_id: p.org_id,
|
|
26
|
+
org_name: p.organizations?.name ?? "Unknown",
|
|
27
|
+
created_at: p.created_at,
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Item queries
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
/**
|
|
34
|
+
* List items in a project with optional filters.
|
|
35
|
+
* Returns rows ordered by created_at, respects soft deletes.
|
|
36
|
+
*/
|
|
37
|
+
export async function listItems(client, projectId, opts) {
|
|
38
|
+
let query = client
|
|
39
|
+
.from("items")
|
|
40
|
+
.select("*", { count: "exact" })
|
|
41
|
+
.eq("project_id", projectId)
|
|
42
|
+
.is("deleted_at", null)
|
|
43
|
+
.order("created_at", { ascending: true })
|
|
44
|
+
.range(opts.offset, opts.offset + opts.limit - 1);
|
|
45
|
+
if (opts.type) {
|
|
46
|
+
query = query.eq("type", opts.type);
|
|
47
|
+
}
|
|
48
|
+
const { data, error, count } = await query;
|
|
49
|
+
if (error)
|
|
50
|
+
throw new Error(error.message);
|
|
51
|
+
// Apply domain/level filters in JS (JSONB nested field filtering)
|
|
52
|
+
let filtered = (data ?? []);
|
|
53
|
+
if (opts.domain) {
|
|
54
|
+
filtered = filtered.filter((item) => {
|
|
55
|
+
const domains = item.data?.tags?.domains ?? [];
|
|
56
|
+
return domains.includes(opts.domain);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (opts.level) {
|
|
60
|
+
filtered = filtered.filter((item) => {
|
|
61
|
+
return item.data?.tags?.level === opts.level;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
items: filtered,
|
|
66
|
+
totalCount: opts.domain || opts.level ? filtered.length : (count ?? 0),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get a single item by ID (excludes soft-deleted).
|
|
71
|
+
*/
|
|
72
|
+
export async function getItemById(client, projectId, itemId) {
|
|
73
|
+
const { data, error } = await client
|
|
74
|
+
.from("items")
|
|
75
|
+
.select("*")
|
|
76
|
+
.eq("id", itemId)
|
|
77
|
+
.eq("project_id", projectId)
|
|
78
|
+
.is("deleted_at", null)
|
|
79
|
+
.maybeSingle();
|
|
80
|
+
if (error)
|
|
81
|
+
throw new Error(error.message);
|
|
82
|
+
return data;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Full-text search across items in a project.
|
|
86
|
+
* Uses ilike on title, body, and summary for broad matching.
|
|
87
|
+
*/
|
|
88
|
+
export async function searchItems(client, projectId, query, opts) {
|
|
89
|
+
const searchTerm = query.trim();
|
|
90
|
+
if (!searchTerm) {
|
|
91
|
+
return { items: [], totalCount: 0 };
|
|
92
|
+
}
|
|
93
|
+
// Use ilike for broad matching across title and JSONB fields
|
|
94
|
+
let queryBuilder = client
|
|
95
|
+
.from("items")
|
|
96
|
+
.select("*", { count: "exact" })
|
|
97
|
+
.eq("project_id", projectId)
|
|
98
|
+
.is("deleted_at", null)
|
|
99
|
+
.or(`title.ilike.%${searchTerm}%,` +
|
|
100
|
+
`data->>body.ilike.%${searchTerm}%,` +
|
|
101
|
+
`data->>summary.ilike.%${searchTerm}%`)
|
|
102
|
+
.limit(opts.limit);
|
|
103
|
+
if (opts.type) {
|
|
104
|
+
queryBuilder = queryBuilder.eq("type", opts.type);
|
|
105
|
+
}
|
|
106
|
+
const { data, error, count } = await queryBuilder;
|
|
107
|
+
if (error)
|
|
108
|
+
throw new Error(error.message);
|
|
109
|
+
return { items: (data ?? []), totalCount: count ?? 0 };
|
|
110
|
+
}
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Relationship queries
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
/**
|
|
115
|
+
* Get all relationships for an item (both directions).
|
|
116
|
+
*/
|
|
117
|
+
export async function getItemRelationships(client, projectId, itemId) {
|
|
118
|
+
const { data, error } = await client
|
|
119
|
+
.from("item_relationships")
|
|
120
|
+
.select("from_id, to_id, type")
|
|
121
|
+
.eq("project_id", projectId)
|
|
122
|
+
.or(`from_id.eq.${itemId},to_id.eq.${itemId}`);
|
|
123
|
+
if (error)
|
|
124
|
+
throw new Error(error.message);
|
|
125
|
+
return data ?? [];
|
|
126
|
+
}
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
// Coverage queries
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
/**
|
|
131
|
+
* Get test coverage report: requirements with/without linked test cases.
|
|
132
|
+
*/
|
|
133
|
+
export async function getCoverageReport(client, projectId, opts) {
|
|
134
|
+
// Get all requirements in the project
|
|
135
|
+
const { data: requirements, error: reqErr } = await client
|
|
136
|
+
.from("items")
|
|
137
|
+
.select("*")
|
|
138
|
+
.eq("project_id", projectId)
|
|
139
|
+
.eq("type", "requirement")
|
|
140
|
+
.is("deleted_at", null);
|
|
141
|
+
if (reqErr)
|
|
142
|
+
throw new Error(reqErr.message);
|
|
143
|
+
const allReqs = (requirements ?? []);
|
|
144
|
+
// Filter by domain/level on the JSONB data
|
|
145
|
+
const filteredReqs = allReqs.filter((req) => {
|
|
146
|
+
if (opts.domain) {
|
|
147
|
+
const domains = req.data?.tags?.domains ?? [];
|
|
148
|
+
if (!domains.includes(opts.domain))
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
if (opts.level) {
|
|
152
|
+
if (req.data?.tags?.level !== opts.level)
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
return true;
|
|
156
|
+
});
|
|
157
|
+
// Get all verified_by relationships for this project
|
|
158
|
+
const { data: verifiedRels, error: relErr } = await client
|
|
159
|
+
.from("item_relationships")
|
|
160
|
+
.select("from_id")
|
|
161
|
+
.eq("project_id", projectId)
|
|
162
|
+
.eq("type", "verified_by");
|
|
163
|
+
if (relErr)
|
|
164
|
+
throw new Error(relErr.message);
|
|
165
|
+
const coveredIds = new Set((verifiedRels ?? []).map((r) => r.from_id));
|
|
166
|
+
const covered = filteredReqs.filter((r) => coveredIds.has(r.id));
|
|
167
|
+
const uncovered = filteredReqs.filter((r) => !coveredIds.has(r.id));
|
|
168
|
+
return { covered, uncovered, total: filteredReqs.length };
|
|
169
|
+
}
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
// History queries
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
/**
|
|
174
|
+
* Get change history for an item, newest first.
|
|
175
|
+
*/
|
|
176
|
+
export async function getChangeHistory(client, projectId, itemId, limit) {
|
|
177
|
+
const { data, error } = await client
|
|
178
|
+
.from("change_history")
|
|
179
|
+
.select("*")
|
|
180
|
+
.eq("item_id", itemId)
|
|
181
|
+
.eq("project_id", projectId)
|
|
182
|
+
.order("changed_at", { ascending: false })
|
|
183
|
+
.limit(limit);
|
|
184
|
+
if (error)
|
|
185
|
+
throw new Error(error.message);
|
|
186
|
+
return data ?? [];
|
|
187
|
+
}
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
// Item mutations (for write tools — Phase 3)
|
|
190
|
+
// ---------------------------------------------------------------------------
|
|
191
|
+
/**
|
|
192
|
+
* Generate the next item ID for a given type in a project.
|
|
193
|
+
* Format: PREFIX-NNNNN (e.g., REQ-00058)
|
|
194
|
+
*/
|
|
195
|
+
export async function generateItemId(client, projectId, type) {
|
|
196
|
+
const prefix = type === "requirement" ? "REQ"
|
|
197
|
+
: type === "test-case" ? "TC"
|
|
198
|
+
: type === "note" ? "NOTE"
|
|
199
|
+
: "TABLE";
|
|
200
|
+
// Use Postgres SECURITY DEFINER function that sees ALL items (including soft-deleted)
|
|
201
|
+
// to avoid ID collisions. Only returns the next ID string, never exposes item data.
|
|
202
|
+
const { data, error } = await client.rpc("next_item_id", {
|
|
203
|
+
p_project_id: projectId,
|
|
204
|
+
p_prefix: prefix,
|
|
205
|
+
});
|
|
206
|
+
if (error)
|
|
207
|
+
throw new Error(`ID generation failed: ${error.message}`);
|
|
208
|
+
return data;
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=queries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../../src/db/queries.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ATOMS MCP Server — CLI Entry Point
|
|
4
|
+
*
|
|
5
|
+
* Commands:
|
|
6
|
+
* npx @atoms-tech/atoms-mcp → Start MCP server (stdio transport)
|
|
7
|
+
* npx @atoms-tech/atoms-mcp login → Authenticate with ATOMS.tech
|
|
8
|
+
* npx @atoms-tech/atoms-mcp whoami → Show current user
|
|
9
|
+
* npx @atoms-tech/atoms-mcp --version → Show version
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ATOMS MCP Server — CLI Entry Point
|
|
4
|
+
*
|
|
5
|
+
* Commands:
|
|
6
|
+
* npx @atoms-tech/atoms-mcp → Start MCP server (stdio transport)
|
|
7
|
+
* npx @atoms-tech/atoms-mcp login → Authenticate with ATOMS.tech
|
|
8
|
+
* npx @atoms-tech/atoms-mcp whoami → Show current user
|
|
9
|
+
* npx @atoms-tech/atoms-mcp --version → Show version
|
|
10
|
+
*/
|
|
11
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
|
+
import { server } from "./server.js";
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
const command = args[0];
|
|
15
|
+
async function main() {
|
|
16
|
+
switch (command) {
|
|
17
|
+
case "login": {
|
|
18
|
+
const { login } = await import("./auth/login.js");
|
|
19
|
+
try {
|
|
20
|
+
const { email } = await login();
|
|
21
|
+
process.stderr.write(`\nLogged in as ${email}\n`);
|
|
22
|
+
process.stderr.write(`\nAdd this to your Claude Desktop config:\n` +
|
|
23
|
+
`{\n "mcpServers": {\n "atoms": {\n` +
|
|
24
|
+
` "command": "npx",\n` +
|
|
25
|
+
` "args": ["@atoms-tech/atoms-mcp@latest"]\n` +
|
|
26
|
+
` }\n }\n}\n`);
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
process.stderr.write(`Login failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
case "whoami": {
|
|
35
|
+
const { getValidToken } = await import("./auth/refresh.js");
|
|
36
|
+
try {
|
|
37
|
+
const { email, user_id } = await getValidToken();
|
|
38
|
+
process.stderr.write(`User: ${email}\nID: ${user_id}\n`);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
process.stderr.write(`Not authenticated: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
case "logout": {
|
|
47
|
+
const { clearCredentials } = await import("./auth/token-store.js");
|
|
48
|
+
await clearCredentials();
|
|
49
|
+
process.stderr.write("Logged out. Credentials removed.\n");
|
|
50
|
+
process.stderr.write("Run 'npx @atoms-tech/atoms-mcp login' to authenticate again.\n");
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
case "--version":
|
|
54
|
+
case "-v": {
|
|
55
|
+
process.stderr.write("@atoms-tech/atoms-mcp v0.1.0\n");
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case "--help":
|
|
59
|
+
case "-h": {
|
|
60
|
+
process.stderr.write(`ATOMS MCP Server — AI agent integration for requirements management\n\n` +
|
|
61
|
+
`Usage:\n` +
|
|
62
|
+
` npx @atoms-tech/atoms-mcp Start MCP server (stdio)\n` +
|
|
63
|
+
` npx @atoms-tech/atoms-mcp login Authenticate with ATOMS.tech\n` +
|
|
64
|
+
` npx @atoms-tech/atoms-mcp logout Switch account / clear credentials\n` +
|
|
65
|
+
` npx @atoms-tech/atoms-mcp whoami Show current user\n` +
|
|
66
|
+
` npx @atoms-tech/atoms-mcp --version Show version\n`);
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
default: {
|
|
70
|
+
// Default: start MCP server via stdio
|
|
71
|
+
process.stderr.write("[atoms-mcp] Starting MCP server via stdio...\n");
|
|
72
|
+
try {
|
|
73
|
+
// Validate auth on startup
|
|
74
|
+
const { getValidToken } = await import("./auth/refresh.js");
|
|
75
|
+
const { email } = await getValidToken();
|
|
76
|
+
process.stderr.write(`[atoms-mcp] Authenticated as ${email}\n`);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
process.stderr.write(`[atoms-mcp] Warning: ${err instanceof Error ? err.message : String(err)}\n` +
|
|
80
|
+
`[atoms-mcp] Some tools may fail without authentication.\n`);
|
|
81
|
+
}
|
|
82
|
+
const transport = new StdioServerTransport();
|
|
83
|
+
await server.connect(transport);
|
|
84
|
+
process.stderr.write("[atoms-mcp] MCP server running. Waiting for tool calls...\n");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
main().catch((err) => {
|
|
89
|
+
process.stderr.write(`[atoms-mcp] Fatal: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fire-and-forget MCP audit logging.
|
|
3
|
+
*
|
|
4
|
+
* Every tool call is logged to mcp_audit_log for enterprise compliance.
|
|
5
|
+
* Logging NEVER blocks or delays the tool response.
|
|
6
|
+
*/
|
|
7
|
+
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
8
|
+
export interface AuditEntry {
|
|
9
|
+
tool_name: string;
|
|
10
|
+
params: Record<string, unknown>;
|
|
11
|
+
status: "success" | "error";
|
|
12
|
+
duration_ms: number;
|
|
13
|
+
error_msg?: string;
|
|
14
|
+
project_id?: string;
|
|
15
|
+
session_id?: string;
|
|
16
|
+
client_name?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Log a tool call to mcp_audit_log. Fire-and-forget — errors are swallowed.
|
|
20
|
+
*/
|
|
21
|
+
export declare function logAudit(client: SupabaseClient, userId: string, entry: AuditEntry): void;
|
|
22
|
+
/**
|
|
23
|
+
* Timer utility for measuring tool duration.
|
|
24
|
+
*/
|
|
25
|
+
export declare function startTimer(): () => number;
|
|
26
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/middleware/audit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,UAAU,GAChB,IAAI,CA6BN;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,MAAM,CAGzC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fire-and-forget MCP audit logging.
|
|
3
|
+
*
|
|
4
|
+
* Every tool call is logged to mcp_audit_log for enterprise compliance.
|
|
5
|
+
* Logging NEVER blocks or delays the tool response.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Log a tool call to mcp_audit_log. Fire-and-forget — errors are swallowed.
|
|
9
|
+
*/
|
|
10
|
+
export function logAudit(client, userId, entry) {
|
|
11
|
+
// Sanitize params — strip any secrets
|
|
12
|
+
const sanitizedParams = { ...entry.params };
|
|
13
|
+
delete sanitizedParams.access_token;
|
|
14
|
+
delete sanitizedParams.refresh_token;
|
|
15
|
+
delete sanitizedParams.password;
|
|
16
|
+
// Fire and forget — don't await
|
|
17
|
+
client
|
|
18
|
+
.from("mcp_audit_log")
|
|
19
|
+
.insert({
|
|
20
|
+
user_id: userId,
|
|
21
|
+
tool_name: entry.tool_name,
|
|
22
|
+
params: sanitizedParams,
|
|
23
|
+
status: entry.status,
|
|
24
|
+
duration_ms: entry.duration_ms,
|
|
25
|
+
error_msg: entry.error_msg ?? null,
|
|
26
|
+
project_id: entry.project_id ?? null,
|
|
27
|
+
session_id: entry.session_id ?? null,
|
|
28
|
+
client_name: entry.client_name ?? null,
|
|
29
|
+
})
|
|
30
|
+
.then(({ error }) => {
|
|
31
|
+
if (error) {
|
|
32
|
+
// Log to stderr, never throw
|
|
33
|
+
process.stderr.write(`[audit] Failed to log: ${error.message}\n`);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Timer utility for measuring tool duration.
|
|
39
|
+
*/
|
|
40
|
+
export function startTimer() {
|
|
41
|
+
const start = performance.now();
|
|
42
|
+
return () => Math.round(performance.now() - start);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/middleware/audit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH;;GAEG;AACH,MAAM,UAAU,QAAQ,CACtB,MAAsB,EACtB,MAAc,EACd,KAAiB;IAEjB,sCAAsC;IACtC,MAAM,eAAe,GAAG,EAAE,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;IAC5C,OAAO,eAAe,CAAC,YAAY,CAAC;IACpC,OAAO,eAAe,CAAC,aAAa,CAAC;IACrC,OAAO,eAAe,CAAC,QAAQ,CAAC;IAEhC,gCAAgC;IAChC,MAAM;SACH,IAAI,CAAC,eAAe,CAAC;SACrB,MAAM,CAAC;QACN,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,MAAM,EAAE,eAAe;QACvB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI;QAClC,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI;QACpC,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI;QACpC,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,IAAI;KACvC,CAAC;SACD,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE;QAClB,IAAI,KAAK,EAAE,CAAC;YACV,6BAA6B;YAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,0BAA0B,KAAK,CAAC,OAAO,IAAI,CAC5C,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAChC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;AACrD,CAAC"}
|