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.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +22 -0
  3. data/CONTRIBUTING.md +84 -0
  4. data/LICENSE +21 -0
  5. data/README.md +579 -0
  6. data/Rakefile +12 -0
  7. data/docs/README.md +69 -0
  8. data/docs/api/middleware.md +721 -0
  9. data/docs/api/prompt.md +858 -0
  10. data/docs/api/resource.md +651 -0
  11. data/docs/api/server.md +509 -0
  12. data/docs/api/test-helpers.md +591 -0
  13. data/docs/api/tool.md +527 -0
  14. data/docs/cookbook/authentication.md +651 -0
  15. data/docs/cookbook/caching.md +877 -0
  16. data/docs/cookbook/dynamic-tools.md +970 -0
  17. data/docs/cookbook/error-handling.md +887 -0
  18. data/docs/cookbook/logging.md +1044 -0
  19. data/docs/cookbook/rate-limiting.md +717 -0
  20. data/docs/examples/code-assistant.md +922 -0
  21. data/docs/examples/complete-server.md +726 -0
  22. data/docs/examples/database-manager.md +1198 -0
  23. data/docs/examples/devops-tools.md +1382 -0
  24. data/docs/examples/echo-server.md +501 -0
  25. data/docs/examples/weather-service.md +822 -0
  26. data/docs/guides/completion.md +472 -0
  27. data/docs/guides/getting-started.md +462 -0
  28. data/docs/guides/middleware.md +823 -0
  29. data/docs/guides/project-structure.md +434 -0
  30. data/docs/guides/prompts.md +920 -0
  31. data/docs/guides/resources.md +720 -0
  32. data/docs/guides/sampling.md +804 -0
  33. data/docs/guides/testing.md +863 -0
  34. data/docs/guides/tools.md +627 -0
  35. data/examples/README.md +92 -0
  36. data/examples/advanced_features.rb +129 -0
  37. data/examples/basic-migrated/app/prompts/weather_chat.rb +44 -0
  38. data/examples/basic-migrated/app/resources/weather_alerts.rb +18 -0
  39. data/examples/basic-migrated/app/tools/get_current_weather.rb +34 -0
  40. data/examples/basic-migrated/app/tools/get_forecast.rb +30 -0
  41. data/examples/basic-migrated/app/tools/get_weather_by_coords.rb +48 -0
  42. data/examples/basic-migrated/server.rb +25 -0
  43. data/examples/basic.rb +73 -0
  44. data/examples/full_featured.rb +175 -0
  45. data/examples/middleware_example.rb +112 -0
  46. data/examples/sampling_example.rb +104 -0
  47. data/examples/weather-service/app/prompts/weather/chat.rb +90 -0
  48. data/examples/weather-service/app/resources/weather/alerts.rb +59 -0
  49. data/examples/weather-service/app/tools/weather/get_current.rb +82 -0
  50. data/examples/weather-service/app/tools/weather/get_forecast.rb +90 -0
  51. data/examples/weather-service/server.rb +28 -0
  52. data/exe/tsikol +6 -0
  53. data/lib/tsikol/cli/templates/Gemfile.erb +10 -0
  54. data/lib/tsikol/cli/templates/README.md.erb +38 -0
  55. data/lib/tsikol/cli/templates/gitignore.erb +49 -0
  56. data/lib/tsikol/cli/templates/prompt.rb.erb +53 -0
  57. data/lib/tsikol/cli/templates/resource.rb.erb +29 -0
  58. data/lib/tsikol/cli/templates/server.rb.erb +24 -0
  59. data/lib/tsikol/cli/templates/tool.rb.erb +60 -0
  60. data/lib/tsikol/cli.rb +203 -0
  61. data/lib/tsikol/error_handler.rb +141 -0
  62. data/lib/tsikol/health.rb +198 -0
  63. data/lib/tsikol/http_transport.rb +72 -0
  64. data/lib/tsikol/lifecycle.rb +149 -0
  65. data/lib/tsikol/middleware.rb +168 -0
  66. data/lib/tsikol/prompt.rb +101 -0
  67. data/lib/tsikol/resource.rb +53 -0
  68. data/lib/tsikol/router.rb +190 -0
  69. data/lib/tsikol/server.rb +660 -0
  70. data/lib/tsikol/stdio_transport.rb +108 -0
  71. data/lib/tsikol/test_helpers.rb +261 -0
  72. data/lib/tsikol/tool.rb +111 -0
  73. data/lib/tsikol/version.rb +5 -0
  74. data/lib/tsikol.rb +72 -0
  75. metadata +219 -0
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/tsikol'
5
+
6
+ Tsikol.server "ai-assistant" do
7
+ # Enable and configure sampling
8
+ on_sampling do |request|
9
+ # This handler is called when the server needs AI assistance
10
+ # In a real implementation, this would be handled by the MCP client
11
+ # For demo purposes, we'll simulate a response
12
+
13
+ messages = request[:messages]
14
+ system_prompt = request[:system_prompt]
15
+ max_tokens = request[:max_tokens] || 100
16
+
17
+ log :info, "Sampling request received", data: {
18
+ message_count: messages.size,
19
+ system_prompt: system_prompt,
20
+ max_tokens: max_tokens
21
+ }
22
+
23
+ # In real usage, the MCP client (like Claude Code) would:
24
+ # 1. Show the request to the user for approval
25
+ # 2. Send to the AI model
26
+ # 3. Return the response
27
+
28
+ # For demo, return a simulated response
29
+ {
30
+ role: "assistant",
31
+ content: {
32
+ type: "text",
33
+ text: "This is a simulated AI response. In production, the MCP client would handle this request and return actual AI-generated content."
34
+ }
35
+ }
36
+ end
37
+
38
+ # Tool that uses sampling to enhance its functionality
39
+ tool "enhance_text" do |text:, style: "professional"|
40
+ log :info, "Enhancing text with style: #{style}"
41
+
42
+ # Request AI assistance via sampling
43
+ begin
44
+ # In a real implementation, the server would send a sampling request
45
+ # to the client, which would then use an AI model
46
+
47
+ # For now, we'll just demonstrate the structure
48
+ enhanced = case style
49
+ when "professional"
50
+ "#{text} (enhanced professionally)"
51
+ when "casual"
52
+ "#{text} (made more casual)"
53
+ when "technical"
54
+ "#{text} (with technical details)"
55
+ else
56
+ text
57
+ end
58
+
59
+ "Enhanced: #{enhanced}"
60
+ rescue => e
61
+ log :error, "Enhancement failed", data: { error: e.message }
62
+ "Error enhancing text: #{e.message}"
63
+ end
64
+ end
65
+
66
+ # Tool that generates content using sampling
67
+ tool "generate_content" do |topic:, length: "short"|
68
+ log :info, "Generating content about: #{topic}"
69
+
70
+ # This would trigger a sampling request in a real implementation
71
+ content_length = case length
72
+ when "short"
73
+ "Brief content about #{topic}."
74
+ when "medium"
75
+ "Detailed explanation about #{topic} with examples and context."
76
+ when "long"
77
+ "Comprehensive analysis of #{topic} including background, current state, and future implications."
78
+ else
79
+ "Content about #{topic}."
80
+ end
81
+
82
+ content_length
83
+ end
84
+
85
+ # Resource that provides sampling configuration info
86
+ resource "sampling/config" do
87
+ {
88
+ enabled: true,
89
+ models_supported: ["claude-3-sonnet", "claude-3-opus"],
90
+ max_tokens_limit: 4096,
91
+ features: [
92
+ "Content generation",
93
+ "Text enhancement",
94
+ "Code generation",
95
+ "Analysis and reasoning"
96
+ ]
97
+ }.to_json
98
+ end
99
+
100
+ # Prompt that can be enhanced with sampling
101
+ prompt "brainstorm" do |topic:, approach: "creative"|
102
+ "Help me brainstorm ideas about #{topic} using a #{approach} approach. Generate diverse and innovative suggestions."
103
+ end
104
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Weather
4
+ class Chat < Tsikol::Prompt
5
+ name "weather-chat"
6
+ description "Interactive weather information assistant"
7
+
8
+ argument :topic do
9
+ type :string
10
+ required
11
+ description "Weather topic to discuss"
12
+
13
+ complete do |partial|
14
+ topics = [
15
+ "current conditions",
16
+ "forecast",
17
+ "climate trends",
18
+ "severe weather",
19
+ "travel planning",
20
+ "outdoor activities",
21
+ "seasonal patterns",
22
+ "weather safety"
23
+ ]
24
+ topics.select { |topic| topic.downcase.include?(partial.downcase) }
25
+ end
26
+ end
27
+
28
+ argument :location do
29
+ type :string
30
+ optional
31
+ description "Specific location for weather discussion"
32
+
33
+ complete do |partial|
34
+ cities = [
35
+ "New York", "London", "Tokyo", "Paris", "Berlin",
36
+ "Sydney", "Toronto", "Mumbai", "Beijing", "Moscow"
37
+ ]
38
+ cities.select { |city| city.downcase.start_with?(partial.downcase) }
39
+ end
40
+ end
41
+
42
+ def get_messages(topic:, location: nil)
43
+ base_prompt = case topic
44
+ when "current conditions"
45
+ "You are a weather assistant. Help me understand the current weather conditions#{location ? " in #{location}" : ""}."
46
+ when "forecast"
47
+ "You are a weather forecasting expert. Provide detailed forecast information#{location ? " for #{location}" : ""}."
48
+ when "climate trends"
49
+ "You are a climate scientist. Discuss recent climate trends and patterns#{location ? " affecting #{location}" : ""}."
50
+ when "severe weather"
51
+ "You are a severe weather specialist. Explain severe weather risks and safety#{location ? " for #{location}" : ""}."
52
+ when "travel planning"
53
+ "You are a travel weather advisor. Help plan travel considering weather conditions#{location ? " for #{location}" : ""}."
54
+ when "outdoor activities"
55
+ "You are an outdoor activity weather consultant. Advise on weather for outdoor activities#{location ? " in #{location}" : ""}."
56
+ when "seasonal patterns"
57
+ "You are a seasonal weather expert. Explain seasonal weather patterns#{location ? " for #{location}" : ""}."
58
+ when "weather safety"
59
+ "You are a weather safety expert. Provide weather safety tips and guidelines#{location ? " for #{location}" : ""}."
60
+ else
61
+ "You are a knowledgeable weather assistant. Help me understand #{topic}#{location ? " for #{location}" : ""}."
62
+ end
63
+
64
+ [
65
+ {
66
+ role: "system",
67
+ content: {
68
+ type: "text",
69
+ text: base_prompt + " Provide accurate, helpful information based on typical weather patterns and safety guidelines."
70
+ }
71
+ },
72
+ {
73
+ role: "user",
74
+ content: {
75
+ type: "text",
76
+ text: "I'd like to know about #{topic}#{location ? " in #{location}" : ""}. What can you tell me?"
77
+ }
78
+ }
79
+ ]
80
+ end
81
+
82
+ def set_server(server)
83
+ @server = server
84
+
85
+ define_singleton_method(:log) do |level, message, data: nil, logger: nil|
86
+ @server.log(level, message, data: data, logger: logger)
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Weather
4
+ class Alerts < Tsikol::Resource
5
+ uri "weather/alerts"
6
+ description "Active weather alerts and warnings"
7
+
8
+ def read
9
+ # Mock weather alerts data
10
+ alerts = [
11
+ {
12
+ type: "Severe Thunderstorm Warning",
13
+ location: "New York",
14
+ severity: "moderate",
15
+ expires: (Time.now + 3600).strftime("%Y-%m-%d %H:%M"),
16
+ description: "Severe thunderstorms capable of producing damaging winds and large hail"
17
+ },
18
+ {
19
+ type: "Heat Advisory",
20
+ location: "Phoenix",
21
+ severity: "minor",
22
+ expires: (Time.now + 7200).strftime("%Y-%m-%d %H:%M"),
23
+ description: "Temperatures expected to reach 110°F. Stay hydrated and avoid prolonged outdoor exposure"
24
+ },
25
+ {
26
+ type: "Winter Storm Watch",
27
+ location: "Chicago",
28
+ severity: "major",
29
+ expires: (Time.now + 10800).strftime("%Y-%m-%d %H:%M"),
30
+ description: "Heavy snow accumulation of 8-12 inches expected. Travel will be dangerous"
31
+ }
32
+ ]
33
+
34
+ if alerts.empty?
35
+ "No active weather alerts at this time."
36
+ else
37
+ output = ["Active Weather Alerts:", ""]
38
+
39
+ alerts.each_with_index do |alert, i|
40
+ output << "#{i + 1}. #{alert[:type]} - #{alert[:location]}"
41
+ output << " Severity: #{alert[:severity].capitalize}"
42
+ output << " Expires: #{alert[:expires]}"
43
+ output << " #{alert[:description]}"
44
+ output << ""
45
+ end
46
+
47
+ output.join("\n").strip
48
+ end
49
+ end
50
+
51
+ def set_server(server)
52
+ @server = server
53
+
54
+ define_singleton_method(:log) do |level, message, data: nil, logger: nil|
55
+ @server.log(level, message, data: data, logger: logger)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Weather
4
+ class GetCurrent < Tsikol::Tool
5
+ description "Get current weather for a location"
6
+
7
+ parameter :location do
8
+ type :string
9
+ required
10
+ description "City name or coordinates"
11
+
12
+ complete do |partial|
13
+ cities = [
14
+ "New York", "London", "Tokyo", "Paris", "Berlin",
15
+ "Sydney", "Toronto", "Mumbai", "Beijing", "Moscow",
16
+ "Los Angeles", "Chicago", "Houston", "Phoenix", "Philadelphia"
17
+ ]
18
+ cities.select { |city| city.downcase.start_with?(partial.downcase) }
19
+ end
20
+ end
21
+
22
+ parameter :units do
23
+ type :string
24
+ optional
25
+ default "fahrenheit"
26
+ enum ["fahrenheit", "celsius", "kelvin"]
27
+ description "Temperature units"
28
+ end
29
+
30
+ def initialize
31
+ @server = nil # Will be set by framework
32
+ end
33
+
34
+ def execute(location:, units: "fahrenheit")
35
+ # Log using server's log method if available
36
+ log :info, "Getting weather for #{location}" if respond_to?(:log)
37
+
38
+ # Mock temperature data
39
+ temps = {
40
+ "New York" => 72,
41
+ "London" => 61,
42
+ "Tokyo" => 77,
43
+ "Paris" => 64,
44
+ "Berlin" => 59
45
+ }
46
+
47
+ temp = temps[location] || 70
48
+
49
+ if temps[location]
50
+ log :debug, "Found temperature in cache", data: { location: location, temp: temp } if respond_to?(:log)
51
+ else
52
+ log :warning, "Location not found, using default", data: { location: location } if respond_to?(:log)
53
+ end
54
+
55
+ converted_temp = convert_temperature(temp, units)
56
+ "Currently #{converted_temp}° #{units} in #{location}"
57
+ end
58
+
59
+ private
60
+
61
+ def convert_temperature(fahrenheit, to_units)
62
+ case to_units
63
+ when "celsius"
64
+ ((fahrenheit - 32) * 5.0 / 9.0).round(1)
65
+ when "kelvin"
66
+ ((fahrenheit - 32) * 5.0 / 9.0 + 273.15).round(1)
67
+ else
68
+ fahrenheit
69
+ end
70
+ end
71
+
72
+ # Allow server to inject log method
73
+ def set_server(server)
74
+ @server = server
75
+
76
+ # Define log method that delegates to server
77
+ define_singleton_method(:log) do |level, message, data: nil, logger: nil|
78
+ @server.log(level, message, data: data, logger: logger)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+
5
+ module Weather
6
+ class GetForecast < Tsikol::Tool
7
+ description "Get weather forecast for a location"
8
+
9
+ parameter :location do
10
+ type :string
11
+ required
12
+ description "City name or coordinates"
13
+
14
+ complete do |partial|
15
+ cities = [
16
+ "New York", "London", "Tokyo", "Paris", "Berlin",
17
+ "Sydney", "Toronto", "Mumbai", "Beijing", "Moscow"
18
+ ]
19
+ cities.select { |city| city.downcase.start_with?(partial.downcase) }
20
+ end
21
+ end
22
+
23
+ parameter :days do
24
+ type :integer
25
+ optional
26
+ default 5
27
+ description "Number of days to forecast (1-10)"
28
+ end
29
+
30
+ parameter :units do
31
+ type :string
32
+ optional
33
+ default "fahrenheit"
34
+ enum ["fahrenheit", "celsius", "kelvin"]
35
+ description "Temperature units"
36
+ end
37
+
38
+ def execute(location:, days: 5, units: "fahrenheit")
39
+ log :info, "Getting #{days}-day forecast for #{location}" if respond_to?(:log)
40
+
41
+ # Validate days
42
+ days = [[days, 1].max, 10].min
43
+
44
+ # Mock forecast data
45
+ base_temps = {
46
+ "New York" => 72,
47
+ "London" => 61,
48
+ "Tokyo" => 77,
49
+ "Paris" => 64,
50
+ "Berlin" => 59
51
+ }
52
+
53
+ base_temp = base_temps[location] || 70
54
+ forecast = []
55
+
56
+ days.times do |i|
57
+ # Add some variation
58
+ temp_variation = rand(-10..10)
59
+ temp = base_temp + temp_variation
60
+ converted_temp = convert_temperature(temp, units)
61
+
62
+ date = (Date.today + i + 1).strftime("%A, %B %d")
63
+ forecast << "#{date}: #{converted_temp}° #{units}"
64
+ end
65
+
66
+ "#{days}-day forecast for #{location}:\n#{forecast.join("\n")}"
67
+ end
68
+
69
+ private
70
+
71
+ def convert_temperature(fahrenheit, to_units)
72
+ case to_units
73
+ when "celsius"
74
+ ((fahrenheit - 32) * 5.0 / 9.0).round(1)
75
+ when "kelvin"
76
+ ((fahrenheit - 32) * 5.0 / 9.0 + 273.15).round(1)
77
+ else
78
+ fahrenheit
79
+ end
80
+ end
81
+
82
+ def set_server(server)
83
+ @server = server
84
+
85
+ define_singleton_method(:log) do |level, message, data: nil, logger: nil|
86
+ @server.log(level, message, data: data, logger: logger)
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../../lib/tsikol'
5
+
6
+ # Require all components
7
+ require_relative 'app/tools/weather/get_current'
8
+ require_relative 'app/tools/weather/get_forecast'
9
+ require_relative 'app/resources/weather/alerts'
10
+ require_relative 'app/prompts/weather/chat'
11
+
12
+ # Start the weather service with inline routes
13
+ Tsikol.start(name: "weather-service") do
14
+ # Register components
15
+ tool Weather::GetCurrent
16
+ tool Weather::GetForecast
17
+ resource Weather::Alerts
18
+ prompt Weather::Chat
19
+
20
+ # Enable capabilities
21
+ logging true
22
+ completion true
23
+
24
+ # Simple inline resource
25
+ resource "server/health" do
26
+ "Weather service is healthy"
27
+ end
28
+ end
data/exe/tsikol ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "tsikol/cli"
5
+
6
+ Tsikol::CLI.start(ARGV)
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "tsikol"
6
+
7
+ group :development, :test do
8
+ gem "minitest", "~> 5.0"
9
+ gem "rake", "~> 13.0"
10
+ end
@@ -0,0 +1,38 @@
1
+ # <%= @project_name.split(/[-_]/).map(&:capitalize).join(' ') %>
2
+
3
+ A Model Context Protocol (MCP) server built with Tsikol.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bundle install
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Start the server:
14
+
15
+ ```bash
16
+ ./server.rb
17
+ ```
18
+
19
+ ## Development
20
+
21
+ Generate new components:
22
+
23
+ ```bash
24
+ # Generate a new tool
25
+ tsikol generate tool my_tool param1:string param2:integer
26
+
27
+ # Generate a new resource
28
+ tsikol generate resource my_resource
29
+
30
+ # Generate a new prompt
31
+ tsikol generate prompt my_prompt topic:string
32
+ ```
33
+
34
+ ## Testing
35
+
36
+ ```bash
37
+ bundle exec rake test
38
+ ```
@@ -0,0 +1,49 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ # Ignore Byebug command history file.
17
+ .byebug_history
18
+
19
+ ## Specific to RubyMotion:
20
+ .dat*
21
+ .repl_history
22
+ build/
23
+ *.bridgesupport
24
+ build-iPhoneOS/
25
+ build-iPhoneSimulator/
26
+
27
+ ## Documentation cache and generated files:
28
+ /.yardoc/
29
+ /_yardoc/
30
+ /doc/
31
+ /rdoc/
32
+
33
+ ## Environment normalization:
34
+ /.bundle/
35
+ /vendor/bundle
36
+ /lib/bundler/man/
37
+
38
+ # MCP specific
39
+ /audit.log
40
+ /server_audit.log
41
+ /tsikol-debug.log
42
+
43
+ # IDE files
44
+ .idea/
45
+ .vscode/
46
+ *.swp
47
+ *.swo
48
+ *~
49
+ .DS_Store
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= @class_name %> < Tsikol::Prompt
4
+ name "<%= @prompt_name %>"
5
+ description "TODO: Add description for <%= @prompt_name %>"
6
+
7
+ <% @arguments.each do |arg| -%>
8
+ argument :<%= arg[:name] %> do
9
+ type :<%= arg[:type] %>
10
+ <% if arg[:required] -%>
11
+ required
12
+ <% else -%>
13
+ optional
14
+ <% end -%>
15
+ description "TODO: Add description for <%= arg[:name] %>"
16
+ <% if arg[:type] == 'string' -%>
17
+
18
+ # Add completion for better UX
19
+ # complete do |partial|
20
+ # # Return array of possible values
21
+ # suggestions = ["suggestion1", "suggestion2", "suggestion3"]
22
+ # suggestions.select { |s| s.downcase.include?(partial.downcase) }
23
+ # end
24
+ <% end -%>
25
+ end
26
+
27
+ <% end -%>
28
+ def get_messages(<%= @arguments.map { |a| "#{a[:name]}:#{a[:required] ? '' : ' nil'}" }.join(', ') %>)
29
+ # TODO: Implement <%= @prompt_name %> logic
30
+ log :info, "Getting messages for <%= @prompt_name %>" if respond_to?(:log)
31
+
32
+ [
33
+ {
34
+ role: "user",
35
+ content: {
36
+ type: "text",
37
+ text: "TODO: Generate prompt based on arguments"
38
+ }
39
+ }
40
+ ]
41
+ end
42
+
43
+ private
44
+
45
+ def set_server(server)
46
+ @server = server
47
+
48
+ # Enable logging
49
+ define_singleton_method(:log) do |level, message, data: nil, logger: nil|
50
+ @server.log(level, message, data: data, logger: logger)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= @class_name %> < Tsikol::Resource
4
+ uri "<%= @uri %>"
5
+ description "TODO: Add description for <%= @resource_name %>"
6
+
7
+ def read
8
+ # TODO: Implement <%= @resource_name %> logic
9
+ log :info, "Reading <%= @resource_name %>" if respond_to?(:log)
10
+
11
+ # Return the resource content
12
+ {
13
+ # Add your data here
14
+ example: "data",
15
+ timestamp: Time.now.iso8601
16
+ }.to_json
17
+ end
18
+
19
+ private
20
+
21
+ def set_server(server)
22
+ @server = server
23
+
24
+ # Enable logging
25
+ define_singleton_method(:log) do |level, message, data: nil, logger: nil|
26
+ @server.log(level, message, data: data, logger: logger)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "tsikol"
6
+
7
+ # Start <%= @project_name %> MCP server
8
+ Tsikol.start(name: "<%= @project_name %>") do
9
+ # Components will be added here by the CLI
10
+
11
+ # Example inline resource
12
+ resource "health" do
13
+ "Server is healthy"
14
+ end
15
+
16
+ # Enable capabilities as needed
17
+ # logging true
18
+ # completion true
19
+ # sampling true
20
+
21
+ # Add middleware as needed
22
+ # use Tsikol::LoggingMiddleware
23
+ # use Tsikol::RateLimitMiddleware, max_requests: 100, window: 60
24
+ end