Executing Tools
If you're building an agent, we recommend using sessions instead. Sessions handle tool fetching, authentication, and execution automatically.
LLMs on their own can only do generation. Tool calling changes that by letting them interact with external services. Instead of just drafting an email, the model can call GMAIL_SEND_EMAIL to actually send it. The tool's results feed back to the LLM, closing the loop so it can decide, act, observe, and adapt.
In Devcaster, every tool is a single API action—fully described with schema, parameters, and return type. Tools live inside toolkits like Gmail, Slack, or GitHub, and Devcaster handles authentication and user scoping.
User Scoping: All tools are scoped to a specific user - that's why every example includes a user_id. Learn how to structure User IDs in User Management. Each user must authenticate with their respective services (Gmail, Calendar, etc.) - see Authentication.
Using Chat Completions
Use the Devcaster SDK with providers like OpenAI, Anthropic, and Google AI. To learn how to set up these providers, see Providers.
from devcaster import Devcaster
from devcaster_openai import OpenAIProvider
from openai import OpenAI
from datetime import datetime
# Use a unique identifier for each user in your application
user_id = "user-k7334"
# Create devcaster client
devcaster = Devcaster(provider=OpenAIProvider(), api_key="your_devcaster_api_key")
# Create openai client
openai = OpenAI()
# Get calendar tools for this user
tools = devcaster.tools.get(
user_id=user_id,
tools=["GOOGLECALENDAR_EVENTS_LIST"]
)
# Ask the LLM to check calendar
result = openai.chat.completions.create(
model="gpt-4o-mini",
tools=tools,
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": f"What's on my calendar for the next 7 days?"}
]
)
# Handle tool calls
result = devcaster.provider.handle_tool_calls(user_id=user_id, response=result)
print(result)import { Devcaster } from '@devcaster/core';
import { AnthropicProvider } from '@devcaster/anthropic';
import { Anthropic } from '@anthropic-ai/sdk';
// Use a unique identifier for each user in your application
const userId = 'user-k7334';
// Create anthropic client
const anthropic = new Anthropic();
// Create Devcaster client
const devcaster = new Devcaster({
apiKey: "your-devcaster-api-key",
provider: new AnthropicProvider(),
});
// Get calendar tools for this user
const tools = await devcaster.tools.get(userId, {
tools: ['GOOGLECALENDAR_EVENTS_LIST'],
});
// Ask the LLM to check calendar
const msg = await anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
tools: tools,
messages: [
{
role: 'user',
content: `What's on my calendar for the next 7 days?`,
},
],
max_tokens: 1024,
});
// Handle tool calls
const result = await devcaster.provider.handleToolCalls(userId, msg);
console.log('Results:', JSON.stringify(result, null, 2));Using Agentic Frameworks
Agentic frameworks automatically handle the tool execution loop. Devcaster provides support for frameworks like this by making sure the tools are formatted into the correct objects for the agentic framework to execute.
import asyncio
from agents import Agent, Runner
from devcaster import Devcaster
from devcaster_openai_agents import OpenAIAgentsProvider
# Use a unique identifier for each user in your application
user_id = "user-k7334"
# Initialize Devcaster toolset
devcaster = Devcaster(provider=OpenAIAgentsProvider(), api_key="your_devcaster_api_key")
# Get all tools for the user
tools = devcaster.tools.get(
user_id=user_id,
toolkits=["DEVCASTER_SEARCH"],
)
# Create an agent with the tools
agent = Agent(
name="Deep Researcher",
instructions="You are an investigative journalist.",
tools=tools,
)
async def main():
result = await Runner.run(
starting_agent=agent,
input=("Do a thorough DEEP research on Golden Gate Bridge"),
)
print(result.final_output)
# Run the agent
asyncio.run(main())import { Devcaster } from '@devcaster/core';
import { generateText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { VercelProvider } from '@devcaster/vercel';
// Use a unique identifier for each user in your application
const userId = 'user-k7334';
// Initialize Devcaster toolset
const devcaster = new Devcaster({
apiKey: process.env.DEVCASTER_API_KEY,
provider: new VercelProvider(),
});
// Get all tools for the user
const tools = await devcaster.tools.get(userId, {
toolkits: ['HACKERNEWS_GET_LATEST_POSTS'],
limit: 10,
});
// Generate text with tool use
const { text } = await generateText({
model: anthropic('claude-sonnet-4-20250514'),
messages: [
{
role: 'user',
content: 'Do a thorough DEEP research on the top articles on Hacker News about Devcaster',
},
],
tools,
});
console.log(text);Direct Tool Execution
If you just want to call a tool without using any framework or LLM provider, you can use the execute method directly.
Finding tool parameters and types:
Platform UI: Auth Configs → Select your toolkit → Tools & Triggers → Select the tool to see its required and optional parameters
CLI: For Python and TypeScript projects, run devcaster generate to generate types. Learn more →
from devcaster import Devcaster
user_id = "user-k7334"
# Configure toolkit versions at SDK level
devcaster = Devcaster(
api_key="your_devcaster_key",
toolkit_versions={"github": "20251027_00"}
)
# Find available arguments for any tool in the Devcaster dashboard
result = devcaster.tools.execute(
"GITHUB_LIST_STARGAZERS",
user_id=user_id,
arguments={"owner": "DevcasterHQ", "repo": "devcaster", "page": 1, "per_page": 5}
)
print(result)import { Devcaster } from "@devcaster/core";
const userId = "user-k7334";
// Configure toolkit versions at SDK level
const devcaster = new Devcaster({
apiKey: "your_devcaster_key",
toolkitVersions: { github: "20251027_00" }
});
// Find available arguments for any tool in the Devcaster dashboard
const result = await devcaster.tools.execute("GITHUB_LIST_STARGAZERS", {
userId,
arguments: {
"owner": "DevcasterHQ",
"repo": "devcaster",
"page": 1,
"per_page": 5
},
});
console.log('GitHub stargazers:', JSON.stringify(result, null, 2));The examples above configure toolkit versions at SDK initialization. You can also pass versions per-execution or use environment variables. See toolkit versioning for all configuration options.
Proxy Execute
You can proxy requests to any supported toolkit API and let Devcaster inject the authentication state. This is useful when you need an API endpoint that isn't available as a predefined tool.
The endpoint can be a relative path or absolute URL. Devcaster uses the connected_account_id to determine the toolkit and resolve relative paths against the appropriate base URL.
# Send a proxy request to the endpoint
response = devcaster.tools.proxy(
endpoint="/repos/devcasterhq/devcaster/issues/1",
method="GET",
connected_account_id="ca_jI6********", # use connected account for github
parameters=[
{
"name": "Accept",
"value": "application/vnd.github.v3+json",
"type": "header",
},
],
)
print(response)import { Devcaster } from '@devcaster/core';
const devcaster = new Devcaster({ apiKey: 'your_api_key' });
// ---cut---
// Send a proxy request to the endpoint
const { data } = await devcaster.tools.proxyExecute({
endpoint:'/repos/devcasterhq/devcaster/issues/1',
method: 'GET',
connectedAccountId: 'ca_jI*****', // use connected account for github
parameters:[
{
"name": "Accept",
"value": "application/vnd.github.v3+json",
"in": "header",
},
],
});
console.log(data);Need an API that isn't supported by any Devcaster toolkit, or want to extend an existing one? Learn how to create custom tools.
Automatic File Handling
Devcaster handles file operations automatically. Pass file paths to tools that need them, and get local file paths back from tools that return files.
File Upload
Pass local file paths, URLs, or File objects to tools that accept files:
# Upload a local file to Google Drive
result = devcaster.tools.execute(
slug="GOOGLEDRIVE_UPLOAD_FILE",
user_id="user-1235***",
arguments={"file_to_upload": os.path.join(os.getcwd(), "document.pdf")},
)
print(result) # Print Google Drive file detailsimport { Devcaster } from '@devcaster/core';
import path from 'path';
const devcaster = new Devcaster({ apiKey: 'your_api_key' });
// ---cut---
// Upload a local file to Google Drive
const result = await devcaster.tools.execute('GOOGLEDRIVE_UPLOAD_FILE', {
userId: 'user-4235***',
arguments: {
file_to_upload: path.join(__dirname, 'document.pdf')
}
});
console.log(result.data); // Contains Google Drive file detailsFile Download
When tools return files, Devcaster downloads them to the local directory and provides the file path in the response:
devcaster = Devcaster(
api_key="your_devcaster_key",
file_download_dir="./downloads" # Optional: Specify download directory
)
result = devcaster.tools.execute(
"GOOGLEDRIVE_DOWNLOAD_FILE",
user_id="user-1235***",
arguments={"file_id": "your_file_id"},
)
# Result includes local file path
print(result)import { Devcaster } from '@devcaster/core';
const devcaster = new Devcaster({ apiKey: 'your_api_key' });
// ---cut---
// Download a file from Google Drive
const result = await devcaster.tools.execute('GOOGLEDRIVE_DOWNLOAD_FILE', {
userId: 'user-1235***',
arguments: {
file_id: 'your-file-id'
}
});
// Result includes local file path
console.log(result);Disabling Auto File Handling
You can disable automatic file handling when initializing the TypeScript SDK. When disabled, handle file uploads and downloads manually using files.upload and files.download:
import { Devcaster } from '@devcaster/core';
import path from 'path';
// ---cut---
const devcaster = new Devcaster({
apiKey: process.env.DEVCASTER_API_KEY,
autoUploadDownloadFiles: false
});
// Now you need to handle files manually using devcaster.files API
const fileData = await devcaster.files.upload({
file: path.join(__dirname, 'document.pdf'),
toolSlug: 'GOOGLEDRIVE_UPLOAD_FILE',
toolkitSlug: 'googledrive'
});