anima-core 0.2.1 → 1.0.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 (280) hide show
  1. checksums.yaml +4 -4
  2. data/.reek.yml +27 -1
  3. data/CHANGELOG.md +19 -0
  4. data/README.md +213 -43
  5. data/agents/codebase-analyzer.md +88 -0
  6. data/agents/codebase-pattern-finder.md +83 -0
  7. data/agents/documentation-researcher.md +59 -0
  8. data/agents/thoughts-analyzer.md +102 -0
  9. data/agents/web-search-researcher.md +71 -0
  10. data/anima-core.gemspec +3 -0
  11. data/app/channels/session_channel.rb +195 -45
  12. data/app/decorators/user_message_decorator.rb +16 -5
  13. data/app/jobs/agent_request_job.rb +55 -2
  14. data/app/jobs/analytical_brain_job.rb +33 -0
  15. data/app/jobs/count_event_tokens_job.rb +15 -4
  16. data/app/models/concerns/event/broadcasting.rb +81 -0
  17. data/app/models/event.rb +20 -1
  18. data/app/models/goal.rb +91 -0
  19. data/app/models/session.rb +366 -21
  20. data/config/application.rb +2 -0
  21. data/config/initializers/event_subscribers.rb +0 -1
  22. data/config/routes.rb +0 -6
  23. data/db/migrate/20260313010000_add_status_to_events.rb +8 -0
  24. data/db/migrate/20260313020000_add_processing_to_sessions.rb +7 -0
  25. data/db/migrate/20260314075248_add_subagent_support_to_sessions.rb +6 -0
  26. data/db/migrate/20260314112417_add_granted_tools_to_sessions.rb +5 -0
  27. data/db/migrate/20260314140000_add_name_to_sessions.rb +7 -0
  28. data/db/migrate/20260314150000_add_viewport_event_ids_to_sessions.rb +7 -0
  29. data/db/migrate/20260315100000_add_active_skills_to_sessions.rb +7 -0
  30. data/db/migrate/20260315140843_create_goals.rb +16 -0
  31. data/db/migrate/20260315144837_add_completed_at_to_goals.rb +5 -0
  32. data/db/migrate/20260315191105_add_active_workflow_to_sessions.rb +5 -0
  33. data/lib/agent_loop.rb +65 -6
  34. data/lib/agents/definition.rb +116 -0
  35. data/lib/agents/registry.rb +106 -0
  36. data/lib/analytical_brain/runner.rb +276 -0
  37. data/lib/analytical_brain/tools/activate_skill.rb +52 -0
  38. data/lib/analytical_brain/tools/deactivate_skill.rb +43 -0
  39. data/lib/analytical_brain/tools/deactivate_workflow.rb +34 -0
  40. data/lib/analytical_brain/tools/everything_is_ready.rb +28 -0
  41. data/lib/analytical_brain/tools/finish_goal.rb +62 -0
  42. data/lib/analytical_brain/tools/read_workflow.rb +58 -0
  43. data/lib/analytical_brain/tools/rename_session.rb +63 -0
  44. data/lib/analytical_brain/tools/set_goal.rb +60 -0
  45. data/lib/analytical_brain/tools/update_goal.rb +60 -0
  46. data/lib/analytical_brain.rb +23 -0
  47. data/lib/anima/cli/mcp/secrets.rb +76 -0
  48. data/lib/anima/cli/mcp.rb +197 -0
  49. data/lib/anima/cli.rb +5 -40
  50. data/lib/anima/installer.rb +168 -0
  51. data/lib/anima/settings.rb +226 -0
  52. data/lib/anima/version.rb +1 -1
  53. data/lib/anima.rb +9 -0
  54. data/lib/credential_store.rb +103 -0
  55. data/lib/environment_probe.rb +232 -0
  56. data/lib/events/subscribers/persister.rb +1 -0
  57. data/lib/events/user_message.rb +17 -0
  58. data/lib/llm/client.rb +29 -10
  59. data/lib/mcp/client_manager.rb +86 -0
  60. data/lib/mcp/config.rb +213 -0
  61. data/lib/mcp/health_check.rb +77 -0
  62. data/lib/mcp/secrets.rb +73 -0
  63. data/lib/mcp/stdio_transport.rb +206 -0
  64. data/lib/providers/anthropic.rb +11 -20
  65. data/lib/shell_session.rb +11 -10
  66. data/lib/skills/definition.rb +97 -0
  67. data/lib/skills/registry.rb +105 -0
  68. data/lib/tools/edit.rb +226 -0
  69. data/lib/tools/mcp_tool.rb +114 -0
  70. data/lib/tools/read.rb +151 -0
  71. data/lib/tools/registry.rb +14 -12
  72. data/lib/tools/request_feature.rb +121 -0
  73. data/lib/tools/return_result.rb +81 -0
  74. data/lib/tools/spawn_specialist.rb +109 -0
  75. data/lib/tools/spawn_subagent.rb +111 -0
  76. data/lib/tools/subagent_prompts.rb +12 -0
  77. data/lib/tools/web_get.rb +8 -9
  78. data/lib/tools/write.rb +86 -0
  79. data/lib/tui/app.rb +985 -26
  80. data/lib/tui/cable_client.rb +69 -31
  81. data/lib/tui/message_store.rb +103 -8
  82. data/lib/tui/screens/chat.rb +293 -45
  83. data/lib/workflows/definition.rb +97 -0
  84. data/lib/workflows/registry.rb +89 -0
  85. data/skills/activerecord/SKILL.md +255 -0
  86. data/skills/activerecord/examples/associations/association_extensions.rb +298 -0
  87. data/skills/activerecord/examples/associations/basic_associations.rb +118 -0
  88. data/skills/activerecord/examples/associations/counter_caches.rb +215 -0
  89. data/skills/activerecord/examples/associations/polymorphic_associations.rb +217 -0
  90. data/skills/activerecord/examples/associations/self_referential.rb +302 -0
  91. data/skills/activerecord/examples/associations/through_associations.rb +203 -0
  92. data/skills/activerecord/examples/basics/crud_operations.rb +209 -0
  93. data/skills/activerecord/examples/basics/dirty_tracking.rb +218 -0
  94. data/skills/activerecord/examples/basics/inheritance.rb +377 -0
  95. data/skills/activerecord/examples/basics/type_casting.rb +317 -0
  96. data/skills/activerecord/examples/callbacks/alternatives_to_callbacks.rb +447 -0
  97. data/skills/activerecord/examples/callbacks/conditional_callbacks.rb +353 -0
  98. data/skills/activerecord/examples/callbacks/lifecycle_callbacks.rb +280 -0
  99. data/skills/activerecord/examples/callbacks/transaction_callbacks.rb +340 -0
  100. data/skills/activerecord/examples/migrations/indexes_and_constraints.rb +337 -0
  101. data/skills/activerecord/examples/migrations/reversible_patterns.rb +403 -0
  102. data/skills/activerecord/examples/migrations/safe_patterns.rb +420 -0
  103. data/skills/activerecord/examples/migrations/schema_changes.rb +277 -0
  104. data/skills/activerecord/examples/querying/batch_processing.rb +226 -0
  105. data/skills/activerecord/examples/querying/eager_loading.rb +259 -0
  106. data/skills/activerecord/examples/querying/finder_methods.rb +170 -0
  107. data/skills/activerecord/examples/querying/optimization.rb +275 -0
  108. data/skills/activerecord/examples/querying/scopes.rb +260 -0
  109. data/skills/activerecord/examples/validations/built_in_validators.rb +277 -0
  110. data/skills/activerecord/examples/validations/conditional_validations.rb +288 -0
  111. data/skills/activerecord/examples/validations/custom_validators.rb +381 -0
  112. data/skills/activerecord/examples/validations/database_constraints.rb +432 -0
  113. data/skills/activerecord/examples/validations/validation_contexts.rb +367 -0
  114. data/skills/activerecord/references/associations.md +709 -0
  115. data/skills/activerecord/references/basics.md +622 -0
  116. data/skills/activerecord/references/callbacks.md +738 -0
  117. data/skills/activerecord/references/migrations.md +657 -0
  118. data/skills/activerecord/references/querying.md +655 -0
  119. data/skills/activerecord/references/validations.md +596 -0
  120. data/skills/dragonruby/SKILL.md +250 -0
  121. data/skills/dragonruby/examples/audio/audio_events.rb +55 -0
  122. data/skills/dragonruby/examples/audio/background_music.rb +29 -0
  123. data/skills/dragonruby/examples/audio/crossfade.rb +51 -0
  124. data/skills/dragonruby/examples/audio/music_controls.rb +51 -0
  125. data/skills/dragonruby/examples/audio/sound_effects.rb +30 -0
  126. data/skills/dragonruby/examples/core/coordinate_system.rb +27 -0
  127. data/skills/dragonruby/examples/core/hello_world.rb +24 -0
  128. data/skills/dragonruby/examples/core/labels.rb +22 -0
  129. data/skills/dragonruby/examples/core/sprites.rb +35 -0
  130. data/skills/dragonruby/examples/core/state_management.rb +29 -0
  131. data/skills/dragonruby/examples/distribution/background_pause.rb +42 -0
  132. data/skills/dragonruby/examples/distribution/build_workflow.sh +26 -0
  133. data/skills/dragonruby/examples/distribution/cvars_production.txt +16 -0
  134. data/skills/dragonruby/examples/distribution/game_metadata_hd.txt +23 -0
  135. data/skills/dragonruby/examples/distribution/game_metadata_minimal.txt +9 -0
  136. data/skills/dragonruby/examples/distribution/game_metadata_mobile.txt +31 -0
  137. data/skills/dragonruby/examples/distribution/platform_detection.rb +36 -0
  138. data/skills/dragonruby/examples/distribution/steam_metadata.txt +19 -0
  139. data/skills/dragonruby/examples/entities/collision_detection.rb +43 -0
  140. data/skills/dragonruby/examples/entities/entity_lifecycle.rb +68 -0
  141. data/skills/dragonruby/examples/entities/entity_storage.rb +38 -0
  142. data/skills/dragonruby/examples/entities/factory_methods.rb +45 -0
  143. data/skills/dragonruby/examples/entities/random_spawning.rb +50 -0
  144. data/skills/dragonruby/examples/game-logic/reset_patterns.rb +98 -0
  145. data/skills/dragonruby/examples/game-logic/save_load.rb +101 -0
  146. data/skills/dragonruby/examples/game-logic/scoring.rb +104 -0
  147. data/skills/dragonruby/examples/game-logic/state_transitions.rb +103 -0
  148. data/skills/dragonruby/examples/game-logic/timers.rb +87 -0
  149. data/skills/dragonruby/examples/input/action_triggers.rb +36 -0
  150. data/skills/dragonruby/examples/input/analog_movement.rb +28 -0
  151. data/skills/dragonruby/examples/input/controller_input.rb +28 -0
  152. data/skills/dragonruby/examples/input/directional_input.rb +24 -0
  153. data/skills/dragonruby/examples/input/keyboard_input.rb +28 -0
  154. data/skills/dragonruby/examples/input/mouse_click.rb +26 -0
  155. data/skills/dragonruby/examples/input/movement_with_bounds.rb +22 -0
  156. data/skills/dragonruby/examples/input/normalized_movement.rb +32 -0
  157. data/skills/dragonruby/examples/rendering/frame_animation.rb +32 -0
  158. data/skills/dragonruby/examples/rendering/labels.rb +32 -0
  159. data/skills/dragonruby/examples/rendering/layering.rb +51 -0
  160. data/skills/dragonruby/examples/rendering/solids.rb +61 -0
  161. data/skills/dragonruby/examples/rendering/sprites.rb +33 -0
  162. data/skills/dragonruby/examples/rendering/spritesheet_animation.rb +39 -0
  163. data/skills/dragonruby/examples/scenes/case_dispatch.rb +60 -0
  164. data/skills/dragonruby/examples/scenes/class_based.rb +150 -0
  165. data/skills/dragonruby/examples/scenes/pause_overlay.rb +100 -0
  166. data/skills/dragonruby/examples/scenes/safe_transitions.rb +68 -0
  167. data/skills/dragonruby/examples/scenes/scene_transitions.rb +98 -0
  168. data/skills/dragonruby/examples/scenes/send_dispatch.rb +88 -0
  169. data/skills/dragonruby/references/audio.md +396 -0
  170. data/skills/dragonruby/references/core.md +385 -0
  171. data/skills/dragonruby/references/distribution.md +434 -0
  172. data/skills/dragonruby/references/entities.md +516 -0
  173. data/skills/dragonruby/references/game-logic/persistence.md +386 -0
  174. data/skills/dragonruby/references/game-logic/state.md +389 -0
  175. data/skills/dragonruby/references/input.md +414 -0
  176. data/skills/dragonruby/references/rendering/animation.md +467 -0
  177. data/skills/dragonruby/references/rendering/primitives.md +403 -0
  178. data/skills/dragonruby/references/scenes.md +443 -0
  179. data/skills/draper-decorators/SKILL.md +344 -0
  180. data/skills/draper-decorators/examples/application_decorator.rb +61 -0
  181. data/skills/draper-decorators/examples/decorator_spec.rb +253 -0
  182. data/skills/draper-decorators/examples/model_decorator.rb +152 -0
  183. data/skills/draper-decorators/references/anti-patterns.md +640 -0
  184. data/skills/draper-decorators/references/patterns.md +507 -0
  185. data/skills/draper-decorators/references/testing.md +559 -0
  186. data/skills/gh-issue.md +182 -0
  187. data/skills/mcp-server/SKILL.md +177 -0
  188. data/skills/mcp-server/examples/dynamic_tools.rb +36 -0
  189. data/skills/mcp-server/examples/file_manager_tool.rb +85 -0
  190. data/skills/mcp-server/examples/http_client.rb +48 -0
  191. data/skills/mcp-server/examples/http_server.rb +97 -0
  192. data/skills/mcp-server/examples/rails_integration.rb +88 -0
  193. data/skills/mcp-server/examples/stdio_server.rb +108 -0
  194. data/skills/mcp-server/examples/streaming_client.rb +95 -0
  195. data/skills/mcp-server/references/gotchas.md +183 -0
  196. data/skills/mcp-server/references/prompts.md +98 -0
  197. data/skills/mcp-server/references/resources.md +53 -0
  198. data/skills/mcp-server/references/server.md +140 -0
  199. data/skills/mcp-server/references/tools.md +146 -0
  200. data/skills/mcp-server/references/transport.md +104 -0
  201. data/skills/ratatui-ruby/SKILL.md +315 -0
  202. data/skills/ratatui-ruby/references/core-concepts.md +340 -0
  203. data/skills/ratatui-ruby/references/events.md +387 -0
  204. data/skills/ratatui-ruby/references/frameworks.md +522 -0
  205. data/skills/ratatui-ruby/references/layout.md +423 -0
  206. data/skills/ratatui-ruby/references/styling.md +268 -0
  207. data/skills/ratatui-ruby/references/testing.md +433 -0
  208. data/skills/ratatui-ruby/references/widgets.md +532 -0
  209. data/skills/rspec/SKILL.md +340 -0
  210. data/skills/rspec/examples/core/basic_structure.rb +69 -0
  211. data/skills/rspec/examples/core/configuration.rb +126 -0
  212. data/skills/rspec/examples/core/hooks.rb +126 -0
  213. data/skills/rspec/examples/core/memoized_helpers.rb +139 -0
  214. data/skills/rspec/examples/core/metadata_filtering.rb +144 -0
  215. data/skills/rspec/examples/core/shared_examples.rb +145 -0
  216. data/skills/rspec/examples/factory_bot/associations.rb +314 -0
  217. data/skills/rspec/examples/factory_bot/build_strategies.rb +272 -0
  218. data/skills/rspec/examples/factory_bot/callbacks.rb +320 -0
  219. data/skills/rspec/examples/factory_bot/custom_construction.rb +328 -0
  220. data/skills/rspec/examples/factory_bot/factory_definition.rb +191 -0
  221. data/skills/rspec/examples/factory_bot/inheritance.rb +314 -0
  222. data/skills/rspec/examples/factory_bot/traits.rb +293 -0
  223. data/skills/rspec/examples/factory_bot/transients.rb +229 -0
  224. data/skills/rspec/examples/matchers/change.rb +115 -0
  225. data/skills/rspec/examples/matchers/collections.rb +154 -0
  226. data/skills/rspec/examples/matchers/comparisons.rb +79 -0
  227. data/skills/rspec/examples/matchers/composing.rb +155 -0
  228. data/skills/rspec/examples/matchers/custom_matchers.rb +197 -0
  229. data/skills/rspec/examples/matchers/equality.rb +58 -0
  230. data/skills/rspec/examples/matchers/errors.rb +136 -0
  231. data/skills/rspec/examples/matchers/output.rb +103 -0
  232. data/skills/rspec/examples/matchers/predicates.rb +87 -0
  233. data/skills/rspec/examples/matchers/truthiness.rb +101 -0
  234. data/skills/rspec/examples/matchers/types.rb +82 -0
  235. data/skills/rspec/examples/matchers/yield.rb +147 -0
  236. data/skills/rspec/examples/mocks/any_instance.rb +172 -0
  237. data/skills/rspec/examples/mocks/argument_matchers.rb +206 -0
  238. data/skills/rspec/examples/mocks/constants.rb +177 -0
  239. data/skills/rspec/examples/mocks/doubles.rb +139 -0
  240. data/skills/rspec/examples/mocks/expectations.rb +137 -0
  241. data/skills/rspec/examples/mocks/message_chains.rb +173 -0
  242. data/skills/rspec/examples/mocks/ordering.rb +144 -0
  243. data/skills/rspec/examples/mocks/receive_counts.rb +181 -0
  244. data/skills/rspec/examples/mocks/responses.rb +223 -0
  245. data/skills/rspec/examples/mocks/spies.rb +149 -0
  246. data/skills/rspec/examples/mocks/stubbing.rb +133 -0
  247. data/skills/rspec/examples/rails/channels.rb +250 -0
  248. data/skills/rspec/examples/rails/controller_specs.rb +302 -0
  249. data/skills/rspec/examples/rails/helper_specs.rb +245 -0
  250. data/skills/rspec/examples/rails/job_specs.rb +256 -0
  251. data/skills/rspec/examples/rails/mailer_specs.rb +228 -0
  252. data/skills/rspec/examples/rails/matchers.rb +374 -0
  253. data/skills/rspec/examples/rails/model_specs.rb +193 -0
  254. data/skills/rspec/examples/rails/request_specs.rb +275 -0
  255. data/skills/rspec/examples/rails/routing_specs.rb +276 -0
  256. data/skills/rspec/examples/rails/system_specs.rb +294 -0
  257. data/skills/rspec/examples/rails/transactions.rb +254 -0
  258. data/skills/rspec/examples/rails/view_specs.rb +252 -0
  259. data/skills/rspec/references/core.md +816 -0
  260. data/skills/rspec/references/factory_bot.md +641 -0
  261. data/skills/rspec/references/matchers.md +516 -0
  262. data/skills/rspec/references/mocks.md +381 -0
  263. data/skills/rspec/references/rails.md +528 -0
  264. data/templates/soul.md +40 -0
  265. data/workflows/commit.md +45 -0
  266. data/workflows/create_handoff.md +98 -0
  267. data/workflows/create_note.md +82 -0
  268. data/workflows/create_plan.md +457 -0
  269. data/workflows/decompose_ticket.md +109 -0
  270. data/workflows/feature.md +91 -0
  271. data/workflows/implement_plan.md +87 -0
  272. data/workflows/iterate_plan.md +247 -0
  273. data/workflows/research_codebase.md +210 -0
  274. data/workflows/resume_handoff.md +217 -0
  275. data/workflows/review_pr.md +320 -0
  276. data/workflows/thoughts_init.md +71 -0
  277. data/workflows/validate_plan.md +166 -0
  278. metadata +290 -3
  279. data/app/controllers/api/sessions_controller.rb +0 -25
  280. data/lib/events/subscribers/action_cable_bridge.rb +0 -59
@@ -0,0 +1,182 @@
1
+ ---
2
+ name: gh-issue
3
+ description: "GitHub issue writing with WHAT/WHY/HOW framework. Activate when user creates issues, writes tickets, drafts GitHub issues, or edits issue descriptions."
4
+ ---
5
+
6
+ # GitHub Issue Writing
7
+
8
+ Write GitHub issues with clear rationale using the WHAT/WHY/HOW framework. Every issue must answer three questions for the reader: What problem are we solving? Why does it matter? How will we solve it?
9
+
10
+ ## Issue Structure
11
+
12
+ ### Title
13
+
14
+ Action-oriented, concise (under 70 characters).
15
+
16
+ **Format:** `[Noun] [Action]` or `[Action] [Noun]`
17
+
18
+ **Examples:**
19
+ - "Assistant Model" (noun for foundational work)
20
+ - "Twitter Banner Verification" (noun phrase for feature)
21
+ - "Add story_context MCP tool" (action for specific implementation)
22
+
23
+ ### Body Template
24
+
25
+ ```markdown
26
+ ## Problem to solve
27
+ [1-3 sentences: What gap, pain point, or need exists? Why can't we proceed without this?]
28
+
29
+ ## Solution
30
+ [1-2 sentences: High-level approach to address the problem]
31
+
32
+ ## [Details Section - varies by issue type]
33
+ [Fields, Routes, Flow, Requirements - whatever is relevant]
34
+
35
+ ## Why [decision]?
36
+ [Explain non-obvious choices. Skip if decision is self-evident]
37
+ ```
38
+
39
+ ## The Three Questions
40
+
41
+ ### WHAT (Problem to solve)
42
+
43
+ State the problem from user/system perspective, not implementation perspective.
44
+
45
+ **Bad:** "We need to create an Assistant model with fields for name and API key."
46
+ **Good:** "The platform needs a core identity for AI agents. Without this model, agents cannot register or interact with the platform."
47
+
48
+ ### WHY (Rationale)
49
+
50
+ Explain decisions that aren't self-evident. Include "Why X?" sections for:
51
+ - Architectural choices (Why separate models? Why MCP not REST?)
52
+ - Technology decisions (Why Twitter? Why no password?)
53
+ - Scope decisions (Why optional field? Why this validation?)
54
+
55
+ Skip rationale for obvious decisions. Don't explain why a user model has an email field.
56
+
57
+ ### HOW (Solution details)
58
+
59
+ Include enough detail to implement without ambiguity:
60
+ - **Models:** Fields with types/constraints, associations, behaviors
61
+ - **Endpoints:** Routes, request/response formats, logic
62
+ - **Flows:** Step-by-step sequences with edge cases
63
+ - **Validation:** Rules with specific constraints
64
+
65
+ ## Issue Types and Patterns
66
+
67
+ ### Model/Entity Issues
68
+
69
+ ```markdown
70
+ ## Problem to solve
71
+ [Why this entity needs to exist in the system]
72
+
73
+ ## Solution
74
+ Create `ModelName` model for [purpose].
75
+
76
+ ## Fields
77
+ - `field_name` — description, constraints
78
+ - `belongs_to :other` — relationship explanation
79
+
80
+ ## Behavior
81
+ - [Key behaviors and state transitions]
82
+ - [Validation rules]
83
+
84
+ ## Why [architectural decision]?
85
+ [Explain non-obvious choices like STI vs separate models]
86
+ ```
87
+
88
+ ### Feature/Flow Issues
89
+
90
+ ```markdown
91
+ ## Problem to solve
92
+ [User need or system requirement]
93
+
94
+ ## Solution
95
+ [High-level approach]
96
+
97
+ ## Flow
98
+ 1. [Step with actor]
99
+ 2. [Step with system response]
100
+ 3. [Step with outcome]
101
+
102
+ ## Implementation Notes
103
+ - [Technical details]
104
+ - [Edge cases]
105
+
106
+ ## Why [approach]?
107
+ [Rationale for chosen solution over alternatives]
108
+ ```
109
+
110
+ ### Research/Spike Issues
111
+
112
+ ```markdown
113
+ ## Problem to solve
114
+ [What we don't know that blocks progress]
115
+
116
+ ## Research Questions
117
+ - [ ] [Specific question to answer]
118
+ - [ ] [Another question]
119
+
120
+ ## Context
121
+ [What we already know, constraints]
122
+
123
+ ## Acceptance Criteria
124
+ [What "done" looks like for research]
125
+ ```
126
+
127
+ ## Writing Guidelines
128
+
129
+ ### Be Specific
130
+
131
+ **Bad:** "Handle edge cases"
132
+ **Good:** "Handle: already verified, name not found, Twitter API timeout"
133
+
134
+ ### Use Concrete Examples
135
+
136
+ **Bad:** "Name must be valid"
137
+ **Good:** "Name must be URL-safe (alphanumeric, underscores, hyphens), 3-30 characters"
138
+
139
+ ### Show, Don't Just Tell
140
+
141
+ Include code snippets for interfaces:
142
+
143
+ ```markdown
144
+ ## MCP Interface
145
+ \`\`\`
146
+ Tool: register_assistant
147
+ Input: { name: "bonk" }
148
+ Output: {
149
+ api_key: "oic_abc123...",
150
+ verification_url: "https://openinstaclaw.ai/bonk"
151
+ }
152
+ \`\`\`
153
+ ```
154
+
155
+ ## gh CLI Usage
156
+
157
+ Create issues with proper formatting:
158
+
159
+ ```bash
160
+ gh issue create \
161
+ --title "Assistant Model" \
162
+ --body "$(cat <<'EOF'
163
+ ## Problem to solve
164
+ [Content here]
165
+
166
+ ## Solution
167
+ [Content here]
168
+ EOF
169
+ )"
170
+ ```
171
+
172
+ ## Quality Checklist
173
+
174
+ Before submitting an issue:
175
+
176
+ - [ ] Title is action-oriented and under 70 characters
177
+ - [ ] "Problem to solve" explains the need, not the implementation
178
+ - [ ] Solution is stated at high level before diving into details
179
+ - [ ] Non-obvious decisions have "Why X?" explanations
180
+ - [ ] Implementation details are specific enough to code from
181
+ - [ ] Edge cases and validation rules are explicit
182
+ - [ ] Code examples included for interfaces/APIs
@@ -0,0 +1,177 @@
1
+ ---
2
+ name: mcp-server
3
+ description: "MCP server development in Ruby — tools, prompts, resources, transport. Activate when building Model Context Protocol servers, defining tools/prompts/resources, working with the mcp gem, or discussing LLM tool integrations."
4
+ ---
5
+
6
+ # MCP Ruby SDK - Server Development Guide
7
+
8
+ Build Model Context Protocol servers in Ruby using the official `mcp` gem (maintained by Anthropic and Shopify).
9
+
10
+ ## Design Philosophy
11
+
12
+ ### Information Provider, Not Analyzer
13
+
14
+ MCP servers provide structured data; LLMs do the reasoning. Return comprehensive frameworks and raw information—let the client perform analysis and context-dependent decisions.
15
+
16
+ > "The MCP server's job is to be the world's best research assistant, not a competing analyst." — Matt Adams
17
+
18
+ ### Context Preservation
19
+
20
+ Agents have limited context windows. Every byte returned that wasn't requested is a byte that could have held useful context. Treat context preservation as a first-class design constraint.
21
+
22
+ **Principles:**
23
+ - Never return data that wasn't explicitly requested
24
+ - Mutations are quiet—return confirmations, not data dumps
25
+ - Explicit over implicit—associations only when asked
26
+ - Filter large datasets before returning (10,000 rows → 5 relevant rows)
27
+
28
+ ### Domain-Aligned Vocabulary
29
+
30
+ Tools should speak the language of your domain, not database/CRUD terminology. Agents are collaborators in your domain process, not database clients.
31
+
32
+ **Example:** A visual novel asset server uses `create_image`, `make_sprite`, `place_character`, `explore_variations`, `compare_images`—not `generate`, `remove_background`, `composite`, `batch_generate`, `get_diff`.
33
+
34
+ ### Tool Budget Management
35
+
36
+ Too many tools overwhelm agents and increase costs. Design toolsets around clear use cases, not API endpoint mirrors.
37
+
38
+ - Group related functionality intelligently
39
+ - Use lazy loading for large tool sets (150K tokens → 2K via on-demand discovery)
40
+ - Tool names ≤64 characters, descriptions narrow and unambiguous
41
+
42
+ ### Security: The Lethal Trifecta
43
+
44
+ Three capabilities that, when combined, create vulnerabilities (Simon Willison):
45
+ 1. Access to private data
46
+ 2. Exposure to untrusted content
47
+ 3. External communication capabilities
48
+
49
+ **Required:** Explicit user consent before tool invocation, clear UI showing exposed tools, alerts when tool descriptions change.
50
+
51
+ ## Domain Components
52
+
53
+ | Component | Purpose | Reference |
54
+ |-----------|---------|-----------|
55
+ | **Tools** | Define callable functions with input/output schemas | [`references/tools.md`](references/tools.md) |
56
+ | **Prompts** | Template-based message generators | [`references/prompts.md`](references/prompts.md) |
57
+ | **Resources** | Static and dynamic file/data registration | [`references/resources.md`](references/resources.md) |
58
+ | **Server** | Core server initialization and configuration | [`references/server.md`](references/server.md) |
59
+ | **Transport** | STDIO and HTTP transport options | [`references/transport.md`](references/transport.md) |
60
+ | **Gotchas** | Tricky behaviors and error handling | [`references/gotchas.md`](references/gotchas.md) |
61
+
62
+ ## Key Concepts
63
+
64
+ | Concept | Purpose |
65
+ |---------|---------|
66
+ | `MCP::Tool` | Base class for defining callable tools |
67
+ | `MCP::Prompt` | Base class for prompt templates |
68
+ | `MCP::Resource` | Static resource registration |
69
+ | `MCP::ResourceTemplate` | Dynamic URI-based resources |
70
+ | `server_context` | Request-scoped data passed to handlers |
71
+ | `MCP::Tool::Response` | Structured tool return value |
72
+
73
+ ## Tool Definition Patterns
74
+
75
+ | Pattern | Use Case |
76
+ |---------|----------|
77
+ | Class-based (`< MCP::Tool`) | Reusable tools with complex logic |
78
+ | Block-based (`MCP::Tool.define`) | Inline, simple tools |
79
+ | Dynamic (`server.define_tool`) | Runtime tool registration |
80
+
81
+ ## Transport Decision Tree
82
+
83
+ ```
84
+ What environment?
85
+ ├── CLI tool / Local server
86
+ │ └── Use STDIO transport
87
+ └── Web server / Production
88
+ └── Need sessions and notifications?
89
+ ├── YES → Use Streamable HTTP (stateful)
90
+ └── NO → Use Streamable HTTP (stateless)
91
+ ```
92
+
93
+ ### Quick Comparison
94
+
95
+ | Transport | Sessions | Notifications | Use For |
96
+ |-----------|----------|---------------|---------|
97
+ | STDIO | N/A | Yes | CLI tools, local dev |
98
+ | HTTP (stateful) | Yes | Yes | Web apps, long-lived connections |
99
+ | HTTP (stateless) | No | No | Simple request/response APIs |
100
+
101
+ ## Protocol Version Features
102
+
103
+ | Feature | Minimum Version |
104
+ |---------|-----------------|
105
+ | `description` | 2025-11-25 |
106
+ | `instructions` | 2025-03-26 |
107
+ | `annotations` | 2025-03-26 |
108
+ | `output_schema` | 2025-03-26 |
109
+
110
+ ## Best Practices
111
+
112
+ ### Do
113
+
114
+ - Use `tool_name` for namespaced classes to avoid conflicts
115
+ - Use `additionalProperties: false` for strict schema validation
116
+ - Use mutex for shared state in HTTP transport (thread safety)
117
+ - Return error responses for business errors (`Response.new([...], error: true)`)
118
+ - Check protocol version before using newer features
119
+ - Use `server_context` for request-scoped data (user_id, env)
120
+
121
+ ### Don't
122
+
123
+ - Don't use `$ref` in schemas (raises ArgumentError, inline only)
124
+ - Don't assume extra args are rejected (`additionalProperties` defaults to allowing extras)
125
+ - Don't use `rpc.` prefix (reserved for protocol methods)
126
+ - Don't send notifications in stateless mode (raises RuntimeError)
127
+ - Don't rely on validation order (required args checked before JSON Schema)
128
+
129
+ ## Anti-Patterns Quick List
130
+
131
+ | Anti-Pattern | Solution |
132
+ |--------------|----------|
133
+ | Missing `additionalProperties: false` | Add to schema for strict validation |
134
+ | Using `$ref` in schemas | Inline all definitions |
135
+ | Notifications in stateless mode | Use stateful transport or skip notifications |
136
+ | Hardcoded server_context | Pass dynamically based on request |
137
+ | Ignoring protocol version | Check version before using gated features |
138
+ | Blocking in tool handlers | Use async patterns for long operations |
139
+
140
+ ## Key Points
141
+
142
+ 1. **Validation is multi-layered** - Required args checked first, then JSON Schema validation
143
+ 2. **Notifications are fire-and-forget** - Errors reported but don't propagate
144
+ 3. **Protocol version matters** - Features are gated by version
145
+ 4. **Server context is opt-in** - Detected from method signature (must include `server_context:` parameter)
146
+ 5. **Schemas are immutable** - Validated at class load time, not runtime
147
+
148
+ ## Additional Resources
149
+
150
+ ### Reference Files
151
+
152
+ For detailed DSL syntax by domain:
153
+
154
+ - **`references/tools.md`** - Tool definition, responses, schemas, annotations
155
+ - **`references/prompts.md`** - Prompt definition, arguments, content types
156
+ - **`references/resources.md`** - Resource registration, templates, read handlers
157
+ - **`references/server.md`** - Server initialization, configuration, custom methods
158
+ - **`references/transport.md`** - Transport config, protocol methods, sessions
159
+ - **`references/gotchas.md`** - Tricky behaviors, error handling, edge cases
160
+
161
+ ### Example Files
162
+
163
+ Working examples in `examples/`:
164
+
165
+ - **`examples/stdio_server.rb`** - Complete STDIO server with tools, prompts, resources
166
+ - **`examples/http_server.rb`** - HTTP server with Rack and logging
167
+ - **`examples/rails_integration.rb`** - Rails controller, routes, and initializer
168
+ - **`examples/file_manager_tool.rb`** - Sandboxed file operations with security patterns
169
+ - **`examples/dynamic_tools.rb`** - Runtime tool registration with notifications
170
+ - **`examples/http_client.rb`** - HTTP client connecting to MCP server
171
+ - **`examples/streaming_client.rb`** - SSE streaming client for real-time notifications
172
+
173
+ ### External Links
174
+
175
+ - [MCP Ruby SDK on GitHub](https://github.com/modelcontextprotocol/ruby-sdk)
176
+ - [MCP Protocol Specification](https://modelcontextprotocol.io)
177
+ - [RubyDoc API Reference](https://rubydoc.info/gems/mcp)
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Dynamic Tool Registration
4
+ # Demonstrates: server.define_tool, notify_tools_list_changed, plugin pattern
5
+ #
6
+ # See: ../references/server.md, ../references/tools.md
7
+
8
+ require "mcp"
9
+
10
+ class PluginManager
11
+ def initialize(server)
12
+ @server = server
13
+ end
14
+
15
+ def load_plugin(name, &block)
16
+ @server.define_tool(
17
+ name: "plugin_#{name}",
18
+ description: "Plugin: #{name}",
19
+ input_schema: { properties: { input: { type: "string" } } }
20
+ ) do |input:, server_context:|
21
+ result = block.call(input, server_context)
22
+ MCP::Tool::Response.new([{ type: "text", text: result.to_s }])
23
+ end
24
+
25
+ @server.notify_tools_list_changed
26
+ end
27
+ end
28
+
29
+ # Usage
30
+ server = MCP::Server.new(name: "plugin_server", tools: [])
31
+ transport = MCP::Server::Transports::StreamableHTTPTransport.new(server)
32
+ server.transport = transport
33
+
34
+ manager = PluginManager.new(server)
35
+ manager.load_plugin("uppercase") { |input, _| input.upcase }
36
+ manager.load_plugin("reverse") { |input, _| input.reverse }
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ # File Manager Tool with sandboxed operations
4
+ # Demonstrates: Security patterns, annotations, error responses, tool_name
5
+ #
6
+ # See: ../references/tools.md, ../references/gotchas.md
7
+
8
+ require "mcp"
9
+ require "fileutils"
10
+
11
+ class FileManagerTool < MCP::Tool
12
+ tool_name "file_manager"
13
+ description "Manages files in a sandboxed directory"
14
+
15
+ input_schema(
16
+ properties: {
17
+ action: { type: "string", enum: %w[read write list delete] },
18
+ path: { type: "string" },
19
+ content: { type: "string" }
20
+ },
21
+ required: %w[action path]
22
+ )
23
+
24
+ annotations(
25
+ destructive_hint: true,
26
+ idempotent_hint: false,
27
+ read_only_hint: false
28
+ )
29
+
30
+ SANDBOX_DIR = "/tmp/mcp_sandbox"
31
+
32
+ class << self
33
+ def call(action:, path:, content: nil, server_context: nil)
34
+ full_path = File.join(SANDBOX_DIR, path)
35
+
36
+ # Security: Ensure path is within sandbox
37
+ unless full_path.start_with?(SANDBOX_DIR)
38
+ return error_response("Path traversal not allowed")
39
+ end
40
+
41
+ case action
42
+ when "read" then read_file(full_path)
43
+ when "write" then write_file(full_path, content)
44
+ when "list" then list_directory(full_path)
45
+ when "delete" then delete_file(full_path)
46
+ else error_response("Unknown action: #{action}")
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def read_file(path)
53
+ return error_response("File not found") unless File.exist?(path)
54
+
55
+ MCP::Tool::Response.new([{ type: "text", text: File.read(path) }])
56
+ end
57
+
58
+ def write_file(path, content)
59
+ FileUtils.mkdir_p(File.dirname(path))
60
+ File.write(path, content)
61
+ MCP::Tool::Response.new([{ type: "text", text: "Written #{content.bytesize} bytes" }])
62
+ end
63
+
64
+ def list_directory(path)
65
+ return error_response("Directory not found") unless Dir.exist?(path)
66
+
67
+ files = Dir.entries(path).reject { |f| f.start_with?(".") }
68
+ MCP::Tool::Response.new(
69
+ [{ type: "text", text: files.join("\n") }],
70
+ structured_content: files
71
+ )
72
+ end
73
+
74
+ def delete_file(path)
75
+ return error_response("File not found") unless File.exist?(path)
76
+
77
+ File.delete(path)
78
+ MCP::Tool::Response.new([{ type: "text", text: "Deleted" }])
79
+ end
80
+
81
+ def error_response(message)
82
+ MCP::Tool::Response.new([{ type: "text", text: message }], error: true)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # HTTP Client Example
5
+ # Demonstrates: MCP::Client, HTTP transport, tool/prompt discovery and invocation
6
+ #
7
+ # See: ../references/transport.md
8
+
9
+ require "mcp"
10
+
11
+ # Create HTTP transport
12
+ http = MCP::Client::HTTP.new(
13
+ url: "http://localhost:9292",
14
+ headers: { "Authorization" => "Bearer token123" }
15
+ )
16
+
17
+ client = MCP::Client.new(transport: http)
18
+
19
+ # List tools
20
+ tools = client.tools
21
+ puts "Available tools:"
22
+ tools.each do |tool|
23
+ puts " - #{tool.name}: #{tool.description}"
24
+ end
25
+
26
+ # Call a tool
27
+ if (weather_tool = tools.find { |t| t.name == "weather" })
28
+ result = client.call_tool(
29
+ tool: weather_tool,
30
+ arguments: { location: "London", units: "celsius" }
31
+ )
32
+ puts "Weather: #{result.dig('result', 'content', 0, 'text')}"
33
+ end
34
+
35
+ # List prompts
36
+ prompts = client.prompts
37
+ prompts.each do |prompt|
38
+ puts "Prompt: #{prompt['name']}"
39
+ end
40
+
41
+ # Get a prompt
42
+ if prompts.any?
43
+ prompt_result = client.get_prompt(
44
+ name: prompts.first["name"],
45
+ arguments: { code: "puts 'hello'" }
46
+ )
47
+ puts "Messages: #{prompt_result['messages'].length}"
48
+ end
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # HTTP MCP Server with Rack
5
+ # Demonstrates: StreamableHTTP transport, configuration, logging, output_schema
6
+ #
7
+ # See: ../references/transport.md, ../references/server.md, ../references/tools.md
8
+ #
9
+ # Run: ruby http_server.rb
10
+ # Server starts on http://localhost:9292
11
+
12
+ require "mcp"
13
+ require "rack"
14
+ require "rackup"
15
+ require "json"
16
+ require "logger"
17
+
18
+ class WeatherTool < MCP::Tool
19
+ description "Gets weather for a location"
20
+ input_schema(
21
+ properties: {
22
+ location: { type: "string" },
23
+ units: { type: "string", enum: %w[celsius fahrenheit] }
24
+ },
25
+ required: ["location"]
26
+ )
27
+
28
+ output_schema(
29
+ properties: {
30
+ temperature: { type: "number" },
31
+ condition: { type: "string" }
32
+ },
33
+ required: %w[temperature condition]
34
+ )
35
+
36
+ annotations(read_only_hint: true, destructive_hint: false)
37
+
38
+ class << self
39
+ def call(location:, units: "celsius", server_context: nil)
40
+ # Simulate API call
41
+ data = { temperature: 22, condition: "sunny" }
42
+
43
+ MCP::Tool::Response.new(
44
+ [{ type: "text", text: data.to_json }],
45
+ structured_content: data
46
+ )
47
+ end
48
+ end
49
+ end
50
+
51
+ $logger = Logger.new($stdout)
52
+ $logger.level = Logger::INFO
53
+
54
+ config = MCP::Configuration.new(
55
+ protocol_version: "2025-11-25",
56
+ exception_reporter: ->(e, ctx) {
57
+ $logger.error("MCP Error: #{e.message}")
58
+ $logger.debug(ctx.inspect)
59
+ },
60
+ instrumentation_callback: ->(data) {
61
+ $logger.info("MCP: #{data[:method]} (#{data[:duration]}s)")
62
+ }
63
+ )
64
+
65
+ server = MCP::Server.new(
66
+ name: "weather_server",
67
+ version: "1.0.0",
68
+ description: "Weather information server",
69
+ tools: [WeatherTool],
70
+ configuration: config
71
+ )
72
+
73
+ transport = MCP::Server::Transports::StreamableHTTPTransport.new(server)
74
+ server.transport = transport
75
+
76
+ app = proc do |env|
77
+ request = Rack::Request.new(env)
78
+
79
+ if request.post?
80
+ body = request.body.read
81
+ request.body.rewind
82
+ $logger.debug("Request: #{body}")
83
+ end
84
+
85
+ response = transport.handle_request(request)
86
+ $logger.debug("Response: #{response[2].first}") if response[2].respond_to?(:first)
87
+ response
88
+ end
89
+
90
+ rack_app = Rack::Builder.new do
91
+ use Rack::CommonLogger, $logger
92
+ use Rack::ShowExceptions
93
+ run app
94
+ end
95
+
96
+ puts "Starting MCP server on http://localhost:9292"
97
+ Rackup::Handler.get("puma").run(rack_app, Port: 9292, Host: "localhost")