All notable changes to this project will be documented in this file. Format: Keep a Changelog — Semantic Versioning.
- BUG-041 —
dataverse_check_record_accessbound-function regression:RetrievePrincipalAccessis a bound OData function on the principal entity (system user or team), not a standalone function. The previous implementation called it as an unbound function, causing runtime failures. Fixed:executeBoundFunction(principalEntitySet, principalId, "RetrievePrincipalAccess", { Target }). All 10 access-check tests now pass. - BUG-A —
dataverse_merge_recordsmissing@odata.type: TheupdateContentpayload was missing the mandatory@odata.typeannotation (application/json;type=entry), causing the Merge action to reject non-empty update bodies.@odata.typeis now injected automatically before the OData POST. - BUG-024 —
dataverse_create_relationshiplookup prefix: The auto-generated lookup column name (referencingEntity + "id") incorrectly reused the referencing entity schema name without deriving its publisher prefix. The prefix is now extracted directly fromreferencingEntity(first segment before_). Example:account → new_testcr01now correctly producesnew_accountid. - BUG-B —
dataverse_merge_recordscustom entity error: Merging custom entities returned an opaque OData stack trace. The tool now catches the unsupported-entity error and returns a clear, actionable message explaining thatMergeis restricted to OOB entity types (Account, Contact, Lead, Incident).
dataverse_create_table— Create a new custom entity (table) in Dataverse by POSTing to/EntityDefinitions. Accepts schema name, display names, ownership type (UserOwned/OrganizationOwned), notes, activities, audit flags, and primary name column config. Returns the newmetadataId.dataverse_create_relationship— Create a One-to-Many or Many-to-Many relationship via/RelationshipDefinitions. Supports cascade delete configuration (1:N) and custom intersect entity names (N:N). Returns the newmetadataId.dataverse_check_record_access— Check which access rights a user or team holds on a specific record using theRetrievePrincipalAccessOData function. Returns parsed rights list (ReadAccess,WriteAccess, etc.).dataverse_grant_access— Grant record-level sharing to a user or team via theGrantAccessDataverse action. Supports all standard AccessMask rights.dataverse_revoke_access— Revoke all shared access from a user or team via theRevokeAccessDataverse action.dataverse_merge_records— Merge two records of the same type via theMergeDataverse action. The subordinate is deactivated and its data carried over to the target. Requiresconfirm: trueto prevent accidental execution.
src/tools/schema.tools.ts— New module withschemaToolsarray andhandleSchemaTooldispatcher.src/tools/record-access.tools.ts— New module withrecordAccessToolsarray andhandleRecordAccessTooldispatcher.src/dataverse/dataverse-client.metadata.ts— AddedcreateEntityDefinition(body)method (POST/EntityDefinitions, extracts GUID fromOData-EntityIdresponse header).src/tools/router.tools.ts— Added tags and descriptions for all 6 new tools.tests/unit/schema-tools.test.tsandtests/unit/record-access-tools.test.ts— 25 new tests; total test suite: 691.
- 79 tools (was 73) · 4 MCP resources · 5 MCP prompts · 27 categories
- MCP Prompts — 5 pre-built workflow prompts exposed via the MCP
prompts/list+prompts/getprotocol endpoints. Clients that support MCP Prompts (Claude, Copilot) can invoke them as slash commands or one-click workflow starters:analyze-org-health— Complete org health check: identity, table inventory, security roles, workflow health, plugin steps, audit logging.data-quality-check— Data quality analysis for a table (required arg:tableName, optional:sampleSize): null rates, duplicates, stale records, referential integrity.schema-review— Expert schema review for a table (required arg:tableName): columns, naming conventions, relationships, alternate keys, views.security-audit— Security model audit: users, roles, over-privileged accounts, teams, business unit hierarchy.analyze-workflow— Workflow/flow analysis (optional args:workflowName,statusFilter): ownership, execution traces, plugin overlap, modernisation candidates.
prompts: {}capability —server.tsnow advertises prompt support in the MCP server capabilities block.
src/prompts/prompt-provider.ts— Prompt catalogue (PROMPTSarray) +listPrompts()/getPrompt(name, args)public API. Each builder function renders the step-by-step prompt text with the exact tool names the AI should call.tests/unit/prompt-provider.test.ts— 28 unit tests covering: prompt count, argument definitions, error handling (unknown name, missing required arg), tool name references per prompt, and argument interpolation.
- Client Credentials auth — App Registration-based service identity for unattended scenarios: CI/CD pipelines, Docker, Azure services. Configured via
authMethod: "client-credentials"plustenantId,clientId, andclientSecret(orAZURE_CLIENT_SECRETenv var — recommended over config file). - Managed Identity auth — zero-secret outbound auth for Azure-hosted deployments (App Service, Container Apps, Azure VMs). Supports both system-assigned and user-assigned identities (
managedIdentityClientId). Configured viaauthMethod: "managed-identity". - Entra JWT inbound validation — when the server runs in HTTP transport mode, bearer tokens are validated against Entra ID JWKS. Implements RFC 9728
/.well-known/oauth-protected-resourcefor automatic MCP client discovery. Validation covers RS256 signature, issuer (login.microsoftonline.com/{tenantId}/v2.0), audience (bare GUID), required scope,exp/nbf, and 30-second clock skew. JWKS is cached with automatic rotation onkidmiss. authMethodconfig option — selects the auth provider:"device-code"(default, unchanged behavior),"client-credentials", or"managed-identity". Also available as theAUTH_METHODenvironment variable.- New environment variables —
AUTH_METHOD,AZURE_TENANT_ID,AZURE_CLIENT_ID,AZURE_CLIENT_SECRET,AZURE_MANAGED_IDENTITY_CLIENT_IDmap to their config equivalents. Documented inconfig.example.jsonc.
- New
/authenticationsection with one page per method: Device Code, Client Credentials, Managed Identity. Includes Azure platform setup steps, Dataverse App User registration, environment variable reference, and a sequence diagram for the hosted flow.
src/auth/auth-provider.interface.ts—AuthProvidercontract; all providers implementgetToken(resource): Promise<string>.src/auth/auth-provider.factory.ts— ReadsauthMethodfrom config/env and instantiates the correct provider. Adding a new method requires no changes outside the factory.src/auth/client-credentials-auth-provider.ts— MSAL confidential client flow (client_credentials grant) with in-memory token cache.src/auth/managed-identity-auth-provider.ts— Azure IMDS token endpoint (169.254.169.254); falls back toDefaultAzureCredential-style resolution for user-assigned identities.src/auth/entra-jwt-validator.ts— HTTP middleware; validates inbound Bearer tokens for the HTTP server.src/auth/crypto-utils.ts— JWKS fetch + caching utilities extracted into a shared module.
- BUG-035 —
dataverse_add_role_privilegeswrong action name: The OData bound action for adding privileges to a role isAddPrivilegesRole(no "To"). A spurious "To" introduced in a previous fix session caused0x80060888 Resource not founderrors. Fixed inrole-privileges.tools.ts; action name is now driven by theCRM_BOUND_ACTIONSconstants file to prevent recurrence. - BUG-036 —
dataverse_replace_role_privilegeswrong action name: Same root cause —ReplacePrivilegesRole(no "To") is the correct Dataverse OData action name. Fixed identically. scripts/check-file-size.jsfalse positive: The line counter used.split('\n').lengthwhich counted a trailing newline as a phantom extra line, causingdataverse-client.ts(450 real lines) to fail the 450-line limit check. Fixed with.trimEnd()before splitting.
src/dataverse/crm-action-names.ts— New constants file (CRM_BOUND_ACTIONS,CRM_FUNCTIONS) as single source of truth for all Dataverse CRM action/function names. JSDoc links to the official Microsoft WebAPI reference. Prevents inline string typos for all future callers.tests/unit/role-privileges-tools.test.ts— 13 new contract tests including explicit.not.toContain("To")guards on action names. Total test suite: 588 tests.
dataverse_create_sitemap— New tool to create or update a model-driven app sitemap. Builds the XML structure fromareas(groups) containingsub_areas(entries), publishes it to Dataverse, and optionally attaches it to an existing App Module viaappModuleUniqueName. Zod-validated inputs; idempotent upsert via GET + conditional PATCH.
- BUG-038 —
dataverse_suggest_toolsphantom registry:TOOL_TAGSandTOOL_DESCRIPTIONSinrouter.tools.tspreviously held arbitrary string keys that were out-of-sync with the real tool registry (phantom entries, missing real tools). Both maps are now rebuilt against the live 73-tool registry — every key is a real callable tool name.suggest_toolsandlist_tool_tagsnow return accurate routing guidance. dataverse_get_role_privileges— documentation enriched: Description now documents the Dataverse OData quirk where the entity set for role privileges is namedroleprivilegess(double‑s). A FetchXML join pattern usingroleprivileges↔privilegeis included as a reliable alternative when the OData endpoint returns empty results.
dataverse_list_solutions— Removed. Listing solutions has low AI-agent utility and is adequately covered by the Dataverse UI or PAC CLI for human operators.dataverse_solution_components— Removed. Superseded; component management is out of scope for AI-agent automation at this stage.dataverse_add_solution_component— Removed. ALM operations require human review and are better handled via dedicated ALM tools (PAC CLI, pipelines).dataverse_remove_solution_component— Removed. Same rationale as above.
- BUG-033 —
dataverse_search:querytypeinvalid as top-level parameter: ThesearchType: "full"option was incorrectly sent asbody.querytype = "full"(top-level). Per the Search API v2.0 spec,querytypebelongs inside the serializedoptionsstring.searchType: "full"now maps tobody.options = '{"querytype":"lucene"}'.searchType: "simple"(default) produces nooptionsfield. - BUG-034 —
dataverse_search:selectinvalid as top-level parameter: Theselectparameter was sent as a top-level field in the request body, which is not recognized by the v2.0 API. Selected columns must be per entity (selectColumns).selectis now embedded inside each object of theentitiesarray asselectColumns. - BUG-035 —
dataverse_search:entitiesmust be a JSON-serialized string: Theentitiesarray was sent as a native JSON array. The Search API v2.0 requiresentitiesto be a JSON-serialized string of entity objects ("[{\"name\":\"account\",...}]"), not a native array. Parameter is now serialized viaJSON.stringify(entityObjects). - BONUS —
facetsandorderbyserialized as JSON strings: Per the v2.0 API spec (both parameters typed asstring),facetsandorderbyare now passed asJSON.stringify(...)of their arrays, not as native arrays.
- BUG-029 —
isErrorabsent fromcontent[0].textbody: All tool errors returnedresult.isError = trueat the MCP envelope level, but the innercontent[0].textJSON body did not includeisError. AI agents that parse the text body would not detect the error and could try to use the result as valid data.output.utils.tsnow consistently setsisError: trueinside the serialized text body whenevererrorCategoryis present. - BUG-032 —
dataverse_search:returntotalrecordcountinvalid in v2.0 payload: Thereturntotalrecordcountparameter from the v1.0 API was still being sent in the v2.0 request body. The correct v2.0 parameter iscount(boolean). Removedreturntotalrecordcount; the v2.0 API already returns counts viaQueryContext.@Search.totalCountin the response.
- BUG-025 —
dataverse_update_entity: suggestions included non-modifiable attributes: The suggestion list returned after a preflight failure or as guidance could include read-only system attributes. The suggestion generator now filters to modifiable attributes only. - BUG-028 —
dataverse_delete_attribute: raw exception on protected columns: Deleting OOB (out-of-box) attributes or columns with active dependencies raised an unhandled exception. The tool now catches0x80048405(managed attribute) andHAS_DEPENDENCIEScodes and returns a structured error witherrorCategory: "SCHEMA_MISMATCH"and an actionable message. - BUG-030 —
dataverse_update_entity: missing preflight check: Large metadata PATCH calls could fail mid-way if environment prerequisites were not met. AhasSysAdminpreflight check is now performed before executing any write. - BUG-031 —
dataverse_search:searchModeinvalid as top-level parameter in v2.0: ThesearchModeparameter (valid in v1.0) is not a recognized top-level parameter in the v2.0 API. Removed from the request body to eliminate0x80048d19deserialization errors.
- Uniform camelCase parameter names in
dataverse_create_annotation:notetext→noteText,filename→fileName,mimetype→mimeType,documentbody→documentBody. Dataverse wire field names in the POST body are unchanged. errorCategoryin all structured error responses:formatPrerequisiteErrornow automatically infers and includeserrorCategory(ENV_LIMITATIONforfeature_disabled,PERMISSIONSforpermission_required,SCHEMA_MISMATCHforschema_missing).dataverse_delete_attribute(error codes0x80048405,HAS_DEPENDENCIES) anddataverse_update_entity(0x80060888) also includeerrorCategory: "SCHEMA_MISMATCH"in their error payloads.dataverse_searchexpanded error detection: The Relevance Search disabled check now uses a comprehensive regex covering HTTP 403/404/400,0x80048D0B(official Dataverse error code for disabled search),search disabled,search not configured, andsearch unavailablemessages — up from 2 patterns to 7.- Search API upgraded to v2.0: The Dataverse search endpoint is now
/api/search/v2.0/query(wasv1.0, which is legacy/deprecated).
- A1 — Customization lock retry:
requestWithRetrynow detects HTTP 400 responses with Dataverse error code0x80071151(import-job lock) and automatically retries up to 5 times with exponential backoff (5 s → 10 s → 20 s → 40 s → 80 s). Prevents all write tools from failing during concurrent deployments. - A2 — Timeout masking success on
dataverse_create_lookup_attribute: A network timeout on the relationship creation POST no longer results in a confusing error. The tool now verifies viagetTableMetadatawhether the attribute was actually created; if found, it returns a success response with a warning and skips publish (the operation was committed server-side). - A3 —
dataverse_get_relationshipsmissing custom lookups on large tables: The three relationship navigation properties (OneToManyRelationships,ManyToOneRelationships,ManyToManyRelationships) now follow@odata.nextLinkpagination. Previously only the first page was fetched; on tables with 100+ relationships (e.g.lead) custom lookup columns from page 2 were silently dropped. - A4 —
dataverse_get_attribute_option_setunsupported on MultiSelectPicklist: The metadata type list now includesMultiSelectPicklistAttributeMetadata, allowing option retrieval for multi-select columns (previously returned a "not a valid option-set attribute" error).
- Attribute Management — 4 new tools:
dataverse_create_attribute— create columns for String, Memo, Integer, Decimal, Money, DateTime, Boolean, Picklist, MultiSelectPicklist, AutoNumber, Image types with full type-specific parameters (maxLength, precision, picklistOptions, autoNumberFormat, etc.)dataverse_update_attribute— update column properties (display name, description, requirement level, searchability)dataverse_delete_attribute— remove custom columns with aconfirm: trueguardrail (⚠️ permanent — deletes all data in the column)dataverse_create_lookup_attribute— create a lookup (N:1) column that defines a 1:N relationship between two tables
- HTTP Transport hardening:
- Bearer token auth with HMAC-SHA256 timing-safe verification (eliminates length timing oracle)
WWW-Authenticate: Bearer realm="MCP Dataverse"header auto-sent on 401- Configurable CORS via
MCP_HTTP_CORS_ORIGINenv var - GET/DELETE requests without a
sessionIdnow return 400 instead of silently failing enableJsonResponsetoggle viaMCP_HTTP_JSON_RESPONSEenv var for non-streaming clients
- Schema:
confirmparameter usesconst: trueon all destructive tools (uniform guardrail) - Preflight:
hasSysAdminenvironment flag now handled incheckPrerequisitesbefore tool dispatch - Quality:
detect_duplicatescachesEntitySetNameto eliminate redundant metadata calls per invocation - Code:
attribute.tools.tssplit intoattribute.definitions.ts(JSON schemas, 253 lines) +attribute.tools.ts(handlers, 202 lines) — both under the 400-line file limit config.example.jsonrenamed toconfig.example.jsonc(supports comments)
MultiSelectPicklistattribute type now correctly usesOptionSetType: "MultiSelect"(not"Picklist")- Batch executor: request body boundary now uses CRLF (
\r\n) per RFC 2046 spec dataverse_search: URL now constructed fromenvironmentUrlconfig (eliminates relative../../path hack)dataverse_execute_function: aliased typed parameters now correctly passed (not coerced to strings)dataverse_execute_bound_function: namespace prefix correctly applied when calling bound functions- Metadata write operations:
MSCRM.MergeLabels: trueheader added on all PUT/PATCH calls to preserve multi-language label translations createRelationship: readsOData-EntityIdresponse header (metadata POST returns 204 No Content with no body)buildLookupRelationshipBody: SchemaName formula no longer generates double publisher prefix; 100-char length guard addedsuggest_tools: no longer surfaces non-callable internal tools to the AI agent
- README cleaned up — removed Battle-Tested section, removed Smithery/MCP Registry links (not yet listed), simplified troubleshooting
- Roadmap v0.4 now includes Azure AD app registration and Managed Identity auth methods
- Roadmap simplified — removed MCP Resources milestone (tools already cover solutions, views, option sets), promoted Prompts to v0.5, moved OBO to ideas backlog
- Docs site switched to light theme with automatic dark mode (follows OS preference), modern accent colors
- Community page simplified — focus on testers, removed contributing/code sections
- GitHub Pages documentation site — full Jekyll + Just the Docs theme at
codeurali.github.io/mcp-dataverse - New doc pages: Getting Started, Roadmap, Community, Capabilities, 5 Use-Case guides
- Roadmap v0.4 expanded with schema consistency reinforcement (parameter naming,
errorCategory, preflight checks) - Upcoming auth methods documented — Azure AD (Client Credentials) and Managed Identity planned, architecture confirmed ready
- CAPABILITIES.md updated to 63 tools, version alignment, ETag fix documented
- README.md refreshed with new docs site URLs and community links
- CHANGELOG backfilled with all versions from v0.2.0 through v0.3.6
- BUG-021 final fix —
dataverse_update_entity:HasNotesnow sent as plainboolean(was incorrectly wrapped in{ Value: bool }— Dataverse expectsEdm.Boolean, notBooleanManagedProperty) dataverse_update_entity: graceful error handling for0x80060888— returns structured JSON with actionable suggestions (e.g. enable org-level audit) instead of raw exceptionIsAuditEnabled/ChangeTrackingEnabledboth return clear guidance when the operation is blocked at org level
- BUG-021 —
dataverse_update_entity:IsAuditEnabledwrapped asBooleanManagedProperty({ Value: bool });@odata.type: "#Microsoft.Dynamics.CRM.EntityMetadata"added to PATCH body - BUG-022 —
dataverse_assign_role_to_user: idempotence now functional — pre-check via$expand=systemuserroles_associationbeforeassociate; returns"already_assigned"if role already present - BUG-023 —
dataverse_remove_role_from_user: idempotence now functional — pre-check beforedisassociate; returns"not_assigned"if role absent
dataverse_list_connection_references— list Connection References in the environment (active/inactive count, connector details)
dataverse_list_roles— list Dataverse security roles with optionalnameContainsfilterdataverse_assign_role_to_user— assign a security role to a user (confirm: truerequired)dataverse_remove_role_from_user— remove a security role from a user (confirm: truerequired)
dataverse_update_entity— modify entity metadata flags (HasNotes, ChangeTracking, Audit) withconfirm: truerequired; auto-publishes by defaultdataverse_create_environment_variable— create an environment variable definition + value in Dataverse (confirm: truerequired)- Write guardrails —
checkWriteGuardrailson destructive tools surfaces[WARN] DESTRUCTIVE_OPindata.warnings[]
- BUG-018 —
dataverse_query:count=truenow shows total in summary ("N records returned from X (total in dataset: Y)") - BUG-019 —
dataverse_get:expandparameter now properly forwarded togetRecord(was silently ignored) - BUG-020 —
formattedValues: trueparameter added todataverse_query,dataverse_get,dataverse_execute_fetchxml,dataverse_retrieve_multiple_with_paging— transmitsPrefer: odata.include-annotationsheader; picklist fields return{ value, label }objects
- Query guardrails —
checkQueryGuardrailssurfaces warnings in response:[WARN] NO_SELECT,[INFO] NO_FILTER,[WARN] LARGE_RESULT_SET
dataverse_list_workflows— reimplemented: queries the real Dataverseworkflowsentity (Cloud Flows, Business Rules, Classic Workflows); supportscategory,nameContains,topparametersdataverse_get_workflow— reimplemented: retrieves a Dataverse Process by GUID with enrichedcategoryLabel/stateLabeldataverse_list_guides— new tool replacing oldlist_workflowsbehavior (lists 10 built-in MCP operational guides)dataverse_get_guide— new tool replacing oldget_workflowbehavior (returns step-by-step MCP guide by name)
- BUG-013 —
dataverse_get_attribute_option_set: summary showed "0 options" — handler usedOptions(PascalCase) but client returnsoptions(camelCase) - BUG-014 —
dataverse_list_dependencies: summary showed "1 dependencies" — fallback wrapped entire result object; now readscountdirectly - BUG-015 —
dataverse_retrieve_multiple_with_paging: summary showed "X records across 1 pages" — usedresult.pagesinstead ofresult.pageCount dataverse_publish_customizations: added 120 s timeout forPublishAllXml/PublishXml(previously timed out on large solutions)dataverse_list_users: removed.refine()— all parameters now truly optionaldataverse_batch_execute: summary now correctly shows"2/2 operations succeeded"instead of"0/N"dataverse_solution_components: summary count now accuratedataverse_create_annotation: actionable error message whenHasNotes=false(instructions to enable Notes in Power Apps maker portal)
- Enriched error messages across 6 tools:
get_environment_variable,set_workflow_state,execute_action,execute_bound_action,execute_bound_function,change_detection
dataverse_assign— assign a record to a different user or team owner viaownerid@odata.binddataverse_list_teams— list Dataverse teams with optional filter by team type (Owner / Access / Office / Security)dataverse_updatenow accepts optionaletagparameter for optimistic concurrency (If-Match: <etag>); when omitted, behaviour is unchanged (If-Match: *)
- MSAL token-cache file now written with
mode: 0o600(owner read/write only) on POSIX systems
- Removed
Dockerfileand.dockerignore— Docker adds unnecessary complexity for an stdio-based MCP server distributed vianpx; PAC CLI auth (recommended) does not work in containers - Removed Docker section from README
- Server startup crash when installed via
npx— incorrectpackage.jsonpath resolution fromdist/(was../../package.json, now../package.json)
- [HIGH]
entitySetNamenow validated against a safe identifier regex (/^[a-zA-Z_][a-zA-Z0-9_]*$/) across all tools — prevents path traversal within same origin (F-01) - [MEDIUM]
relationshipNameandrelatedEntitySetNamenow validated with the same safe identifier regex in relation tools (F-10) - Consolidated all inline OData single-quote escaping calls to use the centralized
esc()utility for consistency (F-06)
48 tools covering the full Microsoft Dataverse Web API surface:
| Category | Tools |
|---|---|
| Auth | dataverse_whoami |
| Metadata (7) | dataverse_list_tables, dataverse_get_table_metadata, dataverse_get_relationships, dataverse_list_global_option_sets, dataverse_get_option_set, dataverse_get_entity_key, dataverse_get_attribute_option_set |
| Query (3) | dataverse_query, dataverse_execute_fetchxml, dataverse_retrieve_multiple_with_paging |
| CRUD (5) | dataverse_get, dataverse_create, dataverse_update, dataverse_delete, dataverse_upsert |
| Relations (2) | dataverse_associate, dataverse_disassociate |
| Actions / Functions (6) | dataverse_execute_action, dataverse_execute_function, dataverse_execute_bound_action, dataverse_execute_bound_function, dataverse_retrieve_dependencies_for_delete, dataverse_list_dependencies |
| Batch (1) | dataverse_batch_execute |
| Change Tracking (1) | dataverse_change_detection |
| Solution (3) | dataverse_list_solutions, dataverse_solution_components, dataverse_publish_customizations |
| Impersonation (1) | dataverse_impersonate |
| Customization (3) | dataverse_list_custom_actions, dataverse_list_plugin_steps, dataverse_set_workflow_state |
| Environment (2) | dataverse_get_environment_variable, dataverse_set_environment_variable |
| Trace (2) | dataverse_get_plugin_trace_logs, dataverse_get_workflow_trace_logs |
| Search (1) | dataverse_search |
| Audit (1) | dataverse_get_audit_log |
| Quality (1) | dataverse_detect_duplicates |
| Annotations (2) | dataverse_get_annotations, dataverse_create_annotation |
| Users (2) | dataverse_list_users, dataverse_get_user_roles |
| Views (1) | dataverse_list_views |
| Files (2) | dataverse_upload_file_column, dataverse_download_file_column |
| Org (1) | dataverse_list_business_units |
- AES-256-GCM encrypted PAC CLI token cache
- SSRF protection on all outbound HTTP via URL allowlist
- UUID validation on all record ID parameters
- OData injection protection via
esc()utility - Path traversal protection on action/function name inputs
- Impersonation gated behind
callerIdHeader(fail-closed design)