Overview
On paid plans, pass a PEM public key, certificate or JWK as publicKey to verify RS, PS, ES and EdDSA signatures and check exp and nbf. HMAC (HS*) secrets and alg none are always refused, and keys are never fetched from URLs.
Live Test JWT Decoder Skill Skill →
The tool
Once your client is connected to the VerveKit server, this appears in its tool list as JWTDecoderSkill. It is read-only and open-world — it fetches and never mutates anything on your side — so most clients call it without asking you to confirm.
{
"name": "JWTDecoderSkill",
"arguments": {
"token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNhbXBsZS1rZXktMSJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaXNzIjoiaHR0cHM6Ly9hdXRoLmV4YW1wbGUuY29tIiwiYXVkIjoiYXBpLmV4YW1wbGUuY29tIiwiaWF0IjoxNzY3MjI1NjAwLCJleHAiOjIwODI3NTg0MDB9.hP0O9ELZGk-KN3upyYqdOnPY2Ig7FAzfUY_xytzOCBIhn0_TWdzl2dbLPlj0rkCDsRIQX0I5t9Xv551VI1W9dmvRGtVztyeD3IKHnIn24baOUtjU7Eb6FHL_iWMu5PqMeQJPFytuYbYhzjjCNfFPgAciGgn1Celr8H7_q51x5naMb-nbTvBc5SIX9MBnegGzabExBYQ-OsdKOoYmK0uymNIgC3834jlPuhuJHkPsxX7ncKoSQ4wheiLaZDwFANlT1NrD2XgKyyh-LIIg9CRwqB_WDktZ_R6UusNkLRhKJ20ff29ttkyRhJZzEqRRK60xYn7iPyg4YQni1J_w-oAa_w"
}
}You do not name the tool yourself; the model picks it. Asking about eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNhbXBsZS1rZXktMSJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaXNzIjoiaHR0cHM6Ly9hdXRoLmV4YW1wbGUuY29tIiwiYXVkIjoiYXBpLmV4YW1wbGUuY29tIiwiaWF0IjoxNzY3MjI1NjAwLCJleHAiOjIwODI3NTg0MDB9.hP0O9ELZGk-KN3upyYqdOnPY2Ig7FAzfUY_xytzOCBIhn0_TWdzl2dbLPlj0rkCDsRIQX0I5t9Xv551VI1W9dmvRGtVztyeD3IKHnIn24baOUtjU7Eb6FHL_iWMu5PqMeQJPFytuYbYhzjjCNfFPgAciGgn1Celr8H7_q51x5naMb-nbTvBc5SIX9MBnegGzabExBYQ-OsdKOoYmK0uymNIgC3834jlPuhuJHkPsxX7ncKoSQ4wheiLaZDwFANlT1NrD2XgKyyh-LIIg9CRwqB_WDktZ_R6UusNkLRhKJ20ff29ttkyRhJZzEqRRK60xYn7iPyg4YQni1J_w-oAa_w in the terms this skill covers is enough for it to reach for JWTDecoderSkill on its own — naming it explicitly also works, and is the way to force the call.
Connecting
One server URL covers every skill in the catalog, including this one. Authorization is OAuth: the client opens a browser once, and there is no key to paste into a config file.
{
"mcpServers": {
"vervekit": {
"url": "https://api.vervekit.com/v1/mcp"
}
}
}https://api.vervekit.com/v1/mcpPer-client setup — Claude, Cursor, VS Code, ChatGPT — is on the MCP setup page.
Arguments
These are the properties on the tool's inputSchema, so a well-behaved client validates them before the call is made. Premium arguments are accepted on every plan but only take effect on plans that include them.
| Argument | Type | Description |
|---|---|---|
tokenRequired | string | JWT token to decode |
publicKeyOptionalPremium | string | Public key to verify the signature with: a PEM public key, a PEM X.509 certificate, or a JWK (as a JSON object or string). Asymmetric algorithms only; HMAC secrets are refused and keys are never fetched from URLs. Omit it to decode only. |
What the model gets back
The result carries a structuredContent object matching the tool's declared outputSchema, so a client reads fields without parsing prose. status is "ok" and error is null on success; a null field means the value was not available for that input, not that the call failed.
{
"status": "ok",
"error": null,
"data": {
"header": {
"alg": "RS256",
"typ": "JWT",
"kid": "sample-key-1"
},
"payload": {
"sub": "1234567890",
"name": "John Doe",
"iss": "https://auth.example.com",
"aud": "api.example.com",
"iat": 1767225600,
"exp": 2082758400
},
"signature": "hP0O9ELZGk-KN3upyYqdOnPY2Ig7FAzfUY_xytzOCBIhn0_TWdzl2dbLPlj0rkCDsRIQX0I5t9Xv551VI1W9dmvRGtVztyeD3IKHnIn24baOUtjU7Eb6FHL_iWMu5PqMeQJPFytuYbYhzjjCNfFPgAciGgn1Celr8H7_q51x5naMb-nbTvBc5SIX9MBnegGzabExBYQ-OsdKOoYmK0uymNIgC3834jlPuhuJHkPsxX7ncKoSQ4wheiLaZDwFANlT1NrD2XgKyyh-LIIg9CRwqB_WDktZ_R6UusNkLRhKJ20ff29ttkyRhJZzEqRRK60xYn7iPyg4YQni1J_w-oAa_w",
"isExpired": false,
"expiresAt": "2036-01-01T00:00:00.000Z",
"issuedAt": "2026-01-01T00:00:00.000Z",
"tokenAge": "309 days",
"algorithm": "RS256",
"expiresIn": 288757380,
"notYetValid": false,
"securityAnalysis": {
"isUnsecured": false,
"hasExpiration": true,
"isLongLived": true,
"issues": [
"Token lifetime exceeds 24 hours"
]
},
"warning": "Signature verified with the supplied public key.",
"signatureVerification": {
"verified": true,
"algorithm": "RS256",
"keyType": "RSA",
"reason": null,
"claimsValid": true,
"claimsReason": null
}
}
}
Response fields
Paths are relative to data. Premium fields are absent rather than zeroed on plans that do not include them, so check for presence instead of comparing to 0.
| Field | Type | Example | Description |
|---|---|---|---|
header | object | {…} | Decoded JWT header, typically containing the signing algorithm and token type |
header.alg | string | RS256 | |
header.typ | string | JWT | |
header.kid | string | sample-key-1 | |
payload | object | {…} | Decoded JWT payload containing the token's claims (e.g. subject, issuer, expiration, and any custom claims) |
payload.sub | string | 1234567890 | |
payload.name | string | John Doe | |
payload.iss | string | https://auth.example.com | |
payload.aud | string | api.example.com | |
payload.iat | number | 1767225600 | |
payload.exp | number | 2082758400 | |
signature | string | hP0O9ELZGk-KN3upyYqdOnPY2Ig7FAzfUY_xytzOCBIhn0_TWdzl2dbLPlj0rkCDsRIQX0I5t9Xv551VI1W9dmvRGtVztyeD3IKH… | Raw signature segment of the token (the third dot-separated part); see signatureVerification for the verification result when publicKey is sent |
isExpired | boolean | false | Whether the token's expiration (exp claim) is in the past |
expiresAt | string | 2036-01-01T00:00:00.000Z | ISO timestamp of when the token expires (from the exp claim); null if there is no exp claim |
issuedAt | string | 2026-01-01T00:00:00.000Z | ISO timestamp of when the token was issued (from iat claim) |
tokenAgePremium | string | 309 days | Human-readable age of the token |
algorithm | string | RS256 | Signing algorithm declared in the token header (alg) |
expiresIn | number | 288757380 | Seconds until the token expires; negative once expired, null if there is no exp claim |
notYetValid | boolean | false | Whether the token's not-before (nbf) claim is still in the future |
securityAnalysisPremium | object | {…} | Structural security assessment of the token: unsigned (alg:none) detection, missing expiration, over-long lifetime, and a list of issues. Does not verify the signature. |
securityAnalysis.isUnsecuredPremium | boolean | false | |
securityAnalysis.hasExpirationPremium | boolean | true | |
securityAnalysis.isLongLivedPremium | boolean | true | |
securityAnalysis.issuesPremium | array | ["Token lifetime exceeds 24 hours"] | |
warning | string | Signature verified with the supplied public key. | Without publicKey, a reminder that the token was decoded but not verified; with publicKey, the verification outcome or the reason it failed |
signatureVerificationPremium | object | {…} | Signature and time-claim check against publicKey; present only when publicKey is sent |
signatureVerification.verifiedPremium | boolean | true | Whether the signature matches publicKey. Always false for alg none, HS* algorithms, or an algorithm that does not match the key type |
signatureVerification.algorithmPremium | string | RS256 | Algorithm from the token header that verification used |
signatureVerification.keyTypePremium | string | RSA | Type of the supplied key, e.g. RSA, EC P-256 or Ed25519 |
signatureVerification.reasonPremium | object | null | Why verification failed; null when verified |
signatureVerification.claimsValidPremium | boolean | true | Whether exp and nbf hold at the current time, reported separately from the signature |
signatureVerification.claimsReasonPremium | object | null | Which time claim failed and when; null when claimsValid is true |
Failure modes
Errors come back as tool errors carrying a sentence the model can act on, not a bare status code. Error handling covers the full list.
| Status | What it means |
|---|---|
400 / 422 | The arguments did not validate. The message names the offending one. |
401 | The OAuth session is invalid or expired — reconnect the server. |
403 | Blocked by a key restriction or an IP allow-list. Never a bad identity. |
404 | This skill is not part of VerveKit. Check the catalog. |
429 | Out of credits, or a brief rate limit. The message tells them apart. |
A call costs 1 credit each time the tool actually runs; a model that reasons about the tool without calling it costs nothing.
Use cases
- Token Debugging
- Debug and inspect JWT tokens during development to view header and payload contents
- Token Analysis
- Analyze token structure and claims for free, and verify the signature against a public key on paid plans
- Expiration Check
- Check token expiration status and view expiration timestamps
- Token Inspection
- Inspect token contents for troubleshooting authentication issues
Other ways to use JWT Decoder Skill
Set up JWT Decoder Skill on VerveKit, or reach the same source a different way. Your VerveKit account and credits work on all of them — one key, one balance.
Related
More in Data Conversion: