Fastn SDK for Python

February 26, 2026 · View on GitHub

Give your AI agents and apps secure, governed access to 250+ enterprise systems.

Production-ready Python SDK for OpenAI, Anthropic, Gemini, and Bedrock function calling. Fully managed OAuth 2.1, SOC 2 certified platform, role-based access control, audit trails, and sub-second execution — so your agent code stays simple and your security team stays happy.

PyPI version Python License: MIT

Why Fastn?

ProblemFastn Solution
Agents receive all tool schemas, burning tokens and increasing hallucinationget_tools_for(prompt, limit=5) returns only the most relevant tools — 250+ connectors, but only 5 reach the LLM
API schemas are deeply nested, wasting context on structureSDK auto-unwraps schemas for LLMs and re-wraps for execution — flat params in, correct API structure out
Each SaaS API has its own OAuth flow, token refresh, and credential storageFully managed OAuth 2.1 vault — acquisition, auto-refresh, tenant isolation. No token management code.
Security and compliance are afterthoughts in most agent toolingSOC 2 certified platform, role-based access control, audit trails, PII filtering
LLM agents need tool schemas in provider-specific formatsget_tools_for("Send a Slack message", format="openai") returns ready-to-use schemas for OpenAI, Anthropic, Gemini, Bedrock
Managing connections per customer in multi-tenant apps is complexFirst-class tenant_id and connection_id support at every level
Building automation workflows requires stitching APIs togetherfastn.flows.create("When a PR is opened, post to Slack") handles orchestration

Installation

pip install fastn-ai

Installs the SDK, CLI, and type stubs for 250+ connectors. Requires Python 3.8+. IDE autocomplete works immediately — no extra setup needed.

Quick Start

from fastn import FastnClient

fastn = FastnClient()
fastn.slack.send_message(channel="general", text="Hello from Fastn!")

Async:

from fastn import AsyncFastnClient

async with AsyncFastnClient() as fastn:
    await fastn.slack.send_message(channel="general", text="Hello!")

How It Works

Fastn sits between your AI agent and 250+ SaaS APIs. The SDK handles client-side concerns (tool discovery, schema transformation, parameter routing). The platform handles server-side concerns (credential injection, access control, batch optimization, prompt safety, observability).

One-liner: Connectors provide tools. Flows compose tools. Agents run tools and flows with reasoning.

Core Capabilities

CapabilityWhat It DoesWhere It Runs
Dynamic tool filteringReturns only tools that match the current prompt or intentPlatform
Context optimizationComposes tools/skills, filters schema I/O, strips PII — minimizes tokens and hallucinationPlatform + SDK
Schema transformerFlattens nested API schemas for LLMs, re-wraps for executionSDK
Intent routingDetermines which tool to use based on agent requestPlatform + SDK
Managed OAuth 2.1Full token lifecycle — acquisition, refresh, revocation, tenant isolationPlatform
Governed accessRBAC, audit trails, and enterprise compliance controls (SOC 2 certified)Platform
Batch optimizerGroups similar calls to reduce cost and timePlatform
Prompt safetyBlocks unsafe or injected tool commandsPlatform
Observability layerTracks cost, latency, and errors per tool callPlatform + SDK CLI

Dynamic Tool Filtering

get_tools_for(prompt) sends a semantic search to the platform, which matches the prompt against the full connector registry and returns only the top N tools (default: 5). This reduces tool context from ~125K tokens to ~2,500 tokens — fewer tools also means fewer hallucinated tool calls.

# Semantic discovery — only the most relevant tools reach the LLM
tools = fastn.get_tools_for("Send a Slack message", format="openai", limit=5)

# Direct lookup — bypass semantic search when you know the connector name
tools = fastn.get_tools_for("slack tools", connector="slack", format="openai")

Context Optimization

Fastn minimizes what reaches the LLM — fewer tokens, less hallucination, better compliance:

  • Tool and skill composition — the platform composes tools and skills into efficient chains, so agents receive focused context instead of raw API surface area
  • Schema I/O filtering — strips unnecessary fields from tool input and output schemas before they reach the LLM, reducing token cost
  • PII filtering — removes personally identifiable information from context for security and compliance
  • Schema transformation — the SDK flattens nested API wrappers (body, param) for the LLM and re-wraps them for execution:
What the API expects:   {"body": {"channel": "#general", "text": "hello"}}
What the LLM sees:      {"channel": "#general", "text": "hello"}

Combined with Dynamic Tool Filtering, this reduces agent context from ~125K tokens to a focused, compliant payload.

Architecture

┌─────────────────────────────────────────────────────────────────────────┐
│  Your Agent                                                             │
│                                                                         │
│  1. Discovery                                                           │
│     get_tools_for(prompt) ──→ SDK ──→ POST /getTools ──→ Platform       │
│     Platform: dynamic tool filtering, intent routing                    │
│                              ←── top N tools (unwrapped schemas) ←──    │
│                                                                         │
│  2. LLM Call                                                            │
│     SDK: format schemas for provider (OpenAI / Claude / Gemini / etc.)  │
│     Send prompt + N tool schemas to LLM                                 │
│     LLM returns tool_call with flat params                              │
│                                                                         │
│  3. Execution                                                           │
│     execute(tool, params) ──→ SDK (re-wrap params) ──→ Platform         │
│     Platform: credential injection, RBAC, prompt safety, batch opt.     │
│                               ←── result ←──                            │
│                                                                         │
│  4. Observability                                                       │
│     Platform: audit trail, cost/latency/error tracking per tool         │
│     SDK CLI: per-call cost, latency, and token summary table            │
└─────────────────────────────────────────────────────────────────────────┘

Terminology

TermDefinitionExample
ConnectorAn adapter to an external service. Provides tools.Slack, Jira, GitHub, Salesforce
ToolA callable action within a connector, exposed to agents/LLMs. The callable unit in MCP.slack.send_message, jira.create_issue
FlowOrchestration of multiple tools with logic, branching, retries, scheduling."When a Jira ticket is created, post to Slack"
ConnectionAn authenticated link between a connector and a specific account.OAuth connection to a Slack workspace
TenantA customer, organization, or team in a multi-tenant app.tenant_id: "acme-corp"
SkillA reusable agent configuration stored in a project.fastn.skills.list()
AgentA goal-driven executor that calls tools/flows with reasoning.fastn agent "Send hello to Slack"
ProjectA container that groups connectors, flows, and tenants.project_id: "proj_xyz"
StageAn environment for isolating dev/staging/production data."LIVE", "STAGING", "DEV"

SDKs

PackageWhat It DoesLanguageStatus
fastn-aiBackend SDK — triggers connectors and flows, discovers tools, executes tool callsPythonAvailable
@fastn/sdkBackend SDK — same capabilities as the Python SDKTypeScriptComing soon
fastn-connectFrontend widget — captures OAuth credentials and connector config for the platformJavaScriptAvailable

Configuration

Credentials are loaded from three sources (highest priority first):

  1. Constructor parameters: FastnClient(api_key="...", project_id="...")
  2. Environment variables: FASTN_API_KEY, FASTN_PROJECT_ID
  3. Config file: .fastn/config.json (created by fastn login)
# Explicit credentials (no config file needed)
fastn = FastnClient(api_key="your-api-key", project_id="your-project-id")
VariableDescription
FASTN_API_KEYAPI key
FASTN_PROJECT_IDProject ID
FASTN_AUTH_TOKENJWT from fastn login
FASTN_TENANT_IDDefault tenant ID
FASTN_STAGEEnvironment: LIVE, STAGING, or DEV

LLM Agent Integration

Fastn handles the three hardest parts of giving LLMs access to tools:

  1. Discoveryget_tools_for(prompt) semantically matches tools from 250+ connectors. Only the top N reach the LLM, keeping context small and hallucination low.
  2. Schema translation — Schemas are flattened for LLM consumption and formatted for your provider (OpenAI, Anthropic, Gemini, Bedrock).
  3. Executionexecute(tool, params) routes through the platform, which handles credential injection, parameter re-wrapping, retries, and logging.
import json
import openai
from fastn import FastnClient

fastn = FastnClient()

# 1. Describe what you need — Fastn discovers the right tools
tools = fastn.get_tools_for(
    "Send a message on Slack and create a Jira ticket",
    format="openai",   # also: anthropic, gemini, bedrock, raw
)

# 2. Send tools + user prompt to the LLM
response = openai.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Send hello to #general on Slack"}],
    tools=tools,
)

# 3. The LLM returns a tool_call when it wants to use a tool
tool_call = response.choices[0].message.tool_calls[0]

# 4. Execute the tool call through Fastn
result = fastn.execute(
    tool=tool_call.function.name,                      # e.g. "send_message"
    params=json.loads(tool_call.function.arguments),    # e.g. {"channel": "general", "text": "hello"}
)
print(result)

Supported LLM Providers

FormatProviderExample
"openai"OpenAI, Azure OpenAIget_tools_for("Send a Slack message", format="openai")
"anthropic"Anthropic Claudeget_tools_for("Create a Jira issue", format="anthropic")
"gemini"Google Gemini / Vertex AIget_tools_for("List GitHub repos", format="gemini")
"bedrock"AWS Bedrock Converse APIget_tools_for("Send an email", format="bedrock")
"raw"Any (raw Fastn schemas)get_tools_for("Notify team", format="raw")

Anthropic Claude Example

import anthropic
from fastn import FastnClient

fastn = FastnClient()
tools = fastn.get_tools_for("Send a message on Slack", format="anthropic")

client = anthropic.Anthropic()
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Send 'Build passed' to #ci"}],
    tools=tools,
)

for block in response.content:
    if block.type == "tool_use":
        result = fastn.execute(tool=block.name, params=block.input)
        print(result)

Google Gemini Example

from google import genai
from fastn import FastnClient

fastn = FastnClient()
tools = fastn.get_tools_for("Send a Slack message", format="gemini", limit=10)

client = genai.Client()
response = client.models.generate_content(
    model="gemini-2.0-flash",
    contents="Send a greeting to #general on Slack",
    config=genai.types.GenerateContentConfig(tools=tools),
)

for part in response.candidates[0].content.parts:
    if fn := part.function_call:
        result = fastn.execute(tool=fn.name, params=dict(fn.args))
        print(result)

AWS Bedrock Example

import boto3
from fastn import FastnClient

fastn = FastnClient()
tools = fastn.get_tools_for("Notify the team on Slack", format="bedrock")

bedrock = boto3.client("bedrock-runtime")
response = bedrock.converse(
    modelId="anthropic.claude-sonnet-4-20250514-v1:0",
    messages=[{"role": "user", "content": [{"text": "Notify #ops that deploy is done"}]}],
    toolConfig={"tools": tools},
)

for block in response["output"]["message"]["content"]:
    if "toolUse" in block:
        tool_use = block["toolUse"]
        result = fastn.execute(tool=tool_use["name"], params=tool_use["input"])
        print(result)

Using Connector Names Directly

For local registry lookup without an API call, pass connector names:

# Single connector
tools = fastn.get_tools_for("slack tools", connector="slack", format="openai")

# Multiple connectors — limit applies to the total across all
tools = fastn.get_tools_for(
    "project tools",
    connector=["slack", "jira", "github"],
    format="openai",
    limit=10,
)

Connector Catalog

Inspect the connector registry programmatically:

# List all connectors in the registry
connectors = fastn.connectors.list()
for c in connectors:
    print(f"{c['name']}: {c['tool_count']} tools")

# Get details for a specific connector
slack = fastn.connectors.get("slack")
print(slack["tools"])

# List all tools in a connector with full schemas
tools = fastn.get_tools("slack")
for t in tools:
    print(f"  {t['name']}: {t['description']}")

# Get a single tool's schema
tool = fastn.get_tool("slack", "send_message")
print(tool["inputSchema"])

Flows

Flows chain tools across multiple connectors. Describe the workflow in natural language and Fastn generates, schedules, and executes it.

# Create a flow from a natural language description
result = fastn.flows.create(
    prompt="When a Jira ticket is created, post a summary to #engineering on Slack"
)
print(result["flow_id"])  # "flow_abc123"

# If Fastn needs more info, it returns questions
if "questions" in result:
    result = fastn.flows.create(
        prompt="When a Jira ticket is created, post to Slack",
        answers={"channel": "#engineering", "jira_project": "ENG"},
    )

# List all flows
flows = fastn.flows.list()
active_flows = fastn.flows.list(status="active")

# Run a flow manually
run = fastn.flows.run(flow_id="flow_abc123")
print(run["run_id"])  # "run_xyz"

# Check run status
status = fastn.flows.get_run(run_id="run_xyz")
print(status["status"])  # "completed", "running", "failed"

# Update a flow (schedule, enable/disable)
fastn.flows.update(
    flow_id="flow_abc123",
    schedule="0 9 * * MON-FRI",  # Every weekday at 9am
    enabled=True,
)

# Delete a flow
fastn.flows.delete(flow_id="flow_abc123")

Authentication

Fastn supports three authentication types at different levels. The SDK triggers connectors and flows — the SOC 2 certified platform auto-manages OAuth 2.1 token refresh and credential injection.

Auth TypeWho AuthenticatesTo WhatHowExpiry
API KeyYour appFastn platformx-fastn-api-key headerNever
OAuth TokenYou (developer)Fastn platformAuthorization: Bearer JWTAuto-refreshed
ConnectionYour end-usersExternal services (Slack, Jira...)OAuth vault + connection_idPlatform-managed

1. API Key — App-to-Platform

Static credential that authenticates your application to the Fastn platform. Created in the Fastn dashboard, never expires.

Your App                          Fastn Platform
  │                                     │
  │  POST /executeTool                  │
  │  x-fastn-api-key: sk_live_xxx      │
  │  x-fastn-space-id: proj_abc        │
  │ ──────────────────────────────────→ │
  │                                     │  Validate API key
  │                                     │  Resolve project
  │              200 OK + result        │
  │ ←────────────────────────────────── │
fastn = FastnClient(api_key="sk_live_xxx", project_id="proj_abc")
# or: export FASTN_API_KEY=sk_live_xxx

2. OAuth Token — Developer-to-Platform

JWT obtained via browser-based login (RFC 8628 Device Authorization Grant). Used by the CLI and dashboard. Auto-refreshed with a 30-second expiry buffer.

Developer          Browser            Fastn (Keycloak)
  │                   │                      │
  │  fastn login      │                      │
  │ ──────────────────────────────────────→  │
  │                   │    device_code +      │
  │  ←───────────────────  user_code         │
  │                   │                      │
  │  "Visit URL,      │                      │
  │   enter code"     │                      │
  │ ────────────────→ │                      │
  │                   │  User logs in +      │
  │                   │  enters code         │
  │                   │ ──────────────────→  │
  │                   │      authorized      │
  │                   │ ←──────────────────  │
  │                   │                      │
  │  Poll for token   │                      │
  │ ──────────────────────────────────────→  │
  │       access_token + refresh_token       │
  │ ←──────────────────────────────────────  │
  │                                          │
  │  (saved to .fastn/config.json)           │
  │                                          │
  │  POST /executeTool                       │
  │  Authorization: Bearer <jwt>             │
  │ ──────────────────────────────────────→  │
  │                                          │
  │  (on expiry: auto-refresh with 30s       │
  │   buffer using refresh_token)            │
# CLI handles this automatically:
# $ fastn login
# Tokens stored in .fastn/config.json (0o600 permissions)
fastn = FastnClient()  # Picks up auth_token from config

3. Connection — End-User-to-External-Service

OAuth connections that link your end-users to external services (Slack, GitHub, etc.) through the Fastn OAuth vault. Tokens are stored server-side, auto-refreshed by the platform, and isolated per tenant. Your code never sees raw credentials.

fastn-connect captures auth and connector config on the frontend. This SDK triggers connectors and flows on the backend — pass connection_id to route to the right account.

Your App (backend)       Frontend (fastn-connect)     Fastn Platform         External Service
  │                           │                         │                       │
  │  auth.connect(            │                         │                       │
  │    connector="slack"      │                         │                       │
  │    tenant="acme")         │                         │                       │
  │ ───────────────────────────────────────────────→    │                       │
  │                           │                         │                       │
  │  { connection_id,         │                         │                       │
  │    auth_url }             │                         │                       │
  │ ←───────────────────────────────────────────────    │                       │
  │                           │                         │                       │
  │  Pass auth_url to         │                         │                       │
  │  frontend ──────────────→ │                         │                       │
  │                           │  Render login /         │                       │
  │                           │  redirect user          │                       │
  │                           │ ────────────────────────────────────────────→   │
  │                           │                         │    User authorizes    │
  │                           │                         │ ←─────────────────    │
  │                           │  Callback received      │  Store tokens in      │
  │                           │ ←───────────────────    │  OAuth vault          │
  │                           │                         │                       │
  │  auth.status(             │                         │                       │
  │    connection_id)         │                         │                       │
  │ ───────────────────────────────────────────────→    │                       │
  │  { status: "authorized" } │                         │                       │
  │ ←───────────────────────────────────────────────    │                       │
  │                           │                         │                       │
  │  execute(tool,            │                         │                       │
  │    connection_id)         │                         │                       │
  │ ───────────────────────────────────────────────→    │                       │
  │                           │                         │  Inject credentials   │
  │                           │                         │ ──────────────────→   │
  │                           │                         │       result          │
  │                           │                         │ ←──────────────────   │
  │       result              │                         │                       │
  │ ←───────────────────────────────────────────────    │                       │
  │                           │                         │                       │
  │  (on token expiry: platform auto-refreshes)         │                       │
# 1. Start an OAuth connection for your end-user
result = fastn.auth.connect(
    connector="slack",
    tenant_id="customer_acme",
    redirect_url="https://myapp.com/callback",
)
# Pass result["auth_url"] to your frontend (fastn-connect handles the OAuth flow)

# 2. Check connection status (after user completes auth)
status = fastn.auth.status(connection_id=result["connection_id"])
print(status["status"])  # "pending", "authorized", "expired"

# Or look up by connector + tenant
status = fastn.auth.status(connector="github", tenant_id="customer_acme")

# 3. Use the connection — credentials injected by platform
fastn.slack.send_message(
    connection_id=result["connection_id"],
    channel="general", text="Hello!",
)

Custom Auth Provider

For apps with their own identity provider, configure Fastn to validate end-user tokens via your userinfo endpoint.

fastn.auth.configure_custom(
    userinfo_url="https://myapp.auth0.com/userinfo",
)

Multi-Connection Support

When you have multiple connections for the same connector (e.g. two Slack workspaces):

# Per-call
fastn.slack.send_message(connection_id="conn_workspace_a", channel="general", text="Hi!")

# Or bind once
slack_a = fastn.connect("conn_workspace_a")
slack_a.send_message(channel="general", text="Hello!")

Multi-Tenant Support

Route requests to the correct tenant (customer, organization, or team):

# Per-call override
fastn.slack.send_message(tenant_id="acme", channel="general", text="Hello!")

# Constructor-level default
fastn = FastnClient(tenant_id="acme")

# Environment variable
# export FASTN_TENANT_ID=acme

# CLI flag
# fastn connector run slack send_message --tenant acme --channel general --text "Hello!"

Priority: per-call tenant_id > CLI --tenant flag > constructor param > FASTN_TENANT_ID env var > config file

Environments

Switch between LIVE, STAGING, and DEV:

dev = FastnClient(stage="DEV")
staging = FastnClient(stage="STAGING")
prod = FastnClient()  # defaults to LIVE

Or: export FASTN_STAGE=DEV

AI-Powered Mode

For quick prototyping, use natural language:

result = fastn.run("Send hello to #general on Slack")

Projects

List available projects for the authenticated user:

projects = fastn.projects.list()
for p in projects:
    print(f"{p['name']} ({p['id']})")

CLI Reference

CommandDescription
fastn loginAuthenticate with Fastn and select a project
fastn logoutLog out and clear saved credentials
fastn whoamiShow the current logged-in user
fastn connector lsList all available connectors
fastn connector ls <name>Show tools for a specific connector
fastn connector syncRefresh connector registry, fetch tool schemas, regenerate type stubs
fastn connector add <name> [...]Fetch full tool schemas for specific connectors
fastn connector remove <name>Remove connector stubs
fastn connector run <name> <tool>Execute a connector tool
fastn connector schema <name> <tool>Print a tool's input/output schema
fastn flow lsList all flows
fastn flow createCreate a flow from a plain-English prompt
fastn flow run <flow_id>Trigger a flow run
fastn skillList available agent skills
fastn agent "<prompt>"Describe a task in plain English — AI calls the right tools
fastn versionShow SDK and registry versions

fastn agent

Agentic CLI — describe what you want in natural language and the agent discovers tools, sends them to your LLM via native function calling, and executes tool calls in a loop until the task is complete.

fastn agent "Send hello to #general on Slack"
fastn agent --connector slack "List all channels"
fastn agent --eval "Create a Jira ticket for the login bug"
OptionDefaultDescription
--connector--Scope tool discovery to a specific connector
--tool--Scope discovery to a specific tool
--max-turns10Maximum agentic loop iterations
--max-tools5Maximum number of tools passed to the LLM
--max-errors2Stop after this many consecutive tool errors
-y / --yesoffSkip confirmation prompts before each tool call
--evaloffRun LLM-based evaluation after the agent finishes
--connection-id--Connection ID for multi-connection connectors
--tenant--Tenant ID override

fastn connector run

Execute connector tools directly from the command line:

# List available tools
fastn connector run slack

# Interactive mode (prompts for each parameter)
fastn connector run slack send_message

# Inline parameters
fastn connector run slack send_message --channel general --text "Hello!"

# With tenant override
fastn connector run slack send_message --tenant acme --channel general --text "Hello!"

API Reference

FastnClient

FastnClient(
    api_key: str = None,        # Fastn API key (or FASTN_API_KEY)
    project_id: str = None,     # Project ID (or FASTN_PROJECT_ID)
    auth_token: str = None,     # JWT from fastn login (or FASTN_AUTH_TOKEN)
    tenant_id: str = None,      # Tenant ID (or FASTN_TENANT_ID)
    stage: str = None,          # "LIVE", "STAGING", "DEV" (or FASTN_STAGE)
    config_path: str = None,    # Path to config.json
    timeout: float = 30.0,      # HTTP timeout in seconds
    max_retries: int = 3,       # Retry count for transient failures
    verbose: bool = False,      # Debug logging
)

Data Plane (tool execution):

MethodDescription
fastn.<connector>.<tool>(**params)Execute a tool on a connector
fastn.connect(connection_id)Bind a connection, return a proxy
fastn.execute(tool, params, ...)Execute by tool name (for LLM agents)
fastn.run(prompt)AI-powered tool discovery and execution

Control Plane (discovery):

MethodDescription
fastn.connectors.list()List all connectors in the registry
fastn.connectors.get(connector_name)Get connector details (name, category, tools)
fastn.get_tools(connector_name)List all tools for a connector with schemas
fastn.get_tool(connector_name, tool_name)Get one tool's schema
fastn.get_tools_for(prompt, format, limit, connector)Discover tools by prompt or connector name in LLM format

Flows:

MethodDescription
fastn.flows.create(prompt, answers)Create a flow from natural language
fastn.flows.list(status)List flows (optional status filter)
fastn.flows.run(flow_id, user_id)Trigger a flow execution
fastn.flows.get_run(run_id)Check run status and results
fastn.flows.update(flow_id, ...)Update schedule, enable/disable
fastn.flows.delete(flow_id)Delete a flow

Auth:

MethodDescription
fastn.auth.connect(connector, tenant_id, redirect_url)Start OAuth connection flow
fastn.auth.status(connection_id)Check connection status
fastn.auth.configure_custom(userinfo_url)Register custom auth provider

Skills:

MethodDescription
fastn.skills.list()List agent skills in the current project

Projects:

MethodDescription
fastn.projects.list()List available projects

AsyncFastnClient

Same API as FastnClient, but all methods are async/await:

async with AsyncFastnClient() as fastn:
    result = await fastn.slack.send_message(channel="general", text="Hi")
    flows = await fastn.flows.list()
    skills = await fastn.skills.list()
    status = await fastn.auth.status(connection_id="conn_abc")

Error Handling

All exceptions inherit from FastnError:

from fastn import (
    FastnClient, FastnError, AuthError, ConfigError, APIError,
    ConnectorNotFoundError, ToolNotFoundError, ConnectionNotFoundError,
    OAuthError, RegistryError, FlowNotFoundError, RunNotFoundError,
)

try:
    fastn.slack.send_message(channel="general", text="Hello!")
except AuthError:
    print("Invalid credentials — check your API key")
except ConnectorNotFoundError as e:
    print(f"Connector '{e.connector_name}' not found — run: fastn connector sync")
except ToolNotFoundError as e:
    print(f"Tool '{e.tool_name}' not found in '{e.connector_name}'")
except APIError as e:
    print(f"HTTP {e.status_code}: {e}")
except ConfigError:
    print("Run: fastn login")
ExceptionWhenKey Attributes
FastnErrorBase class for all SDK errors.message, .details
AuthErrorInvalid or expired credentials--
ConfigErrorMissing API key or project ID--
APIErrorHTTP error from the Fastn API.status_code, .response_body
ConnectorNotFoundErrorConnector not in registry.connector_name
ToolNotFoundErrorTool not found in connector.connector_name, .tool_name
ConnectionNotFoundErrorConnection ID not recognized--
FlowNotFoundErrorFlow not found.flow_id
RunNotFoundErrorRun not found.run_id
OAuthErrorOAuth flow failed.error_code
RegistryErrorRegistry sync or parse failure--

IDE Autocomplete

Type stubs for 250+ connectors ship in the package — autocomplete works after pip install fastn-ai:

  • PyCharm / IntelliJ: Works automatically
  • VS Code (Pylance): Works automatically
  • mypy: Works automatically (PEP 561 py.typed marker included)

To refresh stubs with the latest connector schemas, run fastn connector sync.

Examples

See examples/ for runnable scripts:

DirectoryContents
examples/sdk/SDK usage — basic, async, LLM agents (OpenAI/Claude/Gemini/Bedrock), multi-tenant, flows, auth, environments, error handling
examples/cli/CLI usage — all commands with examples

Development

# Install in dev mode
pip install -e ".[dev]"

# Run all tests (576 tests, ~7s)
make test

# Run only SDK tests
make test-sdk

# Run only CLI tests
make test-cli

# Run a single test file
make test-file F=tests/cli/test_cli_commands.py

# Or use pytest directly
python3 -m pytest tests/sdk/ -q       # SDK only
python3 -m pytest tests/cli/ -q       # CLI only
python3 -m pytest tests/ -q           # All

Test Structure

tests/
├── sdk/                           # SDK core tests
│   ├── test_client.py             # FastnClient, AsyncFastnClient
│   ├── test_connector.py          # DynamicConnector proxy
│   ├── test_config.py             # Config loading, env vars, validation
│   ├── test_exceptions.py         # Exception hierarchy
│   ├── test_auth.py               # Auth helpers
│   ├── test_oauth.py              # OAuth device flow, token refresh
│   ├── test_projects.py           # Projects namespace
│   └── test_skills.py             # Skills namespace
└── cli/                           # CLI command tests
    ├── test_cli_commands.py        # All CLI commands (sync, add, remove, list, run, agent, etc.)
    ├── test_cli_helpers.py         # CLI helper functions
    ├── test_agent_command.py       # fastn agent command
    ├── test_agent_helpers.py       # Agent helper functions
    ├── test_helpers_extended.py    # Extended helper tests (token, schema, parsing)
    ├── test_skills_command.py      # fastn skill command
    ├── test_detect_api_error.py    # API error detection across providers
    └── test_detect_languages.py    # SDK language detection for stub generation

License

MIT