@moonsjs/mcp 0.1.0 → 0.1.2

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/README.md ADDED
@@ -0,0 +1,16 @@
1
+ # @moonsjs/mcp
2
+
3
+ MCP server (stdio) untuk framework [**Moons**](https://github.com/moonsjs/docs):
4
+ memberi agen AI jalan pintas terverifikasi — tabel route, registry serverFn
5
+ (hash), `moons check`, dan pencarian dokumen konteks framework.
6
+
7
+ Tool: `list_routes`, `get_fn_registry`, `run_check`, `search_docs`.
8
+
9
+ ```bash
10
+ bunx --package @moonsjs/mcp moons-mcp # bin `moons-mcp` ≠ nama package → --package
11
+ ```
12
+
13
+ - Dokumentasi API: [docs/api/ai-first.md](https://github.com/moonsjs/docs/blob/main/docs/api/ai-first.md)
14
+ - Konteks agen: [docs/ai/CONTEXT.md](https://github.com/moonsjs/docs/blob/main/docs/ai/CONTEXT.md)
15
+
16
+ MIT.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
  import {
3
3
  createMcpServer2
4
- } from "./index-hp290nyb.js";
4
+ } from "./index-weym72tx.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -4,15 +4,33 @@ import {
4
4
  collectRoutes,
5
5
  executeCheck
6
6
  } from "@moonsjs/cli";
7
- var CONTEXT_URL2 = "https://raw.githubusercontent.com/RamonymousDev/Moons/main/docs/ai/CONTEXT.md";
8
- function defaultFetcher() {
9
- return async () => {
10
- const response = await fetch(CONTEXT_URL2);
11
- if (!response.ok) {
12
- throw new Error(`gagal mengambil ${CONTEXT_URL2} (HTTP ${response.status})`);
7
+ import { existsSync, readFileSync } from "node:fs";
8
+ import { createRequire } from "node:module";
9
+ import { dirname, join } from "node:path";
10
+ var CONTEXT_URL2 = "https://raw.githubusercontent.com/moonsjs/docs/main/docs/ai/CONTEXT.md";
11
+ function localContextPath(cliEntry) {
12
+ return join(dirname(cliEntry), "..", "docs", "ai", "CONTEXT.md");
13
+ }
14
+ function readLocalContext() {
15
+ try {
16
+ const entry = createRequire(import.meta.url).resolve("@moonsjs/cli");
17
+ const path = localContextPath(entry);
18
+ if (existsSync(path)) {
19
+ return { source: `local:${path}`, text: readFileSync(path, "utf8") };
13
20
  }
14
- return await response.text();
15
- };
21
+ } catch {}
22
+ return null;
23
+ }
24
+ async function defaultLoadContext() {
25
+ const local = readLocalContext();
26
+ if (local !== null) {
27
+ return local;
28
+ }
29
+ const response = await fetch(CONTEXT_URL2);
30
+ if (!response.ok) {
31
+ throw new Error(`gagal mengambil ${CONTEXT_URL2} (HTTP ${response.status})`);
32
+ }
33
+ return { source: CONTEXT_URL2, text: await response.text() };
16
34
  }
17
35
  function searchMarkdown2(text, query, limit = 20) {
18
36
  const needle = query.trim().toLowerCase();
@@ -39,7 +57,10 @@ function searchMarkdown2(text, query, limit = 20) {
39
57
  return matches;
40
58
  }
41
59
  function createTools2(options) {
42
- const fetchContext = options.fetchContext ?? defaultFetcher();
60
+ const loadContext = options.fetchContext === undefined ? defaultLoadContext : async () => ({
61
+ source: CONTEXT_URL2,
62
+ text: await options.fetchContext()
63
+ });
43
64
  let cached = null;
44
65
  return {
45
66
  listRoutes: () => collectRoutes(options.cwd),
@@ -47,9 +68,9 @@ function createTools2(options) {
47
68
  runCheck: () => executeCheck(options.cwd, { quiet: true }),
48
69
  searchDocs: async (query) => {
49
70
  if (cached === null) {
50
- cached = await fetchContext();
71
+ cached = await loadContext();
51
72
  }
52
- return { source: CONTEXT_URL2, query, matches: searchMarkdown2(cached, query) };
73
+ return { source: cached.source, query, matches: searchMarkdown2(cached.text, query) };
53
74
  }
54
75
  };
55
76
  }
@@ -92,4 +113,4 @@ function createMcpServer2(options) {
92
113
 
93
114
  export { CONTEXT_URL2, searchMarkdown2, createTools2, createMcpServer2 };
94
115
 
95
- //# debugId=9E9BBAFBC5A8DB0664756E2164756E21
116
+ //# debugId=22A03F28E6EC57F864756E2164756E21
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/tools.ts", "../src/server.ts"],
4
+ "sourcesContent": [
5
+ "import {\n collectFns,\n collectRoutes,\n executeCheck,\n type CheckJson,\n type FnsJson,\n type RoutesJson,\n} from \"@moonsjs/cli\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\n\n/** Sumber teks konteks framework (default: lokal dulu, lalu GitHub raw). */\nexport type ContextFetcher = () => Promise<string>;\n\nexport const CONTEXT_URL =\n \"https://raw.githubusercontent.com/moonsjs/docs/main/docs/ai/CONTEXT.md\";\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 return { source: `local:${path}`, text: readFileSync(path, \"utf8\") };\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 const response = await fetch(CONTEXT_URL);\n if (!response.ok) {\n throw new Error(`gagal mengambil ${CONTEXT_URL} (HTTP ${response.status})`);\n }\n return { source: CONTEXT_URL, text: await response.text() };\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 Toolset {\n listRoutes: () => Promise<RoutesJson>;\n getFnRegistry: () => Promise<FnsJson>;\n runCheck: () => Promise<CheckJson>;\n searchDocs: (query: string) => Promise<SearchDocsResult>;\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 };\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 return server;\n}\n"
7
+ ],
8
+ "mappings": "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",
9
+ "debugId": "22A03F28E6EC57F864756E2164756E21",
10
+ "names": []
11
+ }
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  searchMarkdown2,
4
4
  createTools2,
5
5
  createMcpServer2
6
- } from "./index-hp290nyb.js";
6
+ } from "./index-weym72tx.js";
7
7
  export {
8
8
  CONTEXT_URL2 as CONTEXT_URL,
9
9
  createMcpServer2 as createMcpServer,
package/package.json CHANGED
@@ -1,17 +1,16 @@
1
1
  {
2
2
  "name": "@moonsjs/mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "sideEffects": false,
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "git+https://github.com/RamonymousDev/Moons.git",
10
- "directory": "packages/mcp"
9
+ "url": "git+https://github.com/moonsjs/mcp.git"
11
10
  },
12
- "homepage": "https://github.com/RamonymousDev/Moons#readme",
11
+ "homepage": "https://github.com/moonsjs/docs",
13
12
  "bugs": {
14
- "url": "https://github.com/RamonymousDev/Moons/issues"
13
+ "url": "https://github.com/moonsjs/mcp/issues"
15
14
  },
16
15
  "files": [
17
16
  "dist",
package/src/tools.ts CHANGED
@@ -6,12 +6,45 @@ import {
6
6
  type FnsJson,
7
7
  type RoutesJson,
8
8
  } from "@moonsjs/cli";
9
+ import { existsSync, readFileSync } from "node:fs";
10
+ import { createRequire } from "node:module";
11
+ import { dirname, join } from "node:path";
9
12
 
10
- /** Sumber teks konteks framework (default: GitHub raw; injectable untuk test). */
13
+ /** Sumber teks konteks framework (default: lokal dulu, lalu GitHub raw). */
11
14
  export type ContextFetcher = () => Promise<string>;
12
15
 
13
16
  export const CONTEXT_URL =
14
- "https://raw.githubusercontent.com/RamonymousDev/Moons/main/docs/ai/CONTEXT.md";
17
+ "https://raw.githubusercontent.com/moonsjs/docs/main/docs/ai/CONTEXT.md";
18
+
19
+ /** Path konteks offline di paket `@moonsjs/cli`, dari entry yang di-resolve. */
20
+ export function localContextPath(cliEntry: string): string {
21
+ return join(dirname(cliEntry), "..", "docs", "ai", "CONTEXT.md");
22
+ }
23
+
24
+ function readLocalContext(): { source: string; text: string } | null {
25
+ try {
26
+ const entry = createRequire(import.meta.url).resolve("@moonsjs/cli");
27
+ const path = localContextPath(entry);
28
+ if (existsSync(path)) {
29
+ return { source: `local:${path}`, text: readFileSync(path, "utf8") };
30
+ }
31
+ } catch {
32
+ // fallthrough ke remote
33
+ }
34
+ return null;
35
+ }
36
+
37
+ async function defaultLoadContext(): Promise<{ source: string; text: string }> {
38
+ const local = readLocalContext();
39
+ if (local !== null) {
40
+ return local;
41
+ }
42
+ const response = await fetch(CONTEXT_URL);
43
+ if (!response.ok) {
44
+ throw new Error(`gagal mengambil ${CONTEXT_URL} (HTTP ${response.status})`);
45
+ }
46
+ return { source: CONTEXT_URL, text: await response.text() };
47
+ }
15
48
 
16
49
  export interface DocMatch {
17
50
  /** Judul section terakhir sebelum baris ini. */
@@ -39,16 +72,6 @@ export interface ToolsetOptions {
39
72
  fetchContext?: ContextFetcher;
40
73
  }
41
74
 
42
- function defaultFetcher(): ContextFetcher {
43
- return async () => {
44
- const response = await fetch(CONTEXT_URL);
45
- if (!response.ok) {
46
- throw new Error(`gagal mengambil ${CONTEXT_URL} (HTTP ${response.status})`);
47
- }
48
- return await response.text();
49
- };
50
- }
51
-
52
75
  /** Pencarian substring case-insensitive atas markdown; returns baris + section. */
53
76
  export function searchMarkdown(text: string, query: string, limit = 20): DocMatch[] {
54
77
  const needle = query.trim().toLowerCase();
@@ -79,8 +102,14 @@ export function searchMarkdown(text: string, query: string, limit = 20): DocMatc
79
102
 
80
103
  /** Toolset MCP (Addendum A.7) — deterministik, tanpa panggilan LLM. */
81
104
  export function createTools(options: ToolsetOptions): Toolset {
82
- const fetchContext = options.fetchContext ?? defaultFetcher();
83
- let cached: string | null = null;
105
+ const loadContext =
106
+ options.fetchContext === undefined
107
+ ? defaultLoadContext
108
+ : async (): Promise<{ source: string; text: string }> => ({
109
+ source: CONTEXT_URL,
110
+ text: await (options.fetchContext as ContextFetcher)(),
111
+ });
112
+ let cached: { source: string; text: string } | null = null;
84
113
 
85
114
  return {
86
115
  listRoutes: () => collectRoutes(options.cwd),
@@ -88,9 +117,9 @@ export function createTools(options: ToolsetOptions): Toolset {
88
117
  runCheck: () => executeCheck(options.cwd, { quiet: true }),
89
118
  searchDocs: async (query: string): Promise<SearchDocsResult> => {
90
119
  if (cached === null) {
91
- cached = await fetchContext();
120
+ cached = await loadContext();
92
121
  }
93
- return { source: CONTEXT_URL, query, matches: searchMarkdown(cached, query) };
122
+ return { source: cached.source, query, matches: searchMarkdown(cached.text, query) };
94
123
  },
95
124
  };
96
125
  }
@@ -1,11 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/tools.ts", "../src/server.ts"],
4
- "sourcesContent": [
5
- "import {\n collectFns,\n collectRoutes,\n executeCheck,\n type CheckJson,\n type FnsJson,\n type RoutesJson,\n} from \"@moonsjs/cli\";\n\n/** Sumber teks konteks framework (default: GitHub raw; injectable untuk test). */\nexport type ContextFetcher = () => Promise<string>;\n\nexport const CONTEXT_URL =\n \"https://raw.githubusercontent.com/RamonymousDev/Moons/main/docs/ai/CONTEXT.md\";\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 Toolset {\n listRoutes: () => Promise<RoutesJson>;\n getFnRegistry: () => Promise<FnsJson>;\n runCheck: () => Promise<CheckJson>;\n searchDocs: (query: string) => Promise<SearchDocsResult>;\n}\n\nexport interface ToolsetOptions {\n cwd: string;\n fetchContext?: ContextFetcher;\n}\n\nfunction defaultFetcher(): ContextFetcher {\n return async () => {\n const response = await fetch(CONTEXT_URL);\n if (!response.ok) {\n throw new Error(`gagal mengambil ${CONTEXT_URL} (HTTP ${response.status})`);\n }\n return await response.text();\n };\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 fetchContext = options.fetchContext ?? defaultFetcher();\n let cached: 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 fetchContext();\n }\n return { source: CONTEXT_URL, query, matches: searchMarkdown(cached, query) };\n },\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 return server;\n}\n"
7
- ],
8
- "mappings": "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",
9
- "debugId": "9E9BBAFBC5A8DB0664756E2164756E21",
10
- "names": []
11
- }