sbuilder-mcp 0.23.0 → 0.23.1
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 +11 -0
- package/CHANGELOG.vi.md +11 -0
- package/dist/transport/media.js +79 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,17 @@ All notable changes to this project are documented in this file.
|
|
|
6
6
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
7
7
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [0.23.1] - 2026-09-10
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- sb_media_upload now asks the platform to fetch a URL source itself (`POST /api/media/{siteId}/from-url`) before falling back to downloading and re-uploading the bytes, so the file makes one network hop instead of two and its content type comes from the origin's own answer instead of being guessed from a header or extension.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- sb_media_upload now raises rather than working around it when the platform refuses a URL source as `remote_blocked` (an address outside the public internet, such as a private range or the cloud metadata endpoint), since fetching that same URL from this server's own machine would defeat the platform's guard rather than respect it.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- sb_media_upload now reports the platform's own reason when it cannot reach a URL source (for example, "www.blender.org answered 404") instead of the generic "the platform could not read it".
|
|
19
|
+
|
|
9
20
|
## [0.23.0] - 2026-09-10
|
|
10
21
|
|
|
11
22
|
### Added
|
package/CHANGELOG.vi.md
CHANGED
|
@@ -6,6 +6,17 @@ Mọi thay đổi đáng chú ý của dự án được ghi lại trong file n
|
|
|
6
6
|
Định dạng dựa trên [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
7
7
|
và dự án tuân theo [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [0.23.1] - 2026-09-10
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- sb_media_upload giờ yêu cầu chính nền tảng tải nguồn URL trước (`POST /api/media/{siteId}/from-url`) rồi mới lùi về cách cũ là tải xuống rồi upload lại, nên file chỉ đi một chặng mạng thay vì hai, và content type được lấy từ câu trả lời của chính nguồn thay vì đoán từ header hay phần mở rộng.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- sb_media_upload giờ báo lỗi thay vì tìm cách lách khi nền tảng từ chối một nguồn URL với mã `remote_blocked` (địa chỉ không thuộc internet công khai, như dải private hoặc endpoint metadata của cloud), vì việc tự tải URL đó từ máy chạy server này sẽ phá vỡ lớp bảo vệ của nền tảng thay vì tuân theo nó.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- sb_media_upload giờ báo đúng lý do của chính nền tảng khi không tải được một nguồn URL (ví dụ "www.blender.org answered 404") thay vì thông báo chung "the platform could not read it".
|
|
19
|
+
|
|
9
20
|
## [0.23.0] - 2026-09-10
|
|
10
21
|
|
|
11
22
|
### Added
|
package/dist/transport/media.js
CHANGED
|
@@ -41,8 +41,87 @@ function typeForName(name) {
|
|
|
41
41
|
const dot = name.lastIndexOf('.');
|
|
42
42
|
return dot < 0 ? '' : (TYPE_BY_EXT[name.slice(dot).toLowerCase()] ?? '');
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Ask the platform to fetch the URL itself.
|
|
46
|
+
*
|
|
47
|
+
* Returns the asset when the server took it, or null when the ROUTE is not
|
|
48
|
+
* there — an older deployment — which is the one case the caller may answer by
|
|
49
|
+
* downloading the bytes and posting them the old way.
|
|
50
|
+
*
|
|
51
|
+
* A REFUSAL IS TERMINAL, and that is a security rule rather than tidiness. The
|
|
52
|
+
* server refuses an address that is not on the public internet (`remote_blocked`
|
|
53
|
+
* — loopback, private ranges, the cloud metadata endpoint), and a client that
|
|
54
|
+
* answered by fetching that same URL from its OWN machine and uploading the
|
|
55
|
+
* bytes would walk straight around the guard. The agent's network is not the
|
|
56
|
+
* server's, but "the caller does it instead" is exactly the bypass the check
|
|
57
|
+
* exists to prevent, so the error is raised rather than worked around.
|
|
58
|
+
*/
|
|
59
|
+
async function fromUrl(ctx, siteId, url, source) {
|
|
60
|
+
const doFetch = ctx.fetchImpl ?? fetch;
|
|
61
|
+
const res = await doFetch(`${ctx.base.replace(/\/$/, '')}/api/media/${encodeURIComponent(siteId)}/from-url`, {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
headers: {
|
|
64
|
+
Authorization: `Bearer ${siteToken(ctx)}`,
|
|
65
|
+
'Content-Type': 'application/json',
|
|
66
|
+
Accept: 'application/json',
|
|
67
|
+
...identityHeaders(),
|
|
68
|
+
},
|
|
69
|
+
body: JSON.stringify({
|
|
70
|
+
url,
|
|
71
|
+
...(source.name ? { name: source.name } : {}),
|
|
72
|
+
...(source.folderId ? { folderId: source.folderId } : {}),
|
|
73
|
+
}),
|
|
74
|
+
});
|
|
75
|
+
// 404/405 is "this build has no such route". Anything else is an answer.
|
|
76
|
+
if (res.status === 404 || res.status === 405)
|
|
77
|
+
return null;
|
|
78
|
+
const raw = await res.text();
|
|
79
|
+
let parsed = {};
|
|
80
|
+
try {
|
|
81
|
+
parsed = raw ? JSON.parse(raw) : {};
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// A build that routes this path to something else entirely. Fall back rather
|
|
85
|
+
// than reporting a parse error the caller cannot act on.
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
if (!res.ok) {
|
|
89
|
+
const env = (parsed ?? {});
|
|
90
|
+
if (env.code === 'remote_blocked') {
|
|
91
|
+
throw new ApiError(res.status, 'remote_blocked', `sbuilder: the platform refuses to fetch ${url} — it is not an address on the public ` +
|
|
92
|
+
'internet. This server will not fetch it on the platform\'s behalf either: that would ' +
|
|
93
|
+
'walk around the check rather than satisfy it. Give a public URL, or upload the file ' +
|
|
94
|
+
'with a local path.');
|
|
95
|
+
}
|
|
96
|
+
if (env.code === 'remote_unreachable') {
|
|
97
|
+
// THE PLATFORM'S OWN REASON, not a sentence of ours over the top of it. It
|
|
98
|
+
// says which host answered what — a 404 is a wrong URL, a 403 is an origin
|
|
99
|
+
// refusing this fetcher, a timeout is worth retrying — and replacing that
|
|
100
|
+
// with "could not read it" sends all of them to check their network.
|
|
101
|
+
throw new ApiError(res.status, 'remote_unreachable', `sbuilder: the platform could not fetch ${url} — ${env.error ?? 'no reason given'}.`);
|
|
102
|
+
}
|
|
103
|
+
// Any OTHER refusal — 401, 403, 413, an unsupported type — is one the older
|
|
104
|
+
// path answers with its own, better-worded diagnosis. Let it try.
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
const body = (parsed ?? {});
|
|
108
|
+
return body.asset ?? parsed;
|
|
109
|
+
}
|
|
44
110
|
export async function uploadMedia(ctx, siteId, source) {
|
|
45
111
|
const doFetch = ctx.fetchImpl ?? fetch;
|
|
112
|
+
// THE SERVER'S OWN DOOR FIRST, when the source is a URL.
|
|
113
|
+
//
|
|
114
|
+
// `POST /api/media/{siteId}/from-url` fetches it where the platform already
|
|
115
|
+
// guards outbound requests, so the bytes make ONE hop instead of two and the
|
|
116
|
+
// content type is decided by the origin's own answer rather than reconstructed
|
|
117
|
+
// here. Everything below this is the older path, kept because a deployment
|
|
118
|
+
// without that route must still be able to upload — the same shape the partner
|
|
119
|
+
// -surface retry below has, and for the same reason.
|
|
120
|
+
if (source.url) {
|
|
121
|
+
const viaServer = await fromUrl(ctx, siteId, source.url, source);
|
|
122
|
+
if (viaServer)
|
|
123
|
+
return viaServer;
|
|
124
|
+
}
|
|
46
125
|
let bytes;
|
|
47
126
|
let filename;
|
|
48
127
|
let declared = '';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sbuilder-mcp",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.1",
|
|
4
4
|
"description": "MCP server that designs and operates a Store Builder site — pages, data, theme and publish — through the platform's own API and live-edit protocol.",
|
|
5
5
|
"mcpName": "io.github.vuluu2k/sbuilder-mcp",
|
|
6
6
|
"type": "module",
|