remove examples to be absorved as templates (#753)

🪓 

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Removes legacy example projects to be replaced by templates.
> 
> - Deletes entire `contrib/examples/*` subtrees: `ai-sdk`, `crewai`,
`langchain`, `langchain-ts`, `langgraph-ts`, and `mastra`
> - Removes associated source files, READMEs, env samples, package/lock
files, and miscellaneous configs
> 
> No product code changed; docs/examples only.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
0efc4ecc3e5a9d60d8cf3e853fd0d9aecd1ed4a8. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This commit is contained in:
Mateo Torres 2026-01-23 23:34:42 -03:00 committed by GitHub
parent b3a90889c6
commit e5312bc860
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
60 changed files with 0 additions and 12268 deletions

View file

@ -1,2 +0,0 @@
OPENAI_API_KEY=sk-proj-1234567890
ARCADE_API_KEY=arc_1234567890

View file

@ -1,42 +0,0 @@
# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Environment variables
.env
.env.local
.env.*.local
# Build output
dist/
build/
out/
# IDE and editor files
.idea/
.vscode/
*.swp
*.swo
.DS_Store
# Logs
logs/
*.log
# Testing
coverage/
# Temporary files
tmp/
temp/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history

View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2025, Arcade AI
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,95 +0,0 @@
<h3 align="center">
<a name="readme-top"></a>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/ArcadeAI/.github/refs/heads/main/profile/assets/new_arcade_logo_white.svg" width="300">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/ArcadeAI/.github/refs/heads/main/profile/assets/new_arcade_logo_black.svg" width="300">
<img alt="Fallback image description" src="https://raw.githubusercontent.com/ArcadeAI/.github/refs/heads/main/profile/assets/new_arcade_logo_black.svg" width="300" >
</picture>
</h3>
<div align="center">
<h3>Arcade AI SDK Example</h3>
<a href="https://github.com/your-organization/agents-arcade/blob/main/LICENSE">
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License">
</a>
<p align="center">
<a href="https://docs.arcade.dev" target="_blank">Arcade Documentation</a>
<a href="https://docs.arcade.dev/toolkits" target="_blank">Integrations</a>
<a href="https://github.com/ArcadeAI/arcade-js" target="_blank">Arcade JS Client</a>
<a href="https://sdk.vercel.ai/" target="_blank">AI SDK</a>
</p>
</div>
# Arcade - AI SDK
This example demonstrates how to integrate [Arcade](https://docs.arcade.dev) with the [Vercel AI SDK](https://sdk.vercel.ai/) to create powerful AI agents. Arcade provides access to a wide range of tools including Gmail, Slack, LinkedIn, and more. You can also develop custom tools using the [Tool SDK](https://github.com/ArcadeAI/arcade-mcp).
For a list of all hosted tools and auth providers, see the [Arcade Integrations](https://docs.arcade.dev/toolkits) documentation.
## Prerequisites
- [Node.js](<https://nodejs.org/en/download/>) (v18.20.8 or higher)
- [pnpm](<https://pnpm.io/installation>) (v9.15.9 or higher)
- [OpenAI API key](https://platform.openai.com/account/api-keys)
- [Arcade API key](https://docs.arcade.dev/home/api-keys)
## Installation
1. Install dependencies:
```bash
pnpm install
```
2. Set up environment variables:
- Copy `.env.example` to `.env`
- Fill in your API keys:
```
OPENAI_API_KEY=your_openai_api_key
ARCADE_API_KEY=your_arcade_api_key
```
## Basic Usage
This example demonstrates how to use Arcade's Gmail toolkit to create an AI agent that can read and summarize emails. The agent will access your Gmail account (after authorization) and process your most recent email.
To get started, run the development server:
```bash
pnpm dev
```
If you haven't authorized Arcade with Google yet, you'll see a message like this:
```bash
> pnpm dev
Authorization Required: Please visit this link to connect your Google account: https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&client_id=....
```
Visit the provided URL in your browser to authorize Arcade with Google. Once authorized, run the script again.
You can also wait for authorization to complete before running the script using the helper methods in arcade.js.
Once you've authorized Arcade with a tool, you can use it in your agent by passing the user_id and won't need to authenticate for that specific tool again.
## Development
To modify or extend the functionality:
1. Update the `USER_ID` constant in `index.js` with your application's user identification
2. Modify the `toolkit` parameter in `getArcadeTools` to access different tools. Available toolkits include:
- `"gmail"` - Gmail
- `"slack"` - Slack messaging and channels
- `"github"` - GitHub repositories and issues
- And more in [Arcade Integrations](https://docs.arcade.dev/toolkits) documentation
## Security
- Never commit your `.env` file
- Keep your API keys secure
- Use appropriate user identification in production
## License
This project is licensed under the MIT License - see the LICENSE file for details.

View file

@ -1,48 +0,0 @@
import { openai } from "@ai-sdk/openai"
import { Arcade } from "@arcadeai/arcadejs"
import { executeOrAuthorizeZodTool, toZodToolSet } from "@arcadeai/arcadejs/lib"
import { generateText } from "ai"
const arcade = new Arcade()
/**
* Get the Gmail toolkit.
*/
const gmailToolkit = await arcade.tools.list({
limit: 25,
toolkit: "gmail",
})
/**
* The Vercel AI SDK requires tools to be defined using Zod, a TypeScript-first schema validation library
* that has become the standard for runtime type checking. Zod is particularly valuable because it:
* - Provides runtime type safety and validation
* - Offers excellent TypeScript integration with automatic type inference
* - Has a simple, declarative API for defining schemas
* - Is widely adopted in the TypeScript ecosystem
*
* Arcade provides `toZodToolSet` to convert our tools into Zod format, making them compatible
* with the AI SDK.
*
* The `executeOrAuthorizeZodTool` helper function simplifies authorization.
* It checks if the tool requires authorization: if so, it returns an authorization URL,
* otherwise, it runs the tool directly without extra boilerplate.
*
* Learn more: https://docs.arcade.dev/home/use-tools/get-tool-definitions#get-zod-tool-definitions
*/
const gmailTools = toZodToolSet({
tools: gmailToolkit.items,
client: arcade,
userId: "<YOUR_USER_ID>", // Your app's internal ID for the user (an email, UUID, etc). It's used internally to identify your user in Arcade
executeFactory: executeOrAuthorizeZodTool, // Checks if tool is authorized and executes it, or returns authorization URL if needed
})
const result = await generateText({
model: openai("gpt-4o-mini"),
prompt: "Read my last email and summarize it in a few sentences",
tools: gmailTools,
maxSteps: 5,
})
// Log the result
console.log(result.text)

View file

@ -1,50 +0,0 @@
import { openai } from "@ai-sdk/openai"
import { Arcade } from "@arcadeai/arcadejs"
import { executeOrAuthorizeZodTool, toZodToolSet } from "@arcadeai/arcadejs/lib"
import { streamText } from "ai"
const arcade = new Arcade()
/**
* Get the Gmail toolkit.
*/
const gmailToolkit = await arcade.tools.list({
limit: 25,
toolkit: "gmail",
})
/**
* The Vercel AI SDK requires tools to be defined using Zod, a TypeScript-first schema validation library
* that has become the standard for runtime type checking. Zod is particularly valuable because it:
* - Provides runtime type safety and validation
* - Offers excellent TypeScript integration with automatic type inference
* - Has a simple, declarative API for defining schemas
* - Is widely adopted in the TypeScript ecosystem
*
* Arcade provides `toZodToolSet` to convert our tools into Zod format, making them compatible
* with the AI SDK.
*
* The `executeOrAuthorizeZodTool` helper function simplifies authorization.
* It checks if the tool requires authorization: if so, it returns an authorization URL,
* otherwise, it runs the tool directly without extra boilerplate.
*
* Learn more: https://docs.arcade.dev/home/use-tools/get-tool-definitions#get-zod-tool-definitions
*/
const gmailTools = toZodToolSet({
tools: gmailToolkit.items,
client: arcade,
userId: "<YOUR_USER_ID>", // Your app's internal ID for the user (an email, UUID, etc). It's used internally to identify your user in Arcade
executeFactory: executeOrAuthorizeZodTool,
})
const { textStream } = streamText({
model: openai("gpt-4o-mini"),
prompt: "Read my last email and summarize it in a few sentences",
tools: gmailTools,
maxSteps: 5,
})
// Stream the response to the console
for await (const chunk of textStream) {
process.stdout.write(chunk)
}

View file

@ -1,25 +0,0 @@
{
"name": "arcade-mcp-sdk",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"dev": "node --env-file=.env index.js",
"generateText": "node --env-file=.env generateText.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.6.5",
"dependencies": {
"@ai-sdk/openai": "^1.3.24",
"@arcadeai/arcadejs": "latest",
"ai": "^4.3.19"
},
"pnpm": {
"overrides": {
"form-data@>=4.0.0 <4.0.5": ">=4.0.5"
}
}
}

View file

@ -1,504 +0,0 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
overrides:
form-data@>=4.0.0 <4.0.5: '>=4.0.5'
importers:
.:
dependencies:
'@ai-sdk/openai':
specifier: ^1.3.24
version: 1.3.24(zod@3.25.76)
'@arcadeai/arcadejs':
specifier: latest
version: 1.14.0
ai:
specifier: ^4.3.19
version: 4.3.19(react@19.1.0)(zod@3.25.76)
packages:
'@ai-sdk/openai@1.3.24':
resolution: {integrity: sha512-GYXnGJTHRTZc4gJMSmFRgEQudjqd4PUN0ZjQhPwOAYH1yOAvQoG/Ikqs+HyISRbLPCrhbZnPKCNHuRU4OfpW0Q==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.0.0
'@ai-sdk/provider-utils@2.2.8':
resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.23.8
'@ai-sdk/provider@1.1.3':
resolution: {integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==}
engines: {node: '>=18'}
'@ai-sdk/react@1.2.12':
resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==}
engines: {node: '>=18'}
peerDependencies:
react: ^18 || ^19 || ^19.0.0-rc
zod: ^3.23.8
peerDependenciesMeta:
zod:
optional: true
'@ai-sdk/ui-utils@1.2.11':
resolution: {integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.23.8
'@arcadeai/arcadejs@1.14.0':
resolution: {integrity: sha512-Zcg4kxkZAJfuPyHueUpD5rXUrHZ2C8hHA/2b3bYArfCEcYhY6RkbyA8S7pbxKKHyzBf/929h6SE2SmE+xoMciw==}
'@opentelemetry/api@1.9.0':
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
engines: {node: '>=8.0.0'}
'@types/diff-match-patch@1.0.36':
resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==}
'@types/node-fetch@2.6.13':
resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==}
'@types/node@18.19.130':
resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==}
abort-controller@3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'}
agentkeepalive@4.6.0:
resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==}
engines: {node: '>= 8.0.0'}
ai@4.3.19:
resolution: {integrity: sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q==}
engines: {node: '>=18'}
peerDependencies:
react: ^18 || ^19 || ^19.0.0-rc
zod: ^3.23.8
peerDependenciesMeta:
react:
optional: true
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
chalk@5.6.2:
resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
diff-match-patch@1.0.5:
resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==}
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
es-object-atoms@1.1.1:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
es-set-tostringtag@2.1.0:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'}
event-target-shim@5.0.1:
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
engines: {node: '>=6'}
form-data-encoder@1.7.2:
resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==}
form-data@4.0.5:
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
engines: {node: '>= 6'}
formdata-node@4.4.1:
resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==}
engines: {node: '>= 12.20'}
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
get-proto@1.0.1:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
has-tostringtag@1.0.2:
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
engines: {node: '>= 0.4'}
hasown@2.0.2:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
humanize-ms@1.2.1:
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
json-schema@0.4.0:
resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
jsondiffpatch@0.6.0:
resolution: {integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
deprecated: Use your platform's native DOMException instead
node-fetch@2.7.0:
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
react@19.1.0:
resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==}
engines: {node: '>=0.10.0'}
secure-json-parse@2.7.0:
resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
swr@2.3.6:
resolution: {integrity: sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw==}
peerDependencies:
react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
throttleit@2.1.0:
resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==}
engines: {node: '>=18'}
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
use-sync-external-store@1.6.0:
resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
web-streams-polyfill@4.0.0-beta.3:
resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==}
engines: {node: '>= 14'}
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
zod-to-json-schema@3.25.0:
resolution: {integrity: sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==}
peerDependencies:
zod: ^3.25 || ^4
zod@3.25.76:
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
snapshots:
'@ai-sdk/openai@1.3.24(zod@3.25.76)':
dependencies:
'@ai-sdk/provider': 1.1.3
'@ai-sdk/provider-utils': 2.2.8(zod@3.25.76)
zod: 3.25.76
'@ai-sdk/provider-utils@2.2.8(zod@3.25.76)':
dependencies:
'@ai-sdk/provider': 1.1.3
nanoid: 3.3.11
secure-json-parse: 2.7.0
zod: 3.25.76
'@ai-sdk/provider@1.1.3':
dependencies:
json-schema: 0.4.0
'@ai-sdk/react@1.2.12(react@19.1.0)(zod@3.25.76)':
dependencies:
'@ai-sdk/provider-utils': 2.2.8(zod@3.25.76)
'@ai-sdk/ui-utils': 1.2.11(zod@3.25.76)
react: 19.1.0
swr: 2.3.6(react@19.1.0)
throttleit: 2.1.0
optionalDependencies:
zod: 3.25.76
'@ai-sdk/ui-utils@1.2.11(zod@3.25.76)':
dependencies:
'@ai-sdk/provider': 1.1.3
'@ai-sdk/provider-utils': 2.2.8(zod@3.25.76)
zod: 3.25.76
zod-to-json-schema: 3.25.0(zod@3.25.76)
'@arcadeai/arcadejs@1.14.0':
dependencies:
'@types/node': 18.19.130
'@types/node-fetch': 2.6.13
abort-controller: 3.0.0
agentkeepalive: 4.6.0
form-data-encoder: 1.7.2
formdata-node: 4.4.1
node-fetch: 2.7.0
zod: 3.25.76
transitivePeerDependencies:
- encoding
'@opentelemetry/api@1.9.0': {}
'@types/diff-match-patch@1.0.36': {}
'@types/node-fetch@2.6.13':
dependencies:
'@types/node': 18.19.130
form-data: 4.0.5
'@types/node@18.19.130':
dependencies:
undici-types: 5.26.5
abort-controller@3.0.0:
dependencies:
event-target-shim: 5.0.1
agentkeepalive@4.6.0:
dependencies:
humanize-ms: 1.2.1
ai@4.3.19(react@19.1.0)(zod@3.25.76):
dependencies:
'@ai-sdk/provider': 1.1.3
'@ai-sdk/provider-utils': 2.2.8(zod@3.25.76)
'@ai-sdk/react': 1.2.12(react@19.1.0)(zod@3.25.76)
'@ai-sdk/ui-utils': 1.2.11(zod@3.25.76)
'@opentelemetry/api': 1.9.0
jsondiffpatch: 0.6.0
zod: 3.25.76
optionalDependencies:
react: 19.1.0
asynckit@0.4.0: {}
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
chalk@5.6.2: {}
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
delayed-stream@1.0.0: {}
dequal@2.0.3: {}
diff-match-patch@1.0.5: {}
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
es-errors: 1.3.0
gopd: 1.2.0
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
es-set-tostringtag@2.1.0:
dependencies:
es-errors: 1.3.0
get-intrinsic: 1.3.0
has-tostringtag: 1.0.2
hasown: 2.0.2
event-target-shim@5.0.1: {}
form-data-encoder@1.7.2: {}
form-data@4.0.5:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
es-set-tostringtag: 2.1.0
hasown: 2.0.2
mime-types: 2.1.35
formdata-node@4.4.1:
dependencies:
node-domexception: 1.0.0
web-streams-polyfill: 4.0.0-beta.3
function-bind@1.1.2: {}
get-intrinsic@1.3.0:
dependencies:
call-bind-apply-helpers: 1.0.2
es-define-property: 1.0.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
function-bind: 1.1.2
get-proto: 1.0.1
gopd: 1.2.0
has-symbols: 1.1.0
hasown: 2.0.2
math-intrinsics: 1.1.0
get-proto@1.0.1:
dependencies:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
gopd@1.2.0: {}
has-symbols@1.1.0: {}
has-tostringtag@1.0.2:
dependencies:
has-symbols: 1.1.0
hasown@2.0.2:
dependencies:
function-bind: 1.1.2
humanize-ms@1.2.1:
dependencies:
ms: 2.1.3
json-schema@0.4.0: {}
jsondiffpatch@0.6.0:
dependencies:
'@types/diff-match-patch': 1.0.36
chalk: 5.6.2
diff-match-patch: 1.0.5
math-intrinsics@1.1.0: {}
mime-db@1.52.0: {}
mime-types@2.1.35:
dependencies:
mime-db: 1.52.0
ms@2.1.3: {}
nanoid@3.3.11: {}
node-domexception@1.0.0: {}
node-fetch@2.7.0:
dependencies:
whatwg-url: 5.0.0
react@19.1.0: {}
secure-json-parse@2.7.0: {}
swr@2.3.6(react@19.1.0):
dependencies:
dequal: 2.0.3
react: 19.1.0
use-sync-external-store: 1.6.0(react@19.1.0)
throttleit@2.1.0: {}
tr46@0.0.3: {}
undici-types@5.26.5: {}
use-sync-external-store@1.6.0(react@19.1.0):
dependencies:
react: 19.1.0
web-streams-polyfill@4.0.0-beta.3: {}
webidl-conversions@3.0.1: {}
whatwg-url@5.0.0:
dependencies:
tr46: 0.0.3
webidl-conversions: 3.0.1
zod-to-json-schema@3.25.0(zod@3.25.76):
dependencies:
zod: 3.25.76
zod@3.25.76: {}

View file

@ -1,137 +0,0 @@
"""
This is an example of how to use Arcade with CrewAI.
The ArcadeToolManager allows you to handle both authorization and tool execution in a custom way.
This example demonstrates how to implement a custom auth handler and a custom tool execute handler.
The example assumes the following:
1. You have an Arcade API key and have set the ARCADE_API_KEY environment variable.
2. You have an OpenAI API key and have set the OPENAI_API_KEY environment variable.
3. You have installed the necessary dependencies in the requirements.txt file: `pip install -r requirements.txt`
"""
from typing import Any
from crewai import Agent, Crew, Task
from crewai.crews import CrewOutput
from crewai.llm import LLM
from crewai_arcade import ArcadeToolManager
USER_ID = "user@example.com"
def custom_auth_flow(
manager: ArcadeToolManager, tool_name: str, **tool_input: dict[str, Any]
) -> Any:
"""Custom auth flow for the ArcadeToolManager
This function is called when CrewAI needs to call a tool that requires authorization.
Authorization is handled before executing the tool.
This function overrides the ArcadeToolManager's default auth flow performed by ArcadeToolManager.authorize_tool
"""
# Get authorization status
auth_response = manager.authorize(tool_name, USER_ID)
# If the user is not authorized for the tool,
# then we need to handle the authorization before executing the tool
if not manager.is_authorized(auth_response.id):
print(f"Authorization required for tool: '{tool_name}' with inputs:")
for input_name, input_value in tool_input.items():
print(f" {input_name}: {input_value}")
# Handle authorization
print(f"\nTo authorize, visit: {auth_response.url}")
# Block until the user has completed the authorization
auth_response = manager.wait_for_auth(auth_response)
# Ensure authorization completed successfully
if not manager.is_authorized(auth_response.id):
raise ValueError(f"Authorization failed for {tool_name}. URL: {auth_response.url}")
else:
print(f"Authorization already granted for tool: '{tool_name}' with inputs:")
for input_name, input_value in tool_input.items():
print(f" {input_name}: {input_value}")
def custom_execute_flow(
manager: ArcadeToolManager, tool_name: str, **tool_input: dict[str, Any]
) -> Any:
"""Custom tool execution flow for the ArcadeToolManager
This function is called when CrewAI needs to execute a tool after any authorization has been handled.
This function overrides the ArcadeToolManager's default tool execution flow performed by ArcadeToolManager.execute_tool
"""
print(f"Executing tool: '{tool_name}' with inputs:")
for input_name, input_value in tool_input.items():
print(f" {input_name}: {input_value}")
# Execute the tool
response = manager._client.tools.execute(
tool_name=tool_name,
input=tool_input,
user_id=USER_ID,
)
# Handle the tool error if it exists
tool_error = response.output.error if response.output else None
if tool_error:
return str(tool_error)
# Return the tool output if the tool was executed successfully
if response.success:
return response.output.value # type: ignore[union-attr]
# Return a failure message if the tool was not executed successfully
return "Failed to call " + tool_name
def custom_tool_executor(
manager: ArcadeToolManager, tool_name: str, **tool_input: dict[str, Any]
) -> Any:
"""Custom tool executor for the ArcadeToolManager
ArcadeToolManager's default executor handles authorization and tool execution.
This function overrides the default executor to handle authorization and tool execution in a custom way.
"""
custom_auth_flow(manager, tool_name, **tool_input)
return custom_execute_flow(manager, tool_name, **tool_input)
def main() -> CrewOutput:
manager = ArcadeToolManager(
executor=custom_tool_executor,
)
tools = manager.get_tools(tools=["Gmail.ListEmails"])
crew_agent = Agent(
role="Main Agent",
backstory="You are a helpful assistant",
goal="Help the user with their requests",
tools=tools,
allow_delegation=False,
verbose=True,
llm=LLM(model="gpt-4o"),
)
task = Task(
description="Get the 5 most recent emails from the user's inbox and summarize them and recommend a response for each.",
expected_output="A bulleted list with a one sentence summary of each email and a recommended response to the email.",
agent=crew_agent,
tools=crew_agent.tools,
)
crew = Crew(
agents=[crew_agent],
tasks=[task],
verbose=True,
memory=True,
)
result = crew.kickoff()
return result
if __name__ == "__main__":
result = main()
print("\n\n\n ------------ Result ------------ \n\n\n")
print(result)

View file

@ -1 +0,0 @@
crewai-arcade>=0.1.0

View file

@ -1,46 +0,0 @@
"""
This is a simple example of how to use Arcade with CrewAI.
The example authenticates into the user's Gmail account, retrieves their 5 most recent emails, and summarizes them.
The example assumes the following:
1. You have an Arcade API key and have set the ARCADE_API_KEY environment variable.
2. You have an OpenAI API key and have set the OPENAI_API_KEY environment variable.
3. You have installed the necessary dependencies in the requirements.txt file: `pip install -r requirements.txt`
"""
from crewai import Agent, Crew, Task
from crewai.llm import LLM
from crewai_arcade import ArcadeToolManager
manager = ArcadeToolManager(default_user_id="user@example.com")
tools = manager.get_tools(tools=["Gmail.ListEmails"])
crew_agent = Agent(
role="Main Agent",
backstory="You are a helpful assistant",
goal="Help the user with their requests",
tools=tools,
allow_delegation=False,
verbose=True,
llm=LLM(model="gpt-4o"),
)
task = Task(
description="Get the 5 most recent emails from the user's inbox and summarize them and recommend a response for each.",
expected_output="A bulleted list with a one sentence summary of each email and a recommended response to the email.",
agent=crew_agent,
tools=crew_agent.tools,
)
crew = Crew(
agents=[crew_agent],
tasks=[task],
verbose=True,
memory=True,
)
result = crew.kickoff()
print("\n\n\n ------------ Result ------------ \n\n\n")
print(result)

View file

@ -1,2 +0,0 @@
OPENAI_API_KEY=sk-proj-1234567890
ARCADE_API_KEY=arc_1234567890

View file

@ -1,45 +0,0 @@
# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Environment variables
.env
.env.local
.env.*.local
# Build output
dist/
build/
out/
# IDE and editor files
.idea/
.vscode/
*.swp
*.swo
.DS_Store
# Logs
logs/
*.log
# Testing
coverage/
# Temporary files
tmp/
temp/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# LangGraph API
.langgraph_api

View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2025, Arcade AI
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,79 +0,0 @@
import { Arcade } from "@arcadeai/arcadejs";
import {
GmailCreateDraft,
GmailGetMessage,
GmailGetThread,
GmailSearch,
GmailSendMessage,
} from "@langchain/community/tools/gmail";
import type { StructuredTool } from "@langchain/core/tools";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { ChatOpenAI } from "@langchain/openai";
// Initialize Arcade with API key from environment
const arcade = new Arcade();
// Replace this with your application's user ID (e.g. email address, UUID, etc.)
const USER_ID = "user@example.com";
// Start the authorization process for Gmail
// see all possible gmail scopes here:
// https://developers.google.com/gmail/api/auth/scopes
const authResponse = await arcade.auth.start(USER_ID, "google", {
scopes: ["https://www.googleapis.com/auth/gmail.readonly"],
});
// Prompt the user to authorize if not already completed
if (authResponse.status !== "completed") {
console.log("Please authorize the application in your browser:");
console.log(authResponse.url);
}
// Wait for the user to complete the authorization process, if necessary...
const completedAuth = await arcade.auth.waitForCompletion(authResponse);
if (!completedAuth.context?.token) {
throw new Error("Failed to get authorization token");
}
// Get Gmail tools with credentials
const gmailParam = {
credentials: {
accessToken: completedAuth.context.token,
},
};
const tools: StructuredTool[] = [
new GmailCreateDraft(gmailParam),
new GmailGetMessage(gmailParam),
new GmailGetThread(gmailParam),
new GmailSearch(gmailParam),
new GmailSendMessage(gmailParam),
];
// Initialize the language model and create an agent
const llm = new ChatOpenAI({ model: "gpt-4o" });
const agent_executor = createReactAgent({
llm,
tools,
});
// Define the user query
const exampleQuery = "Read my latest emails and summarize them.";
// Execute the agent with the user query
const events = await agent_executor.stream(
{
messages: [
{
role: "user",
content: exampleQuery,
},
],
},
{
streamMode: "values",
},
);
for await (const event of events) {
console.log(event.messages[event.messages.length - 1]);
}

View file

@ -1,72 +0,0 @@
import { Arcade } from "@arcadeai/arcadejs";
import { executeOrAuthorizeZodTool, toZod } from "@arcadeai/arcadejs/lib";
import { tool } from "@langchain/core/tools";
import { MemorySaver } from "@langchain/langgraph";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { ChatOpenAI } from "@langchain/openai";
// 1) Initialize Arcade
const arcade = new Arcade();
// 2) Fetch Gmail toolkit from Arcade and prepare tools for LangGraph integration
const gmailToolkit = await arcade.tools.list({ toolkit: "gmail", limit: 30 });
const arcadeTools = toZod({
tools: gmailToolkit.items,
client: arcade,
userId: "<YOUR_SYSTEM_USER_ID>", // Replace this with your application's user ID (e.g. email address, UUID, etc.)
executeFactory: executeOrAuthorizeZodTool,
});
// Convert Arcade tools to LangGraph tools
const tools = arcadeTools.map(({ name, description, execute, parameters }) =>
tool(execute, {
name,
description,
schema: parameters,
}),
);
// 3) Create a ChatOpenAI model and bind the Arcade tools.
const model = new ChatOpenAI({
model: "gpt-4o",
apiKey: process.env.OPENAI_API_KEY,
});
const boundModel = model.bindTools(tools);
// 4) Use MemorySaver for checkpointing.
const memory = new MemorySaver();
// 5) Create a ReAct-style agent from the prebuilt function.
const graph = createReactAgent({
llm: boundModel,
tools,
checkpointer: memory,
});
// 6) Provide basic config and a user query.
// Note: user_id is required for the tool to be authorized
const config = {
configurable: {
thread_id: "1",
user_id: "user@example.com",
},
streamMode: "values" as const,
};
const user_input = {
messages: [
{
role: "user",
content: "List any new and important emails in my inbox.",
},
],
};
// 7) Stream the agent's output. If the tool is unauthorized, the agent will ask the user to authorize the tool.
try {
const stream = await graph.stream(user_input, config);
for await (const chunk of stream) {
console.log(chunk.messages[chunk.messages.length - 1]);
}
} catch (error) {
console.error("Error streaming response:", error);
}
// Once you login using the printed link, you can resume the agent.

View file

@ -1,148 +0,0 @@
import { pathToFileURL } from "node:url";
import { Arcade } from "@arcadeai/arcadejs";
import { toZod } from "@arcadeai/arcadejs/lib";
import type { AIMessage } from "@langchain/core/messages";
import { tool } from "@langchain/core/tools";
import { MessagesAnnotation, StateGraph } from "@langchain/langgraph";
import { ToolNode } from "@langchain/langgraph/prebuilt";
import { ChatOpenAI } from "@langchain/openai";
// Initialize Arcade with API key from environment
const arcade = new Arcade();
// Replace with your application's user ID (e.g. email address, UUID, etc.)
const USER_ID = "user@example.com";
// Initialize tools from GitHub toolkit
const githubToolkit = await arcade.tools.list({ toolkit: "github", limit: 30 });
const arcadeTools = toZod({
tools: githubToolkit.items,
client: arcade,
userId: USER_ID,
});
// Convert Arcade tools to LangGraph tools
const tools = arcadeTools.map(({ name, description, execute, parameters }) =>
tool(execute, {
name,
description,
schema: parameters,
}),
);
// Initialize the prebuilt tool node
const toolNode = new ToolNode(tools);
// Create a language model instance and bind it with the tools
const model = new ChatOpenAI({
model: "gpt-4o",
apiKey: process.env.OPENAI_API_KEY,
});
const modelWithTools = model.bindTools(tools);
// Function to check if a tool requires authorization
async function requiresAuth(toolName: string): Promise<{
needsAuth: boolean;
id: string;
authUrl: string;
}> {
const authResponse = await arcade.tools.authorize({
tool_name: toolName,
user_id: USER_ID,
});
return {
needsAuth: authResponse.status === "pending",
id: authResponse.id ?? "",
authUrl: authResponse.url ?? "",
};
}
// Function to invoke the model and get a response
async function callAgent(
state: typeof MessagesAnnotation.State,
): Promise<typeof MessagesAnnotation.Update> {
const messages = state.messages;
const response = await modelWithTools.invoke(messages);
return { messages: [response] };
}
// Function to determine the next step in the workflow based on the last message
async function shouldContinue(
state: typeof MessagesAnnotation.State,
): Promise<string> {
const lastMessage = state.messages[state.messages.length - 1] as AIMessage;
if (lastMessage.tool_calls?.length) {
for (const toolCall of lastMessage.tool_calls) {
const { needsAuth } = await requiresAuth(toolCall.name);
if (needsAuth) {
return "authorization";
}
}
return "tools"; // Proceed to tool execution if no authorization is needed
}
return "__end__"; // End the workflow if no tool calls are present
}
// Function to handle authorization for tools that require it
async function authorize(
state: typeof MessagesAnnotation.State,
): Promise<typeof MessagesAnnotation.Update> {
const lastMessage = state.messages[state.messages.length - 1] as AIMessage;
for (const toolCall of lastMessage.tool_calls || []) {
const toolName = toolCall.name;
const { needsAuth, id, authUrl } = await requiresAuth(toolName);
if (needsAuth) {
// Prompt the user to visit the authorization URL
console.log(`Visit the following URL to authorize: ${authUrl}`);
// Wait for the user to complete the authorization
const response = await arcade.auth.waitForCompletion(id);
if (response.status !== "completed") {
throw new Error("Authorization failed");
}
}
}
return { messages: [] };
}
// Build the workflow graph
const workflow = new StateGraph(MessagesAnnotation)
.addNode("agent", callAgent)
.addNode("tools", toolNode)
.addNode("authorization", authorize)
.addEdge("__start__", "agent")
.addConditionalEdges("agent", shouldContinue, [
"authorization",
"tools",
"__end__",
])
.addEdge("authorization", "tools")
.addEdge("tools", "agent");
// Compile the graph
const graph = workflow.compile();
const main = async () => {
// Define the input messages from the user
const inputs = {
messages: [
{
role: "user",
content: "Star arcadeai/arcade-mcp on github",
},
],
};
// Run the graph and stream the outputs
const stream = await graph.stream(inputs, { streamMode: "values" });
for await (const chunk of stream) {
// Print the last message in the chunk
console.log(chunk.messages[chunk.messages.length - 1].content);
}
};
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
main().catch(console.error);
}
export { graph };

View file

@ -1,9 +0,0 @@
{
"node_version": "20",
"dockerfile_lines": [],
"dependencies": ["."],
"graphs": {
"agent": "./langgraph-with-user-auth.ts:graph"
},
"env": ".env"
}

View file

@ -1,32 +0,0 @@
{
"name": "langchain-js",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"dev": "npx tsx --env-file=.env langgraph-arcade-minimal.ts",
"dev-auth": "npx tsx --env-file=.env langgraph-with-user-auth.ts",
"dev-auth-langchain": "npx tsx --env-file=.env langchain-tool-arcade-auth.ts",
"studio": "npx @langchain/langgraph-cli@latest dev"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.23.0",
"dependencies": {
"@arcadeai/arcadejs": "latest",
"@langchain/community": "^1.0.5",
"@langchain/core": "^1.1.0",
"@langchain/langgraph": "^1.0.2",
"@langchain/openai": "^1.1.3"
},
"devDependencies": {
"@types/node": "^24.10.1"
},
"pnpm": {
"overrides": {
"form-data@>=4.0.0 <4.0.4": ">=4.0.4"
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,20 +0,0 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "dist",
"rootDir": ".",
"types": ["node"],
"lib": ["ES2020"],
"declaration": true,
"sourceMap": true,
"resolveJsonModule": true
},
"include": ["./**/*.ts"],
"exclude": ["node_modules", "dist"]
}

View file

@ -1,59 +0,0 @@
import os
from arcadepy import Arcade
from google.oauth2.credentials import Credentials
from langchain_google_community import GmailToolkit
from langchain_google_community.gmail.utils import (
build_resource_service,
)
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
# Get the API key from the environment variable
api_key = os.getenv("ARCADE_API_KEY")
# Initialize the Arcade client
client = Arcade(api_key=api_key)
# Start the authorization process for Gmail
# see all possible gmail scopes here:
# https://developers.google.com/gmail/api/auth/scopes
user_id = "user@example.com"
auth_response = client.auth.start(
user_id=user_id, provider="google", scopes=["https://www.googleapis.com/auth/gmail.readonly"]
)
# Prompt the user to authorize if not already completed
if auth_response.status != "completed":
print("Please authorize the application in your browser:")
print(auth_response.url)
# Wait for the user to complete the authorization process, if necessary...
auth_response = client.auth.wait_for_completion(auth_response)
# Obtain credentials using the authorization context
creds = Credentials(auth_response.context.token)
api_resource = build_resource_service(credentials=creds)
# Initialize the Gmail toolkit with the authorized API resource
toolkit = GmailToolkit(api_resource=api_resource)
# Retrieve the tools from the langchain gmail toolkit
tools = toolkit.get_tools()
# Initialize the language model and create an agent
llm = ChatOpenAI(model="gpt-4o")
agent_executor = create_react_agent(llm, tools)
# Define the user query
example_query = "Read my latest emails and summarize them."
# Execute the agent with the user query
events = agent_executor.stream(
{"messages": [("user", example_query)]},
stream_mode="values",
)
# Display the agent's response
for event in events:
event["messages"][-1].pretty_print()

View file

@ -1,60 +0,0 @@
import os
from langchain_arcade import ToolManager
from langchain_openai import ChatOpenAI
from langgraph.checkpoint.memory import MemorySaver
from langgraph.prebuilt import create_react_agent
# 1) Set API keys (place your real keys in env variables or directly below)
arcade_api_key = os.environ.get("ARCADE_API_KEY", "YOUR_ARCADE_API_KEY")
openai_api_key = os.environ.get("OPENAI_API_KEY", "YOUR_OPENAI_API_KEY")
# 2) Create an ToolManager and fetch/add tools/toolkits
manager = ToolManager(api_key=arcade_api_key)
# Tool names follow the format "ToolkitName.ToolName"
tools = manager.init_tools(tools=["Firecrawl.ScrapeUrl"])
print(manager.tools)
# Get all tools from a toolkit
tools = manager.init_tools(toolkits=["github"])
print(manager.tools)
# add a tool
manager.add_tool("GoogleSearch.Search")
print(manager.tools)
# add a toolkit
manager.add_toolkit("GoogleSearch")
print(manager.tools)
# 3) Get StructuredTool objects for langchain
lc_tools = manager.to_langchain()
# 4) Create a ChatOpenAI model and bind the Arcade tools.
model = ChatOpenAI(model="gpt-4o", api_key=openai_api_key)
bound_model = model.bind_tools(lc_tools)
# 5) Use MemorySaver for checkpointing.
memory = MemorySaver()
# 5) Create a ReAct-style agent from the prebuilt function.
graph = create_react_agent(model=bound_model, tools=lc_tools, checkpointer=memory)
# 6) Provide basic config and a user query.
# Note: user_id is required for the tool to be authorized
config = {"configurable": {"thread_id": "1", "user_id": "user@example.com"}}
user_input = {"messages": [("user", "star the arcadeai/arcade-mcp repo on github")]}
# 7) Stream the agent's output. If the tool is unauthorized, it may trigger interrupts
for chunk in graph.stream(user_input, config, stream_mode="values"):
chunk["messages"][-1].pretty_print()
# if we were interrupted, we can check for interrupts in state
current_state = graph.get_state(config)
if current_state.tasks:
for task in current_state.tasks:
if hasattr(task, "interrupts"):
for interrupt in task.interrupts:
print(interrupt.value)
# Once you login using the printed link, you can resume the agent

View file

@ -1,107 +0,0 @@
import os
# Import necessary classes and modules
from langchain_arcade import ToolManager
from langchain_openai import ChatOpenAI
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import END, START, MessagesState, StateGraph
from langgraph.prebuilt import ToolNode
arcade_api_key = os.environ["ARCADE_API_KEY"]
# Initialize the tool manager and fetch tools
manager = ToolManager(api_key=arcade_api_key)
manager.init_tools(toolkits=["Github"])
# convert to langchain tools and use interrupts for auth
tools = manager.to_langchain(use_interrupts=True)
# Initialize the prebuilt tool node
tool_node = ToolNode(tools)
# Create a language model instance and bind it with the tools
model = ChatOpenAI(model="gpt-4o")
model_with_tools = model.bind_tools(tools)
#### Workflow ####
# Function to invoke the model and get a response
def call_agent(state: MessagesState):
messages = state["messages"]
response = model_with_tools.invoke(messages)
# Return the updated message history
return {"messages": [response]}
# Function to determine the next step in the workflow based on the last message
def should_continue(state: MessagesState):
if state["messages"][-1].tool_calls:
for tool_call in state["messages"][-1].tool_calls:
if manager.requires_auth(tool_call["name"]):
return "authorization"
return "tools" # Proceed to tool execution if no authorization is needed
return END # End the workflow if no tool calls are present
# Function to handle authorization for tools that require it
def authorize(state: MessagesState, config: dict):
user_id = config["configurable"].get("user_id")
for tool_call in state["messages"][-1].tool_calls:
tool_name = tool_call["name"]
if not manager.requires_auth(tool_name):
continue
auth_response = manager.authorize(tool_name, user_id)
if auth_response.status != "completed":
# Prompt the user to visit the authorization URL
print(f"Visit the following URL to authorize: {auth_response.url}")
# wait for the user to complete the authorization
# and then check the authorization status again
manager.wait_for_auth(auth_response.id)
if not manager.is_authorized(auth_response.id):
# node interrupt?
raise ValueError("Authorization failed")
return {"messages": []}
if __name__ == "__main__":
# Build the workflow graph using StateGraph
workflow = StateGraph(MessagesState)
# Add nodes (steps) to the graph
workflow.add_node("agent", call_agent)
workflow.add_node("tools", tool_node)
workflow.add_node("authorization", authorize)
# Define the edges and control flow between nodes
workflow.add_edge(START, "agent")
workflow.add_conditional_edges("agent", should_continue, ["authorization", "tools", END])
workflow.add_edge("authorization", "tools")
workflow.add_edge("tools", "agent")
# Set up memory for checkpointing the state
memory = MemorySaver()
# Compile the graph with the checkpointer
graph = workflow.compile(checkpointer=memory)
# Define the input messages from the user
inputs = {
"messages": [
{
"role": "user",
"content": "Star arcadeai/arcade-mcp on github",
}
],
}
# Configuration with thread and user IDs for authorization purposes
config = {"configurable": {"thread_id": "4", "user_id": "user@example.comm"}}
# Run the graph and stream the outputs
for chunk in graph.stream(inputs, config=config, stream_mode="values"):
# Pretty-print the last message in the chunk
chunk["messages"][-1].pretty_print()

View file

@ -1,3 +0,0 @@
langchain-google-community[gmail]>=0.1.1
langchain-openai>=0.1.1
langchain-arcade>=1.2.0

View file

@ -1,20 +0,0 @@
## Setup
### Environment
Copy the `env.example` file to `.env` and supply your API keys for **at least** `OPENAI_API_KEY` and `ARCADE_API_KEY`.
- Arcade API key: `ARCADE_API_KEY` (instructions [here](https://docs.arcade.dev/home/api-keys))
- OpenAI API key: `OPENAI_API_KEY` (instructions [here](https://platform.openai.com/docs/quickstart))
## Usage with LangGraph API
### Local testing with LangGraph Studio
[Download LangGraph Studio](https://github.com/langchain-ai/langgraph-studio?tab=readme-ov-file#download) and open this directory in the Studio application.
The `langgraph.json` file in this directory specifies the graph that will be loaded in Studio.
### Deploying to LangGraph Cloud
Follow [these instructions](https://langchain-ai.github.io/langgraph/cloud/quick_start/#deploy-to-cloud) to deploy your graph to LangGraph Cloud.

View file

@ -1,8 +0,0 @@
from dataclasses import dataclass
@dataclass(kw_only=True)
class AgentConfigurable:
"""The configurable fields for the chatbot."""
user_id: str = "default-user"

View file

@ -1,9 +0,0 @@
# To separate your traces from other application
LANGSMITH_PROJECT=arcade-graph
# LANGCHAIN_API_KEY=...
# Arcade API key
# ARCADE_API_KEY=...
# LLM choice:
# OPENAI_API_KEY=...

View file

@ -1,98 +0,0 @@
import os
from datetime import datetime
from configuration import AgentConfigurable
from langchain_arcade import ToolManager
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langgraph.errors import NodeInterrupt
from langgraph.graph import END, START, MessagesState, StateGraph
from langgraph.prebuilt import ToolNode
# Initialize the Arcade Tool Manager with your API key
arcade_api_key = os.getenv("ARCADE_API_KEY")
openai_api_key = os.getenv("OPENAI_API_KEY")
manager = ToolManager(api_key=arcade_api_key)
manager.init_tools(toolkits=["Github"])
tools = manager.to_langchain(use_interrupts=True)
tool_node = ToolNode(tools)
PROMPT_TEMPLATE = f"""
You are a helpful assistant who can use tools to help users with tasks
Today's date is {datetime.now().strftime("%Y-%m-%d")}
ALL RESPONSES should be in plain text and not markdown.
"""
# prompt for the main agent
prompt = ChatPromptTemplate.from_messages([
("system", PROMPT_TEMPLATE),
("placeholder", "{messages}"),
])
# Initialize the language model with your OpenAI API key
model = ChatOpenAI(model="gpt-4o", api_key=openai_api_key).bind_tools(tools)
prompted_model = prompt | model
class AgentState(MessagesState):
auth_url: str | None = None
def call_agent(state):
"""Define the agent function that invokes the model"""
messages = state["messages"]
# replace placeholder with messages from state
response = prompted_model.invoke({"messages": messages})
return {"messages": [response]}
def should_continue(state: AgentState, config: dict):
"""Function to determine the next step based on the model's response"""
last_message = state["messages"][-1]
if last_message.tool_calls:
return "check_auth"
# If no tool calls are present, end the workflow
return END
def check_auth(state: AgentState, config: dict):
user_id = config["configurable"].get("user_id")
tool_name = state["messages"][-1].tool_calls[0]["name"]
auth_response = manager.authorize(tool_name, user_id)
if auth_response.status != "completed":
return {"auth_url": auth_response.url}
else:
return {"auth_url": None}
def authorize(state: AgentState, config: dict):
"""Function to handle tool authorization"""
user_id = config["configurable"].get("user_id")
tool_name = state["messages"][-1].tool_calls[0]["name"]
auth_response = manager.authorize(tool_name, user_id)
if auth_response.status != "completed":
auth_message = (
f"Please authorize the application in your browser:\n\n {state.get('auth_url')}"
)
raise NodeInterrupt(auth_message)
# Build the workflow graph
workflow = StateGraph(AgentState, AgentConfigurable)
# Add nodes to the graph
workflow.add_node("agent", call_agent)
workflow.add_node("tools", tool_node)
workflow.add_node("authorization", authorize)
workflow.add_node("check_auth", check_auth)
# Define the edges and control flow
workflow.add_edge(START, "agent")
workflow.add_conditional_edges("agent", should_continue, ["check_auth", END])
workflow.add_edge("check_auth", "authorization")
workflow.add_edge("authorization", "tools")
workflow.add_edge("tools", "agent")
# Compile the graph with an interrupt after the authorization node
# so that we can prompt the user to authorize the application
graph = workflow.compile()

View file

@ -1,11 +0,0 @@
{
"dockerfile_lines": [],
"graphs": {
"graph": "./graph.py:graph"
},
"env": ".env",
"python_version": "3.11",
"dependencies": [
"."
]
}

View file

@ -1,4 +0,0 @@
langchain>=0.3.0
langchain-openai>=0.1.1
langchain_arcade>=1.0.0
langgraph>=0.2.67

View file

@ -1,69 +0,0 @@
# Dependencies
node_modules/
.pnpm-store/
# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
.env.example
# Build outputs
dist/
build/
out/
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Editor directories and files
.idea/
.vscode/
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.sublime-workspace
*.sublime-project
.project
.classpath
.settings/
# OS specific
.DS_Store
Thumbs.db
ehthumbs.db
Desktop.ini
# pnpm specific
.pnpm-debug.log
pnpm-lock.yaml
# Testing
coverage/
# Cache
.cache/
.turbo/
# LangGraph API
.langgraph_api/
# Temporary files
*.tmp
*.temp
*.bak
*.swp
*~
# Local development
.local/

View file

@ -1,104 +0,0 @@
# Arcade LangGraph.js Agent
This project is based on the [LangChain React Agent JS](https://github.com/langchain-ai/react-agent-js/tree/main) repository.
![LangGraph Studio](studio.png)
This template showcases a LangGraph.js agent integrated with Arcade tools, designed for LangGraph Studio. The agent uses the ReAct pattern to execute API calls and access various tools through the Arcade API.
## What it does
The Arcade LangGraph agent:
1. Takes a user **query** as input
2. Reasons about the query and decides on an action using Arcade tools
3. Executes the chosen action through the Arcade API
4. Observes the result of the action
5. Repeats steps 2-4 until it can provide a final answer
This approach creates a flexible agent that can interact with multiple services like Google, GitHub, and other external tools through Arcade's unified API.
## Getting Started
1. Clone this repository
2. Create a `.env` file:
```bash
cp .env.example .env
```
3. Add your API keys to the `.env` file:
```
OPENAI_API_KEY=your-openai-api-key
ARCADE_API_KEY=your-arcade-api-key
```
4. Install dependencies:
```bash
pnpm install
```
5. Run the development server:
```bash
pnpm dev
```
## How it works
The core logic is defined in `src/graph.ts`
1. Loads Arcade tools for multiple toolkits (e.g., Gmail)
2. Creates a model with the tools bound to it
3. Routes messages between tool calls and model reasoning
4. Compiles everything into a graph you can invoke and deploy
## Project Structure
- `src/graph.ts` - Main graph implementation showing how to create and use a LangGraph agent with Arcade tools
- `src/configuration.ts` - Configurable parameters for the agent
- `src/prompts.ts` - Default prompts used by the agent
## Customization
To use different Arcade toolkits or queries:
1. Modify the `toolkit` string in `arcade.tools.list` in `src/graph.ts` to include the desired toolkit (e.g., `["gmail", "github", "notion"]`)
2. Change the default model in `src/configuration.ts`
3. Update the system prompt in `src/prompts.ts`
Currently supported Arcade toolkits:
- GitHub
- Gmail
- Notion
- Reddit
- X
- And many more
You can check out our [Integrations](https://docs.arcade.dev/toolkits) documentation for more information on how to integrate with other tools.
You can also create your own custom tools and integrate them with LangGraph. Check out our [Custom Tools](https://docs.arcade.dev/home/custom-tools) documentation for more information.
## Development
While iterating on your graph, you can edit past state and rerun your app from past states to debug specific nodes. Local changes will be automatically applied via hot reload.
You can create an entirely new thread, clearing previous history, using the `+` button in the top right of the LangGraph Studio interface.
## Authorization Flow
The integration handles authorization requirements for Arcade tools:
- When a tool requires authentication, an authorization URL is generated
- This URL can be presented to the user to complete the authorization process
## Prerequisites
- Node.js (v18+)
- pnpm
- Arcade account with [API key](https://docs.arcade.dev/home/api-keys)
- OpenAI API key or other compatible LLM

View file

@ -1,9 +0,0 @@
{
"node_version": "20",
"dockerfile_lines": [],
"dependencies": ["."],
"graphs": {
"agent": "./src/graph.ts:graph"
},
"env": ".env"
}

View file

@ -1,27 +0,0 @@
{
"name": "arcade-langgraph",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"dev": "npx @langchain/langgraph-cli dev"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.10.0",
"dependencies": {
"@arcadeai/arcadejs": "latest",
"@langchain/community": "^0.3.42",
"@langchain/core": "^0.3.55",
"@langchain/langgraph": "^0.2.71",
"langchain": "^0.3.24",
"@langchain/openai": "^0.5.10",
"dotenv": "^16.5.0",
"zod": "^3.24.4"
},
"devDependencies": {
"@types/node": "^22.15.17"
}
}

View file

@ -1,32 +0,0 @@
import type { RunnableConfig } from "@langchain/core/runnables";
/**
* Define the configurable parameters for the agent.
*/
import { Annotation } from "@langchain/langgraph";
import { SYSTEM_PROMPT_TEMPLATE } from "./prompts.js";
export const ConfigurationSchema = Annotation.Root({
/**
* The system prompt to be used by the agent.
*/
systemPromptTemplate: Annotation<string>,
/**
* The name of the language model to be used by the agent.
*/
model: Annotation<string>,
});
export function ensureConfiguration(
config: RunnableConfig,
): typeof ConfigurationSchema.State {
/**
* Ensure the defaults are populated.
*/
const configurable = config.configurable ?? {};
return {
systemPromptTemplate:
configurable.systemPromptTemplate ?? SYSTEM_PROMPT_TEMPLATE,
model: configurable.model ?? "gpt-4o-mini",
};
}

View file

@ -1,121 +0,0 @@
import { Arcade } from "@arcadeai/arcadejs";
import { executeOrAuthorizeZodTool, toZod } from "@arcadeai/arcadejs/lib";
import type { AIMessage } from "@langchain/core/messages";
import type { RunnableConfig } from "@langchain/core/runnables";
import { type Tool, tool } from "@langchain/core/tools";
import { MessagesAnnotation, StateGraph } from "@langchain/langgraph";
import { ToolNode } from "@langchain/langgraph/prebuilt";
import { ChatOpenAI } from "@langchain/openai";
import { ConfigurationSchema, ensureConfiguration } from "./configuration.ts";
// Initialize Arcade
const arcade = new Arcade();
// Replace this with your application's user ID (e.g. email address, UUID, etc.)
const USER_ID = "user@example.com";
// Get the Arcade tools, you can customize the toolkit (e.g. "github", "notion", "gmail", etc.)
const gmailToolkit = await arcade.tools.list({ toolkit: "gmail", limit: 30 });
/**
* LangGraph requires tools to be defined using Zod, a TypeScript-first schema validation library
* that has become the standard for runtime type checking. Zod is particularly valuable because it:
* - Provides runtime type safety and validation
* - Offers excellent TypeScript integration with automatic type inference
* - Has a simple, declarative API for defining schemas
* - Is widely adopted in the TypeScript ecosystem
*
* Arcade provides `toZod` to convert our tools into Zod format, making them compatible
* with LangGraph.
*
* The `executeOrAuthorizeZodTool` helper function simplifies authorization.
* It checks if the tool requires authorization: if so, it returns an authorization URL,
* otherwise, it runs the tool directly without extra boilerplate.
*
* Learn more: https://docs.arcade.dev/home/use-tools/get-tool-definitions#get-zod-tool-definitions
*/
const arcadeTools = toZod({
tools: gmailToolkit.items,
client: arcade,
userId: USER_ID,
executeFactory: executeOrAuthorizeZodTool, // Checks if tool is authorized and executes it, or returns authorization URL if needed
});
// Convert Arcade tools to LangChain tools
const tools = arcadeTools.map(({ name, description, execute, parameters }) =>
tool(execute, {
name,
description,
schema: parameters,
}),
);
// Define the function that calls the model
async function callModel(
state: typeof MessagesAnnotation.State,
config: RunnableConfig,
): Promise<typeof MessagesAnnotation.Update> {
/** Call the LLM powering our agent. **/
const configuration = ensureConfiguration(config);
/**
* Initialize the model and bind the tools
*/
const model = new ChatOpenAI({
model: configuration.model,
apiKey: process.env.OPENAI_API_KEY,
}).bindTools(tools);
const response = await model.invoke([
{
role: "system",
content: configuration.systemPromptTemplate.replace(
"{system_time}",
new Date().toISOString(),
),
},
...state.messages,
]);
// We return a list, because this will get added to the existing list
return { messages: [response] };
}
// Define the function that determines whether to continue or not
function routeModelOutput(state: typeof MessagesAnnotation.State): string {
const messages = state.messages;
const lastMessage = messages[messages.length - 1];
// If the LLM is invoking tools, route there.
if ((lastMessage as AIMessage)?.tool_calls?.length) {
return "tools";
}
// Otherwise end the graph.
return "__end__";
}
// Define a new graph. We use the prebuilt MessagesAnnotation to define state:
// https://langchain-ai.github.io/langgraphjs/concepts/low_level/#messagesannotation
const workflow = new StateGraph(MessagesAnnotation, ConfigurationSchema)
// Define the two nodes we will cycle between
.addNode("callModel", callModel)
.addNode("tools", new ToolNode(tools))
// Set the entrypoint as `callModel`
// This means that this node is the first one called
.addEdge("__start__", "callModel")
.addConditionalEdges(
// First, we define the edges' source node. We use `callModel`.
// This means these are the edges taken after the `callModel` node is called.
"callModel",
// Next, we pass in the function that will determine the sink node(s), which
// will be called after the source node is called.
routeModelOutput,
)
// This means that after `tools` is called, `callModel` node is called next.
.addEdge("tools", "callModel");
// Finally, we compile it!
// This compiles it into a graph you can invoke and deploy.
export const graph = workflow.compile({
interruptBefore: [], // if you want to update the state before calling the tools
interruptAfter: [],
});

View file

@ -1,7 +0,0 @@
/**
* Default prompts used by the agent.
*/
export const SYSTEM_PROMPT_TEMPLATE = `You are a helpful AI assistant.
System time: {system_time}`;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 651 KiB

View file

@ -1,2 +0,0 @@
OPENAI_API_KEY=sk-proj-1234567890
ARCADE_API_KEY=arc_1234567890

View file

@ -1,8 +0,0 @@
output.txt
node_modules
dist
.mastra
.env.development
.env
*.db
*.db-*

View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2025, Arcade AI
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,97 +0,0 @@
<h3 align="center">
<a name="readme-top"></a>
<img
src="https://docs.arcade.dev/images/logo/arcade-logo.png"
>
</h3>
<div align="center">
<h3>Arcade - Mastra Example</h3>
<a href="https://github.com/your-organization/agents-arcade/blob/main/LICENSE">
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License">
</a>
<p align="center">
<a href="https://docs.arcade.dev" target="_blank">Arcade Documentation</a>
<a href="https://docs.arcade.dev/toolkits" target="_blank">Integrations</a>
<a href="https://github.com/ArcadeAI/arcade-js" target="_blank">Arcade JS Client</a>
<a href="https://github.com/mastra-ai/mastra" target="_blank">Mastra</a>
</p>
</div>
# Arcade - Mastra Integration
This example demonstrates how to integrate [Arcade](https://docs.arcade.dev) with [Mastra](https://mastra.ai/en/docs) to create powerful AI agents. Arcade provides access to a wide range of tools including Gmail, Slack, LinkedIn, and more, while Mastra provides a robust framework for building AI agents with TypeScript.
For a list of all available tools and authentication options, see the [Arcade Integrations](https://docs.arcade.dev/toolkits) documentation. You can also build custom tools with the [Tool SDK](https://github.com/ArcadeAI/arcade-mcp) as described in our [documentation](https://docs.arcade.dev/home/build-tools/create-a-toolkit).
## Prerequisites
- [Node.js](https://nodejs.org/en/download/) (v20.0 or higher)
- [pnpm](https://pnpm.io/installation) (v9.15.9 or higher)
- [OpenAI API key](https://platform.openai.com/account/api-keys)
- [Arcade API key](https://docs.arcade.dev/home/api-keys)
## Installation
1. Install dependencies:
```bash
pnpm install
```
2. Set up environment variables:
- Copy `.env.example` to `.env`
- Fill in your API keys:
```
OPENAI_API_KEY=your_openai_api_key
ARCADE_API_KEY=your_arcade_api_key
```
## Basic Usage
This example demonstrates how to use Arcade's Gmail toolkit with Mastra to create an AI agent that can help users manage their email. The agent will access your Google account (after authorization) and perform various tasks based on user requests.
To get started:
1. Start the Mastra playground:
```bash
pnpm dev
```
2. Open your browser and navigate to <http://localhost:4111/>
The Mastra playground provides an interactive interface where you can:
- Chat with your agent
- Execute specific tools
## Authorization
When using tools that require authorization, the agent will provide an authorization URL. You'll need to:
1. Click on the authorization URL provided in the chat/execution interface
2. Complete the authorization flow in your browser
3. Return to the Mastra playground to continue your interaction
This authorization process is handled by the `executeOrAuthorizeZodTool` helper function, which checks if a tool requires authorization and returns the appropriate URL when needed. Once authorized, the tool will execute normally in subsequent requests without requiring re-authorization.
## Development
To modify or extend the functionality:
1. Update the `userId` in `agents/gmail.ts` with your application's user identification
2. Modify the `toolkit` parameter in `arcade.tools.list()` to access different tools. Available toolkits include:
- `"gmail"` - Gmail
- `"slack"` - Slack messaging and channels
- `"github"` - GitHub repositories and issues
- And more in [Arcade Integrations](https://docs.arcade.dev/toolkits) documentation
## Security
- Never commit your `.env` file
- Use appropriate user identification in production
## License
This project is licensed under the MIT License - see the LICENSE file for details.

View file

@ -1,33 +0,0 @@
{
"name": "mastra",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "mastra dev",
"build": "mastra build"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "module",
"dependencies": {
"@ai-sdk/openai": "^1.3.24",
"@arcadeai/arcadejs": "^1.14.0",
"@mastra/core": "0.16.3",
"@mastra/libsql": "0.14.1",
"@mastra/memory": "0.15.1",
"zod": "^3.25.76"
},
"devDependencies": {
"@types/node": "^24.10.1",
"mastra": "0.12.3",
"typescript": "^5.9.3"
},
"pnpm": {
"overrides": {
"form-data@>=4.0.0 <4.0.5": ">=4.0.5"
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,29 +0,0 @@
import { openai } from "@ai-sdk/openai"
import { Agent } from "@mastra/core/agent"
import { Memory } from "@mastra/memory"
import { LibSQLStore } from "@mastra/libsql"
import { gmailTools } from "../tools/gmailTools"
// Initialize memory
const memory = new Memory({
storage: new LibSQLStore({
url: "file:../../memory.db",
}),
})
// Create an agent with Gmail tools
export const gmailAgent = new Agent({
name: "gmailAgent",
instructions: `You are a Gmail assistant that helps users manage their inbox.
When helping users:
- Always verify their intent before performing actions
- Keep responses clear and concise
- Confirm important actions before executing them
- Respect user privacy and data security
Use the gmailTools to interact with various Gmail services and perform related tasks.`,
model: openai("gpt-4o-mini"),
memory,
tools: gmailTools,
})

View file

@ -1,33 +0,0 @@
import { openai } from "@ai-sdk/openai"
import { Agent } from "@mastra/core/agent"
import { Memory } from "@mastra/memory"
import { LibSQLStore } from "@mastra/libsql"
import { gmailTools } from "../tools/gmailTools"
import { flightTools } from "../tools/flightSearchTools"
import { hotelTools } from "../tools/hotelSearchTools"
// Initialize memory
const memory = new Memory({
storage: new LibSQLStore({
url: "file:../../memory.db",
}),
})
// Create an agent with Gmail, FlightSearch, and HotelSearch tools
export const inboxTravelSearchAgent = new Agent({
name: "inboxTravelSearchAgent",
instructions: `You are an assistant that helps users manage their Gmail inbox and can help with travel related tasks and planning.
When helping users:
- Always verify their intent before performing actions
- Keep responses clear and concise
- Confirm important actions before executing them
- Respect user privacy and data security
Use the gmailTools to interact with various Gmail services and perform related tasks.
Use the flightTools to interact with various flight services and perform related tasks.
Use the hotelTools to interact with various hotel services and perform related tasks.`,
model: openai("gpt-4o-mini"),
memory,
tools: { ...gmailTools, ...flightTools, ...hotelTools },
})

View file

@ -1,11 +0,0 @@
import { Mastra } from "@mastra/core"
import { LibSQLStore } from "@mastra/libsql"
import { inboxTravelSearchAgent } from "./agents/inboxTravelSearch"
import { gmailAgent } from "./agents/gmail"
export const mastra = new Mastra({
agents: { gmailAgent, inboxTravelSearchAgent },
storage: new LibSQLStore({
url: "file:../mastra.db",
}),
})

View file

@ -1,44 +0,0 @@
import { Arcade } from "@arcadeai/arcadejs"
import { executeOrAuthorizeZodTool, toZodToolSet } from "@arcadeai/arcadejs/lib"
// Initialize Arcade
const arcade = new Arcade()
// Get Arcade GoogleFlights Toolkit
// Toolkit names can be found in the Arcade dashboard via Tools > view > Toolkit or via the CLI `arcade workers list`
const flightToolkit = await arcade.tools.list({ toolkit: "GoogleFlights", limit: 30 })
/**
* Mastra requires tools to be defined using Zod, a TypeScript-first schema validation library
* that has become the standard for runtime type checking. Zod is particularly valuable because it:
* - Provides runtime type safety and validation
* - Offers excellent TypeScript integration with automatic type inference
* - Has a simple, declarative API for defining schemas
* - Is widely adopted in the TypeScript ecosystem
*
* Arcade provides `toZodToolSet` to convert our tools into Zod format, making them compatible
* with Mastra.
*
* The `executeOrAuthorizeZodTool` helper function simplifies authorization.
* It checks if the tool requires authorization: if so, it returns an authorization URL,
* otherwise, it runs the tool directly without extra boilerplate.
*
* Learn more: https://docs.arcade.dev/home/use-tools/get-tool-definitions#get-zod-tool-definitions
*/
const baseFlightTools = toZodToolSet({
tools: flightToolkit.items,
client: arcade,
userId: "<YOUR_USER_ID>", // Your app's internal ID for the user (an email, UUID, etc). It's used internally to identify your user in Arcade
executeFactory: executeOrAuthorizeZodTool, // Checks if tool is authorized and executes it, or returns authorization URL if needed
})
// Add id property to each tool that copies the name
export const flightTools = Object.fromEntries(
Object.entries(baseFlightTools).map(([key, tool]) => [
key,
{
...tool,
id: tool.name,
},
])
)

View file

@ -1,44 +0,0 @@
import { Arcade } from "@arcadeai/arcadejs"
import { executeOrAuthorizeZodTool, toZodToolSet } from "@arcadeai/arcadejs/lib"
// Initialize Arcade
const arcade = new Arcade()
// Get Arcade Gmail Toolkit
// Toolkit names can be found in the Arcade dashboard via Tools > view > Toolkit or via the CLI `arcade workers list`
const gmailToolkit = await arcade.tools.list({ toolkit: "Gmail", limit: 30 })
/**
* Mastra requires tools to be defined using Zod, a TypeScript-first schema validation library
* that has become the standard for runtime type checking. Zod is particularly valuable because it:
* - Provides runtime type safety and validation
* - Offers excellent TypeScript integration with automatic type inference
* - Has a simple, declarative API for defining schemas
* - Is widely adopted in the TypeScript ecosystem
*
* Arcade provides `toZodToolSet` to convert our tools into Zod format, making them compatible
* with Mastra.
*
* The `executeOrAuthorizeZodTool` helper function simplifies authorization.
* It checks if the tool requires authorization: if so, it returns an authorization URL,
* otherwise, it runs the tool directly without extra boilerplate.
*
* Learn more: https://docs.arcade.dev/home/use-tools/get-tool-definitions#get-zod-tool-definitions
*/
const baseGmailTools = toZodToolSet({
tools: gmailToolkit.items,
client: arcade,
userId: "<YOUR_USER_ID>", // Your app's internal ID for the user (an email, UUID, etc). It's used internally to identify your user in Arcade
executeFactory: executeOrAuthorizeZodTool, // Checks if tool is authorized and executes it, or returns authorization URL if needed
})
// Add id property to each tool that copies the name
export const gmailTools = Object.fromEntries(
Object.entries(baseGmailTools).map(([key, tool]) => [
key,
{
...tool,
id: tool.name,
},
])
)

View file

@ -1,44 +0,0 @@
import { Arcade } from "@arcadeai/arcadejs"
import { executeOrAuthorizeZodTool, toZodToolSet } from "@arcadeai/arcadejs/lib"
// Initialize Arcade
const arcade = new Arcade()
// Get Arcade GoogleHotels Toolkit
// Toolkit names can be found in the Arcade dashboard via Tools > view > Toolkit or via the CLI `arcade workers list`
const hotelToolkit = await arcade.tools.list({ toolkit: "GoogleHotels", limit: 30 })
/**
* Mastra requires tools to be defined using Zod, a TypeScript-first schema validation library
* that has become the standard for runtime type checking. Zod is particularly valuable because it:
* - Provides runtime type safety and validation
* - Offers excellent TypeScript integration with automatic type inference
* - Has a simple, declarative API for defining schemas
* - Is widely adopted in the TypeScript ecosystem
*
* Arcade provides `toZodToolSet` to convert our tools into Zod format, making them compatible
* with Mastra.
*
* The `executeOrAuthorizeZodTool` helper function simplifies authorization.
* It checks if the tool requires authorization: if so, it returns an authorization URL,
* otherwise, it runs the tool directly without extra boilerplate.
*
* Learn more: https://docs.arcade.dev/home/use-tools/get-tool-definitions#get-zod-tool-definitions
*/
const baseHotelTools = toZodToolSet({
tools: hotelToolkit.items,
client: arcade,
userId: "<YOUR_USER_ID>", // Your app's internal ID for the user (an email, UUID, etc). It's used internally to identify your user in Arcade
executeFactory: executeOrAuthorizeZodTool, // Checks if tool is authorized and executes it, or returns authorization URL if needed
})
// Add id property to each tool that copies the name
export const hotelTools = Object.fromEntries(
Object.entries(baseHotelTools).map(([key, tool]) => [
key,
{
...tool,
id: tool.name,
},
])
)

View file

@ -1,16 +0,0 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "bundler",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"noEmit": true,
"outDir": "dist"
},
"include": [
"src/**/*"
]
}

View file

@ -1,2 +0,0 @@
OPENAI_API_KEY=sk-proj-1234567890
ARCADE_API_KEY=arc_1234567890

View file

@ -1,42 +0,0 @@
# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Environment variables
.env
.env.local
.env.*.local
# Build output
dist/
build/
out/
# IDE and editor files
.idea/
.vscode/
*.swp
*.swo
.DS_Store
# Logs
logs/
*.log
# Testing
coverage/
# Temporary files
tmp/
temp/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history

View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2025, Arcade AI
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,158 +0,0 @@
<h3 align="center">
<a name="readme-top"></a>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/ArcadeAI/.github/refs/heads/main/profile/assets/new_arcade_logo_white.svg" width="300">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/ArcadeAI/.github/refs/heads/main/profile/assets/new_arcade_logo_black.svg" width="300">
<img alt="Arcade AI Logo" src="https://raw.githubusercontent.com/ArcadeAI/.github/refs/heads/main/profile/assets/new_arcade_logo_black.svg" width="300" >
</picture>
</h3>
<div align="center">
<h3>OpenAI Agents + Arcade AI Example</h3>
<a href="https://github.com/your-organization/openai-ts/blob/main/LICENSE">
<img src="https://img.shields.io/badge/License-ISC-blue.svg" alt="License">
</a>
<p align="center">
<a href="https://docs.arcade.dev" target="_blank">Arcade Documentation</a>
<a href="https://docs.arcade.dev/toolkits" target="_blank">Integrations</a>
<a href="https://github.com/ArcadeAI/arcade-js" target="_blank">Arcade JS Client</a>
<a href="https://platform.openai.com/docs/assistants/tools" target="_blank">OpenAI Agents</a>
</p>
</div>
# OpenAI Agents + Arcade AI
This TypeScript project demonstrates how to integrate [Arcade AI](https://docs.arcade.dev) with [OpenAI Agents](https://openai.github.io/openai-agents-js/) to create powerful AI agents that can interact with external services. Arcade provides access to a wide range of tools including Gmail, Slack, LinkedIn, and more through its Gmail toolkit and other integrations.
The project showcases two approaches:
- **Basic integration** (`src/index.ts`): Simple one-time execution with Gmail toolkit
- **Authorization handling** (`src/waitForCompletion.ts`): Manual authorization flow management
For a list of all hosted tools and auth providers, see the [Arcade Integrations](https://docs.arcade.dev/toolkits) documentation.
## Prerequisites
- [Node.js](https://nodejs.org/en/download/) (v18.20.8 or higher)
- [npm](https://www.npmjs.com/) or [pnpm](https://pnpm.io/installation)
- [OpenAI API key](https://platform.openai.com/account/api-keys)
- [Arcade API key](https://docs.arcade.dev/home/api-keys)
## Installation
1. Clone the repository and install dependencies:
```bash
npm install
```
2. Set up environment variables:
- Create a `.env` file in the root directory
- Add your API keys:
```
OPENAI_API_KEY=your_openai_api_key
ARCADE_API_KEY=your_arcade_api_key
```
3. Update the user ID placeholder:
- In both `src/index.ts` and `src/waitForCompletion.ts`
- Replace `<YOUR_SYSTEM_USER_ID>` with your application's user identifier (e.g., email address, UUID, etc.)
## Basic Usage
This example creates an AI agent that can read and process your Gmail emails using Arcade's Gmail toolkit.
### Simple Execution
Run the basic example:
```bash
npm run dev
```
This script will:
1. Initialize the Arcade client
2. Fetch available Gmail toolkit tools (up to 30)
3. Convert them to OpenAI Agents compatible format
4. Create an agent that can assist with Gmail API calls
5. Ask "What are my latest emails?" and display the result
### Authorization Flow Example
For a more robust implementation that handles authorization flows:
```bash
npm run dev:waitForCompletion
```
This version includes:
- Automatic authorization detection and handling
- Browser-based OAuth flow initiation
- Waiting for authorization completion
- Automatic retry after successful authorization
If you haven't authorized Arcade with Google yet, you'll see a message like:
```bash
Please complete the authorization challenge in your browser: https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&client_id=...
```
Visit the provided URL to authorize Arcade with your Google account. The script will automatically detect when authorization is complete and continue execution.
## Project Structure
```
openai-ts/
├── src/
│ ├── index.ts # Basic OpenAI Agents + Arcade integration
│ └── waitForCompletion.ts # Manual authorization flow management
├── package.json # Dependencies and scripts
└── README.md # This file
```
## Available Toolkits
You can modify the `toolkit` parameter to access different integrations:
- `"gmail"` - Gmail
- `"slack"` - Slack messaging and channels
- `"github"` - GitHub repositories and issues
- `"linkedin"` - LinkedIn posts and connections
- And more in [Arcade Integrations](https://docs.arcade.dev/toolkits) documentation
## Development
To extend or modify the functionality:
1. **Change the toolkit**: Update the `toolkit` parameter in the `client.tools.list()` call
2. **Modify the query**: Change the question asked to the agent in the `run()` function
3. **Add custom instructions**: Update the agent's `instructions` parameter for different behaviors
4. **Handle different models**: Switch the `model` parameter to use different OpenAI models
Example customization:
```typescript
const slackToolkit = await client.tools.list({ toolkit: "slack", limit: 30 });
// ... rest of setup
const slackAgent = new Agent({
name: "Slack agent",
instructions: "You are a helpful assistant for managing Slack communications.",
model: "gpt-4o",
tools
});
const result = await run(slackAgent, "Send a message to the #general channel");
```
## Security Best Practices
- Never commit your `.env` file to version control
- Keep your API keys secure and rotate them regularly
- Use appropriate user identification in production
## License
This project is licensed under the MIT License - see the LICENSE file for details.

File diff suppressed because it is too large Load diff

View file

@ -1,17 +0,0 @@
{
"name": "openai-ts",
"version": "1.0.0",
"description": "",
"license": "ISC",
"author": "",
"type": "module",
"main": "index.js",
"scripts": {
"dev": "npx tsx --env-file=.env src/index.ts",
"dev:waitForCompletion": "npx tsx --env-file=.env src/waitForCompletion.ts"
},
"dependencies": {
"@arcadeai/arcadejs": "1.14.0",
"@openai/agents": "^0.3.3"
}
}

View file

@ -1,29 +0,0 @@
import Arcade from '@arcadeai/arcadejs';
import { executeOrAuthorizeZodTool, toZod } from "@arcadeai/arcadejs/lib";
import { Agent, run, tool } from '@openai/agents';
// 1) Initialize Arcade client
const client = new Arcade();
// 2) Fetch Gmail toolkit from Arcade and prepare tools for OpenAI Agents
const gmailToolkit = await client.tools.list({ toolkit: "gmail", limit: 30 });
const tools = toZod({
tools: gmailToolkit.items,
client,
userId: "<YOUR_SYSTEM_USER_ID>", // Replace this with your application's user ID (e.g. email address, UUID, etc.)
executeFactory: executeOrAuthorizeZodTool,
}).map(tool);
// 3) Create a new agent with the Gmail toolkit
const gmailAgent = new Agent({
name: "Gmail agent",
instructions: "You are a helpful assistant that can assist with Gmail API calls.",
model: "gpt-4o-mini",
tools
});
// 4) Run the agent
const result = await run(gmailAgent, "What are my latest emails?");
// 5) Print the result
console.log(result.finalOutput);

View file

@ -1,49 +0,0 @@
import Arcade from '@arcadeai/arcadejs';
import { isAuthorizationRequiredError, toZod } from "@arcadeai/arcadejs/lib";
import { Agent, run, tool } from '@openai/agents';
async function main() {
// 1) Initialize Arcade client
const client = new Arcade();
// 2) Fetch Gmail toolkit from Arcade and prepare tools for OpenAI Agents
const gmailToolkit = await client.tools.list({ toolkit: "gmail", limit: 30 });
const tools = toZod({
tools: gmailToolkit.items,
client,
userId: "<YOUR_SYSTEM_USER_ID_2>", // Replace this with your application's user ID (e.g. email address, UUID, etc.)
}).map(tool);
// 3) Create a new agent with the Gmail toolkit
const gmailAgent = new Agent({
name: "Gmail agent",
instructions: "You are a helpful assistant that can assist with Gmail API calls.",
model: "gpt-4o-mini",
tools
});
// 4) Run the agent, if authorization is required, wait for it to complete and retry
while (true) {
try {
const result = await run(gmailAgent, "What are my latest emails?");
console.log(result.finalOutput);
break; // Exit the loop if the result is successful
} catch (error) {
if (error instanceof Error && isAuthorizationRequiredError(error)) {
const response = await client.tools.authorize({
tool_name: "Gmail_ListEmails",
user_id: "<YOUR_SYSTEM_USER_ID_2>",
});
if (response.status !== "completed") {
console.log(`Please complete the authorization challenge in your browser: ${response.url}`);
}
// Wait for the authorization to complete
await client.auth.waitForCompletion(response);
console.log("Authorization completed, retrying...");
}
}
}
}
main();