For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://modelgates.ai/docs/_mcp/server.

Stop Conditions

Basic Usage

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

PropertyTypeDescription
stepsStepResult[]All completed steps including results and usage

StepResult

Each step contains:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
// 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:

typescript
stopWhen: [  stepCountIs(100),    // Hard limit  maxCost(10.00),      // Budget limit  customCondition,     // Your logic],

Log Stop Reasons

Track why execution stopped:

typescript
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:

typescript
// 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