Goose makes the client side visible, and that is where MCP gets real.
Here is something that caught me off guard when I first started working with MCP servers: the first bug you hit is often not in the server. It is in the client. A wrong config key, a missing auth header, or a prompt that is too vague can make a perfectly good server look broken.
The weather-alert-agent repo that I describe in the post came out of trying to show that gap in a way that is easy to follow and reproduce. It is not a polished demo but a working example of what happens when Goose has to discover a server, load its tools, and figure out how to use them in a real workflow.
To understand why, it helps to look at what a client actually does.
If something in here does not fit your setup, that is expected. Take what is useful and adjust. 😊
The client does more than connect
An MCP server exposes tools. A client decides what to do with them. That distinction sounds obvious, but it is easy to underestimate until you watch a real session. The server does not pick the tool. The client has to. And it also has to do much more than that; it must:
– discover the available tools at startup
– decide which tools belong in the current conversation
– pass the right arguments
– use the result to choose what happens next
The weather-alert-agent is a small example built to make that visible. The idea is simple: each morning, Goose checks the weather for a given city and sends the forecast as a text message. One prompt, three steps, a real delivery. It is narrow enough to follow but complete enough to show what the client layer actually does.
To build it, you need an MCP server that can send messages. I went with the Infobip Message MCP. It exposes a single tool: send_messages, with support for SMS, RCS, and Viber. It only covers sending, so there are no delivery reports, logs, or anything else loaded into the context window. For a simple notification workflow, that turned out to be enough, and the smaller footprint kept token usage low. Since I already have my Viber sender provisioned on my Infobip account (as part of the free trial period), I went with a shared Viber sender.
If you do need delivery reports or channel failover, the Viber MCP covers those, but it also loads more tools into every context window. Neither is wrong. It depends on what your workflow actually needs.
With the server picked, the next step is getting Goose to recognize it. That comes down to a short config block, but a few of the fields are less forgiving than they look.
The config details decide whether the server loads
Before Goose can use any MCP server, it needs to know where to find it and how to talk to it.
Goose refers to MCP servers as extensions, external add-ons that connect it to tools and services outside its built-in capabilities. Remote extensions, like the Infobip Message MCP, connect over `streamable HTTP`, which is why the config looks like the following.
extensions:
infobip-message:
name: Infobip Message
uri: https://mcp.infobip.com/message
type: streamable_http
headers:
Authorization: "App YOUR_INFOBIP_API_KEY"
One thing to watch out for is that Goose expects `uri`, not `url`. If either field is wrong, Goose skips the extension silently without error-naming the field. The config is usually the first place to check if the server is not loading.
If you would rather not edit YAML directly, `goose configure` walks you through it interactively. Either way gets you to the same place.
The weather demo as orchestration, not a tool call
Getting the config right means the server loads. What happens after that is where things get interesting. The part of this repo I find most useful is not that Goose can send a message, it is that Goose can work out what has to happen first.
The prompt in the demo looks simple:
`Check the weather in Newcastle and send a text message to 447700900123 from sender IBSelfServe.`
But that one line asks Goose to run three things in sequence: fetch the current weather, format the result, and send it through the Message MCP. The client has to work out the order, choose the right tool, and pass the right arguments. None of that is spelled out.
The recipe version makes that gap explicit by laying out the sequence as steps rather than leaving it to inference:
prompt: |
1. Run this shell command to get the weather forecast for Newcastle:
curl -s "wttr.in/Newcastle?format=3"
2. Send the result as a text message to 447700900123 from sender IBSelfServe.
3. Confirm the message was sent successfully.
(IBSelfServe is a shared Viber sender set up in the Infobip account. Swap it for your own if not in the free trial mode.)
There is no decision tree here. Goose figures out how to stitch together a shell command, a tool call, and a confirmation step on its own. That is the part that is easy to miss when you only look at a server in isolation — the server just sits there exposing tools. You do not see the sequencing, the tool selection, or the back-and-forth until a real client is running against it.
Which also means the prompt is doing more work than it looks like.
The prompt is part of the interface
A vague prompt makes a client look unreliable, even when the server is fine. If you only tell Goose to “check the weather,” it still needs to know the city, the destination number, the sender name, and how much detail you want.
The `prompts/examples.md` file in the repo tries to show what that specificity looks like in practice. The prompts that work well name everything that is not over-engineering, it is part of how the system works.
Tool names matter for the same reason. `send_messages`gives the model something clear to reason about. A vague name pushes more work onto the description, which means more tokens and more room for the model to guess wrong.
That clarity carries through to scheduling too, once you want the workflow to run on its own.
Running the recipe on a schedule. It runs, as long as Goose does
The repo includes Goose’s built-in scheduler, which is handy for running the recipe automatically each morning. One thing worth knowing upfront: it only runs while Goose is running. If Goose is closed at the scheduled time, the job is skipped — no retry, no catch-up. That is just how it works, and it changes how much you can rely on it for anything time-sensitive.
If you need the job to fire regardless of whether Goose is open, the repo documents a system cron fallback. It is a reasonable option if reliability matters more than keeping everything inside one tool.
A few things worth carrying forward
Putting this together surfaced a few observations that might be useful if you are building your own MCP server:
- Tool names that read clearly to a model reduce the chance of the wrong tool being picked
- One-sentence descriptions tend to help more than long ones
- Auth and config requirements are worth spelling out explicitly — clients will not guess
- Testing inside a real client catches things that isolated testing misses
- Fewer tools is often better, especially for narrow use cases
None of this is a rule. It is just what came up while putting this together.
Wrapping up
The weather-alert-agent is a small example, but it touches most of what makes MCP interesting to work with in practice: server design, client configuration, prompt specificity, orchestration, and scheduling. None of those pieces work in isolation. They only make sense together, once a real client is running against a real server.
Goose is a useful reference client because it makes that client side visible in a way a server-only demo does not. Discovery, configuration, prompt design, scheduling, and tool orchestration all show up in one place. Once you can see that layer, MCP starts to feel less like a server feature and more like a full integration surface, one where the client does most of the interesting work.
If you want to try it yourself, the full repo is here: weather-alert-agent.