1. Conduid
  2. Blockchain
  3. Jwt Lab
MCP server · Blockchain

Jwt Lab

jwt-lab – A fast, secure, beautiful JWT CLI tool and MCP server for developers & AI agents. Encode, decode, verify, inspect, audit, and generate keys for JSON Web Tokens.

37Low

Scored 4 months ago · breakdown

About Jwt Lab

Jwt Lab is an MCP server in the Blockchain category: jwt-lab – A fast, secure, beautiful JWT CLI tool and MCP server for developers & AI agents. Encode, decode, verify, inspect, audit, and generate keys for JSON Web Tokens. It has been installed 0 times through Conduid.

Install

Claude Code
claude mcp add jwt-lab -- npx -y jwt-lab
npx
npx -y jwt-lab

This server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.

Ask AI

Ask AI about Jwt Lab

Powered by Claude · Grounded in docs

I know everything about Jwt Lab. Ask me about installation, configuration, usage, or troubleshooting.

Security checks

  • ·README presentNot checked yet.
  • ·License declaredNot checked yet.
  • ·Tests presentNot checked yet.
  • ·Dependencies pinnedNot checked yet.
  • ·No dynamic code executionNot checked yet.
  • ·Scoped permissionsNot checked yet.

README


     ██╗██╗    ██╗████████╗   ██╗      █████╗ ██████╗ 
     ██║██║    ██║╚══██╔══╝   ██║     ██╔══██╗██╔══██╗
     ██║██║ █╗ ██║   ██║█████╗██║     ███████║██████╔╝
██   ██║██║███╗██║   ██║╚════╝██║     ██╔══██║██╔══██╗
╚█████╔╝╚███╔███╔╝   ██║      ███████╗██║  ██║██████╔╝
 ╚════╝  ╚══╝╚══╝    ╚═╝      ╚══════╝╚═╝  ╚═╝╚═════╝
────────────────────────────────────────────────────────
v0.1.0 · JWT toolkit for developers & AI agents

jwt-lab

The JWT Swiss-Army Knife for Developers & AI Agents

npm version license node TypeScript CI CodeQL tests MCP

Encode · Decode · Verify · Inspect · Explain · Keygen · MCP Server

A fast, secure, beautiful, and AI-agent-ready command-line tool and TypeScript/JavaScript library for working with JSON Web Tokens (JWTs), plus a full Model Context Protocol (MCP) HTTP/JSON server.

Installation · Quick Start · Commands · MCP Server · Configuration · API Reference


Why jwt-lab?

Feature jwt-lab jwt.io Other CLIs
🔐 Security linting & audit ✅ 6 built-in rules
🤖 AI-native MCP server ✅ Full HTTP/JSON API
� Programmatic TypeScript API ✅ ESM + CJS, Result<T,E>
🔍 One-call token inspection inspectToken()
�🗣️ Natural language encoding "admin token expires in 1h"
⏰ Time travel (--fake-time) ✅ Deterministic testing
📋 Config as code (.jwt-cli.toml) ✅ Profiles, defaults, keys
🎨 Premium terminal UX ✅ Colors, boxes, tables Partial
🔑 Key generation (RSA/EC/Ed25519) ✅ JWK + PEM output Partial
📦 Dual ESM + CJS output N/A
🧪 Strict TypeScript, zero any N/A

Installation

# Global install (recommended for CLI)
npm install -g jwt-lab

# Or use with npx
npx jwt-lab --help

# Add to a project (as a library)
npm install jwt-lab

Quick Start

CLI

# Encode a JWT with HMAC secret
jwt encode '{"sub":"user1","role":"admin"}' --secret my-secret --exp 1h

# Decode without verification
jwt decode eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyMSJ9.xxx

# Verify signature + claims
jwt verify <token> --secret my-secret

# Security audit (no keys needed)
jwt explain <token>

# Inspect with full breakdown
jwt inspect <token> --secret my-secret

# Generate key pairs
jwt keygen ec --pem --out-dir ./keys

# Natural language encoding
jwt encode "admin token for user ali@example.com expires in 12h" --secret s

# Start MCP server for AI agents
jwt mcp serve --port 3000

Library (TypeScript / JavaScript)

import { inspectToken, encodeToken, verifyToken, generateKeyPair } from 'jwt-lab';

// One-call inspection: decode + verify + lint
const result = await inspectToken({
  token: 'eyJ...',
  secret: 'my-secret',
});
if (result.ok) {
  console.log(result.value.status);     // "valid" | "expired" | "not_yet_valid" | "unverified"
  console.log(result.value.algorithm);   // "HS256"
  console.log(result.value.lintFindings); // security findings
}

// Or use individual functions for fine-grained control
const token = await encodeToken({
  payload: { sub: 'user1', role: 'admin' },
  secret: 'my-secret',
  alg: 'HS256',
});

const verified = await verifyToken({
  token: token.value,
  secret: 'my-secret',
  // alg auto-detected from token header
});

// Generate keys
const keys = await generateKeyPair({ type: 'ec', format: 'jwk' });

Commands

jwt encode

Encode a JWT from JSON or natural language.

# JSON payload
jwt encode '{"sub":"user1","role":"admin","email":"user@example.com"}' \
  --secret my-secret \
  --exp 1h \
  --iss https://auth.myapp.com

# Natural language (no LLM — deterministic regex parser)
jwt encode "admin user user@example.com expires in 30m" --secret s

# With asymmetric key
jwt encode '{"sub":"svc"}' --key ./private.pem --alg ES256

# With profile from config
jwt encode '{"sub":"user1"}' --secret s --profile access_token

# Copy to clipboard
jwt encode '{"sub":"user1"}' --secret s --exp 1h --copy

# JSON output
jwt encode '{"sub":"user1"}' --secret s --json

Options:

Flag Description
--secret <string> HMAC secret (HS256/384/512)
--key <path> PEM or JWK private key file
--alg <algorithm> Signing algorithm
--exp <duration> Expiration (e.g., 1h, 30m, 7d)
--iss <string> Issuer claim
--sub <string> Subject claim
--aud <string> Audience claim
--kid <string> Key ID in header
--jti Generate random UUID as JTI
--header <json> Additional header fields
--profile <name> Use named profile from config
--copy Copy token to clipboard
--json Output as JSON

jwt decode

Decode a JWT without verification.

jwt decode <token>

# From stdin
echo "<token>" | jwt decode -

# Batch mode
cat tokens.txt | jwt decode - --batch

# JSON output
jwt decode <token> --json

jwt verify

Full signature verification and claims validation.

# HMAC
jwt verify <token> --secret my-secret

# Asymmetric key
jwt verify <token> --key ./public.pem --alg ES256

# JWKS endpoint
jwt verify <token> --jwks https://auth.example.com/.well-known/jwks.json

# Required claims
jwt verify <token> --secret s --require sub,iss,exp

# Clock skew tolerance
jwt verify <token> --secret s --leeway 30

# Time travel for testing
jwt verify <token> --secret s --fake-time 2024-01-01T00:00:00Z

Output:

✅ Valid JWT
Algorithm: HS256
Subject:   user1

jwt inspect

High-level token breakdown with status, metadata, and security posture.

jwt inspect <token>
jwt inspect <token> --secret my-secret  # with verification
jwt inspect <token> --json              # machine-readable
jwt inspect <token> --table             # table format

Output:

╭───── Token Inspection ──────╮
│                              │
│  Status: ✅ valid            │
│  Algorithm: HS256            │
│  Subject:   user1            │
│  Issuer:    auth.example.com │
│  Expires in: 59m 30s         │
│                              │
│  Lint Findings:              │
│  ⚠️ [pii-claims] Payload ... │
│                              │
╰──────────────────────────────╯

jwt explain

Static security audit — no keys required.

jwt explain <token>
jwt explain <token> --json    # for CI pipelines
jwt explain <token> --table   # table format

Output:

🔍 JWT Security Audit

❌ [none-algorithm] Token uses the "none" algorithm
  → Replace "none" with a secure algorithm such as RS256 or ES256

⚠️ [pii-claims] Payload contains claims that may hold PII: email
  → Avoid embedding PII directly in JWT payloads

ℹ️ [hmac-preferred-asymmetric] Token uses HMAC algorithm (HS256)
  → Consider using an asymmetric algorithm such as RS256 or ES256

Built-in security rules:

Rule ID Severity What it checks
none-algorithm 🔴 error Algorithm is "none"
missing-exp 🟡 warn Token has no expiration
long-lived-token 🟡 warn Lifetime > 24 hours
pii-claims 🟡 warn Claims containing PII patterns
missing-nbf-long-lived 🔵 info Long-lived token without nbf
hmac-preferred-asymmetric 🔵 info HMAC where asymmetric is preferred

jwt keygen

Generate cryptographic key pairs.

# EC key pair (default P-256)
jwt keygen ec

# RSA key pair
jwt keygen rsa --bits 4096

# Ed25519
jwt keygen ed25519

# PEM output to files
jwt keygen ec --pem --out-dir ./keys

# JWK with key ID
jwt keygen rsa --jwk --kid my-production-key

MCP Server

jwt-lab includes a full Model Context Protocol HTTP/JSON server for AI agents and programmatic access.

Start the server

jwt mcp serve --port 3000 --host 0.0.0.0

Endpoints

Method Path Description
POST /encode Encode a JWT
POST /decode Decode a JWT
POST /verify Verify a JWT
POST /inspect Inspect a JWT
POST /keygen Generate key pair
POST /explain Security audit
GET /docs OpenAPI 3.1 spec
GET /health Health check

Examples with curl

# Encode
curl -X POST http://localhost:3000/encode \
  -H "Content-Type: application/json" \
  -d '{"payload":{"sub":"user1"},"secret":"my-secret","alg":"HS256","exp":"1h"}'

# Decode
curl -X POST http://localhost:3000/decode \
  -H "Content-Type: application/json" \
  -d '{"token":"eyJhbGciOiJIUzI1NiJ9..."}'

# Verify
curl -X POST http://localhost:3000/verify \
  -H "Content-Type: application/json" \
  -d '{"token":"eyJ...","secret":"my-secret"}'

# Explain (security audit)
curl -X POST http://localhost:3000/explain \
  -H "Content-Type: application/json" \
  -d '{"token":"eyJ..."}'

# Generate key pair
curl -X POST http://localhost:3000/keygen \
  -H "Content-Type: application/json" \
  -d '{"type":"ec","format":"jwk"}'

# OpenAPI docs
curl http://localhost:3000/docs

Authentication

Set the MCP_API_KEY environment variable to enable Bearer token authentication:

MCP_API_KEY=your-secret-key jwt mcp serve

# Then include the key in requests:
curl -X POST http://localhost:3000/encode \
  -H "Authorization: Bearer your-secret-key" \
  -H "Content-Type: application/json" \
  -d '{"payload":{"sub":"user1"},"secret":"s","alg":"HS256"}'

Security Features

  • Token redaction: Full tokens are never logged; truncated to 20 chars
  • Claim redaction: Configure mcp.redactClaims to hide sensitive claims in responses
  • Rate limiting: Sliding window per IP (configurable)
  • CORS: Configurable allowed origins
  • Input validation: All requests validated with Zod schemas

Configuration

Create a .jwt-cli.toml in your project root:

[defaults]
iss = "https://auth.myapp.com/"
aud = "myapp-api"
alg = "ES256"

[profiles.access_token]
ttl    = "15m"
scopes = ["read", "write"]

[profiles.service_token]
ttl = "1h"
aud = "internal-service"

[lint]
piiClaimPatterns = ["email", "phone", "ssn"]

[lint.severityOverrides]
"missing-exp" = "error"

[mcp]
port = 3000
redactClaims = ["email", "phone"]

[mcp.rateLimit]
windowSeconds = 60
maxRequests   = 100

The CLI auto-discovers .jwt-cli.toml by walking upward from the current directory. Use --config <path> to specify a custom path.

Priority: CLI flags > config file > built-in defaults


Global Flags

Flag Description
--help Show help
--version Show version
--fake-time <iso8601> Override system clock
--config <path> Path to config file
--json Machine-readable JSON output

API Reference

Core Library

jwt-lab's core is a pure, I/O-free TypeScript library (except for OIDC/JWKS which makes HTTP requests). All functions return Result<T, E> types — no exceptions thrown.

import {
  // High-level
  inspectToken,        // decode + verify + lint in one call

  // Low-level building blocks
  encodeToken,         // sign a JWT
  decodeToken,         // decode without verification
  verifyToken,         // verify signature + claims
  lintToken,           // security audit (no keys needed)
  generateKeyPair,     // RSA, EC, Ed25519 key pairs
  parseDuration,       // "1h30m" → 5400 seconds
  parseNaturalLanguagePayload,  // NLP → JWT payload

  // OIDC / JWKS
  resolveOidcJwksUri,  // fetch JWKS URI from OIDC discovery
  buildDiscoveryUrl,   // issuer → discovery URL
} from 'jwt-lab';

inspectToken(opts) — High-Level API

The inspectToken function is the library equivalent of jwt inspect and jwt verify --oidc-discovery. It composes decode → OIDC discovery → verify → lint in a single call:

import { inspectToken } from 'jwt-lab';

// With HMAC secret
const result = await inspectToken({
  token: 'eyJ...',
  secret: 'my-secret',
});

// With OIDC discovery (auto-resolves JWKS)
const result = await inspectToken({
  token: 'eyJ...',
  oidcDiscoveryUrl: 'https://accounts.google.com',
});

// With asymmetric key (auto-detects algorithm from token header)
const result = await inspectToken({
  token: 'eyJ...',
  publicKeyPem: '-----BEGIN PUBLIC KEY-----\n...',
});

// Decode + lint only (no key → status = "unverified")
const result = await inspectToken({ token: 'eyJ...' });

if (result.ok) {
  const { status, algorithm, issuer, subject, expiresAt,
          customClaims, verificationResult, lintFindings } = result.value;
  // status: "valid" | "expired" | "not_yet_valid" | "unverified"
}

InspectTokenOptions:

Option Type Description
token string JWT string (required)
secret string HMAC secret
publicKeyPem string PEM-encoded public key
publicKeyJwk object JWK public key
jwksUri string Remote JWKS endpoint
oidcDiscoveryUrl string OIDC discovery URL (auto-resolves JWKS)
alg SupportedAlgorithm Expected algorithm (auto-detected if omitted)
requiredClaims string[] Claims that must be present
leewaySeconds number Clock skew tolerance
lintConfig LintConfig Lint rule overrides
now Date Clock override for testing

InspectResult:

Field Type Description
status "valid" | "expired" | "not_yet_valid" | "unverified" Overall token status
algorithm string Algorithm from the header
kid string? Key ID from the header
issuer string? iss claim
subject string? sub claim
audience string | string[]? aud claim
issuedAt Date? iat as Date
expiresAt Date? exp as Date
notBefore Date? nbf as Date
timeUntilExpiry number? Seconds until expiry (negative = expired)
customClaims Record<string, unknown> Non-standard claims
verificationResult Result<true, VerifyError>? Signature check result
lintFindings LintFinding[] Security audit findings

Low-Level Functions

// Encode a JWT
const token = await encodeToken({
  payload: { sub: 'user1', role: 'admin' },
  secret: 'my-secret',
  alg: 'HS256',
});

// Decode without verification
const decoded = decodeToken('eyJ...');
// → { header, payload, signaturePresent }

// Verify signature + claims
const verified = await verifyToken({
  token: 'eyJ...',
  secret: 'my-secret',
  // alg auto-detected from header if omitted
});

// Security audit (no keys needed)
const decoded = decodeToken('eyJ...');
const findings = lintToken(decoded.value, {
  piiClaimPatterns: ['email', 'phone'],
});

// Generate key pairs
const keys = await generateKeyPair({
  type: 'ec',       // 'rsa' | 'ec' | 'ed25519'
  format: 'jwk',    // 'jwk' | 'pem'
  kid: 'my-key-id',
});

// Parse durations
const seconds = parseDuration('1h30m');
// → { ok: true, value: 5400 }

// Natural language → payload
const payload = parseNaturalLanguagePayload(
  'admin token for user ali@example.com expires in 1h',
  new Date()
);
// → { ok: true, value: { exp: ..., sub: 'ali@example.com', email: '...', role: 'admin' } }

Algorithm Auto-Detection

verifyToken and inspectToken auto-detect the signing algorithm from the JWT header when alg is not explicitly provided. This means you can verify tokens without knowing the algorithm in advance:

// No need to specify alg — auto-detected from the token header
const result = await verifyToken({
  token: ecSignedJwt,
  publicKeyPem: ecPublicKey,
});

See src/core/ for full API documentation with TSDoc comments.

Examples

The examples/api-usage/ directory contains runnable TypeScript examples:

Script Description
npm start All core functions: encode, decode, verify, lint, keygen, inspectToken, NLP
npm run inspect-local inspectToken with local keys (HMAC, EC, expired, no-key)
npm run verify-asymmetric All asymmetric algorithms + auto-detection (EC, RSA, Ed25519)
npm run nlp-encode Natural language → JWT payload → encode → decode round-trip
npm run oidc-inspect OIDC token inspection with a single inspectToken call
cd examples/api-usage
npm install
npm start                   # run main examples
npm run inspect-local       # inspectToken with local keys
npm run verify-asymmetric   # EC, RSA, Ed25519 verification
npm run nlp-encode          # natural language encoding

Tech Stack

Category Choice
Language TypeScript 6 (strict mode, zero any)
Runtime Node.js ≥ 22
JWT jose v6
CLI commander v14
Validation zod v4
HTTP hono + @hono/node-server
Build tsup (dual ESM + CJS)
Tests vitest v4
Terminal picocolors, boxen, ora, cli-table3

Shell Completions

jwt-lab ships with built-in tab-completion scripts for Bash, Zsh, and Fish. The completions are aware of every subcommand and flag — pressing Tab surfaces commands, options, algorithm names, and file paths in context.

How it works

The jwt completions <shell> command prints a shell-specific completion script to stdout. You either eval it at shell startup or write it to a file that your shell auto-loads. No third-party tools are required.

jwt completions bash   →  prints a Bash completion function + `complete -F` binding
jwt completions zsh    →  prints a Zsh `_jwt` compdef function
jwt completions fish   →  prints Fish `complete` directives

Bash

One-liner (current session only):

eval "$(jwt completions bash)"

Persistent — add to ~/.bashrc:

echo 'eval "$(jwt completions bash)"' >> ~/.bashrc
source ~/.bashrc

Or save to the system completions directory (recommended for shared machines):

jwt completions bash | sudo tee /etc/bash_completion.d/jwt > /dev/null

Requires bash-completion package. Install with brew install bash-completion on macOS or apt install bash-completion on Debian/Ubuntu.

Zsh

One-liner (current session only):

eval "$(jwt completions zsh)"

Persistent — add to ~/.zshrc:

echo 'eval "$(jwt completions zsh)"' >> ~/.zshrc
source ~/.zshrc

Or save to a $fpath directory (the clean approach):

# Pick any directory already in your fpath, or create one
mkdir -p ~/.zsh/completions
jwt completions zsh > ~/.zsh/completions/_jwt

# Make sure the directory is in fpath — add to ~/.zshrc if not already there:
echo 'fpath=(~/.zsh/completions $fpath)' >> ~/.zshrc
echo 'autoload -Uz compinit && compinit' >> ~/.zshrc
source ~/.zshrc

oh-my-zsh users: Save to ~/.oh-my-zsh/completions/_jwt — it's already in fpath.

Fish

Fish completions are discovered automatically from ~/.config/fish/completions/. Just save the script there:

jwt completions fish > ~/.config/fish/completions/jwt.fish

Completions take effect immediately — no source or restart needed.

What gets completed

Context Completions offered
jwt <Tab> All subcommands with descriptions
jwt encode <Tab> --secret, --key, --alg, --exp, --iss, --json, …
jwt verify <Tab> --secret, --key, --jwks, --oidc-discovery, --alg, --require, --leeway, …
jwt keygen <Tab> Algorithm types: RS256 RS384 RS512 ES256 ES384 ES512 EdDSA PS256 PS384 PS512
jwt --alg <Tab> Full algorithm list
--key <Tab> File path completion (all shells)
--config <Tab> File path completion (all shells)
jwt completions <Tab> bash zsh fish

CI/CD & Publishing

Every push and pull request to main runs the full pipeline:

Job Steps
Test & Build lint → type-check → tests → build → CLI smoke test (Node 22 & 24)
Security Audit npm audit at moderate severity
CodeQL Static analysis for JavaScript/TypeScript (separate scheduled workflow)
Publish Runs only on v* tag push → bumps version → builds → publishes to npm with provenance

Publishing a release

# 1. Bump version
npm version 1.0.0 --no-git-tag-version
git add package.json && git commit -m "chore: bump version to 1.0.0"
git push origin main

# 2. Create a GPG-signed annotated tag
git tag -s v1.0.0 -m "Release v1.0.0"
git tag -v v1.0.0  # verify signature

# 3. Push tag — triggers the publish workflow
git push origin v1.0.0

# 4. Create a signed GitHub Release
gh release create v1.0.0 --title "v1.0.0" --notes "Release notes here" --verify-tag

For a prerelease (e.g. v1.1.0-beta.1), the package is published with the beta dist-tag automatically.

The workflow uses npm publish --provenance, which attaches a cryptographic SLSA Level 2 attestation proving the package was built from this exact commit.

Required repository secret

Secret Description
NPM_TOKEN npm Automation token with publish access — add at Settings → Secrets → Actions
# Install dependencies
npm install

# Build
npm run build

# Run tests
npm test

# Type check
npm run type-check

# Lint
npm run lint

# Start MCP server (dev)
npm run dev:mcp

CI/CD & Publishing

Every push and pull request to main runs the full pipeline:

Job Steps
Test & Build lint → type-check → tests → build → CLI smoke test (Node 22 & 24)
Security Audit npm audit at moderate severity
CodeQL Static analysis for JavaScript/TypeScript (separate scheduled workflow)
Publish Runs only on GitHub Release publish → bumps version → builds → publishes to npm

Publishing a release

  1. Create and push a tag: git tag v1.0.0 && git push origin v1.0.0
  2. Create a GitHub Release from that tag (set it to Published, not Draft)
  3. The pipeline auto-bumps package.json, builds, and publishes to npm

For a prerelease (e.g. v1.1.0-beta.1), the package is published with the beta dist-tag automatically.

Required repository secret

Secret Description
NPM_TOKEN npm automation token with publish access — add in Settings → Secrets → Actions

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

MIT © jwt-lab contributors


Built with ❤️ for developers and AI agents

Report Bug · Request Feature · Discussions

README mirrored from the source repository 4 months ago. The original is authoritative.

Questions

About Jwt Lab

How do I install Jwt Lab?

Run claude mcp add jwt-lab -- npx -y jwt-lab, then add the server to your MCP client's configuration. Conduid has recorded 0 installs, so the command is known to work with current clients.

Is Jwt Lab safe to use with an AI agent?

Its trust score is 37 out of 100 (low). Conduid hasn't run static security checks on this repository yet, so review the source yourself before granting it credentials. It has no ConduID identity yet, so agent calls to it are not receipted.

Is Jwt Lab still maintained?

Conduid hasn't recorded a commit date for this repository yet. Check the repository directly for recent activity.