@forestadmin/mcp-server 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +128 -0
  2. package/dist/__mocks__/version.d.ts +3 -0
  3. package/dist/__mocks__/version.js +7 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.js +14 -0
  6. package/dist/factory.d.ts +51 -0
  7. package/dist/factory.js +40 -0
  8. package/dist/forest-oauth-provider.d.ts +44 -0
  9. package/dist/forest-oauth-provider.js +253 -0
  10. package/dist/forest-oauth-provider.test.d.ts +2 -0
  11. package/dist/forest-oauth-provider.test.js +590 -0
  12. package/dist/index.d.ts +4 -0
  13. package/dist/index.js +13 -0
  14. package/dist/mcp-paths.d.ts +5 -0
  15. package/dist/mcp-paths.js +11 -0
  16. package/dist/polyfills.d.ts +12 -0
  17. package/dist/polyfills.js +27 -0
  18. package/dist/schemas/filter.d.ts +4 -0
  19. package/dist/schemas/filter.js +70 -0
  20. package/dist/schemas/filter.test.d.ts +2 -0
  21. package/dist/schemas/filter.test.js +234 -0
  22. package/dist/server.d.ts +87 -0
  23. package/dist/server.js +341 -0
  24. package/dist/server.test.d.ts +2 -0
  25. package/dist/server.test.js +901 -0
  26. package/dist/test-utils/mock-server.d.ts +62 -0
  27. package/dist/test-utils/mock-server.js +187 -0
  28. package/dist/tools/list.d.ts +4 -0
  29. package/dist/tools/list.js +98 -0
  30. package/dist/tools/list.test.d.ts +2 -0
  31. package/dist/tools/list.test.js +385 -0
  32. package/dist/utils/activity-logs-creator.d.ts +9 -0
  33. package/dist/utils/activity-logs-creator.js +65 -0
  34. package/dist/utils/activity-logs-creator.test.d.ts +2 -0
  35. package/dist/utils/activity-logs-creator.test.js +239 -0
  36. package/dist/utils/agent-caller.d.ts +13 -0
  37. package/dist/utils/agent-caller.js +24 -0
  38. package/dist/utils/agent-caller.test.d.ts +2 -0
  39. package/dist/utils/agent-caller.test.js +102 -0
  40. package/dist/utils/error-parser.d.ts +10 -0
  41. package/dist/utils/error-parser.js +56 -0
  42. package/dist/utils/error-parser.test.d.ts +2 -0
  43. package/dist/utils/error-parser.test.js +124 -0
  44. package/dist/utils/schema-fetcher.d.ts +53 -0
  45. package/dist/utils/schema-fetcher.js +85 -0
  46. package/dist/utils/schema-fetcher.test.d.ts +2 -0
  47. package/dist/utils/schema-fetcher.test.js +212 -0
  48. package/dist/utils/sse-error-logger.d.ts +14 -0
  49. package/dist/utils/sse-error-logger.js +112 -0
  50. package/dist/utils/tool-with-logging.d.ts +44 -0
  51. package/dist/utils/tool-with-logging.js +66 -0
  52. package/dist/version.d.ts +3 -0
  53. package/dist/version.js +43 -0
  54. package/package.json +49 -0
package/dist/server.js ADDED
@@ -0,0 +1,341 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ // Import polyfills FIRST - before any MCP SDK imports
40
+ // This ensures URL.canParse is available for MCP SDK's Zod validation
41
+ require("./polyfills");
42
+ const authorize_js_1 = require("@modelcontextprotocol/sdk/server/auth/handlers/authorize.js");
43
+ const token_js_1 = require("@modelcontextprotocol/sdk/server/auth/handlers/token.js");
44
+ const allowedMethods_js_1 = require("@modelcontextprotocol/sdk/server/auth/middleware/allowedMethods.js");
45
+ const bearerAuth_js_1 = require("@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js");
46
+ const router_js_1 = require("@modelcontextprotocol/sdk/server/auth/router.js");
47
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
48
+ const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
49
+ const cors_1 = __importDefault(require("cors"));
50
+ const express_1 = __importDefault(require("express"));
51
+ const http = __importStar(require("http"));
52
+ const forest_oauth_provider_1 = __importDefault(require("./forest-oauth-provider"));
53
+ const mcp_paths_1 = require("./mcp-paths");
54
+ const list_1 = __importDefault(require("./tools/list"));
55
+ const schema_fetcher_1 = require("./utils/schema-fetcher");
56
+ const sse_error_logger_1 = __importDefault(require("./utils/sse-error-logger"));
57
+ const version_1 = require("./version");
58
+ function getDefaultLogFn(level) {
59
+ if (level === 'Error')
60
+ return (msg) => console.error(`[MCP Server] ${msg}`);
61
+ if (level === 'Warn')
62
+ return (msg) => console.warn(`[MCP Server] ${msg}`);
63
+ return (msg) => console.info(`[MCP Server] ${msg}`);
64
+ }
65
+ const defaultLogger = (level, message) => {
66
+ getDefaultLogFn(level)(message);
67
+ };
68
+ /** Fields that are safe to log for each tool (non-sensitive data) */
69
+ const SAFE_ARGUMENTS_FOR_LOGGING = {
70
+ list: ['collectionName'],
71
+ };
72
+ /**
73
+ * Forest Admin MCP Server
74
+ *
75
+ * This server provides HTTP REST API access to Forest Admin operations
76
+ * with OAuth authentication support.
77
+ *
78
+ * Environment Variables (used as fallback when options not provided):
79
+ * - FOREST_ENV_SECRET: Your Forest Admin environment secret (required)
80
+ * - FOREST_AUTH_SECRET: Your Forest Admin authentication secret, it must be the same one as the one on your agent (required)
81
+ * - FOREST_SERVER_URL: Forest Admin server URL (optional)
82
+ * - MCP_SERVER_PORT: Port for the HTTP server (default: 3931)
83
+ */
84
+ class ForestMCPServer {
85
+ constructor(options) {
86
+ this.forestServerUrl =
87
+ options?.forestServerUrl ||
88
+ process.env.FOREST_SERVER_URL ||
89
+ process.env.FOREST_URL ||
90
+ 'https://api.forestadmin.com';
91
+ this.forestAppUrl =
92
+ options?.forestAppUrl || process.env.FOREST_APP_URL || 'https://app.forestadmin.com';
93
+ this.envSecret = options?.envSecret || process.env.FOREST_ENV_SECRET;
94
+ this.authSecret = options?.authSecret || process.env.FOREST_AUTH_SECRET;
95
+ this.logger = options?.logger || defaultLogger;
96
+ this.mcpServer = new mcp_js_1.McpServer({
97
+ name: version_1.NAME,
98
+ version: version_1.VERSION,
99
+ });
100
+ }
101
+ async setupTools() {
102
+ let collectionNames = [];
103
+ try {
104
+ const schema = await (0, schema_fetcher_1.fetchForestSchema)(this.forestServerUrl);
105
+ collectionNames = (0, schema_fetcher_1.getCollectionNames)(schema);
106
+ }
107
+ catch (error) {
108
+ this.logger('Warn', `Failed to fetch forest schema, collection names will not be available: ${error}`);
109
+ }
110
+ (0, list_1.default)(this.mcpServer, this.forestServerUrl, this.logger, collectionNames);
111
+ }
112
+ ensureSecretsAreSet() {
113
+ if (!this.envSecret) {
114
+ throw new Error('FOREST_ENV_SECRET is not set. Provide it via options.envSecret or FOREST_ENV_SECRET environment variable.');
115
+ }
116
+ if (!this.authSecret) {
117
+ throw new Error('FOREST_AUTH_SECRET is not set. Provide it via options.authSecret or FOREST_AUTH_SECRET environment variable.');
118
+ }
119
+ return { envSecret: this.envSecret, authSecret: this.authSecret };
120
+ }
121
+ /**
122
+ * Filters tool arguments to only include non-sensitive fields for logging.
123
+ * Prevents accidentally logging sensitive data like search queries or filters.
124
+ */
125
+ filterArgsForLogging(toolName, args) {
126
+ const allowedFields = SAFE_ARGUMENTS_FOR_LOGGING[toolName] || [];
127
+ return Object.fromEntries(Object.entries(args).filter(([key]) => allowedFields.includes(key)));
128
+ }
129
+ /**
130
+ * Logs tool call information if the request is a tools/call method.
131
+ */
132
+ logToolCallIfPresent(req) {
133
+ const body = req.body;
134
+ if (body?.method !== 'tools/call' || !body.params?.name) {
135
+ return;
136
+ }
137
+ const toolName = body.params.name;
138
+ const args = body.params.arguments || {};
139
+ const safeArgs = this.filterArgsForLogging(toolName, args);
140
+ this.logger('Info', `[MCP] Tool call: ${toolName} - params: ${JSON.stringify(safeArgs)}`);
141
+ }
142
+ /**
143
+ * Handles an incoming MCP request.
144
+ * Logs the request, intercepts the response for error logging, and delegates to the transport.
145
+ */
146
+ async handleMcpRequest(req, res) {
147
+ this.logger('Info', `[MCP] Incoming ${req.method} ${req.path}`);
148
+ if (!this.mcpTransport) {
149
+ throw new Error('MCP transport not initialized');
150
+ }
151
+ this.logToolCallIfPresent(req);
152
+ (0, sse_error_logger_1.default)(res, this.logger);
153
+ await this.mcpTransport.handleRequest(req, res, req.body);
154
+ }
155
+ /**
156
+ * Build and return the Express app without starting a standalone server.
157
+ * Useful for embedding the MCP server into another application.
158
+ *
159
+ * @param baseUrl - Optional base URL override. If not provided, will use the
160
+ * environmentApiEndpoint from Forest Admin API.
161
+ * @returns The configured Express application
162
+ */
163
+ async buildExpressApp(baseUrl) {
164
+ const { envSecret, authSecret } = this.ensureSecretsAreSet();
165
+ // Fetch schema and setup tools before building the app
166
+ await this.setupTools();
167
+ this.mcpTransport = new streamableHttp_js_1.StreamableHTTPServerTransport({
168
+ sessionIdGenerator: undefined,
169
+ });
170
+ await this.mcpServer.connect(this.mcpTransport);
171
+ const app = (0, express_1.default)();
172
+ // Trust proxy headers when behind a reverse proxy (e.g., load balancer, nginx)
173
+ // This is required for express-rate-limit to correctly identify clients
174
+ app.set('trust proxy', 1);
175
+ app.use((0, cors_1.default)({
176
+ origin: '*',
177
+ }));
178
+ // Initialize OAuth provider
179
+ const oauthProvider = new forest_oauth_provider_1.default({
180
+ forestServerUrl: this.forestServerUrl,
181
+ forestAppUrl: this.forestAppUrl,
182
+ envSecret,
183
+ authSecret,
184
+ logger: this.logger,
185
+ });
186
+ await oauthProvider.initialize();
187
+ // Use provided baseUrl or get it from the OAuth provider (environmentApiEndpoint)
188
+ const effectiveBaseUrl = baseUrl || oauthProvider.getBaseUrl();
189
+ if (!effectiveBaseUrl) {
190
+ throw new Error('Could not determine base URL for MCP server. ' +
191
+ 'Either provide a baseUrl parameter or ensure the Forest Admin environment has an api_endpoint configured.');
192
+ }
193
+ const scopesSupported = ['mcp:read', 'mcp:write', 'mcp:action', 'mcp:admin'];
194
+ // Create OAuth metadata with custom registration_endpoint pointing to Forest Admin
195
+ const oauthMetadata = (0, router_js_1.createOAuthMetadata)({
196
+ provider: oauthProvider,
197
+ issuerUrl: effectiveBaseUrl,
198
+ baseUrl: effectiveBaseUrl,
199
+ scopesSupported,
200
+ });
201
+ oauthMetadata.token_endpoint_auth_methods_supported = ['none'];
202
+ oauthMetadata.response_types_supported = ['code'];
203
+ oauthMetadata.code_challenge_methods_supported = ['S256'];
204
+ oauthMetadata.token_endpoint = `${effectiveBaseUrl.href}oauth/token`;
205
+ oauthMetadata.authorization_endpoint = `${effectiveBaseUrl.href}oauth/authorize`;
206
+ // Override registration_endpoint to point to Forest Admin server
207
+ oauthMetadata.registration_endpoint = `${this.forestServerUrl}/oauth/register`;
208
+ // Remove revocation_endpoint from metadata (not supported)
209
+ delete oauthMetadata.revocation_endpoint;
210
+ // Body parsers MUST come before OAuth handlers because the token handler
211
+ // expects req.body to be parsed. When proxied from Koa, the body is already
212
+ // available but Express needs to see it properly.
213
+ app.use(express_1.default.json());
214
+ app.use(express_1.default.urlencoded({ extended: true }));
215
+ // Request logging middleware - logs every request with response status
216
+ app.use((req, res, next) => {
217
+ const startTime = Date.now();
218
+ // Capture the original end method to log after response is sent
219
+ const originalEnd = res.end.bind(res);
220
+ res.end = ((chunk, encoding) => {
221
+ const duration = Date.now() - startTime;
222
+ this.logger('Info', `[${res.statusCode}] ${req.method} ${req.baseUrl || req.path} - ${duration}ms`);
223
+ return originalEnd(chunk, encoding);
224
+ });
225
+ next();
226
+ });
227
+ app.use('/oauth/authorize', (0, authorize_js_1.authorizationHandler)({
228
+ provider: oauthProvider,
229
+ }));
230
+ app.use('/oauth/token', (0, token_js_1.tokenHandler)({ provider: oauthProvider }));
231
+ // Mount metadata router with custom metadata
232
+ // The resourceServerUrl should include the /mcp path to match RFC 9728 requirements.
233
+ // This creates the .well-known/oauth-protected-resource/mcp endpoint.
234
+ const mcpResourceUrl = new URL('mcp', effectiveBaseUrl);
235
+ app.use((0, router_js_1.mcpAuthMetadataRouter)({
236
+ oauthMetadata,
237
+ resourceServerUrl: mcpResourceUrl,
238
+ scopesSupported,
239
+ }));
240
+ app.use((0, allowedMethods_js_1.allowedMethods)(['POST']));
241
+ app.post('/mcp', (0, bearerAuth_js_1.requireBearerAuth)({
242
+ verifier: oauthProvider,
243
+ requiredScopes: ['mcp:read'],
244
+ }), (req, res) => {
245
+ this.handleMcpRequest(req, res).catch(error => {
246
+ this.logger('Error', `MCP Error: ${error}`);
247
+ if (!res.headersSent) {
248
+ res.status(500).json({
249
+ jsonrpc: '2.0',
250
+ error: {
251
+ code: -32603,
252
+ message: error?.message || 'Internal server error',
253
+ },
254
+ id: null,
255
+ });
256
+ }
257
+ });
258
+ });
259
+ // Global error handler to catch any unhandled errors
260
+ // Express requires all 4 parameters to recognize this as an error handler
261
+ // Capture logger for use in error handler (arrow function would lose context)
262
+ const { logger } = this;
263
+ app.use(
264
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
265
+ (err, _req, res, _next) => {
266
+ logger('Error', `Unhandled error: ${err.message}`);
267
+ if (!res.headersSent) {
268
+ res.status(500).json({
269
+ error: 'internal_server_error',
270
+ error_description: err.message,
271
+ });
272
+ }
273
+ });
274
+ this.expressApp = app;
275
+ return app;
276
+ }
277
+ /**
278
+ * Build and return an HTTP callback that can be used as middleware.
279
+ * The callback will handle MCP-related routes (/.well-known/*, /oauth/*, /mcp)
280
+ * and call next() for other routes.
281
+ *
282
+ * @param baseUrl - Optional base URL override. If not provided, will use the
283
+ * environmentApiEndpoint from Forest Admin API.
284
+ * @returns An HTTP callback function
285
+ */
286
+ async getHttpCallback(baseUrl) {
287
+ const app = await this.buildExpressApp(baseUrl);
288
+ return (req, res, next) => {
289
+ const url = req.url || '/';
290
+ if ((0, mcp_paths_1.isMcpRoute)(url)) {
291
+ // Fix for streams that have been consumed by another framework (like Koa)
292
+ // Express's finalhandler calls unpipe() which expects _readableState.pipes to exist
293
+ // Node.js unpipe() accesses _readableState.pipes.length, so pipes must be an array
294
+ /* eslint-disable @typescript-eslint/no-explicit-any, no-underscore-dangle */
295
+ const reqAny = req;
296
+ // Ensure _readableState exists with proper structure
297
+ if (!reqAny._readableState) {
298
+ reqAny._readableState = {
299
+ pipes: [],
300
+ pipesCount: 0,
301
+ flowing: null,
302
+ ended: true,
303
+ endEmitted: true,
304
+ reading: false,
305
+ };
306
+ }
307
+ else if (!Array.isArray(reqAny._readableState.pipes)) {
308
+ // pipes must be an array for Node.js unpipe() to work
309
+ reqAny._readableState.pipes = [];
310
+ }
311
+ /* eslint-enable @typescript-eslint/no-explicit-any, no-underscore-dangle */
312
+ // Handle MCP route with Express app
313
+ app(req, res);
314
+ }
315
+ else if (next) {
316
+ // Not an MCP route, call next middleware
317
+ next();
318
+ }
319
+ else {
320
+ // No next callback and not an MCP route - this shouldn't happen in normal usage
321
+ res.writeHead(404, { 'Content-Type': 'application/json' });
322
+ res.end(JSON.stringify({ error: 'Not found' }));
323
+ }
324
+ };
325
+ }
326
+ /**
327
+ * Run the MCP server as a standalone HTTP server.
328
+ */
329
+ async run() {
330
+ const port = Number(process.env.MCP_SERVER_PORT) || 3931;
331
+ const baseUrl = new URL(`http://localhost:${port}`);
332
+ const app = await this.buildExpressApp(baseUrl);
333
+ // Create HTTP server from Express app
334
+ this.httpServer = http.createServer(app);
335
+ this.httpServer.listen(port, () => {
336
+ this.logger('Info', `Forest Admin MCP Server running on http://localhost:${port}`);
337
+ });
338
+ }
339
+ }
340
+ exports.default = ForestMCPServer;
341
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3NlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLHNEQUFzRDtBQUN0RCxzRUFBc0U7QUFDdEUsdUJBQXFCO0FBSXJCLDhGQUFtRztBQUNuRyxzRkFBdUY7QUFDdkYsMEdBQW9HO0FBQ3BHLGtHQUFtRztBQUNuRywrRUFHeUQ7QUFDekQsb0VBQW9FO0FBQ3BFLDBGQUFtRztBQUNuRyxnREFBd0I7QUFDeEIsc0RBQThCO0FBQzlCLDJDQUE2QjtBQUU3QixvRkFBMEQ7QUFDMUQsMkNBQXlDO0FBQ3pDLHdEQUEyQztBQUMzQywyREFBK0U7QUFDL0UsZ0ZBQXdFO0FBQ3hFLHVDQUEwQztBQVcxQyxTQUFTLGVBQWUsQ0FBQyxLQUFlO0lBQ3RDLElBQUksS0FBSyxLQUFLLE9BQU87UUFBRSxPQUFPLENBQUMsR0FBVyxFQUFFLEVBQUUsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLGdCQUFnQixHQUFHLEVBQUUsQ0FBQyxDQUFDO0lBQ3BGLElBQUksS0FBSyxLQUFLLE1BQU07UUFBRSxPQUFPLENBQUMsR0FBVyxFQUFFLEVBQUUsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLGdCQUFnQixHQUFHLEVBQUUsQ0FBQyxDQUFDO0lBRWxGLE9BQU8sQ0FBQyxHQUFXLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsRUFBRSxDQUFDLENBQUM7QUFDOUQsQ0FBQztBQUVELE1BQU0sYUFBYSxHQUFXLENBQUMsS0FBSyxFQUFFLE9BQU8sRUFBRSxFQUFFO0lBQy9DLGVBQWUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQztBQUNsQyxDQUFDLENBQUM7QUFFRixxRUFBcUU7QUFDckUsTUFBTSwwQkFBMEIsR0FBNkI7SUFDM0QsSUFBSSxFQUFFLENBQUMsZ0JBQWdCLENB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
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=server.test.d.ts.map