diff --git a/src/language/ast/ASTNodeDataType.cpp b/src/language/ast/ASTNodeDataType.cpp
index ce2bbcc60182e0fe025d4836ee43fe0cb6d37a59..1f0847f865a6972e7865f4b50601898ab5930616 100644
--- a/src/language/ast/ASTNodeDataType.cpp
+++ b/src/language/ast/ASTNodeDataType.cpp
@@ -84,6 +84,24 @@ dataTypeName(const ASTNodeDataType& data_type)
   return name;
 }
 
+std::string
+dataTypeName(const std::vector<ASTNodeDataType>& data_type_vector)
+{
+  if (data_type_vector.size() == 0) {
+    return dataTypeName(ASTNodeDataType::build<ASTNodeDataType::void_t>());
+  } else if (data_type_vector.size() == 1) {
+    return dataTypeName(data_type_vector[0]);
+  } else {
+    std::ostringstream os;
+    os << '(' << dataTypeName(data_type_vector[0]);
+    for (size_t i = 1; i < data_type_vector.size(); ++i) {
+      os << ',' << dataTypeName(data_type_vector[i]);
+    }
+    os << ')';
+    return os.str();
+  }
+}
+
 ASTNodeDataType
 dataTypePromotion(const ASTNodeDataType& data_type_1, const ASTNodeDataType& data_type_2)
 {
diff --git a/src/language/ast/ASTNodeDataType.hpp b/src/language/ast/ASTNodeDataType.hpp
index 98dfaecf8dc06da4b0b42090fe9ea9bd411ddc50..951aff6e4d070e4747f5eb3f5a5fcc93f8ba5b8e 100644
--- a/src/language/ast/ASTNodeDataType.hpp
+++ b/src/language/ast/ASTNodeDataType.hpp
@@ -14,6 +14,8 @@ class ASTNodeDataType;
 
 ASTNodeDataType getVectorDataType(const ASTNode& type_node);
 
+std::string dataTypeName(const std::vector<ASTNodeDataType>& data_type_vector);
+
 std::string dataTypeName(const ASTNodeDataType& data_type);
 
 ASTNodeDataType dataTypePromotion(const ASTNodeDataType& data_type_1, const ASTNodeDataType& data_type_2);
diff --git a/src/language/modules/CoreModule.cpp b/src/language/modules/CoreModule.cpp
index d3408934d649ffc625ad84c5bbe206a91f81555d..14a9548238c91682aac53850a6cdf6f36b4d9e93 100644
--- a/src/language/modules/CoreModule.cpp
+++ b/src/language/modules/CoreModule.cpp
@@ -30,4 +30,15 @@ CoreModule::CoreModule() : BuiltinModule(true)
                                                      }
 
                                                      ));
+
+  this->_addBuiltinFunction("getModuleInfo", std::make_shared<BuiltinFunctionEmbedder<std::string(const std::string&)>>(
+
+                                               [](const std::string& module_name) -> std::string {
+                                                 const ModuleRepository& repository =
+                                                   ASTExecutionInfo::current().moduleRepository();
+
+                                                 return repository.getModuleInfo(module_name);
+                                               }
+
+                                               ));
 }
diff --git a/src/language/modules/ModuleRepository.cpp b/src/language/modules/ModuleRepository.cpp
index 13cc6f079c90d0ba2f621e6eb97ae04b0c830d52..abb5d527d4858f86ff3a25e68f4d666c2da8193b 100644
--- a/src/language/modules/ModuleRepository.cpp
+++ b/src/language/modules/ModuleRepository.cpp
@@ -11,6 +11,7 @@
 #include <language/utils/BuiltinFunctionEmbedder.hpp>
 #include <language/utils/ParseError.hpp>
 #include <language/utils/SymbolTable.hpp>
+#include <language/utils/TypeDescriptor.hpp>
 #include <utils/PugsAssert.hpp>
 
 #include <algorithm>
@@ -115,3 +116,37 @@ ModuleRepository::getAvailableModules() const
 
   return os.str();
 }
+
+std::string
+ModuleRepository::getModuleInfo(const std::string& module_name) const
+{
+  std::stringstream os;
+  auto i_module = m_module_set.find(module_name);
+  if (i_module != m_module_set.end()) {
+    os << rang::fgB::yellow << "Module '" << rang::fgB::blue << module_name << rang::fgB::yellow << "' provides"
+       << rang::style::reset << '\n';
+    const auto& builtin_function_map = i_module->second->getNameBuiltinFunctionMap();
+    if (builtin_function_map.size() > 0) {
+      os << "  functions\n";
+      for (auto& [name, function] : builtin_function_map) {
+        os << "    " << rang::fgB::green << name << rang::style::reset << ": ";
+        os << dataTypeName(function->getParameterDataTypes());
+        os << rang::fgB::yellow << " -> " << rang::style::reset;
+        os << dataTypeName(function->getReturnDataType()) << '\n';
+      }
+    }
+
+    const auto& builtin_type_map = i_module->second->getNameTypeMap();
+    if (builtin_type_map.size() > 0) {
+      os << "  types\n";
+      for (auto& [name, descriptor] : builtin_type_map) {
+        os << "    " << rang::fgB::green << name << rang::style::reset << '\n';
+      }
+    }
+
+  } else {
+    throw NormalError(std::string{"could not find module "} + module_name);
+  }
+
+  return os.str();
+}
diff --git a/src/language/modules/ModuleRepository.hpp b/src/language/modules/ModuleRepository.hpp
index 5a211f2bb7def2c0d983ebf994009b16044cbf7b..c224f6491521ad9757ad5253011604e863a10503 100644
--- a/src/language/modules/ModuleRepository.hpp
+++ b/src/language/modules/ModuleRepository.hpp
@@ -31,6 +31,7 @@ class ModuleRepository
   void populateMandatorySymbolTable(const ASTNode& root_node, SymbolTable& symbol_table);
 
   std::string getAvailableModules() const;
+  std::string getModuleInfo(const std::string& module_name) const;
 
   const ModuleRepository& operator=(const ModuleRepository&) = delete;
   const ModuleRepository& operator=(ModuleRepository&&) = delete;