@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,165 +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
- import { getClient } from "../db/client.js";
8
- import { getItemById } from "../db/queries.js";
9
- import { formatToolResult, formatErrorResult, success, notFoundError, dbError, authError, } from "./_base.js";
10
- /** Maximum items returned to prevent huge responses. */
11
- const MAX_ITEMS = 200;
12
- /** Relationship types that point "upstream" (toward parents / dependencies). */
13
- const UPSTREAM_TYPES = ["parent", "verifies"];
14
- /** Relationship types that point "downstream" (toward dependents). */
15
- const DOWNSTREAM_TYPES = ["child", "verified_by"];
16
- /**
17
- * Inverse mapping: when traversing an incoming edge we need
18
- * to know what the outgoing type means for the other side.
19
- */
20
- const INVERSE_TYPE = {
21
- parent: "child",
22
- child: "parent",
23
- verifies: "verified_by",
24
- verified_by: "verifies",
25
- related: "related",
26
- };
27
- export async function traceHandler(params) {
28
- try {
29
- const maxDepth = Math.min(Math.max(params.depth ?? 5, 1), 10);
30
- const client = await getClient();
31
- // Verify root item exists
32
- const rootItem = await getItemById(client, params.project_id, params.item_id);
33
- if (!rootItem) {
34
- return formatErrorResult(notFoundError("Item", params.item_id));
35
- }
36
- // Fetch all relationships in the project once (same pattern as export-mermaid)
37
- const { data: rels, error: relsErr } = await client
38
- .from("item_relationships")
39
- .select("from_id, to_id, type")
40
- .eq("project_id", params.project_id);
41
- if (relsErr)
42
- throw new Error(relsErr.message);
43
- // Fetch all non-deleted items for title/type lookup
44
- const { data: items, error: itemsErr } = await client
45
- .from("items")
46
- .select("id, title, type")
47
- .eq("project_id", params.project_id)
48
- .is("deleted_at", null);
49
- if (itemsErr)
50
- throw new Error(itemsErr.message);
51
- const itemMap = new Map();
52
- for (const item of items ?? []) {
53
- itemMap.set(item.id, { title: item.title, type: item.type });
54
- }
55
- // Build adjacency lists based on direction
56
- // For each item, we store { neighbor, relType, direction }
57
- const adjacency = new Map();
58
- const addEdge = (from, to, relType) => {
59
- if (!adjacency.has(from))
60
- adjacency.set(from, []);
61
- adjacency.get(from).push({ neighbor: to, relType });
62
- };
63
- const allowedTypes = params.relationship_types
64
- ? new Set(params.relationship_types)
65
- : null;
66
- for (const rel of rels ?? []) {
67
- const relType = rel.type;
68
- // Upstream: follow edges where the item is the source and the rel points upstream
69
- // i.e., from_id -> to_id with type "parent" or "verifies"
70
- if (params.direction === "upstream" || params.direction === "both") {
71
- if (UPSTREAM_TYPES.includes(relType)) {
72
- if (!allowedTypes || allowedTypes.has(relType)) {
73
- addEdge(rel.from_id, rel.to_id, relType);
74
- }
75
- }
76
- // Also follow inverse: if from_id has a "child" pointing to to_id,
77
- // then to_id is upstream of from_id — but we model it as:
78
- // from_id's downstream is to_id via "child". For upstream from to_id,
79
- // we'd follow to_id -> from_id via inverse of "child" = "parent".
80
- if (DOWNSTREAM_TYPES.includes(relType)) {
81
- const inverseType = INVERSE_TYPE[relType] ?? relType;
82
- if (!allowedTypes || allowedTypes.has(inverseType)) {
83
- addEdge(rel.to_id, rel.from_id, inverseType);
84
- }
85
- }
86
- }
87
- if (params.direction === "downstream" || params.direction === "both") {
88
- if (DOWNSTREAM_TYPES.includes(relType)) {
89
- if (!allowedTypes || allowedTypes.has(relType)) {
90
- addEdge(rel.from_id, rel.to_id, relType);
91
- }
92
- }
93
- // Inverse: upstream type from the other side means downstream for us
94
- if (UPSTREAM_TYPES.includes(relType)) {
95
- const inverseType = INVERSE_TYPE[relType] ?? relType;
96
- if (!allowedTypes || allowedTypes.has(inverseType)) {
97
- addEdge(rel.to_id, rel.from_id, inverseType);
98
- }
99
- }
100
- }
101
- // "related" is bidirectional — add both directions if allowed
102
- if (relType === "related") {
103
- if (!allowedTypes || allowedTypes.has("related")) {
104
- addEdge(rel.from_id, rel.to_id, "related");
105
- addEdge(rel.to_id, rel.from_id, "related");
106
- }
107
- }
108
- }
109
- // BFS traversal from root
110
- const visited = new Set();
111
- visited.add(params.item_id);
112
- const result = [];
113
- const queue = [];
114
- // Seed queue with direct neighbors of the root
115
- const rootNeighbors = adjacency.get(params.item_id) ?? [];
116
- for (const edge of rootNeighbors) {
117
- queue.push({ id: edge.neighbor, depth: 1, relType: edge.relType });
118
- }
119
- while (queue.length > 0 && result.length < MAX_ITEMS) {
120
- const { id, depth, relType } = queue.shift();
121
- if (visited.has(id) || depth > maxDepth)
122
- continue;
123
- visited.add(id);
124
- const info = itemMap.get(id);
125
- if (!info)
126
- continue; // skip deleted items
127
- result.push({
128
- id,
129
- title: info.title,
130
- type: info.type,
131
- relationship: relType,
132
- depth,
133
- });
134
- // Enqueue neighbors at next depth
135
- if (depth < maxDepth) {
136
- const neighbors = adjacency.get(id) ?? [];
137
- for (const edge of neighbors) {
138
- if (!visited.has(edge.neighbor)) {
139
- queue.push({
140
- id: edge.neighbor,
141
- depth: depth + 1,
142
- relType: edge.relType,
143
- });
144
- }
145
- }
146
- }
147
- }
148
- return formatToolResult(success({
149
- root: params.item_id,
150
- direction: params.direction,
151
- depth_limit: maxDepth,
152
- items: result,
153
- total_count: result.length,
154
- ...(result.length >= MAX_ITEMS
155
- ? { truncated: true, truncation_message: `Results capped at ${MAX_ITEMS} items. Use a smaller depth or filter by relationship_types.` }
156
- : {}),
157
- }));
158
- }
159
- catch (err) {
160
- if (err instanceof Error && err.message.includes("Not authenticated")) {
161
- return formatErrorResult(authError());
162
- }
163
- return formatErrorResult(dbError(err instanceof Error ? err.message : String(err)));
164
- }
165
- }
@@ -1,42 +0,0 @@
1
- /**
2
- * atoms_update_item — Update an existing item's fields.
3
- * Requires editor or admin role. Logs old/new diff to change_history.
4
- */
5
- export declare function updateItemHandler(params: {
6
- project_id: string;
7
- item_id: string;
8
- title?: string;
9
- body?: string;
10
- summary?: string;
11
- domains?: string[];
12
- level?: string;
13
- status?: "passed" | "failed" | "blocked" | "not-run";
14
- }): Promise<{
15
- content: {
16
- type: "text";
17
- text: string;
18
- }[];
19
- structuredContent: {
20
- data: any[];
21
- meta: {
22
- truncated: boolean;
23
- truncation_message: string;
24
- total_count?: number | undefined;
25
- limit?: number | undefined;
26
- offset?: number | undefined;
27
- has_more?: boolean | undefined;
28
- };
29
- status: "success";
30
- };
31
- } | {
32
- content: {
33
- type: "text";
34
- text: string;
35
- }[];
36
- structuredContent: import("../types/responses.js").ToolError | import("../types/responses.js").ToolSuccess<{
37
- id: string;
38
- title: string;
39
- type: string;
40
- updated_fields: string[];
41
- }>;
42
- }>;
@@ -1,97 +0,0 @@
1
- /**
2
- * atoms_update_item — Update an existing item's fields.
3
- * Requires editor or admin role. Logs old/new diff to change_history.
4
- */
5
- import { getClient, getUserId, 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, dbError, authError, } from "./_base.js";
9
- export async function updateItemHandler(params) {
10
- try {
11
- const client = await getClient();
12
- const userId = getUserId();
13
- // Check write access
14
- try {
15
- await requireWriteAccess(client, params.project_id);
16
- }
17
- catch (err) {
18
- if (err instanceof Error && err.message === "VIEWER_ROLE") {
19
- return formatErrorResult(accessDeniedError("Viewer"));
20
- }
21
- throw err;
22
- }
23
- // Get existing item
24
- const existing = await getItemById(client, params.project_id, params.item_id);
25
- if (!existing) {
26
- return formatErrorResult(notFoundError("Item", params.item_id));
27
- }
28
- // Build updated data blob
29
- const oldData = { ...existing.data };
30
- const updatedData = { ...existing.data };
31
- if (params.title !== undefined) {
32
- updatedData.title = params.title;
33
- }
34
- if (params.body !== undefined) {
35
- updatedData.body = params.body;
36
- }
37
- if (params.summary !== undefined) {
38
- updatedData.summary = params.summary;
39
- }
40
- if (params.domains !== undefined) {
41
- updatedData.tags = { ...updatedData.tags, domains: params.domains };
42
- }
43
- if (params.level !== undefined) {
44
- updatedData.tags = { ...updatedData.tags, level: params.level };
45
- }
46
- if (params.status !== undefined) {
47
- updatedData.status = params.status;
48
- }
49
- updatedData.metadata = {
50
- ...updatedData.metadata,
51
- updated_at: new Date().toISOString(),
52
- updated_by: userId,
53
- };
54
- // Update in database
55
- const { error: updateErr } = await client
56
- .from("items")
57
- .update({
58
- title: params.title ?? existing.title,
59
- data: updatedData,
60
- updated_by: userId,
61
- })
62
- .eq("id", params.item_id)
63
- .eq("project_id", params.project_id);
64
- if (updateErr)
65
- throw new Error(updateErr.message);
66
- // Audit: log diff to change_history
67
- await client
68
- .from("change_history")
69
- .insert({
70
- item_id: params.item_id,
71
- project_id: params.project_id,
72
- changed_by: userId,
73
- event_type: "updated",
74
- old_data: oldData,
75
- new_data: updatedData,
76
- actor: "mcp_claude",
77
- session_id: getMcpSessionId(),
78
- })
79
- .then(({ error }) => {
80
- if (error) {
81
- process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${error.message}\n`);
82
- }
83
- });
84
- return formatToolResult(success({
85
- id: params.item_id,
86
- title: updatedData.title,
87
- type: existing.type,
88
- updated_fields: Object.keys(params).filter((k) => k !== "project_id" && k !== "item_id" && params[k] !== undefined),
89
- }));
90
- }
91
- catch (err) {
92
- if (err instanceof Error && err.message.includes("Not authenticated")) {
93
- return formatErrorResult(authError());
94
- }
95
- return formatErrorResult(dbError(err instanceof Error ? err.message : String(err)));
96
- }
97
- }
@@ -1,57 +0,0 @@
1
- /**
2
- * Standard response envelopes for all MCP tool responses.
3
- * Every tool returns JSON matching one of these shapes.
4
- */
5
- export interface ToolSuccess<T = unknown> {
6
- [key: string]: unknown;
7
- status: "success";
8
- data: T;
9
- meta?: PaginationMeta;
10
- }
11
- export interface ToolError {
12
- [key: string]: unknown;
13
- status: "error";
14
- message: string;
15
- next_steps: string[];
16
- }
17
- export interface PaginationMeta {
18
- [key: string]: unknown;
19
- total_count: number;
20
- limit: number;
21
- offset: number;
22
- has_more: boolean;
23
- }
24
- export type ToolResponse<T = unknown> = ToolSuccess<T> | ToolError;
25
- /**
26
- * Item summary returned by list/search tools (@basic fields).
27
- */
28
- export interface ItemSummary {
29
- id: string;
30
- title: string;
31
- type: string;
32
- status?: string;
33
- domains: string[];
34
- level?: string;
35
- }
36
- /**
37
- * Coverage report returned by atoms_get_coverage.
38
- */
39
- export interface CoverageReport {
40
- covered: number;
41
- uncovered: number;
42
- total: number;
43
- coverage_percent: number;
44
- uncovered_items: ItemSummary[];
45
- }
46
- /**
47
- * Change history entry returned by atoms_get_history.
48
- */
49
- export interface HistoryEntry {
50
- id: string;
51
- event_type: string;
52
- changed_by: string | null;
53
- changed_at: string;
54
- actor: string;
55
- session_id: string | null;
56
- fields_changed: string[];
57
- }
@@ -1,5 +0,0 @@
1
- /**
2
- * Standard response envelopes for all MCP tool responses.
3
- * Every tool returns JSON matching one of these shapes.
4
- */
5
- export {};
@@ -1,68 +0,0 @@
1
- /**
2
- * WorkItem type definition — shared with the ATOMS frontend.
3
- * Source of truth: src/app/types.ts in the main app.
4
- *
5
- * This is a subset focused on what the MCP server needs.
6
- */
7
- export interface WorkItemRelationships {
8
- parents: string[];
9
- children: string[];
10
- related: string[];
11
- verified_by?: string[];
12
- verifies?: string[];
13
- }
14
- export interface WorkItemOwnership {
15
- primary: string | null;
16
- additional: string[];
17
- }
18
- export interface WorkItemTags {
19
- domains: string[];
20
- level?: string;
21
- }
22
- export interface TestRun {
23
- date: string;
24
- result: "passed" | "failed" | "blocked" | "not-run";
25
- by: string;
26
- note?: string;
27
- }
28
- export interface WorkItemLink {
29
- url: string;
30
- label: string;
31
- }
32
- export interface WorkItemMetadata {
33
- created_at: string;
34
- created_by: string;
35
- updated_at: string;
36
- updated_by: string;
37
- source_id?: string;
38
- imported_at?: string;
39
- }
40
- export interface WorkItem {
41
- id: string;
42
- type: "requirement" | "test-case" | "note" | "table";
43
- title: string;
44
- summary?: string;
45
- body?: string;
46
- tags: WorkItemTags;
47
- ownership: WorkItemOwnership;
48
- relationships: WorkItemRelationships;
49
- links: WorkItemLink[];
50
- status?: "passed" | "failed" | "blocked" | "not-run";
51
- runs?: TestRun[];
52
- metadata: WorkItemMetadata;
53
- }
54
- /**
55
- * Database row shape from Supabase items table.
56
- */
57
- export interface ItemRow {
58
- id: string;
59
- project_id: string;
60
- type: string;
61
- title: string;
62
- data: WorkItem;
63
- created_by: string | null;
64
- updated_by: string | null;
65
- created_at: string;
66
- updated_at: string;
67
- deleted_at: string | null;
68
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * WorkItem type definition — shared with the ATOMS frontend.
3
- * Source of truth: src/app/types.ts in the main app.
4
- *
5
- * This is a subset focused on what the MCP server needs.
6
- */
7
- export {};