Skip to content

Commit eb18e35

Browse files
authored
Release/v1.7.9.1
Release/v1.7.9.1
2 parents fc23afe + e175c33 commit eb18e35

103 files changed

Lines changed: 6303 additions & 8792 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/CODEOWNERS

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,2 @@
11
# These owners will be the default owners for everything in the repo
2-
* @Phinease
3-
4-
doc/docs/zh/opensource-memorial-wall.md @WMC001
5-
doc/docs/en/opensource-memorial-wall.md @WMC001
2+
* @Phinease @WMC001

CODE_OF_CONDUCT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ representative at an online or offline event.
6161

6262
Instances of abusive, harassing, or otherwise unacceptable behavior may be
6363
reported to the community leaders responsible for enforcement at
64-
[chenshuangrui@huawei.com].
64+
[wanmingchen1@huawei.com].
6565
All complaints will be reviewed and investigated promptly and fairly.
6666

6767
All community leaders are obligated to respect the privacy and security of the

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ To contribute:
162162
Stuck or have questions? We're here to help! Reach out to us via:
163163
- **GitHub Issues**: Open an issue for discussion.
164164
- **Discord**: Join our [Nexent Community](https://discord.gg/YXH5C8SQ) for real-time chat.
165-
- **Email**: Drop us a line at [chenshuangrui@huawei.com](mailto:chenshuangrui@huawei.com).
165+
- **Email**: Drop us a line at [wanmingchen1@huawei.com](mailto:wanmingchen1@huawei.com).
166166

167167
## 🎉 Celebrate Your Contribution!
168168

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Nexent is permitted to be used commercially, including as a backend service for
77
a. Multi-tenant SaaS service: Unless explicitly authorized by Nexent in writing, you may not use the Nexent source code to operate a multi-tenant SaaS service.
88
b. LOGO and copyright information: In the process of using Nexent's frontend, you may not remove or modify the LOGO or copyright information in the Nexent console or applications. This restriction is inapplicable to uses of Nexent that do not involve its frontend.
99

10-
Please contact chenshuangrui@huawei.com by email to inquire about licensing matters.
10+
Please contact zhenggaoqi@huawei.com by email to inquire about licensing matters.
1111

1212
As a contributor, you should agree that:
1313

backend/apps/config_app.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from apps.file_management_app import file_management_config_router as file_manager_router
1111
from apps.image_app import router as proxy_router
1212
from apps.knowledge_summary_app import router as summary_router
13-
from apps.me_model_managment_app import router as me_model_manager_router
1413
from apps.mock_user_management_app import router as mock_user_management_router
1514
from apps.model_managment_app import router as model_manager_router
1615
from apps.prompt_app import router as prompt_router
@@ -37,7 +36,6 @@
3736
allow_headers=["*"], # Allows all headers
3837
)
3938

40-
app.include_router(me_model_manager_router)
4139
app.include_router(model_manager_router)
4240
app.include_router(config_sync_router)
4341
app.include_router(agent_router)

backend/apps/me_model_managment_app.py

Lines changed: 0 additions & 47 deletions
This file was deleted.

backend/apps/remote_mcp_app.py

Lines changed: 100 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import logging
22
from typing import Optional
33

4-
from fastapi import APIRouter, Header, HTTPException
4+
from fastapi import APIRouter, Header, HTTPException, UploadFile, File, Form
55
from fastapi.responses import JSONResponse
66
from http import HTTPStatus
77

8-
from consts.const import MCP_DOCKER_IMAGE
8+
from consts.const import NEXENT_MCP_DOCKER_IMAGE, ENABLE_UPLOAD_IMAGE
99
from consts.exceptions import MCPConnectionError, MCPNameIllegal, MCPContainerError
1010
from consts.model import MCPConfigRequest
1111
from services.remote_mcp_service import (
@@ -14,7 +14,9 @@
1414
get_remote_mcp_server_list,
1515
check_mcp_health_and_update_db,
1616
delete_mcp_by_container_id,
17+
upload_and_start_mcp_image,
1718
)
19+
from database.remote_mcp_db import check_mcp_name_exists
1820
from services.tool_configuration_service import get_tool_from_remote_mcp_server
1921
from services.mcp_container_service import MCPContainerManager
2022
from utils.auth_utils import get_current_user_id
@@ -116,6 +118,7 @@ async def get_remote_proxies(
116118
return JSONResponse(
117119
status_code=HTTPStatus.OK,
118120
content={"remote_mcp_server_list": remote_mcp_server_list,
121+
"enable_upload_image": ENABLE_UPLOAD_IMAGE,
119122
"status": "success"}
120123
)
121124
except Exception as e:
@@ -153,7 +156,7 @@ async def add_mcp_from_config(
153156
"""
154157
Add MCP server by starting a container with command+args config.
155158
Similar to Cursor's MCP server configuration format.
156-
159+
157160
Example request:
158161
{
159162
"mcpServers": {
@@ -167,7 +170,7 @@ async def add_mcp_from_config(
167170
"""
168171
try:
169172
user_id, tenant_id = get_current_user_id(authorization)
170-
173+
171174
# Initialize container manager
172175
try:
173176
container_manager = MCPContainerManager()
@@ -177,25 +180,30 @@ async def add_mcp_from_config(
177180
status_code=HTTPStatus.SERVICE_UNAVAILABLE,
178181
detail="Docker service unavailable. Please ensure Docker socket is mounted."
179182
)
180-
183+
181184
results = []
182185
errors = []
183-
186+
184187
for service_name, config in mcp_config.mcpServers.items():
185188
try:
186189
command = config.command
187190
args = config.args or []
188191
env_vars = config.env or {}
189192
port = config.port
190-
193+
191194
if not command:
192195
errors.append(f"{service_name}: command is required")
193196
continue
194-
197+
195198
if port is None:
196199
errors.append(f"{service_name}: port is required")
197200
continue
198201

202+
# Check if MCP service name already exists before starting container
203+
if check_mcp_name_exists(mcp_name=service_name, tenant_id=tenant_id):
204+
errors.append(f"{service_name}: MCP name already exists")
205+
continue
206+
199207
# Build full command to run inside nexent/nexent-mcp image
200208
full_command = [
201209
"python",
@@ -219,28 +227,19 @@ async def add_mcp_from_config(
219227
user_id=user_id,
220228
env_vars=env_vars,
221229
host_port=port,
222-
image=config.image or MCP_DOCKER_IMAGE,
230+
image=config.image or NEXENT_MCP_DOCKER_IMAGE,
223231
full_command=full_command,
224232
)
225-
233+
226234
# Register to remote MCP server list
227-
try:
228-
await add_remote_mcp_server_list(
229-
tenant_id=tenant_id,
230-
user_id=user_id,
231-
remote_mcp_server=container_info["mcp_url"],
232-
remote_mcp_server_name=service_name,
233-
container_id=container_info["container_id"],
234-
)
235-
except MCPNameIllegal:
236-
# If name already exists, try to stop the container we just created
237-
try:
238-
await container_manager.stop_mcp_container(container_info["container_id"])
239-
except Exception:
240-
pass
241-
errors.append(f"{service_name}: MCP name already exists")
242-
continue
243-
235+
await add_remote_mcp_server_list(
236+
tenant_id=tenant_id,
237+
user_id=user_id,
238+
remote_mcp_server=container_info["mcp_url"],
239+
remote_mcp_server_name=service_name,
240+
container_id=container_info["container_id"],
241+
)
242+
244243
results.append({
245244
"service_name": service_name,
246245
"status": "success",
@@ -249,20 +248,22 @@ async def add_mcp_from_config(
249248
"container_name": container_info.get("container_name"),
250249
"host_port": container_info.get("host_port")
251250
})
252-
251+
253252
except MCPContainerError as e:
254-
logger.error(f"Failed to start MCP container {service_name}: {e}")
253+
logger.error(
254+
f"Failed to start MCP container {service_name}: {e}")
255255
errors.append(f"{service_name}: {str(e)}")
256256
except Exception as e:
257-
logger.error(f"Unexpected error adding MCP {service_name}: {e}")
257+
logger.error(
258+
f"Unexpected error adding MCP {service_name}: {e}")
258259
errors.append(f"{service_name}: {str(e)}")
259-
260+
260261
if errors and not results:
261262
raise HTTPException(
262263
status_code=HTTPStatus.BAD_REQUEST,
263264
detail=f"All MCP servers failed: {errors}"
264265
)
265-
266+
266267
return JSONResponse(
267268
status_code=HTTPStatus.OK,
268269
content={
@@ -272,7 +273,7 @@ async def add_mcp_from_config(
272273
"status": "success"
273274
}
274275
)
275-
276+
276277
except HTTPException:
277278
raise
278279
except Exception as e:
@@ -291,7 +292,7 @@ async def stop_mcp_container(
291292
""" Stop and remove MCP container """
292293
try:
293294
user_id, tenant_id = get_current_user_id(authorization)
294-
295+
295296
try:
296297
container_manager = MCPContainerManager()
297298
except MCPContainerError as e:
@@ -339,7 +340,7 @@ async def list_mcp_containers(
339340
""" List all MCP containers for the current tenant """
340341
try:
341342
user_id, tenant_id = get_current_user_id(authorization)
342-
343+
343344
try:
344345
container_manager = MCPContainerManager()
345346
except MCPContainerError as e:
@@ -348,9 +349,9 @@ async def list_mcp_containers(
348349
status_code=HTTPStatus.SERVICE_UNAVAILABLE,
349350
detail="Docker service unavailable"
350351
)
351-
352+
352353
containers = container_manager.list_mcp_containers(tenant_id=tenant_id)
353-
354+
354355
return JSONResponse(
355356
status_code=HTTPStatus.OK,
356357
content={
@@ -377,7 +378,7 @@ async def get_container_logs(
377378
""" Get logs from MCP container """
378379
try:
379380
user_id, tenant_id = get_current_user_id(authorization)
380-
381+
381382
try:
382383
container_manager = MCPContainerManager()
383384
except MCPContainerError as e:
@@ -386,9 +387,9 @@ async def get_container_logs(
386387
status_code=HTTPStatus.SERVICE_UNAVAILABLE,
387388
detail="Docker service unavailable"
388389
)
389-
390+
390391
logs = container_manager.get_container_logs(container_id, tail=tail)
391-
392+
392393
return JSONResponse(
393394
status_code=HTTPStatus.OK,
394395
content={
@@ -404,3 +405,62 @@ async def get_container_logs(
404405
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
405406
detail=f"Failed to get container logs: {str(e)}"
406407
)
408+
409+
410+
# Conditionally add upload-image route based on ENABLE_UPLOAD_IMAGE setting
411+
if ENABLE_UPLOAD_IMAGE:
412+
@router.post("/upload-image")
413+
async def upload_mcp_image(
414+
file: UploadFile = File(..., description="Docker image tar file"),
415+
port: int = Form(..., ge=1, le=65535,
416+
description="Host port to expose the MCP server on (1-65535)"),
417+
service_name: Optional[str] = Form(
418+
None, description="Name for the MCP service (auto-generated if not provided)"),
419+
env_vars: Optional[str] = Form(
420+
None, description="Environment variables as JSON string"),
421+
authorization: Optional[str] = Header(None)
422+
):
423+
"""
424+
Upload Docker image tar file and start MCP container.
425+
426+
Container naming: {filename-without-extension}-{tenant-id[:8]}-{user-id[:8]}
427+
"""
428+
try:
429+
user_id, tenant_id = get_current_user_id(authorization)
430+
431+
# Read file content
432+
content = await file.read()
433+
434+
# Call service layer to handle the business logic
435+
result = await upload_and_start_mcp_image(
436+
tenant_id=tenant_id,
437+
user_id=user_id,
438+
file_content=content,
439+
filename=file.filename,
440+
port=port,
441+
service_name=service_name,
442+
env_vars=env_vars,
443+
)
444+
445+
return JSONResponse(status_code=HTTPStatus.OK, content=result)
446+
447+
except ValueError as e:
448+
logger.error(f"Validation error: {e}")
449+
raise HTTPException(
450+
status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
451+
except MCPNameIllegal as e:
452+
logger.error(f"MCP name conflict: {e}")
453+
raise HTTPException(status_code=HTTPStatus.CONFLICT, detail=str(e))
454+
except MCPContainerError as e:
455+
logger.error(f"Container error: {e}")
456+
raise HTTPException(
457+
status_code=HTTPStatus.SERVICE_UNAVAILABLE, detail=str(e))
458+
except Exception as e:
459+
logger.error(f"Failed to upload and start MCP container: {e}")
460+
raise HTTPException(
461+
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
462+
detail=f"Failed to upload and start MCP container: {str(e)}"
463+
)
464+
else:
465+
logger.info(
466+
"MCP image upload feature is disabled (ENABLE_UPLOAD_IMAGE=false)")

backend/consts/const.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ class VectorDatabaseType(str, Enum):
129129
DISABLE_CELERY_FLOWER = os.getenv(
130130
"DISABLE_CELERY_FLOWER", "false").lower() == "true"
131131
DOCKER_ENVIRONMENT = os.getenv("DOCKER_ENVIRONMENT", "false").lower() == "true"
132-
DOCKER_HOST = os.getenv("DOCKER_HOST")
133-
MCP_DOCKER_IMAGE = os.getenv("MCP_DOCKER_IMAGE", "nexent/nexent-mcp:latest")
132+
NEXENT_MCP_DOCKER_IMAGE = os.getenv("NEXENT_MCP_DOCKER_IMAGE", "nexent/nexent-mcp:latest")
133+
ENABLE_UPLOAD_IMAGE = os.getenv("ENABLE_UPLOAD_IMAGE", "false").lower() == "true"
134134

135135

136136
# Celery Configuration
@@ -287,4 +287,4 @@ class VectorDatabaseType(str, Enum):
287287

288288

289289
# APP Version
290-
APP_VERSION = "v1.7.9"
290+
APP_VERSION = "v1.7.9.1"

0 commit comments

Comments
 (0)