@atoms-tech/atoms-mcp 0.38.0 → 0.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var e=require("fs/promises"),t=require("os"),r=require("path"),i=require("@supabase/supabase-js"),a=require("http"),n=require("crypto"),s=require("url"),o=require("zod"),d=require("@modelcontextprotocol/ext-apps/server"),l=require("@modelcontextprotocol/sdk/server/mcp.js"),c=require("module"),p=require("@modelcontextprotocol/sdk/server/stdio.js"),m="undefined"!=typeof document?document.currentScript:null;function u(e){return e&&e.__esModule?e:{default:e}}var _,f,h,g,y,w,v,b=u(e),j=u(r),S=u(n),k=Object.defineProperty,x=Object.getOwnPropertyNames,A=(e,t,r)=>function(){if(r)throw r[0];try{return e&&(t=(0,e[x(e)[0]])(e=0)),t}catch(e){throw r=[e],e}},E=(e,t)=>{for(var r in t)k(e,r,{get:t[r],enumerable:!0})};function I(e){return v.get(e)}var R=A({"src/core/tool-catalog.ts"(){_=new Set(["atoms_status","atoms_list_projects"]),f=new Set(["atoms_create_item","atoms_update_item","atoms_delete_item","atoms_restore_item","atoms_link_items","atoms_bulk_import","atoms_record_test_result","atoms_record_test_results","atoms_create_variable","atoms_update_variable","atoms_delete_variable","atoms_create_domain","atoms_update_domain","atoms_delete_domain","atoms_create_level","atoms_delete_level","atoms_reorder_levels","atoms_create_field","atoms_delete_field","atoms_reorder_fields","atoms_create_baseline","atoms_lock_baseline","atoms_restore_baseline","atoms_create_analysis","atoms_fill_row","atoms_link_mitigation","atoms_propose_rows","atoms_suggest_field","atoms_approve_row"]),h=new Set(["atoms_delete_item","atoms_delete_variable","atoms_delete_domain","atoms_delete_level","atoms_delete_field","atoms_restore_baseline"]),g=new Set(["atoms_bulk_import"]),y={items:["atoms_list_items","atoms_get_item","atoms_search","atoms_semantic_search","atoms_browse","atoms_get_history","atoms_create_item","atoms_update_item","atoms_delete_item","atoms_restore_item","atoms_bulk_import"],traceability:["atoms_trace","atoms_link_items","atoms_export_mermaid","atoms_impact_analysis"],coverage:["atoms_get_coverage","atoms_verification_status","atoms_record_test_result","atoms_record_test_results","atoms_project_summary","atoms_list_assets"],variables:["atoms_list_variables","atoms_get_variable","atoms_create_variable","atoms_update_variable","atoms_delete_variable"],domains:["atoms_list_domains","atoms_create_domain","atoms_update_domain","atoms_delete_domain","atoms_list_levels","atoms_create_level","atoms_delete_level","atoms_reorder_levels","atoms_list_fields","atoms_create_field","atoms_delete_field","atoms_reorder_fields"],baselines:["atoms_list_baselines","atoms_create_baseline","atoms_lock_baseline","atoms_diff_baselines","atoms_restore_baseline"],analysis:["atoms_create_analysis","atoms_fill_row","atoms_get_analysis","atoms_link_mitigation","atoms_get_gaps","atoms_propose_rows","atoms_suggest_field","atoms_approve_row"]},w=Object.keys(y),v=new Map;for(const[e,t]of Object.entries(y))for(const r of t)v.set(r,e)}});function O(e,t){if(_.has(t))return{allowed:!0};if(e.lockdown&&!new Set(["atoms_list_items","atoms_get_item","atoms_search","atoms_browse"]).has(t))return{allowed:!1,reason:"lockdown"};if(e.forbidden_tools.includes(t))return{allowed:!1,reason:"forbidden_tool"};if(e.read_only&&f.has(t))return{allowed:!1,reason:"read_only"};if(e.block_destructive&&h.has(t))return{allowed:!1,reason:"block_destructive"};if(e.block_bulk_import&&g.has(t))return{allowed:!1,reason:"block_bulk_import"};const r=I(t);return void 0===r||e.allowed_toolsets.includes(r)?{allowed:!0}:{allowed:!1,reason:"toolset_disabled"}}function q(e,t){return O(e,t).allowed}function z(e){switch(e){case"lockdown":return"MCP is in lockdown mode for your role — only read-only browse is available.";case"read_only":return"Your org policy puts MCP in read-only mode. Mutating tools are not exposed.";case"block_destructive":return"Your org policy blocks destructive operations (delete tools).";case"block_bulk_import":return"Your org policy blocks bulk import.";case"forbidden_tool":return"This tool is on your org's forbidden list.";case"toolset_disabled":return"This tool's toolset is not enabled in your org policy."}}var M,H,D,$,P=A({"src/core/policy.ts"(){R()}}),T=A({"src/core/types.ts"(){M={read_only:!1,lockdown:!1,block_destructive:!1,block_bulk_import:!1,cross_project_browse:!0,allowed_toolsets:["items","traceability","coverage","variables","domains","baselines","analysis"],forbidden_tools:[],project_scope:null,require_confirmation_for:[]},H={read_only:!0,lockdown:!0,block_destructive:!0,block_bulk_import:!0,cross_project_browse:!1,allowed_toolsets:[],forbidden_tools:[],project_scope:[],require_confirmation_for:[]}}});function U(){return D}var C=A({"src/middleware/session-policy.ts"(){T(),D=M,$=null}});function N(){const e=process.env.ATOMS_MCP_TOOLSETS?.trim();if(!e)return null;const t=e.split(",").map(e=>e.trim()).filter(Boolean);if(0===t.length)return null;const r=new Set,i=[];for(const e of t)w.includes(e)?r.add(e):i.push(e);return{allowed:r,unknown:i}}function L(){const e=process.env.ATOMS_MCP_READ_ONLY;return"1"===e||"true"===e?.toLowerCase()}function F(e){if(L()&&f.has(e))return!1;if(_.has(e))return!0;const t=N();if(null===t)return q(U(),e);const r=I(e);return!!r&&!!t.allowed.has(r)&&q(U(),e)}var W,V,B=A({"src/middleware/tool-registry.ts"(){R(),P(),C()}}),J={};async function Q(){try{const t=await e.readFile(V,"utf-8"),r=JSON.parse(t);return r.access_token&&r.refresh_token?r:null}catch{return null}}async function G(t){await e.mkdir(W,{recursive:!0,mode:448}),await e.writeFile(V,JSON.stringify(t,null,2),{mode:384})}async function K(){const e=await Q();return!!e&&e.expires_at>Date.now()+6e4}function Y(e){return e.expires_at>Date.now()+6e4}async function Z(){try{await e.unlink(V)}catch{}}function X(){return V}E(J,{clearCredentials:()=>Z,getCredentialsPath:()=>X,hasValidCredentials:()=>K,isTokenValid:()=>Y,readCredentials:()=>Q,writeCredentials:()=>G});var ee,te,re,ie,ae,ne,se,oe=A({"src/auth/token-store.ts"(){W=r.join(t.homedir(),".atoms"),V=r.join(W,"credentials.json")}}),de={};async function le(){try{await e.mkdir(ee,{recursive:!0,mode:448});const t=await e.open(te,"wx");try{await t.writeFile(`${process.pid} ${Date.now()}`)}finally{await t.close()}return!0}catch(e){if("EEXIST"===e.code)return!1;throw e}}async function ce(){try{await e.unlink(te)}catch{}}async function pe(){try{const t=await e.stat(te);return Date.now()-t.mtimeMs>re}catch{return!1}}async function me(e,t,r=se){const i=r.now()+ie;for(;;){if(await r.tryAcquire())try{try{return await e()}catch{}return await t()}finally{await r.release()}try{return await e()}catch{}if(await r.isStale())await r.release();else{if(r.now()>i)return await t();await r.wait(ae)}}}E(de,{runExclusiveAuth:()=>me});var ue,_e,fe,he=A({"src/auth/lock.ts"(){ee=r.join(t.homedir(),".atoms"),te=r.join(ee,".auth.lock"),re=7e4,ie=12e4,ae=300,ne=e=>new Promise(t=>setTimeout(t,e)),se={now:Date.now,wait:ne,tryAcquire:le,release:ce,isStale:pe}}}),ge={};function ye(e){return e.trim().replace(/\/+$/,"")}E(ge,{ATOMS_APP_URL:()=>fe,ATOMS_SUPABASE_ANON_KEY:()=>_e,ATOMS_SUPABASE_URL:()=>ue,resolveUrl:()=>ye});var we=A({"src/config.ts"(){ue=ye(process.env.ATOMS_SUPABASE_URL??"https://gmebjyhomsbvhrxffzre.supabase.co"),_e=process.env.ATOMS_SUPABASE_ANON_KEY??"sb_publishable_b49MUAB8XCrQiF7x5b9lUA_w1aplSBH",fe=process.env.ATOMS_APP_URL??"https://atoms.tech"}}),ve={};async function be(){const e=process.env.ATOMS_ACCESS_TOKEN;if(e){const t=Se(e);return{access_token:e,refresh_token:"",user_id:t.sub,email:t.email??""}}const t=await Q();if(t&&t.expires_at>Date.now()+6e4){const e=Se(t.access_token);return{access_token:t.access_token,refresh_token:t.refresh_token,user_id:e.sub,email:e.email??t.user_email??""}}return null}async function je(){const e=await be();if(e)return e;const t=await Q();if(!t)throw new Error("Not authenticated. Run 'npx @atoms-tech/atoms-mcp login' to connect your ATOMS account.");return me(async()=>{const e=await be();if(e)return e;throw new Error("no fresh token yet")},()=>async function(e){process.stderr.write("[atoms-mcp] Refreshing access token...\n");const t=i.createClient(ue,_e);let r=await t.auth.refreshSession({refresh_token:e.refresh_token});if(r.error||!r.data.session){const i=await Q();if(i&&i.expires_at>Date.now()+6e4){const e=Se(i.access_token);return{access_token:i.access_token,refresh_token:i.refresh_token,user_id:e.sub,email:e.email??i.user_email??""}}i&&i.refresh_token&&i.refresh_token!==e.refresh_token&&(process.stderr.write("[atoms-mcp] Refresh token was rotated by another session; retrying with the latest...\n"),r=await t.auth.refreshSession({refresh_token:i.refresh_token}))}if(r.error||!r.data.session){const e=r.error?[r.error.status,r.error.message].filter(Boolean).join(" ").trim():"no session returned";throw new Error(`Token refresh failed (${e}). Your saved ATOMS session is no longer valid — re-run 'npx @atoms-tech/atoms-mcp login'.`)}const a=r.data.session;return await G({access_token:a.access_token,refresh_token:a.refresh_token,expires_at:Date.now()+1e3*(a.expires_in??3600),user_email:a.user?.email}),{access_token:a.access_token,refresh_token:a.refresh_token,user_id:a.user?.id??"",email:a.user?.email??""}}(t))}function Se(e){const t=e.split(".");if(3!==t.length)throw new Error("Invalid JWT format");const r=t[1].replace(/-/g,"+").replace(/_/g,"/").padEnd(t[1].length+(4-t[1].length%4)%4,"="),i=JSON.parse(Buffer.from(r,"base64").toString("utf-8"));if(!i.sub)throw new Error("JWT missing 'sub' claim");return i}E(ve,{getValidToken:()=>je,readValidToken:()=>be});var ke,xe,Ae,Ee,Ie,Re=A({"src/auth/refresh.ts"(){oe(),he(),we()}}),Oe={};async function qe(){if(ke)try{const e=await Q();e&&e.expires_at>Ie&&(process.stderr.write("[atoms-mcp] Credentials file refreshed elsewhere — rebuilding client.\n"),ze())}catch{}if(ke&&Ee>Date.now()+6e4)return ke;ke&&process.stderr.write("[atoms-mcp] Token expiring, refreshing client...\n");const{access_token:e,user_id:t,email:r}=await je(),a=e.split(".");if(3===a.length)try{const e=JSON.parse(Buffer.from(a[1].replace(/-/g,"+").replace(/_/g,"/"),"base64").toString());Ee=1e3*(e.exp??0)}catch{Ee=Date.now()+36e5}try{const e=await Q();Ie=e?.expires_at??0}catch{Ie=0}const n=i.createClient(ue,_e,{global:{headers:{Authorization:`Bearer ${e}`}}});return xe=t,Ae=r,ke=n}function ze(){ke=null,xe=null,Ae=null,Ee=0,Ie=0}function Me(e){let t="";if(e instanceof Error)t=e.message;else if(e&&"object"==typeof e){const r=e;t="string"==typeof r.message?r.message:"string"==typeof r.code?r.code:String(e)}else null!=e&&(t=String(e));return/jwt\s*expired|invalid.?jwt|token.*(?:has\s+)?expired|jwt.*invalid|pgrst301/i.test(t)}async function He(e){try{return await e()}catch(t){if(!Me(t))throw t;return process.stderr.write("[atoms-mcp] Data tier rejected JWT — invalidating client and retrying once.\n"),ze(),await e()}}function De(){if(!xe)throw new Error("Not authenticated. Call getClient() first.");return xe}function $e(){return Ae??""}async function Pe(e,t){const r=De(),{data:i,error:a}=await e.from("projects").select("org_id").eq("id",t).maybeSingle();if(a||!i)throw new Error(`Project '${t}' not found`);const{data:n,error:s}=await e.from("org_members").select("role").eq("user_id",r).eq("org_id",i.org_id).maybeSingle();if(s||!n){const{data:t}=await e.rpc("is_platform_admin");if(!0===t)return"admin";throw new Error("Not a member of this project's organization")}if("viewer"===n.role)throw new Error("VIEWER_ROLE");return n.role}async function Te(e,t){const r=De(),{data:i,error:a}=await e.from("projects").select("org_id, created_by").eq("id",t).maybeSingle();if(a||!i)throw new Error(`Project '${t}' not found`);const{data:n,error:s}=await e.from("org_members").select("role").eq("user_id",r).eq("org_id",i.org_id).maybeSingle();if(s||!n)throw new Error("Not a member of this project's organization");if("admin"===n.role)return"admin";if(i.created_by===r)return"owner";throw new Error("PROJECT_FIELD_ADMIN_OR_OWNER_REQUIRED")}E(Oe,{getClient:()=>qe,getUserEmail:()=>$e,getUserId:()=>De,invalidateClient:()=>ze,isJwtExpiredError:()=>Me,requireProjectFieldDefinitionAccess:()=>Te,requireWriteAccess:()=>Pe,withJwtRetry:()=>He});var Ue,Ce,Ne=A({"src/db/client.ts"(){Re(),oe(),we(),ke=null,xe=null,Ae=null,Ee=0,Ie=0}}),Le={};async function Fe(){const e=n.randomBytes(32).toString("base64url"),t=n.createHash("sha256").update(e).digest("base64url"),r=`http://localhost:${Ue}${Ce}`,i=new s.URL(`${fe}/auth/mcp-consent`);return i.searchParams.set("redirect_uri",r),i.searchParams.set("code_challenge",t),i.searchParams.set("code_challenge_method","S256"),new Promise((t,r)=>{let n;function o(){clearTimeout(n)}const d=a.createServer(async(i,a)=>{const n=new s.URL(i.url??"/",`http://localhost:${Ue}`);if(n.pathname!==Ce)return a.writeHead(404),void a.end("Not found");try{if("access_denied"===n.searchParams.get("error"))return a.writeHead(200,{"Content-Type":"text/html"}),a.end(Ve("Authorization was cancelled.")),o(),d.close(),void r(new Error("Authorization cancelled by user."));const i=n.searchParams.get("code");if(i){const n=await fetch(`${ue}/auth/v1/token?grant_type=pkce`,{method:"POST",headers:{"Content-Type":"application/json",apikey:_e},body:JSON.stringify({auth_code:i,code_verifier:e})}),s=await n.json();if(!n.ok||!s.access_token){const e=s.error_description??s.msg??"Unknown error";return a.writeHead(200,{"Content-Type":"text/html"}),a.end(Ve(`Code exchange failed: ${e}`)),o(),d.close(),void r(new Error(`Code exchange failed: ${e}`))}let l="authenticated";try{l=JSON.parse(Buffer.from(s.access_token.split(".")[1],"base64").toString()).email??l}catch{}const{createClient:c}=await import("@supabase/supabase-js"),p=c(ue,_e,{global:{headers:{Authorization:`Bearer ${s.access_token}`}}}),{data:m,error:u}=await p.from("org_members").select("org_id").limit(1);return u||!m||0===m.length?(a.writeHead(200,{"Content-Type":"text/html"}),a.end(Ve('No ATOMS account found for this Google account.<br><br>Please sign up at <a href="https://atoms.tech">atoms.tech</a> first, then run this login command again.')),o(),d.close(),void r(new Error("No ATOMS account found. Sign up at atoms.tech first."))):s.refresh_token?(await G({access_token:s.access_token,refresh_token:s.refresh_token,expires_at:Date.now()+1e3*(s.expires_in??3600),user_email:l}),a.writeHead(200,{"Content-Type":"text/html"}),a.end(We(l)),o(),d.close(),void t({email:l})):(a.writeHead(200,{"Content-Type":"text/html"}),a.end(Ve("Sign-in succeeded but no refresh token was issued, so the session would expire within the hour. This usually points to a Supabase Auth setting (refresh-token rotation or session policy). Please contact ATOMS support.")),o(),d.close(),void r(new Error("No refresh token issued by Supabase — cannot persist a durable session.")))}const s=n.searchParams.get("access_token"),l=n.searchParams.get("refresh_token"),c=parseInt(n.searchParams.get("expires_in")??"3600",10);s&&l?(await G({access_token:s,refresh_token:l,expires_at:Date.now()+1e3*c}),a.writeHead(200,{"Content-Type":"text/html"}),a.end(We("authenticated")),o(),d.close(),t({email:"authenticated"})):(a.writeHead(200,{"Content-Type":"text/html"}),a.end("<!DOCTYPE html>\n<html>\n<head><title>ATOMS MCP Login</title></head>\n<body>\n <h1>Completing login...</h1>\n <script>\n const hash = window.location.hash.substring(1);\n const params = new URLSearchParams(hash);\n const data = {\n access_token: params.get('access_token'),\n refresh_token: params.get('refresh_token'),\n expires_in: params.get('expires_in'),\n user_email: ''\n };\n if (data.access_token) {\n // Decode JWT to get email\n try {\n const payload = JSON.parse(atob(data.access_token.split('.')[1]));\n data.user_email = payload.email || '';\n } catch(e) {}\n fetch('/token', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data)\n }).then(() => {\n document.body.innerHTML = '<h1>Login successful! You can close this tab.</h1>';\n }).catch(() => {\n document.body.innerHTML = '<h1>Login failed. Please try again.</h1>';\n });\n } else {\n document.body.innerHTML = '<h1>Login failed — no tokens received.</h1>';\n }\n <\/script>\n</body>\n</html>"))}catch(e){a.writeHead(500),a.end("Authentication failed"),o(),d.close(),r(e)}});d.on("request",async(e,i)=>{if("POST"===e.method&&"/token"===e.url){let a="";e.on("data",e=>{a+=e.toString()}),e.on("end",async()=>{try{const e=JSON.parse(a);await G({access_token:e.access_token,refresh_token:e.refresh_token,expires_at:Date.now()+1e3*(e.expires_in??3600),user_email:e.user_email}),i.writeHead(200,{"Content-Type":"application/json"}),i.end(JSON.stringify({ok:!0})),o(),d.close(),t({email:e.user_email??"authenticated"})}catch(e){i.writeHead(500),i.end("Failed to store credentials"),o(),d.close(),r(e)}})}}),d.listen(Ue,"127.0.0.1",async()=>{process.stderr.write("\nOpening browser for ATOMS login...\n"),process.stderr.write(`If the browser doesn't open, visit:\n${i.toString()}\n\n`);try{const{default:e}=await import("open");await e(i.toString())}catch{process.stderr.write("Could not open browser automatically. Please open the URL above.\n")}}),n=setTimeout(()=>{d.close(),r(new Error("Login timed out after 60 seconds. Run 'npx @atoms-tech/atoms-mcp login' to try again."))},6e4)})}function We(e){return`<!DOCTYPE html>\n<html>\n<head>\n <title>ATOMS MCP — Authorized</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;\n background: #0a0a0a;\n color: #fafafa;\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n }\n .card {\n background: #171717;\n border: 1px solid #262626;\n border-radius: 16px;\n padding: 48px;\n max-width: 480px;\n width: 100%;\n text-align: center;\n }\n .icon {\n width: 56px;\n height: 56px;\n background: #052e16;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 24px;\n }\n .icon svg { width: 28px; height: 28px; }\n h1 {\n font-size: 22px;\n font-weight: 600;\n margin-bottom: 8px;\n color: #22c55e;\n }\n .email {\n font-size: 14px;\n color: #a3a3a3;\n margin-bottom: 32px;\n }\n .permissions {\n text-align: left;\n background: #0a0a0a;\n border: 1px solid #262626;\n border-radius: 12px;\n padding: 20px;\n margin-bottom: 24px;\n }\n .permissions h3 {\n font-size: 12px;\n font-weight: 600;\n color: #737373;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin-bottom: 12px;\n }\n .perm-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 6px 0;\n font-size: 14px;\n color: #d4d4d4;\n }\n .perm-item .dot {\n width: 6px;\n height: 6px;\n background: #22c55e;\n border-radius: 50%;\n flex-shrink: 0;\n }\n .closing {\n font-size: 13px;\n color: #525252;\n }\n .closing span { color: #a3a3a3; font-variant-numeric: tabular-nums; }\n </style>\n</head>\n<body>\n <div class="card">\n <div class="icon">\n <svg fill="none" viewBox="0 0 24 24" stroke="#22c55e" stroke-width="2.5">\n <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>\n </svg>\n </div>\n <h1>Authorization Granted</h1>\n <p class="email">${e}</p>\n\n <div class="permissions">\n <h3>ATOMS MCP can now</h3>\n <div class="perm-item"><span class="dot"></span> Read your projects and requirements</div>\n <div class="perm-item"><span class="dot"></span> Create and edit requirements and test cases</div>\n <div class="perm-item"><span class="dot"></span> Record test results (pass/fail)</div>\n <div class="perm-item"><span class="dot"></span> Manage traceability relationships</div>\n <div class="perm-item"><span class="dot"></span> Export coverage reports and diagrams</div>\n </div>\n\n <p class="closing">This tab will close in <span id="countdown">5</span>s</p>\n </div>\n\n <script>\n let seconds = 5;\n const el = document.getElementById('countdown');\n const timer = setInterval(() => {\n seconds--;\n el.textContent = seconds;\n if (seconds <= 0) {\n clearInterval(timer);\n window.close();\n // If window.close() is blocked (not opened by script), update message\n setTimeout(() => {\n document.querySelector('.closing').textContent = 'You can close this tab now.';\n }, 500);\n }\n }, 1000);\n <\/script>\n</body>\n</html>`}function Ve(e){return`<!DOCTYPE html>\n<html>\n<head>\n <title>ATOMS MCP — Failed</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;\n background: #0a0a0a;\n color: #fafafa;\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n }\n .card {\n background: #171717;\n border: 1px solid #262626;\n border-radius: 16px;\n padding: 48px;\n max-width: 480px;\n width: 100%;\n text-align: center;\n }\n .icon {\n width: 56px;\n height: 56px;\n background: #450a0a;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 24px;\n }\n .icon svg { width: 28px; height: 28px; }\n h1 { font-size: 22px; font-weight: 600; margin-bottom: 16px; color: #ef4444; }\n .reason { font-size: 14px; color: #a3a3a3; line-height: 1.6; }\n .reason a { color: #7c3aed; text-decoration: underline; }\n </style>\n</head>\n<body>\n <div class="card">\n <div class="icon">\n <svg fill="none" viewBox="0 0 24 24" stroke="#ef4444" stroke-width="2.5">\n <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>\n </svg>\n </div>\n <h1>Authorization Failed</h1>\n <p class="reason">${e}</p>\n </div>\n</body>\n</html>`}E(Le,{login:()=>Fe});var Be,Je,Qe,Ge,Ke=A({"src/auth/login.ts"(){oe(),we(),Ue=19275,Ce="/callback"}});function Ye(e,t=0){if(t>5)return"[depth-limit]";if("string"==typeof e){for(const t of Qe)if(t.test(e.trim()))return"[redacted]";return e.length>Ge?e.slice(0,Ge)+"…":e}if(Array.isArray(e))return e.slice(0,20).map(e=>Ye(e,t+1));if(null!==e&&"object"==typeof e){const r={};for(const[i,a]of Object.entries(e)){const e=i.toLowerCase();Be.has(e)?r[i]="[redacted]":Je.has(e)?r[i]="string"==typeof a?"[content]":Ye(a,t+1):r[i]=Ye(a,t+1)}return r}return e}function Ze(e){return Ye(e)}var Xe,et,tt,rt,it,at=A({"src/middleware/audit.ts"(){Be=new Set(["access_token","refresh_token","password","api_key","secret","token","authorization","credential","credentials","private_key"]),Je=new Set(["body","description","content","notes","summary","title","system_prompt","prompt"]),Qe=[/^[^\s@]+@[^\s@]+\.[^\s@]+$/,/^bearer\s+\S+$/i,/^(sk|pk|npm|gh|glpat|xoxb|xoxp|rk|SG\.)[-_][A-Za-z0-9_-]{16,}/,/^eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/],Ge=200}}),nt=A({"src/middleware/rate-limiter.ts"(){Xe=60,et=6e4}});function st(){const e=process.env.ATOMS_MCP_PROJECT_ID?.trim();return e?tt.test(e)?e:(rt||(process.stderr.write(`[atoms-mcp] WARNING: ATOMS_MCP_PROJECT_ID="${e.slice(0,50)}" is not a valid UUID — scope ignored.\n`),rt=!0),null):null}var ot,dt=A({"src/middleware/project-scope.ts"(){tt=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,rt=!1,it=class extends Error{expectedProjectId;receivedProjectId;constructor(e,t){super("Tool call rejected: project_id is outside the configured ATOMS_MCP_PROJECT_ID scope."),this.name="ProjectScopeError",this.expectedProjectId=e,this.receivedProjectId=t}}}});function lt(e){const t=function(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")}(e??"");return t?t.includes("cursor")?"cursor":t.includes("copilot")?"copilot":t.includes("claude")?ot:t:ot}var ct,pt,mt,ut,_t,ft,ht,gt,yt,wt,vt,bt,jt,St,kt,xt,At,Et,It,Rt,Ot,qt,zt,Mt,Ht,Dt,$t,Pt,Tt,Ut,Ct,Nt,Lt,Ft,Wt,Vt,Bt,Jt,Qt,Gt,Kt,Yt,Zt,Xt,er,tr,rr=A({"src/core/client-identity.ts"(){ot="claude_desktop"}}),ir=A({"src/core/timestamps.ts"(){ct=o.z.string().datetime({offset:!0})}});function ar(e,t,r){return"hara"===e?"iso14971"===t?"hazard_iso14971":"iso26262"===t?"hara_iso26262":"medical"===r?"hazard_iso14971":"hara_iso26262":"process"===t?"fmea_process":"software"===t?"fmea_software":"use"===t?"fmea_use":"design"===t?"dfmea_rpn":"medical"===r?"fmea_process":"dfmea_rpn"}function nr(e){return mt[e]}function sr(e){return ut.some(t=>t.id===e)}function or(e){const t=Array.isArray(e)?e.filter(e=>"string"==typeof e):[],r=t.length>=2?t[1].trim():"";return r?{level:r,source:"taxonomy"}:{level:ft,source:"fallback"}}function dr(e){const t=(e??"").trim().toLowerCase();return"sub-system"===t?"subsystem":t}function lr(e,t){const r=or(e),i=or(t);return dr(r.level)===dr(i.level)?null:`Warning: this changes the system level (the ladder's second rung, L1) from "${r.level}" to "${i.level}". HARA and risk-analysis gap classification (the no_parent / bridge gap) is judged against the system level, so gap results for this project's hazard analyses may change. Re-check atoms_get_gaps on affected analyses.`}function cr(e){return"hazard_iso14971"===e?Ot:[]}function pr(e){return String(e??"").split(/\r\n?|\n/).map(e=>e.trim()).filter(e=>""!==e)}function mr(e){const t=e??{};return{intended_use:String(t.intended_use??"").trim(),foreseeable_misuse:pr(t.foreseeable_misuse),safety_characteristics:pr(t.safety_characteristics)}}function ur(e){const t=e??{},r=String(t.overall_residual_risk??"").trim(),i=String(t.software_safety_class??"").trim();return{residual_risk:qt.includes(r)?r:"",residual_rationale:String(t.overall_residual_rationale??"").trim(),safety_class:zt.includes(i)?i:"",safety_rationale:String(t.software_safety_rationale??"").trim()}}function _r(e){return""!==mr(e).intended_use}function fr(e,t){const r=cr(e);if(0===r.length)return`An analysis parent of scheme ${e} carries no fillable field: ${t.join(", ")} cannot be written on it. Only the ISO 14971 risk analysis carries a scope and an overall residual risk.`;const i=r.filter(e=>!e.endsWith("_rationale"));return`Unknown field(s) for the analysis parent: ${t.join(", ")}. Allowed: ${i.join(", ")} (+ _rationale for the last two)`}function hr(e,t,r){return`Invalid ${e} value ${JSON.stringify(t)}. Allowed: ${r.join(", ")}`}function gr(e){return"risk_analysis"===e||"hazard_analysis"===e?["iso14971"]:pt[Pt[e]]}function yr(e){return Object.entries(Jt[e]).map(([e,t])=>({key:e,label:t}))}function wr(e){return Object.entries(function(e){return"hara_iso26262"===e?Lt:"dfmea_rpn"===e?Vt:"hazard_iso14971"===e?Wt:Bt}(e)).map(([e,t])=>({key:e,text:t}))}function vr(e){return"hazard_iso14971"===e?kt:null}function br(e,t){const r=vr(e);return r?r[t]??null:null}function jr(e){return(e??"").trim().toLowerCase()}function Sr(e,t){const r=jr(e);if(!r)return null;for(const e of t)if(jr(e)===r)return e;return null}function kr(e){const t=e?.severity||"",r=e?.exposure||"",i=e?.controllability||"";return t&&r&&i?"S0"===t||"E0"===r||"C0"===i?"QM":wt[`${t}-${r}-${i}`]||gt:gt}function xr(e){const t=parseInt(e?.severity||"",10),r=parseInt(e?.occurrence||"",10),i=parseInt(e?.detection||"",10);return Number.isNaN(t)||Number.isNaN(r)||Number.isNaN(i)?gt:String(t*r*i)}function Ar(e,t,r){const i=String(e??""),a=String(t??""),n=String(r??"");if(bt.indexOf(i)<0||bt.indexOf(a)<0||bt.indexOf(n)<0)return gt;const s=jt[`${a}-${n}`];return s&&St[`${s}-${i}`]||gt}function Er(e){return Ar(e?.severity,e?.p1,e?.p2)}function Ir(e){return Ar(e?.residual_s,e?.residual_p1,e?.residual_p2)}function Rr(e,t){return"hara_iso26262"===e?kr(t):"hazard_iso14971"===e?Er(t):"fmea_software"===e?function(e){return xr({...e??{},occurrence:xt})}(t):xr(t)}function Or(e,t,r){if(r)return!1;if("hara_iso26262"===e){const e=kr(t);return"C"===e||"D"===e}if("hazard_iso14971"===e)return"High"===Er(t);const i=parseInt(Rr(e,t),10);return!Number.isNaN(i)&&i>=200}function qr(e,t,r){if(!e?.approved||!e.approved_at)return!1;const i=Date.parse(e.approved_at);return!Number.isNaN(i)&&[...e.source_req?[e.source_req]:[],...t].some(e=>{const t=r(e),a=t?Date.parse(t):NaN;return!Number.isNaN(a)&&a>i})}function zr(e,t){const r=function(e){return"hara_iso26262"===e?"asil":"hazard_iso14971"===e?"risk":"rpn"}(e);return`Field${t.length>1?"s":""} ${t.join(", ")} cannot be written: ${r} is derived from the ratings and the approval keys are recorded by atoms_approve_row. Write the ratings instead and read ${r} back as computed_level.`}function Mr(e){return"fmea_software"===e?{field:"occurrence",value:xt}:null}function Hr(e){const t=Mr(e);return t?`Field ${t.field} is fixed at ${t.value} in a Software FMEA (IEC 62304: software failure probability is assumed 100 percent), so the RPN follows severity and detection. Omit it.`:""}function Dr(e){return Yt[e]}function $r(e){return function(e){return"hazard_iso14971"===e||"fmea_process"===e||"fmea_software"===e||"fmea_use"===e}(e)?ut:[]}function Pr(e){return $r(e).map((e,t)=>({...e,precedence:t+1}))}function Tr(){return`control_type is required for a risk control link (ISO 14971 clause 7). Choose one of: ${ut.map(e=>`${e.id} (${e.label})`).join(", ")}.`}function Ur(e){if(null==e||""===e)return null;if("number"==typeof e)return Number.isInteger(e)?e:null;if("string"!=typeof e||!/^\s*\d+\s*$/.test(e))return null;const t=parseInt(e,10);return Number.isNaN(t)?null:t}function Cr(e){const t=parseInt(e,10);return!Number.isNaN(t)&&t>=100}function Nr(e){return"hara_iso26262"===e?Xt:"hazard_iso14971"===e?er:tr}function Lr(e){return!0===e.approved&&(e.approved=!1,e.approved_at=null,e.approved_by=null,e.approved_note="",!0)}function Fr(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e):[]}function Wr(e,t,r=jr){const i=new Set(t.map(r).filter(e=>e.length>0));return e.filter(e=>i.has(r(e)))}function Vr(e){const t=e.length>1;return`Function-flagged tag${t?"s":""} used as domain${t?"s":""}: ${e.join(", ")}. A function is not a domain tag: seed a HARA row with it instead (atoms_create_analysis source_functions, or atoms_fill_row source_function), or unflag it in Taxonomy Management.`}var Br,Jr,Qr,Gr,Kr,Yr,Zr,Xr=A({"src/core/analysis.ts"(){pt={hara:["iso26262","iso14971"],dfmea:["design","process","software","use"]},mt={hara_iso26262:"ISO 26262",hazard_iso14971:"ISO 14971",dfmea_rpn:"AIAG-VDA FMEA Handbook",fmea_process:"AIAG-VDA FMEA Handbook",fmea_software:void 0,fmea_use:void 0},ut=[{id:"inherent",label:"Inherent design",abbr:"ID"},{id:"protective",label:"Protective measure",abbr:"PM"},{id:"information",label:"Information for safety",abbr:"IFS"}],_t="safety",ft="L1",ht=8,gt="-",yt=["prevention","detection","mitigation"],wt={"S1-E1-C1":"QM","S1-E1-C2":"QM","S1-E1-C3":"QM","S1-E2-C1":"QM","S1-E2-C2":"QM","S1-E2-C3":"QM","S1-E3-C1":"QM","S1-E3-C2":"QM","S1-E3-C3":"A","S1-E4-C1":"QM","S1-E4-C2":"A","S1-E4-C3":"B","S2-E1-C1":"QM","S2-E1-C2":"QM","S2-E1-C3":"QM","S2-E2-C1":"QM","S2-E2-C2":"QM","S2-E2-C3":"A","S2-E3-C1":"QM","S2-E3-C2":"A","S2-E3-C3":"B","S2-E4-C1":"A","S2-E4-C2":"B","S2-E4-C3":"C","S3-E1-C1":"QM","S3-E1-C2":"QM","S3-E1-C3":"A","S3-E2-C1":"QM","S3-E2-C2":"A","S3-E2-C3":"B","S3-E3-C1":"A","S3-E3-C2":"B","S3-E3-C3":"C","S3-E4-C1":"B","S3-E4-C2":"C","S3-E4-C3":"D"},jt={"1-1":"1","1-2":"1","1-3":"1","1-4":"2","1-5":"3","2-1":"1","2-2":"1","2-3":"2","2-4":"3","2-5":"4","3-1":"1","3-2":"2","3-3":"3","3-4":"4","3-5":"5","4-1":"2","4-2":"3","4-3":"4","4-4":"5","4-5":"5","5-1":"3","5-2":"4","5-3":"5","5-4":"5","5-5":"5"},St={"1-1":"Low","1-2":"Low","1-3":"Low","1-4":"Low","1-5":"Medium","2-1":"Low","2-2":"Low","2-3":"Low","2-4":"Medium","2-5":"Medium","3-1":"Low","3-2":"Low","3-3":"Medium","3-4":"Medium","3-5":"High","4-1":"Low","4-2":"Medium","4-3":"Medium","4-4":"High","4-5":"High","5-1":"Medium","5-2":"Medium","5-3":"High","5-4":"High","5-5":"High"},kt={Low:"Acceptable",Medium:"Acceptable with justification",High:"Not acceptable"},xt="10",At={hara_iso26262:{severity:["S0","S1","S2","S3"],exposure:["E0","E1","E2","E3","E4"],controllability:["C0","C1","C2","C3"]},dfmea_rpn:{severity:vt=["1","2","3","4","5","6","7","8","9","10"],occurrence:vt,detection:vt},hazard_iso14971:{severity:bt=["1","2","3","4","5"],p1:bt,p2:bt,residual_s:bt,residual_p1:bt,residual_p2:bt},fmea_process:{severity:vt,occurrence:vt,detection:vt},fmea_software:{severity:vt,detection:vt},fmea_use:{severity:vt,occurrence:vt,detection:vt}},Et={hara_iso26262:["failure_mode","operating_scenario","hazard","assumptions","severity","exposure","controllability","safe_state","ftti","rationale","source_function","safety_goal","s_rationale","e_rationale","c_rationale"],dfmea_rpn:["failure_mode","cause","severity","occurrence","detection","rationale"],hazard_iso14971:["failure_mode","operating_scenario","hazard","assumptions","severity","p1","p2","rationale","source_function","safety_goal","s_rationale","p1_rationale","p2_rationale","residual_s","residual_p1","residual_p2","benefit_note"],fmea_process:["process_step","failure_mode","cause","severity","occurrence","current_controls","detection","rationale"],fmea_software:["failure_mode","cause","severity","detection","rationale"],fmea_use:["failure_mode","cause","severity","occurrence","detection","rationale"]},It=["intended_use","foreseeable_misuse","safety_characteristics"],Rt=["overall_residual_risk","overall_residual_rationale","software_safety_class","software_safety_rationale"],Ot=[...It,...Rt],qt=["Low","Medium","High"],zt=["A","B","C"],Mt={hara:"safety-goal",dfmea:"failure-mode"},Ht={hara_iso26262:"SG",dfmea_rpn:"FM",hazard_iso14971:"RSK",fmea_process:"PF",fmea_software:"FM",fmea_use:"FM"},Dt={hara_iso26262:"HARA",dfmea_rpn:"DFMEA",hazard_iso14971:"RA",fmea_process:"PFMEA",fmea_software:"SFMEA",fmea_use:"UFMEA"},$t=["HA"],Array.from(new Set([...Object.values(Dt),...Object.values(Ht),...$t])).sort(),Pt={hara:"hara",dfmea:"dfmea",risk_analysis:"hara",fmea:"dfmea",hazard_analysis:"hara"},Tt=["hara","dfmea","risk_analysis","fmea","hazard_analysis"],Ut={hara:"iso26262",risk_analysis:"iso14971",hazard_analysis:"iso14971",dfmea:"design",fmea:"process"},Ct={hara:"iso14971",risk_analysis:"iso14971",hazard_analysis:"iso14971",dfmea:"process",fmea:"process"},Nt={fmea_failure_mode:"Failure mode: the way the design, the process step or the software fails to do what it should, for example a valve that sticks open or a step skipped.",fmea_effect:"Effect: what the failure mode does to the customer, the patient or the next step of the process. Severity rates the effect.",fmea_cause:"Cause: the mechanism or condition behind the failure mode, what has to be true for it to happen. Occurrence rates the cause.",fmea_action:"Action: the requirement that answers the failure mode. The link records how it answers it, and a row at RPN 100 or above with no action is a gap."},Lt={...Nt,s:"Severity: how badly people could be hurt if the hazardous event happens. S0 no injuries, S1 light, S2 severe but survivable, S3 life-threatening or fatal (ISO 26262-3).",e:"Exposure: how often the operational scenario occurs. E0 incredible, E1 very low, E2 low, E3 medium, E4 high probability (ISO 26262-3).",c:"Controllability: how likely the driver or others can avoid the harm. C0 controllable in general, C1 simply, C2 normally, C3 difficult or uncontrollable (ISO 26262-3).",asil:"Automotive Safety Integrity Level: computed from S, E and C per ISO 26262-3 Table 4. QM means no safety requirement is needed; D is the most demanding.",safe_state:"The condition the system moves to when the function fails, for example handing braking back to the driver.",ftti:"Fault tolerant time interval: the longest time a fault may stay undetected before the hazardous event can occur.",safety_goal:"The top-level safety requirement derived from a hazardous event. One goal per HARA row; it is met by safety requirements that mitigate it.",safety_req:"A requirement of type Safety that mitigates a safety goal. At L1 it is a functional or technical safety requirement and traces to a system requirement.",verified_by:"The test case that proves this safety goal on its operating design domain (ODD).",hazardous_event:"What happens to the vehicle and the people when the failure occurs in this scenario. One per row.",hazard:"The potential harm, for example unintended braking.",assumptions:"The conditions you assume for the rating, one per line, for example light traffic or a speed limit.",fn:"The system function this row analyses, from the project's function tags.",failure_mode:"How the function fails: no function, loss, partial, degradation or misinterpretation.",scenario:"The driving situation in which the failure is analysed. One per row.",dfmea_s:"Severity: how serious the effect of the failure is for the customer, 1 (no effect) to 10 (safety or regulatory failure without warning).",o:"Occurrence: how likely the cause is, 1 (remote) to 10 (very high).",d:"Detection: how likely current controls detect the cause before it reaches the customer, 1 (certain) to 10 (impossible).",rpn:"Risk priority number: S times O times D. Rows at 100 or above are gaps."},Ft={s:"Severity: a measure of the possible consequences of a hazard. 1 Negligible, 2 Minor, 3 Serious, 4 Critical, 5 Catastrophic (ISO 14971).",p1:"P1: the probability of a hazardous situation occurring. 1 Improbable, 2 Remote, 3 Occasional, 4 Probable, 5 Frequent (the example scale of ISO 14971:2007 Annex D, kept in ISO/TR 24971).",p2:"P2: the probability that the hazardous situation leads to harm, given that it occurs. 1 to 5 on the same scale (ISO 14971:2007 Annex D, ISO/TR 24971).",p0:"P0: the overall probability of harm, looked up from P1 and P2 on the 5 by 5 probability table.",risk:"Risk: the combination of the probability of harm and its severity, looked up from P0 and S. Low is acceptable, Medium is acceptable with a justification, High is not acceptable (ISO 14971).",risk_analysis:"Risk analysis: the systematic use of available information to identify hazards and to estimate the risk for each hazardous situation (ISO 14971:2019 definition 3.19 and clause 5). It is the name the standard gives this document.",hazard:"Hazard: a potential source of harm, for example an uncontrolled flow of the drug.",hazardous_situation:"Hazardous situation: the circumstance in which people are exposed to the hazard.",sequence:"Sequence of events: what has to happen for the hazard to become a hazardous situation. One sequence per row.",harm:"Harm: injury or damage to the health of people, or damage to property or the environment.",assumptions:"The conditions you assume for the estimate, one per line, for example the care setting or the patient group.",fn:"The device function this row analyses, from the project's function tags.",risk_control:"Risk control: the measure that reduces the risk. Applied in order: inherent safety by design, then protective measures, then information for safety (ISO 14971 clause 7).",control_req:"A requirement of type Risk control that implements a risk control measure. It carries the control type and traces to a system requirement.",verified_by:"Verification of effectiveness: the test that shows the risk control works, not only that it was implemented (ISO 14971 clause 7.2).",residual:"Residual risk: the risk remaining after the risk control measures are applied, estimated with the same two tables.",benefit:"Benefit-risk analysis: the argument that the medical benefit outweighs a residual risk that is still not acceptable (ISO 14971 clause 7.4).",dfmea_s:"Severity: how serious the effect of the failure is, 1 (no effect) to 10 (harm without warning).",o:"Occurrence: how likely the cause is, 1 (remote) to 10 (very high).",o_sw:"IEC 62304: software failure probability is assumed 100 percent; rate by severity.",d:"Detection: how likely current controls detect the cause before it reaches the patient, 1 (certain) to 10 (impossible).",rpn:"Risk priority number: S times O times D. Rows at 100 or above are gaps.",process_step:"The step of the clinical or manufacturing process this row analyses.",controls:"Current controls: what already stands between this cause and the effect.",intended_use:"Intended use: what the device is for, for which patient, by which user and in which environment. ISO 14971:2019 5.2 asks for it before any hazard is identified.",foreseeable_misuse:"Reasonably foreseeable misuse: use in a way the manufacturer did not intend but can expect, one case per line (ISO 14971:2019 5.2).",safety_characteristics:"Characteristics related to safety: the qualitative and quantitative characteristics that could affect safety, one per line (ISO 14971:2019 5.3).",overall_residual_risk:"Overall residual risk: the risk that remains from the device as a whole, once every individual risk is controlled. Judged by a person against the criteria in the risk management plan (ISO 14971:2019 clause 8).",software_safety_class:"Software safety class: A no injury or damage to health is possible, B non-serious injury is possible, C death or serious injury is possible (IEC 62304 4.3)."},Wt={...Nt,...Ft},Vt={...Nt,dfmea_s:Lt.dfmea_s,o:Lt.o,d:Lt.d,rpn:Lt.rpn},Bt={...Nt,dfmea_s:Ft.dfmea_s,o:Ft.o,o_sw:Ft.o_sw,d:Ft.d,rpn:Ft.rpn,process_step:Ft.process_step,controls:Ft.controls,fn:Ft.fn,risk_control:Ft.risk_control,control_req:Ft.control_req,verified_by:Ft.verified_by},Jt={hara_iso26262:{source_function:"Function",failure_mode:"Failure Mode",operating_scenario:"Operational Scenario",effect:"Hazardous Event",hazard:"Hazard",assumptions:"Assumptions",s_rationale:"S evidence",e_rationale:"E evidence",c_rationale:"C evidence",safe_state:"Safe state",ftti:"FTTI",severity:"S",exposure:"E",controllability:"C",safety_goal:"Safety goal",asil:"ASIL"},dfmea_rpn:{source_req:"Source",failure_mode:"Failure Mode",effect:"Effect",severity:"S",cause:"Cause",occurrence:"O",detection:"D",rpn:"RPN"},hazard_iso14971:{source_function:"Function",failure_mode:"Hazard",operating_scenario:"Sequence of events",effect:"Hazardous situation",hazard:"Harm",assumptions:"Assumptions",s_rationale:"S rationale",p1_rationale:"P1 rationale",p2_rationale:"P2 rationale",residual_s:"Residual S",residual_p1:"Residual P1",residual_p2:"Residual P2",residual_level:"Residual risk",benefit_note:"Benefit-risk note",severity:"S",p1:"P1",p2:"P2",safety_goal:"Risk control statement",risk:"Risk"},fmea_process:{source_req:"Source",process_step:"Process step",failure_mode:"Failure mode",effect:"Effect",severity:"S",cause:"Cause",occurrence:"O",current_controls:"Current controls",detection:"D",rpn:"RPN"},fmea_software:{source_req:"Source",failure_mode:"Failure Mode",effect:"Effect",severity:"S",cause:"Cause",occurrence:"O",detection:"D",rpn:"RPN"},fmea_use:{source_req:"Source",failure_mode:"Failure Mode",effect:"Effect",severity:"S",cause:"Cause",occurrence:"O",detection:"D",rpn:"RPN"}},Qt=["asil","rpn","risk","p0"],Gt=["approved","approved_at","approved_by","approved_note","field_source","link_roles","mitigation_types"],Kt=[...Qt,...Gt],Yt={hara_iso26262:{parent:"HARA",row:"Safety goal",row_statement:"Safety goal",mitigating_requirement:"Safety requirement",lane:"Safety lane",level:"ASIL"},dfmea_rpn:{parent:"DFMEA",row:"Failure mode",row_statement:"Action",mitigating_requirement:"Requirement",lane:"Safety lane",level:"RPN"},hazard_iso14971:{parent:"Risk analysis",row:"Risk",row_statement:"Risk control statement",mitigating_requirement:"Risk control",lane:"Risk lane",level:"Risk"},fmea_process:{parent:"PFMEA",row:"Process failure",row_statement:"Action",mitigating_requirement:"Risk control",lane:"Risk lane",level:"RPN"},fmea_software:{parent:"Software FMEA",row:"Failure mode",row_statement:"Action",mitigating_requirement:"Risk control",lane:"Risk lane",level:"RPN"},fmea_use:{parent:"Use FMEA",row:"Failure mode",row_statement:"Action",mitigating_requirement:"Risk control",lane:"Risk lane",level:"RPN"}},Zt=(e,t,r)=>1===e?t:r,Xt=[{kind:"unmitigated",scope:"row",fix:"link_requirement",fixLabel:"Draft safety requirement",label:e=>`${e} ${Zt(e,"safety goal","safety goals")} unmitigated`,matches:e=>e.level!==gt&&"QM"!==e.level&&!e.linked},{kind:"no_test",scope:"row",fix:"link_test",fixLabel:"Link a test",label:e=>`${e} ${Zt(e,"safety goal","safety goals")} without a test on its ODD`,matches:e=>e.level!==gt&&"QM"!==e.level&&e.linked&&!e.verified},{kind:"no_parent",scope:"requirement",fix:"open_item",fixLabel:"Set parent",label:e=>`${e} ${Zt(e,"safety requirement","safety requirements")} without a system parent`}],er=[{kind:"no_control",scope:"row",fix:"link_requirement",fixLabel:"Draft risk control requirement",label:e=>`${e} ${Zt(e,"risk","risks")} above Low with no control`,matches:e=>e.level!==gt&&"Low"!==e.level&&!e.linked},{kind:"not_verified",scope:"row",fix:"link_test",fixLabel:"Link a test",label:e=>`${e} ${Zt(e,"control","controls")} not verified for effectiveness`,matches:e=>e.level!==gt&&"Low"!==e.level&&e.linked&&!e.verified},{kind:"no_residual",scope:"row",fix:"open_row",fixLabel:"Assess residual risk",label:e=>`${e} residual ${Zt(e,"risk","risks")} not assessed`,matches:e=>e.level!==gt&&"Low"!==e.level&&e.linked&&e.residualLevel===gt},{kind:"no_parent",scope:"requirement",fix:"open_item",fixLabel:"Set parent",label:e=>`${e} risk ${Zt(e,"control","controls")} without a system parent`},{kind:"no_scope",scope:"analysis",fix:"open_analysis",fixLabel:"Open the scope",label:()=>"Scope not stated",matchesAnalysis:e=>e.hasRows&&!e.scopeStated},{kind:"no_overall",scope:"analysis",fix:"open_analysis",fixLabel:"Judge the overall residual risk",label:()=>"Overall residual risk not judged",matchesAnalysis:e=>e.hasRows&&e.everyRowSettled&&!e.overallJudged}],tr=[{kind:"rpn",scope:"row",fix:"link_requirement",fixLabel:"Link a requirement",label:e=>`${e} at RPN 100 or above with no mitigating requirement`,matches:e=>Cr(e.level)&&!e.linked},{kind:"severity",scope:"row",fix:"link_requirement",fixLabel:"Link a requirement",label:e=>`${e} at severity 9 or above below RPN 100 with no mitigating requirement`,matches:e=>!Cr(e.level)&&null!==e.severity&&e.severity>=9&&!e.linked}]}}),ei=A({"src/core/errors.ts"(){}}),ti={};function ri(e,t=Jr){if("string"!=typeof e)return"";const r=e.replace(Gr,"").replace(Kr,"");return r.length<=t?r:r.slice(0,t)+"..."}function ii(e){return"string"!=typeof e?"":e.trim().toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"").replace(/-+/g,"-").replace(/^-|-$/g,"")}function ai(e,t){return{status:"success",data:e,...t?{meta:t}:{}}}function ni(e,t,r){return{total_count:e,limit:t,offset:r,has_more:r+t<e}}function si(e,t){return{status:"error",message:e,next_steps:t}}function oi(e,t){return si(`${e} '${ri(t)}' not found`,[`Verify the ${e.toLowerCase()} ID is correct`,"Use atoms_list_items or atoms_search to find valid IDs"])}function di(e){return si(`${e} role cannot modify project data`,["Contact your org admin to upgrade your role to editor","Use read-only tools (atoms_list_items, atoms_get_item, atoms_search) instead"])}function li(e){return si(`Validation error: ${ri(e,Qr)}`,["Check the parameter types and constraints in the tool description","Ensure all required parameters are provided"])}function ci(){return si("Not authenticated. Run 'npx @atoms-tech/atoms-mcp login' first.",["Run: npx @atoms-tech/atoms-mcp login","Or set ATOMS_ACCESS_TOKEN environment variable"])}function pi(){return si("Your ATOMS session token expired and could not be refreshed automatically.",["Call atoms_status() — it will refresh the token if possible","If that still reports 'expired', restart Claude Desktop (quit and reopen)","If the problem persists, run: npx @atoms-tech/atoms-mcp logout && npx @atoms-tech/atoms-mcp login"])}function mi(e){return si("Tool call rejected: project_id is outside the configured ATOMS_MCP_PROJECT_ID scope.",[`This MCP session is bound to project ${e}`,"Pass that project_id, or restart without ATOMS_MCP_PROJECT_ID to access other projects"])}function ui(e){return si("Rate limited: too many requests to ATOMS API.",[e?`Wait ${e} seconds before retrying.`:"Wait a few seconds before retrying.","Reduce the frequency of back-to-back tool calls"])}function _i(e){return si(`Parse error: ${ri(e,Qr)}`,["Check that body/title/summary fields contain valid text (no unexpected control characters)","If the content was generated programmatically, validate the string before passing it","Simplify the content and retry — if it succeeds, the original content has a malformed character"])}function fi(e){return function(e){return/stale_item_write|sqlstate\s*40001|could not serialize access/i.test(e)}(e)}function hi(){return si("Stale write rejected: this item changed after it was read.",["Call atoms_get_item again and use the returned metadata.updated_at value","Review the current item before retrying so you do not overwrite newer work","Retry atoms_update_item or atoms_link_items with the fresh expected timestamp"])}function gi(){return si("Save paused because this item is already being written.",["Wait a few seconds, read the item again, and retry once","Do not repeatedly retry the same stale payload"])}function yi(){return si("Item saves are temporarily paused by an ATOMS operator.",["Keep your local draft and wait for the operator to re-enable saves","Do not repeatedly retry while the safety brake is active"])}function wi(){return si("Item save rejected because this item is in Trash.",["Restore the item before changing it","Do not retry the same save while the item remains in Trash"])}function vi(e){const t=e instanceof Error?e.message:String(e&&"object"==typeof e&&"message"in e?e.message??"":e),r=e&&"object"==typeof e&&"code"in e?String(e.code??""):"";return/not authenticated/i.test(t)?ci():function(e){let t="";if(e instanceof Error)t=e.message;else if(e&&"object"==typeof e){const r=e;t="string"==typeof r.message?r.message:"string"==typeof r.code?r.code:String(e)}else null!=e&&(t=String(e));return/jwt\s*expired|invalid.?jwt|token.*(?:has\s+)?expired|jwt.*invalid|pgrst301/i.test(t)}(e)?pi():/rate.?limit|too many requests|429/i.test(t)?ui():fi(t)?hi():/soft_deleted_item_write/i.test(t)?wi():/save_temporarily_unavailable/i.test(t)?yi():"55P03"===r||"57014"===r||/item_save_busy|statement timeout|request timeout|upstream request timeout/i.test(t)?gi():"42501"===r&&/org_project_field_admin_required/i.test(t)?di("Org admin"):"42501"===r&&/project_field/i.test(t)?di("Project admin or owner"):"P0002"===r&&/project_field/i.test(t)?si("Project field not found.",["Refresh the field list and retry with an existing field name"]):["22023","23505","23514"].includes(r)&&/project_field/i.test(t)?li(t.replace(/^project_field_/i,"").replace(/_/g," ")):/invalid input syntax|invalid json|unexpected token|malformed|json parse/i.test(t)?_i(t):ji(t)}function bi(e){const t=e instanceof Error?e.message:String(e&&"object"==typeof e&&"message"in e?e.message??"":e??""),r=e&&"object"==typeof e&&"code"in e?String(e.code??""):"";return["40001","PT409","55P03","57014"].includes(r)||/stale_item_write|soft_deleted_item_write|save_temporarily_unavailable|item_save_busy|statement timeout|request timeout|upstream request timeout|fetch failed|network error|connection (?:closed|reset|refused)/i.test(t)}function ji(e){return/row.level security|permission denied for/i.test(e)?si("Permission denied: your account does not have write access to this project.",["Verify you have editor or admin role in this project","Contact your org admin to upgrade your permissions","Use atoms_list_projects to confirm the project_id is one you have access to"]):si(`Database error: ${ri(e,Qr)}`,["This may be a temporary issue — try again","If the error persists, check that the project_id is valid"])}function Si(e){return null==e?e:`<user_content>${e.replace(/<\/\s*user_content\s*>/gi,"</user_content>")}</user_content>`}function ki(e){return"string"!=typeof e?e:e.replace(/<\s*\/?\s*user_content\s*>/gi,"")}function xi(e){return Yr.has(e)}function Ai(e,t,r=!1){return"string"==typeof e?r||null!=t&&Yr.has(t)?Si(e):e:Array.isArray(e)?e.map(e=>Ai(e,t,r)):e&&"object"==typeof e?Object.fromEntries(Object.entries(e).map(([e,t])=>[e,Ai(t,e,r||Zr.has(e))])):e}function Ei(e){if(JSON.stringify(e,null,2).length>Br&&"success"===e.status&&Array.isArray(e.data)){const t=Math.max(1,Math.floor(e.data.length/2)),r={...e,data:e.data.slice(0,t),meta:{...e.meta,truncated:!0,truncation_message:`Response truncated from ${e.data.length} to ${t} items. Use 'offset' parameter or add filters to see more results.`}};return{content:[{type:"text",text:JSON.stringify(Ai(r),null,2)}],structuredContent:r}}return{content:[{type:"text",text:JSON.stringify(Ai(e),null,2)}],structuredContent:e}}function Ii(e){return{content:[{type:"text",text:JSON.stringify(Ai(e),null,2)}],structuredContent:e,isError:!0}}E(ti,{CHARACTER_LIMIT:()=>Br,USER_CONTENT_KEYS:()=>Yr,accessDeniedError:()=>di,authError:()=>ci,classifyError:()=>vi,dbError:()=>ji,errorResponse:()=>si,formatErrorResult:()=>Ii,formatToolResult:()=>Ei,isAutoWrappedKey:()=>xi,isItemSaveStopError:()=>bi,isStaleWriteErrorMessage:()=>fi,jwtExpiredError:()=>pi,notFoundError:()=>oi,paginationMeta:()=>ni,parseError:()=>_i,rateLimitError:()=>ui,sanitizeForEcho:()=>ri,sanitizeTagOrLevel:()=>ii,saveBusyError:()=>gi,saveUnavailableError:()=>yi,scopeError:()=>mi,softDeletedItemError:()=>wi,staleWriteError:()=>hi,stripUserContentMarkers:()=>ki,success:()=>ai,validationError:()=>li,wrapMcpUserContentFields:()=>Ai,wrapUserContent:()=>Si});var Ri,Oi=A({"src/tools/_base.ts"(){ei(),Br=25e3,Jr=50,Qr=200,Gr=new RegExp("[\\u0000-\\u001F\\u007F]","g"),Kr=new RegExp("[\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u206F\\uFEFF]","g"),Yr=new Set(["body","description","domain","domains","error","level","levels","message","name","note","summary","title","value","effect","failure_mode","cause","operating_scenario","safe_state","ftti","rationale","hazard","assumptions","source_function"]),Zr=new Set(["fields"])}});var qi=A({"src/apps/register.ts"(){Ri={resourceDomains:["fonts.googleapis.com","fonts.gstatic.com"]}}});function zi(e){return e.replace(/[\\%_*]/g,e=>`\\${e}`)}function Mi(e){return`"${`*${zi(e)}*`.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function Hi(e){return zi(e)}function Di(e){return`*"${zi(e)}"*`}var $i=A({"src/core/postgrest.ts"(){}});async function Pi(e,t,r){let i=e.from("items").select("*",{count:"exact"}).eq("project_id",t).is("deleted_at",null).order("created_at",{ascending:!0});r.type&&(i=i.eq("type",r.type)),r.domain&&(i=i.filter("data->tags->>domains","ilike",Di(r.domain))),r.level&&(i=i.filter("data->tags->>level","ilike",Hi(r.level))),i=i.range(r.offset,r.offset+r.limit-1);const{data:a,error:n,count:s}=await i;if(n)throw new Error(n.message);return{items:a??[],totalCount:s??0}}async function Ti(e,t,r){const{data:i,error:a}=await e.from("items").select("*").eq("id",r).eq("project_id",t).is("deleted_at",null).maybeSingle();if(a)throw new Error(a.message);return i}async function Ui(e,t){if(t.baseline_id&&t.as_of)throw new Error("baseline_id and as_of are mutually exclusive");if(t.as_of)return t.as_of;if(t.baseline_id){const{data:r,error:i}=await e.from("baselines").select("project_id, snapshot_at, deleted_at").eq("id",t.baseline_id).maybeSingle();if(i)throw new Error(`resolveSnapshotAt: ${i.message}`);if(!r||null!==r.deleted_at)throw new Error(`Baseline not found: ${t.baseline_id}`);if(r.project_id!==t.project_id)throw new Error("Baseline does not belong to this project");return r.snapshot_at}return null}async function Ci(e,t){const{data:r,error:i}=await e.from("baselines").select("id, project_id, name, description, field_definitions_snapshot, snapshot_at, created_at, created_by, locked_at, locked_by, deleted_at, release_version, release_status").eq("id",t).maybeSingle();if(i)throw new Error(`getBaselineById: ${i.message}`);return r&&null===r.deleted_at?r:null}function Ni(e){return{id:e.id,project_id:e.project_id,type:e.type,title:e.title,data:e.data,created_by:e.changed_by,updated_by:e.changed_by,created_at:e.valid_from,updated_at:e.valid_from,deleted_at:null}}async function Li(e,t,r){const{data:i,error:a}=await e.rpc("project_state_at",{p_project_id:t,p_snapshot_at:r});if(a)throw new Error(`projectStateAt: ${a.message}`);return i??[]}async function Fi(e,t,r,i){const a=r.trim();if(!a)return{items:[],totalCount:0};let n=null;if(i.asset&&i.asset.trim()){const{data:r,error:a}=await e.from("test_results").select("item_id").eq("project_id",t).eq("asset",i.asset.trim());if(a)throw new Error(a.message);const s=new Set;for(const e of r??[])s.add(e.item_id);if(n=Array.from(s),0===n.length)return{items:[],totalCount:0}}let s=e.from("items").select("*",{count:"exact"}).eq("project_id",t).is("deleted_at",null).or(`title.ilike.${Mi(a)},data->>body.ilike.${Mi(a)},data->>summary.ilike.${Mi(a)}`).limit(i.limit);i.type&&(s=s.eq("type",i.type)),i.domain&&(s=s.filter("data->tags->>domains","ilike",Di(i.domain))),i.level&&(s=s.filter("data->tags->>level","ilike",Hi(i.level))),n&&(s=s.in("id",n));const{data:o,error:d,count:l}=await s;if(d)throw new Error(d.message);return{items:o??[],totalCount:l??0}}function Wi(e,t){const r=new Set(e.map(e=>e.id)),i=new Set,a=[],n=(e,t,n)=>{if(e===t)return;if(!r.has(e)||!r.has(t))return;const s=`${e}|${t}|${n}`;i.has(s)||(i.add(s),a.push({from_id:e,to_id:t,type:n}))};for(const e of t)n(e.from_id,e.to_id,e.type);for(const t of e){const e=t.data?.relationships??{},r=t=>Array.isArray(e[t])?e[t].filter(e=>"string"==typeof e):[];for(const e of r("parents"))n(t.id,e,"parent");for(const e of r("children"))n(t.id,e,"child");for(const e of r("related"))n(t.id,e,"related");for(const e of r("verifies"))n(t.id,e,"verifies");for(const e of r("verified_by"))n(t.id,e,"verified_by");for(const e of r("mitigated_by"))n(t.id,e,"mitigated_by");for(const e of r("mitigates"))n(t.id,e,"mitigates")}return a}async function Vi(e,t,r){const i="requirement"===r?"REQ":"test-case"===r?"TC":"note"===r?"NOTE":"TABLE",{data:a,error:n}=await e.rpc("next_item_id",{p_project_id:t,p_prefix:i});if(n)throw new Error(`ID generation failed: ${n.message}`);return a}var Bi=A({"src/db/queries.ts"(){$i(),Xr()}}),Ji={};async function Qi(){try{const e=await He(async()=>{const e=await qe();return await async function(e){const{data:t,error:r}=await e.from("projects").select("id, name, description, org_id, created_at, organizations(name)").is("deleted_at",null).order("created_at",{ascending:!1});if(r)throw new Error(r.message);return(t??[]).map(e=>({id:e.id,name:e.name,description:e.description,org_id:e.org_id,org_name:e.organizations?.name??"Unknown",created_at:e.created_at}))}(e)}),t=st(),r=$,i=U().project_scope,a=i?new Set(i):null;return Ei(ai(e.filter(e=>!r||e.org_id===r).filter(e=>!t||e.id===t).filter(e=>!a||a.has(e.id)).map(e=>({id:e.id,name:e.name,description:e.description,org_id:e.org_id,org_name:e.org_name,created_at:e.created_at}))))}catch(e){return Ii(vi(e))}}E(Ji,{listProjectsHandler:()=>Qi});var Gi=A({"src/tools/list-projects.ts"(){Ne(),Bi(),dt(),C(),Oi()}}),Ki={};async function Yi(e){try{const t=await qe();let r,i,a=null;try{a=await Ui(t,{baseline_id:e.baseline_id,as_of:e.as_of,project_id:e.project_id})}catch(e){return Ii(ji(e.message))}if(a){const n=(await Li(t,e.project_id,a)).filter(t=>{if(e.type&&t.type!==e.type)return!1;const r=t.data??{};if(e.domain){const t=r?.tags?.domains??[];if(!Array.isArray(t)||!t.some(t=>jr(t)===jr(e.domain)))return!1}return!e.level||dr(r?.tags?.level)===dr(e.level)});i=n.length,r=n.slice(e.offset,e.offset+e.limit).map(e=>({id:e.id,title:e.title,type:e.type,data:e.data}))}else{const a=await Pi(t,e.project_id,{type:e.type,domain:e.domain,level:e.level,limit:e.limit,offset:e.offset});r=a.items,i=a.totalCount}return Ei(ai(r.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level,req_type:e.data?.req_type})),ni(i,e.limit,e.offset)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}E(Ki,{listItemsHandler:()=>Yi});var Zi,Xi=A({"src/tools/list-items.ts"(){Ne(),Bi(),Oi(),Xr()}}),ea={};function ta(e,t){if(!t)return e;const r={...e};for(const e of Zi){const i=t[e];null!=i&&(r[e]=i)}return r}async function ra(e){try{const r=await qe();let i=null;try{i=await Ui(r,{baseline_id:e.baseline_id,as_of:e.as_of,project_id:e.project_id})}catch(e){return Ii(si(e.message,["Pass baseline_id OR as_of, not both"]))}const a=i?await async function(e,t,r,i){const{data:a,error:n}=await e.rpc("item_state_at",{p_item_id:r,p_project_id:t,p_snapshot_at:i});if(n)throw new Error(`itemStateAt: ${n.message}`);return(a??[])[0]??null}(r,e.project_id,e.item_id,i).then(e=>e?Ni(e):null):await Ti(r,e.project_id,e.item_id);if(!a)return Ii(oi("Item",e.item_id));const n={parents:[],children:[],related:[],verified_by:[],verifies:[],mitigated_by:[],mitigates:[]};if(i){const e=a.data?.relationships??{};for(const t of Object.keys(n)){const r=e[t];Array.isArray(r)&&(n[t]=r.slice())}}else{const t=await async function(e,t,r){const{data:i,error:a}=await e.from("item_relationships").select("from_id, to_id, type").eq("project_id",t).or(`from_id.eq.${r},to_id.eq.${r}`);if(a)throw new Error(a.message);return i??[]}(r,e.project_id,e.item_id),i={parent:"parents",child:"children",related:"related",verifies:"verifies",verified_by:"verified_by",mitigated_by:"mitigated_by",mitigates:"mitigates"},s={parent:"children",child:"parents",verified_by:"verifies",verifies:"verified_by",mitigated_by:"mitigates",mitigates:"mitigated_by",related:"related"},o=(e,t)=>{if(!(e in n))return;const r=n[e];r.includes(t)||r.push(t)},d=new Set([e.item_id]);for(const e of t)d.add(e.from_id),d.add(e.to_id);const l=a.data?.relationships??{};for(const e of Object.keys(n)){const t=l[e];if(Array.isArray(t))for(const e of t)"string"==typeof e&&d.add(e)}let c=new Set([e.item_id]);const p=Array.from(d);if(p.length>0){const{data:t,error:i}=await r.from("items").select("id").eq("project_id",e.project_id).is("deleted_at",null).in("id",p);if(i)throw new Error(i.message);c=new Set((t??[]).map(e=>e.id)),c.add(e.item_id)}const m=e=>c.has(e);for(const r of t)if(r.from_id===e.item_id){if(!m(r.to_id))continue;o(i[r.type]??r.type,r.to_id)}else{if(!m(r.from_id))continue;o(s[r.type]??r.type,r.from_id)}for(const t of Object.keys(n)){const r=l[t];if(Array.isArray(r))for(const i of r)"string"==typeof i&&i!==e.item_id&&m(i)&&o(t,i)}}let s=[];if("test-case"===a.type)if(i)s=(a.data?.runs??[]).map(e=>({base:{result:e.result??"not-run",run_by:e.by??e.run_by??null,run_at:e.date??e.run_at??"",note:e.note??null,..."string"==typeof e.asset?{asset:e.asset}:{}},canonical:e})).filter(e=>"string"==typeof e.base.run_at&&e.base.run_at.length>0).sort((e,t)=>String(t.base.run_at).localeCompare(String(e.base.run_at))).slice(0,20).map(e=>ta(e.base,e.canonical));else{const{data:t,error:i}=await r.from("test_results").select("result, run_by, run_at, note, asset").eq("item_id",e.item_id).eq("project_id",e.project_id).order("run_at",{ascending:!1}).limit(20);if(!i&&t){const e=new Map;for(const t of a.data?.runs??[])"string"==typeof t?.date&&e.set(t.date,t);s=t.map(t=>ta(t,e.get(String(t.run_at))))}}return Ei(ai({id:a.id,type:a.type,req_type:a.data?.req_type,title:a.title,summary:a.data?.summary,body:a.data?.body,domains:a.data?.tags?.domains??[],level:a.data?.tags?.level,tags:a.data?.tags??{domains:[]},ownership:a.data?.ownership??{primary:null,additional:[]},relationships:n,links:a.data?.links??[],fields:(t=a.data?.fields,!t||"object"!=typeof t||Array.isArray(t)?{}:Object.fromEntries(Object.entries(t).filter(e=>"string"==typeof e[1]))),status:a.data?.status,..."test-case"===a.type?{latest_result:s[0]?.result??"not-run",test_results:s}:{},metadata:{created_at:a.created_at,created_by:a.created_by,updated_at:a.updated_at,updated_by:a.updated_by,source_id:a.data?.metadata?.source_id}}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}var t}E(ea,{getItemHandler:()=>ra});var ia=A({"src/tools/get-item.ts"(){Ne(),Bi(),Oi(),Zi=["point","release_id","build","environment","tooling","step_results"]}}),aa={};async function na(e){try{const t=await qe();let r,i,a=null;try{a=await Ui(t,{baseline_id:e.baseline_id,project_id:e.project_id})}catch(e){return Ii(ji(e.message))}if(a){const n=await Li(t,e.project_id,a),s=e.query.trim().toLowerCase(),o=0===s.length?[]:n.filter(t=>{if(e.type&&t.type!==e.type)return!1;const r=t.data??{};return[t.title,r?.body,r?.summary].filter(e=>"string"==typeof e).map(e=>e.toLowerCase()).some(e=>e.includes(s))});i=o.length,r=o.slice(0,e.limit).map(e=>({id:e.id,title:e.title,type:e.type,data:e.data}))}else{const a=await Fi(t,e.project_id,e.query,{type:e.type,limit:e.limit,asset:e.asset});r=a.items,i=a.totalCount}return Ei(ai(r.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level})),ni(i,e.limit,0)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}E(aa,{searchHandler:()=>na});var sa=A({"src/tools/search.ts"(){Ne(),Bi(),Oi()}}),oa={};async function da(e){try{const t=await qe(),{access_token:r}=await je(),{items:i,totalCount:a}=await async function(e,t,r,i){const{ATOMS_SUPABASE_URL:a,ATOMS_SUPABASE_ANON_KEY:n}=await Promise.resolve().then(()=>(we(),ge)),s=await fetch(`${a}/functions/v1/embed`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i.accessToken}`,apikey:n},body:JSON.stringify({input:r})});if(!s.ok)throw new Error(`Embed function failed: ${s.status}`);const{embedding:o}=await s.json(),{data:d,error:l}=await e.rpc("semantic_search",{query_embedding:o,p_project_id:t,match_threshold:.3,match_count:i.limit,p_type:i.type||null});if(l)throw new Error(l.message);return{items:d??[],totalCount:(d??[]).length,method:"semantic"}}(t,e.project_id,e.query,{type:e.type,limit:e.limit,accessToken:r});return Ei(ai(i.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level})),ni(a,e.limit,0)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}E(oa,{semanticSearchHandler:()=>da});var la=A({"src/tools/semantic-search.ts"(){Ne(),Re(),Bi(),Oi()}}),ca={};async function pa(e){try{if(e.baseline_id&&e.as_of)return Ii(li("baseline_id and as_of are mutually exclusive"));const t=await qe();let r;if(e.baseline_id){const i=await Ci(t,e.baseline_id);if(!i)return Ii(li(`Baseline ${e.baseline_id} not found`));if(i.project_id!==e.project_id)return Ii(li("Baseline does not belong to this project"));r=i.snapshot_at}else e.as_of&&(r=e.as_of);const{covered:i,uncovered:a,total:n}=await async function(e,t,r){let i,a,n;if(r.snapshotAt){const s=(await Li(e,t,r.snapshotAt)).map(Ni);i=s.filter(e=>"requirement"===e.type),a=s.map(e=>({id:e.id,data:e.data})),n=[]}else{const{data:r,error:s}=await e.from("items").select("*").eq("project_id",t).eq("type","requirement").is("deleted_at",null);if(s)throw new Error(s.message);i=r??[];const{data:o,error:d}=await e.from("items").select("id, data").eq("project_id",t).is("deleted_at",null);if(d)throw new Error(d.message);a=o??[];const{data:l,error:c}=await e.from("item_relationships").select("from_id, to_id, type").eq("project_id",t);if(c)throw new Error(c.message);n=l??[]}const s=i.filter(e=>!(r.domain&&!(e.data?.tags?.domains??[]).some(e=>jr(e)===jr(r.domain))||r.level&&dr(e.data?.tags?.level)!==dr(r.level))),o=Wi(a,n),d=new Set;for(const e of o)"verifies"===e.type?d.add(e.to_id):"verified_by"===e.type&&d.add(e.from_id);return{covered:s.filter(e=>d.has(e.id)),uncovered:s.filter(e=>!d.has(e.id)),total:s.length}}(t,e.project_id,{domain:e.domain,level:e.level,snapshotAt:r}),s=a.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level}));return Ei(ai({covered:i.length,uncovered:a.length,total:n,coverage_percent:n>0?Math.round(i.length/n*1e3)/10:100,uncovered_items:s}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}E(ca,{getCoverageHandler:()=>pa});var ma,ua=A({"src/tools/get-coverage.ts"(){Ne(),Bi(),Oi()}}),_a={};async function fa(e){try{const t=await qe();let r;if(e.release_id){const i=await Ci(t,e.release_id);if(!i)return Ii(li(`Release ${e.release_id} not found`));if(i.project_id!==e.project_id)return Ii(li("Release does not belong to this project"));if(null===i.release_version)return Ii(si(`Baseline "${i.name}" is not a formal Release.`,["Promote the baseline to a Release first (Snapshots view in the app)","Use atoms_list_baselines to see which baselines carry a release_version"]));r=i}else{const{data:i,error:a}=await t.from("baselines").select("id, release_version, release_status").eq("project_id",e.project_id).eq("release_status","released").is("deleted_at",null).maybeSingle();if(a)return Ii(ji(a.message));if(!i)return Ii(ma);r=i}const{data:i,error:a}=await t.rpc("verification_readiness",{p_project_id:e.project_id,p_release_id:r.id});if(a)return Ii(vi(a));const{data:n,error:s}=await t.from("verification_status").select("requirement_id, state, stale, points_total, points_run").eq("project_id",e.project_id).eq("release_id",r.id);if(s)return Ii(ji(s.message));const o=(n??[]).slice().sort((e,t)=>e.requirement_id<t.requirement_id?-1:e.requirement_id>t.requirement_id?1:0),d={release:{id:r.id,version:r.release_version,status:r.release_status},readiness:i??{},requirements:o,...0===o.length?{hint:"projection empty; an admin may need to run rebuild_verification_from_runs for this project"}:{}};if(e.include_gaps){const{data:i,error:a}=await t.rpc("verification_notrun_points",{p_project_id:e.project_id,p_release_id:r.id});if(a)return Ii(vi(a));d.gaps=i??[]}return Ei(ai(d))}catch(e){return Ii(vi(e))}}E(_a,{verificationStatusHandler:()=>fa});var ha=A({"src/tools/verification-status.ts"(){Ne(),Bi(),Oi(),ma=si("This project has no current formal Release.",["Promote a locked baseline to a Release first (Snapshots view in the app)","Or pass release_id explicitly to query a specific (e.g. superseded) Release","Use atoms_list_baselines to see which baselines carry a release_version"])}});function ga(e){if(!e||"object"!=typeof e)return{};const t=e,r=t.data,i=r&&"object"==typeof r?{...r}:{};return"title"in t&&(i.title=t.title),"type"in t&&(i.type=t.type),i}var ya=A({"src/core/history.ts"(){}}),wa={};async function va(e){try{const t=await qe(),r=await async function(e,t,r,i){const{data:a,error:n}=await e.from("item_revisions").select("id, revision_number, fields_snapshot, valid_from, changed_by, event_source, event_session_id").eq("item_id",r).eq("project_id",t).order("revision_number",{ascending:!1}).limit(i);if(n)throw new Error(n.message);return a??[]}(t,e.project_id,e.item_id,e.limit);if(r.length>0){const t=function(e){return e.map((t,r)=>{const i=e[r+1]??null,a=1===t.revision_number||null===i,n="system"===t.event_source?"system_normalization":1===t.revision_number?"created":"updated",s=a?["(created)"]:function(e,t){const r=new Set([...Object.keys(e),...Object.keys(t)]),i=[];for(const a of r)JSON.stringify(e[a])!==JSON.stringify(t[a])&&i.push(a);return i}(ga(i?.fields_snapshot),ga(t.fields_snapshot));return{id:t.id,event_type:n,changed_by:t.changed_by??null,changed_at:t.valid_from,actor:"mcp"===t.event_source?"mcp_claude":"user",session_id:t.event_session_id??null,fields_changed:s}})}(r);return Ei(ai(t,ni(t.length,e.limit,0)))}const i=await async function(e,t,r,i){const{data:a,error:n}=await e.from("change_history").select("*").eq("item_id",r).eq("project_id",t).order("changed_at",{ascending:!1}).limit(i);if(n)throw new Error(n.message);return a??[]}(t,e.project_id,e.item_id,e.limit),a=i.map(e=>{const t=[],r=e.old_data,i=e.new_data;if(r&&i){const e=new Set([...Object.keys(r),...Object.keys(i)]);for(const a of e)JSON.stringify(r[a])!==JSON.stringify(i[a])&&t.push(a)}else i?t.push("(created)"):r&&t.push("(deleted)");return{id:e.id,event_type:e.event_type,changed_by:e.changed_by??null,changed_at:e.changed_at,actor:e.actor??"user",session_id:e.session_id??null,fields_changed:t}});return Ei(ai(a,ni(a.length,e.limit,0)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}E(wa,{getHistoryHandler:()=>va});var ba=A({"src/tools/get-history.ts"(){Ne(),Bi(),ya(),Oi()}});function ja(e){return`item_${e.replace(/[^A-Za-z0-9_]/g,"_")||"node"}`}function Sa(e){const t=xa(`${e.id}: ${e.title}`);return"test-case"===e.type?`(["${t}"])`:"note"===e.type?`("${t}")`:`["${t}"]`}function ka(e){const t=ja(e.from_id),r=ja(e.to_id);return"child"===e.type?`${t} --\x3e ${r}`:"verified_by"===e.type?`${t} -.-> ${r}`:"related"===e.type?`${t} -.- ${r}`:null}function xa(e){return e.replace(/"/g,"'").replace(/[[\]{}()]/g,"").substring(0,70)}var Aa=A({"src/core/mermaid.ts"(){}}),Ea={};async function Ia(e){try{if(e.baseline_id&&e.as_of)return Ii(li("baseline_id and as_of are mutually exclusive"));const t=await qe();let r,i,a;if(e.baseline_id){const i=await Ci(t,e.baseline_id);if(!i)return Ii(li(`Baseline ${e.baseline_id} not found`));if(i.project_id!==e.project_id)return Ii(li("Baseline does not belong to this project"));r=i.snapshot_at}else e.as_of&&(r=e.as_of);if(r){i=(await Li(t,e.project_id,r)).map(Ni).map(e=>({id:e.id,title:e.title,type:e.type,data:e.data})),a=Wi(i,[])}else{const{data:r,error:n}=await t.from("items").select("id, title, type, data").eq("project_id",e.project_id).is("deleted_at",null);if(n)throw new Error(n.message);i=r??[];const{data:s,error:o}=await t.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id);if(o)throw new Error(o.message);a=Wi(i,s??[])}return Ei(ai(function(e){const t=e.items,r=e.relationships??[],i=Math.max(0,e.depth);if(0===t.length)return{mermaid:`graph TD\n empty[${xa(e.emptyLabel??"No items in project")}]`,node_count:0,edge_count:0};const a=new Map(t.map(e=>[e.id,e])),n=function(e,t,r,i){if(r)return[r];const a=new Set(t.filter(e=>"child"===e.type).map(e=>e.to_id));return e.filter(e=>!a.has(e.id)).filter(e=>i||"test-case"!==e.type).map(e=>e.id)}(t,r,e.rootItemId,e.includeTests),s=n.map(e=>({id:e,depth:0})),o=new Set,d=new Set,l=[],c=[];for(;s.length>0;){const{id:t,depth:n}=s.shift();if(o.has(t)||n>i)continue;const p=a.get(t);if(p&&(e.includeTests||"test-case"!==p.type)){o.add(t),l.push(` ${ja(t)}${Sa(p)}`);for(const l of r){if(l.from_id!==t)continue;const r=a.get(l.to_id);if(!r)continue;if(!e.includeTests&&"test-case"===r.type)continue;if(n+1>i)continue;const p=ka(l);if(!p)continue;const m=`${l.from_id}:${l.type}:${l.to_id}`;d.has(m)||(d.add(m),c.push(` ${p}`),o.has(l.to_id)||s.push({id:l.to_id,depth:n+1}))}}}return{mermaid:["graph TD",...l,...c].join("\n"),node_count:o.size,edge_count:d.size}}({items:i,relationships:a,rootItemId:e.root_item_id,depth:e.depth,includeTests:e.include_tests,emptyLabel:"No items in project"})))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}E(Ea,{exportMermaidHandler:()=>Ia});var Ra,Oa,qa=A({"src/tools/export-mermaid.ts"(){Ne(),Bi(),Oi(),Aa()}});function za(e){const t=process.env.ATOMS_MCP_REQUIRE_CONFIRMATION?.trim();if(t){const r=t.toLowerCase();if("1"===r||"true"===r||"all"===r)return!0;if(t.split(",").map(e=>e.trim()).filter(Boolean).includes(e))return!0}return!!U().require_confirmation_for.includes(e)}function Ma(e,t=Ra){const r=Date.now()+t,i={...e,exp:r},a=Buffer.from(JSON.stringify(i)).toString("base64url");return{token:`${a}.${S.default.createHmac("sha256",Oa).update(a).digest("base64url")}`,expires_at:new Date(r).toISOString(),expires_in_seconds:Math.round(t/1e3)}}function Ha(e,t){if("string"!=typeof e||!e.includes("."))return{valid:!1,reason:"Token format invalid"};const[r,i]=e.split(".");if(!r||!i)return{valid:!1,reason:"Token format invalid"};const a=S.default.createHmac("sha256",Oa).update(r).digest("base64url"),n=Buffer.from(i,"utf8"),s=Buffer.from(a,"utf8");if(n.length!==s.length||!S.default.timingSafeEqual(n,s))return{valid:!1,reason:"Token signature invalid"};let o;try{o=JSON.parse(Buffer.from(r,"base64url").toString("utf8"))}catch{return{valid:!1,reason:"Token payload malformed"}}return"number"!=typeof o.exp||o.exp<Date.now()?{valid:!1,reason:"Token expired"}:o.tool!==t.tool?{valid:!1,reason:"Token issued for a different tool"}:o.project_id!==t.project_id?{valid:!1,reason:"Token issued for a different project"}:o.target!==t.target?{valid:!1,reason:"Token issued for a different target"}:{valid:!0}}function Da(e){if(Array.isArray(e))return e.map(Da);if(null!==e&&"object"==typeof e){const t={};for(const r of Object.keys(e).sort())t[r]=Da(e[r]);return t}return e}function $a(e){const t=JSON.stringify(Da(e));return S.default.createHash("sha256").update(t).digest("base64url").slice(0,16)}var Pa=A({"src/middleware/confirmation.ts"(){C(),Ra=6e4,Oa=(()=>{const e=process.env.ATOMS_MCP_CONFIRMATION_SECRET;return e&&e.length>=16?e:S.default.randomBytes(32).toString("base64url")})()}}),Ta={};function Ua(e){const t=ii(e??"");return"sub-system"===t?"subsystem":t}async function Ca(e,t){const{data:r}=await e.from("metadata").select("levels").eq("project_id",t).maybeSingle();return{levels:r?.levels??[]}}async function Na(e,t,r){const i=ii(r??""),a=Ua(i);if(!i)return!1;if(!q(U(),"atoms_create_level"))return!1;try{const{levels:r}=await Ca(e,t);if(r.some(e=>Ua(e)===a))return!1;const{error:n}=await e.rpc("mutate_project_levels",{p_project_id:t,p_action:"add",p_values:[i]});return!n}catch{return!1}}async function La(e){try{const t=await qe(),{levels:r}=await Ca(t,e.project_id),{data:i}=await t.from("items").select("data").eq("project_id",e.project_id).is("deleted_at",null),a={};for(const e of i??[]){const t=e.data?.tags,r=Ua(t?.level);r&&(a[r]=(a[r]??0)+1)}return Ei(ai({levels:r.map((e,t)=>({name:e,prefix:`L${t}`,item_count:a[Ua(e)]??0})),total:r.length,note:"L# is the level's position in this list (0-based). Names are user-defined."}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}async function Fa(e){try{const t=await qe();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const{levels:r}=await Ca(t,e.project_id),i=ii(e.name),a=Ua(i);if(!i)return Ii(li("Level name cannot be empty."));if(r.some(e=>Ua(e)===a))return Ii(li(`Level "${i}" already exists in this project.`));const{data:n,error:s}=await t.rpc("mutate_project_levels",{p_project_id:e.project_id,p_action:"add",p_values:[i]});if(s)throw new Error(s.message);const o=(n?.levels??[...r,i]).indexOf(i),d=`L${o>=0?o:r.length}`;return Ei(ai({level:{name:i,prefix:d},message:`Level "${i}" registered as ${d}. Tag items with level="${i}".`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}async function Wa(e){try{const t=await qe();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const r=Ua(ki(e.name));if(!r)return Ii(li("Level name cannot be empty."));const{levels:i}=await Ca(t,e.project_id),a=i.filter(e=>Ua(e)===r),{data:n,error:s}=await t.from("items").select("id, data").eq("project_id",e.project_id).is("deleted_at",null);if(s)throw new Error(s.message);const o=(n??[]).filter(e=>Ua(e.data?.tags?.level)===r).length;if(0===a.length&&0===o)return Ii(oi("Level",e.name));const d=a[0]??e.name;if(o>0)return Ii(li(`Level "${d}" is used by ${o} item${o>1?"s":""}. Retag them with atoms_update_item (set a different level) before deleting.`));if(za("atoms_delete_level")){const t={tool:"atoms_delete_level",project_id:e.project_id,target:r};if(!e.confirmation_token){const e=Ma(t);return Ei(ai({status:"confirmation_required",preview:{level_name:d,items_using_count:0},confirmation_token:e.token,expires_at:e.expires_at,expires_in_seconds:e.expires_in_seconds,message:`Re-call atoms_delete_level with the same project_id, name, and confirmation_token within ${e.expires_in_seconds}s to execute.`}))}const i=Ha(e.confirmation_token,t);if(!i.valid)return Ii(li(`confirmation_token rejected: ${i.reason}`))}const{data:l,error:c}=await t.rpc("mutate_project_levels",{p_project_id:e.project_id,p_action:"remove",p_values:a});if(c)throw new Error(c.message);const p=l?.levels??[],m=lr(i,p);return Ei(ai({level:{name:d},remaining_levels:p,message:`Level "${d}" deleted. Every later level's L# shifts down by one.`+(m?" "+m:"")}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}async function Va(e){try{const t=await qe();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const r=Array.isArray(e.names)?e.names.map(e=>ki(String(e??"")).trim()).filter(e=>e.length>0):[];if(0===r.length)return Ii(li("names must be a non-empty array of the project's existing levels, in the order you want them."));const{levels:i}=await Ca(t,e.project_id),a=new Map;for(const e of i)a.set(Ua(e),e);const n=[],s=new Set,o=`names must list every existing level exactly once. Levels: ${i.length>0?i.join(", "):"(none)"}`;for(const e of r){const t=Ua(e),r=a.get(t);if(!r||s.has(t))return Ii(li(o));s.add(t),n.push(r)}if(n.length!==i.length)return Ii(li(o));const{data:d,error:l}=await t.rpc("mutate_project_levels",{p_project_id:e.project_id,p_action:"reorder",p_values:n});if(l)throw new Error(l.message);const c=d?.levels??n,p=lr(i,c);return Ei(ai({levels:c.map((e,t)=>({name:e,prefix:`L${t}`})),message:"Levels reordered. L# now follows this order everywhere it renders (Strata, sidebar, exports)."+(p?" "+p:"")}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}E(Ta,{createLevelHandler:()=>Fa,deleteLevelHandler:()=>Wa,ensureLevelRegistered:()=>Na,listLevelsHandler:()=>La,reorderLevelsHandler:()=>Va});var Ba,Ja,Qa=A({"src/tools/levels.ts"(){Ne(),Pa(),Oi(),Xr(),P(),C()}});function Ga(e,t){return"requirement"!==t?`req_type applies only to requirements (type='requirement'), not '${t}'.`:Ja.includes(e)?`req_type '${e}' names an analysis row, which is created by atoms_create_analysis, not atoms_create_item / atoms_update_item. Use atoms_create_analysis (variant iso26262 | iso14971 | design | process | software | use).`:Ba.includes(e)?null:`Unknown req_type '${e}'. Allowed: ${Ba.join(", ")}.`}var Ka,Ya,Za=A({"src/core/req-type.ts"(){Ba=["functional","interface","performance","safety"],Ja=["safety-goal","failure-mode"]}}),Xa=A({"src/core/project-field-counts.ts"(){}});function en(e){if(!Array.isArray(e))return;const t=new Set(e.filter(e=>"string"==typeof e&&Ya.has(e)));return 0!==t.size?Ka.filter(e=>t.has(e)):void 0}var tn,rn,an=A({"src/core/field-scope.ts"(){Ka=["requirement","test-case","note","table"],Ya=new Set(Ka)}}),nn={};async function sn(e,t){const{data:r,error:i}=await e.from("metadata").select("fields").eq("project_id",t).maybeSingle();if(i)throw i;return on(r?.fields)}function on(e){if(!Array.isArray(e))return[];const t=[],r=new Set;for(const i of e){if(!i||"object"!=typeof i)continue;const e=i,a="string"==typeof e.name?e.name.trim():"";if(!a)continue;const n=a.toLowerCase();if(r.has(n))continue;r.add(n);const s=void 0===e.type?"text":"string"==typeof e.type&&rn.has(e.type)?e.type:"text",o=Array.isArray(e.options)?hn(e.options):[],d=en(e.appliesTo??e.applies_to);t.push({name:a,type:s,..."select"===s?{options:o}:{},...d?{appliesTo:d}:{}})}return t}function dn(e,t){const r=e.name.trim();if(!r)return"Field name cannot be empty.";if(r.length>60)return"Field name must be 60 characters or fewer.";if(!rn.has(e.type))return"Field type is not supported.";const i=t.find(e=>e.name.toLowerCase()===r.toLowerCase());if(i)return`Field "${i.name}" already exists.`;if("select"===e.type){const t=hn(e.options??[]);if(0===t.length)return"Select fields need at least one option.";if(t.length>100)return"Select fields support at most 100 options.";if(t.some(e=>e.length>120))return"Select options must be 120 characters or fewer."}return null}function ln(e,t){const r=new Set(e.map(e=>e.name.toLowerCase()));return Object.keys(t).filter(e=>!r.has(e.toLowerCase()))}function cn(e,t){const r=new Map(e.map(e=>[e.name.toLowerCase(),e.name])),i={};for(const[e,a]of Object.entries(t))i[r.get(e.toLowerCase())??e]=a;return i}function pn(e,t,r=new Set){const i=ln(e,t);if(i.length>0)return{error:`Unknown field${i.length>1?"s":""}: ${i.join(", ")}. Defined: ${e.length>0?e.map(e=>e.name).join(", "):"(none)"}. Define fields first with atoms_create_field.`};const a={},n=new Map(e.map(e=>[e.name.toLowerCase(),e]));for(const[e,i]of Object.entries(t)){const t=n.get(e.toLowerCase());if(!t)continue;const s=mn(t,i,r);if(s.error)return{error:s.error};s.value&&(a[t.name]=s.value)}return{fields:a}}function mn(e,t,r){const i=t.trim();if(!i)return{value:""};if(i.length>1e4)return{error:`${e.name} is too long.`};switch(e.type){case"text":case"long":return{value:i};case"bool":{const t=i.toLowerCase();return"yes"===t?{value:"yes"}:"no"===t?{value:"no"}:{error:`${e.name} must be yes or no.`}}case"date":return function(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e))return!1;const t=new Date(`${e}T00:00:00.000Z`);return!Number.isNaN(t.getTime())&&t.toISOString().slice(0,10)===e}(i)?{value:i}:{error:`${e.name} must be an ISO date.`};case"person":return fn(i)&&r.has(i)?{value:i}:{error:`${e.name} must be an active project member ID.`};case"select":return(e.options??[]).includes(i)?{value:i}:{error:`${e.name} must be one of: ${(e.options??[]).join(", ")}.`};case"number":return Number.isFinite(Number(i))?{value:i}:{error:`${e.name} must be numeric.`};case"link":{const t=function(e){if(!e)return{label:"",url:""};try{const t=JSON.parse(e);if(t&&"object"==typeof t&&"string"==typeof t.url){const e=t.label;return{label:"string"==typeof e?e:"",url:t.url}}}catch{}return{label:"",url:e}}(i);return function(e){try{const t=new URL(e);return"http:"===t.protocol||"https:"===t.protocol}catch{return!1}}(t.url)?{value:un(t.label,t.url)}:{error:`${e.name} must be an http(s) URL.`}}}}function un(e,t){const r=t.trim();return r?JSON.stringify({label:e.trim(),url:r}):""}async function _n(e,t,r,i){const a=new Map(r.map(e=>[e.name.toLowerCase(),e])),n=new Map;for(const[e,t]of Object.entries(i)){const r=a.get(e.toLowerCase());"person"===r?.type&&t.trim()&&n.set(t.trim(),r)}const s=new Set;for(const[r,i]of n){if(!fn(r))return{error:`${i.name} must be an active project member ID.`};const{data:a,error:n}=await e.rpc("is_active_project_member_for_field",{p_project_id:t,p_person_user_id:r,p_actor_user_id:De()});if(n)return{error:n.message};!0===a&&s.add(r)}return pn(r,i,s)}function fn(e){return/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(e)}function hn(e){const t=[],r=new Set;for(const i of e){if("string"!=typeof i)continue;const e=i.trim();if(!e)continue;const a=e.toLowerCase();r.has(a)||(r.add(a),t.push(e))}return t}async function gn(e){try{const t=await qe(),r=await sn(t,e.project_id),i=await async function(e,t,r,i=500){const a=Object.fromEntries(r.map(e=>[e.name,0])),n=new Map(r.map(e=>[e.name.toLowerCase(),e.name]));let s=0;for(;;){const{data:r,error:o}=await e.from("items").select("data").eq("project_id",t).is("deleted_at",null).order("id",{ascending:!0}).range(s,s+i-1);if(o)throw o;const d=r??[];for(const e of d){const t=e.data?.fields;if(t&&"object"==typeof t&&!Array.isArray(t))for(const[e,r]of Object.entries(t)){const t=n.get(e.toLowerCase());t&&"string"==typeof r&&r.length>0&&(a[t]+=1)}}if(d.length<i)return a;s+=d.length}}(t,e.project_id,r);return Ei(ai({fields:r.map(e=>({name:e.name,type:e.type,options:e.options??[],...e.appliesTo?{appliesTo:e.appliesTo}:{},item_count:i[e.name]??0})),total:r.length}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(vi(e))}}async function yn(e){try{const t=await qe();try{await Te(t,e.project_id)}catch(e){if(e instanceof Error&&"PROJECT_FIELD_ADMIN_OR_OWNER_REQUIRED"===e.message)return Ii(di("Project admin or owner"));throw e}const{data:r}=await t.from("metadata").select("fields").eq("project_id",e.project_id).maybeSingle(),i=on(r?.fields),a=en(e.appliesTo),n={name:e.name.trim(),type:e.type??"text",..."select"===e.type?{options:hn(e.options??[])}:{},...a?{appliesTo:a}:{}},s=dn(n,i);if(s)return Ii(li(s));const o=function(e,t){return"organization"!==e?null:t&&0!==t.length?t.length>=Ka.length?null:'An organization-scoped field cannot be restricted to specific item types (appliesTo is a project-scoped-field feature only; the org field library has no per-type scope column). Omit appliesTo, list all item types, or use scope="project".':null}(e.scope,a);if(o)return Ii(li(o));const{data:d,error:l}="organization"===e.scope?await t.rpc("create_org_project_field_definition",{p_project_id:e.project_id,p_field:n,p_actor_user_id:De()}):await t.rpc("mutate_project_field_definition",{p_project_id:e.project_id,p_operation:"create",p_field:n,p_field_name:null,p_actor_user_id:De()});if(l)throw l;return Ei(ai({field:d?.field??n,..."organization"===e.scope?{created_projects:d?.created_projects??[],skipped_projects:d?.skipped_projects??[]}:{},message:`Field "${n.name}" defined. Set values with atoms_update_item fields={"${n.name}": "<value>"}.`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(vi(e))}}async function wn(e){try{const t=await qe();try{await Te(t,e.project_id)}catch(e){if(e instanceof Error&&"PROJECT_FIELD_ADMIN_OR_OWNER_REQUIRED"===e.message)return Ii(di("Project admin or owner"));throw e}const r=e.name.trim(),{data:i,error:a}=await t.rpc("mutate_project_field_definition",{p_project_id:e.project_id,p_operation:"delete",p_field:null,p_field_name:r,p_actor_user_id:De()});if(a)throw a;return Ei(ai({deleted:i?.deleted??r}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(vi(e))}}async function vn(e){try{const t=await qe();try{await Te(t,e.project_id)}catch(e){if(e instanceof Error&&"PROJECT_FIELD_ADMIN_OR_OWNER_REQUIRED"===e.message)return Ii(di("Project admin or owner"));throw e}const r=Array.isArray(e.names)?e.names.map(e=>String(e??"").trim()).filter(e=>e.length>0):[];if(0===r.length)return Ii(li("names must be a non-empty array of existing field names, in the order you want them"));const i=await sn(t,e.project_id),a=i.map(e=>e.name.toLowerCase()).sort(),n=r.map(e=>e.toLowerCase()).sort();if(a.length!==n.length||a.some((e,t)=>e!==n[t]))return Ii(li(`names must list every existing field exactly once. Defined: ${i.length>0?i.map(e=>e.name).join(", "):"(none)"}`));const{data:s,error:o}=await t.rpc("mutate_project_field_definition",{p_project_id:e.project_id,p_operation:"reorder",p_field:r,p_field_name:null,p_actor_user_id:De()});if(o)throw o;return Ei(ai({fields:on(s?.fields).map(e=>e.name)}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(vi(e))}}E(nn,{PROJECT_FIELD_TYPES:()=>tn,canonicalizeFieldKeys:()=>cn,createFieldHandler:()=>yn,deleteFieldHandler:()=>wn,findUnknownFieldNames:()=>ln,getProjectFields:()=>sn,listFieldsHandler:()=>gn,normalizeProjectFields:()=>on,reorderFieldsHandler:()=>vn,validateAndCanonicalizeFieldValues:()=>pn,validateProjectFieldDefinition:()=>dn,validateProjectFieldValuesForWrite:()=>_n});var bn=A({"src/tools/fields.ts"(){Ne(),Oi(),Xa(),an(),tn=["text","long","bool","date","person","select","number","link"],rn=new Set(tn)}}),jn={};async function Sn(e){try{const t=await qe(),r=De();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}e.title=ki(e.title),void 0!==e.body&&(e.body=ki(e.body)),void 0!==e.summary&&(e.summary=ki(e.summary)),void 0!==e.level&&(e.level=ki(e.level)),e.domains&&(e.domains=e.domains.map(e=>ki(e)));const i=(e.domains??[]).map(ii).filter(e=>e.length>0),a=void 0!==e.level?ii(e.level):void 0;if(i.length>0){const{data:r,error:a}=await t.from("metadata").select("domains, function_tags").eq("project_id",e.project_id).maybeSingle();if(a)throw new Error(`metadata read failed: ${a.message}`);const n=r?.domains??[],s=Wr(i,Fr(r?.function_tags),ii);if(s.length>0)return Ii(li(Vr(s)));if(n.length>0){const e=new Set(n.map(ii).filter(e=>e.length>0)),t=i.filter(t=>!e.has(t));if(t.length>0)return Ii(li(`Unknown domain${t.length>1?"s":""}: ${t.join(", ")}. Registered: ${n.join(", ")}. Add new domains via Taxonomy Management in the ATOMS web app first.`))}}let n;if(e.fields&&Object.keys(e.fields).length>0){const{getProjectFields:r,validateProjectFieldValuesForWrite:i}=await Promise.resolve().then(()=>(bn(),nn)),a=await r(t,e.project_id),s=await i(t,e.project_id,a,e.fields);if(s.error)return Ii(li(s.error));n=s.fields}if(void 0!==e.req_type){const t=Ga(e.req_type,e.type);if(t)return Ii(li(t))}const s=await Vi(t,e.project_id,e.type),o=(new Date).toISOString(),d={id:s,type:e.type,title:e.title,summary:e.summary,body:e.body,...e.req_type?{req_type:e.req_type}:{},tags:{domains:i,level:a??""},ownership:{primary:null,additional:[]},relationships:{parents:e.parent_ids??[],children:[],related:[],verifies:[],verified_by:[]},links:[],...n?{fields:n}:{},metadata:{created_at:o,created_by:r,updated_at:o,updated_by:r}},{error:l}=await t.rpc("save_item_with_revision",{p_item_id:s,p_project_id:e.project_id,p_type:e.type,p_title:e.title,p_data:d,p_user_id:r,p_event_source:"mcp",p_event_session_id:Po()??null,p_diff_summary:null});if(l)throw new Error(l.message);if(a&&await Na(t,e.project_id,a),e.parent_ids&&e.parent_ids.length>0)for(const i of e.parent_ids){const{error:a}=await t.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:s,p_to_id:i,p_type:"parent",p_action:"add",p_user_id:r,p_event_source:"mcp"});a&&process.stderr.write(`[atoms-mcp] Warning: parent link sync failed for ${i}: ${a.message}\n`)}return await t.from("change_history").insert({item_id:s,project_id:e.project_id,changed_by:r,event_type:"created",old_data:null,new_data:d,actor:To(),session_id:Po()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),!1===e.echo?Ei(ai({id:s,type:e.type})):Ei(ai({id:s,type:e.type,title:e.title,summary:e.summary??"",body:e.body??"",...e.req_type?{req_type:e.req_type}:{},domains:i,level:a??"",project_id:e.project_id}))}catch(e){return Ii(vi(e))}}E(jn,{createItemHandler:()=>Sn});var kn=A({"src/tools/create-item.ts"(){Ne(),Bi(),Co(),Oi(),Qa(),Xr(),Za()}});function xn(e,t){const r=function(e){return`SG-${e}`}(t);return{id:e,outcome:"captured_as_change",change:{id:r,sg_number:t,status:"open"},message:`Captured as change ${r}, awaiting approval. This requirement stays at its approved revision until a human approver approves the change; no MCP tool can approve, dismiss, or apply it.`}}function An(e){return{id:e,outcome:"under_review",message:"This requirement is under change control. Your edit was submitted; if it changed the requirement's meaning it is now a change awaiting a human approver, otherwise it wrote through live. Reload the item to confirm. No MCP tool can approve, dismiss, or apply a change."}}var En=A({"src/core/change-control.ts"(){}}),In={};async function Rn(e){try{const t=await qe(),r=De();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const i=async(i,a,n,s)=>{const o=await Ti(t,e.project_id,i);if(!o)return{updated_fields:[],error:`Item "${i}" not found`};const d="approved"===o.approval_state;void 0!==a.title&&(a.title=ki(a.title)),void 0!==a.body&&(a.body=ki(a.body)),void 0!==a.summary&&(a.summary=ki(a.summary)),void 0!==a.level&&(a.level=ki(a.level)),void 0!==a.domains&&(a.domains=a.domains.map(e=>ki(e)));const l=void 0!==a.domains?a.domains.map(ii).filter(e=>e.length>0):void 0,c=void 0!==a.level?ii(a.level):void 0;if(void 0!==l&&l.length>0){const{data:r,error:i}=await t.from("metadata").select("domains, function_tags").eq("project_id",e.project_id).maybeSingle();if(i)throw new Error(`metadata read failed: ${i.message}`);const a=r?.domains??[],n=Wr(l,Fr(r?.function_tags),ii);if(n.length>0)return{updated_fields:[],error:Vr(n)};if(a.length>0){const e=new Set(a.map(ii).filter(e=>e.length>0)),t=l.filter(t=>!e.has(t));if(t.length>0)return{updated_fields:[],error:`Unknown domains: ${t.join(", ")}`}}}const p={...o.data},m={...o.data},u=[],_=(e,t)=>JSON.stringify(e)!==JSON.stringify(t);if(void 0!==a.title&&_(o.data.title,a.title)&&(m.title=a.title,u.push("title")),void 0!==a.body&&_(o.data.body,a.body)&&(m.body=a.body,u.push("body")),void 0!==a.summary&&_(o.data.summary,a.summary)&&(m.summary=a.summary,u.push("summary")),void 0!==l&&_(o.data.tags?.domains??[],l)&&(m.tags={...m.tags,domains:l},u.push("domains")),void 0!==c&&_(o.data.tags?.level,c)&&(m.tags={...m.tags,level:c},u.push("level")),void 0!==a.status&&_(o.data.status,a.status)&&(m.status=a.status,u.push("status")),void 0!==a.req_type){const e=Ga(a.req_type,o.type);if(e)return{updated_fields:[],error:e};_(o.data.req_type,a.req_type)&&(m.req_type=a.req_type,u.push("req_type"))}if(void 0!==a.steps&&_(o.data.steps,a.steps)&&(m.steps=a.steps,u.push("steps")),void 0!==a.params&&_(o.data.params,a.params)&&(m.params=a.params,u.push("params")),void 0!==a.fields&&Object.keys(a.fields).length>0){const{getProjectFields:r,validateProjectFieldValuesForWrite:i,canonicalizeFieldKeys:n}=await Promise.resolve().then(()=>(bn(),nn)),s=await r(t,e.project_id),d=await i(t,e.project_id,s,a.fields);if(d.error)return{updated_fields:[],error:d.error};const l=n(s,o.data.fields??{});for(const[e,t]of Object.entries({...n(s,a.fields),...d.fields??{}}))""===t?delete l[e]:l[e]=t;_(o.data.fields??{},l)&&(m.fields=l,u.push("fields"))}if(0===u.length)return{updated_fields:[],updatedData:o.data,itemType:o.type};m.metadata={...m.metadata,updated_at:(new Date).toISOString(),updated_by:r};const f=a.title??o.title,{data:h,error:g}=await t.rpc("save_item_with_revision",{p_item_id:i,p_project_id:e.project_id,p_type:o.type,p_title:f,p_data:m,p_user_id:r,p_event_source:"mcp",p_event_session_id:Po()??null,p_diff_summary:null,p_expected_updated_at:a.expected_updated_at??o.updated_at});if(g)return{updated_fields:[],error:g};if(!h)return{updated_fields:[],error:`Update silently failed for "${i}" — RPC returned no row. Likely RLS policy denied the write; verify your role and project access.`};u.includes("level")&&c&&await Na(t,e.project_id,c);const y={item_id:i,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:p,new_data:m,actor:To(),session_id:Po()};if(n&&(y.batch_id=n),s&&(y.reason=s),await t.from("change_history").insert(y).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),d&&function(e){return e.some(e=>"title"===e||"body"===e||"type"===e)}(u)){const r=await async function(e,t,r){const{data:i,error:a}=await e.from("item_suggestions").select("sg_number").eq("project_id",t).eq("item_id",r).eq("status","open").maybeSingle();return a||!i?null:"number"==typeof i.sg_number?i.sg_number:null}(t,e.project_id,i);return null!==r?{updated_fields:u,updatedData:m,itemType:o.type,capturedSg:r}:{updated_fields:u,updatedData:m,itemType:o.type,capturedUnknown:!0}}return{updated_fields:u,updatedData:m,itemType:o.type}};if(e.updates&&Array.isArray(e.updates)){const t=crypto.randomUUID(),r=!0===e.continue_on_error,a=[],n=[];for(const s of e.updates){const{updated_fields:o,capturedSg:d,capturedUnknown:l,error:c}=await i(s.item_id,s,t,e.reason);if(c){if(bi(c))return Ii(vi(c));if(!r){const e=c instanceof Error?c.message:String("object"==typeof c&&c&&"message"in c?c.message??"":c);return Ii(li(`Batch stopped at "${s.item_id}": ${e}. ${a.length} item(s) already updated.`))}n.push({item_id:s.item_id,error:String(c)})}else void 0!==d?a.push(xn(s.item_id,d)):l?a.push(An(s.item_id)):a.push({id:s.item_id,updated_fields:o})}return Ei(ai({batch_id:t,updated:a.length,results:!1===e.echo?void 0:a,errors:n}))}if(!e.item_id)return Ii(li("Provide either item_id (single) or updates[] (array form)."));const{updated_fields:a,updatedData:n,itemType:s,capturedSg:o,capturedUnknown:d,error:l}=await i(e.item_id,{title:e.title,body:e.body,summary:e.summary,domains:e.domains,level:e.level,status:e.status,req_type:e.req_type,fields:e.fields,steps:e.steps,params:e.params,expected_updated_at:e.expected_updated_at},void 0,e.reason);if(l){const t=l instanceof Error?l.message:String("object"==typeof l&&l&&"message"in l?l.message??"":l);return t.includes("not found")?Ii(oi("Item",e.item_id)):t.includes("Unknown domain")||t.includes("Unknown field")||t.includes("Function-flagged tag")||t.includes("req_type")?Ii(li(t)):fi(t)||bi(l)?Ii(vi(l)):Ii(ji(t))}if(void 0!==o)return Ei(ai(xn(e.item_id,o)));if(d)return Ei(ai(An(e.item_id)));if(!1===e.echo)return Ei(ai({id:e.item_id,updated_fields:a}));const c=n??{};return Ei(ai({id:e.item_id,type:s,title:c.title??"",summary:c.summary??"",body:c.body??"",domains:c.tags?.domains??[],level:c.tags?.level??"",...c.req_type?{req_type:c.req_type}:{},updated_fields:a}))}catch(e){return Ii(vi(e))}}E(In,{updateItemHandler:()=>Rn});var On,qn=A({"src/tools/update-item.ts"(){Ne(),Bi(),Co(),Za(),Oi(),Qa(),En(),Xr()}}),zn={};async function Mn(e){try{const t=await qe(),r=De();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const i=await async function(e,t,r){const{data:i,error:a}=await e.from("items").select("*").eq("id",r).eq("project_id",t).maybeSingle();if(a)throw new Error(a.message);return i}(t,e.project_id,e.item_id);if(!i)return Ii(oi("Item",e.item_id));if(i.deleted_at)return Ii(li(`Item ${e.item_id} is already deleted`));if(za(On)){const t={tool:On,project_id:e.project_id,target:e.item_id};if(!e.confirmation_token){const r=Ma(t);return Ei(ai({status:"confirmation_required",preview:{id:e.item_id,title:i.title,type:i.type,would_soft_delete:!0},confirmation_token:r.token,expires_at:r.expires_at,expires_in_seconds:r.expires_in_seconds,message:`Re-call atoms_delete_item with the same project_id, item_id, and confirmation_token within ${r.expires_in_seconds}s to execute.`}))}const r=Ha(e.confirmation_token,t);if(!r.valid)return Ii(li(`confirmation_token rejected: ${r.reason}`))}const{error:a}=await t.rpc("soft_delete_item_with_revision",{p_item_id:e.item_id,p_project_id:e.project_id,p_user_id:r,p_expected_updated_at:i.updated_at,p_event_source:"mcp",p_actor:To(),p_session_id:Po()});if(a)throw new Error(a.message);return Ei(ai({id:e.item_id,title:i.title,type:i.type,deleted:!0,message:`Item ${e.item_id} soft-deleted. It can still be found in audit logs.`}))}catch(e){return Ii(vi(e))}}E(zn,{deleteItemHandler:()=>Mn});var Hn=A({"src/tools/delete-item.ts"(){Ne(),Bi(),Co(),Pa(),Oi(),On="atoms_delete_item"}}),Dn={};async function $n(e){try{const t=await qe(),r=De();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const{data:i,error:a}=await t.from("items").select("*").eq("id",e.item_id).eq("project_id",e.project_id).maybeSingle();if(a)throw new Error(a.message);if(!i)return Ii(oi("Item",e.item_id));if(!i.deleted_at)return Ii(si(`Item ${e.item_id} is not deleted and cannot be restored.`,["Use atoms_get_item to view the current item state","Use atoms_delete_item first if you intended to delete it"]));const{error:n}=await t.rpc("save_item_with_revision",{p_item_id:e.item_id,p_project_id:e.project_id,p_type:i.type,p_title:i.title,p_data:i.data,p_user_id:r,p_event_source:"restore",p_event_session_id:Po()??null,p_diff_summary:{changed_fields:["deleted_at"],restored:!0},p_expected_updated_at:i.updated_at});if(n)throw new Error(n.message);const{error:s}=await t.rpc("repair_live_item_relationships_from_jsonb",{p_project_id:e.project_id,p_item_id:e.item_id});if(s)throw new Error(`Relationship repair failed: ${s.message}`);return await t.from("change_history").insert({item_id:e.item_id,project_id:e.project_id,changed_by:r,event_type:"restored",old_data:null,new_data:i.data,actor:To(),session_id:Po()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),Ei(ai({id:e.item_id,title:i.title,type:i.type,restored:!0,message:`Item ${e.item_id} restored successfully.`}))}catch(e){return Ii(vi(e))}}E(Dn,{restoreItemHandler:()=>$n});var Pn=A({"src/tools/restore-item.ts"(){Ne(),Co(),Oi()}}),Tn={};async function Un(e){try{const t=await qe(),r=De();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const i=async(i,a,n)=>{const s=await Ti(t,e.project_id,i.from_id);if(!s)throw new Error(`Item "${i.from_id}" not found`);const o=await Ti(t,e.project_id,i.to_id);if(!o)throw new Error(`Item "${i.to_id}" not found`);const{error:d}=await t.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:i.from_id,p_to_id:i.to_id,p_type:i.type,p_action:i.action,p_user_id:r,p_event_source:"mcp",p_expected_from_updated_at:i.expected_from_updated_at??s.updated_at,p_expected_to_updated_at:i.expected_to_updated_at??o.updated_at});if(d)throw new Error(d.message);const l={item_id:i.from_id,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:null,new_data:{relationship_change:{action:i.action,type:i.type,from:i.from_id,to:i.to_id}},actor:To(),session_id:Po()};a&&(l.batch_id=a),n&&(l.reason=n),await t.from("change_history").insert(l).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)})};if(e.operations&&Array.isArray(e.operations)){for(const t of e.operations)if(t.from_id===t.to_id)return Ii(li(`Self-reference in batch: "${t.from_id}"`));const r=[...new Set(e.operations.flatMap(e=>[e.from_id,e.to_id]))],{data:a}=await t.from("items").select("id").eq("project_id",e.project_id).is("deleted_at",null).in("id",r),n=new Set((a??[]).map(e=>e.id)),s=r.filter(e=>!n.has(e));if(s.length>0)return Ii(oi("Items",s.join(", ")));const{data:o}=await t.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id),d=new Map;for(const e of o??[])"parent"===e.type&&(d.has(e.from_id)||d.set(e.from_id,new Set),d.get(e.from_id).add(e.to_id));for(const t of e.operations){if("parent"!==t.type&&"child"!==t.type)continue;const[e,r]="parent"===t.type?[t.from_id,t.to_id]:[t.to_id,t.from_id];"remove"===t.action?d.get(e)?.delete(r):(d.has(e)||d.set(e,new Set),d.get(e).add(r))}const l=0,c=1,p=2,m=new Map,u=new Set;for(const[e,t]of d){u.add(e);for(const e of t)u.add(e)}const _=e=>{if((m.get(e)??l)!==l)return null;const t=[],r=[],i=e=>{m.set(e,c),r.push(e),t.push({node:e,parents:[...d.get(e)??[]],idx:0})};for(i(e);t.length>0;){const e=t[t.length-1];if(e.idx>=e.parents.length){m.set(e.node,p),r.pop(),t.pop();continue}const a=e.parents[e.idx];e.idx++;const n=m.get(a)??l;if(n===c){const e=r.indexOf(a);return[...r.slice(e),a]}n!==p&&i(a)}return null};for(const e of u){const t=_(e);if(t)return Ii(li(`Batch would create a cycle: ${t.join(" -> ")}. No changes applied.`))}const f=crypto.randomUUID(),h=e.operations.filter(e=>"remove"===e.action),g=e.operations.filter(e=>"add"===e.action);for(const t of[...h,...g])await i(t,f,e.reason);return Ei(ai({batch_id:f,processed:e.operations.length,operations:!1===e.echo?void 0:e.operations}))}return e.from_id&&e.to_id&&e.type&&e.action?e.from_id===e.to_id?Ii(li("Cannot create a relationship between an item and itself")):(await i({action:e.action,from_id:e.from_id,to_id:e.to_id,type:e.type,expected_from_updated_at:e.expected_from_updated_at,expected_to_updated_at:e.expected_to_updated_at},void 0,e.reason),!1===e.echo?Ei(ai({action:e.action,from_id:e.from_id,to_id:e.to_id})):Ei(ai({action:e.action,type:e.type,from_id:e.from_id,to_id:e.to_id,message:`Relationship ${"add"===e.action?"added":"removed"}: ${e.from_id} --[${e.type}]--\x3e ${e.to_id}`}))):Ii(li("Provide (from_id, to_id, type, action) for single-op or operations[] for batch."))}catch(e){return Ii(vi(e))}}E(Tn,{linkItemsHandler:()=>Un});var Cn=A({"src/tools/link-items.ts"(){Ne(),Bi(),Co(),Oi()}}),Nn=A({"src/core/bulk-parent-links.ts"(){}}),Ln={};function Fn(e){return new Set(["22023","23503","23505","23514"]).has(e.code??"")}async function Wn(e){try{if(!e.items||0===e.items.length)return Ii(li("items array must contain at least 1 item"));if(e.items.length>100)return Ii(li(`Maximum 100 items per call (received ${e.items.length}). Split into multiple calls.`));const t=await qe(),r=De();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const i=await async function(e,t,r){const{data:i,error:a}=await e.from("metadata").select("domains, function_tags").eq("project_id",t).maybeSingle();if(a)throw new Error(`metadata read failed: ${a.message}`);const n=i?.domains??[],s=Fr(i?.function_tags),o=n.length>0?new Set(n.map(ii).filter(e=>e.length>0)):null,d=$a(n.map(ii).filter(e=>e.length>0).sort()),l=[],c=new Set,p={};for(let e=0;e<r.length;e++){const t=r[e];p[t.type]=(p[t.type]??0)+1;const i=(t.domains??[]).map(ii).filter(e=>e.length>0);if(0===i.length)continue;const a=Wr(i,s,ii);if(a.length>0){c.add(e),l.push({index:e,title:t.title,error:Vr(a)});continue}if(!o)continue;const d=i.filter(e=>!o.has(e));d.length>0&&(c.add(e),l.push({index:e,title:t.title,error:`Unknown domain${d.length>1?"s":""}: ${d.join(", ")}. Registered: ${n.join(", ")}.`}))}return{registeredDomains:n,registeredDomainHash:d,invalidDomainIndexes:c,errors:l,typeBreakdown:p}}(t,e.project_id,e.items);if(za("atoms_bulk_import")){const t={tool:"atoms_bulk_import",project_id:e.project_id,target:$a({items:e.items,registered_domain_hash:i.registeredDomainHash})};if(!e.confirmation_token){const r=Ma(t);return Ei(ai({status:"confirmation_required",preview:{total_items:e.items.length,by_type:i.typeBreakdown,sample_titles:e.items.slice(0,5).map(e=>e.title),would_fail:i.errors.length>0,errors:i.errors,registered_domain_hash:i.registeredDomainHash},confirmation_token:r.token,expires_at:r.expires_at,expires_in_seconds:r.expires_in_seconds,message:`Re-call atoms_bulk_import with the same project_id, items array, and confirmation_token within ${r.expires_in_seconds}s to execute. Changing items or project domains will invalidate the token.`}))}const r=Ha(e.confirmation_token,t);if(!r.valid)return Ii(li(`confirmation_token rejected: ${r.reason}`))}const a=(new Date).toISOString(),n=Po(),s=[],o=[...i.errors],d=[],l=new Map,c=async r=>{let i=l.get(r);if(!i){const a=await Vi(t,e.project_id,r),n=/^([A-Z]+)-(\d+)$/.exec(a);if(!n)throw new Error(`Unexpected item id format from next_item_id: ${a}`);i={prefix:n[1],next:Number(n[2])},l.set(r,i)}const a=`${i.prefix}-${String(i.next).padStart(3,"0")}`;return i.next+=1,a};for(let t=0;t<e.items.length;t++){const r=e.items[t];if(i.invalidDomainIndexes.has(t))continue;const a=(r.domains??[]).map(ii).filter(e=>e.length>0),n=void 0!==r.level?ii(r.level):void 0;try{const e=await c(r.type);d.push({itemId:e,input:r,sanitizedDomains:a,sanitizedLevel:n,index:t})}catch(e){o.push({index:t,title:r.title,error:`ID generation failed: ${e instanceof Error?e.message:String(e)}`})}}if(0===d.length)return Ei(ai({created:0,items:[],errors:o}));const p=[],m=new Map;for(const{itemId:t,input:i,sanitizedDomains:n,sanitizedLevel:s}of d){const o={id:t,type:i.type,title:i.title,summary:i.summary,body:i.body,tags:{domains:n,level:s??""},ownership:{primary:null,additional:[]},relationships:{parents:i.parent_id?[i.parent_id]:[],children:[],related:[]},links:[],metadata:{created_at:a,created_by:r,updated_at:a,updated_by:r}};m.set(t,o),p.push({id:t,project_id:e.project_id,type:i.type,title:i.title,data:o,created_by:r,updated_by:r})}const u=p.map(e=>({id:e.id,type:e.type,title:e.title,data:e.data})),{error:_}=await t.rpc("bulk_save_items_with_revisions",{p_project_id:e.project_id,p_items:u,p_user_id:r,p_event_source:"mcp"});if(_&&Fn(_))for(const{itemId:i,input:a,index:l}of d){const d=p.find(e=>e.id===i);if(!d)continue;const{error:c}=await t.rpc("save_item_with_revision",{p_item_id:d.id,p_project_id:e.project_id,p_type:d.type,p_title:d.title,p_data:d.data,p_user_id:r,p_event_source:"mcp",p_event_session_id:n,p_diff_summary:null});c?o.push({index:l,title:a.title,error:c.message}):s.push({id:i,title:a.title,type:a.type})}else{if(_)throw _;for(const{itemId:e,input:t}of d)s.push({id:e,title:t.title,type:t.type})}const f=d.filter(({input:e})=>e.parent_id).filter(({itemId:e})=>s.some(t=>t.id===e)).map(({itemId:t,input:r})=>({from_id:t,to_id:r.parent_id,type:"parent",project_id:e.project_id}));await async function(e){for(const t of e.links){const{error:r}=await e.client.rpc("apply_link_change",{p_project_id:e.projectId,p_from_id:t.from,p_to_id:t.to,p_type:"parent",p_action:"add",p_user_id:e.userId,p_event_source:"mcp"});r&&e.warn(`bulk parent link sync failed for ${t.to}: ${r.message??"unknown error"}`)}}({client:t,projectId:e.project_id,links:f.map(e=>({from:e.from_id,to:e.to_id})),userId:r,warn:e=>process.stderr.write(`[atoms-mcp] Warning: ${e}\n`)});const h=s.map(t=>({item_id:t.id,project_id:e.project_id,changed_by:r,event_type:"created",old_data:null,new_data:m.get(t.id)??null,actor:To(),session_id:n}));h.length>0&&await t.from("change_history").insert(h).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: bulk change_history log failed: ${e.message}\n`)});const g=new Set(s.map(e=>e.id)),y=new Set(d.filter(({itemId:e,sanitizedLevel:t})=>t&&g.has(e)).map(({sanitizedLevel:e})=>e));for(const r of y)await Na(t,e.project_id,r);return Ei(ai({created:s.length,items:s,errors:o.length>0?o:[],project_id:e.project_id}))}catch(e){return Ii(vi(e))}}E(Ln,{bulkImportHandler:()=>Wn,canRetryBulkImportPerItem:()=>Fn});var Vn=A({"src/tools/bulk-import.ts"(){Ne(),Bi(),Co(),Pa(),Oi(),Nn(),Qa(),Xr()}}),Bn={};async function Jn(e){try{const t=await qe(),r=De(),i=$e();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const a=await Ti(t,e.project_id,e.item_id);if(!a)return Ii(oi("Item",e.item_id));if("test-case"!==a.type)return Ii(li(`Item '${e.item_id}' is a ${a.type}, not a test-case`));const n=(e.asset??"").trim(),s=(new Date).toISOString(),{data:o,error:d}=await t.rpc("record_test_results_bulk",{p_project_id:e.project_id,p_item_id:e.item_id,p_run_by:i||r,p_actor_user_id:r,p_results:[{result:e.result,note:e.note,asset:n||void 0,date:s,point:e.point,release_id:e.release_id,build:e.build,environment:e.environment,tooling:e.tooling,steps_rev:e.steps_rev,step_results:e.step_results}]});if(d)throw new Error(d.message);const l=Array.isArray(o)?o[0]:o?.[0];await t.from("change_history").insert({item_id:e.item_id,project_id:e.project_id,changed_by:r,event_type:"test_result_recorded",old_data:null,new_data:{result:e.result,run_at:s,note:e.note,asset:n||null},actor:To(),session_id:Po()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)});const c=n?` (asset: ${n})`:"";return Ei(ai({item_id:e.item_id,result:e.result,run_at:s,run_by:i||r,note:e.note??null,asset:n||null,asset_id:l?.asset_id??null,asset_created:l?.asset_created??!1,message:`Test result '${e.result}' recorded for ${e.item_id}${c}`}))}catch(e){return Ii(vi(e))}}E(Bn,{recordTestResultHandler:()=>Jn});var Qn=A({"src/tools/record-test-result.ts"(){Ne(),Bi(),Co(),Oi()}}),Gn={};async function Kn(e){try{const t=await qe(),r=De(),i=$e();if(!Array.isArray(e.results)||0===e.results.length)return Ii(li("results must be a non-empty array"));if(e.results.length>100)return Ii(li(`results must have at most 100 entries (got ${e.results.length})`));try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const a=await Ti(t,e.project_id,e.item_id);if(!a)return Ii(oi("Item",e.item_id));if("test-case"!==a.type)return Ii(li(`Item '${e.item_id}' is a ${a.type}, not a test-case`));const{data:n,error:s}=await t.rpc("record_test_results_bulk",{p_project_id:e.project_id,p_item_id:e.item_id,p_run_by:i||r,p_actor_user_id:r,p_results:e.results});if(s)throw new Error(s.message);return t.from("change_history").insert({item_id:e.item_id,project_id:e.project_id,changed_by:r,event_type:"test_result_recorded",old_data:null,new_data:{bulk:!0,count:e.results.length,results:n},actor:To(),session_id:Po()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),Ei(ai({item_id:e.item_id,count:e.results.length,results:n,message:`Recorded ${e.results.length} test results for ${e.item_id}`}))}catch(e){return Ii(vi(e))}}E(Gn,{recordTestResultsBulkHandler:()=>Kn});var Yn,Zn,Xn,es,ts=A({"src/tools/record-test-results-bulk.ts"(){Ne(),Bi(),Co(),Oi()}});function rs(e){return Math.min(Math.max(e??5,1),10)}var is=A({"src/core/trace-graph.ts"(){Yn=200,Zn=new Set(["parent","verifies","mitigated_by"]),Xn=new Set(["child","verified_by","mitigates"]),es={parent:"child",child:"parent",verifies:"verified_by",verified_by:"verifies",mitigated_by:"mitigates",mitigates:"mitigated_by",related:"related"}}}),as={};async function ns(e){try{const t=rs(e.depth),r=await qe(),i=await Ti(r,e.project_id,e.item_id);if(!i)return Ii(oi("Item",e.item_id));const{data:a,error:n}=await r.from("items").select("id, title, type, data").eq("project_id",e.project_id).is("deleted_at",null);if(n)throw new Error(n.message);const{data:s,error:o}=await r.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id);if(o)throw new Error(o.message);const d=Wi(a??[],s??[]);return Ei(ai(function(e){const t=rs(e.depth),r=e.relationshipTypes?.length?new Set(e.relationshipTypes):null,i=new Map(e.items.map(e=>[e.id,e]));i.has(e.rootItemId)||i.set(e.rootItemId,e.rootItem);const a=new Map,n=(e,t,r)=>{a.has(e)||a.set(e,[]),a.get(e).push({neighbor:t,relType:r})};for(const t of e.relationships){const i=t.type;if(("upstream"===e.direction||"both"===e.direction)&&(!Zn.has(i)||r&&!r.has(i)||n(t.from_id,t.to_id,i),Xn.has(i))){const e=es[i]??i;r&&!r.has(e)||n(t.to_id,t.from_id,e)}if(("downstream"===e.direction||"both"===e.direction)&&(!Xn.has(i)||r&&!r.has(i)||n(t.from_id,t.to_id,i),Zn.has(i))){const e=es[i]??i;r&&!r.has(e)||n(t.to_id,t.from_id,e)}"related"!==i||r&&!r.has("related")||(n(t.from_id,t.to_id,"related"),n(t.to_id,t.from_id,"related"))}const s=new Set([e.rootItemId]),o=[],d=[],l=[];for(const t of a.get(e.rootItemId)??[])l.push({id:t.neighbor,depth:1,relType:t.relType,from:e.rootItemId});for(;l.length>0&&o.length<Yn;){const{id:e,depth:r,relType:n,from:c}=l.shift();if(s.has(e)||r>t)continue;s.add(e);const p=i.get(e);if(p&&(o.push({id:e,title:p.title,type:p.type,relationship:n,depth:r,from:c}),d.push({from:c,to:e,type:n}),r<t))for(const t of a.get(e)??[])s.has(t.neighbor)||l.push({id:t.neighbor,depth:r+1,relType:t.relType,from:e})}const c={id:e.rootItemId,title:e.rootItem.title,type:e.rootItem.type,relationship:"root",depth:0,from:e.rootItemId};return{root:e.rootItemId,project_id:e.projectId,direction:e.direction,depth_limit:t,items:[c,...o],edges:d,total_count:o.length+1,...o.length>=Yn?{truncated:!0,truncation_message:`Results capped at ${Yn} items. Use a smaller depth or filter by relationship_types.`}:{}}}({projectId:e.project_id,rootItemId:e.item_id,rootItem:{id:e.item_id,title:i.title??e.item_id,type:i.type??"unknown"},items:a??[],relationships:d??[],direction:e.direction,depth:t,relationshipTypes:e.relationship_types})))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}E(as,{traceHandler:()=>ns});var ss=A({"src/tools/trace.ts"(){Ne(),Bi(),is(),Oi()}}),os={};async function ds(e){try{let t=(await qe()).from("assets").select("id, name, archived_at, created_at").eq("project_id",e.project_id).order("name");e.include_archived||(t=t.is("archived_at",null));const{data:r,error:i}=await t;if(i)throw new Error(i.message);return Ei(ai({assets:r||[],total:(r||[]).length}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}E(os,{listAssetsHandler:()=>ds});var ls=A({"src/tools/assets.ts"(){Ne(),Oi()}}),cs={};async function ps(e,t,r,i){if(r&&i)throw new Error("baseline_id and as_of are mutually exclusive");if(i)return i;if(!r)return null;const{data:a,error:n}=await e.from("baselines").select("project_id, snapshot_at, deleted_at").eq("id",r).maybeSingle();if(n)throw new Error(n.message);if(!a||a.project_id!==t||a.deleted_at)throw new Error("Baseline not found for this project");return a.snapshot_at}async function ms(e,t,r){if(r){const{data:i,error:a}=await e.rpc("variables_at",{p_project_id:t,p_snapshot_at:r});if(a)throw new Error(a.message);return i??[]}const{data:i,error:a}=await e.from("project_variables").select("*").eq("project_id",t).order("name");if(a)throw new Error(a.message);return i??[]}async function us(e,t,r,i){const{data:a,error:n}=await e.rpc("save_variable_with_revision",{p_project_id:t.project_id,p_name:t.name,p_value:t.value,p_unit:t.unit??null,p_description:t.description??null,p_is_signal:t.is_signal??!1,p_range_min:t.range_min??null,p_range_max:t.range_max??null,p_rate_ms:t.rate_ms??null,p_user_id:r,p_event_source:i,p_variable_id:t.id??null});if(n)throw new Error(n.message);return a}async function _s(e){try{const t=await qe(),r=await ps(t,e.project_id,e.baseline_id,e.as_of),i=await ms(t,e.project_id,r),a=e.limit??50,n=e.offset??0;return Ei(ai({variables:i.slice(n,n+a),total:i.length},ni(i.length,a,n)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}async function fs(e){try{const t=await qe(),r=await ps(t,e.project_id,e.baseline_id,e.as_of),i=(await ms(t,e.project_id,r)).find(t=>t.name===e.variable_name)??null;if(!i)return Ii(oi("Variable",e.variable_name));const{data:a}=await t.from("items").select("id, title, type, data").eq("project_id",e.project_id).is("deleted_at",null),n=`{${e.variable_name}}`,s=(a||[]).filter(e=>(e.data?.body||"").includes(n));return Ei(ai({variable:i,referenced_by:s.map(e=>({id:e.id,title:e.title,type:e.type})),reference_count:s.length}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}async function hs(e){try{const t=await qe(),r=De();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const i=await t.from("project_variables").select("*").eq("project_id",e.project_id).eq("name",e.variable_name).maybeSingle();if(!i.data)return Ii(oi("Variable",e.variable_name));const a={updated_by:r,updated_at:(new Date).toISOString()};return void 0!==e.value&&(a.value=e.value),void 0!==e.unit&&(a.unit=e.unit||null),void 0!==e.description&&(a.description=e.description||null),Ei(ai({variable:await us(t,{...i.data,...a,project_id:e.project_id,name:e.variable_name,value:a.value??i.data.value,unit:a.unit??i.data.unit,description:a.description??i.data.description},r,"mcp")}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}async function gs(e){try{const t=await qe(),r=De();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const i=/^[a-zA-Z_][a-zA-Z0-9_]*$/;if(e.variables&&Array.isArray(e.variables)){const a=e.variables.filter(e=>!i.test(e.name));if(a.length>0)return Ii(li(`Invalid variable name(s): ${a.map(e=>e.name).join(", ")}. Use letters, digits, underscores; must start with letter or underscore.`));const n=e.variables.map(e=>e.name),s=n.map(e=>e.toLowerCase()),o=n.filter((e,t)=>s.indexOf(s[t])!==t);if(o.length>0)return Ii(li(`Duplicate names in batch (case-insensitive): ${[...new Set(o)].join(", ")}`));const{data:d}=await t.from("project_variables").select("name").eq("project_id",e.project_id),l=new Set((d??[]).map(e=>e.name.toLowerCase())),c=n.filter(e=>l.has(e.toLowerCase()));if(c.length>0)return Ii(li(`Variable(s) already exist (case-insensitive): ${c.join(", ")}. Remove them from the batch or use atoms_update_variable to change values.`));const p=e.variables.map(t=>({project_id:e.project_id,name:t.name,value:t.value,unit:t.unit??null,description:t.description??null,created_by:r,updated_by:r})),m=await async function(e,t,r,i){const{data:a,error:n}=await e.rpc("bulk_save_variables_with_revisions",{p_project_id:t,p_variables:r,p_user_id:i,p_event_source:"mcp"});if(n)throw new Error(n.message);return a??[]}(t,e.project_id,p,r);return Ei(ai({batch_id:crypto.randomUUID(),created:(m??[]).length,variables:!1===e.echo?(m??[]).map(e=>({name:e.name})):m??[],message:`${(m??[]).length} variable(s) created. Reference them as {name} in requirement bodies.`}))}if(!e.name||!e.value)return Ii(li("Provide either (name, value) for single variable or variables[] for batch."));if(!i.test(e.name))return Ii(li(`Variable name "${e.name}" is invalid. Use letters, digits, and underscores only; must start with a letter or underscore.`));const{data:a}=await t.from("project_variables").select("name").eq("project_id",e.project_id),n=(a??[]).find(t=>t.name.toLowerCase()===e.name.toLowerCase());if(n)return Ii(li(`Variable "${n.name}" already exists in this project (names are unique regardless of case).`));const s=await us(t,{project_id:e.project_id,name:e.name,value:e.value,unit:e.unit??null,description:e.description??null},r,"mcp");return!1===e.echo?Ei(ai({name:e.name})):Ei(ai({variable:s,message:`Variable "${e.name}" created. Reference it in requirement bodies as {${e.name}}.`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}async function ys(e){try{const t=await qe(),r=De();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const{data:i}=await t.from("project_variables").select("id").eq("project_id",e.project_id).eq("name",e.name).maybeSingle();if(!i)return Ii(oi("Variable",e.name));const{data:a}=await t.from("items").select("id").eq("project_id",e.project_id).is("deleted_at",null).filter("data->>body","ilike",`%{${e.name}}%`),n=(a??[]).length;if(n>0&&!e.force)return Ii(li(`Variable "${e.name}" is referenced in ${n} item${n>1?"s":""}. Use force=true to delete anyway, or remove all {${e.name}} references first.`));if(za("atoms_delete_variable")){const t={tool:"atoms_delete_variable",project_id:e.project_id,target:e.name};if(!e.confirmation_token){const r=Ma(t);return Ei(ai({status:"confirmation_required",preview:{variable_name:e.name,referenced_by_items:n,would_orphan_references:n>0},confirmation_token:r.token,expires_at:r.expires_at,expires_in_seconds:r.expires_in_seconds,message:`Re-call atoms_delete_variable with the same project_id, name, and confirmation_token within ${r.expires_in_seconds}s to execute.`}))}const r=Ha(e.confirmation_token,t);if(!r.valid)return Ii(li(`confirmation_token rejected: ${r.reason}`))}const s=await async function(e,t,r,i){const{data:a,error:n}=await e.rpc("delete_variable_with_revision",{p_project_id:t,p_variable_id:r,p_user_id:i,p_event_source:"mcp"});if(n)throw new Error(n.message);return Boolean(a)}(t,e.project_id,i.id,r);return s?Ei(ai({deleted:!0,name:e.name,referenced_by_items:n,warning:n>0?`${n} item${n>1?"s":""} still contain {${e.name}} — those references are now unresolved.`:void 0})):Ii(oi("Variable",e.name))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}E(cs,{createVariableHandler:()=>gs,deleteVariableHandler:()=>ys,getVariableHandler:()=>fs,listVariablesHandler:()=>_s,updateVariableHandler:()=>hs});var ws=A({"src/tools/variables.ts"(){Ne(),Pa(),Oi()}}),vs={};async function bs(e,t){const{data:r}=await e.from("metadata").select("domains, levels, function_tags").eq("project_id",t).maybeSingle();return{domains:r?.domains??[],levels:r?.levels??[],functionTags:Array.isArray(r?.function_tags)?r.function_tags.filter(e=>"string"==typeof e):[]}}async function js(e){try{const t=await qe(),{domains:r,functionTags:i}=await bs(t,e.project_id),a=Wr(r,i),n=new Set(a.map(jr)),s=new Set(i.map(jr)),o=r.filter(e=>!n.has(jr(e))),{counts:d,orphans:l}=await async function(e,t,r){const{data:i}=await e.from("items").select("data").eq("project_id",t).is("deleted_at",null),a={};for(const e of r)a[e]=0;const n={};for(const e of i??[]){const t=e.data?.tags?.domains??[];for(const e of t)n[e]=(n[e]??0)+1,e in a&&a[e]++}const s=new Set(r),o={};for(const[e,t]of Object.entries(n))s.has(e)||(o[e]=t);return{counts:a,orphans:o}}(t,e.project_id,o),c=o.map(e=>({name:e,registered:!0,item_count:d[e]??0})),p=Object.entries(l).filter(([e])=>!s.has(jr(e))).map(([e,t])=>({name:e,registered:!1,item_count:t})).sort((e,t)=>e.name.localeCompare(t.name)),m=[...c,...p];return Ei(ai({domains:m,total:m.length,registered_count:c.length,unregistered_count:p.length,excluded_function_tags:a,note:p.length>0?`${p.length} unregistered tag(s) found on items. Use atoms_create_domain to register them, or atoms_update_item to remove them.`:void 0}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}async function Ss(e){try{const t=await qe();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const r=ki(e.name).trim().toLowerCase();if(!r)return Ii(li("Domain name cannot be empty."));const{data:i,error:a}=await t.rpc("append_project_domain",{p_project_id:e.project_id,p_domain:r});if(a)throw new Error(a.message);return i&&!1===i.added?Ii(li(`Domain "${r}" already exists in this project.`)):Ei(ai({domain:{name:r},message:`Domain "${r}" registered. You can now tag items with domains=["${r}"].`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}async function ks(e){try{const t=await qe(),r=De();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const{domains:i}=await bs(t,e.project_id),a=ki(e.name).trim().toLowerCase(),n=ki(e.new_name).trim().toLowerCase();if(!n)return Ii(li("new_name cannot be empty."));if(!i.includes(a)){const{data:r}=await t.from("items").select("id").eq("project_id",e.project_id).is("deleted_at",null).filter("data->tags->domains","cs",JSON.stringify([a])).limit(1);if(!r||0===r.length)return Ii(oi("Domain",a))}if(n!==a&&i.includes(n))return Ii(li(`Domain "${n}" already exists. Choose a different name.`));const{data:s,error:o}=await t.from("items").select("id, data").eq("project_id",e.project_id).is("deleted_at",null).filter("data->tags->domains","cs",JSON.stringify([a]));if(o)throw new Error(o.message);const d=(s??[]).length;if(e.dry_run)return Ei(ai({dry_run:!0,rename:{from:a,to:n},items_affected:d,message:`Dry run: renaming "${a}" → "${n}" would update ${d} item${1!==d?"s":""}. Call again with dry_run=false to apply.`}));const{error:l}=await t.rpc("mutate_project_domains",{p_project_id:e.project_id,p_action:"rename",p_values:[a,n]});if(l)throw new Error(l.message);const{error:c}=await t.rpc("apply_domain_change",{p_project_id:e.project_id,p_old_domain:a,p_new_domain:n,p_user_id:r,p_event_source:"mcp"});if(c)throw new Error(c.message);return Ei(ai({domain:{name:n},renamed_from:a,items_updated:d,message:`Domain renamed "${a}" → "${n}". ${d} item${1!==d?"s":""} updated.`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}async function xs(e){try{const t=await qe(),r=De();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const{domains:i}=await bs(t,e.project_id),a=ki(e.name).trim().toLowerCase(),n=i.includes(a),{data:s,error:o}=await t.from("items").select("id, data").eq("project_id",e.project_id).is("deleted_at",null).filter("data->tags->domains","cs",JSON.stringify([a]));if(o)throw new Error(o.message);const d=(s??[]).length;if(!n&&0===d)return Ii(oi("Domain",a));if(d>0&&!e.force)return Ii(li(`Domain "${a}" is used by ${d} item${d>1?"s":""}. Use force=true to delete and strip the domain from all items, or re-tag them first.`));if(za("atoms_delete_domain")){const t={tool:"atoms_delete_domain",project_id:e.project_id,target:a};if(!e.confirmation_token){const e=Ma(t);return Ei(ai({status:"confirmation_required",preview:{domain_name:a,items_using_count:d,would_strip_from_items:d>0},confirmation_token:e.token,expires_at:e.expires_at,expires_in_seconds:e.expires_in_seconds,message:`Re-call atoms_delete_domain with the same project_id, name, and confirmation_token within ${e.expires_in_seconds}s to execute.`}))}const r=Ha(e.confirmation_token,t);if(!r.valid)return Ii(li(`confirmation_token rejected: ${r.reason}`))}const{error:l}=await t.rpc("mutate_project_domains",{p_project_id:e.project_id,p_action:"remove",p_values:[a]});if(l)throw new Error(l.message);if(d>0){const{error:i}=await t.rpc("apply_domain_change",{p_project_id:e.project_id,p_old_domain:a,p_new_domain:null,p_user_id:r,p_event_source:"mcp"});if(i)throw new Error(i.message)}return Ei(ai({deleted:!0,name:a,items_updated:d,message:d>0?`Domain "${a}" deleted and stripped from ${d} item${d>1?"s":""}.`:`Domain "${a}" deleted.`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}E(vs,{createDomainHandler:()=>Ss,deleteDomainHandler:()=>xs,listDomainsHandler:()=>js,updateDomainHandler:()=>ks});var As=A({"src/tools/domains.ts"(){Ne(),Pa(),Oi(),Xr()}}),Es={};async function Is(e){try{const t=await qe(),{data:r,error:i}=await t.from("projects").select("id, name").eq("id",e.project_id).is("deleted_at",null).maybeSingle(),a=r?.name??null,n=i?.message??null,{data:s,error:o}=await t.from("items").select("id, type, data").eq("project_id",e.project_id).is("deleted_at",null);if(o)throw new Error(o.message);const d=s??[];if(!a&&0===d.length){if(n)throw new Error(n);return Ii(oi("Project",e.project_id))}const l={requirements:0,test_cases:0,notes:0},c=[],p=[];for(const e of d)if("requirement"===e.type){l.requirements++;const t=e.data?.tags?.domains??[];p.push({id:e.id,domains:t})}else"test-case"===e.type?(l.test_cases++,c.push(e.id)):"note"===e.type&&l.notes++;const m={passed:0,failed:0,blocked:0,not_run:0};if(c.length>0){const{data:r,error:i}=await t.from("test_results").select("item_id, result, run_at").eq("project_id",e.project_id).order("run_at",{ascending:!1});if(i)throw new Error(i.message);const a=new Map;for(const e of r??[])a.has(e.item_id)||a.set(e.item_id,e.result);for(const e of c){const t=a.get(e);t&&"not-run"!==t?"passed"===t?m.passed++:"failed"===t?m.failed++:"blocked"===t?m.blocked++:m.not_run++:m.not_run++}}const{data:u,error:_}=await t.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id);if(_)throw new Error(_.message);const f=Wi(s??[],u??[]),h=new Set;for(const e of f)"verifies"===e.type?h.add(e.to_id):"verified_by"===e.type&&h.add(e.from_id);const g=p.filter(e=>h.has(e.id)).length,y=p.length,w={covered:g,uncovered:y-g,total:y,percent:y>0?Math.round(g/y*1e3)/10:100},v=new Map;for(const e of p){const t=e.domains.length>0?e.domains:["(untagged)"],r=h.has(e.id);for(const e of t){v.has(e)||v.set(e,{covered:0,total:0});const t=v.get(e);t.total++,r&&t.covered++}}const b=Array.from(v.entries()).sort((e,t)=>e[0].localeCompare(t[0])).map(([e,t])=>({domain:e,covered:t.covered,total:t.total,percent:t.total>0?Math.round(t.covered/t.total*1e3)/10:100})),j=new Date;j.setDate(j.getDate()-7);const{count:S,error:k}=await t.from("change_history").select("id",{count:"exact",head:!0}).eq("project_id",e.project_id).gte("changed_at",j.toISOString());if(k)throw new Error(k.message);return Ei(ai({project_id:e.project_id,project_name:a??`Project ${e.project_id.slice(0,8)}`,counts:l,test_status:m,coverage:w,coverage_by_domain:b,recent_changes:S??0,last_updated:(new Date).toISOString()}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}E(Es,{projectSummaryHandler:()=>Is});var Rs,Os=A({"src/tools/project-summary.ts"(){Ne(),Bi(),Oi()}}),qs={};async function zs(e){try{const t=await qe();let r,i;if(e.query&&e.query.trim().length>0){const a=await Fi(t,e.project_id,e.query,{type:e.type,domain:e.domain,level:e.level,limit:e.limit});r=a.items.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level,req_type:e.data?.req_type})),i=a.totalCount}else{const a=await Pi(t,e.project_id,{type:e.type,domain:e.domain,level:e.level,limit:e.limit,offset:e.offset});r=a.items.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level,req_type:e.data?.req_type})),i=a.totalCount}const a=await async function(e,t){const{data:r,error:i}=await e.from("items").select("data").eq("project_id",t).is("deleted_at",null);if(i)throw new Error(i.message);const a=new Set,n=new Set;for(const e of r??[]){const t=e.data?.tags;if(t){const e=t.domains;if(e)for(const t of e)a.add(t);const r=t.level;r&&n.add(r)}}return{domains:[...a].sort(),levels:[...n].sort()}}(t,e.project_id);return Ei(ai({items:r,filters:{domains:a.domains,levels:a.levels,types:Rs},project_id:e.project_id},ni(i,e.limit,e.offset)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}E(qs,{browseHandler:()=>zs});var Ms,Hs,Ds,$s,Ps=A({"src/tools/browse.ts"(){Ne(),Bi(),Oi(),Rs=["requirement","test-case","note","table"]}}),Ts={};async function Us(e){try{const t=e.direction??"downstream",r=null!=e.depth?Math.min(Math.max(e.depth,1),20):null,i=await qe(),a=await Ti(i,e.project_id,e.item_id);if(!a)return Ii(oi("Item",e.item_id));const{data:n,error:s}=await i.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id);if(s)throw new Error(s.message);const{data:o,error:d}=await i.from("items").select("id, title, type").eq("project_id",e.project_id).is("deleted_at",null);if(d)throw new Error(d.message);const l=new Map;for(const e of o??[])l.set(e.id,{title:e.title,type:e.type});const c=new Map,p=(e,t,r)=>{c.has(e)||c.set(e,[]),c.get(e).push({neighbor:t,relType:r})};for(const e of n??[]){const r=e.type;"upstream"!==t&&"both"!==t||(Hs.includes(r)&&p(e.from_id,e.to_id,r),Ds.includes(r)&&p(e.to_id,e.from_id,$s[r]??r)),"downstream"!==t&&"both"!==t||(Ds.includes(r)&&p(e.from_id,e.to_id,r),Hs.includes(r)&&p(e.to_id,e.from_id,$s[r]??r)),"related"===r&&(p(e.from_id,e.to_id,"related"),p(e.to_id,e.from_id,"related"))}const m=new Set;m.add(e.item_id);const u=[];let _=0;const f=[];for(const t of c.get(e.item_id)??[])f.push({id:t.neighbor,depth:1,path:[{from_id:e.item_id,to_id:t.neighbor,relationship_type:t.relType}]});for(;f.length>0&&u.length<Ms;){const{id:e,depth:t,path:i}=f.shift();if(m.has(e))continue;if(null!==r&&t>r)continue;m.add(e);const a=l.get(e);if(a&&(u.push({item_id:e,title:a.title,type:a.type,depth:t,relationship_path:i}),t>_&&(_=t),null===r||t<r))for(const r of c.get(e)??[])m.has(r.neighbor)||f.push({id:r.neighbor,depth:t+1,path:[...i,{from_id:e,to_id:r.neighbor,relationship_type:r.relType}]})}let h=0;if(e.include_variable_refs){const{data:t}=await i.from("variable_references").select("variable_id").eq("item_id",e.item_id).eq("project_id",e.project_id),r=[...new Set((t??[]).map(e=>e.variable_id))];if(r.length>0){const{data:t}=await i.from("project_variables").select("id, name").in("id",r),a=new Map((t??[]).map(e=>[e.id,e.name])),{data:n}=await i.from("variable_references").select("variable_id, item_id").in("variable_id",r).eq("project_id",e.project_id).neq("item_id",e.item_id);for(const t of n??[]){const r=t.item_id;if(m.has(r))continue;if(u.length>=Ms)break;m.add(r);const i=l.get(r);if(!i)continue;const n=a.get(t.variable_id)??t.variable_id;u.push({item_id:r,title:i.title,type:i.type,depth:1,relationship_path:[{from_id:e.item_id,to_id:r,relationship_type:`variable_ref:${n}`}]}),h++}}}const g={};for(const e of u)g[e.type]=(g[e.type]??0)+1;const y=l.get(e.item_id);return Ei(ai({root:{item_id:e.item_id,title:y?.title??a.title,type:y?.type??a.type},impacted_items:u,summary:{total_impacted:u.length,by_type:g,max_depth_reached:_,...h>0?{variable_impact_count:h}:{},...u.length>=Ms?{truncated:!0}:{}}}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):Ii(ji(e instanceof Error?e.message:String(e)))}}E(Ts,{impactAnalysisHandler:()=>Us});var Cs=A({"src/tools/impact-analysis.ts"(){Ne(),Bi(),Oi(),Ms=200,Hs=["parent","verifies","traces_to","allocated_from","decomposed_from"],Ds=["child","verified_by","allocated_to","decomposes_to","traced_by"],$s={parent:"child",child:"parent",verifies:"verified_by",verified_by:"verifies",related:"related",traces_to:"traced_by",traced_by:"traces_to",allocated_to:"allocated_from",allocated_from:"allocated_to",decomposes_to:"decomposed_from",decomposed_from:"decomposes_to"}}}),Ns=A({"src/core/relationship-sync.ts"(){}}),Ls={};function Fs(e){return e instanceof Error&&e.message.includes("Not authenticated")?Ii(ci()):null}async function Ws(e,t){const{data:r,error:i}=await e.rpc("restore_baseline_state",{p_baseline_id:t.baselineId,p_project_id:t.projectId,p_items:[],p_variables:[],p_user_id:t.userId});if(i)throw new Error(i.message);return r}async function Vs(e,t,r){const{data:i,error:a}=await e.from("items").select("id, deleted_at").eq("project_id",t).eq("id",r).maybeSingle();if(a)throw new Error(a.message);if(n=i,!Boolean(n?.id&&null===n.deleted_at))return;var n;const{error:s}=await e.rpc("sync_item_relationships_from_jsonb",{p_project_id:t,p_item_id:r});if(s)throw new Error(s.message)}async function Bs(e,t,r){const{data:i,error:a}=await e.from("projects").select("org_id").eq("id",t).maybeSingle();if(a)return Ii(ji(a.message));if(!i)return Ii(oi("Project",t));const{data:n,error:s}=await e.from("organizations").select("feature_flags").eq("id",i.org_id).maybeSingle();return s?Ii(ji(s.message)):!0!==(n?.feature_flags??{})[r]?Ii(si(`Feature '${r}' is not enabled for this organization`,["Ask an org admin to enable the flag in Org Settings."])):null}async function Js(e){try{const t=await qe(),r=await async function(e,t){const{data:r,error:i}=await e.from("baselines").select("id, project_id, name, description, field_definitions_snapshot, snapshot_at, created_at, created_by, locked_at, locked_by, release_version, release_status").eq("project_id",t).is("deleted_at",null).order("snapshot_at",{ascending:!1});if(i)throw new Error(`listBaselines: ${i.message}`);return r??[]}(t,e.project_id),i=Math.max(e.offset??0,0),a=Math.min(Math.max(e.limit??50,1),100);return Ei(ai({baselines:r.slice(i,i+a).map(e=>({id:e.id,name:e.name,description:e.description,field_definitions_snapshot:e.field_definitions_snapshot,snapshot_at:e.snapshot_at,created_at:e.created_at,created_by:e.created_by,locked:null!==e.locked_at,locked_at:e.locked_at,release_version:e.release_version,release_status:e.release_status})),total:r.length,offset:i,limit:a},ni(r.length,a,i)))}catch(e){return Fs(e)??Ii(ji(e instanceof Error?e.message:String(e)))}}async function Qs(e){try{const t=await qe(),r=await Bs(t,e.project_id,"baselines_enabled");if(r)return r;const{data:i,error:a}=await t.from("baselines").insert({project_id:e.project_id,name:e.name.trim(),description:e.description??null,snapshot_at:(new Date).toISOString()}).select().single();return a?"23505"===a.code?Ii(si(`Baseline name "${e.name}" already exists in this project`,["Pick a different name"])):Ii(ji(a.message)):Ei(ai({id:i.id,name:i.name,description:i.description,field_definitions_snapshot:i.field_definitions_snapshot,snapshot_at:i.snapshot_at,created_at:i.created_at,locked:!1}))}catch(e){return Fs(e)??Ii(ji(e instanceof Error?e.message:String(e)))}}async function Gs(e){try{const t=await qe(),r=await Ci(t,e.baseline_id);if(!r)return Ii(oi("Baseline",e.baseline_id));const i=await Bs(t,r.project_id,"baselines_enabled");if(i)return i;if(null!==r.locked_at)return Ii(si("Baseline is already locked",["Locks are one-way; nothing to do"]));const{data:a}=await t.from("projects").select("org_id").eq("id",r.project_id).maybeSingle();if(!a)return Ii(oi("Project",r.project_id));const{data:n}=await t.auth.getUser(),s=n?.user?.id;if(!s)return Ii(ci());const{data:o}=await t.from("org_members").select("role").eq("user_id",s).eq("org_id",a.org_id).maybeSingle();if(!o||"admin"!==o.role)return Ii(si("Only admins can lock baselines",["Ask an org admin to lock this baseline"]));const{data:d,error:l}=await t.from("baselines").update({locked_at:(new Date).toISOString(),locked_by:s}).eq("id",e.baseline_id).select().single();return l?Ii(ji(l.message)):Ei(ai({id:d.id,locked_at:d.locked_at,locked_by:d.locked_by}))}catch(e){return Fs(e)??Ii(ji(e instanceof Error?e.message:String(e)))}}async function Ks(e){try{const t=await qe(),[r,i]=await Promise.all([Ci(t,e.baseline_a_id),Ci(t,e.baseline_b_id)]);if(!r||!i)return Ii(oi("Baseline","one of the baseline_ids"));if(r.project_id!==i.project_id)return Ii(si("Both baselines must belong to the same project",["Verify both baseline_ids are from the same project_id"]));const[a,n]=await Promise.all([Li(t,r.project_id,r.snapshot_at),Li(t,i.project_id,i.snapshot_at)]),s=new Map(a.map(e=>[e.id,e])),o=new Map(n.map(e=>[e.id,e])),d=Array.from(new Set([...s.keys(),...o.keys()])).sort(),l=[];let c=0,p=0,m=0;for(const e of d){const t=s.get(e),r=o.get(e);if(!t&&r)c++,l.push({item_id:e,change_type:"added"});else if(t&&!r)p++,l.push({item_id:e,change_type:"removed"});else if(t&&r){const i=[];t.title!==r.title&&i.push("title"),t.type!==r.type&&i.push("type");if(JSON.stringify(t.data??{})!==JSON.stringify(r.data??{})){i.push("data");const e=t.data??{},a=r.data??{};(e.status??null)!==(a.status??null)&&i.push("status"),(e.tags?.level??null)!==(a.tags?.level??null)&&i.push("tags.level"),JSON.stringify(e.tags?.domains??[])!==JSON.stringify(a.tags?.domains??[])&&i.push("tags.domains")}i.length>0&&(m++,l.push({item_id:e,change_type:"modified",fields_changed:i}))}}const u=Math.max(e.offset??0,0),_=Math.min(Math.max(e.limit??50,1),200),f=e.detail?l.slice(u,u+_):l.slice(u,u+_).map(({item_id:e,change_type:t,fields_changed:r})=>({item_id:e,change_type:t,fields_changed:r}));return Ei(ai({summary:{added:c,removed:p,modified:m,total_compared:d.length},items:f,page_info:{offset:u,limit:_,total:l.length},baselines:{a:{id:r.id,name:r.name,snapshot_at:r.snapshot_at},b:{id:i.id,name:i.name,snapshot_at:i.snapshot_at}}}))}catch(e){return Fs(e)??Ii(ji(e instanceof Error?e.message:String(e)))}}async function Ys(e){try{const t=await qe(),r=await Ci(t,e.baseline_id);if(!r)return Ii(oi("Baseline",e.baseline_id));const i=await Bs(t,r.project_id,"baselines_enabled");if(i)return i;const{data:a}=await t.from("projects").select("org_id").eq("id",r.project_id).maybeSingle();if(!a)return Ii(oi("Project",r.project_id));const{data:n}=await t.auth.getUser(),s=n?.user?.id;if(!s)return Ii(ci());const{data:o}=await t.from("org_members").select("role").eq("user_id",s).eq("org_id",a.org_id).maybeSingle();if(!o||"admin"!==o.role)return Ii(si("Only admins can restore from a baseline",["Ask an org admin to run the restore"]));const{data:d}=await t.rpc("cc_project_is_controlled",{p_project_id:r.project_id});if(!0===d)return Ii(si("Change control is on for this project. Release review before restoring a baseline.",["Release review on the project, then run the restore"]));const l=await Li(t,r.project_id,r.snapshot_at),c=l.map(e=>e.id),{data:p,error:m}=c.length>0?await t.from("items").select("*").eq("project_id",r.project_id).in("id",c):{data:[],error:null};if(m)return Ii(ji(m.message));const u=function(e,t){const r=new Map(t.map(e=>[e.id,e]));return e.flatMap(e=>{const t=r.get(e.id),i=[];var a,n;return t?(null!==t.deleted_at&&i.push("deleted_at"),t.type!==e.type&&i.push("type"),t.title!==e.title&&i.push("title"),a=t.data,n=e.data,JSON.stringify(a??{})!==JSON.stringify(n??{})&&i.push("data")):i.push("missing"),0===i.length?[]:[{id:e.id,type:e.type,title:e.title,data:e.data??{},diff_summary:{changed_fields:i,restored:!0},oldData:t?.data??null}]})}(l,p??[]),{data:_,error:f}=await t.rpc("variables_at",{p_project_id:r.project_id,p_snapshot_at:r.snapshot_at});if(f)return Ii(ji(f.message));const h=_??[];if(!0!==e.confirm)return Ei(ai({preview:!0,items_to_restore:u.length,variables_to_restore:h.length,field_definitions_to_restore:r.field_definitions_snapshot?.length??null}));const g=await Ws(t,{baselineId:r.id,projectId:r.project_id,userId:s});return Ei(ai({ok:!0,items_restored:g.items_restored,variables_restored:g.variables_restored,field_definitions_restored:g.field_definitions.length}))}catch(e){return Fs(e)??Ii(ji(e instanceof Error?e.message:String(e)))}}E(Ls,{createBaselineHandler:()=>Qs,diffBaselinesHandler:()=>Ks,listBaselinesHandler:()=>Js,lockBaselineHandler:()=>Gs,restoreBaselineHandler:()=>Ys,restoreBaselineStateAtomically:()=>Ws,syncRestoredItemRelationships:()=>Vs});var Zs,Xs=A({"src/tools/baselines.ts"(){Ne(),Co(),Oi(),Bi(),Ns()}}),eo={};function to(e){return"safety-goal"===e?"hara":"failure-mode"===e?"dfmea":null}async function ro(e,t,r){const{data:i,error:a}=await e.rpc("next_item_id",{p_project_id:t,p_prefix:r});if(a)throw new Error(`ID generation failed: ${a.message}`);return i}function io(e){const t=e?.relationships?.mitigated_by;return Array.isArray(t)?t.filter(e=>"string"==typeof e):[]}function ao(e){const t=e?.relationships?.verified_by;return Array.isArray(t)?t.filter(e=>"string"==typeof e):[]}function no(e){return JSON.parse(JSON.stringify(e??{}))}async function so(e,t,r){const i=[];for(const e of r){const t=(e??"").trim();t&&null===Sr(t,i)&&i.push(t)}if(0===i.length)return i;try{const{data:r}=await e.from("metadata").select("domains").eq("project_id",t).maybeSingle(),n=(a=r?.domains,Array.isArray(a)?a.filter(e=>"string"==typeof e):[]),s=i.map(e=>Sr(e,n)??e),{error:o}=await e.rpc("mutate_project_domains",{p_project_id:t,p_action:"add_functions",p_values:s});return o&&process.stderr.write(`[atoms-mcp] Warning: function tag registration failed: ${o.message}\n`),s}catch(e){return process.stderr.write(`[atoms-mcp] Warning: function tag registration failed: ${e instanceof Error?e.message:String(e)}\n`),i}var a}async function oo(e,t){const{data:r,error:i}=await e.from("metadata").select("industry").eq("project_id",t).maybeSingle();if(i)throw new Error(i.message);return a=r,n=a?.industry,"automotive"===n||"medical"===n?a.industry:null;var a,n}async function lo(e,t,r,i){const a=function(e){const t=e?.analysis_props?.variant;return"iso26262"===(r=t)||"iso14971"===r||"design"===r||"process"===r||"software"===r||"use"===r?t:null;var r}(i);return{variant:a,schemeId:ar(r,a,a?null:await oo(e,t))}}async function co(e,t,r,i){const a=function(e){const t=e?.relationships?.parents;return Array.isArray(t)?t.filter(e=>"string"==typeof e)[0]??null:null}(i),n=a?await Ti(e,t,a):null;return lo(e,t,r,n?.data)}function po(e,t,r,i={}){const a=i.where?`${i.where}: `:"",n=new Set([...Et[e],...i.extraKeys??[]]),s=Object.keys(r??{});if(0===s.length)return{ok:!1,message:`${a}fields must contain at least one key`};const o=s.filter(e=>Kt.includes(e));if(o.length>0)return{ok:!1,message:`${a}${zr(e,o)}`};const d=Mr(e);if(d&&s.includes(d.field))return{ok:!1,message:`${a}${Hr(e)}`};const l=s.filter(e=>!n.has(e));if(l.length>0)return{ok:!1,message:`${a}Unknown field(s) for ${e}: ${l.join(", ")}. Allowed: ${[...n].sort().join(", ")}`};const c=At[e],p={};for(const t of s){const i=r[t];if(c[t]){const r="number"==typeof i?String(i):i;if("string"!=typeof r||!c[t].includes(r))return{ok:!1,message:`${a}Invalid ${t} value ${JSON.stringify(i)} for ${e}. Allowed: ${c[t].join(", ")}`};p[t]=r}else{if("string"!=typeof i)return{ok:!1,message:`${a}Field '${t}' must be a string`};p[t]=i}}return{ok:!0,writes:p}}async function mo(e){try{const r=await qe(),i=De();try{await Pe(r,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const a="string"==typeof(t=e.subtype)&&Object.prototype.hasOwnProperty.call(Pt,t)?Pt[t]:null;if(!a)return Ii(li(function(e){return`subtype must be one of: ${Tt.join(", ")} (got ${JSON.stringify(e)})`}(e.subtype)));const n=e.source_req_ids??[],s=e.source_functions??[];if("hara"===a&&n.length>0)return Ii(li("A HARA is seeded from functions, never requirements (a requirement is an output of the analysis): pass source_functions and omit source_req_ids"));if("dfmea"===a&&s.length>0)return Ii(li("source_functions applies to hara only; seed a DFMEA with source_req_ids"));if(n.length>50)return Ii(li("source_req_ids supports at most 50 entries per call"));if(s.length>50)return Ii(li("source_functions supports at most 50 entries per call"));if(n.length>0){const{data:t,error:i}=await r.from("items").select("id, type, data").eq("project_id",e.project_id).is("deleted_at",null).in("id",n);if(i)throw new Error(i.message);const a=new Map((t??[]).map(e=>[e.id,e])),s=n.filter(e=>!a.has(e));if(s.length>0)return Ii(oi("Source requirement(s)",s.join(", ")));const o=n.filter(e=>{const t=a.get(e);return"requirement"!==t.type||Zs.has(t.data?.req_type)});if(o.length>0)return Ii(li(`source_req_ids must reference plain requirements (type='requirement', not analysis rows): ${o.join(", ")}`))}if(void 0!==e.variant&&!gr(e.subtype).includes(e.variant))return Ii(li(function(e,t){const r=gr(e),i="risk_analysis"===e||"hazard_analysis"===e?"a risk analysis (ISO 14971)":"fmea"===e?"an fmea":`a ${e}`;return`variant must be ${1===r.length?"":"one of: "}${r.join(", ")} for ${i} (got ${JSON.stringify(t)}); the analysis carries its standard, so pick the variant that names it`}(e.subtype,e.variant)));const o=e.variant??function(e,t){return"medical"===t?Ct[e]:Ut[e]}(e.subtype,await oo(r,e.project_id)),d=ar(a,o),l=nr(d),c={};for(const t of It){const r=e[t];"string"==typeof r&&""!==r&&(c[t]=ki(r))}const p=Object.keys(c);if(p.length>0&&0===cr(d).length)return Ii(li(fr(d,p)));const m="hara"===a?await so(r,e.project_id,s):n,u=await ro(r,e.project_id,Dt[d]),_=`New ${Dr(d).parent}`,f=e.title??_,h=e.revision??"1.0",g=Mt[a],y=(new Date).toISOString(),w={id:u,type:"analysis",title:f,body:"",tags:{domains:[],level:""},ownership:{primary:null,additional:[]},relationships:{parents:[],children:[],related:[],verifies:[],verified_by:[]},links:[],analysis:{subtype:a,scheme:d,revision:h,...l?{standard:l}:{}},analysis_props:{variant:o,...c},metadata:{created_at:y,created_by:i,updated_at:y,updated_by:i}},{error:v}=await r.rpc("save_item_with_revision",{p_item_id:u,p_project_id:e.project_id,p_type:"analysis",p_title:f,p_data:w,p_user_id:i,p_event_source:"mcp",p_event_session_id:Po()??null,p_diff_summary:null});if(v)throw new Error(v.message);const b=(e,t)=>"hara"===a?{id:e,source_function:t}:{id:e,source_req:t},j=[],S=[];for(const t of m){const n=await ro(r,e.project_id,Ht[d]),s={id:n,type:"requirement",title:"",body:"",req_type:g,tags:{domains:[],level:""},ownership:{primary:null,additional:[]},relationships:{parents:[u],children:[],related:[],verifies:[],verified_by:[]},links:[],analysis_props:"hara"===a?{source_req:null,source_function:t,approved:!1,approved_at:null,field_source:{}}:{source_req:t,approved:!1,approved_at:null,field_source:{}},metadata:{created_at:y,created_by:i,updated_at:y,updated_by:i}},{error:o}=await r.rpc("save_item_with_revision",{p_item_id:n,p_project_id:e.project_id,p_type:"requirement",p_title:"",p_data:s,p_user_id:i,p_event_source:"mcp",p_event_session_id:Po()??null,p_diff_summary:null});if(o)throw new Error(o.message);const{error:l}=await r.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:n,p_to_id:u,p_type:"parent",p_action:"add",p_user_id:i,p_event_source:"mcp"});l&&(process.stderr.write(`[atoms-mcp] Warning: analysis row parent link sync failed for ${n}: ${l.message}\n`),S.push({row_id:n,..."hara"===a?{source_function:t}:{source_req:t},message:l.message})),await r.from("change_history").insert({item_id:n,project_id:e.project_id,changed_by:i,event_type:"created",old_data:null,new_data:s,actor:To(),session_id:Po()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),j.push(b(n,t))}return await r.from("change_history").insert({item_id:u,project_id:e.project_id,changed_by:i,event_type:"created",old_data:null,new_data:w,actor:To(),session_id:Po()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),Ei(ai({id:u,subtype:a,title:f,revision:h,scheme:d,variant:o,labels:Dr(d),...l?{standard:l}:{},rows:j,...S.length>0?{link_warnings:S,warning:`${S.length} row(s) were created but could not be attached to the analysis and will not appear in atoms_get_analysis. Repair: for each row in link_warnings, call atoms_link_items with type 'parent', from_id=<row id>, to_id=${u}.`}:{}}))}catch(e){return Ii(vi(e))}var t}function uo(e){for(const t of Object.keys(e)){const r=e[t];"string"==typeof r&&(e[t]=ki(r))}}async function _o(e){try{const t=await qe(),r=De();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const i=await Ti(t,e.project_id,e.row_id);if(!i)return Ii(oi("Analysis row",e.row_id));const a=i.data;if("analysis"===i.type)return async function(e,t,r,i,a,n){const{schemeId:s}=await lo(e,r,function(e){return"dfmea"===e?.analysis?.subtype?"dfmea":"hara"}(a),a),o=function(e,t){const r=cr(e),i=Object.keys(t??{});if(0===i.length)return{ok:!1,message:"fields must contain at least one key"};const a=i.filter(e=>!r.includes(e));if(a.length>0)return{ok:!1,message:fr(e,a)};const n={};for(const e of i){const r=t[e];if("string"!=typeof r)return{ok:!1,message:`Field '${e}' must be a string`};if("overall_residual_risk"===e&&""!==r&&!qt.includes(r))return{ok:!1,message:hr(e,r,qt)};if("software_safety_class"===e&&""!==r&&!zt.includes(r))return{ok:!1,message:hr(e,r,zt)};n[e]=r}return{ok:!0,writes:n}}(s,n);if(!o.ok)return Ii(li(o.message));const d=o.writes;uo(d);const l=no(a);l.analysis_props&&"object"==typeof l.analysis_props||(l.analysis_props={});const c=l.analysis_props;for(const[e,t]of Object.entries(d))c[e]=t;const p=(new Date).toISOString();l.metadata={...l.metadata??{},updated_at:p,updated_by:t};const{data:m,error:u}=await e.rpc("save_item_with_revision",{p_item_id:i.id,p_project_id:r,p_type:i.type,p_title:i.title,p_data:l,p_user_id:t,p_event_source:"mcp",p_event_session_id:Po()??null,p_diff_summary:null,p_expected_updated_at:i.updated_at});if(u)throw new Error(u.message);return m?(await e.from("change_history").insert({item_id:i.id,project_id:r,changed_by:t,event_type:"updated",old_data:a,new_data:l,actor:To(),session_id:Po()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),Ei(ai({analysis_id:i.id,scheme:s,written_fields:Object.keys(d),scope:mr(c),overall:ur(c)}))):Ii(si(`Fill silently failed for '${i.id}': save RPC returned no row. Likely RLS denied the write.`,["Verify you have editor or admin role in this project","Retry after checking project access"]))}(t,r,e.project_id,i,a,e.fields??{});const n=to(a?.req_type);if(!n)return Ii(li(`Item ${e.row_id} is not an analysis row (req_type must be 'safety-goal' or 'failure-mode')`));const{schemeId:s}=await co(t,e.project_id,n,a),o=po(s,0,e.fields??{},{extraKeys:"hara"===n?["effect","title"]:["effect"]});if(!o.ok)return Ii(li(o.message));const d=o.writes;if(uo(d),"hara"===n&&"string"==typeof d.source_function&&d.source_function.trim()){const[r]=await so(t,e.project_id,[d.source_function]);r&&(d.source_function=r)}const l=a?.analysis_props??null,c=no(a);c.analysis_props&&"object"==typeof c.analysis_props||(c.analysis_props={source_req:null,approved:!1,approved_at:null,field_source:{}});const p=c.analysis_props;p.field_source&&"object"==typeof p.field_source||(p.field_source={});let m=i.title;for(const[e,t]of Object.entries(d))"effect"===e?c.body=t:"title"===e?(c.title=t,m=t):(p[e]=t,"failure_mode"===e&&"dfmea"===n&&(c.title=t,m=t)),p.field_source[e]="mcp";const u=Lr(p),_=(new Date).toISOString();c.metadata={...c.metadata??{},updated_at:_,updated_by:r};const{data:f,error:h}=await t.rpc("save_item_with_revision",{p_item_id:i.id,p_project_id:e.project_id,p_type:i.type,p_title:m,p_data:c,p_user_id:r,p_event_source:"mcp",p_event_session_id:Po()??null,p_diff_summary:null,p_expected_updated_at:i.updated_at});if(h)throw new Error(h.message);if(!f)return Ii(si(`Fill silently failed for '${e.row_id}': save RPC returned no row. Likely RLS denied the write.`,["Verify you have editor or admin role in this project","Retry after checking project access"]));await t.from("change_history").insert({item_id:i.id,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:a,new_data:c,actor:To(),session_id:Po(),...u?{reason:`${Object.keys(d).join(", ")} written through atoms_fill_row; approval cleared (a signature never covers content the approver did not see)`}:{}}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)});const g=Rr(s,p),y=[];if("hara_iso26262"===s){const a=Rr(s,l),n=io(c);if(n.length>0&&g!==gt&&g!==a)for(const a of n){const n=await Ti(t,e.project_id,a);if(!n)continue;const s=n.data;if(s?.asil===g&&s?.asil_source===i.id)continue;const o=s?.asil??null,d=s?.asil_source??null,l=no(s);l.asil=g,l.asil_source=i.id,l.metadata={...l.metadata??{},updated_at:_,updated_by:r};const{error:c}=await t.rpc("save_item_with_revision",{p_item_id:n.id,p_project_id:e.project_id,p_type:n.type,p_title:n.title,p_data:l,p_user_id:r,p_event_source:"mcp",p_event_session_id:Po()??null,p_diff_summary:null,p_expected_updated_at:n.updated_at});if(c)throw new Error(c.message);await t.from("change_history").insert({item_id:n.id,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:{asil:o,asil_source:d},new_data:{asil:g,asil_source:i.id},actor:To(),session_id:Po(),reason:`ASIL propagated from ${i.id} (rating change via atoms_fill_row)`}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),y.push(a)}}return Ei(ai({row_id:i.id,subtype:n,written_fields:Object.keys(d),computed_level:g,...y.length>0?{asil_propagated_to:y}:{},...u?{approval_cleared:!0}:{}}))}catch(e){return Ii(vi(e))}}async function fo(e,t,r){const i=await Ti(e,t,r);if(!i)return{ok:!1,result:Ii(oi("Analysis",r))};if("analysis"!==i.type)return{ok:!1,result:Ii(li(`Item ${r} is not an analysis (type='${i.type}')`))};const a=i.data,n=to("hara"===a?.analysis?.subtype?"safety-goal":"dfmea"===a?.analysis?.subtype?"failure-mode":null);if(!n)return{ok:!1,result:Ii(li(`Analysis ${r} is missing a valid analysis.subtype`))};const s=Mt[n],{variant:o,schemeId:d}=await lo(e,t,n,a),l=Array.isArray(a?.relationships?.children)?a.relationships.children.filter(e=>"string"==typeof e):[];let c=[];if(l.length>0){const{data:r,error:i}=await e.from("items").select("*").eq("project_id",t).is("deleted_at",null).in("id",l);if(i)throw new Error(i.message);const a=new Map((r??[]).filter(e=>!e.deleted_at&&l.includes(e.id)&&e.data?.req_type===s).map(e=>[e.id,e]));c=l.map(e=>a.get(e)).filter(e=>Boolean(e))}const p=new Set;for(const e of c){const t=e.data?.analysis_props??{};"string"==typeof t.source_req&&t.source_req&&p.add(t.source_req);for(const t of io(e.data))p.add(t);for(const t of ao(e.data))p.add(t)}const m=new Map;if(p.size>0){const{data:r,error:i}=await e.from("items").select("id, updated_at").eq("project_id",t).is("deleted_at",null).in("id",[...p]);if(i)throw new Error(i.message);for(const e of r??[])m.set(e.id,e.updated_at)}const u=c.map(e=>{const t=e.data?.analysis_props??{},r=io(e.data).filter(e=>m.has(e)),i=ao(e.data).filter(e=>m.has(e)),a=r.length>0,n=Rr(d,t),s=function(e,t,r){if(r)return null;if("hara_iso26262"===e){const e=kr(t);return"QM"!==e&&e!==gt?`asil ${e} unmitigated`:null}if("hazard_iso14971"===e){const e=Er(t);return"Low"!==e&&e!==gt?`${e.toLowerCase()} risk uncontrolled`:null}const i=parseInt(Rr(e,t),10),a=parseInt(t?.severity||"",10);return!Number.isNaN(i)&&i>=100?"rpn >= 100":!Number.isNaN(a)&&a>=9?"severity >= 9":null}(d,t,a),o={id:e.id,title:"string"==typeof e.title?e.title:"",source_req:"string"==typeof t.source_req?t.source_req:null,effect:"string"==typeof e.data?.body?e.data.body:"",computed_level:n,computed_level_text:br(d,n),is_gap:null!==s,gap_rule:s,is_critical:Or(d,t,a),is_stale:qr(t,r,e=>m.get(e)??null),mitigated_by:r,link_roles:t.link_roles??{},approved:!0===t.approved,approved_at:t.approved_at??null,approved_by:t.approved_by??null,approved_note:"string"==typeof t.approved_note?t.approved_note:"",verified_by:i,residual_level:"hazard_iso14971"===d?Ir(t):null,mitigation_types:t.mitigation_types??{},mcp_fields:Object.entries(t.field_source??{}).filter(([,e])=>"mcp"===e).map(([e])=>e).sort()};for(const e of Et[d])o[e]=t[e]??null;return o});return{ok:!0,analysis:{id:i.id,title:"string"==typeof i.title?i.title:"",subtype:n,scheme:d,revision:"string"==typeof a?.analysis?.revision?a.analysis.revision:"1.0",standard:"string"==typeof a?.analysis?.standard?a.analysis.standard:nr(d)??null,variant:o,labels:Dr(d),acceptability:vr(d),definitions:wr(d),control_types:Pr(d),field_labels:yr(d),scope:cr(d).length>0?mr(a?.analysis_props):null,overall:cr(d).length>0?ur(a?.analysis_props):null},schemeId:d,parentProps:a?.analysis_props??null,rows:u,counts:{rows:u.length,gaps:u.filter(e=>e.is_gap).length,critical:u.filter(e=>e.is_critical).length,stale:u.filter(e=>e.is_stale).length,approved:u.filter(e=>e.approved).length,linked:u.filter(e=>e.mitigated_by.length>0).length}}}async function ho(e){try{const t=await qe(),r=await fo(t,e.project_id,e.analysis_id);return r.ok?Ei(ai({analysis:r.analysis,rows:r.rows,counts:r.counts})):r.result}catch(e){return Ii(vi(e))}}async function go(e){try{const a=await qe(),n=De();try{await Pe(a,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}if(e.row_id===e.req_id)return Ii(li("row_id and req_id must be different items"));const s=await Ti(a,e.project_id,e.row_id);if(!s)return Ii(oi("Analysis row",e.row_id));const o=s.data,d=to(o?.req_type);if(!d)return Ii(li(`Item ${e.row_id} is not an analysis row (req_type must be 'safety-goal' or 'failure-mode')`));const l=await Ti(a,e.project_id,e.req_id);if(!l)return Ii(oi("Requirement",e.req_id));const c=l.data;if("requirement"!==l.type||Zs.has(c?.req_type))return Ii(li(`req_id must reference a plain requirement (type='requirement', not an analysis row): ${e.req_id}`));const{schemeId:p}=await co(a,e.project_id,d,o);if(function(e){return"hara_iso26262"===e||"hazard_iso14971"===e}(p)&&c?.req_type!==_t)return Ii(li(function(e,t){return`${e} is not a ${Yt[t].mitigating_requirement}: a mitigation target must be a requirement of req_type '${_t}'. Create or convert one, then link it.`}(e.req_id,p)));const m=$r(p);if(0===m.length&&void 0!==e.control_type)return Ii(li(function(e){return`control_type applies to the medical schemes only (ISO 14971 clause 7); ${Yt[e].parent} links carry none, so omit it.`}(p)));if(m.length>0&&!e.control_type)return Ii(li(Tr()));if(void 0!==e.control_type&&!sr(e.control_type))return Ii(li(Tr()));if("dfmea"===d&&!e.role)return Ii(li(`role is required for dfmea mitigation links. Choose one of: ${yt.join(", ")}`));if("hara"===d&&e.role)return Ii(li("role is not applicable to hara mitigation links; omit it (roles are a dfmea concept)"));let u=gt;if("hara_iso26262"===p){u=Rr(p,o?.analysis_props);const a=c?.asil;if(u!==gt&&"string"==typeof a&&a.length>0&&a!==u&&!0!==e.override)return Ii((r=u,{...si(`ASIL conflict: the target requirement already carries asil '${t=a}' but ${i=s.id} computes '${r}'.`,["Pass override=true to apply the HARA value; the override is history-logged like a dialog accept","Or align the row's S/E/C ratings with the existing ASIL before linking"]),details:{code:"asil_conflict",current_value:t,incoming_value:r,source_row:i,hint:"pass override=true to apply the HARA value; the override is history-logged like a dialog accept"}}))}const _="dfmea"===d?e.role:null,{error:f}=await a.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:e.row_id,p_to_id:e.req_id,p_type:"mitigated_by",p_action:"add",p_user_id:n,p_event_source:"mcp",p_expected_from_updated_at:s.updated_at,p_expected_to_updated_at:l.updated_at,p_role:_});if(f)throw new Error(f.message);let h,g;if(await a.from("change_history").insert({item_id:e.row_id,project_id:e.project_id,changed_by:n,event_type:"updated",old_data:null,new_data:{relationship_change:{action:"add",type:"mitigated_by",from:e.row_id,to:e.req_id,..._?{role:_}:{}}},actor:To(),session_id:Po()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),e.control_type&&sr(e.control_type)){const t=await Ti(a,e.project_id,e.row_id)??s,r=no(t.data);r.analysis_props&&"object"==typeof r.analysis_props||(r.analysis_props={source_req:null,approved:!1,approved_at:null,field_source:{}});const i=r.analysis_props;i.mitigation_types&&"object"==typeof i.mitigation_types||(i.mitigation_types={}),i.mitigation_types[e.req_id]=e.control_type;const o=(new Date).toISOString();r.metadata={...r.metadata??{},updated_at:o,updated_by:n};const{error:d}=await a.rpc("save_item_with_revision",{p_item_id:t.id,p_project_id:e.project_id,p_type:t.type,p_title:t.title,p_data:r,p_user_id:n,p_event_source:"mcp",p_event_session_id:Po()??null,p_diff_summary:null,p_expected_updated_at:t.updated_at});if(d)throw new Error(d.message);h=e.control_type}if("hara_iso26262"===p&&u!==gt){const t=await Ti(a,e.project_id,e.req_id);if(t){const r=t.data,i=r?.asil??null,o=r?.asil_source??null,d=no(r);d.asil=u,d.asil_source=s.id;const l=(new Date).toISOString();d.metadata={...d.metadata??{},updated_at:l,updated_by:n};const{error:c}=await a.rpc("save_item_with_revision",{p_item_id:t.id,p_project_id:e.project_id,p_type:t.type,p_title:t.title,p_data:d,p_user_id:n,p_event_source:"mcp",p_event_session_id:Po()??null,p_diff_summary:null,p_expected_updated_at:t.updated_at});if(c)throw new Error(c.message);await a.from("change_history").insert({item_id:t.id,project_id:e.project_id,changed_by:n,event_type:"updated",old_data:{asil:i,asil_source:o},new_data:{asil:u,asil_source:s.id},actor:To(),session_id:Po(),reason:!0===e.override?`ASIL override accepted via atoms_link_mitigation (was '${i??"unset"}', now '${u}' from ${s.id})`:`ASIL set via atoms_link_mitigation from ${s.id}`}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),g=u}}return Ei(ai({row_id:e.row_id,req_id:e.req_id,type:"mitigated_by",role:_,scheme:p,...h?{control_type:h}:{},...g?{asil_written:g}:{}}))}catch(e){return Ii(vi(e))}var t,r,i}async function yo(e){try{const a=await qe(),n=await fo(a,e.project_id,e.analysis_id);if(!n.ok)return n.result;const s=n.rows.filter(e=>e.is_gap).map(e=>({id:e.id,title:e.title,source_req:e.source_req,source_function:"string"==typeof e.source_function?e.source_function:null,computed_level:e.computed_level,rule:e.gap_rule})),o=or(await async function(e,t){const{data:r,error:i}=await e.from("metadata").select("levels").eq("project_id",t).maybeSingle();if(i)throw new Error(i.message);const a=r?.levels;return Array.isArray(a)?a.filter(e=>"string"==typeof e):[]}(a,e.project_id)),d=Nr(n.schemeId).some(e=>"requirement"===e.scope)?await async function(e,t,r){const{data:i,error:a}=await e.from("items").select("id, data").eq("project_id",t).is("deleted_at",null).eq("type","requirement");if(a)throw new Error(a.message);const n=(i??[]).filter(e=>e.data?.req_type===_t&&function(e,t){const r=dr(e);return r.length>0&&r===dr(t)}(e.data?.tags?.level,r));if(0===n.length)return[];const s=e=>{const t=e.data?.relationships?.parents;return Array.isArray(t)?t.filter(e=>"string"==typeof e):[]},o=new Set;for(const e of n)for(const t of s(e))o.add(t);const d=new Set;if(o.size>0){const{data:r,error:i}=await e.from("items").select("id").eq("project_id",t).is("deleted_at",null).in("id",[...o]);if(i)throw new Error(i.message);for(const e of r??[])d.add(e.id)}return n.filter(e=>!s(e).some(e=>d.has(e))).map(e=>e.id)}(a,e.project_id,o.level):[],l=n.rows.map(e=>({id:e.id,level:e.computed_level,linked:e.mitigated_by.length>0,verified:e.verified_by.length>0,residualLevel:e.residual_level??gt,severity:Ur(e.severity)})),c=(r=n.parentProps,{hasRows:(i=l).length>0,scopeStated:_r(r),overallJudged:""!==ur(r).residual_risk,everyRowSettled:i.every(e=>e.linked&&e.residualLevel!==gt)}),p=function(e,t,r,i){const a=[];for(const n of Nr(e)){if("analysis"===n.scope){if(!i||!n.matchesAnalysis||!n.matchesAnalysis(i))continue;a.push({kind:n.kind,label:n.label(1),rowIds:[],reqIds:[],count:1,fix:n.fix,fixLabel:n.fixLabel});continue}if("requirement"===n.scope){if(0===r.length)continue;a.push({kind:n.kind,label:n.label(r.length),rowIds:[],reqIds:[...r],count:r.length,fix:n.fix,fixLabel:n.fixLabel});continue}const e=n.matches;if(!e)continue;const s=t.filter(t=>e(t)).map(e=>e.id);0!==s.length&&a.push({kind:n.kind,label:n.label(s.length),rowIds:s,reqIds:[],count:s.length,fix:n.fix,fixLabel:n.fixLabel})}return a}(n.schemeId,l,d,c),m=p.map(e=>({kind:e.kind,label:e.label,row_ids:e.rowIds,req_ids:e.reqIds,gap_count:e.count,fix:e.fix,fix_label:e.fixLabel}));return Ei(ai({analysis_id:n.analysis.id,subtype:n.analysis.subtype,scheme:n.analysis.scheme,labels:n.analysis.labels,gaps:s,count:(t=p,t.reduce((e,t)=>e+t.count,0)),gap_kinds:m,system_level:o.level,system_level_source:o.source}))}catch(e){return Ii(vi(e))}var t,r,i}function wo(e){return(e.failure_mode??"").trim()||(e.hazard??"").trim()||""}async function vo(e){try{const t=await qe(),r=De();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const i=await Ti(t,e.project_id,e.analysis_id);if(!i)return Ii(oi("Analysis",e.analysis_id));if("analysis"!==i.type)return Ii(li(`Item ${e.analysis_id} is not an analysis (type='${i.type}')`));const a=i.data,n=a?.analysis?.subtype,s="hara"===n?"hara":"dfmea"===n?"dfmea":null;if(!s)return Ii(li(`Analysis ${e.analysis_id} is missing a valid analysis.subtype`));const{variant:o,schemeId:d}=await lo(t,e.project_id,s,a);if("hara"===s&&e.source_req)return Ii(li("A risk analysis row is seeded from a function, never a requirement: pass source_function"));if("dfmea"===s&&e.source_function)return Ii(li("source_function applies to a risk analysis only; seed an FMEA row with source_req"));const l=Array.isArray(e.rows)?e.rows:[];if(0===l.length)return Ii(li("rows must contain at least one drafted row"));if(l.length>ht)return Ii(li(`Propose rows accepts at most ${ht} rows per call; ${l.length} were sent. Split the proposal and call again.`));const c=[];for(let e=0;e<l.length;e++){const t=po(d,0,l[e]??{},{extraKeys:"hara"===s?["effect","title"]:["effect"],where:`row ${e+1}`});if(!t.ok)return Ii(li(t.message));uo(t.writes),c.push(t.writes)}let p=ki(e.source_function??"").trim();if("hara"===s&&p){const[r]=await so(t,e.project_id,[p]);r&&(p=r)}const m=(new Date).toISOString(),u=Mt[s],_=[],f=[];for(const i of c){const a=await ro(t,e.project_id,Ht[d]),n={source_req:"dfmea"===s?e.source_req??null:null,..."hara"===s?{source_function:p||null}:{},approved:!1,approved_at:null,approved_by:null,field_source:{}};let o="",l="";for(const[e,t]of Object.entries(i))"effect"===e?o=t:"title"===e?l=t:(n[e]=t,"failure_mode"===e&&"dfmea"===s&&(l=t)),n.field_source[e]="mcp";l||(l=wo(i));const c={id:a,type:"requirement",title:l,body:o,req_type:u,tags:{domains:[],level:""},ownership:{primary:null,additional:[]},relationships:{parents:[e.analysis_id],children:[],related:[],verifies:[],verified_by:[]},links:[],analysis_props:n,metadata:{created_at:m,created_by:r,updated_at:m,updated_by:r}},{error:h}=await t.rpc("save_item_with_revision",{p_item_id:a,p_project_id:e.project_id,p_type:"requirement",p_title:l,p_data:c,p_user_id:r,p_event_source:"mcp",p_event_session_id:Po()??null,p_diff_summary:null});if(h)throw new Error(h.message);const{error:g}=await t.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:a,p_to_id:e.analysis_id,p_type:"parent",p_action:"add",p_user_id:r,p_event_source:"mcp"});g&&(process.stderr.write(`[atoms-mcp] Warning: proposed row parent link sync failed for ${a}: ${g.message}\n`),f.push({row_id:a,message:g.message})),await t.from("change_history").insert({item_id:a,project_id:e.project_id,changed_by:r,event_type:"created",old_data:null,new_data:c,actor:To(),session_id:Po(),reason:"Row proposed through atoms_propose_rows (machine-proposed, unapproved)"}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),_.push({id:a,written_fields:Object.keys(i),computed_level:Rr(d,n)})}return Ei(ai({analysis_id:e.analysis_id,subtype:s,scheme:d,...o?{variant:o}:{},labels:Dr(d),..."hara"===s?{source_function:p||null}:{source_req:e.source_req??null},rows:_,count:_.length,max_rows:ht,approved:!1,...f.length>0?{link_warnings:f,warning:`${f.length} proposed row(s) were created but could not be attached to ${e.analysis_id}. Repair: call atoms_link_items with type 'parent', from_id=<row id>, to_id=${e.analysis_id}.`}:{}}))}catch(e){return Ii(vi(e))}}async function bo(e){try{const t=await qe(),r=De();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const i=await Ti(t,e.project_id,e.row_id);if(!i)return Ii(oi("Analysis row",e.row_id));const a=i.data,n=to(a?.req_type);if(!n)return Ii(li(`Item ${e.row_id} is not an analysis row (req_type must be 'safety-goal' or 'failure-mode')`));const{schemeId:s}=await co(t,e.project_id,n,a),o=e.rationale?function(e,t){return At[e][t]?"hara_iso26262"===e?"severity"===t?"s_rationale":"exposure"===t?"e_rationale":"controllability"===t?"c_rationale":null:"hazard_iso14971"===e?"severity"===t||"residual_s"===t?"s_rationale":"p1"===t||"residual_p1"===t?"p1_rationale":"p2"===t||"residual_p2"===t?"p2_rationale":null:"rationale":null}(s,e.field):null;if(e.rationale&&!o)return Ii(li(`rationale applies to a rating field only; ${e.field} is not a rating of ${s}`));const d={[e.field]:e.value};o&&e.rationale&&(d[o]=e.rationale);const l=po(s,0,d,{extraKeys:"hara"===n?["effect","title"]:["effect"]});if(!l.ok)return Ii(li(l.message));const c=l.writes;uo(c);const p=no(a);p.analysis_props&&"object"==typeof p.analysis_props||(p.analysis_props={source_req:null,approved:!1,approved_at:null,field_source:{}});const m=p.analysis_props;m.field_source&&"object"==typeof m.field_source||(m.field_source={});let u=i.title;for(const[e,t]of Object.entries(c))"effect"===e?p.body=t:"title"===e?(p.title=t,u=t):(m[e]=t,"failure_mode"===e&&"dfmea"===n&&(p.title=t,u=t)),m.field_source[e]="mcp";const _=Lr(m),f=(new Date).toISOString();p.metadata={...p.metadata??{},updated_at:f,updated_by:r};const{data:h,error:g}=await t.rpc("save_item_with_revision",{p_item_id:i.id,p_project_id:e.project_id,p_type:i.type,p_title:u,p_data:p,p_user_id:r,p_event_source:"mcp",p_event_session_id:Po()??null,p_diff_summary:null,p_expected_updated_at:i.updated_at});if(g)throw new Error(g.message);if(!h)return Ii(si(`Suggestion silently failed for '${e.row_id}': save RPC returned no row. Likely RLS denied the write.`,["Verify you have editor or admin role in this project","Retry after checking project access"]));await t.from("change_history").insert({item_id:i.id,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:a,new_data:p,actor:To(),session_id:Po(),reason:`${e.field} suggested through atoms_suggest_field (machine-proposed, unapproved)`+(_?"; approval cleared (a signature never covers content the approver did not see)":"")}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)});const y=Object.entries(m.field_source).filter(([,e])=>"mcp"===e).map(([e])=>e).sort();return Ei(ai({row_id:i.id,subtype:n,scheme:s,field:e.field,field_source:"mcp",written_fields:Object.keys(c),...o?{rationale_field:o}:{},..._?{approval_cleared:!0}:{},row:{id:i.id,computed_level:Rr(s,m),approved:!0===m.approved,mcp_fields:y,...Object.fromEntries(Et[s].map(e=>[e,m[e]??null]))}}))}catch(e){return Ii(vi(e))}}async function jo(e){try{const t=await qe(),r=De();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return Ii(di("Viewer"));throw e}const i=await Ti(t,e.project_id,e.row_id);if(!i)return Ii(oi("Analysis row",e.row_id));const a=i.data,n=to(a?.req_type);if(!n)return Ii(li(`Item ${e.row_id} is not an analysis row (req_type must be 'safety-goal' or 'failure-mode')`));const s=e.approver_id??r,o=await async function(e,t,r){const{data:i,error:a}=await e.from("projects").select("org_id").eq("id",t).maybeSingle();if(a)throw new Error(a.message);if(!i)throw new Error(`Project '${t}' not found`);const{data:n,error:s}=await e.from("org_members").select("role").eq("user_id",r).eq("org_id",i.org_id).maybeSingle();if(s)throw new Error(s.message);const o=n?.role;return"string"==typeof o?o:null}(t,e.project_id,s);if(!o)return Ii(li(`Approver '${s}' is not a member of this project's organization. Approve as a project member (omit approver_id to approve as yourself).`));const{schemeId:d}=await co(t,e.project_id,n,a),l=a?.analysis_props??{},c=Rr(d,l);if(c===gt)return Ii(li(`${e.row_id} cannot be approved before it is rated: complete the rating fields first (the computed ${"hara_iso26262"===d?"ASIL":"hazard_iso14971"===d?"risk":"RPN"} is unset).`));const p=no(a);p.analysis_props&&"object"==typeof p.analysis_props||(p.analysis_props={source_req:null,approved:!1,approved_at:null,field_source:{}});const m=p.analysis_props;m.field_source&&"object"==typeof m.field_source||(m.field_source={});const u=ki(e.note??""),_=(new Date).toISOString();m.approved=!0,m.approved_at=_,m.approved_by=s,m.approved_note=u;const f=[];for(const[e,t]of Object.entries(m.field_source))"mcp"===t&&(m.field_source[e]="user",f.push(e));f.sort(),p.metadata={...p.metadata??{},updated_at:_,updated_by:r};const{data:h,error:g}=await t.rpc("save_item_with_revision",{p_item_id:i.id,p_project_id:e.project_id,p_type:i.type,p_title:i.title,p_data:p,p_user_id:r,p_event_source:"mcp",p_event_session_id:Po()??null,p_diff_summary:null,p_expected_updated_at:i.updated_at});if(g)throw new Error(g.message);return h?(await t.from("change_history").insert({item_id:i.id,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:{approved:!0===l.approved,approved_at:l.approved_at??null,approved_by:l.approved_by??null,approved_note:l.approved_note??""},new_data:{approved:!0,approved_at:_,approved_by:s,approved_note:u},actor:To(),session_id:Po(),reason:`${e.row_id} approved by ${s} via atoms_approve_row`}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),Ei(ai({row_id:i.id,subtype:n,scheme:d,approved:!0,approved_by:s,approved_at:_,approved_note:u,approver_role:o,computed_level:c,cleared_markers:f}))):Ii(si(`Approval silently failed for '${e.row_id}': save RPC returned no row. Likely RLS denied the write.`,["Verify you have editor or admin role in this project","Retry after checking project access"]))}catch(e){return Ii(vi(e))}}E(eo,{approveRowHandler:()=>jo,createAnalysisHandler:()=>mo,fillRowHandler:()=>_o,getAnalysisHandler:()=>ho,getGapsHandler:()=>yo,linkMitigationHandler:()=>go,proposeRowsHandler:()=>vo,registerFunctionTags:()=>so,suggestFieldHandler:()=>bo});var So,ko,xo,Ao,Eo,Io,Ro,Oo,qo,zo,Mo=A({"src/tools/analysis.ts"(){Ne(),Bi(),Co(),Oi(),Xr(),Zs=new Set(["safety-goal","failure-mode"])}}),Ho={};function Do(e){F(e.toolName)&&function(e,t){const r=`ui://${t.toolName}/${t.htmlFile}`,i=function(e,t){const r=s.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:m&&"SCRIPT"===m.tagName.toUpperCase()&&m.src||new URL("index.cjs",document.baseURI).href),i=j.default.dirname(r);return j.default.resolve(i,"apps","src","apps",e,t)}(t.appName,t.htmlFile),a=[...Ri.resourceDomains,...t.extraResourceDomains??[]];d.registerAppTool(e,t.toolName,{title:t.title,description:t.description,inputSchema:t.inputSchema,annotations:t.annotations,_meta:{ui:{resourceUri:r}}},t.handler),d.registerAppResource(e,`${t.title} UI`,r,{mimeType:d.RESOURCE_MIME_TYPE},async()=>{const e=await b.default.readFile(i,"utf-8");return{contents:[{uri:r,mimeType:d.RESOURCE_MIME_TYPE,text:e,_meta:{ui:{csp:{resourceDomains:a}}}}]}})}(Ao,e)}function $o(e,t){return async r=>{const i=O(U(),e);if(!i.allowed)return Ii(si(`Tool ${e} is blocked by org policy.`,[z(i.reason),"Contact your org admin to adjust the MCP access policy."]));try{!function(e){const t=st();if(!t)return;if("object"!=typeof e||null===e)return;const r=e.project_id;if("string"==typeof r&&r!==t)throw new it(t,r)}(r)}catch(e){if(e instanceof it)return Ii(mi(e.expectedProjectId));throw e}{const e=U(),t=r?.project_id;if(null!==e.project_scope&&"string"==typeof t&&!e.project_scope.includes(t))return Ii(si("Tool call rejected: project_id is outside your org's MCP project scope.",[`Allowed projects: ${e.project_scope.length} project${1===e.project_scope.length?"":"s"} configured by your admin`,"Use atoms_list_projects to see which projects you can access in this session"]))}let a,n;try{n=await qe(),a=De()}catch{return t(r)}const s=await async function(e,t){const r=parseInt(process.env.ATOMS_RATE_LIMIT_RPM??"",10)||Xe,i=new Date,a=new Date(i.getTime()-et);try{const{count:n,error:s}=await t.from("mcp_rate_limits").select("id",{count:"exact",head:!0}).eq("user_id",e).gte("requested_at",a.toISOString());if(s)throw new Error(s.message);if((n??0)>=r){const{data:r,error:n}=await t.from("mcp_rate_limits").select("requested_at").eq("user_id",e).gte("requested_at",a.toISOString()).order("requested_at",{ascending:!0}).limit(1).maybeSingle();if(n)throw new Error(n.message);const s=(r?new Date(r.requested_at).getTime():i.getTime())+et-i.getTime();return{allowed:!1,retryAfterSeconds:Math.max(1,Math.ceil(s/1e3))}}t.from("mcp_rate_limits").insert({user_id:e,requested_at:i.toISOString()}).then(({error:e})=>{e&&process.stderr.write(`[rate-limiter] insert failed: ${e.message}\n`)});const o=new Date(i.getTime()-2*et);return t.from("mcp_rate_limits").delete().eq("user_id",e).lt("requested_at",o.toISOString()).then(()=>{}),{allowed:!0}}catch(e){return process.stderr.write(`[rate-limiter] DB check failed (${e instanceof Error?e.message:String(e)}), falling back to allow.\n`),{allowed:!0}}}(a,n);if(!s.allowed)return Ii(ui(s.retryAfterSeconds));if("1"===process.env.ATOMS_MCP_LOCKDOWN||"true"===process.env.ATOMS_MCP_LOCKDOWN){const e=r?.project_id;if("string"==typeof e)try{const{requireWriteAccess:t}=await Promise.resolve().then(()=>(Ne(),Oe));await t(n,e)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message){const{accessDeniedError:e}=await Promise.resolve().then(()=>(Oi(),ti));return Ii(e("Viewer (lockdown mode: read access requires editor or admin)"))}}}const o=function(){const e=performance.now();return()=>Math.round(performance.now()-e)}();let d,l="success";try{const e=await t(r),i=e;if(!0===i?.isError){l="error";const e=i?.content;if(e?.[0]?.text)try{d=JSON.parse(e[0].text).message}catch{}}return e}catch(e){throw l="error",d=e instanceof Error?e.message:String(e),e}finally{const t={tool_name:e,params:r,status:l,duration_ms:o(),error_msg:d,project_id:r.project_id,session_id:Io,client_name:Ro};try{!function(e,t,r){const i=Ze(r.params);e.from("mcp_audit_log").insert({user_id:t,tool_name:r.tool_name,params:i,status:r.status,duration_ms:r.duration_ms,error_msg:r.error_msg??null,project_id:r.project_id??null,session_id:r.session_id??null,client_name:r.client_name??null}).then(({error:e})=>{e&&process.stderr.write(`[audit] Failed to log: ${e.message}\n`)})}(await qe(),a,t)}catch{}}}}function Po(){return Io}function To(){return Oo}function Uo(){return Ro}E(Ho,{ATOMS_MCP_SERVER_VERSION:()=>xo,getMcpActor:()=>To,getMcpClientName:()=>Uo,getMcpSessionId:()=>Po,server:()=>Ao});var Co=A({"src/server.ts"(){at(),nt(),B(),dt(),C(),P(),rr(),ir(),Xr(),Ne(),Oi(),qi(),So=c.createRequire("undefined"==typeof document?require("url").pathToFileURL(__filename).href:m&&"SCRIPT"===m.tagName.toUpperCase()&&m.src||new URL("index.cjs",document.baseURI).href),ko=So("../package.json"),xo=ko.version,Ao=new l.McpServer({name:"atoms-mcp-server",version:xo}),Eo=(e,t,r)=>{if(F(e))return Ao.registerTool(e,t,r)},Io=n.randomUUID(),Ro=lt(process.env.ATOMS_CLIENT_NAME),Oo=function(){const e=lt(Ro);return"cursor"===e?"mcp_cursor":"copilot"===e?"mcp_copilot":"mcp_claude"}(),Eo("atoms_status",{title:"ATOMS MCP Health Check",description:'Check the health and authentication status of the ATOMS MCP server.\n\nCall this FIRST if other atoms tools fail or return auth errors.\nThis tool works WITHOUT authentication — it checks whether credentials\nexist and are valid, and tells the user exactly what to do if not.\n\nArgs: None\n\nReturns:\n { status: "authenticated"|"not_authenticated"|"expired", email, message, next_steps }\n\nExamples:\n - "Is ATOMS MCP working?" → atoms_status()\n - "Why are atoms tools failing?" → atoms_status()',inputSchema:{},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},async()=>{try{const{readCredentials:e,isTokenValid:t}=await Promise.resolve().then(()=>(oe(),J)),r=await e();if(!r)return{content:[{type:"text",text:JSON.stringify({status:"not_authenticated",message:"No ATOMS credentials found. You need to login first.",next_steps:["1. Open a terminal on your machine","2. Run: npx @atoms-tech/atoms-mcp login","3. Complete the login in your browser","4. Restart Claude Desktop (quit and reopen)","5. Try your query again"]},null,2)}]};if(!t(r))try{const{getValidToken:e}=await Promise.resolve().then(()=>(Re(),ve)),{email:t}=await e(),{invalidateClient:r}=await Promise.resolve().then(()=>(Ne(),Oe));return r(),{content:[{type:"text",text:JSON.stringify({status:"authenticated",email:t,message:"Token was expired but has been refreshed. You're good to go!"},null,2)}]}}catch{return{content:[{type:"text",text:JSON.stringify({status:"expired",email:r.user_email??"unknown",message:"Your session has expired and could not be refreshed.",next_steps:["1. Open a terminal on your machine","2. Run: npx @atoms-tech/atoms-mcp logout","3. Run: npx @atoms-tech/atoms-mcp login","4. Complete the login in your browser","5. Restart Claude Desktop (quit and reopen)"]},null,2)}]}}return{content:[{type:"text",text:JSON.stringify({status:"authenticated",email:r.user_email??"unknown",message:"ATOMS MCP is connected and authenticated. All tools are ready."},null,2)}]}}catch(e){return{content:[{type:"text",text:JSON.stringify({status:"error",message:`Health check failed: ${e instanceof Error?e.message:String(e)}`,next_steps:["1. Open a terminal","2. Run: npx @atoms-tech/atoms-mcp login","3. Restart Claude Desktop"]},null,2)}]}}}),Eo("atoms_list_projects",{title:"List ATOMS Projects",description:'List all projects the authenticated user has access to.\n\nThis is the ENTRY POINT tool. Call this first to discover project IDs,\nthen use those IDs with all other tools.\n\nArgs: None\n\nReturns:\n { status: "success", data: [{ id, name, description, org_id, created_at }] }\n\nExamples:\n - "What projects do I have?" → atoms_list_projects()\n - "Show my projects" → atoms_list_projects()\n\nWorkflow:\n atoms_list_projects → atoms_list_items(project_id) → atoms_get_item(project_id, item_id)',inputSchema:{},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_list_projects",async()=>{const{listProjectsHandler:e}=await Promise.resolve().then(()=>(Gi(),Ji));return e()})),Eo("atoms_list_items",{title:"List ATOMS Items",description:'List items in an ATOMS project with optional type/domain/level filters.\n\nThis is the primary ENTRY POINT for discovering items. Use it to browse\nproject contents before drilling into specific items with atoms_get_item.\n\nArgs:\n - project_id (string, UUID): The project to list items from\n - type (string, optional): Filter: requirement|test-case|note|table\n - domain (string, optional): Filter by domain tag\n - level (string, optional): Filter by level (System, Subsystem, Component)\n - limit (number, optional): Max results, 1-200 (default 50)\n - offset (number, optional): Pagination offset (default 0)\n\nReturns:\n { status: "success", data: [{ id, title, type, status, domains, level }], meta: { total_count, limit, offset, has_more } }\n\nExamples:\n - "Show me all requirements" → atoms_list_items(project_id, type="requirement")\n - "What test cases exist?" → atoms_list_items(project_id, type="test-case")\n\nErrors:\n - "Project not found" → verify project_id\n - "Access denied" → check org membership',inputSchema:{project_id:o.z.string().uuid("Must be a valid project UUID").describe("UUID of the project"),type:o.z.enum(["requirement","test-case","note","table","analysis"]).optional().describe("Filter by item type"),domain:o.z.string().max(64).optional().describe("Filter by domain tag (e.g., 'Safety', 'Performance')"),level:o.z.string().max(32).optional().describe("Filter by level (System, Subsystem, Component)"),limit:o.z.number().int().min(1).max(200).default(50).describe("Max results (default 50, max 200)"),offset:o.z.number().int().min(0).default(0).describe("Pagination offset (default 0)"),baseline_id:o.z.string().uuid().optional().describe("Optional baseline UUID. Returns items as they were at that baseline's moment. Mutually exclusive with as_of."),as_of:o.z.string().datetime().optional().describe("Optional ISO 8601 timestamp. Returns items as they were at that moment. Mutually exclusive with baseline_id.")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_list_items",async e=>{const{listItemsHandler:t}=await Promise.resolve().then(()=>(Xi(),Ki));return t(e)})),Eo("atoms_get_item",{title:"Get ATOMS Item Details",description:'Get full details of a single item including relationships, test history, and ownership.\n\nUse after atoms_list_items or atoms_search to drill into a specific item.\n\nArgs:\n - project_id (string, UUID): The project containing the item\n - item_id (string): Item ID (e.g., "REQ-001", "TC-050")\n\nReturns:\n Full WorkItem with relationships, test runs, ownership, metadata.\n\nExamples:\n - "Show me requirement REQ-001" → atoms_get_item(project_id, "REQ-001")\n - "Get test case details" → atoms_get_item(project_id, "TC-050")',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().min(1).max(20).describe("Item ID (e.g., REQ-001)"),baseline_id:o.z.string().uuid().optional().describe("Optional baseline UUID. Returns the item as it was at that baseline's moment. Mutually exclusive with as_of."),as_of:o.z.string().datetime().optional().describe("Optional ISO 8601 timestamp. Returns the item as it was at that moment. Mutually exclusive with baseline_id.")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_get_item",async e=>{const{getItemHandler:t}=await Promise.resolve().then(()=>(ia(),ea));return t(e)})),Eo("atoms_search",{title:"Search ATOMS Items",description:'Full-text search across items in a project. Searches title, body, and summary.\n\nAlternative entry point to atoms_list_items when you know what you\'re looking for.\n\nArgs:\n - project_id (string, UUID): The project to search in\n - query (string): Search text (max 1000 chars)\n - type (string, optional): Filter by type\n - asset (string, optional): Filter to items with at least one run on the given asset\n (VIN, module, lot, ...). Case-sensitive exact match.\n - limit (number, optional): Max results, 1-100 (default 25)\n\nReturns:\n Matching items with @basic fields, ranked by relevance.\n\nExamples:\n - "Find braking requirements" → atoms_search(project_id, "braking system")\n - "Search for safety tests" → atoms_search(project_id, "safety", type="test-case")\n - "Tests that ran on this VIN" → atoms_search(project_id, "", asset="WEV...0005")',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),query:o.z.string().min(1).max(1e3).describe("Search text"),type:o.z.enum(["requirement","test-case","note","table","analysis"]).optional().describe("Filter by item type"),asset:o.z.string().max(200).optional().describe("Filter to items with at least one run on this asset (case-sensitive exact)"),limit:o.z.number().int().min(1).max(100).default(25).describe("Max results (default 25, max 100)"),baseline_id:o.z.string().uuid().optional().describe("Optional baseline UUID. Searches within the project state at that baseline's moment.")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_search",async e=>{const{searchHandler:t}=await Promise.resolve().then(()=>(sa(),aa));return t(e)})),Eo("atoms_semantic_search",{title:"Semantic Search ATOMS Items",description:'Meaning-based search across items using pgvector embeddings (GTE-small, 384d).\n\nUse when atoms_search (exact/substring) misses conceptually related items.\nBest for natural-language concept queries; atoms_search is better for exact phrases or IDs.\n\nArgs:\n - project_id (string, UUID): The project to search in\n - query (string): Natural-language concept query (max 1000 chars)\n - type (string, optional): Filter by type\n - limit (number, optional): Max results, 1-50 (default 10)\n\nReturns:\n Semantically similar items ranked by cosine similarity.\n\nExamples:\n - "Find braking-related requirements" → atoms_semantic_search(project_id, "braking deceleration stopping distance")\n - "Safety-related test cases" → atoms_semantic_search(project_id, "hazard risk mitigation safety", type="test-case")',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),query:o.z.string().min(1).max(1e3).describe("Natural-language concept query"),type:o.z.enum(["requirement","test-case","note","table","analysis"]).optional().describe("Filter by item type"),limit:o.z.number().int().min(1).max(50).default(10).describe("Max results (default 10, max 50)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_semantic_search",async e=>{const{semanticSearchHandler:t}=await Promise.resolve().then(()=>(la(),oa));return t(e)})),Do({appName:"coverage",htmlFile:"coverage-app.html",toolName:"atoms_get_coverage",title:"Get Test Coverage Report",description:'Find requirements without linked test cases (coverage gaps).\n\nEssential for compliance reporting in safety-critical industries.\n\nIn MCP App hosts (Claude, ChatGPT), renders a visual coverage heatmap.\nFalls back to JSON for non-UI clients.\n\nArgs:\n - project_id (string, UUID): The project to analyze\n - domain (string, optional): Filter by domain\n - level (string, optional): Filter by level\n - baseline_id (string, UUID, optional): coverage at that baseline\'s moment\n - as_of (string ISO 8601, optional): coverage at that moment\n baseline_id and as_of are mutually exclusive.\n\nReturns:\n { covered, uncovered, total, coverage_percent, uncovered_items: [...] }\n\nExamples:\n - "What\'s our test coverage?" → atoms_get_coverage(project_id)\n - "Safety coverage gaps?" → atoms_get_coverage(project_id, domain="Safety")\n - "Coverage at PDR submission" → atoms_get_coverage(project_id, baseline_id="...")',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),domain:o.z.string().max(64).optional().describe("Filter by domain tag"),level:o.z.string().max(32).optional().describe("Filter by level"),baseline_id:o.z.string().uuid().optional().describe("Compute coverage as of this baseline's snapshot moment"),as_of:o.z.string().datetime().optional().describe("Compute coverage as of this ISO 8601 moment")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:$o("atoms_get_coverage",async e=>{const{getCoverageHandler:t}=await Promise.resolve().then(()=>(ua(),ca));return t(e)})}),Eo("atoms_verification_status",{title:"Get Release Verification Status",description:'Release-bound verification state per requirement, matching the in-app projection.\n\nEach requirement\'s state is derived from the runs bound to a formal Release:\nverified, failing, incomplete, or not-run, plus a stale flag and the\n(points_run / points_total) counts for its parameterized test points.\n\nArgs:\n - project_id (string, UUID): The project to report on\n - release_id (string, UUID, optional): A formal Release (a baselines row with\n a release_version). Defaults to the project\'s CURRENT Release\n (release_status = \'released\'). Errors if the project has none.\n - include_gaps (boolean, optional, default false): Also return the not-run\n (test x unit x value) points for the Release.\n\nReturns:\n { release: {id, version, status}, readiness: {...roll-up counts...},\n requirements: [{requirement_id, state, stale, points_total, points_run}],\n gaps?: [{tc_id, asset, point_key}] }\n An empty requirements array with a "hint" field means the projection has not\n been rebuilt for this project yet, NOT that everything is unverified.\n\nExamples:\n - "Are we ready to ship 2.0?" → atoms_verification_status(project_id)\n - "What\'s still not run for release R?" → atoms_verification_status(project_id, release_id="...", include_gaps=true)',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),release_id:o.z.string().uuid().optional().describe("Formal Release (baselines row) UUID; defaults to the project's current Release"),include_gaps:o.z.boolean().default(!1).describe("Include the not-run (test x unit x value) points for the Release")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_verification_status",async e=>{const{verificationStatusHandler:t}=await Promise.resolve().then(()=>(ha(),_a));return t(e)})),Eo("atoms_get_history",{title:"Get Item Change History",description:"Audit trail for an item — who changed what, when, and whether it was human or AI.\n\nShows actor attribution (user vs mcp_<client>) and session grouping.\n\nArgs:\n - project_id (string, UUID): The project containing the item\n - item_id (string): Item ID\n - limit (number, optional): Max entries (default 20, max 100)\n\nReturns:\n Change entries with actor, session_id, event_type, changed_at, fields_changed.",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().min(1).max(20).describe("Item ID"),limit:o.z.number().int().min(1).max(100).default(20).describe("Max entries (default 20)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_get_history",async e=>{const{getHistoryHandler:t}=await Promise.resolve().then(()=>(ba(),wa));return t(e)})),Do({appName:"mermaid",htmlFile:"mermaid-app.html",toolName:"atoms_export_mermaid",title:"Export Mermaid Diagram",description:"Generate a Mermaid diagram of the requirement/test hierarchy.\n\nOutput is paste-ready for markdown docs. Shows parent-child relationships\nand requirement-to-test-case verification links.\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive pan/zoom diagram.\nFalls back to Mermaid text for non-UI clients.\n\nArgs:\n - project_id (string, UUID): The project to visualize\n - root_item_id (string, optional): Start node (default: all roots)\n - depth (number, optional): Max depth (default 3, max 10)\n - include_tests (boolean, optional): Show test case links (default true)\n - baseline_id (string, UUID, optional): render the graph at that baseline's moment\n - as_of (string ISO 8601, optional): render the graph at that moment\n baseline_id and as_of are mutually exclusive.\n\nReturns:\n Mermaid graph string.",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),root_item_id:o.z.string().max(20).optional().describe("Root item ID to start from"),depth:o.z.number().int().min(1).max(10).default(3).describe("Max depth"),include_tests:o.z.boolean().default(!0).describe("Include test case links"),baseline_id:o.z.string().uuid().optional().describe("Render the graph as of this baseline's snapshot moment"),as_of:o.z.string().datetime().optional().describe("Render the graph as of this ISO 8601 moment")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:$o("atoms_export_mermaid",async e=>{const{exportMermaidHandler:t}=await Promise.resolve().then(()=>(qa(),Ea));return t(e)})}),Eo("atoms_create_item",{title:"Create ATOMS Item",description:'Create a new requirement, test case, or note in a project.\n\nFor a hazard analysis, HARA, DFMEA or FMEA, use atoms_create_analysis instead (it creates the analysis parent and its rows). This tool does not create analysis items; do not model a hazard analysis as notes with custom fields.\n\nRequires editor or admin role. Changes are logged with AI actor attribution.\n\nArgs:\n - project_id (string, UUID): Target project\n - type (string): requirement|test-case|note\n - req_type (string, optional): requirement classification (functional|interface|performance|safety)\n - title (string): Item title (max 500 chars)\n - body (string, optional): Rich text body\n - summary (string, optional): Brief summary\n - domains (string[], optional): Domain tags (a tag flagged as a function is not a domain and is rejected)\n - level (string, optional): System|Subsystem|Component\n - parent_ids (string[], optional): Parent item IDs to link\n - fields (object, optional): Project-field values, e.g. {"Verification Method": "Design Review"}. Keys must be defined via atoms_create_field first and values must match each field type.\n\nReturns:\n Created item with generated ID (e.g., "REQ-058")\n\nSide effects:\n - Logs to change_history with actor=\'mcp_<client>\'\n - Creates relationships if parent_ids provided',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the target project"),type:o.z.enum(["requirement","test-case","note"]).describe("Item type"),title:o.z.string().min(1).max(500).describe("Item title"),req_type:o.z.enum(["functional","interface","performance","safety","safety-goal","failure-mode"]).optional().describe("Requirement classification (functional|interface|performance|safety). safety-goal and failure-mode are analysis rows created by atoms_create_analysis, not here."),body:o.z.string().max(5e4).optional().describe("Rich text body"),summary:o.z.string().max(2e3).optional().describe("Brief summary"),domains:o.z.array(o.z.string().max(64)).max(20).optional().describe("Domain tags"),level:o.z.string().max(32).optional().describe("System|Subsystem|Component"),parent_ids:o.z.array(o.z.string().max(20)).max(50).optional().describe("Parent item IDs to link"),fields:o.z.record(o.z.string().max(60),o.z.string().max(1e4)).optional().describe("Project-field values keyed by defined field name"),echo:o.z.boolean().optional().describe("false → lean {id,type} response, saves ~580 tokens")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$o("atoms_create_item",async e=>{const{createItemHandler:t}=await Promise.resolve().then(()=>(kn(),jn));return t(e)})),qo={steps:o.z.array(o.z.object({id:o.z.string().describe("Stable step id within the item"),instruction:o.z.string().describe("What the tester does"),test_data:o.z.string().optional().describe("Optional test data for the step"),expected:o.z.string().describe("Expected result")}).passthrough()).optional().describe("Test-case script steps: array of {id, instruction, test_data?, expected}. Max 50 steps, enforced by the DB trigger."),params:o.z.array(o.z.object({name:o.z.string().describe("Parameter name; steps reference it as {name}"),unit:o.z.string().optional().describe("Optional unit (e.g. km/h, V, m)"),values:o.z.array(o.z.union([o.z.string(),o.z.number()])).describe("Value set the test sweeps")}).passthrough()).optional().describe("Matrix parameters: array of {name, unit, values[]}. Matrix points (product of value-set sizes) capped at 64 by the DB trigger.")},Eo("atoms_update_item",{title:"Update ATOMS Item",description:'Update an existing item\'s fields. Only provided fields are changed.\n\nRequires editor or admin role. Logs old/new data diff to change history.\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): Item to update\n - title, body, summary, domains, level, status, req_type: Fields to update (all optional) (a tag flagged as a function is not a domain and is rejected)\n - fields (object, optional): Project-field values, e.g. {"Verification Method": "Design Review"}. Keys must be defined via atoms_create_field; merged per key, "" clears a key; non-empty values must match each field type\n - steps (array, optional): Test-case script steps [{id, instruction, test_data?, expected}]. Max 50 steps (DB-enforced)\n - params (array, optional): Matrix parameters [{name, unit, values[]}]. Matrix points capped at 64 (DB-enforced)\n - expected_updated_at (string, optional): Pass metadata.updated_at from atoms_get_item to reject stale writes\n\nReturns:\n Updated item.',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().min(1).max(20).optional().describe("Item ID (single form)"),title:o.z.string().min(1).max(500).optional().describe("New title"),body:o.z.string().max(5e4).optional().describe("New body"),summary:o.z.string().max(2e3).optional().describe("New summary"),domains:o.z.array(o.z.string().max(64)).max(20).optional().describe("New domain tags (a tag flagged as a function is not a domain and is rejected)"),level:o.z.string().max(32).optional().describe("New level"),status:o.z.enum(["passed","failed","blocked","not-run"]).optional().describe("Test case status"),req_type:o.z.enum(["functional","interface","performance","safety","safety-goal","failure-mode"]).optional().describe("Requirement classification (functional|interface|performance|safety). safety-goal and failure-mode are analysis rows created by atoms_create_analysis, not here."),fields:o.z.record(o.z.string().max(60),o.z.string().max(1e4)).optional().describe('Project-field values keyed by defined field name; "" clears a key'),...qo,expected_updated_at:ct.optional().describe("metadata.updated_at value from atoms_get_item; rejects stale writes"),echo:o.z.boolean().optional().describe("false → lean {id,updated_fields} response, saves ~580 tokens"),reason:o.z.string().max(500).optional().describe("Change rationale, persisted in audit history"),updates:o.z.array(o.z.object({item_id:o.z.string().min(1).max(20),title:o.z.string().min(1).max(500).optional(),body:o.z.string().max(5e4).optional(),summary:o.z.string().max(2e3).optional(),domains:o.z.array(o.z.string().max(64)).max(20).optional(),level:o.z.string().max(32).optional(),status:o.z.enum(["passed","failed","blocked","not-run"]).optional(),req_type:o.z.enum(["functional","interface","performance","safety","safety-goal","failure-mode"]).optional(),fields:o.z.record(o.z.string().max(60),o.z.string().max(1e4)).optional(),...qo,expected_updated_at:ct.optional()})).optional().describe("Array form: update many items in one call"),idempotency_key:o.z.string().max(128).optional().describe("Safe retry key (24h TTL)"),continue_on_error:o.z.boolean().optional().describe("Allow partial success (default false = atomic)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_update_item",async e=>{const{updateItemHandler:t}=await Promise.resolve().then(()=>(qn(),In));return t(e)})),Eo("atoms_delete_item",{title:"Delete ATOMS Item",description:'Soft-delete an item (sets deleted_at, preserves for audit trail).\n\nRequires editor or admin role. The item is never truly removed.\n\nWhen ATOMS_MCP_REQUIRE_CONFIRMATION is set on the server, this tool uses a\ntwo-step flow: the first call returns a preview + confirmation_token; the\nsecond call (with the token) executes. Tokens expire in 60s and are bound\nto (tool, project_id, item_id).\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): Item to delete\n - confirmation_token (string, optional): Token from a prior preview call\n\nReturns:\n Confirmation with deleted item summary, or { status: "confirmation_required",\n preview, confirmation_token } when the confirmation gate is active.',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().min(1).max(20).describe("Item ID to soft-delete"),confirmation_token:o.z.string().max(500).optional().describe("Token from a prior preview call (only used when ATOMS_MCP_REQUIRE_CONFIRMATION is set)")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!1}},$o("atoms_delete_item",async e=>{const{deleteItemHandler:t}=await Promise.resolve().then(()=>(Hn(),zn));return t(e)})),Eo("atoms_restore_item",{title:"Restore ATOMS Item",description:'Undo a soft-delete — restores an item that was previously deleted with atoms_delete_item.\n\nOnly works on items that have been soft-deleted (still in the database with deleted_at set).\nHas no effect if the item is not deleted. Logs the restoration to change_history.\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): ID of the deleted item to restore\n\nReturns:\n Confirmation with the restored item\'s title and type.\n\nExamples:\n - "Restore the deleted requirement REQ-042" → atoms_restore_item(project_id, "REQ-042")',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().max(256).describe("ID of the soft-deleted item to restore")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_restore_item",async e=>{const{restoreItemHandler:t}=await Promise.resolve().then(()=>(Pn(),Dn));return t(e)})),Eo("atoms_link_items",{title:"Link ATOMS Items",description:"Add or remove relationships between items.\n\nSupports: parent, child, related, verifies, verified_by.\nUpdates both items' JSONB data and the shadow table.\n\nArgs:\n - project_id (string, UUID): Project containing both items\n - from_id (string): Source item\n - to_id (string): Target item\n - type (string): parent|child|related|verifies|verified_by\n - action (string): add|remove\n - expected_from_updated_at / expected_to_updated_at (string, optional): pass metadata.updated_at values from atoms_get_item to reject stale link commands\n\nReturns:\n Updated relationship state for both items.",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),from_id:o.z.string().min(1).max(20).optional().describe("Source item ID (single form)"),to_id:o.z.string().min(1).max(20).optional().describe("Target item ID (single form)"),type:o.z.enum(["parent","child","related","verifies","verified_by"]).optional().describe("Relationship type"),action:o.z.enum(["add","remove"]).optional().describe("Add or remove"),expected_from_updated_at:ct.optional().describe("metadata.updated_at for from_id from atoms_get_item"),expected_to_updated_at:ct.optional().describe("metadata.updated_at for to_id from atoms_get_item"),echo:o.z.boolean().optional().describe("false → lean response"),reason:o.z.string().max(500).optional().describe("Change rationale, persisted in audit history"),operations:o.z.array(o.z.object({action:o.z.enum(["add","remove"]),from_id:o.z.string().min(1).max(20),to_id:o.z.string().min(1).max(20),type:o.z.enum(["parent","child","related","verifies","verified_by"]),expected_from_updated_at:ct.optional(),expected_to_updated_at:ct.optional()})).optional().describe("Array form: batch relationship ops. Always atomic with DAG cycle detection."),idempotency_key:o.z.string().max(128).optional().describe("Safe retry key (24h TTL)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_link_items",async e=>{const{linkItemsHandler:t}=await Promise.resolve().then(()=>(Cn(),Tn));return t(e)})),Do({appName:"import",htmlFile:"import-app.html",toolName:"atoms_bulk_import",title:"Bulk Import ATOMS Items",description:"Bulk create multiple items in a single tool call. Essential for AI agents\nthat generate 20+ requirements at once.\n\nChecks write access once, generates sequential IDs, batch inserts all items,\nand reports per-item errors without aborting the entire batch.\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive results table.\nFalls back to JSON for non-UI clients.\n\nArgs:\n - project_id (string, UUID): Target project\n - items (array): Up to 100 items to create, each with:\n - type (string): requirement|test-case|note\n - title (string): Item title (max 500 chars)\n - body (string, optional): Rich text body\n - summary (string, optional): Brief summary\n - domains (string[], optional): Domain tags (a tag flagged as a function is not a domain and is rejected)\n - level (string, optional): System|Subsystem|Component\n - parent_id (string, optional): Parent item ID to link\n\nReturns:\n { created: number, items: [{ id, title, type }], errors: [] }\n\nLimits:\n - Maximum 100 items per call\n - If any item fails, others still succeed — errors reported separately\n\nSide effects:\n - Logs to change_history with actor='mcp_<client>' for each created item\n - Creates parent relationships for items with parent_id",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the target project"),items:o.z.array(o.z.object({type:o.z.enum(["requirement","test-case","note"]).describe("Item type"),title:o.z.string().min(1).max(500).describe("Item title"),body:o.z.string().max(5e4).optional().describe("Rich text body"),summary:o.z.string().max(2e3).optional().describe("Brief summary"),domains:o.z.array(o.z.string().max(64)).max(20).optional().describe("Domain tags"),level:o.z.string().max(32).optional().describe("System|Subsystem|Component"),parent_id:o.z.string().max(20).optional().describe("Parent item ID to link")})).min(1).max(100).describe("Items to create (1-100)"),confirmation_token:o.z.string().max(500).optional().describe("Token from a prior preview call (only used when ATOMS_MCP_REQUIRE_CONFIRMATION is set)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},handler:$o("atoms_bulk_import",async e=>{const{bulkImportHandler:t}=await Promise.resolve().then(()=>(Vn(),Ln));return t(e)})}),zo={point:o.z.record(o.z.string(),o.z.union([o.z.string(),o.z.number()])).optional().describe('Matrix point this run executed, e.g. {"speed": 40}'),release_id:o.z.string().uuid().optional().describe("Formal Release to bind this run to (a baseline with a release_version)"),build:o.z.string().optional().describe("Free-text build label"),environment:o.z.string().optional().describe("Structured environment field, distinct from build"),tooling:o.z.string().optional().describe("Structured tooling field, distinct from build"),steps_rev:o.z.string().optional().describe("Item revision id the run's steps were pinned to"),step_results:o.z.array(o.z.object({step_id:o.z.string().describe("Step id from data.steps"),status:o.z.enum(["passed","failed","blocked","skipped"]).describe("Per-step status"),actual:o.z.string().optional().describe("Actual-result note"),evidence_url:o.z.string().optional().describe("Evidence URL (validated server-side)")}).passthrough()).optional().describe("Per-step results [{step_id, status, actual?, evidence_url?}]. When present the run verdict is derived server-side and the top-level result is ignored.")},Eo("atoms_record_test_result",{title:"Record Test Result",description:"Record a pass/fail/blocked result for a test case.\n\nAppends to canonical items.data.runs and projects test_results from that\nhistory. Use atoms_get_item to see all results.\n\nArgs:\n - project_id (string, UUID): Project containing the test case\n - item_id (string): Test case ID (e.g., \"TC-001\")\n - result (string): passed|failed|blocked|not-run\n - note (string, optional): Reason or comment for this result\n - asset (string, optional): Asset identifier this run was executed against\n (VIN, module ID, lot number, serial number, etc). Max 200 chars.\n Auto-trimmed; empty/whitespace is treated as omitted. If the asset\n name does not yet exist in the project, it is auto-registered.\n - point, release_id, build, environment, tooling, steps_rev (optional):\n run metadata. release_id must be a formal Release (a baseline with a\n release_version). All validated server-side.\n - step_results (array, optional): per-step outcomes for a stepped run.\n When provided, the verdict is derived server-side from the steps.\n\nReturns:\n Recorded result with timestamp. When asset is provided, also returns\n asset_id and asset_created.\n\nSide effects:\n - Appends to items.data.runs through save_item_with_revision\n - Projects public.test_results from items.data.runs\n - Inserts into assets table if asset name is new in this project\n - Logs to change_history with actor='mcp_<client>'",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().min(1).max(20).describe("Test case ID (e.g., TC-001)"),result:o.z.enum(["passed","failed","blocked","not-run"]).describe("Test result"),note:o.z.string().max(500).optional().describe("Reason or comment for this result"),asset:o.z.string().max(200).optional().describe("Asset identifier this run was executed against (VIN, module ID, lot, etc). Auto-registered if new."),...zo},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$o("atoms_record_test_result",async e=>{const{recordTestResultHandler:t}=await Promise.resolve().then(()=>(Qn(),Bn));return t(e)})),Eo("atoms_record_test_results",{title:"Record Test Results (bulk)",description:'Record many runs on one test case in a single atomic call.\n\nOptimized for multi-asset campaigns. Each entry can have its own asset and\nresult. Asset lookup-or-create happens per entry; new asset names are\nauto-registered. Runs are appended to canonical items.data.runs, then\ntest_results is projected from that history. The whole call is one DB\ntransaction; any entry failure rolls back the rest.\n\nUse the singular atoms_record_test_result for one-off runs.\n\nArgs:\n - project_id (string, UUID): Project containing the test case\n - item_id (string): Test case ID (e.g., "TC-001")\n - results (array, 1-100 entries): Each entry has:\n - asset (string, optional): Asset identifier (VIN, module ID, lot)\n - result (string, required): passed|failed|blocked|not-run\n - note (string, optional): Run note\n - date (string, optional): ISO 8601 datetime, default now()\n - point, release_id, build, environment, tooling, steps_rev (optional):\n run metadata; release_id must be a formal Release. Server-validated.\n - step_results (array, optional): per-step outcomes; when present the\n verdict is derived server-side from the steps.\n\nReturns:\n Per-entry outcomes with asset_id, asset_created, run_at, ok flags.\n\nSide effects:\n - Appends to items.data.runs through save_item_with_revision\n - Projects public.test_results from items.data.runs\n - Inserts new rows into assets for any asset name not already registered\n - Logs a single change_history entry summarizing the batch',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().min(1).max(20).describe("Test case ID (e.g., TC-001)"),results:o.z.array(o.z.object({asset:o.z.string().max(200).optional().describe("Asset identifier this run was executed against. Auto-registered if new."),result:o.z.enum(["passed","failed","blocked","not-run"]).describe("Test result"),note:o.z.string().max(500).optional().describe("Run note"),date:o.z.string().optional().describe("ISO 8601 datetime (default: now())"),...zo})).min(1).max(100).describe("1 to 100 result entries on this test case")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$o("atoms_record_test_results",async e=>{const{recordTestResultsBulkHandler:t}=await Promise.resolve().then(()=>(ts(),Gn));return t(e)})),Do({appName:"trace",htmlFile:"trace-app.html",toolName:"atoms_trace",title:"Trace Item Relationships",description:'Walk the traceability graph from a starting item.\n\nAnswers questions like "which requirements does TC-003 verify?" or\n"if I change REQ-001, what\'s affected downstream?"\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive graph visualization.\nFalls back to flat list for non-UI clients.\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): Starting item ID\n - direction (string): upstream|downstream|both\n - upstream: follow parent/verifies links (dependencies)\n - downstream: follow child/verified_by links (dependents)\n - both: trace in both directions\n - depth (number, optional): Max traversal depth (default 5, max 10)\n - relationship_types (string[], optional): Filter to specific types (parent, child, related, verifies, verified_by, mitigated_by, mitigates)\n\nReturns:\n { root, direction, items: [{ id, title, type, relationship, depth }], total_count }\n\nExamples:\n - "What does TC-003 verify?" → atoms_trace(project_id, "TC-003", "upstream")\n - "What\'s affected by REQ-001?" → atoms_trace(project_id, "REQ-001", "downstream")',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().min(1).max(20).describe("Starting item ID"),direction:o.z.enum(["upstream","downstream","both"]).describe("Traversal direction"),depth:o.z.number().int().min(1).max(10).default(5).describe("Max traversal depth (default 5)"),relationship_types:o.z.array(o.z.enum(["parent","child","related","verifies","verified_by","mitigated_by","mitigates"])).optional().describe("Filter to specific relationship types")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:$o("atoms_trace",async e=>{const{traceHandler:t}=await Promise.resolve().then(()=>(ss(),as));return t(e)})}),Eo("atoms_list_assets",{title:"List Project Assets",description:"List asset identifiers registered in a project.\n\nAssets are free-form per-instance IDs (VINs, module names, lot numbers,\nserial numbers) that test runs are tagged against. They are auto-registered\nthe first time they appear on a run via atoms_record_test_result.\n\nArgs:\n - project_id (string, UUID): Project to list assets for\n - include_archived (boolean, optional): Include archived assets (default false)\n\nReturns:\n { assets: [{ id, name, archived_at, created_at }], total }",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),include_archived:o.z.boolean().optional().describe("Include archived assets (default false)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_list_assets",async e=>{const{listAssetsHandler:t}=await Promise.resolve().then(()=>(ls(),os));return t(e)})),Eo("atoms_list_variables",{title:"List Project Variables",description:"List all parameterized variables defined in a project.\n\nVariables are shared values (e.g., backup_camera_latency = 2s) that can be\nreferenced in requirement bodies as {variable_name}.\n\nArgs:\n - project_id (string, UUID): Project to list variables for\n - baseline_id (string, UUID, optional): list variables as they were at that baseline\n - as_of (string ISO 8601, optional): list variables as they were at that moment\n - limit (number, optional): max results (default 50, max 200)\n - offset (number, optional): pagination offset (default 0)\n\nReturns:\n { variables: [{ id, name, value, unit, description }], total }, meta: { total_count, limit, offset, has_more }",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),baseline_id:o.z.string().uuid().optional().describe("Optional baseline UUID for time-travel reads"),as_of:o.z.string().datetime().optional().describe("Optional ISO timestamp for time-travel reads"),limit:o.z.number().int().min(1).max(200).default(50).describe("Max results (default 50, max 200)"),offset:o.z.number().int().min(0).default(0).describe("Pagination offset (default 0)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_list_variables",async e=>{const{listVariablesHandler:t}=await Promise.resolve().then(()=>(ws(),cs));return t(e)})),Eo("atoms_get_variable",{title:"Get Project Variable",description:'Get a specific variable by name, including which items reference it.\n\nArgs:\n - project_id (string, UUID): Project containing the variable\n - variable_name (string): Variable name (e.g., "backup_camera_latency")\n - baseline_id (string, UUID, optional): get the variable as it was at that baseline\n - as_of (string ISO 8601, optional): get the variable as it was at that moment\n\nReturns:\n { variable, referenced_by: [{ id, title, type }], reference_count }',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),variable_name:o.z.string().min(1).max(64).describe("Variable name"),baseline_id:o.z.string().uuid().optional().describe("Optional baseline UUID for time-travel reads"),as_of:o.z.string().datetime().optional().describe("Optional ISO timestamp for time-travel reads")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_get_variable",async e=>{const{getVariableHandler:t}=await Promise.resolve().then(()=>(ws(),cs));return t(e)})),Eo("atoms_update_variable",{title:"Update Project Variable",description:"Update a variable's value, unit, or description.\n\nArgs:\n - project_id (string, UUID): Project containing the variable\n - variable_name (string): Variable name\n - value (string, optional): New value\n - unit (string, optional): Unit abbreviation (e.g., 's', 'Hz')\n - description (string, optional): Human-readable description\n\nReturns:\n { variable }",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),variable_name:o.z.string().min(1).max(64).describe("Variable name"),value:o.z.string().max(500).optional().describe("New value"),unit:o.z.string().max(32).optional().describe("Unit abbreviation"),description:o.z.string().max(2e3).optional().describe("Description")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_update_variable",async e=>{const{updateVariableHandler:t}=await Promise.resolve().then(()=>(ws(),cs));return t(e)})),Eo("atoms_create_variable",{title:"Create Project Variable",description:'Create one or many parameterized variables for a project.\n\nSingle form: provide name + value.\nArray form: provide variables[] for bulk creation (one atomic INSERT).\n\nVariables can then be embedded in requirement bodies as {variable_name}.\n\nArgs (single form):\n - project_id (string, UUID): Project to add the variable to\n - name (string): Variable name, letters, digits, underscores; must start with letter/underscore\n - value (string): Initial value (e.g., "100", "true", "nominal")\n - unit (string, optional): Unit abbreviation (e.g., "Hz", "ms", "m/s²")\n - description (string, optional): Human-readable description\n - echo (bool, optional): false → lean {name} response (default true)\n\nArgs (array form):\n - project_id (string, UUID): Project UUID\n - variables[]: Array of { name, value, unit?, description? }\n - echo (bool, optional): false → lean {name} list (default true)\n - idempotency_key (string, optional): Dedup key\n\nReturns (single): { variable, message }\nReturns (array): { batch_id, created, variables, message }',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(64).optional().describe("Variable name (single form)"),value:o.z.string().min(1).max(500).optional().describe("Initial value (single form)"),unit:o.z.string().max(32).optional().describe("Unit abbreviation"),description:o.z.string().max(2e3).optional().describe("Description"),echo:o.z.boolean().optional().describe("false → lean response (default true)"),variables:o.z.array(o.z.object({name:o.z.string().min(1).max(64).describe("Variable name"),value:o.z.string().min(1).max(500).describe("Initial value"),unit:o.z.string().max(32).optional().describe("Unit abbreviation"),description:o.z.string().max(2e3).optional().describe("Description")})).optional().describe("Array form: batch variable creation. Atomic — all or none."),idempotency_key:o.z.string().max(128).optional().describe("Dedup key for retries")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$o("atoms_create_variable",async e=>{const{createVariableHandler:t}=await Promise.resolve().then(()=>(ws(),cs));return t(e)})),Eo("atoms_delete_variable",{title:"Delete Project Variable",description:"Delete a project variable. Refuses by default if any items reference it.\n\nArgs:\n - project_id (string, UUID): Project containing the variable\n - name (string): Variable name to delete\n - force (bool, optional): Delete even if items still reference it (default false)\n\nReturns:\n { deleted, name, referenced_by_items, warning? }",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(64).describe("Variable name to delete"),force:o.z.boolean().optional().describe("Delete even if items reference it (default false)"),confirmation_token:o.z.string().max(500).optional().describe("Token from a prior preview call (only used when ATOMS_MCP_REQUIRE_CONFIRMATION is set)")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},$o("atoms_delete_variable",async e=>{const{deleteVariableHandler:t}=await Promise.resolve().then(()=>(ws(),cs));return t(e)})),Eo("atoms_list_domains",{title:"List Project Domains",description:"List the domain tags of a project's taxonomy, with item counts.\n\nA tag flagged as a FUNCTION (metadata.function_tags, the seed of a HARA row) is not a domain: it is\nleft out of this list, exactly as the app's domain picker leaves it out, because atoms_create_item,\natoms_update_item and atoms_bulk_import refuse it as a domain. Such tags are named in\nexcluded_function_tags; use them through atoms_create_analysis source_functions or atoms_fill_row\nsource_function.\n\nArgs:\n - project_id (string, UUID): Project to inspect\n\nReturns:\n { domains: [{ name, registered, item_count }], total, registered_count, unregistered_count,\n excluded_function_tags: string[], note? }\n registered: false marks a tag found on items but not in the taxonomy (an orphan); a flagged tag\n an item still carries is a function on that item, never counted as an orphan.",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_list_domains",async e=>{const{listDomainsHandler:t}=await Promise.resolve().then(()=>(As(),vs));return t(e)})),Eo("atoms_create_domain",{title:"Create Project Domain",description:'Register a new domain tag in the project taxonomy.\n\nOnce created, items can be tagged with this domain via atoms_create_item or atoms_update_item.\n\nArgs:\n - project_id (string, UUID): Project to add the domain to\n - name (string): Domain name (lowercased automatically, e.g., "safety", "can-bus")\n\nReturns:\n { domain: { name }, message }',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(64).describe("Domain name")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$o("atoms_create_domain",async e=>{const{createDomainHandler:t}=await Promise.resolve().then(()=>(As(),vs));return t(e)})),Eo("atoms_list_levels",{title:"List Project Levels",description:"List the project's level taxonomy in order, with each level's L# (positional index) and item count.\n\nArgs:\n - project_id (string, UUID): Project to list levels for\n\nReturns:\n { levels: [{ name, prefix, item_count }], total }",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_list_levels",async e=>{const{listLevelsHandler:t}=await Promise.resolve().then(()=>(Qa(),Ta));return t(e)})),Eo("atoms_create_level",{title:"Create Project Level",description:'Register a new level in the project taxonomy so items can be tagged with it and it shows in the Strata view. Appended to the ordered level list; its L# is the next index. Names are user-defined labels (lowercased automatically); matching is case-insensitive.\n\nArgs:\n - project_id (string, UUID): Project to add the level to\n - name (string): Level name, e.g. "feature", "system", "subsystem"\n\nReturns:\n { level: { name, prefix }, message }',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(64).describe("Level name (user-defined label)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$o("atoms_create_level",async e=>{const{createLevelHandler:t}=await Promise.resolve().then(()=>(Qa(),Ta));return t(e)})),Eo("atoms_delete_level",{title:"Delete Project Level",description:"Delete an UNUSED level from the project taxonomy. Refuses if any item still uses the level (re-tag those items with atoms_update_item first). A level is positional, so deleting one shifts every later level's L# down by one. Matching is case-insensitive.\n\nArgs:\n - project_id (string, UUID)\n - name (string): the level to delete\n - confirmation_token (string, optional): from the first call, when confirmation is required\n\nReturns:\n { level: { name }, remaining_levels, message } — or a confirmation_required payload",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(64).describe("Level name to delete"),confirmation_token:o.z.string().optional().describe("Token from the first (preview) call, when confirmation is required")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},$o("atoms_delete_level",async e=>{const{deleteLevelHandler:t}=await Promise.resolve().then(()=>(Qa(),Ta));return t(e)})),Eo("atoms_reorder_levels",{title:"Reorder Project Levels",description:"Reorder the project's levels. Provide every existing level name exactly once, in the order you want them (any casing). L# is positional, so this changes every level's L# wherever it renders (Strata, sidebar, exports). Touches no item.\n\nArgs:\n - project_id (string, UUID)\n - names (string[]): every existing level name, in the new order\n\nReturns:\n { levels: [{ name, prefix }], message }",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),names:o.z.array(o.z.string().min(1).max(64)).min(1).describe("Every existing level name, in the desired order")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_reorder_levels",async e=>{const{reorderLevelsHandler:t}=await Promise.resolve().then(()=>(Qa(),Ta));return t(e)})),Eo("atoms_list_fields",{title:"List Project Fields",description:'List the project\'s custom field definitions (project-scoped key-value slots on items, e.g. "Verification Method"), with counts of items carrying a value.\n\nField values are set per item via atoms_create_item / atoms_update_item fields.\n\nArgs:\n - project_id (string, UUID): Project to inspect\n\nReturns:\n { fields: [{ name, type, options, item_count }], total }',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_list_fields",async e=>{const{listFieldsHandler:t}=await Promise.resolve().then(()=>(bn(),nn));return t(e)})),Eo("atoms_create_field",{title:"Create Project Field",description:'Define a project custom field so items can carry a value for it (shown as a Matrix column, e.g. "Verification Method").\n\nThe name keeps its casing; uniqueness is case-insensitive. Type is one of text, long, bool, date, person, select, number, link. Select fields require options. Field definitions never carry visibility rules, permissions, or workflow logic. A definition applies to every item type unless appliesTo restricts it (requirement|test-case|note|table).\n\nArgs:\n - project_id (string, UUID): Project to define the field in\n - name (string): Field name, 1-60 characters (e.g. "Verification Method", "Essential Output?")\n - type (string, optional): Defaults to text\n - options (string[], optional): Required when type=select\n - scope (string, optional): project (default) or organization (org admin only)\n - appliesTo (string[], optional): item types the field shows on (requirement|test-case|note|table); omit for all types\n\nReturns:\n { field: { name, type, options }, message }',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(60).describe("Field name (display name, 1-60 chars)"),type:o.z.enum(["text","long","bool","date","person","select","number","link"]).optional().describe("Field type, defaults to text"),options:o.z.array(o.z.string().min(1).max(120)).max(100).optional().describe("Allowed values for select fields"),scope:o.z.enum(["project","organization"]).optional().describe("Create in this project or copy to every project in the organization"),appliesTo:o.z.array(o.z.enum(["requirement","test-case","note","table"])).max(4).optional().describe("Item types this field shows on (requirement|test-case|note|table); omit for all types")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$o("atoms_create_field",async e=>{const{createFieldHandler:t}=await Promise.resolve().then(()=>(bn(),nn));return t(e)})),Eo("atoms_delete_field",{title:"Delete Project Field",description:"Delete a project field definition. Existing item values are left in item JSON but no longer render as Matrix columns until a matching field is created again.\n\nArgs:\n - project_id (string, UUID): Project containing the field\n - name (string): Field name to delete",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(60).describe("Field name")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},$o("atoms_delete_field",async e=>{const{deleteFieldHandler:t}=await Promise.resolve().then(()=>(bn(),nn));return t(e)})),Eo("atoms_reorder_fields",{title:"Reorder Project Fields",description:"Reorder project field definitions. Definition order drives the Column Picker listing and a fresh user's Matrix column order.\n\nOrdering only: no definition is added, dropped, renamed, or retyped, and no item value moves. Use this instead of deleting and recreating definitions to change their order.\n\nArgs:\n - project_id (string, UUID): Project containing the fields\n - names (string[]): Every existing field name exactly once, in the order you want them. A list that is not a permutation of the current names is refused, not partially applied.",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),names:o.z.array(o.z.string().min(1).max(60)).describe("Every existing field name exactly once, in the desired order")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_reorder_fields",async e=>{const{reorderFieldsHandler:t}=await Promise.resolve().then(()=>(bn(),nn));return t(e)})),Eo("atoms_update_domain",{title:"Rename Project Domain",description:"Rename a domain tag. Updates the taxonomy and propagates to all tagged items.\n\nUse dry_run=true first to see how many items will be affected before committing.\n\nArgs:\n - project_id (string, UUID): Project containing the domain\n - name (string): Current domain name\n - new_name (string): New domain name\n - dry_run (bool, optional): If true, return impact count without making changes (default false)\n\nReturns (dry_run=false):\n { domain: { name }, renamed_from, items_updated, message }\n\nReturns (dry_run=true):\n { dry_run: true, rename: { from, to }, items_affected, message }",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(64).describe("Current domain name"),new_name:o.z.string().min(1).max(64).describe("New domain name"),dry_run:o.z.boolean().optional().describe("Preview impact without applying (default false)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$o("atoms_update_domain",async e=>{const{updateDomainHandler:t}=await Promise.resolve().then(()=>(As(),vs));return t(e)})),Eo("atoms_delete_domain",{title:"Delete Project Domain",description:"Delete a domain tag from the project taxonomy.\n\nRefuses by default if any items are tagged with it. With force=true, deletes the\ndomain and strips the tag from all items.\n\nArgs:\n - project_id (string, UUID): Project containing the domain\n - name (string): Domain name to delete\n - force (bool, optional): Strip from items and delete anyway (default false)\n\nReturns:\n { deleted, name, items_updated, message }",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(64).describe("Domain name to delete"),force:o.z.boolean().optional().describe("Strip from all items and delete (default false)"),confirmation_token:o.z.string().max(500).optional().describe("Token from a prior preview call (only used when ATOMS_MCP_REQUIRE_CONFIRMATION is set)")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},$o("atoms_delete_domain",async e=>{const{deleteDomainHandler:t}=await Promise.resolve().then(()=>(As(),vs));return t(e)})),Do({appName:"summary",htmlFile:"summary-app.html",toolName:"atoms_project_summary",title:"Project Compliance Summary",description:'One-call project health and compliance dashboard.\n\nReturns item counts by type, test execution status, requirement coverage\n(overall and by domain), and recent change activity.\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive dashboard with charts.\nFalls back to JSON for non-UI clients.\n\nArgs:\n - project_id (string, UUID): Project to summarize\n\nReturns:\n { project_name, counts, test_status, coverage, coverage_by_domain, recent_changes, last_updated }\n\nExamples:\n - "Give me a compliance snapshot" → atoms_project_summary(project_id)\n - "How\'s our test coverage?" → atoms_project_summary(project_id)',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:$o("atoms_project_summary",async e=>{const{projectSummaryHandler:t}=await Promise.resolve().then(()=>(Os(),Es));return t(e)})}),Do({appName:"browse",htmlFile:"browse-app.html",toolName:"atoms_browse",title:"Browse ATOMS Items",description:'View and filter items in an ATOMS project with a visual browser.\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive filterable list\nwith expandable item details. Falls back to JSON for non-UI clients.\n\nArgs:\n - project_id (string, UUID): The project to browse\n - type (string, optional): Filter by type (requirement, test-case, note, table)\n - domain (string, optional): Filter by domain tag\n - level (string, optional): Filter by level (System, Subsystem, Component)\n - query (string, optional): Full-text search query\n - limit (number, optional): Max results (default 25, max 100)\n - offset (number, optional): Pagination offset (default 0)\n\nReturns:\n { items: [...], filters: { domains, levels, types }, meta: { total_count, limit, offset, has_more } }\n\nExamples:\n - "Show me the requirements" → atoms_browse(project_id, type="requirement")\n - "Browse safety items" → atoms_browse(project_id, domain="Safety")\n - "Find braking requirements" → atoms_browse(project_id, query="braking")',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),type:o.z.enum(["requirement","test-case","note","table","analysis"]).optional().describe("Filter by item type"),domain:o.z.string().max(64).optional().describe("Filter by domain tag"),level:o.z.string().max(32).optional().describe("Filter by level (System, Subsystem, Component)"),query:o.z.string().max(1e3).optional().describe("Full-text search query"),limit:o.z.number().int().min(1).max(100).default(25).describe("Max results (default 25, max 100)"),offset:o.z.number().int().min(0).default(0).describe("Pagination offset (default 0)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:$o("atoms_browse",async e=>{const{browseHandler:t}=await Promise.resolve().then(()=>(Ps(),qs));return t(e)})}),Eo("atoms_impact_analysis",{title:"Analyze Impact of Changing an Item",description:'Analyze the blast radius of changing a specific item.\n\nTraverses the relationship graph from the given item and returns every item that\nwould be affected by a change — with their types, depths, and full relationship paths.\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): Item to analyze (e.g., "REQ-001")\n - direction (string, optional): "downstream" (default) | "upstream" | "both"\n - downstream: what this item affects (children, verified_by)\n - upstream: what affects this item (parents, verifies)\n - both: full bidirectional traversal\n - depth (integer, optional): Max traversal depth. Omit for full depth (all reachable nodes).\n - include_variable_refs (boolean, optional): Reserved for future variable-aware impact. Default true.\n\nReturns:\n { root: { item_id, title, type }, impacted_items: [{ item_id, title, type, depth, relationship_path }], summary: { total_impacted, by_type, max_depth_reached } }\n\nExamples:\n - "What breaks if I change REQ-001?" → atoms_impact_analysis(project_id, "REQ-001")\n - "What\'s upstream of TC-005?" → atoms_impact_analysis(project_id, "TC-005", direction="upstream")\n - "Show direct dependencies only" → atoms_impact_analysis(project_id, "REQ-001", depth=1)\n\nErrors:\n - "Item not found" → verify item_id exists in the project',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().min(1).max(20).describe("Item ID to analyze (e.g., REQ-001)"),direction:o.z.enum(["downstream","upstream","both"]).default("downstream").describe("Traversal direction (default: downstream)"),depth:o.z.number().int().min(1).optional().describe("Max traversal depth. Omit for full depth."),include_variable_refs:o.z.boolean().default(!0).describe("Reserved for variable-aware impact (future). Default true.")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_impact_analysis",async e=>{const{impactAnalysisHandler:t}=await Promise.resolve().then(()=>(Cs(),Ts));return t(e)})),Eo("atoms_list_baselines",{title:"List ATOMS Baselines",description:"List named baselines (named project snapshots) for a project, newest first.\n\nA baseline is a labeled moment in time. Use atoms_create_baseline to make one,\natoms_diff_baselines to compare two, and atoms_restore_baseline to roll back.",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),limit:o.z.number().int().min(1).max(100).default(50).describe("Max results (default 50, max 100)"),offset:o.z.number().int().min(0).default(0).describe("Pagination offset")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_list_baselines",async e=>{const{listBaselinesHandler:t}=await Promise.resolve().then(()=>(Xs(),Ls));return t(e)})),Eo("atoms_create_baseline",{title:"Create ATOMS Baseline",description:"Create a named baseline (snapshot label) at the current moment for a project.\n\nThe baseline records a name + timestamp. It does NOT copy item data; the project\nstate at that moment is reconstructed via temporal queries against item_revisions.\nNames must be unique per project (case-insensitive).",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(200).describe("Baseline name (1-200 chars, unique per project)"),description:o.z.string().max(2e3).optional().describe("Optional description (max 2000 chars)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$o("atoms_create_baseline",async e=>{const{createBaselineHandler:t}=await Promise.resolve().then(()=>(Xs(),Ls));return t(e)})),Eo("atoms_lock_baseline",{title:"Lock ATOMS Baseline",description:"Lock a baseline so it becomes audit-grade immutable. Admin only. One-way: there is no unlock.\n\nAfter locking, the baseline's name, description, and snapshot_at cannot be changed,\nand it cannot be deleted. Use for regulatory submissions and DRBs.",inputSchema:{baseline_id:o.z.string().uuid().describe("UUID of the baseline to lock")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$o("atoms_lock_baseline",async e=>{const{lockBaselineHandler:t}=await Promise.resolve().then(()=>(Xs(),Ls));return t(e)})),Eo("atoms_diff_baselines",{title:"Diff ATOMS Baselines",description:"Compute the diff between two baselines: which items were added, removed, or modified.\n\nReturns a summary plus a paginated items array. Pass detail=true to include\nper-item field-level diffs (slower for large projects).",inputSchema:{baseline_a_id:o.z.string().uuid().describe("UUID of the first (older) baseline"),baseline_b_id:o.z.string().uuid().describe("UUID of the second (newer) baseline"),detail:o.z.boolean().default(!1).describe("Include per-item field-level diffs (default false)"),limit:o.z.number().int().min(1).max(200).default(50).describe("Max items per page (default 50)"),offset:o.z.number().int().min(0).default(0).describe("Pagination offset")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_diff_baselines",async e=>{const{diffBaselinesHandler:t}=await Promise.resolve().then(()=>(Xs(),Ls));return t(e)})),Eo("atoms_restore_baseline",{title:"Restore from ATOMS Baseline",description:"Restore project state to a baseline. Admin only. Preview-by-default.\n\nCall with confirm=false (default) for a dry run that returns items_to_restore count.\nCall with confirm=true to execute: each item present at the baseline gets a new\nrevision matching its historical state, event_source='restore'. Items added AFTER\nthe baseline are NOT deleted. Always recoverable via further history.",inputSchema:{baseline_id:o.z.string().uuid().describe("UUID of the baseline to restore from"),confirm:o.z.boolean().default(!1).describe("Set true to execute; false (default) returns a preview")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},$o("atoms_restore_baseline",async e=>{const{restoreBaselineHandler:t}=await Promise.resolve().then(()=>(Xs(),Ls));return t(e)})),Eo("atoms_create_analysis",{title:"Create Analysis (HARA, Risk analysis, FMEA)",description:'Create a new analysis (a HARA under ISO 26262, a Risk analysis under ISO 14971, a DFMEA, a PFMEA,\na Software FMEA or a Use FMEA) with one empty row per seed. The analysis carries its standard:\nthe subtype and the variant name it, and a project may hold analyses under both standards.\n\nRequires editor or admin role. Structure matches analyses created in the ATOMS web app.\nA hara (HARA or risk analysis) is seeded from FUNCTIONS (tags flagged as functions), never from\nrequirements: a requirement is an output of the analysis. A dfmea (DFMEA or FMEA) is seeded from\nrequirements / design elements; a PFMEA from a process step.\n\nArgs:\n - project_id (string, UUID): Target project\n - subtype (string): hara | dfmea | risk_analysis | fmea. risk_analysis is an alias of hara and\n fmea an alias of dfmea, so a medical user never has to say HARA or DFMEA.\n hazard_analysis is still accepted as the older name of risk_analysis.\n - variant (string, optional): the standard the analysis carries. hara: iso26262 | iso14971;\n risk_analysis implies iso14971; dfmea: design | process | software | use; fmea implies\n process (the PFMEA). A variant outside the subtype\'s family is refused by name. Every variant\n is accepted in every project. Name none and the default follows the project\'s industry\n setting, exactly as the create menu in the app does: automotive gives hara iso26262 and dfmea\n design, medical device gives hara iso14971 and dfmea process. The variant is stored on the\n analysis and selects its scheme for every later read and write; the setting is only the\n default and never changes an analysis that already exists.\n - title (string, optional): Analysis title (default "New <parent label>": "New HARA",\n "New Risk analysis", "New DFMEA", "New PFMEA", "New Software FMEA", "New Use FMEA")\n - source_functions (string[], hara only, max 50): Function names to seed rows from. A name\n absent from the project\'s tags is registered as a function-flagged tag; matching is\n case-insensitive and trimmed. Rejected for dfmea.\n - source_req_ids (string[], dfmea only, max 50): Requirement IDs to seed rows from.\n Each must exist, be a plain requirement, and not itself be an analysis row. Rejected for hara.\n - revision (string, optional): Revision label (default "1.0")\n - intended_use (string, optional, ISO 14971 risk analysis only): what the device is for, for\n which patient, by which user and in which environment (ISO 14971:2019 5.2).\n - foreseeable_misuse (string, optional, ISO 14971 risk analysis only): the reasonably\n foreseeable misuse, ONE CASE PER LINE (ISO 14971:2019 5.2).\n - safety_characteristics (string, optional, ISO 14971 risk analysis only): the characteristics\n related to safety, ONE PER LINE (ISO 14971:2019 5.3).\n The three scope fields are the analysis\'s, not a row\'s. They are refused by name on a scheme\n that carries no scope (a HARA, a DFMEA, an FMEA), and they can be written later on the\n analysis id with atoms_fill_row.\n The stored subtype and the row req_types are the same under every standard; ids follow the\n scheme: HARA-n / SG-n (hara_iso26262), RA-n / RSK-n (hazard_iso14971), DFMEA-n / FM-n (dfmea_rpn),\n PFMEA-n / PF-n (fmea_process), SFMEA-n / FM-n (fmea_software), UFMEA-n / FM-n (fmea_use).\n\nReturns:\n { id (e.g. "HARA-001", "RA-001", "PFMEA-001"), subtype, title, revision,\n standard? ("ISO 26262" | "ISO 14971" for a hara; "AIAG-VDA FMEA Handbook" for a DFMEA or PFMEA;\n absent for a Software FMEA or a Use FMEA), scheme, variant,\n labels: { parent, row, row_statement, mitigating_requirement, lane, level },\n rows: [{ id, source_function }] (hara) | [{ id, source_req }] (dfmea),\n link_warnings?: [{ row_id, source_function | source_req, message }], warning? }\n rows lists every created row. If any row could not be attached to the analysis, it is\n named in link_warnings and warning explains the repair (atoms_link_items with type\n \'parent\', from_id=<row id>, to_id=<analysis id>); such rows will not appear in\n atoms_get_analysis until relinked.\n\nSide effects:\n - Creates the analysis parent item plus one row item per seed (row ids follow the scheme: SG-n or RSK-n, FM-n or PF-n)\n - hara: registers new function names in the project\'s tags and function flags (metadata)\n - Links each row to the parent (dual-written to the relationship shadow table)\n - Logs each created item to change_history with actor=\'mcp_<client>\'',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the target project"),subtype:o.z.enum(["hara","dfmea","risk_analysis","fmea","hazard_analysis"]).describe("Analysis subtype; risk_analysis is an alias of hara and fmea of dfmea, and hazard_analysis is still accepted as the older name of risk_analysis (REQ-ANL-192)"),title:o.z.string().min(1).max(500).optional().describe("Analysis title"),source_functions:o.z.array(o.z.string().min(1).max(100)).max(50).optional().describe("hara only: function names to seed rows from (0-50); new names are registered as function-flagged tags"),source_req_ids:o.z.array(o.z.string().min(1).max(20)).max(50).optional().describe("dfmea only: requirement IDs to seed rows from (0-50)"),revision:o.z.string().min(1).max(32).optional().describe("Revision label (default 1.0)"),variant:o.z.enum(["iso26262","iso14971","design","process","software","use"]).optional().describe("The standard the analysis carries (REQ-ANL-212). hara: iso26262 | iso14971; dfmea: design | process | software | use. risk_analysis implies iso14971, fmea implies process. Omit it and the default follows the project industry setting (REQ-ANL-216)."),intended_use:o.z.string().max(1e4).optional().describe("ISO 14971 risk analysis only: what the device is for, for which patient, by which user and in which environment (ISO 14971:2019 5.2)."),foreseeable_misuse:o.z.string().max(1e4).optional().describe("ISO 14971 risk analysis only: the reasonably foreseeable misuse, one case per line (ISO 14971:2019 5.2)."),safety_characteristics:o.z.string().max(1e4).optional().describe("ISO 14971 risk analysis only: the characteristics related to safety, one per line (ISO 14971:2019 5.3).")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$o("atoms_create_analysis",async e=>{const{createAnalysisHandler:t}=await Promise.resolve().then(()=>(Mo(),eo));return t(e)})),Eo("atoms_fill_row",{title:"Fill Analysis Row Fields",description:'Fill fields on one analysis row: a HARA or risk analysis row, a DFMEA or FMEA row (row ids follow the scheme: SG-n or RSK-n, FM-n or PF-n).\n\nRequires editor or admin role. Written fields are marked as MCP-sourced so the\nweb app shows their provenance. The computed level (ASIL, RPN or Risk) is derived and\nnever stored: asil is never a writable key.\n\nrow_id may also name the ANALYSIS PARENT (e.g. "RA-001"), which carries the analysis\'s own\nfields rather than a row\'s: on an ISO 14971 risk analysis exactly intended_use,\nforeseeable_misuse, safety_characteristics (its scope, ISO 14971:2019 5.2 and 5.3),\noverall_residual_risk (Low | Medium | High) with overall_residual_rationale (its overall\nresidual risk, ISO 14971:2019 clause 8) and software_safety_class (A | B | C) with\nsoftware_safety_rationale (IEC 62304 4.3). Any other key on a parent is refused by name with\nthat list given, and a scheme that carries no parent field (a HARA, a DFMEA, an FMEA) refuses\nall of them. Both judged values are a PERSON\'s: nothing computes them, the empty string clears\none, and the app only ever suggests one from the rows.\n\nArgs:\n - project_id (string, UUID): Project containing the row\n - row_id (string): Analysis row ID (e.g. "SG-001", "RSK-002", "FM-003", "PF-004"), or the\n analysis parent ID (e.g. "RA-001") to write the analysis\'s scope and overall judgement\n - fields (object): Field name to value map. Allowed keys:\n hara: failure_mode, operating_scenario, hazard, assumptions, severity (S0-S3), exposure (E0-E4),\n controllability (C0-C3), safe_state, ftti, rationale, source_function, effect, title\n dfmea: failure_mode, cause, severity (1-10), occurrence (1-10), detection (1-10),\n rationale, effect\n medical risk analysis: failure_mode (the hazard), operating_scenario (the sequence of events),\n hazard (the harm), assumptions, severity (1-5), p1 (1-5), p2 (1-5), rationale, source_function,\n safety_goal (the risk control statement), s_rationale, p1_rationale, p2_rationale,\n residual_s, residual_p1, residual_p2, benefit_note, effect\n medical FMEA: the FMEA keys of its variant; a PFMEA adds process_step and current_controls,\n and a Software FMEA has NO occurrence (fixed at 10, IEC 62304).\n The analysis carries its standard: the variant stored on the row\'s parent analysis selects\n the scheme (an analysis stored before v5.4 without one resolves through the project\'s legacy hint).\n The derived level (asil / rpn / risk / p0) and the approval keys (approved, approved_at,\n approved_by, approved_note, field_source, link_roles, mitigation_types) are refused BY NAME.\n A suggested rating is written together with its rationale: pass the rating and its evidence\n field in one call (severity + s_rationale, p1 + p1_rationale), or use atoms_suggest_field.\n \'effect\' writes the row\'s long text body (the hazardous event of a HARA row, the hazardous situation\n of a medical risk analysis row, the effect of an FMEA row). dfmea failure_mode also\n becomes the row title. dfmea ratings accept numbers and are stored as strings.\n hara failure_mode: the standardized suggestions are No function, Loss of function, Partial function,\n Degradation of function, Misinterpretation of function (a suggestion; any text is accepted).\n hara source_function: the function the row analyzes, registered as a function-flagged tag if new.\n\nReturns:\n { row_id, subtype, written_fields, computed_level, asil_propagated_to?, approval_cleared? }\n approval_cleared: true means the row WAS approved and this write cleared approved, approved_by,\n approved_at and approved_note: a signature never covers content the approver did not see. A\n person approves the row again with atoms_approve_row once they have read the new values.\n On the analysis parent instead: { analysis_id, scheme, written_fields,\n scope: { intended_use, foreseeable_misuse: string[], safety_characteristics: string[] },\n overall: { residual_risk, residual_rationale, safety_class, safety_rationale } }.\n\nSide effects:\n - Saves the row with stale-write protection and logs to change_history\n - On an approved row: clears the approval (history-logged with the reason)\n - hara only: if the row is linked and its computed level (ASIL, or Risk under ISO 14971) changed,\n updates each linked requirement\'s asil / asil_source (each update is history-logged)',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),row_id:o.z.string().min(1).max(20).describe("Analysis row ID (e.g. SG-001, FM-003), or the analysis parent ID (e.g. RA-001) for its scope and overall residual risk"),fields:o.z.record(o.z.string().max(64),o.z.union([o.z.string().max(5e4),o.z.number()])).describe("Field name to value map (see description for allowed keys per subtype)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_fill_row",async e=>{const{fillRowHandler:t}=await Promise.resolve().then(()=>(Mo(),eo));return t(e)})),Eo("atoms_get_analysis",{title:"Get Analysis",description:'Get a full analysis view: every row with its computed level (ASIL, RPN or Risk, by the\nanalysis\'s scheme) and gap / critical / stale classifications, plus summary counts and the\nscheme\'s vocabulary.\n\nClassifications match the ATOMS web app exactly (same rules, same thresholds). The analysis\ncarries its standard: the variant stored on it selects the scheme (a hara is a HARA under\nISO 26262 or a Risk analysis under ISO 14971; a dfmea is a DFMEA, a PFMEA, a Software FMEA or\na Use FMEA). An analysis stored before v5.4 without a variant resolves through the project\'s\nlegacy hint and reads variant null.\n\nArgs:\n - project_id (string, UUID): Project containing the analysis\n - analysis_id (string): Analysis ID (e.g. "HARA-001", "RA-001", "PFMEA-002")\n\nReturns:\n { analysis: { id, title, subtype, scheme, revision, standard, variant,\n labels: { parent, row, row_statement, mitigating_requirement, lane, level },\n acceptability: { Low, Medium, High } | null (the ISO 14971 wording per risk level, e.g. High is\n "Not acceptable"; null on every scheme but the medical risk analysis),\n definitions: [{ key, text }] (the scheme\'s glossary, the text the app\'s tooltips show),\n control_types: [{ id, abbr, label, precedence }] (ISO 14971 clause 7, applied in precedence\n order; empty on the automotive schemes),\n field_labels: [{ key, label }] (each raw row key to the label this scheme shows for it, e.g. in a\n medical risk analysis failure_mode is "Hazard", effect is "Hazardous situation" and hazard is\n "Harm"; read the raw keys through this list, never as automotive words),\n scope: { intended_use, foreseeable_misuse: string[], safety_characteristics: string[] } | null\n (the analysis\'s scope, ISO 14971:2019 5.2 and 5.3; the two lists are the stored lines, blank\n lines dropped; null on a scheme that carries no scope),\n overall: { residual_risk, residual_rationale, safety_class, safety_rationale } | null\n (the overall residual risk a person judged, ISO 14971:2019 clause 8, and the IEC 62304 4.3\n software safety class; an unjudged value reads as the empty string; null on a scheme that\n carries neither) },\n rows: [{ id, title, source_req, effect, <the scheme\'s fields under their raw keys>,\n computed_level, computed_level_text (the acceptability wording behind the level, or null),\n is_gap, gap_rule, is_critical, is_stale, mitigated_by, link_roles, mitigation_types,\n verified_by, residual_level (medical risk analysis only; null elsewhere),\n approved, approved_at, approved_by, approved_note, mcp_fields }],\n counts: { rows, gaps, critical, stale, approved, linked } }\n The vocabulary lists are given once per analysis, never repeated per row.',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),analysis_id:o.z.string().min(1).max(20).describe("Analysis ID (e.g. HARA-001, RA-001, PFMEA-002)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_get_analysis",async e=>{const{getAnalysisHandler:t}=await Promise.resolve().then(()=>(Mo(),eo));return t(e)})),Eo("atoms_link_mitigation",{title:"Link Analysis Row to Mitigating Requirement",description:'Link a HARA / risk analysis / FMEA row to the technical requirement that mitigates it (the\nsafety requirement of a HARA row, the risk control of a medical row, the action of an FMEA row).\n\nRequires editor or admin role.\n\nArgs:\n - project_id (string, UUID): Project containing both items\n - row_id (string): Analysis row ID (e.g. "SG-001", "RSK-002", "FM-003", "PF-004")\n - req_id (string): Target requirement ID (must be a plain requirement, not an analysis row)\n hara / medical risk analysis rows: the target must be a requirement of req_type \'safety\'\n (a safety requirement / risk control); any other requirement is refused with the rule\n named (REQ-ANL-142). An FMEA row accepts any requirement, so long as role is given.\n - control_type (string, medical schemes only, REQUIRED there): inherent | protective | information\n (ISO 14971 clause 7: inherently safe design, protective measure, information for safety).\n Stored on the LINK (the row\'s analysis_props.mitigation_types keyed by req_id), never on the\n requirement. The analysis carries its standard: control_type is required exactly when the\n row\'s resolved scheme is medical (hazard_iso14971, fmea_process, fmea_software, fmea_use: a\n risk analysis, PFMEA, Software FMEA or Use FMEA link carries one) and refused on a HARA\n (ISO 26262) or a DFMEA row.\n - role (string, dfmea rows only, REQUIRED there): prevention | detection | mitigation.\n Must be omitted for hara rows (a HARA or a risk analysis).\n - override (boolean, optional, default false): hara only. When the target already\n carries a different level (asil, which holds the Risk under ISO 14971), the call fails with\n a structured error (details.code=\'asil_conflict\') unless override=true; the override is\n history-logged like a dialog accept.\n\nReturns:\n { row_id, req_id, type: "mitigated_by", role, scheme, control_type? (medical links, the type stored\n on the link), asil_written? (hara only: the row\'s computed level, the ASIL or the Risk, written\n onto the target) }\n\nSide effects:\n - Adds the mitigated_by/mitigates relationship (JSONB + shadow table, one transaction)\n - hara only: writes the row\'s computed level (ASIL, or Risk under ISO 14971) onto the target\n requirement (data.asil + data.asil_source) with a history entry recording previous/new values\n - dfmea / every FMEA: never modifies any field on the target requirement',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),row_id:o.z.string().min(1).max(20).describe("Analysis row ID (e.g. SG-001, FM-003)"),req_id:o.z.string().min(1).max(20).describe("Target requirement ID (e.g. REQ-042)"),role:o.z.enum(["prevention","detection","mitigation"]).optional().describe("Mitigation role. Required for dfmea rows (every FMEA); must be omitted for hara rows."),override:o.z.boolean().optional().describe("hara only: apply the row's level (ASIL or Risk) over a conflicting existing value (history-logged)"),control_type:o.z.enum(["inherent","protective","information"]).optional().describe("Medical schemes only (required there): the ISO 14971 clause 7 control type, stored on the link.")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_link_mitigation",async e=>{const{linkMitigationHandler:t}=await Promise.resolve().then(()=>(Mo(),eo));return t(e)})),Eo("atoms_get_gaps",{title:"Get Analysis Gap Rows",description:'List only the gap rows of a HARA / risk analysis / FMEA analysis, each naming the rule that matched.\n\nGap rules (identical to the web app):\n hara (HARA): unmitigated row with ASIL A-D ("asil <X> unmitigated")\n hara (medical risk analysis): uncontrolled row with Risk above Low ("<level> risk uncontrolled")\n dfmea (DFMEA, PFMEA and every FMEA variant): unmitigated row with RPN >= 100 ("rpn >= 100") or\n severity >= 9 ("severity >= 9")\n\nArgs:\n - project_id (string, UUID): Project containing the analysis\n - analysis_id (string): Analysis ID (e.g. "HARA-001", "RA-001", "PFMEA-002")\n\nReturns:\n { analysis_id, subtype, gaps: [{ id, title, source_req, source_function, computed_level, rule }], count }\n count is the gap-line TOTAL: the sum of every gap kind\'s gap_count (a controlled-but-unverified risk, an analysis-scope gap all count), matching the app\'s gap banner, so it can be GREATER than gaps.length, which lists only the per-row gap rules.\n plus scheme, labels and gap_kinds: [{ kind, label, row_ids, req_ids, gap_count, fix, fix_label }],\n the scheme\'s gap kinds with the ids behind each, in the same order and wording the app banner uses.\n gap_count is how many gaps the kind stands for: its rows, or its requirements, or 1 for a kind\n that is one fact about the ANALYSIS and therefore carries neither list.\n hara: unmitigated, no_test (no test on its ODD), no_parent (safety requirement at the system\n level with no parent)\n medical: no_control (a risk above Low), not_verified, no_residual, no_parent (risk control at the\n system level with no parent), no_scope (the analysis holds rows but states no intended\n use, ISO 14971:2019 5.2) and no_overall (every row is controlled and residual-rated but\n the overall residual risk is not judged, ISO 14971:2019 clause 8); both are written on\n the analysis id with atoms_fill_row\n FMEA: rpn (RPN 100 or above with no mitigating requirement), severity (severity 9 or above\n below RPN 100 with no mitigating requirement, the REQ-ANL-047 floor; a row is under one\n kind, never both); an FMEA has no requirement-scope kind, so no project-wide scan runs\n for it\n and system_level, system_level_source: the level the no_parent kind was judged against. The system\n level is the SECOND rung of the project\'s level ladder (metadata.levels, positional: the L1 of\n REQ-ASH-031), matched against tags.level by name; source is "taxonomy". Only when the ladder has\n fewer than two rungs does the literal "L1" apply, and source then reads "fallback".',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),analysis_id:o.z.string().min(1).max(20).describe("Analysis ID (e.g. HARA-001, RA-001, PFMEA-002)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_get_gaps",async e=>{const{getGapsHandler:t}=await Promise.resolve().then(()=>(Mo(),eo));return t(e)})),Eo("atoms_propose_rows",{title:"Write Proposed Analysis Rows",description:"Write rows YOU have drafted for one function into a HARA / risk analysis / FMEA (a DFMEA, a\nPFMEA, a Software FMEA or a Use FMEA). The analysis carries its standard: the rows are validated\nagainst the scheme its stored variant selects.\n\nThe drafting is the assistant's job: read the analysis with atoms_get_analysis (its scope, its\nODD / operating modes and its existing rows), draft the rows, then send them here. This tool\nvalidates every value against the analysis's scheme and writes the rows; it never invents a value.\n\nRequires editor or admin role. Every written value is marked as MCP-sourced and every row is\nwritten UNAPPROVED: a person approves it with atoms_approve_row.\n\nArgs:\n - project_id (string, UUID): Project containing the analysis\n - analysis_id (string): Analysis ID (e.g. \"HARA-001\", \"RA-001\", \"PFMEA-002\")\n - source_function (string, risk analysis only): the function these rows analyse; registered\n as a function-flagged tag if new (matching is case-insensitive and trimmed)\n - source_req (string, FMEA only): the requirement these rows analyse\n - rows (object[], 1 to 8): the drafted rows, each a field name to value map using the\n analysis's scheme keys (the same keys atoms_fill_row accepts, plus 'effect', and 'title'\n on a risk analysis row). A risk analysis row sent without a title is named by its\n failure_mode (the hazard), else by its hazard; an FMEA row by its failure_mode. The cap of 8\n is shared with the app and the assistant; a longer list is refused, never truncated.\n The derived level (asil / rpn / risk) and the approval keys are refused by name.\n\nReturns:\n { analysis_id, subtype, scheme, variant?, labels, source_function | source_req,\n rows: [{ id, written_fields, computed_level }], count, max_rows, approved: false,\n link_warnings?, warning? }\n\nSide effects:\n - Creates one row item per drafted row, attached to the analysis (row ids follow the scheme: SG-n or RSK-n, FM-n or PF-n)\n - Marks every written field field_source='mcp' and leaves each row unapproved\n - Logs each created row to change_history with actor='mcp_<client>'",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),analysis_id:o.z.string().min(1).max(20).describe("Analysis ID (e.g. HARA-001, RA-001, PFMEA-002)"),rows:o.z.array(o.z.record(o.z.string().max(64),o.z.union([o.z.string().max(5e4),o.z.number()]))).min(1).max(ht).describe(`The drafted rows (1-${ht}), each a scheme field name to value map`),source_function:o.z.string().min(1).max(100).optional().describe("Risk analysis only: the function these rows analyse"),source_req:o.z.string().min(1).max(20).optional().describe("FMEA only: the requirement these rows analyse")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$o("atoms_propose_rows",async e=>{const{proposeRowsHandler:t}=await Promise.resolve().then(()=>(Mo(),eo));return t(e)})),Eo("atoms_suggest_field",{title:"Write a Suggested Value for One Analysis Field",description:"Write ONE value YOU have suggested for one field of one analysis row.\n\nThe suggestion is the assistant's job (Suggest evidence, Suggest safety goal, Suggest risk control\nstatement); this tool is the write half: it validates the value against the row's scheme, writes it marked as MCP-sourced and\nreturns the row. A suggested rating is written together with its rationale: pass 'rationale' and\nit lands in the scheme's evidence field for that rating (severity -> s_rationale, exposure ->\ne_rationale, controllability -> c_rationale, p1 -> p1_rationale, p2 -> p2_rationale, and the\nsingle 'rationale' column on an FMEA).\n\nAccepting a suggestion is a client action: it flips that one field's field_source to 'user'. The\nrow stays unapproved either way, until atoms_approve_row records who approved it.\n\nArgs:\n - project_id (string, UUID): Project containing the row\n - row_id (string): Analysis row ID (e.g. \"SG-001\", \"RSK-002\", \"FM-003\", \"PF-004\")\n - field (string): the field to fill (any key atoms_fill_row accepts for this row's scheme)\n - value (string | number): the suggested value; a rating must be one the scheme allows\n - rationale (string, optional): the evidence behind a suggested RATING\n\nReturns:\n { row_id, subtype, scheme, field, field_source: \"mcp\", written_fields,\n rationale_field?, approval_cleared?, row: { id, computed_level, approved, mcp_fields, ...scheme fields } }\n approval_cleared: true means the row WAS approved and this suggestion cleared approved, approved_by,\n approved_at and approved_note: a signature never covers content the approver did not see.\n\nSide effects:\n - Saves the row with stale-write protection and logs to change_history\n - Marks the written field(s) field_source='mcp'; the row stays unapproved\n - On an approved row: clears the approval (history-logged with the reason)",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),row_id:o.z.string().min(1).max(20).describe("Analysis row ID (e.g. SG-001, FM-003)"),field:o.z.string().min(1).max(64).describe("The field to fill (a scheme key of this row)"),value:o.z.union([o.z.string().max(5e4),o.z.number()]).describe("The suggested value"),rationale:o.z.string().max(5e4).optional().describe("The evidence behind a suggested rating; written to the scheme's evidence field")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_suggest_field",async e=>{const{suggestFieldHandler:t}=await Promise.resolve().then(()=>(Mo(),eo));return t(e)})),Eo("atoms_approve_row",{title:"Approve an Analysis Row in a Named Person's Name",description:'Record the sign-off of ONE analysis row, naming who approved it.\n\nRequires editor or admin role. The approver must be a member of the project\'s organization: a\nname that cannot be traced to a member is refused, never stored. The note is optional. Approving\nclears the machine-proposed (✦) markers on the row: the approver takes those values as their own.\nA row whose ratings are incomplete cannot be approved (its level is unset), matching the grid.\n\nThis tool signs off one analysis row. It has nothing to do with change control: it never\napproves, dismisses or applies a suggestion (REQ-CHG-062).\n\nArgs:\n - project_id (string, UUID): Project containing the row\n - row_id (string): Analysis row ID (e.g. "SG-001", "RSK-002", "FM-003", "PF-004")\n - approver_id (string, UUID, optional): the approving member; defaults to the calling user\n - note (string, optional): the note captured with the approval\n\nReturns:\n { row_id, subtype, scheme, approved: true, approved_by, approved_at, approved_note,\n approver_role, computed_level, cleared_markers }\n\nSide effects:\n - Saves the row with stale-write protection: approved, approved_at, approved_by, approved_note\n - Flips every field_source=\'mcp\' entry on the row to \'user\'\n - Logs the approval (previous and new values) to change_history',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),row_id:o.z.string().min(1).max(20).describe("Analysis row ID (e.g. SG-001, FM-003)"),approver_id:o.z.string().uuid().optional().describe("The approving project member; defaults to the calling user"),note:o.z.string().max(2e3).optional().describe("Optional note captured with the approval")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$o("atoms_approve_row",async e=>{const{approveRowHandler:t}=await Promise.resolve().then(()=>(Mo(),eo));return t(e)}))}});B(),C(),Ne(),T();var No=c.createRequire("undefined"==typeof document?require("url").pathToFileURL(__filename).href:m&&"SCRIPT"===m.tagName.toUpperCase()&&m.src||new URL("index.cjs",document.baseURI).href)("../package.json"),Lo=process.argv.slice(2)[0];(async function(){switch(Lo){case"login":{const{login:e}=await Promise.resolve().then(()=>(Ke(),Le));try{const{email:t}=await e();process.stderr.write(`\n [32m✓[0m Authenticated as ${t}\n`),process.stderr.write(" [32m✓[0m Credentials saved to ~/.atoms/credentials.json\n\n"),process.stderr.write(" [1mNext steps:[0m\n"),process.stderr.write(" [36m$[0m claude mcp add atoms -- npx -y @atoms-tech/atoms-mcp\n"),process.stderr.write("\n Or add to Claude Desktop (Settings → MCP → Edit Config):\n"),process.stderr.write(' [90m{\n "mcpServers": {\n "atoms": {\n "command": "npx",\n "args": ["-y", "@atoms-tech/atoms-mcp"]\n }\n }\n }[0m\n\n')}catch(e){process.stderr.write(`\n [31m✗[0m ${e instanceof Error?e.message:String(e)}\n\n`),process.exit(1)}break}case"whoami":{const{getValidToken:e}=await Promise.resolve().then(()=>(Re(),ve));try{const{email:t,user_id:r}=await e();process.stderr.write(`\n [32m✓[0m Logged in as ${t}\n [90mUser ID: ${r}[0m\n\n`)}catch(e){process.stderr.write(`\n [31m✗[0m Not authenticated: ${e instanceof Error?e.message:String(e)}\n Run: npx @atoms-tech/atoms-mcp login\n\n`),process.exit(1)}break}case"logout":{const{clearCredentials:e}=await Promise.resolve().then(()=>(oe(),J));await e(),process.stderr.write("Logged out. Credentials removed.\n"),process.stderr.write("Run 'npx @atoms-tech/atoms-mcp login' to authenticate again.\n");break}case"--version":case"-v":process.stderr.write(`@atoms-tech/atoms-mcp v${No.version}\n`);break;case"--help":case"-h":process.stderr.write("ATOMS MCP Server — AI agent integration for requirements management\n\nUsage:\n npx @atoms-tech/atoms-mcp Start MCP server (stdio)\n npx @atoms-tech/atoms-mcp login Authenticate with ATOMS.tech\n npx @atoms-tech/atoms-mcp logout Switch account / clear credentials\n npx @atoms-tech/atoms-mcp whoami Show current user\n npx @atoms-tech/atoms-mcp --version Show version\n");break;default:{process.stderr.write("[atoms-mcp] Starting MCP server via stdio...\n");{const{getValidToken:e,readValidToken:t}=await Promise.resolve().then(()=>(Re(),ve)),{runExclusiveAuth:r}=await Promise.resolve().then(()=>(he(),de)),{login:i}=await Promise.resolve().then(()=>(Ke(),Le));try{let a;try{a=(await e()).email}catch(e){process.stderr.write(`[atoms-mcp] Could not use the saved session: ${e instanceof Error?e.message:String(e)}\n`),process.stderr.write("[atoms-mcp] Starting login flow...\n");const n=async()=>{const e=await t();if(!e)throw new Error("no valid token yet");return{email:e.email}};a=(await r(n,async()=>({email:(await i()).email}))).email}process.stderr.write(`[atoms-mcp] Authenticated as ${a}\n`)}catch(e){process.stderr.write(`[atoms-mcp] Login failed: ${e instanceof Error?e.message:String(e)}\n[atoms-mcp] Starting server without auth. Tools will fail until you run: npx @atoms-tech/atoms-mcp login\n`)}}try{const t=await qe(),r=De(),i=await async function(e,t){const r=process.env.ATOMS_MCP_ORG_ID?.trim();if(r)return r;const{data:i,error:a}=await e.from("org_members").select("org_id, joined_at").eq("user_id",t).order("joined_at",{ascending:!0}).limit(1).maybeSingle();return a?(process.stderr.write(`[atoms-mcp] Could not resolve session org: ${a.message}\n`),null):i?.org_id??null}(t,r),a=await async function(e,t,r){if(!r)return{role:null,is_member:!1,effective:M};const{data:i,error:a}=await e.rpc("mcp_resolve_policy",{p_user_id:t,p_org_id:r});return a?(process.stderr.write(`[atoms-mcp] mcp_resolve_policy failed (${a.code}): ${a.message}. Falling back to fail-closed policy.\n`),{role:null,is_member:!1,effective:H}):function(e){if(!e||"object"!=typeof e)return{role:null,is_member:!1,effective:M};const t=e,r="string"==typeof t.role?t.role:null,i=!0===t.is_member,a=t.effective??{},n=(e,t)=>Array.isArray(e)?e.filter(e=>"string"==typeof e):t,s=(e,t)=>"boolean"==typeof e?e:t,o=a.project_scope,d=Array.isArray(o)&&o.length>0?o.filter(e=>"string"==typeof e):null;return{role:r,is_member:i,effective:{read_only:s(a.read_only,M.read_only),lockdown:s(a.lockdown,M.lockdown),block_destructive:s(a.block_destructive,M.block_destructive),block_bulk_import:s(a.block_bulk_import,M.block_bulk_import),cross_project_browse:s(a.cross_project_browse,M.cross_project_browse),allowed_toolsets:n(a.allowed_toolsets,M.allowed_toolsets),forbidden_tools:n(a.forbidden_tools,M.forbidden_tools),project_scope:d,require_confirmation_for:n(a.require_confirmation_for,M.require_confirmation_for)}}}(i)}(t,r,i);e={effective:a.effective,role:a.role,org_id:i},D=e.effective,$=e.org_id,a.is_member&&i?process.stderr.write(`[atoms-mcp] Org policy loaded: org=${i.slice(0,8)}… role=${a.role??"?"} read_only=${a.effective.read_only} lockdown=${a.effective.lockdown}\n`):process.stderr.write("[atoms-mcp] No org membership detected — running with permissive default policy.\n")}catch(e){process.stderr.write(`[atoms-mcp] Could not resolve session policy (${e instanceof Error?e.message:String(e)}). Running with permissive default — RLS still enforces row-level access.\n`)}process.stderr.write(`[atoms-mcp] Tool registry: ${function(){const e=L(),t=N(),r=[];r.push(e?"read-only":"read+write"),null===t?r.push("all toolsets"):(r.push(`toolsets=${[...t.allowed].sort().join(",")||"(none — only always-on tools)"}`),t.unknown.length>0&&r.push(`unknown=${t.unknown.join(",")}`));const i=process.env.ATOMS_MCP_PROJECT_ID?.trim();return i&&r.push(`project_scope=${i.slice(0,8)}…`),("1"===process.env.ATOMS_MCP_LOCKDOWN||"true"===process.env.ATOMS_MCP_LOCKDOWN)&&r.push("lockdown=on"),r.join(" | ")}()}\n`);const{server:t}=await Promise.resolve().then(()=>(Co(),Ho)),r=new p.StdioServerTransport;await t.connect(r),process.stderr.write("[atoms-mcp] MCP server running. Waiting for tool calls...\n")}}var e})().catch(e=>{process.stderr.write(`[atoms-mcp] Fatal: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)});
|
|
2
|
+
var e=require("fs/promises"),t=require("os"),r=require("path"),a=require("@supabase/supabase-js"),i=require("http"),n=require("crypto"),s=require("url"),o=require("zod"),c=require("@modelcontextprotocol/ext-apps/server"),l=require("@modelcontextprotocol/sdk/server/mcp.js"),d=require("module"),p=require("@modelcontextprotocol/sdk/server/stdio.js"),m="undefined"!=typeof document?document.currentScript:null;function u(e){return e&&e.__esModule?e:{default:e}}var _,f,h,y,g,w,v,b=u(e),j=u(r),k=u(n),S=Object.defineProperty,x=Object.getOwnPropertyNames,A=(e,t,r)=>function(){if(r)throw r[0];try{return e&&(t=(0,e[x(e)[0]])(e=0)),t}catch(e){throw r=[e],e}},E=(e,t)=>{for(var r in t)S(e,r,{get:t[r],enumerable:!0})};function I(e){return v.get(e)}var R=A({"src/core/tool-catalog.ts"(){_=new Set(["atoms_status","atoms_list_projects"]),f=new Set(["atoms_create_item","atoms_update_item","atoms_delete_item","atoms_restore_item","atoms_link_items","atoms_bulk_import","atoms_record_test_result","atoms_record_test_results","atoms_create_variable","atoms_update_variable","atoms_delete_variable","atoms_create_domain","atoms_update_domain","atoms_delete_domain","atoms_create_level","atoms_delete_level","atoms_reorder_levels","atoms_create_field","atoms_delete_field","atoms_reorder_fields","atoms_create_baseline","atoms_lock_baseline","atoms_restore_baseline","atoms_create_analysis","atoms_fill_row","atoms_link_mitigation","atoms_propose_rows","atoms_suggest_field","atoms_approve_row"]),h=new Set(["atoms_delete_item","atoms_delete_variable","atoms_delete_domain","atoms_delete_level","atoms_delete_field","atoms_restore_baseline"]),y=new Set(["atoms_bulk_import"]),g={items:["atoms_list_items","atoms_get_item","atoms_search","atoms_semantic_search","atoms_browse","atoms_get_history","atoms_create_item","atoms_update_item","atoms_delete_item","atoms_restore_item","atoms_bulk_import"],traceability:["atoms_trace","atoms_link_items","atoms_export_mermaid","atoms_impact_analysis"],coverage:["atoms_get_coverage","atoms_verification_status","atoms_record_test_result","atoms_record_test_results","atoms_project_summary","atoms_list_assets"],variables:["atoms_list_variables","atoms_get_variable","atoms_create_variable","atoms_update_variable","atoms_delete_variable"],domains:["atoms_list_domains","atoms_create_domain","atoms_update_domain","atoms_delete_domain","atoms_list_levels","atoms_create_level","atoms_delete_level","atoms_reorder_levels","atoms_list_fields","atoms_create_field","atoms_delete_field","atoms_reorder_fields"],baselines:["atoms_list_baselines","atoms_create_baseline","atoms_lock_baseline","atoms_diff_baselines","atoms_restore_baseline"],analysis:["atoms_create_analysis","atoms_fill_row","atoms_get_analysis","atoms_link_mitigation","atoms_get_gaps","atoms_propose_rows","atoms_suggest_field","atoms_approve_row","atoms_list_scenario_catalogs","atoms_mutate_scenario_catalog"]},w=Object.keys(g),v=new Map;for(const[e,t]of Object.entries(g))for(const r of t)v.set(r,e)}});function O(e,t){if(_.has(t))return{allowed:!0};if(e.lockdown&&!new Set(["atoms_list_items","atoms_get_item","atoms_search","atoms_browse"]).has(t))return{allowed:!1,reason:"lockdown"};if(e.forbidden_tools.includes(t))return{allowed:!1,reason:"forbidden_tool"};if(e.read_only&&f.has(t))return{allowed:!1,reason:"read_only"};if(e.block_destructive&&h.has(t))return{allowed:!1,reason:"block_destructive"};if(e.block_bulk_import&&y.has(t))return{allowed:!1,reason:"block_bulk_import"};const r=I(t);return void 0===r||e.allowed_toolsets.includes(r)?{allowed:!0}:{allowed:!1,reason:"toolset_disabled"}}function q(e,t){return O(e,t).allowed}function z(e){switch(e){case"lockdown":return"MCP is in lockdown mode for your role — only read-only browse is available.";case"read_only":return"Your org policy puts MCP in read-only mode. Mutating tools are not exposed.";case"block_destructive":return"Your org policy blocks destructive operations (delete tools).";case"block_bulk_import":return"Your org policy blocks bulk import.";case"forbidden_tool":return"This tool is on your org's forbidden list.";case"toolset_disabled":return"This tool's toolset is not enabled in your org policy."}}var $,H,M,T,D=A({"src/core/policy.ts"(){R()}}),P=A({"src/core/types.ts"(){$={read_only:!1,lockdown:!1,block_destructive:!1,block_bulk_import:!1,cross_project_browse:!0,allowed_toolsets:["items","traceability","coverage","variables","domains","baselines","analysis"],forbidden_tools:[],project_scope:null,require_confirmation_for:[]},H={read_only:!0,lockdown:!0,block_destructive:!0,block_bulk_import:!0,cross_project_browse:!1,allowed_toolsets:[],forbidden_tools:[],project_scope:[],require_confirmation_for:[]}}});function C(){return M}var U=A({"src/middleware/session-policy.ts"(){P(),M=$,T=null}});function L(){const e=process.env.ATOMS_MCP_TOOLSETS?.trim();if(!e)return null;const t=e.split(",").map(e=>e.trim()).filter(Boolean);if(0===t.length)return null;const r=new Set,a=[];for(const e of t)w.includes(e)?r.add(e):a.push(e);return{allowed:r,unknown:a}}function N(){const e=process.env.ATOMS_MCP_READ_ONLY;return"1"===e||"true"===e?.toLowerCase()}function F(e){if(N()&&f.has(e))return!1;if(_.has(e))return!0;const t=L();if(null===t)return q(C(),e);const r=I(e);return!!r&&!!t.allowed.has(r)&&q(C(),e)}var W,V,B=A({"src/middleware/tool-registry.ts"(){R(),D(),U()}}),J={};async function Q(){try{const t=await e.readFile(V,"utf-8"),r=JSON.parse(t);return r.access_token&&r.refresh_token?r:null}catch{return null}}async function G(t){await e.mkdir(W,{recursive:!0,mode:448}),await e.writeFile(V,JSON.stringify(t,null,2),{mode:384})}async function Y(){const e=await Q();return!!e&&e.expires_at>Date.now()+6e4}function K(e){return e.expires_at>Date.now()+6e4}async function Z(){try{await e.unlink(V)}catch{}}function X(){return V}E(J,{clearCredentials:()=>Z,getCredentialsPath:()=>X,hasValidCredentials:()=>Y,isTokenValid:()=>K,readCredentials:()=>Q,writeCredentials:()=>G});var ee,te,re,ae,ie,ne,se,oe=A({"src/auth/token-store.ts"(){W=r.join(t.homedir(),".atoms"),V=r.join(W,"credentials.json")}}),ce={};async function le(){try{await e.mkdir(ee,{recursive:!0,mode:448});const t=await e.open(te,"wx");try{await t.writeFile(`${process.pid} ${Date.now()}`)}finally{await t.close()}return!0}catch(e){if("EEXIST"===e.code)return!1;throw e}}async function de(){try{await e.unlink(te)}catch{}}async function pe(){try{const t=await e.stat(te);return Date.now()-t.mtimeMs>re}catch{return!1}}async function me(e,t,r=se){const a=r.now()+ae;for(;;){if(await r.tryAcquire())try{try{return await e()}catch{}return await t()}finally{await r.release()}try{return await e()}catch{}if(await r.isStale())await r.release();else{if(r.now()>a)return await t();await r.wait(ie)}}}E(ce,{runExclusiveAuth:()=>me});var ue,_e,fe,he=A({"src/auth/lock.ts"(){ee=r.join(t.homedir(),".atoms"),te=r.join(ee,".auth.lock"),re=7e4,ae=12e4,ie=300,ne=e=>new Promise(t=>setTimeout(t,e)),se={now:Date.now,wait:ne,tryAcquire:le,release:de,isStale:pe}}}),ye={};function ge(e){return e.trim().replace(/\/+$/,"")}E(ye,{ATOMS_APP_URL:()=>fe,ATOMS_SUPABASE_ANON_KEY:()=>_e,ATOMS_SUPABASE_URL:()=>ue,resolveUrl:()=>ge});var we=A({"src/config.ts"(){ue=ge(process.env.ATOMS_SUPABASE_URL??"https://gmebjyhomsbvhrxffzre.supabase.co"),_e=process.env.ATOMS_SUPABASE_ANON_KEY??"sb_publishable_b49MUAB8XCrQiF7x5b9lUA_w1aplSBH",fe=process.env.ATOMS_APP_URL??"https://atoms.tech"}}),ve={};async function be(){const e=process.env.ATOMS_ACCESS_TOKEN;if(e){const t=ke(e);return{access_token:e,refresh_token:"",user_id:t.sub,email:t.email??""}}const t=await Q();if(t&&t.expires_at>Date.now()+6e4){const e=ke(t.access_token);return{access_token:t.access_token,refresh_token:t.refresh_token,user_id:e.sub,email:e.email??t.user_email??""}}return null}async function je(){const e=await be();if(e)return e;const t=await Q();if(!t)throw new Error("Not authenticated. Run 'npx @atoms-tech/atoms-mcp login' to connect your ATOMS account.");return me(async()=>{const e=await be();if(e)return e;throw new Error("no fresh token yet")},()=>async function(e){process.stderr.write("[atoms-mcp] Refreshing access token...\n");const t=a.createClient(ue,_e);let r=await t.auth.refreshSession({refresh_token:e.refresh_token});if(r.error||!r.data.session){const a=await Q();if(a&&a.expires_at>Date.now()+6e4){const e=ke(a.access_token);return{access_token:a.access_token,refresh_token:a.refresh_token,user_id:e.sub,email:e.email??a.user_email??""}}a&&a.refresh_token&&a.refresh_token!==e.refresh_token&&(process.stderr.write("[atoms-mcp] Refresh token was rotated by another session; retrying with the latest...\n"),r=await t.auth.refreshSession({refresh_token:a.refresh_token}))}if(r.error||!r.data.session){const e=r.error?[r.error.status,r.error.message].filter(Boolean).join(" ").trim():"no session returned";throw new Error(`Token refresh failed (${e}). Your saved ATOMS session is no longer valid — re-run 'npx @atoms-tech/atoms-mcp login'.`)}const i=r.data.session;return await G({access_token:i.access_token,refresh_token:i.refresh_token,expires_at:Date.now()+1e3*(i.expires_in??3600),user_email:i.user?.email}),{access_token:i.access_token,refresh_token:i.refresh_token,user_id:i.user?.id??"",email:i.user?.email??""}}(t))}function ke(e){const t=e.split(".");if(3!==t.length)throw new Error("Invalid JWT format");const r=t[1].replace(/-/g,"+").replace(/_/g,"/").padEnd(t[1].length+(4-t[1].length%4)%4,"="),a=JSON.parse(Buffer.from(r,"base64").toString("utf-8"));if(!a.sub)throw new Error("JWT missing 'sub' claim");return a}E(ve,{getValidToken:()=>je,readValidToken:()=>be});var Se,xe,Ae,Ee,Ie,Re=A({"src/auth/refresh.ts"(){oe(),he(),we()}}),Oe={};async function qe(){if(Se)try{const e=await Q();e&&e.expires_at>Ie&&(process.stderr.write("[atoms-mcp] Credentials file refreshed elsewhere — rebuilding client.\n"),ze())}catch{}if(Se&&Ee>Date.now()+6e4)return Se;Se&&process.stderr.write("[atoms-mcp] Token expiring, refreshing client...\n");const{access_token:e,user_id:t,email:r}=await je(),i=e.split(".");if(3===i.length)try{const e=JSON.parse(Buffer.from(i[1].replace(/-/g,"+").replace(/_/g,"/"),"base64").toString());Ee=1e3*(e.exp??0)}catch{Ee=Date.now()+36e5}try{const e=await Q();Ie=e?.expires_at??0}catch{Ie=0}const n=a.createClient(ue,_e,{global:{headers:{Authorization:`Bearer ${e}`}}});return xe=t,Ae=r,Se=n}function ze(){Se=null,xe=null,Ae=null,Ee=0,Ie=0}function $e(e){let t="";if(e instanceof Error)t=e.message;else if(e&&"object"==typeof e){const r=e;t="string"==typeof r.message?r.message:"string"==typeof r.code?r.code:String(e)}else null!=e&&(t=String(e));return/jwt\s*expired|invalid.?jwt|token.*(?:has\s+)?expired|jwt.*invalid|pgrst301/i.test(t)}async function He(e){try{return await e()}catch(t){if(!$e(t))throw t;return process.stderr.write("[atoms-mcp] Data tier rejected JWT — invalidating client and retrying once.\n"),ze(),await e()}}function Me(){if(!xe)throw new Error("Not authenticated. Call getClient() first.");return xe}function Te(){return Ae??""}async function De(e,t){const r=Me(),{data:a,error:i}=await e.from("projects").select("org_id").eq("id",t).maybeSingle();if(i||!a)throw new Error(`Project '${t}' not found`);const{data:n,error:s}=await e.from("org_members").select("role").eq("user_id",r).eq("org_id",a.org_id).maybeSingle();if(s||!n){const{data:t}=await e.rpc("is_platform_admin");if(!0===t)return"admin";throw new Error("Not a member of this project's organization")}if("viewer"===n.role)throw new Error("VIEWER_ROLE");return n.role}async function Pe(e,t){const r=Me(),{data:a,error:i}=await e.from("projects").select("org_id, created_by").eq("id",t).maybeSingle();if(i||!a)throw new Error(`Project '${t}' not found`);const{data:n,error:s}=await e.from("org_members").select("role").eq("user_id",r).eq("org_id",a.org_id).maybeSingle();if(s||!n)throw new Error("Not a member of this project's organization");if("admin"===n.role)return"admin";if(a.created_by===r)return"owner";throw new Error("PROJECT_FIELD_ADMIN_OR_OWNER_REQUIRED")}E(Oe,{getClient:()=>qe,getUserEmail:()=>Te,getUserId:()=>Me,invalidateClient:()=>ze,isJwtExpiredError:()=>$e,requireProjectFieldDefinitionAccess:()=>Pe,requireWriteAccess:()=>De,withJwtRetry:()=>He});var Ce,Ue,Le=A({"src/db/client.ts"(){Re(),oe(),we(),Se=null,xe=null,Ae=null,Ee=0,Ie=0}}),Ne={};async function Fe(){const e=n.randomBytes(32).toString("base64url"),t=n.createHash("sha256").update(e).digest("base64url"),r=`http://localhost:${Ce}${Ue}`,a=new s.URL(`${fe}/auth/mcp-consent`);return a.searchParams.set("redirect_uri",r),a.searchParams.set("code_challenge",t),a.searchParams.set("code_challenge_method","S256"),new Promise((t,r)=>{let n;function o(){clearTimeout(n)}const c=i.createServer(async(a,i)=>{const n=new s.URL(a.url??"/",`http://localhost:${Ce}`);if(n.pathname!==Ue)return i.writeHead(404),void i.end("Not found");try{if("access_denied"===n.searchParams.get("error"))return i.writeHead(200,{"Content-Type":"text/html"}),i.end(Ve("Authorization was cancelled.")),o(),c.close(),void r(new Error("Authorization cancelled by user."));const a=n.searchParams.get("code");if(a){const n=await fetch(`${ue}/auth/v1/token?grant_type=pkce`,{method:"POST",headers:{"Content-Type":"application/json",apikey:_e},body:JSON.stringify({auth_code:a,code_verifier:e})}),s=await n.json();if(!n.ok||!s.access_token){const e=s.error_description??s.msg??"Unknown error";return i.writeHead(200,{"Content-Type":"text/html"}),i.end(Ve(`Code exchange failed: ${e}`)),o(),c.close(),void r(new Error(`Code exchange failed: ${e}`))}let l="authenticated";try{l=JSON.parse(Buffer.from(s.access_token.split(".")[1],"base64").toString()).email??l}catch{}const{createClient:d}=await import("@supabase/supabase-js"),p=d(ue,_e,{global:{headers:{Authorization:`Bearer ${s.access_token}`}}}),{data:m,error:u}=await p.from("org_members").select("org_id").limit(1);return u||!m||0===m.length?(i.writeHead(200,{"Content-Type":"text/html"}),i.end(Ve('No ATOMS account found for this Google account.<br><br>Please sign up at <a href="https://atoms.tech">atoms.tech</a> first, then run this login command again.')),o(),c.close(),void r(new Error("No ATOMS account found. Sign up at atoms.tech first."))):s.refresh_token?(await G({access_token:s.access_token,refresh_token:s.refresh_token,expires_at:Date.now()+1e3*(s.expires_in??3600),user_email:l}),i.writeHead(200,{"Content-Type":"text/html"}),i.end(We(l)),o(),c.close(),void t({email:l})):(i.writeHead(200,{"Content-Type":"text/html"}),i.end(Ve("Sign-in succeeded but no refresh token was issued, so the session would expire within the hour. This usually points to a Supabase Auth setting (refresh-token rotation or session policy). Please contact ATOMS support.")),o(),c.close(),void r(new Error("No refresh token issued by Supabase — cannot persist a durable session.")))}const s=n.searchParams.get("access_token"),l=n.searchParams.get("refresh_token"),d=parseInt(n.searchParams.get("expires_in")??"3600",10);s&&l?(await G({access_token:s,refresh_token:l,expires_at:Date.now()+1e3*d}),i.writeHead(200,{"Content-Type":"text/html"}),i.end(We("authenticated")),o(),c.close(),t({email:"authenticated"})):(i.writeHead(200,{"Content-Type":"text/html"}),i.end("<!DOCTYPE html>\n<html>\n<head><title>ATOMS MCP Login</title></head>\n<body>\n <h1>Completing login...</h1>\n <script>\n const hash = window.location.hash.substring(1);\n const params = new URLSearchParams(hash);\n const data = {\n access_token: params.get('access_token'),\n refresh_token: params.get('refresh_token'),\n expires_in: params.get('expires_in'),\n user_email: ''\n };\n if (data.access_token) {\n // Decode JWT to get email\n try {\n const payload = JSON.parse(atob(data.access_token.split('.')[1]));\n data.user_email = payload.email || '';\n } catch(e) {}\n fetch('/token', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data)\n }).then(() => {\n document.body.innerHTML = '<h1>Login successful! You can close this tab.</h1>';\n }).catch(() => {\n document.body.innerHTML = '<h1>Login failed. Please try again.</h1>';\n });\n } else {\n document.body.innerHTML = '<h1>Login failed — no tokens received.</h1>';\n }\n <\/script>\n</body>\n</html>"))}catch(e){i.writeHead(500),i.end("Authentication failed"),o(),c.close(),r(e)}});c.on("request",async(e,a)=>{if("POST"===e.method&&"/token"===e.url){let i="";e.on("data",e=>{i+=e.toString()}),e.on("end",async()=>{try{const e=JSON.parse(i);await G({access_token:e.access_token,refresh_token:e.refresh_token,expires_at:Date.now()+1e3*(e.expires_in??3600),user_email:e.user_email}),a.writeHead(200,{"Content-Type":"application/json"}),a.end(JSON.stringify({ok:!0})),o(),c.close(),t({email:e.user_email??"authenticated"})}catch(e){a.writeHead(500),a.end("Failed to store credentials"),o(),c.close(),r(e)}})}}),c.listen(Ce,"127.0.0.1",async()=>{process.stderr.write("\nOpening browser for ATOMS login...\n"),process.stderr.write(`If the browser doesn't open, visit:\n${a.toString()}\n\n`);try{const{default:e}=await import("open");await e(a.toString())}catch{process.stderr.write("Could not open browser automatically. Please open the URL above.\n")}}),n=setTimeout(()=>{c.close(),r(new Error("Login timed out after 60 seconds. Run 'npx @atoms-tech/atoms-mcp login' to try again."))},6e4)})}function We(e){return`<!DOCTYPE html>\n<html>\n<head>\n <title>ATOMS MCP — Authorized</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;\n background: #0a0a0a;\n color: #fafafa;\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n }\n .card {\n background: #171717;\n border: 1px solid #262626;\n border-radius: 16px;\n padding: 48px;\n max-width: 480px;\n width: 100%;\n text-align: center;\n }\n .icon {\n width: 56px;\n height: 56px;\n background: #052e16;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 24px;\n }\n .icon svg { width: 28px; height: 28px; }\n h1 {\n font-size: 22px;\n font-weight: 600;\n margin-bottom: 8px;\n color: #22c55e;\n }\n .email {\n font-size: 14px;\n color: #a3a3a3;\n margin-bottom: 32px;\n }\n .permissions {\n text-align: left;\n background: #0a0a0a;\n border: 1px solid #262626;\n border-radius: 12px;\n padding: 20px;\n margin-bottom: 24px;\n }\n .permissions h3 {\n font-size: 12px;\n font-weight: 600;\n color: #737373;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin-bottom: 12px;\n }\n .perm-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 6px 0;\n font-size: 14px;\n color: #d4d4d4;\n }\n .perm-item .dot {\n width: 6px;\n height: 6px;\n background: #22c55e;\n border-radius: 50%;\n flex-shrink: 0;\n }\n .closing {\n font-size: 13px;\n color: #525252;\n }\n .closing span { color: #a3a3a3; font-variant-numeric: tabular-nums; }\n </style>\n</head>\n<body>\n <div class="card">\n <div class="icon">\n <svg fill="none" viewBox="0 0 24 24" stroke="#22c55e" stroke-width="2.5">\n <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>\n </svg>\n </div>\n <h1>Authorization Granted</h1>\n <p class="email">${e}</p>\n\n <div class="permissions">\n <h3>ATOMS MCP can now</h3>\n <div class="perm-item"><span class="dot"></span> Read your projects and requirements</div>\n <div class="perm-item"><span class="dot"></span> Create and edit requirements and test cases</div>\n <div class="perm-item"><span class="dot"></span> Record test results (pass/fail)</div>\n <div class="perm-item"><span class="dot"></span> Manage traceability relationships</div>\n <div class="perm-item"><span class="dot"></span> Export coverage reports and diagrams</div>\n </div>\n\n <p class="closing">This tab will close in <span id="countdown">5</span>s</p>\n </div>\n\n <script>\n let seconds = 5;\n const el = document.getElementById('countdown');\n const timer = setInterval(() => {\n seconds--;\n el.textContent = seconds;\n if (seconds <= 0) {\n clearInterval(timer);\n window.close();\n // If window.close() is blocked (not opened by script), update message\n setTimeout(() => {\n document.querySelector('.closing').textContent = 'You can close this tab now.';\n }, 500);\n }\n }, 1000);\n <\/script>\n</body>\n</html>`}function Ve(e){return`<!DOCTYPE html>\n<html>\n<head>\n <title>ATOMS MCP — Failed</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;\n background: #0a0a0a;\n color: #fafafa;\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n }\n .card {\n background: #171717;\n border: 1px solid #262626;\n border-radius: 16px;\n padding: 48px;\n max-width: 480px;\n width: 100%;\n text-align: center;\n }\n .icon {\n width: 56px;\n height: 56px;\n background: #450a0a;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 24px;\n }\n .icon svg { width: 28px; height: 28px; }\n h1 { font-size: 22px; font-weight: 600; margin-bottom: 16px; color: #ef4444; }\n .reason { font-size: 14px; color: #a3a3a3; line-height: 1.6; }\n .reason a { color: #7c3aed; text-decoration: underline; }\n </style>\n</head>\n<body>\n <div class="card">\n <div class="icon">\n <svg fill="none" viewBox="0 0 24 24" stroke="#ef4444" stroke-width="2.5">\n <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>\n </svg>\n </div>\n <h1>Authorization Failed</h1>\n <p class="reason">${e}</p>\n </div>\n</body>\n</html>`}E(Ne,{login:()=>Fe});var Be,Je,Qe,Ge,Ye=A({"src/auth/login.ts"(){oe(),we(),Ce=19275,Ue="/callback"}});function Ke(e,t=0){if(t>5)return"[depth-limit]";if("string"==typeof e){for(const t of Qe)if(t.test(e.trim()))return"[redacted]";return e.length>Ge?e.slice(0,Ge)+"…":e}if(Array.isArray(e))return e.slice(0,20).map(e=>Ke(e,t+1));if(null!==e&&"object"==typeof e){const r={};for(const[a,i]of Object.entries(e)){const e=a.toLowerCase();Be.has(e)?r[a]="[redacted]":Je.has(e)?r[a]="string"==typeof i?"[content]":Ke(i,t+1):r[a]=Ke(i,t+1)}return r}return e}function Ze(e){return Ke(e)}var Xe,et,tt,rt,at,it=A({"src/middleware/audit.ts"(){Be=new Set(["access_token","refresh_token","password","api_key","secret","token","authorization","credential","credentials","private_key"]),Je=new Set(["body","description","content","notes","summary","title","system_prompt","prompt"]),Qe=[/^[^\s@]+@[^\s@]+\.[^\s@]+$/,/^bearer\s+\S+$/i,/^(sk|pk|npm|gh|glpat|xoxb|xoxp|rk|SG\.)[-_][A-Za-z0-9_-]{16,}/,/^eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/],Ge=200}}),nt=A({"src/middleware/rate-limiter.ts"(){Xe=60,et=6e4}});function st(){const e=process.env.ATOMS_MCP_PROJECT_ID?.trim();return e?tt.test(e)?e:(rt||(process.stderr.write(`[atoms-mcp] WARNING: ATOMS_MCP_PROJECT_ID="${e.slice(0,50)}" is not a valid UUID — scope ignored.\n`),rt=!0),null):null}var ot,ct=A({"src/middleware/project-scope.ts"(){tt=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,rt=!1,at=class extends Error{expectedProjectId;receivedProjectId;constructor(e,t){super("Tool call rejected: project_id is outside the configured ATOMS_MCP_PROJECT_ID scope."),this.name="ProjectScopeError",this.expectedProjectId=e,this.receivedProjectId=t}}}});function lt(e){const t=function(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")}(e??"");return t?t.includes("cursor")?"cursor":t.includes("copilot")?"copilot":t.includes("claude")?ot:t:ot}var dt,pt,mt,ut,_t,ft,ht,yt,gt=A({"src/core/client-identity.ts"(){ot="claude_desktop"}}),wt=A({"src/core/timestamps.ts"(){dt=o.z.string().datetime({offset:!0})}});function vt(e,t){const r=ut.indexOf(String(e??"")),a=ut.indexOf(String(t??""));return r<0||a<0?pt:_t[r][a]}function bt(e){return"Moderate"===e||"High"===e||"Very High"===e}var jt,kt,St,xt=A({"src/core/securityScheme.ts"(){pt="-",mt=["Very Low","Low","Moderate","High","Very High"],ut=["1","2","3","4","5"],_t=[["Very Low","Very Low","Very Low","Low","Low"],["Very Low","Very Low","Low","Low","Moderate"],["Very Low","Low","Low","Moderate","Moderate"],["Low","Low","Moderate","Moderate","High"],["Low","Moderate","High","High","Very High"]],ft={"Very Low":"meets the acceptance criteria",Low:"meets the acceptance criteria",Moderate:"acceptable only with a recorded justification",High:"does not meet the acceptance criteria","Very High":"does not meet the acceptance criteria"},ht=[{id:"authentication",label:"Authentication",abbr:"Auth"},{id:"authorization",label:"Authorization",abbr:"AuthZ"},{id:"cryptography",label:"Cryptography",abbr:"Crypto"},{id:"code_data_execution_integrity",label:"Code, data and execution integrity",abbr:"Integrity"},{id:"confidentiality",label:"Confidentiality",abbr:"Confid"},{id:"event_detection_logging",label:"Event detection and logging",abbr:"Detect"},{id:"resiliency_recovery",label:"Resiliency and recovery",abbr:"Resil"},{id:"firmware_software_updates",label:"Firmware and software updates",abbr:"Update"}],yt={id:"The row id. The prefix comes from the scheme: SEC for the analysis, CYB for its rows.",asset:"The thing worth protecting, the seed of a row.",threat_event:"What the adversary does, one per row.",vulnerability:"The weakness the threat event exploits.",impact:"What the attacker achieves on the device.",harm:"The consequence for the patient, the data or the delivery of care.",s:"Severity and pervasiveness: how bad the consequence is and how widely it reaches, 1 to 5. The anchor for each level is in the glossary.",e:"The ease with which the vulnerability can be exploited, assessed across the device lifecycle. It is not a probability. Score the worst case, or write the reason for a lower score in the E rationale (FDA premarket guidance 2026, V.A.2).",risk:"Derived from S and E through the one fixed matrix of the scheme. Acceptability is judged on the residual risk, not here.",security_control:"One control per risk, stated as a requirement the device shall meet.",mit:"The requirements that carry this control, each with one of the eight FDA control categories on the link.",haz:"The ISO 14971 risk rows this security risk affects. Nothing is derived from the link onto the risk row ratings.",safety_impact:"Whether this security risk creates or worsens a pathway to patient harm. Yes means it is transferred into the ISO 14971 file and linked to the risk rows it affects. No means the consequence stays inside security. The answer needs a written reason either way.",safety_impact_justification:"Why this does or does not reach a patient. Required whichever way the answer goes.",assumptions:"One assumption per line. Non normative: it saves live with no change record.",s_rationale:"The evidence for the S rating.",e_rationale:"The evidence for the E rating. Say here why a score lower than the worst case is justified.",residual_s:"Severity and pervasiveness after the control is in place.",residual_e:"Exploitability after the control is in place.",residual_risk:"The same fixed matrix applied to Residual S and Residual E. Acceptability is judged here: Very Low and Low meet the acceptance criteria, Moderate is acceptable only with a recorded justification, High and Very High do not meet them.",justification:"Why this control and why the residual is accepted. Required when the residual is Moderate or worse, and on any row at S5 or Residual S5 whatever the band.",verified:"The test cases that verify the linked control requirements. A row with links and no test case counts as a control not verified.",chgstatus:"The approval state of the row under change control, with its revision.",authentication:"Proving the identity of a user, an endpoint, or a piece of information (FDA Appendix 1).",authorization:"Deciding what an authenticated identity may do, evaluated on every action.",cryptography:"Protecting data in transit and at rest with validated algorithms and key handling.",code_data_execution_integrity:"Proving that code, data and the running artefact are what the manufacturer released.",confidentiality:"Keeping data readable only by those entitled to read it.",event_detection_logging:"Recording security relevant events so they can be detected and reconstructed afterwards.",resiliency_recovery:"Keeping the device usable under attack or failure, and recovering it afterwards.",firmware_software_updates:"Delivering and verifying updates to the device across its lifecycle.",threat_model_method:"The methodology used to enumerate threats (for example STRIDE), and why it was chosen for this system boundary.",environment_assumptions:"What is assumed about the environment the device operates in, one per line (for example: assume the hospital network is hostile).",attack_surfaces:"The interfaces and entry points considered, one per line.",lifecycle_sources:"The device lifecycle stages considered as a source of threats (FDA premarket guidance V.A.1): supply chain, manufacturing, deployment, interoperation, maintenance and update, decommission.",related_documents:"Where the software bill of materials, the security testing report and the architecture views live.",overall_residual_risk:"The residual risk conclusion of the security risk assessment (AAMI TIR57), one of the five bands, judged by a person against every row's residual risk."}}});function At(e){return`${e.join(", ")} cannot be written through atoms_fill_row: the acceptance is recorded by a person in the app; it is never written over MCP.`}function Et(e){return`${e}. The signed-in person records that the analysis is accepted, with the date and the open gaps by kind copied at that moment. It is not computed from the rows. Row approval is a separate act. ${St}`}function It(e){const t=e??{},r="accepted"===t.acceptance_state?"accepted":"draft",a=Array.isArray(t.open_gaps_at_acceptance)?t.open_gaps_at_acceptance.filter(e=>!!e&&"object"==typeof e&&"string"==typeof e.kind):[];return{acceptance_state:r,accepted_by:"string"==typeof t.accepted_by?t.accepted_by:null,accepted_at:"string"==typeof t.accepted_at?t.accepted_at:null,acceptance_note:"string"==typeof t.acceptance_note?t.acceptance_note:"",open_gaps_at_acceptance:a}}function Rt(e){return"accepted"===e.acceptance_state&&(e.prev_accepted_by=e.accepted_by??null,e.prev_accepted_at=e.accepted_at??null,e.acceptance_state="draft",e.accepted_by=null,e.accepted_at=null,!0)}function Ot(e){const t=e?.relationships?.parents;return Array.isArray(t)?t.filter(e=>"string"==typeof e)[0]??null:null}function qt(e){const t=e?.analysis_props,r={...t&&"object"==typeof t?t:{}};return{revoked:Rt(r),parentProps:r}}var zt,$t,Ht,Mt,Tt,Dt,Pt,Ct,Ut,Lt,Nt,Ft,Wt,Vt,Bt,Jt,Qt,Gt,Yt,Kt,Zt,Xt,er,tr,rr,ar,ir,nr,sr,or,cr,lr,dr,pr,mr,ur,_r,fr,hr,yr,gr,wr,vr,br,jr,kr,Sr,xr,Ar,Er=A({"src/core/acceptance.ts"(){jt=["acceptance_state","accepted_by","accepted_at","acceptance_note","open_gaps_at_acceptance"],kt=["prev_accepted_by","prev_accepted_at","accepted_snapshot_id","accepted_snapshot_name"],St="This is a record of a decision, not an electronic signature. The signature of record is applied in the manufacturer's own quality system."}});function Ir(e,t,r){return"hara"===e?"tir57"===t?"security_tir57":"iso14971"===t?"hazard_iso14971":"iso26262"===t?"hara_iso26262":"medical"===r?"hazard_iso14971":"hara_iso26262":"process"===t?"fmea_process":"software"===t?"fmea_software":"use"===t?"fmea_use":"design"===t?"dfmea_rpn":"medical"===r?"fmea_process":"dfmea_rpn"}function Rr(e){return $t[e]}function Or(e){return Ht.some(t=>t.id===e)||function(e){return ht.some(t=>t.id===e)}(e)}function qr(e){const t=Array.isArray(e)?e.filter(e=>"string"==typeof e):[],r=t.length>=2?t[1].trim():"";return r?{level:r,source:"taxonomy"}:{level:Tt,source:"fallback"}}function zr(e){const t=(e??"").trim().toLowerCase();return"sub-system"===t?"subsystem":t}function $r(e,t){const r=qr(e),a=qr(t);return zr(r.level)===zr(a.level)?null:`Warning: this changes the system level (the ladder's second rung, L1) from "${r.level}" to "${a.level}". HARA and risk-analysis gap classification (the no_parent / bridge gap) is judged against the system level, so gap results for this project's hazard analyses may change. Re-check atoms_get_gaps on affected analyses.`}function Hr(e){return"hazard_iso14971"===e?Kt:"security_tir57"===e?tr:[]}function Mr(e){return"security_tir57"===e?mt:Zt}function Tr(e){return String(e??"").split(/\r\n?|\n/).map(e=>e.trim()).filter(e=>""!==e)}function Dr(e){const t=e??{};return{intended_use:String(t.intended_use??"").trim(),foreseeable_misuse:Tr(t.foreseeable_misuse),safety_characteristics:Tr(t.safety_characteristics)}}function Pr(e,t=Zt){const r=e??{},a=String(r.overall_residual_risk??"").trim(),i=String(r.software_safety_class??"").trim();return{residual_risk:t.includes(a)?a:"",residual_rationale:String(r.overall_residual_rationale??"").trim(),safety_class:Xt.includes(i)?i:"",safety_rationale:String(r.software_safety_rationale??"").trim()}}function Cr(e){return""!==Dr(e).intended_use}function Ur(e,t){const r=Hr(e);if(0===r.length)return`An analysis parent of scheme ${e} carries no fillable field: ${t.join(", ")} cannot be written on it. Only the ISO 14971 risk analysis carries a scope and an overall residual risk.`;const a=r.filter(e=>!e.endsWith("_rationale"));return`Unknown field(s) for the analysis parent: ${t.join(", ")}. Allowed: ${a.join(", ")} (+ _rationale for the last two)`}function Lr(e,t,r){return`Invalid ${e} value ${JSON.stringify(t)}. Allowed: ${r.join(", ")}`}function Nr(e){return"risk_analysis"===e||"hazard_analysis"===e?["iso14971"]:"security_risk"===e?["tir57"]:zt[sr[e]]}function Fr(e){return Object.entries(hr[e]).map(([e,t])=>({key:e,label:t}))}function Wr(e){return Object.entries(function(e){return"hara_iso26262"===e?pr:"dfmea_rpn"===e?_r:"hazard_iso14971"===e?ur:"security_tir57"===e?yt:fr}(e)).map(([e,t])=>({key:e,text:t}))}function Vr(e){return"hazard_iso14971"===e?Vt:"security_tir57"===e?ft:null}function Br(e,t){const r=Vr(e);return r?r[t]??null:null}function Jr(e){return(e??"").trim().toLowerCase()}function Qr(e,t){const r=Jr(e);if(!r)return null;for(const e of t)if(Jr(e)===r)return e;return null}function Gr(e){const t=e?.severity||"",r=e?.exposure||"",a=e?.controllability||"";return t&&r&&a?"S0"===t||"E0"===r||"C0"===a?"QM":Ut[`${t}-${r}-${a}`]||Pt:Pt}function Yr(e){const t=parseInt(e?.severity||"",10),r=parseInt(e?.occurrence||"",10),a=parseInt(e?.detection||"",10);return Number.isNaN(t)||Number.isNaN(r)||Number.isNaN(a)?Pt:String(t*r*a)}function Kr(e,t,r){const a=String(e??""),i=String(t??""),n=String(r??"");if(Nt.indexOf(a)<0||Nt.indexOf(i)<0||Nt.indexOf(n)<0)return Pt;const s=Ft[`${i}-${n}`];return s&&Wt[`${s}-${a}`]||Pt}function Zr(e){return Kr(e?.severity,e?.p1,e?.p2)}function Xr(e){return Kr(e?.residual_s,e?.residual_p1,e?.residual_p2)}function ea(e){return vt(e?.severity,e?.exploitability)}function ta(e){return vt(e?.residual_s,e?.residual_e)}function ra(e,t){return"hara_iso26262"===e?Gr(t):"hazard_iso14971"===e?Zr(t):"security_tir57"===e?ea(t):"fmea_software"===e?function(e){return Yr({...e??{},occurrence:Bt})}(t):Yr(t)}function aa(e,t,r){if(r)return!1;if("hara_iso26262"===e){const e=Gr(t);return"C"===e||"D"===e}if("hazard_iso14971"===e)return"High"===Zr(t);if("security_tir57"===e){const e=ea(t);return"High"===e||"Very High"===e}const a=parseInt(ra(e,t),10);return!Number.isNaN(a)&&a>=200}function ia(e,t,r){if(!e?.approved||!e.approved_at)return!1;const a=Date.parse(e.approved_at);return!Number.isNaN(a)&&[...e.source_req?[e.source_req]:[],...t].some(e=>{const t=r(e),i=t?Date.parse(t):NaN;return!Number.isNaN(i)&&i>a})}function na(e,t){const r=function(e){return"hara_iso26262"===e?"asil":"hazard_iso14971"===e||"security_tir57"===e?"risk":"rpn"}(e);return`Field${t.length>1?"s":""} ${t.join(", ")} cannot be written: ${r} is derived from the ratings and the approval keys are recorded by atoms_approve_row. Write the ratings instead and read ${r} back as computed_level.`}function sa(e){return"fmea_software"===e?{field:"occurrence",value:Bt}:null}function oa(e){const t=sa(e);return t?`Field ${t.field} is fixed at ${t.value} in a Software FMEA (IEC 62304: software failure probability is assumed 100 percent), so the RPN follows severity and detection. Omit it.`:""}function ca(e){return vr[e]}function la(e){return"security_tir57"===e?ht:function(e){return"hazard_iso14971"===e||"fmea_process"===e||"fmea_software"===e||"fmea_use"===e}(e)?Ht:[]}function da(e){return la(e).map((e,t)=>({...e,precedence:t+1}))}function pa(e){return"security_tir57"===e?`control_type is required for a security control link (FDA Appendix 1). Choose one of: ${ht.map(e=>`${e.id} (${e.label})`).join(", ")}.`:`control_type is required for a risk control link (ISO 14971 clause 7). Choose one of: ${Ht.map(e=>`${e.id} (${e.label})`).join(", ")}.`}function ma(e){if(null==e||""===e)return null;if("number"==typeof e)return Number.isInteger(e)?e:null;if("string"!=typeof e||!/^\s*\d+\s*$/.test(e))return null;const t=parseInt(e,10);return Number.isNaN(t)?null:t}function ua(e){const t=parseInt(e,10);return!Number.isNaN(t)&&t>=100}function _a(e){return"hara_iso26262"===e?kr:"hazard_iso14971"===e?Sr:"security_tir57"===e?Ar:xr}function fa(e){return!0===e.approved&&(e.approved=!1,e.approved_at=null,e.approved_by=null,e.approved_note="",!0)}function ha(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e):[]}function ya(e,t,r=Jr){const a=new Set(t.map(r).filter(e=>e.length>0));return e.filter(e=>a.has(r(e)))}function ga(e){const t=e.length>1;return`Function-flagged tag${t?"s":""} used as domain${t?"s":""}: ${e.join(", ")}. A function is not a domain tag: seed a HARA row with it instead (atoms_create_analysis source_functions, or atoms_fill_row source_function), or unflag it in Taxonomy Management.`}var wa,va,ba,ja,ka,Sa,xa,Aa=A({"src/core/analysis.ts"(){xt(),xt(),Er(),zt={hara:["iso26262","iso14971","tir57"],dfmea:["design","process","software","use"]},$t={hara_iso26262:"ISO 26262",hazard_iso14971:"ISO 14971",dfmea_rpn:"AIAG-VDA FMEA Handbook",fmea_process:"AIAG-VDA FMEA Handbook",fmea_software:void 0,fmea_use:void 0,security_tir57:"AAMI TIR57"},Ht=[{id:"inherent",label:"Inherent design",abbr:"ID"},{id:"protective",label:"Protective measure",abbr:"PM"},{id:"information",label:"Information for safety",abbr:"IFS"}],Mt="safety",Tt="L1",Dt=8,Pt="-",Ct=["prevention","detection","mitigation"],Ut={"S1-E1-C1":"QM","S1-E1-C2":"QM","S1-E1-C3":"QM","S1-E2-C1":"QM","S1-E2-C2":"QM","S1-E2-C3":"QM","S1-E3-C1":"QM","S1-E3-C2":"QM","S1-E3-C3":"A","S1-E4-C1":"QM","S1-E4-C2":"A","S1-E4-C3":"B","S2-E1-C1":"QM","S2-E1-C2":"QM","S2-E1-C3":"QM","S2-E2-C1":"QM","S2-E2-C2":"QM","S2-E2-C3":"A","S2-E3-C1":"QM","S2-E3-C2":"A","S2-E3-C3":"B","S2-E4-C1":"A","S2-E4-C2":"B","S2-E4-C3":"C","S3-E1-C1":"QM","S3-E1-C2":"QM","S3-E1-C3":"A","S3-E2-C1":"QM","S3-E2-C2":"A","S3-E2-C3":"B","S3-E3-C1":"A","S3-E3-C2":"B","S3-E3-C3":"C","S3-E4-C1":"B","S3-E4-C2":"C","S3-E4-C3":"D"},Ft={"1-1":"1","1-2":"1","1-3":"1","1-4":"2","1-5":"3","2-1":"1","2-2":"1","2-3":"2","2-4":"3","2-5":"4","3-1":"1","3-2":"2","3-3":"3","3-4":"4","3-5":"5","4-1":"2","4-2":"3","4-3":"4","4-4":"5","4-5":"5","5-1":"3","5-2":"4","5-3":"5","5-4":"5","5-5":"5"},Wt={"1-1":"Low","1-2":"Low","1-3":"Low","1-4":"Low","1-5":"Medium","2-1":"Low","2-2":"Low","2-3":"Low","2-4":"Medium","2-5":"Medium","3-1":"Low","3-2":"Low","3-3":"Medium","3-4":"Medium","3-5":"High","4-1":"Low","4-2":"Medium","4-3":"Medium","4-4":"High","4-5":"High","5-1":"Medium","5-2":"Medium","5-3":"High","5-4":"High","5-5":"High"},Vt={Low:"Acceptable",Medium:"Acceptable with justification",High:"Not acceptable"},Bt="10",Jt={hara_iso26262:{severity:["S0","S1","S2","S3"],exposure:["E0","E1","E2","E3","E4"],controllability:["C0","C1","C2","C3"]},dfmea_rpn:{severity:Lt=["1","2","3","4","5","6","7","8","9","10"],occurrence:Lt,detection:Lt},hazard_iso14971:{severity:Nt=["1","2","3","4","5"],p1:Nt,p2:Nt,residual_s:Nt,residual_p1:Nt,residual_p2:Nt},fmea_process:{severity:Lt,occurrence:Lt,detection:Lt},fmea_software:{severity:Lt,detection:Lt},fmea_use:{severity:Lt,occurrence:Lt,detection:Lt},security_tir57:{severity:[...ut],exploitability:[...ut],residual_s:[...ut],residual_e:[...ut]}},Qt={hara_iso26262:["failure_mode","operating_scenario","hazard","assumptions","severity","exposure","controllability","safe_state","ftti","rationale","source_function","safety_goal","s_rationale","e_rationale","c_rationale"],dfmea_rpn:["failure_mode","cause","severity","occurrence","detection","rationale"],hazard_iso14971:["failure_mode","operating_scenario","hazard","assumptions","severity","p1","p2","rationale","source_function","safety_goal","s_rationale","p1_rationale","p2_rationale","residual_s","residual_p1","residual_p2","benefit_note"],fmea_process:["process_step","failure_mode","cause","severity","occurrence","current_controls","detection","rationale"],fmea_software:["failure_mode","cause","severity","detection","rationale"],fmea_use:["failure_mode","cause","severity","occurrence","detection","rationale"],security_tir57:["failure_mode","operating_scenario","hazard","assumptions","severity","exploitability","rationale","source_function","safety_goal","s_rationale","e_rationale","residual_s","residual_e","justification","safety_impact","safety_impact_justification"]},Gt=["intended_use","foreseeable_misuse","safety_characteristics"],Yt=["overall_residual_risk","overall_residual_rationale","software_safety_class","software_safety_rationale"],Kt=[...Gt,...Yt],Zt=["Low","Medium","High"],Xt=["A","B","C"],er=["threat_model_method","threat_model_rationale","environment_assumptions","attack_surfaces","related_documents","overall_residual_risk","overall_residual_rationale"],tr=[...er,"lifecycle_sources"],rr={hara:"safety-goal",dfmea:"failure-mode"},ar={hara_iso26262:"SG",dfmea_rpn:"FM",hazard_iso14971:"RSK",fmea_process:"PF",fmea_software:"FM",fmea_use:"FM",security_tir57:"CYB"},ir={hara_iso26262:"HARA",dfmea_rpn:"DFMEA",hazard_iso14971:"RA",fmea_process:"PFMEA",fmea_software:"SFMEA",fmea_use:"UFMEA",security_tir57:"SEC"},nr=["HA"],Array.from(new Set([...Object.values(ir),...Object.values(ar),...nr])).sort(),sr={hara:"hara",dfmea:"dfmea",risk_analysis:"hara",fmea:"dfmea",hazard_analysis:"hara",security_risk:"hara"},or=["hara","dfmea","risk_analysis","fmea","hazard_analysis","security_risk"],cr={hara:"iso26262",risk_analysis:"iso14971",hazard_analysis:"iso14971",dfmea:"design",fmea:"process",security_risk:"tir57"},lr={hara:"iso14971",risk_analysis:"iso14971",hazard_analysis:"iso14971",dfmea:"process",fmea:"process",security_risk:"tir57"},dr={fmea_failure_mode:"Failure mode: the way the design, the process step or the software fails to do what it should, for example a valve that sticks open or a step skipped.",fmea_effect:"Effect: what the failure mode does to the customer, the patient or the next step of the process. Severity rates the effect.",fmea_cause:"Cause: the mechanism or condition behind the failure mode, what has to be true for it to happen. Occurrence rates the cause.",fmea_action:"Action: the requirement that answers the failure mode. The link records how it answers it, and a row at RPN 100 or above with no action is a gap."},pr={...dr,s:"Severity: how badly people could be hurt if the hazardous event happens. S0 no injuries, S1 light, S2 severe but survivable, S3 life-threatening or fatal (ISO 26262-3).",e:"Exposure: how often the operational scenario occurs. E0 incredible, E1 very low, E2 low, E3 medium, E4 high probability (ISO 26262-3).",c:"Controllability: how likely the driver or others can avoid the harm. C0 controllable in general, C1 simply, C2 normally, C3 difficult or uncontrollable (ISO 26262-3).",asil:"Automotive Safety Integrity Level: computed from S, E and C per ISO 26262-3 Table 4. QM means no safety requirement is needed; D is the most demanding.",safe_state:"The condition the system moves to when the function fails, for example handing braking back to the driver.",ftti:"Fault tolerant time interval: the longest time a fault may stay undetected before the hazardous event can occur.",safety_goal:"The top-level safety requirement derived from a hazardous event. One goal per HARA row; it is met by safety requirements that mitigate it.",safety_req:"A requirement of type Safety that mitigates a safety goal. At L1 it is a functional or technical safety requirement and traces to a system requirement.",verified_by:"The test case that proves this safety goal on its operating design domain (ODD).",hazardous_event:"What happens to the vehicle and the people when the failure occurs in this scenario. One per row.",hazard:"The potential harm, for example unintended braking.",assumptions:"The conditions you assume for the rating, one per line, for example light traffic or a speed limit.",fn:"The system function this row analyses, from the project's function tags.",failure_mode:"How the function fails: no function, loss, partial, degradation or misinterpretation.",scenario:"The driving situation in which the failure is analysed. One per row.",dfmea_s:"Severity: how serious the effect of the failure is for the customer, 1 (no effect) to 10 (safety or regulatory failure without warning).",o:"Occurrence: how likely the cause is, 1 (remote) to 10 (very high).",d:"Detection: how likely current controls detect the cause before it reaches the customer, 1 (certain) to 10 (impossible).",rpn:"Risk priority number: S times O times D. Rows at 100 or above are gaps.",accept_analysis:Et("The confirmation review of the HARA (ISO 26262)")},mr={s:"Severity: a measure of the possible consequences of a hazard. 1 Negligible, 2 Minor, 3 Serious, 4 Critical, 5 Catastrophic (ISO 14971).",p1:"P1: the probability of a hazardous situation occurring. 1 Improbable, 2 Remote, 3 Occasional, 4 Probable, 5 Frequent (the example scale of ISO 14971:2007 Annex D, kept in ISO/TR 24971).",p2:"P2: the probability that the hazardous situation leads to harm, given that it occurs. 1 to 5 on the same scale (ISO 14971:2007 Annex D, ISO/TR 24971).",p0:"P0: the overall probability of harm, looked up from P1 and P2 on the 5 by 5 probability table.",risk:"Risk: the combination of the probability of harm and its severity, looked up from P0 and S. Low is acceptable, Medium is acceptable with a justification, High is not acceptable (ISO 14971).",risk_analysis:"Risk analysis: the systematic use of available information to identify hazards and to estimate the risk for each hazardous situation (ISO 14971:2019 definition 3.19 and clause 5). It is the name the standard gives this document.",hazard:"Hazard: a potential source of harm, for example an uncontrolled flow of the drug.",hazardous_situation:"Hazardous situation: the circumstance in which people are exposed to the hazard.",sequence:"Sequence of events: what has to happen for the hazard to become a hazardous situation. One sequence per row.",harm:"Harm: injury or damage to the health of people, or damage to property or the environment.",assumptions:"The conditions you assume for the estimate, one per line, for example the care setting or the patient group.",fn:"The device function this row analyses, from the project's function tags.",risk_control:"Risk control: the measure that reduces the risk. Applied in order: inherent safety by design, then protective measures, then information for safety (ISO 14971 clause 7).",control_req:"A requirement of type Risk control that implements a risk control measure. It carries the control type and traces to a system requirement.",verified_by:"Verification of effectiveness: the test that shows the risk control works, not only that it was implemented (ISO 14971 clause 7.2).",residual:"Residual risk: the risk remaining after the risk control measures are applied, estimated with the same two tables.",benefit:"Benefit-risk analysis: the argument that the medical benefit outweighs a residual risk that is still not acceptable (ISO 14971 clause 7.4).",dfmea_s:"Severity: how serious the effect of the failure is, 1 (no effect) to 10 (harm without warning).",o:"Occurrence: how likely the cause is, 1 (remote) to 10 (very high).",o_sw:"IEC 62304: software failure probability is assumed 100 percent; rate by severity.",d:"Detection: how likely current controls detect the cause before it reaches the patient, 1 (certain) to 10 (impossible).",rpn:"Risk priority number: S times O times D. Rows at 100 or above are gaps.",process_step:"The step of the clinical or manufacturing process this row analyses.",controls:"Current controls: what already stands between this cause and the effect.",intended_use:"Intended use: what the device is for, for which patient, by which user and in which environment. ISO 14971:2019 5.2 asks for it before any hazard is identified.",foreseeable_misuse:"Reasonably foreseeable misuse: use in a way the manufacturer did not intend but can expect, one case per line (ISO 14971:2019 5.2).",safety_characteristics:"Characteristics related to safety: the qualitative and quantitative characteristics that could affect safety, one per line (ISO 14971:2019 5.3).",overall_residual_risk:"Overall residual risk: the risk that remains from the device as a whole, once every individual risk is controlled. Judged by a person against the criteria in the risk management plan (ISO 14971:2019 clause 8).",software_safety_class:"Software safety class: A no injury or damage to health is possible, B non-serious injury is possible, C death or serious injury is possible (IEC 62304 4.3)."},ur={...dr,...mr,accept_analysis:Et("The ISO 14971 clause 8 overall residual risk conclusion")},_r={...dr,dfmea_s:pr.dfmea_s,o:pr.o,d:pr.d,rpn:pr.rpn,accept_analysis:Et("The AIAG-VDA FMEA sign-off")},fr={...dr,dfmea_s:mr.dfmea_s,o:mr.o,o_sw:mr.o_sw,d:mr.d,rpn:mr.rpn,process_step:mr.process_step,controls:mr.controls,fn:mr.fn,risk_control:mr.risk_control,control_req:mr.control_req,verified_by:mr.verified_by,accept_analysis:Et("The FMEA sign-off")},hr={hara_iso26262:{source_function:"Function",failure_mode:"Failure Mode",operating_scenario:"Operational Scenario",effect:"Hazardous Event",hazard:"Hazard",assumptions:"Assumptions",s_rationale:"S evidence",e_rationale:"E evidence",c_rationale:"C evidence",safe_state:"Safe state",ftti:"FTTI",severity:"S",exposure:"E",controllability:"C",safety_goal:"Safety goal",asil:"ASIL"},dfmea_rpn:{source_req:"Source",failure_mode:"Failure Mode",effect:"Effect",severity:"S",cause:"Cause",occurrence:"O",detection:"D",rpn:"RPN"},hazard_iso14971:{source_function:"Function",failure_mode:"Hazard",operating_scenario:"Sequence of events",effect:"Hazardous situation",hazard:"Harm",assumptions:"Assumptions",s_rationale:"S rationale",p1_rationale:"P1 rationale",p2_rationale:"P2 rationale",residual_s:"Residual S",residual_p1:"Residual P1",residual_p2:"Residual P2",residual_level:"Residual risk",benefit_note:"Benefit-risk note",severity:"S",p1:"P1",p2:"P2",safety_goal:"Risk control statement",risk:"Risk"},fmea_process:{source_req:"Source",process_step:"Process step",failure_mode:"Failure mode",effect:"Effect",severity:"S",cause:"Cause",occurrence:"O",current_controls:"Current controls",detection:"D",rpn:"RPN"},fmea_software:{source_req:"Source",failure_mode:"Failure Mode",effect:"Effect",severity:"S",cause:"Cause",occurrence:"O",detection:"D",rpn:"RPN"},fmea_use:{source_req:"Source",failure_mode:"Failure Mode",effect:"Effect",severity:"S",cause:"Cause",occurrence:"O",detection:"D",rpn:"RPN"},security_tir57:{source_function:"Asset",failure_mode:"Threat event",operating_scenario:"Vulnerability",effect:"Impact",hazard:"Harm",assumptions:"Assumptions",s_rationale:"S rationale",e_rationale:"E rationale",residual_s:"Residual S",residual_e:"Residual E",residual_level:"Residual risk",severity:"S",exploitability:"E",risk:"Risk",safety_goal:"Security control statement",safety_impact:"Safety impact",safety_impact_justification:"Safety impact justification",justification:"Justification"}},yr=["asil","rpn","risk","p0"],gr=["approved","approved_at","approved_by","approved_note","field_source","link_roles","mitigation_types"],wr=[...yr,...gr],vr={hara_iso26262:{parent:"HARA",row:"Safety goal",row_statement:"Safety goal",mitigating_requirement:"Safety requirement",lane:"Safety lane",level:"ASIL"},dfmea_rpn:{parent:"DFMEA",row:"Failure mode",row_statement:"Action",mitigating_requirement:"Requirement",lane:"Safety lane",level:"RPN"},hazard_iso14971:{parent:"Risk analysis",row:"Risk",row_statement:"Risk control statement",mitigating_requirement:"Risk control",lane:"Risk lane",level:"Risk"},fmea_process:{parent:"PFMEA",row:"Process failure",row_statement:"Action",mitigating_requirement:"Risk control",lane:"Risk lane",level:"RPN"},fmea_software:{parent:"Software FMEA",row:"Failure mode",row_statement:"Action",mitigating_requirement:"Risk control",lane:"Risk lane",level:"RPN"},fmea_use:{parent:"Use FMEA",row:"Failure mode",row_statement:"Action",mitigating_requirement:"Risk control",lane:"Risk lane",level:"RPN"},security_tir57:{parent:"Security risk analysis",row:"Security risk",row_statement:"Security control statement",mitigating_requirement:"Security control",lane:"Security lane",level:"Risk"}},br=(e,t,r)=>1===e?t:r,kr=[{kind:"unmitigated",scope:"row",fix:"link_requirement",fixLabel:"Draft safety requirement",label:e=>`${e} ${br(e,"safety goal","safety goals")} unmitigated`,matches:e=>e.level!==Pt&&"QM"!==e.level&&!e.linked},{kind:"no_test",scope:"row",fix:"link_test",fixLabel:"Link a test",label:e=>`${e} ${br(e,"safety goal","safety goals")} without a test on its ODD`,matches:e=>e.level!==Pt&&"QM"!==e.level&&e.linked&&!e.verified},{kind:"no_parent",scope:"requirement",fix:"open_item",fixLabel:"Set parent",label:e=>`${e} ${br(e,"safety requirement","safety requirements")} without a system parent`},{kind:"catalog_draft",scope:"analysis",fix:"open_analysis",fixLabel:"Review the scenario catalogs",label:()=>"Scenario catalog not accepted",matchesAnalysis:e=>e.catalogsDraft},jr={kind:"not_accepted",scope:"analysis",fix:"open_analysis",fixLabel:"Accept analysis",label:()=>"Analysis not accepted",matchesAnalysis:e=>e.hasRows&&!e.accepted}],Sr=[{kind:"no_control",scope:"row",fix:"link_requirement",fixLabel:"Draft risk control requirement",label:e=>`${e} ${br(e,"risk","risks")} above Low with no control`,matches:e=>e.level!==Pt&&"Low"!==e.level&&!e.linked},{kind:"not_verified",scope:"row",fix:"link_test",fixLabel:"Link a test",label:e=>`${e} ${br(e,"control","controls")} not verified for effectiveness`,matches:e=>e.level!==Pt&&"Low"!==e.level&&e.linked&&!e.verified},{kind:"no_residual",scope:"row",fix:"open_row",fixLabel:"Assess residual risk",label:e=>`${e} residual ${br(e,"risk","risks")} not assessed`,matches:e=>e.level!==Pt&&"Low"!==e.level&&e.linked&&e.residualLevel===Pt},{kind:"no_parent",scope:"requirement",fix:"open_item",fixLabel:"Set parent",label:e=>`${e} risk ${br(e,"control","controls")} without a system parent`},{kind:"no_scope",scope:"analysis",fix:"open_analysis",fixLabel:"Open the scope",label:()=>"Scope not stated",matchesAnalysis:e=>e.hasRows&&!e.scopeStated},{kind:"no_overall",scope:"analysis",fix:"open_analysis",fixLabel:"Judge the overall residual risk",label:()=>"Overall residual risk not judged",matchesAnalysis:e=>e.hasRows&&e.everyRowSettled&&!e.overallJudged},jr],xr=[{kind:"rpn",scope:"row",fix:"link_requirement",fixLabel:"Link a requirement",label:e=>`${e} at RPN 100 or above with no mitigating requirement`,matches:e=>ua(e.level)&&!e.linked},{kind:"severity",scope:"row",fix:"link_requirement",fixLabel:"Link a requirement",label:e=>`${e} at severity 9 or above below RPN 100 with no mitigating requirement`,matches:e=>!ua(e.level)&&null!==e.severity&&e.severity>=9&&!e.linked},jr],Ar=[{kind:"unmitigated",scope:"row",fix:"link_requirement",fixLabel:"Draft security control",label:e=>`${e} security ${br(e,"risk","risks")} above Low with no control`,matches:e=>bt(e.level)&&!e.linked},{kind:"unassessed",scope:"row",fix:"open_row",fixLabel:"Rate S and E",label:e=>`${e} not assessed`,matches:e=>e.level===Pt},{kind:"not_verified",scope:"row",fix:"link_test",fixLabel:"Link a test",label:e=>`${e} ${br(e,"control","controls")} not verified`,matches:e=>e.linked&&!e.verified},{kind:"no_residual",scope:"row",fix:"open_row",fixLabel:"Assess residual risk",label:e=>`${e} residual not assessed`,matches:e=>e.linked&&e.residualLevel===Pt},{kind:"no_justification",scope:"row",fix:"open_row",fixLabel:"Write the justification",label:e=>`${e} residual without justification`,matches:e=>{const t=String(e.props?.severity??""),r=String(e.props?.residual_s??""),a="5"===t||"5"===r;return(bt(e.residualLevel)||a)&&!String(e.props?.justification??"").trim()}},{kind:"safety_no_hazard",scope:"row",fix:"open_row",fixLabel:"Link a safety hazard",label:e=>`${e} safety impact Yes with no safety hazard`,matches:e=>"Yes"===e.props?.safety_impact&&!e.affectsLinked},{kind:"safety_no_reason",scope:"row",fix:"open_row",fixLabel:"Write the reason",label:e=>`${e} safety impact without a reason`,matches:e=>("Yes"===e.props?.safety_impact||"No"===e.props?.safety_impact)&&!String(e.props?.safety_impact_justification??"").trim()},{kind:"no_parent",scope:"requirement",fix:"open_item",fixLabel:"Set parent",label:e=>`${e} security ${br(e,"control","controls")} without a system parent`},{kind:"no_scope",scope:"analysis",fix:"open_analysis",fixLabel:"Open the threat model",label:()=>"Threat model not stated",matchesAnalysis:e=>e.hasRows&&!e.scopeStated}]}}),Ea=A({"src/core/errors.ts"(){}}),Ia={};function Ra(e,t=va){if("string"!=typeof e)return"";const r=e.replace(ja,"").replace(ka,"");return r.length<=t?r:r.slice(0,t)+"..."}function Oa(e){return"string"!=typeof e?"":e.trim().toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"").replace(/-+/g,"-").replace(/^-|-$/g,"")}function qa(e,t){return{status:"success",data:e,...t?{meta:t}:{}}}function za(e,t,r){return{total_count:e,limit:t,offset:r,has_more:r+t<e}}function $a(e,t){return{status:"error",message:e,next_steps:t}}function Ha(e,t){return $a(`${e} '${Ra(t)}' not found`,[`Verify the ${e.toLowerCase()} ID is correct`,"Use atoms_list_items or atoms_search to find valid IDs"])}function Ma(e){return $a(`${e} role cannot modify project data`,["Contact your org admin to upgrade your role to editor","Use read-only tools (atoms_list_items, atoms_get_item, atoms_search) instead"])}function Ta(e){return $a(`Validation error: ${Ra(e,ba)}`,["Check the parameter types and constraints in the tool description","Ensure all required parameters are provided"])}function Da(){return $a("Not authenticated. Run 'npx @atoms-tech/atoms-mcp login' first.",["Run: npx @atoms-tech/atoms-mcp login","Or set ATOMS_ACCESS_TOKEN environment variable"])}function Pa(){return $a("Your ATOMS session token expired and could not be refreshed automatically.",["Call atoms_status() — it will refresh the token if possible","If that still reports 'expired', restart Claude Desktop (quit and reopen)","If the problem persists, run: npx @atoms-tech/atoms-mcp logout && npx @atoms-tech/atoms-mcp login"])}function Ca(e){return $a("Tool call rejected: project_id is outside the configured ATOMS_MCP_PROJECT_ID scope.",[`This MCP session is bound to project ${e}`,"Pass that project_id, or restart without ATOMS_MCP_PROJECT_ID to access other projects"])}function Ua(e){return $a("Rate limited: too many requests to ATOMS API.",[e?`Wait ${e} seconds before retrying.`:"Wait a few seconds before retrying.","Reduce the frequency of back-to-back tool calls"])}function La(e){return $a(`Parse error: ${Ra(e,ba)}`,["Check that body/title/summary fields contain valid text (no unexpected control characters)","If the content was generated programmatically, validate the string before passing it","Simplify the content and retry — if it succeeds, the original content has a malformed character"])}function Na(e){return function(e){return/stale_item_write|sqlstate\s*40001|could not serialize access/i.test(e)}(e)}function Fa(){return $a("Stale write rejected: this item changed after it was read.",["Call atoms_get_item again and use the returned metadata.updated_at value","Review the current item before retrying so you do not overwrite newer work","Retry atoms_update_item or atoms_link_items with the fresh expected timestamp"])}function Wa(){return $a("Save paused because this item is already being written.",["Wait a few seconds, read the item again, and retry once","Do not repeatedly retry the same stale payload"])}function Va(){return $a("Item saves are temporarily paused by an ATOMS operator.",["Keep your local draft and wait for the operator to re-enable saves","Do not repeatedly retry while the safety brake is active"])}function Ba(){return $a("Item save rejected because this item is in Trash.",["Restore the item before changing it","Do not retry the same save while the item remains in Trash"])}function Ja(e){const t=e instanceof Error?e.message:String(e&&"object"==typeof e&&"message"in e?e.message??"":e),r=e&&"object"==typeof e&&"code"in e?String(e.code??""):"";return/not authenticated/i.test(t)?Da():function(e){let t="";if(e instanceof Error)t=e.message;else if(e&&"object"==typeof e){const r=e;t="string"==typeof r.message?r.message:"string"==typeof r.code?r.code:String(e)}else null!=e&&(t=String(e));return/jwt\s*expired|invalid.?jwt|token.*(?:has\s+)?expired|jwt.*invalid|pgrst301/i.test(t)}(e)?Pa():/rate.?limit|too many requests|429/i.test(t)?Ua():Na(t)?Fa():/soft_deleted_item_write/i.test(t)?Ba():/save_temporarily_unavailable/i.test(t)?Va():"55P03"===r||"57014"===r||/item_save_busy|statement timeout|request timeout|upstream request timeout/i.test(t)?Wa():"42501"===r&&/org_project_field_admin_required/i.test(t)?Ma("Org admin"):"42501"===r&&/project_field/i.test(t)?Ma("Project admin or owner"):"P0002"===r&&/project_field/i.test(t)?$a("Project field not found.",["Refresh the field list and retry with an existing field name"]):["22023","23505","23514"].includes(r)&&/project_field/i.test(t)?Ta(t.replace(/^project_field_/i,"").replace(/_/g," ")):/invalid input syntax|invalid json|unexpected token|malformed|json parse/i.test(t)?La(t):Ga(t)}function Qa(e){const t=e instanceof Error?e.message:String(e&&"object"==typeof e&&"message"in e?e.message??"":e??""),r=e&&"object"==typeof e&&"code"in e?String(e.code??""):"";return["40001","PT409","55P03","57014"].includes(r)||/stale_item_write|soft_deleted_item_write|save_temporarily_unavailable|item_save_busy|statement timeout|request timeout|upstream request timeout|fetch failed|network error|connection (?:closed|reset|refused)/i.test(t)}function Ga(e){return/row.level security|permission denied for/i.test(e)?$a("Permission denied: your account does not have write access to this project.",["Verify you have editor or admin role in this project","Contact your org admin to upgrade your permissions","Use atoms_list_projects to confirm the project_id is one you have access to"]):$a(`Database error: ${Ra(e,ba)}`,["This may be a temporary issue — try again","If the error persists, check that the project_id is valid"])}function Ya(e){return null==e?e:`<user_content>${e.replace(/<\/\s*user_content\s*>/gi,"</user_content>")}</user_content>`}function Ka(e){return"string"!=typeof e?e:e.replace(/<\s*\/?\s*user_content\s*>/gi,"")}function Za(e){return Sa.has(e)}function Xa(e,t,r=!1){return"string"==typeof e?r||null!=t&&Sa.has(t)?Ya(e):e:Array.isArray(e)?e.map(e=>Xa(e,t,r)):e&&"object"==typeof e?Object.fromEntries(Object.entries(e).map(([e,t])=>[e,Xa(t,e,r||xa.has(e))])):e}function ei(e){if(JSON.stringify(e,null,2).length>wa&&"success"===e.status&&Array.isArray(e.data)){const t=Math.max(1,Math.floor(e.data.length/2)),r={...e,data:e.data.slice(0,t),meta:{...e.meta,truncated:!0,truncation_message:`Response truncated from ${e.data.length} to ${t} items. Use 'offset' parameter or add filters to see more results.`}};return{content:[{type:"text",text:JSON.stringify(Xa(r),null,2)}],structuredContent:r}}return{content:[{type:"text",text:JSON.stringify(Xa(e),null,2)}],structuredContent:e}}function ti(e){return{content:[{type:"text",text:JSON.stringify(Xa(e),null,2)}],structuredContent:e,isError:!0}}E(Ia,{CHARACTER_LIMIT:()=>wa,USER_CONTENT_KEYS:()=>Sa,accessDeniedError:()=>Ma,authError:()=>Da,classifyError:()=>Ja,dbError:()=>Ga,errorResponse:()=>$a,formatErrorResult:()=>ti,formatToolResult:()=>ei,isAutoWrappedKey:()=>Za,isItemSaveStopError:()=>Qa,isStaleWriteErrorMessage:()=>Na,jwtExpiredError:()=>Pa,notFoundError:()=>Ha,paginationMeta:()=>za,parseError:()=>La,rateLimitError:()=>Ua,sanitizeForEcho:()=>Ra,sanitizeTagOrLevel:()=>Oa,saveBusyError:()=>Wa,saveUnavailableError:()=>Va,scopeError:()=>Ca,softDeletedItemError:()=>Ba,staleWriteError:()=>Fa,stripUserContentMarkers:()=>Ka,success:()=>qa,validationError:()=>Ta,wrapMcpUserContentFields:()=>Xa,wrapUserContent:()=>Ya});var ri,ai=A({"src/tools/_base.ts"(){Ea(),wa=25e3,va=50,ba=200,ja=new RegExp("[\\u0000-\\u001F\\u007F]","g"),ka=new RegExp("[\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u206F\\uFEFF]","g"),Sa=new Set(["body","description","domain","domains","error","level","levels","message","name","note","summary","title","value","effect","failure_mode","cause","operating_scenario","safe_state","ftti","rationale","hazard","assumptions","source_function"]),xa=new Set(["fields"])}});var ii=A({"src/apps/register.ts"(){ri={resourceDomains:["fonts.googleapis.com","fonts.gstatic.com"]}}});function ni(e){return e.replace(/[\\%_*]/g,e=>`\\${e}`)}function si(e){return`"${`*${ni(e)}*`.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function oi(e){return ni(e)}function ci(e){return`*"${ni(e)}"*`}var li=A({"src/core/postgrest.ts"(){}});async function di(e,t,r){let a=e.from("items").select("*",{count:"exact"}).eq("project_id",t).is("deleted_at",null).order("created_at",{ascending:!0});r.type&&(a=a.eq("type",r.type)),r.domain&&(a=a.filter("data->tags->>domains","ilike",ci(r.domain))),r.level&&(a=a.filter("data->tags->>level","ilike",oi(r.level))),a=a.range(r.offset,r.offset+r.limit-1);const{data:i,error:n,count:s}=await a;if(n)throw new Error(n.message);return{items:i??[],totalCount:s??0}}async function pi(e,t,r){const{data:a,error:i}=await e.from("items").select("*").eq("id",r).eq("project_id",t).is("deleted_at",null).maybeSingle();if(i)throw new Error(i.message);return a}async function mi(e,t){if(t.baseline_id&&t.as_of)throw new Error("baseline_id and as_of are mutually exclusive");if(t.as_of)return t.as_of;if(t.baseline_id){const{data:r,error:a}=await e.from("baselines").select("project_id, snapshot_at, deleted_at").eq("id",t.baseline_id).maybeSingle();if(a)throw new Error(`resolveSnapshotAt: ${a.message}`);if(!r||null!==r.deleted_at)throw new Error(`Baseline not found: ${t.baseline_id}`);if(r.project_id!==t.project_id)throw new Error("Baseline does not belong to this project");return r.snapshot_at}return null}async function ui(e,t){const{data:r,error:a}=await e.from("baselines").select("id, project_id, name, description, field_definitions_snapshot, snapshot_at, created_at, created_by, locked_at, locked_by, deleted_at, release_version, release_status").eq("id",t).maybeSingle();if(a)throw new Error(`getBaselineById: ${a.message}`);return r&&null===r.deleted_at?r:null}function _i(e){return{id:e.id,project_id:e.project_id,type:e.type,title:e.title,data:e.data,created_by:e.changed_by,updated_by:e.changed_by,created_at:e.valid_from,updated_at:e.valid_from,deleted_at:null}}async function fi(e,t,r){const{data:a,error:i}=await e.rpc("project_state_at",{p_project_id:t,p_snapshot_at:r});if(i)throw new Error(`projectStateAt: ${i.message}`);return a??[]}async function hi(e,t,r,a){const i=r.trim();if(!i)return{items:[],totalCount:0};let n=null;if(a.asset&&a.asset.trim()){const{data:r,error:i}=await e.from("test_results").select("item_id").eq("project_id",t).eq("asset",a.asset.trim());if(i)throw new Error(i.message);const s=new Set;for(const e of r??[])s.add(e.item_id);if(n=Array.from(s),0===n.length)return{items:[],totalCount:0}}let s=e.from("items").select("*",{count:"exact"}).eq("project_id",t).is("deleted_at",null).or(`title.ilike.${si(i)},data->>body.ilike.${si(i)},data->>summary.ilike.${si(i)}`).limit(a.limit);a.type&&(s=s.eq("type",a.type)),a.domain&&(s=s.filter("data->tags->>domains","ilike",ci(a.domain))),a.level&&(s=s.filter("data->tags->>level","ilike",oi(a.level))),n&&(s=s.in("id",n));const{data:o,error:c,count:l}=await s;if(c)throw new Error(c.message);return{items:o??[],totalCount:l??0}}function yi(e,t){const r=new Set(e.map(e=>e.id)),a=new Set,i=[],n=(e,t,n)=>{if(e===t)return;if(!r.has(e)||!r.has(t))return;const s=`${e}|${t}|${n}`;a.has(s)||(a.add(s),i.push({from_id:e,to_id:t,type:n}))};for(const e of t)n(e.from_id,e.to_id,e.type);for(const t of e){const e=t.data?.relationships??{},r=t=>Array.isArray(e[t])?e[t].filter(e=>"string"==typeof e):[];for(const e of r("parents"))n(t.id,e,"parent");for(const e of r("children"))n(t.id,e,"child");for(const e of r("related"))n(t.id,e,"related");for(const e of r("verifies"))n(t.id,e,"verifies");for(const e of r("verified_by"))n(t.id,e,"verified_by");for(const e of r("mitigated_by"))n(t.id,e,"mitigated_by");for(const e of r("mitigates"))n(t.id,e,"mitigates")}return i}async function gi(e,t,r){const a="requirement"===r?"REQ":"test-case"===r?"TC":"note"===r?"NOTE":"TABLE",{data:i,error:n}=await e.rpc("next_item_id",{p_project_id:t,p_prefix:a});if(n)throw new Error(`ID generation failed: ${n.message}`);return i}var wi=A({"src/db/queries.ts"(){li(),Aa()}}),vi={};async function bi(){try{const e=await He(async()=>{const e=await qe();return await async function(e){const{data:t,error:r}=await e.from("projects").select("id, name, description, org_id, created_at, organizations(name)").is("deleted_at",null).order("created_at",{ascending:!1});if(r)throw new Error(r.message);return(t??[]).map(e=>({id:e.id,name:e.name,description:e.description,org_id:e.org_id,org_name:e.organizations?.name??"Unknown",created_at:e.created_at}))}(e)}),t=st(),r=T,a=C().project_scope,i=a?new Set(a):null;return ei(qa(e.filter(e=>!r||e.org_id===r).filter(e=>!t||e.id===t).filter(e=>!i||i.has(e.id)).map(e=>({id:e.id,name:e.name,description:e.description,org_id:e.org_id,org_name:e.org_name,created_at:e.created_at}))))}catch(e){return ti(Ja(e))}}E(vi,{listProjectsHandler:()=>bi});var ji=A({"src/tools/list-projects.ts"(){Le(),wi(),ct(),U(),ai()}}),ki={};async function Si(e){try{const t=await qe();let r,a,i=null;try{i=await mi(t,{baseline_id:e.baseline_id,as_of:e.as_of,project_id:e.project_id})}catch(e){return ti(Ga(e.message))}if(i){const n=(await fi(t,e.project_id,i)).filter(t=>{if(e.type&&t.type!==e.type)return!1;const r=t.data??{};if(e.domain){const t=r?.tags?.domains??[];if(!Array.isArray(t)||!t.some(t=>Jr(t)===Jr(e.domain)))return!1}return!e.level||zr(r?.tags?.level)===zr(e.level)});a=n.length,r=n.slice(e.offset,e.offset+e.limit).map(e=>({id:e.id,title:e.title,type:e.type,data:e.data}))}else{const i=await di(t,e.project_id,{type:e.type,domain:e.domain,level:e.level,limit:e.limit,offset:e.offset});r=i.items,a=i.totalCount}return ei(qa(r.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level,req_type:e.data?.req_type})),za(a,e.limit,e.offset)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}E(ki,{listItemsHandler:()=>Si});var xi,Ai=A({"src/tools/list-items.ts"(){Le(),wi(),ai(),Aa()}}),Ei={};function Ii(e,t){if(!t)return e;const r={...e};for(const e of xi){const a=t[e];null!=a&&(r[e]=a)}return r}async function Ri(e){try{const r=await qe();let a=null;try{a=await mi(r,{baseline_id:e.baseline_id,as_of:e.as_of,project_id:e.project_id})}catch(e){return ti($a(e.message,["Pass baseline_id OR as_of, not both"]))}const i=a?await async function(e,t,r,a){const{data:i,error:n}=await e.rpc("item_state_at",{p_item_id:r,p_project_id:t,p_snapshot_at:a});if(n)throw new Error(`itemStateAt: ${n.message}`);return(i??[])[0]??null}(r,e.project_id,e.item_id,a).then(e=>e?_i(e):null):await pi(r,e.project_id,e.item_id);if(!i)return ti(Ha("Item",e.item_id));const n={parents:[],children:[],related:[],verified_by:[],verifies:[],mitigated_by:[],mitigates:[]};if(a){const e=i.data?.relationships??{};for(const t of Object.keys(n)){const r=e[t];Array.isArray(r)&&(n[t]=r.slice())}}else{const t=await async function(e,t,r){const{data:a,error:i}=await e.from("item_relationships").select("from_id, to_id, type").eq("project_id",t).or(`from_id.eq.${r},to_id.eq.${r}`);if(i)throw new Error(i.message);return a??[]}(r,e.project_id,e.item_id),a={parent:"parents",child:"children",related:"related",verifies:"verifies",verified_by:"verified_by",mitigated_by:"mitigated_by",mitigates:"mitigates"},s={parent:"children",child:"parents",verified_by:"verifies",verifies:"verified_by",mitigated_by:"mitigates",mitigates:"mitigated_by",related:"related"},o=(e,t)=>{if(!(e in n))return;const r=n[e];r.includes(t)||r.push(t)},c=new Set([e.item_id]);for(const e of t)c.add(e.from_id),c.add(e.to_id);const l=i.data?.relationships??{};for(const e of Object.keys(n)){const t=l[e];if(Array.isArray(t))for(const e of t)"string"==typeof e&&c.add(e)}let d=new Set([e.item_id]);const p=Array.from(c);if(p.length>0){const{data:t,error:a}=await r.from("items").select("id").eq("project_id",e.project_id).is("deleted_at",null).in("id",p);if(a)throw new Error(a.message);d=new Set((t??[]).map(e=>e.id)),d.add(e.item_id)}const m=e=>d.has(e);for(const r of t)if(r.from_id===e.item_id){if(!m(r.to_id))continue;o(a[r.type]??r.type,r.to_id)}else{if(!m(r.from_id))continue;o(s[r.type]??r.type,r.from_id)}for(const t of Object.keys(n)){const r=l[t];if(Array.isArray(r))for(const a of r)"string"==typeof a&&a!==e.item_id&&m(a)&&o(t,a)}}let s=[];if("test-case"===i.type)if(a)s=(i.data?.runs??[]).map(e=>({base:{result:e.result??"not-run",run_by:e.by??e.run_by??null,run_at:e.date??e.run_at??"",note:e.note??null,..."string"==typeof e.asset?{asset:e.asset}:{}},canonical:e})).filter(e=>"string"==typeof e.base.run_at&&e.base.run_at.length>0).sort((e,t)=>String(t.base.run_at).localeCompare(String(e.base.run_at))).slice(0,20).map(e=>Ii(e.base,e.canonical));else{const{data:t,error:a}=await r.from("test_results").select("result, run_by, run_at, note, asset").eq("item_id",e.item_id).eq("project_id",e.project_id).order("run_at",{ascending:!1}).limit(20);if(!a&&t){const e=new Map;for(const t of i.data?.runs??[])"string"==typeof t?.date&&e.set(t.date,t);s=t.map(t=>Ii(t,e.get(String(t.run_at))))}}return ei(qa({id:i.id,type:i.type,req_type:i.data?.req_type,title:i.title,summary:i.data?.summary,body:i.data?.body,domains:i.data?.tags?.domains??[],level:i.data?.tags?.level,tags:i.data?.tags??{domains:[]},ownership:i.data?.ownership??{primary:null,additional:[]},relationships:n,links:i.data?.links??[],fields:(t=i.data?.fields,!t||"object"!=typeof t||Array.isArray(t)?{}:Object.fromEntries(Object.entries(t).filter(e=>"string"==typeof e[1]))),status:i.data?.status,..."test-case"===i.type?{latest_result:s[0]?.result??"not-run",test_results:s}:{},metadata:{created_at:i.created_at,created_by:i.created_by,updated_at:i.updated_at,updated_by:i.updated_by,source_id:i.data?.metadata?.source_id}}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}var t}E(Ei,{getItemHandler:()=>Ri});var Oi=A({"src/tools/get-item.ts"(){Le(),wi(),ai(),xi=["point","release_id","build","environment","tooling","step_results"]}}),qi={};async function zi(e){try{const t=await qe();let r,a,i=null;try{i=await mi(t,{baseline_id:e.baseline_id,project_id:e.project_id})}catch(e){return ti(Ga(e.message))}if(i){const n=await fi(t,e.project_id,i),s=e.query.trim().toLowerCase(),o=0===s.length?[]:n.filter(t=>{if(e.type&&t.type!==e.type)return!1;const r=t.data??{};return[t.title,r?.body,r?.summary].filter(e=>"string"==typeof e).map(e=>e.toLowerCase()).some(e=>e.includes(s))});a=o.length,r=o.slice(0,e.limit).map(e=>({id:e.id,title:e.title,type:e.type,data:e.data}))}else{const i=await hi(t,e.project_id,e.query,{type:e.type,limit:e.limit,asset:e.asset});r=i.items,a=i.totalCount}return ei(qa(r.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level})),za(a,e.limit,0)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}E(qi,{searchHandler:()=>zi});var $i=A({"src/tools/search.ts"(){Le(),wi(),ai()}}),Hi={};async function Mi(e){try{const t=await qe(),{access_token:r}=await je(),{items:a,totalCount:i}=await async function(e,t,r,a){const{ATOMS_SUPABASE_URL:i,ATOMS_SUPABASE_ANON_KEY:n}=await Promise.resolve().then(()=>(we(),ye)),s=await fetch(`${i}/functions/v1/embed`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a.accessToken}`,apikey:n},body:JSON.stringify({input:r})});if(!s.ok)throw new Error(`Embed function failed: ${s.status}`);const{embedding:o}=await s.json(),{data:c,error:l}=await e.rpc("semantic_search",{query_embedding:o,p_project_id:t,match_threshold:.3,match_count:a.limit,p_type:a.type||null});if(l)throw new Error(l.message);return{items:c??[],totalCount:(c??[]).length,method:"semantic"}}(t,e.project_id,e.query,{type:e.type,limit:e.limit,accessToken:r});return ei(qa(a.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level})),za(i,e.limit,0)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}E(Hi,{semanticSearchHandler:()=>Mi});var Ti=A({"src/tools/semantic-search.ts"(){Le(),Re(),wi(),ai()}}),Di={};async function Pi(e){try{if(e.baseline_id&&e.as_of)return ti(Ta("baseline_id and as_of are mutually exclusive"));const t=await qe();let r;if(e.baseline_id){const a=await ui(t,e.baseline_id);if(!a)return ti(Ta(`Baseline ${e.baseline_id} not found`));if(a.project_id!==e.project_id)return ti(Ta("Baseline does not belong to this project"));r=a.snapshot_at}else e.as_of&&(r=e.as_of);const{covered:a,uncovered:i,total:n}=await async function(e,t,r){let a,i,n;if(r.snapshotAt){const s=(await fi(e,t,r.snapshotAt)).map(_i);a=s.filter(e=>"requirement"===e.type),i=s.map(e=>({id:e.id,data:e.data})),n=[]}else{const{data:r,error:s}=await e.from("items").select("*").eq("project_id",t).eq("type","requirement").is("deleted_at",null);if(s)throw new Error(s.message);a=r??[];const{data:o,error:c}=await e.from("items").select("id, data").eq("project_id",t).is("deleted_at",null);if(c)throw new Error(c.message);i=o??[];const{data:l,error:d}=await e.from("item_relationships").select("from_id, to_id, type").eq("project_id",t);if(d)throw new Error(d.message);n=l??[]}const s=a.filter(e=>!(r.domain&&!(e.data?.tags?.domains??[]).some(e=>Jr(e)===Jr(r.domain))||r.level&&zr(e.data?.tags?.level)!==zr(r.level))),o=yi(i,n),c=new Set;for(const e of o)"verifies"===e.type?c.add(e.to_id):"verified_by"===e.type&&c.add(e.from_id);return{covered:s.filter(e=>c.has(e.id)),uncovered:s.filter(e=>!c.has(e.id)),total:s.length}}(t,e.project_id,{domain:e.domain,level:e.level,snapshotAt:r}),s=i.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level}));return ei(qa({covered:a.length,uncovered:i.length,total:n,coverage_percent:n>0?Math.round(a.length/n*1e3)/10:100,uncovered_items:s}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}E(Di,{getCoverageHandler:()=>Pi});var Ci,Ui=A({"src/tools/get-coverage.ts"(){Le(),wi(),ai()}}),Li={};async function Ni(e){try{const t=await qe();let r;if(e.release_id){const a=await ui(t,e.release_id);if(!a)return ti(Ta(`Release ${e.release_id} not found`));if(a.project_id!==e.project_id)return ti(Ta("Release does not belong to this project"));if(null===a.release_version)return ti($a(`Baseline "${a.name}" is not a formal Release.`,["Promote the baseline to a Release first (Snapshots view in the app)","Use atoms_list_baselines to see which baselines carry a release_version"]));r=a}else{const{data:a,error:i}=await t.from("baselines").select("id, release_version, release_status").eq("project_id",e.project_id).eq("release_status","released").is("deleted_at",null).maybeSingle();if(i)return ti(Ga(i.message));if(!a)return ti(Ci);r=a}const{data:a,error:i}=await t.rpc("verification_readiness",{p_project_id:e.project_id,p_release_id:r.id});if(i)return ti(Ja(i));const{data:n,error:s}=await t.from("verification_status").select("requirement_id, state, stale, points_total, points_run").eq("project_id",e.project_id).eq("release_id",r.id);if(s)return ti(Ga(s.message));const o=(n??[]).slice().sort((e,t)=>e.requirement_id<t.requirement_id?-1:e.requirement_id>t.requirement_id?1:0),c={release:{id:r.id,version:r.release_version,status:r.release_status},readiness:a??{},requirements:o,...0===o.length?{hint:"projection empty; an admin may need to run rebuild_verification_from_runs for this project"}:{}};if(e.include_gaps){const{data:a,error:i}=await t.rpc("verification_notrun_points",{p_project_id:e.project_id,p_release_id:r.id});if(i)return ti(Ja(i));c.gaps=a??[]}return ei(qa(c))}catch(e){return ti(Ja(e))}}E(Li,{verificationStatusHandler:()=>Ni});var Fi=A({"src/tools/verification-status.ts"(){Le(),wi(),ai(),Ci=$a("This project has no current formal Release.",["Promote a locked baseline to a Release first (Snapshots view in the app)","Or pass release_id explicitly to query a specific (e.g. superseded) Release","Use atoms_list_baselines to see which baselines carry a release_version"])}});function Wi(e){if(!e||"object"!=typeof e)return{};const t=e,r=t.data,a=r&&"object"==typeof r?{...r}:{};return"title"in t&&(a.title=t.title),"type"in t&&(a.type=t.type),a}var Vi=A({"src/core/history.ts"(){}}),Bi={};async function Ji(e){try{const t=await qe(),r=await async function(e,t,r,a){const{data:i,error:n}=await e.from("item_revisions").select("id, revision_number, fields_snapshot, valid_from, changed_by, event_source, event_session_id").eq("item_id",r).eq("project_id",t).order("revision_number",{ascending:!1}).limit(a);if(n)throw new Error(n.message);return i??[]}(t,e.project_id,e.item_id,e.limit);if(r.length>0){const t=function(e){return e.map((t,r)=>{const a=e[r+1]??null,i=1===t.revision_number||null===a,n="system"===t.event_source?"system_normalization":1===t.revision_number?"created":"updated",s=i?["(created)"]:function(e,t){const r=new Set([...Object.keys(e),...Object.keys(t)]),a=[];for(const i of r)JSON.stringify(e[i])!==JSON.stringify(t[i])&&a.push(i);return a}(Wi(a?.fields_snapshot),Wi(t.fields_snapshot));return{id:t.id,event_type:n,changed_by:t.changed_by??null,changed_at:t.valid_from,actor:"mcp"===t.event_source?"mcp_claude":"user",session_id:t.event_session_id??null,fields_changed:s}})}(r);return ei(qa(t,za(t.length,e.limit,0)))}const a=await async function(e,t,r,a){const{data:i,error:n}=await e.from("change_history").select("*").eq("item_id",r).eq("project_id",t).order("changed_at",{ascending:!1}).limit(a);if(n)throw new Error(n.message);return i??[]}(t,e.project_id,e.item_id,e.limit),i=a.map(e=>{const t=[],r=e.old_data,a=e.new_data;if(r&&a){const e=new Set([...Object.keys(r),...Object.keys(a)]);for(const i of e)JSON.stringify(r[i])!==JSON.stringify(a[i])&&t.push(i)}else a?t.push("(created)"):r&&t.push("(deleted)");return{id:e.id,event_type:e.event_type,changed_by:e.changed_by??null,changed_at:e.changed_at,actor:e.actor??"user",session_id:e.session_id??null,fields_changed:t}});return ei(qa(i,za(i.length,e.limit,0)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}E(Bi,{getHistoryHandler:()=>Ji});var Qi=A({"src/tools/get-history.ts"(){Le(),wi(),Vi(),ai()}});function Gi(e){return`item_${e.replace(/[^A-Za-z0-9_]/g,"_")||"node"}`}function Yi(e){const t=Zi(`${e.id}: ${e.title}`);return"test-case"===e.type?`(["${t}"])`:"note"===e.type?`("${t}")`:`["${t}"]`}function Ki(e){const t=Gi(e.from_id),r=Gi(e.to_id);return"child"===e.type?`${t} --\x3e ${r}`:"verified_by"===e.type?`${t} -.-> ${r}`:"related"===e.type?`${t} -.- ${r}`:null}function Zi(e){return e.replace(/"/g,"'").replace(/[[\]{}()]/g,"").substring(0,70)}var Xi=A({"src/core/mermaid.ts"(){}}),en={};async function tn(e){try{if(e.baseline_id&&e.as_of)return ti(Ta("baseline_id and as_of are mutually exclusive"));const t=await qe();let r,a,i;if(e.baseline_id){const a=await ui(t,e.baseline_id);if(!a)return ti(Ta(`Baseline ${e.baseline_id} not found`));if(a.project_id!==e.project_id)return ti(Ta("Baseline does not belong to this project"));r=a.snapshot_at}else e.as_of&&(r=e.as_of);if(r){a=(await fi(t,e.project_id,r)).map(_i).map(e=>({id:e.id,title:e.title,type:e.type,data:e.data})),i=yi(a,[])}else{const{data:r,error:n}=await t.from("items").select("id, title, type, data").eq("project_id",e.project_id).is("deleted_at",null);if(n)throw new Error(n.message);a=r??[];const{data:s,error:o}=await t.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id);if(o)throw new Error(o.message);i=yi(a,s??[])}return ei(qa(function(e){const t=e.items,r=e.relationships??[],a=Math.max(0,e.depth);if(0===t.length)return{mermaid:`graph TD\n empty[${Zi(e.emptyLabel??"No items in project")}]`,node_count:0,edge_count:0};const i=new Map(t.map(e=>[e.id,e])),n=function(e,t,r,a){if(r)return[r];const i=new Set(t.filter(e=>"child"===e.type).map(e=>e.to_id));return e.filter(e=>!i.has(e.id)).filter(e=>a||"test-case"!==e.type).map(e=>e.id)}(t,r,e.rootItemId,e.includeTests),s=n.map(e=>({id:e,depth:0})),o=new Set,c=new Set,l=[],d=[];for(;s.length>0;){const{id:t,depth:n}=s.shift();if(o.has(t)||n>a)continue;const p=i.get(t);if(p&&(e.includeTests||"test-case"!==p.type)){o.add(t),l.push(` ${Gi(t)}${Yi(p)}`);for(const l of r){if(l.from_id!==t)continue;const r=i.get(l.to_id);if(!r)continue;if(!e.includeTests&&"test-case"===r.type)continue;if(n+1>a)continue;const p=Ki(l);if(!p)continue;const m=`${l.from_id}:${l.type}:${l.to_id}`;c.has(m)||(c.add(m),d.push(` ${p}`),o.has(l.to_id)||s.push({id:l.to_id,depth:n+1}))}}}return{mermaid:["graph TD",...l,...d].join("\n"),node_count:o.size,edge_count:c.size}}({items:a,relationships:i,rootItemId:e.root_item_id,depth:e.depth,includeTests:e.include_tests,emptyLabel:"No items in project"})))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}E(en,{exportMermaidHandler:()=>tn});var rn,an,nn=A({"src/tools/export-mermaid.ts"(){Le(),wi(),ai(),Xi()}});function sn(e){const t=process.env.ATOMS_MCP_REQUIRE_CONFIRMATION?.trim();if(t){const r=t.toLowerCase();if("1"===r||"true"===r||"all"===r)return!0;if(t.split(",").map(e=>e.trim()).filter(Boolean).includes(e))return!0}return!!C().require_confirmation_for.includes(e)}function on(e,t=rn){const r=Date.now()+t,a={...e,exp:r},i=Buffer.from(JSON.stringify(a)).toString("base64url");return{token:`${i}.${k.default.createHmac("sha256",an).update(i).digest("base64url")}`,expires_at:new Date(r).toISOString(),expires_in_seconds:Math.round(t/1e3)}}function cn(e,t){if("string"!=typeof e||!e.includes("."))return{valid:!1,reason:"Token format invalid"};const[r,a]=e.split(".");if(!r||!a)return{valid:!1,reason:"Token format invalid"};const i=k.default.createHmac("sha256",an).update(r).digest("base64url"),n=Buffer.from(a,"utf8"),s=Buffer.from(i,"utf8");if(n.length!==s.length||!k.default.timingSafeEqual(n,s))return{valid:!1,reason:"Token signature invalid"};let o;try{o=JSON.parse(Buffer.from(r,"base64url").toString("utf8"))}catch{return{valid:!1,reason:"Token payload malformed"}}return"number"!=typeof o.exp||o.exp<Date.now()?{valid:!1,reason:"Token expired"}:o.tool!==t.tool?{valid:!1,reason:"Token issued for a different tool"}:o.project_id!==t.project_id?{valid:!1,reason:"Token issued for a different project"}:o.target!==t.target?{valid:!1,reason:"Token issued for a different target"}:{valid:!0}}function ln(e){if(Array.isArray(e))return e.map(ln);if(null!==e&&"object"==typeof e){const t={};for(const r of Object.keys(e).sort())t[r]=ln(e[r]);return t}return e}function dn(e){const t=JSON.stringify(ln(e));return k.default.createHash("sha256").update(t).digest("base64url").slice(0,16)}var pn=A({"src/middleware/confirmation.ts"(){U(),rn=6e4,an=(()=>{const e=process.env.ATOMS_MCP_CONFIRMATION_SECRET;return e&&e.length>=16?e:k.default.randomBytes(32).toString("base64url")})()}}),mn={};function un(e){const t=Oa(e??"");return"sub-system"===t?"subsystem":t}async function _n(e,t){const{data:r}=await e.from("metadata").select("levels").eq("project_id",t).maybeSingle();return{levels:r?.levels??[]}}async function fn(e,t,r){const a=Oa(r??""),i=un(a);if(!a)return!1;if(!q(C(),"atoms_create_level"))return!1;try{const{levels:r}=await _n(e,t);if(r.some(e=>un(e)===i))return!1;const{error:n}=await e.rpc("mutate_project_levels",{p_project_id:t,p_action:"add",p_values:[a]});return!n}catch{return!1}}async function hn(e){try{const t=await qe(),{levels:r}=await _n(t,e.project_id),{data:a}=await t.from("items").select("data").eq("project_id",e.project_id).is("deleted_at",null),i={};for(const e of a??[]){const t=e.data?.tags,r=un(t?.level);r&&(i[r]=(i[r]??0)+1)}return ei(qa({levels:r.map((e,t)=>({name:e,prefix:`L${t}`,item_count:i[un(e)]??0})),total:r.length,note:"L# is the level's position in this list (0-based). Names are user-defined."}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}async function yn(e){try{const t=await qe();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const{levels:r}=await _n(t,e.project_id),a=Oa(e.name),i=un(a);if(!a)return ti(Ta("Level name cannot be empty."));if(r.some(e=>un(e)===i))return ti(Ta(`Level "${a}" already exists in this project.`));const{data:n,error:s}=await t.rpc("mutate_project_levels",{p_project_id:e.project_id,p_action:"add",p_values:[a]});if(s)throw new Error(s.message);const o=(n?.levels??[...r,a]).indexOf(a),c=`L${o>=0?o:r.length}`;return ei(qa({level:{name:a,prefix:c},message:`Level "${a}" registered as ${c}. Tag items with level="${a}".`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}async function gn(e){try{const t=await qe();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const r=un(Ka(e.name));if(!r)return ti(Ta("Level name cannot be empty."));const{levels:a}=await _n(t,e.project_id),i=a.filter(e=>un(e)===r),{data:n,error:s}=await t.from("items").select("id, data").eq("project_id",e.project_id).is("deleted_at",null);if(s)throw new Error(s.message);const o=(n??[]).filter(e=>un(e.data?.tags?.level)===r).length;if(0===i.length&&0===o)return ti(Ha("Level",e.name));const c=i[0]??e.name;if(o>0)return ti(Ta(`Level "${c}" is used by ${o} item${o>1?"s":""}. Retag them with atoms_update_item (set a different level) before deleting.`));if(sn("atoms_delete_level")){const t={tool:"atoms_delete_level",project_id:e.project_id,target:r};if(!e.confirmation_token){const e=on(t);return ei(qa({status:"confirmation_required",preview:{level_name:c,items_using_count:0},confirmation_token:e.token,expires_at:e.expires_at,expires_in_seconds:e.expires_in_seconds,message:`Re-call atoms_delete_level with the same project_id, name, and confirmation_token within ${e.expires_in_seconds}s to execute.`}))}const a=cn(e.confirmation_token,t);if(!a.valid)return ti(Ta(`confirmation_token rejected: ${a.reason}`))}const{data:l,error:d}=await t.rpc("mutate_project_levels",{p_project_id:e.project_id,p_action:"remove",p_values:i});if(d)throw new Error(d.message);const p=l?.levels??[],m=$r(a,p);return ei(qa({level:{name:c},remaining_levels:p,message:`Level "${c}" deleted. Every later level's L# shifts down by one.`+(m?" "+m:"")}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}async function wn(e){try{const t=await qe();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const r=Array.isArray(e.names)?e.names.map(e=>Ka(String(e??"")).trim()).filter(e=>e.length>0):[];if(0===r.length)return ti(Ta("names must be a non-empty array of the project's existing levels, in the order you want them."));const{levels:a}=await _n(t,e.project_id),i=new Map;for(const e of a)i.set(un(e),e);const n=[],s=new Set,o=`names must list every existing level exactly once. Levels: ${a.length>0?a.join(", "):"(none)"}`;for(const e of r){const t=un(e),r=i.get(t);if(!r||s.has(t))return ti(Ta(o));s.add(t),n.push(r)}if(n.length!==a.length)return ti(Ta(o));const{data:c,error:l}=await t.rpc("mutate_project_levels",{p_project_id:e.project_id,p_action:"reorder",p_values:n});if(l)throw new Error(l.message);const d=c?.levels??n,p=$r(a,d);return ei(qa({levels:d.map((e,t)=>({name:e,prefix:`L${t}`})),message:"Levels reordered. L# now follows this order everywhere it renders (Strata, sidebar, exports)."+(p?" "+p:"")}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}E(mn,{createLevelHandler:()=>yn,deleteLevelHandler:()=>gn,ensureLevelRegistered:()=>fn,listLevelsHandler:()=>hn,reorderLevelsHandler:()=>wn});var vn,bn,jn=A({"src/tools/levels.ts"(){Le(),pn(),ai(),Aa(),D(),U()}});function kn(e,t){return"requirement"!==t?`req_type applies only to requirements (type='requirement'), not '${t}'.`:bn.includes(e)?`req_type '${e}' names an analysis row, which is created by atoms_create_analysis, not atoms_create_item / atoms_update_item. Use atoms_create_analysis (variant iso26262 | iso14971 | design | process | software | use).`:vn.includes(e)?null:`Unknown req_type '${e}'. Allowed: ${vn.join(", ")}.`}var Sn,xn,An=A({"src/core/req-type.ts"(){vn=["functional","interface","performance","safety"],bn=["safety-goal","failure-mode"]}}),En=A({"src/core/project-field-counts.ts"(){}});function In(e){if(!Array.isArray(e))return;const t=new Set(e.filter(e=>"string"==typeof e&&xn.has(e)));return 0!==t.size?Sn.filter(e=>t.has(e)):void 0}var Rn,On,qn=A({"src/core/field-scope.ts"(){Sn=["requirement","test-case","note","table"],xn=new Set(Sn)}}),zn={};async function $n(e,t){const{data:r,error:a}=await e.from("metadata").select("fields").eq("project_id",t).maybeSingle();if(a)throw a;return Hn(r?.fields)}function Hn(e){if(!Array.isArray(e))return[];const t=[],r=new Set;for(const a of e){if(!a||"object"!=typeof a)continue;const e=a,i="string"==typeof e.name?e.name.trim():"";if(!i)continue;const n=i.toLowerCase();if(r.has(n))continue;r.add(n);const s=void 0===e.type?"text":"string"==typeof e.type&&On.has(e.type)?e.type:"text",o=Array.isArray(e.options)?Fn(e.options):[],c=In(e.appliesTo??e.applies_to);t.push({name:i,type:s,..."select"===s?{options:o}:{},...c?{appliesTo:c}:{}})}return t}function Mn(e,t){const r=e.name.trim();if(!r)return"Field name cannot be empty.";if(r.length>60)return"Field name must be 60 characters or fewer.";if(!On.has(e.type))return"Field type is not supported.";const a=t.find(e=>e.name.toLowerCase()===r.toLowerCase());if(a)return`Field "${a.name}" already exists.`;if("select"===e.type){const t=Fn(e.options??[]);if(0===t.length)return"Select fields need at least one option.";if(t.length>100)return"Select fields support at most 100 options.";if(t.some(e=>e.length>120))return"Select options must be 120 characters or fewer."}return null}function Tn(e,t){const r=new Set(e.map(e=>e.name.toLowerCase()));return Object.keys(t).filter(e=>!r.has(e.toLowerCase()))}function Dn(e,t){const r=new Map(e.map(e=>[e.name.toLowerCase(),e.name])),a={};for(const[e,i]of Object.entries(t))a[r.get(e.toLowerCase())??e]=i;return a}function Pn(e,t,r=new Set){const a=Tn(e,t);if(a.length>0)return{error:`Unknown field${a.length>1?"s":""}: ${a.join(", ")}. Defined: ${e.length>0?e.map(e=>e.name).join(", "):"(none)"}. Define fields first with atoms_create_field.`};const i={},n=new Map(e.map(e=>[e.name.toLowerCase(),e]));for(const[e,a]of Object.entries(t)){const t=n.get(e.toLowerCase());if(!t)continue;const s=Cn(t,a,r);if(s.error)return{error:s.error};s.value&&(i[t.name]=s.value)}return{fields:i}}function Cn(e,t,r){const a=t.trim();if(!a)return{value:""};if(a.length>1e4)return{error:`${e.name} is too long.`};switch(e.type){case"text":case"long":return{value:a};case"bool":{const t=a.toLowerCase();return"yes"===t?{value:"yes"}:"no"===t?{value:"no"}:{error:`${e.name} must be yes or no.`}}case"date":return function(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e))return!1;const t=new Date(`${e}T00:00:00.000Z`);return!Number.isNaN(t.getTime())&&t.toISOString().slice(0,10)===e}(a)?{value:a}:{error:`${e.name} must be an ISO date.`};case"person":return Nn(a)&&r.has(a)?{value:a}:{error:`${e.name} must be an active project member ID.`};case"select":return(e.options??[]).includes(a)?{value:a}:{error:`${e.name} must be one of: ${(e.options??[]).join(", ")}.`};case"number":return Number.isFinite(Number(a))?{value:a}:{error:`${e.name} must be numeric.`};case"link":{const t=function(e){if(!e)return{label:"",url:""};try{const t=JSON.parse(e);if(t&&"object"==typeof t&&"string"==typeof t.url){const e=t.label;return{label:"string"==typeof e?e:"",url:t.url}}}catch{}return{label:"",url:e}}(a);return function(e){try{const t=new URL(e);return"http:"===t.protocol||"https:"===t.protocol}catch{return!1}}(t.url)?{value:Un(t.label,t.url)}:{error:`${e.name} must be an http(s) URL.`}}}}function Un(e,t){const r=t.trim();return r?JSON.stringify({label:e.trim(),url:r}):""}async function Ln(e,t,r,a){const i=new Map(r.map(e=>[e.name.toLowerCase(),e])),n=new Map;for(const[e,t]of Object.entries(a)){const r=i.get(e.toLowerCase());"person"===r?.type&&t.trim()&&n.set(t.trim(),r)}const s=new Set;for(const[r,a]of n){if(!Nn(r))return{error:`${a.name} must be an active project member ID.`};const{data:i,error:n}=await e.rpc("is_active_project_member_for_field",{p_project_id:t,p_person_user_id:r,p_actor_user_id:Me()});if(n)return{error:n.message};!0===i&&s.add(r)}return Pn(r,a,s)}function Nn(e){return/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(e)}function Fn(e){const t=[],r=new Set;for(const a of e){if("string"!=typeof a)continue;const e=a.trim();if(!e)continue;const i=e.toLowerCase();r.has(i)||(r.add(i),t.push(e))}return t}async function Wn(e){try{const t=await qe(),r=await $n(t,e.project_id),a=await async function(e,t,r,a=500){const i=Object.fromEntries(r.map(e=>[e.name,0])),n=new Map(r.map(e=>[e.name.toLowerCase(),e.name]));let s=0;for(;;){const{data:r,error:o}=await e.from("items").select("data").eq("project_id",t).is("deleted_at",null).order("id",{ascending:!0}).range(s,s+a-1);if(o)throw o;const c=r??[];for(const e of c){const t=e.data?.fields;if(t&&"object"==typeof t&&!Array.isArray(t))for(const[e,r]of Object.entries(t)){const t=n.get(e.toLowerCase());t&&"string"==typeof r&&r.length>0&&(i[t]+=1)}}if(c.length<a)return i;s+=c.length}}(t,e.project_id,r);return ei(qa({fields:r.map(e=>({name:e.name,type:e.type,options:e.options??[],...e.appliesTo?{appliesTo:e.appliesTo}:{},item_count:a[e.name]??0})),total:r.length}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ja(e))}}async function Vn(e){try{const t=await qe();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"PROJECT_FIELD_ADMIN_OR_OWNER_REQUIRED"===e.message)return ti(Ma("Project admin or owner"));throw e}const{data:r}=await t.from("metadata").select("fields").eq("project_id",e.project_id).maybeSingle(),a=Hn(r?.fields),i=In(e.appliesTo),n={name:e.name.trim(),type:e.type??"text",..."select"===e.type?{options:Fn(e.options??[])}:{},...i?{appliesTo:i}:{}},s=Mn(n,a);if(s)return ti(Ta(s));const o=function(e,t){return"organization"!==e?null:t&&0!==t.length?t.length>=Sn.length?null:'An organization-scoped field cannot be restricted to specific item types (appliesTo is a project-scoped-field feature only; the org field library has no per-type scope column). Omit appliesTo, list all item types, or use scope="project".':null}(e.scope,i);if(o)return ti(Ta(o));const{data:c,error:l}="organization"===e.scope?await t.rpc("create_org_project_field_definition",{p_project_id:e.project_id,p_field:n,p_actor_user_id:Me()}):await t.rpc("mutate_project_field_definition",{p_project_id:e.project_id,p_operation:"create",p_field:n,p_field_name:null,p_actor_user_id:Me()});if(l)throw l;return ei(qa({field:c?.field??n,..."organization"===e.scope?{created_projects:c?.created_projects??[],skipped_projects:c?.skipped_projects??[]}:{},message:`Field "${n.name}" defined. Set values with atoms_update_item fields={"${n.name}": "<value>"}.`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ja(e))}}async function Bn(e){try{const t=await qe();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"PROJECT_FIELD_ADMIN_OR_OWNER_REQUIRED"===e.message)return ti(Ma("Project admin or owner"));throw e}const r=e.name.trim(),{data:a,error:i}=await t.rpc("mutate_project_field_definition",{p_project_id:e.project_id,p_operation:"delete",p_field:null,p_field_name:r,p_actor_user_id:Me()});if(i)throw i;return ei(qa({deleted:a?.deleted??r}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ja(e))}}async function Jn(e){try{const t=await qe();try{await Pe(t,e.project_id)}catch(e){if(e instanceof Error&&"PROJECT_FIELD_ADMIN_OR_OWNER_REQUIRED"===e.message)return ti(Ma("Project admin or owner"));throw e}const r=Array.isArray(e.names)?e.names.map(e=>String(e??"").trim()).filter(e=>e.length>0):[];if(0===r.length)return ti(Ta("names must be a non-empty array of existing field names, in the order you want them"));const a=await $n(t,e.project_id),i=a.map(e=>e.name.toLowerCase()).sort(),n=r.map(e=>e.toLowerCase()).sort();if(i.length!==n.length||i.some((e,t)=>e!==n[t]))return ti(Ta(`names must list every existing field exactly once. Defined: ${a.length>0?a.map(e=>e.name).join(", "):"(none)"}`));const{data:s,error:o}=await t.rpc("mutate_project_field_definition",{p_project_id:e.project_id,p_operation:"reorder",p_field:r,p_field_name:null,p_actor_user_id:Me()});if(o)throw o;return ei(qa({fields:Hn(s?.fields).map(e=>e.name)}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ja(e))}}E(zn,{PROJECT_FIELD_TYPES:()=>Rn,canonicalizeFieldKeys:()=>Dn,createFieldHandler:()=>Vn,deleteFieldHandler:()=>Bn,findUnknownFieldNames:()=>Tn,getProjectFields:()=>$n,listFieldsHandler:()=>Wn,normalizeProjectFields:()=>Hn,reorderFieldsHandler:()=>Jn,validateAndCanonicalizeFieldValues:()=>Pn,validateProjectFieldDefinition:()=>Mn,validateProjectFieldValuesForWrite:()=>Ln});var Qn=A({"src/tools/fields.ts"(){Le(),ai(),En(),qn(),Rn=["text","long","bool","date","person","select","number","link"],On=new Set(Rn)}}),Gn={};async function Yn(e){try{const t=await qe(),r=Me();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}e.title=Ka(e.title),void 0!==e.body&&(e.body=Ka(e.body)),void 0!==e.summary&&(e.summary=Ka(e.summary)),void 0!==e.level&&(e.level=Ka(e.level)),e.domains&&(e.domains=e.domains.map(e=>Ka(e)));const a=(e.domains??[]).map(Oa).filter(e=>e.length>0),i=void 0!==e.level?Oa(e.level):void 0;if(a.length>0){const{data:r,error:i}=await t.from("metadata").select("domains, function_tags").eq("project_id",e.project_id).maybeSingle();if(i)throw new Error(`metadata read failed: ${i.message}`);const n=r?.domains??[],s=ya(a,ha(r?.function_tags),Oa);if(s.length>0)return ti(Ta(ga(s)));if(n.length>0){const e=new Set(n.map(Oa).filter(e=>e.length>0)),t=a.filter(t=>!e.has(t));if(t.length>0)return ti(Ta(`Unknown domain${t.length>1?"s":""}: ${t.join(", ")}. Registered: ${n.join(", ")}. Add new domains via Taxonomy Management in the ATOMS web app first.`))}}let n;if(e.fields&&Object.keys(e.fields).length>0){const{getProjectFields:r,validateProjectFieldValuesForWrite:a}=await Promise.resolve().then(()=>(Qn(),zn)),i=await r(t,e.project_id),s=await a(t,e.project_id,i,e.fields);if(s.error)return ti(Ta(s.error));n=s.fields}if(void 0!==e.req_type){const t=kn(e.req_type,e.type);if(t)return ti(Ta(t))}const s=await gi(t,e.project_id,e.type),o=(new Date).toISOString(),c={id:s,type:e.type,title:e.title,summary:e.summary,body:e.body,...e.req_type?{req_type:e.req_type}:{},tags:{domains:a,level:i??""},ownership:{primary:null,additional:[]},relationships:{parents:e.parent_ids??[],children:[],related:[],verifies:[],verified_by:[]},links:[],...n?{fields:n}:{},metadata:{created_at:o,created_by:r,updated_at:o,updated_by:r}},{error:l}=await t.rpc("save_item_with_revision",{p_item_id:s,p_project_id:e.project_id,p_type:e.type,p_title:e.title,p_data:c,p_user_id:r,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null});if(l)throw new Error(l.message);if(i&&await fn(t,e.project_id,i),e.parent_ids&&e.parent_ids.length>0)for(const a of e.parent_ids){const{error:i}=await t.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:s,p_to_id:a,p_type:"parent",p_action:"add",p_user_id:r,p_event_source:"mcp"});i&&process.stderr.write(`[atoms-mcp] Warning: parent link sync failed for ${a}: ${i.message}\n`)}return await t.from("change_history").insert({item_id:s,project_id:e.project_id,changed_by:r,event_type:"created",old_data:null,new_data:c,actor:Mc(),session_id:Hc()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),!1===e.echo?ei(qa({id:s,type:e.type})):ei(qa({id:s,type:e.type,title:e.title,summary:e.summary??"",body:e.body??"",...e.req_type?{req_type:e.req_type}:{},domains:a,level:i??"",project_id:e.project_id}))}catch(e){return ti(Ja(e))}}E(Gn,{createItemHandler:()=>Yn});var Kn=A({"src/tools/create-item.ts"(){Le(),wi(),Dc(),ai(),jn(),Aa(),An()}});function Zn(e,t){const r=function(e){return`SG-${e}`}(t);return{id:e,outcome:"captured_as_change",change:{id:r,sg_number:t,status:"open"},message:`Captured as change ${r}, awaiting approval. This requirement stays at its approved revision until a human approver approves the change; no MCP tool can approve, dismiss, or apply it.`}}function Xn(e){return{id:e,outcome:"under_review",message:"This requirement is under change control. Your edit was submitted; if it changed the requirement's meaning it is now a change awaiting a human approver, otherwise it wrote through live. Reload the item to confirm. No MCP tool can approve, dismiss, or apply a change."}}var es=A({"src/core/change-control.ts"(){}}),ts={};async function rs(e){try{const t=await qe(),r=Me();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const a=async(a,i,n,s)=>{const o=await pi(t,e.project_id,a);if(!o)return{updated_fields:[],error:`Item "${a}" not found`};const c="approved"===o.approval_state;void 0!==i.title&&(i.title=Ka(i.title)),void 0!==i.body&&(i.body=Ka(i.body)),void 0!==i.summary&&(i.summary=Ka(i.summary)),void 0!==i.level&&(i.level=Ka(i.level)),void 0!==i.domains&&(i.domains=i.domains.map(e=>Ka(e)));const l=void 0!==i.domains?i.domains.map(Oa).filter(e=>e.length>0):void 0,d=void 0!==i.level?Oa(i.level):void 0;if(void 0!==l&&l.length>0){const{data:r,error:a}=await t.from("metadata").select("domains, function_tags").eq("project_id",e.project_id).maybeSingle();if(a)throw new Error(`metadata read failed: ${a.message}`);const i=r?.domains??[],n=ya(l,ha(r?.function_tags),Oa);if(n.length>0)return{updated_fields:[],error:ga(n)};if(i.length>0){const e=new Set(i.map(Oa).filter(e=>e.length>0)),t=l.filter(t=>!e.has(t));if(t.length>0)return{updated_fields:[],error:`Unknown domains: ${t.join(", ")}`}}}const p={...o.data},m={...o.data},u=[],_=(e,t)=>JSON.stringify(e)!==JSON.stringify(t);if(void 0!==i.title&&_(o.data.title,i.title)&&(m.title=i.title,u.push("title")),void 0!==i.body&&_(o.data.body,i.body)&&(m.body=i.body,u.push("body")),void 0!==i.summary&&_(o.data.summary,i.summary)&&(m.summary=i.summary,u.push("summary")),void 0!==l&&_(o.data.tags?.domains??[],l)&&(m.tags={...m.tags,domains:l},u.push("domains")),void 0!==d&&_(o.data.tags?.level,d)&&(m.tags={...m.tags,level:d},u.push("level")),void 0!==i.status&&_(o.data.status,i.status)&&(m.status=i.status,u.push("status")),void 0!==i.req_type){const e=kn(i.req_type,o.type);if(e)return{updated_fields:[],error:e};_(o.data.req_type,i.req_type)&&(m.req_type=i.req_type,u.push("req_type"))}if(void 0!==i.steps&&_(o.data.steps,i.steps)&&(m.steps=i.steps,u.push("steps")),void 0!==i.params&&_(o.data.params,i.params)&&(m.params=i.params,u.push("params")),void 0!==i.fields&&Object.keys(i.fields).length>0){const{getProjectFields:r,validateProjectFieldValuesForWrite:a,canonicalizeFieldKeys:n}=await Promise.resolve().then(()=>(Qn(),zn)),s=await r(t,e.project_id),c=await a(t,e.project_id,s,i.fields);if(c.error)return{updated_fields:[],error:c.error};const l=n(s,o.data.fields??{});for(const[e,t]of Object.entries({...n(s,i.fields),...c.fields??{}}))""===t?delete l[e]:l[e]=t;_(o.data.fields??{},l)&&(m.fields=l,u.push("fields"))}if(0===u.length)return{updated_fields:[],updatedData:o.data,itemType:o.type};m.metadata={...m.metadata,updated_at:(new Date).toISOString(),updated_by:r};const f=i.title??o.title,{data:h,error:y}=await t.rpc("save_item_with_revision",{p_item_id:a,p_project_id:e.project_id,p_type:o.type,p_title:f,p_data:m,p_user_id:r,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null,p_expected_updated_at:i.expected_updated_at??o.updated_at});if(y)return{updated_fields:[],error:y};if(!h)return{updated_fields:[],error:`Update silently failed for "${a}" — RPC returned no row. Likely RLS policy denied the write; verify your role and project access.`};u.includes("level")&&d&&await fn(t,e.project_id,d);const g={item_id:a,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:p,new_data:m,actor:Mc(),session_id:Hc()};if(n&&(g.batch_id=n),s&&(g.reason=s),await t.from("change_history").insert(g).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),c&&function(e){return e.some(e=>"title"===e||"body"===e||"type"===e)}(u)){const r=await async function(e,t,r){const{data:a,error:i}=await e.from("item_suggestions").select("sg_number").eq("project_id",t).eq("item_id",r).eq("status","open").maybeSingle();return i||!a?null:"number"==typeof a.sg_number?a.sg_number:null}(t,e.project_id,a);return null!==r?{updated_fields:u,updatedData:m,itemType:o.type,capturedSg:r}:{updated_fields:u,updatedData:m,itemType:o.type,capturedUnknown:!0}}return{updated_fields:u,updatedData:m,itemType:o.type}};if(e.updates&&Array.isArray(e.updates)){const t=crypto.randomUUID(),r=!0===e.continue_on_error,i=[],n=[];for(const s of e.updates){const{updated_fields:o,capturedSg:c,capturedUnknown:l,error:d}=await a(s.item_id,s,t,e.reason);if(d){if(Qa(d))return ti(Ja(d));if(!r){const e=d instanceof Error?d.message:String("object"==typeof d&&d&&"message"in d?d.message??"":d);return ti(Ta(`Batch stopped at "${s.item_id}": ${e}. ${i.length} item(s) already updated.`))}n.push({item_id:s.item_id,error:String(d)})}else void 0!==c?i.push(Zn(s.item_id,c)):l?i.push(Xn(s.item_id)):i.push({id:s.item_id,updated_fields:o})}return ei(qa({batch_id:t,updated:i.length,results:!1===e.echo?void 0:i,errors:n}))}if(!e.item_id)return ti(Ta("Provide either item_id (single) or updates[] (array form)."));const{updated_fields:i,updatedData:n,itemType:s,capturedSg:o,capturedUnknown:c,error:l}=await a(e.item_id,{title:e.title,body:e.body,summary:e.summary,domains:e.domains,level:e.level,status:e.status,req_type:e.req_type,fields:e.fields,steps:e.steps,params:e.params,expected_updated_at:e.expected_updated_at},void 0,e.reason);if(l){const t=l instanceof Error?l.message:String("object"==typeof l&&l&&"message"in l?l.message??"":l);return t.includes("not found")?ti(Ha("Item",e.item_id)):t.includes("Unknown domain")||t.includes("Unknown field")||t.includes("Function-flagged tag")||t.includes("req_type")?ti(Ta(t)):Na(t)||Qa(l)?ti(Ja(l)):ti(Ga(t))}if(void 0!==o)return ei(qa(Zn(e.item_id,o)));if(c)return ei(qa(Xn(e.item_id)));if(!1===e.echo)return ei(qa({id:e.item_id,updated_fields:i}));const d=n??{};return ei(qa({id:e.item_id,type:s,title:d.title??"",summary:d.summary??"",body:d.body??"",domains:d.tags?.domains??[],level:d.tags?.level??"",...d.req_type?{req_type:d.req_type}:{},updated_fields:i}))}catch(e){return ti(Ja(e))}}E(ts,{updateItemHandler:()=>rs});var as,is=A({"src/tools/update-item.ts"(){Le(),wi(),Dc(),An(),ai(),jn(),es(),Aa()}}),ns={};async function ss(e){try{const t=await qe(),r=Me();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const a=await async function(e,t,r){const{data:a,error:i}=await e.from("items").select("*").eq("id",r).eq("project_id",t).maybeSingle();if(i)throw new Error(i.message);return a}(t,e.project_id,e.item_id);if(!a)return ti(Ha("Item",e.item_id));if(a.deleted_at)return ti(Ta(`Item ${e.item_id} is already deleted`));if(sn(as)){const t={tool:as,project_id:e.project_id,target:e.item_id};if(!e.confirmation_token){const r=on(t);return ei(qa({status:"confirmation_required",preview:{id:e.item_id,title:a.title,type:a.type,would_soft_delete:!0},confirmation_token:r.token,expires_at:r.expires_at,expires_in_seconds:r.expires_in_seconds,message:`Re-call atoms_delete_item with the same project_id, item_id, and confirmation_token within ${r.expires_in_seconds}s to execute.`}))}const r=cn(e.confirmation_token,t);if(!r.valid)return ti(Ta(`confirmation_token rejected: ${r.reason}`))}const{error:i}=await t.rpc("soft_delete_item_with_revision",{p_item_id:e.item_id,p_project_id:e.project_id,p_user_id:r,p_expected_updated_at:a.updated_at,p_event_source:"mcp",p_actor:Mc(),p_session_id:Hc()});if(i)throw new Error(i.message);const n=Ot(a.data);if(n){const a=await pi(t,e.project_id,n);if(a){const{revoked:i,parentProps:n}=qt(a.data);if(i){const i={...a.data,analysis_props:n},{error:s}=await t.rpc("save_item_with_revision",{p_item_id:a.id,p_project_id:e.project_id,p_type:a.type,p_title:a.title,p_data:i,p_user_id:r,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null,p_expected_updated_at:a.updated_at});s&&process.stderr.write(`[atoms-mcp] Warning: acceptance revoke save failed for '${a.id}': ${s.message}\n`)}}}return ei(qa({id:e.item_id,title:a.title,type:a.type,deleted:!0,message:`Item ${e.item_id} soft-deleted. It can still be found in audit logs.`}))}catch(e){return ti(Ja(e))}}E(ns,{deleteItemHandler:()=>ss});var os=A({"src/tools/delete-item.ts"(){Le(),wi(),Dc(),Er(),pn(),ai(),as="atoms_delete_item"}}),cs={};async function ls(e){try{const t=await qe(),r=Me();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const{data:a,error:i}=await t.from("items").select("*").eq("id",e.item_id).eq("project_id",e.project_id).maybeSingle();if(i)throw new Error(i.message);if(!a)return ti(Ha("Item",e.item_id));if(!a.deleted_at)return ti($a(`Item ${e.item_id} is not deleted and cannot be restored.`,["Use atoms_get_item to view the current item state","Use atoms_delete_item first if you intended to delete it"]));const{error:n}=await t.rpc("save_item_with_revision",{p_item_id:e.item_id,p_project_id:e.project_id,p_type:a.type,p_title:a.title,p_data:a.data,p_user_id:r,p_event_source:"restore",p_event_session_id:Hc()??null,p_diff_summary:{changed_fields:["deleted_at"],restored:!0},p_expected_updated_at:a.updated_at});if(n)throw new Error(n.message);const{error:s}=await t.rpc("repair_live_item_relationships_from_jsonb",{p_project_id:e.project_id,p_item_id:e.item_id});if(s)throw new Error(`Relationship repair failed: ${s.message}`);const o=Ot(a.data);if(o){const a=await pi(t,e.project_id,o);if(a){const{revoked:i,parentProps:n}=qt(a.data);if(i){const i={...a.data,analysis_props:n},{error:s}=await t.rpc("save_item_with_revision",{p_item_id:a.id,p_project_id:e.project_id,p_type:a.type,p_title:a.title,p_data:i,p_user_id:r,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null,p_expected_updated_at:a.updated_at});s&&process.stderr.write(`[atoms-mcp] Warning: acceptance revoke save failed for '${a.id}': ${s.message}\n`)}}}return await t.from("change_history").insert({item_id:e.item_id,project_id:e.project_id,changed_by:r,event_type:"restored",old_data:null,new_data:a.data,actor:Mc(),session_id:Hc()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),ei(qa({id:e.item_id,title:a.title,type:a.type,restored:!0,message:`Item ${e.item_id} restored successfully.`}))}catch(e){return ti(Ja(e))}}E(cs,{restoreItemHandler:()=>ls});var ds,ps,ms=A({"src/tools/restore-item.ts"(){Le(),wi(),Dc(),Er(),ai()}}),us={};function _s(e,t,r){const[a,i]="affects"===r?[e,t]:[t,e];return a.startsWith(ds)&&i.startsWith(ps)?null:`affects/affected_by links a security risk analysis row (${ds}n) to an ISO 14971 risk row (${ps}n) only: "${e}" -> "${t}" is not that pair.`}async function fs(e){try{const t=await qe(),r=Me();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const a=async(a,i,n)=>{const s=await pi(t,e.project_id,a.from_id);if(!s)throw new Error(`Item "${a.from_id}" not found`);const o=await pi(t,e.project_id,a.to_id);if(!o)throw new Error(`Item "${a.to_id}" not found`);const{error:c}=await t.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:a.from_id,p_to_id:a.to_id,p_type:a.type,p_action:a.action,p_user_id:r,p_event_source:"mcp",p_expected_from_updated_at:a.expected_from_updated_at??s.updated_at,p_expected_to_updated_at:a.expected_to_updated_at??o.updated_at});if(c)throw new Error(c.message);if("affects"===a.type||"affected_by"===a.type)for(const a of[s,o]){const i=Ot(a.data);if(!i)continue;const n=await pi(t,e.project_id,i);if(!n)continue;const{revoked:s,parentProps:o}=qt(n.data);if(!s)continue;const c={...n.data,analysis_props:o},{error:l}=await t.rpc("save_item_with_revision",{p_item_id:n.id,p_project_id:e.project_id,p_type:n.type,p_title:n.title,p_data:c,p_user_id:r,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null,p_expected_updated_at:n.updated_at});l&&process.stderr.write(`[atoms-mcp] Warning: acceptance revoke save failed for '${n.id}': ${l.message}\n`)}const l={item_id:a.from_id,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:null,new_data:{relationship_change:{action:a.action,type:a.type,from:a.from_id,to:a.to_id}},actor:Mc(),session_id:Hc()};i&&(l.batch_id=i),n&&(l.reason=n),await t.from("change_history").insert(l).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)})};if(e.operations&&Array.isArray(e.operations)){for(const t of e.operations){if(t.from_id===t.to_id)return ti(Ta(`Self-reference in batch: "${t.from_id}"`));if("affects"===t.type||"affected_by"===t.type){const e=_s(t.from_id,t.to_id,t.type);if(e)return ti(Ta(e))}}const r=[...new Set(e.operations.flatMap(e=>[e.from_id,e.to_id]))],{data:i}=await t.from("items").select("id").eq("project_id",e.project_id).is("deleted_at",null).in("id",r),n=new Set((i??[]).map(e=>e.id)),s=r.filter(e=>!n.has(e));if(s.length>0)return ti(Ha("Items",s.join(", ")));const{data:o}=await t.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id),c=new Map;for(const e of o??[])"parent"===e.type&&(c.has(e.from_id)||c.set(e.from_id,new Set),c.get(e.from_id).add(e.to_id));for(const t of e.operations){if("parent"!==t.type&&"child"!==t.type)continue;const[e,r]="parent"===t.type?[t.from_id,t.to_id]:[t.to_id,t.from_id];"remove"===t.action?c.get(e)?.delete(r):(c.has(e)||c.set(e,new Set),c.get(e).add(r))}const l=0,d=1,p=2,m=new Map,u=new Set;for(const[e,t]of c){u.add(e);for(const e of t)u.add(e)}const _=e=>{if((m.get(e)??l)!==l)return null;const t=[],r=[],a=e=>{m.set(e,d),r.push(e),t.push({node:e,parents:[...c.get(e)??[]],idx:0})};for(a(e);t.length>0;){const e=t[t.length-1];if(e.idx>=e.parents.length){m.set(e.node,p),r.pop(),t.pop();continue}const i=e.parents[e.idx];e.idx++;const n=m.get(i)??l;if(n===d){const e=r.indexOf(i);return[...r.slice(e),i]}n!==p&&a(i)}return null};for(const e of u){const t=_(e);if(t)return ti(Ta(`Batch would create a cycle: ${t.join(" -> ")}. No changes applied.`))}const f=crypto.randomUUID(),h=e.operations.filter(e=>"remove"===e.action),y=e.operations.filter(e=>"add"===e.action),g=[...h,...y],w=[],v=e=>`${e.action} ${e.from_id} --[${e.type}]--\x3e ${e.to_id}`;for(const t of g){try{await a(t,f,e.reason)}catch(e){const r=Ja(e);return ti({...r,message:`${r.message}\n\nThis batch is not atomic: its operations are applied one at a time. ${w.length} of ${g.length} operation(s) committed before this one failed and were NOT rolled back (batch ${f}). Committed: ${w.length?w.map(v).join("; "):"none"}. Failed: ${v(t)}.`,next_steps:[`Do not re-send the whole batch: the ${w.length} committed operation(s) would be applied again.`,"Reconcile from the committed list, then retry only the failed and any remaining operations.",...r.next_steps??[]]})}w.push(t)}return ei(qa({batch_id:f,processed:e.operations.length,operations:!1===e.echo?void 0:e.operations}))}if(!(e.from_id&&e.to_id&&e.type&&e.action))return ti(Ta("Provide (from_id, to_id, type, action) for single-op or operations[] for batch."));if(e.from_id===e.to_id)return ti(Ta("Cannot create a relationship between an item and itself"));if("affects"===e.type||"affected_by"===e.type){const t=_s(e.from_id,e.to_id,e.type);if(t)return ti(Ta(t))}return await a({action:e.action,from_id:e.from_id,to_id:e.to_id,type:e.type,expected_from_updated_at:e.expected_from_updated_at,expected_to_updated_at:e.expected_to_updated_at},void 0,e.reason),!1===e.echo?ei(qa({action:e.action,from_id:e.from_id,to_id:e.to_id})):ei(qa({action:e.action,type:e.type,from_id:e.from_id,to_id:e.to_id,message:`Relationship ${"add"===e.action?"added":"removed"}: ${e.from_id} --[${e.type}]--\x3e ${e.to_id}`}))}catch(e){return ti(Ja(e))}}E(us,{linkItemsHandler:()=>fs});var hs=A({"src/tools/link-items.ts"(){Le(),wi(),Dc(),Aa(),Er(),ai(),ds=`${ar.security_tir57}-`,ps=`${ar.hazard_iso14971}-`}}),ys=A({"src/core/bulk-parent-links.ts"(){}}),gs={};function ws(e){return new Set(["22023","23503","23505","23514"]).has(e.code??"")}async function vs(e){try{if(!e.items||0===e.items.length)return ti(Ta("items array must contain at least 1 item"));if(e.items.length>100)return ti(Ta(`Maximum 100 items per call (received ${e.items.length}). Split into multiple calls.`));const t=await qe(),r=Me();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const a=await async function(e,t,r){const{data:a,error:i}=await e.from("metadata").select("domains, function_tags").eq("project_id",t).maybeSingle();if(i)throw new Error(`metadata read failed: ${i.message}`);const n=a?.domains??[],s=ha(a?.function_tags),o=n.length>0?new Set(n.map(Oa).filter(e=>e.length>0)):null,c=dn(n.map(Oa).filter(e=>e.length>0).sort()),l=[],d=new Set,p={};for(let e=0;e<r.length;e++){const t=r[e];p[t.type]=(p[t.type]??0)+1;const a=(t.domains??[]).map(Oa).filter(e=>e.length>0);if(0===a.length)continue;const i=ya(a,s,Oa);if(i.length>0){d.add(e),l.push({index:e,title:t.title,error:ga(i)});continue}if(!o)continue;const c=a.filter(e=>!o.has(e));c.length>0&&(d.add(e),l.push({index:e,title:t.title,error:`Unknown domain${c.length>1?"s":""}: ${c.join(", ")}. Registered: ${n.join(", ")}.`}))}return{registeredDomains:n,registeredDomainHash:c,invalidDomainIndexes:d,errors:l,typeBreakdown:p}}(t,e.project_id,e.items);if(sn("atoms_bulk_import")){const t={tool:"atoms_bulk_import",project_id:e.project_id,target:dn({items:e.items,registered_domain_hash:a.registeredDomainHash})};if(!e.confirmation_token){const r=on(t);return ei(qa({status:"confirmation_required",preview:{total_items:e.items.length,by_type:a.typeBreakdown,sample_titles:e.items.slice(0,5).map(e=>e.title),would_fail:a.errors.length>0,errors:a.errors,registered_domain_hash:a.registeredDomainHash},confirmation_token:r.token,expires_at:r.expires_at,expires_in_seconds:r.expires_in_seconds,message:`Re-call atoms_bulk_import with the same project_id, items array, and confirmation_token within ${r.expires_in_seconds}s to execute. Changing items or project domains will invalidate the token.`}))}const r=cn(e.confirmation_token,t);if(!r.valid)return ti(Ta(`confirmation_token rejected: ${r.reason}`))}const i=(new Date).toISOString(),n=Hc(),s=[],o=[...a.errors],c=[],l=new Map,d=async r=>{let a=l.get(r);if(!a){const i=await gi(t,e.project_id,r),n=/^([A-Z]+)-(\d+)$/.exec(i);if(!n)throw new Error(`Unexpected item id format from next_item_id: ${i}`);a={prefix:n[1],next:Number(n[2])},l.set(r,a)}const i=`${a.prefix}-${String(a.next).padStart(3,"0")}`;return a.next+=1,i};for(let t=0;t<e.items.length;t++){const r=e.items[t];if(a.invalidDomainIndexes.has(t))continue;const i=(r.domains??[]).map(Oa).filter(e=>e.length>0),n=void 0!==r.level?Oa(r.level):void 0;try{const e=await d(r.type);c.push({itemId:e,input:r,sanitizedDomains:i,sanitizedLevel:n,index:t})}catch(e){o.push({index:t,title:r.title,error:`ID generation failed: ${e instanceof Error?e.message:String(e)}`})}}if(0===c.length)return ei(qa({created:0,items:[],errors:o}));const p=[],m=new Map;for(const{itemId:t,input:a,sanitizedDomains:n,sanitizedLevel:s}of c){const o={id:t,type:a.type,title:a.title,summary:a.summary,body:a.body,tags:{domains:n,level:s??""},ownership:{primary:null,additional:[]},relationships:{parents:a.parent_id?[a.parent_id]:[],children:[],related:[]},links:[],metadata:{created_at:i,created_by:r,updated_at:i,updated_by:r}};m.set(t,o),p.push({id:t,project_id:e.project_id,type:a.type,title:a.title,data:o,created_by:r,updated_by:r})}const u=p.map(e=>({id:e.id,type:e.type,title:e.title,data:e.data})),{error:_}=await t.rpc("bulk_save_items_with_revisions",{p_project_id:e.project_id,p_items:u,p_user_id:r,p_event_source:"mcp"});if(_&&ws(_))for(const{itemId:a,input:i,index:l}of c){const c=p.find(e=>e.id===a);if(!c)continue;const{error:d}=await t.rpc("save_item_with_revision",{p_item_id:c.id,p_project_id:e.project_id,p_type:c.type,p_title:c.title,p_data:c.data,p_user_id:r,p_event_source:"mcp",p_event_session_id:n,p_diff_summary:null});d?o.push({index:l,title:i.title,error:d.message}):s.push({id:a,title:i.title,type:i.type})}else{if(_)throw _;for(const{itemId:e,input:t}of c)s.push({id:e,title:t.title,type:t.type})}const f=c.filter(({input:e})=>e.parent_id).filter(({itemId:e})=>s.some(t=>t.id===e)).map(({itemId:t,input:r})=>({from_id:t,to_id:r.parent_id,type:"parent",project_id:e.project_id}));await async function(e){for(const t of e.links){const{error:r}=await e.client.rpc("apply_link_change",{p_project_id:e.projectId,p_from_id:t.from,p_to_id:t.to,p_type:"parent",p_action:"add",p_user_id:e.userId,p_event_source:"mcp"});r&&e.warn(`bulk parent link sync failed for ${t.to}: ${r.message??"unknown error"}`)}}({client:t,projectId:e.project_id,links:f.map(e=>({from:e.from_id,to:e.to_id})),userId:r,warn:e=>process.stderr.write(`[atoms-mcp] Warning: ${e}\n`)});const h=s.map(t=>({item_id:t.id,project_id:e.project_id,changed_by:r,event_type:"created",old_data:null,new_data:m.get(t.id)??null,actor:Mc(),session_id:n}));h.length>0&&await t.from("change_history").insert(h).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: bulk change_history log failed: ${e.message}\n`)});const y=new Set(s.map(e=>e.id)),g=new Set(c.filter(({itemId:e,sanitizedLevel:t})=>t&&y.has(e)).map(({sanitizedLevel:e})=>e));for(const r of g)await fn(t,e.project_id,r);return ei(qa({created:s.length,items:s,errors:o.length>0?o:[],project_id:e.project_id}))}catch(e){return ti(Ja(e))}}E(gs,{bulkImportHandler:()=>vs,canRetryBulkImportPerItem:()=>ws});var bs=A({"src/tools/bulk-import.ts"(){Le(),wi(),Dc(),pn(),ai(),ys(),jn(),Aa()}}),js={};async function ks(e){try{const t=await qe(),r=Me(),a=Te();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const i=await pi(t,e.project_id,e.item_id);if(!i)return ti(Ha("Item",e.item_id));if("test-case"!==i.type)return ti(Ta(`Item '${e.item_id}' is a ${i.type}, not a test-case`));const n=(e.asset??"").trim(),s=(new Date).toISOString(),{data:o,error:c}=await t.rpc("record_test_results_bulk",{p_project_id:e.project_id,p_item_id:e.item_id,p_run_by:a||r,p_actor_user_id:r,p_results:[{result:e.result,note:e.note,asset:n||void 0,date:s,point:e.point,release_id:e.release_id,build:e.build,environment:e.environment,tooling:e.tooling,steps_rev:e.steps_rev,step_results:e.step_results}]});if(c)throw new Error(c.message);const l=Array.isArray(o)?o[0]:o?.[0];await t.from("change_history").insert({item_id:e.item_id,project_id:e.project_id,changed_by:r,event_type:"test_result_recorded",old_data:null,new_data:{result:e.result,run_at:s,note:e.note,asset:n||null},actor:Mc(),session_id:Hc()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)});const d=n?` (asset: ${n})`:"";return ei(qa({item_id:e.item_id,result:e.result,run_at:s,run_by:a||r,note:e.note??null,asset:n||null,asset_id:l?.asset_id??null,asset_created:l?.asset_created??!1,message:`Test result '${e.result}' recorded for ${e.item_id}${d}`}))}catch(e){return ti(Ja(e))}}E(js,{recordTestResultHandler:()=>ks});var Ss=A({"src/tools/record-test-result.ts"(){Le(),wi(),Dc(),ai()}}),xs={};async function As(e){try{const t=await qe(),r=Me(),a=Te();if(!Array.isArray(e.results)||0===e.results.length)return ti(Ta("results must be a non-empty array"));if(e.results.length>100)return ti(Ta(`results must have at most 100 entries (got ${e.results.length})`));try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const i=await pi(t,e.project_id,e.item_id);if(!i)return ti(Ha("Item",e.item_id));if("test-case"!==i.type)return ti(Ta(`Item '${e.item_id}' is a ${i.type}, not a test-case`));const{data:n,error:s}=await t.rpc("record_test_results_bulk",{p_project_id:e.project_id,p_item_id:e.item_id,p_run_by:a||r,p_actor_user_id:r,p_results:e.results});if(s)throw new Error(s.message);return t.from("change_history").insert({item_id:e.item_id,project_id:e.project_id,changed_by:r,event_type:"test_result_recorded",old_data:null,new_data:{bulk:!0,count:e.results.length,results:n},actor:Mc(),session_id:Hc()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),ei(qa({item_id:e.item_id,count:e.results.length,results:n,message:`Recorded ${e.results.length} test results for ${e.item_id}`}))}catch(e){return ti(Ja(e))}}E(xs,{recordTestResultsBulkHandler:()=>As});var Es,Is,Rs,Os,qs=A({"src/tools/record-test-results-bulk.ts"(){Le(),wi(),Dc(),ai()}});function zs(e){return Math.min(Math.max(e??5,1),10)}var $s=A({"src/core/trace-graph.ts"(){Es=200,Is=new Set(["parent","verifies","mitigated_by"]),Rs=new Set(["child","verified_by","mitigates"]),Os={parent:"child",child:"parent",verifies:"verified_by",verified_by:"verifies",mitigated_by:"mitigates",mitigates:"mitigated_by",related:"related"}}}),Hs={};async function Ms(e){try{const t=zs(e.depth),r=await qe(),a=await pi(r,e.project_id,e.item_id);if(!a)return ti(Ha("Item",e.item_id));const{data:i,error:n}=await r.from("items").select("id, title, type, data").eq("project_id",e.project_id).is("deleted_at",null);if(n)throw new Error(n.message);const{data:s,error:o}=await r.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id);if(o)throw new Error(o.message);const c=yi(i??[],s??[]);return ei(qa(function(e){const t=zs(e.depth),r=e.relationshipTypes?.length?new Set(e.relationshipTypes):null,a=new Map(e.items.map(e=>[e.id,e]));a.has(e.rootItemId)||a.set(e.rootItemId,e.rootItem);const i=new Map,n=(e,t,r)=>{i.has(e)||i.set(e,[]),i.get(e).push({neighbor:t,relType:r})};for(const t of e.relationships){const a=t.type;if(("upstream"===e.direction||"both"===e.direction)&&(!Is.has(a)||r&&!r.has(a)||n(t.from_id,t.to_id,a),Rs.has(a))){const e=Os[a]??a;r&&!r.has(e)||n(t.to_id,t.from_id,e)}if(("downstream"===e.direction||"both"===e.direction)&&(!Rs.has(a)||r&&!r.has(a)||n(t.from_id,t.to_id,a),Is.has(a))){const e=Os[a]??a;r&&!r.has(e)||n(t.to_id,t.from_id,e)}"related"!==a||r&&!r.has("related")||(n(t.from_id,t.to_id,"related"),n(t.to_id,t.from_id,"related"))}const s=new Set([e.rootItemId]),o=[],c=[],l=[];for(const t of i.get(e.rootItemId)??[])l.push({id:t.neighbor,depth:1,relType:t.relType,from:e.rootItemId});for(;l.length>0&&o.length<Es;){const{id:e,depth:r,relType:n,from:d}=l.shift();if(s.has(e)||r>t)continue;s.add(e);const p=a.get(e);if(p&&(o.push({id:e,title:p.title,type:p.type,relationship:n,depth:r,from:d}),c.push({from:d,to:e,type:n}),r<t))for(const t of i.get(e)??[])s.has(t.neighbor)||l.push({id:t.neighbor,depth:r+1,relType:t.relType,from:e})}const d={id:e.rootItemId,title:e.rootItem.title,type:e.rootItem.type,relationship:"root",depth:0,from:e.rootItemId};return{root:e.rootItemId,project_id:e.projectId,direction:e.direction,depth_limit:t,items:[d,...o],edges:c,total_count:o.length+1,...o.length>=Es?{truncated:!0,truncation_message:`Results capped at ${Es} items. Use a smaller depth or filter by relationship_types.`}:{}}}({projectId:e.project_id,rootItemId:e.item_id,rootItem:{id:e.item_id,title:a.title??e.item_id,type:a.type??"unknown"},items:i??[],relationships:c??[],direction:e.direction,depth:t,relationshipTypes:e.relationship_types})))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}E(Hs,{traceHandler:()=>Ms});var Ts=A({"src/tools/trace.ts"(){Le(),wi(),$s(),ai()}}),Ds={};async function Ps(e){try{let t=(await qe()).from("assets").select("id, name, archived_at, created_at").eq("project_id",e.project_id).order("name");e.include_archived||(t=t.is("archived_at",null));const{data:r,error:a}=await t;if(a)throw new Error(a.message);return ei(qa({assets:r||[],total:(r||[]).length}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}E(Ds,{listAssetsHandler:()=>Ps});var Cs=A({"src/tools/assets.ts"(){Le(),ai()}}),Us={};async function Ls(e,t,r,a){if(r&&a)throw new Error("baseline_id and as_of are mutually exclusive");if(a)return a;if(!r)return null;const{data:i,error:n}=await e.from("baselines").select("project_id, snapshot_at, deleted_at").eq("id",r).maybeSingle();if(n)throw new Error(n.message);if(!i||i.project_id!==t||i.deleted_at)throw new Error("Baseline not found for this project");return i.snapshot_at}async function Ns(e,t,r){if(r){const{data:a,error:i}=await e.rpc("variables_at",{p_project_id:t,p_snapshot_at:r});if(i)throw new Error(i.message);return a??[]}const{data:a,error:i}=await e.from("project_variables").select("*").eq("project_id",t).order("name");if(i)throw new Error(i.message);return a??[]}async function Fs(e,t,r,a){const{data:i,error:n}=await e.rpc("save_variable_with_revision",{p_project_id:t.project_id,p_name:t.name,p_value:t.value,p_unit:t.unit??null,p_description:t.description??null,p_is_signal:t.is_signal??!1,p_range_min:t.range_min??null,p_range_max:t.range_max??null,p_rate_ms:t.rate_ms??null,p_user_id:r,p_event_source:a,p_variable_id:t.id??null});if(n)throw new Error(n.message);return i}async function Ws(e){try{const t=await qe(),r=await Ls(t,e.project_id,e.baseline_id,e.as_of),a=await Ns(t,e.project_id,r),i=e.limit??50,n=e.offset??0;return ei(qa({variables:a.slice(n,n+i),total:a.length},za(a.length,i,n)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}async function Vs(e){try{const t=await qe(),r=await Ls(t,e.project_id,e.baseline_id,e.as_of),a=(await Ns(t,e.project_id,r)).find(t=>t.name===e.variable_name)??null;if(!a)return ti(Ha("Variable",e.variable_name));const{data:i}=await t.from("items").select("id, title, type, data").eq("project_id",e.project_id).is("deleted_at",null),n=`{${e.variable_name}}`,s=(i||[]).filter(e=>(e.data?.body||"").includes(n));return ei(qa({variable:a,referenced_by:s.map(e=>({id:e.id,title:e.title,type:e.type})),reference_count:s.length}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}async function Bs(e){try{const t=await qe(),r=Me();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const a=await t.from("project_variables").select("*").eq("project_id",e.project_id).eq("name",e.variable_name).maybeSingle();if(!a.data)return ti(Ha("Variable",e.variable_name));const i={updated_by:r,updated_at:(new Date).toISOString()};return void 0!==e.value&&(i.value=e.value),void 0!==e.unit&&(i.unit=e.unit||null),void 0!==e.description&&(i.description=e.description||null),ei(qa({variable:await Fs(t,{...a.data,...i,project_id:e.project_id,name:e.variable_name,value:i.value??a.data.value,unit:i.unit??a.data.unit,description:i.description??a.data.description},r,"mcp")}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}async function Js(e){try{const t=await qe(),r=Me();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const a=/^[a-zA-Z_][a-zA-Z0-9_]*$/;if(e.variables&&Array.isArray(e.variables)){const i=e.variables.filter(e=>!a.test(e.name));if(i.length>0)return ti(Ta(`Invalid variable name(s): ${i.map(e=>e.name).join(", ")}. Use letters, digits, underscores; must start with letter or underscore.`));const n=e.variables.map(e=>e.name),s=n.map(e=>e.toLowerCase()),o=n.filter((e,t)=>s.indexOf(s[t])!==t);if(o.length>0)return ti(Ta(`Duplicate names in batch (case-insensitive): ${[...new Set(o)].join(", ")}`));const{data:c}=await t.from("project_variables").select("name").eq("project_id",e.project_id),l=new Set((c??[]).map(e=>e.name.toLowerCase())),d=n.filter(e=>l.has(e.toLowerCase()));if(d.length>0)return ti(Ta(`Variable(s) already exist (case-insensitive): ${d.join(", ")}. Remove them from the batch or use atoms_update_variable to change values.`));const p=e.variables.map(t=>({project_id:e.project_id,name:t.name,value:t.value,unit:t.unit??null,description:t.description??null,created_by:r,updated_by:r})),m=await async function(e,t,r,a){const{data:i,error:n}=await e.rpc("bulk_save_variables_with_revisions",{p_project_id:t,p_variables:r,p_user_id:a,p_event_source:"mcp"});if(n)throw new Error(n.message);return i??[]}(t,e.project_id,p,r);return ei(qa({batch_id:crypto.randomUUID(),created:(m??[]).length,variables:!1===e.echo?(m??[]).map(e=>({name:e.name})):m??[],message:`${(m??[]).length} variable(s) created. Reference them as {name} in requirement bodies.`}))}if(!e.name||!e.value)return ti(Ta("Provide either (name, value) for single variable or variables[] for batch."));if(!a.test(e.name))return ti(Ta(`Variable name "${e.name}" is invalid. Use letters, digits, and underscores only; must start with a letter or underscore.`));const{data:i}=await t.from("project_variables").select("name").eq("project_id",e.project_id),n=(i??[]).find(t=>t.name.toLowerCase()===e.name.toLowerCase());if(n)return ti(Ta(`Variable "${n.name}" already exists in this project (names are unique regardless of case).`));const s=await Fs(t,{project_id:e.project_id,name:e.name,value:e.value,unit:e.unit??null,description:e.description??null},r,"mcp");return!1===e.echo?ei(qa({name:e.name})):ei(qa({variable:s,message:`Variable "${e.name}" created. Reference it in requirement bodies as {${e.name}}.`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}async function Qs(e){try{const t=await qe(),r=Me();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const{data:a}=await t.from("project_variables").select("id").eq("project_id",e.project_id).eq("name",e.name).maybeSingle();if(!a)return ti(Ha("Variable",e.name));const{data:i}=await t.from("items").select("id").eq("project_id",e.project_id).is("deleted_at",null).filter("data->>body","ilike",`%{${e.name}}%`),n=(i??[]).length;if(n>0&&!e.force)return ti(Ta(`Variable "${e.name}" is referenced in ${n} item${n>1?"s":""}. Use force=true to delete anyway, or remove all {${e.name}} references first.`));if(sn("atoms_delete_variable")){const t={tool:"atoms_delete_variable",project_id:e.project_id,target:e.name};if(!e.confirmation_token){const r=on(t);return ei(qa({status:"confirmation_required",preview:{variable_name:e.name,referenced_by_items:n,would_orphan_references:n>0},confirmation_token:r.token,expires_at:r.expires_at,expires_in_seconds:r.expires_in_seconds,message:`Re-call atoms_delete_variable with the same project_id, name, and confirmation_token within ${r.expires_in_seconds}s to execute.`}))}const r=cn(e.confirmation_token,t);if(!r.valid)return ti(Ta(`confirmation_token rejected: ${r.reason}`))}const s=await async function(e,t,r,a){const{data:i,error:n}=await e.rpc("delete_variable_with_revision",{p_project_id:t,p_variable_id:r,p_user_id:a,p_event_source:"mcp"});if(n)throw new Error(n.message);return Boolean(i)}(t,e.project_id,a.id,r);return s?ei(qa({deleted:!0,name:e.name,referenced_by_items:n,warning:n>0?`${n} item${n>1?"s":""} still contain {${e.name}} — those references are now unresolved.`:void 0})):ti(Ha("Variable",e.name))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}E(Us,{createVariableHandler:()=>Js,deleteVariableHandler:()=>Qs,getVariableHandler:()=>Vs,listVariablesHandler:()=>Ws,updateVariableHandler:()=>Bs});var Gs=A({"src/tools/variables.ts"(){Le(),pn(),ai()}}),Ys={};async function Ks(e,t){const{data:r}=await e.from("metadata").select("domains, levels, function_tags").eq("project_id",t).maybeSingle();return{domains:r?.domains??[],levels:r?.levels??[],functionTags:Array.isArray(r?.function_tags)?r.function_tags.filter(e=>"string"==typeof e):[]}}async function Zs(e){try{const t=await qe(),{domains:r,functionTags:a}=await Ks(t,e.project_id),i=ya(r,a),n=new Set(i.map(Jr)),s=new Set(a.map(Jr)),o=r.filter(e=>!n.has(Jr(e))),{counts:c,orphans:l}=await async function(e,t,r){const{data:a}=await e.from("items").select("data").eq("project_id",t).is("deleted_at",null),i={};for(const e of r)i[e]=0;const n={};for(const e of a??[]){const t=e.data?.tags?.domains??[];for(const e of t)n[e]=(n[e]??0)+1,e in i&&i[e]++}const s=new Set(r),o={};for(const[e,t]of Object.entries(n))s.has(e)||(o[e]=t);return{counts:i,orphans:o}}(t,e.project_id,o),d=o.map(e=>({name:e,registered:!0,item_count:c[e]??0})),p=Object.entries(l).filter(([e])=>!s.has(Jr(e))).map(([e,t])=>({name:e,registered:!1,item_count:t})).sort((e,t)=>e.name.localeCompare(t.name)),m=[...d,...p];return ei(qa({domains:m,total:m.length,registered_count:d.length,unregistered_count:p.length,excluded_function_tags:i,note:p.length>0?`${p.length} unregistered tag(s) found on items. Use atoms_create_domain to register them, or atoms_update_item to remove them.`:void 0}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}async function Xs(e){try{const t=await qe();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const r=Ka(e.name).trim().toLowerCase();if(!r)return ti(Ta("Domain name cannot be empty."));const{data:a,error:i}=await t.rpc("append_project_domain",{p_project_id:e.project_id,p_domain:r});if(i)throw new Error(i.message);return a&&!1===a.added?ti(Ta(`Domain "${r}" already exists in this project.`)):ei(qa({domain:{name:r},message:`Domain "${r}" registered. You can now tag items with domains=["${r}"].`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}async function eo(e){try{const t=await qe(),r=Me();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const{domains:a}=await Ks(t,e.project_id),i=Ka(e.name).trim().toLowerCase(),n=Ka(e.new_name).trim().toLowerCase();if(!n)return ti(Ta("new_name cannot be empty."));if(!a.includes(i)){const{data:r}=await t.from("items").select("id").eq("project_id",e.project_id).is("deleted_at",null).filter("data->tags->domains","cs",JSON.stringify([i])).limit(1);if(!r||0===r.length)return ti(Ha("Domain",i))}if(n!==i&&a.includes(n))return ti(Ta(`Domain "${n}" already exists. Choose a different name.`));const{data:s,error:o}=await t.from("items").select("id, data").eq("project_id",e.project_id).is("deleted_at",null).filter("data->tags->domains","cs",JSON.stringify([i]));if(o)throw new Error(o.message);const c=(s??[]).length;if(e.dry_run)return ei(qa({dry_run:!0,rename:{from:i,to:n},items_affected:c,message:`Dry run: renaming "${i}" → "${n}" would update ${c} item${1!==c?"s":""}. Call again with dry_run=false to apply.`}));const{error:l}=await t.rpc("mutate_project_domains",{p_project_id:e.project_id,p_action:"rename",p_values:[i,n]});if(l)throw new Error(l.message);const{error:d}=await t.rpc("apply_domain_change",{p_project_id:e.project_id,p_old_domain:i,p_new_domain:n,p_user_id:r,p_event_source:"mcp"});if(d)throw new Error(d.message);return ei(qa({domain:{name:n},renamed_from:i,items_updated:c,message:`Domain renamed "${i}" → "${n}". ${c} item${1!==c?"s":""} updated.`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}async function to(e){try{const t=await qe(),r=Me();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const{domains:a}=await Ks(t,e.project_id),i=Ka(e.name).trim().toLowerCase(),n=a.includes(i),{data:s,error:o}=await t.from("items").select("id, data").eq("project_id",e.project_id).is("deleted_at",null).filter("data->tags->domains","cs",JSON.stringify([i]));if(o)throw new Error(o.message);const c=(s??[]).length;if(!n&&0===c)return ti(Ha("Domain",i));if(c>0&&!e.force)return ti(Ta(`Domain "${i}" is used by ${c} item${c>1?"s":""}. Use force=true to delete and strip the domain from all items, or re-tag them first.`));if(sn("atoms_delete_domain")){const t={tool:"atoms_delete_domain",project_id:e.project_id,target:i};if(!e.confirmation_token){const e=on(t);return ei(qa({status:"confirmation_required",preview:{domain_name:i,items_using_count:c,would_strip_from_items:c>0},confirmation_token:e.token,expires_at:e.expires_at,expires_in_seconds:e.expires_in_seconds,message:`Re-call atoms_delete_domain with the same project_id, name, and confirmation_token within ${e.expires_in_seconds}s to execute.`}))}const r=cn(e.confirmation_token,t);if(!r.valid)return ti(Ta(`confirmation_token rejected: ${r.reason}`))}const{error:l}=await t.rpc("mutate_project_domains",{p_project_id:e.project_id,p_action:"remove",p_values:[i]});if(l)throw new Error(l.message);if(c>0){const{error:a}=await t.rpc("apply_domain_change",{p_project_id:e.project_id,p_old_domain:i,p_new_domain:null,p_user_id:r,p_event_source:"mcp"});if(a)throw new Error(a.message)}return ei(qa({deleted:!0,name:i,items_updated:c,message:c>0?`Domain "${i}" deleted and stripped from ${c} item${c>1?"s":""}.`:`Domain "${i}" deleted.`}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}E(Ys,{createDomainHandler:()=>Xs,deleteDomainHandler:()=>to,listDomainsHandler:()=>Zs,updateDomainHandler:()=>eo});var ro=A({"src/tools/domains.ts"(){Le(),pn(),ai(),Aa()}}),ao={};async function io(e){try{const t=await qe(),{data:r,error:a}=await t.from("projects").select("id, name").eq("id",e.project_id).is("deleted_at",null).maybeSingle(),i=r?.name??null,n=a?.message??null,{data:s,error:o}=await t.from("items").select("id, type, data").eq("project_id",e.project_id).is("deleted_at",null);if(o)throw new Error(o.message);const c=s??[];if(!i&&0===c.length){if(n)throw new Error(n);return ti(Ha("Project",e.project_id))}const l={requirements:0,test_cases:0,notes:0},d=[],p=[];for(const e of c)if("requirement"===e.type){l.requirements++;const t=e.data?.tags?.domains??[];p.push({id:e.id,domains:t})}else"test-case"===e.type?(l.test_cases++,d.push(e.id)):"note"===e.type&&l.notes++;const m={passed:0,failed:0,blocked:0,not_run:0};if(d.length>0){const{data:r,error:a}=await t.from("test_results").select("item_id, result, run_at").eq("project_id",e.project_id).order("run_at",{ascending:!1});if(a)throw new Error(a.message);const i=new Map;for(const e of r??[])i.has(e.item_id)||i.set(e.item_id,e.result);for(const e of d){const t=i.get(e);t&&"not-run"!==t?"passed"===t?m.passed++:"failed"===t?m.failed++:"blocked"===t?m.blocked++:m.not_run++:m.not_run++}}const{data:u,error:_}=await t.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id);if(_)throw new Error(_.message);const f=yi(s??[],u??[]),h=new Set;for(const e of f)"verifies"===e.type?h.add(e.to_id):"verified_by"===e.type&&h.add(e.from_id);const y=p.filter(e=>h.has(e.id)).length,g=p.length,w={covered:y,uncovered:g-y,total:g,percent:g>0?Math.round(y/g*1e3)/10:100},v=new Map;for(const e of p){const t=e.domains.length>0?e.domains:["(untagged)"],r=h.has(e.id);for(const e of t){v.has(e)||v.set(e,{covered:0,total:0});const t=v.get(e);t.total++,r&&t.covered++}}const b=Array.from(v.entries()).sort((e,t)=>e[0].localeCompare(t[0])).map(([e,t])=>({domain:e,covered:t.covered,total:t.total,percent:t.total>0?Math.round(t.covered/t.total*1e3)/10:100})),j=new Date;j.setDate(j.getDate()-7);const{count:k,error:S}=await t.from("change_history").select("id",{count:"exact",head:!0}).eq("project_id",e.project_id).gte("changed_at",j.toISOString());if(S)throw new Error(S.message);return ei(qa({project_id:e.project_id,project_name:i??`Project ${e.project_id.slice(0,8)}`,counts:l,test_status:m,coverage:w,coverage_by_domain:b,recent_changes:k??0,last_updated:(new Date).toISOString()}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}E(ao,{projectSummaryHandler:()=>io});var no,so=A({"src/tools/project-summary.ts"(){Le(),wi(),ai()}}),oo={};async function co(e){try{const t=await qe();let r,a;if(e.query&&e.query.trim().length>0){const i=await hi(t,e.project_id,e.query,{type:e.type,domain:e.domain,level:e.level,limit:e.limit});r=i.items.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level,req_type:e.data?.req_type})),a=i.totalCount}else{const i=await di(t,e.project_id,{type:e.type,domain:e.domain,level:e.level,limit:e.limit,offset:e.offset});r=i.items.map(e=>({id:e.id,title:e.title,type:e.type,status:e.data?.status,domains:e.data?.tags?.domains??[],level:e.data?.tags?.level,req_type:e.data?.req_type})),a=i.totalCount}const i=await async function(e,t){const{data:r,error:a}=await e.from("items").select("data").eq("project_id",t).is("deleted_at",null);if(a)throw new Error(a.message);const i=new Set,n=new Set;for(const e of r??[]){const t=e.data?.tags;if(t){const e=t.domains;if(e)for(const t of e)i.add(t);const r=t.level;r&&n.add(r)}}return{domains:[...i].sort(),levels:[...n].sort()}}(t,e.project_id);return ei(qa({items:r,filters:{domains:i.domains,levels:i.levels,types:no},project_id:e.project_id},za(a,e.limit,e.offset)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}E(oo,{browseHandler:()=>co});var lo,po,mo,uo,_o=A({"src/tools/browse.ts"(){Le(),wi(),ai(),no=["requirement","test-case","note","table"]}}),fo={};async function ho(e){try{const t=e.direction??"downstream",r=null!=e.depth?Math.min(Math.max(e.depth,1),20):null,a=await qe(),i=await pi(a,e.project_id,e.item_id);if(!i)return ti(Ha("Item",e.item_id));const{data:n,error:s}=await a.from("item_relationships").select("from_id, to_id, type").eq("project_id",e.project_id);if(s)throw new Error(s.message);const{data:o,error:c}=await a.from("items").select("id, title, type").eq("project_id",e.project_id).is("deleted_at",null);if(c)throw new Error(c.message);const l=new Map;for(const e of o??[])l.set(e.id,{title:e.title,type:e.type});const d=new Map,p=(e,t,r)=>{d.has(e)||d.set(e,[]),d.get(e).push({neighbor:t,relType:r})};for(const e of n??[]){const r=e.type;"upstream"!==t&&"both"!==t||(po.includes(r)&&p(e.from_id,e.to_id,r),mo.includes(r)&&p(e.to_id,e.from_id,uo[r]??r)),"downstream"!==t&&"both"!==t||(mo.includes(r)&&p(e.from_id,e.to_id,r),po.includes(r)&&p(e.to_id,e.from_id,uo[r]??r)),"related"===r&&(p(e.from_id,e.to_id,"related"),p(e.to_id,e.from_id,"related"))}const m=new Set;m.add(e.item_id);const u=[];let _=0;const f=[];for(const t of d.get(e.item_id)??[])f.push({id:t.neighbor,depth:1,path:[{from_id:e.item_id,to_id:t.neighbor,relationship_type:t.relType}]});for(;f.length>0&&u.length<lo;){const{id:e,depth:t,path:a}=f.shift();if(m.has(e))continue;if(null!==r&&t>r)continue;m.add(e);const i=l.get(e);if(i&&(u.push({item_id:e,title:i.title,type:i.type,depth:t,relationship_path:a}),t>_&&(_=t),null===r||t<r))for(const r of d.get(e)??[])m.has(r.neighbor)||f.push({id:r.neighbor,depth:t+1,path:[...a,{from_id:e,to_id:r.neighbor,relationship_type:r.relType}]})}let h=0;if(e.include_variable_refs){const{data:t}=await a.from("variable_references").select("variable_id").eq("item_id",e.item_id).eq("project_id",e.project_id),r=[...new Set((t??[]).map(e=>e.variable_id))];if(r.length>0){const{data:t}=await a.from("project_variables").select("id, name").in("id",r),i=new Map((t??[]).map(e=>[e.id,e.name])),{data:n}=await a.from("variable_references").select("variable_id, item_id").in("variable_id",r).eq("project_id",e.project_id).neq("item_id",e.item_id);for(const t of n??[]){const r=t.item_id;if(m.has(r))continue;if(u.length>=lo)break;m.add(r);const a=l.get(r);if(!a)continue;const n=i.get(t.variable_id)??t.variable_id;u.push({item_id:r,title:a.title,type:a.type,depth:1,relationship_path:[{from_id:e.item_id,to_id:r,relationship_type:`variable_ref:${n}`}]}),h++}}}const y={};for(const e of u)y[e.type]=(y[e.type]??0)+1;const g=l.get(e.item_id);return ei(qa({root:{item_id:e.item_id,title:g?.title??i.title,type:g?.type??i.type},impacted_items:u,summary:{total_impacted:u.length,by_type:y,max_depth_reached:_,...h>0?{variable_impact_count:h}:{},...u.length>=lo?{truncated:!0}:{}}}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}E(fo,{impactAnalysisHandler:()=>ho});var yo,go=A({"src/tools/impact-analysis.ts"(){Le(),wi(),ai(),lo=200,po=["parent","verifies","traces_to","allocated_from","decomposed_from"],mo=["child","verified_by","allocated_to","decomposes_to","traced_by"],uo={parent:"child",child:"parent",verifies:"verified_by",verified_by:"verifies",related:"related",traces_to:"traced_by",traced_by:"traces_to",allocated_to:"allocated_from",allocated_from:"allocated_to",decomposes_to:"decomposed_from",decomposed_from:"decomposes_to"}}}),wo=A({"src/core/relationship-sync.ts"(){}});function vo(e){return null==e||"string"==typeof e||"number"==typeof e||"boolean"==typeof e}function bo(e){return null==e?"":String(e)}function jo(e){return null===e||"object"!=typeof e||Array.isArray(e)?{}:e}function ko(e,t){const r=jo(e),a=jo(t),i=[],n=jo(r.analysis_props),s=jo(a.analysis_props),o=Array.from(new Set([...Object.keys(n),...Object.keys(s)])).sort();for(const e of o){if(yo.has(e))continue;const t=n[e],r=s[e];vo(t)&&vo(r)&&bo(t)!==bo(r)&&i.push(`analysis_props.${e}`)}const c=e=>{const t=new Map;for(const[r,a]of Object.entries(e))"string"==typeof a&&t.set(r.toLowerCase(),{key:r,value:a});return t},l=c(jo(r.fields)),d=c(jo(a.fields)),p=Array.from(new Set([...l.keys(),...d.keys()])).sort();for(const e of p)if((l.get(e)?.value??"")!==(d.get(e)?.value??"")){const t=d.get(e)?.key??l.get(e)?.key??e;i.push(`fields.${t}`)}return i}var So=A({"src/core/baseline-diff.ts"(){yo=new Set(["field_source","approved","approved_at","approved_by","approved_note"])}}),xo={};function Ao(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):null}async function Eo(e,t){const{data:r,error:a}=await e.rpc("restore_baseline_state",{p_baseline_id:t.baselineId,p_project_id:t.projectId,p_items:[],p_variables:[],p_user_id:t.userId});if(a)throw new Error(a.message);return r}async function Io(e,t,r){const{data:a,error:i}=await e.from("items").select("id, deleted_at").eq("project_id",t).eq("id",r).maybeSingle();if(i)throw new Error(i.message);if(n=a,!Boolean(n?.id&&null===n.deleted_at))return;var n;const{error:s}=await e.rpc("sync_item_relationships_from_jsonb",{p_project_id:t,p_item_id:r});if(s)throw new Error(s.message)}async function Ro(e,t,r){const{data:a,error:i}=await e.from("projects").select("org_id").eq("id",t).maybeSingle();if(i)return ti(Ga(i.message));if(!a)return ti(Ha("Project",t));const{data:n,error:s}=await e.from("organizations").select("feature_flags").eq("id",a.org_id).maybeSingle();return s?ti(Ga(s.message)):!0!==(n?.feature_flags??{})[r]?ti($a(`Feature '${r}' is not enabled for this organization`,["Ask an org admin to enable the flag in Org Settings."])):null}async function Oo(e){try{const t=await qe(),r=await async function(e,t){const{data:r,error:a}=await e.from("baselines").select("id, project_id, name, description, field_definitions_snapshot, snapshot_at, created_at, created_by, locked_at, locked_by, release_version, release_status").eq("project_id",t).is("deleted_at",null).order("snapshot_at",{ascending:!1});if(a)throw new Error(`listBaselines: ${a.message}`);return r??[]}(t,e.project_id),a=Math.max(e.offset??0,0),i=Math.min(Math.max(e.limit??50,1),100);return ei(qa({baselines:r.slice(a,a+i).map(e=>({id:e.id,name:e.name,description:e.description,field_definitions_snapshot:e.field_definitions_snapshot,snapshot_at:e.snapshot_at,created_at:e.created_at,created_by:e.created_by,locked:null!==e.locked_at,locked_at:e.locked_at,release_version:e.release_version,release_status:e.release_status})),total:r.length,offset:a,limit:i},za(r.length,i,a)))}catch(e){return Ao(e)??ti(Ga(e instanceof Error?e.message:String(e)))}}async function qo(e){try{const t=await qe(),r=await Ro(t,e.project_id,"baselines_enabled");if(r)return r;const{data:a,error:i}=await t.from("baselines").insert({project_id:e.project_id,name:e.name.trim(),description:e.description??null,snapshot_at:(new Date).toISOString()}).select().single();return i?"23505"===i.code?ti($a(`Baseline name "${e.name}" already exists in this project`,["Pick a different name"])):ti(Ga(i.message)):ei(qa({id:a.id,name:a.name,description:a.description,field_definitions_snapshot:a.field_definitions_snapshot,snapshot_at:a.snapshot_at,created_at:a.created_at,locked:!1}))}catch(e){return Ao(e)??ti(Ga(e instanceof Error?e.message:String(e)))}}async function zo(e){try{const t=await qe(),r=await ui(t,e.baseline_id);if(!r)return ti(Ha("Baseline",e.baseline_id));const a=await Ro(t,r.project_id,"baselines_enabled");if(a)return a;if(null!==r.locked_at)return ti($a("Baseline is already locked",["Locks are one-way; nothing to do"]));const{data:i}=await t.from("projects").select("org_id").eq("id",r.project_id).maybeSingle();if(!i)return ti(Ha("Project",r.project_id));const{data:n}=await t.auth.getUser(),s=n?.user?.id;if(!s)return ti(Da());const{data:o}=await t.from("org_members").select("role").eq("user_id",s).eq("org_id",i.org_id).maybeSingle();if(!o||"admin"!==o.role)return ti($a("Only admins can lock baselines",["Ask an org admin to lock this baseline"]));const{data:c,error:l}=await t.from("baselines").update({locked_at:(new Date).toISOString(),locked_by:s}).eq("id",e.baseline_id).select().single();return l?ti(Ga(l.message)):ei(qa({id:c.id,locked_at:c.locked_at,locked_by:c.locked_by}))}catch(e){return Ao(e)??ti(Ga(e instanceof Error?e.message:String(e)))}}async function $o(e){try{const t=await qe(),[r,a]=await Promise.all([ui(t,e.baseline_a_id),ui(t,e.baseline_b_id)]);if(!r||!a)return ti(Ha("Baseline","one of the baseline_ids"));if(r.project_id!==a.project_id)return ti($a("Both baselines must belong to the same project",["Verify both baseline_ids are from the same project_id"]));const[i,n]=await Promise.all([fi(t,r.project_id,r.snapshot_at),fi(t,a.project_id,a.snapshot_at)]),s=new Map(i.map(e=>[e.id,e])),o=new Map(n.map(e=>[e.id,e])),c=Array.from(new Set([...s.keys(),...o.keys()])).sort(),l=[];let d=0,p=0,m=0;for(const e of c){const t=s.get(e),r=o.get(e);if(!t&&r)d++,l.push({item_id:e,change_type:"added"});else if(t&&!r)p++,l.push({item_id:e,change_type:"removed"});else if(t&&r){const a=[];t.title!==r.title&&a.push("title"),t.type!==r.type&&a.push("type");if(JSON.stringify(t.data??{})!==JSON.stringify(r.data??{})){a.push("data");const e=t.data??{},i=r.data??{};(e.status??null)!==(i.status??null)&&a.push("status"),(e.tags?.level??null)!==(i.tags?.level??null)&&a.push("tags.level"),JSON.stringify(e.tags?.domains??[])!==JSON.stringify(i.tags?.domains??[])&&a.push("tags.domains"),a.push(...ko(t.data,r.data))}a.length>0&&(m++,l.push({item_id:e,change_type:"modified",fields_changed:a}))}}const u=Math.max(e.offset??0,0),_=Math.min(Math.max(e.limit??50,1),200),f=e.detail?l.slice(u,u+_):l.slice(u,u+_).map(({item_id:e,change_type:t,fields_changed:r})=>({item_id:e,change_type:t,fields_changed:r}));return ei(qa({summary:{added:d,removed:p,modified:m,total_compared:c.length},items:f,page_info:{offset:u,limit:_,total:l.length},baselines:{a:{id:r.id,name:r.name,snapshot_at:r.snapshot_at},b:{id:a.id,name:a.name,snapshot_at:a.snapshot_at}}}))}catch(e){return Ao(e)??ti(Ga(e instanceof Error?e.message:String(e)))}}async function Ho(e){try{const t=await qe(),r=await ui(t,e.baseline_id);if(!r)return ti(Ha("Baseline",e.baseline_id));const a=await Ro(t,r.project_id,"baselines_enabled");if(a)return a;const{data:i}=await t.from("projects").select("org_id").eq("id",r.project_id).maybeSingle();if(!i)return ti(Ha("Project",r.project_id));const{data:n}=await t.auth.getUser(),s=n?.user?.id;if(!s)return ti(Da());const{data:o}=await t.from("org_members").select("role").eq("user_id",s).eq("org_id",i.org_id).maybeSingle();if(!o||"admin"!==o.role)return ti($a("Only admins can restore from a baseline",["Ask an org admin to run the restore"]));const{data:c}=await t.rpc("cc_project_is_controlled",{p_project_id:r.project_id});if(!0===c)return ti($a("Change control is on for this project. Release review before restoring a baseline.",["Release review on the project, then run the restore"]));const l=await fi(t,r.project_id,r.snapshot_at),d=l.map(e=>e.id),{data:p,error:m}=d.length>0?await t.from("items").select("*").eq("project_id",r.project_id).in("id",d):{data:[],error:null};if(m)return ti(Ga(m.message));const u=function(e,t){const r=new Map(t.map(e=>[e.id,e]));return e.flatMap(e=>{const t=r.get(e.id),a=[];var i,n;return t?(null!==t.deleted_at&&a.push("deleted_at"),t.type!==e.type&&a.push("type"),t.title!==e.title&&a.push("title"),i=t.data,n=e.data,JSON.stringify(i??{})!==JSON.stringify(n??{})&&a.push("data")):a.push("missing"),0===a.length?[]:[{id:e.id,type:e.type,title:e.title,data:e.data??{},diff_summary:{changed_fields:a,restored:!0},oldData:t?.data??null}]})}(l,p??[]),{data:_,error:f}=await t.rpc("variables_at",{p_project_id:r.project_id,p_snapshot_at:r.snapshot_at});if(f)return ti(Ga(f.message));const h=_??[];if(!0!==e.confirm)return ei(qa({preview:!0,items_to_restore:u.length,variables_to_restore:h.length,field_definitions_to_restore:r.field_definitions_snapshot?.length??null}));const y=await Eo(t,{baselineId:r.id,projectId:r.project_id,userId:s});return ei(qa({ok:!0,items_restored:y.items_restored,variables_restored:y.variables_restored,field_definitions_restored:y.field_definitions.length}))}catch(e){return Ao(e)??ti(Ga(e instanceof Error?e.message:String(e)))}}E(xo,{createBaselineHandler:()=>qo,diffBaselinesHandler:()=>$o,listBaselinesHandler:()=>Oo,lockBaselineHandler:()=>zo,restoreBaselineHandler:()=>Ho,restoreBaselineStateAtomically:()=>Eo,syncRestoredItemRelationships:()=>Io});var Mo,To,Do,Po,Co=A({"src/tools/baselines.ts"(){Le(),Dc(),ai(),wi(),wo(),So()}});function Uo(e){return"string"==typeof e}function Lo(e,t){if(null==e)return{ok:!0,value:{state:"draft",by:null,on:null,count:0,history:[]}};if("object"!=typeof e||Array.isArray(e))return{ok:!1,error:`${t}.acceptance must be an object`};const r=e;return{ok:!0,value:{state:"accepted"===r.state?"accepted":"draft",by:Uo(r.by)?r.by:null,on:Uo(r.on)?r.on:null,count:"number"==typeof r.count&&Number.isFinite(r.count)?r.count:0,history:Array.isArray(r.history)?r.history.filter(e=>"object"==typeof e&&null!==e&&!Array.isArray(e)).map(e=>({by:Uo(e.by)?e.by:"",on:Uo(e.on)?e.on:"",count:"number"==typeof e.count&&Number.isFinite(e.count)?e.count:0})):[]}}}function No(e){if(null==e)return{ok:!0,value:[]};if(!Array.isArray(e))return{ok:!1,error:"scenario_catalogs must be an array of catalogs"};if(e.length>Do)return{ok:!1,error:`scenario_catalogs accepts at most ${Do} catalogs`};const t=[],r=new Set;for(const a of e){if("object"!=typeof a||null===a||Array.isArray(a))return{ok:!1,error:"each catalog must be an object"};const e=a;if(!Uo(e.id)||!e.id.trim())return{ok:!1,error:"each catalog must carry a non-empty id"};if(r.has(e.id))return{ok:!1,error:`duplicate catalog id "${e.id}"`};if(r.add(e.id),!Uo(e.name)||!e.name.trim())return{ok:!1,error:`catalog "${e.id}" must carry a non-empty name`};if(e.name.length>To)return{ok:!1,error:`catalog "${e.id}" name must be at most ${To} characters`};if(!Array.isArray(e.entries))return{ok:!1,error:`catalog "${e.id}" entries must be an array`};if(e.entries.length>Mo)return{ok:!1,error:`catalog "${e.id}" accepts at most ${Mo} entries`};const i=[],n=new Set;for(const t of e.entries){if("object"!=typeof t||null===t||Array.isArray(t))return{ok:!1,error:`an entry of catalog "${e.id}" must be an object`};const r=t;if(!Uo(r.id)||!r.id.trim())return{ok:!1,error:`an entry of catalog "${e.id}" must carry a non-empty id`};if(n.has(r.id))return{ok:!1,error:`duplicate entry id "${r.id}" in catalog "${e.id}"`};n.add(r.id);const a=Uo(r.text)?r.text.trim():"";if(!a)return{ok:!1,error:`an entry of catalog "${e.id}" must carry non-empty text`};if(a.length>To)return{ok:!1,error:`an entry of catalog "${e.id}" must be at most ${To} characters`};const s=Array.isArray(r.function_tags)?r.function_tags.filter(Uo).map(e=>e.trim()).filter(Boolean):[];i.push({id:r.id,text:a,function_tags:s})}const s=Lo(e.acceptance,`catalog "${e.id}"`);if(!s.ok)return s;t.push({id:e.id,name:e.name.trim(),entries:i,acceptance:s.value})}return{ok:!0,value:t}}var Fo,Wo=A({"src/core/catalogs.ts"(){Mo=50,To=100,Do=200,Po=["create_catalog","rename_catalog","delete_catalog","add_entry","edit_entry","remove_entry","tag_entry","accept_catalog"]}}),Vo={};function Bo(e){return"safety-goal"===e?"hara":"failure-mode"===e?"dfmea":null}async function Jo(e,t,r){const{data:a,error:i}=await e.rpc("next_item_id",{p_project_id:t,p_prefix:r});if(i)throw new Error(`ID generation failed: ${i.message}`);return a}function Qo(e){const t=e?.relationships?.mitigated_by;return Array.isArray(t)?t.filter(e=>"string"==typeof e):[]}function Go(e){const t=e?.relationships?.verified_by;return Array.isArray(t)?t.filter(e=>"string"==typeof e):[]}function Yo(e){return JSON.parse(JSON.stringify(e??{}))}async function Ko(e,t,r){const a=[];for(const e of r){const t=(e??"").trim();t&&null===Qr(t,a)&&a.push(t)}if(0===a.length)return a;try{const{data:r}=await e.from("metadata").select("domains").eq("project_id",t).maybeSingle(),n=(i=r?.domains,Array.isArray(i)?i.filter(e=>"string"==typeof e):[]),s=a.map(e=>Qr(e,n)??e),{error:o}=await e.rpc("mutate_project_domains",{p_project_id:t,p_action:"add_functions",p_values:s});return o&&process.stderr.write(`[atoms-mcp] Warning: function tag registration failed: ${o.message}\n`),s}catch(e){return process.stderr.write(`[atoms-mcp] Warning: function tag registration failed: ${e instanceof Error?e.message:String(e)}\n`),a}var i}async function Zo(e,t){const{data:r,error:a}=await e.from("metadata").select("industry").eq("project_id",t).maybeSingle();if(a)throw new Error(a.message);return i=r,n=i?.industry,"automotive"===n||"medical"===n?i.industry:null;var i,n}function Xo(e){const t=e?.relationships?.parents;return Array.isArray(t)?t.filter(e=>"string"==typeof e)[0]??null:null}async function ec(e,t,r,a){const i=function(e){const t=e?.analysis_props?.variant;return"iso26262"===(r=t)||"iso14971"===r||"tir57"===r||"design"===r||"process"===r||"software"===r||"use"===r?t:null;var r}(a);return{variant:i,schemeId:Ir(r,i,i?null:await Zo(e,t))}}async function tc(e,t,r,a){const i=Xo(a),n=i?await pi(e,t,i):null;return ec(e,t,r,n?.data)}function rc(e,t,r,a={}){const i=a.where?`${a.where}: `:"",n=new Set([...Qt[e],...a.extraKeys??[]]),s=Object.keys(r??{});if(0===s.length)return{ok:!1,message:`${i}fields must contain at least one key`};const o=s.filter(e=>wr.includes(e));if(o.length>0)return{ok:!1,message:`${i}${na(e,o)}`};const c=sa(e);if(c&&s.includes(c.field))return{ok:!1,message:`${i}${oa(e)}`};const l=s.filter(e=>!n.has(e));if(l.length>0)return{ok:!1,message:`${i}Unknown field(s) for ${e}: ${l.join(", ")}. Allowed: ${[...n].sort().join(", ")}`};const d=Jt[e],p={};for(const t of s){const a=r[t];if(d[t]){const r="number"==typeof a?String(a):a;if("string"!=typeof r||!d[t].includes(r))return{ok:!1,message:`${i}Invalid ${t} value ${JSON.stringify(a)} for ${e}. Allowed: ${d[t].join(", ")}`};p[t]=r}else{if("string"!=typeof a)return{ok:!1,message:`${i}Field '${t}' must be a string`};p[t]=a}}return{ok:!0,writes:p}}async function ac(e){try{const r=await qe(),a=Me();try{await De(r,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const i="string"==typeof(t=e.subtype)&&Object.prototype.hasOwnProperty.call(sr,t)?sr[t]:null;if(!i)return ti(Ta(function(e){return`subtype must be one of: ${or.join(", ")} (got ${JSON.stringify(e)})`}(e.subtype)));const n=e.source_req_ids??[],s=e.source_functions??[];if("hara"===i&&n.length>0)return ti(Ta("A HARA is seeded from functions, never requirements (a requirement is an output of the analysis): pass source_functions and omit source_req_ids"));if("dfmea"===i&&s.length>0)return ti(Ta("source_functions applies to hara only; seed a DFMEA with source_req_ids"));if(n.length>50)return ti(Ta("source_req_ids supports at most 50 entries per call"));if(s.length>50)return ti(Ta("source_functions supports at most 50 entries per call"));if(n.length>0){const{data:t,error:a}=await r.from("items").select("id, type, data").eq("project_id",e.project_id).is("deleted_at",null).in("id",n);if(a)throw new Error(a.message);const i=new Map((t??[]).map(e=>[e.id,e])),s=n.filter(e=>!i.has(e));if(s.length>0)return ti(Ha("Source requirement(s)",s.join(", ")));const o=n.filter(e=>{const t=i.get(e);return"requirement"!==t.type||Fo.has(t.data?.req_type)});if(o.length>0)return ti(Ta(`source_req_ids must reference plain requirements (type='requirement', not analysis rows): ${o.join(", ")}`))}if(void 0!==e.variant&&!Nr(e.subtype).includes(e.variant))return ti(Ta(function(e,t){const r=Nr(e),a="risk_analysis"===e||"hazard_analysis"===e?"a risk analysis (ISO 14971)":"security_risk"===e?"a security risk analysis (AAMI TIR57)":"fmea"===e?"an fmea":`a ${e}`;return`variant must be ${1===r.length?"":"one of: "}${r.join(", ")} for ${a} (got ${JSON.stringify(t)}); the analysis carries its standard, so pick the variant that names it`}(e.subtype,e.variant)));const o=e.variant??function(e,t){return"medical"===t?lr[e]:cr[e]}(e.subtype,await Zo(r,e.project_id)),c=Ir(i,o),l=Rr(c),d=Hr(c),p={},m=[];for(const t of[...Gt,...er]){const r=e[t];"string"==typeof r&&""!==r&&(m.push(t),d.includes(t)&&(p[t]=Ka(r)))}const u=m.filter(e=>!d.includes(e));if(u.length>0)return ti(Ta(Ur(c,u)));const _="hara"===i?await Ko(r,e.project_id,s):n,f=await Jo(r,e.project_id,ir[c]),h=`New ${ca(c).parent}`,y=e.title??h,g=e.revision??"1.0",w=rr[i],v=(new Date).toISOString(),b={id:f,type:"analysis",title:y,body:"",tags:{domains:[],level:""},ownership:{primary:null,additional:[]},relationships:{parents:[],children:[],related:[],verifies:[],verified_by:[]},links:[],analysis:{subtype:i,scheme:c,revision:g,...l?{standard:l}:{}},analysis_props:{variant:o,...p},metadata:{created_at:v,created_by:a,updated_at:v,updated_by:a}},{error:j}=await r.rpc("save_item_with_revision",{p_item_id:f,p_project_id:e.project_id,p_type:"analysis",p_title:y,p_data:b,p_user_id:a,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null});if(j)throw new Error(j.message);const k=(e,t)=>"hara"===i?{id:e,source_function:t}:{id:e,source_req:t},S=[],x=[];for(const t of _){const n=await Jo(r,e.project_id,ar[c]),s={id:n,type:"requirement",title:"",body:"",req_type:w,tags:{domains:[],level:""},ownership:{primary:null,additional:[]},relationships:{parents:[f],children:[],related:[],verifies:[],verified_by:[]},links:[],analysis_props:"hara"===i?{source_req:null,source_function:t,approved:!1,approved_at:null,field_source:{}}:{source_req:t,approved:!1,approved_at:null,field_source:{}},metadata:{created_at:v,created_by:a,updated_at:v,updated_by:a}},{error:o}=await r.rpc("save_item_with_revision",{p_item_id:n,p_project_id:e.project_id,p_type:"requirement",p_title:"",p_data:s,p_user_id:a,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null});if(o)throw new Error(o.message);const{error:l}=await r.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:n,p_to_id:f,p_type:"parent",p_action:"add",p_user_id:a,p_event_source:"mcp"});l&&(process.stderr.write(`[atoms-mcp] Warning: analysis row parent link sync failed for ${n}: ${l.message}\n`),x.push({row_id:n,..."hara"===i?{source_function:t}:{source_req:t},message:l.message})),await r.from("change_history").insert({item_id:n,project_id:e.project_id,changed_by:a,event_type:"created",old_data:null,new_data:s,actor:Mc(),session_id:Hc()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),S.push(k(n,t))}return await r.from("change_history").insert({item_id:f,project_id:e.project_id,changed_by:a,event_type:"created",old_data:null,new_data:b,actor:Mc(),session_id:Hc()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),ei(qa({id:f,subtype:i,title:y,revision:g,scheme:c,variant:o,labels:ca(c),...l?{standard:l}:{},rows:S,...x.length>0?{link_warnings:x,warning:`${x.length} row(s) were created but could not be attached to the analysis and will not appear in atoms_get_analysis. Repair: for each row in link_warnings, call atoms_link_items with type 'parent', from_id=<row id>, to_id=${f}.`}:{}}))}catch(e){return ti(Ja(e))}var t}function ic(e){for(const t of Object.keys(e)){const r=e[t];"string"==typeof r&&(e[t]=Ka(r))}}async function nc(e){try{const t=await qe(),r=Me();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const a=await pi(t,e.project_id,e.row_id);if(!a)return ti(Ha("Analysis row",e.row_id));const i=a.data;if("analysis"===a.type)return async function(e,t,r,a,i,n){const{schemeId:s}=await ec(e,r,function(e){return"dfmea"===e?.analysis?.subtype?"dfmea":"hara"}(i),i),o=function(e,t){const r=Hr(e),a=Object.keys(t??{});if(0===a.length)return{ok:!1,message:"fields must contain at least one key"};const i=a.filter(e=>{return t=e,jt.includes(t)||kt.includes(t);var t});if(i.length>0)return{ok:!1,message:At(i)};const n=a.filter(e=>!r.includes(e));if(n.length>0)return{ok:!1,message:Ur(e,n)};const s={};for(const r of a){const a=t[r];if("lifecycle_sources"===r){let e=a;if("string"==typeof a)try{e=JSON.parse(a)}catch{return{ok:!1,message:"Field 'lifecycle_sources' must be a JSON array of [name, considered, note] tuples"}}if(!Array.isArray(e)||!e.every(e=>Array.isArray(e)&&3===e.length&&"string"==typeof e[0]&&"boolean"==typeof e[1]&&"string"==typeof e[2]))return{ok:!1,message:"Field 'lifecycle_sources' must be an array of [name, considered, note] tuples"};s[r]=e;continue}if("string"!=typeof a)return{ok:!1,message:`Field '${r}' must be a string`};const i=Mr(e);if("overall_residual_risk"===r&&""!==a&&!i.includes(a))return{ok:!1,message:Lr(r,a,i)};if("software_safety_class"===r&&""!==a&&!Xt.includes(a))return{ok:!1,message:Lr(r,a,Xt)};s[r]=a}return{ok:!0,writes:s}}(s,n);if(!o.ok)return ti(Ta(o.message));const c=o.writes;ic(c);const l=Yo(i);l.analysis_props&&"object"==typeof l.analysis_props||(l.analysis_props={});const d=l.analysis_props;for(const[e,t]of Object.entries(c))d[e]=t;const p=(new Date).toISOString();l.metadata={...l.metadata??{},updated_at:p,updated_by:t};const{data:m,error:u}=await e.rpc("save_item_with_revision",{p_item_id:a.id,p_project_id:r,p_type:a.type,p_title:a.title,p_data:l,p_user_id:t,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null,p_expected_updated_at:a.updated_at});if(u)throw new Error(u.message);return m?(await e.from("change_history").insert({item_id:a.id,project_id:r,changed_by:t,event_type:"updated",old_data:i,new_data:l,actor:Mc(),session_id:Hc()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),ei(qa({analysis_id:a.id,scheme:s,written_fields:Object.keys(c),scope:Dr(d),overall:Pr(d)}))):ti($a(`Fill silently failed for '${a.id}': save RPC returned no row. Likely RLS denied the write.`,["Verify you have editor or admin role in this project","Retry after checking project access"]))}(t,r,e.project_id,a,i,e.fields??{});const n=Bo(i?.req_type);if(!n)return ti(Ta(`Item ${e.row_id} is not an analysis row (req_type must be 'safety-goal' or 'failure-mode')`));const{schemeId:s}=await tc(t,e.project_id,n,i),o=rc(s,0,e.fields??{},{extraKeys:"hara"===n?["effect","title"]:["effect"]});if(!o.ok)return ti(Ta(o.message));const c=o.writes;if(ic(c),"hara"===n&&"string"==typeof c.source_function&&c.source_function.trim()){const[r]=await Ko(t,e.project_id,[c.source_function]);r&&(c.source_function=r)}const l=i?.analysis_props??null,d=Yo(i);d.analysis_props&&"object"==typeof d.analysis_props||(d.analysis_props={source_req:null,approved:!1,approved_at:null,field_source:{}});const p=d.analysis_props;p.field_source&&"object"==typeof p.field_source||(p.field_source={});let m=a.title;for(const[e,t]of Object.entries(c))"effect"===e?d.body=t:"title"===e?(d.title=t,m=t):(p[e]=t,"failure_mode"===e&&"dfmea"===n&&(d.title=t,m=t)),p.field_source[e]="mcp";const u=fa(p),_=(new Date).toISOString();d.metadata={...d.metadata??{},updated_at:_,updated_by:r};const{data:f,error:h}=await t.rpc("save_item_with_revision",{p_item_id:a.id,p_project_id:e.project_id,p_type:a.type,p_title:m,p_data:d,p_user_id:r,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null,p_expected_updated_at:a.updated_at});if(h)throw new Error(h.message);if(!f)return ti($a(`Fill silently failed for '${e.row_id}': save RPC returned no row. Likely RLS denied the write.`,["Verify you have editor or admin role in this project","Retry after checking project access"]));await t.from("change_history").insert({item_id:a.id,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:i,new_data:d,actor:Mc(),session_id:Hc(),...u?{reason:`${Object.keys(c).join(", ")} written through atoms_fill_row; approval cleared (a signature never covers content the approver did not see)`}:{}}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)});let y=!1;const g=Xo(i);if(g){const a=await pi(t,e.project_id,g),i=a?.data,n=i?.analysis_props??{};if(a&&Rt(n)){y=!0;const s={...i,analysis_props:n},{error:o}=await t.rpc("save_item_with_revision",{p_item_id:a.id,p_project_id:e.project_id,p_type:a.type,p_title:a.title,p_data:s,p_user_id:r,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null,p_expected_updated_at:a.updated_at});o&&process.stderr.write(`[atoms-mcp] Warning: acceptance revoke save failed for '${a.id}': ${o.message}\n`)}}const w=ra(s,p),v=[];if("hara_iso26262"===s){const i=ra(s,l),n=Qo(d);if(n.length>0&&w!==Pt&&w!==i)for(const i of n){const n=await pi(t,e.project_id,i);if(!n)continue;const s=n.data;if(s?.asil===w&&s?.asil_source===a.id)continue;const o=s?.asil??null,c=s?.asil_source??null,l=Yo(s);l.asil=w,l.asil_source=a.id,l.metadata={...l.metadata??{},updated_at:_,updated_by:r};const{error:d}=await t.rpc("save_item_with_revision",{p_item_id:n.id,p_project_id:e.project_id,p_type:n.type,p_title:n.title,p_data:l,p_user_id:r,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null,p_expected_updated_at:n.updated_at});if(d)throw new Error(d.message);await t.from("change_history").insert({item_id:n.id,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:{asil:o,asil_source:c},new_data:{asil:w,asil_source:a.id},actor:Mc(),session_id:Hc(),reason:`ASIL propagated from ${a.id} (rating change via atoms_fill_row)`}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),v.push(i)}}return ei(qa({row_id:a.id,subtype:n,written_fields:Object.keys(c),computed_level:w,...v.length>0?{asil_propagated_to:v}:{},...u?{approval_cleared:!0}:{},acceptance_revoked:y}))}catch(e){return ti(Ja(e))}}function sc(e){const t=e??{},r=String(t.threat_model_method??"").trim(),a=e=>String(e??"").split(/\r\n?|\n/).map(e=>e.trim()).filter(e=>""!==e),i=Array.isArray(t.lifecycle_sources)?t.lifecycle_sources.filter(e=>Array.isArray(e)&&3===e.length&&"string"==typeof e[0]&&"boolean"==typeof e[1]&&"string"==typeof e[2]).map(([e,t,r])=>({name:e,considered:t,note:r})):[];return{stated:""!==r,method:r,rationale:String(t.threat_model_rationale??"").trim(),environment_assumptions:a(t.environment_assumptions),attack_surfaces:a(t.attack_surfaces),lifecycle_sources:i,related_documents:String(t.related_documents??"").trim()}}async function oc(e,t,r){const a=await pi(e,t,r);if(!a)return{ok:!1,result:ti(Ha("Analysis",r))};if("analysis"!==a.type)return{ok:!1,result:ti(Ta(`Item ${r} is not an analysis (type='${a.type}')`))};const i=a.data,n=Bo("hara"===i?.analysis?.subtype?"safety-goal":"dfmea"===i?.analysis?.subtype?"failure-mode":null);if(!n)return{ok:!1,result:ti(Ta(`Analysis ${r} is missing a valid analysis.subtype`))};const s=rr[n],{variant:o,schemeId:c}=await ec(e,t,n,i),l=Array.isArray(i?.relationships?.children)?i.relationships.children.filter(e=>"string"==typeof e):[];let d=[];if(l.length>0){const{data:r,error:a}=await e.from("items").select("*").eq("project_id",t).is("deleted_at",null).in("id",l);if(a)throw new Error(a.message);const i=new Map((r??[]).filter(e=>!e.deleted_at&&l.includes(e.id)&&e.data?.req_type===s).map(e=>[e.id,e]));d=l.map(e=>i.get(e)).filter(e=>Boolean(e))}const p=new Set;for(const e of d){const t=e.data?.analysis_props??{};"string"==typeof t.source_req&&t.source_req&&p.add(t.source_req);for(const t of Qo(e.data))p.add(t);for(const t of Go(e.data))p.add(t)}const m=new Map;if(p.size>0){const{data:r,error:a}=await e.from("items").select("id, updated_at").eq("project_id",t).is("deleted_at",null).in("id",[...p]);if(a)throw new Error(a.message);for(const e of r??[])m.set(e.id,e.updated_at)}const u=d.map(e=>{const t=e.data?.analysis_props??{},r=Qo(e.data).filter(e=>m.has(e)),a=Go(e.data).filter(e=>m.has(e)),i=r.length>0,n=ra(c,t),s=function(e,t,r){if(r)return null;if("hara_iso26262"===e){const e=Gr(t);return"QM"!==e&&e!==Pt?`asil ${e} unmitigated`:null}if("hazard_iso14971"===e){const e=Zr(t);return"Low"!==e&&e!==Pt?`${e.toLowerCase()} risk uncontrolled`:null}if("security_tir57"===e){const e=ea(t);return bt(e)?`${e.toLowerCase()} security risk uncontrolled`:null}const a=parseInt(ra(e,t),10),i=parseInt(t?.severity||"",10);return!Number.isNaN(a)&&a>=100?"rpn >= 100":!Number.isNaN(i)&&i>=9?"severity >= 9":null}(c,t,i),o={id:e.id,title:"string"==typeof e.title?e.title:"",source_req:"string"==typeof t.source_req?t.source_req:null,effect:"string"==typeof e.data?.body?e.data.body:"",computed_level:n,computed_level_text:Br(c,n),is_gap:null!==s,gap_rule:s,is_critical:aa(c,t,i),is_stale:ia(t,r,e=>m.get(e)??null),mitigated_by:r,link_roles:t.link_roles??{},approved:!0===t.approved,approved_at:t.approved_at??null,approved_by:t.approved_by??null,approved_note:"string"==typeof t.approved_note?t.approved_note:"",verified_by:a,residual_level:"hazard_iso14971"===c?Xr(t):"security_tir57"===c?ta(t):null,mitigation_types:t.mitigation_types??{},mcp_fields:Object.entries(t.field_source??{}).filter(([,e])=>"mcp"===e).map(([e])=>e).sort()};for(const e of Qt[c])o[e]=t[e]??null;return o});return{ok:!0,analysis:{id:a.id,title:"string"==typeof a.title?a.title:"",subtype:n,scheme:c,revision:"string"==typeof i?.analysis?.revision?i.analysis.revision:"1.0",standard:"string"==typeof i?.analysis?.standard?i.analysis.standard:Rr(c)??null,variant:o,labels:ca(c),acceptability:Vr(c),definitions:Wr(c),control_types:da(c),field_labels:Fr(c),scope:Hr(c).length>0?Dr(i?.analysis_props):null,overall:Hr(c).length>0?Pr(i?.analysis_props,Mr(c)):null,threat_model:"security_tir57"===c?sc(i?.analysis_props):null,acceptance:It(i?.analysis_props)},schemeId:c,parentProps:i?.analysis_props??null,rows:u,counts:{rows:u.length,gaps:u.filter(e=>e.is_gap).length,critical:u.filter(e=>e.is_critical).length,stale:u.filter(e=>e.is_stale).length,approved:u.filter(e=>e.approved).length,linked:u.filter(e=>e.mitigated_by.length>0).length}}}async function cc(e){try{const t=await qe(),r=await oc(t,e.project_id,e.analysis_id);return r.ok?ei(qa({analysis:r.analysis,rows:r.rows,counts:r.counts})):r.result}catch(e){return ti(Ja(e))}}async function lc(e){try{const i=await qe(),n=Me();try{await De(i,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}if(e.row_id===e.req_id)return ti(Ta("row_id and req_id must be different items"));const s=await pi(i,e.project_id,e.row_id);if(!s)return ti(Ha("Analysis row",e.row_id));const o=s.data,c=Bo(o?.req_type);if(!c)return ti(Ta(`Item ${e.row_id} is not an analysis row (req_type must be 'safety-goal' or 'failure-mode')`));const l=await pi(i,e.project_id,e.req_id);if(!l)return ti(Ha("Requirement",e.req_id));const d=l.data;if("requirement"!==l.type||Fo.has(d?.req_type))return ti(Ta(`req_id must reference a plain requirement (type='requirement', not an analysis row): ${e.req_id}`));const{schemeId:p}=await tc(i,e.project_id,c,o);if(function(e){return"hara_iso26262"===e||"hazard_iso14971"===e||"security_tir57"===e}(p)&&d?.req_type!==Mt)return ti(Ta(function(e,t){return`${e} is not a ${vr[t].mitigating_requirement}: a mitigation target must be a requirement of req_type '${Mt}'. Create or convert one, then link it.`}(e.req_id,p)));const m=la(p);if(0===m.length&&void 0!==e.control_type)return ti(Ta(function(e){return`control_type applies to the medical schemes only (ISO 14971 clause 7); ${vr[e].parent} links carry none, so omit it.`}(p)));if(m.length>0&&!e.control_type)return ti(Ta(pa(p)));if(!(void 0===e.control_type||Or(e.control_type)&&function(e,t){return la(e).some(e=>e.id===t)}(p,e.control_type)))return ti(Ta(pa(p)));if("dfmea"===c&&!e.role)return ti(Ta(`role is required for dfmea mitigation links. Choose one of: ${Ct.join(", ")}`));if("hara"===c&&e.role)return ti(Ta("role is not applicable to hara mitigation links; omit it (roles are a dfmea concept)"));let u=Pt;if("hara_iso26262"===p){u=ra(p,o?.analysis_props);const i=d?.asil;if(u!==Pt&&"string"==typeof i&&i.length>0&&i!==u&&!0!==e.override)return ti((r=u,{...$a(`ASIL conflict: the target requirement already carries asil '${t=i}' but ${a=s.id} computes '${r}'.`,["Pass override=true to apply the HARA value; the override is history-logged like a dialog accept","Or align the row's S/E/C ratings with the existing ASIL before linking"]),details:{code:"asil_conflict",current_value:t,incoming_value:r,source_row:a,hint:"pass override=true to apply the HARA value; the override is history-logged like a dialog accept"}}))}const _="dfmea"===c?e.role:null,{error:f}=await i.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:e.row_id,p_to_id:e.req_id,p_type:"mitigated_by",p_action:"add",p_user_id:n,p_event_source:"mcp",p_expected_from_updated_at:s.updated_at,p_expected_to_updated_at:l.updated_at,p_role:_});if(f)throw new Error(f.message);await i.from("change_history").insert({item_id:e.row_id,project_id:e.project_id,changed_by:n,event_type:"updated",old_data:null,new_data:{relationship_change:{action:"add",type:"mitigated_by",from:e.row_id,to:e.req_id,..._?{role:_}:{}}},actor:Mc(),session_id:Hc()}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)});{const t=Xo(o);if(t){const r=await pi(i,e.project_id,t);if(r){const{revoked:t,parentProps:a}=qt(r.data);if(t){const t={...r.data,analysis_props:a},{error:s}=await i.rpc("save_item_with_revision",{p_item_id:r.id,p_project_id:e.project_id,p_type:r.type,p_title:r.title,p_data:t,p_user_id:n,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null,p_expected_updated_at:r.updated_at});s&&process.stderr.write(`[atoms-mcp] Warning: acceptance revoke save failed for '${r.id}': ${s.message}\n`)}}}}let h,y;if(e.control_type&&Or(e.control_type)){const t=await pi(i,e.project_id,e.row_id)??s,r=Yo(t.data);r.analysis_props&&"object"==typeof r.analysis_props||(r.analysis_props={source_req:null,approved:!1,approved_at:null,field_source:{}});const a=r.analysis_props;a.mitigation_types&&"object"==typeof a.mitigation_types||(a.mitigation_types={}),a.mitigation_types[e.req_id]=e.control_type;const o=(new Date).toISOString();r.metadata={...r.metadata??{},updated_at:o,updated_by:n};const{error:c}=await i.rpc("save_item_with_revision",{p_item_id:t.id,p_project_id:e.project_id,p_type:t.type,p_title:t.title,p_data:r,p_user_id:n,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null,p_expected_updated_at:t.updated_at});if(c)throw new Error(c.message);h=e.control_type}if("hara_iso26262"===p&&u!==Pt){const t=await pi(i,e.project_id,e.req_id);if(t){const r=t.data,a=r?.asil??null,o=r?.asil_source??null,c=Yo(r);c.asil=u,c.asil_source=s.id;const l=(new Date).toISOString();c.metadata={...c.metadata??{},updated_at:l,updated_by:n};const{error:d}=await i.rpc("save_item_with_revision",{p_item_id:t.id,p_project_id:e.project_id,p_type:t.type,p_title:t.title,p_data:c,p_user_id:n,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null,p_expected_updated_at:t.updated_at});if(d)throw new Error(d.message);await i.from("change_history").insert({item_id:t.id,project_id:e.project_id,changed_by:n,event_type:"updated",old_data:{asil:a,asil_source:o},new_data:{asil:u,asil_source:s.id},actor:Mc(),session_id:Hc(),reason:!0===e.override?`ASIL override accepted via atoms_link_mitigation (was '${a??"unset"}', now '${u}' from ${s.id})`:`ASIL set via atoms_link_mitigation from ${s.id}`}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),y=u}}return ei(qa({row_id:e.row_id,req_id:e.req_id,type:"mitigated_by",role:_,scheme:p,...h?{control_type:h}:{},...y?{asil_written:y}:{}}))}catch(e){return ti(Ja(e))}var t,r,a}async function dc(e){try{const r=await qe(),a=await oc(r,e.project_id,e.analysis_id);if(!a.ok)return a.result;const i=a.rows.filter(e=>e.is_gap).map(e=>({id:e.id,title:e.title,source_req:e.source_req,source_function:"string"==typeof e.source_function?e.source_function:null,computed_level:e.computed_level,rule:e.gap_rule})),n=qr(await async function(e,t){const{data:r,error:a}=await e.from("metadata").select("levels").eq("project_id",t).maybeSingle();if(a)throw new Error(a.message);const i=r?.levels;return Array.isArray(i)?i.filter(e=>"string"==typeof e):[]}(r,e.project_id)),s=_a(a.schemeId).some(e=>"requirement"===e.scope)?await async function(e,t,r){const{data:a,error:i}=await e.from("items").select("id, data").eq("project_id",t).is("deleted_at",null).eq("type","requirement");if(i)throw new Error(i.message);const n=(a??[]).filter(e=>e.data?.req_type===Mt&&function(e,t){const r=zr(e);return r.length>0&&r===zr(t)}(e.data?.tags?.level,r));if(0===n.length)return[];const s=e=>{const t=e.data?.relationships?.parents;return Array.isArray(t)?t.filter(e=>"string"==typeof e):[]},o=new Set;for(const e of n)for(const t of s(e))o.add(t);const c=new Set;if(o.size>0){const{data:r,error:a}=await e.from("items").select("id").eq("project_id",t).is("deleted_at",null).in("id",[...o]);if(a)throw new Error(a.message);for(const e of r??[])c.add(e.id)}return n.filter(e=>!s(e).some(e=>c.has(e))).map(e=>e.id)}(r,e.project_id,n.level):[],o=a.rows.map(e=>({id:e.id,level:e.computed_level,linked:e.mitigated_by.length>0,verified:e.verified_by.length>0,residualLevel:e.residual_level??Pt,severity:ma(e.severity)})),c="hara_iso26262"===a.schemeId&&function(e,t){const r=Array.isArray(e)?e:[];return 0!==r.length&&r.some(e=>{const r=t.find(t=>t.id===e);return!r||"accepted"!==r.acceptance.state})}(a.parentProps?.scenario_catalogs,await async function(e,t){const{data:r,error:a}=await e.from("metadata").select("scenario_catalogs").eq("project_id",t).maybeSingle();if(a)throw new Error(a.message);const i=No(r?.scenario_catalogs??[]);return i.ok?i.value:[]}(r,e.project_id)),l=function(e,t,r,a=!1){const i="security_tir57"===r,n=i?String(e?.threat_model_method??"").trim():void 0;return{hasRows:t.length>0,scopeStated:i?""!==n:Cr(e),overallJudged:""!==Pr(e,i?mt:Zt).residual_risk,everyRowSettled:t.every(e=>e.linked&&e.residualLevel!==Pt),catalogsDraft:a,accepted:"accepted"===e?.acceptance_state}}(a.parentProps,o,a.schemeId,c),d=function(e,t,r,a){const i=[];for(const n of _a(e)){if("analysis"===n.scope){if(!a||!n.matchesAnalysis||!n.matchesAnalysis(a))continue;i.push({kind:n.kind,label:n.label(1),rowIds:[],reqIds:[],count:1,fix:n.fix,fixLabel:n.fixLabel});continue}if("requirement"===n.scope){if(0===r.length)continue;i.push({kind:n.kind,label:n.label(r.length),rowIds:[],reqIds:[...r],count:r.length,fix:n.fix,fixLabel:n.fixLabel});continue}const e=n.matches;if(!e)continue;const s=t.filter(t=>e(t)).map(e=>e.id);0!==s.length&&i.push({kind:n.kind,label:n.label(s.length),rowIds:s,reqIds:[],count:s.length,fix:n.fix,fixLabel:n.fixLabel})}return i}(a.schemeId,o,s,l),p=d.map(e=>({kind:e.kind,label:e.label,row_ids:e.rowIds,req_ids:e.reqIds,gap_count:e.count,fix:e.fix,fix_label:e.fixLabel}));return ei(qa({analysis_id:a.analysis.id,subtype:a.analysis.subtype,scheme:a.analysis.scheme,labels:a.analysis.labels,gaps:i,count:(t=d,t.reduce((e,t)=>e+t.count,0)),gap_kinds:p,system_level:n.level,system_level_source:n.source}))}catch(e){return ti(Ja(e))}var t}function pc(e){return(e.failure_mode??"").trim()||(e.hazard??"").trim()||""}async function mc(e){try{const t=await qe(),r=Me();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const a=await pi(t,e.project_id,e.analysis_id);if(!a)return ti(Ha("Analysis",e.analysis_id));if("analysis"!==a.type)return ti(Ta(`Item ${e.analysis_id} is not an analysis (type='${a.type}')`));const i=a.data,n=i?.analysis?.subtype,s="hara"===n?"hara":"dfmea"===n?"dfmea":null;if(!s)return ti(Ta(`Analysis ${e.analysis_id} is missing a valid analysis.subtype`));const{variant:o,schemeId:c}=await ec(t,e.project_id,s,i);if("hara"===s&&e.source_req)return ti(Ta("A risk analysis row is seeded from a function, never a requirement: pass source_function"));if("dfmea"===s&&e.source_function)return ti(Ta("source_function applies to a risk analysis only; seed an FMEA row with source_req"));const l=Array.isArray(e.rows)?e.rows:[];if(0===l.length)return ti(Ta("rows must contain at least one drafted row"));if(l.length>Dt)return ti(Ta(`Propose rows accepts at most ${Dt} rows per call; ${l.length} were sent. Split the proposal and call again.`));const d=[];for(let e=0;e<l.length;e++){const t=rc(c,0,l[e]??{},{extraKeys:"hara"===s?["effect","title"]:["effect"],where:`row ${e+1}`});if(!t.ok)return ti(Ta(t.message));ic(t.writes),d.push(t.writes)}let p=Ka(e.source_function??"").trim();if("hara"===s&&p){const[r]=await Ko(t,e.project_id,[p]);r&&(p=r)}const m=(new Date).toISOString(),u=rr[s],_=[],f=[];for(const a of d){const i=await Jo(t,e.project_id,ar[c]),n={source_req:"dfmea"===s?e.source_req??null:null,..."hara"===s?{source_function:p||null}:{},approved:!1,approved_at:null,approved_by:null,field_source:{}};let o="",l="";for(const[e,t]of Object.entries(a))"effect"===e?o=t:"title"===e?l=t:(n[e]=t,"failure_mode"===e&&"dfmea"===s&&(l=t)),n.field_source[e]="mcp";l||(l=pc(a));const d={id:i,type:"requirement",title:l,body:o,req_type:u,tags:{domains:[],level:""},ownership:{primary:null,additional:[]},relationships:{parents:[e.analysis_id],children:[],related:[],verifies:[],verified_by:[]},links:[],analysis_props:n,metadata:{created_at:m,created_by:r,updated_at:m,updated_by:r}},{error:h}=await t.rpc("save_item_with_revision",{p_item_id:i,p_project_id:e.project_id,p_type:"requirement",p_title:l,p_data:d,p_user_id:r,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null});if(h)throw new Error(h.message);const{error:y}=await t.rpc("apply_link_change",{p_project_id:e.project_id,p_from_id:i,p_to_id:e.analysis_id,p_type:"parent",p_action:"add",p_user_id:r,p_event_source:"mcp"});y&&(process.stderr.write(`[atoms-mcp] Warning: proposed row parent link sync failed for ${i}: ${y.message}\n`),f.push({row_id:i,message:y.message})),await t.from("change_history").insert({item_id:i,project_id:e.project_id,changed_by:r,event_type:"created",old_data:null,new_data:d,actor:Mc(),session_id:Hc(),reason:"Row proposed through atoms_propose_rows (machine-proposed, unapproved)"}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)}),_.push({id:i,written_fields:Object.keys(a),computed_level:ra(c,n)})}if(_.length>0){const{revoked:n,parentProps:s}=qt(i);if(n){const n={...i,analysis_props:s},{error:o}=await t.rpc("save_item_with_revision",{p_item_id:a.id,p_project_id:e.project_id,p_type:a.type,p_title:a.title,p_data:n,p_user_id:r,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null,p_expected_updated_at:a.updated_at});o&&process.stderr.write(`[atoms-mcp] Warning: acceptance revoke save failed for '${a.id}': ${o.message}\n`)}}return ei(qa({analysis_id:e.analysis_id,subtype:s,scheme:c,...o?{variant:o}:{},labels:ca(c),..."hara"===s?{source_function:p||null}:{source_req:e.source_req??null},rows:_,count:_.length,max_rows:Dt,approved:!1,...f.length>0?{link_warnings:f,warning:`${f.length} proposed row(s) were created but could not be attached to ${e.analysis_id}. Repair: call atoms_link_items with type 'parent', from_id=<row id>, to_id=${e.analysis_id}.`}:{}}))}catch(e){return ti(Ja(e))}}async function uc(e){try{const t=await qe(),r=Me();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const a=await pi(t,e.project_id,e.row_id);if(!a)return ti(Ha("Analysis row",e.row_id));const i=a.data,n=Bo(i?.req_type);if(!n)return ti(Ta(`Item ${e.row_id} is not an analysis row (req_type must be 'safety-goal' or 'failure-mode')`));const{schemeId:s}=await tc(t,e.project_id,n,i),o=e.rationale?function(e,t){return Jt[e][t]?"hara_iso26262"===e?"severity"===t?"s_rationale":"exposure"===t?"e_rationale":"controllability"===t?"c_rationale":null:"hazard_iso14971"===e?"severity"===t||"residual_s"===t?"s_rationale":"p1"===t||"residual_p1"===t?"p1_rationale":"p2"===t||"residual_p2"===t?"p2_rationale":null:"security_tir57"===e?"severity"===t||"residual_s"===t?"s_rationale":"exploitability"===t||"residual_e"===t?"e_rationale":null:"rationale":null}(s,e.field):null;if(e.rationale&&!o)return ti(Ta(`rationale applies to a rating field only; ${e.field} is not a rating of ${s}`));const c={[e.field]:e.value};o&&e.rationale&&(c[o]=e.rationale);const l=rc(s,0,c,{extraKeys:"hara"===n?["effect","title"]:["effect"]});if(!l.ok)return ti(Ta(l.message));const d=l.writes;ic(d);const p=Yo(i);p.analysis_props&&"object"==typeof p.analysis_props||(p.analysis_props={source_req:null,approved:!1,approved_at:null,field_source:{}});const m=p.analysis_props;m.field_source&&"object"==typeof m.field_source||(m.field_source={});let u=a.title;for(const[e,t]of Object.entries(d))"effect"===e?p.body=t:"title"===e?(p.title=t,u=t):(m[e]=t,"failure_mode"===e&&"dfmea"===n&&(p.title=t,u=t)),m.field_source[e]="mcp";const _=fa(m),f=(new Date).toISOString();p.metadata={...p.metadata??{},updated_at:f,updated_by:r};const{data:h,error:y}=await t.rpc("save_item_with_revision",{p_item_id:a.id,p_project_id:e.project_id,p_type:a.type,p_title:u,p_data:p,p_user_id:r,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null,p_expected_updated_at:a.updated_at});if(y)throw new Error(y.message);if(!h)return ti($a(`Suggestion silently failed for '${e.row_id}': save RPC returned no row. Likely RLS denied the write.`,["Verify you have editor or admin role in this project","Retry after checking project access"]));await t.from("change_history").insert({item_id:a.id,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:i,new_data:p,actor:Mc(),session_id:Hc(),reason:`${e.field} suggested through atoms_suggest_field (machine-proposed, unapproved)`+(_?"; approval cleared (a signature never covers content the approver did not see)":"")}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)});const g=Object.entries(m.field_source).filter(([,e])=>"mcp"===e).map(([e])=>e).sort();return ei(qa({row_id:a.id,subtype:n,scheme:s,field:e.field,field_source:"mcp",written_fields:Object.keys(d),...o?{rationale_field:o}:{},..._?{approval_cleared:!0}:{},row:{id:a.id,computed_level:ra(s,m),approved:!0===m.approved,mcp_fields:g,...Object.fromEntries(Qt[s].map(e=>[e,m[e]??null]))}}))}catch(e){return ti(Ja(e))}}async function _c(e){try{const t=await qe(),r=Me();try{await De(t,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}const a=await pi(t,e.project_id,e.row_id);if(!a)return ti(Ha("Analysis row",e.row_id));const i=a.data,n=Bo(i?.req_type);if(!n)return ti(Ta(`Item ${e.row_id} is not an analysis row (req_type must be 'safety-goal' or 'failure-mode')`));const s=e.approver_id??r,o=await async function(e,t,r){const{data:a,error:i}=await e.from("projects").select("org_id").eq("id",t).maybeSingle();if(i)throw new Error(i.message);if(!a)throw new Error(`Project '${t}' not found`);const{data:n,error:s}=await e.from("org_members").select("role").eq("user_id",r).eq("org_id",a.org_id).maybeSingle();if(s)throw new Error(s.message);const o=n?.role;return"string"==typeof o?o:null}(t,e.project_id,s);if(!o)return ti(Ta(`Approver '${s}' is not a member of this project's organization. Approve as a project member (omit approver_id to approve as yourself).`));const{schemeId:c}=await tc(t,e.project_id,n,i),l=i?.analysis_props??{},d=ra(c,l);if(d===Pt)return ti(Ta(`${e.row_id} cannot be approved before it is rated: complete the rating fields first (the computed ${"hara_iso26262"===c?"ASIL":"hazard_iso14971"===c?"risk":"RPN"} is unset).`));const p=Yo(i);p.analysis_props&&"object"==typeof p.analysis_props||(p.analysis_props={source_req:null,approved:!1,approved_at:null,field_source:{}});const m=p.analysis_props;m.field_source&&"object"==typeof m.field_source||(m.field_source={});const u=Ka(e.note??""),_=(new Date).toISOString();m.approved=!0,m.approved_at=_,m.approved_by=s,m.approved_note=u;const f=[];for(const[e,t]of Object.entries(m.field_source))"mcp"===t&&(m.field_source[e]="user",f.push(e));f.sort(),p.metadata={...p.metadata??{},updated_at:_,updated_by:r};const{data:h,error:y}=await t.rpc("save_item_with_revision",{p_item_id:a.id,p_project_id:e.project_id,p_type:a.type,p_title:a.title,p_data:p,p_user_id:r,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null,p_expected_updated_at:a.updated_at});if(y)throw new Error(y.message);if(!h)return ti($a(`Approval silently failed for '${e.row_id}': save RPC returned no row. Likely RLS denied the write.`,["Verify you have editor or admin role in this project","Retry after checking project access"]));await t.from("change_history").insert({item_id:a.id,project_id:e.project_id,changed_by:r,event_type:"updated",old_data:{approved:!0===l.approved,approved_at:l.approved_at??null,approved_by:l.approved_by??null,approved_note:l.approved_note??""},new_data:{approved:!0,approved_at:_,approved_by:s,approved_note:u},actor:Mc(),session_id:Hc(),reason:`${e.row_id} approved by ${s} via atoms_approve_row`}).then(({error:e})=>{e&&process.stderr.write(`[atoms-mcp] Warning: change_history log failed: ${e.message}\n`)});const g=Xo(i);if(g){const a=await pi(t,e.project_id,g);if(a){const{revoked:i,parentProps:n}=qt(a.data);if(i){const i={...a.data,analysis_props:n},{error:s}=await t.rpc("save_item_with_revision",{p_item_id:a.id,p_project_id:e.project_id,p_type:a.type,p_title:a.title,p_data:i,p_user_id:r,p_event_source:"mcp",p_event_session_id:Hc()??null,p_diff_summary:null,p_expected_updated_at:a.updated_at});s&&process.stderr.write(`[atoms-mcp] Warning: acceptance revoke save failed for '${a.id}': ${s.message}\n`)}}}return ei(qa({row_id:a.id,subtype:n,scheme:c,approved:!0,approved_by:s,approved_at:_,approved_note:u,approver_role:o,computed_level:d,cleared_markers:f}))}catch(e){return ti(Ja(e))}}E(Vo,{approveRowHandler:()=>_c,createAnalysisHandler:()=>ac,fillRowHandler:()=>nc,getAnalysisHandler:()=>cc,getGapsHandler:()=>dc,linkMitigationHandler:()=>lc,proposeRowsHandler:()=>mc,registerFunctionTags:()=>Ko,suggestFieldHandler:()=>uc});var fc=A({"src/tools/analysis.ts"(){Le(),wi(),Dc(),ai(),Aa(),Wo(),Er(),Fo=new Set(["safety-goal","failure-mode"])}}),hc={};async function yc(e,t){const{data:r,error:a}=await e.from("metadata").select("scenario_catalogs").eq("project_id",t).maybeSingle();if(a)throw new Error(a.message);const i=No(r?.scenario_catalogs??[]);return i.ok?i.value:[]}async function gc(e){try{const t=await qe(),r=Math.min(Math.max(e.limit??50,1),200),a=Math.max(e.offset??0,0),i=await yc(t,e.project_id);return ei(qa(i.slice(a,a+r),za(i.length,r,a)))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}}async function wc(e){try{const r=await qe();Me();try{await De(r,e.project_id)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message)return ti(Ma("Viewer"));throw e}if("string"!=typeof(t=e.op)||!Po.includes(t))return ti(Ta("op must be one of create_catalog, rename_catalog, delete_catalog, add_entry, edit_entry, remove_entry, tag_entry, accept_catalog."));const a=e.op;if("create_catalog"===a&&(await yc(r,e.project_id)).length>=Do)return ti(Ta(`This project already carries the maximum of ${Do} catalogs.`));const i={};"create_catalog"!==a||e.catalog_id?void 0!==e.catalog_id&&(i.catalog_id=e.catalog_id):i.catalog_id=`cat-${crypto.randomUUID().replace(/-/g,"").slice(0,12)}`,void 0!==e.name&&(i.name=Ka(e.name)),void 0!==e.entry_id&&(i.entry_id=e.entry_id),void 0!==e.text&&(i.text=Ka(e.text)),void 0!==e.function_tags&&(i.function_tags=e.function_tags),void 0!==e.by&&(i.by=e.by),void 0!==e.on&&(i.on=e.on);const{data:n,error:s}=await r.rpc("mutate_project_scenario_catalogs",{p_project_id:e.project_id,p_op:a,p_payload:i});return s?ti(Ta(s.message)):ei(qa({scenario_catalogs:n?.scenario_catalogs??[]}))}catch(e){return e instanceof Error&&e.message.includes("Not authenticated")?ti(Da()):ti(Ga(e instanceof Error?e.message:String(e)))}var t}E(hc,{listScenarioCatalogsHandler:()=>gc,mutateScenarioCatalogHandler:()=>wc});var vc,bc,jc,kc,Sc,xc,Ac,Ec,Ic,Rc,Oc=A({"src/tools/catalogs.ts"(){Le(),ai(),Wo()}}),qc={};function zc(e){F(e.toolName)&&function(e,t){const r=`ui://${t.toolName}/${t.htmlFile}`,a=function(e,t){const r=s.fileURLToPath("undefined"==typeof document?require("url").pathToFileURL(__filename).href:m&&"SCRIPT"===m.tagName.toUpperCase()&&m.src||new URL("index.cjs",document.baseURI).href),a=j.default.dirname(r);return j.default.resolve(a,"apps","src","apps",e,t)}(t.appName,t.htmlFile),i=[...ri.resourceDomains,...t.extraResourceDomains??[]];c.registerAppTool(e,t.toolName,{title:t.title,description:t.description,inputSchema:t.inputSchema,annotations:t.annotations,_meta:{ui:{resourceUri:r}}},t.handler),c.registerAppResource(e,`${t.title} UI`,r,{mimeType:c.RESOURCE_MIME_TYPE},async()=>{const e=await b.default.readFile(a,"utf-8");return{contents:[{uri:r,mimeType:c.RESOURCE_MIME_TYPE,text:e,_meta:{ui:{csp:{resourceDomains:i}}}}]}})}(kc,e)}function $c(e,t){return async r=>{const a=O(C(),e);if(!a.allowed)return ti($a(`Tool ${e} is blocked by org policy.`,[z(a.reason),"Contact your org admin to adjust the MCP access policy."]));try{!function(e){const t=st();if(!t)return;if("object"!=typeof e||null===e)return;const r=e.project_id;if("string"==typeof r&&r!==t)throw new at(t,r)}(r)}catch(e){if(e instanceof at)return ti(Ca(e.expectedProjectId));throw e}{const e=C(),t=r?.project_id;if(null!==e.project_scope&&"string"==typeof t&&!e.project_scope.includes(t))return ti($a("Tool call rejected: project_id is outside your org's MCP project scope.",[`Allowed projects: ${e.project_scope.length} project${1===e.project_scope.length?"":"s"} configured by your admin`,"Use atoms_list_projects to see which projects you can access in this session"]))}let i,n;try{n=await qe(),i=Me()}catch{return t(r)}const s=await async function(e,t){const r=parseInt(process.env.ATOMS_RATE_LIMIT_RPM??"",10)||Xe,a=new Date,i=new Date(a.getTime()-et);try{const{count:n,error:s}=await t.from("mcp_rate_limits").select("id",{count:"exact",head:!0}).eq("user_id",e).gte("requested_at",i.toISOString());if(s)throw new Error(s.message);if((n??0)>=r){const{data:r,error:n}=await t.from("mcp_rate_limits").select("requested_at").eq("user_id",e).gte("requested_at",i.toISOString()).order("requested_at",{ascending:!0}).limit(1).maybeSingle();if(n)throw new Error(n.message);const s=(r?new Date(r.requested_at).getTime():a.getTime())+et-a.getTime();return{allowed:!1,retryAfterSeconds:Math.max(1,Math.ceil(s/1e3))}}t.from("mcp_rate_limits").insert({user_id:e,requested_at:a.toISOString()}).then(({error:e})=>{e&&process.stderr.write(`[rate-limiter] insert failed: ${e.message}\n`)});const o=new Date(a.getTime()-2*et);return t.from("mcp_rate_limits").delete().eq("user_id",e).lt("requested_at",o.toISOString()).then(()=>{}),{allowed:!0}}catch(e){return process.stderr.write(`[rate-limiter] DB check failed (${e instanceof Error?e.message:String(e)}), falling back to allow.\n`),{allowed:!0}}}(i,n);if(!s.allowed)return ti(Ua(s.retryAfterSeconds));if("1"===process.env.ATOMS_MCP_LOCKDOWN||"true"===process.env.ATOMS_MCP_LOCKDOWN){const e=r?.project_id;if("string"==typeof e)try{const{requireWriteAccess:t}=await Promise.resolve().then(()=>(Le(),Oe));await t(n,e)}catch(e){if(e instanceof Error&&"VIEWER_ROLE"===e.message){const{accessDeniedError:e}=await Promise.resolve().then(()=>(ai(),Ia));return ti(e("Viewer (lockdown mode: read access requires editor or admin)"))}}}const o=function(){const e=performance.now();return()=>Math.round(performance.now()-e)}();let c,l="success";try{const e=await t(r),a=e;if(!0===a?.isError){l="error";const e=a?.content;if(e?.[0]?.text)try{c=JSON.parse(e[0].text).message}catch{}}return e}catch(e){throw l="error",c=e instanceof Error?e.message:String(e),e}finally{const t={tool_name:e,params:r,status:l,duration_ms:o(),error_msg:c,project_id:r.project_id,session_id:xc,client_name:Ac};try{!function(e,t,r){const a=Ze(r.params);e.from("mcp_audit_log").insert({user_id:t,tool_name:r.tool_name,params:a,status:r.status,duration_ms:r.duration_ms,error_msg:r.error_msg??null,project_id:r.project_id??null,session_id:r.session_id??null,client_name:r.client_name??null}).then(({error:e})=>{e&&process.stderr.write(`[audit] Failed to log: ${e.message}\n`)})}(await qe(),i,t)}catch{}}}}function Hc(){return xc}function Mc(){return Ec}function Tc(){return Ac}E(qc,{ATOMS_MCP_SERVER_VERSION:()=>jc,getMcpActor:()=>Mc,getMcpClientName:()=>Tc,getMcpSessionId:()=>Hc,server:()=>kc});var Dc=A({"src/server.ts"(){it(),nt(),B(),ct(),U(),D(),gt(),wt(),Aa(),Le(),ai(),ii(),vc=d.createRequire("undefined"==typeof document?require("url").pathToFileURL(__filename).href:m&&"SCRIPT"===m.tagName.toUpperCase()&&m.src||new URL("index.cjs",document.baseURI).href),bc=vc("../package.json"),jc=bc.version,kc=new l.McpServer({name:"atoms-mcp-server",version:jc}),Sc=(e,t,r)=>{if(F(e))return kc.registerTool(e,t,r)},xc=n.randomUUID(),Ac=lt(process.env.ATOMS_CLIENT_NAME),Ec=function(){const e=lt(Ac);return"cursor"===e?"mcp_cursor":"copilot"===e?"mcp_copilot":"mcp_claude"}(),Sc("atoms_status",{title:"ATOMS MCP Health Check",description:'Check the health and authentication status of the ATOMS MCP server.\n\nCall this FIRST if other atoms tools fail or return auth errors.\nThis tool works WITHOUT authentication — it checks whether credentials\nexist and are valid, and tells the user exactly what to do if not.\n\nArgs: None\n\nReturns:\n { status: "authenticated"|"not_authenticated"|"expired", email, message, next_steps }\n\nExamples:\n - "Is ATOMS MCP working?" → atoms_status()\n - "Why are atoms tools failing?" → atoms_status()',inputSchema:{},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},async()=>{try{const{readCredentials:e,isTokenValid:t}=await Promise.resolve().then(()=>(oe(),J)),r=await e();if(!r)return{content:[{type:"text",text:JSON.stringify({status:"not_authenticated",message:"No ATOMS credentials found. You need to login first.",next_steps:["1. Open a terminal on your machine","2. Run: npx @atoms-tech/atoms-mcp login","3. Complete the login in your browser","4. Restart Claude Desktop (quit and reopen)","5. Try your query again"]},null,2)}]};if(!t(r))try{const{getValidToken:e}=await Promise.resolve().then(()=>(Re(),ve)),{email:t}=await e(),{invalidateClient:r}=await Promise.resolve().then(()=>(Le(),Oe));return r(),{content:[{type:"text",text:JSON.stringify({status:"authenticated",email:t,message:"Token was expired but has been refreshed. You're good to go!"},null,2)}]}}catch{return{content:[{type:"text",text:JSON.stringify({status:"expired",email:r.user_email??"unknown",message:"Your session has expired and could not be refreshed.",next_steps:["1. Open a terminal on your machine","2. Run: npx @atoms-tech/atoms-mcp logout","3. Run: npx @atoms-tech/atoms-mcp login","4. Complete the login in your browser","5. Restart Claude Desktop (quit and reopen)"]},null,2)}]}}return{content:[{type:"text",text:JSON.stringify({status:"authenticated",email:r.user_email??"unknown",message:"ATOMS MCP is connected and authenticated. All tools are ready."},null,2)}]}}catch(e){return{content:[{type:"text",text:JSON.stringify({status:"error",message:`Health check failed: ${e instanceof Error?e.message:String(e)}`,next_steps:["1. Open a terminal","2. Run: npx @atoms-tech/atoms-mcp login","3. Restart Claude Desktop"]},null,2)}]}}}),Sc("atoms_list_projects",{title:"List ATOMS Projects",description:'List all projects the authenticated user has access to.\n\nThis is the ENTRY POINT tool. Call this first to discover project IDs,\nthen use those IDs with all other tools.\n\nArgs: None\n\nReturns:\n { status: "success", data: [{ id, name, description, org_id, created_at }] }\n\nExamples:\n - "What projects do I have?" → atoms_list_projects()\n - "Show my projects" → atoms_list_projects()\n\nWorkflow:\n atoms_list_projects → atoms_list_items(project_id) → atoms_get_item(project_id, item_id)',inputSchema:{},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_list_projects",async()=>{const{listProjectsHandler:e}=await Promise.resolve().then(()=>(ji(),vi));return e()})),Sc("atoms_list_items",{title:"List ATOMS Items",description:'List items in an ATOMS project with optional type/domain/level filters.\n\nThis is the primary ENTRY POINT for discovering items. Use it to browse\nproject contents before drilling into specific items with atoms_get_item.\n\nArgs:\n - project_id (string, UUID): The project to list items from\n - type (string, optional): Filter: requirement|test-case|note|table\n - domain (string, optional): Filter by domain tag\n - level (string, optional): Filter by level (System, Subsystem, Component)\n - limit (number, optional): Max results, 1-200 (default 50)\n - offset (number, optional): Pagination offset (default 0)\n\nReturns:\n { status: "success", data: [{ id, title, type, status, domains, level }], meta: { total_count, limit, offset, has_more } }\n\nExamples:\n - "Show me all requirements" → atoms_list_items(project_id, type="requirement")\n - "What test cases exist?" → atoms_list_items(project_id, type="test-case")\n\nErrors:\n - "Project not found" → verify project_id\n - "Access denied" → check org membership',inputSchema:{project_id:o.z.string().uuid("Must be a valid project UUID").describe("UUID of the project"),type:o.z.enum(["requirement","test-case","note","table","analysis"]).optional().describe("Filter by item type"),domain:o.z.string().max(64).optional().describe("Filter by domain tag (e.g., 'Safety', 'Performance')"),level:o.z.string().max(32).optional().describe("Filter by level (System, Subsystem, Component)"),limit:o.z.number().int().min(1).max(200).default(50).describe("Max results (default 50, max 200)"),offset:o.z.number().int().min(0).default(0).describe("Pagination offset (default 0)"),baseline_id:o.z.string().uuid().optional().describe("Optional baseline UUID. Returns items as they were at that baseline's moment. Mutually exclusive with as_of."),as_of:o.z.string().datetime().optional().describe("Optional ISO 8601 timestamp. Returns items as they were at that moment. Mutually exclusive with baseline_id.")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_list_items",async e=>{const{listItemsHandler:t}=await Promise.resolve().then(()=>(Ai(),ki));return t(e)})),Sc("atoms_get_item",{title:"Get ATOMS Item Details",description:'Get full details of a single item including relationships, test history, and ownership.\n\nUse after atoms_list_items or atoms_search to drill into a specific item.\n\nArgs:\n - project_id (string, UUID): The project containing the item\n - item_id (string): Item ID (e.g., "REQ-001", "TC-050")\n\nReturns:\n Full WorkItem with relationships, test runs, ownership, metadata.\n\nExamples:\n - "Show me requirement REQ-001" → atoms_get_item(project_id, "REQ-001")\n - "Get test case details" → atoms_get_item(project_id, "TC-050")',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().min(1).max(20).describe("Item ID (e.g., REQ-001)"),baseline_id:o.z.string().uuid().optional().describe("Optional baseline UUID. Returns the item as it was at that baseline's moment. Mutually exclusive with as_of."),as_of:o.z.string().datetime().optional().describe("Optional ISO 8601 timestamp. Returns the item as it was at that moment. Mutually exclusive with baseline_id.")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_get_item",async e=>{const{getItemHandler:t}=await Promise.resolve().then(()=>(Oi(),Ei));return t(e)})),Sc("atoms_search",{title:"Search ATOMS Items",description:'Full-text search across items in a project. Searches title, body, and summary.\n\nAlternative entry point to atoms_list_items when you know what you\'re looking for.\n\nArgs:\n - project_id (string, UUID): The project to search in\n - query (string): Search text (max 1000 chars)\n - type (string, optional): Filter by type\n - asset (string, optional): Filter to items with at least one run on the given asset\n (VIN, module, lot, ...). Case-sensitive exact match.\n - limit (number, optional): Max results, 1-100 (default 25)\n\nReturns:\n Matching items with @basic fields, ranked by relevance.\n\nExamples:\n - "Find braking requirements" → atoms_search(project_id, "braking system")\n - "Search for safety tests" → atoms_search(project_id, "safety", type="test-case")\n - "Tests that ran on this VIN" → atoms_search(project_id, "", asset="WEV...0005")',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),query:o.z.string().min(1).max(1e3).describe("Search text"),type:o.z.enum(["requirement","test-case","note","table","analysis"]).optional().describe("Filter by item type"),asset:o.z.string().max(200).optional().describe("Filter to items with at least one run on this asset (case-sensitive exact)"),limit:o.z.number().int().min(1).max(100).default(25).describe("Max results (default 25, max 100)"),baseline_id:o.z.string().uuid().optional().describe("Optional baseline UUID. Searches within the project state at that baseline's moment.")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_search",async e=>{const{searchHandler:t}=await Promise.resolve().then(()=>($i(),qi));return t(e)})),Sc("atoms_semantic_search",{title:"Semantic Search ATOMS Items",description:'Meaning-based search across items using pgvector embeddings (GTE-small, 384d).\n\nUse when atoms_search (exact/substring) misses conceptually related items.\nBest for natural-language concept queries; atoms_search is better for exact phrases or IDs.\n\nArgs:\n - project_id (string, UUID): The project to search in\n - query (string): Natural-language concept query (max 1000 chars)\n - type (string, optional): Filter by type\n - limit (number, optional): Max results, 1-50 (default 10)\n\nReturns:\n Semantically similar items ranked by cosine similarity.\n\nExamples:\n - "Find braking-related requirements" → atoms_semantic_search(project_id, "braking deceleration stopping distance")\n - "Safety-related test cases" → atoms_semantic_search(project_id, "hazard risk mitigation safety", type="test-case")',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),query:o.z.string().min(1).max(1e3).describe("Natural-language concept query"),type:o.z.enum(["requirement","test-case","note","table","analysis"]).optional().describe("Filter by item type"),limit:o.z.number().int().min(1).max(50).default(10).describe("Max results (default 10, max 50)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_semantic_search",async e=>{const{semanticSearchHandler:t}=await Promise.resolve().then(()=>(Ti(),Hi));return t(e)})),zc({appName:"coverage",htmlFile:"coverage-app.html",toolName:"atoms_get_coverage",title:"Get Test Coverage Report",description:'Find requirements without linked test cases (coverage gaps).\n\nEssential for compliance reporting in safety-critical industries.\n\nIn MCP App hosts (Claude, ChatGPT), renders a visual coverage heatmap.\nFalls back to JSON for non-UI clients.\n\nArgs:\n - project_id (string, UUID): The project to analyze\n - domain (string, optional): Filter by domain\n - level (string, optional): Filter by level\n - baseline_id (string, UUID, optional): coverage at that baseline\'s moment\n - as_of (string ISO 8601, optional): coverage at that moment\n baseline_id and as_of are mutually exclusive.\n\nReturns:\n { covered, uncovered, total, coverage_percent, uncovered_items: [...] }\n\nExamples:\n - "What\'s our test coverage?" → atoms_get_coverage(project_id)\n - "Safety coverage gaps?" → atoms_get_coverage(project_id, domain="Safety")\n - "Coverage at PDR submission" → atoms_get_coverage(project_id, baseline_id="...")',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),domain:o.z.string().max(64).optional().describe("Filter by domain tag"),level:o.z.string().max(32).optional().describe("Filter by level"),baseline_id:o.z.string().uuid().optional().describe("Compute coverage as of this baseline's snapshot moment"),as_of:o.z.string().datetime().optional().describe("Compute coverage as of this ISO 8601 moment")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:$c("atoms_get_coverage",async e=>{const{getCoverageHandler:t}=await Promise.resolve().then(()=>(Ui(),Di));return t(e)})}),Sc("atoms_verification_status",{title:"Get Release Verification Status",description:'Release-bound verification state per requirement, matching the in-app projection.\n\nEach requirement\'s state is derived from the runs bound to a formal Release:\nverified, failing, incomplete, or not-run, plus a stale flag and the\n(points_run / points_total) counts for its parameterized test points.\n\nArgs:\n - project_id (string, UUID): The project to report on\n - release_id (string, UUID, optional): A formal Release (a baselines row with\n a release_version). Defaults to the project\'s CURRENT Release\n (release_status = \'released\'). Errors if the project has none.\n - include_gaps (boolean, optional, default false): Also return the not-run\n (test x unit x value) points for the Release.\n\nReturns:\n { release: {id, version, status}, readiness: {...roll-up counts...},\n requirements: [{requirement_id, state, stale, points_total, points_run}],\n gaps?: [{tc_id, asset, point_key}] }\n An empty requirements array with a "hint" field means the projection has not\n been rebuilt for this project yet, NOT that everything is unverified.\n\nExamples:\n - "Are we ready to ship 2.0?" → atoms_verification_status(project_id)\n - "What\'s still not run for release R?" → atoms_verification_status(project_id, release_id="...", include_gaps=true)',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),release_id:o.z.string().uuid().optional().describe("Formal Release (baselines row) UUID; defaults to the project's current Release"),include_gaps:o.z.boolean().default(!1).describe("Include the not-run (test x unit x value) points for the Release")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_verification_status",async e=>{const{verificationStatusHandler:t}=await Promise.resolve().then(()=>(Fi(),Li));return t(e)})),Sc("atoms_get_history",{title:"Get Item Change History",description:"Audit trail for an item — who changed what, when, and whether it was human or AI.\n\nShows actor attribution (user vs mcp_<client>) and session grouping.\n\nArgs:\n - project_id (string, UUID): The project containing the item\n - item_id (string): Item ID\n - limit (number, optional): Max entries (default 20, max 100)\n\nReturns:\n Change entries with actor, session_id, event_type, changed_at, fields_changed.",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().min(1).max(20).describe("Item ID"),limit:o.z.number().int().min(1).max(100).default(20).describe("Max entries (default 20)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_get_history",async e=>{const{getHistoryHandler:t}=await Promise.resolve().then(()=>(Qi(),Bi));return t(e)})),zc({appName:"mermaid",htmlFile:"mermaid-app.html",toolName:"atoms_export_mermaid",title:"Export Mermaid Diagram",description:"Generate a Mermaid diagram of the requirement/test hierarchy.\n\nOutput is paste-ready for markdown docs. Shows parent-child relationships\nand requirement-to-test-case verification links.\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive pan/zoom diagram.\nFalls back to Mermaid text for non-UI clients.\n\nArgs:\n - project_id (string, UUID): The project to visualize\n - root_item_id (string, optional): Start node (default: all roots)\n - depth (number, optional): Max depth (default 3, max 10)\n - include_tests (boolean, optional): Show test case links (default true)\n - baseline_id (string, UUID, optional): render the graph at that baseline's moment\n - as_of (string ISO 8601, optional): render the graph at that moment\n baseline_id and as_of are mutually exclusive.\n\nReturns:\n Mermaid graph string.",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),root_item_id:o.z.string().max(20).optional().describe("Root item ID to start from"),depth:o.z.number().int().min(1).max(10).default(3).describe("Max depth"),include_tests:o.z.boolean().default(!0).describe("Include test case links"),baseline_id:o.z.string().uuid().optional().describe("Render the graph as of this baseline's snapshot moment"),as_of:o.z.string().datetime().optional().describe("Render the graph as of this ISO 8601 moment")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:$c("atoms_export_mermaid",async e=>{const{exportMermaidHandler:t}=await Promise.resolve().then(()=>(nn(),en));return t(e)})}),Sc("atoms_create_item",{title:"Create ATOMS Item",description:'Create a new requirement, test case, or note in a project.\n\nFor a hazard analysis, HARA, DFMEA or FMEA, use atoms_create_analysis instead (it creates the analysis parent and its rows). This tool does not create analysis items; do not model a hazard analysis as notes with custom fields.\n\nRequires editor or admin role. Changes are logged with AI actor attribution.\n\nArgs:\n - project_id (string, UUID): Target project\n - type (string): requirement|test-case|note\n - req_type (string, optional): requirement classification (functional|interface|performance|safety)\n - title (string): Item title (max 500 chars)\n - body (string, optional): Rich text body\n - summary (string, optional): Brief summary\n - domains (string[], optional): Domain tags (a tag flagged as a function is not a domain and is rejected)\n - level (string, optional): System|Subsystem|Component\n - parent_ids (string[], optional): Parent item IDs to link\n - fields (object, optional): Project-field values, e.g. {"Verification Method": "Design Review"}. Keys must be defined via atoms_create_field first and values must match each field type.\n\nReturns:\n Created item with generated ID (e.g., "REQ-058")\n\nSide effects:\n - Logs to change_history with actor=\'mcp_<client>\'\n - Creates relationships if parent_ids provided',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the target project"),type:o.z.enum(["requirement","test-case","note"]).describe("Item type"),title:o.z.string().min(1).max(500).describe("Item title"),req_type:o.z.enum(["functional","interface","performance","safety","safety-goal","failure-mode"]).optional().describe("Requirement classification (functional|interface|performance|safety). safety-goal and failure-mode are analysis rows created by atoms_create_analysis, not here."),body:o.z.string().max(5e4).optional().describe("Rich text body"),summary:o.z.string().max(2e3).optional().describe("Brief summary"),domains:o.z.array(o.z.string().max(64)).max(20).optional().describe("Domain tags"),level:o.z.string().max(32).optional().describe("System|Subsystem|Component"),parent_ids:o.z.array(o.z.string().max(20)).max(50).optional().describe("Parent item IDs to link"),fields:o.z.record(o.z.string().max(60),o.z.string().max(1e4)).optional().describe("Project-field values keyed by defined field name"),echo:o.z.boolean().optional().describe("false → lean {id,type} response, saves ~580 tokens")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$c("atoms_create_item",async e=>{const{createItemHandler:t}=await Promise.resolve().then(()=>(Kn(),Gn));return t(e)})),Ic={steps:o.z.array(o.z.object({id:o.z.string().describe("Stable step id within the item"),instruction:o.z.string().describe("What the tester does"),test_data:o.z.string().optional().describe("Optional test data for the step"),expected:o.z.string().describe("Expected result")}).passthrough()).optional().describe("Test-case script steps: array of {id, instruction, test_data?, expected}. Max 50 steps, enforced by the DB trigger."),params:o.z.array(o.z.object({name:o.z.string().describe("Parameter name; steps reference it as {name}"),unit:o.z.string().optional().describe("Optional unit (e.g. km/h, V, m)"),values:o.z.array(o.z.union([o.z.string(),o.z.number()])).describe("Value set the test sweeps")}).passthrough()).optional().describe("Matrix parameters: array of {name, unit, values[]}. Matrix points (product of value-set sizes) capped at 64 by the DB trigger.")},Sc("atoms_update_item",{title:"Update ATOMS Item",description:'Update an existing item\'s fields. Only provided fields are changed.\n\nRequires editor or admin role. Logs old/new data diff to change history.\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): Item to update\n - title, body, summary, domains, level, status, req_type: Fields to update (all optional) (a tag flagged as a function is not a domain and is rejected)\n - fields (object, optional): Project-field values, e.g. {"Verification Method": "Design Review"}. Keys must be defined via atoms_create_field; merged per key, "" clears a key; non-empty values must match each field type\n - steps (array, optional): Test-case script steps [{id, instruction, test_data?, expected}]. Max 50 steps (DB-enforced)\n - params (array, optional): Matrix parameters [{name, unit, values[]}]. Matrix points capped at 64 (DB-enforced)\n - expected_updated_at (string, optional): Pass metadata.updated_at from atoms_get_item to reject stale writes\n\nReturns:\n Updated item.',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().min(1).max(20).optional().describe("Item ID (single form)"),title:o.z.string().min(1).max(500).optional().describe("New title"),body:o.z.string().max(5e4).optional().describe("New body"),summary:o.z.string().max(2e3).optional().describe("New summary"),domains:o.z.array(o.z.string().max(64)).max(20).optional().describe("New domain tags (a tag flagged as a function is not a domain and is rejected)"),level:o.z.string().max(32).optional().describe("New level"),status:o.z.enum(["passed","failed","blocked","not-run"]).optional().describe("Test case status"),req_type:o.z.enum(["functional","interface","performance","safety","safety-goal","failure-mode"]).optional().describe("Requirement classification (functional|interface|performance|safety). safety-goal and failure-mode are analysis rows created by atoms_create_analysis, not here."),fields:o.z.record(o.z.string().max(60),o.z.string().max(1e4)).optional().describe('Project-field values keyed by defined field name; "" clears a key'),...Ic,expected_updated_at:dt.optional().describe("metadata.updated_at value from atoms_get_item; rejects stale writes"),echo:o.z.boolean().optional().describe("false → lean {id,updated_fields} response, saves ~580 tokens"),reason:o.z.string().max(500).optional().describe("Change rationale, persisted in audit history"),updates:o.z.array(o.z.object({item_id:o.z.string().min(1).max(20),title:o.z.string().min(1).max(500).optional(),body:o.z.string().max(5e4).optional(),summary:o.z.string().max(2e3).optional(),domains:o.z.array(o.z.string().max(64)).max(20).optional(),level:o.z.string().max(32).optional(),status:o.z.enum(["passed","failed","blocked","not-run"]).optional(),req_type:o.z.enum(["functional","interface","performance","safety","safety-goal","failure-mode"]).optional(),fields:o.z.record(o.z.string().max(60),o.z.string().max(1e4)).optional(),...Ic,expected_updated_at:dt.optional()})).optional().describe("Array form: update many items in one call"),idempotency_key:o.z.string().max(128).optional().describe("Safe retry key (24h TTL)"),continue_on_error:o.z.boolean().optional().describe("Allow partial success (default false = atomic)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_update_item",async e=>{const{updateItemHandler:t}=await Promise.resolve().then(()=>(is(),ts));return t(e)})),Sc("atoms_delete_item",{title:"Delete ATOMS Item",description:'Soft-delete an item (sets deleted_at, preserves for audit trail).\n\nRequires editor or admin role. The item is never truly removed.\n\nWhen ATOMS_MCP_REQUIRE_CONFIRMATION is set on the server, this tool uses a\ntwo-step flow: the first call returns a preview + confirmation_token; the\nsecond call (with the token) executes. Tokens expire in 60s and are bound\nto (tool, project_id, item_id).\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): Item to delete\n - confirmation_token (string, optional): Token from a prior preview call\n\nReturns:\n Confirmation with deleted item summary, or { status: "confirmation_required",\n preview, confirmation_token } when the confirmation gate is active.',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().min(1).max(20).describe("Item ID to soft-delete"),confirmation_token:o.z.string().max(500).optional().describe("Token from a prior preview call (only used when ATOMS_MCP_REQUIRE_CONFIRMATION is set)")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!1}},$c("atoms_delete_item",async e=>{const{deleteItemHandler:t}=await Promise.resolve().then(()=>(os(),ns));return t(e)})),Sc("atoms_restore_item",{title:"Restore ATOMS Item",description:'Undo a soft-delete — restores an item that was previously deleted with atoms_delete_item.\n\nOnly works on items that have been soft-deleted (still in the database with deleted_at set).\nHas no effect if the item is not deleted. Logs the restoration to change_history.\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): ID of the deleted item to restore\n\nReturns:\n Confirmation with the restored item\'s title and type.\n\nExamples:\n - "Restore the deleted requirement REQ-042" → atoms_restore_item(project_id, "REQ-042")',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().max(256).describe("ID of the soft-deleted item to restore")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_restore_item",async e=>{const{restoreItemHandler:t}=await Promise.resolve().then(()=>(ms(),cs));return t(e)})),Sc("atoms_link_items",{title:"Link ATOMS Items",description:"Add or remove relationships between items.\n\nSingle-op form:\n - project_id, from_id, to_id, type (parent|child|related|verifies|verified_by|affects|affected_by), action (add|remove)\n - affects/affected_by (REQ-ANL-242): links a security risk analysis row (CYB-n) to an ISO 14971\n risk row (RSK-n) only; nothing is derived onto the risk row's own ratings. Any other pair is\n refused by name.\n - expected_from_updated_at? / expected_to_updated_at?: metadata.updated_at values from atoms_get_item to reject stale link commands\n - echo? false → lean {action,from_id,to_id} response\n - reason?: persisted in audit history\n\nArray form (batch link ops, same tool, much faster for restructures):\n - project_id, operations: [{action, from_id, to_id, type, expected_from_updated_at?, expected_to_updated_at?}]\n - Validated as a whole (DAG cycle check) before any write, then applied one at a time (removes before adds), NOT in a single transaction: a mid-batch failure leaves earlier operations committed and names which ones\n - reason?, idempotency_key? supported",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),from_id:o.z.string().min(1).max(20).optional().describe("Source item ID (single form)"),to_id:o.z.string().min(1).max(20).optional().describe("Target item ID (single form)"),type:o.z.enum(["parent","child","related","verifies","verified_by","affects","affected_by"]).optional().describe("Relationship type"),action:o.z.enum(["add","remove"]).optional().describe("Add or remove"),expected_from_updated_at:dt.optional().describe("metadata.updated_at for from_id from atoms_get_item"),expected_to_updated_at:dt.optional().describe("metadata.updated_at for to_id from atoms_get_item"),echo:o.z.boolean().optional().describe("false → lean response"),reason:o.z.string().max(500).optional().describe("Change rationale, persisted in audit history"),operations:o.z.array(o.z.object({action:o.z.enum(["add","remove"]),from_id:o.z.string().min(1).max(20),to_id:o.z.string().min(1).max(20),type:o.z.enum(["parent","child","related","verifies","verified_by","affects","affected_by"]),expected_from_updated_at:dt.optional(),expected_to_updated_at:dt.optional()})).optional().describe("Array form: batch relationship ops, DAG-validated as a whole then applied in sequence (not one transaction); a mid-batch failure reports which ops committed."),idempotency_key:o.z.string().max(128).optional().describe("Safe retry key (24h TTL)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_link_items",async e=>{const{linkItemsHandler:t}=await Promise.resolve().then(()=>(hs(),us));return t(e)})),zc({appName:"import",htmlFile:"import-app.html",toolName:"atoms_bulk_import",title:"Bulk Import ATOMS Items",description:"Bulk create multiple items in a single tool call. Essential for AI agents\nthat generate 20+ requirements at once.\n\nChecks write access once, generates sequential IDs, batch inserts all items,\nand reports per-item errors without aborting the entire batch.\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive results table.\nFalls back to JSON for non-UI clients.\n\nArgs:\n - project_id (string, UUID): Target project\n - items (array): Up to 100 items to create, each with:\n - type (string): requirement|test-case|note\n - title (string): Item title (max 500 chars)\n - body (string, optional): Rich text body\n - summary (string, optional): Brief summary\n - domains (string[], optional): Domain tags (a tag flagged as a function is not a domain and is rejected)\n - level (string, optional): System|Subsystem|Component\n - parent_id (string, optional): Parent item ID to link\n\nReturns:\n { created: number, items: [{ id, title, type }], errors: [] }\n\nLimits:\n - Maximum 100 items per call\n - If any item fails, others still succeed — errors reported separately\n\nSide effects:\n - Logs to change_history with actor='mcp_<client>' for each created item\n - Creates parent relationships for items with parent_id",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the target project"),items:o.z.array(o.z.object({type:o.z.enum(["requirement","test-case","note"]).describe("Item type"),title:o.z.string().min(1).max(500).describe("Item title"),body:o.z.string().max(5e4).optional().describe("Rich text body"),summary:o.z.string().max(2e3).optional().describe("Brief summary"),domains:o.z.array(o.z.string().max(64)).max(20).optional().describe("Domain tags"),level:o.z.string().max(32).optional().describe("System|Subsystem|Component"),parent_id:o.z.string().max(20).optional().describe("Parent item ID to link")})).min(1).max(100).describe("Items to create (1-100)"),confirmation_token:o.z.string().max(500).optional().describe("Token from a prior preview call (only used when ATOMS_MCP_REQUIRE_CONFIRMATION is set)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},handler:$c("atoms_bulk_import",async e=>{const{bulkImportHandler:t}=await Promise.resolve().then(()=>(bs(),gs));return t(e)})}),Rc={point:o.z.record(o.z.string(),o.z.union([o.z.string(),o.z.number()])).optional().describe('Matrix point this run executed, e.g. {"speed": 40}'),release_id:o.z.string().uuid().optional().describe("Formal Release to bind this run to (a baseline with a release_version)"),build:o.z.string().optional().describe("Free-text build label"),environment:o.z.string().optional().describe("Structured environment field, distinct from build"),tooling:o.z.string().optional().describe("Structured tooling field, distinct from build"),steps_rev:o.z.string().optional().describe("Item revision id the run's steps were pinned to"),step_results:o.z.array(o.z.object({step_id:o.z.string().describe("Step id from data.steps"),status:o.z.enum(["passed","failed","blocked","skipped"]).describe("Per-step status"),actual:o.z.string().optional().describe("Actual-result note"),evidence_url:o.z.string().optional().describe("Evidence URL (validated server-side)")}).passthrough()).optional().describe("Per-step results [{step_id, status, actual?, evidence_url?}]. When present the run verdict is derived server-side and the top-level result is ignored.")},Sc("atoms_record_test_result",{title:"Record Test Result",description:"Record a pass/fail/blocked result for a test case.\n\nAppends to canonical items.data.runs and projects test_results from that\nhistory. Use atoms_get_item to see all results.\n\nArgs:\n - project_id (string, UUID): Project containing the test case\n - item_id (string): Test case ID (e.g., \"TC-001\")\n - result (string): passed|failed|blocked|not-run\n - note (string, optional): Reason or comment for this result\n - asset (string, optional): Asset identifier this run was executed against\n (VIN, module ID, lot number, serial number, etc). Max 200 chars.\n Auto-trimmed; empty/whitespace is treated as omitted. If the asset\n name does not yet exist in the project, it is auto-registered.\n - point, release_id, build, environment, tooling, steps_rev (optional):\n run metadata. release_id must be a formal Release (a baseline with a\n release_version). All validated server-side.\n - step_results (array, optional): per-step outcomes for a stepped run.\n When provided, the verdict is derived server-side from the steps.\n\nReturns:\n Recorded result with timestamp. When asset is provided, also returns\n asset_id and asset_created.\n\nSide effects:\n - Appends to items.data.runs through save_item_with_revision\n - Projects public.test_results from items.data.runs\n - Inserts into assets table if asset name is new in this project\n - Logs to change_history with actor='mcp_<client>'",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().min(1).max(20).describe("Test case ID (e.g., TC-001)"),result:o.z.enum(["passed","failed","blocked","not-run"]).describe("Test result"),note:o.z.string().max(500).optional().describe("Reason or comment for this result"),asset:o.z.string().max(200).optional().describe("Asset identifier this run was executed against (VIN, module ID, lot, etc). Auto-registered if new."),...Rc},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$c("atoms_record_test_result",async e=>{const{recordTestResultHandler:t}=await Promise.resolve().then(()=>(Ss(),js));return t(e)})),Sc("atoms_record_test_results",{title:"Record Test Results (bulk)",description:'Record many runs on one test case in a single atomic call.\n\nOptimized for multi-asset campaigns. Each entry can have its own asset and\nresult. Asset lookup-or-create happens per entry; new asset names are\nauto-registered. Runs are appended to canonical items.data.runs, then\ntest_results is projected from that history. The whole call is one DB\ntransaction; any entry failure rolls back the rest.\n\nUse the singular atoms_record_test_result for one-off runs.\n\nArgs:\n - project_id (string, UUID): Project containing the test case\n - item_id (string): Test case ID (e.g., "TC-001")\n - results (array, 1-100 entries): Each entry has:\n - asset (string, optional): Asset identifier (VIN, module ID, lot)\n - result (string, required): passed|failed|blocked|not-run\n - note (string, optional): Run note\n - date (string, optional): ISO 8601 datetime, default now()\n - point, release_id, build, environment, tooling, steps_rev (optional):\n run metadata; release_id must be a formal Release. Server-validated.\n - step_results (array, optional): per-step outcomes; when present the\n verdict is derived server-side from the steps.\n\nReturns:\n Per-entry outcomes with asset_id, asset_created, run_at, ok flags.\n\nSide effects:\n - Appends to items.data.runs through save_item_with_revision\n - Projects public.test_results from items.data.runs\n - Inserts new rows into assets for any asset name not already registered\n - Logs a single change_history entry summarizing the batch',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().min(1).max(20).describe("Test case ID (e.g., TC-001)"),results:o.z.array(o.z.object({asset:o.z.string().max(200).optional().describe("Asset identifier this run was executed against. Auto-registered if new."),result:o.z.enum(["passed","failed","blocked","not-run"]).describe("Test result"),note:o.z.string().max(500).optional().describe("Run note"),date:o.z.string().optional().describe("ISO 8601 datetime (default: now())"),...Rc})).min(1).max(100).describe("1 to 100 result entries on this test case")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$c("atoms_record_test_results",async e=>{const{recordTestResultsBulkHandler:t}=await Promise.resolve().then(()=>(qs(),xs));return t(e)})),zc({appName:"trace",htmlFile:"trace-app.html",toolName:"atoms_trace",title:"Trace Item Relationships",description:'Walk the traceability graph from a starting item.\n\nAnswers questions like "which requirements does TC-003 verify?" or\n"if I change REQ-001, what\'s affected downstream?"\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive graph visualization.\nFalls back to flat list for non-UI clients.\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): Starting item ID\n - direction (string): upstream|downstream|both\n - upstream: follow parent/verifies links (dependencies)\n - downstream: follow child/verified_by links (dependents)\n - both: trace in both directions\n - depth (number, optional): Max traversal depth (default 5, max 10)\n - relationship_types (string[], optional): Filter to specific types (parent, child, related, verifies, verified_by, mitigated_by, mitigates)\n\nReturns:\n { root, direction, items: [{ id, title, type, relationship, depth }], total_count }\n\nExamples:\n - "What does TC-003 verify?" → atoms_trace(project_id, "TC-003", "upstream")\n - "What\'s affected by REQ-001?" → atoms_trace(project_id, "REQ-001", "downstream")',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().min(1).max(20).describe("Starting item ID"),direction:o.z.enum(["upstream","downstream","both"]).describe("Traversal direction"),depth:o.z.number().int().min(1).max(10).default(5).describe("Max traversal depth (default 5)"),relationship_types:o.z.array(o.z.enum(["parent","child","related","verifies","verified_by","mitigated_by","mitigates","affects","affected_by"])).optional().describe("Filter to specific relationship types")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:$c("atoms_trace",async e=>{const{traceHandler:t}=await Promise.resolve().then(()=>(Ts(),Hs));return t(e)})}),Sc("atoms_list_assets",{title:"List Project Assets",description:"List asset identifiers registered in a project.\n\nAssets are free-form per-instance IDs (VINs, module names, lot numbers,\nserial numbers) that test runs are tagged against. They are auto-registered\nthe first time they appear on a run via atoms_record_test_result.\n\nArgs:\n - project_id (string, UUID): Project to list assets for\n - include_archived (boolean, optional): Include archived assets (default false)\n\nReturns:\n { assets: [{ id, name, archived_at, created_at }], total }",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),include_archived:o.z.boolean().optional().describe("Include archived assets (default false)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_list_assets",async e=>{const{listAssetsHandler:t}=await Promise.resolve().then(()=>(Cs(),Ds));return t(e)})),Sc("atoms_list_variables",{title:"List Project Variables",description:"List all parameterized variables defined in a project.\n\nVariables are shared values (e.g., backup_camera_latency = 2s) that can be\nreferenced in requirement bodies as {variable_name}.\n\nArgs:\n - project_id (string, UUID): Project to list variables for\n - baseline_id (string, UUID, optional): list variables as they were at that baseline\n - as_of (string ISO 8601, optional): list variables as they were at that moment\n - limit (number, optional): max results (default 50, max 200)\n - offset (number, optional): pagination offset (default 0)\n\nReturns:\n { variables: [{ id, name, value, unit, description }], total }, meta: { total_count, limit, offset, has_more }",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),baseline_id:o.z.string().uuid().optional().describe("Optional baseline UUID for time-travel reads"),as_of:o.z.string().datetime().optional().describe("Optional ISO timestamp for time-travel reads"),limit:o.z.number().int().min(1).max(200).default(50).describe("Max results (default 50, max 200)"),offset:o.z.number().int().min(0).default(0).describe("Pagination offset (default 0)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_list_variables",async e=>{const{listVariablesHandler:t}=await Promise.resolve().then(()=>(Gs(),Us));return t(e)})),Sc("atoms_get_variable",{title:"Get Project Variable",description:'Get a specific variable by name, including which items reference it.\n\nArgs:\n - project_id (string, UUID): Project containing the variable\n - variable_name (string): Variable name (e.g., "backup_camera_latency")\n - baseline_id (string, UUID, optional): get the variable as it was at that baseline\n - as_of (string ISO 8601, optional): get the variable as it was at that moment\n\nReturns:\n { variable, referenced_by: [{ id, title, type }], reference_count }',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),variable_name:o.z.string().min(1).max(64).describe("Variable name"),baseline_id:o.z.string().uuid().optional().describe("Optional baseline UUID for time-travel reads"),as_of:o.z.string().datetime().optional().describe("Optional ISO timestamp for time-travel reads")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_get_variable",async e=>{const{getVariableHandler:t}=await Promise.resolve().then(()=>(Gs(),Us));return t(e)})),Sc("atoms_update_variable",{title:"Update Project Variable",description:"Update a variable's value, unit, or description.\n\nArgs:\n - project_id (string, UUID): Project containing the variable\n - variable_name (string): Variable name\n - value (string, optional): New value\n - unit (string, optional): Unit abbreviation (e.g., 's', 'Hz')\n - description (string, optional): Human-readable description\n\nReturns:\n { variable }",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),variable_name:o.z.string().min(1).max(64).describe("Variable name"),value:o.z.string().max(500).optional().describe("New value"),unit:o.z.string().max(32).optional().describe("Unit abbreviation"),description:o.z.string().max(2e3).optional().describe("Description")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_update_variable",async e=>{const{updateVariableHandler:t}=await Promise.resolve().then(()=>(Gs(),Us));return t(e)})),Sc("atoms_create_variable",{title:"Create Project Variable",description:'Create one or many parameterized variables for a project.\n\nSingle form: provide name + value.\nArray form: provide variables[] for bulk creation (one atomic INSERT).\n\nVariables can then be embedded in requirement bodies as {variable_name}.\n\nArgs (single form):\n - project_id (string, UUID): Project to add the variable to\n - name (string): Variable name, letters, digits, underscores; must start with letter/underscore\n - value (string): Initial value (e.g., "100", "true", "nominal")\n - unit (string, optional): Unit abbreviation (e.g., "Hz", "ms", "m/s²")\n - description (string, optional): Human-readable description\n - echo (bool, optional): false → lean {name} response (default true)\n\nArgs (array form):\n - project_id (string, UUID): Project UUID\n - variables[]: Array of { name, value, unit?, description? }\n - echo (bool, optional): false → lean {name} list (default true)\n - idempotency_key (string, optional): Dedup key\n\nReturns (single): { variable, message }\nReturns (array): { batch_id, created, variables, message }',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(64).optional().describe("Variable name (single form)"),value:o.z.string().min(1).max(500).optional().describe("Initial value (single form)"),unit:o.z.string().max(32).optional().describe("Unit abbreviation"),description:o.z.string().max(2e3).optional().describe("Description"),echo:o.z.boolean().optional().describe("false → lean response (default true)"),variables:o.z.array(o.z.object({name:o.z.string().min(1).max(64).describe("Variable name"),value:o.z.string().min(1).max(500).describe("Initial value"),unit:o.z.string().max(32).optional().describe("Unit abbreviation"),description:o.z.string().max(2e3).optional().describe("Description")})).optional().describe("Array form: batch variable creation. Atomic — all or none."),idempotency_key:o.z.string().max(128).optional().describe("Dedup key for retries")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$c("atoms_create_variable",async e=>{const{createVariableHandler:t}=await Promise.resolve().then(()=>(Gs(),Us));return t(e)})),Sc("atoms_delete_variable",{title:"Delete Project Variable",description:"Delete a project variable. Refuses by default if any items reference it.\n\nArgs:\n - project_id (string, UUID): Project containing the variable\n - name (string): Variable name to delete\n - force (bool, optional): Delete even if items still reference it (default false)\n\nReturns:\n { deleted, name, referenced_by_items, warning? }",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(64).describe("Variable name to delete"),force:o.z.boolean().optional().describe("Delete even if items reference it (default false)"),confirmation_token:o.z.string().max(500).optional().describe("Token from a prior preview call (only used when ATOMS_MCP_REQUIRE_CONFIRMATION is set)")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},$c("atoms_delete_variable",async e=>{const{deleteVariableHandler:t}=await Promise.resolve().then(()=>(Gs(),Us));return t(e)})),Sc("atoms_list_domains",{title:"List Project Domains",description:"List the domain tags of a project's taxonomy, with item counts.\n\nA tag flagged as a FUNCTION (metadata.function_tags, the seed of a HARA row) is not a domain: it is\nleft out of this list, exactly as the app's domain picker leaves it out, because atoms_create_item,\natoms_update_item and atoms_bulk_import refuse it as a domain. Such tags are named in\nexcluded_function_tags; use them through atoms_create_analysis source_functions or atoms_fill_row\nsource_function.\n\nArgs:\n - project_id (string, UUID): Project to inspect\n\nReturns:\n { domains: [{ name, registered, item_count }], total, registered_count, unregistered_count,\n excluded_function_tags: string[], note? }\n registered: false marks a tag found on items but not in the taxonomy (an orphan); a flagged tag\n an item still carries is a function on that item, never counted as an orphan.",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_list_domains",async e=>{const{listDomainsHandler:t}=await Promise.resolve().then(()=>(ro(),Ys));return t(e)})),Sc("atoms_create_domain",{title:"Create Project Domain",description:'Register a new domain tag in the project taxonomy.\n\nOnce created, items can be tagged with this domain via atoms_create_item or atoms_update_item.\n\nArgs:\n - project_id (string, UUID): Project to add the domain to\n - name (string): Domain name (lowercased automatically, e.g., "safety", "can-bus")\n\nReturns:\n { domain: { name }, message }',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(64).describe("Domain name")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$c("atoms_create_domain",async e=>{const{createDomainHandler:t}=await Promise.resolve().then(()=>(ro(),Ys));return t(e)})),Sc("atoms_list_levels",{title:"List Project Levels",description:"List the project's level taxonomy in order, with each level's L# (positional index) and item count.\n\nArgs:\n - project_id (string, UUID): Project to list levels for\n\nReturns:\n { levels: [{ name, prefix, item_count }], total }",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_list_levels",async e=>{const{listLevelsHandler:t}=await Promise.resolve().then(()=>(jn(),mn));return t(e)})),Sc("atoms_create_level",{title:"Create Project Level",description:'Register a new level in the project taxonomy so items can be tagged with it and it shows in the Strata view. Appended to the ordered level list; its L# is the next index. Names are user-defined labels (lowercased automatically); matching is case-insensitive.\n\nArgs:\n - project_id (string, UUID): Project to add the level to\n - name (string): Level name, e.g. "feature", "system", "subsystem"\n\nReturns:\n { level: { name, prefix }, message }',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(64).describe("Level name (user-defined label)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$c("atoms_create_level",async e=>{const{createLevelHandler:t}=await Promise.resolve().then(()=>(jn(),mn));return t(e)})),Sc("atoms_delete_level",{title:"Delete Project Level",description:"Delete an UNUSED level from the project taxonomy. Refuses if any item still uses the level (re-tag those items with atoms_update_item first). A level is positional, so deleting one shifts every later level's L# down by one. Matching is case-insensitive.\n\nArgs:\n - project_id (string, UUID)\n - name (string): the level to delete\n - confirmation_token (string, optional): from the first call, when confirmation is required\n\nReturns:\n { level: { name }, remaining_levels, message } — or a confirmation_required payload",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(64).describe("Level name to delete"),confirmation_token:o.z.string().optional().describe("Token from the first (preview) call, when confirmation is required")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},$c("atoms_delete_level",async e=>{const{deleteLevelHandler:t}=await Promise.resolve().then(()=>(jn(),mn));return t(e)})),Sc("atoms_reorder_levels",{title:"Reorder Project Levels",description:"Reorder the project's levels. Provide every existing level name exactly once, in the order you want them (any casing). L# is positional, so this changes every level's L# wherever it renders (Strata, sidebar, exports). Touches no item.\n\nArgs:\n - project_id (string, UUID)\n - names (string[]): every existing level name, in the new order\n\nReturns:\n { levels: [{ name, prefix }], message }",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),names:o.z.array(o.z.string().min(1).max(64)).min(1).describe("Every existing level name, in the desired order")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_reorder_levels",async e=>{const{reorderLevelsHandler:t}=await Promise.resolve().then(()=>(jn(),mn));return t(e)})),Sc("atoms_list_fields",{title:"List Project Fields",description:'List the project\'s custom field definitions (project-scoped key-value slots on items, e.g. "Verification Method"), with counts of items carrying a value.\n\nField values are set per item via atoms_create_item / atoms_update_item fields.\n\nArgs:\n - project_id (string, UUID): Project to inspect\n\nReturns:\n { fields: [{ name, type, options, item_count }], total }',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_list_fields",async e=>{const{listFieldsHandler:t}=await Promise.resolve().then(()=>(Qn(),zn));return t(e)})),Sc("atoms_create_field",{title:"Create Project Field",description:'Define a project custom field so items can carry a value for it (shown as a Matrix column, e.g. "Verification Method").\n\nThe name keeps its casing; uniqueness is case-insensitive. Type is one of text, long, bool, date, person, select, number, link. Select fields require options. Field definitions never carry visibility rules, permissions, or workflow logic. A definition applies to every item type unless appliesTo restricts it (requirement|test-case|note|table).\n\nArgs:\n - project_id (string, UUID): Project to define the field in\n - name (string): Field name, 1-60 characters (e.g. "Verification Method", "Essential Output?")\n - type (string, optional): Defaults to text\n - options (string[], optional): Required when type=select\n - scope (string, optional): project (default) or organization (org admin only)\n - appliesTo (string[], optional): item types the field shows on (requirement|test-case|note|table); omit for all types\n\nReturns:\n { field: { name, type, options }, message }',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(60).describe("Field name (display name, 1-60 chars)"),type:o.z.enum(["text","long","bool","date","person","select","number","link"]).optional().describe("Field type, defaults to text"),options:o.z.array(o.z.string().min(1).max(120)).max(100).optional().describe("Allowed values for select fields"),scope:o.z.enum(["project","organization"]).optional().describe("Create in this project or copy to every project in the organization"),appliesTo:o.z.array(o.z.enum(["requirement","test-case","note","table"])).max(4).optional().describe("Item types this field shows on (requirement|test-case|note|table); omit for all types")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$c("atoms_create_field",async e=>{const{createFieldHandler:t}=await Promise.resolve().then(()=>(Qn(),zn));return t(e)})),Sc("atoms_delete_field",{title:"Delete Project Field",description:"Delete a project field definition. Existing item values are left in item JSON but no longer render as Matrix columns until a matching field is created again.\n\nArgs:\n - project_id (string, UUID): Project containing the field\n - name (string): Field name to delete",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(60).describe("Field name")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},$c("atoms_delete_field",async e=>{const{deleteFieldHandler:t}=await Promise.resolve().then(()=>(Qn(),zn));return t(e)})),Sc("atoms_reorder_fields",{title:"Reorder Project Fields",description:"Reorder project field definitions. Definition order drives the Column Picker listing and a fresh user's Matrix column order.\n\nOrdering only: no definition is added, dropped, renamed, or retyped, and no item value moves. Use this instead of deleting and recreating definitions to change their order.\n\nArgs:\n - project_id (string, UUID): Project containing the fields\n - names (string[]): Every existing field name exactly once, in the order you want them. A list that is not a permutation of the current names is refused, not partially applied.",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),names:o.z.array(o.z.string().min(1).max(60)).describe("Every existing field name exactly once, in the desired order")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_reorder_fields",async e=>{const{reorderFieldsHandler:t}=await Promise.resolve().then(()=>(Qn(),zn));return t(e)})),Sc("atoms_update_domain",{title:"Rename Project Domain",description:"Rename a domain tag. Updates the taxonomy and propagates to all tagged items.\n\nUse dry_run=true first to see how many items will be affected before committing.\n\nArgs:\n - project_id (string, UUID): Project containing the domain\n - name (string): Current domain name\n - new_name (string): New domain name\n - dry_run (bool, optional): If true, return impact count without making changes (default false)\n\nReturns (dry_run=false):\n { domain: { name }, renamed_from, items_updated, message }\n\nReturns (dry_run=true):\n { dry_run: true, rename: { from, to }, items_affected, message }",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(64).describe("Current domain name"),new_name:o.z.string().min(1).max(64).describe("New domain name"),dry_run:o.z.boolean().optional().describe("Preview impact without applying (default false)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$c("atoms_update_domain",async e=>{const{updateDomainHandler:t}=await Promise.resolve().then(()=>(ro(),Ys));return t(e)})),Sc("atoms_delete_domain",{title:"Delete Project Domain",description:"Delete a domain tag from the project taxonomy.\n\nRefuses by default if any items are tagged with it. With force=true, deletes the\ndomain and strips the tag from all items.\n\nArgs:\n - project_id (string, UUID): Project containing the domain\n - name (string): Domain name to delete\n - force (bool, optional): Strip from items and delete anyway (default false)\n\nReturns:\n { deleted, name, items_updated, message }",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(64).describe("Domain name to delete"),force:o.z.boolean().optional().describe("Strip from all items and delete (default false)"),confirmation_token:o.z.string().max(500).optional().describe("Token from a prior preview call (only used when ATOMS_MCP_REQUIRE_CONFIRMATION is set)")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},$c("atoms_delete_domain",async e=>{const{deleteDomainHandler:t}=await Promise.resolve().then(()=>(ro(),Ys));return t(e)})),zc({appName:"summary",htmlFile:"summary-app.html",toolName:"atoms_project_summary",title:"Project Compliance Summary",description:'One-call project health and compliance dashboard.\n\nReturns item counts by type, test execution status, requirement coverage\n(overall and by domain), and recent change activity.\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive dashboard with charts.\nFalls back to JSON for non-UI clients.\n\nArgs:\n - project_id (string, UUID): Project to summarize\n\nReturns:\n { project_name, counts, test_status, coverage, coverage_by_domain, recent_changes, last_updated }\n\nExamples:\n - "Give me a compliance snapshot" → atoms_project_summary(project_id)\n - "How\'s our test coverage?" → atoms_project_summary(project_id)',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:$c("atoms_project_summary",async e=>{const{projectSummaryHandler:t}=await Promise.resolve().then(()=>(so(),ao));return t(e)})}),zc({appName:"browse",htmlFile:"browse-app.html",toolName:"atoms_browse",title:"Browse ATOMS Items",description:'View and filter items in an ATOMS project with a visual browser.\n\nIn MCP App hosts (Claude, ChatGPT), renders an interactive filterable list\nwith expandable item details. Falls back to JSON for non-UI clients.\n\nArgs:\n - project_id (string, UUID): The project to browse\n - type (string, optional): Filter by type (requirement, test-case, note, table)\n - domain (string, optional): Filter by domain tag\n - level (string, optional): Filter by level (System, Subsystem, Component)\n - query (string, optional): Full-text search query\n - limit (number, optional): Max results (default 25, max 100)\n - offset (number, optional): Pagination offset (default 0)\n\nReturns:\n { items: [...], filters: { domains, levels, types }, meta: { total_count, limit, offset, has_more } }\n\nExamples:\n - "Show me the requirements" → atoms_browse(project_id, type="requirement")\n - "Browse safety items" → atoms_browse(project_id, domain="Safety")\n - "Find braking requirements" → atoms_browse(project_id, query="braking")',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),type:o.z.enum(["requirement","test-case","note","table","analysis"]).optional().describe("Filter by item type"),domain:o.z.string().max(64).optional().describe("Filter by domain tag"),level:o.z.string().max(32).optional().describe("Filter by level (System, Subsystem, Component)"),query:o.z.string().max(1e3).optional().describe("Full-text search query"),limit:o.z.number().int().min(1).max(100).default(25).describe("Max results (default 25, max 100)"),offset:o.z.number().int().min(0).default(0).describe("Pagination offset (default 0)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},handler:$c("atoms_browse",async e=>{const{browseHandler:t}=await Promise.resolve().then(()=>(_o(),oo));return t(e)})}),Sc("atoms_impact_analysis",{title:"Analyze Impact of Changing an Item",description:'Analyze the blast radius of changing a specific item.\n\nTraverses the relationship graph from the given item and returns every item that\nwould be affected by a change — with their types, depths, and full relationship paths.\n\nArgs:\n - project_id (string, UUID): Project containing the item\n - item_id (string): Item to analyze (e.g., "REQ-001")\n - direction (string, optional): "downstream" (default) | "upstream" | "both"\n - downstream: what this item affects (children, verified_by)\n - upstream: what affects this item (parents, verifies)\n - both: full bidirectional traversal\n - depth (integer, optional): Max traversal depth. Omit for full depth (all reachable nodes).\n - include_variable_refs (boolean, optional): Reserved for future variable-aware impact. Default true.\n\nReturns:\n { root: { item_id, title, type }, impacted_items: [{ item_id, title, type, depth, relationship_path }], summary: { total_impacted, by_type, max_depth_reached } }\n\nExamples:\n - "What breaks if I change REQ-001?" → atoms_impact_analysis(project_id, "REQ-001")\n - "What\'s upstream of TC-005?" → atoms_impact_analysis(project_id, "TC-005", direction="upstream")\n - "Show direct dependencies only" → atoms_impact_analysis(project_id, "REQ-001", depth=1)\n\nErrors:\n - "Item not found" → verify item_id exists in the project',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),item_id:o.z.string().min(1).max(20).describe("Item ID to analyze (e.g., REQ-001)"),direction:o.z.enum(["downstream","upstream","both"]).default("downstream").describe("Traversal direction (default: downstream)"),depth:o.z.number().int().min(1).optional().describe("Max traversal depth. Omit for full depth."),include_variable_refs:o.z.boolean().default(!0).describe("Reserved for variable-aware impact (future). Default true.")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_impact_analysis",async e=>{const{impactAnalysisHandler:t}=await Promise.resolve().then(()=>(go(),fo));return t(e)})),Sc("atoms_list_baselines",{title:"List ATOMS Baselines",description:"List named baselines (named project snapshots) for a project, newest first.\n\nA baseline is a labeled moment in time. Use atoms_create_baseline to make one,\natoms_diff_baselines to compare two, and atoms_restore_baseline to roll back.",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),limit:o.z.number().int().min(1).max(100).default(50).describe("Max results (default 50, max 100)"),offset:o.z.number().int().min(0).default(0).describe("Pagination offset")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_list_baselines",async e=>{const{listBaselinesHandler:t}=await Promise.resolve().then(()=>(Co(),xo));return t(e)})),Sc("atoms_create_baseline",{title:"Create ATOMS Baseline",description:"Create a named baseline (snapshot label) at the current moment for a project.\n\nThe baseline records a name + timestamp. It does NOT copy item data; the project\nstate at that moment is reconstructed via temporal queries against item_revisions.\nNames must be unique per project (case-insensitive).",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),name:o.z.string().min(1).max(200).describe("Baseline name (1-200 chars, unique per project)"),description:o.z.string().max(2e3).optional().describe("Optional description (max 2000 chars)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$c("atoms_create_baseline",async e=>{const{createBaselineHandler:t}=await Promise.resolve().then(()=>(Co(),xo));return t(e)})),Sc("atoms_lock_baseline",{title:"Lock ATOMS Baseline",description:"Lock a baseline so it becomes audit-grade immutable. Admin only. One-way: there is no unlock.\n\nAfter locking, the baseline's name, description, and snapshot_at cannot be changed,\nand it cannot be deleted. Use for regulatory submissions and DRBs.",inputSchema:{baseline_id:o.z.string().uuid().describe("UUID of the baseline to lock")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$c("atoms_lock_baseline",async e=>{const{lockBaselineHandler:t}=await Promise.resolve().then(()=>(Co(),xo));return t(e)})),Sc("atoms_diff_baselines",{title:"Diff ATOMS Baselines",description:"Compute the diff between two baselines: which items were added, removed, or modified.\n\nReturns a summary plus a paginated items array. Pass detail=true to include\nper-item field-level diffs (slower for large projects).",inputSchema:{baseline_a_id:o.z.string().uuid().describe("UUID of the first (older) baseline"),baseline_b_id:o.z.string().uuid().describe("UUID of the second (newer) baseline"),detail:o.z.boolean().default(!1).describe("Include per-item field-level diffs (default false)"),limit:o.z.number().int().min(1).max(200).default(50).describe("Max items per page (default 50)"),offset:o.z.number().int().min(0).default(0).describe("Pagination offset")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_diff_baselines",async e=>{const{diffBaselinesHandler:t}=await Promise.resolve().then(()=>(Co(),xo));return t(e)})),Sc("atoms_restore_baseline",{title:"Restore from ATOMS Baseline",description:"Restore project state to a baseline. Admin only. Preview-by-default.\n\nCall with confirm=false (default) for a dry run that returns items_to_restore count.\nCall with confirm=true to execute: each item present at the baseline gets a new\nrevision matching its historical state, event_source='restore'. Items added AFTER\nthe baseline are NOT deleted. Always recoverable via further history.",inputSchema:{baseline_id:o.z.string().uuid().describe("UUID of the baseline to restore from"),confirm:o.z.boolean().default(!1).describe("Set true to execute; false (default) returns a preview")},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!1}},$c("atoms_restore_baseline",async e=>{const{restoreBaselineHandler:t}=await Promise.resolve().then(()=>(Co(),xo));return t(e)})),Sc("atoms_create_analysis",{title:"Create Analysis (HARA, Risk analysis, FMEA)",description:'Create a new analysis (a HARA under ISO 26262, a Risk analysis under ISO 14971, a Security risk\nanalysis under AAMI TIR57 (subtype security_risk, or hara with variant tir57), a DFMEA, a PFMEA,\na Software FMEA or a Use FMEA) with one empty row per seed. The analysis carries its standard:\nthe subtype and the variant name it, and a project may hold analyses under both standards.\n\nRequires editor or admin role. Structure matches analyses created in the ATOMS web app.\nA hara (HARA or risk analysis) is seeded from FUNCTIONS (tags flagged as functions), never from\nrequirements: a requirement is an output of the analysis. A dfmea (DFMEA or FMEA) is seeded from\nrequirements / design elements; a PFMEA from a process step.\n\nIn any security risk analysis row, E is exploitability: the ease of exploiting the vulnerability across the device lifecycle, never a probability.\nArgs:\n - project_id (string, UUID): Target project\n - subtype (string): hara | dfmea | risk_analysis | fmea. risk_analysis is an alias of hara and\n fmea an alias of dfmea, so a medical user never has to say HARA or DFMEA.\n hazard_analysis is still accepted as the older name of risk_analysis.\n - variant (string, optional): the standard the analysis carries. hara: iso26262 | iso14971;\n risk_analysis implies iso14971; dfmea: design | process | software | use; fmea implies\n process (the PFMEA). A variant outside the subtype\'s family is refused by name. Every variant\n is accepted in every project. Name none and the default follows the project\'s industry\n setting, exactly as the create menu in the app does: automotive gives hara iso26262 and dfmea\n design, medical device gives hara iso14971 and dfmea process. The variant is stored on the\n analysis and selects its scheme for every later read and write; the setting is only the\n default and never changes an analysis that already exists.\n - title (string, optional): Analysis title (default "New <parent label>": "New HARA",\n "New Risk analysis", "New DFMEA", "New PFMEA", "New Software FMEA", "New Use FMEA")\n - source_functions (string[], hara only, max 50): Function names to seed rows from. A name\n absent from the project\'s tags is registered as a function-flagged tag; matching is\n case-insensitive and trimmed. Rejected for dfmea.\n - source_req_ids (string[], dfmea only, max 50): Requirement IDs to seed rows from.\n Each must exist, be a plain requirement, and not itself be an analysis row. Rejected for hara.\n - revision (string, optional): Revision label (default "1.0")\n - intended_use (string, optional, ISO 14971 risk analysis only): what the device is for, for\n which patient, by which user and in which environment (ISO 14971:2019 5.2).\n - foreseeable_misuse (string, optional, ISO 14971 risk analysis only): the reasonably\n foreseeable misuse, ONE CASE PER LINE (ISO 14971:2019 5.2).\n - safety_characteristics (string, optional, ISO 14971 risk analysis only): the characteristics\n related to safety, ONE PER LINE (ISO 14971:2019 5.3).\n The three scope fields are the analysis\'s, not a row\'s. They are refused by name on a scheme\n that carries no scope (a HARA, a DFMEA, an FMEA), and they can be written later on the\n analysis id with atoms_fill_row.\n The stored subtype and the row req_types are the same under every standard; ids follow the\n scheme: HARA-n / SG-n (hara_iso26262), RA-n / RSK-n (hazard_iso14971), DFMEA-n / FM-n (dfmea_rpn),\n PFMEA-n / PF-n (fmea_process), SFMEA-n / FM-n (fmea_software), UFMEA-n / FM-n (fmea_use).\n\nReturns:\n { id (e.g. "HARA-001", "RA-001", "PFMEA-001"), subtype, title, revision,\n standard? ("ISO 26262" | "ISO 14971" for a hara; "AIAG-VDA FMEA Handbook" for a DFMEA or PFMEA;\n absent for a Software FMEA or a Use FMEA), scheme, variant,\n labels: { parent, row, row_statement, mitigating_requirement, lane, level },\n rows: [{ id, source_function }] (hara) | [{ id, source_req }] (dfmea),\n link_warnings?: [{ row_id, source_function | source_req, message }], warning? }\n rows lists every created row. If any row could not be attached to the analysis, it is\n named in link_warnings and warning explains the repair (atoms_link_items with type\n \'parent\', from_id=<row id>, to_id=<analysis id>); such rows will not appear in\n atoms_get_analysis until relinked.\n\nSide effects:\n - Creates the analysis parent item plus one row item per seed (row ids follow the scheme: SG-n or RSK-n, FM-n or PF-n)\n - hara: registers new function names in the project\'s tags and function flags (metadata)\n - Links each row to the parent (dual-written to the relationship shadow table)\n - Logs each created item to change_history with actor=\'mcp_<client>\'',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the target project"),subtype:o.z.enum(["hara","dfmea","risk_analysis","fmea","hazard_analysis","security_risk"]).describe("Analysis subtype; risk_analysis is an alias of hara and fmea of dfmea, hazard_analysis is still accepted as the older name of risk_analysis, and security_risk names the AAMI TIR57 security risk analysis (implies variant tir57) (REQ-ANL-192, REQ-ANL-245)"),title:o.z.string().min(1).max(500).optional().describe("Analysis title"),source_functions:o.z.array(o.z.string().min(1).max(100)).max(50).optional().describe("hara only: function names to seed rows from (0-50); new names are registered as function-flagged tags"),source_req_ids:o.z.array(o.z.string().min(1).max(20)).max(50).optional().describe("dfmea only: requirement IDs to seed rows from (0-50)"),revision:o.z.string().min(1).max(32).optional().describe("Revision label (default 1.0)"),variant:o.z.enum(["iso26262","iso14971","tir57","design","process","software","use"]).optional().describe("The standard the analysis carries (REQ-ANL-212). hara: iso26262 | iso14971 | tir57; dfmea: design | process | software | use. risk_analysis implies iso14971, fmea implies process, security_risk implies tir57. Omit it and the default follows the project industry setting (REQ-ANL-216)."),intended_use:o.z.string().max(1e4).optional().describe("ISO 14971 risk analysis only: what the device is for, for which patient, by which user and in which environment (ISO 14971:2019 5.2)."),foreseeable_misuse:o.z.string().max(1e4).optional().describe("ISO 14971 risk analysis only: the reasonably foreseeable misuse, one case per line (ISO 14971:2019 5.2)."),safety_characteristics:o.z.string().max(1e4).optional().describe("ISO 14971 risk analysis only: the characteristics related to safety, one per line (ISO 14971:2019 5.3)."),threat_model_method:o.z.string().max(1e4).optional().describe("Security risk analysis only: the threat modeling methodology used, e.g. STRIDE."),threat_model_rationale:o.z.string().max(1e4).optional().describe("Security risk analysis only: why that methodology was chosen."),environment_assumptions:o.z.string().max(1e4).optional().describe("Security risk analysis only: assumptions about the operating environment, one per line."),attack_surfaces:o.z.string().max(1e4).optional().describe("Security risk analysis only: interfaces and entry points considered, one per line."),related_documents:o.z.string().max(1e4).optional().describe("Security risk analysis only: where the SBOM, the security testing report and the architecture views live.")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$c("atoms_create_analysis",async e=>{const{createAnalysisHandler:t}=await Promise.resolve().then(()=>(fc(),Vo));return t(e)})),Sc("atoms_fill_row",{title:"Fill Analysis Row Fields",description:'Fill fields on one analysis row: a HARA or risk analysis row, a security risk analysis row, a DFMEA or FMEA row (row ids follow the scheme: SG-n or RSK-n, CYB-n, FM-n or PF-n).\n\nRequires editor or admin role. Written fields are marked as MCP-sourced so the\nweb app shows their provenance. The computed level (ASIL, RPN or Risk) is derived and\nnever stored: asil is never a writable key.\n\nA security risk analysis row (CYB-n) accepts threat_event, vulnerability, impact, harm,\nseverity, exploitability (E: the ease of exploiting the vulnerability across the device\nlifecycle; it is NOT a probability), residual_s, residual_e, s_rationale, e_rationale,\njustification, safety_goal (the security control statement), safety_impact (Yes|No),\nsafety_impact_justification, source_function (the asset), assumptions and rationale. It\nrefuses p1, p2, occurrence and detection by name (those belong to the other schemes).\n\nrow_id may also name the ANALYSIS PARENT (e.g. "RA-001", "SEC-001"), which carries the\nanalysis\'s own fields rather than a row\'s: on an ISO 14971 risk analysis exactly intended_use,\nforeseeable_misuse, safety_characteristics (its scope, ISO 14971:2019 5.2 and 5.3),\noverall_residual_risk (Low | Medium | High) with overall_residual_rationale (its overall\nresidual risk, ISO 14971:2019 clause 8) and software_safety_class (A | B | C) with\nsoftware_safety_rationale (IEC 62304 4.3); on a security risk analysis exactly\nthreat_model_method, threat_model_rationale, environment_assumptions, attack_surfaces,\nrelated_documents, lifecycle_sources (a JSON array of [name, considered, note] tuples) and\noverall_residual_risk (Very Low | Low | Moderate | High | Very High) with\noverall_residual_rationale. Any other key on a parent is refused by name with that list\ngiven, and a scheme that carries no parent field (a HARA, a DFMEA, an FMEA) refuses all of\nthem. Both judged values are a PERSON\'s: nothing computes them, the empty string clears\none, and the app only ever suggests one from the rows.\n\nIn any security risk analysis row, E is exploitability: the ease of exploiting the vulnerability across the device lifecycle, never a probability.\nArgs:\n - project_id (string, UUID): Project containing the row\n - row_id (string): Analysis row ID (e.g. "SG-001", "RSK-002", "FM-003", "PF-004"), or the\n analysis parent ID (e.g. "RA-001") to write the analysis\'s scope and overall judgement\n - fields (object): Field name to value map. Allowed keys:\n hara: failure_mode, operating_scenario, hazard, assumptions, severity (S0-S3), exposure (E0-E4),\n controllability (C0-C3), safe_state, ftti, rationale, source_function, effect, title\n dfmea: failure_mode, cause, severity (1-10), occurrence (1-10), detection (1-10),\n rationale, effect\n medical risk analysis: failure_mode (the hazard), operating_scenario (the sequence of events),\n hazard (the harm), assumptions, severity (1-5), p1 (1-5), p2 (1-5), rationale, source_function,\n safety_goal (the risk control statement), s_rationale, p1_rationale, p2_rationale,\n residual_s, residual_p1, residual_p2, benefit_note, effect\n medical FMEA: the FMEA keys of its variant; a PFMEA adds process_step and current_controls,\n and a Software FMEA has NO occurrence (fixed at 10, IEC 62304).\n The analysis carries its standard: the variant stored on the row\'s parent analysis selects\n the scheme (an analysis stored before v5.4 without one resolves through the project\'s legacy hint).\n The derived level (asil / rpn / risk / p0) and the approval keys (approved, approved_at,\n approved_by, approved_note, field_source, link_roles, mitigation_types) are refused BY NAME.\n A suggested rating is written together with its rationale: pass the rating and its evidence\n field in one call (severity + s_rationale, p1 + p1_rationale), or use atoms_suggest_field.\n \'effect\' writes the row\'s long text body (the hazardous event of a HARA row, the hazardous situation\n of a medical risk analysis row, the effect of an FMEA row). dfmea failure_mode also\n becomes the row title. dfmea ratings accept numbers and are stored as strings.\n hara failure_mode: the standardized suggestions are No function, Loss of function, Partial function,\n Degradation of function, Misinterpretation of function (a suggestion; any text is accepted).\n hara source_function: the function the row analyzes, registered as a function-flagged tag if new.\n\nReturns:\n { row_id, subtype, written_fields, computed_level, asil_propagated_to?, approval_cleared? }\n approval_cleared: true means the row WAS approved and this write cleared approved, approved_by,\n approved_at and approved_note: a signature never covers content the approver did not see. A\n person approves the row again with atoms_approve_row once they have read the new values.\n On the analysis parent instead: { analysis_id, scheme, written_fields,\n scope: { intended_use, foreseeable_misuse: string[], safety_characteristics: string[] },\n overall: { residual_risk, residual_rationale, safety_class, safety_rationale } }.\n\nSide effects:\n - Saves the row with stale-write protection and logs to change_history\n - On an approved row: clears the approval (history-logged with the reason)\n - ISO 26262 HARA only (scheme hara_iso26262): if the row is linked and its computed ASIL changed,\n updates each linked requirement\'s asil / asil_source (each update is history-logged). asil is an\n ISO 26262 field: a row of an ISO 14971 risk analysis or of a TIR57 security risk analysis never\n writes it (REQ-ANL-257)',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),row_id:o.z.string().min(1).max(20).describe("Analysis row ID (e.g. SG-001, FM-003), or the analysis parent ID (e.g. RA-001) for its scope and overall residual risk"),fields:o.z.record(o.z.string().max(64),o.z.union([o.z.string().max(5e4),o.z.number()])).describe("Field name to value map (see description for allowed keys per subtype)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_fill_row",async e=>{const{fillRowHandler:t}=await Promise.resolve().then(()=>(fc(),Vo));return t(e)})),Sc("atoms_get_analysis",{title:"Get Analysis",description:'Get a full analysis view: every row with its computed level (ASIL, RPN or Risk, by the\nanalysis\'s scheme) and gap / critical / stale classifications, plus summary counts and the\nscheme\'s vocabulary.\n\nClassifications match the ATOMS web app exactly (same rules, same thresholds). The analysis\ncarries its standard: the variant stored on it selects the scheme (a hara is a HARA under\nISO 26262 or a Risk analysis under ISO 14971; a dfmea is a DFMEA, a PFMEA, a Software FMEA or\na Use FMEA). An analysis stored before v5.4 without a variant resolves through the project\'s\nlegacy hint and reads variant null.\n\nIn any security risk analysis row, E is exploitability: the ease of exploiting the vulnerability across the device lifecycle, never a probability.\nArgs:\n - project_id (string, UUID): Project containing the analysis\n - analysis_id (string): Analysis ID (e.g. "HARA-001", "RA-001", "PFMEA-002")\n\nReturns:\n { analysis: { id, title, subtype, scheme, revision, standard, variant,\n labels: { parent, row, row_statement, mitigating_requirement, lane, level },\n acceptability: { Low, Medium, High } | null (the ISO 14971 wording per risk level, e.g. High is\n "Not acceptable"; null on every scheme but the medical risk analysis),\n definitions: [{ key, text }] (the scheme\'s glossary, the text the app\'s tooltips show),\n control_types: [{ id, abbr, label, precedence }] (ISO 14971 clause 7, applied in precedence\n order; empty on the automotive schemes),\n field_labels: [{ key, label }] (each raw row key to the label this scheme shows for it, e.g. in a\n medical risk analysis failure_mode is "Hazard", effect is "Hazardous situation" and hazard is\n "Harm"; read the raw keys through this list, never as automotive words),\n scope: { intended_use, foreseeable_misuse: string[], safety_characteristics: string[] } | null\n (the analysis\'s scope, ISO 14971:2019 5.2 and 5.3; the two lists are the stored lines, blank\n lines dropped; null on a scheme that carries no scope),\n overall: { residual_risk, residual_rationale, safety_class, safety_rationale } | null\n (the overall residual risk a person judged, ISO 14971:2019 clause 8, and the IEC 62304 4.3\n software safety class; an unjudged value reads as the empty string; null on a scheme that\n carries neither),\n threat_model: { stated, method, rationale, environment_assumptions: string[], attack_surfaces: string[],\n lifecycle_sources: [{ name, considered, note }], related_documents } | null (security risk\n analysis only; null on every other scheme),\n acceptance: { acceptance_state, accepted_by, accepted_at, acceptance_note,\n open_gaps_at_acceptance: [{ kind, label }] } (REQ-ANL-249: the analysis-level acceptance\n record; a parent never accepted reads acceptance_state "draft" with an empty gap list,\n never null) },\n rows: [{ id, title, source_req, effect, <the scheme\'s fields under their raw keys>,\n computed_level, computed_level_text (the acceptability wording behind the level, or null),\n is_gap, gap_rule, is_critical, is_stale, mitigated_by, link_roles, mitigation_types,\n verified_by, residual_level (medical risk analysis and security risk analysis only; null\n elsewhere),\n approved, approved_at, approved_by, approved_note, mcp_fields }],\n counts: { rows, gaps, critical, stale, approved, linked } }\n The vocabulary lists are given once per analysis, never repeated per row.',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),analysis_id:o.z.string().min(1).max(20).describe("Analysis ID (e.g. HARA-001, RA-001, PFMEA-002)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_get_analysis",async e=>{const{getAnalysisHandler:t}=await Promise.resolve().then(()=>(fc(),Vo));return t(e)})),Sc("atoms_link_mitigation",{title:"Link Analysis Row to Mitigating Requirement",description:'Link a HARA / risk analysis / FMEA row to the technical requirement that mitigates it (the\nsafety requirement of a HARA row, the risk control of a medical row, the action of an FMEA row).\n\nRequires editor or admin role.\n\nIn any security risk analysis row, E is exploitability: the ease of exploiting the vulnerability across the device lifecycle, never a probability.\nArgs:\n - project_id (string, UUID): Project containing both items\n - row_id (string): Analysis row ID (e.g. "SG-001", "RSK-002", "FM-003", "PF-004")\n - req_id (string): Target requirement ID (must be a plain requirement, not an analysis row)\n hara / medical risk analysis rows: the target must be a requirement of req_type \'safety\'\n (a safety requirement / risk control); any other requirement is refused with the rule\n named (REQ-ANL-142). An FMEA row accepts any requirement, so long as role is given.\n - control_type (string, medical and security schemes only, REQUIRED there): inherent | protective | information\n (ISO 14971 clause 7: inherently safe design, protective measure, information for safety), or on a security risk analysis row (CYB-n, AAMI TIR57) one of the eight FDA Appendix 1 categories: authentication | authorization | cryptography | code_data_execution_integrity | confidentiality | event_detection_logging | resiliency_recovery | firmware_software_updates (REQ-ANL-241).\n Stored on the LINK (the row\'s analysis_props.mitigation_types keyed by req_id), never on the\n requirement. The analysis carries its standard: control_type is required exactly when the\n row\'s resolved scheme is medical (hazard_iso14971, fmea_process, fmea_software, fmea_use: a\n risk analysis, PFMEA, Software FMEA or Use FMEA link carries one) and refused on a HARA\n (ISO 26262) or a DFMEA row.\n - role (string, dfmea rows only, REQUIRED there): prevention | detection | mitigation.\n Must be omitted for hara rows (a HARA or a risk analysis).\n - override (boolean, optional, default false): ISO 26262 HARA only. When the target\n already carries a different ASIL, the call fails with a structured error\n (details.code=\'asil_conflict\') unless override=true; the override is\n history-logged like a dialog accept.\n\nReturns:\n { row_id, req_id, type: "mitigated_by", role, scheme, control_type? (medical links, the type stored\n on the link), asil_written? (ISO 26262 HARA only: the row\'s computed ASIL, written\n onto the target) }\n\nSide effects:\n - Adds the mitigated_by/mitigates relationship (JSONB + shadow table, one transaction)\n - ISO 26262 HARA only (scheme hara_iso26262): writes the row\'s computed ASIL onto the target\n requirement (data.asil + data.asil_source) with a history entry recording previous/new values.\n An ISO 14971 risk row and a TIR57 security row write no field on the target (REQ-ANL-257)\n - dfmea / every FMEA: never modifies any field on the target requirement',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),row_id:o.z.string().min(1).max(20).describe("Analysis row ID (e.g. SG-001, FM-003)"),req_id:o.z.string().min(1).max(20).describe("Target requirement ID (e.g. REQ-042)"),role:o.z.enum(["prevention","detection","mitigation"]).optional().describe("Mitigation role. Required for dfmea rows (every FMEA); must be omitted for hara rows."),override:o.z.boolean().optional().describe("hara only: apply the row's level (ASIL or Risk) over a conflicting existing value (history-logged)"),control_type:o.z.enum(["inherent","protective","information","authentication","authorization","cryptography","code_data_execution_integrity","confidentiality","event_detection_logging","resiliency_recovery","firmware_software_updates"]).optional().describe("Medical schemes only (required there): the ISO 14971 clause 7 control type. Security risk analysis rows (required there): one of the eight FDA Appendix 1 categories (authentication, authorization, cryptography, code_data_execution_integrity, confidentiality, event_detection_logging, resiliency_recovery, firmware_software_updates). Stored on the link; refused when it does not belong to the row's own scheme (REQ-ANL-241).")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_link_mitigation",async e=>{const{linkMitigationHandler:t}=await Promise.resolve().then(()=>(fc(),Vo));return t(e)})),Sc("atoms_get_gaps",{title:"Get Analysis Gap Rows",description:'List only the gap rows of a HARA / risk analysis / FMEA analysis, each naming the rule that matched.\n\nGap rules (identical to the web app):\n hara (HARA): unmitigated row with ASIL A-D ("asil <X> unmitigated")\n hara (medical risk analysis): uncontrolled row with Risk above Low ("<level> risk uncontrolled")\n dfmea (DFMEA, PFMEA and every FMEA variant): unmitigated row with RPN >= 100 ("rpn >= 100") or\n severity >= 9 ("severity >= 9")\n\nIn any security risk analysis row, E is exploitability: the ease of exploiting the vulnerability across the device lifecycle, never a probability.\nArgs:\n - project_id (string, UUID): Project containing the analysis\n - analysis_id (string): Analysis ID (e.g. "HARA-001", "RA-001", "PFMEA-002")\n\nReturns:\n { analysis_id, subtype, gaps: [{ id, title, source_req, source_function, computed_level, rule }], count }\n count is the gap-line TOTAL: the sum of every gap kind\'s gap_count (a controlled-but-unverified risk, an analysis-scope gap all count), matching the app\'s gap banner, so it can be GREATER than gaps.length, which lists only the per-row gap rules.\n plus scheme, labels and gap_kinds: [{ kind, label, row_ids, req_ids, gap_count, fix, fix_label }],\n the scheme\'s gap kinds with the ids behind each, in the same order and wording the app banner uses.\n gap_count is how many gaps the kind stands for: its rows, or its requirements, or 1 for a kind\n that is one fact about the ANALYSIS and therefore carries neither list.\n hara: unmitigated, no_test (no test on its ODD), no_parent (safety requirement at the system\n level with no parent)\n medical: no_control (a risk above Low), not_verified, no_residual, no_parent (risk control at the\n system level with no parent), no_scope (the analysis holds rows but states no intended\n use, ISO 14971:2019 5.2) and no_overall (every row is controlled and residual-rated but\n the overall residual risk is not judged, ISO 14971:2019 clause 8); both are written on\n the analysis id with atoms_fill_row\n FMEA: rpn (RPN 100 or above with no mitigating requirement), severity (severity 9 or above\n below RPN 100 with no mitigating requirement, the REQ-ANL-047 floor; a row is under one\n kind, never both); an FMEA has no requirement-scope kind, so no project-wide scan runs\n for it\n and system_level, system_level_source: the level the no_parent kind was judged against. The system\n level is the SECOND rung of the project\'s level ladder (metadata.levels, positional: the L1 of\n REQ-ASH-031), matched against tags.level by name; source is "taxonomy". Only when the ladder has\n fewer than two rungs does the literal "L1" apply, and source then reads "fallback".',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),analysis_id:o.z.string().min(1).max(20).describe("Analysis ID (e.g. HARA-001, RA-001, PFMEA-002)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_get_gaps",async e=>{const{getGapsHandler:t}=await Promise.resolve().then(()=>(fc(),Vo));return t(e)})),Sc("atoms_propose_rows",{title:"Write Proposed Analysis Rows",description:"Write rows YOU have drafted for one function into a HARA / risk analysis / FMEA (a DFMEA, a\nPFMEA, a Software FMEA or a Use FMEA). The analysis carries its standard: the rows are validated\nagainst the scheme its stored variant selects.\n\nThe drafting is the assistant's job: read the analysis with atoms_get_analysis (its scope, its\nODD / operating modes and its existing rows), draft the rows, then send them here. This tool\nvalidates every value against the analysis's scheme and writes the rows; it never invents a value.\n\nRequires editor or admin role. Every written value is marked as MCP-sourced and every row is\nwritten UNAPPROVED: a person approves it with atoms_approve_row.\n\nIn any security risk analysis row, E is exploitability: the ease of exploiting the vulnerability across the device lifecycle, never a probability.\nArgs:\n - project_id (string, UUID): Project containing the analysis\n - analysis_id (string): Analysis ID (e.g. \"HARA-001\", \"RA-001\", \"PFMEA-002\")\n - source_function (string, risk analysis only): the function these rows analyse; registered\n as a function-flagged tag if new (matching is case-insensitive and trimmed)\n - source_req (string, FMEA only): the requirement these rows analyse\n - rows (object[], 1 to 8): the drafted rows, each a field name to value map using the\n analysis's scheme keys (the same keys atoms_fill_row accepts, plus 'effect', and 'title'\n on a risk analysis row). A risk analysis row sent without a title is named by its\n failure_mode (the hazard), else by its hazard; an FMEA row by its failure_mode. The cap of 8\n is shared with the app and the assistant; a longer list is refused, never truncated.\n The derived level (asil / rpn / risk) and the approval keys are refused by name.\n\nReturns:\n { analysis_id, subtype, scheme, variant?, labels, source_function | source_req,\n rows: [{ id, written_fields, computed_level }], count, max_rows, approved: false,\n link_warnings?, warning? }\n\nSide effects:\n - Creates one row item per drafted row, attached to the analysis (row ids follow the scheme: SG-n or RSK-n, FM-n or PF-n)\n - Marks every written field field_source='mcp' and leaves each row unapproved\n - Logs each created row to change_history with actor='mcp_<client>'",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),analysis_id:o.z.string().min(1).max(20).describe("Analysis ID (e.g. HARA-001, RA-001, PFMEA-002)"),rows:o.z.array(o.z.record(o.z.string().max(64),o.z.union([o.z.string().max(5e4),o.z.number()]))).min(1).max(Dt).describe(`The drafted rows (1-${Dt}), each a scheme field name to value map`),source_function:o.z.string().min(1).max(100).optional().describe("Risk analysis only: the function these rows analyse"),source_req:o.z.string().min(1).max(20).optional().describe("FMEA only: the requirement these rows analyse")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$c("atoms_propose_rows",async e=>{const{proposeRowsHandler:t}=await Promise.resolve().then(()=>(fc(),Vo));return t(e)})),Sc("atoms_suggest_field",{title:"Write a Suggested Value for One Analysis Field",description:"Write ONE value YOU have suggested for one field of one analysis row.\n\nThe suggestion is the assistant's job (Suggest evidence, Suggest safety goal, Suggest risk control\nstatement); this tool is the write half: it validates the value against the row's scheme, writes it marked as MCP-sourced and\nreturns the row. A suggested rating is written together with its rationale: pass 'rationale' and\nit lands in the scheme's evidence field for that rating (severity -> s_rationale, exposure ->\ne_rationale, controllability -> c_rationale, p1 -> p1_rationale, p2 -> p2_rationale, and the\nsingle 'rationale' column on an FMEA).\n\nAccepting a suggestion is a client action: it flips that one field's field_source to 'user'. The\nrow stays unapproved either way, until atoms_approve_row records who approved it.\n\nIn any security risk analysis row, E is exploitability: the ease of exploiting the vulnerability across the device lifecycle, never a probability.\nArgs:\n - project_id (string, UUID): Project containing the row\n - row_id (string): Analysis row ID (e.g. \"SG-001\", \"RSK-002\", \"FM-003\", \"PF-004\")\n - field (string): the field to fill (any key atoms_fill_row accepts for this row's scheme)\n - value (string | number): the suggested value; a rating must be one the scheme allows\n - rationale (string, optional): the evidence behind a suggested RATING\n\nReturns:\n { row_id, subtype, scheme, field, field_source: \"mcp\", written_fields,\n rationale_field?, approval_cleared?, row: { id, computed_level, approved, mcp_fields, ...scheme fields } }\n approval_cleared: true means the row WAS approved and this suggestion cleared approved, approved_by,\n approved_at and approved_note: a signature never covers content the approver did not see.\n\nSide effects:\n - Saves the row with stale-write protection and logs to change_history\n - Marks the written field(s) field_source='mcp'; the row stays unapproved\n - On an approved row: clears the approval (history-logged with the reason)",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),row_id:o.z.string().min(1).max(20).describe("Analysis row ID (e.g. SG-001, FM-003)"),field:o.z.string().min(1).max(64).describe("The field to fill (a scheme key of this row)"),value:o.z.union([o.z.string().max(5e4),o.z.number()]).describe("The suggested value"),rationale:o.z.string().max(5e4).optional().describe("The evidence behind a suggested rating; written to the scheme's evidence field")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_suggest_field",async e=>{const{suggestFieldHandler:t}=await Promise.resolve().then(()=>(fc(),Vo));return t(e)})),Sc("atoms_approve_row",{title:"Approve an Analysis Row in a Named Person's Name",description:'Record the sign-off of ONE analysis row, naming who approved it.\n\nRequires editor or admin role. The approver must be a member of the project\'s organization: a\nname that cannot be traced to a member is refused, never stored. The note is optional. Approving\nclears the machine-proposed (✦) markers on the row: the approver takes those values as their own.\nA row whose ratings are incomplete cannot be approved (its level is unset), matching the grid.\n\nThis tool signs off one analysis row. It has nothing to do with change control: it never\napproves, dismisses or applies a suggestion (REQ-CHG-062).\n\nIn any security risk analysis row, E is exploitability: the ease of exploiting the vulnerability across the device lifecycle, never a probability.\nArgs:\n - project_id (string, UUID): Project containing the row\n - row_id (string): Analysis row ID (e.g. "SG-001", "RSK-002", "FM-003", "PF-004")\n - approver_id (string, UUID, optional): the approving member; defaults to the calling user\n - note (string, optional): the note captured with the approval\n\nReturns:\n { row_id, subtype, scheme, approved: true, approved_by, approved_at, approved_note,\n approver_role, computed_level, cleared_markers }\n\nSide effects:\n - Saves the row with stale-write protection: approved, approved_at, approved_by, approved_note\n - Flips every field_source=\'mcp\' entry on the row to \'user\'\n - Logs the approval (previous and new values) to change_history',inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),row_id:o.z.string().min(1).max(20).describe("Analysis row ID (e.g. SG-001, FM-003)"),approver_id:o.z.string().uuid().optional().describe("The approving project member; defaults to the calling user"),note:o.z.string().max(2e3).optional().describe("Optional note captured with the approval")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_approve_row",async e=>{const{approveRowHandler:t}=await Promise.resolve().then(()=>(fc(),Vo));return t(e)})),Sc("atoms_list_scenario_catalogs",{title:"List Operational Scenario Catalogs",description:'List a project\'s operational scenario catalogs: the situational analysis written\nand accepted before a HARA (ISO 26262) starts. Each catalog is a list of entries (operational\nscenarios), each tagged with the functions it applies to.\n\nIn any security risk analysis row, E is exploitability: the ease of exploiting the vulnerability across the device lifecycle, never a probability.\nArgs:\n - project_id (string, UUID): The project to list catalogs from\n - limit (number, optional): Max results, 1-200 (default 50)\n - offset (number, optional): Pagination offset (default 0)\n\nReturns:\n { status: "success", data: [{ id, name, entries: [{id, text, function_tags}], acceptance }],\n meta: { total_count, limit, offset, has_more } }',inputSchema:{project_id:o.z.string().uuid("Must be a valid project UUID").describe("UUID of the project"),limit:o.z.number().int().min(1).max(200).default(50).describe("Max results (default 50, max 200)"),offset:o.z.number().int().min(0).default(0).describe("Pagination offset (default 0)")},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},$c("atoms_list_scenario_catalogs",async e=>{const{listScenarioCatalogsHandler:t}=await Promise.resolve().then(()=>(Oc(),hc));return t(e)})),Sc("atoms_mutate_scenario_catalog",{title:"Mutate an Operational Scenario Catalog",description:"Create/rename/delete a catalog, or add/edit/remove/tag one of its entries, or accept\na catalog. Every op goes through a row-locked database function and returns the STORED list, so a\nwrite can never report success for an entry the row did not retain.\n\nRequires editor or admin role.\n\nIn any security risk analysis row, E is exploitability: the ease of exploiting the vulnerability across the device lifecycle, never a probability.\nArgs:\n - project_id (string, UUID)\n - op: create_catalog | rename_catalog | delete_catalog | add_entry | edit_entry | remove_entry |\n tag_entry | accept_catalog\n - catalog_id (string, optional for create_catalog, which generates one when omitted; required for every other op)\n - name (string): the catalog name (create_catalog, rename_catalog)\n - entry_id (string, required for add_entry/edit_entry/remove_entry/tag_entry; caller-generated)\n - text (string): the entry's operational scenario text (add_entry, optional on edit_entry)\n - function_tags (string[]): the functions this entry applies to; [] = every function\n (add_entry, edit_entry, tag_entry)\n - by (string): the accepter's name (accept_catalog)\n - on (string): the acceptance date (accept_catalog)\n\nReturns:\n { status: \"success\", data: { scenario_catalogs: [...] } }\n\nSide effects:\n - add_entry/edit_entry/remove_entry return an ACCEPTED catalog to draft, keeping the prior\n acceptance in history. tag_entry and rename_catalog do not.",inputSchema:{project_id:o.z.string().uuid().describe("UUID of the project"),op:o.z.enum(["create_catalog","rename_catalog","delete_catalog","add_entry","edit_entry","remove_entry","tag_entry","accept_catalog"]).describe("The mutation to apply"),catalog_id:o.z.string().min(1).max(64).optional().describe("The catalog id (optional for create_catalog: generated when omitted)"),name:o.z.string().min(1).max(100).optional().describe("The catalog name (create_catalog, rename_catalog)"),entry_id:o.z.string().min(1).max(64).optional().describe("The entry id (caller-generated for add_entry)"),text:o.z.string().min(1).max(100).optional().describe("The entry's operational scenario text"),function_tags:o.z.array(o.z.string()).optional().describe("The functions this entry applies to; [] = every function"),by:o.z.string().min(1).max(200).optional().describe("The accepter's name (accept_catalog)"),on:o.z.string().min(1).max(32).optional().describe("The acceptance date (accept_catalog)")},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1}},$c("atoms_mutate_scenario_catalog",async e=>{const{mutateScenarioCatalogHandler:t}=await Promise.resolve().then(()=>(Oc(),hc));return t(e)}))}});B(),U(),Le(),P();var Pc=d.createRequire("undefined"==typeof document?require("url").pathToFileURL(__filename).href:m&&"SCRIPT"===m.tagName.toUpperCase()&&m.src||new URL("index.cjs",document.baseURI).href)("../package.json"),Cc=process.argv.slice(2)[0];(async function(){switch(Cc){case"login":{const{login:e}=await Promise.resolve().then(()=>(Ye(),Ne));try{const{email:t}=await e();process.stderr.write(`\n [32m✓[0m Authenticated as ${t}\n`),process.stderr.write(" [32m✓[0m Credentials saved to ~/.atoms/credentials.json\n\n"),process.stderr.write(" [1mNext steps:[0m\n"),process.stderr.write(" [36m$[0m claude mcp add atoms -- npx -y @atoms-tech/atoms-mcp\n"),process.stderr.write("\n Or add to Claude Desktop (Settings → MCP → Edit Config):\n"),process.stderr.write(' [90m{\n "mcpServers": {\n "atoms": {\n "command": "npx",\n "args": ["-y", "@atoms-tech/atoms-mcp"]\n }\n }\n }[0m\n\n')}catch(e){process.stderr.write(`\n [31m✗[0m ${e instanceof Error?e.message:String(e)}\n\n`),process.exit(1)}break}case"whoami":{const{getValidToken:e}=await Promise.resolve().then(()=>(Re(),ve));try{const{email:t,user_id:r}=await e();process.stderr.write(`\n [32m✓[0m Logged in as ${t}\n [90mUser ID: ${r}[0m\n\n`)}catch(e){process.stderr.write(`\n [31m✗[0m Not authenticated: ${e instanceof Error?e.message:String(e)}\n Run: npx @atoms-tech/atoms-mcp login\n\n`),process.exit(1)}break}case"logout":{const{clearCredentials:e}=await Promise.resolve().then(()=>(oe(),J));await e(),process.stderr.write("Logged out. Credentials removed.\n"),process.stderr.write("Run 'npx @atoms-tech/atoms-mcp login' to authenticate again.\n");break}case"--version":case"-v":process.stderr.write(`@atoms-tech/atoms-mcp v${Pc.version}\n`);break;case"--help":case"-h":process.stderr.write("ATOMS MCP Server — AI agent integration for requirements management\n\nUsage:\n npx @atoms-tech/atoms-mcp Start MCP server (stdio)\n npx @atoms-tech/atoms-mcp login Authenticate with ATOMS.tech\n npx @atoms-tech/atoms-mcp logout Switch account / clear credentials\n npx @atoms-tech/atoms-mcp whoami Show current user\n npx @atoms-tech/atoms-mcp --version Show version\n");break;default:{process.stderr.write("[atoms-mcp] Starting MCP server via stdio...\n");{const{getValidToken:e,readValidToken:t}=await Promise.resolve().then(()=>(Re(),ve)),{runExclusiveAuth:r}=await Promise.resolve().then(()=>(he(),ce)),{login:a}=await Promise.resolve().then(()=>(Ye(),Ne));try{let i;try{i=(await e()).email}catch(e){process.stderr.write(`[atoms-mcp] Could not use the saved session: ${e instanceof Error?e.message:String(e)}\n`),process.stderr.write("[atoms-mcp] Starting login flow...\n");const n=async()=>{const e=await t();if(!e)throw new Error("no valid token yet");return{email:e.email}};i=(await r(n,async()=>({email:(await a()).email}))).email}process.stderr.write(`[atoms-mcp] Authenticated as ${i}\n`)}catch(e){process.stderr.write(`[atoms-mcp] Login failed: ${e instanceof Error?e.message:String(e)}\n[atoms-mcp] Starting server without auth. Tools will fail until you run: npx @atoms-tech/atoms-mcp login\n`)}}try{const t=await qe(),r=Me(),a=await async function(e,t){const r=process.env.ATOMS_MCP_ORG_ID?.trim();if(r)return r;const{data:a,error:i}=await e.from("org_members").select("org_id, joined_at").eq("user_id",t).order("joined_at",{ascending:!0}).limit(1).maybeSingle();return i?(process.stderr.write(`[atoms-mcp] Could not resolve session org: ${i.message}\n`),null):a?.org_id??null}(t,r),i=await async function(e,t,r){if(!r)return{role:null,is_member:!1,effective:$};const{data:a,error:i}=await e.rpc("mcp_resolve_policy",{p_user_id:t,p_org_id:r});return i?(process.stderr.write(`[atoms-mcp] mcp_resolve_policy failed (${i.code}): ${i.message}. Falling back to fail-closed policy.\n`),{role:null,is_member:!1,effective:H}):function(e){if(!e||"object"!=typeof e)return{role:null,is_member:!1,effective:$};const t=e,r="string"==typeof t.role?t.role:null,a=!0===t.is_member,i=t.effective??{},n=(e,t)=>Array.isArray(e)?e.filter(e=>"string"==typeof e):t,s=(e,t)=>"boolean"==typeof e?e:t,o=i.project_scope,c=Array.isArray(o)&&o.length>0?o.filter(e=>"string"==typeof e):null;return{role:r,is_member:a,effective:{read_only:s(i.read_only,$.read_only),lockdown:s(i.lockdown,$.lockdown),block_destructive:s(i.block_destructive,$.block_destructive),block_bulk_import:s(i.block_bulk_import,$.block_bulk_import),cross_project_browse:s(i.cross_project_browse,$.cross_project_browse),allowed_toolsets:n(i.allowed_toolsets,$.allowed_toolsets),forbidden_tools:n(i.forbidden_tools,$.forbidden_tools),project_scope:c,require_confirmation_for:n(i.require_confirmation_for,$.require_confirmation_for)}}}(a)}(t,r,a);e={effective:i.effective,role:i.role,org_id:a},M=e.effective,T=e.org_id,i.is_member&&a?process.stderr.write(`[atoms-mcp] Org policy loaded: org=${a.slice(0,8)}… role=${i.role??"?"} read_only=${i.effective.read_only} lockdown=${i.effective.lockdown}\n`):process.stderr.write("[atoms-mcp] No org membership detected — running with permissive default policy.\n")}catch(e){process.stderr.write(`[atoms-mcp] Could not resolve session policy (${e instanceof Error?e.message:String(e)}). Running with permissive default — RLS still enforces row-level access.\n`)}process.stderr.write(`[atoms-mcp] Tool registry: ${function(){const e=N(),t=L(),r=[];r.push(e?"read-only":"read+write"),null===t?r.push("all toolsets"):(r.push(`toolsets=${[...t.allowed].sort().join(",")||"(none — only always-on tools)"}`),t.unknown.length>0&&r.push(`unknown=${t.unknown.join(",")}`));const a=process.env.ATOMS_MCP_PROJECT_ID?.trim();return a&&r.push(`project_scope=${a.slice(0,8)}…`),("1"===process.env.ATOMS_MCP_LOCKDOWN||"true"===process.env.ATOMS_MCP_LOCKDOWN)&&r.push("lockdown=on"),r.join(" | ")}()}\n`);const{server:t}=await Promise.resolve().then(()=>(Dc(),qc)),r=new p.StdioServerTransport;await t.connect(r),process.stderr.write("[atoms-mcp] MCP server running. Waiting for tool calls...\n")}}var e})().catch(e=>{process.stderr.write(`[atoms-mcp] Fatal: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)});
|