name is what the API calls the endpoint. events_list is
events_list whether you hand it to LangChain, publish it over MCP, or call it
directly. Charter does not prefix it.
That is not a gap. It is the convention every surface Charter targets already
follows: an MCP server publishes plain names and the host composes
mcp__<server>__<tool> from the server it was configured with; OpenAI
constrains a function name to [A-Za-z0-9_-]{1,64} and defines no namespace at
all; LangChain resolves a call against the list you gave it. Qualifying inside
the library would produce mcp__gcalendar__gcalendar__events_list the moment a
host did its job.
So the rule is that whoever assembles tools into one namespace owns the
names — and Charter ships the one convention they should use.
The collision
Two packs can declare the same tool name, because two APIs can.pack, and why it is not provider
Every tool carries three pieces of identity, answering three different
questions. All three are needed.
provider cannot stand in for pack: gmail, gcalendar, gsheets, gdocs, gdrive and
gforms all authenticate as google and deliberately share one refresh token. Naming by
provider would collapse six packs into one namespace.
base_url cannot either: gmail, calendar, sheets, docs and forms use five different
hosts (Drive shares Calendar’s), so the mapping runs the wrong way, and shopify’s base URL is not known until
configure() is called.
Declare it once, on the factory:
Qualifying
mcp__<server>__<tool> on top, and charter-mcp names
its server charter, so the result reads
mcp__charter__gcalendar__events_list — each segment a different fact.
All of them, or none of them. Neither function qualifies “just the
collisions”. A surface where stripe__products_list sits beside a bare
balance_retrieve makes the pack a substring of some names and not others, so
every filter over it — a +stripe in a tool search, a log grep, an allow-list —
silently misses the unqualified half. A filter that works on most names is worse
than none, because nothing tells you which half you got.
The separator is doubled. Tool names contain single underscores, so
stripe_products_list cannot be split back into its pack and its tool while
stripe__products_list can. MCP doubles it for the same reason.
A tool with no pack cannot be qualified, and saying so is a DeclarationError
rather than a silent fall back to the bare name — which would produce exactly
the half-qualified surface above.