@ebowwa/mcp-nm 2.0.1 → 2.0.2
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/dist/index.js +135 -0
- package/package.json +1 -1
- package/src/index.ts +178 -0
package/dist/index.js
CHANGED
|
@@ -14069,6 +14069,112 @@ async function handleXxdFindPattern(args) {
|
|
|
14069
14069
|
`);
|
|
14070
14070
|
return { content: [{ type: "text", text: summary }] };
|
|
14071
14071
|
}
|
|
14072
|
+
async function handleConvertNumber(args) {
|
|
14073
|
+
let num;
|
|
14074
|
+
const value = args.value.trim().toLowerCase();
|
|
14075
|
+
try {
|
|
14076
|
+
switch (args.fromFormat) {
|
|
14077
|
+
case "hex":
|
|
14078
|
+
num = BigInt(value.startsWith("0x") ? value : `0x${value}`);
|
|
14079
|
+
break;
|
|
14080
|
+
case "decimal":
|
|
14081
|
+
num = BigInt(value);
|
|
14082
|
+
break;
|
|
14083
|
+
case "binary":
|
|
14084
|
+
num = BigInt(value.startsWith("0b") ? value.slice(2) : value, 2);
|
|
14085
|
+
break;
|
|
14086
|
+
case "octal":
|
|
14087
|
+
num = BigInt(value.startsWith("0o") ? value.slice(2) : value, 8);
|
|
14088
|
+
break;
|
|
14089
|
+
case "auto":
|
|
14090
|
+
default:
|
|
14091
|
+
if (value.startsWith("0x") || /^[0-9a-f]+$/i.test(value) && value.length > 4) {
|
|
14092
|
+
num = BigInt(value.startsWith("0x") ? value : `0x${value}`);
|
|
14093
|
+
} else if (value.startsWith("0b")) {
|
|
14094
|
+
num = BigInt(value.slice(2), 2);
|
|
14095
|
+
} else if (value.startsWith("0o")) {
|
|
14096
|
+
num = BigInt(value.slice(2), 8);
|
|
14097
|
+
} else {
|
|
14098
|
+
num = BigInt(value);
|
|
14099
|
+
}
|
|
14100
|
+
break;
|
|
14101
|
+
}
|
|
14102
|
+
} catch {
|
|
14103
|
+
return {
|
|
14104
|
+
content: [{ type: "text", text: `Error: Invalid number format "${args.value}"` }],
|
|
14105
|
+
isError: true
|
|
14106
|
+
};
|
|
14107
|
+
}
|
|
14108
|
+
let signedNum = num;
|
|
14109
|
+
if (args.signed && args.byteSize) {
|
|
14110
|
+
const bits = args.byteSize;
|
|
14111
|
+
const maxUnsigned = (1n << BigInt(bits)) - 1n;
|
|
14112
|
+
const signBit = 1n << BigInt(bits - 1);
|
|
14113
|
+
if (num & signBit) {
|
|
14114
|
+
signedNum = num - (1n << BigInt(bits));
|
|
14115
|
+
}
|
|
14116
|
+
}
|
|
14117
|
+
const toFormat = args.toFormat ?? "all";
|
|
14118
|
+
const results = [];
|
|
14119
|
+
if (toFormat === "all" || toFormat === "decimal") {
|
|
14120
|
+
results.push(`Decimal: ${num.toString()}`);
|
|
14121
|
+
if (args.signed && args.byteSize && signedNum !== num) {
|
|
14122
|
+
results.push(`Signed: ${signedNum.toString()}`);
|
|
14123
|
+
}
|
|
14124
|
+
}
|
|
14125
|
+
if (toFormat === "all" || toFormat === "hex") {
|
|
14126
|
+
const hexStr = num.toString(16).toUpperCase();
|
|
14127
|
+
const paddedHex = args.byteSize ? hexStr.padStart(args.byteSize / 4, "0") : hexStr;
|
|
14128
|
+
results.push(`Hex: 0x${paddedHex}`);
|
|
14129
|
+
}
|
|
14130
|
+
if (toFormat === "all" || toFormat === "binary") {
|
|
14131
|
+
const binStr = num.toString(2);
|
|
14132
|
+
const paddedBin = args.byteSize ? binStr.padStart(args.byteSize, "0") : binStr;
|
|
14133
|
+
const formattedBin = paddedBin.match(/.{1,8}/g)?.join(" ") ?? paddedBin;
|
|
14134
|
+
results.push(`Binary: 0b${formattedBin}`);
|
|
14135
|
+
}
|
|
14136
|
+
if (toFormat === "all" || toFormat === "octal") {
|
|
14137
|
+
results.push(`Octal: 0o${num.toString(8)}`);
|
|
14138
|
+
}
|
|
14139
|
+
if (toFormat === "all" || toFormat === "ascii") {
|
|
14140
|
+
let asciiStr = "";
|
|
14141
|
+
let tempNum = num;
|
|
14142
|
+
while (tempNum > 0n) {
|
|
14143
|
+
const charCode = Number(tempNum & 0xffn);
|
|
14144
|
+
if (charCode >= 32 && charCode <= 126) {
|
|
14145
|
+
asciiStr = String.fromCharCode(charCode) + asciiStr;
|
|
14146
|
+
} else {
|
|
14147
|
+
asciiStr = `\\x${charCode.toString(16).padStart(2, "0")}` + asciiStr;
|
|
14148
|
+
}
|
|
14149
|
+
tempNum = tempNum >> 8n;
|
|
14150
|
+
}
|
|
14151
|
+
results.push(`ASCII: ${asciiStr || "(non-printable)"}`);
|
|
14152
|
+
}
|
|
14153
|
+
if (args.byteSize) {
|
|
14154
|
+
results.push("");
|
|
14155
|
+
results.push(`Size: ${args.byteSize}-bit (${args.byteSize / 8} bytes)`);
|
|
14156
|
+
}
|
|
14157
|
+
if (toFormat === "all" && num > 0xffn) {
|
|
14158
|
+
const bytes = [];
|
|
14159
|
+
let tempNum = num;
|
|
14160
|
+
while (tempNum > 0n) {
|
|
14161
|
+
bytes.unshift(tempNum & 0xffn);
|
|
14162
|
+
tempNum = tempNum >> 8n;
|
|
14163
|
+
}
|
|
14164
|
+
results.push("");
|
|
14165
|
+
results.push("Bytes (big-endian):");
|
|
14166
|
+
results.push(` ${bytes.map((b, i) => `[${i}] 0x${b.toString(16).padStart(2, "0")} (${b})`).join(`
|
|
14167
|
+
`)}`);
|
|
14168
|
+
}
|
|
14169
|
+
const summary = [
|
|
14170
|
+
`Number Conversion: ${args.value}`,
|
|
14171
|
+
`From: ${args.fromFormat}`,
|
|
14172
|
+
"",
|
|
14173
|
+
...results
|
|
14174
|
+
].join(`
|
|
14175
|
+
`);
|
|
14176
|
+
return { content: [{ type: "text", text: summary }] };
|
|
14177
|
+
}
|
|
14072
14178
|
var SYMBOL_TYPE_DESCRIPTIONS = {
|
|
14073
14179
|
A: "Global absolute symbol",
|
|
14074
14180
|
B: "Global BSS (uninitialized data)",
|
|
@@ -15092,6 +15198,33 @@ var TOOLS = [
|
|
|
15092
15198
|
required: ["filePath", "pattern"]
|
|
15093
15199
|
}
|
|
15094
15200
|
},
|
|
15201
|
+
{
|
|
15202
|
+
name: "bin_convert",
|
|
15203
|
+
description: "Convert numbers between hex, decimal, binary, octal, and ASCII formats",
|
|
15204
|
+
inputSchema: {
|
|
15205
|
+
type: "object",
|
|
15206
|
+
properties: {
|
|
15207
|
+
value: { type: "string", description: "Number value to convert" },
|
|
15208
|
+
fromFormat: {
|
|
15209
|
+
type: "string",
|
|
15210
|
+
enum: ["hex", "decimal", "binary", "octal", "auto"],
|
|
15211
|
+
description: "Input format (default: auto-detect)"
|
|
15212
|
+
},
|
|
15213
|
+
toFormat: {
|
|
15214
|
+
type: "string",
|
|
15215
|
+
enum: ["all", "hex", "decimal", "binary", "octal", "ascii"],
|
|
15216
|
+
description: "Output format (default: all)"
|
|
15217
|
+
},
|
|
15218
|
+
byteSize: {
|
|
15219
|
+
type: "number",
|
|
15220
|
+
enum: [8, 16, 32, 64],
|
|
15221
|
+
description: "Byte size for padding (8/16/32/64-bit)"
|
|
15222
|
+
},
|
|
15223
|
+
signed: { type: "boolean", description: "Treat as signed integer" }
|
|
15224
|
+
},
|
|
15225
|
+
required: ["value"]
|
|
15226
|
+
}
|
|
15227
|
+
},
|
|
15095
15228
|
{
|
|
15096
15229
|
name: "bin_strings",
|
|
15097
15230
|
description: "Extract readable strings (ASCII, Unicode) from a binary file",
|
|
@@ -15381,6 +15514,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
15381
15514
|
return await handleNopSled(args);
|
|
15382
15515
|
case "bin_hex_editor":
|
|
15383
15516
|
return await handleHexEditor(args);
|
|
15517
|
+
case "bin_convert":
|
|
15518
|
+
return await handleConvertNumber(args);
|
|
15384
15519
|
default:
|
|
15385
15520
|
throw new Error(`Unknown tool: ${name}`);
|
|
15386
15521
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ebowwa/mcp-nm",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "Comprehensive binary analysis MCP server - symbols (nm), hex dumps (xxd), strings, disassembly, security audit, entropy analysis, ELF/Mach-O inspection, and binary patching",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
package/src/index.ts
CHANGED
|
@@ -674,6 +674,147 @@ async function handleXxdFindPattern(args: {
|
|
|
674
674
|
return { content: [{ type: "text", text: summary }] };
|
|
675
675
|
}
|
|
676
676
|
|
|
677
|
+
// ============================================================================
|
|
678
|
+
// Number Format Conversion Utility
|
|
679
|
+
// ============================================================================
|
|
680
|
+
|
|
681
|
+
async function handleConvertNumber(args: {
|
|
682
|
+
value: string;
|
|
683
|
+
fromFormat: "hex" | "decimal" | "binary" | "octal" | "auto";
|
|
684
|
+
toFormat?: "all" | "hex" | "decimal" | "binary" | "octal" | "ascii";
|
|
685
|
+
byteSize?: 8 | 16 | 32 | 64;
|
|
686
|
+
signed?: boolean;
|
|
687
|
+
}) {
|
|
688
|
+
let num: bigint;
|
|
689
|
+
|
|
690
|
+
// Parse input value
|
|
691
|
+
const value = args.value.trim().toLowerCase();
|
|
692
|
+
|
|
693
|
+
try {
|
|
694
|
+
switch (args.fromFormat) {
|
|
695
|
+
case "hex":
|
|
696
|
+
num = BigInt(value.startsWith("0x") ? value : `0x${value}`);
|
|
697
|
+
break;
|
|
698
|
+
case "decimal":
|
|
699
|
+
num = BigInt(value);
|
|
700
|
+
break;
|
|
701
|
+
case "binary":
|
|
702
|
+
num = BigInt(value.startsWith("0b") ? value.slice(2) : value, 2);
|
|
703
|
+
break;
|
|
704
|
+
case "octal":
|
|
705
|
+
num = BigInt(value.startsWith("0o") ? value.slice(2) : value, 8);
|
|
706
|
+
break;
|
|
707
|
+
case "auto":
|
|
708
|
+
default:
|
|
709
|
+
// Auto-detect format
|
|
710
|
+
if (value.startsWith("0x") || /^[0-9a-f]+$/i.test(value) && value.length > 4) {
|
|
711
|
+
num = BigInt(value.startsWith("0x") ? value : `0x${value}`);
|
|
712
|
+
} else if (value.startsWith("0b")) {
|
|
713
|
+
num = BigInt(value.slice(2), 2);
|
|
714
|
+
} else if (value.startsWith("0o")) {
|
|
715
|
+
num = BigInt(value.slice(2), 8);
|
|
716
|
+
} else {
|
|
717
|
+
num = BigInt(value);
|
|
718
|
+
}
|
|
719
|
+
break;
|
|
720
|
+
}
|
|
721
|
+
} catch {
|
|
722
|
+
return {
|
|
723
|
+
content: [{ type: "text", text: `Error: Invalid number format "${args.value}"` }],
|
|
724
|
+
isError: true,
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// Handle signed conversion if requested
|
|
729
|
+
let signedNum: bigint = num;
|
|
730
|
+
if (args.signed && args.byteSize) {
|
|
731
|
+
const bits = args.byteSize;
|
|
732
|
+
const maxUnsigned = (1n << BigInt(bits)) - 1n;
|
|
733
|
+
const signBit = 1n << BigInt(bits - 1);
|
|
734
|
+
|
|
735
|
+
if (num & signBit) {
|
|
736
|
+
signedNum = num - (1n << BigInt(bits));
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// Format outputs
|
|
741
|
+
const toFormat = args.toFormat ?? "all";
|
|
742
|
+
const results: string[] = [];
|
|
743
|
+
|
|
744
|
+
if (toFormat === "all" || toFormat === "decimal") {
|
|
745
|
+
results.push(`Decimal: ${num.toString()}`);
|
|
746
|
+
if (args.signed && args.byteSize && signedNum !== num) {
|
|
747
|
+
results.push(`Signed: ${signedNum.toString()}`);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (toFormat === "all" || toFormat === "hex") {
|
|
752
|
+
const hexStr = num.toString(16).toUpperCase();
|
|
753
|
+
const paddedHex = args.byteSize
|
|
754
|
+
? hexStr.padStart(args.byteSize / 4, "0")
|
|
755
|
+
: hexStr;
|
|
756
|
+
results.push(`Hex: 0x${paddedHex}`);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
if (toFormat === "all" || toFormat === "binary") {
|
|
760
|
+
const binStr = num.toString(2);
|
|
761
|
+
const paddedBin = args.byteSize
|
|
762
|
+
? binStr.padStart(args.byteSize, "0")
|
|
763
|
+
: binStr;
|
|
764
|
+
// Add spaces every 8 bits for readability
|
|
765
|
+
const formattedBin = paddedBin.match(/.{1,8}/g)?.join(" ") ?? paddedBin;
|
|
766
|
+
results.push(`Binary: 0b${formattedBin}`);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (toFormat === "all" || toFormat === "octal") {
|
|
770
|
+
results.push(`Octal: 0o${num.toString(8)}`);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
if (toFormat === "all" || toFormat === "ascii") {
|
|
774
|
+
// Try to convert to ASCII string
|
|
775
|
+
let asciiStr = "";
|
|
776
|
+
let tempNum = num;
|
|
777
|
+
while (tempNum > 0n) {
|
|
778
|
+
const charCode = Number(tempNum & 0xffn);
|
|
779
|
+
if (charCode >= 32 && charCode <= 126) {
|
|
780
|
+
asciiStr = String.fromCharCode(charCode) + asciiStr;
|
|
781
|
+
} else {
|
|
782
|
+
asciiStr = `\\x${charCode.toString(16).padStart(2, "0")}` + asciiStr;
|
|
783
|
+
}
|
|
784
|
+
tempNum = tempNum >> 8n;
|
|
785
|
+
}
|
|
786
|
+
results.push(`ASCII: ${asciiStr || "(non-printable)"}`);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// Add byte size info
|
|
790
|
+
if (args.byteSize) {
|
|
791
|
+
results.push("");
|
|
792
|
+
results.push(`Size: ${args.byteSize}-bit (${args.byteSize / 8} bytes)`);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// Add byte breakdown for multi-byte values
|
|
796
|
+
if (toFormat === "all" && num > 0xffn) {
|
|
797
|
+
const bytes: string[] = [];
|
|
798
|
+
let tempNum = num;
|
|
799
|
+
while (tempNum > 0n) {
|
|
800
|
+
bytes.unshift(tempNum & 0xffn);
|
|
801
|
+
tempNum = tempNum >> 8n;
|
|
802
|
+
}
|
|
803
|
+
results.push("");
|
|
804
|
+
results.push("Bytes (big-endian):");
|
|
805
|
+
results.push(` ${bytes.map((b, i) => `[${i}] 0x${b.toString(16).padStart(2, "0")} (${b})`).join("\n ")}`);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
const summary = [
|
|
809
|
+
`Number Conversion: ${args.value}`,
|
|
810
|
+
`From: ${args.fromFormat}`,
|
|
811
|
+
"",
|
|
812
|
+
...results,
|
|
813
|
+
].join("\n");
|
|
814
|
+
|
|
815
|
+
return { content: [{ type: "text", text: summary }] };
|
|
816
|
+
}
|
|
817
|
+
|
|
677
818
|
// ============================================================================
|
|
678
819
|
// Symbol type descriptions
|
|
679
820
|
// ============================================================================
|
|
@@ -1986,6 +2127,34 @@ const TOOLS = [
|
|
|
1986
2127
|
required: ["filePath", "pattern"],
|
|
1987
2128
|
},
|
|
1988
2129
|
},
|
|
2130
|
+
// Number Format Conversion
|
|
2131
|
+
{
|
|
2132
|
+
name: "bin_convert",
|
|
2133
|
+
description: "Convert numbers between hex, decimal, binary, octal, and ASCII formats",
|
|
2134
|
+
inputSchema: {
|
|
2135
|
+
type: "object" as const,
|
|
2136
|
+
properties: {
|
|
2137
|
+
value: { type: "string", description: "Number value to convert" },
|
|
2138
|
+
fromFormat: {
|
|
2139
|
+
type: "string",
|
|
2140
|
+
enum: ["hex", "decimal", "binary", "octal", "auto"],
|
|
2141
|
+
description: "Input format (default: auto-detect)",
|
|
2142
|
+
},
|
|
2143
|
+
toFormat: {
|
|
2144
|
+
type: "string",
|
|
2145
|
+
enum: ["all", "hex", "decimal", "binary", "octal", "ascii"],
|
|
2146
|
+
description: "Output format (default: all)",
|
|
2147
|
+
},
|
|
2148
|
+
byteSize: {
|
|
2149
|
+
type: "number",
|
|
2150
|
+
enum: [8, 16, 32, 64],
|
|
2151
|
+
description: "Byte size for padding (8/16/32/64-bit)",
|
|
2152
|
+
},
|
|
2153
|
+
signed: { type: "boolean", description: "Treat as signed integer" },
|
|
2154
|
+
},
|
|
2155
|
+
required: ["value"],
|
|
2156
|
+
},
|
|
2157
|
+
},
|
|
1989
2158
|
// Extended Binary Analysis Tools
|
|
1990
2159
|
{
|
|
1991
2160
|
name: "bin_strings",
|
|
@@ -2322,6 +2491,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2322
2491
|
return await handleNopSled(args as { filePath: string; offset: number; count: number; createBackup?: boolean });
|
|
2323
2492
|
case "bin_hex_editor":
|
|
2324
2493
|
return await handleHexEditor(args as { filePath: string; offset: number; length: number; newHex?: string });
|
|
2494
|
+
// Number conversion utility
|
|
2495
|
+
case "bin_convert":
|
|
2496
|
+
return await handleConvertNumber(args as {
|
|
2497
|
+
value: string;
|
|
2498
|
+
fromFormat: "hex" | "decimal" | "binary" | "octal" | "auto";
|
|
2499
|
+
toFormat?: "all" | "hex" | "decimal" | "binary" | "octal" | "ascii";
|
|
2500
|
+
byteSize?: 8 | 16 | 32 | 64;
|
|
2501
|
+
signed?: boolean;
|
|
2502
|
+
});
|
|
2325
2503
|
default:
|
|
2326
2504
|
throw new Error(`Unknown tool: ${name}`);
|
|
2327
2505
|
}
|