@guren/server 0.2.0-alpha.7 → 1.0.0-rc.9
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/dist/Application-DtWDHXr1.d.ts +2110 -0
- package/dist/BroadcastManager-AkIWUGJo.d.ts +466 -0
- package/dist/CacheManager-BkvHEOZX.d.ts +244 -0
- package/dist/ConsoleKernel-CqCVrdZs.d.ts +207 -0
- package/dist/EventManager-CmIoLt7r.d.ts +207 -0
- package/dist/Gate-CNkBYf8m.d.ts +268 -0
- package/dist/HealthManager-DUyMIzsZ.d.ts +141 -0
- package/dist/I18nManager-Dtgzsf5n.d.ts +270 -0
- package/dist/LogManager-7mxnkaPM.d.ts +256 -0
- package/dist/MailManager-DpMvYiP9.d.ts +292 -0
- package/dist/Scheduler-BstvSca7.d.ts +469 -0
- package/dist/StorageManager-oZTHqaza.d.ts +337 -0
- package/dist/api-token-JOif2CtG.d.ts +1792 -0
- package/dist/app-key-CsBfRC_Q.d.ts +214 -0
- package/dist/auth/index.d.ts +418 -0
- package/dist/auth/index.js +6742 -0
- package/dist/authorization/index.d.ts +129 -0
- package/dist/authorization/index.js +621 -0
- package/dist/broadcasting/index.d.ts +233 -0
- package/dist/broadcasting/index.js +907 -0
- package/dist/cache/index.d.ts +233 -0
- package/dist/cache/index.js +817 -0
- package/dist/encryption/index.d.ts +222 -0
- package/dist/encryption/index.js +602 -0
- package/dist/events/index.d.ts +155 -0
- package/dist/events/index.js +330 -0
- package/dist/health/index.d.ts +185 -0
- package/dist/health/index.js +379 -0
- package/dist/i18n/index.d.ts +101 -0
- package/dist/i18n/index.js +597 -0
- package/dist/index-9_Jzj5jo.d.ts +7 -0
- package/dist/index.d.ts +2628 -619
- package/dist/index.js +22229 -3116
- package/dist/lambda/index.d.ts +156 -0
- package/dist/lambda/index.js +91 -0
- package/dist/logging/index.d.ts +50 -0
- package/dist/logging/index.js +557 -0
- package/dist/mail/index.d.ts +288 -0
- package/dist/mail/index.js +695 -0
- package/dist/mcp/index.d.ts +139 -0
- package/dist/mcp/index.js +382 -0
- package/dist/notifications/index.d.ts +271 -0
- package/dist/notifications/index.js +741 -0
- package/dist/queue/index.d.ts +423 -0
- package/dist/queue/index.js +958 -0
- package/dist/runtime/index.d.ts +93 -0
- package/dist/runtime/index.js +834 -0
- package/dist/scheduling/index.d.ts +41 -0
- package/dist/scheduling/index.js +836 -0
- package/dist/storage/index.d.ts +196 -0
- package/dist/storage/index.js +832 -0
- package/dist/vite/index.js +203 -3
- package/package.json +93 -6
- package/dist/chunk-FK2XQSBF.js +0 -160
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { S as ServiceProvider } from '../Application-DtWDHXr1.js';
|
|
3
|
+
import 'hono';
|
|
4
|
+
import '../api-token-JOif2CtG.js';
|
|
5
|
+
import 'vite';
|
|
6
|
+
import '../EventManager-CmIoLt7r.js';
|
|
7
|
+
import '../CacheManager-BkvHEOZX.js';
|
|
8
|
+
import '../MailManager-DpMvYiP9.js';
|
|
9
|
+
import '../LogManager-7mxnkaPM.js';
|
|
10
|
+
import '../I18nManager-Dtgzsf5n.js';
|
|
11
|
+
import '../BroadcastManager-AkIWUGJo.js';
|
|
12
|
+
import '../index-9_Jzj5jo.js';
|
|
13
|
+
import '../app-key-CsBfRC_Q.js';
|
|
14
|
+
import '../StorageManager-oZTHqaza.js';
|
|
15
|
+
import '../HealthManager-DUyMIzsZ.js';
|
|
16
|
+
import '../Scheduler-BstvSca7.js';
|
|
17
|
+
import '../Gate-CNkBYf8m.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* CLI functions that the MCP server wraps.
|
|
21
|
+
* These are injected at runtime to avoid circular dependencies
|
|
22
|
+
* (@guren/server cannot depend on @guren/cli directly).
|
|
23
|
+
*/
|
|
24
|
+
interface GurenCliApi {
|
|
25
|
+
generateContext(opts: {
|
|
26
|
+
cwd: string;
|
|
27
|
+
}): Promise<{
|
|
28
|
+
framework: {
|
|
29
|
+
name: string;
|
|
30
|
+
version: string;
|
|
31
|
+
};
|
|
32
|
+
models: Array<{
|
|
33
|
+
className: string;
|
|
34
|
+
}>;
|
|
35
|
+
routes: Array<unknown>;
|
|
36
|
+
pages: string[];
|
|
37
|
+
controllers: string[];
|
|
38
|
+
resources: string[];
|
|
39
|
+
events: string[];
|
|
40
|
+
jobs: string[];
|
|
41
|
+
middleware: string[];
|
|
42
|
+
listeners: string[];
|
|
43
|
+
validators: string[];
|
|
44
|
+
}>;
|
|
45
|
+
renderContextMarkdown(ctx: unknown): string;
|
|
46
|
+
runCheck(opts: {
|
|
47
|
+
cwd: string;
|
|
48
|
+
}): Promise<{
|
|
49
|
+
cwd: string;
|
|
50
|
+
checks: Array<{
|
|
51
|
+
key: string;
|
|
52
|
+
title: string;
|
|
53
|
+
status: string;
|
|
54
|
+
message: string;
|
|
55
|
+
suggestion?: string;
|
|
56
|
+
}>;
|
|
57
|
+
passCount: number;
|
|
58
|
+
warnCount: number;
|
|
59
|
+
failCount: number;
|
|
60
|
+
}>;
|
|
61
|
+
listModels(opts: {
|
|
62
|
+
appRoot: string;
|
|
63
|
+
}): Promise<Array<{
|
|
64
|
+
className: string;
|
|
65
|
+
filePath: string;
|
|
66
|
+
tableName?: string;
|
|
67
|
+
relationships: Array<{
|
|
68
|
+
name: string;
|
|
69
|
+
type: string;
|
|
70
|
+
relatedModel?: string;
|
|
71
|
+
}>;
|
|
72
|
+
usesAuth: boolean;
|
|
73
|
+
hasSoftDeletes: boolean;
|
|
74
|
+
}>>;
|
|
75
|
+
generateGuidelines(opts: {
|
|
76
|
+
cwd: string;
|
|
77
|
+
}): Promise<string>;
|
|
78
|
+
runDoctor(opts: {
|
|
79
|
+
cwd: string;
|
|
80
|
+
}): Promise<unknown>;
|
|
81
|
+
suggestNextSteps(opts: {
|
|
82
|
+
cwd: string;
|
|
83
|
+
}): Promise<unknown>;
|
|
84
|
+
makeFeature(name: string, opts: {
|
|
85
|
+
fields?: string;
|
|
86
|
+
withTest?: boolean;
|
|
87
|
+
force?: boolean;
|
|
88
|
+
}): Promise<string[]>;
|
|
89
|
+
makeController(name: string, opts: {
|
|
90
|
+
force?: boolean;
|
|
91
|
+
}): Promise<string | string[]>;
|
|
92
|
+
makeModel(name: string, opts: {
|
|
93
|
+
force?: boolean;
|
|
94
|
+
}): Promise<string | string[]>;
|
|
95
|
+
makeView(name: string, opts: {
|
|
96
|
+
force?: boolean;
|
|
97
|
+
}): Promise<string | string[]>;
|
|
98
|
+
makeTest(name: string, opts: {
|
|
99
|
+
force?: boolean;
|
|
100
|
+
}): Promise<string | string[]>;
|
|
101
|
+
makeRoute(name: string, opts: {
|
|
102
|
+
force?: boolean;
|
|
103
|
+
}): Promise<string | string[]>;
|
|
104
|
+
generateRouteTypes(opts: {
|
|
105
|
+
cwd: string;
|
|
106
|
+
}): Promise<unknown>;
|
|
107
|
+
generatePageTypes(opts: {
|
|
108
|
+
cwd: string;
|
|
109
|
+
}): Promise<unknown>;
|
|
110
|
+
generateDataTypes(opts: {
|
|
111
|
+
cwd: string;
|
|
112
|
+
}): Promise<unknown>;
|
|
113
|
+
generateChannelTypes(opts: {
|
|
114
|
+
cwd: string;
|
|
115
|
+
}): Promise<unknown>;
|
|
116
|
+
}
|
|
117
|
+
interface CreateMcpServerOptions {
|
|
118
|
+
cwd: string;
|
|
119
|
+
cli: GurenCliApi;
|
|
120
|
+
version?: string;
|
|
121
|
+
}
|
|
122
|
+
declare function createMcpServer(options: CreateMcpServerOptions): McpServer;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Registers the MCP (Model Context Protocol) endpoint at /_guren/mcp.
|
|
126
|
+
*
|
|
127
|
+
* This provider is only active in development mode (NODE_ENV !== 'production').
|
|
128
|
+
* It allows AI coding agents (Claude Code, Cursor, etc.) to introspect
|
|
129
|
+
* the project structure, run integrity checks, and scaffold code.
|
|
130
|
+
*
|
|
131
|
+
* @guren/cli is loaded via dynamic import to avoid circular dependencies
|
|
132
|
+
* (@guren/server -> @guren/cli -> @guren/core -> @guren/server).
|
|
133
|
+
*/
|
|
134
|
+
declare class McpServiceProvider extends ServiceProvider {
|
|
135
|
+
register(): void;
|
|
136
|
+
boot(): Promise<void>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export { type CreateMcpServerOptions, McpServiceProvider, createMcpServer };
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
// src/mcp/create-mcp-server.ts
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
function createMcpServer(options) {
|
|
5
|
+
const { cwd, cli, version = "0.2.0" } = options;
|
|
6
|
+
const server = new McpServer({
|
|
7
|
+
name: "guren",
|
|
8
|
+
version
|
|
9
|
+
});
|
|
10
|
+
server.tool(
|
|
11
|
+
"guren_get_context",
|
|
12
|
+
"Get a complete project context map including models, routes, pages, controllers, resources, events, jobs, middleware, listeners, and validators.",
|
|
13
|
+
{
|
|
14
|
+
format: z.enum(["json", "markdown"]).default("json").describe("Output format")
|
|
15
|
+
},
|
|
16
|
+
async ({ format }) => {
|
|
17
|
+
const ctx = await cli.generateContext({ cwd });
|
|
18
|
+
const text = format === "markdown" ? cli.renderContextMarkdown(ctx) : JSON.stringify(ctx, null, 2);
|
|
19
|
+
return { content: [{ type: "text", text }] };
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
server.tool(
|
|
23
|
+
"guren_check",
|
|
24
|
+
"Validate route-to-controller-to-page consistency, check for empty controller methods, missing test files, and missing generated manifests.",
|
|
25
|
+
{},
|
|
26
|
+
async () => {
|
|
27
|
+
const report = await cli.runCheck({ cwd });
|
|
28
|
+
return { content: [{ type: "text", text: JSON.stringify(report, null, 2) }] };
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
server.tool(
|
|
32
|
+
"guren_list_models",
|
|
33
|
+
"List all models with their table names, relationships, authentication trait, and soft deletes status.",
|
|
34
|
+
{},
|
|
35
|
+
async () => {
|
|
36
|
+
const models = await cli.listModels({ appRoot: cwd });
|
|
37
|
+
return { content: [{ type: "text", text: JSON.stringify(models, null, 2) }] };
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
server.tool(
|
|
41
|
+
"guren_generate_guidelines",
|
|
42
|
+
"Generate project-specific coding guidelines based on the current project structure, naming conventions, auth setup, models, validation patterns, and middleware.",
|
|
43
|
+
{},
|
|
44
|
+
async () => {
|
|
45
|
+
const guidelines = await cli.generateGuidelines({ cwd });
|
|
46
|
+
return { content: [{ type: "text", text: guidelines }] };
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
server.tool(
|
|
50
|
+
"guren_doctor",
|
|
51
|
+
"Run a comprehensive health check on the Guren project and optionally suggest actionable next steps.",
|
|
52
|
+
{
|
|
53
|
+
next: z.boolean().default(false).describe("Include actionable next steps")
|
|
54
|
+
},
|
|
55
|
+
async ({ next }) => {
|
|
56
|
+
const report = await cli.runDoctor({ cwd });
|
|
57
|
+
let text = JSON.stringify(report, null, 2);
|
|
58
|
+
if (next) {
|
|
59
|
+
const steps = await cli.suggestNextSteps({ cwd });
|
|
60
|
+
text = JSON.stringify({ ...report, nextSteps: steps }, null, 2);
|
|
61
|
+
}
|
|
62
|
+
return { content: [{ type: "text", text }] };
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
server.tool(
|
|
66
|
+
"guren_make_feature",
|
|
67
|
+
"Generate a complete CRUD feature: controller, model, views (Index, Show, New, Edit), validator, and resource. Optionally include test file.",
|
|
68
|
+
{
|
|
69
|
+
name: z.string().describe('Resource name in PascalCase (e.g., "Post", "BlogComment")'),
|
|
70
|
+
fields: z.string().optional().describe(
|
|
71
|
+
'Comma-separated field definitions (e.g., "title:string,body:text,published:boolean")'
|
|
72
|
+
),
|
|
73
|
+
withTest: z.boolean().default(false).describe("Generate test file"),
|
|
74
|
+
force: z.boolean().default(false).describe("Overwrite existing files")
|
|
75
|
+
},
|
|
76
|
+
async ({ name, fields, withTest, force }) => {
|
|
77
|
+
const originalCwd = process.cwd();
|
|
78
|
+
try {
|
|
79
|
+
process.chdir(cwd);
|
|
80
|
+
const createdFiles = await cli.makeFeature(name, { fields, withTest, force });
|
|
81
|
+
return {
|
|
82
|
+
content: [{ type: "text", text: JSON.stringify({ created: createdFiles }, null, 2) }]
|
|
83
|
+
};
|
|
84
|
+
} finally {
|
|
85
|
+
process.chdir(originalCwd);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
server.tool(
|
|
90
|
+
"guren_make_component",
|
|
91
|
+
"Generate a single component: controller, model, middleware, event, job, listener, resource, view, test, mail, notification, seeder, factory, or migration.",
|
|
92
|
+
{
|
|
93
|
+
type: z.enum([
|
|
94
|
+
"controller",
|
|
95
|
+
"model",
|
|
96
|
+
"middleware",
|
|
97
|
+
"event",
|
|
98
|
+
"job",
|
|
99
|
+
"listener",
|
|
100
|
+
"resource",
|
|
101
|
+
"view",
|
|
102
|
+
"test",
|
|
103
|
+
"mail",
|
|
104
|
+
"notification",
|
|
105
|
+
"seeder",
|
|
106
|
+
"factory",
|
|
107
|
+
"provider",
|
|
108
|
+
"exception",
|
|
109
|
+
"command",
|
|
110
|
+
"channel"
|
|
111
|
+
]).describe("Component type to generate"),
|
|
112
|
+
name: z.string().describe("Component name in PascalCase"),
|
|
113
|
+
force: z.boolean().default(false).describe("Overwrite existing files")
|
|
114
|
+
},
|
|
115
|
+
async ({ type, name, force }) => {
|
|
116
|
+
const originalCwd = process.cwd();
|
|
117
|
+
try {
|
|
118
|
+
process.chdir(cwd);
|
|
119
|
+
const makers = {
|
|
120
|
+
controller: cli.makeController,
|
|
121
|
+
model: cli.makeModel,
|
|
122
|
+
view: cli.makeView,
|
|
123
|
+
test: cli.makeTest,
|
|
124
|
+
route: cli.makeRoute
|
|
125
|
+
};
|
|
126
|
+
const maker = makers[type];
|
|
127
|
+
if (!maker) {
|
|
128
|
+
return {
|
|
129
|
+
content: [
|
|
130
|
+
{
|
|
131
|
+
type: "text",
|
|
132
|
+
text: `Component type "${type}" is not yet supported via MCP. Use the CLI: bunx guren make:${type} ${name}`
|
|
133
|
+
}
|
|
134
|
+
],
|
|
135
|
+
isError: true
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const result = await maker(name, { force });
|
|
139
|
+
const created = Array.isArray(result) ? result : [result];
|
|
140
|
+
return {
|
|
141
|
+
content: [{ type: "text", text: JSON.stringify({ created }, null, 2) }]
|
|
142
|
+
};
|
|
143
|
+
} finally {
|
|
144
|
+
process.chdir(originalCwd);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
server.tool(
|
|
149
|
+
"guren_codegen",
|
|
150
|
+
"Generate type-safe route, page, data, and channel type manifests (.guren/*.gen.ts files).",
|
|
151
|
+
{},
|
|
152
|
+
async () => {
|
|
153
|
+
const originalCwd = process.cwd();
|
|
154
|
+
try {
|
|
155
|
+
process.chdir(cwd);
|
|
156
|
+
const generated = [];
|
|
157
|
+
try {
|
|
158
|
+
await cli.generateRouteTypes({ cwd });
|
|
159
|
+
generated.push(".guren/routes.gen.ts");
|
|
160
|
+
} catch {
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
await cli.generatePageTypes({ cwd });
|
|
164
|
+
generated.push(".guren/pages.gen.ts");
|
|
165
|
+
} catch {
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
await cli.generateDataTypes({ cwd });
|
|
169
|
+
generated.push(".guren/data.gen.ts");
|
|
170
|
+
} catch {
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
await cli.generateChannelTypes({ cwd });
|
|
174
|
+
generated.push(".guren/channels.gen.ts");
|
|
175
|
+
} catch {
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
content: [{ type: "text", text: JSON.stringify({ generated }, null, 2) }]
|
|
179
|
+
};
|
|
180
|
+
} finally {
|
|
181
|
+
process.chdir(originalCwd);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
server.resource(
|
|
186
|
+
"context",
|
|
187
|
+
"guren://context",
|
|
188
|
+
{
|
|
189
|
+
description: "Current project structure map (models, routes, pages, controllers, etc.)",
|
|
190
|
+
mimeType: "application/json"
|
|
191
|
+
},
|
|
192
|
+
async (uri) => {
|
|
193
|
+
const ctx = await cli.generateContext({ cwd });
|
|
194
|
+
return {
|
|
195
|
+
contents: [
|
|
196
|
+
{
|
|
197
|
+
uri: uri.href,
|
|
198
|
+
mimeType: "application/json",
|
|
199
|
+
text: JSON.stringify(ctx, null, 2)
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
server.resource(
|
|
206
|
+
"guidelines",
|
|
207
|
+
"guren://guidelines",
|
|
208
|
+
{
|
|
209
|
+
description: "Auto-generated project-specific coding guidelines",
|
|
210
|
+
mimeType: "text/markdown"
|
|
211
|
+
},
|
|
212
|
+
async (uri) => {
|
|
213
|
+
const guidelines = await cli.generateGuidelines({ cwd });
|
|
214
|
+
return {
|
|
215
|
+
contents: [
|
|
216
|
+
{
|
|
217
|
+
uri: uri.href,
|
|
218
|
+
mimeType: "text/markdown",
|
|
219
|
+
text: guidelines
|
|
220
|
+
}
|
|
221
|
+
]
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
);
|
|
225
|
+
server.prompt(
|
|
226
|
+
"guren_review",
|
|
227
|
+
"Review code changes against project conventions and patterns. Automatically fetches project context and runs integrity checks first.",
|
|
228
|
+
async () => {
|
|
229
|
+
let contextSummary;
|
|
230
|
+
try {
|
|
231
|
+
const ctx = await cli.generateContext({ cwd });
|
|
232
|
+
const check = await cli.runCheck({ cwd });
|
|
233
|
+
contextSummary = [
|
|
234
|
+
"## Project Context",
|
|
235
|
+
`Framework: ${ctx.framework.name} v${ctx.framework.version}`,
|
|
236
|
+
`Models: ${ctx.models.map((m) => m.className).join(", ") || "none"}`,
|
|
237
|
+
`Controllers: ${ctx.controllers.join(", ") || "none"}`,
|
|
238
|
+
`Pages: ${ctx.pages.join(", ") || "none"}`,
|
|
239
|
+
"",
|
|
240
|
+
"## Integrity Check",
|
|
241
|
+
`Pass: ${check.passCount}, Warn: ${check.warnCount}, Fail: ${check.failCount}`,
|
|
242
|
+
...check.checks.filter((c) => c.status !== "pass").map((c) => `- [${c.status}] ${c.title}: ${c.message}`)
|
|
243
|
+
].join("\n");
|
|
244
|
+
} catch {
|
|
245
|
+
contextSummary = "Could not load project context.";
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
messages: [
|
|
249
|
+
{
|
|
250
|
+
role: "user",
|
|
251
|
+
content: {
|
|
252
|
+
type: "text",
|
|
253
|
+
text: [
|
|
254
|
+
"Review the recent code changes in this Guren project.",
|
|
255
|
+
"Check for:",
|
|
256
|
+
"1. Adherence to project naming conventions and patterns",
|
|
257
|
+
"2. Route-controller-page consistency",
|
|
258
|
+
"3. Missing validation schemas",
|
|
259
|
+
"4. Missing tests for new controllers",
|
|
260
|
+
"5. Proper use of Model API (findOrFail, relationships)",
|
|
261
|
+
"",
|
|
262
|
+
contextSummary
|
|
263
|
+
].join("\n")
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
]
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
);
|
|
270
|
+
server.prompt(
|
|
271
|
+
"guren_plan_feature",
|
|
272
|
+
"Plan a new feature given the current project structure. Provide the feature description as an argument.",
|
|
273
|
+
{ feature: z.string().describe("Description of the feature to plan") },
|
|
274
|
+
async ({ feature }) => {
|
|
275
|
+
let contextSummary;
|
|
276
|
+
try {
|
|
277
|
+
const ctx = await cli.generateContext({ cwd });
|
|
278
|
+
const models = await cli.listModels({ appRoot: cwd });
|
|
279
|
+
contextSummary = [
|
|
280
|
+
"## Current Project State",
|
|
281
|
+
`Models: ${models.map((m) => `${m.className}${m.tableName ? ` (${m.tableName})` : ""}`).join(", ") || "none"}`,
|
|
282
|
+
`Controllers: ${ctx.controllers.join(", ") || "none"}`,
|
|
283
|
+
`Routes: ${ctx.routes.length} defined`,
|
|
284
|
+
`Pages: ${ctx.pages.join(", ") || "none"}`,
|
|
285
|
+
"",
|
|
286
|
+
"## Model Relationships",
|
|
287
|
+
...models.flatMap(
|
|
288
|
+
(m) => m.relationships.length > 0 ? [
|
|
289
|
+
`${m.className}: ${m.relationships.map((r) => `${r.type}(${r.name})`).join(", ")}`
|
|
290
|
+
] : []
|
|
291
|
+
)
|
|
292
|
+
].join("\n");
|
|
293
|
+
} catch {
|
|
294
|
+
contextSummary = "Could not load project context.";
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
messages: [
|
|
298
|
+
{
|
|
299
|
+
role: "user",
|
|
300
|
+
content: {
|
|
301
|
+
type: "text",
|
|
302
|
+
text: [
|
|
303
|
+
`Plan the implementation of: ${feature}`,
|
|
304
|
+
"",
|
|
305
|
+
"Provide:",
|
|
306
|
+
"1. Which files to create/modify",
|
|
307
|
+
"2. Database schema changes (migration)",
|
|
308
|
+
"3. Model definition with relationships",
|
|
309
|
+
"4. Controller actions and validation schemas",
|
|
310
|
+
"5. Inertia page components",
|
|
311
|
+
"6. Route definitions",
|
|
312
|
+
"7. Test plan",
|
|
313
|
+
"",
|
|
314
|
+
contextSummary
|
|
315
|
+
].join("\n")
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
]
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
);
|
|
322
|
+
return server;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// src/container/ServiceProvider.ts
|
|
326
|
+
var ServiceProvider = class {
|
|
327
|
+
constructor(container) {
|
|
328
|
+
this.container = container;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Whether this provider should be deferred.
|
|
332
|
+
* Deferred providers are only loaded when one of their provided services is requested.
|
|
333
|
+
*/
|
|
334
|
+
static deferred = false;
|
|
335
|
+
/**
|
|
336
|
+
* The services provided by this provider (for deferred loading).
|
|
337
|
+
*/
|
|
338
|
+
static provides = [];
|
|
339
|
+
/**
|
|
340
|
+
* Get the services provided by the provider.
|
|
341
|
+
*/
|
|
342
|
+
provides() {
|
|
343
|
+
return this.constructor.provides;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Check if this provider is deferred.
|
|
347
|
+
*/
|
|
348
|
+
isDeferred() {
|
|
349
|
+
return this.constructor.deferred;
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
// src/mcp/McpServiceProvider.ts
|
|
354
|
+
var McpServiceProvider = class extends ServiceProvider {
|
|
355
|
+
register() {
|
|
356
|
+
}
|
|
357
|
+
async boot() {
|
|
358
|
+
if (process.env.NODE_ENV === "production") {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
const app = this.container.make("app");
|
|
362
|
+
const hono = app.hono;
|
|
363
|
+
const cwd = process.cwd();
|
|
364
|
+
const [{ WebStandardStreamableHTTPServerTransport }, cli] = await Promise.all([
|
|
365
|
+
import("@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js"),
|
|
366
|
+
// @ts-ignore — @guren/cli is available at runtime via the app's dependencies
|
|
367
|
+
import("@guren/cli")
|
|
368
|
+
]);
|
|
369
|
+
hono.all("/_guren/mcp", async (c) => {
|
|
370
|
+
const mcpServer = createMcpServer({ cwd, cli });
|
|
371
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
372
|
+
sessionIdGenerator: void 0
|
|
373
|
+
});
|
|
374
|
+
await mcpServer.connect(transport);
|
|
375
|
+
return transport.handleRequest(c.req.raw);
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
export {
|
|
380
|
+
McpServiceProvider,
|
|
381
|
+
createMcpServer
|
|
382
|
+
};
|