Skip to main content

connector-for-ai-agents

1,586 API connectors for Python — every auth field each one needs, and the code that turns filled-in fields into a working, verified connection. Sync and async.

PyPI Python CI Licence

Building an AI agent, an integrations page, or an internal tool that has to talk to Stripe, Slack, HubSpot, Notion, Jira, Shopify and 1,581 other APIs? The hard part is rarely the HTTP call. It is knowing that this provider wants a bearer token and that one wants X-API-KEY, that a third needs a client-credentials exchange against a per-tenant domain, and which endpoint proves the credentials actually work.

This package is that knowledge, as data and code you can call:

from connector_manager import ConnectorManager

with ConnectorManager() as manager:
    connection = manager.connect("sendgrid", credentials={"apiKey": "SG.…"})
    print(connection.verified)                       # True — checked against the live API
    print(manager.request(connection, "GET", "/v3/scopes").json())

No network needed to browse the catalogue. No OAuth server to run. No secrets stored on your behalf.


Contents

Why this exists

  • 1,586 connectors, bundled. Auth mode, field definitions, base url and a verification endpoint for each. No API calls to browse them, no rate limits, no service to sign up for. Works offline and in air-gapped builds.
  • 1,190 connect end to end from user input alone. API keys, basic auth, client credentials, multi-step token exchanges, locally signed JWTs, WSSE and OAuth 1.0a request signing all complete inside the library.
  • Verified, not assumed. connect() calls the provider's own verification endpoint, so you learn the credentials are wrong at the moment they are entered, not on the first real request an hour later.
  • One implementation, sync and async. Each auth flow is written once as a generator of requests and driven by either client, so the two managers cannot drift apart.
  • Built for forms and for tools. Every connector describes its own fields — title, description, example, regex, enum, secret, order — so you can render a connection form, or hand an LLM a tool spec, straight from the catalogue.
  • Typed, tested, small. Full type hints with py.typed, 98 tests covering every connector in the catalogue, and four runtime dependencies.
  • Your security model stays yours. Connections come back as plain objects. Where they live and how they are encrypted is your call.
┌─ this package ─────────────────────────────┐   ┌─ your application ───────┐
│ list connectors  (id, name, icon, auth)    │   │ OAuth redirect flow      │
│ describe auth    (which fields, validation)│   │ secret storage/encryption│
│ connect          (token exchange + verify) │-->│ connection persistence   │
│ refresh / authenticated request            │<--│ tenants, users, policies │
└────────────────────────────────────────────┘   └──────────────────────────┘

Install

pip install connector-for-ai-agents
uv add connector-for-ai-agents

Python 3.10+. Runtime dependencies: httpx, PyYAML, PyJWT, cryptography. The distribution carries the connector definitions and all 1,591 logos, so nothing is fetched at runtime.

The distribution is named connector-for-ai-agents; the import is connector_manager:

from connector_manager import ConnectorManager, AsyncConnectorManager, Connection
Installing from git, or working on this repository
# straight from git
uv pip install git+https://github.com/MuhammadHusnainAli/connector-for-ai-agents
pip install "connector-for-ai-agents @ git+https://github.com/MuhammadHusnainAli/connector-for-ai-agents"

# working on this repo
git clone https://github.com/MuhammadHusnainAli/connector-for-ai-agents
cd connector-for-ai-agents
uv sync --extra dev        # or: pip install -e ".[dev]"
uv run pytest -q

uv build (or python -m build) produces the wheel and sdist in dist/.

Quick start

Four calls cover the whole lifecycle: find a connector, ask what it needs, connect, then call the API.

from connector_manager import ConnectorManager

manager = ConnectorManager()

# 1. find it
manager.list_connectors(search="sendgrid")        # [Connector(id='sendgrid', …)]

# 2. ask what it needs — enough to render a form or a tool spec
manager.describe_auth("sendgrid")
# {'connector_id': 'sendgrid', 'auth_mode': 'API_KEY', 'self_service': True,
#  'credentials': [{'name': 'apiKey', 'title': 'API Key', 'required': True, …}], …}

# 3. connect — validates the fields, runs any token exchange, verifies the result
connection = manager.connect("sendgrid", credentials={"apiKey": "SG.…"})
connection.verified                                # True

# 4. call the API with the credentials applied for you
manager.request(connection, "GET", "/v3/scopes").json()

Persist the connection wherever you like:

saved = connection.to_dict()                       # JSON-safe dict for your DB or vault
connection = Connection.from_dict(saved)

Browse the catalogue

len(manager)                       # 1586
manager.categories()               # 31 categories: 'crm', 'accounting', 'hr', …
manager.auth_modes()               # {'API_KEY': 899, 'OAUTH2': 361, 'OAUTH2_CC': 110, …}

for connector in manager.list_connectors(category="crm", limit=3):
    print(connector.id, connector.display_name, connector.auth_mode.value)

manager.get_icon("hubspot")                            # inline SVG string
manager.list_connectors_dict(include_icon=True)        # JSON-ready (icons are large)

Filters: search, category, auth_mode, supported_only, self_service_only, limit, offset.

Pagination

1,586 connectors is too many to hand a UI at once. Ask for a page by number or by raw offset and you get a ConnectorPage carrying the items and the numbers a picker needs — including total, counted before paging, so rendering "showing 21-40 of 1,586" never costs a second call.

page = manager.paginate_connectors(page=2, page_size=20, category="crm")

page.items                                 # list[Connector] for this page
list(page)                                 # a page is iterable, sized, indexable
page.total                                 # 113 — matches for these filters, ignoring paging
page.page, page.pages                      # 2, 6
page.has_next, page.next_offset            # True, 40
page.first_index, page.last_index          # 21, 40

page.to_dict()                             # {"items": [...], "pagination": {...}}

Ordering is stable (display name, then id), so consecutive pages slice the same sequence — no repeated or skipped rows. page_size defaults to 50 and is capped at 1,000. To walk everything without holding it in memory:

for page in manager.iter_connector_pages(page_size=200, self_service_only=True):
    for connector in page:
        ...

Ask what a connector needs

schema = manager.get_auth_schema("1password-users")

schema.auth_mode                 # <AuthMode.OAUTH2_CC>
schema.requires_external_oauth   # False — this package can complete it

for field in schema.user_fields():
    print(field.group.value, field.name, field.title, field.required, field.secret)

# connection_config  domain         API Domain      True   False   (enum of 3 regions)
# connection_config  accountId      Account ID      True   False
# credentials        client_id      Client ID       True   False
# credentials        client_secret  Client Secret   True   True

Every AuthField carries what a form needs: title, description, example, pattern, enum, format, secret, order, default_value, visible_when. Two groups matter:

  • credentials — the secrets: apiKey, client_id / client_secret, …
  • connection_config — per-connection non-secrets that the provider's urls interpolate: domain, accountId, subdomain, …

manager.describe_auth(id) returns the same thing as a plain dict.

Connect

connection = manager.connect(
    "1password-users",
    credentials={"client_id": "…", "client_secret": "…"},
    connection_config={"domain": "api.1password.com", "accountId": "XLQ…"},
)

connection.verified        # True — the provider's verification endpoint answered 2xx
connection.access_token    # the minted token
connection.expires_at

connect() validates every field against the connector's own rules — missing fields, patterns, enums, all reported at once through ValidationError.field_errors — runs the token exchange when the auth mode needs one, then calls the provider's verification endpoint to prove the credentials work.

Pass require_verified=True to raise instead of returning verified=False, or verify=False to skip the network call entirely.

Use the connection and keep it alive

manager.ensure_fresh(connection)      # refresh only if expired or close to it
manager.refresh(connection)           # force a new token
manager.verify(connection)            # re-check the credentials

response = manager.request(connection, "GET", "/v1/customers")
response.status, response.json()

# or hand the fully resolved call to another runtime: a worker, an agent tool,
# a different HTTP client
manager.prepare_request(connection, "GET", "/v1/customers").to_dict()
# {'method': 'GET', 'url': 'https://…', 'headers': {'authorization': 'Bearer …'}, 'params': {}}

Sync and async

Two manager classes, one implementation. Same method names throughout.

from connector_manager import ConnectorManager, AsyncConnectorManager

with ConnectorManager() as manager:
    connection = manager.connect("affinity-v2", credentials={"apiKey": "…"})
    response = manager.request(connection, "GET", "/v2/persons")

async with AsyncConnectorManager() as manager:
    connection = await manager.connect("affinity-v2", credentials={"apiKey": "…"})
    response = await manager.request(connection, "GET", "/v2/persons")

connect, import_connection, verify, refresh, ensure_fresh and request are coroutines on the async manager. The catalogue and schema methods — list_connectors, get_auth_schema, describe_auth, get_icon, prepare_request, validate — only read bundled data, so they stay synchronous in both.

Examples elsewhere in this README use the sync manager; add await for async.

Auth mode coverage

Auth mode Connectors Support
API_KEY 899 Full — connect and verify
OAUTH2_CC 110 Full — client-credentials exchange, including basic, custom and private_key_jwt, plus refresh
BASIC 109 Full — connect and verify
TWO_STEP 63 Full — token exchange, chained additional_steps, cookie and header extraction, refresh
JWT 4 Full — signed locally with HMAC, RSA or EC
NONE, INSTALL_PLUGIN 3 Full
SIGNATURE 1 Full — WS-Security UsernameToken
TBA 1 Full — OAuth 1.0a HMAC-SHA256 request signing
OAUTH2 361 Import tokens from your own OAuth layer; the refresh-token grant runs here
MCP_OAUTH2, MCP_OAUTH2_GENERIC, OAUTH1, APP, CUSTOM 32 Import tokens; request signing works once imported
BILL, AWS_SIGV4 3 Not implemented — raises UnsupportedAuthModeError

1,190 connectors connect end to end from user-supplied values alone. The remaining 396 need a token you obtained elsewhere, or an auth mode this package does not implement.

OAuth connectors

Redirect flows belong in your auth layer. Once you have tokens, import them and everything else works the same way.

manager.requires_external_oauth("slack")   # True

manager.connect("slack", credentials={})   # ExternalAuthRequiredError, explaining what is missing

connection = manager.import_connection(
    "slack",
    credentials={
        "access_token": "xoxb-…",
        "refresh_token": "…",      # optional
        "client_id": "…",          # optional — enables manager.refresh()
        "client_secret": "…",
    },
)

The catalogue still carries each provider's authorization_url, token_url, scopes and PKCE quirks, so you can drive your own redirect flow from it.

Using this from an AI agent

The catalogue is designed to be handed to a model. Every method that matters returns plain JSON-safe dicts, so a tool layer is thin:

from connector_manager import ConnectorManager, Connection

manager = ConnectorManager()

def search_connectors(query: str, page: int = 1) -> dict:
    """Find integrations by name or category."""
    return manager.paginate_connectors(search=query, page=page, page_size=20).to_dict()

def get_required_fields(connector_id: str) -> dict:
    """List exactly what the user must supply to connect."""
    return manager.describe_auth(connector_id)

def call_api(saved_connection: dict, method: str, path: str) -> dict:
    """Make an authenticated call to a connected provider."""
    connection = Connection.from_dict(saved_connection)
    manager.ensure_fresh(connection)
    return manager.request(connection, method, path).json()

Why it suits agent use:

  • describe_auth() is already a field spec — names, types, whether a value is secret, regex, allowed values — so a model can ask the user for exactly the right things and validate before spending a call.
  • paginate_connectors() bounds the context you feed a model, and total lets it reason about how much it has not seen.
  • prepare_request() returns a resolved request without sending it, so an agent can show a user what it is about to do and wait for approval.
  • Errors are typed and structured — ValidationError.field_errors names the bad fields, ExternalAuthRequiredError says what is missing — so a model can recover instead of guessing.
  • Browsing costs no network calls, so catalogue exploration never burns rate limit or wall-clock time.

Command line

Installing the package installs a connectors command, equivalent to python -m connector_manager:

connectors list --search hubspot
connectors list --page 2 --page-size 20        # showing 21-40 of 1586 · page 2/80
connectors list --all --page-size 200          # page through everything
connectors show 1password-users                # every field it needs
connectors stats                               # totals by auth mode and category
connectors icon stripe -o stripe.svg
connectors connect affinity-v2 -c apiKey= -o conn.json
connectors request conn.json GET /v2/persons

-c sets a credential, -x a connection-config value, -i an integration-config value, all as key=value.

Credentials are hidden when output goes to a terminal. connect, verify, refresh and request --dry-run redact credential values, authorization headers and key-bearing query parameters, so secrets do not end up in scrollback or CI logs. Pass --show-secrets to print them anyway. -o FILE writes the usable connection with mode 0600; it holds live credentials, so keep it out of version control.

How it is put together

Module Main classes Role
manager.py BaseConnectorManager, ConnectorManager, AsyncConnectorManager the public facade
registry.py ConnectorRegistry merges data/connectors/*.yaml and icons, pagination, builds AuthSchema
models.py Connector, ConnectorPage, AuthField, AuthSchema, Connection, AuthMode the data model
auth/ AuthStrategy subclasses one class per auth mode
flows.py FlowRunner, AsyncFlowRunner drive one auth flow either sync or async
http.py Request, HttpResponse, HttpClient, AsyncHttpClient the only I/O in the package
proxy.py RequestBuilder base urls, auth headers, query templates, OAuth1 and TBA signing
verification.py CredentialVerifier, VerificationResult proves credentials work
validation.py required, pattern, enum, format, visible_when
interpolation.py the ${…} template engine
data/connectors/ connector definitions, one YAML file per auth mode
data/icons/ 1,591 SVG logos

Run the suite with uv run pytest -q — 109 tests. Coverage includes the whole catalogue: every connector must expose a name, an icon, a buildable auth schema and a fully resolved request with no leftover ${…} in urls or headers, plus mocked connect, refresh and verify flows for each supported auth mode. The async suite asserts the async manager returns byte-identical credentials to the sync one for the same provider response.

Adding your own auth mode

Subclass AuthStrategy and register it. Your strategy works in both managers because it never touches an HTTP client itself: return the credentials directly when no network is needed, or make flow() a generator that yields Request objects and receives HttpResponse back.

from connector_manager import AuthMode, AuthStrategy, Request, register_strategy, TokenExchangeError

class MyBillAuth(AuthStrategy):
    auth_mode = AuthMode.BILL
    refreshable = True

    def flow(self, ctx):
        response = yield Request(
            "POST",
            "https://api.bill.com/api/v2/Login.json",
            content=f"userName={ctx.credentials['username']}",
        )
        if not response.ok:
            raise TokenExchangeError("login failed", status=response.status)
        return {"type": "BILL", "token": response.json()["sessionId"]}

register_strategy(MyBillAuth())

Adding a connector instead? See CONTRIBUTING.md.

FAQ

Does browsing the catalogue make network calls? No. Definitions and logos ship inside the distribution. Only connect, verify, refresh and request touch the network.

Where do the connector definitions come from? From maintained open-source implementations of each provider's API — Nango, Pipedream, ActivePieces and n8n — so base urls, credential placement and verification endpoints come from working code rather than guesswork. Each one was then called live: see docs/added-connectors.md for the evidence behind every connector added in 0.1.2.

Does it store my API keys? No, and that is deliberate. connect() returns a Connection object and hands it to you. Encryption, storage, rotation and tenancy are your application's job.

Can it run the OAuth redirect flow for me? No. Redirects need a browser, a callback url and session state that belong in your app. Run the flow yourself, then import_connection(). Client-credentials OAuth, which needs no redirect, is fully implemented.

How do I know a connector actually works before shipping it? connect() calls the provider's verification endpoint and reports verified. From the shell: connectors connect <id> -c apiKey=… -o conn.json.

What if a provider is missing, or a definition is wrong? Open an issue or a pull request — CONTRIBUTING.md covers the shape of a good connector entry.

Is it typed? Yes, fully, with py.typed, so your type checker sees the annotations.

Which Python versions are supported? 3.10 and newer, tested in CI on 3.10 through 3.13.

Is this open source? It is source-available under the Elastic License 2.0. You can read, modify and use it, including commercially, but you may not provide it to others as a hosted or managed service. See LICENSE.

What is deliberately not here

Connection storage, secret encryption, OAuth redirect handling, webhooks and data syncs.

A handful of connectors rely on provider-specific post-connection or credential-verification scripts that are not implemented here. The fields those scripts would fill are surfaced with automated=True so you can supply them yourself, and such connectors report tested=False from verify().

Documentation links are intentionally absent from the catalogue. It carries only what is needed to authenticate and call each API: auth mode, field definitions, base url, verification endpoint.

Where the definitions live

The catalogue is split by auth mode, one file per mode under src/connector_manager/data/connectors/:

data/connectors/api-key.yaml     899 connectors
                oauth2.yaml      361
                oauth2-cc.yaml   110
                basic.yaml       109
                two-step.yaml     63
                mcp-oauth2.yaml   24
                ...              14 smaller modes

The registry loads every *.yaml under that directory and merges them, so the layout is an organisational detail rather than API: ids stay unique across files, and an alias resolves against its target wherever that target lives. ConnectorRegistry(connectors_file=...) still accepts a single YAML file if you ship your own catalogue.

python scripts/split_connectors.py regroups the files after an auth_mode changes; --check reports drift and is what CI runs.

Project files

File What it covers
CHANGELOG.md What changed in each release
CONTRIBUTING.md Adding a connector, changing the machinery, house style
SECURITY.md Reporting a vulnerability, and handling credentials safely
CODE_OF_CONDUCT.md How we treat each other
docs/added-connectors.md Provenance and live check behind every connector added in 0.1.2
scripts/split_connectors.py Regroups the catalogue into one file per auth mode

Licence

Elastic License 2.0. See LICENSE.

Release files for connector-for-ai-agents 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for connector-for-ai-agents 0.1.3
File Size Uploaded
connector_for_ai_agents-0.1.3.tar.gz 9.0 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for connector-for-ai-agents 0.1.3
File Interpreter ABI Platform
connector_for_ai_agents-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 18.7 MB

Release files / connector_for_ai_agents-0.1.3.tar.gz

Download URL connector_for_ai_agents-0.1.3.tar.gz
Size 9.0 MB
Tags Source
SHA-256 checksum
How to use checksums
e81af1259e9a3a279710976cdb1b7e1052baf0ba5b64243f967f73c22da7cf50
BLAKE2b-256 checksum
How to use checksums
f3d326a5b589b513f19d88972ba65793e927598dc616f5cc28ab5c170b66a6c0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 26, 2026.

Transparency log

Release files / connector_for_ai_agents-0.1.3-py3-none-any.whl

Download URL connector_for_ai_agents-0.1.3-py3-none-any.whl
Size 9.7 MB
Tags Python 3
SHA-256 checksum
How to use checksums
450d14c0f7a56fdcd8bef5a6d48106313ffa8610d0536359f1686e3659035314
BLAKE2b-256 checksum
How to use checksums
7620c7ba302f78157c1ea4942524229b55e56d860ce341341520cc80c2712272
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 26, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

This release

0.1.3 This release

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page