Class: Prescient::MCP::Server

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

Overview

Dependency-free MCP capability adapter over Prescient's public API.

Constant Summary collapse

TOOL_DEFINITIONS =

rubocop:disable Layout/HashAlignment

{
  "prescient_generate" => {
    description:  "Generate a text response.",
    input_schema: { type: "object", required: ["prompt"] }
  },
  "prescient_embed" => {
    description:  "Generate an embedding.",
    input_schema: { type: "object", required: ["input"] }
  },
  "prescient_providers" => {
    description:  "List configured providers.",
    input_schema: { type: "object" }
  },
  "prescient_health" => {
    description:  "Check configured provider health.",
    input_schema: { type: "object" }
  },
  "prescient_agent" => {
    description:  "Run a bounded agent task with explicitly supplied tools.",
    input_schema: { type: "object", required: ["prompt"] }
  }
}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(configuration: Configuration.new, client_factory: nil, authorization: nil) ⇒ Server

rubocop:enable Layout/HashAlignment



32
33
34
35
36
# File 'lib/prescient/mcp/server.rb', line 32

def initialize(configuration: Configuration.new, client_factory: nil, authorization: nil)
  @configuration = configuration
  @client_factory = client_factory || ->(provider) { Prescient.client(provider) }
  @authorization = authorization
end

Instance Method Details

#call_tool(name, arguments = {}, context: {}) ⇒ Hash

Execute one enabled MCP tool.

Parameters:

  • name (String, Symbol)

    Tool name

  • arguments (Hash) (defaults to: {})

    Tool arguments

  • context (Hash) (defaults to: {})

    Request-scoped context

Returns:

  • (Hash)

    MCP tool result



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/prescient/mcp/server.rb', line 91

def call_tool(name, arguments = {}, context: {})
  ensure_enabled!(name)
  validate_input(arguments)
  authorize!(name, arguments, context)
  result = case name.to_s
           when "prescient_generate" then generate(arguments)
           when "prescient_embed" then embed(arguments)
           when "prescient_providers" then providers
           when "prescient_health" then health(arguments)
           when "prescient_agent" then agent(arguments, context)
           else raise ArgumentError, "MCP tool not enabled: #{name}"
           end
  { content: [{ type: "text", text: JSON.generate(result) }], isError: false }
rescue StandardError => e
  { content: [{ type: "text", text: JSON.generate(error: safe_error(e)) }], isError: true }
end

#dispatch(request, context: {}) ⇒ Hash

Dispatch one MCP JSON-RPC method.

Parameters:

  • request (Hash)

    JSON-RPC request

  • context (Hash) (defaults to: {})

    Request-scoped context

Returns:

  • (Hash)

    Method result



54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/prescient/mcp/server.rb', line 54

def dispatch(request, context: {})
  case request["method"]
  when "initialize" then initialize_result
  when "notifications/initialized", "notifications/cancelled", "notifications/progress" then nil
  when "tools/list" then { tools: }
  when "tools/call"
    call_tool(request.dig("params", "name"), request.dig("params", "arguments") || {}, context:)
  when "resources/list" then { resources: }
  when "resources/read" then read_resource(request.dig("params", "uri"))
  else raise ArgumentError, "MCP method not found: #{request["method"]}"
  end
end

#initialize_resultHash

Return the MCP initialization response.

Returns:

  • (Hash)

    Server capabilities and metadata



40
41
42
43
44
45
46
47
48
# File 'lib/prescient/mcp/server.rb', line 40

def initialize_result
  # rubocop:disable Layout/HashAlignment
  {
    protocolVersion: "2025-06-18",
    serverInfo:   { name: @configuration.name, version: @configuration.version },
    capabilities: { tools: {}, resources: {} }
  }
  # rubocop:enable Layout/HashAlignment
end

#read_resource(uri) ⇒ Hash

Read one enabled MCP resource.

Parameters:

  • uri (String, Symbol)

    Resource URI

Returns:

  • (Hash)

    MCP resource response



111
112
113
114
115
116
117
118
# File 'lib/prescient/mcp/server.rb', line 111

def read_resource(uri)
  payload = case uri.to_s
            when "prescient://providers" then providers
            when "prescient://health" then health({})
            else raise ArgumentError, "MCP resource not enabled: #{uri}"
            end
  { contents: [{ uri:, mimeType: "application/json", text: JSON.generate(payload) }] }
end

#resourcesArray<Hash>

Return enabled MCP resources.

Returns:

  • (Array<Hash>)

    Discoverable resources



78
79
80
81
82
83
84
# File 'lib/prescient/mcp/server.rb', line 78

def resources
  @configuration.resources.filter_map do |uri|
    next unless Configuration::SUPPORTED_RESOURCES.include?(uri)

    { uri:, name: uri.delete_prefix("prescient://"), mimeType: "application/json" }
  end
end

#toolsArray<Hash>

Return enabled MCP tool definitions.

Returns:

  • (Array<Hash>)

    Discoverable tools



69
70
71
72
73
74
# File 'lib/prescient/mcp/server.rb', line 69

def tools
  @configuration.tools.filter_map do |name|
    definition = TOOL_DEFINITIONS[name]
    definition && { name:, **definition }
  end
end