vibe-annotations-server 0.1.5 → 0.1.6
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 +137 -18
- package/bin/cli.js +227 -0
- package/lib/server.js +1091 -0
- package/package.json +37 -19
- package/index.js +0 -186
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Raphael Regnier
|
|
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
CHANGED
|
@@ -1,35 +1,154 @@
|
|
|
1
1
|
# vibe-annotations-server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Global MCP server for Vibe Annotations browser extension.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
npm install -g vibe-annotations-server
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
1. Install the latest version from GitHub if not already installed
|
|
13
|
-
2. Start the vibe-annotations server
|
|
11
|
+
## Usage
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
### Start the server
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
```bash
|
|
16
|
+
vibe-annotations-server start
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The server will run in the background on port 3846.
|
|
20
|
+
|
|
21
|
+
### Stop the server
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
vibe-annotations-server stop
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Check server status
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
vibe-annotations-server status
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Restart the server
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
vibe-annotations-server restart
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### View logs
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
vibe-annotations-server logs
|
|
43
|
+
# or follow logs
|
|
44
|
+
vibe-annotations-server logs -f
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## AI Coding Agent Integration
|
|
48
|
+
|
|
49
|
+
After starting the server, connect it to your AI coding agent. The server supports multiple agents via the MCP (Model Context Protocol) using SSE (Server-Sent Events) transport.
|
|
50
|
+
|
|
51
|
+
### Claude Code
|
|
52
|
+
|
|
53
|
+
In your project directory, run:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
claude mcp add --transport sse vibe-annotations http://127.0.0.1:3846/sse
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Cursor
|
|
60
|
+
|
|
61
|
+
1. Open Cursor → Settings → Cursor Settings
|
|
62
|
+
2. Go to the Tools & Integrations tab
|
|
63
|
+
3. Click + Add new global MCP server
|
|
64
|
+
4. Enter the following configuration and save:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"mcpServers": {
|
|
69
|
+
"vibe-annotations": {
|
|
70
|
+
"url": "http://127.0.0.1:3846/sse"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Windsurf
|
|
77
|
+
|
|
78
|
+
1. Navigate to Windsurf → Settings → Advanced Settings
|
|
79
|
+
2. Scroll down to the Cascade section
|
|
80
|
+
3. Click "Add new server" or edit the raw JSON config file
|
|
81
|
+
4. Add the following configuration:
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"mcpServers": {
|
|
86
|
+
"vibe-annotations": {
|
|
87
|
+
"serverUrl": "http://127.0.0.1:3846/sse"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### VS Code
|
|
94
|
+
|
|
95
|
+
1. Install an AI extension that supports MCP (like GitHub Copilot Chat or Continue)
|
|
96
|
+
2. Go to Code → Settings → Settings or use the shortcut ⌘,
|
|
97
|
+
3. In the search bar, type "MCP"
|
|
98
|
+
4. Look for MCP server configurations in your AI extension settings
|
|
99
|
+
5. Add the following SSE configuration:
|
|
18
100
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"mcpServers": {
|
|
104
|
+
"vibe-annotations": {
|
|
105
|
+
"type": "sse",
|
|
106
|
+
"url": "http://127.0.0.1:3846/sse"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Note:** MCP support varies by AI extension. Check your extension's documentation for specific setup instructions.
|
|
113
|
+
|
|
114
|
+
### Other Editors
|
|
115
|
+
|
|
116
|
+
Other code editors and tools that support SSE (Server-Sent Events) can also connect to the Vibe Annotations MCP server. If you're using a different editor or tool, check its documentation to confirm it supports SSE-based communication. If it does, you can manually add the server using this configuration:
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"mcpServers": {
|
|
121
|
+
"vibe-annotations": {
|
|
122
|
+
"url": "http://127.0.0.1:3846/sse"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
23
127
|
|
|
24
|
-
|
|
128
|
+
**Note:** The Vibe Annotations MCP server communicates over the SSE protocol. Use your editor's steps for setting up an SSE-compatible MCP server, and use the URL: `http://127.0.0.1:3846/sse`
|
|
25
129
|
|
|
26
|
-
|
|
27
|
-
https://github.com/RaphaelRegnier/vibe-annotations
|
|
130
|
+
## Architecture
|
|
28
131
|
|
|
29
|
-
|
|
132
|
+
The server provides:
|
|
133
|
+
- **SSE Endpoint** (`/sse`): For AI coding agent MCP connections
|
|
134
|
+
- **HTTP API** (`/api/annotations`): For Chrome extension communication
|
|
135
|
+
- **Health Check** (`/health`): For status monitoring
|
|
30
136
|
|
|
31
|
-
|
|
32
|
-
|
|
137
|
+
Data is stored in `~/.vibe-annotations/annotations.json`.
|
|
138
|
+
|
|
139
|
+
## Development
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Clone the repository
|
|
143
|
+
git clone <repo-url>
|
|
144
|
+
cd vibe-annotations-server
|
|
145
|
+
|
|
146
|
+
# Install dependencies
|
|
147
|
+
npm install
|
|
148
|
+
|
|
149
|
+
# Run in development mode
|
|
150
|
+
npm run dev
|
|
151
|
+
```
|
|
33
152
|
|
|
34
153
|
## License
|
|
35
154
|
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { spawn, exec } from 'child_process';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync, openSync } from 'fs';
|
|
7
|
+
import { join, dirname } from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import { homedir } from 'os';
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
|
|
16
|
+
// Read version from package.json automatically
|
|
17
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8'));
|
|
18
|
+
|
|
19
|
+
const execAsync = promisify(exec);
|
|
20
|
+
const program = new Command();
|
|
21
|
+
|
|
22
|
+
// Configuration paths
|
|
23
|
+
const CONFIG_DIR = join(homedir(), '.vibe-annotations');
|
|
24
|
+
const PID_FILE = join(CONFIG_DIR, 'server.pid');
|
|
25
|
+
const LOG_FILE = join(CONFIG_DIR, 'server.log');
|
|
26
|
+
const PORT = 3846;
|
|
27
|
+
|
|
28
|
+
// Ensure config directory exists
|
|
29
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
30
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Helper functions
|
|
34
|
+
function isServerRunning() {
|
|
35
|
+
if (!existsSync(PID_FILE)) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const pid = readFileSync(PID_FILE, 'utf8').trim();
|
|
41
|
+
process.kill(parseInt(pid), 0); // Check if process exists
|
|
42
|
+
return true;
|
|
43
|
+
} catch (e) {
|
|
44
|
+
// Process doesn't exist
|
|
45
|
+
if (existsSync(PID_FILE)) {
|
|
46
|
+
// Clean up stale PID file
|
|
47
|
+
unlinkSync(PID_FILE);
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function checkPort() {
|
|
54
|
+
try {
|
|
55
|
+
const response = await fetch(`http://127.0.0.1:${PORT}/health`);
|
|
56
|
+
return response.ok;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Commands
|
|
63
|
+
program
|
|
64
|
+
.name('vibe-annotations-server')
|
|
65
|
+
.description('Global MCP server for Vibe Annotations browser extension')
|
|
66
|
+
.version(packageJson.version);
|
|
67
|
+
|
|
68
|
+
program
|
|
69
|
+
.command('start')
|
|
70
|
+
.description('Start the Vibe Annotations server')
|
|
71
|
+
.option('-d, --daemon', 'Run as daemon (background process)')
|
|
72
|
+
.action(async (options) => {
|
|
73
|
+
if (isServerRunning()) {
|
|
74
|
+
console.log(chalk.yellow('✓ Server is already running'));
|
|
75
|
+
console.log(chalk.gray(` Port: ${PORT}`));
|
|
76
|
+
console.log(chalk.gray(` PID: ${readFileSync(PID_FILE, 'utf8').trim()}`));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log(chalk.blue('Starting Vibe Annotations server...'));
|
|
81
|
+
|
|
82
|
+
const serverPath = join(dirname(__dirname), 'lib', 'server.js');
|
|
83
|
+
|
|
84
|
+
if (options.daemon) {
|
|
85
|
+
// Run as daemon
|
|
86
|
+
const out = fs.openSync(LOG_FILE, 'a');
|
|
87
|
+
const err = fs.openSync(LOG_FILE, 'a');
|
|
88
|
+
|
|
89
|
+
const child = spawn('node', [serverPath], {
|
|
90
|
+
detached: true,
|
|
91
|
+
stdio: ['ignore', out, err],
|
|
92
|
+
env: { ...process.env, NODE_ENV: 'production' }
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
child.unref();
|
|
96
|
+
writeFileSync(PID_FILE, child.pid.toString());
|
|
97
|
+
|
|
98
|
+
// Wait for server to start
|
|
99
|
+
let attempts = 0;
|
|
100
|
+
while (attempts < 10) {
|
|
101
|
+
if (await checkPort()) {
|
|
102
|
+
console.log(chalk.green('✅ Vibe Annotations server running on http://127.0.0.1:3846/sse'));
|
|
103
|
+
console.log(chalk.gray(` PID: ${child.pid}`));
|
|
104
|
+
console.log(chalk.gray(` Logs: ${LOG_FILE}`));
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
108
|
+
attempts++;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (attempts >= 10) {
|
|
112
|
+
console.log(chalk.red('❌ Failed to start server. Check logs at:'));
|
|
113
|
+
console.log(chalk.gray(` ${LOG_FILE}`));
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
// Run in foreground
|
|
117
|
+
const child = spawn('node', [serverPath], {
|
|
118
|
+
stdio: 'inherit',
|
|
119
|
+
env: { ...process.env, NODE_ENV: 'production' }
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
writeFileSync(PID_FILE, child.pid.toString());
|
|
123
|
+
|
|
124
|
+
child.on('exit', () => {
|
|
125
|
+
if (existsSync(PID_FILE)) {
|
|
126
|
+
unlinkSync(PID_FILE);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
program
|
|
133
|
+
.command('stop')
|
|
134
|
+
.description('Stop the Vibe Annotations server')
|
|
135
|
+
.action(() => {
|
|
136
|
+
if (!isServerRunning()) {
|
|
137
|
+
console.log(chalk.yellow('Server is not running'));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const pid = readFileSync(PID_FILE, 'utf8').trim();
|
|
143
|
+
process.kill(parseInt(pid), 'SIGTERM');
|
|
144
|
+
|
|
145
|
+
if (existsSync(PID_FILE)) {
|
|
146
|
+
unlinkSync(PID_FILE);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
console.log(chalk.green('✅ Server stopped'));
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.log(chalk.red('❌ Failed to stop server:'), error.message);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
program
|
|
156
|
+
.command('restart')
|
|
157
|
+
.description('Restart the Vibe Annotations server')
|
|
158
|
+
.action(async () => {
|
|
159
|
+
console.log(chalk.blue('Restarting server...'));
|
|
160
|
+
|
|
161
|
+
// Stop if running
|
|
162
|
+
if (isServerRunning()) {
|
|
163
|
+
try {
|
|
164
|
+
const pid = readFileSync(PID_FILE, 'utf8').trim();
|
|
165
|
+
process.kill(parseInt(pid), 'SIGTERM');
|
|
166
|
+
|
|
167
|
+
if (existsSync(PID_FILE)) {
|
|
168
|
+
unlinkSync(PID_FILE);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Wait for process to stop
|
|
172
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.log(chalk.yellow('Warning:'), error.message);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Start with daemon flag
|
|
179
|
+
program.parse(['node', 'cli.js', 'start', '--daemon'], { from: 'user' });
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
program
|
|
183
|
+
.command('status')
|
|
184
|
+
.description('Check server status')
|
|
185
|
+
.action(async () => {
|
|
186
|
+
const running = isServerRunning();
|
|
187
|
+
const portAvailable = await checkPort();
|
|
188
|
+
|
|
189
|
+
if (running && portAvailable) {
|
|
190
|
+
console.log(chalk.green('✅ Server is running'));
|
|
191
|
+
console.log(chalk.gray(` PID: ${readFileSync(PID_FILE, 'utf8').trim()}`));
|
|
192
|
+
console.log(chalk.gray(` Port: ${PORT}`));
|
|
193
|
+
console.log(chalk.gray(` URL: http://127.0.0.1:${PORT}/sse`));
|
|
194
|
+
} else if (running && !portAvailable) {
|
|
195
|
+
console.log(chalk.yellow('⚠️ Server process exists but is not responding'));
|
|
196
|
+
console.log(chalk.gray(' Try running: vibe-annotations-server restart'));
|
|
197
|
+
} else {
|
|
198
|
+
console.log(chalk.gray('○ Server is not running'));
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
program
|
|
203
|
+
.command('logs')
|
|
204
|
+
.description('View server logs')
|
|
205
|
+
.option('-f, --follow', 'Follow log output')
|
|
206
|
+
.option('-n, --lines <number>', 'Number of lines to show', '50')
|
|
207
|
+
.action((options) => {
|
|
208
|
+
if (!existsSync(LOG_FILE)) {
|
|
209
|
+
console.log(chalk.gray('No logs available yet'));
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (options.follow) {
|
|
214
|
+
// Use tail -f
|
|
215
|
+
const tail = spawn('tail', ['-f', LOG_FILE], { stdio: 'inherit' });
|
|
216
|
+
|
|
217
|
+
process.on('SIGINT', () => {
|
|
218
|
+
tail.kill();
|
|
219
|
+
process.exit();
|
|
220
|
+
});
|
|
221
|
+
} else {
|
|
222
|
+
// Show last N lines
|
|
223
|
+
const tail = spawn('tail', ['-n', options.lines, LOG_FILE], { stdio: 'inherit' });
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
program.parse(process.argv);
|