Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/patch-remove-safe-outputs-session-management.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 12 additions & 4 deletions .github/workflows/smoke-codex.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 2 additions & 6 deletions actions/setup/js/safe-outputs-mcp-server.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,20 @@ const { startHttpServer } = require("./safe_outputs_mcp_server_http.cjs");

logger.debug("Successfully required safe_outputs_mcp_server_http.cjs");

// Start the HTTP server
// If run directly, start the HTTP server
Copy link
Contributor

Choose a reason for hiding this comment

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

The updated comment "If run directly, start the HTTP server" is clearer than the previous version. The removal of the stateless-mode explanation comment is appropriate since that context is now documented in safe_outputs_mcp_server_http.cjs directly.

// The server reads configuration from ${RUNNER_TEMP}/gh-aw/safeoutputs/config.json
// Port and API key are configured via environment variables:
// - GH_AW_SAFE_OUTPUTS_PORT
// - GH_AW_SAFE_OUTPUTS_API_KEY
// Log directory is configured via GH_AW_MCP_LOG_DIR environment variable
//
// NOTE: The server runs in stateless mode (no session management) because
// the MCP gateway doesn't perform the MCP protocol initialization handshake.
// It directly calls methods like tools/list without the Mcp-Session-Id header.
if (require.main === module) {
logger.debug("In require.main === module block");
const port = parseInt(process.env.GH_AW_SAFE_OUTPUTS_PORT || "3001", 10);
const logDir = process.env.GH_AW_MCP_LOG_DIR;
logger.debug(`Port: ${port}, LogDir: ${logDir}`);
logger.debug("Calling startHttpServer...");

startHttpServer({ port, logDir, stateless: true }).catch(error => {
startHttpServer({ port, logDir }).catch(error => {
logger.debugError("Failed to start safe-outputs HTTP server: ", error);
process.exit(1);
});
Expand Down
22 changes: 8 additions & 14 deletions actions/setup/js/safe_outputs_mcp_server_http.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,20 @@ moduleLogger.debug("Module is being loaded");
* This module extends the safe-outputs MCP server to support HTTP transport
* using the StreamableHTTPServerTransport from the MCP SDK.
*
* It provides both stateful and stateless HTTP modes, as well as SSE streaming.
* The server runs in stateless mode (no session management) because the MCP
* gateway does not perform the MCP protocol initialization handshake and
* directly calls methods like tools/list without an Mcp-Session-Id header.
*
* Usage:
* node safe_outputs_mcp_server_http.cjs [--port 3000] [--stateless]
* node safe_outputs_mcp_server_http.cjs [--port 3000]
*
* Options:
* --port <number> Port to listen on (default: 3000)
* --stateless Run in stateless mode (no session management)
* --log-dir <path> Directory for log files
*/

const http = require("http");
moduleLogger.debug("Loaded http");
const { randomUUID } = require("crypto");
moduleLogger.debug("Loaded crypto");
const { MCPServer, MCPHTTPTransport } = require("./mcp_http_transport.cjs");
moduleLogger.debug("Loaded mcp_http_transport.cjs");
const { createLogger: createMCPLogger } = require("./mcp_logger.cjs");
Expand Down Expand Up @@ -222,19 +221,17 @@ function createMCPServer(options = {}) {
* Start the HTTP server with MCP protocol support
* @param {Object} options - Server options
* @param {number} [options.port] - Port to listen on (default: 3000)
* @param {boolean} [options.stateless] - Run in stateless mode (default: false)
* @param {string} [options.logDir] - Override log directory from config
*/
async function startHttpServer(options = {}) {
const port = options.port || 3000;
const stateless = options.stateless || false;

const logger = createMCPLogger("safe-outputs-startup");

logger.debug(`startHttpServer called with port=${port}, stateless=${stateless}`);
logger.debug(`startHttpServer called with port=${port}`);
logger.debug(`=== Starting Safe Outputs MCP HTTP Server ===`);
logger.debug(`Port: ${port}`);
logger.debug(`Mode: ${stateless ? "stateless" : "stateful"}`);
logger.debug(`Mode: stateless`);
Copy link
Contributor

Choose a reason for hiding this comment

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

Hardcoding "Mode: stateless" is clean now that stateful mode is removed. Consider also removing the logger.debug for mode entirely if all deployments are always stateless — reduces noise in logs.

logger.debug(`Environment: NODE_VERSION=${process.version}, PLATFORM=${process.platform}`);

// Create the MCP server
Expand All @@ -251,9 +248,9 @@ async function startHttpServer(options = {}) {
logger.debug(`Tools configured: ${Object.keys(config).filter(k => config[k]).length}`);

logger.debug(`Creating HTTP transport...`);
// Create the HTTP transport
// Create the HTTP transport in stateless mode (no session management)
const transport = new MCPHTTPTransport({
sessionIdGenerator: stateless ? undefined : () => randomUUID(),
sessionIdGenerator: undefined,
enableJsonResponse: true,
enableDnsRebindingProtection: false, // Disable for local development
});
Expand Down Expand Up @@ -433,7 +430,6 @@ if (require.main === module) {

const options = {
port: 3000,
stateless: false,
/** @type {string | undefined} */
logDir: undefined,
};
Expand All @@ -443,8 +439,6 @@ if (require.main === module) {
if (args[i] === "--port" && args[i + 1]) {
options.port = parseInt(args[i + 1], 10);
i++;
} else if (args[i] === "--stateless") {
options.stateless = true;
} else if (args[i] === "--log-dir" && args[i + 1]) {
options.logDir = args[i + 1];
i++;
Expand Down
Loading