The standalone application now consumes the TXTextControl.McpServer hosting library. Its admin pages and static assets remain in this application. The reusable package includes the document tools, engine, worker pool, configuration services, and export endpoints.
See package usage for the hosting API and worker bootstrap, and packaging for signing, license, icon, and local package verification. The minimal host demonstrates using the library without the admin UI.
A document automation MCP server for AI-driven workflows using TX Text Control .NET Server.
The server exposes four primary workflows—inspect, edit, convert, and create—plus advanced TX Text Control document automation through stateless MCP HTTP transport. Working documents are stored in InternalUnicodeFormat and can be exported as TX, RTF, DOCX, PDF, HTML, Markdown, or plain text.
- Create and manage document sessions
- Create ordinary fixed-content documents from semantic Markdown in one server-owned import and styling pass
- Load documents from base64 (auto format detection, including markdown edge case)
- Stream document exports over HTTP without Base64 overhead (
tx,rtf,docx,pdf,html,md,txt) - Retain Base64 import/export for compatibility clients
- Discover lightweight server-owned recipes through
list_document_recipes - Execute standard document recipes through
create_document_from_recipe - Discover the complete AI authoring contract through
get_authoring_guide - Format text by:
- character range (
start,length) - paragraph index (
paragraphIndex)
- character range (
- AI-friendly document operation framework:
- neutral core document model (
Document,Section,Paragraph,Run,Table,Image,Style,HeaderFooter,Field) create_documentfor model-first document creationapply_operationsfor semantic incremental editing- external style presets, table style presets, and enabled operations
- first-class capability packs with operation metadata
- text, table, media, field, section, and header/footer operations
- TX-supported image insertion in body, headers, and footers
- template workflows with TX Text Control
MailMerge.MergeJsonData - capability and authoring-guide inspection before authoring
- neutral core document model (
- Admin UI:
- login-protected admin area
- enable/disable capability packs and operations
- edit style presets and table style presets
- inspect MCP endpoint information
- Primary content tools:
inspect_documentreturns relevant text with stable zero-based paragraph indexesclassify_documentreturns a fast live-document category and category-specific suggested actions without an LLM calledit_documentreplaces exact text, one paragraph, a paragraph range, or a character rangeconvert_documentperforms direct TX Text Control conversion without model rewriting or styling
- Structured MCP tool error payloads
- Bounded, isolated persistent document workers with strict per-session ordering and crash recovery
- .NET 10
- C#
- ASP.NET Core Minimal API
- Model Context Protocol (MCP) .NET SDK
- TX Text Control .NET Server
Program.cs- app bootstrap and MCP registrationTools/DocumentTools.cs- session/document lifecycle toolsTools/ContentTools.cs- content/query toolsTools/OperationTools.cs- AI-facing document operation toolsServices/Engines/ServerTextControlDocumentEngine.cs- TX Text Control engine entry pointServices/Engines/ServerTextControlDocumentEngine.Content.cs- content operationsServices/Engines/ServerTextControlDocumentEngine.Operations.cs- operation framework executionServices/Operations/- operation framework, grouped by capability packServices/AuthoringGuideService.cs- self-contained external AI authoring guideModels/DocumentModel/- neutral AI-facing document modelTests/- xUnit test project for framework and model behaviorServices/Workers/- versioned process protocol, bounded scheduler, affinity, recovery, and metricsBenchmarks/- repeatable MCP latency and throughput benchmark harness
- Health:
GET / - Admin:
GET /admin - Automation config JSON:
GET /admin/automation - MCP:
POST /mcp - Export download:
GET /exports/{sessionId}/{exportId}(supports HTTP range requests) - Worker health:
GET /health/document-workers - Worker diagnostics:
GET /admin/document-workers(authenticated)
Transport is configured as stateless HTTP.
create_document_export returns structured metadata and a download URI. The generated artifact is stored inside the document session and expires with that session. The download response is streamed by ASP.NET Core, supports range requests, and uses Cache-Control: private, no-store.
- Session metadata is cached in memory after its first read and persisted as compact JSON.
- New
create_documentandapply_operationssessions are created and populated in one TX engine pass instead of creating and reloading an intermediate blank document. - Content inspection reuses a per-session text/paragraph snapshot, so consecutive
inspect_document,get_text,get_paragraphs, andsearch_textcalls do not reload the TX document. - Re-uploading identical source bytes and source format to the same session is detected with SHA-256 and does not import or rewrite the document again.
- Uploaded-content conversion loads the source, persists the working TX document, and writes the requested output in one TX Text Control instance.
- Merge-field, merge-block, and form-field inspection share one template snapshot.
merge_templatecaptures its before/after state in a single TX document load. - Mutations are serialized per session, while unrelated sessions can execute concurrently.
- Use
create_document_exportplus the/exports/...download URI for production downloads.get_as_base64remains available for compatibility but adds Base64 allocation and transfer overhead. - MCP requests taking at least one second are logged as
McpPerformancewarnings. EnableDebuglogging for that category to record all MCP request timings.
Document engine calls run in a bounded pool of persistent child processes by default. A hot worker keeps one session document loaded, while the canonical TX snapshot and revision remain durable. Restarting the MCP host drops only in-memory caches; persisted sessions and completed mutation records are restored. See the worker-pool architecture and benchmark report.
create_empty_document()— advanced lifecycle tool; not normal document creationload_document(request, sessionId?)— imports TX, RTF, DOCX, HTML, PDF, Markdown, or plain text; specifysourceFormatwhen knownconvert_document(request)— the only tool needed for pure format conversionget_as_base64(request)— compatibility exportcreate_document_export(request)(preferred for AI and high-performance clients)get_session(sessionId)delete_session(sessionId)
inspect_document(request)— primary question/inspection tool; returns bounded indexed chunks withtruncated,nextParagraphIndex, andreturnedCharactersfor token-safe pagingedit_document(request)— primary deterministic text-edit toolformat_text(sessionId, request)get_paragraphs(sessionId, start?, end?)search_text(sessionId, text?, matchCase?, wholeWord?)search_text_ranges(sessionId, text?, matchCase?, wholeWord?)get_text(sessionId)
get_document_automation_capabilities()create_document_from_markdown(request)— preferred creation path for ordinary fixed-content documentsapply_document_preset_styles(request)— normalizes an imported document with configured page, paragraph, and table presetslist_document_recipes()create_document_from_recipe(request)get_authoring_guide()create_document(request)apply_operations(request)get_document_model(sessionId)get_document_structure(sessionId)get_document_styles(sessionId)get_document_tables(sessionId)get_document_fields(sessionId)get_document_headers_footers(sessionId)get_template_merge_fields(sessionId)get_template_merge_blocks(sessionId)get_template_form_fields(sessionId)merge_template(request)
For an ordinary fixed-content document—such as a report, letter, proposal, agenda, article, or invoice with concrete line items—external AI clients should call create_document_from_markdown once. Send complete raw Markdown with one H1 title, H2/H3 hierarchy, lists, emphasis, and valid Markdown tables as appropriate. The server imports the content, maps it to configured paragraph roles, creates native TX tables, applies the default page layout and table preset, and returns the new sessionId. When an output format was requested, call create_document_export separately with that session.
Use list_document_recipes and create_document_from_recipe for reusable templates and advanced server-owned semantics such as merge fields, repeating blocks, and form fields. This keeps operation payloads, field names, layouts, styles, and repeating-block definitions on the MCP server.
Call get_authoring_guide only when the model needs to construct a custom document or operation sequence. It returns recommended workflows, operation-specific schemas and examples, style/table preset definitions, valid value sets, document model guidance, recipes, best practices, and troubleshooting notes.
Use create_document only when a new document requires explicit fonts, colors, sizes, advanced layout, headers/footers, images, fields, or structures Markdown cannot represent. The model-first path applies configured defaults automatically: document.title is rendered with the configured title role, unstyled paragraphs and headers/footers use the configured body role, unstyled tables receive the first configured table style preset, and sections without a page layout receive the configured default. It supports simple table cell styles and uniform whole-cell run styles, but clients must inspect warnings for rich content that was flattened or not rendered with full fidelity. Use apply_operations for precise incremental edits, table header/cell formatting, images, fields, merge blocks, form fields, sections, headers/footers, and targeted formatting.
Operation-first document creation also applies semantic defaults: the first unstyled body paragraph receives the configured title role, later unstyled paragraphs receive the configured body role, and append_table applies the first configured table style preset unless a table styleName is explicitly supplied.
Session continuity rule for external AI clients:
- If the user asks to change, modify, update, edit, adjust, make, increase, decrease, replace, or refers to the current/same/that/previous document, reuse the existing
sessionId. - Inspect the existing document first with
inspect_document; use specialized structure/table/model tools only when needed. - Do not create a new document/session unless the user explicitly asks for a new document.
For paragraph-level changes, use the focused format_paragraph tool. It supports alignment (left, right, center, or justify), paragraph spacing, line spacing, and named styles. Target a paragraph by an MCP zero-based paragraphIndex, by matchText, or explicitly with allParagraphs. Browser-editor selections must use the selected text as matchText and the browser start only as nearTextPosition; the server resolves the closest TX match and its containing paragraph. Do not translate browser offsets into paragraph indexes.
For table appearance changes, use the focused format_table tool. Its scope can be selectedCells, header, cell, row, column, or table. In an editor, send the exact selection as matchText, the browser start as nearTextPosition, and the browser selection length as selectionLength; the server maps that hint to authoritative TX table cells. For example, a red selected-cell background uses scope: "selectedCells" and backgroundColorHex: "#FF0000"; a green header in the selected table uses scope: "header" and backgroundColorHex: "#008000".
get_document_tables reads tables from the live TX document and returns an explicit tableCount plus ordered tableIndex, tableNumber, actual table id, dimensions, and cells. Use add_table_rows for ordinal requests such as “add 5 more rows to the second table” (tableNumber: 2, count: 5) or supply concrete rows. This works for uploaded documents even when no neutral AI table model exists.
Style omission policy for external AI clients:
- If the user prompt does not explicitly mention styling, fonts, colors, sizes, spacing, borders, alignment, or named styles/presets, omit all style-related properties.
- Do not invent
styleName,style,paragraphStyle,paragraph,cellStyle,tableStyleName, font, color, size, border, spacing, or alignment values. - Let the server apply configured defaults automatically.
- Send style information only when the user explicitly asks for it or names a configured style/preset.
- Always inspect
create_documentwarnings. If warnings mention table cell content, spans, mixed inline styles, fields, form fields, or images in cells, useapply_operationsfor exact output.
Named paragraph styles have focused MCP tools:
list_document_stylesreturns the native TX styles, exact names, inheritance, following style, formatting, built-in status, and live usage count.set_document_stylecreates a style or changes an existing style. Existing styles are committed withParagraphStyle.Apply(), so all linked paragraphs update automatically.apply_document_stylelinks one or more target paragraphs to an existing style.rename_document_styleanddelete_document_stylepreserve or explicitly replace paragraph links.create_styles_from_paragraphscompares common character and paragraph formatting, reuses equivalent styles, and creates styles for previously unseen formatting groups. Mixed-format paragraphs are deliberately skipped so inline emphasis is not lost.
For example, “change the style Heading 1 to have red text” maps to set_document_style with styleName: "Heading 1" and text.colorHex: "#FF0000". It must not be translated into repeated direct-formatting calls.
Capability packs are first-class modules represented by ICapabilityPack. A pack declares its name, AI-facing description, and supported operation types. Operation handlers still perform the work, but the registry uses pack metadata to expose enabled modules and reject disabled operations.
The BasicText capability pack includes:
define_stylerename_styledelete_stylecreate_styles_from_paragraphsappend_paragraphapply_style_to_paragraphformat_paragraphsformat_text_occurrencesreplace_text
The Media capability pack includes:
append_image
append_image accepts TX Text Control supported image formats: BMP, TIF/TIFF, WMF, PNG, JPG/JPEG, GIF, EMF, and SVG. Unsupported formats such as WebP are rejected with a structured tool error. Images can be supplied through imagePath, imageBase64, or a data:image/...;base64,... URI. TX runtime sizing uses horizontalScaling and verticalScaling; width and height are retained as neutral-model metadata only.
Use target to choose where the image is inserted. Supported values are body, header, footer, firstPageHeader, firstPageFooter, evenHeader, and evenFooter. The default is body.
The Tables capability pack includes:
append_tableset_table_cell_textformat_table_cellformat_table_header_rowformat_table_columnapply_table_style_presetadd_table_row
Table presets are configured in DocumentAutomation.TableStylePresets. User instructions override defaults, so an explicit request such as “make the table header pink” should be applied after the default table preset.
The Fields capability pack includes:
append_merge_fieldupdate_merge_fieldclear_application_fieldsappend_merge_blockappend_form_fieldupdate_form_fieldclear_form_fields
For LLM clients, the MCP also exposes focused tools that avoid constructing the larger apply_operations payload:
insert_merge_fieldinserts genuineMERGEFIELDmarkup by matching text, replacing an inspected character range, using an absolute text position, targeting a paragraph or table cell, or targeting a header/footer.insert_form_fieldprovides the same positional targets for real TX Text Control form fields.create_merge_blockwraps an existing table row, character range, or paragraph range in atxmb_SubTextPart.update_merge_fieldandupdate_form_fieldupdate every matching real field in the body, headers, and footers and fail instead of reporting success when the name does not exist.clear_application_fieldsandclear_form_fieldsremove only their respective field kind;keepTextdefaults totrue.
When replacing party names or placeholders, use insert_merge_field with matchText and replaceAll (or occurrenceIndex). Do not insert {{name}} as plain text; that is not a merge field.
Form fields support text, selection/dropdown/combobox, checkbox, and date fields. Merge blocks are TX Text Control SubTextPart objects named txmb_<blockName>.
The Sections capability pack includes:
insert_section_breakset_section_layout
Set page size and margins before inserting wide tables because TX Text Control tables do not automatically adapt after page size changes.
The HeaderFooter capability pack includes:
set_header_footer
Build templates with the existing field operations, inspect the real TX Text Control ApplicationFields, then merge data through the TX Text Control MailMerge class:
- Create a template with
apply_operations.- Prefer
insert_merge_fieldfor fields in body text, paragraphs, table cells, headers, or footers. It can replace all matching text or a stale-checked character range. - Prefer
insert_form_fieldfor positional fillable controls. - Prefer
create_merge_blockto wrap a table row, character range, or paragraph range. - The corresponding
append_*operations remain available insideapply_operationsfor batching.
- Prefer
- Confirm template fields and blocks with
get_template_merge_fields(sessionId)andget_template_merge_blocks(sessionId).- This reads actual TX
MERGEFIELDApplicationFieldsfrom the document, not just the neutral model.
- This reads actual TX
- Merge data with
merge_template.- Provide either
request.jsonDataas a JSON string orrequest.dataas a JSON object/array. - The implementation uses
TXTextControl.DocumentServer.MailMerge.MergeJsonData.
- Provide either
- Export the merged result with
create_document_export. Useget_as_base64only for compatibility clients.
Example merge request:
{
"sessionId": "...",
"data": {
"CustomerName": "ACME Corp",
"InvoiceNumber": "INV-1001",
"lineItems": [
{
"ItemName": "TX Fuel",
"Description": "Document automation package",
"Quantity": "2",
"UnitPrice": "199.00",
"LineTotal": "398.00"
}
]
}
}Classify each request first. Do not mix these workflows unless the user explicitly combines intents.
- Ask a question about an upload
- Call
load_documentonce and retain itssessionId. - Call
inspect_documentwith thatsessionIdand the question inquery. - Answer only from the returned indexed paragraphs. Do not edit or export.
- Call
- Modify an upload or current document
- Reuse the existing
sessionIdand callinspect_documentto resolve exact text or paragraph indexes. - Call
edit_documentfor replacement by exact text, paragraph index/range, or character range. - Use
format_textorapply_operationsonly for formatting and structural mutations not supported byedit_document. Never recreate the document.
- Reuse the existing
- Convert a document
- Call
convert_documentonly. Supply eitherdataplus the knownsourceFormat, or an existingsessionId, and theoutputFormat. - Return its
downloadUri. Do not inspect, summarize, rewrite, restyle, or use a recipe.
- Call
- Create a new document
- For a standard type, use
list_document_recipesandcreate_document_from_recipewhen a matching recipe exists. - Otherwise call
create_documentonce with the complete semantic model. - Omit presentation properties not requested by the user. The server fills missing title, heading, body, table, page-size, and margin settings from presets.
- For a standard type, use
- Export a created or edited document
- Call
create_document_export(preferred) orget_as_base64(compatibility) with the samesessionId.
- Call
dotnet build
dotnet run --urls http://localhost:5000Run tests:
dotnet test TxTextControl.McpServer.slnCreate a complete new document:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "create_document",
"arguments": {
"request": {
"createIfMissing": true,
"document": {
"title": "Example",
"sections": [
{
"blocks": [
{
"type": "paragraph",
"paragraph": { "role": "body", "text": "Body text" }
}
]
}
]
}
}
}
}
}Use the returned sessionId in subsequent tool calls.
format_text(sessionId, request) accepts:
start(int, optional)length(int, optional)paragraphIndex(int, optional)bold(bool)italic(bool)underline(bool)color_hex(string, optional, e.g.#FF0000)font_name(string, optional)font_size(float, optional, points)
Use either
start+lengthorparagraphIndex.
Use list_document_recipes() as the lightweight starting point for standard document types. It returns only recipe names and goals. Pass a selected name to create_document_from_recipe({ "recipeName": "..." }); the server executes its authoritative recipe and returns compact session and template metadata.
Use get_authoring_guide() when a client needs the complete authoring contract for custom document construction. The response includes:
recommendedWorkflowtoolMapdocumentModelContractoperationSchemasstyleRolesstylePresetstableStylePresetsstylePolicysessionPolicyvalueSetsrecipesbestPracticestroubleshooting
Create a document from a neutral model:
{
"createIfMissing": true,
"document": {
"styles": [
{
"name": "Title",
"type": "paragraph",
"text": {
"fontName": "Arial",
"fontSize": 30,
"fontSizeUnit": "pt",
"bold": true
}
}
],
"sections": [
{
"pageLayout": {
"pageSize": "Letter",
"orientation": "portrait",
"unit": "in",
"marginLeft": 1,
"marginRight": 1,
"marginTop": 1,
"marginBottom": 1
},
"blocks": [
{
"type": "paragraph",
"paragraph": {
"styleName": "Title",
"runs": [
{ "text": "Quarterly Report" }
]
}
}
]
}
]
}
}Create a styled document in one AI-friendly call:
{
"createIfMissing": true,
"operations": [
{
"type": "define_style",
"style": {
"name": "Heading",
"fontName": "Arial",
"fontSize": 20,
"fontSizeUnit": "px",
"bold": true
}
},
{
"type": "define_style",
"style": {
"name": "Paragraph-Text",
"fontName": "Arial",
"fontSize": 12,
"fontSizeUnit": "px",
"bold": false
}
},
{
"type": "append_paragraph",
"styleName": "Heading",
"text": "This is my title"
},
{
"type": "append_paragraph",
"styleName": "Paragraph-Text",
"text": "This is the paragraph text."
}
]
}The response includes the sessionId and per-operation results. Use create_document_export afterward to export the session as tx, rtf, docx, pdf, html, md, or txt.
Create and style a table:
{
"createIfMissing": true,
"operations": [
{
"type": "set_section_layout",
"pageSize": "Letter",
"unit": "in",
"marginLeft": 1,
"marginRight": 1,
"marginTop": 1,
"marginBottom": 1
},
{
"type": "append_table",
"tableId": "10",
"rows": [
["Country", "Sales", "Qty"],
["Germany", "$842,000", "1280"],
["USA", "$1,240,000", "1985"]
]
},
{
"type": "apply_table_style_preset",
"tableId": "10",
"styleName": "Professional Blue"
},
{
"type": "format_table_header_row",
"tableId": "10",
"style": {
"bold": true,
"colorHex": "#FFFFFF"
},
"cellStyle": {
"backgroundColorHex": "#D0006F",
"border": {
"width": 10,
"colorHex": "#000000"
}
}
}
]
}Add a TX-supported image:
{
"sessionId": "...",
"operations": [
{
"type": "append_image",
"imagePath": "C:\\Images\\photo.png",
"altText": "Product photo",
"horizontalScaling": 75,
"verticalScaling": 75,
"alignment": "centered",
"insertionMode": "displaceText"
}
]
}Add an embedded base64/data URI image:
{
"sessionId": "...",
"operations": [
{
"type": "append_image",
"imageBase64": "data:image/png;base64,...",
"altText": "Embedded product photo",
"horizontalScaling": 50,
"verticalScaling": 50
}
]
}Insert a floating image at a page location:
{
"sessionId": "...",
"operations": [
{
"type": "append_image",
"imagePath": "C:\\Images\\watermark.png",
"pageNumber": 1,
"locationX": 25,
"locationY": 40,
"locationUnit": "mm",
"insertionMode": "belowText"
}
]
}Add a logo to a header:
{
"sessionId": "...",
"operations": [
{
"type": "set_header_footer",
"headerFooterType": "header",
"text": "Company Report "
},
{
"type": "append_image",
"target": "header",
"imagePath": "C:\\Images\\logo.png",
"altText": "Company logo",
"horizontalScaling": 40,
"verticalScaling": 40
}
]
}Use get_document_model(sessionId) to inspect the neutral document tree captured for the session. The model is intentionally independent from TX Text Control runtime classes so additional operation handlers can target a stable structure first and render through TX Text Control afterward.
For ordinary new documents, call create_document_from_markdown with the complete raw Markdown. Do not
Base64-encode it and do not wrap the whole value in a Markdown code fence. A typical request contains one
H1 title, H2/H3 headings, paragraphs, lists, emphasis, and pipe tables. In one TX engine pass, the server:
- imports the Markdown content;
- materializes pipe tables as native TX Text Control tables;
- maps H1/H2/H3 and body text to the configured Title/Heading1/Heading2/Body roles;
- applies the configured default page layout and first table style preset; and
- persists the document and returns its
sessionId.
Call create_document_export with the returned session only when the user requests an output file. Keeping
creation and export as two explicit tools makes the saved artifact verifiable without asking the model to
reconstruct the document.
After load_document, call apply_document_preset_styles with the returned sessionId when the user asks
to apply, normalize, or polish the document using server presets. The tool preserves content and session
identity, applies the configured default page layout, maps imported Markdown H1/H2/H3 hierarchy to the
configured Title/Heading1/Heading2 roles, applies the configured Body style to remaining paragraphs, and
styles native tables with the first configured table preset. The model does not need to inspect the document
or generate one formatting operation per paragraph or table cell.
appsettings.json contains a DocumentAutomation section:
EnabledCapabilityPackscontrols feature groups such asBasicText,Media,Tables,Fields,Sections, andHeaderFooter.EnabledOperationscontrols individual operation types.DefaultParagraphStyleNamedefines the fallback paragraph style.StyleRolesmaps semantic roles such as title, heading1, heading2, and body to configured style names.StylePresetsdefines reusable paragraph/text style names that AI clients can use without redefining them in every request.DefaultPageLayoutdefines the page size, orientation, and margins used whenever creation omits them.TableStylePresetsdefines default table header/body/alternating-row styles, cell backgrounds, and borders. Forcreate_document, the first configured table preset is applied when a table has no explicitstyleName.
The /admin page exposes editable capability packs, operations, style presets, and table presets. The /admin/automation JSON endpoint exposes the currently configured automation surface.
- TX Text Control licensing is required for full runtime functionality.
- Errors from tools are returned as structured MCP payloads (
code,message,isError).
See repository license terms and TX Text Control licensing terms.