facesign-mcp
Server Details
Build and test FaceSign step-up verification flows from your AI coding tool
- Status
- Healthy
- Last Tested
- Transport
- Streamable HTTP
- URL
Available Tools
5 toolsexport_appBInspect
Export the current FaceSign session configuration as a standalone, deployable Next.js application. You MUST supply landingHtml (pre-session page), recapHtml (results page), and uiStrings (per-language dictionary whose keys are invented by you to match placeholders in the HTML). The exported app ships NO default chrome — every visible string comes from you. Run with npm install && npm run dev or deploy to Vercel after setting FACESIGN_API_KEY.
| Name | Required | Description | Default |
|---|---|---|---|
| flow | Yes | Array of nodes forming the session's directed graph | |
| zone | No | Data processing zone | |
| langs | No | Optional whitelist of BCP-47 language codes (from facesign://catalog) the exported session may use. DEFAULT BEHAVIOUR (recommended): OMIT this parameter entirely — the app then supports every language in the FaceSign catalog, and `uiStrings` must cover every catalog language. ONLY set `langs` when the user EXPLICITLY restricts the language set (e.g. 'Spanish-only demo', 'support English and Russian'). Do NOT narrow to `['en']` just because the user described the demo in English or did not mention languages. Example (explicit restriction): ["en", "es", "ru"]. The set of target languages for `uiStrings` is derived from this list (or the full catalog if omitted). | |
| appName | No | Name for the generated app (used in package.json, defaults to 'facesign-app') | |
| avatarId | No | Avatar ID from the facesign://catalog resource. | |
| metadata | No | Arbitrary metadata to attach | |
| recapHtml | Yes | REQUIRED. Raw HTML/JS for the results/recap page. NO CDATA, NO markdown fences. Every visible string MUST come from `uiStrings` (via {{KEY}} or window.t('KEY')). Contract: reads window.__FACESIGN_SESSION__, window.__FACESIGN_SESSION_ID__, window.__refetchSession() (returns Promise with updated session data). The COMMON MISTAKES sections from the `landingHtml` field description apply here too — read them once and follow for both fields. For the complete session data shape (Session, SessionReport, NodeReport types, delayed vs immediate fields), read the `facesign://session-data-types` MCP resource. STYLE GUIDE (Results/Recap Page): Match this visual style for consistency with the FaceSign UI. Background: Light gray #f5f7fa with subtle gradient to light blue at top. Layout: Single-column, max-width 900px, centered (margin 0 auto), padding 2rem. Page header: "Session summary" bold 1.6rem. Subtitle with date/time and duration in muted color #888, font-size 0.9rem. User info card: White card with rounded photo (80-100px), grid of icon+text pairs for age, gender, location, device. Icons in muted blue #6b7faa. Section cards: White background, border-radius 12px, box-shadow 0 2px 12px rgba(0,0,0,.05), padding 1.5rem, margin-bottom 1.5rem. Section headings: Bold 1.15rem, color #1a1a2e, with small emoji/icon prefix (e.g. ✨ AI Analysis, 🔍 Detected Signals, 📋 Transcript). Margin-bottom 1rem. Status banners (full-width within card, border-radius 12px, padding 1rem 1.5rem, white text, bold): - Verified/success: background #4a9d6e, shield ✓ icon - High-risk/warning: background #d97b30, ⚠ warning icon Signal items: Left border 4px solid, padding-left 1rem, margin-bottom 1rem, background white or tinted. - Normal: border-color #059669, light green tint background #f0fdf4 - Suspicious: border-color #f59e0b, light yellow tint background #fefce8 - High-risk: border-color #ef4444, light red tint background #fef2f2 Status badges (inline, pill): border-radius 6px, padding 2px 10px, font-weight 700, font-size 0.8rem, uppercase. - RECOGNIZED/NORMAL: background #d1fae5, color #065f46 - SUSPICIOUS: background #fef3c7, color #92400e Confidence scores: Right-aligned, font-size 0.85rem, color #aaa. Key-value grid: Two-column layout. Label: small text, color #888, font-size 0.8rem, uppercase. Value: font-size 0.95rem, color #1a1a2e, below label. Transcript: Dark background #1e2a3a, border-radius 10px, padding 1.5rem, monospace font. "CONVERSATION LOG" header uppercase, small, muted. Each line: timestamp (gray #777), speaker label (FACESIGN: in teal #4db8a4, USER: in green #6bc96f), message text white. Line-height 1.8. Video: Centered, border-radius 8px, max-width 100%, dark container background. Font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif. BEST PRACTICES FOR STABLE RENDERING (recapHtml): Session data arrives in two stages: some fields are available immediately when the session ends (user info, AI analysis, transcript), while others require polling. Video AI analysis may take up to 60 seconds; media usually arrives sooner. Naive implementations cause layout jumping (DOM rebuild on each poll) and duplicate network requests. ARCHITECTURE: Split Static vs Dynamic Rendering. renderStatic(data) — called ONCE: Renders all sections that won't change: user info (age, sex, location, device), aiAnalysis.overallSummary, aiAnalysis.analysis list, transcript, documentImages. updateDynamic(data) — called on each poll: Updates ONLY sections whose data arrives asynchronously: nodeReports, videoAIAnalysis, deepfakeDetection, and media (avatarVideo, screenshots). IMPORTANT: nodeReports is DELAYED — do NOT render node report data (face compare results, document scan results, etc.) in renderStatic(). Each dynamic section has its own wrapper element (e.g. <div id="video-ai-content">, <div id="node-reports-content">) so updates replace only that element's innerHTML, leaving the rest untouched. Track completion with boolean flags (videoAIDone, recordingDone, nodeReportsDone). Once a section is populated, skip further updates. Set videoAIDone when videoAIAnalysisStatus is either `succeeded` or `failed`. A failed analysis is terminal: replace the spinner with an unavailable message and do not interpret the missing result as clean evidence. If the status is absent, analysis was not requested, so omit the section. HTML STRUCTURE: <!-- Static sections: rendered once --> <div id="userinfo-card"></div> <div id="summary-card"></div> <div id="analysis-card"></div> <!-- Dynamic sections: inner content updated by polling --> <div id="video-ai-card"> <div class="card"> <div class="card-title">Video AI Analysis</div> <div id="video-ai-content"><!-- spinner initially, replaced when data arrives --></div> </div> </div> <div id="recording-card"> <div class="card"> <div class="card-title">Session Recording</div> <div id="recording-content"><!-- spinner initially, replaced when data arrives --></div> </div> </div> <!-- Static section: rendered once --> <div id="transcript-card"></div> POLLING: Single Entry Point with Guard (IIFE pattern): IMPORTANT: Polling MUST be limited — max 30 attempts, max 2 minutes total. Stop polling when limits are reached even if some data hasn't arrived. (function() { var polling = false; var pollCount = 0; var MAX_POLLS = 30; var startTime = Date.now(); var MAX_DURATION = 2 * 60 * 1000; // 2 minutes var videoAIDone = false; var recordingDone = false; var nodeReportsDone = false; function shouldStop() { return pollCount >= MAX_POLLS || (Date.now() - startTime) >= MAX_DURATION; } function poll() { if (polling) return; if (shouldStop()) return; polling = true; pollCount++; if (typeof window.__refetchSession !== 'function') { polling = false; setTimeout(poll, 2000); return; } window.__refetchSession() .then(function(data) { polling = false; updateDynamic(data); if ((!videoAIDone || !recordingDone || !nodeReportsDone) && !shouldStop()) setTimeout(poll, 4000); }) .catch(function() { polling = false; if (!shouldStop()) setTimeout(poll, 4000); }); } function init() { var data = window.__FACESIGN_SESSION__; if (!data) return; renderStatic(data); var needMore = updateDynamic(data); if (needMore) setTimeout(poll, 4000); } if (window.__FACESIGN_SESSION__) { init(); } else { var chk = setInterval(function() { if (window.__FACESIGN_SESSION__) { clearInterval(chk); init(); } }, 500); } })(); KEY RULES: - Call renderStatic() exactly once — avoids DOM rebuild and layout jumps - updateDynamic() only touches dedicated container elements — no reflow outside the updated section - Use a polling boolean guard — prevents concurrent __refetchSession() calls - ALWAYS limit polling — max 30 attempts AND max 2 minutes total. Never poll indefinitely. - Use videoAIDone / recordingDone / nodeReportsDone flags — stops updating a section once its data has been rendered - For video AI, derive videoAIDone from videoAIAnalysisStatus, not from the presence of videoAIAnalysis alone - nodeReports is DELAYED — always render node report data (face compare, document scan, etc.) in updateDynamic(), never in renderStatic() - Single init() entry point via IIFE — eliminates duplicate initialization paths - Check typeof __refetchSession === 'function' before calling — handles the case where the API isn't injected yet - Use setTimeout not setInterval for polling — ensures the next poll starts only after the previous one completes - nodeReport.type is lowercase snake_case ("face_compare", "document_scan") — use case-insensitive comparison - Document scan report fields are Microblink nested objects — use a safe-value extractor function (see COMMON MISTAKES) - videoAIAnalysis criterion is camelCase — convert to human-readable with toStartCase() (see COMMON MISTAKES) SUMMARY: Render once, patch selectively, poll safely with limits. Static content is written to the DOM a single time. Dynamic content (nodeReports, videoAIAnalysis, media) targets specific container elements. Polling is serialized with a guard flag and stops as soon as all async data has arrived or limits are reached (max 30 attempts / 2 minutes). | |
| uiStrings | Yes | REQUIRED. Per-language UI string dictionary: { langId: { key: translatedString } }. You invent the keys to match {{KEY}} / window.t('KEY') in landingHtml and recapHtml. Must contain `en` plus every language in `langs` (or every catalog language if `langs` is omitted). All per-language dicts must share the IDENTICAL key set. | |
| defaultLang | No | Optional fallback BCP-47 language code used when the end-user's browser language is not in `langs`. DEFAULT BEHAVIOUR: OMIT this parameter. When set, must be one of the codes in `langs` (if `langs` is also set). | |
| landingHtml | Yes | REQUIRED. Raw HTML/JS for the pre-session landing page. NO CDATA, NO markdown fences. Every visible string MUST come from `uiStrings` — reference them via {{KEY}} placeholders (interpolated at inject time) or via window.t('KEY') (dynamic at runtime). Contract: reads window.__FACESIGN_FLOW__ (the default flow, for display only). MUST call window.__startSession(input?) to launch the session. input may contain only a declared flowId and/or providedData collected by the form; never pass a flow graph from the browser. STYLE GUIDE (Pre-Session Page): Match this visual style for consistency with the FaceSign UI. Background: Light gray #f5f7fa. Full-viewport centered layout (flexbox, min-height: 100vh; min-height: 100dvh — always use dvh with vh fallback for iOS compatibility). Card: White, border-radius 16px, box-shadow 0 4px 24px rgba(0,0,0,.08), padding 2.5-3rem, max-width 480-600px, centered. Headings: Bold, 1.4-1.8rem, color #1a1a2e, centered in card. Inputs: Full-width, padding 14px 16px, border 2px solid #d1d5db, border-radius 12px, font-size 1rem. Focus: border-color #5b7bab. Placeholder color #9ca3af. Primary buttons: Background #7b8fb5 (steel-blue), color white, font-weight 600, font-size 1.05rem, border-radius 50px (pill shape), padding 14px, full-width in card. Hover: background #6a7fa5. Disabled: background #c5cdd8, cursor not-allowed. Links: Color #4573b8, no underline, underline on hover. Back navigation: "← Back" at top-left of card, color #4573b8, font-size 0.95rem. Profile images: Circular (border-radius 50%), 120-150px diameter, centered, subtle box-shadow. Spacing: 2.5-3rem card padding, 1.5rem between form groups, 1rem between label and input, 2rem above primary button. Font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif. COMMON MISTAKES TO AVOID (UI/CSS): 1. Never set padding or margin on body The custom HTML is injected into a host page that controls its own layout. Setting padding, margin, or min-height on body will conflict with the host page styles and create unwanted spacing. Wrong: body { padding: 2rem; min-height: 100vh; } (Also wrong — using only 100vh without dvh fallback. Always add min-height: 100dvh after 100vh for iOS Safari.) Right — use a wrapper element instead: body { margin: 0; padding: 0; } .wrap { max-width: 860px; margin: 0 auto; padding: 2rem; } The same applies to background on body — avoid it unless you are certain the host page does not set its own background. 2. Use HTML entities for emoji, not JS unicode escapes When building HTML strings in JavaScript (via innerHTML, string concatenation, etc.), JS unicode escapes like \ud83c\udfa5 will NOT render as emoji. They only work inside JS string literals that are directly displayed via textContent or similar APIs. Wrong — renders as garbled text: h += '<div class="title">\ud83c\udfa5 Video AI Analysis</div>'; Right — use HTML numeric entities: h += '<div class="title">🎥 Video AI Analysis</div>'; Also right — use emoji directly in static HTML (outside of JS): <div class="title">🎥 Video AI Analysis</div> Common emoji HTML entities reference: 👤 👤 User/person 🎂 🎂 Birthday cake 📍 📍 Pin/location 💻 💻 Computer ✨ ✨ Sparkles 🔍 🔍 Search 🎥 🎥 Camera 🎬 🎬 Clapper board 📋 📋 Clipboard ⚧ ⚧ Gender symbol ✓ ✓ Checkmark 3. CSS class name collision between landingHtml and recapHtml CRITICAL: landingHtml and recapHtml are injected into the SAME host document. If both fragments use the same class names (e.g., .card, .container, .header), styles from one will leak into the other, causing layout breakage. ALWAYS use unique prefixed class names: - landingHtml (pre-session/landing page): prefix all classes with .lp- (e.g., .lp-card, .lp-header, .lp-btn) - recapHtml (results page): prefix all classes with .r- (e.g., .r-card, .r-header, .r-wrap) Wrong — causes collisions: /* in landingHtml */ .card { max-width: 460px; } /* in recapHtml */ .card { max-width: 900px; } Right — namespaced: /* in landingHtml */ .lp-card { max-width: 460px; } /* in recapHtml */ .r-card { max-width: 900px; } COMMON MISTAKES TO AVOID (Code/API): 4. window.__startSession race condition NEVER call window.__startSession() synchronously in a click handler. The host page may not have injected the function yet. ALWAYS use polling: function waitAndStart(input) { var n = 0; var iv = setInterval(function() { n++; if (typeof window.__startSession === 'function') { clearInterval(iv); window.__startSession(input); } else if (n > 100) { clearInterval(iv); document.body.innerHTML = '<p style="text-align:center;padding:2rem;color:red;">Failed to initialize session. Please refresh.</p>'; } }, 100); } Wrong: startBtn.addEventListener('click', function() { window.__startSession(); }); Right: startBtn.addEventListener('click', function() { waitAndStart(); }); For exported apps, NEVER send window.__FACESIGN_FLOW__ or another graph back to the server. To select a flow declared in export_app, pass its ID: waitAndStart({ flowId: 'enhanced', providedData: { name: nameInput.value } }); 5. Permissions are automatic — do NOT add a permissions node by default FaceSign automatically requests camera and microphone permissions at session start. Most flows do NOT need a PERMISSIONS node. Only add one in these specific cases: (a) The user wants to request microphone and camera permissions separately at different times during the flow (instead of both at once at session start). (b) The user wants to move the permission request to the initial page (landingHtml), outside the FaceSign flow itself. 6. nodeReport.type values are lowercase snake_case The API returns nodeReport.type in lowercase snake_case: "face_compare", "document_scan", "conversation", "permissions", "liveness_detection", etc. — NOT uppercase like "FACE_COMPARE". ALWAYS use case-insensitive comparison when looking up node reports: function getNode(r, type) { var rr = (r && r.nodeReports) || []; var tl = type.toLowerCase(); for (var i = 0; i < rr.length; i++) { if (rr[i].type && rr[i].type.toLowerCase() === tl) return rr[i]; } return null; } // Usage: getNode(report, 'face_compare'), getNode(report, 'document_scan') 7. Microblink document report — nested field structure DOCUMENT_SCAN nodeReport.report fields are NOT plain strings. They use Microblink's nested structure: firstName: { latin: { value: "JANICE" } } dateOfBirth: { originalString: { latin: { value: "04/30/1970" } } } — OR — dateOfBirth: { day: 30, month: 4, year: 1970 } NEVER read fields directly as strings (e.g., dr.firstName will be an object, not "JANICE"). ALWAYS use this safe-value extractor: function sv(v) { if (v == null) return ''; if (typeof v === 'string') return v; if (v.latin && v.latin.value != null) return String(v.latin.value); if (v.originalString) return sv(v.originalString); if (v.day != null && v.month != null && v.year != null) return v.month + '/' + v.day + '/' + v.year; return ''; } // Usage: sv(dr.firstName) → "JANICE", sv(dr.dateOfBirth) → "04/30/1970" 8. videoAIAnalysis criterion is camelCase — format for display The criterion field (e.g., "facialExpressionAndMovement", "useOfExternalDevices") comes in camelCase. Convert to human-readable format: function toStartCase(s) { if (!s) return ''; return s.replace(/([A-Z]+)/g, function(m) { return ' ' + m.toLowerCase(); }) .trim().replace(/^./, function(c) { return c.toUpperCase(); }); } // "facialExpressionAndMovement" → "Facial expression and movement" 9. Conversation node: condition vs prompt In CONVERSATION nodes: - "condition" (in outcomes) = ONLY describes the trigger event for transitioning to the next node (e.g., "User explicitly agrees to proceed") - "prompt" = ALL instructions for the avatar's behavior, including how to greet, how to respond to questions, how to handle objections, and how to persuade - User questions or objections during the conversation are handled within the SAME node (continued dialog), NOT via separate outcomes - Conversation nodes can have any number of outcomes depending on the use case: * Branching nodes (e.g., "what color?") → one outcome per branch + a fallback * Consent/agreement nodes → typically 2: agreement + fallback after N attempts - ALWAYS include a fallback outcome for when the conversation stalls (e.g., "conversation exceeded N exchanges with no condition met" or "user does not want to reply") Wrong — putting behavior instructions in condition: "condition": "User agrees. If they ask questions, answer warmly and ask again" Right — behavior in prompt, condition is just the trigger: "prompt": "Explain the process. If the user has questions, answer them warmly. Once they're ready, confirm." "condition": "User explicitly agrees or says they are ready" | |
| flowVariants | No | Optional additional flows declared at export time. The landing page selects one by calling window.__startSession({ flowId: 'variant-id' }); the browser never sends a graph. | |
| providedData | No | Pre-known user data | |
| customization | No | UI customization | |
| extractionSchema | No | Optional schema describing fields the FaceSign backend should extract from the session transcript using an LLM. Results are populated post-session at `session.report.extractedData` as `{ [fieldName]: string | number | boolean | null }` (null when the transcript did not contain the data). Render results in your recapHtml when present. | |
| clientReferenceId | No | Your own reference ID for this session | |
| videoAIAnalysisEnabled | No | Enable video AI fraud analysis |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It does disclose meaningful behavioral traits: the exported app ships NO default chrome, every visible string comes from the caller, and runtime/deploy steps (npm install && npm run dev, Vercel after setting FACESIGN_API_KEY) are stated. However, for a generative/export operation it does not disclose side effects on the existing session, filesystem writes, reversibility, or any permissions/authentication requirements beyond the deployment API key.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded with the core purpose, with no wasted words. However, for a tool of this complexity (16 params, deeply nested flow graph, high-stakes HTML contracts), the description is arguably under-specified — critical pitfalls (class-name collisions, __startSession race condition, langs narrowing default) live only in the schema and are not surfaced at the tool level. It is efficient but does not earn top marks for a tool this complex.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The schema is exceptionally rich, covering flow node types, HTML contracts, and langs default behavior in detail, so much of the burden is carried there. The description supplies the essential top-level framing (output type, required inputs, deploy method). Still, it omits any mention of the `flow` parameter's importance or the highest-risk caveats, and with no output schema or annotations the agent must dive entirely into a very large schema to use the tool safely. Adequate but with clear gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description does add top-level value by calling out the required trio (landingHtml, recapHtml, uiStrings) and the key-invention contract for uiStrings matching HTML placeholders — reinforcing the schema's per-field text. But most parameter meaning is already exhaustively documented in the schema (notably langs default behavior and the HTML field contracts), so the description mainly reinforces rather than compensates.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Export'), resource ('FaceSign session configuration'), and clear outcome ('standalone, deployable Next.js application'). This inherently distinguishes it from siblings like get_session (fetches data), launch_session_ui (launches UI), list_sessions (lists), and set_api_key (sets credentials) — none of which produce a deployable app. It stops short of a 5 because it does not explicitly name or contrast the sibling tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives no when-to-use guidance or explicit alternatives. It explains what must be supplied (landingHtml, recapHtml, uiStrings) and how to run the output, but never states when to choose export_app over get_session/launch_session_ui, nor when NOT to use it. The only usage-directional guidance lives deep in the schema's `langs` field description, which is too buried to count as effective tool-level guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_sessionAInspect
Retrieve detailed information about a FaceSign session including status, transcript, AI analysis, node reports, video AI analysis, and asynchronous analysis lifecycle states.
| Name | Required | Description | Default |
|---|---|---|---|
| sessionId | Yes | The session ID to retrieve |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are present, so the description carries the full burden. It clearly communicates a read operation by using 'Retrieve' and lists the content categories returned. However, it does not disclose error behavior, permissions, rate limits, or the meaning of 'asynchronous analysis lifecycle states,' leaving some behavioral traits unexplained.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, efficient sentence that front-loads the action and resource, then enumerates the included data types without redundancy. Every phrase adds useful information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter read tool with no output schema, the description provides a strong sense of the return content by listing major components (transcript, AI analysis, node reports, etc.). It is not exhaustive but gives an appropriate high-level overview. Slight ambiguity remains around the 'asynchronous analysis lifecycle states' but the tool is still usable.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with 'sessionId' already described as 'The session ID to retrieve'. The description adds no further parameter-specific meaning, so it does not exceed the schema baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb 'Retrieve' with a clear resource 'FaceSign session' and enumerates the exact scope of information returned. This clearly distinguishes it from siblings like list_sessions (which lists sessions) and launch_session_ui (which launches UI).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The context implies use when a specific session's detailed info is needed, especially after listing sessions. However, it does not explicitly state when to use this tool vs alternatives, nor does it mention exclusions or prerequisites. Sibling tools provide contrast but the description itself offers no guidance on selection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
launch_session_uiAInspect
REQUIRES set_api_key to be called first. Launch a local web page in the browser for a FaceSign session. You MUST supply landingHtml (the pre-session page shown before the iframe), recapHtml (the results page shown after the session finishes), and uiStrings (a per-language dictionary whose keys are invented by you to match placeholders in the HTML). The MCP server provides NO default chrome — every visible string comes from you. Each page load / refresh creates a fresh session.
| Name | Required | Description | Default |
|---|---|---|---|
| flow | Yes | Array of nodes forming the session's directed graph | |
| zone | No | Data processing zone | |
| langs | No | Optional whitelist of BCP-47 language codes (from facesign://catalog) the session may use. DEFAULT BEHAVIOUR (recommended): OMIT this parameter entirely — the session then supports every language in the FaceSign catalog, and `uiStrings` must cover every catalog language. ONLY set `langs` when the user EXPLICITLY restricts the language set (e.g. 'Spanish-only demo', 'support English and Russian'). Do NOT narrow to `['en']` just because the user described the demo in English or did not mention languages — that would silently drop multilingual support. Example (explicit restriction): ["en", "es", "ru"]. The set of target languages for `uiStrings` is derived from this list (or the full catalog if omitted). | |
| avatarId | No | Avatar ID from the facesign://catalog resource. | |
| metadata | No | Arbitrary metadata to attach | |
| recapHtml | Yes | REQUIRED. Raw HTML/JS for the results/recap page. NO CDATA, NO markdown fences. Every visible string MUST come from `uiStrings` (via {{KEY}} or window.t('KEY')). Contract: reads window.__FACESIGN_SESSION__ (session data, re-populated on each poll), window.__FACESIGN_SESSION_ID__, window.__refetchSession() (returns Promise with updated data). The COMMON MISTAKES sections from the `landingHtml` field description apply here too — read them once and follow for both fields. For the complete session data shape (Session, SessionReport, NodeReport types, delayed vs immediate fields), read the `facesign://session-data-types` MCP resource. STYLE GUIDE (Results/Recap Page): Match this visual style for consistency with the FaceSign UI. Background: Light gray #f5f7fa with subtle gradient to light blue at top. Layout: Single-column, max-width 900px, centered (margin 0 auto), padding 2rem. Page header: "Session summary" bold 1.6rem. Subtitle with date/time and duration in muted color #888, font-size 0.9rem. User info card: White card with rounded photo (80-100px), grid of icon+text pairs for age, gender, location, device. Icons in muted blue #6b7faa. Section cards: White background, border-radius 12px, box-shadow 0 2px 12px rgba(0,0,0,.05), padding 1.5rem, margin-bottom 1.5rem. Section headings: Bold 1.15rem, color #1a1a2e, with small emoji/icon prefix (e.g. ✨ AI Analysis, 🔍 Detected Signals, 📋 Transcript). Margin-bottom 1rem. Status banners (full-width within card, border-radius 12px, padding 1rem 1.5rem, white text, bold): - Verified/success: background #4a9d6e, shield ✓ icon - High-risk/warning: background #d97b30, ⚠ warning icon Signal items: Left border 4px solid, padding-left 1rem, margin-bottom 1rem, background white or tinted. - Normal: border-color #059669, light green tint background #f0fdf4 - Suspicious: border-color #f59e0b, light yellow tint background #fefce8 - High-risk: border-color #ef4444, light red tint background #fef2f2 Status badges (inline, pill): border-radius 6px, padding 2px 10px, font-weight 700, font-size 0.8rem, uppercase. - RECOGNIZED/NORMAL: background #d1fae5, color #065f46 - SUSPICIOUS: background #fef3c7, color #92400e Confidence scores: Right-aligned, font-size 0.85rem, color #aaa. Key-value grid: Two-column layout. Label: small text, color #888, font-size 0.8rem, uppercase. Value: font-size 0.95rem, color #1a1a2e, below label. Transcript: Dark background #1e2a3a, border-radius 10px, padding 1.5rem, monospace font. "CONVERSATION LOG" header uppercase, small, muted. Each line: timestamp (gray #777), speaker label (FACESIGN: in teal #4db8a4, USER: in green #6bc96f), message text white. Line-height 1.8. Video: Centered, border-radius 8px, max-width 100%, dark container background. Font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif. BEST PRACTICES FOR STABLE RENDERING (recapHtml): Session data arrives in two stages: some fields are available immediately when the session ends (user info, AI analysis, transcript), while others require polling. Video AI analysis may take up to 60 seconds; media usually arrives sooner. Naive implementations cause layout jumping (DOM rebuild on each poll) and duplicate network requests. ARCHITECTURE: Split Static vs Dynamic Rendering. renderStatic(data) — called ONCE: Renders all sections that won't change: user info (age, sex, location, device), aiAnalysis.overallSummary, aiAnalysis.analysis list, transcript, documentImages. updateDynamic(data) — called on each poll: Updates ONLY sections whose data arrives asynchronously: nodeReports, videoAIAnalysis, deepfakeDetection, and media (avatarVideo, screenshots). IMPORTANT: nodeReports is DELAYED — do NOT render node report data (face compare results, document scan results, etc.) in renderStatic(). Each dynamic section has its own wrapper element (e.g. <div id="video-ai-content">, <div id="node-reports-content">) so updates replace only that element's innerHTML, leaving the rest untouched. Track completion with boolean flags (videoAIDone, recordingDone, nodeReportsDone). Once a section is populated, skip further updates. Set videoAIDone when videoAIAnalysisStatus is either `succeeded` or `failed`. A failed analysis is terminal: replace the spinner with an unavailable message and do not interpret the missing result as clean evidence. If the status is absent, analysis was not requested, so omit the section. HTML STRUCTURE: <!-- Static sections: rendered once --> <div id="userinfo-card"></div> <div id="summary-card"></div> <div id="analysis-card"></div> <!-- Dynamic sections: inner content updated by polling --> <div id="video-ai-card"> <div class="card"> <div class="card-title">Video AI Analysis</div> <div id="video-ai-content"><!-- spinner initially, replaced when data arrives --></div> </div> </div> <div id="recording-card"> <div class="card"> <div class="card-title">Session Recording</div> <div id="recording-content"><!-- spinner initially, replaced when data arrives --></div> </div> </div> <!-- Static section: rendered once --> <div id="transcript-card"></div> POLLING: Single Entry Point with Guard (IIFE pattern): IMPORTANT: Polling MUST be limited — max 30 attempts, max 2 minutes total. Stop polling when limits are reached even if some data hasn't arrived. (function() { var polling = false; var pollCount = 0; var MAX_POLLS = 30; var startTime = Date.now(); var MAX_DURATION = 2 * 60 * 1000; // 2 minutes var videoAIDone = false; var recordingDone = false; var nodeReportsDone = false; function shouldStop() { return pollCount >= MAX_POLLS || (Date.now() - startTime) >= MAX_DURATION; } function poll() { if (polling) return; if (shouldStop()) return; polling = true; pollCount++; if (typeof window.__refetchSession !== 'function') { polling = false; setTimeout(poll, 2000); return; } window.__refetchSession() .then(function(data) { polling = false; updateDynamic(data); if ((!videoAIDone || !recordingDone || !nodeReportsDone) && !shouldStop()) setTimeout(poll, 4000); }) .catch(function() { polling = false; if (!shouldStop()) setTimeout(poll, 4000); }); } function init() { var data = window.__FACESIGN_SESSION__; if (!data) return; renderStatic(data); var needMore = updateDynamic(data); if (needMore) setTimeout(poll, 4000); } if (window.__FACESIGN_SESSION__) { init(); } else { var chk = setInterval(function() { if (window.__FACESIGN_SESSION__) { clearInterval(chk); init(); } }, 500); } })(); KEY RULES: - Call renderStatic() exactly once — avoids DOM rebuild and layout jumps - updateDynamic() only touches dedicated container elements — no reflow outside the updated section - Use a polling boolean guard — prevents concurrent __refetchSession() calls - ALWAYS limit polling — max 30 attempts AND max 2 minutes total. Never poll indefinitely. - Use videoAIDone / recordingDone / nodeReportsDone flags — stops updating a section once its data has been rendered - For video AI, derive videoAIDone from videoAIAnalysisStatus, not from the presence of videoAIAnalysis alone - nodeReports is DELAYED — always render node report data (face compare, document scan, etc.) in updateDynamic(), never in renderStatic() - Single init() entry point via IIFE — eliminates duplicate initialization paths - Check typeof __refetchSession === 'function' before calling — handles the case where the API isn't injected yet - Use setTimeout not setInterval for polling — ensures the next poll starts only after the previous one completes - nodeReport.type is lowercase snake_case ("face_compare", "document_scan") — use case-insensitive comparison - Document scan report fields are Microblink nested objects — use a safe-value extractor function (see COMMON MISTAKES) - videoAIAnalysis criterion is camelCase — convert to human-readable with toStartCase() (see COMMON MISTAKES) SUMMARY: Render once, patch selectively, poll safely with limits. Static content is written to the DOM a single time. Dynamic content (nodeReports, videoAIAnalysis, media) targets specific container elements. Polling is serialized with a guard flag and stops as soon as all async data has arrived or limits are reached (max 30 attempts / 2 minutes). | |
| uiStrings | Yes | REQUIRED. Per-language UI string dictionary: { langId: { key: translatedString } }. You invent the keys to match the {{KEY}} placeholders / window.t('KEY') calls in your landingHtml and recapHtml. Must contain an entry for `en` (ultimate runtime fallback) and for every language in `langs` (or every catalog language if `langs` is omitted). Every per-language dict must share the IDENTICAL set of keys. Example: { "en": { "START": "Start", "LOADING": "Loading..." }, "fr": { "START": "Commencer", "LOADING": "Chargement..." } }. | |
| defaultLang | No | Optional fallback BCP-47 language code used when the end-user's browser language is not in `langs`. DEFAULT BEHAVIOUR: OMIT this parameter. The MCP server falls back to 'en' internally only as a last-resort for UI string lookup; you should not hardcode a default language here unless the user explicitly asks for one. When set, must be one of the codes in `langs` (if `langs` is also set). | |
| landingHtml | Yes | REQUIRED. Raw HTML/JS for the pre-session landing page. NO CDATA, NO markdown fences. Every visible string MUST come from `uiStrings` — reference them via {{KEY}} placeholders (interpolated at inject time) or via window.t('KEY') (dynamic at runtime). Contract: reads window.__FACESIGN_FLOW__ (the flow array). MUST call window.__startSession(updatedFlow?) to launch the session. The session iframe has its own Start button for audio/video autoplay gesture — the landing page does NOT need one for that purpose. STYLE GUIDE (Pre-Session Page): Match this visual style for consistency with the FaceSign UI. Background: Light gray #f5f7fa. Full-viewport centered layout (flexbox, min-height: 100vh; min-height: 100dvh — always use dvh with vh fallback for iOS compatibility). Card: White, border-radius 16px, box-shadow 0 4px 24px rgba(0,0,0,.08), padding 2.5-3rem, max-width 480-600px, centered. Headings: Bold, 1.4-1.8rem, color #1a1a2e, centered in card. Inputs: Full-width, padding 14px 16px, border 2px solid #d1d5db, border-radius 12px, font-size 1rem. Focus: border-color #5b7bab. Placeholder color #9ca3af. Primary buttons: Background #7b8fb5 (steel-blue), color white, font-weight 600, font-size 1.05rem, border-radius 50px (pill shape), padding 14px, full-width in card. Hover: background #6a7fa5. Disabled: background #c5cdd8, cursor not-allowed. Links: Color #4573b8, no underline, underline on hover. Back navigation: "← Back" at top-left of card, color #4573b8, font-size 0.95rem. Profile images: Circular (border-radius 50%), 120-150px diameter, centered, subtle box-shadow. Spacing: 2.5-3rem card padding, 1.5rem between form groups, 1rem between label and input, 2rem above primary button. Font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif. COMMON MISTAKES TO AVOID (UI/CSS): 1. Never set padding or margin on body The custom HTML is injected into a host page that controls its own layout. Setting padding, margin, or min-height on body will conflict with the host page styles and create unwanted spacing. Wrong: body { padding: 2rem; min-height: 100vh; } (Also wrong — using only 100vh without dvh fallback. Always add min-height: 100dvh after 100vh for iOS Safari.) Right — use a wrapper element instead: body { margin: 0; padding: 0; } .wrap { max-width: 860px; margin: 0 auto; padding: 2rem; } The same applies to background on body — avoid it unless you are certain the host page does not set its own background. 2. Use HTML entities for emoji, not JS unicode escapes When building HTML strings in JavaScript (via innerHTML, string concatenation, etc.), JS unicode escapes like \ud83c\udfa5 will NOT render as emoji. They only work inside JS string literals that are directly displayed via textContent or similar APIs. Wrong — renders as garbled text: h += '<div class="title">\ud83c\udfa5 Video AI Analysis</div>'; Right — use HTML numeric entities: h += '<div class="title">🎥 Video AI Analysis</div>'; Also right — use emoji directly in static HTML (outside of JS): <div class="title">🎥 Video AI Analysis</div> Common emoji HTML entities reference: 👤 👤 User/person 🎂 🎂 Birthday cake 📍 📍 Pin/location 💻 💻 Computer ✨ ✨ Sparkles 🔍 🔍 Search 🎥 🎥 Camera 🎬 🎬 Clapper board 📋 📋 Clipboard ⚧ ⚧ Gender symbol ✓ ✓ Checkmark 3. CSS class name collision between landingHtml and recapHtml CRITICAL: landingHtml and recapHtml are injected into the SAME host document. If both fragments use the same class names (e.g., .card, .container, .header), styles from one will leak into the other, causing layout breakage. ALWAYS use unique prefixed class names: - landingHtml (pre-session/landing page): prefix all classes with .lp- (e.g., .lp-card, .lp-header, .lp-btn) - recapHtml (results page): prefix all classes with .r- (e.g., .r-card, .r-header, .r-wrap) Wrong — causes collisions: /* in landingHtml */ .card { max-width: 460px; } /* in recapHtml */ .card { max-width: 900px; } Right — namespaced: /* in landingHtml */ .lp-card { max-width: 460px; } /* in recapHtml */ .r-card { max-width: 900px; } COMMON MISTAKES TO AVOID (Code/API): 4. window.__startSession race condition NEVER call window.__startSession() synchronously in a click handler. The host page may not have injected the function yet. ALWAYS use polling: function waitAndStart(input) { var n = 0; var iv = setInterval(function() { n++; if (typeof window.__startSession === 'function') { clearInterval(iv); window.__startSession(input); } else if (n > 100) { clearInterval(iv); document.body.innerHTML = '<p style="text-align:center;padding:2rem;color:red;">Failed to initialize session. Please refresh.</p>'; } }, 100); } Wrong: startBtn.addEventListener('click', function() { window.__startSession(); }); Right: startBtn.addEventListener('click', function() { waitAndStart(); }); For exported apps, NEVER send window.__FACESIGN_FLOW__ or another graph back to the server. To select a flow declared in export_app, pass its ID: waitAndStart({ flowId: 'enhanced', providedData: { name: nameInput.value } }); 5. Permissions are automatic — do NOT add a permissions node by default FaceSign automatically requests camera and microphone permissions at session start. Most flows do NOT need a PERMISSIONS node. Only add one in these specific cases: (a) The user wants to request microphone and camera permissions separately at different times during the flow (instead of both at once at session start). (b) The user wants to move the permission request to the initial page (landingHtml), outside the FaceSign flow itself. 6. nodeReport.type values are lowercase snake_case The API returns nodeReport.type in lowercase snake_case: "face_compare", "document_scan", "conversation", "permissions", "liveness_detection", etc. — NOT uppercase like "FACE_COMPARE". ALWAYS use case-insensitive comparison when looking up node reports: function getNode(r, type) { var rr = (r && r.nodeReports) || []; var tl = type.toLowerCase(); for (var i = 0; i < rr.length; i++) { if (rr[i].type && rr[i].type.toLowerCase() === tl) return rr[i]; } return null; } // Usage: getNode(report, 'face_compare'), getNode(report, 'document_scan') 7. Microblink document report — nested field structure DOCUMENT_SCAN nodeReport.report fields are NOT plain strings. They use Microblink's nested structure: firstName: { latin: { value: "JANICE" } } dateOfBirth: { originalString: { latin: { value: "04/30/1970" } } } — OR — dateOfBirth: { day: 30, month: 4, year: 1970 } NEVER read fields directly as strings (e.g., dr.firstName will be an object, not "JANICE"). ALWAYS use this safe-value extractor: function sv(v) { if (v == null) return ''; if (typeof v === 'string') return v; if (v.latin && v.latin.value != null) return String(v.latin.value); if (v.originalString) return sv(v.originalString); if (v.day != null && v.month != null && v.year != null) return v.month + '/' + v.day + '/' + v.year; return ''; } // Usage: sv(dr.firstName) → "JANICE", sv(dr.dateOfBirth) → "04/30/1970" 8. videoAIAnalysis criterion is camelCase — format for display The criterion field (e.g., "facialExpressionAndMovement", "useOfExternalDevices") comes in camelCase. Convert to human-readable format: function toStartCase(s) { if (!s) return ''; return s.replace(/([A-Z]+)/g, function(m) { return ' ' + m.toLowerCase(); }) .trim().replace(/^./, function(c) { return c.toUpperCase(); }); } // "facialExpressionAndMovement" → "Facial expression and movement" 9. Conversation node: condition vs prompt In CONVERSATION nodes: - "condition" (in outcomes) = ONLY describes the trigger event for transitioning to the next node (e.g., "User explicitly agrees to proceed") - "prompt" = ALL instructions for the avatar's behavior, including how to greet, how to respond to questions, how to handle objections, and how to persuade - User questions or objections during the conversation are handled within the SAME node (continued dialog), NOT via separate outcomes - Conversation nodes can have any number of outcomes depending on the use case: * Branching nodes (e.g., "what color?") → one outcome per branch + a fallback * Consent/agreement nodes → typically 2: agreement + fallback after N attempts - ALWAYS include a fallback outcome for when the conversation stalls (e.g., "conversation exceeded N exchanges with no condition met" or "user does not want to reply") Wrong — putting behavior instructions in condition: "condition": "User agrees. If they ask questions, answer warmly and ask again" Right — behavior in prompt, condition is just the trigger: "prompt": "Explain the process. If the user has questions, answer them warmly. Once they're ready, confirm." "condition": "User explicitly agrees or says they are ready" | |
| providedData | No | Pre-known user data | |
| customization | No | UI customization | |
| extractionSchema | No | Optional schema describing fields the FaceSign backend should extract from the session transcript using an LLM. Results are populated post-session at `session.report.extractedData` as `{ [fieldName]: string | number | boolean | null }` (null when the transcript did not contain the data — every field is treated as optional). Use this for structured data collection that does NOT need its own dedicated node — e.g. follow-up questions in a single CONVERSATION node where you'd otherwise have to author multiple branching outcomes. The conversation flow itself is unaffected; extraction runs on the final transcript. Render the results in your recapHtml when present. | |
| clientReferenceId | No | Your own reference ID for this session | |
| videoAIAnalysisEnabled | No | Enable video AI fraud analysis |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden, and it delivers: it discloses the fresh-session-per-load behavior ('Each page load / refresh creates a fresh session'), states 'The MCP server provides NO default chrome — every visible string comes from you', and surfaces the set_api_key prerequisite. That is substantial behavioral disclosure beyond what any structured field would indicate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Critical information is front-loaded well (purpose, prerequisite, then required params), but the description is extremely long — embedding full style guides, code examples, and polling implementations that could live in resources. While every sentence is dense, the sheer volume makes it a heavy read for an agent; it is thorough rather than appropriately concise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool of this complexity — 14 parameters with deeply nested flow schemas — the description is exceptionally complete: it documents contracts, deprecation-style cautions, polling limits (max 30 attempts / 2 minutes), case-sensitivity pitfalls, and references companion resources (facesign://catalog, facesign://session-data-types). Nothing an agent needs to call it correctly seems missing, though this completeness comes at the cost of conciseness.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is 100%, the description adds enormous meaning beyond the schema's field names. For uiStrings it explains the key-invention requirement and cross-language consistency; for langs it warns against silently dropping multilingual support and specifies the derived language set; for landingHtml/recapHtml it documents the runtime contracts (window.__FACESIGN_SESSION__, __refetchSession, __startSession), common mistakes, and style guides. This is exactly the added value parameter semantics should provide.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Launch a local web page in the browser for a FaceSign session.' The purpose is unmistakable. It does not, however, differentiate from its closest sibling export_app, which also relates to flow/session authorship, so an agent must infer the distinction (launching a session vs exporting an app) on its own.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly names the prerequisite — 'REQUIRES set_api_key to be called first' — and gives clear when-not guidance for the langs parameter ('ONLY set langs when the user EXPLICITLY restricts'). But it never states when to prefer this tool over export_app or mentions any exclusion conditions for the tool itself, leaving the primary alternative routing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_sessionsBInspect
List FaceSign sessions with optional filtering by status, date range, and search term. Supports pagination via cursor.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max sessions to return (1-100, default 10) | |
| cursor | No | Pagination cursor from a previous response | |
| flowId | No | Filter sessions by flow ID | |
| search | No | Search term to filter sessions | |
| sortBy | No | Field to sort by | |
| status | No | Filter by session status | |
| toDate | No | End of date range (Unix timestamp in ms) | |
| fromDate | No | Start of date range (Unix timestamp in ms) | |
| sortOrder | No | Sort direction | |
| clientReferenceId | No | Filter by client reference ID |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full behavioral disclosure burden. It mentions pagination via cursor, which is a useful trait, but does not explicitly state that the operation is read-only, describe the return format, or mention any side effects or limitations. The 'List' verb implies non-destructive behavior, but it is not explicit.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single, efficient sentence that conveys the core purpose and key filtering options. It is front-loaded with the main action and adds pagination support. While compact, it could list a few more specific examples of filters but remains appropriately sized.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 10 optional parameters, no output schema, and no annotations, the description is incomplete. It does not explain the response structure, error behavior, default sorting, or how to iterate through pages beyond mentioning the cursor. An agent would need to infer many operational details before successfully using the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the input schema already documents all 10 parameters. The description adds a high-level summary of filter capabilities ('status, date range, and search term') and mentions cursor-based pagination, which mirrors schema details. No additional semantic value is provided beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'List FaceSign sessions' with filtering options. The purpose is clear, but it doesn't explicitly differentiate from sibling get_session, relying on the verb 'list' to imply it returns multiple sessions. This is slightly above 'clear but no sibling differentiation'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to use this tool over alternatives. The description only lists optional filters and pagination, but doesn't mention any exclusions, prerequisites, or relation to get_session. An agent must infer that this is for collecting multiple sessions vs. retrieving a single one.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_api_keyAInspect
Set your FaceSign API key for this session. This must be called before any other FaceSign tools. Get your API key from the FaceSign dashboard. The key starts with sk_live_... or sk_test_... Optionally override the API server URL via serverUrl — intended for FaceSign developers pointing the SDK at a non-production backend (e.g. a dev server). Leave unset to use the default production API.
| Name | Required | Description | Default |
|---|---|---|---|
| apiKey | Yes | Your FaceSign API key (sk_live_... or sk_test_...) | |
| serverUrl | No | Optional FaceSign API server URL override. Only set this if the user explicitly asks to target a non-default server (e.g. a dev backend). Omit otherwise to use the production API. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses session-scoped behavior, key format, and the optional serverUrl override with production default. It doesn't mention error handling or side effects but adequately explains the tool's function for a simple setter.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three concise sentences, front-loaded with purpose and prerequisite, each sentence adds value. No redundancy or fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple 2-param setter with full schema coverage, the description provides all necessary context: purpose, prerequisite, optional use case, and default behavior. No gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers both parameters with detailed descriptions (100% coverage). The description adds the session requirement but doesn't add significant parameter-level meaning beyond what the schema already provides. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: 'Set your FaceSign API key for this session' with a specific verb and resource. It also distinguishes itself from sibling tools by noting it must be called before any other FaceSign tools, establishing it as a setup prerequisite.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to call this tool ('must be called before any other FaceSign tools') and provides guidance on when to set serverUrl (only if explicitly targeting a non-default server). This clearly differentiates usage from alternatives and gives actionable instructions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
3 tool updates
- Changed
export_app3 fields changed- added
Input schema / properties / customization / properties / permissionsPage / properties / buttonTextTranslatesAdded value: +{ + "additionalProperties": { + "type": "string" + }, + "description": "Localized permission-button text, for example { es: 'Continuar' }", + "type": "object" +} - added
Input schema / properties / customization / properties / permissionsPage / properties / mainHeadingTranslatesAdded value: +{ + "$ref": "#/properties/customization/properties/permissionsPage/properties/buttonTextTranslates", + "description": "Localized permission-page headings" +} - added
Input schema / properties / customization / properties / permissionsPage / properties / subheadingTranslatesAdded value: +{ + "$ref": "#/properties/customization/properties/permissionsPage/properties/buttonTextTranslates", + "description": "Localized permission-page subheadings" +}
- Changed
launch_session_ui3 fields changed- added
Input schema / properties / customization / properties / permissionsPage / properties / buttonTextTranslatesAdded value: +{ + "additionalProperties": { + "type": "string" + }, + "description": "Localized permission-button text, for example { es: 'Continuar' }", + "type": "object" +} - added
Input schema / properties / customization / properties / permissionsPage / properties / mainHeadingTranslatesAdded value: +{ + "$ref": "#/properties/customization/properties/permissionsPage/properties/buttonTextTranslates", + "description": "Localized permission-page headings" +} - added
Input schema / properties / customization / properties / permissionsPage / properties / subheadingTranslatesAdded value: +{ + "$ref": "#/properties/customization/properties/permissionsPage/properties/buttonTextTranslates", + "description": "Localized permission-page subheadings" +}
- Changed
list_sessions1 field changed- added
Input schema / properties / flowIdAdded value: +{ + "description": "Filter sessions by flow ID", + "type": "string" +}
2 tool updates
- Changed
export_app2 fields changed- added
Input schema / properties / customization / properties / controls / properties / autoHideAdded value: +{ + "description": "Whether controls may automatically fade out. Omitted or true preserves the default behavior; false keeps the selected buttons visible. Ignored when showUxControls is false", + "type": "boolean" +} - added
Input schema / properties / customization / properties / controls / properties / buttonsAdded value: +{ + "description": "Optional exact subset of in-session control buttons. Omit this field for the recommended default panel with all applicable controls. Set it only when the user requests a specialized flow or UI; for example, [\"captions\"] creates a captions-only panel, while an empty array hides the panel. Ignored when showUxControls is false", + "items": { + "enum": [ + "microphone", + "camera", + "captions", + "language", + "close" + ], + "type": "string" + }, + "type": "array" +}
- Changed
launch_session_ui2 fields changed- added
Input schema / properties / customization / properties / controls / properties / autoHideAdded value: +{ + "description": "Whether controls may automatically fade out. Omitted or true preserves the default behavior; false keeps the selected buttons visible. Ignored when showUxControls is false", + "type": "boolean" +} - added
Input schema / properties / customization / properties / controls / properties / buttonsAdded value: +{ + "description": "Optional exact subset of in-session control buttons. Omit this field for the recommended default panel with all applicable controls. Set it only when the user requests a specialized flow or UI; for example, [\"captions\"] creates a captions-only panel, while an empty array hides the panel. Ignored when showUxControls is false", + "items": { + "enum": [ + "microphone", + "camera", + "captions", + "language", + "close" + ], + "type": "string" + }, + "type": "array" +}
5 tool updates
- First observed
export_app - First observed
get_session - First observed
launch_session_ui - First observed
list_sessions - First observed
set_api_key
Frequently Asked Questions
Claiming proves that you control a remote MCP connector. It does not move, proxy, or interrupt the server.
Open the connector listing, choose Claim ownership, and sign in to Glama.
Complete one verification method:
GitHub identity — fastest for official registry listings. For a namespace such as
io.github.alice/server, link the matching GitHub user, then choose Claim with GitHub. An organization namespace such asio.github.acme/serveralso needs that organization to have installed the Glama AI GitHub App and approved its permissions, because GitHub discloses organization membership only to apps it has installed. Use HTTP or DNS when it has not.HTTP challenge — works when you can deploy a public file. Generate a token, publish the exact JSON Glama shows at
/.well-known/glama.jsonon the same origin as the connector, then choose Check HTTP challenge.DNS challenge — works when you control DNS but cannot change the server. Generate a token, create the exact TXT record Glama shows, wait for it to propagate, then choose Check DNS challenge.
After verification, Glama sends a confirmation email and gives you access to listing details, thumbnails, health checks, and analytics. Keep the HTTP file or DNS record in place: Glama periodically checks it and ownership remains verified while the token is discoverable.
The HTTP ownership file has this structure:
{
"$schema": "https://glama.ai/mcp/schemas/connector.json",
"claim": "glama_claim_..."
}Claim tokens are opaque, stable, and bound to the signed-in Glama account. They contain no email address or other personal information. If Glama can no longer discover a verified HTTP or DNS token, it starts a seven-day grace period before removing claim-based access. Restore the same token during that period to keep ownership verified. Never publish an email address, Glama session token, GitHub token, or connector credential as ownership proof.
If verification fails, confirm that you copied the current token exactly. The HTTP file must be public, return valid JSON with a successful HTTP response, and stay on the connector's origin. DNS changes may need more time to propagate. A claim cannot transfer to a different origin or hostname: if the connector target changes, Glama starts the grace period and the new target must be claimed separately after the previous claim is released.
For a connector linked to the official MCP Registry, registry updates continue to replace its name, description, and URL by default. After claiming, open Manage connector and enable Use Glama listing details as the source of truth if edits made on Glama should be preserved. Categories and thumbnails are always managed on Glama; registry linkage and technical connection settings continue to sync.
Control your server's listing on Glama, including description and metadata
Access analytics and receive server usage reports
Get monitoring and health status updates for your server
Feature your server to boost visibility and reach more users
To improve your MCP server's ranking:
Claim ownership of the server listing
Complete the server profile with an accurate description and thumbnail
Provide a test profile so Glama can connect to and evaluate the server
Keep tool definitions clear and complete to earn a high Tool Definition Quality Score (TDQS)
Route real usage through the Glama Gateway; more recorded successful server uses also improve the ranking
For users:
Full audit trail – every tool call is logged with inputs and outputs for compliance and debugging
Granular tool control – enable or disable individual tools per connector to limit what your AI agents can do
Centralized credential management – store and rotate API keys and OAuth tokens in one place
Change alerts – get notified when a connector changes its schema, adds or removes tools, or updates tool definitions, so nothing breaks silently
For server owners:
Proven adoption – public usage metrics on your listing show real-world traction and build trust with prospective users
Tool-level analytics – see which tools are being used most, helping you prioritize development and documentation
Direct user feedback – users can report issues and suggest improvements through the listing, giving you a channel you would not have otherwise
The connector status is unhealthy when Glama is unable to successfully connect to the server. This can happen for several reasons:
The server is experiencing an outage
The URL of the server is wrong
Credentials required to access the server are missing or invalid
If you are the owner of this MCP connector and would like to make modifications to the listing, including providing test credentials for accessing the server, please contact support@glama.ai.
Discussions
No comments yet. Be the first to start the discussion!
Related MCP Connectors
Form companies, manage bank accounts, cards, invoices and more — directly from your AI coding tools.
Direct access to Cypress tests results and accessibility reports in your AI workflow.
Zero-setup MCP gateway securely connecting AI to your tools with authentication and workflows
Give AI coding agents access to your Vynix visual feedback, bug reports, and AI diagnosis.
Related MCP Servers
- AlicenseBqualityCmaintenanceExposes Fortgate's reusable identity and KYC as tools for AI agents to initiate KYC flows, query status, and verify credentials without manual integration.1MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI coding assistants to test web accessibility by scanning URLs, detecting violations, and running focused audits on keyboard navigation, screen reader compatibility, and WCAG criteria — all within the assistant's loop.MIT
- AlicenseAqualityDmaintenanceAllow your AI coding agents to access Figma files & prototypes directly. You can DM me for any issues / improvements: https://x.com/jasonzhou1993 1. Access all figma pages 2. Access all figma components 3. Access figma prototype flows5367MIT

Apiiro Guardian Agent MCPofficial
AlicenseNot gradedqualityCmaintenanceEnables AI coding assistants to leverage Application Security Posture Management (ASPM) capabilities, allowing developers to write secure code, query security risks, trigger diff scans, and manage security findings directly from their AI assistant.4Apache 2.0
Glama MCP Gateway
Add one secure layer between your agents and this server.
TDQS
Each tool targets a distinct action: setting the API key, launching the UI, retrieving session details, listing sessions, and exporting as an app. Even though launch_session_ui and export_app both require HTML/UI string parameters, their purposes (local run vs. deployable export) are clearly different.
All tool names follow a consistent verb_noun pattern in snake_case: set_api_key, launch_session_ui, get_session, list_sessions, export_app. The verbs are clear and the nouns are the corresponding resource, making the pattern predictable.
With 5 tools, the set is well-scoped and each tool directly supports the server's purpose of managing FaceSign sessions. This is comfortably within the ideal 3-15 range and avoids unnecessary bloat.
The tool surface covers the core workflow: setting the API key, launching a session UI, retrieving session data, listing sessions, and exporting a standalone app. A minor gap is the lack of explicit session cancellation or deletion, but this appears manageable since sessions are ephemeral per launch and status can be filtered.