Class: Textus::Surface::MCP::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/textus/surface/mcp/server.rb

Constant Summary collapse

CHECKPOINT_INTERVAL =
30
MCP_ADAPTER =
Textus::Surface::MCP::Adapters::McpAdapter.new

Instance Method Summary collapse

Constructor Details

#initialize(store:, role: Textus::Value::Role::DEFAULT, stdin: $stdin, stdout: $stdout) ⇒ Server

Returns a new instance of Server.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/textus/surface/mcp/server.rb', line 10

def initialize(store:, role: Textus::Value::Role::DEFAULT, stdin: $stdin, stdout: $stdout)
  @store  = store.with_role(role)
  @stdin  = stdin
  @stdout = stdout
  @shutdown = false
  @cursor_manager = CursorManager.new(store: @store, role:)

  @store = @cursor_manager.resume! || @store

  @sdk = MCP_ADAPTER.server(
    name: "textus",
    version: Textus::VERSION,
    tools: Catalog.build_tools(self),
    resources: build_resources,
    server_context: { mcp_server: self },
  )
  @sdk.resources_read_handler { |params, server_context:| handle_resource_read(params[:uri].to_s, server_context) }
end

Instance Method Details

#dispatch(verb_name, args, _server_context) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/textus/surface/mcp/server.rb', line 42

def dispatch(verb_name, args, _server_context)
  str_args = deep_stringify_keys(args)
  drifted = drift_detected?

  result = Catalog.call(verb_name.to_s, store: @store, args: str_args)
  result = DriftAnnotation.apply(
    result, verb_name:, drifted:, is_read: Catalog.read_verbs.include?(verb_name.to_s),
            current_etag: short_etag(contract_etag_now),
            previous_etag: short_etag(@store.contract_etag)
  )

  post_dispatch!(verb_name)
  ::MCP::Tool::Response.new([{ type: "text", text: JSON.dump(result) }])
rescue Textus::CursorExpired => e
  raise_handler_error(e.message, Textus::CursorExpired::JSONRPC_CODE)
rescue Textus::Surface::MCP::ToolError => e
  raise_handler_error(e.message, ToolError::JSONRPC_CODE)
rescue StandardError => e
  raise_handler_error("internal: #{e.message}", -32_603)
end

#runObject



29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/textus/surface/mcp/server.rb', line 29

def run
  @stdin.each_line do |line|
    line = line.strip
    next if line.empty?

    response = @sdk.handle_json(line)
    next unless response

    @stdout.puts(response)
    @stdout.flush
  end
end