groww-mcp 1.0.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.
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrowwMcp
4
+ module Tools
5
+ class GetSmartOrders < GrowwMcp::BaseTool
6
+ description "Get list of all smart orders (GTT — Good Till Triggered, and OCO — One Cancels Other). " \
7
+ "Smart orders are conditional orders that trigger automatically when a price condition is met."
8
+
9
+ input_schema(
10
+ properties: {
11
+ smart_order_type: {
12
+ type: "string",
13
+ enum: %w[GTT OCO],
14
+ description: "Filter by smart order type (optional)",
15
+ },
16
+ page: { type: "integer", description: "Page number (optional)" },
17
+ page_size: { type: "integer", description: "Results per page (optional)" },
18
+ },
19
+ )
20
+
21
+ class << self
22
+ def call(server_context:, smart_order_type: nil, page: nil, page_size: nil)
23
+ client = server_context[:client]
24
+ result = client.smart_order_list(smart_order_type: smart_order_type, page: page, page_size: page_size)
25
+ format_response(result)
26
+ rescue GrowwMcp::ApiError => e
27
+ error_response(e)
28
+ end
29
+ end
30
+ end
31
+
32
+ class CreateSmartOrder < GrowwMcp::BaseTool
33
+ description "Create a GTT (Good Till Triggered) or OCO (One Cancels Other) smart order. " \
34
+ "GTT triggers a buy/sell when price crosses your trigger (requires: trigger_price, " \
35
+ "trigger_direction, order_type). " \
36
+ "OCO sets both a stop-loss AND target on an existing position — whichever hits first " \
37
+ "executes, the other cancels (requires: target_trigger_price, target_order_type, " \
38
+ "stop_loss_trigger_price, stop_loss_order_type). " \
39
+ "⚠️ This creates a REAL conditional order. Always confirm with the user first."
40
+
41
+ input_schema(
42
+ properties: {
43
+ smart_order_type: { type: "string", enum: %w[GTT OCO], description: "GTT or OCO" },
44
+ trading_symbol: { type: "string", description: "Stock/option symbol (e.g., RELIANCE)" },
45
+ exchange: { type: "string", enum: %w[NSE BSE], description: "Exchange (default: NSE)" },
46
+ segment: { type: "string", enum: %w[CASH FNO], description: "Market segment (default: CASH)" },
47
+ transaction_type: { type: "string", enum: %w[BUY SELL], description: "Buy or sell" },
48
+ quantity: { type: "integer", description: "Number of shares/lots" },
49
+ product_type: { type: "string", enum: %w[CNC MIS NRML], description: "CNC=delivery, MIS=intraday, NRML=F&O" },
50
+ duration: { type: "string", enum: %w[DAY GTC], description: "Validity: DAY or GTC (default: GTC)" },
51
+ # GTT fields
52
+ trigger_price: { type: "number", description: "GTT: price at which the order triggers" },
53
+ trigger_direction: { type: "string", enum: %w[UP DOWN], description: "GTT: trigger when price crosses UP or DOWN through trigger_price" },
54
+ order_type: { type: "string", enum: %w[MARKET LIMIT], description: "GTT: order type after trigger" },
55
+ price: { type: "number", description: "GTT: limit price (for LIMIT orders)" },
56
+ # OCO fields
57
+ net_position_quantity: { type: "integer", description: "OCO: current net position quantity in this symbol" },
58
+ target_trigger_price: { type: "number", description: "OCO: target trigger price" },
59
+ target_order_type: { type: "string", enum: %w[MARKET LIMIT], description: "OCO: target leg order type" },
60
+ target_price: { type: "number", description: "OCO: target limit price (for LIMIT)" },
61
+ stop_loss_trigger_price: { type: "number", description: "OCO: stop-loss trigger price" },
62
+ stop_loss_order_type: { type: "string", enum: %w[MARKET LIMIT], description: "OCO: stop-loss leg order type" },
63
+ stop_loss_price: { type: "number", description: "OCO: stop-loss limit price (for LIMIT)" },
64
+ },
65
+ required: %w[smart_order_type trading_symbol transaction_type quantity product_type],
66
+ )
67
+
68
+ class << self
69
+ def call(server_context:, smart_order_type:, trading_symbol:, transaction_type:, quantity:,
70
+ product_type:, exchange: "NSE", segment: "CASH", duration: "GTC",
71
+ trigger_price: nil, trigger_direction: nil, order_type: nil, price: nil,
72
+ net_position_quantity: nil, target_trigger_price: nil, target_order_type: nil,
73
+ target_price: nil, stop_loss_trigger_price: nil, stop_loss_order_type: nil,
74
+ stop_loss_price: nil)
75
+ client = server_context[:client]
76
+
77
+ order = {
78
+ smart_order_type: smart_order_type,
79
+ segment: segment,
80
+ trading_symbol: trading_symbol,
81
+ quantity: quantity,
82
+ product_type: product_type,
83
+ exchange: exchange,
84
+ duration: duration,
85
+ }
86
+
87
+ if smart_order_type == "GTT"
88
+ missing = { trigger_price: trigger_price, trigger_direction: trigger_direction,
89
+ order_type: order_type }.select { |_, v| v.nil? }.keys
90
+ return validation_error("GTT order missing required fields: #{missing.join(', ')}") unless missing.empty?
91
+
92
+ order[:trigger_price] = trigger_price.to_s
93
+ order[:trigger_direction] = trigger_direction
94
+ leg = { order_type: order_type, transaction_type: transaction_type }
95
+ leg[:price] = price if price
96
+ order[:order] = leg
97
+ else # OCO
98
+ missing = { target_trigger_price: target_trigger_price, target_order_type: target_order_type,
99
+ stop_loss_trigger_price: stop_loss_trigger_price,
100
+ stop_loss_order_type: stop_loss_order_type }.select { |_, v| v.nil? }.keys
101
+ return validation_error("OCO order missing required fields: #{missing.join(', ')}") unless missing.empty?
102
+
103
+ order[:transaction_type] = transaction_type
104
+ order[:net_position_quantity] = net_position_quantity if net_position_quantity
105
+
106
+ target = { trigger_price: target_trigger_price, order_type: target_order_type }
107
+ target[:price] = target_price if target_price
108
+ order[:target] = target
109
+
110
+ stop_loss = { trigger_price: stop_loss_trigger_price, order_type: stop_loss_order_type }
111
+ stop_loss[:price] = stop_loss_price if stop_loss_price
112
+ order[:stop_loss] = stop_loss
113
+ end
114
+
115
+ result = client.create_smart_order(order)
116
+ format_response(result)
117
+ rescue GrowwMcp::ApiError => e
118
+ error_response(e)
119
+ end
120
+
121
+ private
122
+
123
+ def validation_error(message)
124
+ MCP::Tool::Response.new([{ type: "text", text: "❌ #{message}" }])
125
+ end
126
+ end
127
+ end
128
+
129
+ class ModifySmartOrder < GrowwMcp::BaseTool
130
+ description "Modify an existing GTT or OCO smart order. " \
131
+ "GTT: change quantity, duration, trigger_price, trigger_direction, or the triggered order. " \
132
+ "OCO: change quantity, duration, product_type, or the target/stop-loss legs " \
133
+ "(a leg must be sent complete: trigger price + order type)."
134
+
135
+ input_schema(
136
+ properties: {
137
+ order_id: { type: "string", description: "Smart order ID (e.g., gtt_91a7f4, oco_a12bc3)" },
138
+ smart_order_type: { type: "string", enum: %w[GTT OCO], description: "GTT or OCO" },
139
+ segment: { type: "string", enum: %w[CASH FNO], description: "Market segment (default: CASH)" },
140
+ quantity: { type: "integer", description: "New quantity (optional)" },
141
+ duration: { type: "string", enum: %w[DAY GTC], description: "New validity (optional)" },
142
+ # GTT fields
143
+ trigger_price: { type: "number", description: "GTT: new trigger price (optional)" },
144
+ trigger_direction: { type: "string", enum: %w[UP DOWN], description: "GTT: new trigger direction (optional)" },
145
+ order_type: { type: "string", enum: %w[MARKET LIMIT], description: "GTT: new order type for the triggered order (optional)" },
146
+ price: { type: "number", description: "GTT: new limit price (optional)" },
147
+ transaction_type: { type: "string", enum: %w[BUY SELL], description: "GTT: transaction type for the triggered order (required when changing order_type/price)" },
148
+ # OCO fields
149
+ product_type: { type: "string", enum: %w[CNC MIS NRML], description: "OCO: new product type (optional)" },
150
+ target_trigger_price: { type: "number", description: "OCO: new target trigger price (optional)" },
151
+ target_order_type: { type: "string", enum: %w[MARKET LIMIT], description: "OCO: target leg order type (required with target_trigger_price)" },
152
+ target_price: { type: "number", description: "OCO: new target limit price (optional)" },
153
+ stop_loss_trigger_price: { type: "number", description: "OCO: new stop-loss trigger price (optional)" },
154
+ stop_loss_order_type: { type: "string", enum: %w[MARKET LIMIT], description: "OCO: stop-loss leg order type (required with stop_loss_trigger_price)" },
155
+ stop_loss_price: { type: "number", description: "OCO: new stop-loss limit price (optional)" },
156
+ },
157
+ required: %w[order_id smart_order_type],
158
+ )
159
+
160
+ class << self
161
+ def call(server_context:, order_id:, smart_order_type:, segment: "CASH",
162
+ quantity: nil, duration: nil, trigger_price: nil, trigger_direction: nil,
163
+ order_type: nil, price: nil, transaction_type: nil, product_type: nil,
164
+ target_trigger_price: nil, target_order_type: nil, target_price: nil,
165
+ stop_loss_trigger_price: nil, stop_loss_order_type: nil, stop_loss_price: nil)
166
+ client = server_context[:client]
167
+
168
+ mods = {}
169
+ mods[:quantity] = quantity if quantity
170
+ mods[:duration] = duration if duration
171
+ mods[:trigger_price] = trigger_price.to_s if trigger_price
172
+ mods[:trigger_direction] = trigger_direction if trigger_direction
173
+ mods[:product_type] = product_type if product_type
174
+
175
+ if order_type || price
176
+ leg = {}
177
+ leg[:order_type] = order_type if order_type
178
+ leg[:price] = price if price
179
+ leg[:transaction_type] = transaction_type if transaction_type
180
+ mods[:order] = leg
181
+ end
182
+
183
+ if target_trigger_price
184
+ target = { trigger_price: target_trigger_price, order_type: target_order_type }
185
+ target[:price] = target_price if target_price
186
+ mods[:target] = target
187
+ end
188
+
189
+ if stop_loss_trigger_price
190
+ stop_loss = { trigger_price: stop_loss_trigger_price, order_type: stop_loss_order_type }
191
+ stop_loss[:price] = stop_loss_price if stop_loss_price
192
+ mods[:stop_loss] = stop_loss
193
+ end
194
+
195
+ result = client.modify_smart_order(order_id, smart_order_type: smart_order_type,
196
+ segment: segment, modifications: mods)
197
+ format_response(result)
198
+ rescue GrowwMcp::ApiError => e
199
+ error_response(e)
200
+ end
201
+ end
202
+ end
203
+
204
+ class CancelSmartOrder < GrowwMcp::BaseTool
205
+ description "Cancel a pending GTT or OCO smart order."
206
+
207
+ input_schema(
208
+ properties: {
209
+ order_id: { type: "string", description: "Smart order ID to cancel (e.g., gtt_91a7f4, oco_a12bc3)" },
210
+ smart_order_type: { type: "string", enum: %w[GTT OCO], description: "GTT or OCO" },
211
+ segment: { type: "string", enum: %w[CASH FNO], description: "Market segment (default: CASH)" },
212
+ },
213
+ required: %w[order_id smart_order_type],
214
+ )
215
+
216
+ class << self
217
+ def call(server_context:, order_id:, smart_order_type:, segment: "CASH")
218
+ client = server_context[:client]
219
+ result = client.cancel_smart_order(order_id, smart_order_type: smart_order_type, segment: segment)
220
+ format_response(result)
221
+ rescue GrowwMcp::ApiError => e
222
+ error_response(e)
223
+ end
224
+ end
225
+ end
226
+ end
227
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mcp"
4
+
5
+ module GrowwMcp
6
+ module Tools
7
+ class GetProfile < GrowwMcp::BaseTool
8
+ description "Get your Groww trading account profile. " \
9
+ "Shows UCC, enabled exchanges (NSE/BSE), DDPI status, and active segments (CASH/FNO)."
10
+
11
+ input_schema(properties: {})
12
+
13
+ class << self
14
+ def call(server_context:)
15
+ client = server_context[:client]
16
+ result = client.profile
17
+ format_response(result)
18
+ rescue GrowwMcp::ApiError => e
19
+ error_response(e)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrowwMcp
4
+ VERSION = "1.0.0"
5
+ end
data/lib/groww_mcp.rb ADDED
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "groww_mcp/version"
4
+ require_relative "groww_mcp/auth"
5
+ require_relative "groww_mcp/client"
6
+ require_relative "groww_mcp/base_tool"
7
+ require_relative "groww_mcp/tools/auth_tools"
8
+ require_relative "groww_mcp/tools/portfolio_tools"
9
+ require_relative "groww_mcp/tools/order_tools"
10
+ require_relative "groww_mcp/tools/smart_order_tools"
11
+ require_relative "groww_mcp/tools/market_tools"
12
+ require_relative "groww_mcp/tools/option_chain_tools"
13
+ require_relative "groww_mcp/tools/instrument_tools"
14
+ require_relative "groww_mcp/tools/user_tools"
15
+
16
+ module GrowwMcp
17
+ ALL_TOOLS = [
18
+ # Auth
19
+ Tools::Authenticate,
20
+ # Portfolio
21
+ Tools::GetHoldings,
22
+ Tools::GetPositions,
23
+ Tools::GetMargins,
24
+ Tools::CalculateMargin,
25
+ # Orders
26
+ Tools::GetOrders,
27
+ Tools::GetOrderDetail,
28
+ Tools::GetOrderTrades,
29
+ Tools::PlaceOrder,
30
+ Tools::ModifyOrder,
31
+ Tools::CancelOrder,
32
+ # Smart Orders (GTT/OCO)
33
+ Tools::GetSmartOrders,
34
+ Tools::CreateSmartOrder,
35
+ Tools::ModifySmartOrder,
36
+ Tools::CancelSmartOrder,
37
+ # Market Data
38
+ Tools::GetQuote,
39
+ Tools::GetLTP,
40
+ Tools::GetOHLC,
41
+ Tools::GetOptionChain,
42
+ Tools::GetGreeks,
43
+ Tools::GetHistoricalData,
44
+ # Instruments
45
+ Tools::SearchInstruments,
46
+ Tools::GetInstrumentDetail,
47
+ Tools::DownloadInstruments,
48
+ # User
49
+ Tools::GetProfile,
50
+ ].freeze
51
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: groww-mcp
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jai Rajput
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: mcp
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.21'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.21'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rotp
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '6.3'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '6.3'
40
+ description: A Model Context Protocol (MCP) server that connects AI assistants to
41
+ the Groww trading platform. Supports automated TOTP authentication, portfolio management,
42
+ order execution, and live market data.
43
+ email:
44
+ - jai@tecorb.co
45
+ executables:
46
+ - groww-mcp
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".env.example"
51
+ - CHANGELOG.md
52
+ - LICENSE
53
+ - README.md
54
+ - bin/groww-mcp
55
+ - lib/groww_mcp.rb
56
+ - lib/groww_mcp/auth.rb
57
+ - lib/groww_mcp/base_tool.rb
58
+ - lib/groww_mcp/client.rb
59
+ - lib/groww_mcp/tools/auth_tools.rb
60
+ - lib/groww_mcp/tools/instrument_tools.rb
61
+ - lib/groww_mcp/tools/market_tools.rb
62
+ - lib/groww_mcp/tools/option_chain_tools.rb
63
+ - lib/groww_mcp/tools/order_tools.rb
64
+ - lib/groww_mcp/tools/portfolio_tools.rb
65
+ - lib/groww_mcp/tools/smart_order_tools.rb
66
+ - lib/groww_mcp/tools/user_tools.rb
67
+ - lib/groww_mcp/version.rb
68
+ homepage: https://github.com/developerjai/groww-mcp
69
+ licenses:
70
+ - MIT
71
+ metadata:
72
+ homepage_uri: https://github.com/developerjai/groww-mcp
73
+ source_code_uri: https://github.com/developerjai/groww-mcp
74
+ changelog_uri: https://github.com/developerjai/groww-mcp/blob/main/CHANGELOG.md
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 3.1.0
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubygems_version: 4.0.3
90
+ specification_version: 4
91
+ summary: MCP server for Groww Trading API
92
+ test_files: []