Lesson 4: WeatherStep + Local MCP
Goal
Capture two city names, validate them, call a local MCP weather tool, persist the temperature into state, and branch once both cities are known.
Prompt and Tool Surface
public getPrompt(): string {
return `
${DemoPrompt.TravelRole}
Ask user to enter 2 city names to compare their current day temperature.
Capture the names of the two cities and call tool 'get_weather' for each city.
If user prefer to exit, call tool 'end_chat'.
`;
}
public defineTool(): ToolType[] {
return [{
name: 'get_weather',
description: 'capture the weather of one city',
schema: z.object({ cityName: z.string() }),
}];
}
public getTool(): string[] {
return ['get_weather', 'end_chat'];
}
Tip
Restrict tools per step to minimize hallucinated actions and keep the LLM on the rails.
Local MCP Integration
The demo does not hard-code a weather result. Instead, WeatherStep calls callCityTemperatureMcpTool(...), which reaches a local MCP-backed weather service. That makes the step feel like a real tool-calling agent rather than a toy prompt-only flow.
const [weather] = await callCityTemperatureMcpTool([cityName]);
if (weather?.temperature !== null && weather?.temperature !== undefined) {
const stateCityName = this.normalizeCityName(cityName);
this.saveState({ [`city_${stateCityName}`]: weather.temperature });
const LA = this.getState('city_LA');
const NYC = this.getState('city_NYC');
return LA && NYC ? FooLogicStep : WeatherStep;
}
Handler Logic
protected async get_weather(tool: ToolCall): Promise<ToolResponseType> {
const cityName = tool.args?.cityName;
if (typeof cityName !== 'string') {
return { step: WeatherStep, tool: 'Only LA and NYC cities are allowed' };
}
const [weather] = await callCityTemperatureMcpTool([cityName]);
if (weather?.temperature !== null && weather?.temperature !== undefined) {
const stateCityName = this.normalizeCityName(cityName);
this.saveState({ [`city_${stateCityName}`]: weather.temperature });
const LA = this.getState('city_LA');
const NYC = this.getState('city_NYC');
return LA && NYC ? FooLogicStep : WeatherStep;
}
return { step: WeatherStep, tool: 'Only LA and NYC cities are allowed' };
}
- Valid city → store the temperature in step state.
- Both temperatures present → transition to
FooLogicStep. - Invalid city → retry in place, surfacing the error as a tool result.
Patterns Illustrated
- Local MCP tool use: the step reaches out to a local service instead of relying on an embedded stub.
- Multi-call capture: stay in the same step until both city temperatures are collected.
- Retry-in-place:
{ step: SameStep, tool: 'error' }keeps the LLM focused. - Stateful guardrails: step state drives deterministic branching.
Next we’ll capture the user’s name, inject runtime context, and run an InContext sub-agent.