aia 0.9.22 → 0.9.24
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 +4 -4
- data/.version +1 -1
- data/CHANGELOG.md +60 -0
- data/README.md +173 -1
- data/docs/directives-reference.md +28 -8
- data/docs/index.md +2 -2
- data/lib/aia/config/base.rb +79 -0
- data/lib/aia/config/defaults.rb +3 -0
- data/lib/aia/directives/checkpoint.rb +283 -0
- data/lib/aia/directives/configuration.rb +3 -88
- data/lib/aia/directives/models.rb +12 -5
- data/lib/aia/directives/registry.rb +2 -0
- data/lib/aia/directives/utility.rb +23 -8
- data/lib/aia/ruby_llm_adapter.rb +20 -4
- data/lib/aia/session.rb +40 -148
- data/lib/aia/topic_context.rb +125 -0
- data/lib/aia/utility.rb +12 -1
- data/lib/extensions/openstruct_merge.rb +4 -0
- metadata +4 -3
- data/lib/aia/context_manager.rb +0 -134
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f9592d9ea1bbbbdc04f92dc822cfbdae91c1e04084530583e50f6cdb8a90460d
|
|
4
|
+
data.tar.gz: 897e53ec02de0fcc46a0e5ddcf15ca13a462b7cc20e4f355950f5872e5b04f00
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5e8b63a8ca892346e4dd1090ef487e4fc6dc6b9395238ecc6208128d8908a5dca87d730a9193a15b05dbb005b8d9eb84e2bb7f63100982130eeec69e0bb9483d
|
|
7
|
+
data.tar.gz: 660c1c40117300223a7bd88c67ec826225a44ada72d459ee6c8fd3e3c015e81a7874a9618220c4dc7caf3de930ee140d29ef1c7c8dfc205ecfeedef8e629b36a
|
data/.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.9.
|
|
1
|
+
0.9.24
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,66 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
## [Unreleased]
|
|
3
3
|
|
|
4
|
+
## [0.9.24] 2025-12-17
|
|
5
|
+
### Fixes
|
|
6
|
+
- Ran into a problem with the `shared_tools` gem and the --require parameter of AIA which required changes to both gems.
|
|
7
|
+
|
|
8
|
+
### Improvements
|
|
9
|
+
- **`//tools` Directive Filter**: Added optional filter parameter to the `//tools` directive
|
|
10
|
+
- Filter tools by name substring (case-insensitive)
|
|
11
|
+
- Example: `//tools error` lists only tools with "error" in the name
|
|
12
|
+
- Shows "No tools match the filter: [filter]" when no matches found
|
|
13
|
+
- Header indicates when filtering is active: "Available Tools (filtered by 'filter')"
|
|
14
|
+
|
|
15
|
+
## [0.9.23] 2025-12-06
|
|
16
|
+
|
|
17
|
+
### New Features
|
|
18
|
+
- **MCP Server Configuration**: Added native support for defining MCP (Model Context Protocol) servers in the config file
|
|
19
|
+
- Configure MCP servers in `~/.aia/config.yml` under the `mcp_servers` key
|
|
20
|
+
- Supports `name`, `command`, `args`, `env`, and `timeout` options per server
|
|
21
|
+
- Automatic PATH resolution for commands (no absolute paths required)
|
|
22
|
+
- Configurable timeouts for slow-starting servers (default: 8000ms)
|
|
23
|
+
- Environment variable support for MCP server processes
|
|
24
|
+
|
|
25
|
+
### Improvements
|
|
26
|
+
- **Robot Display**: Added MCP server names to the robot ASCII art display
|
|
27
|
+
- Shows "MCP: server1, server2, ..." when MCP servers are configured
|
|
28
|
+
|
|
29
|
+
### Technical Changes
|
|
30
|
+
- Added `load_mcp_servers` method to `lib/aia/config/base.rb` for automatic MCP client registration
|
|
31
|
+
- Added `resolve_command_path` method for PATH-based command resolution
|
|
32
|
+
- Added `mcp_servers?` and `mcp_server_names` helper methods to `lib/aia/utility.rb`
|
|
33
|
+
- Fixed `OpenStruct.merge` to skip nil values, preventing config file values from being overwritten
|
|
34
|
+
- Added `mcp_servers: nil` default to prevent merge issues with empty arrays
|
|
35
|
+
|
|
36
|
+
### Configuration Example
|
|
37
|
+
```yaml
|
|
38
|
+
# ~/.aia/config.yml
|
|
39
|
+
:mcp_servers:
|
|
40
|
+
- name: "my-server"
|
|
41
|
+
command: "my_mcp_server.rb"
|
|
42
|
+
args: ["stdio"]
|
|
43
|
+
timeout: 30000
|
|
44
|
+
env:
|
|
45
|
+
MY_VAR: "value"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## [0.9.22] 2025-11-12
|
|
49
|
+
|
|
50
|
+
### Bug Fixes
|
|
51
|
+
- **TEST SUITE**: Fixed all Mocha test isolation issues causing stub contamination between tests
|
|
52
|
+
- Added proper teardown methods with `super` calls to 13 test files to ensure Mocha cleanup
|
|
53
|
+
- Fixed PromptHandlerTest missing teardown and config fields (erb, shell, roles_dir)
|
|
54
|
+
- Fixed ModelsDirectiveTest to use consistent stubbing approach instead of mixing real and stubbed config
|
|
55
|
+
- Fixed MultiModelIsolationTest to use stubs instead of direct instance variable manipulation
|
|
56
|
+
- Fixed AIAIntegrationTest, ChatProcessorServiceTest, ContextManagerTest, DirectiveProcessorTest, RubyLLMAdapterTest, SessionTest, LocalProvidersTest, UtilityTest, AIAMockingTest, and AIAPropertyBasedTest to include proper Mocha cleanup
|
|
57
|
+
- Test results improved from 2 failures, 2 errors to 0 failures, 0 errors (325 runs, 1018 assertions)
|
|
58
|
+
|
|
59
|
+
### Technical Changes
|
|
60
|
+
- Enhanced test isolation by ensuring all tests using Mocha stubs properly clean up via `super` in teardown
|
|
61
|
+
- Standardized stub usage pattern across test suite for consistency
|
|
62
|
+
- Eliminated stub leakage that caused "unexpected invocation" and "AIA was instantiated in one test but receiving invocations in another" errors
|
|
63
|
+
|
|
4
64
|
## [0.9.21] 2025-10-08
|
|
5
65
|
### Bug Fixes
|
|
6
66
|
- **Checkpoint Directive Output**: Fixed `//checkpoint` directive to return empty string instead of status message (lib/aia/directives/configuration.rb:155)
|
data/README.md
CHANGED
|
@@ -100,6 +100,7 @@ For more information on AIA visit these locations:
|
|
|
100
100
|
- [Example Workflow](#example-workflow)
|
|
101
101
|
- [Roles and System Prompts](#roles-and-system-prompts)
|
|
102
102
|
- [RubyLLM::Tool Support](#rubyllmtool-support)
|
|
103
|
+
- [MCP Server Configuration](#mcp-server-configuration)
|
|
103
104
|
- [Examples & Tips](#examples--tips)
|
|
104
105
|
- [Practical Examples](#practical-examples)
|
|
105
106
|
- [Code Review Prompt](#code-review-prompt)
|
|
@@ -350,7 +351,7 @@ Directives are special commands in prompt files that begin with `//` and provide
|
|
|
350
351
|
| `//help` | Show available directives | `//help` |
|
|
351
352
|
| `//model` | Show current model configuration | `//model` |
|
|
352
353
|
| `//available_models` | List available models | `//available_models` |
|
|
353
|
-
| `//tools` | Show
|
|
354
|
+
| `//tools` | Show available tools (optional filter by name) | `//tools` or `//tools file` |
|
|
354
355
|
| `//review` | Review current context with checkpoint markers | `//review` |
|
|
355
356
|
|
|
356
357
|
Directives can also be used in the interactive chat sessions.
|
|
@@ -922,6 +923,177 @@ aia --tools examples/tools/mcp/imcp.rb --chat
|
|
|
922
923
|
|
|
923
924
|
These MCP clients require the `ruby_llm-mcp` gem and provide access to external services and data sources through the Model Context Protocol.
|
|
924
925
|
|
|
926
|
+
### MCP Server Configuration
|
|
927
|
+
|
|
928
|
+
AIA supports defining MCP (Model Context Protocol) servers directly in your configuration file. This allows MCP tools to be automatically loaded at startup without needing to specify them on the command line each time.
|
|
929
|
+
|
|
930
|
+
#### Configuration Format
|
|
931
|
+
|
|
932
|
+
Add MCP servers to your `~/.aia/config.yml` file:
|
|
933
|
+
|
|
934
|
+
```yaml
|
|
935
|
+
:mcp_servers:
|
|
936
|
+
- name: "server-name"
|
|
937
|
+
command: "server_command"
|
|
938
|
+
args: ["arg1", "arg2"]
|
|
939
|
+
timeout: 30000 # milliseconds (default: 8000)
|
|
940
|
+
env:
|
|
941
|
+
ENV_VAR: "value"
|
|
942
|
+
```
|
|
943
|
+
|
|
944
|
+
#### Configuration Options
|
|
945
|
+
|
|
946
|
+
| Option | Required | Default | Description |
|
|
947
|
+
|--------|----------|---------|-------------|
|
|
948
|
+
| `name` | Yes | - | Unique identifier for the MCP server |
|
|
949
|
+
| `command` | Yes | - | Executable command (absolute path or found in PATH) |
|
|
950
|
+
| `args` | No | `[]` | Array of command-line arguments |
|
|
951
|
+
| `timeout` | No | `8000` | Connection timeout in milliseconds |
|
|
952
|
+
| `env` | No | `{}` | Environment variables for the server process |
|
|
953
|
+
|
|
954
|
+
#### Example: GitHub MCP Server
|
|
955
|
+
|
|
956
|
+
The GitHub MCP server provides access to GitHub repositories, issues, pull requests, and more:
|
|
957
|
+
|
|
958
|
+
```yaml
|
|
959
|
+
# ~/.aia/config.yml
|
|
960
|
+
:mcp_servers:
|
|
961
|
+
- name: "github"
|
|
962
|
+
command: "github-mcp-server"
|
|
963
|
+
args: ["stdio"]
|
|
964
|
+
timeout: 15000
|
|
965
|
+
env:
|
|
966
|
+
GITHUB_PERSONAL_ACCESS_TOKEN: "ghp_your_token_here"
|
|
967
|
+
```
|
|
968
|
+
|
|
969
|
+
**Setup:**
|
|
970
|
+
```bash
|
|
971
|
+
# Install GitHub MCP server (macOS)
|
|
972
|
+
brew install github-mcp-server
|
|
973
|
+
|
|
974
|
+
# Or via npm
|
|
975
|
+
npm install -g @anthropic/github-mcp-server
|
|
976
|
+
|
|
977
|
+
# Set your GitHub token (recommended: use environment variable instead of config)
|
|
978
|
+
export GITHUB_PERSONAL_ACCESS_TOKEN="ghp_your_token_here"
|
|
979
|
+
```
|
|
980
|
+
|
|
981
|
+
#### Example: Hierarchical Temporal Memory (HTM)
|
|
982
|
+
|
|
983
|
+
```shell
|
|
984
|
+
gem install htm
|
|
985
|
+
```
|
|
986
|
+
|
|
987
|
+
See the [full HTM documentation](https://madbomber.github.io/htm) for database configuration and system environment variable usage.
|
|
988
|
+
|
|
989
|
+
A custom Ruby-based MCP server for accessing database-backed long term memory:
|
|
990
|
+
|
|
991
|
+
```yaml
|
|
992
|
+
# ~/.aia/config.yml
|
|
993
|
+
:mcp_servers:
|
|
994
|
+
- name: "htm"
|
|
995
|
+
command: "htm_mcp.rb"
|
|
996
|
+
args: ["stdio"]
|
|
997
|
+
timeout: 30000
|
|
998
|
+
env:
|
|
999
|
+
HTM_DBURL: "postgres://localhost:5432/htm_development"
|
|
1000
|
+
...
|
|
1001
|
+
```
|
|
1002
|
+
|
|
1003
|
+
**Notes:**
|
|
1004
|
+
- The `command` can be just the executable name if it's in your PATH
|
|
1005
|
+
- AIA automatically resolves command paths, so you don't need absolute paths
|
|
1006
|
+
- Environment variables in the `env` section are passed only to that MCP server process
|
|
1007
|
+
|
|
1008
|
+
#### Example: Multiple MCP Servers
|
|
1009
|
+
|
|
1010
|
+
You can configure multiple MCP servers to provide different capabilities:
|
|
1011
|
+
|
|
1012
|
+
```yaml
|
|
1013
|
+
# ~/.aia/config.yml
|
|
1014
|
+
:mcp_servers:
|
|
1015
|
+
- name: "github"
|
|
1016
|
+
command: "github-mcp-server"
|
|
1017
|
+
args: ["stdio"]
|
|
1018
|
+
env:
|
|
1019
|
+
GITHUB_PERSONAL_ACCESS_TOKEN: "ghp_your_token_here"
|
|
1020
|
+
|
|
1021
|
+
- name: "htm"
|
|
1022
|
+
command: "htm_mcp.rb"
|
|
1023
|
+
args: ["stdio"]
|
|
1024
|
+
timeout: 30000
|
|
1025
|
+
env:
|
|
1026
|
+
HTM_DBURL: "postgres://localhost:5432/htm_development"
|
|
1027
|
+
|
|
1028
|
+
- name: "filesystem"
|
|
1029
|
+
command: "filesystem-mcp-server"
|
|
1030
|
+
args: ["stdio", "--root", "/Users/me/projects"]
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
#### Verifying MCP Server Configuration
|
|
1034
|
+
|
|
1035
|
+
When MCP servers are configured, AIA displays them in the startup robot:
|
|
1036
|
+
|
|
1037
|
+
```
|
|
1038
|
+
, ,
|
|
1039
|
+
(\____/) AI Assistant (v0.9.23) is Online
|
|
1040
|
+
(_oo_) gpt-4o-mini (supports tools)
|
|
1041
|
+
(O) using ruby_llm (v1.9.0 MCP v0.6.1)
|
|
1042
|
+
__||__ \) model db was last refreshed on
|
|
1043
|
+
[/______\] / 2025-06-03
|
|
1044
|
+
/ \__AI__/ \/ You can share my tools
|
|
1045
|
+
/ /__\ MCP: github, htm
|
|
1046
|
+
(\ /____\
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
Use the `//tools` directive in chat mode to see all available tools including those from MCP servers:
|
|
1050
|
+
|
|
1051
|
+
```bash
|
|
1052
|
+
aia --chat
|
|
1053
|
+
> //tools
|
|
1054
|
+
|
|
1055
|
+
Available Tools:
|
|
1056
|
+
- github_create_issue: Create a new GitHub issue
|
|
1057
|
+
- github_list_repos: List repositories for the authenticated user
|
|
1058
|
+
- htm_query: Execute a query against the HTM database
|
|
1059
|
+
- htm_insert: Insert a record into HTM
|
|
1060
|
+
...
|
|
1061
|
+
|
|
1062
|
+
# Filter tools by name (case-insensitive)
|
|
1063
|
+
> //tools github
|
|
1064
|
+
|
|
1065
|
+
Available Tools (filtered by 'github')
|
|
1066
|
+
- github_create_issue: Create a new GitHub issue
|
|
1067
|
+
- github_list_repos: List repositories for the authenticated user
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
#### Troubleshooting MCP Servers
|
|
1071
|
+
|
|
1072
|
+
If an MCP server fails to load, AIA will display a warning:
|
|
1073
|
+
|
|
1074
|
+
```
|
|
1075
|
+
WARNING: MCP server 'github' command not found: github-mcp-server
|
|
1076
|
+
WARNING: MCP server entry missing name or command: {...}
|
|
1077
|
+
ERROR: Failed to load MCP server 'htm': Connection timeout
|
|
1078
|
+
```
|
|
1079
|
+
|
|
1080
|
+
**Common Issues:**
|
|
1081
|
+
|
|
1082
|
+
| Problem | Solution |
|
|
1083
|
+
|---------|----------|
|
|
1084
|
+
| Command not found | Ensure the command is in your PATH or use absolute path |
|
|
1085
|
+
| Connection timeout | Increase the `timeout` value |
|
|
1086
|
+
| Missing environment variables | Add required env vars to the `env` section |
|
|
1087
|
+
| Server hangs on startup | Check that all required environment variables are set |
|
|
1088
|
+
|
|
1089
|
+
**Debug Mode:**
|
|
1090
|
+
|
|
1091
|
+
Enable debug mode to see detailed MCP server loading information:
|
|
1092
|
+
|
|
1093
|
+
```bash
|
|
1094
|
+
aia --debug --chat
|
|
1095
|
+
```
|
|
1096
|
+
|
|
925
1097
|
**Shared Tools Collection:**
|
|
926
1098
|
AIA can use the [shared_tools gem](https://github.com/madbomber/shared_tools) which provides a curated collection of commonly-used tools (aka functions) via the --require option.
|
|
927
1099
|
|
|
@@ -214,11 +214,21 @@ Speak text using system text-to-speech (macOS/Linux).
|
|
|
214
214
|
## Utility Directives
|
|
215
215
|
|
|
216
216
|
### `//tools`
|
|
217
|
-
Display available RubyLLM tools.
|
|
217
|
+
Display available RubyLLM tools with optional filtering.
|
|
218
218
|
|
|
219
|
-
**Syntax**: `//tools`
|
|
219
|
+
**Syntax**: `//tools [filter]`
|
|
220
220
|
|
|
221
|
-
**
|
|
221
|
+
**Parameters**:
|
|
222
|
+
- `filter` (optional) - Case-insensitive substring to filter tool names
|
|
223
|
+
|
|
224
|
+
**Examples**:
|
|
225
|
+
```markdown
|
|
226
|
+
//tools # List all available tools
|
|
227
|
+
//tools file # List tools with "file" in the name
|
|
228
|
+
//tools analyzer # List tools with "analyzer" in the name
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Example Output** (unfiltered):
|
|
222
232
|
```
|
|
223
233
|
Available Tools
|
|
224
234
|
===============
|
|
@@ -234,6 +244,21 @@ WebScraper
|
|
|
234
244
|
selectors and filters.
|
|
235
245
|
```
|
|
236
246
|
|
|
247
|
+
**Example Output** (filtered with `//tools file`):
|
|
248
|
+
```
|
|
249
|
+
Available Tools (filtered by 'file')
|
|
250
|
+
====================================
|
|
251
|
+
|
|
252
|
+
FileReader
|
|
253
|
+
----------
|
|
254
|
+
Read and analyze file contents with support for multiple formats
|
|
255
|
+
including text, JSON, YAML, and CSV files.
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Notes**:
|
|
259
|
+
- When no tools match the filter, displays "No tools match the filter: [filter]"
|
|
260
|
+
- Filtering is case-insensitive (e.g., "File", "FILE", and "file" all match)
|
|
261
|
+
|
|
237
262
|
### `//next`
|
|
238
263
|
Set the next prompt to execute in a workflow.
|
|
239
264
|
|
|
@@ -509,11 +534,6 @@ SyntaxError: unexpected token
|
|
|
509
534
|
ERROR: PUREMD_API_KEY is required in order to include a webpage
|
|
510
535
|
```
|
|
511
536
|
|
|
512
|
-
**Missing Context Manager**:
|
|
513
|
-
```
|
|
514
|
-
Error: Context manager not available for //clear directive.
|
|
515
|
-
```
|
|
516
|
-
|
|
517
537
|
### Custom Directives
|
|
518
538
|
|
|
519
539
|
You can extend AIA with custom directives by creating Ruby files that define new directive methods:
|
data/docs/index.md
CHANGED
|
@@ -126,13 +126,13 @@ graph TD
|
|
|
126
126
|
- **AIA::PromptHandler** - Main prompt processing orchestrator
|
|
127
127
|
- **AIA::ChatProcessorService** - Interactive chat session management
|
|
128
128
|
- **AIA::DirectiveProcessor** - Processes embedded directives (`//command params`)
|
|
129
|
-
- **AIA::
|
|
130
|
-
- **AIA::RubyLLMAdapter** - Interfaces with the ruby_llm gem for AI model communication
|
|
129
|
+
- **AIA::RubyLLMAdapter** - Interfaces with the ruby_llm gem for AI model communication (manages conversation history via RubyLLM's Chat.@messages)
|
|
131
130
|
- **AIA::ShellCommandExecutor** - Executes shell commands safely within prompts
|
|
132
131
|
- **AIA::HistoryManager** - Manages prompt parameter history and user input
|
|
133
132
|
- **AIA::UIPresenter** - Terminal output formatting and presentation
|
|
134
133
|
- **AIA::Session** - Manages chat sessions and state
|
|
135
134
|
- **AIA::Fzf** - Fuzzy finder integration for prompt selection
|
|
135
|
+
- **AIA::Directives::Checkpoint** - Manages conversation checkpoints, restore, clear, and review operations
|
|
136
136
|
|
|
137
137
|
### External Dependencies
|
|
138
138
|
|
data/lib/aia/config/base.rb
CHANGED
|
@@ -140,6 +140,7 @@ module AIA
|
|
|
140
140
|
config = tailor_the_config(config)
|
|
141
141
|
load_libraries(config)
|
|
142
142
|
load_tools(config)
|
|
143
|
+
load_mcp_servers(config)
|
|
143
144
|
|
|
144
145
|
if config.dump_file
|
|
145
146
|
dump_config(config, config.dump_file)
|
|
@@ -156,6 +157,9 @@ module AIA
|
|
|
156
157
|
config.require_libs.each do |library|
|
|
157
158
|
begin
|
|
158
159
|
require(library)
|
|
160
|
+
# Check if the library provides tools that need eager loading
|
|
161
|
+
# Convert library name to module constant (e.g., 'shared_tools' -> 'SharedTools')
|
|
162
|
+
eager_load_tools_from_library(library)
|
|
159
163
|
rescue => e
|
|
160
164
|
STDERR.puts "Error loading library '#{library}' #{e.message}"
|
|
161
165
|
exit_on_error = true
|
|
@@ -167,6 +171,23 @@ module AIA
|
|
|
167
171
|
config
|
|
168
172
|
end
|
|
169
173
|
|
|
174
|
+
# Attempt to eager load tools from a required library
|
|
175
|
+
# Libraries that use Zeitwerk need their tools eager loaded so they
|
|
176
|
+
# appear in ObjectSpace for AIA's tool discovery
|
|
177
|
+
def eager_load_tools_from_library(library)
|
|
178
|
+
# Convert library name to module constant (e.g., 'shared_tools' -> 'SharedTools')
|
|
179
|
+
module_name = library.split('/').first.split('_').map(&:capitalize).join
|
|
180
|
+
|
|
181
|
+
begin
|
|
182
|
+
mod = Object.const_get(module_name)
|
|
183
|
+
if mod.respond_to?(:load_all_tools)
|
|
184
|
+
mod.load_all_tools
|
|
185
|
+
end
|
|
186
|
+
rescue NameError
|
|
187
|
+
# Module doesn't exist with expected name, skip
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
170
191
|
def load_tools(config)
|
|
171
192
|
return if config.tool_paths.empty?
|
|
172
193
|
|
|
@@ -192,6 +213,64 @@ module AIA
|
|
|
192
213
|
exit(1) if exit_on_error
|
|
193
214
|
end
|
|
194
215
|
|
|
216
|
+
def load_mcp_servers(config)
|
|
217
|
+
servers = config.mcp_servers
|
|
218
|
+
|
|
219
|
+
return config if servers.nil? || (servers.respond_to?(:empty?) && servers.empty?)
|
|
220
|
+
|
|
221
|
+
servers.each do |server|
|
|
222
|
+
name = server[:name] || server["name"]
|
|
223
|
+
command = server[:command] || server["command"]
|
|
224
|
+
args = server[:args] || server["args"] || []
|
|
225
|
+
env = server[:env] || server["env"] || {}
|
|
226
|
+
timeout = server[:timeout] || server["timeout"] || 8000 # default 8 seconds in ms
|
|
227
|
+
|
|
228
|
+
unless name && command
|
|
229
|
+
STDERR.puts "WARNING: MCP server entry missing name or command: #{server.inspect}"
|
|
230
|
+
next
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Resolve command path if not absolute
|
|
234
|
+
resolved_command = resolve_command_path(command)
|
|
235
|
+
unless resolved_command
|
|
236
|
+
STDERR.puts "WARNING: MCP server '#{name}' command not found: #{command}"
|
|
237
|
+
next
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
begin
|
|
241
|
+
RubyLLM::MCP.add_client(
|
|
242
|
+
name: name,
|
|
243
|
+
transport_type: :stdio,
|
|
244
|
+
request_timeout: timeout,
|
|
245
|
+
config: {
|
|
246
|
+
command: resolved_command,
|
|
247
|
+
args: args,
|
|
248
|
+
env: env
|
|
249
|
+
}
|
|
250
|
+
)
|
|
251
|
+
rescue => e
|
|
252
|
+
STDERR.puts "ERROR: Failed to load MCP server '#{name}': #{e.message}"
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
config
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def resolve_command_path(command)
|
|
260
|
+
# If already absolute path, verify it exists
|
|
261
|
+
if command.start_with?('/')
|
|
262
|
+
return File.executable?(command) ? command : nil
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Search in PATH
|
|
266
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |dir|
|
|
267
|
+
full_path = File.join(dir, command)
|
|
268
|
+
return full_path if File.executable?(full_path)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
nil
|
|
272
|
+
end
|
|
273
|
+
|
|
195
274
|
# envar values are always String object so need other config
|
|
196
275
|
# layers to know the prompter type for each key's value
|
|
197
276
|
def envar_options(default, cli_config)
|