mirror of
https://github.com/samanhappy/mcphub.git
synced 2025-12-24 02:39:19 -05:00
Add OpenAPI specification generation for OpenWebUI integration (#295)
Co-authored-by: samanhappy <samanhappy@gmail.com>
This commit is contained in:
250
docs/api-reference/openapi.mdx
Normal file
250
docs/api-reference/openapi.mdx
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
---
|
||||||
|
title: "OpenAPI Integration"
|
||||||
|
description: "Generate OpenAPI specifications from MCP tools for seamless integration with OpenWebUI and other systems"
|
||||||
|
---
|
||||||
|
|
||||||
|
# OpenAPI Generation for OpenWebUI Integration
|
||||||
|
|
||||||
|
MCPHub now supports generating OpenAPI 3.0.3 specifications from MCP tools, enabling seamless integration with OpenWebUI and other OpenAPI-compatible systems without requiring MCPO as an intermediary proxy.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- ✅ **Automatic OpenAPI Generation**: Converts MCP tools to OpenAPI 3.0.3 specification
|
||||||
|
- ✅ **OpenWebUI Compatible**: Direct integration without MCPO proxy
|
||||||
|
- ✅ **Real-time Tool Discovery**: Dynamically includes tools from connected MCP servers
|
||||||
|
- ✅ **Dual Parameter Support**: Supports both GET (query params) and POST (JSON body) for tool execution
|
||||||
|
- ✅ **No Authentication Required**: OpenAPI endpoints are public for easy integration
|
||||||
|
- ✅ **Comprehensive Metadata**: Full OpenAPI specification with proper schemas and documentation
|
||||||
|
|
||||||
|
## API Endpoints
|
||||||
|
|
||||||
|
### OpenAPI Specification
|
||||||
|
|
||||||
|
<CodeGroup>
|
||||||
|
|
||||||
|
```bash GET /api/openapi.json
|
||||||
|
curl "http://localhost:3000/api/openapi.json"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash With Parameters
|
||||||
|
curl "http://localhost:3000/api/openapi.json?title=My MCP API&version=2.0.0"
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeGroup>
|
||||||
|
|
||||||
|
Generates and returns the complete OpenAPI 3.0.3 specification for all connected MCP tools.
|
||||||
|
|
||||||
|
**Query Parameters:**
|
||||||
|
|
||||||
|
<ParamField query="title" type="string" optional>
|
||||||
|
Custom API title
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="description" type="string" optional>
|
||||||
|
Custom API description
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="version" type="string" optional>
|
||||||
|
Custom API version
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="serverUrl" type="string" optional>
|
||||||
|
Custom server URL
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="includeDisabled" type="boolean" optional default="false">
|
||||||
|
Include disabled tools
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="servers" type="string" optional>
|
||||||
|
Comma-separated list of server names to include
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
### Available Servers
|
||||||
|
|
||||||
|
<CodeGroup>
|
||||||
|
|
||||||
|
```bash GET /api/openapi/servers
|
||||||
|
curl "http://localhost:3000/api/openapi/servers"
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeGroup>
|
||||||
|
|
||||||
|
Returns a list of connected MCP server names.
|
||||||
|
|
||||||
|
<ResponseExample>
|
||||||
|
|
||||||
|
```json Example Response
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": ["amap", "playwright", "slack"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</ResponseExample>
|
||||||
|
|
||||||
|
### Tool Statistics
|
||||||
|
|
||||||
|
<CodeGroup>
|
||||||
|
|
||||||
|
```bash GET /api/openapi/stats
|
||||||
|
curl "http://localhost:3000/api/openapi/stats"
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeGroup>
|
||||||
|
|
||||||
|
Returns statistics about available tools and servers.
|
||||||
|
|
||||||
|
<ResponseExample>
|
||||||
|
|
||||||
|
```json Example Response
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"totalServers": 3,
|
||||||
|
"totalTools": 41,
|
||||||
|
"serverBreakdown": [
|
||||||
|
{"name": "amap", "toolCount": 12, "status": "connected"},
|
||||||
|
{"name": "playwright", "toolCount": 21, "status": "connected"},
|
||||||
|
{"name": "slack", "toolCount": 8, "status": "connected"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</ResponseExample>
|
||||||
|
|
||||||
|
### Tool Execution
|
||||||
|
|
||||||
|
<CodeGroup>
|
||||||
|
|
||||||
|
```bash GET /api/tools/{serverName}/{toolName}
|
||||||
|
curl "http://localhost:3000/api/tools/amap/amap-maps_weather?city=Beijing"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash POST /api/tools/{serverName}/{toolName}
|
||||||
|
curl -X POST "http://localhost:3000/api/tools/playwright/playwright-browser_navigate" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"url": "https://example.com"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeGroup>
|
||||||
|
|
||||||
|
Execute MCP tools via OpenAPI-compatible endpoints.
|
||||||
|
|
||||||
|
**Path Parameters:**
|
||||||
|
|
||||||
|
<ParamField path="serverName" type="string" required>
|
||||||
|
The name of the MCP server
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField path="toolName" type="string" required>
|
||||||
|
The name of the tool to execute
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
## OpenWebUI Integration
|
||||||
|
|
||||||
|
To integrate MCPHub with OpenWebUI:
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Start MCPHub">
|
||||||
|
Ensure MCPHub is running with your MCP servers configured
|
||||||
|
</Step>
|
||||||
|
<Step title="Get OpenAPI Specification">
|
||||||
|
```bash
|
||||||
|
curl http://localhost:3000/api/openapi.json > mcphub-api.json
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
<Step title="Add to OpenWebUI">
|
||||||
|
Import the OpenAPI specification file or point to the URL directly in OpenWebUI
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
### Configuration Example
|
||||||
|
|
||||||
|
In OpenWebUI, you can add MCPHub as an OpenAPI tool by using:
|
||||||
|
|
||||||
|
<CardGroup cols={2}>
|
||||||
|
<Card title="OpenAPI URL" icon="link">
|
||||||
|
`http://localhost:3000/api/openapi.json`
|
||||||
|
</Card>
|
||||||
|
<Card title="Base URL" icon="server">
|
||||||
|
`http://localhost:3000/api`
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
|
## Generated OpenAPI Structure
|
||||||
|
|
||||||
|
The generated OpenAPI specification includes:
|
||||||
|
|
||||||
|
### Tool Conversion Logic
|
||||||
|
|
||||||
|
- **Simple tools** (≤10 primitive parameters) → GET endpoints with query parameters
|
||||||
|
- **Complex tools** (objects, arrays, or >10 parameters) → POST endpoints with JSON request body
|
||||||
|
- **All tools** include comprehensive response schemas and error handling
|
||||||
|
|
||||||
|
### Example Generated Operation
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
/tools/amap/amap-maps_weather:
|
||||||
|
get:
|
||||||
|
summary: "根据城市名称或者标准adcode查询指定城市的天气"
|
||||||
|
operationId: "amap_amap-maps_weather"
|
||||||
|
tags: ["amap"]
|
||||||
|
parameters:
|
||||||
|
- name: city
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
description: "城市名称或者adcode"
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: "Successful tool execution"
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ToolResponse'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- Bearer authentication is defined but not enforced for tool execution endpoints
|
||||||
|
- Enables flexible integration with various OpenAPI-compatible systems
|
||||||
|
|
||||||
|
## Benefits over MCPO
|
||||||
|
|
||||||
|
<CardGroup cols={2}>
|
||||||
|
<Card title="Direct Integration" icon="plug">
|
||||||
|
No need for intermediate proxy
|
||||||
|
</Card>
|
||||||
|
<Card title="Real-time Updates" icon="refresh">
|
||||||
|
OpenAPI spec updates automatically as MCP servers connect/disconnect
|
||||||
|
</Card>
|
||||||
|
<Card title="Better Performance" icon="bolt">
|
||||||
|
Direct tool execution without proxy overhead
|
||||||
|
</Card>
|
||||||
|
<Card title="Simplified Architecture" icon="layer-group">
|
||||||
|
One less component to manage
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
<AccordionGroup>
|
||||||
|
<Accordion title="OpenAPI spec shows no tools">
|
||||||
|
Ensure MCP servers are connected. Check `/api/openapi/stats` for server status.
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="Tool execution fails">
|
||||||
|
Verify the tool name and parameters match the OpenAPI specification. Check server logs for details.
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="OpenWebUI can't connect">
|
||||||
|
Ensure MCPHub is accessible from OpenWebUI and the OpenAPI URL is correct.
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="Missing tools in specification">
|
||||||
|
Check if tools are enabled in your MCP server configuration. Use `includeDisabled=true` to see all tools.
|
||||||
|
</Accordion>
|
||||||
|
</AccordionGroup>
|
||||||
@@ -83,6 +83,12 @@
|
|||||||
"api-reference/smart-routing"
|
"api-reference/smart-routing"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"group": "OpenAPI Endpoints",
|
||||||
|
"pages": [
|
||||||
|
"api-reference/openapi"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"group": "Management Endpoints",
|
"group": "Management Endpoints",
|
||||||
"pages": [
|
"pages": [
|
||||||
@@ -107,6 +113,12 @@
|
|||||||
"zh/api-reference/smart-routing"
|
"zh/api-reference/smart-routing"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"group": "OpenAPI 端点",
|
||||||
|
"pages": [
|
||||||
|
"zh/api-reference/openapi"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"group": "管理端点",
|
"group": "管理端点",
|
||||||
"pages": [
|
"pages": [
|
||||||
|
|||||||
250
docs/zh/api-reference/openapi.mdx
Normal file
250
docs/zh/api-reference/openapi.mdx
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
---
|
||||||
|
title: "OpenAPI 集成"
|
||||||
|
description: "从 MCP 工具生成 OpenAPI 规范,与 OpenWebUI 和其他系统无缝集成"
|
||||||
|
---
|
||||||
|
|
||||||
|
# OpenWebUI 集成的 OpenAPI 生成
|
||||||
|
|
||||||
|
MCPHub 现在支持从 MCP 工具生成 OpenAPI 3.0.3 规范,实现与 OpenWebUI 和其他 OpenAPI 兼容系统的无缝集成,无需 MCPO 作为中间代理。
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
- ✅ **自动 OpenAPI 生成**:将 MCP 工具转换为 OpenAPI 3.0.3 规范
|
||||||
|
- ✅ **OpenWebUI 兼容**:无需 MCPO 代理的直接集成
|
||||||
|
- ✅ **实时工具发现**:动态包含已连接 MCP 服务器的工具
|
||||||
|
- ✅ **双参数支持**:支持 GET(查询参数)和 POST(JSON 正文)进行工具执行
|
||||||
|
- ✅ **无需身份验证**:OpenAPI 端点公开,便于集成
|
||||||
|
- ✅ **完整元数据**:具有适当模式和文档的完整 OpenAPI 规范
|
||||||
|
|
||||||
|
## API 端点
|
||||||
|
|
||||||
|
### OpenAPI 规范
|
||||||
|
|
||||||
|
<CodeGroup>
|
||||||
|
|
||||||
|
```bash GET /api/openapi.json
|
||||||
|
curl "http://localhost:3000/api/openapi.json"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash 带参数
|
||||||
|
curl "http://localhost:3000/api/openapi.json?title=我的 MCP API&version=2.0.0"
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeGroup>
|
||||||
|
|
||||||
|
生成并返回所有已连接 MCP 工具的完整 OpenAPI 3.0.3 规范。
|
||||||
|
|
||||||
|
**查询参数:**
|
||||||
|
|
||||||
|
<ParamField query="title" type="string" optional>
|
||||||
|
自定义 API 标题
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="description" type="string" optional>
|
||||||
|
自定义 API 描述
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="version" type="string" optional>
|
||||||
|
自定义 API 版本
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="serverUrl" type="string" optional>
|
||||||
|
自定义服务器 URL
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="includeDisabled" type="boolean" optional default="false">
|
||||||
|
包含禁用的工具
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="servers" type="string" optional>
|
||||||
|
要包含的服务器名称列表(逗号分隔)
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
### 可用服务器
|
||||||
|
|
||||||
|
<CodeGroup>
|
||||||
|
|
||||||
|
```bash GET /api/openapi/servers
|
||||||
|
curl "http://localhost:3000/api/openapi/servers"
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeGroup>
|
||||||
|
|
||||||
|
返回已连接的 MCP 服务器名称列表。
|
||||||
|
|
||||||
|
<ResponseExample>
|
||||||
|
|
||||||
|
```json 示例响应
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": ["amap", "playwright", "slack"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</ResponseExample>
|
||||||
|
|
||||||
|
### 工具统计
|
||||||
|
|
||||||
|
<CodeGroup>
|
||||||
|
|
||||||
|
```bash GET /api/openapi/stats
|
||||||
|
curl "http://localhost:3000/api/openapi/stats"
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeGroup>
|
||||||
|
|
||||||
|
返回有关可用工具和服务器的统计信息。
|
||||||
|
|
||||||
|
<ResponseExample>
|
||||||
|
|
||||||
|
```json 示例响应
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"totalServers": 3,
|
||||||
|
"totalTools": 41,
|
||||||
|
"serverBreakdown": [
|
||||||
|
{"name": "amap", "toolCount": 12, "status": "connected"},
|
||||||
|
{"name": "playwright", "toolCount": 21, "status": "connected"},
|
||||||
|
{"name": "slack", "toolCount": 8, "status": "connected"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</ResponseExample>
|
||||||
|
|
||||||
|
### 工具执行
|
||||||
|
|
||||||
|
<CodeGroup>
|
||||||
|
|
||||||
|
```bash GET /api/tools/{serverName}/{toolName}
|
||||||
|
curl "http://localhost:3000/api/tools/amap/amap-maps_weather?city=Beijing"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash POST /api/tools/{serverName}/{toolName}
|
||||||
|
curl -X POST "http://localhost:3000/api/tools/playwright/playwright-browser_navigate" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"url": "https://example.com"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeGroup>
|
||||||
|
|
||||||
|
通过 OpenAPI 兼容端点执行 MCP 工具。
|
||||||
|
|
||||||
|
**路径参数:**
|
||||||
|
|
||||||
|
<ParamField path="serverName" type="string" required>
|
||||||
|
MCP 服务器的名称
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField path="toolName" type="string" required>
|
||||||
|
要执行的工具名称
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
## OpenWebUI 集成
|
||||||
|
|
||||||
|
要将 MCPHub 与 OpenWebUI 集成:
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="启动 MCPHub">
|
||||||
|
确保 MCPHub 正在运行,并且已配置 MCP 服务器
|
||||||
|
</Step>
|
||||||
|
<Step title="获取 OpenAPI 规范">
|
||||||
|
```bash
|
||||||
|
curl http://localhost:3000/api/openapi.json > mcphub-api.json
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
<Step title="添加到 OpenWebUI">
|
||||||
|
在 OpenWebUI 中导入 OpenAPI 规范文件或直接指向 URL
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
### 配置示例
|
||||||
|
|
||||||
|
在 OpenWebUI 中,您可以通过以下方式将 MCPHub 添加为 OpenAPI 工具:
|
||||||
|
|
||||||
|
<CardGroup cols={2}>
|
||||||
|
<Card title="OpenAPI URL" icon="link">
|
||||||
|
`http://localhost:3000/api/openapi.json`
|
||||||
|
</Card>
|
||||||
|
<Card title="基础 URL" icon="server">
|
||||||
|
`http://localhost:3000/api`
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
|
## 生成的 OpenAPI 结构
|
||||||
|
|
||||||
|
生成的 OpenAPI 规范包括:
|
||||||
|
|
||||||
|
### 工具转换逻辑
|
||||||
|
|
||||||
|
- **简单工具**(≤10 个原始参数)→ 带查询参数的 GET 端点
|
||||||
|
- **复杂工具**(对象、数组或 >10 个参数)→ 带 JSON 请求正文的 POST 端点
|
||||||
|
- **所有工具**都包含完整的响应模式和错误处理
|
||||||
|
|
||||||
|
### 生成操作示例
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
/tools/amap/amap-maps_weather:
|
||||||
|
get:
|
||||||
|
summary: "根据城市名称或者标准adcode查询指定城市的天气"
|
||||||
|
operationId: "amap_amap-maps_weather"
|
||||||
|
tags: ["amap"]
|
||||||
|
parameters:
|
||||||
|
- name: city
|
||||||
|
in: query
|
||||||
|
required: true
|
||||||
|
description: "城市名称或者adcode"
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: "Successful tool execution"
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ToolResponse'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 安全性
|
||||||
|
|
||||||
|
- 定义了 Bearer 身份验证但不对工具执行端点强制执行
|
||||||
|
- 支持与各种 OpenAPI 兼容系统的灵活集成
|
||||||
|
|
||||||
|
## 相比 MCPO 的优势
|
||||||
|
|
||||||
|
<CardGroup cols={2}>
|
||||||
|
<Card title="直接集成" icon="plug">
|
||||||
|
无需中间代理
|
||||||
|
</Card>
|
||||||
|
<Card title="实时更新" icon="refresh">
|
||||||
|
OpenAPI 规范随着 MCP 服务器连接/断开自动更新
|
||||||
|
</Card>
|
||||||
|
<Card title="更好的性能" icon="bolt">
|
||||||
|
直接工具执行,无代理开销
|
||||||
|
</Card>
|
||||||
|
<Card title="简化架构" icon="layer-group">
|
||||||
|
减少一个需要管理的组件
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
|
## 故障排除
|
||||||
|
|
||||||
|
<AccordionGroup>
|
||||||
|
<Accordion title="OpenAPI 规范显示没有工具">
|
||||||
|
确保 MCP 服务器已连接。检查 `/api/openapi/stats` 查看服务器状态。
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="工具执行失败">
|
||||||
|
验证工具名称和参数是否与 OpenAPI 规范匹配。检查服务器日志以获取详细信息。
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="OpenWebUI 无法连接">
|
||||||
|
确保 MCPHub 可从 OpenWebUI 访问,并且 OpenAPI URL 正确。
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="规范中缺少工具">
|
||||||
|
检查您的 MCP 服务器配置中是否启用了工具。使用 `includeDisabled=true` 查看所有工具。
|
||||||
|
</Accordion>
|
||||||
|
</AccordionGroup>
|
||||||
244
package-lock.json
generated
244
package-lock.json
generated
@@ -10,81 +10,81 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apidevtools/swagger-parser": "^11.0.1",
|
"@apidevtools/swagger-parser": "^11.0.1",
|
||||||
"@modelcontextprotocol/sdk": "^1.17.2",
|
"@modelcontextprotocol/sdk": "^1.17.4",
|
||||||
"@types/adm-zip": "^0.5.7",
|
"@types/adm-zip": "^0.5.7",
|
||||||
"@types/multer": "^1.4.13",
|
"@types/multer": "^1.4.13",
|
||||||
"@types/pg": "^8.15.2",
|
"@types/pg": "^8.15.5",
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"axios": "^1.10.0",
|
"axios": "^1.11.0",
|
||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.2",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.6.1",
|
||||||
"dotenv-expand": "^12.0.2",
|
"dotenv-expand": "^12.0.2",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"express-validator": "^7.2.1",
|
"express-validator": "^7.2.1",
|
||||||
"i18next-fs-backend": "^2.6.0",
|
"i18next-fs-backend": "^2.6.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"multer": "^2.0.1",
|
"multer": "^2.0.2",
|
||||||
"openai": "^4.103.0",
|
"openai": "^4.104.0",
|
||||||
"openapi-types": "^12.1.3",
|
"openapi-types": "^12.1.3",
|
||||||
"pg": "^8.16.0",
|
"pg": "^8.16.3",
|
||||||
"pgvector": "^0.2.1",
|
"pgvector": "^0.2.1",
|
||||||
"postgres": "^3.4.7",
|
"postgres": "^3.4.7",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"typeorm": "^0.3.24",
|
"typeorm": "^0.3.26",
|
||||||
"uuid": "^11.1.0"
|
"uuid": "^11.1.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"mcphub": "bin/cli.js"
|
"mcphub": "bin/cli.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@radix-ui/react-accordion": "^1.2.3",
|
"@radix-ui/react-accordion": "^1.2.12",
|
||||||
"@radix-ui/react-slot": "^1.1.2",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@shadcn/ui": "^0.0.4",
|
"@shadcn/ui": "^0.0.4",
|
||||||
"@swc/core": "^1.13.0",
|
"@swc/core": "^1.13.5",
|
||||||
"@swc/jest": "^0.2.39",
|
"@swc/jest": "^0.2.39",
|
||||||
"@tailwindcss/line-clamp": "^0.4.4",
|
"@tailwindcss/line-clamp": "^0.4.4",
|
||||||
"@tailwindcss/postcss": "^4.1.3",
|
"@tailwindcss/postcss": "^4.1.12",
|
||||||
"@tailwindcss/vite": "^4.1.7",
|
"@tailwindcss/vite": "^4.1.12",
|
||||||
"@types/bcryptjs": "^3.0.0",
|
"@types/bcryptjs": "^3.0.0",
|
||||||
"@types/express": "^4.17.21",
|
"@types/express": "^4.17.23",
|
||||||
"@types/jest": "^29.5.5",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/jsonwebtoken": "^9.0.9",
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
"@types/node": "^22.15.21",
|
"@types/node": "^22.17.2",
|
||||||
"@types/react": "^19.0.12",
|
"@types/react": "^19.1.11",
|
||||||
"@types/react-dom": "^19.0.4",
|
"@types/react-dom": "^19.1.7",
|
||||||
"@types/supertest": "^6.0.3",
|
"@types/supertest": "^6.0.3",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||||
"@typescript-eslint/parser": "^6.7.4",
|
"@typescript-eslint/parser": "^6.21.0",
|
||||||
"@vitejs/plugin-react": "^4.4.1",
|
"@vitejs/plugin-react": "^4.7.0",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"concurrently": "^9.1.2",
|
"concurrently": "^9.2.0",
|
||||||
"eslint": "^8.50.0",
|
"eslint": "^8.57.1",
|
||||||
"i18next": "^24.2.3",
|
"i18next": "^24.2.3",
|
||||||
"i18next-browser-languagedetector": "^8.0.4",
|
"i18next-browser-languagedetector": "^8.2.0",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-environment-node": "^30.0.0",
|
"jest-environment-node": "^30.0.5",
|
||||||
"jest-mock-extended": "4.0.0-beta1",
|
"jest-mock-extended": "4.0.0-beta1",
|
||||||
"lucide-react": "^0.486.0",
|
"lucide-react": "^0.486.0",
|
||||||
"next": "^15.2.4",
|
"next": "^15.5.0",
|
||||||
"postcss": "^8.5.3",
|
"postcss": "^8.5.6",
|
||||||
"prettier": "^3.0.3",
|
"prettier": "^3.6.2",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.1",
|
||||||
"react-i18next": "^15.4.1",
|
"react-i18next": "^15.7.2",
|
||||||
"react-router-dom": "^7.6.0",
|
"react-router-dom": "^7.8.2",
|
||||||
"supertest": "^7.1.1",
|
"supertest": "^7.1.4",
|
||||||
"tailwind-merge": "^3.1.0",
|
"tailwind-merge": "^3.3.1",
|
||||||
"tailwind-scrollbar-hide": "^2.0.0",
|
"tailwind-scrollbar-hide": "^2.0.0",
|
||||||
"tailwindcss": "^4.0.17",
|
"tailwindcss": "^4.1.12",
|
||||||
"ts-jest": "^29.1.1",
|
"ts-jest": "^29.4.1",
|
||||||
"ts-node-dev": "^2.0.0",
|
"ts-node-dev": "^2.0.0",
|
||||||
"tsx": "^4.7.0",
|
"tsx": "^4.20.5",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.9.2",
|
||||||
"vite": "^6.3.5",
|
"vite": "^6.3.5",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.0.0 || >=20.0.0"
|
"node": "^18.0.0 || >=20.0.0"
|
||||||
@@ -727,7 +727,7 @@
|
|||||||
"version": "0.8.1",
|
"version": "0.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/trace-mapping": "0.3.9"
|
"@jridgewell/trace-mapping": "0.3.9"
|
||||||
@@ -740,7 +740,7 @@
|
|||||||
"version": "0.3.9",
|
"version": "0.3.9",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/resolve-uri": "^3.0.3",
|
"@jridgewell/resolve-uri": "^3.0.3",
|
||||||
@@ -2443,7 +2443,7 @@
|
|||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
@@ -2453,7 +2453,7 @@
|
|||||||
"version": "1.5.5",
|
"version": "1.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/trace-mapping": {
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
@@ -2468,9 +2468,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@modelcontextprotocol/sdk": {
|
"node_modules/@modelcontextprotocol/sdk": {
|
||||||
"version": "1.17.3",
|
"version": "1.17.4",
|
||||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.3.tgz",
|
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.4.tgz",
|
||||||
"integrity": "sha512-JPwUKWSsbzx+DLFznf/QZ32Qa+ptfbUlHhRLrBQBAFu9iI1iYvizM4p+zhhRDceSsPutXp4z+R/HPVphlIiclg==",
|
"integrity": "sha512-zq24hfuAmmlNZvik0FLI58uE5sriN0WWsQzIlYnzSuKDAHFqJtBFrl/LfB1NLgJT5Y7dEBzaX4yAKqOPrcetaw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "^6.12.6",
|
"ajv": "^6.12.6",
|
||||||
@@ -3618,15 +3618,15 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core": {
|
"node_modules/@swc/core": {
|
||||||
"version": "1.13.3",
|
"version": "1.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.5.tgz",
|
||||||
"integrity": "sha512-ZaDETVWnm6FE0fc+c2UE8MHYVS3Fe91o5vkmGfgwGXFbxYvAjKSqxM/j4cRc9T7VZNSJjriXq58XkfCp3Y6f+w==",
|
"integrity": "sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@swc/counter": "^0.1.3",
|
"@swc/counter": "^0.1.3",
|
||||||
"@swc/types": "^0.1.23"
|
"@swc/types": "^0.1.24"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
@@ -3636,16 +3636,16 @@
|
|||||||
"url": "https://opencollective.com/swc"
|
"url": "https://opencollective.com/swc"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@swc/core-darwin-arm64": "1.13.3",
|
"@swc/core-darwin-arm64": "1.13.5",
|
||||||
"@swc/core-darwin-x64": "1.13.3",
|
"@swc/core-darwin-x64": "1.13.5",
|
||||||
"@swc/core-linux-arm-gnueabihf": "1.13.3",
|
"@swc/core-linux-arm-gnueabihf": "1.13.5",
|
||||||
"@swc/core-linux-arm64-gnu": "1.13.3",
|
"@swc/core-linux-arm64-gnu": "1.13.5",
|
||||||
"@swc/core-linux-arm64-musl": "1.13.3",
|
"@swc/core-linux-arm64-musl": "1.13.5",
|
||||||
"@swc/core-linux-x64-gnu": "1.13.3",
|
"@swc/core-linux-x64-gnu": "1.13.5",
|
||||||
"@swc/core-linux-x64-musl": "1.13.3",
|
"@swc/core-linux-x64-musl": "1.13.5",
|
||||||
"@swc/core-win32-arm64-msvc": "1.13.3",
|
"@swc/core-win32-arm64-msvc": "1.13.5",
|
||||||
"@swc/core-win32-ia32-msvc": "1.13.3",
|
"@swc/core-win32-ia32-msvc": "1.13.5",
|
||||||
"@swc/core-win32-x64-msvc": "1.13.3"
|
"@swc/core-win32-x64-msvc": "1.13.5"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@swc/helpers": ">=0.5.17"
|
"@swc/helpers": ">=0.5.17"
|
||||||
@@ -3657,9 +3657,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-darwin-arm64": {
|
"node_modules/@swc/core-darwin-arm64": {
|
||||||
"version": "1.13.3",
|
"version": "1.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.5.tgz",
|
||||||
"integrity": "sha512-ux0Ws4pSpBTqbDS9GlVP354MekB1DwYlbxXU3VhnDr4GBcCOimpocx62x7cFJkSpEBF8bmX8+/TTCGKh4PbyXw==",
|
"integrity": "sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -3674,9 +3674,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-darwin-x64": {
|
"node_modules/@swc/core-darwin-x64": {
|
||||||
"version": "1.13.3",
|
"version": "1.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.5.tgz",
|
||||||
"integrity": "sha512-p0X6yhxmNUOMZrbeZ3ZNsPige8lSlSe1llllXvpCLkKKxN/k5vZt1sULoq6Nj4eQ7KeHQVm81/+AwKZyf/e0TA==",
|
"integrity": "sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -3691,9 +3691,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
||||||
"version": "1.13.3",
|
"version": "1.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.5.tgz",
|
||||||
"integrity": "sha512-OmDoiexL2fVWvQTCtoh0xHMyEkZweQAlh4dRyvl8ugqIPEVARSYtaj55TBMUJIP44mSUOJ5tytjzhn2KFxFcBA==",
|
"integrity": "sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -3708,9 +3708,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-arm64-gnu": {
|
"node_modules/@swc/core-linux-arm64-gnu": {
|
||||||
"version": "1.13.3",
|
"version": "1.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.5.tgz",
|
||||||
"integrity": "sha512-STfKku3QfnuUj6k3g9ld4vwhtgCGYIFQmsGPPgT9MK/dI3Lwnpe5Gs5t1inoUIoGNP8sIOLlBB4HV4MmBjQuhw==",
|
"integrity": "sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -3725,9 +3725,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-arm64-musl": {
|
"node_modules/@swc/core-linux-arm64-musl": {
|
||||||
"version": "1.13.3",
|
"version": "1.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.5.tgz",
|
||||||
"integrity": "sha512-bc+CXYlFc1t8pv9yZJGus372ldzOVscBl7encUBlU1m/Sig0+NDJLz6cXXRcFyl6ABNOApWeR4Yl7iUWx6C8og==",
|
"integrity": "sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -3742,9 +3742,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-x64-gnu": {
|
"node_modules/@swc/core-linux-x64-gnu": {
|
||||||
"version": "1.13.3",
|
"version": "1.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.5.tgz",
|
||||||
"integrity": "sha512-dFXoa0TEhohrKcxn/54YKs1iwNeW6tUkHJgXW33H381SvjKFUV53WR231jh1sWVJETjA3vsAwxKwR23s7UCmUA==",
|
"integrity": "sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -3759,9 +3759,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-x64-musl": {
|
"node_modules/@swc/core-linux-x64-musl": {
|
||||||
"version": "1.13.3",
|
"version": "1.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.5.tgz",
|
||||||
"integrity": "sha512-ieyjisLB+ldexiE/yD8uomaZuZIbTc8tjquYln9Quh5ykOBY7LpJJYBWvWtm1g3pHv6AXlBI8Jay7Fffb6aLfA==",
|
"integrity": "sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -3776,9 +3776,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-win32-arm64-msvc": {
|
"node_modules/@swc/core-win32-arm64-msvc": {
|
||||||
"version": "1.13.3",
|
"version": "1.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.5.tgz",
|
||||||
"integrity": "sha512-elTQpnaX5vESSbhCEgcwXjpMsnUbqqHfEpB7ewpkAsLzKEXZaK67ihSRYAuAx6ewRQTo7DS5iTT6X5aQD3MzMw==",
|
"integrity": "sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -3793,9 +3793,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-win32-ia32-msvc": {
|
"node_modules/@swc/core-win32-ia32-msvc": {
|
||||||
"version": "1.13.3",
|
"version": "1.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.5.tgz",
|
||||||
"integrity": "sha512-nvehQVEOdI1BleJpuUgPLrclJ0TzbEMc+MarXDmmiRFwEUGqj+pnfkTSb7RZyS1puU74IXdK/YhTirHurtbI9w==",
|
"integrity": "sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -3810,9 +3810,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-win32-x64-msvc": {
|
"node_modules/@swc/core-win32-x64-msvc": {
|
||||||
"version": "1.13.3",
|
"version": "1.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.5.tgz",
|
||||||
"integrity": "sha512-A+JSKGkRbPLVV2Kwx8TaDAV0yXIXm/gc8m98hSkVDGlPBBmydgzNdWy3X7HTUBM7IDk7YlWE7w2+RUGjdgpTmg==",
|
"integrity": "sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -4176,28 +4176,28 @@
|
|||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
||||||
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
|
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@tsconfig/node12": {
|
"node_modules/@tsconfig/node12": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@tsconfig/node14": {
|
"node_modules/@tsconfig/node14": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@tsconfig/node16": {
|
"node_modules/@tsconfig/node16": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
||||||
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/adm-zip": {
|
"node_modules/@types/adm-zip": {
|
||||||
@@ -4465,9 +4465,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "19.1.10",
|
"version": "19.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.11.tgz",
|
||||||
"integrity": "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==",
|
"integrity": "sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4836,7 +4836,7 @@
|
|||||||
"version": "8.15.0",
|
"version": "8.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
@@ -4859,7 +4859,7 @@
|
|||||||
"version": "8.3.4",
|
"version": "8.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
|
||||||
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
|
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "^8.11.0"
|
"acorn": "^8.11.0"
|
||||||
@@ -5014,7 +5014,7 @@
|
|||||||
"version": "4.1.3",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/argparse": {
|
"node_modules/argparse": {
|
||||||
@@ -6013,7 +6013,7 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
@@ -6194,7 +6194,7 @@
|
|||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.3.1"
|
"node": ">=0.3.1"
|
||||||
@@ -9633,7 +9633,7 @@
|
|||||||
"version": "1.3.6",
|
"version": "1.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/makeerror": {
|
"node_modules/makeerror": {
|
||||||
@@ -10956,9 +10956,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-i18next": {
|
"node_modules/react-i18next": {
|
||||||
"version": "15.6.1",
|
"version": "15.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.7.2.tgz",
|
||||||
"integrity": "sha512-uGrzSsOUUe2sDBG/+FJq2J1MM+Y4368/QW8OLEKSFvnDflHBbZhSd1u3UkW0Z06rMhZmnB/AQrhCpYfE5/5XNg==",
|
"integrity": "sha512-xJxq7ibnhUlMvd82lNC4te1GxGUMoM1A05KKyqoqsBXVZtEvZg/fz/fnVzdlY/hhQ3SpP/79qCocZOtICGhd3g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -10966,7 +10966,7 @@
|
|||||||
"html-parse-stringify": "^3.0.1"
|
"html-parse-stringify": "^3.0.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"i18next": ">= 23.2.3",
|
"i18next": ">= 25.4.1",
|
||||||
"react": ">= 16.8.0",
|
"react": ">= 16.8.0",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
},
|
},
|
||||||
@@ -11000,9 +11000,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router": {
|
"node_modules/react-router": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.8.2.tgz",
|
||||||
"integrity": "sha512-5cy/M8DHcG51/KUIka1nfZ2QeylS4PJRs6TT8I4PF5axVsI5JUxp0hC0NZ/AEEj8Vw7xsEoD7L/6FY+zoYaOGA==",
|
"integrity": "sha512-7M2fR1JbIZ/jFWqelpvSZx+7vd7UlBTfdZqf6OSdF9g6+sfdqJDAWcak6ervbHph200ePlu+7G8LdoiC3ReyAQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -11023,13 +11023,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router-dom": {
|
"node_modules/react-router-dom": {
|
||||||
"version": "7.8.1",
|
"version": "7.8.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.8.2.tgz",
|
||||||
"integrity": "sha512-NkgBCF3sVgCiAWIlSt89GR2PLaksMpoo3HDCorpRfnCEfdtRPLiuTf+CNXvqZMI5SJLZCLpVCvcZrTdtGW64xQ==",
|
"integrity": "sha512-Z4VM5mKDipal2jQ385H6UBhiiEDlnJPx6jyWsTYoZQdl5TrjxEV2a9yl3Fi60NBJxYzOTGTTHXPi0pdizvTwow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react-router": "7.8.1"
|
"react-router": "7.8.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.0.0"
|
"node": ">=20.0.0"
|
||||||
@@ -12358,7 +12358,7 @@
|
|||||||
"version": "10.9.2",
|
"version": "10.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cspotcode/source-map-support": "^0.8.0",
|
"@cspotcode/source-map-support": "^0.8.0",
|
||||||
@@ -12500,9 +12500,9 @@
|
|||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
"node_modules/tsx": {
|
"node_modules/tsx": {
|
||||||
"version": "4.20.4",
|
"version": "4.20.5",
|
||||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.4.tgz",
|
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.5.tgz",
|
||||||
"integrity": "sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg==",
|
"integrity": "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -12729,7 +12729,7 @@
|
|||||||
"version": "5.9.2",
|
"version": "5.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
|
||||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
@@ -12850,7 +12850,7 @@
|
|||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/v8-to-istanbul": {
|
"node_modules/v8-to-istanbul": {
|
||||||
@@ -13212,7 +13212,7 @@
|
|||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"axios": "^1.11.0",
|
"axios": "^1.11.0",
|
||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.2",
|
||||||
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.6.1",
|
"dotenv": "^16.6.1",
|
||||||
"dotenv-expand": "^12.0.2",
|
"dotenv-expand": "^12.0.2",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
@@ -79,6 +80,7 @@
|
|||||||
"@tailwindcss/postcss": "^4.1.12",
|
"@tailwindcss/postcss": "^4.1.12",
|
||||||
"@tailwindcss/vite": "^4.1.12",
|
"@tailwindcss/vite": "^4.1.12",
|
||||||
"@types/bcryptjs": "^3.0.0",
|
"@types/bcryptjs": "^3.0.0",
|
||||||
|
"@types/cors": "^2.8.19",
|
||||||
"@types/express": "^4.17.23",
|
"@types/express": "^4.17.23",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/jsonwebtoken": "^9.0.10",
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
|
|||||||
13
pnpm-lock.yaml
generated
13
pnpm-lock.yaml
generated
@@ -36,6 +36,9 @@ importers:
|
|||||||
bcryptjs:
|
bcryptjs:
|
||||||
specifier: ^3.0.2
|
specifier: ^3.0.2
|
||||||
version: 3.0.2
|
version: 3.0.2
|
||||||
|
cors:
|
||||||
|
specifier: ^2.8.5
|
||||||
|
version: 2.8.5
|
||||||
dotenv:
|
dotenv:
|
||||||
specifier: ^16.6.1
|
specifier: ^16.6.1
|
||||||
version: 16.6.1
|
version: 16.6.1
|
||||||
@@ -109,6 +112,9 @@ importers:
|
|||||||
'@types/bcryptjs':
|
'@types/bcryptjs':
|
||||||
specifier: ^3.0.0
|
specifier: ^3.0.0
|
||||||
version: 3.0.0
|
version: 3.0.0
|
||||||
|
'@types/cors':
|
||||||
|
specifier: ^2.8.19
|
||||||
|
version: 2.8.19
|
||||||
'@types/express':
|
'@types/express':
|
||||||
specifier: ^4.17.23
|
specifier: ^4.17.23
|
||||||
version: 4.17.23
|
version: 4.17.23
|
||||||
@@ -1444,6 +1450,9 @@ packages:
|
|||||||
'@types/cookiejar@2.1.5':
|
'@types/cookiejar@2.1.5':
|
||||||
resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==}
|
resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==}
|
||||||
|
|
||||||
|
'@types/cors@2.8.19':
|
||||||
|
resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==}
|
||||||
|
|
||||||
'@types/estree@1.0.8':
|
'@types/estree@1.0.8':
|
||||||
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
||||||
|
|
||||||
@@ -5411,6 +5420,10 @@ snapshots:
|
|||||||
|
|
||||||
'@types/cookiejar@2.1.5': {}
|
'@types/cookiejar@2.1.5': {}
|
||||||
|
|
||||||
|
'@types/cors@2.8.19':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 22.17.2
|
||||||
|
|
||||||
'@types/estree@1.0.8': {}
|
'@types/estree@1.0.8': {}
|
||||||
|
|
||||||
'@types/express-serve-static-core@4.19.6':
|
'@types/express-serve-static-core@4.19.6':
|
||||||
|
|||||||
134
src/controllers/openApiController.ts
Normal file
134
src/controllers/openApiController.ts
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import { Request, Response } from 'express';
|
||||||
|
import {
|
||||||
|
generateOpenAPISpec,
|
||||||
|
getAvailableServers,
|
||||||
|
getToolStats,
|
||||||
|
OpenAPIGenerationOptions
|
||||||
|
} from '../services/openApiGeneratorService.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for OpenAPI generation endpoints
|
||||||
|
* Provides OpenAPI specifications for MCP tools to enable OpenWebUI integration
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate and return OpenAPI specification
|
||||||
|
* GET /api/openapi.json
|
||||||
|
*/
|
||||||
|
export const getOpenAPISpec = (req: Request, res: Response): void => {
|
||||||
|
try {
|
||||||
|
const options: OpenAPIGenerationOptions = {
|
||||||
|
title: req.query.title as string,
|
||||||
|
description: req.query.description as string,
|
||||||
|
version: req.query.version as string,
|
||||||
|
serverUrl: req.query.serverUrl as string,
|
||||||
|
includeDisabledTools: req.query.includeDisabled === 'true',
|
||||||
|
groupFilter: req.query.group as string,
|
||||||
|
serverFilter: req.query.servers ? (req.query.servers as string).split(',') : undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
const openApiSpec = generateOpenAPISpec(options);
|
||||||
|
|
||||||
|
res.setHeader('Content-Type', 'application/json');
|
||||||
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||||
|
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
|
||||||
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
||||||
|
|
||||||
|
res.json(openApiSpec);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating OpenAPI specification:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
error: 'Failed to generate OpenAPI specification',
|
||||||
|
message: error instanceof Error ? error.message : 'Unknown error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get available servers for filtering
|
||||||
|
* GET /api/openapi/servers
|
||||||
|
*/
|
||||||
|
export const getOpenAPIServers = (req: Request, res: Response): void => {
|
||||||
|
try {
|
||||||
|
const servers = getAvailableServers();
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: servers
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting available servers:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Failed to get available servers',
|
||||||
|
message: error instanceof Error ? error.message : 'Unknown error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get tool statistics
|
||||||
|
* GET /api/openapi/stats
|
||||||
|
*/
|
||||||
|
export const getOpenAPIStats = (req: Request, res: Response): void => {
|
||||||
|
try {
|
||||||
|
const stats = getToolStats();
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: stats
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting tool statistics:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Failed to get tool statistics',
|
||||||
|
message: error instanceof Error ? error.message : 'Unknown error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute tool via OpenAPI-compatible endpoint
|
||||||
|
* This allows OpenWebUI to call MCP tools directly
|
||||||
|
* POST /api/tools/:serverName/:toolName
|
||||||
|
* GET /api/tools/:serverName/:toolName (for simple tools)
|
||||||
|
*/
|
||||||
|
export const executeToolViaOpenAPI = async (req: Request, res: Response): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const { serverName, toolName } = req.params;
|
||||||
|
|
||||||
|
// Import handleCallToolRequest function
|
||||||
|
const { handleCallToolRequest } = await import('../services/mcpService.js');
|
||||||
|
|
||||||
|
// Prepare arguments from query params (GET) or body (POST)
|
||||||
|
const args = req.method === 'GET'
|
||||||
|
? req.query
|
||||||
|
: req.body || {};
|
||||||
|
|
||||||
|
// Create a mock request structure that matches what handleCallToolRequest expects
|
||||||
|
const mockRequest = {
|
||||||
|
params: {
|
||||||
|
name: toolName, // Just use the tool name without server prefix as it gets added by handleCallToolRequest
|
||||||
|
arguments: args,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const extra = {
|
||||||
|
sessionId: req.headers['x-session-id'] as string || 'openapi-session',
|
||||||
|
server: serverName,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(`OpenAPI tool execution: ${serverName}/${toolName} with args:`, args);
|
||||||
|
|
||||||
|
const result = await handleCallToolRequest(mockRequest, extra);
|
||||||
|
|
||||||
|
// Return the result in OpenAPI format (matching MCP tool response structure)
|
||||||
|
res.json(result);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error executing tool via OpenAPI:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
error: 'Failed to execute tool',
|
||||||
|
message: error instanceof Error ? error.message : 'Unknown error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -63,6 +63,12 @@ import { callTool } from '../controllers/toolController.js';
|
|||||||
import { getPrompt } from '../controllers/promptController.js';
|
import { getPrompt } from '../controllers/promptController.js';
|
||||||
import { uploadDxtFile, uploadMiddleware } from '../controllers/dxtController.js';
|
import { uploadDxtFile, uploadMiddleware } from '../controllers/dxtController.js';
|
||||||
import { healthCheck } from '../controllers/healthController.js';
|
import { healthCheck } from '../controllers/healthController.js';
|
||||||
|
import {
|
||||||
|
getOpenAPISpec,
|
||||||
|
getOpenAPIServers,
|
||||||
|
getOpenAPIStats,
|
||||||
|
executeToolViaOpenAPI,
|
||||||
|
} from '../controllers/openApiController.js';
|
||||||
import { auth } from '../middlewares/auth.js';
|
import { auth } from '../middlewares/auth.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
@@ -180,6 +186,15 @@ export const initRoutes = (app: express.Application): void => {
|
|||||||
// Public configuration endpoint (no auth required to check skipAuth setting)
|
// Public configuration endpoint (no auth required to check skipAuth setting)
|
||||||
app.get(`${config.basePath}/public-config`, getPublicConfig);
|
app.get(`${config.basePath}/public-config`, getPublicConfig);
|
||||||
|
|
||||||
|
// OpenAPI generation endpoints (no auth required for OpenWebUI integration)
|
||||||
|
app.get(`${config.basePath}/api/openapi.json`, getOpenAPISpec);
|
||||||
|
app.get(`${config.basePath}/api/openapi/servers`, getOpenAPIServers);
|
||||||
|
app.get(`${config.basePath}/api/openapi/stats`, getOpenAPIStats);
|
||||||
|
|
||||||
|
// OpenAPI-compatible tool execution endpoints (no auth required for OpenWebUI integration)
|
||||||
|
app.get(`${config.basePath}/api/tools/:serverName/:toolName`, executeToolViaOpenAPI);
|
||||||
|
app.post(`${config.basePath}/api/tools/:serverName/:toolName`, executeToolViaOpenAPI);
|
||||||
|
|
||||||
app.use(`${config.basePath}/api`, router);
|
app.use(`${config.basePath}/api`, router);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
import cors from 'cors';
|
||||||
import config from './config/index.js';
|
import config from './config/index.js';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
@@ -26,6 +27,7 @@ export class AppServer {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.app = express();
|
this.app = express();
|
||||||
|
this.app.use(cors());
|
||||||
this.port = config.port;
|
this.port = config.port;
|
||||||
this.basePath = config.basePath;
|
this.basePath = config.basePath;
|
||||||
}
|
}
|
||||||
|
|||||||
316
src/services/openApiGeneratorService.ts
Normal file
316
src/services/openApiGeneratorService.ts
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
import { OpenAPIV3 } from 'openapi-types';
|
||||||
|
import { Tool } from '../types/index.js';
|
||||||
|
import { getServersInfo } from './mcpService.js';
|
||||||
|
import config from '../config/index.js';
|
||||||
|
import { loadSettings } from '../config/index.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service for generating OpenAPI 3.x specifications from MCP tools
|
||||||
|
* This enables integration with OpenWebUI and other OpenAPI-compatible systems
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface OpenAPIGenerationOptions {
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
version?: string;
|
||||||
|
serverUrl?: string;
|
||||||
|
includeDisabledTools?: boolean;
|
||||||
|
groupFilter?: string;
|
||||||
|
serverFilter?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert MCP tool input schema to OpenAPI parameter or request body schema
|
||||||
|
*/
|
||||||
|
function convertToolSchemaToOpenAPI(tool: Tool): {
|
||||||
|
parameters?: OpenAPIV3.ParameterObject[];
|
||||||
|
requestBody?: OpenAPIV3.RequestBodyObject;
|
||||||
|
} {
|
||||||
|
const schema = tool.inputSchema as any;
|
||||||
|
|
||||||
|
if (!schema || typeof schema !== 'object') {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// If schema has properties, convert them to parameters or request body
|
||||||
|
if (schema.properties && typeof schema.properties === 'object') {
|
||||||
|
const properties = schema.properties;
|
||||||
|
const required = Array.isArray(schema.required) ? schema.required : [];
|
||||||
|
|
||||||
|
// For simple tools with only primitive parameters, use query parameters
|
||||||
|
const hasComplexTypes = Object.values(properties).some(
|
||||||
|
(prop: any) =>
|
||||||
|
prop.type === 'object' ||
|
||||||
|
prop.type === 'array' ||
|
||||||
|
(prop.type === 'string' && prop.enum && prop.enum.length > 10),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasComplexTypes && Object.keys(properties).length <= 10) {
|
||||||
|
// Use query parameters for simple tools
|
||||||
|
const parameters: OpenAPIV3.ParameterObject[] = Object.entries(properties).map(
|
||||||
|
([name, prop]: [string, any]) => ({
|
||||||
|
name,
|
||||||
|
in: 'query',
|
||||||
|
required: required.includes(name),
|
||||||
|
description: prop.description || `Parameter ${name}`,
|
||||||
|
schema: {
|
||||||
|
type: prop.type || 'string',
|
||||||
|
...(prop.enum && { enum: prop.enum }),
|
||||||
|
...(prop.default !== undefined && { default: prop.default }),
|
||||||
|
...(prop.format && { format: prop.format }),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return { parameters };
|
||||||
|
} else {
|
||||||
|
// Use request body for complex tools
|
||||||
|
const requestBody: OpenAPIV3.RequestBodyObject = {
|
||||||
|
required: required.length > 0,
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
properties,
|
||||||
|
...(required.length > 0 && { required }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return { requestBody };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate OpenAPI operation from MCP tool
|
||||||
|
*/
|
||||||
|
function generateOperationFromTool(tool: Tool, serverName: string): OpenAPIV3.OperationObject {
|
||||||
|
const { parameters, requestBody } = convertToolSchemaToOpenAPI(tool);
|
||||||
|
const operation: OpenAPIV3.OperationObject = {
|
||||||
|
summary: tool.description || `Execute ${tool.name} tool`,
|
||||||
|
description: tool.description || `Execute the ${tool.name} tool from ${serverName} server`,
|
||||||
|
operationId: `${serverName}_${tool.name}`,
|
||||||
|
tags: [serverName],
|
||||||
|
...(parameters && parameters.length > 0 && { parameters }),
|
||||||
|
...(requestBody && { requestBody }),
|
||||||
|
responses: {
|
||||||
|
'200': {
|
||||||
|
description: 'Successful tool execution',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
content: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
type: { type: 'string' },
|
||||||
|
text: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isError: { type: 'boolean' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'400': {
|
||||||
|
description: 'Bad request - invalid parameters',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
error: { type: 'string' },
|
||||||
|
message: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'500': {
|
||||||
|
description: 'Internal server error',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
error: { type: 'string' },
|
||||||
|
message: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate OpenAPI specification from MCP tools
|
||||||
|
*/
|
||||||
|
export function generateOpenAPISpec(options: OpenAPIGenerationOptions = {}): OpenAPIV3.Document {
|
||||||
|
const serverInfos = getServersInfo();
|
||||||
|
|
||||||
|
// Filter servers based on options
|
||||||
|
const filteredServers = serverInfos.filter(
|
||||||
|
(server) =>
|
||||||
|
server.status === 'connected' &&
|
||||||
|
(!options.serverFilter || options.serverFilter.includes(server.name)),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Collect all tools from filtered servers
|
||||||
|
const allTools: Array<{ tool: Tool; serverName: string }> = [];
|
||||||
|
|
||||||
|
for (const serverInfo of filteredServers) {
|
||||||
|
const tools = options.includeDisabledTools
|
||||||
|
? serverInfo.tools
|
||||||
|
: serverInfo.tools.filter((tool) => tool.enabled !== false);
|
||||||
|
|
||||||
|
for (const tool of tools) {
|
||||||
|
allTools.push({ tool, serverName: serverInfo.name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate paths from tools
|
||||||
|
const paths: OpenAPIV3.PathsObject = {};
|
||||||
|
|
||||||
|
for (const { tool, serverName } of allTools) {
|
||||||
|
const operation = generateOperationFromTool(tool, serverName);
|
||||||
|
const { requestBody } = convertToolSchemaToOpenAPI(tool);
|
||||||
|
|
||||||
|
// Create path for the tool
|
||||||
|
const pathName = `/tools/${serverName}/${tool.name}`;
|
||||||
|
const method = requestBody ? 'post' : 'get';
|
||||||
|
|
||||||
|
if (!paths[pathName]) {
|
||||||
|
paths[pathName] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
paths[pathName][method] = operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = loadSettings();
|
||||||
|
// Get server URL
|
||||||
|
const baseUrl =
|
||||||
|
options.serverUrl ||
|
||||||
|
settings.systemConfig?.install?.baseUrl ||
|
||||||
|
`http://localhost:${config.port}`;
|
||||||
|
const serverUrl = `${baseUrl}${config.basePath}/api`;
|
||||||
|
|
||||||
|
// Generate OpenAPI document
|
||||||
|
const openApiDoc: OpenAPIV3.Document = {
|
||||||
|
openapi: '3.0.3',
|
||||||
|
info: {
|
||||||
|
title: options.title || 'MCPHub API',
|
||||||
|
description:
|
||||||
|
options.description ||
|
||||||
|
'OpenAPI specification for MCP tools managed by MCPHub. This enables integration with OpenWebUI and other OpenAPI-compatible systems.',
|
||||||
|
version: options.version || '1.0.0',
|
||||||
|
contact: {
|
||||||
|
name: 'MCPHub',
|
||||||
|
url: 'https://github.com/samanhappy/mcphub',
|
||||||
|
},
|
||||||
|
license: {
|
||||||
|
name: 'ISC',
|
||||||
|
url: 'https://github.com/samanhappy/mcphub/blob/main/LICENSE',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
servers: [
|
||||||
|
{
|
||||||
|
url: serverUrl,
|
||||||
|
description: 'MCPHub API Server',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
paths,
|
||||||
|
components: {
|
||||||
|
schemas: {
|
||||||
|
ToolResponse: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
content: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
type: { type: 'string' },
|
||||||
|
text: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isError: { type: 'boolean' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ErrorResponse: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
error: { type: 'string' },
|
||||||
|
message: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
securitySchemes: {
|
||||||
|
bearerAuth: {
|
||||||
|
type: 'http',
|
||||||
|
scheme: 'bearer',
|
||||||
|
bearerFormat: 'JWT',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tags: filteredServers.map((server) => ({
|
||||||
|
name: server.name,
|
||||||
|
description: `Tools from ${server.name} server`,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
return openApiDoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get available server names for filtering
|
||||||
|
*/
|
||||||
|
export function getAvailableServers(): string[] {
|
||||||
|
const serverInfos = getServersInfo();
|
||||||
|
return serverInfos.filter((server) => server.status === 'connected').map((server) => server.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get statistics about available tools
|
||||||
|
*/
|
||||||
|
export function getToolStats(): {
|
||||||
|
totalServers: number;
|
||||||
|
totalTools: number;
|
||||||
|
serverBreakdown: Array<{ name: string; toolCount: number; status: string }>;
|
||||||
|
} {
|
||||||
|
const serverInfos = getServersInfo();
|
||||||
|
|
||||||
|
const serverBreakdown = serverInfos.map((server) => ({
|
||||||
|
name: server.name,
|
||||||
|
toolCount: server.tools.length,
|
||||||
|
status: server.status,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const totalTools = serverInfos
|
||||||
|
.filter((server) => server.status === 'connected')
|
||||||
|
.reduce((sum, server) => sum + server.tools.length, 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalServers: serverInfos.filter((server) => server.status === 'connected').length,
|
||||||
|
totalTools,
|
||||||
|
serverBreakdown,
|
||||||
|
};
|
||||||
|
}
|
||||||
69
tests/services/openApiGeneratorService.test.ts
Normal file
69
tests/services/openApiGeneratorService.test.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { generateOpenAPISpec, getToolStats } from '../../src/services/openApiGeneratorService';
|
||||||
|
|
||||||
|
describe('OpenAPI Generator Service', () => {
|
||||||
|
describe('generateOpenAPISpec', () => {
|
||||||
|
it('should generate a valid OpenAPI specification', () => {
|
||||||
|
const spec = generateOpenAPISpec();
|
||||||
|
|
||||||
|
// Check basic structure
|
||||||
|
expect(spec).toHaveProperty('openapi');
|
||||||
|
expect(spec).toHaveProperty('info');
|
||||||
|
expect(spec).toHaveProperty('servers');
|
||||||
|
expect(spec).toHaveProperty('paths');
|
||||||
|
expect(spec).toHaveProperty('components');
|
||||||
|
|
||||||
|
// Check OpenAPI version
|
||||||
|
expect(spec.openapi).toBe('3.0.3');
|
||||||
|
|
||||||
|
// Check info section
|
||||||
|
expect(spec.info).toHaveProperty('title');
|
||||||
|
expect(spec.info).toHaveProperty('description');
|
||||||
|
expect(spec.info).toHaveProperty('version');
|
||||||
|
|
||||||
|
// Check components
|
||||||
|
expect(spec.components).toHaveProperty('schemas');
|
||||||
|
expect(spec.components).toHaveProperty('securitySchemes');
|
||||||
|
|
||||||
|
// Check security schemes
|
||||||
|
expect(spec.components?.securitySchemes).toHaveProperty('bearerAuth');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate spec with custom options', () => {
|
||||||
|
const options = {
|
||||||
|
title: 'Custom API',
|
||||||
|
description: 'Custom description',
|
||||||
|
version: '2.0.0',
|
||||||
|
serverUrl: 'https://custom.example.com'
|
||||||
|
};
|
||||||
|
|
||||||
|
const spec = generateOpenAPISpec(options);
|
||||||
|
|
||||||
|
expect(spec.info.title).toBe('Custom API');
|
||||||
|
expect(spec.info.description).toBe('Custom description');
|
||||||
|
expect(spec.info.version).toBe('2.0.0');
|
||||||
|
expect(spec.servers[0].url).toContain('https://custom.example.com');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle empty server list gracefully', () => {
|
||||||
|
const spec = generateOpenAPISpec();
|
||||||
|
|
||||||
|
// Should not throw and should have valid structure
|
||||||
|
expect(spec).toHaveProperty('paths');
|
||||||
|
expect(typeof spec.paths).toBe('object');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getToolStats', () => {
|
||||||
|
it('should return valid tool statistics', () => {
|
||||||
|
const stats = getToolStats();
|
||||||
|
|
||||||
|
expect(stats).toHaveProperty('totalServers');
|
||||||
|
expect(stats).toHaveProperty('totalTools');
|
||||||
|
expect(stats).toHaveProperty('serverBreakdown');
|
||||||
|
|
||||||
|
expect(typeof stats.totalServers).toBe('number');
|
||||||
|
expect(typeof stats.totalTools).toBe('number');
|
||||||
|
expect(Array.isArray(stats.serverBreakdown)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user