ncloud-mcp-server 1.0.0
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/LICENSE +21 -0
- package/README.md +159 -0
- package/README_EN.md +159 -0
- package/dist/auth/signature.d.ts +16 -0
- package/dist/auth/signature.js +18 -0
- package/dist/auth/signature.js.map +1 -0
- package/dist/auth/signature.test.d.ts +1 -0
- package/dist/auth/signature.test.js +90 -0
- package/dist/auth/signature.test.js.map +1 -0
- package/dist/client/ncloud-client.d.ts +27 -0
- package/dist/client/ncloud-client.js +270 -0
- package/dist/client/ncloud-client.js.map +1 -0
- package/dist/client/ncloud-client.test.d.ts +1 -0
- package/dist/client/ncloud-client.test.js +477 -0
- package/dist/client/ncloud-client.test.js.map +1 -0
- package/dist/client/s3-compatible-client.d.ts +49 -0
- package/dist/client/s3-compatible-client.js +210 -0
- package/dist/client/s3-compatible-client.js.map +1 -0
- package/dist/client/swift-compatible-client.d.ts +68 -0
- package/dist/client/swift-compatible-client.js +173 -0
- package/dist/client/swift-compatible-client.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +362 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/acg.d.ts +3 -0
- package/dist/tools/acg.js +220 -0
- package/dist/tools/acg.js.map +1 -0
- package/dist/tools/activity-tracer.d.ts +3 -0
- package/dist/tools/activity-tracer.js +77 -0
- package/dist/tools/activity-tracer.js.map +1 -0
- package/dist/tools/analytics-cdss.d.ts +3 -0
- package/dist/tools/analytics-cdss.js +618 -0
- package/dist/tools/analytics-cdss.js.map +1 -0
- package/dist/tools/analytics-datacatalog.d.ts +10 -0
- package/dist/tools/analytics-datacatalog.js +409 -0
- package/dist/tools/analytics-datacatalog.js.map +1 -0
- package/dist/tools/analytics-dataflow.d.ts +16 -0
- package/dist/tools/analytics-dataflow.js +478 -0
- package/dist/tools/analytics-dataflow.js.map +1 -0
- package/dist/tools/analytics-dataforest.d.ts +10 -0
- package/dist/tools/analytics-dataforest.js +379 -0
- package/dist/tools/analytics-dataforest.js.map +1 -0
- package/dist/tools/analytics-dataquery.d.ts +16 -0
- package/dist/tools/analytics-dataquery.js +143 -0
- package/dist/tools/analytics-dataquery.js.map +1 -0
- package/dist/tools/analytics-datastream.d.ts +13 -0
- package/dist/tools/analytics-datastream.js +359 -0
- package/dist/tools/analytics-datastream.js.map +1 -0
- package/dist/tools/analytics-hadoop.d.ts +12 -0
- package/dist/tools/analytics-hadoop.js +450 -0
- package/dist/tools/analytics-hadoop.js.map +1 -0
- package/dist/tools/analytics-ses.d.ts +3 -0
- package/dist/tools/analytics-ses.js +653 -0
- package/dist/tools/analytics-ses.js.map +1 -0
- package/dist/tools/api-gateway.d.ts +3 -0
- package/dist/tools/api-gateway.js +202 -0
- package/dist/tools/api-gateway.js.map +1 -0
- package/dist/tools/autoscaling.d.ts +3 -0
- package/dist/tools/autoscaling.js +430 -0
- package/dist/tools/autoscaling.js.map +1 -0
- package/dist/tools/billing.d.ts +14 -0
- package/dist/tools/billing.js +545 -0
- package/dist/tools/billing.js.map +1 -0
- package/dist/tools/certificate-manager.d.ts +18 -0
- package/dist/tools/certificate-manager.js +84 -0
- package/dist/tools/certificate-manager.js.map +1 -0
- package/dist/tools/cloud-advisor.d.ts +3 -0
- package/dist/tools/cloud-advisor.js +202 -0
- package/dist/tools/cloud-advisor.js.map +1 -0
- package/dist/tools/cloud-functions.d.ts +3 -0
- package/dist/tools/cloud-functions.js +497 -0
- package/dist/tools/cloud-functions.js.map +1 -0
- package/dist/tools/cloud-insight-integration.d.ts +3 -0
- package/dist/tools/cloud-insight-integration.js +90 -0
- package/dist/tools/cloud-insight-integration.js.map +1 -0
- package/dist/tools/cloud-insight-plugin.d.ts +3 -0
- package/dist/tools/cloud-insight-plugin.js +600 -0
- package/dist/tools/cloud-insight-plugin.js.map +1 -0
- package/dist/tools/cloud-insight-rule.d.ts +3 -0
- package/dist/tools/cloud-insight-rule.js +570 -0
- package/dist/tools/cloud-insight-rule.js.map +1 -0
- package/dist/tools/cloud-insight.d.ts +3 -0
- package/dist/tools/cloud-insight.js +246 -0
- package/dist/tools/cloud-insight.js.map +1 -0
- package/dist/tools/common.d.ts +3 -0
- package/dist/tools/common.js +120 -0
- package/dist/tools/common.js.map +1 -0
- package/dist/tools/compute-initscript.d.ts +3 -0
- package/dist/tools/compute-initscript.js +62 -0
- package/dist/tools/compute-initscript.js.map +1 -0
- package/dist/tools/compute-loginkey.d.ts +3 -0
- package/dist/tools/compute-loginkey.js +59 -0
- package/dist/tools/compute-loginkey.js.map +1 -0
- package/dist/tools/compute-placement.d.ts +3 -0
- package/dist/tools/compute-placement.js +196 -0
- package/dist/tools/compute-placement.js.map +1 -0
- package/dist/tools/compute-publicip.d.ts +3 -0
- package/dist/tools/compute-publicip.js +97 -0
- package/dist/tools/compute-publicip.js.map +1 -0
- package/dist/tools/compute-server.d.ts +3 -0
- package/dist/tools/compute-server.js +445 -0
- package/dist/tools/compute-server.js.map +1 -0
- package/dist/tools/compute-server.test.d.ts +1 -0
- package/dist/tools/compute-server.test.js +224 -0
- package/dist/tools/compute-server.test.js.map +1 -0
- package/dist/tools/compute-storage.d.ts +3 -0
- package/dist/tools/compute-storage.js +240 -0
- package/dist/tools/compute-storage.js.map +1 -0
- package/dist/tools/containers-nks.d.ts +13 -0
- package/dist/tools/containers-nks.js +576 -0
- package/dist/tools/containers-nks.js.map +1 -0
- package/dist/tools/containers-registry.d.ts +3 -0
- package/dist/tools/containers-registry.js +181 -0
- package/dist/tools/containers-registry.js.map +1 -0
- package/dist/tools/database-cache.d.ts +3 -0
- package/dist/tools/database-cache.js +388 -0
- package/dist/tools/database-cache.js.map +1 -0
- package/dist/tools/database-mongodb.d.ts +3 -0
- package/dist/tools/database-mongodb.js +460 -0
- package/dist/tools/database-mongodb.js.map +1 -0
- package/dist/tools/database-mssql.d.ts +3 -0
- package/dist/tools/database-mssql.js +345 -0
- package/dist/tools/database-mssql.js.map +1 -0
- package/dist/tools/database-mysql.d.ts +3 -0
- package/dist/tools/database-mysql.js +500 -0
- package/dist/tools/database-mysql.js.map +1 -0
- package/dist/tools/database-postgresql.d.ts +3 -0
- package/dist/tools/database-postgresql.js +432 -0
- package/dist/tools/database-postgresql.js.map +1 -0
- package/dist/tools/global-dns.d.ts +3 -0
- package/dist/tools/global-dns.js +207 -0
- package/dist/tools/global-dns.js.map +1 -0
- package/dist/tools/global-edge.d.ts +3 -0
- package/dist/tools/global-edge.js +386 -0
- package/dist/tools/global-edge.js.map +1 -0
- package/dist/tools/global-traffic-manager.d.ts +3 -0
- package/dist/tools/global-traffic-manager.js +497 -0
- package/dist/tools/global-traffic-manager.js.map +1 -0
- package/dist/tools/index.d.ts +63 -0
- package/dist/tools/index.js +64 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/kms.d.ts +8 -0
- package/dist/tools/kms.js +529 -0
- package/dist/tools/kms.js.map +1 -0
- package/dist/tools/loadbalancer.d.ts +3 -0
- package/dist/tools/loadbalancer.js +341 -0
- package/dist/tools/loadbalancer.js.map +1 -0
- package/dist/tools/log-analytics.d.ts +3 -0
- package/dist/tools/log-analytics.js +183 -0
- package/dist/tools/log-analytics.js.map +1 -0
- package/dist/tools/media-imageoptimizer.d.ts +3 -0
- package/dist/tools/media-imageoptimizer.js +187 -0
- package/dist/tools/media-imageoptimizer.js.map +1 -0
- package/dist/tools/media-livestation.d.ts +3 -0
- package/dist/tools/media-livestation.js +252 -0
- package/dist/tools/media-livestation.js.map +1 -0
- package/dist/tools/media-vodstation.d.ts +3 -0
- package/dist/tools/media-vodstation.js +197 -0
- package/dist/tools/media-vodstation.js.map +1 -0
- package/dist/tools/nat-gateway.d.ts +3 -0
- package/dist/tools/nat-gateway.js +91 -0
- package/dist/tools/nat-gateway.js.map +1 -0
- package/dist/tools/network-acl.d.ts +3 -0
- package/dist/tools/network-acl.js +322 -0
- package/dist/tools/network-acl.js.map +1 -0
- package/dist/tools/network-interface.d.ts +3 -0
- package/dist/tools/network-interface.js +193 -0
- package/dist/tools/network-interface.js.map +1 -0
- package/dist/tools/private-ca.d.ts +8 -0
- package/dist/tools/private-ca.js +368 -0
- package/dist/tools/private-ca.js.map +1 -0
- package/dist/tools/resource-manager.d.ts +3 -0
- package/dist/tools/resource-manager.js +151 -0
- package/dist/tools/resource-manager.js.map +1 -0
- package/dist/tools/route-table.d.ts +3 -0
- package/dist/tools/route-table.js +205 -0
- package/dist/tools/route-table.js.map +1 -0
- package/dist/tools/security-monitoring.d.ts +3 -0
- package/dist/tools/security-monitoring.js +182 -0
- package/dist/tools/security-monitoring.js.map +1 -0
- package/dist/tools/sens.d.ts +3 -0
- package/dist/tools/sens.js +240 -0
- package/dist/tools/sens.js.map +1 -0
- package/dist/tools/sourcebuild.d.ts +3 -0
- package/dist/tools/sourcebuild.js +481 -0
- package/dist/tools/sourcebuild.js.map +1 -0
- package/dist/tools/sourcecommit.d.ts +13 -0
- package/dist/tools/sourcecommit.js +228 -0
- package/dist/tools/sourcecommit.js.map +1 -0
- package/dist/tools/sourcedeploy.d.ts +3 -0
- package/dist/tools/sourcedeploy.js +448 -0
- package/dist/tools/sourcedeploy.js.map +1 -0
- package/dist/tools/sourcepipeline.d.ts +12 -0
- package/dist/tools/sourcepipeline.js +357 -0
- package/dist/tools/sourcepipeline.js.map +1 -0
- package/dist/tools/storage-archive.d.ts +3 -0
- package/dist/tools/storage-archive.js +337 -0
- package/dist/tools/storage-archive.js.map +1 -0
- package/dist/tools/storage-nas.d.ts +3 -0
- package/dist/tools/storage-nas.js +288 -0
- package/dist/tools/storage-nas.js.map +1 -0
- package/dist/tools/storage-ncloud.d.ts +3 -0
- package/dist/tools/storage-ncloud.js +864 -0
- package/dist/tools/storage-ncloud.js.map +1 -0
- package/dist/tools/storage-object.d.ts +3 -0
- package/dist/tools/storage-object.js +982 -0
- package/dist/tools/storage-object.js.map +1 -0
- package/dist/tools/sub-account.d.ts +3 -0
- package/dist/tools/sub-account.js +311 -0
- package/dist/tools/sub-account.js.map +1 -0
- package/dist/tools/target-group.d.ts +3 -0
- package/dist/tools/target-group.js +207 -0
- package/dist/tools/target-group.js.map +1 -0
- package/dist/tools/vpc-peering.d.ts +3 -0
- package/dist/tools/vpc-peering.js +109 -0
- package/dist/tools/vpc-peering.js.map +1 -0
- package/dist/tools/vpc.d.ts +3 -0
- package/dist/tools/vpc.js +194 -0
- package/dist/tools/vpc.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,864 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Parse S3 XML list buckets response into a structured object.
|
|
4
|
+
*/
|
|
5
|
+
function parseListBucketsXml(xml) {
|
|
6
|
+
const buckets = [];
|
|
7
|
+
const bucketRegex = /<Bucket>\s*<Name>(.*?)<\/Name>\s*<CreationDate>(.*?)<\/CreationDate>\s*<\/Bucket>/gs;
|
|
8
|
+
let match;
|
|
9
|
+
while ((match = bucketRegex.exec(xml)) !== null) {
|
|
10
|
+
buckets.push({ name: match[1], creationDate: match[2] });
|
|
11
|
+
}
|
|
12
|
+
return { buckets };
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Parse ListObjectsV2 XML response into a structured object.
|
|
16
|
+
*/
|
|
17
|
+
function parseListObjectsV2Xml(xml) {
|
|
18
|
+
const nameMatch = xml.match(/<Name>(.*?)<\/Name>/);
|
|
19
|
+
const prefixMatch = xml.match(/<Prefix>(.*?)<\/Prefix>/);
|
|
20
|
+
const keyCountMatch = xml.match(/<KeyCount>(.*?)<\/KeyCount>/);
|
|
21
|
+
const maxKeysMatch = xml.match(/<MaxKeys>(.*?)<\/MaxKeys>/);
|
|
22
|
+
const isTruncatedMatch = xml.match(/<IsTruncated>(.*?)<\/IsTruncated>/);
|
|
23
|
+
const nextTokenMatch = xml.match(/<NextContinuationToken>(.*?)<\/NextContinuationToken>/);
|
|
24
|
+
const contents = [];
|
|
25
|
+
const contentsRegex = /<Contents>\s*<Key>(.*?)<\/Key>\s*<LastModified>(.*?)<\/LastModified>[\s\S]*?<Size>(.*?)<\/Size>\s*<ETag>(.*?)<\/ETag>\s*<StorageClass>(.*?)<\/StorageClass>\s*<\/Contents>/gs;
|
|
26
|
+
let match2;
|
|
27
|
+
while ((match2 = contentsRegex.exec(xml)) !== null) {
|
|
28
|
+
contents.push({
|
|
29
|
+
key: match2[1],
|
|
30
|
+
lastModified: match2[2],
|
|
31
|
+
size: parseInt(match2[3], 10),
|
|
32
|
+
etag: match2[4],
|
|
33
|
+
storageClass: match2[5],
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
const commonPrefixes = [];
|
|
37
|
+
const prefixRegex = /<CommonPrefixes>\s*<Prefix>(.*?)<\/Prefix>\s*<\/CommonPrefixes>/gs;
|
|
38
|
+
let match3;
|
|
39
|
+
while ((match3 = prefixRegex.exec(xml)) !== null) {
|
|
40
|
+
commonPrefixes.push(match3[1]);
|
|
41
|
+
}
|
|
42
|
+
const result = {
|
|
43
|
+
name: nameMatch?.[1] ?? "",
|
|
44
|
+
prefix: prefixMatch?.[1] ?? "",
|
|
45
|
+
keyCount: keyCountMatch ? parseInt(keyCountMatch[1], 10) : 0,
|
|
46
|
+
maxKeys: maxKeysMatch ? parseInt(maxKeysMatch[1], 10) : 1000,
|
|
47
|
+
isTruncated: isTruncatedMatch?.[1] === "true",
|
|
48
|
+
contents,
|
|
49
|
+
commonPrefixes,
|
|
50
|
+
};
|
|
51
|
+
if (nextTokenMatch) {
|
|
52
|
+
result.nextContinuationToken = nextTokenMatch[1];
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Parse S3 XML CORS configuration response into a structured object.
|
|
58
|
+
*/
|
|
59
|
+
function parseCorsConfigXml(xml) {
|
|
60
|
+
const corsRules = [];
|
|
61
|
+
const ruleRegex = /<CORSRule>([\s\S]*?)<\/CORSRule>/g;
|
|
62
|
+
let ruleMatch;
|
|
63
|
+
while ((ruleMatch = ruleRegex.exec(xml)) !== null) {
|
|
64
|
+
const ruleXml = ruleMatch[1];
|
|
65
|
+
const allowedOrigins = [];
|
|
66
|
+
const originRegex = /<AllowedOrigin>(.*?)<\/AllowedOrigin>/g;
|
|
67
|
+
let originMatch;
|
|
68
|
+
while ((originMatch = originRegex.exec(ruleXml)) !== null) {
|
|
69
|
+
allowedOrigins.push(originMatch[1]);
|
|
70
|
+
}
|
|
71
|
+
const allowedMethods = [];
|
|
72
|
+
const methodRegex = /<AllowedMethod>(.*?)<\/AllowedMethod>/g;
|
|
73
|
+
let methodMatch;
|
|
74
|
+
while ((methodMatch = methodRegex.exec(ruleXml)) !== null) {
|
|
75
|
+
allowedMethods.push(methodMatch[1]);
|
|
76
|
+
}
|
|
77
|
+
const allowedHeaders = [];
|
|
78
|
+
const headerRegex = /<AllowedHeader>(.*?)<\/AllowedHeader>/g;
|
|
79
|
+
let headerMatch;
|
|
80
|
+
while ((headerMatch = headerRegex.exec(ruleXml)) !== null) {
|
|
81
|
+
allowedHeaders.push(headerMatch[1]);
|
|
82
|
+
}
|
|
83
|
+
const exposeHeaders = [];
|
|
84
|
+
const exposeRegex = /<ExposeHeader>(.*?)<\/ExposeHeader>/g;
|
|
85
|
+
let exposeMatch;
|
|
86
|
+
while ((exposeMatch = exposeRegex.exec(ruleXml)) !== null) {
|
|
87
|
+
exposeHeaders.push(exposeMatch[1]);
|
|
88
|
+
}
|
|
89
|
+
const maxAgeMatch = ruleXml.match(/<MaxAgeSeconds>(.*?)<\/MaxAgeSeconds>/);
|
|
90
|
+
corsRules.push({
|
|
91
|
+
allowedOrigins,
|
|
92
|
+
allowedMethods,
|
|
93
|
+
allowedHeaders,
|
|
94
|
+
exposeHeaders,
|
|
95
|
+
maxAgeSeconds: maxAgeMatch?.[1],
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return { corsRules };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Build CORS configuration XML from structured input.
|
|
102
|
+
*/
|
|
103
|
+
function buildCorsConfigXml(corsRules) {
|
|
104
|
+
const rulesXml = corsRules.map((rule) => {
|
|
105
|
+
let ruleContent = "";
|
|
106
|
+
for (const origin of rule.allowedOrigins) {
|
|
107
|
+
ruleContent += `<AllowedOrigin>${origin}</AllowedOrigin>`;
|
|
108
|
+
}
|
|
109
|
+
for (const method of rule.allowedMethods) {
|
|
110
|
+
ruleContent += `<AllowedMethod>${method}</AllowedMethod>`;
|
|
111
|
+
}
|
|
112
|
+
if (rule.allowedHeaders) {
|
|
113
|
+
for (const header of rule.allowedHeaders) {
|
|
114
|
+
ruleContent += `<AllowedHeader>${header}</AllowedHeader>`;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (rule.exposeHeaders) {
|
|
118
|
+
for (const header of rule.exposeHeaders) {
|
|
119
|
+
ruleContent += `<ExposeHeader>${header}</ExposeHeader>`;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (rule.maxAgeSeconds !== undefined) {
|
|
123
|
+
ruleContent += `<MaxAgeSeconds>${rule.maxAgeSeconds}</MaxAgeSeconds>`;
|
|
124
|
+
}
|
|
125
|
+
return `<CORSRule>${ruleContent}</CORSRule>`;
|
|
126
|
+
}).join("");
|
|
127
|
+
return `<?xml version="1.0" encoding="UTF-8"?><CORSConfiguration>${rulesXml}</CORSConfiguration>`;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Parse S3 XML lifecycle configuration response into a structured object.
|
|
131
|
+
*/
|
|
132
|
+
function parseLifecycleConfigXml(xml) {
|
|
133
|
+
const rules = [];
|
|
134
|
+
const ruleRegex = /<Rule>([\s\S]*?)<\/Rule>/g;
|
|
135
|
+
let ruleMatch;
|
|
136
|
+
while ((ruleMatch = ruleRegex.exec(xml)) !== null) {
|
|
137
|
+
const ruleXml = ruleMatch[1];
|
|
138
|
+
const idMatch = ruleXml.match(/<ID>(.*?)<\/ID>/);
|
|
139
|
+
const statusMatch = ruleXml.match(/<Status>(.*?)<\/Status>/);
|
|
140
|
+
const prefixMatch = ruleXml.match(/<Prefix>(.*?)<\/Prefix>/);
|
|
141
|
+
// Parse transitions
|
|
142
|
+
const transitions = [];
|
|
143
|
+
const transitionRegex = /<Transition>([\s\S]*?)<\/Transition>/g;
|
|
144
|
+
let transMatch;
|
|
145
|
+
while ((transMatch = transitionRegex.exec(ruleXml)) !== null) {
|
|
146
|
+
const transXml = transMatch[1];
|
|
147
|
+
const daysMatch = transXml.match(/<Days>(.*?)<\/Days>/);
|
|
148
|
+
const dateMatch = transXml.match(/<Date>(.*?)<\/Date>/);
|
|
149
|
+
const storageClassMatch = transXml.match(/<StorageClass>(.*?)<\/StorageClass>/);
|
|
150
|
+
transitions.push({
|
|
151
|
+
days: daysMatch?.[1],
|
|
152
|
+
date: dateMatch?.[1],
|
|
153
|
+
storageClass: storageClassMatch?.[1] ?? "",
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// Parse expiration
|
|
157
|
+
let expiration;
|
|
158
|
+
const expirationMatch = ruleXml.match(/<Expiration>([\s\S]*?)<\/Expiration>/);
|
|
159
|
+
if (expirationMatch) {
|
|
160
|
+
const expXml = expirationMatch[1];
|
|
161
|
+
const expDaysMatch = expXml.match(/<Days>(.*?)<\/Days>/);
|
|
162
|
+
const expDateMatch = expXml.match(/<Date>(.*?)<\/Date>/);
|
|
163
|
+
expiration = {
|
|
164
|
+
days: expDaysMatch?.[1],
|
|
165
|
+
date: expDateMatch?.[1],
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
// Parse abort incomplete multipart upload
|
|
169
|
+
let abortIncompleteMultipartUpload;
|
|
170
|
+
const abortMatch = ruleXml.match(/<AbortIncompleteMultipartUpload>([\s\S]*?)<\/AbortIncompleteMultipartUpload>/);
|
|
171
|
+
if (abortMatch) {
|
|
172
|
+
const daysAfterMatch = abortMatch[1].match(/<DaysAfterInitiation>(.*?)<\/DaysAfterInitiation>/);
|
|
173
|
+
if (daysAfterMatch) {
|
|
174
|
+
abortIncompleteMultipartUpload = { daysAfterInitiation: daysAfterMatch[1] };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
rules.push({
|
|
178
|
+
id: idMatch?.[1] ?? "",
|
|
179
|
+
status: statusMatch?.[1] ?? "",
|
|
180
|
+
prefix: prefixMatch?.[1] ?? "",
|
|
181
|
+
transitions,
|
|
182
|
+
expiration,
|
|
183
|
+
abortIncompleteMultipartUpload,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return { rules };
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Build lifecycle configuration XML from structured input.
|
|
190
|
+
*/
|
|
191
|
+
function buildLifecycleConfigXml(rules) {
|
|
192
|
+
const rulesXml = rules.map((rule) => {
|
|
193
|
+
let ruleContent = "";
|
|
194
|
+
ruleContent += `<ID>${rule.id}</ID>`;
|
|
195
|
+
ruleContent += `<Status>${rule.status ?? "Enabled"}</Status>`;
|
|
196
|
+
// Filter (Prefix-based)
|
|
197
|
+
ruleContent += `<Filter><Prefix>${rule.prefix ?? ""}</Prefix></Filter>`;
|
|
198
|
+
// Transitions
|
|
199
|
+
if (rule.transitions) {
|
|
200
|
+
for (const transition of rule.transitions) {
|
|
201
|
+
ruleContent += "<Transition>";
|
|
202
|
+
if (transition.days !== undefined) {
|
|
203
|
+
ruleContent += `<Days>${transition.days}</Days>`;
|
|
204
|
+
}
|
|
205
|
+
if (transition.date) {
|
|
206
|
+
ruleContent += `<Date>${transition.date}</Date>`;
|
|
207
|
+
}
|
|
208
|
+
ruleContent += `<StorageClass>${transition.storageClass}</StorageClass>`;
|
|
209
|
+
ruleContent += "</Transition>";
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// Expiration
|
|
213
|
+
if (rule.expiration) {
|
|
214
|
+
ruleContent += "<Expiration>";
|
|
215
|
+
if (rule.expiration.days !== undefined) {
|
|
216
|
+
ruleContent += `<Days>${rule.expiration.days}</Days>`;
|
|
217
|
+
}
|
|
218
|
+
if (rule.expiration.date) {
|
|
219
|
+
ruleContent += `<Date>${rule.expiration.date}</Date>`;
|
|
220
|
+
}
|
|
221
|
+
ruleContent += "</Expiration>";
|
|
222
|
+
}
|
|
223
|
+
// AbortIncompleteMultipartUpload
|
|
224
|
+
if (rule.abortIncompleteMultipartUploadDays !== undefined) {
|
|
225
|
+
ruleContent += `<AbortIncompleteMultipartUpload><DaysAfterInitiation>${rule.abortIncompleteMultipartUploadDays}</DaysAfterInitiation></AbortIncompleteMultipartUpload>`;
|
|
226
|
+
}
|
|
227
|
+
return `<Rule>${ruleContent}</Rule>`;
|
|
228
|
+
}).join("");
|
|
229
|
+
return `<?xml version="1.0" encoding="UTF-8"?><LifecycleConfiguration>${rulesXml}</LifecycleConfiguration>`;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Parse S3 XML encryption configuration response into a structured object.
|
|
233
|
+
*/
|
|
234
|
+
function parseEncryptionConfigXml(xml) {
|
|
235
|
+
const rules = [];
|
|
236
|
+
const ruleRegex = /<Rule>([\s\S]*?)<\/Rule>/g;
|
|
237
|
+
let ruleMatch;
|
|
238
|
+
while ((ruleMatch = ruleRegex.exec(xml)) !== null) {
|
|
239
|
+
const ruleXml = ruleMatch[1];
|
|
240
|
+
const algorithmMatch = ruleXml.match(/<SSEAlgorithm>(.*?)<\/SSEAlgorithm>/);
|
|
241
|
+
rules.push({
|
|
242
|
+
applyServerSideEncryptionByDefault: {
|
|
243
|
+
sseAlgorithm: algorithmMatch?.[1] ?? "",
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
return { rules };
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Build encryption configuration XML from algorithm input.
|
|
251
|
+
*/
|
|
252
|
+
function buildEncryptionConfigXml(sseAlgorithm) {
|
|
253
|
+
return `<?xml version="1.0" encoding="UTF-8"?><ServerSideEncryptionConfiguration><Rule><ApplyServerSideEncryptionByDefault><SSEAlgorithm>${sseAlgorithm}</SSEAlgorithm></ApplyServerSideEncryptionByDefault></Rule></ServerSideEncryptionConfiguration>`;
|
|
254
|
+
}
|
|
255
|
+
export function registerStorageNcloudTools(server, client) {
|
|
256
|
+
// ─── Lifecycle Query Tools ─────────────────────────────────────────────────
|
|
257
|
+
server.tool("ncloud_get_bucket_lifecycle", "Get the lifecycle configuration rules for a Ncloud Storage bucket. Returns storage class transition rules, expiration rules, and abort incomplete multipart upload rules.", {
|
|
258
|
+
bucketName: z.string({
|
|
259
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
260
|
+
}).describe("Name of the bucket to retrieve lifecycle configuration for"),
|
|
261
|
+
}, async (params) => {
|
|
262
|
+
try {
|
|
263
|
+
const response = await client.request({
|
|
264
|
+
method: "GET",
|
|
265
|
+
bucket: params.bucketName,
|
|
266
|
+
queryParams: { lifecycle: "" },
|
|
267
|
+
});
|
|
268
|
+
const result = parseLifecycleConfigXml(response.body);
|
|
269
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
// ─── Lifecycle Management Tools ────────────────────────────────────────────
|
|
276
|
+
server.tool("ncloud_put_bucket_lifecycle", "Set lifecycle configuration rules for a Ncloud Storage bucket. Supports storage class transitions (STANDARD, STANDARD_IA, GLACIER) and object expiration. Use dryRun=true to preview the configuration.", {
|
|
277
|
+
bucketName: z.string({
|
|
278
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
279
|
+
}).describe("Name of the bucket to set lifecycle configuration for"),
|
|
280
|
+
rules: z.array(z.object({
|
|
281
|
+
id: z.string().describe("Unique identifier for the rule"),
|
|
282
|
+
status: z.enum(["Enabled", "Disabled"]).optional().default("Enabled").describe("Whether the rule is enabled or disabled"),
|
|
283
|
+
prefix: z.string().optional().default("").describe("Object key prefix to which the rule applies (empty string for all objects)"),
|
|
284
|
+
transitions: z.array(z.object({
|
|
285
|
+
days: z.number().optional().describe("Number of days after object creation to transition"),
|
|
286
|
+
date: z.string().optional().describe("Specific date to transition (ISO 8601 format)"),
|
|
287
|
+
storageClass: z.enum(["STANDARD_IA", "GLACIER"]).describe("Target storage class (STANDARD_IA: Low Frequency, GLACIER: Archive)"),
|
|
288
|
+
})).optional().describe("Storage class transition rules"),
|
|
289
|
+
expiration: z.object({
|
|
290
|
+
days: z.number().optional().describe("Number of days after object creation to expire"),
|
|
291
|
+
date: z.string().optional().describe("Specific date to expire (ISO 8601 format)"),
|
|
292
|
+
}).optional().describe("Object expiration rule"),
|
|
293
|
+
abortIncompleteMultipartUploadDays: z.number().optional().describe("Number of days after which incomplete multipart uploads are aborted"),
|
|
294
|
+
})).min(1, {
|
|
295
|
+
message: "최소 1개 이상의 라이프사이클 규칙이 필요합니다.",
|
|
296
|
+
}).describe("Array of lifecycle rules to apply"),
|
|
297
|
+
dryRun: z.boolean().optional().default(false).describe("If true, returns a preview without actually applying the configuration"),
|
|
298
|
+
}, async (params) => {
|
|
299
|
+
try {
|
|
300
|
+
if (params.dryRun) {
|
|
301
|
+
const preview = {
|
|
302
|
+
label: "🔍 Dry-Run Preview: Bucket Lifecycle Configuration",
|
|
303
|
+
bucketName: params.bucketName,
|
|
304
|
+
rulesCount: params.rules.length,
|
|
305
|
+
rules: params.rules.map((rule) => ({
|
|
306
|
+
id: rule.id,
|
|
307
|
+
status: rule.status,
|
|
308
|
+
prefix: rule.prefix || "(all objects)",
|
|
309
|
+
transitions: rule.transitions?.map((t) => ({
|
|
310
|
+
after: t.days ? `${t.days} days` : t.date,
|
|
311
|
+
targetClass: t.storageClass,
|
|
312
|
+
})),
|
|
313
|
+
expiration: rule.expiration
|
|
314
|
+
? (rule.expiration.days ? `${rule.expiration.days} days` : rule.expiration.date)
|
|
315
|
+
: undefined,
|
|
316
|
+
abortIncompleteMultipartUploadDays: rule.abortIncompleteMultipartUploadDays,
|
|
317
|
+
})),
|
|
318
|
+
message: "이 요청은 실제 라이프사이클 규칙을 적용하지 않습니다. dryRun=false로 호출하면 적용됩니다.",
|
|
319
|
+
};
|
|
320
|
+
return { content: [{ type: "text", text: JSON.stringify(preview, null, 2) }] };
|
|
321
|
+
}
|
|
322
|
+
const xmlBody = buildLifecycleConfigXml(params.rules);
|
|
323
|
+
await client.request({
|
|
324
|
+
method: "PUT",
|
|
325
|
+
bucket: params.bucketName,
|
|
326
|
+
queryParams: { lifecycle: "" },
|
|
327
|
+
headers: { "content-type": "application/xml" },
|
|
328
|
+
body: xmlBody,
|
|
329
|
+
});
|
|
330
|
+
const summary = {
|
|
331
|
+
message: `✅ 버킷 '${params.bucketName}'의 라이프사이클 규칙이 설정되었습니다.`,
|
|
332
|
+
bucket: params.bucketName,
|
|
333
|
+
rulesApplied: params.rules.length,
|
|
334
|
+
rules: params.rules.map((rule) => ({
|
|
335
|
+
id: rule.id,
|
|
336
|
+
status: rule.status,
|
|
337
|
+
prefix: rule.prefix || "(all objects)",
|
|
338
|
+
})),
|
|
339
|
+
};
|
|
340
|
+
return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
|
|
341
|
+
}
|
|
342
|
+
catch (error) {
|
|
343
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
// ─── Lifecycle Delete Tools ────────────────────────────────────────────────
|
|
347
|
+
server.tool("ncloud_delete_bucket_lifecycle", "⚠️ Destructive: Delete all lifecycle configuration rules from a Ncloud Storage bucket. Set confirm=true to execute.", {
|
|
348
|
+
bucketName: z.string({
|
|
349
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
350
|
+
}).describe("Name of the bucket to delete lifecycle configuration from"),
|
|
351
|
+
confirm: z.boolean().optional().default(false).describe("Must be true to actually execute the destructive operation"),
|
|
352
|
+
}, async (params) => {
|
|
353
|
+
try {
|
|
354
|
+
if (!params.confirm) {
|
|
355
|
+
const message = `⚠️ This will permanently delete all lifecycle rules from Bucket [${params.bucketName}]. Do you want to proceed? (yes/no)\n\nTo execute, call this tool again with confirm=true.`;
|
|
356
|
+
return { content: [{ type: "text", text: message }] };
|
|
357
|
+
}
|
|
358
|
+
await client.request({
|
|
359
|
+
method: "DELETE",
|
|
360
|
+
bucket: params.bucketName,
|
|
361
|
+
queryParams: { lifecycle: "" },
|
|
362
|
+
});
|
|
363
|
+
const result = { message: `✅ 버킷 '${params.bucketName}'의 라이프사이클 규칙이 삭제되었습니다.` };
|
|
364
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
// ─── CORS Query Tools ──────────────────────────────────────────────────────
|
|
371
|
+
server.tool("ncloud_get_bucket_cors", "Get the CORS (Cross-Origin Resource Sharing) configuration for a Ncloud Storage bucket. Returns allowed origins, methods, headers, and max age settings.", {
|
|
372
|
+
bucketName: z.string({
|
|
373
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
374
|
+
}).describe("Name of the bucket to retrieve CORS configuration for"),
|
|
375
|
+
}, async (params) => {
|
|
376
|
+
try {
|
|
377
|
+
const response = await client.request({
|
|
378
|
+
method: "GET",
|
|
379
|
+
bucket: params.bucketName,
|
|
380
|
+
queryParams: { cors: "" },
|
|
381
|
+
});
|
|
382
|
+
const result = parseCorsConfigXml(response.body);
|
|
383
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
// ─── CORS Management Tools ─────────────────────────────────────────────────
|
|
390
|
+
server.tool("ncloud_put_bucket_cors", "Set CORS (Cross-Origin Resource Sharing) configuration for a Ncloud Storage bucket. Defines which origins, methods, and headers are allowed for cross-origin requests.", {
|
|
391
|
+
bucketName: z.string({
|
|
392
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
393
|
+
}).describe("Name of the bucket to set CORS configuration for"),
|
|
394
|
+
corsRules: z.array(z.object({
|
|
395
|
+
allowedOrigins: z.array(z.string()).min(1, {
|
|
396
|
+
message: "최소 1개 이상의 allowedOrigins가 필요합니다.",
|
|
397
|
+
}).describe("List of origins allowed to make cross-origin requests (e.g., 'https://example.com' or '*')"),
|
|
398
|
+
allowedMethods: z.array(z.enum(["GET", "PUT", "POST", "DELETE", "HEAD"])).min(1, {
|
|
399
|
+
message: "최소 1개 이상의 allowedMethods가 필요합니다.",
|
|
400
|
+
}).describe("HTTP methods allowed for cross-origin requests"),
|
|
401
|
+
allowedHeaders: z.array(z.string()).optional().describe("Headers allowed in preflight requests (e.g., 'Content-Type', 'Authorization', or '*')"),
|
|
402
|
+
exposeHeaders: z.array(z.string()).optional().describe("Response headers exposed to the browser (e.g., 'x-amz-request-id', 'ETag')"),
|
|
403
|
+
maxAgeSeconds: z.number().optional().describe("Time in seconds the browser can cache preflight response (e.g., 3600)"),
|
|
404
|
+
})).min(1, {
|
|
405
|
+
message: "최소 1개 이상의 CORS 규칙이 필요합니다.",
|
|
406
|
+
}).describe("Array of CORS rules to apply to the bucket"),
|
|
407
|
+
}, async (params) => {
|
|
408
|
+
try {
|
|
409
|
+
const xmlBody = buildCorsConfigXml(params.corsRules);
|
|
410
|
+
await client.request({
|
|
411
|
+
method: "PUT",
|
|
412
|
+
bucket: params.bucketName,
|
|
413
|
+
queryParams: { cors: "" },
|
|
414
|
+
headers: { "content-type": "application/xml" },
|
|
415
|
+
body: xmlBody,
|
|
416
|
+
});
|
|
417
|
+
const summary = {
|
|
418
|
+
message: `✅ 버킷 '${params.bucketName}'의 CORS 설정이 적용되었습니다.`,
|
|
419
|
+
bucket: params.bucketName,
|
|
420
|
+
rulesApplied: params.corsRules.length,
|
|
421
|
+
rules: params.corsRules.map((rule, index) => ({
|
|
422
|
+
ruleIndex: index + 1,
|
|
423
|
+
allowedOrigins: rule.allowedOrigins,
|
|
424
|
+
allowedMethods: rule.allowedMethods,
|
|
425
|
+
allowedHeaders: rule.allowedHeaders ?? [],
|
|
426
|
+
exposeHeaders: rule.exposeHeaders ?? [],
|
|
427
|
+
maxAgeSeconds: rule.maxAgeSeconds,
|
|
428
|
+
})),
|
|
429
|
+
};
|
|
430
|
+
return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
|
|
431
|
+
}
|
|
432
|
+
catch (error) {
|
|
433
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
// ─── CORS Delete Tools ─────────────────────────────────────────────────────
|
|
437
|
+
server.tool("ncloud_delete_bucket_cors", "⚠️ Destructive: Delete the CORS configuration from a Ncloud Storage bucket. This will remove all cross-origin access rules. Set confirm=true to execute.", {
|
|
438
|
+
bucketName: z.string({
|
|
439
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
440
|
+
}).describe("Name of the bucket to delete CORS configuration from"),
|
|
441
|
+
confirm: z.boolean().optional().default(false).describe("Must be true to actually execute the destructive operation"),
|
|
442
|
+
}, async (params) => {
|
|
443
|
+
try {
|
|
444
|
+
if (!params.confirm) {
|
|
445
|
+
const message = `⚠️ This will permanently delete all CORS rules from Bucket [${params.bucketName}]. Do you want to proceed? (yes/no)\n\nTo execute, call this tool again with confirm=true.`;
|
|
446
|
+
return { content: [{ type: "text", text: message }] };
|
|
447
|
+
}
|
|
448
|
+
await client.request({
|
|
449
|
+
method: "DELETE",
|
|
450
|
+
bucket: params.bucketName,
|
|
451
|
+
queryParams: { cors: "" },
|
|
452
|
+
});
|
|
453
|
+
const result = { message: `✅ 버킷 '${params.bucketName}'의 CORS 설정이 삭제되었습니다.` };
|
|
454
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
455
|
+
}
|
|
456
|
+
catch (error) {
|
|
457
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
// ─── Encryption Query Tools ────────────────────────────────────────────────
|
|
461
|
+
server.tool("ncloud_get_bucket_encryption", "Get the default server-side encryption (SSE) configuration for a Ncloud Storage bucket. Returns the encryption algorithm applied to new objects by default.", {
|
|
462
|
+
bucketName: z.string({
|
|
463
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
464
|
+
}).describe("Name of the bucket to retrieve encryption configuration for"),
|
|
465
|
+
}, async (params) => {
|
|
466
|
+
try {
|
|
467
|
+
const response = await client.request({
|
|
468
|
+
method: "GET",
|
|
469
|
+
bucket: params.bucketName,
|
|
470
|
+
queryParams: { encryption: "" },
|
|
471
|
+
});
|
|
472
|
+
const result = parseEncryptionConfigXml(response.body);
|
|
473
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
474
|
+
}
|
|
475
|
+
catch (error) {
|
|
476
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
// ─── Encryption Management Tools ───────────────────────────────────────────
|
|
480
|
+
server.tool("ncloud_put_bucket_encryption", "Set the default server-side encryption (SSE) configuration for a Ncloud Storage bucket. All new objects will be encrypted with the specified algorithm.", {
|
|
481
|
+
bucketName: z.string({
|
|
482
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
483
|
+
}).describe("Name of the bucket to set encryption configuration for"),
|
|
484
|
+
sseAlgorithm: z.enum(["AES256"], {
|
|
485
|
+
required_error: "필수 파라미터 'sseAlgorithm'이 누락되었습니다.",
|
|
486
|
+
}).describe("Server-side encryption algorithm (AES256)"),
|
|
487
|
+
}, async (params) => {
|
|
488
|
+
try {
|
|
489
|
+
const xmlBody = buildEncryptionConfigXml(params.sseAlgorithm);
|
|
490
|
+
await client.request({
|
|
491
|
+
method: "PUT",
|
|
492
|
+
bucket: params.bucketName,
|
|
493
|
+
queryParams: { encryption: "" },
|
|
494
|
+
headers: { "content-type": "application/xml" },
|
|
495
|
+
body: xmlBody,
|
|
496
|
+
});
|
|
497
|
+
const summary = {
|
|
498
|
+
message: `✅ 버킷 '${params.bucketName}'의 기본 암호화가 '${params.sseAlgorithm}'로 설정되었습니다.`,
|
|
499
|
+
bucket: params.bucketName,
|
|
500
|
+
sseAlgorithm: params.sseAlgorithm,
|
|
501
|
+
};
|
|
502
|
+
return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
|
|
503
|
+
}
|
|
504
|
+
catch (error) {
|
|
505
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
// ─── Encryption Delete Tools ───────────────────────────────────────────────
|
|
509
|
+
server.tool("ncloud_delete_bucket_encryption", "⚠️ Destructive: Delete the default server-side encryption (SSE) configuration from a Ncloud Storage bucket. New objects will no longer be encrypted by default. Set confirm=true to execute.", {
|
|
510
|
+
bucketName: z.string({
|
|
511
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
512
|
+
}).describe("Name of the bucket to delete encryption configuration from"),
|
|
513
|
+
confirm: z.boolean().optional().default(false).describe("Must be true to actually execute the destructive operation"),
|
|
514
|
+
}, async (params) => {
|
|
515
|
+
try {
|
|
516
|
+
if (!params.confirm) {
|
|
517
|
+
const message = `⚠️ This will permanently delete the default encryption configuration from Bucket [${params.bucketName}]. New objects will no longer be encrypted by default. Do you want to proceed? (yes/no)\n\nTo execute, call this tool again with confirm=true.`;
|
|
518
|
+
return { content: [{ type: "text", text: message }] };
|
|
519
|
+
}
|
|
520
|
+
await client.request({
|
|
521
|
+
method: "DELETE",
|
|
522
|
+
bucket: params.bucketName,
|
|
523
|
+
queryParams: { encryption: "" },
|
|
524
|
+
});
|
|
525
|
+
const result = { message: `✅ 버킷 '${params.bucketName}'의 기본 암호화 설정이 삭제되었습니다.` };
|
|
526
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
527
|
+
}
|
|
528
|
+
catch (error) {
|
|
529
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
533
|
+
// Ncloud Storage Basic CRUD Tools (Bucket & Object)
|
|
534
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
535
|
+
// ─── List Buckets ──────────────────────────────────────────────────────────
|
|
536
|
+
server.tool("ncloud_ncs_list_buckets", "List all Ncloud Storage buckets in the current region", {}, async () => {
|
|
537
|
+
try {
|
|
538
|
+
const response = await client.request({ method: "GET" });
|
|
539
|
+
const result = parseListBucketsXml(response.body);
|
|
540
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
541
|
+
}
|
|
542
|
+
catch (error) {
|
|
543
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
// ─── Create Bucket ─────────────────────────────────────────────────────────
|
|
547
|
+
server.tool("ncloud_ncs_create_bucket", "Create a new Ncloud Storage bucket. Use dryRun=true to preview.", {
|
|
548
|
+
bucketName: z.string({
|
|
549
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
550
|
+
}).describe("Name of the bucket to create"),
|
|
551
|
+
dryRun: z.boolean().optional().default(true).describe("If true (default), returns a preview without actually creating the bucket"),
|
|
552
|
+
}, async (params) => {
|
|
553
|
+
try {
|
|
554
|
+
if (params.dryRun) {
|
|
555
|
+
const preview = {
|
|
556
|
+
label: "🔍 Dry-Run Preview: Ncloud Storage Bucket Creation",
|
|
557
|
+
bucketName: params.bucketName,
|
|
558
|
+
region: client.getRegionCode(),
|
|
559
|
+
message: "이 요청은 실제 버킷을 생성하지 않습니다. dryRun=false로 호출하면 생성됩니다.",
|
|
560
|
+
};
|
|
561
|
+
return { content: [{ type: "text", text: JSON.stringify(preview, null, 2) }] };
|
|
562
|
+
}
|
|
563
|
+
await client.request({ method: "PUT", bucket: params.bucketName });
|
|
564
|
+
const summary = {
|
|
565
|
+
리소스타입: "Ncloud Storage Bucket",
|
|
566
|
+
리소스명: params.bucketName,
|
|
567
|
+
리전: client.getRegionCode(),
|
|
568
|
+
상태: "created",
|
|
569
|
+
};
|
|
570
|
+
return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
|
|
571
|
+
}
|
|
572
|
+
catch (error) {
|
|
573
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
// ─── Delete Bucket ─────────────────────────────────────────────────────────
|
|
577
|
+
server.tool("ncloud_ncs_delete_bucket", "⚠️ Destructive: Permanently delete a Ncloud Storage bucket. The bucket must be empty. Set confirm=true to execute.", {
|
|
578
|
+
bucketName: z.string({
|
|
579
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
580
|
+
}).describe("Name of the bucket to delete"),
|
|
581
|
+
confirm: z.boolean().optional().default(false).describe("Must be true to actually execute the destructive operation"),
|
|
582
|
+
}, async (params) => {
|
|
583
|
+
try {
|
|
584
|
+
if (!params.confirm) {
|
|
585
|
+
const message = `⚠️ This will permanently delete Ncloud Storage Bucket [${params.bucketName}]. The bucket must be empty. Do you want to proceed? (yes/no)\n\nTo execute, call this tool again with confirm=true.`;
|
|
586
|
+
return { content: [{ type: "text", text: message }] };
|
|
587
|
+
}
|
|
588
|
+
await client.request({ method: "DELETE", bucket: params.bucketName });
|
|
589
|
+
const result = { message: `✅ Ncloud Storage 버킷 '${params.bucketName}'이(가) 삭제되었습니다.` };
|
|
590
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
591
|
+
}
|
|
592
|
+
catch (error) {
|
|
593
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
// ─── Head Bucket ───────────────────────────────────────────────────────────
|
|
597
|
+
server.tool("ncloud_ncs_head_bucket", "Check if a Ncloud Storage bucket exists and retrieve its metadata (region, access permissions)", {
|
|
598
|
+
bucketName: z.string({
|
|
599
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
600
|
+
}).describe("Name of the bucket to check"),
|
|
601
|
+
}, async (params) => {
|
|
602
|
+
try {
|
|
603
|
+
const response = await client.request({ method: "HEAD", bucket: params.bucketName });
|
|
604
|
+
const result = {
|
|
605
|
+
bucket: params.bucketName,
|
|
606
|
+
exists: true,
|
|
607
|
+
statusCode: response.status,
|
|
608
|
+
region: response.headers.get("x-amz-bucket-region") ?? client.getRegionCode(),
|
|
609
|
+
};
|
|
610
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
611
|
+
}
|
|
612
|
+
catch (error) {
|
|
613
|
+
if (error.message.includes("404")) {
|
|
614
|
+
const result = {
|
|
615
|
+
bucket: params.bucketName,
|
|
616
|
+
exists: false,
|
|
617
|
+
statusCode: 404,
|
|
618
|
+
};
|
|
619
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
620
|
+
}
|
|
621
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
// ─── List Objects (V2) ─────────────────────────────────────────────────────
|
|
625
|
+
server.tool("ncloud_ncs_list_objects", "List objects in a Ncloud Storage bucket", {
|
|
626
|
+
bucketName: z.string({
|
|
627
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
628
|
+
}).describe("Name of the bucket"),
|
|
629
|
+
prefix: z.string().optional().describe("Limits results to keys beginning with this prefix"),
|
|
630
|
+
delimiter: z.string().optional().describe("Delimiter for grouping keys (commonly '/')"),
|
|
631
|
+
maxKeys: z.number().optional().describe("Maximum number of keys to return (default 1000)"),
|
|
632
|
+
continuationToken: z.string().optional().describe("Token for pagination (from previous response's nextContinuationToken)"),
|
|
633
|
+
}, async (params) => {
|
|
634
|
+
try {
|
|
635
|
+
const queryParams = { "list-type": "2" };
|
|
636
|
+
if (params.prefix)
|
|
637
|
+
queryParams["prefix"] = params.prefix;
|
|
638
|
+
if (params.delimiter)
|
|
639
|
+
queryParams["delimiter"] = params.delimiter;
|
|
640
|
+
if (params.maxKeys)
|
|
641
|
+
queryParams["max-keys"] = String(params.maxKeys);
|
|
642
|
+
if (params.continuationToken)
|
|
643
|
+
queryParams["continuation-token"] = params.continuationToken;
|
|
644
|
+
const response = await client.request({
|
|
645
|
+
method: "GET",
|
|
646
|
+
bucket: params.bucketName,
|
|
647
|
+
queryParams,
|
|
648
|
+
});
|
|
649
|
+
const result = parseListObjectsV2Xml(response.body);
|
|
650
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
651
|
+
}
|
|
652
|
+
catch (error) {
|
|
653
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
// ─── Put Object ────────────────────────────────────────────────────────────
|
|
657
|
+
server.tool("ncloud_ncs_put_object", "Upload (put) an object to a Ncloud Storage bucket. Use dryRun=true to preview.", {
|
|
658
|
+
bucketName: z.string({
|
|
659
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
660
|
+
}).describe("Name of the bucket"),
|
|
661
|
+
key: z.string({
|
|
662
|
+
required_error: "필수 파라미터 'key'가 누락되었습니다.",
|
|
663
|
+
}).describe("Object key (path) to upload to"),
|
|
664
|
+
body: z.string({
|
|
665
|
+
required_error: "필수 파라미터 'body'가 누락되었습니다.",
|
|
666
|
+
}).describe("Content to upload as the object body"),
|
|
667
|
+
contentType: z.string().optional().describe("Content-Type header for the object (e.g., 'text/plain', 'application/json')"),
|
|
668
|
+
dryRun: z.boolean().optional().default(true).describe("If true (default), returns a preview without actually uploading"),
|
|
669
|
+
}, async (params) => {
|
|
670
|
+
try {
|
|
671
|
+
if (params.dryRun) {
|
|
672
|
+
const preview = {
|
|
673
|
+
label: "🔍 Dry-Run Preview: Ncloud Storage Object Upload",
|
|
674
|
+
bucketName: params.bucketName,
|
|
675
|
+
key: params.key,
|
|
676
|
+
contentType: params.contentType ?? "application/octet-stream",
|
|
677
|
+
bodySize: `${params.body.length} bytes`,
|
|
678
|
+
message: "이 요청은 실제 오브젝트를 업로드하지 않습니다. dryRun=false로 호출하면 업로드됩니다.",
|
|
679
|
+
};
|
|
680
|
+
return { content: [{ type: "text", text: JSON.stringify(preview, null, 2) }] };
|
|
681
|
+
}
|
|
682
|
+
const headers = {};
|
|
683
|
+
if (params.contentType) {
|
|
684
|
+
headers["content-type"] = params.contentType;
|
|
685
|
+
}
|
|
686
|
+
await client.request({
|
|
687
|
+
method: "PUT",
|
|
688
|
+
bucket: params.bucketName,
|
|
689
|
+
key: params.key,
|
|
690
|
+
headers,
|
|
691
|
+
body: params.body,
|
|
692
|
+
});
|
|
693
|
+
const summary = {
|
|
694
|
+
리소스타입: "Ncloud Storage Object",
|
|
695
|
+
버킷: params.bucketName,
|
|
696
|
+
키: params.key,
|
|
697
|
+
크기: `${params.body.length} bytes`,
|
|
698
|
+
상태: "uploaded",
|
|
699
|
+
};
|
|
700
|
+
return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
|
|
701
|
+
}
|
|
702
|
+
catch (error) {
|
|
703
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
704
|
+
}
|
|
705
|
+
});
|
|
706
|
+
// ─── Get Object ────────────────────────────────────────────────────────────
|
|
707
|
+
server.tool("ncloud_ncs_get_object", "Get (download) an object from a Ncloud Storage bucket. Returns the object content as text.", {
|
|
708
|
+
bucketName: z.string({
|
|
709
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
710
|
+
}).describe("Name of the bucket"),
|
|
711
|
+
key: z.string({
|
|
712
|
+
required_error: "필수 파라미터 'key'가 누락되었습니다.",
|
|
713
|
+
}).describe("Object key (path) to retrieve"),
|
|
714
|
+
}, async (params) => {
|
|
715
|
+
try {
|
|
716
|
+
const response = await client.request({
|
|
717
|
+
method: "GET",
|
|
718
|
+
bucket: params.bucketName,
|
|
719
|
+
key: params.key,
|
|
720
|
+
});
|
|
721
|
+
const result = {
|
|
722
|
+
bucket: params.bucketName,
|
|
723
|
+
key: params.key,
|
|
724
|
+
contentLength: response.headers.get("content-length"),
|
|
725
|
+
contentType: response.headers.get("content-type"),
|
|
726
|
+
lastModified: response.headers.get("last-modified"),
|
|
727
|
+
body: response.body,
|
|
728
|
+
};
|
|
729
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
730
|
+
}
|
|
731
|
+
catch (error) {
|
|
732
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
// ─── Head Object ───────────────────────────────────────────────────────────
|
|
736
|
+
server.tool("ncloud_ncs_head_object", "Get metadata of an object in a Ncloud Storage bucket without downloading the body", {
|
|
737
|
+
bucketName: z.string({
|
|
738
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
739
|
+
}).describe("Name of the bucket"),
|
|
740
|
+
key: z.string({
|
|
741
|
+
required_error: "필수 파라미터 'key'가 누락되었습니다.",
|
|
742
|
+
}).describe("Object key (path) to check"),
|
|
743
|
+
}, async (params) => {
|
|
744
|
+
try {
|
|
745
|
+
const response = await client.request({
|
|
746
|
+
method: "HEAD",
|
|
747
|
+
bucket: params.bucketName,
|
|
748
|
+
key: params.key,
|
|
749
|
+
});
|
|
750
|
+
const result = {
|
|
751
|
+
bucket: params.bucketName,
|
|
752
|
+
key: params.key,
|
|
753
|
+
contentLength: response.headers.get("content-length"),
|
|
754
|
+
contentType: response.headers.get("content-type"),
|
|
755
|
+
lastModified: response.headers.get("last-modified"),
|
|
756
|
+
etag: response.headers.get("etag"),
|
|
757
|
+
statusCode: response.status,
|
|
758
|
+
};
|
|
759
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
760
|
+
}
|
|
761
|
+
catch (error) {
|
|
762
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
// ─── Copy Object ───────────────────────────────────────────────────────────
|
|
766
|
+
server.tool("ncloud_ncs_copy_object", "Copy an object within Ncloud Storage", {
|
|
767
|
+
bucketName: z.string({
|
|
768
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
769
|
+
}).describe("Destination bucket name"),
|
|
770
|
+
key: z.string({
|
|
771
|
+
required_error: "필수 파라미터 'key'가 누락되었습니다.",
|
|
772
|
+
}).describe("Destination object key (path)"),
|
|
773
|
+
copySource: z.string({
|
|
774
|
+
required_error: "필수 파라미터 'copySource'가 누락되었습니다.",
|
|
775
|
+
}).describe("Source object path in format: /{sourceBucket}/{sourceKey}"),
|
|
776
|
+
}, async (params) => {
|
|
777
|
+
try {
|
|
778
|
+
const response = await client.request({
|
|
779
|
+
method: "PUT",
|
|
780
|
+
bucket: params.bucketName,
|
|
781
|
+
key: params.key,
|
|
782
|
+
headers: {
|
|
783
|
+
"x-amz-copy-source": params.copySource,
|
|
784
|
+
},
|
|
785
|
+
});
|
|
786
|
+
const result = {
|
|
787
|
+
리소스타입: "Ncloud Storage Object Copy",
|
|
788
|
+
대상버킷: params.bucketName,
|
|
789
|
+
대상키: params.key,
|
|
790
|
+
복사원본: params.copySource,
|
|
791
|
+
상태: "copied",
|
|
792
|
+
response: response.body,
|
|
793
|
+
};
|
|
794
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
795
|
+
}
|
|
796
|
+
catch (error) {
|
|
797
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
798
|
+
}
|
|
799
|
+
});
|
|
800
|
+
// ─── Delete Object ─────────────────────────────────────────────────────────
|
|
801
|
+
server.tool("ncloud_ncs_delete_object", "⚠️ Destructive: Permanently delete an object from a Ncloud Storage bucket. Set confirm=true to execute.", {
|
|
802
|
+
bucketName: z.string({
|
|
803
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
804
|
+
}).describe("Name of the bucket"),
|
|
805
|
+
key: z.string({
|
|
806
|
+
required_error: "필수 파라미터 'key'가 누락되었습니다.",
|
|
807
|
+
}).describe("Object key (path) to delete"),
|
|
808
|
+
confirm: z.boolean().optional().default(false).describe("Must be true to actually execute the destructive operation"),
|
|
809
|
+
}, async (params) => {
|
|
810
|
+
try {
|
|
811
|
+
if (!params.confirm) {
|
|
812
|
+
const message = `⚠️ This will permanently delete Object [${params.bucketName}/${params.key}] from Ncloud Storage. Do you want to proceed? (yes/no)\n\nTo execute, call this tool again with confirm=true.`;
|
|
813
|
+
return { content: [{ type: "text", text: message }] };
|
|
814
|
+
}
|
|
815
|
+
await client.request({
|
|
816
|
+
method: "DELETE",
|
|
817
|
+
bucket: params.bucketName,
|
|
818
|
+
key: params.key,
|
|
819
|
+
});
|
|
820
|
+
const result = { message: `✅ Ncloud Storage 오브젝트 '${params.bucketName}/${params.key}'이(가) 삭제되었습니다.` };
|
|
821
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
822
|
+
}
|
|
823
|
+
catch (error) {
|
|
824
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
825
|
+
}
|
|
826
|
+
});
|
|
827
|
+
// ─── Delete Objects (Multi) ────────────────────────────────────────────────
|
|
828
|
+
server.tool("ncloud_ncs_delete_objects", "⚠️ Destructive: Permanently delete multiple objects from a Ncloud Storage bucket. Set confirm=true to execute.", {
|
|
829
|
+
bucketName: z.string({
|
|
830
|
+
required_error: "필수 파라미터 'bucketName'이 누락되었습니다.",
|
|
831
|
+
}).describe("Name of the bucket"),
|
|
832
|
+
keys: z.array(z.string(), {
|
|
833
|
+
required_error: "필수 파라미터 'keys'가 누락되었습니다.",
|
|
834
|
+
}).describe("Array of object keys to delete"),
|
|
835
|
+
confirm: z.boolean().optional().default(false).describe("Must be true to actually execute the destructive operation"),
|
|
836
|
+
}, async (params) => {
|
|
837
|
+
try {
|
|
838
|
+
if (!params.confirm) {
|
|
839
|
+
const message = `⚠️ This will permanently delete ${params.keys.length} objects from Ncloud Storage Bucket [${params.bucketName}]:\n${params.keys.map((k) => ` - ${k}`).join("\n")}\n\nDo you want to proceed? (yes/no)\n\nTo execute, call this tool again with confirm=true.`;
|
|
840
|
+
return { content: [{ type: "text", text: message }] };
|
|
841
|
+
}
|
|
842
|
+
const objectsXml = params.keys.map((k) => `<Object><Key>${k}</Key></Object>`).join("");
|
|
843
|
+
const xmlBody = `<?xml version="1.0" encoding="UTF-8"?><Delete><Quiet>false</Quiet>${objectsXml}</Delete>`;
|
|
844
|
+
const response = await client.request({
|
|
845
|
+
method: "POST",
|
|
846
|
+
bucket: params.bucketName,
|
|
847
|
+
queryParams: { delete: "" },
|
|
848
|
+
headers: {
|
|
849
|
+
"content-type": "application/xml",
|
|
850
|
+
},
|
|
851
|
+
body: xmlBody,
|
|
852
|
+
});
|
|
853
|
+
const result = {
|
|
854
|
+
message: `✅ Ncloud Storage 버킷 '${params.bucketName}'에서 ${params.keys.length}개 오브젝트가 삭제되었습니다.`,
|
|
855
|
+
response: response.body,
|
|
856
|
+
};
|
|
857
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
858
|
+
}
|
|
859
|
+
catch (error) {
|
|
860
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
//# sourceMappingURL=storage-ncloud.js.map
|