Skip to main content
Glama
nekobato

ChatGPT WebSearch MCP

by nekobato

ask_chatgpt

Send a prompt to ChatGPT and receive a response. Choose between regular models with adjustable temperature or reasoning models with configurable effort and verbosity.

Instructions

Ask ChatGPT a question and get a response. Supports both regular models (with temperature) and reasoning models (with effort/verbosity).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
promptYesThe prompt to send to ChatGPT
modelNoThe model to use (default: from OPENAI_DEFAULT_MODEL env var or gpt-5). Unless specified by the user, you should not set this model parameter. Supported models: gpt-5, gpt-5-mini, gpt-5-nano, o3, o3-pro, o4-mini, gpt-4.1, gpt-4.1-minigpt-5
systemNoSystem prompt to set context and behavior for the AI
temperatureNoTemperature for response generation (0-2). Not available for reasoning models (gpt-5, o1, o3, etc.)
effortNoReasoning effort level: minimal, low, medium, high (default: from REASONING_EFFORT env var). For reasoning models only.
verbosityNoOutput verbosity level: low, medium, high (default: from VERBOSITY env var). For reasoning models only.
searchContextSizeNoSearch context size: low, medium, high (default: from SEARCH_CONTEXT_SIZE env var). For reasoning models only.
maxTokensNoMaximum number of output tokens
maxRetriesNoMaximum number of API retry attempts (default: from OPENAI_MAX_RETRIES env var or 3)
timeoutMsNoRequest timeout in milliseconds. Auto-adjusts based on effort level: high=300s, medium=120s, low/minimal=60s. Can be overridden with OPENAI_API_TIMEOUT env var.
useStreamingNoForce streaming mode to prevent timeouts during long reasoning tasks. Defaults to auto (true for medium/high effort reasoning models).

Implementation Reference

  • Main handler for the 'ask_chatgpt' tool call. Extracts parameters, determines timeout/streaming based on effort level, and calls either chatStream() for streaming or chat() for non-streaming via ChatGPTClient.
    server.setRequestHandler(CallToolRequestSchema, async (request) => {
      if (request.params.name !== 'ask_chatgpt') {
        throw new Error(`Unknown tool: ${request.params.name}`);
      }
    
      const {
        prompt,
        model = process.env.OPENAI_DEFAULT_MODEL || 'gpt-5',
        system,
        temperature = 0.7,
        effort = (process.env.REASONING_EFFORT as
          | 'minimal'
          | 'low'
          | 'medium'
          | 'high') || undefined,
        verbosity = (process.env.VERBOSITY as 'low' | 'medium' | 'high') ||
          undefined,
        searchContextSize = (process.env.SEARCH_CONTEXT_SIZE as
          | 'low'
          | 'medium'
          | 'high') || undefined,
        maxTokens,
        maxRetries = parseInt(process.env.OPENAI_MAX_RETRIES || '3'),
        timeoutMs: userTimeoutMs,
        useStreaming,
      } = request.params.arguments as {
        prompt: string;
        model?: string;
        system?: string;
        temperature?: number;
        effort?: 'minimal' | 'low' | 'medium' | 'high';
        verbosity?: 'low' | 'medium' | 'high';
        searchContextSize?: 'low' | 'medium' | 'high';
        maxTokens?: number;
        maxRetries?: number;
        timeoutMs?: number;
        useStreaming?: boolean;
      };
    
      // Dynamic timeout based on effort level for reasoning models
      let timeoutMs = userTimeoutMs;
      if (timeoutMs === undefined) {
        if (effort === 'high') {
          // 5 minutes for high effort reasoning
          timeoutMs = parseInt(process.env.OPENAI_API_TIMEOUT || '300000');
        } else if (effort === 'medium') {
          // 2 minutes for medium effort reasoning
          timeoutMs = parseInt(process.env.OPENAI_API_TIMEOUT || '120000');
        } else {
          // Default 1 minute for low/minimal effort or regular models
          timeoutMs = parseInt(process.env.OPENAI_API_TIMEOUT || '60000');
        }
      }
    
      // Use streaming for reasoning models with medium/high effort to prevent timeouts
      // Can be overridden by useStreaming parameter
      const shouldUseStreaming =
        useStreaming !== undefined
          ? useStreaming
          : effort === 'medium' || effort === 'high';
    
      const chatRequest: ChatRequest = {
        prompt,
        model: model as SupportedModel,
        system,
        temperature,
        effort,
        verbosity,
        searchContextSize,
        maxTokens,
        maxRetries,
        timeoutMs,
        stream: shouldUseStreaming, // Use streaming for long-running reasoning tasks
      };
    
      try {
        if (shouldUseStreaming) {
          // Use streaming to prevent timeout, but accumulate response for MCP
          console.error('Starting streaming request for reasoning model...');
          let accumulatedContent = '';
          let chunkCount = 0;
    
          for await (const chunk of client.chatStream(chatRequest)) {
            if (!chunk.done && chunk.content) {
              accumulatedContent += chunk.content;
              chunkCount++;
    
              // Show progress to prevent timeout perception
              if (chunkCount % 10 === 0) {
                console.error(
                  `Received ${chunkCount} chunks, accumulated ${accumulatedContent.length} characters...`
                );
              }
            }
    
            if (chunk.done) {
              console.error(
                `Streaming completed. Total chunks: ${chunkCount}, content length: ${accumulatedContent.length}`
              );
              break;
            }
          }
    
          return {
            content: [
              {
                type: 'text',
                text: accumulatedContent,
              },
            ],
          };
        } else {
          // Use regular non-streaming mode
          const response = await client.chat(chatRequest);
    
          return {
            content: [
              {
                type: 'text',
                text: response.content,
              },
            ],
          };
        }
      } catch (error: any) {
        console.error('OpenAI API error:', error);
    
        // Provide helpful timeout message for reasoning models
        if (error.message?.includes('timeout') && effort === 'high') {
          throw new Error(
            `Request timed out after ${timeoutMs}ms. Reasoning models with high effort can take several minutes. ` +
              `Consider increasing OPENAI_API_TIMEOUT environment variable or setting a higher timeoutMs value. ` +
              `Current timeout: ${timeoutMs}ms`
          );
        }
    
        throw new Error(`Failed to call ChatGPT: ${error.message}`);
      }
    });
  • Tool registration with inputSchema for 'ask_chatgpt'. Defines all parameters: prompt (required), model, system, temperature, effort, verbosity, searchContextSize, maxTokens, maxRetries, timeoutMs, useStreaming.
    tools: [
      {
        name: 'ask_chatgpt',
        description:
          'Ask ChatGPT a question and get a response. Supports both regular models (with temperature) and reasoning models (with effort/verbosity).',
        inputSchema: {
          type: 'object',
          properties: {
            prompt: {
              type: 'string',
              description: 'The prompt to send to ChatGPT',
            },
            model: {
              type: 'string',
              // 注意書きが無いとClaude Codeは古いモデルを使おうとする。アホ。
              description: `The model to use (default: from OPENAI_DEFAULT_MODEL env var or gpt-5). Unless specified by the user, you should not set this model parameter. Supported models: ${ALL_SUPPORTED_MODELS.join(', ')}`,
              enum: [...ALL_SUPPORTED_MODELS],
              default: process.env.OPENAI_DEFAULT_MODEL || 'gpt-5',
            },
            system: {
              type: 'string',
              description:
                'System prompt to set context and behavior for the AI',
            },
            temperature: {
              type: 'number',
              description:
                'Temperature for response generation (0-2). Not available for reasoning models (gpt-5, o1, o3, etc.)',
              default: 0.7,
            },
            effort: {
              type: 'string',
              description:
                'Reasoning effort level: minimal, low, medium, high (default: from REASONING_EFFORT env var). For reasoning models only.',
              enum: ['minimal', 'low', 'medium', 'high'],
              default: process.env.REASONING_EFFORT || undefined,
            },
            verbosity: {
              type: 'string',
              description:
                'Output verbosity level: low, medium, high (default: from VERBOSITY env var). For reasoning models only.',
              enum: ['low', 'medium', 'high'],
              default: process.env.VERBOSITY || undefined,
            },
            searchContextSize: {
              type: 'string',
              description:
                'Search context size: low, medium, high (default: from SEARCH_CONTEXT_SIZE env var). For reasoning models only.',
              enum: ['low', 'medium', 'high'],
              default: process.env.SEARCH_CONTEXT_SIZE || undefined,
            },
            maxTokens: {
              type: 'number',
              description: 'Maximum number of output tokens',
            },
            maxRetries: {
              type: 'number',
              description:
                'Maximum number of API retry attempts (default: from OPENAI_MAX_RETRIES env var or 3)',
              default: parseInt(process.env.OPENAI_MAX_RETRIES || '3'),
            },
            timeoutMs: {
              type: 'number',
              description:
                'Request timeout in milliseconds. Auto-adjusts based on effort level: high=300s, medium=120s, low/minimal=60s. Can be overridden with OPENAI_API_TIMEOUT env var.',
            },
            useStreaming: {
              type: 'boolean',
              description:
                'Force streaming mode to prevent timeouts during long reasoning tasks. Defaults to auto (true for medium/high effort reasoning models).',
            },
          },
          required: ['prompt'],
        },
      },
  • src/index.ts:26-105 (registration)
    Registers the 'ask_chatgpt' tool via ListToolsRequestSchema handler on the MCP server, returning the tool definition with name and inputSchema.
    server.setRequestHandler(ListToolsRequestSchema, async () => {
      return {
        tools: [
          {
            name: 'ask_chatgpt',
            description:
              'Ask ChatGPT a question and get a response. Supports both regular models (with temperature) and reasoning models (with effort/verbosity).',
            inputSchema: {
              type: 'object',
              properties: {
                prompt: {
                  type: 'string',
                  description: 'The prompt to send to ChatGPT',
                },
                model: {
                  type: 'string',
                  // 注意書きが無いとClaude Codeは古いモデルを使おうとする。アホ。
                  description: `The model to use (default: from OPENAI_DEFAULT_MODEL env var or gpt-5). Unless specified by the user, you should not set this model parameter. Supported models: ${ALL_SUPPORTED_MODELS.join(', ')}`,
                  enum: [...ALL_SUPPORTED_MODELS],
                  default: process.env.OPENAI_DEFAULT_MODEL || 'gpt-5',
                },
                system: {
                  type: 'string',
                  description:
                    'System prompt to set context and behavior for the AI',
                },
                temperature: {
                  type: 'number',
                  description:
                    'Temperature for response generation (0-2). Not available for reasoning models (gpt-5, o1, o3, etc.)',
                  default: 0.7,
                },
                effort: {
                  type: 'string',
                  description:
                    'Reasoning effort level: minimal, low, medium, high (default: from REASONING_EFFORT env var). For reasoning models only.',
                  enum: ['minimal', 'low', 'medium', 'high'],
                  default: process.env.REASONING_EFFORT || undefined,
                },
                verbosity: {
                  type: 'string',
                  description:
                    'Output verbosity level: low, medium, high (default: from VERBOSITY env var). For reasoning models only.',
                  enum: ['low', 'medium', 'high'],
                  default: process.env.VERBOSITY || undefined,
                },
                searchContextSize: {
                  type: 'string',
                  description:
                    'Search context size: low, medium, high (default: from SEARCH_CONTEXT_SIZE env var). For reasoning models only.',
                  enum: ['low', 'medium', 'high'],
                  default: process.env.SEARCH_CONTEXT_SIZE || undefined,
                },
                maxTokens: {
                  type: 'number',
                  description: 'Maximum number of output tokens',
                },
                maxRetries: {
                  type: 'number',
                  description:
                    'Maximum number of API retry attempts (default: from OPENAI_MAX_RETRIES env var or 3)',
                  default: parseInt(process.env.OPENAI_MAX_RETRIES || '3'),
                },
                timeoutMs: {
                  type: 'number',
                  description:
                    'Request timeout in milliseconds. Auto-adjusts based on effort level: high=300s, medium=120s, low/minimal=60s. Can be overridden with OPENAI_API_TIMEOUT env var.',
                },
                useStreaming: {
                  type: 'boolean',
                  description:
                    'Force streaming mode to prevent timeouts during long reasoning tasks. Defaults to auto (true for medium/high effort reasoning models).',
                },
              },
              required: ['prompt'],
            },
          },
        ],
      };
    });
  • ChatGPTClient class providing chat() and chatStream() methods that call the OpenAI API. chatStream() yields StreamChunks for progressive output; chat() returns a ChatResponse. Both use callResponsesAPI/streamResponsesAPI private methods which include web_search tool integration.
    export class ChatGPTClient {
      private openai: OpenAI;
    
      constructor(apiKey?: string) {
        this.openai = new OpenAI({
          apiKey: apiKey || process.env.OPENAI_API_KEY,
        });
      }
    
      private createOpenAIClient(request: ChatRequest): OpenAI {
        const options: ClientOptions = {
          apiKey: this.openai.apiKey,
        };
    
        if (request.maxRetries !== undefined) {
          options.maxRetries = request.maxRetries;
        }
    
        if (request.timeoutMs !== undefined) {
          options.timeout = request.timeoutMs;
        }
    
        return new OpenAI(options);
      }
    
      private validateRequest(request: ChatRequest): void {
        if (!this.openai.apiKey) {
          throw new Error('OPENAI_API_KEY environment variable is not set');
        }
    
        if (!isSupportedModel(request.model)) {
          throw new Error(
            `Unsupported model "${request.model}". Supported models are: ${ALL_SUPPORTED_MODELS.join(', ')}`
          );
        }
      }
    
      async chat(request: ChatRequest): Promise<ChatResponse> {
        this.validateRequest(request);
    
        const client = this.createOpenAIClient(request);
    
        try {
          return await this.callResponsesAPI(request, client, false);
        } catch (error: any) {
          throw new Error(`Failed to call ChatGPT: ${error.message}`);
        }
      }
    
      async *chatStream(request: ChatRequest): AsyncGenerator<StreamChunk> {
        this.validateRequest(request);
    
        const client = this.createOpenAIClient(request);
    
        try {
          yield* this.streamResponsesAPI(request, client);
        } catch (error: any) {
          throw new Error(`Failed to call ChatGPT: ${error.message}`);
        }
      }
    
      private async callResponsesAPI(
        request: ChatRequest,
        client: OpenAI,
        stream: boolean
      ): Promise<ChatResponse> {
        const requestBody: any = {
          model: request.model,
          input: request.prompt,
          stream,
          tools: [
            {
              type: 'web_search',
              search_context_size:
                request.searchContextSize ||
                process.env.SEARCH_CONTEXT_SIZE ||
                'medium',
            },
          ],
          tool_choice: 'auto',
          parallel_tool_calls: true,
        };
    
        if (request.effort) {
          requestBody.reasoning = { effort: request.effort };
        }
        if (request.verbosity) {
          requestBody.text = { verbosity: request.verbosity };
        }
        if (request.maxTokens) {
          requestBody.max_output_tokens = request.maxTokens;
        }
    
        const completion = await client.responses.create(requestBody);
        const responseContent = (completion as any).output_text || '';
    
        return {
          content: responseContent,
          model: request.model,
          usage: (completion as any).usage,
        };
      }
    
      private async *streamResponsesAPI(
        request: ChatRequest,
        client: OpenAI
      ): AsyncGenerator<StreamChunk> {
        const requestBody: any = {
          model: request.model,
          input: request.prompt,
          stream: true,
        };
    
        if (request.effort) {
          requestBody.reasoning = { effort: request.effort };
        }
        if (request.verbosity) {
          requestBody.text = { verbosity: request.verbosity };
        }
        if (request.maxTokens) {
          requestBody.max_output_tokens = request.maxTokens;
        }
    
        const completion = await client.responses.create(requestBody);
    
        for await (const event of completion as any) {
          if (event.type === 'response.output_text.delta') {
            yield {
              content: event.delta,
              done: false,
            };
          }
        }
    
        yield {
          content: '',
          done: true,
        };
      }
    }
  • Model constants and type definitions. Defines REASONING_MODELS, REGULAR_MODELS, ALL_SUPPORTED_MODELS, and helper functions isSupportedModel/isReasoningModel. Also exports ChatParams and ReasoningParams interfaces.
    export const REASONING_MODELS = [
      'gpt-5',
      'gpt-5-mini',
      'gpt-5-nano',
      'o3',
      'o3-pro',
      'o4-mini',
    ] as const;
    
    export const REGULAR_MODELS = ['gpt-4.1', 'gpt-4.1-mini'] as const;
    
    export const ALL_SUPPORTED_MODELS = [
      ...REASONING_MODELS,
      ...REGULAR_MODELS,
    ] as const;
    
    export type SupportedModel = (typeof ALL_SUPPORTED_MODELS)[number];
    
    export function isReasoningModel(model: string): boolean {
      return REASONING_MODELS.includes(model as any);
    }
    
    export function isSupportedModel(model: string): model is SupportedModel {
      return ALL_SUPPORTED_MODELS.includes(model as any);
    }
    
    export interface ReasoningParams {
      effort?: 'minimal' | 'low' | 'medium' | 'high';
      verbosity?: 'low' | 'medium' | 'high';
    }
    
    export interface ChatParams {
      model: string;
      prompt: string;
      temperature?: number;
      stream?: boolean;
      maxTokens?: number;
      reasoning?: ReasoningParams;
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Without annotations, the description carries full burden. It mentions the core behavior (ask question, get response) and model types, but does not disclose potential side effects (e.g., cost, latency) or authentication requirements. The schema provides additional details like streaming and timeouts, but the description itself is minimal.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences that front-load the core purpose and then provide a succinct differentiation of model types. No unnecessary words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers the essential purpose and model distinction, but given 11 parameters and no output schema, it could mention the return format (text response) and streaming behavior. It is mostly complete, leaving some details to the schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 100% schema coverage, the baseline is 3. The description adds value by grouping parameters: regular models use temperature, reasoning models use effort/verbosity. This contextual guidance helps the agent select appropriate parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool asks ChatGPT a question and gets a response. It distinguishes between regular and reasoning models, which aligns with the model parameter options.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides context on when to use regular models (with temperature) vs reasoning models (with effort/verbosity). However, it lacks explicit guidance on when not to use this tool, though no siblings exist to compare.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/nekobato/chatgpt-websearch-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server