Skip to main content

What are API Sources?

API Sources transform REST APIs into dynamic MCP tools. Instead of manually implementing API clients, you configure the base URL and authentication, and Craft Agents creates a flexible tool that handles requests automatically.

How it Works

  1. Configure API endpoint and auth method
  2. System generates a flexible api_{name} tool
  3. Agent calls tool with { path, method, params }
  4. Authentication headers injected automatically
  5. Response returned (or saved if binary/large)

Authentication Types

API sources support six authentication methods:

Bearer Token

Most common for API keys and access tokens:
{
  "type": "api",
  "api": {
    "baseUrl": "https://api.example.com",
    "authType": "bearer",
    "authScheme": "Bearer"
  }
}
Supports custom schemes:
  • "Bearer" (default) → Authorization: Bearer token123
  • "Token"Authorization: Token token123
  • "" (empty) → Authorization: token123 (no prefix)

Header Authentication

Custom header for API keys:
{
  "type": "api",
  "api": {
    "baseUrl": "https://api.example.com",
    "authType": "header",
    "headerName": "X-API-Key"
  }
}
Request includes:
X-API-Key: your-api-key-here

Multi-Header Authentication

APIs requiring multiple auth headers (e.g., Datadog):
{
  "type": "api",
  "api": {
    "baseUrl": "https://api.datadoghq.com",
    "authType": "header",
    "headerNames": ["DD-API-KEY", "DD-APPLICATION-KEY"]
  }
}
Credential stored as JSON:
{
  "DD-API-KEY": "api-key-value",
  "DD-APPLICATION-KEY": "app-key-value"
}

Query Parameter Authentication

API key in URL query string:
{
  "type": "api",
  "api": {
    "baseUrl": "https://api.example.com",
    "authType": "query",
    "queryParam": "api_key"
  }
}
Requests formatted as:
https://api.example.com/endpoint?api_key=your-key

Basic Authentication

Username and password:
{
  "type": "api",
  "api": {
    "baseUrl": "https://api.example.com",
    "authType": "basic"
  }
}
Credential stored as JSON:
{
  "username": "user",
  "password": "pass"
}
Encoded automatically:
Authorization: Basic dXNlcjpwYXNz

OAuth (Google, Slack, Microsoft)

Full OAuth 2.0 flows with automatic token refresh. See OAuth Providers section below.

OAuth Providers

Three providers have built-in OAuth support with automatic token refresh:

Google APIs

Supports Gmail, Calendar, Drive, Docs, Sheets:
{
  "name": "Gmail",
  "provider": "google",
  "type": "api",
  "api": {
    "baseUrl": "https://gmail.googleapis.com",
    "authType": "bearer",
    "googleService": "gmail"
  }
}
ServiceScopes
gmailgmail.readonly, gmail.send, gmail.modify
calendarcalendar.readonly, calendar.events
drivedrive.readonly, drive.file
docsdocuments.readonly
sheetsspreadsheets.readonly
Override with custom scopes:
"googleScopes": ["https://www.googleapis.com/auth/gmail.compose"]
1

Configure Source

Set provider: "google" and googleService or googleScopes
2

Trigger OAuth Flow

Agent calls source_google_oauth_trigger or user initiates from UI
3

User Authenticates

Browser opens Google OAuth consent screen
4

Token Storage

Access token, refresh token, and expiry saved encrypted
5

Automatic Refresh

Tokens refreshed automatically when expired (within 5 min of expiry)

Slack APIs

Supports Slack Web API with user authentication:
{
  "name": "Slack",
  "provider": "slack",
  "type": "api",
  "api": {
    "baseUrl": "https://slack.com/api",
    "authType": "bearer",
    "slackService": "full"
  }
}
Uses user_scope for user authentication (not bot):
ServiceUser Scopes
messagingchat:write, channels:read, groups:read
channelschannels:history, channels:read
usersusers:read, users:read.email
filesfiles:read, files:write
fullAll of the above
Override with custom scopes:
"slackUserScopes": ["chat:write", "users:read"]

Microsoft APIs

Supports Microsoft Graph (Outlook, OneDrive, Calendar, Teams):
{
  "name": "Outlook",
  "provider": "microsoft",
  "type": "api",
  "api": {
    "baseUrl": "https://graph.microsoft.com/v1.0",
    "authType": "bearer",
    "microsoftService": "outlook"
  }
}
ServiceScopes
outlookMail.Read, Mail.Send
microsoft-calendarCalendars.Read, Calendars.ReadWrite
onedriveFiles.Read, Files.ReadWrite
teamsTeam.ReadBasic.All, Chat.Read
sharepointSites.Read.All
Override with custom scopes:
"microsoftScopes": ["Mail.Read", "Calendars.Read"]
Microsoft rotates refresh tokens on each use. The system handles this automatically by updating stored credentials after each refresh.

Dynamic API Tool

Each API source generates a single flexible tool:
api_{sourceSlug}(
  path: string,
  method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
  params?: Record<string, unknown>,
  _intent?: string
)
Example usage:
await api_gmail({
  path: '/gmail/v1/users/me/messages',
  method: 'GET',
  params: { q: 'is:unread', maxResults: 10 },
  _intent: 'Get recent unread emails to summarize'
});

Request Processing

1

URL Construction

Base URL + path normalized:
https://api.example.com + /v1/users → https://api.example.com/v1/users
2

Auth Injection

Credentials retrieved and headers built:
Authorization: Bearer fresh-token-here
Content-Type: application/json
3

Parameter Handling

  • GET: params → query string
  • POST/PUT/PATCH: params → JSON body
4

Response Processing

  • Binary responses saved to downloads/
  • Large responses (>25KB) summarized
  • Errors include status code and message

Token Refresh Flow

OAuth sources automatically refresh tokens before expiry:
// Token getter function passed to API tool
const getToken = async () => {
  const cred = await credManager.load(source);
  
  // Check if token needs refresh (within 5 min of expiry)
  if (credManager.needsRefresh(cred)) {
    return await credManager.refresh(source);
  }
  
  return cred.value;
};

// API tool calls getToken before each request
const server = createApiServer(config, getToken, sessionPath);
Refresh logic is deduplicated - concurrent requests reuse the same refresh promise to prevent token rotation conflicts (important for Microsoft).

Bridge MCP Server

For Codex and Copilot sessions, API sources run in a subprocess (Bridge MCP Server). Since it can’t access encrypted credentials directly, a credential cache is used:
1

Main Process Writes Cache

When source is enabled, decrypt credential and write to:
~/.craft-agent/workspaces/{ws}/sources/{slug}/.credential-cache.json
Permissions: 0600 (owner read/write only)
2

Bridge Reads on Each Request

Bridge MCP Server reads fresh credential from cache file before each API call
3

Expiry Check

If token expired, Bridge returns auth error and user must re-authenticate
This is a passive refresh model - Bridge doesn’t actively refresh tokens. If a token expires mid-session, the user must re-authenticate via the UI.

Creating API Sources

import { createSource } from '@craft-agent/shared/sources';

await createSource(workspaceRootPath, {
  name: 'Exa Search',
  provider: 'exa',
  type: 'api',
  api: {
    baseUrl: 'https://api.exa.ai',
    authType: 'bearer'
  },
  icon: '🔍',
  enabled: true
});

// Store API key
import { getSourceCredentialManager } from '@craft-agent/shared/sources';
const credManager = getSourceCredentialManager();
await credManager.save(source, { value: 'exa-api-key-123' });

Default Headers

Include headers with every request (e.g., beta features):
{
  "type": "api",
  "api": {
    "baseUrl": "https://api.example.com",
    "authType": "bearer",
    "defaultHeaders": {
      "X-API-Version": "2024-01",
      "X-Enable-Beta": "true"
    }
  }
}

Binary File Handling

API responses are automatically checked for binary content:
// Binary response (PDF, image, archive)
const result = await api_example({
  path: '/generate-report',
  method: 'POST',
  params: { format: 'pdf' }
});

// Returns:
// "[File saved: /path/to/session/downloads/api_example_2024-03-04T12-30-00.pdf (2.3 MB)]"
Supported formats:
  • Images: PNG, JPEG, GIF, WebP, SVG
  • Documents: PDF, DOCX, XLSX, PPTX
  • Archives: ZIP, TAR, GZ
  • Media: MP3, MP4, WAV
Files larger than 100MB are rejected before download to prevent OOM errors.

Large Response Handling

Text responses over 25KB are automatically handled:
  1. Save to file: Response saved to session/downloads/
  2. Summarize: If summarize callback provided, AI generates summary
  3. Return path: Agent receives file path instead of full content
const summarize = (prompt: string) => agent.runMiniCompletion(prompt);

const server = createApiServer(config, credential, sessionPath, summarize);

Best Practices

OAuth tokens are scoped to the authenticated user. Use bearer tokens only for service accounts or public data.
List common endpoints, query patterns, and response structures:
## Common Endpoints
- `/search` - Search issues (GET, query param: q)
- `/issues` - Create issue (POST, body: { title, description })
Always include _intent parameter to explain what you’re trying to accomplish. This helps with debugging and logging.
Check API documentation for rate limits and implement backoff strategies in your agents.

Next Steps

MCP Servers

Learn about full-featured MCP servers with tools and resources

Local Files

Connect to local filesystems and Git repositories

Build docs developers (and LLMs) love