ai-consultation-mcp 1.0.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 +193 -0
- package/dist/api/index.d.ts +27 -0
- package/dist/api/index.js +213 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/middleware/security.d.ts +6 -0
- package/dist/api/middleware/security.js +28 -0
- package/dist/api/middleware/security.js.map +1 -0
- package/dist/api/routes/chat.d.ts +2 -0
- package/dist/api/routes/chat.js +78 -0
- package/dist/api/routes/chat.js.map +1 -0
- package/dist/api/routes/config.d.ts +2 -0
- package/dist/api/routes/config.js +81 -0
- package/dist/api/routes/config.js.map +1 -0
- package/dist/api/routes/providers.d.ts +2 -0
- package/dist/api/routes/providers.js +225 -0
- package/dist/api/routes/providers.js.map +1 -0
- package/dist/api/standalone-server.d.ts +12 -0
- package/dist/api/standalone-server.js +117 -0
- package/dist/api/standalone-server.js.map +1 -0
- package/dist/config/defaults.d.ts +17 -0
- package/dist/config/defaults.js +30 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/encryption.d.ts +12 -0
- package/dist/config/encryption.js +85 -0
- package/dist/config/encryption.js.map +1 -0
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.js +6 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/manager.d.ts +62 -0
- package/dist/config/manager.js +210 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/prompts.d.ts +10 -0
- package/dist/config/prompts.js +168 -0
- package/dist/config/prompts.js.map +1 -0
- package/dist/config/schema.d.ts +141 -0
- package/dist/config/schema.js +54 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +260 -0
- package/dist/index.js.map +1 -0
- package/dist/installer/detector.d.ts +48 -0
- package/dist/installer/detector.js +164 -0
- package/dist/installer/detector.js.map +1 -0
- package/dist/installer/index.d.ts +7 -0
- package/dist/installer/index.js +10 -0
- package/dist/installer/index.js.map +1 -0
- package/dist/installer/installer.d.ts +16 -0
- package/dist/installer/installer.js +262 -0
- package/dist/installer/installer.js.map +1 -0
- package/dist/installer/tools.d.ts +16 -0
- package/dist/installer/tools.js +327 -0
- package/dist/installer/tools.js.map +1 -0
- package/dist/installer/types.d.ts +68 -0
- package/dist/installer/types.js +5 -0
- package/dist/installer/types.js.map +1 -0
- package/dist/providers/base.d.ts +44 -0
- package/dist/providers/base.js +84 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/deepseek.d.ts +30 -0
- package/dist/providers/deepseek.js +148 -0
- package/dist/providers/deepseek.js.map +1 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.js +8 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai.d.ts +30 -0
- package/dist/providers/openai.js +123 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/registry.d.ts +37 -0
- package/dist/providers/registry.js +65 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/types.d.ts +71 -0
- package/dist/providers/types.js +2 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/server/conversation.d.ts +105 -0
- package/dist/server/conversation.js +279 -0
- package/dist/server/conversation.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +3 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/tools/consult.d.ts +15 -0
- package/dist/server/tools/consult.js +164 -0
- package/dist/server/tools/consult.js.map +1 -0
- package/dist/server/tools/index.d.ts +1 -0
- package/dist/server/tools/index.js +2 -0
- package/dist/server/tools/index.js.map +1 -0
- package/dist/types/config.d.ts +20 -0
- package/dist/types/config.js +2 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/messages.d.ts +48 -0
- package/dist/types/messages.js +2 -0
- package/dist/types/messages.js.map +1 -0
- package/dist/types/models.d.ts +39 -0
- package/dist/types/models.js +66 -0
- package/dist/types/models.js.map +1 -0
- package/dist/ui/index.html +1244 -0
- package/dist/utils/errors.d.ts +32 -0
- package/dist/utils/errors.js +53 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +13 -0
- package/dist/utils/logger.js +73 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Menes Ekinci
|
|
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,193 @@
|
|
|
1
|
+
# Agent Consultation MCP
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/ai-consultation-mcp)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
An MCP (Model Context Protocol) server that enables AI agents to get a **second opinion** from other AI models, enriching their perspectives during work.
|
|
7
|
+
|
|
8
|
+
> **Purpose**: When AI agents work on complex tasks, having a single perspective can lead to blind spots or suboptimal solutions. This MCP server allows agents like Claude Code to consult with DeepSeek Reasoner or ChatGPT for alternative viewpoints, validation, or different problem-solving strategies.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **Multi-Tool Auto-Install**: Automatically detects and configures MCP for Claude Code, Cursor, Windsurf, Cline, Continue, Zed, and more
|
|
13
|
+
- **Multi-Provider Support**: DeepSeek (Reasoner, Chat) and OpenAI (GPT-5.2, GPT-5.2 Pro)
|
|
14
|
+
- **Specialized Consultation Modes**: Debug, Code Analysis, Architecture Review, Plan Validation, Concept Explanation
|
|
15
|
+
- **Conversation Management**: Continue multi-turn conversations with context
|
|
16
|
+
- **Web UI**: Configure providers and API keys through a browser interface
|
|
17
|
+
- **Encrypted Storage**: API keys are encrypted at rest
|
|
18
|
+
|
|
19
|
+
## Quick Start (2 steps)
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# 1. Auto-install to all detected AI tools
|
|
23
|
+
npx ai-consultation-mcp --install
|
|
24
|
+
|
|
25
|
+
# 2. Configure your API key in the Web UI that opens automatically
|
|
26
|
+
# Done! Restart your AI tools and start using
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
### Option 1: npm (Recommended)
|
|
32
|
+
|
|
33
|
+
The easiest way to install and use the MCP server:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Auto-install to all detected AI tools
|
|
37
|
+
npx ai-consultation-mcp --install
|
|
38
|
+
|
|
39
|
+
# Or install globally
|
|
40
|
+
npm install -g ai-consultation-mcp
|
|
41
|
+
ai-consultation-mcp --install
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Option 2: From GitHub
|
|
45
|
+
|
|
46
|
+
Clone the repository and build from source:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Clone the repository
|
|
50
|
+
git clone https://github.com/menesekinci/ai-consultation-mcp.git
|
|
51
|
+
cd ai-consultation-mcp
|
|
52
|
+
|
|
53
|
+
# Install dependencies
|
|
54
|
+
npm install
|
|
55
|
+
|
|
56
|
+
# Build
|
|
57
|
+
npm run build
|
|
58
|
+
|
|
59
|
+
# Run the installer
|
|
60
|
+
npm start -- --install
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Auto-Install Features
|
|
64
|
+
|
|
65
|
+
The `--install` flag will:
|
|
66
|
+
- Scan for installed AI tools (Claude Code, Cursor, Windsurf, OpenCode, VSCode Copilot, Cline, Continue, Zed, Roo Code)
|
|
67
|
+
- Add MCP configuration to each detected tool
|
|
68
|
+
- Open the Web UI to configure your API key
|
|
69
|
+
- Show which tools need to be restarted
|
|
70
|
+
|
|
71
|
+
### Supported AI Tools
|
|
72
|
+
|
|
73
|
+
| Tool | Config Location |
|
|
74
|
+
|------|-----------------|
|
|
75
|
+
| Claude Code | `~/.claude/mcp.json` |
|
|
76
|
+
| Cursor | `~/.cursor/mcp.json` |
|
|
77
|
+
| Windsurf | `~/.codeium/windsurf/mcp_config.json` |
|
|
78
|
+
| OpenCode | `~/.config/opencode/opencode.json` |
|
|
79
|
+
| VSCode Copilot | `~/Library/Application Support/Code/User/mcp.json` |
|
|
80
|
+
| Cline | VSCode globalStorage |
|
|
81
|
+
| Continue | `~/.continue/config.json` |
|
|
82
|
+
| Zed | `~/.config/zed/settings.json` |
|
|
83
|
+
| Roo Code | VSCode globalStorage |
|
|
84
|
+
|
|
85
|
+
### Manual Installation (Claude Code)
|
|
86
|
+
|
|
87
|
+
Add to `~/.claude/mcp.json`:
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"mcpServers": {
|
|
92
|
+
"agent-consultation": {
|
|
93
|
+
"command": "npx",
|
|
94
|
+
"args": ["-y", "ai-consultation-mcp"]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Configuration
|
|
101
|
+
|
|
102
|
+
### Setting Up API Keys
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npx ai-consultation-mcp --config
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
This opens a web UI where you can:
|
|
109
|
+
- Add/update API keys for DeepSeek and OpenAI
|
|
110
|
+
- Test API key validity
|
|
111
|
+
- Change default model
|
|
112
|
+
- View conversation history
|
|
113
|
+
|
|
114
|
+
### Supported Providers
|
|
115
|
+
|
|
116
|
+
| Provider | Models | API Key |
|
|
117
|
+
|----------|--------|---------|
|
|
118
|
+
| DeepSeek | `deepseek-reasoner` (default), `deepseek-chat` | [Get API Key](https://platform.deepseek.com/) |
|
|
119
|
+
| OpenAI | `gpt-5.2`, `gpt-5.2-pro` | [Get API Key](https://platform.openai.com/) |
|
|
120
|
+
|
|
121
|
+
## Usage
|
|
122
|
+
|
|
123
|
+
Once configured, Claude Code can use the following tools:
|
|
124
|
+
|
|
125
|
+
### consult_agent
|
|
126
|
+
|
|
127
|
+
Get a second opinion from another AI model.
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
Parameters:
|
|
131
|
+
- question (required): The question or problem to get advice on
|
|
132
|
+
- mode (optional): Consultation mode - debug, analyzeCode, reviewArchitecture, validatePlan, explainConcept, general
|
|
133
|
+
- context (optional): Additional context like code snippets or error messages
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### continue_conversation
|
|
137
|
+
|
|
138
|
+
Continue an existing consultation conversation.
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
Parameters:
|
|
142
|
+
- conversationId (required): The conversation ID from a previous consultation
|
|
143
|
+
- message (required): Your follow-up message
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### end_conversation
|
|
147
|
+
|
|
148
|
+
End an active consultation conversation.
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
Parameters:
|
|
152
|
+
- conversationId (required): The conversation ID to end
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Example
|
|
156
|
+
|
|
157
|
+
In Claude Code, you might use it like:
|
|
158
|
+
|
|
159
|
+
> "Can you consult DeepSeek about this architecture decision? I'm not sure if using a monorepo is the right choice for this microservices setup."
|
|
160
|
+
|
|
161
|
+
Claude will then call the `consult_agent` tool with your question and provide the response.
|
|
162
|
+
|
|
163
|
+
## Consultation Modes
|
|
164
|
+
|
|
165
|
+
| Mode | Description |
|
|
166
|
+
|------|-------------|
|
|
167
|
+
| `debug` | Focus on finding bugs, analyzing errors, and suggesting fixes |
|
|
168
|
+
| `analyzeCode` | Code review focusing on quality, patterns, and improvements |
|
|
169
|
+
| `reviewArchitecture` | Evaluate architectural decisions and suggest alternatives |
|
|
170
|
+
| `validatePlan` | Review implementation plans for completeness and risks |
|
|
171
|
+
| `explainConcept` | Explain technical concepts clearly |
|
|
172
|
+
| `general` | General-purpose consultation |
|
|
173
|
+
|
|
174
|
+
## Development
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# Clone the repo
|
|
178
|
+
git clone https://github.com/menesekinci/ai-consultation-mcp.git
|
|
179
|
+
cd ai-consultation-mcp
|
|
180
|
+
|
|
181
|
+
# Install dependencies
|
|
182
|
+
npm install
|
|
183
|
+
|
|
184
|
+
# Run in development mode
|
|
185
|
+
npm run dev
|
|
186
|
+
|
|
187
|
+
# Build for production
|
|
188
|
+
npm run build
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
MIT
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface ConfigUIOptions {
|
|
2
|
+
port?: number;
|
|
3
|
+
openBrowser?: boolean;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Start the configuration UI HTTP server
|
|
7
|
+
*/
|
|
8
|
+
export declare function startConfigUI(options?: ConfigUIOptions): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Start the UI server in background mode (for MCP mode)
|
|
11
|
+
* Does not block and does not open browser automatically
|
|
12
|
+
*/
|
|
13
|
+
export declare function startBackgroundUIServer(port?: number): Promise<boolean>;
|
|
14
|
+
/**
|
|
15
|
+
* Open the Web UI in browser (only opens once per session)
|
|
16
|
+
* Spawns a completely separate standalone server process that handles
|
|
17
|
+
* both server startup and browser opening.
|
|
18
|
+
*/
|
|
19
|
+
export declare function openWebUI(): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Check if browser has been opened
|
|
22
|
+
*/
|
|
23
|
+
export declare function isBrowserOpened(): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Check if background server is running
|
|
26
|
+
*/
|
|
27
|
+
export declare function isBackgroundServerRunning(): boolean;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { spawn } from 'child_process';
|
|
5
|
+
import open from 'open';
|
|
6
|
+
import { getConfigManager } from '../config/index.js';
|
|
7
|
+
import { logger } from '../utils/index.js';
|
|
8
|
+
import { configRoutes } from './routes/config.js';
|
|
9
|
+
import { providerRoutes } from './routes/providers.js';
|
|
10
|
+
import chatRoutes from './routes/chat.js';
|
|
11
|
+
import { securityMiddleware } from './middleware/security.js';
|
|
12
|
+
// Get __dirname equivalent in ES modules
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = path.dirname(__filename);
|
|
15
|
+
const DEFAULT_PORT = 3456;
|
|
16
|
+
// Track background UI server state
|
|
17
|
+
let backgroundServerRunning = false;
|
|
18
|
+
let browserOpened = false;
|
|
19
|
+
let backgroundServerPort = DEFAULT_PORT;
|
|
20
|
+
/**
|
|
21
|
+
* Start the configuration UI HTTP server
|
|
22
|
+
*/
|
|
23
|
+
export async function startConfigUI(options = {}) {
|
|
24
|
+
const port = options.port ?? DEFAULT_PORT;
|
|
25
|
+
const shouldOpenBrowser = options.openBrowser ?? true;
|
|
26
|
+
// Ensure config is loaded
|
|
27
|
+
const configManager = getConfigManager();
|
|
28
|
+
await configManager.init();
|
|
29
|
+
const app = express();
|
|
30
|
+
// Middleware
|
|
31
|
+
app.use(express.json());
|
|
32
|
+
app.use(securityMiddleware);
|
|
33
|
+
// API routes
|
|
34
|
+
app.use('/api/config', configRoutes);
|
|
35
|
+
app.use('/api/providers', providerRoutes);
|
|
36
|
+
app.use('/api/chat', chatRoutes);
|
|
37
|
+
// Serve static UI files
|
|
38
|
+
const uiPath = path.join(__dirname, '../ui');
|
|
39
|
+
app.use(express.static(uiPath));
|
|
40
|
+
// Fallback to index.html for SPA-like behavior (Express 5 compatible)
|
|
41
|
+
app.use((_req, res, next) => {
|
|
42
|
+
// Only serve index.html for non-API requests that don't have a file extension
|
|
43
|
+
if (!_req.path.startsWith('/api') && !path.extname(_req.path)) {
|
|
44
|
+
res.sendFile(path.join(uiPath, 'index.html'));
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
next();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
// Error handling middleware
|
|
51
|
+
app.use((err, _req, res, _next) => {
|
|
52
|
+
logger.error('HTTP server error', { error: err.message });
|
|
53
|
+
res.status(500).json({
|
|
54
|
+
error: 'Internal server error',
|
|
55
|
+
message: err.message,
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
// CRITICAL: Bind to localhost only for security
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
const server = app.listen(port, '127.0.0.1', () => {
|
|
61
|
+
const url = `http://127.0.0.1:${port}`;
|
|
62
|
+
logger.info(`Config UI running at ${url}`);
|
|
63
|
+
console.log(`
|
|
64
|
+
┌─────────────────────────────────────────────────┐
|
|
65
|
+
│ │
|
|
66
|
+
│ Agent Consultation MCP - Configuration UI │
|
|
67
|
+
│ │
|
|
68
|
+
│ URL: ${url.padEnd(40)}│
|
|
69
|
+
│ │
|
|
70
|
+
│ Press Ctrl+C to stop │
|
|
71
|
+
│ │
|
|
72
|
+
└─────────────────────────────────────────────────┘
|
|
73
|
+
`);
|
|
74
|
+
// Open browser
|
|
75
|
+
if (shouldOpenBrowser) {
|
|
76
|
+
open(url).catch((err) => {
|
|
77
|
+
logger.warn('Failed to open browser', { error: err.message });
|
|
78
|
+
console.log(`Please open ${url} in your browser`);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
resolve();
|
|
82
|
+
});
|
|
83
|
+
server.on('error', (err) => {
|
|
84
|
+
if (err.code === 'EADDRINUSE') {
|
|
85
|
+
logger.error(`Port ${port} is already in use`);
|
|
86
|
+
console.error(`\nError: Port ${port} is already in use.`);
|
|
87
|
+
console.error(`Try a different port: npx agent-consultation-mcp --config --port ${port + 1}\n`);
|
|
88
|
+
}
|
|
89
|
+
reject(err);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Start the UI server in background mode (for MCP mode)
|
|
95
|
+
* Does not block and does not open browser automatically
|
|
96
|
+
*/
|
|
97
|
+
export async function startBackgroundUIServer(port = DEFAULT_PORT) {
|
|
98
|
+
if (backgroundServerRunning) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
// Ensure config is loaded
|
|
102
|
+
const configManager = getConfigManager();
|
|
103
|
+
await configManager.init();
|
|
104
|
+
const app = express();
|
|
105
|
+
// Middleware
|
|
106
|
+
app.use(express.json());
|
|
107
|
+
app.use(securityMiddleware);
|
|
108
|
+
// API routes
|
|
109
|
+
app.use('/api/config', configRoutes);
|
|
110
|
+
app.use('/api/providers', providerRoutes);
|
|
111
|
+
app.use('/api/chat', chatRoutes);
|
|
112
|
+
// Serve static UI files
|
|
113
|
+
const uiPath = path.join(__dirname, '../ui');
|
|
114
|
+
app.use(express.static(uiPath));
|
|
115
|
+
// Fallback to index.html for SPA-like behavior
|
|
116
|
+
app.use((_req, res, next) => {
|
|
117
|
+
if (!_req.path.startsWith('/api') && !path.extname(_req.path)) {
|
|
118
|
+
res.sendFile(path.join(uiPath, 'index.html'));
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
next();
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
// Error handling middleware
|
|
125
|
+
app.use((err, _req, res, _next) => {
|
|
126
|
+
logger.error('Background HTTP server error', { error: err.message });
|
|
127
|
+
res.status(500).json({
|
|
128
|
+
error: 'Internal server error',
|
|
129
|
+
message: err.message,
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
return new Promise((resolve) => {
|
|
133
|
+
const server = app.listen(port, '127.0.0.1', () => {
|
|
134
|
+
backgroundServerRunning = true;
|
|
135
|
+
backgroundServerPort = port;
|
|
136
|
+
logger.info(`Background UI server started at http://127.0.0.1:${port}`);
|
|
137
|
+
resolve(true);
|
|
138
|
+
});
|
|
139
|
+
server.on('error', (err) => {
|
|
140
|
+
if (err.code === 'EADDRINUSE') {
|
|
141
|
+
// Port is in use, maybe server is already running from another process
|
|
142
|
+
logger.debug(`Port ${port} already in use, assuming UI server is running`);
|
|
143
|
+
backgroundServerRunning = true;
|
|
144
|
+
backgroundServerPort = port;
|
|
145
|
+
resolve(true);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
logger.error('Failed to start background UI server', { error: err.message });
|
|
149
|
+
resolve(false);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Open the Web UI in browser (only opens once per session)
|
|
156
|
+
* Spawns a completely separate standalone server process that handles
|
|
157
|
+
* both server startup and browser opening.
|
|
158
|
+
*/
|
|
159
|
+
export async function openWebUI() {
|
|
160
|
+
logger.info('openWebUI called', { browserOpened });
|
|
161
|
+
if (browserOpened) {
|
|
162
|
+
logger.info('Browser already opened, skipping');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
// Path to the standalone server script
|
|
166
|
+
const standaloneServerPath = path.join(__dirname, 'standalone-server.js');
|
|
167
|
+
logger.info('Spawning standalone server process', { standaloneServerPath });
|
|
168
|
+
try {
|
|
169
|
+
// Spawn a completely detached process
|
|
170
|
+
// This process will start the server AND open the browser
|
|
171
|
+
const child = spawn(process.execPath, [standaloneServerPath, String(backgroundServerPort)], {
|
|
172
|
+
detached: true,
|
|
173
|
+
stdio: 'ignore',
|
|
174
|
+
windowsHide: true,
|
|
175
|
+
env: { ...process.env }
|
|
176
|
+
});
|
|
177
|
+
// Unref to allow parent (MCP) process to exit independently
|
|
178
|
+
child.unref();
|
|
179
|
+
browserOpened = true;
|
|
180
|
+
backgroundServerRunning = true;
|
|
181
|
+
logger.info('Standalone server process spawned successfully');
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
logger.error('Failed to spawn standalone server process', {
|
|
185
|
+
error: err instanceof Error ? err.message : 'Unknown error'
|
|
186
|
+
});
|
|
187
|
+
// Fallback: try to open browser directly (server might already be running)
|
|
188
|
+
const url = `http://127.0.0.1:${backgroundServerPort}`;
|
|
189
|
+
try {
|
|
190
|
+
await open(url, { wait: false });
|
|
191
|
+
browserOpened = true;
|
|
192
|
+
logger.info('Browser opened via fallback');
|
|
193
|
+
}
|
|
194
|
+
catch (openErr) {
|
|
195
|
+
logger.error('Fallback browser open also failed', {
|
|
196
|
+
error: openErr instanceof Error ? openErr.message : 'Unknown error'
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Check if browser has been opened
|
|
203
|
+
*/
|
|
204
|
+
export function isBrowserOpened() {
|
|
205
|
+
return browserOpened;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Check if background server is running
|
|
209
|
+
*/
|
|
210
|
+
export function isBackgroundServerRunning() {
|
|
211
|
+
return backgroundServerRunning;
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction } from 'express';
|
|
2
|
+
/**
|
|
3
|
+
* Security middleware for the config UI HTTP server
|
|
4
|
+
* Adds security headers to all responses
|
|
5
|
+
*/
|
|
6
|
+
export declare function securityMiddleware(_req: Request, res: Response, next: NextFunction): void;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security middleware for the config UI HTTP server
|
|
3
|
+
* Adds security headers to all responses
|
|
4
|
+
*/
|
|
5
|
+
export function securityMiddleware(_req, res, next) {
|
|
6
|
+
// Prevent MIME type sniffing
|
|
7
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
8
|
+
// Prevent clickjacking
|
|
9
|
+
res.setHeader('X-Frame-Options', 'DENY');
|
|
10
|
+
// Enable XSS filter in browsers
|
|
11
|
+
res.setHeader('X-XSS-Protection', '1; mode=block');
|
|
12
|
+
// Content Security Policy - allow CDN resources for Alpine.js and Tailwind
|
|
13
|
+
// Note: 'unsafe-eval' is required for Alpine.js to evaluate expressions
|
|
14
|
+
// This is acceptable since the UI runs on localhost only
|
|
15
|
+
res.setHeader('Content-Security-Policy', [
|
|
16
|
+
"default-src 'self'",
|
|
17
|
+
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.tailwindcss.com https://unpkg.com",
|
|
18
|
+
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
|
|
19
|
+
"img-src 'self' data:",
|
|
20
|
+
"font-src 'self' https://fonts.gstatic.com",
|
|
21
|
+
"connect-src 'self'",
|
|
22
|
+
].join('; '));
|
|
23
|
+
// Prevent caching of sensitive data
|
|
24
|
+
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
|
|
25
|
+
res.setHeader('Pragma', 'no-cache');
|
|
26
|
+
next();
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../../src/api/middleware/security.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAAa,EACb,GAAa,EACb,IAAkB;IAElB,6BAA6B;IAC7B,GAAG,CAAC,SAAS,CAAC,wBAAwB,EAAE,SAAS,CAAC,CAAC;IAEnD,uBAAuB;IACvB,GAAG,CAAC,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;IAEzC,gCAAgC;IAChC,GAAG,CAAC,SAAS,CAAC,kBAAkB,EAAE,eAAe,CAAC,CAAC;IAEnD,2EAA2E;IAC3E,wEAAwE;IACxE,yDAAyD;IACzD,GAAG,CAAC,SAAS,CACX,yBAAyB,EACzB;QACE,oBAAoB;QACpB,+FAA+F;QAC/F,+DAA+D;QAC/D,sBAAsB;QACtB,2CAA2C;QAC3C,oBAAoB;KACrB,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;IAEF,oCAAoC;IACpC,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,qCAAqC,CAAC,CAAC;IACtE,GAAG,CAAC,SAAS,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAEpC,IAAI,EAAE,CAAC;AACT,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { ConversationManager, conversationManager } from '../../server/conversation.js';
|
|
3
|
+
const router = Router();
|
|
4
|
+
/**
|
|
5
|
+
* GET /api/chat/history
|
|
6
|
+
* Returns all conversations (active + archived) from files
|
|
7
|
+
*/
|
|
8
|
+
router.get('/history', (_req, res) => {
|
|
9
|
+
try {
|
|
10
|
+
// Load active conversations from both in-memory (runtime) and file
|
|
11
|
+
const inMemoryActive = conversationManager.listActive();
|
|
12
|
+
const fileActive = ConversationManager.loadFromFile();
|
|
13
|
+
// Merge and dedupe by ID (in-memory takes precedence)
|
|
14
|
+
const activeMap = new Map();
|
|
15
|
+
[...fileActive, ...inMemoryActive].forEach((conv) => {
|
|
16
|
+
activeMap.set(conv.id, conv);
|
|
17
|
+
});
|
|
18
|
+
const activeConversations = Array.from(activeMap.values());
|
|
19
|
+
// Load archived conversations and dedupe
|
|
20
|
+
const archivedRaw = ConversationManager.loadHistoryFromFile();
|
|
21
|
+
// Get active IDs to exclude from archived (active takes precedence)
|
|
22
|
+
const activeIds = new Set(activeConversations.map((c) => c.id));
|
|
23
|
+
// Dedupe archived: keep only first occurrence (newest) and exclude active ones
|
|
24
|
+
const archivedMap = new Map();
|
|
25
|
+
archivedRaw.forEach((conv) => {
|
|
26
|
+
if (!activeIds.has(conv.id) && !archivedMap.has(conv.id)) {
|
|
27
|
+
archivedMap.set(conv.id, conv);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
const archivedConversations = Array.from(archivedMap.values());
|
|
31
|
+
// Format active conversations for UI display
|
|
32
|
+
const active = activeConversations.map((conv) => ({
|
|
33
|
+
id: conv.id,
|
|
34
|
+
model: conv.model,
|
|
35
|
+
messageCount: conv.messages.length,
|
|
36
|
+
messages: conv.messages.map((msg, idx) => ({
|
|
37
|
+
id: `${conv.id}-${idx}`,
|
|
38
|
+
role: msg.role,
|
|
39
|
+
content: msg.content,
|
|
40
|
+
})),
|
|
41
|
+
createdAt: conv.createdAt.toISOString(),
|
|
42
|
+
lastActivityAt: conv.lastActivityAt.toISOString(),
|
|
43
|
+
status: 'active',
|
|
44
|
+
}));
|
|
45
|
+
// Format archived conversations for UI display
|
|
46
|
+
const archived = archivedConversations.map((conv) => ({
|
|
47
|
+
id: conv.id,
|
|
48
|
+
model: conv.model,
|
|
49
|
+
messageCount: conv.messages.length,
|
|
50
|
+
messages: conv.messages.map((msg, idx) => ({
|
|
51
|
+
id: `${conv.id}-${idx}`,
|
|
52
|
+
role: msg.role,
|
|
53
|
+
content: msg.content,
|
|
54
|
+
})),
|
|
55
|
+
createdAt: conv.createdAt,
|
|
56
|
+
lastActivityAt: conv.lastActivityAt,
|
|
57
|
+
endedAt: conv.endedAt,
|
|
58
|
+
endReason: conv.endReason,
|
|
59
|
+
status: 'archived',
|
|
60
|
+
}));
|
|
61
|
+
// Combine: active first, then archived (already sorted by newest first)
|
|
62
|
+
const allConversations = [...active, ...archived];
|
|
63
|
+
res.json({
|
|
64
|
+
count: allConversations.length,
|
|
65
|
+
activeCount: active.length,
|
|
66
|
+
archivedCount: archived.length,
|
|
67
|
+
conversations: allConversations,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
res.status(500).json({
|
|
72
|
+
error: 'Failed to fetch conversation history',
|
|
73
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
export default router;
|
|
78
|
+
//# sourceMappingURL=chat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../../../src/api/routes/chat.ts"],"names":[],"mappings":"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"}
|