name is what the API calls the endpoint, and Charter never changes
it. Qualification belongs to whoever assembles tools into one namespace; these
are the functions that do it, so every assembler uses one convention.
Two APIs really do declare the same tool name — products_list exists in both
Stripe and Shopify — so a surface holding both has to disambiguate them.
See Tool names across packs for why the library does not
qualify its own names, and why pack is not provider.
qualified_names
Every tool under <pack>_<tool>, keyed by the qualified name. This is what the
three shipped adapters call, so a pack is named the same way inline, through
LangChain, and over MCP.
Qualified whatever else is loaded. A name is a property of the tool, not of its
neighbours: qualifying only once a second pack appeared would rename every tool
in the first the day someone added one, breaking saved prompts, allow-lists and
logged traces in silence. MCP servers publish the same names whatever else is
installed, and a host composes mcp__<server>__<tool> on top — the MCP entry
point names its server charter, so the result reads
mcp__charter__gcalendar_events_list.
The host builds <server> from the key the server was configured under, and
the whole composed name has to fit the 64 characters OpenAI allows a function
name. One server holding every pack peaks at 60; a server named after the pack
it serves spends eight more on saying the pack a second time and puts two
names over. Which is why --pack takes a list.
stripe_products_list sits beside a bare balance_retrieve makes the pack a
substring of some names and not others, so any filter over it silently misses
the unqualified half.
Raises DeclarationError if a tool
declares no pack, or if two tools qualify to the same name.
qualified_name
One tool’s qualified name.
DeclarationError if the tool
declares no pack. Falling back to the bare name would produce the
half-qualified surface above, silently.
report_key
The row a tool gets in an audit artifact.
egress_map and
conflict_map key their dicts by. The
same qualification as above wherever the tool declares a pack, and the bare name
where it does not, because a report is not a tool surface: nothing dispatches on
these keys, each row carries its pack and name beside the data, and refusing
a pack-less tool would put the artifact out of reach of a tool built by hand.
Both maps raise DeclarationError rather
than let two tools share a row. That guard and this key replace keying on the
bare name, under which twenty-four colliding names across the seventeen shipped packs
turned one assembled surface into 659 rows for 687 tools, naming none of the
twenty-eight it dropped.
Routing a tool call back
MCP and LangChain resolve a tool call by name for you. The OpenAI APIs hand it back to you to execute, so you need the same mapping you published with —qualified_names is it.
Tool directly in Python never involves a name at all — tool.name
stays events_list, and await gcalendar.events_list.ainvoke(...) is a Python
reference. Qualification exists only where tools are published to a model.