@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,57 +0,0 @@
1
- /**
2
- * Shared error handling and response formatting for all MCP tools.
3
- *
4
- * Every error MUST include `next_steps` — actionable guidance for the LLM.
5
- * This is the #1 pattern from the Polarion MCP that improves agent reliability.
6
- */
7
- import type { ToolError, ToolSuccess, PaginationMeta } from "../types/responses.js";
8
- /** Maximum response size in characters. Truncate with clear message if exceeded. */
9
- export declare const CHARACTER_LIMIT = 25000;
10
- export declare function success<T>(data: T, meta?: PaginationMeta): ToolSuccess<T>;
11
- export declare function paginationMeta(totalCount: number, limit: number, offset: number): PaginationMeta;
12
- export declare function errorResponse(message: string, nextSteps: string[]): ToolError;
13
- export declare function notFoundError(resource: string, id: string): ToolError;
14
- export declare function accessDeniedError(role: string): ToolError;
15
- export declare function validationError(message: string): ToolError;
16
- export declare function authError(): ToolError;
17
- export declare function rateLimitError(retryAfterSeconds: number): ToolError;
18
- export declare function dbError(message: string): ToolError;
19
- /**
20
- * Format a tool result as JSON string + structuredContent for the MCP SDK.
21
- * Applies CHARACTER_LIMIT truncation if needed.
22
- */
23
- export declare function formatToolResult<T>(result: ToolSuccess<T> | ToolError): {
24
- content: {
25
- type: "text";
26
- text: string;
27
- }[];
28
- structuredContent: {
29
- data: any[];
30
- meta: {
31
- truncated: boolean;
32
- truncation_message: string;
33
- total_count?: number | undefined;
34
- limit?: number | undefined;
35
- offset?: number | undefined;
36
- has_more?: boolean | undefined;
37
- };
38
- status: "success";
39
- };
40
- } | {
41
- content: {
42
- type: "text";
43
- text: string;
44
- }[];
45
- structuredContent: ToolError | ToolSuccess<T>;
46
- };
47
- /**
48
- * Format an error result for the MCP SDK.
49
- */
50
- export declare function formatErrorResult(error: ToolError): {
51
- content: {
52
- type: "text";
53
- text: string;
54
- }[];
55
- structuredContent: ToolError;
56
- isError: boolean;
57
- };
@@ -1,108 +0,0 @@
1
- /**
2
- * Shared error handling and response formatting for all MCP tools.
3
- *
4
- * Every error MUST include `next_steps` — actionable guidance for the LLM.
5
- * This is the #1 pattern from the Polarion MCP that improves agent reliability.
6
- */
7
- /** Maximum response size in characters. Truncate with clear message if exceeded. */
8
- export const CHARACTER_LIMIT = 25_000;
9
- // ---------------------------------------------------------------------------
10
- // Success helpers
11
- // ---------------------------------------------------------------------------
12
- export function success(data, meta) {
13
- return { status: "success", data, ...(meta ? { meta } : {}) };
14
- }
15
- export function paginationMeta(totalCount, limit, offset) {
16
- return {
17
- total_count: totalCount,
18
- limit,
19
- offset,
20
- has_more: offset + limit < totalCount,
21
- };
22
- }
23
- // ---------------------------------------------------------------------------
24
- // Error helpers
25
- // ---------------------------------------------------------------------------
26
- export function errorResponse(message, nextSteps) {
27
- return { status: "error", message, next_steps: nextSteps };
28
- }
29
- export function notFoundError(resource, id) {
30
- return errorResponse(`${resource} '${id}' not found`, [
31
- `Verify the ${resource.toLowerCase()} ID is correct`,
32
- `Use atoms_list_items or atoms_search to find valid IDs`,
33
- ]);
34
- }
35
- export function accessDeniedError(role) {
36
- return errorResponse(`${role} role cannot modify project data`, [
37
- "Contact your org admin to upgrade your role to editor",
38
- "Use read-only tools (atoms_list_items, atoms_get_item, atoms_search) instead",
39
- ]);
40
- }
41
- export function validationError(message) {
42
- return errorResponse(`Validation error: ${message}`, [
43
- "Check the parameter types and constraints in the tool description",
44
- "Ensure all required parameters are provided",
45
- ]);
46
- }
47
- export function authError() {
48
- return errorResponse("Not authenticated. Run 'npx @atoms-tech/atoms-mcp login' first.", [
49
- "Run: npx @atoms-tech/atoms-mcp login",
50
- "Or set ATOMS_ACCESS_TOKEN environment variable",
51
- ]);
52
- }
53
- export function rateLimitError(retryAfterSeconds) {
54
- return errorResponse("Rate limit exceeded", [
55
- `Wait ${retryAfterSeconds} seconds before making more requests`,
56
- "Reduce the frequency of tool calls",
57
- ]);
58
- }
59
- export function dbError(message) {
60
- return errorResponse(`Database error: ${message}`, [
61
- "This may be a temporary issue — try again",
62
- "If the error persists, check that the project_id is valid",
63
- ]);
64
- }
65
- // ---------------------------------------------------------------------------
66
- // Response formatting
67
- // ---------------------------------------------------------------------------
68
- /**
69
- * Format a tool result as JSON string + structuredContent for the MCP SDK.
70
- * Applies CHARACTER_LIMIT truncation if needed.
71
- */
72
- export function formatToolResult(result) {
73
- const json = JSON.stringify(result, null, 2);
74
- if (json.length > CHARACTER_LIMIT) {
75
- // If it's a success with array data, truncate the data
76
- if (result.status === "success" && Array.isArray(result.data)) {
77
- const halfLength = Math.max(1, Math.floor(result.data.length / 2));
78
- const truncated = {
79
- ...result,
80
- data: result.data.slice(0, halfLength),
81
- meta: {
82
- ...result.meta,
83
- truncated: true,
84
- truncation_message: `Response truncated from ${result.data.length} to ${halfLength} items. ` +
85
- `Use 'offset' parameter or add filters to see more results.`,
86
- },
87
- };
88
- return {
89
- content: [{ type: "text", text: JSON.stringify(truncated, null, 2) }],
90
- structuredContent: truncated,
91
- };
92
- }
93
- }
94
- return {
95
- content: [{ type: "text", text: json }],
96
- structuredContent: result,
97
- };
98
- }
99
- /**
100
- * Format an error result for the MCP SDK.
101
- */
102
- export function formatErrorResult(error) {
103
- return {
104
- content: [{ type: "text", text: JSON.stringify(error, null, 2) }],
105
- structuredContent: error,
106
- isError: true,
107
- };
108
- }
@@ -1,69 +0,0 @@
1
- /**
2
- * atoms_bulk_import — Bulk create multiple items in a single tool call.
3
- * Essential for AI agents that generate 20+ requirements at once.
4
- *
5
- * Checks write access once, generates sequential IDs, batch inserts,
6
- * and reports per-item errors without aborting the entire batch.
7
- */
8
- interface BulkImportItem {
9
- type: "requirement" | "test-case" | "note";
10
- title: string;
11
- body?: string;
12
- summary?: string;
13
- domains?: string[];
14
- level?: string;
15
- parent_id?: string;
16
- }
17
- interface CreatedItem {
18
- id: string;
19
- title: string;
20
- type: string;
21
- }
22
- interface BulkImportError {
23
- index: number;
24
- title: string;
25
- error: string;
26
- }
27
- export declare function bulkImportHandler(params: {
28
- project_id: string;
29
- items: BulkImportItem[];
30
- }): Promise<{
31
- content: {
32
- type: "text";
33
- text: string;
34
- }[];
35
- structuredContent: {
36
- data: any[];
37
- meta: {
38
- truncated: boolean;
39
- truncation_message: string;
40
- total_count?: number | undefined;
41
- limit?: number | undefined;
42
- offset?: number | undefined;
43
- has_more?: boolean | undefined;
44
- };
45
- status: "success";
46
- };
47
- } | {
48
- content: {
49
- type: "text";
50
- text: string;
51
- }[];
52
- structuredContent: import("../types/responses.js").ToolError | import("../types/responses.js").ToolSuccess<{
53
- created: number;
54
- items: never[];
55
- errors: BulkImportError[];
56
- }>;
57
- } | {
58
- content: {
59
- type: "text";
60
- text: string;
61
- }[];
62
- structuredContent: import("../types/responses.js").ToolError | import("../types/responses.js").ToolSuccess<{
63
- created: number;
64
- items: CreatedItem[];
65
- errors: BulkImportError[];
66
- project_id: string;
67
- }>;
68
- }>;
69
- export {};
@@ -1,187 +0,0 @@
1
- /**
2
- * atoms_bulk_import — Bulk create multiple items in a single tool call.
3
- * Essential for AI agents that generate 20+ requirements at once.
4
- *
5
- * Checks write access once, generates sequential IDs, batch inserts,
6
- * and reports per-item errors without aborting the entire batch.
7
- */
8
- import { getClient, getUserId, requireWriteAccess } from "../db/client.js";
9
- import { generateItemId } from "../db/queries.js";
10
- import { getMcpSessionId } from "../server.js";
11
- import { formatToolResult, formatErrorResult, success, accessDeniedError, validationError, dbError, authError, } from "./_base.js";
12
- export async function bulkImportHandler(params) {
13
- try {
14
- // Validate item count
15
- if (!params.items || params.items.length === 0) {
16
- return formatErrorResult(validationError("items array must contain at least 1 item"));
17
- }
18
- if (params.items.length > 100) {
19
- return formatErrorResult(validationError(`Maximum 100 items per call (received ${params.items.length}). Split into multiple calls.`));
20
- }
21
- const client = await getClient();
22
- const userId = getUserId();
23
- // Check write access once for the entire batch
24
- try {
25
- await requireWriteAccess(client, params.project_id);
26
- }
27
- catch (err) {
28
- if (err instanceof Error && err.message === "VIEWER_ROLE") {
29
- return formatErrorResult(accessDeniedError("Viewer"));
30
- }
31
- throw err;
32
- }
33
- const now = new Date().toISOString();
34
- const sessionId = getMcpSessionId();
35
- const created = [];
36
- const errors = [];
37
- // Generate all IDs sequentially (each call increments the counter)
38
- const itemsWithIds = [];
39
- for (let i = 0; i < params.items.length; i++) {
40
- const input = params.items[i];
41
- try {
42
- const itemId = await generateItemId(client, params.project_id, input.type);
43
- itemsWithIds.push({ itemId, input, index: i });
44
- }
45
- catch (err) {
46
- errors.push({
47
- index: i,
48
- title: input.title,
49
- error: `ID generation failed: ${err instanceof Error ? err.message : String(err)}`,
50
- });
51
- }
52
- }
53
- if (itemsWithIds.length === 0) {
54
- return formatToolResult(success({
55
- created: 0,
56
- items: [],
57
- errors,
58
- }));
59
- }
60
- // Build insert rows and WorkItem data blobs
61
- const insertRows = [];
62
- const workItemMap = new Map();
63
- for (const { itemId, input } of itemsWithIds) {
64
- const workItem = {
65
- id: itemId,
66
- type: input.type,
67
- title: input.title,
68
- summary: input.summary,
69
- body: input.body,
70
- tags: {
71
- domains: input.domains ?? [],
72
- level: input.level,
73
- },
74
- ownership: {
75
- primary: null,
76
- additional: [],
77
- },
78
- relationships: {
79
- parents: input.parent_id ? [input.parent_id] : [],
80
- children: [],
81
- related: [],
82
- },
83
- links: [],
84
- metadata: {
85
- created_at: now,
86
- created_by: userId,
87
- updated_at: now,
88
- updated_by: userId,
89
- },
90
- };
91
- workItemMap.set(itemId, workItem);
92
- insertRows.push({
93
- id: itemId,
94
- project_id: params.project_id,
95
- type: input.type,
96
- title: input.title,
97
- data: workItem,
98
- created_by: userId,
99
- updated_by: userId,
100
- });
101
- }
102
- // Batch insert all items
103
- const { error: insertErr } = await client
104
- .from("items")
105
- .insert(insertRows);
106
- if (insertErr) {
107
- // If batch insert fails, fall back to individual inserts
108
- for (const { itemId, input, index } of itemsWithIds) {
109
- const row = insertRows.find((r) => r.id === itemId);
110
- if (!row)
111
- continue;
112
- const { error: singleErr } = await client
113
- .from("items")
114
- .insert(row);
115
- if (singleErr) {
116
- errors.push({
117
- index,
118
- title: input.title,
119
- error: singleErr.message,
120
- });
121
- }
122
- else {
123
- created.push({ id: itemId, title: input.title, type: input.type });
124
- }
125
- }
126
- }
127
- else {
128
- // Batch succeeded — all items created
129
- for (const { itemId, input } of itemsWithIds) {
130
- created.push({ id: itemId, title: input.title, type: input.type });
131
- }
132
- }
133
- // Create relationships for items with parent_id (fire-and-forget)
134
- const relRows = itemsWithIds
135
- .filter(({ input }) => input.parent_id)
136
- .filter(({ itemId }) => created.some((c) => c.id === itemId))
137
- .map(({ itemId, input }) => ({
138
- from_id: itemId,
139
- to_id: input.parent_id,
140
- type: "parent",
141
- project_id: params.project_id,
142
- }));
143
- if (relRows.length > 0) {
144
- await client
145
- .from("item_relationships")
146
- .insert(relRows)
147
- .then(({ error }) => {
148
- if (error) {
149
- process.stderr.write(`[atoms-mcp] Warning: bulk relationship sync failed: ${error.message}\n`);
150
- }
151
- });
152
- }
153
- // Audit: log one change_history entry per created item (fire-and-forget)
154
- const historyRows = created.map((item) => ({
155
- item_id: item.id,
156
- project_id: params.project_id,
157
- changed_by: userId,
158
- event_type: "created",
159
- old_data: null,
160
- new_data: workItemMap.get(item.id) ?? null,
161
- actor: "mcp_claude",
162
- session_id: sessionId,
163
- }));
164
- if (historyRows.length > 0) {
165
- await client
166
- .from("change_history")
167
- .insert(historyRows)
168
- .then(({ error }) => {
169
- if (error) {
170
- process.stderr.write(`[atoms-mcp] Warning: bulk change_history log failed: ${error.message}\n`);
171
- }
172
- });
173
- }
174
- return formatToolResult(success({
175
- created: created.length,
176
- items: created,
177
- errors: errors.length > 0 ? errors : [],
178
- project_id: params.project_id,
179
- }));
180
- }
181
- catch (err) {
182
- if (err instanceof Error && err.message.includes("Not authenticated")) {
183
- return formatErrorResult(authError());
184
- }
185
- return formatErrorResult(dbError(err instanceof Error ? err.message : String(err)));
186
- }
187
- }
@@ -1,42 +0,0 @@
1
- /**
2
- * atoms_create_item — Create a new requirement, test case, or note.
3
- * Requires editor or admin role. Logs with AI actor attribution.
4
- */
5
- export declare function createItemHandler(params: {
6
- project_id: string;
7
- type: "requirement" | "test-case" | "note";
8
- title: string;
9
- body?: string;
10
- summary?: string;
11
- domains?: string[];
12
- level?: string;
13
- parent_ids?: string[];
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
- type: "requirement" | "test-case" | "note";
39
- title: string;
40
- project_id: string;
41
- }>;
42
- }>;
@@ -1,117 +0,0 @@
1
- /**
2
- * atoms_create_item — Create a new requirement, test case, or note.
3
- * Requires editor or admin role. Logs with AI actor attribution.
4
- */
5
- import { getClient, getUserId, requireWriteAccess } from "../db/client.js";
6
- import { generateItemId } from "../db/queries.js";
7
- import { getMcpSessionId } from "../server.js";
8
- import { formatToolResult, formatErrorResult, success, accessDeniedError, dbError, authError, } from "./_base.js";
9
- export async function createItemHandler(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
- // Generate next ID (uses SECURITY DEFINER function to avoid collisions)
24
- const itemId = await generateItemId(client, params.project_id, params.type);
25
- // Build WorkItem data blob
26
- const workItem = {
27
- id: itemId,
28
- type: params.type,
29
- title: params.title,
30
- summary: params.summary,
31
- body: params.body,
32
- tags: {
33
- domains: params.domains ?? [],
34
- level: params.level,
35
- },
36
- ownership: {
37
- primary: null,
38
- additional: [],
39
- },
40
- relationships: {
41
- parents: params.parent_ids ?? [],
42
- children: [],
43
- related: [],
44
- },
45
- links: [],
46
- metadata: {
47
- created_at: new Date().toISOString(),
48
- created_by: userId,
49
- updated_at: new Date().toISOString(),
50
- updated_by: userId,
51
- },
52
- };
53
- // Insert to items table
54
- const { error: insertErr } = await client
55
- .from("items")
56
- .insert({
57
- id: itemId,
58
- project_id: params.project_id,
59
- type: params.type,
60
- title: params.title,
61
- data: workItem,
62
- created_by: userId,
63
- updated_by: userId,
64
- })
65
- .select()
66
- .single();
67
- if (insertErr)
68
- throw new Error(insertErr.message);
69
- // Dual-write: sync relationships to shadow table
70
- if (params.parent_ids && params.parent_ids.length > 0) {
71
- const relRows = params.parent_ids.map((parentId) => ({
72
- from_id: itemId,
73
- to_id: parentId,
74
- type: "parent",
75
- project_id: params.project_id,
76
- }));
77
- await client
78
- .from("item_relationships")
79
- .insert(relRows)
80
- .then(({ error }) => {
81
- if (error) {
82
- process.stderr.write(`[atoms-mcp] Warning: relationship sync failed: ${error.message}\n`);
83
- }
84
- });
85
- }
86
- // Audit: log to change_history with MCP actor
87
- await client
88
- .from("change_history")
89
- .insert({
90
- item_id: itemId,
91
- project_id: params.project_id,
92
- changed_by: userId,
93
- event_type: "created",
94
- old_data: null,
95
- new_data: workItem,
96
- actor: "mcp_claude",
97
- session_id: getMcpSessionId(),
98
- })
99
- .then(({ error }) => {
100
- if (error) {
101
- process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${error.message}\n`);
102
- }
103
- });
104
- return formatToolResult(success({
105
- id: itemId,
106
- type: params.type,
107
- title: params.title,
108
- project_id: params.project_id,
109
- }));
110
- }
111
- catch (err) {
112
- if (err instanceof Error && err.message.includes("Not authenticated")) {
113
- return formatErrorResult(authError());
114
- }
115
- return formatErrorResult(dbError(err instanceof Error ? err.message : String(err)));
116
- }
117
- }
@@ -1,37 +0,0 @@
1
- /**
2
- * atoms_delete_item — Soft-delete an item (sets deleted_at, preserves for audit).
3
- * Requires editor or admin role.
4
- */
5
- export declare function deleteItemHandler(params: {
6
- project_id: string;
7
- item_id: string;
8
- }): Promise<{
9
- content: {
10
- type: "text";
11
- text: string;
12
- }[];
13
- structuredContent: {
14
- data: any[];
15
- meta: {
16
- truncated: boolean;
17
- truncation_message: string;
18
- total_count?: number | undefined;
19
- limit?: number | undefined;
20
- offset?: number | undefined;
21
- has_more?: boolean | undefined;
22
- };
23
- status: "success";
24
- };
25
- } | {
26
- content: {
27
- type: "text";
28
- text: string;
29
- }[];
30
- structuredContent: import("../types/responses.js").ToolError | import("../types/responses.js").ToolSuccess<{
31
- id: string;
32
- title: string;
33
- type: string;
34
- deleted: boolean;
35
- message: string;
36
- }>;
37
- }>;
@@ -1,68 +0,0 @@
1
- /**
2
- * atoms_delete_item — Soft-delete an item (sets deleted_at, preserves for audit).
3
- * Requires editor or admin role.
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 deleteItemHandler(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
- // Soft delete (set deleted_at, never remove the row)
29
- const { error: deleteErr } = await client
30
- .from("items")
31
- .update({ deleted_at: new Date().toISOString() })
32
- .eq("id", params.item_id)
33
- .eq("project_id", params.project_id);
34
- if (deleteErr)
35
- throw new Error(deleteErr.message);
36
- // Audit: log deletion to change_history
37
- await client
38
- .from("change_history")
39
- .insert({
40
- item_id: params.item_id,
41
- project_id: params.project_id,
42
- changed_by: userId,
43
- event_type: "deleted",
44
- old_data: existing.data,
45
- new_data: null,
46
- actor: "mcp_claude",
47
- session_id: getMcpSessionId(),
48
- })
49
- .then(({ error }) => {
50
- if (error) {
51
- process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${error.message}\n`);
52
- }
53
- });
54
- return formatToolResult(success({
55
- id: params.item_id,
56
- title: existing.title,
57
- type: existing.type,
58
- deleted: true,
59
- message: `Item ${params.item_id} soft-deleted. It can still be found in audit logs.`,
60
- }));
61
- }
62
- catch (err) {
63
- if (err instanceof Error && err.message.includes("Not authenticated")) {
64
- return formatErrorResult(authError());
65
- }
66
- return formatErrorResult(dbError(err instanceof Error ? err.message : String(err)));
67
- }
68
- }