dev3000 0.0.92 → 0.0.94
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/dist/dev-environment.d.ts.map +1 -1
- package/dist/dev-environment.js +20 -12
- package/dist/dev-environment.js.map +1 -1
- package/dist/src/tui-interface-impl.tsx +6 -0
- package/dist/tui-interface-impl.d.ts.map +1 -1
- package/dist/tui-interface-impl.js +4 -0
- package/dist/tui-interface-impl.js.map +1 -1
- package/mcp-server/.next/BUILD_ID +1 -1
- package/mcp-server/.next/build-manifest.json +2 -2
- package/mcp-server/.next/fallback-build-manifest.json +2 -2
- package/mcp-server/.next/next-minimal-server.js.nft.json +1 -1
- package/mcp-server/.next/next-server.js.nft.json +1 -1
- package/mcp-server/.next/prerender-manifest.json +3 -3
- package/mcp-server/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/mcp-server/.next/server/app/_global-error.html +2 -2
- package/mcp-server/.next/server/app/_global-error.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/mcp-server/.next/server/app/_not-found.html +1 -1
- package/mcp-server/.next/server/app/_not-found.rsc +1 -1
- package/mcp-server/.next/server/app/api/jank/[session]/route.js.nft.json +1 -1
- package/mcp-server/.next/server/app/api/logs/append/route.js.nft.json +1 -1
- package/mcp-server/.next/server/app/api/logs/head/route.js.nft.json +1 -1
- package/mcp-server/.next/server/app/api/logs/list/route.js.nft.json +1 -1
- package/mcp-server/.next/server/app/api/logs/rotate/route.js.nft.json +1 -1
- package/mcp-server/.next/server/app/api/logs/stream/route.js.nft.json +1 -1
- package/mcp-server/.next/server/app/api/logs/tail/route.js.nft.json +1 -1
- package/mcp-server/.next/server/app/api/orchestrator/route.js.nft.json +1 -1
- package/mcp-server/.next/server/app/api/screenshots/[filename]/route.js.nft.json +1 -1
- package/mcp-server/.next/server/app/api/screenshots/list/route.js.nft.json +1 -1
- package/mcp-server/.next/server/app/api/tools/route.js.nft.json +1 -1
- package/mcp-server/.next/server/app/index.html +1 -1
- package/mcp-server/.next/server/app/index.rsc +1 -1
- package/mcp-server/.next/server/app/logs/page.js.nft.json +1 -1
- package/mcp-server/.next/server/app/mcp/route.js.nft.json +1 -1
- package/mcp-server/.next/server/app/page.js.nft.json +1 -1
- package/mcp-server/.next/server/app/video/[session]/page.js.nft.json +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__94037b23._.js +2 -2
- package/mcp-server/.next/server/chunks/[root-of-the-server]__94037b23._.js.map +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__cc74dbef._.js +2 -2
- package/mcp-server/.next/server/chunks/[root-of-the-server]__cc74dbef._.js.map +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__dc0b0022._.js +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__dc0b0022._.js.map +1 -1
- package/mcp-server/.next/server/server-reference-manifest.js +1 -1
- package/mcp-server/.next/server/server-reference-manifest.json +1 -1
- package/mcp-server/app/mcp/client-manager.ts +76 -23
- package/mcp-server/app/mcp/route.ts +140 -15
- package/package.json +1 -1
- package/src/tui-interface-impl.tsx +6 -0
- /package/mcp-server/.next/static/{_qXVPjMKmdbmLNrnd_3zK → ZeZ7S325drnA0SH-odcGY}/_buildManifest.js +0 -0
- /package/mcp-server/.next/static/{_qXVPjMKmdbmLNrnd_3zK → ZeZ7S325drnA0SH-odcGY}/_clientMiddlewareManifest.json +0 -0
- /package/mcp-server/.next/static/{_qXVPjMKmdbmLNrnd_3zK → ZeZ7S325drnA0SH-odcGY}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
self.__RSC_SERVER_MANIFEST="{\n \"node\": {},\n \"edge\": {},\n \"encryptionKey\": \"
|
|
1
|
+
self.__RSC_SERVER_MANIFEST="{\n \"node\": {},\n \"edge\": {},\n \"encryptionKey\": \"Az3Ay/ZpgjzPNsVoQzRDLlh6S8/dSjBuLozeWfes2Dc=\"\n}"
|
|
@@ -32,6 +32,8 @@ export class MCPClientManager {
|
|
|
32
32
|
private tools: Map<string, Tool[]> = new Map()
|
|
33
33
|
private reconnectTimers: Map<string, NodeJS.Timeout> = new Map()
|
|
34
34
|
private lastConfigs: Map<string, MCPClientConfig> = new Map()
|
|
35
|
+
private toolDiscoveryListeners: Array<() => void> = []
|
|
36
|
+
private toolUpdateSubscribers: Array<(info: { mcpName: string; tools: Tool[] }) => void> = []
|
|
35
37
|
|
|
36
38
|
/**
|
|
37
39
|
* Initialize MCP clients for available downstream servers
|
|
@@ -118,26 +120,23 @@ export class MCPClientManager {
|
|
|
118
120
|
|
|
119
121
|
// Discover available tools (non-fatal - connection already succeeded)
|
|
120
122
|
try {
|
|
121
|
-
const toolsResult = await client.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (toolsResult && "tools" in toolsResult) {
|
|
130
|
-
this.tools.set(config.name, toolsResult.tools as Tool[])
|
|
131
|
-
console.log(`[MCP Orchestrator] Discovered ${toolsResult.tools.length} tools from ${config.name}`)
|
|
123
|
+
const toolsResult = await client.listTools()
|
|
124
|
+
const discoveredTools = Array.isArray(toolsResult?.tools) ? (toolsResult.tools as Tool[]) : []
|
|
125
|
+
|
|
126
|
+
this.tools.set(config.name, discoveredTools)
|
|
127
|
+
|
|
128
|
+
if (discoveredTools.length > 0) {
|
|
129
|
+
console.log(`[MCP Orchestrator] Discovered ${discoveredTools.length} tools from ${config.name}`)
|
|
130
|
+
this.notifyToolDiscovery()
|
|
132
131
|
} else {
|
|
133
|
-
// Set empty array if no tools found
|
|
134
|
-
this.tools.set(config.name, [])
|
|
135
132
|
console.log(`[MCP Orchestrator] No tools discovered from ${config.name} (will retry on first use)`)
|
|
136
133
|
}
|
|
134
|
+
this.notifyToolsUpdated(config.name)
|
|
137
135
|
} catch (_error) {
|
|
138
136
|
// Tool discovery failed but connection succeeded - tools will be discovered on first use
|
|
139
137
|
this.tools.set(config.name, [])
|
|
140
138
|
console.log(`[MCP Orchestrator] Tool discovery deferred for ${config.name} (will discover on first tool call)`)
|
|
139
|
+
this.notifyToolsUpdated(config.name)
|
|
141
140
|
}
|
|
142
141
|
}
|
|
143
142
|
|
|
@@ -167,16 +166,10 @@ export class MCPClientManager {
|
|
|
167
166
|
}
|
|
168
167
|
|
|
169
168
|
try {
|
|
170
|
-
const result = await client.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
name: toolName,
|
|
175
|
-
arguments: args
|
|
176
|
-
}
|
|
177
|
-
},
|
|
178
|
-
undefined as any // Type inference will work at runtime
|
|
179
|
-
)
|
|
169
|
+
const result = await client.callTool({
|
|
170
|
+
name: toolName,
|
|
171
|
+
arguments: args
|
|
172
|
+
})
|
|
180
173
|
|
|
181
174
|
return result as CallToolResult
|
|
182
175
|
} catch (error) {
|
|
@@ -199,6 +192,66 @@ export class MCPClientManager {
|
|
|
199
192
|
return Array.from(this.clients.keys())
|
|
200
193
|
}
|
|
201
194
|
|
|
195
|
+
/**
|
|
196
|
+
* Subscribe to tool updates for a specific MCP. Returns an unsubscribe function.
|
|
197
|
+
*/
|
|
198
|
+
onToolsUpdated(subscriber: (info: { mcpName: string; tools: Tool[] }) => void): () => void {
|
|
199
|
+
this.toolUpdateSubscribers.push(subscriber)
|
|
200
|
+
return () => {
|
|
201
|
+
this.toolUpdateSubscribers = this.toolUpdateSubscribers.filter((fn) => fn !== subscriber)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Wait for at least one downstream MCP to finish tool discovery, or resolve after a timeout.
|
|
207
|
+
* Prevents registering an empty toolset before downstream servers are ready.
|
|
208
|
+
*/
|
|
209
|
+
async waitForInitialTools(timeoutMs: number = 8000): Promise<void> {
|
|
210
|
+
if (this.getAllTools().length > 0) {
|
|
211
|
+
return
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
await new Promise<void>((resolve) => {
|
|
215
|
+
let timeoutId: NodeJS.Timeout
|
|
216
|
+
|
|
217
|
+
const onDiscovery = () => {
|
|
218
|
+
clearTimeout(timeoutId)
|
|
219
|
+
this.toolDiscoveryListeners = this.toolDiscoveryListeners.filter((listener) => listener !== onDiscovery)
|
|
220
|
+
resolve()
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
timeoutId = setTimeout(() => {
|
|
224
|
+
this.toolDiscoveryListeners = this.toolDiscoveryListeners.filter((listener) => listener !== onDiscovery)
|
|
225
|
+
resolve()
|
|
226
|
+
}, timeoutMs)
|
|
227
|
+
|
|
228
|
+
this.toolDiscoveryListeners.push(onDiscovery)
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private notifyToolDiscovery(): void {
|
|
233
|
+
if (this.toolDiscoveryListeners.length === 0 || this.getAllTools().length === 0) {
|
|
234
|
+
return
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const listeners = [...this.toolDiscoveryListeners]
|
|
238
|
+
this.toolDiscoveryListeners = []
|
|
239
|
+
for (const listener of listeners) {
|
|
240
|
+
listener()
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private notifyToolsUpdated(mcpName: string): void {
|
|
245
|
+
const tools = this.tools.get(mcpName) ?? []
|
|
246
|
+
for (const subscriber of this.toolUpdateSubscribers) {
|
|
247
|
+
try {
|
|
248
|
+
subscriber({ mcpName, tools })
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.warn(`[MCP Orchestrator] Tool update subscriber error for ${mcpName}:`, error)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
202
255
|
/**
|
|
203
256
|
* Schedule reconnection for a disconnected MCP
|
|
204
257
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs"
|
|
2
2
|
import { homedir } from "node:os"
|
|
3
3
|
import { join } from "node:path"
|
|
4
|
+
import type { Tool } from "@modelcontextprotocol/sdk/types.js"
|
|
4
5
|
import { createMcpHandler } from "mcp-handler"
|
|
5
6
|
import { z } from "zod"
|
|
6
7
|
import { getMCPClientManager } from "./client-manager"
|
|
@@ -95,14 +96,39 @@ const initializeOrchestration = async () => {
|
|
|
95
96
|
return config
|
|
96
97
|
}
|
|
97
98
|
|
|
99
|
+
const waitForInitialConfig = async (
|
|
100
|
+
timeoutMs: number = 10000,
|
|
101
|
+
pollIntervalMs: number = 250
|
|
102
|
+
): Promise<{ config: Parameters<typeof clientManager.initialize>[0]; waited: boolean }> => {
|
|
103
|
+
const startTime = Date.now()
|
|
104
|
+
let waited = false
|
|
105
|
+
let config = getConfigFromSessions()
|
|
106
|
+
|
|
107
|
+
while (Object.keys(config).length === 0 && Date.now() - startTime < timeoutMs) {
|
|
108
|
+
if (!waited) {
|
|
109
|
+
console.log("[MCP Orchestrator] Waiting for session info before connecting downstream MCPs...")
|
|
110
|
+
waited = true
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs))
|
|
114
|
+
config = getConfigFromSessions()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return { config, waited }
|
|
118
|
+
}
|
|
119
|
+
|
|
98
120
|
try {
|
|
99
121
|
// Initial attempt to connect
|
|
100
|
-
const config =
|
|
122
|
+
const { config, waited } = await waitForInitialConfig()
|
|
101
123
|
if (Object.keys(config).length > 0) {
|
|
102
124
|
await clientManager.initialize(config)
|
|
103
125
|
console.log(`[MCP Orchestrator] Initialized with ${Object.keys(config).join(", ")}`)
|
|
104
126
|
} else {
|
|
105
|
-
|
|
127
|
+
if (waited) {
|
|
128
|
+
console.log("[MCP Orchestrator] No downstream MCPs detected after waiting for session info (will retry)")
|
|
129
|
+
} else {
|
|
130
|
+
console.log("[MCP Orchestrator] No downstream MCPs found yet (will retry)")
|
|
131
|
+
}
|
|
106
132
|
}
|
|
107
133
|
|
|
108
134
|
// Since MCP server starts before Chrome, periodically retry connection
|
|
@@ -146,29 +172,128 @@ const initializeOrchestration = async () => {
|
|
|
146
172
|
}
|
|
147
173
|
|
|
148
174
|
// Initialize on module load
|
|
149
|
-
initializeOrchestration().catch(
|
|
175
|
+
const orchestrationReady = initializeOrchestration().catch((error) => {
|
|
176
|
+
console.error("[MCP Orchestrator] Failed to initialize downstream MCPs:", error)
|
|
177
|
+
})
|
|
150
178
|
|
|
151
179
|
const handler = createMcpHandler(
|
|
152
|
-
(server) => {
|
|
153
|
-
// Dynamically register proxied tools from downstream MCPs
|
|
180
|
+
async (server) => {
|
|
154
181
|
const clientManager = getMCPClientManager()
|
|
155
|
-
const downstreamTools = clientManager.getAllTools()
|
|
156
182
|
|
|
157
|
-
|
|
158
|
-
|
|
183
|
+
await orchestrationReady
|
|
184
|
+
await clientManager.waitForInitialTools()
|
|
185
|
+
|
|
186
|
+
const registeredProxiedTools = new Map<
|
|
187
|
+
string,
|
|
188
|
+
{
|
|
189
|
+
mcpName: string
|
|
190
|
+
toolName: string
|
|
191
|
+
registered: ReturnType<typeof server.tool>
|
|
192
|
+
}
|
|
193
|
+
>()
|
|
194
|
+
|
|
195
|
+
const registerOrUpdateProxiedTool = (mcpName: string, tool: Tool): boolean => {
|
|
159
196
|
const proxiedToolName = `${mcpName}_${tool.name}`
|
|
197
|
+
const existing = registeredProxiedTools.get(proxiedToolName)
|
|
198
|
+
const description = `[${mcpName}] ${tool.description || ""}`
|
|
199
|
+
const annotations = {
|
|
200
|
+
...(tool.annotations ?? {}),
|
|
201
|
+
proxiedFrom: mcpName,
|
|
202
|
+
originalInputSchema: tool.inputSchema
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (existing) {
|
|
206
|
+
existing.registered.update({
|
|
207
|
+
description,
|
|
208
|
+
annotations
|
|
209
|
+
})
|
|
210
|
+
return false
|
|
211
|
+
}
|
|
160
212
|
|
|
161
|
-
|
|
162
|
-
proxiedToolName,
|
|
163
|
-
`[${mcpName}] ${tool.description || ""}`,
|
|
164
|
-
tool.inputSchema as any,
|
|
165
|
-
async (params: Record<string, unknown>) => {
|
|
166
|
-
// Proxy the call to the downstream MCP
|
|
213
|
+
try {
|
|
214
|
+
const proxiedTool = server.tool(proxiedToolName, description, {}, async (params: Record<string, unknown>) => {
|
|
167
215
|
return clientManager.callTool(mcpName, tool.name, params)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
// Allow arbitrary argument objects to pass through to downstream MCPs
|
|
219
|
+
proxiedTool.inputSchema = z.object({}).passthrough()
|
|
220
|
+
|
|
221
|
+
proxiedTool.update({
|
|
222
|
+
annotations
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
registeredProxiedTools.set(proxiedToolName, {
|
|
226
|
+
mcpName,
|
|
227
|
+
toolName: tool.name,
|
|
228
|
+
registered: proxiedTool
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
console.log(`[MCP Orchestrator] Registered proxied tool ${proxiedToolName}`)
|
|
232
|
+
return true
|
|
233
|
+
} catch (error) {
|
|
234
|
+
console.warn(`[MCP Orchestrator] Failed to register proxied tool ${proxiedToolName}:`, error)
|
|
235
|
+
return false
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const removeToolsForMcp = (mcpName: string): number => {
|
|
240
|
+
let removed = 0
|
|
241
|
+
for (const [proxiedToolName, entry] of registeredProxiedTools.entries()) {
|
|
242
|
+
if (entry.mcpName === mcpName) {
|
|
243
|
+
try {
|
|
244
|
+
entry.registered.remove()
|
|
245
|
+
registeredProxiedTools.delete(proxiedToolName)
|
|
246
|
+
removed++
|
|
247
|
+
console.log(`[MCP Orchestrator] Removed proxied tool ${proxiedToolName}`)
|
|
248
|
+
} catch (error) {
|
|
249
|
+
console.warn(`[MCP Orchestrator] Failed to remove proxied tool ${proxiedToolName}:`, error)
|
|
250
|
+
}
|
|
168
251
|
}
|
|
169
|
-
|
|
252
|
+
}
|
|
253
|
+
return removed
|
|
170
254
|
}
|
|
171
255
|
|
|
256
|
+
// Dynamically register proxied tools from downstream MCPs
|
|
257
|
+
const downstreamTools = clientManager.getAllTools()
|
|
258
|
+
|
|
259
|
+
if (downstreamTools.length === 0) {
|
|
260
|
+
console.log("[MCP Orchestrator] No downstream MCP tools available during initial registration")
|
|
261
|
+
} else {
|
|
262
|
+
console.log(`[MCP Orchestrator] Registering ${downstreamTools.length} downstream MCP tools`)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
let initialNewTools = 0
|
|
266
|
+
for (const { mcpName, tool } of downstreamTools) {
|
|
267
|
+
if (registerOrUpdateProxiedTool(mcpName, tool)) {
|
|
268
|
+
initialNewTools++
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (initialNewTools > 0) {
|
|
273
|
+
server.sendToolListChanged()
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
clientManager.onToolsUpdated(({ mcpName, tools }) => {
|
|
277
|
+
if (tools.length === 0) {
|
|
278
|
+
const removed = removeToolsForMcp(mcpName)
|
|
279
|
+
if (removed > 0) {
|
|
280
|
+
server.sendToolListChanged()
|
|
281
|
+
}
|
|
282
|
+
return
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
let addedOrUpdated = 0
|
|
286
|
+
for (const tool of tools) {
|
|
287
|
+
if (registerOrUpdateProxiedTool(mcpName, tool)) {
|
|
288
|
+
addedOrUpdated++
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (addedOrUpdated > 0) {
|
|
293
|
+
server.sendToolListChanged()
|
|
294
|
+
}
|
|
295
|
+
})
|
|
296
|
+
|
|
172
297
|
// Dev3000's own tools below:
|
|
173
298
|
// Enhanced fix_my_app - the ultimate error fixing tool
|
|
174
299
|
server.tool(
|
package/package.json
CHANGED
|
@@ -20,6 +20,8 @@ interface LogEntry {
|
|
|
20
20
|
content: string
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
const NEXTJS_MCP_404_REGEX = /(?:\[POST\]|POST)\s+\/_next\/mcp\b[^\n]*\b404\b/i
|
|
24
|
+
|
|
23
25
|
// Compact ASCII logo for very small terminals
|
|
24
26
|
const COMPACT_LOGO = "d3k"
|
|
25
27
|
|
|
@@ -148,6 +150,10 @@ const TUIApp = ({
|
|
|
148
150
|
let buffer = ""
|
|
149
151
|
|
|
150
152
|
const appendLog = (line: string) => {
|
|
153
|
+
if (NEXTJS_MCP_404_REGEX.test(line)) {
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
|
|
151
157
|
const newLog: LogEntry = {
|
|
152
158
|
id: logIdCounter.current++,
|
|
153
159
|
content: line
|
/package/mcp-server/.next/static/{_qXVPjMKmdbmLNrnd_3zK → ZeZ7S325drnA0SH-odcGY}/_buildManifest.js
RENAMED
|
File without changes
|
|
File without changes
|
/package/mcp-server/.next/static/{_qXVPjMKmdbmLNrnd_3zK → ZeZ7S325drnA0SH-odcGY}/_ssgManifest.js
RENAMED
|
File without changes
|