Parse a pasted JSON args array correctly instead of mangling each token
Real incident: pasting gethomepage.dev's example args array (JSON, quotes/commas/brackets and all) into the plain "Arguments" field produced tokens like "mcp-remote," with the comma baked in, which crashed npx with EINVALIDTAGNAME on the literal package name "mcp-remote,". The existing char-by-char tokenizer treats quote characters as its own quoting mechanism and consumes them, so a JSON array's per-item quotes never get stripped and commas outside them become part of the token. parseArguments now tries decoding a well-formed JSON array of strings first (only when the whole trimmed input is bracket-wrapped valid JSON), falling back to the original shell-style tokenizer otherwise — so pasting a server's args straight from its JSON config now works. Tooltip and help doc updated; a partial paste (e.g. missing the opening bracket) still isn't valid JSON and falls back as before, documented as a known limitation rather than silently guessed at.
This commit is contained in:
@@ -1513,7 +1513,7 @@ Whenever the user asks you to translate something, translate it to Norwegian Bok
|
||||
</ol>
|
||||
|
||||
<div class="tip">
|
||||
<strong>💡 Tip:</strong> Arguments containing spaces can be quoted, e.g. <code>--root "/Users/you/My Documents"</code>.
|
||||
<strong>💡 Tip:</strong> Arguments containing spaces can be quoted, e.g. <code>--root "/Users/you/My Documents"</code>. If a server's documentation shows its config as JSON (an <code>"args": [...]</code> array), you can paste that array — brackets, quotes and all — directly into the Arguments field and Confab parses it correctly. Just paste the <em>whole</em> array, including both the opening <code>[</code> and closing <code>]</code> — a partial paste isn't valid JSON and falls back to being parsed as plain text, which mangles each entry with a stray comma or bracket left attached (a real example: pasting an incomplete array once produced an argument literally named <code>mcp-remote,</code>, which broke <code>npx</code>).
|
||||
</div>
|
||||
|
||||
<h3>Servers That Use <code>npx</code> (Node.js Required)</h3>
|
||||
|
||||
@@ -92,7 +92,25 @@ nonisolated struct ExternalMCPServer: Codable, Identifiable, Sendable, Equatable
|
||||
/// Splits a raw arguments string into tokens, respecting single/double-quoted
|
||||
/// segments so arguments containing spaces (e.g. `--root "/Users/x/My Documents"`)
|
||||
/// survive intact instead of being split on every space.
|
||||
/// If `input` (trimmed) is a syntactically valid JSON array of strings, decodes and returns it
|
||||
/// directly instead of falling through to the shell-style tokenizer below. MCP server configs
|
||||
/// are almost always distributed as JSON, and pasting an `"args": [...]` array's value straight
|
||||
/// into this single-line field is a very natural mistake — without this, the shell tokenizer
|
||||
/// treats each `"…",` as one token complete with its trailing comma (and the JSON array's own
|
||||
/// quote characters get consumed as its OWN quoting mechanism, not stripped), producing mangled
|
||||
/// tokens like `mcp-remote,` that break whatever actually consumes them. Real incident: pasting
|
||||
/// gethomepage.dev's example `args` array this way produced exactly that and crashed `npx` with
|
||||
/// `EINVALIDTAGNAME` on the literal package name `"mcp-remote,"`.
|
||||
nonisolated static func parseArgumentsAsJSONArray(_ input: String) -> [String]? {
|
||||
let trimmed = input.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard trimmed.hasPrefix("["), trimmed.hasSuffix("]"),
|
||||
let data = trimmed.data(using: .utf8) else { return nil }
|
||||
return try? JSONDecoder().decode([String].self, from: data)
|
||||
}
|
||||
|
||||
static func parseArguments(_ input: String) -> [String] {
|
||||
if let jsonArgs = parseArgumentsAsJSONArray(input) { return jsonArgs }
|
||||
|
||||
var args: [String] = []
|
||||
var current = ""
|
||||
var inSingleQuotes = false
|
||||
|
||||
@@ -1583,7 +1583,7 @@ It's better to admit "I need more information" or "I cannot do that" than to fak
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.font(.system(size: 13, design: .monospaced))
|
||||
.frame(width: 240)
|
||||
.help("Space-separated arguments")
|
||||
.help("Space-separated arguments, e.g. -y mcp-remote https://example.com --header \"Authorization: Bearer token\". Pasting a full JSON array (with brackets and quotes, as MCP docs often show it) also works.")
|
||||
}
|
||||
case .http:
|
||||
row("Server URL") {
|
||||
|
||||
Reference in New Issue
Block a user