mcp-server-sfmc 0.2.1 → 0.4.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 +6 -1
- package/ci-templates/Jenkinsfile +71 -0
- package/ci-templates/README.md +36 -0
- package/ci-templates/azure-pipelines.yml +64 -0
- package/ci-templates/bitbucket-pipelines.yml +57 -0
- package/ci-templates/eslint.config.mjs +25 -0
- package/ci-templates/github-action.yml +109 -0
- package/ci-templates/github-copilot-review-instructions.md +72 -0
- package/ci-templates/gitlab-ci.yml +49 -0
- package/ci-templates/gitlab-duo-review-instructions.md +36 -0
- package/dist/cli/reviewDiff.d.ts +22 -0
- package/dist/cli/reviewDiff.d.ts.map +1 -0
- package/dist/cli/reviewDiff.js +226 -0
- package/dist/cli/reviewDiff.js.map +1 -0
- package/dist/index.js +226 -85
- package/dist/index.js.map +1 -1
- package/dist/mce-help-search.d.ts +3 -0
- package/dist/mce-help-search.d.ts.map +1 -1
- package/dist/mce-help-search.js +3 -0
- package/dist/mce-help-search.js.map +1 -1
- package/package.json +25 -6
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* sfmc-review-diff — CI helper: spawn mcp-server-sfmc and call review_change on a unified diff.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
import { stdin } from 'node:process';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
10
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
11
|
+
/**
|
|
12
|
+
* Counts diagnostic lines emitted by the review_change tool (see src/index.ts).
|
|
13
|
+
* @param output
|
|
14
|
+
*/
|
|
15
|
+
export function countReviewSeverities(output) {
|
|
16
|
+
let errors = 0;
|
|
17
|
+
let warnings = 0;
|
|
18
|
+
let infos = 0;
|
|
19
|
+
for (const line of output.split('\n')) {
|
|
20
|
+
if (line.startsWith('🔴 ERROR'))
|
|
21
|
+
errors += 1;
|
|
22
|
+
else if (line.startsWith('🟡 WARNING'))
|
|
23
|
+
warnings += 1;
|
|
24
|
+
else if (line.startsWith('🔵 INFO'))
|
|
25
|
+
infos += 1;
|
|
26
|
+
}
|
|
27
|
+
return { errors, warnings, infos };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Whether the CLI should exit with code 1 given counts and --fail-on policy.
|
|
31
|
+
* @param counts
|
|
32
|
+
* @param failOn
|
|
33
|
+
*/
|
|
34
|
+
export function shouldFail(counts, failOn) {
|
|
35
|
+
if (counts.errors > 0)
|
|
36
|
+
return true;
|
|
37
|
+
if ((failOn === 'warning' || failOn === 'info') && counts.warnings > 0)
|
|
38
|
+
return true;
|
|
39
|
+
if (failOn === 'info' && counts.infos > 0)
|
|
40
|
+
return true;
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
function toolResultToText(result) {
|
|
44
|
+
const parts = [];
|
|
45
|
+
for (const block of result.content ?? []) {
|
|
46
|
+
if (block.type === 'text' && 'text' in block && typeof block.text === 'string') {
|
|
47
|
+
parts.push(block.text);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return parts.join('\n');
|
|
51
|
+
}
|
|
52
|
+
function readStdin() {
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const chunks = [];
|
|
55
|
+
stdin.on('data', (c) => chunks.push(c));
|
|
56
|
+
stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
|
|
57
|
+
stdin.on('error', reject);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function projectRoot() {
|
|
61
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
62
|
+
// dist/cli -> package root
|
|
63
|
+
return path.join(here, '..', '..');
|
|
64
|
+
}
|
|
65
|
+
function serverEntryPath() {
|
|
66
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
67
|
+
return path.join(here, '..', 'index.js');
|
|
68
|
+
}
|
|
69
|
+
function pkgVersion() {
|
|
70
|
+
try {
|
|
71
|
+
const p = path.join(projectRoot(), 'package.json');
|
|
72
|
+
const j = JSON.parse(readFileSync(p, 'utf8'));
|
|
73
|
+
return j.version ?? '0.0.0';
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return '0.0.0';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function printHelp() {
|
|
80
|
+
console.log(`sfmc-review-diff — run MCP review_change on a unified diff (stdin or file).
|
|
81
|
+
|
|
82
|
+
Usage:
|
|
83
|
+
sfmc-review-diff [options] [file]
|
|
84
|
+
git diff base...HEAD | sfmc-review-diff [options]
|
|
85
|
+
|
|
86
|
+
Options:
|
|
87
|
+
--fail-on <error|warning|info> Minimum severity that fails the process (default: error)
|
|
88
|
+
--language <ampscript|ssjs|html|auto> Passed to review_change (default: auto)
|
|
89
|
+
--max-problems <n> Max diagnostics (default: 50)
|
|
90
|
+
-h, --help Show this help
|
|
91
|
+
|
|
92
|
+
Exit codes:
|
|
93
|
+
0 No failing severity per --fail-on (including "no added lines" / clean review)
|
|
94
|
+
1 Review findings matched the failure policy, MCP error, or I/O error
|
|
95
|
+
`);
|
|
96
|
+
}
|
|
97
|
+
function parseArgs(argv) {
|
|
98
|
+
let failOn = 'error';
|
|
99
|
+
let language;
|
|
100
|
+
let maxProblems;
|
|
101
|
+
let help = false;
|
|
102
|
+
const positional = [];
|
|
103
|
+
for (let i = 0; i < argv.length; i++) {
|
|
104
|
+
const a = argv[i];
|
|
105
|
+
if (a === '-h' || a === '--help') {
|
|
106
|
+
help = true;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (a === '--fail-on') {
|
|
110
|
+
const v = argv[++i];
|
|
111
|
+
if (v !== 'error' && v !== 'warning' && v !== 'info') {
|
|
112
|
+
throw new Error(`--fail-on must be error, warning, or info, got: ${v ?? '(missing)'}`);
|
|
113
|
+
}
|
|
114
|
+
failOn = v;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (a === '--language') {
|
|
118
|
+
const v = argv[++i];
|
|
119
|
+
if (v !== 'ampscript' && v !== 'ssjs' && v !== 'html' && v !== 'auto') {
|
|
120
|
+
throw new Error(`--language must be ampscript, ssjs, html, or auto`);
|
|
121
|
+
}
|
|
122
|
+
language = v;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (a === '--max-problems') {
|
|
126
|
+
const v = argv[++i];
|
|
127
|
+
const n = v ? Number.parseInt(v, 10) : Number.NaN;
|
|
128
|
+
if (!Number.isFinite(n) || n < 1) {
|
|
129
|
+
throw new Error(`--max-problems must be a positive integer`);
|
|
130
|
+
}
|
|
131
|
+
maxProblems = n;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (a.startsWith('-')) {
|
|
135
|
+
throw new Error(`Unknown option: ${a}`);
|
|
136
|
+
}
|
|
137
|
+
positional.push(a);
|
|
138
|
+
}
|
|
139
|
+
if (positional.length > 1) {
|
|
140
|
+
throw new Error('At most one file argument is allowed');
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
filePath: positional[0] ?? null,
|
|
144
|
+
failOn,
|
|
145
|
+
language,
|
|
146
|
+
maxProblems,
|
|
147
|
+
help,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function isExecutedDirectly() {
|
|
151
|
+
const runPath = process.argv[1];
|
|
152
|
+
if (!runPath)
|
|
153
|
+
return false;
|
|
154
|
+
try {
|
|
155
|
+
return path.resolve(fileURLToPath(import.meta.url)) === path.resolve(runPath);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function main() {
|
|
162
|
+
let parsed;
|
|
163
|
+
try {
|
|
164
|
+
parsed = parseArgs(process.argv.slice(2));
|
|
165
|
+
}
|
|
166
|
+
catch (ex) {
|
|
167
|
+
console.error(String(ex instanceof Error ? ex.message : ex));
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
if (parsed.help) {
|
|
171
|
+
printHelp();
|
|
172
|
+
process.exit(0);
|
|
173
|
+
}
|
|
174
|
+
const diffText = parsed.filePath ? readFileSync(parsed.filePath, 'utf8') : await readStdin();
|
|
175
|
+
const serverPath = serverEntryPath();
|
|
176
|
+
const cwd = projectRoot();
|
|
177
|
+
const transport = new StdioClientTransport({
|
|
178
|
+
command: process.execPath,
|
|
179
|
+
args: [serverPath],
|
|
180
|
+
cwd,
|
|
181
|
+
});
|
|
182
|
+
const client = new Client({
|
|
183
|
+
name: 'sfmc-review-diff',
|
|
184
|
+
version: pkgVersion(),
|
|
185
|
+
});
|
|
186
|
+
try {
|
|
187
|
+
await client.connect(transport);
|
|
188
|
+
const args = { diff: diffText };
|
|
189
|
+
if (parsed.language !== undefined)
|
|
190
|
+
args.language = parsed.language;
|
|
191
|
+
if (parsed.maxProblems !== undefined)
|
|
192
|
+
args.maxProblems = parsed.maxProblems;
|
|
193
|
+
const result = await client.callTool({
|
|
194
|
+
name: 'review_change',
|
|
195
|
+
arguments: args,
|
|
196
|
+
});
|
|
197
|
+
const text = toolResultToText(result);
|
|
198
|
+
process.stdout.write(text);
|
|
199
|
+
if (!text.endsWith('\n'))
|
|
200
|
+
process.stdout.write('\n');
|
|
201
|
+
if (result.isError) {
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
const counts = countReviewSeverities(text);
|
|
205
|
+
if (shouldFail(counts, parsed.failOn)) {
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
process.exit(0);
|
|
209
|
+
}
|
|
210
|
+
catch (ex) {
|
|
211
|
+
console.error(String(ex instanceof Error ? ex.message : ex));
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
finally {
|
|
215
|
+
try {
|
|
216
|
+
await client.close();
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
/* ignore */
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (isExecutedDirectly()) {
|
|
224
|
+
void main();
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=reviewDiff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reviewDiff.js","sourceRoot":"","sources":["../../src/cli/reviewDiff.ts"],"names":[],"mappings":"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"}
|