Note (2026-04-24): After this document was written, legal_entity was renamed to tenant and the old tenant was renamed to organization. Read references to these terms with the pre-rename meaning.

Design: orcha-fpna-list-files MCP Tool

Overview

MCP tool that lists files in a legal entity's FP&A data directory with metadata. Supports filtering by file type, subdirectory navigation, and opt-in Excel summary extraction (sheet names, column headers, row counts).

The file listing is abstracted behind a FileStore protocol so that different storage backends (local filesystem, S3, Google Drive, SFTP) can be plugged in without changing the tool.

Database Change

Add fpna_data_source JSONB column to legal_entity:

ALTER TABLE legal_entity ADD COLUMN fpna_data_source jsonb;

Example values:

Nullable — legal entities without FP&A data simply don't have one configured.

Namespace Structure

com.getorcha.link.mcp.file-store          ;; FileStore protocol + make-file-store multimethod
com.getorcha.link.mcp.file-store.local    ;; LocalFileStore implementation
com.getorcha.link.mcp.tools.fpna.list-files  ;; MCP tool

FileStore Protocol

(defprotocol FileStore
  (list-files [store path opts]
    "Lists files at path (relative to store root).
     opts: {:file-type \"xlsx\"}
     Returns seq of {:name, :path, :size, :modified, :type, :directory?}")

  (read-file [store path]
    "Returns an InputStream for the file at path."))

Construction via multimethod dispatching on :protocol:

(defmulti make-file-store
  "Constructs a FileStore from a data source config map.
   Dispatches on :protocol."
  :protocol)

LocalFileStore

(defmethod make-file-store "file" ...) — uses java.io.File / java.nio.file.

Key behaviors:

Excel Summary

When include_summary is true, the tool opens each .xlsx/.xls file via read-file and uses Docjure to extract:

This runs in the tool handler, not the FileStore — it's a layer above storage.

MCP Tool: orcha-fpna-list-files

Input Schema

{
  "legal_entity_id": "uuid (optional, auto-resolved if single LE)",
  "path": "string (optional, relative subdirectory)",
  "file_type": "string (optional, e.g. 'xlsx', 'csv', 'pdf')",
  "include_summary": "boolean (optional, default false)"
}

Response

{
  "legal_entity_id": "uuid",
  "path": "/",
  "files": [
    {
      "name": "Budget_2026_v2.xlsx",
      "path": "Budget_2026_v2.xlsx",
      "size": 245760,
      "modified": "2026-02-15T10:30:00Z",
      "type": "xlsx",
      "directory": false,
      "summary": {
        "sheets": [
          {"name": "Budget", "columns": ["Monat", "Umsatz", "Kosten"], "row_count": 48},
          {"name": "Forecast", "columns": ["Q1", "Q2"], "row_count": 12}
        ]
      }
    },
    {
      "name": "payroll/",
      "path": "payroll/",
      "type": "directory",
      "directory": true
    }
  ]
}

summary only present when include_summary: true and file is Excel.

  1. Get legal-entity-ids from context (resolved by MCP middleware)
  2. If legal_entity_id provided: validate it's in the allowed set
  3. If not provided and exactly one LE: auto-resolve
  4. If not provided and multiple: error with guidance
  5. Query fpna_data_source column for that LE
  6. Construct FileStore via make-file-store

Error Cases