Skip to content
AI & Agents
Skill

/mmsp-python

Guidance for using the MMSP Python SDK (`mmsp`). Use when developing agents that call different LLM APIs, need a unified interface for LLM providers, mention MMSP, request `mmsp`, or already import it.

BOOST
From plugin
model-message-stream-protocol
1152 skills
Install
$ npx -y skills add Prism-Shadow/model-message-stream-protocol --skill mmsp-python --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/mmsp-python

Context preview

The summary Claude sees to decide when to auto-load this skill.

Guidance for using the MMSP Python SDK (`mmsp`). Use when developing agents that call different LLM APIs, need a unified interface for LLM providers, mention MMSP, request `mmsp`, or already import it.

SKILL.md

mmsp-python.SKILL.md
name: mmsp-python
description: Guidance for using the MMSP Python SDK (`mmsp`). Use when developing agents that call different LLM APIs, need a unified interface for LLM providers, mention MMSP, request `mmsp`, or already import it.

MMSP Python

MMSP is a unified SDK for calling LLMs across providers with shared data models, tool calling, tracing, and playground support.

Installation

uv add mmsp
# or
pip install mmsp

For model IDs, API keys, and base URLs, see [Model selection](reference/models.md).

Basic Usage

This example asks GPT to call a weather tool, runs the tool, then sends the result back.

import asyncio
from mmsp import AutoLLMClient


def get_weather(location: str) -> str:
    return f"Temperature in {location}: 22 C"


# Map tool names to their implementations so calls can be dispatched by name.
TOOLS = {"get_weather": get_weather}


async def main():
    weather_function = {
        "name": "get_weather",
        "description": "Gets the current weather for a given location.",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The city name"
                }
            },
            "required": ["location"]
        }
    }

    client = AutoLLMClient(model="gpt-5.5")
    config = {"tools": [weather_function]}

    tool_call = None
    async for event in client.streaming_response_stateful(
        message={
            "role": "user",
            "content_items": [{"type": "text.done", "text": "What's the weather in London?"}]
        },
        config=config
    ):
        if event["event_type"] == "stop":
            # Always the last event, exactly once: the response has finished.
            print(event["finish_reason"], event["usage_metadata"])
        for item in event["content_items"]:
            if item["type"] == "tool_call.done":  # the complete call; tool_call.delta items are fragments
                tool_call = item

    if tool_call:
        # Dispatch by tool name instead of hardcoding the function.
        result = TOOLS[tool_call["name"]](**tool_call["arguments"])

        async for event in client.streaming_response_stateful(
            message={
                "role": "user",
                "content_items": [
                    {
                        "type": "tool_result.done",
                        "text": result,
                        "tool_call_id": tool_call["tool_call_id"]
                    }
                ]
            },
            config=config
        ):
            print(event)
            # Streams the answer as text.delta fragments, closes it with text.done, then one stop event carrying usage:
            # {'role': 'assistant', 'event_type': 'delta', 'content_items': [{'type': 'text.delta', 'text': 'The'}], 'usage_metadata': None, 'finish_reason': None}
            # {'role': 'assistant', 'event_type': 'delta', 'content_items': [{'type': 'text.delta', 'text': ' weather'}], 'usage_metadata': None, 'finish_reason': None}
            # {'role': 'assistant', 'event_type': 'delta', 'content_items': [{'type': 'text.delta', 'text': ' is 22 C.'}], 'usage_metadata': None, 'finish_reason': None}
            # {'role': 'assistant', 'event_type': 'delta', 'content_items': [{'type': 'text.done', 'text': 'The weather is 22 C.'}], 'usage_metadata': None, 'finish_reason': None}
            # {'role': 'assistant', 'event_type': 'stop', 'content_items': [], 'usage_metadata': {'cached_tokens': 0, 'prompt_tokens': 12, 'thoughts_tokens': 0, 'response_tokens': 8}, 'finish_reason': 'stop'}


asyncio.run(main())

Notes

Agent loop rules:

  • Read tool calls from `tool_call.done` items. `tool_call.delta` items are argument fragments for live display only.
  • Send every tool result with the exact `tool_call_id` from its originating `tool_call.done`. Do not invent, normalize, or reuse IDs across unrelated tool calls.
  • If streamed tool-call arguments cannot be parsed, MMSP raises `ToolCallArgumentParseError` in place of the `tool_call.done`. Do not execute the tool from partial arguments; let the agent runtime retry or re-prompt the model.
  • Read usage and the finish reason from the `stop` event: it is always the last event, arrives exactly once, and always carries both. `delta` events carry `None` for both. A thinking-only response raises `EmptyResponseError` instead of the `stop` event; its `usage_metadata` still reports the tokens. A stream cut short before its usage or finish reason raises `IncompleteStreamError` instead; retry the request.
  • Write message items with the `.done` types (`text.done`, `tool_result.done`, …). Types without the suffix are still accepted, with a deprecation warning, until 0.6.0.
  • Preserve `thinking.done` and `inline_thinking.done` items. Do not strip or modify `fidelity` fields.
  • For embedding models, each `UniMessage` in the `messages` array produces **one embedding vector**. Within a single message, all items in `content_items` are aggregated into a single embedding. Set `embedding_config.dimensions` in the config to control vector size.

Reference

  • [Model selection](reference/models.md) — model IDs, client types, API keys, and base URLs.
  • [Data models](reference/data-models.md) — `UniConfig`, `UniMessage`, `UniEvent`, the streaming protocol, and errors.
  • [APIs](reference/api.md) — client initialization and method signatures.
  • [Tracer & Playground](reference/integrations.md) — local tracing UI and the manual chat playground.
Read more
Ships withmodel-message-stream-protocol

Integrate every model the same way, and keep one API in your head instead of one per provider.

Get the whole plugin
Stats
115
Stars
13
Forks
Active
Maintenance
TypeScript
Language
Apache-2.0
License
3h ago
Last commit
9mo ago
Created
4h ago
Added

Repo: Prism-Shadow/model-message-stream-protocol