Repository: executeautomation/mcp-playwright
Pull request #64: add: Optional Bearer Authorization to API POST
Author: login: CopilotMe
Created: 2025-03-19T02:07:38Z | Merged: 2025-03-21T03:54:41Z



Diff:
diff --git a/docs/docs/playwright-api/Examples.md b/docs/docs/playwright-api/Examples.md
index 2203bd4a..4e97fc24 100644
--- a/docs/docs/playwright-api/Examples.md
+++ b/docs/docs/playwright-api/Examples.md
@@ -3,22 +3,43 @@ sidebar_position: 2
 ---
 
 # ⚙️Examples of API automation
+
 Lets see how we can use the power of Playwright MCP Server to automate API of our application
 
 ### Scenario
+
 ```json
-Perform POST operation for the URL https://api.restful-api.dev/objects with body 
+// Basic POST request
+Perform POST operation for the URL https://api.restful-api.dev/objects with body
 {
    "name": "Apple MacBook Pro 16",
    "data": {
       "year": 2024,
       "price": 2499,
-      "CPU model": “M4”,
-      "Hard disk size": “5 TB"
+      "CPU model": "M4",
+      "Hard disk size": "5 TB"
    }
 }
 And verify if the response has createdAt and id property and store the ID in a variable for future reference say variable productID
 
+// POST request with Bearer token authorization
+Perform POST operation for the URL https://api.restful-api.dev/objects with Bearer token "your-token-here" set in the headers
+{
+        'Content-Type': 'application/json',
+        'Authorization': 'Bearer your-token-here'
+      },
+and body
+{
+   "name": "Secure MacBook Pro",
+   "data": {
+      "year": 2024,
+      "price": 2999,
+      "CPU model": "M4 Pro",
+      "Hard disk size": "8 TB",
+      "security": "enhanced"
+   }
+}
+
 Perform GET operation for the created ProductID using URL https://api.restful-api.dev/objects/productID and verify the response has properties like Id, name, data
 
 Perform PUT operation for the created ProductID using URL https://api.restful-api.dev/objects/productID with body {
@@ -26,17 +47,17 @@ Perform PUT operation for the created ProductID using URL https://api.restful-ap
    "data": {
       "year": 2025,
       "price": 4099,
-      "CPU model": “M5”,
+      "CPU model": "M5",
       "Hard disk size": "10 TB",
-      "color": “Titanium”
+      "color": "Titanium"
    }
 }
 
 And verify if the response has createdAt and id property
 
-Perform PATCH operation for the created ProductID using URL https://api.restful-api.dev/objects/productID with body 
+Perform PATCH operation for the created ProductID using URL https://api.restful-api.dev/objects/productID with body
 {
-   "name": "Apple MacBook Pro 19 (Limited Edition)”
+   "name": "Apple MacBook Pro 19 (Limited Edition)"
 }
 And verify if the response has updatedAt property with value Apple MacBook Pro 19 (Limited Edition)
 
@@ -50,4 +71,4 @@ And once the entire test operation completes, we will be presented with the enti
 You can also see the `Request/Response/StatusCode` from the execution of Playwright MCP Server
 
 ![Playwright MCP Server](./img/api-response.png)
-:::
\ No newline at end of file
+:::
diff --git a/docs/docs/playwright-api/Supported-Tools.mdx b/docs/docs/playwright-api/Supported-Tools.mdx
index cf4f9724..59b1570d 100644
--- a/docs/docs/playwright-api/Supported-Tools.mdx
+++ b/docs/docs/playwright-api/Supported-Tools.mdx
@@ -44,6 +44,10 @@ Perform a POST operation on any given API request.
     URL to perform the POST operation.  
   - **`value`** *(string)*:  
     Data to include in the body of the POST request.
+  - **`token`** *(string, optional)*:  
+    Bearer token for authorization. When provided, it will be sent as `Authorization: Bearer <token>` header.
+  - **`headers`** *(object, optional)*:  
+    Additional headers to include in the request. Note: Content-Type: application/json is set by default.
 
 - **Response:**
   - **`statusCode`** *(string)*:  
diff --git a/src/__tests__/__snapshots__/tools.test.ts.snap b/src/__tests__/__snapshots__/tools.test.ts.snap
deleted file mode 100644
index bcd8de2d..00000000
--- a/src/__tests__/__snapshots__/tools.test.ts.snap
+++ /dev/null
@@ -1,387 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Tool Definitions should match snapshot 1`] = `
-[
-  {
-    "description": "Navigate to a URL",
-    "inputSchema": {
-      "properties": {
-        "height": {
-          "description": "Viewport height in pixels (default: 720)",
-          "type": "number",
-        },
-        "timeout": {
-          "description": "Navigation timeout in milliseconds",
-          "type": "number",
-        },
-        "url": {
-          "type": "string",
-        },
-        "waitUntil": {
-          "description": "Navigation wait condition",
-          "type": "string",
-        },
-        "width": {
-          "description": "Viewport width in pixels (default: 1280)",
-          "type": "number",
-        },
-      },
-      "required": [
-        "url",
-      ],
-      "type": "object",
-    },
-    "name": "playwright_navigate",
-  },
-  {
-    "description": "Take a screenshot of the current page or a specific element",
-    "inputSchema": {
-      "properties": {
-        "downloadsDir": {
-          "description": "Custom downloads directory path (default: user's Downloads folder)",
-          "type": "string",
-        },
-        "fullPage": {
-          "description": "Store screenshot of the entire page (default: false)",
-          "type": "boolean",
-        },
-        "height": {
-          "description": "Height in pixels (default: 600)",
-          "type": "number",
-        },
-        "name": {
-          "description": "Name for the screenshot",
-          "type": "string",
-        },
-        "savePng": {
-          "description": "Save screenshot as PNG file (default: false)",
-          "type": "boolean",
-        },
-        "selector": {
-          "description": "CSS selector for element to screenshot",
-          "type": "string",
-        },
-        "storeBase64": {
-          "description": "Store screenshot in base64 format (default: true)",
-          "type": "boolean",
-        },
-        "width": {
-          "description": "Width in pixels (default: 800)",
-          "type": "number",
-        },
-      },
-      "required": [
-        "name",
-      ],
-      "type": "object",
-    },
-    "name": "playwright_screenshot",
-  },
-  {
-    "description": "Click an element on the page",
-    "inputSchema": {
-      "properties": {
-        "selector": {
-          "description": "CSS selector for the element to click",
-          "type": "string",
-        },
-      },
-      "required": [
-        "selector",
-      ],
-      "type": "object",
-    },
-    "name": "playwright_click",
-  },
-  {
-    "description": "Click an element in an iframe on the page",
-    "inputSchema": {
-      "properties": {
-        "iframeSelector": {
-          "description": "CSS selector for the iframe containing the element to click",
-          "type": "string",
-        },
-        "selector": {
-          "description": "CSS selector for the element to click",
-          "type": "string",
-        },
-      },
-      "required": [
-        "iframeSelector",
-        "selector",
-      ],
-      "type": "object",
-    },
-    "name": "playwright_iframe_click",
-  },
-  {
-    "description": "fill out an input field",
-    "inputSchema": {
-      "properties": {
-        "selector": {
-          "description": "CSS selector for input field",
-          "type": "string",
-        },
-        "value": {
-          "description": "Value to fill",
-          "type": "string",
-        },
-      },
-      "required": [
-        "selector",
-        "value",
-      ],
-      "type": "object",
-    },
-    "name": "playwright_fill",
-  },
-  {
-    "description": "Select an element on the page with Select tag",
-    "inputSchema": {
-      "properties": {
-        "selector": {
-          "description": "CSS selector for element to select",
-          "type": "string",
-        },
-        "value": {
-          "description": "Value to select",
-          "type": "string",
-        },
-      },
-      "required": [
-        "selector",
-        "value",
-      ],
-      "type": "object",
-    },
-    "name": "playwright_select",
-  },
-  {
-    "description": "Hover an element on the page",
-    "inputSchema": {
-      "properties": {
-        "selector": {
-          "description": "CSS selector for element to hover",
-          "type": "string",
-        },
-      },
-      "required": [
-        "selector",
-      ],
-      "type": "object",
-    },
-    "name": "playwright_hover",
-  },
-  {
-    "description": "Execute JavaScript in the browser console",
-    "inputSchema": {
-      "properties": {
-        "script": {
-          "description": "JavaScript code to execute",
-          "type": "string",
-        },
-      },
-      "required": [
-        "script",
-      ],
-      "type": "object",
-    },
-    "name": "playwright_evaluate",
-  },
-  {
-    "description": "Retrieve console logs from the browser with filtering options",
-    "inputSchema": {
-      "properties": {
-        "clear": {
-          "description": "Whether to clear logs after retrieval (default: false)",
-          "type": "boolean",
-        },
-        "limit": {
-          "description": "Maximum number of logs to return",
-          "type": "number",
-        },
-        "search": {
-          "description": "Text to search for in logs (handles text with square brackets)",
-          "type": "string",
-        },
-        "type": {
-          "description": "Type of logs to retrieve (all, error, warning, log, info, debug)",
-          "enum": [
-            "all",
-            "error",
-            "warning",
-            "log",
-            "info",
-            "debug",
-          ],
-          "type": "string",
-        },
-      },
-      "required": [],
-      "type": "object",
-    },
-    "name": "playwright_console_logs",
-  },
-  {
-    "description": "Close the browser and release all resources",
-    "inputSchema": {
-      "properties": {},
-      "required": [],
-      "type": "object",
-    },
-    "name": "playwright_close",
-  },
-  {
-    "description": "Perform an HTTP GET request",
-    "inputSchema": {
-      "properties": {
-        "url": {
-          "description": "URL to perform GET operation",
-          "type": "string",
-        },
-      },
-      "required": [
-        "url",
-      ],
-      "type": "object",
-    },
-    "name": "playwright_get",
-  },
-  {
-    "description": "Perform an HTTP POST request",
-    "inputSchema": {
-      "properties": {
-        "url": {
-          "description": "URL to perform POST operation",
-          "type": "string",
-        },
-        "value": {
-          "description": "Data to post in the body",
-          "type": "string",
-        },
-      },
-      "required": [
-        "url",
-        "value",
-      ],
-      "type": "object",
-    },
-    "name": "playwright_post",
-  },
-  {
-    "description": "Perform an HTTP PUT request",
-    "inputSchema": {
-      "properties": {
-        "url": {
-          "description": "URL to perform PUT operation",
-          "type": "string",
-        },
-        "value": {
-          "description": "Data to PUT in the body",
-          "type": "string",
-        },
-      },
-      "required": [
-        "url",
-        "value",
-      ],
-      "type": "object",
-    },
-    "name": "playwright_put",
-  },
-  {
-    "description": "Perform an HTTP PATCH request",
-    "inputSchema": {
-      "properties": {
-        "url": {
-          "description": "URL to perform PUT operation",
-          "type": "string",
-        },
-        "value": {
-          "description": "Data to PATCH in the body",
-          "type": "string",
-        },
-      },
-      "required": [
-        "url",
-        "value",
-      ],
-      "type": "object",
-    },
-    "name": "playwright_patch",
-  },
-  {
-    "description": "Perform an HTTP DELETE request",
-    "inputSchema": {
-      "properties": {
-        "url": {
-          "description": "URL to perform DELETE operation",
-          "type": "string",
-        },
-      },
-      "required": [
-        "url",
-      ],
-      "type": "object",
-    },
-    "name": "playwright_delete",
-  },
-  {
-    "description": "Ask Playwright to start waiting for a HTTP response. This tool initiates the wait operation but does not wait for its completion.",
-    "inputSchema": {
-      "properties": {
-        "id": {
-          "description": "Unique & arbitrary identifier to be used for retrieving this response later with \`Playwright_assert_response\`.",
-          "type": "string",
-        },
-        "url": {
-          "description": "URL pattern to match in the response.",
-          "type": "string",
-        },
-      },
-      "required": [
-        "id",
-        "url",
-      ],
-      "type": "object",
-    },
-    "name": "playwright_expect_response",
-  },
-  {
-    "description": "Wait for and validate a previously initiated HTTP response wait operation.",
-    "inputSchema": {
-      "properties": {
-        "id": {
-          "description": "Identifier of the HTTP response initially expected using \`Playwright_expect_response\`.",
-          "type": "string",
-        },
-        "value": {
-          "description": "Data to expect in the body of the HTTP response. If provided, the assertion will fail if this value is not found in the response body.",
-          "type": "string",
-        },
-      },
-      "required": [
-        "id",
-      ],
-      "type": "object",
-    },
-    "name": "playwright_assert_response",
-  },
-  {
-    "description": "Set a custom User Agent for the browser",
-    "inputSchema": {
-      "properties": {
-        "userAgent": {
-          "description": "Custom User Agent for the Playwright browser instance",
-          "type": "string",
-        },
-      },
-      "required": [
-        "userAgent",
-      ],
-      "type": "object",
-    },
-    "name": "playwright_custom_user_agent",
-  },
-]
-`;
diff --git a/src/__tests__/tools/api/requests.test.ts b/src/__tests__/tools/api/requests.test.ts
index 689fa4cf..95d2793a 100644
--- a/src/__tests__/tools/api/requests.test.ts
+++ b/src/__tests__/tools/api/requests.test.ts
@@ -102,7 +102,7 @@ describe('API Request Tools', () => {
   });
 
   describe('PostRequestTool', () => {
-    test('should make a POST request', async () => {
+    test('should make a POST request without token', async () => {
       const args = {
         url: 'https://api.example.com',
         value: '{"data": "test"}'
@@ -110,7 +110,56 @@ describe('API Request Tools', () => {
 
       const result = await postRequestTool.execute(args, mockContext);
 
-      expect(mockPost).toHaveBeenCalledWith('https://api.example.com', { data: args.value });
+      expect(mockPost).toHaveBeenCalledWith('https://api.example.com', { 
+        data: { data: "test" },
+        headers: {
+          'Content-Type': 'application/json'
+        }
+      });
+      expect(result.isError).toBe(false);
+      expect(result.content[0].text).toContain('POST request to');
+    });
+
+    test('should make a POST request with Bearer token', async () => {
+      const args = {
+        url: 'https://api.example.com',
+        value: '{"data": "test"}',
+        token: 'test-token'
+      };
+
+      const result = await postRequestTool.execute(args, mockContext);
+
+      expect(mockPost).toHaveBeenCalledWith('https://api.example.com', { 
+        data: { data: "test" },
+        headers: {
+          'Content-Type': 'application/json',
+          'Authorization': 'Bearer test-token'
+        }
+      });
+      expect(result.isError).toBe(false);
+      expect(result.content[0].text).toContain('POST request to');
+    });
+
+    test('should make a POST request with Bearer token and custom headers', async () => {
+      const args = {
+        url: 'https://api.example.com',
+        value: '{"data": "test"}',
+        token: 'test-token',
+        headers: {
+          'X-Custom-Header': 'custom-value'
+        }
+      };
+
+      const result = await postRequestTool.execute(args, mockContext);
+
+      expect(mockPost).toHaveBeenCalledWith('https://api.example.com', { 
+        data: { data: "test" },
+        headers: {
+          'Content-Type': 'application/json',
+          'Authorization': 'Bearer test-token',
+          'X-Custom-Header': 'custom-value'
+        }
+      });
       expect(result.isError).toBe(false);
       expect(result.content[0].text).toContain('POST request to');
     });
diff --git a/src/tools.ts b/src/tools.ts
index d8f89259..bf05e478 100644
--- a/src/tools.ts
+++ b/src/tools.ts
@@ -159,6 +159,12 @@ export function createToolDefinitions() {
         properties: {
           url: { type: "string", description: "URL to perform POST operation" },
           value: { type: "string", description: "Data to post in the body" },
+          token: { type: "string", description: "Bearer token for authorization" },
+          headers: { 
+            type: "object", 
+            description: "Additional headers to include in the request",
+            additionalProperties: { type: "string" }
+          }
         },
         required: ["url", "value"],
       },
diff --git a/src/tools/api/requests.ts b/src/tools/api/requests.ts
index dadd5da1..cea954b6 100644
--- a/src/tools/api/requests.ts
+++ b/src/tools/api/requests.ts
@@ -48,7 +48,12 @@ export class PostRequestTool extends ApiToolBase {
       }
       
       const response = await apiContext.post(args.url, {
-        data: args.value
+        data: typeof args.value === 'string' ? JSON.parse(args.value) : args.value,
+        headers: {
+          'Content-Type': 'application/json',
+          ...(args.token ? { 'Authorization': `Bearer ${args.token}` } : {}),
+          ...(args.headers || {})
+        }
       });
       
       let responseText;


Reviews:
user: login: executeautomation
state: CHANGES_REQUESTED
author_association: OWNER
submitted_at: 2025-03-19T05:46:45Z
user: login: CopilotMe
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T03:40:28Z
user: login: CopilotMe
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T03:46:37Z
user: login: executeautomation
state: APPROVED
author_association: OWNER
submitted_at: 2025-03-21T02:40:06Z

Review comments:
diff_hunk: @@ -48,7 +48,10 @@ export class PostRequestTool extends ApiToolBase {
       }
       
       const response = await apiContext.post(args.url, {
-        data: args.value
+        data: args.value,
+        headers: args.token ? {
path: src/tools/api/requests.ts
user: login: executeautomation
body: If you are submitting this Pull request, then you need to update the following

1. The tests.
2. The documentation.
3. Make sure you also update the tools.ts
4. And attach screenshot of this execution of the code change that you have made by running in Cline or cursor or Claude Desktop

Thanks,


created_at: 2025-03-19T05:46:41Z
author_association: OWNER
diff_hunk: @@ -48,7 +48,10 @@ export class PostRequestTool extends ApiToolBase {
       }
       
       const response = await apiContext.post(args.url, {
-        data: args.value
+        data: args.value,
+        headers: args.token ? {
path: src/tools/api/requests.ts
user: login: CopilotMe
body: Screenshot:
<img width="749" alt="image" src="https://github.com/user-attachments/assets/f42cb17d-5dea-41d2-b217-81d291cbd8fd" />

created_at: 2025-03-20T03:40:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -48,7 +48,10 @@ export class PostRequestTool extends ApiToolBase {
       }
       
       const response = await apiContext.post(args.url, {
-        data: args.value
+        data: args.value,
+        headers: args.token ? {
path: src/tools/api/requests.ts
user: login: CopilotMe
body: Hey,  @executeautomation! Thank you for your suggestions what to add next!
1. Please review and test in details.
2. Also can you plan to support Bearer in the other requests too in the same manner we did here?

created_at: 2025-03-20T03:46:36Z
author_association: CONTRIBUTOR

PR comments:
user: login: executeautomation
created_at: 2025-03-20T21:45:04Z
body: Looks good @CopilotMe , can you please update your branch and send me the PR, we can merge and push it in today's release. 

Thanks,
Karthik KK
author_association: OWNER
user: login: CopilotMe
created_at: 2025-03-21T01:46:33Z
body: Hi K,
If you mean the snapshot conflict I tried a couple of times to update it, but it I seems git does not show any conflicts locally. Please go ahead and just update the branch yourself if you know how to resolve this. 

Here is example:
```
Snapshot Summary
 › 1 snapshot updated from 1 test suite.

Test Suites: 7 passed, 7 total
Tests:       44 passed, 44 total
Snapshots:   1 updated, 1 total
Time:        3.394 s
Ran all test suites matching /#|or|yarn|test/i.
shteryodzhimov@Shteryos-MacBook-Pro mcp-playwright % touch src/__tests__/__snapshots__/tools.test.ts.snap
shteryodzhimov@Shteryos-MacBook-Pro mcp-playwright % git add src/__tests__/__snapshots__/tools.test.ts.snap
shteryodzhimov@Shteryos-MacBook-Pro mcp-playwright % git commit -m "commit snapshot file"
On branch optional-bearer-token
Your branch is up to date with 'origin/optional-bearer-token'.

nothing to commit, working tree clean
```
author_association: CONTRIBUTOR
user: login: executeautomation
created_at: 2025-03-21T02:39:43Z
body: Yes that looks good to me as well, I will approve it now and merge it !
author_association: OWNER
user: login: executeautomation
created_at: 2025-03-21T02:48:30Z
body: Can you please remove this file and update the branch, I see there is a conflict 

<img width="958" alt="image" src="https://github.com/user-attachments/assets/d87be7d9-5e83-4f7f-a061-dcd5987fc1b7" />


Thanks,
Karthik KK
author_association: OWNER