rspace-online/server/mcp-tools/rflows.ts

81 lines
2.5 KiB
TypeScript

/**
* MCP tools for rFlows (financial modeling & budget allocation).
*
* Tools: rflows_list_flows, rflows_get_budget
*/
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import type { SyncServer } from "../local-first/sync-server";
import { flowsDocId } from "../../modules/rflows/schemas";
import type { FlowsDoc } from "../../modules/rflows/schemas";
import { resolveAccess, accessDeniedResponse } from "./_auth";
export function registerFlowsTools(server: McpServer, syncServer: SyncServer) {
server.tool(
"rflows_list_flows",
"List canvas flows (financial models) in a space",
{
space: z.string().describe("Space slug"),
token: z.string().optional().describe("JWT auth token"),
},
async ({ space, token }) => {
const access = await resolveAccess(token, space, false);
if (!access.allowed) return accessDeniedResponse(access.reason!);
const doc = syncServer.getDoc<FlowsDoc>(flowsDocId(space));
if (!doc) return { content: [{ type: "text", text: JSON.stringify({ error: "No flows data found" }) }] };
const flows = Object.values(doc.canvasFlows || {}).map(f => ({
id: f.id, name: f.name,
nodeCount: f.nodes?.length ?? 0,
createdBy: f.createdBy,
createdAt: f.createdAt, updatedAt: f.updatedAt,
}));
const mortgageCount = Object.keys(doc.mortgagePositions || {}).length;
const reinvestmentCount = Object.keys(doc.reinvestmentPositions || {}).length;
return {
content: [{
type: "text",
text: JSON.stringify({
flows,
activeFlowId: doc.activeFlowId,
mortgagePositionCount: mortgageCount,
reinvestmentPositionCount: reinvestmentCount,
budgetTotalAmount: doc.budgetTotalAmount,
}, null, 2),
}],
};
},
);
server.tool(
"rflows_get_budget",
"Get budget segments and participant allocations",
{
space: z.string().describe("Space slug"),
token: z.string().optional().describe("JWT auth token"),
},
async ({ space, token }) => {
const access = await resolveAccess(token, space, false);
if (!access.allowed) return accessDeniedResponse(access.reason!);
const doc = syncServer.getDoc<FlowsDoc>(flowsDocId(space));
if (!doc) return { content: [{ type: "text", text: JSON.stringify({ error: "No flows data found" }) }] };
return {
content: [{
type: "text",
text: JSON.stringify({
totalAmount: doc.budgetTotalAmount,
segments: doc.budgetSegments,
allocations: doc.budgetAllocations,
}, null, 2),
}],
};
},
);
}