Module: Insika::McpJson

Defined in:
lib/insika/mcp_json.rb

Overview

The de-facto mcpServers JSON format every MCP client (Claude Desktop, Cursor, ...) already uses — one parser shared by all three PR3 config surfaces (CLI insika mcp import, the API /v1/mcp PUT, Studio's "Import JSON" textarea).

{
"mcpServers": {
  "tavily":     { "url": "https://mcp.tavily.com/mcp", "headers": {...} },
  "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"] }
}
}

A server entry with command and no url is stdio; one with url and no command is http (Streamable HTTP, the modern default) unless it names "transport": "sse" explicitly — the bare format has no other way to spell SSE. export always writes transport back so a round-trip is lossless.

Class Method Summary collapse

Class Method Details

.export(mcp_store:) ⇒ Object

-> { "mcpServers" => { name => ... } }, secrets masked (never plaintext — mcp_store.all already masks, this only reshapes).



38
39
40
# File 'lib/insika/mcp_json.rb', line 38

def export(mcp_store:)
  { "mcpServers" => mcp_store.all.each_with_object({}) { |record, acc| acc[record["name"]] = server_from(record) } }
end

.import(json, mcp_store:) ⇒ Object

json — a JSON string or an already-parsed Hash (either key type). Upserts every entry via mcp_store (per-key secret reconciliation, same as any other upsert — re-importing an export's __OCULTO__ sentinel preserves whatever is already stored, never wipes it). -> [Hash] the masked upserted records, in the document's key order.



30
31
32
33
34
# File 'lib/insika/mcp_json.rb', line 30

def import(json, mcp_store:)
  data = json.is_a?(String) ? JSON.parse(json) : stringify(json)
  servers = stringify(data["mcpServers"] || {})
  servers.map { |name, cfg| mcp_store.upsert(attrs_from(name, stringify(cfg))) }
end