json-object-editor
Version:
JOE the Json Object Editor | Platform Edition
230 lines (209 loc) • 8.44 kB
HTML
<html>
<head>
<meta charset="utf-8">
<title>JOE MCP → Assistant Config Export</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;margin:20px;}
label{display:block;margin:8px 0 4px}
input,button,textarea{font-size:14px}
input[type=text]{min-width:320px}
textarea{width:100%;height:200px;font-family:ui-monospace,Menlo,Consolas,monospace}
pre{background:#f6f8fa;border:1px solid #e1e4e8;padding:10px;overflow:auto}
.grid{display:grid;grid-template-columns:1fr;gap:18px}
.row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.small{font-size:12px;color:#666}
.good{color:#0a7d00}.bad{color:#b00020}
code{background:#f6f8fa;padding:2px 4px;border-radius:3px}
</style>
</head>
<body>
<div id="mcp-nav"></div>
<script src="/JsonObjectEditor/_www/mcp-nav.js"></script>
<h1>JOE MCP → Assistant Config Export</h1>
<div class="small">Generate copy/paste config for Custom GPT Actions (OpenAPI) and Assistants (tools array).</div>
<div class="grid">
<section>
<h3>1) Server</h3>
<div class="row">
<label for="base">Base URL</label>
<input id="base" type="text" placeholder="https://example.com" />
</div>
<div class="row">
<label for="manifestPath">Manifest path</label>
<input id="manifestPath" type="text" value="/.well-known/mcp/manifest.json" />
</div>
<div class="row">
<label for="auth">Authorization header (optional)</label>
<input id="auth" type="text" placeholder="Basic BASE64(user:pass)" />
</div>
<div class="row">
<button id="load">Load manifest</button>
<span id="status" class="small"></span>
</div>
<pre id="manifestOut" style="display:none"></pre>
</section>
<section>
<h3>2) Custom GPT Actions (OpenAPI 3.1)</h3>
<div class="small">Paste this schema into GPT Builder → Actions → Import from text.</div>
<textarea id="openapi" readonly></textarea>
</section>
<section>
<h3>3) Assistants API tools (functions array)</h3>
<div class="small">Use this tools JSON in code when creating/updating an Assistant.</div>
<textarea id="tools" readonly></textarea>
</section>
<section>
<h3>4) Starter Agent Instructions</h3>
<div class="small">Copy into your Custom GPT or Assistant system prompt. Keep it brief and expand as needed.</div>
<textarea id="starter" readonly></textarea>
</section>
<section>
<h3>5) JSON-RPC request template</h3>
<div class="small">When the Assistant calls a tool, POST this body to <code>/mcp</code>.</div>
<pre>{
"jsonrpc": "2.0",
"id": "<opaque-id>",
"method": "<toolName>",
"params": { /* per-tool params */ }
}</pre>
</section>
</div>
<script>
(function(){
const $ = (id)=>document.getElementById(id);
const base = $('base');
const manifestPath = $('manifestPath');
const auth = $('auth');
const loadBtn = $('load');
const status = $('status');
const openapiEl = $('openapi');
const toolsEl = $('tools');
const manifestOut = $('manifestOut');
base.value = base.value || (location.origin);
function setStatus(msg, ok){
status.textContent = msg||'';
status.className = 'small ' + (ok===true?'good': ok===false?'bad':'');
}
function buildAssistantsTools(manifest){
const tools = (manifest.tools||[]).map(t=>({
type: 'function',
function: {
name: t.name,
description: t.description||'',
parameters: t.params || { type:'object' }
}
}));
return tools;
}
function buildOpenAPI(manifest, serverUrl){
const methodEnum = (manifest.tools||[]).map(t=>t.name);
const oneOf = (manifest.tools||[]).map(t=>({
type:'object',
description: t.description || t.name,
required: [],
properties: t.params && t.params.properties ? t.params.properties : {},
}));
const schema = {
openapi: '3.1.0',
// Callout to JOE version for quick reference
'x-joeVersion': (manifest.joe && manifest.joe.version) || '',
info: {
title: 'JOE MCP Bridge — ' + ((manifest.joe&&manifest.joe.name)||'JOE'),
version: '1.0.0',
description: 'Generated for JOE ' + (((manifest.joe&&manifest.joe.version)||'').toString() || '')
},
servers: [{ url: serverUrl.replace(/\/$/,'') }],
paths: {
'/mcp': {
post: {
operationId: 'mcpCall',
summary: 'Call a JOE MCP tool',
description: 'Use one of: ' + methodEnum.join(', '),
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['jsonrpc','id','method','params'],
properties: {
jsonrpc: { type:'string', const:'2.0' },
id: { type:'string' },
method: { type:'string', enum: methodEnum },
params: { oneOf: oneOf.length? oneOf : [{ type:'object' }] }
}
}
}
}
},
responses: { '200': { description:'OK' } }
}
}
}
};
return schema;
}
async function fetchJSON(url){
const headers = {};
if(auth.value){ headers['Authorization'] = auth.value; }
const res = await fetch(url, { headers });
if(!res.ok){
throw new Error('HTTP '+res.status+' '+(await res.text()));
}
return res.json();
}
loadBtn.onclick = async function(){
try{
setStatus('Loading...', null);
openapiEl.value = '';
toolsEl.value = '';
manifestOut.style.display='none';
const url = base.value.replace(/\/$/,'') + manifestPath.value;
const manifest = await fetchJSON(url);
// Instance info handled by shared nav script
manifestOut.style.display='block';
manifestOut.textContent = JSON.stringify(manifest, null, 2);
const tools = buildAssistantsTools(manifest);
toolsEl.value = JSON.stringify(tools, null, 2);
const openapi = buildOpenAPI(manifest, base.value);
openapiEl.value = JSON.stringify(openapi, null, 2);
// Starter prompt (concise)
const joeName = (manifest.joe && manifest.joe.name) || 'JOE';
const starter = [
`You are a data assistant for ${joeName} (JOE). Use only the provided tools.`,
'',
'Autonomy:',
'- Act autonomously; if a tool can answer, call it immediately (no permission prompts).',
'- Do not offer multiple-choice options; pick the best action.',
'- Ask one brief clarification only if a required parameter is missing.',
'- On a new session: call hydrate {} first, then proceed.',
'- Keep results scoped (limit 10–25). Flatten is optional and off by default; enable only when needed.',
'- Never expose secrets/tokens. Confirm with the user before saveObject/saveObjects.',
'',
'Typical flow:',
'- listSchemas {}, getSchema { "name": "<itemtype>" }',
'- search { "itemtype": "<itemtype>", "limit": 10 } (cache) or add { "source": "storage" } when authoritative results are needed',
'- getObject { "_id": "<id>", "itemtype": "<itemtype>" } for a single item',
'- saveObject { "object": { ... } } or saveObjects { "objects": [ ... ], "concurrency": 5 } only on explicit user request',
'',
'Examples:',
'- listSchemas {}',
'- getSchema { "name": "client" }',
'- search { "itemtype": "client", "source": "storage", "query": { "status": "active" }, "limit": 10 }',
'- getObject { "_id": "123", "itemtype": "client" }',
'- saveObjects { "objects": [{ "itemtype":"client", "name":"Batch A" }], "stopOnError": false, "concurrency": 5 }'
].join('\n');
$('starter').value = starter;
setStatus('Manifest loaded. Config generated.', true);
}catch(e){
setStatus(e.message||String(e), false);
}
};
// Auto-load
setTimeout(()=>loadBtn.click(), 50);
})();
</script>
</body>
</html>