@clipform/mcp-server 4.6.0 → 4.7.0
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/README.md +2 -2
- package/README.template.md +2 -2
- package/dist/api-client.js +2 -2
- package/dist/auth-context.d.ts +2 -0
- package/dist/auth-context.js +1 -1
- package/dist/{chunk-C66UR4R6.js → chunk-D4Q5ZWFO.js} +3 -3
- package/dist/{chunk-74LQFWVD.js → chunk-EFPJA3JE.js} +28 -10
- package/dist/chunk-EFPJA3JE.js.map +1 -0
- package/dist/{chunk-BV3KA6HC.js → chunk-ENVV65JX.js} +1 -1
- package/dist/chunk-ENVV65JX.js.map +1 -0
- package/dist/{chunk-ZJUVMV2Z.js → chunk-HV5FRLYK.js} +91 -31
- package/dist/chunk-HV5FRLYK.js.map +1 -0
- package/dist/{chunk-UHMV3BDZ.js → chunk-VSSV3IOU.js} +4 -4
- package/dist/{chunk-FYY23O3F.js → chunk-XC3YMWCN.js} +32 -7
- package/dist/chunk-XC3YMWCN.js.map +1 -0
- package/dist/index.js +6 -6
- package/dist/prompts.js +4 -4
- package/dist/resources.js +4 -4
- package/dist/server.d.ts +10 -4
- package/dist/server.js +6 -6
- package/package.json +3 -3
- package/server.json +2 -2
- package/dist/chunk-74LQFWVD.js.map +0 -1
- package/dist/chunk-BV3KA6HC.js.map +0 -1
- package/dist/chunk-FYY23O3F.js.map +0 -1
- package/dist/chunk-ZJUVMV2Z.js.map +0 -1
- /package/dist/{chunk-C66UR4R6.js.map → chunk-D4Q5ZWFO.js.map} +0 -0
- /package/dist/{chunk-UHMV3BDZ.js.map → chunk-VSSV3IOU.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
createServer,
|
|
5
5
|
process as process2,
|
|
6
6
|
serializeMessage
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-HV5FRLYK.js";
|
|
8
|
+
import "./chunk-D4Q5ZWFO.js";
|
|
9
|
+
import "./chunk-VSSV3IOU.js";
|
|
10
|
+
import "./chunk-EFPJA3JE.js";
|
|
11
|
+
import "./chunk-XC3YMWCN.js";
|
|
12
|
+
import "./chunk-ENVV65JX.js";
|
|
13
13
|
import {
|
|
14
14
|
setMcpVersion
|
|
15
15
|
} from "./chunk-PBQ5BQWD.js";
|
package/dist/prompts.js
CHANGED
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
normalizeQuestionCount,
|
|
9
9
|
normalizeResponseFormat,
|
|
10
10
|
registerPrompts
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-D4Q5ZWFO.js";
|
|
12
|
+
import "./chunk-EFPJA3JE.js";
|
|
13
|
+
import "./chunk-XC3YMWCN.js";
|
|
14
|
+
import "./chunk-ENVV65JX.js";
|
|
15
15
|
import "./chunk-PBQ5BQWD.js";
|
|
16
16
|
import "./chunk-PZ5AY32C.js";
|
|
17
17
|
export {
|
package/dist/resources.js
CHANGED
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
QUIZ_VARIANTS,
|
|
4
4
|
getGuideUri,
|
|
5
5
|
registerResources
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-VSSV3IOU.js";
|
|
7
|
+
import "./chunk-EFPJA3JE.js";
|
|
8
|
+
import "./chunk-XC3YMWCN.js";
|
|
9
|
+
import "./chunk-ENVV65JX.js";
|
|
10
10
|
import "./chunk-PBQ5BQWD.js";
|
|
11
11
|
import "./chunk-PZ5AY32C.js";
|
|
12
12
|
export {
|
package/dist/server.d.ts
CHANGED
|
@@ -63,6 +63,12 @@ interface CreateServerOptions {
|
|
|
63
63
|
* existing client unaffected).
|
|
64
64
|
*/
|
|
65
65
|
toolsets?: readonly McpToolset[];
|
|
66
|
+
/**
|
|
67
|
+
* Whether the connector request carried a valid OAuth token (#4847). `false`
|
|
68
|
+
* serves the guest welcome (offers "sign in"); `true` or undefined (stdio,
|
|
69
|
+
* every existing caller) keeps the control copy.
|
|
70
|
+
*/
|
|
71
|
+
signedIn?: boolean;
|
|
66
72
|
}
|
|
67
73
|
/**
|
|
68
74
|
* Lands directly in the client's system prompt. This is the only high-level
|
|
@@ -80,11 +86,11 @@ interface CreateServerOptions {
|
|
|
80
86
|
* text/markdown only (no host-specific widget), so it renders on both ChatGPT
|
|
81
87
|
* and Claude, and it explicitly defers to the existing brief-driven behaviour
|
|
82
88
|
* below when the user already said what they want.
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
89
|
+
* The "control" copy, or the guest variant when `signedIn === false` (#4847) -
|
|
90
|
+
* see lib/onboarding-copy.ts's module doc for why the PostHog
|
|
91
|
+
* `connector-onboarding-copy` flag can't be resolved yet at this point.
|
|
86
92
|
*/
|
|
87
|
-
declare function buildInstructions(caps: ServerCaps): string;
|
|
93
|
+
declare function buildInstructions(caps: ServerCaps, signedIn?: boolean): string;
|
|
88
94
|
declare function createServer(options?: CreateServerOptions): McpServer;
|
|
89
95
|
|
|
90
96
|
export { type CreateServerOptions, MCP_SERVER_CARD_IDENTITY, MCP_SERVER_IDENTITY, RENDER_TIMING, buildInstructions, createServer };
|
package/dist/server.js
CHANGED
|
@@ -7,12 +7,12 @@ import {
|
|
|
7
7
|
createMcpHandler,
|
|
8
8
|
createServer,
|
|
9
9
|
isLegacyRequest
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-HV5FRLYK.js";
|
|
11
|
+
import "./chunk-D4Q5ZWFO.js";
|
|
12
|
+
import "./chunk-VSSV3IOU.js";
|
|
13
|
+
import "./chunk-EFPJA3JE.js";
|
|
14
|
+
import "./chunk-XC3YMWCN.js";
|
|
15
|
+
import "./chunk-ENVV65JX.js";
|
|
16
16
|
import "./chunk-PBQ5BQWD.js";
|
|
17
17
|
import "./chunk-PZ5AY32C.js";
|
|
18
18
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clipform/mcp-server",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"mcpName": "io.github.Clipform/mcp-server",
|
|
5
5
|
"description": "MCP server for building and managing Clipform video forms",
|
|
6
6
|
"license": "MIT",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"zod": "^4.2.0",
|
|
66
66
|
"@clipform/content": "0.1.0",
|
|
67
67
|
"@clipform/mcp-content": "0.0.0",
|
|
68
|
-
"@vid-master/
|
|
69
|
-
"@vid-master/
|
|
68
|
+
"@vid-master/eval-kit": "1.0.0",
|
|
69
|
+
"@vid-master/form-sources": "1.0.0"
|
|
70
70
|
},
|
|
71
71
|
"scripts": {
|
|
72
72
|
"start": "node dist/index.js",
|
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.Clipform/mcp-server",
|
|
4
4
|
"title": "Clipform",
|
|
5
5
|
"description": "Interactive video forms that capture authentic responses. Build engaging forms in minutes.",
|
|
6
|
-
"version": "4.
|
|
6
|
+
"version": "4.7.0",
|
|
7
7
|
"websiteUrl": "https://www.clipform.io",
|
|
8
8
|
"icons": [
|
|
9
9
|
{
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"registryType": "npm",
|
|
24
24
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
25
25
|
"identifier": "@clipform/mcp-server",
|
|
26
|
-
"version": "4.
|
|
26
|
+
"version": "4.7.0",
|
|
27
27
|
"runtimeHint": "npx",
|
|
28
28
|
"transport": {
|
|
29
29
|
"type": "stdio"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/anon-notice.ts","../src/lib/caps.ts","../src/lib/session-context.ts"],"sourcesContent":["import { getMcpAuth } from \"./auth-context.js\";\n\n/**\n * #4650: anonymous (preview) accounts are temporary. Date-free by design -\n * the purge is not enforced yet, so no expiry date or day count is promised\n * (exact dates are follow-up #4662). Defined once; every anonymous-lane\n * surface (create/update_form text, whoami, session resource) reuses it.\n */\nexport const ANON_PREVIEW_NOTICE =\n \"Preview accounts are temporary - claim yours to keep your forms.\";\n\n/** The notice for an anonymous MCP caller, or null for everyone else. */\nexport function anonPreviewNotice(): string | null {\n return getMcpAuth()?.is_anonymous ? ANON_PREVIEW_NOTICE : null;\n}\n\n/**\n * #4845: an anonymous build's link is a private preview, never a respondent\n * link (answers are not saved). Wording defined once; create_form, get_form,\n * the format-form link label and the output-schema descriptions all reuse it.\n */\nexport const ANON_PREVIEW_LINK_LABEL = \"Preview (answers not saved)\";\n\nexport const ANON_PREVIEW_ONLY_NOTICE =\n 'The form is ready as a preview. To go live, say \"publish it\" - that prompts sign-in and keeps this chat connected to your account. In the preview, answers are not saved: do not share the preview link with respondents.';\n\nexport const PREVIEW_URL_DESCRIPTION =\n \"Private preview of the form. Answers are not saved, so do not share it with respondents.\";\n\nexport const CLAIM_URL_DESCRIPTION =\n \"One-time link to sign up, keep this form and publish it.\";\n\n/**\n * #5158: sign-in happens in a popup the model never sees, so after an adopt\n * (apps/api oauth-adopt.ts) the tool result tells it the form is now the\n * user's. Prepended by the server.ts registerTool wrapper.\n */\nexport const ADOPTED_FORM_NOTICE =\n \"Signed in - this form moved from the temporary preview account into your Clipform account, so it is yours now. The earlier claim link is no longer needed.\";\n\n/** Prepend ADOPTED_FORM_NOTICE to a successful result for a just-adopted form. content only; structuredContent untouched. */\nexport function withAdoptedNotice<T>(result: T, args: unknown): T {\n const formId = (args as { form_id?: unknown } | undefined)?.form_id;\n if (typeof formId !== \"string\") return result;\n const adopted = getMcpAuth()?.adopted_form_ids;\n if (!adopted || !adopted.map((id) => id.toLowerCase()).includes(formId.toLowerCase())) return result;\n const r = result as { isError?: boolean; content?: unknown } | undefined;\n if (!r || r.isError || !Array.isArray(r.content)) return result;\n return { ...r, content: [{ type: \"text\", text: ADOPTED_FORM_NOTICE }, ...r.content] } as T;\n}\n\n/** True for a viewer link in preview mode (an anonymous caller's draft). */\nexport function isPreviewViewerUrl(url: unknown): boolean {\n return typeof url === \"string\" && url.includes(\"mode=preview\");\n}\n","import type { McpToolset } from \"@vid-master/config\";\n\n/**\n * What a connected surface can actually do, derived from the connect-time\n * toolset filter (#3309) so the guidance layers (server instructions, prompt\n * recipes, `clipform_get_workflow`, craft guides) never reference a tool the\n * surface doesn't register (#3681).\n *\n * The Claude Connectors Directory surface (`mcp.clipform.io/forms`) is\n * account + forms only: no media generation and no research helpers. A\n * workflow that still says \"call clipform_generate_tts\" there sends the model\n * at a tool that doesn't exist, which shows up in review as failed tool\n * calls. Every guidance producer takes a ServerCaps and gates on it.\n */\nexport interface ServerCaps {\n /** Narration, video, image, music, render, and asset tools are registered. */\n media: boolean;\n /** News lookup and the generation audit log are registered. */\n research: boolean;\n /** Form tools (create, edit, publish) are registered. */\n forms: boolean;\n}\n\n/** Every toolset registered - stdio/npx and the bare remote endpoint. */\nexport const FULL_CAPS: ServerCaps = Object.freeze({ media: true, research: true, forms: true });\n\nexport function capsFromToolsets(toolsets?: readonly McpToolset[]): ServerCaps {\n if (toolsets === undefined) return FULL_CAPS;\n return {\n media: toolsets.includes(\"media\"),\n research: toolsets.includes(\"research\"),\n forms: toolsets.includes(\"forms\"),\n };\n}\n","import { BUSINESS, PLAN_LIMITS, CURRENT_PLAN_VERSIONS } from \"@vid-master/config\";\nimport { callApi } from \"./api-client.js\";\nimport { ANON_PREVIEW_NOTICE } from \"./anon-notice.js\";\nimport { getMcpAuth } from \"./auth-context.js\";\n\nexport type AuthMode = \"authenticated\" | \"anonymous\";\n\nexport interface SessionContextResult {\n text: string;\n authMode: AuthMode;\n}\n\nexport async function getSessionContextWithAuth(): Promise<SessionContextResult> {\n let me: any;\n const authCtx = getMcpAuth();\n if (authCtx?.loadInternalContent && !authCtx.api_key) {\n // #4657: the no-workspace anon lane (get_workflow with no mint) has no\n // workspace for /me to describe, so plan caps default to Free.\n const free = PLAN_LIMITS[CURRENT_PLAN_VERSIONS[0]];\n me = {\n auth_mode: \"anonymous\",\n plan: {\n name: free.name,\n tier: free.tier,\n node_limit: free.node_limit,\n form_limit: free.form_limit ?? null,\n custom_theme: free.custom_theme,\n },\n };\n } else {\n let result;\n try {\n result = await callApi(\"/me\", { method: \"GET\" });\n } catch {\n return { text: \"\", authMode: \"anonymous\" };\n }\n if (!result.ok) return { text: \"\", authMode: \"anonymous\" };\n me = result.data as any;\n }\n // The API's auth_mode is \"api_key\" | \"oauth\" | \"session\" | \"anonymous\".\n // Anything credentialed counts as authenticated - the old `=== \"oauth\"`\n // check matched nothing the API returns, so API-key sessions were labelled\n // anonymous with a \"sign in to unlock\" nudge right next to their real\n // plan (#412 F6).\n // The anon MCP lane runs as a minted bot key (/me says \"api_key\"), so the\n // auth context is the truth for anonymous (#4653).\n const apiAuthMode: string = getMcpAuth()?.is_anonymous\n ? \"anonymous\"\n : ((me.auth_mode as string) ?? \"anonymous\");\n const authMode: AuthMode = apiAuthMode === \"anonymous\" ? \"anonymous\" : \"authenticated\";\n const plan = me.plan;\n const lines: string[] = [];\n\n lines.push(\"## Your Session\");\n if (authMode === \"authenticated\") {\n lines.push(\n apiAuthMode === \"api_key\"\n ? \"Auth: API key (workspace-scoped)\"\n : \"Auth: signed in (connected to a Clipform account)\"\n );\n // Workspace NAME only - the workspace UUID and the raw user_id/company_id\n // are internal database identifiers the model never needs (response\n // hygiene / disclosure guard, #2976).\n lines.push(`Workspace: ${me.workspace?.name ?? \"Unknown\"}`);\n } else {\n lines.push(\"Auth: anonymous (not connected to a Clipform account)\");\n if (me.workspace) {\n lines.push(`Workspace: ${me.workspace.name}`);\n }\n }\n lines.push(`Plan: ${plan.name} (tier ${plan.tier})`);\n if (plan.node_limit !== null) {\n lines.push(`Question limit: ${plan.node_limit} per form`);\n } else {\n lines.push(\"Questions: unlimited\");\n }\n if (plan.form_limit !== null) {\n lines.push(`Form limit: ${plan.form_limit} forms`);\n } else {\n lines.push(\"Forms: unlimited\");\n }\n if (plan.custom_theme === false) {\n lines.push(\"Custom themes: not available on this plan\");\n }\n if (authMode === \"anonymous\") {\n // Neutral statement of fact, no upsell/CTA copy (commerce sweep, #2976):\n // an anonymous session's forms are not tied to an account until one signs in.\n lines.push(`\\nAnonymous session: forms are not tied to a Clipform account until you sign in.`);\n lines.push(ANON_PREVIEW_NOTICE);\n }\n\n return { text: lines.join(\"\\n\"), authMode };\n}\n\nexport async function getSessionContext(): Promise<string> {\n const { text } = await getSessionContextWithAuth();\n return text;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/auth-context.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\n\n/**\n * Identity of the end-user a given MCP request is acting on behalf of.\n *\n * Populated by the in-process host (apps/api) after validating an OAuth\n * Bearer on /mcp. The MCP tool layer reads it to decide which workspace\n * forms should land in and which plan tier gates apply.\n *\n * NOT populated when the server runs as a local stdio process (npx) - in\n * that mode tools fall back to edit-token based auth.\n */\nexport interface McpAuthContext {\n user_id: string;\n workspace_id: string;\n scopes: string[];\n tool_name?: string;\n api_key?: string;\n // Set when the human behind this tool call is an anonymous Supabase\n // session (apps/api's app-chat lane, #2983/#2984) - api-client.ts's\n // getAuthHeaders reads it to mark the outgoing bot-key HTTP request so the\n // API can propagate the restriction even though the request itself\n // authenticates with an API key, not a JWT.\n is_anonymous?: boolean;\n // Set on the OAuth branch of apps/api's mcp/handler.ts when this request's\n // bot API key was resolved on behalf of an OAuth-connected user (#3716) -\n // api-client.ts's getAuthHeaders reads it to mark the outgoing request so\n // /v1/me can report auth_mode \"oauth\" instead of the generic \"api_key\" the\n // bot-key lane would otherwise get, even though the request itself\n // authenticates with an API key, not an OAuth token.\n auth_via?: \"oauth\";\n // #3330 review fix (blocker 1): an HMAC proof (apps/api's mcp-mint-proof.ts)\n // that THIS exact request's #1415 anon lane freshly minted a brand-new anon\n // identity for `workspace_id` - set only on the mint branch of\n // resolveAnonymousMcpAuth, never when auth was resolved via an inbound\n // form_id (even the caller's own). Lets create-form.ts safely offer a\n // same-UID claim link without ever deriving \"who to bind it to\" from a\n // body-supplied id, which would let a batch smuggling another tenant's\n // form_id alongside a create_form call mint a claim token for THAT\n // tenant's identity instead of the caller's own.\n mcp_mint_proof?: string;\n // #4090: sibling of mcp_mint_proof, set when the anon lane REUSED a\n // server-verified session binding (#4652) instead of minting. HMAC-bound\n // to the bound {workspace, uid}; lets create-form.ts re-surface the claim\n // link on later forms in the session.\n mcp_session_proof?: string;\n // #3822: set ONLY on the OAuth branch of apps/api's mcp/handler.ts. Lets a\n // single tool call target another workspace the connected user belongs to\n // (per-call, never per-request - see the #3330 note in the package\n // CLAUDE.md). The host re-checks membership on every call and returns that\n // workspace's own bot key, or null to mean \"act in the default workspace\n // unchanged\" (target is the default, or a form_id the user can't reach -\n // the API then answers exactly as it did before targeting existed). Anon + stdio contexts never carry it, so they\n // stay pinned to one workspace.\n resolveWorkspace?: (target: {\n workspace_id?: string;\n form_id?: string;\n }) => Promise<{ workspace_id: string; workspace_name: string; api_key: string } | null>;\n // #3822: the workspaces the connected user may target (same set\n // resolveWorkspace accepts). OAuth lane only.\n listWorkspaces?: () => Promise<{ id: string; name: string }[]>;\n // #5158: OAuth lane only. The anonymous form ids THIS request just adopted\n // into the signed-in user's workspace (apps/api oauth-adopt.ts, #4638). The\n // sign-in happens in a popup the model never sees, so the tool wrapper uses\n // this to tell it the form is now the user's. INFORMATIONAL ONLY - derived\n // from the server-side adopt outcome of this same request, never from client\n // input, and never read for an access decision.\n adopted_form_ids?: string[];\n // #3822: the workspace picked at authorize time - what a call with no\n // `workspace_id` acts in. OAuth lane only (workspace_id above is swapped\n // per call when a target is resolved, this never is).\n default_workspace_id?: string;\n // #4657: set ONLY by apps/api's mcp/handler.ts for an anonymous request made\n // entirely of `anon_no_workspace` tools (get_guide / get_workflow /\n // list_assets). That lane has NO credential of any kind: the static content\n // is served in-process through this loader instead of an HTTP callApi, so no\n // secret or key ever sits in the anonymous context. Tools use it when\n // present and callApi otherwise (stdio / published behaviour unchanged).\n // #4657: decoded PostHog MCP `$session_id` from the inbound Mcp-Session-Id -\n // ANALYTICS ONLY (stamped on funnel events). api-client.ts forwards it as\n // MCP_SESSION_ID_HEADER; nothing about identity or access derives from it.\n mcp_session_id?: string;\n loadInternalContent?: (\n kind: \"guides\" | \"assets\",\n params?: { type?: string }\n ) => Promise<Record<string, unknown>>;\n}\n\n/**\n * Marker header the in-process MCP tool layer sets on its outgoing API\n * requests (api-client.ts) when `is_anonymous` is true, so an anonymous\n * app-chat session can't launder itself into an unrestricted API-key caller\n * by asking the bot to publish/mint credentials on its behalf. apps/api's\n * authMiddleware / workspaceAccessMiddleware / requireWorkspaceWriteAuth\n * read this on the API-key path and set isAnonymous accordingly - trusting\n * it is safe because it can only RESTRICT a caller: a real integration that\n * sets this header on its own API-key requests only denies itself, it can\n * never use it to gain anonymous-JWT privileges it doesn't already have.\n */\nexport const MCP_ANONYMOUS_HEADER = \"x-clipform-anonymous\";\n\n/**\n * Marker header the in-process MCP tool layer sets on its outgoing API\n * requests (api-client.ts) when `auth_via` is \"oauth\", so /v1/me can report\n * `auth_mode: \"oauth\"` for an OAuth-connected user instead of the generic\n * \"api_key\" the bot-key lane would otherwise get (#3716). Label-only: it\n * changes nothing but the reported string. Trusting it from the wire is\n * safe because a direct API-key caller who sets this header on their own\n * request only mislabels their own whoami output - they gain no privilege,\n * exactly the same reasoning as MCP_ANONYMOUS_HEADER above (except that\n * header can only restrict, and this one can only relabel).\n */\nexport const MCP_AUTH_VIA_HEADER = \"x-clipform-auth-via\";\n\n/**\n * Carries `mcp_mint_proof` on the same outgoing bot-key request. Unlike\n * MCP_ANONYMOUS_HEADER (a boolean that can only ever RESTRICT), this header's\n * VALUE matters - it names an identity a claim token gets bound to - so its\n * value is HMAC-signed (mcp-mint-proof.ts) rather than trusted raw: a direct\n * external caller reaching `POST /v1/forms` with any valid workspace API key\n * could otherwise hand-set an unsigned \"mint uid\" header to an arbitrary\n * victim's uid themselves (#3330 review).\n */\nexport const MCP_MINT_PROOF_HEADER = \"x-clipform-mcp-mint-proof\";\n\n/** #4090: carries `mcp_session_proof` (same trust model as MCP_MINT_PROOF_HEADER: HMAC-verified server-side, never trusted raw). */\nexport const MCP_SESSION_PROOF_HEADER = \"x-clipform-mcp-session-proof\";\n\n/** Carries `mcp_session_id` to the API for funnel analytics only (#4657). */\nexport const MCP_SESSION_ID_HEADER = \"x-clipform-mcp-session-id\";\n\nconst storage = new AsyncLocalStorage<McpAuthContext>();\n\nexport function runWithMcpAuth<T>(ctx: McpAuthContext, fn: () => Promise<T> | T): Promise<T> | T {\n return storage.run(ctx, fn);\n}\n\nexport function getMcpAuth(): McpAuthContext | undefined {\n return storage.getStore();\n}\n\nexport function runWithMcpTool<T>(toolName: string, fn: () => Promise<T> | T): Promise<T> | T {\n const parent = storage.getStore();\n if (!parent) return fn();\n return storage.run({ ...parent, tool_name: toolName }, fn);\n}\n"],"mappings":";AAAA,SAAS,yBAAyB;AAmG3B,IAAM,uBAAuB;AAa7B,IAAM,sBAAsB;AAW5B,IAAM,wBAAwB;AAG9B,IAAM,2BAA2B;AAGjC,IAAM,wBAAwB;AAErC,IAAM,UAAU,IAAI,kBAAkC;AAE/C,SAAS,eAAkB,KAAqB,IAA0C;AAC/F,SAAO,QAAQ,IAAI,KAAK,EAAE;AAC5B;AAEO,SAAS,aAAyC;AACvD,SAAO,QAAQ,SAAS;AAC1B;AAEO,SAAS,eAAkB,UAAkB,IAA0C;AAC5F,QAAM,SAAS,QAAQ,SAAS;AAChC,MAAI,CAAC,OAAQ,QAAO,GAAG;AACvB,SAAO,QAAQ,IAAI,EAAE,GAAG,QAAQ,WAAW,SAAS,GAAG,EAAE;AAC3D;","names":[]}
|