legate 0.1.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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +345 -0
- data/bin/legate +13 -0
- data/examples/00_quickstart.rb +51 -0
- data/examples/01_simple_agent.rb +105 -0
- data/examples/02_multi_tool_agent.rb +140 -0
- data/examples/03_custom_tool.rb +93 -0
- data/examples/04_agent_instructions.rb +84 -0
- data/examples/05_state_and_sessions.rb +91 -0
- data/examples/06_callbacks.rb +186 -0
- data/examples/07_async_jobs.rb +112 -0
- data/examples/08_loop_agent.rb +197 -0
- data/examples/09_sequential_workflow.rb +40 -0
- data/examples/10_parallel_workflow.rb +34 -0
- data/examples/11_agent_delegation.rb +24 -0
- data/examples/12_http_client_tool.rb +156 -0
- data/examples/13_authentication.rb +220 -0
- data/examples/14_mcp_client.rb +154 -0
- data/examples/15_mcp_server.rb +79 -0
- data/examples/16_webhooks.rb +91 -0
- data/examples/README_sequential_agents.md +164 -0
- data/examples/advanced/auth/cookie_auth_tool.rb +146 -0
- data/examples/advanced/auth/custom_auth_flows_example.rb +626 -0
- data/examples/advanced/auth/excon_middleware.rb +317 -0
- data/examples/advanced/auth/excon_middleware_auth.rb +399 -0
- data/examples/advanced/auth/fiber_auth_example.rb +281 -0
- data/examples/advanced/auth/fiber_oidc_example.rb +403 -0
- data/examples/advanced/auth/httpbin_bearer_tool.rb +159 -0
- data/examples/advanced/auth/oauth2_auth.rb +419 -0
- data/examples/advanced/auth/oidc_auth.rb +514 -0
- data/examples/advanced/auth/openweather_api.rb +251 -0
- data/examples/advanced/auth/openweather_tool.rb +153 -0
- data/examples/advanced/auth/query_param_middleware_test.rb +138 -0
- data/examples/advanced/auth/service_account.rb +135 -0
- data/examples/advanced/auth/test_with_httpbin.rb +202 -0
- data/examples/advanced/auth/token_lifecycle_example.rb +428 -0
- data/examples/advanced/callback_monitoring.rb +679 -0
- data/examples/advanced/mas/fixed_delegation_example.rb +191 -0
- data/examples/advanced/mas/loop_workflow.rb +28 -0
- data/examples/advanced/mas/mock_planner.rb +77 -0
- data/examples/advanced/mas/proper_delegation_example.rb +276 -0
- data/examples/advanced/mcp/legate_mcp_server_resource_example.rb +182 -0
- data/examples/advanced/mcp/mcp_resource_server_example.rb +309 -0
- data/examples/advanced/mcp/mcp_server_async.rb +76 -0
- data/examples/advanced/mcp/mcp_server_async_tools.rb +122 -0
- data/examples/advanced/mcp/mcp_server_legate_agent.rb +95 -0
- data/examples/advanced/mcp/mcp_server_rack.rb +89 -0
- data/examples/advanced/random_calculator.rb +104 -0
- data/examples/advanced/sleep_agent.rb +153 -0
- data/examples/advanced/webhooks/webhook_e2e_runner.rb +110 -0
- data/examples/advanced/webhooks/webhook_receiver_agent.rb +58 -0
- data/examples/advanced/workflows/task_refinement_loop_agent.rb +278 -0
- data/examples/advanced/workflows/travel_planner_auto_sequential.rb +444 -0
- data/examples/advanced/workflows/travel_planner_parallel.rb +656 -0
- data/examples/advanced/workflows/travel_planner_sequential.rb +512 -0
- data/examples/tools/oauth2_example.rb +136 -0
- data/examples/tools/sleepy_tool.rb +42 -0
- data/lib/legate/activity_log.rb +71 -0
- data/lib/legate/agent.rb +959 -0
- data/lib/legate/agent_code_generator.rb +185 -0
- data/lib/legate/agent_definition.rb +812 -0
- data/lib/legate/agentic/decision.rb +49 -0
- data/lib/legate/agentic/loop.rb +134 -0
- data/lib/legate/agentic.rb +5 -0
- data/lib/legate/agents/loop_agent.rb +248 -0
- data/lib/legate/agents/parallel_agent.rb +163 -0
- data/lib/legate/agents/sequential_agent.rb +190 -0
- data/lib/legate/agents.rb +14 -0
- data/lib/legate/auth/config.rb +148 -0
- data/lib/legate/auth/coordinator.rb +218 -0
- data/lib/legate/auth/coordinators/oauth2_coordinator.rb +99 -0
- data/lib/legate/auth/coordinators/oidc_coordinator.rb +68 -0
- data/lib/legate/auth/coordinators/service_account_coordinator.rb +122 -0
- data/lib/legate/auth/credential.rb +157 -0
- data/lib/legate/auth/encryption.rb +108 -0
- data/lib/legate/auth/error.rb +94 -0
- data/lib/legate/auth/exchanged_credential.rb +180 -0
- data/lib/legate/auth/excon_middleware.rb +285 -0
- data/lib/legate/auth/http_client_utils.rb +364 -0
- data/lib/legate/auth/manager.rb +531 -0
- data/lib/legate/auth/manager_store.rb +394 -0
- data/lib/legate/auth/middleware_factory.rb +290 -0
- data/lib/legate/auth/runner.rb +279 -0
- data/lib/legate/auth/scheme.rb +125 -0
- data/lib/legate/auth/schemes/api_key.rb +212 -0
- data/lib/legate/auth/schemes/google_service_account.rb +108 -0
- data/lib/legate/auth/schemes/http_bearer.rb +98 -0
- data/lib/legate/auth/schemes/oauth2.rb +396 -0
- data/lib/legate/auth/schemes/openid_connect.rb +346 -0
- data/lib/legate/auth/schemes/service_account.rb +388 -0
- data/lib/legate/auth/schemes.rb +40 -0
- data/lib/legate/auth/token_manager.rb +362 -0
- data/lib/legate/auth/token_store.rb +86 -0
- data/lib/legate/auth/tool_context_extension.rb +97 -0
- data/lib/legate/auth/tool_integration.rb +188 -0
- data/lib/legate/auth/url_guard.rb +81 -0
- data/lib/legate/auth.rb +453 -0
- data/lib/legate/callbacks/callback_context.rb +71 -0
- data/lib/legate/cli/agent_commands.rb +950 -0
- data/lib/legate/cli/auth_commands.rb +520 -0
- data/lib/legate/cli/base_command.rb +24 -0
- data/lib/legate/cli/deployment_commands.rb +934 -0
- data/lib/legate/cli/output_helper.rb +108 -0
- data/lib/legate/cli/session_commands.rb +138 -0
- data/lib/legate/cli/skaffold_commands.rb +223 -0
- data/lib/legate/cli/tool_commands.rb +261 -0
- data/lib/legate/cli/web_commands.rb +182 -0
- data/lib/legate/cli.rb +40 -0
- data/lib/legate/configuration/webhooks.rb +113 -0
- data/lib/legate/configuration.rb +39 -0
- data/lib/legate/definition_store.rb +23 -0
- data/lib/legate/errors.rb +118 -0
- data/lib/legate/event.rb +161 -0
- data/lib/legate/gemini_ai_beta_patch.rb +39 -0
- data/lib/legate/generators/agent_generator.rb +412 -0
- data/lib/legate/generators/code_validator.rb +48 -0
- data/lib/legate/generators/legate/install_generator.rb +35 -0
- data/lib/legate/generators/legate/templates/create_legate_tables.rb.tt +36 -0
- data/lib/legate/generators/legate/templates/initializer.rb +18 -0
- data/lib/legate/generators/runtime_tool_loader.rb +76 -0
- data/lib/legate/generators/tool_generator.rb +408 -0
- data/lib/legate/generators.rb +11 -0
- data/lib/legate/global_definition_registry.rb +506 -0
- data/lib/legate/global_tool_manager.rb +135 -0
- data/lib/legate/llm/adapter.rb +69 -0
- data/lib/legate/llm/gemini.rb +172 -0
- data/lib/legate/llm/ollama.rb +80 -0
- data/lib/legate/llm.rb +34 -0
- data/lib/legate/mcp/client.rb +320 -0
- data/lib/legate/mcp/connection/sse.rb +292 -0
- data/lib/legate/mcp/connection/stdio.rb +273 -0
- data/lib/legate/mcp/connection_manager.rb +103 -0
- data/lib/legate/mcp/server/legate_agent_adapter.rb +170 -0
- data/lib/legate/mcp/server/legate_direct_agent_adapter.rb +140 -0
- data/lib/legate/mcp/server/legate_tool_adapter.rb +119 -0
- data/lib/legate/mcp/tool_wrapper.rb +138 -0
- data/lib/legate/mcp/util/schema_converter.rb +134 -0
- data/lib/legate/mcp.rb +23 -0
- data/lib/legate/plan_executor.rb +375 -0
- data/lib/legate/planner.rb +839 -0
- data/lib/legate/rails/railtie.rb +43 -0
- data/lib/legate/rails.rb +9 -0
- data/lib/legate/redaction.rb +32 -0
- data/lib/legate/session.rb +299 -0
- data/lib/legate/session_service/active_record.rb +300 -0
- data/lib/legate/session_service/base.rb +68 -0
- data/lib/legate/session_service/event_broadcast.rb +74 -0
- data/lib/legate/session_service/in_memory.rb +188 -0
- data/lib/legate/tool/metadata_dsl.rb +122 -0
- data/lib/legate/tool.rb +276 -0
- data/lib/legate/tool_code_generator.rb +103 -0
- data/lib/legate/tool_context.rb +350 -0
- data/lib/legate/tool_loader.rb +39 -0
- data/lib/legate/tool_registry.rb +73 -0
- data/lib/legate/tool_result.rb +61 -0
- data/lib/legate/tools/agent_tool.rb +187 -0
- data/lib/legate/tools/base/http_client.rb +319 -0
- data/lib/legate/tools/base/safe_url.rb +56 -0
- data/lib/legate/tools/base_async_job_tool.rb +91 -0
- data/lib/legate/tools/calculator.rb +89 -0
- data/lib/legate/tools/cat_facts.rb +81 -0
- data/lib/legate/tools/check_job_status_tool.rb +48 -0
- data/lib/legate/tools/current_time_tool.rb +64 -0
- data/lib/legate/tools/echo.rb +43 -0
- data/lib/legate/tools/http_request_tool.rb +105 -0
- data/lib/legate/tools/random_number_tool.rb +64 -0
- data/lib/legate/tools/read_webpage_tool.rb +92 -0
- data/lib/legate/tools/sleepy_tool.rb +74 -0
- data/lib/legate/tools/webhook_tool.rb +146 -0
- data/lib/legate/version.rb +5 -0
- data/lib/legate/web/app.rb +984 -0
- data/lib/legate/web/public/css/main.css +4980 -0
- data/lib/legate/web/public/images/favicon-256.png +0 -0
- data/lib/legate/web/public/images/favicon-32.png +0 -0
- data/lib/legate/web/public/images/legate-logo-dark.png +0 -0
- data/lib/legate/web/public/images/legate-logo-light.png +0 -0
- data/lib/legate/web/public/js/legate.js +616 -0
- data/lib/legate/web/public/styles/main.scss +4402 -0
- data/lib/legate/web/routes/agent_authentication_routes.rb +530 -0
- data/lib/legate/web/routes/agent_definition_routes.rb +803 -0
- data/lib/legate/web/routes/agent_generator_routes.rb +80 -0
- data/lib/legate/web/routes/agent_interaction_routes.rb +734 -0
- data/lib/legate/web/routes/agent_runtime_routes.rb +323 -0
- data/lib/legate/web/routes/api_routes.rb +56 -0
- data/lib/legate/web/routes/authentication_routes.rb +1541 -0
- data/lib/legate/web/routes/core_routes.rb +111 -0
- data/lib/legate/web/routes/documentation_routes.rb +220 -0
- data/lib/legate/web/routes/tool_generator_routes.rb +81 -0
- data/lib/legate/web/routes/tools_ui_routes.rb +207 -0
- data/lib/legate/web/sass_compiler.rb +73 -0
- data/lib/legate/web/views/_active_session_info.slim +25 -0
- data/lib/legate/web/views/_activity_list.slim +55 -0
- data/lib/legate/web/views/_agent_card.slim +56 -0
- data/lib/legate/web/views/_agent_generator_modal.slim +382 -0
- data/lib/legate/web/views/_agent_status_controls.slim +71 -0
- data/lib/legate/web/views/_agent_tool_table.slim +74 -0
- data/lib/legate/web/views/_chat_message.slim +95 -0
- data/lib/legate/web/views/_display_agent_configuration.slim +26 -0
- data/lib/legate/web/views/_display_agent_description.slim +11 -0
- data/lib/legate/web/views/_display_agent_fallback.slim +15 -0
- data/lib/legate/web/views/_display_agent_hierarchy.slim +93 -0
- data/lib/legate/web/views/_display_agent_instruction.slim +17 -0
- data/lib/legate/web/views/_display_agent_mcp.slim +13 -0
- data/lib/legate/web/views/_display_agent_model.slim +17 -0
- data/lib/legate/web/views/_display_agent_name.slim +42 -0
- data/lib/legate/web/views/_display_agent_output_key.slim +26 -0
- data/lib/legate/web/views/_display_agent_type.slim +65 -0
- data/lib/legate/web/views/_edit_agent_configuration.slim +74 -0
- data/lib/legate/web/views/_edit_agent_description.slim +16 -0
- data/lib/legate/web/views/_edit_agent_fallback.slim +25 -0
- data/lib/legate/web/views/_edit_agent_hierarchy.slim +98 -0
- data/lib/legate/web/views/_edit_agent_instruction.slim +49 -0
- data/lib/legate/web/views/_edit_agent_mcp.slim +33 -0
- data/lib/legate/web/views/_edit_agent_model.slim +23 -0
- data/lib/legate/web/views/_edit_agent_output_key.slim +36 -0
- data/lib/legate/web/views/_edit_agent_tools.slim +40 -0
- data/lib/legate/web/views/_edit_agent_type.slim +67 -0
- data/lib/legate/web/views/_session_error.slim +4 -0
- data/lib/legate/web/views/_skeleton.slim +69 -0
- data/lib/legate/web/views/_tool_card.slim +9 -0
- data/lib/legate/web/views/_tool_generator_modal.slim +311 -0
- data/lib/legate/web/views/agent.slim +436 -0
- data/lib/legate/web/views/agent_auth.slim +562 -0
- data/lib/legate/web/views/agents.slim +369 -0
- data/lib/legate/web/views/auth.slim +112 -0
- data/lib/legate/web/views/auth_credential_detail.slim +327 -0
- data/lib/legate/web/views/auth_credentials.slim +261 -0
- data/lib/legate/web/views/auth_debug.slim +94 -0
- data/lib/legate/web/views/auth_mapping_detail.slim +151 -0
- data/lib/legate/web/views/auth_mapping_new.slim +123 -0
- data/lib/legate/web/views/auth_mappings.slim +120 -0
- data/lib/legate/web/views/auth_scheme_detail.slim +274 -0
- data/lib/legate/web/views/auth_schemes.slim +259 -0
- data/lib/legate/web/views/auth_test.slim +418 -0
- data/lib/legate/web/views/chat.slim +192 -0
- data/lib/legate/web/views/docs_index.slim +105 -0
- data/lib/legate/web/views/docs_show.slim +105 -0
- data/lib/legate/web/views/error_404.slim +5 -0
- data/lib/legate/web/views/index.slim +148 -0
- data/lib/legate/web/views/layout.slim +144 -0
- data/lib/legate/web/views/tool_detail.slim +87 -0
- data/lib/legate/web/views/tools.slim +50 -0
- data/lib/legate/web/webhook_listener.rb +367 -0
- data/lib/legate/web.rb +9 -0
- data/lib/legate.rb +220 -0
- data/public/docs/advanced/callbacks.md +828 -0
- data/public/docs/advanced/mcp_schema_conversion.md +59 -0
- data/public/docs/authentication/api_reference/config.md +210 -0
- data/public/docs/authentication/api_reference/credential.md +246 -0
- data/public/docs/authentication/api_reference/encryption.md +218 -0
- data/public/docs/authentication/api_reference/exchanged_credential.md +271 -0
- data/public/docs/authentication/api_reference/excon_middleware.md +175 -0
- data/public/docs/authentication/api_reference/index.md +30 -0
- data/public/docs/authentication/api_reference/scheme.md +250 -0
- data/public/docs/authentication/api_reference/schemes/api_key.md +175 -0
- data/public/docs/authentication/api_reference/schemes/google_service_account.md +221 -0
- data/public/docs/authentication/api_reference/schemes/http_bearer.md +169 -0
- data/public/docs/authentication/api_reference/schemes/oauth2.md +343 -0
- data/public/docs/authentication/api_reference/schemes/oidc.md +73 -0
- data/public/docs/authentication/api_reference/schemes/openid_connect.md +311 -0
- data/public/docs/authentication/api_reference/schemes/service_account.md +287 -0
- data/public/docs/authentication/api_reference/token_manager.md +221 -0
- data/public/docs/authentication/api_reference/token_store.md +146 -0
- data/public/docs/authentication/api_reference/tool_context_extension.md +166 -0
- data/public/docs/authentication/guides/api_key.md +190 -0
- data/public/docs/authentication/guides/bearer.md +172 -0
- data/public/docs/authentication/guides/configuration.md +255 -0
- data/public/docs/authentication/guides/custom_flow.md +523 -0
- data/public/docs/authentication/guides/index.md +24 -0
- data/public/docs/authentication/guides/migration.md +435 -0
- data/public/docs/authentication/guides/oauth2.md +252 -0
- data/public/docs/authentication/guides/oidc.md +241 -0
- data/public/docs/authentication/guides/overview.md +155 -0
- data/public/docs/authentication/guides/secure_storage.md +301 -0
- data/public/docs/authentication/guides/service_account.md +228 -0
- data/public/docs/authentication/guides/token_lifecycle.md +295 -0
- data/public/docs/authentication/guides/web_ui_integration.md +504 -0
- data/public/docs/authentication/index.md +58 -0
- data/public/docs/authentication/troubleshooting/credential_storage.md +550 -0
- data/public/docs/authentication/troubleshooting/environment_variables.md +540 -0
- data/public/docs/authentication/troubleshooting/index.md +11 -0
- data/public/docs/authentication/troubleshooting/oauth2_issues.md +220 -0
- data/public/docs/authentication/troubleshooting/oidc_issues.md +412 -0
- data/public/docs/authentication/troubleshooting/token_refresh.md +338 -0
- data/public/docs/cli/legate_cli_usage.md +363 -0
- data/public/docs/core_concepts/legate_agent_lifecycle.md +124 -0
- data/public/docs/core_concepts/legate_architecture_overview.md +110 -0
- data/public/docs/core_concepts/legate_configuration.md +116 -0
- data/public/docs/core_concepts/legate_definition_store.md +102 -0
- data/public/docs/core_concepts/legate_planner.md +94 -0
- data/public/docs/core_concepts/legate_session_service.md +104 -0
- data/public/docs/error_handling/legate_error_handling.md +122 -0
- data/public/docs/examples.md +199 -0
- data/public/docs/getting_started.md +111 -0
- data/public/docs/guides/agentic_agents.md +137 -0
- data/public/docs/guides/ai_code_generators.md +437 -0
- data/public/docs/guides/auto_loading.md +326 -0
- data/public/docs/guides/configuring_agent_webhooks.md +219 -0
- data/public/docs/guides/http_client_usage.md +264 -0
- data/public/docs/guides/llm_providers.md +137 -0
- data/public/docs/guides/mcp_client_integration.md +232 -0
- data/public/docs/guides/mcp_server_exposure.md +206 -0
- data/public/docs/guides/rails_integration.md +128 -0
- data/public/docs/guides/sending_outbound_webhooks.md +227 -0
- data/public/docs/guides/streaming.md +112 -0
- data/public/docs/guides/webhooks.md +288 -0
- data/public/docs/introduction.md +51 -0
- data/public/docs/multi_agent_systems/advanced_features.md +57 -0
- data/public/docs/multi_agent_systems/agent_delegation.md +190 -0
- data/public/docs/multi_agent_systems/agent_hierarchy.md +49 -0
- data/public/docs/multi_agent_systems/state_management.md +47 -0
- data/public/docs/multi_agent_systems/workflow_agents.md +72 -0
- data/public/docs/tools/legate_built_in_tools.md +332 -0
- data/public/docs/tools/legate_tools_and_registry.md +263 -0
- data/public/docs/web_ui/legate_web_ui.md +137 -0
- metadata +823 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# File: ./examples/advanced/mcp/legate_mcp_server_resource_example.rb
|
|
2
|
+
# !/usr/bin/env ruby
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
|
|
5
|
+
# --- Example: Exposing a multi-tool Legate Agent via MCP ---
|
|
6
|
+
# -------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
ENV['LEGATE_LOG_LEVEL'] = 'ERROR'
|
|
9
|
+
ENV['LEGATE_LOG_TARGET'] = 'STDERR'
|
|
10
|
+
|
|
11
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __dir__)
|
|
12
|
+
require 'legate'
|
|
13
|
+
Legate.load_environment # Handle Bundler, Dotenv, etc.
|
|
14
|
+
require 'fast_mcp'
|
|
15
|
+
# require 'thread' # No longer needed
|
|
16
|
+
# require 'json' # No longer needed
|
|
17
|
+
# require 'singleton' # No longer needed
|
|
18
|
+
require 'logger'
|
|
19
|
+
require 'securerandom'
|
|
20
|
+
require 'net/http' # Needed for CatFactResource
|
|
21
|
+
require 'uri' # Needed for CatFactResource
|
|
22
|
+
|
|
23
|
+
# Load the necessary Legate MCP components
|
|
24
|
+
require 'legate/mcp'
|
|
25
|
+
require 'legate/mcp/server/legate_direct_agent_adapter'
|
|
26
|
+
|
|
27
|
+
# Load Legate components for direct agent instantiation
|
|
28
|
+
require 'legate/agent'
|
|
29
|
+
require 'legate/session_service/in_memory'
|
|
30
|
+
|
|
31
|
+
# --- Load ALL required Legate Tool Classes ---
|
|
32
|
+
require 'legate/tools/calculator'
|
|
33
|
+
require 'legate/tools/agent_tool'
|
|
34
|
+
require 'legate/tools/random_number_tool'
|
|
35
|
+
require 'legate/tools/sleepy_tool'
|
|
36
|
+
require 'legate/tools/cat_facts'
|
|
37
|
+
require 'legate/tools/check_job_status_tool'
|
|
38
|
+
require 'legate/tools/echo'
|
|
39
|
+
# ------------------------------------------
|
|
40
|
+
|
|
41
|
+
# === FastMcp Components Setup ===
|
|
42
|
+
|
|
43
|
+
# --- NEW: Define MCP Resources based on Legate Tool functionality ---
|
|
44
|
+
|
|
45
|
+
# 1. Random Number Resource
|
|
46
|
+
class RandomNumberResource < FastMcp::Resource
|
|
47
|
+
include Singleton
|
|
48
|
+
uri 'random_number'
|
|
49
|
+
resource_name 'RandomNumber'
|
|
50
|
+
description 'Provides a random floating-point number between 0 and 1.'
|
|
51
|
+
mime_type 'application/json'
|
|
52
|
+
|
|
53
|
+
def read
|
|
54
|
+
# Generate a new random number on each read
|
|
55
|
+
{ value: rand }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def content
|
|
59
|
+
JSON.generate(read)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# 2. Cat Fact Resource
|
|
64
|
+
class CatFactResource < FastMcp::Resource
|
|
65
|
+
include Singleton
|
|
66
|
+
uri 'catfact'
|
|
67
|
+
resource_name 'CurrentCatFact'
|
|
68
|
+
description 'Provides a random cat fact from catfact.ninja.'
|
|
69
|
+
mime_type 'application/json'
|
|
70
|
+
|
|
71
|
+
CATFACT_API_URI = URI('https://catfact.ninja/fact')
|
|
72
|
+
|
|
73
|
+
def read
|
|
74
|
+
# Fetch a new fact on each read
|
|
75
|
+
response = Net::HTTP.get_response(CATFACT_API_URI)
|
|
76
|
+
if response.is_a?(Net::HTTPSuccess)
|
|
77
|
+
data = JSON.parse(response.body)
|
|
78
|
+
{ fact: data['fact'] || 'Could not retrieve fact.' }
|
|
79
|
+
else
|
|
80
|
+
Legate.logger.error("CatFactResource: Failed to fetch cat fact. Status: #{response.code}")
|
|
81
|
+
{ fact: "Error fetching fact: #{response.code}" }
|
|
82
|
+
end
|
|
83
|
+
rescue StandardError => e
|
|
84
|
+
Legate.logger.error("CatFactResource: Error fetching cat fact: #{e.message}")
|
|
85
|
+
{ fact: "Error fetching fact: #{e.message}" }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def content
|
|
89
|
+
JSON.generate(read)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# --- NEW: Define MCP Tools to explicitly interact with the new Resources ---
|
|
94
|
+
|
|
95
|
+
class GetNewRandomNumberTool < FastMcp::Tool
|
|
96
|
+
description 'Gets a new random number from the RandomNumber resource.'
|
|
97
|
+
tool_name 'getrandomnumber'
|
|
98
|
+
arguments {}
|
|
99
|
+
def call(**_args)
|
|
100
|
+
# Access the singleton instance and read its current state
|
|
101
|
+
RandomNumberResource.instance.read[:value]
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class GetNewCatFactTool < FastMcp::Tool
|
|
106
|
+
description 'Gets a new cat fact from the CatFact resource.'
|
|
107
|
+
tool_name 'getcatfact'
|
|
108
|
+
arguments {}
|
|
109
|
+
def call(**_args)
|
|
110
|
+
# Access the singleton instance and trigger a read
|
|
111
|
+
CatFactResource.instance.read[:fact]
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# --- 1. Instantiate Master Legate Agent and Wrap it ---
|
|
116
|
+
begin
|
|
117
|
+
# List all the tool CLASSES to be given to the agent
|
|
118
|
+
all_tool_classes = [
|
|
119
|
+
Legate::Tools::Calculator,
|
|
120
|
+
Legate::Tools::AgentTool,
|
|
121
|
+
Legate::Tools::RandomNumberTool,
|
|
122
|
+
Legate::Tools::SleepyTool,
|
|
123
|
+
Legate::Tools::CatFacts,
|
|
124
|
+
Legate::Tools::CheckJobStatusTool,
|
|
125
|
+
Legate::Tools::Echo,
|
|
126
|
+
GetNewRandomNumberTool,
|
|
127
|
+
GetNewCatFactTool
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
# Create the Legate::Agent instance with all tools
|
|
131
|
+
master_agent = Legate::Agent.new(
|
|
132
|
+
name: 'master_agent',
|
|
133
|
+
description: 'An agent with access to all built-in Legate tools.',
|
|
134
|
+
model_name: 'gemini-3.5-flash',
|
|
135
|
+
tool_classes: all_tool_classes
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Create a session service instance
|
|
139
|
+
session_service = Legate::SessionService::InMemory.new
|
|
140
|
+
|
|
141
|
+
# Use the direct adapter to wrap the master agent instance
|
|
142
|
+
AdaptedMasterAgentTool = Legate::Mcp::Server::LegateDirectAgentAdapter.wrap(master_agent, session_service)
|
|
143
|
+
rescue StandardError => e
|
|
144
|
+
warn "Error setting up Legate Master Agent or Adapter: #{e.message}"
|
|
145
|
+
warn e.backtrace.join("\n")
|
|
146
|
+
exit(1)
|
|
147
|
+
end
|
|
148
|
+
# === End FastMcp Components Setup ===
|
|
149
|
+
|
|
150
|
+
# === Setup and Start the MCP Server ===
|
|
151
|
+
|
|
152
|
+
mcp_server = FastMcp::Server.new(
|
|
153
|
+
name: 'Legate Master Agent Server', # Updated server name
|
|
154
|
+
version: Legate::VERSION
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Register the new Resources
|
|
158
|
+
mcp_server.register_resource(RandomNumberResource)
|
|
159
|
+
mcp_server.register_resource(CatFactResource)
|
|
160
|
+
|
|
161
|
+
# Register the Tools (Master Agent + Resource-specific tools)
|
|
162
|
+
mcp_server.register_tools(
|
|
163
|
+
AdaptedMasterAgentTool,
|
|
164
|
+
GetNewRandomNumberTool,
|
|
165
|
+
GetNewCatFactTool
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# Start the server using STDIO transport
|
|
169
|
+
warn '--- Starting Legate Master Agent MCP Server (STDIO) ---'
|
|
170
|
+
warn "Resources: #{[RandomNumberResource.uri, CatFactResource.uri].join(', ')}"
|
|
171
|
+
warn "Tools Available: #{[AdaptedMasterAgentTool.tool_name, GetNewRandomNumberTool.tool_name,
|
|
172
|
+
GetNewCatFactTool.tool_name].join(', ')}"
|
|
173
|
+
warn 'Waiting for MCP client requests on STDIN...'
|
|
174
|
+
begin
|
|
175
|
+
mcp_server.start
|
|
176
|
+
rescue Interrupt
|
|
177
|
+
rescue StandardError => e
|
|
178
|
+
warn "MCP server crashed: #{e.class} - #{e.message}"
|
|
179
|
+
warn e.backtrace.join("\n")
|
|
180
|
+
ensure
|
|
181
|
+
warn "\n--- Legate Master Agent MCP Server Stopped ---"
|
|
182
|
+
end
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# --- Usage ---
|
|
5
|
+
# 1. Run this script from your project root:
|
|
6
|
+
# bundle exec ruby examples/advanced/mcp/mcp_resource_server_example.rb
|
|
7
|
+
#
|
|
8
|
+
# 2. In another terminal, inspect using mcp-inspector:
|
|
9
|
+
# npx mcp-inspector --stdio 'bundle exec ruby examples/advanced/mcp/mcp_resource_server_example.rb'
|
|
10
|
+
#
|
|
11
|
+
# 3. Example inspector commands:
|
|
12
|
+
# > resources/list
|
|
13
|
+
# > resources/read {"uri": "counter"}
|
|
14
|
+
# > tools/list # Should show counter tools AND run_calculator_agent
|
|
15
|
+
# > tools/call {"name": "run_calculator_agent", "arguments": {"prompt": "What is 5 * 8?"}}
|
|
16
|
+
# > tools/call {"name": "run_calculator_agent", "arguments": {"prompt": "Add 100 and 23"}}
|
|
17
|
+
# > tools/call {"name": "incrementcounter", "arguments": {}}
|
|
18
|
+
# -------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
# --- IMPORTANT: Configure Legate logging for MCP compatibility ---
|
|
21
|
+
# Configure ENV variables before requiring Legate to control logging
|
|
22
|
+
ENV['LEGATE_LOG_LEVEL'] = 'ERROR' # Set high level to minimize Legate output
|
|
23
|
+
ENV['LEGATE_LOG_TARGET'] = 'STDERR' # Redirect Legate logs to STDERR
|
|
24
|
+
|
|
25
|
+
# Now load the libraries
|
|
26
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __dir__)
|
|
27
|
+
require 'legate'
|
|
28
|
+
Legate.load_environment # Handle Bundler, Dotenv, etc.
|
|
29
|
+
require 'fast_mcp' # Load the fast-mcp library
|
|
30
|
+
require 'json' # Needed for JSON generation in #content
|
|
31
|
+
require 'singleton' # Needed for Singleton pattern
|
|
32
|
+
require 'logger' # For Logger constant access
|
|
33
|
+
# ---------------------------------------------
|
|
34
|
+
|
|
35
|
+
# === Legate Components Setup ===
|
|
36
|
+
|
|
37
|
+
# 1. Define the Legate Agent that uses the Calculator
|
|
38
|
+
calculator_agent = Legate::Agent.new(
|
|
39
|
+
name: 'calculator_agent_instance', # Runtime instance name
|
|
40
|
+
description: 'An agent that can perform calculations.',
|
|
41
|
+
model_name: 'gemini-test-model', # Specify a model (even if dummy for this example)
|
|
42
|
+
tool_classes: [Legate::Tools::Calculator] # Provide the CLASS
|
|
43
|
+
)
|
|
44
|
+
# Start the agent runtime (needed for run_task)
|
|
45
|
+
calculator_agent.start
|
|
46
|
+
|
|
47
|
+
# 2. Create a Session Service for the Agent
|
|
48
|
+
# Using InMemory for this self-contained example
|
|
49
|
+
session_service = Legate::SessionService::InMemory.new
|
|
50
|
+
|
|
51
|
+
# === End Legate Components Setup ===
|
|
52
|
+
|
|
53
|
+
# === FastMcp Components Setup ===
|
|
54
|
+
|
|
55
|
+
# --- 1. Define the Counter Resource ---
|
|
56
|
+
class CounterResource < FastMcp::Resource
|
|
57
|
+
include Singleton # Use Singleton pattern
|
|
58
|
+
|
|
59
|
+
# --- Define metadata via class methods ---
|
|
60
|
+
def self.uri
|
|
61
|
+
'counter'
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.resource_name
|
|
65
|
+
'Counter' # Changed back to capitalized as per original example convention
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.description
|
|
69
|
+
'A simple counter resource'
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.mime_type
|
|
73
|
+
'application/json'
|
|
74
|
+
end
|
|
75
|
+
# --- End metadata via class methods ---
|
|
76
|
+
|
|
77
|
+
attr_reader :count
|
|
78
|
+
|
|
79
|
+
def initialize # Singleton's initialize
|
|
80
|
+
# Removed super call
|
|
81
|
+
@count = 0
|
|
82
|
+
@lock = Mutex.new
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def read; @lock.synchronize { { value: @count } }; end
|
|
86
|
+
def content; JSON.generate(read); end
|
|
87
|
+
|
|
88
|
+
def increment
|
|
89
|
+
new_value = nil
|
|
90
|
+
@lock.synchronize {
|
|
91
|
+
@count += 1
|
|
92
|
+
new_value = @count
|
|
93
|
+
}
|
|
94
|
+
notify_change # Moved here
|
|
95
|
+
new_value
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def decrement
|
|
99
|
+
new_value = nil
|
|
100
|
+
@lock.synchronize {
|
|
101
|
+
@count -= 1
|
|
102
|
+
new_value = @count
|
|
103
|
+
}
|
|
104
|
+
notify_change # Moved here
|
|
105
|
+
new_value
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def get_value; @lock.synchronize { @count }; end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# --- 2. Define Counter Tools ---
|
|
112
|
+
class IncrementCounterTool < FastMcp::Tool
|
|
113
|
+
# --- Define metadata via class methods ---
|
|
114
|
+
def self.tool_name
|
|
115
|
+
'incrementcounter'
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def self.description
|
|
119
|
+
'Increments the counter resource by 1'
|
|
120
|
+
end
|
|
121
|
+
# --- End metadata via class methods ---
|
|
122
|
+
|
|
123
|
+
# Server accessor needed by FastMcp for notify_change
|
|
124
|
+
attr_accessor :server
|
|
125
|
+
|
|
126
|
+
arguments {} # Define arguments block
|
|
127
|
+
def call(**_args)
|
|
128
|
+
counter = CounterResource.instance
|
|
129
|
+
new_value = counter.increment # Resource handles notify_change
|
|
130
|
+
"Counter incremented. New value: #{new_value}"
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
class DecrementCounterTool < FastMcp::Tool
|
|
135
|
+
# --- Define metadata via class methods ---
|
|
136
|
+
def self.tool_name
|
|
137
|
+
'decrementcounter'
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def self.description
|
|
141
|
+
'Decrements the counter resource by 1'
|
|
142
|
+
end
|
|
143
|
+
# --- End metadata via class methods ---
|
|
144
|
+
|
|
145
|
+
# Server accessor needed by FastMcp for notify_change
|
|
146
|
+
attr_accessor :server
|
|
147
|
+
|
|
148
|
+
arguments {} # Define arguments block
|
|
149
|
+
def call(**_args)
|
|
150
|
+
counter = CounterResource.instance
|
|
151
|
+
new_value = counter.decrement # Resource handles notify_change
|
|
152
|
+
"Counter decremented. New value: #{new_value}"
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
class GetCounterTool < FastMcp::Tool
|
|
157
|
+
# --- Define metadata via class methods ---
|
|
158
|
+
def self.tool_name
|
|
159
|
+
'getcounter'
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def self.description
|
|
163
|
+
'Gets the current value of the counter resource'
|
|
164
|
+
end
|
|
165
|
+
# --- End metadata via class methods ---
|
|
166
|
+
|
|
167
|
+
# Server accessor needed by FastMcp
|
|
168
|
+
attr_accessor :server
|
|
169
|
+
|
|
170
|
+
arguments {} # Define arguments block
|
|
171
|
+
def call(**_args)
|
|
172
|
+
counter = CounterResource.instance
|
|
173
|
+
counter.get_value
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# --- 3. Define the Legate Agent Adapter Tool ---
|
|
178
|
+
class InlineAgentToolAdapter < FastMcp::Tool
|
|
179
|
+
# --- Define metadata via class methods ---
|
|
180
|
+
def self.tool_name
|
|
181
|
+
'run_calculator_agent'
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def self.description
|
|
185
|
+
'Runs the internal Legate Calculator Agent with the given prompt.'
|
|
186
|
+
end
|
|
187
|
+
# --- End metadata via class methods ---
|
|
188
|
+
|
|
189
|
+
# Server accessor needed by FastMcp
|
|
190
|
+
attr_accessor :server
|
|
191
|
+
|
|
192
|
+
# --- Define arguments using DSL block ---
|
|
193
|
+
arguments do
|
|
194
|
+
required(:prompt).filled(:string).description('The user input/prompt for the agent')
|
|
195
|
+
end
|
|
196
|
+
# --- End arguments block ---
|
|
197
|
+
|
|
198
|
+
# --- Use class variables for dependencies (workaround for class-based registration) ---
|
|
199
|
+
@@agent = nil
|
|
200
|
+
@@session_service = nil
|
|
201
|
+
|
|
202
|
+
# Class method to set up the references needed *before* registration
|
|
203
|
+
def self.setup(agent, session_service)
|
|
204
|
+
@@agent = agent
|
|
205
|
+
@@session_service = session_service
|
|
206
|
+
end
|
|
207
|
+
# --- End class variables setup ---
|
|
208
|
+
|
|
209
|
+
# No instance initialize needed if dependencies are class-level
|
|
210
|
+
|
|
211
|
+
def call(prompt:)
|
|
212
|
+
# Make sure dependencies are set up via the class method
|
|
213
|
+
raise 'Agent not configured via InlineAgentToolAdapter.setup' unless @@agent
|
|
214
|
+
raise 'Session service not configured via InlineAgentToolAdapter.setup' unless @@session_service
|
|
215
|
+
|
|
216
|
+
temp_session = nil
|
|
217
|
+
# Legate logs are silenced via ENV var setup
|
|
218
|
+
|
|
219
|
+
begin
|
|
220
|
+
temp_session = @@session_service.create_session(
|
|
221
|
+
app_name: @@agent.name, # Use agent name from class variable
|
|
222
|
+
user_id: "mcp_inline_#{SecureRandom.hex(4)}"
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
final_event = @@agent.run_task( # Use agent from class variable
|
|
226
|
+
session_id: temp_session.id,
|
|
227
|
+
user_input: prompt,
|
|
228
|
+
session_service: @@session_service # Use service from class variable
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
raise StandardError, "Agent task finished with unexpected event format: #{final_event.inspect}" unless final_event.is_a?(Legate::Event) && final_event.role == :agent && final_event.content.is_a?(Hash)
|
|
232
|
+
|
|
233
|
+
result_content = final_event.content
|
|
234
|
+
|
|
235
|
+
case result_content[:status]
|
|
236
|
+
when :success
|
|
237
|
+
result_content[:result]
|
|
238
|
+
when :error
|
|
239
|
+
err_msg = result_content[:error_message] || 'Agent execution failed.'
|
|
240
|
+
raise StandardError, "Agent Error: #{err_msg}"
|
|
241
|
+
when :pending
|
|
242
|
+
job_id = result_content[:job_id]
|
|
243
|
+
msg = result_content[:message] || 'Agent task resulted in a pending job.'
|
|
244
|
+
{ status: 'pending', job_id: job_id, message: msg }
|
|
245
|
+
else
|
|
246
|
+
raise StandardError, "Agent task finished with unknown status: #{result_content[:status]}"
|
|
247
|
+
end
|
|
248
|
+
rescue StandardError => e
|
|
249
|
+
# Log error to STDERR since Legate logger might be fully silenced
|
|
250
|
+
warn "InlineAgentToolAdapter Error during call: #{e.class} - #{e.message}"
|
|
251
|
+
warn e.backtrace.first(5).join("\n")
|
|
252
|
+
raise # Re-raise the error for fast-mcp to handle
|
|
253
|
+
ensure
|
|
254
|
+
if temp_session && @@session_service
|
|
255
|
+
begin
|
|
256
|
+
@@session_service.delete_session(session_id: temp_session.id)
|
|
257
|
+
rescue StandardError => e
|
|
258
|
+
# Log deletion error to STDERR
|
|
259
|
+
warn "InlineAgentToolAdapter: Error deleting temp session #{temp_session.id}: #{e.message}"
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
# --- End Legate Agent Adapter Tool ---
|
|
266
|
+
|
|
267
|
+
# === Setup and Start the MCP Server ===
|
|
268
|
+
|
|
269
|
+
# --- Configure the adapter class BEFORE registration ---
|
|
270
|
+
InlineAgentToolAdapter.setup(calculator_agent, session_service)
|
|
271
|
+
# --- END Configure Adapter ---
|
|
272
|
+
|
|
273
|
+
# --- Create server without logger (following sample) ---
|
|
274
|
+
mcp_server = FastMcp::Server.new(
|
|
275
|
+
name: 'Legate Combined Example Server',
|
|
276
|
+
version: Legate::VERSION
|
|
277
|
+
)
|
|
278
|
+
# --- END ---
|
|
279
|
+
|
|
280
|
+
# Register the resource class
|
|
281
|
+
mcp_server.register_resource(CounterResource)
|
|
282
|
+
|
|
283
|
+
# --- Register TOOL CLASSES ---
|
|
284
|
+
mcp_server.register_tools(
|
|
285
|
+
IncrementCounterTool,
|
|
286
|
+
DecrementCounterTool,
|
|
287
|
+
GetCounterTool,
|
|
288
|
+
InlineAgentToolAdapter # Register the adapter class
|
|
289
|
+
)
|
|
290
|
+
# --- END Registration ---
|
|
291
|
+
|
|
292
|
+
# Start the server (defaults to STDIO transport)
|
|
293
|
+
# Output status messages to STDERR so they don't interfere with STDOUT protocol
|
|
294
|
+
warn '--- Starting Legate Combined MCP Server (STDIO) ---'
|
|
295
|
+
warn 'Resource: counter'
|
|
296
|
+
warn 'Tools: incrementcounter, decrementcounter, getcounter, run_calculator_agent'
|
|
297
|
+
warn 'Waiting for MCP client requests on STDIN...'
|
|
298
|
+
begin
|
|
299
|
+
mcp_server.start # This uses STDIO by default
|
|
300
|
+
rescue Interrupt
|
|
301
|
+
# Expected on Ctrl+C
|
|
302
|
+
rescue StandardError => e
|
|
303
|
+
warn "MCP server crashed: #{e.class} - #{e.message}"
|
|
304
|
+
warn e.backtrace.join("\n")
|
|
305
|
+
ensure
|
|
306
|
+
warn "\n--- Legate Combined MCP Server Stopped ---"
|
|
307
|
+
# Ensure the Legate agent runtime is stopped on exit
|
|
308
|
+
calculator_agent.stop if calculator_agent&.running?
|
|
309
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# --- Configure Logging First! ---
|
|
5
|
+
# Set log level and target *before* requiring Legate to ensure logger initializes correctly.
|
|
6
|
+
ENV['LEGATE_LOG_LEVEL'] = 'ERROR' # Or INFO/DEBUG for more verbose output
|
|
7
|
+
ENV['LEGATE_LOG_TARGET'] = 'STDERR' # Send logs to STDERR to avoid interfering with MCP STDOUT communication
|
|
8
|
+
# -------------------------------
|
|
9
|
+
|
|
10
|
+
# Example: Exposing Async Legate Tools via MCP using fast-mcp
|
|
11
|
+
#
|
|
12
|
+
# This example demonstrates how to wrap an asynchronous Legate tool (like SleepyTool)
|
|
13
|
+
# and the necessary Legate::Tools::CheckJobStatusTool using the LegateToolAdapter
|
|
14
|
+
# so they can be called by an MCP client.
|
|
15
|
+
#
|
|
16
|
+
# Requires:
|
|
17
|
+
# - legate gem with MCP support installed
|
|
18
|
+
# - fast-mcp gem installed
|
|
19
|
+
#
|
|
20
|
+
# To Run:
|
|
21
|
+
# 1. Execute this script: `bundle exec ruby examples/advanced/mcp/mcp_server_async.rb`
|
|
22
|
+
# 2. In another terminal, use mcp-inspector: `npx @modelcontextprotocol/inspector examples/advanced/mcp/mcp_server_async.rb`
|
|
23
|
+
# 3. In the inspector:
|
|
24
|
+
# - Call 'start_sleepy_job' (tool name from SleepyTool metadata) with duration (e.g., 5 seconds).
|
|
25
|
+
# - Observe the pending response with a job_id.
|
|
26
|
+
# - Call 'check_job_status' with the received job_id.
|
|
27
|
+
# - Check status repeatedly until it shows success or error.
|
|
28
|
+
|
|
29
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __dir__)
|
|
30
|
+
require 'legate'
|
|
31
|
+
Legate.load_environment # Handle Bundler, Dotenv, etc.
|
|
32
|
+
require 'fast_mcp' # Load fast-mcp gem
|
|
33
|
+
|
|
34
|
+
# Ensure required Legate components are loaded
|
|
35
|
+
require 'legate/tools/base_async_job_tool'
|
|
36
|
+
require 'legate/tools/sleepy_tool' # Example async tool
|
|
37
|
+
require 'legate/tools/check_job_status_tool' # Tool to check job status
|
|
38
|
+
require 'legate/mcp/server/legate_tool_adapter' # The adapter itself
|
|
39
|
+
|
|
40
|
+
# --- Create the FastMcp Server Instance ---
|
|
41
|
+
Legate.logger.info('Creating fast-mcp server...')
|
|
42
|
+
# Use the logger from Legate for consistency
|
|
43
|
+
mcp_server = FastMcp::Server.new(name: 'legate-async-mcp-server', version: '1.0.0')
|
|
44
|
+
|
|
45
|
+
# --- Wrap the Legate Tools using the Adapter ---
|
|
46
|
+
Legate.logger.info('Wrapping Legate tools for MCP...')
|
|
47
|
+
|
|
48
|
+
begin
|
|
49
|
+
# Wrap the asynchronous tool (SleepyTool)
|
|
50
|
+
wrapped_sleepy_tool = Legate::Mcp::Server::LegateToolAdapter.wrap(Legate::Tools::SleepyTool)
|
|
51
|
+
Legate.logger.info("Wrapped SleepyTool as: #{wrapped_sleepy_tool.tool_name}")
|
|
52
|
+
|
|
53
|
+
# Wrap the CheckJobStatusTool
|
|
54
|
+
wrapped_check_job_tool = Legate::Mcp::Server::LegateToolAdapter.wrap(Legate::Tools::CheckJobStatusTool)
|
|
55
|
+
Legate.logger.info("Wrapped CheckJobStatusTool as: #{wrapped_check_job_tool.tool_name}")
|
|
56
|
+
|
|
57
|
+
# --- Register Wrapped Tools with the FastMcp Server ---
|
|
58
|
+
Legate.logger.info('Registering wrapped tools with fast-mcp server...')
|
|
59
|
+
mcp_server.register_tool(wrapped_sleepy_tool)
|
|
60
|
+
mcp_server.register_tool(wrapped_check_job_tool)
|
|
61
|
+
rescue ArgumentError => e
|
|
62
|
+
Legate.logger.fatal("Failed to wrap Legate tools: #{e.message}")
|
|
63
|
+
Legate.logger.fatal('Ensure the Legate::Tool classes have complete metadata.')
|
|
64
|
+
exit(1)
|
|
65
|
+
rescue StandardError => e
|
|
66
|
+
Legate.logger.fatal("An unexpected error occurred during setup: #{e.message}")
|
|
67
|
+
Legate.logger.fatal(e.backtrace.join("\n"))
|
|
68
|
+
exit(1)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# --- Start the Server (using STDIO transport) ---
|
|
72
|
+
Legate.logger.info('Starting fast-mcp server with STDIO transport...')
|
|
73
|
+
# This will block and listen for JSON-RPC messages on STDIN/STDOUT
|
|
74
|
+
mcp_server.start
|
|
75
|
+
|
|
76
|
+
Legate.logger.info('Server finished.')
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# File: examples/advanced/mcp/mcp_server_async_tools.rb
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# --- Example: MCP Server with Async Legate Tools ---
|
|
5
|
+
#
|
|
6
|
+
# This script demonstrates how to expose both async tools and the
|
|
7
|
+
# CheckJobStatusTool via MCP. It shows:
|
|
8
|
+
# 1. How to wrap async tools for MCP exposure
|
|
9
|
+
# 2. How to expose the CheckJobStatusTool
|
|
10
|
+
# 3. How to use both tools together via MCP
|
|
11
|
+
#
|
|
12
|
+
# Prerequisites:
|
|
13
|
+
# - Run `bundle install` to install dependencies
|
|
14
|
+
# - Run this script: bundle exec ruby examples/advanced/mcp/mcp_server_async_tools.rb
|
|
15
|
+
#
|
|
16
|
+
# Example MCP client interactions:
|
|
17
|
+
# Initialize server:
|
|
18
|
+
# {"jsonrpc": "2.0", "method": "initialize", "params": {}, "id": 1}
|
|
19
|
+
#
|
|
20
|
+
# List tools:
|
|
21
|
+
# {"jsonrpc": "2.0", "method": "listTools", "params": {}, "id": 2}
|
|
22
|
+
#
|
|
23
|
+
# Call SleepyTool:
|
|
24
|
+
# {"jsonrpc": "2.0", "method": "callTool", "params": {"name": "sleepy_tool", "parameters": {"duration": 5}}, "id": 3}
|
|
25
|
+
#
|
|
26
|
+
# Check job status:
|
|
27
|
+
# {"jsonrpc": "2.0", "method": "callTool", "params": {"name": "check_job_status", "parameters": {"job_id": "job_id_from_sleepy_tool"}}, "id": 4}
|
|
28
|
+
#
|
|
29
|
+
# -------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __dir__)
|
|
32
|
+
require 'legate'
|
|
33
|
+
Legate.load_environment # Handle Bundler, Dotenv, etc.
|
|
34
|
+
|
|
35
|
+
require 'legate'
|
|
36
|
+
require 'legate/mcp'
|
|
37
|
+
require 'fast_mcp'
|
|
38
|
+
require 'legate/tools/sleepy_tool'
|
|
39
|
+
require 'legate/tools/check_job_status_tool'
|
|
40
|
+
|
|
41
|
+
# Configure Legate logger
|
|
42
|
+
Legate.logger.level = Logger::INFO
|
|
43
|
+
|
|
44
|
+
# Create and register the SleepyTool
|
|
45
|
+
class SleepyToolWrapper < FastMcp::Tool
|
|
46
|
+
description 'Sleep for a specified duration'
|
|
47
|
+
|
|
48
|
+
arguments do
|
|
49
|
+
required(:duration).filled(:integer, gt?: 0).description('Duration to sleep in seconds')
|
|
50
|
+
required(:message).filled(:string).description('A message to include in the final result')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def call(args)
|
|
54
|
+
tool = Legate::Tools::SleepyTool.new
|
|
55
|
+
context = Legate::ToolContext.new(
|
|
56
|
+
session_id: 'mcp-session',
|
|
57
|
+
user_id: 'mcp-user',
|
|
58
|
+
app_name: 'mcp-server',
|
|
59
|
+
tool_registry: Legate::ToolRegistry.new
|
|
60
|
+
)
|
|
61
|
+
result = tool.execute({ duration: args[:duration], message: args[:message] }, context)
|
|
62
|
+
|
|
63
|
+
case result[:status]
|
|
64
|
+
when :success
|
|
65
|
+
result[:result]
|
|
66
|
+
when :pending
|
|
67
|
+
{
|
|
68
|
+
status: 'pending',
|
|
69
|
+
job_id: result[:job_id],
|
|
70
|
+
message: "Sleepy job started with duration #{args[:duration]}s and message: #{args[:message]}"
|
|
71
|
+
}
|
|
72
|
+
when :error
|
|
73
|
+
raise StandardError, result[:error_message]
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Create and register the CheckJobStatusTool
|
|
79
|
+
class CheckJobStatusToolWrapper < FastMcp::Tool
|
|
80
|
+
description 'Check the status of a background job'
|
|
81
|
+
|
|
82
|
+
arguments do
|
|
83
|
+
required(:job_id).filled(:string, min_size?: 1).description('The ID of the job to check')
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def call(args)
|
|
87
|
+
tool = Legate::Tools::CheckJobStatusTool.new
|
|
88
|
+
context = Legate::ToolContext.new(
|
|
89
|
+
session_id: 'mcp-session',
|
|
90
|
+
user_id: 'mcp-user',
|
|
91
|
+
app_name: 'mcp-server',
|
|
92
|
+
tool_registry: Legate::ToolRegistry.new
|
|
93
|
+
)
|
|
94
|
+
result = tool.execute({ job_id: args[:job_id] }, context)
|
|
95
|
+
|
|
96
|
+
case result[:status]
|
|
97
|
+
when :success
|
|
98
|
+
result[:result]
|
|
99
|
+
when :pending
|
|
100
|
+
{ status: 'pending', job_id: args[:job_id], message: result[:message] }
|
|
101
|
+
when :error
|
|
102
|
+
raise StandardError, result[:error_message]
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Register the tools with the server
|
|
108
|
+
server = FastMcp::Server.new(
|
|
109
|
+
name: 'async-tools',
|
|
110
|
+
version: '1.0.0'
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
server.register_tool(SleepyToolWrapper)
|
|
114
|
+
server.register_tool(CheckJobStatusToolWrapper)
|
|
115
|
+
|
|
116
|
+
# Start the server
|
|
117
|
+
Legate.logger.info('Starting MCP server with async tools...')
|
|
118
|
+
begin
|
|
119
|
+
server.start
|
|
120
|
+
rescue Interrupt
|
|
121
|
+
Legate.logger.info('Shutting down MCP server...')
|
|
122
|
+
end
|