Skip to main content
Everything Charter reads at run time, in one place: how a pack is given its credential, which parameters change behaviour rather than shape, and what the library does with logging.

configure()

Every shipped pack builds its tools at import, so from charter.packs.gmail import TOOLS works before anything is configured — schemas, llm_schema() and to_json_schema() need no credential. The credential arrives afterwards, through the pack’s configure().
A credential is resolved per request, not captured when the tool is built. That is why configure() reaches tools that already exist, and why a reference taken before configuration keeps working after it. Calling a tool on a pack that has none raises CredentialError naming both the configure() call and the environment variable it would have accepted. It fails locally, before the request.
Three shapes, depending on how the API authenticates.

Bearer packs

gmail, gcalendar, gsheets, gdocs, gdrive, gforms, slack, github, notion.
Pass any CredentialProvider: a StaticTokenProvider for a script, an OAuth2Client for a process that stays up, a SubjectProvider wrapping one of those per end user.

API-key packs

stripe, linear, firecrawl, granola. An empty key raises CredentialError.

Shopify

Two values, because the host is a property of the store rather than of the API. shop is the subdomain — "my-store" for my-store.myshopify.com — and the full domain or URL is accepted too. access_token is an Admin API access token.

Environment fallbacks

A pack with no configure() call falls back to a documented variable, read on first use. The bearer variables are read per call, so a sidecar that refreshes one is picked up without a restart. The API-key variables are read at import. This is the path the MCP entry point uses, which is why it needs no configuration code. A raw access token expires in about an hour: for a server that stays up, configure the pack with an OAuth2Client in your own process instead.

Settings that tune behaviour

These change how a request is made rather than what it contains. All are factory parameters unless noted, and each can be overridden per tool.
int
default:"20"
Request timeout in seconds. Per tool: timeout_override.
int
default:"10"
How dead a bearer token must be before the runtime refuses to send it. On oauth_tool_factory only. This is a guard, not a refresh policy: keeping a token fresh is the provider’s job, and OAuth2Client renews 90 seconds early by default. Ten seconds covers clock skew and the request itself — refusing a token the server still calls valid for a minute would be the runtime overruling the server.
Optional[Iterable[int]]
default:"{401}"
Which statuses raise CredentialError rather than APIError. Declare {401, 403} for an API that means “your token” by 403.
Optional[CallSink]
default:"None"
Where finished ToolCall records go. Declared on the factory because observability belongs to the deployment, not to one endpoint. Per tool: on_call_override.
Optional[Dict[str, str]]
default:"None"
Headers sent verbatim on every request — an Azure api-version, a Notion-Version. Not case-converted, absent from the LLM schema, applied after tool input so a schema field cannot overwrite them.
Optional[Dict[str, Any]]
default:"None"
Query parameters sent verbatim on every request. Same three properties.
Optional[Dict[str, Any]]
default:"None"
Body keys sent verbatim. Per tool only: a constant in the body belongs to one operation, the GraphQL query document being the case that motivated it.
KeyCase
default:"\"camel\", \"snake\", \"snake\""
How keys are spelled on the wire. Per tool: body_case_override, query_case_override, path_case_override. Per field: Case. See the cascade.
"json" | "form"
default:"\"json\""
"form" for application/x-www-form-urlencoded. Per tool: body_format_override.
"repeat" | "bracket"
default:"\"repeat\""
How structured query values serialise. Factory-wide; there is no per-tool override.
Nothing here is read from a config file or an environment variable other than the credentials above. A setting is a parameter, in code, at the place the tool is declared.

Logging

The library attaches a NullHandler to the charter logger and configures nothing else for its host: emitting a record is a no-op until the application opts in. Enabling INFO also switches on payload measurement, because something is then listening for the sizes.