Beshero-Bondar, Elisa E., Molly Scott Wright and Michael Roy Simons. “XML, MCP, and Language Models: A Separation of Concerns.” Presented at Balisage: The Markup Conference 2026, Washington, DC, August 3 - 7, 2026. In Proceedings of Balisage: The Markup Conference 2026. Balisage Series on Markup Technologies, vol. 31 (2026). https://doi.org/10.4242/BalisageVol31.Beshero-Bondar01.
Balisage: The Markup Conference 2026 August 3 - 7, 2026
Balisage Paper: XML, MCP, and Language Models
A Separation of Concerns
Elisa E. Beshero-Bondar
Chair
TEI Technical Council
Professor of Digital Humanities
Program Chair of Digital Media, Arts, and Technology
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 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 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.
This paper reflects on experiments to integrate Small Language Models (SLMs) with
XML technologies in academic Digital Humanities work, and discusses the DigitAI
project. The project is motivated by two goals for making AI systems: 1) more
educationally transparent and 2) more economically and environmentally sustainable.
We first attempted to make a small local Qwen model serve as an assistant for
questions about applying the TEI P5 Guidelines. We provided Qwen with a Retrieval
Augmented Generation (RAG) system built from the TEI P5 Guidelines, using XSLT to
transform the guidelines into a neo4j knowledge graph. This approach proved both
bloated and disappointing: the model's conversion of structured XML data into word
embeddings eroded the precision of the original markup, and the outputs of prompts
reflected hallucination and inaccuracy.
Learning from this failure, we came to realize that XML should be kept as XML, held
apart
from the language model's internal machinery, and made accessible instead through
a Model
Context Protocol (MCP) server that allows the SLM to query the TEI document tree directly
using XPath and related XML stack technologies. We have been experimenting with
MCP scripting and evaluating how well SLMs apply XPath, XSLT, and XQuery parsers,
giving them agency to evaluate and contribute to XML and TEI XML projects. Much of
our
learning process has been in preparing a containerized system with Docker for our
project to
run safely in controlled conditions on personal computers, and to share with others
across platforms.
Reflecting on our experience, we propose a separation of concerns (between the
generative model and the structured data it consults) as a principled, declarative
approach to AI-assisted XML work.
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:
Education: To design explainable AI
systems that help us to practically understand how language models process,
retrieve, and generate text;[1]
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:
finding and reviewing information about their project’s encoding and
reporting on inconsistencies beyond schema validation checks;
answering questions in natural language, based on a review of the project’s structured
data;
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
Output the neo4j graph nodes and edge relationships of elements networked
by paragraph and chapter position across the TEI Guidelines, and
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:
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.
[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.
[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