Skip to content

Commit 92ff509

Browse files
committed
feat(all): add reconnect button for offline HTTP/SSE MCP servers
Backend: add POST /teams/:teamId/mcp/installations/:installationId/reconnect endpoint that performs a template-level health check and triggers recovery for all affected instances when the remote server is reachable again. Make McpHealthCheckService.handleRecovery public for route reuse. Frontend: add Reconnect button in GeneralTab MCP Status card, visible only when instance status is offline/error and transport type is http/sse. Shows success toast on recovery or warning toast if server is still down.
1 parent 2c9cae0 commit 92ff509

File tree

8 files changed

+747
-1
lines changed

8 files changed

+747
-1
lines changed

services/backend/api-spec.json

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14527,6 +14527,217 @@
1452714527
}
1452814528
}
1452914529
},
14530+
"/api/teams/{teamId}/mcp/installations/{installationId}/reconnect": {
14531+
"post": {
14532+
"summary": "Reconnect offline HTTP/SSE MCP server",
14533+
"tags": [
14534+
"MCP Installations"
14535+
],
14536+
"description": "Triggers an immediate health check for a remote HTTP/SSE MCP server and initiates recovery if the server is reachable again. Only works for HTTP/SSE servers with offline or error status.",
14537+
"parameters": [
14538+
{
14539+
"schema": {
14540+
"type": "string",
14541+
"minLength": 1
14542+
},
14543+
"in": "path",
14544+
"name": "teamId",
14545+
"required": true,
14546+
"description": "Team ID that owns the installation"
14547+
},
14548+
{
14549+
"schema": {
14550+
"type": "string",
14551+
"minLength": 1
14552+
},
14553+
"in": "path",
14554+
"name": "installationId",
14555+
"required": true,
14556+
"description": "Installation ID"
14557+
}
14558+
],
14559+
"security": [
14560+
{
14561+
"cookieAuth": []
14562+
},
14563+
{
14564+
"bearerAuth": []
14565+
}
14566+
],
14567+
"responses": {
14568+
"200": {
14569+
"description": "Reconnection result",
14570+
"content": {
14571+
"application/json": {
14572+
"schema": {
14573+
"type": "object",
14574+
"properties": {
14575+
"success": {
14576+
"type": "boolean"
14577+
},
14578+
"data": {
14579+
"type": "object",
14580+
"properties": {
14581+
"status": {
14582+
"type": "string",
14583+
"enum": [
14584+
"recovering",
14585+
"still_offline"
14586+
]
14587+
},
14588+
"message": {
14589+
"type": "string"
14590+
},
14591+
"health_check": {
14592+
"type": "object",
14593+
"properties": {
14594+
"error": {
14595+
"type": "string"
14596+
},
14597+
"responseTimeMs": {
14598+
"type": "number"
14599+
}
14600+
}
14601+
}
14602+
},
14603+
"required": [
14604+
"status",
14605+
"message"
14606+
]
14607+
}
14608+
},
14609+
"required": [
14610+
"success",
14611+
"data"
14612+
],
14613+
"description": "Reconnection result"
14614+
}
14615+
}
14616+
}
14617+
},
14618+
"400": {
14619+
"description": "Bad Request - Server is not HTTP/SSE or instance is not in reconnectable state",
14620+
"content": {
14621+
"application/json": {
14622+
"schema": {
14623+
"type": "object",
14624+
"properties": {
14625+
"success": {
14626+
"type": "boolean",
14627+
"default": false
14628+
},
14629+
"error": {
14630+
"type": "string"
14631+
}
14632+
},
14633+
"required": [
14634+
"success",
14635+
"error"
14636+
],
14637+
"description": "Bad Request - Server is not HTTP/SSE or instance is not in reconnectable state"
14638+
}
14639+
}
14640+
}
14641+
},
14642+
"401": {
14643+
"description": "Unauthorized - Authentication required",
14644+
"content": {
14645+
"application/json": {
14646+
"schema": {
14647+
"type": "object",
14648+
"properties": {
14649+
"success": {
14650+
"type": "boolean",
14651+
"default": false
14652+
},
14653+
"error": {
14654+
"type": "string"
14655+
}
14656+
},
14657+
"required": [
14658+
"success",
14659+
"error"
14660+
],
14661+
"description": "Unauthorized - Authentication required"
14662+
}
14663+
}
14664+
}
14665+
},
14666+
"403": {
14667+
"description": "Forbidden - Insufficient permissions",
14668+
"content": {
14669+
"application/json": {
14670+
"schema": {
14671+
"type": "object",
14672+
"properties": {
14673+
"success": {
14674+
"type": "boolean",
14675+
"default": false
14676+
},
14677+
"error": {
14678+
"type": "string"
14679+
}
14680+
},
14681+
"required": [
14682+
"success",
14683+
"error"
14684+
],
14685+
"description": "Forbidden - Insufficient permissions"
14686+
}
14687+
}
14688+
}
14689+
},
14690+
"404": {
14691+
"description": "Not Found - Installation or instance not found",
14692+
"content": {
14693+
"application/json": {
14694+
"schema": {
14695+
"type": "object",
14696+
"properties": {
14697+
"success": {
14698+
"type": "boolean",
14699+
"default": false
14700+
},
14701+
"error": {
14702+
"type": "string"
14703+
}
14704+
},
14705+
"required": [
14706+
"success",
14707+
"error"
14708+
],
14709+
"description": "Not Found - Installation or instance not found"
14710+
}
14711+
}
14712+
}
14713+
},
14714+
"500": {
14715+
"description": "Internal Server Error",
14716+
"content": {
14717+
"application/json": {
14718+
"schema": {
14719+
"type": "object",
14720+
"properties": {
14721+
"success": {
14722+
"type": "boolean",
14723+
"default": false
14724+
},
14725+
"error": {
14726+
"type": "string"
14727+
}
14728+
},
14729+
"required": [
14730+
"success",
14731+
"error"
14732+
],
14733+
"description": "Internal Server Error"
14734+
}
14735+
}
14736+
}
14737+
}
14738+
}
14739+
}
14740+
},
1453014741
"/api/teams/{teamId}/satellites": {
1453114742
"get": {
1453214743
"summary": "Get available satellites for team",

services/backend/api-spec.yaml

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10161,6 +10161,148 @@ paths:
1016110161
responses:
1016210162
"200":
1016310163
description: Default Response
10164+
/api/teams/{teamId}/mcp/installations/{installationId}/reconnect:
10165+
post:
10166+
summary: Reconnect offline HTTP/SSE MCP server
10167+
tags:
10168+
- MCP Installations
10169+
description: Triggers an immediate health check for a remote HTTP/SSE MCP server
10170+
and initiates recovery if the server is reachable again. Only works for
10171+
HTTP/SSE servers with offline or error status.
10172+
parameters:
10173+
- schema:
10174+
type: string
10175+
minLength: 1
10176+
in: path
10177+
name: teamId
10178+
required: true
10179+
description: Team ID that owns the installation
10180+
- schema:
10181+
type: string
10182+
minLength: 1
10183+
in: path
10184+
name: installationId
10185+
required: true
10186+
description: Installation ID
10187+
security:
10188+
- cookieAuth: []
10189+
- bearerAuth: []
10190+
responses:
10191+
"200":
10192+
description: Reconnection result
10193+
content:
10194+
application/json:
10195+
schema:
10196+
type: object
10197+
properties:
10198+
success:
10199+
type: boolean
10200+
data:
10201+
type: object
10202+
properties:
10203+
status:
10204+
type: string
10205+
enum:
10206+
- recovering
10207+
- still_offline
10208+
message:
10209+
type: string
10210+
health_check:
10211+
type: object
10212+
properties:
10213+
error:
10214+
type: string
10215+
responseTimeMs:
10216+
type: number
10217+
required:
10218+
- status
10219+
- message
10220+
required:
10221+
- success
10222+
- data
10223+
description: Reconnection result
10224+
"400":
10225+
description: Bad Request - Server is not HTTP/SSE or instance is not in
10226+
reconnectable state
10227+
content:
10228+
application/json:
10229+
schema:
10230+
type: object
10231+
properties:
10232+
success:
10233+
type: boolean
10234+
default: false
10235+
error:
10236+
type: string
10237+
required:
10238+
- success
10239+
- error
10240+
description: Bad Request - Server is not HTTP/SSE or instance is not in
10241+
reconnectable state
10242+
"401":
10243+
description: Unauthorized - Authentication required
10244+
content:
10245+
application/json:
10246+
schema:
10247+
type: object
10248+
properties:
10249+
success:
10250+
type: boolean
10251+
default: false
10252+
error:
10253+
type: string
10254+
required:
10255+
- success
10256+
- error
10257+
description: Unauthorized - Authentication required
10258+
"403":
10259+
description: Forbidden - Insufficient permissions
10260+
content:
10261+
application/json:
10262+
schema:
10263+
type: object
10264+
properties:
10265+
success:
10266+
type: boolean
10267+
default: false
10268+
error:
10269+
type: string
10270+
required:
10271+
- success
10272+
- error
10273+
description: Forbidden - Insufficient permissions
10274+
"404":
10275+
description: Not Found - Installation or instance not found
10276+
content:
10277+
application/json:
10278+
schema:
10279+
type: object
10280+
properties:
10281+
success:
10282+
type: boolean
10283+
default: false
10284+
error:
10285+
type: string
10286+
required:
10287+
- success
10288+
- error
10289+
description: Not Found - Installation or instance not found
10290+
"500":
10291+
description: Internal Server Error
10292+
content:
10293+
application/json:
10294+
schema:
10295+
type: object
10296+
properties:
10297+
success:
10298+
type: boolean
10299+
default: false
10300+
error:
10301+
type: string
10302+
required:
10303+
- success
10304+
- error
10305+
description: Internal Server Error
1016410306
/api/teams/{teamId}/satellites:
1016510307
get:
1016610308
summary: Get available satellites for team

services/backend/src/routes/teams/mcp-installations/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import getRequestByIdRoute from './request-by-id';
1212
import updateInstallationSettingsRoute from './settings';
1313
import getInstallationLogsStreamRoute from './logs-stream';
1414
import getInstallationRequestsStreamRoute from './requests-stream';
15+
import reconnectInstallationRoute from './reconnect';
1516

1617
export default async function mcpInstallationsRoutes(fastify: FastifyInstance) {
1718
await fastify.register(getInstallationToolsRoute);
@@ -27,4 +28,5 @@ export default async function mcpInstallationsRoutes(fastify: FastifyInstance) {
2728
await fastify.register(updateInstallationSettingsRoute);
2829
await fastify.register(getInstallationLogsStreamRoute);
2930
await fastify.register(getInstallationRequestsStreamRoute);
31+
await fastify.register(reconnectInstallationRoute);
3032
}

0 commit comments

Comments
 (0)