Skip to main content
Glama
by macacoai
network.js8.84 kB
/** * Copyright (c) Microsoft Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { z } from 'zod'; import { defineTabTool } from './tool.js'; const requests = defineTabTool({ capability: 'core', schema: { name: 'browser_network_requests', title: 'List network requests', description: 'Returns all network requests since loading the page with optional filtering by HTTP method, status code, content type, and URL pattern', inputSchema: z.object({ method: z.enum(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']).optional() .describe('Filter by HTTP method'), status: z.number().min(100).max(599).optional() .describe('Filter by HTTP status code'), contentType: z.string().optional() .describe('Filter by content type (partial match, e.g., "json", "html", "image")'), urlPattern: z.string().optional() .describe('Filter by URL pattern (partial match, e.g., "api", "/users/", ".js")'), limit: z.number().min(1).max(100).default(50).optional() .describe('Maximum number of requests to return (default: 50)') }), type: 'readOnly', }, handle: async (tab, params, response) => { const requests = tab.requests(); const requestEntries = [...requests.entries()]; // Apply filters let filteredRequests = requestEntries.filter(([req, res]) => { // Filter by HTTP method if (params.method && req.method().toUpperCase() !== params.method.toUpperCase()) { return false; } // Filter by status code if (params.status && (!res || res.status() !== params.status)) { return false; } // Filter by content type if (params.contentType && res) { const contentType = res.headers()['content-type'] || ''; if (!contentType.toLowerCase().includes(params.contentType.toLowerCase())) { return false; } } // Filter by URL pattern if (params.urlPattern) { if (!req.url().toLowerCase().includes(params.urlPattern.toLowerCase())) { return false; } } return true; }); // Apply limit if (params.limit) { filteredRequests = filteredRequests.slice(0, params.limit); } // Render results filteredRequests.forEach(([req, res]) => { response.addResult(renderRequest(req, res)); }); if (filteredRequests.length === 0) { response.addResult('No requests found matching the specified filters.'); } else { response.addResult(`\nShowing ${filteredRequests.length} request(s). Use browser_network_request_details with the full URL to see details.`); } }, }); const requestDetails = defineTabTool({ capability: 'core', schema: { name: 'browser_network_request_details', title: 'Get network request details', description: 'Get detailed information about a specific network request using its URL and optionally HTTP method', inputSchema: z.object({ url: z.string() .describe('Full URL of the request (copy from browser_network_requests output)'), method: z.enum(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']).optional() .describe('HTTP method (optional, helps when same URL has multiple methods)') }), type: 'readOnly', }, handle: async (tab, params, response) => { const requests = tab.requests(); const requestEntries = [...requests.entries()]; // Find matching request(s) const matchingRequests = requestEntries.filter(([req, res]) => { const urlMatches = req.url() === params.url; const methodMatches = !params.method || req.method().toUpperCase() === params.method.toUpperCase(); return urlMatches && methodMatches; }); if (matchingRequests.length === 0) { response.addError(`No request found for URL: ${params.url}${params.method ? ` with method ${params.method}` : ''}`); return; } if (matchingRequests.length > 1 && !params.method) { response.addResult(`Found ${matchingRequests.length} requests for this URL with different methods:`); matchingRequests.forEach(([req, res]) => { response.addResult(`- ${req.method().toUpperCase()} ${req.url()}`); }); response.addResult(`\nPlease specify the 'method' parameter to get details for a specific request.`); return; } // Show details for the request (use first match if method specified or only one match) const [request, responseObj] = matchingRequests[0]; // Request details response.addResult(`## Request Details`); response.addResult(`**URL:** ${request.url()}`); response.addResult(`**Method:** ${request.method().toUpperCase()}`); response.addResult(`**Resource Type:** ${request.resourceType()}`); // Request headers const requestHeaders = request.headers(); response.addResult(`\n**Request Headers:**`); Object.entries(requestHeaders).forEach(([key, value]) => { response.addResult(` ${key}: ${value}`); }); // Request body (if available) const postData = request.postData(); if (postData) { response.addResult(`\n**Request Body:**`); response.addResult(postData); } // Response details if (responseObj) { response.addResult(`\n## Response Details`); response.addResult(`**Status:** ${responseObj.status()} ${responseObj.statusText()}`); // Response headers const responseHeaders = responseObj.headers(); response.addResult(`\n**Response Headers:**`); Object.entries(responseHeaders).forEach(([key, value]) => { response.addResult(` ${key}: ${value}`); }); // Response body (if text-based) try { const contentType = responseHeaders['content-type'] || ''; if (contentType.includes('text/') || contentType.includes('application/json') || contentType.includes('application/xml')) { const body = await responseObj.text(); if (body && body.length > 0) { response.addResult(`\n**Response Body:**`); // Limit body size for readability if (body.length > 1000) { response.addResult(body.substring(0, 1000) + '\n... (truncated)'); } else { response.addResult(body); } } } } catch (error) { response.addResult(`\n**Response Body:** Unable to read response body: ${error.message}`); } } else { response.addResult(`\n## Response Details`); response.addResult('**Status:** No response received (request may have failed or is still pending)'); } }, }); function renderRequest(request, response) { const result = []; result.push(`[${request.method().toUpperCase()}] ${request.url()}`); if (response) { result.push(`=> [${response.status()}] ${response.statusText()}`); // Add content type if available const contentType = response.headers()['content-type']; if (contentType) { const shortContentType = contentType.split(';')[0]; // Remove charset info result.push(`(${shortContentType})`); } } else { result.push('=> [PENDING/FAILED]'); } return result.join(' '); } export default [ requests, requestDetails, ];

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/macacoai/mcp-playwright'

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