ruby_llm-mcp 0.7.1 → 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.
Files changed (149) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +144 -162
  3. data/lib/generators/ruby_llm/mcp/{install_generator.rb → install/install_generator.rb} +4 -2
  4. data/lib/generators/ruby_llm/mcp/{templates → install/templates}/initializer.rb +21 -4
  5. data/lib/generators/ruby_llm/mcp/install/templates/mcps.yml +29 -0
  6. data/lib/generators/ruby_llm/mcp/oauth/install_generator.rb +354 -0
  7. data/lib/generators/ruby_llm/mcp/oauth/templates/concerns/mcp_token_storage.rb.tt +114 -0
  8. data/lib/generators/ruby_llm/mcp/oauth/templates/concerns/user_mcp_oauth_concern.rb.tt +90 -0
  9. data/lib/generators/ruby_llm/mcp/oauth/templates/controllers/mcp_connections_controller.rb.tt +239 -0
  10. data/lib/generators/ruby_llm/mcp/oauth/templates/jobs/cleanup_expired_oauth_states_job.rb.tt +27 -0
  11. data/lib/generators/ruby_llm/mcp/oauth/templates/jobs/example_job.rb.tt +78 -0
  12. data/lib/generators/ruby_llm/mcp/oauth/templates/lib/mcp_client.rb.tt +68 -0
  13. data/lib/generators/ruby_llm/mcp/oauth/templates/migrations/create_mcp_oauth_credentials.rb.tt +19 -0
  14. data/lib/generators/ruby_llm/mcp/oauth/templates/migrations/create_mcp_oauth_states.rb.tt +21 -0
  15. data/lib/generators/ruby_llm/mcp/oauth/templates/models/mcp_oauth_credential.rb.tt +54 -0
  16. data/lib/generators/ruby_llm/mcp/oauth/templates/models/mcp_oauth_state.rb.tt +30 -0
  17. data/lib/generators/ruby_llm/mcp/oauth/templates/views/index.html.erb +646 -0
  18. data/lib/generators/ruby_llm/mcp/oauth/templates/views/show.html.erb +560 -0
  19. data/lib/ruby_llm/mcp/adapters/base_adapter.rb +215 -0
  20. data/lib/ruby_llm/mcp/adapters/mcp_sdk_adapter.rb +413 -0
  21. data/lib/ruby_llm/mcp/adapters/mcp_transports/coordinator_stub.rb +41 -0
  22. data/lib/ruby_llm/mcp/adapters/mcp_transports/sse.rb +56 -0
  23. data/lib/ruby_llm/mcp/adapters/mcp_transports/stdio.rb +56 -0
  24. data/lib/ruby_llm/mcp/adapters/mcp_transports/streamable_http.rb +90 -0
  25. data/lib/ruby_llm/mcp/adapters/ruby_llm_adapter.rb +216 -0
  26. data/lib/ruby_llm/mcp/auth/browser/callback_handler.rb +71 -0
  27. data/lib/ruby_llm/mcp/auth/browser/callback_server.rb +36 -0
  28. data/lib/ruby_llm/mcp/auth/browser/http_server.rb +112 -0
  29. data/lib/ruby_llm/mcp/auth/browser/opener.rb +39 -0
  30. data/lib/ruby_llm/mcp/auth/browser/pages.rb +607 -0
  31. data/lib/ruby_llm/mcp/auth/browser_oauth_provider.rb +427 -0
  32. data/lib/ruby_llm/mcp/auth/client_registrar.rb +170 -0
  33. data/lib/ruby_llm/mcp/auth/discoverer.rb +255 -0
  34. data/lib/ruby_llm/mcp/auth/flows/authorization_code_flow.rb +122 -0
  35. data/lib/ruby_llm/mcp/auth/flows/client_credentials_flow.rb +67 -0
  36. data/lib/ruby_llm/mcp/auth/grant_strategies/authorization_code.rb +31 -0
  37. data/lib/ruby_llm/mcp/auth/grant_strategies/base.rb +31 -0
  38. data/lib/ruby_llm/mcp/auth/grant_strategies/client_credentials.rb +31 -0
  39. data/lib/ruby_llm/mcp/auth/http_response_handler.rb +63 -0
  40. data/lib/ruby_llm/mcp/auth/memory_storage.rb +91 -0
  41. data/lib/ruby_llm/mcp/auth/oauth_provider.rb +341 -0
  42. data/lib/ruby_llm/mcp/auth/security.rb +44 -0
  43. data/lib/ruby_llm/mcp/auth/session_manager.rb +54 -0
  44. data/lib/ruby_llm/mcp/auth/token_manager.rb +307 -0
  45. data/lib/ruby_llm/mcp/auth/transport_oauth_helper.rb +107 -0
  46. data/lib/ruby_llm/mcp/auth/url_builder.rb +135 -0
  47. data/lib/ruby_llm/mcp/auth.rb +371 -0
  48. data/lib/ruby_llm/mcp/client.rb +312 -35
  49. data/lib/ruby_llm/mcp/configuration.rb +199 -24
  50. data/lib/ruby_llm/mcp/elicitation.rb +261 -14
  51. data/lib/ruby_llm/mcp/errors.rb +29 -0
  52. data/lib/ruby_llm/mcp/extensions/apps/constants.rb +28 -0
  53. data/lib/ruby_llm/mcp/extensions/apps/resource_metadata.rb +24 -0
  54. data/lib/ruby_llm/mcp/extensions/apps/tool_metadata.rb +45 -0
  55. data/lib/ruby_llm/mcp/extensions/configuration.rb +72 -0
  56. data/lib/ruby_llm/mcp/extensions/constants.rb +16 -0
  57. data/lib/ruby_llm/mcp/extensions/registry.rb +85 -0
  58. data/lib/ruby_llm/mcp/handlers/approval_decision.rb +90 -0
  59. data/lib/ruby_llm/mcp/handlers/async_response.rb +181 -0
  60. data/lib/ruby_llm/mcp/handlers/concerns/approval_actions.rb +42 -0
  61. data/lib/ruby_llm/mcp/handlers/concerns/async_execution.rb +80 -0
  62. data/lib/ruby_llm/mcp/handlers/concerns/elicitation_actions.rb +42 -0
  63. data/lib/ruby_llm/mcp/handlers/concerns/error_handling.rb +29 -0
  64. data/lib/ruby_llm/mcp/handlers/concerns/guard_checks.rb +72 -0
  65. data/lib/ruby_llm/mcp/handlers/concerns/lifecycle.rb +84 -0
  66. data/lib/ruby_llm/mcp/handlers/concerns/logging.rb +19 -0
  67. data/lib/ruby_llm/mcp/handlers/concerns/model_filtering.rb +36 -0
  68. data/lib/ruby_llm/mcp/handlers/concerns/options.rb +83 -0
  69. data/lib/ruby_llm/mcp/handlers/concerns/registry_integration.rb +54 -0
  70. data/lib/ruby_llm/mcp/handlers/concerns/sampling_actions.rb +84 -0
  71. data/lib/ruby_llm/mcp/handlers/concerns/timeouts.rb +52 -0
  72. data/lib/ruby_llm/mcp/handlers/concerns/tool_filtering.rb +50 -0
  73. data/lib/ruby_llm/mcp/handlers/elicitation_handler.rb +58 -0
  74. data/lib/ruby_llm/mcp/handlers/elicitation_registry.rb +203 -0
  75. data/lib/ruby_llm/mcp/handlers/human_in_the_loop_handler.rb +93 -0
  76. data/lib/ruby_llm/mcp/handlers/human_in_the_loop_registry.rb +271 -0
  77. data/lib/ruby_llm/mcp/handlers/promise.rb +192 -0
  78. data/lib/ruby_llm/mcp/handlers/sampling_handler.rb +64 -0
  79. data/lib/ruby_llm/mcp/handlers.rb +14 -0
  80. data/lib/ruby_llm/mcp/native/cancellable_operation.rb +94 -0
  81. data/lib/ruby_llm/mcp/native/client.rb +551 -0
  82. data/lib/ruby_llm/mcp/native/json_rpc.rb +170 -0
  83. data/lib/ruby_llm/mcp/native/messages/helpers.rb +39 -0
  84. data/lib/ruby_llm/mcp/native/messages/notifications.rb +60 -0
  85. data/lib/ruby_llm/mcp/native/messages/requests.rb +267 -0
  86. data/lib/ruby_llm/mcp/native/messages/responses.rb +114 -0
  87. data/lib/ruby_llm/mcp/native/messages.rb +43 -0
  88. data/lib/ruby_llm/mcp/native/notification.rb +16 -0
  89. data/lib/ruby_llm/mcp/native/protocol.rb +79 -0
  90. data/lib/ruby_llm/mcp/native/response_handler.rb +220 -0
  91. data/lib/ruby_llm/mcp/native/task_registry.rb +62 -0
  92. data/lib/ruby_llm/mcp/native/transport.rb +88 -0
  93. data/lib/ruby_llm/mcp/native/transports/sse.rb +655 -0
  94. data/lib/ruby_llm/mcp/native/transports/stdio.rb +367 -0
  95. data/lib/ruby_llm/mcp/native/transports/streamable_http.rb +1024 -0
  96. data/lib/ruby_llm/mcp/native/transports/support/http_client.rb +28 -0
  97. data/lib/ruby_llm/mcp/native/transports/support/rate_limiter.rb +49 -0
  98. data/lib/ruby_llm/mcp/native/transports/support/timeout.rb +36 -0
  99. data/lib/ruby_llm/mcp/native.rb +12 -0
  100. data/lib/ruby_llm/mcp/notification_handler.rb +43 -5
  101. data/lib/ruby_llm/mcp/prompt.rb +7 -7
  102. data/lib/ruby_llm/mcp/railtie.rb +7 -13
  103. data/lib/ruby_llm/mcp/resource.rb +17 -8
  104. data/lib/ruby_llm/mcp/resource_template.rb +8 -7
  105. data/lib/ruby_llm/mcp/result.rb +8 -4
  106. data/lib/ruby_llm/mcp/roots.rb +4 -4
  107. data/lib/ruby_llm/mcp/sample.rb +83 -13
  108. data/lib/ruby_llm/mcp/schema_validator.rb +33 -0
  109. data/lib/ruby_llm/mcp/server_capabilities.rb +41 -0
  110. data/lib/ruby_llm/mcp/task.rb +65 -0
  111. data/lib/ruby_llm/mcp/tool.rb +33 -27
  112. data/lib/ruby_llm/mcp/version.rb +1 -1
  113. data/lib/ruby_llm/mcp.rb +37 -7
  114. data/lib/tasks/smoke.rake +66 -0
  115. metadata +115 -39
  116. data/lib/generators/ruby_llm/mcp/templates/mcps.yml +0 -9
  117. data/lib/ruby_llm/mcp/coordinator.rb +0 -293
  118. data/lib/ruby_llm/mcp/notifications/cancelled.rb +0 -32
  119. data/lib/ruby_llm/mcp/notifications/initialize.rb +0 -24
  120. data/lib/ruby_llm/mcp/notifications/roots_list_change.rb +0 -26
  121. data/lib/ruby_llm/mcp/protocol.rb +0 -34
  122. data/lib/ruby_llm/mcp/requests/completion_prompt.rb +0 -50
  123. data/lib/ruby_llm/mcp/requests/completion_resource.rb +0 -50
  124. data/lib/ruby_llm/mcp/requests/initialization.rb +0 -34
  125. data/lib/ruby_llm/mcp/requests/logging_set_level.rb +0 -28
  126. data/lib/ruby_llm/mcp/requests/ping.rb +0 -24
  127. data/lib/ruby_llm/mcp/requests/prompt_call.rb +0 -32
  128. data/lib/ruby_llm/mcp/requests/prompt_list.rb +0 -31
  129. data/lib/ruby_llm/mcp/requests/resource_list.rb +0 -31
  130. data/lib/ruby_llm/mcp/requests/resource_read.rb +0 -30
  131. data/lib/ruby_llm/mcp/requests/resource_template_list.rb +0 -31
  132. data/lib/ruby_llm/mcp/requests/resources_subscribe.rb +0 -30
  133. data/lib/ruby_llm/mcp/requests/shared/meta.rb +0 -32
  134. data/lib/ruby_llm/mcp/requests/shared/pagination.rb +0 -17
  135. data/lib/ruby_llm/mcp/requests/tool_call.rb +0 -35
  136. data/lib/ruby_llm/mcp/requests/tool_list.rb +0 -31
  137. data/lib/ruby_llm/mcp/response_handler.rb +0 -67
  138. data/lib/ruby_llm/mcp/responses/elicitation.rb +0 -33
  139. data/lib/ruby_llm/mcp/responses/error.rb +0 -33
  140. data/lib/ruby_llm/mcp/responses/ping.rb +0 -28
  141. data/lib/ruby_llm/mcp/responses/roots_list.rb +0 -31
  142. data/lib/ruby_llm/mcp/responses/sampling_create_message.rb +0 -50
  143. data/lib/ruby_llm/mcp/transport.rb +0 -58
  144. data/lib/ruby_llm/mcp/transports/sse.rb +0 -341
  145. data/lib/ruby_llm/mcp/transports/stdio.rb +0 -230
  146. data/lib/ruby_llm/mcp/transports/streamable_http.rb +0 -723
  147. data/lib/ruby_llm/mcp/transports/support/http_client.rb +0 -28
  148. data/lib/ruby_llm/mcp/transports/support/rate_limit.rb +0 -47
  149. data/lib/ruby_llm/mcp/transports/support/timeout.rb +0 -34
@@ -0,0 +1,646 @@
1
+ <div class="mcp-connections-container">
2
+ <h1>MCP Server Connections</h1>
3
+
4
+ <% if flash[:notice] %>
5
+ <div class="mcp-alert mcp-alert-success">
6
+ <%= flash[:notice] %>
7
+ </div>
8
+ <% end %>
9
+
10
+ <% if flash[:alert] %>
11
+ <div class="mcp-alert mcp-alert-warning">
12
+ <%= flash[:alert] %>
13
+ </div>
14
+ <% end %>
15
+
16
+ <% if @credentials.any? %>
17
+ <h2>Connected Servers</h2>
18
+ <div class="mcp-table-wrapper">
19
+ <table class="mcp-table">
20
+ <thead>
21
+ <tr>
22
+ <th>Name</th>
23
+ <th>Status</th>
24
+ <th>Scopes</th>
25
+ <th>Expires</th>
26
+ <th>Last Refreshed</th>
27
+ <th>Actions</th>
28
+ </tr>
29
+ </thead>
30
+ <tbody>
31
+ <% @credentials.each do |credential| %>
32
+ <tr>
33
+ <td>
34
+ <strong><%= credential.name %></strong><br>
35
+ <small class="mcp-text-muted"><code class="mcp-code"><%= credential.server_url %></code></small>
36
+ </td>
37
+ <td>
38
+ <% if credential.expired? %>
39
+ <span class="mcp-badge mcp-badge-danger">Expired</span>
40
+ <% elsif credential.expires_soon? %>
41
+ <span class="mcp-badge mcp-badge-warning">Expiring Soon</span>
42
+ <% else %>
43
+ <span class="mcp-badge mcp-badge-success">Active</span>
44
+ <% end %>
45
+ </td>
46
+ <td>
47
+ <% if credential.token&.scope %>
48
+ <code class="mcp-code mcp-code-muted"><%= credential.token.scope %></code>
49
+ <% else %>
50
+ <span class="mcp-text-muted">N/A</span>
51
+ <% end %>
52
+ </td>
53
+ <td>
54
+ <% if credential.token_expires_at %>
55
+ <%= time_tag credential.token_expires_at, credential.token_expires_at.to_fs(:short) %>
56
+ <br>
57
+ <small class="mcp-text-muted">
58
+ (<%= time_ago_in_words(credential.token_expires_at) %>
59
+ <%= credential.token_expires_at > Time.current ? "from now" : "ago" %>)
60
+ </small>
61
+ <% else %>
62
+ <span class="mcp-text-muted">Never</span>
63
+ <% end %>
64
+ </td>
65
+ <td>
66
+ <% if credential.last_refreshed_at %>
67
+ <%= time_ago_in_words(credential.last_refreshed_at) %> ago
68
+ <% else %>
69
+ <span class="mcp-text-muted">Never</span>
70
+ <% end %>
71
+ </td>
72
+ <td>
73
+ <div class="mcp-btn-group">
74
+ <%= link_to "View Tools",
75
+ mcp_connection_path(credential),
76
+ class: "mcp-btn mcp-btn-primary" %>
77
+ <%= button_to "Refresh",
78
+ mcp_connection_path(credential),
79
+ method: :patch,
80
+ class: "mcp-btn #{credential.expired? || credential.expires_soon? ? 'mcp-btn-warning' : 'mcp-btn-secondary'}" %>
81
+ <%= button_to "Disconnect",
82
+ mcp_connection_path(credential),
83
+ method: :delete,
84
+ form: { data: { turbo_confirm: "Are you sure you want to disconnect this MCP server?" } },
85
+ class: "mcp-btn mcp-btn-danger" %>
86
+ </div>
87
+ </td>
88
+ </tr>
89
+ <% end %>
90
+ </tbody>
91
+ </table>
92
+ </div>
93
+ <% else %>
94
+ <div class="mcp-alert mcp-alert-info">
95
+ <h4>No MCP Servers Connected</h4>
96
+ <p>Connect to an MCP server to enable AI-powered features in your account.</p>
97
+ </div>
98
+ <% end %>
99
+
100
+ <div class="mcp-card">
101
+ <div class="mcp-card-body">
102
+ <h3>Add New Server</h3>
103
+ <p>Connect to an MCP server to access tools, resources, and AI capabilities.</p>
104
+
105
+ <%= form_with url: mcp_connections_path, method: :post, class: "mcp-form", data: { turbo: false } do |f| %>
106
+ <div class="mcp-form-group">
107
+ <%= label_tag :name, "Server Name", class: "mcp-form-label" %>
108
+ <%= text_field_tag :name, nil,
109
+ placeholder: "My MCP Server",
110
+ required: true,
111
+ class: "mcp-form-input" %>
112
+ <small class="mcp-form-help">A friendly name to identify this server</small>
113
+ </div>
114
+
115
+ <div class="mcp-form-group">
116
+ <%= label_tag :server_url, "MCP Server URL", class: "mcp-form-label" %>
117
+ <%= text_field_tag :server_url, nil,
118
+ placeholder: "https://mcp.example.com/api",
119
+ required: true,
120
+ class: "mcp-form-input" %>
121
+ <small class="mcp-form-help">Enter the full URL of your MCP server</small>
122
+ </div>
123
+
124
+ <div class="mcp-form-group">
125
+ <%= label_tag :scope, "OAuth Scopes (optional)", class: "mcp-form-label" %>
126
+ <%= text_field_tag :scope, "mcp:read mcp:write",
127
+ placeholder: "mcp:read mcp:write",
128
+ class: "mcp-form-input" %>
129
+ <small class="mcp-form-help">Space-separated list of permissions to request</small>
130
+ </div>
131
+
132
+ <%= submit_tag "Connect Server", class: "mcp-btn mcp-btn-primary" %>
133
+ <% end %>
134
+ </div>
135
+ </div>
136
+
137
+ <div class="mcp-info">
138
+ <h3>What is MCP?</h3>
139
+ <p>
140
+ Model Context Protocol (MCP) allows AI models to securely access external
141
+ data and tools. By connecting your account, you grant AI features permission
142
+ to access specific resources on your behalf.
143
+ </p>
144
+
145
+ <h4>Security & Privacy</h4>
146
+ <ul>
147
+ <li>Tokens are encrypted and stored securely</li>
148
+ <li>Each connection is specific to your account</li>
149
+ <li>You can disconnect at any time</li>
150
+ <li>Tokens automatically refresh when possible</li>
151
+ </ul>
152
+ </div>
153
+ </div>
154
+
155
+ <style>
156
+ /* MCP Connections - Self-contained styles */
157
+ .mcp-connections-container {
158
+ max-width: 1200px;
159
+ margin: 2rem auto;
160
+ padding: 0 1rem;
161
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
162
+ line-height: 1.6;
163
+ }
164
+
165
+ .mcp-connections-container h1 {
166
+ font-size: 2rem;
167
+ font-weight: 600;
168
+ margin-bottom: 2rem;
169
+ color: #1a1a1a;
170
+ }
171
+
172
+ .mcp-connections-container h2 {
173
+ font-size: 1.5rem;
174
+ font-weight: 600;
175
+ margin-top: 2rem;
176
+ margin-bottom: 1rem;
177
+ color: #2a2a2a;
178
+ }
179
+
180
+ .mcp-connections-container h3 {
181
+ font-size: 1.25rem;
182
+ font-weight: 600;
183
+ margin-top: 1.5rem;
184
+ margin-bottom: 0.75rem;
185
+ color: #2a2a2a;
186
+ }
187
+
188
+ .mcp-connections-container h4 {
189
+ font-size: 1.1rem;
190
+ font-weight: 600;
191
+ margin-top: 1rem;
192
+ margin-bottom: 0.5rem;
193
+ color: #2a2a2a;
194
+ }
195
+
196
+ /* Table styles */
197
+ .mcp-table-wrapper {
198
+ overflow-x: auto;
199
+ margin-bottom: 2rem;
200
+ border-radius: 8px;
201
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
202
+ }
203
+
204
+ .mcp-table {
205
+ width: 100%;
206
+ border-collapse: collapse;
207
+ background: white;
208
+ }
209
+
210
+ .mcp-table thead {
211
+ background-color: #f8f9fa;
212
+ border-bottom: 2px solid #dee2e6;
213
+ }
214
+
215
+ .mcp-table th {
216
+ padding: 0.75rem;
217
+ text-align: left;
218
+ font-weight: 600;
219
+ font-size: 0.875rem;
220
+ color: #495057;
221
+ text-transform: uppercase;
222
+ letter-spacing: 0.05em;
223
+ }
224
+
225
+ .mcp-table td {
226
+ padding: 0.75rem;
227
+ border-top: 1px solid #dee2e6;
228
+ vertical-align: top;
229
+ }
230
+
231
+ .mcp-table tbody tr:hover {
232
+ background-color: #f8f9fa;
233
+ }
234
+
235
+ /* Badge styles */
236
+ .mcp-badge {
237
+ display: inline-block;
238
+ padding: 0.25rem 0.5rem;
239
+ font-size: 0.75rem;
240
+ font-weight: 600;
241
+ line-height: 1;
242
+ text-align: center;
243
+ white-space: nowrap;
244
+ border-radius: 4px;
245
+ }
246
+
247
+ .mcp-badge-success {
248
+ background-color: #d4edda;
249
+ color: #155724;
250
+ }
251
+
252
+ .mcp-badge-warning {
253
+ background-color: #fff3cd;
254
+ color: #856404;
255
+ }
256
+
257
+ .mcp-badge-danger {
258
+ background-color: #f8d7da;
259
+ color: #721c24;
260
+ }
261
+
262
+ /* Button styles */
263
+ .mcp-btn {
264
+ display: inline-block;
265
+ padding: 0.375rem 0.75rem;
266
+ font-size: 0.875rem;
267
+ font-weight: 500;
268
+ line-height: 1.5;
269
+ text-align: center;
270
+ text-decoration: none;
271
+ border: 1px solid transparent;
272
+ border-radius: 4px;
273
+ cursor: pointer;
274
+ transition: all 0.15s ease-in-out;
275
+ }
276
+
277
+ .mcp-btn:hover {
278
+ opacity: 0.9;
279
+ }
280
+
281
+ .mcp-btn-primary {
282
+ color: white;
283
+ background-color: #007bff;
284
+ border-color: #007bff;
285
+ }
286
+
287
+ .mcp-btn-primary:hover {
288
+ background-color: #0056b3;
289
+ border-color: #0056b3;
290
+ }
291
+
292
+ .mcp-btn-secondary {
293
+ color: white;
294
+ background-color: #6c757d;
295
+ border-color: #6c757d;
296
+ }
297
+
298
+ .mcp-btn-secondary:hover {
299
+ background-color: #5a6268;
300
+ border-color: #5a6268;
301
+ }
302
+
303
+ .mcp-btn-warning {
304
+ color: #212529;
305
+ background-color: #ffc107;
306
+ border-color: #ffc107;
307
+ }
308
+
309
+ .mcp-btn-warning:hover {
310
+ background-color: #e0a800;
311
+ border-color: #e0a800;
312
+ }
313
+
314
+ .mcp-btn-danger {
315
+ color: white;
316
+ background-color: #dc3545;
317
+ border-color: #dc3545;
318
+ }
319
+
320
+ .mcp-btn-danger:hover {
321
+ background-color: #c82333;
322
+ border-color: #c82333;
323
+ }
324
+
325
+ .mcp-btn-group {
326
+ display: flex;
327
+ gap: 0.5rem;
328
+ flex-wrap: wrap;
329
+ }
330
+
331
+ /* Code styles */
332
+ .mcp-code {
333
+ background-color: #f5f5f5;
334
+ padding: 0.125rem 0.375rem;
335
+ border-radius: 3px;
336
+ font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
337
+ font-size: 0.875em;
338
+ color: #212529;
339
+ }
340
+
341
+ .mcp-code-muted {
342
+ color: #6c757d;
343
+ }
344
+
345
+ /* Text utilities */
346
+ .mcp-text-muted {
347
+ color: #6c757d;
348
+ }
349
+
350
+ /* Card styles */
351
+ .mcp-card {
352
+ background: white;
353
+ border: 1px solid #dee2e6;
354
+ border-radius: 8px;
355
+ margin-bottom: 2rem;
356
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
357
+ }
358
+
359
+ .mcp-card-body {
360
+ padding: 1.5rem;
361
+ }
362
+
363
+ /* Alert styles */
364
+ .mcp-alert {
365
+ padding: 1rem;
366
+ margin-bottom: 1rem;
367
+ border: 1px solid transparent;
368
+ border-radius: 4px;
369
+ }
370
+
371
+ .mcp-alert-info {
372
+ background-color: #d1ecf1;
373
+ border-color: #bee5eb;
374
+ color: #0c5460;
375
+ }
376
+
377
+ .mcp-alert-warning {
378
+ background-color: #fff3cd;
379
+ border-color: #ffeaa7;
380
+ color: #856404;
381
+ }
382
+
383
+ .mcp-alert-success {
384
+ background-color: #d4edda;
385
+ border-color: #c3e6cb;
386
+ color: #155724;
387
+ }
388
+
389
+ .mcp-alert h4 {
390
+ margin-top: 0;
391
+ color: inherit;
392
+ }
393
+
394
+ /* Form styles */
395
+ .mcp-form {
396
+ margin-top: 1rem;
397
+ }
398
+
399
+ .mcp-form-group {
400
+ margin-bottom: 1rem;
401
+ }
402
+
403
+ .mcp-form-label {
404
+ display: block;
405
+ margin-bottom: 0.5rem;
406
+ font-weight: 600;
407
+ font-size: 0.875rem;
408
+ color: #2a2a2a;
409
+ }
410
+
411
+ .mcp-form-input {
412
+ display: block;
413
+ width: 100%;
414
+ padding: 0.5rem 0.75rem;
415
+ font-size: 1rem;
416
+ line-height: 1.5;
417
+ color: #212529;
418
+ background-color: white;
419
+ border: 1px solid #ced4da;
420
+ border-radius: 4px;
421
+ transition: border-color 0.15s ease-in-out;
422
+ }
423
+
424
+ .mcp-form-input:focus {
425
+ outline: 0;
426
+ border-color: #007bff;
427
+ box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
428
+ }
429
+
430
+ .mcp-form-help {
431
+ display: block;
432
+ margin-top: 0.25rem;
433
+ font-size: 0.875rem;
434
+ color: #6c757d;
435
+ }
436
+
437
+ .mcp-form .mcp-btn {
438
+ margin-top: 0.5rem;
439
+ }
440
+
441
+ /* Info section */
442
+ .mcp-info {
443
+ margin-top: 2rem;
444
+ padding: 1.5rem;
445
+ background-color: #f8f9fa;
446
+ border-radius: 8px;
447
+ }
448
+
449
+ .mcp-info ul {
450
+ margin: 0.5rem 0;
451
+ padding-left: 1.5rem;
452
+ }
453
+
454
+ .mcp-info li {
455
+ margin-bottom: 0.25rem;
456
+ }
457
+
458
+ /* Responsive */
459
+ @media (max-width: 768px) {
460
+ .mcp-connections-container {
461
+ padding: 0 0.5rem;
462
+ }
463
+
464
+ .mcp-table {
465
+ font-size: 0.875rem;
466
+ }
467
+
468
+ .mcp-table th,
469
+ .mcp-table td {
470
+ padding: 0.5rem;
471
+ }
472
+
473
+ .mcp-btn-group {
474
+ flex-direction: column;
475
+ }
476
+
477
+ .mcp-btn {
478
+ width: 100%;
479
+ }
480
+ }
481
+
482
+ /* Dark Mode Support */
483
+ @media (prefers-color-scheme: dark) {
484
+ body {
485
+ background-color: #1a1a1a;
486
+ }
487
+
488
+ .mcp-connections-container {
489
+ color: #e0e0e0;
490
+ background-color: #1a1a1a;
491
+ }
492
+
493
+ .mcp-connections-container h1,
494
+ .mcp-connections-container h2,
495
+ .mcp-connections-container h3,
496
+ .mcp-connections-container h4 {
497
+ color: #f0f0f0;
498
+ }
499
+
500
+ /* Table dark mode */
501
+ .mcp-table {
502
+ background: #2a2a2a;
503
+ color: #e0e0e0;
504
+ }
505
+
506
+ .mcp-table thead {
507
+ background-color: #1a1a1a;
508
+ border-bottom-color: #3a3a3a;
509
+ }
510
+
511
+ .mcp-table th {
512
+ color: #b0b0b0;
513
+ }
514
+
515
+ .mcp-table td {
516
+ border-top-color: #3a3a3a;
517
+ }
518
+
519
+ .mcp-table tbody tr:hover {
520
+ background-color: #333333;
521
+ }
522
+
523
+ /* Badge dark mode */
524
+ .mcp-badge-success {
525
+ background-color: #1e4620;
526
+ color: #8fd98f;
527
+ }
528
+
529
+ .mcp-badge-warning {
530
+ background-color: #4a3a00;
531
+ color: #ffd966;
532
+ }
533
+
534
+ .mcp-badge-danger {
535
+ background-color: #4a1a1f;
536
+ color: #ff8a95;
537
+ }
538
+
539
+ /* Button dark mode */
540
+ .mcp-btn-primary {
541
+ background-color: #0d6efd;
542
+ border-color: #0d6efd;
543
+ }
544
+
545
+ .mcp-btn-primary:hover {
546
+ background-color: #0a58ca;
547
+ border-color: #0a58ca;
548
+ }
549
+
550
+ .mcp-btn-secondary {
551
+ background-color: #5a6268;
552
+ border-color: #5a6268;
553
+ }
554
+
555
+ .mcp-btn-secondary:hover {
556
+ background-color: #4e555b;
557
+ border-color: #4e555b;
558
+ }
559
+
560
+ .mcp-btn-warning {
561
+ background-color: #e0a800;
562
+ border-color: #e0a800;
563
+ color: #000;
564
+ }
565
+
566
+ .mcp-btn-warning:hover {
567
+ background-color: #c79100;
568
+ border-color: #c79100;
569
+ }
570
+
571
+ .mcp-btn-danger {
572
+ background-color: #bb2d3b;
573
+ border-color: #bb2d3b;
574
+ }
575
+
576
+ .mcp-btn-danger:hover {
577
+ background-color: #a02633;
578
+ border-color: #a02633;
579
+ }
580
+
581
+ /* Code dark mode */
582
+ .mcp-code {
583
+ background-color: #1a1a1a;
584
+ color: #e0e0e0;
585
+ }
586
+
587
+ .mcp-code-muted {
588
+ color: #8a8a8a;
589
+ }
590
+
591
+ /* Text utilities dark mode */
592
+ .mcp-text-muted {
593
+ color: #8a8a8a;
594
+ }
595
+
596
+ /* Card dark mode */
597
+ .mcp-card {
598
+ background: #2a2a2a;
599
+ border-color: #3a3a3a;
600
+ }
601
+
602
+ /* Alert dark mode */
603
+ .mcp-alert-info {
604
+ background-color: #0c3d47;
605
+ border-color: #0f5460;
606
+ color: #9fe5f0;
607
+ }
608
+
609
+ .mcp-alert-warning {
610
+ background-color: #4a3a00;
611
+ border-color: #665000;
612
+ color: #ffd966;
613
+ }
614
+
615
+ .mcp-alert-success {
616
+ background-color: #1e4620;
617
+ border-color: #2a5e2c;
618
+ color: #8fd98f;
619
+ }
620
+
621
+ /* Form dark mode */
622
+ .mcp-form-input {
623
+ background-color: #1a1a1a;
624
+ border-color: #3a3a3a;
625
+ color: #e0e0e0;
626
+ }
627
+
628
+ .mcp-form-input:focus {
629
+ border-color: #0d6efd;
630
+ box-shadow: 0 0 0 3px rgba(13, 110, 253, 0.25);
631
+ }
632
+
633
+ .mcp-form-label {
634
+ color: #d0d0d0;
635
+ }
636
+
637
+ .mcp-form-help {
638
+ color: #8a8a8a;
639
+ }
640
+
641
+ /* Info section dark mode */
642
+ .mcp-info {
643
+ background-color: #1a1a1a;
644
+ }
645
+ }
646
+ </style>