nitrostack 1.0.14 → 1.0.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +29 -295
  2. package/README.md +9 -1
  3. package/dist/cli/commands/dev.d.ts.map +1 -1
  4. package/dist/cli/commands/dev.js +2 -1
  5. package/dist/cli/commands/dev.js.map +1 -1
  6. package/dist/cli/mcp-dev-wrapper.js +30 -17
  7. package/dist/cli/mcp-dev-wrapper.js.map +1 -1
  8. package/dist/core/transports/http-server.d.ts.map +1 -1
  9. package/dist/core/transports/http-server.js +21 -1
  10. package/dist/core/transports/http-server.js.map +1 -1
  11. package/package.json +8 -6
  12. package/src/studio/app/api/chat/route.ts +12 -1
  13. package/src/studio/app/api/init/route.ts +28 -4
  14. package/src/studio/app/auth/page.tsx +13 -9
  15. package/src/studio/app/chat/page.tsx +544 -133
  16. package/src/studio/app/health/page.tsx +101 -99
  17. package/src/studio/app/layout.tsx +23 -3
  18. package/src/studio/app/page.tsx +61 -56
  19. package/src/studio/app/ping/page.tsx +13 -8
  20. package/src/studio/app/prompts/page.tsx +72 -70
  21. package/src/studio/app/resources/page.tsx +88 -86
  22. package/src/studio/app/settings/page.tsx +270 -0
  23. package/src/studio/components/Sidebar.tsx +197 -35
  24. package/src/studio/lib/http-client-transport.ts +222 -0
  25. package/src/studio/lib/llm-service.ts +97 -0
  26. package/src/studio/lib/log-manager.ts +76 -0
  27. package/src/studio/lib/mcp-client.ts +103 -13
  28. package/src/studio/package-lock.json +622 -189
  29. package/src/studio/package.json +1 -0
  30. package/templates/typescript-auth/README.md +3 -1
  31. package/templates/typescript-auth/src/db/database.ts +5 -8
  32. package/templates/typescript-auth/src/index.ts +13 -2
  33. package/templates/typescript-auth/src/modules/addresses/addresses.tools.ts +49 -6
  34. package/templates/typescript-auth/src/modules/cart/cart.tools.ts +13 -17
  35. package/templates/typescript-auth/src/modules/orders/orders.tools.ts +38 -16
  36. package/templates/typescript-auth/src/modules/products/products.tools.ts +4 -4
  37. package/templates/typescript-auth/src/widgets/app/order-confirmation/page.tsx +25 -0
  38. package/templates/typescript-auth/src/widgets/app/products-grid/page.tsx +26 -1
  39. package/templates/typescript-auth-api-key/README.md +3 -1
  40. package/templates/typescript-auth-api-key/src/index.ts +11 -3
  41. package/templates/typescript-starter/README.md +3 -1
  42. package/templates/typescript-auth-api-key/.env +0 -15
  43. package/templates/typescript-auth-api-key/package-lock.json +0 -124
@@ -0,0 +1,76 @@
1
+ // Log Manager for MCP Server
2
+ // Captures stdout/stderr from MCP server process and streams to clients
3
+
4
+ export interface LogEntry {
5
+ timestamp: number;
6
+ level: 'info' | 'error' | 'warn' | 'debug';
7
+ message: string;
8
+ source: 'stdout' | 'stderr' | 'system';
9
+ }
10
+
11
+ class LogManager {
12
+ private logs: LogEntry[] = [];
13
+ private maxLogs = 1000; // Keep last 1000 logs in memory
14
+ private listeners: Set<(log: LogEntry) => void> = new Set();
15
+
16
+ addLog(message: string, level: LogEntry['level'] = 'info', source: LogEntry['source'] = 'system') {
17
+ const entry: LogEntry = {
18
+ timestamp: Date.now(),
19
+ level,
20
+ message: message.trim(),
21
+ source,
22
+ };
23
+
24
+ this.logs.push(entry);
25
+
26
+ // Keep only last maxLogs entries
27
+ if (this.logs.length > this.maxLogs) {
28
+ this.logs = this.logs.slice(-this.maxLogs);
29
+ }
30
+
31
+ // Notify all listeners
32
+ this.listeners.forEach(listener => {
33
+ try {
34
+ listener(entry);
35
+ } catch (error) {
36
+ console.error('Error in log listener:', error);
37
+ }
38
+ });
39
+ }
40
+
41
+ getLogs(limit?: number): LogEntry[] {
42
+ if (limit) {
43
+ return this.logs.slice(-limit);
44
+ }
45
+ return [...this.logs];
46
+ }
47
+
48
+ clearLogs() {
49
+ this.logs = [];
50
+ this.addLog('Logs cleared', 'info', 'system');
51
+ }
52
+
53
+ subscribe(listener: (log: LogEntry) => void): () => void {
54
+ this.listeners.add(listener);
55
+ return () => {
56
+ this.listeners.delete(listener);
57
+ };
58
+ }
59
+
60
+ getLogCount(): number {
61
+ return this.logs.length;
62
+ }
63
+ }
64
+
65
+ // Global singleton
66
+ declare global {
67
+ var __logManager: LogManager | undefined;
68
+ }
69
+
70
+ export function getLogManager(): LogManager {
71
+ if (!global.__logManager) {
72
+ global.__logManager = new LogManager();
73
+ }
74
+ return global.__logManager;
75
+ }
76
+
@@ -1,20 +1,36 @@
1
1
  // MCP Client for Studio
2
- // Communicates with MCP server via stdio
2
+ // Communicates with MCP server via stdio or HTTP
3
3
 
4
4
  import { spawn, ChildProcess } from 'child_process';
5
5
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
6
6
  import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
7
+ import { HttpClientTransport } from '@/lib/http-client-transport';
8
+ import { getLogManager } from '@/lib/log-manager';
7
9
 
8
- export interface McpClientConfig {
10
+ export type TransportType = 'stdio' | 'http';
11
+
12
+ export interface StdioClientConfig {
13
+ type: 'stdio';
9
14
  command: string;
10
15
  args: string[];
11
16
  env?: Record<string, string>;
12
17
  cwd?: string;
13
18
  }
14
19
 
20
+ export interface HttpClientConfig {
21
+ type: 'http';
22
+ baseUrl: string;
23
+ basePath?: string;
24
+ headers?: Record<string, string>;
25
+ }
26
+
27
+ export type McpClientConfig = StdioClientConfig | HttpClientConfig;
28
+
15
29
  export class McpClient {
16
30
  private client: Client | null = null;
17
- private transport: StdioClientTransport | null = null;
31
+ private transport: StdioClientTransport | HttpClientTransport | null = null;
32
+ private transportType: TransportType | null = null;
33
+ private childProcess: ChildProcess | null = null;
18
34
 
19
35
  async connect(config: McpClientConfig): Promise<void> {
20
36
  if (this.client && this.transport) {
@@ -35,16 +51,73 @@ export class McpClient {
35
51
  this.transport = null;
36
52
  }
37
53
 
38
- console.log('🚀 Connecting to MCP server:', config.command, config.args);
39
- console.log('📝 Environment vars:', Object.keys(config.env || {}).join(', '));
54
+ this.transportType = config.type;
55
+
56
+ // Create appropriate transport based on type
57
+ if (config.type === 'stdio') {
58
+ console.log('🚀 Connecting to MCP server via STDIO:', config.command, config.args);
59
+ console.log('📝 Environment vars:', Object.keys(config.env || {}).join(', '));
60
+
61
+ const logManager = getLogManager();
62
+ logManager.addLog(`Starting MCP server: ${config.command} ${config.args.join(' ')}`, 'info', 'system');
63
+
64
+ // Create STDIO transport (it will spawn the process internally)
65
+ this.transport = new StdioClientTransport({
66
+ command: config.command,
67
+ args: config.args,
68
+ env: { ...process.env, ...config.env },
69
+ cwd: config.cwd,
70
+ });
71
+
72
+ // Access the child process from the transport to capture logs
73
+ // The transport stores the process in a private property, so we need to access it
74
+ setTimeout(() => {
75
+ const transport = this.transport as any;
76
+ if (transport._process || transport.process) {
77
+ this.childProcess = transport._process || transport.process;
78
+
79
+ // Capture stdout
80
+ this.childProcess?.stdout?.on('data', (data: Buffer) => {
81
+ const message = data.toString();
82
+ logManager.addLog(message, 'info', 'stdout');
83
+ });
84
+
85
+ // Capture stderr
86
+ this.childProcess?.stderr?.on('data', (data: Buffer) => {
87
+ const message = data.toString();
88
+ logManager.addLog(message, 'error', 'stderr');
89
+ });
90
+
91
+ // Handle process errors
92
+ this.childProcess?.on('error', (error: Error) => {
93
+ logManager.addLog(`Process error: ${error.message}`, 'error', 'system');
94
+ });
40
95
 
41
- // Create transport (it will spawn the process internally)
42
- this.transport = new StdioClientTransport({
43
- command: config.command,
44
- args: config.args,
45
- env: { ...process.env, ...config.env },
46
- cwd: config.cwd, // Set working directory for the child process
47
- });
96
+ // Handle process exit
97
+ this.childProcess?.on('exit', (code: number | null, signal: string | null) => {
98
+ if (code !== null) {
99
+ logManager.addLog(`MCP server exited with code ${code}`, code === 0 ? 'info' : 'error', 'system');
100
+ } else if (signal) {
101
+ logManager.addLog(`MCP server killed with signal ${signal}`, 'warn', 'system');
102
+ }
103
+ });
104
+ }
105
+ }, 100); // Small delay to ensure transport has spawned the process
106
+ } else if (config.type === 'http') {
107
+ console.log('🌐 Connecting to MCP server via HTTP:', config.baseUrl);
108
+
109
+ // Create HTTP transport
110
+ this.transport = new HttpClientTransport({
111
+ baseUrl: config.baseUrl,
112
+ basePath: config.basePath,
113
+ headers: config.headers,
114
+ });
115
+
116
+ // Start the HTTP transport (establish SSE connection)
117
+ await this.transport.start();
118
+ } else {
119
+ throw new Error(`Unknown transport type: ${(config as any).type}`);
120
+ }
48
121
 
49
122
  // Create client
50
123
  this.client = new Client(
@@ -65,22 +138,25 @@ export class McpClient {
65
138
  console.error('❌ MCP Client error:', error);
66
139
  this.client = null;
67
140
  this.transport = null;
141
+ this.transportType = null;
68
142
  };
69
143
 
70
144
  this.client.onclose = () => {
71
145
  console.log('🔌 MCP Client connection closed');
72
146
  this.client = null;
73
147
  this.transport = null;
148
+ this.transportType = null;
74
149
  };
75
150
 
76
151
  // Connect
77
152
  try {
78
153
  await this.client.connect(this.transport);
79
- console.log('✅ MCP Client connected and ready');
154
+ console.log(`✅ MCP Client connected and ready (${config.type.toUpperCase()} transport)`);
80
155
  } catch (error) {
81
156
  console.error('❌ Failed to connect MCP client:', error);
82
157
  this.client = null;
83
158
  this.transport = null;
159
+ this.transportType = null;
84
160
  throw error;
85
161
  }
86
162
  }
@@ -88,6 +164,8 @@ export class McpClient {
88
164
  async disconnect(): Promise<void> {
89
165
  console.log('🛑 Disconnecting MCP client...');
90
166
 
167
+ const logManager = getLogManager();
168
+
91
169
  if (this.client) {
92
170
  await this.client.close();
93
171
  this.client = null;
@@ -97,12 +175,24 @@ export class McpClient {
97
175
  await this.transport.close();
98
176
  this.transport = null;
99
177
  }
178
+
179
+ if (this.childProcess) {
180
+ logManager.addLog('Stopping MCP server process', 'info', 'system');
181
+ this.childProcess.kill('SIGTERM');
182
+ this.childProcess = null;
183
+ }
184
+
185
+ this.transportType = null;
100
186
  }
101
187
 
102
188
  isConnected(): boolean {
103
189
  return this.client !== null;
104
190
  }
105
191
 
192
+ getTransportType(): TransportType | null {
193
+ return this.transportType;
194
+ }
195
+
106
196
  async listTools() {
107
197
  if (!this.client) throw new Error('Not connected');
108
198
  return await this.client.listTools();