From d5571e98edaafb4a92f65169cc6ad3b4b39f35df Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 23 Aug 2026 17:12:03 +0200 Subject: [PATCH] Setup MCP --- apps/api/src/app/endpoints/mcp/mcp.controller.ts | 3 ++- apps/api/src/app/endpoints/mcp/mcp.module.ts | 5 ++++- apps/api/src/main.ts | 6 ++---- .../api/src/services/impersonation/impersonation.service.ts | 4 +++- libs/common/src/lib/types/impersonation-context.type.ts | 3 ++- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/apps/api/src/app/endpoints/mcp/mcp.controller.ts b/apps/api/src/app/endpoints/mcp/mcp.controller.ts index 065a82938..ce4205748 100644 --- a/apps/api/src/app/endpoints/mcp/mcp.controller.ts +++ b/apps/api/src/app/endpoints/mcp/mcp.controller.ts @@ -27,9 +27,10 @@ export class GhostfolioMcpController { name: 'get-portfolio' }) public async getPortfolio( - @Impersonation() { userId, userSettings }: ImpersonationContext + @Impersonation() { filters, userId, userSettings }: ImpersonationContext ) { const prompt = await this.aiService.getPrompt({ + filters, userId, languageCode: userSettings.language ?? DEFAULT_LANGUAGE_CODE, mode: 'portfolio', diff --git a/apps/api/src/app/endpoints/mcp/mcp.module.ts b/apps/api/src/app/endpoints/mcp/mcp.module.ts index d1bde09f6..e77bc3b19 100644 --- a/apps/api/src/app/endpoints/mcp/mcp.module.ts +++ b/apps/api/src/app/endpoints/mcp/mcp.module.ts @@ -31,7 +31,10 @@ import { GhostfolioMcpController } from './mcp.controller'; transports: [ new StreamableHttpTransport({ endpoint: MCP_ENDPOINT, - security: { allowedOrigins: [hostname] } + security: { + allowedHosts: [hostname], + allowedOrigins: [hostname] + } }) ], version: environment.version, diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index 15d539615..3d4ddf092 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -137,10 +137,8 @@ async function bootstrap() { const PORT = configService.get('PORT') || DEFAULT_PORT; if (configurationService.get('ENABLE_FEATURE_MCP')) { - const rootUrl = configurationService.get('ROOT_URL'); - - if (rootUrl === environment.rootUrl) { - const { hostname } = new URL(rootUrl); + if (!process.env.ROOT_URL) { + const { hostname } = new URL(configurationService.get('ROOT_URL')); logger.warn( `The Model Context Protocol (MCP) is enabled, but ROOT_URL is not set. A client which calls the endpoint from a browser page is accepted only with the origin ${hostname}.` diff --git a/apps/api/src/services/impersonation/impersonation.service.ts b/apps/api/src/services/impersonation/impersonation.service.ts index 372fdb0f8..5dfb5b0f6 100644 --- a/apps/api/src/services/impersonation/impersonation.service.ts +++ b/apps/api/src/services/impersonation/impersonation.service.ts @@ -2,7 +2,7 @@ import { SubscriptionService } from '@ghostfolio/api/app/subscription/subscripti import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; -import { UserSettings } from '@ghostfolio/common/interfaces'; +import { AccessSettings, UserSettings } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { getScopesOfAccess, @@ -63,9 +63,11 @@ export class ImpersonationService { where: { id: impersonatedUserId } }); + const { filters } = (access?.settings ?? {}) as AccessSettings; const settings = impersonatedUser?.settings?.settings as UserSettings; return { + filters, accessId: impersonationId, authenticatedUserSubscription: user?.subscription, isActive: true, diff --git a/libs/common/src/lib/types/impersonation-context.type.ts b/libs/common/src/lib/types/impersonation-context.type.ts index 29b7970b1..b125191db 100644 --- a/libs/common/src/lib/types/impersonation-context.type.ts +++ b/libs/common/src/lib/types/impersonation-context.type.ts @@ -1,4 +1,4 @@ -import { UserSettings } from '@ghostfolio/common/interfaces'; +import { Filter, UserSettings } from '@ghostfolio/common/interfaces'; import { UserWithSettings } from '@ghostfolio/common/types'; /** @@ -10,6 +10,7 @@ import { UserWithSettings } from '@ghostfolio/common/types'; export interface ImpersonationContext { accessId?: string; authenticatedUserSubscription?: UserWithSettings['subscription']; + filters?: Filter[]; isActive: boolean; scopes: string[]; userId: string;