@assistant-ui/react-mcp 0.0.21 → 0.1.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/dist/auth/createOAuthProvider.d.ts +1 -1
- package/dist/auth/createOAuthProvider.js.map +1 -1
- package/dist/auth/types.d.ts +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/mcp-scope.d.ts +32 -1
- package/dist/mcp-scope.d.ts.map +1 -1
- package/dist/primitives/elicitation/McpElicitationPrimitiveAccept.d.ts +13 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveAccept.d.ts.map +1 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveAccept.js +106 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveAccept.js.map +1 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveCancel.d.ts +13 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveCancel.d.ts.map +1 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveCancel.js +43 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveCancel.js.map +1 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveDecline.d.ts +13 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveDecline.d.ts.map +1 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveDecline.js +43 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveDecline.js.map +1 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveError.d.ts +13 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveError.d.ts.map +1 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveError.js +38 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveError.js.map +1 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveFields.d.ts +12 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveFields.d.ts.map +1 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveFields.js +53 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveFields.js.map +1 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveItems.d.ts +13 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveItems.d.ts.map +1 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveItems.js +96 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveItems.js.map +1 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveMessage.d.ts +13 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveMessage.d.ts.map +1 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveMessage.js +29 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveMessage.js.map +1 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveRoot.d.ts +13 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveRoot.d.ts.map +1 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveRoot.js +28 -0
- package/dist/primitives/elicitation/McpElicitationPrimitiveRoot.js.map +1 -0
- package/dist/primitives/elicitation/context.d.ts +21 -0
- package/dist/primitives/elicitation/context.d.ts.map +1 -0
- package/dist/primitives/elicitation/context.js +21 -0
- package/dist/primitives/elicitation/context.js.map +1 -0
- package/dist/primitives/elicitation/initialElicitationDraft.d.ts +5 -0
- package/dist/primitives/elicitation/initialElicitationDraft.d.ts.map +1 -0
- package/dist/primitives/elicitation/initialElicitationDraft.js +27 -0
- package/dist/primitives/elicitation/initialElicitationDraft.js.map +1 -0
- package/dist/primitives/elicitation/prepareElicitationContent.d.ts +9 -0
- package/dist/primitives/elicitation/prepareElicitationContent.d.ts.map +1 -0
- package/dist/primitives/elicitation/prepareElicitationContent.js +46 -0
- package/dist/primitives/elicitation/prepareElicitationContent.js.map +1 -0
- package/dist/primitives/elicitation.d.ts +15 -0
- package/dist/primitives/elicitation.d.ts.map +1 -0
- package/dist/primitives/elicitation.js +27 -0
- package/dist/primitives/elicitation.js.map +1 -0
- package/dist/resources/McpManagerResource.d.ts.map +1 -1
- package/dist/resources/McpManagerResource.js +17 -3
- package/dist/resources/McpManagerResource.js.map +1 -1
- package/dist/resources/McpServerResource.d.ts +4 -0
- package/dist/resources/McpServerResource.d.ts.map +1 -1
- package/dist/resources/McpServerResource.js +153 -42
- package/dist/resources/McpServerResource.js.map +1 -1
- package/dist/resources/storage/McpLocalStorage.js +1 -1
- package/dist/resources/storage/McpLocalStorage.js.map +1 -1
- package/dist/resources/validateElicitationContent.d.ts +9 -0
- package/dist/resources/validateElicitationContent.d.ts.map +1 -0
- package/dist/resources/validateElicitationContent.js +47 -0
- package/dist/resources/validateElicitationContent.js.map +1 -0
- package/package.json +7 -6
- package/src/auth/createOAuthProvider.ts +2 -2
- package/src/auth/types.ts +1 -1
- package/src/index.ts +4 -0
- package/src/mcp-scope.ts +32 -0
- package/src/primitives/elicitation/McpElicitationPrimitiveAccept.tsx +48 -0
- package/src/primitives/elicitation/McpElicitationPrimitiveCancel.tsx +37 -0
- package/src/primitives/elicitation/McpElicitationPrimitiveDecline.tsx +37 -0
- package/src/primitives/elicitation/McpElicitationPrimitiveError.tsx +31 -0
- package/src/primitives/elicitation/McpElicitationPrimitiveFields.tsx +56 -0
- package/src/primitives/elicitation/McpElicitationPrimitiveItems.tsx +58 -0
- package/src/primitives/elicitation/McpElicitationPrimitiveMessage.tsx +26 -0
- package/src/primitives/elicitation/McpElicitationPrimitiveRoot.tsx +24 -0
- package/src/primitives/elicitation/context.tsx +45 -0
- package/src/primitives/elicitation/initialElicitationDraft.test.ts +34 -0
- package/src/primitives/elicitation/initialElicitationDraft.ts +39 -0
- package/src/primitives/elicitation/prepareElicitationContent.test.ts +415 -0
- package/src/primitives/elicitation/prepareElicitationContent.ts +116 -0
- package/src/primitives/elicitation.ts +14 -0
- package/src/resources/McpManagerResource.test.ts +173 -3
- package/src/resources/McpManagerResource.ts +37 -3
- package/src/resources/McpServerResource.test.ts +735 -4
- package/src/resources/McpServerResource.ts +237 -29
- package/src/resources/storage/McpLocalStorage.ts +1 -1
- package/src/resources/validateElicitationContent.test.ts +123 -0
- package/src/resources/validateElicitationContent.ts +79 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpLocalStorage.js","names":["resource","OAuthClientInformationFullSchema","OAuthTokensSchema","MCPAuthConfig","MCPCustomServerRecord","MCPPersistedAuthState","assertValidServerId","MCPStorage","McpLocalStorageOptions","keyPrefix","storage","Storage","resolveStorage","opts","globalThis","localStorage","isRecord","value","Record","Array","isArray","isOptionalString","undefined","isNonEmptyString","trim","length","isOptionalNonEmptyString","isOptionalStringArray","every","item","isOptionalConnectionTimeout","Number","isFinite","isValidServerId","id","isMCPAuthConfig","auth","type","token","scopes","authorizationEndpoint","tokenEndpoint","registrationEndpoint","clientId","clientSecret","isCustomServerRecord","name","url","createdAt","connectionTimeout","normalizeCustomServerRecord","record","normalizeCustomServerRecords","flatMap","normalizeOAuthTokens","result","safeParse","success","data","normalizeClientInformation","normalizePersistedAuthState","state","codeVerifier","tokens","clientInformation","Object","keys","useMcpLocalStorage","prefix","customServersKey","authKey","read","key","fallback","T","raw","getItem","JSON","parse","write","setItem","stringify","remove","removeItem","loadCustomServers","saveCustomServers","records","loadAuthState","saveAuthState","clearAuthState","McpLocalStorage"],"sources":["../../../src/resources/storage/McpLocalStorage.ts"],"sourcesContent":["import { resource } from \"@assistant-ui/tap\";\nimport {\n OAuthClientInformationFullSchema,\n OAuthTokensSchema,\n} from \"@modelcontextprotocol/sdk/shared/auth.js\";\nimport type { MCPAuthConfig, MCPCustomServerRecord } from \"../../mcp-scope\";\nimport type { MCPPersistedAuthState } from \"../../auth/types\";\nimport { assertValidServerId } from \"../../utils/serverId\";\nimport type { MCPStorage } from \"./types\";\n\nexport type McpLocalStorageOptions = {\n /** Namespace prefix for keys. Default \"aui-mcp\". */\n keyPrefix?: string;\n /** Override the underlying Storage. Defaults to globalThis.localStorage. */\n storage?: Storage;\n};\n\nfunction resolveStorage(opts: McpLocalStorageOptions): Storage | null {\n if (opts.storage) return opts.storage;\n if (typeof globalThis !== \"undefined\" && \"localStorage\" in globalThis) {\n try {\n return (globalThis as { localStorage: Storage }).localStorage;\n } catch {\n return null;\n }\n }\n return null;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isOptionalString = (value: unknown): value is string | undefined =>\n value === undefined || typeof value === \"string\";\n\nconst isNonEmptyString = (value: unknown): value is string =>\n typeof value === \"string\" && value.trim().length > 0;\n\nconst isOptionalNonEmptyString = (\n value: unknown,\n): value is string | undefined =>\n value === undefined || isNonEmptyString(value);\n\nconst isOptionalStringArray = (value: unknown): value is string[] | undefined =>\n value === undefined ||\n (Array.isArray(value) && value.every((item) => typeof item === \"string\"));\n\nconst isOptionalConnectionTimeout = (\n value: unknown,\n): value is number | undefined =>\n value === undefined ||\n (typeof value === \"number\" && Number.isFinite(value) && value >= 0);\n\nconst isValidServerId = (id: string): boolean => {\n try {\n assertValidServerId(id);\n return true;\n } catch {\n return false;\n }\n};\n\nconst isMCPAuthConfig = (auth: unknown): auth is MCPAuthConfig => {\n if (!isRecord(auth)) return false;\n\n switch (auth.type) {\n case \"none\":\n return true;\n case \"bearer\":\n return isOptionalNonEmptyString(auth.token);\n case \"oauth\":\n return (\n isOptionalStringArray(auth.scopes) &&\n isOptionalString(auth.authorizationEndpoint) &&\n isOptionalString(auth.tokenEndpoint) &&\n isOptionalString(auth.registrationEndpoint) &&\n isOptionalString(auth.clientId) &&\n isOptionalString(auth.clientSecret)\n );\n default:\n return false;\n }\n};\n\nconst isCustomServerRecord = (\n value: unknown,\n): value is MCPCustomServerRecord => {\n if (!isRecord(value)) return false;\n if (typeof value.id !== \"string\" || !isValidServerId(value.id)) {\n return false;\n }\n return (\n isNonEmptyString(value.name) &&\n isNonEmptyString(value.url) &&\n Number.isFinite(value.createdAt) &&\n isMCPAuthConfig(value.auth) &&\n isOptionalConnectionTimeout(value.connectionTimeout)\n );\n};\n\nconst normalizeCustomServerRecord = (\n value: unknown,\n): MCPCustomServerRecord | null => {\n if (isCustomServerRecord(value)) return value;\n if (!isRecord(value)) return null;\n\n const record = { ...value };\n delete record.connectionTimeout;\n return isCustomServerRecord(record) ? record : null;\n};\n\nexport const normalizeCustomServerRecords = (\n value: unknown,\n): MCPCustomServerRecord[] => {\n if (!Array.isArray(value)) return [];\n return value.flatMap((item) => {\n const record = normalizeCustomServerRecord(item);\n return record === null ? [] : [record];\n });\n};\n\nconst normalizeOAuthTokens = (\n value: unknown,\n): MCPPersistedAuthState[\"tokens\"] | undefined => {\n const result = OAuthTokensSchema.safeParse(value);\n return result.success ? result.data : undefined;\n};\n\nconst normalizeClientInformation = (\n value: unknown,\n): MCPPersistedAuthState[\"clientInformation\"] | undefined => {\n const result = OAuthClientInformationFullSchema.safeParse(value);\n return result.success ? result.data : undefined;\n};\n\nexport const normalizePersistedAuthState = (\n value: unknown,\n): MCPPersistedAuthState | null => {\n if (!isRecord(value)) return null;\n\n const state: MCPPersistedAuthState = {};\n if (isNonEmptyString(value.token)) state.token = value.token;\n if (isNonEmptyString(value.codeVerifier)) {\n state.codeVerifier = value.codeVerifier;\n }\n\n const tokens = normalizeOAuthTokens(value.tokens);\n if (tokens) state.tokens = tokens;\n\n const clientInformation = normalizeClientInformation(value.clientInformation);\n if (clientInformation) state.clientInformation = clientInformation;\n\n return Object.keys(state).length > 0 ? state : null;\n};\n\nconst useMcpLocalStorage = (opts: McpLocalStorageOptions = {}): MCPStorage => {\n const prefix = opts.keyPrefix ?? \"aui-mcp\";\n const customServersKey = `${prefix}:custom-servers`;\n const authKey = (id: string) => `${prefix}:auth:${id}`;\n const storage = resolveStorage(opts);\n\n const read = <T>(key: string, fallback: T): T => {\n if (!storage) return fallback;\n try {\n const raw = storage.getItem(key);\n if (raw == null) return fallback;\n return JSON.parse(raw) as T;\n } catch {\n return fallback;\n }\n };\n\n const write = (key: string, value: unknown): void => {\n if (!storage) return;\n try {\n storage.setItem(key, JSON.stringify(value));\n } catch {\n // quota or serialization failure — silently drop\n }\n };\n\n const remove = (key: string): void => {\n if (!storage) return;\n try {\n storage.removeItem(key);\n } catch {\n // ignore\n }\n };\n\n return {\n loadCustomServers: async () =>\n normalizeCustomServerRecords(read<unknown>(customServersKey, [])),\n saveCustomServers: async (records) => {\n write(customServersKey, records);\n },\n loadAuthState: async (id) =>\n normalizePersistedAuthState(read<unknown>(authKey(id), null)),\n saveAuthState: async (id, state) => {\n write(authKey(id), state);\n },\n clearAuthState: async (id) => {\n remove(authKey(id));\n },\n };\n};\n\nexport const McpLocalStorage = resource(useMcpLocalStorage);\n"],"mappings":";;;;AAiBA,SAASY,eAAeC,MAA8C;CACpE,IAAIA,KAAKH,SAAS,OAAOG,KAAKH;CAC9B,IAAI,OAAOI,eAAe,eAAe,kBAAkBA,YACzD,IAAI;EACF,OAAQA,WAAyCC;CACnD,QAAQ;EACN,OAAO;CACT;CAEF,OAAO;AACT;AAEA,MAAMC,YAAYC,UAChB,OAAOA,UAAU,YAAYA,UAAU,QAAQ,CAACE,MAAMC,QAAQH,KAAK;AAErE,MAAMI,oBAAoBJ,UACxBA,UAAUK,KAAAA,KAAa,OAAOL,UAAU;AAE1C,MAAMM,oBAAoBN,UACxB,OAAOA,UAAU,YAAYA,MAAMO,KAAK,CAAC,CAACC,SAAS;AAErD,MAAMC,4BACJT,UAEAA,UAAUK,KAAAA,KAAaC,iBAAiBN,KAAK;AAE/C,MAAMU,yBAAyBV,UAC7BA,UAAUK,KAAAA,KACTH,MAAMC,QAAQH,KAAK,KAAKA,MAAMW,OAAOC,SAAS,OAAOA,SAAS,QAAQ;AAEzE,MAAMC,+BACJb,UAEAA,UAAUK,KAAAA,KACT,OAAOL,UAAU,YAAYc,OAAOC,SAASf,KAAK,KAAKA,SAAS;AAEnE,MAAMgB,mBAAmBC,OAAwB;CAC/C,IAAI;EACF5B,oBAAoB4B,EAAE;EACtB,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAMC,mBAAmBC,SAAyC;CAChE,IAAI,CAACpB,SAASoB,IAAI,GAAG,OAAO;CAE5B,QAAQA,KAAKC,MAAb;EACE,KAAK,QACH,OAAO;EACT,KAAK,UACH,OAAOX,yBAAyBU,KAAKE,KAAK;EAC5C,KAAK,SACH,OACEX,sBAAsBS,KAAKG,MAAM,KACjClB,iBAAiBe,KAAKI,qBAAqB,KAC3CnB,iBAAiBe,KAAKK,aAAa,KACnCpB,iBAAiBe,KAAKM,oBAAoB,KAC1CrB,iBAAiBe,KAAKO,QAAQ,KAC9BtB,iBAAiBe,KAAKQ,YAAY;EAEtC,SACE,OAAO;CACX;AACF;AAEA,MAAMC,wBACJ5B,UACmC;CACnC,IAAI,CAACD,SAASC,KAAK,GAAG,OAAO;CAC7B,IAAI,OAAOA,MAAMiB,OAAO,YAAY,CAACD,gBAAgBhB,MAAMiB,EAAE,GAC3D,OAAO;CAET,OACEX,iBAAiBN,MAAM6B,IAAI,KAC3BvB,iBAAiBN,MAAM8B,GAAG,KAC1BhB,OAAOC,SAASf,MAAM+B,SAAS,KAC/Bb,gBAAgBlB,MAAMmB,IAAI,KAC1BN,4BAA4Bb,MAAMgC,iBAAiB;AAEvD;AAEA,MAAMC,+BACJjC,UACiC;CACjC,IAAI4B,qBAAqB5B,KAAK,GAAG,OAAOA;CACxC,IAAI,CAACD,SAASC,KAAK,GAAG,OAAO;CAE7B,MAAMkC,SAAS,EAAE,GAAGlC,MAAM;CAC1B,OAAOkC,OAAOF;CACd,OAAOJ,qBAAqBM,MAAM,IAAIA,SAAS;AACjD;AAEA,MAAaC,gCACXnC,UAC4B;CAC5B,IAAI,CAACE,MAAMC,QAAQH,KAAK,GAAG,OAAO,CAAA;CAClC,OAAOA,MAAMoC,SAASxB,SAAS;EAC7B,MAAMsB,SAASD,4BAA4BrB,IAAI;EAC/C,OAAOsB,WAAW,OAAO,CAAA,IAAK,CAACA,MAAM;CACvC,CAAC;AACH;AAEA,MAAMG,wBACJrC,UACgD;CAChD,MAAMsC,SAASrD,kBAAkBsD,UAAUvC,KAAK;CAChD,OAAOsC,OAAOE,UAAUF,OAAOG,OAAOpC,KAAAA;AACxC;AAEA,MAAMqC,8BACJ1C,UAC2D;CAC3D,MAAMsC,SAAStD,iCAAiCuD,UAAUvC,KAAK;CAC/D,OAAOsC,OAAOE,UAAUF,OAAOG,OAAOpC,KAAAA;AACxC;AAEA,MAAasC,+BACX3C,UACiC;CACjC,IAAI,CAACD,SAASC,KAAK,GAAG,OAAO;CAE7B,MAAM4C,QAA+B,CAAC;CACtC,IAAItC,iBAAiBN,MAAMqB,KAAK,GAAGuB,MAAMvB,QAAQrB,MAAMqB;CACvD,IAAIf,iBAAiBN,MAAM6C,YAAY,GACrCD,MAAMC,eAAe7C,MAAM6C;CAG7B,MAAMC,SAAST,qBAAqBrC,MAAM8C,MAAM;CAChD,IAAIA,QAAQF,MAAME,SAASA;CAE3B,MAAMC,oBAAoBL,2BAA2B1C,MAAM+C,iBAAiB;CAC5E,IAAIA,mBAAmBH,MAAMG,oBAAoBA;CAEjD,OAAOC,OAAOC,KAAKL,KAAK,CAAC,CAACpC,SAAS,IAAIoC,QAAQ;AACjD;AAEA,MAAMM,sBAAsBtD,OAA+B,CAAC,MAAkB;CAC5E,MAAMuD,SAASvD,KAAKJ,aAAa;CACjC,MAAM4D,mBAAmB,GAAGD,OAAM;CAClC,MAAME,WAAWpC,OAAe,GAAGkC,OAAM,QAASlC;CAClD,MAAMxB,UAAUE,eAAeC,IAAI;CAEnC,MAAM0D,QAAWC,KAAaC,aAAmB;EAC/C,IAAI,CAAC/D,SAAS,OAAO+D;EACrB,IAAI;GACF,MAAME,MAAMjE,QAAQkE,QAAQJ,GAAG;GAC/B,IAAIG,OAAO,MAAM,OAAOF;GACxB,OAAOI,KAAKC,MAAMH,GAAG;EACvB,QAAQ;GACN,OAAOF;EACT;CACF;CAEA,MAAMM,SAASP,KAAavD,UAAyB;EACnD,IAAI,CAACP,SAAS;EACd,IAAI;GACFA,QAAQsE,QAAQR,KAAKK,KAAKI,UAAUhE,KAAK,CAAC;EAC5C,QAAQ,CACN;CAEJ;CAEA,MAAMiE,UAAUV,QAAsB;EACpC,IAAI,CAAC9D,SAAS;EACd,IAAI;GACFA,QAAQyE,WAAWX,GAAG;EACxB,QAAQ,CACN;CAEJ;CAEA,OAAO;EACLY,mBAAmB,YACjBhC,6BAA6BmB,KAAcF,kBAAkB,CAAA,CAAE,CAAC;EAClEgB,mBAAmB,OAAOC,YAAY;GACpCP,MAAMV,kBAAkBiB,OAAO;EACjC;EACAC,eAAe,OAAOrD,OACpB0B,4BAA4BW,KAAcD,QAAQpC,EAAE,GAAG,IAAI,CAAC;EAC9DsD,eAAe,OAAOtD,IAAI2B,UAAU;GAClCkB,MAAMT,QAAQpC,EAAE,GAAG2B,KAAK;EAC1B;EACA4B,gBAAgB,OAAOvD,OAAO;GAC5BgD,OAAOZ,QAAQpC,EAAE,CAAC;EACpB;CACF;AACF;AAEA,MAAawD,kBAAkB1F,SAASmE,kBAAkB"}
|
|
1
|
+
{"version":3,"file":"McpLocalStorage.js","names":["resource","OAuthClientInformationFullSchema","OAuthTokensSchema","MCPAuthConfig","MCPCustomServerRecord","MCPPersistedAuthState","assertValidServerId","MCPStorage","McpLocalStorageOptions","keyPrefix","storage","Storage","resolveStorage","opts","globalThis","localStorage","isRecord","value","Record","Array","isArray","isOptionalString","undefined","isNonEmptyString","trim","length","isOptionalNonEmptyString","isOptionalStringArray","every","item","isOptionalConnectionTimeout","Number","isFinite","isValidServerId","id","isMCPAuthConfig","auth","type","token","scopes","authorizationEndpoint","tokenEndpoint","registrationEndpoint","clientId","clientSecret","isCustomServerRecord","name","url","createdAt","connectionTimeout","normalizeCustomServerRecord","record","normalizeCustomServerRecords","flatMap","normalizeOAuthTokens","result","safeParse","success","data","normalizeClientInformation","normalizePersistedAuthState","state","codeVerifier","tokens","clientInformation","Object","keys","useMcpLocalStorage","prefix","customServersKey","authKey","read","key","fallback","T","raw","getItem","JSON","parse","write","setItem","stringify","remove","removeItem","loadCustomServers","saveCustomServers","records","loadAuthState","saveAuthState","clearAuthState","McpLocalStorage"],"sources":["../../../src/resources/storage/McpLocalStorage.ts"],"sourcesContent":["import { resource } from \"@assistant-ui/tap\";\nimport {\n OAuthClientInformationFullSchema,\n OAuthTokensSchema,\n} from \"@modelcontextprotocol/core\";\nimport type { MCPAuthConfig, MCPCustomServerRecord } from \"../../mcp-scope\";\nimport type { MCPPersistedAuthState } from \"../../auth/types\";\nimport { assertValidServerId } from \"../../utils/serverId\";\nimport type { MCPStorage } from \"./types\";\n\nexport type McpLocalStorageOptions = {\n /** Namespace prefix for keys. Default \"aui-mcp\". */\n keyPrefix?: string;\n /** Override the underlying Storage. Defaults to globalThis.localStorage. */\n storage?: Storage;\n};\n\nfunction resolveStorage(opts: McpLocalStorageOptions): Storage | null {\n if (opts.storage) return opts.storage;\n if (typeof globalThis !== \"undefined\" && \"localStorage\" in globalThis) {\n try {\n return (globalThis as { localStorage: Storage }).localStorage;\n } catch {\n return null;\n }\n }\n return null;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isOptionalString = (value: unknown): value is string | undefined =>\n value === undefined || typeof value === \"string\";\n\nconst isNonEmptyString = (value: unknown): value is string =>\n typeof value === \"string\" && value.trim().length > 0;\n\nconst isOptionalNonEmptyString = (\n value: unknown,\n): value is string | undefined =>\n value === undefined || isNonEmptyString(value);\n\nconst isOptionalStringArray = (value: unknown): value is string[] | undefined =>\n value === undefined ||\n (Array.isArray(value) && value.every((item) => typeof item === \"string\"));\n\nconst isOptionalConnectionTimeout = (\n value: unknown,\n): value is number | undefined =>\n value === undefined ||\n (typeof value === \"number\" && Number.isFinite(value) && value >= 0);\n\nconst isValidServerId = (id: string): boolean => {\n try {\n assertValidServerId(id);\n return true;\n } catch {\n return false;\n }\n};\n\nconst isMCPAuthConfig = (auth: unknown): auth is MCPAuthConfig => {\n if (!isRecord(auth)) return false;\n\n switch (auth.type) {\n case \"none\":\n return true;\n case \"bearer\":\n return isOptionalNonEmptyString(auth.token);\n case \"oauth\":\n return (\n isOptionalStringArray(auth.scopes) &&\n isOptionalString(auth.authorizationEndpoint) &&\n isOptionalString(auth.tokenEndpoint) &&\n isOptionalString(auth.registrationEndpoint) &&\n isOptionalString(auth.clientId) &&\n isOptionalString(auth.clientSecret)\n );\n default:\n return false;\n }\n};\n\nconst isCustomServerRecord = (\n value: unknown,\n): value is MCPCustomServerRecord => {\n if (!isRecord(value)) return false;\n if (typeof value.id !== \"string\" || !isValidServerId(value.id)) {\n return false;\n }\n return (\n isNonEmptyString(value.name) &&\n isNonEmptyString(value.url) &&\n Number.isFinite(value.createdAt) &&\n isMCPAuthConfig(value.auth) &&\n isOptionalConnectionTimeout(value.connectionTimeout)\n );\n};\n\nconst normalizeCustomServerRecord = (\n value: unknown,\n): MCPCustomServerRecord | null => {\n if (isCustomServerRecord(value)) return value;\n if (!isRecord(value)) return null;\n\n const record = { ...value };\n delete record.connectionTimeout;\n return isCustomServerRecord(record) ? record : null;\n};\n\nexport const normalizeCustomServerRecords = (\n value: unknown,\n): MCPCustomServerRecord[] => {\n if (!Array.isArray(value)) return [];\n return value.flatMap((item) => {\n const record = normalizeCustomServerRecord(item);\n return record === null ? [] : [record];\n });\n};\n\nconst normalizeOAuthTokens = (\n value: unknown,\n): MCPPersistedAuthState[\"tokens\"] | undefined => {\n const result = OAuthTokensSchema.safeParse(value);\n return result.success ? result.data : undefined;\n};\n\nconst normalizeClientInformation = (\n value: unknown,\n): MCPPersistedAuthState[\"clientInformation\"] | undefined => {\n const result = OAuthClientInformationFullSchema.safeParse(value);\n return result.success ? result.data : undefined;\n};\n\nexport const normalizePersistedAuthState = (\n value: unknown,\n): MCPPersistedAuthState | null => {\n if (!isRecord(value)) return null;\n\n const state: MCPPersistedAuthState = {};\n if (isNonEmptyString(value.token)) state.token = value.token;\n if (isNonEmptyString(value.codeVerifier)) {\n state.codeVerifier = value.codeVerifier;\n }\n\n const tokens = normalizeOAuthTokens(value.tokens);\n if (tokens) state.tokens = tokens;\n\n const clientInformation = normalizeClientInformation(value.clientInformation);\n if (clientInformation) state.clientInformation = clientInformation;\n\n return Object.keys(state).length > 0 ? state : null;\n};\n\nconst useMcpLocalStorage = (opts: McpLocalStorageOptions = {}): MCPStorage => {\n const prefix = opts.keyPrefix ?? \"aui-mcp\";\n const customServersKey = `${prefix}:custom-servers`;\n const authKey = (id: string) => `${prefix}:auth:${id}`;\n const storage = resolveStorage(opts);\n\n const read = <T>(key: string, fallback: T): T => {\n if (!storage) return fallback;\n try {\n const raw = storage.getItem(key);\n if (raw == null) return fallback;\n return JSON.parse(raw) as T;\n } catch {\n return fallback;\n }\n };\n\n const write = (key: string, value: unknown): void => {\n if (!storage) return;\n try {\n storage.setItem(key, JSON.stringify(value));\n } catch {\n // quota or serialization failure — silently drop\n }\n };\n\n const remove = (key: string): void => {\n if (!storage) return;\n try {\n storage.removeItem(key);\n } catch {\n // ignore\n }\n };\n\n return {\n loadCustomServers: async () =>\n normalizeCustomServerRecords(read<unknown>(customServersKey, [])),\n saveCustomServers: async (records) => {\n write(customServersKey, records);\n },\n loadAuthState: async (id) =>\n normalizePersistedAuthState(read<unknown>(authKey(id), null)),\n saveAuthState: async (id, state) => {\n write(authKey(id), state);\n },\n clearAuthState: async (id) => {\n remove(authKey(id));\n },\n };\n};\n\nexport const McpLocalStorage = resource(useMcpLocalStorage);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/resources/validateElicitationContent.d.ts
|
|
2
|
+
type ValidationError = {
|
|
3
|
+
property: string;
|
|
4
|
+
message: string;
|
|
5
|
+
};
|
|
6
|
+
declare const validateElicitationContent: (requestedSchema: unknown, content: Record<string, unknown>) => readonly ValidationError[];
|
|
7
|
+
//#endregion
|
|
8
|
+
export { validateElicitationContent };
|
|
9
|
+
//# sourceMappingURL=validateElicitationContent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateElicitationContent.d.ts","names":[],"sources":["../../src/resources/validateElicitationContent.ts"],"mappings":";KAAK;EACH;EACA;;cA+BW,6BACX,0BACA,SAAS,qCACC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//#region src/resources/validateElicitationContent.ts
|
|
2
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3
|
+
const getOwn = (value, key) => Object.hasOwn(value, key) ? value[key] : void 0;
|
|
4
|
+
const matchesType = (type, value) => {
|
|
5
|
+
switch (type) {
|
|
6
|
+
case "string": return typeof value === "string";
|
|
7
|
+
case "number": return typeof value === "number" && Number.isFinite(value);
|
|
8
|
+
case "integer": return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value);
|
|
9
|
+
case "boolean": return typeof value === "boolean";
|
|
10
|
+
default: return true;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const typeMessage = (type) => `Expected ${type === "integer" ? "an" : "a"} ${type}.`;
|
|
14
|
+
const validateElicitationContent = (requestedSchema, content) => {
|
|
15
|
+
if (!isRecord(requestedSchema)) return [];
|
|
16
|
+
const errors = [];
|
|
17
|
+
const properties = getOwn(requestedSchema, "properties");
|
|
18
|
+
if (isRecord(properties)) for (const property of Object.keys(properties)) {
|
|
19
|
+
if (!Object.hasOwn(content, property)) continue;
|
|
20
|
+
const schema = properties[property];
|
|
21
|
+
if (!isRecord(schema)) continue;
|
|
22
|
+
const value = content[property];
|
|
23
|
+
const type = getOwn(schema, "type");
|
|
24
|
+
if (typeof type === "string" && !matchesType(type, value)) errors.push({
|
|
25
|
+
property,
|
|
26
|
+
message: typeMessage(type)
|
|
27
|
+
});
|
|
28
|
+
const values = getOwn(schema, "enum");
|
|
29
|
+
if (Array.isArray(values) && !values.some((enumValue) => Object.is(enumValue, value))) errors.push({
|
|
30
|
+
property,
|
|
31
|
+
message: "Must be one of the allowed values."
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
const required = getOwn(requestedSchema, "required");
|
|
35
|
+
if (Array.isArray(required)) for (const property of required) {
|
|
36
|
+
if (typeof property !== "string" || Object.hasOwn(content, property)) continue;
|
|
37
|
+
errors.push({
|
|
38
|
+
property,
|
|
39
|
+
message: "This property is required."
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return errors;
|
|
43
|
+
};
|
|
44
|
+
//#endregion
|
|
45
|
+
export { validateElicitationContent };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=validateElicitationContent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateElicitationContent.js","names":["ValidationError","property","message","isRecord","value","Record","Array","isArray","getOwn","key","Object","hasOwn","undefined","matchesType","type","Number","isFinite","isInteger","typeMessage","validateElicitationContent","requestedSchema","content","errors","properties","keys","schema","push","values","some","enumValue","is","required"],"sources":["../../src/resources/validateElicitationContent.ts"],"sourcesContent":["type ValidationError = {\n property: string;\n message: string;\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst getOwn = (value: Record<string, unknown>, key: string): unknown =>\n Object.hasOwn(value, key) ? value[key] : undefined;\n\nconst matchesType = (type: unknown, value: unknown): boolean => {\n switch (type) {\n case \"string\":\n return typeof value === \"string\";\n case \"number\":\n return typeof value === \"number\" && Number.isFinite(value);\n case \"integer\":\n return (\n typeof value === \"number\" &&\n Number.isFinite(value) &&\n Number.isInteger(value)\n );\n case \"boolean\":\n return typeof value === \"boolean\";\n default:\n return true;\n }\n};\n\nconst typeMessage = (type: string) =>\n `Expected ${type === \"integer\" ? \"an\" : \"a\"} ${type}.`;\n\nexport const validateElicitationContent = (\n requestedSchema: unknown,\n content: Record<string, unknown>,\n): readonly ValidationError[] => {\n if (!isRecord(requestedSchema)) return [];\n\n const errors: ValidationError[] = [];\n const properties = getOwn(requestedSchema, \"properties\");\n if (isRecord(properties)) {\n for (const property of Object.keys(properties)) {\n if (!Object.hasOwn(content, property)) continue;\n\n const schema = properties[property];\n if (!isRecord(schema)) continue;\n\n const value = content[property];\n const type = getOwn(schema, \"type\");\n if (typeof type === \"string\" && !matchesType(type, value)) {\n errors.push({ property, message: typeMessage(type) });\n }\n\n const values = getOwn(schema, \"enum\");\n if (\n Array.isArray(values) &&\n !values.some((enumValue) => Object.is(enumValue, value))\n ) {\n errors.push({\n property,\n message: \"Must be one of the allowed values.\",\n });\n }\n }\n }\n\n const required = getOwn(requestedSchema, \"required\");\n if (Array.isArray(required)) {\n for (const property of required) {\n if (typeof property !== \"string\" || Object.hasOwn(content, property)) {\n continue;\n }\n errors.push({ property, message: \"This property is required.\" });\n }\n }\n\n return errors;\n};\n"],"mappings":";AAKA,MAAMG,YAAYC,UAChB,OAAOA,UAAU,YAAYA,UAAU,QAAQ,CAACE,MAAMC,QAAQH,KAAK;AAErE,MAAMI,UAAUJ,OAAgCK,QAC9CC,OAAOC,OAAOP,OAAOK,GAAG,IAAIL,MAAMK,OAAOG,KAAAA;AAE3C,MAAMC,eAAeC,MAAeV,UAA4B;CAC9D,QAAQU,MAAR;EACE,KAAK,UACH,OAAO,OAAOV,UAAU;EAC1B,KAAK,UACH,OAAO,OAAOA,UAAU,YAAYW,OAAOC,SAASZ,KAAK;EAC3D,KAAK,WACH,OACE,OAAOA,UAAU,YACjBW,OAAOC,SAASZ,KAAK,KACrBW,OAAOE,UAAUb,KAAK;EAE1B,KAAK,WACH,OAAO,OAAOA,UAAU;EAC1B,SACE,OAAO;CACX;AACF;AAEA,MAAMc,eAAeJ,SACnB,YAAYA,SAAS,YAAY,OAAO,IAAG,GAAIA,KAAI;AAErD,MAAaK,8BACXC,iBACAC,YAC+B;CAC/B,IAAI,CAAClB,SAASiB,eAAe,GAAG,OAAO,CAAA;CAEvC,MAAME,SAA4B,CAAA;CAClC,MAAMC,aAAaf,OAAOY,iBAAiB,YAAY;CACvD,IAAIjB,SAASoB,UAAU,GACrB,KAAK,MAAMtB,YAAYS,OAAOc,KAAKD,UAAU,GAAG;EAC9C,IAAI,CAACb,OAAOC,OAAOU,SAASpB,QAAQ,GAAG;EAEvC,MAAMwB,SAASF,WAAWtB;EAC1B,IAAI,CAACE,SAASsB,MAAM,GAAG;EAEvB,MAAMrB,QAAQiB,QAAQpB;EACtB,MAAMa,OAAON,OAAOiB,QAAQ,MAAM;EAClC,IAAI,OAAOX,SAAS,YAAY,CAACD,YAAYC,MAAMV,KAAK,GACtDkB,OAAOI,KAAK;GAAEzB;GAAUC,SAASgB,YAAYJ,IAAI;EAAE,CAAC;EAGtD,MAAMa,SAASnB,OAAOiB,QAAQ,MAAM;EACpC,IACEnB,MAAMC,QAAQoB,MAAM,KACpB,CAACA,OAAOC,MAAMC,cAAcnB,OAAOoB,GAAGD,WAAWzB,KAAK,CAAC,GAEvDkB,OAAOI,KAAK;GACVzB;GACAC,SAAS;EACX,CAAC;CAEL;CAGF,MAAM6B,WAAWvB,OAAOY,iBAAiB,UAAU;CACnD,IAAId,MAAMC,QAAQwB,QAAQ,GACxB,KAAK,MAAM9B,YAAY8B,UAAU;EAC/B,IAAI,OAAO9B,aAAa,YAAYS,OAAOC,OAAOU,SAASpB,QAAQ,GACjE;EAEFqB,OAAOI,KAAK;GAAEzB;GAAUC,SAAS;EAA6B,CAAC;CACjE;CAGF,OAAOoB;AACT"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-mcp",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "MCP server configuration and connection primitives for @assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -27,12 +27,13 @@
|
|
|
27
27
|
],
|
|
28
28
|
"sideEffects": false,
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@modelcontextprotocol/
|
|
30
|
+
"@modelcontextprotocol/client": "^2.0.0",
|
|
31
|
+
"@modelcontextprotocol/core": "^2.0.0",
|
|
31
32
|
"@radix-ui/react-primitive": "^2.1.10",
|
|
32
|
-
"@assistant-ui/core": "0.3.
|
|
33
|
-
"@assistant-ui/store": "0.3.
|
|
34
|
-
"@assistant-ui/tap": "0.9.
|
|
35
|
-
"assistant-stream": "0.3.
|
|
33
|
+
"@assistant-ui/core": "0.3.2",
|
|
34
|
+
"@assistant-ui/store": "0.3.2",
|
|
35
|
+
"@assistant-ui/tap": "0.9.8",
|
|
36
|
+
"assistant-stream": "0.3.31"
|
|
36
37
|
},
|
|
37
38
|
"peerDependencies": {
|
|
38
39
|
"@types/react": "*",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
2
1
|
import type {
|
|
2
|
+
OAuthClientProvider,
|
|
3
3
|
OAuthClientInformationFull,
|
|
4
4
|
OAuthClientMetadata,
|
|
5
5
|
OAuthTokens,
|
|
6
|
-
} from "@modelcontextprotocol/
|
|
6
|
+
} from "@modelcontextprotocol/client";
|
|
7
7
|
import type { MCPStorage } from "../resources/storage/types";
|
|
8
8
|
import type { MCPAuthConfig } from "../mcp-scope";
|
|
9
9
|
|
package/src/auth/types.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ export type { MCPStorage, MCPStorageElement } from "./resources/storage/types";
|
|
|
30
30
|
export * as McpManagerPrimitive from "./primitives/manager";
|
|
31
31
|
export * as McpServerPrimitive from "./primitives/server";
|
|
32
32
|
export * as McpAddFormPrimitive from "./primitives/addForm";
|
|
33
|
+
export * as McpElicitationPrimitive from "./primitives/elicitation";
|
|
33
34
|
|
|
34
35
|
// Per-server scope providers. The iteration primitives wrap each item in
|
|
35
36
|
// the appropriate ByIndex provider. The ById provider is useful for
|
|
@@ -52,11 +53,14 @@ export type { MCPPersistedAuthState } from "./auth/types";
|
|
|
52
53
|
// Public types
|
|
53
54
|
export type {
|
|
54
55
|
MCPAuthConfig,
|
|
56
|
+
MCPResponseCacheConfig,
|
|
55
57
|
MCPConnector,
|
|
56
58
|
MCPCustomServerRecord,
|
|
57
59
|
MCPServerKind,
|
|
58
60
|
MCPConnectionState,
|
|
59
61
|
MCPToolInfo,
|
|
62
|
+
MCPElicitation,
|
|
63
|
+
MCPElicitationResponse,
|
|
60
64
|
MCPServerState,
|
|
61
65
|
MCPManagerState,
|
|
62
66
|
MCPServerMethods,
|
package/src/mcp-scope.ts
CHANGED
|
@@ -13,6 +13,10 @@ export type MCPAuthConfig =
|
|
|
13
13
|
clientSecret?: string | undefined;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
+
export type MCPResponseCacheConfig = {
|
|
17
|
+
readonly defaultTtlMs?: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
16
20
|
export type MCPConnector = {
|
|
17
21
|
id: string;
|
|
18
22
|
name: string;
|
|
@@ -20,6 +24,8 @@ export type MCPConnector = {
|
|
|
20
24
|
icon?: string | undefined;
|
|
21
25
|
auth: MCPAuthConfig;
|
|
22
26
|
connectionTimeout?: number | undefined;
|
|
27
|
+
readonly cache?: MCPResponseCacheConfig | undefined;
|
|
28
|
+
readonly elicitation?: boolean | undefined;
|
|
23
29
|
};
|
|
24
30
|
|
|
25
31
|
export type MCPCustomServerRecord = {
|
|
@@ -28,6 +34,8 @@ export type MCPCustomServerRecord = {
|
|
|
28
34
|
url: string;
|
|
29
35
|
auth: MCPAuthConfig;
|
|
30
36
|
connectionTimeout?: number | undefined;
|
|
37
|
+
readonly cache?: MCPResponseCacheConfig | undefined;
|
|
38
|
+
readonly elicitation?: boolean | undefined;
|
|
31
39
|
createdAt: number;
|
|
32
40
|
};
|
|
33
41
|
|
|
@@ -47,6 +55,23 @@ export type MCPToolInfo = {
|
|
|
47
55
|
inputSchema: unknown;
|
|
48
56
|
};
|
|
49
57
|
|
|
58
|
+
export type MCPElicitation = {
|
|
59
|
+
readonly id: string;
|
|
60
|
+
readonly message: string;
|
|
61
|
+
readonly requestedSchema: unknown;
|
|
62
|
+
readonly error?:
|
|
63
|
+
| {
|
|
64
|
+
readonly message: string;
|
|
65
|
+
readonly properties?: readonly string[] | undefined;
|
|
66
|
+
}
|
|
67
|
+
| undefined;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export type MCPElicitationResponse =
|
|
71
|
+
| { action: "accept"; content: Record<string, unknown> }
|
|
72
|
+
| { action: "decline" }
|
|
73
|
+
| { action: "cancel" };
|
|
74
|
+
|
|
50
75
|
export type MCPServerState = {
|
|
51
76
|
id: string;
|
|
52
77
|
kind: MCPServerKind;
|
|
@@ -57,6 +82,7 @@ export type MCPServerState = {
|
|
|
57
82
|
lastError: { message: string } | null;
|
|
58
83
|
tools: MCPToolInfo[];
|
|
59
84
|
authorizationUrl: string | null;
|
|
85
|
+
readonly pendingElicitations: readonly MCPElicitation[];
|
|
60
86
|
};
|
|
61
87
|
|
|
62
88
|
export type MCPManagerState = {
|
|
@@ -78,6 +104,10 @@ export type MCPServerMethods = {
|
|
|
78
104
|
readResource: (uri: string) => Promise<unknown>;
|
|
79
105
|
/** OAuth only: pass full callback URL (e.g. window.location.href) */
|
|
80
106
|
completeAuth: (callbackUrl: string) => Promise<void>;
|
|
107
|
+
answerElicitation(
|
|
108
|
+
id: string,
|
|
109
|
+
response: MCPElicitationResponse,
|
|
110
|
+
): readonly { property: string; message: string }[] | undefined;
|
|
81
111
|
};
|
|
82
112
|
|
|
83
113
|
export type MCPServerQuery =
|
|
@@ -98,6 +128,8 @@ export type MCPManagerMethods = {
|
|
|
98
128
|
url: string;
|
|
99
129
|
auth: MCPAuthConfig;
|
|
100
130
|
connectionTimeout?: number | undefined;
|
|
131
|
+
readonly cache?: MCPResponseCacheConfig | undefined;
|
|
132
|
+
readonly elicitation?: boolean | undefined;
|
|
101
133
|
}) => Promise<string>;
|
|
102
134
|
removeServer: (id: string) => Promise<void>;
|
|
103
135
|
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ComponentPropsWithoutRef,
|
|
3
|
+
type ComponentRef,
|
|
4
|
+
forwardRef,
|
|
5
|
+
} from "react";
|
|
6
|
+
import { Primitive } from "@radix-ui/react-primitive";
|
|
7
|
+
import { useAui } from "@assistant-ui/store";
|
|
8
|
+
import { useElicitationContext } from "./context";
|
|
9
|
+
import { prepareElicitationContent } from "./prepareElicitationContent";
|
|
10
|
+
|
|
11
|
+
export namespace McpElicitationPrimitiveAccept {
|
|
12
|
+
export type Element = ComponentRef<typeof Primitive.button>;
|
|
13
|
+
export type Props = ComponentPropsWithoutRef<typeof Primitive.button>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const McpElicitationPrimitiveAccept = forwardRef<
|
|
17
|
+
McpElicitationPrimitiveAccept.Element,
|
|
18
|
+
McpElicitationPrimitiveAccept.Props
|
|
19
|
+
>((props, ref) => {
|
|
20
|
+
const { elicitation, draft } = useElicitationContext();
|
|
21
|
+
const aui = useAui();
|
|
22
|
+
const { content, missingRequired, invalid } = prepareElicitationContent(
|
|
23
|
+
elicitation.requestedSchema,
|
|
24
|
+
draft,
|
|
25
|
+
);
|
|
26
|
+
const hasMissingRequired = missingRequired.length > 0;
|
|
27
|
+
const hasInvalid = invalid.length > 0;
|
|
28
|
+
return (
|
|
29
|
+
<Primitive.button
|
|
30
|
+
{...props}
|
|
31
|
+
type="button"
|
|
32
|
+
ref={ref}
|
|
33
|
+
disabled={props.disabled || hasMissingRequired || hasInvalid}
|
|
34
|
+
data-missing-required={hasMissingRequired ? "" : undefined}
|
|
35
|
+
data-invalid={hasInvalid ? invalid.join(",") : undefined}
|
|
36
|
+
onClick={(event) => {
|
|
37
|
+
props.onClick?.(event);
|
|
38
|
+
if (event.defaultPrevented || hasMissingRequired || hasInvalid) return;
|
|
39
|
+
aui.mcpServer.answerElicitation(elicitation.id, {
|
|
40
|
+
action: "accept",
|
|
41
|
+
content,
|
|
42
|
+
});
|
|
43
|
+
}}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
McpElicitationPrimitiveAccept.displayName = "McpElicitationPrimitive.Accept";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ComponentPropsWithoutRef,
|
|
3
|
+
type ComponentRef,
|
|
4
|
+
forwardRef,
|
|
5
|
+
} from "react";
|
|
6
|
+
import { Primitive } from "@radix-ui/react-primitive";
|
|
7
|
+
import { useAui } from "@assistant-ui/store";
|
|
8
|
+
import { useMcpElicitation } from "./context";
|
|
9
|
+
|
|
10
|
+
export namespace McpElicitationPrimitiveCancel {
|
|
11
|
+
export type Element = ComponentRef<typeof Primitive.button>;
|
|
12
|
+
export type Props = ComponentPropsWithoutRef<typeof Primitive.button>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const McpElicitationPrimitiveCancel = forwardRef<
|
|
16
|
+
McpElicitationPrimitiveCancel.Element,
|
|
17
|
+
McpElicitationPrimitiveCancel.Props
|
|
18
|
+
>((props, ref) => {
|
|
19
|
+
const elicitation = useMcpElicitation();
|
|
20
|
+
const aui = useAui();
|
|
21
|
+
return (
|
|
22
|
+
<Primitive.button
|
|
23
|
+
{...props}
|
|
24
|
+
type="button"
|
|
25
|
+
ref={ref}
|
|
26
|
+
onClick={(event) => {
|
|
27
|
+
props.onClick?.(event);
|
|
28
|
+
if (event.defaultPrevented) return;
|
|
29
|
+
aui.mcpServer.answerElicitation(elicitation.id, {
|
|
30
|
+
action: "cancel",
|
|
31
|
+
});
|
|
32
|
+
}}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
McpElicitationPrimitiveCancel.displayName = "McpElicitationPrimitive.Cancel";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ComponentPropsWithoutRef,
|
|
3
|
+
type ComponentRef,
|
|
4
|
+
forwardRef,
|
|
5
|
+
} from "react";
|
|
6
|
+
import { Primitive } from "@radix-ui/react-primitive";
|
|
7
|
+
import { useAui } from "@assistant-ui/store";
|
|
8
|
+
import { useMcpElicitation } from "./context";
|
|
9
|
+
|
|
10
|
+
export namespace McpElicitationPrimitiveDecline {
|
|
11
|
+
export type Element = ComponentRef<typeof Primitive.button>;
|
|
12
|
+
export type Props = ComponentPropsWithoutRef<typeof Primitive.button>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const McpElicitationPrimitiveDecline = forwardRef<
|
|
16
|
+
McpElicitationPrimitiveDecline.Element,
|
|
17
|
+
McpElicitationPrimitiveDecline.Props
|
|
18
|
+
>((props, ref) => {
|
|
19
|
+
const elicitation = useMcpElicitation();
|
|
20
|
+
const aui = useAui();
|
|
21
|
+
return (
|
|
22
|
+
<Primitive.button
|
|
23
|
+
{...props}
|
|
24
|
+
type="button"
|
|
25
|
+
ref={ref}
|
|
26
|
+
onClick={(event) => {
|
|
27
|
+
props.onClick?.(event);
|
|
28
|
+
if (event.defaultPrevented) return;
|
|
29
|
+
aui.mcpServer.answerElicitation(elicitation.id, {
|
|
30
|
+
action: "decline",
|
|
31
|
+
});
|
|
32
|
+
}}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
McpElicitationPrimitiveDecline.displayName = "McpElicitationPrimitive.Decline";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ComponentPropsWithoutRef,
|
|
3
|
+
type ComponentRef,
|
|
4
|
+
forwardRef,
|
|
5
|
+
} from "react";
|
|
6
|
+
import { Primitive } from "@radix-ui/react-primitive";
|
|
7
|
+
import { useMcpElicitation } from "./context";
|
|
8
|
+
|
|
9
|
+
export namespace McpElicitationPrimitiveError {
|
|
10
|
+
export type Element = ComponentRef<typeof Primitive.div>;
|
|
11
|
+
export type Props = ComponentPropsWithoutRef<typeof Primitive.div>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const McpElicitationPrimitiveError = forwardRef<
|
|
15
|
+
McpElicitationPrimitiveError.Element,
|
|
16
|
+
McpElicitationPrimitiveError.Props
|
|
17
|
+
>((props, ref) => {
|
|
18
|
+
const error = useMcpElicitation().error;
|
|
19
|
+
if (!error) return null;
|
|
20
|
+
return (
|
|
21
|
+
<Primitive.div
|
|
22
|
+
{...props}
|
|
23
|
+
ref={ref}
|
|
24
|
+
data-properties={error.properties?.join(",")}
|
|
25
|
+
>
|
|
26
|
+
{props.children ?? error.message}
|
|
27
|
+
</Primitive.div>
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
McpElicitationPrimitiveError.displayName = "McpElicitationPrimitive.Error";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type FC, type ReactNode } from "react";
|
|
2
|
+
import {
|
|
3
|
+
ElicitationFieldContext,
|
|
4
|
+
type ElicitationFieldContextValue,
|
|
5
|
+
useElicitationContext,
|
|
6
|
+
} from "./context";
|
|
7
|
+
|
|
8
|
+
export { useMcpElicitationField } from "./context";
|
|
9
|
+
|
|
10
|
+
export namespace McpElicitationPrimitiveFields {
|
|
11
|
+
export type Props = {
|
|
12
|
+
children: (field: ElicitationFieldContextValue) => ReactNode;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const McpElicitationPrimitiveFields: FC<
|
|
17
|
+
McpElicitationPrimitiveFields.Props
|
|
18
|
+
> = ({ children }) => {
|
|
19
|
+
const { elicitation, draft, setField } = useElicitationContext();
|
|
20
|
+
const properties = (
|
|
21
|
+
elicitation.requestedSchema as {
|
|
22
|
+
properties?: Record<string, unknown> | undefined;
|
|
23
|
+
} | null
|
|
24
|
+
)?.properties;
|
|
25
|
+
|
|
26
|
+
if (
|
|
27
|
+
properties === null ||
|
|
28
|
+
typeof properties !== "object" ||
|
|
29
|
+
Array.isArray(properties)
|
|
30
|
+
) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const fields = Object.entries(properties);
|
|
35
|
+
if (fields.length === 0) return null;
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<>
|
|
39
|
+
{fields.map(([name, schema]) => {
|
|
40
|
+
const field = {
|
|
41
|
+
name,
|
|
42
|
+
schema,
|
|
43
|
+
value: Object.hasOwn(draft, name) ? draft[name] : undefined,
|
|
44
|
+
setValue: (value: unknown) => setField(name, value),
|
|
45
|
+
};
|
|
46
|
+
return (
|
|
47
|
+
<ElicitationFieldContext.Provider key={name} value={field}>
|
|
48
|
+
{children(field)}
|
|
49
|
+
</ElicitationFieldContext.Provider>
|
|
50
|
+
);
|
|
51
|
+
})}
|
|
52
|
+
</>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
McpElicitationPrimitiveFields.displayName = "McpElicitationPrimitive.Fields";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type FC, type ReactNode, useCallback, useMemo, useState } from "react";
|
|
2
|
+
import { useAuiState } from "@assistant-ui/store";
|
|
3
|
+
import type { MCPElicitation } from "../../mcp-scope";
|
|
4
|
+
import { ElicitationContext } from "./context";
|
|
5
|
+
import { initialElicitationDraft } from "./initialElicitationDraft";
|
|
6
|
+
|
|
7
|
+
export { useMcpElicitation } from "./context";
|
|
8
|
+
|
|
9
|
+
export namespace McpElicitationPrimitiveItems {
|
|
10
|
+
export type Props = {
|
|
11
|
+
children: ReactNode | ((elicitation: MCPElicitation) => ReactNode);
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const McpElicitationPrimitiveItem: FC<{
|
|
16
|
+
elicitation: MCPElicitation;
|
|
17
|
+
children: McpElicitationPrimitiveItems.Props["children"];
|
|
18
|
+
}> = ({ elicitation, children }) => {
|
|
19
|
+
const [draft, setDraft] = useState<Record<string, unknown>>(() =>
|
|
20
|
+
initialElicitationDraft(elicitation.requestedSchema),
|
|
21
|
+
);
|
|
22
|
+
const setField = useCallback((name: string, value: unknown) => {
|
|
23
|
+
setDraft((current) => ({ ...current, [name]: value }));
|
|
24
|
+
}, []);
|
|
25
|
+
const context = useMemo(
|
|
26
|
+
() => ({ elicitation, draft, setField }),
|
|
27
|
+
[elicitation, draft, setField],
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<ElicitationContext.Provider value={context}>
|
|
32
|
+
{typeof children === "function" ? children(elicitation) : children}
|
|
33
|
+
</ElicitationContext.Provider>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const McpElicitationPrimitiveItems: FC<
|
|
38
|
+
McpElicitationPrimitiveItems.Props
|
|
39
|
+
> = ({ children }) => {
|
|
40
|
+
const elicitations = useAuiState(
|
|
41
|
+
(state) => state.mcpServer.pendingElicitations,
|
|
42
|
+
);
|
|
43
|
+
if (elicitations.length === 0) return null;
|
|
44
|
+
return (
|
|
45
|
+
<>
|
|
46
|
+
{elicitations.map((elicitation) => (
|
|
47
|
+
<McpElicitationPrimitiveItem
|
|
48
|
+
key={elicitation.id}
|
|
49
|
+
elicitation={elicitation}
|
|
50
|
+
>
|
|
51
|
+
{children}
|
|
52
|
+
</McpElicitationPrimitiveItem>
|
|
53
|
+
))}
|
|
54
|
+
</>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
McpElicitationPrimitiveItems.displayName = "McpElicitationPrimitive.Items";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ComponentPropsWithoutRef,
|
|
3
|
+
type ComponentRef,
|
|
4
|
+
forwardRef,
|
|
5
|
+
} from "react";
|
|
6
|
+
import { Primitive } from "@radix-ui/react-primitive";
|
|
7
|
+
import { useMcpElicitation } from "./context";
|
|
8
|
+
|
|
9
|
+
export namespace McpElicitationPrimitiveMessage {
|
|
10
|
+
export type Element = ComponentRef<typeof Primitive.span>;
|
|
11
|
+
export type Props = ComponentPropsWithoutRef<typeof Primitive.span>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const McpElicitationPrimitiveMessage = forwardRef<
|
|
15
|
+
McpElicitationPrimitiveMessage.Element,
|
|
16
|
+
McpElicitationPrimitiveMessage.Props
|
|
17
|
+
>((props, ref) => {
|
|
18
|
+
const elicitation = useMcpElicitation();
|
|
19
|
+
return (
|
|
20
|
+
<Primitive.span {...props} ref={ref}>
|
|
21
|
+
{props.children ?? elicitation.message}
|
|
22
|
+
</Primitive.span>
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
McpElicitationPrimitiveMessage.displayName = "McpElicitationPrimitive.Message";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ComponentPropsWithoutRef,
|
|
3
|
+
type ComponentRef,
|
|
4
|
+
forwardRef,
|
|
5
|
+
} from "react";
|
|
6
|
+
import { Primitive } from "@radix-ui/react-primitive";
|
|
7
|
+
import { useMcpElicitation } from "./context";
|
|
8
|
+
|
|
9
|
+
export namespace McpElicitationPrimitiveRoot {
|
|
10
|
+
export type Element = ComponentRef<typeof Primitive.div>;
|
|
11
|
+
export type Props = ComponentPropsWithoutRef<typeof Primitive.div>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const McpElicitationPrimitiveRoot = forwardRef<
|
|
15
|
+
McpElicitationPrimitiveRoot.Element,
|
|
16
|
+
McpElicitationPrimitiveRoot.Props
|
|
17
|
+
>((props, ref) => {
|
|
18
|
+
const elicitation = useMcpElicitation();
|
|
19
|
+
return (
|
|
20
|
+
<Primitive.div {...props} ref={ref} data-elicitation-id={elicitation.id} />
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
McpElicitationPrimitiveRoot.displayName = "McpElicitationPrimitive.Root";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
import type { MCPElicitation } from "../../mcp-scope";
|
|
3
|
+
|
|
4
|
+
export type ElicitationContextValue = {
|
|
5
|
+
elicitation: MCPElicitation;
|
|
6
|
+
draft: Record<string, unknown>;
|
|
7
|
+
setField: (name: string, value: unknown) => void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type ElicitationFieldContextValue = {
|
|
11
|
+
name: string;
|
|
12
|
+
schema: unknown;
|
|
13
|
+
value: unknown;
|
|
14
|
+
setValue: (value: unknown) => void;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const ElicitationContext = createContext<ElicitationContextValue | null>(
|
|
18
|
+
null,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export const ElicitationFieldContext =
|
|
22
|
+
createContext<ElicitationFieldContextValue | null>(null);
|
|
23
|
+
|
|
24
|
+
export const useElicitationContext = (): ElicitationContextValue => {
|
|
25
|
+
const context = useContext(ElicitationContext);
|
|
26
|
+
if (!context) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
"useMcpElicitation must be used inside <McpElicitationPrimitive.Items>",
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return context;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const useMcpElicitation = (): MCPElicitation =>
|
|
35
|
+
useElicitationContext().elicitation;
|
|
36
|
+
|
|
37
|
+
export const useMcpElicitationField = (): ElicitationFieldContextValue => {
|
|
38
|
+
const context = useContext(ElicitationFieldContext);
|
|
39
|
+
if (!context) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
"useMcpElicitationField must be used inside <McpElicitationPrimitive.Fields>",
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
return context;
|
|
45
|
+
};
|