Skip to main content

Overview

FinMCP supports flexible output formatting to optimize data presentation for different use cases. Every tool accepts optional output parameters that control:
  • Response format: JSON (default) or Markdown tables
  • Preview limit: Number of rows to display in Markdown tables

Output Options Schema

All FinMCP tools extend a common output options schema defined in src/index.ts:
const responseFormatSchema = z.enum(["json", "markdown"]).default("json");

const outputOptionsSchema = z.object({
  response_format: responseFormatSchema.optional(),
  preview_limit: z.number().int().min(1).max(200).optional(),
  save: saveSchema,  // Covered in Data Export guide
});
These options are merged into every tool’s schema:
const historySchema = tickerSchema
  .extend({
    period: z.string().optional(),
    interval: z.string().optional(),
    // ... other parameters
  })
  .merge(outputOptionsSchema);  // Adds response_format, preview_limit, save

JSON Format (Default)

When response_format is "json" or omitted, FinMCP returns structured JSON data.

DataFrame Response

DataFrames are serialized with metadata about structure:
{
  "data": {
    "__type__": "dataframe",
    "columns": ["Open", "High", "Low", "Close", "Volume"],
    "index": ["2024-01-01T00:00:00", "2024-01-02T00:00:00"],
    "data": [
      [150.0, 152.5, 149.8, 151.2, 50000000],
      [151.5, 153.0, 150.5, 152.8, 48000000]
    ]
  },
  "saved_path": null
}

Series Response

Series (1-dimensional data) include index and values:
{
  "data": {
    "__type__": "series",
    "name": "Close",
    "index": ["2024-01-01T00:00:00", "2024-01-02T00:00:00"],
    "data": [151.2, 152.8]
  },
  "saved_path": null
}

Object/Array Response

Non-tabular data is returned as plain JSON:
{
  "data": {
    "symbol": "AAPL",
    "longName": "Apple Inc.",
    "marketCap": 3000000000000
  },
  "saved_path": null
}
The saved_path field indicates if data was exported to a file. See Data Export for details.

Markdown Format

When response_format is "markdown", tabular data (DataFrames and Series) is formatted as Markdown tables.

Implementation

The toMarkdown function in src/index.ts:176 handles Markdown conversion:
function toMarkdown(result: unknown, previewLimit: number): string {
  if (isDataFrame(result)) {
    const rows = result.data.slice(0, previewLimit);
    const header = ["index", ...result.columns];
    const separator = header.map(() => "---");
    const body = rows.map((row, idx) => [result.index[idx], ...row]);
    const table = [header, separator, ...body]
      .map((row) => row.map((cell) => String(cell ?? "")).join(" | "))
      .join("\n");
    return table;
  }

  if (isSeries(result)) {
    const rows = result.data.slice(0, previewLimit).map((value, idx) => [result.index[idx], value]);
    const header = ["index", result.name ?? "value"];
    const separator = header.map(() => "---");
    const table = [header, separator, ...rows]
      .map((row) => row.map((cell) => String(cell ?? "")).join(" | "))
      .join("\n");
    return table;
  }

  return `\n\n\`\`\`json\n${stringifyJson(result)}\n\`\`\`\n`;
}

DataFrame Markdown Example

yf_ticker_history(
  ticker="AAPL",
  period="5d",
  response_format="markdown",
  preview_limit=3
)

Series Markdown Example

yf_ticker_dividends(
  ticker="AAPL",
  response_format="markdown",
  preview_limit=5
)

preview_limit Parameter

The preview_limit parameter controls how many rows appear in Markdown tables.
preview_limit
integer
default:"25"
Number of rows to include in Markdown table output. Must be between 1 and 200.

Default Behavior

From src/index.ts:39:
function extractPreviewLimit(options?: OutputOptions): number {
  return options?.preview_limit ?? 25;  // Default: 25 rows
}

Usage Example

// Get 1 year of daily data, but only preview first 10 days
yf_ticker_history(
  ticker="MSFT",
  period="1y",
  response_format="markdown",
  preview_limit=10  // Only show 10 rows in table
)
Use preview_limit with save to view a preview while saving the full dataset to a file.

Non-Tabular Data in Markdown

When response_format="markdown" is used with non-tabular data (objects, arrays), the data is returned as a JSON code block:
return `\n\n\`\`\`json\n${stringifyJson(result)}\n\`\`\`\n`;
Example:
yf_ticker_info(
  ticker="TSLA",
  response_format="markdown"
)

Response Building

The buildResponse function in src/index.ts:201 orchestrates format selection:
async function buildResponse(toolName: string, result: unknown, options?: OutputOptions) {
  let savedPath: string | undefined;
  if (options?.save) {
    savedPath = await saveResult(result, options.save, toolName);
  }

  if ((options?.response_format ?? "json") === "markdown") {
    const preview = toMarkdown(result, extractPreviewLimit(options));
    const savedNote = savedPath ? `\n\nSaved to ${savedPath}` : "";
    return preview + savedNote;
  }

  const payload = {
    data: result,
    saved_path: savedPath ?? null,
  };
  return stringifyJson(payload);
}

Best Practices

Use JSON for Processing

JSON format preserves full data structure and types, ideal for programmatic analysis

Use Markdown for Display

Markdown tables are human-readable and render well in chat interfaces

Combine with Export

Use save parameter to persist full data while getting a preview response

Adjust Preview Limit

Set preview_limit based on context size constraints and readability needs

Build docs developers (and LLMs) love