@aborruso/ckan-mcp-server 0.4.7 → 0.4.8
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 +51 -0
- package/PRD.md +339 -252
- package/README.md +4 -0
- package/dist/index.js +87 -10
- package/dist/worker.js +187 -11
- package/package.json +1 -1
- package/web-gui/PRD.md +158 -0
- package/web-gui/public/index.html +883 -0
package/README.md
CHANGED
|
@@ -74,6 +74,8 @@ Use the public Workers endpoint (no local install required):
|
|
|
74
74
|
}
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
+
**NOTE**: This service uses the Cloudflare Workers free tier which has a limit of 100,000 requests per month.
|
|
78
|
+
|
|
77
79
|
Want your own deployment? See [DEPLOYMENT.md](docs/DEPLOYMENT.md).
|
|
78
80
|
|
|
79
81
|
### Claude Desktop Configuration
|
|
@@ -150,6 +152,8 @@ Use the public Cloudflare Workers deployment (no local installation required):
|
|
|
150
152
|
}
|
|
151
153
|
```
|
|
152
154
|
|
|
155
|
+
**NOTE**: This service uses the Cloudflare Workers free tier which has a limit of 100,000 requests per month.
|
|
156
|
+
|
|
153
157
|
**Note**: This uses the public endpoint. You can also deploy your own Workers instance and use that URL instead.
|
|
154
158
|
|
|
155
159
|
## Available Tools
|
package/dist/index.js
CHANGED
|
@@ -411,6 +411,15 @@ ${params.fq ? `**Filter**: ${params.fq}
|
|
|
411
411
|
const sorted = Object.entries(facetValues).sort((a, b) => b[1] - a[1]).slice(0, 10);
|
|
412
412
|
for (const [value, count] of sorted) {
|
|
413
413
|
markdown += `- **${value}**: ${count}
|
|
414
|
+
`;
|
|
415
|
+
}
|
|
416
|
+
if (Object.keys(facetValues).length > sorted.length) {
|
|
417
|
+
markdown += `
|
|
418
|
+
Note: showing top ${sorted.length} only. Use \`response_format: json\` or increase \`facet_limit\`.
|
|
419
|
+
`;
|
|
420
|
+
} else {
|
|
421
|
+
markdown += `
|
|
422
|
+
Note: showing top ${sorted.length} only. Use \`response_format: json\` for full list.
|
|
414
423
|
`;
|
|
415
424
|
}
|
|
416
425
|
markdown += "\n";
|
|
@@ -897,16 +906,84 @@ Returns:
|
|
|
897
906
|
content: [{ type: "text", text: markdown2 }]
|
|
898
907
|
};
|
|
899
908
|
}
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
909
|
+
let result;
|
|
910
|
+
try {
|
|
911
|
+
result = await makeCkanRequest(
|
|
912
|
+
params.server_url,
|
|
913
|
+
"organization_list",
|
|
914
|
+
{
|
|
915
|
+
all_fields: params.all_fields,
|
|
916
|
+
sort: params.sort,
|
|
917
|
+
limit: params.limit,
|
|
918
|
+
offset: params.offset
|
|
919
|
+
}
|
|
920
|
+
);
|
|
921
|
+
} catch (error) {
|
|
922
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
923
|
+
if (message.includes("CKAN API error (500)")) {
|
|
924
|
+
const searchResult = await makeCkanRequest(
|
|
925
|
+
params.server_url,
|
|
926
|
+
"package_search",
|
|
927
|
+
{
|
|
928
|
+
rows: 0,
|
|
929
|
+
"facet.field": JSON.stringify(["organization"]),
|
|
930
|
+
"facet.limit": -1
|
|
931
|
+
}
|
|
932
|
+
);
|
|
933
|
+
const items = searchResult.search_facets?.organization?.items || [];
|
|
934
|
+
const sortValue = params.sort?.toLowerCase() ?? "name asc";
|
|
935
|
+
const sortedItems = [...items].sort((a, b) => {
|
|
936
|
+
if (sortValue.includes("package_count") || sortValue.includes("count")) {
|
|
937
|
+
return b.count - a.count;
|
|
938
|
+
}
|
|
939
|
+
if (sortValue.includes("name desc")) {
|
|
940
|
+
return String(b.name).localeCompare(String(a.name));
|
|
941
|
+
}
|
|
942
|
+
return String(a.name).localeCompare(String(b.name));
|
|
943
|
+
});
|
|
944
|
+
const pagedItems = sortedItems.slice(params.offset, params.offset + params.limit);
|
|
945
|
+
const organizations = pagedItems.map((item) => ({
|
|
946
|
+
id: item.name,
|
|
947
|
+
name: item.name,
|
|
948
|
+
title: item.display_name || item.name,
|
|
949
|
+
package_count: item.count
|
|
950
|
+
}));
|
|
951
|
+
if (params.response_format === "json" /* JSON */) {
|
|
952
|
+
const output = { count: items.length, organizations };
|
|
953
|
+
return {
|
|
954
|
+
content: [{ type: "text", text: truncateText(JSON.stringify(output, null, 2)) }],
|
|
955
|
+
structuredContent: output
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
let markdown2 = `# CKAN Organizations
|
|
959
|
+
|
|
960
|
+
`;
|
|
961
|
+
markdown2 += `**Server**: ${params.server_url}
|
|
962
|
+
`;
|
|
963
|
+
markdown2 += `**Total**: ${items.length}
|
|
964
|
+
`;
|
|
965
|
+
markdown2 += `
|
|
966
|
+
Note: organization_list returned 500; using package_search facets.
|
|
967
|
+
|
|
968
|
+
`;
|
|
969
|
+
for (const org of organizations) {
|
|
970
|
+
markdown2 += `## ${org.title || org.name}
|
|
971
|
+
|
|
972
|
+
`;
|
|
973
|
+
markdown2 += `- **Name**: \`${org.name}\`
|
|
974
|
+
`;
|
|
975
|
+
markdown2 += `- **Datasets**: ${org.package_count || 0}
|
|
976
|
+
`;
|
|
977
|
+
markdown2 += `- **Link**: ${getOrganizationViewUrl(params.server_url, org)}
|
|
978
|
+
|
|
979
|
+
`;
|
|
980
|
+
}
|
|
981
|
+
return {
|
|
982
|
+
content: [{ type: "text", text: truncateText(markdown2) }]
|
|
983
|
+
};
|
|
908
984
|
}
|
|
909
|
-
|
|
985
|
+
throw error;
|
|
986
|
+
}
|
|
910
987
|
if (params.response_format === "json" /* JSON */) {
|
|
911
988
|
const output = Array.isArray(result) ? { count: result.length, organizations: result } : result;
|
|
912
989
|
return {
|
|
@@ -2149,7 +2226,7 @@ function registerAllResources(server2) {
|
|
|
2149
2226
|
function createServer() {
|
|
2150
2227
|
return new McpServer({
|
|
2151
2228
|
name: "ckan-mcp-server",
|
|
2152
|
-
version: "0.4.
|
|
2229
|
+
version: "0.4.8"
|
|
2153
2230
|
});
|
|
2154
2231
|
}
|
|
2155
2232
|
function registerAll(server2) {
|