language-operator 0.0.1 → 0.1.31
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/.rubocop.yml +125 -0
- data/CHANGELOG.md +88 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +284 -0
- data/LICENSE +229 -21
- data/Makefile +82 -0
- data/README.md +3 -11
- data/Rakefile +63 -0
- data/bin/aictl +7 -0
- data/completions/_aictl +232 -0
- data/completions/aictl.bash +121 -0
- data/completions/aictl.fish +114 -0
- data/docs/architecture/agent-runtime.md +585 -0
- data/docs/dsl/SCHEMA_VERSION.md +250 -0
- data/docs/dsl/agent-reference.md +604 -0
- data/docs/dsl/best-practices.md +1078 -0
- data/docs/dsl/chat-endpoints.md +895 -0
- data/docs/dsl/constraints.md +671 -0
- data/docs/dsl/mcp-integration.md +1177 -0
- data/docs/dsl/webhooks.md +932 -0
- data/docs/dsl/workflows.md +744 -0
- data/lib/language_operator/agent/base.rb +110 -0
- data/lib/language_operator/agent/executor.rb +440 -0
- data/lib/language_operator/agent/instrumentation.rb +54 -0
- data/lib/language_operator/agent/metrics_tracker.rb +183 -0
- data/lib/language_operator/agent/safety/ast_validator.rb +272 -0
- data/lib/language_operator/agent/safety/audit_logger.rb +104 -0
- data/lib/language_operator/agent/safety/budget_tracker.rb +175 -0
- data/lib/language_operator/agent/safety/content_filter.rb +93 -0
- data/lib/language_operator/agent/safety/manager.rb +207 -0
- data/lib/language_operator/agent/safety/rate_limiter.rb +150 -0
- data/lib/language_operator/agent/safety/safe_executor.rb +127 -0
- data/lib/language_operator/agent/scheduler.rb +183 -0
- data/lib/language_operator/agent/telemetry.rb +116 -0
- data/lib/language_operator/agent/web_server.rb +610 -0
- data/lib/language_operator/agent/webhook_authenticator.rb +226 -0
- data/lib/language_operator/agent.rb +149 -0
- data/lib/language_operator/cli/commands/agent.rb +1205 -0
- data/lib/language_operator/cli/commands/cluster.rb +371 -0
- data/lib/language_operator/cli/commands/install.rb +404 -0
- data/lib/language_operator/cli/commands/model.rb +266 -0
- data/lib/language_operator/cli/commands/persona.rb +393 -0
- data/lib/language_operator/cli/commands/quickstart.rb +22 -0
- data/lib/language_operator/cli/commands/status.rb +143 -0
- data/lib/language_operator/cli/commands/system.rb +772 -0
- data/lib/language_operator/cli/commands/tool.rb +537 -0
- data/lib/language_operator/cli/commands/use.rb +47 -0
- data/lib/language_operator/cli/errors/handler.rb +180 -0
- data/lib/language_operator/cli/errors/suggestions.rb +176 -0
- data/lib/language_operator/cli/formatters/code_formatter.rb +77 -0
- data/lib/language_operator/cli/formatters/log_formatter.rb +288 -0
- data/lib/language_operator/cli/formatters/progress_formatter.rb +49 -0
- data/lib/language_operator/cli/formatters/status_formatter.rb +37 -0
- data/lib/language_operator/cli/formatters/table_formatter.rb +163 -0
- data/lib/language_operator/cli/formatters/value_formatter.rb +113 -0
- data/lib/language_operator/cli/helpers/cluster_context.rb +62 -0
- data/lib/language_operator/cli/helpers/cluster_validator.rb +101 -0
- data/lib/language_operator/cli/helpers/editor_helper.rb +58 -0
- data/lib/language_operator/cli/helpers/kubeconfig_validator.rb +167 -0
- data/lib/language_operator/cli/helpers/pastel_helper.rb +24 -0
- data/lib/language_operator/cli/helpers/resource_dependency_checker.rb +74 -0
- data/lib/language_operator/cli/helpers/schedule_builder.rb +108 -0
- data/lib/language_operator/cli/helpers/user_prompts.rb +69 -0
- data/lib/language_operator/cli/main.rb +236 -0
- data/lib/language_operator/cli/templates/tools/generic.yaml +66 -0
- data/lib/language_operator/cli/wizards/agent_wizard.rb +246 -0
- data/lib/language_operator/cli/wizards/quickstart_wizard.rb +588 -0
- data/lib/language_operator/client/base.rb +214 -0
- data/lib/language_operator/client/config.rb +136 -0
- data/lib/language_operator/client/cost_calculator.rb +37 -0
- data/lib/language_operator/client/mcp_connector.rb +123 -0
- data/lib/language_operator/client.rb +19 -0
- data/lib/language_operator/config/cluster_config.rb +101 -0
- data/lib/language_operator/config/tool_patterns.yaml +57 -0
- data/lib/language_operator/config/tool_registry.rb +96 -0
- data/lib/language_operator/config.rb +138 -0
- data/lib/language_operator/dsl/adapter.rb +124 -0
- data/lib/language_operator/dsl/agent_context.rb +90 -0
- data/lib/language_operator/dsl/agent_definition.rb +427 -0
- data/lib/language_operator/dsl/chat_endpoint_definition.rb +115 -0
- data/lib/language_operator/dsl/config.rb +119 -0
- data/lib/language_operator/dsl/context.rb +50 -0
- data/lib/language_operator/dsl/execution_context.rb +47 -0
- data/lib/language_operator/dsl/helpers.rb +109 -0
- data/lib/language_operator/dsl/http.rb +184 -0
- data/lib/language_operator/dsl/mcp_server_definition.rb +73 -0
- data/lib/language_operator/dsl/parameter_definition.rb +124 -0
- data/lib/language_operator/dsl/registry.rb +36 -0
- data/lib/language_operator/dsl/schema.rb +1102 -0
- data/lib/language_operator/dsl/shell.rb +125 -0
- data/lib/language_operator/dsl/tool_definition.rb +112 -0
- data/lib/language_operator/dsl/webhook_authentication.rb +114 -0
- data/lib/language_operator/dsl/webhook_definition.rb +106 -0
- data/lib/language_operator/dsl/workflow_definition.rb +259 -0
- data/lib/language_operator/dsl.rb +161 -0
- data/lib/language_operator/errors.rb +60 -0
- data/lib/language_operator/kubernetes/client.rb +279 -0
- data/lib/language_operator/kubernetes/resource_builder.rb +194 -0
- data/lib/language_operator/loggable.rb +47 -0
- data/lib/language_operator/logger.rb +141 -0
- data/lib/language_operator/retry.rb +123 -0
- data/lib/language_operator/retryable.rb +132 -0
- data/lib/language_operator/templates/README.md +23 -0
- data/lib/language_operator/templates/examples/agent_synthesis.tmpl +115 -0
- data/lib/language_operator/templates/examples/persona_distillation.tmpl +19 -0
- data/lib/language_operator/templates/schema/.gitkeep +0 -0
- data/lib/language_operator/templates/schema/CHANGELOG.md +93 -0
- data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +306 -0
- data/lib/language_operator/templates/schema/agent_dsl_schema.json +452 -0
- data/lib/language_operator/tool_loader.rb +242 -0
- data/lib/language_operator/validators.rb +170 -0
- data/lib/language_operator/version.rb +1 -1
- data/lib/language_operator.rb +65 -3
- data/requirements/tasks/challenge.md +9 -0
- data/requirements/tasks/iterate.md +36 -0
- data/requirements/tasks/optimize.md +21 -0
- data/requirements/tasks/tag.md +5 -0
- data/test_agent_dsl.rb +108 -0
- metadata +507 -20
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://github.com/language-operator/language-operator-gem/schema/agent-dsl.json",
|
|
4
|
+
"title": "Language Operator Agent DSL",
|
|
5
|
+
"description": "Schema for defining autonomous AI agents using the Language Operator DSL",
|
|
6
|
+
"version": "0.1.31",
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"name": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "Unique agent identifier (lowercase, alphanumeric, hyphens)",
|
|
12
|
+
"pattern": "^[a-z0-9-]+$",
|
|
13
|
+
"minLength": 1,
|
|
14
|
+
"maxLength": 63
|
|
15
|
+
},
|
|
16
|
+
"description": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "Human-readable description of agent purpose"
|
|
19
|
+
},
|
|
20
|
+
"persona": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "System prompt or persona defining agent behavior and expertise"
|
|
23
|
+
},
|
|
24
|
+
"schedule": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "Cron expression for scheduled execution (sets mode to :scheduled)",
|
|
27
|
+
"pattern": "^\\s*(\\S+\\s+){4}\\S+\\s*$"
|
|
28
|
+
},
|
|
29
|
+
"mode": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "Execution mode for the agent",
|
|
32
|
+
"enum": [
|
|
33
|
+
"autonomous",
|
|
34
|
+
"scheduled",
|
|
35
|
+
"reactive"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"objectives": {
|
|
39
|
+
"type": "array",
|
|
40
|
+
"description": "List of goals the agent should achieve",
|
|
41
|
+
"items": {
|
|
42
|
+
"type": "string"
|
|
43
|
+
},
|
|
44
|
+
"minItems": 0
|
|
45
|
+
},
|
|
46
|
+
"workflow": {
|
|
47
|
+
"$ref": "#/definitions/WorkflowDefinition"
|
|
48
|
+
},
|
|
49
|
+
"constraints": {
|
|
50
|
+
"$ref": "#/definitions/ConstraintsDefinition"
|
|
51
|
+
},
|
|
52
|
+
"output": {
|
|
53
|
+
"$ref": "#/definitions/OutputDefinition"
|
|
54
|
+
},
|
|
55
|
+
"webhooks": {
|
|
56
|
+
"type": "array",
|
|
57
|
+
"description": "Webhook endpoints for reactive agents",
|
|
58
|
+
"items": {
|
|
59
|
+
"$ref": "#/definitions/WebhookDefinition"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"mcp_server": {
|
|
63
|
+
"$ref": "#/definitions/McpServerDefinition"
|
|
64
|
+
},
|
|
65
|
+
"chat_endpoint": {
|
|
66
|
+
"$ref": "#/definitions/ChatEndpointDefinition"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"required": [
|
|
70
|
+
"name"
|
|
71
|
+
],
|
|
72
|
+
"definitions": {
|
|
73
|
+
"WorkflowDefinition": {
|
|
74
|
+
"type": "object",
|
|
75
|
+
"description": "Multi-step workflow with dependencies",
|
|
76
|
+
"properties": {
|
|
77
|
+
"steps": {
|
|
78
|
+
"type": "array",
|
|
79
|
+
"description": "Ordered list of workflow steps",
|
|
80
|
+
"items": {
|
|
81
|
+
"$ref": "#/definitions/StepDefinition"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"StepDefinition": {
|
|
87
|
+
"type": "object",
|
|
88
|
+
"description": "Individual workflow step",
|
|
89
|
+
"properties": {
|
|
90
|
+
"name": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"description": "Step identifier (symbol or string)"
|
|
93
|
+
},
|
|
94
|
+
"tool": {
|
|
95
|
+
"type": "string",
|
|
96
|
+
"description": "Tool name to execute in this step"
|
|
97
|
+
},
|
|
98
|
+
"params": {
|
|
99
|
+
"type": "object",
|
|
100
|
+
"description": "Parameters to pass to the tool",
|
|
101
|
+
"additionalProperties": true
|
|
102
|
+
},
|
|
103
|
+
"depends_on": {
|
|
104
|
+
"oneOf": [
|
|
105
|
+
{
|
|
106
|
+
"type": "string"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"type": "array",
|
|
110
|
+
"items": {
|
|
111
|
+
"type": "string"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
],
|
|
115
|
+
"description": "Step dependencies (must complete before this step)"
|
|
116
|
+
},
|
|
117
|
+
"prompt": {
|
|
118
|
+
"type": "string",
|
|
119
|
+
"description": "LLM prompt template for this step"
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"required": [
|
|
123
|
+
"name"
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
"ConstraintsDefinition": {
|
|
127
|
+
"type": "object",
|
|
128
|
+
"description": "Execution constraints and limits",
|
|
129
|
+
"properties": {
|
|
130
|
+
"max_iterations": {
|
|
131
|
+
"type": "integer",
|
|
132
|
+
"description": "Maximum number of execution iterations",
|
|
133
|
+
"minimum": 1
|
|
134
|
+
},
|
|
135
|
+
"timeout": {
|
|
136
|
+
"type": "string",
|
|
137
|
+
"description": "Execution timeout (e.g., \"30s\", \"5m\", \"1h\")",
|
|
138
|
+
"pattern": "^\\d+[smh]$"
|
|
139
|
+
},
|
|
140
|
+
"memory": {
|
|
141
|
+
"type": "string",
|
|
142
|
+
"description": "Memory limit (e.g., \"512Mi\", \"1Gi\")"
|
|
143
|
+
},
|
|
144
|
+
"rate_limit": {
|
|
145
|
+
"type": "integer",
|
|
146
|
+
"description": "Maximum requests per time period",
|
|
147
|
+
"minimum": 1
|
|
148
|
+
},
|
|
149
|
+
"daily_budget": {
|
|
150
|
+
"type": "number",
|
|
151
|
+
"description": "Maximum daily cost in USD",
|
|
152
|
+
"minimum": 0
|
|
153
|
+
},
|
|
154
|
+
"hourly_budget": {
|
|
155
|
+
"type": "number",
|
|
156
|
+
"description": "Maximum hourly cost in USD",
|
|
157
|
+
"minimum": 0
|
|
158
|
+
},
|
|
159
|
+
"token_budget": {
|
|
160
|
+
"type": "integer",
|
|
161
|
+
"description": "Maximum total tokens allowed",
|
|
162
|
+
"minimum": 1
|
|
163
|
+
},
|
|
164
|
+
"requests_per_minute": {
|
|
165
|
+
"type": "integer",
|
|
166
|
+
"description": "Maximum requests per minute",
|
|
167
|
+
"minimum": 1
|
|
168
|
+
},
|
|
169
|
+
"requests_per_hour": {
|
|
170
|
+
"type": "integer",
|
|
171
|
+
"description": "Maximum requests per hour",
|
|
172
|
+
"minimum": 1
|
|
173
|
+
},
|
|
174
|
+
"requests_per_day": {
|
|
175
|
+
"type": "integer",
|
|
176
|
+
"description": "Maximum requests per day",
|
|
177
|
+
"minimum": 1
|
|
178
|
+
},
|
|
179
|
+
"blocked_patterns": {
|
|
180
|
+
"type": "array",
|
|
181
|
+
"description": "Content patterns to block",
|
|
182
|
+
"items": {
|
|
183
|
+
"type": "string"
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
"blocked_topics": {
|
|
187
|
+
"type": "array",
|
|
188
|
+
"description": "Topics to avoid",
|
|
189
|
+
"items": {
|
|
190
|
+
"type": "string"
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
"OutputDefinition": {
|
|
196
|
+
"type": "object",
|
|
197
|
+
"description": "Output destination configuration",
|
|
198
|
+
"properties": {
|
|
199
|
+
"workspace": {
|
|
200
|
+
"type": "string",
|
|
201
|
+
"description": "Workspace directory path for file outputs"
|
|
202
|
+
},
|
|
203
|
+
"slack": {
|
|
204
|
+
"type": "object",
|
|
205
|
+
"description": "Slack integration configuration",
|
|
206
|
+
"properties": {
|
|
207
|
+
"channel": {
|
|
208
|
+
"type": "string",
|
|
209
|
+
"description": "Slack channel name or ID"
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
"required": [
|
|
213
|
+
"channel"
|
|
214
|
+
]
|
|
215
|
+
},
|
|
216
|
+
"email": {
|
|
217
|
+
"type": "object",
|
|
218
|
+
"description": "Email notification configuration",
|
|
219
|
+
"properties": {
|
|
220
|
+
"to": {
|
|
221
|
+
"type": "string",
|
|
222
|
+
"description": "Email recipient address",
|
|
223
|
+
"format": "email"
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
"required": [
|
|
227
|
+
"to"
|
|
228
|
+
]
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
"WebhookDefinition": {
|
|
233
|
+
"type": "object",
|
|
234
|
+
"description": "Webhook endpoint configuration",
|
|
235
|
+
"properties": {
|
|
236
|
+
"path": {
|
|
237
|
+
"type": "string",
|
|
238
|
+
"description": "URL path for webhook endpoint",
|
|
239
|
+
"pattern": "^/"
|
|
240
|
+
},
|
|
241
|
+
"method": {
|
|
242
|
+
"type": "string",
|
|
243
|
+
"description": "HTTP method",
|
|
244
|
+
"enum": [
|
|
245
|
+
"get",
|
|
246
|
+
"post",
|
|
247
|
+
"put",
|
|
248
|
+
"delete",
|
|
249
|
+
"patch"
|
|
250
|
+
],
|
|
251
|
+
"default": "post"
|
|
252
|
+
},
|
|
253
|
+
"authentication": {
|
|
254
|
+
"$ref": "#/definitions/WebhookAuthentication"
|
|
255
|
+
},
|
|
256
|
+
"validations": {
|
|
257
|
+
"type": "array",
|
|
258
|
+
"description": "Request validation rules",
|
|
259
|
+
"items": {
|
|
260
|
+
"type": "object",
|
|
261
|
+
"properties": {
|
|
262
|
+
"type": {
|
|
263
|
+
"type": "string",
|
|
264
|
+
"enum": [
|
|
265
|
+
"headers",
|
|
266
|
+
"content_type",
|
|
267
|
+
"custom"
|
|
268
|
+
]
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
"required": [
|
|
275
|
+
"path"
|
|
276
|
+
]
|
|
277
|
+
},
|
|
278
|
+
"WebhookAuthentication": {
|
|
279
|
+
"type": "object",
|
|
280
|
+
"description": "Webhook authentication configuration",
|
|
281
|
+
"properties": {
|
|
282
|
+
"type": {
|
|
283
|
+
"type": "string",
|
|
284
|
+
"description": "Authentication type",
|
|
285
|
+
"enum": [
|
|
286
|
+
"hmac",
|
|
287
|
+
"api_key",
|
|
288
|
+
"bearer",
|
|
289
|
+
"custom"
|
|
290
|
+
]
|
|
291
|
+
},
|
|
292
|
+
"secret": {
|
|
293
|
+
"type": "string",
|
|
294
|
+
"description": "Secret key for authentication"
|
|
295
|
+
},
|
|
296
|
+
"header": {
|
|
297
|
+
"type": "string",
|
|
298
|
+
"description": "Header name containing signature/token"
|
|
299
|
+
},
|
|
300
|
+
"algorithm": {
|
|
301
|
+
"type": "string",
|
|
302
|
+
"description": "HMAC algorithm",
|
|
303
|
+
"enum": [
|
|
304
|
+
"sha1",
|
|
305
|
+
"sha256",
|
|
306
|
+
"sha512"
|
|
307
|
+
]
|
|
308
|
+
},
|
|
309
|
+
"prefix": {
|
|
310
|
+
"type": "string",
|
|
311
|
+
"description": "Signature prefix (e.g., \"sha256=\")"
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
"McpServerDefinition": {
|
|
316
|
+
"type": "object",
|
|
317
|
+
"description": "MCP (Model Context Protocol) server configuration",
|
|
318
|
+
"properties": {
|
|
319
|
+
"name": {
|
|
320
|
+
"type": "string",
|
|
321
|
+
"description": "MCP server name"
|
|
322
|
+
},
|
|
323
|
+
"tools": {
|
|
324
|
+
"type": "object",
|
|
325
|
+
"description": "Tools exposed via MCP",
|
|
326
|
+
"additionalProperties": {
|
|
327
|
+
"$ref": "#/definitions/ToolDefinition"
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
"ChatEndpointDefinition": {
|
|
333
|
+
"type": "object",
|
|
334
|
+
"description": "OpenAI-compatible chat endpoint configuration",
|
|
335
|
+
"properties": {
|
|
336
|
+
"system_prompt": {
|
|
337
|
+
"type": "string",
|
|
338
|
+
"description": "System prompt for chat mode"
|
|
339
|
+
},
|
|
340
|
+
"temperature": {
|
|
341
|
+
"type": "number",
|
|
342
|
+
"description": "Sampling temperature (0.0-2.0)",
|
|
343
|
+
"minimum": 0.0,
|
|
344
|
+
"maximum": 2.0,
|
|
345
|
+
"default": 0.7
|
|
346
|
+
},
|
|
347
|
+
"max_tokens": {
|
|
348
|
+
"type": "integer",
|
|
349
|
+
"description": "Maximum tokens in response",
|
|
350
|
+
"minimum": 1,
|
|
351
|
+
"default": 2000
|
|
352
|
+
},
|
|
353
|
+
"model_name": {
|
|
354
|
+
"type": "string",
|
|
355
|
+
"description": "Model name exposed in API"
|
|
356
|
+
},
|
|
357
|
+
"top_p": {
|
|
358
|
+
"type": "number",
|
|
359
|
+
"description": "Nucleus sampling parameter (0.0-1.0)",
|
|
360
|
+
"minimum": 0.0,
|
|
361
|
+
"maximum": 1.0,
|
|
362
|
+
"default": 1.0
|
|
363
|
+
},
|
|
364
|
+
"frequency_penalty": {
|
|
365
|
+
"type": "number",
|
|
366
|
+
"description": "Frequency penalty (-2.0 to 2.0)",
|
|
367
|
+
"minimum": -2.0,
|
|
368
|
+
"maximum": 2.0,
|
|
369
|
+
"default": 0.0
|
|
370
|
+
},
|
|
371
|
+
"presence_penalty": {
|
|
372
|
+
"type": "number",
|
|
373
|
+
"description": "Presence penalty (-2.0 to 2.0)",
|
|
374
|
+
"minimum": -2.0,
|
|
375
|
+
"maximum": 2.0,
|
|
376
|
+
"default": 0.0
|
|
377
|
+
},
|
|
378
|
+
"stop_sequences": {
|
|
379
|
+
"type": "array",
|
|
380
|
+
"description": "Sequences that stop generation",
|
|
381
|
+
"items": {
|
|
382
|
+
"type": "string"
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
"ToolDefinition": {
|
|
388
|
+
"type": "object",
|
|
389
|
+
"description": "MCP tool definition",
|
|
390
|
+
"properties": {
|
|
391
|
+
"name": {
|
|
392
|
+
"type": "string",
|
|
393
|
+
"description": "Tool name (lowercase, alphanumeric, underscores)",
|
|
394
|
+
"pattern": "^[a-z0-9_]+$"
|
|
395
|
+
},
|
|
396
|
+
"description": {
|
|
397
|
+
"type": "string",
|
|
398
|
+
"description": "Human-readable tool description"
|
|
399
|
+
},
|
|
400
|
+
"parameters": {
|
|
401
|
+
"type": "object",
|
|
402
|
+
"description": "Tool parameters",
|
|
403
|
+
"additionalProperties": {
|
|
404
|
+
"$ref": "#/definitions/ParameterDefinition"
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
"required": [
|
|
409
|
+
"name",
|
|
410
|
+
"description"
|
|
411
|
+
]
|
|
412
|
+
},
|
|
413
|
+
"ParameterDefinition": {
|
|
414
|
+
"type": "object",
|
|
415
|
+
"description": "Tool parameter definition",
|
|
416
|
+
"properties": {
|
|
417
|
+
"type": {
|
|
418
|
+
"type": "string",
|
|
419
|
+
"description": "Parameter type",
|
|
420
|
+
"enum": [
|
|
421
|
+
"string",
|
|
422
|
+
"number",
|
|
423
|
+
"integer",
|
|
424
|
+
"boolean",
|
|
425
|
+
"array",
|
|
426
|
+
"object"
|
|
427
|
+
]
|
|
428
|
+
},
|
|
429
|
+
"description": {
|
|
430
|
+
"type": "string",
|
|
431
|
+
"description": "Parameter description"
|
|
432
|
+
},
|
|
433
|
+
"required": {
|
|
434
|
+
"type": "boolean",
|
|
435
|
+
"description": "Whether parameter is required",
|
|
436
|
+
"default": false
|
|
437
|
+
},
|
|
438
|
+
"default": {
|
|
439
|
+
"description": "Default value if not provided"
|
|
440
|
+
},
|
|
441
|
+
"enum": {
|
|
442
|
+
"type": "array",
|
|
443
|
+
"description": "Allowed values",
|
|
444
|
+
"items": {}
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
"required": [
|
|
448
|
+
"type"
|
|
449
|
+
]
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'dsl'
|
|
4
|
+
require 'mcp'
|
|
5
|
+
require 'opentelemetry/sdk'
|
|
6
|
+
|
|
7
|
+
module LanguageOperator
|
|
8
|
+
# Loads tool definitions from Ruby files
|
|
9
|
+
#
|
|
10
|
+
# Scans a directory for Ruby files containing tool definitions and loads them
|
|
11
|
+
# into a registry. Provides hot-reloading capability for development.
|
|
12
|
+
#
|
|
13
|
+
# @example Basic usage
|
|
14
|
+
# registry = LanguageOperator::Dsl::Registry.new
|
|
15
|
+
# loader = LanguageOperator::ToolLoader.new(registry, '/mcp/tools')
|
|
16
|
+
# loader.load_tools
|
|
17
|
+
# puts "Loaded #{registry.all.length} tools"
|
|
18
|
+
#
|
|
19
|
+
# @example With custom context
|
|
20
|
+
# loader = LanguageOperator::ToolLoader.new(registry)
|
|
21
|
+
# loader.load_tools
|
|
22
|
+
# loader.reload # Hot reload tools
|
|
23
|
+
#
|
|
24
|
+
# @example Start MCP server
|
|
25
|
+
# LanguageOperator::ToolLoader.start # Loads tools and starts MCP server
|
|
26
|
+
class ToolLoader
|
|
27
|
+
# Initialize tool loader
|
|
28
|
+
#
|
|
29
|
+
# @param registry [LanguageOperator::Dsl::Registry] Tool registry
|
|
30
|
+
# @param tools_dir [String] Directory containing tool definition files
|
|
31
|
+
def initialize(registry, tools_dir = '/mcp')
|
|
32
|
+
@registry = registry
|
|
33
|
+
@tools_dir = tools_dir
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Load all tool files from the tools directory
|
|
37
|
+
#
|
|
38
|
+
# @return [void]
|
|
39
|
+
def load_tools
|
|
40
|
+
@registry.clear
|
|
41
|
+
|
|
42
|
+
unless Dir.exist?(@tools_dir)
|
|
43
|
+
puts "Tools directory #{@tools_dir} does not exist. Skipping tool loading."
|
|
44
|
+
return
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
tool_files = Dir.glob(File.join(@tools_dir, '**', '*.rb'))
|
|
48
|
+
|
|
49
|
+
if tool_files.empty?
|
|
50
|
+
puts "No tool files found in #{@tools_dir}"
|
|
51
|
+
return
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
tool_files.each do |file|
|
|
55
|
+
load_tool_file(file)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
puts "Loaded #{@registry.all.length} tools from #{tool_files.length} files"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Load a single tool file
|
|
62
|
+
#
|
|
63
|
+
# @param file [String] Path to tool definition file
|
|
64
|
+
# @return [void]
|
|
65
|
+
def load_tool_file(file)
|
|
66
|
+
puts "Loading tools from: #{file}"
|
|
67
|
+
|
|
68
|
+
begin
|
|
69
|
+
context = LanguageOperator::Dsl::Context.new(@registry)
|
|
70
|
+
code = File.read(file)
|
|
71
|
+
|
|
72
|
+
# Execute in sandbox with validation
|
|
73
|
+
executor = LanguageOperator::Agent::Safety::SafeExecutor.new(context)
|
|
74
|
+
executor.eval(code, file)
|
|
75
|
+
rescue Agent::Safety::SafeExecutor::SecurityError, Agent::Safety::ASTValidator::SecurityError => e
|
|
76
|
+
# Re-raise security errors so they're not silently ignored
|
|
77
|
+
warn "Error loading tool file #{file}: #{e.message}"
|
|
78
|
+
warn e.backtrace.join("\n")
|
|
79
|
+
raise e
|
|
80
|
+
rescue StandardError => e
|
|
81
|
+
warn "Error loading tool file #{file}: #{e.message}"
|
|
82
|
+
warn e.backtrace.join("\n")
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Reload all tools (hot reload)
|
|
87
|
+
#
|
|
88
|
+
# @return [void]
|
|
89
|
+
def reload
|
|
90
|
+
puts 'Reloading tools...'
|
|
91
|
+
load_tools
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Start an MCP server with loaded tools
|
|
95
|
+
#
|
|
96
|
+
# This class method creates a registry, loads tools from /mcp directory,
|
|
97
|
+
# wraps them as MCP::Tool classes, and starts an MCP server.
|
|
98
|
+
#
|
|
99
|
+
# Transport mode is automatically detected:
|
|
100
|
+
# - If PORT environment variable is set: HTTP server mode (for Kubernetes)
|
|
101
|
+
# - Otherwise: stdio transport mode (for local development)
|
|
102
|
+
#
|
|
103
|
+
# @param tools_dir [String] Directory containing tool definition files (default: '/mcp')
|
|
104
|
+
# @param server_name [String] Name of the MCP server (default: 'language-operator-tool')
|
|
105
|
+
# @return [void]
|
|
106
|
+
def self.start(tools_dir: '/mcp', server_name: 'language-operator-tool')
|
|
107
|
+
# Create registry and load tools
|
|
108
|
+
registry = LanguageOperator::Dsl::Registry.new
|
|
109
|
+
loader = new(registry, tools_dir)
|
|
110
|
+
loader.load_tools
|
|
111
|
+
|
|
112
|
+
# Convert DSL tools to MCP::Tool classes
|
|
113
|
+
mcp_tools = registry.all.map do |tool_def|
|
|
114
|
+
create_mcp_tool(tool_def)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Create MCP server
|
|
118
|
+
server = MCP::Server.new(
|
|
119
|
+
name: server_name,
|
|
120
|
+
tools: mcp_tools
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Auto-detect transport mode based on PORT environment variable
|
|
124
|
+
if ENV['PORT']
|
|
125
|
+
start_http_server(server, mcp_tools.length, ENV['PORT'].to_i)
|
|
126
|
+
else
|
|
127
|
+
start_stdio_server(server, mcp_tools.length)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Start MCP server in HTTP mode
|
|
132
|
+
#
|
|
133
|
+
# @param server [MCP::Server] The MCP server instance
|
|
134
|
+
# @param tool_count [Integer] Number of tools loaded
|
|
135
|
+
# @param port [Integer] Port to bind to
|
|
136
|
+
# @return [void]
|
|
137
|
+
def self.start_http_server(server, tool_count, port)
|
|
138
|
+
require 'rack'
|
|
139
|
+
require 'rackup'
|
|
140
|
+
|
|
141
|
+
# Create the Streamable HTTP transport
|
|
142
|
+
transport = MCP::Server::Transports::StreamableHTTPTransport.new(server)
|
|
143
|
+
server.transport = transport
|
|
144
|
+
|
|
145
|
+
# Create the Rack application
|
|
146
|
+
app = proc do |env|
|
|
147
|
+
request = Rack::Request.new(env)
|
|
148
|
+
transport.handle_request(request)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Build the Rack application with middleware
|
|
152
|
+
rack_app = Rack::Builder.new do
|
|
153
|
+
use Rack::CommonLogger
|
|
154
|
+
use Rack::ShowExceptions
|
|
155
|
+
run app
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
puts "Starting MCP HTTP server on http://0.0.0.0:#{port}"
|
|
159
|
+
puts "Loaded #{tool_count} tools"
|
|
160
|
+
|
|
161
|
+
# Start the server with Puma
|
|
162
|
+
Rackup::Handler.get('puma').run(rack_app, Port: port, Host: '0.0.0.0')
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Start MCP server in stdio mode
|
|
166
|
+
#
|
|
167
|
+
# @param server [MCP::Server] The MCP server instance
|
|
168
|
+
# @param tool_count [Integer] Number of tools loaded
|
|
169
|
+
# @return [void]
|
|
170
|
+
def self.start_stdio_server(server, tool_count)
|
|
171
|
+
# Use stdio transport
|
|
172
|
+
transport = MCP::Server::Transports::StdioTransport.new(server)
|
|
173
|
+
puts "Starting MCP server with #{tool_count} tools (stdio mode)"
|
|
174
|
+
transport.open
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Convert a DSL tool definition to an MCP::Tool class
|
|
178
|
+
#
|
|
179
|
+
# @param tool_def [LanguageOperator::Dsl::ToolDefinition] Tool definition from DSL
|
|
180
|
+
# @return [Class] MCP::Tool subclass
|
|
181
|
+
# rubocop:disable Metrics/MethodLength
|
|
182
|
+
def self.create_mcp_tool(tool_def)
|
|
183
|
+
# Capture tool name and tracer for use in the dynamic class
|
|
184
|
+
tool_name = tool_def.name
|
|
185
|
+
tracer = OpenTelemetry.tracer_provider.tracer('language-operator-agent', LanguageOperator::VERSION)
|
|
186
|
+
|
|
187
|
+
# Create a dynamic MCP::Tool class
|
|
188
|
+
Class.new(MCP::Tool) do
|
|
189
|
+
description tool_def.description || "Tool: #{tool_def.name}"
|
|
190
|
+
|
|
191
|
+
# Build input schema from parameters
|
|
192
|
+
properties = {}
|
|
193
|
+
required_params = []
|
|
194
|
+
|
|
195
|
+
tool_def.parameters.each do |param_name, param_def|
|
|
196
|
+
properties[param_name] = {
|
|
197
|
+
type: param_def.type&.to_s || 'string',
|
|
198
|
+
description: param_def.description
|
|
199
|
+
}
|
|
200
|
+
required_params << param_name if param_def.required?
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
input_schema(
|
|
204
|
+
properties: properties,
|
|
205
|
+
required: required_params
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# Store the execute block
|
|
209
|
+
@execute_block = tool_def.execute_block
|
|
210
|
+
|
|
211
|
+
# Define the call method with OpenTelemetry instrumentation
|
|
212
|
+
define_singleton_method(:call) do |**params|
|
|
213
|
+
tracer.in_span('agent.tool.execute', attributes: {
|
|
214
|
+
'tool.name' => tool_name,
|
|
215
|
+
'tool.type' => 'custom'
|
|
216
|
+
}) do |span|
|
|
217
|
+
# Execute the tool's block
|
|
218
|
+
result = @execute_block.call(params)
|
|
219
|
+
|
|
220
|
+
# Set success attribute
|
|
221
|
+
span.set_attribute('tool.result', 'success')
|
|
222
|
+
|
|
223
|
+
# Return MCP response
|
|
224
|
+
MCP::Tool::Response.new([
|
|
225
|
+
{
|
|
226
|
+
type: 'text',
|
|
227
|
+
text: result.to_s
|
|
228
|
+
}
|
|
229
|
+
])
|
|
230
|
+
rescue StandardError => e
|
|
231
|
+
# Record exception and set failure status
|
|
232
|
+
span.record_exception(e)
|
|
233
|
+
span.set_attribute('tool.result', 'failure')
|
|
234
|
+
span.status = OpenTelemetry::Trace::Status.error(e.message)
|
|
235
|
+
raise
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
# rubocop:enable Metrics/MethodLength
|
|
241
|
+
end
|
|
242
|
+
end
|