swallowkit 1.0.0-beta.19 → 1.0.0-beta.20

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.
Files changed (75) hide show
  1. package/README.ja.md +36 -4
  2. package/README.md +36 -4
  3. package/dist/cli/commands/add-auth.d.ts.map +1 -1
  4. package/dist/cli/commands/add-auth.js +2 -0
  5. package/dist/cli/commands/add-auth.js.map +1 -1
  6. package/dist/cli/commands/add-connector.d.ts.map +1 -1
  7. package/dist/cli/commands/add-connector.js +2 -0
  8. package/dist/cli/commands/add-connector.js.map +1 -1
  9. package/dist/cli/commands/create-model.d.ts +0 -4
  10. package/dist/cli/commands/create-model.d.ts.map +1 -1
  11. package/dist/cli/commands/create-model.js +19 -145
  12. package/dist/cli/commands/create-model.js.map +1 -1
  13. package/dist/cli/commands/init.d.ts.map +1 -1
  14. package/dist/cli/commands/init.js +2 -0
  15. package/dist/cli/commands/init.js.map +1 -1
  16. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  17. package/dist/cli/commands/scaffold.js +22 -10
  18. package/dist/cli/commands/scaffold.js.map +1 -1
  19. package/dist/cli/index.d.ts.map +1 -1
  20. package/dist/cli/index.js +5 -0
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/core/operations/create-model.d.ts +15 -0
  23. package/dist/core/operations/create-model.d.ts.map +1 -0
  24. package/dist/core/operations/create-model.js +171 -0
  25. package/dist/core/operations/create-model.js.map +1 -0
  26. package/dist/core/operations/runtime.d.ts +32 -0
  27. package/dist/core/operations/runtime.d.ts.map +1 -0
  28. package/dist/core/operations/runtime.js +225 -0
  29. package/dist/core/operations/runtime.js.map +1 -0
  30. package/dist/core/operations/scaffold-machine.d.ts +16 -0
  31. package/dist/core/operations/scaffold-machine.d.ts.map +1 -0
  32. package/dist/core/operations/scaffold-machine.js +63 -0
  33. package/dist/core/operations/scaffold-machine.js.map +1 -0
  34. package/dist/core/project/manifest.d.ts +92 -0
  35. package/dist/core/project/manifest.d.ts.map +1 -0
  36. package/dist/core/project/manifest.js +321 -0
  37. package/dist/core/project/manifest.js.map +1 -0
  38. package/dist/core/project/validation.d.ts +20 -0
  39. package/dist/core/project/validation.d.ts.map +1 -0
  40. package/dist/core/project/validation.js +204 -0
  41. package/dist/core/project/validation.js.map +1 -0
  42. package/dist/machine/contracts.d.ts +16 -0
  43. package/dist/machine/contracts.d.ts.map +1 -0
  44. package/dist/machine/contracts.js +3 -0
  45. package/dist/machine/contracts.js.map +1 -0
  46. package/dist/machine/errors.d.ts +11 -0
  47. package/dist/machine/errors.d.ts.map +1 -0
  48. package/dist/machine/errors.js +34 -0
  49. package/dist/machine/errors.js.map +1 -0
  50. package/dist/machine/index.d.ts +3 -0
  51. package/dist/machine/index.d.ts.map +1 -0
  52. package/dist/machine/index.js +156 -0
  53. package/dist/machine/index.js.map +1 -0
  54. package/dist/mcp/index.d.ts +25 -0
  55. package/dist/mcp/index.d.ts.map +1 -0
  56. package/dist/mcp/index.js +184 -0
  57. package/dist/mcp/index.js.map +1 -0
  58. package/package.json +6 -4
  59. package/src/__tests__/machine.test.ts +212 -0
  60. package/src/__tests__/mcp.test.ts +56 -0
  61. package/src/cli/commands/add-auth.ts +2 -0
  62. package/src/cli/commands/add-connector.ts +2 -0
  63. package/src/cli/commands/create-model.ts +19 -168
  64. package/src/cli/commands/init.ts +3 -0
  65. package/src/cli/commands/scaffold.ts +27 -10
  66. package/src/cli/index.ts +6 -0
  67. package/src/core/operations/create-model.ts +174 -0
  68. package/src/core/operations/runtime.ts +235 -0
  69. package/src/core/operations/scaffold-machine.ts +91 -0
  70. package/src/core/project/manifest.ts +402 -0
  71. package/src/core/project/validation.ts +221 -0
  72. package/src/machine/contracts.ts +17 -0
  73. package/src/machine/errors.ts +34 -0
  74. package/src/machine/index.ts +173 -0
  75. package/src/mcp/index.ts +185 -0
@@ -0,0 +1,173 @@
1
+ import { Command, CommanderError } from "commander";
2
+ import { createModelOperation } from "../core/operations/create-model";
3
+ import { runMachineScaffoldOperation } from "../core/operations/scaffold-machine";
4
+ import { loadProjectManifest } from "../core/project/manifest";
5
+ import { validateProject } from "../core/project/validation";
6
+ import { MachineErrorResponse, MachineResponse, MachineSuccessResponse } from "./contracts";
7
+ import { MachineCommandError, toMachineError } from "./errors";
8
+
9
+ function writeMachineResponse<TData>(response: MachineResponse<TData>): void {
10
+ process.stdout.write(`${JSON.stringify(response, null, 2)}\n`);
11
+ }
12
+
13
+ function writeMachineSuccess<TData>(command: string, data: TData): void {
14
+ const response: MachineSuccessResponse<TData> = {
15
+ ok: true,
16
+ command,
17
+ data,
18
+ };
19
+ writeMachineResponse(response);
20
+ }
21
+
22
+ function writeMachineError(command: string, error: unknown): void {
23
+ const response: MachineErrorResponse = {
24
+ ok: false,
25
+ command,
26
+ error: toMachineError(error),
27
+ };
28
+ writeMachineResponse(response);
29
+ }
30
+
31
+ async function handleMachineAction<TData>(
32
+ command: string,
33
+ action: () => Promise<TData>
34
+ ): Promise<void> {
35
+ try {
36
+ writeMachineSuccess(command, await action());
37
+ } catch (error) {
38
+ writeMachineError(command, error);
39
+ process.exitCode = 1;
40
+ }
41
+ }
42
+
43
+ function createMachineProgram(): Command {
44
+ const program = new Command();
45
+ program
46
+ .name("swallowkit machine")
47
+ .description("SwallowKit machine-readable CLI for AI and MCP integrations")
48
+ .showHelpAfterError(false)
49
+ .configureOutput({
50
+ writeErr: () => undefined,
51
+ writeOut: () => undefined,
52
+ });
53
+
54
+ const inspect = new Command("inspect");
55
+ inspect
56
+ .command("project")
57
+ .description("Inspect SwallowKit project metadata")
58
+ .action(async () => {
59
+ await handleMachineAction("inspect-project", async () => {
60
+ const loaded = await loadProjectManifest();
61
+ return {
62
+ manifestSource: loaded.source,
63
+ diagnostics: loaded.diagnostics,
64
+ manifest: loaded.manifest,
65
+ };
66
+ });
67
+ });
68
+
69
+ inspect
70
+ .command("entities")
71
+ .description("Inspect SwallowKit entities")
72
+ .action(async () => {
73
+ await handleMachineAction("inspect-entities", async () => {
74
+ const loaded = await loadProjectManifest();
75
+ return {
76
+ manifestSource: loaded.source,
77
+ diagnostics: loaded.diagnostics,
78
+ entities: loaded.manifest.entities,
79
+ };
80
+ });
81
+ });
82
+
83
+ inspect
84
+ .command("routes")
85
+ .description("Inspect SwallowKit routes")
86
+ .action(async () => {
87
+ await handleMachineAction("inspect-routes", async () => {
88
+ const loaded = await loadProjectManifest();
89
+ return {
90
+ manifestSource: loaded.source,
91
+ diagnostics: loaded.diagnostics,
92
+ routes: loaded.manifest.routes,
93
+ };
94
+ });
95
+ });
96
+
97
+ const validate = new Command("validate");
98
+ validate
99
+ .command("project")
100
+ .description("Validate SwallowKit project metadata and conventions")
101
+ .action(async () => {
102
+ await handleMachineAction("validate-project", async () => validateProject());
103
+ });
104
+
105
+ const generate = new Command("generate");
106
+ generate
107
+ .command("model")
108
+ .description("Generate model templates with deterministic JSON output")
109
+ .argument("<names...>", "Model names to generate")
110
+ .option("--models-dir <dir>", "Models directory", "shared/models")
111
+ .option("--connector <name>", "Associate the models with a configured connector")
112
+ .option("--overwrite <mode>", "Overwrite policy: always | never", "never")
113
+ .action(async (names: string[], options: { modelsDir?: string; connector?: string; overwrite?: string }) => {
114
+ await handleMachineAction("generate-model", async () => {
115
+ if (options.overwrite !== "always" && options.overwrite !== "never") {
116
+ throw new MachineCommandError(
117
+ "invalid-overwrite-mode",
118
+ `Unsupported overwrite mode: ${options.overwrite}. Use "always" or "never".`
119
+ );
120
+ }
121
+
122
+ return createModelOperation({
123
+ names,
124
+ modelsDir: options.modelsDir,
125
+ connector: options.connector,
126
+ overwriteMode: options.overwrite,
127
+ });
128
+ });
129
+ });
130
+
131
+ generate
132
+ .command("scaffold")
133
+ .description("Generate scaffold artifacts with deterministic JSON output")
134
+ .argument("<model>", "Model file or model name")
135
+ .option("--functions-dir <dir>", "Functions directory", "functions")
136
+ .option("--api-dir <dir>", "API routes directory", "app/api")
137
+ .option("--api-only", "Generate only API artifacts", false)
138
+ .action(async (model: string, options: { functionsDir?: string; apiDir?: string; apiOnly?: boolean }) => {
139
+ await handleMachineAction("generate-scaffold", async () => runMachineScaffoldOperation({
140
+ model,
141
+ functionsDir: options.functionsDir,
142
+ apiDir: options.apiDir,
143
+ apiOnly: options.apiOnly,
144
+ }));
145
+ });
146
+
147
+ program.addCommand(inspect);
148
+ program.addCommand(validate);
149
+ program.addCommand(generate);
150
+ return program;
151
+ }
152
+
153
+ export function isMachineCommand(argv: string[]): boolean {
154
+ return argv[2] === "machine";
155
+ }
156
+
157
+ export async function runMachineCli(argv: string[] = process.argv): Promise<void> {
158
+ const program = createMachineProgram();
159
+ program.exitOverride();
160
+
161
+ try {
162
+ await program.parseAsync(argv.slice(3), { from: "user" });
163
+ } catch (error) {
164
+ if (error instanceof CommanderError) {
165
+ writeMachineError("machine-parse", new MachineCommandError("invalid-command", error.message));
166
+ process.exitCode = Number.isFinite(error.exitCode) ? error.exitCode : 1;
167
+ return;
168
+ }
169
+
170
+ writeMachineError("machine-parse", error);
171
+ process.exitCode = 1;
172
+ }
173
+ }
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env node
2
+
3
+ import * as path from "path";
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio";
6
+ import * as z from "zod/v4";
7
+ import type { MachineResponse } from "../machine/contracts";
8
+
9
+ interface MachineSuccessPayload<TData> {
10
+ ok: true;
11
+ command: string;
12
+ data: TData;
13
+ }
14
+
15
+ type MachineCliRunner = (args: string[]) => Promise<{ stdout: string; stderr: string; exitCode: number }>;
16
+ type ToolContentResult = { content: Array<{ type: "text"; text: string }> };
17
+ type ToolDefinition = {
18
+ name: string;
19
+ description: string;
20
+ inputSchema: z.ZodTypeAny;
21
+ handler: (input: any) => Promise<ToolContentResult>;
22
+ };
23
+
24
+ function resolveMachineCliEntrypoint(): string {
25
+ return path.resolve(__dirname, "..", "cli", "index.js");
26
+ }
27
+
28
+ async function defaultMachineCliRunner(args: string[]): Promise<{ stdout: string; stderr: string; exitCode: number }> {
29
+ const { execa } = await import("execa");
30
+ const result = await execa(process.execPath, [resolveMachineCliEntrypoint(), "machine", ...args], {
31
+ reject: false,
32
+ });
33
+
34
+ return {
35
+ stdout: result.stdout,
36
+ stderr: result.stderr,
37
+ exitCode: result.exitCode ?? 0,
38
+ };
39
+ }
40
+
41
+ async function executeMachineCommand<TData>(
42
+ args: string[],
43
+ runMachineCli: MachineCliRunner
44
+ ): Promise<MachineSuccessPayload<TData>> {
45
+ const result = await runMachineCli(args);
46
+
47
+ let parsed: MachineResponse<TData>;
48
+ try {
49
+ parsed = JSON.parse(result.stdout) as MachineResponse<TData>;
50
+ } catch {
51
+ throw new Error(result.stderr || result.stdout || "Machine CLI returned invalid JSON.");
52
+ }
53
+
54
+ if (!parsed.ok) {
55
+ throw new Error(parsed.error.message);
56
+ }
57
+
58
+ return parsed;
59
+ }
60
+
61
+ function jsonTextContent(value: unknown): { content: Array<{ type: "text"; text: string }> } {
62
+ return {
63
+ content: [
64
+ {
65
+ type: "text",
66
+ text: JSON.stringify(value, null, 2),
67
+ },
68
+ ],
69
+ };
70
+ }
71
+
72
+ export function buildSwallowKitToolDefinitions(
73
+ runMachineCli: MachineCliRunner = defaultMachineCliRunner
74
+ ): ToolDefinition[] {
75
+ return [
76
+ {
77
+ name: "swallowkit_inspect_project",
78
+ description: "Return framework-owned SwallowKit project metadata.",
79
+ inputSchema: z.object({}),
80
+ handler: async () => {
81
+ const response = await executeMachineCommand(["inspect", "project"], runMachineCli);
82
+ return jsonTextContent(response.data);
83
+ },
84
+ },
85
+ {
86
+ name: "swallowkit_inspect_entities",
87
+ description: "Return SwallowKit entities, schema metadata, and connector/auth annotations.",
88
+ inputSchema: z.object({}),
89
+ handler: async () => {
90
+ const response = await executeMachineCommand(["inspect", "entities"], runMachineCli);
91
+ return jsonTextContent(response.data);
92
+ },
93
+ },
94
+ {
95
+ name: "swallowkit_inspect_routes",
96
+ description: "Return BFF and Functions route metadata understood by SwallowKit.",
97
+ inputSchema: z.object({}),
98
+ handler: async () => {
99
+ const response = await executeMachineCommand(["inspect", "routes"], runMachineCli);
100
+ return jsonTextContent(response.data);
101
+ },
102
+ },
103
+ {
104
+ name: "swallowkit_validate_project",
105
+ description: "Validate project metadata, generated artifacts, and framework conventions.",
106
+ inputSchema: z.object({}),
107
+ handler: async () => {
108
+ const response = await executeMachineCommand(["validate", "project"], runMachineCli);
109
+ return jsonTextContent(response.data);
110
+ },
111
+ },
112
+ {
113
+ name: "swallowkit_generate_model",
114
+ description: "Generate SwallowKit model templates through the official generator.",
115
+ inputSchema: z.object({
116
+ names: z.array(z.string()).min(1),
117
+ modelsDir: z.string().optional(),
118
+ connector: z.string().optional(),
119
+ overwrite: z.enum(["always", "never"]).optional(),
120
+ }),
121
+ handler: async ({ names, modelsDir, connector, overwrite }: { names: string[]; modelsDir?: string; connector?: string; overwrite?: "always" | "never" }) => {
122
+ const response = await executeMachineCommand(["generate", "model", ...names, ...(modelsDir ? ["--models-dir", modelsDir] : []), ...(connector ? ["--connector", connector] : []), "--overwrite", overwrite || "never"], runMachineCli);
123
+ return jsonTextContent(response.data);
124
+ },
125
+ },
126
+ {
127
+ name: "swallowkit_scaffold_model",
128
+ description: "Generate SwallowKit scaffold artifacts through the official generator.",
129
+ inputSchema: z.object({
130
+ model: z.string(),
131
+ functionsDir: z.string().optional(),
132
+ apiDir: z.string().optional(),
133
+ apiOnly: z.boolean().optional(),
134
+ }),
135
+ handler: async ({ model, functionsDir, apiDir, apiOnly }: { model: string; functionsDir?: string; apiDir?: string; apiOnly?: boolean }) => {
136
+ const args = ["generate", "scaffold", model];
137
+ if (functionsDir) {
138
+ args.push("--functions-dir", functionsDir);
139
+ }
140
+ if (apiDir) {
141
+ args.push("--api-dir", apiDir);
142
+ }
143
+ if (apiOnly) {
144
+ args.push("--api-only");
145
+ }
146
+
147
+ const response = await executeMachineCommand(args, runMachineCli);
148
+ return jsonTextContent(response.data);
149
+ },
150
+ },
151
+ ];
152
+ }
153
+
154
+ export function createSwallowKitMcpServer(runMachineCli: MachineCliRunner = defaultMachineCliRunner): McpServer {
155
+ const server = new McpServer({
156
+ name: "swallowkit-mcp",
157
+ version: process.env.npm_package_version || "0.0.0",
158
+ });
159
+
160
+ for (const tool of buildSwallowKitToolDefinitions(runMachineCli)) {
161
+ server.registerTool(
162
+ tool.name,
163
+ {
164
+ description: tool.description,
165
+ inputSchema: tool.inputSchema,
166
+ },
167
+ tool.handler
168
+ );
169
+ }
170
+
171
+ return server;
172
+ }
173
+
174
+ export async function runMcpServer(): Promise<void> {
175
+ const server = createSwallowKitMcpServer();
176
+ const transport = new StdioServerTransport();
177
+ await server.connect(transport);
178
+ }
179
+
180
+ if (require.main === module) {
181
+ void runMcpServer().catch((error) => {
182
+ console.error(error);
183
+ process.exitCode = 1;
184
+ });
185
+ }