Every SaaS your agents need.
One credential store.
Register each service once with a CredentialRecord. Every tool in the stack — Agent, Autopilot, ShipCrew — picks it up automatically. OAuth, API tokens, Basic auth, custom headers, rate-limit detection, least-privilege gates: all handled.
17 SaaS connectors.
One line each.
Every connector is a real Tool the agent picks alongside web_search, RAG, structured output. Brand-coloured, credential-stored, OAuth-ready.
from shipit_agent import Agentfrom shipit_agent.tools import GmailToolagent = Agent(llm=opus_llm,tools=[GmailTool(credential_key="gmail-prod")],)result = agent.run("Find the most recent email from Acme Corp and ""summarise the action items.")
Search inbox, read messages, threads, attachments. Send drafts.
Four steps.
Works for every connector.
Same pattern whether you're wiring up Gmail or Salesforce. The full per-provider walkthrough — exact tokens, scopes, record shapes — lives in the docs.
Build a CredentialStore
InMemoryCredentialStore for tests/notebooks. FileCredentialStore for dev. Subclass for Vault / AWS Secrets / GCP Secret Manager in prod.
from shipit_agent import FileCredentialStorestore = FileCredentialStore(".shipit_credentials.json")
Register each service once
Drop a CredentialRecord per service. Tiny shape — secrets (token / api_key / email+api_token) plus metadata (base_url, auth_scheme).
from shipit_agent import CredentialRecordstore.set(CredentialRecord(key="github", provider="github",secrets={"token": "github_pat_..."},))store.set(CredentialRecord(key="salesforce", provider="salesforce",secrets={"access_token": "00D..."},metadata={"base_url": "https://acme.my.salesforce.com","auth_scheme": "Bearer",},))
Hand the store to your tools
Every connector takes credential_store=. One store, every tool, no glue. Autopilot and ShipCrew share it across child agents automatically.
from shipit_agent import Agentfrom shipit_agent.tools import GitHubTool, SalesforceToolagent = Agent(llm=llm,tools=[GitHubTool(),SalesforceTool(allow_writes=False),],credential_store=store,)
Handle rate limits + auth expiry
Every connector returns structured failure metadata — rate_limited with retry_after_seconds, auth_expired for refresh flows, writes_disabled when allow_writes is off. Your loop just reacts.
out = agent.run("Summarise open PRs in acme/api")for r in out.tool_results or []:if r.metadata.get("error") == "rate_limited":# Wait until retry_after_seconds, retry.passelif r.metadata.get("error") == "auth_expired":# Refresh the OAuth token, re-register, retry.pass