With task installed, simply run task to see the list of available commands. For comments, questions, or requests open a GitHub issue.
- Clone the repository:
git clone https://github.com/dbt-labs/dbt-mcp.git
cd dbt-mcp-
Run
task install -
Configure environment variables:
cp .env.example .env
Then edit
.envwith your specific environment variables (see our docs to determine the values). -
Run
task clientto chat with dbt MCP in your terminal.
Refer to ./cursor/rules for standards and stylistic guidelines.
This repo has automated tests which can be run with task test:unit.
The integration tests exercise system interactions greater than just the unit tests. They require a dbt Platform environment that is setup with semantic layer, developer license, and PAT. These tests can be run with task test:integration.
For dbt Labs employees, a staging environment has been set up. Credentials for this environment can be found by searching for dbt MCP Integration Test Credentials in 1Password.
To test in a client like Cursor or Claude, use a configuration file like this:
{
"mcpServers": {
"dbt": {
"command": "<path-to-uv>",
"args": [
"--directory",
"<path-to-this-directory>/dbt-mcp",
"run",
"dbt-mcp",
"--env-file",
"<path-to-this-directory>/dbt-mcp/.env"
]
}
}
}
Or, if you would like to test with Oauth, use a configuration like this:
{
"mcpServers": {
"dbt": {
"command": "<path-to-uv>",
"args": [
"--directory",
"<path-to-this-directory>/dbt-mcp",
"run",
"dbt-mcp",
],
"env": {
"DBT_HOST": "<dbt-host-with-custom-subdomain>",
}
}
}
}
For improved debugging, you can set the DBT_MCP_SERVER_FILE_LOGGING=true environment variable to log to a ./dbt-mcp.log file.
Tools are defined using the @dbt_mcp_tool decorator and registered with the MCP server via register_tools().
- Add a
ToolNameentry insrc/dbt_mcp/tools/tool_names.py - Map it to a toolset in
src/dbt_mcp/tools/toolsets.py(e.g.,DISCOVERY,SEMANTIC_LAYER,SQL) - Write a prompt file in
src/dbt_mcp/prompts/<category>/describing the tool - Define the tool function in the appropriate
tools.pymodule:@dbt_mcp_tool( description=get_prompt("category/tool_name"), title="My Tool", read_only_hint=True, destructive_hint=False, idempotent_hint=True, ) async def my_tool(context: MyToolContext, param: str) -> dict: return await context.fetcher.do_something(param)
- Add it to the tool list (e.g.,
DISCOVERY_TOOLS) in the same module - The registration function (e.g.,
register_discovery_tools) handles context binding and registration
MCP Apps are tools that have an associated interactive UI rendered by the host. They build on top of regular tools with two additions:
-
Use
structured_output=Trueandmetato link the tool to a UI resource:@dbt_mcp_tool( description=get_prompt("category/tool_name"), title="My Visualization", read_only_hint=True, structured_output=True, meta={"ui": {"resourceUri": "ui://dbt-mcp/my-app"}}, ) async def my_viz_tool(context: MyToolContext, param: str) -> MyResultType: ...
The return type must be a
TypedDictwhenstructured_output=True. -
Register an MCP resource at the matching
ui://URI to serve the HTML app:@dbt_mcp.resource( uri="ui://dbt-mcp/my-app", name="My App", mime_type="text/html;profile=mcp-app", ) def get_my_app_ui() -> str: return Path("packages/my-app/dist/index.html").read_text()
-
Build a frontend in
packages/using@modelcontextprotocol/ext-apps. The app receives tool results via theontoolresultcallback and must be bundled as a single HTML file (e.g., usingvite-plugin-singlefile) or reference external resources via CSPresourceDomains.
The ui:// URI convention is ui://<server-name>/<resource-name>. The meta field is passed through the full tool registration pipeline (@dbt_mcp_tool → GenericToolDefinition → adapt_context → register_tools → FastMCP.add_tool).
Before committing changes, ensure that you have set up signed commits. This repo requires signing on all commits for PRs.
Every PR requires a changelog entry. Install changie and run changie new to create a new changelog entry.
The dbt-mcp server runs with stdio transport by default which does not allow for Python debugger support. For debugging with breakpoints, use streamable-http transport.
- Run
task inspector- this starts both the server and inspector automatically - Open MCP Inspector UI
- Use "STDIO" Transport Type to connect
- Test tools interactively in the inspector UI (uses
stdiotransport, no debugger support)
- Set breakpoints in your code
- Press
F5or select "debug dbt-mcp" from the Run menu - Open MCP Inspector UI via
npx @modelcontextprotocol/inspector - Connect to
http://localhost:8000/mcp/v1using "Streamable HTTP" transport and "Via Proxy" connection type - Call tools from Inspector - your breakpoints will trigger
- Run
task dev- this starts the server withstreamable-httptransport onhttp://localhost:8000 - Set breakpoints in your code
- Attach your debugger manually (see debugpy documentation for examples)
- Open MCP Inspector via
npx @modelcontextprotocol/inspector - Connect to
http://localhost:8000/mcp/v1using "Streamable HTTP" transport and "Via Proxy" connection type - Call tools from Inspector - your breakpoints will trigger
Note: task dev uses streamable-http by default. The streamable-http transport allows the debugger and MCP Inspector to work simultaneously without conflicts. To override, use MCP_TRANSPORT=stdio task dev.
If you encounter any problems, you can try running task run to see errors in your terminal.
Only people in the CODEOWNERS file should trigger a new release with these steps:
- Consider these guidelines when choosing a version number:
- Major
- Removing a tool or toolset without replacement, or in a way that would result in agents behaving much differently.
- Changing the behavior of existing environment variables or configurations
- Minor
- Changes to config system related to the function signature of the register functions (e.g.
register_discovery_tools) - Adding optional parameters to a tool function signature
- Adding a new tool or toolset
- Removing or adding non-optional parameters from tool function signatures
- Renaming a tool
- Removing a tool which overlaps or provides similar functionality as other tool(s)
- Changes to config system related to the function signature of the register functions (e.g.
- Patch
- Bug and security fixes - only major security and bug fixes will be back-ported to prior minor and major versions
- Dependency updates which don’t change behavior
- Minor enhancements
- Editing a tool or parameter description prompt
- Adding an allowed environment variable with the
DBT_MCP_prefix
- Trigger the Create release PR Action.
- If the release is NOT a pre-release, select
auto(default) to automatically determine the version bump based on changelog entries, or manually pick patch, minor or major if needed - If the release is a pre-release, set the bump and the pre-release suffix. We support alpha.N, beta.N and rc.N.
- use alpha for early releases of experimental features that specific people might want to test. Significant changes can be expected between alpha and the official release.
- use beta for releases that are mostly stable but still in development. It can be used to gather feedback from a group of peopleon how a specific feature should work.
- use rc for releases that are mostly stable and already feature complete. Only bugfixes and minor changes are expected between rc and the official release.
- Picking the prerelease suffix will depend on whether the last release was the stable release or a pre-release:
| Last Stable | Last Pre-release | Bump | Pre-release Suffix | Resulting Version |
|---|---|---|---|---|
| 1.2.0 | - | minor | beta.1 | 1.3.0-beta.1 |
| 1.2.0 | 1.3.0-beta.1 | minor | beta.2 | 1.3.0-beta.2 |
| 1.2.0 | 1.3.0-beta.2 | minor | rc.1 | 1.3.0-rc.1 |
| 1.2.0 | 1.3.0-rc.1 | minor | 1.3.0 | |
| 1.2.0 | 1.3.0-beta.2 | minor | - | 1.3.0 |
| 1.2.0 | - | major | rc.1 | 2.0.0-rc.1 |
| 1.2.0 | 2.0.0-rc.1 | major | - | 2.0.0 |
- Get this PR approved & merged in (if the resulting release name is not the one expected in the PR, just close the PR and try again step 1)
- This will trigger the
Release dbt-mcpAction. On theSummarypage of this Action a member of theCODEOWNERSfile will have to manually approve the release. The rest of the release process is automated.