Class: Insika::McpStore
- Inherits:
-
Object
- Object
- Insika::McpStore
- Includes:
- Coercion
- Defined in:
- lib/insika/mcp_store.rb
Overview
MCP instances authored at runtime. One
record per instance in the ConfigStore (scope "mcp"), keyed by the name slug
(tavily, github, ...). Holds transport/command/url, the enabled flag and
a Hash of env credentials (tokens/keys the instance injects into the server).
The credentials (env) NEVER leave here in plaintext to the UI: the display
reads (get/all) mask EACH value with the __OCULTO__ sentinel. Only
get_raw/all_raw (consumed by an MCP client, never by the screen) return
the real values. On write, the sentinel coming back preserves the value; a new
string replaces it; "" clears it (see Insika::SecretMasking, the same pattern
as the LLM api_keys).
Current scope: durable config CRUD (the instances UI). Running an MCP client against these instances is later runtime work — the store is the editable source from now on.
Constant Summary collapse
- SCOPE =
"mcp"
Instance Method Summary collapse
-
#all ⇒ Object
-> [Hash] all MASKED (for the UI).
-
#all_raw ⇒ Object
-> [Hash] all with REAL env (for an MCP client).
-
#delete(name) ⇒ Object
-> bool (did it exist?).
-
#get(name) ⇒ Object
-> MASKED Hash (env with sentinel) | nil.
-
#get_raw(name) ⇒ Object
-> Hash with REAL env | nil.
-
#initialize(config_store:) ⇒ McpStore
constructor
A new instance of McpStore.
-
#names ⇒ Object
-> [String] slugs, lexicographic order.
-
#upsert(attrs) ⇒ Object
Upsert with per-env-key secret reconciliation.
Methods included from Coercion
blank?, deep_stringify, presence, present?, utf8
Constructor Details
#initialize(config_store:) ⇒ McpStore
Returns a new instance of McpStore.
24 25 26 |
# File 'lib/insika/mcp_store.rb', line 24 def initialize(config_store:) @cs = config_store end |
Instance Method Details
#all ⇒ Object
-> [Hash] all MASKED (for the UI).
42 43 44 |
# File 'lib/insika/mcp_store.rb', line 42 def all names.filter_map { |n| get(n) } end |
#all_raw ⇒ Object
-> [Hash] all with REAL env (for an MCP client). Never goes to the screen.
47 48 49 |
# File 'lib/insika/mcp_store.rb', line 47 def all_raw names.filter_map { |n| raw(n) } end |
#delete(name) ⇒ Object
-> bool (did it exist?).
76 |
# File 'lib/insika/mcp_store.rb', line 76 def delete(name) = @cs.delete(SCOPE, name.to_s) |
#get(name) ⇒ Object
-> MASKED Hash (env with sentinel) | nil.
29 30 31 |
# File 'lib/insika/mcp_store.rb', line 29 def get(name) mask(raw(name)) end |
#get_raw(name) ⇒ Object
-> Hash with REAL env | nil. Internal use (MCP client), never the screen.
34 35 36 |
# File 'lib/insika/mcp_store.rb', line 34 def get_raw(name) raw(name) end |
#names ⇒ Object
-> [String] slugs, lexicographic order.
39 |
# File 'lib/insika/mcp_store.rb', line 39 def names = @cs.keys(SCOPE) |
#upsert(attrs) ⇒ Object
Upsert with per-env-key secret reconciliation. attrs
(string|symbol keys):
name (required), transport, command, url, description,
enabled (bool), env ({ "KEY" => value|sentinel|"" })
-> MASKED Hash (the stored record).
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/insika/mcp_store.rb', line 56 def upsert(attrs) h = symbolize(attrs) name = presence(h[:name]) raise Insika::ValidationError, "name is required" if name.nil? existing = raw(name) record = { "name" => name, "transport" => presence(h[:transport]) || "stdio", "command" => presence(h[:command]), "url" => presence(h[:url]), "description" => presence(h[:description]), "enabled" => h.fetch(:enabled, true) ? true : false, "env" => reconcile_env(h[:env], existing && existing["env"]) } @cs.put(SCOPE, name, record) mask(record) end |