@creately/rdm-mcp 0.2.1 → 0.3.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/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@creately/rdm-mcp",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "MCP server for RDM — parse, validate, render, and manipulate diagrams via AI agents",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
7
7
  "bin": {
8
- "rdm-mcp": "src/index.ts"
8
+ "rdm-mcp": "src/cli.js"
9
9
  },
10
10
  "scripts": {
11
11
  "start": "bun run src/index.ts",
@@ -13,10 +13,7 @@
13
13
  },
14
14
  "dependencies": {
15
15
  "@modelcontextprotocol/sdk": "^1.12.1",
16
- "@creately/rdm-core": ">=0.3.0"
17
- },
18
- "publishConfig": {
19
- "access": "public"
16
+ "@creately/rdm-core": ">=0.4.0"
20
17
  },
21
18
  "license": "MIT",
22
19
  "repository": {
package/src/cli.js ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * rdm-mcp CLI wrapper — delegates to bun for TypeScript execution.
5
+ */
6
+
7
+ import { execFileSync } from 'node:child_process';
8
+ import { join, dirname } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ const impl = join(__dirname, 'index.ts');
13
+
14
+ try {
15
+ execFileSync('bun', ['run', impl, ...process.argv.slice(2)], {
16
+ stdio: 'inherit',
17
+ env: process.env,
18
+ });
19
+ } catch (e) {
20
+ if (e.status) process.exit(e.status);
21
+ console.error('rdm-mcp requires bun. Install it: curl -fsSL https://bun.sh/install | bash');
22
+ process.exit(1);
23
+ }
package/src/index.ts CHANGED
@@ -62,7 +62,7 @@ const TOOL_EXECUTORS: Record<string, (args: Record<string, unknown>) => Promise<
62
62
 
63
63
  async function main() {
64
64
  const server = new Server(
65
- { name: 'rdm-mcp', version: '0.2.0' },
65
+ { name: 'rdm-mcp', version: '0.3.0' },
66
66
  { capabilities: { tools: {} } }
67
67
  );
68
68
 
@@ -104,20 +104,22 @@ const SCHEMAS: Record<string, DomainSchema> = {
104
104
  },
105
105
  flowchart: {
106
106
  domain: 'flowchart',
107
- description: 'Simple flowchart diagrams with steps, decisions, and flows.',
107
+ description: 'Simple flowcharts. NOTE: flowcharts use the same syntax as process diagrams. Use frontmatter type: process and block keyword: process. There is NO flowchart block keyword.',
108
108
  nodeTypes: {
109
- step: {
109
+ node: {
110
110
  fields: [
111
- { name: 'type', type: 'enum', values: ['process', 'decision', 'start', 'end', 'subprocess', 'document', 'database'] },
111
+ { name: 'type', type: 'enum', values: ['task', 'gateway', 'start-event', 'end-event'], required: true, description: 'Node role in the flow' },
112
+ { name: 'label', type: 'string', description: 'Display label' },
113
+ { name: 'assignee', type: 'string', description: 'Role or person responsible' },
112
114
  ],
113
115
  },
114
116
  },
115
117
  relationships: [
116
- { syntax: 'flow source -> target', description: 'Sequential flow' },
117
- { syntax: 'flow source -> target { label: "Yes" }', description: 'Labeled flow' },
118
+ { syntax: 'source -> target', description: 'Sequential flow' },
119
+ { syntax: 'source -> target { label: "Yes" }', description: 'Labeled flow (for gateways)' },
118
120
  ],
119
- structure: 'flowchart Name {\n start begin "Start"\n step id "Label" { type: process }\n end done "End"\n flow begin -> id\n flow id -> done\n}',
120
- example: '---\ntype: flowchart\ntitle: "Approval"\n---\n\nflowchart Approval {\n start begin "Start"\n step submit "Submit Request" { type: process }\n step check "Approved?" { type: decision }\n end done "Done"\n\n flow begin -> submit\n flow submit -> check\n flow check -> done { label: "Yes" }\n}',
121
+ structure: 'process Name {\n begin { type: "start-event", label: "Start" }\n step1 { type: "task", label: "Do Something" }\n done { type: "end-event", label: "End" }\n begin -> step1\n step1 -> done\n}',
122
+ example: '---\ntype: process\ntitle: "Simple Flow"\n---\n\nprocess SimpleFlow {\n begin { type: "start-event", label: "Start" }\n submit { type: "task", label: "Submit", assignee: "User" }\n check { type: "exclusive-gateway", label: "OK?" }\n done { type: "end-event", label: "End" }\n\n begin -> submit\n submit -> check\n check -> done { label: "Yes" }\n}',
121
123
  },
122
124
  table: {
123
125
  domain: 'table',
@@ -1,7 +1,10 @@
1
1
  /**
2
- * rdm_validate — Validate RDM text and return diagnostics
2
+ * rdm_validate — Validate RDM text with auto-fix.
3
+ *
4
+ * Auto-corrects common agent mistakes (wrong block keywords, invalid syntax)
5
+ * before parsing. Returns both the validation result and the corrected text.
3
6
  */
4
- import { readFileSync, existsSync } from 'fs';
7
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
5
8
  import { resolve } from 'path';
6
9
  import { RdmService } from '@creately/rdm-core';
7
10
 
@@ -10,8 +13,9 @@ const rdmService = new RdmService();
10
13
  export const rdmValidateTool = {
11
14
  name: 'rdm_validate',
12
15
  description:
13
- 'Validate an .rdm file or RDM text. Returns parse errors and warnings ' +
14
- 'with line numbers. Use this to check correctness before applying changes.',
16
+ 'Validate an .rdm file or RDM text. Auto-fixes common mistakes (wrong block keywords, ' +
17
+ 'invalid syntax) and returns the corrected text along with any remaining errors. ' +
18
+ 'If autoFix is true (default), writes the fixed text back to the file.',
15
19
  inputSchema: {
16
20
  type: 'object' as const,
17
21
  properties: {
@@ -23,17 +27,22 @@ export const rdmValidateTool = {
23
27
  type: 'string',
24
28
  description: 'RDM text to validate directly (alternative to path)',
25
29
  },
30
+ autoFix: {
31
+ type: 'boolean',
32
+ description: 'If true (default), auto-fix mistakes and write corrected file back. Set to false for dry-run.',
33
+ },
26
34
  },
27
35
  },
28
36
  };
29
37
 
30
38
  export async function executeRdmValidate(args: Record<string, unknown>): Promise<unknown> {
31
39
  let rdmText: string;
40
+ const filePath = args.path ? resolve(String(args.path)) : null;
41
+ const autoFix = args.autoFix !== false; // default true
32
42
 
33
43
  if (args.rdmText) {
34
44
  rdmText = String(args.rdmText);
35
- } else if (args.path) {
36
- const filePath = resolve(String(args.path));
45
+ } else if (filePath) {
37
46
  if (!existsSync(filePath)) {
38
47
  return { error: `File not found: ${filePath}` };
39
48
  }
@@ -42,10 +51,24 @@ export async function executeRdmValidate(args: Record<string, unknown>): Promise
42
51
  return { error: 'Either path or rdmText must be provided' };
43
52
  }
44
53
 
45
- const result = rdmService.parseAndValidate(rdmText);
54
+ const result = rdmService.parseAndFix(rdmText);
55
+
56
+ // Auto-fix: write corrected text back to file
57
+ if (autoFix && result.wasFixed && filePath) {
58
+ writeFileSync(filePath, result.fixedText, 'utf-8');
59
+ }
46
60
 
47
61
  return {
48
62
  valid: result.success,
63
+ wasFixed: result.wasFixed,
64
+ fixedText: result.wasFixed ? result.fixedText : undefined,
65
+ fixes: result.fixes.map((f) => ({
66
+ line: f.line,
67
+ rule: f.rule,
68
+ description: f.description,
69
+ before: f.before,
70
+ after: f.after,
71
+ })),
49
72
  errors: result.errors.map((e) => ({
50
73
  code: e.code,
51
74
  message: e.message,
@@ -68,32 +68,39 @@ export async function executeRdmWrite(args: Record<string, unknown>): Promise<un
68
68
  rdmText = existing.slice(0, lastBrace) + '\n ' + fragment!.trim() + '\n' + existing.slice(lastBrace);
69
69
  }
70
70
 
71
- // Validate before writing
72
- const validation = rdmService.parseAndValidate(rdmText);
73
- if (!validation.success) {
71
+ // Auto-lint and validate before writing
72
+ const result = rdmService.parseAndFix(rdmText);
73
+ if (!result.success) {
74
74
  return {
75
75
  error: 'Validation failed — not written',
76
- errors: validation.errors,
76
+ errors: result.errors,
77
+ fixes: result.fixes,
78
+ fixedText: result.wasFixed ? result.fixedText : undefined,
77
79
  };
78
80
  }
79
81
 
82
+ // Use the fixed text
83
+ const finalText = result.fixedText;
84
+
80
85
  // Ensure directory exists
81
86
  const dir = dirname(filePath);
82
87
  if (!existsSync(dir)) {
83
88
  mkdirSync(dir, { recursive: true });
84
89
  }
85
90
 
86
- writeFileSync(filePath, rdmText, 'utf-8');
91
+ writeFileSync(filePath, finalText, 'utf-8');
87
92
 
88
- const nodeCount = countType(validation.document!.structure.declarations, 'node');
89
- const edgeCount = countType(validation.document!.structure.declarations, 'edge');
93
+ const nodeCount = countType(result.document!.structure.declarations, 'node');
94
+ const edgeCount = countType(result.document!.structure.declarations, 'edge');
90
95
 
91
96
  return {
92
97
  path: filePath,
93
98
  written: true,
99
+ wasFixed: result.wasFixed,
100
+ fixes: result.fixes,
94
101
  nodeCount,
95
102
  edgeCount,
96
- sizeBytes: Buffer.byteLength(rdmText, 'utf-8'),
103
+ sizeBytes: Buffer.byteLength(finalText, 'utf-8'),
97
104
  };
98
105
  }
99
106