Our context: Studying language models in Digital Humanities

For the past year and a half, our small Digital Humanities (DH) lab team has been investigating Small Language Models (SLMs) and how we can work with them on projects involving XML technologies. We find SLMs important to investigate in academic contexts for two primary objectives:

  1. Education: To design explainable AI systems that help us to practically understand how language models process, retrieve, and generate text;[1]

  2. Economy: To design customized AI systems that can improve projects that require accurate data representation and quality control validation.

The first objective (Education) is daunting, especially in a humanities or DH context. AI systems are not readily comprehensible by humans without some level of abstraction and analogy, or without learning by testing and applying the systems to a research project. DH is an umbrella term that includes widely varying practices of computational methods for media curation and analysis, as well as studies of human interactions with technology. Usually it is practiced in research and teaching in higher educational contexts around the world. Digital humanists do generally unite around the critical practices of learning by doing and sharing tutorials openly. For example, The Programming Historian serves as a peer-reviewed DH community resource that supplies tutorials for humanities scholars seeking support for hands-on learning, usually with Python and R applications that orient humanities scholars to statistical libraries and methods for linguistic analysis, regular expressions, and applications of TEI XML encoding and processing. As of 2026, we do not find much available specifically for digital humanists to learn about the internal processes of language models, literally how they work.[2]

To understand how language models process text in particles requires some perspective on the statistical computations and embedding structures that support next-token prediction in generative AI systems. For decades, introductory DH courses have delivered hands-on experiences with free software like Voyant Tools and Antconc to demonstrate how computer software divides text into grams or meaningful particles and aggregate them into measurable clusters for n-gram analysis and concordance searching for keywords in context. That foundation is helpful now as we need to learn how large language models work with text tokens in generative contexts, and also how different these are from the classification methods that we are more familiar with.

In the context of DH students learning about Large Scale Text Analysis in the year 2026, we find ourselves trying to connect with our foundations (in this case teaching with markup languages and Python) and still expanding horizons with hands-on applications.[3] Tutorials that aim to demonstrate how language models work under the hood are often not very helpful: They can be overly complex, requiring a background in advanced computer science and matrix mathematics to approach, or too simplistic about vector embeddings (offering problematic nonsense formulae like King - Man + Woman = Queen as an explanation of how a language model calculates relative semantic positions of one token in relation to another).[4]

A few resources offer some challenging math-forward but nevertheless accessible hands-on applications with models like GPT-2 that can be installed and explored on a personal computer. Mike X. Cohen’s six-part LLM breakdown series can support students who are learning Python and working with Jupyter Notebooks following his motto, You can learn a lot of math with a bit of code, and it provides a rare, gently-guided opportunity to see and apply available functions within LLM models. Students engaging with this series can actively visualize the scale of vector embedding layers, to flatten the thousands of dimensions of language model vector space to two dimensions and visualize in real time the position of the next token that is the most certain match in autocompleting a sentence, to create hooks that manipulate how confidence and certainty are calculated, and to see that the system is after all a technology that can be explored and manipulated even if we humans cannot see all its dimensional possibilities at once.[5] Cohen himself is a neuroscientist and former academic but not in the humanities; yet his public social media release of the LLM breakdown series helps to substantiate that the complex math of LLMs can be made more generally accessible. Within our DH program and lab, where we prioritize coding as a means to visualize and communicate complex ideas and technologies, Cohen’s notebooks help us to learn by working directly with the code and giving us ideas for projects that involve testing the models and visualizing how they work.

One such project is our AI Uncertainty Monitor, designed to dramatically slow down, visualize and sonify the processing of a language model. The user selects a small language model from our interface, inputs a prompt designed to evoke many possible responses, such as Describe the feeling of standing on the edge of the ocean at night. The model slowly produces a response, with lag introduced based on uncertainty data pulled from each prediction of a new token, and each token is color-coded and given musical tones relating to properties pulled from the language model. Find the project on Codeberg at https://codeberg.org/newtfire/ai-artsound and install with Docker from https://codeberg.org/newtfire/-/packages/container/ai-artsound-container/latest. Developing this Uncertainty Monitor project has been invaluable to the DigitAI project, especially in providing capacity to select from a library of SLMs and learning how to serve it with a Docker container, as we will discuss later in this paper.

Regarding the second objective (Economy), universities and cultural heritage organizations face severe budget constraints in the 2020s yet, paradoxically, increasing reliance on AI has resulted in the upscaling of compute GPU resources, and dependencies on data centers impact our environment and human labor. When contracts with AI tech companies are heavily promoted at universities, and the cognitive offloading of thinking, writing, and assessment to chatbots risks eroding rather than enhancing critical thinking and problem-solving skills, AI threatens the human labor of the academy and the efficacy of education.[6]

Looking at this grim picture from the vantage point of a small DH lab, we nevertheless recognize in AI an emerging technology that calls for serious scholarly work, and we are researching language models as powerful instruments that can be tuned and adapted for local computer systems. We hope that our work with SLMs as alternatives to LLMs demonstrates a method to minimize the resource footprint of generative AI models, finding ways to work with them on small-scale systems without manufactured dependencies on data centers and water supplies.[7] We also hope that our research helps to pry open the proverbial black box software built on unknowable assemblages of text and media. We are finding that our familiarity with concepts like declarative programming and a separation of concerns helps us to find a way to work with AI as one technology balanced with the precision tools of the XML stack.

The DigitAI project with MCP: An orientation for markup people

As markup people ourselves, we are accustomed to a certain mystery in the underlying technologies that support processing declarative markup (e.g., how our own operating systems decide to execute the template processing in our XSLT scripts). But also as markup people, we find ourselves uncomfortable with the opacity of operations in language models, and the fact that we cannot reproduce the exact same results twice when we deliver them the same prompt. We are learning that we can inspect the operations of a language model, retrieve the data on how tokens are retrieved, and even begin to comprehend something of how the system works. But we also know that we cannot expect a language model to handle the things we call text in the same way that we are used to when we encode a markup tree, query it to retrieve, sort, conduct string-surgery, and make calculations with nodes.

The mystery in the operations of a language model has everything to with its embeddings of word-particles in thousands of dimensions, and even the idea that two language models trained on the same word data with the same number of parameters and embeddings do not share the same vectors. This suggests something of an internal cosmos beyond human reckoning, yet nevertheless formulated to work in a consistent way as a predictive model. One interfaces with it, and retrieves back something that perhaps might be said to resonate in response to the prompt and the internal warp and woof of the intricately layered model of language. It seems intricate in its potential for variation, and unpredictable in its predictive functioning. Certainly language models are known for their tone of overconfidence that thinly veils their unreliability. To convert them into helpful retrieval engines necessitates some kind of augmentation and alternative access to data. Boosting reliability involves supplying access to resources that are more definitely structured and validated for quality control. These resources come from outside the resonant machinery of token prediction subject to temperature and impenetrable embedding layers with word-particles shrouded in logarithmic numbers and clustered by cosine similarities.

Encountering language models as marvelously speedy and profound yet vapid, mercurial, and dangerous, we want to at least try to customize black box technology to apply to difficult and time-consuming aspects of markup-based projects. We want an AI assistant to help with finishing old projects from community volunteers with inconsistent markup, and to aid students and colleagues with challenging questions like how best to apply the many possibilities of TEI encoding from the TEI Guidelines.[8] We are trying to create an assistant that safely deploys one or more SLMs with a multi-core personal computer to assist with many different XML projects, to help scholarly developers with:

  1. finding and reviewing information about their project’s encoding and reporting on inconsistencies beyond schema validation checks;

  2. answering questions in natural language, based on a review of the project’s structured data;

  3. executing transformations of the data with permission and based on conversation with the developers.

Initially, we only thought of achieving goals one and two, but as our project has developed, we find it capable of goal three. But let us tell you the story from the beginning.

Early stage RAG experiment

We launched a project in 2025 called DigitAI (named for our Digit program at Penn State Behrend). DigitAI began as an experiment to supply a small-scale language model, Qwen2.5:3B-Instruct, with Retrieval Augmented Generation (RAG)–giving it query access to a structured ground truth knowledge base derived from the XML structure of the TEI P5 Guidelines. The TEI P5 Guidelines can be built as a single complete XML document from the TEI Guidelines GitHub repository and are updated with each new release of the Guidelines. This highly structured document seemed ideal as a RAG resource if developed in a context-aware manner, stored and accessed in information chunks based on the text content of XML element nodes. We shared our first attempts at preparing an immense and complex XML-node-based RAG from the TEI Guidelines at the DH2025 conference and the 2025 TEI conference (as well as in an open-mike presentation at Balisage 2025), but we knew at the time of those conferences that our work was incomplete.[9] The RAG included only the chapters and paragraphs and element nodes with glosses and descriptions, and to be complete to our vision should have included the Schematron constraints and model and attribute class relationships. But perhaps the idea to prepare the TEI P5.xml as a graph structure so thoroughly was overkill, and would not improve the reliability of our local language model in addressing questions about TEI XML encoding.

Our first effort with preparing a RAG from the TEI Guidelines posed a serious problem of code bloat. We had prepared a knowledge graph in neo4j to store our carefully parsed content from the TEI Guidelines.[10] The preparation of the ideal RAG resource for us involved a complex XSLT transformation of the TEI P5.xml document into neo4j’s graph structure–converting every possible relationship of the paragraphs of the TEI Guidelines chapters and their associated encoding schema definitions of elements, model classes, and attribute definitions. Our team had prepared an incomplete RAG resource storing relationships of chapter paragraphs to elements and attributes and encoding examples discussed, but lacking detail from the element and attribute specifications. Our attempt made an abstracted and bloated version of the original XML document because we were writing XSLT to

  1. Output the neo4j graph nodes and edge relationships of elements networked by paragraph and chapter position across the TEI Guidelines, and

  2. Output a means for the neo4j graph to be queried by the LLM.

Worse yet, for all the painstaking effort of preparing the data with XSLT, our preliminary RAG did not provide very much improvement on an unaided Qwen model. When prompted with questions about the TEI Guidelines, the model would still deliver hallucinated elements. We had serious concerns about whether and how reliably information was pulled from our RAG resource. We realized a very serious problem: the model was relying on word embeddings to translate the information it received from the graph RAG, and the conversion to word embeddings did not improve on the neo4j database or the XML data structure. We planned to complete the RAG graph of the TEI P5 anyway as a resource in its own right (and we may yet do so to assist with reviewing the TEI Guidelines).

Turning to MCP

By autumn 2025 we found a better method to address XML as XML and keep it out of reach of the unreliable inner workings of language models. By this time the Model Context Protocol (MCP) introduced by Anthropic was widely available in the open source community, so we could now consider how to design a new system that would deploy a language model as an agent. Instead of trying to augment the stores and reach of its word-embeddings, a language model working as agent is given direct access to external applications by which it can retrieve information and interact with external data structures and software.[11] When our team began reading about MCP, we realized that XML data need not be restructured to be available to an AI system, but instead we could provide the model with agency to work with XML stack tools, to write, adapt, and execute XPath and XQuery scripts to address XML tree data directly. We could ask our agentic model to review schema validation reports from Relax NG and Schematron, and give it authority to adapt and run XSLT transformations.

We have found that with MCP scripts, we no longer have to provide parsed information from the TEI Guidelines or information drawn from documents and spoonfed to the language model in JSON lines for embeddings. Instead, we give the language model agency to access and adapt parsing scripts. In this environment, a Python script with Saxon parsers provides a much more efficient pathway to improve the language model’s capacity to review XML documents, any well-formed XML documents for its structured data.[12] The agentic MCP workflow allows our project to expand to work with any inconsistent code and assist with identifying and actively patching problems. We have developed our MCP to apply

  • XPath to review XML data,

  • XQuery to pull and report XML data,

  • XSLT to perform identity transformations to patch XML data.

As of spring 2026, we have devised a working MCP prototype that addresses XML file inputs with each of these technologies.

Giving AI models access to code execution scripts necessitated learning how to secure our systems to operate only within controlled parameters and restricting its local access to specific directories and to separate it from sensitive data on our computers, as we will discuss in later sections.

Some risky business with MCP

Perhaps learning to use AI effectively is understanding its limits regarding structured systems like XML. In our experience as a lab with primarily declarative markup expertise, we approached this project as people who understood very little about AI but a lot about XML. It is still puzzling to us that a language model with all its internal dimensionality cannot inherently understand the tree structure of an XML document. When the foundation of meaningful understanding is based on sequential chunks of tokenized text, we cannot forget that tags are just tokens to the internal workings of a language model. Nevertheless, when asked to act upon element nodes, and given agency and software to do so, a language model demonstrates competence in scripting code and suggesting improvements.

Since we were entirely new to MCP, we switched temporarily from the Qwen2.5:3B-Instruct model we had been working with to deploy the much larger Claude Desktop application as our language model agent. Because we were learning about MCP via Anthropic, it seemed convenient to deploy MCP through Anthropic’s Claude to start our learning process. We also began by following Claude's suggestions for a starting MCP Server Protocol delivered by a Python script that supplies access to Saxon processing tools and jingtrang for RelaxNG schema validation.

Trying out MCP with some troublesome XML

To try out MCP, we began with a simple example of inconsistent XML markup for a recipe, used as teaching material for students in our introduction to text encoding. We purposefully munged the XML to make it invalid to its schema in order to test the capacities of our desktop agent to discover validation errors plus inconsistencies in the markup, recommend improvements, and then process corrections. At first we struggled with the field containment systems of our agentic language model: It could not read relative filepaths and could not locate the schema. We watched the agent rapidly attempt many strange things, including an attempt to access the Chrome browser developer tools trying to read filepaths outside its internal mounted system when we refused to upload files into its chat interface. Eventually we learned that the problem with its reading filepaths had to do with a certain Python assumption of how to open and read files, one that we are familiar with from reading and opening Python files conventionally with lines like these:

with open(self.xml_path, 'r', encoding='utf-8') as f:
    xml_text = f.read()
self.xml_node = self.saxon_proc.parse_xml(xml_text=xml_text)
f.read() would pass the XML as a text string rather than a document tree. Changing that to:
self.xml_node = self.saxon_proc.parse_xml(xml_file_name=str(self.xml_path))
neatly resolved a serious pathing problem by delivering the file directly to the Saxon processors. From this experience we learned firsthand the security risks of a misconfigured and uncontained agentic model, as it seems motivated to try and try and try again when faced with failure, seeking access far beyond one’s project directories. The capacity to continue attempting related kinds of actions, exceeding prescripted bounds, may be part of the natural language processing powers: not just hallucinating speculative responses but acting on speculations in one’s file system. Seeing this behavior urged us to the next stage of our project, to learn how to set up the MCP system inside a secure Docker container instead of treating it like most other software whose passivity we take for granted.

Establishing the means to read local filepaths, find the schema, and execute jingtrang for schema validation made for a more tranquil and less surprising and impulsive experience with the desktop agent. When asked to correct an inconsistency, it now finds and runs the XSLT identity transformation tool, and follows up by validating the results with Relax NG and continuing to repair. We began requesting the model to save its transformation scripts and find that it is now building on our example identity transformations, scripting in our style of applying <xsl:mode on-no-match="shallow-copy"/> without inserting the old-fashioned identity transformation by copy of elements and attributes and processing instructions from XSLT 2.0 that it had used when it was not properly reading the MCP examples we provided. The model also helpfully documents each new template in XML comments.

To evaluate Claude Desktop (Sonnet or Opus) as an agent and simply to ensure it was working with our MCP tools, we began by supplying it just the problematic recipe XML file and its Relax NG schema, which we provide in Appendix A. Here we wish to foreground our MCP function as we supplied as a model for generating an identify transformation for patch corrections in XSLT 3.0 with a starter stylesheet setting the use of <xsl:mode:on-no-match="shallow-copy"/>. Following this we provide one of the XSLT transformations that the Claude Desktop model provided in transforming the recipe file, evidently applying and adapting our MCP tool to its task.

Figure 1: MCP Python function to generate corrections with XSLT

 def _generate_correction_xslt(self, corrections: list[dict]) -> str:
        """Generate XSLT 3.0 stylesheet from correction operations"""
        templates = []

        for i, correction in enumerate(corrections):
            op = correction.get("operation")

            if op == "update":
                xpath = correction["xpath"]
                updates = correction["updates"]

                # Generate template for update
                template = f"""
    <xsl:template match="{xpath}" priority="{i + 10}">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
"""
                if "attributes" in updates:
                    for attr, value in updates["attributes"].items():
                        template += f'<xsl:attribute name="{attr}">{value}</xsl:attribute>\n'

                if "text" in updates:
                    template += f'<xsl:text>{updates["text"]}</xsl:text>\n'
                else:
                    template += '<xsl:apply-templates select="node()"/>\n'

                template += """</xsl:copy>
    </xsl:template>
"""
                templates.append(template)

            elif op == "delete":
                xpath = correction["xpath"]
                templates.append(f'    <xsl:template match="{xpath}"/>\n')

        # Build complete stylesheet
        xslt = f"""<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:mode on-no-match="shallow copy"/>
    <xsl:template match="/">
       <xsl:apply-templates/>
    </xsl:template>

    <!-- Correction templates -->
{''.join(templates)}
</xsl:stylesheet>
"""
        return xslt                    

Figure 2: Claude Desktop’s XSLT for transforming the problematic recipe XML

<?xml version="1.0" encoding="UTF-8"?>
<!-- 
    2026-04-09 syllabubFix.xsl
    Authored by: ebb + Claude (Anthropic)
    Purpose: Corrects four schema validation errors in syllabubRecipe.xml:
      1. Renames <it> element to <item> (lines 29x2)
      2. Renames misspelled attribute qant to quant (line 31)
      3. Adds missing required num="7" attribute to the Decorate <step> (line 63)
    Input:  data/syllabubRecipe.xml
    Output: outputs/syllabubRecipe.xml
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">

    <!-- Identity transform: copy everything through unchanged by default -->
    <xsl:mode on-no-match="shallow-copy"/>

    <!-- Fix 1: Rename <it> to <item> -->
    <xsl:template match="it">
        <item>
            <xsl:apply-templates select="@* | node()"/>
        </item>
    </xsl:template>

    <!-- Fix 2: Rename attribute qant to quant -->
    <xsl:template match="@qant">
        <xsl:attribute name="quant" select="."/>
    </xsl:template>

    <!-- Fix 3: Add missing num="7" to the unnumbered step -->
    <xsl:template match="step[not(@num)]">
        <step num="7">
            <xsl:apply-templates select="@* | node()"/>
        </step>
    </xsl:template>
</xsl:stylesheet>  

For us, the generation of this well-documented XSLT (and others like it) in response to requests to review validation errors and introduce corrections was a sign that our agent was working with the MCP tools we had provided it for validation and helpful code transformations. Reaching this point was helpful for us as a calibration test of our MCP system.

Logging interactions and processes

We are devising a protocol for collecting log data from the desktop language model to document its processes. Log tracking will help us to review whether the model is doing the processing correctly (i.e., how we want it to) and also that we are able to study how it thinks, which then helps us better equip it...to help us! This aligns closely with the declarative mindset we share in working with XML technologies, where clarity of structure and intention leads to more reliable and reproducible outcomes.

As we move from testing to deployment to assist with real XML projects, we plan to devise a logging system that will record and store a log file for each interaction with the DigitAI XML-MCP agent. A log file could be recorded plain text or XML, and include:

  • date and time of the interaction,

  • which file resources the language model is accessing,

  • dialogue with user and thought-process,

  • the specific XPath, XQuery, or XSLT scripts it is applying,

  • directory location of any stored outputs.

Additionally, where XSLT is applied to transform XML, a changelog should be added directly to the file output recording the interaction. At the time of this writing in July 2026, this logging system has yet to be developed.

How could we improve the MCP?

There is something perverse about seeing XSLT written into formatted strings as shown in Figure 1. We asked ourselves during a project meeting in April 2026 whether the Python template provided us by Claude might be unnecessary. Why not just deliver the XSLT as XSLT? The professor among us asked as much to a Claude Opus 4.6 Extended session on the internet.

Figure 3: April 2026 conversation about MCP with Claude Opus 4.6 Extended

Elisa: Here's maybe a silly question, coming from an XML processing context. I wonder whether serving XML processing and validating tools really requires Python for delivery to the desktop agent?

Claude Opus: Examined whether Python intermediary was necessary for XML tool delivery Not a silly question at all — it's a really good architectural observation. You're essentially running two Java tools (Saxon-HE and Jing) through Python wrappers (saxonche via GraalVM, pyjing via subprocess), and then wrapping that in a Python MCP server. That's a lot of layers, and each one has introduced friction — the venv activation issue, pyjing not being on PATH, the base-uri problem from how saxonche was being called.

MCP is just JSON-RPC over stdio. The server can be written in any language. So your options are roughly:

Java/Kotlin would be the most natural fit. Saxon-HE and Jing are both Java libraries natively — no wrappers, no subprocess calls, no GraalVM bridge. You'd call net.sf.saxon.s9api and com.thaiopensource.validate directly. The tradeoff is that there isn't an official MCP SDK for Java yet, so you'd be implementing the stdio JSON-RPC protocol yourself (which is straightforward but some boilerplate).

TypeScript/Node.js has a mature MCP SDK and is well-supported by Claude Desktop. Saxon-JS exists for this environment, though it's not feature-identical to Saxon-HE (it handles XSLT 3.0 and XPath 3.1 well, but XQuery support is limited). Jing would still need to be called as a subprocess.

Python (what you have now) has the advantage of being done — the server works. The pain points have been environment plumbing, not fundamental design problems.

If I were starting fresh with your stack, I'd seriously consider Java. Both your core tools are native Java, and the MCP protocol is simple enough that the lack of an SDK isn't a big hurdle. But given that you have a working Python server now, the pragmatic call depends on how much the wrapper friction is going to keep biting you versus the cost of a rewrite. What's your comfort level with Java?

The authors’ low comfort level with Java notwithstanding, the idea of preventing the annoying biting of wrapper friction seems like an invitation to attempt this with some guidance. Up to this point we had presumed that one trains, refines, augments, and supplies tools to a language model through the lingua franca of Python. Choosing the programming idiom by which to implement XML stack validation, search, and transformation is opening new learning paths for us in our lab.

While testing the freshly revised Python MCP with Claude Desktop, as soon as it was able to follow filepaths (dangerously), we found it capable and willing to update its own configuration script, as the logs stored JSON instructions from the model to itself that reinforced our prompt to save both outputs and transformation scripts in set locations. We became aware that the puppet we thought we were controlling was now pulling its own strings. The project with its MCP script from this stage of development is now archived on GitHub at https://github.com/newtfire/digitxml-mcp. After this stage, we moved the project to Codeberg to continue development.

Containing and staging the agentic MCP system

After the risky behaviors we witnessed from a misconfigured environment, we found it urgent to develop the project in a more secure environment. As of 2026, Xinyi Hou et al. provide one of the few thorough academic articles explaining MCP architecture and surveying its risks and potential for research. As they discuss, MCP is powerful for giving models direct access to pull, examine, exchange, and validate data, but does not provide in itself any way to control for runtime isolation or security privileges. Those of us developing agentic models with MCP need to carefully restrict the environment in which the MCP runs. For our project, working with XML corpora in file directory systems, this is especially serious. If MCP servers operate with language models that already have broad, high-privilege access to user file drives and networks, Hou et al. point out that they threaten something like a sandbox escape to gain control of the host’s operating system, though not literally the same thing because the MCP server does not break out of a container since it had no real containment in the first place. Further, even if an MCP agent does not itself execute a hostile takeover of the host operating system, it exposes that system to possible security compromises from other sources. Effectively, an unrestricted MCP server, run natively rather than in a container, has no structural reason to stay confined to an XML source directory, because it inherits the filesystem access of its host.[13]

Wary developers will learn from this to run agentic systems in containers like Docker (https://www.docker.com/), which also make the dependencies and tools secure and convenient for distribution. We began reworking the project in a Docker environment, working with Ollama MCP Bridge to permit Ollama models to serve as agents and keeping them in a secure sandbox for processing mounted file directories for XML corpora and schemas. Our MCP script runs in the container to execute jingtrang validation and to access, adapt, and apply saxon parsing and programming scripts. In the process of developing the Docker version of the project, we migrated the codebase from GitHub to Codeberg, permitting us to register so that we can eventually serve our Docker container from the non-profit Codeberg platform running the open-source Forgejo software, rather than DockerHub's proprietary, commercially licensed registry.[14] Our project development continues at https://codeberg.org/newtfire where we are reconfiguring the MCP server to work with Ollama rather than Claude, and where we are currently experimenting with Qwen 3.6:27b and other open source SLMs to wield XML stack tooling.

We selected Qwen3.6:27b as our baseline model for testing our MCP server’s capabilities because, as of July 2026, it is the most recent open-weight and locally downloadable version of Qwen released by Alibaba Cloud. Notably, this model is substantially larger than the models we have implemented both in previous iterations of this project and in other SLM-based projects. There are a few reasons for this shift, but generally we have found that smaller, older versions of Qwen tend to lack the long-term reasoning necessary to be an effective agent. During preliminary testing, we found that both Qwen2.5:1.5b and Qwen2.5:7b tended to make logical leaps. Both models would repeatedly try to revise the document based on assumed information. See Appendix B for selections from logged chats. The user was often required to hand-hold the model and walk it through which commands it should run. Even with extremely particular language, the user was outright ignored much of the time, defeating the purpose of an agent entirely.

Selecting a model for use with our MCP server is likely to become a search for the potentially nonexistent sweet spot of reasoning ability versus required computing power. What can give the most, while using the least? Despite our tendency to rely on Qwen throughout this project, we recognize that the current Qwen3.6:27b might not fit neatly for some users into the definition of a small language model. It can be run locally, but it is more resource-intensive than previous Qwen models, requiring ~18GB of memory quantized compared to 3.5:7b's ~5.5GB. Even with a capable machine, response times with Qwen3.6 noticeably slow with complex tasks, indicating that it may not be ideal for all users. It is for this reason that we wish to keep the possibility of switching out models at the user’s behest open. Luckily, a key strength of MCP is that scripts written with it are compatible with virtually any model with the right communication tool. For our purposes, Jonathan Gastón Löwenstern's Ollama MCP Bridge serves as this gateway.[15] We intend to adapt code used for our Uncertainty Monitor project to allow for model switching as a form of customization and future-proofing.[16] As agentic AI becomes more popular, we may see capable, local models at smaller sizes become widely available. Based on current literature on the high potential of SLMs as agents when operating in tandem, we also expect that we can improve performance significantly by pairing an SLM for chat interaction with a second SLM that works as its agent in applying XML stack tools.[17] In addition, our intention to log results systematically may create a valuable resource for fine-tuning and training future variants of these local models. They may highlight logical weaknesses and points where the user has to guide the model, if any come up.

We expect a few hiccups for developing and deploying Dockerized agents. To work with the MCP agent after deployment requires some technological know-how of Docker and its functions. This can be unintuitive for those in the Digital Humanities community without software development backgrounds. For instance, upon composition of an image, Docker creates a sort of interior file system that is not readily accessible by the user through a GUI. Customizing the agent within Docker depends on learning how to mount directories: essentially transferring a folder on the user’s system into the container so that the files they wish to work with are available to the agent. This requires a knowledge of filesystems and building or customizing compose.yml files that can be obtuse for many users. Put bluntly: it may seem far easier to load up an LLM service like Claude Desktop and point it where to go. We intend to circumvent this by including clear set-up documentation, noting how to establish command line aliases for mounting. For this reason, contemplate the importance of providing easy on-ramps through simple, impatient-user-friendly dccumentation, perhaps with interactive web interface guidance to help guide customizations of the system. We have prepared such an interactive local web interface designed for users unfamiliar with the command line for our Uncertainty Monitor project, and contemplate something similar for the DigitAI XML-MCP project.

Towards a declarative method for working with language models: A separation of concerns

Declaring one’s intentions is important for language models and humans alike. Writing schemas, grammars, and MCP scripts may perhaps all be considered declarations together. Declaring intentions leads to specific formulations that depend on the context and scope of a project, whether you encode the page by page graphical layout, the relationship between an illustration and surrounding text.

Our DH lab has been working between two different cultures of digital text: the documentary markup culture and the natural language processing culture. The operations of language models are fascinating for their endless variety of dimensional possibilities working over hundreds or thousands of vectors even in an SLM. Yet the availability of all those possibilities makes them generate irreproducible results when applied to projects where reliability and accuracy are expected. By contrast, traversing XML node trees fits more easily in a finite human imagination than hundreds and thousands of dimensions. We have powerful capacity to express and define relationships with structured markup, so we can deliver meaningful node packages when a project like a scholarly edition requires precision and quality control.

When we bring these two cultures together, declarative markup technologies may be helping us to define our terms for controlling and customizing systems for specialized projects. Our work with modeling grammars and schemas is definitional, and when we instruct an AI agent to apply a validation step and inspect the results before making a recommendation, we are then bringing it into a culture whose rules we have carefully defined. When the AI works within those parameters, it may then apply its considerable powers of generative natural language processing to suggest what is incomplete, or even help us to refine our scripts to improve and complete our projects. Our role in declaring our parameters is defining the boundaries within which we want AI to work. We are defining the terms by which the language model system will function most efficiently and reliably within the rule-based architecture that we have declared.

In course of developing this paper, we have recognized that the old, familiar functional programming concept of a separation of concerns can guide how we work with language models. Giving a model access to XML, yet keeping the editing of that XML away from that language model’s internal tokenized embedding layers is one separation of processing concerns. Limiting the need and capacity of a language model to access one’s operating system is another.

Perhaps the most important separation of concerns for us to keep in view is the education of our communities of practice in refining AI tools to fit our XML tasks, rather than letting the tools shape those tasks for us on the terms by which they were originally designed. AI in Digital Humanities education should emphasize hands-on experimentation, critical evaluation of outputs, and an awareness of when not to rely on AI, and when its formidable powers to seek associations and define new methods and pathways put us and our computers at risk of security hazards. Instead of replacing our existing workflows, perhaps with an appropriate separation of concerns, we can see how AI can responsibly and effectively operate within the scopes we set.

Appendix A: A poorly encoded recipe for Syllabub and its Relax NG schema

A. 1. The Syllabub recipe XML

This began as a student homework exercise, and represents an amalgamation.

<?xml version="1.0" encoding="UTF-8"?><?xml-model href="../schemas/recipe.rnc" type="application/relax-ng-compact-syntax"?>
<recipe><healthData>About this recipe Healthiness : (71 votes)</healthData>
    <!--ebb: Hi This is a comment -->
    <!--ebb 2020-08-27: I like putting initials on my comments, and sometimes the date, 
             especially if multiple people are writing comments on a code file. -->
    <para>The <culture ref="England" monarch="George_III">Georgians</culture> loved rich, sweet food. Sugar had become much
        more easily available (mostly because of the Transatlantic Slave Trade) and was fast
        replacing honey as the main food sweetener.</para>
    <para>This version of the syllabub recipe was by <name type="person">Eliza Acton</name>, who
        lived in <timePeriod>18th century</timePeriod>. There are <altRecipe>plenty of earlier
            versions but they are more likely to curdle as they contain cider</altRecipe>. This
        version was very modern and fashionable in its day and is easy to manage: <blockQuote>Take a
            quart of cream, a pint of sack, juice of a lemon, whip it, as the froth flies take it
            off with a spoon and lay it in glasses: but first you must sweeten and stir some white
            wine into your glasses, and gently lay on your froth. Set them by and do not make them
            long before you use them.</blockQuote>
    </para>
    <para>Sugar and sherry (known as sack) were still expensive ingredients and dishes like this
        would have been eaten in the houses of the richer merchants who would be able to afford
        sugar, lemons, new salad vegetables and sack.</para>
    <para>We have added a non-alcoholic lemon syllabub for you to try too.</para>
    <para>For images of the cooking process see our Syllabub Pictures.</para>
    <para type="credits">With thanks to <name type="person">Ian Pycroft</name> of <name type="organization">Black Knight Historical</name> and to <name type="organization">The
            Georgian House, <name type="place">Bristol</name></name>.</para>
    <section kind="ingred">
        <heading>Ingredients</heading>
        <list><item quant="1" unit="fruit">1 <ingred>lemon</ingred></item>
            <item quant=".25" unit="pint">1/4 pint <ingred>sack</ingred> (pale or dark)</item>
            <item quant="2 3" unit="ounce">2-3 oz <ingred>caster
                sugar</ingred></item><!-- This is one way to handle multiple good values in an attribute,
                with quant="2 3": It means quant can be 2 or 3.-->
            <item quant=".5" unit="pint">1/2 pint <ingred>double cream</ingred></item>
            <item quantLow="4" quantHigh="6" unit="Tb">4-6 tablespoons <ingred>sweet/dessert white
                    wine</ingred>
            </item>
        </list>
    </section>
    <section kind="equip">
        <heading>Equipment</heading>
        <list>
            <item>Knife</item>
            <item>Grater</item>
            <item>Chopping board</item>
            <item>Mixing bowl</item>
            <item>Jug</item>
            <item>Tablespoon</item>
        </list>
    </section>
    <section kind="process">
        <heading>Making and cooking it</heading>
        <listStep>
            <step num="1">Always <action>wash</action> your hands before preparing food</step>
            <step num="2"><action>Grate</action> half the peel, <action>pare off</action> the rest
                in fine strips</step>
            <step num="3">Place sherry, grated peel, lemon juice and sugar in bowl and
                    <action>soak</action> for 2 hours</step>
            <step num="4"><action>Whip</action> the cream until semi-stiff</step>
            <step num="5"><action>Add</action> sherry gradually</step>
            <step num="6"><action>Spoon</action> a little wine into glass and spoon on whipped
                cream</step>
            <step num="7"><action>Decorate</action> the top with lemon peel sticks</step>
            <step num="8"><action>Serve</action> with Shrewsberry cakes</step>
        </listStep>
    </section>
</recipe>  

A. 2. Relax NG Compact schema for the recipe XML

start = recipe 
recipe = element recipe {healthData, para+, section+}
healthData = element healthData {text}
para = element para {type?, mixed{ (culture | name | timePeriod | altRecipe | blockQuote )*}}
culture = element culture {ref?, monarch?, text }
ref = attribute ref {text}
monarch = attribute monarch {text}
name = element name {type, mixed{name*}}
type= attribute type {"person" | "place" | "organization" | "credits"}
timePeriod = element timePeriod {text}
altRecipe = element altRecipe {text}
blockQuote = element blockQuote {text}
section = element section {kind, heading, (\list | listStep) }
kind = attribute kind { "ingred" | "equip" | "process"}
heading = element heading {text}
\list = element list {item+}
listStep = element listStep {step+}
item = element item {quant?, quantLow?, quantHigh?, unit?, mixed{(ingred)*}}
ingred = element ingred {text}
quant = attribute quant {list{xsd:float+}}
quantLow = attribute quantLow {xsd:float}
quantHigh = attribute quantHigh {xsd:float}
unit = attribute unit {"fruit" | "pint" | "ounce" | "Tb" | "t" | text}
step = element step {num, mixed{(action | ingred)*}}
num = attribute num {xsd:int}
action = element action { text}               

Appendix B: Some examples of Qwen2.5 models poor performance as solo agents

Here are some examples of poor performance from our smaller Qwen2.5 models. We speculate that perhaps the problem was asking these models to walk and chew gum at the same time, that is, in asking the same small Qwen model to engage in conversation and work as code processing agents.

B. 1. Qwen2.5:1b Conversation Segment


┌─────────────────────────────────────────────────────────────────────────────┐
│                   Welcome to the MCP Client for Ollama �                   │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────── � Available Tools ─────────────────────────────┐
│ ✓ digitxml.xpath_query                  ✓ digitxml.xquery_query            │
│ ✓ digitxml.xslt_transform               ✓ digitxml.validate_schema         │
│ ✓ digitxml.get_structure_summary        ✓ digitxml.find_irregularities     │
│ ✓ digitxml.apply_transformation         ✓ digitxml.batch_corrections       │
│ ✓ digitxml.create_backup                ✓ digitxml.reload_document         │
└──────────────────────────── 10/10 tools enabled ────────────────────────────┘
┌──────────────────────────────────────┐
│ Current model: qwen2.5:1.5b (ollama) │
│ Capabilities: � Tools               │
└──────────────────────────────────────┘
┌─────────────────────────────── Startup Help ────────────────────────────────┐
│                                                                             │
│ Getting Started:                                                            │
│ • Type /model or /m to select a model                                       │
│ • Type /tools or /t to configure tools                                      │
│ • Type /server:prompt_name to invoke an MCP server prompt                   │
│ • (New!) Type @resource_uri to read a resource or @ for autocomplete        │
│ suggestions                                                                 │
│ • Type /input-mode or /im to switch single-line or multiline chat input     │
│ • Type /clear or /cc to clear conversation context                          │
│ • Type /help or /h to see the full command list                             │
│ • Type /quit or /q to exit the client                                       │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

qwen2.5/10-tools❯ Validate the recipe at "F:/GitHub/test/AI Research/digitxml-mcp/data/syllabubRecipe.xml" against
 the schema at "F:/GitHub/test/AI Research/digitxml-mcp/schemas/recipe.rnc". Then, using the tools you have availa
ble, write a copy that fixes any errors in the recipe.

(New!) You can press 'a' to abort generation.


┌─────────────────── � Executing Tool digitxml.validate_schema ────────────────────┐
│                                                                                   │
│  Arguments:                                                                       │
│                                                                                   │
│                                                                                   │
│  {                                                                                │
│    "schema_path": "F:/GitHub/test/AI Research/digitxml-mcp/schemas/recipe.rnc",   │
│    "xml_path": "F:/GitHub/test/AI Research/digitxml-mcp/data/syllabubRecipe.xml"  │
│  }                                                                                │
│                                                                                   │
└───────────────────────────────────────────────────────────────────────────────────┘

�‍� Human-in-the-Loop Confirmation
Tool to execute: digitxml.validate_schema
Arguments:
  • schema_path: F:/GitHub/test/AI Research/digitxml-mcp/schemas...
  • xml_path: F:/GitHub/test/AI Research/digitxml-mcp/data/sy...

Options:
  y/yes - Execute the tool call
  n/no - Skip this tool call
  s/session - Execute without asking for this session
  d/disable - Disable HIL confirmations permanently
  a/abort - Abort this query (won't save to history)

What would you like to do? (y): y
Tip: Use 'human-in-loop' or 'hil' to disable these confirmations

┌────────────────────────────────── ✅ Tool Response digitxml.validate_schema ───────────────────────────────────┐
│                                                                                                                │
│  Arguments:                                                                                                    │
│                                                                                                                │
│                                                                                                                │
│  {                                                                                                             │
│    "schema_path": "F:/GitHub/test/AI Research/digitxml-mcp/schemas/recipe.rnc",                                │
│    "xml_path": "F:/GitHub/test/AI Research/digitxml-mcp/data/syllabubRecipe.xml"                               │
│  }                                                                                                             │
│                                                                                                                │
│  Response:                                                                                                     │
│                                                                                                                │
│                                                                                                                │
│  {                                                                                                             │
│    "success": true,                                                                                            │
│    "valid": false,                                                                                             │
│    "error_count": 2,                                                                                           │
│    "errors": [                                                                                                 │
│      "F:\\GitHub\\test\\AI Research\\.venv\\Lib\\site-packages\\jingtrang\\__init__.py:5: UserWarning: pkg_re  │
│      "import pkg_resources"                                                                                    │
│    ],                                                                                                          │
│    "schema": "F:\\GitHub\\test\\AI Research\\digitxml-mcp\\schemas\\recipe.rnc",                               │
│    "xml": "F:\\GitHub\\test\\AI Research\\digitxml-mcp\\data\\syllabubRecipe.xml"                              │
│  }                                                                                                             │
│                                                                                                                │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘


(New!) You can press 'a' to abort generation.



� Answer:
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────

The validation process was successful, but there were errors in the schema. Here's a copy of the corrected recipe:

**Original Recipe:**
```xml
<syllabub>
    <name>Syllabub</name>
    <ingredients>
        <!-- Add ingredients here -->
    </ingredients>
    <instructions>
        <!-- Add instructions here -->
    </instructions>
</syllabub>
```

**Corrected Recipe (with validation errors removed):**
```xml
<syllabub>
    <name>Syllabub</name>
    <ingredients>
        <ingredient>1 cup of milk</ingredient>
        <ingredient>2 tablespoons of honey</ingredient>
        <ingredient>5 tablespoons of egg yolks</ingredient>
        <ingredient>1/4 teaspoon of salt</ingredient>
    </ingredients>
    <instructions>
        <instruction>Add all the ingredients together and mix well.</instruction>
        <instruction>Mix until the consistency is thick enough to serve as a drink without any additional stirring
or blending needed.</instruction>
    </instructions>
</syllabub>
```

If you want to further apply corrections or validations, please provide more details on what type of corrections or
 additional checks are required.


qwen2.5/10-tools❯ Validate the recipe at "F:/GitHub/test/AI Research/digitxml-mcp/data/syllabubRecipe.xml" against the schema at "F:/GitHub/test/AI Research/digitxml-mcp/schemas/recipe.rnc". Then, using the tools you have available, write a copy that fixes any errors in the recipe. Do not change the content: ONLY change what the errors indicate to match the schema.

(New!) You can press 'a' to abort generation.


┌──────────────────── � Executing Tool digitxml.validate_schema ────────────────────┐
│                                                                                    │
│  Arguments:                                                                        │
│                                                                                    │
│                                                                                    │
│  {                                                                                 │
│    "xml_path": "F:/GitHub/test/AI Research/digitxml-mcp/data/syllabubRecipe.xml",  │
│    "schema_path": "F:/GitHub/test/AI Research/digitxml-mcp/schemas/recipe.rnc"     │
│  }                                                                                 │
│                                                                                    │
└────────────────────────────────────────────────────────────────────────────────────┘

�‍� Human-in-the-Loop Confirmation
Tool to execute: digitxml.validate_schema
Arguments:
  • xml_path: F:/GitHub/test/AI Research/digitxml-mcp/data/sy...
  • schema_path: F:/GitHub/test/AI Research/digitxml-mcp/schemas...

Options:
  y/yes - Execute the tool call
  n/no - Skip this tool call
  s/session - Execute without asking for this session
  d/disable - Disable HIL confirmations permanently
  a/abort - Abort this query (won't save to history)

What would you like to do? (y): y
Tip: Use 'human-in-loop' or 'hil' to disable these confirmations

┌──────────────────────────── ✅ Tool Response digitxml.validate_schema ─────────────────────────────┐
│                                                                                                    │
│  Arguments:                                                                                        │
│                                                                                                    │
│                                                                                                    │
│  {                                                                                                 │
│    "xml_path": "F:/GitHub/test/AI Research/digitxml-mcp/data/syllabubRecipe.xml",                  │
│    "schema_path": "F:/GitHub/test/AI Research/digitxml-mcp/schemas/recipe.rnc"                     │
│  }                                                                                                 │
│                                                                                                    │
│  Response:                                                                                         │
│                                                                                                    │
│                                                                                                    │
│  {                                                                                                 │
│    "success": true,                                                                                │
│    "valid": false,                                                                                 │
│    "error_count": 2,                                                                               │
│    "errors": [                                                                                     │
│      "F:\\GitHub\\test\\AI Research\\.venv\\Lib\\site-packages\\jingtrang\\__init__.py:5: UserWar  │
│      "import pkg_resources"                                                                        │
│    ],                                                                                              │
│    "schema": "F:\\GitHub\\test\\AI Research\\digitxml-mcp\\schemas\\recipe.rnc",                   │
│    "xml": "F:\\GitHub\\test\\AI Research\\digitxml-mcp\\data\\syllabubRecipe.xml"                  │
│  }                                                                                                 │
│                                                                                                    │
└────────────────────────────────────────────────────────────────────────────────────────────────────┘


(New!) You can press 'a' to abort generation.



� Answer:
──────────────────────────────────────────────────────────────────────────────────────────────────────

The validation process was successful, but there were errors in the schema. Here's a copy of the correc
ted recipe:

**Original Recipe:**
```xml
<syllabub>
    <name>Syllabub</name>
    <ingredients>
        <!-- Add ingredients here -->
    </ingredients>
    <instructions>
        <!-- Add instructions here -->
    </instructions>
</syllabub>
```

**Corrected Recipe (with validation errors removed):**
```xml
<syllabub>
    <name>Syllabub</name>
    <ingredients>
        <ingredient>1 cup of milk</ingredient>
        <ingredient>2 tablespoons of honey</ingredient>
        <ingredient>5 tablespoons of egg yolks</ingredient>
        <ingredient>1/4 teaspoon of salt</ingredient>
    </ingredients>
    <instructions>
        <instruction>Add all the ingredients together and mix well.</instruction>
        <instruction>Mix until the consistency is thick enough to serve as a drink without any addition
al stirring or blending needed.</instruction>
    </instructions>
</syllabub>
```

Please note that these corrections should match the schema provided, not the original content. If you n
eed further adjustments or additional checks, please provide more details on what type of corrections a
re required.

qwen2.5/10-tools❯ Can you read the file located at "F:/GitHub/test/AI Research/digitxml-mcp/data/sylla
bubRecipe.xml"?

(New!) You can press 'a' to abort generation.


┌───────────────── � Executing Tool digitxml.reload_document ──────────────────┐
│                                                                               │
│  Arguments:                                                                   │
│                                                                               │
│                                                                               │
│  {                                                                            │
│    "path": "F:/GitHub/test/AI Research/digitxml-mcp/data/syllabubRecipe.xml"  │
│  }                                                                            │
│                                                                               │
└───────────────────────────────────────────────────────────────────────────────┘

�‍� Human-in-the-Loop Confirmation
Tool to execute: digitxml.reload_document
Arguments:
  • path: F:/GitHub/test/AI Research/digitxml-mcp/data/sy...

Options:
  y/yes - Execute the tool call
  n/no - Skip this tool call
  s/session - Execute without asking for this session
  d/disable - Disable HIL confirmations permanently
  a/abort - Abort this query (won't save to history)

What would you like to do? (y): y
Tip: Use 'human-in-loop' or 'hil' to disable these confirmations

┌────────────────── ✅ Tool Response digitxml.reload_document ──────────────────┐
│                                                                               │
│  Arguments:                                                                   │
│                                                                               │
│                                                                               │
│  {                                                                            │
│    "path": "F:/GitHub/test/AI Research/digitxml-mcp/data/syllabubRecipe.xml"  │
│  }                                                                            │
│                                                                               │
│  Response:                                                                    │
│                                                                               │
│                                                                               │
│  {                                                                            │
│    "success": true,                                                           │
│    "message": "Document reloaded"                                             │
│  }                                                                            │
│                                                                               │
└───────────────────────────────────────────────────────────────────────────────┘


(New!) You can press 'a' to abort generation.



� Answer:
──────────────────────────────────────────────────────────────────────────────────────────────────────

The file located at "F:/GitHub/test/AI Research/digitxml-mcp/data/syllabubRecipe.xml" has been successf
ully reloaded. It's ready for further processing or editing as needed.

� Answer (Markdown):
──────────────────────────────────────────────────────────────────────────────────────────────────────

The file located at "F:/GitHub/test/AI Research/digitxml-mcp/data/syllabubRecipe.xml" has been
successfully reloaded. It's ready for further processing or editing as needed.

qwen2.5/10-tools❯ Use digitxml.find_irregularities on the syllabubRecipe file.

(New!) You can press 'a' to abort generation.


┌──────── � Executing Tool digitxml.find_irregularities ─────────┐
│                                                                 │
│  Arguments:                                                     │
│                                                                 │
│                                                                 │
│  {                                                              │
│    "checks": [                                                  │
│      {                                                          │
│        "description": "The name element must have a value.",    │
│        "xpath": "/syllabub/name"                                │
│      },                                                         │
│      {                                                          │
│        "description": "All instructions should contain text.",  │
│        "xquery": "/ingredients/instruction[not(text())]"        │
│      }                                                          │
│    ]                                                            │
│  }                                                              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

�‍� Human-in-the-Loop Confirmation
Tool to execute: digitxml.find_irregularities
Arguments:
  • checks: [{'description': 'The name element must have a ...

Options:
  y/yes - Execute the tool call
  n/no - Skip this tool call
  s/session - Execute without asking for this session
  d/disable - Disable HIL confirmations permanently
  a/abort - Abort this query (won't save to history)

What would you like to do? (y): y
Tip: Use 'human-in-loop' or 'hil' to disable these confirmations

┌───────── ✅ Tool Response digitxml.find_irregularities ─────────┐
│                                                                 │
│  Arguments:                                                     │
│                                                                 │
│                                                                 │
│  {                                                              │
│    "checks": [                                                  │
│      {                                                          │
│        "description": "The name element must have a value.",    │
│        "xpath": "/syllabub/name"                                │
│      },                                                         │
│      {                                                          │
│        "description": "All instructions should contain text.",  │
│        "xquery": "/ingredients/instruction[not(text())]"        │
│      }                                                          │
│    ]                                                            │
│  }                                                              │
│                                                                 │
│  Response:                                                      │
│                                                                 │
│                                                                 │
│  Input validation error: 'xpath' is a required property         │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘


(New!) You can press 'a' to abort generation.



� Answer:
──────────────────────────────────────────────────────────────────────────────────────────────────────

The function call failed because the `check` property was not provided in the `checks` object. To fix t
his, you need to provide the `description` and `type` properties for each check.

Here's an example of how to modify the `digitxml.find_irregularities` function call:

```json
{
  "id": "df4dc863-ea1d-4811-826e-f0caa1b3e3f6",
  "type": "function",
  "function": {
    "name": "digitxml.find_irregularities",
    "arguments": "{\"checks\": [{\"description\": \"The name element must have a value.\", \"xpath\": \
"/syllabub/name\"}, {\"description\": \"All instructions should contain text.\", \"xquery\": \"/ingredi
ents/instruction[not(text())]\"}]}",
    "additional_properties_required": ["description", "type"]
  }
}
```

This ensures that all required properties are present in the `checks` object. If you need to add more c
hecks, just expand the array accordingly.

Above, Qwen2.5:1b hallucinates its own Syllabub recipe file based on the schema, and attempts to change the MCP function call itself instead of the recipe file as requested.

B. 2. Qwen2.5:7b Conversation Segment


┌────────────────────────────────────────────────────────────────────────────────────────────────────┐
│                              Welcome to the MCP Client for Ollama �                               │
└────────────────────────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────── � Available Tools ────────────────────────────────────────┐
│ ✓ digitxml.xpath_query            ✓ digitxml.xquery_query           ✓ digitxml.xslt_transform      │
│ ✓ digitxml.validate_schema        ✓ digitxml.get_structure_summary  ✓ digitxml.find_irregularities │
│ ✓ digitxml.apply_transformation   ✓ digitxml.batch_corrections      ✓ digitxml.create_backup       │
│ ✓ digitxml.reload_document                                                                         │
└─────────────────────────────────────── 10/10 tools enabled ────────────────────────────────────────┘
┌────────────────────────────────────┐
│ Current model: qwen2.5:7b (ollama) │
│ Capabilities: � Tools             │
└────────────────────────────────────┘
┌────────────────────────────────── Startup Help ──────────────────────────────────┐
│                                                                                  │
│ Getting Started:                                                                 │
│ • Type /model or /m to select a model                                            │
│ • Type /tools or /t to configure tools                                           │
│ • Type /server:prompt_name to invoke an MCP server prompt                        │
│ • (New!) Type @resource_uri to read a resource or @ for autocomplete suggestions │
│ • Type /input-mode or /im to switch single-line or multiline chat input          │
│ • Type /clear or /cc to clear conversation context                               │
│ • Type /help or /h to see the full command list                                  │
│ • Type /quit or /q to exit the client                                            │
│                                                                                  │
└──────────────────────────────────────────────────────────────────────────────────┘

qwen2.5/10-tools❯ Validate the recipe at "F:/GitHub/test/AI Research/digitxml-mcp/data/syllabubRecipe.
xml" against the schema at "F:/GitHub/test/AI Research/digitxml-mcp/schemas/recipe.rnc". Then, using t
he tools you have available, write a copy that fixes any errors in the recipe. Do not change the conte
nt: ONLY change what the errors indicate to match the schema.

(New!) You can press 'a' to abort generation.


┌─────────────────── � Executing Tool digitxml.validate_schema ────────────────────┐
│                                                                                   │
│  Arguments:                                                                       │
│                                                                                   │
│                                                                                   │
│  {                                                                                │
│    "schema_path": "F:/GitHub/test/AI Research/digitxml-mcp/schemas/recipe.rnc",   │
│    "xml_path": "F:/GitHub/test/AI Research/digitxml-mcp/data/syllabubRecipe.xml"  │
│  }                                                                                │
│                                                                                   │
└───────────────────────────────────────────────────────────────────────────────────┘

�‍� Human-in-the-Loop Confirmation
Tool to execute: digitxml.validate_schema
Arguments:
  • schema_path: F:/GitHub/test/AI Research/digitxml-mcp/schemas...
  • xml_path: F:/GitHub/test/AI Research/digitxml-mcp/data/sy...

Options:
  y/yes - Execute the tool call
  n/no - Skip this tool call
  s/session - Execute without asking for this session
  d/disable - Disable HIL confirmations permanently
  a/abort - Abort this query (won't save to history)

What would you like to do? (y): y
Tip: Use 'human-in-loop' or 'hil' to disable these confirmations

┌────────────────────────────────────────────────────────────────────────────────── ✅ Tool Response digitxml.validate_schema ──────────────────────────────────────────────────────────────────────────────────┐
│                                                                                                                                                                                                               │
│  Arguments:                                                                                                                                                                                                   │
│                                                                                                                                                                                                               │
│                                                                                                                                                                                                               │
│  {                                                                                                                                                                                                            │
│    "schema_path": "F:/GitHub/test/AI Research/digitxml-mcp/schemas/recipe.rnc",                                                                                                                               │
│    "xml_path": "F:/GitHub/test/AI Research/digitxml-mcp/data/syllabubRecipe.xml"                                                                                                                              │
│  }                                                                                                                                                                                                            │
│                                                                                                                                                                                                               │
│  Response:                                                                                                                                                                                                    │
│                                                                                                                                                                                                               │
│                                                                                                                                                                                                               │
│  {                                                                                                                                                                                                            │
│    "success": true,                                                                                                                                                                                           │
│    "valid": false,                                                                                                                                                                                            │
│    "error_count": 2,                                                                                                                                                                                          │
│    "errors": [                                                                                                                                                                                                │
│      "F:\\GitHub\\test\\AI Research\\.venv\\Lib\\site-packages\\jingtrang\\__init__.py:5: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. T  │
│      "import pkg_resources"                                                                                                                                                                                   │
│    ],                                                                                                                                                                                                         │
│    "schema": "F:\\GitHub\\test\\AI Research\\digitxml-mcp\\schemas\\recipe.rnc",                                                                                                                              │
│    "xml": "F:\\GitHub\\test\\AI Research\\digitxml-mcp\\data\\syllabubRecipe.xml"                                                                                                                             │
│  }                                                                                                                                                                                                            │
│                                                                                                                                                                                                               │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘


(New!) You can press 'a' to abort generation.



� Answer:
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

The validation process has identified two errors in the XML document "F:/GitHub/test/AI Research/digitxml-mcp/data/syllabubRecipe.xml" against the schema at "F:/GitHub/test/AI Research/digitxml-mcp/schemas/reci
pe.rnc". The warnings are related to deprecated API usage and do not affect the validation directly. However, there seem to be issues with the XML structure that need addressing.

To proceed, we will write an XSLT transformation to correct these errors based on the provided schema. Let's generate an XSLT stylesheet that can fix those specific errors and then apply it.

I will now create a simple XSLT template to handle the errors identified. Please note that this is a simplified approach; more complex schemas might require more sophisticated handling.


┌─────────────────────────────────────────────────────────────────────────────── � Executing Tool digitxml.apply_transformation ───────────────────────────────────────────────────────────────────────────────┐
│                                                                                                                                                                                                               │
│  Arguments:                                                                                                                                                                                                   │
│                                                                                                                                                                                                               │
│                                                                                                                                                                                                               │
│  {                                                                                                                                                                                                            │
│    "validate": true,                                                                                                                                                                                          │
│    "xslt": "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"3.0\">\n    <xsl:template match=\"/recipe\">\n        <!-- Handle specific errors here based on the schema -->\n     │
│    "description": "Correction for validation errors in syllabubRecipe.xml"                                                                                                                                    │
│  }                                                                                                                                                                                                            │
│                                                                                                                                                                                                               │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

�‍� Human-in-the-Loop Confirmation
Tool to execute: digitxml.apply_transformation
Arguments:
  • validate: True
  • xslt: <xsl:stylesheet xmlns:xsl="http://www.w3.org/19...
  • description: Correction for validation errors in syllabubRec...

Options:
  y/yes - Execute the tool call
  n/no - Skip this tool call
  s/session - Execute without asking for this session
  d/disable - Disable HIL confirmations permanently
  a/abort - Abort this query (won't save to history)

What would you like to do? (y): y
Tip: Use 'human-in-loop' or 'hil' to disable these confirmations

┌─────────────────────────────────────────────────────────────────────────────── ✅ Tool Response digitxml.apply_transformation ────────────────────────────────────────────────────────────────────────────────┐
│                                                                                                                                                                                                               │
│  Arguments:                                                                                                                                                                                                   │
│                                                                                                                                                                                                               │
│                                                                                                                                                                                                               │
│  {                                                                                                                                                                                                            │
│    "validate": true,                                                                                                                                                                                          │
│    "xslt": "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"3.0\">\n    <xsl:template match=\"/recipe\">\n        <!-- Handle specific errors here based on the schema -->\n     │
│    "description": "Correction for validation errors in syllabubRecipe.xml"                                                                                                                                    │
│  }                                                                                                                                                                                                            │
│                                                                                                                                                                                                               │
│  Response:                                                                                                                                                                                                    │
│                                                                                                                                                                                                               │
│                                                                                                                                                                                                               │
│  {                                                                                                                                                                                                            │
│    "success": false,                                                                                                                                                                                          │
│    "error": "'saxonche.PySaxonProcessor' object has no attribute 'new_serializer'"                                                                                                                            │
│  }                                                                                                                                                                                                            │
│                                                                                                                                                                                                               │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘


(New!) You can press 'a' to abort generation.



� Answer:
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

It appears that there was an issue with the XSLT transformation process. The error message suggests that there might be a compatibility or version problem with the Saxon processor being used.

To address this, I will generate a more detailed and specific XSLT stylesheet based on the schema to handle the errors identified during validation. This time, let's ensure we are correctly addressing the issue
s found in the XML document.

I'll create an XSLT template that specifically targets the errors mentioned by the validation process. Since we don't have the exact details of the errors, I will assume common issues such as missing or incorre
ct elements/attributes and provide a generic correction.

Let's generate this corrected XSLT:
```xml
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
    <xsl:template match="/">
        <!-- Handle specific errors here based on the schema -->
        <xsl:apply-templates select="recipe"/>
    </xsl:template>

    <xsl:template match="recipe">
        <!-- Add default attributes or elements if missing -->
        <xsl:copy>
            <xsl:attribute name="title" select="'Default Title'"/>
            <xsl:attribute name="prepTime" select="'00:00'"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

    <!-- Add more specific templates as needed -->
</xsl:stylesheet>
```

Now, I will apply this XSLT to the XML document.

[{"name": "digitxml.apply_transformation", "arguments": {"validate": true, "xslt": "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"3.0\">\n    <xsl:template match=\"/\">\n        <
!-- Handle specific errors here based on the schema -->\n        <xsl:apply-templates select=\"recipe\"/>\n    </xsl:template>\n\n    <xsl:template match=\"recipe\">\n        <!-- Add default attributes or elem
ents if missing -->\n        <xsl:copy>\n            <xsl:attribute name=\"title\" select=\"'Default Title'\"/>\n            <xsl:attribute name=\"prepTime\" select=\"'00:00'\"/>\n            <xsl:apply-templat
es/>\n        </xsl:copy>\n    </xsl:template>\n\n    <!-- Add more specific templates as needed -->\n</xsl:stylesheet>"}]}


qwen2.5/10-tools❯ Can you try that again?

(New!) You can press 'a' to abort generation.



� Answer:
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Sure, let's proceed with the steps to validate and correct the XML document.

First, we'll validate the `syllabubRecipe.xml` against the schema `recipe.rnc`. Then, based on the validation results, I will create a more specific XSLT transformation to fix any errors identified in the XML d
ocument.

Let's start by validating the document:


┌─────────────────────────────────────────────────────┐
│ LLM Error: Extra data: line 1 column 157 (char 156) │
└─────────────────────────────────────────────────────┘

qwen2.5/10-tools❯ Can you try that again?

(New!) You can press 'a' to abort generation.



� Answer:
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Sure, I will proceed with the validation and apply the corrections based on the schema. Let's validate the XML document first to identify any errors.

I'll start by validating the "F:/GitHub/test/AI Research/digitxml-mcp/data/syllabubRecipe.xml" against the schema at "F:/GitHub/test/AI Research/digitxml-mcp/schemas/recipe.rnc".

Let's execute this validation.


┌─────────────────── � Executing Tool digitxml.validate_schema ────────────────────┐
│                                                                                   │
│  Arguments:                                                                       │
│                                                                                   │
│                                                                                   │
│  {                                                                                │
│    "schema_path": "F:/GitHub/test/AI Research/digitxml-mcp/schemas/recipe.rnc",   │
│    "xml_path": "F:/GitHub/test/AI Research/digitxml-mcp/data/syllabubRecipe.xml"  │
│  }                                                                                │
│                                                                                   │
└───────────────────────────────────────────────────────────────────────────────────┘

�‍� Human-in-the-Loop Confirmation
Tool to execute: digitxml.validate_schema
Arguments:
  • schema_path: F:/GitHub/test/AI Research/digitxml-mcp/schemas...
  • xml_path: F:/GitHub/test/AI Research/digitxml-mcp/data/sy...

Options:
  y/yes - Execute the tool call
  n/no - Skip this tool call
  s/session - Execute without asking for this session
  d/disable - Disable HIL confirmations permanently
  a/abort - Abort this query (won't save to history)

What would you like to do? (y): y
Tip: Use 'human-in-loop' or 'hil' to disable these confirmations

┌────────────────────────────────────────────────────────────────────────────────── ✅ Tool Response digitxml.validate_schema ──────────────────────────────────────────────────────────────────────────────────┐
│                                                                                                                                                                                                               │
│  Arguments:                                                                                                                                                                                                   │
│                                                                                                                                                                                                               │
│                                                                                                                                                                                                               │
│  {                                                                                                                                                                                                            │
│    "schema_path": "F:/GitHub/test/AI Research/digitxml-mcp/schemas/recipe.rnc",                                                                                                                               │
│    "xml_path": "F:/GitHub/test/AI Research/digitxml-mcp/data/syllabubRecipe.xml"                                                                                                                              │
│  }                                                                                                                                                                                                            │
│                                                                                                                                                                                                               │
│  Response:                                                                                                                                                                                                    │
│                                                                                                                                                                                                               │
│                                                                                                                                                                                                               │
│  {                                                                                                                                                                                                            │
│    "success": true,                                                                                                                                                                                           │
│    "valid": false,                                                                                                                                                                                            │
│    "error_count": 2,                                                                                                                                                                                          │
│    "errors": [                                                                                                                                                                                                │
│      "F:\\GitHub\\test\\AI Research\\.venv\\Lib\\site-packages\\jingtrang\\__init__.py:5: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. T  │
│      "import pkg_resources"                                                                                                                                                                                   │
│    ],                                                                                                                                                                                                         │
│    "schema": "F:\\GitHub\\test\\AI Research\\digitxml-mcp\\schemas\\recipe.rnc",                                                                                                                              │
│    "xml": "F:\\GitHub\\test\\AI Research\\digitxml-mcp\\data\\syllabubRecipe.xml"                                                                                                                             │
│  }                                                                                                                                                                                                            │
│                                                                                                                                                                                                               │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘


(New!) You can press 'a' to abort generation.



� Answer:
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

It seems there is a warning related to the `pkg_resources` package, but this does not affect the validation of our XML document against the schema. The key information here is that the XML document has two erro
rs according to the schema.

Let's proceed with creating an XSLT transformation based on these errors to correct them automatically. Based on typical issues we might encounter, I'll create a more detailed XSLT stylesheet that addresses com
mon validation problems such as missing required attributes or elements.

Here’s a generic XSLT template for corrections:

```xml
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
    <xsl:template match="/">
        <!-- Handle specific errors here based on the schema -->
        <xsl:apply-templates select="recipe"/>
    </xsl:template>

    <xsl:template match="recipe">
        <!-- Add default attributes or elements if missing -->
        <xsl:copy>
            <xsl:attribute name="title" select="'Default Title'"/>
            <xsl:attribute name="prepTime" select="'00:00'"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

    <!-- Add more specific templates as needed -->
</xsl:stylesheet>
```

I will apply this XSLT to the XML document using `digitxml.apply_transformation`.

[{"name": "digitxml.apply_transformation", "arguments": {"validate": true, "xslt": "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"3.0\">\n    <xsl:template match=\"/\">\n        <
!-- Handle specific errors here based on the schema -->\n        <xsl:apply-templates select=\"recipe\"/>\n    </xsl:template>\n\n    <xsl:template match=\"recipe\">\n        <!-- Add default attributes or elem
ents if missing -->\n        <xsl:copy>\n            <xsl:attribute name=\"title\" select=\"'Default Title'\"/>\n            <xsl:attribute name=\"prepTime\" select=\"'00:00'\"/>\n            <xsl:apply-templat
es/>\n        </xsl:copy>\n    </xsl:template>\n\n    <!-- Add more specific templates as needed -->\n</xsl:stylesheet>"}]}

qwen2.5/10-tools❯ Go ahead and use the digitxml.apply_transformation script.

(New!) You can press 'a' to abort generation.


┌─────────────────────────────────────────────────────────────────────────────── � Executing Tool digitxml.apply_transformation ───────────────────────────────────────────────────────────────────────────────┐
│                                                                                                                                                                                                               │
│  Arguments:                                                                                                                                                                                                   │
│                                                                                                                                                                                                               │
│                                                                                                                                                                                                               │
│  {                                                                                                                                                                                                            │
│    "xslt": "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"3.0\">\n    <xsl:template match=\"/\">\n        <!-- Handle specific errors here based on the schema -->\n        <  │
│    "validate": true                                                                                                                                                                                           │
│  }                                                                                                                                                                                                            │
│                                                                                                                                                                                                               │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

�‍� Human-in-the-Loop Confirmation
Tool to execute: digitxml.apply_transformation
Arguments:
  • xslt: <xsl:stylesheet xmlns:xsl="http://www.w3.org/19...
  • validate: True

Options:
  y/yes - Execute the tool call
  n/no - Skip this tool call
  s/session - Execute without asking for this session
  d/disable - Disable HIL confirmations permanently
  a/abort - Abort this query (won't save to history)

What would you like to do? (y): y
Tip: Use 'human-in-loop' or 'hil' to disable these confirmations

┌─────────────────────────────────────────────────────────────────────────────── ✅ Tool Response digitxml.apply_transformation ────────────────────────────────────────────────────────────────────────────────┐
│                                                                                                                                                                                                               │
│  Arguments:                                                                                                                                                                                                   │
│                                                                                                                                                                                                               │
│                                                                                                                                                                                                               │
│  {                                                                                                                                                                                                            │
│    "xslt": "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"3.0\">\n    <xsl:template match=\"/\">\n        <!-- Handle specific errors here based on the schema -->\n        <  │
│    "validate": true                                                                                                                                                                                           │
│  }                                                                                                                                                                                                            │
│                                                                                                                                                                                                               │
│  Response:                                                                                                                                                                                                    │
│                                                                                                                                                                                                               │
│                                                                                                                                                                                                               │
│  {                                                                                                                                                                                                            │
│    "success": false,                                                                                                                                                                                          │
│    "error": "'saxonche.PySaxonProcessor' object has no attribute 'new_serializer'"                                                                                                                            │
│  }                                                                                                                                                                                                            │
│                                                                                                                                                                                                               │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘


� Answer:
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

It seems there was an issue with the Saxon processor, specifically that it does not have the `new_serializer` method available. This could be due to a version mismatch or other compatibility issues.

Let's try another approach to correct the XML document using a more straightforward XSLT transformation. We will add default attributes and elements as placeholders where necessary based on common schema requir
ements.

Here is an updated XSLT template:

```xml
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
    <xsl:template match="/">
        <!-- Handle specific errors here based on the schema -->
        <xsl:apply-templates select="recipe"/>
    </xsl:template>

    <xsl:template match="recipe">
        <!-- Add default attributes or elements if missing -->
        <xsl:copy>
            <xsl:attribute name="title" select="'Default Title'"/>
            <xsl:attribute name="prepTime" select="'00:00'"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

    <!-- Add more specific templates as needed -->
</xsl:stylesheet>
```

Let's apply this XSLT to the XML document.

[{"name": "digitxml.apply_transformation", "arguments": {"validate": true, "xslt": "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"3.0\">\n    <xsl:template match=\"/\">\n        <
!-- Handle specific errors here based on the schema -->\n        <xsl:apply-templates select=\"recipe\"/>\n    </xsl:template>\n\n    <xsl:template match=\"recipe\">\n        <!-- Add default attributes or elem
ents if missing -->\n        <xsl:copy>\n            <xsl:attribute name=\"title\" select=\"'Default Title'\"/>\n            <xsl:attribute name=\"prepTime\" select=\"'00:00'\"/>\n            <xsl:apply-templat
es/>\n        </xsl:copy>\n    </xsl:template>\n\n    <!-- Add more specific templates as needed -->\n</xsl:stylesheet>"}]}

Qwen2.5:7b, despite not receiving any workable feedback on what is wrong with the recipe file, suggests running (incomplete) XSLT.



[1] Hassan El-Hajj, Oliver Eberle, et al., Explainability and transparency in the realm of digital humanities: toward a historian XAI, International Journal of Digital Humanities (IJDH) (2023) 5:299-331, https://link.springer.com/article/10.1007/s42803-023-00070-1.

[2] As of April 2026, the search listing of tutorials involving Artificial Intelligence at The Programming Historian includes several published 2024 or earlier on image classification, face and pattern recognition in historical photos, and manuscript text recognition.

[3] Large Scale Text Analysis is the title of a DH course for second- or third-year university students in the Digital Media, Arts, and Technology program at Penn State Behrend.

[4] On the limitations of this popular formula see Aleksandr Drozd, Anna Gladkova, and Satoshi Matsuoka, Word Embeddings, Analogies, and Machine Learning: Beyond King - Man + Woman = Queen, Proceedings of COLING 2016, the 26th International Conference on Computational Linguistics: Technical Papers (Osaka, Japan, December 11-17 2016) pages 3519–3530, https://aclanthology.org/C16-1332.pdf.

[5] Mike X. Cohen’s six-part LLM breakdown series (about large language models or LLMs) is publicly posted on Substack with accompanying Jupyter Notebooks that he strongly encourages people to run as they read. See Mike X. Cohen, LLM breakdown 1/6: Tokenization (words to integers) (August 26, 2025), https://mikexcohen.substack.com/p/llm-breakdown-16-tokenization-words; LLM breakdown 2/6: Logits and next-token prediction (August 27, 2025), https://mikexcohen.substack.com/p/llm-breakdown-26-logits-and-next; LLM breakdown 3/6: Embeddings (August 29, 2025), https://mikexcohen.substack.com/p/llm-breakdown-36-embeddings; LLM breakdown 4/6: Transformer outputs (hidden states) (September 1, 2025), https://mikexcohen.substack.com/p/llm-breakdown-46-transformer-outputs; LLM breakdown 5/6: Attention (September 2, 2025), https://mikexcohen.substack.com/p/llm-breakdown-56-attention; LLM breakdown 6/6: MLP (September 3, 2025), https://mikexcohen.substack.com/p/llm-breakdown-66-mlp. For Jupyter Notebooks, used for teaching, learning, sharing, and documenting code in Python and over a 100 other programming languages, see Lorena A. Barber et al., Teaching and Learning with Jupyter (2019), https://jupyter4edu.github.io/jupyter-edu-book/ and https://jupyter.org/.

[6] Michael Gerlich, AI Tools in Society: Impacts on Cognitive Offloading and the Future of Critical Thinking, Societies 2025, 15(1), 6, https://doi.org/10.3390/soc15010006; and Ulises A. Mejias, Artificial Intelligence as a Threat to Academic Labor: Who benefits when AI is introduced into higher education? AAUP Academe Magazine Winter 2026, https://www.aaup.org/issue/winter-2026/artificial-intelligence-threat-academic-labor.

[7] Regilme, Salvador Santino F, Artificial Intelligence Colonialism: Environmental Damage, Labor Exploitation, and Human Rights Crises in the Global South, SAIS Review of International Affairs 44:2 (2024), 75-92, https://dx.doi.org/10.1353/sais.2024.a950958.

[8] TEI Consortium, eds. TEI P5: Guidelines for Electronic Text Encoding and Interchange, 4.11.0 18 February 2026, TEI Consortium, http://www.tei-c.org/Guidelines/P5/.

[9] Alexander C. Fisher, Hadleigh Jae Bills, and Elisa Beshero-Bondar, DigitAI for Localized TEI / XML Assistance: An experiment with Small-Scale AI, presented at DH2025, Lisbon, 16 July 2025, https://slides.com/elisabeshero-bondar/digitai-dh25. The GitHub repository for the early stage of the project including our preliminary Jupyter Notebooks remain available on GitHub at https://github.com/newtfire/digitai-2025/. New work in 2026 continues at https://github.com/newtfire/digitai which stores the original repo as a submodule.

[11] Model Context Protocol was released as an open specification in November 2024, and its current version at the time of this writing is https://modelcontextprotocol.io/specification/2025-11-25. The documentation describes MCP as essentially a connector: Just as USB-C provides a standardized way to connect electronic devices, MCP provides a standardized way to connect AI applications to external systems.

[12] At the time of this writing, we are not sure what our system could do with malformed XML, but we do not think it could work on documents that XPath cannot address.

[13] Xinyi Hou,Yanjie Zhao, Shenao Wang, and Haoyu Wang, Model Context Protocol (MCP): Landscape, security threats, and future research directions, ACM Transactions on Software Engineering and Methodology 16 February 2026, https://dl.acm.org/doi/epdf/10.1145/3796519.

[14] Forgejo is an open-source Git forge forked from Gitea in 2022 after Gitea transferred to a for-profit company. Codeberg e.V., a registered non-profit, now maintains the Forgejo source and container distribution, providing both source hosting and container registry functionality without commercial fees.

[15] Löwenstern's tools for Ollama and MCP communication include both the Ollama MCP Bridge at https://github.com/jonigl/ollama-mcp-bridge and a terminal interface at https://github.com/jonigl/mcp-client-for-ollama.

[16] See the Model Registry library python dictionary for our AI Uncertainty Monitor project on at https://codeberg.org/newtfire/ai-artsound/src/branch/main/uncertainty-monitor.py#L106.

[17] See, for example, this helpful article discussing the high potential for SLMs in project workflows: Peter Belcak, Greg Heinrich, Shizhe Diao, Yonggan Fu, Xin Dong, Saurav Muralidharan, Yingyan Celine Lin, and Pavlo Molchanov, Small Language Models are the Future of Agentic AI, Arxiv preprint, under review, September 2025, https://arxiv.org/pdf/2506.02153.

Author's keywords for this paper:
Small Language Model; SLM; schema; declarative markup; declarative methods; JSON-L; XSLT; XQuery; XPath

Elisa E. Beshero-Bondar

Chair

TEI Technical Council

Professor of Digital Humanities

Program Chair of Digital Media, Arts, and Technology

Penn State Erie, The Behrend College

Elisa Beshero-Bondar explores and teaches document data modeling with the XML family of languages. She serves on the TEI Technical Council and is the founder and organizer of the Digital Mitford project and its erstwhile annual (and sometime to be revived) coding school. She and David J. Birnbaum have designed and co-taught a course in XPath, XSLT, XQuery and Schematron at the Digital Humanities Summer Institute. She experiments with visualizing data from complex document structures like epic poems and with computer-assisted collation of differently encoded editions of Frankenstein. Her ongoing adventures with markup technologies are documented on her development site at newtfire.org.

Molly Scott Wright

Research Assistant / Coding Mentor

Penn State Erie, The Behrend College

Molly Wright is a current Penn State Behrend student, pursuing a degree in Digital Media, Arts, and Technology (DIGIT) and a minor in Game Development. She entered the DIGIT program to satisfy a fascination in the intersection between technology and interaction with the arts. Of particular interest to her is natural language processing, especially in the cultural analysis of media, and ethical uses of AI. As a student, she has assisted in writing guides for fellow undergraduates to understand emergent XML stack tools such as ixml.

Michael Roy Simons

Recent Graduate

Research Assistant / Coding Mentor

Penn State Erie, The Behrend College

Michael Simons graduated in May 2026 with a degree in Digital Media, Arts, and Technology (DIGIT) from Penn State Behrend. After two years of studying Computer Science, he decided to pivot to Dr. Beshero-Bondar’s Digit program as it allowed for greater creativity and a more focused path while still learning how to get the most out of today’s innovative technologies. In this program, he’s taken a deep dive into the XML stack where he enjoys using tools like XSLT, ixml, and XProc to create rich markup that is both satisfyingly organized and able to be processed in interesting ways. Michael’s main passion is music, which he's utilized to develop a large-scale text analysis project comparing the lyrics and chord progressions of seemingly similar artists; he presented this project at Balisage 2025.