For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://modelgates.ai/docs/_mcp/server.
Stop Conditions
Basic Usage
import { ModelGates, stepCountIs } from '@modelgates/agent'; const modelgates = new ModelGates({ apiKey: process.env.MODELGATES_API_KEY,}); const result = modelgates.callModel({ model: 'openai/gpt-5-nano', input: 'Research this topic thoroughly', tools: [searchTool, analysisTool], stopWhen: stepCountIs(5), // Stop after 5 steps});Built-in Stop Conditions
stepCountIs(n)
Stop after a specific number of steps:
import { stepCountIs } from '@modelgates/agent'; const result = modelgates.callModel({ model: 'openai/gpt-5-nano', input: 'Analyze this data', tools: [analysisTool], stopWhen: stepCountIs(10), // Stop after 10 steps});hasToolCall(name)
Stop when a specific tool is called:
import { hasToolCall } from '@modelgates/agent'; const finishTool = tool({ name: 'finish', description: 'Call this when the task is complete', inputSchema: z.object({ summary: z.string(), }), execute: async (params) => ({ done: true }),}); const result = modelgates.callModel({ model: 'openai/gpt-5-nano', input: 'Research until you have enough information, then call finish', tools: [searchTool, finishTool], stopWhen: hasToolCall('finish'), // Stop when finish tool is called});maxTokensUsed(n)
Stop after using a certain number of tokens:
import { maxTokensUsed } from '@modelgates/agent'; const result = modelgates.callModel({ model: 'openai/gpt-5-nano', input: 'Generate content', tools: [writingTool], stopWhen: maxTokensUsed(5000), // Stop after 5000 total tokens});maxCost(amount)
Stop after reaching a cost threshold:
import { maxCost } from '@modelgates/agent'; const result = modelgates.callModel({ model: 'openai/gpt-5.2', input: 'Perform extensive analysis', tools: [analysisTool], stopWhen: maxCost(1.00), // Stop after $1.00 spent});finishReasonIs(reason)
Stop on a specific finish reason:
import { finishReasonIs } from '@modelgates/agent'; const result = modelgates.callModel({ model: 'openai/gpt-5-nano', input: 'Complete this task', tools: [taskTool], stopWhen: finishReasonIs('stop'), // Stop when model finishes naturally});Combining Conditions
Pass an array to stop on any condition:
import { stepCountIs, hasToolCall, maxCost } from '@modelgates/agent'; const result = modelgates.callModel({ model: 'openai/gpt-5.2', input: 'Research thoroughly but stay within budget', tools: [searchTool, finishTool], stopWhen: [ stepCountIs(10), // Maximum 10 steps maxCost(0.50), // Maximum $0.50 hasToolCall('finish'), // Or when finish is called ],});Execution stops when any condition is met.
Custom Stop Conditions
Create custom conditions with a function:
const result = modelgates.callModel({ model: 'openai/gpt-5-nano', input: 'Process data', tools: [processTool], stopWhen: ({ steps }) => { // Stop after 20 steps if (steps.length >= 20) return true; // Stop if last step had no tool calls const lastStep = steps[steps.length - 1]; if (lastStep && !lastStep.toolCalls?.length) return true; // Continue otherwise return false; },});StopConditionContext
Custom functions receive:
| Property | Type | Description |
|---|---|---|
steps | StepResult[] | All completed steps including results and usage |
StepResult
Each step contains:
interface StepResult { response: Response; toolCalls?: ParsedToolCall[]; toolResults?: ToolExecutionResult[]; tokens: { input: number; output: number; cached: number; }; cost: number;}Advanced Patterns
Time-Based Stopping
Stop after a time limit:
const startTime = Date.now();const maxDuration = 30000; // 30 seconds const result = modelgates.callModel({ model: 'openai/gpt-5-nano', input: 'Work on this task', tools: [workTool], stopWhen: () => { return Date.now() - startTime > maxDuration; },});Content-Based Stopping
Stop based on response content:
const result = modelgates.callModel({ model: 'openai/gpt-5-nano', input: 'Search until you find the answer', tools: [searchTool], stopWhen: ({ steps }) => { const lastStep = steps[steps.length - 1]; if (!lastStep) return false; // Check if response contains certain keywords const content = JSON.stringify(lastStep.response); return content.includes('ANSWER FOUND') || content.includes('TASK COMPLETE'); },});Quality-Based Stopping
Stop when results meet quality threshold:
const result = modelgates.callModel({ model: 'openai/gpt-5-nano', input: 'Improve this text until it scores above 0.9', tools: [improverTool, scorerTool], stopWhen: ({ steps }) => { // Look for score in tool results for (const step of steps) { for (const result of step.toolResults ?? []) { if (result.toolName === 'scorer' && result.result?.score > 0.9) { return true; } } } return false; },});Combination with Early Exit
Combine conditions for complex logic:
import { stepCountIs, maxCost } from '@modelgates/agent'; const result = modelgates.callModel({ model: 'openai/gpt-5.2', input: 'Complex research task', tools: [searchTool, analysisTool, summarizeTool], stopWhen: [ // Hard limits stepCountIs(50), maxCost(5.00), // Custom success condition ({ steps }) => { const lastStep = steps[steps.length - 1]; const hasSummary = lastStep?.toolCalls?.some( tc => tc.name === 'summarize' ); return hasSummary; }, ],});Migration from maxToolRounds
If you were using maxToolRounds, migrate to stopWhen:
// Before: maxToolRoundsconst result = modelgates.callModel({ model: 'openai/gpt-5-nano', input: 'Hello', tools: [myTool], maxToolRounds: 5,}); // After: stopWhenimport { stepCountIs } from '@modelgates/agent'; const result = modelgates.callModel({ model: 'openai/gpt-5-nano', input: 'Hello', tools: [myTool], stopWhen: stepCountIs(5),});Default Behavior
If stopWhen is not specified, the default is stepCountIs(5).
Best Practices
Always Set Limits
Always include a hard limit to prevent runaway execution:
stopWhen: [ stepCountIs(100), // Hard limit maxCost(10.00), // Budget limit customCondition, // Your logic],Log Stop Reasons
Track why execution stopped:
const result = modelgates.callModel({ model: 'openai/gpt-5-nano', input: 'Task', tools: [taskTool], stopWhen: ({ steps }) => { if (steps.length >= 10) { console.log('Stopped: step limit'); return true; } const totalCost = steps.reduce((sum, step) => sum + (step.cost ?? 0), 0); if (totalCost >= 1.00) { console.log('Stopped: cost limit'); return true; } return false; },});Test Conditions
Verify conditions work as expected:
// Test with low limits firstconst testResult = modelgates.callModel({ model: 'openai/gpt-5-nano', input: 'Test task', tools: [testTool], stopWhen: stepCountIs(2), // Low limit for testing});See Also
- Tools - Multi-turn orchestration
- Dynamic Parameters - Adaptive behavior
- nextTurnParams - Tool-driven modifications