Have Claude yell at you with MCP and bash

There’s a lot of excitement currently with Anthropic’s Model Context Protocol MCP and “servers” to allow language models to do more stuff.

Their tutorials use libraries with a lot of code (the npm one is 5MB!)

What if there was a simpler way for an infra person to get started?

That’s right, which if it could be done with ~ 35 lines of bash?

What’s needed?

You need something that runs, and accepts text on stdin - and fulfils an API

So, how can I get an LLM to yell at me?

First, write the “say-mcp” script, which will use “say” on macos for text to speech (tts) to say whatever string the language model sent to it.

For this example, write the following to /tmp/say-mcp.sh and chmod +x /tmp/say-mcp.sh it to ensure it can be executed


#!/bin/bash

echo "Starting mcp_add.sh" >> /tmp/say-mcp-requests.log

while read -r line; do
    echo $line >> /tmp/say-mcp-requests.log
    # Parse JSON input using jq
    method=$(echo "$line" | jq -r '.method' 2>/dev/null)
    id=$(echo "$line" | jq -r '.id' 2>/dev/null)
    if [[ "$method" == "initialize" ]]; then
        echo '{"jsonrpc":"2.0","id":'"$id"',"result":{"protocolVersion":"2024-11-05","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"say","version":"0.0.1"}}}'

    elif [[ "$method" == "notifications/initialized" ]]; then
        : #do nothing

    elif [[ "$method" == "tools/list" ]]; then
        echo '{"jsonrpc":"2.0","id":'"$id"',"result":{"tools":[{"name":"say","description":"Says the provided string with text to speech.\n\nArgs:\n    text\n","inputSchema":{"properties":{"text":{"title":"Text","type":"string"}},"required":["text"],"type":"object"}}]}}'

    elif [[ "$method" == "resources/list" ]]; then
        echo '{"jsonrpc":"2.0","id":'"$id"',"result":{"resources":[]}}'

    elif [[ "$method" == "prompts/list" ]]; then
        echo '{"jsonrpc":"2.0","id":'"$id"',"result":{"prompts":[]}}'

    elif [[ "$method" == "tools/call" ]]; then
        #{"method":"tools/call","params":{"name":"addition","arguments":{"num1":"1","num2":"2"}},"jsonrpc":"2.0","id":20}
        tool_method=$(echo "$line" | jq -r '.params.name' 2>/dev/null)
        speech=$(echo "$line" | jq -r '.params.arguments.text' 2>/dev/null)
        say "${speech}"
        echo '{"jsonrpc":"2.0","id":'"$id"',"result":{"content":[{"type":"text","text":"said ${speech}"}],"isError":false}}'

    else
        echo '{"jsonrpc":"2.0","id":'"$id"',"error":{"code":-32601,"message":"Method not found"}}'
    fi
done || break

Now we have our “MCP server” we need to tell Claude to use it.

Open Claude settings, go to developer, then click “Edit Config”

Add the “mcpServers” section, the important part is the “say” object, and the command needs to be the path of the shell script

{
  "mcpServers": {
    "say": {
      "command": "/tmp/say-mcp.sh",
      "args": []
    }
  }
}

Restart Claude, and you should now have the ability to ask Claude to say/yell stuff at you.

Credits

Thanks to https://github.com/antonum/mcp-server-bash/tree/main for the original idea


Copyright © 2025 Richard Finlay Tweed. All rights reserved. All views expressed are my own