@lobehub/lobehub 2.0.0-next.193 → 2.0.0-next.195

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 (46) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/ar/models.json +39 -0
  4. package/locales/bg-BG/models.json +40 -0
  5. package/locales/de-DE/models.json +31 -0
  6. package/locales/es-ES/models.json +47 -0
  7. package/locales/fa-IR/models.json +1 -0
  8. package/locales/fr-FR/models.json +1 -0
  9. package/locales/it-IT/models.json +1 -0
  10. package/locales/ja-JP/models.json +53 -0
  11. package/locales/ko-KR/models.json +38 -0
  12. package/locales/nl-NL/models.json +1 -0
  13. package/locales/pl-PL/models.json +1 -0
  14. package/locales/pt-BR/models.json +15 -0
  15. package/locales/ru-RU/models.json +35 -0
  16. package/locales/tr-TR/models.json +29 -0
  17. package/locales/vi-VN/models.json +1 -0
  18. package/locales/zh-CN/models.json +59 -0
  19. package/locales/zh-TW/models.json +31 -0
  20. package/package.json +1 -1
  21. package/packages/database/src/models/user.ts +8 -0
  22. package/packages/database/src/repositories/aiInfra/index.test.ts +11 -8
  23. package/packages/database/src/repositories/dataExporter/index.test.ts +11 -9
  24. package/packages/database/src/repositories/tableViewer/index.test.ts +13 -14
  25. package/packages/model-runtime/src/providers/zhipu/index.ts +6 -6
  26. package/src/envs/app.ts +2 -0
  27. package/src/libs/trpc/lambda/middleware/index.ts +1 -0
  28. package/src/libs/trpc/lambda/middleware/telemetry.test.ts +237 -0
  29. package/src/libs/trpc/lambda/middleware/telemetry.ts +74 -0
  30. package/src/server/routers/lambda/market/index.ts +1 -93
  31. package/src/server/routers/tools/_helpers/index.ts +1 -0
  32. package/src/server/routers/tools/_helpers/scheduleToolCallReport.ts +113 -0
  33. package/src/server/routers/tools/index.ts +2 -2
  34. package/src/server/routers/tools/market.ts +375 -0
  35. package/src/server/routers/tools/mcp.ts +77 -20
  36. package/src/services/chat/index.ts +0 -2
  37. package/src/services/codeInterpreter.ts +6 -6
  38. package/src/services/mcp.test.ts +60 -46
  39. package/src/services/mcp.ts +67 -48
  40. package/src/store/chat/slices/plugin/action.test.ts +191 -0
  41. package/src/store/chat/slices/plugin/actions/internals.ts +2 -18
  42. package/src/store/chat/slices/plugin/actions/pluginTypes.ts +31 -44
  43. package/packages/database/src/client/db.test.ts +0 -52
  44. package/packages/database/src/client/db.ts +0 -195
  45. package/packages/database/src/client/type.ts +0 -6
  46. package/src/server/routers/tools/codeInterpreter.ts +0 -255
package/src/envs/app.ts CHANGED
@@ -82,6 +82,7 @@ export const getAppConfig = () => {
82
82
  * @default false
83
83
  */
84
84
  enableQueueAgentRuntime: z.boolean().optional(),
85
+ TELEMETRY_DISABLED: z.boolean().optional(),
85
86
  },
86
87
  runtimeEnv: {
87
88
  // Sentry
@@ -121,6 +122,7 @@ export const getAppConfig = () => {
121
122
  MARKET_TRUSTED_CLIENT_ID: process.env.MARKET_TRUSTED_CLIENT_ID,
122
123
 
123
124
  enableQueueAgentRuntime: process.env.AGENT_RUNTIME_MODE === 'queue',
125
+ TELEMETRY_DISABLED: process.env.TELEMETRY_DISABLED === '1',
124
126
  },
125
127
  });
126
128
  };
@@ -1,3 +1,4 @@
1
1
  export * from './keyVaults';
2
2
  export * from './marketUserInfo';
3
3
  export * from './serverDatabase';
4
+ export * from './telemetry';
@@ -0,0 +1,237 @@
1
+ // @vitest-environment node
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+
4
+ import { appEnv } from '@/envs/app';
5
+
6
+ import { TelemetryContext, checkTelemetryEnabled } from './telemetry';
7
+
8
+ const { mockGetUserSettings, mockGetUserPreference, MockUserModel } = vi.hoisted(() => {
9
+ const mockGetUserSettings = vi.fn();
10
+ const mockGetUserPreference = vi.fn();
11
+ const MockUserModel = vi.fn().mockImplementation(() => ({
12
+ getUserPreference: mockGetUserPreference,
13
+ getUserSettings: mockGetUserSettings,
14
+ })) as any;
15
+ return { MockUserModel, mockGetUserPreference, mockGetUserSettings };
16
+ });
17
+
18
+ vi.mock('@/envs/app', () => ({
19
+ appEnv: {
20
+ TELEMETRY_DISABLED: false,
21
+ },
22
+ }));
23
+
24
+ vi.mock('@/database/models/user', () => ({
25
+ UserModel: MockUserModel,
26
+ }));
27
+
28
+ describe('checkTelemetryEnabled', () => {
29
+ beforeEach(() => {
30
+ vi.clearAllMocks();
31
+ // Reset appEnv mock
32
+ vi.mocked(appEnv).TELEMETRY_DISABLED = false;
33
+ // Default mock returns
34
+ mockGetUserSettings.mockResolvedValue(null);
35
+ mockGetUserPreference.mockResolvedValue(null);
36
+ });
37
+
38
+ describe('environment variable priority (highest)', () => {
39
+ it('should return telemetryEnabled: false when TELEMETRY_DISABLED=true', async () => {
40
+ vi.mocked(appEnv).TELEMETRY_DISABLED = true;
41
+
42
+ const result = await checkTelemetryEnabled({
43
+ serverDB: {} as TelemetryContext['serverDB'],
44
+ userId: 'test-user',
45
+ });
46
+
47
+ expect(result).toEqual({ telemetryEnabled: false });
48
+ // Should not call database
49
+ expect(mockGetUserSettings).not.toHaveBeenCalled();
50
+ });
51
+
52
+ it('should check database when TELEMETRY_DISABLED is false', async () => {
53
+ await checkTelemetryEnabled({
54
+ serverDB: {} as TelemetryContext['serverDB'],
55
+ userId: 'test-user',
56
+ });
57
+
58
+ expect(mockGetUserSettings).toHaveBeenCalled();
59
+ });
60
+
61
+ it('should check database when TELEMETRY_DISABLED is undefined', async () => {
62
+ vi.mocked(appEnv).TELEMETRY_DISABLED = undefined;
63
+
64
+ await checkTelemetryEnabled({
65
+ serverDB: {} as TelemetryContext['serverDB'],
66
+ userId: 'test-user',
67
+ });
68
+
69
+ expect(mockGetUserSettings).toHaveBeenCalled();
70
+ });
71
+ });
72
+
73
+ describe('user_settings.general.telemetry', () => {
74
+ it('should return telemetryEnabled: true when settings.general.telemetry is true and preference is not set', async () => {
75
+ mockGetUserSettings.mockResolvedValue({
76
+ general: { telemetry: true },
77
+ });
78
+ mockGetUserPreference.mockResolvedValue(null);
79
+
80
+ const result = await checkTelemetryEnabled({
81
+ serverDB: {} as TelemetryContext['serverDB'],
82
+ userId: 'test-user',
83
+ });
84
+
85
+ expect(result).toEqual({ telemetryEnabled: true });
86
+ });
87
+
88
+ it('should return telemetryEnabled: false from settings.general', async () => {
89
+ mockGetUserSettings.mockResolvedValue({
90
+ general: { telemetry: false },
91
+ });
92
+
93
+ const result = await checkTelemetryEnabled({
94
+ serverDB: {} as TelemetryContext['serverDB'],
95
+ userId: 'test-user',
96
+ });
97
+
98
+ expect(result).toEqual({ telemetryEnabled: false });
99
+ });
100
+
101
+ it('should check preference when settings.general.telemetry is not set', async () => {
102
+ mockGetUserSettings.mockResolvedValue({
103
+ general: { fontSize: 14 }, // no telemetry field
104
+ });
105
+ mockGetUserPreference.mockResolvedValue({ telemetry: true });
106
+
107
+ const result = await checkTelemetryEnabled({
108
+ serverDB: {} as TelemetryContext['serverDB'],
109
+ userId: 'test-user',
110
+ });
111
+
112
+ // Should fall back to preference.telemetry
113
+ expect(result).toEqual({ telemetryEnabled: true });
114
+ expect(mockGetUserPreference).toHaveBeenCalled();
115
+ });
116
+ });
117
+
118
+ describe('users.preference.telemetry (deprecated, fallback)', () => {
119
+ it('should return telemetryEnabled: true from preference.telemetry', async () => {
120
+ mockGetUserSettings.mockResolvedValue(null);
121
+ mockGetUserPreference.mockResolvedValue({ telemetry: true });
122
+
123
+ const result = await checkTelemetryEnabled({
124
+ serverDB: {} as TelemetryContext['serverDB'],
125
+ userId: 'test-user',
126
+ });
127
+
128
+ expect(result).toEqual({ telemetryEnabled: true });
129
+ });
130
+
131
+ it('should return telemetryEnabled: false from preference.telemetry', async () => {
132
+ mockGetUserSettings.mockResolvedValue(null);
133
+ mockGetUserPreference.mockResolvedValue({ telemetry: false });
134
+
135
+ const result = await checkTelemetryEnabled({
136
+ serverDB: {} as TelemetryContext['serverDB'],
137
+ userId: 'test-user',
138
+ });
139
+
140
+ expect(result).toEqual({ telemetryEnabled: false });
141
+ });
142
+
143
+ it('should use preference.telemetry when settings.general.telemetry is not false', async () => {
144
+ mockGetUserSettings.mockResolvedValue({
145
+ general: { telemetry: true },
146
+ });
147
+ mockGetUserPreference.mockResolvedValue({ telemetry: false });
148
+
149
+ const result = await checkTelemetryEnabled({
150
+ serverDB: {} as TelemetryContext['serverDB'],
151
+ userId: 'test-user',
152
+ });
153
+
154
+ // preference.telemetry is checked when settings.general.telemetry is not false
155
+ expect(result).toEqual({ telemetryEnabled: false });
156
+ expect(mockGetUserPreference).toHaveBeenCalled();
157
+ });
158
+
159
+ it('should not call getUserPreference when settings.general.telemetry is explicitly false', async () => {
160
+ mockGetUserSettings.mockResolvedValue({
161
+ general: { telemetry: false },
162
+ });
163
+ mockGetUserPreference.mockResolvedValue({ telemetry: true });
164
+
165
+ const result = await checkTelemetryEnabled({
166
+ serverDB: {} as TelemetryContext['serverDB'],
167
+ userId: 'test-user',
168
+ });
169
+
170
+ expect(result).toEqual({ telemetryEnabled: false });
171
+ expect(mockGetUserPreference).not.toHaveBeenCalled();
172
+ });
173
+ });
174
+
175
+ describe('default value', () => {
176
+ it('should default to true when settings is null', async () => {
177
+ mockGetUserSettings.mockResolvedValue(null);
178
+
179
+ const result = await checkTelemetryEnabled({
180
+ serverDB: {} as TelemetryContext['serverDB'],
181
+ userId: 'test-user',
182
+ });
183
+
184
+ // Default to true (enabled) unless explicitly disabled
185
+ expect(result).toEqual({ telemetryEnabled: true });
186
+ });
187
+
188
+ it('should default to true when general is null', async () => {
189
+ mockGetUserSettings.mockResolvedValue({
190
+ general: null,
191
+ });
192
+
193
+ const result = await checkTelemetryEnabled({
194
+ serverDB: {} as TelemetryContext['serverDB'],
195
+ userId: 'test-user',
196
+ });
197
+
198
+ // Default to true (enabled) unless explicitly disabled
199
+ expect(result).toEqual({ telemetryEnabled: true });
200
+ });
201
+ });
202
+
203
+ describe('missing context', () => {
204
+ it('should return telemetryEnabled: false when userId is missing', async () => {
205
+ const result = await checkTelemetryEnabled({
206
+ serverDB: {} as TelemetryContext['serverDB'],
207
+ userId: null,
208
+ });
209
+
210
+ expect(result).toEqual({ telemetryEnabled: false });
211
+ expect(mockGetUserSettings).not.toHaveBeenCalled();
212
+ });
213
+
214
+ it('should return telemetryEnabled: false when serverDB is missing', async () => {
215
+ const result = await checkTelemetryEnabled({
216
+ serverDB: undefined,
217
+ userId: 'test-user',
218
+ });
219
+
220
+ expect(result).toEqual({ telemetryEnabled: false });
221
+ expect(mockGetUserSettings).not.toHaveBeenCalled();
222
+ });
223
+ });
224
+
225
+ describe('error handling', () => {
226
+ it('should return telemetryEnabled: false when getUserSettings fails', async () => {
227
+ mockGetUserSettings.mockRejectedValue(new Error('Database error'));
228
+
229
+ const result = await checkTelemetryEnabled({
230
+ serverDB: {} as TelemetryContext['serverDB'],
231
+ userId: 'test-user',
232
+ });
233
+
234
+ expect(result).toEqual({ telemetryEnabled: false });
235
+ });
236
+ });
237
+ });
@@ -0,0 +1,74 @@
1
+ import type { LobeChatDatabase } from '@lobechat/database';
2
+ import type { UserGeneralConfig } from '@lobechat/types';
3
+
4
+ import { UserModel } from '@/database/models/user';
5
+ import { appEnv } from '@/envs/app';
6
+
7
+ import { trpc } from '../init';
8
+
9
+ export interface TelemetryContext {
10
+ serverDB?: LobeChatDatabase;
11
+ userId?: string | null;
12
+ }
13
+
14
+ export interface TelemetryResult {
15
+ telemetryEnabled: boolean;
16
+ }
17
+
18
+ /**
19
+ * Check if telemetry is enabled for the current user
20
+ *
21
+ * Priority:
22
+ * 1. Environment variable TELEMETRY_DISABLED=1 → telemetryEnabled: false (highest priority)
23
+ * 2. User settings from database user_settings.general.telemetry (new location)
24
+ * 3. User preference from database users.preference.telemetry (old location, deprecated)
25
+ * 4. Default to true if not explicitly set
26
+ */
27
+ export const checkTelemetryEnabled = async (ctx: TelemetryContext): Promise<TelemetryResult> => {
28
+ // Priority 1: Check environment variable (highest priority)
29
+ if (appEnv.TELEMETRY_DISABLED) {
30
+ return { telemetryEnabled: false };
31
+ }
32
+
33
+ // If userId or serverDB is not available, default to disabled
34
+ if (!ctx.userId || !ctx.serverDB) {
35
+ return { telemetryEnabled: false };
36
+ }
37
+
38
+ try {
39
+ const userModel = new UserModel(ctx.serverDB, ctx.userId);
40
+
41
+ // Priority 2: Check user settings (new location: settings.general.telemetry)
42
+ const settings = await userModel.getUserSettings();
43
+ const generalConfig = settings?.general as UserGeneralConfig | null | undefined;
44
+
45
+ if (generalConfig?.telemetry === false) {
46
+ return { telemetryEnabled: false };
47
+ }
48
+
49
+ // Priority 3: Check user preference (old location: preference.telemetry)
50
+ const preference = await userModel.getUserPreference();
51
+
52
+ if (typeof preference?.telemetry === 'boolean') {
53
+ return { telemetryEnabled: preference?.telemetry };
54
+ }
55
+
56
+ // Priority 4: Default to true if not explicitly set
57
+ return { telemetryEnabled: true };
58
+ } catch {
59
+ // If fetching user settings fails, default to disabled
60
+ return { telemetryEnabled: false };
61
+ }
62
+ };
63
+
64
+ /**
65
+ * Middleware that checks if telemetry is enabled for the current user
66
+ * and adds telemetryEnabled to the context
67
+ *
68
+ * Requires serverDatabase middleware to be applied first
69
+ */
70
+ export const telemetry = trpc.middleware(async (opts) => {
71
+ const result = await checkTelemetryEnabled(opts.ctx as TelemetryContext);
72
+
73
+ return opts.next({ ctx: result });
74
+ });
@@ -4,15 +4,9 @@ import { serialize } from 'cookie';
4
4
  import debug from 'debug';
5
5
  import { z } from 'zod';
6
6
 
7
- import { type ToolCallContent } from '@/libs/mcp';
8
- import { authedProcedure, publicProcedure, router } from '@/libs/trpc/lambda';
7
+ import { publicProcedure, router } from '@/libs/trpc/lambda';
9
8
  import { marketUserInfo, serverDatabase } from '@/libs/trpc/lambda/middleware';
10
9
  import { DiscoverService } from '@/server/services/discover';
11
- import { FileService } from '@/server/services/file';
12
- import {
13
- contentBlocksToString,
14
- processContentBlocks,
15
- } from '@/server/services/mcp/contentProcessor';
16
10
  import {
17
11
  AssistantSorts,
18
12
  McpConnectionType,
@@ -41,93 +35,7 @@ const marketProcedure = publicProcedure
41
35
  });
42
36
  });
43
37
 
44
- // Procedure with user authentication for operations requiring user access token
45
- const authedMarketProcedure = authedProcedure
46
- .use(serverDatabase)
47
- .use(marketUserInfo)
48
- .use(async ({ ctx, next }) => {
49
- const { UserModel } = await import('@/database/models/user');
50
- const userModel = new UserModel(ctx.serverDB, ctx.userId);
51
-
52
- return next({
53
- ctx: {
54
- discoverService: new DiscoverService({
55
- accessToken: ctx.marketAccessToken,
56
- userInfo: ctx.marketUserInfo,
57
- }),
58
- fileService: new FileService(ctx.serverDB, ctx.userId),
59
- userModel,
60
- },
61
- });
62
- });
63
-
64
38
  export const marketRouter = router({
65
- // ============================== Cloud MCP Gateway ==============================
66
- callCloudMcpEndpoint: authedMarketProcedure
67
- .input(
68
- z.object({
69
- apiParams: z.record(z.any()),
70
- identifier: z.string(),
71
- toolName: z.string(),
72
- }),
73
- )
74
- .mutation(async ({ input, ctx }) => {
75
- log('callCloudMcpEndpoint input: %O', input);
76
-
77
- try {
78
- // Query user_settings to get market.accessToken
79
- const userState = await ctx.userModel.getUserState(async () => ({}));
80
- const userAccessToken = userState.settings?.market?.accessToken;
81
-
82
- log('callCloudMcpEndpoint: userAccessToken exists=%s', !!userAccessToken);
83
-
84
- if (!userAccessToken) {
85
- throw new TRPCError({
86
- code: 'UNAUTHORIZED',
87
- message: 'User access token not found. Please sign in to Market first.',
88
- });
89
- }
90
-
91
- const cloudResult = await ctx.discoverService.callCloudMcpEndpoint({
92
- apiParams: input.apiParams,
93
- identifier: input.identifier,
94
- toolName: input.toolName,
95
- userAccessToken,
96
- });
97
- const cloudResultContent = (cloudResult?.content ?? []) as ToolCallContent[];
98
-
99
- // Format the cloud result to MCPToolCallResult format
100
- // Process content blocks (upload images, etc.)
101
- const newContent =
102
- cloudResult?.isError || !ctx.fileService
103
- ? cloudResultContent
104
- : // FIXME: the type assertion here is a temporary solution, need to remove it after refactoring
105
- await processContentBlocks(cloudResultContent, ctx.fileService);
106
-
107
- // Convert content blocks to string
108
- const content = contentBlocksToString(newContent);
109
- const state = { ...cloudResult, content: newContent };
110
-
111
- if (cloudResult?.isError) {
112
- return { content, state, success: true };
113
- }
114
-
115
- return { content, state, success: true };
116
- } catch (error) {
117
- log('Error calling cloud MCP endpoint: %O', error);
118
-
119
- // Re-throw TRPCError as-is
120
- if (error instanceof TRPCError) {
121
- throw error;
122
- }
123
-
124
- throw new TRPCError({
125
- code: 'INTERNAL_SERVER_ERROR',
126
- message: 'Failed to call cloud MCP endpoint',
127
- });
128
- }
129
- }),
130
-
131
39
  // ============================== Assistant Market ==============================
132
40
  getAssistantCategories: marketProcedure
133
41
  .input(
@@ -0,0 +1 @@
1
+ export * from './scheduleToolCallReport';
@@ -0,0 +1,113 @@
1
+ import { CURRENT_VERSION } from '@lobechat/const';
2
+ import { type CallReportRequest } from '@lobehub/market-types';
3
+ import { after } from 'next/server';
4
+
5
+ import { DiscoverService } from '@/server/services/discover';
6
+
7
+ /**
8
+ * Calculate byte size of object
9
+ */
10
+ const calculateObjectSizeBytes = (obj: unknown): number => {
11
+ try {
12
+ const jsonString = JSON.stringify(obj);
13
+ return new TextEncoder().encode(jsonString).length;
14
+ } catch {
15
+ return 0;
16
+ }
17
+ };
18
+
19
+ export interface ToolCallReportMeta {
20
+ customPluginInfo?: {
21
+ avatar?: string;
22
+ description?: string;
23
+ name?: string;
24
+ };
25
+ isCustomPlugin?: boolean;
26
+ sessionId?: string;
27
+ version?: string;
28
+ }
29
+
30
+ export interface ScheduleToolCallReportParams {
31
+ /** Error code if call failed */
32
+ errorCode?: string;
33
+ /** Error message if call failed */
34
+ errorMessage?: string;
35
+ /** Plugin/tool identifier */
36
+ identifier: string;
37
+ /** Market access token for reporting */
38
+ marketAccessToken?: string;
39
+ /** MCP connection type */
40
+ mcpType: string;
41
+ /** Metadata for reporting */
42
+ meta?: ToolCallReportMeta;
43
+ /** Request payload for size calculation */
44
+ requestPayload: unknown;
45
+ /** Result for size calculation */
46
+ result?: unknown;
47
+ /** Start time of the call */
48
+ startTime: number;
49
+ /** Whether the call was successful */
50
+ success: boolean;
51
+ /** Whether telemetry is enabled */
52
+ telemetryEnabled: boolean;
53
+ /** Tool/method name */
54
+ toolName: string;
55
+ }
56
+
57
+ /**
58
+ * Schedule a tool call report to be sent after the response.
59
+ * Uses Next.js after() to avoid blocking the response.
60
+ */
61
+ export function scheduleToolCallReport(params: ScheduleToolCallReportParams): void {
62
+ const {
63
+ telemetryEnabled,
64
+ marketAccessToken,
65
+ startTime,
66
+ success,
67
+ errorCode,
68
+ errorMessage,
69
+ result,
70
+ meta,
71
+ identifier,
72
+ toolName,
73
+ mcpType,
74
+ requestPayload,
75
+ } = params;
76
+
77
+ // Only report when telemetry is enabled and marketAccessToken exists
78
+ if (!telemetryEnabled || !marketAccessToken) return;
79
+
80
+ // Use Next.js after() to report after response is sent
81
+ after(async () => {
82
+ try {
83
+ const callDurationMs = Date.now() - startTime;
84
+ const requestSizeBytes = calculateObjectSizeBytes(requestPayload);
85
+ const responseSizeBytes = success && result ? calculateObjectSizeBytes(result) : 0;
86
+
87
+ const reportData: CallReportRequest = {
88
+ callDurationMs,
89
+ customPluginInfo: meta?.customPluginInfo,
90
+ errorCode,
91
+ errorMessage,
92
+ identifier,
93
+ isCustomPlugin: meta?.isCustomPlugin,
94
+ metadata: {
95
+ appVersion: CURRENT_VERSION,
96
+ mcpType,
97
+ },
98
+ methodName: toolName,
99
+ methodType: 'tool',
100
+ requestSizeBytes,
101
+ responseSizeBytes,
102
+ sessionId: meta?.sessionId,
103
+ success,
104
+ version: meta?.version || 'unknown',
105
+ };
106
+
107
+ const discoverService = new DiscoverService({ accessToken: marketAccessToken });
108
+ await discoverService.reportCall(reportData);
109
+ } catch (reportError) {
110
+ console.error('Failed to report tool call: %O', reportError);
111
+ }
112
+ });
113
+ }
@@ -1,14 +1,14 @@
1
1
  import { publicProcedure, router } from '@/libs/trpc/lambda';
2
2
 
3
- import { codeInterpreterRouter } from './codeInterpreter';
4
3
  import { klavisRouter } from './klavis';
4
+ import { marketRouter } from './market';
5
5
  import { mcpRouter } from './mcp';
6
6
  import { searchRouter } from './search';
7
7
 
8
8
  export const toolsRouter = router({
9
- codeInterpreter: codeInterpreterRouter,
10
9
  healthcheck: publicProcedure.query(() => "i'm live!"),
11
10
  klavis: klavisRouter,
11
+ market: marketRouter,
12
12
  mcp: mcpRouter,
13
13
  search: searchRouter,
14
14
  });