Class: ClaudeMemory::MCP::Server

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

Overview

MCP JSON-RPC server over stdio. Reads newline-delimited JSON requests from input, dispatches to Tools, and writes JSON responses to output.

Constant Summary collapse

PROTOCOL_VERSION =
"2024-11-05"
ORPHAN_CHECK_INTERVAL_SECONDS =
30

Instance Method Summary collapse

Constructor Details

#initialize(store_or_manager, input: $stdin, output: $stdout, parent_pid: Process.ppid) ⇒ Server

Returns a new instance of Server.

Parameters:

  • store_or_manager (Store::SQLiteStore, Store::StoreManager)

    database backend

  • input (IO) (defaults to: $stdin)

    input stream for JSON-RPC requests (default: $stdin)

  • output (IO) (defaults to: $stdout)

    output stream for JSON-RPC responses (default: $stdout)

  • parent_pid (Integer) (defaults to: Process.ppid)

    pid to watch for orphaning (default: caller’s parent)



23
24
25
26
27
28
29
30
31
# File 'lib/claude_memory/mcp/server.rb', line 23

def initialize(store_or_manager, input: $stdin, output: $stdout, parent_pid: Process.ppid)
  @store_or_manager = store_or_manager
  @tools = Tools.new(store_or_manager)
  @telemetry = Telemetry.new(store_or_manager)
  @input = input
  @output = output
  @parent_pid = parent_pid
  @running = false
end

Instance Method Details

#orphaned?Boolean

True once our original parent has exited and we’ve been reparented (to PID 1 / a subreaper). Claude Code spawns one stdio MCP server per session; a hard kill of the client can leave the server blocked on ‘gets` forever, holding a SQLite connection — the orphan leak in issue #7, Finding 3. The watchdog uses this to terminate.

Returns:

  • (Boolean)


53
54
55
# File 'lib/claude_memory/mcp/server.rb', line 53

def orphaned?
  @parent_pid > 1 && Process.ppid != @parent_pid
end

#runvoid

This method returns an undefined value.

Start the read loop, blocking until input is exhausted or stop is called.



35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/claude_memory/mcp/server.rb', line 35

def run
  @running = true
  watchdog = start_orphan_watchdog
  while @running
    line = @input.gets
    break unless line

    handle_message(line.strip)
  end
ensure
  watchdog&.kill
end

#stopvoid

This method returns an undefined value.

Signal the read loop to exit after the current message.



59
60
61
# File 'lib/claude_memory/mcp/server.rb', line 59

def stop
  @running = false
end