@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
@@ -0,0 +1,62 @@
1
+ type MockRouteHandler<T> = T | ((url: string, options?: RequestInit) => T);
2
+ /**
3
+ * Mock server class for mocking fetch requests to specific routes
4
+ * Also intercepts superagent requests used by forestadmin-client
5
+ */
6
+ export default class MockServer {
7
+ private routes;
8
+ private mockFn;
9
+ private superagentMocked;
10
+ constructor();
11
+ /**
12
+ * Setup superagent mocking to intercept HTTP requests made by forestadmin-client
13
+ */
14
+ setupSuperagentMock(): void;
15
+ /**
16
+ * Restore original superagent methods
17
+ */
18
+ restoreSuperagent(): void;
19
+ private handleSuperagentRequest;
20
+ /**
21
+ * Register a route with a JSON payload or a function that returns a payload
22
+ */
23
+ route<T>(pattern: string | RegExp, response: MockRouteHandler<T>, options?: {
24
+ method?: string;
25
+ status?: number;
26
+ }): this;
27
+ /**
28
+ * Register a GET route
29
+ */
30
+ get<T>(pattern: string | RegExp, response: MockRouteHandler<T>, status?: number): this;
31
+ /**
32
+ * Register a POST route
33
+ */
34
+ post<T>(pattern: string | RegExp, response: MockRouteHandler<T>, status?: number): this;
35
+ /**
36
+ * Register a PUT route
37
+ */
38
+ put<T>(pattern: string | RegExp, response: MockRouteHandler<T>, status?: number): this;
39
+ /**
40
+ * Register a DELETE route
41
+ */
42
+ delete<T>(pattern: string | RegExp, response: MockRouteHandler<T>, status?: number): this;
43
+ /**
44
+ * Register a PATCH route
45
+ */
46
+ patch<T>(pattern: string | RegExp, response: MockRouteHandler<T>, status?: number): this;
47
+ /**
48
+ * Get the mock function to use as global.fetch
49
+ */
50
+ get fetch(): jest.Mock;
51
+ /**
52
+ * Clear mock call history
53
+ */
54
+ clear(): void;
55
+ /**
56
+ * Reset all routes
57
+ */
58
+ reset(): void;
59
+ private handleRequest;
60
+ }
61
+ export {};
62
+ //# sourceMappingURL=mock-server.d.ts.map
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ // Store original superagent methods for restoration
4
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
+ let originalSuperagent = null;
6
+ /**
7
+ * Mock server class for mocking fetch requests to specific routes
8
+ * Also intercepts superagent requests used by forestadmin-client
9
+ */
10
+ class MockServer {
11
+ constructor() {
12
+ this.routes = [];
13
+ this.superagentMocked = false;
14
+ this.mockFn = jest.fn((url, options) => {
15
+ return this.handleRequest(url, options);
16
+ });
17
+ }
18
+ /**
19
+ * Setup superagent mocking to intercept HTTP requests made by forestadmin-client
20
+ */
21
+ setupSuperagentMock() {
22
+ if (this.superagentMocked)
23
+ return;
24
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires, global-require
25
+ const superagent = require('superagent');
26
+ if (!originalSuperagent) {
27
+ originalSuperagent = { ...superagent };
28
+ }
29
+ // Create mock request builder using arrow function to preserve 'this'
30
+ const createMockRequest = (method, reqUrl) => {
31
+ let headers = {};
32
+ let body = null;
33
+ const mockRequest = {
34
+ set: (key, value) => {
35
+ if (typeof key === 'object') {
36
+ headers = { ...headers, ...key };
37
+ }
38
+ else if (value !== undefined) {
39
+ headers[key] = value;
40
+ }
41
+ return mockRequest;
42
+ },
43
+ timeout: () => mockRequest,
44
+ send: async (data) => {
45
+ body = data;
46
+ return this.handleSuperagentRequest(method, reqUrl, headers, body);
47
+ },
48
+ };
49
+ return mockRequest;
50
+ };
51
+ // Override superagent methods
52
+ superagent.get = (reqUrl) => createMockRequest('GET', reqUrl);
53
+ superagent.post = (reqUrl) => createMockRequest('POST', reqUrl);
54
+ superagent.put = (reqUrl) => createMockRequest('PUT', reqUrl);
55
+ superagent.delete = (reqUrl) => createMockRequest('DELETE', reqUrl);
56
+ this.superagentMocked = true;
57
+ }
58
+ /**
59
+ * Restore original superagent methods
60
+ */
61
+ restoreSuperagent() {
62
+ if (!this.superagentMocked || !originalSuperagent)
63
+ return;
64
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires, global-require
65
+ const superagent = require('superagent');
66
+ Object.assign(superagent, originalSuperagent);
67
+ this.superagentMocked = false;
68
+ }
69
+ async handleSuperagentRequest(method, reqUrl, headers, body) {
70
+ for (const route of this.routes) {
71
+ const patternMatch = typeof route.pattern === 'string'
72
+ ? reqUrl.includes(route.pattern)
73
+ : route.pattern.test(reqUrl);
74
+ const methodMatch = !route.method || route.method.toUpperCase() === method.toUpperCase();
75
+ if (patternMatch && methodMatch) {
76
+ const payload = typeof route.response === 'function'
77
+ ? route.response(reqUrl, {
78
+ method,
79
+ headers: headers,
80
+ body: body,
81
+ })
82
+ : route.response;
83
+ const status = route.status ?? 200;
84
+ return { body: payload, status };
85
+ }
86
+ }
87
+ // No match found - throw error like superagent would for 404
88
+ const error = new Error(`Not Found: ${method} ${reqUrl}`);
89
+ error.response = {
90
+ status: 404,
91
+ body: { errors: [{ detail: 'Not found' }] },
92
+ };
93
+ throw error;
94
+ }
95
+ /**
96
+ * Register a route with a JSON payload or a function that returns a payload
97
+ */
98
+ route(pattern, response, options = {}) {
99
+ this.routes.push({
100
+ pattern,
101
+ method: options.method,
102
+ response,
103
+ status: options.status ?? 200,
104
+ });
105
+ return this;
106
+ }
107
+ /**
108
+ * Register a GET route
109
+ */
110
+ get(pattern, response, status) {
111
+ return this.route(pattern, response, { method: 'GET', status });
112
+ }
113
+ /**
114
+ * Register a POST route
115
+ */
116
+ post(pattern, response, status) {
117
+ return this.route(pattern, response, { method: 'POST', status });
118
+ }
119
+ /**
120
+ * Register a PUT route
121
+ */
122
+ put(pattern, response, status) {
123
+ return this.route(pattern, response, { method: 'PUT', status });
124
+ }
125
+ /**
126
+ * Register a DELETE route
127
+ */
128
+ delete(pattern, response, status) {
129
+ return this.route(pattern, response, { method: 'DELETE', status });
130
+ }
131
+ /**
132
+ * Register a PATCH route
133
+ */
134
+ patch(pattern, response, status) {
135
+ return this.route(pattern, response, { method: 'PATCH', status });
136
+ }
137
+ /**
138
+ * Get the mock function to use as global.fetch
139
+ */
140
+ get fetch() {
141
+ return this.mockFn;
142
+ }
143
+ /**
144
+ * Clear mock call history
145
+ */
146
+ clear() {
147
+ this.mockFn.mockClear();
148
+ }
149
+ /**
150
+ * Reset all routes
151
+ */
152
+ reset() {
153
+ this.routes = [];
154
+ this.mockFn.mockClear();
155
+ }
156
+ handleRequest(url, options) {
157
+ const urlString = url.toString();
158
+ const method = options?.method || 'GET';
159
+ for (const route of this.routes) {
160
+ const patternMatch = typeof route.pattern === 'string'
161
+ ? urlString.includes(route.pattern)
162
+ : route.pattern.test(urlString);
163
+ const methodMatch = !route.method || route.method.toUpperCase() === method.toUpperCase();
164
+ if (patternMatch && methodMatch) {
165
+ const payload = typeof route.response === 'function'
166
+ ? route.response(url, options)
167
+ : route.response;
168
+ const status = route.status ?? 200;
169
+ return Promise.resolve({
170
+ ok: status >= 200 && status < 300,
171
+ status,
172
+ statusText: status === 200 ? 'OK' : 'Error',
173
+ json: () => Promise.resolve(payload),
174
+ });
175
+ }
176
+ }
177
+ // Default: return 404 for unknown endpoints
178
+ return Promise.resolve({
179
+ ok: false,
180
+ status: 404,
181
+ statusText: 'Not Found',
182
+ json: () => Promise.resolve({ error: 'Not found' }),
183
+ });
184
+ }
185
+ }
186
+ exports.default = MockServer;
187
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9jay1zZXJ2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdGVzdC11dGlscy9tb2NrLXNlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQVNBLG9EQUFvRDtBQUNwRCw4REFBOEQ7QUFDOUQsSUFBSSxrQkFBa0IsR0FBUSxJQUFJLENBQUM7QUFFbkM7OztHQUdHO0FBQ0gsTUFBcUIsVUFBVTtJQUs3QjtRQUpRLFdBQU0sR0FBeUIsRUFBRSxDQUFDO1FBRWxDLHFCQUFnQixHQUFHLEtBQUssQ0FBQztRQUcvQixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxHQUFXLEVBQUUsT0FBcUIsRUFBRSxFQUFFO1lBQzNELE9BQU8sSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDMUMsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxtQkFBbUI7UUFDakIsSUFBSSxJQUFJLENBQUMsZ0JBQWdCO1lBQUUsT0FBTztRQUVsQyxxSEFBcUg7UUFDckgsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBRXpDLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1lBQ3hCLGtCQUFrQixHQUFHLEVBQUUsR0FBRyxVQUFVLEVBQUUsQ0FBQztRQUN6QyxDQUFDO1FBRUQsc0VBQXNFO1FBQ3RFLE1BQU0saUJBQWlCLEdBQUcsQ0FBQyxNQUFjLEVBQUUsTUFBYyxFQUFFLEVBQUU7WUFDM0QsSUFBSSxPQUFPLEdBQTJCLEVBQUUsQ0FBQztZQUN6QyxJQUFJLElBQUksR0FBWSxJQUFJLENBQUM7WUFFekIsTUFBTSxXQUFXLEdBQUc7Z0JBQ2xCLEdBQUcsRUFBRSxDQUFDLEdBQW9DLEVBQUUsS0FBYyxFQUFFLEVBQUU7b0JBQzVELElBQUksT0FBTyxHQUFHLEtBQUssUUFBUSxFQUFFLENBQUM7d0JBQzVCLE9BQU8sR0FB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
@@ -0,0 +1,4 @@
1
+ import type { Logger } from '../server';
2
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ export default function declareListTool(mcpServer: McpServer, forestServerUrl: string, logger: Logger, collectionNames?: string[]): void;
4
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1,98 @@
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
+ exports.default = declareListTool;
7
+ const zod_1 = require("zod");
8
+ const filter_js_1 = __importDefault(require("../schemas/filter.js"));
9
+ const activity_logs_creator_js_1 = __importDefault(require("../utils/activity-logs-creator.js"));
10
+ const agent_caller_js_1 = __importDefault(require("../utils/agent-caller.js"));
11
+ const error_parser_js_1 = __importDefault(require("../utils/error-parser.js"));
12
+ const schema_fetcher_js_1 = require("../utils/schema-fetcher.js");
13
+ const tool_with_logging_js_1 = __importDefault(require("../utils/tool-with-logging.js"));
14
+ // Preprocess to handle LLM sending filters as JSON string instead of object
15
+ const filtersWithPreprocess = zod_1.z.preprocess(val => {
16
+ if (typeof val !== 'string')
17
+ return val;
18
+ try {
19
+ return JSON.parse(val);
20
+ }
21
+ catch {
22
+ // Return original value to let Zod validation produce a proper error
23
+ return val;
24
+ }
25
+ }, filter_js_1.default);
26
+ const listArgumentSchema = zod_1.z.object({
27
+ collectionName: zod_1.z.string(),
28
+ search: zod_1.z.string().optional(),
29
+ filters: filtersWithPreprocess
30
+ .describe('Filters to apply on collection. To filter on a nested field, use "@@@" to separate relations, e.g. "relationName@@@fieldName". One level deep max.')
31
+ .optional(),
32
+ sort: zod_1.z
33
+ .object({
34
+ field: zod_1.z.string(),
35
+ ascending: zod_1.z.boolean(),
36
+ })
37
+ .optional(),
38
+ shouldSearchInRelation: zod_1.z
39
+ .boolean()
40
+ .optional()
41
+ .default(false)
42
+ .describe('Whether to search also on related collections'),
43
+ fields: zod_1.z
44
+ .array(zod_1.z.string())
45
+ .describe('Fields to include in the list. Reduces the amount of data returned. For sub fields, use "@@@" to separate relations, e.g. "relationName@@@fieldName".')
46
+ .optional(),
47
+ pagination: zod_1.z
48
+ .object({
49
+ size: zod_1.z.number().default(15).optional(),
50
+ number: zod_1.z.number().default(1).optional(),
51
+ })
52
+ .optional(),
53
+ });
54
+ function createListArgumentShape(collectionNames) {
55
+ return {
56
+ ...listArgumentSchema.shape,
57
+ collectionName: collectionNames.length > 0 ? zod_1.z.enum(collectionNames) : zod_1.z.string(),
58
+ };
59
+ }
60
+ function declareListTool(mcpServer, forestServerUrl, logger, collectionNames = []) {
61
+ const listArgumentShape = createListArgumentShape(collectionNames);
62
+ (0, tool_with_logging_js_1.default)(mcpServer, 'list', {
63
+ title: 'List records from a collection',
64
+ description: 'Retrieve a list of records from the specified collection.',
65
+ inputSchema: listArgumentShape,
66
+ }, async (options, extra) => {
67
+ const { rpcClient } = await (0, agent_caller_js_1.default)(extra);
68
+ let actionType = 'index';
69
+ if (options.search) {
70
+ actionType = 'search';
71
+ }
72
+ else if (options.filters) {
73
+ actionType = 'filter';
74
+ }
75
+ await (0, activity_logs_creator_js_1.default)(forestServerUrl, extra, actionType, {
76
+ collectionName: options.collectionName,
77
+ });
78
+ try {
79
+ const result = await rpcClient
80
+ .collection(options.collectionName)
81
+ .list(options);
82
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
83
+ }
84
+ catch (error) {
85
+ // Parse error text if it's a JSON string from the agent
86
+ const errorDetail = (0, error_parser_js_1.default)(error);
87
+ if (errorDetail?.includes('Invalid sort')) {
88
+ const fields = (0, schema_fetcher_js_1.getFieldsOfCollection)(await (0, schema_fetcher_js_1.fetchForestSchema)(forestServerUrl), options.collectionName);
89
+ throw new Error(`The sort field provided is invalid for this collection. Available fields for the collection ${options.collectionName} are: ${fields
90
+ .filter(field => field.isSortable)
91
+ .map(field => field.field)
92
+ .join(', ')}.`);
93
+ }
94
+ throw errorDetail ? new Error(errorDetail) : error;
95
+ }
96
+ }, logger);
97
+ }
98
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90b29scy9saXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBb0VBLGtDQTZEQztBQTdIRCw2QkFBd0I7QUFFeEIscUVBQWdEO0FBQ2hELGlHQUFrRTtBQUNsRSwrRUFBbUQ7QUFDbkQsK0VBQXVEO0FBQ3ZELGtFQUFzRjtBQUN0Rix5RkFBb0U7QUFFcEUsNEVBQTRFO0FBQzVFLE1BQU0scUJBQXFCLEdBQUcsT0FBQyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsRUFBRTtJQUMvQyxJQUFJLE9BQU8sR0FBRyxLQUFLLFFBQVE7UUFBRSxPQUFPLEdBQUcsQ0FBQztJQUV4QyxJQUFJLENBQUM7UUFDSCxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDekIsQ0FBQztJQUFDLE1BQU0sQ0FBQztRQUNQLHFFQUFxRTtRQUNyRSxPQUFPLEdBQUcsQ0FBQztJQUNiLENBQUM7QUFDSCxDQUFDLEVBQUUsbUJBQVksQ0FBQyxDQUFDO0FBRWpCLE1BQU0sa0JBQWtCLEdBQUcsT0FBQyxDQUFDLE1BQU0sQ0FBQztJQUNsQyxjQUFjLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRTtJQUMxQixNQUFNLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsRUFBRTtJQUM3QixPQUFPLEVBQUUscUJBQXFCO1NBQzNCLFFBQVEsQ0FDUCxvSkFBb0osQ0FDcko7U0FDQSxRQUFRLEVBQUU7SUFDYixJQUFJLEVBQUUsT0FBQztTQUNKLE1BQU0sQ0FBQztRQUNOLEtBQUssRUFBRSxPQUFDLENBQUMsTUFBTSxFQUFFO1FBQ2pCLFNBQVMsRUFBRSxPQUFDLENBQUMsT0FBTyxFQUFFO0tBQ3ZCLENBQUM7U0FDRCxRQUFRLEVBQUU7SUFDYixzQkFBc0IsRUFBRSxPQUFDO1NBQ3RCLE9BQU8sRUFBRTtTQUNULFFBQVEsRUFBRTtTQUNWLE9BQU8sQ0FBQyxLQUFL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
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=list.test.d.ts.map