package market import ( "context" "encoding/json" "time" "s1d3sw1ped/robinhood-agentic-mcp/client" "s1d3sw1ped/robinhood-agentic-mcp/internal/wire" ) const ( toolIndexes = "get_indexes" toolIndexQuotes = "get_index_quotes" toolIndexHistoricals = "get_index_historicals" toolFinancials = "get_financials" toolEarningsResults = "get_earnings_results" toolEarningsCalendar = "get_earnings_calendar" toolSECFilingIndex = "get_sec_filing_index" toolSECFiling = "get_sec_filing" toolSECFilingFacts = "get_sec_filing_facts" toolSECFilingFactsCatalog = "get_sec_filing_facts_catalog" ) // IndexesRequest is the argument set for get_indexes. type IndexesRequest struct { Symbols string // comma-separated; live schema is a string, not an array } // IndexesResult is the parsed get_indexes payload. type IndexesResult struct{} // IndexQuotesRequest is the argument set for get_index_quotes. type IndexQuotesRequest struct { InstrumentIDs []string } // IndexQuotesResult is the parsed get_index_quotes payload. type IndexQuotesResult struct{} // IndexHistoricalsRequest is the argument set for get_index_historicals. type IndexHistoricalsRequest struct { InstrumentIDs []string StartTime time.Time EndTime time.Time Interval string // required — no hidden default } // IndexHistoricalsResult is the parsed get_index_historicals payload. type IndexHistoricalsResult struct{} // FinancialsRequest is the argument set for get_financials. type FinancialsRequest struct { Symbols []string Period string Limit int } // FinancialsResult is the parsed get_financials payload. type FinancialsResult struct{} // EarningsResultsRequest is the argument set for get_earnings_results. type EarningsResultsRequest struct { Symbol string } // EarningsResultsResult is the parsed get_earnings_results payload. type EarningsResultsResult struct { NextReportDate string `json:"next_report_date"` ReportDate string `json:"report_date"` } // EarningsCalendarRequest is the argument set for get_earnings_calendar. type EarningsCalendarRequest struct { StartDate string Days int Filter string } // EarningsCalendarResult is the parsed get_earnings_calendar payload. type EarningsCalendarResult struct{} // SECFilingIndexRequest is the argument set for get_sec_filing_index. type SECFilingIndexRequest struct { Symbol string FormType []string Since string Until string Cursor string } // SECFilingIndexResult is the parsed get_sec_filing_index payload. type SECFilingIndexResult struct{} // SECFilingRequest is the argument set for get_sec_filing. type SECFilingRequest struct { FilingID string Section string } // SECFilingResult is the parsed get_sec_filing payload. type SECFilingResult struct{} // SECFilingFactsRequest is the argument set for get_sec_filing_facts. type SECFilingFactsRequest struct { FilingIDs []string Concepts []string } // SECFilingFactsResult is the parsed get_sec_filing_facts payload. type SECFilingFactsResult struct{} // SECFilingFactsCatalogRequest is the argument set for get_sec_filing_facts_catalog. type SECFilingFactsCatalogRequest struct { FilingID string ConceptContains string AxisNameIn []string Offset int } // SECFilingFactsCatalogResult is the parsed get_sec_filing_facts_catalog payload. type SECFilingFactsCatalogResult struct{} // Indexes calls get_indexes. func (c *Client) Indexes(ctx context.Context, req IndexesRequest) (IndexesResult, error) { args := map[string]any{} if req.Symbols != "" { args["symbols"] = req.Symbols } var out IndexesResult if err := c.parse(ctx, toolIndexes, args, &out); err != nil { return IndexesResult{}, err } return out, nil } // IndexQuotes calls get_index_quotes. func (c *Client) IndexQuotes(ctx context.Context, req IndexQuotesRequest) (IndexQuotesResult, error) { args := map[string]any{} if len(req.InstrumentIDs) > 0 { args["instrument_ids"] = req.InstrumentIDs } var out IndexQuotesResult if err := c.parse(ctx, toolIndexQuotes, args, &out); err != nil { return IndexQuotesResult{}, err } return out, nil } // IndexHistoricals calls get_index_historicals. start_time and interval are always sent (required; no hidden default). func (c *Client) IndexHistoricals(ctx context.Context, req IndexHistoricalsRequest) (IndexHistoricalsResult, error) { args := map[string]any{ "start_time": req.StartTime.UTC().Format(time.RFC3339), "interval": req.Interval, } if len(req.InstrumentIDs) > 0 { args["instrument_ids"] = req.InstrumentIDs } if !req.EndTime.IsZero() { args["end_time"] = req.EndTime.UTC().Format(time.RFC3339) } var out IndexHistoricalsResult if err := c.parse(ctx, toolIndexHistoricals, args, &out); err != nil { return IndexHistoricalsResult{}, err } return out, nil } // Financials calls get_financials. func (c *Client) Financials(ctx context.Context, req FinancialsRequest) (FinancialsResult, error) { args := map[string]any{} if len(req.Symbols) > 0 { args["symbols"] = req.Symbols } if req.Period != "" { args["period"] = req.Period } if req.Limit != 0 { args["limit"] = req.Limit } var out FinancialsResult if err := c.parse(ctx, toolFinancials, args, &out); err != nil { return FinancialsResult{}, err } return out, nil } // EarningsResults calls get_earnings_results. func (c *Client) EarningsResults(ctx context.Context, req EarningsResultsRequest) (EarningsResultsResult, error) { args := map[string]any{} if req.Symbol != "" { args["symbol"] = req.Symbol } var out EarningsResultsResult if err := c.parse(ctx, toolEarningsResults, args, &out); err != nil { return EarningsResultsResult{}, err } return out, nil } // EarningsCalendar calls get_earnings_calendar. func (c *Client) EarningsCalendar(ctx context.Context, req EarningsCalendarRequest) (EarningsCalendarResult, error) { args := map[string]any{} if req.StartDate != "" { args["start_date"] = req.StartDate } if req.Days != 0 { args["days"] = req.Days } if req.Filter != "" { args["filter"] = req.Filter } var out EarningsCalendarResult if err := c.parse(ctx, toolEarningsCalendar, args, &out); err != nil { return EarningsCalendarResult{}, err } return out, nil } // SECFilingIndex calls get_sec_filing_index. func (c *Client) SECFilingIndex(ctx context.Context, req SECFilingIndexRequest) (SECFilingIndexResult, error) { args := map[string]any{} if req.Symbol != "" { args["symbol"] = req.Symbol } if len(req.FormType) > 0 { args["form_type"] = req.FormType } if req.Since != "" { args["since"] = req.Since } if req.Until != "" { args["until"] = req.Until } if req.Cursor != "" { args["cursor"] = req.Cursor } var out SECFilingIndexResult if err := c.parse(ctx, toolSECFilingIndex, args, &out); err != nil { return SECFilingIndexResult{}, err } return out, nil } // SECFiling calls get_sec_filing. func (c *Client) SECFiling(ctx context.Context, req SECFilingRequest) (SECFilingResult, error) { args := map[string]any{} if req.FilingID != "" { args["filing_id"] = req.FilingID } if req.Section != "" { args["section"] = req.Section } var out SECFilingResult if err := c.parse(ctx, toolSECFiling, args, &out); err != nil { return SECFilingResult{}, err } return out, nil } // SECFilingFacts calls get_sec_filing_facts. func (c *Client) SECFilingFacts(ctx context.Context, req SECFilingFactsRequest) (SECFilingFactsResult, error) { args := map[string]any{} if len(req.FilingIDs) > 0 { args["filing_ids"] = req.FilingIDs } if len(req.Concepts) > 0 { args["concepts"] = req.Concepts } var out SECFilingFactsResult if err := c.parse(ctx, toolSECFilingFacts, args, &out); err != nil { return SECFilingFactsResult{}, err } return out, nil } // SECFilingFactsCatalog calls get_sec_filing_facts_catalog. func (c *Client) SECFilingFactsCatalog(ctx context.Context, req SECFilingFactsCatalogRequest) (SECFilingFactsCatalogResult, error) { args := map[string]any{} if req.FilingID != "" { args["filing_id"] = req.FilingID } if req.ConceptContains != "" { args["concept_contains"] = req.ConceptContains } if len(req.AxisNameIn) > 0 { args["axis_name_in"] = req.AxisNameIn } if req.Offset != 0 { args["offset"] = req.Offset } var out SECFilingFactsCatalogResult if err := c.parse(ctx, toolSECFilingFactsCatalog, args, &out); err != nil { return SECFilingFactsCatalogResult{}, 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 }