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.
- package/dist/widgets/index.d.ts +4 -4
- package/dist/widgets/index.d.ts.map +1 -1
- package/dist/widgets/index.js +3 -3
- package/dist/widgets/index.js.map +1 -1
- package/package.json +8 -2
- package/ARCHITECTURE.md +0 -302
- package/CHANGELOG.md +0 -49
- package/CONTRIBUTING.md +0 -182
- package/NOTICE +0 -153
- package/jest.config.js +0 -21
- package/src/assets/nitrocloud.png +0 -0
- package/src/studio/README.md +0 -140
- package/src/studio/app/api/auth/fetch-metadata/route.ts +0 -71
- package/src/studio/app/api/auth/register-client/route.ts +0 -67
- package/src/studio/app/api/chat/route.ts +0 -250
- package/src/studio/app/api/health/checks/route.ts +0 -42
- package/src/studio/app/api/health/route.ts +0 -13
- package/src/studio/app/api/init/route.ts +0 -109
- package/src/studio/app/api/ping/route.ts +0 -13
- package/src/studio/app/api/prompts/[name]/route.ts +0 -21
- package/src/studio/app/api/prompts/route.ts +0 -13
- package/src/studio/app/api/resources/[...uri]/route.ts +0 -18
- package/src/studio/app/api/resources/route.ts +0 -13
- package/src/studio/app/api/roots/route.ts +0 -13
- package/src/studio/app/api/sampling/route.ts +0 -14
- package/src/studio/app/api/tools/[name]/call/route.ts +0 -41
- package/src/studio/app/api/tools/route.ts +0 -23
- package/src/studio/app/api/widget-examples/route.ts +0 -44
- package/src/studio/app/auth/callback/page.tsx +0 -175
- package/src/studio/app/auth/page.tsx +0 -560
- package/src/studio/app/chat/page.tsx +0 -1133
- package/src/studio/app/chat/page.tsx.backup +0 -390
- package/src/studio/app/globals.css +0 -486
- package/src/studio/app/health/page.tsx +0 -179
- package/src/studio/app/layout.tsx +0 -68
- package/src/studio/app/logs/page.tsx +0 -279
- package/src/studio/app/page.tsx +0 -351
- package/src/studio/app/page.tsx.backup +0 -346
- package/src/studio/app/ping/page.tsx +0 -209
- package/src/studio/app/prompts/page.tsx +0 -230
- package/src/studio/app/resources/page.tsx +0 -315
- package/src/studio/app/settings/page.tsx +0 -199
- package/src/studio/branding.md +0 -807
- package/src/studio/components/EnlargeModal.tsx +0 -138
- package/src/studio/components/LogMessage.tsx +0 -153
- package/src/studio/components/MarkdownRenderer.tsx +0 -410
- package/src/studio/components/Sidebar.tsx +0 -295
- package/src/studio/components/ToolCard.tsx +0 -139
- package/src/studio/components/WidgetRenderer.tsx +0 -346
- package/src/studio/lib/api.ts +0 -207
- package/src/studio/lib/http-client-transport.ts +0 -222
- package/src/studio/lib/llm-service.ts +0 -480
- package/src/studio/lib/log-manager.ts +0 -76
- package/src/studio/lib/mcp-client.ts +0 -258
- package/src/studio/lib/store.ts +0 -192
- package/src/studio/lib/theme-provider.tsx +0 -50
- package/src/studio/lib/types.ts +0 -107
- package/src/studio/lib/widget-loader.ts +0 -90
- package/src/studio/middleware.ts +0 -27
- package/src/studio/next.config.js +0 -38
- package/src/studio/package.json +0 -35
- package/src/studio/postcss.config.mjs +0 -10
- package/src/studio/public/nitrocloud.png +0 -0
- package/src/studio/tailwind.config.ts +0 -67
- 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
|
package/src/studio/README.md
DELETED
|
@@ -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
|
-
|