@burdenoff/mcp-libs 2026.627.1 → 2026.627.3

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 (73) hide show
  1. package/dist/modules/artistrybase/index.cjs +2112 -0
  2. package/dist/modules/artistrybase/index.cjs.map +1 -0
  3. package/dist/modules/artistrybase/index.d.cts +8 -0
  4. package/dist/modules/artistrybase/index.d.ts +8 -0
  5. package/dist/modules/artistrybase/index.js +2051 -0
  6. package/dist/modules/artistrybase/index.js.map +1 -0
  7. package/dist/modules/assethandler/index.cjs +1601 -0
  8. package/dist/modules/assethandler/index.cjs.map +1 -0
  9. package/dist/modules/assethandler/index.d.cts +8 -0
  10. package/dist/modules/assethandler/index.d.ts +8 -0
  11. package/dist/modules/assethandler/index.js +1540 -0
  12. package/dist/modules/assethandler/index.js.map +1 -0
  13. package/dist/modules/brainyrich/index.cjs +2148 -0
  14. package/dist/modules/brainyrich/index.cjs.map +1 -0
  15. package/dist/modules/brainyrich/index.d.cts +8 -0
  16. package/dist/modules/brainyrich/index.d.ts +8 -0
  17. package/dist/modules/brainyrich/index.js +2087 -0
  18. package/dist/modules/brainyrich/index.js.map +1 -0
  19. package/dist/modules/buildmyiq/index.cjs +1562 -0
  20. package/dist/modules/buildmyiq/index.cjs.map +1 -0
  21. package/dist/modules/buildmyiq/index.d.cts +8 -0
  22. package/dist/modules/buildmyiq/index.d.ts +8 -0
  23. package/dist/modules/buildmyiq/index.js +1501 -0
  24. package/dist/modules/buildmyiq/index.js.map +1 -0
  25. package/dist/modules/collabkin/index.cjs +1577 -0
  26. package/dist/modules/collabkin/index.cjs.map +1 -0
  27. package/dist/modules/collabkin/index.d.cts +8 -0
  28. package/dist/modules/collabkin/index.d.ts +8 -0
  29. package/dist/modules/collabkin/index.js +1516 -0
  30. package/dist/modules/collabkin/index.js.map +1 -0
  31. package/dist/modules/comradecircle/index.cjs +1817 -0
  32. package/dist/modules/comradecircle/index.cjs.map +1 -0
  33. package/dist/modules/comradecircle/index.d.cts +8 -0
  34. package/dist/modules/comradecircle/index.d.ts +8 -0
  35. package/dist/modules/comradecircle/index.js +1756 -0
  36. package/dist/modules/comradecircle/index.js.map +1 -0
  37. package/dist/modules/eventfullymanaged/index.cjs +3364 -0
  38. package/dist/modules/eventfullymanaged/index.cjs.map +1 -0
  39. package/dist/modules/eventfullymanaged/index.d.cts +8 -0
  40. package/dist/modules/eventfullymanaged/index.d.ts +8 -0
  41. package/dist/modules/eventfullymanaged/index.js +3303 -0
  42. package/dist/modules/eventfullymanaged/index.js.map +1 -0
  43. package/dist/modules/headshotmarketing/index.cjs +5031 -0
  44. package/dist/modules/headshotmarketing/index.cjs.map +1 -0
  45. package/dist/modules/headshotmarketing/index.d.cts +8 -0
  46. package/dist/modules/headshotmarketing/index.d.ts +8 -0
  47. package/dist/modules/headshotmarketing/index.js +4970 -0
  48. package/dist/modules/headshotmarketing/index.js.map +1 -0
  49. package/dist/modules/movethewheels/index.cjs +3915 -0
  50. package/dist/modules/movethewheels/index.cjs.map +1 -0
  51. package/dist/modules/movethewheels/index.d.cts +8 -0
  52. package/dist/modules/movethewheels/index.d.ts +8 -0
  53. package/dist/modules/movethewheels/index.js +3854 -0
  54. package/dist/modules/movethewheels/index.js.map +1 -0
  55. package/dist/modules/planmagnet/index.cjs +6171 -0
  56. package/dist/modules/planmagnet/index.cjs.map +1 -0
  57. package/dist/modules/planmagnet/index.d.cts +8 -0
  58. package/dist/modules/planmagnet/index.d.ts +8 -0
  59. package/dist/modules/planmagnet/index.js +6110 -0
  60. package/dist/modules/planmagnet/index.js.map +1 -0
  61. package/dist/modules/proserviceworld/index.cjs +1404 -0
  62. package/dist/modules/proserviceworld/index.cjs.map +1 -0
  63. package/dist/modules/proserviceworld/index.d.cts +8 -0
  64. package/dist/modules/proserviceworld/index.d.ts +8 -0
  65. package/dist/modules/proserviceworld/index.js +1343 -0
  66. package/dist/modules/proserviceworld/index.js.map +1 -0
  67. package/dist/modules/timecampus/index.cjs +6482 -0
  68. package/dist/modules/timecampus/index.cjs.map +1 -0
  69. package/dist/modules/timecampus/index.d.cts +8 -0
  70. package/dist/modules/timecampus/index.d.ts +8 -0
  71. package/dist/modules/timecampus/index.js +6421 -0
  72. package/dist/modules/timecampus/index.js.map +1 -0
  73. package/package.json +1 -1
@@ -0,0 +1,2087 @@
1
+ import { mcpToolName, errorResult, jsonResult, mcpResourceUri, mcpPromptName } from '../../chunk-H2OYWLOA.js';
2
+ import { z } from 'zod';
3
+
4
+ // src/modules/brainyrich/operations.ts
5
+ var PAGE_INFO = `pageInfo { cursor hasMore }`;
6
+ var ACCOUNT_FIELDS = `
7
+ id name type status currency cashBalance accountMask institution isManual
8
+ householdId ownerId workspaceId createdAt updatedAt
9
+ `;
10
+ var HOLDING_FIELDS = `
11
+ id accountId portfolioId symbol name assetClass quantity costBasis lastPrice
12
+ marketValue currency asOf workspaceId createdAt updatedAt
13
+ `;
14
+ var TRANSACTION_FIELDS = `
15
+ id accountId holdingId type symbol quantity price amount fee currency
16
+ tradeDate settleDate notes workspaceId createdAt updatedAt
17
+ `;
18
+ var PORTFOLIO_FIELDS = `
19
+ id name description ownerId householdId modelPortfolioId baseCurrency status
20
+ driftThresholdPct targetAllocation tags workspaceId createdAt updatedAt
21
+ `;
22
+ var GOAL_FIELDS = `
23
+ id name type status priority targetAmount targetDate monthlyContribution
24
+ currency projection ownerId householdId notes tags workspaceId
25
+ createdAt updatedAt
26
+ `;
27
+ var GOAL_FUNDING_FIELDS = `
28
+ id goalId accountId allocationPct fixedAmount workspaceId createdAt updatedAt
29
+ `;
30
+ var HOUSEHOLD_FIELDS = `
31
+ id name status riskProfile aum currency advisorId primaryContactName
32
+ primaryContactEmail notes tags workspaceId createdAt updatedAt
33
+ `;
34
+ var MODEL_ALLOCATION_FIELDS = `
35
+ id modelPortfolioId assetClass targetPct workspaceId createdAt updatedAt
36
+ `;
37
+ var MODEL_PORTFOLIO_FIELDS = `
38
+ id name description baseCurrency riskProfile isActive workspaceId
39
+ createdAt updatedAt
40
+ `;
41
+ var REBALANCE_FIELDS = `
42
+ id portfolioId householdId status driftPct rationale trades proposedById
43
+ approvedById executedAt workspaceId createdAt updatedAt
44
+ `;
45
+ var INSIGHT_FIELDS = `
46
+ id type severity status title detail subjectType subjectId metrics ownerId
47
+ acknowledgedById resolvedAt workspaceId createdAt updatedAt
48
+ `;
49
+ var NET_WORTH_SUMMARY_FIELDS = `
50
+ ownerId workspaceId currency totalNetWorth totalMarketValue totalCash
51
+ accountCount byAssetClass { assetClass marketValue }
52
+ `;
53
+ var GET_ACCOUNT = `
54
+ query BrainyrichGetAccount($id: ID!) {
55
+ braAccount(id: $id) {
56
+ ${ACCOUNT_FIELDS}
57
+ holdings { id symbol name assetClass quantity marketValue currency }
58
+ transactions { id type symbol amount quantity price tradeDate }
59
+ }
60
+ }
61
+ `;
62
+ var LIST_ACCOUNTS = `
63
+ query BrainyrichListAccounts(
64
+ $householdId: ID
65
+ $status: BraAccountStatus
66
+ $pagination: PaginationInput
67
+ ) {
68
+ braListAccounts(householdId: $householdId, status: $status, pagination: $pagination) {
69
+ items { ${ACCOUNT_FIELDS} }
70
+ ${PAGE_INFO}
71
+ }
72
+ }
73
+ `;
74
+ var CREATE_ACCOUNT = `
75
+ mutation BrainyrichCreateAccount($input: BraCreateAccountInput!) {
76
+ braCreateAccount(input: $input) {
77
+ ${ACCOUNT_FIELDS}
78
+ }
79
+ }
80
+ `;
81
+ var UPDATE_ACCOUNT = `
82
+ mutation BrainyrichUpdateAccount($id: ID!, $input: BraUpdateAccountInput!) {
83
+ braUpdateAccount(id: $id, input: $input) {
84
+ ${ACCOUNT_FIELDS}
85
+ }
86
+ }
87
+ `;
88
+ var ARCHIVE_ACCOUNT = `
89
+ mutation BrainyrichArchiveAccount($id: ID!) {
90
+ braArchiveAccount(id: $id)
91
+ }
92
+ `;
93
+ var GET_PORTFOLIO = `
94
+ query BrainyrichGetPortfolio($id: ID!) {
95
+ braPortfolio(id: $id) {
96
+ ${PORTFOLIO_FIELDS}
97
+ holdings { id symbol name assetClass quantity marketValue currency }
98
+ }
99
+ }
100
+ `;
101
+ var LIST_PORTFOLIOS = `
102
+ query BrainyrichListPortfolios(
103
+ $householdId: ID
104
+ $status: BraPortfolioStatus
105
+ $pagination: PaginationInput
106
+ ) {
107
+ braListPortfolios(householdId: $householdId, status: $status, pagination: $pagination) {
108
+ items { ${PORTFOLIO_FIELDS} }
109
+ ${PAGE_INFO}
110
+ }
111
+ }
112
+ `;
113
+ var CREATE_PORTFOLIO = `
114
+ mutation BrainyrichCreatePortfolio($input: BraCreatePortfolioInput!) {
115
+ braCreatePortfolio(input: $input) {
116
+ ${PORTFOLIO_FIELDS}
117
+ }
118
+ }
119
+ `;
120
+ var UPDATE_PORTFOLIO = `
121
+ mutation BrainyrichUpdatePortfolio($id: ID!, $input: BraUpdatePortfolioInput!) {
122
+ braUpdatePortfolio(id: $id, input: $input) {
123
+ ${PORTFOLIO_FIELDS}
124
+ }
125
+ }
126
+ `;
127
+ var ARCHIVE_PORTFOLIO = `
128
+ mutation BrainyrichArchivePortfolio($id: ID!) {
129
+ braArchivePortfolio(id: $id)
130
+ }
131
+ `;
132
+ var LIST_HOLDINGS = `
133
+ query BrainyrichListHoldings($accountId: ID, $portfolioId: ID) {
134
+ braListHoldings(accountId: $accountId, portfolioId: $portfolioId) {
135
+ ${HOLDING_FIELDS}
136
+ }
137
+ }
138
+ `;
139
+ var CREATE_HOLDING = `
140
+ mutation BrainyrichCreateHolding($input: BraCreateHoldingInput!) {
141
+ braCreateHolding(input: $input) {
142
+ ${HOLDING_FIELDS}
143
+ }
144
+ }
145
+ `;
146
+ var UPDATE_HOLDING = `
147
+ mutation BrainyrichUpdateHolding($id: ID!, $input: BraUpdateHoldingInput!) {
148
+ braUpdateHolding(id: $id, input: $input) {
149
+ ${HOLDING_FIELDS}
150
+ }
151
+ }
152
+ `;
153
+ var DELETE_HOLDING = `
154
+ mutation BrainyrichDeleteHolding($id: ID!) {
155
+ braDeleteHolding(id: $id)
156
+ }
157
+ `;
158
+ var LIST_TRANSACTIONS = `
159
+ query BrainyrichListTransactions(
160
+ $accountId: ID
161
+ $type: BraTransactionType
162
+ $pagination: PaginationInput
163
+ ) {
164
+ braListTransactions(accountId: $accountId, type: $type, pagination: $pagination) {
165
+ items { ${TRANSACTION_FIELDS} }
166
+ ${PAGE_INFO}
167
+ }
168
+ }
169
+ `;
170
+ var RECORD_TRANSACTION = `
171
+ mutation BrainyrichRecordTransaction($input: BraCreateTransactionInput!) {
172
+ braRecordTransaction(input: $input) {
173
+ ${TRANSACTION_FIELDS}
174
+ }
175
+ }
176
+ `;
177
+ var GET_GOAL = `
178
+ query BrainyrichGetGoal($id: ID!) {
179
+ braGoal(id: $id) {
180
+ ${GOAL_FIELDS}
181
+ fundings { ${GOAL_FUNDING_FIELDS} }
182
+ }
183
+ }
184
+ `;
185
+ var LIST_GOALS = `
186
+ query BrainyrichListGoals(
187
+ $householdId: ID
188
+ $status: BraGoalStatus
189
+ $pagination: PaginationInput
190
+ ) {
191
+ braListGoals(householdId: $householdId, status: $status, pagination: $pagination) {
192
+ items { ${GOAL_FIELDS} }
193
+ ${PAGE_INFO}
194
+ }
195
+ }
196
+ `;
197
+ var CREATE_GOAL = `
198
+ mutation BrainyrichCreateGoal($input: BraCreateGoalInput!) {
199
+ braCreateGoal(input: $input) {
200
+ ${GOAL_FIELDS}
201
+ }
202
+ }
203
+ `;
204
+ var UPDATE_GOAL = `
205
+ mutation BrainyrichUpdateGoal($id: ID!, $input: BraUpdateGoalInput!) {
206
+ braUpdateGoal(id: $id, input: $input) {
207
+ ${GOAL_FIELDS}
208
+ }
209
+ }
210
+ `;
211
+ var ARCHIVE_GOAL = `
212
+ mutation BrainyrichArchiveGoal($id: ID!) {
213
+ braArchiveGoal(id: $id)
214
+ }
215
+ `;
216
+ var RECALC_GOAL_PROJECTION = `
217
+ mutation BrainyrichRecalcGoalProjection($id: ID!, $fundedAmount: Float!) {
218
+ braRecalcGoalProjection(id: $id, fundedAmount: $fundedAmount) {
219
+ ${GOAL_FIELDS}
220
+ }
221
+ }
222
+ `;
223
+ var LIST_GOAL_FUNDINGS = `
224
+ query BrainyrichListGoalFundings($goalId: ID!) {
225
+ braListGoalFundings(goalId: $goalId) {
226
+ ${GOAL_FUNDING_FIELDS}
227
+ }
228
+ }
229
+ `;
230
+ var LINK_GOAL_FUNDING = `
231
+ mutation BrainyrichLinkGoalFunding($input: BraLinkGoalFundingInput!) {
232
+ braLinkGoalFunding(input: $input) {
233
+ ${GOAL_FUNDING_FIELDS}
234
+ }
235
+ }
236
+ `;
237
+ var UNLINK_GOAL_FUNDING = `
238
+ mutation BrainyrichUnlinkGoalFunding($id: ID!) {
239
+ braUnlinkGoalFunding(id: $id)
240
+ }
241
+ `;
242
+ var GET_HOUSEHOLD = `
243
+ query BrainyrichGetHousehold($id: ID!) {
244
+ braHousehold(id: $id) {
245
+ ${HOUSEHOLD_FIELDS}
246
+ }
247
+ }
248
+ `;
249
+ var LIST_HOUSEHOLDS = `
250
+ query BrainyrichListHouseholds(
251
+ $status: BraHouseholdStatus
252
+ $pagination: PaginationInput
253
+ ) {
254
+ braListHouseholds(status: $status, pagination: $pagination) {
255
+ items { ${HOUSEHOLD_FIELDS} }
256
+ ${PAGE_INFO}
257
+ }
258
+ }
259
+ `;
260
+ var CREATE_HOUSEHOLD = `
261
+ mutation BrainyrichCreateHousehold($input: BraCreateHouseholdInput!) {
262
+ braCreateHousehold(input: $input) {
263
+ ${HOUSEHOLD_FIELDS}
264
+ }
265
+ }
266
+ `;
267
+ var UPDATE_HOUSEHOLD = `
268
+ mutation BrainyrichUpdateHousehold($id: ID!, $input: BraUpdateHouseholdInput!) {
269
+ braUpdateHousehold(id: $id, input: $input) {
270
+ ${HOUSEHOLD_FIELDS}
271
+ }
272
+ }
273
+ `;
274
+ var ARCHIVE_HOUSEHOLD = `
275
+ mutation BrainyrichArchiveHousehold($id: ID!) {
276
+ braArchiveHousehold(id: $id)
277
+ }
278
+ `;
279
+ var GET_MODEL_PORTFOLIO = `
280
+ query BrainyrichGetModelPortfolio($id: ID!) {
281
+ braModelPortfolio(id: $id) {
282
+ ${MODEL_PORTFOLIO_FIELDS}
283
+ allocations { ${MODEL_ALLOCATION_FIELDS} }
284
+ }
285
+ }
286
+ `;
287
+ var LIST_MODEL_PORTFOLIOS = `
288
+ query BrainyrichListModelPortfolios {
289
+ braListModelPortfolios {
290
+ ${MODEL_PORTFOLIO_FIELDS}
291
+ allocations { ${MODEL_ALLOCATION_FIELDS} }
292
+ }
293
+ }
294
+ `;
295
+ var CREATE_MODEL_PORTFOLIO = `
296
+ mutation BrainyrichCreateModelPortfolio($input: BraCreateModelPortfolioInput!) {
297
+ braCreateModelPortfolio(input: $input) {
298
+ ${MODEL_PORTFOLIO_FIELDS}
299
+ allocations { ${MODEL_ALLOCATION_FIELDS} }
300
+ }
301
+ }
302
+ `;
303
+ var LIST_REBALANCE_PROPOSALS = `
304
+ query BrainyrichListRebalanceProposals(
305
+ $householdId: ID
306
+ $portfolioId: ID
307
+ $status: BraRebalanceStatus
308
+ ) {
309
+ braListRebalanceProposals(householdId: $householdId, portfolioId: $portfolioId, status: $status) {
310
+ ${REBALANCE_FIELDS}
311
+ }
312
+ }
313
+ `;
314
+ var CREATE_REBALANCE_PROPOSAL = `
315
+ mutation BrainyrichCreateRebalanceProposal($input: BraCreateRebalanceProposalInput!) {
316
+ braCreateRebalanceProposal(input: $input) {
317
+ ${REBALANCE_FIELDS}
318
+ }
319
+ }
320
+ `;
321
+ var TRANSITION_REBALANCE_PROPOSAL = `
322
+ mutation BrainyrichTransitionRebalanceProposal($id: ID!, $status: BraRebalanceStatus!) {
323
+ braTransitionRebalanceProposal(id: $id, status: $status) {
324
+ ${REBALANCE_FIELDS}
325
+ }
326
+ }
327
+ `;
328
+ var GET_INSIGHT = `
329
+ query BrainyrichGetInsight($id: ID!) {
330
+ braInsight(id: $id) {
331
+ ${INSIGHT_FIELDS}
332
+ }
333
+ }
334
+ `;
335
+ var LIST_INSIGHTS = `
336
+ query BrainyrichListInsights(
337
+ $severity: BraInsightSeverity
338
+ $status: BraInsightStatus
339
+ $subjectId: ID
340
+ $subjectType: String
341
+ $pagination: PaginationInput
342
+ ) {
343
+ braListInsights(
344
+ severity: $severity
345
+ status: $status
346
+ subjectId: $subjectId
347
+ subjectType: $subjectType
348
+ pagination: $pagination
349
+ ) {
350
+ items { ${INSIGHT_FIELDS} }
351
+ ${PAGE_INFO}
352
+ }
353
+ }
354
+ `;
355
+ var CREATE_INSIGHT = `
356
+ mutation BrainyrichCreateInsight($input: BraCreateInsightInput!) {
357
+ braCreateInsight(input: $input) {
358
+ ${INSIGHT_FIELDS}
359
+ }
360
+ }
361
+ `;
362
+ var ACKNOWLEDGE_INSIGHT = `
363
+ mutation BrainyrichAcknowledgeInsight($id: ID!) {
364
+ braAcknowledgeInsight(id: $id) {
365
+ ${INSIGHT_FIELDS}
366
+ }
367
+ }
368
+ `;
369
+ var RESOLVE_INSIGHT = `
370
+ mutation BrainyrichResolveInsight($id: ID!) {
371
+ braResolveInsight(id: $id) {
372
+ ${INSIGHT_FIELDS}
373
+ }
374
+ }
375
+ `;
376
+ var DISMISS_INSIGHT = `
377
+ mutation BrainyrichDismissInsight($id: ID!) {
378
+ braDismissInsight(id: $id) {
379
+ ${INSIGHT_FIELDS}
380
+ }
381
+ }
382
+ `;
383
+ var NET_WORTH_SUMMARY = `
384
+ query BrainyrichNetWorthSummary($ownerId: ID) {
385
+ braNetWorthSummary(ownerId: $ownerId) {
386
+ ${NET_WORTH_SUMMARY_FIELDS}
387
+ }
388
+ }
389
+ `;
390
+ var HEALTH = `
391
+ query BrainyrichHealth {
392
+ braHealth
393
+ }
394
+ `;
395
+ var AccountStatusEnum = z.enum(["ACTIVE", "ARCHIVED", "CLOSED"]).describe("Lifecycle status of a financial account");
396
+ var AccountTypeEnum = z.enum([
397
+ "ALTERNATIVE",
398
+ "BANK",
399
+ "BROKERAGE",
400
+ "CASH",
401
+ "CRYPTO",
402
+ "LIABILITY",
403
+ "REAL_ESTATE",
404
+ "RETIREMENT"
405
+ ]).describe("Type/classification of a financial account");
406
+ var AssetClassEnum = z.enum(["ALTERNATIVE", "CASH", "COMMODITY", "CRYPTO", "EQUITY", "FIXED_INCOME", "REAL_ESTATE"]).describe("Asset class of a holding or model allocation");
407
+ var GoalStatusEnum = z.enum(["ABANDONED", "ACHIEVED", "ACTIVE", "ON_HOLD"]).describe("Lifecycle status of a financial goal");
408
+ var GoalTypeEnum = z.enum([
409
+ "CUSTOM",
410
+ "EDUCATION",
411
+ "EMERGENCY_FUND",
412
+ "HOME",
413
+ "MAJOR_PURCHASE",
414
+ "RETIREMENT",
415
+ "WEALTH_GROWTH"
416
+ ]).describe("Category of a financial goal");
417
+ var HouseholdStatusEnum = z.enum(["ACTIVE", "INACTIVE", "ONBOARDING", "PROSPECT"]).describe("Lifecycle status of an advisory household");
418
+ var InsightSeverityEnum = z.enum(["CRITICAL", "HIGH", "INFO", "LOW", "MEDIUM"]).describe("Severity of a proactive insight");
419
+ var InsightStatusEnum = z.enum(["ACKNOWLEDGED", "DISMISSED", "OPEN", "RESOLVED"]).describe("Lifecycle status of a proactive insight");
420
+ var InsightTypeEnum = z.enum([
421
+ "CASH_DRAG",
422
+ "CONCENTRATION_RISK",
423
+ "DRIFT",
424
+ "FEE_ANOMALY",
425
+ "GOAL_AT_RISK",
426
+ "REBALANCE_DUE",
427
+ "TAX_OPPORTUNITY"
428
+ ]).describe("Category of a proactive insight signal");
429
+ var PortfolioStatusEnum = z.enum(["ACTIVE", "ARCHIVED", "DRAFT"]).describe("Lifecycle status of an investment portfolio");
430
+ var RebalanceStatusEnum = z.enum(["APPROVED", "CANCELLED", "DRAFT", "EXECUTED", "PROPOSED", "REJECTED"]).describe("Review-lifecycle status of a rebalance proposal");
431
+ var RiskProfileEnum = z.enum(["AGGRESSIVE", "BALANCED", "CONSERVATIVE", "GROWTH", "MODERATE"]).describe("Risk profile of a household or model portfolio");
432
+ var TransactionTypeEnum = z.enum(["BUY", "DEPOSIT", "DIVIDEND", "FEE", "INTEREST", "SELL", "TRANSFER", "WITHDRAWAL"]).describe("Type of an account transaction");
433
+ var JsonObject = z.record(z.string(), z.unknown());
434
+
435
+ // src/modules/brainyrich/tools/accounts.ts
436
+ function registerAccountTools(server, ctx, track) {
437
+ server.registerTool(
438
+ track(mcpToolName(ctx.productName, "accounts_get")),
439
+ {
440
+ title: "Get Account",
441
+ description: "Fetch a single Brainyrich financial account by ID, including its holdings and recent transactions.",
442
+ inputSchema: { id: z.string().describe("Account ID") }
443
+ },
444
+ async ({ id }) => {
445
+ try {
446
+ const data = await ctx.gql.workspace.request(GET_ACCOUNT, { id });
447
+ if (!data.braAccount) return errorResult(`Account ${id} not found`);
448
+ return jsonResult(data.braAccount);
449
+ } catch (error) {
450
+ ctx.logger.error("brainyrich accounts_get failed", { error: String(error) });
451
+ return errorResult(
452
+ `Failed to get account: ${error instanceof Error ? error.message : String(error)}`
453
+ );
454
+ }
455
+ }
456
+ );
457
+ server.registerTool(
458
+ track(mcpToolName(ctx.productName, "accounts_list")),
459
+ {
460
+ title: "List Accounts",
461
+ description: "List financial accounts in the bound workspace with optional household/status filters and pagination.",
462
+ inputSchema: {
463
+ householdId: z.string().optional().describe("Filter by household ID"),
464
+ status: AccountStatusEnum.optional().describe("Filter by lifecycle status"),
465
+ limit: z.number().int().optional().describe("Page size (default 50)"),
466
+ offset: z.number().int().optional().describe("Pagination offset (default 0)")
467
+ }
468
+ },
469
+ async ({ householdId, status, limit, offset }) => {
470
+ try {
471
+ const data = await ctx.gql.workspace.request(LIST_ACCOUNTS, {
472
+ householdId,
473
+ status,
474
+ pagination: { limit: limit ?? 50, offset: offset ?? 0 }
475
+ });
476
+ return jsonResult(data.braListAccounts);
477
+ } catch (error) {
478
+ ctx.logger.error("brainyrich accounts_list failed", { error: String(error) });
479
+ return errorResult(
480
+ `Failed to list accounts: ${error instanceof Error ? error.message : String(error)}`
481
+ );
482
+ }
483
+ }
484
+ );
485
+ server.registerTool(
486
+ track(mcpToolName(ctx.productName, "accounts_create")),
487
+ {
488
+ title: "Create Account",
489
+ description: "Open a new financial account. `name` is required; `type` defaults to BROKERAGE and `status` to ACTIVE.",
490
+ inputSchema: {
491
+ name: z.string().min(1).describe("Account display name"),
492
+ type: AccountTypeEnum.optional().describe("Account type (default BROKERAGE)"),
493
+ status: AccountStatusEnum.optional().describe("Lifecycle status (default ACTIVE)"),
494
+ currency: z.string().optional().describe("ISO currency code (default workspace currency)"),
495
+ cashBalance: z.number().optional().describe("Opening cash balance"),
496
+ accountMask: z.string().optional().describe('Masked account number, e.g. "****1234"'),
497
+ institution: z.string().optional().describe("Custodian / institution name"),
498
+ isManual: z.boolean().optional().describe("Whether the account is manually tracked"),
499
+ householdId: z.string().optional().describe("Owning household ID"),
500
+ ownerId: z.string().optional().describe("Owning user ID (defaults to the caller)")
501
+ }
502
+ },
503
+ async ({
504
+ name,
505
+ type,
506
+ status,
507
+ currency,
508
+ cashBalance,
509
+ accountMask,
510
+ institution,
511
+ isManual,
512
+ householdId,
513
+ ownerId
514
+ }) => {
515
+ try {
516
+ const input = { name };
517
+ if (type !== void 0) input.type = type;
518
+ if (status !== void 0) input.status = status;
519
+ if (currency !== void 0) input.currency = currency;
520
+ if (cashBalance !== void 0) input.cashBalance = cashBalance;
521
+ if (accountMask !== void 0) input.accountMask = accountMask;
522
+ if (institution !== void 0) input.institution = institution;
523
+ if (isManual !== void 0) input.isManual = isManual;
524
+ if (householdId !== void 0) input.householdId = householdId;
525
+ if (ownerId !== void 0) input.ownerId = ownerId;
526
+ const data = await ctx.gql.workspace.request(CREATE_ACCOUNT, { input });
527
+ return jsonResult(data.braCreateAccount);
528
+ } catch (error) {
529
+ ctx.logger.error("brainyrich accounts_create failed", { error: String(error) });
530
+ return errorResult(
531
+ `Failed to create account: ${error instanceof Error ? error.message : String(error)}`
532
+ );
533
+ }
534
+ }
535
+ );
536
+ server.registerTool(
537
+ track(mcpToolName(ctx.productName, "accounts_update")),
538
+ {
539
+ title: "Update Account",
540
+ description: "Update a financial account. At least one mutable field must be provided.",
541
+ inputSchema: {
542
+ id: z.string().describe("Account ID"),
543
+ name: z.string().optional().describe("Updated display name"),
544
+ type: AccountTypeEnum.optional().describe("Updated account type"),
545
+ status: AccountStatusEnum.optional().describe("Updated lifecycle status"),
546
+ currency: z.string().optional().describe("Updated currency code"),
547
+ cashBalance: z.number().optional().describe("Updated cash balance"),
548
+ accountMask: z.string().optional().describe("Updated masked account number"),
549
+ institution: z.string().optional().describe("Updated institution name"),
550
+ householdId: z.string().optional().describe("Updated owning household ID")
551
+ }
552
+ },
553
+ async ({
554
+ id,
555
+ name,
556
+ type,
557
+ status,
558
+ currency,
559
+ cashBalance,
560
+ accountMask,
561
+ institution,
562
+ householdId
563
+ }) => {
564
+ try {
565
+ const input = {};
566
+ if (name !== void 0) input.name = name;
567
+ if (type !== void 0) input.type = type;
568
+ if (status !== void 0) input.status = status;
569
+ if (currency !== void 0) input.currency = currency;
570
+ if (cashBalance !== void 0) input.cashBalance = cashBalance;
571
+ if (accountMask !== void 0) input.accountMask = accountMask;
572
+ if (institution !== void 0) input.institution = institution;
573
+ if (householdId !== void 0) input.householdId = householdId;
574
+ if (Object.keys(input).length === 0) {
575
+ return errorResult(
576
+ "No updates specified \u2014 provide at least one of name/type/status/currency/cashBalance/accountMask/institution/householdId"
577
+ );
578
+ }
579
+ const data = await ctx.gql.workspace.request(UPDATE_ACCOUNT, { id, input });
580
+ return jsonResult(data.braUpdateAccount);
581
+ } catch (error) {
582
+ ctx.logger.error("brainyrich accounts_update failed", { error: String(error) });
583
+ return errorResult(
584
+ `Failed to update account: ${error instanceof Error ? error.message : String(error)}`
585
+ );
586
+ }
587
+ }
588
+ );
589
+ server.registerTool(
590
+ track(mcpToolName(ctx.productName, "accounts_archive")),
591
+ {
592
+ title: "Archive Account",
593
+ description: "Archive (soft-delete) a financial account by ID. Returns true on success.",
594
+ inputSchema: { id: z.string().describe("Account ID") }
595
+ },
596
+ async ({ id }) => {
597
+ try {
598
+ const data = await ctx.gql.workspace.request(
599
+ ARCHIVE_ACCOUNT,
600
+ { id }
601
+ );
602
+ return jsonResult({ archived: data.braArchiveAccount, id });
603
+ } catch (error) {
604
+ ctx.logger.error("brainyrich accounts_archive failed", { error: String(error) });
605
+ return errorResult(
606
+ `Failed to archive account: ${error instanceof Error ? error.message : String(error)}`
607
+ );
608
+ }
609
+ }
610
+ );
611
+ }
612
+ function registerPortfolioTools(server, ctx, track) {
613
+ server.registerTool(
614
+ track(mcpToolName(ctx.productName, "portfolios_get")),
615
+ {
616
+ title: "Get Portfolio",
617
+ description: "Fetch a single investment portfolio by ID, including its holdings.",
618
+ inputSchema: { id: z.string().describe("Portfolio ID") }
619
+ },
620
+ async ({ id }) => {
621
+ try {
622
+ const data = await ctx.gql.workspace.request(GET_PORTFOLIO, { id });
623
+ if (!data.braPortfolio) return errorResult(`Portfolio ${id} not found`);
624
+ return jsonResult(data.braPortfolio);
625
+ } catch (error) {
626
+ ctx.logger.error("brainyrich portfolios_get failed", { error: String(error) });
627
+ return errorResult(
628
+ `Failed to get portfolio: ${error instanceof Error ? error.message : String(error)}`
629
+ );
630
+ }
631
+ }
632
+ );
633
+ server.registerTool(
634
+ track(mcpToolName(ctx.productName, "portfolios_list")),
635
+ {
636
+ title: "List Portfolios",
637
+ description: "List investment portfolios with optional household/status filters and pagination.",
638
+ inputSchema: {
639
+ householdId: z.string().optional().describe("Filter by household ID"),
640
+ status: PortfolioStatusEnum.optional().describe("Filter by lifecycle status"),
641
+ limit: z.number().int().optional().describe("Page size (default 50)"),
642
+ offset: z.number().int().optional().describe("Pagination offset (default 0)")
643
+ }
644
+ },
645
+ async ({ householdId, status, limit, offset }) => {
646
+ try {
647
+ const data = await ctx.gql.workspace.request(LIST_PORTFOLIOS, {
648
+ householdId,
649
+ status,
650
+ pagination: { limit: limit ?? 50, offset: offset ?? 0 }
651
+ });
652
+ return jsonResult(data.braListPortfolios);
653
+ } catch (error) {
654
+ ctx.logger.error("brainyrich portfolios_list failed", { error: String(error) });
655
+ return errorResult(
656
+ `Failed to list portfolios: ${error instanceof Error ? error.message : String(error)}`
657
+ );
658
+ }
659
+ }
660
+ );
661
+ server.registerTool(
662
+ track(mcpToolName(ctx.productName, "portfolios_create")),
663
+ {
664
+ title: "Create Portfolio",
665
+ description: "Create an investment portfolio. `name` is required. Optionally bind a model portfolio (`modelPortfolioId`) and a target allocation JSON.",
666
+ inputSchema: {
667
+ name: z.string().min(1).describe("Portfolio display name"),
668
+ description: z.string().optional().describe("Portfolio description"),
669
+ ownerId: z.string().optional().describe("Owning user ID (defaults to the caller)"),
670
+ householdId: z.string().optional().describe("Owning household ID"),
671
+ modelPortfolioId: z.string().optional().describe("Model portfolio to track"),
672
+ baseCurrency: z.string().optional().describe("Base currency code"),
673
+ driftThresholdPct: z.number().optional().describe("Drift threshold percentage that triggers rebalance signals"),
674
+ tags: z.array(z.string()).optional().describe("Free-form tags"),
675
+ targetAllocation: JsonObject.optional().describe(
676
+ "Target allocation JSON (asset-class \u2192 target percentage)"
677
+ )
678
+ }
679
+ },
680
+ async ({
681
+ name,
682
+ description,
683
+ ownerId,
684
+ householdId,
685
+ modelPortfolioId,
686
+ baseCurrency,
687
+ driftThresholdPct,
688
+ tags,
689
+ targetAllocation
690
+ }) => {
691
+ try {
692
+ const input = { name };
693
+ if (description !== void 0) input.description = description;
694
+ if (ownerId !== void 0) input.ownerId = ownerId;
695
+ if (householdId !== void 0) input.householdId = householdId;
696
+ if (modelPortfolioId !== void 0) input.modelPortfolioId = modelPortfolioId;
697
+ if (baseCurrency !== void 0) input.baseCurrency = baseCurrency;
698
+ if (driftThresholdPct !== void 0) input.driftThresholdPct = driftThresholdPct;
699
+ if (tags !== void 0) input.tags = tags;
700
+ if (targetAllocation !== void 0) input.targetAllocation = targetAllocation;
701
+ const data = await ctx.gql.workspace.request(CREATE_PORTFOLIO, { input });
702
+ return jsonResult(data.braCreatePortfolio);
703
+ } catch (error) {
704
+ ctx.logger.error("brainyrich portfolios_create failed", { error: String(error) });
705
+ return errorResult(
706
+ `Failed to create portfolio: ${error instanceof Error ? error.message : String(error)}`
707
+ );
708
+ }
709
+ }
710
+ );
711
+ server.registerTool(
712
+ track(mcpToolName(ctx.productName, "portfolios_update")),
713
+ {
714
+ title: "Update Portfolio",
715
+ description: "Update an investment portfolio. At least one mutable field must be provided.",
716
+ inputSchema: {
717
+ id: z.string().describe("Portfolio ID"),
718
+ name: z.string().optional().describe("Updated display name"),
719
+ description: z.string().optional().describe("Updated description"),
720
+ status: PortfolioStatusEnum.optional().describe("Updated lifecycle status"),
721
+ baseCurrency: z.string().optional().describe("Updated base currency"),
722
+ modelPortfolioId: z.string().optional().describe("Updated model portfolio binding"),
723
+ driftThresholdPct: z.number().optional().describe("Updated drift threshold percentage"),
724
+ tags: z.array(z.string()).optional().describe("Updated tags"),
725
+ targetAllocation: JsonObject.optional().describe("Updated target allocation JSON")
726
+ }
727
+ },
728
+ async ({
729
+ id,
730
+ name,
731
+ description,
732
+ status,
733
+ baseCurrency,
734
+ modelPortfolioId,
735
+ driftThresholdPct,
736
+ tags,
737
+ targetAllocation
738
+ }) => {
739
+ try {
740
+ const input = {};
741
+ if (name !== void 0) input.name = name;
742
+ if (description !== void 0) input.description = description;
743
+ if (status !== void 0) input.status = status;
744
+ if (baseCurrency !== void 0) input.baseCurrency = baseCurrency;
745
+ if (modelPortfolioId !== void 0) input.modelPortfolioId = modelPortfolioId;
746
+ if (driftThresholdPct !== void 0) input.driftThresholdPct = driftThresholdPct;
747
+ if (tags !== void 0) input.tags = tags;
748
+ if (targetAllocation !== void 0) input.targetAllocation = targetAllocation;
749
+ if (Object.keys(input).length === 0) {
750
+ return errorResult(
751
+ "No updates specified \u2014 provide at least one of name/description/status/baseCurrency/modelPortfolioId/driftThresholdPct/tags/targetAllocation"
752
+ );
753
+ }
754
+ const data = await ctx.gql.workspace.request(UPDATE_PORTFOLIO, { id, input });
755
+ return jsonResult(data.braUpdatePortfolio);
756
+ } catch (error) {
757
+ ctx.logger.error("brainyrich portfolios_update failed", { error: String(error) });
758
+ return errorResult(
759
+ `Failed to update portfolio: ${error instanceof Error ? error.message : String(error)}`
760
+ );
761
+ }
762
+ }
763
+ );
764
+ server.registerTool(
765
+ track(mcpToolName(ctx.productName, "portfolios_archive")),
766
+ {
767
+ title: "Archive Portfolio",
768
+ description: "Archive (soft-delete) a portfolio by ID. Returns true on success.",
769
+ inputSchema: { id: z.string().describe("Portfolio ID") }
770
+ },
771
+ async ({ id }) => {
772
+ try {
773
+ const data = await ctx.gql.workspace.request(
774
+ ARCHIVE_PORTFOLIO,
775
+ { id }
776
+ );
777
+ return jsonResult({ archived: data.braArchivePortfolio, id });
778
+ } catch (error) {
779
+ ctx.logger.error("brainyrich portfolios_archive failed", { error: String(error) });
780
+ return errorResult(
781
+ `Failed to archive portfolio: ${error instanceof Error ? error.message : String(error)}`
782
+ );
783
+ }
784
+ }
785
+ );
786
+ }
787
+ function registerHoldingTools(server, ctx, track) {
788
+ server.registerTool(
789
+ track(mcpToolName(ctx.productName, "holdings_list")),
790
+ {
791
+ title: "List Holdings",
792
+ description: "List holdings (positions), optionally filtered by account and/or portfolio. Each holding includes a server-derived marketValue.",
793
+ inputSchema: {
794
+ accountId: z.string().optional().describe("Filter by account ID"),
795
+ portfolioId: z.string().optional().describe("Filter by portfolio ID")
796
+ }
797
+ },
798
+ async ({ accountId, portfolioId }) => {
799
+ try {
800
+ const data = await ctx.gql.workspace.request(LIST_HOLDINGS, { accountId, portfolioId });
801
+ return jsonResult(data.braListHoldings);
802
+ } catch (error) {
803
+ ctx.logger.error("brainyrich holdings_list failed", { error: String(error) });
804
+ return errorResult(
805
+ `Failed to list holdings: ${error instanceof Error ? error.message : String(error)}`
806
+ );
807
+ }
808
+ }
809
+ );
810
+ server.registerTool(
811
+ track(mcpToolName(ctx.productName, "holdings_create")),
812
+ {
813
+ title: "Create Holding",
814
+ description: "Add a holding (position) to an account. `accountId` and `symbol` are required; `assetClass` defaults to EQUITY.",
815
+ inputSchema: {
816
+ accountId: z.string().min(1).describe("Owning account ID"),
817
+ symbol: z.string().min(1).describe("Instrument symbol/ticker"),
818
+ name: z.string().optional().describe("Instrument display name"),
819
+ assetClass: AssetClassEnum.optional().describe("Asset class (default EQUITY)"),
820
+ quantity: z.number().optional().describe("Units held"),
821
+ costBasis: z.number().optional().describe("Total cost basis"),
822
+ lastPrice: z.number().optional().describe("Last known unit price"),
823
+ currency: z.string().optional().describe("Currency code"),
824
+ portfolioId: z.string().optional().describe("Portfolio this holding belongs to"),
825
+ asOf: z.string().optional().describe("As-of timestamp (ISO-8601 DateTime)")
826
+ }
827
+ },
828
+ async ({
829
+ accountId,
830
+ symbol,
831
+ name,
832
+ assetClass,
833
+ quantity,
834
+ costBasis,
835
+ lastPrice,
836
+ currency,
837
+ portfolioId,
838
+ asOf
839
+ }) => {
840
+ try {
841
+ const input = { accountId, symbol };
842
+ if (name !== void 0) input.name = name;
843
+ if (assetClass !== void 0) input.assetClass = assetClass;
844
+ if (quantity !== void 0) input.quantity = quantity;
845
+ if (costBasis !== void 0) input.costBasis = costBasis;
846
+ if (lastPrice !== void 0) input.lastPrice = lastPrice;
847
+ if (currency !== void 0) input.currency = currency;
848
+ if (portfolioId !== void 0) input.portfolioId = portfolioId;
849
+ if (asOf !== void 0) input.asOf = asOf;
850
+ const data = await ctx.gql.workspace.request(CREATE_HOLDING, { input });
851
+ return jsonResult(data.braCreateHolding);
852
+ } catch (error) {
853
+ ctx.logger.error("brainyrich holdings_create failed", { error: String(error) });
854
+ return errorResult(
855
+ `Failed to create holding: ${error instanceof Error ? error.message : String(error)}`
856
+ );
857
+ }
858
+ }
859
+ );
860
+ server.registerTool(
861
+ track(mcpToolName(ctx.productName, "holdings_update")),
862
+ {
863
+ title: "Update Holding",
864
+ description: "Update a holding (e.g. mark price, quantity, cost basis). At least one mutable field required.",
865
+ inputSchema: {
866
+ id: z.string().describe("Holding ID"),
867
+ name: z.string().optional().describe("Updated instrument name"),
868
+ assetClass: AssetClassEnum.optional().describe("Updated asset class"),
869
+ quantity: z.number().optional().describe("Updated units held"),
870
+ costBasis: z.number().optional().describe("Updated cost basis"),
871
+ lastPrice: z.number().optional().describe("Updated last price"),
872
+ portfolioId: z.string().optional().describe("Updated portfolio binding"),
873
+ asOf: z.string().optional().describe("Updated as-of timestamp (ISO-8601 DateTime)")
874
+ }
875
+ },
876
+ async ({ id, name, assetClass, quantity, costBasis, lastPrice, portfolioId, asOf }) => {
877
+ try {
878
+ const input = {};
879
+ if (name !== void 0) input.name = name;
880
+ if (assetClass !== void 0) input.assetClass = assetClass;
881
+ if (quantity !== void 0) input.quantity = quantity;
882
+ if (costBasis !== void 0) input.costBasis = costBasis;
883
+ if (lastPrice !== void 0) input.lastPrice = lastPrice;
884
+ if (portfolioId !== void 0) input.portfolioId = portfolioId;
885
+ if (asOf !== void 0) input.asOf = asOf;
886
+ if (Object.keys(input).length === 0) {
887
+ return errorResult(
888
+ "No updates specified \u2014 provide at least one of name/assetClass/quantity/costBasis/lastPrice/portfolioId/asOf"
889
+ );
890
+ }
891
+ const data = await ctx.gql.workspace.request(UPDATE_HOLDING, { id, input });
892
+ return jsonResult(data.braUpdateHolding);
893
+ } catch (error) {
894
+ ctx.logger.error("brainyrich holdings_update failed", { error: String(error) });
895
+ return errorResult(
896
+ `Failed to update holding: ${error instanceof Error ? error.message : String(error)}`
897
+ );
898
+ }
899
+ }
900
+ );
901
+ server.registerTool(
902
+ track(mcpToolName(ctx.productName, "holdings_delete")),
903
+ {
904
+ title: "Delete Holding",
905
+ description: "Delete a holding by ID. Returns true on success.",
906
+ inputSchema: { id: z.string().describe("Holding ID") }
907
+ },
908
+ async ({ id }) => {
909
+ try {
910
+ const data = await ctx.gql.workspace.request(
911
+ DELETE_HOLDING,
912
+ { id }
913
+ );
914
+ return jsonResult({ deleted: data.braDeleteHolding, id });
915
+ } catch (error) {
916
+ ctx.logger.error("brainyrich holdings_delete failed", { error: String(error) });
917
+ return errorResult(
918
+ `Failed to delete holding: ${error instanceof Error ? error.message : String(error)}`
919
+ );
920
+ }
921
+ }
922
+ );
923
+ }
924
+ function registerTransactionTools(server, ctx, track) {
925
+ server.registerTool(
926
+ track(mcpToolName(ctx.productName, "transactions_list")),
927
+ {
928
+ title: "List Transactions",
929
+ description: "List account transactions, optionally filtered by account and/or type, with pagination.",
930
+ inputSchema: {
931
+ accountId: z.string().optional().describe("Filter by account ID"),
932
+ type: TransactionTypeEnum.optional().describe("Filter by transaction type"),
933
+ limit: z.number().int().optional().describe("Page size (default 50)"),
934
+ offset: z.number().int().optional().describe("Pagination offset (default 0)")
935
+ }
936
+ },
937
+ async ({ accountId, type, limit, offset }) => {
938
+ try {
939
+ const data = await ctx.gql.workspace.request(LIST_TRANSACTIONS, {
940
+ accountId,
941
+ type,
942
+ pagination: { limit: limit ?? 50, offset: offset ?? 0 }
943
+ });
944
+ return jsonResult(data.braListTransactions);
945
+ } catch (error) {
946
+ ctx.logger.error("brainyrich transactions_list failed", { error: String(error) });
947
+ return errorResult(
948
+ `Failed to list transactions: ${error instanceof Error ? error.message : String(error)}`
949
+ );
950
+ }
951
+ }
952
+ );
953
+ server.registerTool(
954
+ track(mcpToolName(ctx.productName, "transactions_record")),
955
+ {
956
+ title: "Record Transaction",
957
+ description: "Record a transaction against an account. `accountId`, `type`, and `tradeDate` are required. Trade types (BUY/SELL/etc.) may carry symbol/quantity/price; cash types (DEPOSIT/WITHDRAWAL/etc.) carry amount.",
958
+ inputSchema: {
959
+ accountId: z.string().min(1).describe("Account the transaction posts to"),
960
+ type: TransactionTypeEnum.describe("Transaction type"),
961
+ tradeDate: z.string().min(1).describe("Trade date (ISO-8601 DateTime)"),
962
+ symbol: z.string().optional().describe("Instrument symbol (trades)"),
963
+ holdingId: z.string().optional().describe("Related holding ID (trades)"),
964
+ quantity: z.number().optional().describe("Units transacted"),
965
+ price: z.number().optional().describe("Unit price"),
966
+ amount: z.number().optional().describe("Cash amount (cash transactions)"),
967
+ fee: z.number().optional().describe("Transaction fee"),
968
+ currency: z.string().optional().describe("Currency code"),
969
+ settleDate: z.string().optional().describe("Settlement date (ISO-8601 DateTime)"),
970
+ notes: z.string().optional().describe("Free-form notes")
971
+ }
972
+ },
973
+ async ({
974
+ accountId,
975
+ type,
976
+ tradeDate,
977
+ symbol,
978
+ holdingId,
979
+ quantity,
980
+ price,
981
+ amount,
982
+ fee,
983
+ currency,
984
+ settleDate,
985
+ notes
986
+ }) => {
987
+ try {
988
+ const input = { accountId, type, tradeDate };
989
+ if (symbol !== void 0) input.symbol = symbol;
990
+ if (holdingId !== void 0) input.holdingId = holdingId;
991
+ if (quantity !== void 0) input.quantity = quantity;
992
+ if (price !== void 0) input.price = price;
993
+ if (amount !== void 0) input.amount = amount;
994
+ if (fee !== void 0) input.fee = fee;
995
+ if (currency !== void 0) input.currency = currency;
996
+ if (settleDate !== void 0) input.settleDate = settleDate;
997
+ if (notes !== void 0) input.notes = notes;
998
+ const data = await ctx.gql.workspace.request(RECORD_TRANSACTION, { input });
999
+ return jsonResult(data.braRecordTransaction);
1000
+ } catch (error) {
1001
+ ctx.logger.error("brainyrich transactions_record failed", { error: String(error) });
1002
+ return errorResult(
1003
+ `Failed to record transaction: ${error instanceof Error ? error.message : String(error)}`
1004
+ );
1005
+ }
1006
+ }
1007
+ );
1008
+ }
1009
+ function registerGoalTools(server, ctx, track) {
1010
+ server.registerTool(
1011
+ track(mcpToolName(ctx.productName, "goals_get")),
1012
+ {
1013
+ title: "Get Goal",
1014
+ description: "Fetch a single financial goal by ID, including its funding links.",
1015
+ inputSchema: { id: z.string().describe("Goal ID") }
1016
+ },
1017
+ async ({ id }) => {
1018
+ try {
1019
+ const data = await ctx.gql.workspace.request(GET_GOAL, { id });
1020
+ if (!data.braGoal) return errorResult(`Goal ${id} not found`);
1021
+ return jsonResult(data.braGoal);
1022
+ } catch (error) {
1023
+ ctx.logger.error("brainyrich goals_get failed", { error: String(error) });
1024
+ return errorResult(
1025
+ `Failed to get goal: ${error instanceof Error ? error.message : String(error)}`
1026
+ );
1027
+ }
1028
+ }
1029
+ );
1030
+ server.registerTool(
1031
+ track(mcpToolName(ctx.productName, "goals_list")),
1032
+ {
1033
+ title: "List Goals",
1034
+ description: "List financial goals with optional household/status filters and pagination.",
1035
+ inputSchema: {
1036
+ householdId: z.string().optional().describe("Filter by household ID"),
1037
+ status: GoalStatusEnum.optional().describe("Filter by goal status"),
1038
+ limit: z.number().int().optional().describe("Page size (default 50)"),
1039
+ offset: z.number().int().optional().describe("Pagination offset (default 0)")
1040
+ }
1041
+ },
1042
+ async ({ householdId, status, limit, offset }) => {
1043
+ try {
1044
+ const data = await ctx.gql.workspace.request(LIST_GOALS, {
1045
+ householdId,
1046
+ status,
1047
+ pagination: { limit: limit ?? 50, offset: offset ?? 0 }
1048
+ });
1049
+ return jsonResult(data.braListGoals);
1050
+ } catch (error) {
1051
+ ctx.logger.error("brainyrich goals_list failed", { error: String(error) });
1052
+ return errorResult(
1053
+ `Failed to list goals: ${error instanceof Error ? error.message : String(error)}`
1054
+ );
1055
+ }
1056
+ }
1057
+ );
1058
+ server.registerTool(
1059
+ track(mcpToolName(ctx.productName, "goals_create")),
1060
+ {
1061
+ title: "Create Goal",
1062
+ description: "Create a financial goal. `name`, `targetAmount`, and `targetDate` are required; `type` defaults to CUSTOM.",
1063
+ inputSchema: {
1064
+ name: z.string().min(1).describe("Goal name"),
1065
+ targetAmount: z.number().describe("Target amount to reach"),
1066
+ targetDate: z.string().min(1).describe("Target date (ISO-8601 DateTime)"),
1067
+ type: GoalTypeEnum.optional().describe("Goal category (default CUSTOM)"),
1068
+ monthlyContribution: z.number().optional().describe("Planned monthly contribution"),
1069
+ priority: z.number().int().optional().describe("Priority rank"),
1070
+ currency: z.string().optional().describe("Currency code"),
1071
+ ownerId: z.string().optional().describe("Owning user ID (defaults to the caller)"),
1072
+ householdId: z.string().optional().describe("Owning household ID"),
1073
+ notes: z.string().optional().describe("Free-form notes"),
1074
+ tags: z.array(z.string()).optional().describe("Free-form tags")
1075
+ }
1076
+ },
1077
+ async ({
1078
+ name,
1079
+ targetAmount,
1080
+ targetDate,
1081
+ type,
1082
+ monthlyContribution,
1083
+ priority,
1084
+ currency,
1085
+ ownerId,
1086
+ householdId,
1087
+ notes,
1088
+ tags
1089
+ }) => {
1090
+ try {
1091
+ const input = { name, targetAmount, targetDate };
1092
+ if (type !== void 0) input.type = type;
1093
+ if (monthlyContribution !== void 0) input.monthlyContribution = monthlyContribution;
1094
+ if (priority !== void 0) input.priority = priority;
1095
+ if (currency !== void 0) input.currency = currency;
1096
+ if (ownerId !== void 0) input.ownerId = ownerId;
1097
+ if (householdId !== void 0) input.householdId = householdId;
1098
+ if (notes !== void 0) input.notes = notes;
1099
+ if (tags !== void 0) input.tags = tags;
1100
+ const data = await ctx.gql.workspace.request(CREATE_GOAL, { input });
1101
+ return jsonResult(data.braCreateGoal);
1102
+ } catch (error) {
1103
+ ctx.logger.error("brainyrich goals_create failed", { error: String(error) });
1104
+ return errorResult(
1105
+ `Failed to create goal: ${error instanceof Error ? error.message : String(error)}`
1106
+ );
1107
+ }
1108
+ }
1109
+ );
1110
+ server.registerTool(
1111
+ track(mcpToolName(ctx.productName, "goals_update")),
1112
+ {
1113
+ title: "Update Goal",
1114
+ description: "Update a financial goal. At least one mutable field must be provided.",
1115
+ inputSchema: {
1116
+ id: z.string().describe("Goal ID"),
1117
+ name: z.string().optional().describe("Updated name"),
1118
+ type: GoalTypeEnum.optional().describe("Updated goal category"),
1119
+ status: GoalStatusEnum.optional().describe("Updated status"),
1120
+ priority: z.number().int().optional().describe("Updated priority rank"),
1121
+ targetAmount: z.number().optional().describe("Updated target amount"),
1122
+ targetDate: z.string().optional().describe("Updated target date (ISO-8601 DateTime)"),
1123
+ monthlyContribution: z.number().optional().describe("Updated monthly contribution"),
1124
+ currency: z.string().optional().describe("Updated currency code"),
1125
+ notes: z.string().optional().describe("Updated notes"),
1126
+ tags: z.array(z.string()).optional().describe("Updated tags")
1127
+ }
1128
+ },
1129
+ async ({
1130
+ id,
1131
+ name,
1132
+ type,
1133
+ status,
1134
+ priority,
1135
+ targetAmount,
1136
+ targetDate,
1137
+ monthlyContribution,
1138
+ currency,
1139
+ notes,
1140
+ tags
1141
+ }) => {
1142
+ try {
1143
+ const input = {};
1144
+ if (name !== void 0) input.name = name;
1145
+ if (type !== void 0) input.type = type;
1146
+ if (status !== void 0) input.status = status;
1147
+ if (priority !== void 0) input.priority = priority;
1148
+ if (targetAmount !== void 0) input.targetAmount = targetAmount;
1149
+ if (targetDate !== void 0) input.targetDate = targetDate;
1150
+ if (monthlyContribution !== void 0) input.monthlyContribution = monthlyContribution;
1151
+ if (currency !== void 0) input.currency = currency;
1152
+ if (notes !== void 0) input.notes = notes;
1153
+ if (tags !== void 0) input.tags = tags;
1154
+ if (Object.keys(input).length === 0) {
1155
+ return errorResult(
1156
+ "No updates specified \u2014 provide at least one of name/type/status/priority/targetAmount/targetDate/monthlyContribution/currency/notes/tags"
1157
+ );
1158
+ }
1159
+ const data = await ctx.gql.workspace.request(UPDATE_GOAL, { id, input });
1160
+ return jsonResult(data.braUpdateGoal);
1161
+ } catch (error) {
1162
+ ctx.logger.error("brainyrich goals_update failed", { error: String(error) });
1163
+ return errorResult(
1164
+ `Failed to update goal: ${error instanceof Error ? error.message : String(error)}`
1165
+ );
1166
+ }
1167
+ }
1168
+ );
1169
+ server.registerTool(
1170
+ track(mcpToolName(ctx.productName, "goals_archive")),
1171
+ {
1172
+ title: "Archive Goal",
1173
+ description: "Archive (soft-delete) a goal by ID. Returns true on success.",
1174
+ inputSchema: { id: z.string().describe("Goal ID") }
1175
+ },
1176
+ async ({ id }) => {
1177
+ try {
1178
+ const data = await ctx.gql.workspace.request(
1179
+ ARCHIVE_GOAL,
1180
+ { id }
1181
+ );
1182
+ return jsonResult({ archived: data.braArchiveGoal, id });
1183
+ } catch (error) {
1184
+ ctx.logger.error("brainyrich goals_archive failed", { error: String(error) });
1185
+ return errorResult(
1186
+ `Failed to archive goal: ${error instanceof Error ? error.message : String(error)}`
1187
+ );
1188
+ }
1189
+ }
1190
+ );
1191
+ server.registerTool(
1192
+ track(mcpToolName(ctx.productName, "goals_recalc_projection")),
1193
+ {
1194
+ title: "Recalculate Goal Projection",
1195
+ description: "Recompute and persist a goal's projection (AHEAD/ON_TRACK/BEHIND/AT_RISK) from its funded value vs. target.",
1196
+ inputSchema: {
1197
+ id: z.string().describe("Goal ID"),
1198
+ fundedAmount: z.number().describe("Currently funded amount toward the goal")
1199
+ }
1200
+ },
1201
+ async ({ id, fundedAmount }) => {
1202
+ try {
1203
+ const data = await ctx.gql.workspace.request(RECALC_GOAL_PROJECTION, { id, fundedAmount });
1204
+ return jsonResult(data.braRecalcGoalProjection);
1205
+ } catch (error) {
1206
+ ctx.logger.error("brainyrich goals_recalc_projection failed", { error: String(error) });
1207
+ return errorResult(
1208
+ `Failed to recalc goal projection: ${error instanceof Error ? error.message : String(error)}`
1209
+ );
1210
+ }
1211
+ }
1212
+ );
1213
+ server.registerTool(
1214
+ track(mcpToolName(ctx.productName, "goals_list_fundings")),
1215
+ {
1216
+ title: "List Goal Fundings",
1217
+ description: "List the funding links (account allocations) attached to a goal.",
1218
+ inputSchema: { goalId: z.string().describe("Goal ID") }
1219
+ },
1220
+ async ({ goalId }) => {
1221
+ try {
1222
+ const data = await ctx.gql.workspace.request(LIST_GOAL_FUNDINGS, { goalId });
1223
+ return jsonResult(data.braListGoalFundings);
1224
+ } catch (error) {
1225
+ ctx.logger.error("brainyrich goals_list_fundings failed", { error: String(error) });
1226
+ return errorResult(
1227
+ `Failed to list goal fundings: ${error instanceof Error ? error.message : String(error)}`
1228
+ );
1229
+ }
1230
+ }
1231
+ );
1232
+ server.registerTool(
1233
+ track(mcpToolName(ctx.productName, "goals_link_funding")),
1234
+ {
1235
+ title: "Link Goal Funding",
1236
+ description: "Link an account to a goal as a funding source. Provide either an allocation percentage or a fixed amount.",
1237
+ inputSchema: {
1238
+ goalId: z.string().min(1).describe("Goal to fund"),
1239
+ accountId: z.string().min(1).describe("Funding account"),
1240
+ allocationPct: z.number().optional().describe("Percentage of the account allocated to the goal"),
1241
+ fixedAmount: z.number().optional().describe("Fixed amount allocated to the goal")
1242
+ }
1243
+ },
1244
+ async ({ goalId, accountId, allocationPct, fixedAmount }) => {
1245
+ try {
1246
+ const input = { goalId, accountId };
1247
+ if (allocationPct !== void 0) input.allocationPct = allocationPct;
1248
+ if (fixedAmount !== void 0) input.fixedAmount = fixedAmount;
1249
+ const data = await ctx.gql.workspace.request(LINK_GOAL_FUNDING, { input });
1250
+ return jsonResult(data.braLinkGoalFunding);
1251
+ } catch (error) {
1252
+ ctx.logger.error("brainyrich goals_link_funding failed", { error: String(error) });
1253
+ return errorResult(
1254
+ `Failed to link goal funding: ${error instanceof Error ? error.message : String(error)}`
1255
+ );
1256
+ }
1257
+ }
1258
+ );
1259
+ server.registerTool(
1260
+ track(mcpToolName(ctx.productName, "goals_unlink_funding")),
1261
+ {
1262
+ title: "Unlink Goal Funding",
1263
+ description: "Remove a goal funding link by ID. Returns true on success.",
1264
+ inputSchema: { id: z.string().describe("Goal funding ID") }
1265
+ },
1266
+ async ({ id }) => {
1267
+ try {
1268
+ const data = await ctx.gql.workspace.request(
1269
+ UNLINK_GOAL_FUNDING,
1270
+ { id }
1271
+ );
1272
+ return jsonResult({ unlinked: data.braUnlinkGoalFunding, id });
1273
+ } catch (error) {
1274
+ ctx.logger.error("brainyrich goals_unlink_funding failed", { error: String(error) });
1275
+ return errorResult(
1276
+ `Failed to unlink goal funding: ${error instanceof Error ? error.message : String(error)}`
1277
+ );
1278
+ }
1279
+ }
1280
+ );
1281
+ }
1282
+ function registerHouseholdTools(server, ctx, track) {
1283
+ server.registerTool(
1284
+ track(mcpToolName(ctx.productName, "households_get")),
1285
+ {
1286
+ title: "Get Household",
1287
+ description: "Fetch a single advisory household by ID.",
1288
+ inputSchema: { id: z.string().describe("Household ID") }
1289
+ },
1290
+ async ({ id }) => {
1291
+ try {
1292
+ const data = await ctx.gql.workspace.request(GET_HOUSEHOLD, { id });
1293
+ if (!data.braHousehold) return errorResult(`Household ${id} not found`);
1294
+ return jsonResult(data.braHousehold);
1295
+ } catch (error) {
1296
+ ctx.logger.error("brainyrich households_get failed", { error: String(error) });
1297
+ return errorResult(
1298
+ `Failed to get household: ${error instanceof Error ? error.message : String(error)}`
1299
+ );
1300
+ }
1301
+ }
1302
+ );
1303
+ server.registerTool(
1304
+ track(mcpToolName(ctx.productName, "households_list")),
1305
+ {
1306
+ title: "List Households",
1307
+ description: "List advisory households with an optional status filter and pagination.",
1308
+ inputSchema: {
1309
+ status: HouseholdStatusEnum.optional().describe("Filter by household status"),
1310
+ limit: z.number().int().optional().describe("Page size (default 50)"),
1311
+ offset: z.number().int().optional().describe("Pagination offset (default 0)")
1312
+ }
1313
+ },
1314
+ async ({ status, limit, offset }) => {
1315
+ try {
1316
+ const data = await ctx.gql.workspace.request(LIST_HOUSEHOLDS, {
1317
+ status,
1318
+ pagination: { limit: limit ?? 50, offset: offset ?? 0 }
1319
+ });
1320
+ return jsonResult(data.braListHouseholds);
1321
+ } catch (error) {
1322
+ ctx.logger.error("brainyrich households_list failed", { error: String(error) });
1323
+ return errorResult(
1324
+ `Failed to list households: ${error instanceof Error ? error.message : String(error)}`
1325
+ );
1326
+ }
1327
+ }
1328
+ );
1329
+ server.registerTool(
1330
+ track(mcpToolName(ctx.productName, "households_create")),
1331
+ {
1332
+ title: "Create Household",
1333
+ description: "Create an advisory household. `name` is required; `riskProfile` defaults to BALANCED and `status` to PROSPECT.",
1334
+ inputSchema: {
1335
+ name: z.string().min(1).describe("Household name"),
1336
+ status: HouseholdStatusEnum.optional().describe("Lifecycle status (default PROSPECT)"),
1337
+ riskProfile: RiskProfileEnum.optional().describe("Risk profile (default BALANCED)"),
1338
+ currency: z.string().optional().describe("Reporting currency code"),
1339
+ advisorId: z.string().optional().describe("Assigned advisor user ID"),
1340
+ primaryContactName: z.string().optional().describe("Primary contact name"),
1341
+ primaryContactEmail: z.string().optional().describe("Primary contact email"),
1342
+ notes: z.string().optional().describe("Free-form notes"),
1343
+ tags: z.array(z.string()).optional().describe("Free-form tags")
1344
+ }
1345
+ },
1346
+ async ({
1347
+ name,
1348
+ status,
1349
+ riskProfile,
1350
+ currency,
1351
+ advisorId,
1352
+ primaryContactName,
1353
+ primaryContactEmail,
1354
+ notes,
1355
+ tags
1356
+ }) => {
1357
+ try {
1358
+ const input = { name };
1359
+ if (status !== void 0) input.status = status;
1360
+ if (riskProfile !== void 0) input.riskProfile = riskProfile;
1361
+ if (currency !== void 0) input.currency = currency;
1362
+ if (advisorId !== void 0) input.advisorId = advisorId;
1363
+ if (primaryContactName !== void 0) input.primaryContactName = primaryContactName;
1364
+ if (primaryContactEmail !== void 0) input.primaryContactEmail = primaryContactEmail;
1365
+ if (notes !== void 0) input.notes = notes;
1366
+ if (tags !== void 0) input.tags = tags;
1367
+ const data = await ctx.gql.workspace.request(CREATE_HOUSEHOLD, { input });
1368
+ return jsonResult(data.braCreateHousehold);
1369
+ } catch (error) {
1370
+ ctx.logger.error("brainyrich households_create failed", { error: String(error) });
1371
+ return errorResult(
1372
+ `Failed to create household: ${error instanceof Error ? error.message : String(error)}`
1373
+ );
1374
+ }
1375
+ }
1376
+ );
1377
+ server.registerTool(
1378
+ track(mcpToolName(ctx.productName, "households_update")),
1379
+ {
1380
+ title: "Update Household",
1381
+ description: "Update an advisory household. At least one mutable field must be provided.",
1382
+ inputSchema: {
1383
+ id: z.string().describe("Household ID"),
1384
+ name: z.string().optional().describe("Updated name"),
1385
+ status: HouseholdStatusEnum.optional().describe("Updated status"),
1386
+ riskProfile: RiskProfileEnum.optional().describe("Updated risk profile"),
1387
+ aum: z.number().optional().describe("Updated assets-under-management figure"),
1388
+ currency: z.string().optional().describe("Updated currency code"),
1389
+ primaryContactName: z.string().optional().describe("Updated primary contact name"),
1390
+ primaryContactEmail: z.string().optional().describe("Updated primary contact email"),
1391
+ notes: z.string().optional().describe("Updated notes"),
1392
+ tags: z.array(z.string()).optional().describe("Updated tags")
1393
+ }
1394
+ },
1395
+ async ({
1396
+ id,
1397
+ name,
1398
+ status,
1399
+ riskProfile,
1400
+ aum,
1401
+ currency,
1402
+ primaryContactName,
1403
+ primaryContactEmail,
1404
+ notes,
1405
+ tags
1406
+ }) => {
1407
+ try {
1408
+ const input = {};
1409
+ if (name !== void 0) input.name = name;
1410
+ if (status !== void 0) input.status = status;
1411
+ if (riskProfile !== void 0) input.riskProfile = riskProfile;
1412
+ if (aum !== void 0) input.aum = aum;
1413
+ if (currency !== void 0) input.currency = currency;
1414
+ if (primaryContactName !== void 0) input.primaryContactName = primaryContactName;
1415
+ if (primaryContactEmail !== void 0) input.primaryContactEmail = primaryContactEmail;
1416
+ if (notes !== void 0) input.notes = notes;
1417
+ if (tags !== void 0) input.tags = tags;
1418
+ if (Object.keys(input).length === 0) {
1419
+ return errorResult(
1420
+ "No updates specified \u2014 provide at least one of name/status/riskProfile/aum/currency/primaryContactName/primaryContactEmail/notes/tags"
1421
+ );
1422
+ }
1423
+ const data = await ctx.gql.workspace.request(UPDATE_HOUSEHOLD, { id, input });
1424
+ return jsonResult(data.braUpdateHousehold);
1425
+ } catch (error) {
1426
+ ctx.logger.error("brainyrich households_update failed", { error: String(error) });
1427
+ return errorResult(
1428
+ `Failed to update household: ${error instanceof Error ? error.message : String(error)}`
1429
+ );
1430
+ }
1431
+ }
1432
+ );
1433
+ server.registerTool(
1434
+ track(mcpToolName(ctx.productName, "households_archive")),
1435
+ {
1436
+ title: "Archive Household",
1437
+ description: "Archive (soft-delete) a household by ID. Returns true on success.",
1438
+ inputSchema: { id: z.string().describe("Household ID") }
1439
+ },
1440
+ async ({ id }) => {
1441
+ try {
1442
+ const data = await ctx.gql.workspace.request(
1443
+ ARCHIVE_HOUSEHOLD,
1444
+ { id }
1445
+ );
1446
+ return jsonResult({ archived: data.braArchiveHousehold, id });
1447
+ } catch (error) {
1448
+ ctx.logger.error("brainyrich households_archive failed", { error: String(error) });
1449
+ return errorResult(
1450
+ `Failed to archive household: ${error instanceof Error ? error.message : String(error)}`
1451
+ );
1452
+ }
1453
+ }
1454
+ );
1455
+ }
1456
+ var AllocationSchema = z.object({
1457
+ assetClass: AssetClassEnum.describe("Asset class for this allocation slice"),
1458
+ targetPct: z.number().describe("Target percentage for this asset class")
1459
+ }).describe("A single model-portfolio asset-class allocation");
1460
+ function registerModelPortfolioTools(server, ctx, track) {
1461
+ server.registerTool(
1462
+ track(mcpToolName(ctx.productName, "model_portfolios_get")),
1463
+ {
1464
+ title: "Get Model Portfolio",
1465
+ description: "Fetch a single model portfolio by ID, including its asset-class allocations.",
1466
+ inputSchema: { id: z.string().describe("Model portfolio ID") }
1467
+ },
1468
+ async ({ id }) => {
1469
+ try {
1470
+ const data = await ctx.gql.workspace.request(GET_MODEL_PORTFOLIO, { id });
1471
+ if (!data.braModelPortfolio) return errorResult(`Model portfolio ${id} not found`);
1472
+ return jsonResult(data.braModelPortfolio);
1473
+ } catch (error) {
1474
+ ctx.logger.error("brainyrich model_portfolios_get failed", { error: String(error) });
1475
+ return errorResult(
1476
+ `Failed to get model portfolio: ${error instanceof Error ? error.message : String(error)}`
1477
+ );
1478
+ }
1479
+ }
1480
+ );
1481
+ server.registerTool(
1482
+ track(mcpToolName(ctx.productName, "model_portfolios_list")),
1483
+ {
1484
+ title: "List Model Portfolios",
1485
+ description: "List all model portfolios in the bound workspace, each with its allocations.",
1486
+ inputSchema: {}
1487
+ },
1488
+ async () => {
1489
+ try {
1490
+ const data = await ctx.gql.workspace.request(LIST_MODEL_PORTFOLIOS, {});
1491
+ return jsonResult(data.braListModelPortfolios);
1492
+ } catch (error) {
1493
+ ctx.logger.error("brainyrich model_portfolios_list failed", { error: String(error) });
1494
+ return errorResult(
1495
+ `Failed to list model portfolios: ${error instanceof Error ? error.message : String(error)}`
1496
+ );
1497
+ }
1498
+ }
1499
+ );
1500
+ server.registerTool(
1501
+ track(mcpToolName(ctx.productName, "model_portfolios_create")),
1502
+ {
1503
+ title: "Create Model Portfolio",
1504
+ description: "Create a model portfolio (allocation template). `name` is required; `riskProfile` defaults to BALANCED. Provide allocations as asset-class/target-percentage slices.",
1505
+ inputSchema: {
1506
+ name: z.string().min(1).describe("Model portfolio name"),
1507
+ description: z.string().optional().describe("Description"),
1508
+ baseCurrency: z.string().optional().describe("Base currency code"),
1509
+ riskProfile: RiskProfileEnum.optional().describe("Risk profile (default BALANCED)"),
1510
+ allocations: z.array(AllocationSchema).optional().describe("Asset-class allocation slices (target percentages)")
1511
+ }
1512
+ },
1513
+ async ({ name, description, baseCurrency, riskProfile, allocations }) => {
1514
+ try {
1515
+ const input = { name };
1516
+ if (description !== void 0) input.description = description;
1517
+ if (baseCurrency !== void 0) input.baseCurrency = baseCurrency;
1518
+ if (riskProfile !== void 0) input.riskProfile = riskProfile;
1519
+ if (allocations !== void 0) input.allocations = allocations;
1520
+ const data = await ctx.gql.workspace.request(CREATE_MODEL_PORTFOLIO, { input });
1521
+ return jsonResult(data.braCreateModelPortfolio);
1522
+ } catch (error) {
1523
+ ctx.logger.error("brainyrich model_portfolios_create failed", { error: String(error) });
1524
+ return errorResult(
1525
+ `Failed to create model portfolio: ${error instanceof Error ? error.message : String(error)}`
1526
+ );
1527
+ }
1528
+ }
1529
+ );
1530
+ }
1531
+ function registerRebalanceTools(server, ctx, track) {
1532
+ server.registerTool(
1533
+ track(mcpToolName(ctx.productName, "rebalances_list")),
1534
+ {
1535
+ title: "List Rebalance Proposals",
1536
+ description: "List rebalance proposals, optionally filtered by household, portfolio, and/or status.",
1537
+ inputSchema: {
1538
+ householdId: z.string().optional().describe("Filter by household ID"),
1539
+ portfolioId: z.string().optional().describe("Filter by portfolio ID"),
1540
+ status: RebalanceStatusEnum.optional().describe("Filter by proposal status")
1541
+ }
1542
+ },
1543
+ async ({ householdId, portfolioId, status }) => {
1544
+ try {
1545
+ const data = await ctx.gql.workspace.request(LIST_REBALANCE_PROPOSALS, { householdId, portfolioId, status });
1546
+ return jsonResult(data.braListRebalanceProposals);
1547
+ } catch (error) {
1548
+ ctx.logger.error("brainyrich rebalances_list failed", { error: String(error) });
1549
+ return errorResult(
1550
+ `Failed to list rebalance proposals: ${error instanceof Error ? error.message : String(error)}`
1551
+ );
1552
+ }
1553
+ }
1554
+ );
1555
+ server.registerTool(
1556
+ track(mcpToolName(ctx.productName, "rebalances_create")),
1557
+ {
1558
+ title: "Create Rebalance Proposal",
1559
+ description: "Create a rebalance proposal for a portfolio. `portfolioId` is required; `trades` is a JSON array of proposed trades.",
1560
+ inputSchema: {
1561
+ portfolioId: z.string().min(1).describe("Portfolio to rebalance"),
1562
+ householdId: z.string().optional().describe("Owning household ID"),
1563
+ driftPct: z.number().optional().describe("Current drift percentage"),
1564
+ rationale: z.string().optional().describe("Rationale / notes for the proposal"),
1565
+ trades: z.union([JsonObject, z.array(z.unknown())]).optional().describe("Proposed trades JSON (array of buy/sell instructions)")
1566
+ }
1567
+ },
1568
+ async ({ portfolioId, householdId, driftPct, rationale, trades }) => {
1569
+ try {
1570
+ const input = { portfolioId };
1571
+ if (householdId !== void 0) input.householdId = householdId;
1572
+ if (driftPct !== void 0) input.driftPct = driftPct;
1573
+ if (rationale !== void 0) input.rationale = rationale;
1574
+ if (trades !== void 0) input.trades = trades;
1575
+ const data = await ctx.gql.workspace.request(CREATE_REBALANCE_PROPOSAL, { input });
1576
+ return jsonResult(data.braCreateRebalanceProposal);
1577
+ } catch (error) {
1578
+ ctx.logger.error("brainyrich rebalances_create failed", { error: String(error) });
1579
+ return errorResult(
1580
+ `Failed to create rebalance proposal: ${error instanceof Error ? error.message : String(error)}`
1581
+ );
1582
+ }
1583
+ }
1584
+ );
1585
+ server.registerTool(
1586
+ track(mcpToolName(ctx.productName, "rebalances_transition")),
1587
+ {
1588
+ title: "Transition Rebalance Proposal",
1589
+ description: "Advance a rebalance proposal through its review lifecycle (validated transitions: DRAFT \u2192 PROPOSED \u2192 APPROVED \u2192 EXECUTED, or REJECTED/CANCELLED).",
1590
+ inputSchema: {
1591
+ id: z.string().describe("Rebalance proposal ID"),
1592
+ status: RebalanceStatusEnum.describe("Target status")
1593
+ }
1594
+ },
1595
+ async ({ id, status }) => {
1596
+ try {
1597
+ const data = await ctx.gql.workspace.request(TRANSITION_REBALANCE_PROPOSAL, { id, status });
1598
+ return jsonResult(data.braTransitionRebalanceProposal);
1599
+ } catch (error) {
1600
+ ctx.logger.error("brainyrich rebalances_transition failed", { error: String(error) });
1601
+ return errorResult(
1602
+ `Failed to transition rebalance proposal: ${error instanceof Error ? error.message : String(error)}`
1603
+ );
1604
+ }
1605
+ }
1606
+ );
1607
+ }
1608
+ function registerInsightTools(server, ctx, track) {
1609
+ server.registerTool(
1610
+ track(mcpToolName(ctx.productName, "insights_get")),
1611
+ {
1612
+ title: "Get Insight",
1613
+ description: "Fetch a single proactive insight by ID.",
1614
+ inputSchema: { id: z.string().describe("Insight ID") }
1615
+ },
1616
+ async ({ id }) => {
1617
+ try {
1618
+ const data = await ctx.gql.workspace.request(GET_INSIGHT, { id });
1619
+ if (!data.braInsight) return errorResult(`Insight ${id} not found`);
1620
+ return jsonResult(data.braInsight);
1621
+ } catch (error) {
1622
+ ctx.logger.error("brainyrich insights_get failed", { error: String(error) });
1623
+ return errorResult(
1624
+ `Failed to get insight: ${error instanceof Error ? error.message : String(error)}`
1625
+ );
1626
+ }
1627
+ }
1628
+ );
1629
+ server.registerTool(
1630
+ track(mcpToolName(ctx.productName, "insights_list")),
1631
+ {
1632
+ title: "List Insights",
1633
+ description: "List proactive insights with optional severity/status/subject filters and pagination.",
1634
+ inputSchema: {
1635
+ severity: InsightSeverityEnum.optional().describe("Filter by severity"),
1636
+ status: InsightStatusEnum.optional().describe("Filter by triage status"),
1637
+ subjectId: z.string().optional().describe("Filter by subject entity ID"),
1638
+ subjectType: z.string().optional().describe('Filter by subject entity type (e.g. "portfolio", "household", "goal")'),
1639
+ limit: z.number().int().optional().describe("Page size (default 50)"),
1640
+ offset: z.number().int().optional().describe("Pagination offset (default 0)")
1641
+ }
1642
+ },
1643
+ async ({ severity, status, subjectId, subjectType, limit, offset }) => {
1644
+ try {
1645
+ const data = await ctx.gql.workspace.request(LIST_INSIGHTS, {
1646
+ severity,
1647
+ status,
1648
+ subjectId,
1649
+ subjectType,
1650
+ pagination: { limit: limit ?? 50, offset: offset ?? 0 }
1651
+ });
1652
+ return jsonResult(data.braListInsights);
1653
+ } catch (error) {
1654
+ ctx.logger.error("brainyrich insights_list failed", { error: String(error) });
1655
+ return errorResult(
1656
+ `Failed to list insights: ${error instanceof Error ? error.message : String(error)}`
1657
+ );
1658
+ }
1659
+ }
1660
+ );
1661
+ server.registerTool(
1662
+ track(mcpToolName(ctx.productName, "insights_create")),
1663
+ {
1664
+ title: "Create Insight",
1665
+ description: "Create a proactive insight/signal about a subject. `subjectId`, `subjectType`, `title`, and `type` are required; `severity` defaults to MEDIUM.",
1666
+ inputSchema: {
1667
+ subjectId: z.string().min(1).describe("Subject entity ID the insight is about"),
1668
+ subjectType: z.string().min(1).describe('Subject entity type (e.g. "portfolio", "goal")'),
1669
+ title: z.string().min(1).describe("Insight title"),
1670
+ type: InsightTypeEnum.describe("Insight signal type"),
1671
+ severity: InsightSeverityEnum.optional().describe("Severity (default MEDIUM)"),
1672
+ detail: z.string().optional().describe("Detailed explanation"),
1673
+ ownerId: z.string().optional().describe("Owning user ID (defaults to the caller)"),
1674
+ metrics: JsonObject.optional().describe("Supporting metrics JSON")
1675
+ }
1676
+ },
1677
+ async ({ subjectId, subjectType, title, type, severity, detail, ownerId, metrics }) => {
1678
+ try {
1679
+ const input = { subjectId, subjectType, title, type };
1680
+ if (severity !== void 0) input.severity = severity;
1681
+ if (detail !== void 0) input.detail = detail;
1682
+ if (ownerId !== void 0) input.ownerId = ownerId;
1683
+ if (metrics !== void 0) input.metrics = metrics;
1684
+ const data = await ctx.gql.workspace.request(CREATE_INSIGHT, { input });
1685
+ return jsonResult(data.braCreateInsight);
1686
+ } catch (error) {
1687
+ ctx.logger.error("brainyrich insights_create failed", { error: String(error) });
1688
+ return errorResult(
1689
+ `Failed to create insight: ${error instanceof Error ? error.message : String(error)}`
1690
+ );
1691
+ }
1692
+ }
1693
+ );
1694
+ server.registerTool(
1695
+ track(mcpToolName(ctx.productName, "insights_acknowledge")),
1696
+ {
1697
+ title: "Acknowledge Insight",
1698
+ description: "Acknowledge a proactive insight by ID (marks it ACKNOWLEDGED).",
1699
+ inputSchema: { id: z.string().describe("Insight ID") }
1700
+ },
1701
+ async ({ id }) => {
1702
+ try {
1703
+ const data = await ctx.gql.workspace.request(ACKNOWLEDGE_INSIGHT, { id });
1704
+ return jsonResult(data.braAcknowledgeInsight);
1705
+ } catch (error) {
1706
+ ctx.logger.error("brainyrich insights_acknowledge failed", { error: String(error) });
1707
+ return errorResult(
1708
+ `Failed to acknowledge insight: ${error instanceof Error ? error.message : String(error)}`
1709
+ );
1710
+ }
1711
+ }
1712
+ );
1713
+ server.registerTool(
1714
+ track(mcpToolName(ctx.productName, "insights_resolve")),
1715
+ {
1716
+ title: "Resolve Insight",
1717
+ description: "Resolve a proactive insight by ID (marks it RESOLVED and stamps resolvedAt).",
1718
+ inputSchema: { id: z.string().describe("Insight ID") }
1719
+ },
1720
+ async ({ id }) => {
1721
+ try {
1722
+ const data = await ctx.gql.workspace.request(RESOLVE_INSIGHT, { id });
1723
+ return jsonResult(data.braResolveInsight);
1724
+ } catch (error) {
1725
+ ctx.logger.error("brainyrich insights_resolve failed", { error: String(error) });
1726
+ return errorResult(
1727
+ `Failed to resolve insight: ${error instanceof Error ? error.message : String(error)}`
1728
+ );
1729
+ }
1730
+ }
1731
+ );
1732
+ server.registerTool(
1733
+ track(mcpToolName(ctx.productName, "insights_dismiss")),
1734
+ {
1735
+ title: "Dismiss Insight",
1736
+ description: "Dismiss a proactive insight by ID (marks it DISMISSED).",
1737
+ inputSchema: { id: z.string().describe("Insight ID") }
1738
+ },
1739
+ async ({ id }) => {
1740
+ try {
1741
+ const data = await ctx.gql.workspace.request(DISMISS_INSIGHT, { id });
1742
+ return jsonResult(data.braDismissInsight);
1743
+ } catch (error) {
1744
+ ctx.logger.error("brainyrich insights_dismiss failed", { error: String(error) });
1745
+ return errorResult(
1746
+ `Failed to dismiss insight: ${error instanceof Error ? error.message : String(error)}`
1747
+ );
1748
+ }
1749
+ }
1750
+ );
1751
+ }
1752
+ function registerAnalyticsTools(server, ctx, track) {
1753
+ server.registerTool(
1754
+ track(mcpToolName(ctx.productName, "analytics_net_worth_summary")),
1755
+ {
1756
+ title: "Net-Worth Summary",
1757
+ description: "Aggregate the caller's (or a given owner's) accounts + holdings into a single net-worth summary, broken out by asset class.",
1758
+ inputSchema: {
1759
+ ownerId: z.string().optional().describe("Owner user ID to summarize (defaults to the calling actor)")
1760
+ }
1761
+ },
1762
+ async ({ ownerId }) => {
1763
+ try {
1764
+ const data = await ctx.gql.workspace.request(NET_WORTH_SUMMARY, { ownerId });
1765
+ return jsonResult(data.braNetWorthSummary);
1766
+ } catch (error) {
1767
+ ctx.logger.error("brainyrich analytics_net_worth failed", { error: String(error) });
1768
+ return errorResult(
1769
+ `Failed to get net-worth summary: ${error instanceof Error ? error.message : String(error)}`
1770
+ );
1771
+ }
1772
+ }
1773
+ );
1774
+ server.registerTool(
1775
+ track(mcpToolName(ctx.productName, "system_health")),
1776
+ {
1777
+ title: "Brainyrich Health Check",
1778
+ description: "Public liveness/identity probe for the Brainyrich subgraph (braHealth). Requires no authentication.",
1779
+ inputSchema: {}
1780
+ },
1781
+ async () => {
1782
+ try {
1783
+ const data = await ctx.gql.workspace.request(HEALTH, {});
1784
+ return jsonResult({ ok: true, status: data.braHealth });
1785
+ } catch (error) {
1786
+ ctx.logger.error("brainyrich health failed", { error: String(error) });
1787
+ return errorResult(
1788
+ `Brainyrich health check failed: ${error instanceof Error ? error.message : String(error)}`
1789
+ );
1790
+ }
1791
+ }
1792
+ );
1793
+ }
1794
+
1795
+ // src/modules/brainyrich/resources.ts
1796
+ function registerBrainyrichResources(server, ctx, track) {
1797
+ const schemaUri = track(mcpResourceUri(ctx.productName, "schema"));
1798
+ const netWorthUri = track(mcpResourceUri(ctx.productName, "net-worth"));
1799
+ const householdsUri = track(mcpResourceUri(ctx.productName, "households"));
1800
+ const insightsUri = track(mcpResourceUri(ctx.productName, "insights"));
1801
+ server.registerResource(
1802
+ "brainyrich-schema",
1803
+ schemaUri,
1804
+ {
1805
+ title: "Brainyrich Domain Schema",
1806
+ description: "Overview of the Brainyrich wealth-management domain \u2014 entities, enums, and the per-entity tool surface.",
1807
+ mimeType: "text/plain"
1808
+ },
1809
+ async () => {
1810
+ ctx.logger.debug(`Resource ${schemaUri} requested`);
1811
+ const schema = `# Brainyrich Domain (Overview)
1812
+
1813
+ Wealth-management platform: accounts, portfolios, holdings, transactions,
1814
+ goals, model portfolios, rebalancing, households, and proactive insights.
1815
+
1816
+ ## Entities
1817
+ - BraAccount \u2014 a financial account (holdings, transactions)
1818
+ - BraHolding \u2014 a position within an account/portfolio (derived marketValue)
1819
+ - BraTransaction \u2014 an account-ledger entry
1820
+ - BraPortfolio \u2014 an investment portfolio (holdings, target allocation)
1821
+ - BraModelPortfolio (+BraModelAllocation) \u2014 an allocation template
1822
+ - BraGoal (+BraGoalFunding) \u2014 a financial goal + its funding links + projection
1823
+ - BraHousehold \u2014 an advisory household (CRM)
1824
+ - BraRebalanceProposal \u2014 a drift-correction proposal with a review lifecycle
1825
+ - BraInsight \u2014 a proactive signal with a triage lifecycle
1826
+
1827
+ ## Enums
1828
+ - BraAccountStatus: ACTIVE, ARCHIVED, CLOSED
1829
+ - BraAccountType: ALTERNATIVE, BANK, BROKERAGE, CASH, CRYPTO, LIABILITY, REAL_ESTATE, RETIREMENT
1830
+ - BraAssetClass: ALTERNATIVE, CASH, COMMODITY, CRYPTO, EQUITY, FIXED_INCOME, REAL_ESTATE
1831
+ - BraGoalStatus: ABANDONED, ACHIEVED, ACTIVE, ON_HOLD
1832
+ - BraGoalType: CUSTOM, EDUCATION, EMERGENCY_FUND, HOME, MAJOR_PURCHASE, RETIREMENT, WEALTH_GROWTH
1833
+ - BraGoalProjection: AHEAD, AT_RISK, BEHIND, ON_TRACK
1834
+ - BraHouseholdStatus: ACTIVE, INACTIVE, ONBOARDING, PROSPECT
1835
+ - BraInsightSeverity: CRITICAL, HIGH, INFO, LOW, MEDIUM
1836
+ - BraInsightStatus: ACKNOWLEDGED, DISMISSED, OPEN, RESOLVED
1837
+ - BraInsightType: CASH_DRAG, CONCENTRATION_RISK, DRIFT, FEE_ANOMALY, GOAL_AT_RISK, REBALANCE_DUE, TAX_OPPORTUNITY
1838
+ - BraPortfolioStatus: ACTIVE, ARCHIVED, DRAFT
1839
+ - BraRebalanceStatus: APPROVED, CANCELLED, DRAFT, EXECUTED, PROPOSED, REJECTED
1840
+ - BraRiskProfile: AGGRESSIVE, BALANCED, CONSERVATIVE, GROWTH, MODERATE
1841
+ - BraTransactionType: BUY, DEPOSIT, DIVIDEND, FEE, INTEREST, SELL, TRANSFER, WITHDRAWAL
1842
+
1843
+ ## Tool groups
1844
+ accounts, portfolios, holdings, transactions, goals (+fundings), households,
1845
+ model_portfolios, rebalance, insights, analytics. All run against the
1846
+ WORKSPACE gateway (workspace-scoped) except the public health probe.
1847
+ `;
1848
+ return {
1849
+ contents: [{ uri: schemaUri, text: schema, mimeType: "text/plain" }]
1850
+ };
1851
+ }
1852
+ );
1853
+ server.registerResource(
1854
+ "brainyrich-net-worth",
1855
+ netWorthUri,
1856
+ {
1857
+ title: "Brainyrich Net-Worth Summary",
1858
+ description: "Live net-worth roll-up for the caller, broken out by asset class.",
1859
+ mimeType: "application/json"
1860
+ },
1861
+ async () => {
1862
+ ctx.logger.debug(`Resource ${netWorthUri} requested`);
1863
+ try {
1864
+ const data = await ctx.gql.workspace.request(
1865
+ NET_WORTH_SUMMARY,
1866
+ {}
1867
+ );
1868
+ return {
1869
+ contents: [
1870
+ {
1871
+ uri: netWorthUri,
1872
+ text: JSON.stringify(data.braNetWorthSummary, null, 2),
1873
+ mimeType: "application/json"
1874
+ }
1875
+ ]
1876
+ };
1877
+ } catch (error) {
1878
+ return {
1879
+ contents: [
1880
+ {
1881
+ uri: netWorthUri,
1882
+ text: JSON.stringify({
1883
+ error: error instanceof Error ? error.message : String(error)
1884
+ }),
1885
+ mimeType: "application/json"
1886
+ }
1887
+ ]
1888
+ };
1889
+ }
1890
+ }
1891
+ );
1892
+ server.registerResource(
1893
+ "brainyrich-households",
1894
+ householdsUri,
1895
+ {
1896
+ title: "Brainyrich Household Book",
1897
+ description: "The first 50 advisory households in the bound workspace.",
1898
+ mimeType: "application/json"
1899
+ },
1900
+ async () => {
1901
+ ctx.logger.debug(`Resource ${householdsUri} requested`);
1902
+ try {
1903
+ const data = await ctx.gql.workspace.request(
1904
+ LIST_HOUSEHOLDS,
1905
+ { pagination: { limit: 50, offset: 0 } }
1906
+ );
1907
+ return {
1908
+ contents: [
1909
+ {
1910
+ uri: householdsUri,
1911
+ text: JSON.stringify(data.braListHouseholds, null, 2),
1912
+ mimeType: "application/json"
1913
+ }
1914
+ ]
1915
+ };
1916
+ } catch (error) {
1917
+ return {
1918
+ contents: [
1919
+ {
1920
+ uri: householdsUri,
1921
+ text: JSON.stringify({
1922
+ error: error instanceof Error ? error.message : String(error)
1923
+ }),
1924
+ mimeType: "application/json"
1925
+ }
1926
+ ]
1927
+ };
1928
+ }
1929
+ }
1930
+ );
1931
+ server.registerResource(
1932
+ "brainyrich-insights",
1933
+ insightsUri,
1934
+ {
1935
+ title: "Brainyrich Open Insights",
1936
+ description: "The first 50 OPEN proactive insights in the bound workspace.",
1937
+ mimeType: "application/json"
1938
+ },
1939
+ async () => {
1940
+ ctx.logger.debug(`Resource ${insightsUri} requested`);
1941
+ try {
1942
+ const data = await ctx.gql.workspace.request(LIST_INSIGHTS, {
1943
+ status: "OPEN",
1944
+ pagination: { limit: 50, offset: 0 }
1945
+ });
1946
+ return {
1947
+ contents: [
1948
+ {
1949
+ uri: insightsUri,
1950
+ text: JSON.stringify(data.braListInsights, null, 2),
1951
+ mimeType: "application/json"
1952
+ }
1953
+ ]
1954
+ };
1955
+ } catch (error) {
1956
+ return {
1957
+ contents: [
1958
+ {
1959
+ uri: insightsUri,
1960
+ text: JSON.stringify({
1961
+ error: error instanceof Error ? error.message : String(error)
1962
+ }),
1963
+ mimeType: "application/json"
1964
+ }
1965
+ ]
1966
+ };
1967
+ }
1968
+ }
1969
+ );
1970
+ }
1971
+ function registerBrainyrichPrompts(server, ctx, track) {
1972
+ const p = ctx.productName;
1973
+ const onboardingName = track(mcpPromptName(p, "household-onboarding"));
1974
+ const reviewName = track(mcpPromptName(p, "portfolio-review"));
1975
+ server.registerPrompt(
1976
+ onboardingName,
1977
+ {
1978
+ title: "Household Onboarding",
1979
+ description: "Guide for onboarding a new Brainyrich advisory household end to end: create the household, open accounts, set goals, and link funding.",
1980
+ argsSchema: {
1981
+ name: z.string().optional().describe("Household name"),
1982
+ riskProfile: z.string().optional().describe("Risk profile")
1983
+ }
1984
+ },
1985
+ ({ name, riskProfile }) => {
1986
+ ctx.logger.debug(`Prompt ${onboardingName} called`, { name, riskProfile });
1987
+ const nameNote = name ? `Use the household name "${name}".` : "Ask the user for the household name.";
1988
+ const riskNote = riskProfile ? `Use riskProfile ${riskProfile}.` : "Help the user pick a BraRiskProfile (CONSERVATIVE/BALANCED/MODERATE/GROWTH/AGGRESSIVE).";
1989
+ return {
1990
+ messages: [
1991
+ {
1992
+ role: "user",
1993
+ content: {
1994
+ type: "text",
1995
+ text: `Help me onboard a new Brainyrich advisory household.
1996
+
1997
+ 1. **Create Household**: Use ${p}_households_create. ${nameNote} ${riskNote} Optionally set primaryContactName/primaryContactEmail.
1998
+ 2. **Open Accounts**: Use ${p}_accounts_create for each account \u2014 set type (BANK/BROKERAGE/RETIREMENT/...), currency, and householdId from step 1.
1999
+ 3. **Add Holdings**: Use ${p}_holdings_create against each brokerage/retirement account (symbol, assetClass, quantity, lastPrice).
2000
+ 4. **Set Goals**: Use ${p}_goals_create (name, targetAmount, targetDate, type) for each objective, then ${p}_goals_link_funding to attach funding accounts.
2001
+ 5. **Net Worth**: Use ${p}_analytics_net_worth_summary to confirm the household's roll-up.
2002
+
2003
+ Show results at each step and confirm before moving on.`
2004
+ }
2005
+ }
2006
+ ]
2007
+ };
2008
+ }
2009
+ );
2010
+ server.registerPrompt(
2011
+ reviewName,
2012
+ {
2013
+ title: "Portfolio Review",
2014
+ description: "Periodic advisory review workflow: check drift, triage insights, propose a rebalance, and revisit goal projections.",
2015
+ argsSchema: {
2016
+ portfolioId: z.string().optional().describe("Portfolio ID to review")
2017
+ }
2018
+ },
2019
+ ({ portfolioId }) => {
2020
+ ctx.logger.debug(`Prompt ${reviewName} called`, { portfolioId });
2021
+ const portfolioNote = portfolioId ? `Review portfolio "${portfolioId}".` : `First list portfolios with ${p}_portfolios_list and pick one.`;
2022
+ return {
2023
+ messages: [
2024
+ {
2025
+ role: "user",
2026
+ content: {
2027
+ type: "text",
2028
+ text: `Run a periodic Brainyrich portfolio review. ${portfolioNote}
2029
+
2030
+ 1. **Holdings & Drift**: Use ${p}_portfolios_get and ${p}_holdings_list to inspect current positions vs. the target allocation.
2031
+ 2. **Insights**: Use ${p}_insights_list filtered by the portfolio (subjectType="portfolio", status=OPEN) to surface DRIFT/CONCENTRATION_RISK/REBALANCE_DUE signals; acknowledge with ${p}_insights_acknowledge.
2032
+ 3. **Rebalance**: If drift exceeds the threshold, use ${p}_rebalances_create with proposed trades, then advance it with ${p}_rebalances_transition (DRAFT \u2192 PROPOSED \u2192 APPROVED \u2192 EXECUTED).
2033
+ 4. **Goals**: Use ${p}_goals_list and ${p}_goals_recalc_projection to refresh each goal's projection (AHEAD/ON_TRACK/BEHIND/AT_RISK).
2034
+ 5. **Resolve**: Resolve addressed insights with ${p}_insights_resolve.
2035
+
2036
+ Summarize what needs attention after each step.`
2037
+ }
2038
+ }
2039
+ ]
2040
+ };
2041
+ }
2042
+ );
2043
+ }
2044
+
2045
+ // src/modules/brainyrich/index.ts
2046
+ var brainyrichModule = {
2047
+ id: "brainyrich",
2048
+ version: "0.0.1",
2049
+ displayName: "Brainyrich",
2050
+ dependsOn: ["auth"],
2051
+ capabilities: { tools: {}, resources: {}, prompts: {} },
2052
+ register(server, ctx) {
2053
+ const tools = [];
2054
+ const resources = [];
2055
+ const prompts = [];
2056
+ const trackTool = (n) => {
2057
+ tools.push(n);
2058
+ return n;
2059
+ };
2060
+ const trackResource = (uri) => {
2061
+ resources.push(uri);
2062
+ return uri;
2063
+ };
2064
+ const trackPrompt = (n) => {
2065
+ prompts.push(n);
2066
+ return n;
2067
+ };
2068
+ registerAccountTools(server, ctx, trackTool);
2069
+ registerPortfolioTools(server, ctx, trackTool);
2070
+ registerHoldingTools(server, ctx, trackTool);
2071
+ registerTransactionTools(server, ctx, trackTool);
2072
+ registerGoalTools(server, ctx, trackTool);
2073
+ registerHouseholdTools(server, ctx, trackTool);
2074
+ registerModelPortfolioTools(server, ctx, trackTool);
2075
+ registerRebalanceTools(server, ctx, trackTool);
2076
+ registerInsightTools(server, ctx, trackTool);
2077
+ registerAnalyticsTools(server, ctx, trackTool);
2078
+ registerBrainyrichResources(server, ctx, trackResource);
2079
+ registerBrainyrichPrompts(server, ctx, trackPrompt);
2080
+ return { tools, resources, prompts };
2081
+ }
2082
+ };
2083
+ var brainyrich_default = brainyrichModule;
2084
+
2085
+ export { brainyrich_default as default };
2086
+ //# sourceMappingURL=index.js.map
2087
+ //# sourceMappingURL=index.js.map