Merge pull request 'feat: structural schema lint for server definitions (#54)' (#58) from feat/54-schema-lint into main
CI / Lint (ruff) (push) Successful in 7s
CI / Tests (py3.10 / ubuntu-latest) (push) Successful in 9s
CI / Tests (py3.12 / windows-latest) (push) Successful in 20s
CI / Tests (py3.12 / ubuntu-latest) (push) Successful in 9s
CI / Tests (py3.13 / ubuntu-latest) (push) Successful in 9s
CI / Lint (ruff) (push) Successful in 7s
CI / Tests (py3.10 / ubuntu-latest) (push) Successful in 9s
CI / Tests (py3.12 / windows-latest) (push) Successful in 20s
CI / Tests (py3.12 / ubuntu-latest) (push) Successful in 9s
CI / Tests (py3.13 / ubuntu-latest) (push) Successful in 9s
This commit was merged in pull request #58.
This commit is contained in:
+57
@@ -1256,6 +1256,63 @@ def validate_servers(servers: list[ServerEntry]) -> list[str]:
|
||||
return problems
|
||||
|
||||
|
||||
def lint_server(name: str, data: dict) -> list[str]:
|
||||
"""Return non-blocking structural warnings for a single server definition.
|
||||
|
||||
Unlike validate_servers, nothing here blocks Save -- these are advisory
|
||||
notes about shapes that will round-trip through JSON fine but are
|
||||
probably not what the user intended (args given as a plain string
|
||||
instead of a list, an env value that isn't a string, an unrecognized
|
||||
`type`, unknown top-level fields, etc.).
|
||||
"""
|
||||
nm = name.strip() or "(unnamed)"
|
||||
warnings: list[str] = []
|
||||
|
||||
if "command" in data and not isinstance(data["command"], str):
|
||||
warnings.append(f"'{nm}': 'command' should be a string")
|
||||
|
||||
if "args" in data:
|
||||
args = data["args"]
|
||||
if not isinstance(args, list):
|
||||
warnings.append(f"'{nm}': 'args' should be a list (one argument per item)")
|
||||
elif any(not isinstance(a, str) for a in args):
|
||||
warnings.append(
|
||||
f"'{nm}': 'args' contains non-string values "
|
||||
"(they will be saved as-is; Claude expects strings)"
|
||||
)
|
||||
|
||||
for field in ("env", "headers"):
|
||||
if field not in data:
|
||||
continue
|
||||
val = data[field]
|
||||
if not isinstance(val, dict):
|
||||
warnings.append(f"'{nm}': '{field}' should be an object of string key/value pairs")
|
||||
elif any(not isinstance(v, str) for v in val.values()):
|
||||
warnings.append(
|
||||
f"'{nm}': '{field}' contains non-string values "
|
||||
"(they will be saved as-is; Claude expects strings)"
|
||||
)
|
||||
|
||||
if "type" in data:
|
||||
t = data["type"]
|
||||
if t not in ("http", "sse", "stdio"):
|
||||
warnings.append(f"'{nm}': 'type' should be one of http, sse, stdio (found {t!r})")
|
||||
|
||||
extra = sorted(k for k in data if k not in KNOWN_FIELDS)
|
||||
if extra:
|
||||
warnings.append(f"'{nm}': extra fields preserved as-is: {', '.join(extra)}")
|
||||
|
||||
return warnings
|
||||
|
||||
|
||||
def lint_servers(servers: list[ServerEntry]) -> list[str]:
|
||||
"""Concatenate lint_server warnings across every entry, in order."""
|
||||
out: list[str] = []
|
||||
for s in servers:
|
||||
out.extend(lint_server(s.name, s.data))
|
||||
return out
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Search / filter
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
Reference in New Issue
Block a user