tdfs4ds.agent.mcp_connection¶
tdfs4ds.agent.mcp_connection
¶
Connection building for the external MCP server.
There are two Teradata MCP products, and tdfs4ds has to talk to either:
- Community Edition — embeds its own credentials, so tdfs4ds sends none.
This is the default and the historical behaviour: with none of the
MCP_*auth variables set, the connection dict produced here is byte-identical to the hardcoded{"url": ..., "transport": ...}that preceded this module. - Enterprise — requires HTTP Basic auth supplied by the caller, and exposes a different tool set.
The edition is therefore a real distinction (tdfs4ds.MCP_SERVER_EDITION),
not merely an auth switch, and it gates whether credentials are sent at all.
That gating matters: TDFS4DS_TD_USERNAME/TD_PASSWORD are set in
essentially every deployment, so deriving Basic auth from them unconditionally
would make every existing install start sending the Teradata password to
its already-configured Community endpoint on upgrade — a credential reaching
somewhere it does not reach today, opted into by nobody.
This module is the single seam: the consumer agent
(_skill_mcp_tools_async) and tdfs4ds.check_configuration both build
their connection here, so a diagnostic that passes describes the connection the
agent actually makes.
Nothing here logs or returns a credential value — only variable names.
_TOOL_STOP_WORDS
module-attribute
¶
_TOOL_STOP_WORDS = frozenset({'a', 'an', 'and', 'are', 'as', 'at', 'be', 'by', 'can', 'do', 'does', 'for', 'from', 'give', 'has', 'have', 'how', 'i', 'in', 'is', 'it', 'list', 'me', 'my', 'of', 'on', 'or', 'please', 'show', 'that', 'the', 'their', 'them', 'there', 'these', 'this', 'to', 'what', 'when', 'where', 'which', 'who', 'with', 'you', 'your'})
_CONTEXT_WINDOW_MARKERS
module-attribute
¶
_CONTEXT_WINDOW_MARKERS = ('contextwindowexceeded', 'context window', 'context length', 'maximum context', 'too many tokens', 'prompt is too long', 'exceed context limit', 'reduce the length of the messages', 'context_length_exceeded')
_SELECTION_PROMPT
module-attribute
¶
_SELECTION_PROMPT = 'You are selecting tools, not answering.\n\nBelow is every tool available, one per line, as `name — description`.\nReturn ONLY the names of the tools needed to answer the question, one per line, most important first, at most {limit}. No numbering, no prose, no explanation. If none apply, return nothing.\n\nTOOLS:\n{index}\n\nQUESTION: {question}'
mcp_edition
¶
Return the configured MCP edition, normalised to 'community' or
'enterprise'.
An unrecognised value falls back to 'community' (the safe side: send no
credentials) with a warning naming the accepted values — never silently.
_auth_mode
¶
Resolve the auth mechanism: explicit MCP_AUTH wins, else derived
from the edition (enterprise ⇒ Basic, community ⇒ none).
_basic_credentials
¶
Return (username, password, user_source, password_source).
Both fall back to the Teradata connection credentials: the Teradata MCP
server authenticates with the database identity, so there is nothing to
restate. *_source are variable NAMES, for diagnostics.
build_mcp_headers
¶
Return the HTTP headers for the MCP connection, or None.
None means no headers key at all in the connection dict — the
Community Edition path, identical to the behaviour before this module
existed.
Resolution order:
MCP_SERVER_HEADERS— used verbatim, for any scheme Basic does not cover (Bearer, API key, ...). Setting it implies auth on its own.- Basic, when the resolved mode is
'basic'(MCP_AUTH, else enterprise edition), fromMCP_USERNAME/MCP_PASSWORDfalling back to the Teradata credentials. None.
A half-configured Basic setup returns None with a prescriptive warning
rather than sending a malformed header.
describe_mcp_auth
¶
One-line, secret-free summary of what will be sent.
Names the variable each part came from so a wrong-account failure is diagnosable without ever revealing a value.
_ca_client_factory
¶
Build an McpHttpClientFactory that trusts ca_bundle.
Only the trust anchor changes: verification stays on, and the MCP SDK's
own client defaults (follow_redirects=True, 30 s / 300 s timeouts) are
reproduced so supplying a CA bundle does not silently alter transport
behaviour.
The SSL context is built here, not per request, so an unreadable or
malformed bundle fails immediately with a message naming the variable —
rather than as an opaque [X509] PEM lib error at connect time. A bad
bundle is never downgraded to default verification: that would weaken TLS
invisibly.
_tool_tokens
¶
Lower-cased word tokens of text, for relevance matching.
Splits camelCase as well as separators, and adds a crude singular form,
because MCP tool names are camelCase compounds while questions are English:
without both, base_databaseList never matches "databases" and every tool
scores zero, silently reducing the ranking to "whatever the server listed
first".
without_client_side_plot_tools
¶
The MCP tools minus the plot_* ones.
teradata-mcp-server's plot_* tools read the rows out of the database and
return Chart.js data drawn in the client. Charts are drawn in the database by
TD_PLOT instead (the PLOT skill, :mod:tdfs4ds.plotting), so these tools are
not bound. The list is returned unchanged when it holds none.
select_tools
¶
Return at most max_tools tools, most relevant to question first.
Every tool's JSON schema is sent to the model on each turn, so the tool set
is a context-window cost paid before the question is even read. The
Enterprise MCP edition exposes ~132 tools where the Community one exposes a
few dozen — enough to overflow a model outright
(litellm.ContextWindowExceededError), which is exactly the "different
tool set" difference MCP_SERVER_EDITION exists to describe.
Under the cap the list is returned untouched, so a Community-edition install binds precisely the tools it always did — the cap only engages where the alternative is failing.
Relevance is a deterministic token overlap against each tool's name and description — no LLM, no extra round trip. Ties keep the server's ordering, and unmatched tools still fill the remaining slots so an unforeseen phrasing degrades instead of emptying the toolbox.
is_context_window_error
¶
Is exc the model refusing an over-long prompt?
Recognised across gateways because each words it differently: LiteLLM/Bedrock
say ContextWindowExceededError, OpenAI maximum context length,
Anthropic prompt is too long. Checked against the flattened exception
text plus any code the SDK attaches, since the useful string is often
nested inside a generic BadRequestError.
A false positive costs one wasted retry; a false negative leaves the caller with the hard failure it already had.
build_tool_index
¶
Render tools as a compact name — description text index.
This is the whole point of the two-phase fallback: a tool's JSON schema (nested parameter objects, types, enums) is an order of magnitude larger than one line naming it. ~130 tools fit in a couple of thousand tokens here, where their schemas do not fit at all.
Truncated to max_chars so a server exposing thousands of tools cannot turn the fallback into the same failure it exists to avoid.
parse_tool_selection
¶
Map a model's reply onto real tool objects.
Every name is verified against the actual tool set and anything unrecognised
is dropped — a model asked for names will occasionally invent, number or
decorate them, and binding a hallucinated name would fail at call time.
Substring matching catches the decorated cases (1. base_tableList or
`base_tableList`) without inventing anything.
select_tools_via_model
¶
Ask llm which of tools the question needs — the two-phase fallback.
Used only after binding the tool schemas has already failed on context size, so the extra model call is never paid on a working path. llm is injected rather than built here, keeping this module free of any dependency on the agent (and making the whole path testable without a model).
Returns [] when the model names nothing usable, which the caller should
read as "give up and report", not as "bind nothing".
build_mcp_connection
¶
Return the connection dict for MultiServerMCPClient.
Optional keys are added only when configured, so an install using none of
the MCP auth/TLS variables gets exactly {"url": ..., "transport": ...}
— the shape that predates this module.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
server_url
|
Endpoint; defaults to |
None
|
|
transport
|
|
None
|