Compare commits

..

6 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
eb93edbe40 Enhance security documentation: add password generation instructions and reduce default credential exposure
Co-authored-by: samanhappy <2755122+samanhappy@users.noreply.github.com>
2025-10-31 14:53:06 +00:00
copilot-swe-agent[bot]
173ef5c0f3 Address security concerns: use env vars for passwords and add warnings about default credentials
Co-authored-by: samanhappy <2755122+samanhappy@users.noreply.github.com>
2025-10-31 14:50:31 +00:00
copilot-swe-agent[bot]
582be6e035 Add comprehensive examples directory README with navigation and troubleshooting guide
Co-authored-by: samanhappy <2755122+samanhappy@users.noreply.github.com>
2025-10-31 14:47:44 +00:00
copilot-swe-agent[bot]
9084772ade Update README files with Atlassian/Jira configuration examples and add screenshot guide
Co-authored-by: samanhappy <2755122+samanhappy@users.noreply.github.com>
2025-10-31 14:46:01 +00:00
copilot-swe-agent[bot]
ccd8ff50aa Add comprehensive Atlassian/Jira MCP server configuration examples and documentation
Co-authored-by: samanhappy <2755122+samanhappy@users.noreply.github.com>
2025-10-31 14:43:51 +00:00
copilot-swe-agent[bot]
420214259c Initial plan 2025-10-31 14:35:10 +00:00
28 changed files with 1507 additions and 1317 deletions

View File

@@ -9,9 +9,25 @@ RUN apt-get update && apt-get install -y curl gnupg git \
RUN npm install -g pnpm
ENV PNPM_HOME=/usr/local/share/pnpm
ENV PATH=$PNPM_HOME:$PATH
RUN mkdir -p $PNPM_HOME && \
ENV MCP_DATA_DIR=/app/data
ENV MCP_SERVERS_DIR=$MCP_DATA_DIR/servers
ENV MCP_NPM_DIR=$MCP_SERVERS_DIR/npm
ENV MCP_PYTHON_DIR=$MCP_SERVERS_DIR/python
ENV PNPM_HOME=$MCP_DATA_DIR/pnpm
ENV NPM_CONFIG_PREFIX=$MCP_DATA_DIR/npm-global
ENV NPM_CONFIG_CACHE=$MCP_DATA_DIR/npm-cache
ENV UV_TOOL_DIR=$MCP_DATA_DIR/uv/tools
ENV UV_CACHE_DIR=$MCP_DATA_DIR/uv/cache
ENV PATH=$PNPM_HOME:$NPM_CONFIG_PREFIX/bin:$UV_TOOL_DIR/bin:$PATH
RUN mkdir -p \
$PNPM_HOME \
$NPM_CONFIG_PREFIX/bin \
$NPM_CONFIG_PREFIX/lib/node_modules \
$NPM_CONFIG_CACHE \
$UV_TOOL_DIR \
$UV_CACHE_DIR \
$MCP_NPM_DIR \
$MCP_PYTHON_DIR && \
pnpm add -g @amap/amap-maps-mcp-server @playwright/mcp@latest tavily-mcp@latest @modelcontextprotocol/server-github @modelcontextprotocol/server-slack
ARG INSTALL_EXT=false

View File

@@ -57,6 +57,15 @@ Créez un fichier `mcp_settings.json` pour personnaliser les paramètres de votr
}
```
#### Exemples de Configuration
Pour des configurations spécifiques de serveurs MCP, consultez le répertoire [examples](./examples/) :
- **[Démarrage rapide Jira Cloud](./examples/QUICK_START_JIRA.md)** - Guide de configuration en 5 minutes pour Jira Cloud
- **[Guide complet Atlassian/Jira](./examples/README_ATLASSIAN_JIRA.md)** - Configuration détaillée pour Jira et Confluence
- **[Variables d'environnement](./examples/mcp_settings_with_env_vars.json)** - Utilisation de variables d'environnement dans la configuration
- **[OpenAPI Schema](./examples/openapi-schema-config.json)** - Serveurs MCP basés sur OpenAPI
### Déploiement avec Docker
**Recommandé** : Montez votre configuration personnalisée :

View File

@@ -59,6 +59,15 @@ Create a `mcp_settings.json` file to customize your server settings:
}
```
#### Configuration Examples
For specific MCP server configurations, see the [examples](./examples/) directory:
- **[Jira Cloud Quick Start](./examples/QUICK_START_JIRA.md)** - 5-minute setup guide for Jira Cloud
- **[Atlassian/Jira Complete Guide](./examples/README_ATLASSIAN_JIRA.md)** - Detailed setup for Jira and Confluence
- **[Environment Variables](./examples/mcp_settings_with_env_vars.json)** - Using environment variables in configuration
- **[OpenAPI Schema](./examples/openapi-schema-config.json)** - OpenAPI-based MCP servers
#### OAuth Configuration (Optional)
MCPHub supports OAuth 2.0 for authenticating with upstream MCP servers. See the [OAuth feature guide](docs/features/oauth.mdx) for a full walkthrough. In practice you will run into two configuration patterns:

View File

@@ -57,6 +57,15 @@ MCPHub 通过将多个 MCPModel Context Protocol服务器组织为灵活
}
```
#### 配置示例
有关特定 MCP 服务器配置,请参阅 [examples](./examples/) 目录:
- **[Jira Cloud 快速入门](./examples/QUICK_START_JIRA.md)** - Jira Cloud 5 分钟配置指南
- **[Atlassian/Jira 完整指南](./examples/README_ATLASSIAN_JIRA.md)** - Jira 和 Confluence 详细设置
- **[环境变量](./examples/mcp_settings_with_env_vars.json)** - 在配置中使用环境变量
- **[OpenAPI Schema](./examples/openapi-schema-config.json)** - 基于 OpenAPI 的 MCP 服务器
#### OAuth 配置(可选)
MCPHub 支持通过 OAuth 2.0 访问上游 MCP 服务器。完整说明请参阅[《OAuth 功能指南》](docs/zh/features/oauth.mdx)。实际使用中通常会遇到两类配置:

View File

@@ -1,175 +0,0 @@
# BASE_PATH Configuration Guide
## Overview
MCPHub supports running under a custom base path (e.g., `/mcphub/`) for scenarios where you need to deploy the application under a subdirectory or behind a reverse proxy.
## Configuration
### Setting BASE_PATH
Add the `BASE_PATH` environment variable to your `.env` file:
```bash
PORT=3000
NODE_ENV=development
BASE_PATH=/mcphub/
```
**Note:** Trailing slashes in BASE_PATH are automatically normalized (removed). Both `/mcphub/` and `/mcphub` will work and be normalized to `/mcphub`.
### In Production (Docker)
Set the environment variable when running the container:
```bash
docker run -e BASE_PATH=/mcphub/ -p 3000:3000 mcphub
```
### Behind a Reverse Proxy (nginx)
Example nginx configuration:
```nginx
location /mcphub/ {
proxy_pass http://localhost:3000/mcphub/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
```
## How It Works
### Backend Routes
All backend routes are automatically prefixed with BASE_PATH:
- **Without BASE_PATH:**
- Config: `http://localhost:3000/config`
- Auth: `http://localhost:3000/api/auth/login`
- Health: `http://localhost:3000/health`
- **With BASE_PATH="/mcphub":**
- Config: `http://localhost:3000/mcphub/config`
- Auth: `http://localhost:3000/mcphub/api/auth/login`
- Health: `http://localhost:3000/health` (global, no prefix)
### Frontend
The frontend automatically detects the BASE_PATH at runtime by calling the `/config` endpoint. All API calls are automatically prefixed.
### Development Mode
The Vite dev server proxy is automatically configured to support BASE_PATH:
1. Set `BASE_PATH` in your `.env` file
2. Start the dev server: `pnpm dev`
3. Access the application through Vite: `http://localhost:5173`
4. All API calls are proxied correctly with the BASE_PATH prefix
## Testing
You can test the BASE_PATH configuration with curl:
```bash
# Set BASE_PATH=/mcphub/ in .env file
# Test config endpoint
curl http://localhost:3000/mcphub/config
# Test login
curl -X POST http://localhost:3000/mcphub/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}'
```
## Troubleshooting
### Issue: Login fails with BASE_PATH set
**Solution:** Make sure you're using version 0.10.4 or later, which includes the fix for BASE_PATH in development mode.
### Issue: 404 errors on API endpoints
**Symptoms:**
- Login returns 404
- Config endpoint returns 404
- API calls fail with 404
**Solution:**
1. Verify BASE_PATH is set correctly in `.env` file
2. Restart the backend server to pick up the new configuration
3. Check that you're accessing the correct URL with the BASE_PATH prefix
### Issue: Vite proxy not working
**Solution:**
1. Ensure you have the latest version of `frontend/vite.config.ts`
2. Restart the frontend dev server
3. Verify the BASE_PATH is being loaded from the `.env` file in the project root
## Implementation Details
### Backend (src/config/index.ts)
```typescript
const normalizeBasePath = (path: string): string => {
if (!path) return '';
return path.replace(/\/+$/, '');
};
const defaultConfig = {
basePath: normalizeBasePath(process.env.BASE_PATH || ''),
// ...
};
```
### Frontend (frontend/vite.config.ts)
```typescript
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, path.resolve(__dirname, '..'), '');
let basePath = env.BASE_PATH || '';
basePath = basePath.replace(/\/+$/, '');
const proxyConfig: Record<string, any> = {};
const pathsToProxy = ['/api', '/config', '/public-config', '/health', '/oauth'];
pathsToProxy.forEach((path) => {
const proxyPath = basePath + path;
proxyConfig[proxyPath] = {
target: 'http://localhost:3000',
changeOrigin: true,
};
});
return {
server: {
proxy: proxyConfig,
},
};
});
```
### Frontend Runtime (frontend/src/utils/runtime.ts)
The frontend loads the BASE_PATH at runtime from the `/config` endpoint:
```typescript
export const loadRuntimeConfig = async (): Promise<RuntimeConfig> => {
// Tries different possible config paths
const response = await fetch('/config');
const data = await response.json();
return data.data; // Contains basePath, version, name
};
```
## Related Files
- `src/config/index.ts` - Backend BASE_PATH normalization
- `frontend/vite.config.ts` - Vite proxy configuration
- `frontend/src/utils/runtime.ts` - Frontend runtime config loading
- `tests/integration/base-path-routes.test.ts` - Integration tests

View File

@@ -207,6 +207,55 @@ MCPHub uses several configuration files:
}
```
### Productivity and Project Management
#### Atlassian (Jira & Confluence) Server
```json
{
"atlassian": {
"command": "uvx",
"args": [
"mcp-atlassian",
"--jira-url=${JIRA_URL}",
"--jira-username=${JIRA_USERNAME}",
"--jira-token=${JIRA_TOKEN}",
"--confluence-url=${CONFLUENCE_URL}",
"--confluence-username=${CONFLUENCE_USERNAME}",
"--confluence-token=${CONFLUENCE_TOKEN}"
],
"env": {}
}
}
```
**Jira Cloud Only Configuration:**
```json
{
"jira": {
"command": "uvx",
"args": [
"mcp-atlassian",
"--jira-url=${JIRA_URL}",
"--jira-username=${JIRA_USERNAME}",
"--jira-token=${JIRA_TOKEN}"
],
"env": {}
}
}
```
**Required Environment Variables:**
- `JIRA_URL`: Your Jira Cloud URL (e.g., `https://your-company.atlassian.net`)
- `JIRA_USERNAME`: Your Atlassian account email
- `JIRA_TOKEN`: API token from [Atlassian API Tokens](https://id.atlassian.com/manage-profile/security/api-tokens)
- `CONFLUENCE_URL`: Your Confluence URL (e.g., `https://your-company.atlassian.net/wiki`)
- `CONFLUENCE_USERNAME`: Your Confluence account email (often same as Jira)
- `CONFLUENCE_TOKEN`: Confluence API token (can be same as Jira token for Cloud)
**Setup Guide:** See [examples/README_ATLASSIAN_JIRA.md](../../examples/README_ATLASSIAN_JIRA.md) for detailed setup instructions.
### Map and Location Services
#### Amap (高德地图) Server

View File

@@ -78,7 +78,7 @@ git clone https://github.com/YOUR_USERNAME/mcphub.git
cd mcphub
# 2. Add upstream remote
git remote add upstream https://github.com/samanhappy/mcphub.git
git remote add upstream https://github.com/mcphub/mcphub.git
# 3. Install dependencies
pnpm install

View File

@@ -294,47 +294,22 @@ Optional for Smart Routing:
labels:
app: mcphub
spec:
initContainers:
- name: prepare-config
image: busybox:1.28
command:
[
"sh",
"-c",
"cp /config-ro/mcp_settings.json /etc/mcphub/mcp_settings.json",
]
volumeMounts:
- name: config
mountPath: /config-ro
readOnly: true
- name: app-storage
mountPath: /etc/mcphub
containers:
- name: mcphub
image: samanhappy/mcphub:latest
ports:
- containerPort: 3000
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
env:
- name: PORT
value: "3000"
- name: MCPHUB_SETTING_PATH
value: /etc/mcphub/mcp_settings.json
volumeMounts:
- name: app-storage
mountPath: /etc/mcphub
volumes:
- name: mcphub
image: samanhappy/mcphub:latest
ports:
- containerPort: 3000
env:
- name: PORT
value: "3000"
volumeMounts:
- name: config
configMap:
name: mcphub-config
- name: app-storage
emptyDir: {}
mountPath: /app/mcp_settings.json
subPath: mcp_settings.json
volumes:
- name: config
configMap:
name: mcphub-config
```
#### 3. Service

View File

@@ -48,7 +48,7 @@ docker --version
```bash
# 克隆主仓库
git clone https://github.com/samanhappy/mcphub.git
git clone https://github.com/mcphub/mcphub.git
cd mcphub
# 或者克隆您的 fork

View File

@@ -388,7 +388,7 @@ CMD ["node", "dist/index.js"]
````md
```bash
# 克隆 MCPHub 仓库
git clone https://github.com/samanhappy/mcphub.git
git clone https://github.com/mcphub/mcphub.git
cd mcphub
# 安装依赖
@@ -413,7 +413,7 @@ npm start
```bash
# 克隆 MCPHub 仓库
git clone https://github.com/samanhappy/mcphub.git
git clone https://github.com/mcphub/mcphub.git
cd mcphub
# 安装依赖
@@ -441,7 +441,7 @@ npm start
```powershell
# Windows PowerShell 安装步骤
# 克隆仓库
git clone https://github.com/samanhappy/mcphub.git
git clone https://github.com/mcphub/mcphub.git
Set-Location mcphub
# 安装 Node.js 依赖
@@ -458,7 +458,7 @@ npm run dev
```powershell
# Windows PowerShell 安装步骤
# 克隆仓库
git clone https://github.com/samanhappy/mcphub.git
git clone https://github.com/mcphub/mcphub.git
Set-Location mcphub
# 安装 Node.js 依赖

View File

@@ -331,7 +331,7 @@ MCPHub 文档支持以下图标库的图标:
"pages": [
{
"name": "GitHub 仓库",
"url": "https://github.com/samanhappy/mcphub",
"url": "https://github.com/mcphub/mcphub",
"icon": "github"
},
{
@@ -382,6 +382,7 @@ zh/
"pages": [
"zh/concepts/introduction",
"zh/concepts/architecture",
"zh/concepts/mcp-protocol",
"zh/concepts/routing"
]
}

View File

@@ -35,6 +35,9 @@ MCPHub 是一个现代化的 Model Context Protocol (MCP) 服务器管理平台
了解 MCPHub 的核心概念,为深入使用做好准备。
<CardGroup cols={2}>
<Card title="MCP 协议介绍" icon="network-wired" href="/zh/concepts/mcp-protocol">
深入了解 Model Context Protocol 的工作原理和最佳实践
</Card>
<Card title="智能路由机制" icon="route" href="/zh/features/smart-routing">
学习 MCPHub 的智能路由算法和配置策略
</Card>
@@ -54,6 +57,12 @@ MCPHub 支持多种部署方式,满足不同规模和场景的需求。
<Card title="Docker 部署" icon="docker" href="/zh/configuration/docker-setup">
使用 Docker 容器快速部署,支持单机和集群模式
</Card>
<Card title="云服务部署" icon="cloud" href="/zh/deployment/cloud">
在 AWS、GCP、Azure 等云平台上部署 MCPHub
</Card>
<Card title="Kubernetes" icon="dharmachakra" href="/zh/deployment/kubernetes">
在 Kubernetes 集群中部署高可用的 MCPHub 服务
</Card>
</CardGroup>
## API 和集成
@@ -64,6 +73,9 @@ MCPHub 提供完整的 RESTful API 和多语言 SDK方便与现有系统集
<Card title="API 参考文档" icon="code" href="/zh/api-reference/introduction">
完整的 API 接口文档,包含详细的请求示例和响应格式
</Card>
<Card title="SDK 和工具" icon="toolbox" href="/zh/sdk">
官方 SDK 和命令行工具,加速开发集成
</Card>
</CardGroup>
## 社区和支持
@@ -71,7 +83,7 @@ MCPHub 提供完整的 RESTful API 和多语言 SDK方便与现有系统集
加入 MCPHub 社区,获取帮助和分享经验。
<CardGroup cols={2}>
<Card title="GitHub 仓库" icon="github" href="https://github.com/samanhappy/mcphub">
<Card title="GitHub 仓库" icon="github" href="https://github.com/mcphub/mcphub">
查看源代码、提交问题和贡献代码
</Card>
<Card title="Discord 社区" icon="discord" href="https://discord.gg/mcphub">

View File

@@ -1,5 +1,27 @@
#!/bin/bash
DATA_DIR=${MCP_DATA_DIR:-/app/data}
SERVERS_DIR=${MCP_SERVERS_DIR:-$DATA_DIR/servers}
NPM_SERVER_DIR=${MCP_NPM_DIR:-$SERVERS_DIR/npm}
PYTHON_SERVER_DIR=${MCP_PYTHON_DIR:-$SERVERS_DIR/python}
PNPM_HOME=${PNPM_HOME:-$DATA_DIR/pnpm}
NPM_CONFIG_PREFIX=${NPM_CONFIG_PREFIX:-$DATA_DIR/npm-global}
NPM_CONFIG_CACHE=${NPM_CONFIG_CACHE:-$DATA_DIR/npm-cache}
UV_TOOL_DIR=${UV_TOOL_DIR:-$DATA_DIR/uv/tools}
UV_CACHE_DIR=${UV_CACHE_DIR:-$DATA_DIR/uv/cache}
mkdir -p \
"$PNPM_HOME" \
"$NPM_CONFIG_PREFIX/bin" \
"$NPM_CONFIG_PREFIX/lib/node_modules" \
"$NPM_CONFIG_CACHE" \
"$UV_TOOL_DIR" \
"$UV_CACHE_DIR" \
"$NPM_SERVER_DIR" \
"$PYTHON_SERVER_DIR"
export PATH="$PNPM_HOME:$NPM_CONFIG_PREFIX/bin:$UV_TOOL_DIR/bin:$PATH"
NPM_REGISTRY=${NPM_REGISTRY:-https://registry.npmjs.org/}
echo "Setting npm registry to ${NPM_REGISTRY}"
npm config set registry "$NPM_REGISTRY"

View File

@@ -0,0 +1,18 @@
# Atlassian Jira Cloud Configuration Example
# Copy this to your .env file and fill in your actual values
# Jira Configuration (Required)
JIRA_URL=https://your-company.atlassian.net
JIRA_USERNAME=your.email@company.com
JIRA_TOKEN=your_jira_api_token_here
# Confluence Configuration (Optional - only if you want to use Confluence)
CONFLUENCE_URL=https://your-company.atlassian.net/wiki
CONFLUENCE_USERNAME=your.email@company.com
CONFLUENCE_TOKEN=your_confluence_api_token_here
# Notes:
# 1. Get your API token from: https://id.atlassian.com/manage-profile/security/api-tokens
# 2. For Atlassian Cloud, you can often use the same API token for both Jira and Confluence
# 3. The username should be your Atlassian account email address
# 4. Never commit your .env file to version control

View File

@@ -0,0 +1,264 @@
# Atlassian/Jira Configuration Screenshot Guide
This guide shows what your configuration should look like at each step.
## 📋 Configuration File Structure
Your `mcp_settings.json` should look like this:
```json
{
"mcpServers": {
"jira": {
"command": "uvx",
"args": [
"mcp-atlassian",
"--jira-url=${JIRA_URL}",
"--jira-username=${JIRA_USERNAME}",
"--jira-token=${JIRA_TOKEN}"
]
}
},
"users": [
{
"username": "admin",
"password": "${ADMIN_PASSWORD_HASH}",
"isAdmin": true
}
]
}
```
**Security Note:** The `password` field must contain a bcrypt hash, not plain text.
**To generate a bcrypt hash:**
```bash
node -e "console.log(require('bcrypt').hashSync('your-password', 10))"
```
**⚠️ CRITICAL SECURITY:**
- Never use default credentials in production
- Always change the admin password before deploying
- Store password hashes, never plain text passwords
## 📁 File Structure
Your project should have these files:
```
mcphub/
├── mcp_settings.json ← Your configuration file
├── .env ← Your environment variables (DO NOT COMMIT!)
├── data/ ← Database directory (auto-created)
└── ...
```
## 🔐 Environment Variables (.env file)
```env
# .env file content
JIRA_URL=https://mycompany.atlassian.net
JIRA_USERNAME=myemail@company.com
JIRA_TOKEN=ATBBxxxxxxxxxxxxxxxxxxx
```
## 🎯 Expected Dashboard View
After starting MCPHub, you should see:
### 1. Server List View
```
┌─────────────────────────────────────────────────┐
│ MCP Servers │
├─────────────────────────────────────────────────┤
│ │
│ ✅ jira │
│ Status: Connected │
│ Type: stdio │
│ Command: uvx mcp-atlassian │
│ Tools: 15 available │
│ │
│ [View Details] [Restart] [Stop] │
│ │
└─────────────────────────────────────────────────┘
```
### 2. Server Details View
```
┌─────────────────────────────────────────────────┐
│ Server: jira │
├─────────────────────────────────────────────────┤
│ │
│ Status: ✅ Connected │
│ Type: stdio │
│ Command: uvx │
│ │
│ Available Tools: │
│ • jira_search_issues │
│ • jira_get_issue │
│ • jira_list_projects │
│ • jira_get_project │
│ • ... and 11 more │
│ │
│ Logs: │
│ [INFO] Successfully connected to Jira │
│ [INFO] Loaded 15 tools │
│ │
└─────────────────────────────────────────────────┘
```
### 3. Connection Endpoints
Once connected, your Jira server is available at:
| Endpoint | URL | Description |
|----------|-----|-------------|
| All Servers | `http://localhost:3000/mcp` | Access all configured MCP servers |
| Jira Only | `http://localhost:3000/mcp/jira` | Direct access to Jira server |
| SSE (Legacy) | `http://localhost:3000/sse/jira` | SSE endpoint for Jira |
## ✅ Success Indicators
You'll know the configuration is working when you see:
1. **✅ Green status indicator** next to the server name
2. **"Connected" status** in the server details
3. **Tool count showing** (e.g., "15 tools available")
4. **No error messages** in the logs
5. **Server responds** to health check requests
## ❌ Common Error Indicators
### Connection Failed
```
┌─────────────────────────────────────────────────┐
│ ❌ jira │
│ Status: Disconnected │
│ Error: Failed to start server │
│ Last error: 401 Unauthorized │
│ │
│ Possible causes: │
│ • Invalid API token │
│ • Wrong username/email │
│ • Incorrect Jira URL │
└─────────────────────────────────────────────────┘
```
### UVX Not Found
```
┌─────────────────────────────────────────────────┐
│ ❌ jira │
│ Status: Error │
│ Error: Command not found: uvx │
│ │
│ Solution: Install UV │
│ curl -LsSf https://astral.sh/uv/install.sh | sh │
└─────────────────────────────────────────────────┘
```
### Environment Variable Not Set
```
┌─────────────────────────────────────────────────┐
│ ⚠️ jira │
│ Status: Configuration Error │
│ Error: Environment variable JIRA_TOKEN not found │
│ │
│ Solution: Check your .env file │
└─────────────────────────────────────────────────┘
```
## 🧪 Testing Your Configuration
### Test 1: Health Check
```bash
curl http://localhost:3000/api/health
```
Expected response:
```json
{
"status": "ok",
"servers": {
"jira": "connected"
}
}
```
### Test 2: List Servers
```bash
curl http://localhost:3000/api/servers
```
Expected response:
```json
{
"servers": [
{
"name": "jira",
"status": "connected",
"type": "stdio",
"toolCount": 15
}
]
}
```
### Test 3: MCP Endpoint
```bash
curl http://localhost:3000/mcp/jira \
-H "Content-Type: application/json" \
-d '{
"method": "tools/list",
"params": {}
}'
```
Expected response: List of available Jira tools
## 📊 Log Messages Explained
### Successful Startup
```
[INFO] Loading configuration from mcp_settings.json
[INFO] Found 1 MCP server(s) to initialize
[INFO] Starting server: jira
[INFO] Executing: uvx mcp-atlassian --jira-url=https://...
[INFO] Successfully connected client for server: jira
[INFO] Successfully listed 15 tools for server: jira
[INFO] ✅ Server jira is ready
```
### Connection Issues
```
[ERROR] Failed to start server: jira
[ERROR] Error: spawn uvx ENOENT
[WARN] Server jira will retry in 5 seconds
```
### Authentication Issues
```
[ERROR] Failed to connect to Jira
[ERROR] HTTP 401: Unauthorized
[ERROR] Please check your API token and credentials
```
## 🔍 Debugging Steps
If your server shows as disconnected:
1. **Check logs** in the dashboard or console
2. **Verify environment variables** are set correctly
3. **Test manually** with uvx:
```bash
uvx mcp-atlassian --jira-url=https://your-company.atlassian.net --jira-username=your@email.com --jira-token=your_token
```
4. **Check network connectivity** to Jira
5. **Verify API token** is still valid
6. **Restart MCPHub** after making changes
## 📚 Additional Resources
- [Quick Start Guide](./QUICK_START_JIRA.md)
- [Complete Setup Guide](./README_ATLASSIAN_JIRA.md)
- [MCPHub Documentation](https://docs.mcphubx.com/)
- [Atlassian API Tokens](https://id.atlassian.com/manage-profile/security/api-tokens)

View File

@@ -0,0 +1,134 @@
# Quick Start: Jira Cloud Integration
This is a quick 5-minute setup guide for connecting MCPHub to Jira Cloud.
## ⚡ Quick Setup (5 minutes)
### Step 1: Get Your Jira API Token (2 minutes)
1. Go to https://id.atlassian.com/manage-profile/security/api-tokens
2. Click **"Create API token"**
3. Label it "MCPHub Integration"
4. **Copy the token** (you can't see it again!)
### Step 2: Find Your Jira URL (30 seconds)
Your Jira URL is what you see in your browser:
- Example: `https://mycompany.atlassian.net`
- ✅ Include: `https://` protocol
- ❌ Don't include: trailing `/` or `/jira`
### Step 3: Create .env File (1 minute)
Create a `.env` file in your MCPHub root directory:
```bash
JIRA_URL=https://mycompany.atlassian.net
JIRA_USERNAME=myemail@company.com
JIRA_TOKEN=paste_your_token_here
```
Replace with your actual values!
### Step 4: Update mcp_settings.json (1 minute)
Add this to your `mcp_settings.json`:
```json
{
"mcpServers": {
"jira": {
"command": "uvx",
"args": [
"mcp-atlassian",
"--jira-url=${JIRA_URL}",
"--jira-username=${JIRA_USERNAME}",
"--jira-token=${JIRA_TOKEN}"
]
}
}
}
```
### Step 5: Install UV & Start MCPHub (1 minute)
#### Install UV (if not already installed):
**macOS/Linux:**
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
**Windows:**
```powershell
irm https://astral.sh/uv/install.ps1 | iex
```
#### Start MCPHub:
**With Docker:**
```bash
docker run -p 3000:3000 \
--env-file .env \
-v ./mcp_settings.json:/app/mcp_settings.json \
samanhappy/mcphub
```
**Without Docker:**
```bash
pnpm install
pnpm dev
```
### Step 6: Verify Connection (30 seconds)
1. Open http://localhost:3000
2. Login with default credentials (see [README_ATLASSIAN_JIRA.md](./README_ATLASSIAN_JIRA.md#verification) for credentials)
**⚠️ CRITICAL:** Immediately change the admin password through dashboard Settings → Users
3. Check dashboard - you should see "jira" server as "Connected" ✅
## 🎉 That's It!
You can now use Jira through MCPHub at:
- All servers: `http://localhost:3000/mcp`
- Jira only: `http://localhost:3000/mcp/jira`
## 🐛 Common Issues
### "uvx command not found"
```bash
# Install UV first (see Step 5)
curl -LsSf https://astral.sh/uv/install.sh | sh
```
### "401 Unauthorized"
- Double-check your API token
- Make sure username is your email
- Try regenerating the API token
### Server shows "Disconnected"
- Check logs for specific errors
- Verify .env file is in the correct location
- Ensure no trailing slashes in JIRA_URL
### "Downloading cryptography" errors
- This is usually temporary
- Wait and restart MCPHub
- Check internet connection
## 📚 Need More Help?
See [README_ATLASSIAN_JIRA.md](./README_ATLASSIAN_JIRA.md) for the complete guide with:
- Both Jira + Confluence setup
- Detailed troubleshooting
- Security best practices
- Example use cases
## 🔒 Security Reminder
- ✅ Never commit `.env` to git
- ✅ Keep API tokens secret
- ✅ Rotate tokens regularly
- ✅ Use different tokens for dev/prod

233
examples/README.md Normal file
View File

@@ -0,0 +1,233 @@
# MCPHub Configuration Examples
This directory contains example configurations for various MCP servers and use cases.
## 📁 Directory Contents
### Atlassian/Jira Configuration
| File | Description | Best For |
|------|-------------|----------|
| [QUICK_START_JIRA.md](./QUICK_START_JIRA.md) | 5-minute quick start guide | Getting started fast with Jira Cloud |
| [README_ATLASSIAN_JIRA.md](./README_ATLASSIAN_JIRA.md) | Complete setup guide | Comprehensive setup with troubleshooting |
| [CONFIGURATION_SCREENSHOT_GUIDE.md](./CONFIGURATION_SCREENSHOT_GUIDE.md) | Visual configuration guide | Understanding the dashboard and logs |
| [mcp_settings_atlassian_jira.json](./mcp_settings_atlassian_jira.json) | Basic Jira configuration | Copy-paste configuration template |
| [.env.atlassian.example](./.env.atlassian.example) | Environment variables template | Setting up credentials securely |
### General Configuration Examples
| File | Description |
|------|-------------|
| [mcp_settings_with_env_vars.json](./mcp_settings_with_env_vars.json) | Environment variable examples for various server types (SSE, HTTP, stdio, OpenAPI) |
| [openapi-schema-config.json](./openapi-schema-config.json) | OpenAPI-based MCP server configuration examples |
## 🚀 Quick Start Guides
### For Jira Cloud Users
**New to MCPHub?** Start here: [QUICK_START_JIRA.md](./QUICK_START_JIRA.md)
This 5-minute guide covers:
- ✅ Getting your API token
- ✅ Basic configuration
- ✅ Starting MCPHub
- ✅ Verifying connection
### For Experienced Users
**Need detailed setup?** See: [README_ATLASSIAN_JIRA.md](./README_ATLASSIAN_JIRA.md)
This comprehensive guide includes:
- 📋 Both Jira and Confluence configuration
- 🔧 Multiple installation methods (uvx, python, docker)
- 🐛 Extensive troubleshooting section
- 🔒 Security best practices
- 💡 Example use cases
### Need Visual Guidance?
**Want to see what to expect?** Check: [CONFIGURATION_SCREENSHOT_GUIDE.md](./CONFIGURATION_SCREENSHOT_GUIDE.md)
This visual guide shows:
- 📊 Expected dashboard views
- ✅ Success indicators
- ❌ Common error messages
- 🧪 Test commands and expected outputs
## 📝 Configuration Templates
### Jira Cloud Only
Minimal configuration for Jira Cloud:
```json
{
"mcpServers": {
"jira": {
"command": "uvx",
"args": [
"mcp-atlassian",
"--jira-url=${JIRA_URL}",
"--jira-username=${JIRA_USERNAME}",
"--jira-token=${JIRA_TOKEN}"
]
}
}
}
```
### Jira + Confluence
Combined configuration:
```json
{
"mcpServers": {
"atlassian": {
"command": "uvx",
"args": [
"mcp-atlassian",
"--jira-url=${JIRA_URL}",
"--jira-username=${JIRA_USERNAME}",
"--jira-token=${JIRA_TOKEN}",
"--confluence-url=${CONFLUENCE_URL}",
"--confluence-username=${CONFLUENCE_USERNAME}",
"--confluence-token=${CONFLUENCE_TOKEN}"
]
}
}
}
```
### Environment Variables
Create a `.env` file based on [.env.atlassian.example](./.env.atlassian.example):
```env
JIRA_URL=https://your-company.atlassian.net
JIRA_USERNAME=your.email@company.com
JIRA_TOKEN=your_api_token_here
```
## 🔐 Security Best Practices
1. **Never commit sensitive data**
- ✅ Use `.env` files for credentials
- ✅ Add `.env` to `.gitignore`
- ✅ Use environment variable substitution: `${VAR_NAME}`
2. **Protect your API tokens**
- ✅ Rotate tokens regularly
- ✅ Use different tokens for dev/staging/prod
- ✅ Revoke unused tokens immediately
3. **Secure your configuration**
- ✅ Restrict file permissions on `.env` files
- ✅ Use secrets management in production
- ✅ Audit token usage regularly
## 🛠️ Common Use Cases
### Case 1: Development Environment
**Scenario**: Testing Jira integration locally
**Files needed**:
- `mcp_settings_atlassian_jira.json` → Copy to `mcp_settings.json`
- `.env.atlassian.example` → Copy to `.env` and fill in values
**Steps**:
1. Copy template files
2. Fill in your credentials
3. Run `pnpm dev`
### Case 2: Production Deployment
**Scenario**: Deploying MCPHub with Jira to production
**Approach**:
- Use environment variables in configuration
- Store secrets in your deployment platform's secrets manager
- Use Docker with environment file: `docker run --env-file .env ...`
### Case 3: Multiple Environments
**Scenario**: Separate dev, staging, prod configurations
**Structure**:
```
.env.development
.env.staging
.env.production
```
**Usage**:
```bash
# Development
docker run --env-file .env.development ...
# Staging
docker run --env-file .env.staging ...
# Production
docker run --env-file .env.production ...
```
## 🐛 Troubleshooting
### Quick Diagnostics
| Symptom | Likely Cause | Quick Fix |
|---------|--------------|-----------|
| "uvx command not found" | UV not installed | Install UV: `curl -LsSf https://astral.sh/uv/install.sh | sh` |
| "401 Unauthorized" | Wrong API token | Regenerate token at Atlassian settings |
| Server "Disconnected" | Missing env vars | Check `.env` file exists and has values |
| "Downloading cryptography" errors | Network/Python issue | Wait and retry, check internet connection |
### Detailed Troubleshooting
For comprehensive troubleshooting steps, see:
- [README_ATLASSIAN_JIRA.md - Troubleshooting Section](./README_ATLASSIAN_JIRA.md#troubleshooting)
- [CONFIGURATION_SCREENSHOT_GUIDE.md - Error Indicators](./CONFIGURATION_SCREENSHOT_GUIDE.md#-common-error-indicators)
## 📚 Additional Resources
### Official Documentation
- [MCPHub Documentation](https://docs.mcphubx.com/)
- [MCPHub GitHub Repository](https://github.com/samanhappy/mcphub)
- [MCP Protocol Specification](https://modelcontextprotocol.io/)
### Atlassian Resources
- [Atlassian API Tokens](https://id.atlassian.com/manage-profile/security/api-tokens)
- [Jira Cloud REST API](https://developer.atlassian.com/cloud/jira/platform/rest/v3/)
- [Confluence Cloud REST API](https://developer.atlassian.com/cloud/confluence/rest/v2/)
- [MCP Atlassian Server](https://github.com/sooperset/mcp-atlassian)
### Community Support
- [MCPHub Discord Community](https://discord.gg/qMKNsn5Q)
- [GitHub Issues](https://github.com/samanhappy/mcphub/issues)
- [GitHub Discussions](https://github.com/samanhappy/mcphub/discussions)
## 🤝 Contributing
Have a useful configuration example? We'd love to include it!
1. Create your example configuration
2. Add documentation explaining the setup
3. Submit a pull request to the repository
Example contributions:
- Configuration for other MCP servers
- Multi-server setup examples
- Docker Compose configurations
- Kubernetes deployment examples
- CI/CD integration examples
## 📄 License
All examples in this directory are provided under the same license as MCPHub (Apache 2.0).
Feel free to use, modify, and distribute these examples as needed for your projects.

View File

@@ -0,0 +1,319 @@
# Atlassian Jira Cloud MCP Server Configuration
This guide provides detailed instructions for configuring the MCP Atlassian server to connect to Jira Cloud in MCPHub.
## Prerequisites
1. **Jira Cloud Account**: You need access to a Jira Cloud instance
2. **API Token**: Generate an API token from your Atlassian account
3. **Python/UV**: The mcp-atlassian server requires Python and `uvx` (UV package manager)
## Step 1: Generate Jira API Token
1. Go to [Atlassian Account Settings](https://id.atlassian.com/manage-profile/security/api-tokens)
2. Click **"Create API token"**
3. Give it a label (e.g., "MCPHub Integration")
4. Copy the generated token (you won't be able to see it again!)
5. Save it securely
## Step 2: Get Your Jira Information
You'll need the following information:
- **JIRA_URL**: Your Jira Cloud URL (e.g., `https://your-company.atlassian.net`)
- **JIRA_USERNAME**: Your Atlassian account email (e.g., `your.email@company.com`)
- **JIRA_TOKEN**: The API token you generated in Step 1
## Step 3: Set Environment Variables
Create or update your `.env` file in the MCPHub root directory:
```bash
# Jira Configuration
JIRA_URL=https://your-company.atlassian.net
JIRA_USERNAME=your.email@company.com
JIRA_TOKEN=your_api_token_here
```
**Important Security Note**: Never commit your `.env` file to version control. It should be listed in `.gitignore`.
## Step 4: Configure MCPHub
### Option 1: Using Environment Variables (Recommended)
Update your `mcp_settings.json`:
```json
{
"mcpServers": {
"atlassian": {
"command": "uvx",
"args": [
"mcp-atlassian",
"--jira-url=${JIRA_URL}",
"--jira-username=${JIRA_USERNAME}",
"--jira-token=${JIRA_TOKEN}"
],
"env": {}
}
}
}
```
### Option 2: Direct Configuration (Not Recommended)
If you prefer not to use environment variables (less secure):
```json
{
"mcpServers": {
"atlassian": {
"command": "uvx",
"args": [
"mcp-atlassian",
"--jira-url=https://your-company.atlassian.net",
"--jira-username=your.email@company.com",
"--jira-token=your_api_token_here"
],
"env": {}
}
}
}
```
### Option 3: Jira Only (Without Confluence)
If you only want to use Jira and not Confluence:
```json
{
"mcpServers": {
"jira": {
"command": "uvx",
"args": [
"mcp-atlassian",
"--jira-url=${JIRA_URL}",
"--jira-username=${JIRA_USERNAME}",
"--jira-token=${JIRA_TOKEN}"
],
"env": {}
}
}
}
```
### Option 4: Both Jira and Confluence
To use both Jira and Confluence, you'll need API tokens for both:
```bash
# .env file
JIRA_URL=https://your-company.atlassian.net
JIRA_USERNAME=your.email@company.com
JIRA_TOKEN=your_jira_api_token
CONFLUENCE_URL=https://your-company.atlassian.net/wiki
CONFLUENCE_USERNAME=your.email@company.com
CONFLUENCE_TOKEN=your_confluence_api_token
```
```json
{
"mcpServers": {
"atlassian": {
"command": "uvx",
"args": [
"mcp-atlassian",
"--confluence-url=${CONFLUENCE_URL}",
"--confluence-username=${CONFLUENCE_USERNAME}",
"--confluence-token=${CONFLUENCE_TOKEN}",
"--jira-url=${JIRA_URL}",
"--jira-username=${JIRA_USERNAME}",
"--jira-token=${JIRA_TOKEN}"
],
"env": {}
}
}
}
```
**Note**: For Atlassian Cloud, you can often use the same API token for both Jira and Confluence.
## Step 5: Install UV (if not already installed)
The mcp-atlassian server uses `uvx` to run. Install UV if you haven't already:
### On macOS/Linux:
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
### On Windows:
```powershell
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
```
### Using pip:
```bash
pip install uv
```
## Step 6: Start MCPHub
### Using Docker:
```bash
docker run -p 3000:3000 \
-v ./mcp_settings.json:/app/mcp_settings.json \
-v ./data:/app/data \
-e JIRA_URL="${JIRA_URL}" \
-e JIRA_USERNAME="${JIRA_USERNAME}" \
-e JIRA_TOKEN="${JIRA_TOKEN}" \
samanhappy/mcphub
```
### Using Development Mode:
```bash
pnpm install
pnpm dev
```
### Using Production Mode:
```bash
pnpm install
pnpm build
pnpm start
```
## Verification
After starting MCPHub:
1. Open `http://localhost:3000` in your browser
2. Log in with default credentials: `admin` / `admin123`
**⚠️ SECURITY WARNING:** Change the default admin password immediately in production!
**To change the password:**
- Option 1: Use the dashboard after logging in (Settings → Users → Change Password)
- Option 2: Generate a bcrypt hash and update `mcp_settings.json`:
```bash
node -e "console.log(require('bcrypt').hashSync('your-new-password', 10))"
```
3. Check the dashboard to see if the Atlassian server is connected
4. Look for the server status - it should show as "Connected" or "Running"
5. Check the logs for any connection errors
## Troubleshooting
### Error: "uvx command not found"
**Solution**: Install UV as described in Step 5 above.
### Error: "Traceback (most recent call last): File ... mcp-atlassian"
This error usually indicates:
1. Missing or incorrect API credentials
2. Network connectivity issues
3. Python dependency issues
**Solutions**:
- Verify your API token is correct
- Ensure your Jira URL doesn't have trailing slashes
- Check that your username is the email address you use for Atlassian
- Verify network connectivity to your Jira instance
- Try regenerating your API token
### Error: "401 Unauthorized"
**Solution**:
- Double-check your API token is correct
- Ensure you're using the email address associated with your Atlassian account
- Regenerate your API token if needed
### Error: "403 Forbidden"
**Solution**:
- Check that your account has appropriate permissions in Jira
- Verify your Jira administrator hasn't restricted API access
### Error: Downloading cryptography errors
**Solution**:
- This is usually a transient network or Python package installation issue
- Wait a moment and try restarting MCPHub
- Ensure you have a stable internet connection
- If the issue persists, try installing mcp-atlassian manually:
```bash
uvx mcp-atlassian --help
```
### Server shows as "Disconnected"
**Solution**:
1. Check MCPHub logs for specific error messages
2. Verify all environment variables are set correctly
3. Test the connection manually:
```bash
uvx mcp-atlassian \
--jira-url=https://your-company.atlassian.net \
--jira-username=your.email@company.com \
--jira-token=your_token
```
## Using the Jira MCP Server
Once connected, you can use the Jira MCP server to:
- **Search Issues**: Query Jira issues using JQL
- **Read Issues**: Get detailed information about specific issues
- **Access Projects**: List and retrieve project metadata
- **View Comments**: Read issue comments and discussions
- **Get Transitions**: Check available status transitions for issues
Access the server through:
- **All servers**: `http://localhost:3000/mcp`
- **Specific server**: `http://localhost:3000/mcp/atlassian`
- **Server groups**: `http://localhost:3000/mcp/{group}` (if configured)
## Additional Resources
- [MCP Atlassian GitHub Repository](https://github.com/sooperset/mcp-atlassian)
- [Atlassian API Token Documentation](https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/)
- [Jira Cloud REST API](https://developer.atlassian.com/cloud/jira/platform/rest/v3/)
- [MCPHub Documentation](https://docs.mcphubx.com/)
## Security Best Practices
1. ✅ **Always use environment variables** for sensitive credentials
2. ✅ **Never commit `.env` files** to version control
3. ✅ **Rotate API tokens** regularly
4. ✅ **Use separate tokens** for different environments (dev, staging, prod)
5. ✅ **Restrict API token permissions** to only what's needed
6. ✅ **Monitor token usage** in Atlassian account settings
7. ✅ **Revoke unused tokens** immediately
## Example Use Cases
### Example 1: Search for Issues
Query: "List all open bugs assigned to me"
- Tool: `jira_search_issues`
- JQL: `project = MYPROJECT AND status = Open AND assignee = currentUser() AND type = Bug`
### Example 2: Get Issue Details
Query: "Show me details of issue PROJ-123"
- Tool: `jira_get_issue`
- Issue Key: `PROJ-123`
### Example 3: List Projects
Query: "What Jira projects do I have access to?"
- Tool: `jira_list_projects`
## Need Help?
If you're still experiencing issues:
1. Check the [MCPHub Discord community](https://discord.gg/qMKNsn5Q)
2. Review [MCPHub GitHub Issues](https://github.com/samanhappy/mcphub/issues)
3. Check [mcp-atlassian Issues](https://github.com/sooperset/mcp-atlassian/issues)
4. Contact your Jira administrator for API access questions

View File

@@ -0,0 +1,22 @@
{
"mcpServers": {
"atlassian": {
"command": "uvx",
"args": [
"mcp-atlassian",
"--jira-url=${JIRA_URL}",
"--jira-username=${JIRA_USERNAME}",
"--jira-token=${JIRA_TOKEN}"
],
"env": {}
}
},
"users": [
{
"username": "admin",
"_comment": "Password must be a bcrypt hash. Generate with: node -e \"console.log(require('bcrypt').hashSync('your-password', 10))\"",
"password": "${ADMIN_PASSWORD_HASH}",
"isAdmin": true
}
]
}

View File

@@ -11,8 +11,7 @@ const LanguageSwitch: React.FC = () => {
const availableLanguages = [
{ code: 'en', label: 'English' },
{ code: 'zh', label: '中文' },
{ code: 'fr', label: 'Français' },
{ code: 'tr', label: 'Türkçe' }
{ code: 'fr', label: 'Français' }
];
// Update current language when it changes

View File

@@ -6,7 +6,6 @@ import LanguageDetector from 'i18next-browser-languagedetector';
import enTranslation from '../../locales/en.json';
import zhTranslation from '../../locales/zh.json';
import frTranslation from '../../locales/fr.json';
import trTranslation from '../../locales/tr.json';
i18n
// Detect user language
@@ -25,9 +24,6 @@ i18n
fr: {
translation: frTranslation,
},
tr: {
translation: trTranslation,
},
},
fallbackLng: 'en',
debug: process.env.NODE_ENV === 'development',

View File

@@ -1,4 +1,4 @@
import { defineConfig, loadEnv } from 'vite';
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import tailwindcss from '@tailwindcss/vite';
@@ -8,48 +8,45 @@ import { readFileSync } from 'fs';
// Get package.json version
const packageJson = JSON.parse(readFileSync(path.resolve(__dirname, '../package.json'), 'utf-8'));
// For runtime configuration, we'll always use relative paths
// BASE_PATH will be determined at runtime
const basePath = '';
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
// Load env file from parent directory (project root)
const env = loadEnv(mode, path.resolve(__dirname, '..'), '');
// Get BASE_PATH from environment, default to empty string
// Normalize by removing trailing slashes to avoid double slashes
let basePath = env.BASE_PATH || '';
basePath = basePath.replace(/\/+$/, '');
// Create proxy configuration dynamically based on BASE_PATH
const proxyConfig: Record<string, any> = {};
// List of paths that need to be proxied
const pathsToProxy = ['/api', '/config', '/public-config', '/health', '/oauth'];
pathsToProxy.forEach((path) => {
const proxyPath = basePath + path;
proxyConfig[proxyPath] = {
target: 'http://localhost:3000',
changeOrigin: true,
};
});
return {
base: './', // Always use relative paths for runtime configuration
plugins: [react(), tailwindcss()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
export default defineConfig({
base: './', // Always use relative paths for runtime configuration
plugins: [react(), tailwindcss()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
define: {
// Make package version available as global variable
// BASE_PATH will be loaded at runtime
'import.meta.env.PACKAGE_VERSION': JSON.stringify(packageJson.version),
},
build: {
sourcemap: true, // Enable source maps for production build
},
server: {
proxy: {
[`${basePath}/api`]: {
target: 'http://localhost:3000',
changeOrigin: true,
},
[`${basePath}/auth`]: {
target: 'http://localhost:3000',
changeOrigin: true,
},
[`${basePath}/config`]: {
target: 'http://localhost:3000',
changeOrigin: true,
},
[`${basePath}/public-config`]: {
target: 'http://localhost:3000',
changeOrigin: true,
},
},
define: {
// Make package version available as global variable
// BASE_PATH will be loaded at runtime
'import.meta.env.PACKAGE_VERSION': JSON.stringify(packageJson.version),
},
build: {
sourcemap: true, // Enable source maps for production build
},
server: {
proxy: proxyConfig,
},
};
},
});

View File

@@ -1,747 +0,0 @@
{
"app": {
"title": "MCPHub Kontrol Paneli",
"error": "Hata",
"closeButton": "Kapat",
"noServers": "Kullanılabilir MCP sunucusu yok",
"loading": "Yükleniyor...",
"logout": ıkış Yap",
"profile": "Profil",
"changePassword": "Şifre Değiştir",
"toggleSidebar": "Kenar Çubuğunu Aç/Kapat",
"welcomeUser": "Hoş geldin, {{username}}",
"name": "MCPHub"
},
"about": {
"title": "Hakkında",
"versionInfo": "MCPHub Sürümü: {{version}}",
"newVersion": "Yeni sürüm mevcut!",
"currentVersion": "Mevcut sürüm",
"newVersionAvailable": "Yeni sürüm {{version}} mevcut",
"viewOnGitHub": "GitHub'da Görüntüle",
"checkForUpdates": "Güncellemeleri Kontrol Et",
"checking": "Güncellemeler kontrol ediliyor..."
},
"profile": {
"viewProfile": "Profili görüntüle",
"userCenter": "Kullanıcı Merkezi"
},
"sponsor": {
"label": "Sponsor",
"title": "Projeyi Destekle",
"rewardAlt": "Ödül QR Kodu",
"supportMessage": "Bana bir kahve ısmarlayarak MCPHub'ın geliştirilmesini destekleyin!",
"supportButton": "Ko-fi'de Destek Ol"
},
"wechat": {
"label": "WeChat",
"title": "WeChat ile Bağlan",
"qrCodeAlt": "WeChat QR Kodu",
"scanMessage": "WeChat'te bizimle bağlantı kurmak için bu QR kodunu tarayın"
},
"discord": {
"label": "Discord",
"title": "Discord sunucumuza katılın",
"community": "Destek, tartışmalar ve güncellemeler için büyüyen Discord topluluğumuza katılın!"
},
"theme": {
"title": "Tema",
"light": "Açık",
"dark": "Koyu",
"system": "Sistem"
},
"auth": {
"login": "Giriş Yap",
"loginTitle": "MCPHub'a Giriş Yap",
"slogan": "Birleşik MCP sunucu yönetim platformu",
"subtitle": "Model Context Protocol sunucuları için merkezi yönetim platformu. Esnek yönlendirme stratejileri ile birden fazla MCP sunucusunu organize edin, izleyin ve ölçeklendirin.",
"username": "Kullanıcı Adı",
"password": "Şifre",
"loggingIn": "Giriş yapılıyor...",
"emptyFields": "Kullanıcı adı ve şifre boş olamaz",
"loginFailed": "Giriş başarısız, lütfen kullanıcı adınızı ve şifrenizi kontrol edin",
"loginError": "Giriş sırasında bir hata oluştu",
"currentPassword": "Mevcut Şifre",
"newPassword": "Yeni Şifre",
"confirmPassword": "Şifreyi Onayla",
"passwordsNotMatch": "Yeni şifre ve onay eşleşmiyor",
"changePasswordSuccess": "Şifre başarıyla değiştirildi",
"changePasswordError": "Şifre değişikliği başarısız oldu",
"changePassword": "Şifre Değiştir",
"passwordChanged": "Şifre başarıyla değiştirildi",
"passwordChangeError": "Şifre değişikliği başarısız oldu",
"defaultPasswordWarning": "Varsayılan Şifre Güvenlik Uyarısı",
"defaultPasswordMessage": "Varsayılan şifreyi (admin123) kullanıyorsunuz, bu bir güvenlik riski oluşturur. Hesabınızı korumak için lütfen şifrenizi hemen değiştirin.",
"goToSettings": "Ayarlara Git",
"passwordStrengthError": "Şifre güvenlik gereksinimlerini karşılamıyor",
"passwordMinLength": "Şifre en az 8 karakter uzunluğunda olmalıdır",
"passwordRequireLetter": "Şifre en az bir harf içermelidir",
"passwordRequireNumber": "Şifre en az bir rakam içermelidir",
"passwordRequireSpecial": "Şifre en az bir özel karakter içermelidir",
"passwordStrengthHint": "Şifre en az 8 karakter olmalı ve harf, rakam ve özel karakter içermelidir"
},
"server": {
"addServer": "Sunucu Ekle",
"add": "Ekle",
"edit": "Düzenle",
"copy": "Kopyala",
"delete": "Sil",
"confirmDelete": "Bu sunucuyu silmek istediğinizden emin misiniz?",
"deleteWarning": "'{{name}}' sunucusunu silmek, onu ve tüm verilerini kaldıracaktır. Bu işlem geri alınamaz.",
"status": "Durum",
"tools": "Araçlar",
"prompts": "İstekler",
"name": "Sunucu Adı",
"url": "Sunucu URL'si",
"apiKey": "API Anahtarı",
"save": "Kaydet",
"cancel": "İptal",
"invalidConfig": "{{serverName}} için yapılandırma verisi bulunamadı",
"addError": "Sunucu eklenemedi",
"editError": "{{serverName}} sunucusu düzenlenemedi",
"deleteError": "{{serverName}} sunucusu silinemedi",
"updateError": "Sunucu güncellenemedi",
"editTitle": "Sunucuyu Düzenle: {{serverName}}",
"type": "Sunucu Türü",
"typeStdio": "STDIO",
"typeSse": "SSE",
"typeStreamableHttp": "Akış Yapılabilir HTTP",
"typeOpenapi": "OpenAPI",
"command": "Komut",
"arguments": "Argümanlar",
"envVars": "Ortam Değişkenleri",
"headers": "HTTP Başlıkları",
"key": "anahtar",
"value": "değer",
"enabled": "Etkin",
"enable": "Etkinleştir",
"disable": "Devre Dışı Bırak",
"requestOptions": "Bağlantı Yapılandırması",
"timeout": "İstek Zaman Aşımı",
"timeoutDescription": "MCP sunucusuna yapılan istekler için zaman aşımı (ms)",
"maxTotalTimeout": "Maksimum Toplam Zaman Aşımı",
"maxTotalTimeoutDescription": "MCP sunucusuna gönderilen istekler için maksimum toplam zaman aşımı (ms) (İlerleme bildirimleriyle kullanın)",
"resetTimeoutOnProgress": "İlerlemede Zaman Aşımını Sıfırla",
"resetTimeoutOnProgressDescription": "İlerleme bildirimlerinde zaman aşımını sıfırla",
"remove": "Kaldır",
"toggleError": "{{serverName}} sunucusu açılamadı/kapatılamadı",
"alreadyExists": "{{serverName}} sunucusu zaten mevcut",
"invalidData": "Geçersiz sunucu verisi sağlandı",
"notFound": "{{serverName}} sunucusu bulunamadı",
"namePlaceholder": "Sunucu adını girin",
"urlPlaceholder": "Sunucu URL'sini girin",
"commandPlaceholder": "Komutu girin",
"argumentsPlaceholder": "Argümanları girin",
"errorDetails": "Hata Detayları",
"viewErrorDetails": "Hata detaylarını görüntüle",
"copyConfig": "Yapılandırmayı Kopyala",
"confirmVariables": "Değişken Yapılandırmasını Onayla",
"variablesDetected": "Yapılandırmada değişkenler algılandı. Lütfen bu değişkenlerin düzgün yapılandırıldığını onaylayın:",
"detectedVariables": "Algılanan Değişkenler",
"confirmVariablesMessage": "Lütfen bu değişkenlerin çalışma ortamınızda düzgün tanımlandığından emin olun. Sunucu eklemeye devam edilsin mi?",
"confirmAndAdd": "Onayla ve Ekle",
"openapi": {
"inputMode": "Giriş Modu",
"inputModeUrl": "Şartname URL'si",
"inputModeSchema": "JSON Şeması",
"specUrl": "OpenAPI Şartname URL'si",
"schema": "OpenAPI JSON Şeması",
"schemaHelp": "Eksiksiz OpenAPI JSON şemanızı buraya yapıştırın",
"security": "Güvenlik Türü",
"securityNone": "Yok",
"securityApiKey": "API Anahtarı",
"securityHttp": "HTTP Kimlik Doğrulaması",
"securityOAuth2": "OAuth 2.0",
"securityOpenIdConnect": "OpenID Connect",
"apiKeyConfig": "API Anahtarı Yapılandırması",
"apiKeyName": "Başlık/Parametre Adı",
"apiKeyIn": "Konum",
"apiKeyValue": "API Anahtarı Değeri",
"httpAuthConfig": "HTTP Kimlik Doğrulama Yapılandırması",
"httpScheme": "Kimlik Doğrulama Şeması",
"httpCredentials": "Kimlik Bilgileri",
"httpSchemeBasic": "Basit",
"httpSchemeBearer": "Bearer",
"httpSchemeDigest": "Digest",
"oauth2Config": "OAuth 2.0 Yapılandırması",
"oauth2Token": "Erişim Anahtarı",
"openIdConnectConfig": "OpenID Connect Yapılandırması",
"openIdConnectUrl": "URL'yi Keşfet",
"openIdConnectToken": "ID Token",
"apiKeyInHeader": "Başlık",
"apiKeyInQuery": "Sorgu",
"apiKeyInCookie": "Çerez",
"passthroughHeaders": "Geçiş Başlıkları",
"passthroughHeadersHelp": "Araç çağrısı isteklerinden yukarı akış OpenAPI uç noktalarına geçirilecek başlık adlarının virgülle ayrılmış listesi (örn. Authorization, X-API-Key)"
},
"oauth": {
"sectionTitle": "OAuth Yapılandırması",
"sectionDescription": "OAuth korumalı sunucular için istemci kimlik bilgilerini yapılandırın (isteğe bağlı).",
"clientId": "İstemci ID",
"clientSecret": "İstemci Gizli Anahtarı",
"authorizationEndpoint": "Yetkilendirme Uç Noktası",
"tokenEndpoint": "Token Uç Noktası",
"scopes": "Kapsamlar",
"scopesPlaceholder": "scope1 scope2",
"resource": "Kaynak / Hedef Kitle",
"accessToken": "Erişim Tokeni",
"refreshToken": "Yenileme Tokeni"
}
},
"status": {
"online": "Çevrimiçi",
"offline": "Çevrimdışı",
"connecting": "Bağlanıyor",
"oauthRequired": "OAuth Gerekli",
"clickToAuthorize": "OAuth ile yetkilendirmek için tıklayın",
"oauthWindowOpened": "OAuth yetkilendirme penceresi açıldı. Lütfen yetkilendirmeyi tamamlayın."
},
"errors": {
"general": "Bir şeyler yanlış gitti",
"network": "Ağ bağlantı hatası. Lütfen internet bağlantınızı kontrol edin",
"serverConnection": "Sunucuya bağlanılamıyor. Lütfen sunucunun çalışıp çalışmadığını kontrol edin",
"serverAdd": "Sunucu eklenemedi. Lütfen sunucu durumunu kontrol edin",
"serverUpdate": "{{serverName}} sunucusu düzenlenemedi. Lütfen sunucu durumunu kontrol edin",
"serverFetch": "Sunucu verileri alınamadı. Lütfen daha sonra tekrar deneyin",
"initialStartup": "Sunucu başlatılıyor olabilir. İlk başlatmada bu işlem biraz zaman alabileceğinden lütfen bekleyin...",
"serverInstall": "Sunucu yüklenemedi",
"failedToFetchSettings": "Ayarlar getirilemedi",
"failedToUpdateRouteConfig": "Route yapılandırması güncellenemedi",
"failedToUpdateSmartRoutingConfig": "Akıllı yönlendirme yapılandırması güncellenemedi"
},
"common": {
"processing": "İşleniyor...",
"save": "Kaydet",
"cancel": "İptal",
"back": "Geri",
"refresh": "Yenile",
"create": "Oluştur",
"creating": "Oluşturuluyor...",
"update": "Güncelle",
"updating": "Güncelleniyor...",
"submitting": "Gönderiliyor...",
"delete": "Sil",
"remove": "Kaldır",
"copy": "Kopyala",
"copyId": "ID'yi Kopyala",
"copyUrl": "URL'yi Kopyala",
"copyJson": "JSON'u Kopyala",
"copySuccess": "Panoya kopyalandı",
"copyFailed": "Kopyalama başarısız",
"copied": "Kopyalandı",
"close": "Kapat",
"confirm": "Onayla",
"language": "Dil",
"true": "Doğru",
"false": "Yanlış",
"dismiss": "Anımsatma",
"github": "GitHub",
"wechat": "WeChat",
"discord": "Discord",
"required": "Gerekli",
"secret": "Gizli",
"default": "Varsayılan",
"value": "Değer",
"type": "Tür",
"repeated": "Tekrarlanan",
"valueHint": "Değer İpucu",
"choices": "Seçenekler"
},
"nav": {
"dashboard": "Kontrol Paneli",
"servers": "Sunucular",
"groups": "Gruplar",
"users": "Kullanıcılar",
"settings": "Ayarlar",
"changePassword": "Şifre Değiştir",
"market": "Market",
"cloud": "Bulut Market",
"logs": "Günlükler"
},
"pages": {
"dashboard": {
"title": "Kontrol Paneli",
"totalServers": "Toplam",
"onlineServers": "Çevrimiçi",
"offlineServers": "Çevrimdışı",
"connectingServers": "Bağlanıyor",
"recentServers": "Son Sunucular"
},
"servers": {
"title": "Sunucu Yönetimi"
},
"groups": {
"title": "Grup Yönetimi"
},
"users": {
"title": "Kullanıcı Yönetimi"
},
"settings": {
"title": "Ayarlar",
"language": "Dil",
"account": "Hesap Ayarları",
"password": "Şifre Değiştir",
"appearance": "Görünüm",
"routeConfig": "Güvenlik",
"installConfig": "Kurulum",
"smartRouting": "Akıllı Yönlendirme"
},
"market": {
"title": "Market Yönetimi - Yerel ve Bulut Marketler"
},
"logs": {
"title": "Sistem Günlükleri"
}
},
"logs": {
"filters": "Filtreler",
"search": "Günlüklerde ara...",
"autoScroll": "Otomatik kaydır",
"clearLogs": "Günlükleri temizle",
"loading": "Günlükler yükleniyor...",
"noLogs": "Kullanılabilir günlük yok.",
"noMatch": "Mevcut filtrelerle eşleşen günlük yok.",
"mainProcess": "Ana İşlem",
"childProcess": "Alt İşlem",
"main": "Ana",
"child": "Alt"
},
"groups": {
"add": "Ekle",
"addNew": "Yeni Grup Ekle",
"edit": "Grubu Düzenle",
"delete": "Sil",
"confirmDelete": "Bu grubu silmek istediğinizden emin misiniz?",
"deleteWarning": "'{{name}}' grubunu silmek, onu ve tüm sunucu ilişkilerini kaldıracaktır. Bu işlem geri alınamaz.",
"name": "Grup Adı",
"namePlaceholder": "Grup adını girin",
"nameRequired": "Grup adı gereklidir",
"description": "Açıklama",
"descriptionPlaceholder": "Grup açıklamasını girin (isteğe bağlı)",
"createError": "Grup oluşturulamadı",
"updateError": "Grup güncellenemedi",
"deleteError": "Grup silinemedi",
"serverAddError": "Sunucu gruba eklenemedi",
"serverRemoveError": "Sunucu gruptan kaldırılamadı",
"addServer": "Gruba Sunucu Ekle",
"selectServer": "Eklenecek bir sunucu seçin",
"servers": "Gruptaki Sunucular",
"remove": "Kaldır",
"noGroups": "Kullanılabilir grup yok. Başlamak için yeni bir grup oluşturun.",
"noServers": "Bu grupta sunucu yok.",
"noServerOptions": "Kullanılabilir sunucu yok",
"serverCount": "{{count}} Sunucu",
"toolSelection": "Araç Seçimi",
"toolsSelected": "Seçildi",
"allTools": "Tümü",
"selectedTools": "Seçili araçlar",
"selectAll": "Tümünü Seç",
"selectNone": "Hiçbirini Seçme",
"configureTools": "Araçları Yapılandır"
},
"market": {
"title": "Yerel Kurulum",
"official": "Resmi",
"by": "Geliştirici",
"unknown": "Bilinmeyen",
"tools": "araçlar",
"search": "Ara",
"searchPlaceholder": "Sunucuları isme, kategoriye veya etiketlere göre ara",
"clearFilters": "Temizle",
"clearCategoryFilter": "",
"clearTagFilter": "",
"categories": "Kategoriler",
"tags": "Etiketler",
"showTags": "Etiketleri göster",
"hideTags": "Etiketleri gizle",
"moreTags": "",
"noServers": "Aramanızla eşleşen sunucu bulunamadı",
"backToList": "Listeye dön",
"install": "Yükle",
"installing": "Yükleniyor...",
"installed": "Yüklendi",
"installServer": "Sunucu Yükle: {{name}}",
"installSuccess": "{{serverName}} sunucusu başarıyla yüklendi",
"author": "Yazar",
"license": "Lisans",
"repository": "Depo",
"examples": "Örnekler",
"arguments": "Argümanlar",
"argumentName": "Ad",
"description": "Açıklama",
"required": "Gerekli",
"example": "Örnek",
"viewSchema": "Şemayı görüntüle",
"fetchError": "Market sunucuları getirilirken hata",
"serverNotFound": "Sunucu bulunamadı",
"searchError": "Sunucular aranırken hata",
"filterError": "Sunucular kategoriye göre filtrelenirken hata",
"tagFilterError": "Sunucular etikete göre filtrelenirken hata",
"noInstallationMethod": "Bu sunucu için kullanılabilir kurulum yöntemi yok",
"showing": "{{total}} sunucudan {{from}}-{{to}} arası gösteriliyor",
"perPage": "Sayfa başına",
"confirmVariablesMessage": "Lütfen bu değişkenlerin çalışma ortamınızda düzgün tanımlandığından emin olun. Sunucu yüklemeye devam edilsin mi?",
"confirmAndInstall": "Onayla ve Yükle"
},
"cloud": {
"title": "Bulut Desteği",
"subtitle": "MCPRouter tarafından desteklenmektedir",
"by": "Geliştirici",
"server": "Sunucu",
"config": "Yapılandırma",
"created": "Oluşturuldu",
"updated": "Güncellendi",
"available": "Kullanılabilir",
"description": "Açıklama",
"details": "Detaylar",
"tools": "Araçlar",
"tool": "araç",
"toolsAvailable": "{{count}} araç mevcut",
"loadingTools": "Araçlar yükleniyor...",
"noTools": "Bu sunucu için kullanılabilir araç yok",
"noDescription": "Kullanılabilir açıklama yok",
"viewDetails": "Detayları Görüntüle",
"parameters": "Parametreler",
"result": "Sonuç",
"error": "Hata",
"callTool": "Çalıştır",
"calling": "Çalıştırılıyor...",
"toolCallSuccess": "{{toolName}} aracı başarıyla çalıştırıldı",
"toolCallError": "{{toolName}} aracı çalıştırılamadı: {{error}}",
"viewSchema": "Şemayı Görüntüle",
"backToList": "Bulut Market'e Dön",
"search": "Ara",
"searchPlaceholder": "Bulut sunucularını isme, başlığa veya geliştiriciye göre ara",
"clearFilters": "Filtreleri Temizle",
"clearCategoryFilter": "Temizle",
"clearTagFilter": "Temizle",
"categories": "Kategoriler",
"tags": "Etiketler",
"noCategories": "Kategori bulunamadı",
"noTags": "Etiket bulunamadı",
"noServers": "Bulut sunucusu bulunamadı",
"fetchError": "Bulut sunucuları getirilirken hata",
"serverNotFound": "Bulut sunucusu bulunamadı",
"searchError": "Bulut sunucuları aranırken hata",
"filterError": "Bulut sunucuları kategoriye göre filtrelenirken hata",
"tagFilterError": "Bulut sunucuları etikete göre filtrelenirken hata",
"showing": "{{total}} bulut sunucusundan {{from}}-{{to}} arası gösteriliyor",
"perPage": "Sayfa başına",
"apiKeyNotConfigured": "MCPRouter API anahtarı yapılandırılmamış",
"apiKeyNotConfiguredDescription": "Bulut sunucularını kullanmak için MCPRouter API anahtarınızı yapılandırmanız gerekir.",
"getApiKey": "API Anahtarı Al",
"configureInSettings": "Ayarlarda Yapılandır",
"installServer": "{{name}} Yükle",
"installSuccess": "{{name}} sunucusu başarıyla yüklendi",
"installError": "Sunucu yüklenemedi: {{error}}"
},
"registry": {
"title": "Kayıt",
"official": "Resmi",
"latest": "En Son",
"description": "Açıklama",
"website": "Web Sitesi",
"repository": "Depo",
"packages": "Paketler",
"package": "paket",
"remotes": "Uzak Sunucular",
"remote": "uzak sunucu",
"published": "Yayınlandı",
"updated": "Güncellendi",
"install": "Yükle",
"installing": "Yükleniyor...",
"installed": "Yüklendi",
"installServer": "{{name}} Yükle",
"installSuccess": "{{name}} sunucusu başarıyla yüklendi",
"installError": "Sunucu yüklenemedi: {{error}}",
"noDescription": "Kullanılabilir açıklama yok",
"viewDetails": "Detayları Görüntüle",
"backToList": "Kayda Dön",
"search": "Ara",
"searchPlaceholder": "Kayıt sunucularını isme göre ara",
"clearFilters": "Temizle",
"noServers": "Kayıt sunucusu bulunamadı",
"fetchError": "Kayıt sunucuları getirilirken hata",
"serverNotFound": "Kayıt sunucusu bulunamadı",
"showing": "{{total}} kayıt sunucusundan {{from}}-{{to}} arası gösteriliyor",
"perPage": "Sayfa başına",
"environmentVariables": "Ortam Değişkenleri",
"packageArguments": "Paket Argümanları",
"runtimeArguments": "Çalışma Zamanı Argümanları",
"headers": "Başlıklar"
},
"tool": {
"run": "Çalıştır",
"running": "Çalıştırılıyor...",
"runTool": "Aracı Çalıştır",
"cancel": "İptal",
"noDescription": "Kullanılabilir açıklama yok",
"inputSchema": "Giriş Şeması:",
"runToolWithName": "Aracı Çalıştır: {{name}}",
"execution": "Araç Çalıştırma",
"successful": "Başarılı",
"failed": "Başarısız",
"result": "Sonuç:",
"error": "Hata",
"errorDetails": "Hata Detayları:",
"noContent": "Araç başarıyla çalıştırıldı ancak içerik döndürmedi.",
"unknownError": "Bilinmeyen hata oluştu",
"jsonResponse": "JSON Yanıtı:",
"toolResult": "Araç sonucu",
"noParameters": "Bu araç herhangi bir parametre gerektirmez.",
"selectOption": "Bir seçenek seçin",
"enterValue": "{{type}} değeri girin",
"enabled": "Etkin",
"enableSuccess": "{{name}} aracı başarıyla etkinleştirildi",
"disableSuccess": "{{name}} aracı başarıyla devre dışı bırakıldı",
"toggleFailed": "Araç durumu değiştirilemedi",
"parameters": "Araç Parametreleri",
"formMode": "Form Modu",
"jsonMode": "JSON Modu",
"jsonConfiguration": "JSON Yapılandırması",
"invalidJsonFormat": "Geçersiz JSON formatı",
"fixJsonBeforeSwitching": "Form moduna geçmeden önce lütfen JSON formatını düzeltin",
"item": "Öğe {{index}}",
"addItem": "{{key}} öğesi ekle",
"enterKey": "{{key}} girin"
},
"prompt": {
"run": "Getir",
"running": "Getiriliyor...",
"result": "İstek Sonucu",
"error": "İstek Hatası",
"execution": "İstek Çalıştırma",
"successful": "Başarılı",
"failed": "Başarısız",
"errorDetails": "Hata Detayları:",
"noContent": "İstek başarıyla çalıştırıldı ancak içerik döndürmedi.",
"unknownError": "Bilinmeyen hata oluştu",
"jsonResponse": "JSON Yanıtı:",
"description": "Açıklama",
"messages": "Mesajlar",
"noDescription": "Kullanılabilir açıklama yok",
"runPromptWithName": "İsteği Getir: {{name}}"
},
"settings": {
"enableGlobalRoute": "Global Yönlendirmeyi Etkinleştir",
"enableGlobalRouteDescription": "Grup ID'si belirtmeden /sse uç noktasına bağlantıya izin ver",
"enableGroupNameRoute": "Grup Adı Yönlendirmeyi Etkinleştir",
"enableGroupNameRouteDescription": "Sadece grup ID'leri yerine grup adları kullanarak /sse uç noktasına bağlantıya izin ver",
"enableBearerAuth": "Bearer Kimlik Doğrulamasını Etkinleştir",
"enableBearerAuthDescription": "MCP istekleri için bearer token kimlik doğrulaması gerektir",
"bearerAuthKey": "Bearer Kimlik Doğrulama Anahtarı",
"bearerAuthKeyDescription": "Bearer token'da gerekli olacak kimlik doğrulama anahtarı",
"bearerAuthKeyPlaceholder": "Bearer kimlik doğrulama anahtarını girin",
"skipAuth": "Kimlik Doğrulamayı Atla",
"skipAuthDescription": "Arayüz ve API erişimi için giriş gereksinimini atla (Güvenlik için VARSAYILAN KAPALI)",
"pythonIndexUrl": "Python Paket Deposu URL'si",
"pythonIndexUrlDescription": "Python paket kurulumu için UV_DEFAULT_INDEX ortam değişkenini ayarla",
"pythonIndexUrlPlaceholder": "örn. https://pypi.org/simple",
"npmRegistry": "NPM Kayıt URL'si",
"npmRegistryDescription": "NPM paket kurulumu için npm_config_registry ortam değişkenini ayarla",
"npmRegistryPlaceholder": "örn. https://registry.npmjs.org/",
"baseUrl": "Temel URL",
"baseUrlDescription": "MCP istekleri için temel URL",
"baseUrlPlaceholder": "örn. http://localhost:3000",
"installConfig": "Kurulum",
"systemConfigUpdated": "Sistem yapılandırması başarıyla güncellendi",
"enableSmartRouting": "Akıllı Yönlendirmeyi Etkinleştir",
"enableSmartRoutingDescription": "Girdiye göre en uygun aracı aramak için akıllı yönlendirme özelliğini etkinleştir ($smart grup adını kullanarak)",
"dbUrl": "PostgreSQL URL'si (pgvector desteği gerektirir)",
"dbUrlPlaceholder": "örn. postgresql://kullanıcı:şifre@localhost:5432/veritabanıadı",
"openaiApiBaseUrl": "OpenAI API Temel URL'si",
"openaiApiBaseUrlPlaceholder": "https://api.openai.com/v1",
"openaiApiKey": "OpenAI API Anahtarı",
"openaiApiKeyPlaceholder": "OpenAI API anahtarını girin",
"openaiApiEmbeddingModel": "OpenAI Entegrasyon Modeli",
"openaiApiEmbeddingModelPlaceholder": "text-embedding-3-small",
"smartRoutingConfigUpdated": "Akıllı yönlendirme yapılandırması başarıyla güncellendi",
"smartRoutingRequiredFields": "Akıllı yönlendirmeyi etkinleştirmek için Veritabanı URL'si ve OpenAI API Anahtarı gereklidir",
"smartRoutingValidationError": "Akıllı Yönlendirmeyi etkinleştirmeden önce lütfen gerekli alanları doldurun: {{fields}}",
"mcpRouterConfig": "Bulut Market",
"mcpRouterApiKey": "MCPRouter API Anahtarı",
"mcpRouterApiKeyDescription": "MCPRouter bulut market hizmetlerine erişim için API anahtarı",
"mcpRouterApiKeyPlaceholder": "MCPRouter API anahtarını girin",
"mcpRouterReferer": "Yönlendiren",
"mcpRouterRefererDescription": "MCPRouter API istekleri için Referer başlığı",
"mcpRouterRefererPlaceholder": "https://www.mcphubx.com",
"mcpRouterTitle": "Başlık",
"mcpRouterTitleDescription": "MCPRouter API istekleri için Başlık başlığı",
"mcpRouterTitlePlaceholder": "MCPHub",
"mcpRouterBaseUrl": "Temel URL",
"mcpRouterBaseUrlDescription": "MCPRouter API için temel URL",
"mcpRouterBaseUrlPlaceholder": "https://api.mcprouter.to/v1",
"systemSettings": "Sistem Ayarları",
"nameSeparatorLabel": "İsim Ayırıcı",
"nameSeparatorDescription": "Sunucu adı ile araç/istek adını ayırmak için kullanılan karakter (varsayılan: -)",
"restartRequired": "Yapılandırma kaydedildi. Tüm hizmetlerin yeni ayarları doğru şekilde yüklemesini sağlamak için uygulamayı yeniden başlatmanız önerilir.",
"exportMcpSettings": "Ayarları Dışa Aktar",
"mcpSettingsJson": "MCP Ayarları JSON",
"mcpSettingsJsonDescription": "Yedekleme veya diğer araçlara taşıma için mevcut mcp_settings.json yapılandırmanızı görüntüleyin, kopyalayın veya indirin",
"copyToClipboard": "Panoya Kopyala",
"downloadJson": "JSON Olarak İndir",
"exportSuccess": "Ayarlar başarıyla dışa aktarıldı",
"exportError": "Ayarlar getirilemedi"
},
"dxt": {
"upload": "Yükle",
"uploadTitle": "DXT Uzantısı Yükle",
"dropFileHere": ".dxt dosyanızı buraya bırakın",
"orClickToSelect": "veya bilgisayarınızdan seçmek için tıklayın",
"invalidFileType": "Lütfen geçerli bir .dxt dosyası seçin",
"noFileSelected": "Lütfen yüklemek için bir .dxt dosyası seçin",
"uploading": "Yükleniyor...",
"uploadFailed": "DXT dosyası yüklenemedi",
"installServer": "DXT'den MCP Sunucusu Yükle",
"extensionInfo": "Uzantı Bilgisi",
"name": "Ad",
"version": "Sürüm",
"description": "Açıklama",
"author": "Geliştirici",
"tools": "Araçlar",
"serverName": "Sunucu Adı",
"serverNamePlaceholder": "Bu sunucu için bir ad girin",
"install": "Yükle",
"installing": "Yükleniyor...",
"installFailed": "DXT'den sunucu yüklenemedi",
"serverExistsTitle": "Sunucu Zaten Mevcut",
"serverExistsConfirm": "'{{serverName}}' sunucusu zaten mevcut. Yeni sürümle geçersiz kılmak istiyor musunuz?",
"override": "Geçersiz Kıl"
},
"jsonImport": {
"button": "İçe Aktar",
"title": "JSON'dan Sunucuları İçe Aktar",
"inputLabel": "Sunucu Yapılandırma JSON",
"inputHelp": "Sunucu yapılandırma JSON'unuzu yapıştırın. STDIO, SSE ve HTTP (streamable-http) sunucu türlerini destekler.",
"preview": "Önizle",
"previewTitle": "İçe Aktarılacak Sunucuları Önizle",
"import": "İçe Aktar",
"importing": "İçe aktarılıyor...",
"invalidFormat": "Geçersiz JSON formatı. JSON bir 'mcpServers' nesnesi içermelidir.",
"parseError": "JSON ayrıştırılamadı. Lütfen formatı kontrol edip tekrar deneyin.",
"addFailed": "Sunucu eklenemedi",
"importFailed": "Sunucular içe aktarılamadı",
"partialSuccess": "{{total}} sunucudan {{count}} tanesi başarıyla içe aktarıldı. Bazı sunucular başarısız oldu:"
},
"users": {
"add": "Kullanıcı Ekle",
"addNew": "Yeni Kullanıcı Ekle",
"edit": "Kullanıcıyı Düzenle",
"delete": "Kullanıcıyı Sil",
"create": "Kullanıcı Oluştur",
"update": "Kullanıcıyı Güncelle",
"username": "Kullanıcı Adı",
"password": "Şifre",
"newPassword": "Yeni Şifre",
"confirmPassword": "Şifreyi Onayla",
"adminRole": "Yönetici",
"admin": "Yönetici",
"user": "Kullanıcı",
"permissions": "İzinler",
"adminPermissions": "Tam sistem erişimi",
"userPermissions": "Sınırlı erişim",
"currentUser": "Siz",
"noUsers": "Kullanıcı bulunamadı",
"adminRequired": "Kullanıcıları yönetmek için yönetici erişimi gereklidir",
"usernameRequired": "Kullanıcı adı gereklidir",
"passwordRequired": "Şifre gereklidir",
"passwordTooShort": "Şifre en az 6 karakter uzunluğunda olmalıdır",
"passwordMismatch": "Şifreler eşleşmiyor",
"usernamePlaceholder": "Kullanıcı adını girin",
"passwordPlaceholder": "Şifreyi girin",
"newPasswordPlaceholder": "Mevcut şifreyi korumak için boş bırakın",
"confirmPasswordPlaceholder": "Yeni şifreyi onaylayın",
"createError": "Kullanıcı oluşturulamadı",
"updateError": "Kullanıcı güncellenemedi",
"deleteError": "Kullanıcı silinemedi",
"statsError": "Kullanıcı istatistikleri getirilemedi",
"deleteConfirmation": "'{{username}}' kullanıcısını silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.",
"confirmDelete": "Kullanıcıyı Sil",
"deleteWarning": "'{{username}}' kullanıcısını silmek istediğinizden emin misiniz? Bu işlem geri alınamaz."
},
"api": {
"errors": {
"readonly": "Demo ortamı için salt okunur",
"invalid_credentials": "Geçersiz kullanıcı adı veya şifre",
"serverNameRequired": "Sunucu adı gereklidir",
"serverConfigRequired": "Sunucu yapılandırması gereklidir",
"serverConfigInvalid": "Sunucu yapılandırması bir URL, OpenAPI şartname URL'si veya şema, ya da argümanlı komut içermelidir",
"serverTypeInvalid": "Sunucu türü şunlardan biri olmalıdır: stdio, sse, streamable-http, openapi",
"urlRequiredForType": "{{type}} sunucu türü için URL gereklidir",
"openapiSpecRequired": "OpenAPI sunucu türü için OpenAPI şartname URL'si veya şema gereklidir",
"headersInvalidFormat": "Başlıklar bir nesne olmalıdır",
"headersNotSupportedForStdio": "Başlıklar stdio sunucu türü için desteklenmez",
"serverNotFound": "Sunucu bulunamadı",
"failedToRemoveServer": "Sunucu bulunamadı veya kaldırılamadı",
"internalServerError": "Dahili sunucu hatası",
"failedToGetServers": "Sunucu bilgileri alınamadı",
"failedToGetServerSettings": "Sunucu ayarları alınamadı",
"failedToGetServerConfig": "Sunucu yapılandırması alınamadı",
"failedToSaveSettings": "Ayarlar kaydedilemedi",
"toolNameRequired": "Sunucu adı ve araç adı gereklidir",
"descriptionMustBeString": "Açıklama bir string olmalıdır",
"groupIdRequired": "Grup ID gereklidir",
"groupNameRequired": "Grup adı gereklidir",
"groupNotFound": "Grup bulunamadı",
"groupIdAndServerNameRequired": "Grup ID ve sunucu adı gereklidir",
"groupOrServerNotFound": "Grup veya sunucu bulunamadı",
"toolsMustBeAllOrArray": "Araçlar \"all\" veya bir string dizisi olmalıdır",
"serverNameAndToolNameRequired": "Sunucu adı ve araç adı gereklidir",
"usernameRequired": "Kullanıcı adı gereklidir",
"userNotFound": "Kullanıcı bulunamadı",
"failedToGetUsers": "Kullanıcı bilgileri alınamadı",
"failedToGetUserInfo": "Kullanıcı bilgisi alınamadı",
"failedToGetUserStats": "Kullanıcı istatistikleri alınamadı",
"marketServerNameRequired": "Sunucu adı gereklidir",
"marketServerNotFound": "Market sunucusu bulunamadı",
"failedToGetMarketServers": "Market sunucuları bilgisi alınamadı",
"failedToGetMarketServer": "Market sunucusu bilgisi alınamadı",
"failedToGetMarketCategories": "Market kategorileri alınamadı",
"failedToGetMarketTags": "Market etiketleri alınamadı",
"failedToSearchMarketServers": "Market sunucuları aranamadı",
"failedToFilterMarketServers": "Market sunucuları filtrelenemedi",
"failedToProcessDxtFile": "DXT dosyası işlenemedi"
},
"success": {
"serverCreated": "Sunucu başarıyla oluşturuldu",
"serverUpdated": "Sunucu başarıyla güncellendi",
"serverRemoved": "Sunucu başarıyla kaldırıldı",
"serverToggled": "Sunucu durumu başarıyla değiştirildi",
"toolToggled": "{{name}} aracı başarıyla {{action}}",
"toolDescriptionUpdated": "{{name}} aracının açıklaması başarıyla güncellendi",
"systemConfigUpdated": "Sistem yapılandırması başarıyla güncellendi",
"groupCreated": "Grup başarıyla oluşturuldu",
"groupUpdated": "Grup başarıyla güncellendi",
"groupDeleted": "Grup başarıyla silindi",
"serverAddedToGroup": "Sunucu başarıyla gruba eklendi",
"serverRemovedFromGroup": "Sunucu başarıyla gruptan kaldırıldı",
"serverToolsUpdated": "Sunucu araçları başarıyla güncellendi"
}
},
"oauthCallback": {
"authorizationFailed": "Yetkilendirme Başarısız",
"authorizationFailedError": "Hata",
"authorizationFailedDetails": "Detaylar",
"invalidRequest": "Geçersiz İstek",
"missingStateParameter": "Gerekli OAuth durum parametresi eksik.",
"missingCodeParameter": "Gerekli yetkilendirme kodu parametresi eksik.",
"serverNotFound": "Sunucu Bulunamadı",
"serverNotFoundMessage": "Bu yetkilendirme isteğiyle ilişkili sunucu bulunamadı.",
"sessionExpiredMessage": "Yetkilendirme oturumunun süresi dolmuş olabilir. Lütfen tekrar yetkilendirmeyi deneyin.",
"authorizationSuccessful": "Yetkilendirme Başarılı",
"server": "Sunucu",
"status": "Durum",
"connected": "Bağlandı",
"successMessage": "Sunucu başarıyla yetkilendirildi ve bağlandı.",
"autoCloseMessage": "Bu pencere 3 saniye içinde otomatik olarak kapanacak...",
"closeNow": "Şimdi Kapat",
"connectionError": "Bağlantı Hatası",
"connectionErrorMessage": "Yetkilendirme başarılı oldu, ancak sunucuya bağlanılamadı.",
"reconnectMessage": "Lütfen kontrol panelinden yeniden bağlanmayı deneyin.",
"configurationError": "Yapılandırma Hatası",
"configurationErrorMessage": "Sunucu aktarımı OAuth finishAuth() desteklemiyor. Lütfen sunucunun streamable-http aktarımıyla yapılandırıldığından emin olun.",
"internalError": "İçsel Hata",
"internalErrorMessage": "OAuth geri araması işlenirken beklenmeyen bir hata oluştu.",
"closeWindow": "Pencereyi Kapat"
}
}

View File

@@ -64,7 +64,7 @@
"i18next-fs-backend": "^2.6.0",
"jsonwebtoken": "^9.0.2",
"multer": "^2.0.2",
"openai": "^6.7.0",
"openai": "^4.104.0",
"openapi-types": "^12.1.3",
"openid-client": "^6.8.1",
"pg": "^8.16.3",
@@ -105,12 +105,12 @@
"jest": "^30.2.0",
"jest-environment-node": "^30.0.5",
"jest-mock-extended": "4.0.0",
"lucide-react": "^0.552.0",
"lucide-react": "^0.486.0",
"next": "^15.5.0",
"postcss": "^8.5.6",
"prettier": "^3.6.2",
"react": "19.1.1",
"react-dom": "19.1.1",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-i18next": "^15.7.2",
"react-router-dom": "^7.8.2",
"supertest": "^7.1.4",

353
pnpm-lock.yaml generated
View File

@@ -35,7 +35,7 @@ importers:
version: 0.5.16
axios:
specifier: ^1.12.2
version: 1.13.1
version: 1.12.2
bcrypt:
specifier: ^6.0.0
version: 6.0.0
@@ -59,7 +59,7 @@ importers:
version: 7.2.1
i18next:
specifier: ^25.5.0
version: 25.6.0(typescript@5.9.2)
version: 25.5.0(typescript@5.9.2)
i18next-fs-backend:
specifier: ^2.6.0
version: 2.6.0
@@ -70,8 +70,8 @@ importers:
specifier: ^2.0.2
version: 2.0.2
openai:
specifier: ^6.7.0
version: 6.7.0(zod@3.25.76)
specifier: ^4.104.0
version: 4.104.0(zod@3.25.76)
openapi-types:
specifier: ^12.1.3
version: 12.1.3
@@ -99,10 +99,10 @@ importers:
devDependencies:
'@radix-ui/react-accordion':
specifier: ^1.2.12
version: 1.2.12(@types/react-dom@19.1.7(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
version: 1.2.12(@types/react-dom@19.1.7(@types/react@19.1.11))(@types/react@19.1.11)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-slot':
specifier: ^1.2.3
version: 1.2.3(@types/react@19.2.2)(react@19.1.1)
version: 1.2.3(@types/react@19.1.11)(react@19.1.1)
'@shadcn/ui':
specifier: ^0.0.4
version: 0.0.4
@@ -141,10 +141,10 @@ importers:
version: 24.6.2
'@types/react':
specifier: ^19.1.11
version: 19.2.2
version: 19.1.11
'@types/react-dom':
specifier: ^19.1.7
version: 19.1.7(@types/react@19.2.2)
version: 19.1.7(@types/react@19.1.11)
'@types/supertest':
specifier: ^6.0.3
version: 6.0.3
@@ -188,8 +188,8 @@ importers:
specifier: 4.0.0
version: 4.0.0(@jest/globals@30.2.0)(jest@30.2.0(@types/node@24.6.2)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.6.2)(typescript@5.9.2)))(typescript@5.9.2)
lucide-react:
specifier: ^0.552.0
version: 0.552.0(react@19.1.1)
specifier: ^0.486.0
version: 0.486.0(react@19.1.1)
next:
specifier: ^15.5.0
version: 15.5.2(@babel/core@7.28.4)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
@@ -200,14 +200,14 @@ importers:
specifier: ^3.6.2
version: 3.6.2
react:
specifier: 19.1.1
specifier: ^19.1.1
version: 19.1.1
react-dom:
specifier: 19.1.1
specifier: ^19.1.1
version: 19.1.1(react@19.1.1)
react-i18next:
specifier: ^15.7.2
version: 15.7.2(i18next@25.6.0(typescript@5.9.2))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)
version: 15.7.2(i18next@25.5.0(typescript@5.9.2))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)
react-router-dom:
specifier: ^7.8.2
version: 7.8.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
@@ -447,10 +447,6 @@ packages:
resolution: {integrity: sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==}
engines: {node: '>=6.9.0'}
'@babel/runtime@7.28.4':
resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==}
engines: {node: '>=6.9.0'}
'@babel/template@7.27.2':
resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
engines: {node: '>=6.9.0'}
@@ -856,92 +852,78 @@ packages:
resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-arm@1.2.0':
resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-ppc64@1.2.0':
resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.2.0':
resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-x64@1.2.0':
resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.2.0':
resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.2.0':
resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-linux-arm64@0.34.3':
resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-arm@0.34.3':
resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-linux-ppc64@0.34.3':
resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-s390x@0.34.3':
resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-linux-x64@0.34.3':
resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.34.3':
resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-linuxmusl-x64@0.34.3':
resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-wasm32@0.34.3':
resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==}
@@ -1133,28 +1115,24 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@next/swc-linux-arm64-musl@15.5.2':
resolution: {integrity: sha512-s6N8k8dF9YGc5T01UPQ08yxsK6fUow5gG1/axWc1HVVBYQBgOjca4oUZF7s4p+kwhkB1bDSGR8QznWrFZ/Rt5g==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@next/swc-linux-x64-gnu@15.5.2':
resolution: {integrity: sha512-o1RV/KOODQh6dM6ZRJGZbc+MOAHww33Vbs5JC9Mp1gDk8cpEO+cYC/l7rweiEalkSm5/1WGa4zY7xrNwObN4+Q==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@next/swc-linux-x64-musl@15.5.2':
resolution: {integrity: sha512-/VUnh7w8RElYZ0IV83nUcP/J4KJ6LLYliiBIri3p3aW2giF+PAVgZb6mk8jbQSB3WlTai8gEmCAr7kptFa1H6g==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@next/swc-win32-arm64-msvc@15.5.2':
resolution: {integrity: sha512-sMPyTvRcNKXseNQ/7qRfVRLa0VhR0esmQ29DD6pqvG71+JdVnESJaHPA8t7bc67KD5spP3+DOCNLhqlEI2ZgQg==}
@@ -1372,67 +1350,56 @@ packages:
resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.52.5':
resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==}
cpu: [arm]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.52.5':
resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.52.5':
resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.52.5':
resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==}
cpu: [loong64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-gnu@4.52.5':
resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-gnu@4.52.5':
resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.52.5':
resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.52.5':
resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.52.5':
resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.52.5':
resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==}
cpu: [x64]
os: [linux]
libc: [musl]
'@rollup/rollup-openharmony-arm64@4.52.5':
resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==}
@@ -1498,28 +1465,24 @@ packages:
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@swc/core-linux-arm64-musl@1.13.5':
resolution: {integrity: sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@swc/core-linux-x64-gnu@1.13.5':
resolution: {integrity: sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@swc/core-linux-x64-musl@1.13.5':
resolution: {integrity: sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@swc/core-win32-arm64-msvc@1.13.5':
resolution: {integrity: sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==}
@@ -1639,56 +1602,48 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-arm64-gnu@4.1.14':
resolution: {integrity: sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-arm64-musl@4.1.12':
resolution: {integrity: sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@tailwindcss/oxide-linux-arm64-musl@4.1.14':
resolution: {integrity: sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@tailwindcss/oxide-linux-x64-gnu@4.1.12':
resolution: {integrity: sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-x64-gnu@4.1.14':
resolution: {integrity: sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-x64-musl@4.1.12':
resolution: {integrity: sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@tailwindcss/oxide-linux-x64-musl@4.1.14':
resolution: {integrity: sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@tailwindcss/oxide-wasm32-wasi@4.1.12':
resolution: {integrity: sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg==}
@@ -1845,6 +1800,12 @@ packages:
'@types/multer@1.4.13':
resolution: {integrity: sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==}
'@types/node-fetch@2.6.13':
resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==}
'@types/node@18.19.129':
resolution: {integrity: sha512-hrmi5jWt2w60ayox3iIXwpMEnfUvOLJCRtrOPbHtH15nTjvO7uhnelvrdAs0dO0/zl5DZ3ZbahiaXEVb54ca/A==}
'@types/node@24.6.2':
resolution: {integrity: sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==}
@@ -1862,8 +1823,8 @@ packages:
peerDependencies:
'@types/react': ^19.0.0
'@types/react@19.2.2':
resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==}
'@types/react@19.1.11':
resolution: {integrity: sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ==}
'@types/semver@7.7.0':
resolution: {integrity: sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==}
@@ -2019,49 +1980,41 @@ packages:
resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-arm64-musl@1.11.1':
resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-x64-gnu@1.11.1':
resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-x64-musl@1.11.1':
resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
cpu: [x64]
os: [linux]
libc: [musl]
'@unrs/resolver-binding-wasm32-wasi@1.11.1':
resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
@@ -2089,6 +2042,10 @@ packages:
peerDependencies:
vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
abort-controller@3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'}
accepts@1.3.8:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'}
@@ -2115,6 +2072,10 @@ packages:
resolution: {integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==}
engines: {node: '>=12.0'}
agentkeepalive@4.6.0:
resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==}
engines: {node: '>= 8.0.0'}
ajv-draft-04@1.0.0:
resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==}
peerDependencies:
@@ -2205,8 +2166,8 @@ packages:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
axios@1.13.1:
resolution: {integrity: sha512-hU4EGxxt+j7TQijx1oYdAjw4xuIp1wRQSsbMFwSthCWeBQur1eF+qJ5iQ5sN3Tw8YRzQNKb8jszgBdMDVqwJcw==}
axios@1.12.2:
resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==}
babel-jest@30.2.0:
resolution: {integrity: sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==}
@@ -2712,6 +2673,10 @@ packages:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'}
event-target-shim@5.0.1:
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
engines: {node: '>=6'}
eventsource-parser@3.0.5:
resolution: {integrity: sha512-bSRG85ZrMdmWtm7qkF9He9TNRzc/Bm99gEJMaQoHJ9E6Kv9QBbsldh2oMj7iXmYNEAVvNgvv5vPorG6W+XtBhQ==}
engines: {node: '>=20.0.0'}
@@ -2840,10 +2805,17 @@ packages:
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
engines: {node: '>=14'}
form-data-encoder@1.7.2:
resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==}
form-data@4.0.4:
resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
engines: {node: '>= 6'}
formdata-node@4.4.1:
resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==}
engines: {node: '>= 12.20'}
formdata-polyfill@4.0.10:
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
engines: {node: '>=12.20.0'}
@@ -2985,14 +2957,17 @@ packages:
resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==}
engines: {node: '>=14.18.0'}
humanize-ms@1.2.1:
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
i18next-browser-languagedetector@8.2.0:
resolution: {integrity: sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g==}
i18next-fs-backend@2.6.0:
resolution: {integrity: sha512-3ZlhNoF9yxnM8pa8bWp5120/Ob6t4lVl1l/tbLmkml/ei3ud8IWySCHt2lrY5xWRlSU5D9IV2sm5bEbGuTqwTw==}
i18next@25.6.0:
resolution: {integrity: sha512-tTn8fLrwBYtnclpL5aPXK/tAYBLWVvoHM1zdfXoRNLcI+RvtMsoZRV98ePlaW3khHYKuNh/Q65W/+NVFUeIwVw==}
i18next@25.5.0:
resolution: {integrity: sha512-Mm2CgIq0revRFbBvfzqW9kDw1r44M4VDWC+YNRx9vTo5bU/iogSdEAC2HEonDA4czEce/iSbAkK90Tw7UrRZKA==}
peerDependencies:
typescript: ^5
peerDependenciesMeta:
@@ -3394,28 +3369,24 @@ packages:
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.30.1:
resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.30.1:
resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.30.1:
resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.30.1:
resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
@@ -3484,8 +3455,8 @@ packages:
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
lucide-react@0.552.0:
resolution: {integrity: sha512-g9WCjmfwqbexSnZE+2cl21PCfXOcqnGeWeMTNAOGEfpPbm/ZF4YIq77Z8qWrxbu660EKuLB4nSLggoKnCb+isw==}
lucide-react@0.486.0:
resolution: {integrity: sha512-xWop/wMsC1ikiEVLZrxXjPKw4vU/eAip33G2mZHgbWnr4Nr5Rt4Vx4s/q1D3B/rQVbxjOuqASkEZcUxDEKzecw==}
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
@@ -3677,6 +3648,15 @@ packages:
engines: {node: '>=10.5.0'}
deprecated: Use your platform's native DOMException instead
node-fetch@2.7.0:
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
node-fetch@3.3.2:
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -3733,12 +3713,12 @@ packages:
resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
engines: {node: '>=12'}
openai@6.7.0:
resolution: {integrity: sha512-mgSQXa3O/UXTbA8qFzoa7aydbXBJR5dbLQXCRapAOtoNT+v69sLdKMZzgiakpqhclRnhPggPAXoniVGn2kMY2A==}
openai@4.104.0:
resolution: {integrity: sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==}
hasBin: true
peerDependencies:
ws: ^8.18.0
zod: ^3.25 || ^4.0
zod: ^3.23.8
peerDependenciesMeta:
ws:
optional: true
@@ -4386,6 +4366,9 @@ packages:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
engines: {node: '>=0.6'}
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
tree-kill@1.2.2:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
@@ -4568,6 +4551,9 @@ packages:
engines: {node: '>=0.8.0'}
hasBin: true
undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
undici-types@7.13.0:
resolution: {integrity: sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==}
@@ -4671,6 +4657,16 @@ packages:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
engines: {node: '>= 8'}
web-streams-polyfill@4.0.0-beta.3:
resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==}
engines: {node: '>= 14'}
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
which-typed-array@1.1.19:
resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==}
engines: {node: '>= 0.4'}
@@ -4983,8 +4979,6 @@ snapshots:
'@babel/runtime@7.28.3': {}
'@babel/runtime@7.28.4': {}
'@babel/template@7.27.2':
dependencies:
'@babel/code-frame': 7.27.1
@@ -5662,122 +5656,122 @@ snapshots:
'@radix-ui/primitive@1.1.3': {}
'@radix-ui/react-accordion@1.2.12(@types/react-dom@19.1.7(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
'@radix-ui/react-accordion@1.2.12(@types/react-dom@19.1.7(@types/react@19.1.11))(@types/react@19.1.11)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
dependencies:
'@radix-ui/primitive': 1.1.3
'@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.1.7(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.7(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.1.7(@types/react@19.1.11))(@types/react@19.1.11)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.11))(@types/react@19.1.11)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.11)(react@19.1.1)
'@radix-ui/react-context': 1.1.2(@types/react@19.1.11)(react@19.1.1)
'@radix-ui/react-direction': 1.1.1(@types/react@19.1.11)(react@19.1.1)
'@radix-ui/react-id': 1.1.1(@types/react@19.1.11)(react@19.1.1)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.11))(@types/react@19.1.11)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.11)(react@19.1.1)
react: 19.1.1
react-dom: 19.1.1(react@19.1.1)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.1.7(@types/react@19.2.2)
'@types/react': 19.1.11
'@types/react-dom': 19.1.7(@types/react@19.1.11)
'@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.1.7(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
'@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.1.7(@types/react@19.1.11))(@types/react@19.1.11)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
dependencies:
'@radix-ui/primitive': 1.1.3
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.7(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.11)(react@19.1.1)
'@radix-ui/react-context': 1.1.2(@types/react@19.1.11)(react@19.1.1)
'@radix-ui/react-id': 1.1.1(@types/react@19.1.11)(react@19.1.1)
'@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.7(@types/react@19.1.11))(@types/react@19.1.11)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.11))(@types/react@19.1.11)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.11)(react@19.1.1)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.11)(react@19.1.1)
react: 19.1.1
react-dom: 19.1.1(react@19.1.1)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.1.7(@types/react@19.2.2)
'@types/react': 19.1.11
'@types/react-dom': 19.1.7(@types/react@19.1.11)
'@radix-ui/react-collection@1.1.7(@types/react-dom@19.1.7(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
'@radix-ui/react-collection@1.1.7(@types/react-dom@19.1.7(@types/react@19.1.11))(@types/react@19.1.11)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.11)(react@19.1.1)
'@radix-ui/react-context': 1.1.2(@types/react@19.1.11)(react@19.1.1)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.11))(@types/react@19.1.11)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-slot': 1.2.3(@types/react@19.1.11)(react@19.1.1)
react: 19.1.1
react-dom: 19.1.1(react@19.1.1)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.1.7(@types/react@19.2.2)
'@types/react': 19.1.11
'@types/react-dom': 19.1.7(@types/react@19.1.11)
'@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.2)(react@19.1.1)':
'@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.11)(react@19.1.1)':
dependencies:
react: 19.1.1
optionalDependencies:
'@types/react': 19.2.2
'@types/react': 19.1.11
'@radix-ui/react-context@1.1.2(@types/react@19.2.2)(react@19.1.1)':
'@radix-ui/react-context@1.1.2(@types/react@19.1.11)(react@19.1.1)':
dependencies:
react: 19.1.1
optionalDependencies:
'@types/react': 19.2.2
'@types/react': 19.1.11
'@radix-ui/react-direction@1.1.1(@types/react@19.2.2)(react@19.1.1)':
'@radix-ui/react-direction@1.1.1(@types/react@19.1.11)(react@19.1.1)':
dependencies:
react: 19.1.1
optionalDependencies:
'@types/react': 19.2.2
'@types/react': 19.1.11
'@radix-ui/react-id@1.1.1(@types/react@19.2.2)(react@19.1.1)':
'@radix-ui/react-id@1.1.1(@types/react@19.1.11)(react@19.1.1)':
dependencies:
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.11)(react@19.1.1)
react: 19.1.1
optionalDependencies:
'@types/react': 19.2.2
'@types/react': 19.1.11
'@radix-ui/react-presence@1.1.5(@types/react-dom@19.1.7(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
'@radix-ui/react-presence@1.1.5(@types/react-dom@19.1.7(@types/react@19.1.11))(@types/react@19.1.11)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.11)(react@19.1.1)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.11)(react@19.1.1)
react: 19.1.1
react-dom: 19.1.1(react@19.1.1)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.1.7(@types/react@19.2.2)
'@types/react': 19.1.11
'@types/react-dom': 19.1.7(@types/react@19.1.11)
'@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.7(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
'@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.7(@types/react@19.1.11))(@types/react@19.1.11)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
dependencies:
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-slot': 1.2.3(@types/react@19.1.11)(react@19.1.1)
react: 19.1.1
react-dom: 19.1.1(react@19.1.1)
optionalDependencies:
'@types/react': 19.2.2
'@types/react-dom': 19.1.7(@types/react@19.2.2)
'@types/react': 19.1.11
'@types/react-dom': 19.1.7(@types/react@19.1.11)
'@radix-ui/react-slot@1.2.3(@types/react@19.2.2)(react@19.1.1)':
'@radix-ui/react-slot@1.2.3(@types/react@19.1.11)(react@19.1.1)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.11)(react@19.1.1)
react: 19.1.1
optionalDependencies:
'@types/react': 19.2.2
'@types/react': 19.1.11
'@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.2)(react@19.1.1)':
'@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.11)(react@19.1.1)':
dependencies:
'@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.11)(react@19.1.1)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.11)(react@19.1.1)
react: 19.1.1
optionalDependencies:
'@types/react': 19.2.2
'@types/react': 19.1.11
'@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.2)(react@19.1.1)':
'@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.11)(react@19.1.1)':
dependencies:
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.1)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.11)(react@19.1.1)
react: 19.1.1
optionalDependencies:
'@types/react': 19.2.2
'@types/react': 19.1.11
'@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.2)(react@19.1.1)':
'@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.11)(react@19.1.1)':
dependencies:
react: 19.1.1
optionalDependencies:
'@types/react': 19.2.2
'@types/react': 19.1.11
'@rolldown/pluginutils@1.0.0-beta.27': {}
@@ -6191,6 +6185,15 @@ snapshots:
dependencies:
'@types/express': 4.17.23
'@types/node-fetch@2.6.13':
dependencies:
'@types/node': 24.6.2
form-data: 4.0.4
'@types/node@18.19.129':
dependencies:
undici-types: 5.26.5
'@types/node@24.6.2':
dependencies:
undici-types: 7.13.0
@@ -6205,11 +6208,11 @@ snapshots:
'@types/range-parser@1.2.7': {}
'@types/react-dom@19.1.7(@types/react@19.2.2)':
'@types/react-dom@19.1.7(@types/react@19.1.11)':
dependencies:
'@types/react': 19.2.2
'@types/react': 19.1.11
'@types/react@19.2.2':
'@types/react@19.1.11':
dependencies:
csstype: 3.1.3
@@ -6438,6 +6441,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
abort-controller@3.0.0:
dependencies:
event-target-shim: 5.0.1
accepts@1.3.8:
dependencies:
mime-types: 2.1.35
@@ -6460,6 +6467,10 @@ snapshots:
adm-zip@0.5.16: {}
agentkeepalive@4.6.0:
dependencies:
humanize-ms: 1.2.1
ajv-draft-04@1.0.0(ajv@8.17.1):
optionalDependencies:
ajv: 8.17.1
@@ -6537,7 +6548,7 @@ snapshots:
dependencies:
possible-typed-array-names: 1.1.0
axios@1.13.1:
axios@1.12.2:
dependencies:
follow-redirects: 1.15.11
form-data: 4.0.4
@@ -7113,6 +7124,8 @@ snapshots:
etag@1.8.1: {}
event-target-shim@5.0.1: {}
eventsource-parser@3.0.5: {}
eventsource@3.0.7:
@@ -7326,6 +7339,8 @@ snapshots:
cross-spawn: 7.0.6
signal-exit: 4.1.0
form-data-encoder@1.7.2: {}
form-data@4.0.4:
dependencies:
asynckit: 0.4.0
@@ -7334,6 +7349,11 @@ snapshots:
hasown: 2.0.2
mime-types: 2.1.35
formdata-node@4.4.1:
dependencies:
node-domexception: 1.0.0
web-streams-polyfill: 4.0.0-beta.3
formdata-polyfill@4.0.10:
dependencies:
fetch-blob: 3.2.0
@@ -7483,15 +7503,19 @@ snapshots:
human-signals@4.3.1: {}
humanize-ms@1.2.1:
dependencies:
ms: 2.1.3
i18next-browser-languagedetector@8.2.0:
dependencies:
'@babel/runtime': 7.28.3
i18next-fs-backend@2.6.0: {}
i18next@25.6.0(typescript@5.9.2):
i18next@25.5.0(typescript@5.9.2):
dependencies:
'@babel/runtime': 7.28.4
'@babel/runtime': 7.28.3
optionalDependencies:
typescript: 5.9.2
@@ -8139,7 +8163,7 @@ snapshots:
dependencies:
yallist: 3.1.1
lucide-react@0.552.0(react@19.1.1):
lucide-react@0.486.0(react@19.1.1):
dependencies:
react: 19.1.1
@@ -8287,6 +8311,10 @@ snapshots:
node-domexception@1.0.0: {}
node-fetch@2.7.0:
dependencies:
whatwg-url: 5.0.0
node-fetch@3.3.2:
dependencies:
data-uri-to-buffer: 4.0.1
@@ -8333,9 +8361,19 @@ snapshots:
dependencies:
mimic-fn: 4.0.0
openai@6.7.0(zod@3.25.76):
openai@4.104.0(zod@3.25.76):
dependencies:
'@types/node': 18.19.129
'@types/node-fetch': 2.6.13
abort-controller: 3.0.0
agentkeepalive: 4.6.0
form-data-encoder: 1.7.2
formdata-node: 4.4.1
node-fetch: 2.7.0
optionalDependencies:
zod: 3.25.76
transitivePeerDependencies:
- encoding
openapi-types@12.1.3: {}
@@ -8561,11 +8599,11 @@ snapshots:
react: 19.1.1
scheduler: 0.26.0
react-i18next@15.7.2(i18next@25.6.0(typescript@5.9.2))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2):
react-i18next@15.7.2(i18next@25.5.0(typescript@5.9.2))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2):
dependencies:
'@babel/runtime': 7.28.3
html-parse-stringify: 3.0.1
i18next: 25.6.0(typescript@5.9.2)
i18next: 25.5.0(typescript@5.9.2)
react: 19.1.1
optionalDependencies:
react-dom: 19.1.1(react@19.1.1)
@@ -9021,6 +9059,8 @@ snapshots:
toidentifier@1.0.1: {}
tr46@0.0.3: {}
tree-kill@1.2.2: {}
ts-api-utils@1.4.3(typescript@5.9.2):
@@ -9165,6 +9205,8 @@ snapshots:
uglify-js@3.19.3:
optional: true
undici-types@5.26.5: {}
undici-types@7.13.0: {}
universalify@2.0.1: {}
@@ -9250,6 +9292,15 @@ snapshots:
web-streams-polyfill@3.3.3: {}
web-streams-polyfill@4.0.0-beta.3: {}
webidl-conversions@3.0.1: {}
whatwg-url@5.0.0:
dependencies:
tr46: 0.0.3
webidl-conversions: 3.0.1
which-typed-array@1.1.19:
dependencies:
available-typed-arrays: 1.0.7

View File

@@ -8,19 +8,10 @@ import { DataService } from '../services/dataService.js';
dotenv.config();
/**
* Normalize the base path by removing trailing slashes
*/
const normalizeBasePath = (path: string): string => {
if (!path) return '';
// Remove trailing slashes
return path.replace(/\/+$/, '');
};
const defaultConfig = {
port: process.env.PORT || 3000,
initTimeout: process.env.INIT_TIMEOUT || 300000,
basePath: normalizeBasePath(process.env.BASE_PATH || ''),
basePath: process.env.BASE_PATH || '',
readonly: 'true' === process.env.READONLY || false,
mcpHubName: 'mcphub',
mcpHubVersion: getPackageVersion(),

View File

@@ -1,4 +1,6 @@
import fs from 'fs';
import os from 'os';
import path from 'path';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import {
CallToolRequestSchema,
@@ -31,6 +33,77 @@ const servers: { [sessionId: string]: Server } = {};
const serverDao = getServerDao();
const ensureDirExists = (dir: string | undefined): string => {
if (!dir) {
throw new Error('Directory path is undefined');
}
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
return dir;
};
const getDataRootDir = (): string => {
return ensureDirExists(process.env.MCP_DATA_DIR || path.join(process.cwd(), 'data'));
};
const getServersStorageRoot = (): string => {
return ensureDirExists(process.env.MCP_SERVERS_DIR || path.join(getDataRootDir(), 'servers'));
};
const getNpmBaseDir = (): string => {
return ensureDirExists(process.env.MCP_NPM_DIR || path.join(getServersStorageRoot(), 'npm'));
};
const getPythonBaseDir = (): string => {
return ensureDirExists(
process.env.MCP_PYTHON_DIR || path.join(getServersStorageRoot(), 'python'),
);
};
const getNpmCacheDir = (): string => {
return ensureDirExists(process.env.NPM_CONFIG_CACHE || path.join(getDataRootDir(), 'npm-cache'));
};
const getNpmPrefixDir = (): string => {
const dir = ensureDirExists(
process.env.NPM_CONFIG_PREFIX || path.join(getDataRootDir(), 'npm-global'),
);
ensureDirExists(path.join(dir, 'bin'));
ensureDirExists(path.join(dir, 'lib', 'node_modules'));
return dir;
};
const getUvCacheDir = (): string => {
return ensureDirExists(process.env.UV_CACHE_DIR || path.join(getDataRootDir(), 'uv', 'cache'));
};
const getUvToolDir = (): string => {
const dir = ensureDirExists(process.env.UV_TOOL_DIR || path.join(getDataRootDir(), 'uv', 'tools'));
ensureDirExists(path.join(dir, 'bin'));
return dir;
};
const getServerInstallDir = (serverName: string, kind: 'npm' | 'python'): string => {
const baseDir = kind === 'npm' ? getNpmBaseDir() : getPythonBaseDir();
return ensureDirExists(path.join(baseDir, serverName));
};
const prependToPath = (currentPath: string, dir: string): string => {
if (!dir) {
return currentPath;
}
const delimiter = path.delimiter;
const segments = currentPath ? currentPath.split(delimiter) : [];
if (segments.includes(dir)) {
return currentPath;
}
return currentPath ? `${dir}${delimiter}${currentPath}` : dir;
};
const NODE_COMMANDS = new Set(['npm', 'npx', 'pnpm', 'yarn', 'node', 'bun', 'bunx']);
const PYTHON_COMMANDS = new Set(['uv', 'uvx', 'python', 'pip', 'pip3', 'pipx']);
// Helper function to set up keep-alive ping for SSE connections
const setupKeepAlive = (serverInfo: ServerInfo, serverConfig: ServerConfig): void => {
// Only set up keep-alive for SSE connections
@@ -213,7 +286,7 @@ export const createTransportFromConfig = async (name: string, conf: ServerConfig
...(process.env as Record<string, string>),
...replaceEnvVars(conf.env || {}),
};
env['PATH'] = expandEnvVars(process.env.PATH as string) || '';
env['PATH'] = expandEnvVars(env['PATH'] || process.env.PATH || '');
const settings = loadSettings();
// Add UV_DEFAULT_INDEX and npm_config_registry if needed
@@ -235,9 +308,52 @@ export const createTransportFromConfig = async (name: string, conf: ServerConfig
env['npm_config_registry'] = settings.systemConfig.install.npmRegistry;
}
// Ensure stdio servers use persistent directories under /app/data (or configured override)
let workingDirectory = os.homedir();
const commandLower = conf.command.toLowerCase();
if (NODE_COMMANDS.has(commandLower)) {
const serverDir = getServerInstallDir(name, 'npm');
workingDirectory = serverDir;
const npmCacheDir = getNpmCacheDir();
const npmPrefixDir = getNpmPrefixDir();
if (!env['npm_config_cache']) {
env['npm_config_cache'] = npmCacheDir;
}
if (!env['NPM_CONFIG_CACHE']) {
env['NPM_CONFIG_CACHE'] = env['npm_config_cache'];
}
if (!env['npm_config_prefix']) {
env['npm_config_prefix'] = npmPrefixDir;
}
if (!env['NPM_CONFIG_PREFIX']) {
env['NPM_CONFIG_PREFIX'] = env['npm_config_prefix'];
}
env['PATH'] = prependToPath(env['PATH'], path.join(env['npm_config_prefix'], 'bin'));
} else if (PYTHON_COMMANDS.has(commandLower)) {
const serverDir = getServerInstallDir(name, 'python');
workingDirectory = serverDir;
const uvCacheDir = getUvCacheDir();
const uvToolDir = getUvToolDir();
if (!env['UV_CACHE_DIR']) {
env['UV_CACHE_DIR'] = uvCacheDir;
}
if (!env['UV_TOOL_DIR']) {
env['UV_TOOL_DIR'] = uvToolDir;
}
env['PATH'] = prependToPath(env['PATH'], path.join(env['UV_TOOL_DIR'], 'bin'));
}
// Expand environment variables in command
transport = new StdioClientTransport({
cwd: os.homedir(),
cwd: workingDirectory,
command: conf.command,
args: replaceEnvVars(conf.args) as string[],
env: env,

View File

@@ -1,130 +0,0 @@
import { beforeEach, describe, expect, it, jest } from '@jest/globals';
import request from 'supertest';
// Mock dependencies
jest.mock('../../src/utils/i18n.js', () => ({
__esModule: true,
initI18n: jest.fn().mockResolvedValue(undefined),
}));
jest.mock('../../src/models/User.js', () => ({
__esModule: true,
initializeDefaultUser: jest.fn().mockResolvedValue(undefined),
}));
jest.mock('../../src/services/oauthService.js', () => ({
__esModule: true,
initOAuthProvider: jest.fn(),
getOAuthRouter: jest.fn(() => null),
}));
jest.mock('../../src/services/mcpService.js', () => ({
__esModule: true,
initUpstreamServers: jest.fn().mockResolvedValue(undefined),
connected: jest.fn().mockReturnValue(true),
}));
jest.mock('../../src/middlewares/userContext.js', () => ({
__esModule: true,
userContextMiddleware: jest.fn((_req, _res, next) => next()),
sseUserContextMiddleware: jest.fn((_req, _res, next) => next()),
}));
describe('AppServer with BASE_PATH configuration', () => {
// Save original BASE_PATH
const originalBasePath = process.env.BASE_PATH;
beforeEach(() => {
jest.clearAllMocks();
// Clear module cache to allow fresh imports with different config
jest.resetModules();
});
afterEach(() => {
// Restore original BASE_PATH or remove it
if (originalBasePath !== undefined) {
process.env.BASE_PATH = originalBasePath;
} else {
delete process.env.BASE_PATH;
}
});
const flushPromises = async () => {
await new Promise((resolve) => setImmediate(resolve));
};
it('should serve auth routes with BASE_PATH=/mcphub/', async () => {
// Set environment variable for BASE_PATH (with trailing slash)
process.env.BASE_PATH = '/mcphub/';
// Dynamically import after setting env var
const { AppServer } = await import('../../src/server.js');
const config = await import('../../src/config/index.js');
// Verify config loaded the BASE_PATH and normalized it (removed trailing slash)
expect(config.default.basePath).toBe('/mcphub');
const appServer = new AppServer();
await appServer.initialize();
await flushPromises();
const app = appServer.getApp();
// Test that /mcphub/config endpoint exists
const configResponse = await request(app).get('/mcphub/config');
expect(configResponse.status).not.toBe(404);
// Test that /mcphub/public-config endpoint exists
const publicConfigResponse = await request(app).get('/mcphub/public-config');
expect(publicConfigResponse.status).not.toBe(404);
});
it('should serve auth routes without BASE_PATH (default)', async () => {
// Ensure BASE_PATH is not set
delete process.env.BASE_PATH;
// Dynamically import after clearing env var
jest.resetModules();
const { AppServer } = await import('../../src/server.js');
const config = await import('../../src/config/index.js');
// Verify config has empty BASE_PATH
expect(config.default.basePath).toBe('');
const appServer = new AppServer();
await appServer.initialize();
await flushPromises();
const app = appServer.getApp();
// Test that /config endpoint exists (without base path)
const configResponse = await request(app).get('/config');
expect(configResponse.status).not.toBe(404);
// Test that /public-config endpoint exists
const publicConfigResponse = await request(app).get('/public-config');
expect(publicConfigResponse.status).not.toBe(404);
});
it('should serve global endpoints without BASE_PATH prefix', async () => {
process.env.BASE_PATH = '/test-base/';
jest.resetModules();
const { AppServer } = await import('../../src/server.js');
const appServer = new AppServer();
await appServer.initialize();
await flushPromises();
const app = appServer.getApp();
// Test that /health endpoint is accessible globally (no BASE_PATH prefix)
// The /health endpoint is intentionally mounted without BASE_PATH
const healthResponse = await request(app).get('/health');
expect(healthResponse.status).not.toBe(404);
// Also verify that BASE_PATH prefixed routes exist
const configResponse = await request(app).get('/test-base/config');
expect(configResponse.status).not.toBe(404);
});
});