@executeautomation/playwright-mcp-server
Version:
Model Context Protocol servers for Playwright
253 lines (252 loc) • 9.18 kB
JavaScript
import { ApiToolBase } from './base.js';
import { createSuccessResponse, createErrorResponse } from '../common/types.js';
/**
* Helper function to safely parse JSON string or return the value as-is
* @param value The value to parse (can be string or object)
* @returns Parsed JSON object or the original value
*/
function parseJsonSafely(value) {
if (typeof value === 'string') {
try {
return JSON.parse(value);
}
catch (error) {
// Log warning for debugging
console.warn('Failed to parse JSON, using raw string:', error instanceof Error ? error.message : 'Unknown error');
return value;
}
}
return value;
}
/**
* Helper function to build request headers with optional token and custom headers
* @param token Optional Bearer token for authorization
* @param customHeaders Optional custom headers to include
* @param includeContentType Whether to include Content-Type: application/json header
* @returns Merged headers object
*/
function buildHeaders(token, customHeaders, includeContentType = false) {
const headers = {};
if (includeContentType) {
headers['Content-Type'] = 'application/json';
}
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
if (customHeaders) {
// Warn if both token and Authorization header are provided
if (token && customHeaders['Authorization']) {
console.warn('Both token and Authorization header provided. Custom Authorization header will override token.');
}
Object.assign(headers, customHeaders);
}
return headers;
}
/**
* Validate headers are all strings
* @param headers Headers to validate
* @returns Error message if invalid, null if valid
*/
function validateHeaders(headers) {
if (!headers)
return null;
for (const [key, value] of Object.entries(headers)) {
if (typeof value !== 'string') {
return `Header '${key}' must be a string, got ${typeof value}`;
}
}
return null;
}
/**
* Tool for making GET requests
*/
export class GetRequestTool extends ApiToolBase {
/**
* Execute the GET request tool
*/
async execute(args, context) {
return this.safeExecute(context, async (apiContext) => {
// Validate headers
const headerError = validateHeaders(args.headers);
if (headerError) {
return createErrorResponse(headerError);
}
const response = await apiContext.get(args.url, {
headers: buildHeaders(args.token, args.headers)
});
let responseText;
try {
responseText = await response.text();
}
catch (error) {
responseText = "Unable to get response text";
}
return createSuccessResponse([
`GET request to ${args.url}`,
`Status: ${response.status()} ${response.statusText()}`,
`Response: ${responseText.substring(0, 1000)}${responseText.length > 1000 ? '...' : ''}`
]);
});
}
}
/**
* Tool for making POST requests
*/
export class PostRequestTool extends ApiToolBase {
/**
* Execute the POST request tool
*/
async execute(args, context) {
return this.safeExecute(context, async (apiContext) => {
// Validate headers
const headerError = validateHeaders(args.headers);
if (headerError) {
return createErrorResponse(headerError);
}
// Check if the value is valid JSON if it starts with { or [
if (args.value && typeof args.value === 'string' &&
(args.value.startsWith('{') || args.value.startsWith('['))) {
try {
JSON.parse(args.value);
}
catch (error) {
return createErrorResponse(`Failed to parse request body: ${error.message}`);
}
}
const response = await apiContext.post(args.url, {
data: parseJsonSafely(args.value),
headers: buildHeaders(args.token, args.headers, true)
});
let responseText;
try {
responseText = await response.text();
}
catch (error) {
responseText = "Unable to get response text";
}
return createSuccessResponse([
`POST request to ${args.url}`,
`Status: ${response.status()} ${response.statusText()}`,
`Response: ${responseText.substring(0, 1000)}${responseText.length > 1000 ? '...' : ''}`
]);
});
}
}
/**
* Tool for making PUT requests
*/
export class PutRequestTool extends ApiToolBase {
/**
* Execute the PUT request tool
*/
async execute(args, context) {
return this.safeExecute(context, async (apiContext) => {
// Validate headers
const headerError = validateHeaders(args.headers);
if (headerError) {
return createErrorResponse(headerError);
}
// Check if the value is valid JSON if it starts with { or [
if (args.value && typeof args.value === 'string' &&
(args.value.startsWith('{') || args.value.startsWith('['))) {
try {
JSON.parse(args.value);
}
catch (error) {
return createErrorResponse(`Failed to parse request body: ${error.message}`);
}
}
const response = await apiContext.put(args.url, {
data: parseJsonSafely(args.value),
headers: buildHeaders(args.token, args.headers, true)
});
let responseText;
try {
responseText = await response.text();
}
catch (error) {
responseText = "Unable to get response text";
}
return createSuccessResponse([
`PUT request to ${args.url}`,
`Status: ${response.status()} ${response.statusText()}`,
`Response: ${responseText.substring(0, 1000)}${responseText.length > 1000 ? '...' : ''}`
]);
});
}
}
/**
* Tool for making PATCH requests
*/
export class PatchRequestTool extends ApiToolBase {
/**
* Execute the PATCH request tool
*/
async execute(args, context) {
return this.safeExecute(context, async (apiContext) => {
// Validate headers
const headerError = validateHeaders(args.headers);
if (headerError) {
return createErrorResponse(headerError);
}
// Check if the value is valid JSON if it starts with { or [
if (args.value && typeof args.value === 'string' &&
(args.value.startsWith('{') || args.value.startsWith('['))) {
try {
JSON.parse(args.value);
}
catch (error) {
return createErrorResponse(`Failed to parse request body: ${error.message}`);
}
}
const response = await apiContext.patch(args.url, {
data: parseJsonSafely(args.value),
headers: buildHeaders(args.token, args.headers, true)
});
let responseText;
try {
responseText = await response.text();
}
catch (error) {
responseText = "Unable to get response text";
}
return createSuccessResponse([
`PATCH request to ${args.url}`,
`Status: ${response.status()} ${response.statusText()}`,
`Response: ${responseText.substring(0, 1000)}${responseText.length > 1000 ? '...' : ''}`
]);
});
}
}
/**
* Tool for making DELETE requests
*/
export class DeleteRequestTool extends ApiToolBase {
/**
* Execute the DELETE request tool
*/
async execute(args, context) {
return this.safeExecute(context, async (apiContext) => {
// Validate headers
const headerError = validateHeaders(args.headers);
if (headerError) {
return createErrorResponse(headerError);
}
const response = await apiContext.delete(args.url, {
headers: buildHeaders(args.token, args.headers)
});
let responseText;
try {
responseText = await response.text();
}
catch (error) {
responseText = "Unable to get response text";
}
return createSuccessResponse([
`DELETE request to ${args.url}`,
`Status: ${response.status()} ${response.statusText()}`,
`Response: ${responseText.substring(0, 1000)}${responseText.length > 1000 ? '...' : ''}`
]);
});
}
}