improvement(tools): added visibility for tools that were missing it, added new google and github tools (#2874)

* improvement(tools): added visibility for tools that were missing it, added new google tools

* fixed the name for google forms

* revert schema enrichers change

* fixed block ordering
This commit is contained in:
Waleed
2026-01-17 20:51:15 -08:00
committed by GitHub
parent 19a8daedf7
commit ee7572185a
231 changed files with 19104 additions and 1925 deletions

View File

@@ -1535,4 +1535,800 @@ Delete a GitHub Project V2. This action is permanent and cannot be undone. Requi
| `number` | number | Deleted project number |
| `url` | string | Deleted project URL |
### `github_search_code`
Search for code across GitHub repositories. Use qualifiers like repo:owner/name, language:js, path:src, extension:py
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `q` | string | Yes | Search query with optional qualifiers \(repo:, language:, path:, extension:, user:, org:\) |
| `sort` | string | No | Sort by indexed date \(default: best match\) |
| `order` | string | No | Sort order: asc or desc \(default: desc\) |
| `per_page` | number | No | Results per page \(max 100, default: 30\) |
| `page` | number | No | Page number \(default: 1\) |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `total_count` | number | Total matching results |
| `incomplete_results` | boolean | Whether results are incomplete |
| `items` | array | Array of code matches from GitHub API |
| ↳ `name` | string | File name |
| ↳ `path` | string | File path |
| ↳ `sha` | string | Blob SHA |
| ↳ `html_url` | string | GitHub web URL |
| ↳ `repository` | object | Repository object |
### `github_search_commits`
Search for commits across GitHub. Use qualifiers like repo:owner/name, author:user, committer:user, author-date:>2023-01-01
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `q` | string | Yes | Search query with optional qualifiers \(repo:, author:, committer:, author-date:, committer-date:, merge:true/false\) |
| `sort` | string | No | Sort by: author-date or committer-date \(default: best match\) |
| `order` | string | No | Sort order: asc or desc \(default: desc\) |
| `per_page` | number | No | Results per page \(max 100, default: 30\) |
| `page` | number | No | Page number \(default: 1\) |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `total_count` | number | Total matching results |
| `incomplete_results` | boolean | Whether results are incomplete |
| `items` | array | Array of commit objects from GitHub API |
| ↳ `sha` | string | Commit SHA |
| ↳ `html_url` | string | Web URL |
| ↳ `commit` | object | Commit data |
| ↳ `author` | object | GitHub user |
| ↳ `committer` | object | GitHub user |
| ↳ `repository` | object | Repository |
### `github_search_issues`
Search for issues and pull requests across GitHub. Use qualifiers like repo:owner/name, is:issue, is:pr, state:open, label:bug, author:user
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `q` | string | Yes | Search query with optional qualifiers \(repo:, is:issue, is:pr, state:, label:, author:, assignee:\) |
| `sort` | string | No | Sort by: comments, reactions, created, updated, interactions \(default: best match\) |
| `order` | string | No | Sort order: asc or desc \(default: desc\) |
| `per_page` | number | No | Results per page \(max 100, default: 30\) |
| `page` | number | No | Page number \(default: 1\) |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `total_count` | number | Total matching results |
| `incomplete_results` | boolean | Whether results are incomplete |
| `items` | array | Array of issue/PR objects from GitHub API |
| ↳ `id` | number | Issue ID |
| ↳ `number` | number | Issue number |
| ↳ `title` | string | Title |
| ↳ `state` | string | State |
| ↳ `html_url` | string | Web URL |
| ↳ `body` | string | Body text |
| ↳ `user` | object | Author |
| ↳ `labels` | array | Labels |
| ↳ `assignees` | array | Assignees |
| ↳ `created_at` | string | Creation date |
| ↳ `updated_at` | string | Update date |
| ↳ `closed_at` | string | Close date |
### `github_search_repos`
Search for repositories across GitHub. Use qualifiers like language:python, stars:>1000, topic:react, user:owner, org:name
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `q` | string | Yes | Search query with optional qualifiers \(language:, stars:, forks:, topic:, user:, org:, in:name,description,readme\) |
| `sort` | string | No | Sort by: stars, forks, help-wanted-issues, updated \(default: best match\) |
| `order` | string | No | Sort order: asc or desc \(default: desc\) |
| `per_page` | number | No | Results per page \(max 100, default: 30\) |
| `page` | number | No | Page number \(default: 1\) |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `total_count` | number | Total matching results |
| `incomplete_results` | boolean | Whether results are incomplete |
| `items` | array | Array of repository objects from GitHub API |
| ↳ `id` | number | Repository ID |
| ↳ `full_name` | string | Full name |
| ↳ `description` | string | Description |
| ↳ `html_url` | string | Web URL |
| ↳ `stargazers_count` | number | Stars |
| ↳ `forks_count` | number | Forks |
| ↳ `open_issues_count` | number | Open issues |
| ↳ `language` | string | Language |
| ↳ `topics` | array | Topics |
| ↳ `owner` | object | Owner |
### `github_search_users`
Search for users and organizations on GitHub. Use qualifiers like type:user, type:org, followers:>1000, repos:>10, location:city
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `q` | string | Yes | Search query with optional qualifiers \(type:user/org, followers:, repos:, location:, language:, created:\) |
| `sort` | string | No | Sort by: followers, repositories, joined \(default: best match\) |
| `order` | string | No | Sort order: asc or desc \(default: desc\) |
| `per_page` | number | No | Results per page \(max 100, default: 30\) |
| `page` | number | No | Page number \(default: 1\) |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `total_count` | number | Total matching results |
| `incomplete_results` | boolean | Whether results are incomplete |
| `items` | array | Array of user objects from GitHub API |
| ↳ `id` | number | User ID |
| ↳ `login` | string | Username |
| ↳ `html_url` | string | Profile URL |
| ↳ `avatar_url` | string | Avatar URL |
| ↳ `type` | string | User or Organization |
| ↳ `site_admin` | boolean | Is site admin |
### `github_list_commits`
List commits in a repository with optional filtering by SHA, path, author, committer, or date range
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner |
| `repo` | string | Yes | Repository name |
| `sha` | string | No | SHA or branch to start listing commits from |
| `path` | string | No | Only commits containing this file path |
| `author` | string | No | GitHub login or email address to filter by author |
| `committer` | string | No | GitHub login or email address to filter by committer |
| `since` | string | No | Only commits after this date \(ISO 8601 format\) |
| `until` | string | No | Only commits before this date \(ISO 8601 format\) |
| `per_page` | number | No | Results per page \(max 100, default: 30\) |
| `page` | number | No | Page number \(default: 1\) |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `items` | array | Array of commit objects from GitHub API |
| ↳ `sha` | string | Commit SHA |
| ↳ `html_url` | string | Web URL |
| ↳ `commit` | object | Commit data |
| ↳ `author` | object | GitHub user |
| ↳ `committer` | object | GitHub user |
| ↳ `parents` | array | Parent commits |
| `count` | number | Number of commits returned |
### `github_get_commit`
Get detailed information about a specific commit including files changed and stats
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner |
| `repo` | string | Yes | Repository name |
| `ref` | string | Yes | Commit SHA, branch name, or tag name |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `sha` | string | Commit SHA |
| `html_url` | string | Web URL |
| `commit` | object | Commit data |
| `author` | object | GitHub user |
| `committer` | object | GitHub user |
| `stats` | object | Change stats |
| `files` | array | Changed files |
| `parents` | array | Parent commits |
### `github_compare_commits`
Compare two commits or branches to see the diff, commits between them, and changed files
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner |
| `repo` | string | Yes | Repository name |
| `base` | string | Yes | Base branch/tag/SHA for comparison |
| `head` | string | Yes | Head branch/tag/SHA for comparison |
| `per_page` | number | No | Results per page for files \(max 100, default: 30\) |
| `page` | number | No | Page number for files \(default: 1\) |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `status` | string | Comparison status |
| `ahead_by` | number | Commits ahead |
| `behind_by` | number | Commits behind |
| `total_commits` | number | Total commits |
| `html_url` | string | Web URL |
| `diff_url` | string | Diff URL |
| `patch_url` | string | Patch URL |
| `base_commit` | object | Base commit |
| `merge_base_commit` | object | Merge base |
| `commits` | array | Commits between |
| `files` | array | Changed files |
### `github_create_gist`
Create a new gist with one or more files
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `description` | string | No | Description of the gist |
| `files` | json | Yes | JSON object with filenames as keys and content as values. Example: \{"file.txt": \{"content": "Hello"\}\} |
| `Example` | string | No | No description |
| `public` | boolean | No | Whether the gist is public \(default: false\) |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Gist ID |
| `html_url` | string | Web URL |
| `git_pull_url` | string | Git pull URL |
| `git_push_url` | string | Git push URL |
| `description` | string | Description |
| `public` | boolean | Is public |
| `created_at` | string | Creation date |
| `updated_at` | string | Update date |
| `files` | object | Files in gist |
| `owner` | object | Owner info |
### `github_get_gist`
Get a gist by ID including its file contents
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `gist_id` | string | Yes | The gist ID |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Gist ID |
| `html_url` | string | Web URL |
| `description` | string | Description |
| `public` | boolean | Is public |
| `created_at` | string | Creation date |
| `updated_at` | string | Update date |
| `files` | object | Files with content |
| `owner` | object | Owner info |
| `comments` | number | Comment count |
### `github_list_gists`
List gists for a user or the authenticated user
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `username` | string | No | GitHub username \(omit for authenticated user's gists\) |
| `since` | string | No | Only gists updated after this time \(ISO 8601\) |
| `per_page` | number | No | Results per page \(max 100, default: 30\) |
| `page` | number | No | Page number \(default: 1\) |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `items` | array | Array of gist objects from GitHub API |
| ↳ `id` | string | Gist ID |
| ↳ `html_url` | string | Web URL |
| ↳ `description` | string | Description |
| ↳ `public` | boolean | Is public |
| ↳ `files` | object | Files |
| ↳ `owner` | object | Owner |
| `count` | number | Number of gists returned |
### `github_update_gist`
Update a gist description or files. To delete a file, set its value to null in files object
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `gist_id` | string | Yes | The gist ID to update |
| `description` | string | No | New description for the gist |
| `files` | json | No | JSON object with filenames as keys. Set to null to delete, or provide content to update/add |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Gist ID |
| `html_url` | string | Web URL |
| `description` | string | Description |
| `public` | boolean | Is public |
| `updated_at` | string | Update date |
| `files` | object | Current files |
### `github_delete_gist`
Delete a gist by ID
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `gist_id` | string | Yes | The gist ID to delete |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `deleted` | boolean | Whether deletion succeeded |
| `gist_id` | string | The deleted gist ID |
### `github_fork_gist`
Fork a gist to create your own copy
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `gist_id` | string | Yes | The gist ID to fork |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | New gist ID |
| `html_url` | string | Web URL |
| `description` | string | Description |
| `public` | boolean | Is public |
| `created_at` | string | Creation date |
| `owner` | object | Owner info |
| `files` | object | Files |
### `github_star_gist`
Star a gist
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `gist_id` | string | Yes | The gist ID to star |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `starred` | boolean | Whether starring succeeded |
| `gist_id` | string | The gist ID |
### `github_unstar_gist`
Unstar a gist
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `gist_id` | string | Yes | The gist ID to unstar |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `unstarred` | boolean | Whether unstarring succeeded |
| `gist_id` | string | The gist ID |
### `github_fork_repo`
Fork a repository to your account or an organization
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner to fork from |
| `repo` | string | Yes | Repository name to fork |
| `organization` | string | No | Organization to fork into \(omit to fork to your account\) |
| `name` | string | No | Custom name for the forked repository |
| `default_branch_only` | boolean | No | Only fork the default branch \(default: false\) |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | number | Repository ID |
| `full_name` | string | Full name |
| `html_url` | string | Web URL |
| `clone_url` | string | Clone URL |
| `ssh_url` | string | SSH URL |
| `default_branch` | string | Default branch |
| `fork` | boolean | Is a fork |
| `parent` | object | Parent repository |
| `owner` | object | Owner |
### `github_list_forks`
List forks of a repository
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner |
| `repo` | string | Yes | Repository name |
| `sort` | string | No | Sort by: newest, oldest, stargazers, watchers \(default: newest\) |
| `per_page` | number | No | Results per page \(max 100, default: 30\) |
| `page` | number | No | Page number \(default: 1\) |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `items` | array | Array of fork repository objects from GitHub API |
| ↳ `id` | number | Repository ID |
| ↳ `full_name` | string | Full name |
| ↳ `html_url` | string | Web URL |
| ↳ `owner` | object | Owner |
| ↳ `stargazers_count` | number | Stars |
| ↳ `forks_count` | number | Forks |
| `count` | number | Number of forks returned |
### `github_create_milestone`
Create a milestone in a repository
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner |
| `repo` | string | Yes | Repository name |
| `title` | string | Yes | Milestone title |
| `state` | string | No | State: open or closed \(default: open\) |
| `description` | string | No | Milestone description |
| `due_on` | string | No | Due date \(ISO 8601 format, e.g., 2024-12-31T23:59:59Z\) |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `number` | number | Milestone number |
| `title` | string | Title |
| `description` | string | Description |
| `state` | string | State |
| `html_url` | string | Web URL |
| `due_on` | string | Due date |
| `open_issues` | number | Open issues |
| `closed_issues` | number | Closed issues |
| `creator` | object | Creator |
### `github_get_milestone`
Get a specific milestone by number
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner |
| `repo` | string | Yes | Repository name |
| `milestone_number` | number | Yes | Milestone number |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `number` | number | Milestone number |
| `title` | string | Title |
| `description` | string | Description |
| `state` | string | State |
| `html_url` | string | Web URL |
| `due_on` | string | Due date |
| `open_issues` | number | Open issues |
| `closed_issues` | number | Closed issues |
| `closed_at` | string | Close date |
| `creator` | object | Creator |
### `github_list_milestones`
List milestones in a repository
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner |
| `repo` | string | Yes | Repository name |
| `state` | string | No | Filter by state: open, closed, all \(default: open\) |
| `sort` | string | No | Sort by: due_on or completeness \(default: due_on\) |
| `direction` | string | No | Sort direction: asc or desc \(default: asc\) |
| `per_page` | number | No | Results per page \(max 100, default: 30\) |
| `page` | number | No | Page number \(default: 1\) |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `items` | array | Array of milestone objects from GitHub API |
| ↳ `number` | number | Milestone number |
| ↳ `title` | string | Title |
| ↳ `state` | string | State |
| ↳ `html_url` | string | Web URL |
| ↳ `open_issues` | number | Open issues |
| ↳ `closed_issues` | number | Closed issues |
| `count` | number | Number of milestones returned |
### `github_update_milestone`
Update a milestone in a repository
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner |
| `repo` | string | Yes | Repository name |
| `milestone_number` | number | Yes | Milestone number to update |
| `title` | string | No | New milestone title |
| `state` | string | No | New state: open or closed |
| `description` | string | No | New description |
| `due_on` | string | No | New due date \(ISO 8601 format\) |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `number` | number | Milestone number |
| `title` | string | Title |
| `description` | string | Description |
| `state` | string | State |
| `html_url` | string | Web URL |
| `due_on` | string | Due date |
| `open_issues` | number | Open issues |
| `closed_issues` | number | Closed issues |
### `github_delete_milestone`
Delete a milestone from a repository
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner |
| `repo` | string | Yes | Repository name |
| `milestone_number` | number | Yes | Milestone number to delete |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `deleted` | boolean | Whether deletion succeeded |
| `milestone_number` | number | The deleted milestone number |
### `github_create_issue_reaction`
Add a reaction to an issue
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner |
| `repo` | string | Yes | Repository name |
| `issue_number` | number | Yes | Issue number |
| `content` | string | Yes | Reaction type: +1 \(thumbs up\), -1 \(thumbs down\), laugh, confused, heart, hooray, rocket, eyes |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | number | Reaction ID |
| `user` | object | User who reacted |
| `content` | string | Reaction type |
| `created_at` | string | Creation date |
### `github_delete_issue_reaction`
Remove a reaction from an issue
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner |
| `repo` | string | Yes | Repository name |
| `issue_number` | number | Yes | Issue number |
| `reaction_id` | number | Yes | Reaction ID to delete |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `deleted` | boolean | Whether deletion succeeded |
| `reaction_id` | number | The deleted reaction ID |
### `github_create_comment_reaction`
Add a reaction to an issue comment
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner |
| `repo` | string | Yes | Repository name |
| `comment_id` | number | Yes | Comment ID |
| `content` | string | Yes | Reaction type: +1 \(thumbs up\), -1 \(thumbs down\), laugh, confused, heart, hooray, rocket, eyes |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | number | Reaction ID |
| `user` | object | User who reacted |
| `content` | string | Reaction type |
| `created_at` | string | Creation date |
### `github_delete_comment_reaction`
Remove a reaction from an issue comment
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner |
| `repo` | string | Yes | Repository name |
| `comment_id` | number | Yes | Comment ID |
| `reaction_id` | number | Yes | Reaction ID to delete |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `deleted` | boolean | Whether deletion succeeded |
| `reaction_id` | number | The deleted reaction ID |
### `github_star_repo`
Star a repository
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner |
| `repo` | string | Yes | Repository name |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `starred` | boolean | Whether starring succeeded |
| `owner` | string | Repository owner |
| `repo` | string | Repository name |
### `github_unstar_repo`
Remove star from a repository
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner |
| `repo` | string | Yes | Repository name |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `unstarred` | boolean | Whether unstarring succeeded |
| `owner` | string | Repository owner |
| `repo` | string | Repository name |
### `github_check_star`
Check if you have starred a repository
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner |
| `repo` | string | Yes | Repository name |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `starred` | boolean | Whether you have starred the repo |
| `owner` | string | Repository owner |
| `repo` | string | Repository name |
### `github_list_stargazers`
List users who have starred a repository
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `owner` | string | Yes | Repository owner |
| `repo` | string | Yes | Repository name |
| `per_page` | number | No | Results per page \(max 100, default: 30\) |
| `page` | number | No | Page number \(default: 1\) |
| `apiKey` | string | Yes | GitHub API token |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `items` | array | Array of user objects from GitHub API |
| ↳ `login` | string | Username |
| ↳ `id` | number | User ID |
| ↳ `avatar_url` | string | Avatar URL |
| ↳ `html_url` | string | Profile URL |
| ↳ `type` | string | User or Organization |
| `count` | number | Number of stargazers returned |

View File

@@ -119,6 +119,145 @@ Get a specific event from Google Calendar. Returns API-aligned fields only.
| `creator` | json | Event creator |
| `organizer` | json | Event organizer |
### `google_calendar_update`
Update an existing event in Google Calendar. Returns API-aligned fields only.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `calendarId` | string | No | Calendar ID \(defaults to primary\) |
| `eventId` | string | Yes | Event ID to update |
| `summary` | string | No | New event title/summary |
| `description` | string | No | New event description |
| `location` | string | No | New event location |
| `startDateTime` | string | No | New start date and time. MUST include timezone offset \(e.g., 2025-06-03T10:00:00-08:00\) OR provide timeZone parameter |
| `endDateTime` | string | No | New end date and time. MUST include timezone offset \(e.g., 2025-06-03T11:00:00-08:00\) OR provide timeZone parameter |
| `timeZone` | string | No | Time zone \(e.g., America/Los_Angeles\). Required if datetime does not include offset. |
| `attendees` | array | No | Array of attendee email addresses \(replaces existing attendees\) |
| `sendUpdates` | string | No | How to send updates to attendees: all, externalOnly, or none |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Event ID |
| `htmlLink` | string | Event link |
| `status` | string | Event status |
| `summary` | string | Event title |
| `description` | string | Event description |
| `location` | string | Event location |
| `start` | json | Event start |
| `end` | json | Event end |
| `attendees` | json | Event attendees |
| `creator` | json | Event creator |
| `organizer` | json | Event organizer |
### `google_calendar_delete`
Delete an event from Google Calendar. Returns API-aligned fields only.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `calendarId` | string | No | Calendar ID \(defaults to primary\) |
| `eventId` | string | Yes | Event ID to delete |
| `sendUpdates` | string | No | How to send updates to attendees: all, externalOnly, or none |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventId` | string | Deleted event ID |
| `deleted` | boolean | Whether deletion was successful |
### `google_calendar_move`
Move an event to a different calendar. Returns API-aligned fields only.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `calendarId` | string | No | Source calendar ID \(defaults to primary\) |
| `eventId` | string | Yes | Event ID to move |
| `destinationCalendarId` | string | Yes | Destination calendar ID |
| `sendUpdates` | string | No | How to send updates to attendees: all, externalOnly, or none |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Event ID |
| `htmlLink` | string | Event link |
| `status` | string | Event status |
| `summary` | string | Event title |
| `description` | string | Event description |
| `location` | string | Event location |
| `start` | json | Event start |
| `end` | json | Event end |
| `attendees` | json | Event attendees |
| `creator` | json | Event creator |
| `organizer` | json | Event organizer |
### `google_calendar_instances`
Get instances of a recurring event from Google Calendar. Returns API-aligned fields only.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `calendarId` | string | No | Calendar ID \(defaults to primary\) |
| `eventId` | string | Yes | Recurring event ID to get instances of |
| `timeMin` | string | No | Lower bound for instances \(RFC3339 timestamp, e.g., 2025-06-03T00:00:00Z\) |
| `timeMax` | string | No | Upper bound for instances \(RFC3339 timestamp, e.g., 2025-06-04T00:00:00Z\) |
| `maxResults` | number | No | Maximum number of instances to return \(default 250, max 2500\) |
| `pageToken` | string | No | Token for retrieving subsequent pages of results |
| `showDeleted` | boolean | No | Include deleted instances |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `nextPageToken` | string | Next page token |
| `timeZone` | string | Calendar time zone |
| `instances` | json | List of recurring event instances |
### `google_calendar_list_calendars`
List all calendars in the user
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `minAccessRole` | string | No | Minimum access role for returned calendars: freeBusyReader, reader, writer, or owner |
| `maxResults` | number | No | Maximum number of calendars to return \(default 100, max 250\) |
| `pageToken` | string | No | Token for retrieving subsequent pages of results |
| `showDeleted` | boolean | No | Include deleted calendars |
| `showHidden` | boolean | No | Include hidden calendars |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `nextPageToken` | string | Next page token |
| `calendars` | array | List of calendars |
| ↳ `id` | string | Calendar ID |
| ↳ `summary` | string | Calendar title |
| ↳ `description` | string | Calendar description |
| ↳ `location` | string | Calendar location |
| ↳ `timeZone` | string | Calendar time zone |
| ↳ `accessRole` | string | Access role for the calendar |
| ↳ `backgroundColor` | string | Calendar background color |
| ↳ `foregroundColor` | string | Calendar foreground color |
| ↳ `primary` | boolean | Whether this is the primary calendar |
| ↳ `hidden` | boolean | Whether the calendar is hidden |
| ↳ `selected` | boolean | Whether the calendar is selected |
### `google_calendar_quick_add`
Create events from natural language text. Returns API-aligned fields only.

View File

@@ -1,6 +1,6 @@
---
title: Google Drive
description: Create, upload, and list files
description: Manage files, folders, and permissions
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
@@ -40,12 +40,178 @@ In Sim, the Google Drive integration enables your agents to interact directly wi
## Usage Instructions
Integrate Google Drive into the workflow. Can create, upload, and list files.
Integrate Google Drive into the workflow. Can create, upload, download, copy, move, delete, share files and manage permissions.
## Tools
### `google_drive_list`
List files and folders in Google Drive with complete metadata
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `folderSelector` | string | No | Select the folder to list files from |
| `folderId` | string | No | The ID of the folder to list files from \(internal use\) |
| `query` | string | No | Search term to filter files by name \(e.g. "budget" finds files with "budget" in the name\). Do NOT use Google Drive query syntax here - just provide a plain search term. |
| `pageSize` | number | No | The maximum number of files to return \(default: 100\) |
| `pageToken` | string | No | The page token to use for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `files` | array | Array of file metadata objects from Google Drive |
| ↳ `id` | string | Google Drive file ID |
| ↳ `name` | string | File name |
| ↳ `mimeType` | string | MIME type |
| ↳ `kind` | string | Resource type identifier |
| ↳ `description` | string | File description |
| ↳ `originalFilename` | string | Original uploaded filename |
| ↳ `fullFileExtension` | string | Full file extension |
| ↳ `fileExtension` | string | File extension |
| ↳ `owners` | json | List of file owners |
| ↳ `permissions` | json | File permissions |
| ↳ `permissionIds` | json | Permission IDs |
| ↳ `shared` | boolean | Whether file is shared |
| ↳ `ownedByMe` | boolean | Whether owned by current user |
| ↳ `writersCanShare` | boolean | Whether writers can share |
| ↳ `viewersCanCopyContent` | boolean | Whether viewers can copy |
| ↳ `copyRequiresWriterPermission` | boolean | Whether copy requires writer permission |
| ↳ `sharingUser` | json | User who shared the file |
| ↳ `starred` | boolean | Whether file is starred |
| ↳ `trashed` | boolean | Whether file is in trash |
| ↳ `explicitlyTrashed` | boolean | Whether explicitly trashed |
| ↳ `appProperties` | json | App-specific properties |
| ↳ `createdTime` | string | File creation time |
| ↳ `modifiedTime` | string | Last modification time |
| ↳ `modifiedByMeTime` | string | When modified by current user |
| ↳ `viewedByMeTime` | string | When last viewed by current user |
| ↳ `sharedWithMeTime` | string | When shared with current user |
| ↳ `lastModifyingUser` | json | User who last modified the file |
| ↳ `viewedByMe` | boolean | Whether viewed by current user |
| ↳ `modifiedByMe` | boolean | Whether modified by current user |
| ↳ `webViewLink` | string | URL to view in browser |
| ↳ `webContentLink` | string | Direct download URL |
| ↳ `iconLink` | string | URL to file icon |
| ↳ `thumbnailLink` | string | URL to thumbnail |
| ↳ `exportLinks` | json | Export format links |
| ↳ `size` | string | File size in bytes |
| ↳ `quotaBytesUsed` | string | Storage quota used |
| ↳ `md5Checksum` | string | MD5 hash |
| ↳ `sha1Checksum` | string | SHA-1 hash |
| ↳ `sha256Checksum` | string | SHA-256 hash |
| ↳ `parents` | json | Parent folder IDs |
| ↳ `spaces` | json | Spaces containing file |
| ↳ `driveId` | string | Shared drive ID |
| ↳ `capabilities` | json | User capabilities on file |
| ↳ `version` | string | Version number |
| ↳ `headRevisionId` | string | Head revision ID |
| ↳ `hasThumbnail` | boolean | Whether has thumbnail |
| ↳ `thumbnailVersion` | string | Thumbnail version |
| ↳ `imageMediaMetadata` | json | Image-specific metadata |
| ↳ `videoMediaMetadata` | json | Video-specific metadata |
| ↳ `isAppAuthorized` | boolean | Whether created by requesting app |
| ↳ `contentRestrictions` | json | Content restrictions |
| ↳ `linkShareMetadata` | json | Link share metadata |
| `nextPageToken` | string | Token for fetching the next page of results |
### `google_drive_get_file`
Get metadata for a specific file in Google Drive by its ID
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `fileId` | string | Yes | The ID of the file to retrieve |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `file` | json | The file metadata |
| ↳ `id` | string | Google Drive file ID |
| ↳ `name` | string | File name |
| ↳ `mimeType` | string | MIME type |
| ↳ `description` | string | File description |
| ↳ `size` | string | File size in bytes |
| ↳ `starred` | boolean | Whether file is starred |
| ↳ `trashed` | boolean | Whether file is in trash |
| ↳ `webViewLink` | string | URL to view in browser |
| ↳ `webContentLink` | string | Direct download URL |
| ↳ `iconLink` | string | URL to file icon |
| ↳ `thumbnailLink` | string | URL to thumbnail |
| ↳ `parents` | json | Parent folder IDs |
| ↳ `owners` | json | List of file owners |
| ↳ `permissions` | json | File permissions |
| ↳ `createdTime` | string | File creation time |
| ↳ `modifiedTime` | string | Last modification time |
| ↳ `lastModifyingUser` | json | User who last modified the file |
| ↳ `shared` | boolean | Whether file is shared |
| ↳ `ownedByMe` | boolean | Whether owned by current user |
| ↳ `capabilities` | json | User capabilities on file |
| ↳ `md5Checksum` | string | MD5 hash |
| ↳ `version` | string | Version number |
### `google_drive_create_folder`
Create a new folder in Google Drive with complete metadata returned
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `fileName` | string | Yes | Name of the folder to create |
| `folderSelector` | string | No | Select the parent folder to create the folder in |
| `folderId` | string | No | ID of the parent folder \(internal use\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `file` | object | Complete created folder metadata from Google Drive |
| ↳ `id` | string | Google Drive folder ID |
| ↳ `name` | string | Folder name |
| ↳ `mimeType` | string | MIME type \(application/vnd.google-apps.folder\) |
| ↳ `kind` | string | Resource type identifier |
| ↳ `description` | string | Folder description |
| ↳ `owners` | json | List of folder owners |
| ↳ `permissions` | json | Folder permissions |
| ↳ `permissionIds` | json | Permission IDs |
| ↳ `shared` | boolean | Whether folder is shared |
| ↳ `ownedByMe` | boolean | Whether owned by current user |
| ↳ `writersCanShare` | boolean | Whether writers can share |
| ↳ `viewersCanCopyContent` | boolean | Whether viewers can copy |
| ↳ `copyRequiresWriterPermission` | boolean | Whether copy requires writer permission |
| ↳ `sharingUser` | json | User who shared the folder |
| ↳ `starred` | boolean | Whether folder is starred |
| ↳ `trashed` | boolean | Whether folder is in trash |
| ↳ `explicitlyTrashed` | boolean | Whether explicitly trashed |
| ↳ `appProperties` | json | App-specific properties |
| ↳ `folderColorRgb` | string | Folder color |
| ↳ `createdTime` | string | Folder creation time |
| ↳ `modifiedTime` | string | Last modification time |
| ↳ `modifiedByMeTime` | string | When modified by current user |
| ↳ `viewedByMeTime` | string | When last viewed by current user |
| ↳ `sharedWithMeTime` | string | When shared with current user |
| ↳ `lastModifyingUser` | json | User who last modified the folder |
| ↳ `viewedByMe` | boolean | Whether viewed by current user |
| ↳ `modifiedByMe` | boolean | Whether modified by current user |
| ↳ `webViewLink` | string | URL to view in browser |
| ↳ `iconLink` | string | URL to folder icon |
| ↳ `parents` | json | Parent folder IDs |
| ↳ `spaces` | json | Spaces containing folder |
| ↳ `driveId` | string | Shared drive ID |
| ↳ `capabilities` | json | User capabilities on folder |
| ↳ `version` | string | Version number |
| ↳ `isAppAuthorized` | boolean | Whether created by requesting app |
| ↳ `contentRestrictions` | json | Content restrictions |
| ↳ `linkShareMetadata` | json | Link share metadata |
### `google_drive_upload`
Upload a file to Google Drive with complete metadata returned
@@ -119,61 +285,6 @@ Upload a file to Google Drive with complete metadata returned
| ↳ `contentRestrictions` | json | Content restrictions |
| ↳ `linkShareMetadata` | json | Link share metadata |
### `google_drive_create_folder`
Create a new folder in Google Drive with complete metadata returned
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `fileName` | string | Yes | Name of the folder to create |
| `folderSelector` | string | No | Select the parent folder to create the folder in |
| `folderId` | string | No | ID of the parent folder \(internal use\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `file` | object | Complete created folder metadata from Google Drive |
| ↳ `id` | string | Google Drive folder ID |
| ↳ `name` | string | Folder name |
| ↳ `mimeType` | string | MIME type \(application/vnd.google-apps.folder\) |
| ↳ `kind` | string | Resource type identifier |
| ↳ `description` | string | Folder description |
| ↳ `owners` | json | List of folder owners |
| ↳ `permissions` | json | Folder permissions |
| ↳ `permissionIds` | json | Permission IDs |
| ↳ `shared` | boolean | Whether folder is shared |
| ↳ `ownedByMe` | boolean | Whether owned by current user |
| ↳ `writersCanShare` | boolean | Whether writers can share |
| ↳ `viewersCanCopyContent` | boolean | Whether viewers can copy |
| ↳ `copyRequiresWriterPermission` | boolean | Whether copy requires writer permission |
| ↳ `sharingUser` | json | User who shared the folder |
| ↳ `starred` | boolean | Whether folder is starred |
| ↳ `trashed` | boolean | Whether folder is in trash |
| ↳ `explicitlyTrashed` | boolean | Whether explicitly trashed |
| ↳ `appProperties` | json | App-specific properties |
| ↳ `folderColorRgb` | string | Folder color |
| ↳ `createdTime` | string | Folder creation time |
| ↳ `modifiedTime` | string | Last modification time |
| ↳ `modifiedByMeTime` | string | When modified by current user |
| ↳ `viewedByMeTime` | string | When last viewed by current user |
| ↳ `sharedWithMeTime` | string | When shared with current user |
| ↳ `lastModifyingUser` | json | User who last modified the folder |
| ↳ `viewedByMe` | boolean | Whether viewed by current user |
| ↳ `modifiedByMe` | boolean | Whether modified by current user |
| ↳ `webViewLink` | string | URL to view in browser |
| ↳ `iconLink` | string | URL to folder icon |
| ↳ `parents` | json | Parent folder IDs |
| ↳ `spaces` | json | Spaces containing folder |
| ↳ `driveId` | string | Shared drive ID |
| ↳ `capabilities` | json | User capabilities on folder |
| ↳ `version` | string | Version number |
| ↳ `isAppAuthorized` | boolean | Whether created by requesting app |
| ↳ `contentRestrictions` | json | Content restrictions |
| ↳ `linkShareMetadata` | json | Link share metadata |
### `google_drive_download`
Download a file from Google Drive with complete metadata (exports Google Workspace files automatically)
@@ -251,77 +362,229 @@ Download a file from Google Drive with complete metadata (exports Google Workspa
| ↳ `linkShareMetadata` | json | Link share metadata |
| ↳ `revisions` | json | File revision history \(first 100 revisions only\) |
### `google_drive_list`
### `google_drive_copy`
List files and folders in Google Drive with complete metadata
Create a copy of a file in Google Drive
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `folderSelector` | string | No | Select the folder to list files from |
| `folderId` | string | No | The ID of the folder to list files from \(internal use\) |
| `query` | string | No | Search term to filter files by name \(e.g. "budget" finds files with "budget" in the name\). Do NOT use Google Drive query syntax here - just provide a plain search term. |
| `pageSize` | number | No | The maximum number of files to return \(default: 100\) |
| `pageToken` | string | No | The page token to use for pagination |
| `fileId` | string | Yes | The ID of the file to copy |
| `newName` | string | No | Name for the copied file \(defaults to "Copy of \[original name\]"\) |
| `destinationFolderId` | string | No | ID of the folder to place the copy in \(defaults to same location as original\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `files` | array | Array of file metadata objects from Google Drive |
| ↳ `id` | string | Google Drive file ID |
| ↳ `name` | string | File name |
| ↳ `mimeType` | string | MIME type |
| ↳ `kind` | string | Resource type identifier |
| ↳ `description` | string | File description |
| ↳ `originalFilename` | string | Original uploaded filename |
| ↳ `fullFileExtension` | string | Full file extension |
| ↳ `fileExtension` | string | File extension |
| ↳ `owners` | json | List of file owners |
| ↳ `permissions` | json | File permissions |
| ↳ `permissionIds` | json | Permission IDs |
| ↳ `shared` | boolean | Whether file is shared |
| ↳ `ownedByMe` | boolean | Whether owned by current user |
| ↳ `writersCanShare` | boolean | Whether writers can share |
| ↳ `viewersCanCopyContent` | boolean | Whether viewers can copy |
| ↳ `copyRequiresWriterPermission` | boolean | Whether copy requires writer permission |
| ↳ `sharingUser` | json | User who shared the file |
| ↳ `starred` | boolean | Whether file is starred |
| ↳ `trashed` | boolean | Whether file is in trash |
| ↳ `explicitlyTrashed` | boolean | Whether explicitly trashed |
| ↳ `appProperties` | json | App-specific properties |
| ↳ `createdTime` | string | File creation time |
| ↳ `modifiedTime` | string | Last modification time |
| ↳ `modifiedByMeTime` | string | When modified by current user |
| ↳ `viewedByMeTime` | string | When last viewed by current user |
| ↳ `sharedWithMeTime` | string | When shared with current user |
| ↳ `lastModifyingUser` | json | User who last modified the file |
| ↳ `viewedByMe` | boolean | Whether viewed by current user |
| ↳ `modifiedByMe` | boolean | Whether modified by current user |
| ↳ `webViewLink` | string | URL to view in browser |
| ↳ `webContentLink` | string | Direct download URL |
| ↳ `iconLink` | string | URL to file icon |
| ↳ `thumbnailLink` | string | URL to thumbnail |
| ↳ `exportLinks` | json | Export format links |
| ↳ `size` | string | File size in bytes |
| ↳ `quotaBytesUsed` | string | Storage quota used |
| ↳ `md5Checksum` | string | MD5 hash |
| ↳ `sha1Checksum` | string | SHA-1 hash |
| ↳ `sha256Checksum` | string | SHA-256 hash |
| ↳ `parents` | json | Parent folder IDs |
| ↳ `spaces` | json | Spaces containing file |
| ↳ `driveId` | string | Shared drive ID |
| ↳ `capabilities` | json | User capabilities on file |
| ↳ `version` | string | Version number |
| ↳ `headRevisionId` | string | Head revision ID |
| ↳ `hasThumbnail` | boolean | Whether has thumbnail |
| ↳ `thumbnailVersion` | string | Thumbnail version |
| ↳ `imageMediaMetadata` | json | Image-specific metadata |
| ↳ `videoMediaMetadata` | json | Video-specific metadata |
| ↳ `isAppAuthorized` | boolean | Whether created by requesting app |
| ↳ `contentRestrictions` | json | Content restrictions |
| ↳ `linkShareMetadata` | json | Link share metadata |
| `nextPageToken` | string | Token for fetching the next page of results |
| `file` | json | The copied file metadata |
| ↳ `id` | string | Google Drive file ID of the copy |
| ↳ `name` | string | File name |
| ↳ `mimeType` | string | MIME type |
| ↳ `webViewLink` | string | URL to view in browser |
| ↳ `parents` | json | Parent folder IDs |
| ↳ `createdTime` | string | File creation time |
| ↳ `modifiedTime` | string | Last modification time |
| ↳ `owners` | json | List of file owners |
| ↳ `size` | string | File size in bytes |
### `google_drive_update`
Update file metadata in Google Drive (rename, move, star, add description)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `fileId` | string | Yes | The ID of the file to update |
| `name` | string | No | New name for the file |
| `description` | string | No | New description for the file |
| `addParents` | string | No | Comma-separated list of parent folder IDs to add \(moves file to these folders\) |
| `removeParents` | string | No | Comma-separated list of parent folder IDs to remove |
| `starred` | boolean | No | Whether to star or unstar the file |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `file` | json | The updated file metadata |
| ↳ `id` | string | Google Drive file ID |
| ↳ `name` | string | File name |
| ↳ `mimeType` | string | MIME type |
| ↳ `description` | string | File description |
| ↳ `starred` | boolean | Whether file is starred |
| ↳ `webViewLink` | string | URL to view in browser |
| ↳ `parents` | json | Parent folder IDs |
| ↳ `modifiedTime` | string | Last modification time |
### `google_drive_trash`
Move a file to the trash in Google Drive (can be restored later)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `fileId` | string | Yes | The ID of the file to move to trash |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `file` | json | The trashed file metadata |
| ↳ `id` | string | Google Drive file ID |
| ↳ `name` | string | File name |
| ↳ `mimeType` | string | MIME type |
| ↳ `trashed` | boolean | Whether file is in trash \(should be true\) |
| ↳ `trashedTime` | string | When file was trashed |
| ↳ `webViewLink` | string | URL to view in browser |
### `google_drive_untrash`
Restore a file from the trash in Google Drive
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `fileId` | string | Yes | The ID of the file to restore from trash |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `file` | json | The restored file metadata |
| ↳ `id` | string | Google Drive file ID |
| ↳ `name` | string | File name |
| ↳ `mimeType` | string | MIME type |
| ↳ `trashed` | boolean | Whether file is in trash \(should be false\) |
| ↳ `webViewLink` | string | URL to view in browser |
| ↳ `parents` | json | Parent folder IDs |
### `google_drive_delete`
Permanently delete a file from Google Drive (bypasses trash)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `fileId` | string | Yes | The ID of the file to permanently delete |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `deleted` | boolean | Whether the file was successfully deleted |
| `fileId` | string | The ID of the deleted file |
### `google_drive_share`
Share a file with a user, group, domain, or make it public
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `fileId` | string | Yes | The ID of the file to share |
| `type` | string | Yes | Type of grantee: user, group, domain, or anyone |
| `role` | string | Yes | Permission role: owner \(transfer ownership\), organizer \(shared drive only\), fileOrganizer \(shared drive only\), writer \(edit\), commenter \(view and comment\), reader \(view only\) |
| `email` | string | No | Email address of the user or group \(required for type=user or type=group\) |
| `domain` | string | No | Domain to share with \(required for type=domain\) |
| `transferOwnership` | boolean | No | Required when role is owner. Transfers ownership to the specified user. |
| `moveToNewOwnersRoot` | boolean | No | When transferring ownership, move the file to the new owner's My Drive root folder. |
| `sendNotification` | boolean | No | Whether to send an email notification \(default: true\) |
| `emailMessage` | string | No | Custom message to include in the notification email |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `permission` | json | The created permission details |
| ↳ `id` | string | Permission ID |
| ↳ `type` | string | Grantee type \(user, group, domain, anyone\) |
| ↳ `role` | string | Permission role |
| ↳ `emailAddress` | string | Email of the grantee |
| ↳ `displayName` | string | Display name of the grantee |
| ↳ `domain` | string | Domain of the grantee |
| ↳ `expirationTime` | string | Expiration time |
| ↳ `deleted` | boolean | Whether grantee is deleted |
### `google_drive_unshare`
Remove a permission from a file (revoke access)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `fileId` | string | Yes | The ID of the file to modify permissions on |
| `permissionId` | string | Yes | The ID of the permission to remove \(use list_permissions to find this\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `removed` | boolean | Whether the permission was successfully removed |
| `fileId` | string | The ID of the file |
| `permissionId` | string | The ID of the removed permission |
### `google_drive_list_permissions`
List all permissions (who has access) for a file in Google Drive
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `fileId` | string | Yes | The ID of the file to list permissions for |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `permissions` | array | List of permissions on the file |
| ↳ `id` | string | Permission ID \(use to remove permission\) |
| ↳ `type` | string | Grantee type \(user, group, domain, anyone\) |
| ↳ `role` | string | Permission role \(owner, organizer, fileOrganizer, writer, commenter, reader\) |
| ↳ `emailAddress` | string | Email of the grantee |
| ↳ `displayName` | string | Display name of the grantee |
| ↳ `photoLink` | string | Photo URL of the grantee |
| ↳ `domain` | string | Domain of the grantee |
| ↳ `expirationTime` | string | When permission expires |
| ↳ `deleted` | boolean | Whether grantee account is deleted |
| ↳ `allowFileDiscovery` | boolean | Whether file is discoverable by grantee |
| ↳ `pendingOwner` | boolean | Whether ownership transfer is pending |
| ↳ `permissionDetails` | json | Details about inherited permissions |
### `google_drive_get_about`
Get information about the user and their Google Drive (storage quota, capabilities)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `user` | json | Information about the authenticated user |
| ↳ `displayName` | string | User display name |
| ↳ `emailAddress` | string | User email address |
| ↳ `photoLink` | string | URL to user profile photo |
| ↳ `permissionId` | string | User permission ID |
| ↳ `me` | boolean | Whether this is the authenticated user |
| `storageQuota` | json | Storage quota information in bytes |
| ↳ `limit` | string | Total storage limit in bytes \(null for unlimited\) |
| ↳ `usage` | string | Total storage used in bytes |
| ↳ `usageInDrive` | string | Storage used by Drive files in bytes |
| ↳ `usageInDriveTrash` | string | Storage used by trashed files in bytes |
| `canCreateDrives` | boolean | Whether user can create shared drives |
| `importFormats` | json | Map of MIME types that can be imported and their target formats |
| `exportFormats` | json | Map of Google Workspace MIME types and their exportable formats |
| `maxUploadSize` | string | Maximum upload size in bytes |

View File

@@ -1,6 +1,6 @@
---
title: Google Forms
description: Read responses from a Google Form
description: Manage Google Forms and responses
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
@@ -29,7 +29,7 @@ In Sim, the Google Forms integration enables your agents to programmatically acc
## Usage Instructions
Integrate Google Forms into your workflow. Provide a Form ID to list responses, or specify a Response ID to fetch a single response. Requires OAuth.
Integrate Google Forms into your workflow. Read form structure, get responses, create forms, update content, and manage notification watches.
@@ -37,15 +37,202 @@ Integrate Google Forms into your workflow. Provide a Form ID to list responses,
### `google_forms_get_responses`
Retrieve a single response or list responses from a Google Form
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `formId` | string | Yes | The ID of the Google Form |
| `responseId` | string | No | If provided, returns this specific response |
| `pageSize` | number | No | Maximum number of responses to return \(service may return fewer\). Defaults to 5000. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `data` | json | Response or list of responses |
| `response` | json | Operation response data |
| `formId` | string | Form ID |
| `title` | string | Form title |
| `responderUri` | string | Form responder URL |
| `items` | json | Form items |
| `responses` | json | Form responses |
| `watches` | json | Form watches |
### `google_forms_get_form`
Retrieve a form structure including its items, settings, and metadata
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `formId` | string | Yes | The ID of the Google Form to retrieve |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `formId` | string | The form ID |
| `title` | string | The form title visible to responders |
| `description` | string | The form description |
| `documentTitle` | string | The document title visible in Drive |
| `responderUri` | string | The URI to share with responders |
| `linkedSheetId` | string | The ID of the linked Google Sheet |
| `revisionId` | string | The revision ID of the form |
| `items` | array | The form items \(questions, sections, etc.\) |
| ↳ `itemId` | string | Item ID |
| ↳ `title` | string | Item title |
| ↳ `description` | string | Item description |
| `settings` | json | Form settings |
| `publishSettings` | json | Form publish settings |
### `google_forms_create_form`
Create a new Google Form with a title
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `title` | string | Yes | The title of the form visible to responders |
| `documentTitle` | string | No | The document title visible in Drive \(defaults to form title\) |
| `unpublished` | boolean | No | If true, create an unpublished form that does not accept responses |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `formId` | string | The ID of the created form |
| `title` | string | The form title |
| `documentTitle` | string | The document title in Drive |
| `responderUri` | string | The URI to share with responders |
| `revisionId` | string | The revision ID of the form |
### `google_forms_batch_update`
Apply multiple updates to a form (add items, update info, change settings, etc.)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `formId` | string | Yes | The ID of the Google Form to update |
| `requests` | json | Yes | Array of update requests \(updateFormInfo, updateSettings, createItem, updateItem, moveItem, deleteItem\) |
| `includeFormInResponse` | boolean | No | Whether to return the updated form in the response |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `replies` | array | The replies from each update request |
| `writeControl` | json | Write control information with revision IDs |
| `form` | json | The updated form \(if includeFormInResponse was true\) |
### `google_forms_set_publish_settings`
Update the publish settings of a form (publish/unpublish, accept responses)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `formId` | string | Yes | The ID of the Google Form |
| `isPublished` | boolean | Yes | Whether the form is published and visible to others |
| `isAcceptingResponses` | boolean | No | Whether the form accepts responses \(forced to false if isPublished is false\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `formId` | string | The form ID |
| `publishSettings` | json | The updated publish settings |
| ↳ `publishState` | object | The publish state |
| ↳ `isPublished` | boolean | Whether the form is published |
| ↳ `isAcceptingResponses` | boolean | Whether the form accepts responses |
| ↳ `isPublished` | boolean | Whether the form is published |
| ↳ `isAcceptingResponses` | boolean | Whether the form accepts responses |
### `google_forms_create_watch`
Create a notification watch for form changes (schema changes or new responses)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `formId` | string | Yes | The ID of the Google Form to watch |
| `eventType` | string | Yes | Event type to watch: SCHEMA \(form changes\) or RESPONSES \(new submissions\) |
| `topicName` | string | Yes | The Cloud Pub/Sub topic name \(format: projects/\{project\}/topics/\{topic\}\) |
| `watchId` | string | No | Custom watch ID \(4-63 chars, lowercase letters, numbers, hyphens\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | The watch ID |
| `eventType` | string | The event type being watched |
| `topicName` | string | The Cloud Pub/Sub topic |
| `createTime` | string | When the watch was created |
| `expireTime` | string | When the watch expires \(7 days after creation\) |
| `state` | string | The watch state \(ACTIVE, SUSPENDED\) |
### `google_forms_list_watches`
List all notification watches for a form
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `formId` | string | Yes | The ID of the Google Form |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `watches` | array | List of watches for the form |
| ↳ `id` | string | Watch ID |
| ↳ `eventType` | string | Event type \(SCHEMA or RESPONSES\) |
| ↳ `createTime` | string | When the watch was created |
| ↳ `expireTime` | string | When the watch expires |
| ↳ `state` | string | Watch state |
### `google_forms_delete_watch`
Delete a notification watch from a form
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `formId` | string | Yes | The ID of the Google Form |
| `watchId` | string | Yes | The ID of the watch to delete |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `deleted` | boolean | Whether the watch was successfully deleted |
### `google_forms_renew_watch`
Renew a notification watch for another 7 days
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `formId` | string | Yes | The ID of the Google Form |
| `watchId` | string | Yes | The ID of the watch to renew |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | The watch ID |
| `eventType` | string | The event type being watched |
| `expireTime` | string | The new expiration time |
| `state` | string | The watch state |

View File

@@ -215,4 +215,191 @@ Check if a user is a member of a Google Group
| --------- | ---- | ----------- |
| `isMember` | boolean | Whether the user is a member of the group |
### `google_groups_list_aliases`
List all email aliases for a Google Group
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `groupKey` | string | Yes | Group email address or unique group ID |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `aliases` | array | List of email aliases for the group |
| ↳ `id` | string | Unique group identifier |
| ↳ `primaryEmail` | string | Group |
| ↳ `alias` | string | Alias email address |
| ↳ `kind` | string | API resource type |
| ↳ `etag` | string | Resource version identifier |
### `google_groups_add_alias`
Add an email alias to a Google Group
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `groupKey` | string | Yes | Group email address or unique group ID |
| `alias` | string | Yes | The email alias to add to the group |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Unique group identifier |
| `primaryEmail` | string | Group |
| `alias` | string | The alias that was added |
| `kind` | string | API resource type |
| `etag` | string | Resource version identifier |
### `google_groups_remove_alias`
Remove an email alias from a Google Group
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `groupKey` | string | Yes | Group email address or unique group ID |
| `alias` | string | Yes | The email alias to remove from the group |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `deleted` | boolean | Whether the alias was successfully deleted |
### `google_groups_get_settings`
Get the settings for a Google Group including access permissions, moderation, and posting options
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `groupEmail` | string | Yes | The email address of the group |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `email` | string | The group |
| `name` | string | The group name \(max 75 characters\) |
| `description` | string | The group description \(max 4096 characters\) |
| `whoCanJoin` | string | Who can join the group \(ANYONE_CAN_JOIN, ALL_IN_DOMAIN_CAN_JOIN, INVITED_CAN_JOIN, CAN_REQUEST_TO_JOIN\) |
| `whoCanViewMembership` | string | Who can view group membership |
| `whoCanViewGroup` | string | Who can view group messages |
| `whoCanPostMessage` | string | Who can post messages to the group |
| `allowExternalMembers` | string | Whether external users can be members |
| `allowWebPosting` | string | Whether web posting is allowed |
| `primaryLanguage` | string | The group |
| `isArchived` | string | Whether messages are archived |
| `archiveOnly` | string | Whether the group is archive-only \(inactive\) |
| `messageModerationLevel` | string | Message moderation level |
| `spamModerationLevel` | string | Spam handling level \(ALLOW, MODERATE, SILENTLY_MODERATE, REJECT\) |
| `replyTo` | string | Default reply destination |
| `customReplyTo` | string | Custom email for replies |
| `includeCustomFooter` | string | Whether to include custom footer |
| `customFooterText` | string | Custom footer text \(max 1000 characters\) |
| `sendMessageDenyNotification` | string | Whether to send rejection notifications |
| `defaultMessageDenyNotificationText` | string | Default rejection message text |
| `membersCanPostAsTheGroup` | string | Whether members can post as the group |
| `includeInGlobalAddressList` | string | Whether included in Global Address List |
| `whoCanLeaveGroup` | string | Who can leave the group |
| `whoCanContactOwner` | string | Who can contact the group owner |
| `favoriteRepliesOnTop` | string | Whether favorite replies appear at top |
| `whoCanApproveMembers` | string | Who can approve new members |
| `whoCanBanUsers` | string | Who can ban users |
| `whoCanModerateMembers` | string | Who can manage members |
| `whoCanModerateContent` | string | Who can moderate content |
| `whoCanAssistContent` | string | Who can assist with content metadata |
| `enableCollaborativeInbox` | string | Whether collaborative inbox is enabled |
| `whoCanDiscoverGroup` | string | Who can discover the group |
| `defaultSender` | string | Default sender identity \(DEFAULT_SELF or GROUP\) |
### `google_groups_update_settings`
Update the settings for a Google Group including access permissions, moderation, and posting options
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `groupEmail` | string | Yes | The email address of the group |
| `name` | string | No | The group name \(max 75 characters\) |
| `description` | string | No | The group description \(max 4096 characters\) |
| `whoCanJoin` | string | No | Who can join: ANYONE_CAN_JOIN, ALL_IN_DOMAIN_CAN_JOIN, INVITED_CAN_JOIN, CAN_REQUEST_TO_JOIN |
| `whoCanViewMembership` | string | No | Who can view membership: ALL_IN_DOMAIN_CAN_VIEW, ALL_MEMBERS_CAN_VIEW, ALL_MANAGERS_CAN_VIEW |
| `whoCanViewGroup` | string | No | Who can view group messages: ANYONE_CAN_VIEW, ALL_IN_DOMAIN_CAN_VIEW, ALL_MEMBERS_CAN_VIEW, ALL_MANAGERS_CAN_VIEW |
| `whoCanPostMessage` | string | No | Who can post: NONE_CAN_POST, ALL_MANAGERS_CAN_POST, ALL_MEMBERS_CAN_POST, ALL_OWNERS_CAN_POST, ALL_IN_DOMAIN_CAN_POST, ANYONE_CAN_POST |
| `allowExternalMembers` | string | No | Whether external users can be members: true or false |
| `allowWebPosting` | string | No | Whether web posting is allowed: true or false |
| `primaryLanguage` | string | No | The group's primary language \(e.g., en\) |
| `isArchived` | string | No | Whether messages are archived: true or false |
| `archiveOnly` | string | No | Whether the group is archive-only \(inactive\): true or false |
| `messageModerationLevel` | string | No | Message moderation: MODERATE_ALL_MESSAGES, MODERATE_NON_MEMBERS, MODERATE_NEW_MEMBERS, MODERATE_NONE |
| `spamModerationLevel` | string | No | Spam handling: ALLOW, MODERATE, SILENTLY_MODERATE, REJECT |
| `replyTo` | string | No | Default reply: REPLY_TO_CUSTOM, REPLY_TO_SENDER, REPLY_TO_LIST, REPLY_TO_OWNER, REPLY_TO_IGNORE, REPLY_TO_MANAGERS |
| `customReplyTo` | string | No | Custom email for replies \(when replyTo is REPLY_TO_CUSTOM\) |
| `includeCustomFooter` | string | No | Whether to include custom footer: true or false |
| `customFooterText` | string | No | Custom footer text \(max 1000 characters\) |
| `sendMessageDenyNotification` | string | No | Whether to send rejection notifications: true or false |
| `defaultMessageDenyNotificationText` | string | No | Default rejection message text |
| `membersCanPostAsTheGroup` | string | No | Whether members can post as the group: true or false |
| `includeInGlobalAddressList` | string | No | Whether included in Global Address List: true or false |
| `whoCanLeaveGroup` | string | No | Who can leave: ALL_MANAGERS_CAN_LEAVE, ALL_MEMBERS_CAN_LEAVE, NONE_CAN_LEAVE |
| `whoCanContactOwner` | string | No | Who can contact owner: ALL_IN_DOMAIN_CAN_CONTACT, ALL_MANAGERS_CAN_CONTACT, ALL_MEMBERS_CAN_CONTACT, ANYONE_CAN_CONTACT |
| `favoriteRepliesOnTop` | string | No | Whether favorite replies appear at top: true or false |
| `whoCanApproveMembers` | string | No | Who can approve members: ALL_OWNERS_CAN_APPROVE, ALL_MANAGERS_CAN_APPROVE, ALL_MEMBERS_CAN_APPROVE, NONE_CAN_APPROVE |
| `whoCanBanUsers` | string | No | Who can ban users: OWNERS_ONLY, OWNERS_AND_MANAGERS, NONE |
| `whoCanModerateMembers` | string | No | Who can manage members: OWNERS_ONLY, OWNERS_AND_MANAGERS, ALL_MEMBERS, NONE |
| `whoCanModerateContent` | string | No | Who can moderate content: OWNERS_ONLY, OWNERS_AND_MANAGERS, ALL_MEMBERS, NONE |
| `whoCanAssistContent` | string | No | Who can assist with content metadata: OWNERS_ONLY, OWNERS_AND_MANAGERS, ALL_MEMBERS, NONE |
| `enableCollaborativeInbox` | string | No | Whether collaborative inbox is enabled: true or false |
| `whoCanDiscoverGroup` | string | No | Who can discover: ANYONE_CAN_DISCOVER, ALL_IN_DOMAIN_CAN_DISCOVER, ALL_MEMBERS_CAN_DISCOVER |
| `defaultSender` | string | No | Default sender: DEFAULT_SELF or GROUP |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `email` | string | The group |
| `name` | string | The group name |
| `description` | string | The group description |
| `whoCanJoin` | string | Who can join the group |
| `whoCanViewMembership` | string | Who can view group membership |
| `whoCanViewGroup` | string | Who can view group messages |
| `whoCanPostMessage` | string | Who can post messages to the group |
| `allowExternalMembers` | string | Whether external users can be members |
| `allowWebPosting` | string | Whether web posting is allowed |
| `primaryLanguage` | string | The group |
| `isArchived` | string | Whether messages are archived |
| `archiveOnly` | string | Whether the group is archive-only |
| `messageModerationLevel` | string | Message moderation level |
| `spamModerationLevel` | string | Spam handling level |
| `replyTo` | string | Default reply destination |
| `customReplyTo` | string | Custom email for replies |
| `includeCustomFooter` | string | Whether to include custom footer |
| `customFooterText` | string | Custom footer text |
| `sendMessageDenyNotification` | string | Whether to send rejection notifications |
| `defaultMessageDenyNotificationText` | string | Default rejection message text |
| `membersCanPostAsTheGroup` | string | Whether members can post as the group |
| `includeInGlobalAddressList` | string | Whether included in Global Address List |
| `whoCanLeaveGroup` | string | Who can leave the group |
| `whoCanContactOwner` | string | Who can contact the group owner |
| `favoriteRepliesOnTop` | string | Whether favorite replies appear at top |
| `whoCanApproveMembers` | string | Who can approve new members |
| `whoCanBanUsers` | string | Who can ban users |
| `whoCanModerateMembers` | string | Who can manage members |
| `whoCanModerateContent` | string | Who can moderate content |
| `whoCanAssistContent` | string | Who can assist with content metadata |
| `enableCollaborativeInbox` | string | Whether collaborative inbox is enabled |
| `whoCanDiscoverGroup` | string | Who can discover the group |
| `defaultSender` | string | Default sender identity |

View File

@@ -28,7 +28,7 @@ In Sim, the Google Sheets integration empowers your agents to automate reading f
## Usage Instructions
Integrate Google Sheets into the workflow with explicit sheet selection. Can read, write, append, and update data in specific sheets.
Integrate Google Sheets into the workflow with explicit sheet selection. Can read, write, append, update, clear data, create spreadsheets, get spreadsheet info, and copy sheets.
@@ -42,9 +42,8 @@ Read data from a specific sheet in a Google Sheets spreadsheet
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `spreadsheetId` | string | Yes | The ID of the spreadsheet |
| `sheetName` | string | Yes | The name of the sheet/tab to read from |
| `cellRange` | string | No | The cell range to read \(e.g. "A1:D10"\). Defaults to "A1:Z1000" if not specified. |
| `spreadsheetId` | string | Yes | The ID of the spreadsheet \(found in the URL: docs.google.com/spreadsheets/d/\{SPREADSHEET_ID\}/edit\). |
| `range` | string | No | The A1 notation range to read \(e.g. "Sheet1!A1:D10", "A1:B5"\). Defaults to first sheet A1:Z1000 if not specified. |
#### Output
@@ -66,8 +65,7 @@ Write data to a specific sheet in a Google Sheets spreadsheet
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `spreadsheetId` | string | Yes | The ID of the spreadsheet |
| `sheetName` | string | Yes | The name of the sheet/tab to write to |
| `cellRange` | string | No | The cell range to write to \(e.g. "A1:D10", "A1"\). Defaults to "A1" if not specified. |
| `range` | string | No | The A1 notation range to write to \(e.g. "Sheet1!A1:D10", "A1:B5"\) |
| `values` | array | Yes | The data to write as a 2D array \(e.g. \[\["Name", "Age"\], \["Alice", 30\], \["Bob", 25\]\]\) or array of objects. |
| `valueInputOption` | string | No | The format of the data to write |
| `includeValuesInResponse` | boolean | No | Whether to include the written values in the response |
@@ -93,8 +91,7 @@ Update data in a specific sheet in a Google Sheets spreadsheet
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `spreadsheetId` | string | Yes | The ID of the spreadsheet to update |
| `sheetName` | string | Yes | The name of the sheet/tab to update |
| `cellRange` | string | No | The cell range to update \(e.g. "A1:D10", "A1"\). Defaults to "A1" if not specified. |
| `range` | string | No | The A1 notation range to update \(e.g. "Sheet1!A1:D10", "A1:B5"\) |
| `values` | array | Yes | The data to update as a 2D array \(e.g. \[\["Name", "Age"\], \["Alice", 30\]\]\) or array of objects. |
| `valueInputOption` | string | No | The format of the data to update |
| `includeValuesInResponse` | boolean | No | Whether to include the updated values in the response |
@@ -120,7 +117,7 @@ Append data to the end of a specific sheet in a Google Sheets spreadsheet
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `spreadsheetId` | string | Yes | The ID of the spreadsheet to append to |
| `sheetName` | string | Yes | The name of the sheet/tab to append to |
| `range` | string | No | The A1 notation range to append after \(e.g. "Sheet1", "Sheet1!A:D"\) |
| `values` | array | Yes | The data to append as a 2D array \(e.g. \[\["Alice", 30\], \["Bob", 25\]\]\) or array of objects. |
| `valueInputOption` | string | No | The format of the data to append |
| `insertDataOption` | string | No | How to insert the data \(OVERWRITE or INSERT_ROWS\) |
@@ -139,4 +136,180 @@ Append data to the end of a specific sheet in a Google Sheets spreadsheet
| ↳ `spreadsheetId` | string | Google Sheets spreadsheet ID |
| ↳ `spreadsheetUrl` | string | Spreadsheet URL |
### `google_sheets_clear`
Clear values from a specific range in a Google Sheets spreadsheet
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `spreadsheetId` | string | Yes | The ID of the spreadsheet |
| `sheetName` | string | Yes | The name of the sheet/tab to clear |
| `cellRange` | string | No | The cell range to clear \(e.g. "A1:D10"\). Clears entire sheet if not specified. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `clearedRange` | string | The range that was cleared |
| `sheetName` | string | Name of the sheet that was cleared |
| `metadata` | json | Spreadsheet metadata including ID and URL |
| ↳ `spreadsheetId` | string | Google Sheets spreadsheet ID |
| ↳ `spreadsheetUrl` | string | Spreadsheet URL |
### `google_sheets_get_spreadsheet`
Get metadata about a Google Sheets spreadsheet including title and sheet list
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `spreadsheetId` | string | Yes | The ID of the spreadsheet |
| `includeGridData` | boolean | No | Whether to include grid data \(cell values\). Defaults to false. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `spreadsheetId` | string | The spreadsheet ID |
| `title` | string | The title of the spreadsheet |
| `locale` | string | The locale of the spreadsheet |
| `timeZone` | string | The time zone of the spreadsheet |
| `spreadsheetUrl` | string | URL to the spreadsheet |
| `sheets` | array | List of sheets in the spreadsheet |
| ↳ `sheetId` | number | The sheet ID |
| ↳ `title` | string | The sheet title/name |
| ↳ `index` | number | The sheet index \(position\) |
| ↳ `rowCount` | number | Number of rows in the sheet |
| ↳ `columnCount` | number | Number of columns in the sheet |
| ↳ `hidden` | boolean | Whether the sheet is hidden |
### `google_sheets_create_spreadsheet`
Create a new Google Sheets spreadsheet
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `title` | string | Yes | The title of the new spreadsheet |
| `sheetTitles` | json | No | Array of sheet names to create \(e.g., \["Sheet1", "Data", "Summary"\]\). Defaults to a single "Sheet1". |
| `locale` | string | No | The locale of the spreadsheet \(e.g., "en_US"\) |
| `timeZone` | string | No | The time zone of the spreadsheet \(e.g., "America/New_York"\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `spreadsheetId` | string | The ID of the created spreadsheet |
| `title` | string | The title of the created spreadsheet |
| `spreadsheetUrl` | string | URL to the created spreadsheet |
| `sheets` | array | List of sheets created in the spreadsheet |
| ↳ `sheetId` | number | The sheet ID |
| ↳ `title` | string | The sheet title/name |
| ↳ `index` | number | The sheet index \(position\) |
### `google_sheets_batch_get`
Read multiple ranges from a Google Sheets spreadsheet in a single request
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `spreadsheetId` | string | Yes | The ID of the spreadsheet |
| `ranges` | json | Yes | Array of ranges to read \(e.g., \["Sheet1!A1:D10", "Sheet2!A1:B5"\]\). Each range should include sheet name. |
| `majorDimension` | string | No | The major dimension of values: "ROWS" \(default\) or "COLUMNS" |
| `valueRenderOption` | string | No | How values should be rendered: "FORMATTED_VALUE" \(default\), "UNFORMATTED_VALUE", or "FORMULA" |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `spreadsheetId` | string | The spreadsheet ID |
| `valueRanges` | array | Array of value ranges read from the spreadsheet |
| ↳ `range` | string | The range that was read |
| ↳ `majorDimension` | string | Major dimension \(ROWS or COLUMNS\) |
| ↳ `values` | array | The cell values as a 2D array |
| `metadata` | json | Spreadsheet metadata including ID and URL |
| ↳ `spreadsheetId` | string | Google Sheets spreadsheet ID |
| ↳ `spreadsheetUrl` | string | Spreadsheet URL |
### `google_sheets_batch_update`
Update multiple ranges in a Google Sheets spreadsheet in a single request
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `spreadsheetId` | string | Yes | The ID of the spreadsheet |
| `data` | json | Yes | Array of value ranges to update. Each item should have "range" \(e.g., "Sheet1!A1:D10"\) and "values" \(2D array\). |
| `valueInputOption` | string | No | How input data should be interpreted: "RAW" or "USER_ENTERED" \(default\). USER_ENTERED parses formulas. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `spreadsheetId` | string | The spreadsheet ID |
| `totalUpdatedRows` | number | Total number of rows updated |
| `totalUpdatedColumns` | number | Total number of columns updated |
| `totalUpdatedCells` | number | Total number of cells updated |
| `totalUpdatedSheets` | number | Total number of sheets updated |
| `responses` | array | Array of update responses for each range |
| ↳ `spreadsheetId` | string | The spreadsheet ID |
| ↳ `updatedRange` | string | The range that was updated |
| ↳ `updatedRows` | number | Number of rows updated in this range |
| ↳ `updatedColumns` | number | Number of columns updated in this range |
| ↳ `updatedCells` | number | Number of cells updated in this range |
| `metadata` | json | Spreadsheet metadata including ID and URL |
| ↳ `spreadsheetId` | string | Google Sheets spreadsheet ID |
| ↳ `spreadsheetUrl` | string | Spreadsheet URL |
### `google_sheets_batch_clear`
Clear multiple ranges in a Google Sheets spreadsheet in a single request
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `spreadsheetId` | string | Yes | The ID of the spreadsheet |
| `ranges` | json | Yes | Array of ranges to clear \(e.g., \["Sheet1!A1:D10", "Sheet2!A1:B5"\]\). Each range should include sheet name. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `spreadsheetId` | string | The spreadsheet ID |
| `clearedRanges` | array | Array of ranges that were cleared |
| `metadata` | json | Spreadsheet metadata including ID and URL |
| ↳ `spreadsheetId` | string | Google Sheets spreadsheet ID |
| ↳ `spreadsheetUrl` | string | Spreadsheet URL |
### `google_sheets_copy_sheet`
Copy a sheet from one spreadsheet to another
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `sourceSpreadsheetId` | string | Yes | The ID of the source spreadsheet |
| `sheetId` | number | Yes | The ID of the sheet to copy \(numeric ID, not the sheet name\). Use Get Spreadsheet to find sheet IDs. |
| `destinationSpreadsheetId` | string | Yes | The ID of the destination spreadsheet where the sheet will be copied |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `sheetId` | number | The ID of the newly created sheet in the destination |
| `title` | string | The title of the copied sheet |
| `index` | number | The index \(position\) of the copied sheet |
| `sheetType` | string | The type of the sheet \(GRID, CHART, etc.\) |
| `destinationSpreadsheetId` | string | The ID of the destination spreadsheet |
| `destinationSpreadsheetUrl` | string | URL to the destination spreadsheet |

View File

@@ -30,7 +30,7 @@ In Sim, the Google Slides integration enables your agents to interact directly w
## Usage Instructions
Integrate Google Slides into the workflow. Can read, write, create presentations, replace text, add slides, add images, and get thumbnails.
Integrate Google Slides into the workflow. Can read, write, create presentations, replace text, add slides, add images, get thumbnails, get page details, delete objects, duplicate objects, reorder slides, create tables, create shapes, and insert text.
@@ -177,4 +177,157 @@ Generate a thumbnail image of a specific slide in a Google Slides presentation
| `height` | number | Height of the thumbnail in pixels |
| `metadata` | json | Operation metadata including presentation ID and page object ID |
### `google_slides_get_page`
Get detailed information about a specific slide/page in a Google Slides presentation
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `presentationId` | string | Yes | The ID of the presentation |
| `pageObjectId` | string | Yes | The object ID of the slide/page to retrieve |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `objectId` | string | The object ID of the page |
| `pageType` | string | The type of page \(SLIDE, MASTER, LAYOUT, NOTES, NOTES_MASTER\) |
| `pageElements` | json | Array of page elements \(shapes, images, tables, etc.\) on this page |
| `slideProperties` | json | Properties specific to slides \(layout, master, notes\) |
| `metadata` | json | Operation metadata including presentation ID and URL |
### `google_slides_delete_object`
Delete a page element (shape, image, table, etc.) or an entire slide from a Google Slides presentation
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `presentationId` | string | Yes | The ID of the presentation |
| `objectId` | string | Yes | The object ID of the element or slide to delete |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `deleted` | boolean | Whether the object was successfully deleted |
| `objectId` | string | The object ID that was deleted |
| `metadata` | json | Operation metadata including presentation ID and URL |
### `google_slides_duplicate_object`
Duplicate an object (slide, shape, image, table, etc.) in a Google Slides presentation
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `presentationId` | string | Yes | The ID of the presentation |
| `objectId` | string | Yes | The object ID of the element or slide to duplicate |
| `objectIds` | string | No | Optional JSON object mapping source object IDs \(within the slide being duplicated\) to new object IDs for the duplicates. Format: \{"sourceId1":"newId1","sourceId2":"newId2"\} |
| `Format` | string | No | No description |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `duplicatedObjectId` | string | The object ID of the newly created duplicate |
| `metadata` | json | Operation metadata including presentation ID and source object ID |
### `google_slides_update_slides_position`
Move one or more slides to a new position in a Google Slides presentation
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `presentationId` | string | Yes | The ID of the presentation |
| `slideObjectIds` | string | Yes | Comma-separated list of slide object IDs to move. The slides will maintain their relative order. |
| `insertionIndex` | number | Yes | The zero-based index where the slides should be moved. All slides with indices greater than or equal to this will be shifted right. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `moved` | boolean | Whether the slides were successfully moved |
| `slideObjectIds` | array | The slide object IDs that were moved |
| `insertionIndex` | number | The index where the slides were moved to |
| `metadata` | json | Operation metadata including presentation ID and URL |
### `google_slides_create_table`
Create a new table on a slide in a Google Slides presentation
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `presentationId` | string | Yes | The ID of the presentation |
| `pageObjectId` | string | Yes | The object ID of the slide/page to add the table to |
| `rows` | number | Yes | Number of rows in the table \(minimum 1\) |
| `columns` | number | Yes | Number of columns in the table \(minimum 1\) |
| `width` | number | No | Width of the table in points \(default: 400\) |
| `height` | number | No | Height of the table in points \(default: 200\) |
| `positionX` | number | No | X position from the left edge in points \(default: 100\) |
| `positionY` | number | No | Y position from the top edge in points \(default: 100\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `tableId` | string | The object ID of the newly created table |
| `rows` | number | Number of rows in the table |
| `columns` | number | Number of columns in the table |
| `metadata` | json | Operation metadata including presentation ID and page object ID |
### `google_slides_create_shape`
Create a shape (rectangle, ellipse, text box, arrow, etc.) on a slide in a Google Slides presentation
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `presentationId` | string | Yes | The ID of the presentation |
| `pageObjectId` | string | Yes | The object ID of the slide/page to add the shape to |
| `shapeType` | string | Yes | The type of shape to create. Common types: TEXT_BOX, RECTANGLE, ROUND_RECTANGLE, ELLIPSE, TRIANGLE, DIAMOND, STAR_5, ARROW_EAST, HEART, CLOUD |
| `width` | number | No | Width of the shape in points \(default: 200\) |
| `height` | number | No | Height of the shape in points \(default: 100\) |
| `positionX` | number | No | X position from the left edge in points \(default: 100\) |
| `positionY` | number | No | Y position from the top edge in points \(default: 100\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `shapeId` | string | The object ID of the newly created shape |
| `shapeType` | string | The type of shape that was created |
| `metadata` | json | Operation metadata including presentation ID and page object ID |
### `google_slides_insert_text`
Insert text into a shape or table cell in a Google Slides presentation. Use this to add text to text boxes, shapes, or table cells.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `presentationId` | string | Yes | The ID of the presentation |
| `objectId` | string | Yes | The object ID of the shape or table cell to insert text into. For table cells, use the cell object ID. |
| `text` | string | Yes | The text to insert |
| `insertionIndex` | number | No | The zero-based index at which to insert the text. If not specified, text is inserted at the beginning \(index 0\). |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `inserted` | boolean | Whether the text was successfully inserted |
| `objectId` | string | The object ID where text was inserted |
| `text` | string | The text that was inserted |
| `metadata` | json | Operation metadata including presentation ID and URL |

View File

@@ -51,6 +51,7 @@ Search for similar content in a knowledge base using vector similarity
| `properties` | string | No | No description |
| `tagName` | string | No | No description |
| `tagValue` | string | No | No description |
| `tagFilters` | string | No | No description |
#### Output
@@ -108,19 +109,8 @@ Create a new document in a knowledge base
| `knowledgeBaseId` | string | Yes | ID of the knowledge base containing the document |
| `name` | string | Yes | Name of the document |
| `content` | string | Yes | Content of the document |
| `tag1` | string | No | Tag 1 value for the document |
| `tag2` | string | No | Tag 2 value for the document |
| `tag3` | string | No | Tag 3 value for the document |
| `tag4` | string | No | Tag 4 value for the document |
| `tag5` | string | No | Tag 5 value for the document |
| `tag6` | string | No | Tag 6 value for the document |
| `tag7` | string | No | Tag 7 value for the document |
| `documentTagsData` | array | No | Structured tag data with names, types, and values |
| `items` | object | No | No description |
| `properties` | string | No | No description |
| `tagName` | string | No | No description |
| `tagValue` | string | No | No description |
| `tagType` | string | No | No description |
| `documentTags` | object | No | Document tags |
| `documentTags` | string | No | No description |
#### Output

View File

@@ -45,8 +45,7 @@ Read data from a specific sheet in a Microsoft Excel spreadsheet
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `spreadsheetId` | string | Yes | The ID of the spreadsheet to read from |
| `sheetName` | string | Yes | The name of the sheet/tab to read from |
| `cellRange` | string | No | The cell range to read \(e.g., "A1:D10"\). If not specified, reads the entire used range. |
| `range` | string | No | The range of cells to read from. Accepts "SheetName!A1:B2" for explicit ranges or just "SheetName" to read the used range of that sheet. If omitted, reads the used range of the first sheet. |
#### Output
@@ -68,9 +67,8 @@ Write data to a specific sheet in a Microsoft Excel spreadsheet
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `spreadsheetId` | string | Yes | The ID of the spreadsheet to write to |
| `sheetName` | string | Yes | The name of the sheet/tab to write to |
| `cellRange` | string | No | The cell range to write to \(e.g., "A1:D10", "A1"\). Defaults to "A1" if not specified. |
| `values` | array | Yes | The data to write as a 2D array \(e.g. \[\["Name", "Age"\], \["Alice", 30\], \["Bob", 25\]\]\) or array of objects. |
| `range` | string | No | The range of cells to write to |
| `values` | array | Yes | The data to write to the spreadsheet |
| `valueInputOption` | string | No | The format of the data to write |
| `includeValuesInResponse` | boolean | No | Whether to include the written values in the response |

View File

@@ -84,9 +84,10 @@ Send messages to Slack channels or direct messages. Supports Slack mrkdwn format
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `authMethod` | string | No | Authentication method: oauth or bot_token |
| `destinationType` | string | No | Destination type: channel or dm |
| `botToken` | string | No | Bot token for Custom Bot |
| `channel` | string | No | Target Slack channel \(e.g., #general\) |
| `userId` | string | No | Target Slack user ID for direct messages \(e.g., U1234567890\) |
| `dmUserId` | string | No | Target Slack user for direct messages |
| `text` | string | Yes | Message text to send \(supports Slack mrkdwn formatting\) |
| `thread_ts` | string | No | Thread timestamp to reply to \(creates thread reply\) |
| `files` | file[] | No | Files to attach to the message |
@@ -132,9 +133,10 @@ Read the latest messages from Slack channels. Retrieve conversation history with
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `authMethod` | string | No | Authentication method: oauth or bot_token |
| `destinationType` | string | No | Destination type: channel or dm |
| `botToken` | string | No | Bot token for Custom Bot |
| `channel` | string | No | Slack channel to read messages from \(e.g., #general\) |
| `userId` | string | No | User ID for DM conversation \(e.g., U1234567890\) |
| `dmUserId` | string | No | Target Slack user for DM conversation |
| `limit` | number | No | Number of messages to retrieve \(default: 10, max: 15\) |
| `oldest` | string | No | Start of time range \(timestamp\) |
| `latest` | string | No | End of time range \(timestamp\) |

View File

@@ -84,6 +84,44 @@ export const GitHubBlock: BlockConfig<GitHubResponse> = {
{ label: 'Create project', id: 'github_create_project' },
{ label: 'Update project', id: 'github_update_project' },
{ label: 'Delete project', id: 'github_delete_project' },
// Search Operations
{ label: 'Search code', id: 'github_search_code' },
{ label: 'Search commits', id: 'github_search_commits' },
{ label: 'Search issues', id: 'github_search_issues' },
{ label: 'Search repositories', id: 'github_search_repos' },
{ label: 'Search users', id: 'github_search_users' },
// Commit Operations
{ label: 'List commits', id: 'github_list_commits' },
{ label: 'Get commit', id: 'github_get_commit' },
{ label: 'Compare commits', id: 'github_compare_commits' },
// Gist Operations
{ label: 'Create gist', id: 'github_create_gist' },
{ label: 'Get gist', id: 'github_get_gist' },
{ label: 'List gists', id: 'github_list_gists' },
{ label: 'Update gist', id: 'github_update_gist' },
{ label: 'Delete gist', id: 'github_delete_gist' },
{ label: 'Fork gist', id: 'github_fork_gist' },
{ label: 'Star gist', id: 'github_star_gist' },
{ label: 'Unstar gist', id: 'github_unstar_gist' },
// Fork Operations
{ label: 'Fork repository', id: 'github_fork_repo' },
{ label: 'List forks', id: 'github_list_forks' },
// Milestone Operations
{ label: 'Create milestone', id: 'github_create_milestone' },
{ label: 'Get milestone', id: 'github_get_milestone' },
{ label: 'List milestones', id: 'github_list_milestones' },
{ label: 'Update milestone', id: 'github_update_milestone' },
{ label: 'Delete milestone', id: 'github_delete_milestone' },
// Reaction Operations
{ label: 'Add issue reaction', id: 'github_create_issue_reaction' },
{ label: 'Remove issue reaction', id: 'github_delete_issue_reaction' },
{ label: 'Add comment reaction', id: 'github_create_comment_reaction' },
{ label: 'Remove comment reaction', id: 'github_delete_comment_reaction' },
// Star Operations
{ label: 'Star repository', id: 'github_star_repo' },
{ label: 'Unstar repository', id: 'github_unstar_repo' },
{ label: 'Check if starred', id: 'github_check_star' },
{ label: 'List stargazers', id: 'github_list_stargazers' },
],
value: () => 'github_pr',
},
@@ -998,6 +1036,440 @@ export const GitHubBlock: BlockConfig<GitHubResponse> = {
required: true,
condition: { field: 'operation', value: 'github_delete_project' },
},
// Search operations parameters
{
id: 'q',
title: 'Search Query',
type: 'short-input',
placeholder: 'e.g., react language:typescript',
required: true,
condition: {
field: 'operation',
value: [
'github_search_code',
'github_search_commits',
'github_search_issues',
'github_search_repos',
'github_search_users',
],
},
wandConfig: {
enabled: true,
prompt: `Generate a GitHub search query based on the user's description.
GitHub search supports these qualifiers:
- For repos: language:python, stars:>1000, forks:>100, topic:react, user:owner, org:name, created:>2023-01-01
- For code: repo:owner/name, path:src, extension:ts, language:javascript
- For issues/PRs: is:issue, is:pr, is:open, is:closed, label:bug, author:user, assignee:user
- For commits: repo:owner/name, author:user, committer:user, author-date:>2023-01-01
- For users: type:user, type:org, followers:>100, repos:>10, location:city
Examples:
- "Python repos with more than 1000 stars" -> language:python stars:>1000
- "Open bugs in facebook/react" -> repo:facebook/react is:issue is:open label:bug
- "TypeScript files in src folder" -> language:typescript path:src
Return ONLY the search query - no explanations.`,
placeholder: 'Describe what you want to search for...',
},
},
{
id: 'sort',
title: 'Sort By',
type: 'dropdown',
options: [
{ label: 'Best match', id: '' },
{ label: 'Stars', id: 'stars' },
{ label: 'Forks', id: 'forks' },
{ label: 'Updated', id: 'updated' },
],
condition: { field: 'operation', value: 'github_search_repos' },
},
{
id: 'order',
title: 'Order',
type: 'dropdown',
options: [
{ label: 'Descending', id: 'desc' },
{ label: 'Ascending', id: 'asc' },
],
condition: {
field: 'operation',
value: [
'github_search_code',
'github_search_commits',
'github_search_issues',
'github_search_repos',
'github_search_users',
],
},
},
// Commit operations parameters
{
id: 'sha',
title: 'SHA or Branch',
type: 'short-input',
placeholder: 'e.g., main or abc123',
condition: { field: 'operation', value: 'github_list_commits' },
},
{
id: 'author',
title: 'Author Filter',
type: 'short-input',
placeholder: 'GitHub username or email',
condition: { field: 'operation', value: 'github_list_commits' },
},
{
id: 'since',
title: 'Since Date',
type: 'short-input',
placeholder: 'ISO 8601: 2024-01-01T00:00:00Z',
condition: { field: 'operation', value: ['github_list_commits', 'github_list_gists'] },
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "last week" -> Calculate 7 days ago at 00:00:00Z
- "yesterday" -> Calculate yesterday's date at 00:00:00Z
- "beginning of this month" -> First day of current month at 00:00:00Z
- "30 days ago" -> Calculate 30 days before current time
- "January 1st 2024" -> 2024-01-01T00:00:00Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the start date (e.g., "last week", "beginning of month")...',
generationType: 'timestamp',
},
},
{
id: 'until',
title: 'Until Date',
type: 'short-input',
placeholder: 'ISO 8601: 2024-12-31T23:59:59Z',
condition: { field: 'operation', value: 'github_list_commits' },
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "now" -> Current timestamp
- "end of today" -> Today's date at 23:59:59Z
- "end of last week" -> Calculate end of last week
- "yesterday" -> Yesterday's date at 23:59:59Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the end date (e.g., "now", "end of yesterday")...',
generationType: 'timestamp',
},
},
{
id: 'ref',
title: 'Commit Reference',
type: 'short-input',
placeholder: 'SHA, branch, or tag',
required: true,
condition: { field: 'operation', value: 'github_get_commit' },
},
{
id: 'base',
title: 'Base Reference',
type: 'short-input',
placeholder: 'Base branch/tag/SHA',
required: true,
condition: { field: 'operation', value: 'github_compare_commits' },
},
{
id: 'head',
title: 'Head Reference',
type: 'short-input',
placeholder: 'Head branch/tag/SHA',
required: true,
condition: { field: 'operation', value: 'github_compare_commits' },
},
// Gist operations parameters
{
id: 'gist_id',
title: 'Gist ID',
type: 'short-input',
placeholder: 'e.g., aa5a315d61ae9438b18d',
required: true,
condition: {
field: 'operation',
value: [
'github_get_gist',
'github_update_gist',
'github_delete_gist',
'github_fork_gist',
'github_star_gist',
'github_unstar_gist',
],
},
},
{
id: 'description',
title: 'Description',
type: 'short-input',
placeholder: 'Gist description',
condition: { field: 'operation', value: ['github_create_gist', 'github_update_gist'] },
},
{
id: 'files',
title: 'Files (JSON)',
type: 'long-input',
placeholder: '{"file.txt": {"content": "Hello"}}',
required: true,
condition: { field: 'operation', value: 'github_create_gist' },
wandConfig: {
enabled: true,
prompt: `Generate a JSON object for GitHub Gist files based on the user's description.
The format is: {"filename.ext": {"content": "file contents"}}
Examples:
- "A Python hello world file" -> {"hello.py": {"content": "print('Hello, World!')"}}
- "A README with project title" -> {"README.md": {"content": "# My Project\\n\\nDescription here"}}
- "JavaScript function to add numbers" -> {"add.js": {"content": "function add(a, b) {\\n return a + b;\\n}"}}
- "Two files: index.html and style.css" -> {"index.html": {"content": "<!DOCTYPE html>..."}, "style.css": {"content": "body { margin: 0; }"}}
Return ONLY valid JSON - no explanations, no markdown formatting.`,
placeholder: 'Describe the files you want to create...',
generationType: 'json-object',
},
},
{
id: 'files',
title: 'Files (JSON)',
type: 'long-input',
placeholder: '{"file.txt": {"content": "Updated"}}',
condition: { field: 'operation', value: 'github_update_gist' },
wandConfig: {
enabled: true,
prompt: `Generate a JSON object for updating GitHub Gist files based on the user's description.
The format is: {"filename.ext": {"content": "new contents"}}
To delete a file, set its value to null: {"old-file.txt": null}
To rename a file, set the new filename: {"old-name.txt": {"filename": "new-name.txt", "content": "..."}}
Examples:
- "Update hello.py to print goodbye" -> {"hello.py": {"content": "print('Goodbye!')"}}
- "Delete the old readme" -> {"README.md": null}
- "Rename script.js to main.js" -> {"script.js": {"filename": "main.js"}}
Return ONLY valid JSON - no explanations, no markdown formatting.`,
placeholder: 'Describe the file changes...',
generationType: 'json-object',
},
},
{
id: 'gist_public',
title: 'Public',
type: 'dropdown',
options: [
{ label: 'Secret', id: 'false' },
{ label: 'Public', id: 'true' },
],
condition: { field: 'operation', value: 'github_create_gist' },
},
{
id: 'username',
title: 'Username',
type: 'short-input',
placeholder: 'GitHub username (optional)',
condition: { field: 'operation', value: 'github_list_gists' },
},
// Fork operations parameters
{
id: 'organization',
title: 'Organization',
type: 'short-input',
placeholder: 'Fork to org (optional)',
condition: { field: 'operation', value: 'github_fork_repo' },
},
{
id: 'fork_name',
title: 'Fork Name',
type: 'short-input',
placeholder: 'Custom name (optional)',
condition: { field: 'operation', value: 'github_fork_repo' },
},
{
id: 'default_branch_only',
title: 'Default Branch Only',
type: 'dropdown',
options: [
{ label: 'No', id: 'false' },
{ label: 'Yes', id: 'true' },
],
condition: { field: 'operation', value: 'github_fork_repo' },
},
{
id: 'fork_sort',
title: 'Sort By',
type: 'dropdown',
options: [
{ label: 'Newest', id: 'newest' },
{ label: 'Oldest', id: 'oldest' },
{ label: 'Stargazers', id: 'stargazers' },
{ label: 'Watchers', id: 'watchers' },
],
condition: { field: 'operation', value: 'github_list_forks' },
},
// Milestone operations parameters
{
id: 'milestone_title',
title: 'Milestone Title',
type: 'short-input',
placeholder: 'e.g., v1.0 Release',
required: true,
condition: { field: 'operation', value: 'github_create_milestone' },
},
{
id: 'milestone_title',
title: 'New Title',
type: 'short-input',
placeholder: 'Updated title (optional)',
condition: { field: 'operation', value: 'github_update_milestone' },
},
{
id: 'milestone_description',
title: 'Description',
type: 'long-input',
placeholder: 'Milestone description',
condition: {
field: 'operation',
value: ['github_create_milestone', 'github_update_milestone'],
},
},
{
id: 'due_on',
title: 'Due Date',
type: 'short-input',
placeholder: 'ISO 8601: 2024-12-31T23:59:59Z',
condition: {
field: 'operation',
value: ['github_create_milestone', 'github_update_milestone'],
},
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp for a milestone due date based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "end of this month" -> Last day of current month at 23:59:59Z
- "next Friday" -> Calculate next Friday's date at 23:59:59Z
- "in 2 weeks" -> Calculate 14 days from now at 23:59:59Z
- "December 31st" -> 2024-12-31T23:59:59Z (current year)
- "Q1 2025" -> 2025-03-31T23:59:59Z (end of Q1)
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the due date (e.g., "end of month", "next Friday")...',
generationType: 'timestamp',
},
},
{
id: 'milestone_number',
title: 'Milestone Number',
type: 'short-input',
placeholder: 'e.g., 1',
required: true,
condition: {
field: 'operation',
value: ['github_get_milestone', 'github_update_milestone', 'github_delete_milestone'],
},
},
{
id: 'milestone_state',
title: 'State Filter',
type: 'dropdown',
options: [
{ label: 'Open', id: 'open' },
{ label: 'Closed', id: 'closed' },
{ label: 'All', id: 'all' },
],
condition: { field: 'operation', value: 'github_list_milestones' },
},
{
id: 'milestone_sort',
title: 'Sort By',
type: 'dropdown',
options: [
{ label: 'Due Date', id: 'due_on' },
{ label: 'Completeness', id: 'completeness' },
],
condition: { field: 'operation', value: 'github_list_milestones' },
},
// Reaction operations parameters
{
id: 'reaction_content',
title: 'Reaction',
type: 'dropdown',
options: [
{ label: '👍 +1', id: '+1' },
{ label: '👎 -1', id: '-1' },
{ label: '😄 Laugh', id: 'laugh' },
{ label: '😕 Confused', id: 'confused' },
{ label: '❤️ Heart', id: 'heart' },
{ label: '🎉 Hooray', id: 'hooray' },
{ label: '🚀 Rocket', id: 'rocket' },
{ label: '👀 Eyes', id: 'eyes' },
],
required: true,
condition: {
field: 'operation',
value: ['github_create_issue_reaction', 'github_create_comment_reaction'],
},
},
{
id: 'issue_number',
title: 'Issue Number',
type: 'short-input',
placeholder: 'e.g., 123',
required: true,
condition: {
field: 'operation',
value: ['github_create_issue_reaction', 'github_delete_issue_reaction'],
},
},
{
id: 'reaction_id',
title: 'Reaction ID',
type: 'short-input',
placeholder: 'e.g., 12345678',
required: true,
condition: {
field: 'operation',
value: ['github_delete_issue_reaction', 'github_delete_comment_reaction'],
},
},
{
id: 'comment_id',
title: 'Comment ID',
type: 'short-input',
placeholder: 'e.g., 987654321',
required: true,
condition: {
field: 'operation',
value: ['github_create_comment_reaction', 'github_delete_comment_reaction'],
},
},
// Star operations parameters - owner/repo already covered by existing subBlocks
{
id: 'per_page',
title: 'Results Per Page',
type: 'short-input',
placeholder: 'e.g., 30 (default: 30, max: 100)',
condition: {
field: 'operation',
value: [
'github_search_code',
'github_search_commits',
'github_search_issues',
'github_search_repos',
'github_search_users',
'github_list_commits',
'github_list_gists',
'github_list_forks',
'github_list_milestones',
'github_list_stargazers',
],
},
},
{
id: 'apiKey',
title: 'GitHub Token',
@@ -1118,6 +1590,44 @@ export const GitHubBlock: BlockConfig<GitHubResponse> = {
'github_create_project',
'github_update_project',
'github_delete_project',
// Search tools
'github_search_code',
'github_search_commits',
'github_search_issues',
'github_search_repos',
'github_search_users',
// Commit tools
'github_list_commits',
'github_get_commit',
'github_compare_commits',
// Gist tools
'github_create_gist',
'github_get_gist',
'github_list_gists',
'github_update_gist',
'github_delete_gist',
'github_fork_gist',
'github_star_gist',
'github_unstar_gist',
// Fork tools
'github_fork_repo',
'github_list_forks',
// Milestone tools
'github_create_milestone',
'github_get_milestone',
'github_list_milestones',
'github_update_milestone',
'github_delete_milestone',
// Reaction tools
'github_create_issue_reaction',
'github_delete_issue_reaction',
'github_create_comment_reaction',
'github_delete_comment_reaction',
// Star tools
'github_star_repo',
'github_unstar_repo',
'github_check_star',
'github_list_stargazers',
],
config: {
tool: (params) => {
@@ -1234,6 +1744,75 @@ export const GitHubBlock: BlockConfig<GitHubResponse> = {
return 'github_update_project'
case 'github_delete_project':
return 'github_delete_project'
// Search operations
case 'github_search_code':
return 'github_search_code'
case 'github_search_commits':
return 'github_search_commits'
case 'github_search_issues':
return 'github_search_issues'
case 'github_search_repos':
return 'github_search_repos'
case 'github_search_users':
return 'github_search_users'
// Commit operations
case 'github_list_commits':
return 'github_list_commits'
case 'github_get_commit':
return 'github_get_commit'
case 'github_compare_commits':
return 'github_compare_commits'
// Gist operations
case 'github_create_gist':
return 'github_create_gist'
case 'github_get_gist':
return 'github_get_gist'
case 'github_list_gists':
return 'github_list_gists'
case 'github_update_gist':
return 'github_update_gist'
case 'github_delete_gist':
return 'github_delete_gist'
case 'github_fork_gist':
return 'github_fork_gist'
case 'github_star_gist':
return 'github_star_gist'
case 'github_unstar_gist':
return 'github_unstar_gist'
// Fork operations
case 'github_fork_repo':
return 'github_fork_repo'
case 'github_list_forks':
return 'github_list_forks'
// Milestone operations
case 'github_create_milestone':
return 'github_create_milestone'
case 'github_get_milestone':
return 'github_get_milestone'
case 'github_list_milestones':
return 'github_list_milestones'
case 'github_update_milestone':
return 'github_update_milestone'
case 'github_delete_milestone':
return 'github_delete_milestone'
// Reaction operations
case 'github_create_issue_reaction':
return 'github_create_issue_reaction'
case 'github_delete_issue_reaction':
return 'github_delete_issue_reaction'
case 'github_create_comment_reaction':
return 'github_create_comment_reaction'
case 'github_delete_comment_reaction':
return 'github_delete_comment_reaction'
// Star operations
case 'github_star_repo':
return 'github_star_repo'
case 'github_unstar_repo':
return 'github_unstar_repo'
case 'github_check_star':
return 'github_check_star'
case 'github_list_stargazers':
return 'github_list_stargazers'
default:
return 'github_repo_info'
}
@@ -1297,6 +1876,38 @@ export const GitHubBlock: BlockConfig<GitHubResponse> = {
project_number: { type: 'number', description: 'Project number' },
project_id: { type: 'string', description: 'Project node ID' },
project_public: { type: 'boolean', description: 'Project public status' },
// Search parameters
q: { type: 'string', description: 'Search query with qualifiers' },
sort: { type: 'string', description: 'Sort field' },
order: { type: 'string', description: 'Sort order (asc or desc)' },
// Commit parameters
author: { type: 'string', description: 'Author filter' },
committer: { type: 'string', description: 'Committer filter' },
since: { type: 'string', description: 'Date filter (since)' },
until: { type: 'string', description: 'Date filter (until)' },
// Gist parameters
gist_id: { type: 'string', description: 'Gist ID' },
description: { type: 'string', description: 'Description' },
files: { type: 'string', description: 'Files JSON object' },
gist_public: { type: 'boolean', description: 'Public gist status' },
username: { type: 'string', description: 'GitHub username' },
// Fork parameters
organization: { type: 'string', description: 'Target organization for fork' },
fork_name: { type: 'string', description: 'Custom name for fork' },
default_branch_only: { type: 'boolean', description: 'Fork only default branch' },
fork_sort: { type: 'string', description: 'Fork list sort field' },
// Milestone parameters
milestone_title: { type: 'string', description: 'Milestone title' },
milestone_description: { type: 'string', description: 'Milestone description' },
due_on: { type: 'string', description: 'Milestone due date' },
milestone_number: { type: 'number', description: 'Milestone number' },
milestone_state: { type: 'string', description: 'Milestone state filter' },
milestone_sort: { type: 'string', description: 'Milestone sort field' },
// Reaction parameters
reaction_content: { type: 'string', description: 'Reaction type' },
reaction_id: { type: 'number', description: 'Reaction ID' },
// Pagination parameters
page: { type: 'number', description: 'Page number for pagination' },
},
outputs: {
content: { type: 'string', description: 'Response content' },

View File

@@ -25,6 +25,11 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
{ label: 'Create Event', id: 'create' },
{ label: 'List Events', id: 'list' },
{ label: 'Get Event', id: 'get' },
{ label: 'Update Event', id: 'update' },
{ label: 'Delete Event', id: 'delete' },
{ label: 'Move Event', id: 'move' },
{ label: 'Get Recurring Instances', id: 'instances' },
{ label: 'List Calendars', id: 'list_calendars' },
{ label: 'Quick Add (Natural Language)', id: 'quick_add' },
{ label: 'Invite Attendees', id: 'invite' },
],
@@ -39,7 +44,7 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
requiredScopes: ['https://www.googleapis.com/auth/calendar'],
placeholder: 'Select Google Calendar account',
},
// Calendar selector (basic mode)
// Calendar selector (basic mode) - not needed for list_calendars
{
id: 'calendarId',
title: 'Calendar',
@@ -50,8 +55,9 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
placeholder: 'Select calendar',
dependsOn: ['credential'],
mode: 'basic',
condition: { field: 'operation', value: 'list_calendars', not: true },
},
// Manual calendar ID input (advanced mode)
// Manual calendar ID input (advanced mode) - not needed for list_calendars
{
id: 'manualCalendarId',
title: 'Calendar ID',
@@ -59,6 +65,7 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
canonicalParamId: 'calendarId',
placeholder: 'Enter calendar ID (e.g., primary or calendar@gmail.com)',
mode: 'advanced',
condition: { field: 'operation', value: 'list_calendars', not: true },
},
// Create Event Fields
@@ -204,10 +211,179 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
title: 'Event ID',
type: 'short-input',
placeholder: 'Event ID',
condition: { field: 'operation', value: ['get', 'invite'] },
condition: {
field: 'operation',
value: ['get', 'update', 'delete', 'move', 'instances', 'invite'],
},
required: true,
},
// Update Event Fields
{
id: 'summary',
title: 'New Event Title',
type: 'short-input',
placeholder: 'Updated meeting title',
condition: { field: 'operation', value: 'update' },
wandConfig: {
enabled: true,
prompt: `Generate a clear, descriptive calendar event title based on the user's request.
The title should be concise but informative about the event's purpose.
Return ONLY the event title - no explanations, no extra text.`,
placeholder: 'Describe the new event title...',
},
},
{
id: 'description',
title: 'New Description',
type: 'long-input',
placeholder: 'Updated event description',
condition: { field: 'operation', value: 'update' },
wandConfig: {
enabled: true,
prompt: `Generate a helpful calendar event description based on the user's request.
Include relevant details like:
- Purpose of the event
- Agenda items
- Preparation notes
- Links or resources
Return ONLY the description - no explanations, no extra text.`,
placeholder: 'Describe the new event details...',
},
},
{
id: 'location',
title: 'New Location',
type: 'short-input',
placeholder: 'Updated location',
condition: { field: 'operation', value: 'update' },
},
{
id: 'startDateTime',
title: 'New Start Date & Time',
type: 'short-input',
placeholder: '2025-06-03T10:00:00-08:00',
condition: { field: 'operation', value: 'update' },
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp with timezone offset based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SS+HH:MM or YYYY-MM-DDTHH:MM:SS-HH:MM
Examples:
- "tomorrow at 2pm" -> Calculate tomorrow's date at 14:00:00 with local timezone offset
- "next Monday at 9am" -> Calculate next Monday at 09:00:00 with local timezone offset
- "in 2 hours" -> Calculate current time + 2 hours with local timezone offset
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the new start time (e.g., "tomorrow at 2pm")...',
generationType: 'timestamp',
},
},
{
id: 'endDateTime',
title: 'New End Date & Time',
type: 'short-input',
placeholder: '2025-06-03T11:00:00-08:00',
condition: { field: 'operation', value: 'update' },
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp with timezone offset based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SS+HH:MM or YYYY-MM-DDTHH:MM:SS-HH:MM
Examples:
- "tomorrow at 3pm" -> Calculate tomorrow's date at 15:00:00 with local timezone offset
- "1 hour after start" -> Calculate start time + 1 hour with local timezone offset
- "next Monday at 5pm" -> Calculate next Monday at 17:00:00 with local timezone offset
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the new end time (e.g., "tomorrow at 3pm")...',
generationType: 'timestamp',
},
},
{
id: 'attendees',
title: 'New Attendees (comma-separated emails)',
type: 'short-input',
placeholder: 'john@example.com, jane@example.com',
condition: { field: 'operation', value: 'update' },
},
// Move Event Fields
{
id: 'destinationCalendarId',
title: 'Destination Calendar ID',
type: 'short-input',
placeholder: 'destination@group.calendar.google.com',
condition: { field: 'operation', value: 'move' },
required: true,
},
// Instances Fields
{
id: 'timeMin',
title: 'Start Time Filter',
type: 'short-input',
placeholder: '2025-06-03T00:00:00Z',
condition: { field: 'operation', value: 'instances' },
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp in UTC based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "today" -> Calculate today's date at 00:00:00Z
- "yesterday" -> Calculate yesterday's date at 00:00:00Z
- "last week" -> Calculate 7 days ago at 00:00:00Z
- "beginning of this month" -> Calculate the first day of current month at 00:00:00Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the start of time range (e.g., "today", "last week")...',
generationType: 'timestamp',
},
},
{
id: 'timeMax',
title: 'End Time Filter',
type: 'short-input',
placeholder: '2025-06-04T00:00:00Z',
condition: { field: 'operation', value: 'instances' },
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp in UTC based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "tomorrow" -> Calculate tomorrow's date at 00:00:00Z
- "end of today" -> Calculate today's date at 23:59:59Z
- "next week" -> Calculate 7 days from now at 00:00:00Z
- "end of this month" -> Calculate the last day of current month at 23:59:59Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the end of time range (e.g., "tomorrow", "end of this week")...',
generationType: 'timestamp',
},
},
{
id: 'maxResults',
title: 'Max Results',
type: 'short-input',
placeholder: '250',
condition: { field: 'operation', value: ['instances', 'list_calendars'] },
},
// List Calendars Fields
{
id: 'minAccessRole',
title: 'Minimum Access Role',
type: 'dropdown',
condition: { field: 'operation', value: 'list_calendars' },
options: [
{ label: 'Any Role', id: '' },
{ label: 'Free/Busy Reader', id: 'freeBusyReader' },
{ label: 'Reader', id: 'reader' },
{ label: 'Writer', id: 'writer' },
{ label: 'Owner', id: 'owner' },
],
},
// Invite Attendees Fields
{
id: 'attendees',
@@ -262,14 +438,14 @@ Return ONLY the natural language event text - no explanations.`,
required: true,
},
// Notification setting (for create, quick_add, invite)
// Notification setting (for create, update, delete, move, quick_add, invite)
{
id: 'sendUpdates',
title: 'Send Email Notifications',
type: 'dropdown',
condition: {
field: 'operation',
value: ['create', 'quick_add', 'invite'],
value: ['create', 'update', 'delete', 'move', 'quick_add', 'invite'],
},
options: [
{ label: 'All attendees', id: 'all' },
@@ -283,6 +459,11 @@ Return ONLY the natural language event text - no explanations.`,
'google_calendar_create',
'google_calendar_list',
'google_calendar_get',
'google_calendar_update',
'google_calendar_delete',
'google_calendar_move',
'google_calendar_instances',
'google_calendar_list_calendars',
'google_calendar_quick_add',
'google_calendar_invite',
],
@@ -295,6 +476,16 @@ Return ONLY the natural language event text - no explanations.`,
return 'google_calendar_list'
case 'get':
return 'google_calendar_get'
case 'update':
return 'google_calendar_update'
case 'delete':
return 'google_calendar_delete'
case 'move':
return 'google_calendar_move'
case 'instances':
return 'google_calendar_instances'
case 'list_calendars':
return 'google_calendar_list_calendars'
case 'quick_add':
return 'google_calendar_quick_add'
case 'invite':
@@ -341,10 +532,23 @@ Return ONLY the natural language event text - no explanations.`,
}
// Set default sendUpdates to 'all' if not specified for operations that support it
if (['create', 'quick_add', 'invite'].includes(operation) && !processedParams.sendUpdates) {
if (
['create', 'update', 'delete', 'move', 'quick_add', 'invite'].includes(operation) &&
!processedParams.sendUpdates
) {
processedParams.sendUpdates = 'all'
}
// Convert maxResults to number if provided
if (processedParams.maxResults && typeof processedParams.maxResults === 'string') {
processedParams.maxResults = Number.parseInt(processedParams.maxResults, 10)
}
// Remove empty minAccessRole
if (processedParams.minAccessRole === '') {
processedParams.minAccessRole = undefined
}
return {
credential,
...processedParams,
@@ -358,7 +562,7 @@ Return ONLY the natural language event text - no explanations.`,
calendarId: { type: 'string', description: 'Calendar identifier' },
manualCalendarId: { type: 'string', description: 'Manual calendar identifier' },
// Create operation inputs
// Create/Update operation inputs
summary: { type: 'string', description: 'Event title' },
description: { type: 'string', description: 'Event description' },
location: { type: 'string', description: 'Event location' },
@@ -366,13 +570,20 @@ Return ONLY the natural language event text - no explanations.`,
endDateTime: { type: 'string', description: 'Event end time' },
attendees: { type: 'string', description: 'Attendee email list' },
// List operation inputs
// List/Instances operation inputs
timeMin: { type: 'string', description: 'Start time filter' },
timeMax: { type: 'string', description: 'End time filter' },
maxResults: { type: 'string', description: 'Maximum number of results' },
// Get/Invite operation inputs
// Get/Update/Delete/Move/Instances/Invite operation inputs
eventId: { type: 'string', description: 'Event identifier' },
// Move operation inputs
destinationCalendarId: { type: 'string', description: 'Destination calendar ID' },
// List Calendars operation inputs
minAccessRole: { type: 'string', description: 'Minimum access role filter' },
// Quick add inputs
text: { type: 'string', description: 'Natural language event' },
@@ -384,7 +595,7 @@ Return ONLY the natural language event text - no explanations.`,
},
outputs: {
content: { type: 'string', description: 'Operation response content' },
metadata: { type: 'json', description: 'Event metadata' },
metadata: { type: 'json', description: 'Event or calendar metadata' },
},
}
@@ -399,6 +610,11 @@ export const GoogleCalendarV2Block: BlockConfig<GoogleCalendarResponse> = {
'google_calendar_create_v2',
'google_calendar_list_v2',
'google_calendar_get_v2',
'google_calendar_update_v2',
'google_calendar_delete_v2',
'google_calendar_move_v2',
'google_calendar_instances_v2',
'google_calendar_list_calendars_v2',
'google_calendar_quick_add_v2',
'google_calendar_invite_v2',
],
@@ -413,6 +629,7 @@ export const GoogleCalendarV2Block: BlockConfig<GoogleCalendarResponse> = {
},
},
outputs: {
// Event outputs (create, get, update, move, quick_add, invite)
id: { type: 'string', description: 'Event ID' },
htmlLink: { type: 'string', description: 'Event link' },
status: { type: 'string', description: 'Event status' },
@@ -424,9 +641,17 @@ export const GoogleCalendarV2Block: BlockConfig<GoogleCalendarResponse> = {
attendees: { type: 'json', description: 'Event attendees' },
creator: { type: 'json', description: 'Event creator' },
organizer: { type: 'json', description: 'Event organizer' },
// List events outputs
events: { type: 'json', description: 'List of events (list operation)' },
// Delete outputs
eventId: { type: 'string', description: 'Deleted event ID' },
deleted: { type: 'boolean', description: 'Whether deletion was successful' },
// Instances outputs
instances: { type: 'json', description: 'List of recurring event instances' },
// List calendars outputs
calendars: { type: 'json', description: 'List of calendars' },
// Common outputs
nextPageToken: { type: 'string', description: 'Next page token' },
nextSyncToken: { type: 'string', description: 'Next sync token' },
timeZone: { type: 'string', description: 'Calendar time zone' },
},
}

View File

@@ -6,9 +6,10 @@ import type { GoogleDriveResponse } from '@/tools/google_drive/types'
export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
type: 'google_drive',
name: 'Google Drive',
description: 'Create, upload, and list files',
description: 'Manage files, folders, and permissions',
authMode: AuthMode.OAuth,
longDescription: 'Integrate Google Drive into the workflow. Can create, upload, and list files.',
longDescription:
'Integrate Google Drive into the workflow. Can create, upload, download, copy, move, delete, share files and manage permissions.',
docsLink: 'https://docs.sim.ai/tools/google_drive',
category: 'tools',
bgColor: '#E0E0E0',
@@ -20,13 +21,23 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'List Files', id: 'list' },
{ label: 'Get File Info', id: 'get_file' },
{ label: 'Create Folder', id: 'create_folder' },
{ label: 'Create File', id: 'create_file' },
{ label: 'Upload File', id: 'upload' },
{ label: 'Download File', id: 'download' },
{ label: 'List Files', id: 'list' },
{ label: 'Copy File', id: 'copy' },
{ label: 'Update File', id: 'update' },
{ label: 'Move to Trash', id: 'trash' },
{ label: 'Restore from Trash', id: 'untrash' },
{ label: 'Delete Permanently', id: 'delete' },
{ label: 'Share File', id: 'share' },
{ label: 'Remove Sharing', id: 'unshare' },
{ label: 'List Permissions', id: 'list_permissions' },
{ label: 'Get Drive Info', id: 'get_about' },
],
value: () => 'create_folder',
value: () => 'list',
},
// Google Drive Credentials
{
@@ -326,26 +337,453 @@ Return ONLY the query string - no explanations, no quotes around the whole thing
placeholder: 'Optional: Override the filename',
condition: { field: 'operation', value: 'download' },
},
// Get File Info Fields
{
id: 'fileSelector',
title: 'Select File',
type: 'file-selector',
canonicalParamId: 'fileId',
serviceId: 'google-drive',
requiredScopes: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive',
],
placeholder: 'Select a file to get info for',
mode: 'basic',
dependsOn: ['credential'],
condition: { field: 'operation', value: 'get_file' },
},
{
id: 'manualFileId',
title: 'File ID',
type: 'short-input',
canonicalParamId: 'fileId',
placeholder: 'Enter file ID',
mode: 'advanced',
condition: { field: 'operation', value: 'get_file' },
required: true,
},
// Copy File Fields
{
id: 'fileSelector',
title: 'Select File to Copy',
type: 'file-selector',
canonicalParamId: 'fileId',
serviceId: 'google-drive',
requiredScopes: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive',
],
placeholder: 'Select a file to copy',
mode: 'basic',
dependsOn: ['credential'],
condition: { field: 'operation', value: 'copy' },
},
{
id: 'manualFileId',
title: 'File ID',
type: 'short-input',
canonicalParamId: 'fileId',
placeholder: 'Enter file ID to copy',
mode: 'advanced',
condition: { field: 'operation', value: 'copy' },
required: true,
},
{
id: 'newName',
title: 'New File Name',
type: 'short-input',
placeholder: 'Name for the copy (optional)',
condition: { field: 'operation', value: 'copy' },
},
{
id: 'folderSelector',
title: 'Destination Folder',
type: 'file-selector',
canonicalParamId: 'destinationFolderId',
serviceId: 'google-drive',
requiredScopes: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive',
],
mimeType: 'application/vnd.google-apps.folder',
placeholder: 'Select destination folder (optional)',
mode: 'basic',
dependsOn: ['credential'],
condition: { field: 'operation', value: 'copy' },
},
{
id: 'manualDestinationFolderId',
title: 'Destination Folder ID',
type: 'short-input',
canonicalParamId: 'destinationFolderId',
placeholder: 'Enter destination folder ID (optional)',
mode: 'advanced',
condition: { field: 'operation', value: 'copy' },
},
// Update File Fields
{
id: 'fileSelector',
title: 'Select File to Update',
type: 'file-selector',
canonicalParamId: 'fileId',
serviceId: 'google-drive',
requiredScopes: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive',
],
placeholder: 'Select a file to update',
mode: 'basic',
dependsOn: ['credential'],
condition: { field: 'operation', value: 'update' },
},
{
id: 'manualFileId',
title: 'File ID',
type: 'short-input',
canonicalParamId: 'fileId',
placeholder: 'Enter file ID to update',
mode: 'advanced',
condition: { field: 'operation', value: 'update' },
required: true,
},
{
id: 'name',
title: 'New Name',
type: 'short-input',
placeholder: 'New name for the file (optional)',
condition: { field: 'operation', value: 'update' },
},
{
id: 'description',
title: 'Description',
type: 'long-input',
placeholder: 'New description for the file (optional)',
condition: { field: 'operation', value: 'update' },
wandConfig: {
enabled: true,
prompt: `Generate a clear, informative file description based on the user's input.
The description should help users understand the file's purpose and contents.
Keep it concise but comprehensive - typically 1-3 sentences.
Return ONLY the description text - no explanations, no quotes, no extra text.`,
placeholder: 'Describe what this file is about...',
},
},
{
id: 'starred',
title: 'Starred',
type: 'dropdown',
options: [
{ label: 'No Change', id: '' },
{ label: 'Star', id: 'true' },
{ label: 'Unstar', id: 'false' },
],
placeholder: 'Star or unstar the file',
condition: { field: 'operation', value: 'update' },
},
{
id: 'addParents',
title: 'Add to Folders',
type: 'short-input',
placeholder: 'Comma-separated folder IDs to add file to',
mode: 'advanced',
condition: { field: 'operation', value: 'update' },
},
{
id: 'removeParents',
title: 'Remove from Folders',
type: 'short-input',
placeholder: 'Comma-separated folder IDs to remove file from',
mode: 'advanced',
condition: { field: 'operation', value: 'update' },
},
// Trash File Fields
{
id: 'fileSelector',
title: 'Select File to Trash',
type: 'file-selector',
canonicalParamId: 'fileId',
serviceId: 'google-drive',
requiredScopes: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive',
],
placeholder: 'Select a file to move to trash',
mode: 'basic',
dependsOn: ['credential'],
condition: { field: 'operation', value: 'trash' },
},
{
id: 'manualFileId',
title: 'File ID',
type: 'short-input',
canonicalParamId: 'fileId',
placeholder: 'Enter file ID to trash',
mode: 'advanced',
condition: { field: 'operation', value: 'trash' },
required: true,
},
// Untrash File Fields
{
id: 'manualFileId',
title: 'File ID',
type: 'short-input',
canonicalParamId: 'fileId',
placeholder: 'Enter file ID to restore from trash',
condition: { field: 'operation', value: 'untrash' },
required: true,
},
// Delete File Fields
{
id: 'fileSelector',
title: 'Select File to Delete',
type: 'file-selector',
canonicalParamId: 'fileId',
serviceId: 'google-drive',
requiredScopes: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive',
],
placeholder: 'Select a file to permanently delete',
mode: 'basic',
dependsOn: ['credential'],
condition: { field: 'operation', value: 'delete' },
},
{
id: 'manualFileId',
title: 'File ID',
type: 'short-input',
canonicalParamId: 'fileId',
placeholder: 'Enter file ID to permanently delete',
mode: 'advanced',
condition: { field: 'operation', value: 'delete' },
required: true,
},
// Share File Fields
{
id: 'fileSelector',
title: 'Select File to Share',
type: 'file-selector',
canonicalParamId: 'fileId',
serviceId: 'google-drive',
requiredScopes: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive',
],
placeholder: 'Select a file to share',
mode: 'basic',
dependsOn: ['credential'],
condition: { field: 'operation', value: 'share' },
},
{
id: 'manualFileId',
title: 'File ID',
type: 'short-input',
canonicalParamId: 'fileId',
placeholder: 'Enter file ID to share',
mode: 'advanced',
condition: { field: 'operation', value: 'share' },
required: true,
},
{
id: 'shareType',
title: 'Share With',
type: 'dropdown',
options: [
{ label: 'User (email)', id: 'user' },
{ label: 'Group (email)', id: 'group' },
{ label: 'Domain', id: 'domain' },
{ label: 'Anyone with link', id: 'anyone' },
],
placeholder: 'Who to share with',
condition: { field: 'operation', value: 'share' },
required: true,
},
{
id: 'role',
title: 'Permission Level',
type: 'dropdown',
options: [
{ label: 'Viewer (read only)', id: 'reader' },
{ label: 'Commenter (view & comment)', id: 'commenter' },
{ label: 'Editor (can edit)', id: 'writer' },
{ label: 'Transfer Ownership', id: 'owner' },
],
placeholder: 'Permission level',
condition: { field: 'operation', value: 'share' },
required: true,
},
{
id: 'email',
title: 'Email Address',
type: 'short-input',
placeholder: 'Email of user or group to share with',
condition: {
field: 'operation',
value: 'share',
and: { field: 'shareType', value: ['user', 'group'] },
},
required: true,
},
{
id: 'domain',
title: 'Domain',
type: 'short-input',
placeholder: 'Domain to share with (e.g., example.com)',
condition: {
field: 'operation',
value: 'share',
and: { field: 'shareType', value: 'domain' },
},
required: true,
},
{
id: 'sendNotification',
title: 'Send Notification',
type: 'dropdown',
options: [
{ label: 'Yes (default)', id: 'true' },
{ label: 'No', id: 'false' },
],
placeholder: 'Send email notification',
condition: {
field: 'operation',
value: 'share',
and: { field: 'shareType', value: ['user', 'group'] },
},
},
{
id: 'emailMessage',
title: 'Custom Message',
type: 'long-input',
placeholder: 'Custom message for the notification email (optional)',
condition: {
field: 'operation',
value: 'share',
and: { field: 'shareType', value: ['user', 'group'] },
},
wandConfig: {
enabled: true,
prompt: `Generate a professional, friendly sharing notification message based on the user's input.
The message should clearly explain why the file is being shared and any relevant context.
Keep it concise and appropriate for a business email - typically 2-4 sentences.
Return ONLY the message text - no subject line, no greetings/signatures, no extra formatting.`,
placeholder: 'Describe why you are sharing this file...',
},
},
// Unshare (Remove Permission) Fields
{
id: 'fileSelector',
title: 'Select File',
type: 'file-selector',
canonicalParamId: 'fileId',
serviceId: 'google-drive',
requiredScopes: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive',
],
placeholder: 'Select a file to remove sharing from',
mode: 'basic',
dependsOn: ['credential'],
condition: { field: 'operation', value: 'unshare' },
},
{
id: 'manualFileId',
title: 'File ID',
type: 'short-input',
canonicalParamId: 'fileId',
placeholder: 'Enter file ID',
mode: 'advanced',
condition: { field: 'operation', value: 'unshare' },
required: true,
},
{
id: 'permissionId',
title: 'Permission ID',
type: 'short-input',
placeholder: 'Permission ID to remove (use List Permissions to find)',
condition: { field: 'operation', value: 'unshare' },
required: true,
},
// List Permissions Fields
{
id: 'fileSelector',
title: 'Select File',
type: 'file-selector',
canonicalParamId: 'fileId',
serviceId: 'google-drive',
requiredScopes: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive',
],
placeholder: 'Select a file to list permissions for',
mode: 'basic',
dependsOn: ['credential'],
condition: { field: 'operation', value: 'list_permissions' },
},
{
id: 'manualFileId',
title: 'File ID',
type: 'short-input',
canonicalParamId: 'fileId',
placeholder: 'Enter file ID',
mode: 'advanced',
condition: { field: 'operation', value: 'list_permissions' },
required: true,
},
// Get Drive Info has no additional fields (just needs credential)
],
tools: {
access: [
'google_drive_upload',
'google_drive_create_folder',
'google_drive_download',
'google_drive_list',
'google_drive_get_file',
'google_drive_create_folder',
'google_drive_upload',
'google_drive_download',
'google_drive_copy',
'google_drive_update',
'google_drive_trash',
'google_drive_untrash',
'google_drive_delete',
'google_drive_share',
'google_drive_unshare',
'google_drive_list_permissions',
'google_drive_get_about',
],
config: {
tool: (params) => {
switch (params.operation) {
case 'list':
return 'google_drive_list'
case 'get_file':
return 'google_drive_get_file'
case 'create_folder':
return 'google_drive_create_folder'
case 'create_file':
case 'upload':
return 'google_drive_upload'
case 'create_folder':
return 'google_drive_create_folder'
case 'download':
return 'google_drive_download'
case 'list':
return 'google_drive_list'
case 'copy':
return 'google_drive_copy'
case 'update':
return 'google_drive_update'
case 'trash':
return 'google_drive_trash'
case 'untrash':
return 'google_drive_untrash'
case 'delete':
return 'google_drive_delete'
case 'share':
return 'google_drive_share'
case 'unshare':
return 'google_drive_unshare'
case 'list_permissions':
return 'google_drive_list_permissions'
case 'get_about':
return 'google_drive_get_about'
default:
throw new Error(`Invalid Google Drive operation: ${params.operation}`)
}
@@ -355,9 +793,13 @@ Return ONLY the query string - no explanations, no quotes around the whole thing
credential,
folderSelector,
manualFolderId,
manualDestinationFolderId,
fileSelector,
manualFileId,
mimeType,
shareType,
starred,
sendNotification,
...rest
} = params
@@ -367,12 +809,30 @@ Return ONLY the query string - no explanations, no quotes around the whole thing
// Use fileSelector if provided, otherwise use manualFileId
const effectiveFileId = (fileSelector || manualFileId || '').trim()
// Use folderSelector for destination or manualDestinationFolderId for copy operation
const effectiveDestinationFolderId =
params.operation === 'copy'
? (folderSelector || manualDestinationFolderId || '').trim()
: undefined
// Convert starred dropdown to boolean
const starredValue = starred === 'true' ? true : starred === 'false' ? false : undefined
// Convert sendNotification dropdown to boolean
const sendNotificationValue =
sendNotification === 'true' ? true : sendNotification === 'false' ? false : undefined
return {
credential,
folderId: effectiveFolderId || undefined,
fileId: effectiveFileId || undefined,
destinationFolderId: effectiveDestinationFolderId || undefined,
pageSize: rest.pageSize ? Number.parseInt(rest.pageSize as string, 10) : undefined,
mimeType: mimeType,
type: shareType, // Map shareType to type for share tool
starred: starredValue,
sendNotification: sendNotificationValue,
transferOwnership: rest.role === 'owner' ? true : undefined,
...rest,
}
},
@@ -381,22 +841,47 @@ Return ONLY the query string - no explanations, no quotes around the whole thing
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
credential: { type: 'string', description: 'Google Drive access token' },
// Upload and Create Folder operation inputs
// File selection inputs
fileSelector: { type: 'string', description: 'Selected file' },
manualFileId: { type: 'string', description: 'Manual file identifier' },
// Folder selection inputs
folderSelector: { type: 'string', description: 'Selected folder' },
manualFolderId: { type: 'string', description: 'Manual folder identifier' },
manualDestinationFolderId: { type: 'string', description: 'Destination folder for copy' },
// Upload and Create inputs
fileName: { type: 'string', description: 'File or folder name' },
file: { type: 'json', description: 'File to upload (UserFile object)' },
content: { type: 'string', description: 'Text content to upload' },
mimeType: { type: 'string', description: 'File MIME type or export format' },
// Download operation inputs
fileSelector: { type: 'string', description: 'Selected file to download' },
manualFileId: { type: 'string', description: 'Manual file identifier' },
// List operation inputs
folderSelector: { type: 'string', description: 'Selected folder' },
manualFolderId: { type: 'string', description: 'Manual folder identifier' },
query: { type: 'string', description: 'Search query' },
pageSize: { type: 'number', description: 'Results per page' },
// Copy operation inputs
newName: { type: 'string', description: 'New name for copied file' },
// Update operation inputs
name: { type: 'string', description: 'New name for file' },
description: { type: 'string', description: 'New description for file' },
starred: { type: 'string', description: 'Star or unstar the file' },
addParents: { type: 'string', description: 'Folder IDs to add file to' },
removeParents: { type: 'string', description: 'Folder IDs to remove file from' },
// Share operation inputs
shareType: { type: 'string', description: 'Type of sharing (user, group, domain, anyone)' },
role: { type: 'string', description: 'Permission role' },
email: { type: 'string', description: 'Email address to share with' },
domain: { type: 'string', description: 'Domain to share with' },
sendNotification: { type: 'string', description: 'Send notification email' },
emailMessage: { type: 'string', description: 'Custom notification message' },
// Unshare operation inputs
permissionId: { type: 'string', description: 'Permission ID to remove' },
},
outputs: {
file: { type: 'json', description: 'File data' },
files: { type: 'json', description: 'Files list' },
file: { type: 'json', description: 'File metadata' },
files: { type: 'json', description: 'List of files' },
permission: { type: 'json', description: 'Permission details' },
permissions: { type: 'json', description: 'List of permissions' },
user: { type: 'json', description: 'User information' },
storageQuota: { type: 'json', description: 'Storage quota information' },
deleted: { type: 'boolean', description: 'Whether file was deleted' },
removed: { type: 'boolean', description: 'Whether permission was removed' },
},
}

View File

@@ -1,86 +0,0 @@
import { GoogleFormsIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { getTrigger } from '@/triggers'
export const GoogleFormsBlock: BlockConfig = {
type: 'google_forms',
name: 'Google Forms',
description: 'Read responses from a Google Form',
longDescription:
'Integrate Google Forms into your workflow. Provide a Form ID to list responses, or specify a Response ID to fetch a single response. Requires OAuth.',
docsLink: 'https://docs.sim.ai/tools/google_forms',
category: 'tools',
bgColor: '#E0E0E0',
icon: GoogleFormsIcon,
subBlocks: [
{
id: 'credential',
title: 'Google Account',
type: 'oauth-input',
required: true,
serviceId: 'google-forms',
requiredScopes: [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/forms.responses.readonly',
],
placeholder: 'Select Google account',
},
{
id: 'formId',
title: 'Form ID',
type: 'short-input',
required: true,
placeholder: 'Enter the Google Form ID',
dependsOn: ['credential'],
},
{
id: 'responseId',
title: 'Response ID',
type: 'short-input',
placeholder: 'Enter a specific response ID',
},
{
id: 'pageSize',
title: 'Page Size',
type: 'short-input',
placeholder: 'Max responses to retrieve (default 5000)',
},
...getTrigger('google_forms_webhook').subBlocks,
],
tools: {
access: ['google_forms_get_responses'],
config: {
tool: () => 'google_forms_get_responses',
params: (params) => {
const { credential, formId, responseId, pageSize, ...rest } = params
const effectiveFormId = String(formId || '').trim()
if (!effectiveFormId) {
throw new Error('Form ID is required.')
}
return {
...rest,
formId: effectiveFormId,
responseId: responseId ? String(responseId).trim() : undefined,
pageSize: pageSize ? Number(pageSize) : undefined,
credential,
}
},
},
},
inputs: {
credential: { type: 'string', description: 'Google OAuth credential' },
formId: { type: 'string', description: 'Google Form ID' },
responseId: { type: 'string', description: 'Specific response ID' },
pageSize: { type: 'string', description: 'Max responses to retrieve (default 5000)' },
},
outputs: {
data: { type: 'json', description: 'Response or list of responses' },
},
triggers: {
enabled: true,
available: ['google_forms_webhook'],
},
}

View File

@@ -0,0 +1,329 @@
import { GoogleFormsIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { getTrigger } from '@/triggers'
export const GoogleFormsBlock: BlockConfig = {
type: 'google_forms',
name: 'Google Forms',
description: 'Manage Google Forms and responses',
longDescription:
'Integrate Google Forms into your workflow. Read form structure, get responses, create forms, update content, and manage notification watches.',
docsLink: 'https://docs.sim.ai/tools/google_forms',
category: 'tools',
bgColor: '#E0E0E0',
icon: GoogleFormsIcon,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Get Responses', id: 'get_responses' },
{ label: 'Get Form', id: 'get_form' },
{ label: 'Create Form', id: 'create_form' },
{ label: 'Batch Update', id: 'batch_update' },
{ label: 'Set Publish Settings', id: 'set_publish_settings' },
{ label: 'Create Watch', id: 'create_watch' },
{ label: 'List Watches', id: 'list_watches' },
{ label: 'Delete Watch', id: 'delete_watch' },
{ label: 'Renew Watch', id: 'renew_watch' },
],
value: () => 'get_responses',
},
{
id: 'credential',
title: 'Google Account',
type: 'oauth-input',
required: true,
serviceId: 'google-forms',
requiredScopes: [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/forms.body',
'https://www.googleapis.com/auth/forms.responses.readonly',
],
placeholder: 'Select Google account',
},
// Form ID - required for most operations except create_form
{
id: 'formId',
title: 'Form ID',
type: 'short-input',
required: true,
placeholder: 'Enter the Google Form ID',
dependsOn: ['credential'],
condition: {
field: 'operation',
value: 'create_form',
not: true,
},
},
// Get Responses specific fields
{
id: 'responseId',
title: 'Response ID',
type: 'short-input',
placeholder: 'Enter a specific response ID (optional)',
condition: { field: 'operation', value: 'get_responses' },
},
{
id: 'pageSize',
title: 'Page Size',
type: 'short-input',
placeholder: 'Max responses to retrieve (default 5000)',
condition: { field: 'operation', value: 'get_responses' },
},
// Create Form specific fields
{
id: 'title',
title: 'Form Title',
type: 'short-input',
required: true,
placeholder: 'Enter the form title',
condition: { field: 'operation', value: 'create_form' },
},
{
id: 'documentTitle',
title: 'Document Title',
type: 'short-input',
placeholder: 'Title visible in Drive (optional)',
condition: { field: 'operation', value: 'create_form' },
},
{
id: 'unpublished',
title: 'Create Unpublished',
type: 'switch',
condition: { field: 'operation', value: 'create_form' },
},
// Batch Update specific fields
{
id: 'requests',
title: 'Update Requests',
type: 'code',
placeholder: 'JSON array of update requests',
required: true,
condition: { field: 'operation', value: 'batch_update' },
wandConfig: {
enabled: true,
prompt: `Generate a Google Forms batchUpdate requests array based on the user's description.
The requests array can contain these operation types:
- updateFormInfo: Update form title/description. Structure: {updateFormInfo: {info: {title?, description?}, updateMask: "title,description"}}
- updateSettings: Update form settings. Structure: {updateSettings: {settings: {quizSettings?: {isQuiz: boolean}}, updateMask: "quizSettings.isQuiz"}}
- createItem: Add a question/section. Structure: {createItem: {item: {title, questionItem?: {question: {required?: boolean, choiceQuestion?: {type: "RADIO"|"CHECKBOX"|"DROP_DOWN", options: [{value: string}]}, textQuestion?: {paragraph?: boolean}, scaleQuestion?: {low: number, high: number}}}}, location: {index: number}}}
- updateItem: Modify existing item. Structure: {updateItem: {item: {...}, location: {index: number}, updateMask: "..."}}
- moveItem: Reorder item. Structure: {moveItem: {originalLocation: {index: number}, newLocation: {index: number}}}
- deleteItem: Remove item. Structure: {deleteItem: {location: {index: number}}}
Return ONLY a valid JSON array of request objects. No explanations.
Example for "Add a required multiple choice question about favorite color":
[{"createItem":{"item":{"title":"What is your favorite color?","questionItem":{"question":{"required":true,"choiceQuestion":{"type":"RADIO","options":[{"value":"Red"},{"value":"Blue"},{"value":"Green"}]}}}},"location":{"index":0}}}]`,
placeholder: 'Describe what you want to add or change in the form...',
},
},
{
id: 'includeFormInResponse',
title: 'Include Form in Response',
type: 'switch',
condition: { field: 'operation', value: 'batch_update' },
},
// Set Publish Settings specific fields
{
id: 'isPublished',
title: 'Published',
type: 'switch',
required: true,
condition: { field: 'operation', value: 'set_publish_settings' },
},
{
id: 'isAcceptingResponses',
title: 'Accepting Responses',
type: 'switch',
condition: { field: 'operation', value: 'set_publish_settings' },
},
// Watch specific fields
{
id: 'eventType',
title: 'Event Type',
type: 'dropdown',
options: [
{ label: 'Form Responses', id: 'RESPONSES' },
{ label: 'Form Schema Changes', id: 'SCHEMA' },
],
required: true,
condition: { field: 'operation', value: 'create_watch' },
},
{
id: 'topicName',
title: 'Pub/Sub Topic',
type: 'short-input',
required: true,
placeholder: 'projects/{project}/topics/{topic}',
condition: { field: 'operation', value: 'create_watch' },
},
{
id: 'watchId',
title: 'Watch ID',
type: 'short-input',
placeholder: 'Custom watch ID (optional)',
condition: { field: 'operation', value: ['create_watch', 'delete_watch', 'renew_watch'] },
required: { field: 'operation', value: ['delete_watch', 'renew_watch'] },
},
...getTrigger('google_forms_webhook').subBlocks,
],
tools: {
access: [
'google_forms_get_responses',
'google_forms_get_form',
'google_forms_create_form',
'google_forms_batch_update',
'google_forms_set_publish_settings',
'google_forms_create_watch',
'google_forms_list_watches',
'google_forms_delete_watch',
'google_forms_renew_watch',
],
config: {
tool: (params) => {
switch (params.operation) {
case 'get_responses':
return 'google_forms_get_responses'
case 'get_form':
return 'google_forms_get_form'
case 'create_form':
return 'google_forms_create_form'
case 'batch_update':
return 'google_forms_batch_update'
case 'set_publish_settings':
return 'google_forms_set_publish_settings'
case 'create_watch':
return 'google_forms_create_watch'
case 'list_watches':
return 'google_forms_list_watches'
case 'delete_watch':
return 'google_forms_delete_watch'
case 'renew_watch':
return 'google_forms_renew_watch'
default:
throw new Error(`Invalid Google Forms operation: ${params.operation}`)
}
},
params: (params) => {
const {
credential,
operation,
formId,
responseId,
pageSize,
title,
documentTitle,
unpublished,
requests,
includeFormInResponse,
isPublished,
isAcceptingResponses,
eventType,
topicName,
watchId,
...rest
} = params
const baseParams = { ...rest, credential }
const effectiveFormId = formId ? String(formId).trim() : undefined
switch (operation) {
case 'get_responses':
if (!effectiveFormId) throw new Error('Form ID is required.')
return {
...baseParams,
formId: effectiveFormId,
responseId: responseId ? String(responseId).trim() : undefined,
pageSize: pageSize ? Number(pageSize) : undefined,
}
case 'get_form':
case 'list_watches':
if (!effectiveFormId) throw new Error('Form ID is required.')
return { ...baseParams, formId: effectiveFormId }
case 'create_form':
if (!title) throw new Error('Form title is required.')
return {
...baseParams,
title: String(title).trim(),
documentTitle: documentTitle ? String(documentTitle).trim() : undefined,
unpublished: unpublished ?? false,
}
case 'batch_update':
if (!effectiveFormId) throw new Error('Form ID is required.')
if (!requests) throw new Error('Update requests are required.')
return {
...baseParams,
formId: effectiveFormId,
requests: typeof requests === 'string' ? JSON.parse(requests) : requests,
includeFormInResponse: includeFormInResponse ?? false,
}
case 'set_publish_settings':
if (!effectiveFormId) throw new Error('Form ID is required.')
return {
...baseParams,
formId: effectiveFormId,
isPublished: isPublished ?? false,
isAcceptingResponses: isAcceptingResponses,
}
case 'create_watch':
if (!effectiveFormId) throw new Error('Form ID is required.')
if (!eventType) throw new Error('Event type is required.')
if (!topicName) throw new Error('Pub/Sub topic is required.')
return {
...baseParams,
formId: effectiveFormId,
eventType: String(eventType),
topicName: String(topicName).trim(),
watchId: watchId ? String(watchId).trim() : undefined,
}
case 'delete_watch':
case 'renew_watch':
if (!effectiveFormId) throw new Error('Form ID is required.')
if (!watchId) throw new Error('Watch ID is required.')
return {
...baseParams,
formId: effectiveFormId,
watchId: String(watchId).trim(),
}
default:
throw new Error(`Invalid Google Forms operation: ${operation}`)
}
},
},
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
credential: { type: 'string', description: 'Google OAuth credential' },
formId: { type: 'string', description: 'Google Form ID' },
responseId: { type: 'string', description: 'Specific response ID' },
pageSize: { type: 'string', description: 'Max responses to retrieve' },
title: { type: 'string', description: 'Form title for creation' },
documentTitle: { type: 'string', description: 'Document title in Drive' },
unpublished: { type: 'boolean', description: 'Create as unpublished' },
requests: { type: 'json', description: 'Batch update requests' },
includeFormInResponse: { type: 'boolean', description: 'Include form in response' },
isPublished: { type: 'boolean', description: 'Form published state' },
isAcceptingResponses: { type: 'boolean', description: 'Form accepting responses' },
eventType: { type: 'string', description: 'Watch event type' },
topicName: { type: 'string', description: 'Pub/Sub topic name' },
watchId: { type: 'string', description: 'Watch ID' },
},
outputs: {
response: { type: 'json', description: 'Operation response data' },
formId: { type: 'string', description: 'Form ID' },
title: { type: 'string', description: 'Form title' },
responderUri: { type: 'string', description: 'Form responder URL' },
items: { type: 'json', description: 'Form items' },
responses: { type: 'json', description: 'Form responses' },
watches: { type: 'json', description: 'Form watches' },
},
triggers: {
enabled: true,
available: ['google_forms_webhook'],
},
}

View File

@@ -30,6 +30,11 @@ export const GoogleGroupsBlock: BlockConfig = {
{ label: 'Update Member Role', id: 'update_member' },
{ label: 'Remove Member', id: 'remove_member' },
{ label: 'Check Membership', id: 'has_member' },
{ label: 'List Aliases', id: 'list_aliases' },
{ label: 'Add Alias', id: 'add_alias' },
{ label: 'Remove Alias', id: 'remove_alias' },
{ label: 'Get Settings', id: 'get_settings' },
{ label: 'Update Settings', id: 'update_settings' },
],
value: () => 'list_groups',
},
@@ -112,10 +117,37 @@ Return ONLY the query string - no explanations, no quotes, no extra text.`,
'update_member',
'remove_member',
'has_member',
'list_aliases',
'add_alias',
'remove_alias',
],
},
},
{
id: 'groupEmail',
title: 'Group Email',
type: 'short-input',
placeholder: 'group@example.com',
required: true,
condition: {
field: 'operation',
value: ['get_settings', 'update_settings'],
},
},
{
id: 'alias',
title: 'Alias Email',
type: 'short-input',
placeholder: 'alias@example.com',
required: true,
condition: {
field: 'operation',
value: ['add_alias', 'remove_alias'],
},
},
{
id: 'email',
title: 'Group Email',
@@ -233,6 +265,11 @@ Return ONLY the description text - no explanations, no quotes, no extra text.`,
'google_groups_remove_member',
'google_groups_update_member',
'google_groups_has_member',
'google_groups_list_aliases',
'google_groups_add_alias',
'google_groups_remove_alias',
'google_groups_get_settings',
'google_groups_update_settings',
],
config: {
tool: (params) => {
@@ -259,6 +296,16 @@ Return ONLY the description text - no explanations, no quotes, no extra text.`,
return 'google_groups_remove_member'
case 'has_member':
return 'google_groups_has_member'
case 'list_aliases':
return 'google_groups_list_aliases'
case 'add_alias':
return 'google_groups_add_alias'
case 'remove_alias':
return 'google_groups_remove_alias'
case 'get_settings':
return 'google_groups_get_settings'
case 'update_settings':
return 'google_groups_update_settings'
default:
throw new Error(`Invalid Google Groups operation: ${params.operation}`)
}
@@ -330,6 +377,33 @@ Return ONLY the description text - no explanations, no quotes, no extra text.`,
groupKey: rest.groupKey,
memberKey: rest.memberKey,
}
case 'list_aliases':
return {
credential,
groupKey: rest.groupKey,
}
case 'add_alias':
return {
credential,
groupKey: rest.groupKey,
alias: rest.alias,
}
case 'remove_alias':
return {
credential,
groupKey: rest.groupKey,
alias: rest.alias,
}
case 'get_settings':
return {
credential,
groupEmail: rest.groupEmail,
}
case 'update_settings':
return {
credential,
groupEmail: rest.groupEmail,
}
default:
return { credential, ...rest }
}
@@ -353,6 +427,8 @@ Return ONLY the description text - no explanations, no quotes, no extra text.`,
memberEmail: { type: 'string', description: 'Email of member to add' },
role: { type: 'string', description: 'Member role (MEMBER, MANAGER, OWNER)' },
roles: { type: 'string', description: 'Filter by roles for list members' },
alias: { type: 'string', description: 'Alias email address' },
groupEmail: { type: 'string', description: 'Group email address for settings operations' },
},
outputs: {
groups: { type: 'json', description: 'Array of group objects (for list_groups)' },
@@ -362,5 +438,8 @@ Return ONLY the description text - no explanations, no quotes, no extra text.`,
isMember: { type: 'boolean', description: 'Membership check result (for has_member)' },
message: { type: 'string', description: 'Success message (for delete/remove operations)' },
nextPageToken: { type: 'string', description: 'Token for fetching next page of results' },
aliases: { type: 'json', description: 'Array of alias objects (for list_aliases)' },
settings: { type: 'json', description: 'Group settings object (for get/update_settings)' },
deleted: { type: 'boolean', description: 'Deletion result (for remove_alias)' },
},
}

View File

@@ -1,7 +1,7 @@
import { GoogleSheetsIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import type { GoogleSheetsResponse } from '@/tools/google_sheets/types'
import type { GoogleSheetsResponse, GoogleSheetsV2Response } from '@/tools/google_sheets/types'
// Legacy block - hidden from toolbar
export const GoogleSheetsBlock: BlockConfig<GoogleSheetsResponse> = {
@@ -284,3 +284,725 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
tableRange: { type: 'string', description: 'Table range' },
},
}
export const GoogleSheetsV2Block: BlockConfig<GoogleSheetsV2Response> = {
type: 'google_sheets_v2',
name: 'Google Sheets',
description: 'Read, write, and update data with sheet selection',
authMode: AuthMode.OAuth,
hideFromToolbar: false,
longDescription:
'Integrate Google Sheets into the workflow with explicit sheet selection. Can read, write, append, update, clear data, create spreadsheets, get spreadsheet info, and copy sheets.',
docsLink: 'https://docs.sim.ai/tools/google_sheets',
category: 'tools',
bgColor: '#E0E0E0',
icon: GoogleSheetsIcon,
subBlocks: [
// Operation selector
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Read Data', id: 'read' },
{ label: 'Write Data', id: 'write' },
{ label: 'Update Data', id: 'update' },
{ label: 'Append Data', id: 'append' },
{ label: 'Clear Data', id: 'clear' },
{ label: 'Get Spreadsheet Info', id: 'get_info' },
{ label: 'Create Spreadsheet', id: 'create' },
{ label: 'Batch Read', id: 'batch_get' },
{ label: 'Batch Update', id: 'batch_update' },
{ label: 'Batch Clear', id: 'batch_clear' },
{ label: 'Copy Sheet', id: 'copy_sheet' },
],
value: () => 'read',
},
// Google Sheets Credentials
{
id: 'credential',
title: 'Google Account',
type: 'oauth-input',
required: true,
serviceId: 'google-sheets',
requiredScopes: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive',
],
placeholder: 'Select Google account',
},
// Spreadsheet Selector (basic mode) - not for create operation
{
id: 'spreadsheetId',
title: 'Select Spreadsheet',
type: 'file-selector',
canonicalParamId: 'spreadsheetId',
serviceId: 'google-sheets',
requiredScopes: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive',
],
mimeType: 'application/vnd.google-apps.spreadsheet',
placeholder: 'Select a spreadsheet',
dependsOn: ['credential'],
mode: 'basic',
condition: { field: 'operation', value: 'create', not: true },
},
// Manual Spreadsheet ID (advanced mode) - not for create operation
{
id: 'manualSpreadsheetId',
title: 'Spreadsheet ID',
type: 'short-input',
canonicalParamId: 'spreadsheetId',
placeholder: 'ID of the spreadsheet (from URL)',
dependsOn: ['credential'],
mode: 'advanced',
condition: { field: 'operation', value: 'create', not: true },
},
// Sheet Name Selector (basic mode) - for operations that need sheet name
{
id: 'sheetName',
title: 'Sheet (Tab)',
type: 'sheet-selector',
canonicalParamId: 'sheetName',
serviceId: 'google-sheets',
placeholder: 'Select a sheet',
required: true,
dependsOn: { all: ['credential'], any: ['spreadsheetId', 'manualSpreadsheetId'] },
mode: 'basic',
condition: { field: 'operation', value: ['read', 'write', 'update', 'append', 'clear'] },
},
// Manual Sheet Name (advanced mode) - for operations that need sheet name
{
id: 'manualSheetName',
title: 'Sheet Name',
type: 'short-input',
canonicalParamId: 'sheetName',
placeholder: 'Name of the sheet/tab (e.g., Sheet1)',
required: true,
dependsOn: ['credential'],
mode: 'advanced',
condition: { field: 'operation', value: ['read', 'write', 'update', 'append', 'clear'] },
},
// Cell Range (optional for read/write/update/clear)
{
id: 'cellRange',
title: 'Cell Range',
type: 'short-input',
placeholder: 'Cell range (e.g., A1:D10). Defaults to A1 for write.',
condition: { field: 'operation', value: ['read', 'write', 'update', 'clear'] },
wandConfig: {
enabled: true,
prompt: `Generate a valid cell range based on the user's description.
### VALID FORMATS
- Single cell: A1
- Range: A1:D10
- Entire column: A:A
- Entire row: 1:1
- Multiple columns: A:D
- Multiple rows: 1:10
### RANGE RULES
- Column letters are uppercase: A, B, C, ... Z, AA, AB, etc.
- Row numbers start at 1 (not 0)
### EXAMPLES
- "first 100 rows" -> A1:Z100
- "cells A1 through C50" -> A1:C50
- "column A" -> A:A
- "just the headers row" -> 1:1
- "first cell" -> A1
Return ONLY the range string - no sheet name, no explanations, no quotes.`,
placeholder: 'Describe the range (e.g., "first 50 rows" or "column A")...',
},
},
// Write-specific Fields
{
id: 'values',
title: 'Values',
type: 'long-input',
placeholder:
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}])',
condition: { field: 'operation', value: 'write' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate Google Sheets data as a JSON array based on the user's description.
Format options:
1. Array of arrays: [["Header1", "Header2"], ["Value1", "Value2"]]
2. Array of objects: [{"column1": "value1", "column2": "value2"}]
Examples:
- "sales data with product and revenue columns" -> [["Product", "Revenue"], ["Widget A", 1500], ["Widget B", 2300]]
- "list of employees with name and email" -> [{"name": "John Doe", "email": "john@example.com"}, {"name": "Jane Smith", "email": "jane@example.com"}]
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the data you want to write...',
generationType: 'json-object',
},
},
{
id: 'valueInputOption',
title: 'Value Input Option',
type: 'dropdown',
options: [
{ label: 'User Entered (Parse formulas)', id: 'USER_ENTERED' },
{ label: "Raw (Don't parse formulas)", id: 'RAW' },
],
condition: { field: 'operation', value: ['write', 'batch_update'] },
},
// Update-specific Fields
{
id: 'values',
title: 'Values',
type: 'long-input',
placeholder:
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects',
condition: { field: 'operation', value: 'update' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate Google Sheets data as a JSON array based on the user's description.
Format options:
1. Array of arrays: [["Header1", "Header2"], ["Value1", "Value2"]]
2. Array of objects: [{"column1": "value1", "column2": "value2"}]
Examples:
- "update with new prices" -> [["Product", "Price"], ["Widget A", 29.99], ["Widget B", 49.99]]
- "quarterly targets" -> [{"Q1": 10000, "Q2": 12000, "Q3": 15000, "Q4": 18000}]
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the data you want to update...',
generationType: 'json-object',
},
},
// Append-specific Fields
{
id: 'values',
title: 'Values',
type: 'long-input',
placeholder:
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects',
condition: { field: 'operation', value: 'append' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate Google Sheets data as a JSON array based on the user's description.
Format options:
1. Array of arrays: [["Value1", "Value2"], ["Value3", "Value4"]]
2. Array of objects: [{"column1": "value1", "column2": "value2"}]
Examples:
- "add new sales record" -> [["2024-01-15", "Widget Pro", 5, 249.99]]
- "append customer info" -> [{"name": "Acme Corp", "contact": "John Smith", "status": "Active"}]
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the data you want to append...',
generationType: 'json-object',
},
},
{
id: 'insertDataOption',
title: 'Insert Data Option',
type: 'dropdown',
options: [
{ label: 'Insert Rows (Add new rows)', id: 'INSERT_ROWS' },
{ label: 'Overwrite (Add to existing data)', id: 'OVERWRITE' },
],
condition: { field: 'operation', value: 'append' },
},
// Create Spreadsheet Fields
{
id: 'title',
title: 'Spreadsheet Title',
type: 'short-input',
placeholder: 'Title for the new spreadsheet',
condition: { field: 'operation', value: 'create' },
required: true,
},
{
id: 'sheetTitles',
title: 'Sheet Names',
type: 'short-input',
placeholder: 'Comma-separated sheet names (e.g., Sheet1, Data, Summary)',
condition: { field: 'operation', value: 'create' },
},
// Batch Get Fields
{
id: 'ranges',
title: 'Ranges',
type: 'long-input',
placeholder:
'JSON array of ranges to read (e.g., ["Sheet1!A1:D10", "Sheet2!A1:B5"]). Include sheet name in each range.',
condition: { field: 'operation', value: 'batch_get' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a JSON array of Google Sheets ranges based on the user's description.
### FORMAT
Return a JSON array of range strings. Each range must include the sheet name.
Format: ["SheetName!CellRange", "SheetName!CellRange", ...]
### RANGE RULES
- Always include sheet name: Sheet1!A1:D10 (not just A1:D10)
- Sheet names with spaces must be quoted: 'My Sheet'!A1:B10
- Column letters are uppercase: A, B, C, ... Z, AA, AB
- Row numbers start at 1
- For entire column: Sheet1!A:A
- For entire row: Sheet1!1:1
### EXAMPLES
- "all data from Sales and the summary from Reports" -> ["Sales!A1:Z1000", "Reports!A1:D20"]
- "first 100 rows from Sheet1 and Sheet2" -> ["Sheet1!A1:Z100", "Sheet2!A1:Z100"]
- "headers from all three sheets" -> ["Sheet1!1:1", "Sheet2!1:1", "Sheet3!1:1"]
- "column A from Products and Orders" -> ["Products!A:A", "Orders!A:A"]
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
placeholder:
'Describe the ranges you want to read (e.g., "all data from Sales and summary from Reports")...',
generationType: 'json-object',
},
},
// Batch Update Fields
{
id: 'batchData',
title: 'Data',
type: 'long-input',
placeholder:
'JSON array of {range, values} objects (e.g., [{"range": "Sheet1!A1:B2", "values": [["A","B"],["C","D"]]}])',
condition: { field: 'operation', value: 'batch_update' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a JSON array of data updates for Google Sheets based on the user's description.
### FORMAT
Return a JSON array where each item has:
- "range": The target range including sheet name (e.g., "Sheet1!A1:B2")
- "values": A 2D array of values to write
Format: [{"range": "SheetName!CellRange", "values": [[row1], [row2], ...]}, ...]
### RANGE RULES
- Always include sheet name: Sheet1!A1:D10
- Sheet names with spaces must be quoted: 'My Sheet'!A1:B10
- The range size should match the values array dimensions
### EXAMPLES
- "set headers to Name, Email, Phone in Sheet1 and Status, Date in Sheet2" ->
[{"range": "Sheet1!A1:C1", "values": [["Name", "Email", "Phone"]]}, {"range": "Sheet2!A1:B1", "values": [["Status", "Date"]]}]
- "add totals row in A10 of Sales with formula" ->
[{"range": "Sales!A10:B10", "values": [["Total", "=SUM(B1:B9)"]]}]
- "update the first three rows of data in Products" ->
[{"range": "Products!A2:C4", "values": [["Widget", 10, 29.99], ["Gadget", 5, 49.99], ["Tool", 20, 9.99]]}]
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
placeholder:
'Describe the updates (e.g., "set headers in Sheet1 and add totals in Sheet2")...',
generationType: 'json-object',
},
},
// Batch Clear Fields
{
id: 'ranges',
title: 'Ranges to Clear',
type: 'long-input',
placeholder:
'JSON array of ranges to clear (e.g., ["Sheet1!A1:D10", "Sheet2!A1:B5"]). Include sheet name in each range.',
condition: { field: 'operation', value: 'batch_clear' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a JSON array of Google Sheets ranges to clear based on the user's description.
### FORMAT
Return a JSON array of range strings. Each range must include the sheet name.
Format: ["SheetName!CellRange", "SheetName!CellRange", ...]
### RANGE RULES
- Always include sheet name: Sheet1!A1:D10 (not just A1:D10)
- Sheet names with spaces must be quoted: 'My Sheet'!A1:B10
- Column letters are uppercase: A, B, C, ... Z, AA, AB
- Row numbers start at 1
- For entire column: Sheet1!A:A
- For entire row: Sheet1!1:1
- For entire sheet: Sheet1!A:ZZ (or use large range)
### EXAMPLES
- "clear all data from Sales and Reports" -> ["Sales!A1:ZZ10000", "Reports!A1:ZZ10000"]
- "clear rows 2-100 from Sheet1 and Sheet2, keep headers" -> ["Sheet1!A2:ZZ100", "Sheet2!A2:ZZ100"]
- "clear column A from Products and Orders" -> ["Products!A:A", "Orders!A:A"]
- "clear the summary section in Reports" -> ["Reports!A1:D20"]
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
placeholder:
'Describe the ranges to clear (e.g., "clear all data from Sales and Reports, keep headers")...',
generationType: 'json-object',
},
},
// Copy Sheet Fields
{
id: 'sheetId',
title: 'Sheet ID',
type: 'short-input',
placeholder: 'Numeric ID of the sheet to copy (use Get Spreadsheet Info to find IDs)',
condition: { field: 'operation', value: 'copy_sheet' },
required: true,
},
{
id: 'destinationSpreadsheetId',
title: 'Destination Spreadsheet ID',
type: 'short-input',
placeholder: 'ID of the spreadsheet to copy to',
condition: { field: 'operation', value: 'copy_sheet' },
required: true,
},
],
tools: {
access: [
'google_sheets_read_v2',
'google_sheets_write_v2',
'google_sheets_update_v2',
'google_sheets_append_v2',
'google_sheets_clear_v2',
'google_sheets_get_spreadsheet_v2',
'google_sheets_create_spreadsheet_v2',
'google_sheets_batch_get_v2',
'google_sheets_batch_update_v2',
'google_sheets_batch_clear_v2',
'google_sheets_copy_sheet_v2',
],
config: {
tool: (params) => {
switch (params.operation) {
case 'read':
return 'google_sheets_read_v2'
case 'write':
return 'google_sheets_write_v2'
case 'update':
return 'google_sheets_update_v2'
case 'append':
return 'google_sheets_append_v2'
case 'clear':
return 'google_sheets_clear_v2'
case 'get_info':
return 'google_sheets_get_spreadsheet_v2'
case 'create':
return 'google_sheets_create_spreadsheet_v2'
case 'batch_get':
return 'google_sheets_batch_get_v2'
case 'batch_update':
return 'google_sheets_batch_update_v2'
case 'batch_clear':
return 'google_sheets_batch_clear_v2'
case 'copy_sheet':
return 'google_sheets_copy_sheet_v2'
default:
throw new Error(`Invalid Google Sheets V2 operation: ${params.operation}`)
}
},
params: (params) => {
const {
credential,
values,
spreadsheetId,
manualSpreadsheetId,
sheetName,
manualSheetName,
cellRange,
title,
sheetTitles,
ranges,
batchData,
sheetId,
destinationSpreadsheetId,
...rest
} = params
const operation = params.operation as string
// Handle create operation
if (operation === 'create') {
const sheetTitlesArray = sheetTitles
? (sheetTitles as string).split(',').map((s: string) => s.trim())
: undefined
return {
title: (title as string)?.trim(),
sheetTitles: sheetTitlesArray,
credential,
}
}
const effectiveSpreadsheetId = (
(spreadsheetId || manualSpreadsheetId || '') as string
).trim()
if (!effectiveSpreadsheetId) {
throw new Error('Spreadsheet ID is required.')
}
// Handle get_info operation
if (operation === 'get_info') {
return {
spreadsheetId: effectiveSpreadsheetId,
credential,
}
}
// Handle batch_get operation
if (operation === 'batch_get') {
const parsedRanges = ranges ? JSON.parse(ranges as string) : []
return {
spreadsheetId: effectiveSpreadsheetId,
ranges: parsedRanges,
credential,
}
}
// Handle batch_update operation
if (operation === 'batch_update') {
const parsedData = batchData ? JSON.parse(batchData as string) : []
return {
...rest,
spreadsheetId: effectiveSpreadsheetId,
data: parsedData,
credential,
}
}
// Handle batch_clear operation
if (operation === 'batch_clear') {
const parsedRanges = ranges ? JSON.parse(ranges as string) : []
return {
spreadsheetId: effectiveSpreadsheetId,
ranges: parsedRanges,
credential,
}
}
// Handle copy_sheet operation
if (operation === 'copy_sheet') {
return {
sourceSpreadsheetId: effectiveSpreadsheetId,
sheetId: Number.parseInt(sheetId as string, 10),
destinationSpreadsheetId: (destinationSpreadsheetId as string)?.trim(),
credential,
}
}
// Handle read/write/update/append/clear operations (require sheet name)
const effectiveSheetName = ((sheetName || manualSheetName || '') as string).trim()
if (!effectiveSheetName) {
throw new Error('Sheet name is required. Please select or enter a sheet name.')
}
const parsedValues = values ? JSON.parse(values as string) : undefined
return {
...rest,
spreadsheetId: effectiveSpreadsheetId,
sheetName: effectiveSheetName,
cellRange: cellRange ? (cellRange as string).trim() : undefined,
values: parsedValues,
credential,
}
},
},
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
credential: { type: 'string', description: 'Google Sheets access token' },
spreadsheetId: { type: 'string', description: 'Spreadsheet identifier' },
manualSpreadsheetId: { type: 'string', description: 'Manual spreadsheet identifier' },
sheetName: { type: 'string', description: 'Name of the sheet/tab' },
manualSheetName: { type: 'string', description: 'Manual sheet name entry' },
cellRange: { type: 'string', description: 'Cell range (e.g., A1:D10)' },
values: { type: 'string', description: 'Cell values data' },
valueInputOption: { type: 'string', description: 'Value input option' },
insertDataOption: { type: 'string', description: 'Data insertion option' },
title: { type: 'string', description: 'Title for new spreadsheet' },
sheetTitles: { type: 'string', description: 'Comma-separated sheet names for new spreadsheet' },
ranges: { type: 'string', description: 'JSON array of ranges for batch operations' },
batchData: { type: 'string', description: 'JSON array of data for batch update' },
sheetId: { type: 'string', description: 'Numeric sheet ID for copy operation' },
destinationSpreadsheetId: {
type: 'string',
description: 'Destination spreadsheet ID for copy',
},
},
outputs: {
// Read outputs
sheetName: {
type: 'string',
description: 'Name of the sheet',
condition: { field: 'operation', value: ['read', 'clear'] },
},
range: {
type: 'string',
description: 'Range that was read',
condition: { field: 'operation', value: 'read' },
},
values: {
type: 'json',
description: 'Cell values as 2D array',
condition: { field: 'operation', value: 'read' },
},
// Write/Update/Append outputs
updatedRange: {
type: 'string',
description: 'Updated range',
condition: { field: 'operation', value: ['write', 'update', 'append'] },
},
updatedRows: {
type: 'number',
description: 'Updated rows count',
condition: { field: 'operation', value: ['write', 'update', 'append'] },
},
updatedColumns: {
type: 'number',
description: 'Updated columns count',
condition: { field: 'operation', value: ['write', 'update', 'append'] },
},
updatedCells: {
type: 'number',
description: 'Updated cells count',
condition: { field: 'operation', value: ['write', 'update', 'append'] },
},
tableRange: {
type: 'string',
description: 'Table range',
condition: { field: 'operation', value: 'append' },
},
// Clear outputs
clearedRange: {
type: 'string',
description: 'Range that was cleared',
condition: { field: 'operation', value: 'clear' },
},
// Get Info / Create / Batch outputs
spreadsheetId: {
type: 'string',
description: 'Spreadsheet ID',
condition: {
field: 'operation',
value: ['get_info', 'create', 'batch_get', 'batch_update', 'batch_clear'],
},
},
title: {
type: 'string',
description: 'Spreadsheet title (or copied sheet title for copy_sheet)',
condition: { field: 'operation', value: ['get_info', 'create', 'copy_sheet'] },
},
sheets: {
type: 'json',
description: 'List of sheets in the spreadsheet',
condition: { field: 'operation', value: ['get_info', 'create'] },
},
locale: {
type: 'string',
description: 'Spreadsheet locale',
condition: { field: 'operation', value: 'get_info' },
},
timeZone: {
type: 'string',
description: 'Spreadsheet time zone',
condition: { field: 'operation', value: 'get_info' },
},
spreadsheetUrl: {
type: 'string',
description: 'Spreadsheet URL',
condition: { field: 'operation', value: ['get_info', 'create'] },
},
// Batch Get outputs
valueRanges: {
type: 'json',
description: 'Array of value ranges read from the spreadsheet',
condition: { field: 'operation', value: 'batch_get' },
},
// Batch Update outputs
totalUpdatedRows: {
type: 'number',
description: 'Total rows updated',
condition: { field: 'operation', value: 'batch_update' },
},
totalUpdatedColumns: {
type: 'number',
description: 'Total columns updated',
condition: { field: 'operation', value: 'batch_update' },
},
totalUpdatedCells: {
type: 'number',
description: 'Total cells updated',
condition: { field: 'operation', value: 'batch_update' },
},
totalUpdatedSheets: {
type: 'number',
description: 'Total sheets updated',
condition: { field: 'operation', value: 'batch_update' },
},
responses: {
type: 'json',
description: 'Array of update responses for each range',
condition: { field: 'operation', value: 'batch_update' },
},
// Batch Clear outputs
clearedRanges: {
type: 'json',
description: 'Array of ranges that were cleared',
condition: { field: 'operation', value: 'batch_clear' },
},
// Copy Sheet outputs
sheetId: {
type: 'number',
description: 'ID of the copied sheet in the destination',
condition: { field: 'operation', value: 'copy_sheet' },
},
index: {
type: 'number',
description: 'Position/index of the copied sheet',
condition: { field: 'operation', value: 'copy_sheet' },
},
sheetType: {
type: 'string',
description: 'Type of the sheet (GRID, CHART, etc.)',
condition: { field: 'operation', value: 'copy_sheet' },
},
destinationSpreadsheetId: {
type: 'string',
description: 'ID of the destination spreadsheet',
condition: { field: 'operation', value: 'copy_sheet' },
},
destinationSpreadsheetUrl: {
type: 'string',
description: 'URL of the destination spreadsheet',
condition: { field: 'operation', value: 'copy_sheet' },
},
// Common metadata
metadata: {
type: 'json',
description: 'Spreadsheet metadata including ID and URL',
condition: {
field: 'operation',
value: [
'read',
'write',
'update',
'append',
'clear',
'batch_get',
'batch_update',
'batch_clear',
],
},
},
},
}

View File

@@ -1,360 +0,0 @@
import { GoogleSheetsIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import type { GoogleSheetsV2Response } from '@/tools/google_sheets/types'
export const GoogleSheetsV2Block: BlockConfig<GoogleSheetsV2Response> = {
type: 'google_sheets_v2',
name: 'Google Sheets',
description: 'Read, write, and update data with sheet selection',
authMode: AuthMode.OAuth,
hideFromToolbar: false,
longDescription:
'Integrate Google Sheets into the workflow with explicit sheet selection. Can read, write, append, and update data in specific sheets.',
docsLink: 'https://docs.sim.ai/tools/google_sheets',
category: 'tools',
bgColor: '#E0E0E0',
icon: GoogleSheetsIcon,
subBlocks: [
// Operation selector
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Read Data', id: 'read' },
{ label: 'Write Data', id: 'write' },
{ label: 'Update Data', id: 'update' },
{ label: 'Append Data', id: 'append' },
],
value: () => 'read',
},
// Google Sheets Credentials
{
id: 'credential',
title: 'Google Account',
type: 'oauth-input',
required: true,
serviceId: 'google-sheets',
requiredScopes: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive',
],
placeholder: 'Select Google account',
},
// Spreadsheet Selector (basic mode)
{
id: 'spreadsheetId',
title: 'Select Spreadsheet',
type: 'file-selector',
canonicalParamId: 'spreadsheetId',
serviceId: 'google-sheets',
requiredScopes: [
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive',
],
mimeType: 'application/vnd.google-apps.spreadsheet',
placeholder: 'Select a spreadsheet',
dependsOn: ['credential'],
mode: 'basic',
},
// Manual Spreadsheet ID (advanced mode)
{
id: 'manualSpreadsheetId',
title: 'Spreadsheet ID',
type: 'short-input',
canonicalParamId: 'spreadsheetId',
placeholder: 'ID of the spreadsheet (from URL)',
dependsOn: ['credential'],
mode: 'advanced',
},
// Sheet Name Selector (basic mode)
{
id: 'sheetName',
title: 'Sheet (Tab)',
type: 'sheet-selector',
canonicalParamId: 'sheetName',
serviceId: 'google-sheets',
placeholder: 'Select a sheet',
required: true,
dependsOn: { all: ['credential'], any: ['spreadsheetId', 'manualSpreadsheetId'] },
mode: 'basic',
},
// Manual Sheet Name (advanced mode)
{
id: 'manualSheetName',
title: 'Sheet Name',
type: 'short-input',
canonicalParamId: 'sheetName',
placeholder: 'Name of the sheet/tab (e.g., Sheet1)',
required: true,
dependsOn: ['credential'],
mode: 'advanced',
},
// Cell Range (optional for read/write/update)
{
id: 'cellRange',
title: 'Cell Range',
type: 'short-input',
placeholder: 'Cell range (e.g., A1:D10). Defaults to A1 for write.',
condition: { field: 'operation', value: ['read', 'write', 'update'] },
wandConfig: {
enabled: true,
prompt: `Generate a valid cell range based on the user's description.
### VALID FORMATS
- Single cell: A1
- Range: A1:D10
- Entire column: A:A
- Entire row: 1:1
- Multiple columns: A:D
- Multiple rows: 1:10
### RANGE RULES
- Column letters are uppercase: A, B, C, ... Z, AA, AB, etc.
- Row numbers start at 1 (not 0)
### EXAMPLES
- "first 100 rows" -> A1:Z100
- "cells A1 through C50" -> A1:C50
- "column A" -> A:A
- "just the headers row" -> 1:1
- "first cell" -> A1
Return ONLY the range string - no sheet name, no explanations, no quotes.`,
placeholder: 'Describe the range (e.g., "first 50 rows" or "column A")...',
},
},
// Write-specific Fields
{
id: 'values',
title: 'Values',
type: 'long-input',
placeholder:
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}])',
condition: { field: 'operation', value: 'write' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate Google Sheets data as a JSON array based on the user's description.
Format options:
1. Array of arrays: [["Header1", "Header2"], ["Value1", "Value2"]]
2. Array of objects: [{"column1": "value1", "column2": "value2"}]
Examples:
- "sales data with product and revenue columns" -> [["Product", "Revenue"], ["Widget A", 1500], ["Widget B", 2300]]
- "list of employees with name and email" -> [{"name": "John Doe", "email": "john@example.com"}, {"name": "Jane Smith", "email": "jane@example.com"}]
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the data you want to write...',
generationType: 'json-object',
},
},
{
id: 'valueInputOption',
title: 'Value Input Option',
type: 'dropdown',
options: [
{ label: 'User Entered (Parse formulas)', id: 'USER_ENTERED' },
{ label: "Raw (Don't parse formulas)", id: 'RAW' },
],
condition: { field: 'operation', value: 'write' },
},
// Update-specific Fields
{
id: 'values',
title: 'Values',
type: 'long-input',
placeholder:
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects',
condition: { field: 'operation', value: 'update' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate Google Sheets data as a JSON array based on the user's description.
Format options:
1. Array of arrays: [["Header1", "Header2"], ["Value1", "Value2"]]
2. Array of objects: [{"column1": "value1", "column2": "value2"}]
Examples:
- "update with new prices" -> [["Product", "Price"], ["Widget A", 29.99], ["Widget B", 49.99]]
- "quarterly targets" -> [{"Q1": 10000, "Q2": 12000, "Q3": 15000, "Q4": 18000}]
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the data you want to update...',
generationType: 'json-object',
},
},
{
id: 'valueInputOption',
title: 'Value Input Option',
type: 'dropdown',
options: [
{ label: 'User Entered (Parse formulas)', id: 'USER_ENTERED' },
{ label: "Raw (Don't parse formulas)", id: 'RAW' },
],
condition: { field: 'operation', value: 'update' },
},
// Append-specific Fields
{
id: 'values',
title: 'Values',
type: 'long-input',
placeholder:
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects',
condition: { field: 'operation', value: 'append' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate Google Sheets data as a JSON array based on the user's description.
Format options:
1. Array of arrays: [["Value1", "Value2"], ["Value3", "Value4"]]
2. Array of objects: [{"column1": "value1", "column2": "value2"}]
Examples:
- "add new sales record" -> [["2024-01-15", "Widget Pro", 5, 249.99]]
- "append customer info" -> [{"name": "Acme Corp", "contact": "John Smith", "status": "Active"}]
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the data you want to append...',
generationType: 'json-object',
},
},
{
id: 'valueInputOption',
title: 'Value Input Option',
type: 'dropdown',
options: [
{ label: 'User Entered (Parse formulas)', id: 'USER_ENTERED' },
{ label: "Raw (Don't parse formulas)", id: 'RAW' },
],
condition: { field: 'operation', value: 'append' },
},
{
id: 'insertDataOption',
title: 'Insert Data Option',
type: 'dropdown',
options: [
{ label: 'Insert Rows (Add new rows)', id: 'INSERT_ROWS' },
{ label: 'Overwrite (Add to existing data)', id: 'OVERWRITE' },
],
condition: { field: 'operation', value: 'append' },
},
],
tools: {
access: [
'google_sheets_read_v2',
'google_sheets_write_v2',
'google_sheets_update_v2',
'google_sheets_append_v2',
],
config: {
tool: (params) => {
switch (params.operation) {
case 'read':
return 'google_sheets_read_v2'
case 'write':
return 'google_sheets_write_v2'
case 'update':
return 'google_sheets_update_v2'
case 'append':
return 'google_sheets_append_v2'
default:
throw new Error(`Invalid Google Sheets V2 operation: ${params.operation}`)
}
},
params: (params) => {
const {
credential,
values,
spreadsheetId,
manualSpreadsheetId,
sheetName,
manualSheetName,
cellRange,
...rest
} = params
const parsedValues = values ? JSON.parse(values as string) : undefined
const effectiveSpreadsheetId = (spreadsheetId || manualSpreadsheetId || '').trim()
const effectiveSheetName = ((sheetName || manualSheetName || '') as string).trim()
if (!effectiveSpreadsheetId) {
throw new Error('Spreadsheet ID is required.')
}
if (!effectiveSheetName) {
throw new Error('Sheet name is required. Please select or enter a sheet name.')
}
return {
...rest,
spreadsheetId: effectiveSpreadsheetId,
sheetName: effectiveSheetName,
cellRange: cellRange ? (cellRange as string).trim() : undefined,
values: parsedValues,
credential,
}
},
},
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
credential: { type: 'string', description: 'Google Sheets access token' },
spreadsheetId: { type: 'string', description: 'Spreadsheet identifier' },
manualSpreadsheetId: { type: 'string', description: 'Manual spreadsheet identifier' },
sheetName: { type: 'string', description: 'Name of the sheet/tab' },
manualSheetName: { type: 'string', description: 'Manual sheet name entry' },
cellRange: { type: 'string', description: 'Cell range (e.g., A1:D10)' },
values: { type: 'string', description: 'Cell values data' },
valueInputOption: { type: 'string', description: 'Value input option' },
insertDataOption: { type: 'string', description: 'Data insertion option' },
},
outputs: {
sheetName: {
type: 'string',
description: 'Name of the sheet',
condition: { field: 'operation', value: 'read' },
},
range: {
type: 'string',
description: 'Range that was read',
condition: { field: 'operation', value: 'read' },
},
values: {
type: 'json',
description: 'Cell values as 2D array',
condition: { field: 'operation', value: 'read' },
},
updatedRange: {
type: 'string',
description: 'Updated range',
condition: { field: 'operation', value: ['write', 'update', 'append'] },
},
updatedRows: {
type: 'number',
description: 'Updated rows count',
condition: { field: 'operation', value: ['write', 'update', 'append'] },
},
updatedColumns: {
type: 'number',
description: 'Updated columns count',
condition: { field: 'operation', value: ['write', 'update', 'append'] },
},
updatedCells: {
type: 'number',
description: 'Updated cells count',
condition: { field: 'operation', value: ['write', 'update', 'append'] },
},
tableRange: {
type: 'string',
description: 'Table range',
condition: { field: 'operation', value: 'append' },
},
metadata: { type: 'json', description: 'Spreadsheet metadata including ID and URL' },
},
}

View File

@@ -9,7 +9,7 @@ export const GoogleSlidesBlock: BlockConfig<GoogleSlidesResponse> = {
description: 'Read, write, and create presentations',
authMode: AuthMode.OAuth,
longDescription:
'Integrate Google Slides into the workflow. Can read, write, create presentations, replace text, add slides, add images, and get thumbnails.',
'Integrate Google Slides into the workflow. Can read, write, create presentations, replace text, add slides, add images, get thumbnails, get page details, delete objects, duplicate objects, reorder slides, create tables, create shapes, and insert text.',
docsLink: 'https://docs.sim.ai/tools/google_slides',
category: 'tools',
bgColor: '#E0E0E0',
@@ -28,6 +28,13 @@ export const GoogleSlidesBlock: BlockConfig<GoogleSlidesResponse> = {
{ label: 'Add Slide', id: 'add_slide' },
{ label: 'Add Image', id: 'add_image' },
{ label: 'Get Thumbnail', id: 'get_thumbnail' },
{ label: 'Get Page', id: 'get_page' },
{ label: 'Delete Object', id: 'delete_object' },
{ label: 'Duplicate Object', id: 'duplicate_object' },
{ label: 'Reorder Slides', id: 'reorder_slides' },
{ label: 'Create Table', id: 'create_table' },
{ label: 'Create Shape', id: 'create_shape' },
{ label: 'Insert Text', id: 'insert_text' },
],
value: () => 'read',
},
@@ -58,7 +65,21 @@ export const GoogleSlidesBlock: BlockConfig<GoogleSlidesResponse> = {
mode: 'basic',
condition: {
field: 'operation',
value: ['read', 'write', 'replace_all_text', 'add_slide', 'add_image', 'get_thumbnail'],
value: [
'read',
'write',
'replace_all_text',
'add_slide',
'add_image',
'get_thumbnail',
'get_page',
'delete_object',
'duplicate_object',
'reorder_slides',
'create_table',
'create_shape',
'insert_text',
],
},
},
// Manual presentation ID input (advanced mode)
@@ -72,7 +93,21 @@ export const GoogleSlidesBlock: BlockConfig<GoogleSlidesResponse> = {
mode: 'advanced',
condition: {
field: 'operation',
value: ['read', 'write', 'replace_all_text', 'add_slide', 'add_image', 'get_thumbnail'],
value: [
'read',
'write',
'replace_all_text',
'add_slide',
'add_image',
'get_thumbnail',
'get_page',
'delete_object',
'duplicate_object',
'reorder_slides',
'create_table',
'create_shape',
'insert_text',
],
},
},
@@ -348,6 +383,213 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
condition: { field: 'operation', value: 'get_thumbnail' },
value: () => 'PNG',
},
// ========== Get Page Operation Fields ==========
{
id: 'getPageObjectId',
title: 'Page/Slide ID',
type: 'short-input',
placeholder: 'Object ID of the slide/page to retrieve',
condition: { field: 'operation', value: 'get_page' },
required: true,
},
// ========== Delete Object Operation Fields ==========
{
id: 'deleteObjectId',
title: 'Object ID',
type: 'short-input',
placeholder: 'Object ID of the element or slide to delete',
condition: { field: 'operation', value: 'delete_object' },
required: true,
},
// ========== Duplicate Object Operation Fields ==========
{
id: 'duplicateObjectId',
title: 'Object ID',
type: 'short-input',
placeholder: 'Object ID of the element or slide to duplicate',
condition: { field: 'operation', value: 'duplicate_object' },
required: true,
},
{
id: 'duplicateObjectIds',
title: 'Object ID Mappings',
type: 'long-input',
placeholder: 'JSON object: {"sourceId1":"newId1","sourceId2":"newId2"}',
condition: { field: 'operation', value: 'duplicate_object' },
mode: 'advanced',
},
// ========== Reorder Slides Operation Fields ==========
{
id: 'reorderSlideIds',
title: 'Slide IDs',
type: 'short-input',
placeholder: 'Comma-separated slide object IDs to move',
condition: { field: 'operation', value: 'reorder_slides' },
required: true,
},
{
id: 'reorderInsertionIndex',
title: 'New Position',
type: 'short-input',
placeholder: 'Zero-based index where slides should be moved',
condition: { field: 'operation', value: 'reorder_slides' },
required: true,
},
// ========== Create Table Operation Fields ==========
{
id: 'tablePageObjectId',
title: 'Slide ID',
type: 'short-input',
placeholder: 'Object ID of the slide to add the table to',
condition: { field: 'operation', value: 'create_table' },
required: true,
},
{
id: 'tableRows',
title: 'Rows',
type: 'short-input',
placeholder: 'Number of rows (minimum 1)',
condition: { field: 'operation', value: 'create_table' },
required: true,
},
{
id: 'tableColumns',
title: 'Columns',
type: 'short-input',
placeholder: 'Number of columns (minimum 1)',
condition: { field: 'operation', value: 'create_table' },
required: true,
},
{
id: 'tableWidth',
title: 'Width (points)',
type: 'short-input',
placeholder: 'Table width in points (default: 400)',
condition: { field: 'operation', value: 'create_table' },
},
{
id: 'tableHeight',
title: 'Height (points)',
type: 'short-input',
placeholder: 'Table height in points (default: 200)',
condition: { field: 'operation', value: 'create_table' },
},
{
id: 'tablePositionX',
title: 'X Position (points)',
type: 'short-input',
placeholder: 'X position from left (default: 100)',
condition: { field: 'operation', value: 'create_table' },
},
{
id: 'tablePositionY',
title: 'Y Position (points)',
type: 'short-input',
placeholder: 'Y position from top (default: 100)',
condition: { field: 'operation', value: 'create_table' },
},
// ========== Create Shape Operation Fields ==========
{
id: 'shapePageObjectId',
title: 'Slide ID',
type: 'short-input',
placeholder: 'Object ID of the slide to add the shape to',
condition: { field: 'operation', value: 'create_shape' },
required: true,
},
{
id: 'shapeType',
title: 'Shape Type',
type: 'dropdown',
options: [
{ label: 'Text Box', id: 'TEXT_BOX' },
{ label: 'Rectangle', id: 'RECTANGLE' },
{ label: 'Rounded Rectangle', id: 'ROUND_RECTANGLE' },
{ label: 'Ellipse', id: 'ELLIPSE' },
{ label: 'Triangle', id: 'TRIANGLE' },
{ label: 'Diamond', id: 'DIAMOND' },
{ label: 'Star (5 points)', id: 'STAR_5' },
{ label: 'Arrow (Right)', id: 'RIGHT_ARROW' },
{ label: 'Arrow (Left)', id: 'LEFT_ARROW' },
{ label: 'Arrow (Up)', id: 'UP_ARROW' },
{ label: 'Arrow (Down)', id: 'DOWN_ARROW' },
{ label: 'Heart', id: 'HEART' },
{ label: 'Cloud', id: 'CLOUD' },
{ label: 'Lightning Bolt', id: 'LIGHTNING_BOLT' },
],
condition: { field: 'operation', value: 'create_shape' },
value: () => 'RECTANGLE',
},
{
id: 'shapeWidth',
title: 'Width (points)',
type: 'short-input',
placeholder: 'Shape width in points (default: 200)',
condition: { field: 'operation', value: 'create_shape' },
},
{
id: 'shapeHeight',
title: 'Height (points)',
type: 'short-input',
placeholder: 'Shape height in points (default: 100)',
condition: { field: 'operation', value: 'create_shape' },
},
{
id: 'shapePositionX',
title: 'X Position (points)',
type: 'short-input',
placeholder: 'X position from left (default: 100)',
condition: { field: 'operation', value: 'create_shape' },
},
{
id: 'shapePositionY',
title: 'Y Position (points)',
type: 'short-input',
placeholder: 'Y position from top (default: 100)',
condition: { field: 'operation', value: 'create_shape' },
},
// ========== Insert Text Operation Fields ==========
{
id: 'insertTextObjectId',
title: 'Object ID',
type: 'short-input',
placeholder: 'Object ID of the shape or table cell',
condition: { field: 'operation', value: 'insert_text' },
required: true,
},
{
id: 'insertTextContent',
title: 'Text',
type: 'long-input',
placeholder: 'Text to insert',
condition: { field: 'operation', value: 'insert_text' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate text content for a presentation slide based on the user's description.
The text should be:
- Clear and concise
- Professional and appropriate for presentations
- Well-structured with bullet points if listing items
Return ONLY the text content - no explanations, no markdown formatting markers, no extra text.`,
placeholder: 'Describe the text you want to insert...',
},
},
{
id: 'insertTextIndex',
title: 'Insertion Index',
type: 'short-input',
placeholder: 'Zero-based index (default: 0)',
condition: { field: 'operation', value: 'insert_text' },
},
],
tools: {
access: [
@@ -358,6 +600,13 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
'google_slides_add_slide',
'google_slides_add_image',
'google_slides_get_thumbnail',
'google_slides_get_page',
'google_slides_delete_object',
'google_slides_duplicate_object',
'google_slides_update_slides_position',
'google_slides_create_table',
'google_slides_create_shape',
'google_slides_insert_text',
],
config: {
tool: (params) => {
@@ -376,6 +625,20 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
return 'google_slides_add_image'
case 'get_thumbnail':
return 'google_slides_get_thumbnail'
case 'get_page':
return 'google_slides_get_page'
case 'delete_object':
return 'google_slides_delete_object'
case 'duplicate_object':
return 'google_slides_duplicate_object'
case 'reorder_slides':
return 'google_slides_update_slides_position'
case 'create_table':
return 'google_slides_create_table'
case 'create_shape':
return 'google_slides_create_shape'
case 'insert_text':
return 'google_slides_insert_text'
default:
throw new Error(`Invalid Google Slides operation: ${params.operation}`)
}
@@ -439,6 +702,82 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
result.pageObjectId = thumbnailPageId
}
// Get Page operation
if (params.operation === 'get_page') {
result.pageObjectId = params.getPageObjectId
}
// Delete Object operation
if (params.operation === 'delete_object') {
result.objectId = params.deleteObjectId
}
// Duplicate Object operation
if (params.operation === 'duplicate_object') {
result.objectId = params.duplicateObjectId
if (params.duplicateObjectIds) {
result.objectIds = params.duplicateObjectIds
}
}
// Reorder Slides operation
if (params.operation === 'reorder_slides') {
result.slideObjectIds = params.reorderSlideIds
if (params.reorderInsertionIndex) {
result.insertionIndex = Number.parseInt(params.reorderInsertionIndex as string, 10)
}
}
// Create Table operation
if (params.operation === 'create_table') {
result.pageObjectId = params.tablePageObjectId
if (params.tableRows) {
result.rows = Number.parseInt(params.tableRows as string, 10)
}
if (params.tableColumns) {
result.columns = Number.parseInt(params.tableColumns as string, 10)
}
if (params.tableWidth) {
result.width = Number.parseInt(params.tableWidth as string, 10)
}
if (params.tableHeight) {
result.height = Number.parseInt(params.tableHeight as string, 10)
}
if (params.tablePositionX) {
result.positionX = Number.parseInt(params.tablePositionX as string, 10)
}
if (params.tablePositionY) {
result.positionY = Number.parseInt(params.tablePositionY as string, 10)
}
}
// Create Shape operation
if (params.operation === 'create_shape') {
result.pageObjectId = params.shapePageObjectId
result.shapeType = params.shapeType
if (params.shapeWidth) {
result.width = Number.parseInt(params.shapeWidth as string, 10)
}
if (params.shapeHeight) {
result.height = Number.parseInt(params.shapeHeight as string, 10)
}
if (params.shapePositionX) {
result.positionX = Number.parseInt(params.shapePositionX as string, 10)
}
if (params.shapePositionY) {
result.positionY = Number.parseInt(params.shapePositionY as string, 10)
}
}
// Insert Text operation
if (params.operation === 'insert_text') {
result.objectId = params.insertTextObjectId
result.text = params.insertTextContent
if (params.insertTextIndex) {
result.insertionIndex = Number.parseInt(params.insertTextIndex as string, 10)
}
}
return result
},
},
@@ -479,6 +818,35 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
thumbnailPageId: { type: 'string', description: 'Slide object ID for thumbnail' },
thumbnailSize: { type: 'string', description: 'Thumbnail size' },
mimeType: { type: 'string', description: 'Image format (PNG or GIF)' },
// Get page operation
getPageObjectId: { type: 'string', description: 'Page/slide object ID to retrieve' },
// Delete object operation
deleteObjectId: { type: 'string', description: 'Object ID to delete' },
// Duplicate object operation
duplicateObjectId: { type: 'string', description: 'Object ID to duplicate' },
duplicateObjectIds: { type: 'string', description: 'JSON object ID mappings' },
// Reorder slides operation
reorderSlideIds: { type: 'string', description: 'Comma-separated slide IDs to move' },
reorderInsertionIndex: { type: 'number', description: 'New position for slides' },
// Create table operation
tablePageObjectId: { type: 'string', description: 'Slide ID for table' },
tableRows: { type: 'number', description: 'Number of rows' },
tableColumns: { type: 'number', description: 'Number of columns' },
tableWidth: { type: 'number', description: 'Table width in points' },
tableHeight: { type: 'number', description: 'Table height in points' },
tablePositionX: { type: 'number', description: 'Table X position in points' },
tablePositionY: { type: 'number', description: 'Table Y position in points' },
// Create shape operation
shapePageObjectId: { type: 'string', description: 'Slide ID for shape' },
shapeType: { type: 'string', description: 'Shape type' },
shapeWidth: { type: 'number', description: 'Shape width in points' },
shapeHeight: { type: 'number', description: 'Shape height in points' },
shapePositionX: { type: 'number', description: 'Shape X position in points' },
shapePositionY: { type: 'number', description: 'Shape Y position in points' },
// Insert text operation
insertTextObjectId: { type: 'string', description: 'Object ID for text insertion' },
insertTextContent: { type: 'string', description: 'Text to insert' },
insertTextIndex: { type: 'number', description: 'Insertion index' },
},
outputs: {
// Read operation
@@ -496,5 +864,26 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
contentUrl: { type: 'string', description: 'URL to the thumbnail image' },
width: { type: 'number', description: 'Thumbnail width in pixels' },
height: { type: 'number', description: 'Thumbnail height in pixels' },
// Get page operation
objectId: { type: 'string', description: 'Page object ID' },
pageType: { type: 'string', description: 'Page type (SLIDE, MASTER, etc.)' },
pageElements: { type: 'json', description: 'Page elements array' },
slideProperties: { type: 'json', description: 'Slide-specific properties' },
// Delete object operation
deleted: { type: 'boolean', description: 'Whether object was deleted' },
// Duplicate object operation
duplicatedObjectId: { type: 'string', description: 'Object ID of the duplicate' },
// Reorder slides operation
moved: { type: 'boolean', description: 'Whether slides were moved' },
slideObjectIds: { type: 'json', description: 'Slide IDs that were moved' },
// Create table operation
tableId: { type: 'string', description: 'Object ID of newly created table' },
rows: { type: 'number', description: 'Number of rows created' },
columns: { type: 'number', description: 'Number of columns created' },
// Create shape operation
shapeId: { type: 'string', description: 'Object ID of newly created shape' },
// Insert text operation
inserted: { type: 'boolean', description: 'Whether text was inserted' },
text: { type: 'string', description: 'Text that was inserted' },
},
}

View File

@@ -1,7 +1,10 @@
import { MicrosoftExcelIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import type { MicrosoftExcelResponse } from '@/tools/microsoft_excel/types'
import type {
MicrosoftExcelResponse,
MicrosoftExcelV2Response,
} from '@/tools/microsoft_excel/types'
export const MicrosoftExcelBlock: BlockConfig<MicrosoftExcelResponse> = {
type: 'microsoft_excel',
@@ -325,3 +328,260 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
},
},
}
export const MicrosoftExcelV2Block: BlockConfig<MicrosoftExcelV2Response> = {
type: 'microsoft_excel_v2',
name: 'Microsoft Excel',
description: 'Read and write data with sheet selection',
authMode: AuthMode.OAuth,
hideFromToolbar: false,
longDescription:
'Integrate Microsoft Excel into the workflow with explicit sheet selection. Can read and write data in specific sheets.',
docsLink: 'https://docs.sim.ai/tools/microsoft_excel',
category: 'tools',
bgColor: '#E0E0E0',
icon: MicrosoftExcelIcon,
subBlocks: [
// Operation selector
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Read Data', id: 'read' },
{ label: 'Write Data', id: 'write' },
],
value: () => 'read',
},
// Microsoft Excel Credentials
{
id: 'credential',
title: 'Microsoft Account',
type: 'oauth-input',
serviceId: 'microsoft-excel',
requiredScopes: [
'openid',
'profile',
'email',
'Files.Read',
'Files.ReadWrite',
'offline_access',
],
placeholder: 'Select Microsoft account',
required: true,
},
// Spreadsheet Selector (basic mode)
{
id: 'spreadsheetId',
title: 'Select Spreadsheet',
type: 'file-selector',
canonicalParamId: 'spreadsheetId',
serviceId: 'microsoft-excel',
requiredScopes: [],
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
placeholder: 'Select a spreadsheet',
dependsOn: ['credential'],
mode: 'basic',
},
// Manual Spreadsheet ID (advanced mode)
{
id: 'manualSpreadsheetId',
title: 'Spreadsheet ID',
type: 'short-input',
canonicalParamId: 'spreadsheetId',
placeholder: 'Enter spreadsheet ID',
dependsOn: ['credential'],
mode: 'advanced',
},
// Sheet Name Selector (basic mode)
{
id: 'sheetName',
title: 'Sheet (Tab)',
type: 'sheet-selector',
canonicalParamId: 'sheetName',
serviceId: 'microsoft-excel',
placeholder: 'Select a sheet',
required: true,
dependsOn: { all: ['credential'], any: ['spreadsheetId', 'manualSpreadsheetId'] },
mode: 'basic',
},
// Manual Sheet Name (advanced mode)
{
id: 'manualSheetName',
title: 'Sheet Name',
type: 'short-input',
canonicalParamId: 'sheetName',
placeholder: 'Name of the sheet/tab (e.g., Sheet1)',
required: true,
dependsOn: ['credential'],
mode: 'advanced',
},
// Cell Range (optional for read/write)
{
id: 'cellRange',
title: 'Cell Range',
type: 'short-input',
placeholder: 'Cell range (e.g., A1:D10). Defaults to used range for read, A1 for write.',
wandConfig: {
enabled: true,
prompt: `Generate a valid cell range based on the user's description.
### VALID FORMATS
- Single cell: A1
- Range: A1:D10
- Entire column: A:A
- Entire row: 1:1
- Multiple columns: A:D
- Multiple rows: 1:10
### RANGE RULES
- Column letters are uppercase: A, B, C, ... Z, AA, AB, etc.
- Row numbers start at 1 (not 0)
### EXAMPLES
- "first 100 rows" -> A1:Z100
- "cells A1 through C50" -> A1:C50
- "column A" -> A:A
- "just the headers row" -> 1:1
- "first cell" -> A1
Return ONLY the range string - no sheet name, no explanations, no quotes.`,
placeholder: 'Describe the range (e.g., "first 50 rows" or "column A")...',
},
},
// Write-specific Fields
{
id: 'values',
title: 'Values',
type: 'long-input',
placeholder:
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}])',
condition: { field: 'operation', value: 'write' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate Microsoft Excel data as a JSON array based on the user's description.
Format options:
1. Array of arrays: [["Header1", "Header2"], ["Value1", "Value2"]]
2. Array of objects: [{"column1": "value1", "column2": "value2"}]
Examples:
- "sales data with product and revenue columns" -> [["Product", "Revenue"], ["Widget A", 1500], ["Widget B", 2300]]
- "list of employees with name and email" -> [{"name": "John Doe", "email": "john@example.com"}, {"name": "Jane Smith", "email": "jane@example.com"}]
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the data you want to write...',
generationType: 'json-object',
},
},
{
id: 'valueInputOption',
title: 'Value Input Option',
type: 'dropdown',
options: [
{ label: 'User Entered (Parse formulas)', id: 'USER_ENTERED' },
{ label: "Raw (Don't parse formulas)", id: 'RAW' },
],
condition: { field: 'operation', value: 'write' },
},
],
tools: {
access: ['microsoft_excel_read_v2', 'microsoft_excel_write_v2'],
config: {
tool: (params) => {
switch (params.operation) {
case 'read':
return 'microsoft_excel_read_v2'
case 'write':
return 'microsoft_excel_write_v2'
default:
throw new Error(`Invalid Microsoft Excel V2 operation: ${params.operation}`)
}
},
params: (params) => {
const {
credential,
values,
spreadsheetId,
manualSpreadsheetId,
sheetName,
manualSheetName,
cellRange,
...rest
} = params
const parsedValues = values ? JSON.parse(values as string) : undefined
const effectiveSpreadsheetId = (spreadsheetId || manualSpreadsheetId || '').trim()
const effectiveSheetName = ((sheetName || manualSheetName || '') as string).trim()
if (!effectiveSpreadsheetId) {
throw new Error('Spreadsheet ID is required.')
}
if (!effectiveSheetName) {
throw new Error('Sheet name is required. Please select or enter a sheet name.')
}
return {
...rest,
spreadsheetId: effectiveSpreadsheetId,
sheetName: effectiveSheetName,
cellRange: cellRange ? (cellRange as string).trim() : undefined,
values: parsedValues,
credential,
}
},
},
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
credential: { type: 'string', description: 'Microsoft Excel access token' },
spreadsheetId: { type: 'string', description: 'Spreadsheet identifier' },
manualSpreadsheetId: { type: 'string', description: 'Manual spreadsheet identifier' },
sheetName: { type: 'string', description: 'Name of the sheet/tab' },
manualSheetName: { type: 'string', description: 'Manual sheet name entry' },
cellRange: { type: 'string', description: 'Cell range (e.g., A1:D10)' },
values: { type: 'string', description: 'Cell values data' },
valueInputOption: { type: 'string', description: 'Value input option' },
},
outputs: {
sheetName: {
type: 'string',
description: 'Name of the sheet',
condition: { field: 'operation', value: 'read' },
},
range: {
type: 'string',
description: 'Range that was read',
condition: { field: 'operation', value: 'read' },
},
values: {
type: 'json',
description: 'Cell values as 2D array',
condition: { field: 'operation', value: 'read' },
},
updatedRange: {
type: 'string',
description: 'Updated range',
condition: { field: 'operation', value: 'write' },
},
updatedRows: {
type: 'number',
description: 'Updated rows count',
condition: { field: 'operation', value: 'write' },
},
updatedColumns: {
type: 'number',
description: 'Updated columns count',
condition: { field: 'operation', value: 'write' },
},
updatedCells: {
type: 'number',
description: 'Updated cells count',
condition: { field: 'operation', value: 'write' },
},
metadata: { type: 'json', description: 'Spreadsheet metadata including ID and URL' },
},
}

View File

@@ -1,261 +0,0 @@
import { MicrosoftExcelIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import type { MicrosoftExcelV2Response } from '@/tools/microsoft_excel/types'
export const MicrosoftExcelV2Block: BlockConfig<MicrosoftExcelV2Response> = {
type: 'microsoft_excel_v2',
name: 'Microsoft Excel',
description: 'Read and write data with sheet selection',
authMode: AuthMode.OAuth,
hideFromToolbar: false,
longDescription:
'Integrate Microsoft Excel into the workflow with explicit sheet selection. Can read and write data in specific sheets.',
docsLink: 'https://docs.sim.ai/tools/microsoft_excel',
category: 'tools',
bgColor: '#E0E0E0',
icon: MicrosoftExcelIcon,
subBlocks: [
// Operation selector
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Read Data', id: 'read' },
{ label: 'Write Data', id: 'write' },
],
value: () => 'read',
},
// Microsoft Excel Credentials
{
id: 'credential',
title: 'Microsoft Account',
type: 'oauth-input',
serviceId: 'microsoft-excel',
requiredScopes: [
'openid',
'profile',
'email',
'Files.Read',
'Files.ReadWrite',
'offline_access',
],
placeholder: 'Select Microsoft account',
required: true,
},
// Spreadsheet Selector (basic mode)
{
id: 'spreadsheetId',
title: 'Select Spreadsheet',
type: 'file-selector',
canonicalParamId: 'spreadsheetId',
serviceId: 'microsoft-excel',
requiredScopes: [],
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
placeholder: 'Select a spreadsheet',
dependsOn: ['credential'],
mode: 'basic',
},
// Manual Spreadsheet ID (advanced mode)
{
id: 'manualSpreadsheetId',
title: 'Spreadsheet ID',
type: 'short-input',
canonicalParamId: 'spreadsheetId',
placeholder: 'Enter spreadsheet ID',
dependsOn: ['credential'],
mode: 'advanced',
},
// Sheet Name Selector (basic mode)
{
id: 'sheetName',
title: 'Sheet (Tab)',
type: 'sheet-selector',
canonicalParamId: 'sheetName',
serviceId: 'microsoft-excel',
placeholder: 'Select a sheet',
required: true,
dependsOn: { all: ['credential'], any: ['spreadsheetId', 'manualSpreadsheetId'] },
mode: 'basic',
},
// Manual Sheet Name (advanced mode)
{
id: 'manualSheetName',
title: 'Sheet Name',
type: 'short-input',
canonicalParamId: 'sheetName',
placeholder: 'Name of the sheet/tab (e.g., Sheet1)',
required: true,
dependsOn: ['credential'],
mode: 'advanced',
},
// Cell Range (optional for read/write)
{
id: 'cellRange',
title: 'Cell Range',
type: 'short-input',
placeholder: 'Cell range (e.g., A1:D10). Defaults to used range for read, A1 for write.',
wandConfig: {
enabled: true,
prompt: `Generate a valid cell range based on the user's description.
### VALID FORMATS
- Single cell: A1
- Range: A1:D10
- Entire column: A:A
- Entire row: 1:1
- Multiple columns: A:D
- Multiple rows: 1:10
### RANGE RULES
- Column letters are uppercase: A, B, C, ... Z, AA, AB, etc.
- Row numbers start at 1 (not 0)
### EXAMPLES
- "first 100 rows" -> A1:Z100
- "cells A1 through C50" -> A1:C50
- "column A" -> A:A
- "just the headers row" -> 1:1
- "first cell" -> A1
Return ONLY the range string - no sheet name, no explanations, no quotes.`,
placeholder: 'Describe the range (e.g., "first 50 rows" or "column A")...',
},
},
// Write-specific Fields
{
id: 'values',
title: 'Values',
type: 'long-input',
placeholder:
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}])',
condition: { field: 'operation', value: 'write' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate Microsoft Excel data as a JSON array based on the user's description.
Format options:
1. Array of arrays: [["Header1", "Header2"], ["Value1", "Value2"]]
2. Array of objects: [{"column1": "value1", "column2": "value2"}]
Examples:
- "sales data with product and revenue columns" -> [["Product", "Revenue"], ["Widget A", 1500], ["Widget B", 2300]]
- "list of employees with name and email" -> [{"name": "John Doe", "email": "john@example.com"}, {"name": "Jane Smith", "email": "jane@example.com"}]
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the data you want to write...',
generationType: 'json-object',
},
},
{
id: 'valueInputOption',
title: 'Value Input Option',
type: 'dropdown',
options: [
{ label: 'User Entered (Parse formulas)', id: 'USER_ENTERED' },
{ label: "Raw (Don't parse formulas)", id: 'RAW' },
],
condition: { field: 'operation', value: 'write' },
},
],
tools: {
access: ['microsoft_excel_read_v2', 'microsoft_excel_write_v2'],
config: {
tool: (params) => {
switch (params.operation) {
case 'read':
return 'microsoft_excel_read_v2'
case 'write':
return 'microsoft_excel_write_v2'
default:
throw new Error(`Invalid Microsoft Excel V2 operation: ${params.operation}`)
}
},
params: (params) => {
const {
credential,
values,
spreadsheetId,
manualSpreadsheetId,
sheetName,
manualSheetName,
cellRange,
...rest
} = params
const parsedValues = values ? JSON.parse(values as string) : undefined
const effectiveSpreadsheetId = (spreadsheetId || manualSpreadsheetId || '').trim()
const effectiveSheetName = ((sheetName || manualSheetName || '') as string).trim()
if (!effectiveSpreadsheetId) {
throw new Error('Spreadsheet ID is required.')
}
if (!effectiveSheetName) {
throw new Error('Sheet name is required. Please select or enter a sheet name.')
}
return {
...rest,
spreadsheetId: effectiveSpreadsheetId,
sheetName: effectiveSheetName,
cellRange: cellRange ? (cellRange as string).trim() : undefined,
values: parsedValues,
credential,
}
},
},
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
credential: { type: 'string', description: 'Microsoft Excel access token' },
spreadsheetId: { type: 'string', description: 'Spreadsheet identifier' },
manualSpreadsheetId: { type: 'string', description: 'Manual spreadsheet identifier' },
sheetName: { type: 'string', description: 'Name of the sheet/tab' },
manualSheetName: { type: 'string', description: 'Manual sheet name entry' },
cellRange: { type: 'string', description: 'Cell range (e.g., A1:D10)' },
values: { type: 'string', description: 'Cell values data' },
valueInputOption: { type: 'string', description: 'Value input option' },
},
outputs: {
sheetName: {
type: 'string',
description: 'Name of the sheet',
condition: { field: 'operation', value: 'read' },
},
range: {
type: 'string',
description: 'Range that was read',
condition: { field: 'operation', value: 'read' },
},
values: {
type: 'json',
description: 'Cell values as 2D array',
condition: { field: 'operation', value: 'read' },
},
updatedRange: {
type: 'string',
description: 'Updated range',
condition: { field: 'operation', value: 'write' },
},
updatedRows: {
type: 'number',
description: 'Updated rows count',
condition: { field: 'operation', value: 'write' },
},
updatedColumns: {
type: 'number',
description: 'Updated columns count',
condition: { field: 'operation', value: 'write' },
},
updatedCells: {
type: 'number',
description: 'Updated cells count',
condition: { field: 'operation', value: 'write' },
},
metadata: { type: 'json', description: 'Spreadsheet metadata including ID and URL' },
},
}

View File

@@ -8,7 +8,6 @@ import { ApifyBlock } from '@/blocks/blocks/apify'
import { ApolloBlock } from '@/blocks/blocks/apollo'
import { ArxivBlock } from '@/blocks/blocks/arxiv'
import { AsanaBlock } from '@/blocks/blocks/asana'
// import { BoxBlock } from '@/blocks/blocks/box' // TODO: Box OAuth integration
import { BrowserUseBlock } from '@/blocks/blocks/browser_use'
import { CalendlyBlock } from '@/blocks/blocks/calendly'
import { ChatTriggerBlock } from '@/blocks/blocks/chat_trigger'
@@ -38,10 +37,9 @@ import { GoogleSearchBlock } from '@/blocks/blocks/google'
import { GoogleCalendarBlock, GoogleCalendarV2Block } from '@/blocks/blocks/google_calendar'
import { GoogleDocsBlock } from '@/blocks/blocks/google_docs'
import { GoogleDriveBlock } from '@/blocks/blocks/google_drive'
import { GoogleFormsBlock } from '@/blocks/blocks/google_form'
import { GoogleFormsBlock } from '@/blocks/blocks/google_forms'
import { GoogleGroupsBlock } from '@/blocks/blocks/google_groups'
import { GoogleSheetsBlock } from '@/blocks/blocks/google_sheets'
import { GoogleSheetsV2Block } from '@/blocks/blocks/google_sheets_v2'
import { GoogleSheetsBlock, GoogleSheetsV2Block } from '@/blocks/blocks/google_sheets'
import { GoogleSlidesBlock } from '@/blocks/blocks/google_slides'
import { GoogleVaultBlock } from '@/blocks/blocks/google_vault'
import { GrafanaBlock } from '@/blocks/blocks/grafana'
@@ -73,8 +71,7 @@ import { ManualTriggerBlock } from '@/blocks/blocks/manual_trigger'
import { McpBlock } from '@/blocks/blocks/mcp'
import { Mem0Block } from '@/blocks/blocks/mem0'
import { MemoryBlock } from '@/blocks/blocks/memory'
import { MicrosoftExcelBlock } from '@/blocks/blocks/microsoft_excel'
import { MicrosoftExcelV2Block } from '@/blocks/blocks/microsoft_excel_v2'
import { MicrosoftExcelBlock, MicrosoftExcelV2Block } from '@/blocks/blocks/microsoft_excel'
import { MicrosoftPlannerBlock } from '@/blocks/blocks/microsoft_planner'
import { MicrosoftTeamsBlock } from '@/blocks/blocks/microsoft_teams'
import { MistralParseBlock } from '@/blocks/blocks/mistral_parse'
@@ -116,6 +113,7 @@ import { ShopifyBlock } from '@/blocks/blocks/shopify'
import { SlackBlock } from '@/blocks/blocks/slack'
import { SmtpBlock } from '@/blocks/blocks/smtp'
import { SpotifyBlock } from '@/blocks/blocks/spotify'
import { SQSBlock } from '@/blocks/blocks/sqs'
import { SSHBlock } from '@/blocks/blocks/ssh'
import { StagehandBlock } from '@/blocks/blocks/stagehand'
import { StartTriggerBlock } from '@/blocks/blocks/start_trigger'
@@ -151,7 +149,6 @@ import { ZendeskBlock } from '@/blocks/blocks/zendesk'
import { ZepBlock } from '@/blocks/blocks/zep'
import { ZoomBlock } from '@/blocks/blocks/zoom'
import type { BlockConfig } from '@/blocks/types'
import { SQSBlock } from './blocks/sqs'
// Registry of all available blocks, alphabetically sorted
export const registry: Record<string, BlockConfig> = {
@@ -165,7 +162,6 @@ export const registry: Record<string, BlockConfig> = {
apollo: ApolloBlock,
arxiv: ArxivBlock,
asana: AsanaBlock,
// box: BoxBlock, // TODO: Box OAuth integration
browser_use: BrowserUseBlock,
calendly: CalendlyBlock,
chat_trigger: ChatTriggerBlock,
@@ -179,8 +175,9 @@ export const registry: Record<string, BlockConfig> = {
discord: DiscordBlock,
dropbox: DropboxBlock,
duckduckgo: DuckDuckGoBlock,
elevenlabs: ElevenLabsBlock,
dynamodb: DynamoDBBlock,
elasticsearch: ElasticsearchBlock,
elevenlabs: ElevenLabsBlock,
evaluator: EvaluatorBlock,
exa: ExaBlock,
file: FileBlock,
@@ -193,21 +190,21 @@ export const registry: Record<string, BlockConfig> = {
gitlab: GitLabBlock,
gmail: GmailBlock,
gmail_v2: GmailV2Block,
grain: GrainBlock,
grafana: GrafanaBlock,
greptile: GreptileBlock,
guardrails: GuardrailsBlock,
google_calendar: GoogleCalendarBlock,
google_calendar_v2: GoogleCalendarV2Block,
google_docs: GoogleDocsBlock,
google_drive: GoogleDriveBlock,
google_forms: GoogleFormsBlock,
google_groups: GoogleGroupsBlock,
google_search: GoogleSearchBlock,
google_sheets: GoogleSheetsBlock,
google_sheets_v2: GoogleSheetsV2Block,
google_slides: GoogleSlidesBlock,
google_vault: GoogleVaultBlock,
google_groups: GoogleGroupsBlock,
grafana: GrafanaBlock,
grain: GrainBlock,
greptile: GreptileBlock,
guardrails: GuardrailsBlock,
hubspot: HubSpotBlock,
huggingface: HuggingFaceBlock,
human_in_the_loop: HumanInTheLoopBlock,
@@ -239,7 +236,6 @@ export const registry: Record<string, BlockConfig> = {
microsoft_planner: MicrosoftPlannerBlock,
microsoft_teams: MicrosoftTeamsBlock,
mistral_parse: MistralParseBlock,
reducto: ReductoBlock,
mongodb: MongoDBBlock,
mysql: MySQLBlock,
neo4j: Neo4jBlock,
@@ -259,35 +255,34 @@ export const registry: Record<string, BlockConfig> = {
pulse: PulseBlock,
qdrant: QdrantBlock,
rds: RDSBlock,
sqs: SQSBlock,
dynamodb: DynamoDBBlock,
reddit: RedditBlock,
reducto: ReductoBlock,
resend: ResendBlock,
response: ResponseBlock,
rss: RssBlock,
router: RouterBlock,
router_v2: RouterV2Block,
rss: RssBlock,
s3: S3Block,
salesforce: SalesforceBlock,
schedule: ScheduleBlock,
search: SearchBlock,
sendgrid: SendGridBlock,
sentry: SentryBlock,
servicenow: ServiceNowBlock,
serper: SerperBlock,
servicenow: ServiceNowBlock,
sftp: SftpBlock,
sharepoint: SharepointBlock,
shopify: ShopifyBlock,
slack: SlackBlock,
spotify: SpotifyBlock,
smtp: SmtpBlock,
sftp: SftpBlock,
spotify: SpotifyBlock,
sqs: SQSBlock,
ssh: SSHBlock,
stagehand: StagehandBlock,
starter: StarterBlock,
start_trigger: StartTriggerBlock,
stt: SttBlock,
tts: TtsBlock,
starter: StarterBlock,
stripe: StripeBlock,
stt: SttBlock,
supabase: SupabaseBlock,
tavily: TavilyBlock,
telegram: TelegramBlock,
@@ -295,6 +290,7 @@ export const registry: Record<string, BlockConfig> = {
tinybird: TinybirdBlock,
translate: TranslateBlock,
trello: TrelloBlock,
tts: TtsBlock,
twilio_sms: TwilioSMSBlock,
twilio_voice: TwilioVoiceBlock,
typeform: TypeformBlock,
@@ -312,8 +308,8 @@ export const registry: Record<string, BlockConfig> = {
workflow_input: WorkflowInputBlock,
x: XBlock,
youtube: YouTubeBlock,
zep: ZepBlock,
zendesk: ZendeskBlock,
zep: ZepBlock,
zoom: ZoomBlock,
}

View File

@@ -587,9 +587,14 @@ const Combobox = memo(
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Escape') {
setOpen(false)
setSearchQuery('')
// Forward navigation keys to main handler
if (
e.key === 'ArrowDown' ||
e.key === 'ArrowUp' ||
e.key === 'Enter' ||
e.key === 'Escape'
) {
handleKeyDown(e as unknown as KeyboardEvent<HTMLDivElement>)
}
}}
/>

View File

@@ -11,15 +11,18 @@ export const a2aCancelTaskTool: ToolConfig<A2ACancelTaskParams, A2ACancelTaskRes
agentUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The A2A agent endpoint URL',
},
taskId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Task ID to cancel',
},
apiKey: {
type: 'string',
visibility: 'user-only',
description: 'API key for authentication',
},
},

View File

@@ -14,20 +14,24 @@ export const a2aDeletePushNotificationTool: ToolConfig<
agentUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The A2A agent endpoint URL',
},
taskId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Task ID to delete notification config for',
},
pushNotificationConfigId: {
type: 'string',
visibility: 'user-or-llm',
description:
'Push notification configuration ID to delete (optional - server can derive from taskId)',
},
apiKey: {
type: 'string',
visibility: 'user-only',
description: 'API key for authentication',
},
},

View File

@@ -11,10 +11,12 @@ export const a2aGetAgentCardTool: ToolConfig<A2AGetAgentCardParams, A2AGetAgentC
agentUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The A2A agent endpoint URL',
},
apiKey: {
type: 'string',
visibility: 'user-only',
description: 'API key for authentication (if required)',
},
},

View File

@@ -14,15 +14,18 @@ export const a2aGetPushNotificationTool: ToolConfig<
agentUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The A2A agent endpoint URL',
},
taskId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Task ID to get notification config for',
},
apiKey: {
type: 'string',
visibility: 'user-only',
description: 'API key for authentication',
},
},

View File

@@ -11,19 +11,23 @@ export const a2aGetTaskTool: ToolConfig<A2AGetTaskParams, A2AGetTaskResponse> =
agentUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The A2A agent endpoint URL',
},
taskId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Task ID to query',
},
apiKey: {
type: 'string',
visibility: 'user-only',
description: 'API key for authentication',
},
historyLength: {
type: 'number',
visibility: 'user-or-llm',
description: 'Number of history messages to include',
},
},

View File

@@ -11,15 +11,18 @@ export const a2aResubscribeTool: ToolConfig<A2AResubscribeParams, A2AResubscribe
agentUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The A2A agent endpoint URL',
},
taskId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Task ID to resubscribe to',
},
apiKey: {
type: 'string',
visibility: 'user-only',
description: 'API key for authentication',
},
},

View File

@@ -11,31 +11,38 @@ export const a2aSendMessageTool: ToolConfig<A2ASendMessageParams, A2ASendMessage
agentUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The A2A agent endpoint URL',
},
message: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Message to send to the agent',
},
taskId: {
type: 'string',
visibility: 'user-or-llm',
description: 'Task ID for continuing an existing task',
},
contextId: {
type: 'string',
visibility: 'user-or-llm',
description: 'Context ID for conversation continuity',
},
data: {
type: 'string',
visibility: 'user-or-llm',
description: 'Structured data to include with the message (JSON string)',
},
files: {
type: 'array',
visibility: 'user-only',
description: 'Files to include with the message',
},
apiKey: {
type: 'string',
visibility: 'user-only',
description: 'API key for authentication',
},
},

View File

@@ -14,24 +14,29 @@ export const a2aSetPushNotificationTool: ToolConfig<
agentUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The A2A agent endpoint URL',
},
taskId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Task ID to configure notifications for',
},
webhookUrl: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'HTTPS webhook URL to receive notifications',
},
token: {
type: 'string',
visibility: 'user-only',
description: 'Token for webhook validation',
},
apiKey: {
type: 'string',
visibility: 'user-only',
description: 'API key for authentication',
},
},

View File

@@ -0,0 +1,115 @@
import type { ToolConfig } from '@/tools/types'
interface CheckStarParams {
owner: string
repo: string
apiKey: string
}
interface CheckStarResponse {
success: boolean
output: {
content: string
metadata: {
starred: boolean
owner: string
repo: string
}
}
}
export const checkStarTool: ToolConfig<CheckStarParams, CheckStarResponse> = {
id: 'github_check_star',
name: 'GitHub Check Star',
description: 'Check if you have starred a repository',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => `https://api.github.com/user/starred/${params.owner}/${params.repo}`,
method: 'GET',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response, params) => {
const starred = response.status === 204
return {
success: true,
output: {
content: starred
? `You have starred ${params?.owner}/${params?.repo}`
: `You have not starred ${params?.owner}/${params?.repo}`,
metadata: {
starred,
owner: params?.owner ?? '',
repo: params?.repo ?? '',
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable result' },
metadata: {
type: 'object',
description: 'Check star metadata',
properties: {
starred: { type: 'boolean', description: 'Whether you have starred the repo' },
owner: { type: 'string', description: 'Repository owner' },
repo: { type: 'string', description: 'Repository name' },
},
},
},
}
export const checkStarV2Tool: ToolConfig<CheckStarParams, any> = {
id: 'github_check_star_v2',
name: checkStarTool.name,
description: checkStarTool.description,
version: '2.0.0',
params: checkStarTool.params,
request: checkStarTool.request,
transformResponse: async (response: Response, params) => {
const starred = response.status === 204
return {
success: true,
output: {
starred,
owner: params?.owner ?? '',
repo: params?.repo ?? '',
},
}
},
outputs: {
starred: { type: 'boolean', description: 'Whether you have starred the repo' },
owner: { type: 'string', description: 'Repository owner' },
repo: { type: 'string', description: 'Repository name' },
},
}

View File

@@ -0,0 +1,256 @@
import type { ToolConfig } from '@/tools/types'
interface CompareCommitsParams {
owner: string
repo: string
base: string
head: string
per_page?: number
page?: number
apiKey: string
}
interface CompareCommitsResponse {
success: boolean
output: {
content: string
metadata: {
status: string
ahead_by: number
behind_by: number
total_commits: number
html_url: string
diff_url: string
patch_url: string
base_commit: { sha: string; html_url: string }
merge_base_commit: { sha: string; html_url: string }
commits: Array<{
sha: string
html_url: string
message: string
author: { login?: string; name: string }
}>
files: Array<{
filename: string
status: string
additions: number
deletions: number
changes: number
}>
}
}
}
export const compareCommitsTool: ToolConfig<CompareCommitsParams, CompareCommitsResponse> = {
id: 'github_compare_commits',
name: 'GitHub Compare Commits',
description:
'Compare two commits or branches to see the diff, commits between them, and changed files',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name',
},
base: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Base branch/tag/SHA for comparison',
},
head: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Head branch/tag/SHA for comparison',
},
per_page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Results per page for files (max 100, default: 30)',
default: 30,
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for files (default: 1)',
default: 1,
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => {
const url = new URL(
`https://api.github.com/repos/${params.owner}/${params.repo}/compare/${params.base}...${params.head}`
)
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
if (params.page) url.searchParams.append('page', String(params.page))
return url.toString()
},
method: 'GET',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const commits = (data.commits ?? []).map((c: any) => ({
sha: c.sha,
html_url: c.html_url,
message: c.commit.message,
author: {
login: c.author?.login,
name: c.commit.author.name,
},
}))
const files = (data.files ?? []).map((f: any) => ({
filename: f.filename,
status: f.status,
additions: f.additions,
deletions: f.deletions,
changes: f.changes,
}))
const metadata = {
status: data.status,
ahead_by: data.ahead_by,
behind_by: data.behind_by,
total_commits: data.total_commits,
html_url: data.html_url,
diff_url: data.diff_url,
patch_url: data.patch_url,
base_commit: {
sha: data.base_commit.sha,
html_url: data.base_commit.html_url,
},
merge_base_commit: {
sha: data.merge_base_commit.sha,
html_url: data.merge_base_commit.html_url,
},
commits,
files,
}
const content = `Comparing ${data.base_commit.sha.substring(0, 7)}...${data.commits?.length > 0 ? data.commits[data.commits.length - 1].sha.substring(0, 7) : 'HEAD'}
Status: ${data.status} | Ahead: ${data.ahead_by} | Behind: ${data.behind_by}
Total commits: ${data.total_commits} | Files changed: ${files.length}
${data.html_url}
Commits:
${commits.map((c: any) => ` ${c.sha.substring(0, 7)} - ${c.message.split('\n')[0]}`).join('\n')}
Files changed:
${files.map((f: any) => ` ${f.status}: ${f.filename} (+${f.additions} -${f.deletions})`).join('\n')}`
return {
success: true,
output: {
content,
metadata,
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable comparison' },
metadata: {
type: 'object',
description: 'Comparison metadata',
properties: {
status: { type: 'string', description: 'ahead, behind, identical, or diverged' },
ahead_by: { type: 'number', description: 'Commits ahead' },
behind_by: { type: 'number', description: 'Commits behind' },
total_commits: { type: 'number', description: 'Total commits between' },
html_url: { type: 'string', description: 'GitHub web URL' },
diff_url: { type: 'string', description: 'Diff URL' },
patch_url: { type: 'string', description: 'Patch URL' },
base_commit: { type: 'object', description: 'Base commit info' },
merge_base_commit: { type: 'object', description: 'Merge base commit info' },
commits: {
type: 'array',
description: 'Commits between base and head',
items: {
type: 'object',
properties: {
sha: { type: 'string', description: 'Commit SHA' },
html_url: { type: 'string', description: 'Web URL' },
message: { type: 'string', description: 'Commit message' },
author: { type: 'object', description: 'Author info' },
},
},
},
files: {
type: 'array',
description: 'Changed files',
items: {
type: 'object',
properties: {
filename: { type: 'string', description: 'File path' },
status: { type: 'string', description: 'Change type' },
additions: { type: 'number', description: 'Lines added' },
deletions: { type: 'number', description: 'Lines deleted' },
changes: { type: 'number', description: 'Total changes' },
},
},
},
},
},
},
}
export const compareCommitsV2Tool: ToolConfig<CompareCommitsParams, any> = {
id: 'github_compare_commits_v2',
name: compareCommitsTool.name,
description: compareCommitsTool.description,
version: '2.0.0',
params: compareCommitsTool.params,
request: compareCommitsTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
...data,
commits: data.commits ?? [],
files: data.files ?? [],
},
}
},
outputs: {
status: { type: 'string', description: 'Comparison status' },
ahead_by: { type: 'number', description: 'Commits ahead' },
behind_by: { type: 'number', description: 'Commits behind' },
total_commits: { type: 'number', description: 'Total commits' },
html_url: { type: 'string', description: 'Web URL' },
diff_url: { type: 'string', description: 'Diff URL' },
patch_url: { type: 'string', description: 'Patch URL' },
base_commit: { type: 'object', description: 'Base commit' },
merge_base_commit: { type: 'object', description: 'Merge base' },
commits: { type: 'array', description: 'Commits between' },
files: { type: 'array', description: 'Changed files' },
},
}

View File

@@ -0,0 +1,138 @@
import type { ToolConfig } from '@/tools/types'
interface CreateCommentReactionParams {
owner: string
repo: string
comment_id: number
content: '+1' | '-1' | 'laugh' | 'confused' | 'heart' | 'hooray' | 'rocket' | 'eyes'
apiKey: string
}
interface CreateCommentReactionResponse {
success: boolean
output: {
content: string
metadata: {
id: number
user: { login: string }
content: string
created_at: string
}
}
}
export const createCommentReactionTool: ToolConfig<
CreateCommentReactionParams,
CreateCommentReactionResponse
> = {
id: 'github_create_comment_reaction',
name: 'GitHub Create Comment Reaction',
description: 'Add a reaction to an issue comment',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name',
},
comment_id: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Comment ID',
},
content: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Reaction type: +1 (thumbs up), -1 (thumbs down), laugh, confused, heart, hooray, rocket, eyes',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) =>
`https://api.github.com/repos/${params.owner}/${params.repo}/issues/comments/${params.comment_id}/reactions`,
method: 'POST',
headers: (params) => ({
Accept: 'application/vnd.github.squirrel-girl-preview+json',
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
'X-GitHub-Api-Version': '2022-11-28',
}),
body: (params) => ({
content: params.content,
}),
},
transformResponse: async (response) => {
const data = await response.json()
const content = `Added ${data.content} reaction to comment by ${data.user?.login ?? 'unknown'}`
return {
success: true,
output: {
content,
metadata: {
id: data.id,
user: { login: data.user?.login ?? 'unknown' },
content: data.content,
created_at: data.created_at,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable result' },
metadata: {
type: 'object',
description: 'Reaction metadata',
properties: {
id: { type: 'number', description: 'Reaction ID' },
user: { type: 'object', description: 'User who reacted' },
content: { type: 'string', description: 'Reaction type' },
created_at: { type: 'string', description: 'Creation date' },
},
},
},
}
export const createCommentReactionV2Tool: ToolConfig<CreateCommentReactionParams, any> = {
id: 'github_create_comment_reaction_v2',
name: createCommentReactionTool.name,
description: createCommentReactionTool.description,
version: '2.0.0',
params: createCommentReactionTool.params,
request: createCommentReactionTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: data,
}
},
outputs: {
id: { type: 'number', description: 'Reaction ID' },
user: { type: 'object', description: 'User who reacted' },
content: { type: 'string', description: 'Reaction type' },
created_at: { type: 'string', description: 'Creation date' },
},
}

View File

@@ -0,0 +1,183 @@
import type { ToolConfig } from '@/tools/types'
interface CreateGistParams {
description?: string
files: string
public?: boolean
apiKey: string
}
interface CreateGistResponse {
success: boolean
output: {
content: string
metadata: {
id: string
html_url: string
git_pull_url: string
git_push_url: string
description: string | null
public: boolean
created_at: string
updated_at: string
files: Record<
string,
{ filename: string; type: string; language: string | null; size: number }
>
owner: { login: string }
}
}
}
export const createGistTool: ToolConfig<CreateGistParams, CreateGistResponse> = {
id: 'github_create_gist',
name: 'GitHub Create Gist',
description: 'Create a new gist with one or more files',
version: '1.0.0',
params: {
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Description of the gist',
},
files: {
type: 'json',
required: true,
visibility: 'user-or-llm',
description:
'JSON object with filenames as keys and content as values. Example: {"file.txt": {"content": "Hello"}}',
},
public: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether the gist is public (default: false)',
default: false,
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: () => 'https://api.github.com/gists',
method: 'POST',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
'X-GitHub-Api-Version': '2022-11-28',
}),
body: (params) => {
const filesObj = typeof params.files === 'string' ? JSON.parse(params.files) : params.files
return {
description: params.description,
public: params.public ?? false,
files: filesObj,
}
},
},
transformResponse: async (response) => {
const data = await response.json()
const files: Record<
string,
{ filename: string; type: string; language: string | null; size: number }
> = {}
for (const [key, value] of Object.entries(data.files ?? {})) {
const file = value as any
files[key] = {
filename: file.filename,
type: file.type,
language: file.language ?? null,
size: file.size,
}
}
const metadata = {
id: data.id,
html_url: data.html_url,
git_pull_url: data.git_pull_url,
git_push_url: data.git_push_url,
description: data.description ?? null,
public: data.public,
created_at: data.created_at,
updated_at: data.updated_at,
files,
owner: { login: data.owner?.login ?? 'unknown' },
}
const content = `Created gist: ${data.html_url}
Description: ${data.description ?? 'No description'}
Public: ${data.public}
Files: ${Object.keys(files).join(', ')}`
return {
success: true,
output: {
content,
metadata,
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable result' },
metadata: {
type: 'object',
description: 'Gist metadata',
properties: {
id: { type: 'string', description: 'Gist ID' },
html_url: { type: 'string', description: 'Web URL' },
git_pull_url: { type: 'string', description: 'Git pull URL' },
git_push_url: { type: 'string', description: 'Git push URL' },
description: { type: 'string', description: 'Description', optional: true },
public: { type: 'boolean', description: 'Is public' },
created_at: { type: 'string', description: 'Creation date' },
updated_at: { type: 'string', description: 'Update date' },
files: { type: 'object', description: 'Files in gist' },
owner: { type: 'object', description: 'Owner info' },
},
},
},
}
export const createGistV2Tool: ToolConfig<CreateGistParams, any> = {
id: 'github_create_gist_v2',
name: createGistTool.name,
description: createGistTool.description,
version: '2.0.0',
params: createGistTool.params,
request: createGistTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
...data,
description: data.description ?? null,
files: data.files ?? {},
},
}
},
outputs: {
id: { type: 'string', description: 'Gist ID' },
html_url: { type: 'string', description: 'Web URL' },
git_pull_url: { type: 'string', description: 'Git pull URL' },
git_push_url: { type: 'string', description: 'Git push URL' },
description: { type: 'string', description: 'Description', optional: true },
public: { type: 'boolean', description: 'Is public' },
created_at: { type: 'string', description: 'Creation date' },
updated_at: { type: 'string', description: 'Update date' },
files: { type: 'object', description: 'Files in gist' },
owner: { type: 'object', description: 'Owner info' },
},
}

View File

@@ -0,0 +1,138 @@
import type { ToolConfig } from '@/tools/types'
interface CreateIssueReactionParams {
owner: string
repo: string
issue_number: number
content: '+1' | '-1' | 'laugh' | 'confused' | 'heart' | 'hooray' | 'rocket' | 'eyes'
apiKey: string
}
interface CreateIssueReactionResponse {
success: boolean
output: {
content: string
metadata: {
id: number
user: { login: string }
content: string
created_at: string
}
}
}
export const createIssueReactionTool: ToolConfig<
CreateIssueReactionParams,
CreateIssueReactionResponse
> = {
id: 'github_create_issue_reaction',
name: 'GitHub Create Issue Reaction',
description: 'Add a reaction to an issue',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name',
},
issue_number: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Issue number',
},
content: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Reaction type: +1 (thumbs up), -1 (thumbs down), laugh, confused, heart, hooray, rocket, eyes',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) =>
`https://api.github.com/repos/${params.owner}/${params.repo}/issues/${params.issue_number}/reactions`,
method: 'POST',
headers: (params) => ({
Accept: 'application/vnd.github.squirrel-girl-preview+json',
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
'X-GitHub-Api-Version': '2022-11-28',
}),
body: (params) => ({
content: params.content,
}),
},
transformResponse: async (response) => {
const data = await response.json()
const content = `Added ${data.content} reaction to issue by ${data.user?.login ?? 'unknown'}`
return {
success: true,
output: {
content,
metadata: {
id: data.id,
user: { login: data.user?.login ?? 'unknown' },
content: data.content,
created_at: data.created_at,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable result' },
metadata: {
type: 'object',
description: 'Reaction metadata',
properties: {
id: { type: 'number', description: 'Reaction ID' },
user: { type: 'object', description: 'User who reacted' },
content: { type: 'string', description: 'Reaction type' },
created_at: { type: 'string', description: 'Creation date' },
},
},
},
}
export const createIssueReactionV2Tool: ToolConfig<CreateIssueReactionParams, any> = {
id: 'github_create_issue_reaction_v2',
name: createIssueReactionTool.name,
description: createIssueReactionTool.description,
version: '2.0.0',
params: createIssueReactionTool.params,
request: createIssueReactionTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: data,
}
},
outputs: {
id: { type: 'number', description: 'Reaction ID' },
user: { type: 'object', description: 'User who reacted' },
content: { type: 'string', description: 'Reaction type' },
created_at: { type: 'string', description: 'Creation date' },
},
}

View File

@@ -0,0 +1,182 @@
import type { ToolConfig } from '@/tools/types'
interface CreateMilestoneParams {
owner: string
repo: string
title: string
state?: 'open' | 'closed'
description?: string
due_on?: string
apiKey: string
}
interface CreateMilestoneResponse {
success: boolean
output: {
content: string
metadata: {
number: number
title: string
description: string | null
state: string
html_url: string
due_on: string | null
open_issues: number
closed_issues: number
created_at: string
creator: { login: string }
}
}
}
export const createMilestoneTool: ToolConfig<CreateMilestoneParams, CreateMilestoneResponse> = {
id: 'github_create_milestone',
name: 'GitHub Create Milestone',
description: 'Create a milestone in a repository',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name',
},
title: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Milestone title',
},
state: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'State: open or closed (default: open)',
default: 'open',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Milestone description',
},
due_on: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Due date (ISO 8601 format, e.g., 2024-12-31T23:59:59Z)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => `https://api.github.com/repos/${params.owner}/${params.repo}/milestones`,
method: 'POST',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
'X-GitHub-Api-Version': '2022-11-28',
}),
body: (params) => ({
title: params.title,
state: params.state ?? 'open',
description: params.description,
due_on: params.due_on,
}),
},
transformResponse: async (response) => {
const data = await response.json()
const content = `Created milestone: ${data.title}
Number: ${data.number}
State: ${data.state}
Due: ${data.due_on ?? 'No due date'}
${data.html_url}`
return {
success: true,
output: {
content,
metadata: {
number: data.number,
title: data.title,
description: data.description ?? null,
state: data.state,
html_url: data.html_url,
due_on: data.due_on ?? null,
open_issues: data.open_issues,
closed_issues: data.closed_issues,
created_at: data.created_at,
creator: { login: data.creator?.login ?? 'unknown' },
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable result' },
metadata: {
type: 'object',
description: 'Milestone metadata',
properties: {
number: { type: 'number', description: 'Milestone number' },
title: { type: 'string', description: 'Title' },
description: { type: 'string', description: 'Description', optional: true },
state: { type: 'string', description: 'State' },
html_url: { type: 'string', description: 'Web URL' },
due_on: { type: 'string', description: 'Due date', optional: true },
open_issues: { type: 'number', description: 'Open issues count' },
closed_issues: { type: 'number', description: 'Closed issues count' },
created_at: { type: 'string', description: 'Creation date' },
creator: { type: 'object', description: 'Creator info' },
},
},
},
}
export const createMilestoneV2Tool: ToolConfig<CreateMilestoneParams, any> = {
id: 'github_create_milestone_v2',
name: createMilestoneTool.name,
description: createMilestoneTool.description,
version: '2.0.0',
params: createMilestoneTool.params,
request: createMilestoneTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
...data,
description: data.description ?? null,
due_on: data.due_on ?? null,
},
}
},
outputs: {
number: { type: 'number', description: 'Milestone number' },
title: { type: 'string', description: 'Title' },
description: { type: 'string', description: 'Description', optional: true },
state: { type: 'string', description: 'State' },
html_url: { type: 'string', description: 'Web URL' },
due_on: { type: 'string', description: 'Due date', optional: true },
open_issues: { type: 'number', description: 'Open issues' },
closed_issues: { type: 'number', description: 'Closed issues' },
creator: { type: 'object', description: 'Creator' },
},
}

View File

@@ -0,0 +1,128 @@
import type { ToolConfig } from '@/tools/types'
interface DeleteCommentReactionParams {
owner: string
repo: string
comment_id: number
reaction_id: number
apiKey: string
}
interface DeleteCommentReactionResponse {
success: boolean
output: {
content: string
metadata: {
deleted: boolean
reaction_id: number
}
}
}
export const deleteCommentReactionTool: ToolConfig<
DeleteCommentReactionParams,
DeleteCommentReactionResponse
> = {
id: 'github_delete_comment_reaction',
name: 'GitHub Delete Comment Reaction',
description: 'Remove a reaction from an issue comment',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name',
},
comment_id: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Comment ID',
},
reaction_id: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Reaction ID to delete',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) =>
`https://api.github.com/repos/${params.owner}/${params.repo}/issues/comments/${params.comment_id}/reactions/${params.reaction_id}`,
method: 'DELETE',
headers: (params) => ({
Accept: 'application/vnd.github.squirrel-girl-preview+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response, params) => {
const deleted = response.status === 204
return {
success: deleted,
output: {
content: deleted
? `Successfully deleted reaction ${params?.reaction_id}`
: `Failed to delete reaction ${params?.reaction_id}`,
metadata: {
deleted,
reaction_id: params?.reaction_id ?? 0,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable result' },
metadata: {
type: 'object',
description: 'Delete operation metadata',
properties: {
deleted: { type: 'boolean', description: 'Whether deletion succeeded' },
reaction_id: { type: 'number', description: 'The deleted reaction ID' },
},
},
},
}
export const deleteCommentReactionV2Tool: ToolConfig<DeleteCommentReactionParams, any> = {
id: 'github_delete_comment_reaction_v2',
name: deleteCommentReactionTool.name,
description: deleteCommentReactionTool.description,
version: '2.0.0',
params: deleteCommentReactionTool.params,
request: deleteCommentReactionTool.request,
transformResponse: async (response: Response, params) => {
const deleted = response.status === 204
return {
success: deleted,
output: {
deleted,
reaction_id: params?.reaction_id ?? 0,
},
}
},
outputs: {
deleted: { type: 'boolean', description: 'Whether deletion succeeded' },
reaction_id: { type: 'number', description: 'The deleted reaction ID' },
},
}

View File

@@ -0,0 +1,103 @@
import type { ToolConfig } from '@/tools/types'
interface DeleteGistParams {
gist_id: string
apiKey: string
}
interface DeleteGistResponse {
success: boolean
output: {
content: string
metadata: {
deleted: boolean
gist_id: string
}
}
}
export const deleteGistTool: ToolConfig<DeleteGistParams, DeleteGistResponse> = {
id: 'github_delete_gist',
name: 'GitHub Delete Gist',
description: 'Delete a gist by ID',
version: '1.0.0',
params: {
gist_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The gist ID to delete',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => `https://api.github.com/gists/${params.gist_id?.trim()}`,
method: 'DELETE',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response, params) => {
const deleted = response.status === 204
return {
success: deleted,
output: {
content: deleted
? `Successfully deleted gist ${params?.gist_id}`
: `Failed to delete gist ${params?.gist_id}`,
metadata: {
deleted,
gist_id: params?.gist_id ?? '',
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable result' },
metadata: {
type: 'object',
description: 'Delete operation metadata',
properties: {
deleted: { type: 'boolean', description: 'Whether deletion succeeded' },
gist_id: { type: 'string', description: 'The deleted gist ID' },
},
},
},
}
export const deleteGistV2Tool: ToolConfig<DeleteGistParams, any> = {
id: 'github_delete_gist_v2',
name: deleteGistTool.name,
description: deleteGistTool.description,
version: '2.0.0',
params: deleteGistTool.params,
request: deleteGistTool.request,
transformResponse: async (response: Response, params) => {
const deleted = response.status === 204
return {
success: deleted,
output: {
deleted,
gist_id: params?.gist_id ?? '',
},
}
},
outputs: {
deleted: { type: 'boolean', description: 'Whether deletion succeeded' },
gist_id: { type: 'string', description: 'The deleted gist ID' },
},
}

View File

@@ -0,0 +1,128 @@
import type { ToolConfig } from '@/tools/types'
interface DeleteIssueReactionParams {
owner: string
repo: string
issue_number: number
reaction_id: number
apiKey: string
}
interface DeleteIssueReactionResponse {
success: boolean
output: {
content: string
metadata: {
deleted: boolean
reaction_id: number
}
}
}
export const deleteIssueReactionTool: ToolConfig<
DeleteIssueReactionParams,
DeleteIssueReactionResponse
> = {
id: 'github_delete_issue_reaction',
name: 'GitHub Delete Issue Reaction',
description: 'Remove a reaction from an issue',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name',
},
issue_number: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Issue number',
},
reaction_id: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Reaction ID to delete',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) =>
`https://api.github.com/repos/${params.owner}/${params.repo}/issues/${params.issue_number}/reactions/${params.reaction_id}`,
method: 'DELETE',
headers: (params) => ({
Accept: 'application/vnd.github.squirrel-girl-preview+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response, params) => {
const deleted = response.status === 204
return {
success: deleted,
output: {
content: deleted
? `Successfully deleted reaction ${params?.reaction_id}`
: `Failed to delete reaction ${params?.reaction_id}`,
metadata: {
deleted,
reaction_id: params?.reaction_id ?? 0,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable result' },
metadata: {
type: 'object',
description: 'Delete operation metadata',
properties: {
deleted: { type: 'boolean', description: 'Whether deletion succeeded' },
reaction_id: { type: 'number', description: 'The deleted reaction ID' },
},
},
},
}
export const deleteIssueReactionV2Tool: ToolConfig<DeleteIssueReactionParams, any> = {
id: 'github_delete_issue_reaction_v2',
name: deleteIssueReactionTool.name,
description: deleteIssueReactionTool.description,
version: '2.0.0',
params: deleteIssueReactionTool.params,
request: deleteIssueReactionTool.request,
transformResponse: async (response: Response, params) => {
const deleted = response.status === 204
return {
success: deleted,
output: {
deleted,
reaction_id: params?.reaction_id ?? 0,
},
}
},
outputs: {
deleted: { type: 'boolean', description: 'Whether deletion succeeded' },
reaction_id: { type: 'number', description: 'The deleted reaction ID' },
},
}

View File

@@ -0,0 +1,118 @@
import type { ToolConfig } from '@/tools/types'
interface DeleteMilestoneParams {
owner: string
repo: string
milestone_number: number
apiKey: string
}
interface DeleteMilestoneResponse {
success: boolean
output: {
content: string
metadata: {
deleted: boolean
milestone_number: number
}
}
}
export const deleteMilestoneTool: ToolConfig<DeleteMilestoneParams, DeleteMilestoneResponse> = {
id: 'github_delete_milestone',
name: 'GitHub Delete Milestone',
description: 'Delete a milestone from a repository',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name',
},
milestone_number: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Milestone number to delete',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) =>
`https://api.github.com/repos/${params.owner}/${params.repo}/milestones/${params.milestone_number}`,
method: 'DELETE',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response, params) => {
const deleted = response.status === 204
return {
success: deleted,
output: {
content: deleted
? `Successfully deleted milestone #${params?.milestone_number}`
: `Failed to delete milestone #${params?.milestone_number}`,
metadata: {
deleted,
milestone_number: params?.milestone_number ?? 0,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable result' },
metadata: {
type: 'object',
description: 'Delete operation metadata',
properties: {
deleted: { type: 'boolean', description: 'Whether deletion succeeded' },
milestone_number: { type: 'number', description: 'The deleted milestone number' },
},
},
},
}
export const deleteMilestoneV2Tool: ToolConfig<DeleteMilestoneParams, any> = {
id: 'github_delete_milestone_v2',
name: deleteMilestoneTool.name,
description: deleteMilestoneTool.description,
version: '2.0.0',
params: deleteMilestoneTool.params,
request: deleteMilestoneTool.request,
transformResponse: async (response: Response, params) => {
const deleted = response.status === 204
return {
success: deleted,
output: {
deleted,
milestone_number: params?.milestone_number ?? 0,
},
}
},
outputs: {
deleted: { type: 'boolean', description: 'Whether deletion succeeded' },
milestone_number: { type: 'number', description: 'The deleted milestone number' },
},
}

View File

@@ -0,0 +1,131 @@
import type { ToolConfig } from '@/tools/types'
interface ForkGistParams {
gist_id: string
apiKey: string
}
interface ForkGistResponse {
success: boolean
output: {
content: string
metadata: {
id: string
html_url: string
git_pull_url: string
description: string | null
public: boolean
created_at: string
owner: { login: string }
files: string[]
}
}
}
export const forkGistTool: ToolConfig<ForkGistParams, ForkGistResponse> = {
id: 'github_fork_gist',
name: 'GitHub Fork Gist',
description: 'Fork a gist to create your own copy',
version: '1.0.0',
params: {
gist_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The gist ID to fork',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => `https://api.github.com/gists/${params.gist_id?.trim()}/forks`,
method: 'POST',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const files = Object.keys(data.files ?? {})
const content = `Forked gist: ${data.html_url}
Description: ${data.description ?? 'No description'}
Files: ${files.join(', ')}`
return {
success: true,
output: {
content,
metadata: {
id: data.id,
html_url: data.html_url,
git_pull_url: data.git_pull_url,
description: data.description ?? null,
public: data.public,
created_at: data.created_at,
owner: { login: data.owner?.login ?? 'unknown' },
files,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable result' },
metadata: {
type: 'object',
description: 'Forked gist metadata',
properties: {
id: { type: 'string', description: 'New gist ID' },
html_url: { type: 'string', description: 'Web URL' },
git_pull_url: { type: 'string', description: 'Git pull URL' },
description: { type: 'string', description: 'Description', optional: true },
public: { type: 'boolean', description: 'Is public' },
created_at: { type: 'string', description: 'Creation date' },
owner: { type: 'object', description: 'Owner info' },
files: { type: 'array', description: 'File names' },
},
},
},
}
export const forkGistV2Tool: ToolConfig<ForkGistParams, any> = {
id: 'github_fork_gist_v2',
name: forkGistTool.name,
description: forkGistTool.description,
version: '2.0.0',
params: forkGistTool.params,
request: forkGistTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
...data,
description: data.description ?? null,
files: data.files ?? {},
},
}
},
outputs: {
id: { type: 'string', description: 'New gist ID' },
html_url: { type: 'string', description: 'Web URL' },
description: { type: 'string', description: 'Description', optional: true },
public: { type: 'boolean', description: 'Is public' },
created_at: { type: 'string', description: 'Creation date' },
owner: { type: 'object', description: 'Owner info' },
files: { type: 'object', description: 'Files' },
},
}

View File

@@ -0,0 +1,179 @@
import type { ToolConfig } from '@/tools/types'
interface ForkRepoParams {
owner: string
repo: string
organization?: string
name?: string
default_branch_only?: boolean
apiKey: string
}
interface ForkRepoResponse {
success: boolean
output: {
content: string
metadata: {
id: number
full_name: string
html_url: string
clone_url: string
ssh_url: string
default_branch: string
fork: boolean
parent: { full_name: string; html_url: string }
owner: { login: string }
created_at: string
}
}
}
export const forkRepoTool: ToolConfig<ForkRepoParams, ForkRepoResponse> = {
id: 'github_fork_repo',
name: 'GitHub Fork Repository',
description: 'Fork a repository to your account or an organization',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner to fork from',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name to fork',
},
organization: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Organization to fork into (omit to fork to your account)',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Custom name for the forked repository',
},
default_branch_only: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Only fork the default branch (default: false)',
default: false,
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => `https://api.github.com/repos/${params.owner}/${params.repo}/forks`,
method: 'POST',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
'X-GitHub-Api-Version': '2022-11-28',
}),
body: (params) => {
const body: Record<string, any> = {}
if (params.organization) body.organization = params.organization
if (params.name) body.name = params.name
if (params.default_branch_only !== undefined)
body.default_branch_only = params.default_branch_only
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
const content = `Forked repository: ${data.html_url}
Forked from: ${data.parent?.full_name ?? 'unknown'}
Clone URL: ${data.clone_url}
Default branch: ${data.default_branch}`
return {
success: true,
output: {
content,
metadata: {
id: data.id,
full_name: data.full_name,
html_url: data.html_url,
clone_url: data.clone_url,
ssh_url: data.ssh_url,
default_branch: data.default_branch,
fork: data.fork,
parent: {
full_name: data.parent?.full_name ?? '',
html_url: data.parent?.html_url ?? '',
},
owner: { login: data.owner?.login ?? 'unknown' },
created_at: data.created_at,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable result' },
metadata: {
type: 'object',
description: 'Forked repository metadata',
properties: {
id: { type: 'number', description: 'Repository ID' },
full_name: { type: 'string', description: 'Full name (owner/repo)' },
html_url: { type: 'string', description: 'Web URL' },
clone_url: { type: 'string', description: 'HTTPS clone URL' },
ssh_url: { type: 'string', description: 'SSH clone URL' },
default_branch: { type: 'string', description: 'Default branch' },
fork: { type: 'boolean', description: 'Is a fork' },
parent: { type: 'object', description: 'Parent repository' },
owner: { type: 'object', description: 'Owner info' },
created_at: { type: 'string', description: 'Creation date' },
},
},
},
}
export const forkRepoV2Tool: ToolConfig<ForkRepoParams, any> = {
id: 'github_fork_repo_v2',
name: forkRepoTool.name,
description: forkRepoTool.description,
version: '2.0.0',
params: forkRepoTool.params,
request: forkRepoTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
...data,
parent: data.parent ?? null,
source: data.source ?? null,
},
}
},
outputs: {
id: { type: 'number', description: 'Repository ID' },
full_name: { type: 'string', description: 'Full name' },
html_url: { type: 'string', description: 'Web URL' },
clone_url: { type: 'string', description: 'Clone URL' },
ssh_url: { type: 'string', description: 'SSH URL' },
default_branch: { type: 'string', description: 'Default branch' },
fork: { type: 'boolean', description: 'Is a fork' },
parent: { type: 'object', description: 'Parent repository', optional: true },
owner: { type: 'object', description: 'Owner' },
},
}

View File

@@ -0,0 +1,202 @@
import type { ToolConfig } from '@/tools/types'
interface GetCommitParams {
owner: string
repo: string
ref: string
apiKey: string
}
interface GetCommitResponse {
success: boolean
output: {
content: string
metadata: {
sha: string
html_url: string
message: string
author: { name: string; email: string; date: string; login?: string }
committer: { name: string; email: string; date: string; login?: string }
stats: { additions: number; deletions: number; total: number }
files: Array<{
filename: string
status: string
additions: number
deletions: number
changes: number
patch?: string
}>
parents: Array<{ sha: string; html_url: string }>
}
}
}
export const getCommitTool: ToolConfig<GetCommitParams, GetCommitResponse> = {
id: 'github_get_commit',
name: 'GitHub Get Commit',
description: 'Get detailed information about a specific commit including files changed and stats',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name',
},
ref: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Commit SHA, branch name, or tag name',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) =>
`https://api.github.com/repos/${params.owner}/${params.repo}/commits/${params.ref}`,
method: 'GET',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const files = (data.files ?? []).map((f: any) => ({
filename: f.filename,
status: f.status,
additions: f.additions,
deletions: f.deletions,
changes: f.changes,
patch: f.patch,
}))
const metadata = {
sha: data.sha,
html_url: data.html_url,
message: data.commit.message,
author: {
name: data.commit.author.name,
email: data.commit.author.email,
date: data.commit.author.date,
login: data.author?.login,
},
committer: {
name: data.commit.committer.name,
email: data.commit.committer.email,
date: data.commit.committer.date,
login: data.committer?.login,
},
stats: data.stats ?? { additions: 0, deletions: 0, total: 0 },
files,
parents: data.parents.map((p: any) => ({ sha: p.sha, html_url: p.html_url })),
}
const content = `Commit ${data.sha.substring(0, 7)}
Message: ${data.commit.message.split('\n')[0]}
Author: ${metadata.author.login ?? metadata.author.name} (${metadata.author.date})
Stats: +${metadata.stats.additions} -${metadata.stats.deletions} (${files.length} files)
${data.html_url}
Files changed:
${files.map((f: any) => ` ${f.status}: ${f.filename} (+${f.additions} -${f.deletions})`).join('\n')}`
return {
success: true,
output: {
content,
metadata,
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable commit details' },
metadata: {
type: 'object',
description: 'Commit metadata',
properties: {
sha: { type: 'string', description: 'Full commit SHA' },
html_url: { type: 'string', description: 'GitHub web URL' },
message: { type: 'string', description: 'Commit message' },
author: { type: 'object', description: 'Author info' },
committer: { type: 'object', description: 'Committer info' },
stats: {
type: 'object',
description: 'Change stats',
properties: {
additions: { type: 'number', description: 'Lines added' },
deletions: { type: 'number', description: 'Lines deleted' },
total: { type: 'number', description: 'Total changes' },
},
},
files: {
type: 'array',
description: 'Changed files',
items: {
type: 'object',
properties: {
filename: { type: 'string', description: 'File path' },
status: { type: 'string', description: 'Change type' },
additions: { type: 'number', description: 'Lines added' },
deletions: { type: 'number', description: 'Lines deleted' },
changes: { type: 'number', description: 'Total changes' },
patch: { type: 'string', description: 'Diff patch', optional: true },
},
},
},
parents: { type: 'array', description: 'Parent commits' },
},
},
},
}
export const getCommitV2Tool: ToolConfig<GetCommitParams, any> = {
id: 'github_get_commit_v2',
name: getCommitTool.name,
description: getCommitTool.description,
version: '2.0.0',
params: getCommitTool.params,
request: getCommitTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
...data,
author: data.author ?? null,
committer: data.committer ?? null,
stats: data.stats ?? null,
files: data.files ?? [],
},
}
},
outputs: {
sha: { type: 'string', description: 'Commit SHA' },
html_url: { type: 'string', description: 'Web URL' },
commit: { type: 'object', description: 'Commit data' },
author: { type: 'object', description: 'GitHub user', optional: true },
committer: { type: 'object', description: 'GitHub user', optional: true },
stats: { type: 'object', description: 'Change stats', optional: true },
files: { type: 'array', description: 'Changed files' },
parents: { type: 'array', description: 'Parent commits' },
},
}

View File

@@ -0,0 +1,177 @@
import type { ToolConfig } from '@/tools/types'
interface GetGistParams {
gist_id: string
apiKey: string
}
interface GetGistResponse {
success: boolean
output: {
content: string
metadata: {
id: string
html_url: string
git_pull_url: string
git_push_url: string
description: string | null
public: boolean
created_at: string
updated_at: string
files: Record<
string,
{ filename: string; type: string; language: string | null; size: number; content: string }
>
owner: { login: string }
comments: number
forks_url: string
commits_url: string
}
}
}
export const getGistTool: ToolConfig<GetGistParams, GetGistResponse> = {
id: 'github_get_gist',
name: 'GitHub Get Gist',
description: 'Get a gist by ID including its file contents',
version: '1.0.0',
params: {
gist_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The gist ID',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => `https://api.github.com/gists/${params.gist_id?.trim()}`,
method: 'GET',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const files: Record<
string,
{ filename: string; type: string; language: string | null; size: number; content: string }
> = {}
for (const [key, value] of Object.entries(data.files ?? {})) {
const file = value as any
files[key] = {
filename: file.filename,
type: file.type,
language: file.language ?? null,
size: file.size,
content: file.content ?? '',
}
}
const metadata = {
id: data.id,
html_url: data.html_url,
git_pull_url: data.git_pull_url,
git_push_url: data.git_push_url,
description: data.description ?? null,
public: data.public,
created_at: data.created_at,
updated_at: data.updated_at,
files,
owner: { login: data.owner?.login ?? 'unknown' },
comments: data.comments ?? 0,
forks_url: data.forks_url,
commits_url: data.commits_url,
}
const fileList = Object.entries(files)
.map(([name, f]) => `${name} (${f.language ?? 'unknown'}, ${f.size} bytes)`)
.join(', ')
const content = `Gist: ${data.html_url}
Description: ${data.description ?? 'No description'}
Public: ${data.public} | Comments: ${data.comments ?? 0}
Owner: ${data.owner?.login ?? 'unknown'}
Files: ${fileList}
${Object.entries(files)
.map(([name, f]) => `--- ${name} ---\n${f.content}`)
.join('\n\n')}`
return {
success: true,
output: {
content,
metadata,
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable gist with file contents' },
metadata: {
type: 'object',
description: 'Gist metadata',
properties: {
id: { type: 'string', description: 'Gist ID' },
html_url: { type: 'string', description: 'Web URL' },
git_pull_url: { type: 'string', description: 'Git pull URL' },
git_push_url: { type: 'string', description: 'Git push URL' },
description: { type: 'string', description: 'Description', optional: true },
public: { type: 'boolean', description: 'Is public' },
created_at: { type: 'string', description: 'Creation date' },
updated_at: { type: 'string', description: 'Update date' },
files: { type: 'object', description: 'Files with content' },
owner: { type: 'object', description: 'Owner info' },
comments: { type: 'number', description: 'Comment count' },
forks_url: { type: 'string', description: 'Forks URL' },
commits_url: { type: 'string', description: 'Commits URL' },
},
},
},
}
export const getGistV2Tool: ToolConfig<GetGistParams, any> = {
id: 'github_get_gist_v2',
name: getGistTool.name,
description: getGistTool.description,
version: '2.0.0',
params: getGistTool.params,
request: getGistTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
...data,
description: data.description ?? null,
files: data.files ?? {},
comments: data.comments ?? 0,
},
}
},
outputs: {
id: { type: 'string', description: 'Gist ID' },
html_url: { type: 'string', description: 'Web URL' },
description: { type: 'string', description: 'Description', optional: true },
public: { type: 'boolean', description: 'Is public' },
created_at: { type: 'string', description: 'Creation date' },
updated_at: { type: 'string', description: 'Update date' },
files: { type: 'object', description: 'Files with content' },
owner: { type: 'object', description: 'Owner info' },
comments: { type: 'number', description: 'Comment count' },
},
}

View File

@@ -0,0 +1,167 @@
import type { ToolConfig } from '@/tools/types'
interface GetMilestoneParams {
owner: string
repo: string
milestone_number: number
apiKey: string
}
interface GetMilestoneResponse {
success: boolean
output: {
content: string
metadata: {
number: number
title: string
description: string | null
state: string
html_url: string
due_on: string | null
open_issues: number
closed_issues: number
created_at: string
updated_at: string
closed_at: string | null
creator: { login: string }
}
}
}
export const getMilestoneTool: ToolConfig<GetMilestoneParams, GetMilestoneResponse> = {
id: 'github_get_milestone',
name: 'GitHub Get Milestone',
description: 'Get a specific milestone by number',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name',
},
milestone_number: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Milestone number',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) =>
`https://api.github.com/repos/${params.owner}/${params.repo}/milestones/${params.milestone_number}`,
method: 'GET',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const progress =
data.open_issues + data.closed_issues > 0
? Math.round((data.closed_issues / (data.open_issues + data.closed_issues)) * 100)
: 0
const content = `Milestone: ${data.title} (#${data.number})
State: ${data.state} | Progress: ${progress}% (${data.closed_issues}/${data.open_issues + data.closed_issues} issues)
Due: ${data.due_on ?? 'No due date'}
Description: ${data.description ?? 'No description'}
${data.html_url}`
return {
success: true,
output: {
content,
metadata: {
number: data.number,
title: data.title,
description: data.description ?? null,
state: data.state,
html_url: data.html_url,
due_on: data.due_on ?? null,
open_issues: data.open_issues,
closed_issues: data.closed_issues,
created_at: data.created_at,
updated_at: data.updated_at,
closed_at: data.closed_at ?? null,
creator: { login: data.creator?.login ?? 'unknown' },
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable milestone details' },
metadata: {
type: 'object',
description: 'Milestone metadata',
properties: {
number: { type: 'number', description: 'Milestone number' },
title: { type: 'string', description: 'Title' },
description: { type: 'string', description: 'Description', optional: true },
state: { type: 'string', description: 'State' },
html_url: { type: 'string', description: 'Web URL' },
due_on: { type: 'string', description: 'Due date', optional: true },
open_issues: { type: 'number', description: 'Open issues count' },
closed_issues: { type: 'number', description: 'Closed issues count' },
created_at: { type: 'string', description: 'Creation date' },
updated_at: { type: 'string', description: 'Update date' },
closed_at: { type: 'string', description: 'Close date', optional: true },
creator: { type: 'object', description: 'Creator info' },
},
},
},
}
export const getMilestoneV2Tool: ToolConfig<GetMilestoneParams, any> = {
id: 'github_get_milestone_v2',
name: getMilestoneTool.name,
description: getMilestoneTool.description,
version: '2.0.0',
params: getMilestoneTool.params,
request: getMilestoneTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
...data,
description: data.description ?? null,
due_on: data.due_on ?? null,
closed_at: data.closed_at ?? null,
},
}
},
outputs: {
number: { type: 'number', description: 'Milestone number' },
title: { type: 'string', description: 'Title' },
description: { type: 'string', description: 'Description', optional: true },
state: { type: 'string', description: 'State' },
html_url: { type: 'string', description: 'Web URL' },
due_on: { type: 'string', description: 'Due date', optional: true },
open_issues: { type: 'number', description: 'Open issues' },
closed_issues: { type: 'number', description: 'Closed issues' },
closed_at: { type: 'string', description: 'Close date', optional: true },
creator: { type: 'object', description: 'Creator' },
},
}

View File

@@ -1,27 +1,54 @@
import { addAssigneesTool, addAssigneesV2Tool } from '@/tools/github/add_assignees'
import { addLabelsTool, addLabelsV2Tool } from '@/tools/github/add_labels'
import { cancelWorkflowRunTool, cancelWorkflowRunV2Tool } from '@/tools/github/cancel_workflow_run'
import { checkStarTool, checkStarV2Tool } from '@/tools/github/check_star'
import { closeIssueTool, closeIssueV2Tool } from '@/tools/github/close_issue'
import { closePRTool, closePRV2Tool } from '@/tools/github/close_pr'
import { commentTool, commentV2Tool } from '@/tools/github/comment'
import { compareCommitsTool, compareCommitsV2Tool } from '@/tools/github/compare_commits'
import { createBranchTool, createBranchV2Tool } from '@/tools/github/create_branch'
import {
createCommentReactionTool,
createCommentReactionV2Tool,
} from '@/tools/github/create_comment_reaction'
import { createFileTool, createFileV2Tool } from '@/tools/github/create_file'
import { createGistTool, createGistV2Tool } from '@/tools/github/create_gist'
import { createIssueTool, createIssueV2Tool } from '@/tools/github/create_issue'
import {
createIssueReactionTool,
createIssueReactionV2Tool,
} from '@/tools/github/create_issue_reaction'
import { createMilestoneTool, createMilestoneV2Tool } from '@/tools/github/create_milestone'
import { createPRTool, createPRV2Tool } from '@/tools/github/create_pr'
import { createProjectTool, createProjectV2Tool } from '@/tools/github/create_project'
import { createReleaseTool, createReleaseV2Tool } from '@/tools/github/create_release'
import { deleteBranchTool, deleteBranchV2Tool } from '@/tools/github/delete_branch'
import { deleteCommentTool, deleteCommentV2Tool } from '@/tools/github/delete_comment'
import {
deleteCommentReactionTool,
deleteCommentReactionV2Tool,
} from '@/tools/github/delete_comment_reaction'
import { deleteFileTool, deleteFileV2Tool } from '@/tools/github/delete_file'
import { deleteGistTool, deleteGistV2Tool } from '@/tools/github/delete_gist'
import {
deleteIssueReactionTool,
deleteIssueReactionV2Tool,
} from '@/tools/github/delete_issue_reaction'
import { deleteMilestoneTool, deleteMilestoneV2Tool } from '@/tools/github/delete_milestone'
import { deleteProjectTool, deleteProjectV2Tool } from '@/tools/github/delete_project'
import { deleteReleaseTool, deleteReleaseV2Tool } from '@/tools/github/delete_release'
import { forkGistTool, forkGistV2Tool } from '@/tools/github/fork_gist'
import { forkRepoTool, forkRepoV2Tool } from '@/tools/github/fork_repo'
import { getBranchTool, getBranchV2Tool } from '@/tools/github/get_branch'
import {
getBranchProtectionTool,
getBranchProtectionV2Tool,
} from '@/tools/github/get_branch_protection'
import { getCommitTool, getCommitV2Tool } from '@/tools/github/get_commit'
import { getFileContentTool, getFileContentV2Tool } from '@/tools/github/get_file_content'
import { getGistTool, getGistV2Tool } from '@/tools/github/get_gist'
import { getIssueTool, getIssueV2Tool } from '@/tools/github/get_issue'
import { getMilestoneTool, getMilestoneV2Tool } from '@/tools/github/get_milestone'
import { getPRFilesTool, getPRFilesV2Tool } from '@/tools/github/get_pr_files'
import { getProjectTool, getProjectV2Tool } from '@/tools/github/get_project'
import { getReleaseTool, getReleaseV2Tool } from '@/tools/github/get_release'
@@ -31,12 +58,17 @@ import { getWorkflowRunTool, getWorkflowRunV2Tool } from '@/tools/github/get_wor
import { issueCommentTool, issueCommentV2Tool } from '@/tools/github/issue_comment'
import { latestCommitTool, latestCommitV2Tool } from '@/tools/github/latest_commit'
import { listBranchesTool, listBranchesV2Tool } from '@/tools/github/list_branches'
import { listCommitsTool, listCommitsV2Tool } from '@/tools/github/list_commits'
import { listForksTool, listForksV2Tool } from '@/tools/github/list_forks'
import { listGistsTool, listGistsV2Tool } from '@/tools/github/list_gists'
import { listIssueCommentsTool, listIssueCommentsV2Tool } from '@/tools/github/list_issue_comments'
import { listIssuesTool, listIssuesV2Tool } from '@/tools/github/list_issues'
import { listMilestonesTool, listMilestonesV2Tool } from '@/tools/github/list_milestones'
import { listPRCommentsTool, listPRCommentsV2Tool } from '@/tools/github/list_pr_comments'
import { listProjectsTool, listProjectsV2Tool } from '@/tools/github/list_projects'
import { listPRsTool, listPRsV2Tool } from '@/tools/github/list_prs'
import { listReleasesTool, listReleasesV2Tool } from '@/tools/github/list_releases'
import { listStargazersTool, listStargazersV2Tool } from '@/tools/github/list_stargazers'
import { listWorkflowRunsTool, listWorkflowRunsV2Tool } from '@/tools/github/list_workflow_runs'
import { listWorkflowsTool, listWorkflowsV2Tool } from '@/tools/github/list_workflows'
import { mergePRTool, mergePRV2Tool } from '@/tools/github/merge_pr'
@@ -45,20 +77,38 @@ import { removeLabelTool, removeLabelV2Tool } from '@/tools/github/remove_label'
import { repoInfoTool, repoInfoV2Tool } from '@/tools/github/repo_info'
import { requestReviewersTool, requestReviewersV2Tool } from '@/tools/github/request_reviewers'
import { rerunWorkflowTool, rerunWorkflowV2Tool } from '@/tools/github/rerun_workflow'
import { searchCodeTool, searchCodeV2Tool } from '@/tools/github/search_code'
import { searchCommitsTool, searchCommitsV2Tool } from '@/tools/github/search_commits'
import { searchIssuesTool, searchIssuesV2Tool } from '@/tools/github/search_issues'
import { searchReposTool, searchReposV2Tool } from '@/tools/github/search_repos'
import { searchUsersTool, searchUsersV2Tool } from '@/tools/github/search_users'
import { starGistTool, starGistV2Tool } from '@/tools/github/star_gist'
import { starRepoTool, starRepoV2Tool } from '@/tools/github/star_repo'
import { triggerWorkflowTool, triggerWorkflowV2Tool } from '@/tools/github/trigger_workflow'
import { unstarGistTool, unstarGistV2Tool } from '@/tools/github/unstar_gist'
import { unstarRepoTool, unstarRepoV2Tool } from '@/tools/github/unstar_repo'
import {
updateBranchProtectionTool,
updateBranchProtectionV2Tool,
} from '@/tools/github/update_branch_protection'
import { updateCommentTool, updateCommentV2Tool } from '@/tools/github/update_comment'
import { updateFileTool, updateFileV2Tool } from '@/tools/github/update_file'
import { updateGistTool, updateGistV2Tool } from '@/tools/github/update_gist'
import { updateIssueTool, updateIssueV2Tool } from '@/tools/github/update_issue'
import { updateMilestoneTool, updateMilestoneV2Tool } from '@/tools/github/update_milestone'
import { updatePRTool, updatePRV2Tool } from '@/tools/github/update_pr'
import { updateProjectTool, updateProjectV2Tool } from '@/tools/github/update_project'
import { updateReleaseTool, updateReleaseV2Tool } from '@/tools/github/update_release'
// Existing exports
export const githubAddAssigneesTool = addAssigneesTool
export const githubAddAssigneesV2Tool = addAssigneesV2Tool
export const githubAddLabelsTool = addLabelsTool
export const githubAddLabelsV2Tool = addLabelsV2Tool
export const githubCancelWorkflowRunTool = cancelWorkflowRunTool
export const githubCancelWorkflowRunV2Tool = cancelWorkflowRunV2Tool
export const githubCloseIssueTool = closeIssueTool
export const githubCloseIssueV2Tool = closeIssueV2Tool
export const githubClosePRTool = closePRTool
export const githubClosePRV2Tool = closePRV2Tool
export const githubCommentTool = commentTool
@@ -67,6 +117,8 @@ export const githubCreateBranchTool = createBranchTool
export const githubCreateBranchV2Tool = createBranchV2Tool
export const githubCreateFileTool = createFileTool
export const githubCreateFileV2Tool = createFileV2Tool
export const githubCreateIssueTool = createIssueTool
export const githubCreateIssueV2Tool = createIssueV2Tool
export const githubCreatePRTool = createPRTool
export const githubCreatePRV2Tool = createPRV2Tool
export const githubCreateProjectTool = createProjectTool
@@ -89,6 +141,8 @@ export const githubGetBranchProtectionTool = getBranchProtectionTool
export const githubGetBranchProtectionV2Tool = getBranchProtectionV2Tool
export const githubGetFileContentTool = getFileContentTool
export const githubGetFileContentV2Tool = getFileContentV2Tool
export const githubGetIssueTool = getIssueTool
export const githubGetIssueV2Tool = getIssueV2Tool
export const githubGetPRFilesTool = getPRFilesTool
export const githubGetPRFilesV2Tool = getPRFilesV2Tool
export const githubGetProjectTool = getProjectTool
@@ -109,6 +163,8 @@ export const githubListBranchesTool = listBranchesTool
export const githubListBranchesV2Tool = listBranchesV2Tool
export const githubListIssueCommentsTool = listIssueCommentsTool
export const githubListIssueCommentsV2Tool = listIssueCommentsV2Tool
export const githubListIssuesTool = listIssuesTool
export const githubListIssuesV2Tool = listIssuesV2Tool
export const githubListPRCommentsTool = listPRCommentsTool
export const githubListPRCommentsV2Tool = listPRCommentsV2Tool
export const githubListPRsTool = listPRsTool
@@ -125,6 +181,8 @@ export const githubMergePRTool = mergePRTool
export const githubMergePRV2Tool = mergePRV2Tool
export const githubPrTool = prTool
export const githubPrV2Tool = prV2Tool
export const githubRemoveLabelTool = removeLabelTool
export const githubRemoveLabelV2Tool = removeLabelV2Tool
export const githubRepoInfoTool = repoInfoTool
export const githubRepoInfoV2Tool = repoInfoV2Tool
export const githubRequestReviewersTool = requestReviewersTool
@@ -139,25 +197,87 @@ export const githubUpdateCommentTool = updateCommentTool
export const githubUpdateCommentV2Tool = updateCommentV2Tool
export const githubUpdateFileTool = updateFileTool
export const githubUpdateFileV2Tool = updateFileV2Tool
export const githubUpdateIssueTool = updateIssueTool
export const githubUpdateIssueV2Tool = updateIssueV2Tool
export const githubUpdatePRTool = updatePRTool
export const githubUpdatePRV2Tool = updatePRV2Tool
export const githubUpdateProjectTool = updateProjectTool
export const githubUpdateProjectV2Tool = updateProjectV2Tool
export const githubUpdateReleaseTool = updateReleaseTool
export const githubUpdateReleaseV2Tool = updateReleaseV2Tool
export const githubAddAssigneesTool = addAssigneesTool
export const githubAddAssigneesV2Tool = addAssigneesV2Tool
export const githubAddLabelsTool = addLabelsTool
export const githubAddLabelsV2Tool = addLabelsV2Tool
export const githubCloseIssueTool = closeIssueTool
export const githubCloseIssueV2Tool = closeIssueV2Tool
export const githubCreateIssueTool = createIssueTool
export const githubCreateIssueV2Tool = createIssueV2Tool
export const githubGetIssueTool = getIssueTool
export const githubGetIssueV2Tool = getIssueV2Tool
export const githubListIssuesTool = listIssuesTool
export const githubListIssuesV2Tool = listIssuesV2Tool
export const githubRemoveLabelTool = removeLabelTool
export const githubRemoveLabelV2Tool = removeLabelV2Tool
export const githubUpdateIssueTool = updateIssueTool
export const githubUpdateIssueV2Tool = updateIssueV2Tool
// New exports - Search tools
export const githubSearchCodeTool = searchCodeTool
export const githubSearchCodeV2Tool = searchCodeV2Tool
export const githubSearchCommitsTool = searchCommitsTool
export const githubSearchCommitsV2Tool = searchCommitsV2Tool
export const githubSearchIssuesTool = searchIssuesTool
export const githubSearchIssuesV2Tool = searchIssuesV2Tool
export const githubSearchReposTool = searchReposTool
export const githubSearchReposV2Tool = searchReposV2Tool
export const githubSearchUsersTool = searchUsersTool
export const githubSearchUsersV2Tool = searchUsersV2Tool
// New exports - Commit tools
export const githubListCommitsTool = listCommitsTool
export const githubListCommitsV2Tool = listCommitsV2Tool
export const githubGetCommitTool = getCommitTool
export const githubGetCommitV2Tool = getCommitV2Tool
export const githubCompareCommitsTool = compareCommitsTool
export const githubCompareCommitsV2Tool = compareCommitsV2Tool
// New exports - Gist tools
export const githubCreateGistTool = createGistTool
export const githubCreateGistV2Tool = createGistV2Tool
export const githubGetGistTool = getGistTool
export const githubGetGistV2Tool = getGistV2Tool
export const githubListGistsTool = listGistsTool
export const githubListGistsV2Tool = listGistsV2Tool
export const githubUpdateGistTool = updateGistTool
export const githubUpdateGistV2Tool = updateGistV2Tool
export const githubDeleteGistTool = deleteGistTool
export const githubDeleteGistV2Tool = deleteGistV2Tool
export const githubForkGistTool = forkGistTool
export const githubForkGistV2Tool = forkGistV2Tool
export const githubStarGistTool = starGistTool
export const githubStarGistV2Tool = starGistV2Tool
export const githubUnstarGistTool = unstarGistTool
export const githubUnstarGistV2Tool = unstarGistV2Tool
// New exports - Fork tools
export const githubForkRepoTool = forkRepoTool
export const githubForkRepoV2Tool = forkRepoV2Tool
export const githubListForksTool = listForksTool
export const githubListForksV2Tool = listForksV2Tool
// New exports - Milestone tools
export const githubCreateMilestoneTool = createMilestoneTool
export const githubCreateMilestoneV2Tool = createMilestoneV2Tool
export const githubGetMilestoneTool = getMilestoneTool
export const githubGetMilestoneV2Tool = getMilestoneV2Tool
export const githubListMilestonesTool = listMilestonesTool
export const githubListMilestonesV2Tool = listMilestonesV2Tool
export const githubUpdateMilestoneTool = updateMilestoneTool
export const githubUpdateMilestoneV2Tool = updateMilestoneV2Tool
export const githubDeleteMilestoneTool = deleteMilestoneTool
export const githubDeleteMilestoneV2Tool = deleteMilestoneV2Tool
// New exports - Reaction tools
export const githubCreateIssueReactionTool = createIssueReactionTool
export const githubCreateIssueReactionV2Tool = createIssueReactionV2Tool
export const githubDeleteIssueReactionTool = deleteIssueReactionTool
export const githubDeleteIssueReactionV2Tool = deleteIssueReactionV2Tool
export const githubCreateCommentReactionTool = createCommentReactionTool
export const githubCreateCommentReactionV2Tool = createCommentReactionV2Tool
export const githubDeleteCommentReactionTool = deleteCommentReactionTool
export const githubDeleteCommentReactionV2Tool = deleteCommentReactionV2Tool
// New exports - Star tools
export const githubStarRepoTool = starRepoTool
export const githubStarRepoV2Tool = starRepoV2Tool
export const githubUnstarRepoTool = unstarRepoTool
export const githubUnstarRepoV2Tool = unstarRepoV2Tool
export const githubCheckStarTool = checkStarTool
export const githubCheckStarV2Tool = checkStarV2Tool
export const githubListStargazersTool = listStargazersTool
export const githubListStargazersV2Tool = listStargazersV2Tool

View File

@@ -0,0 +1,243 @@
import type { ToolConfig } from '@/tools/types'
interface ListCommitsParams {
owner: string
repo: string
sha?: string
path?: string
author?: string
committer?: string
since?: string
until?: string
per_page?: number
page?: number
apiKey: string
}
interface ListCommitsResponse {
success: boolean
output: {
content: string
metadata: {
commits: Array<{
sha: string
html_url: string
message: string
author: { name: string; email: string; date: string; login?: string }
committer: { name: string; email: string; date: string; login?: string }
}>
count: number
}
}
}
export const listCommitsTool: ToolConfig<ListCommitsParams, ListCommitsResponse> = {
id: 'github_list_commits',
name: 'GitHub List Commits',
description:
'List commits in a repository with optional filtering by SHA, path, author, committer, or date range',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name',
},
sha: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'SHA or branch to start listing commits from',
},
path: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Only commits containing this file path',
},
author: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'GitHub login or email address to filter by author',
},
committer: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'GitHub login or email address to filter by committer',
},
since: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Only commits after this date (ISO 8601 format)',
},
until: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Only commits before this date (ISO 8601 format)',
},
per_page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Results per page (max 100, default: 30)',
default: 30,
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number (default: 1)',
default: 1,
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => {
const url = new URL(`https://api.github.com/repos/${params.owner}/${params.repo}/commits`)
if (params.sha) url.searchParams.append('sha', params.sha)
if (params.path) url.searchParams.append('path', params.path)
if (params.author) url.searchParams.append('author', params.author)
if (params.committer) url.searchParams.append('committer', params.committer)
if (params.since) url.searchParams.append('since', params.since)
if (params.until) url.searchParams.append('until', params.until)
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
if (params.page) url.searchParams.append('page', String(params.page))
return url.toString()
},
method: 'GET',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const commits = data.map((item: any) => ({
sha: item.sha,
html_url: item.html_url,
message: item.commit.message,
author: {
name: item.commit.author.name,
email: item.commit.author.email,
date: item.commit.author.date,
login: item.author?.login,
},
committer: {
name: item.commit.committer.name,
email: item.commit.committer.email,
date: item.commit.committer.date,
login: item.committer?.login,
},
}))
const content = `Found ${commits.length} commit(s):
${commits
.map(
(c: any) =>
`${c.sha.substring(0, 7)} - ${c.message.split('\n')[0]}
Author: ${c.author.login ?? c.author.name} (${c.author.date})
${c.html_url}`
)
.join('\n\n')}`
return {
success: true,
output: {
content,
metadata: {
commits,
count: commits.length,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable commit list' },
metadata: {
type: 'object',
description: 'Commits metadata',
properties: {
commits: {
type: 'array',
description: 'Array of commits',
items: {
type: 'object',
properties: {
sha: { type: 'string', description: 'Commit SHA' },
html_url: { type: 'string', description: 'GitHub web URL' },
message: { type: 'string', description: 'Commit message' },
author: { type: 'object', description: 'Author info' },
committer: { type: 'object', description: 'Committer info' },
},
},
},
count: { type: 'number', description: 'Number of commits returned' },
},
},
},
}
export const listCommitsV2Tool: ToolConfig<ListCommitsParams, any> = {
id: 'github_list_commits_v2',
name: listCommitsTool.name,
description: listCommitsTool.description,
version: '2.0.0',
params: listCommitsTool.params,
request: listCommitsTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
items: data.map((item: any) => ({
...item,
author: item.author ?? null,
committer: item.committer ?? null,
})),
count: data.length,
},
}
},
outputs: {
items: {
type: 'array',
description: 'Array of commit objects from GitHub API',
items: {
type: 'object',
properties: {
sha: { type: 'string', description: 'Commit SHA' },
html_url: { type: 'string', description: 'Web URL' },
commit: { type: 'object', description: 'Commit data' },
author: { type: 'object', description: 'GitHub user', optional: true },
committer: { type: 'object', description: 'GitHub user', optional: true },
parents: { type: 'array', description: 'Parent commits' },
},
},
},
count: { type: 'number', description: 'Number of commits returned' },
},
}

View File

@@ -0,0 +1,201 @@
import type { ToolConfig } from '@/tools/types'
interface ListForksParams {
owner: string
repo: string
sort?: 'newest' | 'oldest' | 'stargazers' | 'watchers'
per_page?: number
page?: number
apiKey: string
}
interface ListForksResponse {
success: boolean
output: {
content: string
metadata: {
forks: Array<{
id: number
full_name: string
html_url: string
owner: { login: string }
stargazers_count: number
forks_count: number
created_at: string
updated_at: string
default_branch: string
}>
count: number
}
}
}
export const listForksTool: ToolConfig<ListForksParams, ListForksResponse> = {
id: 'github_list_forks',
name: 'GitHub List Forks',
description: 'List forks of a repository',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name',
},
sort: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort by: newest, oldest, stargazers, watchers (default: newest)',
default: 'newest',
},
per_page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Results per page (max 100, default: 30)',
default: 30,
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number (default: 1)',
default: 1,
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => {
const url = new URL(`https://api.github.com/repos/${params.owner}/${params.repo}/forks`)
if (params.sort) url.searchParams.append('sort', params.sort)
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
if (params.page) url.searchParams.append('page', String(params.page))
return url.toString()
},
method: 'GET',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const forks = data.map((f: any) => ({
id: f.id,
full_name: f.full_name,
html_url: f.html_url,
owner: { login: f.owner?.login ?? 'unknown' },
stargazers_count: f.stargazers_count,
forks_count: f.forks_count,
created_at: f.created_at,
updated_at: f.updated_at,
default_branch: f.default_branch,
}))
const content = `Found ${forks.length} fork(s):
${forks
.map(
(f: any) =>
`${f.full_name}${f.stargazers_count}
Owner: ${f.owner.login}
${f.html_url}`
)
.join('\n\n')}`
return {
success: true,
output: {
content,
metadata: {
forks,
count: forks.length,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable fork list' },
metadata: {
type: 'object',
description: 'Forks metadata',
properties: {
forks: {
type: 'array',
description: 'Array of forks',
items: {
type: 'object',
properties: {
id: { type: 'number', description: 'Repository ID' },
full_name: { type: 'string', description: 'Full name' },
html_url: { type: 'string', description: 'Web URL' },
owner: { type: 'object', description: 'Owner info' },
stargazers_count: { type: 'number', description: 'Star count' },
forks_count: { type: 'number', description: 'Fork count' },
created_at: { type: 'string', description: 'Creation date' },
updated_at: { type: 'string', description: 'Update date' },
default_branch: { type: 'string', description: 'Default branch' },
},
},
},
count: { type: 'number', description: 'Number of forks returned' },
},
},
},
}
export const listForksV2Tool: ToolConfig<ListForksParams, any> = {
id: 'github_list_forks_v2',
name: listForksTool.name,
description: listForksTool.description,
version: '2.0.0',
params: listForksTool.params,
request: listForksTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
items: data,
count: data.length,
},
}
},
outputs: {
items: {
type: 'array',
description: 'Array of fork repository objects from GitHub API',
items: {
type: 'object',
properties: {
id: { type: 'number', description: 'Repository ID' },
full_name: { type: 'string', description: 'Full name' },
html_url: { type: 'string', description: 'Web URL' },
owner: { type: 'object', description: 'Owner' },
stargazers_count: { type: 'number', description: 'Stars' },
forks_count: { type: 'number', description: 'Forks' },
},
},
},
count: { type: 'number', description: 'Number of forks returned' },
},
}

View File

@@ -0,0 +1,201 @@
import type { ToolConfig } from '@/tools/types'
interface ListGistsParams {
username?: string
since?: string
per_page?: number
page?: number
apiKey: string
}
interface ListGistsResponse {
success: boolean
output: {
content: string
metadata: {
gists: Array<{
id: string
html_url: string
description: string | null
public: boolean
created_at: string
updated_at: string
files: string[]
owner: { login: string }
comments: number
}>
count: number
}
}
}
export const listGistsTool: ToolConfig<ListGistsParams, ListGistsResponse> = {
id: 'github_list_gists',
name: 'GitHub List Gists',
description: 'List gists for a user or the authenticated user',
version: '1.0.0',
params: {
username: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "GitHub username (omit for authenticated user's gists)",
},
since: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Only gists updated after this time (ISO 8601)',
},
per_page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Results per page (max 100, default: 30)',
default: 30,
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number (default: 1)',
default: 1,
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => {
const baseUrl = params.username
? `https://api.github.com/users/${params.username}/gists`
: 'https://api.github.com/gists'
const url = new URL(baseUrl)
if (params.since) url.searchParams.append('since', params.since)
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
if (params.page) url.searchParams.append('page', String(params.page))
return url.toString()
},
method: 'GET',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const gists = data.map((g: any) => ({
id: g.id,
html_url: g.html_url,
description: g.description ?? null,
public: g.public,
created_at: g.created_at,
updated_at: g.updated_at,
files: Object.keys(g.files ?? {}),
owner: { login: g.owner?.login ?? 'unknown' },
comments: g.comments ?? 0,
}))
const content = `Found ${gists.length} gist(s):
${gists
.map(
(g: any) =>
`${g.id} - ${g.description ?? 'No description'} (${g.public ? 'public' : 'secret'})
Files: ${g.files.join(', ')}
${g.html_url}`
)
.join('\n\n')}`
return {
success: true,
output: {
content,
metadata: {
gists,
count: gists.length,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable gist list' },
metadata: {
type: 'object',
description: 'Gists metadata',
properties: {
gists: {
type: 'array',
description: 'Array of gists',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Gist ID' },
html_url: { type: 'string', description: 'Web URL' },
description: { type: 'string', description: 'Description', optional: true },
public: { type: 'boolean', description: 'Is public' },
created_at: { type: 'string', description: 'Creation date' },
updated_at: { type: 'string', description: 'Update date' },
files: { type: 'array', description: 'File names' },
owner: { type: 'object', description: 'Owner info' },
comments: { type: 'number', description: 'Comment count' },
},
},
},
count: { type: 'number', description: 'Number of gists returned' },
},
},
},
}
export const listGistsV2Tool: ToolConfig<ListGistsParams, any> = {
id: 'github_list_gists_v2',
name: listGistsTool.name,
description: listGistsTool.description,
version: '2.0.0',
params: listGistsTool.params,
request: listGistsTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
items: data.map((g: any) => ({
...g,
description: g.description ?? null,
files: g.files ?? {},
comments: g.comments ?? 0,
})),
count: data.length,
},
}
},
outputs: {
items: {
type: 'array',
description: 'Array of gist objects from GitHub API',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Gist ID' },
html_url: { type: 'string', description: 'Web URL' },
description: { type: 'string', description: 'Description', optional: true },
public: { type: 'boolean', description: 'Is public' },
files: { type: 'object', description: 'Files' },
owner: { type: 'object', description: 'Owner' },
},
},
},
count: { type: 'number', description: 'Number of gists returned' },
},
}

View File

@@ -0,0 +1,226 @@
import type { ToolConfig } from '@/tools/types'
interface ListMilestonesParams {
owner: string
repo: string
state?: 'open' | 'closed' | 'all'
sort?: 'due_on' | 'completeness'
direction?: 'asc' | 'desc'
per_page?: number
page?: number
apiKey: string
}
interface ListMilestonesResponse {
success: boolean
output: {
content: string
metadata: {
milestones: Array<{
number: number
title: string
description: string | null
state: string
html_url: string
due_on: string | null
open_issues: number
closed_issues: number
}>
count: number
}
}
}
export const listMilestonesTool: ToolConfig<ListMilestonesParams, ListMilestonesResponse> = {
id: 'github_list_milestones',
name: 'GitHub List Milestones',
description: 'List milestones in a repository',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name',
},
state: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by state: open, closed, all (default: open)',
default: 'open',
},
sort: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort by: due_on or completeness (default: due_on)',
default: 'due_on',
},
direction: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort direction: asc or desc (default: asc)',
default: 'asc',
},
per_page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Results per page (max 100, default: 30)',
default: 30,
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number (default: 1)',
default: 1,
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => {
const url = new URL(`https://api.github.com/repos/${params.owner}/${params.repo}/milestones`)
if (params.state) url.searchParams.append('state', params.state)
if (params.sort) url.searchParams.append('sort', params.sort)
if (params.direction) url.searchParams.append('direction', params.direction)
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
if (params.page) url.searchParams.append('page', String(params.page))
return url.toString()
},
method: 'GET',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const milestones = data.map((m: any) => {
const total = m.open_issues + m.closed_issues
const progress = total > 0 ? Math.round((m.closed_issues / total) * 100) : 0
return {
number: m.number,
title: m.title,
description: m.description ?? null,
state: m.state,
html_url: m.html_url,
due_on: m.due_on ?? null,
open_issues: m.open_issues,
closed_issues: m.closed_issues,
progress,
}
})
const content = `Found ${milestones.length} milestone(s):
${milestones
.map(
(m: any) =>
`#${m.number}: ${m.title} (${m.state})
Progress: ${m.progress}% (${m.closed_issues}/${m.open_issues + m.closed_issues} issues)
Due: ${m.due_on ?? 'No due date'}
${m.html_url}`
)
.join('\n\n')}`
return {
success: true,
output: {
content,
metadata: {
milestones,
count: milestones.length,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable milestone list' },
metadata: {
type: 'object',
description: 'Milestones metadata',
properties: {
milestones: {
type: 'array',
description: 'Array of milestones',
items: {
type: 'object',
properties: {
number: { type: 'number', description: 'Milestone number' },
title: { type: 'string', description: 'Title' },
description: { type: 'string', description: 'Description', optional: true },
state: { type: 'string', description: 'State' },
html_url: { type: 'string', description: 'Web URL' },
due_on: { type: 'string', description: 'Due date', optional: true },
open_issues: { type: 'number', description: 'Open issues' },
closed_issues: { type: 'number', description: 'Closed issues' },
},
},
},
count: { type: 'number', description: 'Number of milestones returned' },
},
},
},
}
export const listMilestonesV2Tool: ToolConfig<ListMilestonesParams, any> = {
id: 'github_list_milestones_v2',
name: listMilestonesTool.name,
description: listMilestonesTool.description,
version: '2.0.0',
params: listMilestonesTool.params,
request: listMilestonesTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
items: data.map((m: any) => ({
...m,
description: m.description ?? null,
due_on: m.due_on ?? null,
})),
count: data.length,
},
}
},
outputs: {
items: {
type: 'array',
description: 'Array of milestone objects from GitHub API',
items: {
type: 'object',
properties: {
number: { type: 'number', description: 'Milestone number' },
title: { type: 'string', description: 'Title' },
state: { type: 'string', description: 'State' },
html_url: { type: 'string', description: 'Web URL' },
open_issues: { type: 'number', description: 'Open issues' },
closed_issues: { type: 'number', description: 'Closed issues' },
},
},
},
count: { type: 'number', description: 'Number of milestones returned' },
},
}

View File

@@ -0,0 +1,172 @@
import type { ToolConfig } from '@/tools/types'
interface ListStargazersParams {
owner: string
repo: string
per_page?: number
page?: number
apiKey: string
}
interface ListStargazersResponse {
success: boolean
output: {
content: string
metadata: {
stargazers: Array<{
login: string
id: number
avatar_url: string
html_url: string
type: string
}>
count: number
}
}
}
export const listStargazersTool: ToolConfig<ListStargazersParams, ListStargazersResponse> = {
id: 'github_list_stargazers',
name: 'GitHub List Stargazers',
description: 'List users who have starred a repository',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name',
},
per_page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Results per page (max 100, default: 30)',
default: 30,
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number (default: 1)',
default: 1,
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => {
const url = new URL(`https://api.github.com/repos/${params.owner}/${params.repo}/stargazers`)
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
if (params.page) url.searchParams.append('page', String(params.page))
return url.toString()
},
method: 'GET',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const stargazers = data.map((u: any) => ({
login: u.login,
id: u.id,
avatar_url: u.avatar_url,
html_url: u.html_url,
type: u.type,
}))
const content = `Found ${stargazers.length} stargazer(s):
${stargazers.map((u: any) => `@${u.login} (${u.type}) - ${u.html_url}`).join('\n')}`
return {
success: true,
output: {
content,
metadata: {
stargazers,
count: stargazers.length,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable stargazer list' },
metadata: {
type: 'object',
description: 'Stargazers metadata',
properties: {
stargazers: {
type: 'array',
description: 'Array of stargazers',
items: {
type: 'object',
properties: {
login: { type: 'string', description: 'Username' },
id: { type: 'number', description: 'User ID' },
avatar_url: { type: 'string', description: 'Avatar URL' },
html_url: { type: 'string', description: 'Profile URL' },
type: { type: 'string', description: 'User or Organization' },
},
},
},
count: { type: 'number', description: 'Number of stargazers returned' },
},
},
},
}
export const listStargazersV2Tool: ToolConfig<ListStargazersParams, any> = {
id: 'github_list_stargazers_v2',
name: listStargazersTool.name,
description: listStargazersTool.description,
version: '2.0.0',
params: listStargazersTool.params,
request: listStargazersTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
items: data,
count: data.length,
},
}
},
outputs: {
items: {
type: 'array',
description: 'Array of user objects from GitHub API',
items: {
type: 'object',
properties: {
login: { type: 'string', description: 'Username' },
id: { type: 'number', description: 'User ID' },
avatar_url: { type: 'string', description: 'Avatar URL' },
html_url: { type: 'string', description: 'Profile URL' },
type: { type: 'string', description: 'User or Organization' },
},
},
},
count: { type: 'number', description: 'Number of stargazers returned' },
},
}

View File

@@ -0,0 +1,211 @@
import type { ToolConfig } from '@/tools/types'
interface SearchCodeParams {
q: string
sort?: 'indexed'
order?: 'asc' | 'desc'
per_page?: number
page?: number
apiKey: string
}
interface SearchCodeResponse {
success: boolean
output: {
content: string
metadata: {
total_count: number
incomplete_results: boolean
items: Array<{
name: string
path: string
sha: string
html_url: string
repository: {
full_name: string
html_url: string
}
}>
}
}
}
export const searchCodeTool: ToolConfig<SearchCodeParams, SearchCodeResponse> = {
id: 'github_search_code',
name: 'GitHub Search Code',
description:
'Search for code across GitHub repositories. Use qualifiers like repo:owner/name, language:js, path:src, extension:py',
version: '1.0.0',
params: {
q: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Search query with optional qualifiers (repo:, language:, path:, extension:, user:, org:)',
},
sort: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort by indexed date (default: best match)',
},
order: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort order: asc or desc (default: desc)',
},
per_page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Results per page (max 100, default: 30)',
default: 30,
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number (default: 1)',
default: 1,
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => {
const url = new URL('https://api.github.com/search/code')
url.searchParams.append('q', params.q)
if (params.sort) url.searchParams.append('sort', params.sort)
if (params.order) url.searchParams.append('order', params.order)
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
if (params.page) url.searchParams.append('page', String(params.page))
return url.toString()
},
method: 'GET',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const items = data.items.map((item: any) => ({
name: item.name,
path: item.path,
sha: item.sha,
html_url: item.html_url,
repository: {
full_name: item.repository.full_name,
html_url: item.repository.html_url,
},
}))
const content = `Found ${data.total_count} code result(s)${data.incomplete_results ? ' (incomplete)' : ''}:
${items
.map(
(item: any) =>
`- ${item.repository.full_name}/${item.path}
${item.html_url}`
)
.join('\n')}`
return {
success: true,
output: {
content,
metadata: {
total_count: data.total_count,
incomplete_results: data.incomplete_results,
items,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable search results' },
metadata: {
type: 'object',
description: 'Search results metadata',
properties: {
total_count: { type: 'number', description: 'Total matching results' },
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
items: {
type: 'array',
description: 'Array of code matches',
items: {
type: 'object',
properties: {
name: { type: 'string', description: 'File name' },
path: { type: 'string', description: 'File path' },
sha: { type: 'string', description: 'Blob SHA' },
html_url: { type: 'string', description: 'GitHub web URL' },
repository: {
type: 'object',
description: 'Repository info',
properties: {
full_name: { type: 'string', description: 'Repository full name' },
html_url: { type: 'string', description: 'Repository URL' },
},
},
},
},
},
},
},
},
}
export const searchCodeV2Tool: ToolConfig<SearchCodeParams, any> = {
id: 'github_search_code_v2',
name: searchCodeTool.name,
description: searchCodeTool.description,
version: '2.0.0',
params: searchCodeTool.params,
request: searchCodeTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
total_count: data.total_count,
incomplete_results: data.incomplete_results,
items: data.items.map((item: any) => ({
...item,
text_matches: item.text_matches ?? [],
})),
},
}
},
outputs: {
total_count: { type: 'number', description: 'Total matching results' },
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
items: {
type: 'array',
description: 'Array of code matches from GitHub API',
items: {
type: 'object',
properties: {
name: { type: 'string', description: 'File name' },
path: { type: 'string', description: 'File path' },
sha: { type: 'string', description: 'Blob SHA' },
html_url: { type: 'string', description: 'GitHub web URL' },
repository: { type: 'object', description: 'Repository object' },
},
},
},
},
}

View File

@@ -0,0 +1,224 @@
import type { ToolConfig } from '@/tools/types'
interface SearchCommitsParams {
q: string
sort?: 'author-date' | 'committer-date'
order?: 'asc' | 'desc'
per_page?: number
page?: number
apiKey: string
}
interface SearchCommitsResponse {
success: boolean
output: {
content: string
metadata: {
total_count: number
incomplete_results: boolean
items: Array<{
sha: string
html_url: string
commit: {
message: string
author: { name: string; email: string; date: string }
committer: { name: string; email: string; date: string }
}
author: { login: string } | null
committer: { login: string } | null
repository: { full_name: string; html_url: string }
}>
}
}
}
export const searchCommitsTool: ToolConfig<SearchCommitsParams, SearchCommitsResponse> = {
id: 'github_search_commits',
name: 'GitHub Search Commits',
description:
'Search for commits across GitHub. Use qualifiers like repo:owner/name, author:user, committer:user, author-date:>2023-01-01',
version: '1.0.0',
params: {
q: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Search query with optional qualifiers (repo:, author:, committer:, author-date:, committer-date:, merge:true/false)',
},
sort: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort by: author-date or committer-date (default: best match)',
},
order: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort order: asc or desc (default: desc)',
},
per_page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Results per page (max 100, default: 30)',
default: 30,
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number (default: 1)',
default: 1,
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => {
const url = new URL('https://api.github.com/search/commits')
url.searchParams.append('q', params.q)
if (params.sort) url.searchParams.append('sort', params.sort)
if (params.order) url.searchParams.append('order', params.order)
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
if (params.page) url.searchParams.append('page', String(params.page))
return url.toString()
},
method: 'GET',
headers: (params) => ({
Accept: 'application/vnd.github.cloak-preview+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const items = data.items.map((item: any) => ({
sha: item.sha,
html_url: item.html_url,
commit: {
message: item.commit.message,
author: item.commit.author,
committer: item.commit.committer,
},
author: item.author ? { login: item.author.login } : null,
committer: item.committer ? { login: item.committer.login } : null,
repository: {
full_name: item.repository.full_name,
html_url: item.repository.html_url,
},
}))
const content = `Found ${data.total_count} commit(s)${data.incomplete_results ? ' (incomplete)' : ''}:
${items
.map(
(item: any) =>
`${item.sha.substring(0, 7)} - ${item.commit.message.split('\n')[0]}
Repository: ${item.repository.full_name}
Author: ${item.author?.login ?? item.commit.author.name} (${item.commit.author.date})
${item.html_url}`
)
.join('\n\n')}`
return {
success: true,
output: {
content,
metadata: {
total_count: data.total_count,
incomplete_results: data.incomplete_results,
items,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable search results' },
metadata: {
type: 'object',
description: 'Search results metadata',
properties: {
total_count: { type: 'number', description: 'Total matching results' },
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
items: {
type: 'array',
description: 'Array of commits',
items: {
type: 'object',
properties: {
sha: { type: 'string', description: 'Commit SHA' },
html_url: { type: 'string', description: 'GitHub web URL' },
commit: {
type: 'object',
description: 'Commit details',
properties: {
message: { type: 'string', description: 'Commit message' },
author: { type: 'object', description: 'Author info' },
committer: { type: 'object', description: 'Committer info' },
},
},
author: { type: 'object', description: 'GitHub user (author)', optional: true },
committer: { type: 'object', description: 'GitHub user (committer)', optional: true },
repository: { type: 'object', description: 'Repository info' },
},
},
},
},
},
},
}
export const searchCommitsV2Tool: ToolConfig<SearchCommitsParams, any> = {
id: 'github_search_commits_v2',
name: searchCommitsTool.name,
description: searchCommitsTool.description,
version: '2.0.0',
params: searchCommitsTool.params,
request: searchCommitsTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
total_count: data.total_count,
incomplete_results: data.incomplete_results,
items: data.items.map((item: any) => ({
...item,
author: item.author ?? null,
committer: item.committer ?? null,
})),
},
}
},
outputs: {
total_count: { type: 'number', description: 'Total matching results' },
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
items: {
type: 'array',
description: 'Array of commit objects from GitHub API',
items: {
type: 'object',
properties: {
sha: { type: 'string', description: 'Commit SHA' },
html_url: { type: 'string', description: 'Web URL' },
commit: { type: 'object', description: 'Commit data' },
author: { type: 'object', description: 'GitHub user', optional: true },
committer: { type: 'object', description: 'GitHub user', optional: true },
repository: { type: 'object', description: 'Repository' },
},
},
},
},
}

View File

@@ -0,0 +1,240 @@
import type { ToolConfig } from '@/tools/types'
interface SearchIssuesParams {
q: string
sort?:
| 'comments'
| 'reactions'
| 'reactions-+1'
| 'reactions--1'
| 'reactions-smile'
| 'reactions-thinking_face'
| 'reactions-heart'
| 'reactions-tada'
| 'interactions'
| 'created'
| 'updated'
order?: 'asc' | 'desc'
per_page?: number
page?: number
apiKey: string
}
interface SearchIssuesResponse {
success: boolean
output: {
content: string
metadata: {
total_count: number
incomplete_results: boolean
items: Array<{
number: number
title: string
state: string
html_url: string
user: { login: string }
labels: string[]
created_at: string
updated_at: string
comments: number
is_pull_request: boolean
repository_url: string
}>
}
}
}
export const searchIssuesTool: ToolConfig<SearchIssuesParams, SearchIssuesResponse> = {
id: 'github_search_issues',
name: 'GitHub Search Issues',
description:
'Search for issues and pull requests across GitHub. Use qualifiers like repo:owner/name, is:issue, is:pr, state:open, label:bug, author:user',
version: '1.0.0',
params: {
q: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Search query with optional qualifiers (repo:, is:issue, is:pr, state:, label:, author:, assignee:)',
},
sort: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Sort by: comments, reactions, created, updated, interactions (default: best match)',
},
order: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort order: asc or desc (default: desc)',
},
per_page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Results per page (max 100, default: 30)',
default: 30,
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number (default: 1)',
default: 1,
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => {
const url = new URL('https://api.github.com/search/issues')
url.searchParams.append('q', params.q)
if (params.sort) url.searchParams.append('sort', params.sort)
if (params.order) url.searchParams.append('order', params.order)
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
if (params.page) url.searchParams.append('page', String(params.page))
return url.toString()
},
method: 'GET',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const items = data.items.map((item: any) => ({
number: item.number,
title: item.title,
state: item.state,
html_url: item.html_url,
user: { login: item.user?.login ?? 'unknown' },
labels: item.labels?.map((l: any) => l.name) ?? [],
created_at: item.created_at,
updated_at: item.updated_at,
comments: item.comments ?? 0,
is_pull_request: !!item.pull_request,
repository_url: item.repository_url,
}))
const content = `Found ${data.total_count} result(s)${data.incomplete_results ? ' (incomplete)' : ''}:
${items
.map(
(item: any) =>
`#${item.number}: "${item.title}" (${item.state}) [${item.is_pull_request ? 'PR' : 'Issue'}]
${item.html_url}
Labels: ${item.labels.length > 0 ? item.labels.join(', ') : 'none'} | Comments: ${item.comments}`
)
.join('\n\n')}`
return {
success: true,
output: {
content,
metadata: {
total_count: data.total_count,
incomplete_results: data.incomplete_results,
items,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable search results' },
metadata: {
type: 'object',
description: 'Search results metadata',
properties: {
total_count: { type: 'number', description: 'Total matching results' },
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
items: {
type: 'array',
description: 'Array of issues/PRs',
items: {
type: 'object',
properties: {
number: { type: 'number', description: 'Issue/PR number' },
title: { type: 'string', description: 'Title' },
state: { type: 'string', description: 'State (open/closed)' },
html_url: { type: 'string', description: 'GitHub web URL' },
user: { type: 'object', description: 'Author info' },
labels: { type: 'array', description: 'Label names' },
created_at: { type: 'string', description: 'Creation date' },
updated_at: { type: 'string', description: 'Last update date' },
comments: { type: 'number', description: 'Comment count' },
is_pull_request: { type: 'boolean', description: 'Whether this is a PR' },
repository_url: { type: 'string', description: 'Repository API URL' },
},
},
},
},
},
},
}
export const searchIssuesV2Tool: ToolConfig<SearchIssuesParams, any> = {
id: 'github_search_issues_v2',
name: searchIssuesTool.name,
description: searchIssuesTool.description,
version: '2.0.0',
params: searchIssuesTool.params,
request: searchIssuesTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
total_count: data.total_count,
incomplete_results: data.incomplete_results,
items: data.items.map((item: any) => ({
...item,
body: item.body ?? null,
closed_at: item.closed_at ?? null,
milestone: item.milestone ?? null,
labels: item.labels ?? [],
assignees: item.assignees ?? [],
})),
},
}
},
outputs: {
total_count: { type: 'number', description: 'Total matching results' },
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
items: {
type: 'array',
description: 'Array of issue/PR objects from GitHub API',
items: {
type: 'object',
properties: {
id: { type: 'number', description: 'Issue ID' },
number: { type: 'number', description: 'Issue number' },
title: { type: 'string', description: 'Title' },
state: { type: 'string', description: 'State' },
html_url: { type: 'string', description: 'Web URL' },
body: { type: 'string', description: 'Body text', optional: true },
user: { type: 'object', description: 'Author' },
labels: { type: 'array', description: 'Labels' },
assignees: { type: 'array', description: 'Assignees' },
created_at: { type: 'string', description: 'Creation date' },
updated_at: { type: 'string', description: 'Update date' },
closed_at: { type: 'string', description: 'Close date', optional: true },
},
},
},
},
}

View File

@@ -0,0 +1,226 @@
import type { ToolConfig } from '@/tools/types'
interface SearchReposParams {
q: string
sort?: 'stars' | 'forks' | 'help-wanted-issues' | 'updated'
order?: 'asc' | 'desc'
per_page?: number
page?: number
apiKey: string
}
interface SearchReposResponse {
success: boolean
output: {
content: string
metadata: {
total_count: number
incomplete_results: boolean
items: Array<{
id: number
full_name: string
description: string | null
html_url: string
stargazers_count: number
forks_count: number
language: string | null
topics: string[]
created_at: string
updated_at: string
owner: { login: string }
}>
}
}
}
export const searchReposTool: ToolConfig<SearchReposParams, SearchReposResponse> = {
id: 'github_search_repos',
name: 'GitHub Search Repositories',
description:
'Search for repositories across GitHub. Use qualifiers like language:python, stars:>1000, topic:react, user:owner, org:name',
version: '1.0.0',
params: {
q: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Search query with optional qualifiers (language:, stars:, forks:, topic:, user:, org:, in:name,description,readme)',
},
sort: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort by: stars, forks, help-wanted-issues, updated (default: best match)',
},
order: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort order: asc or desc (default: desc)',
},
per_page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Results per page (max 100, default: 30)',
default: 30,
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number (default: 1)',
default: 1,
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => {
const url = new URL('https://api.github.com/search/repositories')
url.searchParams.append('q', params.q)
if (params.sort) url.searchParams.append('sort', params.sort)
if (params.order) url.searchParams.append('order', params.order)
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
if (params.page) url.searchParams.append('page', String(params.page))
return url.toString()
},
method: 'GET',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const items = data.items.map((item: any) => ({
id: item.id,
full_name: item.full_name,
description: item.description ?? null,
html_url: item.html_url,
stargazers_count: item.stargazers_count,
forks_count: item.forks_count,
language: item.language ?? null,
topics: item.topics ?? [],
created_at: item.created_at,
updated_at: item.updated_at,
owner: { login: item.owner?.login ?? 'unknown' },
}))
const content = `Found ${data.total_count} repository(s)${data.incomplete_results ? ' (incomplete)' : ''}:
${items
.map(
(item: any) =>
`${item.full_name}${item.stargazers_count} | 🍴 ${item.forks_count}
${item.description ?? 'No description'}
${item.html_url}
Language: ${item.language ?? 'N/A'} | Topics: ${item.topics.length > 0 ? item.topics.join(', ') : 'none'}`
)
.join('\n\n')}`
return {
success: true,
output: {
content,
metadata: {
total_count: data.total_count,
incomplete_results: data.incomplete_results,
items,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable search results' },
metadata: {
type: 'object',
description: 'Search results metadata',
properties: {
total_count: { type: 'number', description: 'Total matching results' },
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
items: {
type: 'array',
description: 'Array of repositories',
items: {
type: 'object',
properties: {
id: { type: 'number', description: 'Repository ID' },
full_name: { type: 'string', description: 'Full name (owner/repo)' },
description: { type: 'string', description: 'Description', optional: true },
html_url: { type: 'string', description: 'GitHub web URL' },
stargazers_count: { type: 'number', description: 'Star count' },
forks_count: { type: 'number', description: 'Fork count' },
language: { type: 'string', description: 'Primary language', optional: true },
topics: { type: 'array', description: 'Repository topics' },
created_at: { type: 'string', description: 'Creation date' },
updated_at: { type: 'string', description: 'Last update date' },
owner: { type: 'object', description: 'Owner info' },
},
},
},
},
},
},
}
export const searchReposV2Tool: ToolConfig<SearchReposParams, any> = {
id: 'github_search_repos_v2',
name: searchReposTool.name,
description: searchReposTool.description,
version: '2.0.0',
params: searchReposTool.params,
request: searchReposTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
total_count: data.total_count,
incomplete_results: data.incomplete_results,
items: data.items.map((item: any) => ({
...item,
description: item.description ?? null,
language: item.language ?? null,
topics: item.topics ?? [],
license: item.license ?? null,
})),
},
}
},
outputs: {
total_count: { type: 'number', description: 'Total matching results' },
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
items: {
type: 'array',
description: 'Array of repository objects from GitHub API',
items: {
type: 'object',
properties: {
id: { type: 'number', description: 'Repository ID' },
full_name: { type: 'string', description: 'Full name' },
description: { type: 'string', description: 'Description', optional: true },
html_url: { type: 'string', description: 'Web URL' },
stargazers_count: { type: 'number', description: 'Stars' },
forks_count: { type: 'number', description: 'Forks' },
open_issues_count: { type: 'number', description: 'Open issues' },
language: { type: 'string', description: 'Language', optional: true },
topics: { type: 'array', description: 'Topics' },
owner: { type: 'object', description: 'Owner' },
},
},
},
},
}

View File

@@ -0,0 +1,193 @@
import type { ToolConfig } from '@/tools/types'
interface SearchUsersParams {
q: string
sort?: 'followers' | 'repositories' | 'joined'
order?: 'asc' | 'desc'
per_page?: number
page?: number
apiKey: string
}
interface SearchUsersResponse {
success: boolean
output: {
content: string
metadata: {
total_count: number
incomplete_results: boolean
items: Array<{
id: number
login: string
html_url: string
avatar_url: string
type: string
score: number
}>
}
}
}
export const searchUsersTool: ToolConfig<SearchUsersParams, SearchUsersResponse> = {
id: 'github_search_users',
name: 'GitHub Search Users',
description:
'Search for users and organizations on GitHub. Use qualifiers like type:user, type:org, followers:>1000, repos:>10, location:city',
version: '1.0.0',
params: {
q: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Search query with optional qualifiers (type:user/org, followers:, repos:, location:, language:, created:)',
},
sort: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort by: followers, repositories, joined (default: best match)',
},
order: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort order: asc or desc (default: desc)',
},
per_page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Results per page (max 100, default: 30)',
default: 30,
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number (default: 1)',
default: 1,
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => {
const url = new URL('https://api.github.com/search/users')
url.searchParams.append('q', params.q)
if (params.sort) url.searchParams.append('sort', params.sort)
if (params.order) url.searchParams.append('order', params.order)
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
if (params.page) url.searchParams.append('page', String(params.page))
return url.toString()
},
method: 'GET',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response) => {
const data = await response.json()
const items = data.items.map((item: any) => ({
id: item.id,
login: item.login,
html_url: item.html_url,
avatar_url: item.avatar_url,
type: item.type,
score: item.score,
}))
const content = `Found ${data.total_count} user(s)/organization(s)${data.incomplete_results ? ' (incomplete)' : ''}:
${items.map((item: any) => `@${item.login} (${item.type}) - ${item.html_url}`).join('\n')}`
return {
success: true,
output: {
content,
metadata: {
total_count: data.total_count,
incomplete_results: data.incomplete_results,
items,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable search results' },
metadata: {
type: 'object',
description: 'Search results metadata',
properties: {
total_count: { type: 'number', description: 'Total matching results' },
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
items: {
type: 'array',
description: 'Array of users/orgs',
items: {
type: 'object',
properties: {
id: { type: 'number', description: 'User ID' },
login: { type: 'string', description: 'Username' },
html_url: { type: 'string', description: 'Profile URL' },
avatar_url: { type: 'string', description: 'Avatar URL' },
type: { type: 'string', description: 'User or Organization' },
score: { type: 'number', description: 'Search relevance score' },
},
},
},
},
},
},
}
export const searchUsersV2Tool: ToolConfig<SearchUsersParams, any> = {
id: 'github_search_users_v2',
name: searchUsersTool.name,
description: searchUsersTool.description,
version: '2.0.0',
params: searchUsersTool.params,
request: searchUsersTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
total_count: data.total_count,
incomplete_results: data.incomplete_results,
items: data.items,
},
}
},
outputs: {
total_count: { type: 'number', description: 'Total matching results' },
incomplete_results: { type: 'boolean', description: 'Whether results are incomplete' },
items: {
type: 'array',
description: 'Array of user objects from GitHub API',
items: {
type: 'object',
properties: {
id: { type: 'number', description: 'User ID' },
login: { type: 'string', description: 'Username' },
html_url: { type: 'string', description: 'Profile URL' },
avatar_url: { type: 'string', description: 'Avatar URL' },
type: { type: 'string', description: 'User or Organization' },
site_admin: { type: 'boolean', description: 'Is site admin' },
},
},
},
},
}

View File

@@ -0,0 +1,104 @@
import type { ToolConfig } from '@/tools/types'
interface StarGistParams {
gist_id: string
apiKey: string
}
interface StarGistResponse {
success: boolean
output: {
content: string
metadata: {
starred: boolean
gist_id: string
}
}
}
export const starGistTool: ToolConfig<StarGistParams, StarGistResponse> = {
id: 'github_star_gist',
name: 'GitHub Star Gist',
description: 'Star a gist',
version: '1.0.0',
params: {
gist_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The gist ID to star',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => `https://api.github.com/gists/${params.gist_id?.trim()}/star`,
method: 'PUT',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'Content-Length': '0',
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response, params) => {
const starred = response.status === 204
return {
success: starred,
output: {
content: starred
? `Successfully starred gist ${params?.gist_id}`
: `Failed to star gist ${params?.gist_id}`,
metadata: {
starred,
gist_id: params?.gist_id ?? '',
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable result' },
metadata: {
type: 'object',
description: 'Star operation metadata',
properties: {
starred: { type: 'boolean', description: 'Whether starring succeeded' },
gist_id: { type: 'string', description: 'The gist ID' },
},
},
},
}
export const starGistV2Tool: ToolConfig<StarGistParams, any> = {
id: 'github_star_gist_v2',
name: starGistTool.name,
description: starGistTool.description,
version: '2.0.0',
params: starGistTool.params,
request: starGistTool.request,
transformResponse: async (response: Response, params) => {
const starred = response.status === 204
return {
success: starred,
output: {
starred,
gist_id: params?.gist_id ?? '',
},
}
},
outputs: {
starred: { type: 'boolean', description: 'Whether starring succeeded' },
gist_id: { type: 'string', description: 'The gist ID' },
},
}

View File

@@ -0,0 +1,116 @@
import type { ToolConfig } from '@/tools/types'
interface StarRepoParams {
owner: string
repo: string
apiKey: string
}
interface StarRepoResponse {
success: boolean
output: {
content: string
metadata: {
starred: boolean
owner: string
repo: string
}
}
}
export const starRepoTool: ToolConfig<StarRepoParams, StarRepoResponse> = {
id: 'github_star_repo',
name: 'GitHub Star Repository',
description: 'Star a repository',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => `https://api.github.com/user/starred/${params.owner}/${params.repo}`,
method: 'PUT',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'Content-Length': '0',
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response, params) => {
const starred = response.status === 204
return {
success: starred,
output: {
content: starred
? `Successfully starred ${params?.owner}/${params?.repo}`
: `Failed to star ${params?.owner}/${params?.repo}`,
metadata: {
starred,
owner: params?.owner ?? '',
repo: params?.repo ?? '',
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable result' },
metadata: {
type: 'object',
description: 'Star operation metadata',
properties: {
starred: { type: 'boolean', description: 'Whether starring succeeded' },
owner: { type: 'string', description: 'Repository owner' },
repo: { type: 'string', description: 'Repository name' },
},
},
},
}
export const starRepoV2Tool: ToolConfig<StarRepoParams, any> = {
id: 'github_star_repo_v2',
name: starRepoTool.name,
description: starRepoTool.description,
version: '2.0.0',
params: starRepoTool.params,
request: starRepoTool.request,
transformResponse: async (response: Response, params) => {
const starred = response.status === 204
return {
success: starred,
output: {
starred,
owner: params?.owner ?? '',
repo: params?.repo ?? '',
},
}
},
outputs: {
starred: { type: 'boolean', description: 'Whether starring succeeded' },
owner: { type: 'string', description: 'Repository owner' },
repo: { type: 'string', description: 'Repository name' },
},
}

View File

@@ -0,0 +1,103 @@
import type { ToolConfig } from '@/tools/types'
interface UnstarGistParams {
gist_id: string
apiKey: string
}
interface UnstarGistResponse {
success: boolean
output: {
content: string
metadata: {
unstarred: boolean
gist_id: string
}
}
}
export const unstarGistTool: ToolConfig<UnstarGistParams, UnstarGistResponse> = {
id: 'github_unstar_gist',
name: 'GitHub Unstar Gist',
description: 'Unstar a gist',
version: '1.0.0',
params: {
gist_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The gist ID to unstar',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => `https://api.github.com/gists/${params.gist_id?.trim()}/star`,
method: 'DELETE',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response, params) => {
const unstarred = response.status === 204
return {
success: unstarred,
output: {
content: unstarred
? `Successfully unstarred gist ${params?.gist_id}`
: `Failed to unstar gist ${params?.gist_id}`,
metadata: {
unstarred,
gist_id: params?.gist_id ?? '',
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable result' },
metadata: {
type: 'object',
description: 'Unstar operation metadata',
properties: {
unstarred: { type: 'boolean', description: 'Whether unstarring succeeded' },
gist_id: { type: 'string', description: 'The gist ID' },
},
},
},
}
export const unstarGistV2Tool: ToolConfig<UnstarGistParams, any> = {
id: 'github_unstar_gist_v2',
name: unstarGistTool.name,
description: unstarGistTool.description,
version: '2.0.0',
params: unstarGistTool.params,
request: unstarGistTool.request,
transformResponse: async (response: Response, params) => {
const unstarred = response.status === 204
return {
success: unstarred,
output: {
unstarred,
gist_id: params?.gist_id ?? '',
},
}
},
outputs: {
unstarred: { type: 'boolean', description: 'Whether unstarring succeeded' },
gist_id: { type: 'string', description: 'The gist ID' },
},
}

View File

@@ -0,0 +1,115 @@
import type { ToolConfig } from '@/tools/types'
interface UnstarRepoParams {
owner: string
repo: string
apiKey: string
}
interface UnstarRepoResponse {
success: boolean
output: {
content: string
metadata: {
unstarred: boolean
owner: string
repo: string
}
}
}
export const unstarRepoTool: ToolConfig<UnstarRepoParams, UnstarRepoResponse> = {
id: 'github_unstar_repo',
name: 'GitHub Unstar Repository',
description: 'Remove star from a repository',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => `https://api.github.com/user/starred/${params.owner}/${params.repo}`,
method: 'DELETE',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'X-GitHub-Api-Version': '2022-11-28',
}),
},
transformResponse: async (response, params) => {
const unstarred = response.status === 204
return {
success: unstarred,
output: {
content: unstarred
? `Successfully unstarred ${params?.owner}/${params?.repo}`
: `Failed to unstar ${params?.owner}/${params?.repo}`,
metadata: {
unstarred,
owner: params?.owner ?? '',
repo: params?.repo ?? '',
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable result' },
metadata: {
type: 'object',
description: 'Unstar operation metadata',
properties: {
unstarred: { type: 'boolean', description: 'Whether unstarring succeeded' },
owner: { type: 'string', description: 'Repository owner' },
repo: { type: 'string', description: 'Repository name' },
},
},
},
}
export const unstarRepoV2Tool: ToolConfig<UnstarRepoParams, any> = {
id: 'github_unstar_repo_v2',
name: unstarRepoTool.name,
description: unstarRepoTool.description,
version: '2.0.0',
params: unstarRepoTool.params,
request: unstarRepoTool.request,
transformResponse: async (response: Response, params) => {
const unstarred = response.status === 204
return {
success: unstarred,
output: {
unstarred,
owner: params?.owner ?? '',
repo: params?.repo ?? '',
},
}
},
outputs: {
unstarred: { type: 'boolean', description: 'Whether unstarring succeeded' },
owner: { type: 'string', description: 'Repository owner' },
repo: { type: 'string', description: 'Repository name' },
},
}

View File

@@ -0,0 +1,164 @@
import type { ToolConfig } from '@/tools/types'
interface UpdateGistParams {
gist_id: string
description?: string
files?: string
apiKey: string
}
interface UpdateGistResponse {
success: boolean
output: {
content: string
metadata: {
id: string
html_url: string
description: string | null
public: boolean
updated_at: string
files: Record<
string,
{ filename: string; type: string; language: string | null; size: number }
>
}
}
}
export const updateGistTool: ToolConfig<UpdateGistParams, UpdateGistResponse> = {
id: 'github_update_gist',
name: 'GitHub Update Gist',
description:
'Update a gist description or files. To delete a file, set its value to null in files object',
version: '1.0.0',
params: {
gist_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The gist ID to update',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New description for the gist',
},
files: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description:
'JSON object with filenames as keys. Set to null to delete, or provide content to update/add',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) => `https://api.github.com/gists/${params.gist_id?.trim()}`,
method: 'PATCH',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
'X-GitHub-Api-Version': '2022-11-28',
}),
body: (params) => {
const body: Record<string, any> = {}
if (params.description !== undefined) body.description = params.description
if (params.files) {
body.files = typeof params.files === 'string' ? JSON.parse(params.files) : params.files
}
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
const files: Record<
string,
{ filename: string; type: string; language: string | null; size: number }
> = {}
for (const [key, value] of Object.entries(data.files ?? {})) {
const file = value as any
files[key] = {
filename: file.filename,
type: file.type,
language: file.language ?? null,
size: file.size,
}
}
const content = `Updated gist: ${data.html_url}
Description: ${data.description ?? 'No description'}
Files: ${Object.keys(files).join(', ')}`
return {
success: true,
output: {
content,
metadata: {
id: data.id,
html_url: data.html_url,
description: data.description ?? null,
public: data.public,
updated_at: data.updated_at,
files,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable result' },
metadata: {
type: 'object',
description: 'Updated gist metadata',
properties: {
id: { type: 'string', description: 'Gist ID' },
html_url: { type: 'string', description: 'Web URL' },
description: { type: 'string', description: 'Description', optional: true },
public: { type: 'boolean', description: 'Is public' },
updated_at: { type: 'string', description: 'Update date' },
files: { type: 'object', description: 'Current files' },
},
},
},
}
export const updateGistV2Tool: ToolConfig<UpdateGistParams, any> = {
id: 'github_update_gist_v2',
name: updateGistTool.name,
description: updateGistTool.description,
version: '2.0.0',
params: updateGistTool.params,
request: updateGistTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
...data,
description: data.description ?? null,
files: data.files ?? {},
},
}
},
outputs: {
id: { type: 'string', description: 'Gist ID' },
html_url: { type: 'string', description: 'Web URL' },
description: { type: 'string', description: 'Description', optional: true },
public: { type: 'boolean', description: 'Is public' },
updated_at: { type: 'string', description: 'Update date' },
files: { type: 'object', description: 'Current files' },
},
}

View File

@@ -0,0 +1,186 @@
import type { ToolConfig } from '@/tools/types'
interface UpdateMilestoneParams {
owner: string
repo: string
milestone_number: number
title?: string
state?: 'open' | 'closed'
description?: string
due_on?: string
apiKey: string
}
interface UpdateMilestoneResponse {
success: boolean
output: {
content: string
metadata: {
number: number
title: string
description: string | null
state: string
html_url: string
due_on: string | null
open_issues: number
closed_issues: number
updated_at: string
}
}
}
export const updateMilestoneTool: ToolConfig<UpdateMilestoneParams, UpdateMilestoneResponse> = {
id: 'github_update_milestone',
name: 'GitHub Update Milestone',
description: 'Update a milestone in a repository',
version: '1.0.0',
params: {
owner: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository owner',
},
repo: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Repository name',
},
milestone_number: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Milestone number to update',
},
title: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New milestone title',
},
state: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New state: open or closed',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New description',
},
due_on: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New due date (ISO 8601 format)',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitHub API token',
},
},
request: {
url: (params) =>
`https://api.github.com/repos/${params.owner}/${params.repo}/milestones/${params.milestone_number}`,
method: 'PATCH',
headers: (params) => ({
Accept: 'application/vnd.github.v3+json',
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
'X-GitHub-Api-Version': '2022-11-28',
}),
body: (params) => {
const body: Record<string, any> = {}
if (params.title !== undefined) body.title = params.title
if (params.state !== undefined) body.state = params.state
if (params.description !== undefined) body.description = params.description
if (params.due_on !== undefined) body.due_on = params.due_on
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
const content = `Updated milestone: ${data.title} (#${data.number})
State: ${data.state}
Due: ${data.due_on ?? 'No due date'}
${data.html_url}`
return {
success: true,
output: {
content,
metadata: {
number: data.number,
title: data.title,
description: data.description ?? null,
state: data.state,
html_url: data.html_url,
due_on: data.due_on ?? null,
open_issues: data.open_issues,
closed_issues: data.closed_issues,
updated_at: data.updated_at,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Human-readable result' },
metadata: {
type: 'object',
description: 'Updated milestone metadata',
properties: {
number: { type: 'number', description: 'Milestone number' },
title: { type: 'string', description: 'Title' },
description: { type: 'string', description: 'Description', optional: true },
state: { type: 'string', description: 'State' },
html_url: { type: 'string', description: 'Web URL' },
due_on: { type: 'string', description: 'Due date', optional: true },
open_issues: { type: 'number', description: 'Open issues' },
closed_issues: { type: 'number', description: 'Closed issues' },
updated_at: { type: 'string', description: 'Update date' },
},
},
},
}
export const updateMilestoneV2Tool: ToolConfig<UpdateMilestoneParams, any> = {
id: 'github_update_milestone_v2',
name: updateMilestoneTool.name,
description: updateMilestoneTool.description,
version: '2.0.0',
params: updateMilestoneTool.params,
request: updateMilestoneTool.request,
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
...data,
description: data.description ?? null,
due_on: data.due_on ?? null,
},
}
},
outputs: {
number: { type: 'number', description: 'Milestone number' },
title: { type: 'string', description: 'Title' },
description: { type: 'string', description: 'Description', optional: true },
state: { type: 'string', description: 'State' },
html_url: { type: 'string', description: 'Web URL' },
due_on: { type: 'string', description: 'Due date', optional: true },
open_issues: { type: 'number', description: 'Open issues' },
closed_issues: { type: 'number', description: 'Closed issues' },
},
}

View File

@@ -14,16 +14,19 @@ export const gitlabCancelPipelineTool: ToolConfig<
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
pipelineId: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Pipeline ID',
},
},

View File

@@ -12,46 +12,55 @@ export const gitlabCreateIssueTool: ToolConfig<GitLabCreateIssueParams, GitLabCr
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
title: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Issue title',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Issue description (Markdown supported)',
},
labels: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of label names',
},
assigneeIds: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Array of user IDs to assign',
},
milestoneId: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Milestone ID to assign',
},
dueDate: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Due date in YYYY-MM-DD format',
},
confidential: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether the issue is confidential',
},
},

View File

@@ -14,21 +14,25 @@ export const gitlabCreateIssueNoteTool: ToolConfig<
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
issueIid: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Issue internal ID (IID)',
},
body: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Comment body (Markdown supported)',
},
},

View File

@@ -17,61 +17,73 @@ export const gitlabCreateMergeRequestTool: ToolConfig<
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
sourceBranch: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Source branch name',
},
targetBranch: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Target branch name',
},
title: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Merge request title',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Merge request description (Markdown supported)',
},
labels: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of label names',
},
assigneeIds: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Array of user IDs to assign',
},
milestoneId: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Milestone ID to assign',
},
removeSourceBranch: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Delete source branch after merge',
},
squash: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Squash commits on merge',
},
draft: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Mark as draft (work in progress)',
},
},

View File

@@ -17,21 +17,25 @@ export const gitlabCreateMergeRequestNoteTool: ToolConfig<
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
mergeRequestIid: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Merge request internal ID (IID)',
},
body: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Comment body (Markdown supported)',
},
},

View File

@@ -14,21 +14,25 @@ export const gitlabCreatePipelineTool: ToolConfig<
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
ref: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Branch or tag to run the pipeline on',
},
variables: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description:
'Array of variables for the pipeline (each with key, value, and optional variable_type)',
},

View File

@@ -12,16 +12,19 @@ export const gitlabDeleteIssueTool: ToolConfig<GitLabDeleteIssueParams, GitLabDe
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
issueIid: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Issue internal ID (IID)',
},
},

View File

@@ -11,16 +11,19 @@ export const gitlabGetIssueTool: ToolConfig<GitLabGetIssueParams, GitLabGetIssue
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
issueIid: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Issue number within the project (the # shown in GitLab UI)',
},
},

View File

@@ -17,16 +17,19 @@ export const gitlabGetMergeRequestTool: ToolConfig<
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
mergeRequestIid: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Merge request internal ID (IID)',
},
},

View File

@@ -12,16 +12,19 @@ export const gitlabGetPipelineTool: ToolConfig<GitLabGetPipelineParams, GitLabGe
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
pipelineId: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Pipeline ID',
},
},

View File

@@ -11,11 +11,13 @@ export const gitlabGetProjectTool: ToolConfig<GitLabGetProjectParams, GitLabGetP
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path (e.g., "namespace/project")',
},
},

View File

@@ -11,56 +11,67 @@ export const gitlabListIssuesTool: ToolConfig<GitLabListIssuesParams, GitLabList
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
state: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by state (opened, closed, all)',
},
labels: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of label names',
},
assigneeId: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Filter by assignee user ID',
},
milestoneTitle: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by milestone title',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search issues by title and description',
},
orderBy: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Order by field (created_at, updated_at)',
},
sort: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort direction (asc, desc)',
},
perPage: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of results per page (default 20, max 100)',
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},

View File

@@ -17,51 +17,61 @@ export const gitlabListMergeRequestsTool: ToolConfig<
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
state: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by state (opened, closed, merged, all)',
},
labels: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of label names',
},
sourceBranch: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by source branch',
},
targetBranch: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by target branch',
},
orderBy: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Order by field (created_at, updated_at)',
},
sort: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort direction (asc, desc)',
},
perPage: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of results per page (default 20, max 100)',
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},

View File

@@ -14,42 +14,50 @@ export const gitlabListPipelinesTool: ToolConfig<
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
ref: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by ref (branch or tag)',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Filter by status (created, waiting_for_resource, preparing, pending, running, success, failed, canceled, skipped, manual, scheduled)',
},
orderBy: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Order by field (id, status, ref, updated_at, user_id)',
},
sort: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort direction (asc, desc)',
},
perPage: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of results per page (default 20, max 100)',
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},

View File

@@ -14,46 +14,55 @@ export const gitlabListProjectsTool: ToolConfig<
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
owned: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Limit to projects owned by the current user',
},
membership: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Limit to projects the current user is a member of',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search projects by name',
},
visibility: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by visibility (public, internal, private)',
},
orderBy: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Order by field (id, name, path, created_at, updated_at, last_activity_at)',
},
sort: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Sort direction (asc, desc)',
},
perPage: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of results per page (default 20, max 100)',
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},

View File

@@ -17,41 +17,49 @@ export const gitlabMergeMergeRequestTool: ToolConfig<
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
mergeRequestIid: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Merge request internal ID (IID)',
},
mergeCommitMessage: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Custom merge commit message',
},
squashCommitMessage: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Custom squash commit message',
},
squash: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Squash commits before merging',
},
shouldRemoveSourceBranch: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Delete source branch after merge',
},
mergeWhenPipelineSucceeds: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Merge when pipeline succeeds',
},
},

View File

@@ -14,16 +14,19 @@ export const gitlabRetryPipelineTool: ToolConfig<
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
pipelineId: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Pipeline ID',
},
},

View File

@@ -12,56 +12,67 @@ export const gitlabUpdateIssueTool: ToolConfig<GitLabUpdateIssueParams, GitLabUp
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
issueIid: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Issue internal ID (IID)',
},
title: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New issue title',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New issue description (Markdown supported)',
},
stateEvent: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'State event (close or reopen)',
},
labels: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of label names',
},
assigneeIds: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Array of user IDs to assign',
},
milestoneId: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Milestone ID to assign',
},
dueDate: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Due date in YYYY-MM-DD format',
},
confidential: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether the issue is confidential',
},
},

View File

@@ -17,66 +17,79 @@ export const gitlabUpdateMergeRequestTool: ToolConfig<
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
mergeRequestIid: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Merge request internal ID (IID)',
},
title: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New merge request title',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New merge request description',
},
stateEvent: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'State event (close or reopen)',
},
labels: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of label names',
},
assigneeIds: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Array of user IDs to assign',
},
milestoneId: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Milestone ID to assign',
},
targetBranch: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New target branch',
},
removeSourceBranch: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Delete source branch after merge',
},
squash: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Squash commits on merge',
},
draft: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Mark as draft (work in progress)',
},
},

View File

@@ -0,0 +1,135 @@
import { CALENDAR_API_BASE, type GoogleCalendarDeleteParams } from '@/tools/google_calendar/types'
import type { ToolConfig } from '@/tools/types'
interface GoogleCalendarDeleteResponse {
success: boolean
output: {
content: string
metadata: {
eventId: string
deleted: boolean
}
}
}
export const deleteTool: ToolConfig<GoogleCalendarDeleteParams, GoogleCalendarDeleteResponse> = {
id: 'google_calendar_delete',
name: 'Google Calendar Delete Event',
description: 'Delete an event from Google Calendar',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-calendar',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Google Calendar API',
},
calendarId: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Calendar ID (defaults to primary)',
},
eventId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Event ID to delete',
},
sendUpdates: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'How to send updates to attendees: all, externalOnly, or none',
},
},
request: {
url: (params: GoogleCalendarDeleteParams) => {
const calendarId = params.calendarId || 'primary'
const queryParams = new URLSearchParams()
if (params.sendUpdates !== undefined) {
queryParams.append('sendUpdates', params.sendUpdates)
}
const queryString = queryParams.toString()
return `${CALENDAR_API_BASE}/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(params.eventId)}${queryString ? `?${queryString}` : ''}`
},
method: 'DELETE',
headers: (params: GoogleCalendarDeleteParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response, params) => {
// DELETE returns 204 No Content on success
if (response.status === 204 || response.ok) {
return {
success: true,
output: {
content: `Event successfully deleted`,
metadata: {
eventId: params?.eventId || '',
deleted: true,
},
},
}
}
const errorData = await response.json()
throw new Error(errorData.error?.message || 'Failed to delete event')
},
outputs: {
content: { type: 'string', description: 'Event deletion confirmation message' },
metadata: {
type: 'json',
description: 'Deletion details including event ID',
},
},
}
interface GoogleCalendarDeleteV2Response {
success: boolean
output: {
eventId: string
deleted: boolean
}
}
export const deleteV2Tool: ToolConfig<GoogleCalendarDeleteParams, GoogleCalendarDeleteV2Response> =
{
id: 'google_calendar_delete_v2',
name: 'Google Calendar Delete Event',
description: 'Delete an event from Google Calendar. Returns API-aligned fields only.',
version: '2.0.0',
oauth: deleteTool.oauth,
params: deleteTool.params,
request: deleteTool.request,
transformResponse: async (response: Response, params) => {
if (response.status === 204 || response.ok) {
return {
success: true,
output: {
eventId: params?.eventId || '',
deleted: true,
},
}
}
const errorData = await response.json()
throw new Error(errorData.error?.message || 'Failed to delete event')
},
outputs: {
eventId: { type: 'string', description: 'Deleted event ID' },
deleted: { type: 'boolean', description: 'Whether deletion was successful' },
},
}

View File

@@ -1,17 +1,32 @@
import { createTool, createV2Tool } from '@/tools/google_calendar/create'
import { deleteTool, deleteV2Tool } from '@/tools/google_calendar/delete'
import { getTool, getV2Tool } from '@/tools/google_calendar/get'
import { instancesTool, instancesV2Tool } from '@/tools/google_calendar/instances'
import { inviteTool, inviteV2Tool } from '@/tools/google_calendar/invite'
import { listTool, listV2Tool } from '@/tools/google_calendar/list'
import { listCalendarsTool, listCalendarsV2Tool } from '@/tools/google_calendar/list_calendars'
import { moveTool, moveV2Tool } from '@/tools/google_calendar/move'
import { quickAddTool, quickAddV2Tool } from '@/tools/google_calendar/quick_add'
import { updateTool, updateV2Tool } from '@/tools/google_calendar/update'
export const googleCalendarCreateTool = createTool
export const googleCalendarDeleteTool = deleteTool
export const googleCalendarGetTool = getTool
export const googleCalendarInstancesTool = instancesTool
export const googleCalendarInviteTool = inviteTool
export const googleCalendarListTool = listTool
export const googleCalendarListCalendarsTool = listCalendarsTool
export const googleCalendarMoveTool = moveTool
export const googleCalendarQuickAddTool = quickAddTool
export const googleCalendarUpdateTool = updateTool
export const googleCalendarCreateV2Tool = createV2Tool
export const googleCalendarDeleteV2Tool = deleteV2Tool
export const googleCalendarGetV2Tool = getV2Tool
export const googleCalendarInstancesV2Tool = instancesV2Tool
export const googleCalendarInviteV2Tool = inviteV2Tool
export const googleCalendarListV2Tool = listV2Tool
export const googleCalendarListCalendarsV2Tool = listCalendarsV2Tool
export const googleCalendarMoveV2Tool = moveV2Tool
export const googleCalendarQuickAddV2Tool = quickAddV2Tool
export const googleCalendarUpdateV2Tool = updateV2Tool

View File

@@ -0,0 +1,268 @@
import {
CALENDAR_API_BASE,
type GoogleCalendarApiEventResponse,
} from '@/tools/google_calendar/types'
import type { ToolConfig } from '@/tools/types'
interface GoogleCalendarInstancesParams {
accessToken: string
calendarId?: string
eventId: string
timeMin?: string
timeMax?: string
maxResults?: number
pageToken?: string
showDeleted?: boolean
}
interface GoogleCalendarInstancesResponse {
success: boolean
output: {
content: string
metadata: {
nextPageToken?: string
timeZone: string
instances: Array<{
id: string
htmlLink: string
status: string
summary: string
description?: string
location?: string
start: {
dateTime?: string
date?: string
timeZone?: string
}
end: {
dateTime?: string
date?: string
timeZone?: string
}
attendees?: Array<{
email: string
displayName?: string
responseStatus: string
}>
creator?: {
email: string
displayName?: string
}
organizer?: {
email: string
displayName?: string
}
recurringEventId: string
originalStartTime: {
dateTime?: string
date?: string
timeZone?: string
}
}>
}
}
}
interface InstanceApiResponse {
kind: string
etag: string
summary: string
description?: string
updated: string
timeZone: string
accessRole: string
nextPageToken?: string
items: Array<
GoogleCalendarApiEventResponse & {
recurringEventId: string
originalStartTime: {
dateTime?: string
date?: string
timeZone?: string
}
}
>
}
export const instancesTool: ToolConfig<
GoogleCalendarInstancesParams,
GoogleCalendarInstancesResponse
> = {
id: 'google_calendar_instances',
name: 'Google Calendar Get Instances',
description: 'Get instances of a recurring event from Google Calendar',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-calendar',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Google Calendar API',
},
calendarId: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Calendar ID (defaults to primary)',
},
eventId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Recurring event ID to get instances of',
},
timeMin: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Lower bound for instances (RFC3339 timestamp, e.g., 2025-06-03T00:00:00Z)',
},
timeMax: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Upper bound for instances (RFC3339 timestamp, e.g., 2025-06-04T00:00:00Z)',
},
maxResults: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of instances to return (default 250, max 2500)',
},
pageToken: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Token for retrieving subsequent pages of results',
},
showDeleted: {
type: 'boolean',
required: false,
visibility: 'hidden',
description: 'Include deleted instances',
},
},
request: {
url: (params: GoogleCalendarInstancesParams) => {
const calendarId = params.calendarId || 'primary'
const queryParams = new URLSearchParams()
if (params.timeMin) queryParams.append('timeMin', params.timeMin)
if (params.timeMax) queryParams.append('timeMax', params.timeMax)
if (params.maxResults) queryParams.append('maxResults', params.maxResults.toString())
if (params.pageToken) queryParams.append('pageToken', params.pageToken)
if (params.showDeleted !== undefined)
queryParams.append('showDeleted', params.showDeleted.toString())
const queryString = queryParams.toString()
return `${CALENDAR_API_BASE}/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(params.eventId)}/instances${queryString ? `?${queryString}` : ''}`
},
method: 'GET',
headers: (params: GoogleCalendarInstancesParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data: InstanceApiResponse = await response.json()
const instances = data.items || []
const instancesCount = instances.length
return {
success: true,
output: {
content: `Found ${instancesCount} instance${instancesCount !== 1 ? 's' : ''} of the recurring event`,
metadata: {
nextPageToken: data.nextPageToken,
timeZone: data.timeZone,
instances: instances.map((instance) => ({
id: instance.id,
htmlLink: instance.htmlLink,
status: instance.status,
summary: instance.summary || 'No title',
description: instance.description,
location: instance.location,
start: instance.start,
end: instance.end,
attendees: instance.attendees,
creator: instance.creator,
organizer: instance.organizer,
recurringEventId: instance.recurringEventId,
originalStartTime: instance.originalStartTime,
})),
},
},
}
},
outputs: {
content: { type: 'string', description: 'Summary of found instances count' },
metadata: {
type: 'json',
description: 'List of recurring event instances with pagination tokens',
},
},
}
interface GoogleCalendarInstancesV2Response {
success: boolean
output: {
nextPageToken: string | null
timeZone: string | null
instances: Array<Record<string, any>>
}
}
export const instancesV2Tool: ToolConfig<
GoogleCalendarInstancesParams,
GoogleCalendarInstancesV2Response
> = {
id: 'google_calendar_instances_v2',
name: 'Google Calendar Get Instances',
description:
'Get instances of a recurring event from Google Calendar. Returns API-aligned fields only.',
version: '2.0.0',
oauth: instancesTool.oauth,
params: instancesTool.params,
request: instancesTool.request,
transformResponse: async (response: Response) => {
const data: InstanceApiResponse = await response.json()
const instances = data.items || []
return {
success: true,
output: {
nextPageToken: data.nextPageToken ?? null,
timeZone: data.timeZone ?? null,
instances: instances.map((instance) => ({
id: instance.id,
htmlLink: instance.htmlLink,
status: instance.status,
summary: instance.summary ?? null,
description: instance.description ?? null,
location: instance.location ?? null,
start: instance.start,
end: instance.end,
attendees: instance.attendees ?? null,
creator: instance.creator,
organizer: instance.organizer,
recurringEventId: instance.recurringEventId,
originalStartTime: instance.originalStartTime,
})),
},
}
},
outputs: {
nextPageToken: { type: 'string', description: 'Next page token', optional: true },
timeZone: { type: 'string', description: 'Calendar time zone', optional: true },
instances: { type: 'json', description: 'List of recurring event instances' },
},
}

View File

@@ -0,0 +1,280 @@
import { CALENDAR_API_BASE } from '@/tools/google_calendar/types'
import type { ToolConfig } from '@/tools/types'
interface GoogleCalendarListCalendarsParams {
accessToken: string
minAccessRole?: 'freeBusyReader' | 'reader' | 'writer' | 'owner'
maxResults?: number
pageToken?: string
showDeleted?: boolean
showHidden?: boolean
}
interface CalendarListEntry {
kind: string
etag: string
id: string
summary: string
description?: string
location?: string
timeZone: string
summaryOverride?: string
colorId: string
backgroundColor: string
foregroundColor: string
hidden?: boolean
selected?: boolean
accessRole: string
defaultReminders: Array<{
method: string
minutes: number
}>
notificationSettings?: {
notifications: Array<{
type: string
method: string
}>
}
primary?: boolean
deleted?: boolean
conferenceProperties?: {
allowedConferenceSolutionTypes: string[]
}
}
interface CalendarListApiResponse {
kind: string
etag: string
nextPageToken?: string
nextSyncToken?: string
items: CalendarListEntry[]
}
interface GoogleCalendarListCalendarsResponse {
success: boolean
output: {
content: string
metadata: {
nextPageToken?: string
calendars: Array<{
id: string
summary: string
description?: string
location?: string
timeZone: string
accessRole: string
backgroundColor: string
foregroundColor: string
primary?: boolean
hidden?: boolean
selected?: boolean
}>
}
}
}
export const listCalendarsTool: ToolConfig<
GoogleCalendarListCalendarsParams,
GoogleCalendarListCalendarsResponse
> = {
id: 'google_calendar_list_calendars',
name: 'Google Calendar List Calendars',
description: "List all calendars in the user's calendar list",
version: '1.0.0',
oauth: {
required: true,
provider: 'google-calendar',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Google Calendar API',
},
minAccessRole: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Minimum access role for returned calendars: freeBusyReader, reader, writer, or owner',
},
maxResults: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum number of calendars to return (default 100, max 250)',
},
pageToken: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Token for retrieving subsequent pages of results',
},
showDeleted: {
type: 'boolean',
required: false,
visibility: 'hidden',
description: 'Include deleted calendars',
},
showHidden: {
type: 'boolean',
required: false,
visibility: 'hidden',
description: 'Include hidden calendars',
},
},
request: {
url: (params: GoogleCalendarListCalendarsParams) => {
const queryParams = new URLSearchParams()
if (params.minAccessRole) queryParams.append('minAccessRole', params.minAccessRole)
if (params.maxResults) queryParams.append('maxResults', params.maxResults.toString())
if (params.pageToken) queryParams.append('pageToken', params.pageToken)
if (params.showDeleted !== undefined)
queryParams.append('showDeleted', params.showDeleted.toString())
if (params.showHidden !== undefined)
queryParams.append('showHidden', params.showHidden.toString())
const queryString = queryParams.toString()
return `${CALENDAR_API_BASE}/users/me/calendarList${queryString ? `?${queryString}` : ''}`
},
method: 'GET',
headers: (params: GoogleCalendarListCalendarsParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data: CalendarListApiResponse = await response.json()
const calendars = data.items || []
const calendarsCount = calendars.length
return {
success: true,
output: {
content: `Found ${calendarsCount} calendar${calendarsCount !== 1 ? 's' : ''}`,
metadata: {
nextPageToken: data.nextPageToken,
calendars: calendars.map((calendar) => ({
id: calendar.id,
summary: calendar.summaryOverride || calendar.summary,
description: calendar.description,
location: calendar.location,
timeZone: calendar.timeZone,
accessRole: calendar.accessRole,
backgroundColor: calendar.backgroundColor,
foregroundColor: calendar.foregroundColor,
primary: calendar.primary,
hidden: calendar.hidden,
selected: calendar.selected,
})),
},
},
}
},
outputs: {
content: { type: 'string', description: 'Summary of found calendars count' },
metadata: {
type: 'json',
description: 'List of calendars with their details',
},
},
}
interface GoogleCalendarListCalendarsV2Response {
success: boolean
output: {
nextPageToken: string | null
calendars: Array<{
id: string
summary: string
description: string | null
location: string | null
timeZone: string
accessRole: string
backgroundColor: string
foregroundColor: string
primary: boolean | null
hidden: boolean | null
selected: boolean | null
}>
}
}
export const listCalendarsV2Tool: ToolConfig<
GoogleCalendarListCalendarsParams,
GoogleCalendarListCalendarsV2Response
> = {
id: 'google_calendar_list_calendars_v2',
name: 'Google Calendar List Calendars',
description: "List all calendars in the user's calendar list. Returns API-aligned fields only.",
version: '2.0.0',
oauth: listCalendarsTool.oauth,
params: listCalendarsTool.params,
request: listCalendarsTool.request,
transformResponse: async (response: Response) => {
const data: CalendarListApiResponse = await response.json()
const calendars = data.items || []
return {
success: true,
output: {
nextPageToken: data.nextPageToken ?? null,
calendars: calendars.map((calendar) => ({
id: calendar.id,
summary: calendar.summaryOverride || calendar.summary,
description: calendar.description ?? null,
location: calendar.location ?? null,
timeZone: calendar.timeZone,
accessRole: calendar.accessRole,
backgroundColor: calendar.backgroundColor,
foregroundColor: calendar.foregroundColor,
primary: calendar.primary ?? null,
hidden: calendar.hidden ?? null,
selected: calendar.selected ?? null,
})),
},
}
},
outputs: {
nextPageToken: { type: 'string', description: 'Next page token', optional: true },
calendars: {
type: 'array',
description: 'List of calendars',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Calendar ID' },
summary: { type: 'string', description: 'Calendar title' },
description: { type: 'string', description: 'Calendar description', optional: true },
location: { type: 'string', description: 'Calendar location', optional: true },
timeZone: { type: 'string', description: 'Calendar time zone' },
accessRole: { type: 'string', description: 'Access role for the calendar' },
backgroundColor: { type: 'string', description: 'Calendar background color' },
foregroundColor: { type: 'string', description: 'Calendar foreground color' },
primary: {
type: 'boolean',
description: 'Whether this is the primary calendar',
optional: true,
},
hidden: {
type: 'boolean',
description: 'Whether the calendar is hidden',
optional: true,
},
selected: {
type: 'boolean',
description: 'Whether the calendar is selected',
optional: true,
},
},
},
},
},
}

View File

@@ -0,0 +1,208 @@
import {
CALENDAR_API_BASE,
type GoogleCalendarApiEventResponse,
} from '@/tools/google_calendar/types'
import type { ToolConfig } from '@/tools/types'
interface GoogleCalendarMoveParams {
accessToken: string
calendarId?: string
eventId: string
destinationCalendarId: string
sendUpdates?: 'all' | 'externalOnly' | 'none'
}
interface GoogleCalendarMoveResponse {
success: boolean
output: {
content: string
metadata: {
id: string
htmlLink: string
status: string
summary: string
description?: string
location?: string
start: {
dateTime?: string
date?: string
timeZone?: string
}
end: {
dateTime?: string
date?: string
timeZone?: string
}
attendees?: Array<{
email: string
displayName?: string
responseStatus: string
}>
creator?: {
email: string
displayName?: string
}
organizer?: {
email: string
displayName?: string
}
}
}
}
export const moveTool: ToolConfig<GoogleCalendarMoveParams, GoogleCalendarMoveResponse> = {
id: 'google_calendar_move',
name: 'Google Calendar Move Event',
description: 'Move an event to a different calendar',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-calendar',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Google Calendar API',
},
calendarId: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Source calendar ID (defaults to primary)',
},
eventId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Event ID to move',
},
destinationCalendarId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Destination calendar ID',
},
sendUpdates: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'How to send updates to attendees: all, externalOnly, or none',
},
},
request: {
url: (params: GoogleCalendarMoveParams) => {
const calendarId = params.calendarId || 'primary'
const queryParams = new URLSearchParams()
queryParams.append('destination', params.destinationCalendarId)
if (params.sendUpdates !== undefined) {
queryParams.append('sendUpdates', params.sendUpdates)
}
return `${CALENDAR_API_BASE}/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(params.eventId)}/move?${queryParams.toString()}`
},
method: 'POST',
headers: (params: GoogleCalendarMoveParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data: GoogleCalendarApiEventResponse = await response.json()
return {
success: true,
output: {
content: `Event "${data.summary}" moved successfully`,
metadata: {
id: data.id,
htmlLink: data.htmlLink,
status: data.status,
summary: data.summary,
description: data.description,
location: data.location,
start: data.start,
end: data.end,
attendees: data.attendees,
creator: data.creator,
organizer: data.organizer,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Event move confirmation message' },
metadata: {
type: 'json',
description: 'Moved event metadata including new details',
},
},
}
interface GoogleCalendarMoveV2Response {
success: boolean
output: {
id: string
htmlLink: string
status: string
summary: string | null
description: string | null
location: string | null
start: any
end: any
attendees: any | null
creator: any
organizer: any
}
}
export const moveV2Tool: ToolConfig<GoogleCalendarMoveParams, GoogleCalendarMoveV2Response> = {
id: 'google_calendar_move_v2',
name: 'Google Calendar Move Event',
description: 'Move an event to a different calendar. Returns API-aligned fields only.',
version: '2.0.0',
oauth: moveTool.oauth,
params: moveTool.params,
request: moveTool.request,
transformResponse: async (response: Response) => {
const data: GoogleCalendarApiEventResponse = await response.json()
return {
success: true,
output: {
id: data.id,
htmlLink: data.htmlLink,
status: data.status,
summary: data.summary ?? null,
description: data.description ?? null,
location: data.location ?? null,
start: data.start,
end: data.end,
attendees: data.attendees ?? null,
creator: data.creator,
organizer: data.organizer,
},
}
},
outputs: {
id: { type: 'string', description: 'Event ID' },
htmlLink: { type: 'string', description: 'Event link' },
status: { type: 'string', description: 'Event status' },
summary: { type: 'string', description: 'Event title', optional: true },
description: { type: 'string', description: 'Event description', optional: true },
location: { type: 'string', description: 'Event location', optional: true },
start: { type: 'json', description: 'Event start' },
end: { type: 'json', description: 'Event end' },
attendees: { type: 'json', description: 'Event attendees', optional: true },
creator: { type: 'json', description: 'Event creator' },
organizer: { type: 'json', description: 'Event organizer' },
},
}

View File

@@ -75,6 +75,30 @@ export interface GoogleCalendarInviteParams extends BaseGoogleCalendarParams {
replaceExisting?: boolean // Whether to replace existing attendees or add to them
}
export interface GoogleCalendarMoveParams extends BaseGoogleCalendarParams {
eventId: string
destinationCalendarId: string
sendUpdates?: 'all' | 'externalOnly' | 'none'
}
export interface GoogleCalendarInstancesParams extends BaseGoogleCalendarParams {
eventId: string
timeMin?: string
timeMax?: string
maxResults?: number
pageToken?: string
showDeleted?: boolean
}
export interface GoogleCalendarListCalendarsParams {
accessToken: string
minAccessRole?: 'freeBusyReader' | 'reader' | 'writer' | 'owner'
maxResults?: number
pageToken?: string
showDeleted?: boolean
showHidden?: boolean
}
export type GoogleCalendarToolParams =
| GoogleCalendarCreateParams
| GoogleCalendarListParams
@@ -83,6 +107,9 @@ export type GoogleCalendarToolParams =
| GoogleCalendarDeleteParams
| GoogleCalendarQuickAddParams
| GoogleCalendarInviteParams
| GoogleCalendarMoveParams
| GoogleCalendarInstancesParams
| GoogleCalendarListCalendarsParams
interface EventMetadata {
id: string
@@ -277,6 +304,65 @@ export interface GoogleCalendarApiListResponse {
items: GoogleCalendarApiEventResponse[]
}
export interface GoogleCalendarDeleteResponse extends ToolResponse {
output: {
content: string
metadata: {
eventId: string
deleted: boolean
}
}
}
export interface GoogleCalendarMoveResponse extends ToolResponse {
output: {
content: string
metadata: EventMetadata
}
}
export interface GoogleCalendarInstancesResponse extends ToolResponse {
output: {
content: string
metadata: {
nextPageToken?: string
timeZone: string
instances: Array<
EventMetadata & {
recurringEventId: string
originalStartTime: {
dateTime?: string
date?: string
timeZone?: string
}
}
>
}
}
}
export interface GoogleCalendarListCalendarsResponse extends ToolResponse {
output: {
content: string
metadata: {
nextPageToken?: string
calendars: Array<{
id: string
summary: string
description?: string
location?: string
timeZone: string
accessRole: string
backgroundColor: string
foregroundColor: string
primary?: boolean
hidden?: boolean
selected?: boolean
}>
}
}
}
export type GoogleCalendarResponse =
| GoogleCalendarCreateResponse
| GoogleCalendarListResponse
@@ -284,3 +370,7 @@ export type GoogleCalendarResponse =
| GoogleCalendarQuickAddResponse
| GoogleCalendarInviteResponse
| GoogleCalendarUpdateResponse
| GoogleCalendarDeleteResponse
| GoogleCalendarMoveResponse
| GoogleCalendarInstancesResponse
| GoogleCalendarListCalendarsResponse

View File

@@ -0,0 +1,255 @@
import {
CALENDAR_API_BASE,
type GoogleCalendarApiEventResponse,
type GoogleCalendarUpdateParams,
type GoogleCalendarUpdateResponse,
} from '@/tools/google_calendar/types'
import type { ToolConfig } from '@/tools/types'
export const updateTool: ToolConfig<GoogleCalendarUpdateParams, GoogleCalendarUpdateResponse> = {
id: 'google_calendar_update',
name: 'Google Calendar Update Event',
description: 'Update an existing event in Google Calendar',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-calendar',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Google Calendar API',
},
calendarId: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Calendar ID (defaults to primary)',
},
eventId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Event ID to update',
},
summary: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New event title/summary',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New event description',
},
location: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New event location',
},
startDateTime: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'New start date and time. MUST include timezone offset (e.g., 2025-06-03T10:00:00-08:00) OR provide timeZone parameter',
},
endDateTime: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'New end date and time. MUST include timezone offset (e.g., 2025-06-03T11:00:00-08:00) OR provide timeZone parameter',
},
timeZone: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Time zone (e.g., America/Los_Angeles). Required if datetime does not include offset.',
},
attendees: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Array of attendee email addresses (replaces existing attendees)',
},
sendUpdates: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'How to send updates to attendees: all, externalOnly, or none',
},
},
request: {
url: (params: GoogleCalendarUpdateParams) => {
const calendarId = params.calendarId || 'primary'
const queryParams = new URLSearchParams()
if (params.sendUpdates !== undefined) {
queryParams.append('sendUpdates', params.sendUpdates)
}
const queryString = queryParams.toString()
return `${CALENDAR_API_BASE}/calendars/${encodeURIComponent(calendarId)}/events/${encodeURIComponent(params.eventId)}${queryString ? `?${queryString}` : ''}`
},
method: 'PATCH',
headers: (params: GoogleCalendarUpdateParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
body: (params: GoogleCalendarUpdateParams) => {
const updateData: Record<string, unknown> = {}
if (params.summary !== undefined) {
updateData.summary = params.summary
}
if (params.description !== undefined) {
updateData.description = params.description
}
if (params.location !== undefined) {
updateData.location = params.location
}
if (params.startDateTime !== undefined) {
const needsTimezone =
!params.startDateTime.includes('+') && !params.startDateTime.includes('-', 10)
updateData.start = {
dateTime: params.startDateTime,
...(needsTimezone && params.timeZone ? { timeZone: params.timeZone } : {}),
}
}
if (params.endDateTime !== undefined) {
const needsTimezone =
!params.endDateTime.includes('+') && !params.endDateTime.includes('-', 10)
updateData.end = {
dateTime: params.endDateTime,
...(needsTimezone && params.timeZone ? { timeZone: params.timeZone } : {}),
}
}
// Handle attendees - convert to array format
if (params.attendees !== undefined) {
let attendeeList: string[] = []
const attendees = params.attendees as string | string[]
if (Array.isArray(attendees)) {
attendeeList = attendees.filter((email: string) => email && email.trim().length > 0)
} else if (typeof attendees === 'string' && attendees.trim().length > 0) {
attendeeList = attendees
.split(',')
.map((email: string) => email.trim())
.filter((email: string) => email.length > 0)
}
updateData.attendees = attendeeList.map((email: string) => ({ email }))
}
return updateData
},
},
transformResponse: async (response: Response) => {
const data: GoogleCalendarApiEventResponse = await response.json()
return {
success: true,
output: {
content: `Event "${data.summary}" updated successfully`,
metadata: {
id: data.id,
htmlLink: data.htmlLink,
status: data.status,
summary: data.summary,
description: data.description,
location: data.location,
start: data.start,
end: data.end,
attendees: data.attendees,
creator: data.creator,
organizer: data.organizer,
},
},
}
},
outputs: {
content: { type: 'string', description: 'Event update confirmation message' },
metadata: {
type: 'json',
description: 'Updated event metadata including ID, status, and details',
},
},
}
interface GoogleCalendarUpdateV2Response {
success: boolean
output: {
id: string
htmlLink: string
status: string
summary: string | null
description: string | null
location: string | null
start: any
end: any
attendees: any | null
creator: any
organizer: any
}
}
export const updateV2Tool: ToolConfig<GoogleCalendarUpdateParams, GoogleCalendarUpdateV2Response> =
{
id: 'google_calendar_update_v2',
name: 'Google Calendar Update Event',
description: 'Update an existing event in Google Calendar. Returns API-aligned fields only.',
version: '2.0.0',
oauth: updateTool.oauth,
params: updateTool.params,
request: updateTool.request,
transformResponse: async (response: Response) => {
const data: GoogleCalendarApiEventResponse = await response.json()
return {
success: true,
output: {
id: data.id,
htmlLink: data.htmlLink,
status: data.status,
summary: data.summary ?? null,
description: data.description ?? null,
location: data.location ?? null,
start: data.start,
end: data.end,
attendees: data.attendees ?? null,
creator: data.creator,
organizer: data.organizer,
},
}
},
outputs: {
id: { type: 'string', description: 'Event ID' },
htmlLink: { type: 'string', description: 'Event link' },
status: { type: 'string', description: 'Event status' },
summary: { type: 'string', description: 'Event title', optional: true },
description: { type: 'string', description: 'Event description', optional: true },
location: { type: 'string', description: 'Event location', optional: true },
start: { type: 'json', description: 'Event start' },
end: { type: 'json', description: 'Event end' },
attendees: { type: 'json', description: 'Event attendees', optional: true },
creator: { type: 'json', description: 'Event creator' },
organizer: { type: 'json', description: 'Event organizer' },
},
}

View File

@@ -0,0 +1,111 @@
import type { GoogleDriveFile, GoogleDriveToolParams } from '@/tools/google_drive/types'
import { ALL_FILE_FIELDS } from '@/tools/google_drive/utils'
import type { ToolConfig, ToolResponse } from '@/tools/types'
interface GoogleDriveCopyParams extends GoogleDriveToolParams {
fileId: string
newName?: string
destinationFolderId?: string
}
interface GoogleDriveCopyResponse extends ToolResponse {
output: {
file: GoogleDriveFile
}
}
export const copyTool: ToolConfig<GoogleDriveCopyParams, GoogleDriveCopyResponse> = {
id: 'google_drive_copy',
name: 'Copy Google Drive File',
description: 'Create a copy of a file in Google Drive',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-drive',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
fileId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the file to copy',
},
newName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Name for the copied file (defaults to "Copy of [original name]")',
},
destinationFolderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'ID of the folder to place the copy in (defaults to same location as original)',
},
},
request: {
url: (params) => {
const url = new URL(`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}/copy`)
url.searchParams.append('fields', ALL_FILE_FIELDS)
url.searchParams.append('supportsAllDrives', 'true')
return url.toString()
},
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {}
if (params.newName) {
body.name = params.newName
}
if (params.destinationFolderId) {
body.parents = [params.destinationFolderId.trim()]
}
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || 'Failed to copy Google Drive file')
}
return {
success: true,
output: {
file: data,
},
}
},
outputs: {
file: {
type: 'json',
description: 'The copied file metadata',
properties: {
id: { type: 'string', description: 'Google Drive file ID of the copy' },
name: { type: 'string', description: 'File name' },
mimeType: { type: 'string', description: 'MIME type' },
webViewLink: { type: 'string', description: 'URL to view in browser' },
parents: { type: 'json', description: 'Parent folder IDs' },
createdTime: { type: 'string', description: 'File creation time' },
modifiedTime: { type: 'string', description: 'Last modification time' },
owners: { type: 'json', description: 'List of file owners' },
size: { type: 'string', description: 'File size in bytes' },
},
},
},
}

View File

@@ -0,0 +1,78 @@
import type { GoogleDriveToolParams } from '@/tools/google_drive/types'
import type { ToolConfig, ToolResponse } from '@/tools/types'
interface GoogleDriveDeleteParams extends GoogleDriveToolParams {
fileId: string
}
interface GoogleDriveDeleteResponse extends ToolResponse {
output: {
deleted: boolean
fileId: string
}
}
export const deleteTool: ToolConfig<GoogleDriveDeleteParams, GoogleDriveDeleteResponse> = {
id: 'google_drive_delete',
name: 'Delete Google Drive File',
description: 'Permanently delete a file from Google Drive (bypasses trash)',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-drive',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
fileId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the file to permanently delete',
},
},
request: {
url: (params) => {
const url = new URL(`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}`)
url.searchParams.append('supportsAllDrives', 'true')
return url.toString()
},
method: 'DELETE',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response, params) => {
if (!response.ok) {
const data = await response.json()
throw new Error(data.error?.message || 'Failed to delete Google Drive file')
}
return {
success: true,
output: {
deleted: true,
fileId: params?.fileId ?? '',
},
}
},
outputs: {
deleted: {
type: 'boolean',
description: 'Whether the file was successfully deleted',
},
fileId: {
type: 'string',
description: 'The ID of the deleted file',
},
},
}

View File

@@ -0,0 +1,137 @@
import type { GoogleDriveToolParams, GoogleDriveUser } from '@/tools/google_drive/types'
import type { ToolConfig, ToolResponse } from '@/tools/types'
interface GoogleDriveGetAboutParams extends GoogleDriveToolParams {}
interface GoogleDriveGetAboutResponse extends ToolResponse {
output: {
user: GoogleDriveUser & {
emailAddress: string
}
storageQuota: {
limit: string | null
usage: string
usageInDrive: string
usageInDriveTrash: string
}
canCreateDrives: boolean
importFormats: Record<string, string[]>
exportFormats: Record<string, string[]>
maxUploadSize: string
}
}
export const getAboutTool: ToolConfig<GoogleDriveGetAboutParams, GoogleDriveGetAboutResponse> = {
id: 'google_drive_get_about',
name: 'Get Google Drive Info',
description:
'Get information about the user and their Google Drive (storage quota, capabilities)',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-drive',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
},
request: {
url: () => {
const url = new URL('https://www.googleapis.com/drive/v3/about')
url.searchParams.append(
'fields',
'user,storageQuota,canCreateDrives,importFormats,exportFormats,maxUploadSize'
)
return url.toString()
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || 'Failed to get Google Drive info')
}
return {
success: true,
output: {
user: {
displayName: data.user?.displayName ?? null,
emailAddress: data.user?.emailAddress ?? '',
photoLink: data.user?.photoLink ?? null,
permissionId: data.user?.permissionId ?? null,
me: data.user?.me ?? true,
},
storageQuota: {
limit: data.storageQuota?.limit ?? null,
usage: data.storageQuota?.usage ?? '0',
usageInDrive: data.storageQuota?.usageInDrive ?? '0',
usageInDriveTrash: data.storageQuota?.usageInDriveTrash ?? '0',
},
canCreateDrives: data.canCreateDrives ?? false,
importFormats: data.importFormats ?? {},
exportFormats: data.exportFormats ?? {},
maxUploadSize: data.maxUploadSize ?? '0',
},
}
},
outputs: {
user: {
type: 'json',
description: 'Information about the authenticated user',
properties: {
displayName: { type: 'string', description: 'User display name' },
emailAddress: { type: 'string', description: 'User email address' },
photoLink: { type: 'string', description: 'URL to user profile photo', optional: true },
permissionId: { type: 'string', description: 'User permission ID' },
me: { type: 'boolean', description: 'Whether this is the authenticated user' },
},
},
storageQuota: {
type: 'json',
description: 'Storage quota information in bytes',
properties: {
limit: {
type: 'string',
description: 'Total storage limit in bytes (null for unlimited)',
optional: true,
},
usage: { type: 'string', description: 'Total storage used in bytes' },
usageInDrive: { type: 'string', description: 'Storage used by Drive files in bytes' },
usageInDriveTrash: {
type: 'string',
description: 'Storage used by trashed files in bytes',
},
},
},
canCreateDrives: {
type: 'boolean',
description: 'Whether user can create shared drives',
},
importFormats: {
type: 'json',
description: 'Map of MIME types that can be imported and their target formats',
},
exportFormats: {
type: 'json',
description: 'Map of Google Workspace MIME types and their exportable formats',
},
maxUploadSize: {
type: 'string',
description: 'Maximum upload size in bytes',
},
},
}

View File

@@ -0,0 +1,99 @@
import type { GoogleDriveFile, GoogleDriveToolParams } from '@/tools/google_drive/types'
import { ALL_FILE_FIELDS } from '@/tools/google_drive/utils'
import type { ToolConfig, ToolResponse } from '@/tools/types'
interface GoogleDriveGetFileParams extends GoogleDriveToolParams {
fileId: string
}
interface GoogleDriveGetFileResponse extends ToolResponse {
output: {
file: GoogleDriveFile
}
}
export const getFileTool: ToolConfig<GoogleDriveGetFileParams, GoogleDriveGetFileResponse> = {
id: 'google_drive_get_file',
name: 'Get Google Drive File',
description: 'Get metadata for a specific file in Google Drive by its ID',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-drive',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
fileId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the file to retrieve',
},
},
request: {
url: (params) => {
const url = new URL(`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}`)
url.searchParams.append('fields', ALL_FILE_FIELDS)
url.searchParams.append('supportsAllDrives', 'true')
return url.toString()
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || 'Failed to get Google Drive file')
}
return {
success: true,
output: {
file: data,
},
}
},
outputs: {
file: {
type: 'json',
description: 'The file metadata',
properties: {
id: { type: 'string', description: 'Google Drive file ID' },
name: { type: 'string', description: 'File name' },
mimeType: { type: 'string', description: 'MIME type' },
description: { type: 'string', description: 'File description', optional: true },
size: { type: 'string', description: 'File size in bytes', optional: true },
starred: { type: 'boolean', description: 'Whether file is starred' },
trashed: { type: 'boolean', description: 'Whether file is in trash' },
webViewLink: { type: 'string', description: 'URL to view in browser' },
webContentLink: { type: 'string', description: 'Direct download URL', optional: true },
iconLink: { type: 'string', description: 'URL to file icon' },
thumbnailLink: { type: 'string', description: 'URL to thumbnail', optional: true },
parents: { type: 'json', description: 'Parent folder IDs' },
owners: { type: 'json', description: 'List of file owners' },
permissions: { type: 'json', description: 'File permissions', optional: true },
createdTime: { type: 'string', description: 'File creation time' },
modifiedTime: { type: 'string', description: 'Last modification time' },
lastModifyingUser: { type: 'json', description: 'User who last modified the file' },
shared: { type: 'boolean', description: 'Whether file is shared' },
ownedByMe: { type: 'boolean', description: 'Whether owned by current user' },
capabilities: { type: 'json', description: 'User capabilities on file' },
md5Checksum: { type: 'string', description: 'MD5 hash', optional: true },
version: { type: 'string', description: 'Version number' },
},
},
},
}

View File

@@ -1,11 +1,31 @@
import { copyTool } from '@/tools/google_drive/copy'
import { createFolderTool } from '@/tools/google_drive/create_folder'
import { deleteTool } from '@/tools/google_drive/delete'
import { downloadTool } from '@/tools/google_drive/download'
import { getAboutTool } from '@/tools/google_drive/get_about'
import { getContentTool } from '@/tools/google_drive/get_content'
import { getFileTool } from '@/tools/google_drive/get_file'
import { listTool } from '@/tools/google_drive/list'
import { listPermissionsTool } from '@/tools/google_drive/list_permissions'
import { shareTool } from '@/tools/google_drive/share'
import { trashTool } from '@/tools/google_drive/trash'
import { unshareTool } from '@/tools/google_drive/unshare'
import { untrashTool } from '@/tools/google_drive/untrash'
import { updateTool } from '@/tools/google_drive/update'
import { uploadTool } from '@/tools/google_drive/upload'
export const googleDriveCopyTool = copyTool
export const googleDriveCreateFolderTool = createFolderTool
export const googleDriveDeleteTool = deleteTool
export const googleDriveDownloadTool = downloadTool
export const googleDriveGetAboutTool = getAboutTool
export const googleDriveGetContentTool = getContentTool
export const googleDriveGetFileTool = getFileTool
export const googleDriveListTool = listTool
export const googleDriveListPermissionsTool = listPermissionsTool
export const googleDriveShareTool = shareTool
export const googleDriveTrashTool = trashTool
export const googleDriveUnshareTool = unshareTool
export const googleDriveUntrashTool = untrashTool
export const googleDriveUpdateTool = updateTool
export const googleDriveUploadTool = uploadTool

View File

@@ -0,0 +1,124 @@
import type { GoogleDrivePermission, GoogleDriveToolParams } from '@/tools/google_drive/types'
import type { ToolConfig, ToolResponse } from '@/tools/types'
interface GoogleDriveListPermissionsParams extends GoogleDriveToolParams {
fileId: string
}
interface GoogleDriveListPermissionsResponse extends ToolResponse {
output: {
permissions: GoogleDrivePermission[]
}
}
export const listPermissionsTool: ToolConfig<
GoogleDriveListPermissionsParams,
GoogleDriveListPermissionsResponse
> = {
id: 'google_drive_list_permissions',
name: 'List Google Drive Permissions',
description: 'List all permissions (who has access) for a file in Google Drive',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-drive',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
fileId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the file to list permissions for',
},
},
request: {
url: (params) => {
const url = new URL(
`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}/permissions`
)
url.searchParams.append('supportsAllDrives', 'true')
url.searchParams.append(
'fields',
'permissions(id,type,role,emailAddress,displayName,photoLink,domain,expirationTime,deleted,allowFileDiscovery,pendingOwner,permissionDetails)'
)
return url.toString()
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || 'Failed to list Google Drive permissions')
}
const permissions = (data.permissions ?? []).map((p: Record<string, unknown>) => ({
id: p.id ?? null,
type: p.type ?? null,
role: p.role ?? null,
emailAddress: p.emailAddress ?? null,
displayName: p.displayName ?? null,
photoLink: p.photoLink ?? null,
domain: p.domain ?? null,
expirationTime: p.expirationTime ?? null,
deleted: p.deleted ?? false,
allowFileDiscovery: p.allowFileDiscovery ?? null,
pendingOwner: p.pendingOwner ?? false,
permissionDetails: p.permissionDetails ?? null,
}))
return {
success: true,
output: {
permissions,
},
}
},
outputs: {
permissions: {
type: 'array',
description: 'List of permissions on the file',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Permission ID (use to remove permission)' },
type: { type: 'string', description: 'Grantee type (user, group, domain, anyone)' },
role: {
type: 'string',
description:
'Permission role (owner, organizer, fileOrganizer, writer, commenter, reader)',
},
emailAddress: { type: 'string', description: 'Email of the grantee' },
displayName: { type: 'string', description: 'Display name of the grantee' },
photoLink: { type: 'string', description: 'Photo URL of the grantee' },
domain: { type: 'string', description: 'Domain of the grantee' },
expirationTime: { type: 'string', description: 'When permission expires' },
deleted: { type: 'boolean', description: 'Whether grantee account is deleted' },
allowFileDiscovery: {
type: 'boolean',
description: 'Whether file is discoverable by grantee',
},
pendingOwner: { type: 'boolean', description: 'Whether ownership transfer is pending' },
permissionDetails: {
type: 'json',
description: 'Details about inherited permissions',
},
},
},
},
},
}

View File

@@ -0,0 +1,178 @@
import type { GoogleDrivePermission, GoogleDriveToolParams } from '@/tools/google_drive/types'
import type { ToolConfig, ToolResponse } from '@/tools/types'
interface GoogleDriveShareParams extends GoogleDriveToolParams {
fileId: string
email?: string
domain?: string
type: 'user' | 'group' | 'domain' | 'anyone'
role: 'owner' | 'organizer' | 'fileOrganizer' | 'writer' | 'commenter' | 'reader'
transferOwnership?: boolean
moveToNewOwnersRoot?: boolean
sendNotification?: boolean
emailMessage?: string
}
interface GoogleDriveShareResponse extends ToolResponse {
output: {
permission: GoogleDrivePermission
}
}
export const shareTool: ToolConfig<GoogleDriveShareParams, GoogleDriveShareResponse> = {
id: 'google_drive_share',
name: 'Share Google Drive File',
description: 'Share a file with a user, group, domain, or make it public',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-drive',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
fileId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the file to share',
},
type: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Type of grantee: user, group, domain, or anyone',
},
role: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Permission role: owner (transfer ownership), organizer (shared drive only), fileOrganizer (shared drive only), writer (edit), commenter (view and comment), reader (view only)',
},
email: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Email address of the user or group (required for type=user or type=group)',
},
domain: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Domain to share with (required for type=domain)',
},
transferOwnership: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Required when role is owner. Transfers ownership to the specified user.',
},
moveToNewOwnersRoot: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description:
"When transferring ownership, move the file to the new owner's My Drive root folder.",
},
sendNotification: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to send an email notification (default: true)',
},
emailMessage: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Custom message to include in the notification email',
},
},
request: {
url: (params) => {
const url = new URL(
`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}/permissions`
)
url.searchParams.append('supportsAllDrives', 'true')
if (params.transferOwnership) {
url.searchParams.append('transferOwnership', 'true')
}
if (params.moveToNewOwnersRoot) {
url.searchParams.append('moveToNewOwnersRoot', 'true')
}
if (params.sendNotification !== undefined) {
url.searchParams.append('sendNotificationEmail', String(params.sendNotification))
}
if (params.emailMessage) {
url.searchParams.append('emailMessage', params.emailMessage)
}
return url.toString()
},
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
type: params.type,
role: params.role,
}
if (params.email) {
body.emailAddress = params.email.trim()
}
if (params.domain) {
body.domain = params.domain.trim()
}
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || 'Failed to share Google Drive file')
}
return {
success: true,
output: {
permission: {
id: data.id ?? null,
type: data.type ?? null,
role: data.role ?? null,
emailAddress: data.emailAddress ?? null,
displayName: data.displayName ?? null,
domain: data.domain ?? null,
expirationTime: data.expirationTime ?? null,
deleted: data.deleted ?? false,
},
},
}
},
outputs: {
permission: {
type: 'json',
description: 'The created permission details',
properties: {
id: { type: 'string', description: 'Permission ID' },
type: { type: 'string', description: 'Grantee type (user, group, domain, anyone)' },
role: { type: 'string', description: 'Permission role' },
emailAddress: { type: 'string', description: 'Email of the grantee', optional: true },
displayName: { type: 'string', description: 'Display name of the grantee', optional: true },
domain: { type: 'string', description: 'Domain of the grantee', optional: true },
expirationTime: { type: 'string', description: 'Expiration time', optional: true },
deleted: { type: 'boolean', description: 'Whether grantee is deleted' },
},
},
},
}

View File

@@ -0,0 +1,87 @@
import type { GoogleDriveFile, GoogleDriveToolParams } from '@/tools/google_drive/types'
import { ALL_FILE_FIELDS } from '@/tools/google_drive/utils'
import type { ToolConfig, ToolResponse } from '@/tools/types'
interface GoogleDriveTrashParams extends GoogleDriveToolParams {
fileId: string
}
interface GoogleDriveTrashResponse extends ToolResponse {
output: {
file: GoogleDriveFile
}
}
export const trashTool: ToolConfig<GoogleDriveTrashParams, GoogleDriveTrashResponse> = {
id: 'google_drive_trash',
name: 'Trash Google Drive File',
description: 'Move a file to the trash in Google Drive (can be restored later)',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-drive',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
fileId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the file to move to trash',
},
},
request: {
url: (params) => {
const url = new URL(`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}`)
url.searchParams.append('fields', ALL_FILE_FIELDS)
url.searchParams.append('supportsAllDrives', 'true')
return url.toString()
},
method: 'PATCH',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
body: () => ({
trashed: true,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || 'Failed to trash Google Drive file')
}
return {
success: true,
output: {
file: data,
},
}
},
outputs: {
file: {
type: 'json',
description: 'The trashed file metadata',
properties: {
id: { type: 'string', description: 'Google Drive file ID' },
name: { type: 'string', description: 'File name' },
mimeType: { type: 'string', description: 'MIME type' },
trashed: { type: 'boolean', description: 'Whether file is in trash (should be true)' },
trashedTime: { type: 'string', description: 'When file was trashed' },
webViewLink: { type: 'string', description: 'URL to view in browser' },
},
},
},
}

View File

@@ -0,0 +1,93 @@
import type { GoogleDriveToolParams } from '@/tools/google_drive/types'
import type { ToolConfig, ToolResponse } from '@/tools/types'
interface GoogleDriveUnshareParams extends GoogleDriveToolParams {
fileId: string
permissionId: string
}
interface GoogleDriveUnshareResponse extends ToolResponse {
output: {
removed: boolean
fileId: string
permissionId: string
}
}
export const unshareTool: ToolConfig<GoogleDriveUnshareParams, GoogleDriveUnshareResponse> = {
id: 'google_drive_unshare',
name: 'Unshare Google Drive File',
description: 'Remove a permission from a file (revoke access)',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-drive',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
fileId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the file to modify permissions on',
},
permissionId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the permission to remove (use list_permissions to find this)',
},
},
request: {
url: (params) => {
const url = new URL(
`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}/permissions/${params.permissionId?.trim()}`
)
url.searchParams.append('supportsAllDrives', 'true')
return url.toString()
},
method: 'DELETE',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response, params) => {
if (!response.ok) {
const data = await response.json()
throw new Error(data.error?.message || 'Failed to remove permission from Google Drive file')
}
return {
success: true,
output: {
removed: true,
fileId: params?.fileId ?? '',
permissionId: params?.permissionId ?? '',
},
}
},
outputs: {
removed: {
type: 'boolean',
description: 'Whether the permission was successfully removed',
},
fileId: {
type: 'string',
description: 'The ID of the file',
},
permissionId: {
type: 'string',
description: 'The ID of the removed permission',
},
},
}

View File

@@ -0,0 +1,87 @@
import type { GoogleDriveFile, GoogleDriveToolParams } from '@/tools/google_drive/types'
import { ALL_FILE_FIELDS } from '@/tools/google_drive/utils'
import type { ToolConfig, ToolResponse } from '@/tools/types'
interface GoogleDriveUntrashParams extends GoogleDriveToolParams {
fileId: string
}
interface GoogleDriveUntrashResponse extends ToolResponse {
output: {
file: GoogleDriveFile
}
}
export const untrashTool: ToolConfig<GoogleDriveUntrashParams, GoogleDriveUntrashResponse> = {
id: 'google_drive_untrash',
name: 'Restore Google Drive File',
description: 'Restore a file from the trash in Google Drive',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-drive',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
fileId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the file to restore from trash',
},
},
request: {
url: (params) => {
const url = new URL(`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}`)
url.searchParams.append('fields', ALL_FILE_FIELDS)
url.searchParams.append('supportsAllDrives', 'true')
return url.toString()
},
method: 'PATCH',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
body: () => ({
trashed: false,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || 'Failed to restore Google Drive file from trash')
}
return {
success: true,
output: {
file: data,
},
}
},
outputs: {
file: {
type: 'json',
description: 'The restored file metadata',
properties: {
id: { type: 'string', description: 'Google Drive file ID' },
name: { type: 'string', description: 'File name' },
mimeType: { type: 'string', description: 'MIME type' },
trashed: { type: 'boolean', description: 'Whether file is in trash (should be false)' },
webViewLink: { type: 'string', description: 'URL to view in browser' },
parents: { type: 'json', description: 'Parent folder IDs' },
},
},
},
}

View File

@@ -0,0 +1,140 @@
import type { GoogleDriveFile, GoogleDriveToolParams } from '@/tools/google_drive/types'
import { ALL_FILE_FIELDS } from '@/tools/google_drive/utils'
import type { ToolConfig, ToolResponse } from '@/tools/types'
interface GoogleDriveUpdateParams extends GoogleDriveToolParams {
fileId: string
name?: string
description?: string
addParents?: string
removeParents?: string
starred?: boolean
}
interface GoogleDriveUpdateResponse extends ToolResponse {
output: {
file: GoogleDriveFile
}
}
export const updateTool: ToolConfig<GoogleDriveUpdateParams, GoogleDriveUpdateResponse> = {
id: 'google_drive_update',
name: 'Update Google Drive File',
description: 'Update file metadata in Google Drive (rename, move, star, add description)',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-drive',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
fileId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the file to update',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New name for the file',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New description for the file',
},
addParents: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of parent folder IDs to add (moves file to these folders)',
},
removeParents: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of parent folder IDs to remove',
},
starred: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to star or unstar the file',
},
},
request: {
url: (params) => {
const url = new URL(`https://www.googleapis.com/drive/v3/files/${params.fileId?.trim()}`)
url.searchParams.append('fields', ALL_FILE_FIELDS)
url.searchParams.append('supportsAllDrives', 'true')
if (params.addParents) {
url.searchParams.append('addParents', params.addParents.trim())
}
if (params.removeParents) {
url.searchParams.append('removeParents', params.removeParents.trim())
}
return url.toString()
},
method: 'PATCH',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {}
if (params.name !== undefined) {
body.name = params.name
}
if (params.description !== undefined) {
body.description = params.description
}
if (params.starred !== undefined) {
body.starred = params.starred
}
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || 'Failed to update Google Drive file')
}
return {
success: true,
output: {
file: data,
},
}
},
outputs: {
file: {
type: 'json',
description: 'The updated file metadata',
properties: {
id: { type: 'string', description: 'Google Drive file ID' },
name: { type: 'string', description: 'File name' },
mimeType: { type: 'string', description: 'MIME type' },
description: { type: 'string', description: 'File description', optional: true },
starred: { type: 'boolean', description: 'Whether file is starred' },
webViewLink: { type: 'string', description: 'URL to view in browser' },
parents: { type: 'json', description: 'Parent folder IDs' },
modifiedTime: { type: 'string', description: 'Last modification time' },
},
},
},
}

Some files were not shown because too many files have changed in this diff Show More