@makeshkumar/blueorch-studio 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/bin/cli.js +69 -0
- package/cache-manager.js +191 -0
- package/chat.routes.js +691 -0
- package/llm.routes.js +285 -0
- package/log.routes.js +42 -0
- package/logger.js +86 -0
- package/mcp.routes.js +320 -0
- package/package.json +37 -0
- package/public/3rdpartylicenses.txt +416 -0
- package/public/browser/assets/monaco/_commonjsHelpers-CT9FvmAN.js +1 -0
- package/public/browser/assets/monaco/abap-D-t0cyap.js +1 -0
- package/public/browser/assets/monaco/apex-CcIm7xu6.js +1 -0
- package/public/browser/assets/monaco/assets/css.worker-HnVq6Ewq.js +93 -0
- package/public/browser/assets/monaco/assets/editor.worker-Be8ye1pW.js +26 -0
- package/public/browser/assets/monaco/assets/html.worker-B51mlPHg.js +470 -0
- package/public/browser/assets/monaco/assets/json.worker-DKiEKt88.js +58 -0
- package/public/browser/assets/monaco/assets/ts.worker-CMbG-7ft.js +67731 -0
- package/public/browser/assets/monaco/azcli-BA0tQDCg.js +1 -0
- package/public/browser/assets/monaco/basic-languages/monaco.contribution.js +1 -0
- package/public/browser/assets/monaco/bat-C397hTD6.js +1 -0
- package/public/browser/assets/monaco/bicep-DF5aW17k.js +2 -0
- package/public/browser/assets/monaco/cameligo-plsz8qhj.js +1 -0
- package/public/browser/assets/monaco/clojure-Y2auQMzK.js +1 -0
- package/public/browser/assets/monaco/coffee-Bu45yuWE.js +1 -0
- package/public/browser/assets/monaco/cpp-CkKPQIni.js +1 -0
- package/public/browser/assets/monaco/csharp-CX28MZyh.js +1 -0
- package/public/browser/assets/monaco/csp-D8uWnyxW.js +1 -0
- package/public/browser/assets/monaco/css-CaeNmE3S.js +3 -0
- package/public/browser/assets/monaco/cssMode-CjiAH6dQ.js +1 -0
- package/public/browser/assets/monaco/cypher-DVThT8BS.js +1 -0
- package/public/browser/assets/monaco/dart-CmGfCvrO.js +1 -0
- package/public/browser/assets/monaco/dockerfile-CZqqYdch.js +1 -0
- package/public/browser/assets/monaco/ecl-30fUercY.js +1 -0
- package/public/browser/assets/monaco/editor/editor.main.css +1 -0
- package/public/browser/assets/monaco/editor/editor.main.js +5 -0
- package/public/browser/assets/monaco/editor.api-CalNCsUg.js +903 -0
- package/public/browser/assets/monaco/elixir-xjPaIfzF.js +1 -0
- package/public/browser/assets/monaco/flow9-DqtmStfK.js +1 -0
- package/public/browser/assets/monaco/freemarker2-Cz_sV6Md.js +3 -0
- package/public/browser/assets/monaco/fsharp-BOMdg4U1.js +1 -0
- package/public/browser/assets/monaco/go-D_hbi-Jt.js +1 -0
- package/public/browser/assets/monaco/graphql-CKUU4kLG.js +1 -0
- package/public/browser/assets/monaco/handlebars-OwglfO-1.js +1 -0
- package/public/browser/assets/monaco/hcl-DTaboeZW.js +1 -0
- package/public/browser/assets/monaco/html-Pa1xEWsY.js +1 -0
- package/public/browser/assets/monaco/htmlMode-Bz67EXwp.js +1 -0
- package/public/browser/assets/monaco/ini-CsNwO04R.js +1 -0
- package/public/browser/assets/monaco/java-CI4ZMsH9.js +1 -0
- package/public/browser/assets/monaco/javascript-PczUCGdz.js +1 -0
- package/public/browser/assets/monaco/jsonMode-DULH5oaX.js +7 -0
- package/public/browser/assets/monaco/julia-BwzEvaQw.js +1 -0
- package/public/browser/assets/monaco/kotlin-IUYPiTV8.js +1 -0
- package/public/browser/assets/monaco/language/css/monaco.contribution.js +1 -0
- package/public/browser/assets/monaco/language/html/monaco.contribution.js +1 -0
- package/public/browser/assets/monaco/language/json/monaco.contribution.js +1 -0
- package/public/browser/assets/monaco/language/typescript/monaco.contribution.js +1 -0
- package/public/browser/assets/monaco/less-C0eDYdqa.js +2 -0
- package/public/browser/assets/monaco/lexon-iON-Kj97.js +1 -0
- package/public/browser/assets/monaco/liquid-DqKjdPGy.js +1 -0
- package/public/browser/assets/monaco/loader.js +1368 -0
- package/public/browser/assets/monaco/lspLanguageFeatures-kM9O9rjY.js +4 -0
- package/public/browser/assets/monaco/lua-DtygF91M.js +1 -0
- package/public/browser/assets/monaco/m3-CsR4AuFi.js +1 -0
- package/public/browser/assets/monaco/markdown-C_rD0bIw.js +1 -0
- package/public/browser/assets/monaco/mdx-DEWtB1K5.js +1 -0
- package/public/browser/assets/monaco/mips-CiYP61RB.js +1 -0
- package/public/browser/assets/monaco/monaco.contribution-D2OdxNBt.js +1 -0
- package/public/browser/assets/monaco/monaco.contribution-DO3azKX8.js +1 -0
- package/public/browser/assets/monaco/monaco.contribution-EcChJV6a.js +1 -0
- package/public/browser/assets/monaco/monaco.contribution-qLAYrEOP.js +1 -0
- package/public/browser/assets/monaco/msdax-C38-sJlp.js +1 -0
- package/public/browser/assets/monaco/mysql-CdtbpvbG.js +1 -0
- package/public/browser/assets/monaco/nls.messages-loader.js +1 -0
- package/public/browser/assets/monaco/nls.messages.cs.js.js +17 -0
- package/public/browser/assets/monaco/nls.messages.de.js.js +17 -0
- package/public/browser/assets/monaco/nls.messages.es.js.js +17 -0
- package/public/browser/assets/monaco/nls.messages.fr.js.js +15 -0
- package/public/browser/assets/monaco/nls.messages.it.js.js +15 -0
- package/public/browser/assets/monaco/nls.messages.ja.js.js +17 -0
- package/public/browser/assets/monaco/nls.messages.js.js +10 -0
- package/public/browser/assets/monaco/nls.messages.ko.js.js +25 -0
- package/public/browser/assets/monaco/nls.messages.pl.js.js +17 -0
- package/public/browser/assets/monaco/nls.messages.pt-br.js.js +6 -0
- package/public/browser/assets/monaco/nls.messages.ru.js.js +17 -0
- package/public/browser/assets/monaco/nls.messages.tr.js.js +15 -0
- package/public/browser/assets/monaco/nls.messages.zh-cn.js.js +17 -0
- package/public/browser/assets/monaco/nls.messages.zh-tw.js.js +15 -0
- package/public/browser/assets/monaco/objective-c-CntZFaHX.js +1 -0
- package/public/browser/assets/monaco/pascal-r6kuqfl_.js +1 -0
- package/public/browser/assets/monaco/pascaligo-BiXoTmXh.js +1 -0
- package/public/browser/assets/monaco/perl-DABw_TcH.js +1 -0
- package/public/browser/assets/monaco/pgsql-me_jFXeX.js +1 -0
- package/public/browser/assets/monaco/php-D_kh-9LK.js +1 -0
- package/public/browser/assets/monaco/pla-VfZjczW0.js +1 -0
- package/public/browser/assets/monaco/postiats-BBSzz8Pk.js +1 -0
- package/public/browser/assets/monaco/powerquery-Dt-g_2cc.js +1 -0
- package/public/browser/assets/monaco/powershell-B-7ap1zc.js +1 -0
- package/public/browser/assets/monaco/protobuf-BmtuEB1A.js +2 -0
- package/public/browser/assets/monaco/pug-BRpRNeEb.js +1 -0
- package/public/browser/assets/monaco/python-Cr0UkIbn.js +1 -0
- package/public/browser/assets/monaco/qsharp-BzsFaUU9.js +1 -0
- package/public/browser/assets/monaco/r-f8dDdrp4.js +1 -0
- package/public/browser/assets/monaco/razor-BYAHOTkz.js +1 -0
- package/public/browser/assets/monaco/redis-fvZQY4PI.js +1 -0
- package/public/browser/assets/monaco/redshift-45Et0LQi.js +1 -0
- package/public/browser/assets/monaco/restructuredtext-C7UUFKFD.js +1 -0
- package/public/browser/assets/monaco/ruby-CZO8zYTz.js +1 -0
- package/public/browser/assets/monaco/rust-Bfetafyc.js +1 -0
- package/public/browser/assets/monaco/sb-3GYllVck.js +1 -0
- package/public/browser/assets/monaco/scala-foMgrKo1.js +1 -0
- package/public/browser/assets/monaco/scheme-CHdMtr7p.js +1 -0
- package/public/browser/assets/monaco/scss-C1cmLt9V.js +3 -0
- package/public/browser/assets/monaco/shell-ClXCKCEW.js +1 -0
- package/public/browser/assets/monaco/solidity-MZ6ExpPy.js +1 -0
- package/public/browser/assets/monaco/sophia-DWkuSsPQ.js +1 -0
- package/public/browser/assets/monaco/sparql-AUGFYSyk.js +1 -0
- package/public/browser/assets/monaco/sql-32GpJSV2.js +1 -0
- package/public/browser/assets/monaco/st-CuDFIVZ_.js +1 -0
- package/public/browser/assets/monaco/swift-n-2HociN.js +3 -0
- package/public/browser/assets/monaco/systemverilog-Ch4vA8Yt.js +1 -0
- package/public/browser/assets/monaco/tcl-D74tq1nH.js +1 -0
- package/public/browser/assets/monaco/tsMode-CZz1Umrk.js +11 -0
- package/public/browser/assets/monaco/twig-C6taOxMV.js +1 -0
- package/public/browser/assets/monaco/typescript-DfOrAzoV.js +1 -0
- package/public/browser/assets/monaco/typespec-D-PIh9Xw.js +1 -0
- package/public/browser/assets/monaco/vb-Dyb2648j.js +1 -0
- package/public/browser/assets/monaco/wgsl-BhLXMOR0.js +298 -0
- package/public/browser/assets/monaco/workers-DcJshg-q.js +1 -0
- package/public/browser/assets/monaco/xml-CdsdnY8S.js +1 -0
- package/public/browser/assets/monaco/yaml-DYGvmE88.js +1 -0
- package/public/browser/favicon.ico +0 -0
- package/public/browser/favicon.svg +4 -0
- package/public/browser/index.html +20 -0
- package/public/browser/main-BRU65EMU.js +80 -0
- package/public/browser/polyfills-FFHMD2TL.js +2 -0
- package/public/browser/styles-R37AZPY2.css +1 -0
- package/server.js +60 -0
- package/system.routes.js +150 -0
- package/usage-normalizer.js +117 -0
package/mcp.routes.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { isAbsolute, join } from 'path';
|
|
5
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
6
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
7
|
+
|
|
8
|
+
const router = Router();
|
|
9
|
+
const PATH_KEY_PATTERN = /(path|file|dir|directory|folder|filename)$/i;
|
|
10
|
+
const PATH_TEXT_PATTERN = /(absolute path|relative path|file path|directory path|folder path|workspace path|file name|filename|directory|folder|path)/i;
|
|
11
|
+
|
|
12
|
+
// ─── Active Clients Map ───────────────────────────────────────────────────────
|
|
13
|
+
// Key: connectionId (UUID)
|
|
14
|
+
// Value: { client, transport, tools, command, args, label, logs, connectedAt }
|
|
15
|
+
// Exported so chat.routes.js can call tools on active connections.
|
|
16
|
+
export const activeClients = new Map();
|
|
17
|
+
|
|
18
|
+
const ts = () => new Date(Date.now() + (5 * 60 + 30) * 60000).toISOString().replace('Z', '+05:30');
|
|
19
|
+
|
|
20
|
+
function isPathLikeSchemaProperty(key, schemaProperty) {
|
|
21
|
+
const normalizedKey = (key ?? '').replace(/[_-]/g, '').toLowerCase();
|
|
22
|
+
const descriptionText = [
|
|
23
|
+
schemaProperty?.description,
|
|
24
|
+
schemaProperty?.title,
|
|
25
|
+
].filter(Boolean).join(' ');
|
|
26
|
+
const schemaType = schemaProperty?.type;
|
|
27
|
+
|
|
28
|
+
if (schemaType && schemaType !== 'string') {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (normalizedKey === 'content') {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return PATH_KEY_PATTERN.test(normalizedKey) || PATH_TEXT_PATTERN.test(descriptionText);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getPathArgumentKeys(toolArgs, inputSchema) {
|
|
40
|
+
const schemaProperties = inputSchema?.properties ?? {};
|
|
41
|
+
const keys = new Set();
|
|
42
|
+
|
|
43
|
+
for (const [key, schemaProperty] of Object.entries(schemaProperties)) {
|
|
44
|
+
if (isPathLikeSchemaProperty(key, schemaProperty)) {
|
|
45
|
+
keys.add(key);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const [key, value] of Object.entries(toolArgs ?? {})) {
|
|
50
|
+
if (typeof value !== 'string') continue;
|
|
51
|
+
if (isPathLikeSchemaProperty(key, schemaProperties[key])) {
|
|
52
|
+
keys.add(key);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return [...keys];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function resolveWorkspacePathValue(pathValue, activeWorkspacePath) {
|
|
60
|
+
console.log(`[INIT] ${ts()} resolveWorkspacePathValue()`);
|
|
61
|
+
|
|
62
|
+
if (!activeWorkspacePath || typeof pathValue !== 'string') {
|
|
63
|
+
console.log(`[SUCCESS] ${ts()} resolveWorkspacePathValue() | no rewrite`);
|
|
64
|
+
return pathValue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const trimmedPath = pathValue.trim();
|
|
68
|
+
|
|
69
|
+
if (!trimmedPath || trimmedPath === '.' || trimmedPath === './') {
|
|
70
|
+
console.log(`[SUCCESS] ${ts()} resolveWorkspacePathValue() | workspace root injected`);
|
|
71
|
+
return activeWorkspacePath;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (isAbsolute(trimmedPath)) {
|
|
75
|
+
console.log(`[SUCCESS] ${ts()} resolveWorkspacePathValue() | absolute path preserved`);
|
|
76
|
+
return trimmedPath;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const resolvedPath = join(activeWorkspacePath, trimmedPath);
|
|
80
|
+
console.log(`[SUCCESS] ${ts()} resolveWorkspacePathValue() | resolved: "${resolvedPath}"`);
|
|
81
|
+
return resolvedPath;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function resolveWorkspaceToolArgs(toolName, toolArgs, activeWorkspacePath, inputSchema) {
|
|
85
|
+
console.log(`[INIT] ${ts()} resolveWorkspaceToolArgs() | tool: ${toolName}`);
|
|
86
|
+
|
|
87
|
+
const finalArgs = { ...(toolArgs ?? {}) };
|
|
88
|
+
const keysToResolve = getPathArgumentKeys(finalArgs, inputSchema);
|
|
89
|
+
|
|
90
|
+
let didRewrite = false;
|
|
91
|
+
for (const key of keysToResolve) {
|
|
92
|
+
const nextValue = resolveWorkspacePathValue(finalArgs[key], activeWorkspacePath || homedir());
|
|
93
|
+
if (nextValue !== undefined && nextValue !== finalArgs[key]) {
|
|
94
|
+
finalArgs[key] = nextValue;
|
|
95
|
+
didRewrite = true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
console.log(
|
|
100
|
+
`[SUCCESS] ${ts()} resolveWorkspaceToolArgs() | tool: ${toolName} | rewritten: ${didRewrite}`
|
|
101
|
+
);
|
|
102
|
+
return finalArgs;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ─── POST /api/mcp/connect ────────────────────────────────────────────────────
|
|
106
|
+
// Body: { command: string, args: string[] }
|
|
107
|
+
router.post('/connect', async (req, res) => {
|
|
108
|
+
const { command, args } = req.body;
|
|
109
|
+
|
|
110
|
+
if (!command || typeof command !== 'string') {
|
|
111
|
+
return res.status(400).json({ error: '"command" (string) is required, e.g. "node" or "uvx"' });
|
|
112
|
+
}
|
|
113
|
+
if (!Array.isArray(args)) {
|
|
114
|
+
return res.status(400).json({ error: '"args" (array of strings) is required' });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const connectionId = randomUUID();
|
|
118
|
+
|
|
119
|
+
// ── Default filesystem path to homedir when none is provided ────────────────
|
|
120
|
+
// Detects: `npx -y @modelcontextprotocol/server-filesystem` with no trailing path
|
|
121
|
+
let resolvedArgs = [...args];
|
|
122
|
+
const fsIdx = args.findIndex(a => a.includes('server-filesystem'));
|
|
123
|
+
if (fsIdx !== -1 && fsIdx === args.length - 1) {
|
|
124
|
+
resolvedArgs = [...args, homedir()];
|
|
125
|
+
console.log(`[INIT] ${ts()} Filesystem server: no path given — defaulting to ${homedir()}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ── Derive a human-readable label from command + args ─────────────────────
|
|
129
|
+
// Skip flag args (-y, --flag) to find the actual package/script name.
|
|
130
|
+
// For scoped npm packages (@scope/name) keep "scope/name" so context isn't lost.
|
|
131
|
+
// For plain path-like args (/usr/local/bin/tool) use only the last segment.
|
|
132
|
+
const pkgArg = resolvedArgs.find(a => !a.startsWith('-'));
|
|
133
|
+
let label = pkgArg ?? command;
|
|
134
|
+
if (label.startsWith('@')) {
|
|
135
|
+
label = label.slice(1); // "@playwright/mcp" → "playwright/mcp"
|
|
136
|
+
} else if (label.includes('/')) {
|
|
137
|
+
label = label.split('/').pop(); // plain path → last segment only
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
console.log(`[INIT] ${ts()} Connecting | command: ${command} | args: ${args.join(' ')} | id: ${connectionId}`);
|
|
141
|
+
|
|
142
|
+
const transport = new StdioClientTransport({ command, args: resolvedArgs, stderr: 'pipe' });
|
|
143
|
+
|
|
144
|
+
const client = new Client(
|
|
145
|
+
{ name: 'BlueOrch-Studio-Web-Bridge', version: '1.0.0' },
|
|
146
|
+
{ capabilities: {} }
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
await client.connect(transport);
|
|
151
|
+
const { tools } = await client.listTools();
|
|
152
|
+
|
|
153
|
+
const logs = [`[BRIDGE] ${ts()} Connected — ${tools.length} tool(s) available`];
|
|
154
|
+
|
|
155
|
+
activeClients.set(connectionId, {
|
|
156
|
+
client,
|
|
157
|
+
transport,
|
|
158
|
+
tools,
|
|
159
|
+
command,
|
|
160
|
+
args: resolvedArgs,
|
|
161
|
+
label,
|
|
162
|
+
logs,
|
|
163
|
+
connectedAt: ts(),
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Capture spawned server's stderr as log entries
|
|
167
|
+
transport.stderr?.on('data', (chunk) => {
|
|
168
|
+
const entry = activeClients.get(connectionId);
|
|
169
|
+
if (!entry) return;
|
|
170
|
+
chunk.toString().split('\n').forEach(line => {
|
|
171
|
+
const trimmed = line.trim();
|
|
172
|
+
if (trimmed) {
|
|
173
|
+
entry.logs.push(`[SERVER] ${ts()} ${trimmed}`);
|
|
174
|
+
if (entry.logs.length > 300) entry.logs.shift();
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
console.log(`[SUCCESS] ${ts()} Connected | id: ${connectionId} | tools: ${tools.length}`);
|
|
180
|
+
return res.json({ connectionId, label, command, args: resolvedArgs, tools });
|
|
181
|
+
} catch (err) {
|
|
182
|
+
console.error(`[ERROR] ${ts()} Connection failed: ${err.message}`);
|
|
183
|
+
return res.status(500).json({ error: err.message });
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// ─── GET /api/mcp/clients ─────────────────────────────────────────────────────
|
|
188
|
+
router.get('/clients', (_req, res) => {
|
|
189
|
+
const clientList = Array.from(activeClients.entries()).map(([id, entry]) => ({
|
|
190
|
+
connectionId: id,
|
|
191
|
+
label: entry.label,
|
|
192
|
+
command: entry.command,
|
|
193
|
+
args: entry.args,
|
|
194
|
+
toolCount: entry.tools.length,
|
|
195
|
+
connectedAt: entry.connectedAt,
|
|
196
|
+
}));
|
|
197
|
+
|
|
198
|
+
console.log(`[SUCCESS] ${ts()} Listing ${clientList.length} active client(s)`);
|
|
199
|
+
return res.json(clientList);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// ─── GET /api/mcp/tools/:connectionId ────────────────────────────────────────
|
|
203
|
+
router.get('/tools/:connectionId', (req, res) => {
|
|
204
|
+
const { connectionId } = req.params;
|
|
205
|
+
const entry = activeClients.get(connectionId);
|
|
206
|
+
|
|
207
|
+
if (!entry) {
|
|
208
|
+
return res.status(404).json({ error: `Connection "${connectionId}" not found` });
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
console.log(`[SUCCESS] ${ts()} Tools for ${connectionId}: ${entry.tools.length}`);
|
|
212
|
+
return res.json({ connectionId, tools: entry.tools });
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// ─── POST /api/mcp/execute ────────────────────────────────────────────────────
|
|
216
|
+
// Body: { connectionId, toolName, arguments }
|
|
217
|
+
router.post('/execute', async (req, res) => {
|
|
218
|
+
const { connectionId, toolName, arguments: toolArgs } = req.body;
|
|
219
|
+
|
|
220
|
+
if (!connectionId || !toolName) {
|
|
221
|
+
return res.status(400).json({ error: 'connectionId and toolName are required' });
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const entry = activeClients.get(connectionId);
|
|
225
|
+
if (!entry) {
|
|
226
|
+
return res.status(404).json({ error: `Connection "${connectionId}" not found` });
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
console.log(`[INIT] ${ts()} Executing tool "${toolName}" on ${connectionId}`);
|
|
230
|
+
entry.logs.push(`[BRIDGE] ${ts()} Tool called: ${toolName} | args: ${JSON.stringify(toolArgs ?? {})}`);
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
const result = await entry.client.callTool({
|
|
234
|
+
name: toolName,
|
|
235
|
+
arguments: toolArgs ?? {},
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
entry.logs.push(`[BRIDGE] ${ts()} Tool "${toolName}" completed successfully`);
|
|
239
|
+
if (entry.logs.length > 300) entry.logs.shift();
|
|
240
|
+
console.log(`[SUCCESS] ${ts()} Tool "${toolName}" completed`);
|
|
241
|
+
return res.json({ connectionId, toolName, result });
|
|
242
|
+
} catch (err) {
|
|
243
|
+
entry.logs.push(`[ERROR] ${ts()} Tool "${toolName}" failed: ${err.message}`);
|
|
244
|
+
console.error(`[ERROR] ${ts()} Tool "${toolName}" failed: ${err.message}`);
|
|
245
|
+
return res.status(500).json({ error: err.message });
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// ─── DELETE /api/mcp/disconnect/:connectionId ─────────────────────────────────
|
|
250
|
+
router.delete('/disconnect/:connectionId', async (req, res) => {
|
|
251
|
+
const { connectionId } = req.params;
|
|
252
|
+
const entry = activeClients.get(connectionId);
|
|
253
|
+
|
|
254
|
+
if (!entry) {
|
|
255
|
+
return res.status(404).json({ error: `Connection "${connectionId}" not found` });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
await entry.client.close();
|
|
260
|
+
} catch (_) {
|
|
261
|
+
// Suppress — the spawned process may have already exited
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
activeClients.delete(connectionId);
|
|
265
|
+
console.log(`[SUCCESS] ${ts()} Disconnected ${connectionId}`);
|
|
266
|
+
return res.json({ success: true, connectionId });
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// ─── GET /api/mcp/logs/:connectionId ──────────────────────────────────────────
|
|
270
|
+
router.get('/logs/:connectionId', (req, res) => {
|
|
271
|
+
const entry = activeClients.get(req.params.connectionId);
|
|
272
|
+
if (!entry) return res.status(404).json({ error: 'Connection not found' });
|
|
273
|
+
return res.json({ connectionId: req.params.connectionId, logs: entry.logs });
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// ─── DELETE /api/mcp/logs/:connectionId/clear ────────────────────────────────────────
|
|
277
|
+
router.delete('/logs/:connectionId/clear', (req, res) => {
|
|
278
|
+
const entry = activeClients.get(req.params.connectionId);
|
|
279
|
+
if (!entry) return res.status(404).json({ error: 'Connection not found' });
|
|
280
|
+
entry.logs.length = 0;
|
|
281
|
+
console.log(`[SUCCESS] ${ts()} Logs cleared for ${req.params.connectionId}`);
|
|
282
|
+
return res.json({ success: true });
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// ─── POST /api/mcp/proxy ────────────────────────────────────────────────────────────
|
|
286
|
+
// Body: { connectionId, toolName, toolArgs, activeWorkspacePath }
|
|
287
|
+
// Intercepts list_directory / read_file calls and injects activeWorkspacePath
|
|
288
|
+
// when the supplied path is empty or refers to the current directory.
|
|
289
|
+
router.post('/proxy', async (req, res) => {
|
|
290
|
+
const { connectionId, toolName, toolArgs, activeWorkspacePath } = req.body;
|
|
291
|
+
|
|
292
|
+
if (!connectionId || !toolName) {
|
|
293
|
+
return res.status(400).json({ error: 'connectionId and toolName are required' });
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const entry = activeClients.get(connectionId);
|
|
297
|
+
if (!entry) {
|
|
298
|
+
return res.status(404).json({ error: `Connection "${connectionId}" not found` });
|
|
299
|
+
}
|
|
300
|
+
console.log('***************************** ',activeWorkspacePath)
|
|
301
|
+
|
|
302
|
+
const tool = entry.tools.find(t => t.name === toolName);
|
|
303
|
+
const finalArgs = resolveWorkspaceToolArgs(toolName, toolArgs, activeWorkspacePath, tool?.inputSchema);
|
|
304
|
+
console.log('##################### ',finalArgs)
|
|
305
|
+
entry.logs.push(`[BRIDGE] ${ts()} Proxy tool: ${toolName} | args: ${JSON.stringify(finalArgs)}`);
|
|
306
|
+
console.log(`[INIT] ${ts()} /mcp/proxy | tool: ${toolName} | connection: ${connectionId}`);
|
|
307
|
+
|
|
308
|
+
try {
|
|
309
|
+
const result = await entry.client.callTool({ name: toolName, arguments: finalArgs });
|
|
310
|
+
entry.logs.push(`[BRIDGE] ${ts()} Proxy tool "${toolName}" completed`);
|
|
311
|
+
console.log(`[SUCCESS] ${ts()} Proxy tool "${toolName}" completed`);
|
|
312
|
+
return res.json({ connectionId, toolName, result });
|
|
313
|
+
} catch (err) {
|
|
314
|
+
entry.logs.push(`[ERROR] ${ts()} Proxy tool "${toolName}" failed: ${err.message}`);
|
|
315
|
+
console.error(`[ERROR] ${ts()} Proxy tool "${toolName}" failed: ${err.message}`);
|
|
316
|
+
return res.status(500).json({ error: err.message });
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
export default router;
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@makeshkumar/blueorch-studio",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "BlueOrch Studio — Modular MCP Orchestrator with LLM chat, served as an npx command",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "server.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"blueorch": "./bin/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin/",
|
|
12
|
+
"public/",
|
|
13
|
+
"*.js",
|
|
14
|
+
"*.json",
|
|
15
|
+
"!*.test.js",
|
|
16
|
+
"!package-lock.json"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"start": "NODE_ENV=production node server.js",
|
|
20
|
+
"dev": "NODE_ENV=development node --watch server.js",
|
|
21
|
+
"build:prod": "cd ../web-ui && ng build",
|
|
22
|
+
"publish:npm": "npm run build:prod && npm publish"
|
|
23
|
+
},
|
|
24
|
+
"keywords": ["mcp", "orchestrator", "llm", "ai", "blueorch"],
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@anthropic-ai/sdk": "^0.80.0",
|
|
27
|
+
"@google/genai": "^1.46.0",
|
|
28
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
29
|
+
"commander": "^14.0.3",
|
|
30
|
+
"cors": "^2.8.5",
|
|
31
|
+
"dotenv": "^17.3.1",
|
|
32
|
+
"express": "^4.18.0",
|
|
33
|
+
"open": "^11.0.0",
|
|
34
|
+
"openai": "^6.32.0",
|
|
35
|
+
"winston": "^3.19.0"
|
|
36
|
+
}
|
|
37
|
+
}
|