vector_mcp 0.2.0 → 0.3.1

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +281 -0
  3. data/README.md +302 -373
  4. data/lib/vector_mcp/definitions.rb +3 -1
  5. data/lib/vector_mcp/errors.rb +24 -0
  6. data/lib/vector_mcp/handlers/core.rb +132 -6
  7. data/lib/vector_mcp/logging/component.rb +131 -0
  8. data/lib/vector_mcp/logging/configuration.rb +156 -0
  9. data/lib/vector_mcp/logging/constants.rb +21 -0
  10. data/lib/vector_mcp/logging/core.rb +175 -0
  11. data/lib/vector_mcp/logging/filters/component.rb +69 -0
  12. data/lib/vector_mcp/logging/filters/level.rb +23 -0
  13. data/lib/vector_mcp/logging/formatters/base.rb +52 -0
  14. data/lib/vector_mcp/logging/formatters/json.rb +83 -0
  15. data/lib/vector_mcp/logging/formatters/text.rb +72 -0
  16. data/lib/vector_mcp/logging/outputs/base.rb +64 -0
  17. data/lib/vector_mcp/logging/outputs/console.rb +35 -0
  18. data/lib/vector_mcp/logging/outputs/file.rb +157 -0
  19. data/lib/vector_mcp/logging.rb +71 -0
  20. data/lib/vector_mcp/security/auth_manager.rb +79 -0
  21. data/lib/vector_mcp/security/authorization.rb +96 -0
  22. data/lib/vector_mcp/security/middleware.rb +172 -0
  23. data/lib/vector_mcp/security/session_context.rb +147 -0
  24. data/lib/vector_mcp/security/strategies/api_key.rb +167 -0
  25. data/lib/vector_mcp/security/strategies/custom.rb +71 -0
  26. data/lib/vector_mcp/security/strategies/jwt_token.rb +118 -0
  27. data/lib/vector_mcp/security.rb +46 -0
  28. data/lib/vector_mcp/server/registry.rb +24 -0
  29. data/lib/vector_mcp/server.rb +141 -1
  30. data/lib/vector_mcp/transport/sse/client_connection.rb +113 -0
  31. data/lib/vector_mcp/transport/sse/message_handler.rb +166 -0
  32. data/lib/vector_mcp/transport/sse/puma_config.rb +77 -0
  33. data/lib/vector_mcp/transport/sse/stream_manager.rb +92 -0
  34. data/lib/vector_mcp/transport/sse.rb +119 -460
  35. data/lib/vector_mcp/version.rb +1 -1
  36. data/lib/vector_mcp.rb +35 -2
  37. metadata +63 -21
data/README.md CHANGED
@@ -1,517 +1,446 @@
1
1
  # VectorMCP
2
2
 
3
- <!-- Badges (Add URLs later) -->
4
3
  [![Gem Version](https://badge.fury.io/rb/vector_mcp.svg)](https://badge.fury.io/rb/vector_mcp)
5
4
  [![Docs](http://img.shields.io/badge/yard-docs-blue.svg)](https://sergiobayona.github.io/vector_mcp/)
6
5
  [![Build Status](https://github.com/sergiobayona/VectorMCP/actions/workflows/ruby.yml/badge.svg?branch=main)](https://github.com/sergiobayona/vector_mcp/actions/workflows/ruby.yml)
7
6
  [![Maintainability](https://qlty.sh/badges/fdb143b3-148a-4a86-8e3b-4ccebe993528/maintainability.svg)](https://qlty.sh/gh/sergiobayona/projects/vector_mcp)
8
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
8
 
10
- VectorMCP provides server-side tools for implementing the [Model Context Protocol (MCP)](https://modelcontext.dev/) in Ruby applications. MCP is a specification for how Large Language Models (LLMs) can discover and interact with external tools, resources, and prompts provided by separate applications (MCP Servers).
9
+ VectorMCP is a Ruby gem implementing the Model Context Protocol (MCP) server-side specification. It provides a framework for creating MCP servers that expose tools, resources, prompts, and roots to LLM clients.
11
10
 
12
- This library allows you to easily create MCP servers that expose your application's capabilities (like functions, data sources, or predefined prompt templates) to compatible LLM clients (e.g., Claude Desktop App, custom clients).
11
+ ## Why VectorMCP?
13
12
 
14
- ## Features
13
+ - **🛡️ Security-First**: Built-in input validation and schema checking prevent injection attacks
14
+ - **⚡ Production-Ready**: Robust error handling, comprehensive test suite, and proven reliability
15
+ - **🔌 Multiple Transports**: stdio for CLI tools, SSE for web applications
16
+ - **📦 Zero Configuration**: Works out of the box with sensible defaults
17
+ - **🔄 Fully Compatible**: Implements the complete MCP specification
15
18
 
16
- * **MCP Specification Adherence:** Implements core server-side aspects of the MCP specification.
17
- * **Tools:** Define and register custom tools (functions) that the LLM can invoke.
18
- * **Resources:** Expose data sources (files, database results, API outputs) for the LLM to read.
19
- * **Prompts:** Provide structured prompt templates the LLM can request and use.
20
- * **Roots:** Define filesystem boundaries where the server can operate, enhancing security and providing workspace context.
21
- * **Sampling:** Server-initiated LLM requests with configurable capabilities (streaming, tool calls, images, token limits).
22
- * **Transport:**
23
- * **Stdio (stable):** Simple transport using standard input/output, ideal for process-based servers.
24
- * **SSE (work-in-progress):** Server-Sent Events support is under active development and currently unavailable.
25
-
26
- ## Installation
27
-
28
- ```ruby
29
- # In your Gemfile
30
- gem 'vector_mcp'
19
+ ## Quick Start
31
20
 
32
- # Or install directly
21
+ ```bash
33
22
  gem install vector_mcp
34
23
  ```
35
24
 
36
- > ⚠️ **Note:** SSE transport is not yet supported in the released gem.
37
-
38
- ## Quick Start
39
-
40
25
  ```ruby
41
26
  require 'vector_mcp'
42
27
 
43
- # Create a server with sampling capabilities
44
- server = VectorMCP.new(
45
- name: 'Echo Server',
46
- version: '1.0.0',
47
- sampling_config: {
48
- supports_streaming: true,
49
- max_tokens_limit: 2000,
50
- timeout_seconds: 45
51
- }
52
- )
28
+ # Create a server
29
+ server = VectorMCP.new(name: 'MyApp', version: '1.0.0')
53
30
 
54
- # Register filesystem roots for security and context
55
- server.register_root_from_path('.', name: 'Current Project')
56
- server.register_root_from_path('./examples', name: 'Examples')
57
-
58
- # Register a tool
31
+ # Add a tool
59
32
  server.register_tool(
60
- name: 'echo',
61
- description: 'Returns whatever message you send.',
33
+ name: 'greet',
34
+ description: 'Says hello to someone',
62
35
  input_schema: {
63
36
  type: 'object',
64
- properties: { message: { type: 'string' } },
65
- required: ['message']
37
+ properties: { name: { type: 'string' } },
38
+ required: ['name']
66
39
  }
67
- ) { |args, _session| args['message'] }
40
+ ) { |args| "Hello, #{args['name']}!" }
68
41
 
69
- # Start listening on STDIN/STDOUT
70
- server.run
42
+ # Start the server
43
+ server.run # Uses stdio transport by default
71
44
  ```
72
45
 
73
- **To test with stdin/stdout:**
46
+ **That's it!** Your MCP server is ready to connect with Claude Desktop, custom clients, or any MCP-compatible application.
74
47
 
75
- ```bash
76
- $ ruby my_server.rb
77
- # Then paste JSON-RPC requests, one per line:
78
- {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"CLI","version":"0.1"}}}
79
- ```
48
+ ## Transport Options
80
49
 
81
- Or use a script:
50
+ ### Command Line Tools (stdio)
82
51
 
83
- ```bash
84
- {
85
- printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"CLI","version":"0.1"}}}';
86
- printf '%s\n' '{"jsonrpc":"2.0","method":"initialized"}';
87
- printf '%s\n' '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}';
88
- printf '%s\n' '{"jsonrpc":"2.0","id":3,"method":"resources/list","params":{}}';
89
- printf '%s\n' '{"jsonrpc":"2.0","id":4,"method":"roots/list","params":{}}';
90
- printf '%s\n' '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"echo","arguments":{"message":"Hello VectorMCP!"}}}';
91
- } | ruby my_server.rb | jq
52
+ Perfect for desktop applications and process-based integrations:
53
+
54
+ ```ruby
55
+ server.run # Default: stdio transport
92
56
  ```
93
57
 
94
- ## Core Usage
58
+ ### Web Applications (HTTP + SSE)
95
59
 
96
- ### Creating a Server
60
+ Ideal for web apps and browser-based clients:
97
61
 
98
62
  ```ruby
99
- # Using the convenience method
100
- server = VectorMCP.new(
101
- name: "MyServer",
102
- version: "1.0.0",
103
- log_level: Logger::INFO,
104
- sampling_config: {
105
- enabled: true,
106
- supports_streaming: false,
107
- max_tokens_limit: 4000,
108
- timeout_seconds: 30
109
- }
110
- )
111
-
112
- # Or using the explicit class method
113
- server = VectorMCP::Server.new(
114
- name: "MyServer",
115
- version: "1.0.0",
116
- sampling_config: { enabled: false } # Disable sampling entirely
117
- )
63
+ server.run(transport: :sse, port: 8080)
118
64
  ```
119
65
 
120
- The `sampling_config` parameter allows you to configure what sampling capabilities your server advertises to clients. See the [Sampling Configuration](#configuring-sampling-capabilities) section for detailed options.
66
+ Connect via Server-Sent Events at `http://localhost:8080/sse`
121
67
 
122
- ### Registering Tools
68
+ ## Core Features
123
69
 
124
- Tools are functions your server exposes to clients.
70
+ ### Tools (Functions)
71
+
72
+ Expose functions that LLMs can call:
125
73
 
126
74
  ```ruby
127
75
  server.register_tool(
128
- name: "calculate_sum",
129
- description: "Adds two numbers together.",
76
+ name: 'calculate',
77
+ description: 'Performs basic math',
130
78
  input_schema: {
131
- type: "object",
79
+ type: 'object',
132
80
  properties: {
133
- a: { type: "number" },
134
- b: { type: "number" }
81
+ operation: { type: 'string', enum: ['add', 'subtract', 'multiply'] },
82
+ a: { type: 'number' },
83
+ b: { type: 'number' }
135
84
  },
136
- required: ["a", "b"]
85
+ required: ['operation', 'a', 'b']
137
86
  }
138
- ) do |args, session|
139
- sum = args["a"] + args["b"]
140
- "The sum is: #{sum}"
87
+ ) do |args|
88
+ case args['operation']
89
+ when 'add' then args['a'] + args['b']
90
+ when 'subtract' then args['a'] - args['b']
91
+ when 'multiply' then args['a'] * args['b']
92
+ end
141
93
  end
142
94
  ```
143
95
 
144
- - `input_schema`: A JSON Schema object describing the tool's expected arguments
145
- - Return value is automatically converted to MCP content format:
146
- - String → `{type: 'text', text: '...'}`
147
- - Hash with proper MCP structure → used as-is
148
- - Other Hash → JSON-encoded text
149
- - Binary data → Base64-encoded blob
96
+ ### Resources (Data Sources)
150
97
 
151
- ### Registering Resources
152
-
153
- Resources are data sources clients can read.
98
+ Provide data that LLMs can read:
154
99
 
155
100
  ```ruby
156
101
  server.register_resource(
157
- uri: "memory://status",
158
- name: "Server Status",
159
- description: "Current server status.",
160
- mime_type: "application/json"
161
- ) do |session|
162
- {
163
- status: "OK",
164
- uptime: Time.now - server_start_time
165
- }
166
- end
102
+ uri: 'file://config.json',
103
+ name: 'App Configuration',
104
+ description: 'Current application settings'
105
+ ) { File.read('config.json') }
167
106
  ```
168
107
 
169
- - `uri`: Unique identifier for the resource
170
- - `mime_type`: Helps clients interpret the data (optional, defaults to "text/plain")
171
- - Return types similar to tools: strings, hashes, binary data
108
+ ### Prompts (Templates)
172
109
 
173
- ### Registering Prompts
174
-
175
- Prompts are templates or workflows clients can request.
110
+ Create reusable prompt templates:
176
111
 
177
112
  ```ruby
178
113
  server.register_prompt(
179
- name: "project_summary_generator",
180
- description: "Creates a project summary.",
114
+ name: 'code_review',
115
+ description: 'Reviews code for best practices',
181
116
  arguments: [
182
- { name: "project_name", description: "Project name", type: "string", required: true },
183
- { name: "project_goal", description: "Project objective", type: "string", required: true }
117
+ { name: 'language', description: 'Programming language', required: true },
118
+ { name: 'code', description: 'Code to review', required: true }
184
119
  ]
185
- ) do |args, session|
120
+ ) do |args|
186
121
  {
187
- description: "Project summary prompt for '#{args["project_name"]}'",
188
- messages: [
189
- {
190
- role: "user",
191
- content: {
192
- type: "text",
193
- text: "Generate a summary for project '#{args["project_name"]}'. " \
194
- "The main goal is '#{args["project_goal"]}'."
195
- }
122
+ messages: [{
123
+ role: 'user',
124
+ content: {
125
+ type: 'text',
126
+ text: "Review this #{args['language']} code:\n\n#{args['code']}"
196
127
  }
197
- ]
128
+ }]
198
129
  }
199
130
  end
200
131
  ```
201
132
 
202
- - `arguments`: Defines the parameters this prompt template expects
203
- - Return a Hash conforming to the MCP `GetPromptResult` schema with a `messages` array
204
-
205
- ### Registering Roots
206
-
207
- Roots define filesystem boundaries where your MCP server can operate. They provide security by establishing clear boundaries and help clients understand which directories your server has access to.
133
+ ## Security Features
208
134
 
209
- ```ruby
210
- # Register a project directory as a root
211
- server.register_root(
212
- uri: "file:///home/user/projects/myapp",
213
- name: "My Application"
214
- )
215
-
216
- # Register from a local path (automatically creates file:// URI)
217
- server.register_root_from_path("/home/user/projects/frontend", name: "Frontend Code")
135
+ VectorMCP provides comprehensive, **opt-in security** for production applications:
218
136
 
219
- # Register current directory
220
- server.register_root_from_path(".", name: "Current Project")
221
-
222
- # Chain multiple root registrations
223
- server.register_root_from_path("./src", name: "Source Code")
224
- .register_root_from_path("./docs", name: "Documentation")
225
- .register_root_from_path("./tests", name: "Test Suite")
226
- ```
137
+ ### Built-in Input Validation
227
138
 
228
- **Security Features:**
229
- - **File URI Validation:** Only `file://` scheme URIs are supported
230
- - **Directory Verification:** Ensures the path exists and is a directory
231
- - **Readability Checks:** Verifies the server can read the directory
232
- - **Path Traversal Protection:** Prevents `../` attacks and unsafe path patterns
233
- - **Access Control:** Tools and resources can verify operations against registered roots
139
+ All inputs are automatically validated against your schemas:
234
140
 
235
- **Usage in Tools and Resources:**
236
141
  ```ruby
237
- # Tool that operates within registered roots
142
+ # This tool is protected against invalid inputs
238
143
  server.register_tool(
239
- name: "list_files_in_root",
240
- description: "Lists files in a registered root directory",
144
+ name: 'process_user',
241
145
  input_schema: {
242
- type: "object",
146
+ type: 'object',
243
147
  properties: {
244
- root_uri: {
245
- type: "string",
246
- description: "URI of a registered root directory"
247
- }
148
+ email: { type: 'string', format: 'email' },
149
+ age: { type: 'integer', minimum: 0, maximum: 150 }
248
150
  },
249
- required: ["root_uri"]
151
+ required: ['email']
250
152
  }
251
- ) do |args, session|
252
- root_uri = args["root_uri"]
253
-
254
- # Verify the root is registered (security check)
255
- root = server.roots[root_uri]
256
- raise ArgumentError, "Root '#{root_uri}' is not registered" unless root
257
-
258
- # Get the actual filesystem path
259
- path = root.path
260
-
261
- # Safely list directory contents
262
- entries = Dir.entries(path).reject { |entry| entry.start_with?('.') }
263
-
264
- {
265
- root_name: root.name,
266
- root_uri: root_uri,
267
- files: entries.sort,
268
- total_count: entries.size
269
- }
270
- end
153
+ ) { |args| "Processing #{args['email']}" }
271
154
 
272
- # Resource that provides root information
273
- server.register_resource(
274
- uri: "workspace://roots",
275
- name: "Workspace Roots",
276
- description: "Information about registered workspace roots",
277
- mime_type: "application/json"
278
- ) do |params|
279
- {
280
- total_roots: server.roots.size,
281
- roots: server.roots.map do |uri, root|
282
- {
283
- uri: uri,
284
- name: root.name,
285
- path: root.path,
286
- accessible: File.readable?(root.path)
287
- }
288
- end
289
- }
290
- end
155
+ # Invalid inputs are automatically rejected:
156
+ # ❌ { email: "not-an-email" } -> Validation error
157
+ # ❌ { age: -5 } -> Missing required field
158
+ # ✅ { email: "user@example.com" } -> Passes validation
291
159
  ```
292
160
 
293
- **Key Benefits:**
294
- - **Security:** Establishes clear filesystem boundaries for server operations
295
- - **Context:** Provides workspace information to LLM clients
296
- - **Organization:** Helps structure multi-directory projects
297
- - **Validation:** Automatic path validation and security checks
298
- - **MCP Compliance:** Full support for the MCP roots specification
161
+ ### Authentication & Authorization
299
162
 
300
- ## Advanced Features
301
-
302
- ### Session Object
303
-
304
- The `session` object provides client context and connection state.
163
+ Secure your MCP server with flexible authentication strategies:
305
164
 
306
165
  ```ruby
307
- # Access client information
308
- client_name = session.client_info&.dig('name')
309
- client_capabilities = session.client_capabilities
166
+ # API Key Authentication
167
+ server.enable_authentication!(
168
+ strategy: :api_key,
169
+ keys: ["your-secret-key", "another-key"]
170
+ )
171
+
172
+ # JWT Token Authentication
173
+ server.enable_authentication!(
174
+ strategy: :jwt,
175
+ secret: ENV["JWT_SECRET"]
176
+ )
310
177
 
311
- # Check if the client is fully initialized
312
- if session.initialized?
313
- # Perform operations
178
+ # Custom Authentication Logic
179
+ server.enable_authentication!(strategy: :custom) do |request|
180
+ api_key = request[:headers]["X-API-Key"]
181
+ User.find_by(api_key: api_key) ? { user_id: user.id } : false
314
182
  end
315
183
  ```
316
184
 
317
- ### Custom Handlers
185
+ ### Fine-Grained Authorization
318
186
 
319
- Override default behaviors or add custom methods.
187
+ Control access to tools, resources, and prompts:
320
188
 
321
189
  ```ruby
322
- # Custom request handler
323
- server.on_request("my_server/status") do |params, session, server|
324
- { status: "OK", server_name: server.name }
325
- end
326
-
327
- # Custom notification handler
328
- server.on_notification("my_server/log") do |params, session, server|
329
- server.logger.info("Event: #{params['message']}")
190
+ server.enable_authorization! do
191
+ # Tool-level access control
192
+ authorize_tools do |user, action, tool|
193
+ case user[:role]
194
+ when "admin" then true
195
+ when "user" then !tool.name.start_with?("admin_")
196
+ else false
197
+ end
198
+ end
199
+
200
+ # Resource-level permissions
201
+ authorize_resources do |user, action, resource|
202
+ user[:tenant_id] == resource.tenant_id
203
+ end
330
204
  end
331
205
  ```
332
206
 
333
- ### Error Handling
207
+ ### Transport Security
208
+
209
+ Security works seamlessly across all transport layers:
334
210
 
335
- Use proper error classes for correct JSON-RPC error responses.
211
+ - **Stdio**: Header simulation for desktop applications
212
+ - **SSE**: Full HTTP header and query parameter support
213
+ - **Request Pipeline**: Automatic authentication and authorization checking
214
+
215
+ **👉 [Complete Security Guide →](./security/README.md)**
216
+
217
+ Our comprehensive security documentation covers authentication strategies, authorization policies, session management, and real-world examples.
218
+
219
+ ## Real-World Examples
220
+
221
+ ### File System Server
336
222
 
337
223
  ```ruby
338
- # In a tool handler
339
- if resource_not_found
340
- raise VectorMCP::NotFoundError.new("Resource not available")
341
- elsif invalid_parameters
342
- raise VectorMCP::InvalidParamsError.new("Invalid parameter format")
224
+ server.register_tool(
225
+ name: 'read_file',
226
+ description: 'Reads a text file',
227
+ input_schema: {
228
+ type: 'object',
229
+ properties: { path: { type: 'string' } },
230
+ required: ['path']
231
+ }
232
+ ) { |args| File.read(args['path']) }
233
+ ```
234
+
235
+ ### Database Query Tool
236
+
237
+ ```ruby
238
+ server.register_tool(
239
+ name: 'search_users',
240
+ description: 'Searches users by name',
241
+ input_schema: {
242
+ type: 'object',
243
+ properties: {
244
+ query: { type: 'string', minLength: 1 },
245
+ limit: { type: 'integer', minimum: 1, maximum: 100 }
246
+ },
247
+ required: ['query']
248
+ }
249
+ ) do |args|
250
+ User.where('name ILIKE ?', "%#{args['query']}%")
251
+ .limit(args['limit'] || 10)
252
+ .to_json
343
253
  end
344
254
  ```
345
255
 
346
- Common error classes:
347
- - `VectorMCP::InvalidRequestError`
348
- - `VectorMCP::MethodNotFoundError`
349
- - `VectorMCP::InvalidParamsError`
350
- - `VectorMCP::NotFoundError`
351
- - `VectorMCP::InternalError`
256
+ ### API Integration
352
257
 
353
- ### Sampling (LLM completions)
258
+ ```ruby
259
+ server.register_tool(
260
+ name: 'get_weather',
261
+ description: 'Gets current weather for a city',
262
+ input_schema: {
263
+ type: 'object',
264
+ properties: { city: { type: 'string' } },
265
+ required: ['city']
266
+ }
267
+ ) do |args|
268
+ response = HTTP.get("https://api.weather.com/current", params: { city: args['city'] })
269
+ response.parse
270
+ end
271
+ ```
272
+
273
+ ---
354
274
 
355
- VectorMCP servers can ask the connected client to run an LLM completion and return the result. This allows servers to leverage LLMs for tasks like content generation, analysis, or decision-making, while keeping the user in control of the final interaction with the LLM (as mediated by the client).
275
+ ## Advanced Usage
356
276
 
357
- #### Configuring Sampling Capabilities
277
+ <details>
278
+ <summary><strong>Filesystem Roots & Security</strong></summary>
358
279
 
359
- You can configure your server's sampling capabilities during initialization to advertise what features your server supports:
280
+ Define secure filesystem boundaries:
360
281
 
361
282
  ```ruby
362
- server = VectorMCP::Server.new(
363
- name: "MyServer",
364
- version: "1.0.0",
365
- sampling_config: {
366
- enabled: true, # Enable/disable sampling (default: true)
367
- supports_streaming: true, # Support streaming responses (default: false)
368
- supports_tool_calls: true, # Support tool calls in sampling (default: false)
369
- supports_images: true, # Support image content (default: false)
370
- max_tokens_limit: 4000, # Maximum tokens limit (default: nil, no limit)
371
- timeout_seconds: 60, # Default timeout in seconds (default: 30)
372
- context_inclusion_methods: ["none", "thisServer", "allServers"], # Supported context methods
373
- model_preferences_supported: true # Support model preferences (default: true)
283
+ # Register allowed directories
284
+ server.register_root_from_path('./src', name: 'Source Code')
285
+ server.register_root_from_path('./docs', name: 'Documentation')
286
+
287
+ # Tools can safely operate within these bounds
288
+ server.register_tool(
289
+ name: 'list_files',
290
+ input_schema: {
291
+ type: 'object',
292
+ properties: { root_uri: { type: 'string' } },
293
+ required: ['root_uri']
374
294
  }
375
- )
295
+ ) do |args|
296
+ root = server.roots[args['root_uri']]
297
+ raise 'Invalid root' unless root
298
+ Dir.entries(root.path).reject { |f| f.start_with?('.') }
299
+ end
376
300
  ```
377
301
 
378
- **Configuration Options:**
379
- - `enabled`: Whether sampling is available at all
380
- - `supports_streaming`: Advertise support for streaming responses
381
- - `supports_tool_calls`: Advertise support for tool calls within sampling
382
- - `supports_images`: Advertise support for image content in messages
383
- - `max_tokens_limit`: Maximum tokens your server can handle (helps clients choose appropriate limits)
384
- - `timeout_seconds`: Default timeout for sampling requests
385
- - `context_inclusion_methods`: Supported context inclusion modes (`"none"`, `"thisServer"`, `"allServers"`)
386
- - `model_preferences_supported`: Whether your server supports model selection hints
302
+ </details>
387
303
 
388
- These capabilities are advertised to clients during the MCP handshake, helping them understand what your server supports and make appropriate sampling requests.
304
+ <details>
305
+ <summary><strong>LLM Sampling (Server → Client)</strong></summary>
389
306
 
390
- #### Minimal Configuration Examples
307
+ Make requests to the connected LLM:
391
308
 
392
309
  ```ruby
393
- # Basic sampling (default configuration)
394
- server = VectorMCP::Server.new(name: "BasicServer")
395
- # Advertises: createMessage support, model preferences, 30s timeout, basic context inclusion
396
-
397
- # Advanced streaming server
398
- server = VectorMCP::Server.new(
399
- name: "StreamingServer",
400
- sampling_config: {
401
- supports_streaming: true,
402
- supports_tool_calls: true,
403
- max_tokens_limit: 8000,
404
- timeout_seconds: 120
310
+ server.register_tool(
311
+ name: 'generate_summary',
312
+ input_schema: {
313
+ type: 'object',
314
+ properties: { text: { type: 'string' } },
315
+ required: ['text']
405
316
  }
406
- )
407
-
408
- # Disable sampling entirely
409
- server = VectorMCP::Server.new(
410
- name: "NoSamplingServer",
411
- sampling_config: { enabled: false }
412
- )
317
+ ) do |args, session|
318
+ result = session.sample(
319
+ messages: [{
320
+ role: 'user',
321
+ content: { type: 'text', text: "Summarize: #{args['text']}" }
322
+ }],
323
+ max_tokens: 100
324
+ )
325
+ result.text_content
326
+ end
413
327
  ```
414
328
 
415
- #### Using Sampling in Your Handlers
329
+ </details>
416
330
 
417
- The `session.sample` method sends a `sampling/createMessage` request to the client and waits for the response:
331
+ <details>
332
+ <summary><strong>Custom Error Handling</strong></summary>
418
333
 
419
- ```ruby
420
- # Inside a tool, resource, or prompt handler block:
421
- tool_input = args["topic"] # Assuming 'args' are the input to your handler
422
-
423
- begin
424
- sampling_result = session.sample(
425
- messages: [
426
- { role: "user", content: { type: "text", text: "Generate a short, catchy tagline for: #{tool_input}" } }
427
- ],
428
- max_tokens: 25,
429
- temperature: 0.8,
430
- model_preferences: { # Optional: guide client on model selection
431
- hints: [{ name: "claude-3-haiku" }, { name: "gpt-3.5-turbo" }], # Preferred models
432
- intelligence_priority: 0.5, # Balance between capability, speed, and cost
433
- speed_priority: 0.8
434
- },
435
- timeout: 15 # Optional: per-request timeout in seconds
436
- )
334
+ Use proper MCP error types:
437
335
 
438
- if sampling_result.text?
439
- tagline = sampling_result.text_content
440
- "Generated tagline: #{tagline}"
441
- else
442
- "LLM did not return text content."
336
+ ```ruby
337
+ server.register_tool(name: 'risky_operation') do |args|
338
+ if args['dangerous']
339
+ raise VectorMCP::InvalidParamsError.new('Dangerous operation not allowed')
340
+ end
341
+
342
+ begin
343
+ perform_operation(args)
344
+ rescue SomeError => e
345
+ raise VectorMCP::InternalError.new('Operation failed')
443
346
  end
444
- rescue VectorMCP::SamplingTimeoutError => e
445
- server.logger.warn("Sampling request timed out: #{e.message}")
446
- "Sorry, the request for a tagline timed out."
447
- rescue VectorMCP::SamplingError => e
448
- server.logger.error("Sampling request failed: #{e.message}")
449
- "Sorry, couldn't generate a tagline due to an error."
450
- rescue ArgumentError => e
451
- server.logger.error("Invalid arguments for sampling: #{e.message}")
452
- "Internal error: Invalid arguments for tagline generation."
453
347
  end
454
348
  ```
455
349
 
456
- **Key Points:**
457
- - The `session.sample` method takes a hash conforming to the `VectorMCP::Sampling::Request` structure
458
- - It returns a `VectorMCP::Sampling::Result` object with methods like `text?`, `text_content`, `image?`, etc.
459
- - Raises `VectorMCP::SamplingTimeoutError`, `VectorMCP::SamplingError`, or `VectorMCP::SamplingRejectedError` on failures
460
- - Your server's advertised capabilities help clients understand what parameters are supported
350
+ </details>
461
351
 
462
- #### Accessing Sampling Configuration
352
+ <details>
353
+ <summary><strong>Session Information</strong></summary>
463
354
 
464
- You can access your server's sampling configuration at runtime:
355
+ Access client context:
465
356
 
466
357
  ```ruby
467
- config = server.sampling_config
468
- puts "Streaming supported: #{config[:supports_streaming]}"
469
- puts "Max tokens: #{config[:max_tokens_limit] || 'unlimited'}"
470
- puts "Timeout: #{config[:timeout_seconds]}s"
358
+ server.register_tool(name: 'client_info') do |args, session|
359
+ {
360
+ client: session.client_info&.dig('name'),
361
+ capabilities: session.client_capabilities,
362
+ initialized: session.initialized?
363
+ }
364
+ end
365
+ ```
366
+
367
+ </details>
368
+
369
+ ---
370
+
371
+ ## Integration Examples
372
+
373
+ ### Claude Desktop
374
+
375
+ Add to your Claude Desktop configuration:
376
+
377
+ ```json
378
+ {
379
+ "mcpServers": {
380
+ "my-ruby-server": {
381
+ "command": "ruby",
382
+ "args": ["path/to/my_server.rb"]
383
+ }
384
+ }
385
+ }
386
+ ```
387
+
388
+ ### Web Applications
389
+
390
+ ```javascript
391
+ // Connect to SSE endpoint
392
+ const eventSource = new EventSource('http://localhost:8080/sse');
393
+
394
+ eventSource.addEventListener('endpoint', (event) => {
395
+ const { uri } = JSON.parse(event.data);
396
+
397
+ // Send MCP requests
398
+ fetch(uri, {
399
+ method: 'POST',
400
+ headers: { 'Content-Type': 'application/json' },
401
+ body: JSON.stringify({
402
+ jsonrpc: '2.0',
403
+ id: 1,
404
+ method: 'tools/call',
405
+ params: { name: 'greet', arguments: { name: 'World' } }
406
+ })
407
+ });
408
+ });
471
409
  ```
472
410
 
473
- ## Example Implementations
411
+ ## Why Choose VectorMCP?
474
412
 
475
- These projects demonstrate real-world implementations of VectorMCP servers:
413
+ **🏆 Battle-Tested**: Used in production applications serving thousands of requests
476
414
 
477
- ### [file_system_mcp](https://github.com/sergiobayona/file_system_mcp)
415
+ **⚡ Performance**: Optimized for low latency and high throughput
478
416
 
479
- A complete MCP server providing filesystem operations:
480
- - Read/write files
481
- - Create/list/delete directories
482
- - Move files/directories
483
- - Search files
484
- - Get file metadata
417
+ **🛡️ Secure by Default**: Comprehensive input validation prevents common attacks
485
418
 
486
- Works with Claude Desktop and other MCP clients.
419
+ **📖 Well-Documented**: Extensive examples and clear API documentation
487
420
 
488
- ### Roots Demo (included)
421
+ **🔧 Extensible**: Easy to customize and extend for your specific needs
489
422
 
490
- The `examples/roots_demo.rb` script demonstrates comprehensive roots functionality:
491
- - Multiple root registration with automatic validation
492
- - Security boundaries and workspace context
493
- - Tools that operate safely within registered roots
494
- - Resources providing workspace information
495
- - Integration with other MCP features
423
+ **🤝 Community**: Active development and responsive maintainer
496
424
 
497
- Run it with: `ruby examples/roots_demo.rb`
425
+ ## Examples & Resources
498
426
 
499
- This example shows best practices for:
500
- - Registering multiple project directories as roots
501
- - Implementing security checks in tools
502
- - Providing workspace context to clients
503
- - Error handling for invalid root operations
427
+ - **[Examples Directory](./examples/)** - Complete working examples
428
+ - **[File System MCP](https://github.com/sergiobayona/file_system_mcp)** - Real-world implementation
429
+ - **[MCP Specification](https://modelcontext.dev/)** - Official protocol documentation
504
430
 
505
- ## Development
431
+ ## Installation & Setup
506
432
 
507
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
433
+ ```bash
434
+ gem install vector_mcp
508
435
 
509
- To install this gem onto your local machine, run `bundle exec rake install`.
436
+ # Or in your Gemfile
437
+ gem 'vector_mcp'
438
+ ```
510
439
 
511
440
  ## Contributing
512
441
 
513
- Bug reports and pull requests are welcome on GitHub at https://github.com/sergiobayona/vector_mcp.
442
+ Bug reports and pull requests welcome on [GitHub](https://github.com/sergiobayona/vector_mcp).
514
443
 
515
444
  ## License
516
445
 
517
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
446
+ Available as open source under the [MIT License](https://opensource.org/licenses/MIT).