mirror of
https://github.com/zap-proto/mcp.git
synced 2026-10-30 15:04:37 +00:00
* feat: ZAP JavaScript/TypeScript bindings v0.2.1
JavaScript/TypeScript client library for ZAP (Zero-Copy App Proto).
- Full ZAP protocol support (tools, resources, prompts)
- MCP Gateway bridging
- Post-quantum cryptography (ML-KEM, ML-DSA)
- W3C DID support
- Lux consensus integration
* feat: add documentation site using @hanzo/docs framework
- Next.js 14 docs site in docs/ directory
- Package.json with @hanzo/ui and @hanzo/docs dependencies
- GitHub Actions workflow for deployment to GitHub Pages
- Documentation pages covering:
- Installation and Quick Start
- Client API, Gateway, Server
- TypeScript Types and Configuration
- Error Handling and Identity/DIDs
- Browser and Node.js usage guides
- Code examples and patterns
- Configured basePath '/zap-js' for GitHub Pages
- Uses pnpm and Node 22
* fix: docs build - remove unused @hanzo/docs, upgrade to Next.js 15
- Remove @hanzo/docs and @hanzo/ui dependencies (not actually used)
- Upgrade to Next.js 15 and React 19 for next.config.ts support
- Rename postcss.config.js to postcss.config.cjs for ESM compatibility
- Add pnpm-lock.yaml for reproducible builds
* rebrand: Cap'n Proto → ZAP
Update all documentation to use ZAP branding instead of Cap'n Proto.
* chore: update to Node 22 and latest packages
- Node.js 22 in CI workflow
- fumadocs-core/ui v15
- next v15.1
- react v19
- Consistent workflow across all repos
* fix: revert to fumadocs v14 for compatibility
* fix: update metadata description to use ZAP branding
* chore: rename package to @zap-proto/zap
- Change npm package name from @hanzo/zap to @zap-proto/zap
- Update repository URL to zap-protocol org
- Update homepage to zap-protocol.github.io
- Update keywords and author
* feat: add transport layer and gateway improvements
- Add server transport abstraction
- Add crypto, gateway, and protocol tests
- Update client/server implementations
- Add CI workflow configuration
- Update vitest config with coverage thresholds
* feat: add .zap test schema for native ZAP format testing
* feat: add ZAP-format schema
Convert zap.capnp to proper whitespace-based .zap syntax
* chore: brand-neutral cleanup — remove cross-tenant references
* docs(readme): add canonical link → zap-proto.dev/docs
* docs(readme): also link Native ZAP RPC guide
* test(pqrns): import canonical KAT fixture
* test(pqrns): DID KAT — must match Go reference at zap-proto/rns
* chore: depend on @zap-proto/zap ^1.1.0 (foundation rolled back from premature v2.0.0)
* chore: pin @zap-proto/zap to ^0.1.0 (foundation reset to pre-1.0)
The @zap-proto/zap foundation was reset to v0.1.0 — native ZAP wire
runtime, no Cap'n Proto. Match the pre-1.0 line; no tags until 1.0
works end-to-end across @zap-proto/{zap,web,mcp}.
* chore: depend on @zap-proto/zap ^1.1.0
@zap-proto/zap is published at 1.1.0 (native ZAP wire runtime, back in
the 1.x line). Point the runtime dep at it instead of the pre-publish
^0.1.0. Lockfiles (npm + pnpm) regenerated; 105 tests still pass.
---------
Co-authored-by: Hanzo Dev <[email protected]>
Co-authored-by: z <[email protected]>
153 lines
4.4 KiB
TypeScript
153 lines
4.4 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
Protocol,
|
|
MessageType,
|
|
ZAP_MAGIC,
|
|
generateId,
|
|
generateClientId,
|
|
defaultProtocol,
|
|
} from '../src/protocol.js';
|
|
import type { ZapRequest, Handshake } from '../src/types.js';
|
|
|
|
describe('ZAP_MAGIC', () => {
|
|
it('should be 4 bytes', () => {
|
|
expect(ZAP_MAGIC.length).toBe(4);
|
|
});
|
|
|
|
it('should start with ZAP', () => {
|
|
expect(String.fromCharCode(ZAP_MAGIC[0]!, ZAP_MAGIC[1]!, ZAP_MAGIC[2]!)).toBe('ZAP');
|
|
});
|
|
|
|
it('should have version 1', () => {
|
|
expect(ZAP_MAGIC[3]).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe('MessageType', () => {
|
|
it('should have correct values', () => {
|
|
expect(MessageType.Handshake).toBe(0x01);
|
|
expect(MessageType.HandshakeResponse).toBe(0x02);
|
|
expect(MessageType.Request).toBe(0x10);
|
|
expect(MessageType.Response).toBe(0x11);
|
|
expect(MessageType.Stream).toBe(0x20);
|
|
expect(MessageType.Ping).toBe(0xFE);
|
|
expect(MessageType.Pong).toBe(0xFF);
|
|
});
|
|
});
|
|
|
|
describe('Protocol', () => {
|
|
describe('binary mode', () => {
|
|
const protocol = new Protocol(true);
|
|
|
|
it('should encode and decode request', () => {
|
|
const request: ZapRequest = {
|
|
id: 'test-123',
|
|
method: 'tools/list',
|
|
};
|
|
|
|
const encoded = protocol.encode(MessageType.Request, request);
|
|
expect(encoded).toBeInstanceOf(ArrayBuffer);
|
|
|
|
const decoded = protocol.decode(encoded);
|
|
expect(decoded.type).toBe(MessageType.Request);
|
|
expect(decoded.payload).toEqual(request);
|
|
});
|
|
|
|
it('should encode and decode handshake', () => {
|
|
const handshake: Handshake = {
|
|
version: '1.0.0',
|
|
clientType: 1,
|
|
clientId: 'test-client',
|
|
capabilities: ['tools', 'browser'],
|
|
};
|
|
|
|
const encoded = protocol.encodeHandshake(handshake);
|
|
expect(encoded).toBeInstanceOf(ArrayBuffer);
|
|
|
|
const decoded = protocol.decode(encoded);
|
|
expect(decoded.type).toBe(MessageType.Handshake);
|
|
expect(decoded.payload).toEqual(handshake);
|
|
});
|
|
|
|
it('should verify magic bytes', () => {
|
|
const badData = new ArrayBuffer(20);
|
|
const view = new Uint8Array(badData);
|
|
view[0] = 0x00; // Bad magic
|
|
|
|
expect(() => protocol.decode(badData)).toThrow('Invalid ZAP message');
|
|
});
|
|
|
|
it('should encode ping/pong', () => {
|
|
const ping = protocol.encodePing();
|
|
const decoded = protocol.decode(ping);
|
|
expect(decoded.type).toBe(MessageType.Ping);
|
|
|
|
const pong = protocol.encodePong(12345);
|
|
const decodedPong = protocol.decode(pong);
|
|
expect(decodedPong.type).toBe(MessageType.Pong);
|
|
expect((decodedPong.payload as { ts: number }).ts).toBe(12345);
|
|
});
|
|
});
|
|
|
|
describe('JSON mode', () => {
|
|
const protocol = new Protocol(false);
|
|
|
|
it('should encode and decode as JSON string', () => {
|
|
const request: ZapRequest = {
|
|
id: 'test-456',
|
|
method: 'tools/call',
|
|
params: { name: 'search', arguments: { query: 'test' } },
|
|
};
|
|
|
|
const encoded = protocol.encode(MessageType.Request, request);
|
|
expect(typeof encoded).toBe('string');
|
|
|
|
const decoded = protocol.decode(encoded as string);
|
|
expect(decoded.type).toBe(MessageType.Request);
|
|
});
|
|
|
|
it('should include _zap metadata in JSON', () => {
|
|
const encoded = protocol.encodeRequest({ id: 'x', method: 'test' }) as string;
|
|
const parsed = JSON.parse(encoded);
|
|
expect(parsed._zap).toBeDefined();
|
|
expect(parsed._zap.type).toBe(MessageType.Request);
|
|
expect(parsed._zap.version).toBe(1);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('generateId', () => {
|
|
it('should generate unique IDs', () => {
|
|
const id1 = generateId();
|
|
const id2 = generateId();
|
|
expect(id1).not.toBe(id2);
|
|
});
|
|
|
|
it('should contain timestamp and random part', () => {
|
|
const id = generateId();
|
|
expect(id).toContain('-');
|
|
const parts = id.split('-');
|
|
expect(parts.length).toBe(2);
|
|
});
|
|
});
|
|
|
|
describe('generateClientId', () => {
|
|
it('should generate unique client IDs', () => {
|
|
const id1 = generateClientId();
|
|
const id2 = generateClientId();
|
|
expect(id1).not.toBe(id2);
|
|
});
|
|
|
|
it('should start with zap- prefix', () => {
|
|
const id = generateClientId();
|
|
expect(id.startsWith('zap-')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('defaultProtocol', () => {
|
|
it('should be in binary mode', () => {
|
|
const encoded = defaultProtocol.encode(MessageType.Ping, { ts: 1 });
|
|
expect(encoded).toBeInstanceOf(ArrayBuffer);
|
|
});
|
|
});
|