Showing posts with label MCP. Show all posts
Showing posts with label MCP. Show all posts

Friday, 31 October 2025

Playwright Agents in VS Code

I started looking at the latest version of Playwright late last night. The Agents add-in for VS Code is amazing.  I can't stop improving my code, my tests, and my automation.  It is highly addictive.

Playwright 1.56.1 includes the new Playwright CLI, which has the test agents as shown in VS Code above:

Node: v22.21.0
npx: 11.6.2
Playwright test package: ^1.56.1

Wednesday, 30 July 2025

AI for developers and Architects

The cost of prototypes is unbelievably low using AI. 

Rapidly creating a prototype, especially with new or less well-known technology, is where I derive significant benefits from AI.

How to build application prototypes?

  1. Write /reverse prompt/Adjust instructions into md file
  2. Agentic AI (specialising in Doc Extraction) to extract and refine from md file
  3. Run using IDE-based copilot (VS Code with GitHub Copilot) (AmazonQ) (Cursor, Windsurf, Steamlit) 
Thoughts: Developers are adjusting to using Ai to support software solutions.  The developer role will continue the trend of making technical implementation more accessible, allowing knowledgeable IT engineers or domain experts to build faster and better than citizen/amateur developers.  Ai assists in complex decisions!  

What needs to improve?
  • Knowledge is key.  AI needs to have narrow expertise at the right time. i.e. only domain knowledge, not influenced by other data.  Quality of input data used to train.  Allows for dynamic reasoning.
  • Session/long-term contact agreement/understanding to improve the understanding between your IDE and me.  Remember how I prompt and provide feedback on how I digest information.  Context between the human developer and Ai is Paramount.
  • Control of IDE integration with coding copilots, clear return to the user developer to make better decisions.  Context is Paramount.
  • Governance & Data (Connectors, API's, code complex processes (MCP maybe), quality of data).

Retrieval Augmentation Generate (RAG)


AI needs to be able to connect to my existing Tool Landscape:
I use Azure, C#, Playwright, and GitLab.  I want my IDE to work with these tools and many more.  MCP servers publish their functionality, and I can connect my Copilot/Agent to use multiple MCP servers.  This is what GHCP does for VS Code, allowing you to add MCP clients dynamically to use existing MCP Servers. 

Model Context Protocol (MCP)

MCP is a protocol (created by Anthropic) that allows an MCP client to connect to an MCP server, which in turn provides specialist knowledge. Authentication should use OAuth to secure access.

My Applications/Agents use the MCP to ask the MCP Server, 'What can you do?' so they know how to use it.  The MCP server allows you to interact with a system.  It is often referred to as the "Arms and Legs" of AI.

The MCP Server, when built, informs the client of its capabilities and then performs actions, such as updates, via an API.  

Summary: Use MCP to enable the client to communicate with other resources/tools.

NB: An Agent can utilise multiple MCP Servers.

Agents-to-agent (A2A) 

A2A allows agents to work together.  So two agents can leverage each other; the other agent solves the issue and returns the answer for the first agent to use.  Whereas MCP allows any agent to speak to a source.  Agents complete a task and give it back to the calling agent. 
 
Summary: Use A2A to talk to specialised Agents, and the agent returns the calling agent's answers.


Monday, 2 June 2025

Copilot Studio 2025 Notes

Copilot Studio is fantastic, the licensing is complex, and the AI integration is excellent. Architects really need to understand Licensing and billing, or AI will get out of control.  The Purview and governance look very good.  Copilot Studio Cost Estimator (preview June 2025)

MS Build 2025: 

MCP Server in Preview - possible to collect data from other AI services or write back.

Connector Kit - So, you can add custom connectors from Power Platform Connectors, including Copilot Studio - great stuff.

Agent Flow - Added functionality to Power Automate flows (Copilot Studio aware), deployed via solutions.

NoteThe M365 Agent Toolkit appears to be an interesting tool that allows agents to perform tasks using Office add-ins with VS Code.

Licensing

You need to be aware:

  • M365 agents - require all end users to have M365 Copilot licences, retailing at $20/user.  Alternatively, users can consume the agents using a PAYG model per message (it racks up quickly).  I can add these to MS Teams, and it appears that people with licences can ask the M365 agent, while others can view the results (some more testing and understanding are needed here by me).
  • Copilot Studio - Requires a Copilot Studio AI Studio/maker licence at $30/retail. Users don't need a licence to use it, but you pay per message, and this can rack up quickly, so watch your usage. Buying bulk message credits can help reduce costs.
  • Each prompt generates multiple messages, which are all billable (complex to calculate)
  • (If you use Copilot Studio and it calls Azure AI Foundry, it also bills Tokens (also complex to estimate)
  • Copilot Studio utilises the AI Foundry connector through its Premium connector.

Wednesday, 6 November 2024

Fix links within Pdf files when moving from a File share to web hosting

Overview: Build a console to help migrate more than 80k PDF document internal links. The client used a DFS SMB file share to hold index PDFs and multiple documents that needed to be moved to a SharePoint document library.

Hypothesis: Loop through all PDFs in a folder; if there are links, identify the file server links and convert them to web links so they work in the new SharePoint document library.  Various tools were identified as possible solutions, but came up short in the migration.  Two good tools are Replace Magic & PDF-XChange Editor.

Resolution: Below is the C# code I wrote to update the links in VS Code.  The debugger was helpful because there were many different link types across the plethora of PDFs.


C# Code

using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Annot;
using iText.Kernel.Pdf.Action;
using iText.Bouncycastle.Crypto;  // pdf fails at runtime periodically without the directive
using System.Text.RegularExpressions;
class Program
{
    static void Main(string[] args)
    {
        string folderPath = @"C:\Users\PaulBeck\Downloads\Software\LinkConvert\ConvertLinksCsharp\"; // Replace with your folder path          
        Console.WriteLine("Please enter a folder Path: e.g. " + folderPath);
        string inputPath = Console.ReadLine();        
        Console.WriteLine("Last Path: e.g. Childfolder2");
        string inputPathVol = Console.ReadLine();
        if (inputPath.Length>5)  {
            folderPath = inputPath;   }
        string[] pdfFiles = Directory.GetFiles(folderPath, "*.pdf");
        foreach (string file in pdfFiles)
        {
            Uri fileUri = new Uri(file);
            string directory = Path.GetDirectoryName(file);
            string filename = Path.GetFileNameWithoutExtension(file);
            string extension = Path.GetExtension(file);           
            string newFilename = $"{filename}_new{extension}";  // Create the new filename
            string newFilePath = Path.Combine(directory, newFilename); // Combine the directory and new filename to form the new URL
            UpdatePdf(fileUri.AbsoluteUri,newFilePath, inputPathVol);
        }
    }
private static void UpdatePdf(string inputFilePath, string outputFilePath, string lastPathPart)
{
    var varInnerName = "";
    PdfDocument pdfDoc = new PdfDocument(new PdfReader(inputFilePath), new PdfWriter(outputFilePath));
        for (int i = 1; i <= pdfDoc.GetNumberOfPages(); i++)        // Iterate through the pages
        {
            var page = pdfDoc.GetPage(i);
            var annotations = page.GetAnnotations();
            foreach (var annotation in annotations)            // Iterate through the annotations
            {
                if (annotation.GetSubtype().Equals(PdfName.Link))
                {
                    var linkAnnotation = (PdfLinkAnnotation)annotation;
                    var action = linkAnnotation.GetAction();                                                        
                    if (action is PdfDictionary dictionary)
                    {
                        foreach (var key in dictionary.KeySet())
                        {
                            var value = dictionary.Get(key);
                            Console.WriteLine($"{key}: {value}");
                            var varPdfNameF = dictionary.Get(PdfName.F);
                            if (varPdfNameF is PdfDictionary varF2Dict)      {
                                varInnerName = varF2Dict.Get(PdfName.F).ToString();  }
                        }
                    }
                    else
                    {   Console.WriteLine("No URL found.");    }  
if (action != null && (action.Get(PdfName.S).Equals(PdfName.GoToR) || action.Get(PdfName.S).Equals(PdfName.Launch)))                
                      {
                        var varF = action.GetAsString(PdfName.F)?.ToString() ?? "";
                        var uri = $"https://radimaging.sharepoint.com/sites/Documents/Standards/{lastPathPart}/{varInnerName}";          
                       string pattern = @"\.\./";
                       if(uri.Contains("../"))    // string cleanUrl1 = Regex.Replace(uri, pattern, string.Empty);
                       {          
                        uri = $"https://radimaging.sharepoint.com/sites/Documents/Standards/{varInnerName}";
                        uri= Regex.Replace(uri, pattern, string.Empty);                       
                       }
                        var newAction = PdfAction.CreateURI(uri);
                        if (varF.Length > 20)      {
                            newAction = PdfAction.CreateURI(varF);     }
                        linkAnnotation.SetAction(newAction);
                    }           
                }
            }
        }
        pdfDoc.Close();
        Console.WriteLine("PDF links updated successfully!");
    }
}


Quick way to use the PDF X-Change Editor tool to change links.

Example of how to correctly set internal links within a pdf:


Example of how to set links to pages or files on the Internet/extranet:


Update Nov 2025: User Playwright MCP to download an index pdf, and then download or open files and pages to validate the links worked.  Updated the index pdf using C# Console built using Github Copilot (GHCP) and updated into SharePoint using MS Graph the updated fixed pdf.