diff --git a/packages/agent-bff/README.md b/packages/agent-bff/README.md index df0ce6a38c..82e64c41e7 100644 --- a/packages/agent-bff/README.md +++ b/packages/agent-bff/README.md @@ -1,5 +1,11 @@ # @forestadmin/agent-bff +> [!IMPORTANT] +> Internal building block of the Forest Gateway. For new deployments, use +> [`@forestadmin/gateway`](https://docs.forestadmin.com/product/embed/gateway-standalone). The +> `forest-bff` command keeps working, deprecated: moving to `forest-gateway` serves the API under +> `/api`. + Standalone REST BFF (Backend-For-Frontend) that lets a trusted third-party UI call a Forest agent from a browser without learning MCP or JSON:API. diff --git a/packages/agent-bff/src/cli-dispatch.ts b/packages/agent-bff/src/cli-dispatch.ts index 9adb192b5d..25263c146c 100644 --- a/packages/agent-bff/src/cli-dispatch.ts +++ b/packages/agent-bff/src/cli-dispatch.ts @@ -27,6 +27,11 @@ Options: export const HINT = "Run 'forest-bff --help' for usage."; +export const DEPRECATION_WARNING = + 'forest-bff is deprecated: it keeps working, but new deployments should run forest-gateway ' + + '(@forestadmin/gateway). Moving changes the API URL to /api: ' + + 'https://docs.forestadmin.com/product/embed/gateway-standalone'; + const HELP_FLAGS = new Set(['-h', '--help']); const VERSION_FLAGS = new Set(['-v', '--version']); @@ -49,7 +54,10 @@ export default async function dispatchCli( const [subcommand, ...rest] = argv; if (subcommand === undefined) { - return { exitCode: 0, server: await runCli(env, logger) }; + const bootLogger = logger ?? createConsoleLogger(); + bootLogger('Warn', DEPRECATION_WARNING); + + return { exitCode: 0, server: await runCli(env, bootLogger) }; } if (HELP_FLAGS.has(subcommand)) { diff --git a/packages/agent-bff/test/openapi/openapi-cli.test.ts b/packages/agent-bff/test/openapi/openapi-cli.test.ts index 5b786e9dd5..9db2782194 100644 --- a/packages/agent-bff/test/openapi/openapi-cli.test.ts +++ b/packages/agent-bff/test/openapi/openapi-cli.test.ts @@ -8,7 +8,12 @@ import { tmpdir } from 'os'; import path from 'path'; import request from 'supertest'; -import dispatchCli, { DEFAULT_OUTPUT_FILE, HINT, USAGE } from '../../src/cli-dispatch'; +import dispatchCli, { + DEFAULT_OUTPUT_FILE, + DEPRECATION_WARNING, + HINT, + USAGE, +} from '../../src/cli-dispatch'; import { issueBffAccessToken } from '../../src/oauth/bff-token'; import { OPENAPI_PATH } from '../../src/openapi/openapi-routes'; import renderOpenApi from '../../src/openapi/render-openapi'; @@ -394,6 +399,37 @@ describe('dispatchCli', () => { await outcome.server?.stop(); } }); + + it('should warn once that forest-bff is deprecated in favor of forest-gateway', async () => { + const logger = jest.fn(); + const outcome = await dispatchCli([], VALID_ENV, logger); + + try { + const deprecations = logger.mock.calls.filter(([, message]) => + String(message).includes('deprecated'), + ); + + expect(deprecations).toEqual([['Warn', DEPRECATION_WARNING]]); + expect(DEPRECATION_WARNING).toContain('forest-gateway'); + } finally { + await outcome.server?.stop(); + } + }); + }); + + describe('when the openapi subcommand runs', () => { + it('should not warn about the deprecation, since it serves nothing', async () => { + const stdout = jest.spyOn(process.stdout, 'write').mockImplementation(() => true); + const logger = jest.fn(); + + try { + await dispatchCli(['openapi'], {}, logger); + + expect(logger).not.toHaveBeenCalledWith('Warn', DEPRECATION_WARNING); + } finally { + stdout.mockRestore(); + } + }); }); describe.each([ diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md index c0ca93b959..5ed8e009f1 100644 --- a/packages/mcp-server/README.md +++ b/packages/mcp-server/README.md @@ -1,5 +1,10 @@ # @forestadmin/mcp-server +> [!IMPORTANT] +> Internal building block of the Forest Gateway. For new deployments, use +> [`@forestadmin/gateway`](https://docs.forestadmin.com/product/embed/gateway-standalone). The +> `forest-mcp-server` command keeps working, deprecated. + Model Context Protocol (MCP) server for Forest Admin with OAuth authentication support. ## Overview diff --git a/packages/mcp-server/src/cli.ts b/packages/mcp-server/src/cli.ts index b8d5ece5a1..1e90a9359d 100644 --- a/packages/mcp-server/src/cli.ts +++ b/packages/mcp-server/src/cli.ts @@ -4,7 +4,14 @@ import parseMcpEnv from './mcp-env'; import ForestMCPServer from './server'; import loadFileUploads from './utils/load-file-uploads'; +const DEPRECATION_WARNING = + 'forest-mcp-server is deprecated: it keeps working, but new deployments should run ' + + 'forest-gateway (@forestadmin/gateway): ' + + 'https://docs.forestadmin.com/product/embed/gateway-standalone'; + async function main() { + console.warn(DEPRECATION_WARNING); + const { options, uploadStorageModule } = parseMcpEnv(process.env); const fileUploads = await loadFileUploads(uploadStorageModule); diff --git a/packages/mcp-server/test/cli.test.ts b/packages/mcp-server/test/cli.test.ts index 8a32eed402..0387c5af82 100644 --- a/packages/mcp-server/test/cli.test.ts +++ b/packages/mcp-server/test/cli.test.ts @@ -97,6 +97,20 @@ describe('forest-mcp-server', () => { expect(Server.mock.results[0].value.run).toHaveBeenCalledWith(); }); + it('warns once that it is deprecated in favor of forest-gateway', async () => { + const consoleWarn = jest.spyOn(console, 'warn').mockImplementation(); + + await startCli({}); + + const deprecations = consoleWarn.mock.calls.filter(([message]) => + String(message).includes('deprecated'), + ); + + expect(deprecations).toHaveLength(1); + expect(deprecations[0][0]).toContain('forest-mcp-server is deprecated'); + expect(deprecations[0][0]).toContain('forest-gateway'); + }); + it("passes fileUploads: false on FOREST_MCP_FILE_UPLOADS='false', module or not", async () => { const { Server } = await startCli({ FOREST_MCP_FILE_UPLOADS: 'false',