IMF Data MCP Server
by c-cf
Verified
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
IMF MCP Server Example
This server integrates the free IMF data API and provides the following features:
1. Returns all IMF datasets as resources (Dataflow API)
2. Returns the structure of a specified dataset as a resource (DataStructure API)
3. Retrieves time series data based on requirements as a tool (CompactData API)
4. Lists available indicators in the DataMapper API as a resource
5. Lists available countries as a resource (according to the IMF DataMapper API, the URL may need to be adjusted based on official documentation)
6. Provides a prompt template to guide users on how to query data with indicators and intentions
Note:
- The IMF API limits each user to a maximum of 10 requests every 5 seconds, and the overall application to a maximum of 50 requests per second.
- The default return format is XML (some APIs return JSON).
Please further parse or convert the returned data format as needed.
"""
from utils import process_imf_data
from mcp.server.fastmcp import FastMCP
import requests
import os
import json
# Create an instance of the MCP server
mcp = FastMCP("IMF Data Server")
# 1. Resource: List all IMF datasets (Dataflow API)
@mcp.resource("imf://datasets")
def list_datasets() -> dict:
"""
Returns IMF Dataflow information (list of datasets).
Returns:
dict: A dictionary containing dataset IDs and their descriptions.
"""
return {
"IFS": "International Financial Statistics",
"DOT": "Direction of Trade Statistics",
"BOP": "Balance of Payments Statistics",
"CDIS": "Coordinated Direct Investment Survey",
"CPIS": "Coordinated Portfolio Investment Survey",
"GFSMAB": "Government Finance Statistics, Main Aggregates and Balances",
"MFS": "Monetary and Financial Statistics",
"FSI": "Financial Soundness Indicators"
}
# 2. Resource: Get the structure of a specified dataset (DataStructure API)
@mcp.resource("imf://structure/{dataset_id}")
def get_structure(dataset_id: str):
"""
Returns the structure description of the specified dataset.
Args:
dataset_id (str): The ID of the dataset.
Returns:
str: The structure description in XML format.
"""
url = f"http://dataservices.imf.org/REST/SDMX_XML.svc/DataStructure/{dataset_id}"
try:
response = requests.get(url)
response.raise_for_status()
return response.text # XML format data
except Exception as e:
return f"Error fetching structure for {dataset_id}: {str(e)}"
@mcp.tool()
def fetch_ifs_data(freq: str, country: str, indicator: str, start: str | int, end: str | int) -> str:
"""
Retrieves compact format time series data from the IFS database based on the input parameters.
Args:
freq (str): Frequency (e.g., "A" for annual).
country (str): Country code, multiple country codes can be connected with "+".
indicator (str): Indicator code.
start (str | int): Start year.
end (str | int): End year.
Returns:
str: Description of the queried data. Do not perform further analysis or retry if the query fails.
"""
dimensions = f"{freq}.{country}.{indicator}"
url = f"http://dataservices.imf.org/REST/SDMX_JSON.svc/CompactData/IFS/{dimensions}?startPeriod={start}&endPeriod={end}"
try:
response = requests.get(url)
response.raise_for_status()
data = response.json()
return process_imf_data(data)
except Exception as e:
return f"Error fetching IFS data: {str(e)}"
@mcp.tool()
def fetch_dot_data(freq: str, country: str, indicator: str, counterpart: str, start: str | int, end: str | int) -> str:
"""
Retrieves compact format time series data from the DOT database based on the input parameters.
Args:
freq (str): Frequency (e.g., "A" for annual).
country (str): Country code, multiple country codes can be connected with "+".
indicator (str): Indicator code.
counterpart (str): Counterpart country code.
start (str | int): Start year.
end (str | int): End year.
Returns:
str: Description of the queried data. Do not perform further analysis or retry if the query fails.
"""
dimensions = f"{freq}.{country}.{indicator}.{counterpart}"
url = f"http://dataservices.imf.org/REST/SDMX_JSON.svc/CompactData/DOT/{dimensions}?startPeriod={start}&endPeriod={end}"
try:
response = requests.get(url)
response.raise_for_status()
data = response.json()
return process_imf_data(data)
except Exception as e:
return f"Error fetching DOT data: {str(e)}"
@mcp.tool()
def fetch_bop_data(freq: str, country: str, indicator: str, start: str | int, end: str | int) -> str:
"""
Retrieves compact format time series data from the BOP database based on the input parameters.
Args:
freq (str): Frequency (e.g., "A" for annual, "Q" for quarterly, "M" for monthly).
country (str): Country code, multiple country codes can be connected with "+".
indicator (str): Indicator code.
start (str | int): Start year.
end (str | int): End year.
Returns:
str: Description of the queried data. Do not perform further analysis or retry if the query fails.
"""
dimensions = f"{freq}.{country}.{indicator}"
url = f"http://dataservices.imf.org/REST/SDMX_JSON.svc/CompactData/BOP/{dimensions}?startPeriod={start}&endPeriod={end}"
try:
response = requests.get(url)
response.raise_for_status()
data = response.json()
return process_imf_data(data)
except Exception as e:
return f"Error fetching BOP data: {str(e)}"
@mcp.tool()
def fetch_cdis_data(freq: str, country: str, indicator: str, counterpart: str, start: str | int, end: str | int) -> str:
"""
Retrieves compact format time series data from the CDIS database based on the input parameters.
Args:
freq (str): Frequency (e.g., "A" for annual).
country (str): Country code, multiple country codes can be connected with "+".
indicator (str): Indicator code.
counterpart (str): Counterpart country code.
start (str | int): Start year.
end (str | int): End year.
Returns:
str: Description of the queried data. Do not perform further analysis or retry if the query fails.
"""
dimensions = f"{freq}.{country}.{indicator}.{counterpart}"
url = f"http://dataservices.imf.org/REST/SDMX_JSON.svc/CompactData/CDIS/{dimensions}?startPeriod={start}&endPeriod={end}"
try:
response = requests.get(url)
response.raise_for_status()
data = response.json()
return process_imf_data(data)
except Exception as e:
return f"Error fetching CDIS data: {str(e)}"
@mcp.tool()
def fetch_cpis_data(freq: str, country: str, indicator: str, counter_country: str, start: str | int, end: str | int) -> str:
"""
Retrieves compact format time series data from the CPIS database based on the input parameters.
Args:
freq (str): Frequency (e.g., "A" for annual).
country (str): Country code, multiple country codes can be connected with "+".
indicator (str): Indicator code.
counter_country (str): Counterpart country code.
start (str | int): Start year.
end (str | int): End year.
Returns:
str: Description of the queried data. Do not perform further analysis or retry if the query fails.
"""
dimensions = f"{freq}.{country}.{indicator}.T.T.{counter_country}"
url = f"http://dataservices.imf.org/REST/SDMX_JSON.svc/CompactData/CPIS/{dimensions}?startPeriod={start}&endPeriod={end}"
try:
response = requests.get(url)
response.raise_for_status()
data = response.json()
return process_imf_data(data)
except Exception as e:
return f"Error fetching CPIS data: {str(e)}"
@mcp.tool()
def fetch_gfsmab_data(freq: str, country: str, unit: str, indicator: str, start: str | int, end: str | int) -> str:
"""
Retrieves compact format time series data from the GFSMAB database based on the input parameters.
Args:
freq (str): Frequency (e.g., "A" for annual).
country (str): Country code, multiple country codes can be connected with "+".
unit (str): Unit code XDC or XDC_R_B1GQ (Percent of GDP).
indicator (str): Indicator code.
start (str | int): Start year.
end (str | int): End year.
Returns:
str: Description of the queried data. Do not perform further analysis or retry if the query fails.
"""
if "xdc" == unit.lower():
unit = "XDC"
else:
unit = "XDC_R_B1GQ"
dimensions = f"{freq}.{country}.S13.{unit}.{indicator}"
url = f"http://dataservices.imf.org/REST/SDMX_JSON.svc/CompactData/GFSMAB/{dimensions}?startPeriod={start}&endPeriod={end}"
try:
response = requests.get(url)
response.raise_for_status()
data = response.json()
return process_imf_data(data)
except Exception as e:
return f"Error fetching GFSMAB data: {str(e)}"
@mcp.tool()
def fetch_mfs_data(freq: str, country: str, indicator: str, start: str | int, end: str | int) -> str:
"""
Retrieves compact format time series data from the MFS database based on the input parameters.
Args:
freq (str): Frequency (e.g., "A" for annual).
country (str): Country code, multiple country codes can be connected with "+".
indicator (str): Indicator code.
start (str | int): Start year.
end (str | int): End year.
Returns:
str: Description of the queried data. Do not perform further analysis or retry if the query fails.
"""
dimensions = f"{freq}.{country}.{indicator}"
url = f"http://dataservices.imf.org/REST/SDMX_JSON.svc/CompactData/MFS/{dimensions}?startPeriod={start}&endPeriod={end}"
try:
response = requests.get(url)
response.raise_for_status()
data = response.json()
return process_imf_data(data)
except Exception as e:
return f"Error fetching MFS data: {str(e)}"
@mcp.tool()
def fetch_fsi_data(freq: str, country: str, indicator: str, start: str | int, end: str | int) -> str:
"""
Retrieves compact format time series data from the FSI database based on the input parameters.
Args:
freq (str): Frequency (e.g., "A" for annual).
country (str): Country code, multiple country codes can be connected with "+".
indicator (str): Indicator code.
start (str | int): Start year.
end (str | int): End year.
Returns:
str: Description of the queried data. Do not perform further analysis or retry if the query fails.
"""
dimensions = f"{freq}.{country}.{indicator}"
url = f"http://dataservices.imf.org/REST/SDMX_JSON.svc/CompactData/FSI/{dimensions}?startPeriod={start}&endPeriod={end}"
try:
response = requests.get(url)
response.raise_for_status()
data = response.json()
return process_imf_data(data)
except Exception as e:
return f"Error fetching FSI data: {str(e)}"
# 4. Resource: List indicators in the DataMapper API (returns list format)
@mcp.tool()
def list_indicators(dataset_id: str) -> list:
"""
Returns a list of indicators for the specified dataset, read from the corresponding .json file in the local indicators directory.
Args:
dataset_id (str): Dataset ID, such as "IFS", "DOT", "BOP", etc.
Returns:
list: List of indicators.
"""
file_path = os.path.join(os.path.dirname(__file__), "resources", "indicators", f"{dataset_id.lower()}.json")
try:
with open(file_path, 'r', encoding='utf-8') as file:
data = json.load(file)
return data
except FileNotFoundError:
return {"error": f"File not found: {file_path}"}
except json.JSONDecodeError:
return {"error": f"Error decoding JSON from file: {file_path}"}
except Exception as e:
return {"error": f"Error reading file: {str(e)}"}
# 5. Resource: List available countries (according to the IMF DataMapper API)
@mcp.tool()
def list_countries(dataset_id: str) -> list:
"""
Returns a list of available countries for the specified dataset, read from the corresponding .json file in the local areas directory.
Args:
dataset_id (str): Dataset ID, such as "IFS", "DOT", "BOP", etc.
Returns:
list: List of countries.
"""
file_path = os.path.join(os.path.dirname(__file__), "resources", "areas", f"{dataset_id.lower()}.json")
try:
with open(file_path, 'r', encoding='utf-8') as file:
data = json.load(file)
return data
except FileNotFoundError:
return {"error": f"File not found: {file_path}"}
except json.JSONDecodeError:
return {"error": f"Error decoding JSON from file: {file_path}"}
except Exception as e:
return {"error": f"Error reading file: {str(e)}"}
# 6. Prompt: Provide a query prompt template to guide users on how to query data with indicators and intentions
@mcp.prompt()
def imf_query_prompt() -> str:
"""
Returns a prompt template explaining how to query IMF data with indicators and user intentions.
Returns:
str: A prompt template for guiding users on querying IMF data.
"""
prompt_text = """
You are a professional IMF data analysis assistant. Please follow these steps to help users obtain and analyze IMF data:
1. First, use the imf://datasets resource to get a list of available datasets and show the user the 5-10 most commonly used datasets with a brief description.
2. When the user selects a dataset, use the following two tools to get detailed information about the dataset:
- list_countries: List available country or region codes
- list_indicators: List available indicator codes and names
3. Assist the user in determining their interests:
- Country or region (provide codes, and multiple country codes can be connected with "+")
- Indicator (provide codes and names)
- Time range (start year and end year)
- Data frequency (if applicable: annual A, quarterly Q, monthly M, etc.)
4. Based on the user's selection, construct appropriate query parameters and use the fetch_compact_data tool to get the data
** Note: When you get warnings like "Warning: No indicator value" or "Warning: No indicator value for {country} in that Year," it means there is a lack of data for that period. Do not perform further analysis or retry. **
"""
return prompt_text
# Start the MCP server
if __name__ == "__main__":
mcp.run()