@dypai-ai/mcp 1.6.5 → 1.6.6
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/package.json +1 -1
- package/src/index.js +1 -1
- package/src/tools/project-artifacts.js +85 -34
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -553,7 +553,7 @@ async function handleRequest(msg) {
|
|
|
553
553
|
return makeResponse(id, {
|
|
554
554
|
protocolVersion: "2024-11-05",
|
|
555
555
|
capabilities: { tools: {} },
|
|
556
|
-
serverInfo: { name: "dypai", version: "1.6.
|
|
556
|
+
serverInfo: { name: "dypai", version: "1.6.6" },
|
|
557
557
|
instructions: serverInstructions,
|
|
558
558
|
})
|
|
559
559
|
}
|
|
@@ -32,24 +32,38 @@ const STUDIO_SAFE_MODULE_KINDS = new Set([
|
|
|
32
32
|
|
|
33
33
|
function isStudioSafeArtifact(item) {
|
|
34
34
|
if (!isObject(item)) return false
|
|
35
|
+
if (item.can_apply === true) return true
|
|
36
|
+
if (!item.artifact_type && !item.kind && !item.module_kind && !item.feature_kind && !item.category) return true
|
|
35
37
|
const artifactType = item.artifact_type || item.kind
|
|
36
|
-
const moduleKind = item.module_kind || item.feature_kind
|
|
38
|
+
const moduleKind = item.module_kind || item.feature_kind || item.category
|
|
37
39
|
return artifactType === "feature" && STUDIO_SAFE_MODULE_KINDS.has(moduleKind)
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
function
|
|
42
|
+
function leanArtifact(item) {
|
|
43
|
+
return {
|
|
44
|
+
slug: item.slug,
|
|
45
|
+
name: item.name,
|
|
46
|
+
summary: item.summary || item.description,
|
|
47
|
+
best_for: item.best_for || item.use_when,
|
|
48
|
+
requires: normalizeList(item.requires || item.requires_artifacts).slice(0, 5),
|
|
49
|
+
includes: normalizeList(item.includes || item.provides_capabilities).slice(0, 6),
|
|
50
|
+
next_step: item.next_step || (item.slug ? `manage_project_artifact(operation:'inspect', slug:'${item.slug}')` : undefined),
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function filterArtifactSearchResult(raw, limit) {
|
|
41
55
|
if (!isObject(raw)) return raw
|
|
42
|
-
const cap = Number.isFinite(Number(limit)) ? Number(limit) :
|
|
43
|
-
const artifacts = normalizeArtifactList(raw.artifacts)
|
|
44
|
-
|
|
56
|
+
const cap = Number.isFinite(Number(limit)) ? Number(limit) : 3
|
|
57
|
+
const artifacts = normalizeArtifactList(raw.artifacts)
|
|
58
|
+
.filter(isStudioSafeArtifact)
|
|
59
|
+
.slice(0, cap)
|
|
60
|
+
.map(leanArtifact)
|
|
45
61
|
return {
|
|
46
|
-
|
|
47
|
-
|
|
62
|
+
ok: raw.ok !== false,
|
|
63
|
+
query: raw.query,
|
|
48
64
|
results_count: artifacts.length,
|
|
49
65
|
artifacts,
|
|
50
|
-
|
|
51
|
-
features,
|
|
52
|
-
migration_note: "Studio only exposes frontend/UI artifacts. Backend/database artifacts must be implemented as Flow before Studio install.",
|
|
66
|
+
hint: raw.hint || "Pick one slug, inspect it, then apply only if it fits the current app.",
|
|
53
67
|
}
|
|
54
68
|
}
|
|
55
69
|
|
|
@@ -76,11 +90,20 @@ function manifestFromModuleContract(contract) {
|
|
|
76
90
|
const mount = Array.isArray(contract?.mount_contract?.manifest) ? contract.mount_contract.manifest : []
|
|
77
91
|
const frontendManifest = []
|
|
78
92
|
const backendManifest = []
|
|
93
|
+
const flowManifest = []
|
|
79
94
|
const databaseManifest = {}
|
|
80
95
|
for (const entry of mount) {
|
|
81
96
|
if (!entry?.source) continue
|
|
82
97
|
const source = String(entry.source)
|
|
83
98
|
const role = String(entry.role || "")
|
|
99
|
+
if (role === "flow" || source.includes("/flows/")) {
|
|
100
|
+
flowManifest.push({
|
|
101
|
+
source,
|
|
102
|
+
suggestedTarget: entry.target,
|
|
103
|
+
flowName: entry.export_name || basename(source).replace(/\.flow\.ts$/i, ""),
|
|
104
|
+
})
|
|
105
|
+
continue
|
|
106
|
+
}
|
|
84
107
|
if (role === "endpoint" || source.includes("/endpoints/")) {
|
|
85
108
|
backendManifest.push({
|
|
86
109
|
source,
|
|
@@ -128,10 +151,14 @@ function manifestFromModuleContract(contract) {
|
|
|
128
151
|
dependencies,
|
|
129
152
|
frontendManifest,
|
|
130
153
|
backendManifest,
|
|
154
|
+
flowManifest,
|
|
131
155
|
databaseManifest,
|
|
156
|
+
flow_ready: contract.flow_ready === true,
|
|
157
|
+
studio_safe: contract.studio_safe === true,
|
|
158
|
+
install_risk: contract.install_risk || null,
|
|
132
159
|
install: {
|
|
133
160
|
requiresExecuteSql: Boolean(databaseManifest.schema || databaseManifest.seed),
|
|
134
|
-
requiresBackendPublish: backendManifest.length > 0,
|
|
161
|
+
requiresBackendPublish: backendManifest.length > 0 || flowManifest.length > 0,
|
|
135
162
|
},
|
|
136
163
|
}
|
|
137
164
|
}
|
|
@@ -160,24 +187,24 @@ function loadArtifactFromDir(artifactDir) {
|
|
|
160
187
|
return { dir: artifactDir, manifest }
|
|
161
188
|
}
|
|
162
189
|
|
|
163
|
-
async function materializeArtifactFromGithub({ source_repo, source_path, source_ref }) {
|
|
190
|
+
async function materializeArtifactFromGithub({ slug, source_repo, source_path, source_ref }) {
|
|
164
191
|
const repo = String(source_repo || "").trim()
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
192
|
+
const wantedSlug = String(slug || "").trim()
|
|
193
|
+
if (!repo && !wantedSlug) throw new Error("slug is required.")
|
|
168
194
|
const path = String(source_path || "").trim()
|
|
169
195
|
const ref = String(source_ref || "main").trim() || "main"
|
|
170
196
|
|
|
171
|
-
const remote = await proxyToolCall(
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
197
|
+
const remote = await proxyToolCall(
|
|
198
|
+
"fetch_project_artifact",
|
|
199
|
+
repo
|
|
200
|
+
? { source_repo: repo, source_path: path, source_ref: ref }
|
|
201
|
+
: { slug: wantedSlug },
|
|
202
|
+
)
|
|
176
203
|
if (!isObject(remote) || !remote.ok) {
|
|
177
204
|
throw new Error(isObject(remote) && remote.error ? remote.error : "fetch_project_artifact failed")
|
|
178
205
|
}
|
|
179
206
|
|
|
180
|
-
const key = crypto.createHash("sha256").update(`${repo}|${path}|${ref}`).digest("hex")
|
|
207
|
+
const key = crypto.createHash("sha256").update(`${repo || wantedSlug}|${path}|${ref}`).digest("hex")
|
|
181
208
|
const artifactDir = join(tmpdir(), "dypai-artifact-fetch", key)
|
|
182
209
|
rmSync(artifactDir, { recursive: true, force: true })
|
|
183
210
|
mkdirSync(artifactDir, { recursive: true })
|
|
@@ -232,29 +259,27 @@ async function lookupArtifactGithubSource(slug) {
|
|
|
232
259
|
}
|
|
233
260
|
|
|
234
261
|
async function searchProjectArtifacts(args = {}) {
|
|
235
|
-
const
|
|
236
|
-
const requestedLimit = Number(args.limit || 20)
|
|
237
|
-
const proxyLimit = studioSafe || args.frontend_only ? Math.max(requestedLimit, 50) : requestedLimit
|
|
262
|
+
const requestedLimit = Number(args.limit || 3)
|
|
238
263
|
const remoteArgs = {
|
|
239
264
|
...args,
|
|
240
|
-
limit: Math.min(Math.max(
|
|
265
|
+
limit: Math.min(Math.max(requestedLimit, 1), 5),
|
|
241
266
|
}
|
|
242
267
|
delete remoteArgs.frontend_only
|
|
243
268
|
|
|
244
269
|
const raw = await proxyToolCall("search_project_artifacts", remoteArgs)
|
|
245
270
|
if (!isObject(raw) || raw.ok === false) return raw
|
|
246
|
-
|
|
247
|
-
return filterStudioSafeSearchResult(raw, requestedLimit)
|
|
271
|
+
return filterArtifactSearchResult(raw, requestedLimit)
|
|
248
272
|
}
|
|
249
273
|
|
|
250
274
|
async function resolveArtifactInstallation({ slug, source_repo, source_path, source_ref }) {
|
|
251
275
|
const resolved = String(source_repo || "").trim()
|
|
252
276
|
? {
|
|
277
|
+
slug: String(slug || "").trim(),
|
|
253
278
|
source_repo: String(source_repo).trim(),
|
|
254
279
|
source_path: String(source_path || "").trim(),
|
|
255
280
|
source_ref: String(source_ref || "main").trim() || "main",
|
|
256
281
|
}
|
|
257
|
-
:
|
|
282
|
+
: { slug: String(slug || "").trim() }
|
|
258
283
|
return materializeArtifactFromGithub(resolved)
|
|
259
284
|
}
|
|
260
285
|
|
|
@@ -362,6 +387,7 @@ function resolveArtifactAssetPath(artifactDir, source) {
|
|
|
362
387
|
|
|
363
388
|
function defaultTargetForSource(manifest, source, kind) {
|
|
364
389
|
if (kind === "frontend") return `src/dypai-kits/${manifest.slug}/${stripFrontendPrefix(source)}`
|
|
390
|
+
if (kind === "flow") return `dypai/flows/${source.split("/").pop()}`
|
|
365
391
|
if (kind === "backend") return `dypai/endpoints/${manifest.slug}/${source.split("/").pop()}`
|
|
366
392
|
if (kind === "database") return `dypai/kit-installations/${manifest.slug}/${manifest.version}/${source.split("/").pop()}`
|
|
367
393
|
return `.dypai/kits/${manifest.slug}/${source.split("/").pop()}`
|
|
@@ -525,11 +551,17 @@ async function applyProjectArtifact({
|
|
|
525
551
|
|
|
526
552
|
const installFrontend = install.frontend !== false
|
|
527
553
|
const studioSafe = isStudioArtifactProfile()
|
|
528
|
-
const
|
|
554
|
+
const hasLegacyBackendAssets = (manifest.backendManifest || []).length > 0
|
|
555
|
+
const hasFlowAssets = (manifest.flowManifest || []).length > 0
|
|
529
556
|
const hasDatabaseAssets = Boolean(manifest.databaseManifest?.schema || manifest.databaseManifest?.seed)
|
|
530
|
-
if (studioSafe && (
|
|
557
|
+
if (studioSafe && (hasLegacyBackendAssets || hasDatabaseAssets)) {
|
|
531
558
|
throw new Error(
|
|
532
|
-
`Artifact "${manifest.slug}" includes backend
|
|
559
|
+
`Artifact "${manifest.slug}" includes legacy YAML backend or database assets. Studio can only apply frontend/UI artifacts until backend is Flow-only.`,
|
|
560
|
+
)
|
|
561
|
+
}
|
|
562
|
+
if (studioSafe && hasFlowAssets && manifest.studio_safe !== true) {
|
|
563
|
+
throw new Error(
|
|
564
|
+
`Artifact "${manifest.slug}" has Flow backend but is not marked studio_safe. Complete Flow migration and set studio_safe in module.contract.json.`,
|
|
533
565
|
)
|
|
534
566
|
}
|
|
535
567
|
if (studioSafe && install.backend === true) {
|
|
@@ -545,6 +577,7 @@ async function applyProjectArtifact({
|
|
|
545
577
|
throw new Error(`Artifact "${manifest.slug}" has no frontend files to install in Studio.`)
|
|
546
578
|
}
|
|
547
579
|
const installBackend = studioSafe ? false : install.backend !== false
|
|
580
|
+
const installFlows = studioSafe ? false : install.flows !== false && install.backend !== false
|
|
548
581
|
const installDatabase = studioSafe ? false : install.database !== false
|
|
549
582
|
|
|
550
583
|
if (installFrontend) {
|
|
@@ -579,6 +612,19 @@ async function applyProjectArtifact({
|
|
|
579
612
|
}
|
|
580
613
|
}
|
|
581
614
|
|
|
615
|
+
if (installFlows) {
|
|
616
|
+
for (const asset of manifest.flowManifest || []) {
|
|
617
|
+
const sourceAbs = resolveArtifactAssetPath(dir, asset.source)
|
|
618
|
+
let targetRel = asset.suggestedTarget || defaultTargetForSource(manifest, asset.source, "flow")
|
|
619
|
+
if (target.flowDir) {
|
|
620
|
+
targetRel = join(target.flowDir, basename(asset.source)).split(sep).join("/")
|
|
621
|
+
}
|
|
622
|
+
if (writeTemplateFile({ sourceAbs, targetRel, workspaceRoot, manifest, naming, kind: "frontend", overwrite, record, copied, skipped })) {
|
|
623
|
+
copiedByKind.backend.push(targetRel)
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
582
628
|
if (installDatabase && isObject(manifest.databaseManifest)) {
|
|
583
629
|
for (const key of ["schema", "seed"]) {
|
|
584
630
|
const source = manifest.databaseManifest[key]
|
|
@@ -604,9 +650,14 @@ async function applyProjectArtifact({
|
|
|
604
650
|
return true
|
|
605
651
|
})
|
|
606
652
|
|
|
607
|
-
const backendEndpoints =
|
|
608
|
-
|
|
609
|
-
|
|
653
|
+
const backendEndpoints = [
|
|
654
|
+
...(manifest.backendManifest || []).map((asset) =>
|
|
655
|
+
adaptTarget(asset.endpointName || asset.source.split("/").pop().replace(/\.ya?ml$/, ""), manifest, naming),
|
|
656
|
+
),
|
|
657
|
+
...(manifest.flowManifest || []).map((asset) =>
|
|
658
|
+
adaptTarget(asset.flowName || asset.source.split("/").pop().replace(/\.flow\.ts$/, ""), manifest, naming),
|
|
659
|
+
),
|
|
660
|
+
]
|
|
610
661
|
const databaseTables = normalizeList(manifest.databaseManifest?.tables).map((table) =>
|
|
611
662
|
adaptTarget(table, manifest, naming),
|
|
612
663
|
)
|