plm-upload-mcp 1.0.0 → 1.0.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.
Files changed (2) hide show
  1. package/index.mjs +22 -1
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -43,6 +43,13 @@ const MIME = {
43
43
  }
44
44
  const mimeFor = (name) => MIME[extname(name).toLowerCase()] || 'application/octet-stream'
45
45
 
46
+ // Supabase Storage rejects object keys with chars outside its safe set (notably
47
+ // '[' ']', which the common "[SKU] Name" convention starts with) -> the store
48
+ // call 400s with "storage upload failed: 400". The key is internal/UUID-prefixed
49
+ // and never shown, so sanitize it to the safe set; the Drive display name (the
50
+ // separate `upload` filename) keeps the original, brackets and all.
51
+ const storageKeyName = (name) => name.replace(/[^\w !\-.*'()&$@=;:+,?/]/g, '_')
52
+
46
53
  // --- Auth: cache one access token, re-grant when it's near expiry. -----------
47
54
  let cached = { token: null, exp: 0 }
48
55
  async function getToken() {
@@ -80,7 +87,8 @@ async function uploadOne({ path, sku, skuName, category, bucket }) {
80
87
  const mimeType = mimeFor(filename)
81
88
  const token = await getToken()
82
89
 
83
- const params = new URLSearchParams({ action: 'store', sku, filename, mimeType })
90
+ // Store under a storage-safe key; the Drive file keeps the original filename below.
91
+ const params = new URLSearchParams({ action: 'store', sku, filename: storageKeyName(filename), mimeType })
84
92
  const sResp = await fetch(`${EDGE}/drive-library?${params}`, {
85
93
  method: 'POST',
86
94
  headers: { 'content-type': mimeType, authorization: `Bearer ${token}` },
@@ -201,5 +209,18 @@ server.registerTool(
201
209
  },
202
210
  )
203
211
 
212
+ // `node index.mjs --selftest` — verify the storage-key sanitizer without a server.
213
+ if (process.argv.includes('--selftest')) {
214
+ const assert = (c, m) => { if (!c) { console.error('FAIL:', m); process.exit(1) } }
215
+ assert(storageKeyName('[MNT-0930-X0] HANDLEBAR MOUNT LEFT_260908.STEP')
216
+ === '_MNT-0930-X0_ HANDLEBAR MOUNT LEFT_260908.STEP', 'brackets -> _')
217
+ assert(storageKeyName('BRG-0245-X0 Bushing_260908.pdf')
218
+ === 'BRG-0245-X0 Bushing_260908.pdf', 'safe name unchanged')
219
+ const SAFE = /^[\w !\-.*'()&$@=;:+,?/]*$/
220
+ assert(SAFE.test(storageKeyName('a[b]c#d%e\\f"g<h>i.STEP')), 'all unsafe chars replaced')
221
+ console.log('selftest ok')
222
+ process.exit(0)
223
+ }
224
+
204
225
  const transport = new StdioServerTransport()
205
226
  await server.connect(transport)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plm-upload-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "MCP server to bulk-upload SolidWorks exports (STEP/PDF) into the LionsBot PLM by SKU.",
5
5
  "license": "UNLICENSED",
6
6
  "keywords": ["mcp", "modelcontextprotocol", "lionsbot", "plm", "solidworks"],