@assistant-ui/react-mcp 0.0.22 → 0.1.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.
Files changed (94) hide show
  1. package/dist/auth/createOAuthProvider.d.ts +1 -1
  2. package/dist/auth/createOAuthProvider.js.map +1 -1
  3. package/dist/auth/types.d.ts +1 -1
  4. package/dist/index.d.ts +3 -2
  5. package/dist/index.js +2 -1
  6. package/dist/mcp-scope.d.ts +32 -1
  7. package/dist/mcp-scope.d.ts.map +1 -1
  8. package/dist/primitives/elicitation/McpElicitationPrimitiveAccept.d.ts +13 -0
  9. package/dist/primitives/elicitation/McpElicitationPrimitiveAccept.d.ts.map +1 -0
  10. package/dist/primitives/elicitation/McpElicitationPrimitiveAccept.js +106 -0
  11. package/dist/primitives/elicitation/McpElicitationPrimitiveAccept.js.map +1 -0
  12. package/dist/primitives/elicitation/McpElicitationPrimitiveCancel.d.ts +13 -0
  13. package/dist/primitives/elicitation/McpElicitationPrimitiveCancel.d.ts.map +1 -0
  14. package/dist/primitives/elicitation/McpElicitationPrimitiveCancel.js +43 -0
  15. package/dist/primitives/elicitation/McpElicitationPrimitiveCancel.js.map +1 -0
  16. package/dist/primitives/elicitation/McpElicitationPrimitiveDecline.d.ts +13 -0
  17. package/dist/primitives/elicitation/McpElicitationPrimitiveDecline.d.ts.map +1 -0
  18. package/dist/primitives/elicitation/McpElicitationPrimitiveDecline.js +43 -0
  19. package/dist/primitives/elicitation/McpElicitationPrimitiveDecline.js.map +1 -0
  20. package/dist/primitives/elicitation/McpElicitationPrimitiveError.d.ts +13 -0
  21. package/dist/primitives/elicitation/McpElicitationPrimitiveError.d.ts.map +1 -0
  22. package/dist/primitives/elicitation/McpElicitationPrimitiveError.js +38 -0
  23. package/dist/primitives/elicitation/McpElicitationPrimitiveError.js.map +1 -0
  24. package/dist/primitives/elicitation/McpElicitationPrimitiveFields.d.ts +12 -0
  25. package/dist/primitives/elicitation/McpElicitationPrimitiveFields.d.ts.map +1 -0
  26. package/dist/primitives/elicitation/McpElicitationPrimitiveFields.js +53 -0
  27. package/dist/primitives/elicitation/McpElicitationPrimitiveFields.js.map +1 -0
  28. package/dist/primitives/elicitation/McpElicitationPrimitiveItems.d.ts +13 -0
  29. package/dist/primitives/elicitation/McpElicitationPrimitiveItems.d.ts.map +1 -0
  30. package/dist/primitives/elicitation/McpElicitationPrimitiveItems.js +96 -0
  31. package/dist/primitives/elicitation/McpElicitationPrimitiveItems.js.map +1 -0
  32. package/dist/primitives/elicitation/McpElicitationPrimitiveMessage.d.ts +13 -0
  33. package/dist/primitives/elicitation/McpElicitationPrimitiveMessage.d.ts.map +1 -0
  34. package/dist/primitives/elicitation/McpElicitationPrimitiveMessage.js +29 -0
  35. package/dist/primitives/elicitation/McpElicitationPrimitiveMessage.js.map +1 -0
  36. package/dist/primitives/elicitation/McpElicitationPrimitiveRoot.d.ts +13 -0
  37. package/dist/primitives/elicitation/McpElicitationPrimitiveRoot.d.ts.map +1 -0
  38. package/dist/primitives/elicitation/McpElicitationPrimitiveRoot.js +28 -0
  39. package/dist/primitives/elicitation/McpElicitationPrimitiveRoot.js.map +1 -0
  40. package/dist/primitives/elicitation/context.d.ts +21 -0
  41. package/dist/primitives/elicitation/context.d.ts.map +1 -0
  42. package/dist/primitives/elicitation/context.js +21 -0
  43. package/dist/primitives/elicitation/context.js.map +1 -0
  44. package/dist/primitives/elicitation/initialElicitationDraft.d.ts +5 -0
  45. package/dist/primitives/elicitation/initialElicitationDraft.d.ts.map +1 -0
  46. package/dist/primitives/elicitation/initialElicitationDraft.js +27 -0
  47. package/dist/primitives/elicitation/initialElicitationDraft.js.map +1 -0
  48. package/dist/primitives/elicitation/prepareElicitationContent.d.ts +9 -0
  49. package/dist/primitives/elicitation/prepareElicitationContent.d.ts.map +1 -0
  50. package/dist/primitives/elicitation/prepareElicitationContent.js +46 -0
  51. package/dist/primitives/elicitation/prepareElicitationContent.js.map +1 -0
  52. package/dist/primitives/elicitation.d.ts +15 -0
  53. package/dist/primitives/elicitation.d.ts.map +1 -0
  54. package/dist/primitives/elicitation.js +27 -0
  55. package/dist/primitives/elicitation.js.map +1 -0
  56. package/dist/resources/McpManagerResource.d.ts.map +1 -1
  57. package/dist/resources/McpManagerResource.js +17 -3
  58. package/dist/resources/McpManagerResource.js.map +1 -1
  59. package/dist/resources/McpServerResource.d.ts +4 -0
  60. package/dist/resources/McpServerResource.d.ts.map +1 -1
  61. package/dist/resources/McpServerResource.js +153 -42
  62. package/dist/resources/McpServerResource.js.map +1 -1
  63. package/dist/resources/storage/McpLocalStorage.js +1 -1
  64. package/dist/resources/storage/McpLocalStorage.js.map +1 -1
  65. package/dist/resources/validateElicitationContent.d.ts +9 -0
  66. package/dist/resources/validateElicitationContent.d.ts.map +1 -0
  67. package/dist/resources/validateElicitationContent.js +47 -0
  68. package/dist/resources/validateElicitationContent.js.map +1 -0
  69. package/package.json +7 -6
  70. package/src/auth/createOAuthProvider.ts +2 -2
  71. package/src/auth/types.ts +1 -1
  72. package/src/index.ts +4 -0
  73. package/src/mcp-scope.ts +32 -0
  74. package/src/primitives/elicitation/McpElicitationPrimitiveAccept.tsx +48 -0
  75. package/src/primitives/elicitation/McpElicitationPrimitiveCancel.tsx +37 -0
  76. package/src/primitives/elicitation/McpElicitationPrimitiveDecline.tsx +37 -0
  77. package/src/primitives/elicitation/McpElicitationPrimitiveError.tsx +31 -0
  78. package/src/primitives/elicitation/McpElicitationPrimitiveFields.tsx +56 -0
  79. package/src/primitives/elicitation/McpElicitationPrimitiveItems.tsx +58 -0
  80. package/src/primitives/elicitation/McpElicitationPrimitiveMessage.tsx +26 -0
  81. package/src/primitives/elicitation/McpElicitationPrimitiveRoot.tsx +24 -0
  82. package/src/primitives/elicitation/context.tsx +45 -0
  83. package/src/primitives/elicitation/initialElicitationDraft.test.ts +34 -0
  84. package/src/primitives/elicitation/initialElicitationDraft.ts +39 -0
  85. package/src/primitives/elicitation/prepareElicitationContent.test.ts +415 -0
  86. package/src/primitives/elicitation/prepareElicitationContent.ts +116 -0
  87. package/src/primitives/elicitation.ts +14 -0
  88. package/src/resources/McpManagerResource.test.ts +173 -3
  89. package/src/resources/McpManagerResource.ts +37 -3
  90. package/src/resources/McpServerResource.test.ts +735 -4
  91. package/src/resources/McpServerResource.ts +237 -29
  92. package/src/resources/storage/McpLocalStorage.ts +1 -1
  93. package/src/resources/validateElicitationContent.test.ts +123 -0
  94. 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":"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"}
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.22",
3
+ "version": "0.1.1",
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/sdk": "^1.29.0",
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.1",
33
- "@assistant-ui/store": "0.3.1",
34
- "@assistant-ui/tap": "0.9.7",
35
- "assistant-stream": "0.3.30"
33
+ "@assistant-ui/core": "0.3.3",
34
+ "@assistant-ui/store": "0.3.2",
35
+ "@assistant-ui/tap": "0.9.9",
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/sdk/shared/auth.js";
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
@@ -1,7 +1,7 @@
1
1
  import type {
2
2
  OAuthTokens,
3
3
  OAuthClientInformationFull,
4
- } from "@modelcontextprotocol/sdk/shared/auth.js";
4
+ } from "@modelcontextprotocol/client";
5
5
 
6
6
  export type MCPPersistedAuthState = {
7
7
  tokens?: OAuthTokens;
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
+ };