@aborruso/ckan-mcp-server 0.4.87 → 0.4.88
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/LOG.md +10 -0
- package/dist/index.js +10 -7
- package/dist/worker.js +3 -3
- package/package.json +1 -1
package/LOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# LOG
|
|
2
2
|
|
|
3
|
+
## 2026-03-19
|
|
4
|
+
|
|
5
|
+
### v0.4.88
|
|
6
|
+
|
|
7
|
+
- fix(`tools/organization.ts`): `ckan_organization_search` now shows `view_url` in markdown table and JSON output; `ckan_organization_show` JSON includes `view_url` — all using `portals.json` custom patterns
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
- evals(`evals/tool-selection`): tool selection eval pipeline — synthetic NL query generation (Gemini, 1583 records), train/eval split, fine-tuning Qwen2.5-0.5B with Unsloth on Colab T4; 86.3% accuracy on 8 tools; model published at huggingface.co/aborruso/ckan-tool-selector
|
|
12
|
+
|
|
3
13
|
## 2026-03-17
|
|
4
14
|
|
|
5
15
|
- fix(`tools/sparql.ts`): add `; charset=utf-8` to POST Content-Type — fixes accented chars corruption in SPARQL queries (issue #22)
|
package/dist/index.js
CHANGED
|
@@ -1919,7 +1919,7 @@ function compactOrganizationList(result) {
|
|
|
1919
1919
|
})
|
|
1920
1920
|
};
|
|
1921
1921
|
}
|
|
1922
|
-
function compactOrganizationShow(result) {
|
|
1922
|
+
function compactOrganizationShow(result, serverUrl) {
|
|
1923
1923
|
return {
|
|
1924
1924
|
id: result.id,
|
|
1925
1925
|
name: result.name,
|
|
@@ -1928,6 +1928,7 @@ function compactOrganizationShow(result) {
|
|
|
1928
1928
|
image_url: result.image_url || null,
|
|
1929
1929
|
package_count: result.package_count ?? 0,
|
|
1930
1930
|
created: result.created || null,
|
|
1931
|
+
view_url: getOrganizationViewUrl(serverUrl, result),
|
|
1931
1932
|
packages: (result.packages || []).map((pkg) => ({
|
|
1932
1933
|
id: pkg.id,
|
|
1933
1934
|
name: pkg.name,
|
|
@@ -2174,7 +2175,7 @@ Typical workflow: ckan_organization_show \u2192 ckan_package_show (inspect a dat
|
|
|
2174
2175
|
}
|
|
2175
2176
|
);
|
|
2176
2177
|
if (params.response_format === "json" /* JSON */) {
|
|
2177
|
-
const compact = compactOrganizationShow(result);
|
|
2178
|
+
const compact = compactOrganizationShow(result, params.server_url);
|
|
2178
2179
|
return {
|
|
2179
2180
|
content: [{ type: "text", text: truncateJson(compact) }],
|
|
2180
2181
|
structuredContent: compact
|
|
@@ -2251,7 +2252,8 @@ Typical workflow: ckan_organization_search \u2192 ckan_organization_show (get de
|
|
|
2251
2252
|
organizations: orgFacets.map((item) => ({
|
|
2252
2253
|
name: item.name,
|
|
2253
2254
|
display_name: item.display_name,
|
|
2254
|
-
dataset_count: item.count
|
|
2255
|
+
dataset_count: item.count,
|
|
2256
|
+
view_url: getOrganizationViewUrl(params.server_url, { name: item.name })
|
|
2255
2257
|
}))
|
|
2256
2258
|
};
|
|
2257
2259
|
return {
|
|
@@ -2281,12 +2283,13 @@ Typical workflow: ckan_organization_search \u2192 ckan_organization_show (get de
|
|
|
2281
2283
|
markdown += `## Matching Organizations
|
|
2282
2284
|
|
|
2283
2285
|
`;
|
|
2284
|
-
markdown += `| Organization | Datasets |
|
|
2286
|
+
markdown += `| Organization | Datasets | Link |
|
|
2285
2287
|
`;
|
|
2286
|
-
markdown +=
|
|
2288
|
+
markdown += `|--------------|----------|------|
|
|
2287
2289
|
`;
|
|
2288
2290
|
for (const org of orgFacets) {
|
|
2289
|
-
|
|
2291
|
+
const viewUrl = getOrganizationViewUrl(params.server_url, { name: org.name });
|
|
2292
|
+
markdown += `| ${org.display_name || org.name} | ${org.count} | ${viewUrl} |
|
|
2290
2293
|
`;
|
|
2291
2294
|
}
|
|
2292
2295
|
}
|
|
@@ -5124,7 +5127,7 @@ var registerAllPrompts = (server2) => {
|
|
|
5124
5127
|
function createServer() {
|
|
5125
5128
|
return new McpServer({
|
|
5126
5129
|
name: "ckan-mcp-server",
|
|
5127
|
-
version: "0.4.
|
|
5130
|
+
version: "0.4.88"
|
|
5128
5131
|
});
|
|
5129
5132
|
}
|
|
5130
5133
|
function registerAll(server2) {
|
package/dist/worker.js
CHANGED
|
@@ -825,7 +825,7 @@ Returns:
|
|
|
825
825
|
Total dataset count, categories ranked by count, file formats ranked by count, organizations ranked by count.
|
|
826
826
|
|
|
827
827
|
Typical workflow: ckan_catalog_stats (understand the portal) \u2192 ckan_package_search (query specific data)`,inputSchema:v.object({server_url:v.string().url().describe("Base URL of the CKAN server (e.g., https://dati.comune.messina.it)"),facet_limit:v.number().int().min(1).max(100).optional().default(20).describe("Max entries per facet section (default 20)"),response_format:fe}).strict(),annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},async e=>{try{let r=await F(e.server_url,"package_search",{q:"*:*",rows:0,"facet.field":JSON.stringify(["groups","res_format","organization"]),"facet.limit":e.facet_limit});if(e.response_format==="json")return{content:[{type:"text",text:W(JSON.stringify({total:r.count,facets:r.facets},null,2))}]};let n=DO(e.server_url,r.count,r.facets);return{content:[{type:"text",text:W(se(n))}]}}catch(r){return{content:[{type:"text",text:`Error retrieving catalog stats: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}})}var Na=25,Yf=1e3;function ZO(t){let e=t.replace(/#[^\n]*/g,"");if(!/\bSELECT\b/i.test(e))throw new Error("Only SELECT queries are supported (not CONSTRUCT, ASK, DESCRIBE, or write operations).")}function LO(t,e){return/\bLIMIT\b/i.test(t)?t:`${t.trimEnd()}
|
|
828
|
-
LIMIT ${e}`}async function UO(t,e){if(Hf(t),new URL(t).protocol!=="https:")throw new Error("Only HTTPS endpoints are allowed");let o=Av(t)?.method??"POST",s={Accept:"application/sparql-results+json","User-Agent":"Mozilla/5.0 (compatible; CKAN-MCP-Server/1.0)"},i=new AbortController,a=setTimeout(()=>i.abort(),15e3),c;try{if(o==="GET"){let u=new URL(t);u.searchParams.set("query",e),c=await fetch(u.toString(),{method:"GET",signal:i.signal,headers:s})}else if(c=await fetch(t,{method:"POST",signal:i.signal,headers:{...s,"Content-Type":"application/sparql-query"},body:e}),c.status===403||c.status===405){let u=new URL(t);u.searchParams.set("query",e),c=await fetch(u.toString(),{method:"GET",signal:i.signal,headers:s})}}finally{clearTimeout(a)}if(!c.ok)throw new Error(`SPARQL endpoint error (${c.status}): ${c.statusText}`);return c.json()}function FO(t,e){let r=t.head.vars,n=t.results.bindings,o=`# SPARQL Query Results
|
|
828
|
+
LIMIT ${e}`}async function UO(t,e){if(Hf(t),new URL(t).protocol!=="https:")throw new Error("Only HTTPS endpoints are allowed");let o=Av(t)?.method??"POST",s={Accept:"application/sparql-results+json","User-Agent":"Mozilla/5.0 (compatible; CKAN-MCP-Server/1.0)"},i=new AbortController,a=setTimeout(()=>i.abort(),15e3),c;try{if(o==="GET"){let u=new URL(t);u.searchParams.set("query",e),c=await fetch(u.toString(),{method:"GET",signal:i.signal,headers:s})}else if(c=await fetch(t,{method:"POST",signal:i.signal,headers:{...s,"Content-Type":"application/sparql-query; charset=utf-8"},body:e}),c.status===403||c.status===405){let u=new URL(t);u.searchParams.set("query",e),c=await fetch(u.toString(),{method:"GET",signal:i.signal,headers:s})}}finally{clearTimeout(a)}if(!c.ok)throw new Error(`SPARQL endpoint error (${c.status}): ${c.statusText}`);return c.json()}function FO(t,e){let r=t.head.vars,n=t.results.bindings,o=`# SPARQL Query Results
|
|
829
829
|
|
|
830
830
|
`;if(o+=`**Endpoint**: ${e}
|
|
831
831
|
`,o+=`**Rows**: ${n.length}
|
|
@@ -1086,7 +1086,7 @@ ckan_package_search({
|
|
|
1086
1086
|
})
|
|
1087
1087
|
\`\`\`
|
|
1088
1088
|
|
|
1089
|
-
If the portal supports HVD classification, look for datasets with fields like \`hvd_category\` or \`applicable_legislation\`.`,$b=t=>{t.registerPrompt(aC,{title:"Search High-Value Datasets (HVD)",description:"Guided prompt to find High-Value Datasets (HVD) on a CKAN portal. Automatically uses the correct filter field from portal configuration.",argsSchema:{server_url:v.string().url().describe("Base URL of the CKAN server"),rows:v.coerce.number().int().positive().default(10).describe("Max results to return")}},async({server_url:e,rows:r})=>{let n=so(e);return ht(cC(e,r,n?.category_field??null))})};var Sb=t=>{vb(t),bb(t),wb(t),xb(t),kb(t),$b(t)};function zb(){return new ma({name:"ckan-mcp-server",version:"0.4.
|
|
1089
|
+
If the portal supports HVD classification, look for datasets with fields like \`hvd_category\` or \`applicable_legislation\`.`,$b=t=>{t.registerPrompt(aC,{title:"Search High-Value Datasets (HVD)",description:"Guided prompt to find High-Value Datasets (HVD) on a CKAN portal. Automatically uses the correct filter field from portal configuration.",argsSchema:{server_url:v.string().url().describe("Base URL of the CKAN server"),rows:v.coerce.number().int().positive().default(10).describe("Max results to return")}},async({server_url:e,rows:r})=>{let n=so(e);return ht(cC(e,r,n?.category_field??null))})};var Sb=t=>{vb(t),bb(t),wb(t),xb(t),kb(t),$b(t)};function zb(){return new ma({name:"ckan-mcp-server",version:"0.4.87"})}function Tb(t){Kv(t),Jv(t),Gv(t),Qv(t),Yv(t),eb(t),ib(t),ab(t),cb(t),ub(t),lb(t),_b(t),Sb(t)}var ja=class{constructor(e={}){this._started=!1,this._streamMapping=new Map,this._requestToStreamMapping=new Map,this._requestResponseMap=new Map,this._initialized=!1,this._enableJsonResponse=!1,this._standaloneSseStreamId="_GET_stream",this.sessionIdGenerator=e.sessionIdGenerator,this._enableJsonResponse=e.enableJsonResponse??!1,this._eventStore=e.eventStore,this._onsessioninitialized=e.onsessioninitialized,this._onsessionclosed=e.onsessionclosed,this._allowedHosts=e.allowedHosts,this._allowedOrigins=e.allowedOrigins,this._enableDnsRebindingProtection=e.enableDnsRebindingProtection??!1,this._retryInterval=e.retryInterval}async start(){if(this._started)throw new Error("Transport already started");this._started=!0}createJsonErrorResponse(e,r,n,o){let s={code:r,message:n};return o?.data!==void 0&&(s.data=o.data),new Response(JSON.stringify({jsonrpc:"2.0",error:s,id:null}),{status:e,headers:{"Content-Type":"application/json",...o?.headers}})}validateRequestHeaders(e){if(this._enableDnsRebindingProtection){if(this._allowedHosts&&this._allowedHosts.length>0){let r=e.headers.get("host");if(!r||!this._allowedHosts.includes(r)){let n=`Invalid Host header: ${r}`;return this.onerror?.(new Error(n)),this.createJsonErrorResponse(403,-32e3,n)}}if(this._allowedOrigins&&this._allowedOrigins.length>0){let r=e.headers.get("origin");if(r&&!this._allowedOrigins.includes(r)){let n=`Invalid Origin header: ${r}`;return this.onerror?.(new Error(n)),this.createJsonErrorResponse(403,-32e3,n)}}}}async handleRequest(e,r){let n=this.validateRequestHeaders(e);if(n)return n;switch(e.method){case"POST":return this.handlePostRequest(e,r);case"GET":return this.handleGetRequest(e);case"DELETE":return this.handleDeleteRequest(e);default:return this.handleUnsupportedRequest()}}async writePrimingEvent(e,r,n,o){if(!this._eventStore||o<"2025-11-25")return;let s=await this._eventStore.storeEvent(n,{}),i=`id: ${s}
|
|
1090
1090
|
data:
|
|
1091
1091
|
|
|
1092
1092
|
`;this._retryInterval!==void 0&&(i=`id: ${s}
|
|
@@ -1280,4 +1280,4 @@ data:
|
|
|
1280
1280
|
</div>
|
|
1281
1281
|
</div>
|
|
1282
1282
|
</body>
|
|
1283
|
-
</html>`,{headers:{"Content-Type":"text/html; charset=utf-8","Access-Control-Allow-Origin":"*"}});if(t.method==="GET"&&e.pathname==="/health")return new Response(JSON.stringify({status:"ok",version:"0.4.
|
|
1283
|
+
</html>`,{headers:{"Content-Type":"text/html; charset=utf-8","Access-Control-Allow-Origin":"*"}});if(t.method==="GET"&&e.pathname==="/health")return new Response(JSON.stringify({status:"ok",version:"0.4.87",tools:20,resources:7,prompts:6,runtime:"cloudflare-workers"}),{headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}});if(e.pathname==="/mcp"){if(t.method==="OPTIONS")return new Response(null,{status:204,headers:{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"GET, POST, DELETE, OPTIONS","Access-Control-Allow-Headers":"Content-Type, Accept, Authorization, Mcp-Session-Id","Access-Control-Max-Age":"86400"}});try{let r=t.clone();try{let s=await r.json();if(s?.method==="tools/call"&&s?.params?.name){let i=s.params.name,a=s.params.arguments??{},c={tool:i,server:a.server_url??a.endpoint_url??""};a.q!==void 0&&(c.q=a.q),a.fq!==void 0&&(c.fq=a.fq),a.query!==void 0&&(c.query=a.query),a.id!==void 0&&(c.id=a.id),a.name!==void 0&&(c.name=a.name),a.pattern!==void 0&&(c.pattern=a.pattern),a.resource_id!==void 0&&(c.resource_id=a.resource_id),a.format_filter!==void 0&&(c.format_filter=a.format_filter),a.sort!==void 0&&(c.sort=a.sort),a.rows!==void 0&&(c.rows=a.rows),a.limit!==void 0&&(c.limit=a.limit),a.sql!==void 0&&(c.sql=String(a.sql).slice(0,200)),a.country!==void 0&&(c.country=a.country),a.language!==void 0&&(c.language=a.language),a.has_datastore!==void 0&&(c.has_datastore=a.has_datastore),a.min_datasets!==void 0&&(c.min_datasets=a.min_datasets),console.log(JSON.stringify(c))}}catch{}let n=await Eb.handleRequest(t),o=new Headers(n.headers);return o.set("Access-Control-Allow-Origin","*"),o.set("X-Service-Notice","Demo instance - 100k requests/day shared quota"),o.set("X-Recommendation","https://github.com/ondata/ckan-mcp-server#installation"),new Response(n.body,{status:n.status,statusText:n.statusText,headers:o})}catch(r){return console.error("Worker error:",r),new Response(JSON.stringify({jsonrpc:"2.0",error:{code:-32603,message:"Internal error",data:r instanceof Error?r.message:String(r)},id:null}),{status:500,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}}return new Response("Not Found",{status:404,headers:{"Access-Control-Allow-Origin":"*"}})}};export{oF as default};
|
package/package.json
CHANGED