Skip to content

Commit 8d8878d

Browse files
committed
Add dev server
1 parent 927f99f commit 8d8878d

File tree

2 files changed

+174
-0
lines changed

2 files changed

+174
-0
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"description": "PostHog example projects",
66
"scripts": {
77
"build:docs": "node scripts/build-examples-mcp-resources.js",
8+
"dev": "node scripts/dev-server.js",
89
"test:plugins": "vitest run scripts/plugins/tests",
910
"test:plugins:watch": "vitest scripts/plugins/tests"
1011
},

scripts/dev-server.js

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Development server for MCP resources
5+
*
6+
* Serves the generated ZIP file over HTTP and watches markdown files
7+
* for changes, automatically rebuilding when needed.
8+
*
9+
* Usage: npm run dev
10+
*/
11+
12+
const http = require('http');
13+
const fs = require('fs');
14+
const path = require('path');
15+
const { spawn } = require('child_process');
16+
17+
const PORT = 8765;
18+
const ZIP_PATH = path.join(__dirname, '..', 'dist', 'examples-mcp-resources.zip');
19+
20+
// Directories to watch for changes
21+
const WATCH_DIRS = [
22+
path.join(__dirname, '..', 'llm-prompts'),
23+
path.join(__dirname, '..', 'mcp-commands'),
24+
path.join(__dirname, '..', 'basics'),
25+
];
26+
27+
let isRebuilding = false;
28+
let rebuildQueued = false;
29+
30+
/**
31+
* Run the build script
32+
*/
33+
function rebuild() {
34+
if (isRebuilding) {
35+
rebuildQueued = true;
36+
return;
37+
}
38+
39+
console.log('\n🔨 Rebuilding resources...');
40+
isRebuilding = true;
41+
42+
const buildProcess = spawn('node', [path.join(__dirname, 'build-examples-mcp-resources.js')], {
43+
stdio: 'inherit',
44+
cwd: path.join(__dirname, '..')
45+
});
46+
47+
buildProcess.on('close', (code) => {
48+
isRebuilding = false;
49+
50+
if (code === 0) {
51+
console.log('✅ Rebuild complete!\n');
52+
} else {
53+
console.error(`❌ Build failed with code ${code}\n`);
54+
}
55+
56+
// If another rebuild was queued, run it now
57+
if (rebuildQueued) {
58+
rebuildQueued = false;
59+
rebuild();
60+
}
61+
});
62+
}
63+
64+
/**
65+
* Watch directories for markdown file changes
66+
*/
67+
function setupWatchers() {
68+
console.log('\n👀 Watching for changes in:');
69+
70+
WATCH_DIRS.forEach(dir => {
71+
if (!fs.existsSync(dir)) {
72+
console.log(` ⚠️ ${path.relative(path.join(__dirname, '..'), dir)} (not found, skipping)`);
73+
return;
74+
}
75+
76+
console.log(` 📁 ${path.relative(path.join(__dirname, '..'), dir)}`);
77+
78+
// Watch recursively
79+
fs.watch(dir, { recursive: true }, (eventType, filename) => {
80+
if (!filename) return;
81+
82+
// Only trigger on markdown or JSON files
83+
if (filename.endsWith('.md') || filename.endsWith('.json')) {
84+
console.log(`\n📝 Changed: ${filename}`);
85+
rebuild();
86+
}
87+
});
88+
});
89+
}
90+
91+
/**
92+
* Create HTTP server to serve the ZIP file
93+
*/
94+
function createServer() {
95+
const server = http.createServer((req, res) => {
96+
// Only serve the ZIP file
97+
if (req.url === '/examples-mcp-resources.zip' || req.url === '/') {
98+
if (!fs.existsSync(ZIP_PATH)) {
99+
res.writeHead(404, { 'Content-Type': 'text/plain' });
100+
res.end('ZIP file not found. Run build first.');
101+
return;
102+
}
103+
104+
const stat = fs.statSync(ZIP_PATH);
105+
const fileSize = stat.size;
106+
const fileStream = fs.createReadStream(ZIP_PATH);
107+
108+
res.writeHead(200, {
109+
'Content-Type': 'application/zip',
110+
'Content-Length': fileSize,
111+
'Content-Disposition': 'attachment; filename="examples-mcp-resources.zip"',
112+
'Cache-Control': 'no-cache, no-store, must-revalidate',
113+
'Pragma': 'no-cache',
114+
'Expires': '0'
115+
});
116+
117+
fileStream.pipe(res);
118+
119+
console.log(`📦 Served ZIP file (${(fileSize / 1024).toFixed(1)} KB)`);
120+
} else {
121+
res.writeHead(404, { 'Content-Type': 'text/plain' });
122+
res.end('Not found. Use /examples-mcp-resources.zip');
123+
}
124+
});
125+
126+
server.listen(PORT, () => {
127+
console.log('\n🚀 Development server started!');
128+
console.log(`\n📍 ZIP available at: http://localhost:${PORT}/examples-mcp-resources.zip`);
129+
console.log('\n💡 To use with MCP server, set environment variable:');
130+
console.log(` POSTHOG_MCP_LOCAL_EXAMPLES_URL=http://localhost:${PORT}/examples-mcp-resources.zip`);
131+
});
132+
}
133+
134+
/**
135+
* Main entry point
136+
*/
137+
async function main() {
138+
console.log('🎯 PostHog MCP Resources Development Server');
139+
console.log('==========================================');
140+
141+
// Initial build if ZIP doesn't exist
142+
if (!fs.existsSync(ZIP_PATH)) {
143+
console.log('\n⚠️ ZIP file not found. Running initial build...');
144+
await new Promise((resolve) => {
145+
const buildProcess = spawn('node', [path.join(__dirname, 'build-examples-mcp-resources.js')], {
146+
stdio: 'inherit',
147+
cwd: path.join(__dirname, '..')
148+
});
149+
buildProcess.on('close', resolve);
150+
});
151+
}
152+
153+
// Start server
154+
createServer();
155+
156+
// Setup file watchers
157+
setupWatchers();
158+
159+
console.log('\n✨ Ready for development!');
160+
console.log(' Press Ctrl+C to stop\n');
161+
}
162+
163+
// Handle graceful shutdown
164+
process.on('SIGINT', () => {
165+
console.log('\n\n👋 Shutting down dev server...');
166+
process.exit(0);
167+
});
168+
169+
// Run
170+
main().catch(err => {
171+
console.error('Fatal error:', err);
172+
process.exit(1);
173+
});

0 commit comments

Comments
 (0)