Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,10 @@ dxt unsign my-extension.dxt
For signing extensions, you need:

1. **Certificate**: X.509 certificate in PEM format

- Should have Code Signing extended key usage
- Can be self-signed (for development) or CA-issued (for production)

2. **Private Key**: Corresponding private key in PEM format

- Must match the certificate's public key

3. **Intermediate Certificates** (optional): For CA-issued certificates
Expand Down
2 changes: 0 additions & 2 deletions MANIFEST.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,14 +290,12 @@ The `server` object defines how to run the MCP server:
### Server Types

1. **Python**: `server.type = "python"`

- Requires `entry_point` to Python file
- All dependencies must be bundled in the DXT
- Can use `server/lib` for packages or `server/venv` for full virtual environment
- Python runtime version specified in `compatibility.runtimes.python`

2. **Node.js**: `server.type = "node"`

- Requires `entry_point` to JavaScript file
- All dependencies must be bundled in `node_modules`
- Node.js runtime version specified in `compatibility.runtimes.node`
Expand Down
8 changes: 2 additions & 6 deletions examples/file-system-node/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,7 @@
]
}
},
"keywords": [
"api",
"automation",
"productivity"
],
"keywords": ["api", "automation", "productivity"],
"license": "MIT",
"compatibility": {
"claude_desktop": ">=0.10.0",
Expand All @@ -94,4 +90,4 @@
"default": []
}
}
}
}
2 changes: 1 addition & 1 deletion examples/file-system-node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
"dependencies": {
"@modelcontextprotocol/server-filesystem": "2025.1.14"
}
}
}
15 changes: 9 additions & 6 deletions examples/file-system-node/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,23 @@ const loadServer = async () => {
try {
// Save original argv
const originalArgv = process.argv;

// The filesystem server expects directories as command line arguments
process.argv = [process.argv[0], process.argv[1], ...args];

// Dynamically import the ESM module
await import('@modelcontextprotocol/server-filesystem/dist/index.js');
await import("@modelcontextprotocol/server-filesystem/dist/index.js");

// Restore original argv
process.argv = originalArgv;
} catch (error) {
console.error('Failed to load @modelcontextprotocol/server-filesystem:', error);
console.error(
"Failed to load @modelcontextprotocol/server-filesystem:",
error,
);
process.exit(1);
}
};

// Execute the async function
loadServer();
loadServer();
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default {
"^.+\\.ts$": [
"ts-jest",
{
tsConfig: 'tsconfig.test.json'
tsConfig: "tsconfig.test.json",
},
],
},
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,6 @@
"resolutions": {
"@babel/helpers": "7.27.1",
"@babel/parser": "7.27.3"
}
},
"packageManager": "[email protected]+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
7 changes: 3 additions & 4 deletions scripts/build-dxt-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import fs from "node:fs/promises";
import path from "node:path";

const schemasToWrite = {
'dxt-manifest': DxtManifestSchema,
'dxt-signature-info': DxtSignatureInfoSchema
}
"dxt-manifest": DxtManifestSchema,
"dxt-signature-info": DxtSignatureInfoSchema,
};

await fs.mkdir(path.join(import.meta.dirname, "../dist"), { recursive: true });

Expand All @@ -20,4 +20,3 @@ for (const key in schemasToWrite) {
},
);
}

24 changes: 17 additions & 7 deletions src/cli/unpack.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { unzipSync } from "fflate";
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
import {
chmodSync,
existsSync,
mkdirSync,
readFileSync,
writeFileSync,
} from "fs";
import { join, resolve, sep } from "path";

import { extractSignatureBlock } from "../node/sign.js";
Expand Down Expand Up @@ -41,7 +47,7 @@ export async function unpackExtension({
if (isUnix) {
// Parse ZIP central directory to extract file attributes
const zipBuffer = originalContent;

// Find end of central directory record
let eocdOffset = -1;
for (let i = zipBuffer.length - 22; i >= 0; i--) {
Expand All @@ -54,21 +60,25 @@ export async function unpackExtension({
if (eocdOffset !== -1) {
const centralDirOffset = zipBuffer.readUInt32LE(eocdOffset + 16);
const centralDirEntries = zipBuffer.readUInt16LE(eocdOffset + 8);

let offset = centralDirOffset;

for (let i = 0; i < centralDirEntries; i++) {
if (zipBuffer.readUInt32LE(offset) === 0x02014b50) {
const externalAttrs = zipBuffer.readUInt32LE(offset + 38);
const filenameLength = zipBuffer.readUInt16LE(offset + 28);
const filename = zipBuffer.toString('utf8', offset + 46, offset + 46 + filenameLength);

const filename = zipBuffer.toString(
"utf8",
offset + 46,
offset + 46 + filenameLength,
);

// Extract Unix permissions from external attributes (upper 16 bits)
const mode = (externalAttrs >> 16) & 0o777;
if (mode > 0) {
fileAttributes.set(filename, mode);
}

const extraFieldLength = zipBuffer.readUInt16LE(offset + 30);
const commentLength = zipBuffer.readUInt16LE(offset + 32);
offset += 46 + filenameLength + extraFieldLength + commentLength;
Expand Down
33 changes: 22 additions & 11 deletions test/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,10 +204,13 @@ describe("DXT CLI", () => {
},
}),
);

// Create an executable script
const executableScript = join(tempExecDir, "run-script.sh");
fs.writeFileSync(executableScript, "#!/bin/bash\necho 'Hello from executable'");
fs.writeFileSync(
executableScript,
"#!/bin/bash\necho 'Hello from executable'",
);
fs.chmodSync(executableScript, 0o755); // Make it executable

// Create a regular file for comparison
Expand All @@ -221,25 +224,33 @@ describe("DXT CLI", () => {
});

// Unpack the extension
execSync(`node ${cliPath} unpack ${execPackedFilePath} ${execUnpackedDir}`, {
encoding: "utf-8",
});
execSync(
`node ${cliPath} unpack ${execPackedFilePath} ${execUnpackedDir}`,
{
encoding: "utf-8",
},
);

// Check that the executable file preserved its permissions
const originalStats = fs.statSync(executableScript);
const unpackedStats = fs.statSync(join(execUnpackedDir, "run-script.sh"));

const unpackedStats = fs.statSync(
join(execUnpackedDir, "run-script.sh"),
);

// Check that executable permissions are preserved (0o755)
expect(unpackedStats.mode & 0o777).toBe(0o755);
expect(originalStats.mode & 0o777).toBe(unpackedStats.mode & 0o777);

// Check that regular file permissions are preserved (0o644)
const originalRegularStats = fs.statSync(regularFile);
const unpackedRegularStats = fs.statSync(join(execUnpackedDir, "regular-file.txt"));

expect(unpackedRegularStats.mode & 0o777).toBe(0o644);
expect(originalRegularStats.mode & 0o777).toBe(unpackedRegularStats.mode & 0o777);
const unpackedRegularStats = fs.statSync(
join(execUnpackedDir, "regular-file.txt"),
);

expect(unpackedRegularStats.mode & 0o777).toBe(0o644);
expect(originalRegularStats.mode & 0o777).toBe(
unpackedRegularStats.mode & 0o777,
);
} finally {
// Clean up
fs.rmSync(tempExecDir, { recursive: true, force: true });
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.test.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"noEmit": true,
"module": "node18",
"moduleResolution": "node16",
"types": ["node", "jest"],
"types": ["node", "jest"]
},
"include": ["src/**/*.ts", "test/**/*.ts"]
}
Loading