@specverse/engines 4.1.14 → 4.1.15
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/assets/prompts/core/standard/v9/behavior.prompt.yaml +7 -1
- package/dist/ai/behavior-ai-service.d.ts +2 -0
- package/dist/ai/behavior-ai-service.d.ts.map +1 -1
- package/dist/ai/behavior-ai-service.js +2 -0
- package/dist/ai/behavior-ai-service.js.map +1 -1
- package/dist/ai/prompt-loader.js +2 -2
- package/dist/inference/quint-transpiler.d.ts.map +1 -1
- package/dist/inference/quint-transpiler.js +204 -4
- package/dist/inference/quint-transpiler.js.map +1 -1
- package/dist/libs/instance-factories/applications/templates/generic/backend-package-json-generator.js +4 -1
- package/dist/libs/instance-factories/applications/templates/generic/backend-tsconfig-generator.js +2 -2
- package/dist/libs/instance-factories/applications/templates/react/runtime-package-json-generator.js +1 -0
- package/dist/libs/instance-factories/cli/templates/commander/command-generator.js +81 -22
- package/dist/libs/instance-factories/communication/templates/eventemitter/bus-generator.js +2 -3
- package/dist/libs/instance-factories/controllers/templates/fastify/routes-generator.js +21 -1
- package/dist/libs/instance-factories/scaffolding/templates/generic/tsconfig-generator.js +10 -2
- package/dist/libs/instance-factories/services/templates/prisma/ai-behaviors-generator.js +130 -22
- package/dist/libs/instance-factories/services/templates/prisma/behavior-generator.js +14 -7
- package/dist/libs/instance-factories/services/templates/prisma/controller-generator.js +29 -54
- package/dist/libs/instance-factories/services/templates/prisma/service-generator.js +31 -10
- package/dist/libs/instance-factories/services/templates/prisma/step-conventions.js +1 -1
- package/dist/libs/instance-factories/views/templates/react/components-generator.js +40 -10
- package/dist/realize/index.d.ts.map +1 -1
- package/dist/realize/index.js +138 -23
- package/dist/realize/index.js.map +1 -1
- package/libs/instance-factories/applications/templates/generic/backend-package-json-generator.ts +4 -1
- package/libs/instance-factories/applications/templates/generic/backend-tsconfig-generator.ts +2 -2
- package/libs/instance-factories/applications/templates/react/runtime-package-json-generator.ts +6 -1
- package/libs/instance-factories/cli/templates/commander/command-generator.ts +99 -22
- package/libs/instance-factories/communication/templates/eventemitter/bus-generator.ts +2 -3
- package/libs/instance-factories/controllers/templates/fastify/routes-generator.ts +27 -2
- package/libs/instance-factories/scaffolding/templates/generic/tsconfig-generator.ts +23 -2
- package/libs/instance-factories/services/templates/prisma/ai-behaviors-generator.ts +185 -20
- package/libs/instance-factories/services/templates/prisma/behavior-generator.ts +34 -9
- package/libs/instance-factories/services/templates/prisma/controller-generator.ts +37 -59
- package/libs/instance-factories/services/templates/prisma/service-generator.ts +40 -10
- package/libs/instance-factories/services/templates/prisma/step-conventions.ts +4 -1
- package/libs/instance-factories/views/templates/react/components-generator.ts +50 -10
- package/package.json +1 -1
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/controllers/MCPServerController.js +0 -232
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/events/EventEmitter.js +0 -49
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/index.js +0 -18
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/interfaces/ResourceProvider.js +0 -0
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/models/LibrarySuggestion.js +0 -97
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/models/SpecVerseResource.js +0 -64
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/server/mcp-server.js +0 -182
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/CLIProxyService.js +0 -1210
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/EmbeddedResourcesAdapter.js +0 -172
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/EntityModuleService.js +0 -240
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/HybridResourcesProvider.js +0 -147
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/LibraryToolsService.js +0 -281
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/OrchestratorBridge.js +0 -409
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/OrchestratorToolsService.js +0 -414
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/PromptToolsService.js +0 -467
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/ResourcesProviderService.js +0 -135
- package/dist/libs/instance-factories/tools/templates/mcp/static/src/types/index.js +0 -0
- package/dist/libs/instance-factories/tools/templates/vscode/static/extension.js +0 -965
|
@@ -22,23 +22,29 @@ export default function generatePrismaService(context: TemplateContext): string
|
|
|
22
22
|
const hasEvents = (service.publishes && service.publishes.length > 0) ||
|
|
23
23
|
(service.subscribes && service.subscribes.length > 0);
|
|
24
24
|
|
|
25
|
+
// Generate operations first so we can detect what symbols they use in the output
|
|
26
|
+
const operationsCode = generateOperationsWithHelpers(service);
|
|
27
|
+
// If the generated code contains `aiBehaviors.`, we need to import the AI file
|
|
28
|
+
const hasAiBehaviors = /\baiBehaviors\./.test(operationsCode);
|
|
29
|
+
// Only emit the prisma client binding + import if the operations actually use it
|
|
30
|
+
const usesPrisma = /\bprisma\b/.test(operationsCode);
|
|
31
|
+
|
|
25
32
|
return `/**
|
|
26
33
|
* ${serviceName}
|
|
27
34
|
* Abstract business logic service
|
|
28
35
|
* ${service.description || ''}
|
|
29
36
|
*/
|
|
30
|
-
|
|
31
|
-
import {
|
|
32
|
-
${
|
|
33
|
-
|
|
34
|
-
const prisma = new PrismaClient();
|
|
37
|
+
${usesPrisma ? `\nimport { PrismaClient } from '@prisma/client';` : ''}
|
|
38
|
+
${hasEvents ? `import { eventBus } from '../events/eventBus.js';` : ''}
|
|
39
|
+
${hasAiBehaviors ? `import * as aiBehaviors from '../behaviors/${serviceName}.ai.js';` : ''}
|
|
40
|
+
${usesPrisma ? `\nconst prisma = new PrismaClient();` : ''}
|
|
35
41
|
|
|
36
42
|
/**
|
|
37
43
|
* ${serviceName} class
|
|
38
44
|
*/
|
|
39
45
|
export class ${serviceName} {
|
|
40
46
|
${generateConstructor(service)}
|
|
41
|
-
${
|
|
47
|
+
${operationsCode}
|
|
42
48
|
${generateEventSubscriptions(service)}
|
|
43
49
|
}
|
|
44
50
|
|
|
@@ -143,8 +149,12 @@ function generateOperations(service: any): string {
|
|
|
143
149
|
* Generate individual operation
|
|
144
150
|
*/
|
|
145
151
|
function generateOperation(operationName: string, operation: any, service: any): string {
|
|
146
|
-
const
|
|
152
|
+
const rawParams = generateOperationParams(operation);
|
|
147
153
|
const hasPublish = service.publishes && service.publishes.length > 0;
|
|
154
|
+
const body = generateOperationLogic(operation, service);
|
|
155
|
+
// Rename any operation parameter that the body doesn't reference so tsc's
|
|
156
|
+
// noUnusedParameters doesn't trip on placeholder service stubs.
|
|
157
|
+
const params = renameUnusedParams(rawParams, body);
|
|
148
158
|
|
|
149
159
|
return `
|
|
150
160
|
/**
|
|
@@ -153,7 +163,7 @@ function generateOperation(operationName: string, operation: any, service: any):
|
|
|
153
163
|
*/
|
|
154
164
|
public async ${operationName}(${params}): Promise<any> {
|
|
155
165
|
try {
|
|
156
|
-
${
|
|
166
|
+
${body}
|
|
157
167
|
|
|
158
168
|
${hasPublish ? `
|
|
159
169
|
// Publish event (example)
|
|
@@ -173,6 +183,24 @@ function generateOperation(operationName: string, operation: any, service: any):
|
|
|
173
183
|
`;
|
|
174
184
|
}
|
|
175
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Rename each parameter with a leading underscore if its name isn't referenced
|
|
188
|
+
* in the operation body (suppresses TS6133 on placeholder stubs).
|
|
189
|
+
*/
|
|
190
|
+
function renameUnusedParams(paramsString: string, body: string): string {
|
|
191
|
+
if (!paramsString.trim()) return paramsString;
|
|
192
|
+
return paramsString.split(',').map(segment => {
|
|
193
|
+
const trimmed = segment.trim();
|
|
194
|
+
const nameMatch = trimmed.match(/^(\w+)/);
|
|
195
|
+
if (!nameMatch) return segment;
|
|
196
|
+
const name = nameMatch[1];
|
|
197
|
+
if (name.startsWith('_')) return segment;
|
|
198
|
+
const re = new RegExp(`\\b${name}\\b`);
|
|
199
|
+
if (re.test(body)) return segment;
|
|
200
|
+
return segment.replace(new RegExp(`\\b${name}\\b`), `_${name}`);
|
|
201
|
+
}).join(', ');
|
|
202
|
+
}
|
|
203
|
+
|
|
176
204
|
/**
|
|
177
205
|
* Generate operation parameters
|
|
178
206
|
*/
|
|
@@ -214,18 +242,20 @@ function generateOperationLogic(operation: any, service: any): string {
|
|
|
214
242
|
if (impl.preconditions?.length || impl.postconditions?.length || impl.steps?.length || impl.transactional) {
|
|
215
243
|
// L3: Generate from behavioral specification
|
|
216
244
|
const modelName = inferModelFromServiceName(service.name);
|
|
245
|
+
const parameterNames = Object.keys(operation.parameters || {});
|
|
217
246
|
const context: BehaviorContext = {
|
|
218
247
|
modelName,
|
|
219
248
|
serviceName: service.name,
|
|
220
249
|
operationName: operation.name || 'execute',
|
|
221
|
-
prismaModel: modelName
|
|
250
|
+
prismaModel: modelName,
|
|
251
|
+
parameterNames,
|
|
222
252
|
};
|
|
223
253
|
|
|
224
254
|
const behavior: BehaviorMetadata = {
|
|
225
255
|
preconditions: impl.preconditions || [],
|
|
226
256
|
postconditions: impl.postconditions || [],
|
|
227
257
|
sideEffects: impl.sideEffects || [],
|
|
228
|
-
steps: impl.steps || [],
|
|
258
|
+
steps: impl.steps || operation.steps || [],
|
|
229
259
|
transactional: impl.transactional || false
|
|
230
260
|
};
|
|
231
261
|
|
|
@@ -22,6 +22,8 @@ export interface StepContext {
|
|
|
22
22
|
parameterNames?: string[];
|
|
23
23
|
/** Variables already declared (to avoid redeclaration) */
|
|
24
24
|
declaredVars?: Set<string>;
|
|
25
|
+
/** Named result variable (from spec's `as:` clause) */
|
|
26
|
+
resultName?: string;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
function toVar(name: string): string {
|
|
@@ -373,7 +375,8 @@ export function matchStep(
|
|
|
373
375
|
const paramNames = ctx.parameterNames || [];
|
|
374
376
|
const inputs = [...paramNames, ...declared];
|
|
375
377
|
|
|
376
|
-
|
|
378
|
+
// Use named result from spec (`as:`) or default to stepNResult
|
|
379
|
+
const resultVar = ctx.resultName || `step${ctx.stepNum}Result`;
|
|
377
380
|
const inputObj = inputs.length > 0 ? `{ ${inputs.join(', ')} }` : '{}';
|
|
378
381
|
|
|
379
382
|
// Register the result variable so subsequent steps can reference it
|
|
@@ -37,27 +37,67 @@ export default function generateReactComponent(context: TemplateContext): string
|
|
|
37
37
|
const lifecycle = getLifecycle(model);
|
|
38
38
|
const classified = classifyAttrs(attrs, lifecycle);
|
|
39
39
|
|
|
40
|
+
let body: string;
|
|
40
41
|
switch (viewType) {
|
|
41
42
|
case 'list':
|
|
42
|
-
|
|
43
|
+
body = generateListView(componentName, modelName, lower, plural, api, classified, belongsTo, lifecycle, view);
|
|
44
|
+
break;
|
|
43
45
|
case 'detail':
|
|
44
|
-
|
|
46
|
+
body = generateDetailView(componentName, modelName, lower, plural, api, classified, belongsTo, hasMany, lifecycle, view);
|
|
47
|
+
break;
|
|
45
48
|
case 'form':
|
|
46
|
-
|
|
49
|
+
body = generateFormView(componentName, modelName, lower, plural, api, classified, belongsTo, lifecycle, view);
|
|
50
|
+
break;
|
|
47
51
|
case 'dashboard':
|
|
48
|
-
|
|
52
|
+
body = generateDashboardView(componentName, modelName, lower, plural, api, classified, view, model);
|
|
53
|
+
break;
|
|
49
54
|
case 'board':
|
|
50
55
|
case 'workflow':
|
|
51
|
-
|
|
56
|
+
body = generateBoardView(componentName, modelName, lower, plural, api, lifecycle, view);
|
|
57
|
+
break;
|
|
52
58
|
case 'timeline':
|
|
53
|
-
|
|
59
|
+
body = generateTimelineView(componentName, modelName, lower, plural, api, view);
|
|
60
|
+
break;
|
|
54
61
|
case 'calendar':
|
|
55
|
-
|
|
62
|
+
body = generateCalendarView(componentName, modelName, lower, plural, api, view, model);
|
|
63
|
+
break;
|
|
56
64
|
case 'analytics':
|
|
57
|
-
|
|
65
|
+
body = generateAnalyticsView(componentName, modelName, lower, plural, api, classified, lifecycle, view, model);
|
|
66
|
+
break;
|
|
58
67
|
default:
|
|
59
|
-
|
|
68
|
+
body = generateListView(componentName, modelName, lower, plural, api, classified, belongsTo, lifecycle, view);
|
|
60
69
|
}
|
|
70
|
+
return stripUnusedImports(body);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Rewrite the top-of-file named imports to drop symbols the body doesn't
|
|
75
|
+
* reference. The view generators import every helper unconditionally; this
|
|
76
|
+
* pass keeps them clean under tsc's `noUnusedLocals`.
|
|
77
|
+
*/
|
|
78
|
+
function stripUnusedImports(source: string): string {
|
|
79
|
+
// Process only the leading contiguous run of named imports.
|
|
80
|
+
const lines = source.split('\n');
|
|
81
|
+
const out: string[] = [];
|
|
82
|
+
let i = 0;
|
|
83
|
+
// Find the end of the import block.
|
|
84
|
+
while (i < lines.length) {
|
|
85
|
+
const line = lines[i];
|
|
86
|
+
const match = line.match(/^import\s+\{\s*([^}]+?)\s*\}\s+from\s+(['"][^'"]+['"]);?\s*$/);
|
|
87
|
+
if (!match) break;
|
|
88
|
+
const names = match[1].split(',').map(s => s.trim()).filter(Boolean);
|
|
89
|
+
const from = match[2];
|
|
90
|
+
// Join all following lines as the usage body so we can scan for references.
|
|
91
|
+
const rest = lines.slice(i + 1).join('\n');
|
|
92
|
+
const used = names.filter(n => new RegExp(`\\b${n.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(rest));
|
|
93
|
+
if (used.length === 0) {
|
|
94
|
+
// drop the import entirely
|
|
95
|
+
} else {
|
|
96
|
+
out.push(`import { ${used.join(', ')} } from ${from};`);
|
|
97
|
+
}
|
|
98
|
+
i++;
|
|
99
|
+
}
|
|
100
|
+
return [...out, ...lines.slice(i)].join('\n');
|
|
61
101
|
}
|
|
62
102
|
|
|
63
103
|
// ============================================================================
|
|
@@ -427,7 +467,7 @@ ${lifecycle.states.map(s => ` <option value="${s}">${s.replace(/[_-]/g,
|
|
|
427
467
|
}
|
|
428
468
|
|
|
429
469
|
const t = type.toLowerCase();
|
|
430
|
-
if (t === 'boolean') return ` <input type="checkbox" name="${n}" checked={!!form.${n}} onChange={e => setForm(f => ({...f, ${n}: e.target.checked}))} className="h-4 w-4 text-blue-600 rounded" />`;
|
|
470
|
+
if (t === 'boolean') return ` <input type="checkbox" name="${n}" checked={!!form.${n}} onChange={e => setForm((f: any) => ({...f, ${n}: e.target.checked}))} className="h-4 w-4 text-blue-600 rounded" />`;
|
|
431
471
|
if (t.includes('date') || t.includes('timestamp')) return ` <input type="datetime-local" name="${n}" value={form.${n} || ''} onChange={handleChange} className="w-full px-3 py-2 border border-gray-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"${req} />`;
|
|
432
472
|
if (t === 'integer' || t === 'number' || t === 'money' || t === 'decimal' || t === 'float') return ` <input type="number" name="${n}" value={form.${n} || ''} onChange={handleChange} className="w-full px-3 py-2 border border-gray-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"${req} />`;
|
|
433
473
|
if (n.toLowerCase().includes('description') || n.toLowerCase().includes('content') || n.toLowerCase().includes('body')) return ` <textarea name="${n}" value={form.${n} || ''} onChange={handleChange} rows={4} className="w-full px-3 py-2 border border-gray-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"${req} />`;
|
package/package.json
CHANGED
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
-
import {
|
|
3
|
-
ListResourcesRequestSchema,
|
|
4
|
-
ReadResourceRequestSchema,
|
|
5
|
-
ListToolsRequestSchema,
|
|
6
|
-
CallToolRequestSchema
|
|
7
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
8
|
-
import { HybridResourcesProvider } from "../services/HybridResourcesProvider.js";
|
|
9
|
-
import { CLIProxyService } from "../services/CLIProxyService.js";
|
|
10
|
-
import { EntityModuleService } from "../services/EntityModuleService.js";
|
|
11
|
-
import { EventEmitter } from "../events/EventEmitter.js";
|
|
12
|
-
class MCPServerController {
|
|
13
|
-
server;
|
|
14
|
-
config;
|
|
15
|
-
resourcesProvider;
|
|
16
|
-
cliProxy;
|
|
17
|
-
entityModuleService;
|
|
18
|
-
eventEmitter;
|
|
19
|
-
constructor(config) {
|
|
20
|
-
this.config = config;
|
|
21
|
-
this.eventEmitter = new EventEmitter();
|
|
22
|
-
this.server = new Server({
|
|
23
|
-
name: "specverse-dynamic-cli",
|
|
24
|
-
version: "1.0.0"
|
|
25
|
-
}, {
|
|
26
|
-
capabilities: {
|
|
27
|
-
resources: {},
|
|
28
|
-
tools: {}
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
this.resourcesProvider = new HybridResourcesProvider({
|
|
32
|
-
mode: "auto",
|
|
33
|
-
resourcesPath: config.resources_path
|
|
34
|
-
});
|
|
35
|
-
this.cliProxy = new CLIProxyService();
|
|
36
|
-
this.entityModuleService = new EntityModuleService();
|
|
37
|
-
this.setupHandlers();
|
|
38
|
-
}
|
|
39
|
-
async listResources() {
|
|
40
|
-
try {
|
|
41
|
-
const startTime = Date.now();
|
|
42
|
-
const resources = await this.resourcesProvider.listResources();
|
|
43
|
-
if (this.entityModuleService.isAvailable()) {
|
|
44
|
-
resources.push(...this.entityModuleService.generateResources());
|
|
45
|
-
}
|
|
46
|
-
this.eventEmitter.emit("resource-requested", {
|
|
47
|
-
uri: "LIST_ALL",
|
|
48
|
-
requestTime: /* @__PURE__ */ new Date()
|
|
49
|
-
});
|
|
50
|
-
if (this.config.logging && this.config.mode !== "local" && process.env.MCP_DEBUG) {
|
|
51
|
-
console.error(`Listed ${resources.length} resources in ${Date.now() - startTime}ms`);
|
|
52
|
-
}
|
|
53
|
-
return resources;
|
|
54
|
-
} catch (error) {
|
|
55
|
-
this.eventEmitter.emit("error-occurred", {
|
|
56
|
-
operation: "listResources",
|
|
57
|
-
error: error instanceof Error ? error.message : String(error)
|
|
58
|
-
});
|
|
59
|
-
throw error;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
async readResource(uri) {
|
|
63
|
-
try {
|
|
64
|
-
const startTime = Date.now();
|
|
65
|
-
if (uri.startsWith("specverse://entities/") && this.entityModuleService.isAvailable()) {
|
|
66
|
-
const entityResources = this.entityModuleService.generateResources();
|
|
67
|
-
const resource2 = entityResources.find((r) => r.uri === uri);
|
|
68
|
-
if (resource2) {
|
|
69
|
-
this.eventEmitter.emit("resource-requested", { uri, requestTime: /* @__PURE__ */ new Date() });
|
|
70
|
-
return resource2;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
const content = await this.resourcesProvider.getResourceContent(uri);
|
|
74
|
-
const resources = await this.resourcesProvider.listResources();
|
|
75
|
-
const resource = resources.find((r) => r.uri === uri);
|
|
76
|
-
if (!resource) {
|
|
77
|
-
throw new Error(`Resource not found: ${uri}`);
|
|
78
|
-
}
|
|
79
|
-
this.eventEmitter.emit("resource-requested", {
|
|
80
|
-
uri,
|
|
81
|
-
requestTime: /* @__PURE__ */ new Date()
|
|
82
|
-
});
|
|
83
|
-
if (this.config.logging && this.config.mode !== "local" && process.env.MCP_DEBUG) {
|
|
84
|
-
console.error(`Read resource ${uri} in ${Date.now() - startTime}ms`);
|
|
85
|
-
}
|
|
86
|
-
return {
|
|
87
|
-
...resource,
|
|
88
|
-
content
|
|
89
|
-
};
|
|
90
|
-
} catch (error) {
|
|
91
|
-
this.eventEmitter.emit("error-occurred", {
|
|
92
|
-
operation: "readResource",
|
|
93
|
-
error: error instanceof Error ? error.message : String(error),
|
|
94
|
-
context: { uri }
|
|
95
|
-
});
|
|
96
|
-
throw error;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
setupHandlers() {
|
|
100
|
-
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
101
|
-
const resources = await this.listResources();
|
|
102
|
-
return { resources };
|
|
103
|
-
});
|
|
104
|
-
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
105
|
-
const resource = await this.readResource(request.params.uri);
|
|
106
|
-
return {
|
|
107
|
-
contents: [{
|
|
108
|
-
uri: resource.uri,
|
|
109
|
-
mimeType: resource.mimeType,
|
|
110
|
-
text: resource.content
|
|
111
|
-
}]
|
|
112
|
-
};
|
|
113
|
-
});
|
|
114
|
-
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
115
|
-
let dynamicCliTools = [];
|
|
116
|
-
try {
|
|
117
|
-
dynamicCliTools = await this.cliProxy.generateMCPTools();
|
|
118
|
-
if (this.config.logging && this.config.mode !== "local" && process.env.MCP_DEBUG) {
|
|
119
|
-
console.error(`\u{1F527} Generated ${dynamicCliTools.length} dynamic CLI tools`);
|
|
120
|
-
}
|
|
121
|
-
} catch (error) {
|
|
122
|
-
if (this.config.logging && process.env.MCP_DEBUG) {
|
|
123
|
-
console.error("\u26A0\uFE0F Failed to generate dynamic CLI tools:", error);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
if (this.entityModuleService.isAvailable()) {
|
|
127
|
-
dynamicCliTools.push(...this.entityModuleService.generateTools());
|
|
128
|
-
}
|
|
129
|
-
return { tools: dynamicCliTools };
|
|
130
|
-
});
|
|
131
|
-
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
132
|
-
try {
|
|
133
|
-
const { name, arguments: args } = request.params;
|
|
134
|
-
const entityToolNames = ["specverse_expand_constraint", "specverse_list_conventions", "specverse_entity_info"];
|
|
135
|
-
if (entityToolNames.includes(name) && this.entityModuleService.isAvailable()) {
|
|
136
|
-
return await this.entityModuleService.executeTool(name, args || {});
|
|
137
|
-
}
|
|
138
|
-
if (name.startsWith("specverse_")) {
|
|
139
|
-
const result = await this.cliProxy.executeCommand(name, args || {});
|
|
140
|
-
return { content: result.content };
|
|
141
|
-
} else {
|
|
142
|
-
return {
|
|
143
|
-
content: [{
|
|
144
|
-
type: "text",
|
|
145
|
-
text: `Error: Unknown tool '${name}'. Only dynamic CLI tools (prefixed with 'specverse_') are supported.`
|
|
146
|
-
}]
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
} catch (error) {
|
|
150
|
-
return {
|
|
151
|
-
content: [{
|
|
152
|
-
type: "text",
|
|
153
|
-
text: `Error in tool handler: ${error instanceof Error ? error.message : String(error)}`
|
|
154
|
-
}]
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Start the MCP server
|
|
161
|
-
*/
|
|
162
|
-
async start() {
|
|
163
|
-
try {
|
|
164
|
-
await this.resourcesProvider.initializeResources();
|
|
165
|
-
const entityAvailable = await this.entityModuleService.initialize();
|
|
166
|
-
if (this.config.logging && process.env.MCP_DEBUG) {
|
|
167
|
-
if (entityAvailable) {
|
|
168
|
-
const modules = this.entityModuleService.getEntityModules();
|
|
169
|
-
console.error(`\u{1F9E9} Entity modules loaded: ${modules.length} modules, ${modules.reduce((s, m) => s + m.inferenceRuleCount, 0)} rules`);
|
|
170
|
-
} else {
|
|
171
|
-
console.error("\u26A0\uFE0F Entity modules not available in this deployment");
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
this.eventEmitter.emit("server-started", {
|
|
175
|
-
serverName: "SpecVerse Dynamic CLI",
|
|
176
|
-
mode: this.config.mode,
|
|
177
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
178
|
-
featuresEnabled: this.config.features,
|
|
179
|
-
pid: process.pid
|
|
180
|
-
});
|
|
181
|
-
if (this.config.logging && process.env.MCP_DEBUG) {
|
|
182
|
-
const mode = this.config.mode || "local";
|
|
183
|
-
const resourceCount = this.resourcesProvider.getCachedResourceCount();
|
|
184
|
-
console.error(`\u{1F680} SpecVerse MCP Server started in ${mode} mode`);
|
|
185
|
-
console.error(`\u{1F4DA} Loaded ${resourceCount} resources`);
|
|
186
|
-
console.error(`\u{1F527} Dynamic CLI integration enabled - all tools from CLI`);
|
|
187
|
-
}
|
|
188
|
-
} catch (error) {
|
|
189
|
-
this.eventEmitter.emit("error-occurred", {
|
|
190
|
-
operation: "start",
|
|
191
|
-
error: error instanceof Error ? error.message : String(error)
|
|
192
|
-
});
|
|
193
|
-
throw error;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* Stop the MCP server
|
|
198
|
-
*/
|
|
199
|
-
async stop() {
|
|
200
|
-
try {
|
|
201
|
-
await this.server.close();
|
|
202
|
-
if (this.config.logging && process.env.MCP_DEBUG) {
|
|
203
|
-
console.error("\u{1F6D1} SpecVerse MCP Server stopped");
|
|
204
|
-
}
|
|
205
|
-
} catch (error) {
|
|
206
|
-
this.eventEmitter.emit("error-occurred", {
|
|
207
|
-
operation: "stop",
|
|
208
|
-
error: error instanceof Error ? error.message : String(error)
|
|
209
|
-
});
|
|
210
|
-
throw error;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
// Methods expected by the main server
|
|
214
|
-
getEventEmitter() {
|
|
215
|
-
return this.eventEmitter;
|
|
216
|
-
}
|
|
217
|
-
async initialize() {
|
|
218
|
-
await this.start();
|
|
219
|
-
}
|
|
220
|
-
getServer() {
|
|
221
|
-
return this.server;
|
|
222
|
-
}
|
|
223
|
-
getMetrics() {
|
|
224
|
-
return {
|
|
225
|
-
resourceCount: this.resourcesProvider.getCachedResourceCount(),
|
|
226
|
-
cliIntegration: true
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
export {
|
|
231
|
-
MCPServerController
|
|
232
|
-
};
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
class EventEmitter {
|
|
2
|
-
listeners = /* @__PURE__ */ new Map();
|
|
3
|
-
on(eventType, handler) {
|
|
4
|
-
if (!this.listeners.has(eventType)) {
|
|
5
|
-
this.listeners.set(eventType, []);
|
|
6
|
-
}
|
|
7
|
-
this.listeners.get(eventType).push(handler);
|
|
8
|
-
}
|
|
9
|
-
off(eventType, handler) {
|
|
10
|
-
const handlers = this.listeners.get(eventType);
|
|
11
|
-
if (handlers) {
|
|
12
|
-
const index = handlers.indexOf(handler);
|
|
13
|
-
if (index !== -1) {
|
|
14
|
-
handlers.splice(index, 1);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
async emit(eventType, data) {
|
|
19
|
-
const handlers = this.listeners.get(eventType);
|
|
20
|
-
if (handlers) {
|
|
21
|
-
const promises = handlers.map((handler) => {
|
|
22
|
-
try {
|
|
23
|
-
const result = handler(data);
|
|
24
|
-
return Promise.resolve(result);
|
|
25
|
-
} catch (error) {
|
|
26
|
-
console.error(`Error in event handler for ${eventType}:`, error);
|
|
27
|
-
return Promise.resolve();
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
await Promise.allSettled(promises);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
removeAllListeners(eventType) {
|
|
34
|
-
if (eventType) {
|
|
35
|
-
this.listeners.delete(eventType);
|
|
36
|
-
} else {
|
|
37
|
-
this.listeners.clear();
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
listenerCount(eventType) {
|
|
41
|
-
return this.listeners.get(eventType)?.length || 0;
|
|
42
|
-
}
|
|
43
|
-
eventNames() {
|
|
44
|
-
return Array.from(this.listeners.keys());
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
export {
|
|
48
|
-
EventEmitter
|
|
49
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { SpecVerseCleanMCPServer } from "./server/mcp-server.js";
|
|
2
|
-
import { MCPServerController } from "./controllers/MCPServerController.js";
|
|
3
|
-
import { ResourcesProviderService } from "./services/ResourcesProviderService.js";
|
|
4
|
-
import { LibraryToolsService } from "./services/LibraryToolsService.js";
|
|
5
|
-
import { PromptToolsService } from "./services/PromptToolsService.js";
|
|
6
|
-
import { SpecVerseResourceModel } from "./models/SpecVerseResource.js";
|
|
7
|
-
import { LibrarySuggestionModel } from "./models/LibrarySuggestion.js";
|
|
8
|
-
import { EventEmitter } from "./events/EventEmitter.js";
|
|
9
|
-
export {
|
|
10
|
-
EventEmitter,
|
|
11
|
-
LibrarySuggestionModel,
|
|
12
|
-
LibraryToolsService,
|
|
13
|
-
MCPServerController,
|
|
14
|
-
PromptToolsService,
|
|
15
|
-
ResourcesProviderService,
|
|
16
|
-
SpecVerseCleanMCPServer,
|
|
17
|
-
SpecVerseResourceModel
|
|
18
|
-
};
|
package/dist/libs/instance-factories/tools/templates/mcp/static/src/interfaces/ResourceProvider.js
DELETED
|
File without changes
|
package/dist/libs/instance-factories/tools/templates/mcp/static/src/models/LibrarySuggestion.js
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
const LibrarySuggestionSchema = z.object({
|
|
3
|
-
name: z.string().min(1, "Name is required"),
|
|
4
|
-
path: z.string().min(1, "Path is required"),
|
|
5
|
-
type: z.enum(["deployment", "domain", "manifest", "type", "standard"]),
|
|
6
|
-
description: z.string().min(1, "Description is required"),
|
|
7
|
-
ai_description: z.string().min(1, "AI description is required"),
|
|
8
|
-
expansion_factor: z.number().min(1, "Expansion factor must be >= 1"),
|
|
9
|
-
complexity_level: z.enum(["low", "medium", "high"]),
|
|
10
|
-
best_for: z.array(z.string()).min(1, "Must specify at least one use case")
|
|
11
|
-
});
|
|
12
|
-
class LibrarySuggestionModel {
|
|
13
|
-
constructor(name, path, type, description, ai_description, expansion_factor, complexity_level, best_for) {
|
|
14
|
-
this.name = name;
|
|
15
|
-
this.path = path;
|
|
16
|
-
this.type = type;
|
|
17
|
-
this.description = description;
|
|
18
|
-
this.ai_description = ai_description;
|
|
19
|
-
this.expansion_factor = expansion_factor;
|
|
20
|
-
this.complexity_level = complexity_level;
|
|
21
|
-
this.best_for = best_for;
|
|
22
|
-
}
|
|
23
|
-
static create(data) {
|
|
24
|
-
const validated = LibrarySuggestionSchema.parse(data);
|
|
25
|
-
return new LibrarySuggestionModel(
|
|
26
|
-
validated.name,
|
|
27
|
-
validated.path,
|
|
28
|
-
validated.type,
|
|
29
|
-
validated.description,
|
|
30
|
-
validated.ai_description,
|
|
31
|
-
validated.expansion_factor,
|
|
32
|
-
validated.complexity_level,
|
|
33
|
-
validated.best_for
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
static fromJson(json) {
|
|
37
|
-
const data = JSON.parse(json);
|
|
38
|
-
return this.create(data);
|
|
39
|
-
}
|
|
40
|
-
toJson() {
|
|
41
|
-
return JSON.stringify({
|
|
42
|
-
name: this.name,
|
|
43
|
-
path: this.path,
|
|
44
|
-
type: this.type,
|
|
45
|
-
description: this.description,
|
|
46
|
-
ai_description: this.ai_description,
|
|
47
|
-
expansion_factor: this.expansion_factor,
|
|
48
|
-
complexity_level: this.complexity_level,
|
|
49
|
-
best_for: this.best_for
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
matchesContext(context, scale) {
|
|
53
|
-
let score = 0;
|
|
54
|
-
const contextLower = context.toLowerCase();
|
|
55
|
-
for (const useCase of this.best_for) {
|
|
56
|
-
if (contextLower.includes(useCase.toLowerCase())) {
|
|
57
|
-
score += 2;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
const scaleComplexity = this.getScaleComplexity(scale);
|
|
61
|
-
const complexityScore = this.getComplexityScore();
|
|
62
|
-
const complexityMatch = Math.abs(complexityScore - scaleComplexity);
|
|
63
|
-
score += Math.max(0, 3 - complexityMatch);
|
|
64
|
-
return score;
|
|
65
|
-
}
|
|
66
|
-
getScaleComplexity(scale) {
|
|
67
|
-
switch (scale) {
|
|
68
|
-
case "personal":
|
|
69
|
-
return 1;
|
|
70
|
-
case "business":
|
|
71
|
-
return 2;
|
|
72
|
-
case "enterprise":
|
|
73
|
-
return 3;
|
|
74
|
-
default:
|
|
75
|
-
return 2;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
getComplexityScore() {
|
|
79
|
-
switch (this.complexity_level) {
|
|
80
|
-
case "low":
|
|
81
|
-
return 1;
|
|
82
|
-
case "medium":
|
|
83
|
-
return 2;
|
|
84
|
-
case "high":
|
|
85
|
-
return 3;
|
|
86
|
-
default:
|
|
87
|
-
return 2;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
getRelevanceScore() {
|
|
91
|
-
return this.expansion_factor * this.getComplexityScore();
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
export {
|
|
95
|
-
LibrarySuggestionModel,
|
|
96
|
-
LibrarySuggestionSchema
|
|
97
|
-
};
|
package/dist/libs/instance-factories/tools/templates/mcp/static/src/models/SpecVerseResource.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
const SpecVerseResourceSchema = z.object({
|
|
3
|
-
uri: z.string().min(1, "URI is required").regex(/^specverse:\/\//, "URI must start with specverse://"),
|
|
4
|
-
name: z.string().min(1, "Name is required"),
|
|
5
|
-
description: z.string().min(1, "Description is required"),
|
|
6
|
-
mimeType: z.string().min(1, "MIME type is required"),
|
|
7
|
-
content: z.string().optional(),
|
|
8
|
-
metadata: z.record(z.any()).optional()
|
|
9
|
-
});
|
|
10
|
-
class SpecVerseResourceModel {
|
|
11
|
-
constructor(uri, name, description, mimeType, content, metadata) {
|
|
12
|
-
this.uri = uri;
|
|
13
|
-
this.name = name;
|
|
14
|
-
this.description = description;
|
|
15
|
-
this.mimeType = mimeType;
|
|
16
|
-
this.content = content;
|
|
17
|
-
this.metadata = metadata;
|
|
18
|
-
}
|
|
19
|
-
static create(data) {
|
|
20
|
-
const validated = SpecVerseResourceSchema.parse(data);
|
|
21
|
-
return new SpecVerseResourceModel(
|
|
22
|
-
validated.uri,
|
|
23
|
-
validated.name,
|
|
24
|
-
validated.description,
|
|
25
|
-
validated.mimeType,
|
|
26
|
-
validated.content,
|
|
27
|
-
validated.metadata
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
static fromJson(json) {
|
|
31
|
-
const data = JSON.parse(json);
|
|
32
|
-
return this.create(data);
|
|
33
|
-
}
|
|
34
|
-
toJson() {
|
|
35
|
-
return JSON.stringify({
|
|
36
|
-
uri: this.uri,
|
|
37
|
-
name: this.name,
|
|
38
|
-
description: this.description,
|
|
39
|
-
mimeType: this.mimeType,
|
|
40
|
-
content: this.content,
|
|
41
|
-
metadata: this.metadata
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
withContent(content) {
|
|
45
|
-
return new SpecVerseResourceModel(
|
|
46
|
-
this.uri,
|
|
47
|
-
this.name,
|
|
48
|
-
this.description,
|
|
49
|
-
this.mimeType,
|
|
50
|
-
content,
|
|
51
|
-
this.metadata
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
matches(uri) {
|
|
55
|
-
return this.uri === uri;
|
|
56
|
-
}
|
|
57
|
-
isLoaded() {
|
|
58
|
-
return this.content !== void 0;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
export {
|
|
62
|
-
SpecVerseResourceModel,
|
|
63
|
-
SpecVerseResourceSchema
|
|
64
|
-
};
|