catalyst-core-internal 0.1.4 → 0.1.6
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/bin/catalyst.js +1 -8
- package/changelog.md +21 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/BridgeMessageValidator.kt +1 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +1 -12
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +3 -18
- package/dist/native/buildAppAndroid.js +2 -2
- package/dist/native/buildAppIos.js +17 -10
- package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +2 -13
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +0 -6
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +0 -4
- package/mcp_v2/conversion-tasks.json +326 -0
- package/mcp_v2/knowledge-base.json +1068 -0
- package/mcp_v2/lib/helpers.js +170 -0
- package/mcp_v2/mcp.js +563 -0
- package/mcp_v2/package.json +13 -0
- package/mcp_v2/schema.sql +88 -0
- package/mcp_v2/setup.js +282 -0
- package/mcp_v2/tools/build.js +686 -0
- package/mcp_v2/tools/config.js +453 -0
- package/mcp_v2/tools/conversion.js +799 -0
- package/mcp_v2/tools/debug.js +113 -0
- package/mcp_v2/tools/knowledge.js +219 -0
- package/mcp_v2/tools/sync.js +23 -0
- package/mcp_v2/tools/tasks.js +945 -0
- package/package.json +7 -15
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/CatalystPlugin.kt +0 -7
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/GeneratedPluginIndex.kt +0 -6
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/PluginBridge.kt +0 -253
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/plugins/PluginBridgeTest.kt +0 -139
- package/dist/native/internal-plugins/device-info-plugin/android/DeviceInfoPlugin.kt +0 -43
- package/dist/native/internal-plugins/device-info-plugin/ios/DeviceInfoPlugin.swift +0 -28
- package/dist/native/internal-plugins/device-info-plugin/manifest.json +0 -19
- package/dist/native/internalPluginUtils.js +0 -1
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/CatalystPlugin.swift +0 -5
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/GeneratedPluginIndex.swift +0 -6
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/PluginBridge.swift +0 -364
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WeakScriptMessageHandler.swift +0 -14
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/PluginBridgeTests.swift +0 -160
- package/dist/native/plugin-bridge/PluginBridge.js +0 -1
- package/dist/native/pluginComposerAndroid.js +0 -9
- package/dist/native/pluginComposerIos.js +0 -7
- package/dist/scripts/plugins.js +0 -1
- package/license +0 -10
package/mcp_v2/mcp.js
ADDED
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Catalyst MCP v2 — mcp.js (entry point)
|
|
4
|
+
*
|
|
5
|
+
* Startup validation → module init → JSON-RPC loop over stdio.
|
|
6
|
+
*
|
|
7
|
+
* Hard fails if:
|
|
8
|
+
* - No Catalyst package in package.json (not a catalyst project)
|
|
9
|
+
* - context.db missing (setup.js not run)
|
|
10
|
+
*
|
|
11
|
+
* Tool routing: LLM reads tool name + description to route.
|
|
12
|
+
* No classifier code at runtime — descriptions use intent language.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require("fs")
|
|
16
|
+
const path = require("path")
|
|
17
|
+
const readline = require("readline")
|
|
18
|
+
const Database = require("better-sqlite3")
|
|
19
|
+
|
|
20
|
+
const { findCatalystRoot } = require("./lib/helpers")
|
|
21
|
+
const conversion = require("./tools/conversion")
|
|
22
|
+
const config = require("./tools/config")
|
|
23
|
+
const debug = require("./tools/debug")
|
|
24
|
+
const build = require("./tools/build")
|
|
25
|
+
const tasks = require("./tools/tasks")
|
|
26
|
+
const sync = require("./tools/sync")
|
|
27
|
+
const knowledge = require("./tools/knowledge")
|
|
28
|
+
|
|
29
|
+
const MCP_DIR = __dirname
|
|
30
|
+
const DB_PATH = path.join(MCP_DIR, "context.db")
|
|
31
|
+
const TASKS_PATH = path.join(MCP_DIR, "conversion-tasks.json")
|
|
32
|
+
|
|
33
|
+
// ── Startup validation ────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
const projectInfo = findCatalystRoot()
|
|
36
|
+
if (!projectInfo) {
|
|
37
|
+
process.stderr.write(
|
|
38
|
+
JSON.stringify({
|
|
39
|
+
jsonrpc: "2.0",
|
|
40
|
+
error: {
|
|
41
|
+
code: -32000,
|
|
42
|
+
message:
|
|
43
|
+
"Not a Catalyst project. No catalyst-core or catalyst-core-internal dependency found in package.json.",
|
|
44
|
+
},
|
|
45
|
+
id: null,
|
|
46
|
+
}) + "\n"
|
|
47
|
+
)
|
|
48
|
+
process.exit(1)
|
|
49
|
+
}
|
|
50
|
+
if (projectInfo.notInstalled) {
|
|
51
|
+
process.stderr.write(
|
|
52
|
+
JSON.stringify({
|
|
53
|
+
jsonrpc: "2.0",
|
|
54
|
+
error: {
|
|
55
|
+
code: -32000,
|
|
56
|
+
message: `${projectInfo.catalystPackageName} is listed in package.json (${projectInfo.catalystVersion}) but not installed in node_modules. Run: npm install`,
|
|
57
|
+
},
|
|
58
|
+
id: null,
|
|
59
|
+
}) + "\n"
|
|
60
|
+
)
|
|
61
|
+
process.exit(1)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!fs.existsSync(DB_PATH)) {
|
|
65
|
+
process.stderr.write(
|
|
66
|
+
JSON.stringify({
|
|
67
|
+
jsonrpc: "2.0",
|
|
68
|
+
error: {
|
|
69
|
+
code: -32000,
|
|
70
|
+
message: `context.db not found at ${DB_PATH}. Run setup first: node ${path.join(MCP_DIR, "setup.js")}`,
|
|
71
|
+
},
|
|
72
|
+
id: null,
|
|
73
|
+
}) + "\n"
|
|
74
|
+
)
|
|
75
|
+
process.exit(1)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const db = new Database(DB_PATH, { readonly: false })
|
|
79
|
+
const CONVERSION_TASKS = JSON.parse(fs.readFileSync(TASKS_PATH, "utf8"))
|
|
80
|
+
|
|
81
|
+
// ── Module init ───────────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
conversion.init(projectInfo, CONVERSION_TASKS)
|
|
84
|
+
config.init(projectInfo)
|
|
85
|
+
debug.init(db)
|
|
86
|
+
build.init(db)
|
|
87
|
+
tasks.init(db)
|
|
88
|
+
sync.init(db)
|
|
89
|
+
knowledge.init(db)
|
|
90
|
+
|
|
91
|
+
// ── Intent classification (internal, not exposed as tool) ─────────────────────
|
|
92
|
+
|
|
93
|
+
const INTENT_PATTERNS = {
|
|
94
|
+
// execute = wants to DO something — only intent that should chain create_task_plan
|
|
95
|
+
execute: /\b(convert|migrat|implement|set\s*up|add\s+support|upgrade|rewrite|refactor|integrate)\b/i,
|
|
96
|
+
// guidance = wants to UNDERSTAND something — answer only, no task planning
|
|
97
|
+
guidance:
|
|
98
|
+
/\b(what\s+is|what\s+are|how\s+does|how\s+do|explain|show\s+me|tell\s+me|hook|api|usage|example)\b/i,
|
|
99
|
+
status: /status|done|complet|finish|check.*config|config.*check|what.*(left|remain|todo|next|pending)|how far|progress/i,
|
|
100
|
+
debug: /error|fail|broken|not work|crash|issue|bug|why|wrong/i,
|
|
101
|
+
build: /build|compile|webpack|bundle|android|ios|platform/i,
|
|
102
|
+
sync: /sync|update.*doc|fetch.*doc|latest.*doc/i,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// next_action tells the LLM exactly what to do after getting this response
|
|
106
|
+
const INTENT_NEXT_ACTION = {
|
|
107
|
+
execute: "create_task_plan — user wants to implement something. Plan and execute.",
|
|
108
|
+
guidance: "answer_only — user asked a question. Return the answer. Do NOT call create_task_plan.",
|
|
109
|
+
status: "answer_only — show status or config check result. Do NOT call create_task_plan.",
|
|
110
|
+
debug: "answer_only — provide debug guidance. Do NOT call create_task_plan.",
|
|
111
|
+
build: "answer_only — explain build flow. Do NOT call create_task_plan.",
|
|
112
|
+
sync: "answer_only — sync complete. Do NOT call create_task_plan.",
|
|
113
|
+
unknown: "answer_only — unclear intent. Return what you found. Do NOT call create_task_plan.",
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function classifyIntent(query) {
|
|
117
|
+
const intents = []
|
|
118
|
+
for (const [intent, pattern] of Object.entries(INTENT_PATTERNS)) {
|
|
119
|
+
if (pattern.test(query)) intents.push(intent)
|
|
120
|
+
}
|
|
121
|
+
const primary = intents[0] || "unknown"
|
|
122
|
+
return {
|
|
123
|
+
intents,
|
|
124
|
+
primary,
|
|
125
|
+
out_of_scope:
|
|
126
|
+
intents.length === 0 && !/catalyst|config|router|bridge|native|build|convert/i.test(query),
|
|
127
|
+
next_action: INTENT_NEXT_ACTION[primary],
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ── Tool definitions ──────────────────────────────────────────────────────────
|
|
132
|
+
|
|
133
|
+
const TOOLS = [
|
|
134
|
+
{
|
|
135
|
+
name: "get_conversion_tasks",
|
|
136
|
+
description:
|
|
137
|
+
"Use when the developer asks: 'what do I need to convert?', 'what's left to do?', 'show me pending tasks', 'what do I fix next?', 'give me a fix guide'. Runs live detection on project files and returns only the tasks relevant to THIS project (features it actually uses). Not-applicable tasks (features the project doesn't use) are hidden by default.",
|
|
138
|
+
inputSchema: {
|
|
139
|
+
type: "object",
|
|
140
|
+
properties: {
|
|
141
|
+
project_path: {
|
|
142
|
+
type: "string",
|
|
143
|
+
description: "Path to the catalyst project root. Defaults to cwd.",
|
|
144
|
+
},
|
|
145
|
+
filter: {
|
|
146
|
+
type: "string",
|
|
147
|
+
enum: ["all", "critical", "native", "enhancements"],
|
|
148
|
+
description:
|
|
149
|
+
"Filter by tier: 'critical' = Tier 1 (routing/config/server), 'native' = Tier 2 (WEBVIEW_CONFIG/icons), 'enhancements' = Tier 3 (hooks). Defaults to all.",
|
|
150
|
+
},
|
|
151
|
+
include_not_applicable: {
|
|
152
|
+
type: "boolean",
|
|
153
|
+
description:
|
|
154
|
+
"Include tasks where the feature is not used in this project. Default false.",
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: "get_conversion_status",
|
|
161
|
+
description:
|
|
162
|
+
"Use when the developer asks: 'how far along am I?', 'what have I completed?', 'show my conversion progress', 'what's done vs pending?'. Auto-detects which tasks apply to THIS project based on what web features it actually uses. Tasks for unused features are not_applicable and hidden by default.",
|
|
163
|
+
inputSchema: {
|
|
164
|
+
type: "object",
|
|
165
|
+
properties: {
|
|
166
|
+
project_path: {
|
|
167
|
+
type: "string",
|
|
168
|
+
description: "Path to the catalyst project root. Defaults to cwd.",
|
|
169
|
+
},
|
|
170
|
+
include_not_applicable: {
|
|
171
|
+
type: "boolean",
|
|
172
|
+
description:
|
|
173
|
+
"Include tasks where the feature is not used in this project. Default false.",
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: "debug_issue",
|
|
180
|
+
description:
|
|
181
|
+
"Use when the developer reports an error, something is broken, or asks 'why is X not working?'. Matches the symptom against known_errors in the DB and returns cause + fix. Also queries relevant framework_knowledge rows by layer.",
|
|
182
|
+
inputSchema: {
|
|
183
|
+
type: "object",
|
|
184
|
+
properties: {
|
|
185
|
+
symptom: {
|
|
186
|
+
type: "string",
|
|
187
|
+
description: "The error message, behavior, or problem description.",
|
|
188
|
+
},
|
|
189
|
+
layer: {
|
|
190
|
+
type: "string",
|
|
191
|
+
enum: ["Config", "Build", "Bridge", "Runtime", "Component"],
|
|
192
|
+
description: "Optional layer hint to narrow search.",
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
required: ["symptom"],
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: "check_config",
|
|
200
|
+
description:
|
|
201
|
+
"Use when the developer asks: 'is my config correct?', 'check my WEBVIEW_CONFIG', 'validate my setup', 'what's wrong with my config?'. Reads WEBVIEW_CONFIG from the project and validates required fields for both platforms.",
|
|
202
|
+
inputSchema: {
|
|
203
|
+
type: "object",
|
|
204
|
+
properties: {
|
|
205
|
+
project_path: {
|
|
206
|
+
type: "string",
|
|
207
|
+
description: "Path to the catalyst project root. Defaults to cwd.",
|
|
208
|
+
},
|
|
209
|
+
platform: {
|
|
210
|
+
type: "string",
|
|
211
|
+
enum: ["android", "ios", "both"],
|
|
212
|
+
description: "Which platform config to check. Defaults to both.",
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: "get_build_flow",
|
|
219
|
+
description:
|
|
220
|
+
"Use when the developer asks about building, serving, or deploying — e.g. 'how do I build for android?', 'walk me through the release build', 'how do I serve in production?', 'my android build is failing', 'how does the iOS debug build work?'. Returns step-by-step build flow adapted to the project's actual config, with warnings for missing config and related known errors.",
|
|
221
|
+
inputSchema: {
|
|
222
|
+
type: "object",
|
|
223
|
+
properties: {
|
|
224
|
+
platform: {
|
|
225
|
+
type: "string",
|
|
226
|
+
enum: ["android", "ios", "web"],
|
|
227
|
+
description: "Target platform.",
|
|
228
|
+
},
|
|
229
|
+
mode: {
|
|
230
|
+
type: "string",
|
|
231
|
+
enum: ["dev", "build", "production", "staging", "release"],
|
|
232
|
+
description: "Build/serve mode. Defaults to dev for web, debug for native.",
|
|
233
|
+
},
|
|
234
|
+
symptom: {
|
|
235
|
+
type: "string",
|
|
236
|
+
description:
|
|
237
|
+
'Optional: describe the error or problem (e.g. "gradle build fails", "app not installing"). Surfaces related known errors alongside the flow.',
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
required: ["platform"],
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: "get_architecture_diagram",
|
|
245
|
+
description:
|
|
246
|
+
"Use when the developer asks how something works architecturally — e.g. 'how does the universal app work?', 'explain the bridge', 'show me request lifecycle', 'how does routing work?', 'what part of the config controls X?', 'show me the build pipeline'. Returns a layered a→b→c flow diagram with project-specific context and known pitfalls.",
|
|
247
|
+
inputSchema: {
|
|
248
|
+
type: "object",
|
|
249
|
+
properties: {
|
|
250
|
+
feature: {
|
|
251
|
+
type: "string",
|
|
252
|
+
description:
|
|
253
|
+
'What to diagram. Examples: "universal app", "request lifecycle", "bridge architecture", "build pipeline", "routing". Free text — will be matched to the best diagram.',
|
|
254
|
+
},
|
|
255
|
+
symptom: {
|
|
256
|
+
type: "string",
|
|
257
|
+
description:
|
|
258
|
+
'Optional: describe a problem (e.g. "bridge not responding"). Surfaces related known errors alongside the diagram.',
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
required: ["feature"],
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
name: "create_task_plan",
|
|
266
|
+
description:
|
|
267
|
+
"Use when the developer wants to DO something — 'help me convert this app', 'plan out the migration', 'I want to add camera support', 'guide me through implementing X'. Intent: execute. Creates a step-by-step plan persisted in DB — survives context resets.",
|
|
268
|
+
inputSchema: {
|
|
269
|
+
type: "object",
|
|
270
|
+
properties: {
|
|
271
|
+
goal: {
|
|
272
|
+
type: "string",
|
|
273
|
+
description:
|
|
274
|
+
'One sentence describing what the developer wants to accomplish. e.g. "Convert 1mg_web to universal app" or "Add camera support to checkout flow".',
|
|
275
|
+
},
|
|
276
|
+
steps: {
|
|
277
|
+
type: "array",
|
|
278
|
+
description:
|
|
279
|
+
"Optional: provide custom steps as strings or {title, detail} objects. If omitted, steps are auto-generated from the goal.",
|
|
280
|
+
items: { type: ["string", "object"] },
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
required: ["goal"],
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
name: "update_task_step",
|
|
288
|
+
description:
|
|
289
|
+
"Use when the developer says: 'mark step X done', 'I finished step 2', 'this step is blocked', 'move to next step', 'add a note to step 1'. Updates the active plan in DB and auto-advances to the next step.",
|
|
290
|
+
inputSchema: {
|
|
291
|
+
type: "object",
|
|
292
|
+
properties: {
|
|
293
|
+
step_index: { type: "number", description: "Zero-based index of the step to update." },
|
|
294
|
+
status: {
|
|
295
|
+
type: "string",
|
|
296
|
+
enum: ["done", "blocked", "skipped", "in_progress", "pending"],
|
|
297
|
+
description: 'New status for the step. Defaults to "done".',
|
|
298
|
+
},
|
|
299
|
+
note: {
|
|
300
|
+
type: "string",
|
|
301
|
+
description:
|
|
302
|
+
"Optional note or finding to attach to this step (e.g. what was discovered, why it is blocked).",
|
|
303
|
+
},
|
|
304
|
+
plan_slug: {
|
|
305
|
+
type: "string",
|
|
306
|
+
description:
|
|
307
|
+
"Optional: target a specific plan by slug. Defaults to the current active plan.",
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
required: ["step_index"],
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
name: "get_active_task",
|
|
315
|
+
description:
|
|
316
|
+
"Use when the developer asks: 'what was I doing?', 'where did I leave off?', 'resume my task', 'what is the current step?', 'show me my plan'. Returns current active plan with pending steps and progress. Call this at the start of any session to restore context.",
|
|
317
|
+
inputSchema: {
|
|
318
|
+
type: "object",
|
|
319
|
+
properties: {
|
|
320
|
+
include_all_steps: {
|
|
321
|
+
type: "boolean",
|
|
322
|
+
description:
|
|
323
|
+
"If true, include completed and skipped steps too. Default: false (only pending/in_progress/blocked).",
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
name: "close_task_plan",
|
|
330
|
+
description:
|
|
331
|
+
"Use when the developer says: 'close the task', 'I am done', 'mark plan complete', 'finish the task', 'clean up the task file'. All steps must be terminal (done/skipped/blocked) before closing. Asks the developer whether to delete the task MD file. If delete_file:true, deletes .mcp_tasks/<slug>.md and removes the DB record. If delete_file:false (default), keeps the file for review and marks it done.",
|
|
332
|
+
inputSchema: {
|
|
333
|
+
type: "object",
|
|
334
|
+
properties: {
|
|
335
|
+
delete_file: {
|
|
336
|
+
type: "boolean",
|
|
337
|
+
description:
|
|
338
|
+
"If true, delete the .mcp_tasks/<slug>.md file after closing. If false (default), keep it for reference.",
|
|
339
|
+
},
|
|
340
|
+
plan_slug: {
|
|
341
|
+
type: "string",
|
|
342
|
+
description:
|
|
343
|
+
"Optional: target a specific plan by slug. Defaults to the current active or last completed plan.",
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
name: "query_knowledge",
|
|
350
|
+
description:
|
|
351
|
+
"Use when the developer asks ANY question about Catalyst — how-to, conceptual, debugging, or setup. Examples: 'how do I add meta tags?', 'how does routing work?', 'what hooks are available?', 'how do I set the page title?', 'explain the bridge', 'list all native hooks', 'how does getDeviceInfo work', 'how do I add SEO?', 'how do I use setMetaData?'. Intent: guidance. CRITICAL: Before calling, expand the user's plain-English terms into Catalyst-specific technical keywords. Examples: 'meta tags' → ['setMetaData', 'metaTags', 'Head', 'seo']; 'page title' → ['setMetaData', 'title', 'meta']; 'SEO' → ['setMetaData', 'MetaTag', 'seo', 'head']; 'fonts' → ['document', 'Head', 'custom-document']. Always include both the user's words AND the likely Catalyst API names. Do NOT pass a section unless you are certain — omit section when unsure to search all sections. Do NOT search node_modules or the filesystem — use this tool instead.",
|
|
352
|
+
inputSchema: {
|
|
353
|
+
type: "object",
|
|
354
|
+
properties: {
|
|
355
|
+
query: {
|
|
356
|
+
type: "string",
|
|
357
|
+
description:
|
|
358
|
+
"The original user question verbatim. Used for display and intent classification.",
|
|
359
|
+
},
|
|
360
|
+
keywords: {
|
|
361
|
+
type: "array",
|
|
362
|
+
items: { type: "string" },
|
|
363
|
+
description:
|
|
364
|
+
'Catalyst-specific technical terms expanded from the user query. Always expand plain-English to API names: "meta tags" → ["setMetaData","metaTags","Head","seo"]; "page title" → ["setMetaData","title"]; "SEO" → ["setMetaData","MetaTag","seo"]; "native hooks" → ["useCamera","useFilePicker","hooks"]; "data fetching" → ["serverFetcher","serverDataFetcher"]; "routing" → ["getRoutes","matchPath","RouterDataProvider"]. Include both user terms and expanded API names for best FTS match.',
|
|
365
|
+
},
|
|
366
|
+
section: {
|
|
367
|
+
type: "string",
|
|
368
|
+
enum: [
|
|
369
|
+
"framework_identity",
|
|
370
|
+
"data_fetching",
|
|
371
|
+
"routing",
|
|
372
|
+
"native_hooks",
|
|
373
|
+
"bridge_architecture",
|
|
374
|
+
"build_system",
|
|
375
|
+
"caching",
|
|
376
|
+
"config_structure",
|
|
377
|
+
"known_errors",
|
|
378
|
+
"security",
|
|
379
|
+
"server_structure",
|
|
380
|
+
"seo_metadata",
|
|
381
|
+
"transport_architecture",
|
|
382
|
+
"webview_config",
|
|
383
|
+
],
|
|
384
|
+
description:
|
|
385
|
+
"Optional: only pass when you are certain of the section. Omit if unsure — wrong section returns zero results.",
|
|
386
|
+
},
|
|
387
|
+
github_files: {
|
|
388
|
+
type: "array",
|
|
389
|
+
items: { type: "string" },
|
|
390
|
+
description:
|
|
391
|
+
'Optional: catalyst-core source file paths to fetch from GitHub if KB has no match. E.g. ["src/native/bridge/WebBridge.js", "src/native/bridge/hooks.js"]. Use when you know where in the source the answer lives.',
|
|
392
|
+
},
|
|
393
|
+
_query: {
|
|
394
|
+
type: "string",
|
|
395
|
+
description: "Pass the original user query here verbatim for intent classification.",
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
required: ["query"],
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
name: "sync_catalyst_docs",
|
|
403
|
+
description:
|
|
404
|
+
"Use when the developer asks: 'sync docs', 'update framework knowledge', 'fetch latest catalyst docs'. Intent: sync. Fetches changelog and template diffs, updates the KB. Maintenance only — no task planning needed after.",
|
|
405
|
+
inputSchema: {
|
|
406
|
+
type: "object",
|
|
407
|
+
properties: {
|
|
408
|
+
force: {
|
|
409
|
+
type: "boolean",
|
|
410
|
+
description: "Force re-fetch all pages even if unchanged. Defaults to false.",
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
]
|
|
416
|
+
|
|
417
|
+
// ── Tool handler dispatch ─────────────────────────────────────────────────────
|
|
418
|
+
|
|
419
|
+
const TOOL_HANDLERS = {
|
|
420
|
+
get_conversion_tasks: conversion.handle_get_conversion_tasks,
|
|
421
|
+
get_conversion_status: conversion.handle_get_conversion_status,
|
|
422
|
+
debug_issue: debug.handle_debug_issue,
|
|
423
|
+
check_config: config.handle_check_config,
|
|
424
|
+
get_build_flow: build.handle_get_build_flow,
|
|
425
|
+
get_architecture_diagram: build.handle_get_architecture_diagram,
|
|
426
|
+
create_task_plan: tasks.handle_create_task_plan,
|
|
427
|
+
update_task_step: tasks.handle_update_task_step,
|
|
428
|
+
get_active_task: tasks.handle_get_active_task,
|
|
429
|
+
close_task_plan: tasks.handle_close_task_plan,
|
|
430
|
+
sync_catalyst_docs: sync.handle_sync_catalyst_docs,
|
|
431
|
+
query_knowledge: knowledge.handle_query_knowledge,
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// ── MCP JSON-RPC over stdio ───────────────────────────────────────────────────
|
|
435
|
+
|
|
436
|
+
const rl = readline.createInterface({ input: process.stdin })
|
|
437
|
+
|
|
438
|
+
function send(obj) {
|
|
439
|
+
process.stdout.write(JSON.stringify(obj) + "\n")
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function wrapWithIntent(result, detectedIntent) {
|
|
443
|
+
if (detectedIntent) {
|
|
444
|
+
result._intent = { primary: detectedIntent.primary, next_action: detectedIntent.next_action }
|
|
445
|
+
}
|
|
446
|
+
return result
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
rl.on("line", (line) => {
|
|
450
|
+
let msg
|
|
451
|
+
try {
|
|
452
|
+
msg = JSON.parse(line)
|
|
453
|
+
} catch {
|
|
454
|
+
return
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const { id, method, params } = msg
|
|
458
|
+
|
|
459
|
+
if (method === "initialize") {
|
|
460
|
+
send({
|
|
461
|
+
jsonrpc: "2.0",
|
|
462
|
+
id,
|
|
463
|
+
result: {
|
|
464
|
+
protocolVersion: "2024-11-05",
|
|
465
|
+
capabilities: { tools: {} },
|
|
466
|
+
serverInfo: {
|
|
467
|
+
name: "catalyst-mcp",
|
|
468
|
+
version: "2.0.0",
|
|
469
|
+
description: `Catalyst MCP v2 — ${projectInfo.pkg.name || projectInfo.dir} (catalyst-core@${projectInfo.catalystVersion})`,
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
})
|
|
473
|
+
return
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (method === "tools/list") {
|
|
477
|
+
send({ jsonrpc: "2.0", id, result: { tools: TOOLS } })
|
|
478
|
+
return
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (method === "tools/call") {
|
|
482
|
+
const { name, arguments: args } = params
|
|
483
|
+
|
|
484
|
+
// Internal intent classification — runs on every query, never exposed as a tool
|
|
485
|
+
let detectedIntent = null
|
|
486
|
+
if (args && args._query) {
|
|
487
|
+
detectedIntent = classifyIntent(args._query)
|
|
488
|
+
if (detectedIntent.out_of_scope) {
|
|
489
|
+
send({
|
|
490
|
+
jsonrpc: "2.0",
|
|
491
|
+
id,
|
|
492
|
+
result: {
|
|
493
|
+
content: [
|
|
494
|
+
{
|
|
495
|
+
type: "text",
|
|
496
|
+
text: JSON.stringify({
|
|
497
|
+
error: "out_of_scope",
|
|
498
|
+
message:
|
|
499
|
+
"This query is outside Catalyst MCP scope. MCP handles: conversion tracking, debugging, config validation, build flow, architecture, task planning, and doc sync.",
|
|
500
|
+
}),
|
|
501
|
+
},
|
|
502
|
+
],
|
|
503
|
+
},
|
|
504
|
+
})
|
|
505
|
+
return
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const handler = TOOL_HANDLERS[name]
|
|
510
|
+
if (!handler) {
|
|
511
|
+
send({ jsonrpc: "2.0", id, error: { code: -32601, message: `Unknown tool: ${name}` } })
|
|
512
|
+
return
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
try {
|
|
516
|
+
const maybePromise = handler(args || {})
|
|
517
|
+
if (maybePromise && typeof maybePromise.then === "function") {
|
|
518
|
+
maybePromise
|
|
519
|
+
.then((result) =>
|
|
520
|
+
send({
|
|
521
|
+
jsonrpc: "2.0",
|
|
522
|
+
id,
|
|
523
|
+
result: {
|
|
524
|
+
content: [
|
|
525
|
+
{
|
|
526
|
+
type: "text",
|
|
527
|
+
text: JSON.stringify(wrapWithIntent(result, detectedIntent), null, 2),
|
|
528
|
+
},
|
|
529
|
+
],
|
|
530
|
+
},
|
|
531
|
+
})
|
|
532
|
+
)
|
|
533
|
+
.catch((e) => send({ jsonrpc: "2.0", id, error: { code: -32000, message: e.message } }))
|
|
534
|
+
} else {
|
|
535
|
+
send({
|
|
536
|
+
jsonrpc: "2.0",
|
|
537
|
+
id,
|
|
538
|
+
result: {
|
|
539
|
+
content: [
|
|
540
|
+
{
|
|
541
|
+
type: "text",
|
|
542
|
+
text: JSON.stringify(wrapWithIntent(maybePromise, detectedIntent), null, 2),
|
|
543
|
+
},
|
|
544
|
+
],
|
|
545
|
+
},
|
|
546
|
+
})
|
|
547
|
+
}
|
|
548
|
+
} catch (e) {
|
|
549
|
+
send({ jsonrpc: "2.0", id, error: { code: -32000, message: e.message } })
|
|
550
|
+
}
|
|
551
|
+
return
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// notifications (initialized, etc.) — no response needed
|
|
555
|
+
if (!id) return
|
|
556
|
+
|
|
557
|
+
send({ jsonrpc: "2.0", id, error: { code: -32601, message: `Method not found: ${method}` } })
|
|
558
|
+
})
|
|
559
|
+
|
|
560
|
+
process.on("SIGINT", () => {
|
|
561
|
+
db.close()
|
|
562
|
+
process.exit(0)
|
|
563
|
+
})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "catalyst-mcp",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Catalyst MCP v2 — developer workflow tools for catalyst-core projects",
|
|
5
|
+
"main": "mcp.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"setup": "node setup.js",
|
|
8
|
+
"start": "node mcp.js"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"better-sqlite3": "^9.4.3"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
-- Catalyst MCP v2 — context.db schema
|
|
2
|
+
-- Run once during setup.js. Never run from mcp.js at runtime.
|
|
3
|
+
|
|
4
|
+
CREATE TABLE IF NOT EXISTS project_context (
|
|
5
|
+
id INTEGER PRIMARY KEY,
|
|
6
|
+
repo_path TEXT NOT NULL,
|
|
7
|
+
package_name TEXT NOT NULL,
|
|
8
|
+
catalyst_version TEXT NOT NULL,
|
|
9
|
+
detected_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
-- Static knowledge (source='static') + sitemap docs (source='sitemap')
|
|
13
|
+
-- sync_catalyst_docs only touches rows where source='sitemap'
|
|
14
|
+
CREATE TABLE IF NOT EXISTS framework_knowledge (
|
|
15
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16
|
+
section TEXT NOT NULL,
|
|
17
|
+
title TEXT NOT NULL,
|
|
18
|
+
content TEXT NOT NULL,
|
|
19
|
+
layer TEXT NOT NULL, -- Config | Build | Bridge | Runtime | Component
|
|
20
|
+
source TEXT NOT NULL, -- 'static' | 'sitemap'
|
|
21
|
+
tags TEXT NOT NULL DEFAULT '[]', -- JSON array
|
|
22
|
+
url TEXT, -- only for source='sitemap'
|
|
23
|
+
github_files TEXT, -- JSON array of repo-relative paths; fetched when list-intent detected or on KB miss
|
|
24
|
+
last_updated TEXT NOT NULL DEFAULT (datetime('now'))
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
CREATE TABLE IF NOT EXISTS known_errors (
|
|
28
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
29
|
+
symptom TEXT NOT NULL,
|
|
30
|
+
cause TEXT NOT NULL,
|
|
31
|
+
fix TEXT NOT NULL,
|
|
32
|
+
layer TEXT NOT NULL,
|
|
33
|
+
tags TEXT NOT NULL DEFAULT '[]' -- JSON array
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
-- 18 conversion tasks seeded from conversion-tasks.json (Phase 2)
|
|
37
|
+
CREATE TABLE IF NOT EXISTS conversion_tasks (
|
|
38
|
+
id TEXT PRIMARY KEY, -- e.g. "config-ios-webview"
|
|
39
|
+
title TEXT NOT NULL,
|
|
40
|
+
category TEXT NOT NULL, -- config | native_apis | build_setup | security | ux
|
|
41
|
+
description TEXT NOT NULL,
|
|
42
|
+
how_to_check TEXT NOT NULL,
|
|
43
|
+
fix_guide TEXT NOT NULL,
|
|
44
|
+
depends_on TEXT NOT NULL DEFAULT '[]', -- JSON array of task ids
|
|
45
|
+
status TEXT NOT NULL DEFAULT 'pending' -- pending | done | blocked
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
-- Two snapshots max: prev + curr per URL
|
|
49
|
+
-- Used by sync_catalyst_docs for diff-based fetching
|
|
50
|
+
CREATE TABLE IF NOT EXISTS doc_snapshots (
|
|
51
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
52
|
+
url TEXT NOT NULL,
|
|
53
|
+
content_hash TEXT NOT NULL,
|
|
54
|
+
fetched_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
55
|
+
slot TEXT NOT NULL DEFAULT 'curr' -- 'prev' | 'curr'
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
-- Maps sitemap URL → framework_knowledge row(s) it updated
|
|
59
|
+
CREATE TABLE IF NOT EXISTS linkage_map (
|
|
60
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
61
|
+
url TEXT NOT NULL,
|
|
62
|
+
knowledge_id INTEGER NOT NULL REFERENCES framework_knowledge(id) ON DELETE CASCADE,
|
|
63
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
CREATE INDEX IF NOT EXISTS idx_fk_section ON framework_knowledge(section);
|
|
67
|
+
CREATE INDEX IF NOT EXISTS idx_fk_source ON framework_knowledge(source);
|
|
68
|
+
CREATE INDEX IF NOT EXISTS idx_fk_layer ON framework_knowledge(layer);
|
|
69
|
+
CREATE INDEX IF NOT EXISTS idx_ke_layer ON known_errors(layer);
|
|
70
|
+
CREATE INDEX IF NOT EXISTS idx_ct_category ON conversion_tasks(category);
|
|
71
|
+
CREATE INDEX IF NOT EXISTS idx_ct_status ON conversion_tasks(status);
|
|
72
|
+
CREATE INDEX IF NOT EXISTS idx_ds_url ON doc_snapshots(url);
|
|
73
|
+
|
|
74
|
+
-- Task plans — persisted across sessions (Phase 5)
|
|
75
|
+
-- One active plan per project_root at a time.
|
|
76
|
+
CREATE TABLE IF NOT EXISTS task_plans (
|
|
77
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
78
|
+
slug TEXT NOT NULL UNIQUE, -- kebab-case goal slug, e.g. "convert-1mg-web-to-universal"
|
|
79
|
+
goal TEXT NOT NULL, -- original goal string
|
|
80
|
+
project_root TEXT NOT NULL,
|
|
81
|
+
status TEXT NOT NULL DEFAULT 'active', -- active | completed | abandoned
|
|
82
|
+
steps TEXT NOT NULL DEFAULT '[]', -- JSON array of step objects
|
|
83
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
84
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
CREATE INDEX IF NOT EXISTS idx_tp_status ON task_plans(status);
|
|
88
|
+
CREATE INDEX IF NOT EXISTS idx_tp_project ON task_plans(project_root);
|