schwab_mcp 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schwab_mcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joseph Platta
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-08-13 00:00:00.000000000 Z
10
+ date: 2025-10-20 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: mcp
@@ -71,6 +71,7 @@ files:
71
71
  - lib/schwab_mcp/resources/.keep
72
72
  - lib/schwab_mcp/schwab_client_factory.rb
73
73
  - lib/schwab_mcp/tools/cancel_order_tool.rb
74
+ - lib/schwab_mcp/tools/get_account_names_tool.rb
74
75
  - lib/schwab_mcp/tools/get_market_hours_tool.rb
75
76
  - lib/schwab_mcp/tools/get_order_tool.rb
76
77
  - lib/schwab_mcp/tools/get_price_history_tool.rb
@@ -78,7 +79,6 @@ files:
78
79
  - lib/schwab_mcp/tools/list_account_orders_tool.rb
79
80
  - lib/schwab_mcp/tools/list_account_transactions_tool.rb
80
81
  - lib/schwab_mcp/tools/list_movers_tool.rb
81
- - lib/schwab_mcp/tools/list_schwab_accounts_tool.rb
82
82
  - lib/schwab_mcp/tools/option_chain_tool.rb
83
83
  - lib/schwab_mcp/tools/place_order_tool.rb
84
84
  - lib/schwab_mcp/tools/preview_order_tool.rb
@@ -87,14 +87,7 @@ files:
87
87
  - lib/schwab_mcp/tools/replace_order_tool.rb
88
88
  - lib/schwab_mcp/tools/schwab_account_details_tool.rb
89
89
  - lib/schwab_mcp/version.rb
90
- - orders_example.json
91
90
  - sig/schwab_mcp.rbs
92
- - spx_option_chain.json
93
- - start_mcp_server.sh
94
- - test_mcp.rb
95
- - test_server.rb
96
- - trading_brokerage_account_details.json
97
- - transactions_example.json
98
91
  homepage: https://github.com/jwplatta/schwab_mcp
99
92
  licenses:
100
93
  - MIT
@@ -1,149 +0,0 @@
1
- require "mcp"
2
- require "schwab_rb"
3
- require "json"
4
- require_relative "../loggable"
5
- require_relative "../redactor"
6
- require_relative "../schwab_client_factory"
7
-
8
- module SchwabMCP
9
- module Tools
10
- class ListSchwabAccountsTool < MCP::Tool
11
- extend Loggable
12
- description "List all configured Schwab accounts with their friendly names and basic info"
13
-
14
- input_schema(
15
- properties: {},
16
- required: []
17
- )
18
-
19
- annotations(
20
- title: "List Schwab Accounts",
21
- read_only_hint: true,
22
- destructive_hint: false,
23
- idempotent_hint: true
24
- )
25
-
26
- def self.call(server_context:)
27
- log_info("Listing all configured Schwab accounts")
28
-
29
- begin
30
- client = SchwabClientFactory.create_client
31
- return SchwabClientFactory.client_error_response unless client
32
-
33
- log_debug("Fetching account numbers from Schwab API")
34
- account_numbers = client.get_account_numbers
35
-
36
- unless account_numbers
37
- log_error("Failed to retrieve account numbers")
38
- return MCP::Tool::Response.new([{
39
- type: "text",
40
- text: "**Error**: Failed to retrieve account numbers from Schwab API"
41
- }])
42
- end
43
-
44
- log_debug("Retrieved #{account_numbers.size} accounts from Schwab API")
45
-
46
- configured_accounts = find_configured_accounts(account_numbers)
47
-
48
- if configured_accounts.empty?
49
- return MCP::Tool::Response.new([{
50
- type: "text",
51
- text: "**No Configured Accounts Found**\n\nNo environment variables found ending with '_ACCOUNT'.\n\nTo configure accounts, set environment variables like:\n- TRADING_BROKERAGE_ACCOUNT=123456789\n- RETIREMENT_IRA_ACCOUNT=987654321\n- INCOME_BROKERAGE_ACCOUNT=555666777"
52
- }])
53
- end
54
-
55
- formatted_response = format_accounts_list(configured_accounts, account_numbers)
56
-
57
- MCP::Tool::Response.new([{
58
- type: "text",
59
- text: formatted_response
60
- }])
61
-
62
- rescue JSON::ParserError => e
63
- log_error("JSON parsing error: #{e.message}")
64
- MCP::Tool::Response.new([{
65
- type: "text",
66
- text: "**Error**: Failed to parse API response: #{e.message}"
67
- }])
68
- rescue => e
69
- log_error("Error listing accounts: #{e.message}")
70
- log_debug("Backtrace: #{e.backtrace.first(3).join('\n')}")
71
- MCP::Tool::Response.new([{
72
- type: "text",
73
- text: "**Error** listing accounts: #{e.message}\n\n#{e.backtrace.first(3).join('\n')}"
74
- }])
75
- end
76
- end
77
-
78
- private
79
-
80
- def self.find_configured_accounts(account_numbers)
81
- # Get all account IDs from Schwab API data object
82
- schwab_account_ids = account_numbers.account_numbers
83
-
84
- # Find environment variables ending with "_ACCOUNT"
85
- configured = []
86
- ENV.each do |key, value|
87
- next unless key.end_with?('_ACCOUNT')
88
-
89
- if schwab_account_ids.include?(value)
90
- account = account_numbers.find_by_account_number(value)
91
- configured << {
92
- name: key,
93
- friendly_name: friendly_name_from_env_key(key),
94
- account_id: value,
95
- account: account
96
- }
97
- end
98
- end
99
-
100
- configured.sort_by { |account| account[:name] }
101
- end
102
-
103
- def self.friendly_name_from_env_key(env_key)
104
- # Convert "TRADING_BROKERAGE_ACCOUNT" to "Trading Brokerage"
105
- env_key.gsub('_ACCOUNT', '').split('_').map(&:capitalize).join(' ')
106
- end
107
-
108
- def self.format_accounts_list(configured_accounts, account_numbers)
109
- response = "**Configured Schwab Accounts:**\n\n"
110
-
111
- configured_accounts.each_with_index do |account, index|
112
- response += "#{index + 1}. **#{account[:friendly_name]}** (`#{account[:name]}`)\n"
113
- response += " - Status: ✅ Configured\n\n"
114
- end
115
-
116
- # Show unconfigured accounts (if any)
117
- unconfigured_accounts = account_numbers.accounts.reject do |account_obj|
118
- configured_accounts.any? { |config| config[:account_id] == account_obj.account_number }
119
- end
120
-
121
- if unconfigured_accounts.any?
122
- response += "**Unconfigured Accounts Available:**\n\n"
123
- unconfigured_accounts.each_with_index do |account_obj, index|
124
- response += " - To configure: Set `YOUR_NAME_ACCOUNT=#{Redactor::REDACTED_ACCOUNT_PLACEHOLDER}` in your .env file\n\n"
125
- end
126
- end
127
-
128
- response += "**Usage:**\n"
129
- response += "To get account information, use the `schwab_account` tool with one of these account names:\n"
130
- configured_accounts.each do |account|
131
- response += "- `#{account[:name]}`\n"
132
- end
133
-
134
- if configured_accounts.any?
135
- response += "\n**Example:**\n"
136
- first_account = configured_accounts.first
137
- response += "```\n"
138
- response += "Tool: schwab_account\n"
139
- response += "Parameters: {\n"
140
- response += " \"account_name\": \"#{first_account[:name]}\"\n"
141
- response += "}\n"
142
- response += "```"
143
- end
144
-
145
- response
146
- end
147
- end
148
- end
149
- end