Browse Source

Add tool to update account balance to MCP server

pull/8178/head
Thomas Kaul 1 day ago
parent
commit
88ed206acd
  1. 23
      apps/api/src/app/endpoints/mcp/mcp.schemas.spec.ts
  2. 26
      apps/api/src/app/endpoints/mcp/mcp.schemas.ts
  3. 5
      apps/api/src/app/endpoints/mcp/mcp.service.spec.ts
  4. 4
      apps/api/src/app/endpoints/mcp/mcp.service.ts

23
apps/api/src/app/endpoints/mcp/mcp.schemas.spec.ts

@ -7,10 +7,8 @@ import {
SEARCH_QUERY_MINIMUM_LENGTH,
SYMBOL_MAXIMUM_LENGTH
} from '@ghostfolio/common/config';
import { DATE_FORMAT } from '@ghostfolio/common/helper';
import { DataSource } from '@prisma/client';
import { addDays, format } from 'date-fns';
import {
GET_ACCOUNTS_PARAMETERS,
@ -317,6 +315,14 @@ describe('SEARCH_ASSET_PROFILES_PARAMETERS', () => {
});
describe('UPDATE_ACCOUNT_BALANCE_PARAMETERS', () => {
beforeAll(() => {
jest.useFakeTimers().setSystemTime(new Date('2026-09-03T23:30:00.000Z'));
});
afterAll(() => {
jest.useRealTimers();
});
function parse(parameters: Record<string, unknown>) {
return UPDATE_ACCOUNT_BALANCE_PARAMETERS.safeParse({
accountId: 'account-id',
@ -327,7 +333,7 @@ describe('UPDATE_ACCOUNT_BALANCE_PARAMETERS', () => {
}
it('Accepts a cash balance of today', () => {
expect(parse({ date: format(new Date(), DATE_FORMAT) })).toBe(true);
expect(parse({ date: '2026-09-03' })).toBe(true);
});
it('Accepts a negative cash balance', () => {
@ -339,12 +345,15 @@ describe('UPDATE_ACCOUNT_BALANCE_PARAMETERS', () => {
});
it('Refuses a date at or before the epoch', () => {
expect(parse({ date: '0000-01-01' })).toBe(false);
expect(parse({ date: '1969-12-31' })).toBe(false);
expect(parse({ date: '1970-01-01' })).toBe(false);
});
it('Refuses a date and time', () => {
expect(parse({ date: '2024-01-01T00:30:00+02:00' })).toBe(false);
});
it('Refuses a date in the future', () => {
expect(parse({ date: format(addDays(new Date(), 2), DATE_FORMAT) })).toBe(
false
);
expect(parse({ date: '2026-09-05' })).toBe(false);
});
});

26
apps/api/src/app/endpoints/mcp/mcp.schemas.ts

@ -11,14 +11,12 @@ import {
SYMBOL_MAXIMUM_LENGTH
} from '@ghostfolio/common/config';
import {
isAccountBalanceInFuture,
isValidAccountBalanceDate,
isValidCurrencyCode,
isValidDateAfter1970,
resetHours
isValidDateAfter1970
} from '@ghostfolio/common/helper';
import { AssetClass, DataSource, Type as ActivityType } from '@prisma/client';
import { parseISO } from 'date-fns';
import { z } from 'zod';
const DATA_SOURCE_PARAMETER_ERROR = `Invalid option: expected one of ${Object.values(DataSource).join('|')} or a data source as given by a tool`;
@ -188,14 +186,16 @@ export const UPDATE_ACCOUNT_BALANCE_PARAMETERS = z.object({
balance: z
.number()
.describe('The cash balance of the account in the currency of the account'),
date: z
.string()
.refine(isValidDateAfter1970)
.refine(
(date) => {
return !isAccountBalanceInFuture({ date: resetHours(parseISO(date)) });
},
{ error: 'The date must not be in the future' }
)
date: z.iso
.date({
abort: true,
error: 'The date must be an ISO 8601 date, for example 2024-01-01'
})
.refine(isValidDateAfter1970, {
error: 'The date must be after 1970-01-01'
})
.refine(isValidAccountBalanceDate, {
error: 'The date must not be in the future'
})
.describe('The date of the cash balance, as an ISO 8601 date')
});

5
apps/api/src/app/endpoints/mcp/mcp.service.spec.ts

@ -700,7 +700,10 @@ describe('McpService', () => {
expect(await mcpService.updateAccountBalance(parameters)).toEqual({
content: [
{ text: 'Updated the cash balance of the account', type: 'text' }
{
text: 'Updated the cash balance of the account at 2024-01-01',
type: 'text'
}
]
});

4
apps/api/src/app/endpoints/mcp/mcp.service.ts

@ -280,7 +280,9 @@ export class McpService {
userId: account.userId
});
return this.getTextResult('Updated the cash balance of the account');
return this.getTextResult(
`Updated the cash balance of the account at ${date}`
);
}
private getHoldingFilterParameters({

Loading…
Cancel
Save