Module: Tep::MCP

Defined in:
lib/tep/mcp.rb

Defined Under Namespace

Classes: ResourceContent, Result

Constant Summary collapse

PROTOCOL_VERSION =

MCP protocol version this server claims to speak. Tracks the 2025-03 (“Streamable HTTP”) revision of the spec.

"2025-03-26"

Class Method Summary collapse

Class Method Details

.error(s) ⇒ Object



50
51
52
53
54
55
# File 'lib/tep/mcp.rb', line 50

def self.error(s)
  r = Tep::MCP::Result.new
  r.text     = s
  r.is_error = 1
  r
end

.initialize_envelope(req_id, server_name, server_version) ⇒ Object

JSON-RPC 2.0 response envelope for ‘initialize`. The MCP client expects serverInfo + capabilities + protocolVersion. capabilities lists which method groups this server speaks.



106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/tep/mcp.rb', line 106

def self.initialize_envelope(req_id, server_name, server_version)
  "{\"jsonrpc\":\"2.0\",\"id\":" + req_id.to_s + "," +
    "\"result\":{" +
      "\"protocolVersion\":\"" + Tep::MCP::PROTOCOL_VERSION + "\"," +
      "\"capabilities\":{\"tools\":{},\"resources\":{}}," +
      "\"serverInfo\":{" +
        "\"name\":"    + Tep::Json.quote(server_name)    + "," +
        "\"version\":" + Tep::Json.quote(server_version) +
      "}" +
    "}" +
  "}"
end

.method_not_found_envelope(req_id, method_name) ⇒ Object

Error envelope for an unrecognized JSON-RPC method. Spec code -32601 (method not found).



195
196
197
198
199
200
201
# File 'lib/tep/mcp.rb', line 195

def self.method_not_found_envelope(req_id, method_name)
  "{\"jsonrpc\":\"2.0\",\"id\":" + req_id.to_s + "," +
    "\"error\":{\"code\":-32601," +
      "\"message\":" + Tep::Json.quote("method not found: " + method_name) +
    "}" +
  "}"
end

.nested_extract(json, key) ⇒ Object

Pull a nested JSON value out of ‘json` by top-level key, returning the value’s JSON-string form. Used by the translator-emitted dispatcher to dig ‘params` out of the JSON-RPC envelope, then `arguments` out of params, before handing the arguments sub-object to the per-tool cmeth.

Returns “{}” when the key isn’t present (so downstream Tep::Json.get_str / get_int calls see an empty object that returns their zero-default cleanly).



91
92
93
94
95
96
97
98
99
100
101
# File 'lib/tep/mcp.rb', line 91

def self.nested_extract(json, key)
  pos = Tep::Json.find_value_start(json, key)
  if pos < 0
    return "{}"
  end
  end_pos = Tep::Json.skip_value(json, pos)
  if end_pos <= pos
    return "{}"
  end
  json[pos, end_pos - pos]
end

.resource_text(uri, text) ⇒ Object

Build a text-mime resource content block. URI is the resource’s identifier (echoed back to the client so clients can correlate the response with the request).



74
75
76
77
78
79
80
# File 'lib/tep/mcp.rb', line 74

def self.resource_text(uri, text)
  c = Tep::MCP::ResourceContent.new
  c.uri  = uri
  c.mime = "text/plain"
  c.text = text
  c
end

.resources_list_envelope(req_id, resources_array_json) ⇒ Object

Wrap a pre-built resources-array JSON string into the resources/list response envelope. Same shape as tools_list_envelope – translator emits the array literally at compile time so spinel doesn’t need to walk it at runtime.



152
153
154
155
156
# File 'lib/tep/mcp.rb', line 152

def self.resources_list_envelope(req_id, resources_array_json)
  "{\"jsonrpc\":\"2.0\",\"id\":" + req_id.to_s + "," +
    "\"result\":{\"resources\":" + resources_array_json + "}" +
  "}"
end

.resources_read_envelope(req_id, uri, mime, text) ⇒ Object

Wrap a ResourceContent into a resources/read response envelope. contents is a one-element array per MCP spec; the uri / mimeType / text fields are read off as scalars (same spinel-friendly pattern as tools_call_envelope) before being spliced into the JSON.



163
164
165
166
167
168
169
170
171
# File 'lib/tep/mcp.rb', line 163

def self.resources_read_envelope(req_id, uri, mime, text)
  "{\"jsonrpc\":\"2.0\",\"id\":" + req_id.to_s + "," +
    "\"result\":{\"contents\":[" +
      "{\"uri\":" + Tep::Json.quote(uri) + "," +
       "\"mimeType\":" + Tep::Json.quote(mime) + "," +
       "\"text\":" + Tep::Json.quote(text) + "}" +
    "]}" +
  "}"
end

.text(s) ⇒ Object



43
44
45
46
47
48
# File 'lib/tep/mcp.rb', line 43

def self.text(s)
  r = Tep::MCP::Result.new
  r.text     = s
  r.is_error = 0
  r
end

.tools_call_envelope(req_id, text, is_error) ⇒ Object

Wrap a tool’s text + error-flag into the tools/call response envelope. content is a one-element array with a text block. Takes scalars rather than the Result struct directly so spinel tracks the String param locally through json_quote without going through attr_accessor return-type inference.



133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/tep/mcp.rb', line 133

def self.tools_call_envelope(req_id, text, is_error)
  is_err_str = "false"
  if is_error == 1
    is_err_str = "true"
  end
  "{\"jsonrpc\":\"2.0\",\"id\":" + req_id.to_s + "," +
    "\"result\":{" +
      "\"content\":[" +
        "{\"type\":\"text\",\"text\":" + Tep::Json.quote(text) + "}" +
      "]," +
      "\"isError\":" + is_err_str +
    "}" +
  "}"
end

.tools_list_envelope(req_id, tools_array_json) ⇒ Object

Wrap a pre-built tools-array JSON string into the tools/list response envelope. tools_array_json is the literal ‘[…, …]` the translator emits at compile time.



122
123
124
125
126
# File 'lib/tep/mcp.rb', line 122

def self.tools_list_envelope(req_id, tools_array_json)
  "{\"jsonrpc\":\"2.0\",\"id\":" + req_id.to_s + "," +
    "\"result\":{\"tools\":" + tools_array_json + "}" +
  "}"
end

.unknown_resource_envelope(req_id, uri) ⇒ Object

Error envelope for resources/read on an unknown URI. Same JSON-RPC code as unknown_tool (-32602 invalid params).



175
176
177
178
179
180
181
# File 'lib/tep/mcp.rb', line 175

def self.unknown_resource_envelope(req_id, uri)
  "{\"jsonrpc\":\"2.0\",\"id\":" + req_id.to_s + "," +
    "\"error\":{\"code\":-32602," +
      "\"message\":" + Tep::Json.quote("unknown resource: " + uri) +
    "}" +
  "}"
end

.unknown_tool_envelope(req_id, tool_name) ⇒ Object

Error envelope for tools/call on an unknown tool name. Sent as a JSON-RPC error (-32602 invalid params) per the spec.



185
186
187
188
189
190
191
# File 'lib/tep/mcp.rb', line 185

def self.unknown_tool_envelope(req_id, tool_name)
  "{\"jsonrpc\":\"2.0\",\"id\":" + req_id.to_s + "," +
    "\"error\":{\"code\":-32602," +
      "\"message\":" + Tep::Json.quote("unknown tool: " + tool_name) +
    "}" +
  "}"
end