@forestadmin/mcp-server 0.1.0 → 1.0.1

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.
@@ -1,901 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
7
- const supertest_1 = __importDefault(require("supertest"));
8
- const server_1 = __importDefault(require("./server"));
9
- const mock_server_1 = __importDefault(require("./test-utils/mock-server"));
10
- function shutDownHttpServer(server) {
11
- if (!server)
12
- return Promise.resolve();
13
- return new Promise(resolve => {
14
- server.close(() => {
15
- resolve();
16
- });
17
- });
18
- }
19
- /**
20
- * Integration tests for ForestMCPServer instance
21
- * Tests the actual server class and its behavior
22
- */
23
- describe('ForestMCPServer Instance', () => {
24
- let server;
25
- let originalEnv;
26
- let modifiedEnv;
27
- let mockServer;
28
- const originalFetch = global.fetch;
29
- beforeAll(() => {
30
- originalEnv = { ...process.env };
31
- process.env.FOREST_ENV_SECRET = 'test-env-secret';
32
- process.env.FOREST_AUTH_SECRET = 'test-auth-secret';
33
- process.env.FOREST_SERVER_URL = 'https://test.forestadmin.com';
34
- process.env.AGENT_HOSTNAME = 'http://localhost:3310';
35
- // Setup mock for Forest Admin server
36
- mockServer = new mock_server_1.default();
37
- mockServer
38
- .get('/liana/environment', {
39
- data: { id: '12345', attributes: { api_endpoint: 'https://api.example.com' } },
40
- })
41
- .get('/liana/forest-schema', {
42
- data: [
43
- {
44
- id: 'users',
45
- type: 'collections',
46
- attributes: { name: 'users', fields: [{ field: 'id', type: 'Number' }] },
47
- },
48
- {
49
- id: 'products',
50
- type: 'collections',
51
- attributes: { name: 'products', fields: [{ field: 'name', type: 'String' }] },
52
- },
53
- ],
54
- meta: { liana: 'forest-express-sequelize', liana_version: '9.0.0', liana_features: null },
55
- })
56
- .get(/\/oauth\/register\/registered-client/, {
57
- client_id: 'registered-client',
58
- redirect_uris: ['https://example.com/callback'],
59
- client_name: 'Test Client',
60
- })
61
- .get(/\/oauth\/register\//, { error: 'Client not found' }, 404);
62
- global.fetch = mockServer.fetch;
63
- });
64
- afterAll(async () => {
65
- process.env = originalEnv;
66
- global.fetch = originalFetch;
67
- });
68
- beforeEach(() => {
69
- modifiedEnv = { ...process.env };
70
- mockServer.clear();
71
- });
72
- afterEach(async () => {
73
- process.env = modifiedEnv;
74
- });
75
- describe('constructor', () => {
76
- it('should create server instance', () => {
77
- server = new server_1.default();
78
- expect(server).toBeDefined();
79
- expect(server).toBeInstanceOf(server_1.default);
80
- });
81
- it('should initialize with FOREST_SERVER_URL', () => {
82
- process.env.FOREST_SERVER_URL = 'https://custom.forestadmin.com';
83
- server = new server_1.default();
84
- expect(server.forestServerUrl).toBe('https://custom.forestadmin.com');
85
- });
86
- it('should fallback to FOREST_URL', () => {
87
- delete process.env.FOREST_SERVER_URL;
88
- process.env.FOREST_URL = 'https://fallback.forestadmin.com';
89
- server = new server_1.default();
90
- expect(server.forestServerUrl).toBe('https://fallback.forestadmin.com');
91
- });
92
- it('should use default URL when neither is provided', () => {
93
- delete process.env.FOREST_SERVER_URL;
94
- delete process.env.FOREST_URL;
95
- server = new server_1.default();
96
- expect(server.forestServerUrl).toBe('https://api.forestadmin.com');
97
- });
98
- it('should create MCP server instance', () => {
99
- server = new server_1.default();
100
- expect(server.mcpServer).toBeDefined();
101
- });
102
- });
103
- describe('environment validation', () => {
104
- it('should throw error when FOREST_ENV_SECRET is missing', async () => {
105
- delete process.env.FOREST_ENV_SECRET;
106
- server = new server_1.default();
107
- await expect(server.run()).rejects.toThrow('FOREST_ENV_SECRET is not set. Provide it via options.envSecret or FOREST_ENV_SECRET environment variable.');
108
- });
109
- it('should throw error when FOREST_AUTH_SECRET is missing', async () => {
110
- delete process.env.FOREST_AUTH_SECRET;
111
- server = new server_1.default();
112
- await expect(server.run()).rejects.toThrow('FOREST_AUTH_SECRET is not set. Provide it via options.authSecret or FOREST_AUTH_SECRET environment variable.');
113
- });
114
- });
115
- describe('run method', () => {
116
- afterEach(async () => {
117
- await shutDownHttpServer(server?.httpServer);
118
- });
119
- it('should start server on specified port', async () => {
120
- const testPort = 39310; // Use a different port for testing
121
- process.env.MCP_SERVER_PORT = testPort.toString();
122
- server = new server_1.default();
123
- // Start the server without awaiting (it runs indefinitely)
124
- server.run();
125
- // Wait a bit for the server to start
126
- await new Promise(resolve => {
127
- setTimeout(resolve, 500);
128
- });
129
- // Verify the server is running by making a request
130
- const { httpServer } = server;
131
- expect(httpServer).toBeDefined();
132
- // Make a request to verify server is responding
133
- const response = await (0, supertest_1.default)(httpServer)
134
- .post('/mcp')
135
- .send({ jsonrpc: '2.0', method: 'tools/list', id: 1 });
136
- expect(response.status).toBeDefined();
137
- });
138
- it('should create transport instance', async () => {
139
- const testPort = 39311;
140
- process.env.MCP_SERVER_PORT = testPort.toString();
141
- server = new server_1.default();
142
- server.run();
143
- await new Promise(resolve => {
144
- setTimeout(resolve, 500);
145
- });
146
- expect(server.mcpTransport).toBeDefined();
147
- });
148
- });
149
- describe('HTTP endpoint', () => {
150
- let httpServer;
151
- beforeAll(async () => {
152
- const testPort = 39312;
153
- process.env.MCP_SERVER_PORT = testPort.toString();
154
- server = new server_1.default();
155
- server.run();
156
- await new Promise(resolve => {
157
- setTimeout(resolve, 500);
158
- });
159
- httpServer = server.httpServer;
160
- });
161
- afterAll(async () => {
162
- await shutDownHttpServer(server?.httpServer);
163
- });
164
- it('should handle POST requests to /mcp', async () => {
165
- const response = await (0, supertest_1.default)(httpServer)
166
- .post('/mcp')
167
- .send({ jsonrpc: '2.0', method: 'initialize', id: 1 });
168
- expect(response.status).not.toBe(404);
169
- });
170
- it('should reject GET requests', async () => {
171
- const response = await (0, supertest_1.default)(httpServer).get('/mcp');
172
- expect(response.status).toBe(405);
173
- });
174
- it('should handle CORS', async () => {
175
- const response = await (0, supertest_1.default)(httpServer)
176
- .post('/mcp')
177
- .set('Origin', 'https://example.com')
178
- .send({ jsonrpc: '2.0', method: 'initialize', id: 1 });
179
- expect(response.headers['access-control-allow-origin']).toBe('*');
180
- });
181
- it('should return JSON-RPC error on transport failure', async () => {
182
- // Send invalid request
183
- const response = await (0, supertest_1.default)(httpServer).post('/mcp').send('invalid json');
184
- // Should handle the error gracefully
185
- expect(response.status).toBeGreaterThanOrEqual(400);
186
- });
187
- describe('OAuth metadata endpoint', () => {
188
- it('should return OAuth metadata at /.well-known/oauth-authorization-server', async () => {
189
- const response = await (0, supertest_1.default)(server.httpServer).get('/.well-known/oauth-authorization-server');
190
- expect(response.status).toBe(200);
191
- expect(response.headers['content-type']).toMatch(/application\/json/);
192
- expect(response.body.issuer).toBe('http://localhost:39312/');
193
- expect(response.body.registration_endpoint).toBe('https://test.forestadmin.com/oauth/register');
194
- expect(response.body.authorization_endpoint).toBe(`http://localhost:39312/oauth/authorize`);
195
- expect(response.body.token_endpoint).toBe(`http://localhost:39312/oauth/token`);
196
- expect(response.body.revocation_endpoint).toBeUndefined();
197
- expect(response.body.scopes_supported).toEqual([
198
- 'mcp:read',
199
- 'mcp:write',
200
- 'mcp:action',
201
- 'mcp:admin',
202
- ]);
203
- expect(response.body.response_types_supported).toEqual(['code']);
204
- expect(response.body.grant_types_supported).toEqual([
205
- 'authorization_code',
206
- 'refresh_token',
207
- ]);
208
- expect(response.body.code_challenge_methods_supported).toEqual(['S256']);
209
- expect(response.body.token_endpoint_auth_methods_supported).toEqual(['none']);
210
- });
211
- it('should return registration_endpoint with custom FOREST_SERVER_URL', async () => {
212
- // Clean up previous server
213
- await shutDownHttpServer(server?.httpServer);
214
- process.env.FOREST_SERVER_URL = 'https://custom.forestadmin.com';
215
- process.env.MCP_SERVER_PORT = '39314';
216
- server = new server_1.default();
217
- server.run();
218
- await new Promise(resolve => {
219
- setTimeout(resolve, 500);
220
- });
221
- const response = await (0, supertest_1.default)(server.httpServer).get('/.well-known/oauth-authorization-server');
222
- expect(response.body.registration_endpoint).toBe('https://custom.forestadmin.com/oauth/register');
223
- });
224
- });
225
- describe('/oauth/authorize endpoint', () => {
226
- it('should return 400 when required parameters are missing', async () => {
227
- const response = await (0, supertest_1.default)(httpServer).get('/oauth/authorize');
228
- expect(response.status).toBe(400);
229
- });
230
- it('should return 400 when client_id is missing', async () => {
231
- const response = await (0, supertest_1.default)(httpServer).get('/oauth/authorize').query({
232
- redirect_uri: 'https://example.com/callback',
233
- response_type: 'code',
234
- code_challenge: 'test-challenge',
235
- code_challenge_method: 'S256',
236
- state: 'test-state',
237
- });
238
- expect(response.status).toBe(400);
239
- });
240
- it('should return 400 when redirect_uri is missing', async () => {
241
- const response = await (0, supertest_1.default)(httpServer).get('/oauth/authorize').query({
242
- client_id: 'test-client',
243
- response_type: 'code',
244
- code_challenge: 'test-challenge',
245
- code_challenge_method: 'S256',
246
- state: 'test-state',
247
- });
248
- expect(response.status).toBe(400);
249
- });
250
- it('should return 400 when code_challenge is missing', async () => {
251
- const response = await (0, supertest_1.default)(httpServer).get('/oauth/authorize').query({
252
- client_id: 'test-client',
253
- redirect_uri: 'https://example.com/callback',
254
- response_type: 'code',
255
- code_challenge_method: 'S256',
256
- state: 'test-state',
257
- });
258
- expect(response.status).toBe(400);
259
- });
260
- it('should return 400 when client is not registered', async () => {
261
- const response = await (0, supertest_1.default)(httpServer).get('/oauth/authorize').query({
262
- client_id: 'unregistered-client',
263
- redirect_uri: 'https://example.com/callback',
264
- response_type: 'code',
265
- code_challenge: 'test-challenge',
266
- code_challenge_method: 'S256',
267
- state: 'test-state',
268
- scope: 'mcp:read',
269
- });
270
- expect(response.status).toBe(400);
271
- });
272
- it('should redirect to Forest Admin frontend with correct parameters', async () => {
273
- const response = await (0, supertest_1.default)(httpServer).get('/oauth/authorize').query({
274
- client_id: 'registered-client',
275
- redirect_uri: 'https://example.com/callback',
276
- response_type: 'code',
277
- code_challenge: 'test-challenge',
278
- code_challenge_method: 'S256',
279
- state: 'test-state',
280
- scope: 'mcp:read profile',
281
- });
282
- expect(response.status).toBe(302);
283
- expect(response.headers.location).toContain('https://app.forestadmin.com/oauth/authorize');
284
- const redirectUrl = new URL(response.headers.location);
285
- expect(redirectUrl.searchParams.get('redirect_uri')).toBe('https://example.com/callback');
286
- expect(redirectUrl.searchParams.get('code_challenge')).toBe('test-challenge');
287
- expect(redirectUrl.searchParams.get('code_challenge_method')).toBe('S256');
288
- expect(redirectUrl.searchParams.get('response_type')).toBe('code');
289
- expect(redirectUrl.searchParams.get('client_id')).toBe('registered-client');
290
- expect(redirectUrl.searchParams.get('state')).toBe('test-state');
291
- expect(redirectUrl.searchParams.get('scope')).toBe('mcp:read+profile');
292
- expect(redirectUrl.searchParams.get('environmentId')).toBe('12345');
293
- });
294
- it('should redirect to default frontend when FOREST_FRONTEND_HOSTNAME is not set', async () => {
295
- const response = await (0, supertest_1.default)(httpServer).get('/oauth/authorize').query({
296
- client_id: 'registered-client',
297
- redirect_uri: 'https://example.com/callback',
298
- response_type: 'code',
299
- code_challenge: 'test-challenge',
300
- code_challenge_method: 'S256',
301
- state: 'test-state',
302
- scope: 'mcp:read',
303
- });
304
- expect(response.status).toBe(302);
305
- expect(response.headers.location).toContain('https://app.forestadmin.com/oauth/authorize');
306
- });
307
- it('should handle POST method for authorize', async () => {
308
- // POST /authorize uses form-encoded body
309
- const response = await (0, supertest_1.default)(httpServer).post('/oauth/authorize').type('form').send({
310
- client_id: 'registered-client',
311
- redirect_uri: 'https://example.com/callback',
312
- response_type: 'code',
313
- code_challenge: 'test-challenge',
314
- code_challenge_method: 'S256',
315
- state: 'test-state',
316
- scope: 'mcp:read',
317
- resource: 'https://example.com/resource',
318
- });
319
- expect(response.status).toBe(302);
320
- expect(response.headers.location).toStrictEqual(`https://app.forestadmin.com/oauth/authorize?redirect_uri=${encodeURIComponent('https://example.com/callback')}&code_challenge=test-challenge&code_challenge_method=S256&response_type=code&client_id=registered-client&state=test-state&scope=${encodeURIComponent('mcp:read')}&resource=${encodeURIComponent('https://example.com/resource')}&environmentId=12345`);
321
- });
322
- });
323
- });
324
- /**
325
- * Integration tests for /oauth/token endpoint
326
- * Uses a separate server instance with mock server for Forest Admin API
327
- */
328
- describe('/oauth/token endpoint', () => {
329
- let mcpServer;
330
- let mcpHttpServer;
331
- let mcpMockServer;
332
- beforeAll(async () => {
333
- process.env.FOREST_ENV_SECRET = 'test-env-secret';
334
- process.env.FOREST_AUTH_SECRET = 'test-auth-secret';
335
- process.env.FOREST_SERVER_URL = 'https://test.forestadmin.com';
336
- process.env.MCP_SERVER_PORT = '39320';
337
- // Setup mock for Forest Admin server API responses
338
- mcpMockServer = new mock_server_1.default();
339
- mcpMockServer
340
- .get('/liana/environment', {
341
- data: { id: '12345', attributes: { api_endpoint: 'https://api.example.com' } },
342
- })
343
- .get('/liana/forest-schema', {
344
- data: [
345
- {
346
- id: 'users',
347
- type: 'collections',
348
- attributes: { name: 'users', fields: [{ field: 'id', type: 'Number' }] },
349
- },
350
- {
351
- id: 'products',
352
- type: 'collections',
353
- attributes: { name: 'products', fields: [{ field: 'name', type: 'String' }] },
354
- },
355
- ],
356
- meta: { liana: 'forest-express-sequelize', liana_version: '9.0.0', liana_features: null },
357
- })
358
- .get(/\/oauth\/register\/registered-client/, {
359
- client_id: 'registered-client',
360
- redirect_uris: ['https://example.com/callback'],
361
- client_name: 'Test Client',
362
- scope: 'mcp:read mcp:write',
363
- })
364
- .get(/\/oauth\/register\//, { error: 'Client not found' }, 404)
365
- // Mock Forest Admin OAuth token endpoint - returns valid JWTs with meta.renderingId, exp, iat, scope
366
- // access_token JWT payload: { meta: { renderingId: 456 }, scope: 'mcp:read mcp:write', iat: 2524608000, exp: 2524611600 }
367
- // refresh_token JWT payload: { iat: 2524608000, exp: 2525212800 }
368
- .post('/oauth/token', {
369
- access_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXRhIjp7InJlbmRlcmluZ0lkIjo0NTZ9LCJzY29wZSI6Im1jcDpyZWFkIG1jcDp3cml0ZSIsImlhdCI6MjUyNDYwODAwMCwiZXhwIjoyNTI0NjExNjAwfQ.fake',
370
- refresh_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjI1MjQ2MDgwMDAsImV4cCI6MjUyNTIxMjgwMH0.fake',
371
- expires_in: 3600,
372
- token_type: 'Bearer',
373
- scope: 'mcp:read mcp:write',
374
- })
375
- // Mock Forest Admin user info endpoint (called by forestadmin-client via superagent)
376
- .get(/\/liana\/v2\/renderings\/\d+\/authorization/, {
377
- data: {
378
- id: '123',
379
- attributes: {
380
- email: 'user@example.com',
381
- first_name: 'Test',
382
- last_name: 'User',
383
- teams: ['Operations'],
384
- role: 'Admin',
385
- permission_level: 'admin',
386
- tags: [],
387
- },
388
- },
389
- });
390
- global.fetch = mcpMockServer.fetch;
391
- // Also mock superagent for forestadmin-client requests
392
- mcpMockServer.setupSuperagentMock();
393
- // Create and start server
394
- mcpServer = new server_1.default();
395
- mcpServer.run();
396
- await new Promise(resolve => {
397
- setTimeout(resolve, 500);
398
- });
399
- mcpHttpServer = mcpServer.httpServer;
400
- });
401
- afterAll(async () => {
402
- mcpMockServer.restoreSuperagent();
403
- await new Promise(resolve => {
404
- if (mcpServer?.httpServer) {
405
- mcpServer.httpServer.close(() => resolve());
406
- }
407
- else {
408
- resolve();
409
- }
410
- });
411
- });
412
- it('should return 400 when grant_type is missing', async () => {
413
- const response = await (0, supertest_1.default)(mcpHttpServer).post('/oauth/token').type('form').send({
414
- code: 'auth-code-123',
415
- redirect_uri: 'https://example.com/callback',
416
- client_id: 'registered-client',
417
- });
418
- expect(response.status).toBe(400);
419
- expect(response.body.error).toBe('invalid_request');
420
- });
421
- it('should return 400 when code is missing', async () => {
422
- const response = await (0, supertest_1.default)(mcpHttpServer).post('/oauth/token').type('form').send({
423
- grant_type: 'authorization_code',
424
- redirect_uri: 'https://example.com/callback',
425
- client_id: 'registered-client',
426
- });
427
- expect(response.status).toBe(400);
428
- expect(response.body.error).toBe('invalid_request');
429
- });
430
- it('should call Forest Admin server to exchange code', async () => {
431
- mcpMockServer.clear();
432
- const response = await (0, supertest_1.default)(mcpHttpServer).post('/oauth/token').type('form').send({
433
- grant_type: 'authorization_code',
434
- code: 'valid-auth-code',
435
- redirect_uri: 'https://example.com/callback',
436
- client_id: 'registered-client',
437
- code_verifier: 'test-code-verifier',
438
- });
439
- expect(mcpMockServer.fetch).toHaveBeenCalledWith('https://test.forestadmin.com/oauth/token', expect.objectContaining({
440
- method: 'POST',
441
- body: expect.stringContaining('"grant_type":"authorization_code"'),
442
- }));
443
- expect(response.status).toBe(200);
444
- expect(response.body.access_token).toBeDefined();
445
- expect(response.body.refresh_token).toBeDefined();
446
- expect(response.body.token_type).toBe('Bearer');
447
- // expires_in is calculated as exp - now from the JWT, so it's a large value for our test tokens
448
- expect(response.body.expires_in).toBeGreaterThan(0);
449
- // The scope is returned from the decoded forest token
450
- expect(response.body.scope).toBe('mcp:read mcp:write');
451
- const accessToken = response.body.access_token;
452
- expect(() => jsonwebtoken_1.default.verify(accessToken, process.env.FOREST_AUTH_SECRET)).not.toThrow();
453
- // The forestadmin-client transforms the response from the API
454
- // (e.g., first_name → firstName, id string → number, teams[0] → team)
455
- const decoded = jsonwebtoken_1.default.decode(accessToken);
456
- expect(decoded).toMatchObject({
457
- id: 123,
458
- email: 'user@example.com',
459
- firstName: 'Test',
460
- lastName: 'User',
461
- team: 'Operations',
462
- role: 'Admin',
463
- permissionLevel: 'admin',
464
- renderingId: 456,
465
- tags: {},
466
- serverToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXRhIjp7InJlbmRlcmluZ0lkIjo0NTZ9LCJzY29wZSI6Im1jcDpyZWFkIG1jcDp3cml0ZSIsImlhdCI6MjUyNDYwODAwMCwiZXhwIjoyNTI0NjExNjAwfQ.fake',
467
- });
468
- // JWT should also have iat and exp claims
469
- expect(decoded.iat).toBeDefined();
470
- expect(decoded.exp).toBeDefined();
471
- // Verify refresh token structure
472
- const refreshToken = response.body.refresh_token;
473
- const decodedRefreshToken = jsonwebtoken_1.default.decode(refreshToken);
474
- expect(decodedRefreshToken).toMatchObject({
475
- type: 'refresh',
476
- clientId: 'registered-client',
477
- userId: 123,
478
- renderingId: 456,
479
- // The serverRefreshToken is the JWT returned from Forest Admin
480
- serverRefreshToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjI1MjQ2MDgwMDAsImV4cCI6MjUyNTIxMjgwMH0.fake',
481
- });
482
- });
483
- it('should exchange refresh token for new tokens', async () => {
484
- mcpMockServer.clear();
485
- // First, get initial tokens
486
- const initialResponse = await (0, supertest_1.default)(mcpHttpServer).post('/oauth/token').type('form').send({
487
- grant_type: 'authorization_code',
488
- code: 'valid-auth-code',
489
- redirect_uri: 'https://example.com/callback',
490
- client_id: 'registered-client',
491
- code_verifier: 'test-code-verifier',
492
- });
493
- expect(initialResponse.status).toBe(200);
494
- const refreshToken = initialResponse.body.refresh_token;
495
- // Clear mock to track new calls
496
- mcpMockServer.clear();
497
- // Now exchange refresh token for new tokens
498
- const refreshResponse = await (0, supertest_1.default)(mcpHttpServer).post('/oauth/token').type('form').send({
499
- grant_type: 'refresh_token',
500
- refresh_token: refreshToken,
501
- client_id: 'registered-client',
502
- });
503
- expect(refreshResponse.status).toBe(200);
504
- expect(refreshResponse.body.access_token).toBeDefined();
505
- expect(refreshResponse.body.refresh_token).toBeDefined();
506
- expect(refreshResponse.body.token_type).toBe('Bearer');
507
- // expires_in is calculated as exp - now from the JWT (duration in seconds)
508
- expect(refreshResponse.body.expires_in).toBeGreaterThan(0);
509
- // Verify the new access token is valid
510
- const newAccessToken = refreshResponse.body.access_token;
511
- expect(() => jsonwebtoken_1.default.verify(newAccessToken, process.env.FOREST_AUTH_SECRET)).not.toThrow();
512
- // Verify token rotation: new refresh token is returned
513
- const newRefreshToken = refreshResponse.body.refresh_token;
514
- expect(newRefreshToken).toBeDefined();
515
- // Verify it's a valid JWT with refresh token structure
516
- const decodedNewRefresh = jsonwebtoken_1.default.decode(newRefreshToken);
517
- expect(decodedNewRefresh.type).toBe('refresh');
518
- expect(decodedNewRefresh.clientId).toBe('registered-client');
519
- // Verify Forest Admin token endpoint was called with refresh_token grant
520
- expect(mcpMockServer.fetch).toHaveBeenCalledWith('https://test.forestadmin.com/oauth/token', expect.objectContaining({
521
- method: 'POST',
522
- body: expect.stringContaining('"grant_type":"refresh_token"'),
523
- }));
524
- // Note: Token rotation is implemented - the new refresh token should be different
525
- // However, since both requests use the same mock returning the same forest-server-refresh-token,
526
- // the generated JWT will have similar claims but different iat/exp timestamps
527
- const oldDecoded = jsonwebtoken_1.default.decode(refreshToken);
528
- const newDecoded = jsonwebtoken_1.default.decode(newRefreshToken);
529
- expect(newDecoded.iat).toBeGreaterThanOrEqual(oldDecoded.iat);
530
- });
531
- it('should return 400 for invalid refresh token', async () => {
532
- const response = await (0, supertest_1.default)(mcpHttpServer).post('/oauth/token').type('form').send({
533
- grant_type: 'refresh_token',
534
- refresh_token: 'invalid-token',
535
- client_id: 'registered-client',
536
- });
537
- expect(response.status).toBe(400);
538
- expect(response.body.error).toBeDefined();
539
- });
540
- it('should return 400 when refresh_token is missing for refresh_token grant', async () => {
541
- const response = await (0, supertest_1.default)(mcpHttpServer).post('/oauth/token').type('form').send({
542
- grant_type: 'refresh_token',
543
- client_id: 'registered-client',
544
- });
545
- expect(response.status).toBe(400);
546
- });
547
- it('should return 400 when client_id does not match refresh token', async () => {
548
- // Create a refresh token for a different client
549
- const refreshToken = jsonwebtoken_1.default.sign({
550
- type: 'refresh',
551
- clientId: 'different-client',
552
- userId: 123,
553
- renderingId: 456,
554
- serverRefreshToken: 'forest-refresh-token',
555
- }, process.env.FOREST_AUTH_SECRET, { expiresIn: '7d' });
556
- const response = await (0, supertest_1.default)(mcpHttpServer).post('/oauth/token').type('form').send({
557
- grant_type: 'refresh_token',
558
- refresh_token: refreshToken,
559
- client_id: 'registered-client',
560
- });
561
- expect(response.status).toBe(400);
562
- expect(response.body.error).toBeDefined();
563
- });
564
- describe('error handling', () => {
565
- const setupErrorMock = (errorResponse, statusCode) => {
566
- mcpMockServer.reset();
567
- mcpMockServer
568
- .get('/liana/environment', {
569
- data: { id: '12345', attributes: { api_endpoint: 'https://api.example.com' } },
570
- })
571
- .get('/liana/forest-schema', {
572
- data: [
573
- {
574
- id: 'users',
575
- type: 'collections',
576
- attributes: { name: 'users', fields: [{ field: 'id', type: 'Number' }] },
577
- },
578
- ],
579
- meta: {
580
- liana: 'forest-express-sequelize',
581
- liana_version: '9.0.0',
582
- liana_features: null,
583
- },
584
- })
585
- .get(/\/oauth\/register\/registered-client/, {
586
- client_id: 'registered-client',
587
- redirect_uris: ['https://example.com/callback'],
588
- client_name: 'Test Client',
589
- scope: 'mcp:read mcp:write',
590
- })
591
- .get(/\/oauth\/register\//, { error: 'Client not found' }, 404)
592
- .post('/oauth/token', errorResponse, statusCode);
593
- };
594
- // Note: The implementation wraps all OAuth errors in InvalidRequestError,
595
- // so the error code is always 'invalid_request' with the original error in the description
596
- it('should return error when authorization code is invalid', async () => {
597
- setupErrorMock({
598
- error: 'invalid_grant',
599
- error_description: 'The authorization code has expired or is invalid',
600
- }, 400);
601
- const response = await (0, supertest_1.default)(mcpHttpServer).post('/oauth/token').type('form').send({
602
- grant_type: 'authorization_code',
603
- code: 'expired-or-invalid-code',
604
- redirect_uri: 'https://example.com/callback',
605
- client_id: 'registered-client',
606
- code_verifier: 'test-code-verifier',
607
- });
608
- expect(response.status).toBe(400);
609
- expect(response.body.error).toBe('invalid_request');
610
- expect(response.body.error_description).toContain('Failed to exchange authorization code');
611
- });
612
- it('should return error when client authentication fails', async () => {
613
- setupErrorMock({
614
- error: 'invalid_client',
615
- error_description: 'Client authentication failed',
616
- }, 401);
617
- const response = await (0, supertest_1.default)(mcpHttpServer).post('/oauth/token').type('form').send({
618
- grant_type: 'authorization_code',
619
- code: 'some-code',
620
- redirect_uri: 'https://example.com/callback',
621
- client_id: 'registered-client',
622
- code_verifier: 'test-code-verifier',
623
- });
624
- expect(response.status).toBe(400);
625
- expect(response.body.error).toBe('invalid_request');
626
- expect(response.body.error_description).toContain('Failed to exchange authorization code');
627
- });
628
- it('should return error when requested scope is invalid', async () => {
629
- setupErrorMock({
630
- error: 'invalid_scope',
631
- error_description: 'The requested scope is invalid or unknown',
632
- }, 400);
633
- const response = await (0, supertest_1.default)(mcpHttpServer).post('/oauth/token').type('form').send({
634
- grant_type: 'authorization_code',
635
- code: 'some-code',
636
- redirect_uri: 'https://example.com/callback',
637
- client_id: 'registered-client',
638
- code_verifier: 'test-code-verifier',
639
- });
640
- expect(response.status).toBe(400);
641
- expect(response.body.error).toBe('invalid_request');
642
- expect(response.body.error_description).toContain('Failed to exchange authorization code');
643
- });
644
- it('should return error when client is not authorized', async () => {
645
- setupErrorMock({
646
- error: 'unauthorized_client',
647
- error_description: 'The client is not authorized to use this grant type',
648
- }, 403);
649
- const response = await (0, supertest_1.default)(mcpHttpServer).post('/oauth/token').type('form').send({
650
- grant_type: 'authorization_code',
651
- code: 'some-code',
652
- redirect_uri: 'https://example.com/callback',
653
- client_id: 'registered-client',
654
- code_verifier: 'test-code-verifier',
655
- });
656
- expect(response.status).toBe(400);
657
- expect(response.body.error).toBe('invalid_request');
658
- expect(response.body.error_description).toContain('Failed to exchange authorization code');
659
- });
660
- it('should return error when Forest Admin server has internal error', async () => {
661
- setupErrorMock({
662
- error: 'server_error',
663
- error_description: 'An unexpected error occurred on the server',
664
- }, 500);
665
- const response = await (0, supertest_1.default)(mcpHttpServer).post('/oauth/token').type('form').send({
666
- grant_type: 'authorization_code',
667
- code: 'some-code',
668
- redirect_uri: 'https://example.com/callback',
669
- client_id: 'registered-client',
670
- code_verifier: 'test-code-verifier',
671
- });
672
- expect(response.status).toBe(400);
673
- expect(response.body.error).toBe('invalid_request');
674
- expect(response.body.error_description).toContain('Failed to exchange authorization code');
675
- });
676
- it('should use default error description when not provided by Forest server', async () => {
677
- setupErrorMock({ error: 'invalid_request' }, 400);
678
- const response = await (0, supertest_1.default)(mcpHttpServer).post('/oauth/token').type('form').send({
679
- grant_type: 'authorization_code',
680
- code: 'some-code',
681
- redirect_uri: 'https://example.com/callback',
682
- client_id: 'registered-client',
683
- code_verifier: 'test-code-verifier',
684
- });
685
- expect(response.status).toBe(400);
686
- expect(response.body.error).toBe('invalid_request');
687
- expect(response.body.error_description).toContain('Failed to exchange authorization code');
688
- });
689
- it('should return error when Forest server returns error without error code', async () => {
690
- setupErrorMock({ message: 'Something went wrong' }, 500);
691
- const response = await (0, supertest_1.default)(mcpHttpServer).post('/oauth/token').type('form').send({
692
- grant_type: 'authorization_code',
693
- code: 'some-code',
694
- redirect_uri: 'https://example.com/callback',
695
- client_id: 'registered-client',
696
- code_verifier: 'test-code-verifier',
697
- });
698
- expect(response.status).toBe(400);
699
- expect(response.body.error).toBe('invalid_request');
700
- expect(response.body.error_description).toContain('Failed to exchange authorization code');
701
- });
702
- });
703
- });
704
- /**
705
- * Integration tests for the list tool
706
- * Tests that the list tool is properly registered and accessible
707
- */
708
- describe('List tool integration', () => {
709
- let listServer;
710
- let listHttpServer;
711
- let listMockServer;
712
- beforeAll(async () => {
713
- process.env.FOREST_ENV_SECRET = 'test-env-secret';
714
- process.env.FOREST_AUTH_SECRET = 'test-auth-secret';
715
- process.env.FOREST_SERVER_URL = 'https://test.forestadmin.com';
716
- process.env.AGENT_HOSTNAME = 'http://localhost:3310';
717
- process.env.MCP_SERVER_PORT = '39330';
718
- listMockServer = new mock_server_1.default();
719
- listMockServer
720
- .get('/liana/environment', {
721
- data: { id: '12345', attributes: { api_endpoint: 'https://api.example.com' } },
722
- })
723
- .get('/liana/forest-schema', {
724
- data: [
725
- {
726
- id: 'users',
727
- type: 'collections',
728
- attributes: { name: 'users', fields: [{ field: 'id', type: 'Number' }] },
729
- },
730
- {
731
- id: 'products',
732
- type: 'collections',
733
- attributes: { name: 'products', fields: [{ field: 'name', type: 'String' }] },
734
- },
735
- ],
736
- meta: { liana: 'forest-express-sequelize', liana_version: '9.0.0', liana_features: null },
737
- })
738
- .get(/\/oauth\/register\/registered-client/, {
739
- client_id: 'registered-client',
740
- redirect_uris: ['https://example.com/callback'],
741
- client_name: 'Test Client',
742
- scope: 'mcp:read mcp:write',
743
- })
744
- .get(/\/oauth\/register\//, { error: 'Client not found' }, 404);
745
- global.fetch = listMockServer.fetch;
746
- listServer = new server_1.default();
747
- listServer.run();
748
- await new Promise(resolve => {
749
- setTimeout(resolve, 500);
750
- });
751
- listHttpServer = listServer.httpServer;
752
- });
753
- afterAll(async () => {
754
- await new Promise(resolve => {
755
- if (listServer?.httpServer) {
756
- listServer.httpServer.close(() => resolve());
757
- }
758
- else {
759
- resolve();
760
- }
761
- });
762
- });
763
- it('should have list tool registered in the MCP server', () => {
764
- expect(listServer.mcpServer).toBeDefined();
765
- // The tool should be registered during server initialization
766
- // We verify this by checking the server started successfully
767
- expect(listHttpServer).toBeDefined();
768
- });
769
- it('should require authentication to access /mcp endpoint', async () => {
770
- const response = await (0, supertest_1.default)(listHttpServer).post('/mcp').send({
771
- jsonrpc: '2.0',
772
- method: 'tools/list',
773
- id: 1,
774
- });
775
- // Without a valid bearer token, we should get an authentication error
776
- expect(response.status).toBe(401);
777
- });
778
- it('should reject requests with invalid bearer token', async () => {
779
- const response = await (0, supertest_1.default)(listHttpServer)
780
- .post('/mcp')
781
- .set('Authorization', 'Bearer invalid-token')
782
- .send({
783
- jsonrpc: '2.0',
784
- method: 'tools/list',
785
- id: 1,
786
- });
787
- expect(response.status).toBe(401);
788
- });
789
- it('should accept requests with valid bearer token and list available tools', async () => {
790
- // Create a valid JWT token
791
- const authSecret = process.env.FOREST_AUTH_SECRET || 'test-auth-secret';
792
- const validToken = jsonwebtoken_1.default.sign({
793
- id: 123,
794
- email: 'user@example.com',
795
- renderingId: 456,
796
- }, authSecret, { expiresIn: '1h' });
797
- const response = await (0, supertest_1.default)(listHttpServer)
798
- .post('/mcp')
799
- .set('Authorization', `Bearer ${validToken}`)
800
- .set('Content-Type', 'application/json')
801
- .set('Accept', 'application/json, text/event-stream')
802
- .send({
803
- jsonrpc: '2.0',
804
- method: 'tools/list',
805
- id: 1,
806
- });
807
- expect(response.status).toBe(200);
808
- // The MCP SDK returns the response as text that needs to be parsed
809
- // The response may be in JSON-RPC format or as a newline-delimited JSON stream
810
- let responseData;
811
- if (response.body && Object.keys(response.body).length > 0) {
812
- responseData = response.body;
813
- }
814
- else {
815
- // Parse the text response - MCP returns Server-Sent Events format with "data: " prefix
816
- const textResponse = response.text;
817
- const lines = textResponse.split('\n').filter((line) => line.trim());
818
- // Find the line with the actual JSON-RPC response (starts with "data: ")
819
- const dataLine = lines.find((line) => line.startsWith('data: '));
820
- if (dataLine) {
821
- responseData = JSON.parse(dataLine.replace('data: ', ''));
822
- }
823
- else {
824
- responseData = JSON.parse(lines[lines.length - 1]);
825
- }
826
- }
827
- expect(responseData.jsonrpc).toBe('2.0');
828
- expect(responseData.id).toBe(1);
829
- expect(responseData.result).toBeDefined();
830
- expect(responseData.result.tools).toBeDefined();
831
- expect(Array.isArray(responseData.result.tools)).toBe(true);
832
- // Verify the 'list' tool is registered
833
- const listTool = responseData.result.tools.find((tool) => tool.name === 'list');
834
- expect(listTool).toBeDefined();
835
- expect(listTool.description).toBe('Retrieve a list of records from the specified collection.');
836
- expect(listTool.inputSchema).toBeDefined();
837
- expect(listTool.inputSchema.properties).toHaveProperty('collectionName');
838
- expect(listTool.inputSchema.properties).toHaveProperty('search');
839
- expect(listTool.inputSchema.properties).toHaveProperty('filters');
840
- expect(listTool.inputSchema.properties).toHaveProperty('sort');
841
- // Verify collectionName has enum with the collection names from the mocked schema
842
- const collectionNameSchema = listTool.inputSchema.properties.collectionName;
843
- expect(collectionNameSchema.type).toBe('string');
844
- expect(collectionNameSchema.enum).toBeDefined();
845
- expect(collectionNameSchema.enum).toEqual(['users', 'products']);
846
- });
847
- it('should create activity log with forestServerToken when calling list tool', async () => {
848
- // This test verifies that the activity log API is called with the forestServerToken
849
- // (the original Forest server token) and NOT the MCP JWT token.
850
- // The forestServerToken is embedded in the MCP JWT during token exchange and extracted
851
- // by verifyAccessToken into authInfo.extra.forestServerToken
852
- const authSecret = process.env.FOREST_AUTH_SECRET || 'test-auth-secret';
853
- const forestServerToken = 'original-forest-server-token-for-activity-log';
854
- // Create MCP JWT with embedded serverToken (as done during OAuth token exchange)
855
- const mcpToken = jsonwebtoken_1.default.sign({
856
- id: 123,
857
- email: 'user@example.com',
858
- renderingId: 456,
859
- serverToken: forestServerToken,
860
- }, authSecret, { expiresIn: '1h' });
861
- // Setup mock to capture the activity log API call and mock agent response
862
- listMockServer.clear();
863
- listMockServer
864
- .post('/api/activity-logs-requests', { success: true })
865
- .post('/forest/rpc', { result: [{ id: 1, name: 'Test' }] });
866
- const response = await (0, supertest_1.default)(listHttpServer)
867
- .post('/mcp')
868
- .set('Authorization', `Bearer ${mcpToken}`)
869
- .set('Content-Type', 'application/json')
870
- .set('Accept', 'application/json, text/event-stream')
871
- .send({
872
- jsonrpc: '2.0',
873
- method: 'tools/call',
874
- params: {
875
- name: 'list',
876
- arguments: { collectionName: 'users' },
877
- },
878
- id: 2,
879
- });
880
- // The tool call should succeed (or fail on agent call, but activity log should be created first)
881
- expect(response.status).toBe(200);
882
- // Verify activity log API was called with the correct forestServerToken
883
- // The mock fetch captures all calls as [url, options] tuples
884
- const activityLogCall = listMockServer.fetch.mock.calls.find((call) => call[0] === 'https://test.forestadmin.com/api/activity-logs-requests');
885
- expect(activityLogCall).toBeDefined();
886
- expect(activityLogCall[1].headers).toMatchObject({
887
- Authorization: `Bearer ${forestServerToken}`,
888
- 'Content-Type': 'application/json',
889
- 'Forest-Application-Source': 'MCP',
890
- });
891
- // Verify the body contains the correct data
892
- const body = JSON.parse(activityLogCall[1].body);
893
- expect(body.data.attributes.action).toBe('index');
894
- expect(body.data.relationships.collection.data).toEqual({
895
- id: 'users',
896
- type: 'collections',
897
- });
898
- });
899
- });
900
- });
901
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyLnRlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvc2VydmVyLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFFQSxnRUFBd0M7QUFDeEMsMERBQWdDO0FBRWhDLHNEQUF1QztBQUN2QywyRUFBa0Q7QUFFbEQsU0FBUyxrQkFBa0IsQ0FBQyxNQUErQjtJQUN6RCxJQUFJLENBQUMsTUFBTTtRQUFFLE9BQU8sT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBRXRDLE9BQU8sSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUU7UUFDM0IsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUU7WUFDaEIsT0FBTyxFQUFFLENBQUM7UUFDWixDQUFDLENBQUMsQ0FBQztJQUNMLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOzs7R0FHRztBQUNILFFBQVEsQ0FBQywwQkFBMEIsRUFBRSxHQUFHLEVBQUU7SUFDeEMsSUFBSSxNQUF1QixDQUFDO0lBQzVCLElBQUksV0FBOEIsQ0FBQztJQUNuQyxJQUFJLFdBQThCLENBQUM7SUFDbkMsSUFBSSxVQUFzQixDQUFDO0lBQzNCLE1BQU0sYUFBYSxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUM7SUFFbkMsU0FBUyxDQUFDLEdBQUcsRUFBRTtRQUNiLFdBQVcsR0FBRyxFQUFFLEdBQUcsT0FBTyxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ2pDLE9BQU8sQ0FBQyxHQUFHLENBQUMsaUJBQWlCLEdBQUcsaUJBQWlCLENBQUM7UUFDbEQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxrQkFBa0IsR0FBRyxrQkFBa0IsQ0FBQztRQUNwRCxPQUFPLENBQUMsR0FBRyxDQUFDLGlCQUFpQixHQUFHLDhCQUE4QixDQUFDO1FBQy9ELE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxHQUFHLHVCQUF1QixDQUFDO1FBRXJELHFDQUFxQztRQUNyQyxVQUFVLEdBQUcsSUFBSSxxQkFBVSxFQUFFLENB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