vector_mcp 0.3.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.
data/README.md CHANGED
@@ -1,655 +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 (stable):** Server-Sent Events over HTTP, enabling web-based MCP clients and browser integration.
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
-
37
- ## Quick Start
38
-
39
25
  ```ruby
40
26
  require 'vector_mcp'
41
27
 
42
- # Create a server with sampling capabilities
43
- server = VectorMCP.new(
44
- name: 'Echo Server',
45
- version: '1.0.0',
46
- sampling_config: {
47
- supports_streaming: true,
48
- max_tokens_limit: 2000,
49
- timeout_seconds: 45
50
- }
51
- )
52
-
53
- # Register filesystem roots for security and context
54
- server.register_root_from_path('.', name: 'Current Project')
55
- server.register_root_from_path('./examples', name: 'Examples')
28
+ # Create a server
29
+ server = VectorMCP.new(name: 'MyApp', version: '1.0.0')
56
30
 
57
- # Register a tool
31
+ # Add a tool
58
32
  server.register_tool(
59
- name: 'echo',
60
- description: 'Returns whatever message you send.',
33
+ name: 'greet',
34
+ description: 'Says hello to someone',
61
35
  input_schema: {
62
36
  type: 'object',
63
- properties: { message: { type: 'string' } },
64
- required: ['message']
37
+ properties: { name: { type: 'string' } },
38
+ required: ['name']
65
39
  }
66
- ) { |args, _session| args['message'] }
40
+ ) { |args| "Hello, #{args['name']}!" }
67
41
 
68
- # Start listening on STDIN/STDOUT
69
- server.run
42
+ # Start the server
43
+ server.run # Uses stdio transport by default
70
44
  ```
71
45
 
72
- **To test with stdin/stdout:**
73
-
74
- ```bash
75
- $ ruby my_server.rb
76
- # Then paste JSON-RPC requests, one per line:
77
- {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"CLI","version":"0.1"}}}
78
- ```
46
+ **That's it!** Your MCP server is ready to connect with Claude Desktop, custom clients, or any MCP-compatible application.
79
47
 
80
- Or use a script:
48
+ ## Transport Options
81
49
 
82
- ```bash
83
- {
84
- printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"CLI","version":"0.1"}}}';
85
- printf '%s\n' '{"jsonrpc":"2.0","method":"initialized"}';
86
- printf '%s\n' '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}';
87
- printf '%s\n' '{"jsonrpc":"2.0","id":3,"method":"resources/list","params":{}}';
88
- printf '%s\n' '{"jsonrpc":"2.0","id":4,"method":"roots/list","params":{}}';
89
- printf '%s\n' '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"echo","arguments":{"message":"Hello VectorMCP!"}}}';
90
- } | ruby my_server.rb | jq
91
- ```
50
+ ### Command Line Tools (stdio)
92
51
 
93
- ### HTTP/SSE Transport
94
-
95
- For web-based clients and browser integration, use the SSE transport:
52
+ Perfect for desktop applications and process-based integrations:
96
53
 
97
54
  ```ruby
98
- require 'vector_mcp'
55
+ server.run # Default: stdio transport
56
+ ```
99
57
 
100
- server = VectorMCP.new(
101
- name: 'My HTTP Server',
102
- version: '1.0.0'
103
- )
58
+ ### Web Applications (HTTP + SSE)
104
59
 
105
- # Register tools, resources, prompts...
106
- server.register_tool(
107
- name: 'echo',
108
- description: 'Returns the input message',
109
- input_schema: {
110
- type: 'object',
111
- properties: { message: { type: 'string' } },
112
- required: ['message']
113
- }
114
- ) { |args| args['message'] }
60
+ Ideal for web apps and browser-based clients:
115
61
 
116
- # Start HTTP server with SSE transport
117
- server.run(transport: :sse, options: { port: 8080, host: 'localhost' })
62
+ ```ruby
63
+ server.run(transport: :sse, port: 8080)
118
64
  ```
119
65
 
120
- The server provides two HTTP endpoints:
66
+ Connect via Server-Sent Events at `http://localhost:8080/sse`
121
67
 
122
- * **SSE Stream:** `GET /sse` - Establishes server-sent events connection
123
- * **Messages:** `POST /message?session_id=<id>` - Sends JSON-RPC requests
68
+ ## Core Features
124
69
 
125
- **Client Integration:**
70
+ ### Tools (Functions)
126
71
 
127
- ```javascript
128
- // Connect to SSE stream
129
- const eventSource = new EventSource('http://localhost:8080/sse');
130
-
131
- eventSource.addEventListener('endpoint', (event) => {
132
- const data = JSON.parse(event.data);
133
- const messageUrl = data.uri; // POST endpoint for this session
134
-
135
- // Send MCP initialization
136
- fetch(messageUrl, {
137
- method: 'POST',
138
- headers: { 'Content-Type': 'application/json' },
139
- body: JSON.stringify({
140
- jsonrpc: '2.0',
141
- id: 1,
142
- method: 'initialize',
143
- params: {
144
- protocolVersion: '2024-11-05',
145
- capabilities: {},
146
- clientInfo: { name: 'WebClient', version: '1.0' }
147
- }
148
- })
149
- });
150
- });
72
+ Expose functions that LLMs can call:
151
73
 
152
- eventSource.addEventListener('message', (event) => {
153
- const response = JSON.parse(event.data);
154
- console.log('MCP Response:', response);
155
- });
74
+ ```ruby
75
+ server.register_tool(
76
+ name: 'calculate',
77
+ description: 'Performs basic math',
78
+ input_schema: {
79
+ type: 'object',
80
+ properties: {
81
+ operation: { type: 'string', enum: ['add', 'subtract', 'multiply'] },
82
+ a: { type: 'number' },
83
+ b: { type: 'number' }
84
+ },
85
+ required: ['operation', 'a', 'b']
86
+ }
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
93
+ end
156
94
  ```
157
95
 
158
- ## Core Usage
96
+ ### Resources (Data Sources)
159
97
 
160
- ### Creating a Server
98
+ Provide data that LLMs can read:
161
99
 
162
100
  ```ruby
163
- # Using the convenience method
164
- server = VectorMCP.new(
165
- name: "MyServer",
166
- version: "1.0.0",
167
- log_level: Logger::INFO,
168
- sampling_config: {
169
- enabled: true,
170
- supports_streaming: false,
171
- max_tokens_limit: 4000,
172
- timeout_seconds: 30
173
- }
174
- )
175
-
176
- # Or using the explicit class method
177
- server = VectorMCP::Server.new(
178
- name: "MyServer",
179
- version: "1.0.0",
180
- sampling_config: { enabled: false } # Disable sampling entirely
181
- )
101
+ server.register_resource(
102
+ uri: 'file://config.json',
103
+ name: 'App Configuration',
104
+ description: 'Current application settings'
105
+ ) { File.read('config.json') }
182
106
  ```
183
107
 
184
- 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.
108
+ ### Prompts (Templates)
185
109
 
186
- ### Registering Tools
187
-
188
- Tools are functions your server exposes to clients.
110
+ Create reusable prompt templates:
189
111
 
190
112
  ```ruby
191
- server.register_tool(
192
- name: "calculate_sum",
193
- description: "Adds two numbers together.",
194
- input_schema: {
195
- type: "object",
196
- properties: {
197
- a: { type: "number" },
198
- b: { type: "number" }
199
- },
200
- required: ["a", "b"]
113
+ server.register_prompt(
114
+ name: 'code_review',
115
+ description: 'Reviews code for best practices',
116
+ arguments: [
117
+ { name: 'language', description: 'Programming language', required: true },
118
+ { name: 'code', description: 'Code to review', required: true }
119
+ ]
120
+ ) do |args|
121
+ {
122
+ messages: [{
123
+ role: 'user',
124
+ content: {
125
+ type: 'text',
126
+ text: "Review this #{args['language']} code:\n\n#{args['code']}"
127
+ }
128
+ }]
201
129
  }
202
- ) do |args, session|
203
- sum = args["a"] + args["b"]
204
- "The sum is: #{sum}"
205
130
  end
206
131
  ```
207
132
 
208
- #### Automatic Input Validation
133
+ ## Security Features
209
134
 
210
- VectorMCP automatically validates all tool arguments against their defined `input_schema` before executing the tool handler. This provides several security and reliability benefits:
135
+ VectorMCP provides comprehensive, **opt-in security** for production applications:
211
136
 
212
- - **Type Safety**: Ensures arguments match expected types (string, number, boolean, etc.)
213
- - **Required Fields**: Validates that all required parameters are present
214
- - **Format Validation**: Supports JSON Schema constraints like patterns, enums, and ranges
215
- - **Security**: Prevents injection attacks and malformed input from reaching your tool logic
216
- - **Better Error Messages**: Provides clear validation errors to help clients fix their requests
137
+ ### Built-in Input Validation
138
+
139
+ All inputs are automatically validated against your schemas:
217
140
 
218
141
  ```ruby
219
- # Example with comprehensive validation
142
+ # This tool is protected against invalid inputs
220
143
  server.register_tool(
221
- name: "process_user_data",
222
- description: "Processes user information with strict validation",
144
+ name: 'process_user',
223
145
  input_schema: {
224
- type: "object",
146
+ type: 'object',
225
147
  properties: {
226
- name: {
227
- type: "string",
228
- minLength: 1,
229
- maxLength: 100,
230
- pattern: "^[a-zA-Z\\s]+$"
231
- },
232
- age: {
233
- type: "integer",
234
- minimum: 0,
235
- maximum: 150
236
- },
237
- email: {
238
- type: "string",
239
- format: "email"
240
- },
241
- role: {
242
- type: "string",
243
- enum: ["admin", "user", "guest"]
244
- },
245
- preferences: {
246
- type: "object",
247
- properties: {
248
- theme: { type: "string" },
249
- notifications: { type: "boolean" }
250
- },
251
- additionalProperties: false
252
- }
148
+ email: { type: 'string', format: 'email' },
149
+ age: { type: 'integer', minimum: 0, maximum: 150 }
253
150
  },
254
- required: ["name", "email", "role"],
255
- additionalProperties: false
151
+ required: ['email']
256
152
  }
257
- ) do |args, session|
258
- # At this point, you can trust that:
259
- # - All required fields are present
260
- # - Data types are correct
261
- # - String lengths and number ranges are valid
262
- # - Email format is valid
263
- # - Role is one of the allowed values
264
-
265
- "Processing user: #{args['name']} (#{args['role']})"
266
- end
267
- ```
268
-
269
- **What happens with invalid input:**
270
- - VectorMCP returns a JSON-RPC error response with code `-32602` (Invalid params)
271
- - The error message includes specific details about what validation failed
272
- - Your tool handler is never called with invalid data
273
- - Clients receive clear feedback on how to fix their requests
153
+ ) { |args| "Processing #{args['email']}" }
274
154
 
275
- **Backward Compatibility:**
276
- - Tools without `input_schema` continue to work normally
277
- - No validation is performed if `input_schema` is not provided
278
- - Existing tools are unaffected by this security enhancement
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
159
+ ```
279
160
 
280
- #### Tool Registration Options
161
+ ### Authentication & Authorization
281
162
 
282
- - `input_schema`: A JSON Schema object describing the tool's expected arguments (recommended for security)
283
- - Return value is automatically converted to MCP content format:
284
- - String → `{type: 'text', text: '...'}`
285
- - Hash with proper MCP structure → used as-is
286
- - Other Hash → JSON-encoded text
287
- - Binary data → Base64-encoded blob
163
+ Secure your MCP server with flexible authentication strategies:
288
164
 
289
- ### Registering Resources
165
+ ```ruby
166
+ # API Key Authentication
167
+ server.enable_authentication!(
168
+ strategy: :api_key,
169
+ keys: ["your-secret-key", "another-key"]
170
+ )
290
171
 
291
- Resources are data sources clients can read.
172
+ # JWT Token Authentication
173
+ server.enable_authentication!(
174
+ strategy: :jwt,
175
+ secret: ENV["JWT_SECRET"]
176
+ )
292
177
 
293
- ```ruby
294
- server.register_resource(
295
- uri: "memory://status",
296
- name: "Server Status",
297
- description: "Current server status.",
298
- mime_type: "application/json"
299
- ) do |session|
300
- {
301
- status: "OK",
302
- uptime: Time.now - server_start_time
303
- }
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
304
182
  end
305
183
  ```
306
184
 
307
- - `uri`: Unique identifier for the resource
308
- - `mime_type`: Helps clients interpret the data (optional, defaults to "text/plain")
309
- - Return types similar to tools: strings, hashes, binary data
310
-
311
- ### Registering Prompts
185
+ ### Fine-Grained Authorization
312
186
 
313
- Prompts are templates or workflows clients can request.
187
+ Control access to tools, resources, and prompts:
314
188
 
315
189
  ```ruby
316
- server.register_prompt(
317
- name: "project_summary_generator",
318
- description: "Creates a project summary.",
319
- arguments: [
320
- { name: "project_name", description: "Project name", type: "string", required: true },
321
- { name: "project_goal", description: "Project objective", type: "string", required: true }
322
- ]
323
- ) do |args, session|
324
- {
325
- description: "Project summary prompt for '#{args["project_name"]}'",
326
- messages: [
327
- {
328
- role: "user",
329
- content: {
330
- type: "text",
331
- text: "Generate a summary for project '#{args["project_name"]}'. " \
332
- "The main goal is '#{args["project_goal"]}'."
333
- }
334
- }
335
- ]
336
- }
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
337
204
  end
338
205
  ```
339
206
 
340
- - `arguments`: Defines the parameters this prompt template expects
341
- - Return a Hash conforming to the MCP `GetPromptResult` schema with a `messages` array
207
+ ### Transport Security
342
208
 
343
- ### Registering Roots
209
+ Security works seamlessly across all transport layers:
344
210
 
345
- 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.
211
+ - **Stdio**: Header simulation for desktop applications
212
+ - **SSE**: Full HTTP header and query parameter support
213
+ - **Request Pipeline**: Automatic authentication and authorization checking
346
214
 
347
- ```ruby
348
- # Register a project directory as a root
349
- server.register_root(
350
- uri: "file:///home/user/projects/myapp",
351
- name: "My Application"
352
- )
215
+ **👉 [Complete Security Guide →](./security/README.md)**
216
+
217
+ Our comprehensive security documentation covers authentication strategies, authorization policies, session management, and real-world examples.
353
218
 
354
- # Register from a local path (automatically creates file:// URI)
355
- server.register_root_from_path("/home/user/projects/frontend", name: "Frontend Code")
219
+ ## Real-World Examples
356
220
 
357
- # Register current directory
358
- server.register_root_from_path(".", name: "Current Project")
221
+ ### File System Server
359
222
 
360
- # Chain multiple root registrations
361
- server.register_root_from_path("./src", name: "Source Code")
362
- .register_root_from_path("./docs", name: "Documentation")
363
- .register_root_from_path("./tests", name: "Test Suite")
223
+ ```ruby
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']) }
364
233
  ```
365
234
 
366
- **Security Features:**
367
- - **File URI Validation:** Only `file://` scheme URIs are supported
368
- - **Directory Verification:** Ensures the path exists and is a directory
369
- - **Readability Checks:** Verifies the server can read the directory
370
- - **Path Traversal Protection:** Prevents `../` attacks and unsafe path patterns
371
- - **Access Control:** Tools and resources can verify operations against registered roots
235
+ ### Database Query Tool
372
236
 
373
- **Usage in Tools and Resources:**
374
237
  ```ruby
375
- # Tool that operates within registered roots
376
238
  server.register_tool(
377
- name: "list_files_in_root",
378
- description: "Lists files in a registered root directory",
239
+ name: 'search_users',
240
+ description: 'Searches users by name',
379
241
  input_schema: {
380
- type: "object",
381
- properties: {
382
- root_uri: {
383
- type: "string",
384
- description: "URI of a registered root directory"
385
- }
242
+ type: 'object',
243
+ properties: {
244
+ query: { type: 'string', minLength: 1 },
245
+ limit: { type: 'integer', minimum: 1, maximum: 100 }
386
246
  },
387
- required: ["root_uri"]
388
- }
389
- ) do |args, session|
390
- root_uri = args["root_uri"]
391
-
392
- # Verify the root is registered (security check)
393
- root = server.roots[root_uri]
394
- raise ArgumentError, "Root '#{root_uri}' is not registered" unless root
395
-
396
- # Get the actual filesystem path
397
- path = root.path
398
-
399
- # Safely list directory contents
400
- entries = Dir.entries(path).reject { |entry| entry.start_with?('.') }
401
-
402
- {
403
- root_name: root.name,
404
- root_uri: root_uri,
405
- files: entries.sort,
406
- total_count: entries.size
247
+ required: ['query']
407
248
  }
249
+ ) do |args|
250
+ User.where('name ILIKE ?', "%#{args['query']}%")
251
+ .limit(args['limit'] || 10)
252
+ .to_json
408
253
  end
254
+ ```
409
255
 
410
- # Resource that provides root information
411
- server.register_resource(
412
- uri: "workspace://roots",
413
- name: "Workspace Roots",
414
- description: "Information about registered workspace roots",
415
- mime_type: "application/json"
416
- ) do |params|
417
- {
418
- total_roots: server.roots.size,
419
- roots: server.roots.map do |uri, root|
420
- {
421
- uri: uri,
422
- name: root.name,
423
- path: root.path,
424
- accessible: File.readable?(root.path)
425
- }
426
- end
256
+ ### API Integration
257
+
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']
427
266
  }
267
+ ) do |args|
268
+ response = HTTP.get("https://api.weather.com/current", params: { city: args['city'] })
269
+ response.parse
428
270
  end
429
271
  ```
430
272
 
431
- **Key Benefits:**
432
- - **Security:** Establishes clear filesystem boundaries for server operations
433
- - **Context:** Provides workspace information to LLM clients
434
- - **Organization:** Helps structure multi-directory projects
435
- - **Validation:** Automatic path validation and security checks
436
- - **MCP Compliance:** Full support for the MCP roots specification
273
+ ---
437
274
 
438
- ## Advanced Features
275
+ ## Advanced Usage
439
276
 
440
- ### Session Object
277
+ <details>
278
+ <summary><strong>Filesystem Roots & Security</strong></summary>
441
279
 
442
- The `session` object provides client context and connection state.
280
+ Define secure filesystem boundaries:
443
281
 
444
282
  ```ruby
445
- # Access client information
446
- client_name = session.client_info&.dig('name')
447
- client_capabilities = session.client_capabilities
283
+ # Register allowed directories
284
+ server.register_root_from_path('./src', name: 'Source Code')
285
+ server.register_root_from_path('./docs', name: 'Documentation')
448
286
 
449
- # Check if the client is fully initialized
450
- if session.initialized?
451
- # Perform operations
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']
294
+ }
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?('.') }
452
299
  end
453
300
  ```
454
301
 
455
- ### Custom Handlers
302
+ </details>
456
303
 
457
- Override default behaviors or add custom methods.
304
+ <details>
305
+ <summary><strong>LLM Sampling (Server → Client)</strong></summary>
458
306
 
459
- ```ruby
460
- # Custom request handler
461
- server.on_request("my_server/status") do |params, session, server|
462
- { status: "OK", server_name: server.name }
463
- end
307
+ Make requests to the connected LLM:
464
308
 
465
- # Custom notification handler
466
- server.on_notification("my_server/log") do |params, session, server|
467
- server.logger.info("Event: #{params['message']}")
309
+ ```ruby
310
+ server.register_tool(
311
+ name: 'generate_summary',
312
+ input_schema: {
313
+ type: 'object',
314
+ properties: { text: { type: 'string' } },
315
+ required: ['text']
316
+ }
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
468
326
  end
469
327
  ```
470
328
 
471
- ### Error Handling
329
+ </details>
330
+
331
+ <details>
332
+ <summary><strong>Custom Error Handling</strong></summary>
472
333
 
473
- Use proper error classes for correct JSON-RPC error responses.
334
+ Use proper MCP error types:
474
335
 
475
336
  ```ruby
476
- # In a tool handler
477
- if resource_not_found
478
- raise VectorMCP::NotFoundError.new("Resource not available")
479
- elsif invalid_parameters
480
- raise VectorMCP::InvalidParamsError.new("Invalid parameter format")
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')
346
+ end
481
347
  end
482
348
  ```
483
349
 
484
- Common error classes:
485
- - `VectorMCP::InvalidRequestError`
486
- - `VectorMCP::MethodNotFoundError`
487
- - `VectorMCP::InvalidParamsError`
488
- - `VectorMCP::NotFoundError`
489
- - `VectorMCP::InternalError`
350
+ </details>
490
351
 
491
- ### Sampling (LLM completions)
352
+ <details>
353
+ <summary><strong>Session Information</strong></summary>
492
354
 
493
- 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).
494
-
495
- #### Configuring Sampling Capabilities
496
-
497
- You can configure your server's sampling capabilities during initialization to advertise what features your server supports:
355
+ Access client context:
498
356
 
499
357
  ```ruby
500
- server = VectorMCP::Server.new(
501
- name: "MyServer",
502
- version: "1.0.0",
503
- sampling_config: {
504
- enabled: true, # Enable/disable sampling (default: true)
505
- supports_streaming: true, # Support streaming responses (default: false)
506
- supports_tool_calls: true, # Support tool calls in sampling (default: false)
507
- supports_images: true, # Support image content (default: false)
508
- max_tokens_limit: 4000, # Maximum tokens limit (default: nil, no limit)
509
- timeout_seconds: 60, # Default timeout in seconds (default: 30)
510
- context_inclusion_methods: ["none", "thisServer", "allServers"], # Supported context methods
511
- model_preferences_supported: true # Support model preferences (default: true)
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?
512
363
  }
513
- )
364
+ end
514
365
  ```
515
366
 
516
- **Configuration Options:**
517
- - `enabled`: Whether sampling is available at all
518
- - `supports_streaming`: Advertise support for streaming responses
519
- - `supports_tool_calls`: Advertise support for tool calls within sampling
520
- - `supports_images`: Advertise support for image content in messages
521
- - `max_tokens_limit`: Maximum tokens your server can handle (helps clients choose appropriate limits)
522
- - `timeout_seconds`: Default timeout for sampling requests
523
- - `context_inclusion_methods`: Supported context inclusion modes (`"none"`, `"thisServer"`, `"allServers"`)
524
- - `model_preferences_supported`: Whether your server supports model selection hints
367
+ </details>
525
368
 
526
- These capabilities are advertised to clients during the MCP handshake, helping them understand what your server supports and make appropriate sampling requests.
369
+ ---
527
370
 
528
- #### Minimal Configuration Examples
371
+ ## Integration Examples
529
372
 
530
- ```ruby
531
- # Basic sampling (default configuration)
532
- server = VectorMCP::Server.new(name: "BasicServer")
533
- # Advertises: createMessage support, model preferences, 30s timeout, basic context inclusion
534
-
535
- # Advanced streaming server
536
- server = VectorMCP::Server.new(
537
- name: "StreamingServer",
538
- sampling_config: {
539
- supports_streaming: true,
540
- supports_tool_calls: true,
541
- max_tokens_limit: 8000,
542
- timeout_seconds: 120
543
- }
544
- )
545
-
546
- # Disable sampling entirely
547
- server = VectorMCP::Server.new(
548
- name: "NoSamplingServer",
549
- sampling_config: { enabled: false }
550
- )
551
- ```
373
+ ### Claude Desktop
552
374
 
553
- #### Using Sampling in Your Handlers
554
-
555
- The `session.sample` method sends a `sampling/createMessage` request to the client and waits for the response:
556
-
557
- ```ruby
558
- # Inside a tool, resource, or prompt handler block:
559
- tool_input = args["topic"] # Assuming 'args' are the input to your handler
560
-
561
- begin
562
- sampling_result = session.sample(
563
- messages: [
564
- { role: "user", content: { type: "text", text: "Generate a short, catchy tagline for: #{tool_input}" } }
565
- ],
566
- max_tokens: 25,
567
- temperature: 0.8,
568
- model_preferences: { # Optional: guide client on model selection
569
- hints: [{ name: "claude-3-haiku" }, { name: "gpt-3.5-turbo" }], # Preferred models
570
- intelligence_priority: 0.5, # Balance between capability, speed, and cost
571
- speed_priority: 0.8
572
- },
573
- timeout: 15 # Optional: per-request timeout in seconds
574
- )
375
+ Add to your Claude Desktop configuration:
575
376
 
576
- if sampling_result.text?
577
- tagline = sampling_result.text_content
578
- "Generated tagline: #{tagline}"
579
- else
580
- "LLM did not return text content."
581
- end
582
- rescue VectorMCP::SamplingTimeoutError => e
583
- server.logger.warn("Sampling request timed out: #{e.message}")
584
- "Sorry, the request for a tagline timed out."
585
- rescue VectorMCP::SamplingError => e
586
- server.logger.error("Sampling request failed: #{e.message}")
587
- "Sorry, couldn't generate a tagline due to an error."
588
- rescue ArgumentError => e
589
- server.logger.error("Invalid arguments for sampling: #{e.message}")
590
- "Internal error: Invalid arguments for tagline generation."
591
- end
377
+ ```json
378
+ {
379
+ "mcpServers": {
380
+ "my-ruby-server": {
381
+ "command": "ruby",
382
+ "args": ["path/to/my_server.rb"]
383
+ }
384
+ }
385
+ }
592
386
  ```
593
387
 
594
- **Key Points:**
595
- - The `session.sample` method takes a hash conforming to the `VectorMCP::Sampling::Request` structure
596
- - It returns a `VectorMCP::Sampling::Result` object with methods like `text?`, `text_content`, `image?`, etc.
597
- - Raises `VectorMCP::SamplingTimeoutError`, `VectorMCP::SamplingError`, or `VectorMCP::SamplingRejectedError` on failures
598
- - Your server's advertised capabilities help clients understand what parameters are supported
388
+ ### Web Applications
599
389
 
600
- #### Accessing Sampling Configuration
601
-
602
- You can access your server's sampling configuration at runtime:
390
+ ```javascript
391
+ // Connect to SSE endpoint
392
+ const eventSource = new EventSource('http://localhost:8080/sse');
603
393
 
604
- ```ruby
605
- config = server.sampling_config
606
- puts "Streaming supported: #{config[:supports_streaming]}"
607
- puts "Max tokens: #{config[:max_tokens_limit] || 'unlimited'}"
608
- puts "Timeout: #{config[:timeout_seconds]}s"
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
+ });
609
409
  ```
610
410
 
611
- ## Example Implementations
411
+ ## Why Choose VectorMCP?
612
412
 
613
- These projects demonstrate real-world implementations of VectorMCP servers:
413
+ **🏆 Battle-Tested**: Used in production applications serving thousands of requests
614
414
 
615
- ### [file_system_mcp](https://github.com/sergiobayona/file_system_mcp)
415
+ **⚡ Performance**: Optimized for low latency and high throughput
616
416
 
617
- A complete MCP server providing filesystem operations:
618
- - Read/write files
619
- - Create/list/delete directories
620
- - Move files/directories
621
- - Search files
622
- - Get file metadata
417
+ **🛡️ Secure by Default**: Comprehensive input validation prevents common attacks
623
418
 
624
- Works with Claude Desktop and other MCP clients.
419
+ **📖 Well-Documented**: Extensive examples and clear API documentation
625
420
 
626
- ### Roots Demo (included)
421
+ **🔧 Extensible**: Easy to customize and extend for your specific needs
627
422
 
628
- The `examples/roots_demo.rb` script demonstrates comprehensive roots functionality:
629
- - Multiple root registration with automatic validation
630
- - Security boundaries and workspace context
631
- - Tools that operate safely within registered roots
632
- - Resources providing workspace information
633
- - Integration with other MCP features
423
+ **🤝 Community**: Active development and responsive maintainer
634
424
 
635
- Run it with: `ruby examples/roots_demo.rb`
425
+ ## Examples & Resources
636
426
 
637
- This example shows best practices for:
638
- - Registering multiple project directories as roots
639
- - Implementing security checks in tools
640
- - Providing workspace context to clients
641
- - 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
642
430
 
643
- ## Development
431
+ ## Installation & Setup
644
432
 
645
- 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
646
435
 
647
- To install this gem onto your local machine, run `bundle exec rake install`.
436
+ # Or in your Gemfile
437
+ gem 'vector_mcp'
438
+ ```
648
439
 
649
440
  ## Contributing
650
441
 
651
- 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).
652
443
 
653
444
  ## License
654
445
 
655
- 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).