@atoms-tech/atoms-mcp 0.1.0 → 0.2.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/README.md +136 -0
- package/dist/auth/login.d.ts +6 -4
- package/dist/auth/login.d.ts.map +1 -1
- package/dist/auth/login.js +187 -19
- package/dist/auth/login.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/index.js +9 -9
- package/dist/index.js.map +1 -1
- package/dist/server.js +126 -0
- package/dist/server.js.map +1 -1
- package/dist/tools/bulk-import.d.ts +70 -0
- package/dist/tools/bulk-import.d.ts.map +1 -0
- package/dist/tools/bulk-import.js +188 -0
- package/dist/tools/bulk-import.js.map +1 -0
- package/dist/tools/project-summary.d.ts +64 -0
- package/dist/tools/project-summary.d.ts.map +1 -0
- package/dist/tools/project-summary.js +170 -0
- package/dist/tools/project-summary.js.map +1 -0
- package/dist/tools/trace.d.ts +53 -0
- package/dist/tools/trace.d.ts.map +1 -0
- package/dist/tools/trace.js +166 -0
- package/dist/tools/trace.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,166 @@
|
|
|
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
|
+
}
|
|
166
|
+
//# sourceMappingURL=trace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace.js","sourceRoot":"","sources":["../../src/tools/trace.ts"],"names":[],"mappings":"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"}
|