flowmind 1.5.1 → 1.5.3

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/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.5.2] - 2026-07-01
6
+
7
+ ### Added
8
+ - `sdd-agent-sync` now imports workflow resources from local `auto-flow` learning data into FlowMind
9
+ - Workflow sync now writes a local `workflow` resource, a `workflow` component config, and a reusable `workflow` binding for `friday-auto-flow`
10
+
11
+ ### Changed
12
+ - `flowmind resource --sync-sdd-agent` can now seed the workflow MCP path automatically, so `auto-flow` no longer depends on a manual local binding
13
+
5
14
  ## [1.5.1] - 2026-07-01
6
15
 
7
16
  ### Added
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  const BaseAdapter = require('./base-adapter');
7
+ const { callMcpTool } = require('../mcp-http-client');
7
8
 
8
9
  class McpAdapter extends BaseAdapter {
9
10
  constructor(providerName, config = {}) {
@@ -60,6 +61,30 @@ class McpAdapter extends BaseAdapter {
60
61
  tools: this.getToolMappings()
61
62
  };
62
63
  }
64
+
65
+ getTransportConfig() {
66
+ return this.config.transport || this.config.mcpServerConfig || this.config.mcpTransport || {};
67
+ }
68
+
69
+ async callMcpTool(localName, args = {}) {
70
+ const tool = this.resolveTool(localName);
71
+ if (!tool) {
72
+ throw new Error(`Unknown MCP tool mapping: ${localName}`);
73
+ }
74
+
75
+ const transport = this.getTransportConfig();
76
+ const serverName = this.mcpServer || this.providerName;
77
+ const response = await callMcpTool({
78
+ url: transport.url || transport.endpoint || transport.baseUrl,
79
+ headers: transport.headers || {},
80
+ tool,
81
+ args,
82
+ timeoutMs: transport.timeoutMs || 60000,
83
+ serverName
84
+ });
85
+
86
+ return response;
87
+ }
63
88
  }
64
89
 
65
90
  module.exports = McpAdapter;
@@ -29,6 +29,32 @@ class WorkflowAdapter extends McpAdapter {
29
29
  throw new Error('Subclasses must implement listPipelines()');
30
30
  }
31
31
 
32
+ /**
33
+ * Query the current iterate metadata used by workflow lookup flows.
34
+ * @returns {Promise<object>}
35
+ */
36
+ async getCurrentIterate() {
37
+ throw new Error('Subclasses must implement getCurrentIterate()');
38
+ }
39
+
40
+ /**
41
+ * List deploy checklist records that contain service-to-pipeline mappings.
42
+ * @param {object} params
43
+ * @returns {Promise<object>}
44
+ */
45
+ async listDeployChecklists(params) {
46
+ throw new Error('Subclasses must implement listDeployChecklists()');
47
+ }
48
+
49
+ /**
50
+ * Query historical flow orders that can be used as a fallback pipeline source.
51
+ * @param {object} params
52
+ * @returns {Promise<object>}
53
+ */
54
+ async orderList(params) {
55
+ throw new Error('Subclasses must implement orderList()');
56
+ }
57
+
32
58
  /**
33
59
  * Start a pipeline run.
34
60
  * @param {string} pipelineId
@@ -41,6 +41,10 @@ class ComponentRegistry {
41
41
  const AliyunDmsAdapter = require('./providers/aliyun/dms-adapter');
42
42
  return new AliyunDmsAdapter(this.getProviderConfig('databaseManager', 'aliyun-dms'));
43
43
  });
44
+ this.registerFactory('aliyun-rds-query', () => {
45
+ const AliyunRdsQueryAdapter = require('./providers/aliyun/rds-query-adapter');
46
+ return new AliyunRdsQueryAdapter(this.getProviderConfig('databaseQuery', 'aliyun-rds-query'));
47
+ });
44
48
  this.registerFactory('aliyun-redis', () => {
45
49
  const AliyunRedisAdapter = require('./providers/aliyun/redis-adapter');
46
50
  return new AliyunRedisAdapter(this.getProviderConfig('redisMonitor', 'aliyun-redis'));
@@ -79,7 +83,21 @@ class ComponentRegistry {
79
83
  const components = this.config.get('components', {});
80
84
  const typeConfig = components[componentType] || {};
81
85
  const providers = typeConfig.providers || {};
82
- return providers[providerName] || {};
86
+ const baseConfig = providers[providerName] || {};
87
+ const mcpServerName = McpCompatibility.getMcpServer(componentType, providerName);
88
+ const mcpServers = this.config.get('mcpServers', {});
89
+ const transport = mcpServerName ? mcpServers[mcpServerName] : null;
90
+
91
+ if (!transport) {
92
+ return baseConfig;
93
+ }
94
+
95
+ return {
96
+ ...baseConfig,
97
+ transport: {
98
+ ...transport
99
+ }
100
+ };
83
101
  }
84
102
 
85
103
  /**
@@ -82,7 +82,10 @@ class ConfigManager {
82
82
  if (await fs.pathExists(resourceConfigPath)) {
83
83
  try {
84
84
  const resourceConfig = await fs.readJson(resourceConfigPath);
85
- this.mergeConfig({ resources: resourceConfig.resources || {} });
85
+ this.mergeConfig({
86
+ resources: resourceConfig.resources || {},
87
+ mcpServers: resourceConfig.mcpServers || {}
88
+ });
86
89
  } catch (error) {
87
90
  console.warn('Failed to load resource config:', error.message);
88
91
  }
@@ -216,7 +219,9 @@ class ConfigManager {
216
219
  redis: { enabled: false },
217
220
  logs: { enabled: false },
218
221
  apiDocs: { enabled: false }
219
- }
222
+ },
223
+
224
+ mcpServers: {}
220
225
  };
221
226
  }
222
227
 
package/core/index.js CHANGED
@@ -11,6 +11,8 @@ const ConfigManager = require('./config-manager');
11
11
  const ComponentRegistry = require('./component-registry');
12
12
  const ModelManager = require('./ai/model-manager');
13
13
  const eventBus = require('./event-bus');
14
+ const { autoSyncSddAgentToFlowMind } = require('./sdd-agent-sync');
15
+ const { inferSourceContext } = require('./source-inference');
14
16
 
15
17
  class FlowMind {
16
18
  constructor(options = {}) {
@@ -32,6 +34,7 @@ class FlowMind {
32
34
  async init() {
33
35
  if (this.initialized) return this;
34
36
 
37
+ await autoSyncSddAgentToFlowMind();
35
38
  await this.config.load();
36
39
 
37
40
  // Validate configuration
@@ -191,15 +194,25 @@ class FlowMind {
191
194
  // Get learning rules for this skill
192
195
  const learnings = await this.learning.getSkillLearnings(skill.name);
193
196
  const resourceBinding = await this.learning.matchResourceBinding(input, skill.name);
197
+ let sourceInference = context.sourceInference || null;
198
+ if (!sourceInference) {
199
+ try {
200
+ sourceInference = await inferSourceContext(input);
201
+ } catch (error) {
202
+ sourceInference = null;
203
+ }
204
+ }
194
205
 
195
206
  // Apply learning rules to context
196
207
  const enhancedContext = {
197
208
  ...context,
198
209
  flowmind: context.flowmind || this,
210
+ componentRegistry: context.componentRegistry || this.components,
199
211
  currentSkill: context.currentSkill || skill.name,
200
212
  learnings: learnings,
201
213
  preferences: await this.learning.getPreferences(skill.name),
202
- resourceBinding
214
+ resourceBinding,
215
+ sourceInference
203
216
  };
204
217
 
205
218
  // Execute skill
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Minimal JSON-RPC MCP HTTP client.
3
+ * Sends tools/call requests to an MCP gateway over HTTP.
4
+ */
5
+
6
+ async function callMcpTool({ url, headers = {}, tool, args = {}, timeoutMs = 60000, serverName = 'mcp' }) {
7
+ if (!url) {
8
+ throw new Error(`MCP transport URL not configured for ${serverName}`);
9
+ }
10
+
11
+ const requestId = Date.now();
12
+ const controller = new AbortController();
13
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
14
+
15
+ try {
16
+ const response = await fetch(url, {
17
+ method: 'POST',
18
+ headers: {
19
+ 'Content-Type': 'application/json',
20
+ 'User-Agent': 'flowmind',
21
+ ...headers
22
+ },
23
+ body: JSON.stringify({
24
+ jsonrpc: '2.0',
25
+ id: requestId,
26
+ method: 'tools/call',
27
+ params: {
28
+ name: tool,
29
+ arguments: args
30
+ }
31
+ }),
32
+ signal: controller.signal
33
+ });
34
+
35
+ const text = await response.text();
36
+ let payload = null;
37
+
38
+ if (text) {
39
+ try {
40
+ payload = JSON.parse(text);
41
+ } catch (parseError) {
42
+ throw new Error(`Invalid MCP response from ${serverName}: ${text.slice(0, 200)}`);
43
+ }
44
+ }
45
+
46
+ if (!response.ok) {
47
+ const errorMessage = payload?.error?.message || payload?.message || response.statusText;
48
+ throw new Error(`MCP request failed for ${serverName}: ${errorMessage}`);
49
+ }
50
+
51
+ if (payload?.error) {
52
+ throw new Error(payload.error.message || `MCP tool call failed for ${serverName}`);
53
+ }
54
+
55
+ return payload?.result ?? payload ?? null;
56
+ } finally {
57
+ clearTimeout(timeout);
58
+ }
59
+ }
60
+
61
+ module.exports = {
62
+ callMcpTool
63
+ };
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Aliyun RDS Direct Query Adapter
3
+ * Wraps the friday-rds-redis-query MCP server for SQL and Redis reads.
4
+ */
5
+
6
+ const DatabaseQueryAdapter = require('../../adapters/database-query-adapter');
7
+
8
+ class AliyunRdsQueryAdapter extends DatabaseQueryAdapter {
9
+ constructor(config = {}) {
10
+ super('aliyun-rds-query', config);
11
+
12
+ this.registerTool('fetchSources', 'mcpFetchSource');
13
+ this.registerTool('fetchDatabases', 'mcpFetchBase');
14
+ this.registerTool('fetchTables', 'mcpFetchTables');
15
+ this.registerTool('fetchFields', 'mcpFetchFields');
16
+ this.registerTool('queryExec', 'mcpQueryExec');
17
+ this.registerTool('redisSources', 'mcpRedisSource');
18
+ this.registerTool('redisKeys', 'mcpRedisKeys');
19
+ this.registerTool('redisKeyGet', 'mcpRedisKeyGet');
20
+ this.registerTool('redisScan', 'mcpRedisScan');
21
+ }
22
+
23
+ get mcpServer() {
24
+ return 'friday-rds-redis-query';
25
+ }
26
+
27
+ async fetchSources(params) {
28
+ return this.callMcpTool('fetchSources', params || {});
29
+ }
30
+
31
+ async fetchDatabases(sourceIdOrParams) {
32
+ const params = typeof sourceIdOrParams === 'object'
33
+ ? sourceIdOrParams
34
+ : { source_id: sourceIdOrParams };
35
+ return this.callMcpTool('fetchDatabases', params || {});
36
+ }
37
+
38
+ async fetchTables(sourceIdOrParams, schema) {
39
+ const params = typeof sourceIdOrParams === 'object'
40
+ ? sourceIdOrParams
41
+ : { source_id: sourceIdOrParams, schema };
42
+ return this.callMcpTool('fetchTables', params || {});
43
+ }
44
+
45
+ async fetchFields(params) {
46
+ return this.callMcpTool('fetchFields', params || {});
47
+ }
48
+
49
+ async queryExec(params) {
50
+ return this.callMcpTool('queryExec', params || {});
51
+ }
52
+
53
+ async redisSources(params) {
54
+ return this.callMcpTool('redisSources', params || {});
55
+ }
56
+
57
+ async redisKeys(params) {
58
+ return this.callMcpTool('redisKeys', params || {});
59
+ }
60
+
61
+ async redisKeyGet(params) {
62
+ return this.callMcpTool('redisKeyGet', params || {});
63
+ }
64
+
65
+ async redisScan(params) {
66
+ return this.callMcpTool('redisScan', params || {});
67
+ }
68
+ }
69
+
70
+ module.exports = AliyunRdsQueryAdapter;
@@ -26,6 +26,7 @@ class FridayFlowAdapter extends WorkflowAdapter {
26
26
  this.registerTool('logPipelineJobRun', 'flowLogPipelineJobRun');
27
27
  this.registerTool('passPipelineValidate', 'flowPassPipelineValidate');
28
28
  this.registerTool('refusePipelineValidate', 'flowRefusePipelineValidate');
29
+ this.registerTool('listDeployChecklists', 'listDeployChecklists');
29
30
 
30
31
  // Order/task tools
31
32
  this.registerTool('orderList', 'orderList');
@@ -42,51 +43,39 @@ class FridayFlowAdapter extends WorkflowAdapter {
42
43
  }
43
44
 
44
45
  async listPipelineGroups(params) {
45
- return {
46
- mcpServer: this.mcpServer,
47
- tool: this.resolveTool('listPipelineGroups'),
48
- params: params || { perPage: '20', page: '1' }
49
- };
46
+ return this.callMcpTool('listPipelineGroups', params || { perPage: '20', page: '1' });
50
47
  }
51
48
 
52
49
  async listPipelines(params) {
53
- return {
54
- mcpServer: this.mcpServer,
55
- tool: this.resolveTool('listPipelines'),
56
- params: params || {}
57
- };
50
+ return this.callMcpTool('listPipelines', params || {});
51
+ }
52
+
53
+ async getCurrentIterate() {
54
+ return this.callMcpTool('getCurrentIterate', {});
55
+ }
56
+
57
+ async listDeployChecklists(params) {
58
+ return this.callMcpTool('listDeployChecklists', params || {});
59
+ }
60
+
61
+ async orderList(params) {
62
+ return this.callMcpTool('orderList', params || {});
58
63
  }
59
64
 
60
65
  async startPipelineRun(pipelineId) {
61
- return {
62
- mcpServer: this.mcpServer,
63
- tool: this.resolveTool('startPipelineRun'),
64
- params: { pipelineId }
65
- };
66
+ return this.callMcpTool('startPipelineRun', { pipelineId });
66
67
  }
67
68
 
68
69
  async startBatchPipelineRun(params) {
69
- return {
70
- mcpServer: this.mcpServer,
71
- tool: this.resolveTool('startBatchPipelineRun'),
72
- params: params || {}
73
- };
70
+ return this.callMcpTool('startBatchPipelineRun', params || {});
74
71
  }
75
72
 
76
73
  async getPipelineRun(pipelineId, runId) {
77
- return {
78
- mcpServer: this.mcpServer,
79
- tool: this.resolveTool('getPipelineRun'),
80
- params: { pipelineId, pipelineRunId: runId }
81
- };
74
+ return this.callMcpTool('getPipelineRun', { pipelineId, pipelineRunId: runId });
82
75
  }
83
76
 
84
77
  async listPipelineRuns(pipelineId, params) {
85
- return {
86
- mcpServer: this.mcpServer,
87
- tool: this.resolveTool('listPipelineRuns'),
88
- params: { pipelineId, ...(params || {}) }
89
- };
78
+ return this.callMcpTool('listPipelineRuns', { pipelineId, ...(params || {}) });
90
79
  }
91
80
  }
92
81