@yinuo-ngm/mcp-server 0.1.2 → 0.1.4

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.
Files changed (214) hide show
  1. package/README.md +191 -208
  2. package/lib/audit/audit-event.d.ts +14 -0
  3. package/lib/audit/audit-event.js +2 -0
  4. package/lib/audit/audit-log.service.d.ts +7 -0
  5. package/lib/audit/audit-log.service.js +187 -0
  6. package/lib/audit/redact.d.ts +3 -0
  7. package/lib/audit/redact.js +28 -0
  8. package/lib/catalog/capabilities/blocked-local-actions.d.ts +1 -0
  9. package/lib/catalog/capabilities/blocked-local-actions.js +18 -0
  10. package/lib/catalog/capabilities/frontend-standard.d.ts +2 -0
  11. package/lib/catalog/capabilities/frontend-standard.js +36 -0
  12. package/lib/catalog/capabilities/hub-v2.d.ts +2 -0
  13. package/lib/catalog/capabilities/hub-v2.js +34 -0
  14. package/lib/catalog/capabilities/nginx.d.ts +2 -0
  15. package/lib/catalog/capabilities/nginx.js +23 -0
  16. package/lib/catalog/capabilities/project.d.ts +2 -0
  17. package/lib/catalog/capabilities/project.js +23 -0
  18. package/lib/catalog/capabilities/router.d.ts +2 -0
  19. package/lib/catalog/capabilities/router.js +11 -0
  20. package/lib/catalog/capabilities/runtime.d.ts +2 -0
  21. package/lib/catalog/capabilities/runtime.js +17 -0
  22. package/lib/catalog/capabilities/workspace.d.ts +2 -0
  23. package/lib/catalog/capabilities/workspace.js +23 -0
  24. package/lib/catalog/helpers.d.ts +3 -0
  25. package/lib/catalog/helpers.js +42 -0
  26. package/lib/catalog/index.d.ts +4 -0
  27. package/lib/catalog/index.js +23 -0
  28. package/lib/catalog/tools/frontend-standard.d.ts +2 -0
  29. package/lib/catalog/tools/frontend-standard.js +166 -0
  30. package/lib/catalog/tools/hub-v2-api.d.ts +2 -0
  31. package/lib/catalog/tools/hub-v2-api.js +124 -0
  32. package/lib/catalog/tools/hub-v2-docs.d.ts +2 -0
  33. package/lib/catalog/tools/hub-v2-docs.js +40 -0
  34. package/lib/catalog/tools/nginx.d.ts +2 -0
  35. package/lib/catalog/tools/nginx.js +82 -0
  36. package/lib/catalog/tools/project.d.ts +2 -0
  37. package/lib/catalog/tools/project.js +124 -0
  38. package/lib/catalog/tools/router.d.ts +2 -0
  39. package/lib/catalog/tools/router.js +26 -0
  40. package/lib/catalog/tools/runtime.d.ts +2 -0
  41. package/lib/catalog/tools/runtime.js +40 -0
  42. package/lib/catalog/tools/workspace.d.ts +2 -0
  43. package/lib/catalog/tools/workspace.js +75 -0
  44. package/lib/catalog/types.d.ts +15 -0
  45. package/lib/catalog/types.js +2 -0
  46. package/lib/context/create-tool-context.js +11 -10
  47. package/lib/context/local-server-client.d.ts +2 -0
  48. package/lib/context/local-server-client.js +174 -0
  49. package/lib/context/tool-context.d.ts +36 -0
  50. package/lib/doctor.d.ts +8 -0
  51. package/lib/doctor.js +194 -0
  52. package/lib/errors/error-codes.d.ts +12 -0
  53. package/lib/errors/error-codes.js +14 -0
  54. package/lib/errors/mcp-tool-error.d.ts +8 -0
  55. package/lib/errors/mcp-tool-error.js +14 -0
  56. package/lib/filesystem/project-files.d.ts +18 -0
  57. package/lib/filesystem/project-files.js +112 -0
  58. package/lib/git/local-git-read-service.d.ts +2 -0
  59. package/lib/git/local-git-read-service.js +96 -0
  60. package/lib/index.d.ts +1 -0
  61. package/lib/index.js +4 -0
  62. package/lib/policy/assert-tool-policy.js +10 -1
  63. package/lib/register-tools.js +67 -10
  64. package/lib/registry/tool-names.d.ts +95 -0
  65. package/lib/registry/tool-names.js +97 -0
  66. package/lib/services/path-guard.service.d.ts +4 -0
  67. package/lib/services/path-guard.service.js +75 -0
  68. package/lib/services/permission.service.d.ts +5 -0
  69. package/lib/services/permission.service.js +38 -0
  70. package/lib/services/project-resolver.service.d.ts +32 -0
  71. package/lib/services/project-resolver.service.js +95 -0
  72. package/lib/standard/frontend-standard.default.d.ts +2 -0
  73. package/lib/standard/frontend-standard.default.js +51 -0
  74. package/lib/standard/frontend-standard.schema.d.ts +196 -0
  75. package/lib/standard/frontend-standard.schema.js +61 -0
  76. package/lib/standard/frontend-standard.service.d.ts +79 -0
  77. package/lib/standard/frontend-standard.service.js +115 -0
  78. package/lib/standard/project-scan.d.ts +9 -0
  79. package/lib/standard/project-scan.js +91 -0
  80. package/lib/standard/validators/angular-structure.validator.d.ts +4 -0
  81. package/lib/standard/validators/angular-structure.validator.js +75 -0
  82. package/lib/standard/validators/component.validator.d.ts +4 -0
  83. package/lib/standard/validators/component.validator.js +94 -0
  84. package/lib/standard/validators/git.validator.d.ts +8 -0
  85. package/lib/standard/validators/git.validator.js +32 -0
  86. package/lib/standard/validators/review.validator.d.ts +15 -0
  87. package/lib/standard/validators/review.validator.js +67 -0
  88. package/lib/standard/validators/test.validator.d.ts +19 -0
  89. package/lib/standard/validators/test.validator.js +89 -0
  90. package/lib/tool-catalog.d.ts +2 -0
  91. package/lib/tool-catalog.js +6 -0
  92. package/lib/tools/angular/angular-standard.tools.d.ts +2 -0
  93. package/lib/tools/angular/angular-standard.tools.js +53 -0
  94. package/lib/tools/angular/index.d.ts +1 -0
  95. package/lib/tools/angular/index.js +5 -0
  96. package/lib/tools/capability.tools.d.ts +2 -0
  97. package/lib/tools/capability.tools.js +205 -0
  98. package/lib/tools/controlled/index.d.ts +2 -0
  99. package/lib/tools/controlled/index.js +13 -0
  100. package/lib/tools/controlled/local-server.d.ts +6 -0
  101. package/lib/tools/controlled/local-server.js +17 -0
  102. package/lib/tools/controlled/operation-policy.d.ts +22 -0
  103. package/lib/tools/controlled/operation-policy.js +50 -0
  104. package/lib/tools/controlled/operation-result.d.ts +30 -0
  105. package/lib/tools/controlled/operation-result.js +33 -0
  106. package/lib/tools/controlled/schemas.d.ts +159 -0
  107. package/lib/tools/controlled/schemas.js +49 -0
  108. package/lib/tools/controlled.tools.d.ts +1 -0
  109. package/lib/tools/controlled.tools.js +5 -0
  110. package/lib/tools/file-write.tools.d.ts +2 -0
  111. package/lib/tools/file-write.tools.js +70 -0
  112. package/lib/tools/git.tools.js +109 -8
  113. package/lib/tools/hub-v2/client.d.ts +6 -1
  114. package/lib/tools/hub-v2/client.js +15 -0
  115. package/lib/tools/hub-v2/config/config-paths.d.ts +2 -0
  116. package/lib/tools/hub-v2/config/config-paths.js +17 -0
  117. package/lib/tools/hub-v2/config/env.d.ts +1 -0
  118. package/lib/tools/hub-v2/config/env.js +12 -0
  119. package/lib/tools/hub-v2/config/index.d.ts +8 -0
  120. package/lib/tools/hub-v2/config/index.js +18 -0
  121. package/lib/tools/hub-v2/config/jsonc.d.ts +5 -0
  122. package/lib/tools/hub-v2/config/jsonc.js +86 -0
  123. package/lib/tools/hub-v2/config/load-config.d.ts +18 -0
  124. package/lib/tools/hub-v2/config/load-config.js +93 -0
  125. package/lib/tools/hub-v2/config/project-selector.d.ts +5 -0
  126. package/lib/tools/hub-v2/config/project-selector.js +92 -0
  127. package/lib/tools/hub-v2/config/resolve-context.d.ts +13 -0
  128. package/lib/tools/hub-v2/config/resolve-context.js +33 -0
  129. package/lib/tools/hub-v2/docs.tools.js +138 -4
  130. package/lib/tools/hub-v2/index.js +2 -0
  131. package/lib/tools/hub-v2/issues-workflow.tools.d.ts +2 -0
  132. package/lib/tools/hub-v2/issues-workflow.tools.js +199 -0
  133. package/lib/tools/hub-v2/issues.tools.js +96 -6
  134. package/lib/tools/hub-v2/projects.tools.js +16 -3
  135. package/lib/tools/hub-v2/raw.d.ts +8 -0
  136. package/lib/tools/hub-v2/raw.js +33 -0
  137. package/lib/tools/hub-v2/rd.tools.js +167 -8
  138. package/lib/tools/hub-v2/schemas.d.ts +668 -71
  139. package/lib/tools/hub-v2/schemas.js +152 -1
  140. package/lib/tools/hub-v2/upload.tools.js +53 -5
  141. package/lib/tools/index.d.ts +1 -0
  142. package/lib/tools/index.js +22 -0
  143. package/lib/tools/log.tools.js +33 -6
  144. package/lib/tools/nginx/index.d.ts +1 -0
  145. package/lib/tools/nginx/index.js +5 -0
  146. package/lib/tools/nginx/nginx-control.tools.d.ts +2 -0
  147. package/lib/tools/nginx/nginx-control.tools.js +133 -0
  148. package/lib/tools/nginx/nginx-proxy.d.ts +24 -0
  149. package/lib/tools/nginx/nginx-proxy.js +154 -0
  150. package/lib/tools/nginx.tools.d.ts +2 -0
  151. package/lib/tools/nginx.tools.js +111 -0
  152. package/lib/tools/project/index.d.ts +2 -0
  153. package/lib/tools/project/index.js +7 -0
  154. package/lib/tools/project/launch-status.d.ts +10 -0
  155. package/lib/tools/project/launch-status.js +78 -0
  156. package/lib/tools/project/local-diagnostics.d.ts +19 -0
  157. package/lib/tools/project/local-diagnostics.js +97 -0
  158. package/lib/tools/project/observe-redaction.d.ts +3 -0
  159. package/lib/tools/project/observe-redaction.js +25 -0
  160. package/lib/tools/project/observe-runtime.d.ts +72 -0
  161. package/lib/tools/project/observe-runtime.js +147 -0
  162. package/lib/tools/project/project-control.tools.d.ts +2 -0
  163. package/lib/tools/project/project-control.tools.js +216 -0
  164. package/lib/tools/project/project-observe.tools.d.ts +2 -0
  165. package/lib/tools/project/project-observe.tools.js +191 -0
  166. package/lib/tools/project/runtime-config.d.ts +7 -0
  167. package/lib/tools/project/runtime-config.js +50 -0
  168. package/lib/tools/project-observe.tools.d.ts +1 -0
  169. package/lib/tools/project-observe.tools.js +5 -0
  170. package/lib/tools/project.tools.d.ts +8 -0
  171. package/lib/tools/project.tools.js +97 -6
  172. package/lib/tools/proxy.tools.js +4 -4
  173. package/lib/tools/review/index.d.ts +1 -0
  174. package/lib/tools/review/index.js +5 -0
  175. package/lib/tools/review/review.tools.d.ts +2 -0
  176. package/lib/tools/review/review.tools.js +152 -0
  177. package/lib/tools/runtime/index.d.ts +1 -0
  178. package/lib/tools/runtime/index.js +5 -0
  179. package/lib/tools/runtime/runtime-control.tools.d.ts +2 -0
  180. package/lib/tools/runtime/runtime-control.tools.js +89 -0
  181. package/lib/tools/runtime.tools.js +41 -4
  182. package/lib/tools/standard/index.d.ts +1 -0
  183. package/lib/tools/standard/index.js +5 -0
  184. package/lib/tools/standard/standard.tools.d.ts +2 -0
  185. package/lib/tools/standard/standard.tools.js +91 -0
  186. package/lib/tools/task.tools.js +44 -9
  187. package/lib/tools/test/index.d.ts +1 -0
  188. package/lib/tools/test/index.js +5 -0
  189. package/lib/tools/test/test-standard.tools.d.ts +2 -0
  190. package/lib/tools/test/test-standard.tools.js +51 -0
  191. package/lib/tools/tool-catalog.d.ts +2 -0
  192. package/lib/tools/tool-catalog.js +7 -0
  193. package/lib/tools/workflow/frontend-workflow.tools.d.ts +2 -0
  194. package/lib/tools/workflow/frontend-workflow.tools.js +364 -0
  195. package/lib/tools/workflow/index.d.ts +1 -0
  196. package/lib/tools/workflow/index.js +5 -0
  197. package/lib/tools/workspace-package.d.ts +22 -0
  198. package/lib/tools/workspace-package.js +130 -0
  199. package/lib/tools/workspace.tools.d.ts +7 -0
  200. package/lib/tools/workspace.tools.js +336 -0
  201. package/lib/utils/errors.js +6 -1
  202. package/lib/utils/result.d.ts +9 -0
  203. package/lib/utils/result.js +9 -0
  204. package/lib/workflow/frontend-task.schema.d.ts +83 -0
  205. package/lib/workflow/frontend-task.schema.js +25 -0
  206. package/lib/workflow/frontend-task.service.d.ts +57 -0
  207. package/lib/workflow/frontend-task.service.js +195 -0
  208. package/lib/workflow/workflow-status.d.ts +2 -0
  209. package/lib/workflow/workflow-status.js +14 -0
  210. package/lib/workflow/workflow-transition.d.ts +9 -0
  211. package/lib/workflow/workflow-transition.js +38 -0
  212. package/package.json +5 -3
  213. package/lib/tools/hub-v2/config.d.ts +0 -34
  214. package/lib/tools/hub-v2/config.js +0 -297
package/README.md CHANGED
@@ -1,69 +1,50 @@
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:
1
+ # @yinuo-ngm/mcp-server
2
+
3
+ Local MCP Server for ng-manager.
4
+
5
+ Provides MCP tools that expose ng-manager capabilities to AI Agents such as Codex, OpenCode, Claude Code, Cursor, and other MCP-compatible clients.
6
+
7
+ The MCP server is an adapter layer only. Business logic belongs in `packages/core`.
8
+
9
+ ---
10
+
11
+ ## Architecture
10
12
 
11
13
  ```text
12
- MCP client -> packages/mcp-server -> ToolContext.services -> packages/core
14
+ AI Agent
15
+
16
+ MCP Client
17
+
18
+ packages/mcp-server
19
+
20
+ ToolContext.services
21
+
22
+ packages/core
23
+
24
+ Local Services
25
+ ```
26
+
27
+ Principles:
28
+
29
+ - MCP tools are thin adapters
30
+ - No duplicated business logic
31
+ - No Fastify dependency
32
+ - No Electron dependency
33
+ - Reuse the same core services as CLI, UI, and Local Server
34
+
35
+ ---
36
+
37
+ ## Quick Start
38
+
39
+ Repository root:
40
+
41
+ ```bash
42
+ npm run mcp:dev
43
+ npm run mcp:build
44
+ npm run mcp:start
13
45
  ```
14
46
 
15
- For ng-manager local capabilities, MCP tools should prefer `packages/core`. HTTP routes, Electron IPC, CLI commands, and MCP tools should adapt the same local core services instead of duplicating business logic or calling the local Fastify API.
16
-
17
- Hub V2 tools are the exception: they may call the Hub V2 Token HTTP API because that API is the integration contract Hub V2 exposes to AI Agents and other external clients. Those tools must keep token handling inside configuration/client layers and must not accept token values as tool arguments.
18
-
19
- The MCP server must not provide arbitrary shell execution, mutate system environment settings, or remotely execute client-side commands.
20
-
21
- ## Safety
22
-
23
- Tools are assigned one risk level:
24
-
25
- ```text
26
- read
27
- write
28
- execute
29
- dangerous
30
- ```
31
-
32
- Default policy:
33
-
34
- ```text
35
- read allowed
36
- write blocked
37
- execute blocked
38
- dangerous blocked
39
- ```
40
-
41
- Write tools are registered only for scoped Hub V2 Personal Token workflows that require explicit tool confirmation when implemented. The server does not implement arbitrary shell execution, task start/stop/restart, git pull/checkout/commit/reset, proxy reload, runtime install/remove, file deletion, system environment mutation, or remote client command execution.
42
-
43
- ## Environment
44
-
45
- ```text
46
- NGM_DATA_DIR ng-manager data directory. Defaults to ~/.ng-manager.
47
- NGM_WORKSPACE_ROOT Optional workspace hint. Defaults to process.cwd().
48
- NGM_MCP_UPLOAD_ROOT Optional extra root for Hub V2 markdown image uploads.
49
- NGM_MCP_MAX_UPLOAD_BYTES Max Hub V2 markdown image upload bytes. Defaults to 5242880.
50
- NGM_MCP_MAX_RESULT_CHARS Max MCP text result characters. Defaults to 120000.
51
- NGM_MCP_ALLOW_WRITE Enables confirmed write tools. Defaults to false.
52
- NGM_MCP_ALLOW_EXECUTE Enables execute tools. Defaults to false.
53
- NGM_MCP_ALLOW_DANGEROUS Enables dangerous tools. Defaults to false.
54
- ```
55
-
56
- ## Commands
57
-
58
- From the repository root:
59
-
60
- ```bash
61
- npm run mcp:dev
62
- npm run mcp:build
63
- npm run mcp:start
64
- ```
65
-
66
- Direct workspace commands:
47
+ Workspace commands:
67
48
 
68
49
  ```bash
69
50
  npm run dev -w @yinuo-ngm/mcp-server
@@ -71,172 +52,174 @@ npm run build -w @yinuo-ngm/mcp-server
71
52
  npm run start -w @yinuo-ngm/mcp-server
72
53
  ```
73
54
 
74
- CLI entrypoint:
55
+ CLI:
75
56
 
76
57
  ```bash
77
58
  ngm mcp
78
59
  ```
79
60
 
80
- The server uses stdio transport only. It does not listen on an HTTP port, and stdout is reserved for the MCP protocol.
81
-
82
- ## MCP Client Configuration
83
-
84
- Built output:
85
-
86
- ```json
87
- {
88
- "mcpServers": {
89
- "ng-manager": {
90
- "command": "node",
91
- "args": [
92
- "D:/ng-manager/packages/mcp-server/lib/index.js"
93
- ],
94
- "env": {
95
- "NGM_DATA_DIR": "C:/Users/you/.ng-manager",
96
- "NGM_WORKSPACE_ROOT": "D:/ng-manager",
97
- "NGM_MCP_ALLOW_WRITE": "false",
98
- "NGM_MCP_ALLOW_EXECUTE": "false",
99
- "NGM_MCP_ALLOW_DANGEROUS": "false"
100
- }
101
- }
102
- }
103
- }
104
- ```
105
-
106
- Development:
107
-
108
- ```json
109
- {
110
- "mcpServers": {
111
- "ng-manager": {
112
- "command": "npm",
113
- "args": [
114
- "run",
115
- "dev",
116
- "-w",
117
- "@yinuo-ngm/mcp-server"
118
- ],
119
- "env": {
120
- "NGM_DATA_DIR": "C:/Users/you/.ng-manager",
121
- "NGM_WORKSPACE_ROOT": "D:/ng-manager"
122
- }
123
- }
124
- }
61
+ Diagnostics:
62
+
63
+ ```bash
64
+ ngm mcp doctor
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Environment Variables
70
+
71
+ | Variable | Description |
72
+ |-----------|-------------|
73
+ | NGM_DATA_DIR | ng-manager data directory |
74
+ | NGM_MCP_ALLOW_WRITE | Enable write tools |
75
+ | NGM_MCP_ALLOW_EXECUTE | Enable execute tools |
76
+ | NGM_MCP_ALLOW_DANGEROUS | Enable dangerous tools |
77
+ | NGM_MCP_ALLOW_HUB | Enable Hub V2 write operations |
78
+ | NGM_MCP_MAX_RESULT_CHARS | MCP response size limit |
79
+
80
+ ---
81
+
82
+ ## MCP Client Example
83
+
84
+ ```json
85
+ {
86
+ "mcpServers": {
87
+ "ng-manager": {
88
+ "command": "node",
89
+ "args": [
90
+ "packages/mcp-server/lib/index.js"
91
+ ],
92
+ "env": {
93
+ "NGM_DATA_DIR": "~/.ng-manager"
94
+ }
95
+ }
96
+ }
125
97
  }
126
98
  ```
127
99
 
128
- ## Hub V2 Config
100
+ ---
101
+
102
+ ## Tool Categories
103
+
104
+ ### Workspace
105
+
106
+ - Workspace discovery
107
+ - Package metadata
108
+ - Capability routing
109
+ - Context generation
110
+
111
+ ### Project
112
+
113
+ - Project metadata
114
+ - Package.json access
115
+ - Script discovery
116
+ - Task status and logs
117
+
118
+ ### Runtime
119
+
120
+ - Node runtime discovery
121
+ - Runtime resolution
122
+ - Project runtime configuration
129
123
 
130
- Hub V2 tools read token configuration from MCP server configuration only. Tool schemas do not accept token arguments.
124
+ ### Nginx
131
125
 
132
- Configuration priority is:
126
+ - Status inspection
127
+ - Configuration validation
128
+ - Proxy management
129
+
130
+ ### Git
131
+
132
+ - Status and diff
133
+ - Commit message generation
134
+ - Review assistance
135
+
136
+ ### Frontend Workflow
137
+
138
+ - Standards validation
139
+ - Review scanning
140
+ - Task planning
141
+ - Delivery reports
142
+
143
+ ### Hub V2
144
+
145
+ - Documents
146
+ - Issues
147
+ - RD workflows
148
+ - File uploads
149
+
150
+ ---
151
+
152
+ ## Safety Model
153
+
154
+ Risk levels:
133
155
 
134
156
  ```text
135
- tool args project/projectKey
136
- HUB_V2_* environment variables
137
- HUB_V2_CONFIG explicit config path
138
- ~/.ng-manager/agent-connections.json
157
+ read
158
+ write
159
+ execute
160
+ dangerous
139
161
  ```
140
162
 
141
- Use `~/.ng-manager/agent-connections.json` for persistent local configuration, and `HUB_V2_*` environment variables for temporary overrides, tests, or MCP client injection. Treat `agent-connections.json` as a local secret file: do not commit it, do not pass tokens as tool arguments, and do not print full tokens in logs or Agent replies.
142
-
143
- ## Tools
144
-
145
- Project:
146
-
147
- ```text
148
- ngm.project.list
149
- ngm.project.get
150
- ngm.project.getScripts
151
- ```
152
-
153
- Task:
154
-
155
- ```text
156
- ngm.task.list
157
- ngm.task.getStatus
158
- ```
159
-
160
- Log:
161
-
162
- ```text
163
- ngm.log.tail
164
- ngm.log.search
165
- ```
166
-
167
- Git:
168
-
169
- ```text
170
- ngm.git.status
171
- ngm.git.diff
172
- ```
173
-
174
- 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.
175
-
176
- Runtime:
163
+ Default policy:
177
164
 
178
165
  ```text
179
- ngm.runtime.list
180
- ngm.runtime.resolveForProject
166
+ read allowed
167
+ write blocked
168
+ execute blocked
169
+ dangerous blocked
181
170
  ```
182
171
 
183
- Proxy:
184
-
185
- ```text
186
- ngm.proxy.list
187
- ngm.proxy.validate
188
- ```
189
-
190
- In this package, "proxy" means ng-manager's current Nginx/proxy management domain, not the operating system global proxy.
172
+ Write and execute operations require:
173
+
174
+ - `confirm=true`
175
+ - Matching environment flag enabled
176
+ - Policy validation passed
177
+
178
+ The MCP server does not provide:
179
+
180
+ - Arbitrary shell execution
181
+ - Arbitrary file system access
182
+ - System environment mutation
183
+ - Remote client command execution
191
184
 
192
- Hub V2:
185
+ ---
186
+
187
+ ## Documentation
188
+
189
+ Detailed documentation is maintained under:
190
+
191
+ ```text
192
+ packages/mcp-server/docs/
193
+ ```
194
+
195
+ Recommended structure:
193
196
 
194
197
  ```text
195
- hub_v2_projects_list
196
- hub_v2_projects_get
197
- hub_v2_docs_list
198
- hub_v2_docs_get
199
- hub_v2_docs_get_by_slug
200
- hub_v2_issues_list
201
- hub_v2_issues_get
202
- hub_v2_issues_create
203
- hub_v2_issues_comment
204
- hub_v2_issues_update
205
- hub_v2_upload_markdown_image
206
- hub_v2_rd_list
207
- hub_v2_rd_get
208
- hub_v2_rd_stage_tasks_list
209
- hub_v2_rd_create
210
- hub_v2_rd_advance_stage
211
- hub_v2_rd_stage_tasks_create
212
- hub_v2_rd_update_progress
198
+ docs/
199
+ ├─ architecture.md
200
+ ├─ security.md
201
+ ├─ configuration.md
202
+ └─ tools/
203
+ ├─ ngm-workspace.md
204
+ ├─ ngm-project.md
205
+ ├─ ngm-runtime.md
206
+ ├─ ngm-nginx.md
207
+ ├─ ngm-git.md
208
+ └─ hub-v2.md
213
209
  ```
214
210
 
215
- Hub V2 reads use Project Token configuration and writes use Personal Token configuration. `hub_v2_upload_markdown_image` uploads image files for Markdown bodies and returns a snippet that can be inserted into RD descriptions, RD stage-task descriptions, Issue descriptions, or Issue comments before calling the matching create/comment tool. Prefer `HUB_V2_*` environment variables for temporary overrides and `~/.ng-manager/agent-connections.json` for persistent local configuration.
216
-
217
-
218
- ## Result Shape
219
-
220
- All tools return structured JSON as text content:
221
-
222
- ```json
223
- {
224
- "ok": true,
225
- "tool": "ngm.project.list",
226
- "data": []
227
- }
228
- ```
229
-
230
- Errors use:
231
-
232
- ```json
233
- {
234
- "ok": false,
235
- "tool": "ngm.project.get",
236
- "error": "projectId or projectPath is required"
237
- }
238
- ```
239
-
240
- ## Replacing Stubs
241
-
242
- 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.
211
+ ---
212
+
213
+ ## Design Goal
214
+
215
+ ng-manager MCP Server is the unified AI integration layer for local-first engineering workflows.
216
+
217
+ ```text
218
+ AI Agent
219
+
220
+ MCP
221
+
222
+ ng-manager Core
223
+
224
+ Local Control Plane
225
+ ```
@@ -0,0 +1,14 @@
1
+ import type { ToolRiskLevel } from "../policy/tool-policy";
2
+ import type { ToolResult } from "../utils/result";
3
+ export type AuditToolEvent = {
4
+ tool: string;
5
+ riskLevel: ToolRiskLevel;
6
+ args?: unknown;
7
+ result?: ToolResult;
8
+ error?: unknown;
9
+ durationMs: number;
10
+ };
11
+ export type AuditWarning = {
12
+ code: "AUDIT_LOG_WRITE_FAILED";
13
+ message: string;
14
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,7 @@
1
+ import type { ToolRiskLevel } from "../policy/tool-policy";
2
+ import type { ToolContext } from "../context/tool-context";
3
+ import type { AuditToolEvent, AuditWarning } from "./audit-event";
4
+ export declare function summarizeAuditArgs(args: unknown): Record<string, unknown>;
5
+ export declare function writeAuditLog(context: ToolContext, event: AuditToolEvent): Promise<void>;
6
+ export declare function shouldAuditTool(toolName: string, riskLevel: ToolRiskLevel): boolean;
7
+ export declare function auditWarning(error: unknown): AuditWarning;
@@ -0,0 +1,187 @@
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.summarizeAuditArgs = summarizeAuditArgs;
37
+ exports.writeAuditLog = writeAuditLog;
38
+ exports.shouldAuditTool = shouldAuditTool;
39
+ exports.auditWarning = auditWarning;
40
+ const fs = __importStar(require("fs/promises"));
41
+ const path = __importStar(require("path"));
42
+ const project_files_1 = require("../filesystem/project-files");
43
+ const redact_1 = require("./redact");
44
+ function isRecord(value) {
45
+ return typeof value === "object" && value !== null && !Array.isArray(value);
46
+ }
47
+ function getString(value, key) {
48
+ return isRecord(value) && typeof value[key] === "string" ? value[key] : undefined;
49
+ }
50
+ function normalizePath(value) {
51
+ const resolved = path.resolve(value);
52
+ return process.platform === "win32" ? resolved.replace(/\\/g, "/").toLowerCase() : resolved;
53
+ }
54
+ async function registeredProjectByPath(context, projectPath) {
55
+ const list = context.services.core?.project?.list;
56
+ if (typeof list !== "function")
57
+ return null;
58
+ let projects;
59
+ try {
60
+ projects = await list.call(context.services.core.project);
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ if (!Array.isArray(projects))
66
+ return null;
67
+ const requested = normalizePath(projectPath);
68
+ const matched = projects.find((project) => typeof project?.root === "string" && normalizePath(project.root) === requested);
69
+ return matched ? {
70
+ projectId: typeof matched.id === "string" ? matched.id : undefined,
71
+ projectName: typeof matched.name === "string" ? matched.name : undefined,
72
+ projectRoot: path.resolve(matched.root),
73
+ } : null;
74
+ }
75
+ async function resolveAuditProjectRoot(context, args) {
76
+ const projectId = getString(args, "projectId");
77
+ if (projectId) {
78
+ const project = await context.services.core.project.get(projectId);
79
+ return {
80
+ projectId: project.id,
81
+ projectName: project.name,
82
+ projectRoot: path.resolve(project.root),
83
+ };
84
+ }
85
+ const projectPath = getString(args, "projectPath");
86
+ if (projectPath) {
87
+ const requestedRoot = path.resolve(projectPath);
88
+ const registered = await registeredProjectByPath(context, requestedRoot);
89
+ if (registered)
90
+ return registered;
91
+ throw new Error("Audit projectPath must match a registered project root");
92
+ }
93
+ return {
94
+ projectRoot: path.resolve(context.workspaceRoot),
95
+ };
96
+ }
97
+ function byteLength(value) {
98
+ return typeof value === "string" ? Buffer.byteLength(value, "utf-8") : undefined;
99
+ }
100
+ function shortText(value, maxChars = 200) {
101
+ if (typeof value !== "string")
102
+ return undefined;
103
+ const redacted = (0, redact_1.redactText)(value);
104
+ return redacted.length > maxChars ? `${redacted.slice(0, maxChars)}...` : redacted;
105
+ }
106
+ function summarizeAuditArgs(args) {
107
+ const source = isRecord(args) ? args : {};
108
+ const out = {};
109
+ for (const key of ["projectId", "projectPath", "taskId"]) {
110
+ const value = getString(source, key);
111
+ if (value)
112
+ out[key] = value;
113
+ }
114
+ for (const key of ["confirm", "dryRun", "overwrite"]) {
115
+ if (typeof source[key] === "boolean")
116
+ out[key] = source[key];
117
+ }
118
+ const title = shortText(source.title);
119
+ if (title)
120
+ out.title = title;
121
+ const patchBytes = byteLength(source.patch);
122
+ if (patchBytes !== undefined)
123
+ out.patchBytes = patchBytes;
124
+ const contextBytes = byteLength(source.context);
125
+ if (contextBytes !== undefined)
126
+ out.contextBytes = contextBytes;
127
+ const markdownBytes = byteLength(source.markdown);
128
+ if (markdownBytes !== undefined)
129
+ out.markdownBytes = markdownBytes;
130
+ const summaryBytes = byteLength(source.summary);
131
+ if (summaryBytes !== undefined)
132
+ out.summaryBytes = summaryBytes;
133
+ return out;
134
+ }
135
+ function resultStatus(result, error) {
136
+ if (error) {
137
+ const message = error instanceof Error ? error.message : String(error);
138
+ return message.includes("blocked by policy") ? "blocked" : "failed";
139
+ }
140
+ if (!result)
141
+ return "unknown";
142
+ if (!result.ok)
143
+ return "failed";
144
+ const data = isRecord(result.data) ? result.data : {};
145
+ const operation = isRecord(data.operation) ? data.operation : undefined;
146
+ return typeof operation?.status === "string" ? operation.status : "ok";
147
+ }
148
+ function changedFilesFromResult(result) {
149
+ if (!result?.ok || !isRecord(result.data))
150
+ return [];
151
+ const value = result.data.changedFiles;
152
+ if (!Array.isArray(value))
153
+ return [];
154
+ return value.filter((item) => typeof item === "string").slice(0, 100);
155
+ }
156
+ function todayFileName(now = new Date()) {
157
+ return `mcp-${now.toISOString().slice(0, 10)}.jsonl`;
158
+ }
159
+ async function writeAuditLog(context, event) {
160
+ const args = isRecord(event.args) ? event.args : {};
161
+ const project = await resolveAuditProjectRoot(context, args);
162
+ const auditPath = (0, project_files_1.resolveNgManagerPath)(project.projectRoot, "audit", todayFileName());
163
+ const entry = {
164
+ time: new Date().toISOString(),
165
+ tool: event.tool,
166
+ riskLevel: event.riskLevel,
167
+ projectId: getString(args, "projectId") ?? project.projectId,
168
+ projectRoot: project.projectRoot,
169
+ taskId: getString(args, "taskId"),
170
+ status: resultStatus(event.result, event.error),
171
+ changedFiles: changedFilesFromResult(event.result),
172
+ durationMs: event.durationMs,
173
+ error: event.error ? (0, redact_1.redactValue)(event.error instanceof Error ? event.error.message : String(event.error)) : undefined,
174
+ argsSummary: summarizeAuditArgs(event.args),
175
+ };
176
+ await fs.mkdir(path.dirname(auditPath), { recursive: true });
177
+ await fs.appendFile(auditPath, `${JSON.stringify(entry)}\n`, "utf-8");
178
+ }
179
+ function shouldAuditTool(toolName, riskLevel) {
180
+ return riskLevel !== "read" || toolName.startsWith("ngm_workflow_");
181
+ }
182
+ function auditWarning(error) {
183
+ return {
184
+ code: "AUDIT_LOG_WRITE_FAILED",
185
+ message: error instanceof Error ? error.message : String(error),
186
+ };
187
+ }
@@ -0,0 +1,3 @@
1
+ export declare const SENSITIVE_KEY_RE: RegExp;
2
+ export declare function redactText(value: string): string;
3
+ export declare function redactValue(value: unknown): unknown;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SENSITIVE_KEY_RE = void 0;
4
+ exports.redactText = redactText;
5
+ exports.redactValue = redactValue;
6
+ exports.SENSITIVE_KEY_RE = /(authorization|cookie|token|password|passwd|secret|api[-_]?key|access[-_]?token|refresh[-_]?token|private[-_]?key|env)/i;
7
+ function redactText(value) {
8
+ return value
9
+ .replace(/(^|\n)([A-Z0-9_]*(?:TOKEN|PASSWORD|PASSWD|SECRET|API_KEY|ACCESS_TOKEN|REFRESH_TOKEN|AUTHORIZATION|COOKIE)[A-Z0-9_]*\s*=\s*)[^\r\n]+/gi, "$1$2[REDACTED]")
10
+ .replace(/(^|\n)(\s*[A-Za-z0-9_.-]*\.env\s*[:=]\s*)[^\r\n]+/gi, "$1$2[REDACTED]")
11
+ .replace(/(authorization\s*[:=]\s*)(bearer\s+)?[^\s,;]+/gi, "$1[REDACTED]")
12
+ .replace(/(cookie\s*[:=]\s*)[^\r\n]+/gi, "$1[REDACTED]")
13
+ .replace(/((?:token|password|passwd|secret|api[-_]?key|access[-_]?token|refresh[-_]?token|private[-_]?key)\s*[:=]\s*)("[^"]*"|'[^']*'|[^\s,;&]+)/gi, "$1[REDACTED]")
14
+ .replace(/([?&](?:token|password|passwd|secret|api[-_]?key|access[-_]?token|refresh[-_]?token)=)[^&\s]+/gi, "$1[REDACTED]");
15
+ }
16
+ function redactValue(value) {
17
+ if (typeof value === "string")
18
+ return redactText(value);
19
+ if (Array.isArray(value))
20
+ return value.map(redactValue);
21
+ if (typeof value !== "object" || value === null)
22
+ return value;
23
+ const out = {};
24
+ for (const [key, item] of Object.entries(value)) {
25
+ out[key] = exports.SENSITIVE_KEY_RE.test(key) ? "[REDACTED]" : redactValue(item);
26
+ }
27
+ return out;
28
+ }
@@ -0,0 +1 @@
1
+ export declare const blockedLocalActions: string[];