nitrostack 1.0.57 → 1.0.59

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 (65) hide show
  1. package/dist/widgets/index.d.ts +4 -4
  2. package/dist/widgets/index.d.ts.map +1 -1
  3. package/dist/widgets/index.js +3 -3
  4. package/dist/widgets/index.js.map +1 -1
  5. package/package.json +8 -2
  6. package/ARCHITECTURE.md +0 -302
  7. package/CHANGELOG.md +0 -49
  8. package/CONTRIBUTING.md +0 -182
  9. package/NOTICE +0 -153
  10. package/jest.config.js +0 -21
  11. package/src/assets/nitrocloud.png +0 -0
  12. package/src/studio/README.md +0 -140
  13. package/src/studio/app/api/auth/fetch-metadata/route.ts +0 -71
  14. package/src/studio/app/api/auth/register-client/route.ts +0 -67
  15. package/src/studio/app/api/chat/route.ts +0 -250
  16. package/src/studio/app/api/health/checks/route.ts +0 -42
  17. package/src/studio/app/api/health/route.ts +0 -13
  18. package/src/studio/app/api/init/route.ts +0 -109
  19. package/src/studio/app/api/ping/route.ts +0 -13
  20. package/src/studio/app/api/prompts/[name]/route.ts +0 -21
  21. package/src/studio/app/api/prompts/route.ts +0 -13
  22. package/src/studio/app/api/resources/[...uri]/route.ts +0 -18
  23. package/src/studio/app/api/resources/route.ts +0 -13
  24. package/src/studio/app/api/roots/route.ts +0 -13
  25. package/src/studio/app/api/sampling/route.ts +0 -14
  26. package/src/studio/app/api/tools/[name]/call/route.ts +0 -41
  27. package/src/studio/app/api/tools/route.ts +0 -23
  28. package/src/studio/app/api/widget-examples/route.ts +0 -44
  29. package/src/studio/app/auth/callback/page.tsx +0 -175
  30. package/src/studio/app/auth/page.tsx +0 -560
  31. package/src/studio/app/chat/page.tsx +0 -1133
  32. package/src/studio/app/chat/page.tsx.backup +0 -390
  33. package/src/studio/app/globals.css +0 -486
  34. package/src/studio/app/health/page.tsx +0 -179
  35. package/src/studio/app/layout.tsx +0 -68
  36. package/src/studio/app/logs/page.tsx +0 -279
  37. package/src/studio/app/page.tsx +0 -351
  38. package/src/studio/app/page.tsx.backup +0 -346
  39. package/src/studio/app/ping/page.tsx +0 -209
  40. package/src/studio/app/prompts/page.tsx +0 -230
  41. package/src/studio/app/resources/page.tsx +0 -315
  42. package/src/studio/app/settings/page.tsx +0 -199
  43. package/src/studio/branding.md +0 -807
  44. package/src/studio/components/EnlargeModal.tsx +0 -138
  45. package/src/studio/components/LogMessage.tsx +0 -153
  46. package/src/studio/components/MarkdownRenderer.tsx +0 -410
  47. package/src/studio/components/Sidebar.tsx +0 -295
  48. package/src/studio/components/ToolCard.tsx +0 -139
  49. package/src/studio/components/WidgetRenderer.tsx +0 -346
  50. package/src/studio/lib/api.ts +0 -207
  51. package/src/studio/lib/http-client-transport.ts +0 -222
  52. package/src/studio/lib/llm-service.ts +0 -480
  53. package/src/studio/lib/log-manager.ts +0 -76
  54. package/src/studio/lib/mcp-client.ts +0 -258
  55. package/src/studio/lib/store.ts +0 -192
  56. package/src/studio/lib/theme-provider.tsx +0 -50
  57. package/src/studio/lib/types.ts +0 -107
  58. package/src/studio/lib/widget-loader.ts +0 -90
  59. package/src/studio/middleware.ts +0 -27
  60. package/src/studio/next.config.js +0 -38
  61. package/src/studio/package.json +0 -35
  62. package/src/studio/postcss.config.mjs +0 -10
  63. package/src/studio/public/nitrocloud.png +0 -0
  64. package/src/studio/tailwind.config.ts +0 -67
  65. package/src/studio/tsconfig.json +0 -42
package/NOTICE DELETED
@@ -1,153 +0,0 @@
1
- NitroStack
2
- Copyright 2025 Abhishek Pandit
3
-
4
- This product includes software developed by Abhishek Pandit for NitroStack (https://nitrostack.vercel.app/).
5
-
6
- This project contains code and dependencies from various open source projects:
7
-
8
- ================================================================================
9
-
10
- @modelcontextprotocol/sdk
11
- Copyright (c) Anthropic PBC
12
- Licensed under the MIT License
13
-
14
- @google/generative-ai
15
- Copyright (c) Google LLC
16
- Licensed under the Apache License, Version 2.0
17
-
18
- express
19
- Copyright (c) 2009-2014 TJ Holowaychuk <tj@vision-media.ca>
20
- Copyright (c) 2013-2014 Roman Shtylman <shtylman+expressjs@gmail.com>
21
- Copyright (c) 2014-2015 Douglas Christopher Wilson <doug@somethingdoug.com>
22
- Licensed under the MIT License
23
-
24
- zod
25
- Copyright (c) 2020 Colin McDonnell
26
- Licensed under the MIT License
27
-
28
- reflect-metadata
29
- Copyright (c) Microsoft Corporation
30
- Licensed under the Apache License, Version 2.0
31
-
32
- winston
33
- Copyright (c) 2010 Charlie Robbins
34
- Licensed under the MIT License
35
-
36
- jsonwebtoken
37
- Copyright (c) 2015 Auth0, Inc. <support@auth0.com> (http://auth0.com)
38
- Licensed under the MIT License
39
-
40
- jose
41
- Copyright (c) 2020 Filip Skokan
42
- Licensed under the MIT License
43
-
44
- better-sqlite3
45
- Copyright (c) 2016 Joshua Wise
46
- Licensed under the MIT License
47
-
48
- bcryptjs
49
- Copyright (c) 2012 Nevins Bartolomeo
50
- Licensed under the MIT License
51
-
52
- inquirer
53
- Copyright (c) 2012 Simon Boudrias
54
- Licensed under the MIT License
55
-
56
- commander
57
- Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
58
- Licensed under the MIT License
59
-
60
- chokidar
61
- Copyright (c) 2012-2019 Paul Miller (https://paulmillr.com)
62
- Copyright (c) 2014-2019 Elan Shanker
63
- Licensed under the MIT License
64
-
65
- openai
66
- Copyright (c) OpenAI
67
- Licensed under the Apache License, Version 2.0
68
-
69
- fs-extra
70
- Copyright (c) 2011-2017 JP Richardson
71
- Licensed under the MIT License
72
-
73
- dotenv
74
- Copyright (c) 2015, Scott Motte
75
- Licensed under the BSD 2-Clause License
76
-
77
- ora
78
- Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
79
- Licensed under the MIT License
80
-
81
- open
82
- Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
83
- Licensed under the MIT License
84
-
85
- uuid
86
- Copyright (c) 2010-2020 Robert Kieffer and other contributors
87
- Licensed under the MIT License
88
-
89
- ws
90
- Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
91
- Licensed under the MIT License
92
-
93
- cors
94
- Copyright (c) 2013 Troy Goode <troygoode@gmail.com>
95
- Licensed under the MIT License
96
-
97
- http-proxy-middleware
98
- Copyright (c) 2015 Steven Chim
99
- Licensed under the MIT License
100
-
101
- zod-to-json-schema
102
- Copyright (c) 2021 Stefan Terdell
103
- Licensed under the ISC License
104
-
105
- ================================================================================
106
-
107
- For the full license texts of these dependencies, please refer to their
108
- respective package directories in node_modules or their source repositories.
109
-
110
- ================================================================================
111
-
112
- The HyperMCP project also includes documentation and examples that reference
113
- various standards and specifications:
114
-
115
- - Model Context Protocol (MCP) Specification
116
- https://modelcontextprotocol.io/
117
-
118
- - OAuth 2.1 (draft-ietf-oauth-v2-1-13)
119
- https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13
120
-
121
- - RFC 9728 - OAuth 2.0 Protected Resource Metadata
122
- https://datatracker.ietf.org/doc/html/rfc9728
123
-
124
- - RFC 8707 - Resource Indicators for OAuth 2.0
125
- https://datatracker.ietf.org/doc/html/rfc8707
126
-
127
- - RFC 7662 - OAuth 2.0 Token Introspection
128
- https://datatracker.ietf.org/doc/html/rfc7662
129
-
130
- - RFC 8414 - OAuth 2.0 Authorization Server Metadata
131
- https://datatracker.ietf.org/doc/html/rfc8414
132
-
133
- - RFC 7591 - OAuth 2.0 Dynamic Client Registration Protocol
134
- https://datatracker.ietf.org/doc/html/rfc7591
135
-
136
- - RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients (PKCE)
137
- https://datatracker.ietf.org/doc/html/rfc7636
138
-
139
- ================================================================================
140
-
141
- TRADEMARKS
142
-
143
- NitroStack, the NitroStack logo, and related marks are trademarks of the NitroStack
144
- project. Use of these trademarks is subject to the trademark policy available
145
- at https://nitrostack.vercel.app/.
146
-
147
- ================================================================================
148
-
149
- For more information, visit:
150
- - Website: https://nitrostack.vercel.app/
151
- - Documentation: https://nitrostack-docs.vercel.app/
152
- - GitHub: https://github.com/abhishekpanditofficial/nitrostack
153
-
package/jest.config.js DELETED
@@ -1,21 +0,0 @@
1
- module.exports = {
2
- preset: 'ts-jest',
3
- testEnvironment: 'node',
4
- roots: ['<rootDir>/src'],
5
- testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
6
- transform: {
7
- '^.+\\.ts$': 'ts-jest',
8
- },
9
- collectCoverageFrom: [
10
- 'src/**/*.ts',
11
- '!src/**/*.d.ts',
12
- '!src/**/*.test.ts',
13
- '!src/**/*.spec.ts',
14
- ],
15
- coverageDirectory: 'coverage',
16
- coverageReporters: ['text', 'lcov', 'html'],
17
- moduleFileExtensions: ['ts', 'js', 'json'],
18
- verbose: true,
19
- };
20
-
21
-
Binary file
@@ -1,140 +0,0 @@
1
- # NitroStack Studio 🎨
2
-
3
- Modern, React-based visual inspector for MCP servers.
4
-
5
- ## Features
6
-
7
- - ⚡ **Tools Browser** - Execute tools with auto-generated forms
8
- - 🎨 **Widget Rendering** - Preview UI components in dev and prod
9
- - 🤖 **AI Chat** - Chat with tool integration (OpenAI/Gemini)
10
- - 📦 **Resources** - Browse MCP resources and schemas
11
- - 💬 **Prompts** - Execute MCP prompts
12
- - 🔐 **Auth** - OAuth 2.1, JWT, and API key support
13
- - 💚 **Health** - System health monitoring
14
- - 📡 **Ping** - Latency testing
15
- - 🎲 **Sampling** - Text completion testing
16
- - 🌳 **Roots** - MCP server roots
17
-
18
- ## Architecture
19
-
20
- ```
21
- src/studio/
22
- ├── app/ # Next.js App Router pages
23
- │ ├── page.tsx # Tools (default)
24
- │ ├── chat/ # AI Chat
25
- │ ├── resources/ # Resources browser
26
- │ ├── prompts/ # Prompts executor
27
- │ ├── auth/ # Authentication
28
- │ ├── health/ # Health checks
29
- │ ├── ping/ # Ping testing
30
- │ ├── sampling/ # Sampling API
31
- │ └── roots/ # Roots browser
32
- ├── components/ # Reusable React components
33
- │ ├── Sidebar.tsx # Navigation sidebar
34
- │ ├── ToolCard.tsx # Tool display card
35
- │ ├── WidgetRenderer.tsx # Widget iframe loader
36
- │ └── EnlargeModal.tsx # Full-screen widget viewer
37
- ├── lib/ # Utilities and shared logic
38
- │ ├── api.ts # API client
39
- │ ├── store.ts # Zustand state management
40
- │ ├── types.ts # TypeScript types
41
- │ ├── widget-loader.ts # Dev/prod widget loading
42
- │ └── dummy-data.ts # Preview data generator
43
- └── next.config.ts # Next.js configuration
44
-
45
- ## Development
46
-
47
- ```bash
48
- # Install dependencies
49
- cd src/studio
50
- npm install
51
-
52
- # Run dev server
53
- npm run dev
54
-
55
- # Build static export
56
- npm run build
57
- ```
58
-
59
- ## Widget Rendering
60
-
61
- The Studio supports two modes:
62
-
63
- ### Dev Mode (Port 3001)
64
- - Widgets load from Next.js dev server
65
- - Hot reload enabled
66
- - URL: `http://localhost:3001/widget-name`
67
-
68
- ### Production Mode
69
- - Widgets compile to static HTML
70
- - Bundled with server
71
- - Served from `/widgets/*`
72
-
73
- ## Static Export
74
-
75
- Studio builds to static HTML/CSS/JS for easy deployment:
76
-
77
- ```bash
78
- npm run build
79
- # Output: ../../dist/studio/
80
- ```
81
-
82
- Deploy to:
83
- - Vercel
84
- - Netlify
85
- - Cloudflare Pages
86
- - GitHub Pages
87
- - S3 + CloudFront
88
- - Any static hosting
89
-
90
- ## State Management
91
-
92
- Uses Zustand for global state:
93
- - Connection status
94
- - Tools, resources, prompts
95
- - Chat messages
96
- - Auth tokens
97
- - Modal state
98
-
99
- ## API Integration
100
-
101
- All API calls go through the unified `StudioAPI` client in `lib/api.ts`:
102
- - Tools: `api.getTools()`, `api.callTool()`
103
- - Resources: `api.getResources()`, `api.getResource()`
104
- - Chat: `api.chat()`
105
- - Auth: `api.discoverAuth()`, `api.registerClient()`
106
- - Health: `api.getHealth()`
107
-
108
- ## Styling
109
-
110
- - **Tailwind CSS** - Utility-first styling
111
- - **Dark Theme** - Optimized for developer experience
112
- - **Custom CSS** - Global styles in `app/globals.css`
113
- - **Animations** - Fade-in, slide-in, pulse, skeleton loading
114
-
115
- ## Integration with NitroStack
116
-
117
- When you run `nitrostack dev`, it starts:
118
- 1. **MCP Server** (your project on stdio)
119
- 2. **Widget Dev Server** (Next.js on port 3001)
120
- 3. **Studio** (Next.js on port 3000)
121
-
122
- Studio proxies API calls to the MCP server and loads widgets from the widget dev server.
123
-
124
- ## Browser Support
125
-
126
- - Modern browsers (Chrome, Firefox, Safari, Edge)
127
- - ES2022+ JavaScript
128
- - CSS Grid & Flexbox
129
-
130
- ## Performance
131
-
132
- - **Static Export** - Pre-rendered HTML
133
- - **Code Splitting** - Automatic by Next.js
134
- - **Tree Shaking** - Unused code removed
135
- - **Lazy Loading** - Components load on demand
136
-
137
- ## License
138
-
139
- MIT - Part of NitroStack
140
-
@@ -1,71 +0,0 @@
1
- /**
2
- * Fetch OAuth 2.1 Protected Resource Metadata
3
- *
4
- * This endpoint fetches OAuth metadata from an MCP server's
5
- * /.well-known/oauth-protected-resource endpoint (RFC 9728)
6
- */
7
-
8
- import { NextRequest, NextResponse } from 'next/server';
9
-
10
- export async function POST(request: NextRequest) {
11
- try {
12
- const { url, type } = await request.json();
13
-
14
- if (!url) {
15
- return NextResponse.json(
16
- { error: 'url is required' },
17
- { status: 400 }
18
- );
19
- }
20
-
21
- let metadataUrl: string;
22
-
23
- if (type === 'resource') {
24
- // Fetch Protected Resource Metadata (RFC 9728)
25
- metadataUrl = new URL(url).toString();
26
- } else if (type === 'auth-server') {
27
- // Fetch Authorization Server Metadata (RFC 8414)
28
- metadataUrl = new URL(url).toString();
29
- } else {
30
- return NextResponse.json(
31
- { error: 'type must be "resource" or "auth-server"' },
32
- { status: 400 }
33
- );
34
- }
35
-
36
- // Fetch the metadata
37
- const response = await fetch(metadataUrl, {
38
- method: 'GET',
39
- headers: {
40
- 'Accept': 'application/json',
41
- },
42
- signal: AbortSignal.timeout(5000), // 5 second timeout
43
- });
44
-
45
- if (!response.ok) {
46
- return NextResponse.json(
47
- { error: `Failed to fetch metadata: ${response.statusText}` },
48
- { status: response.status }
49
- );
50
- }
51
-
52
- const metadata = await response.json();
53
-
54
- // Basic validation
55
- if (type === 'resource' && (!metadata.resource || !metadata.authorization_servers)) {
56
- return NextResponse.json(
57
- { error: 'Invalid OAuth resource metadata format' },
58
- { status: 400 }
59
- );
60
- }
61
-
62
- return NextResponse.json(metadata);
63
- } catch (error: any) {
64
- console.error('Error fetching OAuth metadata:', error);
65
- return NextResponse.json(
66
- { error: error.message || 'Failed to fetch OAuth metadata' },
67
- { status: 500 }
68
- );
69
- }
70
- }
71
-
@@ -1,67 +0,0 @@
1
- /**
2
- * Dynamic Client Registration (RFC 7591)
3
- *
4
- * This endpoint handles OAuth 2.1 Dynamic Client Registration
5
- * by proxying requests to the authorization server's registration endpoint.
6
- */
7
-
8
- import { NextRequest, NextResponse } from 'next/server';
9
-
10
- export async function POST(request: NextRequest) {
11
- try {
12
- const { endpoint, metadata } = await request.json();
13
-
14
- if (!endpoint) {
15
- return NextResponse.json(
16
- { error: 'endpoint is required' },
17
- { status: 400 }
18
- );
19
- }
20
-
21
- if (!metadata) {
22
- return NextResponse.json(
23
- { error: 'metadata is required' },
24
- { status: 400 }
25
- );
26
- }
27
-
28
- // Register the client with the authorization server
29
- const response = await fetch(endpoint, {
30
- method: 'POST',
31
- headers: {
32
- 'Content-Type': 'application/json',
33
- 'Accept': 'application/json',
34
- },
35
- body: JSON.stringify(metadata),
36
- signal: AbortSignal.timeout(10000), // 10 second timeout
37
- });
38
-
39
- if (!response.ok) {
40
- const errorText = await response.text();
41
- console.error('Client registration failed:', response.status, errorText);
42
- return NextResponse.json(
43
- { error: `Registration failed: ${response.statusText}`, details: errorText },
44
- { status: response.status }
45
- );
46
- }
47
-
48
- const registrationResponse = await response.json();
49
-
50
- // Validate the response contains required fields
51
- if (!registrationResponse.client_id) {
52
- return NextResponse.json(
53
- { error: 'Invalid registration response: missing client_id' },
54
- { status: 400 }
55
- );
56
- }
57
-
58
- return NextResponse.json(registrationResponse);
59
- } catch (error: any) {
60
- console.error('Error during client registration:', error);
61
- return NextResponse.json(
62
- { error: error.message || 'Failed to register client' },
63
- { status: 500 }
64
- );
65
- }
66
- }
67
-
@@ -1,250 +0,0 @@
1
- import { NextRequest, NextResponse } from 'next/server';
2
- import { getMcpClient } from '@/lib/mcp-client';
3
- import { LLMService, type ChatMessage, type LLMProvider } from '@/lib/llm-service';
4
-
5
- const llmService = new LLMService();
6
-
7
- export async function POST(request: NextRequest) {
8
- try {
9
- const { provider, messages, apiKey, jwtToken, mcpApiKey } = await request.json() as {
10
- provider: LLMProvider;
11
- messages: ChatMessage[];
12
- apiKey: string; // LLM API key
13
- jwtToken?: string; // MCP server JWT
14
- mcpApiKey?: string; // MCP server API key
15
- };
16
-
17
- console.log('Received chat request:', {
18
- provider,
19
- messagesCount: messages?.length,
20
- messages: JSON.stringify(messages),
21
- hasApiKey: !!apiKey,
22
- hasJwtToken: !!jwtToken,
23
- hasMcpApiKey: !!mcpApiKey,
24
- jwtTokenPreview: jwtToken ? jwtToken.substring(0, 20) + '...' : null
25
- });
26
-
27
- if (!provider || !messages || !apiKey) {
28
- return NextResponse.json(
29
- { error: 'Missing required fields' },
30
- { status: 400 }
31
- );
32
- }
33
-
34
- if (messages.length === 0) {
35
- return NextResponse.json(
36
- { error: 'Messages array is empty' },
37
- { status: 400 }
38
- );
39
- }
40
-
41
- // Get available tools from MCP server
42
- const client = getMcpClient();
43
- const toolsList = await client.listTools();
44
- const mcpTools = toolsList.tools?.map((tool: any) => ({
45
- name: tool.name,
46
- description: tool.description || '',
47
- inputSchema: tool.inputSchema || {},
48
- })) || [];
49
-
50
- // Add synthetic tools for prompts and resources
51
- const promptsList = await client.listPrompts().catch(() => ({ prompts: [] }));
52
- const resourcesList = await client.listResources().catch(() => ({ resources: [] }));
53
-
54
- const syntheticTools = [];
55
-
56
- // Add a tool to list available prompts
57
- if (promptsList.prompts && promptsList.prompts.length > 0) {
58
- syntheticTools.push({
59
- name: 'list_prompts',
60
- description: 'List all available MCP prompts with their names, descriptions, and required arguments. Call this when user asks what prompts are available. After calling this, present the complete list of prompts to the user.',
61
- inputSchema: {
62
- type: 'object',
63
- properties: {},
64
- },
65
- });
66
-
67
- // Add a tool to execute prompts
68
- syntheticTools.push({
69
- name: 'execute_prompt',
70
- description: 'Execute an MCP prompt with given arguments. Returns the prompt result. After calling this, display the result to the user.',
71
- inputSchema: {
72
- type: 'object',
73
- properties: {
74
- name: {
75
- type: 'string',
76
- description: 'The name of the prompt to execute',
77
- },
78
- arguments: {
79
- type: 'object',
80
- description: 'Arguments to pass to the prompt (key-value pairs)',
81
- },
82
- },
83
- required: ['name'],
84
- },
85
- });
86
- }
87
-
88
- // Add a tool to list available resources
89
- if (resourcesList.resources && resourcesList.resources.length > 0) {
90
- syntheticTools.push({
91
- name: 'list_resources',
92
- description: 'List all available MCP resources with their URIs, names, descriptions, and mime types. Call this when user asks to see what resources are available. After calling this, present the complete list of resources to the user.',
93
- inputSchema: {
94
- type: 'object',
95
- properties: {},
96
- },
97
- });
98
-
99
- // Add a tool to read resources
100
- syntheticTools.push({
101
- name: 'read_resource',
102
- description: 'Read the contents of an MCP resource by its URI. Returns the full resource content. After calling this, display the resource content to the user.',
103
- inputSchema: {
104
- type: 'object',
105
- properties: {
106
- uri: {
107
- type: 'string',
108
- description: 'The URI of the resource to read (e.g., "widget://examples", "config://settings")',
109
- },
110
- },
111
- required: ['uri'],
112
- },
113
- });
114
- }
115
-
116
- const tools = [...mcpTools, ...syntheticTools];
117
-
118
- // Call LLM
119
- const response = await llmService.chat(provider, messages, tools, apiKey);
120
-
121
- // If LLM wants to call tools, execute them
122
- if (response.toolCalls && response.toolCalls.length > 0) {
123
- const toolResults: ChatMessage[] = [];
124
-
125
- for (const toolCall of response.toolCalls) {
126
- try {
127
- let result: any;
128
- let toolContent = '';
129
-
130
- // Handle synthetic tools
131
- if (toolCall.name === 'list_prompts') {
132
- const prompts = await client.listPrompts();
133
- const formattedPrompts = prompts.prompts?.map((p: any) => ({
134
- name: p.name,
135
- description: p.description,
136
- arguments: p.arguments,
137
- })) || [];
138
- toolContent = JSON.stringify(formattedPrompts, null, 2);
139
- console.log('📝 list_prompts result:', toolContent);
140
- } else if (toolCall.name === 'execute_prompt') {
141
- const promptResult = await client.getPrompt(
142
- toolCall.arguments.name,
143
- toolCall.arguments.arguments || {}
144
- );
145
- toolContent = JSON.stringify(promptResult.messages || promptResult, null, 2);
146
- console.log('▶️ execute_prompt result:', toolContent.substring(0, 200) + '...');
147
- } else if (toolCall.name === 'list_resources') {
148
- const resources = await client.listResources();
149
- const formattedResources = resources.resources?.map((r: any) => ({
150
- uri: r.uri,
151
- name: r.name,
152
- description: r.description,
153
- mimeType: r.mimeType,
154
- })) || [];
155
- toolContent = JSON.stringify(formattedResources, null, 2);
156
- console.log('📋 list_resources result:', toolContent);
157
- } else if (toolCall.name === 'read_resource') {
158
- const resource = await client.readResource(toolCall.arguments.uri);
159
- // Extract the actual content from the resource
160
- let resourceContent = resource;
161
- if (resource.contents && Array.isArray(resource.contents)) {
162
- // If it's an array of contents, extract text from each
163
- resourceContent = resource.contents.map((c: any) => {
164
- if (c.text) return c.text;
165
- if (c.blob) return `[Binary data: ${c.mimeType || 'unknown type'}]`;
166
- return JSON.stringify(c);
167
- }).join('\n\n');
168
- }
169
- toolContent = typeof resourceContent === 'string' ? resourceContent : JSON.stringify(resourceContent, null, 2);
170
- console.log('📖 read_resource result:', toolContent.substring(0, 200) + '...');
171
- } else {
172
- // Regular MCP tool execution
173
- // Inject auth tokens into tool arguments if available
174
- const toolArgs = { ...toolCall.arguments };
175
- if (jwtToken || mcpApiKey) {
176
- toolArgs._meta = {
177
- ...(toolArgs._meta || {}),
178
- };
179
-
180
- if (jwtToken) {
181
- toolArgs._meta._jwt = jwtToken;
182
- toolArgs._meta.authorization = `Bearer ${jwtToken}`;
183
- }
184
-
185
- if (mcpApiKey) {
186
- toolArgs._meta.apiKey = mcpApiKey;
187
- toolArgs._meta['x-api-key'] = mcpApiKey;
188
- }
189
-
190
- console.log(`🔐 Executing tool "${toolCall.name}" with auth:`, {
191
- hasJwt: !!jwtToken,
192
- hasMcpApiKey: !!mcpApiKey,
193
- metaKeys: Object.keys(toolArgs._meta)
194
- });
195
- } else {
196
- console.log(`⚠️ Executing tool "${toolCall.name}" WITHOUT auth tokens`);
197
- }
198
-
199
- // Execute tool via MCP client
200
- result = await client.executeTool(toolCall.name, toolArgs);
201
-
202
- // Extract content from MCP result
203
- if (result.content && Array.isArray(result.content)) {
204
- toolContent = result.content
205
- .map((c: any) => c.text || JSON.stringify(c))
206
- .join('\n');
207
- } else {
208
- toolContent = JSON.stringify(result);
209
- }
210
- }
211
-
212
- // Store tool result with both ID and NAME (Gemini needs the name!)
213
- toolResults.push({
214
- role: 'tool',
215
- content: toolContent,
216
- toolCallId: toolCall.id,
217
- toolName: toolCall.name, // Add tool name for Gemini function responses
218
- });
219
- } catch (error: any) {
220
- console.error(`❌ Error executing tool "${toolCall.name}":`, error);
221
- const errorMessage = `Error executing ${toolCall.name}: ${error.message}`;
222
- toolResults.push({
223
- role: 'tool',
224
- content: JSON.stringify({ error: error.message, message: errorMessage }),
225
- toolCallId: toolCall.id,
226
- toolName: toolCall.name, // Add tool name for Gemini function responses
227
- });
228
- }
229
- }
230
-
231
- // Return response with tool results
232
- return NextResponse.json({
233
- message: response.message,
234
- toolCalls: response.toolCalls,
235
- toolResults,
236
- finishReason: response.finishReason,
237
- });
238
- }
239
-
240
- // No tool calls, just return the message
241
- return NextResponse.json({
242
- message: response.message,
243
- finishReason: response.finishReason,
244
- });
245
- } catch (error: any) {
246
- console.error('Chat error:', error);
247
- return NextResponse.json({ error: error.message }, { status: 500 });
248
- }
249
- }
250
-