@dypai-ai/mcp 1.0.9 → 1.1.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.
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Bidirectional transformations between the DB's executable JSON workflow
3
+ * format and the declarative YAML/files format.
4
+ *
5
+ * CRITICAL: every transform declares its pull (DB → file) and push (file → DB)
6
+ * direction together. This guarantees round-trip symmetry by construction —
7
+ * there's no separate "push" file that can drift out of sync with "pull".
8
+ *
9
+ * Adding a new transform = edit one object here, both directions in one commit.
10
+ *
11
+ * Context shape for pull:
12
+ * {
13
+ * endpointName, nodeId,
14
+ * credIdToName, groupIdToName, endpointIdToName,
15
+ * emitFile(path, content), // collects files to write to disk
16
+ * }
17
+ *
18
+ * Context shape for push:
19
+ * {
20
+ * endpointName, nodeId,
21
+ * credNameToId, groupNameToId, endpointNameToId,
22
+ * readFile(path), // returns file contents
23
+ * }
24
+ */
25
+
26
+ // Only extract truly large content. Below these thresholds, SQL and prompts
27
+ // stay inline in the YAML so the endpoint is one self-contained file.
28
+ const SQL_INLINE_MAX_CHARS = 500
29
+ const PROMPT_INLINE_MAX_CHARS = 800
30
+
31
+ const shouldInlineSql = (q) => !q || q.length <= SQL_INLINE_MAX_CHARS
32
+
33
+ // ─── Node-level field transforms ────────────────────────────────────────────
34
+
35
+ export const NODE_FIELD_TRANSFORMS = [
36
+ {
37
+ name: "credential",
38
+ pull(params, ctx) {
39
+ if (params.credential_id && ctx.credIdToName[params.credential_id]) {
40
+ return { credential: ctx.credIdToName[params.credential_id] }
41
+ }
42
+ return {}
43
+ },
44
+ push(params, ctx) {
45
+ if (params.credential && ctx.credNameToId[params.credential]) {
46
+ return { credential_id: ctx.credNameToId[params.credential] }
47
+ }
48
+ return {}
49
+ },
50
+ pullConsumes: ["credential_id"],
51
+ pushConsumes: ["credential"],
52
+ },
53
+
54
+ {
55
+ name: "agent_tools",
56
+ appliesWhen: (nodeType) => nodeType === "agent",
57
+ pull(params, ctx) {
58
+ if (Array.isArray(params.tool_ids)) {
59
+ return { tools: params.tool_ids.map(id => ctx.endpointIdToName[id] || id) }
60
+ }
61
+ return {}
62
+ },
63
+ push(params, ctx) {
64
+ if (Array.isArray(params.tools)) {
65
+ return { tool_ids: params.tools.map(n => ctx.endpointNameToId[n] || n) }
66
+ }
67
+ return {}
68
+ },
69
+ pullConsumes: ["tool_ids"],
70
+ pushConsumes: ["tools"],
71
+ },
72
+
73
+ {
74
+ name: "sql_extraction",
75
+ appliesWhen: (nodeType) => nodeType === "dypai_database",
76
+ pull(params, ctx) {
77
+ if (params.query && !shouldInlineSql(params.query)) {
78
+ // Flat layout: sql/<endpoint>.sql if only one SQL node, else sql/<endpoint>.<node>.sql
79
+ const suffix = ctx.sqlNodeCount > 1 ? `.${ctx.nodeId}` : ""
80
+ const path = `sql/${ctx.endpointName}${suffix}.sql`
81
+ ctx.emitFile(path, params.query.trim() + "\n")
82
+ return { query_file: path }
83
+ }
84
+ return {}
85
+ },
86
+ push(params, ctx) {
87
+ if (params.query_file) {
88
+ return { query: ctx.readFile(params.query_file).trimEnd() }
89
+ }
90
+ return {}
91
+ },
92
+ pullConsumes: ["query"],
93
+ pushConsumes: ["query_file"],
94
+ },
95
+
96
+ {
97
+ name: "prompt_extraction",
98
+ appliesWhen: (nodeType) => nodeType === "agent",
99
+ pull(params, ctx) {
100
+ if (params.system_prompt && params.system_prompt.length > PROMPT_INLINE_MAX_CHARS) {
101
+ const suffix = ctx.promptNodeCount > 1 ? `.${ctx.nodeId}` : ""
102
+ const path = `prompts/${ctx.endpointName}${suffix}.md`
103
+ ctx.emitFile(path, params.system_prompt.trim() + "\n")
104
+ return { system_prompt_file: path }
105
+ }
106
+ return {}
107
+ },
108
+ push(params, ctx) {
109
+ if (params.system_prompt_file) {
110
+ return { system_prompt: ctx.readFile(params.system_prompt_file).trimEnd() }
111
+ }
112
+ return {}
113
+ },
114
+ pullConsumes: ["system_prompt"],
115
+ pushConsumes: ["system_prompt_file"],
116
+ },
117
+
118
+ {
119
+ name: "code_extraction",
120
+ appliesWhen: (nodeType) => nodeType === "javascript_code" || nodeType === "python_code",
121
+ pull(params, ctx) {
122
+ if (params.code && params.code.length > SQL_INLINE_MAX_CHARS) {
123
+ const ext = ctx.nodeType === "python_code" ? "py" : "js"
124
+ const suffix = ctx.codeNodeCount > 1 ? `.${ctx.nodeId}` : ""
125
+ const path = `code/${ctx.endpointName}${suffix}.${ext}`
126
+ ctx.emitFile(path, params.code.trim() + "\n")
127
+ return { code_file: path }
128
+ }
129
+ return {}
130
+ },
131
+ push(params, ctx) {
132
+ if (params.code_file) {
133
+ return { code: ctx.readFile(params.code_file).trimEnd() }
134
+ }
135
+ return {}
136
+ },
137
+ pullConsumes: ["code"],
138
+ pushConsumes: ["code_file"],
139
+ },
140
+ ]
141
+
142
+ // ─── Engine ────────────────────────────────────────────────────────────────
143
+
144
+ function applyTransforms(input, direction, ctx, nodeType) {
145
+ let result = { ...input }
146
+ for (const t of NODE_FIELD_TRANSFORMS) {
147
+ if (t.appliesWhen && !t.appliesWhen(nodeType)) continue
148
+ const added = t[direction](result, ctx) || {}
149
+ const consumes = direction === "pull" ? t.pullConsumes : t.pushConsumes
150
+ for (const f of consumes || []) delete result[f]
151
+ Object.assign(result, added)
152
+ }
153
+ return result
154
+ }
155
+
156
+ export const pullNodeParams = (params, ctx, nodeType) => applyTransforms(params, "pull", ctx, nodeType)
157
+ export const pushNodeParams = (params, ctx, nodeType) => applyTransforms(params, "push", ctx, nodeType)