I’ve been experimenting with MCP and tool calling to learn a bit more about how it works, and what its capabilities are, and ultimately how that can be applied to Quality Engineering through test automation.
My initial aim was to investigate the MCP server for Playwright but I wanted to learn how to call the tools from code as well as use the tools interactively, so rather than follow one of the many guides out there, I started to look at how MCP clients are built.
There’s a ‘getting started’ guide for all things MCP at https://modelcontextprotocol.io/docs/getting-started/intro , and within that, some code samples including details on how to build a client in node (https://modelcontextprotocol.io/docs/develop/build-client#node), and the sample code in github here.
Unfortunately, the sample uses Anthropic’s Claude model whilst I have an OpenAI API subscription (and also run local models using https://lmstudio.ai/), so I (heavily) modified the TypeScript MCP client to suit my needs (using ‘vibe coding’ / prompt-driven-development)
I’ve pushed my code to a branch on a fork of the original project, so if you’re interested in taking a look clone this:
https://github.com/ObjectiveTester/quickstart-resources/tree/multiserver-support
Getting started – OpenAI
To get started, you’ll need an API key for OpenAI – write it to a .env
file in the mcp-client-multiserver-typescript
directory with:echo "OPENAI_API_KEY=<your key here>" > .env
Getting started – Local model
You’ll need LMStudio installed or accessible somewhere, with a good tool calling model like gpt-oss-20b – modify index.ts
to comment in the loopback address for the API endpoint and set the appropriate model (and comment out the remote OpenAI stuff)
Running
Then run the following commands from the mcp-client-multiserver-typescript
directory:npm install
npm run mcp
Assuming no errors, you should see a lot of debug, including a list of all the available tools – by default the config includes Playwright and Filesystem MCP servers.
Controlling Playwright
You can then get started with controlling the browser in natural language – as a demonstration of the capabilities try:start a codegen session. write the output to the current directory
open www.saucedemo.com
create a page object from the page source in the current directory and name it login_object.js
login using standard_user / secret_sauce
create a page object from the page source in the current directory and name it products_object.js
add a backpack to the cart
start the checkout process
create a page object from the page source in the current directory and name it shipping_object.js
fill out the shipping details and continue
assert the total price is 32.39
create a page object from the page source in the current directory and name it overview_object.js
click finish
The generated page objects and the output from the codegen session look to be complete and it would be quite easy to refactor the generated code – so let’s try it !refactor the codegen file test_1e97f687-bde6-435d-bdf1-2653fca371b2.spec.ts to use the _object.js page object files in this directory
The resultant output is:
import { test } from '@playwright/test';
import { expect } from '@playwright/test';
import LoginPage from './login_object.js';
import ProductsPage from './products_object.js';
import CheckoutInformationPage from './shipping_object.js';
import CheckoutOverviewPage from './overview_object.js';
test('Test_2025-10-13', async ({ page, context }) => {
// Create page object instances
const loginPage = new LoginPage(page);
const productsPage = new ProductsPage(page);
const checkoutInformationPage = new CheckoutInformationPage(page);
const checkoutOverviewPage = new CheckoutOverviewPage(page);
// Navigate to URL
await page.goto('https://www.saucedemo.com');
// Perform login
await loginPage.enterUsername('standard_user');
await loginPage.enterPassword('secret_sauce');
await loginPage.clickLoginButton();
// Add product to cart
await productsPage.addProductToCart('sauce-labs-backpack');
// Proceed to checkout
await page.click('.shopping_cart_link');
await page.click('#checkout');
// Fill shipping information
await checkoutInformationPage.enterFirstName('John');
await checkoutInformationPage.enterLastName('Doe');
await checkoutInformationPage.enterPostalCode('12345');
await checkoutInformationPage.clickContinue();
// Complete the purchase
await checkoutOverviewPage.clickFinish();
});
All it’s missing is the assertion (which I can easily add), but in a few minutes I’ve been able to prototype up a test using page objects just by describing in natural language what I want to do
Running
The above code works as expected and can be executed with:npm install @playwright/test
npx playwright install
npx playwright test --headed
Future development
The client supports multiple MCP servers so you can easily spin up and include additional MCP servers through Docker, either to experiment with other things or to begin to add additional capabilities to the rapid test prototyping workflow.