Skip to content

Commit fdafd47

Browse files
author
andypalmi
committed
feat(mcp): add instance configuration read tools
Add read-only MCP tools for HA, custom hostnames, protected instance, auto-update stacks, static files and HTTP security config, with unit and equivalence tests. Allow-list their scopes for the expert-mcp platform token.
1 parent 913be23 commit fdafd47

3 files changed

Lines changed: 416 additions & 0 deletions

File tree

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
const { z } = require('zod')
2+
3+
const { hostedInstanceId, remoteInstanceId } = require('../schemas')
4+
5+
module.exports = [
6+
{
7+
name: 'platform_get_instance_ha',
8+
title: 'Get Instance High Availability',
9+
description: `FlowFuse platform automation tool:
10+
Returns the High Availability configuration for a hosted instance.
11+
High Availability runs an instance across multiple replicas so it stays up if one replica fails.
12+
High Availability is a plan-gated feature: a team without it enabled gets a 404 error.`,
13+
annotations: { readOnlyHint: true, destructiveHint: false },
14+
inputSchema: {
15+
hostedInstanceId
16+
},
17+
handler: async (args, { inject }) => {
18+
const response = await inject({ method: 'GET', url: `/api/v1/projects/${args.hostedInstanceId}/ha` })
19+
return response
20+
}
21+
},
22+
{
23+
name: 'platform_get_instance_custom_hostname',
24+
title: 'Get Instance Custom Hostname',
25+
description: `FlowFuse platform automation tool:
26+
Returns the custom hostname configured for a hosted instance.
27+
Custom hostnames are a plan-gated feature: a team without it enabled gets a 404 error.
28+
Use platform_get_instance_custom_hostname_status to check whether the hostname is verified and routable.`,
29+
annotations: { readOnlyHint: true, destructiveHint: false },
30+
inputSchema: {
31+
hostedInstanceId
32+
},
33+
handler: async (args, { inject }) => {
34+
const response = await inject({ method: 'GET', url: `/api/v1/projects/${args.hostedInstanceId}/customHostname` })
35+
return response
36+
}
37+
},
38+
{
39+
name: 'platform_get_instance_custom_hostname_status',
40+
title: 'Get Instance Custom Hostname Status',
41+
description: `FlowFuse platform automation tool:
42+
Returns the status of the custom hostname for a hosted instance, i.e. whether the DNS
43+
CNAME record has been set up correctly and points at the platform.
44+
A 200 response means the hostname is verified. A 410 response means a hostname is set
45+
but its CNAME record does not resolve to the platform yet. A 404 response can mean no
46+
custom hostname is configured, the platform does not support hostname verification, or
47+
the custom hostname feature is not enabled for the team.`,
48+
annotations: { readOnlyHint: true, destructiveHint: false },
49+
inputSchema: {
50+
hostedInstanceId
51+
},
52+
handler: async (args, { inject }) => {
53+
const response = await inject({ method: 'GET', url: `/api/v1/projects/${args.hostedInstanceId}/customHostname/status` })
54+
return response
55+
}
56+
},
57+
{
58+
name: 'platform_get_instance_protection',
59+
title: 'Get Instance Protection',
60+
description: `FlowFuse platform automation tool:
61+
Returns the protected-instance configuration for a hosted instance.
62+
A protected instance requires extra confirmation before destructive actions such as
63+
suspension or deletion can be performed against it.
64+
Protected instance is a plan-gated feature: a team without it enabled gets a 404 error.`,
65+
annotations: { readOnlyHint: true, destructiveHint: false },
66+
inputSchema: {
67+
hostedInstanceId
68+
},
69+
handler: async (args, { inject }) => {
70+
const response = await inject({ method: 'GET', url: `/api/v1/projects/${args.hostedInstanceId}/protectInstance` })
71+
return response
72+
}
73+
},
74+
{
75+
name: 'platform_get_instance_auto_update_stack',
76+
title: 'Get Instance Auto-Update Stack Schedule',
77+
description: `FlowFuse platform automation tool:
78+
Returns the auto-update stack (weekly restart) schedule for a hosted instance.
79+
This schedule controls the windows in which the platform is allowed to automatically
80+
restart the instance to apply a stack update.
81+
This surface has no plan gate: a 404 response means the instance does not exist.`,
82+
annotations: { readOnlyHint: true, destructiveHint: false },
83+
inputSchema: {
84+
hostedInstanceId
85+
},
86+
handler: async (args, { inject }) => {
87+
const response = await inject({ method: 'GET', url: `/api/v1/projects/${args.hostedInstanceId}/autoUpdateStack` })
88+
return response
89+
}
90+
},
91+
{
92+
name: 'platform_list_instance_files',
93+
title: 'List Instance Files',
94+
description: `FlowFuse platform automation tool:
95+
Lists files and directories within a hosted instance file store at the given path.
96+
Use an empty string for the path to list the root of the file store.
97+
Static file storage is a plan-gated feature: a team without it enabled gets a 404 error.`,
98+
annotations: { readOnlyHint: true, destructiveHint: false },
99+
inputSchema: {
100+
hostedInstanceId,
101+
path: z.string().describe('Directory path within the instance file store to list')
102+
},
103+
handler: async (args, { inject }) => {
104+
const response = await inject({ method: 'GET', url: `/api/v1/projects/${args.hostedInstanceId}/files/_/${encodeURIComponent(args.path)}` })
105+
return response
106+
}
107+
},
108+
{
109+
name: 'platform_list_instance_http_tokens',
110+
title: 'List Instance HTTP Tokens',
111+
description: `FlowFuse platform automation tool:
112+
Lists the HTTP bearer tokens configured for a hosted instance.
113+
These tokens are used by external callers to authenticate HTTP requests handled by the
114+
instance's Node-RED flows.
115+
HTTP bearer tokens are a plan-gated feature: a team without it enabled gets a 404 error.`,
116+
annotations: { readOnlyHint: true, destructiveHint: false },
117+
inputSchema: {
118+
hostedInstanceId
119+
},
120+
handler: async (args, { inject }) => {
121+
const response = await inject({ method: 'GET', url: `/api/v1/projects/${args.hostedInstanceId}/httpTokens` })
122+
return response
123+
}
124+
},
125+
{
126+
name: 'platform_list_remote_instance_http_tokens',
127+
title: 'List Remote Instance HTTP Tokens',
128+
description: `FlowFuse platform automation tool:
129+
Lists the HTTP bearer tokens configured for a remote instance (device).
130+
These tokens are used by external callers to authenticate HTTP requests handled by the
131+
remote instance's Node-RED flows.
132+
HTTP bearer tokens are a plan-gated feature: a team without it enabled gets a 404 error.`,
133+
annotations: { readOnlyHint: true, destructiveHint: false },
134+
inputSchema: {
135+
remoteInstanceId
136+
},
137+
handler: async (args, { inject }) => {
138+
const response = await inject({ method: 'GET', url: `/api/v1/devices/${args.remoteInstanceId}/httpTokens` })
139+
return response
140+
}
141+
}
142+
]

forge/routes/auth/permissions.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ const IMPLICIT_TOKEN_SCOPES = {
4545
'project:snapshot:create', // create hosted instance snapshot
4646
'device:snapshot:list', // list remote instance snapshots
4747
'device:snapshot:create', // create remote instance snapshot
48+
// instance configuration
49+
'project:edit', // get instance custom hostname, custom hostname status and HTTP tokens
50+
'project:files:list', // list instance files
4851
// teams
4952
'user:team:list', // list teams
5053
'team:read', // get team details

0 commit comments

Comments
 (0)