language-operator 0.0.1 → 0.1.30
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 +53 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +284 -0
- data/LICENSE +229 -21
- data/Makefile +77 -0
- data/README.md +3 -11
- data/Rakefile +34 -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/agent-reference.md +591 -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/examples/README.md +569 -0
- data/examples/agent_example.rb +86 -0
- data/examples/chat_endpoint_agent.rb +118 -0
- data/examples/github_webhook_agent.rb +171 -0
- data/examples/mcp_agent.rb +158 -0
- data/examples/oauth_callback_agent.rb +296 -0
- data/examples/stripe_webhook_agent.rb +219 -0
- data/examples/webhook_agent.rb +80 -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 +115 -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 +1252 -0
- data/lib/language_operator/cli/commands/cluster.rb +335 -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 +396 -0
- data/lib/language_operator/cli/commands/quickstart.rb +22 -0
- data/lib/language_operator/cli/commands/status.rb +156 -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 +81 -0
- data/lib/language_operator/cli/formatters/log_formatter.rb +290 -0
- data/lib/language_operator/cli/formatters/progress_formatter.rb +53 -0
- data/lib/language_operator/cli/formatters/table_formatter.rb +179 -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/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 +232 -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/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 +160 -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/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 +503 -20
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example agent that exposes an OpenAI-compatible chat completion endpoint
|
|
5
|
+
#
|
|
6
|
+
# This demonstrates Phase 4: Agents as Chat Completion Endpoints
|
|
7
|
+
# The agent can be used as a drop-in replacement for OpenAI's API
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# PORT=8080 ruby examples/chat_endpoint_agent.rb
|
|
11
|
+
#
|
|
12
|
+
# Test non-streaming:
|
|
13
|
+
# curl -X POST http://localhost:8080/v1/chat/completions \
|
|
14
|
+
# -H "Content-Type: application/json" \
|
|
15
|
+
# -d '{
|
|
16
|
+
# "model": "github-expert",
|
|
17
|
+
# "messages": [{"role": "user", "content": "How do I create a PR?"}]
|
|
18
|
+
# }'
|
|
19
|
+
#
|
|
20
|
+
# Test streaming:
|
|
21
|
+
# curl -X POST http://localhost:8080/v1/chat/completions \
|
|
22
|
+
# -H "Content-Type: application/json" \
|
|
23
|
+
# -d '{
|
|
24
|
+
# "model": "github-expert",
|
|
25
|
+
# "messages": [{"role": "user", "content": "How do I create a PR?"}],
|
|
26
|
+
# "stream": true
|
|
27
|
+
# }'
|
|
28
|
+
#
|
|
29
|
+
# Test with OpenAI SDK:
|
|
30
|
+
# require 'openai'
|
|
31
|
+
# client = OpenAI::Client.new(
|
|
32
|
+
# access_token: "not-needed",
|
|
33
|
+
# uri_base: "http://localhost:8080/v1/"
|
|
34
|
+
# )
|
|
35
|
+
# response = client.chat(
|
|
36
|
+
# parameters: {
|
|
37
|
+
# model: "github-expert",
|
|
38
|
+
# messages: [{role: "user", content: "How do I create a PR?"}]
|
|
39
|
+
# }
|
|
40
|
+
# )
|
|
41
|
+
|
|
42
|
+
require 'bundler/setup'
|
|
43
|
+
require 'language_operator'
|
|
44
|
+
require 'language_operator/dsl'
|
|
45
|
+
|
|
46
|
+
# Define an agent that acts as a GitHub expert LLM
|
|
47
|
+
LanguageOperator::Dsl.define do
|
|
48
|
+
agent 'github-expert' do
|
|
49
|
+
description 'GitHub API and workflow expert'
|
|
50
|
+
mode :reactive
|
|
51
|
+
|
|
52
|
+
# Configure as an OpenAI-compatible chat endpoint
|
|
53
|
+
as_chat_endpoint do
|
|
54
|
+
system_prompt <<~PROMPT
|
|
55
|
+
You are a GitHub expert assistant with deep knowledge of:
|
|
56
|
+
- GitHub API and workflows
|
|
57
|
+
- Pull requests, issues, and code review
|
|
58
|
+
- GitHub Actions and CI/CD
|
|
59
|
+
- Repository management and best practices
|
|
60
|
+
|
|
61
|
+
Provide helpful, accurate answers about GitHub topics.
|
|
62
|
+
Keep responses concise but informative.
|
|
63
|
+
PROMPT
|
|
64
|
+
|
|
65
|
+
# Model parameters
|
|
66
|
+
model 'github-expert-v1'
|
|
67
|
+
temperature 0.7
|
|
68
|
+
max_tokens 2000
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Optionally, also expose as webhook
|
|
72
|
+
webhook '/github/webhook' do
|
|
73
|
+
method :post
|
|
74
|
+
on_request do |_context|
|
|
75
|
+
{
|
|
76
|
+
status: 'received',
|
|
77
|
+
message: 'GitHub webhook processed',
|
|
78
|
+
chat_endpoint: '/v1/chat/completions'
|
|
79
|
+
}
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
# Get the agent definition and run it
|
|
85
|
+
agent_def = LanguageOperator::Dsl.agent_registry.get('github-expert')
|
|
86
|
+
|
|
87
|
+
if agent_def
|
|
88
|
+
puts "Starting GitHub Expert agent on port #{ENV.fetch('PORT', '8080')}"
|
|
89
|
+
puts
|
|
90
|
+
puts 'OpenAI-Compatible Endpoints:'
|
|
91
|
+
puts ' POST /v1/chat/completions - Chat completion (streaming & non-streaming)'
|
|
92
|
+
puts ' GET /v1/models - List available models'
|
|
93
|
+
puts
|
|
94
|
+
puts 'Additional Endpoints:'
|
|
95
|
+
puts ' GET /health - Health check'
|
|
96
|
+
puts ' GET /ready - Readiness check'
|
|
97
|
+
puts ' POST /github/webhook - GitHub webhook endpoint'
|
|
98
|
+
puts
|
|
99
|
+
puts 'Test with curl:'
|
|
100
|
+
puts <<~CURL
|
|
101
|
+
curl -X POST http://localhost:8080/v1/chat/completions \\
|
|
102
|
+
-H "Content-Type: application/json" \\
|
|
103
|
+
-d '{"model": "github-expert-v1", "messages": [{"role": "user", "content": "How do I create a PR?"}]}'
|
|
104
|
+
CURL
|
|
105
|
+
puts
|
|
106
|
+
puts 'Test streaming:'
|
|
107
|
+
puts <<~CURL
|
|
108
|
+
curl -N -X POST http://localhost:8080/v1/chat/completions \\
|
|
109
|
+
-H "Content-Type: application/json" \\
|
|
110
|
+
-d '{"model": "github-expert-v1", "messages": [{"role": "user", "content": "Explain GitHub Actions"}], "stream": true}'
|
|
111
|
+
CURL
|
|
112
|
+
puts "\nPress Ctrl+C to stop\n\n"
|
|
113
|
+
|
|
114
|
+
agent_def.run!
|
|
115
|
+
else
|
|
116
|
+
puts 'Error: Agent definition not found'
|
|
117
|
+
exit 1
|
|
118
|
+
end
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# GitHub Webhook Agent Example
|
|
5
|
+
#
|
|
6
|
+
# This example shows how to create an agent that receives GitHub webhook events
|
|
7
|
+
# with proper signature verification for security.
|
|
8
|
+
#
|
|
9
|
+
# Setup:
|
|
10
|
+
# 1. Set GITHUB_WEBHOOK_SECRET environment variable
|
|
11
|
+
# 2. Configure GitHub webhook to send events to https://<agent-url>/github/events
|
|
12
|
+
# 3. Select events you want to receive (e.g., pull_request, issues, push)
|
|
13
|
+
#
|
|
14
|
+
# GitHub webhook documentation:
|
|
15
|
+
# https://docs.github.com/en/webhooks
|
|
16
|
+
|
|
17
|
+
require 'bundler/setup'
|
|
18
|
+
require 'language_operator'
|
|
19
|
+
|
|
20
|
+
LanguageOperator::Dsl.define_agents do
|
|
21
|
+
agent 'github-pr-reviewer' do
|
|
22
|
+
description 'Automatically reviews pull requests and provides feedback'
|
|
23
|
+
|
|
24
|
+
# Set to reactive mode to receive webhooks
|
|
25
|
+
mode :reactive
|
|
26
|
+
|
|
27
|
+
# GitHub Pull Request webhook
|
|
28
|
+
webhook '/github/pull_request' do
|
|
29
|
+
method :post
|
|
30
|
+
|
|
31
|
+
# Verify GitHub webhook signature
|
|
32
|
+
# GitHub sends signature in X-Hub-Signature-256 header
|
|
33
|
+
# Format: "sha256=<signature>"
|
|
34
|
+
authenticate do
|
|
35
|
+
verify_signature(
|
|
36
|
+
header: 'X-Hub-Signature-256',
|
|
37
|
+
secret: ENV.fetch('GITHUB_WEBHOOK_SECRET', nil),
|
|
38
|
+
algorithm: :sha256,
|
|
39
|
+
prefix: 'sha256='
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Validate request format
|
|
44
|
+
require_content_type 'application/json'
|
|
45
|
+
require_headers(
|
|
46
|
+
'X-GitHub-Event' => nil, # Just check presence, any value OK
|
|
47
|
+
'X-GitHub-Delivery' => nil
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
on_request do |context|
|
|
51
|
+
event = JSON.parse(context[:body])
|
|
52
|
+
action = event['action']
|
|
53
|
+
pr = event['pull_request']
|
|
54
|
+
|
|
55
|
+
case action
|
|
56
|
+
when 'opened'
|
|
57
|
+
handle_pr_opened(pr, context)
|
|
58
|
+
when 'synchronize'
|
|
59
|
+
handle_pr_updated(pr, context)
|
|
60
|
+
when 'closed'
|
|
61
|
+
handle_pr_closed(pr, context)
|
|
62
|
+
else
|
|
63
|
+
{ status: 'ignored', action: action }
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# GitHub Issue webhook
|
|
69
|
+
webhook '/github/issues' do
|
|
70
|
+
method :post
|
|
71
|
+
|
|
72
|
+
authenticate do
|
|
73
|
+
verify_signature(
|
|
74
|
+
header: 'X-Hub-Signature-256',
|
|
75
|
+
secret: ENV.fetch('GITHUB_WEBHOOK_SECRET', nil),
|
|
76
|
+
algorithm: :sha256,
|
|
77
|
+
prefix: 'sha256='
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
require_content_type 'application/json'
|
|
82
|
+
|
|
83
|
+
on_request do |context|
|
|
84
|
+
event = JSON.parse(context[:body])
|
|
85
|
+
action = event['action']
|
|
86
|
+
issue = event['issue']
|
|
87
|
+
|
|
88
|
+
case action
|
|
89
|
+
when 'opened'
|
|
90
|
+
{
|
|
91
|
+
status: 'processed',
|
|
92
|
+
message: "New issue: #{issue['title']}",
|
|
93
|
+
issue_number: issue['number']
|
|
94
|
+
}
|
|
95
|
+
when 'labeled'
|
|
96
|
+
{
|
|
97
|
+
status: 'processed',
|
|
98
|
+
message: "Issue labeled: #{event['label']['name']}"
|
|
99
|
+
}
|
|
100
|
+
else
|
|
101
|
+
{ status: 'ignored', action: action }
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# GitHub Push webhook
|
|
107
|
+
webhook '/github/push' do
|
|
108
|
+
method :post
|
|
109
|
+
|
|
110
|
+
authenticate do
|
|
111
|
+
verify_signature(
|
|
112
|
+
header: 'X-Hub-Signature-256',
|
|
113
|
+
secret: ENV.fetch('GITHUB_WEBHOOK_SECRET', nil),
|
|
114
|
+
algorithm: :sha256,
|
|
115
|
+
prefix: 'sha256='
|
|
116
|
+
)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
require_content_type 'application/json'
|
|
120
|
+
|
|
121
|
+
on_request do |context|
|
|
122
|
+
event = JSON.parse(context[:body])
|
|
123
|
+
ref = event['ref']
|
|
124
|
+
commits = event['commits']
|
|
125
|
+
|
|
126
|
+
{
|
|
127
|
+
status: 'processed',
|
|
128
|
+
message: "Received #{commits.length} commits on #{ref}",
|
|
129
|
+
commits: commits.map { |c| c['message'] }
|
|
130
|
+
}
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Helper methods for PR handling
|
|
137
|
+
def handle_pr_opened(pr, _context)
|
|
138
|
+
{
|
|
139
|
+
status: 'processed',
|
|
140
|
+
action: 'pr_opened',
|
|
141
|
+
pr_number: pr['number'],
|
|
142
|
+
pr_title: pr['title'],
|
|
143
|
+
pr_url: pr['html_url'],
|
|
144
|
+
message: "Reviewing PR ##{pr['number']}: #{pr['title']}"
|
|
145
|
+
}
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def handle_pr_updated(pr, _context)
|
|
149
|
+
{
|
|
150
|
+
status: 'processed',
|
|
151
|
+
action: 'pr_updated',
|
|
152
|
+
pr_number: pr['number'],
|
|
153
|
+
message: "PR ##{pr['number']} was updated"
|
|
154
|
+
}
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def handle_pr_closed(pr, _context)
|
|
158
|
+
{
|
|
159
|
+
status: 'processed',
|
|
160
|
+
action: 'pr_closed',
|
|
161
|
+
pr_number: pr['number'],
|
|
162
|
+
merged: pr['merged'],
|
|
163
|
+
message: "PR ##{pr['number']} was #{pr['merged'] ? 'merged' : 'closed'}"
|
|
164
|
+
}
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Run the agent if this file is executed directly
|
|
168
|
+
if __FILE__ == $PROGRAM_NAME
|
|
169
|
+
agent = LanguageOperator::Dsl.agent_registry.get('github-pr-reviewer')
|
|
170
|
+
agent.run!
|
|
171
|
+
end
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example agent that exposes tools via MCP protocol
|
|
5
|
+
#
|
|
6
|
+
# This demonstrates Phase 3: Agents as MCP Servers
|
|
7
|
+
# The agent exposes tools that other agents or MCP clients can call
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# PORT=8080 ruby examples/mcp_agent.rb
|
|
11
|
+
#
|
|
12
|
+
# Test MCP protocol:
|
|
13
|
+
# curl -X POST http://localhost:8080/mcp \
|
|
14
|
+
# -H "Content-Type: application/json" \
|
|
15
|
+
# -d '{"jsonrpc": "2.0", "method": "tools/list", "id": 1}'
|
|
16
|
+
#
|
|
17
|
+
# Test webhook:
|
|
18
|
+
# curl -X POST http://localhost:8080/webhook \
|
|
19
|
+
# -H "Content-Type: application/json" \
|
|
20
|
+
# -d '{"message": "test"}'
|
|
21
|
+
|
|
22
|
+
require 'bundler/setup'
|
|
23
|
+
require 'language_operator'
|
|
24
|
+
require 'language_operator/dsl'
|
|
25
|
+
|
|
26
|
+
# Define an agent that acts as both a webhook receiver and MCP server
|
|
27
|
+
# rubocop:disable Metrics/BlockLength
|
|
28
|
+
LanguageOperator::Dsl.define do
|
|
29
|
+
agent 'data-processor-mcp' do
|
|
30
|
+
description 'Data processing agent that exposes MCP tools'
|
|
31
|
+
mode :reactive
|
|
32
|
+
|
|
33
|
+
# Define tools this agent exposes via MCP
|
|
34
|
+
as_mcp_server do
|
|
35
|
+
# Tool 1: Process CSV data
|
|
36
|
+
tool 'process_csv' do
|
|
37
|
+
description 'Process CSV data and return summary statistics'
|
|
38
|
+
|
|
39
|
+
parameter :csv_data do
|
|
40
|
+
type :string
|
|
41
|
+
required true
|
|
42
|
+
description 'CSV data as string'
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
execute do |params|
|
|
46
|
+
# Simple CSV processing example
|
|
47
|
+
lines = params['csv_data'].split("\n")
|
|
48
|
+
headers = lines.first&.split(',') || []
|
|
49
|
+
data_rows = lines[1..]
|
|
50
|
+
|
|
51
|
+
{
|
|
52
|
+
total_rows: data_rows&.length || 0,
|
|
53
|
+
total_columns: headers.length,
|
|
54
|
+
headers: headers,
|
|
55
|
+
sample: data_rows&.first || ''
|
|
56
|
+
}.to_json
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Tool 2: Calculate statistics
|
|
61
|
+
tool 'calculate_stats' do
|
|
62
|
+
description 'Calculate basic statistics for a list of numbers'
|
|
63
|
+
|
|
64
|
+
parameter :numbers do
|
|
65
|
+
type :array
|
|
66
|
+
required true
|
|
67
|
+
description 'Array of numbers'
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
execute do |params|
|
|
71
|
+
nums = params['numbers']
|
|
72
|
+
return { error: 'Empty array' }.to_json if nums.empty?
|
|
73
|
+
|
|
74
|
+
sum = nums.sum
|
|
75
|
+
mean = sum.to_f / nums.length
|
|
76
|
+
sorted = nums.sort
|
|
77
|
+
median = if nums.length.odd?
|
|
78
|
+
sorted[nums.length / 2]
|
|
79
|
+
else
|
|
80
|
+
(sorted[(nums.length / 2) - 1] + sorted[nums.length / 2]) / 2.0
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
{
|
|
84
|
+
count: nums.length,
|
|
85
|
+
sum: sum,
|
|
86
|
+
mean: mean,
|
|
87
|
+
median: median,
|
|
88
|
+
min: nums.min,
|
|
89
|
+
max: nums.max
|
|
90
|
+
}.to_json
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Tool 3: Format data
|
|
95
|
+
tool 'format_json' do
|
|
96
|
+
description 'Format and validate JSON data'
|
|
97
|
+
|
|
98
|
+
parameter :json_string do
|
|
99
|
+
type :string
|
|
100
|
+
required true
|
|
101
|
+
description 'JSON string to format'
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
parameter :indent do
|
|
105
|
+
type :number
|
|
106
|
+
required false
|
|
107
|
+
description 'Indentation spaces (default: 2)'
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
execute do |params|
|
|
111
|
+
indent = params['indent'] || 2
|
|
112
|
+
parsed = JSON.parse(params['json_string'])
|
|
113
|
+
JSON.pretty_generate(parsed, indent: ' ' * indent.to_i)
|
|
114
|
+
rescue JSON::ParserError => e
|
|
115
|
+
{ error: "Invalid JSON: #{e.message}" }.to_json
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Also expose a webhook endpoint
|
|
121
|
+
webhook '/process' do
|
|
122
|
+
method :post
|
|
123
|
+
on_request do |_context|
|
|
124
|
+
{
|
|
125
|
+
status: 'processed',
|
|
126
|
+
message: 'Data received for processing',
|
|
127
|
+
tools_available: 3,
|
|
128
|
+
mcp_endpoint: '/mcp'
|
|
129
|
+
}
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
# rubocop:enable Metrics/BlockLength
|
|
135
|
+
|
|
136
|
+
# Get the agent definition and run it
|
|
137
|
+
agent_def = LanguageOperator::Dsl.agent_registry.get('data-processor-mcp')
|
|
138
|
+
|
|
139
|
+
if agent_def
|
|
140
|
+
puts "Starting MCP agent on port #{ENV.fetch('PORT', '8080')}"
|
|
141
|
+
puts 'Available endpoints:'
|
|
142
|
+
puts ' GET /health - Health check'
|
|
143
|
+
puts ' GET /ready - Readiness check'
|
|
144
|
+
puts ' POST /webhook - Webhook endpoint'
|
|
145
|
+
puts ' POST /process - Data processing endpoint'
|
|
146
|
+
puts ' POST /mcp - MCP protocol endpoint'
|
|
147
|
+
puts
|
|
148
|
+
puts 'MCP Tools:'
|
|
149
|
+
puts ' - process_csv - Process CSV data'
|
|
150
|
+
puts ' - calculate_stats - Calculate statistics'
|
|
151
|
+
puts ' - format_json - Format JSON data'
|
|
152
|
+
puts "\nPress Ctrl+C to stop\n\n"
|
|
153
|
+
|
|
154
|
+
agent_def.run!
|
|
155
|
+
else
|
|
156
|
+
puts 'Error: Agent definition not found'
|
|
157
|
+
exit 1
|
|
158
|
+
end
|