@atoms-tech/atoms-mcp 0.3.0 → 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.
Files changed (65) hide show
  1. package/README.md +1 -1
  2. package/dist/index.cjs +2 -0
  3. package/dist/index.js +1 -90
  4. package/package.json +12 -3
  5. package/dist/apps/register.d.ts +0 -36
  6. package/dist/apps/register.js +0 -65
  7. package/dist/auth/login.d.ts +0 -24
  8. package/dist/auth/login.js +0 -413
  9. package/dist/auth/refresh.d.ts +0 -16
  10. package/dist/auth/refresh.js +0 -81
  11. package/dist/auth/token-store.d.ts +0 -33
  12. package/dist/auth/token-store.js +0 -60
  13. package/dist/config.d.ts +0 -18
  14. package/dist/config.js +0 -18
  15. package/dist/db/client.d.ts +0 -29
  16. package/dist/db/client.js +0 -109
  17. package/dist/db/graph.d.ts +0 -7
  18. package/dist/db/graph.js +0 -7
  19. package/dist/db/queries.d.ts +0 -76
  20. package/dist/db/queries.js +0 -209
  21. package/dist/index.d.ts +0 -11
  22. package/dist/middleware/audit.d.ts +0 -25
  23. package/dist/middleware/audit.js +0 -43
  24. package/dist/middleware/rate-limiter.d.ts +0 -20
  25. package/dist/middleware/rate-limiter.js +0 -42
  26. package/dist/middleware/validator.d.ts +0 -21
  27. package/dist/middleware/validator.js +0 -90
  28. package/dist/server.d.ts +0 -14
  29. package/dist/server.js +0 -679
  30. package/dist/tools/_base.d.ts +0 -57
  31. package/dist/tools/_base.js +0 -108
  32. package/dist/tools/bulk-import.d.ts +0 -69
  33. package/dist/tools/bulk-import.js +0 -187
  34. package/dist/tools/create-item.d.ts +0 -42
  35. package/dist/tools/create-item.js +0 -117
  36. package/dist/tools/delete-item.d.ts +0 -37
  37. package/dist/tools/delete-item.js +0 -68
  38. package/dist/tools/export-mermaid.d.ts +0 -35
  39. package/dist/tools/export-mermaid.js +0 -124
  40. package/dist/tools/get-coverage.d.ts +0 -33
  41. package/dist/tools/get-coverage.js +0 -35
  42. package/dist/tools/get-history.d.ts +0 -33
  43. package/dist/tools/get-history.js +0 -52
  44. package/dist/tools/get-item.d.ts +0 -61
  45. package/dist/tools/get-item.js +0 -92
  46. package/dist/tools/link-items.d.ts +0 -40
  47. package/dist/tools/link-items.js +0 -149
  48. package/dist/tools/list-items.d.ts +0 -36
  49. package/dist/tools/list-items.js +0 -35
  50. package/dist/tools/list-projects.d.ts +0 -37
  51. package/dist/tools/list-projects.js +0 -27
  52. package/dist/tools/project-summary.d.ts +0 -63
  53. package/dist/tools/project-summary.js +0 -169
  54. package/dist/tools/record-test-result.d.ts +0 -40
  55. package/dist/tools/record-test-result.js +0 -79
  56. package/dist/tools/search.d.ts +0 -33
  57. package/dist/tools/search.js +0 -27
  58. package/dist/tools/trace.d.ts +0 -52
  59. package/dist/tools/trace.js +0 -165
  60. package/dist/tools/update-item.d.ts +0 -42
  61. package/dist/tools/update-item.js +0 -97
  62. package/dist/types/responses.d.ts +0 -57
  63. package/dist/types/responses.js +0 -5
  64. package/dist/types/work-item.d.ts +0 -68
  65. package/dist/types/work-item.js +0 -7
@@ -1,36 +0,0 @@
1
- /**
2
- * atoms_list_items — List items in a project with optional filters.
3
- * ENTRY POINT tool for discovering project contents.
4
- */
5
- import type { ItemSummary } from "../types/responses.js";
6
- export declare function listItemsHandler(params: {
7
- project_id: string;
8
- type?: string;
9
- domain?: string;
10
- level?: string;
11
- limit: number;
12
- offset: number;
13
- }): Promise<{
14
- content: {
15
- type: "text";
16
- text: string;
17
- }[];
18
- structuredContent: {
19
- data: any[];
20
- meta: {
21
- truncated: boolean;
22
- truncation_message: string;
23
- total_count?: number | undefined;
24
- limit?: number | undefined;
25
- offset?: number | undefined;
26
- has_more?: boolean | undefined;
27
- };
28
- status: "success";
29
- };
30
- } | {
31
- content: {
32
- type: "text";
33
- text: string;
34
- }[];
35
- structuredContent: import("../types/responses.js").ToolError | import("../types/responses.js").ToolSuccess<ItemSummary[]>;
36
- }>;
@@ -1,35 +0,0 @@
1
- /**
2
- * atoms_list_items — List items in a project with optional filters.
3
- * ENTRY POINT tool for discovering project contents.
4
- */
5
- import { getClient } from "../db/client.js";
6
- import { listItems } from "../db/queries.js";
7
- import { formatToolResult, formatErrorResult, success, paginationMeta, dbError, authError, } from "./_base.js";
8
- export async function listItemsHandler(params) {
9
- try {
10
- const client = await getClient();
11
- const { items, totalCount } = await listItems(client, params.project_id, {
12
- type: params.type,
13
- domain: params.domain,
14
- level: params.level,
15
- limit: params.limit,
16
- offset: params.offset,
17
- });
18
- // Map to @basic fields
19
- const data = items.map((item) => ({
20
- id: item.id,
21
- title: item.title,
22
- type: item.type,
23
- status: item.data?.status,
24
- domains: item.data?.tags?.domains ?? [],
25
- level: item.data?.tags?.level,
26
- }));
27
- return formatToolResult(success(data, paginationMeta(totalCount, params.limit, params.offset)));
28
- }
29
- catch (err) {
30
- if (err instanceof Error && err.message.includes("Not authenticated")) {
31
- return formatErrorResult(authError());
32
- }
33
- return formatErrorResult(dbError(err instanceof Error ? err.message : String(err)));
34
- }
35
- }
@@ -1,37 +0,0 @@
1
- /**
2
- * atoms_list_projects — Discover accessible projects.
3
- *
4
- * ENTRY POINT tool. Must be called first to get project_id for all other tools.
5
- * RLS ensures only projects the user has org membership for are returned.
6
- */
7
- export declare function listProjectsHandler(): Promise<{
8
- content: {
9
- type: "text";
10
- text: string;
11
- }[];
12
- structuredContent: {
13
- data: any[];
14
- meta: {
15
- truncated: boolean;
16
- truncation_message: string;
17
- total_count?: number | undefined;
18
- limit?: number | undefined;
19
- offset?: number | undefined;
20
- has_more?: boolean | undefined;
21
- };
22
- status: "success";
23
- };
24
- } | {
25
- content: {
26
- type: "text";
27
- text: string;
28
- }[];
29
- structuredContent: import("../types/responses.js").ToolError | import("../types/responses.js").ToolSuccess<{
30
- id: string;
31
- name: string;
32
- description: string | null;
33
- org_id: string;
34
- org_name: string;
35
- created_at: string;
36
- }[]>;
37
- }>;
@@ -1,27 +0,0 @@
1
- /**
2
- * atoms_list_projects — Discover accessible projects.
3
- *
4
- * ENTRY POINT tool. Must be called first to get project_id for all other tools.
5
- * RLS ensures only projects the user has org membership for are returned.
6
- */
7
- import { getClient } from "../db/client.js";
8
- import { listProjects } from "../db/queries.js";
9
- import { success, formatToolResult, formatErrorResult, dbError } from "./_base.js";
10
- export async function listProjectsHandler() {
11
- try {
12
- const client = await getClient();
13
- const projects = await listProjects(client);
14
- const data = projects.map((p) => ({
15
- id: p.id,
16
- name: p.name,
17
- description: p.description,
18
- org_id: p.org_id,
19
- org_name: p.org_name,
20
- created_at: p.created_at,
21
- }));
22
- return formatToolResult(success(data));
23
- }
24
- catch (err) {
25
- return formatErrorResult(dbError(err instanceof Error ? err.message : "Unknown error"));
26
- }
27
- }
@@ -1,63 +0,0 @@
1
- /**
2
- * atoms_project_summary — One-call compliance/health dashboard for a project.
3
- *
4
- * Returns item counts by type, test status breakdown, coverage stats
5
- * (overall + per domain), and recent change activity.
6
- */
7
- interface DomainCoverage {
8
- domain: string;
9
- covered: number;
10
- total: number;
11
- percent: number;
12
- }
13
- interface ProjectSummaryData {
14
- project_id: string;
15
- project_name: string;
16
- counts: {
17
- requirements: number;
18
- test_cases: number;
19
- notes: number;
20
- };
21
- test_status: {
22
- passed: number;
23
- failed: number;
24
- blocked: number;
25
- not_run: number;
26
- };
27
- coverage: {
28
- covered: number;
29
- uncovered: number;
30
- total: number;
31
- percent: number;
32
- };
33
- coverage_by_domain: DomainCoverage[];
34
- recent_changes: number;
35
- last_updated: string;
36
- }
37
- export declare function projectSummaryHandler(params: {
38
- project_id: string;
39
- }): Promise<{
40
- content: {
41
- type: "text";
42
- text: string;
43
- }[];
44
- structuredContent: {
45
- data: any[];
46
- meta: {
47
- truncated: boolean;
48
- truncation_message: string;
49
- total_count?: number | undefined;
50
- limit?: number | undefined;
51
- offset?: number | undefined;
52
- has_more?: boolean | undefined;
53
- };
54
- status: "success";
55
- };
56
- } | {
57
- content: {
58
- type: "text";
59
- text: string;
60
- }[];
61
- structuredContent: import("../types/responses.js").ToolError | import("../types/responses.js").ToolSuccess<ProjectSummaryData>;
62
- }>;
63
- export {};
@@ -1,169 +0,0 @@
1
- /**
2
- * atoms_project_summary — One-call compliance/health dashboard for a project.
3
- *
4
- * Returns item counts by type, test status breakdown, coverage stats
5
- * (overall + per domain), and recent change activity.
6
- */
7
- import { getClient } from "../db/client.js";
8
- import { formatToolResult, formatErrorResult, success, dbError, authError, notFoundError, } from "./_base.js";
9
- export async function projectSummaryHandler(params) {
10
- try {
11
- const client = await getClient();
12
- // 1. Verify project exists and get its name
13
- const { data: project, error: projErr } = await client
14
- .from("projects")
15
- .select("id, name")
16
- .eq("id", params.project_id)
17
- .is("deleted_at", null)
18
- .maybeSingle();
19
- if (projErr)
20
- throw new Error(projErr.message);
21
- if (!project) {
22
- return formatErrorResult(notFoundError("Project", params.project_id));
23
- }
24
- // 2. Get all items grouped by type
25
- const { data: items, error: itemsErr } = await client
26
- .from("items")
27
- .select("id, type, data")
28
- .eq("project_id", params.project_id)
29
- .is("deleted_at", null);
30
- if (itemsErr)
31
- throw new Error(itemsErr.message);
32
- const allItems = items ?? [];
33
- const counts = {
34
- requirements: 0,
35
- test_cases: 0,
36
- notes: 0,
37
- };
38
- const testCaseIds = [];
39
- const requirementItems = [];
40
- for (const item of allItems) {
41
- if (item.type === "requirement") {
42
- counts.requirements++;
43
- const domains = item.data?.tags?.domains ?? [];
44
- requirementItems.push({ id: item.id, domains });
45
- }
46
- else if (item.type === "test-case") {
47
- counts.test_cases++;
48
- testCaseIds.push(item.id);
49
- }
50
- else if (item.type === "note") {
51
- counts.notes++;
52
- }
53
- }
54
- // 3. Get latest test result per test case from test_results shadow table
55
- const testStatus = {
56
- passed: 0,
57
- failed: 0,
58
- blocked: 0,
59
- not_run: 0,
60
- };
61
- if (testCaseIds.length > 0) {
62
- // Fetch all test results for this project, ordered by run_at desc
63
- const { data: results, error: resultsErr } = await client
64
- .from("test_results")
65
- .select("item_id, result, run_at")
66
- .eq("project_id", params.project_id)
67
- .order("run_at", { ascending: false });
68
- if (resultsErr)
69
- throw new Error(resultsErr.message);
70
- // Deduplicate to latest result per test case
71
- const latestByItem = new Map();
72
- for (const r of results ?? []) {
73
- if (!latestByItem.has(r.item_id)) {
74
- latestByItem.set(r.item_id, r.result);
75
- }
76
- }
77
- // Count statuses — test cases without any result are "not_run"
78
- for (const tcId of testCaseIds) {
79
- const result = latestByItem.get(tcId);
80
- if (!result || result === "not-run") {
81
- testStatus.not_run++;
82
- }
83
- else if (result === "passed") {
84
- testStatus.passed++;
85
- }
86
- else if (result === "failed") {
87
- testStatus.failed++;
88
- }
89
- else if (result === "blocked") {
90
- testStatus.blocked++;
91
- }
92
- else {
93
- testStatus.not_run++;
94
- }
95
- }
96
- }
97
- // 4. Coverage: requirements with at least one verified_by relationship
98
- const { data: verifiedRels, error: relErr } = await client
99
- .from("item_relationships")
100
- .select("from_id")
101
- .eq("project_id", params.project_id)
102
- .eq("type", "verified_by");
103
- if (relErr)
104
- throw new Error(relErr.message);
105
- const coveredIds = new Set((verifiedRels ?? []).map((r) => r.from_id));
106
- const coveredCount = requirementItems.filter((r) => coveredIds.has(r.id)).length;
107
- const totalReqs = requirementItems.length;
108
- const coverage = {
109
- covered: coveredCount,
110
- uncovered: totalReqs - coveredCount,
111
- total: totalReqs,
112
- percent: totalReqs > 0
113
- ? Math.round((coveredCount / totalReqs) * 1000) / 10
114
- : 100,
115
- };
116
- // 5. Coverage by domain
117
- const domainMap = new Map();
118
- for (const req of requirementItems) {
119
- const domains = req.domains.length > 0 ? req.domains : ["(untagged)"];
120
- const isCovered = coveredIds.has(req.id);
121
- for (const domain of domains) {
122
- if (!domainMap.has(domain)) {
123
- domainMap.set(domain, { covered: 0, total: 0 });
124
- }
125
- const entry = domainMap.get(domain);
126
- entry.total++;
127
- if (isCovered)
128
- entry.covered++;
129
- }
130
- }
131
- const coverageByDomain = Array.from(domainMap.entries())
132
- .sort((a, b) => a[0].localeCompare(b[0]))
133
- .map(([domain, stats]) => ({
134
- domain,
135
- covered: stats.covered,
136
- total: stats.total,
137
- percent: stats.total > 0
138
- ? Math.round((stats.covered / stats.total) * 1000) / 10
139
- : 100,
140
- }));
141
- // 6. Recent changes (last 7 days)
142
- const sevenDaysAgo = new Date();
143
- sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
144
- const { count: recentChanges, error: histErr } = await client
145
- .from("change_history")
146
- .select("id", { count: "exact", head: true })
147
- .eq("project_id", params.project_id)
148
- .gte("changed_at", sevenDaysAgo.toISOString());
149
- if (histErr)
150
- throw new Error(histErr.message);
151
- const data = {
152
- project_id: params.project_id,
153
- project_name: project.name,
154
- counts,
155
- test_status: testStatus,
156
- coverage,
157
- coverage_by_domain: coverageByDomain,
158
- recent_changes: recentChanges ?? 0,
159
- last_updated: new Date().toISOString(),
160
- };
161
- return formatToolResult(success(data));
162
- }
163
- catch (err) {
164
- if (err instanceof Error && err.message.includes("Not authenticated")) {
165
- return formatErrorResult(authError());
166
- }
167
- return formatErrorResult(dbError(err instanceof Error ? err.message : String(err)));
168
- }
169
- }
@@ -1,40 +0,0 @@
1
- /**
2
- * atoms_record_test_result — Record a pass/fail/blocked result for a test case.
3
- * Appends to test_results shadow table. Requires editor or admin role.
4
- */
5
- export declare function recordTestResultHandler(params: {
6
- project_id: string;
7
- item_id: string;
8
- result: "passed" | "failed" | "blocked" | "not-run";
9
- note?: string;
10
- }): Promise<{
11
- content: {
12
- type: "text";
13
- text: string;
14
- }[];
15
- structuredContent: {
16
- data: any[];
17
- meta: {
18
- truncated: boolean;
19
- truncation_message: string;
20
- total_count?: number | undefined;
21
- limit?: number | undefined;
22
- offset?: number | undefined;
23
- has_more?: boolean | undefined;
24
- };
25
- status: "success";
26
- };
27
- } | {
28
- content: {
29
- type: "text";
30
- text: string;
31
- }[];
32
- structuredContent: import("../types/responses.js").ToolError | import("../types/responses.js").ToolSuccess<{
33
- item_id: string;
34
- result: "passed" | "failed" | "blocked" | "not-run";
35
- run_at: string;
36
- run_by: string;
37
- note: string | null;
38
- message: string;
39
- }>;
40
- }>;
@@ -1,79 +0,0 @@
1
- /**
2
- * atoms_record_test_result — Record a pass/fail/blocked result for a test case.
3
- * Appends to test_results shadow table. Requires editor or admin role.
4
- */
5
- import { getClient, getUserId, getUserEmail, requireWriteAccess } from "../db/client.js";
6
- import { getItemById } from "../db/queries.js";
7
- import { getMcpSessionId } from "../server.js";
8
- import { formatToolResult, formatErrorResult, success, notFoundError, accessDeniedError, validationError, dbError, authError, } from "./_base.js";
9
- export async function recordTestResultHandler(params) {
10
- try {
11
- const client = await getClient();
12
- const userId = getUserId();
13
- const email = getUserEmail();
14
- // Check write access
15
- try {
16
- await requireWriteAccess(client, params.project_id);
17
- }
18
- catch (err) {
19
- if (err instanceof Error && err.message === "VIEWER_ROLE") {
20
- return formatErrorResult(accessDeniedError("Viewer"));
21
- }
22
- throw err;
23
- }
24
- // Verify the item exists and is a test case
25
- const item = await getItemById(client, params.project_id, params.item_id);
26
- if (!item) {
27
- return formatErrorResult(notFoundError("Item", params.item_id));
28
- }
29
- if (item.type !== "test-case") {
30
- return formatErrorResult(validationError(`Item '${params.item_id}' is a ${item.type}, not a test-case`));
31
- }
32
- const runAt = new Date().toISOString();
33
- // Insert into test_results shadow table
34
- const { error: insertErr } = await client
35
- .from("test_results")
36
- .insert({
37
- item_id: params.item_id,
38
- project_id: params.project_id,
39
- result: params.result,
40
- run_by: email || userId,
41
- run_at: runAt,
42
- note: params.note ?? null,
43
- });
44
- if (insertErr)
45
- throw new Error(insertErr.message);
46
- // Audit: log to change_history
47
- await client
48
- .from("change_history")
49
- .insert({
50
- item_id: params.item_id,
51
- project_id: params.project_id,
52
- changed_by: userId,
53
- event_type: "test_result_recorded",
54
- old_data: null,
55
- new_data: { result: params.result, run_at: runAt, note: params.note },
56
- actor: "mcp_claude",
57
- session_id: getMcpSessionId(),
58
- })
59
- .then(({ error }) => {
60
- if (error) {
61
- process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${error.message}\n`);
62
- }
63
- });
64
- return formatToolResult(success({
65
- item_id: params.item_id,
66
- result: params.result,
67
- run_at: runAt,
68
- run_by: email || userId,
69
- note: params.note ?? null,
70
- message: `Test result '${params.result}' recorded for ${params.item_id}`,
71
- }));
72
- }
73
- catch (err) {
74
- if (err instanceof Error && err.message.includes("Not authenticated")) {
75
- return formatErrorResult(authError());
76
- }
77
- return formatErrorResult(dbError(err instanceof Error ? err.message : String(err)));
78
- }
79
- }
@@ -1,33 +0,0 @@
1
- /**
2
- * atoms_search — Full-text search across items in a project.
3
- */
4
- import type { ItemSummary } from "../types/responses.js";
5
- export declare function searchHandler(params: {
6
- project_id: string;
7
- query: string;
8
- type?: string;
9
- limit: number;
10
- }): Promise<{
11
- content: {
12
- type: "text";
13
- text: string;
14
- }[];
15
- structuredContent: {
16
- data: any[];
17
- meta: {
18
- truncated: boolean;
19
- truncation_message: string;
20
- total_count?: number | undefined;
21
- limit?: number | undefined;
22
- offset?: number | undefined;
23
- has_more?: boolean | undefined;
24
- };
25
- status: "success";
26
- };
27
- } | {
28
- content: {
29
- type: "text";
30
- text: string;
31
- }[];
32
- structuredContent: import("../types/responses.js").ToolError | import("../types/responses.js").ToolSuccess<ItemSummary[]>;
33
- }>;
@@ -1,27 +0,0 @@
1
- /**
2
- * atoms_search — Full-text search across items in a project.
3
- */
4
- import { getClient } from "../db/client.js";
5
- import { searchItems } from "../db/queries.js";
6
- import { formatToolResult, formatErrorResult, success, paginationMeta, dbError, authError, } from "./_base.js";
7
- export async function searchHandler(params) {
8
- try {
9
- const client = await getClient();
10
- const { items, totalCount } = await searchItems(client, params.project_id, params.query, { type: params.type, limit: params.limit });
11
- const data = items.map((item) => ({
12
- id: item.id,
13
- title: item.title,
14
- type: item.type,
15
- status: item.data?.status,
16
- domains: item.data?.tags?.domains ?? [],
17
- level: item.data?.tags?.level,
18
- }));
19
- return formatToolResult(success(data, paginationMeta(totalCount, params.limit, 0)));
20
- }
21
- catch (err) {
22
- if (err instanceof Error && err.message.includes("Not authenticated")) {
23
- return formatErrorResult(authError());
24
- }
25
- return formatErrorResult(dbError(err instanceof Error ? err.message : String(err)));
26
- }
27
- }
@@ -1,52 +0,0 @@
1
- /**
2
- * atoms_trace — Graph traversal for traceability queries.
3
- *
4
- * Answers questions like "which requirements does TC-00003 verify?"
5
- * or "what's affected if I change REQ-00001?"
6
- */
7
- interface TraceNode {
8
- id: string;
9
- title: string;
10
- type: string;
11
- relationship: string;
12
- depth: number;
13
- }
14
- export declare function traceHandler(params: {
15
- project_id: string;
16
- item_id: string;
17
- direction: "upstream" | "downstream" | "both";
18
- depth: number;
19
- relationship_types?: string[];
20
- }): Promise<{
21
- content: {
22
- type: "text";
23
- text: string;
24
- }[];
25
- structuredContent: {
26
- data: any[];
27
- meta: {
28
- truncated: boolean;
29
- truncation_message: string;
30
- total_count?: number | undefined;
31
- limit?: number | undefined;
32
- offset?: number | undefined;
33
- has_more?: boolean | undefined;
34
- };
35
- status: "success";
36
- };
37
- } | {
38
- content: {
39
- type: "text";
40
- text: string;
41
- }[];
42
- structuredContent: import("../types/responses.js").ToolError | import("../types/responses.js").ToolSuccess<{
43
- truncated?: boolean | undefined;
44
- truncation_message?: string | undefined;
45
- root: string;
46
- direction: "upstream" | "downstream" | "both";
47
- depth_limit: number;
48
- items: TraceNode[];
49
- total_count: number;
50
- }>;
51
- }>;
52
- export {};