Skip to content

oavlloh-wq/msglint

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

msglint

Python License: MIT Dependencies Status

Pre-flight validator for the Anthropic Messages API. Catch the structural mistakes that make messages.create come back with a cryptic 400before you spend the network round-trip (and the time debugging it).

You know these errors:

messages: roles must alternate between "user" and "assistant"
messages.1: each `tool_use` block must have a corresponding `tool_result` ...
messages.0: text content blocks must be non-empty

msglint finds all of them locally, points at the exact block, and tells you how to fix it. Zero dependencies, no API key, no network.


What it catches

  • ❌ a system role smuggled into the messages list (it's a top-level param)
  • ❌ non-alternating roles / a conversation that doesn't start with user
  • ❌ a tool_use with no matching tool_result in the next user turn (dangling)
  • ❌ a tool_result that references a tool_use id that was never declared (orphan)
  • ❌ duplicate tool_use ids
  • ❌ empty text / empty content lists (the API rejects them)
  • tool_use in a user turn, or tool_result in an assistant turn
  • ❌ malformed image blocks (missing media_type, bad source.type, …)
  • ⚠️ tool_use naming a tool that isn't in your tools list
  • ⚠️ unrecognized block types

Install

pip install msglint
# or, from source:
git clone https://github.com/oavlloh-wq/msglint && pip install -e msglint

Use it as a library

from msglint import lint, format_report

messages = [
    {"role": "user", "content": "weather in Paris?"},
    {"role": "assistant", "content": [
        {"type": "tool_use", "id": "toolu_1", "name": "get_weather", "input": {"city": "Paris"}}
    ]},
    # ...forgot the tool_result here
]

issues = lint(messages, tools=[{"name": "get_weather"}])
if any(i.is_error for i in issues):
    print(format_report(issues))
    raise SystemExit("payload is not safe to send")

Wire it into your client wrapper so a bad payload never leaves the building:

def safe_create(client, **kwargs):
    issues = lint(kwargs.get("messages"), system=kwargs.get("system"),
                  tools=kwargs.get("tools"))
    errs = [i for i in issues if i.is_error]
    if errs:
        raise ValueError("msglint: " + "; ".join(str(e) for e in errs))
    return client.messages.create(**kwargs)

Use it from the CLI

msglint payload.json          # lint a saved request payload
cat payload.json | msglint -  # read from stdin
msglint --selftest            # offline demo, no API key needed

It accepts either a bare messages list or a full request object ({"messages": [...], "system": ..., "tools": [...]}). Exit code is 1 when there are errors, so it drops straight into CI or a pre-commit hook.

Console demo

$ msglint --selftest

### An assistant tool_use with no following tool_result
msglint
  [ERROR] dangling-tool-use      messages[1].content[0]: tool_use id 'toolu_x' is never answered;
                                 append a user message with a matching tool_result before sending
  1 error(s), 0 warning(s)

### A tool_result that references nothing
msglint
  [ERROR] orphan-tool-result     messages[2].content[0]: tool_result references 'toolu_ghost',
                                 which no preceding assistant tool_use declared
  1 error(s), 0 warning(s)

### A system role smuggled into messages
msglint
  [ERROR] system-role            messages[0].role: 'system' is not a valid message role;
                                 pass it as the top-level `system` parameter instead
  1 error(s), 0 warning(s)

API

lint(messages, *, system=None, tools=None) -> list[Issue]

Returns a list of Issue(level, code, path, message). An empty list means the payload is structurally sound. Pass system/tools for extra checks (e.g. a system role hiding in messages, or a tool_use naming an undefined tool). format_report(issues, source=None) -> str renders them for humans.

License

MIT

About

Pre-flight validator for the Anthropic Messages API — catch the structural bugs that cause 400s before you send.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages