Skip to main content
Glama
akutishevsky

LunchMoney MCP Server

create_manual_account

Create a new manually-managed account by specifying its type, name, and current balance. Optionally set subtype, currency, institution, or close date.

Instructions

Create a new manually-managed account. (Formerly create_asset.)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
typeYesPrimary type of the manual account.
subtypeNoOptional subtype (e.g., retirement, checking, savings).
nameYesName of the account. 1-45 characters.
display_nameNoDisplay name. If unset, derived from institution_name and name.
balanceYesCurrent balance of the account.
balance_as_ofNoDate or datetime the balance is as of (ISO 8601).
currencyNoThree-letter lowercase currency code (defaults to primary currency).
institution_nameNoName of the institution holding the account.
closed_onNoDate the account was closed (YYYY-MM-DD). If set, status is forced to closed.
exclude_from_transactionsNoIf true, transactions cannot be assigned to this account.

Implementation Reference

  • The async handler function that executes the create_manual_account tool logic. It destructures the input parameters, builds a request body, calls POST /manual_accounts, and returns the response.
    async ({
        type,
        subtype,
        name,
        display_name,
        balance,
        balance_as_of,
        currency,
        institution_name,
        closed_on,
        exclude_from_transactions,
    }) => {
        try {
            const body: Record<string, unknown> = {
                type,
                name,
                balance: balance.toString(),
            };
    
            if (subtype !== undefined) body.subtype = subtype;
            if (display_name !== undefined)
                body.display_name = display_name;
            if (balance_as_of !== undefined)
                body.balance_as_of = balance_as_of;
            if (currency !== undefined) body.currency = currency;
            if (institution_name !== undefined)
                body.institution_name = institution_name;
            if (closed_on !== undefined) body.closed_on = closed_on;
            if (exclude_from_transactions !== undefined)
                body.exclude_from_transactions = exclude_from_transactions;
    
            const response = await api.post("/manual_accounts", body);
    
            if (!response.ok) {
                return handleApiError(
                    response,
                    "Failed to create manual account",
                );
            }
    
            return dataResponse(await response.json());
        } catch (error) {
            return catchError(error, "Failed to create manual account");
        }
    },
  • The input schema for create_manual_account, defining all parameters: type (enum), subtype, name, display_name, balance, balance_as_of, currency, institution_name, closed_on, exclude_from_transactions.
    inputSchema: {
        type: manualAccountTypeEnum.describe(
            "Primary type of the manual account.",
        ),
        subtype: z
            .string()
            .optional()
            .describe(
                "Optional subtype (e.g., retirement, checking, savings).",
            ),
        name: z
            .string()
            .min(1)
            .max(45)
            .describe("Name of the account. 1-45 characters."),
        display_name: z
            .string()
            .optional()
            .describe(
                "Display name. If unset, derived from institution_name and name.",
            ),
        balance: z.coerce
            .number()
            .describe("Current balance of the account."),
        balance_as_of: z
            .string()
            .optional()
            .describe(
                "Date or datetime the balance is as of (ISO 8601).",
            ),
        currency: z
            .string()
            .length(3)
            .optional()
            .describe(
                "Three-letter lowercase currency code (defaults to primary currency).",
            ),
        institution_name: z
            .string()
            .optional()
            .describe("Name of the institution holding the account."),
        closed_on: z
            .string()
            .optional()
            .describe(
                "Date the account was closed (YYYY-MM-DD). If set, status is forced to closed.",
            ),
        exclude_from_transactions: z
            .boolean()
            .optional()
            .describe(
                "If true, transactions cannot be assigned to this account.",
            ),
    },
  • The registration call: server.registerTool('create_manual_account', ...) which binds the name, description, input schema, annotations, and handler.
    server.registerTool(
        "create_manual_account",
        {
            description:
                "Create a new manually-managed account. (Formerly `create_asset`.)",
            inputSchema: {
                type: manualAccountTypeEnum.describe(
                    "Primary type of the manual account.",
                ),
                subtype: z
                    .string()
                    .optional()
                    .describe(
                        "Optional subtype (e.g., retirement, checking, savings).",
                    ),
                name: z
                    .string()
                    .min(1)
                    .max(45)
                    .describe("Name of the account. 1-45 characters."),
                display_name: z
                    .string()
                    .optional()
                    .describe(
                        "Display name. If unset, derived from institution_name and name.",
                    ),
                balance: z.coerce
                    .number()
                    .describe("Current balance of the account."),
                balance_as_of: z
                    .string()
                    .optional()
                    .describe(
                        "Date or datetime the balance is as of (ISO 8601).",
                    ),
                currency: z
                    .string()
                    .length(3)
                    .optional()
                    .describe(
                        "Three-letter lowercase currency code (defaults to primary currency).",
                    ),
                institution_name: z
                    .string()
                    .optional()
                    .describe("Name of the institution holding the account."),
                closed_on: z
                    .string()
                    .optional()
                    .describe(
                        "Date the account was closed (YYYY-MM-DD). If set, status is forced to closed.",
                    ),
                exclude_from_transactions: z
                    .boolean()
                    .optional()
                    .describe(
                        "If true, transactions cannot be assigned to this account.",
                    ),
            },
            annotations: {
                idempotentHint: false,
            },
        },
        async ({
            type,
            subtype,
            name,
            display_name,
            balance,
            balance_as_of,
            currency,
            institution_name,
            closed_on,
            exclude_from_transactions,
        }) => {
            try {
                const body: Record<string, unknown> = {
                    type,
                    name,
                    balance: balance.toString(),
                };
    
                if (subtype !== undefined) body.subtype = subtype;
                if (display_name !== undefined)
                    body.display_name = display_name;
                if (balance_as_of !== undefined)
                    body.balance_as_of = balance_as_of;
                if (currency !== undefined) body.currency = currency;
                if (institution_name !== undefined)
                    body.institution_name = institution_name;
                if (closed_on !== undefined) body.closed_on = closed_on;
                if (exclude_from_transactions !== undefined)
                    body.exclude_from_transactions = exclude_from_transactions;
    
                const response = await api.post("/manual_accounts", body);
    
                if (!response.ok) {
                    return handleApiError(
                        response,
                        "Failed to create manual account",
                    );
                }
    
                return dataResponse(await response.json());
            } catch (error) {
                return catchError(error, "Failed to create manual account");
            }
        },
    );
  • src/index.ts:12-31 (registration)
    The import and invocation of registerManualAccountTools in the main entry point, which registers all manual account tools including create_manual_account on the MCP server.
    import { registerManualAccountTools } from "./tools/manual-accounts.js";
    import { registerPlaidAccountTools } from "./tools/plaid-accounts.js";
    import { registerCryptoTools } from "./tools/crypto.js";
    import { registerPrompts } from "./prompts.js";
    
    const require = createRequire(import.meta.url);
    const { version } = require("../package.json");
    
    const server = new McpServer({
        name: "lunchmoney-mcp",
        version,
    });
    
    registerUserTools(server);
    registerCategoryTools(server);
    registerTagTools(server);
    registerTransactionTools(server);
    registerRecurringItemsTools(server);
    registerBudgetTools(server);
    registerManualAccountTools(server);
  • The manualAccountTypeEnum helper used in the input schema to validate the 'type' field (cash, credit, cryptocurrency, etc.).
    const manualAccountTypeEnum = z.enum([
        "cash",
        "credit",
        "cryptocurrency",
        "employee compensation",
        "investment",
        "loan",
        "other liability",
        "other asset",
        "real estate",
        "vehicle",
    ]);
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations provide only idempotentHint: false, indicating non-idempotent behavior. The description does not add any behavioral context beyond the action, such as side effects or authorization requirements.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Description is very concise, with two short sentences and no unnecessary information. It efficiently conveys the core purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 10 parameters and no output schema, the description lacks information about return values or expected outcomes. While the schema is well-documented, the description does not round out the context for an agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the input schema already documents all parameters thoroughly. The description does not add additional meaning, meeting the baseline for good schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states the tool creates a manually-managed account, using a specific verb and resource. It distinguishes from sibling tools like update_manual_account and get_all_manual_accounts, and includes the former name for clarity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit guidance on when to use this tool versus alternatives. While it is the only account creation tool among siblings, the description does not mention context or prerequisites for creating manual accounts.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/akutishevsky/lunchmoney-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server