@programinglive/dev-workflow-mcp-server 1.7.13 โ 1.7.16
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/CHANGELOG.md +70 -0
- package/README.md +69 -1
- package/cloudflared-config.yml +3 -0
- package/db/adapters/postgres.js +85 -28
- package/db/adapters/sqlite.js +7 -1
- package/docker-compose.yml +17 -0
- package/docs/product/PRD.md +4 -2
- package/docs/release-notes/RELEASE_NOTES.md +61 -0
- package/index.js +12 -2
- package/netlify.toml +7 -0
- package/nginx-api.conf +27 -0
- package/package.json +1 -1
- package/scripts/test-auth-flow.js +75 -0
- package/tests/build.test.js +1 -1
- package/tests/startup-resilience.test.js +73 -0
- package/web/Dockerfile.api +25 -0
- package/web/__tests__/api/workflow-history.test.js +14 -12
- package/web/api-server.js +211 -0
- package/web/app/docs/page.tsx +1 -1
- package/web/app/history/page.tsx +25 -18
- package/web/app/login/page.tsx +12 -14
- package/web/components/Hero.tsx +1 -1
- package/web/components/History.tsx +4 -4
- package/web/components/Navbar.tsx +11 -12
- package/web/lib/api-client.ts +123 -0
- package/web/netlify.toml +5 -0
- package/web/next.config.ts +2 -2
- package/web/package-lock.json +6407 -2241
- package/web/package.json +11 -2
- package/workflow-state.js +3 -2
- package/scripts/add-user.js +0 -52
- package/scripts/health-check.js +0 -50
- package/scripts/test-history.js +0 -56
- package/web/app/api/history-fallback/route.ts +0 -56
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,76 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [1.7.16](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.15...v1.7.16) (2025-12-24)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### ๐ Bug Fixes
|
|
9
|
+
|
|
10
|
+
* align database schema with dashboard and improve user ID handling ([69e7f38](https://github.com/programinglive/dev-workflow-mcp-server/commit/69e7f381955d15bb727a6e309082a344729d1c1e))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### ๐งน Chores
|
|
14
|
+
|
|
15
|
+
* cleanup temporary debugging scripts ([a5e2b15](https://github.com/programinglive/dev-workflow-mcp-server/commit/a5e2b15b894632efadce46d83caf4d008e50fdd3))
|
|
16
|
+
* sync version to 1.7.15 across web and landing pages ([c520e4a](https://github.com/programinglive/dev-workflow-mcp-server/commit/c520e4a5d795c880fb74f31142b128a9c55beb6f))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### ๐ Documentation
|
|
20
|
+
|
|
21
|
+
* update macOS MCP config instructions ([1e368da](https://github.com/programinglive/dev-workflow-mcp-server/commit/1e368da37bede45e0282b1312002f2fedeb6ad60))
|
|
22
|
+
|
|
23
|
+
### [1.7.15](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.14...v1.7.15) (2025-12-19)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### ๐ Bug Fixes
|
|
27
|
+
|
|
28
|
+
* optimize startup performance and add database connection resilience ([bff2714](https://github.com/programinglive/dev-workflow-mcp-server/commit/bff27146fe9537788741e4a084191bce93588569))
|
|
29
|
+
* refactor Navbar and History to use absolute API client URLs v1.7.18 ([dc33eae](https://github.com/programinglive/dev-workflow-mcp-server/commit/dc33eae612067c5246ca69d08960090c0a0ad6bd))
|
|
30
|
+
* sync all version labels to 1.7.14 and improve netlify routing ([78712eb](https://github.com/programinglive/dev-workflow-mcp-server/commit/78712eb398458605f77b2ba78c9ff61a0be1d335))
|
|
31
|
+
* sync all version strings to 1.7.14 ([e2a1795](https://github.com/programinglive/dev-workflow-mcp-server/commit/e2a1795a7b794c8874b1e610de3370cd6ed5fa7a))
|
|
32
|
+
* sync version to 1.7.14 and fix static routing via trailingSlash ([1c152bd](https://github.com/programinglive/dev-workflow-mcp-server/commit/1c152bd23bbfd30a73e0a89107f4ea18564d20d0))
|
|
33
|
+
|
|
34
|
+
### [1.7.14](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.13...v1.7.14) (2025-12-19)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### โจ Features
|
|
38
|
+
|
|
39
|
+
* add API dependencies and update login to use API client ([94bb42f](https://github.com/programinglive/dev-workflow-mcp-server/commit/94bb42f27b4bd225bb3813b81d5f2bff973edb16))
|
|
40
|
+
* add REST API server for frontend database access ([cdb347c](https://github.com/programinglive/dev-workflow-mcp-server/commit/cdb347cf204ce423b94db7e72980783e986a124d))
|
|
41
|
+
* implement JWT authentication and update client ([8efbe80](https://github.com/programinglive/dev-workflow-mcp-server/commit/8efbe80c3a69046f716b247fc359158dc8fec3e2))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
### โป๏ธ Refactors
|
|
45
|
+
|
|
46
|
+
* remove session cookies, enforce JWT only ([bacd83a](https://github.com/programinglive/dev-workflow-mcp-server/commit/bacd83a16510499667143f4906d0cc8de0127d61))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
### ๐ Bug Fixes
|
|
50
|
+
|
|
51
|
+
* add credentials:include to getCurrentUser to persist session ([b8f2df5](https://github.com/programinglive/dev-workflow-mcp-server/commit/b8f2df531a2d2bae78ad3c3ea2c7dcda62e5e835))
|
|
52
|
+
* add root netlify.toml for monorepo support ([54c5ed3](https://github.com/programinglive/dev-workflow-mcp-server/commit/54c5ed3960aafd5a814773efa487efe9197d4385))
|
|
53
|
+
* enable trust proxy and force secure cookies for cross-domain session ([2c5d6e8](https://github.com/programinglive/dev-workflow-mcp-server/commit/2c5d6e858d05de06304d88286b931b1f12a95bf1))
|
|
54
|
+
* ensure getMe uses full API URL ([b3bd0da](https://github.com/programinglive/dev-workflow-mcp-server/commit/b3bd0da097de57b42d9253615f791677011486af))
|
|
55
|
+
* force API URL to tunnel address to resolve cached old IP ([1883731](https://github.com/programinglive/dev-workflow-mcp-server/commit/18837313a7b96395f1cf04b56d7fe1a74e13b1ed))
|
|
56
|
+
* inline auth functions in API server ([eefd73c](https://github.com/programinglive/dev-workflow-mcp-server/commit/eefd73c82dcf47d419662eaeb8854f6047e6464a))
|
|
57
|
+
* remove duplicate variable declarations in API server ([31d3621](https://github.com/programinglive/dev-workflow-mcp-server/commit/31d3621213ddb3560ee0570fdf6411d9acf7018f))
|
|
58
|
+
* remove dynamic API route for static export ([05a4dd0](https://github.com/programinglive/dev-workflow-mcp-server/commit/05a4dd0e4901e5ac329e50ebe392839d1242de4e))
|
|
59
|
+
* remove explicit publish path to fix netlify build ([cd64bcf](https://github.com/programinglive/dev-workflow-mcp-server/commit/cd64bcf0b4effb71be090235357d665d1f26b567))
|
|
60
|
+
* remove incorrect netlify publish dir for dynamic build ([ea34cf4](https://github.com/programinglive/dev-workflow-mcp-server/commit/ea34cf464466b4ddc8eaa6432434d1fbabb3583e))
|
|
61
|
+
* remove incorrect publish dir from netlify config ([010236f](https://github.com/programinglive/dev-workflow-mcp-server/commit/010236fd0eb1419f7cae4429159f4aac1a988174))
|
|
62
|
+
* restore static export for netlify deployment ([685d841](https://github.com/programinglive/dev-workflow-mcp-server/commit/685d84178b239b5cd681b4d479456e69519a8af8))
|
|
63
|
+
* update API URL to use Cloudflare Tunnel for HTTPS ([ab72f6c](https://github.com/programinglive/dev-workflow-mcp-server/commit/ab72f6c0b8acc6974aba7c8df8418e35da6f2a51))
|
|
64
|
+
* use apiClient in history page and add version indicator ([4889669](https://github.com/programinglive/dev-workflow-mcp-server/commit/48896694166cd5d42ab4134d4dbfdb0df8ca0d44))
|
|
65
|
+
* use npm install instead of ci to avoid lockfile issues ([e5fae26](https://github.com/programinglive/dev-workflow-mcp-server/commit/e5fae26f767c5f0e4aba2593df321c700ac14a38))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
### ๐งน Chores
|
|
69
|
+
|
|
70
|
+
* bump version to 1.7.14 to ensure fresh build ([f7d8886](https://github.com/programinglive/dev-workflow-mcp-server/commit/f7d88861ceb111c57e3b34bf4b5a5cc25a19805b))
|
|
71
|
+
* bump version to 1.7.15 for JWT frontend deploy ([7850268](https://github.com/programinglive/dev-workflow-mcp-server/commit/7850268d019251619d60a0a841db2bab8061be04))
|
|
72
|
+
* cleanup post-release and update website version to 1.7.13 ([7341d44](https://github.com/programinglive/dev-workflow-mcp-server/commit/7341d446bbddcb9232e70606acaf950fde7ff5c2))
|
|
73
|
+
* save pending changes before release ([4d4b1c9](https://github.com/programinglive/dev-workflow-mcp-server/commit/4d4b1c9e6122baed3cc6782a572fe3700dabf19b))
|
|
74
|
+
|
|
5
75
|
### [1.7.13](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.12...v1.7.13) (2025-12-18)
|
|
6
76
|
|
|
7
77
|
|
package/README.md
CHANGED
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
An MCP (Model Context Protocol) server that helps enforce development discipline and workflow best practices. This server acts as your coding conscience, reminding you to follow proper development workflows.
|
|
10
10
|
|
|
11
|
+
### ๐ก๏ธ Resilience Features
|
|
12
|
+
- **Startup Resilience**: 5-second connection timeouts for external databases.
|
|
13
|
+
- **Background Loading**: Non-critical assets (compatibility mirrors) initialize in the background to ensure sub-second startup.
|
|
14
|
+
- **Diagnostic Mode**: Automatic startup timing logs displayed in stderr.
|
|
15
|
+
|
|
11
16
|
## ๐ฏ Purpose
|
|
12
17
|
|
|
13
18
|
This MCP server guides you through a disciplined development workflow:
|
|
@@ -100,7 +105,12 @@ Point your MCP client to the server entry point. Replace `<PROJECT_ROOT>` with t
|
|
|
100
105
|
"mcpServers": {
|
|
101
106
|
"dev-workflow": {
|
|
102
107
|
"command": "node",
|
|
103
|
-
"
|
|
108
|
+
"cwd": "<PROJECT_ROOT>",
|
|
109
|
+
"args": ["index.js"],
|
|
110
|
+
"env": {
|
|
111
|
+
"DEV_WORKFLOW_DB_TYPE": "postgres",
|
|
112
|
+
"DEV_WORKFLOW_DB_URL": "postgres://USER:PASS@HOST:5432/devworkflow"
|
|
113
|
+
}
|
|
104
114
|
}
|
|
105
115
|
}
|
|
106
116
|
}
|
|
@@ -177,6 +187,28 @@ Point your MCP client to the server entry point. Replace `<PROJECT_ROOT>` with t
|
|
|
177
187
|
|
|
178
188
|
> **Note:** On Windows paths in JSON require escaped backslashes (e.g., `"C:\\path\\to\\project"`).
|
|
179
189
|
|
|
190
|
+
#### Full Windsurf MCP Config Example (macOS)
|
|
191
|
+
|
|
192
|
+
If you keep this repository checked out at `/Users/alex/code/dev-workflow-mcp-server` and want to point Windsurf at a hosted PostgreSQL instance, drop the following into `~/Library/Application Support/Windsurf/mcp_config.json`:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"mcpServers": {
|
|
197
|
+
"dev-workflow": {
|
|
198
|
+
"command": "node",
|
|
199
|
+
"cwd": "/Users/alex/code/dev-workflow-mcp-server",
|
|
200
|
+
"args": ["index.js"],
|
|
201
|
+
"env": {
|
|
202
|
+
"DEV_WORKFLOW_DB_TYPE": "postgres",
|
|
203
|
+
"DEV_WORKFLOW_DB_URL": "postgres://devworkflow:devworkflow_secure_password@34.50.121.142:5432/devworkflow"
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
This mirrors the Windows configuration shared in previous releases, but avoids `npx` lookup issues on macOS by launching the local `index.js` directly.
|
|
211
|
+
|
|
180
212
|
### 4. Restart Windsurf/Claude Desktop
|
|
181
213
|
|
|
182
214
|
After adding the configuration, restart the application to load the MCP server.
|
|
@@ -201,6 +233,29 @@ Antigravity users should configure the MCP server in their `mcp_config.json`.
|
|
|
201
233
|
|
|
202
234
|
See [Antigravity Getting Started](./docs/antigravity/GETTING-STARTED.md) for detailed instructions and troubleshooting.
|
|
203
235
|
|
|
236
|
+
### โก Performance Optimization (Recommended)
|
|
237
|
+
|
|
238
|
+
To ensure the fastest possible startup (critical for IDE-integrated clients like Claude Desktop), we recommend pointing directly to `index.js` using `node` rather than `npx`. This avoids the overhead of checking for package updates.
|
|
239
|
+
|
|
240
|
+
**Optimized Configuration:**
|
|
241
|
+
|
|
242
|
+
```json
|
|
243
|
+
{
|
|
244
|
+
"mcpServers": {
|
|
245
|
+
"dev-workflow": {
|
|
246
|
+
"command": "node",
|
|
247
|
+
"args": ["<PROJECT_ROOT>\\index.js"],
|
|
248
|
+
"env": {
|
|
249
|
+
"DEV_WORKFLOW_USER_ID": "your_user_id"
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The server automatically times its own startup and logs it to stderr:
|
|
257
|
+
`Dev Workflow MCP Server running on stdio (startup took 15ms)`
|
|
258
|
+
|
|
204
259
|
## โ๏ธ Configuration
|
|
205
260
|
|
|
206
261
|
### Database Setup
|
|
@@ -241,6 +296,19 @@ export DEV_WORKFLOW_DB_TYPE=postgres
|
|
|
241
296
|
export DEV_WORKFLOW_DB_URL="postgresql://user:password@localhost:5432/dev_workflow"
|
|
242
297
|
```
|
|
243
298
|
|
|
299
|
+
#### ๐ ๏ธ Database Schema Normalization (Postgres/MySQL)
|
|
300
|
+
To ensure compatibility with existing reporting dashboards, the `PostgresAdapter` and `MysqlAdapter` automatically normalize column names:
|
|
301
|
+
- `task_description` โ DB column: `description`
|
|
302
|
+
- `timestamp` โ DB column: `completed_at`
|
|
303
|
+
|
|
304
|
+
The adapters use aliases in queries so the MCP tools still receive the expected `task_description` and `timestamp` fields.
|
|
305
|
+
|
|
306
|
+
#### ๐ค User ID Handling (Postgres/MySQL)
|
|
307
|
+
These databases use an `INTEGER` column for `user_id`.
|
|
308
|
+
- **Numeric strings** (e.g., `"1"`) are parsed directly into integers.
|
|
309
|
+
- **Non-numeric strings** (e.g., `"programinglive"`) are automatically hashed into a consistent, positive integer to ensure compatibility with the schema while maintaining unique user isolation.
|
|
310
|
+
|
|
311
|
+
|
|
244
312
|
### User & State Management
|
|
245
313
|
|
|
246
314
|
| Variable | Description |
|
package/db/adapters/postgres.js
CHANGED
|
@@ -15,11 +15,25 @@ export class PostgresAdapter extends DbAdapter {
|
|
|
15
15
|
this.pool = null;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
getNumericUserId(userId) {
|
|
19
|
+
let numericUserId = parseInt(userId, 10);
|
|
20
|
+
if (isNaN(numericUserId)) {
|
|
21
|
+
let hash = 0;
|
|
22
|
+
for (let i = 0; i < userId.length; i++) {
|
|
23
|
+
hash = (hash << 5) - hash + userId.charCodeAt(i);
|
|
24
|
+
hash |= 0;
|
|
25
|
+
}
|
|
26
|
+
numericUserId = Math.abs(hash);
|
|
27
|
+
}
|
|
28
|
+
return numericUserId;
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
async connect() {
|
|
19
32
|
if (!this.pool) {
|
|
20
33
|
this.pool = new Pool({
|
|
21
34
|
connectionString: this.config.connectionUrl,
|
|
22
|
-
//
|
|
35
|
+
connectionTimeoutMillis: 5000, // 5 second timeout for new connections
|
|
36
|
+
idleTimeoutMillis: 10000, // close idle connections after 10 seconds
|
|
23
37
|
});
|
|
24
38
|
await this.migrate();
|
|
25
39
|
}
|
|
@@ -31,23 +45,55 @@ export class PostgresAdapter extends DbAdapter {
|
|
|
31
45
|
await client.query(`
|
|
32
46
|
CREATE TABLE IF NOT EXISTS workflow_history (
|
|
33
47
|
id SERIAL PRIMARY KEY,
|
|
34
|
-
user_id
|
|
48
|
+
user_id INTEGER NOT NULL,
|
|
35
49
|
project_path TEXT DEFAULT '',
|
|
36
|
-
|
|
50
|
+
description TEXT NOT NULL,
|
|
37
51
|
task_type TEXT NOT NULL,
|
|
38
52
|
commit_message TEXT,
|
|
39
|
-
|
|
53
|
+
completed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
40
54
|
forced BOOLEAN DEFAULT FALSE,
|
|
41
55
|
force_reason TEXT,
|
|
42
56
|
dropped BOOLEAN DEFAULT FALSE,
|
|
43
57
|
drop_reason TEXT,
|
|
44
|
-
UNIQUE (user_id, project_path,
|
|
58
|
+
UNIQUE (user_id, project_path, completed_at, description)
|
|
45
59
|
)
|
|
46
60
|
`);
|
|
47
61
|
|
|
62
|
+
// Migration: Add columns to existing table if they don't exist
|
|
63
|
+
const columns = await client.query(`
|
|
64
|
+
SELECT column_name FROM information_schema.columns WHERE table_name = 'workflow_history'
|
|
65
|
+
`);
|
|
66
|
+
const existingColumns = columns.rows.map(r => r.column_name);
|
|
67
|
+
|
|
68
|
+
if (!existingColumns.includes('project_path')) {
|
|
69
|
+
await client.query('ALTER TABLE workflow_history ADD COLUMN project_path TEXT DEFAULT \'\'');
|
|
70
|
+
}
|
|
71
|
+
if (!existingColumns.includes('forced')) {
|
|
72
|
+
await client.query('ALTER TABLE workflow_history ADD COLUMN forced BOOLEAN DEFAULT FALSE');
|
|
73
|
+
}
|
|
74
|
+
if (!existingColumns.includes('force_reason')) {
|
|
75
|
+
await client.query('ALTER TABLE workflow_history ADD COLUMN force_reason TEXT');
|
|
76
|
+
}
|
|
77
|
+
if (!existingColumns.includes('dropped')) {
|
|
78
|
+
await client.query('ALTER TABLE workflow_history ADD COLUMN dropped BOOLEAN DEFAULT FALSE');
|
|
79
|
+
}
|
|
80
|
+
if (!existingColumns.includes('drop_reason')) {
|
|
81
|
+
await client.query('ALTER TABLE workflow_history ADD COLUMN drop_reason TEXT');
|
|
82
|
+
}
|
|
83
|
+
// Ensure completed_at vs timestamp handled if table exists
|
|
84
|
+
if (!existingColumns.includes('completed_at') && existingColumns.includes('timestamp')) {
|
|
85
|
+
await client.query('ALTER TABLE workflow_history RENAME COLUMN timestamp TO completed_at');
|
|
86
|
+
} else if (existingColumns.includes('completed_at') && !existingColumns.includes('timestamp')) {
|
|
87
|
+
// Keep completed_at
|
|
88
|
+
}
|
|
89
|
+
// Logic for description vs task_description
|
|
90
|
+
if (!existingColumns.includes('description') && existingColumns.includes('task_description')) {
|
|
91
|
+
await client.query('ALTER TABLE workflow_history RENAME COLUMN task_description TO description');
|
|
92
|
+
}
|
|
93
|
+
|
|
48
94
|
await client.query(`
|
|
49
95
|
CREATE TABLE IF NOT EXISTS project_summary (
|
|
50
|
-
user_id
|
|
96
|
+
user_id INTEGER,
|
|
51
97
|
project_path TEXT DEFAULT '',
|
|
52
98
|
total_tasks INTEGER DEFAULT 0,
|
|
53
99
|
task_types JSONB,
|
|
@@ -60,7 +106,7 @@ export class PostgresAdapter extends DbAdapter {
|
|
|
60
106
|
|
|
61
107
|
await client.query(`
|
|
62
108
|
CREATE TABLE IF NOT EXISTS workflow_state (
|
|
63
|
-
user_id
|
|
109
|
+
user_id INTEGER,
|
|
64
110
|
project_path TEXT DEFAULT '',
|
|
65
111
|
state_json JSONB,
|
|
66
112
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
@@ -75,12 +121,13 @@ export class PostgresAdapter extends DbAdapter {
|
|
|
75
121
|
async insertHistoryEntry(userId, projectPath, entry) {
|
|
76
122
|
await this.connect();
|
|
77
123
|
const normalizedProjectPath = projectPath || '';
|
|
124
|
+
const numericUserId = this.getNumericUserId(userId);
|
|
78
125
|
|
|
79
126
|
const query = `
|
|
80
127
|
INSERT INTO workflow_history
|
|
81
|
-
(user_id, project_path,
|
|
128
|
+
(user_id, project_path, description, task_type, commit_message, completed_at, forced, force_reason, dropped, drop_reason)
|
|
82
129
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
83
|
-
ON CONFLICT (user_id, project_path,
|
|
130
|
+
ON CONFLICT (user_id, project_path, completed_at, description) DO UPDATE SET
|
|
84
131
|
commit_message=EXCLUDED.commit_message,
|
|
85
132
|
forced=EXCLUDED.forced,
|
|
86
133
|
force_reason=EXCLUDED.force_reason,
|
|
@@ -89,12 +136,12 @@ export class PostgresAdapter extends DbAdapter {
|
|
|
89
136
|
`;
|
|
90
137
|
|
|
91
138
|
await this.pool.query(query, [
|
|
92
|
-
|
|
139
|
+
numericUserId,
|
|
93
140
|
normalizedProjectPath,
|
|
94
|
-
entry.taskDescription,
|
|
141
|
+
entry.taskDescription || entry.description,
|
|
95
142
|
entry.taskType,
|
|
96
143
|
entry.commitMessage,
|
|
97
|
-
entry.timestamp || new Date().toISOString(),
|
|
144
|
+
entry.timestamp || entry.completed_at || new Date().toISOString(),
|
|
98
145
|
entry.forced || false,
|
|
99
146
|
entry.forceReason || null,
|
|
100
147
|
entry.dropped || false,
|
|
@@ -107,14 +154,16 @@ export class PostgresAdapter extends DbAdapter {
|
|
|
107
154
|
async updateSummaryForUser(userId, projectPath) {
|
|
108
155
|
await this.connect();
|
|
109
156
|
const normalizedProjectPath = projectPath || '';
|
|
157
|
+
const numericUserId = this.getNumericUserId(userId);
|
|
110
158
|
|
|
111
159
|
const res = await this.pool.query(`
|
|
112
|
-
SELECT task_type, timestamp, task_description
|
|
160
|
+
SELECT task_type, completed_at as timestamp, description as task_description
|
|
113
161
|
FROM workflow_history
|
|
114
162
|
WHERE user_id = $1 AND project_path = $2
|
|
115
|
-
ORDER BY
|
|
163
|
+
ORDER BY completed_at DESC
|
|
116
164
|
LIMIT 20
|
|
117
|
-
`, [
|
|
165
|
+
`, [numericUserId, normalizedProjectPath]);
|
|
166
|
+
// ... (lines continue similarly)
|
|
118
167
|
|
|
119
168
|
const rows = res.rows;
|
|
120
169
|
|
|
@@ -144,7 +193,7 @@ export class PostgresAdapter extends DbAdapter {
|
|
|
144
193
|
`;
|
|
145
194
|
|
|
146
195
|
await this.pool.query(upsert, [
|
|
147
|
-
|
|
196
|
+
numericUserId,
|
|
148
197
|
normalizedProjectPath,
|
|
149
198
|
rows.length,
|
|
150
199
|
JSON.stringify(taskTypes),
|
|
@@ -157,10 +206,11 @@ export class PostgresAdapter extends DbAdapter {
|
|
|
157
206
|
async getSummaryForUser(userId, projectPath) {
|
|
158
207
|
await this.connect();
|
|
159
208
|
const normalizedProjectPath = projectPath || '';
|
|
209
|
+
const numericUserId = this.getNumericUserId(userId);
|
|
160
210
|
|
|
161
211
|
const res = await this.pool.query(`
|
|
162
212
|
SELECT * FROM project_summary WHERE user_id = $1 AND project_path = $2
|
|
163
|
-
`, [
|
|
213
|
+
`, [numericUserId, normalizedProjectPath]);
|
|
164
214
|
|
|
165
215
|
const row = res.rows[0];
|
|
166
216
|
if (!row) return null;
|
|
@@ -183,16 +233,18 @@ export class PostgresAdapter extends DbAdapter {
|
|
|
183
233
|
const sanitizedPageSize = Math.min(100, Math.max(1, Number.isFinite(Number(pageSize)) ? Math.floor(Number(pageSize)) : 20));
|
|
184
234
|
const offset = (sanitizedPage - 1) * sanitizedPageSize;
|
|
185
235
|
|
|
236
|
+
const numericUserId = this.getNumericUserId(userId);
|
|
237
|
+
|
|
186
238
|
const clauses = [];
|
|
187
|
-
const params = [
|
|
239
|
+
const params = [numericUserId, normalizedProjectPath];
|
|
188
240
|
let paramIdx = 3;
|
|
189
241
|
|
|
190
242
|
if (startDate) {
|
|
191
|
-
clauses.push(`DATE(
|
|
243
|
+
clauses.push(`DATE(completed_at) >= DATE($${paramIdx++})`);
|
|
192
244
|
params.push(startDate);
|
|
193
245
|
}
|
|
194
246
|
if (endDate) {
|
|
195
|
-
clauses.push(`DATE(
|
|
247
|
+
clauses.push(`DATE(completed_at) <= DATE($${paramIdx++})`);
|
|
196
248
|
params.push(endDate);
|
|
197
249
|
}
|
|
198
250
|
|
|
@@ -205,10 +257,10 @@ export class PostgresAdapter extends DbAdapter {
|
|
|
205
257
|
const total = parseInt(countRes.rows[0].total, 10);
|
|
206
258
|
|
|
207
259
|
const entriesRes = await this.pool.query(`
|
|
208
|
-
SELECT
|
|
260
|
+
SELECT *, description as task_description, completed_at as timestamp
|
|
209
261
|
FROM workflow_history
|
|
210
262
|
WHERE ${where}
|
|
211
|
-
ORDER BY
|
|
263
|
+
ORDER BY completed_at DESC
|
|
212
264
|
LIMIT $${paramIdx++}
|
|
213
265
|
OFFSET $${paramIdx++}
|
|
214
266
|
`, [...params, sanitizedPageSize, offset]);
|
|
@@ -228,23 +280,25 @@ export class PostgresAdapter extends DbAdapter {
|
|
|
228
280
|
const { startDate, endDate, frequency = "daily" } = options;
|
|
229
281
|
const format = FREQUENCY_FORMATS[frequency] || FREQUENCY_FORMATS.daily;
|
|
230
282
|
|
|
283
|
+
const numericUserId = this.getNumericUserId(userId);
|
|
284
|
+
|
|
231
285
|
const clauses = [];
|
|
232
|
-
const params = [
|
|
286
|
+
const params = [numericUserId, normalizedProjectPath];
|
|
233
287
|
let paramIdx = 3;
|
|
234
288
|
|
|
235
289
|
if (startDate) {
|
|
236
|
-
clauses.push(`DATE(
|
|
290
|
+
clauses.push(`DATE(completed_at) >= DATE($${paramIdx++})`);
|
|
237
291
|
params.push(startDate);
|
|
238
292
|
}
|
|
239
293
|
if (endDate) {
|
|
240
|
-
clauses.push(`DATE(
|
|
294
|
+
clauses.push(`DATE(completed_at) <= DATE($${paramIdx++})`);
|
|
241
295
|
params.push(endDate);
|
|
242
296
|
}
|
|
243
297
|
|
|
244
298
|
const where = [`user_id = $1`, `project_path = $2`, ...clauses].join(" AND ");
|
|
245
299
|
|
|
246
300
|
const res = await this.pool.query(`
|
|
247
|
-
SELECT to_char(
|
|
301
|
+
SELECT to_char(completed_at, '${format}') AS period, COUNT(*) AS count
|
|
248
302
|
FROM workflow_history
|
|
249
303
|
WHERE ${where}
|
|
250
304
|
GROUP BY period
|
|
@@ -267,8 +321,10 @@ export class PostgresAdapter extends DbAdapter {
|
|
|
267
321
|
updated_at=EXCLUDED.updated_at
|
|
268
322
|
`;
|
|
269
323
|
|
|
324
|
+
const numericUserId = this.getNumericUserId(userId);
|
|
325
|
+
|
|
270
326
|
await this.pool.query(query, [
|
|
271
|
-
|
|
327
|
+
numericUserId,
|
|
272
328
|
normalizedProjectPath,
|
|
273
329
|
JSON.stringify(state),
|
|
274
330
|
new Date().toISOString()
|
|
@@ -278,10 +334,11 @@ export class PostgresAdapter extends DbAdapter {
|
|
|
278
334
|
async getState(userId, projectPath) {
|
|
279
335
|
await this.connect();
|
|
280
336
|
const normalizedProjectPath = projectPath || '';
|
|
337
|
+
const numericUserId = this.getNumericUserId(userId);
|
|
281
338
|
|
|
282
339
|
const res = await this.pool.query(`
|
|
283
340
|
SELECT state_json FROM workflow_state WHERE user_id = $1 AND project_path = $2
|
|
284
|
-
`, [
|
|
341
|
+
`, [numericUserId, normalizedProjectPath]);
|
|
285
342
|
|
|
286
343
|
const row = res.rows[0];
|
|
287
344
|
if (!row) return null;
|
package/db/adapters/sqlite.js
CHANGED
|
@@ -25,9 +25,15 @@ export class SqliteAdapter extends DbAdapter {
|
|
|
25
25
|
// Otherwise, attempt to find project root relative to CWD
|
|
26
26
|
const initCwd = process.env.INIT_CWD || process.cwd();
|
|
27
27
|
let projectRoot = initCwd;
|
|
28
|
-
|
|
28
|
+
let limit = 20; // Prevent infinite loop in weird environments
|
|
29
|
+
while (projectRoot !== path.dirname(projectRoot) && limit > 0) {
|
|
29
30
|
if (existsSync(path.join(projectRoot, "package.json"))) break;
|
|
30
31
|
projectRoot = path.dirname(projectRoot);
|
|
32
|
+
limit--;
|
|
33
|
+
}
|
|
34
|
+
if (limit === 0) {
|
|
35
|
+
console.error("Warning: Could not find project root effectively, defaulting to INIT_CWD");
|
|
36
|
+
projectRoot = initCwd;
|
|
31
37
|
}
|
|
32
38
|
return path.join(projectRoot, ".state", DB_FILENAME);
|
|
33
39
|
}
|
package/docker-compose.yml
CHANGED
|
@@ -19,6 +19,23 @@ services:
|
|
|
19
19
|
timeout: 5s
|
|
20
20
|
retries: 5
|
|
21
21
|
|
|
22
|
+
api:
|
|
23
|
+
build:
|
|
24
|
+
context: ./web
|
|
25
|
+
dockerfile: Dockerfile.api
|
|
26
|
+
container_name: dev-workflow-api
|
|
27
|
+
ports:
|
|
28
|
+
- "8080:8080"
|
|
29
|
+
environment:
|
|
30
|
+
- DEV_WORKFLOW_DB_URL=${DEV_WORKFLOW_DB_URL}
|
|
31
|
+
- SESSION_SECRET=${SESSION_SECRET:-dev-workflow-secret-change-in-production}
|
|
32
|
+
- NODE_ENV=${NODE_ENV:-production}
|
|
33
|
+
- API_PORT=8080
|
|
34
|
+
depends_on:
|
|
35
|
+
postgres:
|
|
36
|
+
condition: service_healthy
|
|
37
|
+
restart: unless-stopped
|
|
38
|
+
|
|
22
39
|
volumes:
|
|
23
40
|
postgres_data:
|
|
24
41
|
driver: local
|
package/docs/product/PRD.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
- **Product**: Development Workflow MCP Server
|
|
5
5
|
- **Prepared by**: Cascade (AI assistant)
|
|
6
6
|
- **Date**: 2025-12-16
|
|
7
|
-
- **Status**: Live v1.7.
|
|
7
|
+
- **Status**: Live v1.7.15
|
|
8
8
|
|
|
9
9
|
## 2. Problem Statement
|
|
10
10
|
Developers need structured guidance to maintain disciplined workflows across projects. Existing tooling lacks enforcement of sequential best practices (testing, documentation, release hygiene) and offers limited visibility into historical task execution.
|
|
@@ -67,7 +67,9 @@ Developers need structured guidance to maintain disciplined workflows across pro
|
|
|
67
67
|
3. **v1.5.0**: Netlify deployment support for static web dashboard (complete).
|
|
68
68
|
4. **v1.6.0+**: Dark/Light mode, UI Improvements (complete).
|
|
69
69
|
5. **Multi-Database Support**: Refactor storage layer to support MySQL and PostgreSQL (complete v1.7.1).
|
|
70
|
-
|
|
70
|
+
7. **v1.7.14 Performance & Resilience**: Implemented connection timeouts and optimized startup sequence for stdio clients (complete).
|
|
71
|
+
8. **v1.7.15 Schema Alignment**: Implemented Postgres/MySQL column normalization and automated integer User ID hashing for cross-dashboard compatibility (complete).
|
|
72
|
+
9. **Future**: Expanded dashboard analytics, customizable workflows, richer integrations.
|
|
71
73
|
|
|
72
74
|
## 11. Risks & Mitigations
|
|
73
75
|
- **Risk**: Workflow feels overly prescriptive. **Mitigation**: Provide escape hatches (`force_complete_task`, `drop_task`) with audit trails.
|
|
@@ -4,6 +4,10 @@ Authoritative history of user-visible changes for the Development Workflow MCP S
|
|
|
4
4
|
|
|
5
5
|
| Version | Date | Notes |
|
|
6
6
|
|---------|------|-------|
|
|
7
|
+
| 1.7.16 | 2025-12-24 | align database schema with dashboard and improve user ID handling (69e7f38) |
|
|
8
|
+
| 1.7.15 | 2025-12-19 | optimize startup performance and add database connection resilience (bff2714) |
|
|
9
|
+
| 1.7.14 | 2025-12-19 | add API dependencies and update login to use API client (94bb42f) |
|
|
10
|
+
| 1.7.13 | 2025-12-18 | remote db connection, history api 500 errors, and windows startup (ce559a6) |
|
|
7
11
|
| 1.7.12 | 2025-12-17 | post-release cleanup v1.7.11 (c2ee733) |
|
|
8
12
|
| 1.7.11 | 2025-12-17 | test: comprehensive DB adapter tests, env support, and warning fixes (64b029a) |
|
|
9
13
|
| 1.7.10 | 2025-12-16 | update release notes and finalize ci config (92ac78a) |
|
|
@@ -37,6 +41,63 @@ Authoritative history of user-visible changes for the Development Workflow MCP S
|
|
|
37
41
|
|
|
38
42
|
|
|
39
43
|
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## 1.7.16 โ ๐ Bug Fixes
|
|
49
|
+
|
|
50
|
+
Released on **2025-12-24**.
|
|
51
|
+
|
|
52
|
+
- align database schema with dashboard and improve user ID handling (69e7f38)
|
|
53
|
+
- cleanup temporary debugging scripts (a5e2b15)
|
|
54
|
+
- sync version to 1.7.15 across web and landing pages (c520e4a)
|
|
55
|
+
- update macOS MCP config instructions (1e368da)
|
|
56
|
+
|
|
57
|
+
## 1.7.15 โ ๐ Bug Fixes
|
|
58
|
+
|
|
59
|
+
Released on **2025-12-19**.
|
|
60
|
+
|
|
61
|
+
- optimize startup performance and add database connection resilience (bff2714)
|
|
62
|
+
- refactor Navbar and History to use absolute API client URLs v1.7.18 (dc33eae)
|
|
63
|
+
- sync all version labels to 1.7.14 and improve netlify routing (78712eb)
|
|
64
|
+
- sync all version strings to 1.7.14 (e2a1795)
|
|
65
|
+
- sync version to 1.7.14 and fix static routing via trailingSlash (1c152bd)
|
|
66
|
+
|
|
67
|
+
## 1.7.14 โ โจ Features
|
|
68
|
+
|
|
69
|
+
Released on **2025-12-19**.
|
|
70
|
+
|
|
71
|
+
- add API dependencies and update login to use API client (94bb42f)
|
|
72
|
+
- add REST API server for frontend database access (cdb347c)
|
|
73
|
+
- implement JWT authentication and update client (8efbe80)
|
|
74
|
+
- remove session cookies, enforce JWT only (bacd83a)
|
|
75
|
+
- add credentials:include to getCurrentUser to persist session (b8f2df5)
|
|
76
|
+
- add root netlify.toml for monorepo support (54c5ed3)
|
|
77
|
+
- enable trust proxy and force secure cookies for cross-domain session (2c5d6e8)
|
|
78
|
+
- ensure getMe uses full API URL (b3bd0da)
|
|
79
|
+
- force API URL to tunnel address to resolve cached old IP (1883731)
|
|
80
|
+
- inline auth functions in API server (eefd73c)
|
|
81
|
+
- remove duplicate variable declarations in API server (31d3621)
|
|
82
|
+
- remove dynamic API route for static export (05a4dd0)
|
|
83
|
+
- remove explicit publish path to fix netlify build (cd64bcf)
|
|
84
|
+
- remove incorrect netlify publish dir for dynamic build (ea34cf4)
|
|
85
|
+
- remove incorrect publish dir from netlify config (010236f)
|
|
86
|
+
- restore static export for netlify deployment (685d841)
|
|
87
|
+
- update API URL to use Cloudflare Tunnel for HTTPS (ab72f6c)
|
|
88
|
+
- use apiClient in history page and add version indicator (4889669)
|
|
89
|
+
- use npm install instead of ci to avoid lockfile issues (e5fae26)
|
|
90
|
+
- bump version to 1.7.14 to ensure fresh build (f7d8886)
|
|
91
|
+
- bump version to 1.7.15 for JWT frontend deploy (7850268)
|
|
92
|
+
- cleanup post-release and update website version to 1.7.13 (7341d44)
|
|
93
|
+
- save pending changes before release (4d4b1c9)
|
|
94
|
+
|
|
95
|
+
## 1.7.13 โ ๐ Bug Fixes
|
|
96
|
+
|
|
97
|
+
Released on **2025-12-18**.
|
|
98
|
+
|
|
99
|
+
- remote db connection, history api 500 errors, and windows startup (ce559a6)
|
|
100
|
+
|
|
40
101
|
## 1.7.12 โ ๐ Documentation
|
|
41
102
|
|
|
42
103
|
Released on **2025-12-17**.
|
package/index.js
CHANGED
|
@@ -191,20 +191,30 @@ Use 'check_ready_to_commit' to verify workflow completion.
|
|
|
191
191
|
|
|
192
192
|
// Start server
|
|
193
193
|
export async function main() {
|
|
194
|
+
const startTime = Date.now();
|
|
195
|
+
console.error(`[${new Date().toISOString()}] MCP Server starting...`);
|
|
196
|
+
|
|
194
197
|
try {
|
|
195
198
|
// Initialize workflow state
|
|
196
199
|
workflowState = new WorkflowState();
|
|
200
|
+
console.error(`[${new Date().toISOString()}] Loading workflow state...`);
|
|
197
201
|
await workflowState.load();
|
|
202
|
+
console.error(`[${new Date().toISOString()}] Workflow state loaded.`);
|
|
203
|
+
|
|
204
|
+
console.error(`[${new Date().toISOString()}] Ensuring primary file...`);
|
|
198
205
|
await workflowState.ensurePrimaryFile();
|
|
206
|
+
console.error(`[${new Date().toISOString()}] Workflow state initialization complete.`);
|
|
199
207
|
} catch (error) {
|
|
200
|
-
console.error(
|
|
208
|
+
console.error(`[${new Date().toISOString()}] Warning: Failed to initialize workflow state:`, error.message);
|
|
201
209
|
// Continue anyway - workflow state is optional for MCP to function
|
|
202
210
|
workflowState = new WorkflowState();
|
|
203
211
|
}
|
|
204
212
|
|
|
205
213
|
const transport = new StdioServerTransport();
|
|
214
|
+
console.error(`[${new Date().toISOString()}] Connecting transport...`);
|
|
206
215
|
await server.connect(transport);
|
|
207
|
-
|
|
216
|
+
const duration = Date.now() - startTime;
|
|
217
|
+
console.error(`[${new Date().toISOString()}] Dev Workflow MCP Server running on stdio (startup took ${duration}ms)`);
|
|
208
218
|
}
|
|
209
219
|
|
|
210
220
|
// --- Lightweight CLI: dev-workflow-mcp call <toolName> [--args '<json>'] ---
|
package/netlify.toml
ADDED
package/nginx-api.conf
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
server {
|
|
2
|
+
listen 80;
|
|
3
|
+
server_name api.devworkflow.programinglive.com;
|
|
4
|
+
|
|
5
|
+
location / {
|
|
6
|
+
proxy_pass http://localhost:8080;
|
|
7
|
+
proxy_http_version 1.1;
|
|
8
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
9
|
+
proxy_set_header Connection 'upgrade';
|
|
10
|
+
proxy_set_header Host $host;
|
|
11
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
12
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
13
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
14
|
+
proxy_cache_bypass $http_upgrade;
|
|
15
|
+
|
|
16
|
+
# CORS headers (backup in case API doesn't set them)
|
|
17
|
+
add_header 'Access-Control-Allow-Origin' 'https://devworkflow.programinglive.com' always;
|
|
18
|
+
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
|
19
|
+
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
|
|
20
|
+
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
|
|
21
|
+
|
|
22
|
+
# Handle preflight requests
|
|
23
|
+
if ($request_method = 'OPTIONS') {
|
|
24
|
+
return 204;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@programinglive/dev-workflow-mcp-server",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.16",
|
|
4
4
|
"description": "MCP server to enforce development workflow discipline",
|
|
5
5
|
"homepage": "https://devworkflow.programinglive.com/",
|
|
6
6
|
"mcpName": "io.github.programinglive/dev-workflow-mcp-server",
|