Skip to main content
Glama

search_news

Search news articles across multiple sources with automatic API failover to ensure reliable results when querying topics in various languages.

Instructions

Search for news articles using multiple news APIs with automatic failover. The system intelligently switches between different news APIs when one reaches its limit or fails.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesSearch query for news articles (keywords, topics, etc.)
languageNoLanguage code for news articles (e.g., 'en', 'zh', 'es')en
limitNoMaximum number of articles to return (1-10)

Implementation Reference

  • The 'run' method of the newsSearch tool, containing the core logic for searching news articles across multiple APIs with failover, rate limiting, and formatted output.
    async run(args: { query: string; language?: string; limit?: number }) {
      try {
        // Validate input
        if (!args.query || args.query.trim().length === 0) {
          throw new Error("Search query cannot be empty");
        }
        
        const query = args.query.trim();
        const language = args.language || 'en';
        const limit = Math.min(Math.max(args.limit || 5, 1), 10);
        
        // Get available APIs
        const activeAPIs = getActiveAPIs();
        
        if (activeAPIs.length === 0) {
          return {
            content: [{
              type: "text",
              text: "❌ **No News APIs Configured**\n\nPlease configure at least one news API key in your .env file. Available options:\n\n" +
                    "- **NewsAPI.org**: Get free key at https://newsapi.org/register\n" +
                    "- **GNews API**: Get free key at https://gnews.io/\n" +
                    "- **TheNewsAPI**: Get free key at https://www.thenewsapi.com/\n" +
                    "- **NewsData.io**: Get free key at https://newsdata.io/\n" +
                    "- **Twingly**: Get trial key at https://www.twingly.com/news-api/\n\n" +
                    "Add your API keys to the .env file and restart the service."
            }],
            isError: true
          };
        }
        
        let lastError = '';
        let attemptedAPIs: string[] = [];
        
        // Try each API in priority order
        for (const apiConfig of activeAPIs) {
          // Skip APIs that have reached their daily limit
          if (!hasRemainingQuota(apiConfig)) {
            attemptedAPIs.push(`${apiConfig.name} (quota exceeded)`);
            continue;
          }
          
          attemptedAPIs.push(apiConfig.name);
          
          const result = await searchWithAPI(apiConfig, query, language, limit);
          
          if (result.success && result.data) {
            const formattedResponse = formatNewsResponse(result.data, result.apiUsed!);
            const quotaInfo = `\n\n---\n\n**API Used:** ${result.apiUsed}\n**Remaining Quota:** ${result.remainingQuota} requests\n**Attempted APIs:** ${attemptedAPIs.join(', ')}`;
            
            return {
              content: [{
                type: "text",
                text: formattedResponse + quotaInfo
              }]
            };
          } else {
            lastError = result.error || 'Unknown error';
            console.warn(`API ${apiConfig.name} failed: ${lastError}`);
          }
        }
        
        // All APIs failed
        return {
          content: [{
            type: "text",
            text: `❌ **All News APIs Failed**\n\n` +
                  `**Query:** "${query}"\n` +
                  `**Attempted APIs:** ${attemptedAPIs.join(', ')}\n` +
                  `**Last Error:** ${lastError}\n\n` +
                  `**Suggestions:**\n` +
                  `- Check your API keys in the .env file\n` +
                  `- Verify your internet connection\n` +
                  `- Try a different search query\n` +
                  `- Some APIs may have temporary outages`
          }],
          isError: true
        };
        
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Unknown error';
        return {
          content: [{
            type: "text",
            text: `❌ **Search Failed:** ${errorMessage}`
          }],
          isError: true
        };
      }
    }
  • JSON Schema defining the input parameters for the 'search_news' tool, including query (required), language, and limit.
    parameters: {
      type: "object",
      properties: {
        query: {
          type: "string",
          description: "Search query for news articles (keywords, topics, etc.)"
        },
        language: {
          type: "string",
          description: "Language code for news articles (e.g., 'en', 'zh', 'es')",
          default: "en"
        },
        limit: {
          type: "number",
          description: "Maximum number of articles to return (1-10)",
          minimum: 1,
          maximum: 10,
          default: 5
        }
      },
      required: ["query"]
    },
  • src/index.ts:19-29 (registration)
    MCP server registration for listing tools, exposing the 'search_news' tool with its name, description, and input schema.
    server.setRequestHandler(ListToolsRequestSchema, async () => {
      return {
        tools: [
          {
            name: newsSearch.name,
            description: newsSearch.description,
            inputSchema: newsSearch.parameters
          }
        ]
      };
    });
  • src/index.ts:32-39 (registration)
    MCP server handler for tool calls, dispatching 'search_news' requests to the newsSearch.run implementation.
    server.setRequestHandler(CallToolRequestSchema, async (request) => {
      switch (request.params.name) {
        case "search_news":
          return await newsSearch.run(request.params.arguments as { query: string; language?: string; limit?: number });
        default:
          throw new Error(`Unknown tool: ${request.params.name}`);
      }
    });
  • Helper function to perform API-specific news searches, handling different endpoints and error conditions for multiple news providers.
    async function searchWithAPI(apiConfig: NewsAPIConfig, query: string, language: string = 'en', pageSize: number = 10): Promise<NewsAPIResponse> {
      try {
        let url = '';
        let headers: any = {};
        
        // Build API-specific request
        switch (apiConfig.name) {
          case 'NewsAPI.org':
            url = `${apiConfig.baseUrl}?q=${encodeURIComponent(query)}&language=${language}&pageSize=${pageSize}&apiKey=${apiConfig.apiKey}`;
            break;
            
          case 'GNews':
            url = `${apiConfig.baseUrl}?q=${encodeURIComponent(query)}&lang=${language}&max=${pageSize}&apikey=${apiConfig.apiKey}`;
            break;
            
          case 'TheNewsAPI':
            url = `${apiConfig.baseUrl}/all?api_token=${apiConfig.apiKey}&search=${encodeURIComponent(query)}&language=${language}&limit=${pageSize}`;
            break;
            
          case 'NewsData.io':
            url = `${apiConfig.baseUrl}?apikey=${apiConfig.apiKey}&q=${encodeURIComponent(query)}&language=${language}&size=${pageSize}`;
            break;
            
          case 'Twingly':
            url = `${apiConfig.baseUrl}?q=${encodeURIComponent(query)}&format=json&apikey=${apiConfig.apiKey}`;
            break;
            
          default:
            throw new Error(`Unsupported API: ${apiConfig.name}`);
        }
        
        const response = await fetch(url, { headers });
        
        if (!response.ok) {
          const errorText = await response.text();
          throw new Error(`HTTP ${response.status}: ${errorText}`);
        }
        
        const data = await response.json();
        
        // Check for API-specific error responses
        if ((data as any).status === 'error' || (data as any).error) {
          throw new Error((data as any).message || (data as any).error || 'API returned an error');
        }
        
        incrementUsage(apiConfig.name);
        
        return {
          success: true,
          data,
          apiUsed: apiConfig.name,
          remainingQuota: getRemainingQuota(apiConfig)
        };
        
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Unknown error';
        return {
          success: false,
          error: errorMessage,
          apiUsed: apiConfig.name
        };
      }
    }
Behavior3/5

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

No annotations are provided, so the description carries the full burden. It discloses key behavioral traits: the tool uses multiple APIs and includes automatic failover when limits are reached or failures occur. However, it lacks details on rate limits, authentication needs, error handling, or response format. This provides some context but is incomplete for a tool with no annotations.

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

Conciseness4/5

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

The description is concise and front-loaded, consisting of two sentences that directly explain the tool's function and key feature (failover). Every sentence adds value, but it could be slightly more structured by separating purpose from behavioral details for clarity.

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

Completeness3/5

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

Given the tool's moderate complexity (search with failover), no annotations, and no output schema, the description is partially complete. It covers the core functionality and failover behavior but misses details like response format, error cases, or API specifics. This leaves gaps for an agent to use the tool effectively without additional context.

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

Parameters3/5

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

The input schema has 100% description coverage, documenting all three parameters (query, language, limit) with details like defaults and constraints. The description adds no parameter-specific information beyond what the schema provides, such as query examples or language code specifics. With high schema coverage, the baseline is 3.

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

Purpose4/5

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

The description clearly states the tool's purpose: 'Search for news articles using multiple news APIs with automatic failover.' It specifies the verb ('Search') and resource ('news articles'), and mentions the mechanism ('multiple news APIs with automatic failover'). However, since there are no sibling tools, it cannot differentiate from alternatives, preventing a score of 5.

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

Usage Guidelines2/5

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

The description provides no guidance on when to use this tool versus alternatives. It mentions the failover feature but does not specify scenarios where this is beneficial (e.g., high-volume searches, reliability needs) or any prerequisites. Without sibling tools, explicit alternatives aren't needed, but general usage context is lacking.

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/guangxiangdebizi/news-mcp'

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