@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.
- package/LICENSE +21 -0
- package/README.md +269 -0
- package/build/auth/gate.js +225 -0
- package/build/auth/index.js +55 -0
- package/build/auth/public_key.js +27 -0
- package/build/auth/token_cache.js +122 -0
- package/build/auth/token_verifier.js +103 -0
- package/build/bootstrap/doctor.js +115 -0
- package/build/bootstrap/installer.js +673 -0
- package/build/bootstrap/lock.js +37 -0
- package/build/bootstrap/platform.js +26 -0
- package/build/bootstrap/registry.js +37 -0
- package/build/cache/index.js +147 -0
- package/build/cli.js +101 -0
- package/build/contracts.js +22 -0
- package/build/control_plane/gate.js +161 -0
- package/build/control_plane/index.js +6 -0
- package/build/dx/activity.js +139 -0
- package/build/engine.js +106 -0
- package/build/errors.js +171 -0
- package/build/generated/activate_input.js +2 -0
- package/build/generated/activate_output.js +57 -0
- package/build/generated/advisory_review_input.js +2 -0
- package/build/generated/advisory_review_output.js +35 -0
- package/build/generated/auth_token_file.js +2 -0
- package/build/generated/briefing_input.js +2 -0
- package/build/generated/briefing_output.js +2 -0
- package/build/generated/clinic_bridge_file.js +13 -0
- package/build/generated/contracts_bundle_info.js +5 -0
- package/build/generated/create_work_order_input.js +2 -0
- package/build/generated/create_work_order_output.js +2 -0
- package/build/generated/current_work_order_file.js +2 -0
- package/build/generated/doctor_input.js +2 -0
- package/build/generated/doctor_output.js +24 -0
- package/build/generated/execution_result.js +2 -0
- package/build/generated/execution_task.js +2 -0
- package/build/generated/export_output_input.js +2 -0
- package/build/generated/export_output_output.js +2 -0
- package/build/generated/finalize_work_input.js +2 -0
- package/build/generated/finalize_work_output.js +2 -0
- package/build/generated/gate_input.js +2 -0
- package/build/generated/gate_output.js +2 -0
- package/build/generated/gate_result_v1.js +2 -0
- package/build/generated/get_decision_input.js +2 -0
- package/build/generated/get_decision_output.js +13 -0
- package/build/generated/handoff_to_clinic.js +2 -0
- package/build/generated/index.js +75 -0
- package/build/generated/inspect_code_input.js +2 -0
- package/build/generated/inspect_code_output.js +13 -0
- package/build/generated/memory_retrieve_output.js +2 -0
- package/build/generated/memory_state_file.js +2 -0
- package/build/generated/memory_status_input.js +2 -0
- package/build/generated/memory_status_output.js +13 -0
- package/build/generated/memory_sync_input.js +2 -0
- package/build/generated/memory_sync_output.js +13 -0
- package/build/generated/plugin_result.js +2 -0
- package/build/generated/react_perf_check_patterns_input.js +2 -0
- package/build/generated/react_perf_check_patterns_output.js +2 -0
- package/build/generated/react_perf_generate_report_input.js +2 -0
- package/build/generated/react_perf_generate_report_output.js +2 -0
- package/build/generated/repair_plan_input.js +2 -0
- package/build/generated/repair_plan_output.js +2 -0
- package/build/generated/run_app_input.js +2 -0
- package/build/generated/run_app_output.js +2 -0
- package/build/generated/run_state_file.js +13 -0
- package/build/generated/scaffold_input.js +2 -0
- package/build/generated/scaffold_output.js +2 -0
- package/build/generated/search_oss_input.js +2 -0
- package/build/generated/search_oss_output.js +2 -0
- package/build/generated/selection_validation_result.js +2 -0
- package/build/generated/signal_agent_input.js +2 -0
- package/build/generated/spec_high_ask_queue_items_file.js +2 -0
- package/build/generated/spec_high_clinic_bridge_output.js +2 -0
- package/build/generated/spec_high_decision_draft_output.js +2 -0
- package/build/generated/spec_high_validate_output.js +2 -0
- package/build/generated/status_input.js +2 -0
- package/build/generated/status_output.js +2 -0
- package/build/generated/submit_decision_input.js +2 -0
- package/build/generated/submit_decision_output.js +2 -0
- package/build/generated/tool_error_output.js +2 -0
- package/build/generated/undo_last_task_input.js +2 -0
- package/build/generated/undo_last_task_output.js +2 -0
- package/build/generated/update_input.js +2 -0
- package/build/generated/update_output.js +2 -0
- package/build/generated/vibe_pm_inspection_result.js +2 -0
- package/build/generated/vibe_pm_report_markdown.js +2 -0
- package/build/generated/vibe_pm_verdict.js +2 -0
- package/build/generated/vibe_repo_config.js +2 -0
- package/build/generated/vibecoding_helper_answer_output.js +2 -0
- package/build/generated/vibecoding_helper_one_loop_selection_output.js +2 -0
- package/build/generated/vibecoding_helper_show_ask_queue_output.js +2 -0
- package/build/generated/work_order_v1.js +2 -0
- package/build/generated/zoekt_evidence_input.js +2 -0
- package/build/generated/zoekt_evidence_output.js +2 -0
- package/build/index.js +111 -0
- package/build/legacy_alias.js +65 -0
- package/build/local-mode/bash.js +61 -0
- package/build/local-mode/config.js +171 -0
- package/build/local-mode/git.js +33 -0
- package/build/local-mode/init.js +110 -0
- package/build/local-mode/paths.js +24 -0
- package/build/local-mode/templates.js +856 -0
- package/build/local-mode/work-order.js +41 -0
- package/build/resources/index.js +246 -0
- package/build/security/input-validator.js +119 -0
- package/build/security/path-policy.js +289 -0
- package/build/security/sandbox.js +228 -0
- package/build/tools/react_perf/check_patterns.js +172 -0
- package/build/tools/react_perf/generate_report.js +337 -0
- package/build/tools/react_perf/index.js +119 -0
- package/build/tools/react_perf/rules/advanced.js +325 -0
- package/build/tools/react_perf/rules/async.js +104 -0
- package/build/tools/react_perf/rules/bundle.js +101 -0
- package/build/tools/react_perf/rules/client.js +186 -0
- package/build/tools/react_perf/rules/index.js +74 -0
- package/build/tools/react_perf/rules/js.js +148 -0
- package/build/tools/react_perf/rules/rendering.js +166 -0
- package/build/tools/react_perf/rules/rerender.js +161 -0
- package/build/tools/react_perf/rules/server.js +141 -0
- package/build/tools/react_perf/types.js +127 -0
- package/build/tools/vibe_pm/activate.js +102 -0
- package/build/tools/vibe_pm/advisory_review.js +77 -0
- package/build/tools/vibe_pm/briefing.js +178 -0
- package/build/tools/vibe_pm/context.js +439 -0
- package/build/tools/vibe_pm/create_work_order.js +271 -0
- package/build/tools/vibe_pm/doc_status_gate.js +370 -0
- package/build/tools/vibe_pm/doctor.js +262 -0
- package/build/tools/vibe_pm/entity_gate/preflight.js +78 -0
- package/build/tools/vibe_pm/export_output.js +135 -0
- package/build/tools/vibe_pm/finalize_work.js +393 -0
- package/build/tools/vibe_pm/gate.js +33 -0
- package/build/tools/vibe_pm/get_decision.js +281 -0
- package/build/tools/vibe_pm/index.js +593 -0
- package/build/tools/vibe_pm/inspect_code.js +828 -0
- package/build/tools/vibe_pm/intent/generator.js +294 -0
- package/build/tools/vibe_pm/intent/index.js +5 -0
- package/build/tools/vibe_pm/intent/prompt_density.js +227 -0
- package/build/tools/vibe_pm/intent/types.js +70 -0
- package/build/tools/vibe_pm/intent/verifier.js +237 -0
- package/build/tools/vibe_pm/kce/doc_usage.js +51 -0
- package/build/tools/vibe_pm/kce/on_finalize.js +11 -0
- package/build/tools/vibe_pm/kce/preflight.js +232 -0
- package/build/tools/vibe_pm/local_memory.js +26 -0
- package/build/tools/vibe_pm/memory_status.js +82 -0
- package/build/tools/vibe_pm/memory_sync.js +134 -0
- package/build/tools/vibe_pm/modules/decision_snapshot.js +29 -0
- package/build/tools/vibe_pm/modules/ensure.js +100 -0
- package/build/tools/vibe_pm/modules/fingerprint.js +30 -0
- package/build/tools/vibe_pm/modules/fix_dependencies.js +394 -0
- package/build/tools/vibe_pm/modules/planning_v1.js +110 -0
- package/build/tools/vibe_pm/modules/repo_context.js +56 -0
- package/build/tools/vibe_pm/modules/research_v1.js +114 -0
- package/build/tools/vibe_pm/modules/skills_v1.js +100 -0
- package/build/tools/vibe_pm/pm_language.js +222 -0
- package/build/tools/vibe_pm/repair_plan.js +199 -0
- package/build/tools/vibe_pm/run_app.js +597 -0
- package/build/tools/vibe_pm/run_app_podman.js +64 -0
- package/build/tools/vibe_pm/scaffold.js +550 -0
- package/build/tools/vibe_pm/search_oss.js +124 -0
- package/build/tools/vibe_pm/status.js +153 -0
- package/build/tools/vibe_pm/submit_decision.js +87 -0
- package/build/tools/vibe_pm/system_design/issue_mapping.js +47 -0
- package/build/tools/vibe_pm/system_design/rulebook.js +112 -0
- package/build/tools/vibe_pm/system_design/semgrep.js +132 -0
- package/build/tools/vibe_pm/types.js +229 -0
- package/build/tools/vibe_pm/undo_last_task.js +163 -0
- package/build/tools/vibe_pm/update.js +146 -0
- package/build/tools/vibe_pm/zoekt_evidence.js +96 -0
- package/build/tools.js +269 -0
- package/build/version-check.js +239 -0
- package/build/vibe-cli.js +631 -0
- 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
|
+
[](https://www.npmjs.com/package/@vibecode/mcp-server)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](#testing)
|
|
6
|
+
[](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();
|