Skip to content
Merged
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
103 changes: 103 additions & 0 deletions docker.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@ const FormData = require('form-data')
const Docker = require('dockerode')
const { WebSocket } = require('ws')

/**
* @typedef {object} accessToken
* @property {string} [scheme] - auth scheme, e.g. 'Bearer'. Basic auth is currently rejected.
* @property {string} [token] - the token value
* @property {string|string[]} [scope] - token scope(s); only tokens including `ff-expert:mcp` are honoured
*
* @typedef {object} McpEndpointSpec - Specification for an MCP endpoint. Additional properties are allowed and will be passed back in the result (for correlation purposes).
* @property {string} endpoint - The path of the MCP server e.g. '/mcp'. Should not contain the host/port; those are determined to agent/launcher
Comment thread
Steve-Mcl marked this conversation as resolved.
* @property {object} [headers] - extra request headers to send with every MCP HTTP request
* @property {accessToken} [accessToken] - access token; merged into `Authorization` when scoped for MCP
*/

const createContainer = async (project, domain) => {
const stack = project.ProjectStack.properties
const contOptions = {
Expand Down Expand Up @@ -635,6 +647,97 @@ module.exports = {
}
})
},

// #region MCP Support

// MCP ROUTE: step 4 (hosted)
// Called By: forge app (forge/containers/wrapper.js)
// Calls To : launchers `command` endpoint in lib/admin.js

/**
* Get MCP features
* @param {Project} project - the project model instance
* @param {Array<string|McpEndpointSpec>} endpoints - list of MCP endpoints to query.
* Each entry may be a bare URL/path string, or an object `{ endpoint, headers?, accessToken? }`
* @returns {Object} MCP features
Comment thread
Steve-Mcl marked this conversation as resolved.
*/
getMCPFeatures: async (project, endpoints) => {
if (this._projects[project.id] === undefined) {
throw new Error('Instance cannot get MCP features')
}
Comment thread
Steve-Mcl marked this conversation as resolved.
try {
const response = await got.post('http://' + project.id + ':2880/flowforge/command', {
json: {
cmd: 'mcp:get-features',
data: {
endpoints
}
}
}).json()
return response
} catch (error) {
throw new Error(`Failed to get MCP features: ${error.message}`)
}
Comment thread
Steve-Mcl marked this conversation as resolved.
},

/**
* Call MCP endpoint
* @param {Project} project - the project model instance
* @param {string|McpEndpointSpec} endpoint - MCP endpoint to call.
* @param {string} name - Name of the MCP tool to call
* @param {Object} input - Arguments to pass to the MCP tool
* @returns {Object} MCP tool result
*/
callMCPTool: async (project, endpoint, name, input) => {
if (this._projects[project.id] === undefined) {
throw new Error('Instance cannot call MCP tool')
}
Comment thread
Steve-Mcl marked this conversation as resolved.
try {
const response = await got.post('http://' + project.id + ':2880/flowforge/command', {
json: {
cmd: 'mcp:call-tool',
data: {
endpoint,
name,
input
}
}
}).json()
return response
} catch (error) {
throw new Error(`Failed to call MCP tool: ${error.message}`)
}
},

/**
* Read MCP resource
* @param {Project} project - the project model instance
* @param {string|McpEndpointSpec} endpoint - MCP endpoint to call.
* @param {string} uri - URI of the MCP resource to read
* @returns {Object} MCP resource result
*/
readMCPResource: async (project, endpoint, uri) => {
if (this._projects[project.id] === undefined) {
throw new Error('Instance cannot read MCP resource')
}
Comment thread
Steve-Mcl marked this conversation as resolved.
try {
const response = await got.post('http://' + project.id + ':2880/flowforge/command', {
json: {
cmd: 'mcp:read-resource',
data: {
endpoint,
uri
}
}
}).json()
return response
} catch (error) {
throw new Error(`Failed to read MCP resource: ${error.message}`)
}
},

// #endregion MCP Support

/**
* Logout Node-RED instance
* @param {Project} project - the project model instance
Expand Down