@yinuo-ngm/mcp-server 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026 ZhangJing
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any purpose
6
+ with or without fee is hereby granted, provided that the above copyright notice
7
+ and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
11
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
13
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
14
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
15
+ THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,188 @@
1
+ # @yinuo-ngm/mcp-server
2
+
3
+ Local MCP stdio server for ng-manager core capabilities.
4
+
5
+ This package is an AI Agent adapter layer. It is not a business core, not a Fastify replacement, not an Electron lifecycle manager, and not a general shell execution entrypoint.
6
+
7
+ ## Positioning
8
+
9
+ The intended boundary is:
10
+
11
+ ```text
12
+ MCP client -> packages/mcp-server -> ToolContext.services -> packages/core
13
+ ```
14
+
15
+ MCP tools must not call the local Fastify HTTP API. HTTP routes, Electron IPC, CLI commands, and MCP tools should all adapt the same core services.
16
+
17
+ ## Safety
18
+
19
+ Tools are assigned one risk level:
20
+
21
+ ```text
22
+ read
23
+ write
24
+ execute
25
+ dangerous
26
+ ```
27
+
28
+ Default policy:
29
+
30
+ ```text
31
+ read allowed
32
+ write blocked
33
+ execute blocked
34
+ dangerous blocked
35
+ ```
36
+
37
+ This MVP only registers read tools. It does not implement arbitrary shell execution, task start/stop/restart, git pull/checkout/commit/reset, proxy reload, runtime install/remove, file deletion, or system environment mutation.
38
+
39
+ ## Environment
40
+
41
+ ```text
42
+ NGM_DATA_DIR ng-manager data directory. Defaults to ~/.ng-manager.
43
+ NGM_WORKSPACE_ROOT Optional workspace hint. Defaults to process.cwd().
44
+ NGM_MCP_ALLOW_WRITE Future policy flag for write tools. Defaults to false.
45
+ NGM_MCP_ALLOW_EXECUTE Future policy flag for execute tools. Defaults to false.
46
+ NGM_MCP_ALLOW_DANGEROUS Future policy flag for dangerous tools. Defaults to false.
47
+ ```
48
+
49
+ ## Commands
50
+
51
+ From the repository root:
52
+
53
+ ```bash
54
+ npm run mcp:dev
55
+ npm run mcp:build
56
+ npm run mcp:start
57
+ ```
58
+
59
+ Direct workspace commands:
60
+
61
+ ```bash
62
+ npm run dev -w @yinuo-ngm/mcp-server
63
+ npm run build -w @yinuo-ngm/mcp-server
64
+ npm run start -w @yinuo-ngm/mcp-server
65
+ ```
66
+
67
+ The server uses stdio transport only. It does not listen on an HTTP port, and stdout is reserved for the MCP protocol.
68
+
69
+ ## MCP Client Configuration
70
+
71
+ Built output:
72
+
73
+ ```json
74
+ {
75
+ "mcpServers": {
76
+ "ng-manager": {
77
+ "command": "node",
78
+ "args": [
79
+ "D:/ng-manager/packages/mcp-server/lib/index.js"
80
+ ],
81
+ "env": {
82
+ "NGM_DATA_DIR": "C:/Users/you/.ng-manager",
83
+ "NGM_WORKSPACE_ROOT": "D:/ng-manager",
84
+ "NGM_MCP_ALLOW_WRITE": "false",
85
+ "NGM_MCP_ALLOW_EXECUTE": "false",
86
+ "NGM_MCP_ALLOW_DANGEROUS": "false"
87
+ }
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ Development:
94
+
95
+ ```json
96
+ {
97
+ "mcpServers": {
98
+ "ng-manager": {
99
+ "command": "npm",
100
+ "args": [
101
+ "run",
102
+ "dev",
103
+ "-w",
104
+ "@yinuo-ngm/mcp-server"
105
+ ],
106
+ "env": {
107
+ "NGM_DATA_DIR": "C:/Users/you/.ng-manager",
108
+ "NGM_WORKSPACE_ROOT": "D:/ng-manager"
109
+ }
110
+ }
111
+ }
112
+ }
113
+ ```
114
+
115
+ ## Tools
116
+
117
+ Project:
118
+
119
+ ```text
120
+ ngm.project.list
121
+ ngm.project.get
122
+ ngm.project.getScripts
123
+ ```
124
+
125
+ Task:
126
+
127
+ ```text
128
+ ngm.task.list
129
+ ngm.task.getStatus
130
+ ```
131
+
132
+ Log:
133
+
134
+ ```text
135
+ ngm.log.tail
136
+ ngm.log.search
137
+ ```
138
+
139
+ Git:
140
+
141
+ ```text
142
+ ngm.git.status
143
+ ngm.git.diff
144
+ ```
145
+
146
+ The Git tools are registered in this MVP but return a clear "not implemented in core yet" error through the Git service stub. A future phase should add a read-only Git service to `packages/core` first.
147
+
148
+ Runtime:
149
+
150
+ ```text
151
+ ngm.runtime.list
152
+ ngm.runtime.resolveForProject
153
+ ```
154
+
155
+ Proxy:
156
+
157
+ ```text
158
+ ngm.proxy.list
159
+ ngm.proxy.validate
160
+ ```
161
+
162
+ In this package, "proxy" means ng-manager's current Nginx/proxy management domain, not the operating system global proxy.
163
+
164
+ ## Result Shape
165
+
166
+ All tools return structured JSON as text content:
167
+
168
+ ```json
169
+ {
170
+ "ok": true,
171
+ "tool": "ngm.project.list",
172
+ "data": []
173
+ }
174
+ ```
175
+
176
+ Errors use:
177
+
178
+ ```json
179
+ {
180
+ "ok": false,
181
+ "tool": "ngm.project.get",
182
+ "error": "projectId or projectPath is required"
183
+ }
184
+ ```
185
+
186
+ ## Replacing Stubs
187
+
188
+ Add missing capabilities to `packages/core` first, then replace the corresponding service inside `ToolContext.services`. MCP tools should remain thin adapters that validate input, enforce policy, call core services, and cap output size.
@@ -0,0 +1,2 @@
1
+ import type { ToolContext } from "./tool-context";
2
+ export declare function createToolContext(): Promise<ToolContext>;
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.createToolContext = createToolContext;
37
+ const os = __importStar(require("os"));
38
+ const path = __importStar(require("path"));
39
+ const core_1 = require("@yinuo-ngm/core");
40
+ const gitStub = {
41
+ async status() {
42
+ throw new Error("Git service is not implemented in core yet");
43
+ },
44
+ async diff() {
45
+ throw new Error("Git service is not implemented in core yet");
46
+ },
47
+ };
48
+ async function createToolContext() {
49
+ const dataDir = process.env.NGM_DATA_DIR || path.join(os.homedir(), ".ng-manager");
50
+ const workspaceRoot = process.env.NGM_WORKSPACE_ROOT || process.cwd();
51
+ const core = await (0, core_1.createCoreApp)({
52
+ dataDir,
53
+ sysLogCapacity: 3000,
54
+ });
55
+ return {
56
+ workspaceRoot,
57
+ dataDir,
58
+ services: {
59
+ core,
60
+ git: gitStub,
61
+ },
62
+ async dispose() {
63
+ await core.dispose();
64
+ },
65
+ };
66
+ }
@@ -0,0 +1,22 @@
1
+ import type { CoreApp } from "@yinuo-ngm/core";
2
+ export type GitReadService = {
3
+ status(input: {
4
+ projectId?: string;
5
+ projectPath?: string;
6
+ }): Promise<unknown>;
7
+ diff(input: {
8
+ projectId?: string;
9
+ projectPath?: string;
10
+ maxBytes?: number;
11
+ }): Promise<unknown>;
12
+ };
13
+ export type ToolServices = {
14
+ core: CoreApp;
15
+ git: GitReadService;
16
+ };
17
+ export type ToolContext = {
18
+ workspaceRoot: string;
19
+ dataDir: string;
20
+ services: ToolServices;
21
+ dispose(): Promise<void>;
22
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { ToolContext } from "./context/tool-context";
3
+ export declare function createMcpServer(context: ToolContext): McpServer;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMcpServer = createMcpServer;
4
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
5
+ const register_tools_1 = require("./register-tools");
6
+ function createMcpServer(context) {
7
+ const server = new mcp_js_1.McpServer({ name: "ng-manager", version: "0.1.0" }, { capabilities: { tools: {} } });
8
+ (0, register_tools_1.registerTools)(server, context);
9
+ return server;
10
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function main(): Promise<void>;
package/lib/index.js ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.main = main;
5
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+ const create_tool_context_1 = require("./context/create-tool-context");
7
+ const create_mcp_server_1 = require("./create-mcp-server");
8
+ const errors_1 = require("./utils/errors");
9
+ async function main() {
10
+ const context = await (0, create_tool_context_1.createToolContext)();
11
+ const server = (0, create_mcp_server_1.createMcpServer)(context);
12
+ const cleanup = async () => {
13
+ await context.dispose();
14
+ };
15
+ process.once("SIGINT", () => {
16
+ void cleanup().finally(() => process.exit(0));
17
+ });
18
+ process.once("SIGTERM", () => {
19
+ void cleanup().finally(() => process.exit(0));
20
+ });
21
+ await server.connect(new stdio_js_1.StdioServerTransport());
22
+ }
23
+ if (require.main === module) {
24
+ main().catch((error) => {
25
+ process.stderr.write(`${(0, errors_1.errorMessage)(error)}\n`);
26
+ process.exit(1);
27
+ });
28
+ }
@@ -0,0 +1,2 @@
1
+ import type { ToolRiskLevel, ToolPolicy } from "./tool-policy";
2
+ export declare function assertToolPolicy(policy: ToolPolicy, toolName: string, riskLevel: ToolRiskLevel): void;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertToolPolicy = assertToolPolicy;
4
+ function assertToolPolicy(policy, toolName, riskLevel) {
5
+ if (!policy[riskLevel]) {
6
+ throw new Error(`Tool ${toolName} is blocked by policy: ${riskLevel} tools are disabled`);
7
+ }
8
+ }
@@ -0,0 +1,3 @@
1
+ export type ToolRiskLevel = "read" | "write" | "execute" | "dangerous";
2
+ export type ToolPolicy = Record<ToolRiskLevel, boolean>;
3
+ export declare function createDefaultToolPolicy(): ToolPolicy;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createDefaultToolPolicy = createDefaultToolPolicy;
4
+ function envFlag(name) {
5
+ return String(process.env[name] ?? "").trim().toLowerCase() === "true";
6
+ }
7
+ function createDefaultToolPolicy() {
8
+ return {
9
+ read: true,
10
+ write: envFlag("NGM_MCP_ALLOW_WRITE"),
11
+ execute: envFlag("NGM_MCP_ALLOW_EXECUTE"),
12
+ dangerous: envFlag("NGM_MCP_ALLOW_DANGEROUS"),
13
+ };
14
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { ToolContext } from "./context/tool-context";
3
+ export declare function registerTools(server: McpServer, context: ToolContext): void;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerTools = registerTools;
4
+ const assert_tool_policy_1 = require("./policy/assert-tool-policy");
5
+ const tool_policy_1 = require("./policy/tool-policy");
6
+ const tools_1 = require("./tools");
7
+ const errors_1 = require("./utils/errors");
8
+ const result_1 = require("./utils/result");
9
+ function registerTools(server, context) {
10
+ const policy = (0, tool_policy_1.createDefaultToolPolicy)();
11
+ const registerTool = server.registerTool.bind(server);
12
+ for (const tool of (0, tools_1.allTools)()) {
13
+ registerTool(tool.name, {
14
+ description: tool.description,
15
+ inputSchema: tool.inputSchema,
16
+ }, async (args) => {
17
+ try {
18
+ (0, assert_tool_policy_1.assertToolPolicy)(policy, tool.name, tool.riskLevel);
19
+ const parsed = tool.inputSchema.parse(args);
20
+ const result = await tool.handler(parsed, context);
21
+ return (0, result_1.toMcpTextResult)(result);
22
+ }
23
+ catch (error) {
24
+ return (0, result_1.toMcpTextResult)((0, result_1.fail)(tool.name, (0, errors_1.errorMessage)(error)));
25
+ }
26
+ });
27
+ }
28
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpToolDefinition } from "./index";
2
+ export declare function gitTools(): McpToolDefinition[];
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.gitTools = gitTools;
4
+ const zod_1 = require("zod");
5
+ const result_1 = require("../utils/result");
6
+ const gitProjectSchema = zod_1.z.object({
7
+ projectId: zod_1.z.string().trim().min(1).optional(),
8
+ projectPath: zod_1.z.string().trim().min(1).optional(),
9
+ }).strict();
10
+ const gitDiffSchema = gitProjectSchema.extend({
11
+ maxBytes: zod_1.z.number().int().min(1).max(200000).optional(),
12
+ }).strict();
13
+ function gitTools() {
14
+ return [
15
+ {
16
+ name: "ngm.git.status",
17
+ description: "Read Git working tree status for a project. This MVP uses a core Git service stub.",
18
+ riskLevel: "read",
19
+ inputSchema: gitProjectSchema,
20
+ async handler(args, context) {
21
+ const data = await context.services.git.status(args);
22
+ return (0, result_1.ok)("ngm.git.status", data);
23
+ },
24
+ },
25
+ {
26
+ name: "ngm.git.diff",
27
+ description: "Read Git diff for a project. This MVP uses a core Git service stub.",
28
+ riskLevel: "read",
29
+ inputSchema: gitDiffSchema,
30
+ async handler(args, context) {
31
+ const data = await context.services.git.diff(args);
32
+ return (0, result_1.ok)("ngm.git.diff", data);
33
+ },
34
+ },
35
+ ];
36
+ }
@@ -0,0 +1,12 @@
1
+ import type { z } from "zod";
2
+ import type { ToolContext } from "../context/tool-context";
3
+ import type { ToolRiskLevel } from "../policy/tool-policy";
4
+ import type { ToolResult } from "../utils/result";
5
+ export type McpToolDefinition<TSchema extends z.AnyZodObject = z.AnyZodObject> = {
6
+ name: string;
7
+ description: string;
8
+ riskLevel: ToolRiskLevel;
9
+ inputSchema: TSchema;
10
+ handler(args: z.infer<TSchema>, context: ToolContext): Promise<ToolResult> | ToolResult;
11
+ };
12
+ export declare function allTools(): McpToolDefinition[];
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.allTools = allTools;
4
+ const git_tools_1 = require("./git.tools");
5
+ const log_tools_1 = require("./log.tools");
6
+ const project_tools_1 = require("./project.tools");
7
+ const proxy_tools_1 = require("./proxy.tools");
8
+ const runtime_tools_1 = require("./runtime.tools");
9
+ const task_tools_1 = require("./task.tools");
10
+ function allTools() {
11
+ return [
12
+ ...(0, project_tools_1.projectTools)(),
13
+ ...(0, task_tools_1.taskTools)(),
14
+ ...(0, log_tools_1.logTools)(),
15
+ ...(0, git_tools_1.gitTools)(),
16
+ ...(0, runtime_tools_1.runtimeTools)(),
17
+ ...(0, proxy_tools_1.proxyTools)(),
18
+ ];
19
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpToolDefinition } from "./index";
2
+ export declare function logTools(): McpToolDefinition[];
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logTools = logTools;
4
+ const zod_1 = require("zod");
5
+ const result_1 = require("../utils/result");
6
+ const logTailSchema = zod_1.z.object({
7
+ runId: zod_1.z.string().trim().min(1).optional(),
8
+ taskId: zod_1.z.string().trim().min(1).optional(),
9
+ tail: zod_1.z.number().int().min(1).max(500).optional(),
10
+ }).strict();
11
+ const logSearchSchema = zod_1.z.object({
12
+ query: zod_1.z.string().trim().min(1),
13
+ limit: zod_1.z.number().int().min(1).max(200).optional(),
14
+ source: zod_1.z.string().trim().min(1).optional(),
15
+ scope: zod_1.z.string().trim().min(1).optional(),
16
+ }).strict();
17
+ function clampTail(value) {
18
+ return Math.min(Math.max(value ?? 100, 1), 500);
19
+ }
20
+ function clampSearchLimit(value) {
21
+ return Math.min(Math.max(value ?? 50, 1), 200);
22
+ }
23
+ function logTools() {
24
+ return [
25
+ {
26
+ name: "ngm.log.tail",
27
+ description: "Read recent task logs by runId or taskId.",
28
+ riskLevel: "read",
29
+ inputSchema: logTailSchema,
30
+ async handler(args, context) {
31
+ const tail = clampTail(args.tail);
32
+ let runId = args.runId;
33
+ if (!runId && args.taskId) {
34
+ const snapshot = await context.services.core.task.getSnapshotByTaskId(args.taskId);
35
+ runId = snapshot?.runId;
36
+ }
37
+ if (!runId) {
38
+ throw new Error("runId or taskId is required");
39
+ }
40
+ const lines = await context.services.core.task.getTailLogsByRun(runId, tail);
41
+ return (0, result_1.ok)("ngm.log.tail", {
42
+ runId,
43
+ tail,
44
+ lines,
45
+ });
46
+ },
47
+ },
48
+ {
49
+ name: "ngm.log.search",
50
+ description: "Search recent ng-manager system logs by keyword.",
51
+ riskLevel: "read",
52
+ inputSchema: logSearchSchema,
53
+ async handler(args, context) {
54
+ const limit = clampSearchLimit(args.limit);
55
+ const filter = {
56
+ ...(args.source ? { source: args.source } : {}),
57
+ ...(args.scope ? { scope: args.scope } : {}),
58
+ };
59
+ const needle = args.query.toLowerCase();
60
+ const entries = context.services.core.sysLog
61
+ .query(filter, { limit: 10000 })
62
+ .filter((entry) => {
63
+ const text = `${entry.text} ${JSON.stringify(entry.data ?? {})}`.toLowerCase();
64
+ return text.includes(needle);
65
+ })
66
+ .slice(0, limit);
67
+ return (0, result_1.ok)("ngm.log.search", {
68
+ query: args.query,
69
+ limit,
70
+ entries,
71
+ });
72
+ },
73
+ },
74
+ ];
75
+ }
@@ -0,0 +1,18 @@
1
+ import type { Project } from "@yinuo-ngm/project";
2
+ import { z } from "zod";
3
+ import type { ToolContext } from "../context/tool-context";
4
+ import type { McpToolDefinition } from "./index";
5
+ export declare const projectLocatorSchema: z.ZodObject<{
6
+ projectId: z.ZodOptional<z.ZodString>;
7
+ projectPath: z.ZodOptional<z.ZodString>;
8
+ }, "strict", z.ZodTypeAny, {
9
+ projectId?: string | undefined;
10
+ projectPath?: string | undefined;
11
+ }, {
12
+ projectId?: string | undefined;
13
+ projectPath?: string | undefined;
14
+ }>;
15
+ type ProjectLocator = z.infer<typeof projectLocatorSchema>;
16
+ export declare function resolveProject(context: ToolContext, locator: ProjectLocator): Promise<Project>;
17
+ export declare function projectTools(): McpToolDefinition[];
18
+ export {};
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.projectLocatorSchema = void 0;
37
+ exports.resolveProject = resolveProject;
38
+ exports.projectTools = projectTools;
39
+ const path = __importStar(require("path"));
40
+ const zod_1 = require("zod");
41
+ const result_1 = require("../utils/result");
42
+ exports.projectLocatorSchema = zod_1.z.object({
43
+ projectId: zod_1.z.string().trim().min(1).optional(),
44
+ projectPath: zod_1.z.string().trim().min(1).optional(),
45
+ }).strict();
46
+ async function resolveProject(context, locator) {
47
+ if (locator.projectId) {
48
+ return context.services.core.project.get(locator.projectId);
49
+ }
50
+ if (locator.projectPath) {
51
+ const projects = await context.services.core.project.list();
52
+ const requestedPath = normalizeFsPath(locator.projectPath);
53
+ const project = projects.find((item) => normalizeFsPath(item.root) === requestedPath);
54
+ if (project) {
55
+ return project;
56
+ }
57
+ throw new Error(`Project not found for path: ${locator.projectPath}`);
58
+ }
59
+ throw new Error("projectId or projectPath is required");
60
+ }
61
+ function normalizeFsPath(value) {
62
+ const resolved = path.resolve(value);
63
+ return process.platform === "win32" ? resolved.replace(/\\/g, "/").toLowerCase() : resolved;
64
+ }
65
+ function toProjectSummary(project) {
66
+ return {
67
+ id: project.id,
68
+ name: project.name,
69
+ root: project.root,
70
+ createdAt: project.createdAt,
71
+ updatedAt: project.updatedAt,
72
+ scripts: project.scripts,
73
+ packageManager: project.packageManager,
74
+ runtime: project.runtime,
75
+ nodeVersion: project.nodeVersion,
76
+ framework: project.framework,
77
+ env: project.env,
78
+ isFavorite: project.isFavorite,
79
+ lastOpened: project.lastOpened,
80
+ repoUrl: project.repoUrl,
81
+ repoPageUrl: project.repoPageUrl,
82
+ assets: project.assets,
83
+ };
84
+ }
85
+ function toProjectScripts(project) {
86
+ return {
87
+ id: project.id,
88
+ name: project.name,
89
+ root: project.root,
90
+ scripts: project.scripts ?? {},
91
+ packageManager: project.packageManager,
92
+ nodeVersion: project.nodeVersion,
93
+ runtime: project.runtime,
94
+ };
95
+ }
96
+ function projectTools() {
97
+ return [
98
+ {
99
+ name: "ngm.project.list",
100
+ description: "List projects managed by ng-manager.",
101
+ riskLevel: "read",
102
+ inputSchema: zod_1.z.object({}).strict(),
103
+ async handler(_args, context) {
104
+ const projects = await context.services.core.project.list();
105
+ return (0, result_1.ok)("ngm.project.list", projects.map(toProjectSummary));
106
+ },
107
+ },
108
+ {
109
+ name: "ngm.project.get",
110
+ description: "Get one ng-manager project by projectId or projectPath.",
111
+ riskLevel: "read",
112
+ inputSchema: exports.projectLocatorSchema,
113
+ async handler(args, context) {
114
+ const project = await resolveProject(context, args);
115
+ return (0, result_1.ok)("ngm.project.get", toProjectSummary(project));
116
+ },
117
+ },
118
+ {
119
+ name: "ngm.project.getScripts",
120
+ description: "Get package scripts and runtime hints for one ng-manager project.",
121
+ riskLevel: "read",
122
+ inputSchema: exports.projectLocatorSchema,
123
+ async handler(args, context) {
124
+ const project = await resolveProject(context, args);
125
+ return (0, result_1.ok)("ngm.project.getScripts", toProjectScripts(project));
126
+ },
127
+ },
128
+ ];
129
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpToolDefinition } from "./index";
2
+ export declare function proxyTools(): McpToolDefinition[];
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.proxyTools = proxyTools;
4
+ const zod_1 = require("zod");
5
+ const result_1 = require("../utils/result");
6
+ const proxyValidateSchema = zod_1.z.object({
7
+ configText: zod_1.z.string().optional(),
8
+ }).strict();
9
+ function proxyTools() {
10
+ return [
11
+ {
12
+ name: "ngm.proxy.list",
13
+ description: "List current ng-manager Nginx/proxy binding, status, servers, and upstreams.",
14
+ riskLevel: "read",
15
+ inputSchema: zod_1.z.object({}).strict(),
16
+ async handler(_args, context) {
17
+ const nginx = context.services.core.nginx;
18
+ const instance = nginx.service.getInstance();
19
+ const [status, servers, upstreams] = await Promise.all([
20
+ nginx.service.getStatus().catch((error) => ({
21
+ isRunning: false,
22
+ error: error instanceof Error ? error.message : String(error),
23
+ })),
24
+ nginx.server.getAllServers().catch(() => []),
25
+ nginx.module.getUpstreams().catch(() => []),
26
+ ]);
27
+ return (0, result_1.ok)("ngm.proxy.list", {
28
+ instance,
29
+ status,
30
+ servers,
31
+ upstreams,
32
+ });
33
+ },
34
+ },
35
+ {
36
+ name: "ngm.proxy.validate",
37
+ description: "Validate the current or supplied ng-manager Nginx/proxy config without reload.",
38
+ riskLevel: "read",
39
+ inputSchema: proxyValidateSchema,
40
+ async handler(args, context) {
41
+ const validation = await context.services.core.nginx.config.validateConfig(args.configText);
42
+ return (0, result_1.ok)("ngm.proxy.validate", validation);
43
+ },
44
+ },
45
+ ];
46
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpToolDefinition } from "./index";
2
+ export declare function runtimeTools(): McpToolDefinition[];
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runtimeTools = runtimeTools;
4
+ const zod_1 = require("zod");
5
+ const project_tools_1 = require("./project.tools");
6
+ const result_1 = require("../utils/result");
7
+ function normalizePackageManager(value) {
8
+ return value === "pnpm" || value === "yarn" ? value : "npm";
9
+ }
10
+ function runtimeConfigForProject(project) {
11
+ if (project.runtime) {
12
+ return {
13
+ ...project.runtime,
14
+ packageManager: normalizePackageManager(project.runtime.packageManager ?? project.packageManager),
15
+ };
16
+ }
17
+ if (project.nodeVersion) {
18
+ return {
19
+ type: "managed",
20
+ version: project.nodeVersion,
21
+ packageManager: normalizePackageManager(project.packageManager),
22
+ };
23
+ }
24
+ return {
25
+ type: "system",
26
+ packageManager: normalizePackageManager(project.packageManager),
27
+ };
28
+ }
29
+ function runtimeTools() {
30
+ return [
31
+ {
32
+ name: "ngm.runtime.list",
33
+ description: "List Node runtimes known to ng-manager.",
34
+ riskLevel: "read",
35
+ inputSchema: zod_1.z.object({}).strict(),
36
+ async handler(_args, context) {
37
+ const runtimes = await context.services.core.nodeRuntime.listRuntimes();
38
+ return (0, result_1.ok)("ngm.runtime.list", runtimes);
39
+ },
40
+ },
41
+ {
42
+ name: "ngm.runtime.resolveForProject",
43
+ description: "Resolve the Node runtime ng-manager would use for a project.",
44
+ riskLevel: "read",
45
+ inputSchema: project_tools_1.projectLocatorSchema,
46
+ async handler(args, context) {
47
+ const project = await (0, project_tools_1.resolveProject)(context, args);
48
+ const requestedRuntime = runtimeConfigForProject(project);
49
+ const resolvedRuntime = await context.services.core.nodeRuntime.resolveRuntime(requestedRuntime);
50
+ return (0, result_1.ok)("ngm.runtime.resolveForProject", {
51
+ project: {
52
+ id: project.id,
53
+ name: project.name,
54
+ root: project.root,
55
+ packageManager: project.packageManager,
56
+ nodeVersion: project.nodeVersion,
57
+ runtime: project.runtime,
58
+ },
59
+ requestedRuntime,
60
+ resolvedRuntime,
61
+ });
62
+ },
63
+ },
64
+ ];
65
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpToolDefinition } from "./index";
2
+ export declare function taskTools(): McpToolDefinition[];
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.taskTools = taskTools;
4
+ const zod_1 = require("zod");
5
+ const result_1 = require("../utils/result");
6
+ const taskListSchema = zod_1.z.object({
7
+ projectId: zod_1.z.string().trim().min(1).optional(),
8
+ }).strict();
9
+ const taskStatusSchema = zod_1.z.object({
10
+ taskId: zod_1.z.string().trim().min(1),
11
+ }).strict();
12
+ function taskTools() {
13
+ return [
14
+ {
15
+ name: "ngm.task.list",
16
+ description: "List registered task views for a project, or active tasks when projectId is omitted.",
17
+ riskLevel: "read",
18
+ inputSchema: taskListSchema,
19
+ async handler(args, context) {
20
+ if (args.projectId) {
21
+ const rows = await context.services.core.task.listViewsByProject(args.projectId);
22
+ return (0, result_1.ok)("ngm.task.list", rows);
23
+ }
24
+ const activeTasks = await context.services.core.task.listActive();
25
+ return (0, result_1.ok)("ngm.task.list", activeTasks);
26
+ },
27
+ },
28
+ {
29
+ name: "ngm.task.getStatus",
30
+ description: "Get the runtime status for a registered task.",
31
+ riskLevel: "read",
32
+ inputSchema: taskStatusSchema,
33
+ async handler(args, context) {
34
+ const runtime = await context.services.core.task.status(args.taskId);
35
+ return (0, result_1.ok)("ngm.task.getStatus", runtime);
36
+ },
37
+ },
38
+ ];
39
+ }
@@ -0,0 +1 @@
1
+ export declare function errorMessage(error: unknown): string;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.errorMessage = errorMessage;
4
+ function errorMessage(error) {
5
+ if (error instanceof Error && error.message) {
6
+ return error.message;
7
+ }
8
+ return String(error ?? "Unknown error");
9
+ }
@@ -0,0 +1,19 @@
1
+ export type ToolSuccessResult<T = unknown> = {
2
+ ok: true;
3
+ tool: string;
4
+ data: T;
5
+ };
6
+ export type ToolErrorResult = {
7
+ ok: false;
8
+ tool: string;
9
+ error: string;
10
+ };
11
+ export type ToolResult<T = unknown> = ToolSuccessResult<T> | ToolErrorResult;
12
+ export declare function ok<T>(tool: string, data: T): ToolSuccessResult<T>;
13
+ export declare function fail(tool: string, error: string): ToolErrorResult;
14
+ export declare function toMcpTextResult(result: ToolResult): {
15
+ content: Array<{
16
+ type: "text";
17
+ text: string;
18
+ }>;
19
+ };
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ok = ok;
4
+ exports.fail = fail;
5
+ exports.toMcpTextResult = toMcpTextResult;
6
+ function ok(tool, data) {
7
+ return { ok: true, tool, data };
8
+ }
9
+ function fail(tool, error) {
10
+ return { ok: false, tool, error };
11
+ }
12
+ function toMcpTextResult(result) {
13
+ return {
14
+ content: [
15
+ {
16
+ type: "text",
17
+ text: JSON.stringify(result, null, 2),
18
+ },
19
+ ],
20
+ };
21
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@yinuo-ngm/mcp-server",
3
+ "version": "0.1.1",
4
+ "description": "Local MCP stdio server for ng-manager core capabilities.",
5
+ "author": "ZhangJing <892295834@qq.com>",
6
+ "license": "ISC",
7
+ "bin": {
8
+ "ngm-mcp-server": "lib/index.js"
9
+ },
10
+ "main": "lib/index.js",
11
+ "types": "lib/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./lib/index.d.ts",
15
+ "default": "./lib/index.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "lib"
20
+ ],
21
+ "engines": {
22
+ "node": ">=18"
23
+ },
24
+ "scripts": {
25
+ "dev": "tsx watch src/index.ts",
26
+ "build": "tsc -b tsconfig.json",
27
+ "start": "node lib/index.js"
28
+ },
29
+ "dependencies": {
30
+ "@modelcontextprotocol/sdk": "^1.26.0",
31
+ "@yinuo-ngm/core": "^0.1.14",
32
+ "zod": "^3.25.76"
33
+ },
34
+ "devDependencies": {
35
+ "tsx": "^4.21.0"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "gitHead": "f64135f6db80de6928bc0b83c29c26e408e9fcfb"
41
+ }