Skip to main content
Charter is not an agent framework and does not ship an agent loop. It is the layer your framework calls through, so an adapter is a projection of a Tool: it hands the framework an argument schema and a coroutine, and execution stays in Charter. That means tools stay built once. The credential lookup happens below every adapter, so serving many users works unchanged whichever one you use — or none.
Every adapter publishes <pack>_<tool>. Both factories require a pack for that reason, so a tool built by one always has a name an assembler can use. A Tool constructed directly does not — that case is for something you only ever ainvoke yourself — and publishing one is refused here rather than given a bare name that a +stripe filter or a log grep would silently miss. The naming rules are the argument.

OpenAI

openai_tools.py
Plain function definitions — the same JSON Schema Tool.to_json_schema produces — so this needs no extra. The same output works anywhere the OpenAI tool format is accepted.

LangChain

langchain_tools.py
Returns a StructuredTool per tool, whose args_schema is the Charter LLM view (Tool.llm_schema) and whose coroutine is Tool.ainvoke. LangChain supplies the calling convention; it does not touch serialization, casing, or field policy. Validation failures surface the way LangChain expects, so a model that sends a malformed argument gets a message it can act on rather than a stack trace. See tool validation and error handling.
Do not let a client flatten the schema on the way out. Charter emits $ref/$defs, and support for references in tool schemas is uneven, so some code resolves them before sending. Resolving is multiplicative, and against a cyclic schema — Linear’s GraphQL filters, for instance — there is no size it converges to. langchain_core 1.6.1 does this in convert_to_openai_function, which calls dereference_refs and discards $defs; on the uncurated linear.teams_list_full it had not returned after 45 seconds and had allocated 1.92 GB.to_langchain_tools does not do this. If something downstream of you does, the fix is to remove the cycle with a projection rather than to bound the expansion.
This one needs the extra:
The import is deferred, so a missing extra is a clear error at the moment you build the tools rather than a dependency everyone carries:

Passing the host’s context

A tool with a FromContext field needs the host to say whom each call is for, and every adapter carries that through, from wherever the framework already keeps it. The model never supplies it: the field is not in the schema any adapter publishes. OpenAI. The run’s context goes to every call it makes:
LangChain agents. CharterMiddleware reads the agent’s own context, the one agent.ainvoke(..., context=...) is given, on every tool call. One agent serves every user, and a dataclass, a TypedDict or a pydantic model all work:
Outside an agent, to_langchain(tool, context={...}) and to_langchain_tools(tools, context={...}) bind one mapping to every call; wrap per request when it varies by user. MCP. build_server(tools, context=...) takes a mapping, for a stdio server, which is one process for one user, or a function of the MCP request context, sync or async, called for every tool call, for a server that learns the user from the request:
A call whose context lacks a required value is answered with a ContextError, whose message tells the model the application has to fix it and that a different call will not help.

Sending schemas on demand

A ToolSession sends the schemas of the tools the model has reached for, plus a ToolSearch that loads the rest, so Gmail and Stripe together cost 924 tokens on the first turn instead of 41,361. Its tool list grows, which means the loop has to rebuild that list every turn. Charter ships the loop rather than asking you to remember:
Both run every Charter tool through Tool.ainvoke exactly as a statically bound tool would be. A loaded tool is the same fully typed tool it would have been. to_openai_tools and to_langchain_tools both accept a session too, if you own your loop and want to drive the projection yourself. See run and CharterMiddleware.
Serving over the Model Context Protocol is a compile target rather than an adapter, and has its own page.