trak_flow 0.1.3
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/.envrc +3 -0
- data/CHANGELOG.md +69 -0
- data/COMMITS.md +196 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +281 -0
- data/README.md +479 -0
- data/Rakefile +16 -0
- data/bin/tf +6 -0
- data/bin/tf_mcp +81 -0
- data/docs/.keep +0 -0
- data/docs/api/database.md +434 -0
- data/docs/api/ruby-library.md +349 -0
- data/docs/api/task-model.md +341 -0
- data/docs/assets/stylesheets/extra.css +53 -0
- data/docs/assets/trak_flow.jpg +0 -0
- data/docs/cli/admin-commands.md +369 -0
- data/docs/cli/dependency-commands.md +321 -0
- data/docs/cli/label-commands.md +222 -0
- data/docs/cli/overview.md +163 -0
- data/docs/cli/plan-commands.md +344 -0
- data/docs/cli/task-commands.md +333 -0
- data/docs/core-concepts/dependencies.md +232 -0
- data/docs/core-concepts/labels.md +217 -0
- data/docs/core-concepts/overview.md +178 -0
- data/docs/core-concepts/plans-workflows.md +264 -0
- data/docs/core-concepts/tasks.md +205 -0
- data/docs/getting-started/configuration.md +120 -0
- data/docs/getting-started/installation.md +79 -0
- data/docs/getting-started/quick-start.md +245 -0
- data/docs/index.md +169 -0
- data/docs/mcp/integration.md +302 -0
- data/docs/mcp/overview.md +206 -0
- data/docs/mcp/resources.md +284 -0
- data/docs/mcp/tools.md +457 -0
- data/examples/basic_usage.rb +365 -0
- data/examples/cli_demo.sh +314 -0
- data/examples/mcp/Gemfile +9 -0
- data/examples/mcp/Gemfile.lock +226 -0
- data/examples/mcp/http_demo.rb +232 -0
- data/examples/mcp/stdio_demo.rb +146 -0
- data/lib/trak_flow/cli/admin_commands.rb +136 -0
- data/lib/trak_flow/cli/config_commands.rb +260 -0
- data/lib/trak_flow/cli/dep_commands.rb +71 -0
- data/lib/trak_flow/cli/label_commands.rb +76 -0
- data/lib/trak_flow/cli/main_commands.rb +386 -0
- data/lib/trak_flow/cli/plan_commands.rb +185 -0
- data/lib/trak_flow/cli/workflow_commands.rb +133 -0
- data/lib/trak_flow/cli.rb +110 -0
- data/lib/trak_flow/config/defaults.yml +114 -0
- data/lib/trak_flow/config/section.rb +74 -0
- data/lib/trak_flow/config.rb +276 -0
- data/lib/trak_flow/graph/dependency_graph.rb +288 -0
- data/lib/trak_flow/id_generator.rb +52 -0
- data/lib/trak_flow/mcp/resources/base_resource.rb +25 -0
- data/lib/trak_flow/mcp/resources/dependency_graph.rb +31 -0
- data/lib/trak_flow/mcp/resources/label_list.rb +21 -0
- data/lib/trak_flow/mcp/resources/plan_by_id.rb +27 -0
- data/lib/trak_flow/mcp/resources/plan_list.rb +21 -0
- data/lib/trak_flow/mcp/resources/task_by_id.rb +31 -0
- data/lib/trak_flow/mcp/resources/task_list.rb +21 -0
- data/lib/trak_flow/mcp/resources/task_next.rb +30 -0
- data/lib/trak_flow/mcp/resources/workflow_by_id.rb +27 -0
- data/lib/trak_flow/mcp/resources/workflow_list.rb +21 -0
- data/lib/trak_flow/mcp/server.rb +140 -0
- data/lib/trak_flow/mcp/tools/base_tool.rb +29 -0
- data/lib/trak_flow/mcp/tools/comment_add.rb +33 -0
- data/lib/trak_flow/mcp/tools/dep_add.rb +34 -0
- data/lib/trak_flow/mcp/tools/dep_remove.rb +25 -0
- data/lib/trak_flow/mcp/tools/label_add.rb +28 -0
- data/lib/trak_flow/mcp/tools/label_remove.rb +25 -0
- data/lib/trak_flow/mcp/tools/plan_add_step.rb +35 -0
- data/lib/trak_flow/mcp/tools/plan_create.rb +33 -0
- data/lib/trak_flow/mcp/tools/plan_run.rb +58 -0
- data/lib/trak_flow/mcp/tools/plan_start.rb +58 -0
- data/lib/trak_flow/mcp/tools/task_block.rb +27 -0
- data/lib/trak_flow/mcp/tools/task_close.rb +26 -0
- data/lib/trak_flow/mcp/tools/task_create.rb +51 -0
- data/lib/trak_flow/mcp/tools/task_defer.rb +27 -0
- data/lib/trak_flow/mcp/tools/task_start.rb +25 -0
- data/lib/trak_flow/mcp/tools/task_update.rb +36 -0
- data/lib/trak_flow/mcp/tools/workflow_discard.rb +28 -0
- data/lib/trak_flow/mcp/tools/workflow_summarize.rb +34 -0
- data/lib/trak_flow/mcp.rb +38 -0
- data/lib/trak_flow/models/comment.rb +71 -0
- data/lib/trak_flow/models/dependency.rb +96 -0
- data/lib/trak_flow/models/label.rb +90 -0
- data/lib/trak_flow/models/task.rb +188 -0
- data/lib/trak_flow/storage/database.rb +638 -0
- data/lib/trak_flow/storage/jsonl.rb +259 -0
- data/lib/trak_flow/time_parser.rb +15 -0
- data/lib/trak_flow/version.rb +5 -0
- data/lib/trak_flow.rb +100 -0
- data/mkdocs.yml +143 -0
- metadata +392 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
trak_flow (0.1.0)
|
|
5
|
+
anyway_config (~> 2.0)
|
|
6
|
+
debug_me
|
|
7
|
+
fast-mcp
|
|
8
|
+
oj (~> 3.16)
|
|
9
|
+
pastel (~> 0.8)
|
|
10
|
+
puma (~> 6.0)
|
|
11
|
+
rackup (~> 2.0)
|
|
12
|
+
sequel (~> 5.0)
|
|
13
|
+
sqlite3 (~> 2.0)
|
|
14
|
+
thor (~> 1.3)
|
|
15
|
+
tty-spinner (~> 0.9)
|
|
16
|
+
tty-table (~> 0.12)
|
|
17
|
+
|
|
18
|
+
GEM
|
|
19
|
+
remote: https://rubygems.org/
|
|
20
|
+
specs:
|
|
21
|
+
addressable (2.8.8)
|
|
22
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
23
|
+
ansi (1.5.0)
|
|
24
|
+
anyway_config (2.7.2)
|
|
25
|
+
ruby-next-core (~> 1.0)
|
|
26
|
+
ast (2.4.3)
|
|
27
|
+
base64 (0.3.0)
|
|
28
|
+
bigdecimal (3.3.1)
|
|
29
|
+
builder (3.3.0)
|
|
30
|
+
concurrent-ruby (1.3.6)
|
|
31
|
+
debug_me (1.1.3)
|
|
32
|
+
docile (1.4.1)
|
|
33
|
+
dry-configurable (1.3.0)
|
|
34
|
+
dry-core (~> 1.1)
|
|
35
|
+
zeitwerk (~> 2.6)
|
|
36
|
+
dry-core (1.2.0)
|
|
37
|
+
concurrent-ruby (~> 1.0)
|
|
38
|
+
logger
|
|
39
|
+
zeitwerk (~> 2.6)
|
|
40
|
+
dry-inflector (1.2.0)
|
|
41
|
+
dry-initializer (3.2.0)
|
|
42
|
+
dry-logic (1.6.0)
|
|
43
|
+
bigdecimal
|
|
44
|
+
concurrent-ruby (~> 1.0)
|
|
45
|
+
dry-core (~> 1.1)
|
|
46
|
+
zeitwerk (~> 2.6)
|
|
47
|
+
dry-schema (1.14.1)
|
|
48
|
+
concurrent-ruby (~> 1.0)
|
|
49
|
+
dry-configurable (~> 1.0, >= 1.0.1)
|
|
50
|
+
dry-core (~> 1.1)
|
|
51
|
+
dry-initializer (~> 3.2)
|
|
52
|
+
dry-logic (~> 1.5)
|
|
53
|
+
dry-types (~> 1.8)
|
|
54
|
+
zeitwerk (~> 2.6)
|
|
55
|
+
dry-types (1.8.3)
|
|
56
|
+
bigdecimal (~> 3.0)
|
|
57
|
+
concurrent-ruby (~> 1.0)
|
|
58
|
+
dry-core (~> 1.0)
|
|
59
|
+
dry-inflector (~> 1.0)
|
|
60
|
+
dry-logic (~> 1.4)
|
|
61
|
+
zeitwerk (~> 2.6)
|
|
62
|
+
fast-mcp (1.6.0)
|
|
63
|
+
addressable (~> 2.8)
|
|
64
|
+
base64
|
|
65
|
+
dry-schema (~> 1.14)
|
|
66
|
+
json (~> 2.0)
|
|
67
|
+
mime-types (~> 3.4)
|
|
68
|
+
rack (>= 2.0, < 4.0)
|
|
69
|
+
json (2.18.0)
|
|
70
|
+
language_server-protocol (3.17.0.5)
|
|
71
|
+
lint_roller (1.1.0)
|
|
72
|
+
logger (1.7.0)
|
|
73
|
+
mime-types (3.7.0)
|
|
74
|
+
logger
|
|
75
|
+
mime-types-data (~> 3.2025, >= 3.2025.0507)
|
|
76
|
+
mime-types-data (3.2025.0924)
|
|
77
|
+
minitest (5.27.0)
|
|
78
|
+
minitest-reporters (1.7.1)
|
|
79
|
+
ansi
|
|
80
|
+
builder
|
|
81
|
+
minitest (>= 5.0)
|
|
82
|
+
ruby-progressbar
|
|
83
|
+
nio4r (2.7.5)
|
|
84
|
+
oj (3.16.13)
|
|
85
|
+
bigdecimal (>= 3.0)
|
|
86
|
+
ostruct (>= 0.2)
|
|
87
|
+
ostruct (0.6.3)
|
|
88
|
+
parallel (1.27.0)
|
|
89
|
+
parser (3.3.10.0)
|
|
90
|
+
ast (~> 2.4.1)
|
|
91
|
+
racc
|
|
92
|
+
pastel (0.8.0)
|
|
93
|
+
tty-color (~> 0.5)
|
|
94
|
+
prism (1.7.0)
|
|
95
|
+
public_suffix (7.0.0)
|
|
96
|
+
puma (6.6.1)
|
|
97
|
+
nio4r (~> 2.0)
|
|
98
|
+
racc (1.8.1)
|
|
99
|
+
rack (3.2.4)
|
|
100
|
+
rackup (2.3.1)
|
|
101
|
+
rack (>= 3)
|
|
102
|
+
rainbow (3.1.1)
|
|
103
|
+
rake (13.3.1)
|
|
104
|
+
regexp_parser (2.11.3)
|
|
105
|
+
rubocop (1.82.1)
|
|
106
|
+
json (~> 2.3)
|
|
107
|
+
language_server-protocol (~> 3.17.0.2)
|
|
108
|
+
lint_roller (~> 1.1.0)
|
|
109
|
+
parallel (~> 1.10)
|
|
110
|
+
parser (>= 3.3.0.2)
|
|
111
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
112
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
113
|
+
rubocop-ast (>= 1.48.0, < 2.0)
|
|
114
|
+
ruby-progressbar (~> 1.7)
|
|
115
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
116
|
+
rubocop-ast (1.49.0)
|
|
117
|
+
parser (>= 3.3.7.2)
|
|
118
|
+
prism (~> 1.7)
|
|
119
|
+
ruby-next-core (1.1.2)
|
|
120
|
+
ruby-progressbar (1.13.0)
|
|
121
|
+
sequel (5.100.0)
|
|
122
|
+
bigdecimal
|
|
123
|
+
simplecov (0.22.0)
|
|
124
|
+
docile (~> 1.1)
|
|
125
|
+
simplecov-html (~> 0.11)
|
|
126
|
+
simplecov_json_formatter (~> 0.1)
|
|
127
|
+
simplecov-html (0.13.2)
|
|
128
|
+
simplecov_json_formatter (0.1.4)
|
|
129
|
+
sqlite3 (2.9.0-arm64-darwin)
|
|
130
|
+
strings (0.2.1)
|
|
131
|
+
strings-ansi (~> 0.2)
|
|
132
|
+
unicode-display_width (>= 1.5, < 3.0)
|
|
133
|
+
unicode_utils (~> 1.4)
|
|
134
|
+
strings-ansi (0.2.0)
|
|
135
|
+
thor (1.4.0)
|
|
136
|
+
tty-color (0.6.0)
|
|
137
|
+
tty-cursor (0.7.1)
|
|
138
|
+
tty-screen (0.8.2)
|
|
139
|
+
tty-spinner (0.9.3)
|
|
140
|
+
tty-cursor (~> 0.7)
|
|
141
|
+
tty-table (0.12.0)
|
|
142
|
+
pastel (~> 0.8)
|
|
143
|
+
strings (~> 0.2.0)
|
|
144
|
+
tty-screen (~> 0.8)
|
|
145
|
+
unicode-display_width (2.6.0)
|
|
146
|
+
unicode_utils (1.4.0)
|
|
147
|
+
zeitwerk (2.7.4)
|
|
148
|
+
|
|
149
|
+
PLATFORMS
|
|
150
|
+
arm64-darwin-25
|
|
151
|
+
|
|
152
|
+
DEPENDENCIES
|
|
153
|
+
bundler
|
|
154
|
+
minitest (~> 5.0)
|
|
155
|
+
minitest-reporters (~> 1.6)
|
|
156
|
+
rake (~> 13.0)
|
|
157
|
+
rubocop (~> 1.0)
|
|
158
|
+
simplecov (~> 0.22)
|
|
159
|
+
trak_flow!
|
|
160
|
+
|
|
161
|
+
CHECKSUMS
|
|
162
|
+
addressable (2.8.8) sha256=7c13b8f9536cf6364c03b9d417c19986019e28f7c00ac8132da4eb0fe393b057
|
|
163
|
+
ansi (1.5.0)
|
|
164
|
+
anyway_config (2.7.2)
|
|
165
|
+
ast (2.4.3)
|
|
166
|
+
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
|
167
|
+
bigdecimal (3.3.1) sha256=eaa01e228be54c4f9f53bf3cc34fe3d5e845c31963e7fcc5bedb05a4e7d52218
|
|
168
|
+
builder (3.3.0)
|
|
169
|
+
concurrent-ruby (1.3.6)
|
|
170
|
+
debug_me (1.1.3)
|
|
171
|
+
docile (1.4.1)
|
|
172
|
+
dry-configurable (1.3.0)
|
|
173
|
+
dry-core (1.2.0)
|
|
174
|
+
dry-inflector (1.2.0)
|
|
175
|
+
dry-initializer (3.2.0)
|
|
176
|
+
dry-logic (1.6.0)
|
|
177
|
+
dry-schema (1.14.1)
|
|
178
|
+
dry-types (1.8.3)
|
|
179
|
+
fast-mcp (1.6.0)
|
|
180
|
+
json (2.18.0) sha256=b10506aee4183f5cf49e0efc48073d7b75843ce3782c68dbeb763351c08fd505
|
|
181
|
+
language_server-protocol (3.17.0.5)
|
|
182
|
+
lint_roller (1.1.0)
|
|
183
|
+
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
|
184
|
+
mime-types (3.7.0)
|
|
185
|
+
mime-types-data (3.2025.0924)
|
|
186
|
+
minitest (5.27.0)
|
|
187
|
+
minitest-reporters (1.7.1)
|
|
188
|
+
nio4r (2.7.5)
|
|
189
|
+
oj (3.16.13)
|
|
190
|
+
ostruct (0.6.3)
|
|
191
|
+
parallel (1.27.0)
|
|
192
|
+
parser (3.3.10.0)
|
|
193
|
+
pastel (0.8.0)
|
|
194
|
+
prism (1.7.0)
|
|
195
|
+
public_suffix (7.0.0) sha256=f7090b5beb0e56f9f10d79eed4d5fbe551b3b425da65877e075dad47a6a1b095
|
|
196
|
+
puma (6.6.1)
|
|
197
|
+
racc (1.8.1)
|
|
198
|
+
rack (3.2.4)
|
|
199
|
+
rackup (2.3.1)
|
|
200
|
+
rainbow (3.1.1)
|
|
201
|
+
rake (13.3.1)
|
|
202
|
+
regexp_parser (2.11.3)
|
|
203
|
+
rubocop (1.82.1)
|
|
204
|
+
rubocop-ast (1.49.0)
|
|
205
|
+
ruby-next-core (1.1.2)
|
|
206
|
+
ruby-progressbar (1.13.0)
|
|
207
|
+
sequel (5.100.0)
|
|
208
|
+
simplecov (0.22.0)
|
|
209
|
+
simplecov-html (0.13.2)
|
|
210
|
+
simplecov_json_formatter (0.1.4)
|
|
211
|
+
sqlite3 (2.9.0-arm64-darwin)
|
|
212
|
+
strings (0.2.1)
|
|
213
|
+
strings-ansi (0.2.0)
|
|
214
|
+
thor (1.4.0)
|
|
215
|
+
trak_flow (0.1.0)
|
|
216
|
+
tty-color (0.6.0)
|
|
217
|
+
tty-cursor (0.7.1)
|
|
218
|
+
tty-screen (0.8.2)
|
|
219
|
+
tty-spinner (0.9.3)
|
|
220
|
+
tty-table (0.12.0)
|
|
221
|
+
unicode-display_width (2.6.0)
|
|
222
|
+
unicode_utils (1.4.0)
|
|
223
|
+
zeitwerk (2.7.4) sha256=2bef90f356bdafe9a6c2bd32bcd804f83a4f9b8bc27f3600fff051eb3edcec8b
|
|
224
|
+
|
|
225
|
+
BUNDLED WITH
|
|
226
|
+
4.0.3
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Demo: TrakFlow MCP Server with HTTP Transport
|
|
5
|
+
#
|
|
6
|
+
# This example shows how to connect to the TrakFlow MCP server
|
|
7
|
+
# using HTTP/SSE transport with the ruby_llm and ruby_llm-mcp gems.
|
|
8
|
+
#
|
|
9
|
+
# The demo demonstrates:
|
|
10
|
+
# - Connecting via HTTP/SSE transport
|
|
11
|
+
# - Listing available tools and resources
|
|
12
|
+
# - Creating a task via MCP tool
|
|
13
|
+
# - Starting and closing the task (showing state changes)
|
|
14
|
+
# - Optionally using an LLM with MCP tools
|
|
15
|
+
#
|
|
16
|
+
# Prerequisites:
|
|
17
|
+
# - Ollama running: ollama serve
|
|
18
|
+
# - Model available: ollama pull gpt-oss (or set OLLAMA_MODEL env var)
|
|
19
|
+
#
|
|
20
|
+
# The demo will automatically start the MCP server if not running.
|
|
21
|
+
#
|
|
22
|
+
# Usage:
|
|
23
|
+
# cd examples/mcp
|
|
24
|
+
# bundle install
|
|
25
|
+
# ruby http_demo.rb
|
|
26
|
+
|
|
27
|
+
require "bundler/setup"
|
|
28
|
+
require "ruby_llm"
|
|
29
|
+
require "ruby_llm/mcp"
|
|
30
|
+
require "fileutils"
|
|
31
|
+
require "socket"
|
|
32
|
+
require "tmpdir"
|
|
33
|
+
|
|
34
|
+
# Configure RubyLLM to use Ollama
|
|
35
|
+
RubyLLM.configure do |config|
|
|
36
|
+
config.ollama_api_base = ENV["OLLAMA_API_BASE"] || ENV["OLLAMA_URL"] || "http://localhost:11434/v1"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Model configuration
|
|
40
|
+
OLLAMA_MODEL = ENV.fetch("OLLAMA_MODEL", "gpt-oss:latest")
|
|
41
|
+
|
|
42
|
+
# MCP server HTTP endpoint (default port from TrakFlow config)
|
|
43
|
+
MCP_PORT = ENV.fetch("MCP_PORT", "3333").to_i
|
|
44
|
+
MCP_HTTP_URL = ENV.fetch("MCP_URL", "http://localhost:#{MCP_PORT}")
|
|
45
|
+
|
|
46
|
+
# Path to the TrakFlow MCP server executable and project root
|
|
47
|
+
PROJECT_ROOT = File.expand_path("../..", __dir__)
|
|
48
|
+
TF_MCP_PATH = File.join(PROJECT_ROOT, "bin/tf_mcp")
|
|
49
|
+
TF_CLI_PATH = File.join(PROJECT_ROOT, "bin/tf")
|
|
50
|
+
GEMFILE_PATH = File.join(PROJECT_ROOT, "Gemfile")
|
|
51
|
+
|
|
52
|
+
def port_open?(port, host = "127.0.0.1")
|
|
53
|
+
Socket.tcp(host, port, connect_timeout: 1) { true }
|
|
54
|
+
rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT, SocketError
|
|
55
|
+
false
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def wait_for_server(port, timeout: 10)
|
|
59
|
+
start = Time.now
|
|
60
|
+
until port_open?(port)
|
|
61
|
+
return false if Time.now - start > timeout
|
|
62
|
+
sleep 0.2
|
|
63
|
+
end
|
|
64
|
+
true
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
puts "TrakFlow MCP HTTP Demo"
|
|
68
|
+
puts "=" * 40
|
|
69
|
+
puts
|
|
70
|
+
|
|
71
|
+
# Check if server is running, start it if not
|
|
72
|
+
server_pid = nil
|
|
73
|
+
work_dir = nil
|
|
74
|
+
|
|
75
|
+
unless port_open?(MCP_PORT)
|
|
76
|
+
puts "MCP server not running on port #{MCP_PORT}. Starting it..."
|
|
77
|
+
|
|
78
|
+
# Create a temp directory and initialize TrakFlow there
|
|
79
|
+
work_dir = Dir.mktmpdir("trakflow_mcp_http_demo")
|
|
80
|
+
puts "Working directory: #{work_dir}"
|
|
81
|
+
|
|
82
|
+
# Initialize TrakFlow in the temp directory
|
|
83
|
+
Dir.chdir(work_dir) do
|
|
84
|
+
system({ "BUNDLE_GEMFILE" => GEMFILE_PATH }, "bundle", "exec", TF_CLI_PATH, "init", out: File::NULL, err: File::NULL)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Start the MCP server in the background
|
|
88
|
+
server_pid = spawn(
|
|
89
|
+
{ "BUNDLE_GEMFILE" => GEMFILE_PATH },
|
|
90
|
+
"bundle", "exec", TF_MCP_PATH, "--http", "--port", MCP_PORT.to_s,
|
|
91
|
+
chdir: work_dir,
|
|
92
|
+
out: File::NULL,
|
|
93
|
+
err: File::NULL
|
|
94
|
+
)
|
|
95
|
+
Process.detach(server_pid)
|
|
96
|
+
|
|
97
|
+
puts "Started MCP server (PID: #{server_pid})"
|
|
98
|
+
|
|
99
|
+
unless wait_for_server(MCP_PORT)
|
|
100
|
+
puts "ERROR: Server failed to start within timeout"
|
|
101
|
+
Process.kill("TERM", server_pid) rescue nil
|
|
102
|
+
FileUtils.rm_rf(work_dir) if work_dir
|
|
103
|
+
exit 1
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
puts "MCP server is ready."
|
|
107
|
+
else
|
|
108
|
+
puts "MCP server already running on port #{MCP_PORT}"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
puts
|
|
112
|
+
puts "Connecting to: #{MCP_HTTP_URL}"
|
|
113
|
+
puts
|
|
114
|
+
|
|
115
|
+
# Create MCP client with SSE transport (HTTP-based)
|
|
116
|
+
client = RubyLLM::MCP.client(
|
|
117
|
+
name: "trakflow",
|
|
118
|
+
transport_type: :sse,
|
|
119
|
+
config: {
|
|
120
|
+
url: "#{MCP_HTTP_URL}/mcp/sse"
|
|
121
|
+
}
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
begin
|
|
125
|
+
# The client connects automatically on creation
|
|
126
|
+
puts "Connected to TrakFlow MCP server via HTTP/SSE..."
|
|
127
|
+
puts "Connection alive: #{client.alive?}"
|
|
128
|
+
|
|
129
|
+
# List available tools
|
|
130
|
+
puts "\nAvailable Tools:"
|
|
131
|
+
puts "-" * 40
|
|
132
|
+
client.tools.each do |tool|
|
|
133
|
+
puts " - #{tool.name}: #{tool.description}"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# List available resources
|
|
137
|
+
puts "\nAvailable Resources:"
|
|
138
|
+
puts "-" * 40
|
|
139
|
+
client.resources.each do |resource|
|
|
140
|
+
puts " - #{resource.uri}: #{resource.name}"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Example: Create a task using the MCP tool
|
|
144
|
+
puts "\n" + "=" * 40
|
|
145
|
+
puts "Creating a test task..."
|
|
146
|
+
puts "-" * 40
|
|
147
|
+
|
|
148
|
+
tool = client.tool("task_create")
|
|
149
|
+
result = tool.execute(
|
|
150
|
+
title: "Demo task from HTTP",
|
|
151
|
+
description: "Created via MCP HTTP transport demo"
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
puts "Result: #{result}"
|
|
155
|
+
|
|
156
|
+
# Example: Create a plan
|
|
157
|
+
puts "\n" + "=" * 40
|
|
158
|
+
puts "Creating a plan..."
|
|
159
|
+
puts "-" * 40
|
|
160
|
+
|
|
161
|
+
plan_tool = client.tool("plan_create")
|
|
162
|
+
plan_result = plan_tool.execute(
|
|
163
|
+
title: "Example workflow plan",
|
|
164
|
+
description: "A demo plan with steps"
|
|
165
|
+
)
|
|
166
|
+
puts "Plan created: #{plan_result}"
|
|
167
|
+
|
|
168
|
+
# Extract the task ID from the creation result
|
|
169
|
+
# result may be a String or object with .to_s
|
|
170
|
+
result_str = result.to_s
|
|
171
|
+
task_id = result_str.match(/id: "([^"]+)"/)[1] rescue nil
|
|
172
|
+
|
|
173
|
+
# Example: Start the task to demonstrate a second tool call
|
|
174
|
+
if task_id
|
|
175
|
+
puts "\n" + "=" * 40
|
|
176
|
+
puts "Starting the task via MCP tool..."
|
|
177
|
+
puts "-" * 40
|
|
178
|
+
|
|
179
|
+
start_tool = client.tool("task_start")
|
|
180
|
+
start_result = start_tool.execute(id: task_id)
|
|
181
|
+
puts "Task started: #{start_result}"
|
|
182
|
+
|
|
183
|
+
# Close the task
|
|
184
|
+
puts "\n" + "=" * 40
|
|
185
|
+
puts "Closing the task via MCP tool..."
|
|
186
|
+
puts "-" * 40
|
|
187
|
+
|
|
188
|
+
close_tool = client.tool("task_close")
|
|
189
|
+
close_result = close_tool.execute(id: task_id)
|
|
190
|
+
puts "Task closed: #{close_result}"
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Example: Use with RubyLLM chat (optional - requires Ollama)
|
|
194
|
+
if ENV["SKIP_LLM"] != "1"
|
|
195
|
+
puts "\n" + "=" * 40
|
|
196
|
+
puts "Sending prompt to LLM with MCP tools..."
|
|
197
|
+
puts "-" * 40
|
|
198
|
+
|
|
199
|
+
chat = RubyLLM.chat(model: OLLAMA_MODEL, provider: :ollama, assume_model_exists: true)
|
|
200
|
+
chat.with_tools(*client.tools)
|
|
201
|
+
response = chat.ask("Use the available tools to list all tasks and summarize what you find.")
|
|
202
|
+
|
|
203
|
+
puts "\nLLM Response:"
|
|
204
|
+
puts response.content
|
|
205
|
+
else
|
|
206
|
+
puts "\n" + "=" * 40
|
|
207
|
+
puts "Skipping LLM test (SKIP_LLM=1)"
|
|
208
|
+
puts "-" * 40
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
rescue Errno::ECONNREFUSED
|
|
212
|
+
puts "Error: Could not connect to MCP server at #{MCP_HTTP_URL}"
|
|
213
|
+
rescue StandardError => e
|
|
214
|
+
puts "Error: #{e.message}"
|
|
215
|
+
puts e.backtrace.first(5).join("\n")
|
|
216
|
+
ensure
|
|
217
|
+
client.cleanup if client.respond_to?(:cleanup)
|
|
218
|
+
|
|
219
|
+
# Stop server if we started it
|
|
220
|
+
if server_pid
|
|
221
|
+
Process.kill("TERM", server_pid) rescue nil
|
|
222
|
+
puts "Stopped MCP server (PID: #{server_pid})"
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Clean up temp directory if we created it
|
|
226
|
+
if work_dir && File.exist?(work_dir)
|
|
227
|
+
FileUtils.rm_rf(work_dir)
|
|
228
|
+
puts "Cleaned up temp directory."
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
puts "\nCleaned up MCP client."
|
|
232
|
+
end
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Demo: TrakFlow MCP Server with STDIO Transport
|
|
5
|
+
#
|
|
6
|
+
# This example shows how to connect to the TrakFlow MCP server
|
|
7
|
+
# using STDIO transport with the ruby_llm and ruby_llm-mcp gems.
|
|
8
|
+
#
|
|
9
|
+
# Prerequisites:
|
|
10
|
+
# - Ollama running: ollama serve
|
|
11
|
+
# - Model available: ollama pull gpt-oss (or set OLLAMA_MODEL env var)
|
|
12
|
+
#
|
|
13
|
+
# Usage:
|
|
14
|
+
# cd examples/mcp
|
|
15
|
+
# bundle install
|
|
16
|
+
# ruby stdio_demo.rb
|
|
17
|
+
|
|
18
|
+
require "bundler/setup"
|
|
19
|
+
require "ruby_llm"
|
|
20
|
+
require "ruby_llm/mcp"
|
|
21
|
+
require "tmpdir"
|
|
22
|
+
require "fileutils"
|
|
23
|
+
|
|
24
|
+
# Configure RubyLLM to use Ollama
|
|
25
|
+
RubyLLM.configure do |config|
|
|
26
|
+
config.ollama_api_base = ENV["OLLAMA_API_BASE"] || ENV["OLLAMA_URL"] || "http://localhost:11434/v1"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Model configuration
|
|
30
|
+
OLLAMA_MODEL = ENV.fetch("OLLAMA_MODEL", "gpt-oss:latest")
|
|
31
|
+
|
|
32
|
+
# Path to the TrakFlow MCP server executable and project root
|
|
33
|
+
PROJECT_ROOT = File.expand_path("../..", __dir__)
|
|
34
|
+
TF_MCP_PATH = File.join(PROJECT_ROOT, "bin/tf_mcp")
|
|
35
|
+
TF_CLI_PATH = File.join(PROJECT_ROOT, "bin/tf")
|
|
36
|
+
GEMFILE_PATH = File.join(PROJECT_ROOT, "Gemfile")
|
|
37
|
+
|
|
38
|
+
puts "TrakFlow MCP STDIO Demo"
|
|
39
|
+
puts "=" * 40
|
|
40
|
+
puts
|
|
41
|
+
|
|
42
|
+
# Create a temp directory and initialize TrakFlow there
|
|
43
|
+
WORK_DIR = Dir.mktmpdir("trakflow_mcp_demo")
|
|
44
|
+
puts "Working directory: #{WORK_DIR}"
|
|
45
|
+
|
|
46
|
+
# Initialize TrakFlow in the temp directory
|
|
47
|
+
Dir.chdir(WORK_DIR) do
|
|
48
|
+
system({ "BUNDLE_GEMFILE" => GEMFILE_PATH }, "bundle", "exec", TF_CLI_PATH, "init", out: File::NULL, err: File::NULL)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
puts "TrakFlow initialized."
|
|
52
|
+
puts
|
|
53
|
+
|
|
54
|
+
# Create MCP client with STDIO transport
|
|
55
|
+
# Use a shell wrapper to cd to work dir and run with proper bundle
|
|
56
|
+
client = RubyLLM::MCP.client(
|
|
57
|
+
name: "trakflow",
|
|
58
|
+
transport_type: :stdio,
|
|
59
|
+
config: {
|
|
60
|
+
command: "/bin/sh",
|
|
61
|
+
args: ["-c", "cd #{WORK_DIR} && BUNDLE_GEMFILE=#{GEMFILE_PATH} bundle exec #{TF_MCP_PATH}"]
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
begin
|
|
66
|
+
# The client connects automatically on creation
|
|
67
|
+
puts "Connected to TrakFlow MCP server via STDIO..."
|
|
68
|
+
puts "Connection alive: #{client.alive?}"
|
|
69
|
+
|
|
70
|
+
# List available tools
|
|
71
|
+
puts "\nAvailable Tools:"
|
|
72
|
+
puts "-" * 40
|
|
73
|
+
client.tools.each do |tool|
|
|
74
|
+
puts " - #{tool.name}: #{tool.description}"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# List available resources
|
|
78
|
+
puts "\nAvailable Resources:"
|
|
79
|
+
puts "-" * 40
|
|
80
|
+
client.resources.each do |resource|
|
|
81
|
+
puts " - #{resource.uri}: #{resource.name}"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Example: Create a task using the MCP tool
|
|
85
|
+
puts "\n" + "=" * 40
|
|
86
|
+
puts "Creating a test task..."
|
|
87
|
+
puts "-" * 40
|
|
88
|
+
|
|
89
|
+
tool = client.tool("task_create")
|
|
90
|
+
result = tool.execute(
|
|
91
|
+
title: "Demo task from STDIO",
|
|
92
|
+
description: "Created via MCP STDIO transport demo"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
puts "Result: #{result}"
|
|
96
|
+
|
|
97
|
+
# Extract the task ID from the creation result
|
|
98
|
+
result_str = result.to_s
|
|
99
|
+
task_id = result_str.match(/id: "([^"]+)"/)[1] rescue nil
|
|
100
|
+
|
|
101
|
+
# Example: Start the task to demonstrate a second tool call
|
|
102
|
+
if task_id
|
|
103
|
+
puts "\n" + "=" * 40
|
|
104
|
+
puts "Starting the task via MCP tool..."
|
|
105
|
+
puts "-" * 40
|
|
106
|
+
|
|
107
|
+
start_tool = client.tool("task_start")
|
|
108
|
+
start_result = start_tool.execute(id: task_id)
|
|
109
|
+
puts "Task started: #{start_result}"
|
|
110
|
+
|
|
111
|
+
# Close the task
|
|
112
|
+
puts "\n" + "=" * 40
|
|
113
|
+
puts "Closing the task via MCP tool..."
|
|
114
|
+
puts "-" * 40
|
|
115
|
+
|
|
116
|
+
close_tool = client.tool("task_close")
|
|
117
|
+
close_result = close_tool.execute(id: task_id)
|
|
118
|
+
puts "Task closed: #{close_result}"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Example: Use with RubyLLM chat (optional - requires Ollama)
|
|
122
|
+
if ENV["SKIP_LLM"] != "1"
|
|
123
|
+
puts "\n" + "=" * 40
|
|
124
|
+
puts "Sending prompt to LLM with MCP tools..."
|
|
125
|
+
puts "-" * 40
|
|
126
|
+
|
|
127
|
+
chat = RubyLLM.chat(model: OLLAMA_MODEL, provider: :ollama, assume_model_exists: true)
|
|
128
|
+
chat.with_tools(*client.tools)
|
|
129
|
+
response = chat.ask("Use the available tools to list all tasks and summarize what you find.")
|
|
130
|
+
|
|
131
|
+
puts "\nLLM Response:"
|
|
132
|
+
puts response.content
|
|
133
|
+
else
|
|
134
|
+
puts "\n" + "=" * 40
|
|
135
|
+
puts "Skipping LLM test (SKIP_LLM=1)"
|
|
136
|
+
puts "-" * 40
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
rescue StandardError => e
|
|
140
|
+
puts "Error: #{e.message}"
|
|
141
|
+
puts e.backtrace.first(5).join("\n")
|
|
142
|
+
ensure
|
|
143
|
+
client.cleanup if client.respond_to?(:cleanup)
|
|
144
|
+
FileUtils.rm_rf(WORK_DIR) if defined?(WORK_DIR) && File.exist?(WORK_DIR)
|
|
145
|
+
puts "\nCleaned up MCP client and temp directory."
|
|
146
|
+
end
|