feat: add accounts MCP methods
This commit is contained in:
@@ -0,0 +1,274 @@
|
|||||||
|
package accounts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
decimal "github.com/alpacahq/alpacadecimal"
|
||||||
|
"s1d3sw1ped/robinhood-agentic-mcp/client"
|
||||||
|
"s1d3sw1ped/robinhood-agentic-mcp/internal/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
toolAccounts = "get_accounts"
|
||||||
|
toolPortfolio = "get_portfolio"
|
||||||
|
toolRealizedPnL = "get_realized_pnl"
|
||||||
|
toolPnLTradeHistory = "get_pnl_trade_history"
|
||||||
|
toolLimitedMarginUpgrade = "get_limited_margin_upgrade_info"
|
||||||
|
toolOptionLevelUpgrade = "get_option_level_upgrade_info"
|
||||||
|
toolCryptoOnboarding = "get_crypto_account_onboarding_info"
|
||||||
|
toolSearch = "search"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AccountsRequest is the argument set for get_accounts (none).
|
||||||
|
type AccountsRequest struct{}
|
||||||
|
|
||||||
|
// Account is one brokerage account from get_accounts.
|
||||||
|
type Account struct {
|
||||||
|
AccountNumber string
|
||||||
|
ID string
|
||||||
|
RHSAccountNumber string
|
||||||
|
Type string
|
||||||
|
AgenticAllowed bool
|
||||||
|
Cash bool
|
||||||
|
BuyingPower *decimal.Decimal
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountsResult is the parsed get_accounts payload.
|
||||||
|
type AccountsResult struct {
|
||||||
|
Accounts []Account
|
||||||
|
}
|
||||||
|
|
||||||
|
// PortfolioRequest is the argument set for get_portfolio.
|
||||||
|
type PortfolioRequest struct {
|
||||||
|
AccountNumber string
|
||||||
|
}
|
||||||
|
|
||||||
|
// PortfolioResult is the parsed get_portfolio payload.
|
||||||
|
type PortfolioResult struct {
|
||||||
|
BuyingPower decimal.Decimal
|
||||||
|
}
|
||||||
|
|
||||||
|
// RealizedPnLRequest is the argument set for get_realized_pnl.
|
||||||
|
type RealizedPnLRequest struct {
|
||||||
|
AccountNumber string
|
||||||
|
Span string
|
||||||
|
StartDate string
|
||||||
|
EndDate string
|
||||||
|
AssetClasses []string
|
||||||
|
DisplayCurrency string
|
||||||
|
Timezone string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RealizedPnLResult is the parsed get_realized_pnl payload.
|
||||||
|
type RealizedPnLResult struct{}
|
||||||
|
|
||||||
|
// PnLTradeHistoryRequest is the argument set for get_pnl_trade_history.
|
||||||
|
type PnLTradeHistoryRequest struct {
|
||||||
|
AccountNumber string
|
||||||
|
Span string
|
||||||
|
Symbol string
|
||||||
|
Cursor string
|
||||||
|
}
|
||||||
|
|
||||||
|
// PnLTradeHistoryResult is the parsed get_pnl_trade_history payload.
|
||||||
|
type PnLTradeHistoryResult struct{}
|
||||||
|
|
||||||
|
// AccountNumberRequest is a single account_number argument.
|
||||||
|
type AccountNumberRequest struct {
|
||||||
|
AccountNumber string
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpgradeInfoResult is the parsed upgrade-info payload.
|
||||||
|
type UpgradeInfoResult struct{}
|
||||||
|
|
||||||
|
// OnboardingInfoResult is the parsed crypto onboarding payload.
|
||||||
|
type OnboardingInfoResult struct{}
|
||||||
|
|
||||||
|
// SearchRequest is the argument set for search.
|
||||||
|
type SearchRequest struct {
|
||||||
|
Query string
|
||||||
|
AssetType string
|
||||||
|
Limit int
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchResult is the parsed search payload.
|
||||||
|
type SearchResult struct{}
|
||||||
|
|
||||||
|
// Accounts calls get_accounts and returns every account (no IRA/margin filter).
|
||||||
|
func (c *Client) Accounts(ctx context.Context, req AccountsRequest) (AccountsResult, error) {
|
||||||
|
raw, err := c.c.Call(ctx, toolAccounts, map[string]any{})
|
||||||
|
if err != nil {
|
||||||
|
return AccountsResult{}, err
|
||||||
|
}
|
||||||
|
var wrap struct {
|
||||||
|
Accounts []struct {
|
||||||
|
AccountNumber string `json:"account_number"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
RHSAccountNumber string `json:"rhs_account_number"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
AgenticAllowed bool `json:"agentic_allowed"`
|
||||||
|
Cash bool `json:"cash"`
|
||||||
|
BuyingPower any `json:"buying_power"`
|
||||||
|
} `json:"accounts"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(wire.Unwrap(raw), &wrap); err != nil {
|
||||||
|
return AccountsResult{}, client.ToolErrorf(toolAccounts, "parse: %w", err)
|
||||||
|
}
|
||||||
|
out := make([]Account, 0, len(wrap.Accounts))
|
||||||
|
for _, a := range wrap.Accounts {
|
||||||
|
bp, err := wire.DecOpt(a.BuyingPower)
|
||||||
|
if err != nil {
|
||||||
|
return AccountsResult{}, client.ToolErrorf(toolAccounts, "parse: %w", err)
|
||||||
|
}
|
||||||
|
out = append(out, Account{
|
||||||
|
AccountNumber: a.AccountNumber,
|
||||||
|
ID: a.ID,
|
||||||
|
RHSAccountNumber: a.RHSAccountNumber,
|
||||||
|
Type: a.Type,
|
||||||
|
AgenticAllowed: a.AgenticAllowed,
|
||||||
|
Cash: a.Cash,
|
||||||
|
BuyingPower: bp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return AccountsResult{Accounts: out}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Portfolio calls get_portfolio.
|
||||||
|
func (c *Client) Portfolio(ctx context.Context, req PortfolioRequest) (PortfolioResult, error) {
|
||||||
|
args := map[string]any{}
|
||||||
|
if req.AccountNumber != "" {
|
||||||
|
args["account_number"] = req.AccountNumber
|
||||||
|
}
|
||||||
|
raw, err := c.c.Call(ctx, toolPortfolio, args)
|
||||||
|
if err != nil {
|
||||||
|
return PortfolioResult{}, err
|
||||||
|
}
|
||||||
|
var loose struct {
|
||||||
|
BuyingPower struct {
|
||||||
|
BuyingPower any `json:"buying_power"`
|
||||||
|
} `json:"buying_power"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(wire.Unwrap(raw), &loose); err != nil {
|
||||||
|
return PortfolioResult{}, client.ToolErrorf(toolPortfolio, "parse: %w", err)
|
||||||
|
}
|
||||||
|
bp, err := wire.Dec(loose.BuyingPower.BuyingPower)
|
||||||
|
if err != nil {
|
||||||
|
return PortfolioResult{}, client.ToolErrorf(toolPortfolio, "parse: %w", err)
|
||||||
|
}
|
||||||
|
return PortfolioResult{BuyingPower: bp}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RealizedPnL calls get_realized_pnl.
|
||||||
|
func (c *Client) RealizedPnL(ctx context.Context, req RealizedPnLRequest) (RealizedPnLResult, error) {
|
||||||
|
args := map[string]any{}
|
||||||
|
if req.AccountNumber != "" {
|
||||||
|
args["account_number"] = req.AccountNumber
|
||||||
|
}
|
||||||
|
if req.Span != "" {
|
||||||
|
args["span"] = req.Span
|
||||||
|
}
|
||||||
|
if req.StartDate != "" {
|
||||||
|
args["start_date"] = req.StartDate
|
||||||
|
}
|
||||||
|
if req.EndDate != "" {
|
||||||
|
args["end_date"] = req.EndDate
|
||||||
|
}
|
||||||
|
if len(req.AssetClasses) > 0 {
|
||||||
|
args["asset_classes"] = req.AssetClasses
|
||||||
|
}
|
||||||
|
if req.DisplayCurrency != "" {
|
||||||
|
args["display_currency"] = req.DisplayCurrency
|
||||||
|
}
|
||||||
|
if req.Timezone != "" {
|
||||||
|
args["timezone"] = req.Timezone
|
||||||
|
}
|
||||||
|
var out RealizedPnLResult
|
||||||
|
if err := c.parse(ctx, toolRealizedPnL, args, &out); err != nil {
|
||||||
|
return RealizedPnLResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PnLTradeHistory calls get_pnl_trade_history.
|
||||||
|
func (c *Client) PnLTradeHistory(ctx context.Context, req PnLTradeHistoryRequest) (PnLTradeHistoryResult, error) {
|
||||||
|
args := map[string]any{}
|
||||||
|
if req.AccountNumber != "" {
|
||||||
|
args["account_number"] = req.AccountNumber
|
||||||
|
}
|
||||||
|
if req.Span != "" {
|
||||||
|
args["span"] = req.Span
|
||||||
|
}
|
||||||
|
if req.Symbol != "" {
|
||||||
|
args["symbol"] = req.Symbol
|
||||||
|
}
|
||||||
|
if req.Cursor != "" {
|
||||||
|
args["cursor"] = req.Cursor
|
||||||
|
}
|
||||||
|
var out PnLTradeHistoryResult
|
||||||
|
if err := c.parse(ctx, toolPnLTradeHistory, args, &out); err != nil {
|
||||||
|
return PnLTradeHistoryResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LimitedMarginUpgradeInfo calls get_limited_margin_upgrade_info.
|
||||||
|
func (c *Client) LimitedMarginUpgradeInfo(ctx context.Context, req AccountNumberRequest) (UpgradeInfoResult, error) {
|
||||||
|
return c.upgradeInfo(ctx, toolLimitedMarginUpgrade, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionLevelUpgradeInfo calls get_option_level_upgrade_info.
|
||||||
|
func (c *Client) OptionLevelUpgradeInfo(ctx context.Context, req AccountNumberRequest) (UpgradeInfoResult, error) {
|
||||||
|
return c.upgradeInfo(ctx, toolOptionLevelUpgrade, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) upgradeInfo(ctx context.Context, tool string, req AccountNumberRequest) (UpgradeInfoResult, error) {
|
||||||
|
args := map[string]any{}
|
||||||
|
if req.AccountNumber != "" {
|
||||||
|
args["account_number"] = req.AccountNumber
|
||||||
|
}
|
||||||
|
var out UpgradeInfoResult
|
||||||
|
if err := c.parse(ctx, tool, args, &out); err != nil {
|
||||||
|
return UpgradeInfoResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CryptoOnboardingInfo calls get_crypto_account_onboarding_info.
|
||||||
|
func (c *Client) CryptoOnboardingInfo(ctx context.Context, req struct{}) (OnboardingInfoResult, error) {
|
||||||
|
var out OnboardingInfoResult
|
||||||
|
if err := c.parse(ctx, toolCryptoOnboarding, map[string]any{}, &out); err != nil {
|
||||||
|
return OnboardingInfoResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search calls search.
|
||||||
|
func (c *Client) Search(ctx context.Context, req SearchRequest) (SearchResult, error) {
|
||||||
|
args := map[string]any{}
|
||||||
|
if req.Query != "" {
|
||||||
|
args["query"] = req.Query
|
||||||
|
}
|
||||||
|
if req.AssetType != "" {
|
||||||
|
args["asset_type"] = req.AssetType
|
||||||
|
}
|
||||||
|
if req.Limit != 0 {
|
||||||
|
args["limit"] = req.Limit
|
||||||
|
}
|
||||||
|
var out SearchResult
|
||||||
|
if err := c.parse(ctx, toolSearch, args, &out); err != nil {
|
||||||
|
return SearchResult{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) parse(ctx context.Context, tool string, args map[string]any, dest any) error {
|
||||||
|
raw, err := c.c.Call(ctx, tool, args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(wire.Unwrap(raw), dest); err != nil {
|
||||||
|
return client.ToolErrorf(tool, "parse: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
package accounts_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
decimal "github.com/alpacahq/alpacadecimal"
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"s1d3sw1ped/robinhood-agentic-mcp/accounts"
|
||||||
|
"s1d3sw1ped/robinhood-agentic-mcp/client"
|
||||||
|
"s1d3sw1ped/robinhood-agentic-mcp/internal/rhntest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccounts_toolNames(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
call func(*accounts.Client) error
|
||||||
|
wantName string
|
||||||
|
wantArgs map[string]any
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Accounts",
|
||||||
|
call: func(c *accounts.Client) error {
|
||||||
|
_, err := c.Accounts(context.Background(), accounts.AccountsRequest{})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_accounts",
|
||||||
|
wantArgs: map[string]any{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Portfolio",
|
||||||
|
call: func(c *accounts.Client) error {
|
||||||
|
_, err := c.Portfolio(context.Background(), accounts.PortfolioRequest{AccountNumber: "acct-1"})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_portfolio",
|
||||||
|
wantArgs: map[string]any{"account_number": "acct-1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "RealizedPnL",
|
||||||
|
call: func(c *accounts.Client) error {
|
||||||
|
_, err := c.RealizedPnL(context.Background(), accounts.RealizedPnLRequest{
|
||||||
|
AccountNumber: "acct-1",
|
||||||
|
Span: "month",
|
||||||
|
StartDate: "2026-01-01",
|
||||||
|
EndDate: "2026-01-31",
|
||||||
|
AssetClasses: []string{"equity"},
|
||||||
|
DisplayCurrency: "USD",
|
||||||
|
Timezone: "America/New_York",
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_realized_pnl",
|
||||||
|
wantArgs: map[string]any{
|
||||||
|
"account_number": "acct-1",
|
||||||
|
"span": "month",
|
||||||
|
"start_date": "2026-01-01",
|
||||||
|
"end_date": "2026-01-31",
|
||||||
|
"asset_classes": []string{"equity"},
|
||||||
|
"display_currency": "USD",
|
||||||
|
"timezone": "America/New_York",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "PnLTradeHistory",
|
||||||
|
call: func(c *accounts.Client) error {
|
||||||
|
_, err := c.PnLTradeHistory(context.Background(), accounts.PnLTradeHistoryRequest{
|
||||||
|
AccountNumber: "acct-1",
|
||||||
|
Span: "week",
|
||||||
|
Symbol: "MU",
|
||||||
|
Cursor: "c1",
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_pnl_trade_history",
|
||||||
|
wantArgs: map[string]any{
|
||||||
|
"account_number": "acct-1",
|
||||||
|
"span": "week",
|
||||||
|
"symbol": "MU",
|
||||||
|
"cursor": "c1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "LimitedMarginUpgradeInfo",
|
||||||
|
call: func(c *accounts.Client) error {
|
||||||
|
_, err := c.LimitedMarginUpgradeInfo(context.Background(), accounts.AccountNumberRequest{AccountNumber: "acct-1"})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_limited_margin_upgrade_info",
|
||||||
|
wantArgs: map[string]any{"account_number": "acct-1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OptionLevelUpgradeInfo",
|
||||||
|
call: func(c *accounts.Client) error {
|
||||||
|
_, err := c.OptionLevelUpgradeInfo(context.Background(), accounts.AccountNumberRequest{AccountNumber: "acct-1"})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_option_level_upgrade_info",
|
||||||
|
wantArgs: map[string]any{"account_number": "acct-1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "CryptoOnboardingInfo",
|
||||||
|
call: func(c *accounts.Client) error {
|
||||||
|
_, err := c.CryptoOnboardingInfo(context.Background(), struct{}{})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "get_crypto_account_onboarding_info",
|
||||||
|
wantArgs: map[string]any{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Search",
|
||||||
|
call: func(c *accounts.Client) error {
|
||||||
|
_, err := c.Search(context.Background(), accounts.SearchRequest{
|
||||||
|
Query: "apple",
|
||||||
|
AssetType: "instrument",
|
||||||
|
Limit: 5,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
wantName: "search",
|
||||||
|
wantArgs: map[string]any{
|
||||||
|
"query": "apple",
|
||||||
|
"asset_type": "instrument",
|
||||||
|
"limit": 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
var gotName string
|
||||||
|
var gotArgs map[string]any
|
||||||
|
c := accounts.New(client.Func(func(ctx context.Context, name string, args map[string]any) (json.RawMessage, error) {
|
||||||
|
gotName, gotArgs = name, args
|
||||||
|
return json.RawMessage(`{}`), nil
|
||||||
|
}))
|
||||||
|
if err := tc.call(c); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if gotName != tc.wantName {
|
||||||
|
t.Fatalf("%s %+v", gotName, gotArgs)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(tc.wantArgs, gotArgs); diff != "" {
|
||||||
|
t.Fatal(diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTools(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
want := []string{
|
||||||
|
"get_accounts",
|
||||||
|
"get_crypto_account_onboarding_info",
|
||||||
|
"get_limited_margin_upgrade_info",
|
||||||
|
"get_option_level_upgrade_info",
|
||||||
|
"get_pnl_trade_history",
|
||||||
|
"get_portfolio",
|
||||||
|
"get_realized_pnl",
|
||||||
|
"search",
|
||||||
|
}
|
||||||
|
got := append([]string(nil), accounts.Tools()...)
|
||||||
|
sort.Strings(got)
|
||||||
|
if diff := cmp.Diff(want, got); diff != "" {
|
||||||
|
t.Fatal(diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccounts_rhntest(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
raw, err := os.ReadFile("testdata/accounts.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
s := rhntest.New(t)
|
||||||
|
s.Set("get_accounts", raw)
|
||||||
|
c := accounts.New(&client.Client{URL: s.URL})
|
||||||
|
got, err := c.Accounts(context.Background(), accounts.AccountsRequest{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(got.Accounts) != 1 {
|
||||||
|
t.Fatalf("%+v", got)
|
||||||
|
}
|
||||||
|
a := got.Accounts[0]
|
||||||
|
if a.ID != "acct-1" || a.Type != "cash" || !a.AgenticAllowed || !a.Cash {
|
||||||
|
t.Fatalf("%+v", a)
|
||||||
|
}
|
||||||
|
if a.BuyingPower == nil || !a.BuyingPower.Equal(decimal.RequireFromString("1000")) {
|
||||||
|
t.Fatalf("buying power %v", a.BuyingPower)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPortfolio_rhntest(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
s := rhntest.New(t)
|
||||||
|
s.Set("get_portfolio", json.RawMessage(`{"data":{"buying_power":{"buying_power":"1000.0"}}}`))
|
||||||
|
c := accounts.New(&client.Client{URL: s.URL})
|
||||||
|
got, err := c.Portfolio(context.Background(), accounts.PortfolioRequest{AccountNumber: "acct-1"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !got.BuyingPower.Equal(decimal.RequireFromString("1000.0")) {
|
||||||
|
t.Fatalf("buying power %s", got.BuyingPower)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package accounts
|
||||||
|
|
||||||
|
import "s1d3sw1ped/robinhood-agentic-mcp/client"
|
||||||
|
|
||||||
|
// Client wraps Robinhood account MCP tools.
|
||||||
|
type Client struct {
|
||||||
|
c client.Caller
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns an accounts client that invokes tools through c.
|
||||||
|
func New(c client.Caller) *Client {
|
||||||
|
return &Client{c: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tools returns the MCP names this package implements.
|
||||||
|
func Tools() []string {
|
||||||
|
return []string{
|
||||||
|
toolAccounts,
|
||||||
|
toolPortfolio,
|
||||||
|
toolRealizedPnL,
|
||||||
|
toolPnLTradeHistory,
|
||||||
|
toolLimitedMarginUpgrade,
|
||||||
|
toolOptionLevelUpgrade,
|
||||||
|
toolCryptoOnboarding,
|
||||||
|
toolSearch,
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"accounts":[{"id":"acct-1","type":"cash","agentic_allowed":true,"cash":true,"buying_power":1000.0}]}
|
||||||
@@ -4,6 +4,7 @@ go 1.25.0
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alpacahq/alpacadecimal v0.0.9
|
github.com/alpacahq/alpacadecimal v0.0.9
|
||||||
|
github.com/google/go-cmp v0.7.0
|
||||||
github.com/modelcontextprotocol/go-sdk v1.7.0
|
github.com/modelcontextprotocol/go-sdk v1.7.0
|
||||||
golang.org/x/oauth2 v0.35.0
|
golang.org/x/oauth2 v0.35.0
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user