Compare commits

..

1 Commits

Author SHA1 Message Date
Emir Karabeg
bed37ab6fc feat(terminal): added collapse JSON 2026-01-26 22:50:59 -08:00
89 changed files with 2347 additions and 8886 deletions

View File

@@ -101,6 +101,7 @@ import {
ShopifyIcon,
SlackIcon,
SmtpIcon,
SpotifyIcon,
SQSIcon,
SshIcon,
STTIcon,
@@ -181,7 +182,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
jina: JinaAIIcon,
jira: JiraIcon,
jira_service_management: JiraServiceManagementIcon,
kalshi_v2: KalshiIcon,
kalshi: KalshiIcon,
knowledge: PackageSearchIcon,
langsmith: LangsmithIcon,
lemlist: LemlistIcon,
@@ -228,6 +229,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
shopify: ShopifyIcon,
slack: SlackIcon,
smtp: SmtpIcon,
spotify: SpotifyIcon,
sqs: SQSIcon,
ssh: SshIcon,
stagehand: StagehandIcon,

View File

@@ -47,7 +47,6 @@ Runs a browser automation task using BrowserUse
| `save_browser_data` | boolean | No | Whether to save browser data |
| `model` | string | No | LLM model to use \(default: gpt-4o\) |
| `apiKey` | string | Yes | API key for BrowserUse API |
| `profile_id` | string | No | Browser profile ID for persistent sessions \(cookies, login state\) |
#### Output

View File

@@ -647,42 +647,6 @@ Retrieve a single ticket by ID from Intercom. Returns API-aligned fields only.
| `ticketId` | string | ID of the retrieved ticket |
| `success` | boolean | Operation success status |
### `intercom_update_ticket`
Update a ticket in Intercom (change state, assignment, attributes)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `ticketId` | string | Yes | The ID of the ticket to update |
| `ticket_attributes` | string | No | JSON object with ticket attributes \(e.g., \{"_default_title_":"New Title","_default_description_":"Updated description"\}\) |
| `open` | boolean | No | Set to false to close the ticket, true to keep it open |
| `is_shared` | boolean | No | Whether the ticket is visible to users |
| `snoozed_until` | number | No | Unix timestamp for when the ticket should reopen |
| `admin_id` | string | No | The ID of the admin performing the update \(needed for workflows and attribution\) |
| `assignee_id` | string | No | The ID of the admin or team to assign the ticket to. Set to "0" to unassign. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `ticket` | object | The updated ticket object |
| ↳ `id` | string | Unique identifier for the ticket |
| ↳ `type` | string | Object type \(ticket\) |
| ↳ `ticket_id` | string | Ticket ID shown in Intercom UI |
| ↳ `ticket_state` | string | State of the ticket |
| ↳ `ticket_attributes` | object | Attributes of the ticket |
| ↳ `open` | boolean | Whether the ticket is open |
| ↳ `is_shared` | boolean | Whether the ticket is visible to users |
| ↳ `snoozed_until` | number | Unix timestamp when ticket will reopen |
| ↳ `admin_assignee_id` | string | ID of assigned admin |
| ↳ `team_assignee_id` | string | ID of assigned team |
| ↳ `created_at` | number | Unix timestamp when ticket was created |
| ↳ `updated_at` | number | Unix timestamp when ticket was last updated |
| `ticketId` | string | ID of the updated ticket |
| `ticket_state` | string | Current state of the ticket |
### `intercom_create_message`
Create and send a new admin-initiated message in Intercom. Returns API-aligned fields only.
@@ -716,340 +680,4 @@ Create and send a new admin-initiated message in Intercom. Returns API-aligned f
| `messageId` | string | ID of the created message |
| `success` | boolean | Operation success status |
### `intercom_list_admins`
Fetch a list of all admins for the workspace
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `admins` | array | Array of admin objects |
| ↳ `id` | string | Unique identifier for the admin |
| ↳ `type` | string | Object type \(admin\) |
| ↳ `name` | string | Name of the admin |
| ↳ `email` | string | Email of the admin |
| ↳ `job_title` | string | Job title of the admin |
| ↳ `away_mode_enabled` | boolean | Whether admin is in away mode |
| ↳ `away_mode_reassign` | boolean | Whether to reassign conversations when away |
| ↳ `has_inbox_seat` | boolean | Whether admin has a paid inbox seat |
| ↳ `team_ids` | array | List of team IDs the admin belongs to |
| ↳ `avatar` | object | Avatar information |
| ↳ `email_verified` | boolean | Whether email is verified |
| `type` | string | Object type \(admin.list\) |
### `intercom_close_conversation`
Close a conversation in Intercom
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `conversationId` | string | Yes | The ID of the conversation to close |
| `admin_id` | string | Yes | The ID of the admin performing the action |
| `body` | string | No | Optional closing message to add to the conversation |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `conversation` | object | The closed conversation object |
| ↳ `id` | string | Unique identifier for the conversation |
| ↳ `type` | string | Object type \(conversation\) |
| ↳ `state` | string | State of the conversation \(closed\) |
| ↳ `open` | boolean | Whether the conversation is open \(false\) |
| ↳ `read` | boolean | Whether the conversation has been read |
| ↳ `created_at` | number | Unix timestamp when conversation was created |
| ↳ `updated_at` | number | Unix timestamp when conversation was last updated |
| `conversationId` | string | ID of the closed conversation |
| `state` | string | State of the conversation \(closed\) |
### `intercom_open_conversation`
Open a closed or snoozed conversation in Intercom
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `conversationId` | string | Yes | The ID of the conversation to open |
| `admin_id` | string | Yes | The ID of the admin performing the action |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `conversation` | object | The opened conversation object |
| ↳ `id` | string | Unique identifier for the conversation |
| ↳ `type` | string | Object type \(conversation\) |
| ↳ `state` | string | State of the conversation \(open\) |
| ↳ `open` | boolean | Whether the conversation is open \(true\) |
| ↳ `read` | boolean | Whether the conversation has been read |
| ↳ `created_at` | number | Unix timestamp when conversation was created |
| ↳ `updated_at` | number | Unix timestamp when conversation was last updated |
| `conversationId` | string | ID of the opened conversation |
| `state` | string | State of the conversation \(open\) |
### `intercom_snooze_conversation`
Snooze a conversation to reopen at a future time
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `conversationId` | string | Yes | The ID of the conversation to snooze |
| `admin_id` | string | Yes | The ID of the admin performing the action |
| `snoozed_until` | number | Yes | Unix timestamp for when the conversation should reopen |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `conversation` | object | The snoozed conversation object |
| ↳ `id` | string | Unique identifier for the conversation |
| ↳ `type` | string | Object type \(conversation\) |
| ↳ `state` | string | State of the conversation \(snoozed\) |
| ↳ `open` | boolean | Whether the conversation is open |
| ↳ `snoozed_until` | number | Unix timestamp when conversation will reopen |
| ↳ `created_at` | number | Unix timestamp when conversation was created |
| ↳ `updated_at` | number | Unix timestamp when conversation was last updated |
| `conversationId` | string | ID of the snoozed conversation |
| `state` | string | State of the conversation \(snoozed\) |
| `snoozed_until` | number | Unix timestamp when conversation will reopen |
### `intercom_assign_conversation`
Assign a conversation to an admin or team in Intercom
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `conversationId` | string | Yes | The ID of the conversation to assign |
| `admin_id` | string | Yes | The ID of the admin performing the assignment |
| `assignee_id` | string | Yes | The ID of the admin or team to assign the conversation to. Set to "0" to unassign. |
| `body` | string | No | Optional message to add when assigning \(e.g., "Passing to the support team"\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `conversation` | object | The assigned conversation object |
| ↳ `id` | string | Unique identifier for the conversation |
| ↳ `type` | string | Object type \(conversation\) |
| ↳ `state` | string | State of the conversation |
| ↳ `open` | boolean | Whether the conversation is open |
| ↳ `admin_assignee_id` | number | ID of the assigned admin |
| ↳ `team_assignee_id` | string | ID of the assigned team |
| ↳ `created_at` | number | Unix timestamp when conversation was created |
| ↳ `updated_at` | number | Unix timestamp when conversation was last updated |
| `conversationId` | string | ID of the assigned conversation |
| `admin_assignee_id` | number | ID of the assigned admin |
| `team_assignee_id` | string | ID of the assigned team |
### `intercom_list_tags`
Fetch a list of all tags in the workspace
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `tags` | array | Array of tag objects |
| ↳ `id` | string | Unique identifier for the tag |
| ↳ `type` | string | Object type \(tag\) |
| ↳ `name` | string | Name of the tag |
| `type` | string | Object type \(list\) |
### `intercom_create_tag`
Create a new tag or update an existing tag name
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `name` | string | Yes | The name of the tag. Will create a new tag if not found, or update the name if id is provided. |
| `id` | string | No | The ID of an existing tag to update. Omit to create a new tag. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Unique identifier for the tag |
| `name` | string | Name of the tag |
| `type` | string | Object type \(tag\) |
### `intercom_tag_contact`
Add a tag to a specific contact
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `contactId` | string | Yes | The ID of the contact to tag |
| `tagId` | string | Yes | The ID of the tag to apply |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Unique identifier for the tag |
| `name` | string | Name of the tag |
| `type` | string | Object type \(tag\) |
### `intercom_untag_contact`
Remove a tag from a specific contact
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `contactId` | string | Yes | The ID of the contact to untag |
| `tagId` | string | Yes | The ID of the tag to remove |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Unique identifier for the tag that was removed |
| `name` | string | Name of the tag that was removed |
| `type` | string | Object type \(tag\) |
### `intercom_tag_conversation`
Add a tag to a specific conversation
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `conversationId` | string | Yes | The ID of the conversation to tag |
| `tagId` | string | Yes | The ID of the tag to apply |
| `admin_id` | string | Yes | The ID of the admin applying the tag |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Unique identifier for the tag |
| `name` | string | Name of the tag |
| `type` | string | Object type \(tag\) |
### `intercom_create_note`
Add a note to a specific contact
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `contactId` | string | Yes | The ID of the contact to add the note to |
| `body` | string | Yes | The text content of the note |
| `admin_id` | string | No | The ID of the admin creating the note |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Unique identifier for the note |
| `body` | string | The text content of the note |
| `created_at` | number | Unix timestamp when the note was created |
| `type` | string | Object type \(note\) |
| `author` | object | The admin who created the note |
| ↳ `type` | string | Author type \(admin\) |
| ↳ `id` | string | Author ID |
| ↳ `name` | string | Author name |
| ↳ `email` | string | Author email |
| `contact` | object | The contact the note was created for |
| ↳ `type` | string | Contact type |
| ↳ `id` | string | Contact ID |
### `intercom_create_event`
Track a custom event for a contact in Intercom
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `event_name` | string | Yes | The name of the event \(e.g., "order-completed"\). Use past-tense verb-noun format for readability. |
| `created_at` | number | No | Unix timestamp for when the event occurred. Strongly recommended for uniqueness. |
| `user_id` | string | No | Your identifier for the user \(external_id\) |
| `email` | string | No | Email address of the user. Use only if your app uses email to uniquely identify users. |
| `id` | string | No | The Intercom contact ID |
| `metadata` | string | No | JSON object with up to 10 metadata key-value pairs about the event \(e.g., \{"order_value": 99.99\}\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `accepted` | boolean | Whether the event was accepted \(202 Accepted\) |
### `intercom_attach_contact_to_company`
Attach a contact to a company in Intercom
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `contactId` | string | Yes | The ID of the contact to attach to the company |
| `companyId` | string | Yes | The ID of the company to attach the contact to |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `company` | object | The company object the contact was attached to |
| ↳ `id` | string | Unique identifier for the company |
| ↳ `type` | string | Object type \(company\) |
| ↳ `company_id` | string | The company_id you defined |
| ↳ `name` | string | Name of the company |
| ↳ `created_at` | number | Unix timestamp when company was created |
| ↳ `updated_at` | number | Unix timestamp when company was updated |
| ↳ `user_count` | number | Number of users in the company |
| ↳ `session_count` | number | Number of sessions |
| ↳ `monthly_spend` | number | Monthly spend amount |
| ↳ `plan` | object | Company plan details |
| `companyId` | string | ID of the company |
| `name` | string | Name of the company |
### `intercom_detach_contact_from_company`
Remove a contact from a company in Intercom
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `contactId` | string | Yes | The ID of the contact to detach from the company |
| `companyId` | string | Yes | The ID of the company to detach the contact from |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `company` | object | The company object the contact was detached from |
| ↳ `id` | string | Unique identifier for the company |
| ↳ `type` | string | Object type \(company\) |
| ↳ `company_id` | string | The company_id you defined |
| ↳ `name` | string | Name of the company |
| `companyId` | string | ID of the company |
| `name` | string | Name of the company |

View File

@@ -6,7 +6,7 @@ description: Access prediction markets and trade on Kalshi
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="kalshi_v2"
type="kalshi"
color="#09C285"
/>
@@ -36,7 +36,7 @@ Integrate Kalshi prediction markets into the workflow. Can get markets, market,
### `kalshi_get_markets`
Retrieve a list of prediction markets from Kalshi with all filtering options (V2 - full API response)
Retrieve a list of prediction markets from Kalshi with optional filtering
#### Input
@@ -52,12 +52,12 @@ Retrieve a list of prediction markets from Kalshi with all filtering options (V2
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `markets` | array | Array of market objects with all API fields |
| `cursor` | string | Pagination cursor for fetching more results |
| `markets` | array | Array of market objects |
| `paging` | object | Pagination cursor for fetching more results |
### `kalshi_get_market`
Retrieve details of a specific prediction market by ticker (V2 - full API response)
Retrieve details of a specific prediction market by ticker
#### Input
@@ -69,62 +69,11 @@ Retrieve details of a specific prediction market by ticker (V2 - full API respon
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `market` | object | Market object with all API fields |
| ↳ `ticker` | string | Market ticker |
| ↳ `event_ticker` | string | Event ticker |
| ↳ `market_type` | string | Market type |
| ↳ `title` | string | Market title |
| ↳ `subtitle` | string | Market subtitle |
| ↳ `yes_sub_title` | string | Yes outcome subtitle |
| ↳ `no_sub_title` | string | No outcome subtitle |
| ↳ `open_time` | string | Market open time |
| ↳ `close_time` | string | Market close time |
| ↳ `expected_expiration_time` | string | Expected expiration time |
| ↳ `expiration_time` | string | Expiration time |
| ↳ `latest_expiration_time` | string | Latest expiration time |
| ↳ `settlement_timer_seconds` | number | Settlement timer in seconds |
| ↳ `status` | string | Market status |
| ↳ `response_price_units` | string | Response price units |
| ↳ `notional_value` | number | Notional value |
| ↳ `tick_size` | number | Tick size |
| ↳ `yes_bid` | number | Current yes bid price |
| ↳ `yes_ask` | number | Current yes ask price |
| ↳ `no_bid` | number | Current no bid price |
| ↳ `no_ask` | number | Current no ask price |
| ↳ `last_price` | number | Last trade price |
| ↳ `previous_yes_bid` | number | Previous yes bid |
| ↳ `previous_yes_ask` | number | Previous yes ask |
| ↳ `previous_price` | number | Previous price |
| ↳ `volume` | number | Total volume |
| ↳ `volume_24h` | number | 24-hour volume |
| ↳ `liquidity` | number | Market liquidity |
| ↳ `open_interest` | number | Open interest |
| ↳ `result` | string | Market result |
| ↳ `cap_strike` | number | Cap strike |
| ↳ `floor_strike` | number | Floor strike |
| ↳ `can_close_early` | boolean | Can close early |
| ↳ `expiration_value` | string | Expiration value |
| ↳ `category` | string | Market category |
| ↳ `risk_limit_cents` | number | Risk limit in cents |
| ↳ `strike_type` | string | Strike type |
| ↳ `rules_primary` | string | Primary rules |
| ↳ `rules_secondary` | string | Secondary rules |
| ↳ `settlement_source_url` | string | Settlement source URL |
| ↳ `custom_strike` | object | Custom strike object |
| ↳ `underlying` | string | Underlying asset |
| ↳ `settlement_value` | number | Settlement value |
| ↳ `cfd_contract_size` | number | CFD contract size |
| ↳ `yes_fee_fp` | number | Yes fee \(fixed-point\) |
| ↳ `no_fee_fp` | number | No fee \(fixed-point\) |
| ↳ `last_price_fp` | number | Last price \(fixed-point\) |
| ↳ `yes_bid_fp` | number | Yes bid \(fixed-point\) |
| ↳ `yes_ask_fp` | number | Yes ask \(fixed-point\) |
| ↳ `no_bid_fp` | number | No bid \(fixed-point\) |
| ↳ `no_ask_fp` | number | No ask \(fixed-point\) |
| `market` | object | Market object with details |
### `kalshi_get_events`
Retrieve a list of events from Kalshi with optional filtering (V2 - exact API response)
Retrieve a list of events from Kalshi with optional filtering
#### Input
@@ -141,12 +90,11 @@ Retrieve a list of events from Kalshi with optional filtering (V2 - exact API re
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `events` | array | Array of event objects |
| `milestones` | array | Array of milestone objects \(if requested\) |
| `cursor` | string | Pagination cursor for fetching more results |
| `paging` | object | Pagination cursor for fetching more results |
### `kalshi_get_event`
Retrieve details of a specific event by ticker (V2 - exact API response)
Retrieve details of a specific event by ticker
#### Input
@@ -159,23 +107,11 @@ Retrieve details of a specific event by ticker (V2 - exact API response)
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `event` | object | Event object with full details matching Kalshi API response |
| ↳ `event_ticker` | string | Event ticker |
| ↳ `series_ticker` | string | Series ticker |
| ↳ `title` | string | Event title |
| ↳ `sub_title` | string | Event subtitle |
| ↳ `mutually_exclusive` | boolean | Mutually exclusive markets |
| ↳ `category` | string | Event category |
| ↳ `collateral_return_type` | string | Collateral return type |
| ↳ `strike_date` | string | Strike date |
| ↳ `strike_period` | string | Strike period |
| ↳ `available_on_brokers` | boolean | Available on brokers |
| ↳ `product_metadata` | object | Product metadata |
| ↳ `markets` | array | Nested markets \(if requested\) |
| `event` | object | Event object with details |
### `kalshi_get_balance`
Retrieve your account balance and portfolio value from Kalshi (V2 - exact API response)
Retrieve your account balance and portfolio value from Kalshi
#### Input
@@ -189,12 +125,11 @@ Retrieve your account balance and portfolio value from Kalshi (V2 - exact API re
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `balance` | number | Account balance in cents |
| `portfolio_value` | number | Portfolio value in cents |
| `updated_ts` | number | Unix timestamp of last update \(milliseconds\) |
| `portfolioValue` | number | Portfolio value in cents |
### `kalshi_get_positions`
Retrieve your open positions from Kalshi (V2 - exact API response)
Retrieve your open positions from Kalshi
#### Input
@@ -212,13 +147,12 @@ Retrieve your open positions from Kalshi (V2 - exact API response)
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `market_positions` | array | Array of market position objects |
| `event_positions` | array | Array of event position objects |
| `cursor` | string | Pagination cursor for fetching more results |
| `positions` | array | Array of position objects |
| `paging` | object | Pagination cursor for fetching more results |
### `kalshi_get_orders`
Retrieve your orders from Kalshi with optional filtering (V2 with full API response)
Retrieve your orders from Kalshi with optional filtering
#### Input
@@ -236,12 +170,12 @@ Retrieve your orders from Kalshi with optional filtering (V2 with full API respo
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `orders` | array | Array of order objects with full API response fields |
| `cursor` | string | Pagination cursor for fetching more results |
| `orders` | array | Array of order objects |
| `paging` | object | Pagination cursor for fetching more results |
### `kalshi_get_order`
Retrieve details of a specific order by ID from Kalshi (V2 with full API response)
Retrieve details of a specific order by ID from Kalshi
#### Input
@@ -255,44 +189,11 @@ Retrieve details of a specific order by ID from Kalshi (V2 with full API respons
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `order` | object | Order object with full API response fields |
| ↳ `order_id` | string | Order ID |
| ↳ `user_id` | string | User ID |
| ↳ `client_order_id` | string | Client order ID |
| ↳ `ticker` | string | Market ticker |
| ↳ `side` | string | Order side \(yes/no\) |
| ↳ `action` | string | Action \(buy/sell\) |
| ↳ `type` | string | Order type \(limit/market\) |
| ↳ `status` | string | Order status \(resting/canceled/executed\) |
| ↳ `yes_price` | number | Yes price in cents |
| ↳ `no_price` | number | No price in cents |
| ↳ `yes_price_dollars` | string | Yes price in dollars |
| ↳ `no_price_dollars` | string | No price in dollars |
| ↳ `fill_count` | number | Filled contract count |
| ↳ `fill_count_fp` | string | Filled count \(fixed-point\) |
| ↳ `remaining_count` | number | Remaining contracts |
| ↳ `remaining_count_fp` | string | Remaining count \(fixed-point\) |
| ↳ `initial_count` | number | Initial contract count |
| ↳ `initial_count_fp` | string | Initial count \(fixed-point\) |
| ↳ `taker_fees` | number | Taker fees in cents |
| ↳ `maker_fees` | number | Maker fees in cents |
| ↳ `taker_fees_dollars` | string | Taker fees in dollars |
| ↳ `maker_fees_dollars` | string | Maker fees in dollars |
| ↳ `taker_fill_cost` | number | Taker fill cost in cents |
| ↳ `maker_fill_cost` | number | Maker fill cost in cents |
| ↳ `taker_fill_cost_dollars` | string | Taker fill cost in dollars |
| ↳ `maker_fill_cost_dollars` | string | Maker fill cost in dollars |
| ↳ `queue_position` | number | Queue position \(deprecated\) |
| ↳ `expiration_time` | string | Order expiration time |
| ↳ `created_time` | string | Order creation time |
| ↳ `last_update_time` | string | Last update time |
| ↳ `self_trade_prevention_type` | string | Self-trade prevention type |
| ↳ `order_group_id` | string | Order group ID |
| ↳ `cancel_order_on_pause` | boolean | Cancel on market pause |
| `order` | object | Order object with details |
### `kalshi_get_orderbook`
Retrieve the orderbook (yes and no bids) for a specific market (V2 - includes depth and fp fields)
Retrieve the orderbook (yes and no bids) for a specific market
#### Input
@@ -304,18 +205,11 @@ Retrieve the orderbook (yes and no bids) for a specific market (V2 - includes de
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `orderbook` | object | Orderbook with yes/no bids \(legacy integer counts\) |
| ↳ `yes` | array | Yes side bids as tuples \[price_cents, count\] |
| ↳ `no` | array | No side bids as tuples \[price_cents, count\] |
| ↳ `yes_dollars` | array | Yes side bids as tuples \[dollars_string, count\] |
| ↳ `no_dollars` | array | No side bids as tuples \[dollars_string, count\] |
| `orderbook_fp` | object | Orderbook with fixed-point counts \(preferred\) |
| ↳ `yes_dollars` | array | Yes side bids as tuples \[dollars_string, fp_count_string\] |
| ↳ `no_dollars` | array | No side bids as tuples \[dollars_string, fp_count_string\] |
| `orderbook` | object | Orderbook with yes/no bids and asks |
### `kalshi_get_trades`
Retrieve recent trades with additional filtering options (V2 - includes trade_id and count_fp)
Retrieve recent trades across all markets
#### Input
@@ -328,12 +222,12 @@ Retrieve recent trades with additional filtering options (V2 - includes trade_id
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `trades` | array | Array of trade objects with trade_id and count_fp |
| `cursor` | string | Pagination cursor for fetching more results |
| `trades` | array | Array of trade objects |
| `paging` | object | Pagination cursor for fetching more results |
### `kalshi_get_candlesticks`
Retrieve OHLC candlestick data for a specific market (V2 - full API response)
Retrieve OHLC candlestick data for a specific market
#### Input
@@ -349,8 +243,7 @@ Retrieve OHLC candlestick data for a specific market (V2 - full API response)
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `ticker` | string | Market ticker |
| `candlesticks` | array | Array of OHLC candlestick data with nested bid/ask/price objects |
| `candlesticks` | array | Array of OHLC candlestick data |
### `kalshi_get_fills`
@@ -373,12 +266,12 @@ Retrieve your portfolio
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `fills` | array | Array of fill/trade objects with all API fields |
| `cursor` | string | Pagination cursor for fetching more results |
| `fills` | array | Array of fill/trade objects |
| `paging` | object | Pagination cursor for fetching more results |
### `kalshi_get_series_by_ticker`
Retrieve details of a specific market series by ticker (V2 - exact API response)
Retrieve details of a specific market series by ticker
#### Input
@@ -390,25 +283,11 @@ Retrieve details of a specific market series by ticker (V2 - exact API response)
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `series` | object | Series object with full details matching Kalshi API response |
| ↳ `ticker` | string | Series ticker |
| ↳ `title` | string | Series title |
| ↳ `frequency` | string | Event frequency |
| ↳ `category` | string | Series category |
| ↳ `tags` | array | Series tags |
| ↳ `settlement_sources` | array | Settlement sources |
| ↳ `contract_url` | string | Contract URL |
| ↳ `contract_terms_url` | string | Contract terms URL |
| ↳ `fee_type` | string | Fee type |
| ↳ `fee_multiplier` | number | Fee multiplier |
| ↳ `additional_prohibitions` | array | Additional prohibitions |
| ↳ `product_metadata` | object | Product metadata |
| ↳ `volume` | number | Series volume |
| ↳ `volume_fp` | number | Volume \(fixed-point\) |
| `series` | object | Series object with details |
### `kalshi_get_exchange_status`
Retrieve the current status of the Kalshi exchange (V2 - exact API response)
Retrieve the current status of the Kalshi exchange (trading and exchange activity)
#### Input
@@ -419,13 +298,11 @@ Retrieve the current status of the Kalshi exchange (V2 - exact API response)
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `exchange_active` | boolean | Whether the exchange is active |
| `trading_active` | boolean | Whether trading is active |
| `exchange_estimated_resume_time` | string | Estimated time when exchange will resume \(if inactive\) |
| `status` | object | Exchange status with trading_active and exchange_active flags |
### `kalshi_create_order`
Create a new order on a Kalshi prediction market (V2 with full API response)
Create a new order on a Kalshi prediction market
#### Input
@@ -455,44 +332,11 @@ Create a new order on a Kalshi prediction market (V2 with full API response)
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `order` | object | The created order object with full API response fields |
| ↳ `order_id` | string | Order ID |
| ↳ `user_id` | string | User ID |
| ↳ `client_order_id` | string | Client order ID |
| ↳ `ticker` | string | Market ticker |
| ↳ `side` | string | Order side \(yes/no\) |
| ↳ `action` | string | Action \(buy/sell\) |
| ↳ `type` | string | Order type \(limit/market\) |
| ↳ `status` | string | Order status \(resting/canceled/executed\) |
| ↳ `yes_price` | number | Yes price in cents |
| ↳ `no_price` | number | No price in cents |
| ↳ `yes_price_dollars` | string | Yes price in dollars |
| ↳ `no_price_dollars` | string | No price in dollars |
| ↳ `fill_count` | number | Filled contract count |
| ↳ `fill_count_fp` | string | Filled count \(fixed-point\) |
| ↳ `remaining_count` | number | Remaining contracts |
| ↳ `remaining_count_fp` | string | Remaining count \(fixed-point\) |
| ↳ `initial_count` | number | Initial contract count |
| ↳ `initial_count_fp` | string | Initial count \(fixed-point\) |
| ↳ `taker_fees` | number | Taker fees in cents |
| ↳ `maker_fees` | number | Maker fees in cents |
| ↳ `taker_fees_dollars` | string | Taker fees in dollars |
| ↳ `maker_fees_dollars` | string | Maker fees in dollars |
| ↳ `taker_fill_cost` | number | Taker fill cost in cents |
| ↳ `maker_fill_cost` | number | Maker fill cost in cents |
| ↳ `taker_fill_cost_dollars` | string | Taker fill cost in dollars |
| ↳ `maker_fill_cost_dollars` | string | Maker fill cost in dollars |
| ↳ `queue_position` | number | Queue position \(deprecated\) |
| ↳ `expiration_time` | string | Order expiration time |
| ↳ `created_time` | string | Order creation time |
| ↳ `last_update_time` | string | Last update time |
| ↳ `self_trade_prevention_type` | string | Self-trade prevention type |
| ↳ `order_group_id` | string | Order group ID |
| ↳ `cancel_order_on_pause` | boolean | Cancel on market pause |
| `order` | object | The created order object |
### `kalshi_cancel_order`
Cancel an existing order on Kalshi (V2 with full API response)
Cancel an existing order on Kalshi
#### Input
@@ -506,46 +350,12 @@ Cancel an existing order on Kalshi (V2 with full API response)
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `order` | object | The canceled order object with full API response fields |
| ↳ `order_id` | string | Order ID |
| ↳ `user_id` | string | User ID |
| ↳ `client_order_id` | string | Client order ID |
| ↳ `ticker` | string | Market ticker |
| ↳ `side` | string | Order side \(yes/no\) |
| ↳ `action` | string | Action \(buy/sell\) |
| ↳ `type` | string | Order type \(limit/market\) |
| ↳ `status` | string | Order status \(resting/canceled/executed\) |
| ↳ `yes_price` | number | Yes price in cents |
| ↳ `no_price` | number | No price in cents |
| ↳ `yes_price_dollars` | string | Yes price in dollars |
| ↳ `no_price_dollars` | string | No price in dollars |
| ↳ `fill_count` | number | Filled contract count |
| ↳ `fill_count_fp` | string | Filled count \(fixed-point\) |
| ↳ `remaining_count` | number | Remaining contracts |
| ↳ `remaining_count_fp` | string | Remaining count \(fixed-point\) |
| ↳ `initial_count` | number | Initial contract count |
| ↳ `initial_count_fp` | string | Initial count \(fixed-point\) |
| ↳ `taker_fees` | number | Taker fees in cents |
| ↳ `maker_fees` | number | Maker fees in cents |
| ↳ `taker_fees_dollars` | string | Taker fees in dollars |
| ↳ `maker_fees_dollars` | string | Maker fees in dollars |
| ↳ `taker_fill_cost` | number | Taker fill cost in cents |
| ↳ `maker_fill_cost` | number | Maker fill cost in cents |
| ↳ `taker_fill_cost_dollars` | string | Taker fill cost in dollars |
| ↳ `maker_fill_cost_dollars` | string | Maker fill cost in dollars |
| ↳ `queue_position` | number | Queue position \(deprecated\) |
| ↳ `expiration_time` | string | Order expiration time |
| ↳ `created_time` | string | Order creation time |
| ↳ `last_update_time` | string | Last update time |
| ↳ `self_trade_prevention_type` | string | Self-trade prevention type |
| ↳ `order_group_id` | string | Order group ID |
| ↳ `cancel_order_on_pause` | boolean | Cancel on market pause |
| `reduced_by` | number | Number of contracts canceled |
| `reduced_by_fp` | string | Number of contracts canceled in fixed-point format |
| `order` | object | The canceled order object |
| `reducedBy` | number | Number of contracts canceled |
### `kalshi_amend_order`
Modify the price or quantity of an existing order on Kalshi (V2 with full API response)
Modify the price or quantity of an existing order on Kalshi
#### Input
@@ -569,63 +379,6 @@ Modify the price or quantity of an existing order on Kalshi (V2 with full API re
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `old_order` | object | The original order object before amendment |
| ↳ `order_id` | string | Order ID |
| ↳ `user_id` | string | User ID |
| ↳ `ticker` | string | Market ticker |
| ↳ `event_ticker` | string | Event ticker |
| ↳ `status` | string | Order status |
| ↳ `side` | string | Order side \(yes/no\) |
| ↳ `type` | string | Order type \(limit/market\) |
| ↳ `yes_price` | number | Yes price in cents |
| ↳ `no_price` | number | No price in cents |
| ↳ `action` | string | Action \(buy/sell\) |
| ↳ `count` | number | Number of contracts |
| ↳ `remaining_count` | number | Remaining contracts |
| ↳ `created_time` | string | Order creation time |
| ↳ `expiration_time` | string | Order expiration time |
| ↳ `order_group_id` | string | Order group ID |
| ↳ `client_order_id` | string | Client order ID |
| ↳ `place_count` | number | Place count |
| ↳ `decrease_count` | number | Decrease count |
| ↳ `queue_position` | number | Queue position |
| ↳ `maker_fill_count` | number | Maker fill count |
| ↳ `taker_fill_count` | number | Taker fill count |
| ↳ `maker_fees` | number | Maker fees |
| ↳ `taker_fees` | number | Taker fees |
| ↳ `last_update_time` | string | Last update time |
| ↳ `take_profit_order_id` | string | Take profit order ID |
| ↳ `stop_loss_order_id` | string | Stop loss order ID |
| ↳ `amend_count` | number | Amend count |
| ↳ `amend_taker_fill_count` | number | Amend taker fill count |
| `order` | object | The amended order object with full API response fields |
| ↳ `order_id` | string | Order ID |
| ↳ `user_id` | string | User ID |
| ↳ `ticker` | string | Market ticker |
| ↳ `event_ticker` | string | Event ticker |
| ↳ `status` | string | Order status |
| ↳ `side` | string | Order side \(yes/no\) |
| ↳ `type` | string | Order type \(limit/market\) |
| ↳ `yes_price` | number | Yes price in cents |
| ↳ `no_price` | number | No price in cents |
| ↳ `action` | string | Action \(buy/sell\) |
| ↳ `count` | number | Number of contracts |
| ↳ `remaining_count` | number | Remaining contracts |
| ↳ `created_time` | string | Order creation time |
| ↳ `expiration_time` | string | Order expiration time |
| ↳ `order_group_id` | string | Order group ID |
| ↳ `client_order_id` | string | Client order ID |
| ↳ `place_count` | number | Place count |
| ↳ `decrease_count` | number | Decrease count |
| ↳ `queue_position` | number | Queue position |
| ↳ `maker_fill_count` | number | Maker fill count |
| ↳ `taker_fill_count` | number | Taker fill count |
| ↳ `maker_fees` | number | Maker fees |
| ↳ `taker_fees` | number | Taker fees |
| ↳ `last_update_time` | string | Last update time |
| ↳ `take_profit_order_id` | string | Take profit order ID |
| ↳ `stop_loss_order_id` | string | Stop loss order ID |
| ↳ `amend_count` | number | Amend count |
| ↳ `amend_taker_fill_count` | number | Amend taker fill count |
| `order` | object | The amended order object |

View File

@@ -97,6 +97,7 @@
"shopify",
"slack",
"smtp",
"spotify",
"sqs",
"ssh",
"stagehand",

View File

@@ -29,7 +29,7 @@ By using these documented API endpoints, you can seamlessly integrate Polymarket
## Usage Instructions
Integrate Polymarket prediction markets into the workflow. Can get markets, market, events, event, tags, series, orderbook, price, midpoint, price history, last trade price, spread, tick size, positions, trades, activity, leaderboard, holders, and search.
Integrate Polymarket prediction markets into the workflow. Can get markets, market, events, event, tags, series, orderbook, price, midpoint, price history, last trade price, spread, tick size, positions, trades, and search.
@@ -43,7 +43,7 @@ Retrieve a list of prediction markets from Polymarket with optional filtering
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `closed` | string | No | Filter by closed status \(true/false\). Use false for open markets only. |
| `closed` | string | No | Filter by closed status \(true/false\). Use false for active markets only. |
| `order` | string | No | Sort field \(e.g., volumeNum, liquidityNum, startDate, endDate, createdAt\) |
| `ascending` | string | No | Sort direction \(true for ascending, false for descending\) |
| `tagId` | string | No | Filter by tag ID |
@@ -55,21 +55,6 @@ Retrieve a list of prediction markets from Polymarket with optional filtering
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `markets` | array | Array of market objects |
| ↳ `id` | string | Market ID |
| ↳ `question` | string | Market question |
| ↳ `conditionId` | string | Condition ID |
| ↳ `slug` | string | Market slug |
| ↳ `endDate` | string | End date |
| ↳ `image` | string | Market image URL |
| ↳ `outcomes` | string | Outcomes JSON string |
| ↳ `outcomePrices` | string | Outcome prices JSON string |
| ↳ `volume` | string | Total volume |
| ↳ `liquidity` | string | Total liquidity |
| ↳ `active` | boolean | Whether market is active |
| ↳ `closed` | boolean | Whether market is closed |
| ↳ `volumeNum` | number | Volume as number |
| ↳ `liquidityNum` | number | Liquidity as number |
| ↳ `clobTokenIds` | array | CLOB token IDs |
### `polymarket_get_market`
@@ -87,28 +72,6 @@ Retrieve details of a specific prediction market by ID or slug
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `market` | object | Market object with details |
| ↳ `id` | string | Market ID |
| ↳ `question` | string | Market question |
| ↳ `conditionId` | string | Condition ID |
| ↳ `slug` | string | Market slug |
| ↳ `resolutionSource` | string | Resolution source |
| ↳ `endDate` | string | End date |
| ↳ `startDate` | string | Start date |
| ↳ `image` | string | Market image URL |
| ↳ `icon` | string | Market icon URL |
| ↳ `description` | string | Market description |
| ↳ `outcomes` | string | Outcomes JSON string |
| ↳ `outcomePrices` | string | Outcome prices JSON string |
| ↳ `volume` | string | Total volume |
| ↳ `liquidity` | string | Total liquidity |
| ↳ `active` | boolean | Whether market is active |
| ↳ `closed` | boolean | Whether market is closed |
| ↳ `archived` | boolean | Whether market is archived |
| ↳ `volumeNum` | number | Volume as number |
| ↳ `liquidityNum` | number | Liquidity as number |
| ↳ `clobTokenIds` | array | CLOB token IDs |
| ↳ `acceptingOrders` | boolean | Whether accepting orders |
| ↳ `negRisk` | boolean | Whether negative risk |
### `polymarket_get_events`
@@ -118,7 +81,7 @@ Retrieve a list of events from Polymarket with optional filtering
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `closed` | string | No | Filter by closed status \(true/false\). Use false for open events only. |
| `closed` | string | No | Filter by closed status \(true/false\). Use false for active events only. |
| `order` | string | No | Sort field \(e.g., volume, liquidity, startDate, endDate, createdAt\) |
| `ascending` | string | No | Sort direction \(true for ascending, false for descending\) |
| `tagId` | string | No | Filter by tag ID |
@@ -130,21 +93,6 @@ Retrieve a list of events from Polymarket with optional filtering
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `events` | array | Array of event objects |
| ↳ `id` | string | Event ID |
| ↳ `ticker` | string | Event ticker |
| ↳ `slug` | string | Event slug |
| ↳ `title` | string | Event title |
| ↳ `description` | string | Event description |
| ↳ `startDate` | string | Start date |
| ↳ `endDate` | string | End date |
| ↳ `image` | string | Event image URL |
| ↳ `icon` | string | Event icon URL |
| ↳ `active` | boolean | Whether event is active |
| ↳ `closed` | boolean | Whether event is closed |
| ↳ `archived` | boolean | Whether event is archived |
| ↳ `liquidity` | number | Total liquidity |
| ↳ `volume` | number | Total volume |
| ↳ `markets` | array | Array of markets in this event |
### `polymarket_get_event`
@@ -162,24 +110,6 @@ Retrieve details of a specific event by ID or slug
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `event` | object | Event object with details |
| ↳ `id` | string | Event ID |
| ↳ `ticker` | string | Event ticker |
| ↳ `slug` | string | Event slug |
| ↳ `title` | string | Event title |
| ↳ `description` | string | Event description |
| ↳ `startDate` | string | Start date |
| ↳ `creationDate` | string | Creation date |
| ↳ `endDate` | string | End date |
| ↳ `image` | string | Event image URL |
| ↳ `icon` | string | Event icon URL |
| ↳ `active` | boolean | Whether event is active |
| ↳ `closed` | boolean | Whether event is closed |
| ↳ `archived` | boolean | Whether event is archived |
| ↳ `liquidity` | number | Total liquidity |
| ↳ `volume` | number | Total volume |
| ↳ `openInterest` | number | Open interest |
| ↳ `commentCount` | number | Comment count |
| ↳ `markets` | array | Array of markets in this event |
### `polymarket_get_tags`
@@ -196,12 +126,7 @@ Retrieve available tags for filtering markets from Polymarket
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `tags` | array | Array of tag objects |
| ↳ `id` | string | Tag ID |
| ↳ `label` | string | Tag label |
| ↳ `slug` | string | Tag slug |
| ↳ `createdAt` | string | Creation timestamp |
| ↳ `updatedAt` | string | Last update timestamp |
| `tags` | array | Array of tag objects with id, label, and slug |
### `polymarket_search`
@@ -213,28 +138,13 @@ Search for markets, events, and profiles on Polymarket
| --------- | ---- | -------- | ----------- |
| `query` | string | Yes | Search query term |
| `limit` | string | No | Number of results per page \(max 50\) |
| `page` | string | No | Page number for pagination \(1-indexed\) |
| `cache` | string | No | Enable caching \(true/false\) |
| `eventsStatus` | string | No | Filter events by status |
| `limitPerType` | string | No | Limit results per type \(markets, events, profiles\) |
| `eventsTag` | string | No | Filter by event tags \(comma-separated\) |
| `sort` | string | No | Sort field |
| `ascending` | string | No | Sort direction \(true for ascending, false for descending\) |
| `searchTags` | string | No | Include tags in search results \(true/false\) |
| `searchProfiles` | string | No | Include profiles in search results \(true/false\) |
| `recurrence` | string | No | Filter by recurrence type |
| `excludeTagId` | string | No | Exclude events with these tag IDs \(comma-separated\) |
| `keepClosedMarkets` | string | No | Include closed markets in results \(0 or 1\) |
| `offset` | string | No | Pagination offset |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `results` | object | Search results containing markets, events, tags, and profiles arrays |
| ↳ `markets` | array | Array of matching market objects |
| ↳ `events` | array | Array of matching event objects |
| ↳ `tags` | array | Array of matching tag objects |
| ↳ `profiles` | array | Array of matching profile objects |
| `results` | object | Search results containing markets, events, and profiles arrays |
### `polymarket_get_series`
@@ -252,21 +162,6 @@ Retrieve series (related market groups) from Polymarket
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `series` | array | Array of series objects |
| ↳ `id` | string | Series ID |
| ↳ `ticker` | string | Series ticker |
| ↳ `slug` | string | Series slug |
| ↳ `title` | string | Series title |
| ↳ `seriesType` | string | Series type |
| ↳ `recurrence` | string | Recurrence pattern |
| ↳ `image` | string | Series image URL |
| ↳ `icon` | string | Series icon URL |
| ↳ `active` | boolean | Whether series is active |
| ↳ `closed` | boolean | Whether series is closed |
| ↳ `archived` | boolean | Whether series is archived |
| ↳ `featured` | boolean | Whether series is featured |
| ↳ `volume` | number | Total volume |
| ↳ `liquidity` | number | Total liquidity |
| ↳ `eventCount` | number | Number of events in series |
### `polymarket_get_series_by_id`
@@ -283,23 +178,6 @@ Retrieve a specific series (related market group) by ID from Polymarket
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `series` | object | Series object with details |
| ↳ `id` | string | Series ID |
| ↳ `ticker` | string | Series ticker |
| ↳ `slug` | string | Series slug |
| ↳ `title` | string | Series title |
| ↳ `seriesType` | string | Series type |
| ↳ `recurrence` | string | Recurrence pattern |
| ↳ `image` | string | Series image URL |
| ↳ `icon` | string | Series icon URL |
| ↳ `active` | boolean | Whether series is active |
| ↳ `closed` | boolean | Whether series is closed |
| ↳ `archived` | boolean | Whether series is archived |
| ↳ `featured` | boolean | Whether series is featured |
| ↳ `volume` | number | Total volume |
| ↳ `liquidity` | number | Total liquidity |
| ↳ `commentCount` | number | Comment count |
| ↳ `eventCount` | number | Number of events in series |
| ↳ `events` | array | Array of events in this series |
### `polymarket_get_orderbook`
@@ -316,21 +194,6 @@ Retrieve the order book summary for a specific token
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `orderbook` | object | Order book with bids and asks arrays |
| ↳ `market` | string | Market identifier |
| ↳ `asset_id` | string | Asset token ID |
| ↳ `hash` | string | Order book hash |
| ↳ `timestamp` | string | Timestamp |
| ↳ `bids` | array | Bid orders |
| ↳ `price` | string | Bid price |
| ↳ `size` | string | Bid size |
| ↳ `price` | string | Ask price |
| ↳ `size` | string | Ask size |
| ↳ `asks` | array | Ask orders |
| ↳ `price` | string | Ask price |
| ↳ `size` | string | Ask size |
| ↳ `min_order_size` | string | Minimum order size |
| ↳ `tick_size` | string | Tick size |
| ↳ `neg_risk` | boolean | Whether negative risk |
### `polymarket_get_price`
@@ -383,9 +246,7 @@ Retrieve historical price data for a specific market token
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `history` | array | Array of price history entries |
| ↳ `t` | number | Unix timestamp |
| ↳ `p` | number | Price at timestamp |
| `history` | array | Array of price history entries with timestamp \(t\) and price \(p\) |
### `polymarket_get_last_trade_price`
@@ -402,7 +263,6 @@ Retrieve the last trade price for a specific token
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `price` | string | Last trade price |
| `side` | string | Side of the last trade \(BUY or SELL\) |
### `polymarket_get_spread`
@@ -418,8 +278,7 @@ Retrieve the bid-ask spread for a specific token
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `spread` | object | Spread value between bid and ask |
| ↳ `spread` | string | The spread value |
| `spread` | object | Bid-ask spread with bid and ask prices |
### `polymarket_get_tick_size`
@@ -446,47 +305,13 @@ Retrieve user positions from Polymarket
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `user` | string | Yes | User wallet address |
| `market` | string | No | Condition IDs to filter positions \(comma-separated, mutually exclusive with eventId\) |
| `eventId` | string | No | Event ID to filter positions \(mutually exclusive with market\) |
| `sizeThreshold` | string | No | Minimum position size threshold \(default: 1\) |
| `redeemable` | string | No | Filter for redeemable positions only \(true/false\) |
| `mergeable` | string | No | Filter for mergeable positions only \(true/false\) |
| `sortBy` | string | No | Sort field \(TOKENS, CURRENT, INITIAL, CASHPNL, PERCENTPNL, TITLE, RESOLVING, PRICE, AVGPRICE\) |
| `sortDirection` | string | No | Sort direction \(ASC or DESC\) |
| `title` | string | No | Search filter by title |
| `limit` | string | No | Number of results per page |
| `offset` | string | No | Pagination offset |
| `market` | string | No | Optional market ID to filter positions |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `positions` | array | Array of position objects |
| ↳ `proxyWallet` | string | Proxy wallet address |
| ↳ `asset` | string | Asset token ID |
| ↳ `conditionId` | string | Condition ID |
| ↳ `size` | number | Position size |
| ↳ `avgPrice` | number | Average price |
| ↳ `initialValue` | number | Initial value |
| ↳ `currentValue` | number | Current value |
| ↳ `cashPnl` | number | Cash profit/loss |
| ↳ `percentPnl` | number | Percent profit/loss |
| ↳ `totalBought` | number | Total bought |
| ↳ `realizedPnl` | number | Realized profit/loss |
| ↳ `percentRealizedPnl` | number | Percent realized profit/loss |
| ↳ `curPrice` | number | Current price |
| ↳ `redeemable` | boolean | Whether position is redeemable |
| ↳ `mergeable` | boolean | Whether position is mergeable |
| ↳ `title` | string | Market title |
| ↳ `slug` | string | Market slug |
| ↳ `icon` | string | Market icon URL |
| ↳ `eventSlug` | string | Event slug |
| ↳ `outcome` | string | Outcome name |
| ↳ `outcomeIndex` | number | Outcome index |
| ↳ `oppositeOutcome` | string | Opposite outcome name |
| ↳ `oppositeAsset` | string | Opposite asset token ID |
| ↳ `endDate` | string | End date |
| ↳ `negativeRisk` | boolean | Whether negative risk |
### `polymarket_get_trades`
@@ -497,13 +322,8 @@ Retrieve trade history from Polymarket
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `user` | string | No | User wallet address to filter trades |
| `market` | string | No | Market/condition ID to filter trades \(mutually exclusive with eventId\) |
| `eventId` | string | No | Event ID to filter trades \(mutually exclusive with market\) |
| `side` | string | No | Trade direction filter \(BUY or SELL\) |
| `takerOnly` | string | No | Filter for taker trades only \(true/false, default: true\) |
| `filterType` | string | No | Filter type \(CASH or TOKENS\) - requires filterAmount |
| `filterAmount` | string | No | Filter amount threshold - requires filterType |
| `limit` | string | No | Number of results per page \(default: 100, max: 10000\) |
| `market` | string | No | Market ID to filter trades |
| `limit` | string | No | Number of results per page \(max 50\) |
| `offset` | string | No | Pagination offset \(skip this many results\) |
#### Output
@@ -511,141 +331,5 @@ Retrieve trade history from Polymarket
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `trades` | array | Array of trade objects |
| ↳ `proxyWallet` | string | Proxy wallet address |
| ↳ `side` | string | Trade side \(BUY or SELL\) |
| ↳ `asset` | string | Asset token ID |
| ↳ `conditionId` | string | Condition ID |
| ↳ `size` | number | Trade size |
| ↳ `price` | number | Trade price |
| ↳ `timestamp` | number | Unix timestamp |
| ↳ `title` | string | Market title |
| ↳ `slug` | string | Market slug |
| ↳ `icon` | string | Market icon URL |
| ↳ `eventSlug` | string | Event slug |
| ↳ `outcome` | string | Outcome name |
| ↳ `outcomeIndex` | number | Outcome index |
| ↳ `name` | string | Trader name |
| ↳ `pseudonym` | string | Trader pseudonym |
| ↳ `bio` | string | Trader bio |
| ↳ `profileImage` | string | Profile image URL |
| ↳ `profileImageOptimized` | string | Optimized profile image URL |
| ↳ `transactionHash` | string | Transaction hash |
### `polymarket_get_activity`
Retrieve on-chain activity for a user including trades, splits, merges, redemptions, rewards, and conversions
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `user` | string | Yes | User wallet address \(0x-prefixed\) |
| `limit` | string | No | Maximum results \(default: 100, max: 500\) |
| `offset` | string | No | Pagination offset \(default: 0, max: 10000\) |
| `market` | string | No | Comma-separated condition IDs \(mutually exclusive with eventId\) |
| `eventId` | string | No | Comma-separated event IDs \(mutually exclusive with market\) |
| `type` | string | No | Activity type filter: TRADE, SPLIT, MERGE, REDEEM, REWARD, CONVERSION, MAKER_REBATE |
| `start` | number | No | Start timestamp \(Unix seconds\) |
| `end` | number | No | End timestamp \(Unix seconds\) |
| `sortBy` | string | No | Sort field: TIMESTAMP, TOKENS, or CASH \(default: TIMESTAMP\) |
| `sortDirection` | string | No | Sort direction: ASC or DESC \(default: DESC\) |
| `side` | string | No | Trade side filter: BUY or SELL \(only applies to trades\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `activity` | array | Array of activity entries |
| ↳ `proxyWallet` | string | User proxy wallet address |
| ↳ `timestamp` | number | Unix timestamp of activity |
| ↳ `conditionId` | string | Market condition ID |
| ↳ `type` | string | Activity type \(TRADE, SPLIT, MERGE, REDEEM, REWARD, CONVERSION\) |
| ↳ `size` | number | Size in tokens |
| ↳ `usdcSize` | number | Size in USDC |
| ↳ `transactionHash` | string | Blockchain transaction hash |
| ↳ `price` | number | Price \(for trades\) |
| ↳ `asset` | string | Asset/token ID |
| ↳ `side` | string | Trade side \(BUY/SELL\) |
| ↳ `outcomeIndex` | number | Outcome index |
| ↳ `title` | string | Market title |
| ↳ `slug` | string | Market slug |
| ↳ `icon` | string | Market icon URL |
| ↳ `eventSlug` | string | Event slug |
| ↳ `outcome` | string | Outcome name |
| ↳ `name` | string | User display name |
| ↳ `pseudonym` | string | User pseudonym |
| ↳ `bio` | string | User bio |
| ↳ `profileImage` | string | User profile image URL |
| ↳ `profileImageOptimized` | string | Optimized profile image URL |
### `polymarket_get_leaderboard`
Retrieve trader leaderboard rankings by profit/loss or volume
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `category` | string | No | Category filter: OVERALL, POLITICS, SPORTS, CRYPTO, CULTURE, MENTIONS, WEATHER, ECONOMICS, TECH, FINANCE \(default: OVERALL\) |
| `timePeriod` | string | No | Time period: DAY, WEEK, MONTH, ALL \(default: DAY\) |
| `orderBy` | string | No | Order by: PNL or VOL \(default: PNL\) |
| `limit` | string | No | Number of results \(1-50, default: 25\) |
| `offset` | string | No | Pagination offset \(0-1000, default: 0\) |
| `user` | string | No | Filter by specific user wallet address |
| `userName` | string | No | Filter by username |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leaderboard` | array | Array of leaderboard entries |
| ↳ `rank` | string | Leaderboard rank position |
| ↳ `proxyWallet` | string | User proxy wallet address |
| ↳ `userName` | string | User display name |
| ↳ `vol` | number | Trading volume |
| ↳ `pnl` | number | Profit and loss |
| ↳ `profileImage` | string | User profile image URL |
| ↳ `xUsername` | string | Twitter/X username |
| ↳ `verifiedBadge` | boolean | Whether user has verified badge |
### `polymarket_get_holders`
Retrieve top holders of a specific market token
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `market` | string | Yes | Comma-separated list of condition IDs |
| `limit` | string | No | Number of holders to return \(0-20, default: 20\) |
| `minBalance` | string | No | Minimum balance threshold \(default: 1\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `holders` | array | Array of market holder groups by token |
| ↳ `token` | string | Token/asset ID |
| ↳ `holders` | array | Array of holders for this token |
| ↳ `proxyWallet` | string | Holder wallet address |
| ↳ `bio` | string | Holder bio |
| ↳ `asset` | string | Asset ID |
| ↳ `pseudonym` | string | Holder pseudonym |
| ↳ `amount` | number | Amount held |
| ↳ `displayUsernamePublic` | boolean | Whether username is publicly displayed |
| ↳ `outcomeIndex` | number | Outcome index |
| ↳ `name` | string | Holder display name |
| ↳ `profileImage` | string | Profile image URL |
| ↳ `profileImageOptimized` | string | Optimized profile image URL |
| ↳ `proxyWallet` | string | Holder wallet address |
| ↳ `bio` | string | Holder bio |
| ↳ `asset` | string | Asset ID |
| ↳ `pseudonym` | string | Holder pseudonym |
| ↳ `amount` | number | Amount held |
| ↳ `displayUsernamePublic` | boolean | Whether username is publicly displayed |
| ↳ `outcomeIndex` | number | Outcome index |
| ↳ `name` | string | Holder display name |
| ↳ `profileImage` | string | Profile image URL |
| ↳ `profileImageOptimized` | string | Optimized profile image URL |

File diff suppressed because it is too large Load Diff

View File

@@ -299,7 +299,7 @@ Upload a file to a Supabase storage bucket
| `bucket` | string | Yes | The name of the storage bucket |
| `fileName` | string | Yes | The name of the file \(e.g., "document.pdf", "image.jpg"\) |
| `path` | string | No | Optional folder path \(e.g., "folder/subfolder/"\) |
| `fileData` | json | Yes | File to upload - UserFile object \(basic mode\) or string content \(advanced mode: base64 or plain text\). Supports data URLs. |
| `fileContent` | string | Yes | The file content \(base64 encoded for binary files, or plain text\) |
| `contentType` | string | No | MIME type of the file \(e.g., "image/jpeg", "text/plain"\) |
| `upsert` | boolean | No | If true, overwrites existing file \(default: false\) |
| `apiKey` | string | Yes | Your Supabase service role secret key |
@@ -309,7 +309,7 @@ Upload a file to a Supabase storage bucket
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `message` | string | Operation status message |
| `results` | object | Upload result including file path, bucket, and public URL |
| `results` | object | Upload result including file path and metadata |
### `supabase_storage_download`

View File

@@ -58,6 +58,7 @@ export function ExecutionSnapshot({
onClose = () => {},
}: ExecutionSnapshotProps) {
const { data, isLoading, error } = useExecutionSnapshot(executionId)
const lastExecutionIdRef = useRef<string | null>(null)
const [isMenuOpen, setIsMenuOpen] = useState(false)
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 })
@@ -81,6 +82,12 @@ export function ExecutionSnapshot({
const workflowState = data?.workflowState as WorkflowState | undefined
// Track execution ID changes for key reset
const executionKey = executionId !== lastExecutionIdRef.current ? executionId : undefined
if (executionId !== lastExecutionIdRef.current) {
lastExecutionIdRef.current = executionId
}
const renderContent = () => {
if (isLoading) {
return (
@@ -145,7 +152,7 @@ export function ExecutionSnapshot({
return (
<Preview
key={executionId}
key={executionKey}
workflowState={workflowState}
traceSpans={traceSpans}
className={className}

View File

@@ -268,6 +268,7 @@ const OutputCodeContent = React.memo(function OutputCodeContent({
onMatchCountChange={onMatchCountChange}
contentRef={contentRef}
virtualized
showCollapseColumn={language === 'json'}
/>
)
})

View File

@@ -36,22 +36,7 @@ export const IntercomBlock: BlockConfig = {
{ label: 'Search Conversations', id: 'search_conversations' },
{ label: 'Create Ticket', id: 'create_ticket' },
{ label: 'Get Ticket', id: 'get_ticket' },
{ label: 'Update Ticket', id: 'update_ticket' },
{ label: 'Create Message', id: 'create_message' },
{ label: 'List Admins', id: 'list_admins' },
{ label: 'Close Conversation', id: 'close_conversation' },
{ label: 'Open Conversation', id: 'open_conversation' },
{ label: 'Snooze Conversation', id: 'snooze_conversation' },
{ label: 'Assign Conversation', id: 'assign_conversation' },
{ label: 'List Tags', id: 'list_tags' },
{ label: 'Create Tag', id: 'create_tag' },
{ label: 'Tag Contact', id: 'tag_contact' },
{ label: 'Untag Contact', id: 'untag_contact' },
{ label: 'Tag Conversation', id: 'tag_conversation' },
{ label: 'Create Note', id: 'create_note' },
{ label: 'Create Event', id: 'create_event' },
{ label: 'Attach Contact to Company', id: 'attach_contact_to_company' },
{ label: 'Detach Contact from Company', id: 'detach_contact_from_company' },
],
value: () => 'create_contact',
},
@@ -399,15 +384,7 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
required: true,
condition: {
field: 'operation',
value: [
'get_conversation',
'reply_conversation',
'close_conversation',
'open_conversation',
'snooze_conversation',
'assign_conversation',
'tag_conversation',
],
value: ['get_conversation', 'reply_conversation'],
},
},
{
@@ -500,20 +477,11 @@ Return ONLY the message text - no explanations.`,
id: 'admin_id',
title: 'Admin ID',
type: 'short-input',
placeholder: 'ID of the admin performing the action',
placeholder: 'ID of the admin sending the message',
required: true,
condition: {
field: 'operation',
value: [
'reply_conversation',
'close_conversation',
'open_conversation',
'snooze_conversation',
'assign_conversation',
'tag_conversation',
'create_note',
'update_ticket',
],
value: ['reply_conversation'],
},
},
{
@@ -558,7 +526,7 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
required: true,
condition: {
field: 'operation',
value: ['get_ticket', 'update_ticket'],
value: ['get_ticket'],
},
},
{
@@ -831,307 +799,6 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
value: ['list_companies'],
},
},
// Close/Open conversation body
{
id: 'close_body',
title: 'Closing Message',
type: 'long-input',
placeholder: 'Optional message to add when closing',
condition: {
field: 'operation',
value: ['close_conversation'],
},
},
// Snooze conversation
{
id: 'snoozed_until',
title: 'Snooze Until',
type: 'short-input',
placeholder: 'Unix timestamp when conversation should reopen',
required: true,
condition: {
field: 'operation',
value: ['snooze_conversation'],
},
wandConfig: {
enabled: true,
prompt: `Generate a Unix timestamp in seconds based on the user's description.
The timestamp should be a Unix epoch time in seconds (10 digits).
Examples:
- "tomorrow" -> Tomorrow at 09:00:00 as Unix timestamp
- "in 2 hours" -> Current time plus 7200 seconds
- "next Monday" -> Next Monday at 09:00:00 as Unix timestamp
Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
placeholder: 'Describe when to unsnooze (e.g., "tomorrow", "in 2 hours")...',
generationType: 'timestamp',
},
},
// Assign conversation
{
id: 'assignee_id',
title: 'Assignee ID',
type: 'short-input',
placeholder: 'Admin or team ID to assign to (0 to unassign)',
required: true,
condition: {
field: 'operation',
value: ['assign_conversation'],
},
},
{
id: 'assign_body',
title: 'Assignment Message',
type: 'long-input',
placeholder: 'Optional message when assigning',
condition: {
field: 'operation',
value: ['assign_conversation'],
},
},
// Update ticket fields
{
id: 'update_ticket_attributes',
title: 'Ticket Attributes',
type: 'long-input',
placeholder: 'JSON object with ticket attributes to update',
condition: {
field: 'operation',
value: ['update_ticket'],
},
wandConfig: {
enabled: true,
prompt: `Generate a JSON object for Intercom ticket attributes based on the user's description.
Example: {"_default_title_": "Updated title", "_default_description_": "Updated description"}
Return ONLY the JSON object - no explanations or markdown formatting.`,
placeholder: 'Describe the ticket updates (e.g., "change title to Bug Fixed")...',
generationType: 'json-object',
},
},
{
id: 'ticket_open',
title: 'Ticket Open',
type: 'dropdown',
options: [
{ label: 'Keep Open', id: 'true' },
{ label: 'Close Ticket', id: 'false' },
],
condition: {
field: 'operation',
value: ['update_ticket'],
},
},
{
id: 'ticket_is_shared',
title: 'Ticket Visible to Users',
type: 'dropdown',
options: [
{ label: 'Yes', id: 'true' },
{ label: 'No', id: 'false' },
],
condition: {
field: 'operation',
value: ['update_ticket'],
},
},
{
id: 'ticket_snoozed_until',
title: 'Snooze Ticket Until',
type: 'short-input',
placeholder: 'Unix timestamp when ticket should reopen',
condition: {
field: 'operation',
value: ['update_ticket'],
},
wandConfig: {
enabled: true,
prompt: `Generate a Unix timestamp in seconds based on the user's description.
Examples:
- "tomorrow" -> Tomorrow at 09:00:00 as Unix timestamp
- "next week" -> 7 days from now
Return ONLY the numeric timestamp.`,
placeholder: 'Describe when to unsnooze (e.g., "tomorrow")...',
generationType: 'timestamp',
},
},
{
id: 'ticket_assignee_id',
title: 'Ticket Assignee ID',
type: 'short-input',
placeholder: 'Admin or team ID to assign to (0 to unassign)',
condition: {
field: 'operation',
value: ['update_ticket'],
},
},
// Tag fields
{
id: 'tagId',
title: 'Tag ID',
type: 'short-input',
placeholder: 'ID of the tag',
required: true,
condition: {
field: 'operation',
value: ['tag_contact', 'untag_contact', 'tag_conversation'],
},
},
{
id: 'tag_name',
title: 'Tag Name',
type: 'short-input',
placeholder: 'Name of the tag to create',
required: true,
condition: {
field: 'operation',
value: ['create_tag'],
},
},
{
id: 'tag_id_update',
title: 'Tag ID (for update)',
type: 'short-input',
placeholder: 'ID of existing tag to update (leave empty to create new)',
condition: {
field: 'operation',
value: ['create_tag'],
},
},
// Contact ID for tag/untag/note operations
{
id: 'tag_contact_id',
title: 'Contact ID',
type: 'short-input',
placeholder: 'ID of the contact',
required: true,
condition: {
field: 'operation',
value: [
'tag_contact',
'untag_contact',
'create_note',
'attach_contact_to_company',
'detach_contact_from_company',
],
},
},
// Note fields
{
id: 'note_body',
title: 'Note Content',
type: 'long-input',
placeholder: 'Text content of the note',
required: true,
condition: {
field: 'operation',
value: ['create_note'],
},
wandConfig: {
enabled: true,
prompt: `Generate a note for Intercom based on the user's description.
The note should be clear, professional, and capture the key information.
Return ONLY the note text - no explanations.`,
placeholder: 'Describe the note content (e.g., "customer requested callback")...',
},
},
// Event fields
{
id: 'event_name',
title: 'Event Name',
type: 'short-input',
placeholder: 'Event name (e.g., order-completed)',
required: true,
condition: {
field: 'operation',
value: ['create_event'],
},
},
{
id: 'event_user_id',
title: 'User ID',
type: 'short-input',
placeholder: 'Your identifier for the user',
condition: {
field: 'operation',
value: ['create_event'],
},
},
{
id: 'event_email',
title: 'User Email',
type: 'short-input',
placeholder: 'Email address of the user',
condition: {
field: 'operation',
value: ['create_event'],
},
},
{
id: 'event_contact_id',
title: 'Contact ID',
type: 'short-input',
placeholder: 'Intercom contact ID',
condition: {
field: 'operation',
value: ['create_event'],
},
},
{
id: 'event_metadata',
title: 'Event Metadata',
type: 'long-input',
placeholder: 'JSON object with event metadata (max 10 keys)',
condition: {
field: 'operation',
value: ['create_event'],
},
wandConfig: {
enabled: true,
prompt: `Generate a JSON object for Intercom event metadata based on the user's description.
The object should contain key-value pairs (max 10 keys).
Example: {"order_value": 99.99, "items": 3, "coupon_used": true}
Return ONLY the JSON object - no explanations or markdown formatting.`,
placeholder: 'Describe the event data (e.g., "order value $50, 2 items")...',
generationType: 'json-object',
},
},
{
id: 'event_created_at',
title: 'Event Time',
type: 'short-input',
placeholder: 'Unix timestamp when event occurred',
condition: {
field: 'operation',
value: ['create_event'],
},
wandConfig: {
enabled: true,
prompt: `Generate a Unix timestamp in seconds based on the user's description.
Examples:
- "now" -> Current Unix timestamp
- "5 minutes ago" -> Current time minus 300 seconds
Return ONLY the numeric timestamp.`,
placeholder: 'Describe when the event occurred (e.g., "now")...',
generationType: 'timestamp',
},
},
// Company attachment fields
{
id: 'attach_company_id',
title: 'Company ID',
type: 'short-input',
placeholder: 'ID of the company to attach/detach',
required: true,
condition: {
field: 'operation',
value: ['attach_contact_to_company', 'detach_contact_from_company'],
},
},
],
tools: {
access: [
@@ -1151,21 +818,6 @@ Return ONLY the numeric timestamp.`,
'intercom_create_ticket',
'intercom_get_ticket',
'intercom_create_message',
'intercom_update_ticket_v2',
'intercom_list_admins_v2',
'intercom_close_conversation_v2',
'intercom_open_conversation_v2',
'intercom_snooze_conversation_v2',
'intercom_assign_conversation_v2',
'intercom_list_tags_v2',
'intercom_create_tag_v2',
'intercom_tag_contact_v2',
'intercom_untag_contact_v2',
'intercom_tag_conversation_v2',
'intercom_create_note_v2',
'intercom_create_event_v2',
'intercom_attach_contact_to_company_v2',
'intercom_detach_contact_from_company_v2',
],
config: {
tool: (params) => {
@@ -1202,36 +854,6 @@ Return ONLY the numeric timestamp.`,
return 'intercom_get_ticket'
case 'create_message':
return 'intercom_create_message'
case 'update_ticket':
return 'intercom_update_ticket_v2'
case 'list_admins':
return 'intercom_list_admins_v2'
case 'close_conversation':
return 'intercom_close_conversation_v2'
case 'open_conversation':
return 'intercom_open_conversation_v2'
case 'snooze_conversation':
return 'intercom_snooze_conversation_v2'
case 'assign_conversation':
return 'intercom_assign_conversation_v2'
case 'list_tags':
return 'intercom_list_tags_v2'
case 'create_tag':
return 'intercom_create_tag_v2'
case 'tag_contact':
return 'intercom_tag_contact_v2'
case 'untag_contact':
return 'intercom_untag_contact_v2'
case 'tag_conversation':
return 'intercom_tag_conversation_v2'
case 'create_note':
return 'intercom_create_note_v2'
case 'create_event':
return 'intercom_create_event_v2'
case 'attach_contact_to_company':
return 'intercom_attach_contact_to_company_v2'
case 'detach_contact_from_company':
return 'intercom_detach_contact_from_company_v2'
default:
throw new Error(`Unknown operation: ${params.operation}`)
}
@@ -1248,23 +870,6 @@ Return ONLY the numeric timestamp.`,
message_created_at,
include_translations,
disable_notifications,
close_body,
assign_body,
tag_contact_id,
attach_company_id,
update_ticket_attributes,
ticket_open,
ticket_is_shared,
ticket_snoozed_until,
ticket_assignee_id,
tag_name,
tag_id_update,
note_body,
event_user_id,
event_email,
event_contact_id,
event_metadata,
event_created_at,
...rest
} = params
const cleanParams: Record<string, any> = {}
@@ -1292,7 +897,7 @@ Return ONLY the numeric timestamp.`,
cleanParams.created_at = Number(reply_created_at)
}
// Map ticket fields for create_ticket
// Map ticket fields
if (operation === 'create_ticket') {
if (ticket_company_id) cleanParams.company_id = ticket_company_id
if (ticket_created_at) cleanParams.created_at = Number(ticket_created_at)
@@ -1315,71 +920,6 @@ Return ONLY the numeric timestamp.`,
cleanParams.include_translations = include_translations === 'true'
}
// Map close_body to body for close_conversation
if (operation === 'close_conversation' && close_body) {
cleanParams.body = close_body
}
// Map assign_body to body for assign_conversation
if (operation === 'assign_conversation' && assign_body) {
cleanParams.body = assign_body
}
// Map tag_contact_id to contactId for tag/note/company attachment operations
if (
[
'tag_contact',
'untag_contact',
'create_note',
'attach_contact_to_company',
'detach_contact_from_company',
].includes(operation) &&
tag_contact_id
) {
cleanParams.contactId = tag_contact_id
}
// Map attach_company_id to companyId for company attachment operations
if (
['attach_contact_to_company', 'detach_contact_from_company'].includes(operation) &&
attach_company_id
) {
cleanParams.companyId = attach_company_id
}
// Map update_ticket fields
if (operation === 'update_ticket') {
if (update_ticket_attributes) cleanParams.ticket_attributes = update_ticket_attributes
if (ticket_open !== undefined && ticket_open !== '') {
cleanParams.open = ticket_open === 'true'
}
if (ticket_is_shared !== undefined && ticket_is_shared !== '') {
cleanParams.is_shared = ticket_is_shared === 'true'
}
if (ticket_snoozed_until) cleanParams.snoozed_until = Number(ticket_snoozed_until)
if (ticket_assignee_id) cleanParams.assignee_id = ticket_assignee_id
}
// Map tag fields for create_tag
if (operation === 'create_tag') {
if (tag_name) cleanParams.name = tag_name
if (tag_id_update) cleanParams.id = tag_id_update
}
// Map note_body to body for create_note
if (operation === 'create_note' && note_body) {
cleanParams.body = note_body
}
// Map event fields for create_event
if (operation === 'create_event') {
if (event_user_id) cleanParams.user_id = event_user_id
if (event_email) cleanParams.email = event_email
if (event_contact_id) cleanParams.id = event_contact_id
if (event_metadata) cleanParams.metadata = event_metadata
if (event_created_at) cleanParams.created_at = Number(event_created_at)
}
Object.entries(rest).forEach(([key, value]) => {
if (value !== undefined && value !== null && value !== '') {
cleanParams[key] = value
@@ -1423,22 +963,7 @@ export const IntercomV2Block: BlockConfig = {
'intercom_search_conversations_v2',
'intercom_create_ticket_v2',
'intercom_get_ticket_v2',
'intercom_update_ticket_v2',
'intercom_create_message_v2',
'intercom_list_admins_v2',
'intercom_close_conversation_v2',
'intercom_open_conversation_v2',
'intercom_snooze_conversation_v2',
'intercom_assign_conversation_v2',
'intercom_list_tags_v2',
'intercom_create_tag_v2',
'intercom_tag_contact_v2',
'intercom_untag_contact_v2',
'intercom_tag_conversation_v2',
'intercom_create_note_v2',
'intercom_create_event_v2',
'intercom_attach_contact_to_company_v2',
'intercom_detach_contact_from_company_v2',
],
config: {
tool: createVersionedToolSelector({
@@ -1474,38 +999,8 @@ export const IntercomV2Block: BlockConfig = {
return 'intercom_create_ticket'
case 'get_ticket':
return 'intercom_get_ticket'
case 'update_ticket':
return 'intercom_update_ticket'
case 'create_message':
return 'intercom_create_message'
case 'list_admins':
return 'intercom_list_admins'
case 'close_conversation':
return 'intercom_close_conversation'
case 'open_conversation':
return 'intercom_open_conversation'
case 'snooze_conversation':
return 'intercom_snooze_conversation'
case 'assign_conversation':
return 'intercom_assign_conversation'
case 'list_tags':
return 'intercom_list_tags'
case 'create_tag':
return 'intercom_create_tag'
case 'tag_contact':
return 'intercom_tag_contact'
case 'untag_contact':
return 'intercom_untag_contact'
case 'tag_conversation':
return 'intercom_tag_conversation'
case 'create_note':
return 'intercom_create_note'
case 'create_event':
return 'intercom_create_event'
case 'attach_contact_to_company':
return 'intercom_attach_contact_to_company'
case 'detach_contact_from_company':
return 'intercom_detach_contact_from_company'
default:
return 'intercom_create_contact'
}
@@ -1513,158 +1008,7 @@ export const IntercomV2Block: BlockConfig = {
suffix: '_v2',
fallbackToolId: 'intercom_create_contact_v2',
}),
params: (params) => {
const {
operation,
message_type_msg,
company_name,
contact_company_id,
reply_created_at,
ticket_company_id,
ticket_created_at,
message_created_at,
include_translations,
disable_notifications,
close_body,
assign_body,
tag_contact_id,
attach_company_id,
update_ticket_attributes,
ticket_open,
ticket_is_shared,
ticket_snoozed_until,
ticket_assignee_id,
tag_name,
tag_id_update,
note_body,
event_user_id,
event_email,
event_contact_id,
event_metadata,
event_created_at,
...rest
} = params
const cleanParams: Record<string, any> = {}
// Special mapping for message_type in create_message
if (operation === 'create_message' && message_type_msg) {
cleanParams.message_type = message_type_msg
}
// Special mapping for company name
if (operation === 'create_company' && company_name) {
cleanParams.name = company_name
}
// Map contact_company_id to company_id for contact operations
if (
(operation === 'create_contact' || operation === 'update_contact') &&
contact_company_id
) {
cleanParams.company_id = contact_company_id
}
// Map reply_created_at to created_at for reply_conversation
if (operation === 'reply_conversation' && reply_created_at) {
cleanParams.created_at = Number(reply_created_at)
}
// Map ticket fields for create_ticket
if (operation === 'create_ticket') {
if (ticket_company_id) cleanParams.company_id = ticket_company_id
if (ticket_created_at) cleanParams.created_at = Number(ticket_created_at)
if (disable_notifications !== undefined && disable_notifications !== '') {
cleanParams.disable_notifications = disable_notifications === 'true'
}
}
// Map message_created_at to created_at for create_message
if (operation === 'create_message' && message_created_at) {
cleanParams.created_at = Number(message_created_at)
}
// Convert include_translations string to boolean for get_conversation
if (
operation === 'get_conversation' &&
include_translations !== undefined &&
include_translations !== ''
) {
cleanParams.include_translations = include_translations === 'true'
}
// Map close_body to body for close_conversation
if (operation === 'close_conversation' && close_body) {
cleanParams.body = close_body
}
// Map assign_body to body for assign_conversation
if (operation === 'assign_conversation' && assign_body) {
cleanParams.body = assign_body
}
// Map tag_contact_id to contactId for tag/note/company attachment operations
if (
[
'tag_contact',
'untag_contact',
'create_note',
'attach_contact_to_company',
'detach_contact_from_company',
].includes(operation) &&
tag_contact_id
) {
cleanParams.contactId = tag_contact_id
}
// Map attach_company_id to companyId for company attachment operations
if (
['attach_contact_to_company', 'detach_contact_from_company'].includes(operation) &&
attach_company_id
) {
cleanParams.companyId = attach_company_id
}
// Map update_ticket fields
if (operation === 'update_ticket') {
if (update_ticket_attributes) cleanParams.ticket_attributes = update_ticket_attributes
if (ticket_open !== undefined && ticket_open !== '') {
cleanParams.open = ticket_open === 'true'
}
if (ticket_is_shared !== undefined && ticket_is_shared !== '') {
cleanParams.is_shared = ticket_is_shared === 'true'
}
if (ticket_snoozed_until) cleanParams.snoozed_until = Number(ticket_snoozed_until)
if (ticket_assignee_id) cleanParams.assignee_id = ticket_assignee_id
}
// Map tag fields for create_tag
if (operation === 'create_tag') {
if (tag_name) cleanParams.name = tag_name
if (tag_id_update) cleanParams.id = tag_id_update
}
// Map note_body to body for create_note
if (operation === 'create_note' && note_body) {
cleanParams.body = note_body
}
// Map event fields for create_event
if (operation === 'create_event') {
if (event_user_id) cleanParams.user_id = event_user_id
if (event_email) cleanParams.email = event_email
if (event_contact_id) cleanParams.id = event_contact_id
if (event_metadata) cleanParams.metadata = event_metadata
if (event_created_at) cleanParams.created_at = Number(event_created_at)
}
Object.entries(rest).forEach(([key, value]) => {
if (value !== undefined && value !== null && value !== '') {
cleanParams[key] = value
}
})
return cleanParams
},
params: IntercomBlock.tools!.config!.params,
},
},
outputs: {
@@ -1687,23 +1031,10 @@ export const IntercomV2Block: BlockConfig = {
type: 'array',
description: 'Array of conversations (for list/search operations)',
},
state: { type: 'string', description: 'Conversation state (for close/open/snooze operations)' },
ticket: { type: 'json', description: 'Ticket object with id, ticket_id, ticket_state' },
ticketId: { type: 'string', description: 'ID of the ticket (for create/update operations)' },
ticket_state: { type: 'string', description: 'Ticket state (for update_ticket operation)' },
ticketId: { type: 'string', description: 'ID of the ticket (for create operations)' },
message: { type: 'json', description: 'Message object with id, type' },
messageId: { type: 'string', description: 'ID of the message (for create operations)' },
admins: { type: 'array', description: 'Array of admin objects (for list_admins operation)' },
tags: { type: 'array', description: 'Array of tag objects (for list_tags operation)' },
tag: { type: 'json', description: 'Tag object with id and name (for tag operations)' },
tagId: { type: 'string', description: 'ID of the tag (for create_tag operation)' },
note: { type: 'json', description: 'Note object with id and body (for create_note operation)' },
noteId: { type: 'string', description: 'ID of the note (for create_note operation)' },
event_name: {
type: 'string',
description: 'Name of the tracked event (for create_event operation)',
},
name: { type: 'string', description: 'Name of the resource (for various operations)' },
total_count: { type: 'number', description: 'Total count (for list/search operations)' },
pages: { type: 'json', description: 'Pagination info with page, per_page, total_pages' },
id: { type: 'string', description: 'ID of the deleted item (for delete operations)' },

View File

@@ -1,18 +1,16 @@
import { KalshiIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import { createVersionedToolSelector } from '@/blocks/utils'
export const KalshiBlock: BlockConfig = {
type: 'kalshi',
name: 'Kalshi (Legacy)',
name: 'Kalshi',
description: 'Access prediction markets and trade on Kalshi',
longDescription:
'Integrate Kalshi prediction markets into the workflow. Can get markets, market, events, event, balance, positions, orders, orderbook, trades, candlesticks, fills, series, exchange status, and place/cancel/amend trades.',
docsLink: 'https://docs.sim.ai/tools/kalshi',
authMode: AuthMode.ApiKey,
category: 'tools',
hideFromToolbar: true,
bgColor: '#09C285',
icon: KalshiIcon,
subBlocks: [
@@ -351,14 +349,8 @@ Return ONLY the numeric timestamp (seconds since Unix epoch) - no explanations,
id: 'count',
title: 'Contracts',
type: 'short-input',
placeholder: 'Number of contracts (or use countFp)',
condition: { field: 'operation', value: ['create_order'] },
},
{
id: 'countFp',
title: 'Contracts (Fixed-Point)',
type: 'short-input',
placeholder: 'Fixed-point count (e.g., "10.50")',
placeholder: 'Number of contracts',
required: true,
condition: { field: 'operation', value: ['create_order'] },
},
{
@@ -682,143 +674,3 @@ Return ONLY the numeric timestamp (seconds since Unix epoch) - no explanations,
paging: { type: 'json', description: 'Pagination cursor for fetching more results' },
},
}
export const KalshiV2Block: BlockConfig = {
...KalshiBlock,
type: 'kalshi_v2',
name: 'Kalshi',
description: 'Access prediction markets and trade on Kalshi',
longDescription:
'Integrate Kalshi prediction markets into the workflow. Can get markets, market, events, event, balance, positions, orders, orderbook, trades, candlesticks, fills, series, exchange status, and place/cancel/amend trades.',
hideFromToolbar: false,
tools: {
...KalshiBlock.tools,
access: [
'kalshi_get_markets_v2',
'kalshi_get_market_v2',
'kalshi_get_events_v2',
'kalshi_get_event_v2',
'kalshi_get_balance_v2',
'kalshi_get_positions_v2',
'kalshi_get_orders_v2',
'kalshi_get_order_v2',
'kalshi_get_orderbook_v2',
'kalshi_get_trades_v2',
'kalshi_get_candlesticks_v2',
'kalshi_get_fills_v2',
'kalshi_get_series_by_ticker_v2',
'kalshi_get_exchange_status_v2',
'kalshi_create_order_v2',
'kalshi_cancel_order_v2',
'kalshi_amend_order_v2',
],
config: {
...KalshiBlock.tools!.config,
tool: createVersionedToolSelector({
baseToolSelector: (params) => {
switch (params.operation) {
case 'get_markets':
return 'kalshi_get_markets'
case 'get_market':
return 'kalshi_get_market'
case 'get_events':
return 'kalshi_get_events'
case 'get_event':
return 'kalshi_get_event'
case 'get_balance':
return 'kalshi_get_balance'
case 'get_positions':
return 'kalshi_get_positions'
case 'get_orders':
return 'kalshi_get_orders'
case 'get_order':
return 'kalshi_get_order'
case 'get_orderbook':
return 'kalshi_get_orderbook'
case 'get_trades':
return 'kalshi_get_trades'
case 'get_candlesticks':
return 'kalshi_get_candlesticks'
case 'get_fills':
return 'kalshi_get_fills'
case 'get_series_by_ticker':
return 'kalshi_get_series_by_ticker'
case 'get_exchange_status':
return 'kalshi_get_exchange_status'
case 'create_order':
return 'kalshi_create_order'
case 'cancel_order':
return 'kalshi_cancel_order'
case 'amend_order':
return 'kalshi_amend_order'
default:
return 'kalshi_get_markets'
}
},
suffix: '_v2',
fallbackToolId: 'kalshi_get_markets_v2',
}),
},
},
outputs: {
// List operations (V2 uses snake_case and flat cursor)
markets: { type: 'json', description: 'Array of market objects (get_markets)' },
events: { type: 'json', description: 'Array of event objects (get_events)' },
orders: { type: 'json', description: 'Array of order objects (get_orders)' },
market_positions: {
type: 'json',
description: 'Array of market position objects (get_positions)',
},
event_positions: {
type: 'json',
description: 'Array of event position objects (get_positions)',
},
fills: { type: 'json', description: 'Array of fill objects (get_fills)' },
trades: { type: 'json', description: 'Array of trade objects (get_trades)' },
candlesticks: {
type: 'json',
description: 'Array of candlestick data with yes_bid/yes_ask/price nested objects',
},
milestones: {
type: 'json',
description: 'Array of milestone objects (get_events with milestones)',
},
// Single item operations
market: { type: 'json', description: 'Single market object (get_market)' },
event: { type: 'json', description: 'Single event object (get_event)' },
order: {
type: 'json',
description: 'Order object with _dollars and _fp fields (get_order, create_order, etc.)',
},
series: { type: 'json', description: 'Series object (get_series_by_ticker)' },
// Account operations
balance: { type: 'number', description: 'Account balance in cents (get_balance)' },
portfolio_value: { type: 'number', description: 'Portfolio value in cents (get_balance)' },
updated_ts: { type: 'number', description: 'Unix timestamp of last update (get_balance)' },
// Orderbook (V2 uses tuple arrays)
orderbook: {
type: 'json',
description: 'Orderbook with yes/no/yes_dollars/no_dollars tuple arrays',
},
orderbook_fp: {
type: 'json',
description: 'Fixed-point orderbook with yes_dollars/no_dollars tuple arrays',
},
// Exchange status
exchange_status: {
type: 'string',
description: 'Exchange status string (get_exchange_status)',
},
trading_active: { type: 'boolean', description: 'Trading active flag (get_exchange_status)' },
// Cancel order specific
reduced_by: { type: 'number', description: 'Number of contracts reduced (cancel_order)' },
reduced_by_fp: {
type: 'string',
description: 'Contracts reduced in fixed-point (cancel_order)',
},
// Candlesticks ticker
ticker: { type: 'string', description: 'Market ticker (get_candlesticks)' },
// Pagination (flat cursor instead of nested paging object)
cursor: { type: 'string', description: 'Pagination cursor for fetching more results' },
},
}

View File

@@ -6,7 +6,7 @@ export const PolymarketBlock: BlockConfig = {
name: 'Polymarket',
description: 'Access prediction markets data from Polymarket',
longDescription:
'Integrate Polymarket prediction markets into the workflow. Can get markets, market, events, event, tags, series, orderbook, price, midpoint, price history, last trade price, spread, tick size, positions, trades, activity, leaderboard, holders, and search.',
'Integrate Polymarket prediction markets into the workflow. Can get markets, market, events, event, tags, series, orderbook, price, midpoint, price history, last trade price, spread, tick size, positions, trades, and search.',
docsLink: 'https://docs.sim.ai/tools/polymarket',
category: 'tools',
bgColor: '#4C82FB',
@@ -34,9 +34,6 @@ export const PolymarketBlock: BlockConfig = {
{ label: 'Get Tick Size', id: 'get_tick_size' },
{ label: 'Get Positions', id: 'get_positions' },
{ label: 'Get Trades', id: 'get_trades' },
{ label: 'Get Activity', id: 'get_activity' },
{ label: 'Get Leaderboard', id: 'get_leaderboard' },
{ label: 'Get Market Holders', id: 'get_holders' },
],
value: () => 'get_markets',
},
@@ -104,281 +101,14 @@ export const PolymarketBlock: BlockConfig = {
placeholder: 'Wallet address (optional filter)',
condition: { field: 'operation', value: ['get_trades'] },
},
// Market/Event filter for positions and trades
// Market filter for positions and trades
{
id: 'market',
title: 'Condition ID',
title: 'Market ID',
type: 'short-input',
placeholder: 'Condition ID filter (comma-separated)',
placeholder: 'Market ID (optional filter)',
condition: { field: 'operation', value: ['get_positions', 'get_trades'] },
},
{
id: 'positionEventId',
title: 'Event ID',
type: 'short-input',
placeholder: 'Event ID filter (alternative to Condition ID)',
condition: { field: 'operation', value: ['get_positions', 'get_trades'] },
},
// Positions-specific filters
{
id: 'sizeThreshold',
title: 'Size Threshold',
type: 'short-input',
placeholder: 'Minimum position size (default: 1)',
condition: { field: 'operation', value: ['get_positions'] },
},
{
id: 'redeemable',
title: 'Redeemable',
type: 'dropdown',
options: [
{ label: 'All', id: '' },
{ label: 'Redeemable Only', id: 'true' },
{ label: 'Non-Redeemable Only', id: 'false' },
],
condition: { field: 'operation', value: ['get_positions'] },
},
{
id: 'mergeable',
title: 'Mergeable',
type: 'dropdown',
options: [
{ label: 'All', id: '' },
{ label: 'Mergeable Only', id: 'true' },
{ label: 'Non-Mergeable Only', id: 'false' },
],
condition: { field: 'operation', value: ['get_positions'] },
},
{
id: 'positionSortBy',
title: 'Sort By',
type: 'dropdown',
options: [
{ label: 'Default', id: '' },
{ label: 'Tokens', id: 'TOKENS' },
{ label: 'Current Value', id: 'CURRENT' },
{ label: 'Initial Value', id: 'INITIAL' },
{ label: 'Cash P&L', id: 'CASHPNL' },
{ label: 'Percent P&L', id: 'PERCENTPNL' },
{ label: 'Title', id: 'TITLE' },
{ label: 'Price', id: 'PRICE' },
{ label: 'Avg Price', id: 'AVGPRICE' },
],
condition: { field: 'operation', value: ['get_positions'] },
},
{
id: 'positionSortDirection',
title: 'Sort Direction',
type: 'dropdown',
options: [
{ label: 'Descending', id: 'DESC' },
{ label: 'Ascending', id: 'ASC' },
],
condition: { field: 'operation', value: ['get_positions'] },
},
{
id: 'positionTitle',
title: 'Title Filter',
type: 'short-input',
placeholder: 'Search by title',
condition: { field: 'operation', value: ['get_positions'] },
},
// Trades-specific filters
{
id: 'tradeSide',
title: 'Trade Side',
type: 'dropdown',
options: [
{ label: 'All', id: '' },
{ label: 'Buy', id: 'BUY' },
{ label: 'Sell', id: 'SELL' },
],
condition: { field: 'operation', value: ['get_trades'] },
},
{
id: 'takerOnly',
title: 'Taker Only',
type: 'dropdown',
options: [
{ label: 'Yes (default)', id: 'true' },
{ label: 'No', id: 'false' },
],
condition: { field: 'operation', value: ['get_trades'] },
},
{
id: 'filterType',
title: 'Filter Type',
type: 'dropdown',
options: [
{ label: 'None', id: '' },
{ label: 'Cash', id: 'CASH' },
{ label: 'Tokens', id: 'TOKENS' },
],
condition: { field: 'operation', value: ['get_trades'] },
},
{
id: 'filterAmount',
title: 'Filter Amount',
type: 'short-input',
placeholder: 'Minimum amount threshold',
condition: { field: 'operation', value: ['get_trades'] },
},
// Activity-specific fields
{
id: 'activityUser',
title: 'User Wallet Address',
type: 'short-input',
placeholder: 'Wallet address (0x-prefixed)',
required: true,
condition: { field: 'operation', value: ['get_activity'] },
},
{
id: 'activityType',
title: 'Activity Type',
type: 'dropdown',
options: [
{ label: 'All', id: '' },
{ label: 'Trade', id: 'TRADE' },
{ label: 'Split', id: 'SPLIT' },
{ label: 'Merge', id: 'MERGE' },
{ label: 'Redeem', id: 'REDEEM' },
{ label: 'Reward', id: 'REWARD' },
{ label: 'Conversion', id: 'CONVERSION' },
{ label: 'Maker Rebate', id: 'MAKER_REBATE' },
],
condition: { field: 'operation', value: ['get_activity'] },
},
{
id: 'activityMarket',
title: 'Condition ID',
type: 'short-input',
placeholder: 'Condition ID filter (comma-separated)',
condition: { field: 'operation', value: ['get_activity'] },
},
{
id: 'activityEventId',
title: 'Event ID',
type: 'short-input',
placeholder: 'Event ID filter (comma-separated)',
condition: { field: 'operation', value: ['get_activity'] },
},
{
id: 'activitySide',
title: 'Trade Side',
type: 'dropdown',
options: [
{ label: 'All', id: '' },
{ label: 'Buy', id: 'BUY' },
{ label: 'Sell', id: 'SELL' },
],
condition: { field: 'operation', value: ['get_activity'] },
},
{
id: 'activitySortBy',
title: 'Sort By',
type: 'dropdown',
options: [
{ label: 'Timestamp', id: 'TIMESTAMP' },
{ label: 'Tokens', id: 'TOKENS' },
{ label: 'Cash', id: 'CASH' },
],
condition: { field: 'operation', value: ['get_activity'] },
},
{
id: 'activitySortDirection',
title: 'Sort Direction',
type: 'dropdown',
options: [
{ label: 'Descending', id: 'DESC' },
{ label: 'Ascending', id: 'ASC' },
],
condition: { field: 'operation', value: ['get_activity'] },
},
{
id: 'activityStart',
title: 'Start Timestamp',
type: 'short-input',
placeholder: 'Unix timestamp (seconds)',
condition: { field: 'operation', value: ['get_activity'] },
},
{
id: 'activityEnd',
title: 'End Timestamp',
type: 'short-input',
placeholder: 'Unix timestamp (seconds)',
condition: { field: 'operation', value: ['get_activity'] },
},
// Leaderboard-specific fields
{
id: 'leaderboardCategory',
title: 'Category',
type: 'dropdown',
options: [
{ label: 'Overall', id: 'OVERALL' },
{ label: 'Politics', id: 'POLITICS' },
{ label: 'Sports', id: 'SPORTS' },
{ label: 'Crypto', id: 'CRYPTO' },
{ label: 'Culture', id: 'CULTURE' },
{ label: 'Mentions', id: 'MENTIONS' },
{ label: 'Weather', id: 'WEATHER' },
{ label: 'Economics', id: 'ECONOMICS' },
{ label: 'Tech', id: 'TECH' },
{ label: 'Finance', id: 'FINANCE' },
],
condition: { field: 'operation', value: ['get_leaderboard'] },
},
{
id: 'leaderboardTimePeriod',
title: 'Time Period',
type: 'dropdown',
options: [
{ label: 'Day', id: 'DAY' },
{ label: 'Week', id: 'WEEK' },
{ label: 'Month', id: 'MONTH' },
{ label: 'All Time', id: 'ALL' },
],
condition: { field: 'operation', value: ['get_leaderboard'] },
},
{
id: 'leaderboardOrderBy',
title: 'Order By',
type: 'dropdown',
options: [
{ label: 'Profit/Loss', id: 'PNL' },
{ label: 'Volume', id: 'VOL' },
],
condition: { field: 'operation', value: ['get_leaderboard'] },
},
{
id: 'leaderboardUser',
title: 'User Address',
type: 'short-input',
placeholder: 'Filter by specific user wallet',
condition: { field: 'operation', value: ['get_leaderboard'] },
},
{
id: 'leaderboardUserName',
title: 'Username',
type: 'short-input',
placeholder: 'Filter by username',
condition: { field: 'operation', value: ['get_leaderboard'] },
},
// Market Holders-specific fields
{
id: 'holdersMarket',
title: 'Condition ID',
type: 'short-input',
placeholder: 'Condition ID (comma-separated)',
required: true,
condition: { field: 'operation', value: ['get_holders'] },
},
{
id: 'holdersMinBalance',
title: 'Min Balance',
type: 'short-input',
placeholder: 'Minimum balance threshold (default: 1)',
condition: { field: 'operation', value: ['get_holders'] },
},
// Token ID for CLOB operations
{
id: 'tokenId',
@@ -475,11 +205,11 @@ Return ONLY the Unix timestamp as a number - no explanations, no quotes, no extr
// Filters for list operations
{
id: 'closed',
title: 'Closed Status',
title: 'Status',
type: 'dropdown',
options: [
{ label: 'All', id: '' },
{ label: 'Open Only', id: 'false' },
{ label: 'Active Only', id: 'false' },
{ label: 'Closed Only', id: 'true' },
],
condition: { field: 'operation', value: ['get_markets', 'get_events'] },
@@ -539,18 +269,7 @@ Return ONLY the Unix timestamp as a number - no explanations, no quotes, no extr
placeholder: 'Number of results (max 50)',
condition: {
field: 'operation',
value: [
'get_markets',
'get_events',
'get_tags',
'search',
'get_series',
'get_trades',
'get_positions',
'get_activity',
'get_leaderboard',
'get_holders',
],
value: ['get_markets', 'get_events', 'get_tags', 'search', 'get_series', 'get_trades'],
},
},
{
@@ -560,25 +279,9 @@ Return ONLY the Unix timestamp as a number - no explanations, no quotes, no extr
placeholder: 'Pagination offset',
condition: {
field: 'operation',
value: [
'get_markets',
'get_events',
'get_tags',
'get_series',
'get_trades',
'get_positions',
'get_activity',
'get_leaderboard',
],
value: ['get_markets', 'get_events', 'get_tags', 'search', 'get_series', 'get_trades'],
},
},
{
id: 'page',
title: 'Page',
type: 'short-input',
placeholder: 'Page number (1-indexed)',
condition: { field: 'operation', value: ['search'] },
},
],
tools: {
access: [
@@ -599,9 +302,6 @@ Return ONLY the Unix timestamp as a number - no explanations, no quotes, no extr
'polymarket_get_tick_size',
'polymarket_get_positions',
'polymarket_get_trades',
'polymarket_get_activity',
'polymarket_get_leaderboard',
'polymarket_get_holders',
],
config: {
tool: (params) => {
@@ -640,49 +340,12 @@ Return ONLY the Unix timestamp as a number - no explanations, no quotes, no extr
return 'polymarket_get_positions'
case 'get_trades':
return 'polymarket_get_trades'
case 'get_activity':
return 'polymarket_get_activity'
case 'get_leaderboard':
return 'polymarket_get_leaderboard'
case 'get_holders':
return 'polymarket_get_holders'
default:
return 'polymarket_get_markets'
}
},
params: (params) => {
const {
operation,
marketSlug,
eventSlug,
orderEvents,
order,
positionEventId,
tradeSide,
positionSortBy,
positionSortDirection,
positionTitle,
// Activity params
activityUser,
activityType,
activityMarket,
activityEventId,
activitySide,
activitySortBy,
activitySortDirection,
activityStart,
activityEnd,
// Leaderboard params
leaderboardCategory,
leaderboardTimePeriod,
leaderboardOrderBy,
leaderboardUser,
leaderboardUserName,
// Holders params
holdersMarket,
holdersMinBalance,
...rest
} = params
const { operation, marketSlug, eventSlug, orderEvents, order, ...rest } = params
const cleanParams: Record<string, any> = {}
// Map marketSlug to slug for get_market
@@ -702,51 +365,6 @@ Return ONLY the Unix timestamp as a number - no explanations, no quotes, no extr
cleanParams.order = orderEvents
}
// Map positionEventId to eventId for positions and trades
if ((operation === 'get_positions' || operation === 'get_trades') && positionEventId) {
cleanParams.eventId = positionEventId
}
// Map tradeSide to side for trades
if (operation === 'get_trades' && tradeSide) {
cleanParams.side = tradeSide
}
// Map position-specific fields
if (operation === 'get_positions') {
if (positionSortBy) cleanParams.sortBy = positionSortBy
if (positionSortDirection) cleanParams.sortDirection = positionSortDirection
if (positionTitle) cleanParams.title = positionTitle
}
// Map activity-specific fields
if (operation === 'get_activity') {
if (activityUser) cleanParams.user = activityUser
if (activityType) cleanParams.type = activityType
if (activityMarket) cleanParams.market = activityMarket
if (activityEventId) cleanParams.eventId = activityEventId
if (activitySide) cleanParams.side = activitySide
if (activitySortBy) cleanParams.sortBy = activitySortBy
if (activitySortDirection) cleanParams.sortDirection = activitySortDirection
if (activityStart) cleanParams.start = Number(activityStart)
if (activityEnd) cleanParams.end = Number(activityEnd)
}
// Map leaderboard-specific fields
if (operation === 'get_leaderboard') {
if (leaderboardCategory) cleanParams.category = leaderboardCategory
if (leaderboardTimePeriod) cleanParams.timePeriod = leaderboardTimePeriod
if (leaderboardOrderBy) cleanParams.orderBy = leaderboardOrderBy
if (leaderboardUser) cleanParams.user = leaderboardUser
if (leaderboardUserName) cleanParams.userName = leaderboardUserName
}
// Map holders-specific fields
if (operation === 'get_holders') {
if (holdersMarket) cleanParams.market = holdersMarket
if (holdersMinBalance) cleanParams.minBalance = holdersMinBalance
}
// Convert numeric fields from string to number for get_price_history
if (operation === 'get_price_history') {
if (rest.fidelity) cleanParams.fidelity = Number(rest.fidelity)
@@ -776,55 +394,13 @@ Return ONLY the Unix timestamp as a number - no explanations, no quotes, no extr
seriesId: { type: 'string', description: 'Series ID' },
query: { type: 'string', description: 'Search query' },
user: { type: 'string', description: 'User wallet address' },
market: { type: 'string', description: 'Condition ID filter' },
positionEventId: { type: 'string', description: 'Event ID filter for positions/trades' },
market: { type: 'string', description: 'Market ID filter' },
tokenId: { type: 'string', description: 'CLOB Token ID' },
side: { type: 'string', description: 'Order side (buy/sell)' },
interval: { type: 'string', description: 'Price history interval' },
fidelity: { type: 'number', description: 'Data resolution in minutes' },
startTs: { type: 'number', description: 'Start timestamp (Unix)' },
endTs: { type: 'number', description: 'End timestamp (Unix)' },
// Positions-specific inputs
sizeThreshold: { type: 'string', description: 'Minimum position size threshold' },
redeemable: { type: 'string', description: 'Filter by redeemable status' },
mergeable: { type: 'string', description: 'Filter by mergeable status' },
positionSortBy: { type: 'string', description: 'Sort positions by field' },
positionSortDirection: { type: 'string', description: 'Sort direction (ASC/DESC)' },
positionTitle: { type: 'string', description: 'Filter positions by title' },
// Trades-specific inputs
tradeSide: { type: 'string', description: 'Filter trades by side (BUY/SELL)' },
takerOnly: { type: 'string', description: 'Filter to taker trades only' },
filterType: { type: 'string', description: 'Trade filter type (CASH/TOKENS)' },
filterAmount: { type: 'string', description: 'Minimum trade amount threshold' },
// List operation filters
closed: { type: 'string', description: 'Filter by closed status' },
order: { type: 'string', description: 'Sort field for markets' },
orderEvents: { type: 'string', description: 'Sort field for events' },
ascending: { type: 'string', description: 'Sort order (true/false)' },
tagId: { type: 'string', description: 'Filter by tag ID' },
// Pagination
limit: { type: 'string', description: 'Number of results per page' },
offset: { type: 'string', description: 'Pagination offset' },
page: { type: 'string', description: 'Page number for search' },
// Activity-specific inputs
activityUser: { type: 'string', description: 'User wallet address for activity' },
activityType: { type: 'string', description: 'Activity type filter' },
activityMarket: { type: 'string', description: 'Condition ID filter for activity' },
activityEventId: { type: 'string', description: 'Event ID filter for activity' },
activitySide: { type: 'string', description: 'Trade side filter for activity' },
activitySortBy: { type: 'string', description: 'Sort field for activity' },
activitySortDirection: { type: 'string', description: 'Sort direction for activity' },
activityStart: { type: 'string', description: 'Start timestamp for activity' },
activityEnd: { type: 'string', description: 'End timestamp for activity' },
// Leaderboard-specific inputs
leaderboardCategory: { type: 'string', description: 'Leaderboard category' },
leaderboardTimePeriod: { type: 'string', description: 'Leaderboard time period' },
leaderboardOrderBy: { type: 'string', description: 'Leaderboard order by field' },
leaderboardUser: { type: 'string', description: 'Filter leaderboard by user' },
leaderboardUserName: { type: 'string', description: 'Filter leaderboard by username' },
// Holders-specific inputs
holdersMarket: { type: 'string', description: 'Condition ID for holders lookup' },
holdersMinBalance: { type: 'string', description: 'Minimum balance threshold' },
},
outputs: {
// List operations
@@ -846,19 +422,11 @@ Return ONLY the Unix timestamp as a number - no explanations, no quotes, no extr
description: 'Search results with markets, events, profiles (search)',
},
// CLOB operations
orderbook: {
type: 'json',
description: 'Order book with bids and asks (get_orderbook)',
},
orderbook: { type: 'json', description: 'Order book with bids and asks (get_orderbook)' },
price: { type: 'string', description: 'Market price (get_price, get_last_trade_price)' },
side: { type: 'string', description: 'Last trade side - BUY or SELL (get_last_trade_price)' },
midpoint: { type: 'string', description: 'Midpoint price (get_midpoint)' },
history: { type: 'json', description: 'Price history entries (get_price_history)' },
spread: { type: 'json', description: 'Spread value object (get_spread)' },
spread: { type: 'json', description: 'Bid-ask spread (get_spread)' },
tickSize: { type: 'string', description: 'Minimum tick size (get_tick_size)' },
// Data API operations
activity: { type: 'json', description: 'Array of user activity entries (get_activity)' },
leaderboard: { type: 'json', description: 'Array of leaderboard entries (get_leaderboard)' },
holders: { type: 'json', description: 'Array of market holder groups (get_holders)' },
},
}

View File

@@ -58,7 +58,7 @@ import { IntercomBlock, IntercomV2Block } from '@/blocks/blocks/intercom'
import { JinaBlock } from '@/blocks/blocks/jina'
import { JiraBlock } from '@/blocks/blocks/jira'
import { JiraServiceManagementBlock } from '@/blocks/blocks/jira_service_management'
import { KalshiBlock, KalshiV2Block } from '@/blocks/blocks/kalshi'
import { KalshiBlock } from '@/blocks/blocks/kalshi'
import { KnowledgeBlock } from '@/blocks/blocks/knowledge'
import { LangsmithBlock } from '@/blocks/blocks/langsmith'
import { LemlistBlock } from '@/blocks/blocks/lemlist'
@@ -222,7 +222,6 @@ export const registry: Record<string, BlockConfig> = {
jira: JiraBlock,
jira_service_management: JiraServiceManagementBlock,
kalshi: KalshiBlock,
kalshi_v2: KalshiV2Block,
knowledge: KnowledgeBlock,
langsmith: LangsmithBlock,
lemlist: LemlistBlock,

View File

@@ -10,6 +10,7 @@ import {
useRef,
useState,
} from 'react'
import { ChevronRight } from 'lucide-react'
import { highlight, languages } from 'prismjs'
import { List, type RowComponentProps, useDynamicRowHeight, useListRef } from 'react-window'
import 'prismjs/components/prism-javascript'
@@ -36,6 +37,11 @@ export const CODE_LINE_HEIGHT_PX = 21
*/
const GUTTER_WIDTHS = [20, 20, 30, 38, 46, 54] as const
/**
* Width of the collapse column in pixels.
*/
const COLLAPSE_COLUMN_WIDTH = 12
/**
* Calculates the dynamic gutter width based on the number of lines.
* @param lineCount - The total number of lines in the code
@@ -46,6 +52,261 @@ export function calculateGutterWidth(lineCount: number): number {
return GUTTER_WIDTHS[Math.min(digits - 1, GUTTER_WIDTHS.length - 1)]
}
/**
* Information about a collapsible region in code.
*/
interface CollapsibleRegion {
/** Line index where the region starts (0-based) */
startLine: number
/** Line index where the region ends (0-based, inclusive) */
endLine: number
/** Type of collapsible region */
type: 'block' | 'string'
}
/**
* Minimum string length to be considered collapsible.
*/
const MIN_COLLAPSIBLE_STRING_LENGTH = 80
/**
* Regex to match a JSON string value (key: "value" pattern).
* Pre-compiled for performance.
*/
const STRING_VALUE_REGEX = /:\s*"([^"\\]|\\.)*"[,]?\s*$/
/**
* Finds collapsible regions in JSON code by matching braces and detecting long strings.
* A region is collapsible if it spans multiple lines OR contains a long string value.
*
* @param lines - Array of code lines
* @returns Map of start line index to CollapsibleRegion
*/
function findCollapsibleRegions(lines: string[]): Map<number, CollapsibleRegion> {
const regions = new Map<number, CollapsibleRegion>()
const stack: { char: '{' | '['; line: number }[] = []
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
const stringMatch = line.match(STRING_VALUE_REGEX)
if (stringMatch) {
const colonIdx = line.indexOf('":')
if (colonIdx !== -1) {
const valueStart = line.indexOf('"', colonIdx + 1)
const valueEnd = line.lastIndexOf('"')
if (valueStart !== -1 && valueEnd > valueStart) {
const stringValue = line.slice(valueStart + 1, valueEnd)
// Check if string is long enough or contains escaped newlines
if (stringValue.length >= MIN_COLLAPSIBLE_STRING_LENGTH || stringValue.includes('\\n')) {
regions.set(i, { startLine: i, endLine: i, type: 'string' })
}
}
}
}
// Check for block regions (objects/arrays)
for (const char of line) {
if (char === '{' || char === '[') {
stack.push({ char, line: i })
} else if (char === '}' || char === ']') {
const expected = char === '}' ? '{' : '['
if (stack.length > 0 && stack[stack.length - 1].char === expected) {
const start = stack.pop()!
// Only create a region if it spans multiple lines
if (i > start.line) {
regions.set(start.line, {
startLine: start.line,
endLine: i,
type: 'block',
})
}
}
}
}
}
return regions
}
/**
* Computes visible line indices based on collapsed regions.
* Only block regions hide lines; string regions just truncate content.
*
* @param totalLines - Total number of lines
* @param collapsedLines - Set of line indices that are collapsed (start lines of regions)
* @param regions - Map of collapsible regions
* @returns Sorted array of visible line indices
*/
function computeVisibleLineIndices(
totalLines: number,
collapsedLines: Set<number>,
regions: Map<number, CollapsibleRegion>
): number[] {
if (collapsedLines.size === 0) {
return Array.from({ length: totalLines }, (_, i) => i)
}
// Build sorted list of hidden ranges (only for block regions, not string regions)
const hiddenRanges: Array<{ start: number; end: number }> = []
for (const startLine of collapsedLines) {
const region = regions.get(startLine)
if (region && region.type === 'block' && region.endLine > region.startLine + 1) {
hiddenRanges.push({ start: region.startLine + 1, end: region.endLine - 1 })
}
}
hiddenRanges.sort((a, b) => a.start - b.start)
// Merge overlapping ranges
const merged: Array<{ start: number; end: number }> = []
for (const range of hiddenRanges) {
if (merged.length === 0 || merged[merged.length - 1].end < range.start - 1) {
merged.push(range)
} else {
merged[merged.length - 1].end = Math.max(merged[merged.length - 1].end, range.end)
}
}
// Build visible indices by skipping hidden ranges
const visible: number[] = []
let rangeIdx = 0
for (let i = 0; i < totalLines; i++) {
while (rangeIdx < merged.length && merged[rangeIdx].end < i) {
rangeIdx++
}
if (rangeIdx < merged.length && i >= merged[rangeIdx].start && i <= merged[rangeIdx].end) {
continue
}
visible.push(i)
}
return visible
}
/**
* Truncates a long string value in a JSON line for collapsed display.
*
* @param line - The original line content
* @returns Truncated line with ellipsis
*/
function truncateStringLine(line: string): string {
const colonIdx = line.indexOf('":')
if (colonIdx === -1) return line
const valueStart = line.indexOf('"', colonIdx + 1)
if (valueStart === -1) return line
const prefix = line.slice(0, valueStart + 1)
const suffix = line.charCodeAt(line.length - 1) === 44 /* ',' */ ? '",' : '"'
const truncated = line.slice(valueStart + 1, valueStart + 31)
return `${prefix}${truncated}...${suffix}`
}
/**
* Custom hook for managing JSON collapse state and computations.
*
* @param lines - Array of code lines
* @param showCollapseColumn - Whether collapse functionality is enabled
* @param language - Programming language for syntax detection
* @returns Object containing collapse state and handlers
*/
function useJsonCollapse(
lines: string[],
showCollapseColumn: boolean,
language: string
): {
collapsedLines: Set<number>
collapsibleLines: Set<number>
collapsibleRegions: Map<number, CollapsibleRegion>
collapsedStringLines: Set<number>
visibleLineIndices: number[]
toggleCollapse: (lineIndex: number) => void
} {
const [collapsedLines, setCollapsedLines] = useState<Set<number>>(new Set())
const collapsibleRegions = useMemo(() => {
if (!showCollapseColumn || language !== 'json') return new Map<number, CollapsibleRegion>()
return findCollapsibleRegions(lines)
}, [lines, showCollapseColumn, language])
const collapsibleLines = useMemo(() => new Set(collapsibleRegions.keys()), [collapsibleRegions])
// Track which collapsed lines are string type (need truncation, not hiding)
const collapsedStringLines = useMemo(() => {
const stringLines = new Set<number>()
for (const lineIdx of collapsedLines) {
const region = collapsibleRegions.get(lineIdx)
if (region?.type === 'string') {
stringLines.add(lineIdx)
}
}
return stringLines
}, [collapsedLines, collapsibleRegions])
const visibleLineIndices = useMemo(() => {
if (!showCollapseColumn) {
return Array.from({ length: lines.length }, (_, i) => i)
}
return computeVisibleLineIndices(lines.length, collapsedLines, collapsibleRegions)
}, [lines.length, collapsedLines, collapsibleRegions, showCollapseColumn])
const toggleCollapse = useCallback((lineIndex: number) => {
setCollapsedLines((prev) => {
const next = new Set(prev)
if (next.has(lineIndex)) {
next.delete(lineIndex)
} else {
next.add(lineIndex)
}
return next
})
}, [])
return {
collapsedLines,
collapsibleLines,
collapsibleRegions,
collapsedStringLines,
visibleLineIndices,
toggleCollapse,
}
}
/**
* Props for the CollapseButton component.
*/
interface CollapseButtonProps {
/** Whether the region is currently collapsed */
isCollapsed: boolean
/** Handler for toggle click */
onClick: () => void
}
/**
* Collapse/expand button with chevron icon.
* Rotates chevron based on collapse state.
*
* @param props - Component props
*/
const CollapseButton = memo(function CollapseButton({ isCollapsed, onClick }: CollapseButtonProps) {
return (
<button
type='button'
onClick={onClick}
className='flex h-[21px] w-[12px] cursor-pointer items-center justify-center border-none bg-transparent p-0 text-[var(--text-muted)] hover:text-[var(--text-secondary)]'
aria-label={isCollapsed ? 'Expand' : 'Collapse'}
>
<ChevronRight
className={cn(
'!h-[12px] !w-[12px] transition-transform duration-100',
!isCollapsed && 'rotate-90'
)}
/>
</button>
)
})
/**
* Props for the Code.Container component.
*/
@@ -256,28 +517,64 @@ function Placeholder({ children, gutterWidth, show, className }: CodePlaceholder
}
/**
* Props for virtualized row rendering.
* Represents a highlighted line of code.
*/
interface HighlightedLine {
/** 1-based line number */
lineNumber: number
/** Syntax-highlighted HTML content */
html: string
}
/**
* Props for virtualized row rendering.
*/
interface CodeRowProps {
/** Array of highlighted lines to render */
lines: HighlightedLine[]
/** Width of the gutter in pixels */
gutterWidth: number
/** Whether to show the line number gutter */
showGutter: boolean
/** Custom styles for the gutter */
gutterStyle?: React.CSSProperties
/** Left offset for alignment */
leftOffset: number
/** Whether to wrap long lines */
wrapText: boolean
/** Whether to show the collapse column */
showCollapseColumn: boolean
/** Set of line indices that can be collapsed */
collapsibleLines: Set<number>
/** Set of line indices that are currently collapsed */
collapsedLines: Set<number>
/** Handler for toggling collapse state */
onToggleCollapse: (lineIndex: number) => void
}
/**
* Row component for virtualized code viewer.
* Renders a single line with optional gutter and collapse button.
*
* @param props - Row component props from react-window
*/
function CodeRow({ index, style, ...props }: RowComponentProps<CodeRowProps>) {
const { lines, gutterWidth, showGutter, gutterStyle, leftOffset, wrapText } = props
const {
lines,
gutterWidth,
showGutter,
gutterStyle,
leftOffset,
wrapText,
showCollapseColumn,
collapsibleLines,
collapsedLines,
onToggleCollapse,
} = props
const line = lines[index]
const originalLineIndex = line.lineNumber - 1
const isCollapsible = showCollapseColumn && collapsibleLines.has(originalLineIndex)
const isCollapsed = collapsedLines.has(originalLineIndex)
return (
<div style={style} className={cn('flex', wrapText && 'overflow-hidden')} data-row-index={index}>
@@ -289,6 +586,19 @@ function CodeRow({ index, style, ...props }: RowComponentProps<CodeRowProps>) {
{line.lineNumber}
</div>
)}
{showCollapseColumn && (
<div
className='ml-1 flex flex-shrink-0 items-start justify-end'
style={{ width: COLLAPSE_COLUMN_WIDTH }}
>
{isCollapsible && (
<CollapseButton
isCollapsed={isCollapsed}
onClick={() => onToggleCollapse(originalLineIndex)}
/>
)}
</div>
)}
<pre
className={cn(
'm-0 flex-1 pr-2 pl-2 font-mono text-[13px] text-[var(--text-primary)] leading-[21px] dark:text-[#eeeeee]',
@@ -302,6 +612,12 @@ function CodeRow({ index, style, ...props }: RowComponentProps<CodeRowProps>) {
/**
* Applies search highlighting to a single line for virtualized rendering.
*
* @param html - The syntax-highlighted HTML string
* @param searchQuery - The search query to highlight
* @param currentMatchIndex - Index of the current match (for distinct highlighting)
* @param globalMatchOffset - Cumulative match count before this line
* @returns Object containing highlighted HTML and count of matches in this line
*/
function applySearchHighlightingToLine(
html: string,
@@ -366,6 +682,8 @@ interface CodeViewerProps {
contentRef?: React.RefObject<HTMLDivElement | null>
/** Enable virtualized rendering for large outputs (uses react-window) */
virtualized?: boolean
/** Whether to show a collapse column for JSON folding (only for json language) */
showCollapseColumn?: boolean
}
/**
@@ -422,42 +740,41 @@ function applySearchHighlighting(
.join('')
}
/**
* Counts all matches for a search query in the given code.
*
* @param code - The raw code string
* @param searchQuery - The search query
* @returns Number of matches found
*/
function countSearchMatches(code: string, searchQuery: string): number {
if (!searchQuery.trim()) return 0
const escaped = escapeRegex(searchQuery)
const regex = new RegExp(escaped, 'gi')
const matches = code.match(regex)
return matches?.length ?? 0
}
/**
* Props for inner viewer components (with defaults already applied).
*/
type ViewerInnerProps = {
/** Code content to display */
code: string
/** Whether to show line numbers gutter */
showGutter: boolean
/** Language for syntax highlighting */
language: 'javascript' | 'json' | 'python'
/** Additional CSS classes for the container */
className?: string
/** Left padding offset in pixels */
paddingLeft: number
/** Custom styles for the gutter */
gutterStyle?: React.CSSProperties
/** Whether to wrap long lines */
wrapText: boolean
/** Search query to highlight */
searchQuery?: string
/** Index of the current active match */
currentMatchIndex: number
/** Callback when match count changes */
onMatchCountChange?: (count: number) => void
/** Ref for the content container */
contentRef?: React.RefObject<HTMLDivElement | null>
/** Whether to show collapse column for JSON folding */
showCollapseColumn: boolean
}
/**
* Virtualized code viewer implementation using react-window.
* Optimized for large outputs with efficient scrolling and dynamic row heights.
*
* @param props - Viewer props
*/
const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
code,
@@ -471,6 +788,7 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
currentMatchIndex,
onMatchCountChange,
contentRef,
showCollapseColumn,
}: ViewerInnerProps) {
const containerRef = useRef<HTMLDivElement>(null)
const listRef = useListRef(null)
@@ -481,52 +799,70 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
key: wrapText ? 'wrap' : 'nowrap',
})
const matchCount = useMemo(() => countSearchMatches(code, searchQuery || ''), [code, searchQuery])
const lines = useMemo(() => code.split('\n'), [code])
const gutterWidth = useMemo(() => calculateGutterWidth(lines.length), [lines.length])
const {
collapsedLines,
collapsibleLines,
collapsedStringLines,
visibleLineIndices,
toggleCollapse,
} = useJsonCollapse(lines, showCollapseColumn, language)
// Compute display lines (accounting for truncation of collapsed strings)
const displayLines = useMemo(() => {
return lines.map((line, idx) =>
collapsedStringLines.has(idx) ? truncateStringLine(line) : line
)
}, [lines, collapsedStringLines])
// Pre-compute cumulative match offsets based on DISPLAYED content (handles truncation)
const { matchOffsets, matchCount } = useMemo(() => {
if (!searchQuery?.trim()) return { matchOffsets: [], matchCount: 0 }
const offsets: number[] = []
let cumulative = 0
const escaped = escapeRegex(searchQuery)
const regex = new RegExp(escaped, 'gi')
const visibleSet = new Set(visibleLineIndices)
for (let i = 0; i < lines.length; i++) {
offsets.push(cumulative)
// Only count matches in visible lines, using displayed (possibly truncated) content
if (visibleSet.has(i)) {
const matches = displayLines[i].match(regex)
cumulative += matches?.length ?? 0
}
}
return { matchOffsets: offsets, matchCount: cumulative }
}, [lines.length, displayLines, visibleLineIndices, searchQuery])
useEffect(() => {
onMatchCountChange?.(matchCount)
}, [matchCount, onMatchCountChange])
const lines = useMemo(() => code.split('\n'), [code])
const lineCount = lines.length
const gutterWidth = useMemo(() => calculateGutterWidth(lineCount), [lineCount])
const highlightedLines = useMemo(() => {
// Only process visible lines for efficiency (not all lines)
const visibleLines = useMemo(() => {
const lang = languages[language] || languages.javascript
return lines.map((line, idx) => ({
lineNumber: idx + 1,
html: highlight(line, lang, language),
}))
}, [lines, language])
const hasSearch = searchQuery?.trim()
const matchOffsets = useMemo(() => {
if (!searchQuery?.trim()) return []
const offsets: number[] = []
let cumulative = 0
const escaped = escapeRegex(searchQuery)
const regex = new RegExp(escaped, 'gi')
return visibleLineIndices.map((idx) => {
let html = highlight(displayLines[idx], lang, language)
for (const line of lines) {
offsets.push(cumulative)
const matches = line.match(regex)
cumulative += matches?.length ?? 0
}
return offsets
}, [lines, searchQuery])
if (hasSearch && searchQuery) {
const result = applySearchHighlightingToLine(
html,
searchQuery,
currentMatchIndex,
matchOffsets[idx]
)
html = result.html
}
const linesWithSearch = useMemo(() => {
if (!searchQuery?.trim()) return highlightedLines
return highlightedLines.map((line, idx) => {
const { html } = applySearchHighlightingToLine(
line.html,
searchQuery,
currentMatchIndex,
matchOffsets[idx]
)
return { ...line, html }
return { lineNumber: idx + 1, html }
})
}, [highlightedLines, searchQuery, currentMatchIndex, matchOffsets])
}, [displayLines, language, visibleLineIndices, searchQuery, currentMatchIndex, matchOffsets])
useEffect(() => {
if (!searchQuery?.trim() || matchCount === 0 || !listRef.current) return
@@ -535,12 +871,15 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
for (let i = 0; i < matchOffsets.length; i++) {
const matchesInThisLine = (matchOffsets[i + 1] ?? matchCount) - matchOffsets[i]
if (currentMatchIndex >= accumulated && currentMatchIndex < accumulated + matchesInThisLine) {
listRef.current.scrollToRow({ index: i, align: 'center' })
const visibleIndex = visibleLineIndices.indexOf(i)
if (visibleIndex !== -1) {
listRef.current.scrollToRow({ index: visibleIndex, align: 'center' })
}
break
}
accumulated += matchesInThisLine
}
}, [currentMatchIndex, searchQuery, matchCount, matchOffsets, listRef])
}, [currentMatchIndex, searchQuery, matchCount, matchOffsets, listRef, visibleLineIndices])
useEffect(() => {
const container = containerRef.current
@@ -549,15 +888,11 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
const parent = container.parentElement
if (!parent) return
const updateHeight = () => {
setContainerHeight(parent.clientHeight)
}
const updateHeight = () => setContainerHeight(parent.clientHeight)
updateHeight()
const resizeObserver = new ResizeObserver(updateHeight)
resizeObserver.observe(parent)
return () => resizeObserver.disconnect()
}, [])
@@ -571,7 +906,7 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
if (rows.length === 0) return
return dynamicRowHeight.observeRowElements(rows)
}, [wrapText, dynamicRowHeight, linesWithSearch])
}, [wrapText, dynamicRowHeight, visibleLines])
const setRefs = useCallback(
(el: HTMLDivElement | null) => {
@@ -585,14 +920,29 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
const rowProps = useMemo(
() => ({
lines: linesWithSearch,
lines: visibleLines,
gutterWidth,
showGutter,
gutterStyle,
leftOffset: paddingLeft,
wrapText,
showCollapseColumn,
collapsibleLines,
collapsedLines,
onToggleCollapse: toggleCollapse,
}),
[linesWithSearch, gutterWidth, showGutter, gutterStyle, paddingLeft, wrapText]
[
visibleLines,
gutterWidth,
showGutter,
gutterStyle,
paddingLeft,
wrapText,
showCollapseColumn,
collapsibleLines,
collapsedLines,
toggleCollapse,
]
)
return (
@@ -609,7 +959,7 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
<List
listRef={listRef}
defaultHeight={containerHeight}
rowCount={lineCount}
rowCount={visibleLines.length}
rowHeight={wrapText ? dynamicRowHeight : CODE_LINE_HEIGHT_PX}
rowComponent={CodeRow}
rowProps={rowProps}
@@ -647,6 +997,9 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
*/
/**
* Non-virtualized code viewer implementation.
* Renders all lines directly without windowing.
*
* @param props - Viewer props
*/
function ViewerInner({
code,
@@ -660,23 +1013,96 @@ function ViewerInner({
currentMatchIndex,
onMatchCountChange,
contentRef,
showCollapseColumn,
}: ViewerInnerProps) {
// Compute match count and notify parent
const matchCount = useMemo(() => countSearchMatches(code, searchQuery || ''), [code, searchQuery])
const lines = useMemo(() => code.split('\n'), [code])
const gutterWidth = useMemo(() => calculateGutterWidth(lines.length), [lines.length])
const {
collapsedLines,
collapsibleLines,
collapsedStringLines,
visibleLineIndices,
toggleCollapse,
} = useJsonCollapse(lines, showCollapseColumn, language)
// Compute display lines (accounting for truncation of collapsed strings)
const displayLines = useMemo(() => {
return lines.map((line, idx) =>
collapsedStringLines.has(idx) ? truncateStringLine(line) : line
)
}, [lines, collapsedStringLines])
// Pre-compute cumulative match offsets based on DISPLAYED content (handles truncation)
const { cumulativeMatches, matchCount } = useMemo(() => {
if (!searchQuery?.trim()) return { cumulativeMatches: [0], matchCount: 0 }
const cumulative: number[] = [0]
const escaped = escapeRegex(searchQuery)
const regex = new RegExp(escaped, 'gi')
const visibleSet = new Set(visibleLineIndices)
for (let i = 0; i < lines.length; i++) {
const prev = cumulative[cumulative.length - 1]
// Only count matches in visible lines, using displayed content
if (visibleSet.has(i)) {
const matches = displayLines[i].match(regex)
cumulative.push(prev + (matches?.length ?? 0))
} else {
cumulative.push(prev)
}
}
return { cumulativeMatches: cumulative, matchCount: cumulative[cumulative.length - 1] }
}, [lines.length, displayLines, visibleLineIndices, searchQuery])
useEffect(() => {
onMatchCountChange?.(matchCount)
}, [matchCount, onMatchCountChange])
// Determine whitespace class based on wrap setting
// Pre-compute highlighted lines with search for visible indices (for gutter mode)
const highlightedVisibleLines = useMemo(() => {
const lang = languages[language] || languages.javascript
if (!searchQuery?.trim()) {
return visibleLineIndices.map((idx) => ({
lineNumber: idx + 1,
html: highlight(displayLines[idx], lang, language) || '&nbsp;',
}))
}
return visibleLineIndices.map((idx) => {
let html = highlight(displayLines[idx], lang, language)
const matchCounter = { count: cumulativeMatches[idx] }
html = applySearchHighlighting(html, searchQuery, currentMatchIndex, matchCounter)
return { lineNumber: idx + 1, html: html || '&nbsp;' }
})
}, [
displayLines,
language,
visibleLineIndices,
searchQuery,
currentMatchIndex,
cumulativeMatches,
])
// Pre-compute simple highlighted code (for no-gutter mode)
const highlightedCode = useMemo(() => {
const lang = languages[language] || languages.javascript
const visibleCode = visibleLineIndices.map((idx) => displayLines[idx]).join('\n')
let html = highlight(visibleCode, lang, language)
if (searchQuery?.trim()) {
const matchCounter = { count: 0 }
html = applySearchHighlighting(html, searchQuery, currentMatchIndex, matchCounter)
}
return html
}, [displayLines, language, visibleLineIndices, searchQuery, currentMatchIndex])
const whitespaceClass = wrapText ? 'whitespace-pre-wrap break-words' : 'whitespace-pre'
const collapseColumnWidth = showCollapseColumn ? COLLAPSE_COLUMN_WIDTH : 0
// Special rendering path: when wrapping with gutter, render per-line rows so gutter stays aligned.
if (showGutter && wrapText) {
const lines = code.split('\n')
const gutterWidth = calculateGutterWidth(lines.length)
const matchCounter = { count: 0 }
// Grid-based rendering for gutter alignment (works with wrap)
if (showGutter) {
return (
<Container className={className}>
<Content className='code-editor-theme' editorRef={contentRef}>
@@ -686,37 +1112,40 @@ function ViewerInner({
paddingTop: '8px',
paddingBottom: '8px',
display: 'grid',
gridTemplateColumns: `${gutterWidth}px 1fr`,
gridTemplateColumns: showCollapseColumn
? `${gutterWidth}px ${collapseColumnWidth}px 1fr`
: `${gutterWidth}px 1fr`,
}}
>
{lines.map((line, idx) => {
let perLineHighlighted = highlight(
line,
languages[language] || languages.javascript,
language
)
// Apply search highlighting if query exists
if (searchQuery?.trim()) {
perLineHighlighted = applySearchHighlighting(
perLineHighlighted,
searchQuery,
currentMatchIndex,
matchCounter
)
}
{highlightedVisibleLines.map(({ lineNumber, html }) => {
const idx = lineNumber - 1
const isCollapsible = collapsibleLines.has(idx)
const isCollapsed = collapsedLines.has(idx)
return (
<Fragment key={idx}>
<div
className='select-none pr-0.5 text-right text-[var(--text-muted)] text-xs tabular-nums leading-[21px] dark:text-[#a8a8a8]'
style={{ transform: 'translateY(0.25px)', ...gutterStyle }}
style={gutterStyle}
>
{idx + 1}
{lineNumber}
</div>
{showCollapseColumn && (
<div className='ml-1 flex items-start justify-end'>
{isCollapsible && (
<CollapseButton
isCollapsed={isCollapsed}
onClick={() => toggleCollapse(idx)}
/>
)}
</div>
)}
<pre
className='m-0 min-w-0 whitespace-pre-wrap break-words pr-2 pl-2 font-mono text-[13px] text-[var(--text-primary)] leading-[21px] dark:text-[#eeeeee]'
dangerouslySetInnerHTML={{ __html: perLineHighlighted || '&nbsp;' }}
className={cn(
'm-0 min-w-0 pr-2 pl-2 font-mono text-[13px] text-[var(--text-primary)] leading-[21px] dark:text-[#eeeeee]',
whitespaceClass
)}
dangerouslySetInnerHTML={{ __html: html }}
/>
</Fragment>
)
@@ -727,69 +1156,16 @@ function ViewerInner({
)
}
// Apply syntax highlighting
let highlightedCode = highlight(code, languages[language] || languages.javascript, language)
// Apply search highlighting if query exists
if (searchQuery?.trim()) {
const matchCounter = { count: 0 }
highlightedCode = applySearchHighlighting(
highlightedCode,
searchQuery,
currentMatchIndex,
matchCounter
)
}
if (!showGutter) {
// Simple display without gutter
return (
<Container className={className}>
<Content className='code-editor-theme' editorRef={contentRef}>
<pre
className={cn(
whitespaceClass,
'p-2 font-mono text-[13px] text-[var(--text-primary)] leading-[21px] dark:text-[#eeeeee]'
)}
dangerouslySetInnerHTML={{ __html: highlightedCode }}
/>
</Content>
</Container>
)
}
// Calculate line numbers
const lineCount = code.split('\n').length
const gutterWidth = calculateGutterWidth(lineCount)
// Render line numbers
const lineNumbers = []
for (let i = 1; i <= lineCount; i++) {
lineNumbers.push(
<div
key={i}
className='text-right text-[var(--text-muted)] text-xs tabular-nums leading-[21px] dark:text-[#a8a8a8]'
>
{i}
</div>
)
}
// Simple display without gutter
return (
<Container className={className}>
<Gutter width={gutterWidth} style={{ left: `${paddingLeft}px`, ...gutterStyle }}>
{lineNumbers}
</Gutter>
<Content
className='code-editor-theme'
paddingLeft={`${gutterWidth + paddingLeft}px`}
editorRef={contentRef}
>
<Content className='code-editor-theme' editorRef={contentRef}>
<pre
className={cn(
whitespaceClass,
'p-2 font-mono text-[13px] text-[var(--text-primary)] leading-[21px] dark:text-[#eeeeee]'
)}
style={{ paddingLeft: paddingLeft > 0 ? paddingLeft : undefined }}
dangerouslySetInnerHTML={{ __html: highlightedCode }}
/>
</Content>
@@ -800,6 +1176,9 @@ function ViewerInner({
/**
* Readonly code viewer with optional gutter and syntax highlighting.
* Routes to either standard or virtualized implementation based on the `virtualized` prop.
*
* @param props - Component props
* @returns React component
*/
function Viewer({
code,
@@ -814,6 +1193,7 @@ function Viewer({
onMatchCountChange,
contentRef,
virtualized = false,
showCollapseColumn = false,
}: CodeViewerProps) {
const innerProps: ViewerInnerProps = {
code,
@@ -827,6 +1207,7 @@ function Viewer({
currentMatchIndex,
onMatchCountChange,
contentRef,
showCollapseColumn,
}
return virtualized ? <VirtualizedViewerInner {...innerProps} /> : <ViewerInner {...innerProps} />

View File

@@ -1,146 +0,0 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomAssignConversationParams {
accessToken: string
conversationId: string
admin_id: string
assignee_id: string
body?: string
}
export interface IntercomAssignConversationV2Response {
success: boolean
output: {
conversation: any
conversationId: string
admin_assignee_id: number | null
team_assignee_id: string | null
}
}
const assignConversationBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
conversationId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the conversation to assign',
},
admin_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the admin performing the assignment',
},
assignee_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'The ID of the admin or team to assign the conversation to. Set to "0" to unassign.',
},
body: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional message to add when assigning (e.g., "Passing to the support team")',
},
},
request: {
url: (params: IntercomAssignConversationParams) =>
buildIntercomUrl(`/conversations/${params.conversationId}/parts`),
method: 'POST',
headers: (params: IntercomAssignConversationParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomAssignConversationParams) => {
const payload: any = {
message_type: 'assignment',
type: 'admin',
admin_id: params.admin_id,
assignee_id: params.assignee_id,
}
if (params.body) {
payload.body = params.body
}
return payload
},
},
} satisfies Pick<ToolConfig<IntercomAssignConversationParams, any>, 'params' | 'request'>
export const intercomAssignConversationV2Tool: ToolConfig<
IntercomAssignConversationParams,
IntercomAssignConversationV2Response
> = {
...assignConversationBase,
id: 'intercom_assign_conversation_v2',
name: 'Assign Conversation in Intercom',
description: 'Assign a conversation to an admin or team in Intercom',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'assign_conversation')
}
const data = await response.json()
return {
success: true,
output: {
conversation: data,
conversationId: data.id,
admin_assignee_id: data.admin_assignee_id ?? null,
team_assignee_id: data.team_assignee_id ?? null,
},
}
},
outputs: {
conversation: {
type: 'object',
description: 'The assigned conversation object',
properties: {
id: { type: 'string', description: 'Unique identifier for the conversation' },
type: { type: 'string', description: 'Object type (conversation)' },
state: { type: 'string', description: 'State of the conversation' },
open: { type: 'boolean', description: 'Whether the conversation is open' },
admin_assignee_id: {
type: 'number',
description: 'ID of the assigned admin',
optional: true,
},
team_assignee_id: {
type: 'string',
description: 'ID of the assigned team',
optional: true,
},
created_at: { type: 'number', description: 'Unix timestamp when conversation was created' },
updated_at: {
type: 'number',
description: 'Unix timestamp when conversation was last updated',
},
},
},
conversationId: { type: 'string', description: 'ID of the assigned conversation' },
admin_assignee_id: {
type: 'number',
description: 'ID of the assigned admin',
optional: true,
},
team_assignee_id: { type: 'string', description: 'ID of the assigned team', optional: true },
},
}

View File

@@ -1,115 +0,0 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomAttachContactToCompanyParams {
accessToken: string
contactId: string
companyId: string
}
export interface IntercomAttachContactToCompanyV2Response {
success: boolean
output: {
company: any
companyId: string
name: string | null
}
}
const attachContactToCompanyBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
contactId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the contact to attach to the company',
},
companyId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the company to attach the contact to',
},
},
request: {
url: (params: IntercomAttachContactToCompanyParams) =>
buildIntercomUrl(`/contacts/${params.contactId}/companies`),
method: 'POST',
headers: (params: IntercomAttachContactToCompanyParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomAttachContactToCompanyParams) => ({
id: params.companyId,
}),
},
} satisfies Pick<ToolConfig<IntercomAttachContactToCompanyParams, any>, 'params' | 'request'>
export const intercomAttachContactToCompanyV2Tool: ToolConfig<
IntercomAttachContactToCompanyParams,
IntercomAttachContactToCompanyV2Response
> = {
...attachContactToCompanyBase,
id: 'intercom_attach_contact_to_company_v2',
name: 'Attach Contact to Company in Intercom',
description: 'Attach a contact to a company in Intercom',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'attach_contact_to_company')
}
const data = await response.json()
return {
success: true,
output: {
company: {
id: data.id,
type: data.type ?? 'company',
company_id: data.company_id ?? null,
name: data.name ?? null,
created_at: data.created_at ?? null,
updated_at: data.updated_at ?? null,
user_count: data.user_count ?? null,
session_count: data.session_count ?? null,
monthly_spend: data.monthly_spend ?? null,
plan: data.plan ?? null,
},
companyId: data.id,
name: data.name ?? null,
},
}
},
outputs: {
company: {
type: 'object',
description: 'The company object the contact was attached to',
properties: {
id: { type: 'string', description: 'Unique identifier for the company' },
type: { type: 'string', description: 'Object type (company)' },
company_id: { type: 'string', description: 'The company_id you defined' },
name: { type: 'string', description: 'Name of the company' },
created_at: { type: 'number', description: 'Unix timestamp when company was created' },
updated_at: { type: 'number', description: 'Unix timestamp when company was updated' },
user_count: { type: 'number', description: 'Number of users in the company' },
session_count: { type: 'number', description: 'Number of sessions' },
monthly_spend: { type: 'number', description: 'Monthly spend amount' },
plan: { type: 'object', description: 'Company plan details' },
},
},
companyId: { type: 'string', description: 'ID of the company' },
name: { type: 'string', description: 'Name of the company', optional: true },
},
}

View File

@@ -1,129 +0,0 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomCloseConversationParams {
accessToken: string
conversationId: string
admin_id: string
body?: string
}
export interface IntercomCloseConversationV2Response {
success: boolean
output: {
conversation: any
conversationId: string
state: string
}
}
const closeConversationBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
conversationId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the conversation to close',
},
admin_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the admin performing the action',
},
body: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional closing message to add to the conversation',
},
},
request: {
url: (params: IntercomCloseConversationParams) =>
buildIntercomUrl(`/conversations/${params.conversationId}/parts`),
method: 'POST',
headers: (params: IntercomCloseConversationParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomCloseConversationParams) => {
const payload: any = {
message_type: 'close',
type: 'admin',
admin_id: params.admin_id,
}
if (params.body) {
payload.body = params.body
}
return payload
},
},
} satisfies Pick<ToolConfig<IntercomCloseConversationParams, any>, 'params' | 'request'>
export const intercomCloseConversationV2Tool: ToolConfig<
IntercomCloseConversationParams,
IntercomCloseConversationV2Response
> = {
...closeConversationBase,
id: 'intercom_close_conversation_v2',
name: 'Close Conversation in Intercom',
description: 'Close a conversation in Intercom',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'close_conversation')
}
const data = await response.json()
return {
success: true,
output: {
conversation: {
id: data.id,
type: data.type ?? 'conversation',
state: data.state ?? 'closed',
open: data.open ?? false,
read: data.read ?? false,
created_at: data.created_at ?? null,
updated_at: data.updated_at ?? null,
},
conversationId: data.id,
state: data.state ?? 'closed',
},
}
},
outputs: {
conversation: {
type: 'object',
description: 'The closed conversation object',
properties: {
id: { type: 'string', description: 'Unique identifier for the conversation' },
type: { type: 'string', description: 'Object type (conversation)' },
state: { type: 'string', description: 'State of the conversation (closed)' },
open: { type: 'boolean', description: 'Whether the conversation is open (false)' },
read: { type: 'boolean', description: 'Whether the conversation has been read' },
created_at: { type: 'number', description: 'Unix timestamp when conversation was created' },
updated_at: {
type: 'number',
description: 'Unix timestamp when conversation was last updated',
},
},
},
conversationId: { type: 'string', description: 'ID of the closed conversation' },
state: { type: 'string', description: 'State of the conversation (closed)' },
},
}

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomCreateCompany')
@@ -60,7 +60,7 @@ const createCompanyBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
visibility: 'hidden',
description: 'Intercom API access token',
},
company_id: {

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomCreateContact')
@@ -88,7 +88,7 @@ const intercomCreateContactBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
visibility: 'hidden',
description: 'Intercom API access token',
},
role: {

View File

@@ -1,148 +0,0 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('IntercomCreateEvent')
export interface IntercomCreateEventParams {
accessToken: string
event_name: string
created_at?: number
user_id?: string
email?: string
id?: string
metadata?: string
}
export interface IntercomCreateEventV2Response {
success: boolean
output: {
accepted: boolean
}
}
const createEventBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
event_name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'The name of the event (e.g., "order-completed"). Use past-tense verb-noun format for readability.',
},
created_at: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description:
'Unix timestamp for when the event occurred. Strongly recommended for uniqueness.',
},
user_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Your identifier for the user (external_id)',
},
email: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Email address of the user. Use only if your app uses email to uniquely identify users.',
},
id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The Intercom contact ID',
},
metadata: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'JSON object with up to 10 metadata key-value pairs about the event (e.g., {"order_value": 99.99})',
},
},
request: {
url: () => buildIntercomUrl('/events'),
method: 'POST',
headers: (params: IntercomCreateEventParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomCreateEventParams) => {
const payload: any = {
event_name: params.event_name,
}
if (params.created_at) {
payload.created_at = params.created_at
} else {
payload.created_at = Math.floor(Date.now() / 1000)
}
if (params.user_id) {
payload.user_id = params.user_id
}
if (params.email) {
payload.email = params.email
}
if (params.id) {
payload.id = params.id
}
if (params.metadata) {
try {
payload.metadata = JSON.parse(params.metadata)
} catch (error) {
logger.warn('Failed to parse metadata, ignoring', { error })
}
}
return payload
},
},
} satisfies Pick<ToolConfig<IntercomCreateEventParams, any>, 'params' | 'request'>
export const intercomCreateEventV2Tool: ToolConfig<
IntercomCreateEventParams,
IntercomCreateEventV2Response
> = {
...createEventBase,
id: 'intercom_create_event_v2',
name: 'Create Event in Intercom',
description: 'Track a custom event for a contact in Intercom',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'create_event')
}
return {
success: true,
output: {
accepted: true,
},
}
},
outputs: {
accepted: {
type: 'boolean',
description: 'Whether the event was accepted (202 Accepted)',
},
},
}

View File

@@ -1,5 +1,5 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
export interface IntercomCreateMessageParams {
accessToken: string
@@ -40,7 +40,7 @@ const createMessageBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
visibility: 'hidden',
description: 'Intercom API access token',
},
message_type: {

View File

@@ -1,131 +0,0 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomCreateNoteParams {
accessToken: string
contactId: string
body: string
admin_id?: string
}
export interface IntercomCreateNoteV2Response {
success: boolean
output: {
id: string
body: string
created_at: number
type: string
author: any | null
contact: any | null
}
}
const createNoteBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
contactId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the contact to add the note to',
},
body: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The text content of the note',
},
admin_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The ID of the admin creating the note',
},
},
request: {
url: (params: IntercomCreateNoteParams) =>
buildIntercomUrl(`/contacts/${params.contactId}/notes`),
method: 'POST',
headers: (params: IntercomCreateNoteParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomCreateNoteParams) => {
const payload: any = {
body: params.body,
}
if (params.admin_id) {
payload.admin_id = params.admin_id
}
return payload
},
},
} satisfies Pick<ToolConfig<IntercomCreateNoteParams, any>, 'params' | 'request'>
export const intercomCreateNoteV2Tool: ToolConfig<
IntercomCreateNoteParams,
IntercomCreateNoteV2Response
> = {
...createNoteBase,
id: 'intercom_create_note_v2',
name: 'Create Note in Intercom',
description: 'Add a note to a specific contact',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'create_note')
}
const data = await response.json()
return {
success: true,
output: {
id: data.id,
body: data.body,
created_at: data.created_at,
type: data.type ?? 'note',
author: data.author ?? null,
contact: data.contact ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Unique identifier for the note' },
body: { type: 'string', description: 'The text content of the note' },
created_at: { type: 'number', description: 'Unix timestamp when the note was created' },
type: { type: 'string', description: 'Object type (note)' },
author: {
type: 'object',
description: 'The admin who created the note',
optional: true,
properties: {
type: { type: 'string', description: 'Author type (admin)' },
id: { type: 'string', description: 'Author ID' },
name: { type: 'string', description: 'Author name' },
email: { type: 'string', description: 'Author email' },
},
},
contact: {
type: 'object',
description: 'The contact the note was created for',
optional: true,
properties: {
type: { type: 'string', description: 'Contact type' },
id: { type: 'string', description: 'Contact ID' },
},
},
},
}

View File

@@ -1,97 +0,0 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomCreateTagParams {
accessToken: string
name: string
id?: string
}
export interface IntercomCreateTagV2Response {
success: boolean
output: {
id: string
name: string
type: string
}
}
const createTagBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'The name of the tag. Will create a new tag if not found, or update the name if id is provided.',
},
id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The ID of an existing tag to update. Omit to create a new tag.',
},
},
request: {
url: () => buildIntercomUrl('/tags'),
method: 'POST',
headers: (params: IntercomCreateTagParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomCreateTagParams) => {
const payload: any = {
name: params.name,
}
if (params.id) {
payload.id = params.id
}
return payload
},
},
} satisfies Pick<ToolConfig<IntercomCreateTagParams, any>, 'params' | 'request'>
export const intercomCreateTagV2Tool: ToolConfig<
IntercomCreateTagParams,
IntercomCreateTagV2Response
> = {
...createTagBase,
id: 'intercom_create_tag_v2',
name: 'Create Tag in Intercom',
description: 'Create a new tag or update an existing tag name',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'create_tag')
}
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
type: data.type ?? 'tag',
},
}
},
outputs: {
id: { type: 'string', description: 'Unique identifier for the tag' },
name: { type: 'string', description: 'Name of the tag' },
type: { type: 'string', description: 'Object type (tag)' },
},
}

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomCreateTicket')
@@ -41,7 +41,7 @@ const createTicketBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
visibility: 'hidden',
description: 'Intercom API access token',
},
ticket_type_id: {

View File

@@ -1,5 +1,5 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
export interface IntercomDeleteContactParams {
accessToken: string
@@ -23,7 +23,7 @@ const intercomDeleteContactBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
visibility: 'hidden',
description: 'Intercom API access token',
},
contactId: {

View File

@@ -1,101 +0,0 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomDetachContactFromCompanyParams {
accessToken: string
contactId: string
companyId: string
}
export interface IntercomDetachContactFromCompanyV2Response {
success: boolean
output: {
company: any
companyId: string
name: string | null
}
}
const detachContactFromCompanyBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
contactId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the contact to detach from the company',
},
companyId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the company to detach the contact from',
},
},
request: {
url: (params: IntercomDetachContactFromCompanyParams) =>
buildIntercomUrl(`/contacts/${params.contactId}/companies/${params.companyId}`),
method: 'DELETE',
headers: (params: IntercomDetachContactFromCompanyParams) => ({
Authorization: `Bearer ${params.accessToken}`,
Accept: 'application/json',
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
},
} satisfies Pick<ToolConfig<IntercomDetachContactFromCompanyParams, any>, 'params' | 'request'>
export const intercomDetachContactFromCompanyV2Tool: ToolConfig<
IntercomDetachContactFromCompanyParams,
IntercomDetachContactFromCompanyV2Response
> = {
...detachContactFromCompanyBase,
id: 'intercom_detach_contact_from_company_v2',
name: 'Detach Contact from Company in Intercom',
description: 'Remove a contact from a company in Intercom',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'detach_contact_from_company')
}
const data = await response.json()
return {
success: true,
output: {
company: {
id: data.id,
type: data.type ?? 'company',
company_id: data.company_id ?? null,
name: data.name ?? null,
},
companyId: data.id,
name: data.name ?? null,
},
}
},
outputs: {
company: {
type: 'object',
description: 'The company object the contact was detached from',
properties: {
id: { type: 'string', description: 'Unique identifier for the company' },
type: { type: 'string', description: 'Object type (company)' },
company_id: { type: 'string', description: 'The company_id you defined' },
name: { type: 'string', description: 'Name of the company' },
},
},
companyId: { type: 'string', description: 'ID of the company' },
name: { type: 'string', description: 'Name of the company', optional: true },
},
}

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomGetCompany')
@@ -25,7 +25,7 @@ const getCompanyBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
visibility: 'hidden',
description: 'Intercom API access token',
},
companyId: {

View File

@@ -1,5 +1,5 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
export interface IntercomGetContactParams {
accessToken: string
@@ -22,7 +22,7 @@ const intercomGetContactBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
visibility: 'hidden',
description: 'Intercom API access token',
},
contactId: {

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomGetConversation')
@@ -27,7 +27,7 @@ const getConversationBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
visibility: 'hidden',
description: 'Intercom API access token',
},
conversationId: {

View File

@@ -1,5 +1,5 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
export interface IntercomGetTicketParams {
accessToken: string
@@ -31,7 +31,7 @@ const getTicketBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
visibility: 'hidden',
description: 'Intercom API access token',
},
ticketId: {

View File

@@ -1,28 +1,24 @@
export { intercomAssignConversationV2Tool } from './assign_conversation'
export { intercomAttachContactToCompanyV2Tool } from './attach_contact_to_company'
export { intercomCloseConversationV2Tool } from './close_conversation'
// Contact tools
// Company tools
export { intercomCreateCompanyTool, intercomCreateCompanyV2Tool } from './create_company'
export { intercomCreateContactTool, intercomCreateContactV2Tool } from './create_contact'
export { intercomCreateEventV2Tool } from './create_event'
// Message tools
export { intercomCreateMessageTool, intercomCreateMessageV2Tool } from './create_message'
export { intercomCreateNoteV2Tool } from './create_note'
export { intercomCreateTagV2Tool } from './create_tag'
// Ticket tools
export { intercomCreateTicketTool, intercomCreateTicketV2Tool } from './create_ticket'
export { intercomDeleteContactTool, intercomDeleteContactV2Tool } from './delete_contact'
export { intercomDetachContactFromCompanyV2Tool } from './detach_contact_from_company'
export { intercomGetCompanyTool, intercomGetCompanyV2Tool } from './get_company'
export { intercomGetContactTool, intercomGetContactV2Tool } from './get_contact'
// Conversation tools
export { intercomGetConversationTool, intercomGetConversationV2Tool } from './get_conversation'
export { intercomGetTicketTool, intercomGetTicketV2Tool } from './get_ticket'
export { intercomListAdminsV2Tool } from './list_admins'
export { intercomListCompaniesTool, intercomListCompaniesV2Tool } from './list_companies'
export { intercomListContactsTool, intercomListContactsV2Tool } from './list_contacts'
export {
intercomListConversationsTool,
intercomListConversationsV2Tool,
} from './list_conversations'
export { intercomListTagsV2Tool } from './list_tags'
export { intercomOpenConversationV2Tool } from './open_conversation'
export {
intercomReplyConversationTool,
intercomReplyConversationV2Tool,
@@ -32,9 +28,4 @@ export {
intercomSearchConversationsTool,
intercomSearchConversationsV2Tool,
} from './search_conversations'
export { intercomSnoozeConversationV2Tool } from './snooze_conversation'
export { intercomTagContactV2Tool } from './tag_contact'
export { intercomTagConversationV2Tool } from './tag_conversation'
export { intercomUntagContactV2Tool } from './untag_contact'
export { intercomUpdateContactTool, intercomUpdateContactV2Tool } from './update_contact'
export { intercomUpdateTicketV2Tool } from './update_ticket'

View File

@@ -1,125 +0,0 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomListAdminsParams {
accessToken: string
}
interface IntercomAdmin {
type: string
id: string
name: string
email: string
job_title: string | null
away_mode_enabled: boolean
away_mode_reassign: boolean
has_inbox_seat: boolean
team_ids: number[]
avatar: {
type: string
image_url: string | null
} | null
email_verified: boolean | null
}
export interface IntercomListAdminsV2Response {
success: boolean
output: {
admins: IntercomAdmin[]
type: string
}
}
const listAdminsBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
},
request: {
url: () => buildIntercomUrl('/admins'),
method: 'GET',
headers: (params: IntercomListAdminsParams) => ({
Authorization: `Bearer ${params.accessToken}`,
Accept: 'application/json',
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
},
} satisfies Pick<ToolConfig<IntercomListAdminsParams, any>, 'params' | 'request'>
export const intercomListAdminsV2Tool: ToolConfig<
IntercomListAdminsParams,
IntercomListAdminsV2Response
> = {
...listAdminsBase,
id: 'intercom_list_admins_v2',
name: 'List Admins from Intercom',
description: 'Fetch a list of all admins for the workspace',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'list_admins')
}
const data = await response.json()
return {
success: true,
output: {
admins: data.admins ?? [],
type: data.type ?? 'admin.list',
},
}
},
outputs: {
admins: {
type: 'array',
description: 'Array of admin objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique identifier for the admin' },
type: { type: 'string', description: 'Object type (admin)' },
name: { type: 'string', description: 'Name of the admin' },
email: { type: 'string', description: 'Email of the admin' },
job_title: { type: 'string', description: 'Job title of the admin', optional: true },
away_mode_enabled: {
type: 'boolean',
description: 'Whether admin is in away mode',
},
away_mode_reassign: {
type: 'boolean',
description: 'Whether to reassign conversations when away',
},
has_inbox_seat: {
type: 'boolean',
description: 'Whether admin has a paid inbox seat',
},
team_ids: {
type: 'array',
description: 'List of team IDs the admin belongs to',
},
avatar: {
type: 'object',
description: 'Avatar information',
optional: true,
},
email_verified: {
type: 'boolean',
description: 'Whether email is verified',
optional: true,
},
},
},
},
type: { type: 'string', description: 'Object type (admin.list)' },
},
}

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomListCompanies')
@@ -29,7 +29,7 @@ const listCompaniesBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
visibility: 'hidden',
description: 'Intercom API access token',
},
per_page: {

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomListContacts')
@@ -28,7 +28,7 @@ const listContactsBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
visibility: 'hidden',
description: 'Intercom API access token',
},
per_page: {

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomListConversations')
@@ -30,7 +30,7 @@ const listConversationsBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
visibility: 'hidden',
description: 'Intercom API access token',
},
per_page: {

View File

@@ -1,86 +0,0 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomListTagsParams {
accessToken: string
}
interface IntercomTag {
type: string
id: string
name: string
}
export interface IntercomListTagsV2Response {
success: boolean
output: {
tags: IntercomTag[]
type: string
}
}
const listTagsBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
},
request: {
url: () => buildIntercomUrl('/tags'),
method: 'GET',
headers: (params: IntercomListTagsParams) => ({
Authorization: `Bearer ${params.accessToken}`,
Accept: 'application/json',
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
},
} satisfies Pick<ToolConfig<IntercomListTagsParams, any>, 'params' | 'request'>
export const intercomListTagsV2Tool: ToolConfig<
IntercomListTagsParams,
IntercomListTagsV2Response
> = {
...listTagsBase,
id: 'intercom_list_tags_v2',
name: 'List Tags from Intercom',
description: 'Fetch a list of all tags in the workspace',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'list_tags')
}
const data = await response.json()
return {
success: true,
output: {
tags: data.tags ?? [],
type: data.type ?? 'tag.list',
},
}
},
outputs: {
tags: {
type: 'array',
description: 'Array of tag objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique identifier for the tag' },
type: { type: 'string', description: 'Object type (tag)' },
name: { type: 'string', description: 'Name of the tag' },
},
},
},
type: { type: 'string', description: 'Object type (list)' },
},
}

View File

@@ -1,114 +0,0 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomOpenConversationParams {
accessToken: string
conversationId: string
admin_id: string
}
export interface IntercomOpenConversationV2Response {
success: boolean
output: {
conversation: any
conversationId: string
state: string
}
}
const openConversationBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
conversationId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the conversation to open',
},
admin_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the admin performing the action',
},
},
request: {
url: (params: IntercomOpenConversationParams) =>
buildIntercomUrl(`/conversations/${params.conversationId}/parts`),
method: 'POST',
headers: (params: IntercomOpenConversationParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomOpenConversationParams) => ({
message_type: 'open',
type: 'admin',
admin_id: params.admin_id,
}),
},
} satisfies Pick<ToolConfig<IntercomOpenConversationParams, any>, 'params' | 'request'>
export const intercomOpenConversationV2Tool: ToolConfig<
IntercomOpenConversationParams,
IntercomOpenConversationV2Response
> = {
...openConversationBase,
id: 'intercom_open_conversation_v2',
name: 'Open Conversation in Intercom',
description: 'Open a closed or snoozed conversation in Intercom',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'open_conversation')
}
const data = await response.json()
return {
success: true,
output: {
conversation: {
id: data.id,
type: data.type ?? 'conversation',
state: data.state ?? 'open',
open: data.open ?? true,
read: data.read ?? false,
created_at: data.created_at ?? null,
updated_at: data.updated_at ?? null,
},
conversationId: data.id,
state: data.state ?? 'open',
},
}
},
outputs: {
conversation: {
type: 'object',
description: 'The opened conversation object',
properties: {
id: { type: 'string', description: 'Unique identifier for the conversation' },
type: { type: 'string', description: 'Object type (conversation)' },
state: { type: 'string', description: 'State of the conversation (open)' },
open: { type: 'boolean', description: 'Whether the conversation is open (true)' },
read: { type: 'boolean', description: 'Whether the conversation has been read' },
created_at: { type: 'number', description: 'Unix timestamp when conversation was created' },
updated_at: {
type: 'number',
description: 'Unix timestamp when conversation was last updated',
},
},
},
conversationId: { type: 'string', description: 'ID of the opened conversation' },
state: { type: 'string', description: 'State of the conversation (open)' },
},
}

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomReplyConversation')
@@ -31,7 +31,7 @@ const replyConversationBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
visibility: 'hidden',
description: 'Intercom API access token',
},
conversationId: {

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomSearchContacts')
@@ -87,7 +87,7 @@ const searchContactsBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
visibility: 'hidden',
description: 'Intercom API access token',
},
query: {

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomSearchConversations')
@@ -41,7 +41,7 @@ const searchConversationsBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
visibility: 'hidden',
description: 'Intercom API access token',
},
query: {

View File

@@ -1,124 +0,0 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomSnoozeConversationParams {
accessToken: string
conversationId: string
admin_id: string
snoozed_until: number
}
export interface IntercomSnoozeConversationV2Response {
success: boolean
output: {
conversation: any
conversationId: string
state: string
snoozed_until: number | null
}
}
const snoozeConversationBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
conversationId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the conversation to snooze',
},
admin_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the admin performing the action',
},
snoozed_until: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Unix timestamp for when the conversation should reopen',
},
},
request: {
url: (params: IntercomSnoozeConversationParams) =>
buildIntercomUrl(`/conversations/${params.conversationId}/reply`),
method: 'POST',
headers: (params: IntercomSnoozeConversationParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomSnoozeConversationParams) => ({
message_type: 'snoozed',
admin_id: params.admin_id,
snoozed_until: params.snoozed_until,
}),
},
} satisfies Pick<ToolConfig<IntercomSnoozeConversationParams, any>, 'params' | 'request'>
export const intercomSnoozeConversationV2Tool: ToolConfig<
IntercomSnoozeConversationParams,
IntercomSnoozeConversationV2Response
> = {
...snoozeConversationBase,
id: 'intercom_snooze_conversation_v2',
name: 'Snooze Conversation in Intercom',
description: 'Snooze a conversation to reopen at a future time',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'snooze_conversation')
}
const data = await response.json()
return {
success: true,
output: {
conversation: data,
conversationId: data.id,
state: data.state ?? 'snoozed',
snoozed_until: data.snoozed_until ?? null,
},
}
},
outputs: {
conversation: {
type: 'object',
description: 'The snoozed conversation object',
properties: {
id: { type: 'string', description: 'Unique identifier for the conversation' },
type: { type: 'string', description: 'Object type (conversation)' },
state: { type: 'string', description: 'State of the conversation (snoozed)' },
open: { type: 'boolean', description: 'Whether the conversation is open' },
snoozed_until: {
type: 'number',
description: 'Unix timestamp when conversation will reopen',
optional: true,
},
created_at: { type: 'number', description: 'Unix timestamp when conversation was created' },
updated_at: {
type: 'number',
description: 'Unix timestamp when conversation was last updated',
},
},
},
conversationId: { type: 'string', description: 'ID of the snoozed conversation' },
state: { type: 'string', description: 'State of the conversation (snoozed)' },
snoozed_until: {
type: 'number',
description: 'Unix timestamp when conversation will reopen',
optional: true,
},
},
}

View File

@@ -1,89 +0,0 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomTagContactParams {
accessToken: string
contactId: string
tagId: string
}
export interface IntercomTagContactV2Response {
success: boolean
output: {
id: string
name: string
type: string
}
}
const tagContactBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
contactId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the contact to tag',
},
tagId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the tag to apply',
},
},
request: {
url: (params: IntercomTagContactParams) =>
buildIntercomUrl(`/contacts/${params.contactId}/tags`),
method: 'POST',
headers: (params: IntercomTagContactParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomTagContactParams) => ({
id: params.tagId,
}),
},
} satisfies Pick<ToolConfig<IntercomTagContactParams, any>, 'params' | 'request'>
export const intercomTagContactV2Tool: ToolConfig<
IntercomTagContactParams,
IntercomTagContactV2Response
> = {
...tagContactBase,
id: 'intercom_tag_contact_v2',
name: 'Tag Contact in Intercom',
description: 'Add a tag to a specific contact',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'tag_contact')
}
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
type: data.type ?? 'tag',
},
}
},
outputs: {
id: { type: 'string', description: 'Unique identifier for the tag' },
name: { type: 'string', description: 'Name of the tag' },
type: { type: 'string', description: 'Object type (tag)' },
},
}

View File

@@ -1,97 +0,0 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomTagConversationParams {
accessToken: string
conversationId: string
tagId: string
admin_id: string
}
export interface IntercomTagConversationV2Response {
success: boolean
output: {
id: string
name: string
type: string
}
}
const tagConversationBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
conversationId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the conversation to tag',
},
tagId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the tag to apply',
},
admin_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the admin applying the tag',
},
},
request: {
url: (params: IntercomTagConversationParams) =>
buildIntercomUrl(`/conversations/${params.conversationId}/tags`),
method: 'POST',
headers: (params: IntercomTagConversationParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomTagConversationParams) => ({
id: params.tagId,
admin_id: params.admin_id,
}),
},
} satisfies Pick<ToolConfig<IntercomTagConversationParams, any>, 'params' | 'request'>
export const intercomTagConversationV2Tool: ToolConfig<
IntercomTagConversationParams,
IntercomTagConversationV2Response
> = {
...tagConversationBase,
id: 'intercom_tag_conversation_v2',
name: 'Tag Conversation in Intercom',
description: 'Add a tag to a specific conversation',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'tag_conversation')
}
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
type: data.type ?? 'tag',
},
}
},
outputs: {
id: { type: 'string', description: 'Unique identifier for the tag' },
name: { type: 'string', description: 'Name of the tag' },
type: { type: 'string', description: 'Object type (tag)' },
},
}

View File

@@ -2,13 +2,14 @@ import { createLogger } from '@sim/logger'
const logger = createLogger('Intercom')
// Base params for Intercom API
export interface IntercomBaseParams {
accessToken: string
accessToken: string // OAuth token or API token (hidden)
}
export interface IntercomPaginationParams {
per_page?: number
starting_after?: string
starting_after?: string // Cursor for pagination
}
export interface IntercomPagingInfo {
@@ -32,10 +33,12 @@ export interface IntercomResponse<T> {
}
}
// Helper function to build Intercom API URLs
export function buildIntercomUrl(path: string): string {
return `https://api.intercom.io${path}`
}
// Helper function for consistent error handling
export function handleIntercomError(data: any, status: number, operation: string): never {
logger.error(`Intercom API request failed for ${operation}`, { data, status })

View File

@@ -1,87 +0,0 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomUntagContactParams {
accessToken: string
contactId: string
tagId: string
}
export interface IntercomUntagContactV2Response {
success: boolean
output: {
id: string
name: string
type: string
}
}
const untagContactBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
contactId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the contact to untag',
},
tagId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the tag to remove',
},
},
request: {
url: (params: IntercomUntagContactParams) =>
buildIntercomUrl(`/contacts/${params.contactId}/tags/${params.tagId}`),
method: 'DELETE',
headers: (params: IntercomUntagContactParams) => ({
Authorization: `Bearer ${params.accessToken}`,
Accept: 'application/json',
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
},
} satisfies Pick<ToolConfig<IntercomUntagContactParams, any>, 'params' | 'request'>
export const intercomUntagContactV2Tool: ToolConfig<
IntercomUntagContactParams,
IntercomUntagContactV2Response
> = {
...untagContactBase,
id: 'intercom_untag_contact_v2',
name: 'Untag Contact in Intercom',
description: 'Remove a tag from a specific contact',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'untag_contact')
}
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
type: data.type ?? 'tag',
},
}
},
outputs: {
id: { type: 'string', description: 'Unique identifier for the tag that was removed' },
name: { type: 'string', description: 'Name of the tag that was removed' },
type: { type: 'string', description: 'Object type (tag)' },
},
}

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomUpdateContact')
@@ -38,7 +38,7 @@ const intercomUpdateContactBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
visibility: 'hidden',
description: 'Intercom API access token',
},
contactId: {

View File

@@ -1,193 +0,0 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('IntercomUpdateTicket')
export interface IntercomUpdateTicketParams {
accessToken: string
ticketId: string
ticket_attributes?: string
open?: boolean
is_shared?: boolean
snoozed_until?: number
admin_id?: string
assignee_id?: string
}
export interface IntercomUpdateTicketV2Response {
success: boolean
output: {
ticket: any
ticketId: string
ticket_state: string
}
}
const updateTicketBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
ticketId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the ticket to update',
},
ticket_attributes: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'JSON object with ticket attributes (e.g., {"_default_title_":"New Title","_default_description_":"Updated description"})',
},
open: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Set to false to close the ticket, true to keep it open',
},
is_shared: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether the ticket is visible to users',
},
snoozed_until: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Unix timestamp for when the ticket should reopen',
},
admin_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'The ID of the admin performing the update (needed for workflows and attribution)',
},
assignee_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The ID of the admin or team to assign the ticket to. Set to "0" to unassign.',
},
},
request: {
url: (params: IntercomUpdateTicketParams) => buildIntercomUrl(`/tickets/${params.ticketId}`),
method: 'PUT',
headers: (params: IntercomUpdateTicketParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomUpdateTicketParams) => {
const payload: any = {}
if (params.ticket_attributes) {
try {
payload.ticket_attributes = JSON.parse(params.ticket_attributes)
} catch (error) {
logger.error('Failed to parse ticket_attributes', { error })
throw new Error('ticket_attributes must be a valid JSON object')
}
}
if (params.open !== undefined) {
payload.open = params.open
}
if (params.is_shared !== undefined) {
payload.is_shared = params.is_shared
}
if (params.snoozed_until !== undefined) {
payload.snoozed_until = params.snoozed_until
}
if (params.admin_id) {
payload.admin_id = params.admin_id
}
if (params.assignee_id) {
payload.assignee_id = params.assignee_id
}
return payload
},
},
} satisfies Pick<ToolConfig<IntercomUpdateTicketParams, any>, 'params' | 'request'>
export const intercomUpdateTicketV2Tool: ToolConfig<
IntercomUpdateTicketParams,
IntercomUpdateTicketV2Response
> = {
...updateTicketBase,
id: 'intercom_update_ticket_v2',
name: 'Update Ticket in Intercom',
description: 'Update a ticket in Intercom (change state, assignment, attributes)',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'update_ticket')
}
const data = await response.json()
return {
success: true,
output: {
ticket: {
id: data.id,
type: data.type ?? 'ticket',
ticket_id: data.ticket_id ?? null,
ticket_state: data.ticket_state ?? null,
ticket_attributes: data.ticket_attributes ?? null,
open: data.open ?? null,
is_shared: data.is_shared ?? null,
snoozed_until: data.snoozed_until ?? null,
admin_assignee_id: data.admin_assignee_id ?? null,
team_assignee_id: data.team_assignee_id ?? null,
created_at: data.created_at ?? null,
updated_at: data.updated_at ?? null,
},
ticketId: data.id,
ticket_state: data.ticket_state ?? null,
},
}
},
outputs: {
ticket: {
type: 'object',
description: 'The updated ticket object',
properties: {
id: { type: 'string', description: 'Unique identifier for the ticket' },
type: { type: 'string', description: 'Object type (ticket)' },
ticket_id: { type: 'string', description: 'Ticket ID shown in Intercom UI' },
ticket_state: { type: 'string', description: 'State of the ticket' },
ticket_attributes: { type: 'object', description: 'Attributes of the ticket' },
open: { type: 'boolean', description: 'Whether the ticket is open' },
is_shared: { type: 'boolean', description: 'Whether the ticket is visible to users' },
snoozed_until: {
type: 'number',
description: 'Unix timestamp when ticket will reopen',
optional: true,
},
admin_assignee_id: { type: 'string', description: 'ID of assigned admin', optional: true },
team_assignee_id: { type: 'string', description: 'ID of assigned team', optional: true },
created_at: { type: 'number', description: 'Unix timestamp when ticket was created' },
updated_at: { type: 'number', description: 'Unix timestamp when ticket was last updated' },
},
},
ticketId: { type: 'string', description: 'ID of the updated ticket' },
ticket_state: { type: 'string', description: 'Current state of the ticket' },
},
}

View File

@@ -158,300 +158,3 @@ export const kalshiAmendOrderTool: ToolConfig<KalshiAmendOrderParams, KalshiAmen
},
},
}
export interface KalshiAmendOrderV2Params extends KalshiAuthParams {
orderId: string // Order ID to amend (required)
ticker: string // Market ticker (required)
side: string // 'yes' or 'no' (required)
action: string // 'buy' or 'sell' (required)
clientOrderId?: string // Original client order ID (optional in V2)
updatedClientOrderId?: string // New client order ID (optional in V2)
count?: string // Updated quantity
yesPrice?: string // Updated yes price in cents (1-99)
noPrice?: string // Updated no price in cents (1-99)
yesPriceDollars?: string // Updated yes price in dollars
noPriceDollars?: string // Updated no price in dollars
countFp?: string // Count in fixed-point for fractional contracts
}
export interface KalshiAmendOrderV2Order {
order_id: string
user_id: string | null
ticker: string
event_ticker: string
status: string
side: string
type: string
yes_price: number | null
no_price: number | null
action: string
count: number
remaining_count: number
created_time: string
expiration_time: string | null
order_group_id: string | null
client_order_id: string | null
place_count: number | null
decrease_count: number | null
queue_position: number | null
maker_fill_count: number | null
taker_fill_count: number | null
maker_fees: number | null
taker_fees: number | null
last_update_time: string | null
take_profit_order_id: string | null
stop_loss_order_id: string | null
amend_count: number | null
amend_taker_fill_count: number | null
}
export interface KalshiAmendOrderV2Response {
success: boolean
output: {
old_order: KalshiAmendOrderV2Order
order: KalshiAmendOrderV2Order
}
}
export const kalshiAmendOrderV2Tool: ToolConfig<
KalshiAmendOrderV2Params,
KalshiAmendOrderV2Response
> = {
id: 'kalshi_amend_order_v2',
name: 'Amend Order on Kalshi V2',
description:
'Modify the price or quantity of an existing order on Kalshi (V2 with full API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
orderId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The order ID to amend',
},
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker',
},
side: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: "Side of the order: 'yes' or 'no'",
},
action: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: "Action type: 'buy' or 'sell'",
},
clientOrderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The original client-specified order ID',
},
updatedClientOrderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The new client-specified order ID after amendment',
},
count: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated quantity for the order',
},
yesPrice: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated yes price in cents (1-99)',
},
noPrice: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated no price in cents (1-99)',
},
yesPriceDollars: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated yes price in dollars (e.g., "0.56")',
},
noPriceDollars: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated no price in dollars (e.g., "0.56")',
},
countFp: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Count in fixed-point for fractional contracts',
},
},
request: {
url: (params) => buildKalshiUrl(`/portfolio/orders/${params.orderId}/amend`),
method: 'POST',
headers: (params) => {
const path = `/trade-api/v2/portfolio/orders/${params.orderId}/amend`
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'POST', path)
},
body: (params) => {
const body: Record<string, any> = {
ticker: params.ticker,
side: params.side.toLowerCase(),
action: params.action.toLowerCase(),
}
if (params.clientOrderId) body.client_order_id = params.clientOrderId
if (params.updatedClientOrderId) body.updated_client_order_id = params.updatedClientOrderId
if (params.count) body.count = Number.parseInt(params.count, 10)
if (params.yesPrice) body.yes_price = Number.parseInt(params.yesPrice, 10)
if (params.noPrice) body.no_price = Number.parseInt(params.noPrice, 10)
if (params.yesPriceDollars) body.yes_price_dollars = params.yesPriceDollars
if (params.noPriceDollars) body.no_price_dollars = params.noPriceDollars
if (params.countFp) body.count_fp = params.countFp
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'amend_order_v2')
}
const mapOrder = (order: any): KalshiAmendOrderV2Order => ({
order_id: order.order_id ?? null,
user_id: order.user_id ?? null,
ticker: order.ticker ?? null,
event_ticker: order.event_ticker ?? null,
status: order.status ?? null,
side: order.side ?? null,
type: order.type ?? null,
yes_price: order.yes_price ?? null,
no_price: order.no_price ?? null,
action: order.action ?? null,
count: order.count ?? null,
remaining_count: order.remaining_count ?? null,
created_time: order.created_time ?? null,
expiration_time: order.expiration_time ?? null,
order_group_id: order.order_group_id ?? null,
client_order_id: order.client_order_id ?? null,
place_count: order.place_count ?? null,
decrease_count: order.decrease_count ?? null,
queue_position: order.queue_position ?? null,
maker_fill_count: order.maker_fill_count ?? null,
taker_fill_count: order.taker_fill_count ?? null,
maker_fees: order.maker_fees ?? null,
taker_fees: order.taker_fees ?? null,
last_update_time: order.last_update_time ?? null,
take_profit_order_id: order.take_profit_order_id ?? null,
stop_loss_order_id: order.stop_loss_order_id ?? null,
amend_count: order.amend_count ?? null,
amend_taker_fill_count: order.amend_taker_fill_count ?? null,
})
return {
success: true,
output: {
old_order: mapOrder(data.old_order || {}),
order: mapOrder(data.order || {}),
},
}
},
outputs: {
old_order: {
type: 'object',
description: 'The original order object before amendment',
properties: {
order_id: { type: 'string', description: 'Order ID' },
user_id: { type: 'string', description: 'User ID' },
ticker: { type: 'string', description: 'Market ticker' },
event_ticker: { type: 'string', description: 'Event ticker' },
status: { type: 'string', description: 'Order status' },
side: { type: 'string', description: 'Order side (yes/no)' },
type: { type: 'string', description: 'Order type (limit/market)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
action: { type: 'string', description: 'Action (buy/sell)' },
count: { type: 'number', description: 'Number of contracts' },
remaining_count: { type: 'number', description: 'Remaining contracts' },
created_time: { type: 'string', description: 'Order creation time' },
expiration_time: { type: 'string', description: 'Order expiration time' },
order_group_id: { type: 'string', description: 'Order group ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
place_count: { type: 'number', description: 'Place count' },
decrease_count: { type: 'number', description: 'Decrease count' },
queue_position: { type: 'number', description: 'Queue position' },
maker_fill_count: { type: 'number', description: 'Maker fill count' },
taker_fill_count: { type: 'number', description: 'Taker fill count' },
maker_fees: { type: 'number', description: 'Maker fees' },
taker_fees: { type: 'number', description: 'Taker fees' },
last_update_time: { type: 'string', description: 'Last update time' },
take_profit_order_id: { type: 'string', description: 'Take profit order ID' },
stop_loss_order_id: { type: 'string', description: 'Stop loss order ID' },
amend_count: { type: 'number', description: 'Amend count' },
amend_taker_fill_count: { type: 'number', description: 'Amend taker fill count' },
},
},
order: {
type: 'object',
description: 'The amended order object with full API response fields',
properties: {
order_id: { type: 'string', description: 'Order ID' },
user_id: { type: 'string', description: 'User ID' },
ticker: { type: 'string', description: 'Market ticker' },
event_ticker: { type: 'string', description: 'Event ticker' },
status: { type: 'string', description: 'Order status' },
side: { type: 'string', description: 'Order side (yes/no)' },
type: { type: 'string', description: 'Order type (limit/market)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
action: { type: 'string', description: 'Action (buy/sell)' },
count: { type: 'number', description: 'Number of contracts' },
remaining_count: { type: 'number', description: 'Remaining contracts' },
created_time: { type: 'string', description: 'Order creation time' },
expiration_time: { type: 'string', description: 'Order expiration time' },
order_group_id: { type: 'string', description: 'Order group ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
place_count: { type: 'number', description: 'Place count' },
decrease_count: { type: 'number', description: 'Decrease count' },
queue_position: { type: 'number', description: 'Queue position' },
maker_fill_count: { type: 'number', description: 'Maker fill count' },
taker_fill_count: { type: 'number', description: 'Taker fill count' },
maker_fees: { type: 'number', description: 'Maker fees' },
taker_fees: { type: 'number', description: 'Taker fees' },
last_update_time: { type: 'string', description: 'Last update time' },
take_profit_order_id: { type: 'string', description: 'Take profit order ID' },
stop_loss_order_id: { type: 'string', description: 'Stop loss order ID' },
amend_count: { type: 'number', description: 'Amend count' },
amend_taker_fill_count: { type: 'number', description: 'Amend taker fill count' },
},
},
},
}

View File

@@ -78,193 +78,3 @@ export const kalshiCancelOrderTool: ToolConfig<KalshiCancelOrderParams, KalshiCa
},
},
}
export interface KalshiCancelOrderV2Params extends KalshiAuthParams {
orderId: string // Order ID to cancel (required)
}
export interface KalshiCancelOrderV2Response {
success: boolean
output: {
order: {
order_id: string
user_id: string | null
client_order_id: string | null
ticker: string
side: string
action: string
type: string
status: string
yes_price: number | null
no_price: number | null
yes_price_dollars: string | null
no_price_dollars: string | null
fill_count: number | null
fill_count_fp: string | null
remaining_count: number | null
remaining_count_fp: string | null
initial_count: number | null
initial_count_fp: string | null
taker_fees: number | null
maker_fees: number | null
taker_fees_dollars: string | null
maker_fees_dollars: string | null
taker_fill_cost: number | null
maker_fill_cost: number | null
taker_fill_cost_dollars: string | null
maker_fill_cost_dollars: string | null
queue_position: number | null
expiration_time: string | null
created_time: string | null
last_update_time: string | null
self_trade_prevention_type: string | null
order_group_id: string | null
cancel_order_on_pause: boolean | null
}
reduced_by: number
reduced_by_fp: string | null
}
}
export const kalshiCancelOrderV2Tool: ToolConfig<
KalshiCancelOrderV2Params,
KalshiCancelOrderV2Response
> = {
id: 'kalshi_cancel_order_v2',
name: 'Cancel Order on Kalshi V2',
description: 'Cancel an existing order on Kalshi (V2 with full API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
orderId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The order ID to cancel',
},
},
request: {
url: (params) => buildKalshiUrl(`/portfolio/orders/${params.orderId}`),
method: 'DELETE',
headers: (params) => {
const path = `/trade-api/v2/portfolio/orders/${params.orderId}`
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'DELETE', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'cancel_order_v2')
}
const order = data.order || {}
return {
success: true,
output: {
order: {
order_id: order.order_id ?? null,
user_id: order.user_id ?? null,
client_order_id: order.client_order_id ?? null,
ticker: order.ticker ?? null,
side: order.side ?? null,
action: order.action ?? null,
type: order.type ?? null,
status: order.status ?? null,
yes_price: order.yes_price ?? null,
no_price: order.no_price ?? null,
yes_price_dollars: order.yes_price_dollars ?? null,
no_price_dollars: order.no_price_dollars ?? null,
fill_count: order.fill_count ?? null,
fill_count_fp: order.fill_count_fp ?? null,
remaining_count: order.remaining_count ?? null,
remaining_count_fp: order.remaining_count_fp ?? null,
initial_count: order.initial_count ?? null,
initial_count_fp: order.initial_count_fp ?? null,
taker_fees: order.taker_fees ?? null,
maker_fees: order.maker_fees ?? null,
taker_fees_dollars: order.taker_fees_dollars ?? null,
maker_fees_dollars: order.maker_fees_dollars ?? null,
taker_fill_cost: order.taker_fill_cost ?? null,
maker_fill_cost: order.maker_fill_cost ?? null,
taker_fill_cost_dollars: order.taker_fill_cost_dollars ?? null,
maker_fill_cost_dollars: order.maker_fill_cost_dollars ?? null,
queue_position: order.queue_position ?? null,
expiration_time: order.expiration_time ?? null,
created_time: order.created_time ?? null,
last_update_time: order.last_update_time ?? null,
self_trade_prevention_type: order.self_trade_prevention_type ?? null,
order_group_id: order.order_group_id ?? null,
cancel_order_on_pause: order.cancel_order_on_pause ?? null,
},
reduced_by: data.reduced_by ?? 0,
reduced_by_fp: data.reduced_by_fp ?? null,
},
}
},
outputs: {
order: {
type: 'object',
description: 'The canceled order object with full API response fields',
properties: {
order_id: { type: 'string', description: 'Order ID' },
user_id: { type: 'string', description: 'User ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
ticker: { type: 'string', description: 'Market ticker' },
side: { type: 'string', description: 'Order side (yes/no)' },
action: { type: 'string', description: 'Action (buy/sell)' },
type: { type: 'string', description: 'Order type (limit/market)' },
status: { type: 'string', description: 'Order status (resting/canceled/executed)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
yes_price_dollars: { type: 'string', description: 'Yes price in dollars' },
no_price_dollars: { type: 'string', description: 'No price in dollars' },
fill_count: { type: 'number', description: 'Filled contract count' },
fill_count_fp: { type: 'string', description: 'Filled count (fixed-point)' },
remaining_count: { type: 'number', description: 'Remaining contracts' },
remaining_count_fp: { type: 'string', description: 'Remaining count (fixed-point)' },
initial_count: { type: 'number', description: 'Initial contract count' },
initial_count_fp: { type: 'string', description: 'Initial count (fixed-point)' },
taker_fees: { type: 'number', description: 'Taker fees in cents' },
maker_fees: { type: 'number', description: 'Maker fees in cents' },
taker_fees_dollars: { type: 'string', description: 'Taker fees in dollars' },
maker_fees_dollars: { type: 'string', description: 'Maker fees in dollars' },
taker_fill_cost: { type: 'number', description: 'Taker fill cost in cents' },
maker_fill_cost: { type: 'number', description: 'Maker fill cost in cents' },
taker_fill_cost_dollars: { type: 'string', description: 'Taker fill cost in dollars' },
maker_fill_cost_dollars: { type: 'string', description: 'Maker fill cost in dollars' },
queue_position: { type: 'number', description: 'Queue position (deprecated)' },
expiration_time: { type: 'string', description: 'Order expiration time' },
created_time: { type: 'string', description: 'Order creation time' },
last_update_time: { type: 'string', description: 'Last update time' },
self_trade_prevention_type: { type: 'string', description: 'Self-trade prevention type' },
order_group_id: { type: 'string', description: 'Order group ID' },
cancel_order_on_pause: { type: 'boolean', description: 'Cancel on market pause' },
},
},
reduced_by: {
type: 'number',
description: 'Number of contracts canceled',
},
reduced_by_fp: {
type: 'string',
description: 'Number of contracts canceled in fixed-point format',
},
},
}

View File

@@ -209,344 +209,3 @@ export const kalshiCreateOrderTool: ToolConfig<KalshiCreateOrderParams, KalshiCr
},
},
}
export interface KalshiCreateOrderV2Params extends KalshiAuthParams {
ticker: string // Market ticker (required)
side: string // 'yes' or 'no' (required)
action: string // 'buy' or 'sell' (required)
count?: string // Number of contracts (optional - provide count or countFp)
type?: string // 'limit' or 'market' (default: limit)
yesPrice?: string // Yes price in cents (1-99)
noPrice?: string // No price in cents (1-99)
yesPriceDollars?: string // Yes price in dollars (e.g., "0.56")
noPriceDollars?: string // No price in dollars (e.g., "0.56")
clientOrderId?: string // Custom order identifier
expirationTs?: string // Unix timestamp expiration
timeInForce?: string // 'fill_or_kill', 'good_till_canceled', 'immediate_or_cancel'
buyMaxCost?: string // Maximum cost in cents
postOnly?: string // 'true' or 'false' - maker-only orders
reduceOnly?: string // 'true' or 'false' - position reduction only
selfTradePreventionType?: string // 'taker_at_cross' or 'maker'
orderGroupId?: string // Associated order group
countFp?: string // Count in fixed-point (for fractional contracts)
cancelOrderOnPause?: string // 'true' or 'false' - cancel on market pause
subaccount?: string // Subaccount to use for the order
}
export interface KalshiCreateOrderV2Response {
success: boolean
output: {
order: {
order_id: string
user_id: string | null
client_order_id: string | null
ticker: string
side: string
action: string
type: string
status: string
yes_price: number | null
no_price: number | null
yes_price_dollars: string | null
no_price_dollars: string | null
fill_count: number | null
fill_count_fp: string | null
remaining_count: number | null
remaining_count_fp: string | null
initial_count: number | null
initial_count_fp: string | null
taker_fees: number | null
maker_fees: number | null
taker_fees_dollars: string | null
maker_fees_dollars: string | null
taker_fill_cost: number | null
maker_fill_cost: number | null
taker_fill_cost_dollars: string | null
maker_fill_cost_dollars: string | null
queue_position: number | null
expiration_time: string | null
created_time: string | null
last_update_time: string | null
self_trade_prevention_type: string | null
order_group_id: string | null
cancel_order_on_pause: boolean | null
}
}
}
export const kalshiCreateOrderV2Tool: ToolConfig<
KalshiCreateOrderV2Params,
KalshiCreateOrderV2Response
> = {
id: 'kalshi_create_order_v2',
name: 'Create Order on Kalshi V2',
description: 'Create a new order on a Kalshi prediction market (V2 with full API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker (e.g., KXBTC-24DEC31)',
},
side: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: "Side of the order: 'yes' or 'no'",
},
action: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: "Action type: 'buy' or 'sell'",
},
count: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of contracts (provide count or countFp)',
},
type: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Order type: 'limit' or 'market' (default: limit)",
},
yesPrice: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Yes price in cents (1-99)',
},
noPrice: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'No price in cents (1-99)',
},
yesPriceDollars: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Yes price in dollars (e.g., "0.56")',
},
noPriceDollars: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'No price in dollars (e.g., "0.56")',
},
clientOrderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Custom order identifier',
},
expirationTs: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Unix timestamp for order expiration',
},
timeInForce: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Time in force: 'fill_or_kill', 'good_till_canceled', 'immediate_or_cancel'",
},
buyMaxCost: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Maximum cost in cents (auto-enables fill_or_kill)',
},
postOnly: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Set to 'true' for maker-only orders",
},
reduceOnly: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Set to 'true' for position reduction only",
},
selfTradePreventionType: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Self-trade prevention: 'taker_at_cross' or 'maker'",
},
orderGroupId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Associated order group ID',
},
countFp: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Count in fixed-point for fractional contracts',
},
cancelOrderOnPause: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Set to 'true' to cancel order on market pause",
},
subaccount: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Subaccount to use for the order',
},
},
request: {
url: () => buildKalshiUrl('/portfolio/orders'),
method: 'POST',
headers: (params) => {
const path = '/trade-api/v2/portfolio/orders'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'POST', path)
},
body: (params) => {
const body: Record<string, any> = {
ticker: params.ticker,
side: params.side.toLowerCase(),
action: params.action.toLowerCase(),
}
// count or count_fp must be provided (but not both required)
if (params.count) body.count = Number.parseInt(params.count, 10)
if (params.countFp) body.count_fp = params.countFp
if (params.type) body.type = params.type.toLowerCase()
if (params.yesPrice) body.yes_price = Number.parseInt(params.yesPrice, 10)
if (params.noPrice) body.no_price = Number.parseInt(params.noPrice, 10)
if (params.yesPriceDollars) body.yes_price_dollars = params.yesPriceDollars
if (params.noPriceDollars) body.no_price_dollars = params.noPriceDollars
if (params.clientOrderId) body.client_order_id = params.clientOrderId
if (params.expirationTs) body.expiration_ts = Number.parseInt(params.expirationTs, 10)
if (params.timeInForce) body.time_in_force = params.timeInForce
if (params.buyMaxCost) body.buy_max_cost = Number.parseInt(params.buyMaxCost, 10)
if (params.postOnly) body.post_only = params.postOnly === 'true'
if (params.reduceOnly) body.reduce_only = params.reduceOnly === 'true'
if (params.selfTradePreventionType)
body.self_trade_prevention_type = params.selfTradePreventionType
if (params.orderGroupId) body.order_group_id = params.orderGroupId
if (params.cancelOrderOnPause)
body.cancel_order_on_pause = params.cancelOrderOnPause === 'true'
if (params.subaccount) body.subaccount = params.subaccount
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'create_order_v2')
}
const order = data.order || {}
return {
success: true,
output: {
order: {
order_id: order.order_id ?? null,
user_id: order.user_id ?? null,
client_order_id: order.client_order_id ?? null,
ticker: order.ticker ?? null,
side: order.side ?? null,
action: order.action ?? null,
type: order.type ?? null,
status: order.status ?? null,
yes_price: order.yes_price ?? null,
no_price: order.no_price ?? null,
yes_price_dollars: order.yes_price_dollars ?? null,
no_price_dollars: order.no_price_dollars ?? null,
fill_count: order.fill_count ?? null,
fill_count_fp: order.fill_count_fp ?? null,
remaining_count: order.remaining_count ?? null,
remaining_count_fp: order.remaining_count_fp ?? null,
initial_count: order.initial_count ?? null,
initial_count_fp: order.initial_count_fp ?? null,
taker_fees: order.taker_fees ?? null,
maker_fees: order.maker_fees ?? null,
taker_fees_dollars: order.taker_fees_dollars ?? null,
maker_fees_dollars: order.maker_fees_dollars ?? null,
taker_fill_cost: order.taker_fill_cost ?? null,
maker_fill_cost: order.maker_fill_cost ?? null,
taker_fill_cost_dollars: order.taker_fill_cost_dollars ?? null,
maker_fill_cost_dollars: order.maker_fill_cost_dollars ?? null,
queue_position: order.queue_position ?? null,
expiration_time: order.expiration_time ?? null,
created_time: order.created_time ?? null,
last_update_time: order.last_update_time ?? null,
self_trade_prevention_type: order.self_trade_prevention_type ?? null,
order_group_id: order.order_group_id ?? null,
cancel_order_on_pause: order.cancel_order_on_pause ?? null,
},
},
}
},
outputs: {
order: {
type: 'object',
description: 'The created order object with full API response fields',
properties: {
order_id: { type: 'string', description: 'Order ID' },
user_id: { type: 'string', description: 'User ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
ticker: { type: 'string', description: 'Market ticker' },
side: { type: 'string', description: 'Order side (yes/no)' },
action: { type: 'string', description: 'Action (buy/sell)' },
type: { type: 'string', description: 'Order type (limit/market)' },
status: { type: 'string', description: 'Order status (resting/canceled/executed)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
yes_price_dollars: { type: 'string', description: 'Yes price in dollars' },
no_price_dollars: { type: 'string', description: 'No price in dollars' },
fill_count: { type: 'number', description: 'Filled contract count' },
fill_count_fp: { type: 'string', description: 'Filled count (fixed-point)' },
remaining_count: { type: 'number', description: 'Remaining contracts' },
remaining_count_fp: { type: 'string', description: 'Remaining count (fixed-point)' },
initial_count: { type: 'number', description: 'Initial contract count' },
initial_count_fp: { type: 'string', description: 'Initial count (fixed-point)' },
taker_fees: { type: 'number', description: 'Taker fees in cents' },
maker_fees: { type: 'number', description: 'Maker fees in cents' },
taker_fees_dollars: { type: 'string', description: 'Taker fees in dollars' },
maker_fees_dollars: { type: 'string', description: 'Maker fees in dollars' },
taker_fill_cost: { type: 'number', description: 'Taker fill cost in cents' },
maker_fill_cost: { type: 'number', description: 'Maker fill cost in cents' },
taker_fill_cost_dollars: { type: 'string', description: 'Taker fill cost in dollars' },
maker_fill_cost_dollars: { type: 'string', description: 'Maker fill cost in dollars' },
queue_position: { type: 'number', description: 'Queue position (deprecated)' },
expiration_time: { type: 'string', description: 'Order expiration time' },
created_time: { type: 'string', description: 'Order creation time' },
last_update_time: { type: 'string', description: 'Last update time' },
self_trade_prevention_type: { type: 'string', description: 'Self-trade prevention type' },
order_group_id: { type: 'string', description: 'Order group ID' },
cancel_order_on_pause: { type: 'boolean', description: 'Cancel on market pause' },
},
},
},
}

View File

@@ -66,78 +66,3 @@ export const kalshiGetBalanceTool: ToolConfig<KalshiGetBalanceParams, KalshiGetB
portfolioValue: { type: 'number', description: 'Portfolio value in cents' },
},
}
/**
* V2 Params for Get Balance
*/
export interface KalshiGetBalanceV2Params extends KalshiAuthParams {}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetBalanceV2Response {
success: boolean
output: {
balance: number
portfolio_value: number
updated_ts: number | null
}
}
export const kalshiGetBalanceV2Tool: ToolConfig<
KalshiGetBalanceV2Params,
KalshiGetBalanceV2Response
> = {
id: 'kalshi_get_balance_v2',
name: 'Get Balance from Kalshi V2',
description:
'Retrieve your account balance and portfolio value from Kalshi (V2 - exact API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
},
request: {
url: () => buildKalshiUrl('/portfolio/balance'),
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/balance'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_balance_v2')
}
return {
success: true,
output: {
balance: data.balance ?? 0,
portfolio_value: data.portfolio_value ?? 0,
updated_ts: data.updated_ts ?? null,
},
}
},
outputs: {
balance: { type: 'number', description: 'Account balance in cents' },
portfolio_value: { type: 'number', description: 'Portfolio value in cents' },
updated_ts: { type: 'number', description: 'Unix timestamp of last update (milliseconds)' },
},
}

View File

@@ -102,246 +102,3 @@ export const kalshiGetCandlesticksTool: ToolConfig<
},
},
}
/**
* BidAskDistribution - OHLC data for yes_bid and yes_ask
*/
export interface BidAskDistribution {
open: number | null
open_dollars: string | null
low: number | null
low_dollars: string | null
high: number | null
high_dollars: string | null
close: number | null
close_dollars: string | null
}
/**
* PriceDistribution - Extended OHLC data for price field
*/
export interface PriceDistribution {
open: number | null
open_dollars: string | null
low: number | null
low_dollars: string | null
high: number | null
high_dollars: string | null
close: number | null
close_dollars: string | null
mean: number | null
mean_dollars: string | null
previous: number | null
previous_dollars: string | null
min: number | null
min_dollars: string | null
max: number | null
max_dollars: string | null
}
/**
* V2 Get Candlesticks Tool - Returns exact Kalshi API response structure
*/
export interface KalshiGetCandlesticksV2Response {
success: boolean
output: {
ticker: string
candlesticks: Array<{
end_period_ts: number | null
yes_bid: BidAskDistribution
yes_ask: BidAskDistribution
price: PriceDistribution
volume: number | null
volume_fp: string | null
open_interest: number | null
open_interest_fp: string | null
}>
}
}
export const kalshiGetCandlesticksV2Tool: ToolConfig<
KalshiGetCandlesticksParams,
KalshiGetCandlesticksV2Response
> = {
id: 'kalshi_get_candlesticks_v2',
name: 'Get Market Candlesticks from Kalshi V2',
description: 'Retrieve OHLC candlestick data for a specific market (V2 - full API response)',
version: '2.0.0',
params: {
seriesTicker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Series ticker',
},
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker (e.g., KXBTC-24DEC31)',
},
startTs: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Start timestamp (Unix seconds)',
},
endTs: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'End timestamp (Unix seconds)',
},
periodInterval: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Period interval: 1 (1min), 60 (1hour), or 1440 (1day)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
queryParams.append('start_ts', params.startTs.toString())
queryParams.append('end_ts', params.endTs.toString())
queryParams.append('period_interval', params.periodInterval.toString())
const query = queryParams.toString()
const url = buildKalshiUrl(
`/series/${params.seriesTicker}/markets/${params.ticker}/candlesticks`
)
return `${url}?${query}`
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_candlesticks_v2')
}
const mapBidAsk = (obj: Record<string, unknown> | null): BidAskDistribution => ({
open: (obj?.open as number) ?? null,
open_dollars: (obj?.open_dollars as string) ?? null,
low: (obj?.low as number) ?? null,
low_dollars: (obj?.low_dollars as string) ?? null,
high: (obj?.high as number) ?? null,
high_dollars: (obj?.high_dollars as string) ?? null,
close: (obj?.close as number) ?? null,
close_dollars: (obj?.close_dollars as string) ?? null,
})
const mapPrice = (obj: Record<string, unknown> | null): PriceDistribution => ({
open: (obj?.open as number) ?? null,
open_dollars: (obj?.open_dollars as string) ?? null,
low: (obj?.low as number) ?? null,
low_dollars: (obj?.low_dollars as string) ?? null,
high: (obj?.high as number) ?? null,
high_dollars: (obj?.high_dollars as string) ?? null,
close: (obj?.close as number) ?? null,
close_dollars: (obj?.close_dollars as string) ?? null,
mean: (obj?.mean as number) ?? null,
mean_dollars: (obj?.mean_dollars as string) ?? null,
previous: (obj?.previous as number) ?? null,
previous_dollars: (obj?.previous_dollars as string) ?? null,
min: (obj?.min as number) ?? null,
min_dollars: (obj?.min_dollars as string) ?? null,
max: (obj?.max as number) ?? null,
max_dollars: (obj?.max_dollars as string) ?? null,
})
const candlesticks = (data.candlesticks || []).map((c: Record<string, unknown>) => ({
end_period_ts: (c.end_period_ts as number) ?? null,
yes_bid: mapBidAsk(c.yes_bid as Record<string, unknown> | null),
yes_ask: mapBidAsk(c.yes_ask as Record<string, unknown> | null),
price: mapPrice(c.price as Record<string, unknown> | null),
volume: (c.volume as number) ?? null,
volume_fp: (c.volume_fp as string) ?? null,
open_interest: (c.open_interest as number) ?? null,
open_interest_fp: (c.open_interest_fp as string) ?? null,
}))
return {
success: true,
output: {
ticker: data.ticker ?? null,
candlesticks,
},
}
},
outputs: {
ticker: {
type: 'string',
description: 'Market ticker',
},
candlesticks: {
type: 'array',
description: 'Array of OHLC candlestick data with nested bid/ask/price objects',
properties: {
end_period_ts: { type: 'number', description: 'End period timestamp (Unix)' },
yes_bid: {
type: 'object',
description: 'Yes bid OHLC data',
properties: {
open: { type: 'number', description: 'Open price (cents)' },
open_dollars: { type: 'string', description: 'Open price (dollars)' },
low: { type: 'number', description: 'Low price (cents)' },
low_dollars: { type: 'string', description: 'Low price (dollars)' },
high: { type: 'number', description: 'High price (cents)' },
high_dollars: { type: 'string', description: 'High price (dollars)' },
close: { type: 'number', description: 'Close price (cents)' },
close_dollars: { type: 'string', description: 'Close price (dollars)' },
},
},
yes_ask: {
type: 'object',
description: 'Yes ask OHLC data',
properties: {
open: { type: 'number', description: 'Open price (cents)' },
open_dollars: { type: 'string', description: 'Open price (dollars)' },
low: { type: 'number', description: 'Low price (cents)' },
low_dollars: { type: 'string', description: 'Low price (dollars)' },
high: { type: 'number', description: 'High price (cents)' },
high_dollars: { type: 'string', description: 'High price (dollars)' },
close: { type: 'number', description: 'Close price (cents)' },
close_dollars: { type: 'string', description: 'Close price (dollars)' },
},
},
price: {
type: 'object',
description: 'Trade price OHLC data with additional statistics',
properties: {
open: { type: 'number', description: 'Open price (cents)' },
open_dollars: { type: 'string', description: 'Open price (dollars)' },
low: { type: 'number', description: 'Low price (cents)' },
low_dollars: { type: 'string', description: 'Low price (dollars)' },
high: { type: 'number', description: 'High price (cents)' },
high_dollars: { type: 'string', description: 'High price (dollars)' },
close: { type: 'number', description: 'Close price (cents)' },
close_dollars: { type: 'string', description: 'Close price (dollars)' },
mean: { type: 'number', description: 'Mean price (cents)' },
mean_dollars: { type: 'string', description: 'Mean price (dollars)' },
previous: { type: 'number', description: 'Previous price (cents)' },
previous_dollars: { type: 'string', description: 'Previous price (dollars)' },
min: { type: 'number', description: 'Min price (cents)' },
min_dollars: { type: 'string', description: 'Min price (dollars)' },
max: { type: 'number', description: 'Max price (cents)' },
max_dollars: { type: 'string', description: 'Max price (dollars)' },
},
},
volume: { type: 'number', description: 'Volume (contracts)' },
volume_fp: { type: 'string', description: 'Volume (fixed-point string)' },
open_interest: { type: 'number', description: 'Open interest (contracts)' },
open_interest_fp: { type: 'string', description: 'Open interest (fixed-point string)' },
},
},
},
}

View File

@@ -73,179 +73,3 @@ export const kalshiGetEventTool: ToolConfig<KalshiGetEventParams, KalshiGetEvent
},
},
}
/**
* V2 Params for Get Event
*/
export interface KalshiGetEventV2Params {
eventTicker: string
withNestedMarkets?: string
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetEventV2Response {
success: boolean
output: {
event: {
event_ticker: string
series_ticker: string
title: string
sub_title: string | null
mutually_exclusive: boolean
category: string
collateral_return_type: string | null
strike_date: string | null
strike_period: string | null
available_on_brokers: boolean | null
product_metadata: Record<string, unknown> | null
markets: Array<{
ticker: string
event_ticker: string
market_type: string
title: string
subtitle: string | null
yes_sub_title: string | null
no_sub_title: string | null
open_time: string
close_time: string
expiration_time: string
status: string
yes_bid: number
yes_ask: number
no_bid: number
no_ask: number
last_price: number
previous_yes_bid: number | null
previous_yes_ask: number | null
previous_price: number | null
volume: number
volume_24h: number
liquidity: number | null
open_interest: number | null
result: string | null
cap_strike: number | null
floor_strike: number | null
}> | null
}
}
}
export const kalshiGetEventV2Tool: ToolConfig<KalshiGetEventV2Params, KalshiGetEventV2Response> = {
id: 'kalshi_get_event_v2',
name: 'Get Event from Kalshi V2',
description: 'Retrieve details of a specific event by ticker (V2 - exact API response)',
version: '2.0.0',
params: {
eventTicker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The event ticker',
},
withNestedMarkets: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include nested markets in response (true/false)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.withNestedMarkets)
queryParams.append('with_nested_markets', params.withNestedMarkets)
const query = queryParams.toString()
const url = buildKalshiUrl(`/events/${params.eventTicker}`)
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_event_v2')
}
const event = data.event || {}
const markets =
event.markets?.map((m: Record<string, unknown>) => ({
ticker: m.ticker ?? null,
event_ticker: m.event_ticker ?? null,
market_type: m.market_type ?? null,
title: m.title ?? null,
subtitle: m.subtitle ?? null,
yes_sub_title: m.yes_sub_title ?? null,
no_sub_title: m.no_sub_title ?? null,
open_time: m.open_time ?? null,
close_time: m.close_time ?? null,
expiration_time: m.expiration_time ?? null,
status: m.status ?? null,
yes_bid: m.yes_bid ?? 0,
yes_ask: m.yes_ask ?? 0,
no_bid: m.no_bid ?? 0,
no_ask: m.no_ask ?? 0,
last_price: m.last_price ?? 0,
previous_yes_bid: m.previous_yes_bid ?? null,
previous_yes_ask: m.previous_yes_ask ?? null,
previous_price: m.previous_price ?? null,
volume: m.volume ?? 0,
volume_24h: m.volume_24h ?? 0,
liquidity: m.liquidity ?? null,
open_interest: m.open_interest ?? null,
result: m.result ?? null,
cap_strike: m.cap_strike ?? null,
floor_strike: m.floor_strike ?? null,
})) ?? null
return {
success: true,
output: {
event: {
event_ticker: event.event_ticker ?? null,
series_ticker: event.series_ticker ?? null,
title: event.title ?? null,
sub_title: event.sub_title ?? null,
mutually_exclusive: event.mutually_exclusive ?? false,
category: event.category ?? null,
collateral_return_type: event.collateral_return_type ?? null,
strike_date: event.strike_date ?? null,
strike_period: event.strike_period ?? null,
available_on_brokers: event.available_on_brokers ?? null,
product_metadata: event.product_metadata ?? null,
markets,
},
},
}
},
outputs: {
event: {
type: 'object',
description: 'Event object with full details matching Kalshi API response',
properties: {
event_ticker: { type: 'string', description: 'Event ticker' },
series_ticker: { type: 'string', description: 'Series ticker' },
title: { type: 'string', description: 'Event title' },
sub_title: { type: 'string', description: 'Event subtitle' },
mutually_exclusive: { type: 'boolean', description: 'Mutually exclusive markets' },
category: { type: 'string', description: 'Event category' },
collateral_return_type: { type: 'string', description: 'Collateral return type' },
strike_date: { type: 'string', description: 'Strike date' },
strike_period: { type: 'string', description: 'Strike period' },
available_on_brokers: { type: 'boolean', description: 'Available on brokers' },
product_metadata: { type: 'object', description: 'Product metadata' },
markets: { type: 'array', description: 'Nested markets (if requested)' },
},
},
},
}

View File

@@ -106,197 +106,3 @@ export const kalshiGetEventsTool: ToolConfig<KalshiGetEventsParams, KalshiGetEve
},
},
}
/**
* V2 Params for Get Events
*/
export interface KalshiGetEventsV2Params extends KalshiPaginationParams {
status?: string
seriesTicker?: string
withNestedMarkets?: string
withMilestones?: string
minCloseTs?: number
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetEventsV2Response {
success: boolean
output: {
events: Array<{
event_ticker: string
series_ticker: string
title: string
sub_title: string | null
mutually_exclusive: boolean
category: string
collateral_return_type: string | null
strike_date: string | null
strike_period: string | null
available_on_brokers: boolean | null
product_metadata: Record<string, unknown> | null
markets: Array<Record<string, unknown>> | null
}>
milestones: Array<{
event_ticker: string
milestone_type: string
milestone_date: string
milestone_title: string | null
}> | null
cursor: string | null
}
}
export const kalshiGetEventsV2Tool: ToolConfig<KalshiGetEventsV2Params, KalshiGetEventsV2Response> =
{
id: 'kalshi_get_events_v2',
name: 'Get Events from Kalshi V2',
description:
'Retrieve a list of events from Kalshi with optional filtering (V2 - exact API response)',
version: '2.0.0',
params: {
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by status (open, closed, settled)',
},
seriesTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by series ticker',
},
withNestedMarkets: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include nested markets in response (true/false)',
},
withMilestones: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include milestones in response (true/false)',
},
minCloseTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum close timestamp (Unix seconds)',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results (1-200, default: 200)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor for next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.status) queryParams.append('status', params.status)
if (params.seriesTicker) queryParams.append('series_ticker', params.seriesTicker)
if (params.withNestedMarkets)
queryParams.append('with_nested_markets', params.withNestedMarkets)
if (params.withMilestones) queryParams.append('with_milestones', params.withMilestones)
if (params.minCloseTs !== undefined)
queryParams.append('min_close_ts', params.minCloseTs.toString())
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/events')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_events_v2')
}
const events = (data.events || []).map((e: Record<string, unknown>) => ({
event_ticker: e.event_ticker ?? null,
series_ticker: e.series_ticker ?? null,
title: e.title ?? null,
sub_title: e.sub_title ?? null,
mutually_exclusive: e.mutually_exclusive ?? false,
category: e.category ?? null,
collateral_return_type: e.collateral_return_type ?? null,
strike_date: e.strike_date ?? null,
strike_period: e.strike_period ?? null,
available_on_brokers: e.available_on_brokers ?? null,
product_metadata: e.product_metadata ?? null,
markets: e.markets ?? null,
}))
const milestones = data.milestones
? (data.milestones as Array<Record<string, unknown>>).map((m) => ({
event_ticker: (m.event_ticker as string) ?? '',
milestone_type: (m.milestone_type as string) ?? '',
milestone_date: (m.milestone_date as string) ?? '',
milestone_title: (m.milestone_title as string | null) ?? null,
}))
: null
return {
success: true,
output: {
events,
milestones,
cursor: data.cursor ?? null,
},
}
},
outputs: {
events: {
type: 'array',
description: 'Array of event objects',
properties: {
event_ticker: { type: 'string', description: 'Event ticker' },
series_ticker: { type: 'string', description: 'Series ticker' },
title: { type: 'string', description: 'Event title' },
sub_title: { type: 'string', description: 'Event subtitle' },
mutually_exclusive: { type: 'boolean', description: 'Mutually exclusive markets' },
category: { type: 'string', description: 'Event category' },
collateral_return_type: { type: 'string', description: 'Collateral return type' },
strike_date: { type: 'string', description: 'Strike date' },
strike_period: { type: 'string', description: 'Strike period' },
available_on_brokers: { type: 'boolean', description: 'Available on brokers' },
product_metadata: { type: 'object', description: 'Product metadata' },
markets: { type: 'array', description: 'Nested markets (if requested)' },
},
},
milestones: {
type: 'array',
description: 'Array of milestone objects (if requested)',
properties: {
event_ticker: { type: 'string', description: 'Event ticker' },
milestone_type: { type: 'string', description: 'Milestone type' },
milestone_date: { type: 'string', description: 'Milestone date' },
milestone_title: { type: 'string', description: 'Milestone title' },
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}

View File

@@ -59,74 +59,3 @@ export const kalshiGetExchangeStatusTool: ToolConfig<
},
},
}
/**
* V2 Params for Get Exchange Status
*/
export type KalshiGetExchangeStatusV2Params = Record<string, never>
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetExchangeStatusV2Response {
success: boolean
output: {
exchange_active: boolean
trading_active: boolean
exchange_estimated_resume_time: string | null
}
}
export const kalshiGetExchangeStatusV2Tool: ToolConfig<
KalshiGetExchangeStatusV2Params,
KalshiGetExchangeStatusV2Response
> = {
id: 'kalshi_get_exchange_status_v2',
name: 'Get Exchange Status from Kalshi V2',
description: 'Retrieve the current status of the Kalshi exchange (V2 - exact API response)',
version: '2.0.0',
params: {},
request: {
url: () => {
return buildKalshiUrl('/exchange/status')
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_exchange_status_v2')
}
return {
success: true,
output: {
exchange_active: data.exchange_active ?? false,
trading_active: data.trading_active ?? false,
exchange_estimated_resume_time: data.exchange_estimated_resume_time ?? null,
},
}
},
outputs: {
exchange_active: {
type: 'boolean',
description: 'Whether the exchange is active',
},
trading_active: {
type: 'boolean',
description: 'Whether trading is active',
},
exchange_estimated_resume_time: {
type: 'string',
description: 'Estimated time when exchange will resume (if inactive)',
},
},
}

View File

@@ -131,198 +131,3 @@ export const kalshiGetFillsTool: ToolConfig<KalshiGetFillsParams, KalshiGetFills
},
},
}
/**
* V2 Params for Get Fills - fixes limit max to 200, adds subaccount
*/
export interface KalshiGetFillsV2Params extends KalshiAuthParams, KalshiPaginationParams {
ticker?: string
orderId?: string
minTs?: number
maxTs?: number
subaccount?: string
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetFillsV2Response {
success: boolean
output: {
fills: Array<{
fill_id: string
trade_id: string
order_id: string
client_order_id: string | null
ticker: string
market_ticker: string
side: string
action: string
count: number
count_fp: string | null
price: number | null
yes_price: number
no_price: number
yes_price_fixed: string | null
no_price_fixed: string | null
is_taker: boolean
created_time: string | null
ts: number | null
}>
cursor: string | null
}
}
export const kalshiGetFillsV2Tool: ToolConfig<KalshiGetFillsV2Params, KalshiGetFillsV2Response> = {
id: 'kalshi_get_fills_v2',
name: 'Get Fills from Kalshi V2',
description: "Retrieve your portfolio's fills/trades from Kalshi (V2 - exact API response)",
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market ticker',
},
orderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by order ID',
},
minTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum timestamp (Unix milliseconds)',
},
maxTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum timestamp (Unix milliseconds)',
},
subaccount: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Subaccount to get fills for',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results (1-200, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor for next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.ticker) queryParams.append('ticker', params.ticker)
if (params.orderId) queryParams.append('order_id', params.orderId)
if (params.minTs !== undefined) queryParams.append('min_ts', params.minTs.toString())
if (params.maxTs !== undefined) queryParams.append('max_ts', params.maxTs.toString())
if (params.subaccount) queryParams.append('subaccount', params.subaccount)
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/portfolio/fills')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/fills'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_fills_v2')
}
const fills = (data.fills || []).map((f: Record<string, unknown>) => ({
fill_id: f.fill_id ?? null,
trade_id: f.trade_id ?? null,
order_id: f.order_id ?? null,
client_order_id: f.client_order_id ?? null,
ticker: f.ticker ?? null,
market_ticker: f.market_ticker ?? null,
side: f.side ?? null,
action: f.action ?? null,
count: f.count ?? 0,
count_fp: f.count_fp ?? null,
price: f.price ?? null,
yes_price: f.yes_price ?? 0,
no_price: f.no_price ?? 0,
yes_price_fixed: f.yes_price_fixed ?? null,
no_price_fixed: f.no_price_fixed ?? null,
is_taker: f.is_taker ?? false,
created_time: f.created_time ?? null,
ts: f.ts ?? null,
}))
return {
success: true,
output: {
fills,
cursor: data.cursor ?? null,
},
}
},
outputs: {
fills: {
type: 'array',
description: 'Array of fill/trade objects with all API fields',
properties: {
fill_id: { type: 'string', description: 'Fill ID' },
trade_id: { type: 'string', description: 'Trade ID (same as fill_id)' },
order_id: { type: 'string', description: 'Order ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
ticker: { type: 'string', description: 'Market ticker' },
market_ticker: { type: 'string', description: 'Market ticker (legacy)' },
side: { type: 'string', description: 'Side (yes/no)' },
action: { type: 'string', description: 'Action (buy/sell)' },
count: { type: 'number', description: 'Number of contracts' },
count_fp: { type: 'string', description: 'Count (fixed-point)' },
price: { type: 'number', description: 'Price (deprecated)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
yes_price_fixed: { type: 'string', description: 'Yes price in dollars' },
no_price_fixed: { type: 'string', description: 'No price in dollars' },
is_taker: { type: 'boolean', description: 'Whether fill was taker' },
created_time: { type: 'string', description: 'Fill creation time' },
ts: { type: 'number', description: 'Unix timestamp (milliseconds)' },
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}

View File

@@ -58,219 +58,3 @@ export const kalshiGetMarketTool: ToolConfig<KalshiGetMarketParams, KalshiGetMar
},
},
}
/**
* V2 Get Market Tool - Returns exact Kalshi API response structure
*/
export interface KalshiGetMarketV2Response {
success: boolean
output: {
market: {
ticker: string
event_ticker: string
market_type: string
title: string
subtitle: string | null
yes_sub_title: string | null
no_sub_title: string | null
open_time: string | null
close_time: string | null
expected_expiration_time: string | null
expiration_time: string | null
latest_expiration_time: string | null
settlement_timer_seconds: number | null
status: string
response_price_units: string | null
notional_value: number | null
tick_size: number | null
yes_bid: number | null
yes_ask: number | null
no_bid: number | null
no_ask: number | null
last_price: number | null
previous_yes_bid: number | null
previous_yes_ask: number | null
previous_price: number | null
volume: number | null
volume_24h: number | null
liquidity: number | null
open_interest: number | null
result: string | null
cap_strike: number | null
floor_strike: number | null
can_close_early: boolean | null
expiration_value: string | null
category: string | null
risk_limit_cents: number | null
strike_type: string | null
rules_primary: string | null
rules_secondary: string | null
settlement_source_url: string | null
custom_strike: object | null
underlying: string | null
settlement_value: number | null
cfd_contract_size: number | null
yes_fee_fp: number | null
no_fee_fp: number | null
last_price_fp: number | null
yes_bid_fp: number | null
yes_ask_fp: number | null
no_bid_fp: number | null
no_ask_fp: number | null
}
}
}
export const kalshiGetMarketV2Tool: ToolConfig<KalshiGetMarketParams, KalshiGetMarketV2Response> = {
id: 'kalshi_get_market_v2',
name: 'Get Market from Kalshi V2',
description:
'Retrieve details of a specific prediction market by ticker (V2 - full API response)',
version: '2.0.0',
params: {
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The market ticker (e.g., "KXBTC-24DEC31")',
},
},
request: {
url: (params) => buildKalshiUrl(`/markets/${params.ticker}`),
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_market_v2')
}
const m = data.market || {}
return {
success: true,
output: {
market: {
ticker: m.ticker ?? null,
event_ticker: m.event_ticker ?? null,
market_type: m.market_type ?? null,
title: m.title ?? null,
subtitle: m.subtitle ?? null,
yes_sub_title: m.yes_sub_title ?? null,
no_sub_title: m.no_sub_title ?? null,
open_time: m.open_time ?? null,
close_time: m.close_time ?? null,
expected_expiration_time: m.expected_expiration_time ?? null,
expiration_time: m.expiration_time ?? null,
latest_expiration_time: m.latest_expiration_time ?? null,
settlement_timer_seconds: m.settlement_timer_seconds ?? null,
status: m.status ?? null,
response_price_units: m.response_price_units ?? null,
notional_value: m.notional_value ?? null,
tick_size: m.tick_size ?? null,
yes_bid: m.yes_bid ?? null,
yes_ask: m.yes_ask ?? null,
no_bid: m.no_bid ?? null,
no_ask: m.no_ask ?? null,
last_price: m.last_price ?? null,
previous_yes_bid: m.previous_yes_bid ?? null,
previous_yes_ask: m.previous_yes_ask ?? null,
previous_price: m.previous_price ?? null,
volume: m.volume ?? null,
volume_24h: m.volume_24h ?? null,
liquidity: m.liquidity ?? null,
open_interest: m.open_interest ?? null,
result: m.result ?? null,
cap_strike: m.cap_strike ?? null,
floor_strike: m.floor_strike ?? null,
can_close_early: m.can_close_early ?? null,
expiration_value: m.expiration_value ?? null,
category: m.category ?? null,
risk_limit_cents: m.risk_limit_cents ?? null,
strike_type: m.strike_type ?? null,
rules_primary: m.rules_primary ?? null,
rules_secondary: m.rules_secondary ?? null,
settlement_source_url: m.settlement_source_url ?? null,
custom_strike: m.custom_strike ?? null,
underlying: m.underlying ?? null,
settlement_value: m.settlement_value ?? null,
cfd_contract_size: m.cfd_contract_size ?? null,
yes_fee_fp: m.yes_fee_fp ?? null,
no_fee_fp: m.no_fee_fp ?? null,
last_price_fp: m.last_price_fp ?? null,
yes_bid_fp: m.yes_bid_fp ?? null,
yes_ask_fp: m.yes_ask_fp ?? null,
no_bid_fp: m.no_bid_fp ?? null,
no_ask_fp: m.no_ask_fp ?? null,
},
},
}
},
outputs: {
market: {
type: 'object',
description: 'Market object with all API fields',
properties: {
ticker: { type: 'string', description: 'Market ticker' },
event_ticker: { type: 'string', description: 'Event ticker' },
market_type: { type: 'string', description: 'Market type' },
title: { type: 'string', description: 'Market title' },
subtitle: { type: 'string', description: 'Market subtitle' },
yes_sub_title: { type: 'string', description: 'Yes outcome subtitle' },
no_sub_title: { type: 'string', description: 'No outcome subtitle' },
open_time: { type: 'string', description: 'Market open time' },
close_time: { type: 'string', description: 'Market close time' },
expected_expiration_time: { type: 'string', description: 'Expected expiration time' },
expiration_time: { type: 'string', description: 'Expiration time' },
latest_expiration_time: { type: 'string', description: 'Latest expiration time' },
settlement_timer_seconds: { type: 'number', description: 'Settlement timer in seconds' },
status: { type: 'string', description: 'Market status' },
response_price_units: { type: 'string', description: 'Response price units' },
notional_value: { type: 'number', description: 'Notional value' },
tick_size: { type: 'number', description: 'Tick size' },
yes_bid: { type: 'number', description: 'Current yes bid price' },
yes_ask: { type: 'number', description: 'Current yes ask price' },
no_bid: { type: 'number', description: 'Current no bid price' },
no_ask: { type: 'number', description: 'Current no ask price' },
last_price: { type: 'number', description: 'Last trade price' },
previous_yes_bid: { type: 'number', description: 'Previous yes bid' },
previous_yes_ask: { type: 'number', description: 'Previous yes ask' },
previous_price: { type: 'number', description: 'Previous price' },
volume: { type: 'number', description: 'Total volume' },
volume_24h: { type: 'number', description: '24-hour volume' },
liquidity: { type: 'number', description: 'Market liquidity' },
open_interest: { type: 'number', description: 'Open interest' },
result: { type: 'string', description: 'Market result' },
cap_strike: { type: 'number', description: 'Cap strike' },
floor_strike: { type: 'number', description: 'Floor strike' },
can_close_early: { type: 'boolean', description: 'Can close early' },
expiration_value: { type: 'string', description: 'Expiration value' },
category: { type: 'string', description: 'Market category' },
risk_limit_cents: { type: 'number', description: 'Risk limit in cents' },
strike_type: { type: 'string', description: 'Strike type' },
rules_primary: { type: 'string', description: 'Primary rules' },
rules_secondary: { type: 'string', description: 'Secondary rules' },
settlement_source_url: { type: 'string', description: 'Settlement source URL' },
custom_strike: { type: 'object', description: 'Custom strike object' },
underlying: { type: 'string', description: 'Underlying asset' },
settlement_value: { type: 'number', description: 'Settlement value' },
cfd_contract_size: { type: 'number', description: 'CFD contract size' },
yes_fee_fp: { type: 'number', description: 'Yes fee (fixed-point)' },
no_fee_fp: { type: 'number', description: 'No fee (fixed-point)' },
last_price_fp: { type: 'number', description: 'Last price (fixed-point)' },
yes_bid_fp: { type: 'number', description: 'Yes bid (fixed-point)' },
yes_ask_fp: { type: 'number', description: 'Yes ask (fixed-point)' },
no_bid_fp: { type: 'number', description: 'No bid (fixed-point)' },
no_ask_fp: { type: 'number', description: 'No ask (fixed-point)' },
},
},
},
}

View File

@@ -105,341 +105,3 @@ export const kalshiGetMarketsTool: ToolConfig<KalshiGetMarketsParams, KalshiGetM
},
},
}
/**
* V2 Get Markets Tool - Returns exact Kalshi API response structure with all params
*/
export interface KalshiGetMarketsV2Params extends KalshiPaginationParams {
status?: string // unopened, open, closed, settled
seriesTicker?: string
eventTicker?: string
minCreatedTs?: number
maxCreatedTs?: number
minUpdatedTs?: number
minCloseTs?: number
maxCloseTs?: number
minSettledTs?: number
maxSettledTs?: number
tickers?: string // comma-separated list
mveFilter?: string // display or all
}
export interface KalshiGetMarketsV2Response {
success: boolean
output: {
markets: Array<{
ticker: string
event_ticker: string
market_type: string
title: string
subtitle: string | null
yes_sub_title: string | null
no_sub_title: string | null
open_time: string | null
close_time: string | null
expected_expiration_time: string | null
expiration_time: string | null
latest_expiration_time: string | null
settlement_timer_seconds: number | null
status: string
response_price_units: string | null
notional_value: number | null
tick_size: number | null
yes_bid: number | null
yes_ask: number | null
no_bid: number | null
no_ask: number | null
last_price: number | null
previous_yes_bid: number | null
previous_yes_ask: number | null
previous_price: number | null
volume: number | null
volume_24h: number | null
liquidity: number | null
open_interest: number | null
result: string | null
cap_strike: number | null
floor_strike: number | null
can_close_early: boolean | null
expiration_value: string | null
category: string | null
risk_limit_cents: number | null
strike_type: string | null
rules_primary: string | null
rules_secondary: string | null
settlement_source_url: string | null
custom_strike: object | null
underlying: string | null
settlement_value: number | null
cfd_contract_size: number | null
yes_fee_fp: number | null
no_fee_fp: number | null
last_price_fp: number | null
yes_bid_fp: number | null
yes_ask_fp: number | null
no_bid_fp: number | null
no_ask_fp: number | null
}>
cursor: string | null
}
}
export const kalshiGetMarketsV2Tool: ToolConfig<
KalshiGetMarketsV2Params,
KalshiGetMarketsV2Response
> = {
id: 'kalshi_get_markets_v2',
name: 'Get Markets from Kalshi V2',
description:
'Retrieve a list of prediction markets from Kalshi with all filtering options (V2 - full API response)',
version: '2.0.0',
params: {
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by status (unopened, open, closed, settled)',
},
seriesTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by series ticker',
},
eventTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by event ticker',
},
minCreatedTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum created timestamp (Unix seconds)',
},
maxCreatedTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum created timestamp (Unix seconds)',
},
minUpdatedTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum updated timestamp (Unix seconds)',
},
minCloseTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum close timestamp (Unix seconds)',
},
maxCloseTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum close timestamp (Unix seconds)',
},
minSettledTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum settled timestamp (Unix seconds)',
},
maxSettledTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum settled timestamp (Unix seconds)',
},
tickers: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of tickers to filter',
},
mveFilter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'MVE filter (display or all)',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor for next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.status) queryParams.append('status', params.status)
if (params.seriesTicker) queryParams.append('series_ticker', params.seriesTicker)
if (params.eventTicker) queryParams.append('event_ticker', params.eventTicker)
if (params.minCreatedTs) queryParams.append('min_created_ts', params.minCreatedTs.toString())
if (params.maxCreatedTs) queryParams.append('max_created_ts', params.maxCreatedTs.toString())
if (params.minUpdatedTs) queryParams.append('min_updated_ts', params.minUpdatedTs.toString())
if (params.minCloseTs) queryParams.append('min_close_ts', params.minCloseTs.toString())
if (params.maxCloseTs) queryParams.append('max_close_ts', params.maxCloseTs.toString())
if (params.minSettledTs) queryParams.append('min_settled_ts', params.minSettledTs.toString())
if (params.maxSettledTs) queryParams.append('max_settled_ts', params.maxSettledTs.toString())
if (params.tickers) queryParams.append('tickers', params.tickers)
if (params.mveFilter) queryParams.append('mve_filter', params.mveFilter)
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/markets')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_markets_v2')
}
const markets = (data.markets || []).map((m: Record<string, unknown>) => ({
ticker: m.ticker ?? null,
event_ticker: m.event_ticker ?? null,
market_type: m.market_type ?? null,
title: m.title ?? null,
subtitle: m.subtitle ?? null,
yes_sub_title: m.yes_sub_title ?? null,
no_sub_title: m.no_sub_title ?? null,
open_time: m.open_time ?? null,
close_time: m.close_time ?? null,
expected_expiration_time: m.expected_expiration_time ?? null,
expiration_time: m.expiration_time ?? null,
latest_expiration_time: m.latest_expiration_time ?? null,
settlement_timer_seconds: m.settlement_timer_seconds ?? null,
status: m.status ?? null,
response_price_units: m.response_price_units ?? null,
notional_value: m.notional_value ?? null,
tick_size: m.tick_size ?? null,
yes_bid: m.yes_bid ?? null,
yes_ask: m.yes_ask ?? null,
no_bid: m.no_bid ?? null,
no_ask: m.no_ask ?? null,
last_price: m.last_price ?? null,
previous_yes_bid: m.previous_yes_bid ?? null,
previous_yes_ask: m.previous_yes_ask ?? null,
previous_price: m.previous_price ?? null,
volume: m.volume ?? null,
volume_24h: m.volume_24h ?? null,
liquidity: m.liquidity ?? null,
open_interest: m.open_interest ?? null,
result: m.result ?? null,
cap_strike: m.cap_strike ?? null,
floor_strike: m.floor_strike ?? null,
can_close_early: m.can_close_early ?? null,
expiration_value: m.expiration_value ?? null,
category: m.category ?? null,
risk_limit_cents: m.risk_limit_cents ?? null,
strike_type: m.strike_type ?? null,
rules_primary: m.rules_primary ?? null,
rules_secondary: m.rules_secondary ?? null,
settlement_source_url: m.settlement_source_url ?? null,
custom_strike: m.custom_strike ?? null,
underlying: m.underlying ?? null,
settlement_value: m.settlement_value ?? null,
cfd_contract_size: m.cfd_contract_size ?? null,
yes_fee_fp: m.yes_fee_fp ?? null,
no_fee_fp: m.no_fee_fp ?? null,
last_price_fp: m.last_price_fp ?? null,
yes_bid_fp: m.yes_bid_fp ?? null,
yes_ask_fp: m.yes_ask_fp ?? null,
no_bid_fp: m.no_bid_fp ?? null,
no_ask_fp: m.no_ask_fp ?? null,
}))
return {
success: true,
output: {
markets,
cursor: data.cursor ?? null,
},
}
},
outputs: {
markets: {
type: 'array',
description: 'Array of market objects with all API fields',
properties: {
ticker: { type: 'string', description: 'Market ticker' },
event_ticker: { type: 'string', description: 'Event ticker' },
market_type: { type: 'string', description: 'Market type' },
title: { type: 'string', description: 'Market title' },
subtitle: { type: 'string', description: 'Market subtitle' },
yes_sub_title: { type: 'string', description: 'Yes outcome subtitle' },
no_sub_title: { type: 'string', description: 'No outcome subtitle' },
open_time: { type: 'string', description: 'Market open time' },
close_time: { type: 'string', description: 'Market close time' },
expected_expiration_time: { type: 'string', description: 'Expected expiration time' },
expiration_time: { type: 'string', description: 'Expiration time' },
latest_expiration_time: { type: 'string', description: 'Latest expiration time' },
settlement_timer_seconds: { type: 'number', description: 'Settlement timer in seconds' },
status: { type: 'string', description: 'Market status' },
response_price_units: { type: 'string', description: 'Response price units' },
notional_value: { type: 'number', description: 'Notional value' },
tick_size: { type: 'number', description: 'Tick size' },
yes_bid: { type: 'number', description: 'Current yes bid price' },
yes_ask: { type: 'number', description: 'Current yes ask price' },
no_bid: { type: 'number', description: 'Current no bid price' },
no_ask: { type: 'number', description: 'Current no ask price' },
last_price: { type: 'number', description: 'Last trade price' },
previous_yes_bid: { type: 'number', description: 'Previous yes bid' },
previous_yes_ask: { type: 'number', description: 'Previous yes ask' },
previous_price: { type: 'number', description: 'Previous price' },
volume: { type: 'number', description: 'Total volume' },
volume_24h: { type: 'number', description: '24-hour volume' },
liquidity: { type: 'number', description: 'Market liquidity' },
open_interest: { type: 'number', description: 'Open interest' },
result: { type: 'string', description: 'Market result' },
cap_strike: { type: 'number', description: 'Cap strike' },
floor_strike: { type: 'number', description: 'Floor strike' },
can_close_early: { type: 'boolean', description: 'Can close early' },
expiration_value: { type: 'string', description: 'Expiration value' },
category: { type: 'string', description: 'Market category' },
risk_limit_cents: { type: 'number', description: 'Risk limit in cents' },
strike_type: { type: 'string', description: 'Strike type' },
rules_primary: { type: 'string', description: 'Primary rules' },
rules_secondary: { type: 'string', description: 'Secondary rules' },
settlement_source_url: { type: 'string', description: 'Settlement source URL' },
custom_strike: { type: 'object', description: 'Custom strike object' },
underlying: { type: 'string', description: 'Underlying asset' },
settlement_value: { type: 'number', description: 'Settlement value' },
cfd_contract_size: { type: 'number', description: 'CFD contract size' },
yes_fee_fp: { type: 'number', description: 'Yes fee (fixed-point)' },
no_fee_fp: { type: 'number', description: 'No fee (fixed-point)' },
last_price_fp: { type: 'number', description: 'Last price (fixed-point)' },
yes_bid_fp: { type: 'number', description: 'Yes bid (fixed-point)' },
yes_ask_fp: { type: 'number', description: 'Yes ask (fixed-point)' },
no_bid_fp: { type: 'number', description: 'No bid (fixed-point)' },
no_ask_fp: { type: 'number', description: 'No ask (fixed-point)' },
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}

View File

@@ -71,178 +71,3 @@ export const kalshiGetOrderTool: ToolConfig<KalshiGetOrderParams, KalshiGetOrder
},
},
}
export interface KalshiGetOrderV2Params extends KalshiAuthParams {
orderId: string // Order ID to retrieve (required)
}
export interface KalshiGetOrderV2Response {
success: boolean
output: {
order: {
order_id: string
user_id: string | null
client_order_id: string | null
ticker: string
side: string
action: string
type: string
status: string
yes_price: number | null
no_price: number | null
yes_price_dollars: string | null
no_price_dollars: string | null
fill_count: number | null
fill_count_fp: string | null
remaining_count: number | null
remaining_count_fp: string | null
initial_count: number | null
initial_count_fp: string | null
taker_fees: number | null
maker_fees: number | null
taker_fees_dollars: string | null
maker_fees_dollars: string | null
taker_fill_cost: number | null
maker_fill_cost: number | null
taker_fill_cost_dollars: string | null
maker_fill_cost_dollars: string | null
queue_position: number | null
expiration_time: string | null
created_time: string | null
last_update_time: string | null
self_trade_prevention_type: string | null
order_group_id: string | null
cancel_order_on_pause: boolean | null
}
}
}
export const kalshiGetOrderV2Tool: ToolConfig<KalshiGetOrderV2Params, KalshiGetOrderV2Response> = {
id: 'kalshi_get_order_v2',
name: 'Get Order from Kalshi V2',
description: 'Retrieve details of a specific order by ID from Kalshi (V2 with full API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
orderId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The order ID to retrieve',
},
},
request: {
url: (params) => buildKalshiUrl(`/portfolio/orders/${params.orderId}`),
method: 'GET',
headers: (params) => {
const path = `/trade-api/v2/portfolio/orders/${params.orderId}`
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_order_v2')
}
const order = data.order || {}
return {
success: true,
output: {
order: {
order_id: order.order_id ?? null,
user_id: order.user_id ?? null,
client_order_id: order.client_order_id ?? null,
ticker: order.ticker ?? null,
side: order.side ?? null,
action: order.action ?? null,
type: order.type ?? null,
status: order.status ?? null,
yes_price: order.yes_price ?? null,
no_price: order.no_price ?? null,
yes_price_dollars: order.yes_price_dollars ?? null,
no_price_dollars: order.no_price_dollars ?? null,
fill_count: order.fill_count ?? null,
fill_count_fp: order.fill_count_fp ?? null,
remaining_count: order.remaining_count ?? null,
remaining_count_fp: order.remaining_count_fp ?? null,
initial_count: order.initial_count ?? null,
initial_count_fp: order.initial_count_fp ?? null,
taker_fees: order.taker_fees ?? null,
maker_fees: order.maker_fees ?? null,
taker_fees_dollars: order.taker_fees_dollars ?? null,
maker_fees_dollars: order.maker_fees_dollars ?? null,
taker_fill_cost: order.taker_fill_cost ?? null,
maker_fill_cost: order.maker_fill_cost ?? null,
taker_fill_cost_dollars: order.taker_fill_cost_dollars ?? null,
maker_fill_cost_dollars: order.maker_fill_cost_dollars ?? null,
queue_position: order.queue_position ?? null,
expiration_time: order.expiration_time ?? null,
created_time: order.created_time ?? null,
last_update_time: order.last_update_time ?? null,
self_trade_prevention_type: order.self_trade_prevention_type ?? null,
order_group_id: order.order_group_id ?? null,
cancel_order_on_pause: order.cancel_order_on_pause ?? null,
},
},
}
},
outputs: {
order: {
type: 'object',
description: 'Order object with full API response fields',
properties: {
order_id: { type: 'string', description: 'Order ID' },
user_id: { type: 'string', description: 'User ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
ticker: { type: 'string', description: 'Market ticker' },
side: { type: 'string', description: 'Order side (yes/no)' },
action: { type: 'string', description: 'Action (buy/sell)' },
type: { type: 'string', description: 'Order type (limit/market)' },
status: { type: 'string', description: 'Order status (resting/canceled/executed)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
yes_price_dollars: { type: 'string', description: 'Yes price in dollars' },
no_price_dollars: { type: 'string', description: 'No price in dollars' },
fill_count: { type: 'number', description: 'Filled contract count' },
fill_count_fp: { type: 'string', description: 'Filled count (fixed-point)' },
remaining_count: { type: 'number', description: 'Remaining contracts' },
remaining_count_fp: { type: 'string', description: 'Remaining count (fixed-point)' },
initial_count: { type: 'number', description: 'Initial contract count' },
initial_count_fp: { type: 'string', description: 'Initial count (fixed-point)' },
taker_fees: { type: 'number', description: 'Taker fees in cents' },
maker_fees: { type: 'number', description: 'Maker fees in cents' },
taker_fees_dollars: { type: 'string', description: 'Taker fees in dollars' },
maker_fees_dollars: { type: 'string', description: 'Maker fees in dollars' },
taker_fill_cost: { type: 'number', description: 'Taker fill cost in cents' },
maker_fill_cost: { type: 'number', description: 'Maker fill cost in cents' },
taker_fill_cost_dollars: { type: 'string', description: 'Taker fill cost in dollars' },
maker_fill_cost_dollars: { type: 'string', description: 'Maker fill cost in dollars' },
queue_position: { type: 'number', description: 'Queue position (deprecated)' },
expiration_time: { type: 'string', description: 'Order expiration time' },
created_time: { type: 'string', description: 'Order creation time' },
last_update_time: { type: 'string', description: 'Last update time' },
self_trade_prevention_type: { type: 'string', description: 'Self-trade prevention type' },
order_group_id: { type: 'string', description: 'Order group ID' },
cancel_order_on_pause: { type: 'boolean', description: 'Cancel on market pause' },
},
},
},
}

View File

@@ -63,135 +63,3 @@ export const kalshiGetOrderbookTool: ToolConfig<
},
},
}
/**
* V2 Get Orderbook Tool - Returns exact Kalshi API response structure with depth param
* API returns tuple arrays: [price, count] for orderbook, [dollars_string, count] for _dollars variants
*/
export interface KalshiGetOrderbookV2Params {
ticker: string
depth?: number // Number of price levels to return
}
export interface KalshiGetOrderbookV2Response {
success: boolean
output: {
orderbook: {
yes: Array<[number, number]> // [price_in_cents, count]
no: Array<[number, number]> // [price_in_cents, count]
yes_dollars: Array<[string, number]> // [dollars_string, count]
no_dollars: Array<[string, number]> // [dollars_string, count]
}
orderbook_fp: {
yes_dollars: Array<[string, string]> // [dollars_string, fp_count_string]
no_dollars: Array<[string, string]> // [dollars_string, fp_count_string]
}
}
}
export const kalshiGetOrderbookV2Tool: ToolConfig<
KalshiGetOrderbookV2Params,
KalshiGetOrderbookV2Response
> = {
id: 'kalshi_get_orderbook_v2',
name: 'Get Market Orderbook from Kalshi V2',
description:
'Retrieve the orderbook (yes and no bids) for a specific market (V2 - includes depth and fp fields)',
version: '2.0.0',
params: {
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker (e.g., KXBTC-24DEC31)',
},
depth: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of price levels to return (default: all)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.depth) queryParams.append('depth', params.depth.toString())
const query = queryParams.toString()
const url = buildKalshiUrl(`/markets/${params.ticker}/orderbook`)
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_orderbook_v2')
}
const orderbook = data.orderbook || {}
const orderbookFp = data.orderbook_fp || {}
return {
success: true,
output: {
orderbook: {
yes: orderbook.yes ?? [],
no: orderbook.no ?? [],
yes_dollars: orderbook.yes_dollars ?? [],
no_dollars: orderbook.no_dollars ?? [],
},
orderbook_fp: {
yes_dollars: orderbookFp.yes_dollars ?? [],
no_dollars: orderbookFp.no_dollars ?? [],
},
},
}
},
outputs: {
orderbook: {
type: 'object',
description: 'Orderbook with yes/no bids (legacy integer counts)',
properties: {
yes: {
type: 'array',
description: 'Yes side bids as tuples [price_cents, count]',
},
no: {
type: 'array',
description: 'No side bids as tuples [price_cents, count]',
},
yes_dollars: {
type: 'array',
description: 'Yes side bids as tuples [dollars_string, count]',
},
no_dollars: {
type: 'array',
description: 'No side bids as tuples [dollars_string, count]',
},
},
},
orderbook_fp: {
type: 'object',
description: 'Orderbook with fixed-point counts (preferred)',
properties: {
yes_dollars: {
type: 'array',
description: 'Yes side bids as tuples [dollars_string, fp_count_string]',
},
no_dollars: {
type: 'array',
description: 'No side bids as tuples [dollars_string, fp_count_string]',
},
},
},
},
}

View File

@@ -123,250 +123,3 @@ export const kalshiGetOrdersTool: ToolConfig<KalshiGetOrdersParams, KalshiGetOrd
},
},
}
export interface KalshiGetOrdersV2Params extends KalshiAuthParams, KalshiPaginationParams {
ticker?: string
eventTicker?: string
status?: string // resting, canceled, executed
minTs?: string // Minimum timestamp filter (Unix timestamp)
maxTs?: string // Maximum timestamp filter (Unix timestamp)
subaccount?: string // Subaccount to filter orders
}
export interface KalshiOrderV2 {
order_id: string
user_id: string | null
client_order_id: string | null
ticker: string
side: string
action: string
type: string
status: string
yes_price: number | null
no_price: number | null
yes_price_dollars: string | null
no_price_dollars: string | null
fill_count: number | null
fill_count_fp: string | null
remaining_count: number | null
remaining_count_fp: string | null
initial_count: number | null
initial_count_fp: string | null
taker_fees: number | null
maker_fees: number | null
taker_fees_dollars: string | null
maker_fees_dollars: string | null
taker_fill_cost: number | null
maker_fill_cost: number | null
taker_fill_cost_dollars: string | null
maker_fill_cost_dollars: string | null
queue_position: number | null
expiration_time: string | null
created_time: string | null
last_update_time: string | null
self_trade_prevention_type: string | null
order_group_id: string | null
cancel_order_on_pause: boolean | null
}
export interface KalshiGetOrdersV2Response {
success: boolean
output: {
orders: KalshiOrderV2[]
cursor: string | null
}
}
export const kalshiGetOrdersV2Tool: ToolConfig<KalshiGetOrdersV2Params, KalshiGetOrdersV2Response> =
{
id: 'kalshi_get_orders_v2',
name: 'Get Orders from Kalshi V2',
description:
'Retrieve your orders from Kalshi with optional filtering (V2 with full API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market ticker',
},
eventTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by event ticker (max 10 comma-separated)',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by status (resting, canceled, executed)',
},
minTs: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Minimum timestamp filter (Unix timestamp)',
},
maxTs: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Maximum timestamp filter (Unix timestamp)',
},
subaccount: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Subaccount to filter orders',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results (1-200, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor for next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.ticker) queryParams.append('ticker', params.ticker)
if (params.eventTicker) queryParams.append('event_ticker', params.eventTicker)
if (params.status) queryParams.append('status', params.status)
if (params.minTs) queryParams.append('min_ts', params.minTs)
if (params.maxTs) queryParams.append('max_ts', params.maxTs)
if (params.subaccount) queryParams.append('subaccount', params.subaccount)
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/portfolio/orders')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/orders'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_orders_v2')
}
const rawOrders = data.orders || []
const orders: KalshiOrderV2[] = rawOrders.map((order: any) => ({
order_id: order.order_id ?? null,
user_id: order.user_id ?? null,
client_order_id: order.client_order_id ?? null,
ticker: order.ticker ?? null,
side: order.side ?? null,
action: order.action ?? null,
type: order.type ?? null,
status: order.status ?? null,
yes_price: order.yes_price ?? null,
no_price: order.no_price ?? null,
yes_price_dollars: order.yes_price_dollars ?? null,
no_price_dollars: order.no_price_dollars ?? null,
fill_count: order.fill_count ?? null,
fill_count_fp: order.fill_count_fp ?? null,
remaining_count: order.remaining_count ?? null,
remaining_count_fp: order.remaining_count_fp ?? null,
initial_count: order.initial_count ?? null,
initial_count_fp: order.initial_count_fp ?? null,
taker_fees: order.taker_fees ?? null,
maker_fees: order.maker_fees ?? null,
taker_fees_dollars: order.taker_fees_dollars ?? null,
maker_fees_dollars: order.maker_fees_dollars ?? null,
taker_fill_cost: order.taker_fill_cost ?? null,
maker_fill_cost: order.maker_fill_cost ?? null,
taker_fill_cost_dollars: order.taker_fill_cost_dollars ?? null,
maker_fill_cost_dollars: order.maker_fill_cost_dollars ?? null,
queue_position: order.queue_position ?? null,
expiration_time: order.expiration_time ?? null,
created_time: order.created_time ?? null,
last_update_time: order.last_update_time ?? null,
self_trade_prevention_type: order.self_trade_prevention_type ?? null,
order_group_id: order.order_group_id ?? null,
cancel_order_on_pause: order.cancel_order_on_pause ?? null,
}))
return {
success: true,
output: {
orders,
cursor: data.cursor ?? null,
},
}
},
outputs: {
orders: {
type: 'array',
description: 'Array of order objects with full API response fields',
properties: {
order_id: { type: 'string', description: 'Order ID' },
user_id: { type: 'string', description: 'User ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
ticker: { type: 'string', description: 'Market ticker' },
side: { type: 'string', description: 'Order side (yes/no)' },
action: { type: 'string', description: 'Action (buy/sell)' },
type: { type: 'string', description: 'Order type (limit/market)' },
status: { type: 'string', description: 'Order status (resting/canceled/executed)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
yes_price_dollars: { type: 'string', description: 'Yes price in dollars' },
no_price_dollars: { type: 'string', description: 'No price in dollars' },
fill_count: { type: 'number', description: 'Filled contract count' },
fill_count_fp: { type: 'string', description: 'Filled count (fixed-point)' },
remaining_count: { type: 'number', description: 'Remaining contracts' },
remaining_count_fp: { type: 'string', description: 'Remaining count (fixed-point)' },
initial_count: { type: 'number', description: 'Initial contract count' },
initial_count_fp: { type: 'string', description: 'Initial count (fixed-point)' },
taker_fees: { type: 'number', description: 'Taker fees in cents' },
maker_fees: { type: 'number', description: 'Maker fees in cents' },
taker_fees_dollars: { type: 'string', description: 'Taker fees in dollars' },
maker_fees_dollars: { type: 'string', description: 'Maker fees in dollars' },
taker_fill_cost: { type: 'number', description: 'Taker fill cost in cents' },
maker_fill_cost: { type: 'number', description: 'Maker fill cost in cents' },
taker_fill_cost_dollars: { type: 'string', description: 'Taker fill cost in dollars' },
maker_fill_cost_dollars: { type: 'string', description: 'Maker fill cost in dollars' },
queue_position: { type: 'number', description: 'Queue position (deprecated)' },
expiration_time: { type: 'string', description: 'Order expiration time' },
created_time: { type: 'string', description: 'Order creation time' },
last_update_time: { type: 'string', description: 'Last update time' },
self_trade_prevention_type: { type: 'string', description: 'Self-trade prevention type' },
order_group_id: { type: 'string', description: 'Order group ID' },
cancel_order_on_pause: { type: 'boolean', description: 'Cancel on market pause' },
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}

View File

@@ -126,195 +126,3 @@ export const kalshiGetPositionsTool: ToolConfig<
},
},
}
/**
* V2 Params for Get Positions - removes invalid settlementStatus, adds countFilter and subaccount
*/
export interface KalshiGetPositionsV2Params extends KalshiAuthParams, KalshiPaginationParams {
ticker?: string
eventTicker?: string
countFilter?: string
subaccount?: string
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetPositionsV2Response {
success: boolean
output: {
market_positions: Array<{
ticker: string
event_ticker: string
event_title: string | null
market_title: string | null
position: number
market_exposure: number | null
realized_pnl: number | null
total_traded: number | null
resting_orders_count: number | null
fees_paid: number | null
}>
event_positions: Array<{
event_ticker: string
event_exposure: number
realized_pnl: number | null
total_cost: number | null
}> | null
cursor: string | null
}
}
export const kalshiGetPositionsV2Tool: ToolConfig<
KalshiGetPositionsV2Params,
KalshiGetPositionsV2Response
> = {
id: 'kalshi_get_positions_v2',
name: 'Get Positions from Kalshi V2',
description: 'Retrieve your open positions from Kalshi (V2 - exact API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market ticker',
},
eventTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by event ticker (max 10 comma-separated)',
},
countFilter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by count (all, positive, negative). Default: all',
},
subaccount: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Subaccount to get positions for',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor for next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.ticker) queryParams.append('ticker', params.ticker)
if (params.eventTicker) queryParams.append('event_ticker', params.eventTicker)
if (params.countFilter) queryParams.append('count_filter', params.countFilter)
if (params.subaccount) queryParams.append('subaccount', params.subaccount)
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/portfolio/positions')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/positions'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_positions_v2')
}
const marketPositions = (data.market_positions || []).map((p: Record<string, unknown>) => ({
ticker: p.ticker ?? null,
event_ticker: p.event_ticker ?? null,
event_title: p.event_title ?? null,
market_title: p.market_title ?? null,
position: p.position ?? 0,
market_exposure: p.market_exposure ?? null,
realized_pnl: p.realized_pnl ?? null,
total_traded: p.total_traded ?? null,
resting_orders_count: p.resting_orders_count ?? null,
fees_paid: p.fees_paid ?? null,
}))
const eventPositions = data.event_positions
? (data.event_positions as Array<Record<string, unknown>>).map((p) => ({
event_ticker: (p.event_ticker as string) ?? '',
event_exposure: (p.event_exposure as number) ?? 0,
realized_pnl: (p.realized_pnl as number | null) ?? null,
total_cost: (p.total_cost as number | null) ?? null,
}))
: null
return {
success: true,
output: {
market_positions: marketPositions,
event_positions: eventPositions,
cursor: data.cursor ?? null,
},
}
},
outputs: {
market_positions: {
type: 'array',
description: 'Array of market position objects',
properties: {
ticker: { type: 'string', description: 'Market ticker' },
event_ticker: { type: 'string', description: 'Event ticker' },
event_title: { type: 'string', description: 'Event title' },
market_title: { type: 'string', description: 'Market title' },
position: { type: 'number', description: 'Position size' },
market_exposure: { type: 'number', description: 'Market exposure' },
realized_pnl: { type: 'number', description: 'Realized P&L' },
total_traded: { type: 'number', description: 'Total traded' },
resting_orders_count: { type: 'number', description: 'Resting orders count' },
fees_paid: { type: 'number', description: 'Fees paid' },
},
},
event_positions: {
type: 'array',
description: 'Array of event position objects',
properties: {
event_ticker: { type: 'string', description: 'Event ticker' },
event_exposure: { type: 'number', description: 'Event exposure' },
realized_pnl: { type: 'number', description: 'Realized P&L' },
total_cost: { type: 'number', description: 'Total cost' },
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}

View File

@@ -65,141 +65,3 @@ export const kalshiGetSeriesByTickerTool: ToolConfig<
},
},
}
/**
* V2 Params for Get Series by Ticker
*/
export interface KalshiGetSeriesByTickerV2Params {
seriesTicker: string
includeVolume?: string
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetSeriesByTickerV2Response {
success: boolean
output: {
series: {
ticker: string
title: string
frequency: string
category: string
tags: string[] | null
settlement_sources: Array<{
name: string
url: string
}> | null
contract_url: string | null
contract_terms_url: string | null
fee_type: string | null
fee_multiplier: number | null
additional_prohibitions: string[] | null
product_metadata: Record<string, unknown> | null
volume: number | null
volume_fp: number | null
}
}
}
export const kalshiGetSeriesByTickerV2Tool: ToolConfig<
KalshiGetSeriesByTickerV2Params,
KalshiGetSeriesByTickerV2Response
> = {
id: 'kalshi_get_series_by_ticker_v2',
name: 'Get Series by Ticker from Kalshi V2',
description: 'Retrieve details of a specific market series by ticker (V2 - exact API response)',
version: '2.0.0',
params: {
seriesTicker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Series ticker',
},
includeVolume: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include volume data in response (true/false)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.includeVolume) queryParams.append('include_volume', params.includeVolume)
const query = queryParams.toString()
const url = buildKalshiUrl(`/series/${params.seriesTicker}`)
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_series_by_ticker_v2')
}
const series = data.series || data
const settlementSources = series.settlement_sources
? (series.settlement_sources as Array<Record<string, unknown>>).map((s) => ({
name: (s.name as string) ?? null,
url: (s.url as string) ?? null,
}))
: null
return {
success: true,
output: {
series: {
ticker: series.ticker ?? null,
title: series.title ?? null,
frequency: series.frequency ?? null,
category: series.category ?? null,
tags: series.tags ?? null,
settlement_sources: settlementSources,
contract_url: series.contract_url ?? null,
contract_terms_url: series.contract_terms_url ?? null,
fee_type: series.fee_type ?? null,
fee_multiplier: series.fee_multiplier ?? null,
additional_prohibitions: series.additional_prohibitions ?? null,
product_metadata: series.product_metadata ?? null,
volume: series.volume ?? null,
volume_fp: series.volume_fp ?? null,
},
},
}
},
outputs: {
series: {
type: 'object',
description: 'Series object with full details matching Kalshi API response',
properties: {
ticker: { type: 'string', description: 'Series ticker' },
title: { type: 'string', description: 'Series title' },
frequency: { type: 'string', description: 'Event frequency' },
category: { type: 'string', description: 'Series category' },
tags: { type: 'array', description: 'Series tags' },
settlement_sources: { type: 'array', description: 'Settlement sources' },
contract_url: { type: 'string', description: 'Contract URL' },
contract_terms_url: { type: 'string', description: 'Contract terms URL' },
fee_type: { type: 'string', description: 'Fee type' },
fee_multiplier: { type: 'number', description: 'Fee multiplier' },
additional_prohibitions: { type: 'array', description: 'Additional prohibitions' },
product_metadata: { type: 'object', description: 'Product metadata' },
volume: { type: 'number', description: 'Series volume' },
volume_fp: { type: 'number', description: 'Volume (fixed-point)' },
},
},
},
}

View File

@@ -80,138 +80,3 @@ export const kalshiGetTradesTool: ToolConfig<KalshiGetTradesParams, KalshiGetTra
},
},
}
/**
* V2 Get Trades Tool - Returns exact Kalshi API response structure with additional params
*/
export interface KalshiGetTradesV2Params extends KalshiPaginationParams {
ticker?: string // Filter by market ticker
minTs?: number // Minimum timestamp (Unix seconds)
maxTs?: number // Maximum timestamp (Unix seconds)
}
export interface KalshiGetTradesV2Response {
success: boolean
output: {
trades: Array<{
trade_id: string | null
ticker: string
yes_price: number | null
no_price: number | null
count: number | null
count_fp: number | null
created_time: string | null
taker_side: string | null
}>
cursor: string | null
}
}
export const kalshiGetTradesV2Tool: ToolConfig<KalshiGetTradesV2Params, KalshiGetTradesV2Response> =
{
id: 'kalshi_get_trades_v2',
name: 'Get Trades from Kalshi V2',
description:
'Retrieve recent trades with additional filtering options (V2 - includes trade_id and count_fp)',
version: '2.0.0',
params: {
ticker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market ticker',
},
minTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum timestamp (Unix seconds)',
},
maxTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum timestamp (Unix seconds)',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor for next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.ticker) queryParams.append('ticker', params.ticker)
if (params.minTs) queryParams.append('min_ts', params.minTs.toString())
if (params.maxTs) queryParams.append('max_ts', params.maxTs.toString())
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/markets/trades')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_trades_v2')
}
const trades = (data.trades || []).map((t: Record<string, unknown>) => ({
trade_id: t.trade_id ?? null,
ticker: t.ticker ?? null,
yes_price: t.yes_price ?? null,
no_price: t.no_price ?? null,
count: t.count ?? null,
count_fp: t.count_fp ?? null,
created_time: t.created_time ?? null,
taker_side: t.taker_side ?? null,
}))
return {
success: true,
output: {
trades,
cursor: data.cursor ?? null,
},
}
},
outputs: {
trades: {
type: 'array',
description: 'Array of trade objects with trade_id and count_fp',
properties: {
trade_id: { type: 'string', description: 'Trade ID' },
ticker: { type: 'string', description: 'Market ticker' },
yes_price: { type: 'number', description: 'Yes price' },
no_price: { type: 'number', description: 'No price' },
count: { type: 'number', description: 'Number of contracts' },
count_fp: { type: 'number', description: 'Count (fixed-point)' },
created_time: { type: 'string', description: 'Trade creation time' },
taker_side: { type: 'string', description: 'Taker side (yes/no)' },
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}

View File

@@ -1,17 +1,17 @@
export { kalshiAmendOrderTool, kalshiAmendOrderV2Tool } from './amend_order'
export { kalshiCancelOrderTool, kalshiCancelOrderV2Tool } from './cancel_order'
export { kalshiCreateOrderTool, kalshiCreateOrderV2Tool } from './create_order'
export { kalshiGetBalanceTool, kalshiGetBalanceV2Tool } from './get_balance'
export { kalshiGetCandlesticksTool, kalshiGetCandlesticksV2Tool } from './get_candlesticks'
export { kalshiGetEventTool, kalshiGetEventV2Tool } from './get_event'
export { kalshiGetEventsTool, kalshiGetEventsV2Tool } from './get_events'
export { kalshiGetExchangeStatusTool, kalshiGetExchangeStatusV2Tool } from './get_exchange_status'
export { kalshiGetFillsTool, kalshiGetFillsV2Tool } from './get_fills'
export { kalshiGetMarketTool, kalshiGetMarketV2Tool } from './get_market'
export { kalshiGetMarketsTool, kalshiGetMarketsV2Tool } from './get_markets'
export { kalshiGetOrderTool, kalshiGetOrderV2Tool } from './get_order'
export { kalshiGetOrderbookTool, kalshiGetOrderbookV2Tool } from './get_orderbook'
export { kalshiGetOrdersTool, kalshiGetOrdersV2Tool } from './get_orders'
export { kalshiGetPositionsTool, kalshiGetPositionsV2Tool } from './get_positions'
export { kalshiGetSeriesByTickerTool, kalshiGetSeriesByTickerV2Tool } from './get_series_by_ticker'
export { kalshiGetTradesTool, kalshiGetTradesV2Tool } from './get_trades'
export { kalshiAmendOrderTool } from './amend_order'
export { kalshiCancelOrderTool } from './cancel_order'
export { kalshiCreateOrderTool } from './create_order'
export { kalshiGetBalanceTool } from './get_balance'
export { kalshiGetCandlesticksTool } from './get_candlesticks'
export { kalshiGetEventTool } from './get_event'
export { kalshiGetEventsTool } from './get_events'
export { kalshiGetExchangeStatusTool } from './get_exchange_status'
export { kalshiGetFillsTool } from './get_fills'
export { kalshiGetMarketTool } from './get_market'
export { kalshiGetMarketsTool } from './get_markets'
export { kalshiGetOrderTool } from './get_order'
export { kalshiGetOrderbookTool } from './get_orderbook'
export { kalshiGetOrdersTool } from './get_orders'
export { kalshiGetPositionsTool } from './get_positions'
export { kalshiGetSeriesByTickerTool } from './get_series_by_ticker'
export { kalshiGetTradesTool } from './get_trades'

View File

@@ -1,206 +0,0 @@
import type { PolymarketActivity } from '@/tools/polymarket/types'
import { buildDataUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
export interface PolymarketGetActivityParams {
user: string
limit?: string
offset?: string
market?: string
eventId?: string
type?: string
start?: number
end?: number
sortBy?: string
sortDirection?: string
side?: string
}
export interface PolymarketGetActivityResponse {
success: boolean
output: {
activity: PolymarketActivity[]
}
}
export const polymarketGetActivityTool: ToolConfig<
PolymarketGetActivityParams,
PolymarketGetActivityResponse
> = {
id: 'polymarket_get_activity',
name: 'Get Activity from Polymarket',
description:
'Retrieve on-chain activity for a user including trades, splits, merges, redemptions, rewards, and conversions',
version: '1.0.0',
params: {
user: {
type: 'string',
required: true,
description: 'User wallet address (0x-prefixed)',
visibility: 'user-or-llm',
},
limit: {
type: 'string',
required: false,
description: 'Maximum results (default: 100, max: 500)',
visibility: 'user-or-llm',
},
offset: {
type: 'string',
required: false,
description: 'Pagination offset (default: 0, max: 10000)',
visibility: 'user-or-llm',
},
market: {
type: 'string',
required: false,
description: 'Comma-separated condition IDs (mutually exclusive with eventId)',
visibility: 'user-or-llm',
},
eventId: {
type: 'string',
required: false,
description: 'Comma-separated event IDs (mutually exclusive with market)',
visibility: 'user-or-llm',
},
type: {
type: 'string',
required: false,
description:
'Activity type filter: TRADE, SPLIT, MERGE, REDEEM, REWARD, CONVERSION, MAKER_REBATE',
visibility: 'user-or-llm',
},
start: {
type: 'number',
required: false,
description: 'Start timestamp (Unix seconds)',
visibility: 'user-or-llm',
},
end: {
type: 'number',
required: false,
description: 'End timestamp (Unix seconds)',
visibility: 'user-or-llm',
},
sortBy: {
type: 'string',
required: false,
description: 'Sort field: TIMESTAMP, TOKENS, or CASH (default: TIMESTAMP)',
visibility: 'user-or-llm',
},
sortDirection: {
type: 'string',
required: false,
description: 'Sort direction: ASC or DESC (default: DESC)',
visibility: 'user-or-llm',
},
side: {
type: 'string',
required: false,
description: 'Trade side filter: BUY or SELL (only applies to trades)',
visibility: 'user-or-llm',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
queryParams.append('user', params.user)
if (params.limit) queryParams.append('limit', params.limit)
if (params.offset) queryParams.append('offset', params.offset)
if (params.market) queryParams.append('market', params.market)
if (params.eventId) queryParams.append('eventId', params.eventId)
if (params.type) queryParams.append('type', params.type)
if (params.start != null && !Number.isNaN(params.start))
queryParams.append('start', String(params.start))
if (params.end != null && !Number.isNaN(params.end))
queryParams.append('end', String(params.end))
if (params.sortBy) queryParams.append('sortBy', params.sortBy)
if (params.sortDirection) queryParams.append('sortDirection', params.sortDirection)
if (params.side) queryParams.append('side', params.side)
return `${buildDataUrl('/activity')}?${queryParams.toString()}`
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handlePolymarketError(data, response.status, 'get_activity')
}
const activityList = Array.isArray(data) ? data : []
const activity: PolymarketActivity[] = activityList.map((a: any) => ({
proxyWallet: a.proxyWallet ?? null,
timestamp: a.timestamp ?? 0,
conditionId: a.conditionId ?? '',
type: a.type ?? '',
size: a.size ?? 0,
usdcSize: a.usdcSize ?? 0,
transactionHash: a.transactionHash ?? null,
price: a.price ?? null,
asset: a.asset ?? null,
side: a.side ?? null,
outcomeIndex: a.outcomeIndex ?? null,
title: a.title ?? null,
slug: a.slug ?? null,
icon: a.icon ?? null,
eventSlug: a.eventSlug ?? null,
outcome: a.outcome ?? null,
name: a.name ?? null,
pseudonym: a.pseudonym ?? null,
bio: a.bio ?? null,
profileImage: a.profileImage ?? null,
profileImageOptimized: a.profileImageOptimized ?? null,
}))
return {
success: true,
output: {
activity,
},
}
},
outputs: {
activity: {
type: 'array',
description: 'Array of activity entries',
items: {
type: 'object',
properties: {
proxyWallet: { type: 'string', description: 'User proxy wallet address' },
timestamp: { type: 'number', description: 'Unix timestamp of activity' },
conditionId: { type: 'string', description: 'Market condition ID' },
type: {
type: 'string',
description: 'Activity type (TRADE, SPLIT, MERGE, REDEEM, REWARD, CONVERSION)',
},
size: { type: 'number', description: 'Size in tokens' },
usdcSize: { type: 'number', description: 'Size in USDC' },
transactionHash: { type: 'string', description: 'Blockchain transaction hash' },
price: { type: 'number', description: 'Price (for trades)' },
asset: { type: 'string', description: 'Asset/token ID' },
side: { type: 'string', description: 'Trade side (BUY/SELL)' },
outcomeIndex: { type: 'number', description: 'Outcome index' },
title: { type: 'string', description: 'Market title' },
slug: { type: 'string', description: 'Market slug' },
icon: { type: 'string', description: 'Market icon URL' },
eventSlug: { type: 'string', description: 'Event slug' },
outcome: { type: 'string', description: 'Outcome name' },
name: { type: 'string', description: 'User display name' },
pseudonym: { type: 'string', description: 'User pseudonym' },
bio: { type: 'string', description: 'User bio' },
profileImage: { type: 'string', description: 'User profile image URL' },
profileImageOptimized: { type: 'string', description: 'Optimized profile image URL' },
},
},
},
},
}

View File

@@ -1,6 +1,6 @@
import type { PolymarketEvent } from '@/tools/polymarket/types'
import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketEvent } from './types'
import { buildGammaUrl, handlePolymarketError } from './types'
export interface PolymarketGetEventParams {
eventId?: string // Event ID
@@ -71,26 +71,6 @@ export const polymarketGetEventTool: ToolConfig<
event: {
type: 'object',
description: 'Event object with details',
properties: {
id: { type: 'string', description: 'Event ID' },
ticker: { type: 'string', description: 'Event ticker' },
slug: { type: 'string', description: 'Event slug' },
title: { type: 'string', description: 'Event title' },
description: { type: 'string', description: 'Event description' },
startDate: { type: 'string', description: 'Start date' },
creationDate: { type: 'string', description: 'Creation date' },
endDate: { type: 'string', description: 'End date' },
image: { type: 'string', description: 'Event image URL' },
icon: { type: 'string', description: 'Event icon URL' },
active: { type: 'boolean', description: 'Whether event is active' },
closed: { type: 'boolean', description: 'Whether event is closed' },
archived: { type: 'boolean', description: 'Whether event is archived' },
liquidity: { type: 'number', description: 'Total liquidity' },
volume: { type: 'number', description: 'Total volume' },
openInterest: { type: 'number', description: 'Open interest' },
commentCount: { type: 'number', description: 'Comment count' },
markets: { type: 'array', description: 'Array of markets in this event' },
},
},
},
}

View File

@@ -1,12 +1,12 @@
import type { PolymarketEvent, PolymarketPaginationParams } from '@/tools/polymarket/types'
import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketEvent, PolymarketPaginationParams } from './types'
import { buildGammaUrl, handlePolymarketError } from './types'
export interface PolymarketGetEventsParams extends PolymarketPaginationParams {
closed?: string
order?: string
ascending?: string
tagId?: string
closed?: string // 'true' or 'false' - filter for closed/active events
order?: string // sort field (e.g., 'volume', 'liquidity', 'startDate', 'endDate')
ascending?: string // 'true' or 'false' - sort direction
tagId?: string // filter by tag ID
}
export interface PolymarketGetEventsResponse {
@@ -29,7 +29,7 @@ export const polymarketGetEventsTool: ToolConfig<
closed: {
type: 'string',
required: false,
description: 'Filter by closed status (true/false). Use false for open events only.',
description: 'Filter by closed status (true/false). Use false for active events only.',
visibility: 'user-or-llm',
},
order: {
@@ -71,11 +71,13 @@ export const polymarketGetEventsTool: ToolConfig<
if (params.order) queryParams.append('order', params.order)
if (params.ascending) queryParams.append('ascending', params.ascending)
if (params.tagId) queryParams.append('tag_id', params.tagId)
// Default limit to 50 to prevent browser crashes from large data sets
queryParams.append('limit', params.limit || '50')
if (params.offset) queryParams.append('offset', params.offset)
const query = queryParams.toString()
const url = buildGammaUrl('/events')
return `${url}?${queryParams.toString()}`
return `${url}?${query}`
},
method: 'GET',
headers: () => ({
@@ -90,6 +92,7 @@ export const polymarketGetEventsTool: ToolConfig<
handlePolymarketError(data, response.status, 'get_events')
}
// Response is an array of events
const events = Array.isArray(data) ? data : []
return {
@@ -104,26 +107,6 @@ export const polymarketGetEventsTool: ToolConfig<
events: {
type: 'array',
description: 'Array of event objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Event ID' },
ticker: { type: 'string', description: 'Event ticker' },
slug: { type: 'string', description: 'Event slug' },
title: { type: 'string', description: 'Event title' },
description: { type: 'string', description: 'Event description' },
startDate: { type: 'string', description: 'Start date' },
endDate: { type: 'string', description: 'End date' },
image: { type: 'string', description: 'Event image URL' },
icon: { type: 'string', description: 'Event icon URL' },
active: { type: 'boolean', description: 'Whether event is active' },
closed: { type: 'boolean', description: 'Whether event is closed' },
archived: { type: 'boolean', description: 'Whether event is archived' },
liquidity: { type: 'number', description: 'Total liquidity' },
volume: { type: 'number', description: 'Total volume' },
markets: { type: 'array', description: 'Array of markets in this event' },
},
},
},
},
}

View File

@@ -1,132 +0,0 @@
import type { PolymarketMarketHolders } from '@/tools/polymarket/types'
import { buildDataUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
export interface PolymarketGetHoldersParams {
market: string
limit?: string
minBalance?: string
}
export interface PolymarketGetHoldersResponse {
success: boolean
output: {
holders: PolymarketMarketHolders[]
}
}
export const polymarketGetHoldersTool: ToolConfig<
PolymarketGetHoldersParams,
PolymarketGetHoldersResponse
> = {
id: 'polymarket_get_holders',
name: 'Get Market Holders from Polymarket',
description: 'Retrieve top holders of a specific market token',
version: '1.0.0',
params: {
market: {
type: 'string',
required: true,
description: 'Comma-separated list of condition IDs',
visibility: 'user-or-llm',
},
limit: {
type: 'string',
required: false,
description: 'Number of holders to return (0-20, default: 20)',
visibility: 'user-or-llm',
},
minBalance: {
type: 'string',
required: false,
description: 'Minimum balance threshold (default: 1)',
visibility: 'user-or-llm',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
queryParams.append('market', params.market)
if (params.limit) queryParams.append('limit', params.limit)
if (params.minBalance) queryParams.append('minBalance', params.minBalance)
return `${buildDataUrl('/holders')}?${queryParams.toString()}`
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handlePolymarketError(data, response.status, 'get_holders')
}
const marketHolders = Array.isArray(data) ? data : []
const holders: PolymarketMarketHolders[] = marketHolders.map((mh: any) => ({
token: mh.token ?? '',
holders: (mh.holders ?? []).map((h: any) => ({
proxyWallet: h.proxyWallet ?? '',
bio: h.bio ?? null,
asset: h.asset ?? '',
pseudonym: h.pseudonym ?? null,
amount: h.amount ?? 0,
displayUsernamePublic: h.displayUsernamePublic ?? false,
outcomeIndex: h.outcomeIndex ?? 0,
name: h.name ?? null,
profileImage: h.profileImage ?? null,
profileImageOptimized: h.profileImageOptimized ?? null,
})),
}))
return {
success: true,
output: {
holders,
},
}
},
outputs: {
holders: {
type: 'array',
description: 'Array of market holder groups by token',
items: {
type: 'object',
properties: {
token: { type: 'string', description: 'Token/asset ID' },
holders: {
type: 'array',
description: 'Array of holders for this token',
items: {
type: 'object',
properties: {
proxyWallet: { type: 'string', description: 'Holder wallet address' },
bio: { type: 'string', description: 'Holder bio' },
asset: { type: 'string', description: 'Asset ID' },
pseudonym: { type: 'string', description: 'Holder pseudonym' },
amount: { type: 'number', description: 'Amount held' },
displayUsernamePublic: {
type: 'boolean',
description: 'Whether username is publicly displayed',
},
outcomeIndex: { type: 'number', description: 'Outcome index' },
name: { type: 'string', description: 'Holder display name' },
profileImage: { type: 'string', description: 'Profile image URL' },
profileImageOptimized: {
type: 'string',
description: 'Optimized profile image URL',
},
},
},
},
},
},
},
},
}

View File

@@ -1,5 +1,5 @@
import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import { buildClobUrl, handlePolymarketError } from './types'
export interface PolymarketGetLastTradePriceParams {
tokenId: string // The token ID (CLOB token ID from market)
@@ -9,7 +9,6 @@ export interface PolymarketGetLastTradePriceResponse {
success: boolean
output: {
price: string
side: string
}
}
@@ -53,8 +52,7 @@ export const polymarketGetLastTradePriceTool: ToolConfig<
return {
success: true,
output: {
price: data.price ?? '',
side: data.side ?? '',
price: typeof data === 'string' ? data : data.price || '',
},
}
},
@@ -64,9 +62,5 @@ export const polymarketGetLastTradePriceTool: ToolConfig<
type: 'string',
description: 'Last trade price',
},
side: {
type: 'string',
description: 'Side of the last trade (BUY or SELL)',
},
},
}

View File

@@ -1,143 +0,0 @@
import type { PolymarketLeaderboardEntry } from '@/tools/polymarket/types'
import { buildDataUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
export interface PolymarketGetLeaderboardParams {
category?: string
timePeriod?: string
orderBy?: string
limit?: string
offset?: string
user?: string
userName?: string
}
export interface PolymarketGetLeaderboardResponse {
success: boolean
output: {
leaderboard: PolymarketLeaderboardEntry[]
}
}
export const polymarketGetLeaderboardTool: ToolConfig<
PolymarketGetLeaderboardParams,
PolymarketGetLeaderboardResponse
> = {
id: 'polymarket_get_leaderboard',
name: 'Get Leaderboard from Polymarket',
description: 'Retrieve trader leaderboard rankings by profit/loss or volume',
version: '1.0.0',
params: {
category: {
type: 'string',
required: false,
description:
'Category filter: OVERALL, POLITICS, SPORTS, CRYPTO, CULTURE, MENTIONS, WEATHER, ECONOMICS, TECH, FINANCE (default: OVERALL)',
visibility: 'user-or-llm',
},
timePeriod: {
type: 'string',
required: false,
description: 'Time period: DAY, WEEK, MONTH, ALL (default: DAY)',
visibility: 'user-or-llm',
},
orderBy: {
type: 'string',
required: false,
description: 'Order by: PNL or VOL (default: PNL)',
visibility: 'user-or-llm',
},
limit: {
type: 'string',
required: false,
description: 'Number of results (1-50, default: 25)',
visibility: 'user-or-llm',
},
offset: {
type: 'string',
required: false,
description: 'Pagination offset (0-1000, default: 0)',
visibility: 'user-or-llm',
},
user: {
type: 'string',
required: false,
description: 'Filter by specific user wallet address',
visibility: 'user-or-llm',
},
userName: {
type: 'string',
required: false,
description: 'Filter by username',
visibility: 'user-or-llm',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.category) queryParams.append('category', params.category)
if (params.timePeriod) queryParams.append('timePeriod', params.timePeriod)
if (params.orderBy) queryParams.append('orderBy', params.orderBy)
if (params.limit) queryParams.append('limit', params.limit)
if (params.offset) queryParams.append('offset', params.offset)
if (params.user) queryParams.append('user', params.user)
if (params.userName) queryParams.append('userName', params.userName)
const query = queryParams.toString()
return query ? `${buildDataUrl('/v1/leaderboard')}?${query}` : buildDataUrl('/v1/leaderboard')
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handlePolymarketError(data, response.status, 'get_leaderboard')
}
const entries = Array.isArray(data) ? data : []
const leaderboard: PolymarketLeaderboardEntry[] = entries.map((entry: any) => ({
rank: entry.rank ?? '',
proxyWallet: entry.proxyWallet ?? '',
userName: entry.userName ?? null,
vol: entry.vol ?? 0,
pnl: entry.pnl ?? 0,
profileImage: entry.profileImage ?? null,
xUsername: entry.xUsername ?? null,
verifiedBadge: entry.verifiedBadge ?? false,
}))
return {
success: true,
output: {
leaderboard,
},
}
},
outputs: {
leaderboard: {
type: 'array',
description: 'Array of leaderboard entries',
items: {
type: 'object',
properties: {
rank: { type: 'string', description: 'Leaderboard rank position' },
proxyWallet: { type: 'string', description: 'User proxy wallet address' },
userName: { type: 'string', description: 'User display name' },
vol: { type: 'number', description: 'Trading volume' },
pnl: { type: 'number', description: 'Profit and loss' },
profileImage: { type: 'string', description: 'User profile image URL' },
xUsername: { type: 'string', description: 'Twitter/X username' },
verifiedBadge: { type: 'boolean', description: 'Whether user has verified badge' },
},
},
},
},
}

View File

@@ -1,6 +1,6 @@
import type { PolymarketMarket } from '@/tools/polymarket/types'
import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketMarket } from './types'
import { buildGammaUrl, handlePolymarketError } from './types'
export interface PolymarketGetMarketParams {
marketId?: string // Market ID
@@ -71,30 +71,6 @@ export const polymarketGetMarketTool: ToolConfig<
market: {
type: 'object',
description: 'Market object with details',
properties: {
id: { type: 'string', description: 'Market ID' },
question: { type: 'string', description: 'Market question' },
conditionId: { type: 'string', description: 'Condition ID' },
slug: { type: 'string', description: 'Market slug' },
resolutionSource: { type: 'string', description: 'Resolution source' },
endDate: { type: 'string', description: 'End date' },
startDate: { type: 'string', description: 'Start date' },
image: { type: 'string', description: 'Market image URL' },
icon: { type: 'string', description: 'Market icon URL' },
description: { type: 'string', description: 'Market description' },
outcomes: { type: 'string', description: 'Outcomes JSON string' },
outcomePrices: { type: 'string', description: 'Outcome prices JSON string' },
volume: { type: 'string', description: 'Total volume' },
liquidity: { type: 'string', description: 'Total liquidity' },
active: { type: 'boolean', description: 'Whether market is active' },
closed: { type: 'boolean', description: 'Whether market is closed' },
archived: { type: 'boolean', description: 'Whether market is archived' },
volumeNum: { type: 'number', description: 'Volume as number' },
liquidityNum: { type: 'number', description: 'Liquidity as number' },
clobTokenIds: { type: 'array', description: 'CLOB token IDs' },
acceptingOrders: { type: 'boolean', description: 'Whether accepting orders' },
negRisk: { type: 'boolean', description: 'Whether negative risk' },
},
},
},
}

View File

@@ -1,12 +1,12 @@
import type { PolymarketMarket, PolymarketPaginationParams } from '@/tools/polymarket/types'
import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketMarket, PolymarketPaginationParams } from './types'
import { buildGammaUrl, handlePolymarketError } from './types'
export interface PolymarketGetMarketsParams extends PolymarketPaginationParams {
closed?: string
order?: string
ascending?: string
tagId?: string
closed?: string // 'true' or 'false' - filter for closed/active markets
order?: string // sort field - use camelCase (e.g., 'volumeNum', 'liquidityNum', 'startDate', 'endDate')
ascending?: string // 'true' or 'false' - sort direction
tagId?: string // filter by tag ID
}
export interface PolymarketGetMarketsResponse {
@@ -29,7 +29,7 @@ export const polymarketGetMarketsTool: ToolConfig<
closed: {
type: 'string',
required: false,
description: 'Filter by closed status (true/false). Use false for open markets only.',
description: 'Filter by closed status (true/false). Use false for active markets only.',
visibility: 'user-or-llm',
},
order: {
@@ -71,11 +71,13 @@ export const polymarketGetMarketsTool: ToolConfig<
if (params.order) queryParams.append('order', params.order)
if (params.ascending) queryParams.append('ascending', params.ascending)
if (params.tagId) queryParams.append('tag_id', params.tagId)
// Default limit to 50 to prevent browser crashes from large data sets
queryParams.append('limit', params.limit || '50')
if (params.offset) queryParams.append('offset', params.offset)
const query = queryParams.toString()
const url = buildGammaUrl('/markets')
return `${url}?${queryParams.toString()}`
return `${url}?${query}`
},
method: 'GET',
headers: () => ({
@@ -105,26 +107,6 @@ export const polymarketGetMarketsTool: ToolConfig<
markets: {
type: 'array',
description: 'Array of market objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Market ID' },
question: { type: 'string', description: 'Market question' },
conditionId: { type: 'string', description: 'Condition ID' },
slug: { type: 'string', description: 'Market slug' },
endDate: { type: 'string', description: 'End date' },
image: { type: 'string', description: 'Market image URL' },
outcomes: { type: 'string', description: 'Outcomes JSON string' },
outcomePrices: { type: 'string', description: 'Outcome prices JSON string' },
volume: { type: 'string', description: 'Total volume' },
liquidity: { type: 'string', description: 'Total liquidity' },
active: { type: 'boolean', description: 'Whether market is active' },
closed: { type: 'boolean', description: 'Whether market is closed' },
volumeNum: { type: 'number', description: 'Volume as number' },
liquidityNum: { type: 'number', description: 'Liquidity as number' },
clobTokenIds: { type: 'array', description: 'CLOB token IDs' },
},
},
},
},
}

View File

@@ -1,5 +1,5 @@
import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import { buildClobUrl, handlePolymarketError } from './types'
export interface PolymarketGetMidpointParams {
tokenId: string // The token ID (CLOB token ID from market)

View File

@@ -1,6 +1,6 @@
import type { PolymarketOrderBook } from '@/tools/polymarket/types'
import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketOrderBook } from './types'
import { buildClobUrl, handlePolymarketError } from './types'
export interface PolymarketGetOrderbookParams {
tokenId: string // The token ID (CLOB token ID from market)
@@ -50,22 +50,10 @@ export const polymarketGetOrderbookTool: ToolConfig<
handlePolymarketError(data, response.status, 'get_orderbook')
}
const orderbook: PolymarketOrderBook = {
market: data.market ?? '',
asset_id: data.asset_id ?? '',
hash: data.hash ?? '',
timestamp: data.timestamp ?? '',
bids: data.bids ?? [],
asks: data.asks ?? [],
min_order_size: data.min_order_size ?? '0',
tick_size: data.tick_size ?? '0',
neg_risk: data.neg_risk ?? false,
}
return {
success: true,
output: {
orderbook,
orderbook: data,
},
}
},
@@ -74,37 +62,6 @@ export const polymarketGetOrderbookTool: ToolConfig<
orderbook: {
type: 'object',
description: 'Order book with bids and asks arrays',
properties: {
market: { type: 'string', description: 'Market identifier' },
asset_id: { type: 'string', description: 'Asset token ID' },
hash: { type: 'string', description: 'Order book hash' },
timestamp: { type: 'string', description: 'Timestamp' },
bids: {
type: 'array',
description: 'Bid orders',
items: {
type: 'object',
properties: {
price: { type: 'string', description: 'Bid price' },
size: { type: 'string', description: 'Bid size' },
},
},
},
asks: {
type: 'array',
description: 'Ask orders',
items: {
type: 'object',
properties: {
price: { type: 'string', description: 'Ask price' },
size: { type: 'string', description: 'Ask size' },
},
},
},
min_order_size: { type: 'string', description: 'Minimum order size' },
tick_size: { type: 'string', description: 'Tick size' },
neg_risk: { type: 'boolean', description: 'Whether negative risk' },
},
},
},
}

View File

@@ -1,19 +1,10 @@
import type { PolymarketPosition } from '@/tools/polymarket/types'
import { buildDataUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketPosition } from './types'
import { buildDataUrl, handlePolymarketError } from './types'
export interface PolymarketGetPositionsParams {
user: string
market?: string
eventId?: string
sizeThreshold?: string
redeemable?: string
mergeable?: string
sortBy?: string
sortDirection?: string
title?: string
limit?: string
offset?: string
user: string // Wallet address (required)
market?: string // Optional market filter
}
export interface PolymarketGetPositionsResponse {
@@ -42,63 +33,7 @@ export const polymarketGetPositionsTool: ToolConfig<
market: {
type: 'string',
required: false,
description:
'Condition IDs to filter positions (comma-separated, mutually exclusive with eventId)',
visibility: 'user-or-llm',
},
eventId: {
type: 'string',
required: false,
description: 'Event ID to filter positions (mutually exclusive with market)',
visibility: 'user-or-llm',
},
sizeThreshold: {
type: 'string',
required: false,
description: 'Minimum position size threshold (default: 1)',
visibility: 'user-or-llm',
},
redeemable: {
type: 'string',
required: false,
description: 'Filter for redeemable positions only (true/false)',
visibility: 'user-or-llm',
},
mergeable: {
type: 'string',
required: false,
description: 'Filter for mergeable positions only (true/false)',
visibility: 'user-or-llm',
},
sortBy: {
type: 'string',
required: false,
description:
'Sort field (TOKENS, CURRENT, INITIAL, CASHPNL, PERCENTPNL, TITLE, RESOLVING, PRICE, AVGPRICE)',
visibility: 'user-or-llm',
},
sortDirection: {
type: 'string',
required: false,
description: 'Sort direction (ASC or DESC)',
visibility: 'user-or-llm',
},
title: {
type: 'string',
required: false,
description: 'Search filter by title',
visibility: 'user-or-llm',
},
limit: {
type: 'string',
required: false,
description: 'Number of results per page',
visibility: 'user-or-llm',
},
offset: {
type: 'string',
required: false,
description: 'Pagination offset',
description: 'Optional market ID to filter positions',
visibility: 'user-or-llm',
},
},
@@ -108,15 +43,6 @@ export const polymarketGetPositionsTool: ToolConfig<
const queryParams = new URLSearchParams()
queryParams.append('user', params.user)
if (params.market) queryParams.append('market', params.market)
if (params.eventId) queryParams.append('eventId', params.eventId)
if (params.sizeThreshold) queryParams.append('sizeThreshold', params.sizeThreshold)
if (params.redeemable) queryParams.append('redeemable', params.redeemable)
if (params.mergeable) queryParams.append('mergeable', params.mergeable)
if (params.sortBy) queryParams.append('sortBy', params.sortBy)
if (params.sortDirection) queryParams.append('sortDirection', params.sortDirection)
if (params.title) queryParams.append('title', params.title)
if (params.limit) queryParams.append('limit', params.limit)
if (params.offset) queryParams.append('offset', params.offset)
return `${buildDataUrl('/positions')}?${queryParams.toString()}`
},
@@ -133,34 +59,8 @@ export const polymarketGetPositionsTool: ToolConfig<
handlePolymarketError(data, response.status, 'get_positions')
}
const rawPositions = Array.isArray(data) ? data : []
const positions: PolymarketPosition[] = rawPositions.map((p: Record<string, unknown>) => ({
proxyWallet: (p.proxyWallet as string) ?? null,
asset: (p.asset as string) ?? '',
conditionId: (p.conditionId as string) ?? '',
size: (p.size as number) ?? 0,
avgPrice: (p.avgPrice as number) ?? 0,
initialValue: (p.initialValue as number) ?? 0,
currentValue: (p.currentValue as number) ?? 0,
cashPnl: (p.cashPnl as number) ?? 0,
percentPnl: (p.percentPnl as number) ?? 0,
totalBought: (p.totalBought as number) ?? 0,
realizedPnl: (p.realizedPnl as number) ?? 0,
percentRealizedPnl: (p.percentRealizedPnl as number) ?? 0,
curPrice: (p.curPrice as number) ?? 0,
redeemable: (p.redeemable as boolean) ?? false,
mergeable: (p.mergeable as boolean) ?? false,
title: (p.title as string) ?? null,
slug: (p.slug as string) ?? null,
icon: (p.icon as string) ?? null,
eventSlug: (p.eventSlug as string) ?? null,
outcome: (p.outcome as string) ?? null,
outcomeIndex: (p.outcomeIndex as number) ?? null,
oppositeOutcome: (p.oppositeOutcome as string) ?? null,
oppositeAsset: (p.oppositeAsset as string) ?? null,
endDate: (p.endDate as string) ?? null,
negativeRisk: (p.negativeRisk as boolean) ?? false,
}))
// Response is an array of positions
const positions = Array.isArray(data) ? data : []
return {
success: true,
@@ -174,36 +74,6 @@ export const polymarketGetPositionsTool: ToolConfig<
positions: {
type: 'array',
description: 'Array of position objects',
items: {
type: 'object',
properties: {
proxyWallet: { type: 'string', description: 'Proxy wallet address' },
asset: { type: 'string', description: 'Asset token ID' },
conditionId: { type: 'string', description: 'Condition ID' },
size: { type: 'number', description: 'Position size' },
avgPrice: { type: 'number', description: 'Average price' },
initialValue: { type: 'number', description: 'Initial value' },
currentValue: { type: 'number', description: 'Current value' },
cashPnl: { type: 'number', description: 'Cash profit/loss' },
percentPnl: { type: 'number', description: 'Percent profit/loss' },
totalBought: { type: 'number', description: 'Total bought' },
realizedPnl: { type: 'number', description: 'Realized profit/loss' },
percentRealizedPnl: { type: 'number', description: 'Percent realized profit/loss' },
curPrice: { type: 'number', description: 'Current price' },
redeemable: { type: 'boolean', description: 'Whether position is redeemable' },
mergeable: { type: 'boolean', description: 'Whether position is mergeable' },
title: { type: 'string', description: 'Market title' },
slug: { type: 'string', description: 'Market slug' },
icon: { type: 'string', description: 'Market icon URL' },
eventSlug: { type: 'string', description: 'Event slug' },
outcome: { type: 'string', description: 'Outcome name' },
outcomeIndex: { type: 'number', description: 'Outcome index' },
oppositeOutcome: { type: 'string', description: 'Opposite outcome name' },
oppositeAsset: { type: 'string', description: 'Opposite asset token ID' },
endDate: { type: 'string', description: 'End date' },
negativeRisk: { type: 'boolean', description: 'Whether negative risk' },
},
},
},
},
}

View File

@@ -1,5 +1,5 @@
import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import { buildClobUrl, handlePolymarketError } from './types'
export interface PolymarketGetPriceParams {
tokenId: string // The token ID (CLOB token ID from market)

View File

@@ -1,6 +1,6 @@
import type { PolymarketPriceHistoryEntry } from '@/tools/polymarket/types'
import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketPriceHistoryEntry } from './types'
import { buildClobUrl, handlePolymarketError } from './types'
export interface PolymarketGetPriceHistoryParams {
tokenId: string
@@ -99,14 +99,7 @@ export const polymarketGetPriceHistoryTool: ToolConfig<
outputs: {
history: {
type: 'array',
description: 'Array of price history entries',
items: {
type: 'object',
properties: {
t: { type: 'number', description: 'Unix timestamp' },
p: { type: 'number', description: 'Price at timestamp' },
},
},
description: 'Array of price history entries with timestamp (t) and price (p)',
},
},
}

View File

@@ -1,6 +1,6 @@
import type { PolymarketPaginationParams, PolymarketSeries } from '@/tools/polymarket/types'
import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketPaginationParams, PolymarketSeries } from './types'
import { buildGammaUrl, handlePolymarketError } from './types'
export interface PolymarketGetSeriesParams extends PolymarketPaginationParams {}
@@ -97,26 +97,6 @@ export const polymarketGetSeriesTool: ToolConfig<
series: {
type: 'array',
description: 'Array of series objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Series ID' },
ticker: { type: 'string', description: 'Series ticker' },
slug: { type: 'string', description: 'Series slug' },
title: { type: 'string', description: 'Series title' },
seriesType: { type: 'string', description: 'Series type' },
recurrence: { type: 'string', description: 'Recurrence pattern' },
image: { type: 'string', description: 'Series image URL' },
icon: { type: 'string', description: 'Series icon URL' },
active: { type: 'boolean', description: 'Whether series is active' },
closed: { type: 'boolean', description: 'Whether series is closed' },
archived: { type: 'boolean', description: 'Whether series is archived' },
featured: { type: 'boolean', description: 'Whether series is featured' },
volume: { type: 'number', description: 'Total volume' },
liquidity: { type: 'number', description: 'Total liquidity' },
eventCount: { type: 'number', description: 'Number of events in series' },
},
},
},
},
}

View File

@@ -1,6 +1,6 @@
import type { PolymarketSeries } from '@/tools/polymarket/types'
import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketSeries } from './types'
import { buildGammaUrl, handlePolymarketError } from './types'
export interface PolymarketGetSeriesByIdParams {
seriesId: string // Series ID (required)
@@ -58,25 +58,6 @@ export const polymarketGetSeriesByIdTool: ToolConfig<
series: {
type: 'object',
description: 'Series object with details',
properties: {
id: { type: 'string', description: 'Series ID' },
ticker: { type: 'string', description: 'Series ticker' },
slug: { type: 'string', description: 'Series slug' },
title: { type: 'string', description: 'Series title' },
seriesType: { type: 'string', description: 'Series type' },
recurrence: { type: 'string', description: 'Recurrence pattern' },
image: { type: 'string', description: 'Series image URL' },
icon: { type: 'string', description: 'Series icon URL' },
active: { type: 'boolean', description: 'Whether series is active' },
closed: { type: 'boolean', description: 'Whether series is closed' },
archived: { type: 'boolean', description: 'Whether series is archived' },
featured: { type: 'boolean', description: 'Whether series is featured' },
volume: { type: 'number', description: 'Total volume' },
liquidity: { type: 'number', description: 'Total liquidity' },
commentCount: { type: 'number', description: 'Comment count' },
eventCount: { type: 'number', description: 'Number of events in series' },
events: { type: 'array', description: 'Array of events in this series' },
},
},
},
}

View File

@@ -1,6 +1,6 @@
import type { PolymarketSpread } from '@/tools/polymarket/types'
import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketSpread } from './types'
import { buildClobUrl, handlePolymarketError } from './types'
export interface PolymarketGetSpreadParams {
tokenId: string // The token ID (CLOB token ID from market)
@@ -53,9 +53,7 @@ export const polymarketGetSpreadTool: ToolConfig<
return {
success: true,
output: {
spread: {
spread: data.spread ?? '',
},
spread: data,
},
}
},
@@ -63,10 +61,7 @@ export const polymarketGetSpreadTool: ToolConfig<
outputs: {
spread: {
type: 'object',
description: 'Spread value between bid and ask',
properties: {
spread: { type: 'string', description: 'The spread value' },
},
description: 'Bid-ask spread with bid and ask prices',
},
},
}

View File

@@ -1,6 +1,6 @@
import type { PolymarketPaginationParams, PolymarketTag } from '@/tools/polymarket/types'
import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketPaginationParams, PolymarketTag } from './types'
import { buildGammaUrl, handlePolymarketError } from './types'
export interface PolymarketGetTagsParams extends PolymarketPaginationParams {}
@@ -71,17 +71,7 @@ export const polymarketGetTagsTool: ToolConfig<PolymarketGetTagsParams, Polymark
outputs: {
tags: {
type: 'array',
description: 'Array of tag objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Tag ID' },
label: { type: 'string', description: 'Tag label' },
slug: { type: 'string', description: 'Tag slug' },
createdAt: { type: 'string', description: 'Creation timestamp' },
updatedAt: { type: 'string', description: 'Last update timestamp' },
},
},
description: 'Array of tag objects with id, label, and slug',
},
},
}

View File

@@ -1,5 +1,5 @@
import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import { buildClobUrl, handlePolymarketError } from './types'
export interface PolymarketGetTickSizeParams {
tokenId: string // The token ID (CLOB token ID from market)

View File

@@ -1,17 +1,10 @@
import type { PolymarketTrade } from '@/tools/polymarket/types'
import { buildDataUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketPaginationParams, PolymarketTrade } from './types'
import { buildDataUrl, handlePolymarketError } from './types'
export interface PolymarketGetTradesParams {
user?: string
market?: string
eventId?: string
side?: string
takerOnly?: string
filterType?: string
filterAmount?: string
limit?: string
offset?: string
export interface PolymarketGetTradesParams extends PolymarketPaginationParams {
user?: string // Optional user wallet address
market?: string // Optional market filter
}
export interface PolymarketGetTradesResponse {
@@ -40,43 +33,13 @@ export const polymarketGetTradesTool: ToolConfig<
market: {
type: 'string',
required: false,
description: 'Market/condition ID to filter trades (mutually exclusive with eventId)',
visibility: 'user-or-llm',
},
eventId: {
type: 'string',
required: false,
description: 'Event ID to filter trades (mutually exclusive with market)',
visibility: 'user-or-llm',
},
side: {
type: 'string',
required: false,
description: 'Trade direction filter (BUY or SELL)',
visibility: 'user-or-llm',
},
takerOnly: {
type: 'string',
required: false,
description: 'Filter for taker trades only (true/false, default: true)',
visibility: 'user-or-llm',
},
filterType: {
type: 'string',
required: false,
description: 'Filter type (CASH or TOKENS) - requires filterAmount',
visibility: 'user-or-llm',
},
filterAmount: {
type: 'string',
required: false,
description: 'Filter amount threshold - requires filterType',
description: 'Market ID to filter trades',
visibility: 'user-or-llm',
},
limit: {
type: 'string',
required: false,
description: 'Number of results per page (default: 100, max: 10000)',
description: 'Number of results per page (max 50)',
visibility: 'user-or-llm',
},
offset: {
@@ -92,16 +55,13 @@ export const polymarketGetTradesTool: ToolConfig<
const queryParams = new URLSearchParams()
if (params.user) queryParams.append('user', params.user)
if (params.market) queryParams.append('market', params.market)
if (params.eventId) queryParams.append('eventId', params.eventId)
if (params.side) queryParams.append('side', params.side.toUpperCase())
if (params.takerOnly) queryParams.append('takerOnly', params.takerOnly)
if (params.filterType) queryParams.append('filterType', params.filterType.toUpperCase())
if (params.filterAmount) queryParams.append('filterAmount', params.filterAmount)
if (params.limit) queryParams.append('limit', params.limit)
// Default limit to 50 to prevent browser crashes from large data sets
queryParams.append('limit', params.limit || '50')
if (params.offset) queryParams.append('offset', params.offset)
const query = queryParams.toString()
const url = buildDataUrl('/trades')
return `${url}?${queryParams.toString()}`
return `${url}?${query}`
},
method: 'GET',
headers: () => ({
@@ -116,28 +76,8 @@ export const polymarketGetTradesTool: ToolConfig<
handlePolymarketError(data, response.status, 'get_trades')
}
const rawTrades = Array.isArray(data) ? data : []
const trades: PolymarketTrade[] = rawTrades.map((t: Record<string, unknown>) => ({
proxyWallet: (t.proxyWallet as string) ?? null,
side: (t.side as string) ?? '',
asset: (t.asset as string) ?? '',
conditionId: (t.conditionId as string) ?? '',
size: (t.size as number) ?? 0,
price: (t.price as number) ?? 0,
timestamp: (t.timestamp as number) ?? 0,
title: (t.title as string) ?? null,
slug: (t.slug as string) ?? null,
icon: (t.icon as string) ?? null,
eventSlug: (t.eventSlug as string) ?? null,
outcome: (t.outcome as string) ?? null,
outcomeIndex: (t.outcomeIndex as number) ?? null,
name: (t.name as string) ?? null,
pseudonym: (t.pseudonym as string) ?? null,
bio: (t.bio as string) ?? null,
profileImage: (t.profileImage as string) ?? null,
profileImageOptimized: (t.profileImageOptimized as string) ?? null,
transactionHash: (t.transactionHash as string) ?? null,
}))
// Response is an array of trades
const trades = Array.isArray(data) ? data : []
return {
success: true,
@@ -151,30 +91,6 @@ export const polymarketGetTradesTool: ToolConfig<
trades: {
type: 'array',
description: 'Array of trade objects',
items: {
type: 'object',
properties: {
proxyWallet: { type: 'string', description: 'Proxy wallet address' },
side: { type: 'string', description: 'Trade side (BUY or SELL)' },
asset: { type: 'string', description: 'Asset token ID' },
conditionId: { type: 'string', description: 'Condition ID' },
size: { type: 'number', description: 'Trade size' },
price: { type: 'number', description: 'Trade price' },
timestamp: { type: 'number', description: 'Unix timestamp' },
title: { type: 'string', description: 'Market title' },
slug: { type: 'string', description: 'Market slug' },
icon: { type: 'string', description: 'Market icon URL' },
eventSlug: { type: 'string', description: 'Event slug' },
outcome: { type: 'string', description: 'Outcome name' },
outcomeIndex: { type: 'number', description: 'Outcome index' },
name: { type: 'string', description: 'Trader name' },
pseudonym: { type: 'string', description: 'Trader pseudonym' },
bio: { type: 'string', description: 'Trader bio' },
profileImage: { type: 'string', description: 'Profile image URL' },
profileImageOptimized: { type: 'string', description: 'Optimized profile image URL' },
transactionHash: { type: 'string', description: 'Transaction hash' },
},
},
},
},
}

View File

@@ -1,20 +1,18 @@
export { polymarketGetActivityTool } from './get_activity'
export { polymarketGetEventTool } from './get_event'
export { polymarketGetEventsTool } from './get_events'
export { polymarketGetHoldersTool } from './get_holders'
export { polymarketGetLastTradePriceTool } from './get_last_trade_price'
export { polymarketGetLeaderboardTool } from './get_leaderboard'
export { polymarketGetMarketTool } from './get_market'
export { polymarketGetMarketsTool } from './get_markets'
export { polymarketGetMidpointTool } from './get_midpoint'
export { polymarketGetOrderbookTool } from './get_orderbook'
export { polymarketGetPositionsTool } from './get_positions'
export { polymarketGetPriceTool } from './get_price'
export { polymarketGetPriceHistoryTool } from './get_price_history'
export { polymarketGetSeriesTool } from './get_series'
export { polymarketGetSeriesByIdTool } from './get_series_by_id'
export { polymarketGetSpreadTool } from './get_spread'
export { polymarketGetTagsTool } from './get_tags'
export { polymarketGetTickSizeTool } from './get_tick_size'
export { polymarketGetTradesTool } from './get_trades'
export { polymarketSearchTool } from './search'
export * from './get_event'
export * from './get_events'
export * from './get_last_trade_price'
export * from './get_market'
export * from './get_markets'
export * from './get_midpoint'
export * from './get_orderbook'
export * from './get_positions'
export * from './get_price'
export * from './get_price_history'
export * from './get_series'
export * from './get_series_by_id'
export * from './get_spread'
export * from './get_tags'
export * from './get_tick_size'
export * from './get_trades'
export * from './search'
export * from './types'

View File

@@ -1,22 +1,9 @@
import type { PolymarketSearchResult } from '@/tools/polymarket/types'
import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketPaginationParams, PolymarketSearchResult } from './types'
import { buildGammaUrl, handlePolymarketError } from './types'
export interface PolymarketSearchParams {
query: string
limit?: string
page?: string
cache?: string
eventsStatus?: string
limitPerType?: string
eventsTag?: string
sort?: string
ascending?: string
searchTags?: string
searchProfiles?: string
recurrence?: string
excludeTagId?: string
keepClosedMarkets?: string
export interface PolymarketSearchParams extends PolymarketPaginationParams {
query: string // Search term (required)
}
export interface PolymarketSearchResponse {
@@ -45,76 +32,10 @@ export const polymarketSearchTool: ToolConfig<PolymarketSearchParams, Polymarket
description: 'Number of results per page (max 50)',
visibility: 'user-or-llm',
},
page: {
offset: {
type: 'string',
required: false,
description: 'Page number for pagination (1-indexed)',
visibility: 'user-or-llm',
},
cache: {
type: 'string',
required: false,
description: 'Enable caching (true/false)',
visibility: 'user-or-llm',
},
eventsStatus: {
type: 'string',
required: false,
description: 'Filter events by status',
visibility: 'user-or-llm',
},
limitPerType: {
type: 'string',
required: false,
description: 'Limit results per type (markets, events, profiles)',
visibility: 'user-or-llm',
},
eventsTag: {
type: 'string',
required: false,
description: 'Filter by event tags (comma-separated)',
visibility: 'user-or-llm',
},
sort: {
type: 'string',
required: false,
description: 'Sort field',
visibility: 'user-or-llm',
},
ascending: {
type: 'string',
required: false,
description: 'Sort direction (true for ascending, false for descending)',
visibility: 'user-or-llm',
},
searchTags: {
type: 'string',
required: false,
description: 'Include tags in search results (true/false)',
visibility: 'user-or-llm',
},
searchProfiles: {
type: 'string',
required: false,
description: 'Include profiles in search results (true/false)',
visibility: 'user-or-llm',
},
recurrence: {
type: 'string',
required: false,
description: 'Filter by recurrence type',
visibility: 'user-or-llm',
},
excludeTagId: {
type: 'string',
required: false,
description: 'Exclude events with these tag IDs (comma-separated)',
visibility: 'user-or-llm',
},
keepClosedMarkets: {
type: 'string',
required: false,
description: 'Include closed markets in results (0 or 1)',
description: 'Pagination offset',
visibility: 'user-or-llm',
},
},
@@ -123,20 +44,9 @@ export const polymarketSearchTool: ToolConfig<PolymarketSearchParams, Polymarket
url: (params) => {
const queryParams = new URLSearchParams()
queryParams.append('q', params.query)
// Default limit to 50 to prevent browser crashes from large data sets
queryParams.append('limit', params.limit || '50')
if (params.page) queryParams.append('page', params.page)
if (params.cache) queryParams.append('cache', params.cache)
if (params.eventsStatus) queryParams.append('events_status', params.eventsStatus)
if (params.limitPerType) queryParams.append('limit_per_type', params.limitPerType)
if (params.eventsTag) queryParams.append('events_tag', params.eventsTag)
if (params.sort) queryParams.append('sort', params.sort)
if (params.ascending) queryParams.append('ascending', params.ascending)
if (params.searchTags) queryParams.append('search_tags', params.searchTags)
if (params.searchProfiles) queryParams.append('search_profiles', params.searchProfiles)
if (params.recurrence) queryParams.append('recurrence', params.recurrence)
if (params.excludeTagId) queryParams.append('exclude_tag_id', params.excludeTagId)
if (params.keepClosedMarkets)
queryParams.append('keep_closed_markets', params.keepClosedMarkets)
if (params.offset) queryParams.append('offset', params.offset)
return `${buildGammaUrl('/public-search')}?${queryParams.toString()}`
},
@@ -153,11 +63,11 @@ export const polymarketSearchTool: ToolConfig<PolymarketSearchParams, Polymarket
handlePolymarketError(data, response.status, 'search')
}
// Response contains markets, events, and profiles arrays
const results: PolymarketSearchResult = {
markets: data.markets ?? [],
events: data.events ?? [],
tags: data.tags ?? [],
profiles: data.profiles ?? [],
markets: data.markets || [],
events: data.events || [],
profiles: data.profiles || [],
}
return {
@@ -171,13 +81,7 @@ export const polymarketSearchTool: ToolConfig<PolymarketSearchParams, Polymarket
outputs: {
results: {
type: 'object',
description: 'Search results containing markets, events, tags, and profiles arrays',
properties: {
markets: { type: 'array', description: 'Array of matching market objects' },
events: { type: 'array', description: 'Array of matching event objects' },
tags: { type: 'array', description: 'Array of matching tag objects' },
profiles: { type: 'array', description: 'Array of matching profile objects' },
},
description: 'Search results containing markets, events, and profiles arrays',
},
},
}

View File

@@ -92,11 +92,6 @@ export interface PolymarketTag {
id: string
label: string
slug: string
createdAt?: string
updatedAt?: string
forceShow?: boolean
forceHide?: boolean
isCarousel?: boolean
}
export interface PolymarketOrderBookEntry {
@@ -111,13 +106,11 @@ export interface PolymarketOrderBook {
timestamp: string
bids: PolymarketOrderBookEntry[]
asks: PolymarketOrderBookEntry[]
min_order_size: string
tick_size: string
neg_risk: boolean
}
export interface PolymarketPrice {
price: string
side: string
}
export interface PolymarketPriceHistoryEntry {
@@ -150,125 +143,31 @@ export interface PolymarketSeries {
export interface PolymarketSearchResult {
markets: PolymarketMarket[]
events: PolymarketEvent[]
tags: PolymarketTag[]
profiles: PolymarketProfile[]
}
export interface PolymarketProfile {
id: string
name: string | null
pseudonym: string | null
bio: string | null
profileImage: string | null
profileImageOptimized: string | null
walletAddress: string
profiles: any[]
}
export interface PolymarketSpread {
spread: string
bid: string
ask: string
}
export interface PolymarketPosition {
proxyWallet: string | null
asset: string
conditionId: string
size: number
avgPrice: number
initialValue: number
currentValue: number
cashPnl: number
percentPnl: number
totalBought: number
realizedPnl: number
percentRealizedPnl: number
curPrice: number
redeemable: boolean
mergeable: boolean
title: string | null
slug: string | null
icon: string | null
eventSlug: string | null
outcome: string | null
outcomeIndex: number | null
oppositeOutcome: string | null
oppositeAsset: string | null
endDate: string | null
negativeRisk: boolean
market: string
asset_id: string
size: string
value: string
}
export interface PolymarketTrade {
proxyWallet: string | null
id: string
market: string
asset_id: string
side: string
asset: string
conditionId: string
size: number
price: number
timestamp: number
title: string | null
slug: string | null
icon: string | null
eventSlug: string | null
outcome: string | null
outcomeIndex: number | null
name: string | null
pseudonym: string | null
bio: string | null
profileImage: string | null
profileImageOptimized: string | null
transactionHash: string | null
}
export interface PolymarketActivity {
proxyWallet: string | null
timestamp: number
conditionId: string
type: string
size: number
usdcSize: number
transactionHash: string | null
price: number | null
asset: string | null
side: string | null
outcomeIndex: number | null
title: string | null
slug: string | null
icon: string | null
eventSlug: string | null
outcome: string | null
name: string | null
pseudonym: string | null
bio: string | null
profileImage: string | null
profileImageOptimized: string | null
}
export interface PolymarketLeaderboardEntry {
rank: string
proxyWallet: string
userName: string | null
vol: number
pnl: number
profileImage: string | null
xUsername: string | null
verifiedBadge: boolean
}
export interface PolymarketHolder {
proxyWallet: string
bio: string | null
asset: string
pseudonym: string | null
amount: number
displayUsernamePublic: boolean
outcomeIndex: number
name: string | null
profileImage: string | null
profileImageOptimized: string | null
}
export interface PolymarketMarketHolders {
token: string
holders: PolymarketHolder[]
size: string
price: string
timestamp: string
maker: string
taker: string
}
export function handlePolymarketError(data: any, status: number, operation: string): never {

View File

@@ -663,23 +663,16 @@ import {
incidentioWorkflowsUpdateTool,
} from '@/tools/incidentio'
import {
intercomAssignConversationV2Tool,
intercomAttachContactToCompanyV2Tool,
intercomCloseConversationV2Tool,
intercomCreateCompanyTool,
intercomCreateCompanyV2Tool,
intercomCreateContactTool,
intercomCreateContactV2Tool,
intercomCreateEventV2Tool,
intercomCreateMessageTool,
intercomCreateMessageV2Tool,
intercomCreateNoteV2Tool,
intercomCreateTagV2Tool,
intercomCreateTicketTool,
intercomCreateTicketV2Tool,
intercomDeleteContactTool,
intercomDeleteContactV2Tool,
intercomDetachContactFromCompanyV2Tool,
intercomGetCompanyTool,
intercomGetCompanyV2Tool,
intercomGetContactTool,
@@ -688,28 +681,20 @@ import {
intercomGetConversationV2Tool,
intercomGetTicketTool,
intercomGetTicketV2Tool,
intercomListAdminsV2Tool,
intercomListCompaniesTool,
intercomListCompaniesV2Tool,
intercomListContactsTool,
intercomListContactsV2Tool,
intercomListConversationsTool,
intercomListConversationsV2Tool,
intercomListTagsV2Tool,
intercomOpenConversationV2Tool,
intercomReplyConversationTool,
intercomReplyConversationV2Tool,
intercomSearchContactsTool,
intercomSearchContactsV2Tool,
intercomSearchConversationsTool,
intercomSearchConversationsV2Tool,
intercomSnoozeConversationV2Tool,
intercomTagContactV2Tool,
intercomTagConversationV2Tool,
intercomUntagContactV2Tool,
intercomUpdateContactTool,
intercomUpdateContactV2Tool,
intercomUpdateTicketV2Tool,
} from '@/tools/intercom'
import { jinaReadUrlTool, jinaSearchTool } from '@/tools/jina'
import {
@@ -761,39 +746,22 @@ import {
} from '@/tools/jsm'
import {
kalshiAmendOrderTool,
kalshiAmendOrderV2Tool,
kalshiCancelOrderTool,
kalshiCancelOrderV2Tool,
kalshiCreateOrderTool,
kalshiCreateOrderV2Tool,
kalshiGetBalanceTool,
kalshiGetBalanceV2Tool,
kalshiGetCandlesticksTool,
kalshiGetCandlesticksV2Tool,
kalshiGetEventsTool,
kalshiGetEventsV2Tool,
kalshiGetEventTool,
kalshiGetEventV2Tool,
kalshiGetExchangeStatusTool,
kalshiGetExchangeStatusV2Tool,
kalshiGetFillsTool,
kalshiGetFillsV2Tool,
kalshiGetMarketsTool,
kalshiGetMarketsV2Tool,
kalshiGetMarketTool,
kalshiGetMarketV2Tool,
kalshiGetOrderbookTool,
kalshiGetOrderbookV2Tool,
kalshiGetOrdersTool,
kalshiGetOrdersV2Tool,
kalshiGetOrderTool,
kalshiGetOrderV2Tool,
kalshiGetPositionsTool,
kalshiGetPositionsV2Tool,
kalshiGetSeriesByTickerTool,
kalshiGetSeriesByTickerV2Tool,
kalshiGetTradesTool,
kalshiGetTradesV2Tool,
} from '@/tools/kalshi'
import {
knowledgeCreateDocumentTool,
@@ -1104,12 +1072,9 @@ import {
pipedriveUpdateLeadTool,
} from '@/tools/pipedrive'
import {
polymarketGetActivityTool,
polymarketGetEventsTool,
polymarketGetEventTool,
polymarketGetHoldersTool,
polymarketGetLastTradePriceTool,
polymarketGetLeaderboardTool,
polymarketGetMarketsTool,
polymarketGetMarketTool,
polymarketGetMidpointTool,
@@ -1850,39 +1815,22 @@ export const tools: Record<string, ToolConfig> = {
jsm_get_approvals: jsmGetApprovalsTool,
jsm_answer_approval: jsmAnswerApprovalTool,
kalshi_get_markets: kalshiGetMarketsTool,
kalshi_get_markets_v2: kalshiGetMarketsV2Tool,
kalshi_get_market: kalshiGetMarketTool,
kalshi_get_market_v2: kalshiGetMarketV2Tool,
kalshi_get_events: kalshiGetEventsTool,
kalshi_get_events_v2: kalshiGetEventsV2Tool,
kalshi_get_event: kalshiGetEventTool,
kalshi_get_event_v2: kalshiGetEventV2Tool,
kalshi_get_balance: kalshiGetBalanceTool,
kalshi_get_balance_v2: kalshiGetBalanceV2Tool,
kalshi_get_positions: kalshiGetPositionsTool,
kalshi_get_positions_v2: kalshiGetPositionsV2Tool,
kalshi_get_orders: kalshiGetOrdersTool,
kalshi_get_orders_v2: kalshiGetOrdersV2Tool,
kalshi_get_order: kalshiGetOrderTool,
kalshi_get_order_v2: kalshiGetOrderV2Tool,
kalshi_get_orderbook: kalshiGetOrderbookTool,
kalshi_get_orderbook_v2: kalshiGetOrderbookV2Tool,
kalshi_get_trades: kalshiGetTradesTool,
kalshi_get_trades_v2: kalshiGetTradesV2Tool,
kalshi_get_candlesticks: kalshiGetCandlesticksTool,
kalshi_get_candlesticks_v2: kalshiGetCandlesticksV2Tool,
kalshi_get_fills: kalshiGetFillsTool,
kalshi_get_fills_v2: kalshiGetFillsV2Tool,
kalshi_get_series_by_ticker: kalshiGetSeriesByTickerTool,
kalshi_get_series_by_ticker_v2: kalshiGetSeriesByTickerV2Tool,
kalshi_get_exchange_status: kalshiGetExchangeStatusTool,
kalshi_get_exchange_status_v2: kalshiGetExchangeStatusV2Tool,
kalshi_create_order: kalshiCreateOrderTool,
kalshi_create_order_v2: kalshiCreateOrderV2Tool,
kalshi_cancel_order: kalshiCancelOrderTool,
kalshi_cancel_order_v2: kalshiCancelOrderV2Tool,
kalshi_amend_order: kalshiAmendOrderTool,
kalshi_amend_order_v2: kalshiAmendOrderV2Tool,
polymarket_get_markets: polymarketGetMarketsTool,
polymarket_get_market: polymarketGetMarketTool,
polymarket_get_events: polymarketGetEventsTool,
@@ -1900,9 +1848,6 @@ export const tools: Record<string, ToolConfig> = {
polymarket_get_tick_size: polymarketGetTickSizeTool,
polymarket_get_positions: polymarketGetPositionsTool,
polymarket_get_trades: polymarketGetTradesTool,
polymarket_get_activity: polymarketGetActivityTool,
polymarket_get_leaderboard: polymarketGetLeaderboardTool,
polymarket_get_holders: polymarketGetHoldersTool,
slack_message: slackMessageTool,
slack_message_reader: slackMessageReaderTool,
slack_list_channels: slackListChannelsTool,
@@ -3141,23 +3086,8 @@ export const tools: Record<string, ToolConfig> = {
intercom_create_ticket_v2: intercomCreateTicketV2Tool,
intercom_get_ticket: intercomGetTicketTool,
intercom_get_ticket_v2: intercomGetTicketV2Tool,
intercom_update_ticket_v2: intercomUpdateTicketV2Tool,
intercom_create_message: intercomCreateMessageTool,
intercom_create_message_v2: intercomCreateMessageV2Tool,
intercom_list_admins_v2: intercomListAdminsV2Tool,
intercom_close_conversation_v2: intercomCloseConversationV2Tool,
intercom_open_conversation_v2: intercomOpenConversationV2Tool,
intercom_snooze_conversation_v2: intercomSnoozeConversationV2Tool,
intercom_assign_conversation_v2: intercomAssignConversationV2Tool,
intercom_list_tags_v2: intercomListTagsV2Tool,
intercom_create_tag_v2: intercomCreateTagV2Tool,
intercom_tag_contact_v2: intercomTagContactV2Tool,
intercom_untag_contact_v2: intercomUntagContactV2Tool,
intercom_tag_conversation_v2: intercomTagConversationV2Tool,
intercom_create_note_v2: intercomCreateNoteV2Tool,
intercom_create_event_v2: intercomCreateEventV2Tool,
intercom_attach_contact_to_company_v2: intercomAttachContactToCompanyV2Tool,
intercom_detach_contact_from_company_v2: intercomDetachContactFromCompanyV2Tool,
sentry_issues_list: listIssuesTool,
sentry_issues_get: getIssueTool,
sentry_issues_update: updateIssueTool,