ios-vibe-mcp01 0.1.4 → 0.1.5
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/index.js +11 -7
- package/dist/iosVibe.js +14 -51
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
3
|
import { registerIosVibeTool } from "./iosVibe.js";
|
|
5
|
-
const server = new McpServer({
|
|
6
|
-
name: "iOS Vibe Factory",
|
|
7
|
-
version: "0.1.0",
|
|
8
|
-
});
|
|
9
|
-
registerIosVibeTool(server);
|
|
10
4
|
async function main() {
|
|
5
|
+
// MCP server metadata (Cursor’da da görünür)
|
|
6
|
+
const server = new McpServer({
|
|
7
|
+
name: "ios-vibe-factory",
|
|
8
|
+
version: "0.1.5",
|
|
9
|
+
});
|
|
10
|
+
// ✅ Tool’ları register et
|
|
11
|
+
registerIosVibeTool(server);
|
|
12
|
+
// ✅ MCP server’ı başlat (Cursor bununla tool listesini alır)
|
|
11
13
|
const transport = new StdioServerTransport();
|
|
12
14
|
await server.connect(transport);
|
|
15
|
+
// (opsiyonel) log
|
|
16
|
+
console.log("ios-vibe-mcp01 running (MCP server started)");
|
|
13
17
|
}
|
|
14
18
|
main().catch((err) => {
|
|
15
|
-
console.error("
|
|
19
|
+
console.error("Failed to start ios-vibe-mcp01:", err);
|
|
16
20
|
process.exit(1);
|
|
17
21
|
});
|
package/dist/iosVibe.js
CHANGED
|
@@ -3,8 +3,8 @@ import { renderFilesForCommand } from "./templates.js";
|
|
|
3
3
|
import { validateWorkflow } from "./rules.js";
|
|
4
4
|
const CommandSchema = z.enum(["plan", "architecture", "coding", "uiux", "test", "review", "ship"]);
|
|
5
5
|
const InputSchema = z.object({
|
|
6
|
-
command: CommandSchema,
|
|
7
|
-
idea: z.string().optional(),
|
|
6
|
+
command: CommandSchema.describe("Which stage of the iOS app workflow to run"),
|
|
7
|
+
idea: z.string().optional().describe("Your app idea (required for plan)"),
|
|
8
8
|
config: z
|
|
9
9
|
.object({
|
|
10
10
|
appName: z.string().default("MyApp"),
|
|
@@ -13,7 +13,8 @@ const InputSchema = z.object({
|
|
|
13
13
|
tone: z.string().default("soft pink"),
|
|
14
14
|
constraints: z.array(z.string()).default(["offline-first", "no firebase", "mvvm", "repository"]),
|
|
15
15
|
})
|
|
16
|
-
.optional()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("Optional project configuration"),
|
|
17
18
|
state: z
|
|
18
19
|
.object({
|
|
19
20
|
projectBlueprintMd: z.string().optional(),
|
|
@@ -26,14 +27,9 @@ const InputSchema = z.object({
|
|
|
26
27
|
runbookMd: z.string().optional(),
|
|
27
28
|
backlogMd: z.string().optional(),
|
|
28
29
|
})
|
|
29
|
-
.optional()
|
|
30
|
+
.optional()
|
|
31
|
+
.describe("Previous stage outputs (auto-managed)"),
|
|
30
32
|
});
|
|
31
|
-
/**
|
|
32
|
-
* Accepts both:
|
|
33
|
-
* 1) JSON input (object): { command: "plan", idea: "..." }
|
|
34
|
-
* 2) Human text input (string): "plan: ..." or "architecture" etc.
|
|
35
|
-
* Also supports { text: "plan: ..." } shape as a convenience.
|
|
36
|
-
*/
|
|
37
33
|
function normalizeInput(raw) {
|
|
38
34
|
const text = typeof raw === "string"
|
|
39
35
|
? raw
|
|
@@ -43,71 +39,38 @@ function normalizeInput(raw) {
|
|
|
43
39
|
if (!text)
|
|
44
40
|
return raw;
|
|
45
41
|
const t = text.trim();
|
|
46
|
-
// plan: <idea>
|
|
47
42
|
const planMatch = t.match(/^plan\s*:\s*(.+)$/i);
|
|
48
|
-
if (planMatch)
|
|
43
|
+
if (planMatch)
|
|
49
44
|
return { command: "plan", idea: planMatch[1].trim() };
|
|
50
|
-
}
|
|
51
|
-
// Single-word commands
|
|
52
45
|
const lower = t.toLowerCase();
|
|
53
46
|
const allowed = ["architecture", "coding", "uiux", "test", "review", "ship"];
|
|
54
|
-
if (allowed.includes(lower))
|
|
47
|
+
if (allowed.includes(lower))
|
|
55
48
|
return { command: lower };
|
|
56
|
-
|
|
57
|
-
// If user writes just "plan" without idea, let workflow validator handle it
|
|
58
|
-
if (/^plan$/i.test(t)) {
|
|
49
|
+
if (/^plan$/i.test(t))
|
|
59
50
|
return { command: "plan", idea: "" };
|
|
60
|
-
}
|
|
61
|
-
// Fallback: keep original raw so Zod can throw a clear error
|
|
62
51
|
return raw;
|
|
63
52
|
}
|
|
64
53
|
export function registerIosVibeTool(server) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// or "text" wrappers depending on the client
|
|
68
|
-
command: z.string().optional(),
|
|
69
|
-
idea: z.string().optional(),
|
|
70
|
-
config: z.any().optional(),
|
|
71
|
-
state: z.any().optional(),
|
|
72
|
-
text: z.string().optional(),
|
|
73
|
-
}, async (raw) => {
|
|
54
|
+
// ✅ Use InputSchema.shape for maximum SDK compatibility
|
|
55
|
+
server.tool("ios_vibe", InputSchema.shape, async (raw) => {
|
|
74
56
|
const normalized = normalizeInput(raw);
|
|
75
57
|
const parsed = InputSchema.safeParse(normalized);
|
|
76
58
|
if (!parsed.success) {
|
|
77
|
-
return {
|
|
78
|
-
content: [
|
|
79
|
-
{
|
|
80
|
-
type: "text",
|
|
81
|
-
text: `Invalid input: ${parsed.error.message}`,
|
|
82
|
-
},
|
|
83
|
-
],
|
|
84
|
-
};
|
|
59
|
+
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.message}` }] };
|
|
85
60
|
}
|
|
86
61
|
const input = parsed.data;
|
|
87
|
-
// 1) Workflow gatekeeping
|
|
88
62
|
const flow = validateWorkflow(input);
|
|
89
63
|
if (!flow.ok) {
|
|
90
64
|
return {
|
|
91
65
|
content: [
|
|
92
66
|
{
|
|
93
67
|
type: "text",
|
|
94
|
-
text: `Command blocked: ${flow.reason}\n\n
|
|
95
|
-
`Fix: ${flow.fix}\n\n` +
|
|
96
|
-
`Suggested next: ${flow.suggestedNext}`,
|
|
68
|
+
text: `Command blocked: ${flow.reason}\n\nFix: ${flow.fix}\n\nSuggested next: ${flow.suggestedNext}`,
|
|
97
69
|
},
|
|
98
70
|
],
|
|
99
71
|
};
|
|
100
72
|
}
|
|
101
|
-
// 2) Generate files
|
|
102
73
|
const result = renderFilesForCommand(input);
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
content: [
|
|
106
|
-
{
|
|
107
|
-
type: "text",
|
|
108
|
-
text: JSON.stringify(result, null, 2),
|
|
109
|
-
},
|
|
110
|
-
],
|
|
111
|
-
};
|
|
74
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
112
75
|
});
|
|
113
76
|
}
|
package/package.json
CHANGED