tsikol 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +22 -0
- data/CONTRIBUTING.md +84 -0
- data/LICENSE +21 -0
- data/README.md +579 -0
- data/Rakefile +12 -0
- data/docs/README.md +69 -0
- data/docs/api/middleware.md +721 -0
- data/docs/api/prompt.md +858 -0
- data/docs/api/resource.md +651 -0
- data/docs/api/server.md +509 -0
- data/docs/api/test-helpers.md +591 -0
- data/docs/api/tool.md +527 -0
- data/docs/cookbook/authentication.md +651 -0
- data/docs/cookbook/caching.md +877 -0
- data/docs/cookbook/dynamic-tools.md +970 -0
- data/docs/cookbook/error-handling.md +887 -0
- data/docs/cookbook/logging.md +1044 -0
- data/docs/cookbook/rate-limiting.md +717 -0
- data/docs/examples/code-assistant.md +922 -0
- data/docs/examples/complete-server.md +726 -0
- data/docs/examples/database-manager.md +1198 -0
- data/docs/examples/devops-tools.md +1382 -0
- data/docs/examples/echo-server.md +501 -0
- data/docs/examples/weather-service.md +822 -0
- data/docs/guides/completion.md +472 -0
- data/docs/guides/getting-started.md +462 -0
- data/docs/guides/middleware.md +823 -0
- data/docs/guides/project-structure.md +434 -0
- data/docs/guides/prompts.md +920 -0
- data/docs/guides/resources.md +720 -0
- data/docs/guides/sampling.md +804 -0
- data/docs/guides/testing.md +863 -0
- data/docs/guides/tools.md +627 -0
- data/examples/README.md +92 -0
- data/examples/advanced_features.rb +129 -0
- data/examples/basic-migrated/app/prompts/weather_chat.rb +44 -0
- data/examples/basic-migrated/app/resources/weather_alerts.rb +18 -0
- data/examples/basic-migrated/app/tools/get_current_weather.rb +34 -0
- data/examples/basic-migrated/app/tools/get_forecast.rb +30 -0
- data/examples/basic-migrated/app/tools/get_weather_by_coords.rb +48 -0
- data/examples/basic-migrated/server.rb +25 -0
- data/examples/basic.rb +73 -0
- data/examples/full_featured.rb +175 -0
- data/examples/middleware_example.rb +112 -0
- data/examples/sampling_example.rb +104 -0
- data/examples/weather-service/app/prompts/weather/chat.rb +90 -0
- data/examples/weather-service/app/resources/weather/alerts.rb +59 -0
- data/examples/weather-service/app/tools/weather/get_current.rb +82 -0
- data/examples/weather-service/app/tools/weather/get_forecast.rb +90 -0
- data/examples/weather-service/server.rb +28 -0
- data/exe/tsikol +6 -0
- data/lib/tsikol/cli/templates/Gemfile.erb +10 -0
- data/lib/tsikol/cli/templates/README.md.erb +38 -0
- data/lib/tsikol/cli/templates/gitignore.erb +49 -0
- data/lib/tsikol/cli/templates/prompt.rb.erb +53 -0
- data/lib/tsikol/cli/templates/resource.rb.erb +29 -0
- data/lib/tsikol/cli/templates/server.rb.erb +24 -0
- data/lib/tsikol/cli/templates/tool.rb.erb +60 -0
- data/lib/tsikol/cli.rb +203 -0
- data/lib/tsikol/error_handler.rb +141 -0
- data/lib/tsikol/health.rb +198 -0
- data/lib/tsikol/http_transport.rb +72 -0
- data/lib/tsikol/lifecycle.rb +149 -0
- data/lib/tsikol/middleware.rb +168 -0
- data/lib/tsikol/prompt.rb +101 -0
- data/lib/tsikol/resource.rb +53 -0
- data/lib/tsikol/router.rb +190 -0
- data/lib/tsikol/server.rb +660 -0
- data/lib/tsikol/stdio_transport.rb +108 -0
- data/lib/tsikol/test_helpers.rb +261 -0
- data/lib/tsikol/tool.rb +111 -0
- data/lib/tsikol/version.rb +5 -0
- data/lib/tsikol.rb +72 -0
- metadata +219 -0
@@ -0,0 +1,434 @@
|
|
1
|
+
# Project Structure Guide
|
2
|
+
|
3
|
+
Understanding how Tsikol projects are organized helps you build maintainable MCP servers.
|
4
|
+
|
5
|
+
## Table of Contents
|
6
|
+
|
7
|
+
1. [Directory Layout](#directory-layout)
|
8
|
+
2. [File Organization](#file-organization)
|
9
|
+
3. [Naming Conventions](#naming-conventions)
|
10
|
+
4. [Component Structure](#component-structure)
|
11
|
+
5. [Configuration](#configuration)
|
12
|
+
6. [Best Practices](#best-practices)
|
13
|
+
|
14
|
+
## Directory Layout
|
15
|
+
|
16
|
+
A typical Tsikol project follows this structure:
|
17
|
+
|
18
|
+
```
|
19
|
+
my-mcp-server/
|
20
|
+
├── server.rb # Main server file and entry point
|
21
|
+
├── Gemfile # Ruby dependencies
|
22
|
+
├── README.md # Project documentation
|
23
|
+
├── .gitignore # Git ignore rules
|
24
|
+
├── app/ # Application components
|
25
|
+
│ ├── tools/ # Tool implementations
|
26
|
+
│ ├── resources/ # Resource implementations
|
27
|
+
│ └── prompts/ # Prompt implementations
|
28
|
+
├── config/ # Configuration files (optional)
|
29
|
+
├── lib/ # Additional libraries (optional)
|
30
|
+
├── test/ # Test files
|
31
|
+
└── tmp/ # Temporary files (logs, etc.)
|
32
|
+
```
|
33
|
+
|
34
|
+
## File Organization
|
35
|
+
|
36
|
+
### Tools Directory (`app/tools/`)
|
37
|
+
|
38
|
+
Tools are organized by functionality:
|
39
|
+
|
40
|
+
```
|
41
|
+
app/tools/
|
42
|
+
├── file_manager.rb # Single tool
|
43
|
+
├── database_query.rb # Single tool
|
44
|
+
└── analytics/ # Grouped tools
|
45
|
+
├── report_generator.rb
|
46
|
+
├── data_processor.rb
|
47
|
+
└── metrics_calculator.rb
|
48
|
+
```
|
49
|
+
|
50
|
+
### Resources Directory (`app/resources/`)
|
51
|
+
|
52
|
+
Resources follow URI-based organization:
|
53
|
+
|
54
|
+
```
|
55
|
+
app/resources/
|
56
|
+
├── system_status.rb # URI: system/status
|
57
|
+
├── health_check.rb # URI: health/check
|
58
|
+
└── api/ # Grouped resources
|
59
|
+
├── endpoints.rb # URI: api/endpoints
|
60
|
+
└── documentation.rb # URI: api/documentation
|
61
|
+
```
|
62
|
+
|
63
|
+
### Prompts Directory (`app/prompts/`)
|
64
|
+
|
65
|
+
Prompts are organized by purpose:
|
66
|
+
|
67
|
+
```
|
68
|
+
app/prompts/
|
69
|
+
├── code_assistant.rb # Single prompt
|
70
|
+
├── writer.rb # Single prompt
|
71
|
+
└── development/ # Grouped prompts
|
72
|
+
├── debugger.rb
|
73
|
+
├── refactorer.rb
|
74
|
+
└── reviewer.rb
|
75
|
+
```
|
76
|
+
|
77
|
+
## Naming Conventions
|
78
|
+
|
79
|
+
### File Names
|
80
|
+
|
81
|
+
- Use snake_case: `file_manager.rb`, `system_status.rb`
|
82
|
+
- Match the class name: `FileManager` → `file_manager.rb`
|
83
|
+
- For namespaced classes: `Analytics::ReportGenerator` → `analytics/report_generator.rb`
|
84
|
+
|
85
|
+
### Class Names
|
86
|
+
|
87
|
+
- Use PascalCase: `FileManager`, `SystemStatus`
|
88
|
+
- Inherit from appropriate base: `< Tsikol::Tool`, `< Tsikol::Resource`, `< Tsikol::Prompt`
|
89
|
+
- Use modules for namespacing: `module Analytics; class ReportGenerator < Tsikol::Tool`
|
90
|
+
|
91
|
+
### URIs and Names
|
92
|
+
|
93
|
+
- Resources: URI matches file structure
|
94
|
+
- File: `app/resources/weather/alerts.rb`
|
95
|
+
- URI: `weather/alerts`
|
96
|
+
- Tools: Name is class name in snake_case
|
97
|
+
- Class: `FileManager`
|
98
|
+
- Name: `file_manager`
|
99
|
+
- Prompts: Explicit name in class
|
100
|
+
- Class: `CodeAssistant`
|
101
|
+
- Name: `code_assistant` (set with `name "code_assistant"`)
|
102
|
+
|
103
|
+
## Component Structure
|
104
|
+
|
105
|
+
### Tool Structure
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
# app/tools/example_tool.rb
|
109
|
+
class ExampleTool < Tsikol::Tool
|
110
|
+
description "Brief description of what this tool does"
|
111
|
+
|
112
|
+
parameter :required_param do
|
113
|
+
type :string
|
114
|
+
required
|
115
|
+
description "What this parameter is for"
|
116
|
+
end
|
117
|
+
|
118
|
+
parameter :optional_param do
|
119
|
+
type :integer
|
120
|
+
optional
|
121
|
+
default 10
|
122
|
+
description "Optional parameter with default"
|
123
|
+
end
|
124
|
+
|
125
|
+
def execute(required_param:, optional_param: 10)
|
126
|
+
# Implementation
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
def helper_method
|
132
|
+
# Private helpers
|
133
|
+
end
|
134
|
+
|
135
|
+
def set_server(server)
|
136
|
+
@server = server
|
137
|
+
# Setup logging
|
138
|
+
end
|
139
|
+
end
|
140
|
+
```
|
141
|
+
|
142
|
+
### Resource Structure
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
# app/resources/example_resource.rb
|
146
|
+
class ExampleResource < Tsikol::Resource
|
147
|
+
uri "example/resource"
|
148
|
+
description "What this resource provides"
|
149
|
+
|
150
|
+
def read
|
151
|
+
# Return data as string (JSON, text, etc.)
|
152
|
+
{
|
153
|
+
data: fetch_data,
|
154
|
+
timestamp: Time.now.iso8601
|
155
|
+
}.to_json
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def fetch_data
|
161
|
+
# Implementation
|
162
|
+
end
|
163
|
+
|
164
|
+
def set_server(server)
|
165
|
+
@server = server
|
166
|
+
end
|
167
|
+
end
|
168
|
+
```
|
169
|
+
|
170
|
+
### Prompt Structure
|
171
|
+
|
172
|
+
```ruby
|
173
|
+
# app/prompts/example_prompt.rb
|
174
|
+
class ExamplePrompt < Tsikol::Prompt
|
175
|
+
name "example_prompt"
|
176
|
+
description "What this prompt helps with"
|
177
|
+
|
178
|
+
argument :topic do
|
179
|
+
type :string
|
180
|
+
required
|
181
|
+
description "The topic to discuss"
|
182
|
+
end
|
183
|
+
|
184
|
+
def get_messages(topic:)
|
185
|
+
[
|
186
|
+
{
|
187
|
+
role: "system",
|
188
|
+
content: {
|
189
|
+
type: "text",
|
190
|
+
text: "You are a helpful assistant."
|
191
|
+
}
|
192
|
+
},
|
193
|
+
{
|
194
|
+
role: "user",
|
195
|
+
content: {
|
196
|
+
type: "text",
|
197
|
+
text: "Help me with: #{topic}"
|
198
|
+
}
|
199
|
+
}
|
200
|
+
]
|
201
|
+
end
|
202
|
+
|
203
|
+
def set_server(server)
|
204
|
+
@server = server
|
205
|
+
end
|
206
|
+
end
|
207
|
+
```
|
208
|
+
|
209
|
+
## Configuration
|
210
|
+
|
211
|
+
### Server Configuration
|
212
|
+
|
213
|
+
The main `server.rb` file configures your MCP server:
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
#!/usr/bin/env ruby
|
217
|
+
# frozen_string_literal: true
|
218
|
+
|
219
|
+
require 'bundler/setup'
|
220
|
+
require 'tsikol'
|
221
|
+
|
222
|
+
# Require all components
|
223
|
+
Dir.glob('app/**/*.rb').each { |file| require_relative file }
|
224
|
+
|
225
|
+
Tsikol.start(name: "my-mcp-server") do
|
226
|
+
# Register components
|
227
|
+
tool FileManager
|
228
|
+
tool DatabaseQuery
|
229
|
+
resource SystemStatus
|
230
|
+
prompt CodeAssistant
|
231
|
+
|
232
|
+
# Enable capabilities
|
233
|
+
logging true
|
234
|
+
completion true
|
235
|
+
sampling true
|
236
|
+
|
237
|
+
# Add middleware
|
238
|
+
use Tsikol::LoggingMiddleware
|
239
|
+
use Tsikol::RateLimitMiddleware, max_requests: 100
|
240
|
+
|
241
|
+
# Lifecycle hooks
|
242
|
+
before_start do
|
243
|
+
# Initialize
|
244
|
+
end
|
245
|
+
|
246
|
+
after_start do
|
247
|
+
# Ready
|
248
|
+
end
|
249
|
+
end
|
250
|
+
```
|
251
|
+
|
252
|
+
### Environment Configuration
|
253
|
+
|
254
|
+
Use environment variables for configuration:
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
# config/environment.rb
|
258
|
+
module Config
|
259
|
+
DATABASE_URL = ENV.fetch('DATABASE_URL', 'sqlite://db/development.db')
|
260
|
+
API_KEY = ENV.fetch('API_KEY', nil)
|
261
|
+
LOG_LEVEL = ENV.fetch('LOG_LEVEL', 'info')
|
262
|
+
MAX_REQUEST_SIZE = ENV.fetch('MAX_REQUEST_SIZE', '1048576').to_i
|
263
|
+
end
|
264
|
+
|
265
|
+
# In server.rb
|
266
|
+
require_relative 'config/environment'
|
267
|
+
```
|
268
|
+
|
269
|
+
## Best Practices
|
270
|
+
|
271
|
+
### 1. Single Responsibility
|
272
|
+
|
273
|
+
Each component should have one clear purpose:
|
274
|
+
|
275
|
+
```ruby
|
276
|
+
# Good: Focused tool
|
277
|
+
class FileReader < Tsikol::Tool
|
278
|
+
description "Read file contents"
|
279
|
+
# Just reads files
|
280
|
+
end
|
281
|
+
|
282
|
+
# Bad: Doing too much
|
283
|
+
class FileManager < Tsikol::Tool
|
284
|
+
description "Read, write, delete, compress, upload files"
|
285
|
+
# Too many responsibilities
|
286
|
+
end
|
287
|
+
```
|
288
|
+
|
289
|
+
### 2. Consistent Naming
|
290
|
+
|
291
|
+
Follow conventions throughout:
|
292
|
+
|
293
|
+
```ruby
|
294
|
+
# File: app/tools/code_analyzer.rb
|
295
|
+
class CodeAnalyzer < Tsikol::Tool
|
296
|
+
# Matches file name
|
297
|
+
end
|
298
|
+
|
299
|
+
# File: app/resources/system/metrics.rb
|
300
|
+
module System
|
301
|
+
class Metrics < Tsikol::Resource
|
302
|
+
uri "system/metrics" # Matches file path
|
303
|
+
end
|
304
|
+
end
|
305
|
+
```
|
306
|
+
|
307
|
+
### 3. Modular Organization
|
308
|
+
|
309
|
+
Group related components:
|
310
|
+
|
311
|
+
```
|
312
|
+
app/tools/git/
|
313
|
+
├── commit.rb
|
314
|
+
├── branch.rb
|
315
|
+
├── merge.rb
|
316
|
+
└── push.rb
|
317
|
+
```
|
318
|
+
|
319
|
+
### 4. Shared Code
|
320
|
+
|
321
|
+
Put shared code in lib:
|
322
|
+
|
323
|
+
```ruby
|
324
|
+
# lib/database.rb
|
325
|
+
module Database
|
326
|
+
def self.connection
|
327
|
+
@connection ||= establish_connection
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
# In tools
|
332
|
+
require_relative '../../lib/database'
|
333
|
+
|
334
|
+
class QueryTool < Tsikol::Tool
|
335
|
+
def execute(query:)
|
336
|
+
Database.connection.execute(query)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
```
|
340
|
+
|
341
|
+
### 5. Testing Structure
|
342
|
+
|
343
|
+
Mirror your app structure:
|
344
|
+
|
345
|
+
```
|
346
|
+
test/
|
347
|
+
├── tools/
|
348
|
+
│ ├── file_manager_test.rb
|
349
|
+
│ └── analytics/
|
350
|
+
│ └── report_generator_test.rb
|
351
|
+
├── resources/
|
352
|
+
│ └── system_status_test.rb
|
353
|
+
└── test_helper.rb
|
354
|
+
```
|
355
|
+
|
356
|
+
## Advanced Patterns
|
357
|
+
|
358
|
+
### Namespaced Components
|
359
|
+
|
360
|
+
For larger projects:
|
361
|
+
|
362
|
+
```ruby
|
363
|
+
# app/tools/analytics/sales_report.rb
|
364
|
+
module Analytics
|
365
|
+
class SalesReport < Tsikol::Tool
|
366
|
+
description "Generate sales reports"
|
367
|
+
|
368
|
+
def execute(period:)
|
369
|
+
# Implementation
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
# In server.rb
|
375
|
+
tool Analytics::SalesReport
|
376
|
+
```
|
377
|
+
|
378
|
+
### Shared Base Classes
|
379
|
+
|
380
|
+
Create base classes for common functionality:
|
381
|
+
|
382
|
+
```ruby
|
383
|
+
# app/tools/base_tool.rb
|
384
|
+
class BaseTool < Tsikol::Tool
|
385
|
+
def set_server(server)
|
386
|
+
@server = server
|
387
|
+
setup_logging
|
388
|
+
setup_metrics
|
389
|
+
end
|
390
|
+
|
391
|
+
private
|
392
|
+
|
393
|
+
def setup_logging
|
394
|
+
define_singleton_method(:log) do |level, message, data: nil|
|
395
|
+
@server.log(level, message, data: data)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
def setup_metrics
|
400
|
+
@metrics = @server.metrics
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
# app/tools/specific_tool.rb
|
405
|
+
class SpecificTool < BaseTool
|
406
|
+
# Inherits common setup
|
407
|
+
end
|
408
|
+
```
|
409
|
+
|
410
|
+
### Plugin System
|
411
|
+
|
412
|
+
For extensible servers:
|
413
|
+
|
414
|
+
```ruby
|
415
|
+
# lib/plugin_loader.rb
|
416
|
+
module PluginLoader
|
417
|
+
def self.load_plugins(server)
|
418
|
+
Dir.glob('plugins/*/init.rb').each do |plugin|
|
419
|
+
require_relative plugin
|
420
|
+
end
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
# plugins/custom_tools/init.rb
|
425
|
+
require_relative 'tools/custom_tool'
|
426
|
+
Tsikol.current_server.tool CustomTool
|
427
|
+
```
|
428
|
+
|
429
|
+
## Next Steps
|
430
|
+
|
431
|
+
- Learn about [Tools](tools.md) in detail
|
432
|
+
- Understand [Resources](resources.md)
|
433
|
+
- Explore [Prompts](prompts.md)
|
434
|
+
- Set up [Testing](testing.md)
|