@moonsjs/mcp 0.1.11 → 0.2.0

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/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
  import {
3
3
  createMcpServer2
4
- } from "./index-fd4fnsn3.js";
4
+ } from "./index-a9wjs6se.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -5,6 +5,7 @@ import {
5
5
  collectRoutes,
6
6
  executeAudit,
7
7
  executeCheck,
8
+ executeDoctor,
8
9
  runTypegen
9
10
  } from "@moonsjs/cli";
10
11
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
@@ -108,6 +109,7 @@ function createTools2(options) {
108
109
  };
109
110
  },
110
111
  audit: () => executeAudit(options.cwd, { env: process.env }),
112
+ diagnose: () => executeDoctor(options.cwd),
111
113
  runTypegen: async () => {
112
114
  await runTypegen(options.cwd);
113
115
  return { ok: true, outDir: ".moons" };
@@ -239,6 +241,9 @@ function createMcpServer2(options) {
239
241
  server.registerTool("audit", {
240
242
  description: "Security audit: serverFn inventory, env() access map, cookie flags, CSP, and a dist/client scan for env-like secrets (Addendum H)."
241
243
  }, () => guard(() => tools.audit()));
244
+ server.registerTool("diagnose", {
245
+ description: "Diagnose the app: moons.config.ts loads, @moonsjs/* versions, duplicate/nested framework copies, peer packages, typegen freshness, and port availability (Addendum P)."
246
+ }, () => guard(() => tools.diagnose()));
242
247
  server.registerTool("run_typegen", {
243
248
  description: "Run typegen: writes .moons/routes.d.ts + .moons/context.d.ts for the app in cwd."
244
249
  }, () => guard(() => tools.runTypegen()));
@@ -269,4 +274,4 @@ function createMcpServer2(options) {
269
274
 
270
275
  export { CONTEXT_URL2, CONTEXT_URLS2, localContextPath2, searchMarkdown2, createTools2, createMcpServer2 };
271
276
 
272
- //# debugId=9D4C7C9F7270964764756E2164756E21
277
+ //# debugId=B8B13F9B78DBD27864756E2164756E21
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/tools.ts", "../src/server.ts"],
4
+ "sourcesContent": [
5
+ "import { MOONS_ERROR_CODES, findErrorEntry, type ErrorCatalogEntry } from \"@moonsjs/core\";\nimport {\n collectFns,\n collectRoutes,\n executeAudit,\n executeCheck,\n executeDoctor,\n runTypegen,\n type AuditJson,\n type CheckJson,\n type DoctorJson,\n type FnsJson,\n type RoutesJson,\n} from \"@moonsjs/cli\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join, relative, sep } from \"node:path\";\n\n/** Sumber teks konteks framework (default: lokal dulu, lalu GitHub raw). */\nexport type ContextFetcher = () => Promise<string>;\n\n export const CONTEXT_URL =\n \"https://raw.githubusercontent.com/moonsjs/docs/main/ai/CONTEXT.md\";\n\n/** Rantai fallback remote: situs docs → GitHub raw (repo docs). */\nexport const CONTEXT_URLS: readonly string[] = [\n \"https://docs.devmoon.net/raw/ai/CONTEXT.md\",\n CONTEXT_URL,\n];\n\n/**\n * Accept only the Markdown context. A redirect or an error page can return\n * HTTP 200 with HTML; that must not become the context an agent searches.\n */\nfunction isContextMarkdown(text: string): boolean {\n return /^#\\s+Moons\\b/.test(text.trimStart());\n}\n\n/** Path konteks offline di paket `@moonsjs/cli`, dari entry yang di-resolve. */\nexport function localContextPath(cliEntry: string): string {\n return join(dirname(cliEntry), \"..\", \"docs\", \"ai\", \"CONTEXT.md\");\n}\n\nfunction readLocalContext(): { source: string; text: string } | null {\n try {\n const entry = createRequire(import.meta.url).resolve(\"@moonsjs/cli\");\n const path = localContextPath(entry);\n if (existsSync(path)) {\n const text = readFileSync(path, \"utf8\");\n if (isContextMarkdown(text)) {\n return { source: `local:${path}`, text };\n }\n }\n } catch {\n // fallthrough ke remote\n }\n return null;\n}\n\nasync function defaultLoadContext(): Promise<{ source: string; text: string }> {\n const local = readLocalContext();\n if (local !== null) {\n return local;\n }\n let lastError: unknown = null;\n for (const url of CONTEXT_URLS) {\n try {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n const text = await response.text();\n if (!isContextMarkdown(text)) {\n throw new Error(`unexpected context payload (not Markdown) from ${url}`);\n }\n return { source: url, text };\n } catch (error) {\n lastError = error;\n }\n }\n throw new Error(\n `failed to load context from ${CONTEXT_URLS.join(\" and \")}: ${String(lastError)}`,\n );\n}\n\nexport interface DocMatch {\n /** Judul section terakhir sebelum baris ini. */\n section: string;\n /** Nomor baris (1-based) di CONTEXT.md. */\n line: number;\n text: string;\n}\n\nexport interface SearchDocsResult {\n source: string;\n query: string;\n matches: DocMatch[];\n}\n\nexport interface ExplainErrorResult {\n code: string;\n found: boolean;\n entry?: ErrorCatalogEntry;\n validCodes: string[];\n}\n\nexport interface Toolset {\n listRoutes: () => Promise<RoutesJson>;\n getFnRegistry: () => Promise<FnsJson>;\n runCheck: () => Promise<CheckJson>;\n searchDocs: (query: string) => Promise<SearchDocsResult>;\n /** Addendum B.6: kode error §15 → pemicu + saran. */\n explainError: (code: string) => Promise<ExplainErrorResult>;\n /** Addendum H: audit keamanan (RPC/env/cookie/CSP/dist) machine-readable. */\n audit: () => Promise<AuditJson>;\n /** Addendum P: diagnosa project (config, deps/duplikat, typegen, port). */\n diagnose: () => Promise<DoctorJson>;\n /** Addendum H: jalankan typegen (`.moons/*.d.ts`). */\n runTypegen: () => Promise<{ ok: true; outDir: string }>;\n /** Addendum L.4: buat file route mengikuti konvensi §11 (tanpa menimpa). */\n scaffoldRoute: (input: {\n route: string;\n kind?: \"page\" | \"api\";\n loader?: boolean;\n }) => Promise<ScaffoldRouteResult>;\n /** Addendum L.4: baca N baris terakhir `.moons/dev.log` (dikelola `moons dev`). */\n tailDevLogs: (lines: number) => Promise<TailDevLogsResult>;\n}\n\nexport interface ScaffoldRouteResult {\n file: string;\n kind: \"page\" | \"api\";\n created: true;\n}\n\nexport interface TailDevLogsResult {\n file: string;\n lines: number;\n exists: boolean;\n text: string;\n}\n\nexport interface ToolsetOptions {\n cwd: string;\n fetchContext?: ContextFetcher;\n}\n\n/** Pencarian substring case-insensitive atas markdown; returns baris + section. */\nexport function searchMarkdown(text: string, query: string, limit = 20): DocMatch[] {\n const needle = query.trim().toLowerCase();\n if (needle.length === 0) {\n return [];\n }\n\n const lines = text.split(\"\\n\");\n const matches: DocMatch[] = [];\n let section = \"(top)\";\n\n for (let index = 0; index < lines.length; index += 1) {\n const line = lines[index] as string;\n const heading = /^#{1,6}\\s+(.*)$/.exec(line);\n if (heading !== null) {\n section = (heading[1] ?? \"\").trim();\n }\n if (line.toLowerCase().includes(needle)) {\n matches.push({ section, line: index + 1, text: line.trim() });\n if (matches.length >= limit) {\n break;\n }\n }\n }\n\n return matches;\n}\n\n/** Toolset MCP (Addendum A.7) — deterministik, tanpa panggilan LLM. */\nexport function createTools(options: ToolsetOptions): Toolset {\n const loadContext =\n options.fetchContext === undefined\n ? defaultLoadContext\n : async (): Promise<{ source: string; text: string }> => ({\n source: CONTEXT_URL,\n text: await (options.fetchContext as ContextFetcher)(),\n });\n let cached: { source: string; text: string } | null = null;\n\n return {\n listRoutes: () => collectRoutes(options.cwd),\n getFnRegistry: () => Promise.resolve(collectFns(options.cwd)),\n runCheck: () => executeCheck(options.cwd, { quiet: true }),\n searchDocs: async (query: string): Promise<SearchDocsResult> => {\n if (cached === null) {\n cached = await loadContext();\n }\n return { source: cached.source, query, matches: searchMarkdown(cached.text, query) };\n },\n explainError: async (code: string): Promise<ExplainErrorResult> => {\n const normalized = code.trim().toUpperCase();\n const entry = findErrorEntry(normalized);\n return {\n code: normalized,\n found: entry !== undefined,\n ...(entry === undefined ? {} : { entry }),\n validCodes: [...MOONS_ERROR_CODES],\n };\n },\n audit: () => executeAudit(options.cwd, { env: process.env }),\n diagnose: () => executeDoctor(options.cwd),\n runTypegen: async () => {\n await runTypegen(options.cwd);\n return { ok: true as const, outDir: \".moons\" };\n },\n scaffoldRoute: async (input) => {\n const kind = input.kind ?? \"page\";\n if (kind !== \"page\" && kind !== \"api\") {\n throw new Error(`unknown kind: ${String(kind)}`);\n }\n const route = input.route.trim().replace(/^\\/+|\\/+$/g, \"\");\n if (route.length === 0) {\n throw new Error(\"`route` must not be empty (example: orders/[id])\");\n }\n for (const segment of route.split(\"/\")) {\n if (segment.length === 0 || segment === \".\" || segment === \"..\") {\n throw new Error(`invalid route segment: \\`${segment}\\``);\n }\n if (/[\\\\<>:\"|?*]/.test(segment)) {\n throw new Error(`route segment contains forbidden characters: \\`${segment}\\``);\n }\n }\n const dir = kind === \"page\" ? \"src/routes\" : \"src/api\";\n const ext = kind === \"page\" ? \".tsx\" : \".ts\";\n const file = join(options.cwd, dir, `${route}${ext}`);\n const relativeFile = relative(options.cwd, file).split(sep).join(\"/\");\n if (existsSync(file)) {\n throw new Error(`file already exists: ${relativeFile} (not overwriting)`);\n }\n mkdirSync(dirname(file), { recursive: true });\n const content =\n kind === \"page\"\n ? renderPageRoute(route, input.loader === true)\n : renderApiRoute(route);\n writeFileSync(file, content);\n return { file: relativeFile, kind, created: true as const };\n },\n tailDevLogs: async (lines) => {\n const clamped = Math.min(Math.max(Math.trunc(lines), 1), 500);\n const file = join(options.cwd, \".moons\", \"dev.log\");\n if (!existsSync(file)) {\n return {\n file: \".moons/dev.log\",\n lines: clamped,\n exists: false,\n text: \"no dev log yet — run `moons dev` first\",\n };\n }\n const text = readFileSync(file, \"utf8\");\n const all = text.length === 0 ? [] : text.replace(/\\n$/, \"\").split(\"\\n\");\n return {\n file: \".moons/dev.log\",\n lines: clamped,\n exists: true,\n text: all.slice(-clamped).join(\"\\n\"),\n };\n },\n };\n}\n\n/** Template route page (§11): default export + loader opsional. */\nfunction renderPageRoute(route: string, withLoader: boolean): string {\n if (!withLoader) {\n return [\n `// Route: /${route} — created by MCP scaffold_route.`,\n \"export default function Page() {\",\n ` return <p>/${route}</p>;`,\n \"}\",\n \"\",\n ].join(\"\\n\");\n }\n return [\n `// Route: /${route} — created by MCP scaffold_route.`,\n 'import { useLoaderData } from \"@moonsjs/react\";',\n \"\",\n \"export const loader = async () => ({ items: [] as string[] });\",\n \"\",\n \"export default function Page() {\",\n \" const data = useLoaderData<typeof loader>();\",\n \" return <p>{data.items.length} item</p>;\",\n \"}\",\n \"\",\n ].join(\"\\n\");\n}\n\n/** Template endpoint API (Addendum D.1): export method + params. */\nfunction renderApiRoute(route: string): string {\n return [\n `// Endpoint: /api/${route} — created by MCP scaffold_route.`,\n 'import type { ApiHandlerArgs } from \"@moonsjs/server\";',\n \"\",\n \"export async function GET({ params }: ApiHandlerArgs): Promise<Response> {\",\n \" return Response.json({ ok: true, params });\",\n \"}\",\n \"\",\n ].join(\"\\n\");\n}\n",
6
+ "import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { createTools, type ToolsetOptions } from \"./tools\";\n\nfunction jsonContent(value: unknown, isError = false): {\n content: Array<{ type: \"text\"; text: string }>;\n isError?: boolean;\n} {\n return {\n content: [{ type: \"text\", text: JSON.stringify(value, null, 2) }],\n ...(isError ? { isError: true } : {}),\n };\n}\n\nasync function guard(\n run: () => Promise<unknown>,\n): Promise<{ content: Array<{ type: \"text\"; text: string }>; isError?: boolean }> {\n try {\n return jsonContent(await run());\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return jsonContent({ ok: false, error: message }, true);\n }\n}\n\n/** Bangun MCP server `moons` (stdio) untuk satu project (Addendum A.7). */\nexport function createMcpServer(options: ToolsetOptions): McpServer {\n const tools = createTools(options);\n const server = new McpServer({ name: \"moons\", version: \"0.1.0\" });\n\n server.registerTool(\n \"list_routes\",\n {\n description:\n \"Route table of the Moons app (path, file, key, kind, params, layouts) — scanned, no build needed.\",\n },\n () => guard(() => tools.listRoutes()),\n );\n\n server.registerTool(\n \"get_fn_registry\",\n {\n description:\n \"serverFn registry of the Moons app (file#export → hash, line) plus the registry map written to .moons/fn-registry.json.\",\n },\n () => guard(() => tools.getFnRegistry()),\n );\n\n server.registerTool(\n \"run_check\",\n {\n description:\n \"Definition of done: typegen → tsc --noEmit → build client → build server. Returns { ok, steps }.\",\n },\n () => guard(() => tools.runCheck()),\n );\n\n server.registerTool(\n \"search_docs\",\n {\n description:\n \"Search the Moons agent context pack (docs/ai/CONTEXT.md) by substring; returns matching lines with their sections.\",\n inputSchema: { query: z.string().describe(\"substring to search (case-insensitive)\") },\n },\n ({ query }) => guard(() => tools.searchDocs(query)),\n );\n\n server.registerTool(\n \"audit\",\n {\n description:\n \"Security audit: serverFn inventory, env() access map, cookie flags, CSP, and a dist/client scan for env-like secrets (Addendum H).\",\n },\n () => guard(() => tools.audit()),\n );\n\n server.registerTool(\n \"diagnose\",\n {\n description:\n \"Diagnose the app: moons.config.ts loads, @moonsjs/* versions, duplicate/nested framework copies, peer packages, typegen freshness, and port availability (Addendum P).\",\n },\n () => guard(() => tools.diagnose()),\n );\n\n server.registerTool(\n \"run_typegen\",\n {\n description:\n \"Run typegen: writes .moons/routes.d.ts + .moons/context.d.ts for the app in cwd.\",\n },\n () => guard(() => tools.runTypegen()),\n );\n\n server.registerTool(\n \"scaffold_route\",\n {\n description:\n \"Create a route file following Moons conventions: page → src/routes/<route>.tsx (default export + optional loader), api → src/api/<route>.ts (GET handler). Never overwrites existing files (Addendum L.4).\",\n inputSchema: {\n route: z.string().describe('route path without extension, e.g. \"orders/[id]\"'),\n kind: z.enum([\"page\", \"api\"]).optional().describe(\"default: page\"),\n loader: z\n .boolean()\n .optional()\n .describe(\"page only: include a sample loader + useLoaderData\"),\n },\n },\n ({ route, kind, loader }) =>\n guard(() =>\n tools.scaffoldRoute({\n route,\n ...(kind === undefined ? {} : { kind }),\n ...(loader === undefined ? {} : { loader }),\n }),\n ),\n );\n\n server.registerTool(\n \"tail_dev_logs\",\n {\n description:\n \"Tail the CLI-managed dev log (.moons/dev.log written by `moons dev`); default 50 lines, max 500 (Addendum L.4).\",\n inputSchema: {\n lines: z.number().int().min(1).max(500).optional().describe(\"default 50\"),\n },\n },\n ({ lines }) => guard(() => tools.tailDevLogs(lines ?? 50)),\n );\n\n server.registerTool(\n \"explain_error\",\n {\n description:\n \"Explain a Moons error code (format [moons] E_...): trigger, details, and fix suggestions. Returns found=false + validCodes for unknown codes.\",\n inputSchema: { code: z.string().describe(\"error code, e.g. E_SERVER_IMPORT_IN_CLIENT\") },\n },\n ({ code }) => guard(() => tools.explainError(code)),\n );\n\n return server;\n}\n"
7
+ ],
8
+ "mappings": "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",
9
+ "debugId": "B8B13F9B78DBD27864756E2164756E21",
10
+ "names": []
11
+ }
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  searchMarkdown2,
6
6
  createTools2,
7
7
  createMcpServer2
8
- } from "./index-fd4fnsn3.js";
8
+ } from "./index-a9wjs6se.js";
9
9
  export {
10
10
  CONTEXT_URL2 as CONTEXT_URL,
11
11
  CONTEXT_URLS2 as CONTEXT_URLS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moonsjs/mcp",
3
- "version": "0.1.11",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "sideEffects": false,
@@ -33,8 +33,8 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@modelcontextprotocol/sdk": "^1.26.0",
36
- "@moonsjs/cli": "^0.2.0",
37
- "@moonsjs/core": "^0.2.0",
36
+ "@moonsjs/cli": "^0.3.0",
37
+ "@moonsjs/core": "^0.3.0",
38
38
  "zod": "^4.6.7"
39
39
  },
40
40
  "devDependencies": {
package/src/server.ts CHANGED
@@ -74,6 +74,15 @@ export function createMcpServer(options: ToolsetOptions): McpServer {
74
74
  () => guard(() => tools.audit()),
75
75
  );
76
76
 
77
+ server.registerTool(
78
+ "diagnose",
79
+ {
80
+ description:
81
+ "Diagnose the app: moons.config.ts loads, @moonsjs/* versions, duplicate/nested framework copies, peer packages, typegen freshness, and port availability (Addendum P).",
82
+ },
83
+ () => guard(() => tools.diagnose()),
84
+ );
85
+
77
86
  server.registerTool(
78
87
  "run_typegen",
79
88
  {
package/src/tools.ts CHANGED
@@ -4,9 +4,11 @@ import {
4
4
  collectRoutes,
5
5
  executeAudit,
6
6
  executeCheck,
7
+ executeDoctor,
7
8
  runTypegen,
8
9
  type AuditJson,
9
10
  type CheckJson,
11
+ type DoctorJson,
10
12
  type FnsJson,
11
13
  type RoutesJson,
12
14
  } from "@moonsjs/cli";
@@ -111,6 +113,8 @@ export interface Toolset {
111
113
  explainError: (code: string) => Promise<ExplainErrorResult>;
112
114
  /** Addendum H: audit keamanan (RPC/env/cookie/CSP/dist) machine-readable. */
113
115
  audit: () => Promise<AuditJson>;
116
+ /** Addendum P: diagnosa project (config, deps/duplikat, typegen, port). */
117
+ diagnose: () => Promise<DoctorJson>;
114
118
  /** Addendum H: jalankan typegen (`.moons/*.d.ts`). */
115
119
  runTypegen: () => Promise<{ ok: true; outDir: string }>;
116
120
  /** Addendum L.4: buat file route mengikuti konvensi §11 (tanpa menimpa). */
@@ -201,6 +205,7 @@ export function createTools(options: ToolsetOptions): Toolset {
201
205
  };
202
206
  },
203
207
  audit: () => executeAudit(options.cwd, { env: process.env }),
208
+ diagnose: () => executeDoctor(options.cwd),
204
209
  runTypegen: async () => {
205
210
  await runTypegen(options.cwd);
206
211
  return { ok: true as const, outDir: ".moons" };
@@ -1,11 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/tools.ts", "../src/server.ts"],
4
- "sourcesContent": [
5
- "import { MOONS_ERROR_CODES, findErrorEntry, type ErrorCatalogEntry } from \"@moonsjs/core\";\nimport {\n collectFns,\n collectRoutes,\n executeAudit,\n executeCheck,\n runTypegen,\n type AuditJson,\n type CheckJson,\n type FnsJson,\n type RoutesJson,\n} from \"@moonsjs/cli\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join, relative, sep } from \"node:path\";\n\n/** Sumber teks konteks framework (default: lokal dulu, lalu GitHub raw). */\nexport type ContextFetcher = () => Promise<string>;\n\n export const CONTEXT_URL =\n \"https://raw.githubusercontent.com/moonsjs/docs/main/ai/CONTEXT.md\";\n\n/** Rantai fallback remote: situs docs → GitHub raw (repo docs). */\nexport const CONTEXT_URLS: readonly string[] = [\n \"https://docs.devmoon.net/raw/ai/CONTEXT.md\",\n CONTEXT_URL,\n];\n\n/**\n * Accept only the Markdown context. A redirect or an error page can return\n * HTTP 200 with HTML; that must not become the context an agent searches.\n */\nfunction isContextMarkdown(text: string): boolean {\n return /^#\\s+Moons\\b/.test(text.trimStart());\n}\n\n/** Path konteks offline di paket `@moonsjs/cli`, dari entry yang di-resolve. */\nexport function localContextPath(cliEntry: string): string {\n return join(dirname(cliEntry), \"..\", \"docs\", \"ai\", \"CONTEXT.md\");\n}\n\nfunction readLocalContext(): { source: string; text: string } | null {\n try {\n const entry = createRequire(import.meta.url).resolve(\"@moonsjs/cli\");\n const path = localContextPath(entry);\n if (existsSync(path)) {\n const text = readFileSync(path, \"utf8\");\n if (isContextMarkdown(text)) {\n return { source: `local:${path}`, text };\n }\n }\n } catch {\n // fallthrough ke remote\n }\n return null;\n}\n\nasync function defaultLoadContext(): Promise<{ source: string; text: string }> {\n const local = readLocalContext();\n if (local !== null) {\n return local;\n }\n let lastError: unknown = null;\n for (const url of CONTEXT_URLS) {\n try {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n const text = await response.text();\n if (!isContextMarkdown(text)) {\n throw new Error(`unexpected context payload (not Markdown) from ${url}`);\n }\n return { source: url, text };\n } catch (error) {\n lastError = error;\n }\n }\n throw new Error(\n `failed to load context from ${CONTEXT_URLS.join(\" and \")}: ${String(lastError)}`,\n );\n}\n\nexport interface DocMatch {\n /** Judul section terakhir sebelum baris ini. */\n section: string;\n /** Nomor baris (1-based) di CONTEXT.md. */\n line: number;\n text: string;\n}\n\nexport interface SearchDocsResult {\n source: string;\n query: string;\n matches: DocMatch[];\n}\n\nexport interface ExplainErrorResult {\n code: string;\n found: boolean;\n entry?: ErrorCatalogEntry;\n validCodes: string[];\n}\n\nexport interface Toolset {\n listRoutes: () => Promise<RoutesJson>;\n getFnRegistry: () => Promise<FnsJson>;\n runCheck: () => Promise<CheckJson>;\n searchDocs: (query: string) => Promise<SearchDocsResult>;\n /** Addendum B.6: kode error §15 → pemicu + saran. */\n explainError: (code: string) => Promise<ExplainErrorResult>;\n /** Addendum H: audit keamanan (RPC/env/cookie/CSP/dist) machine-readable. */\n audit: () => Promise<AuditJson>;\n /** Addendum H: jalankan typegen (`.moons/*.d.ts`). */\n runTypegen: () => Promise<{ ok: true; outDir: string }>;\n /** Addendum L.4: buat file route mengikuti konvensi §11 (tanpa menimpa). */\n scaffoldRoute: (input: {\n route: string;\n kind?: \"page\" | \"api\";\n loader?: boolean;\n }) => Promise<ScaffoldRouteResult>;\n /** Addendum L.4: baca N baris terakhir `.moons/dev.log` (dikelola `moons dev`). */\n tailDevLogs: (lines: number) => Promise<TailDevLogsResult>;\n}\n\nexport interface ScaffoldRouteResult {\n file: string;\n kind: \"page\" | \"api\";\n created: true;\n}\n\nexport interface TailDevLogsResult {\n file: string;\n lines: number;\n exists: boolean;\n text: string;\n}\n\nexport interface ToolsetOptions {\n cwd: string;\n fetchContext?: ContextFetcher;\n}\n\n/** Pencarian substring case-insensitive atas markdown; returns baris + section. */\nexport function searchMarkdown(text: string, query: string, limit = 20): DocMatch[] {\n const needle = query.trim().toLowerCase();\n if (needle.length === 0) {\n return [];\n }\n\n const lines = text.split(\"\\n\");\n const matches: DocMatch[] = [];\n let section = \"(top)\";\n\n for (let index = 0; index < lines.length; index += 1) {\n const line = lines[index] as string;\n const heading = /^#{1,6}\\s+(.*)$/.exec(line);\n if (heading !== null) {\n section = (heading[1] ?? \"\").trim();\n }\n if (line.toLowerCase().includes(needle)) {\n matches.push({ section, line: index + 1, text: line.trim() });\n if (matches.length >= limit) {\n break;\n }\n }\n }\n\n return matches;\n}\n\n/** Toolset MCP (Addendum A.7) — deterministik, tanpa panggilan LLM. */\nexport function createTools(options: ToolsetOptions): Toolset {\n const loadContext =\n options.fetchContext === undefined\n ? defaultLoadContext\n : async (): Promise<{ source: string; text: string }> => ({\n source: CONTEXT_URL,\n text: await (options.fetchContext as ContextFetcher)(),\n });\n let cached: { source: string; text: string } | null = null;\n\n return {\n listRoutes: () => collectRoutes(options.cwd),\n getFnRegistry: () => Promise.resolve(collectFns(options.cwd)),\n runCheck: () => executeCheck(options.cwd, { quiet: true }),\n searchDocs: async (query: string): Promise<SearchDocsResult> => {\n if (cached === null) {\n cached = await loadContext();\n }\n return { source: cached.source, query, matches: searchMarkdown(cached.text, query) };\n },\n explainError: async (code: string): Promise<ExplainErrorResult> => {\n const normalized = code.trim().toUpperCase();\n const entry = findErrorEntry(normalized);\n return {\n code: normalized,\n found: entry !== undefined,\n ...(entry === undefined ? {} : { entry }),\n validCodes: [...MOONS_ERROR_CODES],\n };\n },\n audit: () => executeAudit(options.cwd, { env: process.env }),\n runTypegen: async () => {\n await runTypegen(options.cwd);\n return { ok: true as const, outDir: \".moons\" };\n },\n scaffoldRoute: async (input) => {\n const kind = input.kind ?? \"page\";\n if (kind !== \"page\" && kind !== \"api\") {\n throw new Error(`unknown kind: ${String(kind)}`);\n }\n const route = input.route.trim().replace(/^\\/+|\\/+$/g, \"\");\n if (route.length === 0) {\n throw new Error(\"`route` must not be empty (example: orders/[id])\");\n }\n for (const segment of route.split(\"/\")) {\n if (segment.length === 0 || segment === \".\" || segment === \"..\") {\n throw new Error(`invalid route segment: \\`${segment}\\``);\n }\n if (/[\\\\<>:\"|?*]/.test(segment)) {\n throw new Error(`route segment contains forbidden characters: \\`${segment}\\``);\n }\n }\n const dir = kind === \"page\" ? \"src/routes\" : \"src/api\";\n const ext = kind === \"page\" ? \".tsx\" : \".ts\";\n const file = join(options.cwd, dir, `${route}${ext}`);\n const relativeFile = relative(options.cwd, file).split(sep).join(\"/\");\n if (existsSync(file)) {\n throw new Error(`file already exists: ${relativeFile} (not overwriting)`);\n }\n mkdirSync(dirname(file), { recursive: true });\n const content =\n kind === \"page\"\n ? renderPageRoute(route, input.loader === true)\n : renderApiRoute(route);\n writeFileSync(file, content);\n return { file: relativeFile, kind, created: true as const };\n },\n tailDevLogs: async (lines) => {\n const clamped = Math.min(Math.max(Math.trunc(lines), 1), 500);\n const file = join(options.cwd, \".moons\", \"dev.log\");\n if (!existsSync(file)) {\n return {\n file: \".moons/dev.log\",\n lines: clamped,\n exists: false,\n text: \"no dev log yet — run `moons dev` first\",\n };\n }\n const text = readFileSync(file, \"utf8\");\n const all = text.length === 0 ? [] : text.replace(/\\n$/, \"\").split(\"\\n\");\n return {\n file: \".moons/dev.log\",\n lines: clamped,\n exists: true,\n text: all.slice(-clamped).join(\"\\n\"),\n };\n },\n };\n}\n\n/** Template route page (§11): default export + loader opsional. */\nfunction renderPageRoute(route: string, withLoader: boolean): string {\n if (!withLoader) {\n return [\n `// Route: /${route} — created by MCP scaffold_route.`,\n \"export default function Page() {\",\n ` return <p>/${route}</p>;`,\n \"}\",\n \"\",\n ].join(\"\\n\");\n }\n return [\n `// Route: /${route} — created by MCP scaffold_route.`,\n 'import { useLoaderData } from \"@moonsjs/react\";',\n \"\",\n \"export const loader = async () => ({ items: [] as string[] });\",\n \"\",\n \"export default function Page() {\",\n \" const data = useLoaderData<typeof loader>();\",\n \" return <p>{data.items.length} item</p>;\",\n \"}\",\n \"\",\n ].join(\"\\n\");\n}\n\n/** Template endpoint API (Addendum D.1): export method + params. */\nfunction renderApiRoute(route: string): string {\n return [\n `// Endpoint: /api/${route} — created by MCP scaffold_route.`,\n 'import type { ApiHandlerArgs } from \"@moonsjs/server\";',\n \"\",\n \"export async function GET({ params }: ApiHandlerArgs): Promise<Response> {\",\n \" return Response.json({ ok: true, params });\",\n \"}\",\n \"\",\n ].join(\"\\n\");\n}\n",
6
- "import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { createTools, type ToolsetOptions } from \"./tools\";\n\nfunction jsonContent(value: unknown, isError = false): {\n content: Array<{ type: \"text\"; text: string }>;\n isError?: boolean;\n} {\n return {\n content: [{ type: \"text\", text: JSON.stringify(value, null, 2) }],\n ...(isError ? { isError: true } : {}),\n };\n}\n\nasync function guard(\n run: () => Promise<unknown>,\n): Promise<{ content: Array<{ type: \"text\"; text: string }>; isError?: boolean }> {\n try {\n return jsonContent(await run());\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return jsonContent({ ok: false, error: message }, true);\n }\n}\n\n/** Bangun MCP server `moons` (stdio) untuk satu project (Addendum A.7). */\nexport function createMcpServer(options: ToolsetOptions): McpServer {\n const tools = createTools(options);\n const server = new McpServer({ name: \"moons\", version: \"0.1.0\" });\n\n server.registerTool(\n \"list_routes\",\n {\n description:\n \"Route table of the Moons app (path, file, key, kind, params, layouts) — scanned, no build needed.\",\n },\n () => guard(() => tools.listRoutes()),\n );\n\n server.registerTool(\n \"get_fn_registry\",\n {\n description:\n \"serverFn registry of the Moons app (file#export → hash, line) plus the registry map written to .moons/fn-registry.json.\",\n },\n () => guard(() => tools.getFnRegistry()),\n );\n\n server.registerTool(\n \"run_check\",\n {\n description:\n \"Definition of done: typegen → tsc --noEmit → build client → build server. Returns { ok, steps }.\",\n },\n () => guard(() => tools.runCheck()),\n );\n\n server.registerTool(\n \"search_docs\",\n {\n description:\n \"Search the Moons agent context pack (docs/ai/CONTEXT.md) by substring; returns matching lines with their sections.\",\n inputSchema: { query: z.string().describe(\"substring to search (case-insensitive)\") },\n },\n ({ query }) => guard(() => tools.searchDocs(query)),\n );\n\n server.registerTool(\n \"audit\",\n {\n description:\n \"Security audit: serverFn inventory, env() access map, cookie flags, CSP, and a dist/client scan for env-like secrets (Addendum H).\",\n },\n () => guard(() => tools.audit()),\n );\n\n server.registerTool(\n \"run_typegen\",\n {\n description:\n \"Run typegen: writes .moons/routes.d.ts + .moons/context.d.ts for the app in cwd.\",\n },\n () => guard(() => tools.runTypegen()),\n );\n\n server.registerTool(\n \"scaffold_route\",\n {\n description:\n \"Create a route file following Moons conventions: page → src/routes/<route>.tsx (default export + optional loader), api → src/api/<route>.ts (GET handler). Never overwrites existing files (Addendum L.4).\",\n inputSchema: {\n route: z.string().describe('route path without extension, e.g. \"orders/[id]\"'),\n kind: z.enum([\"page\", \"api\"]).optional().describe(\"default: page\"),\n loader: z\n .boolean()\n .optional()\n .describe(\"page only: include a sample loader + useLoaderData\"),\n },\n },\n ({ route, kind, loader }) =>\n guard(() =>\n tools.scaffoldRoute({\n route,\n ...(kind === undefined ? {} : { kind }),\n ...(loader === undefined ? {} : { loader }),\n }),\n ),\n );\n\n server.registerTool(\n \"tail_dev_logs\",\n {\n description:\n \"Tail the CLI-managed dev log (.moons/dev.log written by `moons dev`); default 50 lines, max 500 (Addendum L.4).\",\n inputSchema: {\n lines: z.number().int().min(1).max(500).optional().describe(\"default 50\"),\n },\n },\n ({ lines }) => guard(() => tools.tailDevLogs(lines ?? 50)),\n );\n\n server.registerTool(\n \"explain_error\",\n {\n description:\n \"Explain a Moons error code (format [moons] E_...): trigger, details, and fix suggestions. Returns found=false + validCodes for unknown codes.\",\n inputSchema: { code: z.string().describe(\"error code, e.g. E_SERVER_IMPORT_IN_CLIENT\") },\n },\n ({ code }) => guard(() => tools.explainError(code)),\n );\n\n return server;\n}\n"
7
- ],
8
- "mappings": "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",
9
- "debugId": "9D4C7C9F7270964764756E2164756E21",
10
- "names": []
11
- }