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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7680eeeed2f4a22cf70f836af9ab019c60e6de76867408acf79d0ffdd3217b33
4
- data.tar.gz: e236d0f991b031b84873fbc8244b623dd74603ac3ab043d3586a987a8656eb37
3
+ metadata.gz: be351ba4378eab6d224a3b690a8feb205491fa9cd264c3e46038c48f8d4f7227
4
+ data.tar.gz: 0ff6dd0f16abda8d20d070143f777ed4685322d2f0f8c303d9e3990344da225b
5
5
  SHA512:
6
- metadata.gz: 678fb6af3f9de96315e9798fa5f8aebd4dde3bcc102859b7c3e761aefb2b7d3ec591b56267978936114099a68f746a3c43b0a72abf4bd711c83822758053f63f
7
- data.tar.gz: ae505ca27dbf4f8579279a05845befed68fc6c1690cdd9d469b585239827e931e3726381795d60fb8c086d2de70fdfc0966fc24ea4b9f380c3f4311846a4ec58
6
+ metadata.gz: b80dee8bbeb80f3eb7940bafe0e43aedfa266c2981087b6865de8de79b8a12578a6e0fd5316918a942b012318b5f8e466f8655bf43fbd75732c0436c379f7bb2
7
+ data.tar.gz: 83a841a0a278a2a53ccc5d141365a0013829a863e1dd0d54784648313175dd7a463071c34b969751c4508575260e28298c26ba0310ac12b83a13342afcd0282c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] - 2025-10-20
4
+
5
+ ### Added
6
+ - Refactored account management to use user-defined account names instead of directly referencing account numbers. ([#7](https://github.com/jwplatta/schwab_mcp/pull/7))
7
+ - Updated dependency `schwab_rb` to version `0.4.0`. ([#6](https://github.com/jwplatta/schwab_mcp/pull/6))
8
+
9
+ ### Changed
10
+ - Refactored order tools to use the `schwab_rb` order factory for improved modularity. ([#5](https://github.com/jwplatta/schwab_mcp/pull/5))
11
+
12
+ ### Infrastructure
13
+ - Added GitHub Actions workflows for Claude integration. ([#3](https://github.com/jwplatta/schwab_mcp/pull/3), [#4](https://github.com/jwplatta/schwab_mcp/pull/4))
14
+
3
15
  ## [0.1.0] - 2025-07-10
4
16
 
5
17
  - Initial release
data/README.md CHANGED
@@ -40,7 +40,7 @@ Create a `.env` file in your project root with the following required variables:
40
40
  SCHWAB_API_KEY=your_schwab_api_key
41
41
  SCHWAB_APP_SECRET=your_schwab_app_secret
42
42
  SCHWAB_CALLBACK_URI=your_callback_uri
43
- TOKEN_PATH=path/to/your/token.json
43
+ SCHWAB_TOKEN_PATH=path/to/your/token.json
44
44
  ```
45
45
 
46
46
  ### Running the MCP Server
@@ -51,12 +51,6 @@ Start the server using the provided executable:
51
51
  bundle exec exe/schwab_mcp
52
52
  ```
53
53
 
54
- Or use the convenience script:
55
-
56
- ```bash
57
- ./start_mcp_server.sh
58
- ```
59
-
60
54
  ### Token Management
61
55
 
62
56
  The gem includes utility scripts for managing Schwab API authentication tokens:
data/debug_env.rb CHANGED
@@ -6,10 +6,10 @@ puts "=== Environment Variables ==="
6
6
  puts "SCHWAB_API_KEY: #{ENV['SCHWAB_API_KEY'] ? '[SET]' : '[NOT SET]'}"
7
7
  puts "SCHWAB_APP_SECRET: #{ENV['SCHWAB_APP_SECRET'] ? '[SET]' : '[NOT SET]'}"
8
8
  puts "APP_CALLBACK_URL: #{ENV['APP_CALLBACK_URL'] ? '[SET]' : '[NOT SET]'}"
9
- puts "TOKEN_PATH: #{ENV['TOKEN_PATH'] ? '[SET]' : '[NOT SET]'}"
9
+ puts "SCHWAB_TOKEN_PATH: #{ENV['SCHWAB_TOKEN_PATH'] ? '[SET]' : '[NOT SET]'}"
10
10
 
11
11
  puts "\n=== Testing Token File ==="
12
- token_path = ENV['TOKEN_PATH'] || './token.json'
12
+ token_path = ENV['SCHWAB_TOKEN_PATH'] || './token.json'
13
13
  puts "Checking token file at: #{token_path}"
14
14
  puts "Token file exists: #{File.exist?(token_path)}"
15
15
 
data/exe/schwab_mcp CHANGED
@@ -9,22 +9,22 @@ required_vars = [
9
9
  'SCHWAB_API_KEY',
10
10
  'SCHWAB_APP_SECRET',
11
11
  'SCHWAB_CALLBACK_URI',
12
- 'TOKEN_PATH'
12
+ 'SCHWAB_TOKEN_PATH'
13
13
  ]
14
14
  missing_vars = required_vars.select { |var| ENV[var].nil? || ENV[var].empty? }
15
15
 
16
16
  unless missing_vars.empty?
17
- STDERR.puts "[SCRIPT] Missing required environment variables: #{missing_vars.join(', ')}"
17
+ STDERR.puts "Missing required environment variables: #{missing_vars.join(', ')}"
18
18
  exit 1
19
19
  end
20
20
 
21
- STDERR.puts "[SCRIPT] All required environment variables are set."
21
+ STDERR.puts "All required environment variables are set."
22
22
 
23
23
  begin
24
- STDERR.puts "[SCRIPT] Starting Schwab MCP server..."
24
+ STDERR.puts "Starting Schwab MCP server..."
25
25
  SchwabMCP::Server.new.start
26
26
  rescue StandardError => e
27
- STDERR.puts "[SCRIPT] Error starting Schwab MCP server: #{e.message}"
27
+ STDERR.puts "Error starting Schwab MCP server: #{e.message}"
28
28
  STDERR.puts e.backtrace.join("\n")
29
29
  exit 1
30
30
  end
@@ -13,7 +13,7 @@ required_vars = [
13
13
  'SCHWAB_API_KEY',
14
14
  'SCHWAB_APP_SECRET',
15
15
  'SCHWAB_CALLBACK_URI',
16
- 'TOKEN_PATH'
16
+ 'SCHWAB_TOKEN_PATH'
17
17
  ]
18
18
  missing_vars = required_vars.select { |var| ENV[var].nil? || ENV[var].empty? }
19
19
 
@@ -22,7 +22,7 @@ unless missing_vars.empty?
22
22
  exit 1
23
23
  end
24
24
 
25
- token_path = ENV['TOKEN_PATH']
25
+ token_path = ENV['SCHWAB_TOKEN_PATH']
26
26
  puts "Token path: #{token_path}"
27
27
 
28
28
  begin
@@ -12,38 +12,39 @@ required_vars = [
12
12
  'SCHWAB_API_KEY',
13
13
  'SCHWAB_APP_SECRET',
14
14
  'SCHWAB_CALLBACK_URI',
15
- 'TOKEN_PATH'
15
+ 'SCHWAB_TOKEN_PATH',
16
+ 'SCHWAB_HOME',
16
17
  ]
17
18
  missing_vars = required_vars.select { |var| ENV[var].nil? || ENV[var].empty? }
18
19
 
19
20
  unless missing_vars.empty?
20
- puts "Missing required environment variables: #{missing_vars.join(', ')}"
21
+ puts "Missing required environment variables: #{missing_vars.join(', ')}"
21
22
  exit 1
22
23
  end
23
24
 
24
- token_path = ENV['TOKEN_PATH']
25
+ token_path = ENV['SCHWAB_TOKEN_PATH']
25
26
  puts "Token path: #{token_path}"
26
27
 
27
28
  if File.exist?(token_path)
28
- puts "🗑️ Deleting existing token file: #{token_path}"
29
+ puts "Deleting existing token file: #{token_path}"
29
30
  File.delete(token_path)
30
- puts "Token file deleted successfully"
31
+ puts "Token file deleted successfully"
31
32
  else
32
- puts "ℹ️ Token file doesn't exist at: #{token_path}"
33
+ puts "Token file doesn't exist at: #{token_path}"
33
34
  end
34
35
 
35
36
  refresh_script_path = File.join(File.dirname(__FILE__), 'schwab_token_refresh')
36
- puts "🔄 Calling token refresh script: #{refresh_script_path}"
37
+ puts "Calling token refresh script: #{refresh_script_path}"
37
38
 
38
39
  begin
39
40
  result = system(refresh_script_path)
40
41
  if result
41
- puts "Token reset and refresh completed successfully"
42
+ puts "Token reset and refresh completed successfully"
42
43
  else
43
- puts "Token refresh script failed"
44
+ puts "Token refresh script failed"
44
45
  exit 1
45
46
  end
46
47
  rescue => e
47
- puts "Failed to execute token refresh script: #{e.message}"
48
+ puts "Failed to execute token refresh script: #{e.message}"
48
49
  exit 1
49
50
  end
@@ -13,7 +13,7 @@ module SchwabMCP
13
13
  ENV['SCHWAB_API_KEY'],
14
14
  ENV['SCHWAB_APP_SECRET'],
15
15
  ENV['SCHWAB_CALLBACK_URI'],
16
- ENV['TOKEN_PATH']
16
+ ENV['SCHWAB_TOKEN_PATH']
17
17
  )
18
18
 
19
19
  unless client
@@ -56,50 +56,19 @@ module SchwabMCP
56
56
  client = SchwabClientFactory.create_client
57
57
  return SchwabClientFactory.client_error_response unless client
58
58
 
59
- account_id = ENV[account_name]
60
- unless account_id
61
- available_accounts = ENV.keys.select { |key| key.end_with?('_ACCOUNT') }
62
- log_error("Account name '#{account_name}' not found in environment variables")
59
+ available_accounts = client.available_account_names
60
+ unless available_accounts.include?(account_name)
61
+ log_error("Account name '#{account_name}' not found in configured accounts")
63
62
  return MCP::Tool::Response.new([{
64
63
  type: "text",
65
- text: "**Error**: Account name '#{account_name}' not found in environment variables.\n\nAvailable accounts: #{available_accounts.join(', ')}\n\nTo configure: Set ENV['#{account_name}'] to your account ID."
64
+ text: "**Error**: Account name '#{account_name}' not found in configured accounts.\n\nAvailable accounts: #{available_accounts.join(', ')}\n\nTo configure: Add the account to your schwab_rb configuration file."
66
65
  }])
67
66
  end
68
67
 
69
- log_debug("Found account ID: [REDACTED] for account name: #{account_name}")
70
- log_debug("Fetching account numbers mapping")
71
-
72
-
73
- account_numbers = client.get_account_numbers # returns SchwabRb::DataObjects::AccountNumbers
74
- unless account_numbers && account_numbers.respond_to?(:accounts)
75
- log_error("Failed to retrieve account numbers")
76
- return MCP::Tool::Response.new([{
77
- type: "text",
78
- text: "**Error**: Failed to retrieve account numbers from Schwab API"
79
- }])
80
- end
81
-
82
- account_hash = nil
83
- account_numbers.accounts.each do |acct|
84
- if acct.account_number.to_s == account_id.to_s
85
- account_hash = acct.hash_value
86
- break
87
- end
88
- end
89
-
90
- unless account_hash
91
- log_error("Account ID not found in available accounts")
92
- return MCP::Tool::Response.new([{
93
- type: "text",
94
- text: "**Error**: Account ID not found in available accounts. #{account_numbers.accounts.length} accounts available."
95
- }])
96
- end
97
-
98
- log_debug("Found account hash for account ID: #{account_name}")
68
+ log_debug("Using account name: #{account_name}")
99
69
  log_debug("Verifying order exists before attempting cancellation")
100
70
 
101
-
102
- order = client.get_order(order_id, account_hash) # returns SchwabRb::DataObjects::Order
71
+ order = client.get_order(order_id, account_name: account_name) # returns SchwabRb::DataObjects::Order
103
72
  unless order
104
73
  log_warn("Order not found or empty response for order ID: #{order_id}")
105
74
  return MCP::Tool::Response.new([{
@@ -121,7 +90,7 @@ module SchwabMCP
121
90
  end
122
91
 
123
92
  log_info("Attempting to cancel order ID: #{order_id} (Status: #{order_status})")
124
- cancel_response = client.cancel_order(order_id, account_hash)
93
+ cancel_response = client.cancel_order(order_id, account_name: account_name)
125
94
 
126
95
  if cancel_response.respond_to?(:status) && cancel_response.status == 200
127
96
  log_info("Successfully cancelled order ID: #{order_id}")
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mcp"
4
+ require_relative "../loggable"
5
+ require "schwab_rb"
6
+
7
+ module SchwabMCP
8
+ module Tools
9
+ class GetAccountNamesTool < MCP::Tool
10
+ extend Loggable
11
+ description "Get a list of configured Schwab account names"
12
+
13
+ input_schema(
14
+ properties: {
15
+ topic: {
16
+ type: "string",
17
+ description: "Asking about a specific topic related to account names (optional)"
18
+ }
19
+ },
20
+ required: []
21
+ )
22
+
23
+ annotations(
24
+ title: "Get Account Names",
25
+ read_only_hint: true,
26
+ destructive_hint: false,
27
+ idempotent_hint: true
28
+ )
29
+
30
+ def self.call(topic: nil, server_context:)
31
+ account_names = SchwabRb::AccountHashManager.new.available_account_names
32
+ acct_names_content = if account_names && !account_names.empty?
33
+ formatted_names = account_names.map { |name| "- #{name}" }.join("\n")
34
+ "Configured Schwab Account Names:\n\n#{formatted_names}"
35
+ else
36
+ <<~NO_ACCOUNTS
37
+ No Schwab Account Names Configured
38
+
39
+ You need to configure your Schwab account names in the account_names.json file.
40
+
41
+ This file should be located in your schwab_home directory (typically ~/.schwab_rb/).
42
+
43
+ For detailed setup instructions, please refer to:
44
+ https://github.com/jwplatta/schwab_rb/blob/main/doc/ACCOUNT_MANAGEMENT.md
45
+
46
+ The account_names.json file should map friendly names to your Schwab account hashes.
47
+ NO_ACCOUNTS
48
+ end
49
+
50
+ MCP::Tool::Response.new([{
51
+ type: "text",
52
+ text: acct_names_content
53
+ }])
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -57,49 +57,19 @@ module SchwabMCP
57
57
  client = SchwabClientFactory.create_client
58
58
  return SchwabClientFactory.client_error_response unless client
59
59
 
60
- account_id = ENV[account_name]
61
- unless account_id
62
- available_accounts = ENV.keys.select { |key| key.end_with?('_ACCOUNT') }
63
- log_error("Account name '#{account_name}' not found in environment variables")
60
+ available_accounts = client.available_account_names
61
+ unless available_accounts.include?(account_name)
62
+ log_error("Account name '#{account_name}' not found in configured accounts")
64
63
  return MCP::Tool::Response.new([{
65
64
  type: "text",
66
- text: "**Error**: Account name '#{account_name}' not found in environment variables.\n\nAvailable accounts: #{available_accounts.join(', ')}\n\nTo configure: Set ENV['#{account_name}'] to your account ID."
65
+ text: "**Error**: Account name '#{account_name}' not found in configured accounts.\n\nAvailable accounts: #{available_accounts.join(', ')}\n\nTo configure: Add the account to your schwab_rb configuration file."
67
66
  }])
68
67
  end
69
68
 
70
- log_debug("Found account ID: [REDACTED] for account name: #{account_name}")
71
- log_debug("Fetching account numbers mapping")
72
-
73
-
74
- account_numbers = client.get_account_numbers # returns SchwabRb::DataObjects::AccountNumbers
75
- unless account_numbers && account_numbers.respond_to?(:accounts)
76
- log_error("Failed to retrieve account numbers")
77
- return MCP::Tool::Response.new([{
78
- type: "text",
79
- text: "**Error**: Failed to retrieve account numbers from Schwab API"
80
- }])
81
- end
82
-
83
- account_hash = nil
84
- account_numbers.accounts.each do |acct|
85
- if acct.account_number.to_s == account_id.to_s
86
- account_hash = acct.hash_value
87
- break
88
- end
89
- end
90
-
91
- unless account_hash
92
- log_error("Account ID not found in available accounts")
93
- return MCP::Tool::Response.new([{
94
- type: "text",
95
- text: "**Error**: Account ID not found in available accounts. #{account_numbers.accounts.length} accounts available."
96
- }])
97
- end
98
-
99
- log_debug("Found account hash for account ID: #{account_name}")
69
+ log_debug("Using account name: #{account_name}")
100
70
  log_debug("Fetching order details for order ID: #{order_id}")
101
71
 
102
- order = client.get_order(order_id, account_hash) # returns SchwabRb::DataObjects::Order
72
+ order = client.get_order(order_id, account_name: account_name) # returns SchwabRb::DataObjects::Order
103
73
  if order
104
74
  log_info("Successfully retrieved order details for order ID: #{order_id}")
105
75
  formatted_response = format_order_object(order, order_id, account_name)
@@ -44,7 +44,7 @@ module SchwabMCP
44
44
 
45
45
  def self.get_general_help
46
46
  <<~HELP
47
- # 📊 Schwab MCP Server
47
+ # Schwab MCP Server
48
48
 
49
49
  ## Available Tools:
50
50
 
@@ -57,9 +57,9 @@ module SchwabMCP
57
57
  - **list_movers_tool**: Get top ten movers for a given index
58
58
 
59
59
  ### Option Order Tools:
60
- - **preview_order_tool**: Preview an options order before placing (⚠️ SAFE PREVIEW)
61
- - **place_order_tool**: Place an options order for execution (⚠️ DESTRUCTIVE)
62
- - **replace_order_tool**: Replace an existing order with a new one (⚠️ DESTRUCTIVE)
60
+ - **preview_order_tool**: Preview an options order before placing (SAFE PREVIEW)
61
+ - **place_order_tool**: Place an options order for execution (DESTRUCTIVE)
62
+ - **replace_order_tool**: Replace an existing order with a new one (DESTRUCTIVE)
63
63
 
64
64
  ### Account Management:
65
65
  - **schwab_account_details_tool**: Get account information using account name mapping
@@ -67,7 +67,7 @@ module SchwabMCP
67
67
  - **list_account_orders_tool**: List orders for a specific account using account name mapping
68
68
  - **list_account_transactions_tool**: List transactions for a specific account
69
69
  - **get_order_tool**: Get detailed information for a specific order by order ID
70
- - **cancel_order_tool**: Cancel a specific order by order ID (⚠️ DESTRUCTIVE)
70
+ - **cancel_order_tool**: Cancel a specific order by order ID (DESTRUCTIVE)
71
71
 
72
72
  ### Documentation:
73
73
  - **help_tool**: This help system
@@ -118,7 +118,7 @@ module SchwabMCP
118
118
 
119
119
  def self.get_tools_help
120
120
  <<~HELP
121
- # 🔧 Available Tools
121
+ # Available Tools
122
122
 
123
123
  ## Market Data & Analysis Tools
124
124
 
@@ -197,7 +197,7 @@ module SchwabMCP
197
197
 
198
198
  ## Order Management Tools
199
199
 
200
- ### preview_order_tool ⚠️ SAFE PREVIEW
200
+ ### preview_order_tool SAFE PREVIEW
201
201
  Preview an options order before placing to validate structure and see estimated costs.
202
202
  **Parameters**:
203
203
  - `account_name` (required) - Account name ending with '_ACCOUNT'
@@ -213,7 +213,7 @@ module SchwabMCP
213
213
  preview_order_tool(account_name: "TRADING_BROKERAGE_ACCOUNT", strategy_type: "callspread", short_symbol: "SPY250117C00600000", long_symbol: "SPY250117C00610000", price: 2.50)
214
214
  ```
215
215
 
216
- ### place_order_tool ⚠️ DESTRUCTIVE OPERATION
216
+ ### place_order_tool DESTRUCTIVE OPERATION
217
217
  Place an options order for execution. **WARNING**: This places real orders with real money.
218
218
  **Parameters**: Same as preview_order_tool
219
219
  **Safety Features**:
@@ -226,7 +226,7 @@ module SchwabMCP
226
226
  place_order_tool(account_name: "TRADING_BROKERAGE_ACCOUNT", strategy_type: "ironcondor", price: 1.50, quantity: 1)
227
227
  ```
228
228
 
229
- ### replace_order_tool ⚠️ DESTRUCTIVE OPERATION
229
+ ### replace_order_tool DESTRUCTIVE OPERATION
230
230
  Replace an existing order with a new one. **WARNING**: Cancels existing order and places new one.
231
231
  **Parameters**:
232
232
  - `order_id` (required) - ID of existing order to replace
@@ -298,7 +298,7 @@ module SchwabMCP
298
298
  get_order_tool(order_id: "987654321", account_name: "IRA_ACCOUNT")
299
299
  ```
300
300
 
301
- ### cancel_order_tool ⚠️ DESTRUCTIVE OPERATION
301
+ ### cancel_order_tool DESTRUCTIVE OPERATION
302
302
  Cancel a specific order by order ID. **WARNING**: This action cannot be undone.
303
303
  **Parameters**:
304
304
  - `order_id` (required) - The numeric order ID to cancel
@@ -335,7 +335,7 @@ module SchwabMCP
335
335
 
336
336
  def self.get_setup_help
337
337
  <<~HELP
338
- # 🚀 Setup Guide
338
+ # Setup Guide
339
339
 
340
340
  ## 1. Environment Variables
341
341
  Set these required environment variables:
@@ -87,42 +87,16 @@ module SchwabMCP
87
87
  client = SchwabClientFactory.create_client
88
88
  return SchwabClientFactory.client_error_response unless client
89
89
 
90
- account_id = ENV[account_name]
91
- unless account_id
92
- available_accounts = ENV.keys.select { |key| key.end_with?('_ACCOUNT') }
93
- log_error("Account name '#{account_name}' not found in environment variables")
90
+ available_accounts = client.available_account_names
91
+ unless available_accounts.include?(account_name)
92
+ log_error("Account name '#{account_name}' not found in configured accounts")
94
93
  return MCP::Tool::Response.new([{
95
94
  type: "text",
96
- text: "**Error**: Account name '#{account_name}' not found in environment variables.\n\nAvailable accounts: #{available_accounts.join(', ')}\n\nTo configure: Set ENV['#{account_name}'] to your account ID."
95
+ text: "**Error**: Account name '#{account_name}' not found in configured accounts.\n\nAvailable accounts: #{available_accounts.join(', ')}\n\nTo configure: Add the account to your schwab_rb configuration file."
97
96
  }])
98
97
  end
99
98
 
100
- log_debug("Found account ID: [REDACTED] for account name: #{account_name}")
101
- log_debug("Fetching account numbers mapping")
102
-
103
- account_numbers = client.get_account_numbers
104
-
105
- unless account_numbers
106
- log_error("Failed to retrieve account numbers")
107
- return MCP::Tool::Response.new([{
108
- type: "text",
109
- text: "**Error**: Failed to retrieve account numbers from Schwab API"
110
- }])
111
- end
112
-
113
- log_debug("Account numbers retrieved (#{account_numbers.size} accounts found)")
114
-
115
- account_hash = account_numbers.find_hash_value(account_id)
116
-
117
- unless account_hash
118
- log_error("Account ID not found in available accounts")
119
- return MCP::Tool::Response.new([{
120
- type: "text",
121
- text: "**Error**: Account ID not found in available accounts. #{account_numbers.size} accounts available."
122
- }])
123
- end
124
-
125
- log_debug("Found account hash for account ID: #{account_name}")
99
+ log_debug("Using account name: #{account_name}")
126
100
 
127
101
  from_datetime = nil
128
102
  to_datetime = nil
@@ -154,7 +128,7 @@ module SchwabMCP
154
128
  log_debug("Fetching orders with params - max_results: #{max_results}, from_datetime: #{from_datetime}, to_datetime: #{to_datetime}, status: #{status}")
155
129
 
156
130
  orders = client.get_account_orders(
157
- account_hash,
131
+ account_name: account_name,
158
132
  max_results: max_results,
159
133
  from_entered_datetime: from_datetime,
160
134
  to_entered_datetime: to_datetime,
@@ -83,42 +83,16 @@ module SchwabMCP
83
83
  client = SchwabClientFactory.create_client
84
84
  return SchwabClientFactory.client_error_response unless client
85
85
 
86
- account_id = ENV[account_name]
87
- unless account_id
88
- available_accounts = ENV.keys.select { |key| key.end_with?('_ACCOUNT') }
89
- log_error("Account name '#{account_name}' not found in environment variables")
86
+ available_accounts = client.available_account_names
87
+ unless available_accounts.include?(account_name)
88
+ log_error("Account name '#{account_name}' not found in configured accounts")
90
89
  return MCP::Tool::Response.new([{
91
90
  type: "text",
92
- text: "**Error**: Account name '#{account_name}' not found in environment variables.\n\nAvailable accounts: #{available_accounts.join(', ')}\n\nTo configure: Set ENV['#{account_name}'] to your account ID."
91
+ text: "**Error**: Account name '#{account_name}' not found in configured accounts.\n\nAvailable accounts: #{available_accounts.join(', ')}\n\nTo configure: Add the account to your schwab_rb configuration file."
93
92
  }])
94
93
  end
95
94
 
96
- log_debug("Found account ID: [REDACTED] for account name: #{account_name}")
97
- log_debug("Fetching account numbers mapping")
98
-
99
- account_numbers = client.get_account_numbers
100
-
101
- unless account_numbers
102
- log_error("Failed to retrieve account numbers")
103
- return MCP::Tool::Response.new([{
104
- type: "text",
105
- text: "**Error**: Failed to retrieve account numbers from Schwab API"
106
- }])
107
- end
108
-
109
- log_debug("Account mappings retrieved (#{account_numbers.size} accounts found)")
110
-
111
- account_hash = account_numbers.find_hash_value(account_id)
112
-
113
- unless account_hash
114
- log_error("Account ID not found in available accounts")
115
- return MCP::Tool::Response.new([{
116
- type: "text",
117
- text: "**Error**: Account ID not found in available accounts. #{account_numbers.size} accounts available."
118
- }])
119
- end
120
-
121
- log_debug("Found account hash for account ID: #{account_name}")
95
+ log_debug("Using account name: #{account_name}")
122
96
 
123
97
  start_date_obj = nil
124
98
  end_date_obj = nil
@@ -150,7 +124,7 @@ module SchwabMCP
150
124
  log_debug("Fetching transactions with params - start_date: #{start_date_obj}, end_date: #{end_date_obj}, transaction_types: #{transaction_types}, symbol: #{symbol}")
151
125
 
152
126
  transactions = client.get_transactions(
153
- account_hash,
127
+ account_name: account_name,
154
128
  start_date: start_date_obj,
155
129
  end_date: end_date_obj,
156
130
  transaction_types: transaction_types,
@@ -16,8 +16,8 @@ module SchwabMCP
16
16
  properties: {
17
17
  symbol: {
18
18
  type: "string",
19
- description: "Instrument symbol (e.g., 'AAPL', 'TSLA')",
20
- pattern: "^[A-Za-z]{1,5}$"
19
+ description: "Instrument symbol (e.g., 'AAPL', 'TSLA', '$SPX')",
20
+ pattern: '^[\$\^]?[A-Za-z0-9]{1,5}$'
21
21
  },
22
22
  contract_type: {
23
23
  type: "string",
@@ -169,7 +169,7 @@ module SchwabMCP
169
169
  params[:entitlement] = entitlement if entitlement
170
170
 
171
171
  log_debug("Making API request for option chain with params: #{params}")
172
- option_chain = client.get_option_chain(symbol.upcase, return_data_objects: true, **params)
172
+ option_chain = client.get_option_chain(symbol.upcase, **params)
173
173
 
174
174
  if option_chain
175
175
  log_info("Successfully retrieved option chain for #{symbol}")