Coverage
Gmail
23 tools · OAuth bearer
Google Calendar
13 tools · OAuth bearer
Google Sheets
17 tools · OAuth bearer
Google Docs
3 tools · OAuth bearer
Google Drive
25 tools · OAuth bearer
Google Forms
6 tools · OAuth bearer
Slack
18 tools · OAuth bearer
GitHub
139 tools · OAuth bearer
Stripe
59 tools · API key
Linear
128 tools · API key
Shopify
22 tools · API key
Notion
35 tools · OAuth bearer
Granola
9 tools · API key
Firecrawl
43 tools · API key
Tavily
9 tools · API key
Compatibility with agents
Every pack exportsTOOLS, which is what an adapter takes:
packs_to_langchain_tools.py
TOOLS is the default list, not the complete one. A pack may build tools it
leaves out.
What the hard cases taught the runtime
Slack — failure arrives as HTTP 200
Slack — failure arrives as HTTP 200
Slack reports failure as HTTP 200 with
{"ok": false, "error": "..."}, so a
generic HTTP client hands the model an error payload as if the message had
sent.That is not patched per tool. It is declared once, as an
Envelope on the factory, and enforced by the runtime on
every call — including calls by tools somebody adds next year. The same one
line handles any GraphQL API, whose failures are always a 200 with an
errors array. Read more.Stripe — form encoding and a derived cursor
Stripe — form encoding and a derived cursor
Form-encoded in both directions with bracket notation
(
line_items[0][price]=…), and a cursor that is the last object’s id rather
than a token the API hands back (data[-1].id). Both are declared once on
the factory. Read more.GitHub — OAuth plus constant headers
GitHub — OAuth plus constant headers
A bearer token from your credential provider, alongside an
Accept, an
X-GitHub-Api-Version and a User-Agent that GitHub requires and the model
must never see. Those go in static_headers.GitHub also pages by number rather than by cursor — ask for page 1, 2, 3 and
stop when a page comes back short — which is the second pagination style
Pagination declares. Read more.Linear and Shopify — GraphQL, and why static_body exists
Linear and Shopify — GraphQL, and why static_body exists
Every tool in a GraphQL pack POSTs to the same URL. What makes Shopify additionally has no fixed host: a store lives at
issues_list
different from issue_create is the query document, which is a constant
belonging to the tool rather than a parameter. Putting it in static_body
keeps it off the schema entirely.That is the whole boundary: a GraphQL endpoint accepts arbitrary documents,
so a tool that let the model write the query would not be an integration, it
would be a shell.Both packs also carry a failure GraphQL’s own errors array does not. A
mutation the server understood and then declined comes back HTTP 200, with no
errors, and the refusal in the payload — Linear’s success: false,
Shopify’s userErrors. Every signal a runtime normally trusts says the write
happened. So Envelope field names are paths, with * standing for
whichever operation the tool called:https://{shop}.myshopify.com/, so its base URL is resolved per request from
configure() rather than being a schema field the model could point
elsewhere. Linear · Shopify.Writing your own pack
Twelve packs will never cover the API you actually work with. That is the normal case, and the answer is not to wait for a twelfth. A pack is declarations rather than integration code, which makes writing one small: the sameoauth_tool_factory or
api_key_tool_factory call that
every pack page prints in full, then one schema per endpoint.
And you mostly do not write it yourself. The pack-writing
skill is a single markdown file that gives a coding agent the order
to work in and the reading it must not skip, which is the step that decides
whether a pack is right. The conformance suite then
holds the result to the same rules as the eleven above.
Improving existing packs
A pack covers the endpoints most people reach for, not all of them. When yours is missing,TOOLS is an ordinary list:
packs_extend.py