Class: Ask::MCP::Server::Stdio

Inherits:
Object
  • Object
show all
Defined in:
lib/ask/mcp/server/stdio.rb

Overview

MCP server over stdio transport.

Constant Summary collapse

MAX_RESULT_CACHE =
100
PROTOCOL_VERSION =

Deprecated: use Ask::MCP::PROTOCOL_VERSION (the canonical constant).

Ask::MCP::PROTOCOL_VERSION

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name:, tools: [], capabilities: {}, resources: {}, prompts: {}, resource_templates: {}, debug: false, tool_timeout: nil, cache_ttl_ms: 60_000, cache_scope: "private") ⇒ Stdio

Returns a new instance of Stdio.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/ask/mcp/server/stdio.rb', line 17

def initialize(name:, tools: [], capabilities: {}, resources: {}, prompts: {},
               resource_templates: {}, debug: false, tool_timeout: nil,
               cache_ttl_ms: 60_000, cache_scope: "private")
  @name = name
  @capabilities = capabilities
  @resources = resources
  @prompts = prompts
  @resource_templates = resource_templates
  @debug = debug
  @tool_timeout = tool_timeout
  @cache_ttl_ms = cache_ttl_ms
  @cache_scope = cache_scope

  @adapter = Adapters::ToolServer.new(tools || [])
  @initialized = false
  @running = false
  @shutdown_requested = false
  @result_cache = {}
  # Negotiated protocol version. nil until the client tells us which
  # revision it speaks (legacy `initialize` or stateless `_meta`).
  @protocol_version = nil
  @stateless = false
end

Instance Attribute Details

#capabilitiesObject (readonly)

Returns the value of attribute capabilities.



15
16
17
# File 'lib/ask/mcp/server/stdio.rb', line 15

def capabilities
  @capabilities
end

#nameObject (readonly)

Returns the value of attribute name.



15
16
17
# File 'lib/ask/mcp/server/stdio.rb', line 15

def name
  @name
end

#promptsObject (readonly)

Returns the value of attribute prompts.



15
16
17
# File 'lib/ask/mcp/server/stdio.rb', line 15

def prompts
  @prompts
end

#resourcesObject (readonly)

Returns the value of attribute resources.



15
16
17
# File 'lib/ask/mcp/server/stdio.rb', line 15

def resources
  @resources
end

#toolsObject (readonly)

Returns the value of attribute tools.



15
16
17
# File 'lib/ask/mcp/server/stdio.rb', line 15

def tools
  @tools
end

Instance Method Details

#notify_prompts_list_changedObject



91
92
93
# File 'lib/ask/mcp/server/stdio.rb', line 91

def notify_prompts_list_changed
  send_notification("notifications/prompts/list_changed")
end

#notify_resources_list_changedObject



87
88
89
# File 'lib/ask/mcp/server/stdio.rb', line 87

def notify_resources_list_changed
  send_notification("notifications/resources/list_changed")
end

#notify_tools_list_changedObject

Emit notifications/tools/list_changed (2026-07-28: consumed by clients on the shared stdio channel or on a subscriptions/listen stream). Call these after your tool/resource/prompt sets change. Safe to call from any thread; writes are flushed immediately.



83
84
85
# File 'lib/ask/mcp/server/stdio.rb', line 83

def notify_tools_list_changed
  send_notification("notifications/tools/list_changed")
end

#running?Boolean

Returns:

  • (Boolean)


75
76
77
# File 'lib/ask/mcp/server/stdio.rb', line 75

def running?
  @running
end

#startObject



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/ask/mcp/server/stdio.rb', line 41

def start
  @running = true
  $stdout.sync = true

  # Graceful shutdown: on TERM/HUP, set flag and close stdin to
  # unblock the read loop so the current tool call can finish.
  trap("TERM") { graceful_shutdown }
  trap("HUP")  { graceful_shutdown }

  debug_log "Server starting: #{@name} (PID #{Process.pid})"
  debug_log "Tools: #{@adapter.definitions.map { |d| d[:name] }.join(', ')}"

  while @running && !@shutdown_requested && (line = $stdin.gets)
    line = line.strip
    next if line.empty?
    process_line(line)
  end

  debug_log "stdin closed — exiting"
rescue Errno::EBADF, IOError
  # stdin closed externally (e.g. from trap handler)
rescue SignalException
  # SIGTERM or SIGHUP during blocked read
ensure
  @running = false
  @shutdown_requested = false
  trap("TERM", "DEFAULT")
  trap("HUP", "DEFAULT")
end

#stopObject



71
72
73
# File 'lib/ask/mcp/server/stdio.rb', line 71

def stop
  @running = false
end