@@ -28,17 +28,21 @@ private static bool IsNullOrWhiteSpaceOrJsonObject(string? text)
2828 return false ;
2929 }
3030 }
31+
32+ private static string ? NormalizeOptionalText ( string ? text )
33+ => string . IsNullOrWhiteSpace ( text ) ? null : text . Trim ( ) ;
34+
3135 [ HttpGet ]
3236 public async Task < ActionResult < McpServerListItemDto [ ] > > ListAllMcpServers ( CancellationToken cancellationToken )
3337 {
34- IQueryable < McpServer > query = db . McpServers . Where ( x => x . UserMcps . Any ( um => um . UserId == currentUser . Id ) ) ;
35-
36- McpServerListItemDto [ ] data = await query
37- . OrderByDescending ( x => x . Id )
38- . Select ( x => new McpServerListItemDto
38+ McpServerListItemDto [ ] data = await db . UserMcps
39+ . Where ( um => um . UserId == currentUser . Id )
40+ . OrderByDescending ( um => um . McpServerId )
41+ . Select ( um => new McpServerListItemDto
3942 {
40- Id = x . Id ,
41- Label = x . Label ,
43+ Id = um . McpServer . Id ,
44+ Label = um . McpServer . Label ,
45+ ShowShortcut = um . ShowShortcut ,
4246 } )
4347 . ToArrayAsync ( cancellationToken ) ;
4448 return Ok ( data ) ;
@@ -64,6 +68,11 @@ public async Task<ActionResult<ManagementMcpServerDto[]>> ListAllMcpServersForMa
6468 Editable = currentUser . IsAdmin || x . OwnerUserId == currentUser . Id , // admin or owner can edit
6569 Owner = x . OwnerUser . DisplayName ,
6670 AssignedUserCount = x . UserMcps . Count ,
71+ AssignedToMe = x . UserMcps . Any ( um => um . UserId == currentUser . Id ) ,
72+ ShowShortcut = x . UserMcps
73+ . Where ( um => um . UserId == currentUser . Id )
74+ . Select ( um => ( bool ? ) um . ShowShortcut )
75+ . FirstOrDefault ( ) ?? false ,
6776 } )
6877 . ToArrayAsync ( cancellationToken ) ;
6978 return Ok ( data ) ;
@@ -92,12 +101,18 @@ public async Task<ActionResult<McpServerDetailsDto>> GetMcpServerDetails(int mcp
92101 Label = x . Label ,
93102 Url = x . Url ,
94103 Headers = x . Headers ,
104+ ServerInstructions = x . ServerInstructions ,
95105 Owner = x . OwnerUser . DisplayName ,
96106 CreatedAt = x . CreatedAt ,
97107 UpdatedAt = x . UpdatedAt ,
98108 ToolsCount = x . McpTools . Count ,
99109 Editable = currentUser . IsAdmin || x . OwnerUserId == currentUser . Id ,
100110 AssignedUserCount = x . UserMcps . Count ,
111+ AssignedToMe = x . UserMcps . Any ( um => um . UserId == currentUser . Id ) ,
112+ ShowShortcut = x . UserMcps
113+ . Where ( um => um . UserId == currentUser . Id )
114+ . Select ( um => ( bool ? ) um . ShowShortcut )
115+ . FirstOrDefault ( ) ?? false ,
101116 Tools = x . McpTools
102117 . OrderBy ( t => t . Id )
103118 . Select ( t => new McpToolBasicInfo
@@ -167,7 +182,8 @@ public async Task<ActionResult<McpServerDetailsDto>> CreateMcpServer([FromBody]
167182 {
168183 Label = request . Label ,
169184 Url = request . Url ,
170- Headers = string . IsNullOrWhiteSpace ( request . Headers ) ? null : request . Headers ,
185+ Headers = NormalizeOptionalText ( request . Headers ) ,
186+ ServerInstructions = NormalizeOptionalText ( request . ServerInstructions ) ,
171187 OwnerUserId = currentUser . Id ,
172188 CreatedAt = DateTime . UtcNow ,
173189 UpdatedAt = DateTime . UtcNow ,
@@ -176,6 +192,7 @@ public async Task<ActionResult<McpServerDetailsDto>> CreateMcpServer([FromBody]
176192 new UserMcp
177193 {
178194 UserId = currentUser . Id , // auto-assign to creator
195+ ShowShortcut = false ,
179196 }
180197 ]
181198 } ;
@@ -256,12 +273,14 @@ public async Task<ActionResult<McpServerDetailsDto>> UpdateMcpServer(int mcpId,
256273
257274 server . Label = request . Label ;
258275 server . Url = request . Url ;
259- server . Headers = string . IsNullOrWhiteSpace ( request . Headers ) ? null : request . Headers ;
276+ server . Headers = NormalizeOptionalText ( request . Headers ) ;
277+ server . ServerInstructions = NormalizeOptionalText ( request . ServerInstructions ) ;
260278 if ( ! server . UserMcps . Any ( um => um . UserId == currentUser . Id ) )
261279 {
262280 server . UserMcps . Add ( new UserMcp
263281 {
264282 UserId = currentUser . Id ,
283+ ShowShortcut = false ,
265284 } ) ;
266285 }
267286
@@ -328,8 +347,8 @@ public async Task<ActionResult> DeleteMcpServer(int mcpId, CancellationToken can
328347 }
329348
330349 [ HttpPost ( "fetch-tools" ) ]
331- public async Task < ActionResult < List < McpToolBasicInfo > > > FetchMcpTools (
332- [ FromBody ] FetchToolsRequest req ,
350+ public async Task < ActionResult < FetchToolsResponse > > FetchMcpTools (
351+ [ FromBody ] FetchToolsRequest req ,
333352 [ FromServices ] ILogger < McpController > logger ,
334353 [ FromServices ] IHttpClientFactory httpClientFactory ,
335354 [ FromServices ] ILoggerFactory loggerFactory ,
@@ -364,7 +383,12 @@ public async Task<ActionResult<List<McpToolBasicInfo>>> FetchMcpTools(
364383
365384 try
366385 {
367- McpClient client = await McpClient . CreateAsync ( new HttpClientTransport ( options , httpClientFactory . CreateClient ( HttpClientNames . McpController ) , loggerFactory ) , cancellationToken : cancellationToken ) ;
386+ await using HttpClientTransport transport = new (
387+ options ,
388+ httpClientFactory . CreateClient ( HttpClientNames . McpController ) ,
389+ loggerFactory ,
390+ ownsHttpClient : false ) ;
391+ await using McpClient client = await McpClient . CreateAsync ( transport , cancellationToken : cancellationToken ) ;
368392 List < McpToolBasicInfo > tools = [ ] ;
369393 ListToolsResult mcpToolsResp = await client . ListToolsAsync ( new ListToolsRequestParams ( ) , cancellationToken ) ;
370394 foreach ( Tool tool in mcpToolsResp . Tools )
@@ -376,9 +400,13 @@ public async Task<ActionResult<List<McpToolBasicInfo>>> FetchMcpTools(
376400 Parameters = JSON . Serialize ( tool . InputSchema ) ,
377401 } ) ;
378402 }
379- return Ok ( tools ) ;
403+ return Ok ( new FetchToolsResponse
404+ {
405+ Tools = tools ,
406+ ServerInstructions = NormalizeOptionalText ( client . ServerInstructions ) ,
407+ } ) ;
380408 }
381- catch ( HttpRequestException ex )
409+ catch ( Exception ex ) when ( ex is HttpRequestException or TimeoutException or JsonException or IOException or InvalidOperationException )
382410 {
383411 logger . LogWarning ( ex , "Failed to fetch MCP tools from {Url}" , req . ServerUrl ) ;
384412 return BadRequest ( ex . Message ) ;
@@ -404,7 +432,7 @@ public async Task<ActionResult> AssignUsersToMcp(int mcpId, [FromBody] AssignUse
404432 McpServer ? server = await db . McpServers
405433 . Include ( x => x . UserMcps )
406434 . FirstOrDefaultAsync ( x => x . Id == mcpId , cancellationToken ) ;
407-
435+
408436 if ( server == null )
409437 {
410438 return NotFound ( ) ;
@@ -421,13 +449,17 @@ public async Task<ActionResult> AssignUsersToMcp(int mcpId, [FromBody] AssignUse
421449 . Where ( u => allUserIds . Contains ( u . Id ) )
422450 . Select ( u => u . Id )
423451 . ToListAsync ( cancellationToken ) ;
424-
452+
425453 if ( existingUserIds . Count != allUserIds . Count )
426454 {
427455 List < int > missingUserIds = [ .. allUserIds . Except ( existingUserIds ) ] ;
428456 return BadRequest ( $ "User IDs not found: { string . Join ( ", " , missingUserIds ) } ") ;
429457 }
430458
459+ bool assignerShowShortcut = server . UserMcps
460+ . FirstOrDefault ( um => um . UserId == currentUser . Id )
461+ ? . ShowShortcut ?? false ;
462+
431463 // 处理新分配的用户
432464 foreach ( AssignedUserInfo userInfo in request . ToAssignedUsers )
433465 {
@@ -447,6 +479,7 @@ public async Task<ActionResult> AssignUsersToMcp(int mcpId, [FromBody] AssignUse
447479 {
448480 UserId = userInfo . Id ,
449481 CustomHeaders = userInfo . CustomHeaders ,
482+ ShowShortcut = userInfo . ShowShortcut ?? assignerShowShortcut ,
450483 McpServerId = mcpId
451484 } ) ;
452485 }
@@ -466,6 +499,10 @@ public async Task<ActionResult> AssignUsersToMcp(int mcpId, [FromBody] AssignUse
466499 }
467500
468501 existingAssignment . CustomHeaders = userInfo . CustomHeaders ;
502+ if ( userInfo . ShowShortcut . HasValue )
503+ {
504+ existingAssignment . ShowShortcut = userInfo . ShowShortcut . Value ;
505+ }
469506 }
470507
471508 // 处理删除的用户
@@ -491,9 +528,9 @@ public async Task<ActionResult> AssignUsersToMcp(int mcpId, [FromBody] AssignUse
491528
492529 [ HttpGet ( "{mcpId:int}/get-unassigned-users" ) ]
493530 public async Task < ActionResult < UnassignedUserDto [ ] > > GetUnassignedUsers (
494- int mcpId ,
495- [ FromQuery ] string ? search = null ,
496- [ FromQuery ] int limit = 10 ,
531+ int mcpId ,
532+ [ FromQuery ] string ? search = null ,
533+ [ FromQuery ] int limit = 10 ,
497534 CancellationToken cancellationToken = default )
498535 {
499536 // 只有管理员可以调用此API
@@ -558,13 +595,41 @@ public async Task<ActionResult<AssignedUserDetailsDto[]>> GetAssignedUserDetails
558595 {
559596 Id = um . UserId ,
560597 UserName = um . User . DisplayName ,
561- CustomHeaders = um . CustomHeaders
598+ CustomHeaders = um . CustomHeaders ,
599+ ShowShortcut = um . ShowShortcut ,
562600 } )
563601 . ToArrayAsync ( cancellationToken ) ;
564602
565603 return Ok ( assignedUsers ) ;
566604 }
567605
606+ [ HttpPut ( "{mcpId:int}/my-assignment" ) ]
607+ public async Task < ActionResult > UpdateMyMcpAssignment (
608+ int mcpId ,
609+ [ FromBody ] UpdateMyMcpAssignmentRequest request ,
610+ CancellationToken cancellationToken )
611+ {
612+ if ( ! ModelState . IsValid )
613+ {
614+ return BadRequest ( ModelState ) ;
615+ }
616+
617+ UserMcp ? assignment = await db . UserMcps
618+ . FirstOrDefaultAsync ( um => um . McpServerId == mcpId && um . UserId == currentUser . Id , cancellationToken ) ;
619+ if ( assignment is null )
620+ {
621+ return NotFound ( ) ;
622+ }
623+
624+ if ( assignment . ShowShortcut != request . ShowShortcut )
625+ {
626+ assignment . ShowShortcut = request . ShowShortcut ;
627+ await db . SaveChangesAsync ( cancellationToken ) ;
628+ }
629+
630+ return Ok ( ) ;
631+ }
632+
568633 [ HttpGet ( "{mcpId:int}/assigned-user-names" ) ]
569634 public async Task < ActionResult < AssignedUserNameDto [ ] > > GetAssignedUserNames ( int mcpId , CancellationToken cancellationToken )
570635 {
0 commit comments