@pandanpc/mcp-server 0.1.23 → 0.1.25

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/binary-name.js ADDED
@@ -0,0 +1,8 @@
1
+ const pkg = require('./package.json')
2
+
3
+ function binaryName(platform = process.platform, version = pkg.version) {
4
+ const extension = platform === 'win32' ? '.exe' : ''
5
+ return `pandanpc-mcp-v${version}${extension}`
6
+ }
7
+
8
+ module.exports = { binaryName }
package/index.js CHANGED
@@ -7,8 +7,9 @@
7
7
  const path = require('path')
8
8
  const { spawn } = require('child_process')
9
9
  const fs = require('fs')
10
+ const { binaryName } = require('./binary-name')
10
11
 
11
- const binName = process.platform === 'win32' ? 'pandanpc-mcp.exe' : 'pandanpc-mcp'
12
+ const binName = binaryName()
12
13
  const binPath = path.join(__dirname, 'bin', binName)
13
14
 
14
15
  if (!fs.existsSync(binPath)) {
package/install.js CHANGED
@@ -10,6 +10,7 @@ const fs = require('fs')
10
10
  const path = require('path')
11
11
  const https = require('https')
12
12
  const crypto = require('crypto')
13
+ const { binaryName } = require('./binary-name')
13
14
 
14
15
  const pkg = require('./package.json')
15
16
  const VERSION = pkg.version
@@ -66,6 +67,18 @@ function sha256(file) {
66
67
  })
67
68
  }
68
69
 
70
+ function checksumFor(sumsText, artifactName) {
71
+ const line = String(sumsText || '')
72
+ .split('\n')
73
+ .map((entry) => entry.trim().split(/\s+/))
74
+ .find((entry) => entry[1] === artifactName)
75
+ const checksum = line?.[0]?.toLowerCase() || ''
76
+ if (!/^[a-f0-9]{64}$/.test(checksum)) {
77
+ throw new Error(`SHA256SUMS has no valid checksum for ${artifactName}`)
78
+ }
79
+ return checksum
80
+ }
81
+
69
82
  async function fetchText(url) {
70
83
  return new Promise((resolve, reject) => {
71
84
  https.get(url, (res) => {
@@ -80,54 +93,75 @@ async function fetchText(url) {
80
93
  })
81
94
  }
82
95
 
83
- async function main() {
84
- const { key, name } = detectPlatform()
85
- const binDir = path.join(__dirname, 'bin')
86
- if (!fs.existsSync(binDir)) fs.mkdirSync(binDir, { recursive: true })
87
- const localBin = path.join(binDir, process.platform === 'win32' ? 'pandanpc-mcp.exe' : 'pandanpc-mcp')
88
-
89
- // Skip download if binary already exists (e.g. manually placed for development)
90
- if (fs.existsSync(localBin) && fs.statSync(localBin).size > 0) {
91
- console.log(`[pandanpc-mcp] binary already present at ${localBin}, skipping download`)
92
- return
96
+ function removeTemporaryFile(file) {
97
+ try { fs.unlinkSync(file) } catch (error) {
98
+ if (error?.code !== 'ENOENT') throw error
93
99
  }
100
+ }
94
101
 
95
- const url = `${BASE_URL}/${name}`
96
- console.log(`[pandanpc-mcp] downloading ${url} -> ${localBin}`)
102
+ async function ensureBinary({
103
+ key,
104
+ artifactName,
105
+ localBin,
106
+ platform = process.platform,
107
+ baseUrl = BASE_URL,
108
+ fetchTextFn = fetchText,
109
+ downloadFn = download,
110
+ sha256Fn = sha256,
111
+ logger = console,
112
+ }) {
113
+ const sumsUrl = `${baseUrl}/SHA256SUMS`
114
+ const expected = checksumFor(await fetchTextFn(sumsUrl), artifactName)
97
115
 
98
- try {
99
- await download(url, localBin)
100
- } catch (err) {
101
- console.error(`[pandanpc-mcp] download failed: ${err.message}`)
102
- console.error(`[pandanpc-mcp] If you are on an unsupported platform, build from source: https://github.com/pandanpc/mcp-server`)
103
- process.exit(1)
116
+ if (fs.existsSync(localBin) && fs.statSync(localBin).size > 0) {
117
+ const actual = await sha256Fn(localBin)
118
+ if (actual === expected) {
119
+ if (platform !== 'win32') fs.chmodSync(localBin, 0o755)
120
+ logger.log(`[pandanpc-mcp] verified cached binary for ${key}`)
121
+ return { installed: false, checksum: actual }
122
+ }
123
+ logger.warn(`[pandanpc-mcp] cached binary checksum is stale; downloading ${artifactName}`)
104
124
  }
105
125
 
106
- // Verify SHA256 checksum
126
+ const temporaryBin = `${localBin}.download-${process.pid}-${Date.now()}`
127
+ const url = `${baseUrl}/${artifactName}`
128
+ logger.log(`[pandanpc-mcp] downloading ${url} -> ${localBin}`)
129
+
107
130
  try {
108
- const sumsText = await fetchText(`${BASE_URL}/SHA256SUMS`)
109
- const line = sumsText.split('\n').find((l) => l.includes(name))
110
- if (line) {
111
- const expected = line.trim().split(/\s+/)[0]
112
- const actual = await sha256(localBin)
113
- if (expected !== actual) {
114
- fs.unlinkSync(localBin)
115
- throw new Error(`SHA256 mismatch: expected ${expected}, got ${actual}`)
116
- }
117
- console.log(`[pandanpc-mcp] SHA256 verified`)
131
+ await downloadFn(url, temporaryBin)
132
+ const actual = await sha256Fn(temporaryBin)
133
+ if (actual !== expected) {
134
+ throw new Error(`SHA256 mismatch: expected ${expected}, got ${actual}`)
118
135
  }
119
- } catch (err) {
120
- console.warn(`[pandanpc-mcp] SHA256 verification skipped: ${err.message}`)
136
+ if (platform !== 'win32') fs.chmodSync(temporaryBin, 0o755)
137
+ fs.renameSync(temporaryBin, localBin)
138
+ logger.log('[pandanpc-mcp] SHA256 verified')
139
+ logger.log(`[pandanpc-mcp] installed for ${key}`)
140
+ return { installed: true, checksum: actual }
141
+ } catch (error) {
142
+ removeTemporaryFile(temporaryBin)
143
+ throw error
121
144
  }
145
+ }
122
146
 
123
- if (process.platform !== 'win32') {
124
- fs.chmodSync(localBin, 0o755)
125
- }
147
+ async function main() {
148
+ const { key, name: artifactName } = detectPlatform()
149
+ const binDir = path.join(__dirname, 'bin')
150
+ if (!fs.existsSync(binDir)) fs.mkdirSync(binDir, { recursive: true })
151
+ const localBin = path.join(binDir, binaryName())
152
+
153
+ await ensureBinary({ key, artifactName, localBin })
154
+ }
126
155
 
127
- console.log(`[pandanpc-mcp] installed for ${key}`)
156
+ if (require.main === module) {
157
+ main().catch((err) => {
158
+ console.error('[pandanpc-mcp] install failed:', err.message)
159
+ console.error('[pandanpc-mcp] Build from source if the release artifact is unavailable: https://github.com/pandanpc/mcp-server')
160
+ process.exit(1)
161
+ })
128
162
  }
129
163
 
130
- main().catch((err) => {
131
- console.error('[pandanpc-mcp] install failed:', err.message)
132
- process.exit(1)
133
- })
164
+ module.exports = {
165
+ checksumFor,
166
+ ensureBinary,
167
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pandanpc/mcp-server",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "description": "PandaNpc MCP Server — PandaNote and Agent connection management for Codex, Claude Code, and other MCP clients",
5
5
  "keywords": [
6
6
  "mcp",
@@ -20,6 +20,7 @@
20
20
  "pandanpc-mcp": "index.js"
21
21
  },
22
22
  "files": [
23
+ "binary-name.js",
23
24
  "index.js",
24
25
  "install.js",
25
26
  "README.md"