Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/user/expert/chat.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ For example, if you ask Expert to "add three nodes" with Write set to *Ask*, it
| Action type | What it covers | Actions |
|---|---|---|
| **Read** | View only, no changes | List Applications, Get Application, Get Application Hosted Instances, Get Application Remote Instances, Get Application Instances Status, Get Application Audit Log, List Teams, Get Team, Get Hosted Instance, Get Hosted Instance Status, Get Hosted Instance Logs, Check Hosted Instance Name Availability, List Team Remote Instances, Get Remote Instance, Get Remote Instance Status, List Hosted Instance Snapshots, List Remote Instance Snapshots, List Hosted Instance Types, List Stacks, List Templates, List Blueprints, Open Hosted Instance, Open Hosted Instance Editor |
| **Write** | Create or change resources | Create Application, Create Hosted Instance, Create Remote Instance, Assign Remote Instance To Application, Create Hosted Instance Snapshot, Create Remote Instance Snapshot |
| **Write** | Create or change resources | Create Application, Create Hosted Instance, Create Remote Instance, Assign Remote Instance To Application, Create Hosted Instance Snapshot, Create Remote Instance Snapshot, Suspend Hosted Instance |

#### Context: What the Expert Can See

Expand Down
17 changes: 17 additions & 0 deletions forge/ee/lib/mcp/tools/instances.js
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,23 @@ module.exports = [
const response = await inject({ method: 'GET', url: `/api/v1/teams/${args.teamId}/dashboard-instances` })
return response
}
},
{
name: 'platform_suspend_hosted_instance',
title: 'Suspend Hosted Instance',
description: `FlowFuse platform automation tool:
Suspends a hosted instance, stopping its running Node-RED runtime.
Credentials, flow and packages are stored securely, and will continue execution when the hosted instance is started again.
Use this to stop an instance from running (e.g. to save on resources) without deleting it.
Check platform_get_hosted_instance_status first if you need to confirm the instance is currently running before suspending it.`,
annotations: { readOnlyHint: false, destructiveHint: false },
inputSchema: {
hostedInstanceId: z.string().describe('The ID or hashid of the hosted instance')

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The questions is a bit if a z.number() or so would be better. The fact we're interpolating it later made me decide a string was fine.

},
handler: async (args, { inject }) => {
const response = await inject({ method: 'POST', url: `/api/v1/projects/${args.hostedInstanceId}/actions/suspend` })
return response
}
}
]

Expand Down
21 changes: 21 additions & 0 deletions test/unit/forge/ee/lib/mcp/tools/instances_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -585,4 +585,25 @@ describe('MCP Instances Tools', function () {
response.should.equal(routeResponse)
})
})

describe('platform_suspend_hosted_instance', function () {
const tool = getTool('platform_suspend_hosted_instance')

it('posts to the suspend action endpoint for the given instance', async function () {
const suspendResponse = { statusCode: 200, json: () => ({ status: 'okay' }) }
inject.withArgs({ method: 'POST', url: '/api/v1/projects/instance1/actions/suspend' }).resolves(suspendResponse)

const response = await tool.handler({ hostedInstanceId: 'instance1' }, { inject })

response.should.equal(suspendResponse)
})

it('passes through an error response', async function () {
const errorResponse = { statusCode: 400, json: () => ({ code: 'project_suspended' }) }
inject.resolves(errorResponse)

const response = await tool.handler({ hostedInstanceId: 'instance1' }, { inject })
response.should.equal(errorResponse)
})
})
})
Loading