Agent-native API integration SDK. Curated specs. Direct execution. Zero wrappers.
Project description
anytool
Agent-native API integration SDK. Curated specs. Direct execution. Zero wrappers.
Give your AI agent curated API specs + OAuth tokens — it calls any API directly.
Quick Start
from anytool import AnyTool, MemoryTokenStore, AppCredentials
api = AnyTool(token_store=MemoryTokenStore())
api.register_app(AppCredentials(
app="google",
client_id="xxx.apps.googleusercontent.com",
client_secret="GOCSPX-xxx",
scopes=["https://www.googleapis.com/auth/gmail.send"],
redirect_uri="http://localhost:8000/oauth/callback",
))
# 1. Start OAuth → redirect user to this URL
auth_url = await api.get_auth_url("google", connection_id="user-123")
# 2. Handle callback → tokens stored + auto-refreshed
tokens = await api.handle_callback("google", code="xxx", state="xxx")
# 3. Call any API by action name
result = await api.call(
"gmail_send_email",
connection_id="user-123",
to="vendor@example.com",
subject="Invoice Follow-up",
body="Hi, please send the updated invoice.",
)
# → {"data": {"id": "msg-123", "threadId": "thread-456"}, "successful": True}
# Get LangChain tools for an app
tools = api.get_tools("google", connection_id="user-123")
# → [gmail_send_email, gmail_search, sheets_append_row, ...]
# Or get tools for ALL apps at once
all_tools = api.get_all_tools(connection_id="user-123")
# → 98 tools across 8 apps, ready for llm.bind_tools()
The Problem
Existing integration platforms pre-build wrappers for each API action. These wrappers:
- Drop nested data — complex payloads like
templateRoles: [{roleName, email, name}]arrive at the API as[{}] - Break across versions — SDK updates introduce Python version incompatibilities that crash your production server
- Return non-standard formats — responses come back as Python repr strings instead of JSON, requiring custom parsing
- Silently limit results — only 20 tools returned by default, with no indication that actions are missing
- Add unnecessary latency — every request routes through a third-party proxy before reaching the actual API
How anytool Works
| Layer | What | How |
|---|---|---|
| Auth | OAuth, token refresh, storage | Built-in OAuth manager with pluggable token store |
| Knowledge | Curated API specs — params, paths, descriptions | anytool specs (~15 lines per action) |
| Execution | Build HTTP request, handle API quirks, parse response | anytool executor (direct HTTP) |
No intermediate wrappers. No serialization layers. No third-party proxy. What the LLM constructs is what the API receives.
Install
pip install anytool # Core (httpx + pydantic + loguru)
pip install anytool[langchain] # + LangChain tool generation
Supported Apps — 98 Actions
| App | Actions | Auth |
|---|---|---|
| Gmail | 7 — send, search, get, thread, reply, labels, modify | OAuth2 |
| Google Sheets | 2 — append row, read range | OAuth2 |
| Google Drive | 2 — list files, get file | OAuth2 |
| Google Calendar | 6 — list/get/create/update/delete events, list calendars | OAuth2 |
| Google Docs | 5 — get/create doc, batch update, insert/replace text | OAuth2 |
| DocuSign | 6 — create envelope, get status, list, recipients, void, resend | OAuth2 |
| Freshdesk | 10 — create/get/update/delete ticket, reply, note, list, search, conversations, agents | API Key |
| Slack | 7 — send/update message, channels, history, thread, reaction, lookup user | OAuth2 |
| HubSpot | 15 — contacts, companies, deals (CRUD + search), notes, associations, owners | OAuth2 |
| GitHub | 16 — issues, PRs, repos, commits, workflows, branches, search | OAuth2 |
| Zendesk | 13 — tickets (CRUD), comments, search, agents, groups, users | OAuth2 |
| 9 — send template/text/image/document, reactions, read receipts | Bearer Token |
Usage
Standalone Mode (Recommended)
Full control. Manage OAuth yourself. No third-party dependencies.
from anytool import AnyTool, MemoryTokenStore, AppCredentials
api = AnyTool(token_store=MemoryTokenStore())
api.register_app(AppCredentials(
app="google",
client_id="xxx.apps.googleusercontent.com",
client_secret="GOCSPX-xxx",
scopes=["https://www.googleapis.com/auth/gmail.send"],
redirect_uri="http://localhost:8000/oauth/callback",
))
# Start OAuth flow
auth_url = await api.get_auth_url("google", connection_id="user-123")
# Handle callback (after user authorizes)
tokens = await api.handle_callback("google", code="xxx", state="xxx")
# Call APIs — tokens auto-refresh when expired
result = await api.call("gmail_send_email", connection_id="user-123", to="...", subject="...", body="...")
LangChain Integration
from anytool import AnyTool, MemoryTokenStore
api = AnyTool(token_store=MemoryTokenStore())
# ... register apps ...
# Get tools for one app
gmail_tools = api.get_tools("google", connection_id="user-123")
# Get tools for specific actions only
send_tools = api.get_tools("google", connection_id="user-123", actions=["gmail_send_email", "gmail_search"])
# Get tools for multiple apps
all_tools = api.get_all_tools(connection_id="user-123", apps=["google", "slack", "freshdesk"])
# Use with LangChain
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o")
llm_with_tools = llm.bind_tools(all_tools)
Triggers (Event Detection)
Poll-based triggers that detect new events and POST to your webhook:
from anytool import AnyTool, MemoryTokenStore, TriggerEngine, MemoryTriggerStore, TriggerConfig
api = AnyTool(token_store=MemoryTokenStore())
# ... register apps ...
engine = TriggerEngine(api=api, store=MemoryTriggerStore())
await engine.register(TriggerConfig(
id="t1",
trigger_type="gmail_new_message",
provider="google",
connection_id="user-123",
webhook_url="https://your-app.com/api/webhook/trigger",
filters={"from_contains": "vendor@example.com"},
poll_interval_seconds=90,
))
await engine.start()
Custom Token Store
Implement TokenStore for your database:
from anytool.auth.token_store import TokenStore
from anytool.auth.models import UserTokens, OAuthState
class PostgresTokenStore(TokenStore):
async def save_tokens(self, tokens: UserTokens) -> None:
# Encrypt and store in your DB
...
async def get_tokens(self, app: str, user_id: str) -> Optional[UserTokens]:
# Decrypt and return from your DB
...
async def delete_tokens(self, app: str, user_id: str) -> None: ...
async def list_connected(self, user_id: str) -> list[UserTokens]: ...
async def save_oauth_state(self, state: OAuthState) -> None: ...
async def get_oauth_state(self, state_key: str) -> Optional[OAuthState]: ...
Adding a New App
apps/registry.py— AddAppConfig(OAuth URLs, base URL)specs/newapp.py— WriteActionSpecper endpoint (~15 lines each)client.py— Import and register specsexecutor.py— Add_build_*method only if the API has payload quirkstests/test_core.py— Add tests
Architecture
┌─────────────────────────────────────────────┐
│ Your AI Agent (LangChain / CrewAI / raw) │
│ │
│ tools = api.get_tools("google", conn_id) │
│ result = await api.call("gmail_send_email")│
└──────────────────┬──────────────────────────┘
│
┌─────────▼─────────┐
│ anytool client │
│ │
│ Spec Registry │ ← 98 curated ActionSpecs
│ OAuth Manager │ ← token refresh, CSRF
└─────────┬─────────┘
│
┌─────────▼─────────┐
│ API Executor │
│ │
│ Build URL/path │
│ Build query │
│ Build body │ ← request transforms for quirky APIs
│ Extract IDs │ ← response_ids mapping
└─────────┬─────────┘
│
┌─────────▼─────────┐
│ Direct HTTP │────────▶ Gmail, Slack,
│ (auto-injects │◀──────── HubSpot, etc.
│ OAuth tokens) │
└───────────────────┘
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file anytool-0.2.0.tar.gz.
File metadata
- Download URL: anytool-0.2.0.tar.gz
- Upload date:
- Size: 53.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d6bafe88c0e1fd7cb6de08ad239b30fbd49beb7b73e91d90e0d92dc5dbdbf55d
|
|
| MD5 |
b89ed3843547082cc5534b739283e1ba
|
|
| BLAKE2b-256 |
d9f176c587ca3fdf696c49cddd73b0c664a5bdd47954e10dfcb624357eaf0382
|
File details
Details for the file anytool-0.2.0-py3-none-any.whl.
File metadata
- Download URL: anytool-0.2.0-py3-none-any.whl
- Upload date:
- Size: 43.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
af7c9a5fb61aa43782a9a398188a754865445eb09d63b4e924d668c991776a95
|
|
| MD5 |
c712c0e60a1f122a9184995f4eb927fc
|
|
| BLAKE2b-256 |
bd9d2f664282ff53f0ccd21b4fce8b2b69b0da9d0c6299e51f9a1831418a159a
|