@vibecodetown/mcp-server 2.1.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.
Files changed (172) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +269 -0
  3. package/build/auth/gate.js +225 -0
  4. package/build/auth/index.js +55 -0
  5. package/build/auth/public_key.js +27 -0
  6. package/build/auth/token_cache.js +122 -0
  7. package/build/auth/token_verifier.js +103 -0
  8. package/build/bootstrap/doctor.js +115 -0
  9. package/build/bootstrap/installer.js +673 -0
  10. package/build/bootstrap/lock.js +37 -0
  11. package/build/bootstrap/platform.js +26 -0
  12. package/build/bootstrap/registry.js +37 -0
  13. package/build/cache/index.js +147 -0
  14. package/build/cli.js +101 -0
  15. package/build/contracts.js +22 -0
  16. package/build/control_plane/gate.js +161 -0
  17. package/build/control_plane/index.js +6 -0
  18. package/build/dx/activity.js +139 -0
  19. package/build/engine.js +106 -0
  20. package/build/errors.js +171 -0
  21. package/build/generated/activate_input.js +2 -0
  22. package/build/generated/activate_output.js +57 -0
  23. package/build/generated/advisory_review_input.js +2 -0
  24. package/build/generated/advisory_review_output.js +35 -0
  25. package/build/generated/auth_token_file.js +2 -0
  26. package/build/generated/briefing_input.js +2 -0
  27. package/build/generated/briefing_output.js +2 -0
  28. package/build/generated/clinic_bridge_file.js +13 -0
  29. package/build/generated/contracts_bundle_info.js +5 -0
  30. package/build/generated/create_work_order_input.js +2 -0
  31. package/build/generated/create_work_order_output.js +2 -0
  32. package/build/generated/current_work_order_file.js +2 -0
  33. package/build/generated/doctor_input.js +2 -0
  34. package/build/generated/doctor_output.js +24 -0
  35. package/build/generated/execution_result.js +2 -0
  36. package/build/generated/execution_task.js +2 -0
  37. package/build/generated/export_output_input.js +2 -0
  38. package/build/generated/export_output_output.js +2 -0
  39. package/build/generated/finalize_work_input.js +2 -0
  40. package/build/generated/finalize_work_output.js +2 -0
  41. package/build/generated/gate_input.js +2 -0
  42. package/build/generated/gate_output.js +2 -0
  43. package/build/generated/gate_result_v1.js +2 -0
  44. package/build/generated/get_decision_input.js +2 -0
  45. package/build/generated/get_decision_output.js +13 -0
  46. package/build/generated/handoff_to_clinic.js +2 -0
  47. package/build/generated/index.js +75 -0
  48. package/build/generated/inspect_code_input.js +2 -0
  49. package/build/generated/inspect_code_output.js +13 -0
  50. package/build/generated/memory_retrieve_output.js +2 -0
  51. package/build/generated/memory_state_file.js +2 -0
  52. package/build/generated/memory_status_input.js +2 -0
  53. package/build/generated/memory_status_output.js +13 -0
  54. package/build/generated/memory_sync_input.js +2 -0
  55. package/build/generated/memory_sync_output.js +13 -0
  56. package/build/generated/plugin_result.js +2 -0
  57. package/build/generated/react_perf_check_patterns_input.js +2 -0
  58. package/build/generated/react_perf_check_patterns_output.js +2 -0
  59. package/build/generated/react_perf_generate_report_input.js +2 -0
  60. package/build/generated/react_perf_generate_report_output.js +2 -0
  61. package/build/generated/repair_plan_input.js +2 -0
  62. package/build/generated/repair_plan_output.js +2 -0
  63. package/build/generated/run_app_input.js +2 -0
  64. package/build/generated/run_app_output.js +2 -0
  65. package/build/generated/run_state_file.js +13 -0
  66. package/build/generated/scaffold_input.js +2 -0
  67. package/build/generated/scaffold_output.js +2 -0
  68. package/build/generated/search_oss_input.js +2 -0
  69. package/build/generated/search_oss_output.js +2 -0
  70. package/build/generated/selection_validation_result.js +2 -0
  71. package/build/generated/signal_agent_input.js +2 -0
  72. package/build/generated/spec_high_ask_queue_items_file.js +2 -0
  73. package/build/generated/spec_high_clinic_bridge_output.js +2 -0
  74. package/build/generated/spec_high_decision_draft_output.js +2 -0
  75. package/build/generated/spec_high_validate_output.js +2 -0
  76. package/build/generated/status_input.js +2 -0
  77. package/build/generated/status_output.js +2 -0
  78. package/build/generated/submit_decision_input.js +2 -0
  79. package/build/generated/submit_decision_output.js +2 -0
  80. package/build/generated/tool_error_output.js +2 -0
  81. package/build/generated/undo_last_task_input.js +2 -0
  82. package/build/generated/undo_last_task_output.js +2 -0
  83. package/build/generated/update_input.js +2 -0
  84. package/build/generated/update_output.js +2 -0
  85. package/build/generated/vibe_pm_inspection_result.js +2 -0
  86. package/build/generated/vibe_pm_report_markdown.js +2 -0
  87. package/build/generated/vibe_pm_verdict.js +2 -0
  88. package/build/generated/vibe_repo_config.js +2 -0
  89. package/build/generated/vibecoding_helper_answer_output.js +2 -0
  90. package/build/generated/vibecoding_helper_one_loop_selection_output.js +2 -0
  91. package/build/generated/vibecoding_helper_show_ask_queue_output.js +2 -0
  92. package/build/generated/work_order_v1.js +2 -0
  93. package/build/generated/zoekt_evidence_input.js +2 -0
  94. package/build/generated/zoekt_evidence_output.js +2 -0
  95. package/build/index.js +111 -0
  96. package/build/legacy_alias.js +65 -0
  97. package/build/local-mode/bash.js +61 -0
  98. package/build/local-mode/config.js +171 -0
  99. package/build/local-mode/git.js +33 -0
  100. package/build/local-mode/init.js +110 -0
  101. package/build/local-mode/paths.js +24 -0
  102. package/build/local-mode/templates.js +856 -0
  103. package/build/local-mode/work-order.js +41 -0
  104. package/build/resources/index.js +246 -0
  105. package/build/security/input-validator.js +119 -0
  106. package/build/security/path-policy.js +289 -0
  107. package/build/security/sandbox.js +228 -0
  108. package/build/tools/react_perf/check_patterns.js +172 -0
  109. package/build/tools/react_perf/generate_report.js +337 -0
  110. package/build/tools/react_perf/index.js +119 -0
  111. package/build/tools/react_perf/rules/advanced.js +325 -0
  112. package/build/tools/react_perf/rules/async.js +104 -0
  113. package/build/tools/react_perf/rules/bundle.js +101 -0
  114. package/build/tools/react_perf/rules/client.js +186 -0
  115. package/build/tools/react_perf/rules/index.js +74 -0
  116. package/build/tools/react_perf/rules/js.js +148 -0
  117. package/build/tools/react_perf/rules/rendering.js +166 -0
  118. package/build/tools/react_perf/rules/rerender.js +161 -0
  119. package/build/tools/react_perf/rules/server.js +141 -0
  120. package/build/tools/react_perf/types.js +127 -0
  121. package/build/tools/vibe_pm/activate.js +102 -0
  122. package/build/tools/vibe_pm/advisory_review.js +77 -0
  123. package/build/tools/vibe_pm/briefing.js +178 -0
  124. package/build/tools/vibe_pm/context.js +439 -0
  125. package/build/tools/vibe_pm/create_work_order.js +271 -0
  126. package/build/tools/vibe_pm/doc_status_gate.js +370 -0
  127. package/build/tools/vibe_pm/doctor.js +262 -0
  128. package/build/tools/vibe_pm/entity_gate/preflight.js +78 -0
  129. package/build/tools/vibe_pm/export_output.js +135 -0
  130. package/build/tools/vibe_pm/finalize_work.js +393 -0
  131. package/build/tools/vibe_pm/gate.js +33 -0
  132. package/build/tools/vibe_pm/get_decision.js +281 -0
  133. package/build/tools/vibe_pm/index.js +593 -0
  134. package/build/tools/vibe_pm/inspect_code.js +828 -0
  135. package/build/tools/vibe_pm/intent/generator.js +294 -0
  136. package/build/tools/vibe_pm/intent/index.js +5 -0
  137. package/build/tools/vibe_pm/intent/prompt_density.js +227 -0
  138. package/build/tools/vibe_pm/intent/types.js +70 -0
  139. package/build/tools/vibe_pm/intent/verifier.js +237 -0
  140. package/build/tools/vibe_pm/kce/doc_usage.js +51 -0
  141. package/build/tools/vibe_pm/kce/on_finalize.js +11 -0
  142. package/build/tools/vibe_pm/kce/preflight.js +232 -0
  143. package/build/tools/vibe_pm/local_memory.js +26 -0
  144. package/build/tools/vibe_pm/memory_status.js +82 -0
  145. package/build/tools/vibe_pm/memory_sync.js +134 -0
  146. package/build/tools/vibe_pm/modules/decision_snapshot.js +29 -0
  147. package/build/tools/vibe_pm/modules/ensure.js +100 -0
  148. package/build/tools/vibe_pm/modules/fingerprint.js +30 -0
  149. package/build/tools/vibe_pm/modules/fix_dependencies.js +394 -0
  150. package/build/tools/vibe_pm/modules/planning_v1.js +110 -0
  151. package/build/tools/vibe_pm/modules/repo_context.js +56 -0
  152. package/build/tools/vibe_pm/modules/research_v1.js +114 -0
  153. package/build/tools/vibe_pm/modules/skills_v1.js +100 -0
  154. package/build/tools/vibe_pm/pm_language.js +222 -0
  155. package/build/tools/vibe_pm/repair_plan.js +199 -0
  156. package/build/tools/vibe_pm/run_app.js +597 -0
  157. package/build/tools/vibe_pm/run_app_podman.js +64 -0
  158. package/build/tools/vibe_pm/scaffold.js +550 -0
  159. package/build/tools/vibe_pm/search_oss.js +124 -0
  160. package/build/tools/vibe_pm/status.js +153 -0
  161. package/build/tools/vibe_pm/submit_decision.js +87 -0
  162. package/build/tools/vibe_pm/system_design/issue_mapping.js +47 -0
  163. package/build/tools/vibe_pm/system_design/rulebook.js +112 -0
  164. package/build/tools/vibe_pm/system_design/semgrep.js +132 -0
  165. package/build/tools/vibe_pm/types.js +229 -0
  166. package/build/tools/vibe_pm/undo_last_task.js +163 -0
  167. package/build/tools/vibe_pm/update.js +146 -0
  168. package/build/tools/vibe_pm/zoekt_evidence.js +96 -0
  169. package/build/tools.js +269 -0
  170. package/build/version-check.js +239 -0
  171. package/build/vibe-cli.js +631 -0
  172. package/package.json +76 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 VibeCoding Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,269 @@
1
+ # @vibecode/mcp-server
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@vibecode/mcp-server.svg)](https://www.npmjs.com/package/@vibecode/mcp-server)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![Tests](https://img.shields.io/badge/tests-140%20passing-brightgreen.svg)](#testing)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue.svg)](https://www.typescriptlang.org/)
7
+
8
+ **Vibe PM** - AI Project Manager for Non-Technical Founders
9
+
10
+ MCP (Model Context Protocol) server that transforms AI coding assistants into project managers who protect your codebase from chaos.
11
+
12
+ ## Features
13
+
14
+ - **7 PM-focused tools** (`vibe_pm.*`) for structured project management
15
+ - **Natural language interface** - no CLI commands to learn
16
+ - **Automatic context tracking** - no run_id management
17
+ - **PM-friendly output** - GO/FIX/BLOCK instead of technical jargon
18
+ - **Self-healing bootstrap** - automatic engine binary download
19
+
20
+ ## Installation
21
+
22
+ ### One-line Install
23
+
24
+ ```bash
25
+ curl -fsSL https://vibecode.town/install.sh | sh
26
+ ```
27
+
28
+ ### Manual Install
29
+
30
+ ```bash
31
+ npm install -g @vibecode/mcp-server
32
+ ```
33
+
34
+ ### Add to Claude Code
35
+
36
+ ```bash
37
+ claude mcp add vibecode npx @vibecode/mcp-server
38
+ ```
39
+
40
+ ### Add to Cursor
41
+
42
+ Copy `AGENTS.md` to `.cursorrules` in your project root.
43
+
44
+ ## Quick Start
45
+
46
+ Just talk to your AI assistant naturally:
47
+
48
+ ```
49
+ You: "나 당근마켓 같은 거 만들고 싶어. 시작해줘."
50
+
51
+ Vibe PM: "네, 대표님. 중고거래 앱 접수했습니다.
52
+ 시작하기 전에 하나만 결재해 주세요.
53
+ 속도가 중요한가요, 퀄리티가 중요한가요?"
54
+
55
+ You: "속도가 중요해."
56
+
57
+ Vibe PM: "알겠습니다. MVP 모드로 작업 지시서를 뽑았습니다.
58
+ 이제 제가 알아서 코드를 짜겠습니다..."
59
+ ```
60
+
61
+ ## Tools (vibe_pm.* - Recommended)
62
+
63
+ | Tool | Purpose | When to Use |
64
+ |------|---------|-------------|
65
+ | `vibe_pm.briefing` | Start or resume project | User says "시작해줘" or describes idea |
66
+ | `vibe_pm.get_decision` | Fetch approval items | Before any implementation |
67
+ | `vibe_pm.submit_decision` | Submit approval | After user chooses A/B/C |
68
+ | `vibe_pm.create_work_order` | Generate work order | After decisions are made |
69
+ | `vibe_pm.inspect_code` | Review implementation | After coding is complete |
70
+ | `vibe_pm.status` | Check project status | User asks "상태", "어디까지?" |
71
+ | `vibe_pm.repair_plan` | Generate fix request | When inspect returns FIX/BLOCK |
72
+
73
+ ## Workflow
74
+
75
+ ```
76
+ briefing → get_decision → submit_decision → create_work_order → implement → inspect_code
77
+ ```
78
+
79
+ 1. **Briefing**: AI receives your idea and structures it
80
+ 2. **Decision**: You approve scope/approach (A/B/C choices)
81
+ 3. **Work Order**: AI gets clear implementation guidelines
82
+ 4. **Implementation**: AI writes code following the work order
83
+ 5. **Inspection**: AI validates the implementation
84
+
85
+ ## Review Results
86
+
87
+ | Result | Meaning | Action |
88
+ |--------|---------|--------|
89
+ | **GO** | All good | Proceed to next task |
90
+ | **FIX** | Minor issue | AI fixes automatically |
91
+ | **BLOCK** | Major risk | Review and fix required |
92
+
93
+ ## One-click Bootstrap
94
+
95
+ On first tool call, the server automatically:
96
+ 1. Downloads pinned engine binaries from GitHub Releases
97
+ 2. Verifies SHA256 checksums
98
+ 3. Caches them locally
99
+ 4. Executes tools using cached paths (no PATH dependency)
100
+
101
+ ### Engines (auto-installed)
102
+
103
+ | Engine | Purpose |
104
+ |--------|---------|
105
+ | spec-high | Decision validation & bridge generation |
106
+ | vibecoding-helper | Orchestrator CLI |
107
+ | clinic | Code verification |
108
+
109
+ ### Cache Location
110
+
111
+ | Platform | Path |
112
+ |----------|------|
113
+ | Linux | `~/.cache/vibecode/engines/` |
114
+ | macOS | `~/Library/Caches/vibecode/engines/` |
115
+ | Windows | `%LOCALAPPDATA%\vibecode\engines\` |
116
+
117
+ ## Development
118
+
119
+ ```bash
120
+ # Clone and install
121
+ git clone https://github.com/vibecode/vibecoding-helper
122
+ cd vibecoding-helper/adapters/mcp-ts
123
+ npm install
124
+
125
+ # Build
126
+ npm run build
127
+
128
+ # Run in dev mode (watch)
129
+ npm run dev
130
+
131
+ # Start server
132
+ npm start
133
+
134
+ # Type check
135
+ npm run typecheck
136
+ ```
137
+
138
+ ## Testing
139
+
140
+ ```bash
141
+ # Run all tests
142
+ npm test
143
+
144
+ # Run with coverage
145
+ npm run test:coverage
146
+
147
+ # Run specific test file
148
+ npm test -- tests/cache.test.ts
149
+ ```
150
+
151
+ ### Test Coverage
152
+
153
+ | Module | Coverage |
154
+ |--------|----------|
155
+ | `src/cache/` | 100% |
156
+ | `src/security/` | ~93% |
157
+ | `src/tools/vibe_pm/` | ~85% |
158
+
159
+ ### Test Structure
160
+
161
+ ```
162
+ tests/
163
+ ├── cache.test.ts # TieredCache unit tests
164
+ ├── security.test.ts # Input validation & path policy
165
+ ├── tools.test.ts # Tool handler tests
166
+ ├── performance.test.ts # Benchmark tests
167
+ └── e2e/
168
+ ├── engine-integration.test.ts # Engine + cache flow
169
+ └── security-scenarios.test.ts # Attack scenario tests
170
+ ```
171
+
172
+ ## Claude Desktop Config
173
+
174
+ ```json
175
+ {
176
+ "mcpServers": {
177
+ "vibecode": {
178
+ "command": "npx",
179
+ "args": ["@vibecode/mcp-server"]
180
+ }
181
+ }
182
+ }
183
+ ```
184
+
185
+ Or with local build:
186
+
187
+ ```json
188
+ {
189
+ "mcpServers": {
190
+ "vibecode": {
191
+ "command": "node",
192
+ "args": ["<ABSOLUTE_PATH>/adapters/mcp-ts/build/index.js"]
193
+ }
194
+ }
195
+ }
196
+ ```
197
+
198
+ ## Health Check
199
+
200
+ Call `vibecode.doctor` to check engine status:
201
+
202
+ ```json
203
+ {
204
+ "status": "OK",
205
+ "engines": {
206
+ "spec-high": { "version": "1.0.0", "path": "~/.cache/vibecode/..." },
207
+ "vibecoding-helper": { "version": "1.0.0", "path": "..." },
208
+ "clinic": { "version": "2.3.0", "path": "..." }
209
+ }
210
+ }
211
+ ```
212
+
213
+ ## Legacy Tools (Deprecated)
214
+
215
+ For backward compatibility, legacy tool names are supported:
216
+
217
+ | Legacy | New |
218
+ |--------|-----|
219
+ | `vibecode.one_loop` | `vibe_pm.inspect_code` |
220
+ | `vibecode.show_ask_queue` | `vibe_pm.get_decision` |
221
+ | `vibecode.answer` | `vibe_pm.submit_decision` |
222
+ | `vibecode.show_decisions` | `vibe_pm.status` |
223
+ | `spec_high.clinic_bridge` | `vibe_pm.create_work_order` |
224
+ | `spec_high.validate` | `vibe_pm.inspect_code` |
225
+ | `clinic.verify` | `vibe_pm.inspect_code` |
226
+
227
+ ## Error Handling
228
+
229
+ Installation failures return structured errors:
230
+
231
+ ```json
232
+ {
233
+ "status": "ERROR",
234
+ "reason": "sha_mismatch",
235
+ "message": "Downloaded file doesn't match expected checksum"
236
+ }
237
+ ```
238
+
239
+ Common reasons:
240
+ - `sha_mismatch`: Checksum verification failed
241
+ - `download_failed`: Network error
242
+ - `bin_not_found_after_extract`: Archive extraction issue
243
+ - `lock_timeout`: Another installation in progress
244
+
245
+ ## Architecture
246
+
247
+ ```
248
+ User (natural language)
249
+
250
+ AI Agent (Claude/Cursor/Codex)
251
+ ↓ AGENTS.md rules
252
+ MCP Server (@vibecode/mcp-server)
253
+ ↓ vibe_pm.* tools
254
+ Bootstrap Layer
255
+ ↓ auto-download if missing
256
+ Engine Binaries (cached)
257
+
258
+ SSOT Files (runs/<run_id>/...)
259
+ ```
260
+
261
+ ## License
262
+
263
+ MIT
264
+
265
+ ## Links
266
+
267
+ - [Documentation](https://vibecode.town/docs)
268
+ - [GitHub](https://github.com/vibecode/vibecoding-helper)
269
+ - [Issues](https://github.com/vibecode/vibecoding-helper/issues)
@@ -0,0 +1,225 @@
1
+ const AUTH_PROXY_URL = process.env.VIBE_AUTH_PROXY_URL || 'https://auth.vibe-pm.dev';
2
+ export class AuthGate {
3
+ cache;
4
+ verifier;
5
+ constructor(cache, verifier) {
6
+ this.cache = cache;
7
+ this.verifier = verifier;
8
+ }
9
+ /**
10
+ * Check if a feature is allowed for the current user
11
+ */
12
+ async check(feature) {
13
+ // Get cached token
14
+ const cached = await this.cache.get();
15
+ if (!cached) {
16
+ return { allowed: false, reason: 'no_token' };
17
+ }
18
+ // Verify token is still valid
19
+ const result = await this.verifier.verify(cached.token);
20
+ if (!result.success) {
21
+ // Clear invalid token
22
+ await this.cache.clear();
23
+ if (result.error === 'expired_token') {
24
+ return { allowed: false, reason: 'expired_token' };
25
+ }
26
+ return { allowed: false, reason: 'invalid_token' };
27
+ }
28
+ const { entitlements, policy } = result.token;
29
+ // Check if feature is enabled
30
+ if (!entitlements[feature]) {
31
+ return {
32
+ allowed: false,
33
+ reason: 'feature_disabled',
34
+ entitlements,
35
+ policy,
36
+ };
37
+ }
38
+ return {
39
+ allowed: true,
40
+ entitlements,
41
+ policy,
42
+ };
43
+ }
44
+ /**
45
+ * Check multiple features at once
46
+ */
47
+ async checkAll(features) {
48
+ const results = new Map();
49
+ // Get token once for all checks
50
+ const cached = await this.cache.get();
51
+ if (!cached) {
52
+ for (const feature of features) {
53
+ results.set(feature, { allowed: false, reason: 'no_token' });
54
+ }
55
+ return results;
56
+ }
57
+ const verifyResult = await this.verifier.verify(cached.token);
58
+ if (!verifyResult.success) {
59
+ await this.cache.clear();
60
+ const reason = verifyResult.error === 'expired_token' ? 'expired_token' : 'invalid_token';
61
+ for (const feature of features) {
62
+ results.set(feature, { allowed: false, reason });
63
+ }
64
+ return results;
65
+ }
66
+ const { entitlements, policy } = verifyResult.token;
67
+ for (const feature of features) {
68
+ if (entitlements[feature]) {
69
+ results.set(feature, { allowed: true, entitlements, policy });
70
+ }
71
+ else {
72
+ results.set(feature, { allowed: false, reason: 'feature_disabled', entitlements, policy });
73
+ }
74
+ }
75
+ return results;
76
+ }
77
+ /**
78
+ * Get current entitlements (if token is valid)
79
+ */
80
+ async getEntitlements() {
81
+ const cached = await this.cache.get();
82
+ if (!cached) {
83
+ return null;
84
+ }
85
+ const result = await this.verifier.verify(cached.token);
86
+ if (!result.success || !result.token) {
87
+ return null;
88
+ }
89
+ return {
90
+ entitlements: result.token.entitlements,
91
+ policy: result.token.policy,
92
+ };
93
+ }
94
+ /**
95
+ * Exchange a license key for an access token
96
+ */
97
+ async exchangeToken(licenseKey, appVersion) {
98
+ try {
99
+ const response = await fetch(`${AUTH_PROXY_URL}/v1/auth/exchange`, {
100
+ method: 'POST',
101
+ headers: { 'Content-Type': 'application/json' },
102
+ body: JSON.stringify({
103
+ client_type: 'mcp_local',
104
+ license_key: licenseKey,
105
+ app_version: appVersion,
106
+ }),
107
+ });
108
+ const data = (await response.json());
109
+ if (!response.ok) {
110
+ return {
111
+ success: false,
112
+ error: data.error || 'exchange_failed',
113
+ };
114
+ }
115
+ if (typeof data.access_token !== 'string' || !data.access_token.trim()) {
116
+ return {
117
+ success: false,
118
+ error: 'missing_access_token',
119
+ };
120
+ }
121
+ // Cache the token
122
+ const accessToken = data.access_token;
123
+ const verifyResult = await this.verifier.verify(accessToken);
124
+ if (!verifyResult.success || !verifyResult.token) {
125
+ return {
126
+ success: false,
127
+ error: 'invalid_response_token',
128
+ };
129
+ }
130
+ await this.cache.set({
131
+ token: accessToken,
132
+ expiresAt: verifyResult.token.expiresAt,
133
+ entitlements: verifyResult.token.entitlements,
134
+ policy: verifyResult.token.policy,
135
+ subjectId: verifyResult.token.subjectId,
136
+ cachedAt: Math.floor(Date.now() / 1000),
137
+ });
138
+ return {
139
+ success: true,
140
+ entitlements: verifyResult.token.entitlements,
141
+ policy: verifyResult.token.policy,
142
+ };
143
+ }
144
+ catch (error) {
145
+ return {
146
+ success: false,
147
+ error: 'network_error',
148
+ };
149
+ }
150
+ }
151
+ /**
152
+ * Refresh the token if needed
153
+ * Returns true if refresh was successful or not needed
154
+ */
155
+ async refreshIfNeeded(appVersion) {
156
+ const cached = await this.cache.get();
157
+ if (!cached) {
158
+ return false; // No token to refresh
159
+ }
160
+ // Check if refresh is needed (within 5 minutes of expiry)
161
+ if (!this.cache.needsRefresh(cached, 300)) {
162
+ return true; // No refresh needed
163
+ }
164
+ // Respect explicit offline mode
165
+ if ((process.env.VIBECODE_OFFLINE || '').trim() === '1') {
166
+ return false;
167
+ }
168
+ try {
169
+ const response = await fetch(`${AUTH_PROXY_URL}/v1/auth/refresh`, {
170
+ method: 'POST',
171
+ headers: {
172
+ 'Content-Type': 'application/json',
173
+ Authorization: `Bearer ${cached.token}`,
174
+ },
175
+ body: JSON.stringify({
176
+ client_type: 'mcp_local',
177
+ app_version: appVersion,
178
+ }),
179
+ });
180
+ const data = (await response.json());
181
+ if (!response.ok) {
182
+ // Clear cached token on hard auth failure
183
+ await this.cache.clear();
184
+ return false;
185
+ }
186
+ if (typeof data.access_token !== 'string' || !data.access_token.trim()) {
187
+ return false;
188
+ }
189
+ const accessToken = data.access_token;
190
+ const verifyResult = await this.verifier.verify(accessToken);
191
+ if (!verifyResult.success || !verifyResult.token) {
192
+ return false;
193
+ }
194
+ await this.cache.set({
195
+ token: accessToken,
196
+ expiresAt: verifyResult.token.expiresAt,
197
+ entitlements: verifyResult.token.entitlements,
198
+ policy: verifyResult.token.policy,
199
+ subjectId: verifyResult.token.subjectId,
200
+ cachedAt: Math.floor(Date.now() / 1000),
201
+ });
202
+ return true;
203
+ }
204
+ catch {
205
+ return false;
206
+ }
207
+ }
208
+ /**
209
+ * Check if user is authenticated (has valid token)
210
+ */
211
+ async isAuthenticated() {
212
+ const cached = await this.cache.get();
213
+ if (!cached) {
214
+ return false;
215
+ }
216
+ const result = await this.verifier.verify(cached.token);
217
+ return result.success;
218
+ }
219
+ /**
220
+ * Log out (clear cached token)
221
+ */
222
+ async logout() {
223
+ await this.cache.clear();
224
+ }
225
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Auth module for MCP client
3
+ *
4
+ * Provides:
5
+ * - Token caching and persistence
6
+ * - Offline JWT verification
7
+ * - Feature gating based on entitlements
8
+ */
9
+ export { TokenCache } from './token_cache.js';
10
+ export { TokenVerifier } from './token_verifier.js';
11
+ export { AuthGate } from './gate.js';
12
+ export { PUBLIC_KEY } from './public_key.js';
13
+ // Re-export convenience functions
14
+ import { TokenCache } from './token_cache.js';
15
+ import { TokenVerifier } from './token_verifier.js';
16
+ import { AuthGate } from './gate.js';
17
+ let _cache = null;
18
+ let _verifier = null;
19
+ let _gate = null;
20
+ /**
21
+ * Get singleton TokenCache instance
22
+ */
23
+ export function getTokenCache() {
24
+ if (!_cache) {
25
+ _cache = new TokenCache();
26
+ }
27
+ return _cache;
28
+ }
29
+ /**
30
+ * Get singleton TokenVerifier instance
31
+ */
32
+ export function getTokenVerifier() {
33
+ if (!_verifier) {
34
+ _verifier = new TokenVerifier();
35
+ }
36
+ return _verifier;
37
+ }
38
+ /**
39
+ * Get singleton AuthGate instance
40
+ */
41
+ export function getAuthGate() {
42
+ if (!_gate) {
43
+ _gate = new AuthGate(getTokenCache(), getTokenVerifier());
44
+ }
45
+ return _gate;
46
+ }
47
+ /**
48
+ * Check if a feature is available for the current user
49
+ * Convenience function that uses the singleton gate
50
+ */
51
+ export async function checkFeature(feature) {
52
+ const gate = getAuthGate();
53
+ const result = await gate.check(feature);
54
+ return result.allowed;
55
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Auth Proxy public key for JWT verification
3
+ *
4
+ * This key is used to verify JWT tokens offline.
5
+ * It is safe to embed in the client as it can only verify, not sign.
6
+ *
7
+ * Algorithm: ES256 (ECDSA P-256)
8
+ *
9
+ * To update this key:
10
+ * 1. Generate new key pair on the auth proxy server
11
+ * 2. Copy the public key here
12
+ * 3. Release new client version
13
+ */
14
+ const ENV_PUBLIC_KEY = (process.env.VIBECODE_AUTH_PUBLIC_KEY || process.env.VIBE_AUTH_PUBLIC_KEY || "").trim();
15
+ export const PUBLIC_KEY = ENV_PUBLIC_KEY ||
16
+ `-----BEGIN PUBLIC KEY-----
17
+ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
18
+ REPLACE_WITH_YOUR_PRODUCTION_PUBLIC_KEY
19
+ -----END PUBLIC KEY-----`;
20
+ /**
21
+ * JWT issuer for verification
22
+ */
23
+ export const JWT_ISSUER = (process.env.VIBECODE_AUTH_JWT_ISSUER || process.env.VIBE_AUTH_JWT_ISSUER || 'vibe-auth').trim();
24
+ /**
25
+ * JWT audience for verification
26
+ */
27
+ export const JWT_AUDIENCE = (process.env.VIBECODE_AUTH_JWT_AUDIENCE || process.env.VIBE_AUTH_JWT_AUDIENCE || 'vibe-client').trim();