json-object-editor 0.10.654 → 0.10.660
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -1
- package/_www/mcp-test.html +287 -276
- package/css/joe-styles.css +6 -0
- package/css/joe.css +7 -1
- package/css/joe.min.css +1 -1
- package/docs/React_Form_Integration_Strategy.md +398 -0
- package/form-qs.json +1007 -0
- package/js/JsonObjectEditor.jquery.craydent.js +56 -12
- package/js/joe-ai.js +2275 -2052
- package/js/joe.js +57 -13
- package/js/joe.min.js +1 -1
- package/package.json +1 -1
- package/react-form-spa-ex.js +570 -0
- package/readme.md +34 -1
- package/server/fields/core.js +4 -1
- package/server/modules/MCP.js +1364 -1237
- package/server/modules/Sites.js +79 -0
- package/server/modules/Storage.js +28 -1
- package/server/plugins/chatgpt.js +1864 -1495
- package/server/schemas/ai_assistant.js +15 -1
- package/server/schemas/ai_prompt.js +389 -324
- package/server/schemas/ai_response.js +414 -374
- package/server/schemas/ai_widget_conversation.js +31 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
## 0.10.660
|
|
2
|
+
|
|
3
|
+
- MCP as an optional layer on all AI surfaces
|
|
4
|
+
- `ai_prompt`: added MCP config block (`mcp_enabled`, `mcp_toolset`, `mcp_selected_tools`, `mcp_instructions_mode`) so prompts can opt into MCP tools with named toolsets (`read-only`, `minimal`, `all`, `custom`) and auto‑generated tool instructions.
|
|
5
|
+
- Autofill: field‑level `ai` configs now accept the same MCP keys, enabling per‑field MCP tooling for `_joe.Ai.populateField` runs.
|
|
6
|
+
- `ai_response`: now records MCP config and `mcp_tools_used[]` for every run, plus `used_openai_file_ids[]` for attached OpenAI Files.
|
|
7
|
+
- Uploader file roles + AI‑aware attachments
|
|
8
|
+
- Uploader fields can declare `file_roles` (array of `{ value, label?, default? }`); the UI renders a per‑file role `<select>` and persists the choice to `file_role` on each file object.
|
|
9
|
+
- `executeJOEAiPrompt` now injects an `uploaded_files[]` header into the user input, listing `{ itemtype, field, name, role, openai_file_id }` for each attached file so prompts can distinguish “transcript” vs “summary” sources.
|
|
10
|
+
- `runWithTools` now applies `attachFilesToResponsesPayload` to both the initial tool‑planning call and the final Responses call (including the token‑limit retry), so MCP runs consistently see file attachments.
|
|
11
|
+
- Storage / history robustness
|
|
12
|
+
- Hardened `JOE.Storage.save` history diffing by sanitizing cached vs current documents before `$c.changes(...)` to avoid a `craydent-object.equals` edge case where comparing `null`/`undefined` could throw on `.toString()`. This only affects `_history.changes`, not saved documents.
|
|
13
|
+
- Object chat unification
|
|
14
|
+
- The legacy assistants-based `objectChat` flow has been replaced with a floating `<joe-ai-widget>` shell (with `<joe-ai-assistant-picker>`), so object chat uses the same widget + MCP stack as AIHub.
|
|
15
|
+
- `ai_widget_conversation` now supports scoped chats via `scope_itemtype`/`scope_id` and tracks attached OpenAI Files via `attached_openai_file_ids` and `attached_files_meta`.
|
|
16
|
+
- On the first turn of an object-scoped chat, the server preloads a slimmed `understandObject` snapshot for the scoped object and merges object files + assistant files into the `openai_file_ids` passed to `runWithTools`, so assistants immediately understand which record they’re talking about and can reason over its files.
|
|
17
|
+
|
|
1
18
|
## 0.10.654
|
|
2
19
|
|
|
3
20
|
- Uploader + OpenAI Files integration
|
|
@@ -1029,4 +1046,4 @@ icons added, bug fixes.
|
|
|
1029
1046
|
*v0.4.1
|
|
1030
1047
|
files directory added. defaults to /_files/_
|
|
1031
1048
|
*V0.4.0
|
|
1032
|
-
now ready to be loaded as a node require.
|
|
1049
|
+
now ready to be loaded as a node require.
|
package/_www/mcp-test.html
CHANGED
|
@@ -1,276 +1,287 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<title>JOE MCP Test</title>
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
7
|
-
<style>
|
|
8
|
-
body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;margin:20px;}
|
|
9
|
-
label{display:block;margin:8px 0 4px}
|
|
10
|
-
select, input, textarea, button{font-size:14px}
|
|
11
|
-
textarea{width:100%;height:160px;font-family:ui-monospace,Menlo,Consolas,monospace}
|
|
12
|
-
pre{background:#f6f8fa;border:1px solid #e1e4e8;padding:10px;overflow:auto}
|
|
13
|
-
.row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}
|
|
14
|
-
.small{font-size:12px;color:#666}
|
|
15
|
-
.bad{color:#b00020}
|
|
16
|
-
.good{color:#0a7d00}
|
|
17
|
-
.preset{margin:8px 0;}
|
|
18
|
-
</style>
|
|
19
|
-
</head>
|
|
20
|
-
<body>
|
|
21
|
-
<div id="mcp-nav"></div>
|
|
22
|
-
<script src="/JsonObjectEditor/_www/mcp-nav.js"></script>
|
|
23
|
-
<h1>JOE MCP Test</h1>
|
|
24
|
-
<div class="small">Use this page to discover tools and call the JSON-RPC endpoint.</div>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<button id="
|
|
43
|
-
<button id="
|
|
44
|
-
<button id="
|
|
45
|
-
<button id="
|
|
46
|
-
<button id="
|
|
47
|
-
<button id="
|
|
48
|
-
<button id="
|
|
49
|
-
<button id="
|
|
50
|
-
<button id="
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (!
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>JOE MCP Test</title>
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
7
|
+
<style>
|
|
8
|
+
body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;margin:20px;}
|
|
9
|
+
label{display:block;margin:8px 0 4px}
|
|
10
|
+
select, input, textarea, button{font-size:14px}
|
|
11
|
+
textarea{width:100%;height:160px;font-family:ui-monospace,Menlo,Consolas,monospace}
|
|
12
|
+
pre{background:#f6f8fa;border:1px solid #e1e4e8;padding:10px;overflow:auto}
|
|
13
|
+
.row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}
|
|
14
|
+
.small{font-size:12px;color:#666}
|
|
15
|
+
.bad{color:#b00020}
|
|
16
|
+
.good{color:#0a7d00}
|
|
17
|
+
.preset{margin:8px 0;}
|
|
18
|
+
</style>
|
|
19
|
+
</head>
|
|
20
|
+
<body>
|
|
21
|
+
<div id="mcp-nav"></div>
|
|
22
|
+
<script src="/JsonObjectEditor/_www/mcp-nav.js"></script>
|
|
23
|
+
<h1>JOE MCP Test</h1>
|
|
24
|
+
<div class="small">Use this page to discover tools and call the JSON-RPC endpoint.</div>
|
|
25
|
+
|
|
26
|
+
<h3>Toolsets (from manifest)</h3>
|
|
27
|
+
<pre id="toolsets"></pre>
|
|
28
|
+
|
|
29
|
+
<h3>Manifest</h3>
|
|
30
|
+
<div class="row">
|
|
31
|
+
<label for="base">Base URL</label>
|
|
32
|
+
<input id="base" value="" placeholder="http://localhost:{{PORT}}" style="min-width:280px"/>
|
|
33
|
+
<button id="loadManifest">Load manifest</button>
|
|
34
|
+
<span id="status" class="small"></span>
|
|
35
|
+
</div>
|
|
36
|
+
<label for="tool">Tools</label>
|
|
37
|
+
<select id="tool"></select>
|
|
38
|
+
<pre id="toolInfo"></pre>
|
|
39
|
+
|
|
40
|
+
<h3>Presets</h3>
|
|
41
|
+
<div class="preset">
|
|
42
|
+
<button id="presetFuzzyUser">fuzzySearch users: q="corey hadden"</button>
|
|
43
|
+
<button id="presetFuzzyHouse">fuzzySearch houses: q="backyard"</button>
|
|
44
|
+
<button id="presetListApps">listApps: all app definitions</button>
|
|
45
|
+
<button id="presetGetSchemas">getSchemas: ["client","user"]</button>
|
|
46
|
+
<button id="presetGetSchemasSummary">getSchemas (summaryOnly): ["task","project"]</button>
|
|
47
|
+
<button id="presetSearchSlimRecent">search clients (slim, recent)</button>
|
|
48
|
+
<button id="presetSearchWithCount">search clients (withCount)</button>
|
|
49
|
+
<button id="presetSearchCountOnly">search clients (countOnly)</button>
|
|
50
|
+
<button id="presetSaveObjects">saveObjects: batch save (example)</button>
|
|
51
|
+
<button id="presetUnderstandObject">understandObject: by _id</button>
|
|
52
|
+
<button id="presetFindByTag">findObjectsByTag: by tags (example)</button>
|
|
53
|
+
<button id="presetFindByStatus">findObjectsByStatus: by status (example)</button>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<h3>Call JSON-RPC</h3>
|
|
57
|
+
<label for="params">Params (JSON)</label>
|
|
58
|
+
<textarea id="params">{}</textarea>
|
|
59
|
+
<div class="row">
|
|
60
|
+
<button id="call">POST /mcp</button>
|
|
61
|
+
<span id="callStatus" class="small"></span>
|
|
62
|
+
</div>
|
|
63
|
+
<pre id="result"></pre>
|
|
64
|
+
|
|
65
|
+
<script>
|
|
66
|
+
(function(){
|
|
67
|
+
const $ = (id)=>document.getElementById(id);
|
|
68
|
+
const base = $('base');
|
|
69
|
+
const loadBtn = $('loadManifest');
|
|
70
|
+
const toolsetsEl = document.getElementById('toolsets');
|
|
71
|
+
const status = $('status');
|
|
72
|
+
const toolSel = $('tool');
|
|
73
|
+
const toolInfo = $('toolInfo');
|
|
74
|
+
const params = $('params');
|
|
75
|
+
const callBtn = $('call');
|
|
76
|
+
const callStatus = $('callStatus');
|
|
77
|
+
const result = $('result');
|
|
78
|
+
const presetFuzzyUser = $('presetFuzzyUser');
|
|
79
|
+
const presetFuzzyHouse = $('presetFuzzyHouse');
|
|
80
|
+
const presetListApps = $('presetListApps');
|
|
81
|
+
const presetGetSchemas = $('presetGetSchemas');
|
|
82
|
+
const presetGetSchemasSummary = $('presetGetSchemasSummary');
|
|
83
|
+
const presetSearchSlimRecent = $('presetSearchSlimRecent');
|
|
84
|
+
const presetSearchWithCount = $('presetSearchWithCount');
|
|
85
|
+
const presetSearchCountOnly = $('presetSearchCountOnly');
|
|
86
|
+
const presetSaveObjects = $('presetSaveObjects');
|
|
87
|
+
const presetUnderstandObject = $('presetUnderstandObject');
|
|
88
|
+
const presetFindByTag = $('presetFindByTag');
|
|
89
|
+
const presetFindByStatus = $('presetFindByStatus');
|
|
90
|
+
|
|
91
|
+
// Try to infer base from window location
|
|
92
|
+
base.value = base.value || (location.origin);
|
|
93
|
+
|
|
94
|
+
let manifest = null;
|
|
95
|
+
let idCounter = 1;
|
|
96
|
+
|
|
97
|
+
function ensureTool(name, meta){
|
|
98
|
+
if (!manifest) { manifest = { tools: [] }; }
|
|
99
|
+
manifest.tools = manifest.tools || [];
|
|
100
|
+
var existing = (manifest.tools||[]).find(function(t){ return t && t.name === name; });
|
|
101
|
+
if (!existing && meta) { manifest.tools.push(meta); }
|
|
102
|
+
// ensure option exists in select
|
|
103
|
+
var hasOpt = false;
|
|
104
|
+
for (var i=0;i<toolSel.options.length;i++){ if (toolSel.options[i].value === name) { hasOpt = true; break; } }
|
|
105
|
+
if (!hasOpt){ var opt=document.createElement('option'); opt.value=name; opt.textContent=name; toolSel.appendChild(opt); }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function setStatus(el, msg, ok){
|
|
109
|
+
el.textContent = msg || '';
|
|
110
|
+
el.className = 'small ' + (ok===true?'good': ok===false?'bad':'');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function fetchJSON(url, opts){
|
|
114
|
+
const res = await fetch(url, opts);
|
|
115
|
+
const ct = res.headers.get('content-type')||'';
|
|
116
|
+
const isJSON = ct.includes('application/json');
|
|
117
|
+
if(!res.ok){
|
|
118
|
+
let detail = isJSON ? await res.json().catch(()=>({})) : await res.text();
|
|
119
|
+
throw new Error('HTTP '+res.status+': '+(isJSON?JSON.stringify(detail):detail));
|
|
120
|
+
}
|
|
121
|
+
return isJSON ? res.json() : res.text();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
loadBtn.onclick = async function(){
|
|
125
|
+
setStatus(status, 'Loading...', null);
|
|
126
|
+
toolSel.innerHTML = '';
|
|
127
|
+
toolInfo.textContent='';
|
|
128
|
+
try{
|
|
129
|
+
const url = base.value.replace(/\/$/,'') + '/.well-known/mcp/manifest.json';
|
|
130
|
+
manifest = await fetchJSON(url);
|
|
131
|
+
// Instance info handled by shared nav script
|
|
132
|
+
(manifest.tools||[]).forEach(t=>{
|
|
133
|
+
const opt=document.createElement('option');
|
|
134
|
+
opt.value=t.name; opt.textContent=t.name;
|
|
135
|
+
toolSel.appendChild(opt);
|
|
136
|
+
});
|
|
137
|
+
if((manifest.tools||[]).length){
|
|
138
|
+
toolSel.selectedIndex=0; renderToolInfo();
|
|
139
|
+
}
|
|
140
|
+
if (toolsetsEl) {
|
|
141
|
+
try{
|
|
142
|
+
toolsetsEl.textContent = JSON.stringify(manifest.toolsets || {}, null, 2);
|
|
143
|
+
}catch(_e){
|
|
144
|
+
toolsetsEl.textContent = '';
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
setStatus(status, 'Manifest loaded', true);
|
|
148
|
+
}catch(e){
|
|
149
|
+
setStatus(status, e.message||String(e), false);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
function renderToolInfo(){
|
|
154
|
+
const name = toolSel.value;
|
|
155
|
+
const tool = (manifest.tools||[]).find(t=>t.name===name);
|
|
156
|
+
toolInfo.textContent = tool ? JSON.stringify(tool, null, 2) : '';
|
|
157
|
+
// Prefill common params for convenience
|
|
158
|
+
if(tool && tool.params){
|
|
159
|
+
params.value = JSON.stringify(Object.fromEntries(Object.keys(tool.params.properties||{}).map(k=>[k, null])), null, 2);
|
|
160
|
+
}
|
|
161
|
+
// Hydrate presets UI
|
|
162
|
+
if (name === 'hydrate') {
|
|
163
|
+
params.value = JSON.stringify({}, null, 2);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
toolSel.onchange = renderToolInfo;
|
|
167
|
+
|
|
168
|
+
presetFuzzyUser.onclick = function(){
|
|
169
|
+
toolSel.value = 'fuzzySearch';
|
|
170
|
+
renderToolInfo();
|
|
171
|
+
params.value = JSON.stringify({ q: 'corey hadden', filters: { itemtype: 'user' }, threshold: 0.5 }, null, 2);
|
|
172
|
+
};
|
|
173
|
+
presetFuzzyHouse.onclick = function(){
|
|
174
|
+
toolSel.value = 'fuzzySearch';
|
|
175
|
+
renderToolInfo();
|
|
176
|
+
params.value = JSON.stringify({ q: 'backyard', filters: { itemtype: 'house' }, threshold: 0.5 }, null, 2);
|
|
177
|
+
};
|
|
178
|
+
presetListApps.onclick = function(){
|
|
179
|
+
toolSel.value = 'listApps';
|
|
180
|
+
renderToolInfo();
|
|
181
|
+
params.value = JSON.stringify({}, null, 2);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
presetGetSchemas.onclick = function(){
|
|
185
|
+
ensureTool('getSchemas', {
|
|
186
|
+
name: 'getSchemas',
|
|
187
|
+
description: 'Retrieve multiple schema definitions. If omitted, returns all.',
|
|
188
|
+
params: { type: 'object', properties: { names: { type: 'array', items: { type: 'string' } } } },
|
|
189
|
+
returns: { type: 'object' }
|
|
190
|
+
});
|
|
191
|
+
toolSel.value = 'getSchemas';
|
|
192
|
+
renderToolInfo();
|
|
193
|
+
params.value = JSON.stringify({ names: ["client", "user"] }, null, 2);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
presetGetSchemasSummary.onclick = function(){
|
|
197
|
+
ensureTool('getSchemas', {
|
|
198
|
+
name: 'getSchemas',
|
|
199
|
+
description: 'Retrieve multiple schemas. With summaryOnly=true, returns summaries; if names omitted, returns all.',
|
|
200
|
+
params: { type: 'object', properties: { names: { type: 'array', items: { type: 'string' } }, summaryOnly: { type: 'boolean' } } },
|
|
201
|
+
returns: { type: 'object' }
|
|
202
|
+
});
|
|
203
|
+
toolSel.value = 'getSchemas';
|
|
204
|
+
renderToolInfo();
|
|
205
|
+
params.value = JSON.stringify({ names: ["task", "project"], summaryOnly: true }, null, 2);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
presetSearchSlimRecent.onclick = function(){
|
|
209
|
+
toolSel.value = 'search';
|
|
210
|
+
renderToolInfo();
|
|
211
|
+
params.value = JSON.stringify({ itemtype: 'client', source: 'cache', query: { itemtype: 'client' }, limit: 25, sortBy: 'joeUpdated', sortDir: 'desc', slim: true }, null, 2);
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
presetSearchWithCount.onclick = function(){
|
|
215
|
+
toolSel.value = 'search';
|
|
216
|
+
renderToolInfo();
|
|
217
|
+
params.value = JSON.stringify({ itemtype: 'client', source: 'cache', query: { itemtype: 'client' }, limit: 25, withCount: true, sortBy: 'joeUpdated', sortDir: 'desc' }, null, 2);
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
presetSearchCountOnly.onclick = function(){
|
|
221
|
+
toolSel.value = 'search';
|
|
222
|
+
renderToolInfo();
|
|
223
|
+
params.value = JSON.stringify({ itemtype: 'client', source: 'cache', query: { itemtype: 'client' }, countOnly: true }, null, 2);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
presetSaveObjects.onclick = function(){
|
|
227
|
+
toolSel.value = 'saveObjects';
|
|
228
|
+
renderToolInfo();
|
|
229
|
+
// Example: two minimal objects; adjust itemtype/fields as needed
|
|
230
|
+
params.value = JSON.stringify({
|
|
231
|
+
objects: [
|
|
232
|
+
{ itemtype: "client", name: "Batch Client A" },
|
|
233
|
+
{ itemtype: "client", name: "Batch Client B" }
|
|
234
|
+
],
|
|
235
|
+
stopOnError: false,
|
|
236
|
+
concurrency: 5
|
|
237
|
+
}, null, 2);
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
presetUnderstandObject.onclick = function(){
|
|
241
|
+
toolSel.value = 'understandObject';
|
|
242
|
+
renderToolInfo();
|
|
243
|
+
// Provide a template; user should replace _id with a real object id.
|
|
244
|
+
params.value = JSON.stringify({ _id: "REPLACE_WITH_OBJECT_ID", depth: 2 }, null, 2);
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
presetFindByTag.onclick = function(){
|
|
248
|
+
toolSel.value = 'findObjectsByTag';
|
|
249
|
+
renderToolInfo();
|
|
250
|
+
// Example: find objects with all of these tags
|
|
251
|
+
params.value = JSON.stringify({ tags: ["REPLACE_WITH_TAG_ID_1", "REPLACE_WITH_TAG_ID_2"], itemtype: "task" }, null, 2);
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
presetFindByStatus.onclick = function(){
|
|
255
|
+
toolSel.value = 'findObjectsByStatus';
|
|
256
|
+
renderToolInfo();
|
|
257
|
+
// Example: find objects with this status
|
|
258
|
+
params.value = JSON.stringify({ status: "REPLACE_WITH_STATUS_ID", itemtype: "task" }, null, 2);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
callBtn.onclick = async function(){
|
|
262
|
+
setStatus(callStatus, 'Calling...', null);
|
|
263
|
+
result.textContent='';
|
|
264
|
+
try{
|
|
265
|
+
const url = base.value.replace(/\/$/,'') + '/mcp';
|
|
266
|
+
let p = {};
|
|
267
|
+
try{ p = params.value ? JSON.parse(params.value) : {}; }catch(e){ throw new Error('Invalid JSON in params'); }
|
|
268
|
+
const body = {
|
|
269
|
+
jsonrpc: '2.0',
|
|
270
|
+
id: String(idCounter++),
|
|
271
|
+
method: toolSel.value,
|
|
272
|
+
params: p
|
|
273
|
+
};
|
|
274
|
+
const resp = await fetchJSON(url, { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(body)});
|
|
275
|
+
result.textContent = JSON.stringify(resp, null, 2);
|
|
276
|
+
setStatus(callStatus, 'OK', true);
|
|
277
|
+
}catch(e){
|
|
278
|
+
setStatus(callStatus, e.message||String(e), false);
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
// Auto-load manifest on open
|
|
283
|
+
setTimeout(()=>loadBtn.click(), 50);
|
|
284
|
+
})();
|
|
285
|
+
</script>
|
|
286
|
+
</body>
|
|
287
|
+
</html>
|
package/css/joe-styles.css
CHANGED
|
@@ -2472,6 +2472,12 @@ joe-uploader-file-oaid{
|
|
|
2472
2472
|
height: 20px;
|
|
2473
2473
|
line-height: 20px;
|
|
2474
2474
|
}
|
|
2475
|
+
.joe-uploader-file-role {
|
|
2476
|
+
position: absolute;
|
|
2477
|
+
bottom: -30px;
|
|
2478
|
+
left: 0;
|
|
2479
|
+
right: 0;
|
|
2480
|
+
}
|
|
2475
2481
|
/*-------------------------
|
|
2476
2482
|
COMMENTS
|
|
2477
2483
|
-------------------------*/
|