fdic-mcp-server 1.7.6 → 1.8.1
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.
- package/README.md +14 -3
- package/dist/index.js +137 -57
- package/dist/server.js +137 -57
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,6 +32,8 @@ The FDIC BankFind Suite API is public and useful, but it is not packaged for MCP
|
|
|
32
32
|
|
|
33
33
|
## Documentation
|
|
34
34
|
|
|
35
|
+
Public user docs:
|
|
36
|
+
|
|
35
37
|
- [GitHub Pages docs entry point](https://jflamb.github.io/fdic-mcp-server/)
|
|
36
38
|
- [Hosted MCP endpoint](https://bankfind.jflamb.com/mcp)
|
|
37
39
|
- [Local docs home](./docs/index.md)
|
|
@@ -42,9 +44,18 @@ The FDIC BankFind Suite API is public and useful, but it is not packaged for MCP
|
|
|
42
44
|
- [Client setup](./docs/clients.md)
|
|
43
45
|
- [Troubleshooting and FAQ](./docs/troubleshooting.md)
|
|
44
46
|
- [Compatibility matrix](./docs/compatibility-matrix.md)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
|
|
48
|
+
Repo reference docs:
|
|
49
|
+
|
|
50
|
+
- [Reference home](./reference/README.md)
|
|
51
|
+
- [Technical specification](./reference/specification.md)
|
|
52
|
+
- [Architecture](./reference/architecture.md)
|
|
53
|
+
- [Key decisions](./reference/decisions.md)
|
|
54
|
+
- [Cloud Run deployment](./reference/cloud-run-deployment.md)
|
|
55
|
+
- [Plans and design notes](./reference/plans/README.md)
|
|
56
|
+
|
|
57
|
+
Project and release info:
|
|
58
|
+
|
|
48
59
|
- [Release history](https://github.com/jflamb/fdic-mcp-server/releases)
|
|
49
60
|
- [Archived release notes](./docs/release-notes/index.md)
|
|
50
61
|
- [Security policy](./SECURITY.md)
|
package/dist/index.js
CHANGED
|
@@ -32,7 +32,7 @@ var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
|
32
32
|
var import_express2 = __toESM(require("express"));
|
|
33
33
|
|
|
34
34
|
// src/constants.ts
|
|
35
|
-
var VERSION = true ? "1.
|
|
35
|
+
var VERSION = true ? "1.8.1" : process.env.npm_package_version ?? "0.0.0-dev";
|
|
36
36
|
var FDIC_API_BASE_URL = "https://banks.data.fdic.gov/api";
|
|
37
37
|
var CHARACTER_LIMIT = 5e4;
|
|
38
38
|
var DEFAULT_FDIC_MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
|
|
@@ -31850,10 +31850,10 @@ function formatToolError(err) {
|
|
|
31850
31850
|
var import_zod = require("zod");
|
|
31851
31851
|
var CommonQuerySchema = import_zod.z.object({
|
|
31852
31852
|
filters: import_zod.z.string().optional().describe(
|
|
31853
|
-
'ElasticSearch query string
|
|
31853
|
+
'FDIC API filter using ElasticSearch query string syntax. Combine conditions with AND/OR, use quotes for multi-word values, and [min TO max] for ranges (* = unbounded). Common fields: NAME (institution name), STNAME (state name), STALP (two-letter state code), CERT (certificate number), ASSET (total assets in $thousands), ACTIVE (1=active, 0=inactive). Examples: STNAME:"California", ACTIVE:1 AND ASSET:[1000000 TO *], NAME:"Chase"'
|
|
31854
31854
|
),
|
|
31855
31855
|
fields: import_zod.z.string().optional().describe(
|
|
31856
|
-
"Comma-separated list of
|
|
31856
|
+
"Comma-separated list of FDIC field names to return. Leave empty to return all fields. Field names are ALL_CAPS (e.g., NAME, CERT, ASSET, DEP, STALP). Example: NAME,CERT,ASSET,DEP,STALP"
|
|
31857
31857
|
),
|
|
31858
31858
|
limit: import_zod.z.number().int().min(1).max(1e4).default(20).describe("Maximum number of records to return (1-10000, default: 20)"),
|
|
31859
31859
|
offset: import_zod.z.number().int().min(0).default(0).describe("Number of records to skip for pagination (default: 0)"),
|
|
@@ -31889,18 +31889,26 @@ Common filter examples:
|
|
|
31889
31889
|
- Savings institutions: MUTUAL:1
|
|
31890
31890
|
- Recently established: ESTYMD:[2010-01-01 TO *]
|
|
31891
31891
|
|
|
31892
|
+
Charter class codes (BKCLASS):
|
|
31893
|
+
N = National commercial bank (OCC-supervised)
|
|
31894
|
+
SM = State-chartered, Federal Reserve member
|
|
31895
|
+
NM = State-chartered, non-member (FDIC-supervised)
|
|
31896
|
+
SB = Federal savings bank (OCC-supervised)
|
|
31897
|
+
SA = State savings association
|
|
31898
|
+
OI = Insured branch of foreign bank
|
|
31899
|
+
|
|
31892
31900
|
Key returned fields:
|
|
31893
31901
|
- CERT: FDIC Certificate Number (unique ID)
|
|
31894
31902
|
- NAME: Institution name
|
|
31895
|
-
- CITY, STALP, STNAME: Location
|
|
31903
|
+
- CITY, STALP (two-letter state code), STNAME (full state name): Location
|
|
31896
31904
|
- ASSET: Total assets ($thousands)
|
|
31897
31905
|
- DEP: Total deposits ($thousands)
|
|
31898
|
-
- BKCLASS: Charter class code
|
|
31906
|
+
- BKCLASS: Charter class code (see above)
|
|
31899
31907
|
- ACTIVE: 1 if currently active, 0 if inactive
|
|
31900
31908
|
- ROA, ROE: Profitability ratios
|
|
31901
31909
|
- OFFICES: Number of branch offices
|
|
31902
|
-
- ESTYMD: Establishment date
|
|
31903
|
-
- REGAGNT: Primary federal regulator
|
|
31910
|
+
- ESTYMD: Establishment date (YYYY-MM-DD)
|
|
31911
|
+
- REGAGNT: Primary federal regulator (OCC, FRS, FDIC)
|
|
31904
31912
|
|
|
31905
31913
|
Args:
|
|
31906
31914
|
- filters (string, optional): ElasticSearch query filter
|
|
@@ -32023,22 +32031,27 @@ function registerFailureTools(server) {
|
|
|
32023
32031
|
Returns data on bank failures including failure date, resolution type, estimated cost to the FDIC Deposit Insurance Fund, and acquiring institution info.
|
|
32024
32032
|
|
|
32025
32033
|
Common filter examples:
|
|
32026
|
-
- By state: STALP:CA
|
|
32034
|
+
- By state: STALP:CA (two-letter state code)
|
|
32027
32035
|
- By year range: FAILDATE:[2008-01-01 TO 2010-12-31]
|
|
32028
32036
|
- Recent failures: FAILDATE:[2020-01-01 TO *]
|
|
32029
|
-
- By resolution type: RESTYPE:PAYOFF or RESTYPE:
|
|
32037
|
+
- By resolution type: RESTYPE:PAYOFF or RESTYPE:"PURCHASE AND ASSUMPTION"
|
|
32030
32038
|
- Large failures by cost: COST:[100000 TO *] (cost in $thousands)
|
|
32031
32039
|
- By name: NAME:"Washington Mutual"
|
|
32032
32040
|
|
|
32041
|
+
Resolution types (RESTYPE):
|
|
32042
|
+
PAYOFF = depositors paid directly, no acquirer
|
|
32043
|
+
PURCHASE AND ASSUMPTION = acquirer buys assets and assumes deposits
|
|
32044
|
+
PAYOUT = variant of payoff with insured-deposit transfer
|
|
32045
|
+
|
|
32033
32046
|
Key returned fields:
|
|
32034
32047
|
- CERT: FDIC Certificate Number
|
|
32035
32048
|
- NAME: Institution name
|
|
32036
|
-
- CITY, STALP, STNAME: Location
|
|
32049
|
+
- CITY, STALP (two-letter state code), STNAME (full state name): Location
|
|
32037
32050
|
- FAILDATE: Date of failure (YYYY-MM-DD)
|
|
32038
|
-
- SAVR: Savings
|
|
32039
|
-
- RESTYPE: Resolution type (
|
|
32051
|
+
- SAVR: Savings association flag (SA) or bank (BK)
|
|
32052
|
+
- RESTYPE: Resolution type (see above)
|
|
32040
32053
|
- QBFASSET: Total assets at failure ($thousands)
|
|
32041
|
-
- COST: Estimated cost to FDIC
|
|
32054
|
+
- COST: Estimated cost to FDIC Deposit Insurance Fund ($thousands)
|
|
32042
32055
|
|
|
32043
32056
|
Args:
|
|
32044
32057
|
- filters (string, optional): ElasticSearch query filter
|
|
@@ -32158,6 +32171,27 @@ var import_zod2 = require("zod");
|
|
|
32158
32171
|
var CHUNK_SIZE = 25;
|
|
32159
32172
|
var MAX_CONCURRENCY = 4;
|
|
32160
32173
|
var ANALYSIS_TIMEOUT_MS = 9e4;
|
|
32174
|
+
function getDefaultReportDate() {
|
|
32175
|
+
const target = new Date(Date.now() - 90 * 24 * 60 * 60 * 1e3);
|
|
32176
|
+
const year = target.getFullYear();
|
|
32177
|
+
const month = target.getMonth() + 1;
|
|
32178
|
+
if (month >= 10) return `${year}0930`;
|
|
32179
|
+
if (month >= 7) return `${year}0630`;
|
|
32180
|
+
if (month >= 4) return `${year}0331`;
|
|
32181
|
+
return `${year - 1}1231`;
|
|
32182
|
+
}
|
|
32183
|
+
function getReportDateOneYearPrior(repdte) {
|
|
32184
|
+
const year = Number.parseInt(repdte.slice(0, 4), 10);
|
|
32185
|
+
return `${year - 1}${repdte.slice(4)}`;
|
|
32186
|
+
}
|
|
32187
|
+
var VALID_QUARTER_END_SUFFIXES = /* @__PURE__ */ new Set(["0331", "0630", "0930", "1231"]);
|
|
32188
|
+
function validateQuarterEndDate(repdte, label) {
|
|
32189
|
+
const suffix = repdte.slice(4);
|
|
32190
|
+
if (!VALID_QUARTER_END_SUFFIXES.has(suffix)) {
|
|
32191
|
+
return `${label} "${repdte}" is not a valid quarter-end date. FDIC data is published quarterly \u2014 use a date ending in 0331 (Q1), 0630 (Q2), 0930 (Q3), or 1231 (Q4). Example: ${repdte.slice(0, 4)}1231 for Q4 ${repdte.slice(0, 4)}.`;
|
|
32192
|
+
}
|
|
32193
|
+
return null;
|
|
32194
|
+
}
|
|
32161
32195
|
function asNumber(value) {
|
|
32162
32196
|
return typeof value === "number" ? value : null;
|
|
32163
32197
|
}
|
|
@@ -32226,23 +32260,34 @@ Returns branch/office data including address, city, state, coordinates, branch t
|
|
|
32226
32260
|
|
|
32227
32261
|
Common filter examples:
|
|
32228
32262
|
- All branches of a bank: CERT:3511
|
|
32229
|
-
- By state: STALP:TX
|
|
32263
|
+
- By state: STALP:TX (two-letter state code)
|
|
32230
32264
|
- By city: CITY:"Austin"
|
|
32231
32265
|
- Main offices only: BRNUM:0
|
|
32232
32266
|
- By county: COUNTY:"Travis"
|
|
32233
|
-
- Active branches: ENDEFYMD:[9999-01-01 TO *]
|
|
32234
|
-
- By
|
|
32267
|
+
- Active branches only: ENDEFYMD:[9999-01-01 TO *] (sentinel date 9999-12-31 means still open)
|
|
32268
|
+
- By metro area (CBSA): CBSA_METRO_NAME:"New York-Newark-Jersey City"
|
|
32269
|
+
|
|
32270
|
+
Branch service types (BRSERTYP):
|
|
32271
|
+
11 = Full service brick and mortar
|
|
32272
|
+
12 = Full service retail
|
|
32273
|
+
21 = Limited service administrative
|
|
32274
|
+
22 = Limited service military
|
|
32275
|
+
23 = Limited service drive-through
|
|
32276
|
+
24 = Limited service loan production
|
|
32277
|
+
25 = Limited service consumer/trust
|
|
32278
|
+
26 = Limited service Internet/mobile
|
|
32279
|
+
29 = Limited service other
|
|
32235
32280
|
|
|
32236
32281
|
Key returned fields:
|
|
32237
32282
|
- CERT: FDIC Certificate Number
|
|
32238
32283
|
- UNINAME: Institution name
|
|
32239
32284
|
- NAMEFULL: Full branch name
|
|
32240
|
-
- ADDRESS, CITY, STALP, ZIP: Branch address
|
|
32285
|
+
- ADDRESS, CITY, STALP (two-letter state code), ZIP: Branch address
|
|
32241
32286
|
- COUNTY: County name
|
|
32242
32287
|
- BRNUM: Branch number (0 = main office)
|
|
32243
|
-
- BRSERTYP: Branch service type
|
|
32288
|
+
- BRSERTYP: Branch service type code (see above)
|
|
32244
32289
|
- LATITUDE, LONGITUDE: Geographic coordinates
|
|
32245
|
-
- ESTYMD: Branch established date
|
|
32290
|
+
- ESTYMD: Branch established date (YYYY-MM-DD)
|
|
32246
32291
|
- ENDEFYMD: Branch end date (9999-12-31 if still active)
|
|
32247
32292
|
|
|
32248
32293
|
Args:
|
|
@@ -32323,22 +32368,36 @@ Common filter examples:
|
|
|
32323
32368
|
- History for a specific bank: CERT:3511
|
|
32324
32369
|
- Mergers: TYPE:merger
|
|
32325
32370
|
- Failures: TYPE:failure
|
|
32326
|
-
- Name changes: CHANGECODE:CO
|
|
32371
|
+
- Name changes: CHANGECODE:CO
|
|
32327
32372
|
- By date range: PROCDATE:[2008-01-01 TO 2009-12-31]
|
|
32328
|
-
- By state: PSTALP:CA
|
|
32373
|
+
- By state: PSTALP:CA (two-letter state code)
|
|
32374
|
+
|
|
32375
|
+
Event types (TYPE):
|
|
32376
|
+
merger = institution was merged into another
|
|
32377
|
+
failure = institution failed
|
|
32378
|
+
assistance = received FDIC assistance transaction
|
|
32379
|
+
insurance = insurance-related event (new coverage, termination)
|
|
32380
|
+
|
|
32381
|
+
Common change codes (CHANGECODE):
|
|
32382
|
+
CO = name change
|
|
32383
|
+
CR = charter conversion
|
|
32384
|
+
DC = deposit assumption change
|
|
32385
|
+
MA = merger/acquisition (absorbed by another institution)
|
|
32386
|
+
NI = new institution insured
|
|
32387
|
+
TC = trust company conversion
|
|
32329
32388
|
|
|
32330
32389
|
Key returned fields:
|
|
32331
32390
|
- CERT: FDIC Certificate Number
|
|
32332
32391
|
- INSTNAME: Institution name
|
|
32333
32392
|
- CLASS: Charter class at time of change
|
|
32334
|
-
- PCITY, PSTALP: Location (city, state
|
|
32335
|
-
- PROCDATE: Processing date of the change
|
|
32336
|
-
- EFFDATE: Effective date of the change
|
|
32393
|
+
- PCITY, PSTALP: Location (city, two-letter state code)
|
|
32394
|
+
- PROCDATE: Processing date of the change (YYYY-MM-DD)
|
|
32395
|
+
- EFFDATE: Effective date of the change (YYYY-MM-DD)
|
|
32337
32396
|
- ENDEFYMD: End effective date
|
|
32338
32397
|
- PCERT: Predecessor/successor CERT (for mergers)
|
|
32339
|
-
- TYPE: Type of structural change
|
|
32340
|
-
- CHANGECODE: Code for type of change
|
|
32341
|
-
- CHANGECODE_DESC:
|
|
32398
|
+
- TYPE: Type of structural change (see above)
|
|
32399
|
+
- CHANGECODE: Code for type of change (see above)
|
|
32400
|
+
- CHANGECODE_DESC: Human-readable description of the change code
|
|
32342
32401
|
- INSDATE: Insurance date
|
|
32343
32402
|
|
|
32344
32403
|
Args:
|
|
@@ -32409,7 +32468,7 @@ var FinancialQuerySchema = CommonQuerySchema.extend({
|
|
|
32409
32468
|
"Filter by FDIC Certificate Number to get financials for a specific institution"
|
|
32410
32469
|
),
|
|
32411
32470
|
repdte: import_zod4.z.string().optional().describe(
|
|
32412
|
-
"Filter by
|
|
32471
|
+
"Filter by Report Date (REPDTE) in YYYYMMDD format. FDIC data is published quarterly on call report dates: March 31, June 30, September 30, and December 31. Example: 20231231 for Q4 2023. If omitted, returns all available dates (sorted most recent first by default)."
|
|
32413
32472
|
)
|
|
32414
32473
|
});
|
|
32415
32474
|
var SummaryQuerySchema = CommonQuerySchema.extend({
|
|
@@ -32434,7 +32493,7 @@ Common filter examples:
|
|
|
32434
32493
|
|
|
32435
32494
|
Key returned fields:
|
|
32436
32495
|
- CERT: FDIC Certificate Number
|
|
32437
|
-
- REPDTE: Report
|
|
32496
|
+
- REPDTE: Report Date \u2014 the last day of the quarterly reporting period (YYYYMMDD)
|
|
32438
32497
|
- ASSET: Total assets ($thousands)
|
|
32439
32498
|
- DEP: Total deposits ($thousands)
|
|
32440
32499
|
- DEPDOM: Domestic deposits ($thousands)
|
|
@@ -32447,7 +32506,7 @@ Key returned fields:
|
|
|
32447
32506
|
|
|
32448
32507
|
Args:
|
|
32449
32508
|
- cert (number, optional): Filter by institution CERT number
|
|
32450
|
-
- repdte (string, optional): Report
|
|
32509
|
+
- repdte (string, optional): Report Date in YYYYMMDD format (quarter-end dates: 0331, 0630, 0930, 1231)
|
|
32451
32510
|
- filters (string, optional): Additional ElasticSearch query filters
|
|
32452
32511
|
- fields (string, optional): Comma-separated field names (the full set has 1,100+ fields)
|
|
32453
32512
|
- limit (number): Records to return (default: 20)
|
|
@@ -32527,7 +32586,7 @@ Key returned fields:
|
|
|
32527
32586
|
- ROA: Return on assets (%)
|
|
32528
32587
|
- ROE: Return on equity (%)
|
|
32529
32588
|
- OFFICES: Number of branch offices
|
|
32530
|
-
- REPDTE: Report
|
|
32589
|
+
- REPDTE: Report Date \u2014 the last day of the reporting period (YYYYMMDD)
|
|
32531
32590
|
|
|
32532
32591
|
Args:
|
|
32533
32592
|
- cert (number, optional): Filter by institution CERT number
|
|
@@ -32692,7 +32751,7 @@ var import_zod6 = require("zod");
|
|
|
32692
32751
|
var DemographicsQuerySchema = CommonQuerySchema.extend({
|
|
32693
32752
|
cert: import_zod6.z.number().int().positive().optional().describe("Filter by FDIC Certificate Number"),
|
|
32694
32753
|
repdte: import_zod6.z.string().optional().describe(
|
|
32695
|
-
"Filter by
|
|
32754
|
+
"Filter by Report Date (REPDTE) in YYYYMMDD format. FDIC data is published quarterly on: March 31, June 30, September 30, and December 31. Example: 20251231 for Q4 2025. If omitted, returns all available dates."
|
|
32696
32755
|
)
|
|
32697
32756
|
});
|
|
32698
32757
|
function registerDemographicsTools(server) {
|
|
@@ -32713,7 +32772,7 @@ Common filter examples:
|
|
|
32713
32772
|
|
|
32714
32773
|
Key returned fields:
|
|
32715
32774
|
- CERT: FDIC Certificate Number
|
|
32716
|
-
- REPDTE: Report
|
|
32775
|
+
- REPDTE: Report Date \u2014 the last day of the quarterly reporting period (YYYYMMDD)
|
|
32717
32776
|
- QTRNO: Quarter number
|
|
32718
32777
|
- OFFTOT: Total offices
|
|
32719
32778
|
- OFFSTATE: Offices in other states
|
|
@@ -32727,7 +32786,7 @@ Key returned fields:
|
|
|
32727
32786
|
|
|
32728
32787
|
Args:
|
|
32729
32788
|
- cert (number, optional): Filter by institution CERT number
|
|
32730
|
-
- repdte (string, optional): Report
|
|
32789
|
+
- repdte (string, optional): Report Date in YYYYMMDD format (quarter-end dates: 0331, 0630, 0930, 1231)
|
|
32731
32790
|
- filters (string, optional): Additional ElasticSearch query filters
|
|
32732
32791
|
- fields (string, optional): Comma-separated field names
|
|
32733
32792
|
- limit (number): Records to return (default: 20)
|
|
@@ -32838,8 +32897,12 @@ var SnapshotAnalysisSchema = import_zod7.z.object({
|
|
|
32838
32897
|
'Additional institution-level filter used when building the comparison set. Example: BKCLASS:N or CITY:"Charlotte"'
|
|
32839
32898
|
),
|
|
32840
32899
|
active_only: import_zod7.z.boolean().default(true).describe("Limit the comparison set to currently active institutions."),
|
|
32841
|
-
start_repdte: import_zod7.z.string().regex(/^\d{8}$/).describe(
|
|
32842
|
-
|
|
32900
|
+
start_repdte: import_zod7.z.string().regex(/^\d{8}$/).optional().describe(
|
|
32901
|
+
"Starting Report Date (REPDTE) in YYYYMMDD format. Must be a quarter-end date: March 31 (0331), June 30 (0630), September 30 (0930), or December 31 (1231). Example: 20210331 for Q1 2021. If omitted, defaults to the same quarter one year before end_repdte."
|
|
32902
|
+
),
|
|
32903
|
+
end_repdte: import_zod7.z.string().regex(/^\d{8}$/).optional().describe(
|
|
32904
|
+
"Ending Report Date (REPDTE) in YYYYMMDD format. Must be a quarter-end date: March 31 (0331), June 30 (0630), September 30 (0930), or December 31 (1231). Must be later than start_repdte. Example: 20251231 for Q4 2025. If omitted, defaults to the most recent quarter-end date with published data (~90-day lag)."
|
|
32905
|
+
),
|
|
32843
32906
|
analysis_mode: AnalysisModeSchema.default("snapshot").describe(
|
|
32844
32907
|
"Use snapshot for two-point comparison or timeseries for quarterly trend analysis across the date range."
|
|
32845
32908
|
),
|
|
@@ -32848,14 +32911,23 @@ var SnapshotAnalysisSchema = import_zod7.z.object({
|
|
|
32848
32911
|
),
|
|
32849
32912
|
limit: import_zod7.z.number().int().min(1).max(100).default(10).describe("Maximum number of ranked comparisons to return."),
|
|
32850
32913
|
sort_by: SortFieldSchema.default("asset_growth").describe(
|
|
32851
|
-
"Comparison field used to rank institutions."
|
|
32914
|
+
"Comparison field used to rank institutions. Valid options: asset_growth, asset_growth_pct, dep_growth, dep_growth_pct, netinc_change, netinc_change_pct, roa_change, roe_change, offices_change, assets_per_office_change, deposits_per_office_change, deposits_to_assets_change."
|
|
32852
32915
|
),
|
|
32853
32916
|
sort_order: import_zod7.z.enum(["ASC", "DESC"]).default("DESC").describe("Sort direction for the ranked comparisons.")
|
|
32854
32917
|
});
|
|
32918
|
+
function resolveSnapshotDefaults(params) {
|
|
32919
|
+
const end_repdte = params.end_repdte ?? getDefaultReportDate();
|
|
32920
|
+
const start_repdte = params.start_repdte ?? getReportDateOneYearPrior(end_repdte);
|
|
32921
|
+
return { ...params, start_repdte, end_repdte };
|
|
32922
|
+
}
|
|
32855
32923
|
function validateSnapshotAnalysisParams(value) {
|
|
32856
32924
|
if (!value.state && (!value.certs || value.certs.length === 0)) {
|
|
32857
32925
|
return "Provide either state or certs.";
|
|
32858
32926
|
}
|
|
32927
|
+
const startDateError = validateQuarterEndDate(value.start_repdte, "start_repdte");
|
|
32928
|
+
if (startDateError) return startDateError;
|
|
32929
|
+
const endDateError = validateQuarterEndDate(value.end_repdte, "end_repdte");
|
|
32930
|
+
if (endDateError) return endDateError;
|
|
32859
32931
|
if (value.start_repdte >= value.end_repdte) {
|
|
32860
32932
|
return "start_repdte must be earlier than end_repdte.";
|
|
32861
32933
|
}
|
|
@@ -33381,12 +33453,12 @@ Good uses:
|
|
|
33381
33453
|
|
|
33382
33454
|
Inputs:
|
|
33383
33455
|
- state or certs: choose a geographic roster or provide a direct comparison set
|
|
33384
|
-
- start_repdte, end_repdte:
|
|
33456
|
+
- start_repdte, end_repdte: Report Dates (REPDTE) in YYYYMMDD format \u2014 must be quarter-end dates (0331, 0630, 0930, 1231)
|
|
33385
33457
|
- analysis_mode: snapshot or timeseries
|
|
33386
33458
|
- institution_filters: optional extra institution filter when building the roster
|
|
33387
33459
|
- active_only: default true
|
|
33388
33460
|
- include_demographics: default true, adds office-count comparisons when available
|
|
33389
|
-
- sort_by: ranking field
|
|
33461
|
+
- sort_by: ranking field (default: asset_growth). All options: asset_growth, asset_growth_pct, dep_growth, dep_growth_pct, netinc_change, netinc_change_pct, roa_change, roe_change, offices_change, assets_per_office_change, deposits_per_office_change, deposits_to_assets_change
|
|
33390
33462
|
- sort_order: ASC or DESC
|
|
33391
33463
|
- limit: maximum ranked results to return
|
|
33392
33464
|
|
|
@@ -33399,19 +33471,20 @@ Returns concise comparison text plus structured deltas, derived metrics, and ins
|
|
|
33399
33471
|
openWorldHint: true
|
|
33400
33472
|
}
|
|
33401
33473
|
},
|
|
33402
|
-
async ({
|
|
33403
|
-
|
|
33404
|
-
|
|
33405
|
-
|
|
33406
|
-
|
|
33407
|
-
|
|
33408
|
-
|
|
33409
|
-
|
|
33410
|
-
|
|
33411
|
-
|
|
33412
|
-
|
|
33413
|
-
|
|
33414
|
-
|
|
33474
|
+
async (rawParams, extra) => {
|
|
33475
|
+
const {
|
|
33476
|
+
state,
|
|
33477
|
+
certs,
|
|
33478
|
+
institution_filters,
|
|
33479
|
+
active_only,
|
|
33480
|
+
start_repdte,
|
|
33481
|
+
end_repdte,
|
|
33482
|
+
analysis_mode,
|
|
33483
|
+
include_demographics,
|
|
33484
|
+
limit,
|
|
33485
|
+
sort_by,
|
|
33486
|
+
sort_order
|
|
33487
|
+
} = resolveSnapshotDefaults(rawParams);
|
|
33415
33488
|
const controller = new AbortController();
|
|
33416
33489
|
const timeoutId = setTimeout(() => controller.abort(), ANALYSIS_TIMEOUT_MS);
|
|
33417
33490
|
const progressToken = extra._meta?.progressToken;
|
|
@@ -33631,7 +33704,7 @@ Returns concise comparison text plus structured deltas, derived metrics, and ins
|
|
|
33631
33704
|
if (controller.signal.aborted) {
|
|
33632
33705
|
return formatToolError(
|
|
33633
33706
|
new Error(
|
|
33634
|
-
`Analysis timed out after ${Math.floor(ANALYSIS_TIMEOUT_MS / 1e3)} seconds.
|
|
33707
|
+
`Analysis timed out after ${Math.floor(ANALYSIS_TIMEOUT_MS / 1e3)} seconds. Try reducing the comparison set: use certs (max 100) instead of a state-wide roster, add institution_filters (e.g., BKCLASS:N), or shorten the date range for timeseries mode.`
|
|
33635
33708
|
)
|
|
33636
33709
|
);
|
|
33637
33710
|
}
|
|
@@ -33792,7 +33865,9 @@ var PeerGroupInputSchema = import_zod8.z.object({
|
|
|
33792
33865
|
cert: import_zod8.z.number().int().positive().optional().describe(
|
|
33793
33866
|
"Subject institution CERT number. When provided, auto-derives peer criteria and ranks this bank against peers."
|
|
33794
33867
|
),
|
|
33795
|
-
repdte: import_zod8.z.string().regex(/^\d{8}$/).describe(
|
|
33868
|
+
repdte: import_zod8.z.string().regex(/^\d{8}$/).optional().describe(
|
|
33869
|
+
"Report Date (REPDTE) in YYYYMMDD format. FDIC data is published quarterly on: March 31, June 30, September 30, and December 31. Example: 20231231 for Q4 2023. If omitted, defaults to the most recent quarter-end date likely to have published data (~90-day lag)."
|
|
33870
|
+
),
|
|
33796
33871
|
asset_min: import_zod8.z.number().positive().optional().describe(
|
|
33797
33872
|
"Minimum total assets ($thousands) for peer selection. Defaults to 50% of subject's report-date assets when cert is provided."
|
|
33798
33873
|
),
|
|
@@ -33948,7 +34023,8 @@ Override precedence: cert derives defaults, then explicit params override them.`
|
|
|
33948
34023
|
openWorldHint: true
|
|
33949
34024
|
}
|
|
33950
34025
|
},
|
|
33951
|
-
async (
|
|
34026
|
+
async (rawParams, extra) => {
|
|
34027
|
+
const params = { ...rawParams, repdte: rawParams.repdte ?? getDefaultReportDate() };
|
|
33952
34028
|
const controller = new AbortController();
|
|
33953
34029
|
const timeoutId = setTimeout(() => controller.abort(), ANALYSIS_TIMEOUT_MS);
|
|
33954
34030
|
const progressToken = extra._meta?.progressToken;
|
|
@@ -33957,6 +34033,10 @@ Override precedence: cert derives defaults, then explicit params override them.`
|
|
|
33957
34033
|
if (validationError) {
|
|
33958
34034
|
return formatToolError(new Error(validationError));
|
|
33959
34035
|
}
|
|
34036
|
+
const dateError = validateQuarterEndDate(params.repdte, "repdte");
|
|
34037
|
+
if (dateError) {
|
|
34038
|
+
return formatToolError(new Error(dateError));
|
|
34039
|
+
}
|
|
33960
34040
|
const extraFieldsError = validateExtraFields(params.extra_fields);
|
|
33961
34041
|
if (extraFieldsError) {
|
|
33962
34042
|
return formatToolError(extraFieldsError);
|
|
@@ -34004,7 +34084,7 @@ Override precedence: cert derives defaults, then explicit params override them.`
|
|
|
34004
34084
|
if (financialRecords.length === 0) {
|
|
34005
34085
|
return formatToolError(
|
|
34006
34086
|
new Error(
|
|
34007
|
-
`No financial data for CERT ${params.cert} at report date ${params.repdte}.
|
|
34087
|
+
`No financial data for CERT ${params.cert} at report date ${params.repdte}. FDIC quarterly data is published ~90 days after each quarter-end (March 31, June 30, September 30, December 31). Try an earlier quarter-end date, or verify the institution was active at that date.`
|
|
34008
34088
|
)
|
|
34009
34089
|
);
|
|
34010
34090
|
}
|
|
@@ -34259,7 +34339,7 @@ Override precedence: cert derives defaults, then explicit params override them.`
|
|
|
34259
34339
|
if (controller.signal.aborted) {
|
|
34260
34340
|
return formatToolError(
|
|
34261
34341
|
new Error(
|
|
34262
|
-
`Peer group analysis timed out after ${Math.floor(ANALYSIS_TIMEOUT_MS / 1e3)} seconds.
|
|
34342
|
+
`Peer group analysis timed out after ${Math.floor(ANALYSIS_TIMEOUT_MS / 1e3)} seconds. Try narrowing the peer group: add a state filter, tighten the asset_min/asset_max range, or specify charter_classes.`
|
|
34263
34343
|
)
|
|
34264
34344
|
);
|
|
34265
34345
|
}
|
package/dist/server.js
CHANGED
|
@@ -46,7 +46,7 @@ var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
|
46
46
|
var import_express2 = __toESM(require("express"));
|
|
47
47
|
|
|
48
48
|
// src/constants.ts
|
|
49
|
-
var VERSION = true ? "1.
|
|
49
|
+
var VERSION = true ? "1.8.1" : process.env.npm_package_version ?? "0.0.0-dev";
|
|
50
50
|
var FDIC_API_BASE_URL = "https://banks.data.fdic.gov/api";
|
|
51
51
|
var CHARACTER_LIMIT = 5e4;
|
|
52
52
|
var DEFAULT_FDIC_MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
|
|
@@ -31864,10 +31864,10 @@ function formatToolError(err) {
|
|
|
31864
31864
|
var import_zod = require("zod");
|
|
31865
31865
|
var CommonQuerySchema = import_zod.z.object({
|
|
31866
31866
|
filters: import_zod.z.string().optional().describe(
|
|
31867
|
-
'ElasticSearch query string
|
|
31867
|
+
'FDIC API filter using ElasticSearch query string syntax. Combine conditions with AND/OR, use quotes for multi-word values, and [min TO max] for ranges (* = unbounded). Common fields: NAME (institution name), STNAME (state name), STALP (two-letter state code), CERT (certificate number), ASSET (total assets in $thousands), ACTIVE (1=active, 0=inactive). Examples: STNAME:"California", ACTIVE:1 AND ASSET:[1000000 TO *], NAME:"Chase"'
|
|
31868
31868
|
),
|
|
31869
31869
|
fields: import_zod.z.string().optional().describe(
|
|
31870
|
-
"Comma-separated list of
|
|
31870
|
+
"Comma-separated list of FDIC field names to return. Leave empty to return all fields. Field names are ALL_CAPS (e.g., NAME, CERT, ASSET, DEP, STALP). Example: NAME,CERT,ASSET,DEP,STALP"
|
|
31871
31871
|
),
|
|
31872
31872
|
limit: import_zod.z.number().int().min(1).max(1e4).default(20).describe("Maximum number of records to return (1-10000, default: 20)"),
|
|
31873
31873
|
offset: import_zod.z.number().int().min(0).default(0).describe("Number of records to skip for pagination (default: 0)"),
|
|
@@ -31903,18 +31903,26 @@ Common filter examples:
|
|
|
31903
31903
|
- Savings institutions: MUTUAL:1
|
|
31904
31904
|
- Recently established: ESTYMD:[2010-01-01 TO *]
|
|
31905
31905
|
|
|
31906
|
+
Charter class codes (BKCLASS):
|
|
31907
|
+
N = National commercial bank (OCC-supervised)
|
|
31908
|
+
SM = State-chartered, Federal Reserve member
|
|
31909
|
+
NM = State-chartered, non-member (FDIC-supervised)
|
|
31910
|
+
SB = Federal savings bank (OCC-supervised)
|
|
31911
|
+
SA = State savings association
|
|
31912
|
+
OI = Insured branch of foreign bank
|
|
31913
|
+
|
|
31906
31914
|
Key returned fields:
|
|
31907
31915
|
- CERT: FDIC Certificate Number (unique ID)
|
|
31908
31916
|
- NAME: Institution name
|
|
31909
|
-
- CITY, STALP, STNAME: Location
|
|
31917
|
+
- CITY, STALP (two-letter state code), STNAME (full state name): Location
|
|
31910
31918
|
- ASSET: Total assets ($thousands)
|
|
31911
31919
|
- DEP: Total deposits ($thousands)
|
|
31912
|
-
- BKCLASS: Charter class code
|
|
31920
|
+
- BKCLASS: Charter class code (see above)
|
|
31913
31921
|
- ACTIVE: 1 if currently active, 0 if inactive
|
|
31914
31922
|
- ROA, ROE: Profitability ratios
|
|
31915
31923
|
- OFFICES: Number of branch offices
|
|
31916
|
-
- ESTYMD: Establishment date
|
|
31917
|
-
- REGAGNT: Primary federal regulator
|
|
31924
|
+
- ESTYMD: Establishment date (YYYY-MM-DD)
|
|
31925
|
+
- REGAGNT: Primary federal regulator (OCC, FRS, FDIC)
|
|
31918
31926
|
|
|
31919
31927
|
Args:
|
|
31920
31928
|
- filters (string, optional): ElasticSearch query filter
|
|
@@ -32037,22 +32045,27 @@ function registerFailureTools(server) {
|
|
|
32037
32045
|
Returns data on bank failures including failure date, resolution type, estimated cost to the FDIC Deposit Insurance Fund, and acquiring institution info.
|
|
32038
32046
|
|
|
32039
32047
|
Common filter examples:
|
|
32040
|
-
- By state: STALP:CA
|
|
32048
|
+
- By state: STALP:CA (two-letter state code)
|
|
32041
32049
|
- By year range: FAILDATE:[2008-01-01 TO 2010-12-31]
|
|
32042
32050
|
- Recent failures: FAILDATE:[2020-01-01 TO *]
|
|
32043
|
-
- By resolution type: RESTYPE:PAYOFF or RESTYPE:
|
|
32051
|
+
- By resolution type: RESTYPE:PAYOFF or RESTYPE:"PURCHASE AND ASSUMPTION"
|
|
32044
32052
|
- Large failures by cost: COST:[100000 TO *] (cost in $thousands)
|
|
32045
32053
|
- By name: NAME:"Washington Mutual"
|
|
32046
32054
|
|
|
32055
|
+
Resolution types (RESTYPE):
|
|
32056
|
+
PAYOFF = depositors paid directly, no acquirer
|
|
32057
|
+
PURCHASE AND ASSUMPTION = acquirer buys assets and assumes deposits
|
|
32058
|
+
PAYOUT = variant of payoff with insured-deposit transfer
|
|
32059
|
+
|
|
32047
32060
|
Key returned fields:
|
|
32048
32061
|
- CERT: FDIC Certificate Number
|
|
32049
32062
|
- NAME: Institution name
|
|
32050
|
-
- CITY, STALP, STNAME: Location
|
|
32063
|
+
- CITY, STALP (two-letter state code), STNAME (full state name): Location
|
|
32051
32064
|
- FAILDATE: Date of failure (YYYY-MM-DD)
|
|
32052
|
-
- SAVR: Savings
|
|
32053
|
-
- RESTYPE: Resolution type (
|
|
32065
|
+
- SAVR: Savings association flag (SA) or bank (BK)
|
|
32066
|
+
- RESTYPE: Resolution type (see above)
|
|
32054
32067
|
- QBFASSET: Total assets at failure ($thousands)
|
|
32055
|
-
- COST: Estimated cost to FDIC
|
|
32068
|
+
- COST: Estimated cost to FDIC Deposit Insurance Fund ($thousands)
|
|
32056
32069
|
|
|
32057
32070
|
Args:
|
|
32058
32071
|
- filters (string, optional): ElasticSearch query filter
|
|
@@ -32172,6 +32185,27 @@ var import_zod2 = require("zod");
|
|
|
32172
32185
|
var CHUNK_SIZE = 25;
|
|
32173
32186
|
var MAX_CONCURRENCY = 4;
|
|
32174
32187
|
var ANALYSIS_TIMEOUT_MS = 9e4;
|
|
32188
|
+
function getDefaultReportDate() {
|
|
32189
|
+
const target = new Date(Date.now() - 90 * 24 * 60 * 60 * 1e3);
|
|
32190
|
+
const year = target.getFullYear();
|
|
32191
|
+
const month = target.getMonth() + 1;
|
|
32192
|
+
if (month >= 10) return `${year}0930`;
|
|
32193
|
+
if (month >= 7) return `${year}0630`;
|
|
32194
|
+
if (month >= 4) return `${year}0331`;
|
|
32195
|
+
return `${year - 1}1231`;
|
|
32196
|
+
}
|
|
32197
|
+
function getReportDateOneYearPrior(repdte) {
|
|
32198
|
+
const year = Number.parseInt(repdte.slice(0, 4), 10);
|
|
32199
|
+
return `${year - 1}${repdte.slice(4)}`;
|
|
32200
|
+
}
|
|
32201
|
+
var VALID_QUARTER_END_SUFFIXES = /* @__PURE__ */ new Set(["0331", "0630", "0930", "1231"]);
|
|
32202
|
+
function validateQuarterEndDate(repdte, label) {
|
|
32203
|
+
const suffix = repdte.slice(4);
|
|
32204
|
+
if (!VALID_QUARTER_END_SUFFIXES.has(suffix)) {
|
|
32205
|
+
return `${label} "${repdte}" is not a valid quarter-end date. FDIC data is published quarterly \u2014 use a date ending in 0331 (Q1), 0630 (Q2), 0930 (Q3), or 1231 (Q4). Example: ${repdte.slice(0, 4)}1231 for Q4 ${repdte.slice(0, 4)}.`;
|
|
32206
|
+
}
|
|
32207
|
+
return null;
|
|
32208
|
+
}
|
|
32175
32209
|
function asNumber(value) {
|
|
32176
32210
|
return typeof value === "number" ? value : null;
|
|
32177
32211
|
}
|
|
@@ -32240,23 +32274,34 @@ Returns branch/office data including address, city, state, coordinates, branch t
|
|
|
32240
32274
|
|
|
32241
32275
|
Common filter examples:
|
|
32242
32276
|
- All branches of a bank: CERT:3511
|
|
32243
|
-
- By state: STALP:TX
|
|
32277
|
+
- By state: STALP:TX (two-letter state code)
|
|
32244
32278
|
- By city: CITY:"Austin"
|
|
32245
32279
|
- Main offices only: BRNUM:0
|
|
32246
32280
|
- By county: COUNTY:"Travis"
|
|
32247
|
-
- Active branches: ENDEFYMD:[9999-01-01 TO *]
|
|
32248
|
-
- By
|
|
32281
|
+
- Active branches only: ENDEFYMD:[9999-01-01 TO *] (sentinel date 9999-12-31 means still open)
|
|
32282
|
+
- By metro area (CBSA): CBSA_METRO_NAME:"New York-Newark-Jersey City"
|
|
32283
|
+
|
|
32284
|
+
Branch service types (BRSERTYP):
|
|
32285
|
+
11 = Full service brick and mortar
|
|
32286
|
+
12 = Full service retail
|
|
32287
|
+
21 = Limited service administrative
|
|
32288
|
+
22 = Limited service military
|
|
32289
|
+
23 = Limited service drive-through
|
|
32290
|
+
24 = Limited service loan production
|
|
32291
|
+
25 = Limited service consumer/trust
|
|
32292
|
+
26 = Limited service Internet/mobile
|
|
32293
|
+
29 = Limited service other
|
|
32249
32294
|
|
|
32250
32295
|
Key returned fields:
|
|
32251
32296
|
- CERT: FDIC Certificate Number
|
|
32252
32297
|
- UNINAME: Institution name
|
|
32253
32298
|
- NAMEFULL: Full branch name
|
|
32254
|
-
- ADDRESS, CITY, STALP, ZIP: Branch address
|
|
32299
|
+
- ADDRESS, CITY, STALP (two-letter state code), ZIP: Branch address
|
|
32255
32300
|
- COUNTY: County name
|
|
32256
32301
|
- BRNUM: Branch number (0 = main office)
|
|
32257
|
-
- BRSERTYP: Branch service type
|
|
32302
|
+
- BRSERTYP: Branch service type code (see above)
|
|
32258
32303
|
- LATITUDE, LONGITUDE: Geographic coordinates
|
|
32259
|
-
- ESTYMD: Branch established date
|
|
32304
|
+
- ESTYMD: Branch established date (YYYY-MM-DD)
|
|
32260
32305
|
- ENDEFYMD: Branch end date (9999-12-31 if still active)
|
|
32261
32306
|
|
|
32262
32307
|
Args:
|
|
@@ -32337,22 +32382,36 @@ Common filter examples:
|
|
|
32337
32382
|
- History for a specific bank: CERT:3511
|
|
32338
32383
|
- Mergers: TYPE:merger
|
|
32339
32384
|
- Failures: TYPE:failure
|
|
32340
|
-
- Name changes: CHANGECODE:CO
|
|
32385
|
+
- Name changes: CHANGECODE:CO
|
|
32341
32386
|
- By date range: PROCDATE:[2008-01-01 TO 2009-12-31]
|
|
32342
|
-
- By state: PSTALP:CA
|
|
32387
|
+
- By state: PSTALP:CA (two-letter state code)
|
|
32388
|
+
|
|
32389
|
+
Event types (TYPE):
|
|
32390
|
+
merger = institution was merged into another
|
|
32391
|
+
failure = institution failed
|
|
32392
|
+
assistance = received FDIC assistance transaction
|
|
32393
|
+
insurance = insurance-related event (new coverage, termination)
|
|
32394
|
+
|
|
32395
|
+
Common change codes (CHANGECODE):
|
|
32396
|
+
CO = name change
|
|
32397
|
+
CR = charter conversion
|
|
32398
|
+
DC = deposit assumption change
|
|
32399
|
+
MA = merger/acquisition (absorbed by another institution)
|
|
32400
|
+
NI = new institution insured
|
|
32401
|
+
TC = trust company conversion
|
|
32343
32402
|
|
|
32344
32403
|
Key returned fields:
|
|
32345
32404
|
- CERT: FDIC Certificate Number
|
|
32346
32405
|
- INSTNAME: Institution name
|
|
32347
32406
|
- CLASS: Charter class at time of change
|
|
32348
|
-
- PCITY, PSTALP: Location (city, state
|
|
32349
|
-
- PROCDATE: Processing date of the change
|
|
32350
|
-
- EFFDATE: Effective date of the change
|
|
32407
|
+
- PCITY, PSTALP: Location (city, two-letter state code)
|
|
32408
|
+
- PROCDATE: Processing date of the change (YYYY-MM-DD)
|
|
32409
|
+
- EFFDATE: Effective date of the change (YYYY-MM-DD)
|
|
32351
32410
|
- ENDEFYMD: End effective date
|
|
32352
32411
|
- PCERT: Predecessor/successor CERT (for mergers)
|
|
32353
|
-
- TYPE: Type of structural change
|
|
32354
|
-
- CHANGECODE: Code for type of change
|
|
32355
|
-
- CHANGECODE_DESC:
|
|
32412
|
+
- TYPE: Type of structural change (see above)
|
|
32413
|
+
- CHANGECODE: Code for type of change (see above)
|
|
32414
|
+
- CHANGECODE_DESC: Human-readable description of the change code
|
|
32356
32415
|
- INSDATE: Insurance date
|
|
32357
32416
|
|
|
32358
32417
|
Args:
|
|
@@ -32423,7 +32482,7 @@ var FinancialQuerySchema = CommonQuerySchema.extend({
|
|
|
32423
32482
|
"Filter by FDIC Certificate Number to get financials for a specific institution"
|
|
32424
32483
|
),
|
|
32425
32484
|
repdte: import_zod4.z.string().optional().describe(
|
|
32426
|
-
"Filter by
|
|
32485
|
+
"Filter by Report Date (REPDTE) in YYYYMMDD format. FDIC data is published quarterly on call report dates: March 31, June 30, September 30, and December 31. Example: 20231231 for Q4 2023. If omitted, returns all available dates (sorted most recent first by default)."
|
|
32427
32486
|
)
|
|
32428
32487
|
});
|
|
32429
32488
|
var SummaryQuerySchema = CommonQuerySchema.extend({
|
|
@@ -32448,7 +32507,7 @@ Common filter examples:
|
|
|
32448
32507
|
|
|
32449
32508
|
Key returned fields:
|
|
32450
32509
|
- CERT: FDIC Certificate Number
|
|
32451
|
-
- REPDTE: Report
|
|
32510
|
+
- REPDTE: Report Date \u2014 the last day of the quarterly reporting period (YYYYMMDD)
|
|
32452
32511
|
- ASSET: Total assets ($thousands)
|
|
32453
32512
|
- DEP: Total deposits ($thousands)
|
|
32454
32513
|
- DEPDOM: Domestic deposits ($thousands)
|
|
@@ -32461,7 +32520,7 @@ Key returned fields:
|
|
|
32461
32520
|
|
|
32462
32521
|
Args:
|
|
32463
32522
|
- cert (number, optional): Filter by institution CERT number
|
|
32464
|
-
- repdte (string, optional): Report
|
|
32523
|
+
- repdte (string, optional): Report Date in YYYYMMDD format (quarter-end dates: 0331, 0630, 0930, 1231)
|
|
32465
32524
|
- filters (string, optional): Additional ElasticSearch query filters
|
|
32466
32525
|
- fields (string, optional): Comma-separated field names (the full set has 1,100+ fields)
|
|
32467
32526
|
- limit (number): Records to return (default: 20)
|
|
@@ -32541,7 +32600,7 @@ Key returned fields:
|
|
|
32541
32600
|
- ROA: Return on assets (%)
|
|
32542
32601
|
- ROE: Return on equity (%)
|
|
32543
32602
|
- OFFICES: Number of branch offices
|
|
32544
|
-
- REPDTE: Report
|
|
32603
|
+
- REPDTE: Report Date \u2014 the last day of the reporting period (YYYYMMDD)
|
|
32545
32604
|
|
|
32546
32605
|
Args:
|
|
32547
32606
|
- cert (number, optional): Filter by institution CERT number
|
|
@@ -32706,7 +32765,7 @@ var import_zod6 = require("zod");
|
|
|
32706
32765
|
var DemographicsQuerySchema = CommonQuerySchema.extend({
|
|
32707
32766
|
cert: import_zod6.z.number().int().positive().optional().describe("Filter by FDIC Certificate Number"),
|
|
32708
32767
|
repdte: import_zod6.z.string().optional().describe(
|
|
32709
|
-
"Filter by
|
|
32768
|
+
"Filter by Report Date (REPDTE) in YYYYMMDD format. FDIC data is published quarterly on: March 31, June 30, September 30, and December 31. Example: 20251231 for Q4 2025. If omitted, returns all available dates."
|
|
32710
32769
|
)
|
|
32711
32770
|
});
|
|
32712
32771
|
function registerDemographicsTools(server) {
|
|
@@ -32727,7 +32786,7 @@ Common filter examples:
|
|
|
32727
32786
|
|
|
32728
32787
|
Key returned fields:
|
|
32729
32788
|
- CERT: FDIC Certificate Number
|
|
32730
|
-
- REPDTE: Report
|
|
32789
|
+
- REPDTE: Report Date \u2014 the last day of the quarterly reporting period (YYYYMMDD)
|
|
32731
32790
|
- QTRNO: Quarter number
|
|
32732
32791
|
- OFFTOT: Total offices
|
|
32733
32792
|
- OFFSTATE: Offices in other states
|
|
@@ -32741,7 +32800,7 @@ Key returned fields:
|
|
|
32741
32800
|
|
|
32742
32801
|
Args:
|
|
32743
32802
|
- cert (number, optional): Filter by institution CERT number
|
|
32744
|
-
- repdte (string, optional): Report
|
|
32803
|
+
- repdte (string, optional): Report Date in YYYYMMDD format (quarter-end dates: 0331, 0630, 0930, 1231)
|
|
32745
32804
|
- filters (string, optional): Additional ElasticSearch query filters
|
|
32746
32805
|
- fields (string, optional): Comma-separated field names
|
|
32747
32806
|
- limit (number): Records to return (default: 20)
|
|
@@ -32852,8 +32911,12 @@ var SnapshotAnalysisSchema = import_zod7.z.object({
|
|
|
32852
32911
|
'Additional institution-level filter used when building the comparison set. Example: BKCLASS:N or CITY:"Charlotte"'
|
|
32853
32912
|
),
|
|
32854
32913
|
active_only: import_zod7.z.boolean().default(true).describe("Limit the comparison set to currently active institutions."),
|
|
32855
|
-
start_repdte: import_zod7.z.string().regex(/^\d{8}$/).describe(
|
|
32856
|
-
|
|
32914
|
+
start_repdte: import_zod7.z.string().regex(/^\d{8}$/).optional().describe(
|
|
32915
|
+
"Starting Report Date (REPDTE) in YYYYMMDD format. Must be a quarter-end date: March 31 (0331), June 30 (0630), September 30 (0930), or December 31 (1231). Example: 20210331 for Q1 2021. If omitted, defaults to the same quarter one year before end_repdte."
|
|
32916
|
+
),
|
|
32917
|
+
end_repdte: import_zod7.z.string().regex(/^\d{8}$/).optional().describe(
|
|
32918
|
+
"Ending Report Date (REPDTE) in YYYYMMDD format. Must be a quarter-end date: March 31 (0331), June 30 (0630), September 30 (0930), or December 31 (1231). Must be later than start_repdte. Example: 20251231 for Q4 2025. If omitted, defaults to the most recent quarter-end date with published data (~90-day lag)."
|
|
32919
|
+
),
|
|
32857
32920
|
analysis_mode: AnalysisModeSchema.default("snapshot").describe(
|
|
32858
32921
|
"Use snapshot for two-point comparison or timeseries for quarterly trend analysis across the date range."
|
|
32859
32922
|
),
|
|
@@ -32862,14 +32925,23 @@ var SnapshotAnalysisSchema = import_zod7.z.object({
|
|
|
32862
32925
|
),
|
|
32863
32926
|
limit: import_zod7.z.number().int().min(1).max(100).default(10).describe("Maximum number of ranked comparisons to return."),
|
|
32864
32927
|
sort_by: SortFieldSchema.default("asset_growth").describe(
|
|
32865
|
-
"Comparison field used to rank institutions."
|
|
32928
|
+
"Comparison field used to rank institutions. Valid options: asset_growth, asset_growth_pct, dep_growth, dep_growth_pct, netinc_change, netinc_change_pct, roa_change, roe_change, offices_change, assets_per_office_change, deposits_per_office_change, deposits_to_assets_change."
|
|
32866
32929
|
),
|
|
32867
32930
|
sort_order: import_zod7.z.enum(["ASC", "DESC"]).default("DESC").describe("Sort direction for the ranked comparisons.")
|
|
32868
32931
|
});
|
|
32932
|
+
function resolveSnapshotDefaults(params) {
|
|
32933
|
+
const end_repdte = params.end_repdte ?? getDefaultReportDate();
|
|
32934
|
+
const start_repdte = params.start_repdte ?? getReportDateOneYearPrior(end_repdte);
|
|
32935
|
+
return { ...params, start_repdte, end_repdte };
|
|
32936
|
+
}
|
|
32869
32937
|
function validateSnapshotAnalysisParams(value) {
|
|
32870
32938
|
if (!value.state && (!value.certs || value.certs.length === 0)) {
|
|
32871
32939
|
return "Provide either state or certs.";
|
|
32872
32940
|
}
|
|
32941
|
+
const startDateError = validateQuarterEndDate(value.start_repdte, "start_repdte");
|
|
32942
|
+
if (startDateError) return startDateError;
|
|
32943
|
+
const endDateError = validateQuarterEndDate(value.end_repdte, "end_repdte");
|
|
32944
|
+
if (endDateError) return endDateError;
|
|
32873
32945
|
if (value.start_repdte >= value.end_repdte) {
|
|
32874
32946
|
return "start_repdte must be earlier than end_repdte.";
|
|
32875
32947
|
}
|
|
@@ -33395,12 +33467,12 @@ Good uses:
|
|
|
33395
33467
|
|
|
33396
33468
|
Inputs:
|
|
33397
33469
|
- state or certs: choose a geographic roster or provide a direct comparison set
|
|
33398
|
-
- start_repdte, end_repdte:
|
|
33470
|
+
- start_repdte, end_repdte: Report Dates (REPDTE) in YYYYMMDD format \u2014 must be quarter-end dates (0331, 0630, 0930, 1231)
|
|
33399
33471
|
- analysis_mode: snapshot or timeseries
|
|
33400
33472
|
- institution_filters: optional extra institution filter when building the roster
|
|
33401
33473
|
- active_only: default true
|
|
33402
33474
|
- include_demographics: default true, adds office-count comparisons when available
|
|
33403
|
-
- sort_by: ranking field
|
|
33475
|
+
- sort_by: ranking field (default: asset_growth). All options: asset_growth, asset_growth_pct, dep_growth, dep_growth_pct, netinc_change, netinc_change_pct, roa_change, roe_change, offices_change, assets_per_office_change, deposits_per_office_change, deposits_to_assets_change
|
|
33404
33476
|
- sort_order: ASC or DESC
|
|
33405
33477
|
- limit: maximum ranked results to return
|
|
33406
33478
|
|
|
@@ -33413,19 +33485,20 @@ Returns concise comparison text plus structured deltas, derived metrics, and ins
|
|
|
33413
33485
|
openWorldHint: true
|
|
33414
33486
|
}
|
|
33415
33487
|
},
|
|
33416
|
-
async ({
|
|
33417
|
-
|
|
33418
|
-
|
|
33419
|
-
|
|
33420
|
-
|
|
33421
|
-
|
|
33422
|
-
|
|
33423
|
-
|
|
33424
|
-
|
|
33425
|
-
|
|
33426
|
-
|
|
33427
|
-
|
|
33428
|
-
|
|
33488
|
+
async (rawParams, extra) => {
|
|
33489
|
+
const {
|
|
33490
|
+
state,
|
|
33491
|
+
certs,
|
|
33492
|
+
institution_filters,
|
|
33493
|
+
active_only,
|
|
33494
|
+
start_repdte,
|
|
33495
|
+
end_repdte,
|
|
33496
|
+
analysis_mode,
|
|
33497
|
+
include_demographics,
|
|
33498
|
+
limit,
|
|
33499
|
+
sort_by,
|
|
33500
|
+
sort_order
|
|
33501
|
+
} = resolveSnapshotDefaults(rawParams);
|
|
33429
33502
|
const controller = new AbortController();
|
|
33430
33503
|
const timeoutId = setTimeout(() => controller.abort(), ANALYSIS_TIMEOUT_MS);
|
|
33431
33504
|
const progressToken = extra._meta?.progressToken;
|
|
@@ -33645,7 +33718,7 @@ Returns concise comparison text plus structured deltas, derived metrics, and ins
|
|
|
33645
33718
|
if (controller.signal.aborted) {
|
|
33646
33719
|
return formatToolError(
|
|
33647
33720
|
new Error(
|
|
33648
|
-
`Analysis timed out after ${Math.floor(ANALYSIS_TIMEOUT_MS / 1e3)} seconds.
|
|
33721
|
+
`Analysis timed out after ${Math.floor(ANALYSIS_TIMEOUT_MS / 1e3)} seconds. Try reducing the comparison set: use certs (max 100) instead of a state-wide roster, add institution_filters (e.g., BKCLASS:N), or shorten the date range for timeseries mode.`
|
|
33649
33722
|
)
|
|
33650
33723
|
);
|
|
33651
33724
|
}
|
|
@@ -33806,7 +33879,9 @@ var PeerGroupInputSchema = import_zod8.z.object({
|
|
|
33806
33879
|
cert: import_zod8.z.number().int().positive().optional().describe(
|
|
33807
33880
|
"Subject institution CERT number. When provided, auto-derives peer criteria and ranks this bank against peers."
|
|
33808
33881
|
),
|
|
33809
|
-
repdte: import_zod8.z.string().regex(/^\d{8}$/).describe(
|
|
33882
|
+
repdte: import_zod8.z.string().regex(/^\d{8}$/).optional().describe(
|
|
33883
|
+
"Report Date (REPDTE) in YYYYMMDD format. FDIC data is published quarterly on: March 31, June 30, September 30, and December 31. Example: 20231231 for Q4 2023. If omitted, defaults to the most recent quarter-end date likely to have published data (~90-day lag)."
|
|
33884
|
+
),
|
|
33810
33885
|
asset_min: import_zod8.z.number().positive().optional().describe(
|
|
33811
33886
|
"Minimum total assets ($thousands) for peer selection. Defaults to 50% of subject's report-date assets when cert is provided."
|
|
33812
33887
|
),
|
|
@@ -33962,7 +34037,8 @@ Override precedence: cert derives defaults, then explicit params override them.`
|
|
|
33962
34037
|
openWorldHint: true
|
|
33963
34038
|
}
|
|
33964
34039
|
},
|
|
33965
|
-
async (
|
|
34040
|
+
async (rawParams, extra) => {
|
|
34041
|
+
const params = { ...rawParams, repdte: rawParams.repdte ?? getDefaultReportDate() };
|
|
33966
34042
|
const controller = new AbortController();
|
|
33967
34043
|
const timeoutId = setTimeout(() => controller.abort(), ANALYSIS_TIMEOUT_MS);
|
|
33968
34044
|
const progressToken = extra._meta?.progressToken;
|
|
@@ -33971,6 +34047,10 @@ Override precedence: cert derives defaults, then explicit params override them.`
|
|
|
33971
34047
|
if (validationError) {
|
|
33972
34048
|
return formatToolError(new Error(validationError));
|
|
33973
34049
|
}
|
|
34050
|
+
const dateError = validateQuarterEndDate(params.repdte, "repdte");
|
|
34051
|
+
if (dateError) {
|
|
34052
|
+
return formatToolError(new Error(dateError));
|
|
34053
|
+
}
|
|
33974
34054
|
const extraFieldsError = validateExtraFields(params.extra_fields);
|
|
33975
34055
|
if (extraFieldsError) {
|
|
33976
34056
|
return formatToolError(extraFieldsError);
|
|
@@ -34018,7 +34098,7 @@ Override precedence: cert derives defaults, then explicit params override them.`
|
|
|
34018
34098
|
if (financialRecords.length === 0) {
|
|
34019
34099
|
return formatToolError(
|
|
34020
34100
|
new Error(
|
|
34021
|
-
`No financial data for CERT ${params.cert} at report date ${params.repdte}.
|
|
34101
|
+
`No financial data for CERT ${params.cert} at report date ${params.repdte}. FDIC quarterly data is published ~90 days after each quarter-end (March 31, June 30, September 30, December 31). Try an earlier quarter-end date, or verify the institution was active at that date.`
|
|
34022
34102
|
)
|
|
34023
34103
|
);
|
|
34024
34104
|
}
|
|
@@ -34273,7 +34353,7 @@ Override precedence: cert derives defaults, then explicit params override them.`
|
|
|
34273
34353
|
if (controller.signal.aborted) {
|
|
34274
34354
|
return formatToolError(
|
|
34275
34355
|
new Error(
|
|
34276
|
-
`Peer group analysis timed out after ${Math.floor(ANALYSIS_TIMEOUT_MS / 1e3)} seconds.
|
|
34356
|
+
`Peer group analysis timed out after ${Math.floor(ANALYSIS_TIMEOUT_MS / 1e3)} seconds. Try narrowing the peer group: add a state filter, tighten the asset_min/asset_max range, or specify charter_classes.`
|
|
34277
34357
|
)
|
|
34278
34358
|
);
|
|
34279
34359
|
}
|