Skip to main content
Glama

MCP Specification Server

by MCPJam
23
6
  • Apple
index.js•9.32 kB
import { StringType, UINT32_LE } from 'token-types'; import { decompressSync } from 'fflate'; import initDebug from 'debug'; import { DataDescriptor, EndOfCentralDirectoryRecordToken, FileHeader, LocalFileHeaderToken, Signature } from "./ZipToken.js"; function signatureToArray(signature) { const signatureBytes = new Uint8Array(UINT32_LE.len); UINT32_LE.put(signatureBytes, 0, signature); return signatureBytes; } const debug = initDebug('tokenizer:inflate'); const syncBufferSize = 256 * 1024; const ddSignatureArray = signatureToArray(Signature.DataDescriptor); const eocdSignatureBytes = signatureToArray(Signature.EndOfCentralDirectory); export class ZipHandler { constructor(tokenizer) { this.tokenizer = tokenizer; this.syncBuffer = new Uint8Array(syncBufferSize); } async isZip() { return await this.peekSignature() === Signature.LocalFileHeader; } peekSignature() { return this.tokenizer.peekToken(UINT32_LE); } async findEndOfCentralDirectoryLocator() { const randomReadTokenizer = this.tokenizer; const chunkLength = Math.min(16 * 1024, randomReadTokenizer.fileInfo.size); const buffer = this.syncBuffer.subarray(0, chunkLength); await this.tokenizer.readBuffer(buffer, { position: randomReadTokenizer.fileInfo.size - chunkLength }); // Search the buffer from end to beginning for EOCD signature // const signature = 0x06054b50; for (let i = buffer.length - 4; i >= 0; i--) { // Compare 4 bytes directly without calling readUInt32LE if (buffer[i] === eocdSignatureBytes[0] && buffer[i + 1] === eocdSignatureBytes[1] && buffer[i + 2] === eocdSignatureBytes[2] && buffer[i + 3] === eocdSignatureBytes[3]) { return randomReadTokenizer.fileInfo.size - chunkLength + i; } } return -1; } async readCentralDirectory() { if (!this.tokenizer.supportsRandomAccess()) { debug('Cannot reading central-directory without random-read support'); return; } debug('Reading central-directory...'); const pos = this.tokenizer.position; const offset = await this.findEndOfCentralDirectoryLocator(); if (offset > 0) { debug('Central-directory 32-bit signature found'); const eocdHeader = await this.tokenizer.readToken(EndOfCentralDirectoryRecordToken, offset); const files = []; this.tokenizer.setPosition(eocdHeader.offsetOfStartOfCd); for (let n = 0; n < eocdHeader.nrOfEntriesOfSize; ++n) { const entry = await this.tokenizer.readToken(FileHeader); if (entry.signature !== Signature.CentralFileHeader) { throw new Error('Expected Central-File-Header signature'); } entry.filename = await this.tokenizer.readToken(new StringType(entry.filenameLength, 'utf-8')); await this.tokenizer.ignore(entry.extraFieldLength); await this.tokenizer.ignore(entry.fileCommentLength); files.push(entry); debug(`Add central-directory file-entry: n=${n + 1}/${files.length}: filename=${files[n].filename}`); } this.tokenizer.setPosition(pos); return files; } this.tokenizer.setPosition(pos); } async unzip(fileCb) { const entries = await this.readCentralDirectory(); if (entries) { // Use Central Directory to iterate over files return this.iterateOverCentralDirectory(entries, fileCb); } // Scan Zip files for local-file-header let stop = false; do { const zipHeader = await this.readLocalFileHeader(); if (!zipHeader) break; const next = fileCb(zipHeader); stop = !!next.stop; let fileData = undefined; await this.tokenizer.ignore(zipHeader.extraFieldLength); if (zipHeader.dataDescriptor && zipHeader.compressedSize === 0) { const chunks = []; let len = syncBufferSize; debug('Compressed-file-size unknown, scanning for next data-descriptor-signature....'); let nextHeaderIndex = -1; while (nextHeaderIndex < 0 && len === syncBufferSize) { len = await this.tokenizer.peekBuffer(this.syncBuffer, { mayBeLess: true }); nextHeaderIndex = indexOf(this.syncBuffer.subarray(0, len), ddSignatureArray); const size = nextHeaderIndex >= 0 ? nextHeaderIndex : len; if (next.handler) { const data = new Uint8Array(size); await this.tokenizer.readBuffer(data); chunks.push(data); } else { // Move position to the next header if found, skip the whole buffer otherwise await this.tokenizer.ignore(size); } } debug(`Found data-descriptor-signature at pos=${this.tokenizer.position}`); if (next.handler) { await this.inflate(zipHeader, mergeArrays(chunks), next.handler); } } else { if (next.handler) { debug(`Reading compressed-file-data: ${zipHeader.compressedSize} bytes`); fileData = new Uint8Array(zipHeader.compressedSize); await this.tokenizer.readBuffer(fileData); await this.inflate(zipHeader, fileData, next.handler); } else { debug(`Ignoring compressed-file-data: ${zipHeader.compressedSize} bytes`); await this.tokenizer.ignore(zipHeader.compressedSize); } } debug(`Reading data-descriptor at pos=${this.tokenizer.position}`); if (zipHeader.dataDescriptor) { // await this.tokenizer.ignore(DataDescriptor.len); const dataDescriptor = await this.tokenizer.readToken(DataDescriptor); if (dataDescriptor.signature !== 0x08074b50) { throw new Error(`Expected data-descriptor-signature at position ${this.tokenizer.position - DataDescriptor.len}`); } } } while (!stop); } async iterateOverCentralDirectory(entries, fileCb) { for (const fileHeader of entries) { const next = fileCb(fileHeader); if (next.handler) { this.tokenizer.setPosition(fileHeader.relativeOffsetOfLocalHeader); const zipHeader = await this.readLocalFileHeader(); if (zipHeader) { await this.tokenizer.ignore(zipHeader.extraFieldLength); const fileData = new Uint8Array(fileHeader.compressedSize); await this.tokenizer.readBuffer(fileData); await this.inflate(zipHeader, fileData, next.handler); } } if (next.stop) break; } } inflate(zipHeader, fileData, cb) { if (zipHeader.compressedMethod === 0) { return cb(fileData); } debug(`Decompress filename=${zipHeader.filename}, compressed-size=${fileData.length}`); const uncompressedData = decompressSync(fileData); return cb(uncompressedData); } async readLocalFileHeader() { const signature = await this.tokenizer.peekToken(UINT32_LE); if (signature === Signature.LocalFileHeader) { const header = await this.tokenizer.readToken(LocalFileHeaderToken); header.filename = await this.tokenizer.readToken(new StringType(header.filenameLength, 'utf-8')); return header; } if (signature === Signature.CentralFileHeader) { return false; } if (signature === 0xE011CFD0) { throw new Error('Encrypted ZIP'); } throw new Error('Unexpected signature'); } } function indexOf(buffer, portion) { const bufferLength = buffer.length; const portionLength = portion.length; // Return -1 if the portion is longer than the buffer if (portionLength > bufferLength) return -1; // Search for the portion in the buffer for (let i = 0; i <= bufferLength - portionLength; i++) { let found = true; for (let j = 0; j < portionLength; j++) { if (buffer[i + j] !== portion[j]) { found = false; break; } } if (found) { return i; // Return the starting offset } } return -1; // Not found } function mergeArrays(chunks) { // Concatenate chunks into a single Uint8Array const totalLength = chunks.reduce((acc, curr) => acc + curr.length, 0); const mergedArray = new Uint8Array(totalLength); let offset = 0; for (const chunk of chunks) { mergedArray.set(chunk, offset); offset += chunk.length; } return mergedArray; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/MCPJam/mcp-spec'

If you have feedback or need assistance with the MCP directory API, please join our Discord server