@trygordian/mcp-newyorkfed
Version:
Model Context Protocol server for New York Fed Markets Data APIs
685 lines • 27.7 kB
JavaScript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
// Base URL for NY Fed API
const BASE_URL = "https://markets.newyorkfed.org";
// Supported formats
const SUPPORTED_FORMATS = ["json", "xml", "csv", "pdf", "xlsx"];
// Tool definitions
const REFERENCE_RATES_ALL_TOOL = {
name: "get_reference_rates_all",
description: "Get all reference rates (SOFR, EFFR, OBFR, etc.) for the latest available date or search with filters",
inputSchema: {
type: "object",
properties: {
operation: {
type: "string",
enum: ["latest", "search"],
description: "Get latest rates or search with filters",
default: "latest"
},
format: {
type: "string",
enum: SUPPORTED_FORMATS,
description: "Response format",
default: "json"
},
startDate: {
type: "string",
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
description: "Start date for search (YYYY-MM-DD format, only for search operation)"
},
endDate: {
type: "string",
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
description: "End date for search (YYYY-MM-DD format, only for search operation)"
}
},
required: ["operation", "format"]
}
};
const REFERENCE_RATES_SECURED_TOOL = {
name: "get_secured_rates",
description: "Get secured reference rates (SOFR, TGCR, BGCR) with options for latest data or historical search",
inputSchema: {
type: "object",
properties: {
operation: {
type: "string",
enum: ["latest", "last", "search"],
description: "Type of operation to perform"
},
rateType: {
type: "string",
enum: ["sofr", "tgcr", "bgcr"],
description: "Type of secured rate (required for last/search operations)"
},
number: {
type: "number",
minimum: 1,
maximum: 1000,
description: "Number of records to retrieve (only for 'last' operation)"
},
format: {
type: "string",
enum: SUPPORTED_FORMATS,
description: "Response format",
default: "json"
},
startDate: {
type: "string",
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
description: "Start date for search (YYYY-MM-DD format, only for search operation)"
},
endDate: {
type: "string",
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
description: "End date for search (YYYY-MM-DD format, only for search operation)"
}
},
required: ["operation", "format"]
}
};
const REFERENCE_RATES_UNSECURED_TOOL = {
name: "get_unsecured_rates",
description: "Get unsecured reference rates (EFFR, OBFR) with options for latest data or historical search",
inputSchema: {
type: "object",
properties: {
operation: {
type: "string",
enum: ["latest", "last", "search"],
description: "Type of operation to perform"
},
rateType: {
type: "string",
enum: ["effr", "obfr"],
description: "Type of unsecured rate (required for last/search operations)"
},
number: {
type: "number",
minimum: 1,
maximum: 1000,
description: "Number of records to retrieve (only for 'last' operation)"
},
format: {
type: "string",
enum: SUPPORTED_FORMATS,
description: "Response format",
default: "json"
},
startDate: {
type: "string",
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
description: "Start date for search (YYYY-MM-DD format, only for search operation)"
},
endDate: {
type: "string",
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
description: "End date for search (YYYY-MM-DD format, only for search operation)"
}
},
required: ["operation", "format"]
}
};
const SOMA_HOLDINGS_TOOL = {
name: "get_soma_holdings",
description: "Get System Open Market Account (SOMA) holdings data for Treasury and Agency securities",
inputSchema: {
type: "object",
properties: {
operation: {
type: "string",
enum: ["summary", "latest_date", "list_dates", "treasury_by_date", "agency_by_date", "treasury_by_cusip", "agency_by_cusip"],
description: "Type of SOMA data to retrieve"
},
format: {
type: "string",
enum: SUPPORTED_FORMATS,
description: "Response format",
default: "json"
},
date: {
type: "string",
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
description: "As-of date for holdings (YYYY-MM-DD format, required for *_by_date operations)"
},
cusip: {
type: "string",
description: "CUSIP identifier (required for *_by_cusip operations)"
},
holdingType: {
type: "string",
enum: ["bills", "notesbonds", "frn", "tips", "agency debts", "mbs", "cmbs"],
description: "Type of holding (optional for *_by_date operations)"
}
},
required: ["operation", "format"]
}
};
const TREASURY_OPERATIONS_TOOL = {
name: "get_treasury_operations",
description: "Get Treasury securities operations including purchases, sales, and prices paid",
inputSchema: {
type: "object",
properties: {
operation: {
type: "string",
enum: ["purchases", "sales"],
description: "Type of Treasury operation"
},
timeframe: {
type: "string",
enum: ["latest", "lastTwoWeeks", "last"],
description: "Timeframe for operations"
},
status: {
type: "string",
enum: ["all", "completed"],
description: "Operation status filter (required for latest timeframe)"
},
include: {
type: "string",
enum: ["details", "summary"],
description: "Level of detail to include"
},
number: {
type: "number",
minimum: 1,
maximum: 1000,
description: "Number of operations to retrieve (only for 'last' timeframe)"
},
format: {
type: "string",
enum: SUPPORTED_FORMATS,
description: "Response format",
default: "json"
},
startDate: {
type: "string",
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
description: "Start date for search (YYYY-MM-DD format)"
},
endDate: {
type: "string",
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
description: "End date for search (YYYY-MM-DD format)"
}
},
required: ["operation", "timeframe", "include", "format"]
}
};
const REPO_OPERATIONS_TOOL = {
name: "get_repo_operations",
description: "Get repo and reverse repo operations data",
inputSchema: {
type: "object",
properties: {
operationType: {
type: "string",
enum: ["repo", "reverserepo"],
description: "Type of repo operation"
},
method: {
type: "string",
enum: ["all", "single-tranche", "multiple-price"],
description: "Operation method (not required for search operations)"
},
timeframe: {
type: "string",
enum: ["latest", "lastTwoWeeks", "last", "search"],
description: "Timeframe for operations"
},
status: {
type: "string",
enum: ["all", "completed"],
description: "Operation status (required for latest timeframe)"
},
number: {
type: "number",
minimum: 1,
maximum: 1000,
description: "Number of operations to retrieve (only for 'last' timeframe)"
},
format: {
type: "string",
enum: SUPPORTED_FORMATS,
description: "Response format",
default: "json"
},
startDate: {
type: "string",
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
description: "Start date for search (YYYY-MM-DD format)"
},
endDate: {
type: "string",
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
description: "End date for search (YYYY-MM-DD format)"
}
},
required: ["operationType", "timeframe", "format"]
}
};
const PRIMARY_DEALER_STATS_TOOL = {
name: "get_primary_dealer_statistics",
description: "Get Primary Dealer Statistics survey data",
inputSchema: {
type: "object",
properties: {
operation: {
type: "string",
enum: ["latest_by_series", "all_surveys", "list_timeseries", "list_dates", "list_series_breaks", "by_date", "by_timeseries", "by_series_and_timeseries"],
description: "Type of dealer statistics operation"
},
seriesBreak: {
type: "string",
description: "Series break identifier (required for latest_by_series and by_series_and_timeseries)"
},
date: {
type: "string",
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
description: "As-of date (YYYY-MM-DD format, required for by_date operation)"
},
timeseries: {
type: "string",
description: "Time series/key ID (required for by_timeseries and by_series_and_timeseries)"
},
format: {
type: "string",
enum: SUPPORTED_FORMATS,
description: "Response format",
default: "json"
}
},
required: ["operation", "format"]
}
};
const MARKET_SHARE_TOOL = {
name: "get_market_share",
description: "Get Primary Dealer market share data (quarterly and year-to-date)",
inputSchema: {
type: "object",
properties: {
period: {
type: "string",
enum: ["quarterly", "ytd"],
description: "Market share period"
},
format: {
type: "string",
enum: SUPPORTED_FORMATS,
description: "Response format",
default: "json"
}
},
required: ["period", "format"]
}
};
// Server implementation
const server = new Server({
name: "newyorkfed-mcp-server",
version: "1.0.0",
}, {
capabilities: {
tools: {},
},
});
// Utility function to build API URLs
function buildApiUrl(path, queryParams) {
const url = new URL(`${BASE_URL}${path}`);
if (queryParams) {
Object.entries(queryParams).forEach(([key, value]) => {
if (value)
url.searchParams.set(key, value);
});
}
return url.toString();
}
// Utility function to make API requests
async function makeApiRequest(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`API request failed: ${response.status} ${response.statusText}`);
}
const contentType = response.headers.get('content-type') || '';
if (contentType.includes('application/json')) {
const data = await response.json();
return JSON.stringify(data, null, 2);
}
else {
return await response.text();
}
}
catch (error) {
throw new Error(`Failed to fetch data: ${error instanceof Error ? error.message : String(error)}`);
}
}
// Tool handlers
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
REFERENCE_RATES_ALL_TOOL,
REFERENCE_RATES_SECURED_TOOL,
REFERENCE_RATES_UNSECURED_TOOL,
SOMA_HOLDINGS_TOOL,
TREASURY_OPERATIONS_TOOL,
REPO_OPERATIONS_TOOL,
PRIMARY_DEALER_STATS_TOOL,
MARKET_SHARE_TOOL,
],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
const { name, arguments: args } = request.params;
if (!args) {
throw new Error("No arguments provided");
}
switch (name) {
case "get_reference_rates_all": {
const { operation, format, startDate, endDate } = args;
let path;
const queryParams = {};
if (operation === "latest") {
path = `/api/rates/all/latest.${format}`;
}
else if (operation === "search") {
path = `/api/rates/all/search.${format}`;
if (startDate)
queryParams.startDate = startDate;
if (endDate)
queryParams.endDate = endDate;
}
else {
throw new Error("Invalid operation. Must be 'latest' or 'search'");
}
const url = buildApiUrl(path, queryParams);
const result = await makeApiRequest(url);
return {
content: [{ type: "text", text: result }],
isError: false,
};
}
case "get_secured_rates": {
const { operation, rateType, number, format, startDate, endDate } = args;
let path;
const queryParams = {};
if (operation === "latest") {
path = `/api/rates/secured/all/latest.${format}`;
}
else if (operation === "last") {
if (!rateType || !number) {
throw new Error("rateType and number are required for 'last' operation");
}
path = `/api/rates/secured/${rateType}/last/${number}.${format}`;
}
else if (operation === "search") {
if (!rateType) {
throw new Error("rateType is required for 'search' operation");
}
path = `/api/rates/secured/${rateType}/search.${format}`;
if (startDate)
queryParams.startDate = startDate;
if (endDate)
queryParams.endDate = endDate;
}
else {
throw new Error("Invalid operation. Must be 'latest', 'last', or 'search'");
}
const url = buildApiUrl(path, queryParams);
const result = await makeApiRequest(url);
return {
content: [{ type: "text", text: result }],
isError: false,
};
}
case "get_unsecured_rates": {
const { operation, rateType, number, format, startDate, endDate } = args;
let path;
const queryParams = {};
if (operation === "latest") {
path = `/api/rates/unsecured/all/latest.${format}`;
}
else if (operation === "last") {
if (!rateType || !number) {
throw new Error("rateType and number are required for 'last' operation");
}
path = `/api/rates/unsecured/${rateType}/last/${number}.${format}`;
}
else if (operation === "search") {
if (!rateType) {
throw new Error("rateType is required for 'search' operation");
}
path = `/api/rates/unsecured/${rateType}/search.${format}`;
if (startDate)
queryParams.startDate = startDate;
if (endDate)
queryParams.endDate = endDate;
}
else {
throw new Error("Invalid operation. Must be 'latest', 'last', or 'search'");
}
const url = buildApiUrl(path, queryParams);
const result = await makeApiRequest(url);
return {
content: [{ type: "text", text: result }],
isError: false,
};
}
case "get_soma_holdings": {
const { operation, format, date, cusip, holdingType } = args;
let path;
switch (operation) {
case "summary":
path = `/api/soma/summary.${format}`;
break;
case "latest_date":
path = `/api/soma/asofdates/latest.${format}`;
break;
case "list_dates":
path = `/api/soma/asofdates/list.${format}`;
break;
case "treasury_by_date":
if (!date)
throw new Error("date is required for treasury_by_date operation");
if (holdingType) {
path = `/api/soma/tsy/get/${holdingType}/asof/${date}.${format}`;
}
else {
path = `/api/soma/tsy/get/asof/${date}.${format}`;
}
break;
case "agency_by_date":
if (!date)
throw new Error("date is required for agency_by_date operation");
if (holdingType) {
path = `/api/soma/agency/get/${holdingType}/asof/${date}.${format}`;
}
else {
path = `/api/soma/agency/get/asof/${date}.${format}`;
}
break;
case "treasury_by_cusip":
if (!cusip)
throw new Error("cusip is required for treasury_by_cusip operation");
path = `/api/soma/tsy/get/cusip/${cusip}.${format}`;
break;
case "agency_by_cusip":
if (!cusip)
throw new Error("cusip is required for agency_by_cusip operation");
path = `/api/soma/agency/get/cusip/${cusip}.${format}`;
break;
default:
throw new Error("Invalid operation");
}
const url = buildApiUrl(path);
const result = await makeApiRequest(url);
return {
content: [{ type: "text", text: result }],
isError: false,
};
}
case "get_treasury_operations": {
const { operation, timeframe, status, include, number, format, startDate, endDate } = args;
let path;
const queryParams = {};
if (timeframe === "latest") {
if (!status)
throw new Error("status is required for latest timeframe");
path = `/api/tsy/${operation}/${status}/${include}/latest.${format}`;
}
else if (timeframe === "lastTwoWeeks") {
path = `/api/tsy/${operation}/results/${include}/lastTwoWeeks.${format}`;
}
else if (timeframe === "last") {
if (!number)
throw new Error("number is required for last timeframe");
path = `/api/tsy/${operation}/results/${include}/last/${number}.${format}`;
}
else if (timeframe === "search") {
path = `/api/tsy/${operation}/results/${include}/search.${format}`;
if (startDate)
queryParams.startDate = startDate;
if (endDate)
queryParams.endDate = endDate;
}
else {
throw new Error("Invalid timeframe");
}
const url = buildApiUrl(path, queryParams);
const result = await makeApiRequest(url);
return {
content: [{ type: "text", text: result }],
isError: false,
};
}
case "get_repo_operations": {
const { operationType, method, timeframe, status, number, format, startDate, endDate } = args;
let path;
const queryParams = {};
if (timeframe === "search") {
if (operationType === "reverserepo") {
path = `/api/rp/reverserepo/propositions/search.${format}`;
}
else {
path = `/api/rp/results/search.${format}`;
}
if (startDate)
queryParams.startDate = startDate;
if (endDate)
queryParams.endDate = endDate;
}
else if (timeframe === "latest") {
if (!method || !status)
throw new Error("method and status are required for latest timeframe");
path = `/api/rp/${operationType}/${method}/${status}/latest.${format}`;
}
else if (timeframe === "lastTwoWeeks") {
if (!method)
throw new Error("method is required for lastTwoWeeks timeframe");
path = `/api/rp/${operationType}/${method}/results/lastTwoWeeks.${format}`;
}
else if (timeframe === "last") {
if (!method || !number)
throw new Error("method and number are required for last timeframe");
path = `/api/rp/${operationType}/${method}/results/last/${number}.${format}`;
}
else {
throw new Error("Invalid timeframe");
}
const url = buildApiUrl(path, queryParams);
const result = await makeApiRequest(url);
return {
content: [{ type: "text", text: result }],
isError: false,
};
}
case "get_primary_dealer_statistics": {
const { operation, seriesBreak, date, timeseries, format } = args;
let path;
switch (operation) {
case "latest_by_series":
if (!seriesBreak)
throw new Error("seriesBreak is required for latest_by_series operation");
path = `/api/pd/latest/${seriesBreak}.${format}`;
break;
case "all_surveys":
if (format !== "csv")
throw new Error("all_surveys operation only supports CSV format");
path = `/api/pd/get/all/timeseries.csv`;
break;
case "list_timeseries":
path = `/api/pd/list/timeseries.${format}`;
break;
case "list_dates":
path = `/api/pd/list/asof.${format}`;
break;
case "list_series_breaks":
path = `/api/pd/list/seriesbreaks.${format}`;
break;
case "by_date":
if (!date)
throw new Error("date is required for by_date operation");
path = `/api/pd/get/asof/${date}.${format}`;
break;
case "by_timeseries":
if (!timeseries)
throw new Error("timeseries is required for by_timeseries operation");
path = `/api/pd/get/${timeseries}.${format}`;
break;
case "by_series_and_timeseries":
if (!seriesBreak || !timeseries) {
throw new Error("seriesBreak and timeseries are required for by_series_and_timeseries operation");
}
path = `/api/pd/get/${seriesBreak}/timeseries/${timeseries}.${format}`;
break;
default:
throw new Error("Invalid operation");
}
const url = buildApiUrl(path);
const result = await makeApiRequest(url);
return {
content: [{ type: "text", text: result }],
isError: false,
};
}
case "get_market_share": {
const { period, format } = args;
const path = `/api/marketshare/${period}/latest.${format}`;
const url = buildApiUrl(path);
const result = await makeApiRequest(url);
return {
content: [{ type: "text", text: result }],
isError: false,
};
}
default:
return {
content: [{ type: "text", text: `Unknown tool: ${name}` }],
isError: true,
};
}
}
catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
},
],
isError: true,
};
}
});
async function runServer() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("New York Fed MCP Server running on stdio");
}
runServer().catch((error) => {
console.error("Fatal error running server:", error);
process.exit(1);
});
//# sourceMappingURL=index.js.map