Skip to content

Commit cdebe18

Browse files
authored
Merge pull request #181 from Teradata/embed-config-files-in-the-build
Embed config files in the build
2 parents b3ad7a1 + 493b9c3 commit cdebe18

File tree

8 files changed

+1375
-1707
lines changed

8 files changed

+1375
-1707
lines changed

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ working/
1010
env
1111
CLAUDE.md
1212
var/
13-
profiles.yml
14-
*_objects.yml
13+
/# Only ignore root-level overrides
14+
/profiles.yml
15+
/*_objects.yml

MANIFEST.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
include README.md
2+
include LICENSE
3+
recursive-include src/teradata_mcp_server *.py *.yml

docs/server_guide/QUICK_START.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@ uvx teradata-mcp-server --help
3131
If that works, test with your database:
3232

3333
```bash
34-
export DATABASE_URI="teradata://username:password@host:1025/database"
35-
uvx teradata-mcp-server
34+
uvx teradata-mcp-server --database_uri "teradata://username:password@host:1025/database"
3635
```
3736

3837
You should see "NFO Starting MCP server 'teradata-mcp-server' with transport 'stdio'" messages. Press `Ctrl+C` to stop.
@@ -91,6 +90,7 @@ You should see Claude connect to your Teradata database and return results!
9190
**Server won't start?**
9291
- Check your `DATABASE_URI` format
9392
- Verify database connectivity: `ping your-host`
93+
- Rollback to a prior version changing your config file: `"args": ["teradata-mcp-server==0.1.3", "--profile", "all"],`
9494
- See [Installation Guide](INSTALLATION.md) for alternative methods
9595

9696
**Claude can't see tools?**

examples/app-voice-agent/profiles.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,23 @@ profiles:
3333
You will help the user analyze sales data, retrieve customer information, and provide insights based on the tools available.
3434
Your responses should be clear and concise and always grounded in the data retrieved.
3535
When reading order numbers, please read digits by two or individually, separated by pauses. For example, order #1234567 should be read as 'twelve, thirty-four, fifty-six, seven'.
36+
When you fetch detailed data, DO NOT share contact information unless explicitely asked for: for example when you get a customer profile containing an email address and phone number, give the customer name, key metrics, and say "I also have their customer email address and phone number if you need it", and wait for the user confirmation to share the details.
3637
Do not share technical details of your tool interactions and do not use technical jargon or spell out technical attribute names, only the results. For example if you are using a tool to track an customer and get a <customer_key>, 'I identified the customer, it is the customer ' and then read the <customer_key> number or simply the name if you have it.
3738
language: "fr"
3839
voice_id: "ambre"
39-
40+
41+
sales:
42+
mcp_server_url: "http://127.0.0.1:8001/mcp"
43+
system_prompt: |
44+
You are a friendly analyst supporting sales operations. The user and you will engage in a spoken dialog exchanging the transcripts of a natural real-time conversation.
45+
You will help the user analyze sales data, retrieve customer information, and provide insights based on the tools available.
46+
Your responses should be clear and concise and always grounded in the data retrieved.
47+
When reading numbers longer than 4 digits, please read digits individually, separated by pauses, otherwise read the number normally. For example, customer_key #36 should be read as 'customer number thirty-six'; order_nb #34543 should be read as 'order number three, four, five, four, three'.
48+
Do not share technical details of your tool interactions and do not use technical jargon or spell out technical attribute names, only the results. For example if you are using a tool to track an customer and get a <customer_key>, 'I identified the customer, it is the customer ' and then read the <customer_key> number or simply the name if you have it.
49+
When you fetch detailed data, DO NOT share contact information unless explicitely asked for: for example when you get a customer profile containing an email address and phone number, give the customer name, key metrics, and say "I also have their customer email address and phone number if you need it", and wait for the user confirmation to share the details.
50+
language: "en"
51+
voice_id: "matthew"
52+
4053
# Business analyst profile with custom MCP prompt
4154
business_analyst:
4255
mcp_server_url: "http://127.0.0.1:8001/mcp"

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "teradata-mcp-server"
3-
version = "0.1.5"
3+
version = "0.1.5.post1"
44
description = "Model Context Protocol (MCP) server for Teradata, Community edition"
55
readme = {file = "README.md", content-type = "text/markdown"}
66
license = {text = "MIT"}
@@ -23,7 +23,7 @@ classifiers = [
2323
]
2424
dependencies = [
2525
"pydantic>=2.8.0,<3.0.0",
26-
"fastmcp==2.11.3",
26+
"fastmcp>=2.12.0,<3.0.0",
2727
"mcp[cli]>=1.0.0,<2.0.0",
2828
"teradatasqlalchemy>=20.0.0.0",
2929
"python-dotenv>=1.0.0",

src/teradata_mcp_server/tools/module_loader.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -151,26 +151,31 @@ def get_all_functions(self) -> dict[str, Any]:
151151

152152
return all_functions
153153

154-
def get_required_yaml_paths(self) -> list[str]:
154+
def get_required_yaml_paths(self) -> list:
155155
"""
156156
Get the paths to YAML files for only the required modules.
157157
158158
Returns:
159-
List of file paths for YAML files that should be loaded
159+
List of file paths/resources for YAML files that should be loaded
160160
"""
161-
import glob
162-
import os
161+
from importlib.resources import files as pkg_files
163162

164163
yaml_paths = []
165-
base_path = os.path.dirname(__file__)
166-
167-
for module_name in self._required_modules:
168-
if module_name in self.MODULE_MAP:
169-
# Get YAML files for this specific module
170-
module_dir = os.path.join(base_path, module_name)
171-
if os.path.exists(module_dir):
172-
pattern = os.path.join(module_dir, "*.yml")
173-
yaml_paths.extend(glob.glob(pattern))
164+
165+
try:
166+
tools_pkg_root = pkg_files("teradata_mcp_server").joinpath("tools")
167+
if tools_pkg_root.is_dir():
168+
for module_name in self._required_modules:
169+
if module_name in self.MODULE_MAP:
170+
module_dir = tools_pkg_root.joinpath(module_name)
171+
if module_dir.is_dir():
172+
for entry in module_dir.iterdir():
173+
if entry.is_file() and entry.name.endswith('.yml'):
174+
yaml_paths.append(entry)
175+
except Exception as e:
176+
import logging
177+
logger = logging.getLogger("teradata_mcp_server")
178+
logger.error(f"Failed to load packaged YAML files: {e}")
174179

175180
return yaml_paths
176181

src/teradata_mcp_server/utils.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,22 +161,46 @@ def load_profiles(working_dir: Optional[Path] = None) -> Dict[str, Any]:
161161

162162
# Load packaged profiles.yml
163163
try:
164-
config_files = pkg_files("teradata_mcp_server.config")
165-
profiles_file = config_files / "profiles.yml"
166-
if profiles_file.is_file():
167-
profiles.update(yaml.safe_load(profiles_file.read_text(encoding='utf-8')) or {})
164+
import importlib.resources
165+
try:
166+
# Try the new importlib.resources API (Python 3.9+)
167+
config_files = importlib.resources.files("teradata_mcp_server.config")
168+
profiles_file = config_files / "profiles.yml"
169+
logger.debug(f"Looking for packaged profiles at: {profiles_file}")
170+
if profiles_file.is_file():
171+
packaged_profiles = yaml.safe_load(profiles_file.read_text(encoding='utf-8')) or {}
172+
profiles.update(packaged_profiles)
173+
logger.info(f"Loaded {len(packaged_profiles)} packaged profiles: {list(packaged_profiles.keys())}")
174+
else:
175+
logger.warning(f"Packaged profiles.yml not found at: {profiles_file}")
176+
except AttributeError:
177+
# Fallback for older Python versions
178+
import importlib.resources as resources
179+
with resources.path("teradata_mcp_server.config", "profiles.yml") as profiles_path:
180+
logger.debug(f"Looking for packaged profiles at: {profiles_path}")
181+
if profiles_path.exists():
182+
packaged_profiles = yaml.safe_load(profiles_path.read_text(encoding='utf-8')) or {}
183+
profiles.update(packaged_profiles)
184+
logger.info(f"Loaded {len(packaged_profiles)} packaged profiles: {list(packaged_profiles.keys())}")
185+
else:
186+
logger.warning(f"Packaged profiles.yml not found at: {profiles_path}")
168187
except Exception as e:
169-
logger.error(f"Failed to load packaged profiles: {e}")
188+
logger.error(f"Failed to load packaged profiles: {e}", exc_info=True)
170189

171190
# Load working directory profiles.yml (overrides packaged)
172191
profiles_path = working_dir / "profiles.yml"
173192
if profiles_path.exists():
174193
try:
175194
with open(profiles_path, encoding='utf-8') as f:
176-
profiles.update(yaml.safe_load(f) or {})
195+
working_dir_profiles = yaml.safe_load(f) or {}
196+
profiles.update(working_dir_profiles)
197+
logger.info(f"Loaded {len(working_dir_profiles)} working directory profiles: {list(working_dir_profiles.keys())}")
177198
except Exception as e:
178199
logger.error(f"Failed to load external profiles: {e}")
200+
else:
201+
logger.debug(f"No working directory profiles.yml found at: {profiles_path}")
179202

203+
logger.info(f"Total profiles loaded: {list(profiles.keys())}")
180204
return profiles
181205

182206

0 commit comments

Comments
 (0)