diff --git a/docs/function-calling/CUSTOM-TOOLS.md b/docs/function-calling/CUSTOM-TOOLS.md
new file mode 100644
index 0000000..1cabcad
--- /dev/null
+++ b/docs/function-calling/CUSTOM-TOOLS.md
@@ -0,0 +1,119 @@
+# Custom Tools
+Loki is designed to be as flexible and as customizable as possible. One of the key
+features that enables this flexibility is the ability to create and integrate custom tools
+into your Loki setup. This document provides a guide on how to create and use custom tools within Loki.
+
+## Quick Links
+
+- [Supported Languages](#supported-languages)
+- [Creating a Custom Tool](#creating-a-custom-tool)
+ - [Environment Variables](#environment-variables)
+ - [Custom Bash-Based Tools](#custom-bash-based-tools)
+ - [Custom Python-Based Tools](#custom-python-based-tools)
+
+
+---
+
+## Supported Languages
+Loki supports custom tools written in the following programming languages:
+
+* Python
+* Bash
+
+## Creating a Custom Tool
+All tools are created as scripts in either Python or Bash. They should be placed in the `functions/tools` directory.
+The location of the `functions` directory varies between systems, so you can use the following command to locate
+your `functions` directory:
+
+```shell
+loki --info | grep functions_dir | awk '{print $2}'
+```
+
+Once you've created your custom tool, remember to add it to the `visible_tools` array in your global `config.yaml` file
+to enable it globally. See the [Tools](TOOLS.md#enablingdisabling-global-tools) documentation for more information on how Loki utilizes the
+`visible_tools` array.
+
+### Environment Variables
+All tools have access to the following environment variables that provide context about the current execution environment:
+
+| Variable | Description |
+|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------|
+| `LLM_OUTPUT` | Indicates where the output of the tool should go.
In certain situations, this may be set to a temporary file instead of `/dev/stdout`. |
+| `LLM_ROOT_DIR` | The root `config_dir` directory for Loki
(i.e. `dirname $(loki --info \| grep config_file \| awk '{print $2}')`) |
+| `LLM_TOOL_NAME` | The name of the tool being executed |
+| `LLM_TOOL_CACHE_DIR` | A directory specific to the tool for storing cache or temporary files |
+
+Loki also searches the tools directory on startup for a `.env` file. If found, all tools in `functions/tools/` will have
+the environment variables defined in the `.env` file available to them.
+
+### Custom Bash-Based Tools
+To create a Bash-based tool, refer to the [custom bash tools documentation](CUSTOM-BASH-TOOLS.md).
+
+### Custom Python-Based Tools
+Loki supports tools written in Python.
+
+Each Python-based tool must follow a specific structure in order for Loki to be able to properly compile and
+execute it:
+
+* The tool must be a Python script with a `.py` file extension.
+* The tool must have a `def run` function that serves as the entry point for the tool.
+* The `run` function must accept parameters that define the inputs for the tool.
+ * Always use type hints to specify the data type of each parameter.
+ * Use `Optional[...]` to indicate optional parameters
+* The `run` function must return a `str`.
+ * For Python, this is automatically written to the `LLM_OUTPUT` environment variable, so there's no need to explicitly
+ write to the environment variable within the function.
+* The function must also have a docstring that describes the tool and its parameters.
+ * Each parameter in the `run` function should be documented in the docstring using the `Args:` section. They should use the following format:
+ * `: ` Where
+ * ``: The name of the parameter
+ * ``: The description of the parameter
+ * These are *very* important because these descriptions are what's passed to the LLM as the description of the tool,
+ letting the LLM know what the tool does and how to use it.
+
+It's important to note that any functions prefixed with `_` are not sent to the LLM, so they will be invisible to the LLM
+at runtime.
+
+Below is the [`demo_py.py`](../../assets/functions/tools/demo_py.py) tool definition that comes pre-packaged with
+Loki and demonstrates how to create a Python-based tool:
+
+```python
+import os
+from typing import List, Literal, Optional
+
+def run(
+ string: str,
+ string_enum: Literal["foo", "bar"],
+ boolean: bool,
+ integer: int,
+ number: float,
+ array: List[str],
+ string_optional: Optional[str] = None,
+ array_optional: Optional[List[str]] = None,
+):
+ """Demonstrates how to create a tool using Python and how to use comments.
+ Args:
+ string: Define a required string property
+ string_enum: Define a required string property with enum
+ boolean: Define a required boolean property
+ integer: Define a required integer property
+ number: Define a required number property
+ array: Define a required string array property
+ string_optional: Define an optional string property
+ array_optional: Define an optional string array property
+ """
+ output = f"""string: {string}
+string_enum: {string_enum}
+string_optional: {string_optional}
+boolean: {boolean}
+integer: {integer}
+number: {number}
+array: {array}
+array_optional: {array_optional}"""
+
+ for key, value in os.environ.items():
+ if key.startswith("LLM_"):
+ output = f"{output}\n{key}: {value}"
+
+ return output
+```