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.
Files changed (51) hide show
  1. package/dist/dev-environment.d.ts.map +1 -1
  2. package/dist/dev-environment.js +20 -12
  3. package/dist/dev-environment.js.map +1 -1
  4. package/dist/src/tui-interface-impl.tsx +6 -0
  5. package/dist/tui-interface-impl.d.ts.map +1 -1
  6. package/dist/tui-interface-impl.js +4 -0
  7. package/dist/tui-interface-impl.js.map +1 -1
  8. package/mcp-server/.next/BUILD_ID +1 -1
  9. package/mcp-server/.next/build-manifest.json +2 -2
  10. package/mcp-server/.next/fallback-build-manifest.json +2 -2
  11. package/mcp-server/.next/next-minimal-server.js.nft.json +1 -1
  12. package/mcp-server/.next/next-server.js.nft.json +1 -1
  13. package/mcp-server/.next/prerender-manifest.json +3 -3
  14. package/mcp-server/.next/server/app/_global-error/page.js.nft.json +1 -1
  15. package/mcp-server/.next/server/app/_global-error.html +2 -2
  16. package/mcp-server/.next/server/app/_global-error.rsc +1 -1
  17. package/mcp-server/.next/server/app/_not-found/page.js.nft.json +1 -1
  18. package/mcp-server/.next/server/app/_not-found.html +1 -1
  19. package/mcp-server/.next/server/app/_not-found.rsc +1 -1
  20. package/mcp-server/.next/server/app/api/jank/[session]/route.js.nft.json +1 -1
  21. package/mcp-server/.next/server/app/api/logs/append/route.js.nft.json +1 -1
  22. package/mcp-server/.next/server/app/api/logs/head/route.js.nft.json +1 -1
  23. package/mcp-server/.next/server/app/api/logs/list/route.js.nft.json +1 -1
  24. package/mcp-server/.next/server/app/api/logs/rotate/route.js.nft.json +1 -1
  25. package/mcp-server/.next/server/app/api/logs/stream/route.js.nft.json +1 -1
  26. package/mcp-server/.next/server/app/api/logs/tail/route.js.nft.json +1 -1
  27. package/mcp-server/.next/server/app/api/orchestrator/route.js.nft.json +1 -1
  28. package/mcp-server/.next/server/app/api/screenshots/[filename]/route.js.nft.json +1 -1
  29. package/mcp-server/.next/server/app/api/screenshots/list/route.js.nft.json +1 -1
  30. package/mcp-server/.next/server/app/api/tools/route.js.nft.json +1 -1
  31. package/mcp-server/.next/server/app/index.html +1 -1
  32. package/mcp-server/.next/server/app/index.rsc +1 -1
  33. package/mcp-server/.next/server/app/logs/page.js.nft.json +1 -1
  34. package/mcp-server/.next/server/app/mcp/route.js.nft.json +1 -1
  35. package/mcp-server/.next/server/app/page.js.nft.json +1 -1
  36. package/mcp-server/.next/server/app/video/[session]/page.js.nft.json +1 -1
  37. package/mcp-server/.next/server/chunks/[root-of-the-server]__94037b23._.js +2 -2
  38. package/mcp-server/.next/server/chunks/[root-of-the-server]__94037b23._.js.map +1 -1
  39. package/mcp-server/.next/server/chunks/[root-of-the-server]__cc74dbef._.js +2 -2
  40. package/mcp-server/.next/server/chunks/[root-of-the-server]__cc74dbef._.js.map +1 -1
  41. package/mcp-server/.next/server/chunks/[root-of-the-server]__dc0b0022._.js +1 -1
  42. package/mcp-server/.next/server/chunks/[root-of-the-server]__dc0b0022._.js.map +1 -1
  43. package/mcp-server/.next/server/server-reference-manifest.js +1 -1
  44. package/mcp-server/.next/server/server-reference-manifest.json +1 -1
  45. package/mcp-server/app/mcp/client-manager.ts +76 -23
  46. package/mcp-server/app/mcp/route.ts +140 -15
  47. package/package.json +1 -1
  48. package/src/tui-interface-impl.tsx +6 -0
  49. /package/mcp-server/.next/static/{_qXVPjMKmdbmLNrnd_3zK → ZeZ7S325drnA0SH-odcGY}/_buildManifest.js +0 -0
  50. /package/mcp-server/.next/static/{_qXVPjMKmdbmLNrnd_3zK → ZeZ7S325drnA0SH-odcGY}/_clientMiddlewareManifest.json +0 -0
  51. /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\": \"lZgibe2ziYqyEAkZ7RFH62f938aB/wxVlceTcZMnbR8=\"\n}"
1
+ self.__RSC_SERVER_MANIFEST="{\n \"node\": {},\n \"edge\": {},\n \"encryptionKey\": \"Az3Ay/ZpgjzPNsVoQzRDLlh6S8/dSjBuLozeWfes2Dc=\"\n}"
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "node": {},
3
3
  "edge": {},
4
- "encryptionKey": "lZgibe2ziYqyEAkZ7RFH62f938aB/wxVlceTcZMnbR8="
4
+ "encryptionKey": "Az3Ay/ZpgjzPNsVoQzRDLlh6S8/dSjBuLozeWfes2Dc="
5
5
  }
@@ -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.request(
122
- {
123
- method: "tools/list",
124
- params: {}
125
- },
126
- undefined as any // Type inference will work at runtime
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.request(
171
- {
172
- method: "tools/call",
173
- params: {
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 = getConfigFromSessions()
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
- console.log("[MCP Orchestrator] No downstream MCPs found yet (will retry)")
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(console.error)
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
- for (const { mcpName, tool } of downstreamTools) {
158
- // Add prefix to avoid conflicts with dev3000's own tools
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
- server.tool(
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dev3000",
3
- "version": "0.0.92",
3
+ "version": "0.0.94",
4
4
  "description": "AI-powered development tools with browser monitoring and MCP server integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -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