Files
mcp-docx/assets/html_interface.html
T
akadmin f655336757
Continuous Integration / Test Suite (macos-latest, nightly) (push) Has been cancelled
Continuous Integration / Test Suite (macos-latest, stable) (push) Has been cancelled
Continuous Integration / Test Suite (ubuntu-latest, 1.70.0) (push) Has been cancelled
Continuous Integration / Test Suite (ubuntu-latest, beta) (push) Has been cancelled
Continuous Integration / Test Suite (ubuntu-latest, nightly) (push) Has been cancelled
Continuous Integration / Test Suite (ubuntu-latest, stable) (push) Has been cancelled
Continuous Integration / Test Suite (windows-latest, stable) (push) Has been cancelled
Continuous Integration / Security Audit (push) Has been cancelled
Continuous Integration / Code Coverage (push) Has been cancelled
Continuous Integration / Performance Benchmarks (push) Has been cancelled
Continuous Integration / Memory Safety Check (push) Has been cancelled
Continuous Integration / Docker Build Test (push) Has been cancelled
Continuous Integration / Release Readiness (push) Has been cancelled
Continuous Integration / Integration Tests (push) Has been cancelled
Continuous Integration / Stress Testing (push) Has been cancelled
Continuous Integration / Notify Results (push) Has been cancelled
Add HTTP interface, templates, generate_from_template, unified Dockerfile
2026-06-13 00:22:02 +00:00

554 lines
18 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOCX MCP Server - Web Interface</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #f5f5f5;
color: #333;
}
.header {
background: #1a73e8;
color: white;
padding: 1rem 2rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.header h1 {
font-size: 1.5rem;
font-weight: 500;
}
.header p {
font-size: 0.875rem;
opacity: 0.9;
margin-top: 0.25rem;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
.panel {
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.panel h2 {
font-size: 1.25rem;
margin-bottom: 1rem;
color: #1a73e8;
}
.tool-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
}
.tool-card {
border: 1px solid #ddd;
border-radius: 6px;
padding: 1rem;
cursor: pointer;
transition: all 0.2s;
}
.tool-card:hover {
border-color: #1a73e8;
box-shadow: 0 2px 8px rgba(26, 115, 232, 0.2);
transform: translateY(-2px);
}
.tool-card h3 {
font-size: 1rem;
color: #1a73e8;
margin-bottom: 0.5rem;
}
.tool-card p {
font-size: 0.875rem;
color: #666;
line-height: 1.4;
}
.form-group {
margin-bottom: 1rem;
}
.form-group label {
display: block;
font-weight: 500;
margin-bottom: 0.25rem;
color: #555;
}
.form-group input,
.form-group textarea,
.form-group select {
width: 100%;
padding: 0.5rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 0.875rem;
}
.form-group textarea {
min-height: 200px;
font-family: monospace;
}
.btn {
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
font-size: 0.875rem;
cursor: pointer;
transition: background 0.2s;
}
.btn-primary {
background: #1a73e8;
color: white;
}
.btn-primary:hover {
background: #1557b0;
}
.btn-secondary {
background: #f1f1f1;
color: #333;
}
.btn-secondary:hover {
background: #ddd;
}
.response-panel {
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
padding: 1rem;
margin-top: 1rem;
max-height: 400px;
overflow: auto;
}
.response-panel pre {
margin: 0;
white-space: pre-wrap;
font-family: monospace;
font-size: 0.875rem;
}
.status {
display: inline-block;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 500;
}
.status.success {
background: #d4edda;
color: #155724;
}
.status.error {
background: #f8d7da;
color: #721c24;
}
.status.loading {
background: #fff3cd;
color: #856404;
}
.hidden {
display: none;
}
.connection-status {
position: fixed;
bottom: 1rem;
right: 1rem;
padding: 0.5rem 1rem;
border-radius: 4px;
font-size: 0.875rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.connection-status.connected {
background: #d4edda;
color: #155724;
}
.connection-status.disconnected {
background: #f8d7da;
color: #721c24;
}
</style>
</head>
<body>
<div class="header">
<h1>DOCX MCP Server</h1>
<p>Word Document Processing Interface</p>
</div>
<div class="container">
<div class="panel">
<h2>Templates</h2>
<div id="templatesPanel">
<p>Loading templates...</p>
</div>
</div>
<div class="panel">
<h2>Available Tools</h2>
<div class="tool-grid" id="toolGrid">
<div style="text-align: center; padding: 2rem;">
<p>Loading tools...</p>
</div>
</div>
</div>
<div class="panel" id="toolFormPanel" style="display: none;">
<h2 id="toolName">Tool Name</h2>
<p id="toolDescription" style="margin-bottom: 1rem; color: #666;"></p>
<div id="toolForm">
<!-- Form fields will be generated here -->
</div>
<div style="margin-top: 1rem;">
<button class="btn btn-primary" onclick="executeTool()">Execute</button>
<button class="btn btn-secondary" onclick="resetForm()">Reset</button>
</div>
</div>
<div class="panel" id="responsePanel" style="display: none;">
<h2>Response</h2>
<div id="responseStatus"></div>
<div class="response-panel">
<pre id="responseContent"></pre>
</div>
</div>
</div>
<div class="connection-status" id="connectionStatus">
Connecting...
</div>
<script>
let currentTool = null;
let tools = [];
let ws = null;
// Initialize on page load
document.addEventListener('DOMContentLoaded', () => {
loadTools();
loadTemplates();
connectWebSocket();
});
// Load available templates
async function loadTemplates() {
const container = document.getElementById('templatesPanel');
try {
const response = await fetch('/api/call', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'list_templates', arguments: {} })
});
const data = await response.json();
if (!data.success || !data.content || !data.content.templates || data.content.templates.length === 0) {
container.innerHTML = '<p>No templates available.</p>';
return;
}
const list = document.createElement('div');
list.style.display = 'flex';
list.style.flexWrap = 'wrap';
list.style.gap = '0.5rem';
data.content.templates.forEach(t => {
const btn = document.createElement('button');
btn.className = 'btn btn-secondary';
btn.textContent = t;
btn.onclick = () => openTemplate(t);
list.appendChild(btn);
});
container.appendChild(list);
} catch (err) {
container.innerHTML = '<p>Failed to load templates.</p>';
}
}
// Open a template via the server
async function openTemplate(name) {
const responsePanel = document.getElementById('responsePanel');
const status = document.getElementById('responseStatus');
const content = document.getElementById('responseContent');
responsePanel.style.display = 'block';
status.innerHTML = '<span class="status loading">Opening template...</span>';
content.textContent = '';
try {
const res = await fetch('/api/call', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'open_template', arguments: { name } })
});
const data = await res.json();
if (data.success) {
status.innerHTML = '<span class="status success">Template opened</span>';
content.textContent = JSON.stringify(data.content, null, 2);
} else {
status.innerHTML = '<span class="status error">Error</span>';
content.textContent = data.error || JSON.stringify(data, null, 2);
}
} catch (err) {
status.innerHTML = '<span class="status error">Error</span>';
content.textContent = err.message;
}
}
// Load available tools
async function loadTools() {
try {
const response = await fetch('/api/tools');
const data = await response.json();
if (data.success) {
tools = data.tools;
renderToolGrid();
}
} catch (error) {
console.error('Failed to load tools:', error);
}
}
// Render tool cards
function renderToolGrid() {
const grid = document.getElementById('toolGrid');
grid.innerHTML = '';
tools.forEach(tool => {
const card = document.createElement('div');
card.className = 'tool-card';
card.onclick = () => selectTool(tool);
card.innerHTML = `
<h3>${tool.name}</h3>
<p>${tool.description || 'No description available'}</p>
`;
grid.appendChild(card);
});
}
// Select a tool to use
function selectTool(tool) {
currentTool = tool;
document.getElementById('toolName').textContent = tool.name;
document.getElementById('toolDescription').textContent = tool.description;
// Generate form based on input schema
generateForm(tool.input_schema);
document.getElementById('toolFormPanel').style.display = 'block';
document.getElementById('responsePanel').style.display = 'none';
}
// Generate form fields from schema
function generateForm(schema) {
const form = document.getElementById('toolForm');
form.innerHTML = '';
if (!schema.properties) return;
Object.entries(schema.properties).forEach(([name, prop]) => {
const group = document.createElement('div');
group.className = 'form-group';
const label = document.createElement('label');
label.textContent = `${name}${schema.required && schema.required.includes(name) ? ' *' : ''}`;
let input;
switch (prop.type) {
case 'string':
if (prop.enum) {
input = document.createElement('select');
input.id = `field_${name}`;
input.innerHTML = '<option value="">Select...</option>';
prop.enum.forEach(option => {
const opt = document.createElement('option');
opt.value = option;
opt.textContent = option;
input.appendChild(opt);
});
} else {
input = document.createElement('textarea');
input.id = `field_${name}`;
input.placeholder = prop.description || `Enter ${name}`;
}
break;
case 'boolean':
input = document.createElement('input');
input.type = 'checkbox';
input.id = `field_${name}`;
break;
case 'number':
case 'integer':
input = document.createElement('input');
input.type = 'number';
input.id = `field_${name}`;
input.placeholder = prop.description || `Enter ${name}`;
break;
case 'array':
case 'object':
input = document.createElement('textarea');
input.id = `field_${name}`;
input.placeholder = prop.description || `Enter JSON for ${name}`;
input.style.fontFamily = 'monospace';
break;
default:
input = document.createElement('input');
input.id = `field_${name}`;
input.placeholder = prop.description || `Enter ${name}`;
}
group.appendChild(label);
group.appendChild(input);
form.appendChild(group);
});
}
// Execute tool call
async function executeTool() {
if (!currentTool) return;
const status = document.getElementById('responseStatus');
const content = document.getElementById('responseContent');
status.innerHTML = '<span class="status loading">Executing...</span>';
content.textContent = '';
document.getElementById('responsePanel').style.display = 'block';
// Collect form data
const arguments = {};
const schema = currentTool.input_schema;
if (schema.properties) {
Object.entries(schema.properties).forEach(([name, prop]) => {
const field = document.getElementById(`field_${name}`);
if (field) {
let value;
switch (prop.type) {
case 'boolean':
value = field.checked;
break;
case 'number':
case 'integer':
value = parseInt(field.value) || field.value;
break;
case 'array':
case 'object':
try {
value = JSON.parse(field.value);
} catch {
value = field.value;
}
break;
default:
value = field.value;
}
if (value || value === false) {
arguments[name] = value;
}
}
});
}
try {
const response = await fetch('/api/call', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: currentTool.name,
arguments: arguments
})
});
const data = await response.json();
if (data.success) {
status.innerHTML = '<span class="status success">Success</span>';
content.textContent = JSON.stringify(data.content, null, 2);
} else {
status.innerHTML = '<span class="status error">Error</span>';
content.textContent = data.error || 'Unknown error occurred';
}
} catch (error) {
status.innerHTML = '<span class="status error">Error</span>';
content.textContent = error.message;
}
}
// Reset form
function resetForm() {
const fields = document.querySelectorAll('#toolForm input, #toolForm textarea, #toolForm select');
fields.forEach(field => {
if (field.type === 'checkbox') {
field.checked = false;
} else {
field.value = '';
}
});
}
// WebSocket connection for real-time updates
function connectWebSocket() {
const status = document.getElementById('connectionStatus');
try {
ws = new WebSocket(`ws://${window.location.host}/ws`);
ws.onopen = () => {
status.textContent = 'Connected';
status.className = 'connection-status connected';
};
ws.onclose = () => {
status.textContent = 'Disconnected';
status.className = 'connection-status disconnected';
setTimeout(connectWebSocket, 5000);
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
status.textContent = 'Connection Error';
status.className = 'connection-status disconnected';
};
} catch (error) {
console.error('Failed to connect WebSocket:', error);
}
}
</script>
</body>
</html>