json-object-editor 0.10.504 → 0.10.506

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 CHANGED
@@ -29,6 +29,15 @@
29
29
  - Added privacy_policy_url and terms_of_service_url to manifest; added public /privacy and /terms pages
30
30
  - New Setting: PRIVACY_CONTACT used for contact email on /privacy and /terms
31
31
 
32
+ 505 - Prompt page and shared nav consolidation
33
+ - Added /mcp-prompt.html (concise starter instructions for Custom GPT/Assistants)
34
+ - Extracted shared nav to /JsonObjectEditor/_www/mcp-nav.js and used across Test/Export/Privacy/Terms
35
+ - Nav now shows instance name/version/host and links to Privacy/Terms (named windows)
36
+
37
+ 506 - Agent context + search count
38
+ - Added concise JOE context block for agent prompts (schema-first decisions, IDs, query strategy, writes, autonomy)
39
+ - search now supports { countOnly: true } to return only a count for large-result checks
40
+
32
41
  ### 0.10.500
33
42
  500 - MCP integration (initial)
34
43
  - MCP core module with JSON-RPC 2.0 endpoint (/mcp) protected by auth
@@ -19,17 +19,10 @@
19
19
  </style>
20
20
  </head>
21
21
  <body>
22
- <nav style="display:flex;gap:10px;align-items:center;margin-bottom:8px">
23
- <a href="/mcp-test.html">MCP Test</a>
24
- <a href="/mcp-export.html">MCP Export</a>
25
- <span style="margin-left:auto"></span>
26
- <a href="/privacy" target="privacy_win" rel="noopener">Privacy</a>
27
- <a href="/terms" target="terms_win" rel="noopener">Terms</a>
28
- </nav>
22
+ <div id="mcp-nav"></div>
23
+ <script src="/JsonObjectEditor/_www/mcp-nav.js"></script>
29
24
  <h1>JOE MCP → Assistant Config Export</h1>
30
- <div class="small">Generate copy/paste config for Custom GPT Actions (OpenAPI) and Assistants (tools array).</div>
31
-
32
- <pre id="instanceInfo" class="small">Loading instance info…</pre>
25
+ <div class="small">Generate copy/paste config for Custom GPT Actions (OpenAPI) and Assistants (tools array).</div>
33
26
 
34
27
  <div class="grid">
35
28
  <section>
@@ -177,9 +170,7 @@
177
170
 
178
171
  const url = base.value.replace(/\/$/,'') + manifestPath.value;
179
172
  const manifest = await fetchJSON(url);
180
- if (manifest.joe){
181
- $('instanceInfo').textContent = `Name: ${manifest.joe.name}\nVersion: ${manifest.joe.version}\nHost: ${manifest.joe.hostname}`;
182
- }
173
+ // Instance info handled by shared nav script
183
174
  manifestOut.style.display='block';
184
175
  manifestOut.textContent = JSON.stringify(manifest, null, 2);
185
176
 
@@ -189,22 +180,24 @@
189
180
  const openapi = buildOpenAPI(manifest, base.value);
190
181
  openapiEl.value = JSON.stringify(openapi, null, 2);
191
182
 
192
- // Starter prompt
183
+ // Starter prompt (concise)
193
184
  const joeName = (manifest.joe && manifest.joe.name) || 'JOE';
194
185
  const starter = [
195
186
  `You are a data assistant for ${joeName} (JOE). Use only the provided tools.`,
196
187
  '',
197
- 'Workflow:',
198
- '1) hydrate {} to see core field definitions, schemas, statuses, tags.',
199
- '2) listSchemas {} and getSchema { "name": "<schema>" } for field shapes.',
200
- '3) search { "query": { "itemtype": "<schema>" }, "limit": 10 } (cache).',
201
- ' Use { "source": "storage" } when results must be authoritative.',
202
- '4) getObject { "_id": "<id>", "schema": "<schema>" } for a single item.',
203
- '5) saveObject only on explicit user request; reflect changes and confirm first.',
188
+ 'Autonomy:',
189
+ '- Act autonomously; if a tool can answer, call it immediately (no permission prompts).',
190
+ '- Do not offer multiple-choice options; pick the best action.',
191
+ '- Ask one brief clarification only if a required parameter is missing.',
192
+ '- On a new session: call hydrate {} first, then proceed.',
193
+ '- Keep results scoped (limit 10–25). Flatten is optional and off by default; enable only when needed.',
194
+ '- Never expose secrets/tokens. Confirm with the user before saveObject.',
204
195
  '',
205
- 'Defaults:',
206
- '- search: cache by default; keep limits small (10–25).',
207
- '- flatten is optional and off by default; enable only if you need expanded refs.',
196
+ 'Typical flow:',
197
+ '- listSchemas {}, getSchema { "name": "<schema>" }',
198
+ '- search { "query": { "itemtype": "<schema>" }, "limit": 10 } (cache) or { "source": "storage" } when authoritative results are needed',
199
+ '- getObject { "_id": "<id>", "schema": "<schema>" } for a single item',
200
+ '- saveObject { "object": { ... } } only on explicit user request',
208
201
  '',
209
202
  'Examples:',
210
203
  '- listSchemas {}',
@@ -0,0 +1,42 @@
1
+ ;(function(){
2
+ function buildNav(){
3
+ var nav = document.createElement('nav');
4
+ nav.setAttribute('style','display:flex;gap:10px;align-items:center;margin-bottom:8px');
5
+ nav.innerHTML = [
6
+ '<a href="/mcp-test.html" target="mcp_test_win" rel="noopener">MCP Test</a>',
7
+ '<a href="/mcp-export.html" target="mcp_export_win" rel="noopener">MCP Export</a>',
8
+ '<a href="/mcp-prompt.html" target="mcp_prompt_win" rel="noopener">MCP Prompt</a>',
9
+ '<span style="margin-left:auto"></span>',
10
+ '<span id="mcp-nav-info" class="small" style="opacity:.8;margin-right:10px"></span>',
11
+ '<a href="/privacy" target="privacy_win" rel="noopener">Privacy</a>',
12
+ '<a href="/terms" target="terms_win" rel="noopener">Terms</a>'
13
+ ].join('');
14
+ return nav;
15
+ }
16
+ function insert(){
17
+ var placeholder = document.getElementById('mcp-nav');
18
+ var nav = buildNav();
19
+ if(placeholder){
20
+ placeholder.parentNode.replaceChild(nav, placeholder);
21
+ }else{
22
+ document.body.insertBefore(nav, document.body.firstChild);
23
+ }
24
+ }
25
+ async function updateInstance(){
26
+ try{
27
+ var res = await fetch('/.well-known/mcp/manifest.json');
28
+ if(!res.ok) return;
29
+ var m = await res.json();
30
+ var info = document.getElementById('mcp-nav-info');
31
+ if(info && m && m.joe){
32
+ info.textContent = 'Name: '+(m.joe.name||'JOE')+' | Version: '+(m.joe.version||'')+' | Host: '+(m.joe.hostname||'');
33
+ }
34
+ }catch(e){}
35
+ }
36
+ if(document.readyState === 'loading'){
37
+ document.addEventListener('DOMContentLoaded', insert);
38
+ document.addEventListener('DOMContentLoaded', updateInstance);
39
+ }else{ insert(); updateInstance(); }
40
+ })();
41
+
42
+
@@ -0,0 +1,29 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>JOE MCP Prompt</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
+ textarea{width:100%;height:360px;font-family:ui-monospace,Menlo,Consolas,monospace}
10
+ .small{font-size:12px;color:#666}
11
+ </style>
12
+ </head>
13
+ <body>
14
+ <div id="mcp-nav"></div>
15
+ <script src="/JsonObjectEditor/_www/mcp-nav.js"></script>
16
+ <h1>Starter Agent Instructions</h1>
17
+ <div class="small">Copy into your Custom GPT or Assistant system prompt. Adjust as needed.</div>
18
+ <textarea readonly id="prompt"></textarea>
19
+ <script>
20
+ (function(){
21
+ const text = [
22
+ `You are a data assistant for JOE. Use only the provided tools.\n\nAutonomy:\n- Act autonomously; if a tool can answer, call it immediately (no permission prompts).\n- Do not offer multiple-choice options; pick the best action.\n- Ask one brief clarification only if a required parameter is missing.\n- On a new session: call hydrate {} first, then proceed.\n- Keep results scoped (limit 10–25). Flatten is optional and off by default; enable only when needed.\n- Never expose secrets/tokens. Confirm with the user before saveObject.\n\nTypical flow:\n- listSchemas {}, getSchema { "name": "<schema>" }\n- search { "query": { "itemtype": "<schema>" }, "limit": 10 } (cache) or { "source": "storage" } for authoritative results\n- getObject { "_id": "<id>", "schema": "<schema>" }\n- saveObject { "object": { ... } } only on explicit user request\n\nExamples:\n- listSchemas {}\n- getSchema { "name": "client" }\n- search { "schema": "client", "source": "storage", "query": { "status": "active" }, "limit": 10 }\n- getObject { "_id": "123", "schema": "client" }`
23
+ ].join('\n');
24
+ document.getElementById('prompt').value = text;
25
+ })();
26
+ </script>
27
+ </body>
28
+ </html>
29
+
@@ -17,16 +17,10 @@
17
17
  </style>
18
18
  </head>
19
19
  <body>
20
- <nav style="display:flex;gap:10px;align-items:center;margin-bottom:8px">
21
- <a href="/mcp-test.html">MCP Test</a>
22
- <a href="/mcp-export.html">MCP Export</a>
23
- <span style="margin-left:auto"></span>
24
- <a href="/privacy" target="privacy_win" rel="noopener">Privacy</a>
25
- <a href="/terms" target="terms_win" rel="noopener">Terms</a>
26
- </nav>
20
+ <div id="mcp-nav"></div>
21
+ <script src="/JsonObjectEditor/_www/mcp-nav.js"></script>
27
22
  <h1>JOE MCP Test</h1>
28
23
  <div class="small">Use this page to discover tools and call the JSON-RPC endpoint.</div>
29
- <pre id="instanceInfo" class="small">Loading instance info…</pre>
30
24
 
31
25
  <h3>Manifest</h3>
32
26
  <div class="row">
@@ -90,10 +84,7 @@
90
84
  try{
91
85
  const url = base.value.replace(/\/$/,'') + '/.well-known/mcp/manifest.json';
92
86
  manifest = await fetchJSON(url);
93
- // Instance info
94
- if (manifest.joe){
95
- $('instanceInfo').textContent = `Name: ${manifest.joe.name} | Version: ${manifest.joe.version} | Host: ${manifest.joe.hostname}`;
96
- }
87
+ // Instance info handled by shared nav script
97
88
  (manifest.tools||[]).forEach(t=>{
98
89
  const opt=document.createElement('option');
99
90
  opt.value=t.name; opt.textContent=t.name;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-object-editor",
3
- "version": "0.10.504",
3
+ "version": "0.10.506",
4
4
  "description": "JOE the Json Object Editor | Platform Edition",
5
5
  "main": "app.js",
6
6
  "scripts": {
@@ -91,7 +91,7 @@ MCP.tools = {
91
91
  */
92
92
 
93
93
  // Unified search: defaults to cache; set source="storage" to query DB for a schema
94
- search: async ({ schema, query = {}, ids, source = 'cache', limit = 50, flatten = false, depth = 1 }, _ctx) => {
94
+ search: async ({ schema, query = {}, ids, source = 'cache', limit = 50, flatten = false, depth = 1, countOnly = false }, _ctx) => {
95
95
  const useCache = !source || source === 'cache';
96
96
  const useStorage = source === 'storage';
97
97
 
@@ -113,6 +113,9 @@ MCP.tools = {
113
113
  if (flatten && JOE && JOE.Utils && JOE.Utils.flattenObject) {
114
114
  try { items = ids.map(id => JOE.Utils.flattenObject(id, { recursive: true, depth })); } catch (e) {}
115
115
  }
116
+ if (countOnly) {
117
+ return { count: (items || []).length };
118
+ }
116
119
  const sliced = (typeof limit === 'number' && limit > 0) ? items.slice(0, limit) : items;
117
120
  return { items: sanitizeItems(sliced) };
118
121
  }
@@ -122,6 +125,9 @@ MCP.tools = {
122
125
  if (!JOE || !JOE.Cache || !JOE.Cache.search) throw new Error('Cache not initialized');
123
126
  let results = JOE.Cache.search(query || {});
124
127
  if (schema) results = (results || []).filter(i => i && i.itemtype === schema);
128
+ if (countOnly) {
129
+ return { count: (results || []).length };
130
+ }
125
131
  const sliced = (typeof limit === 'number' && limit > 0) ? results.slice(0, limit) : results;
126
132
  return { items: sanitizeItems(sliced) };
127
133
  }
@@ -129,6 +135,9 @@ MCP.tools = {
129
135
  if (useStorage) {
130
136
  if (!schema) throw new Error("'schema' is required when source=storage");
131
137
  const results = await loadFromStorage(schema, query || {});
138
+ if (countOnly) {
139
+ return { count: (results || []).length };
140
+ }
132
141
  const sliced = (typeof limit === 'number' && limit > 0) ? (results || []).slice(0, limit) : (results || []);
133
142
  if (flatten && JOE && JOE.Utils && JOE.Utils.flattenObject) {
134
143
  try { return { items: sanitizeItems(sliced.map(it => JOE.Utils.flattenObject(it && it._id, { recursive: true, depth }))) }; } catch (e) {}
@@ -225,7 +234,8 @@ MCP.params = {
225
234
  source: { type: "string", enum: ["cache","storage"] },
226
235
  limit: { type: "integer" },
227
236
  flatten: { type: "boolean" },
228
- depth: { type: "integer" }
237
+ depth: { type: "integer" },
238
+ countOnly: { type: "boolean" }
229
239
  },
230
240
  required: []
231
241
  },
@@ -255,6 +265,7 @@ MCP.returns = {
255
265
  items: { type: "array", items: { type: "object" } }
256
266
  }
257
267
  },
268
+ // When countOnly is true, search returns { count }
258
269
  saveObject: { type: "object" },
259
270
  hydrate: { type: "object" }
260
271
  };
@@ -114,6 +114,8 @@ server.get(['/privacy','/privacy-policy'],function(req,res){
114
114
  <!doctype html>
115
115
  <html><head><meta charset="utf-8"><title>Privacy Policy - ${name}</title></head>
116
116
  <body style="font-family:system-ui,Segoe UI,Roboto,Arial,sans-serif;padding:20px;max-width:900px;line-height:1.6">
117
+ <div id="mcp-nav"></div>
118
+ <script src="${JOE.webconfig.joepath}_www/mcp-nav.js"></script>
117
119
  <h1>Privacy Policy for JOE MCP Interface</h1>
118
120
  <p><em>Last updated: ${updated}</em></p>
119
121
  <h3>Data Processed</h3>
@@ -139,6 +141,8 @@ server.get(['/terms','/terms-of-service'],function(req,res){
139
141
  <!doctype html>
140
142
  <html><head><meta charset="utf-8"><title>Terms of Service - ${name}</title></head>
141
143
  <body style="font-family:system-ui,Segoe UI,Roboto,Arial,sans-serif;padding:20px;max-width:900px;line-height:1.6">
144
+ <div id="mcp-nav"></div>
145
+ <script src="${JOE.webconfig.joepath}_www/mcp-nav.js"></script>
142
146
  <h1>Terms of Service for JOE MCP Interface</h1>
143
147
  <p><em>Last updated: ${updated}</em></p>
144
148
  <p>Use of this interface implies consent to log and process structured data requests for schema and object retrieval purposes. Access is governed by your JOE configuration and authentication. Do not submit sensitive data unless you are authorized to do so.</p>