@peoplethread/saas-mcp 0.0.0-stage → 0.3.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 (46) hide show
  1. package/README.md +6 -2
  2. package/dist/client/index.d.ts +1 -0
  3. package/dist/client/index.js +1 -0
  4. package/dist/client/management.d.ts +80 -0
  5. package/dist/client/management.js +78 -0
  6. package/dist/core/http.d.ts +27 -0
  7. package/dist/core/http.js +29 -0
  8. package/dist/core/index.d.ts +5 -0
  9. package/dist/core/index.js +5 -0
  10. package/dist/core/redaction.d.ts +11 -0
  11. package/dist/core/redaction.js +43 -0
  12. package/dist/core/scopes.d.ts +13 -0
  13. package/dist/core/scopes.js +35 -0
  14. package/dist/core/types.d.ts +252 -0
  15. package/dist/core/types.js +1 -0
  16. package/dist/core/urls.d.ts +97 -0
  17. package/dist/core/urls.js +106 -0
  18. package/dist/hono/index.d.ts +1 -0
  19. package/dist/hono/index.js +1 -0
  20. package/dist/hono/routes.d.ts +295 -0
  21. package/dist/hono/routes.js +739 -0
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.js +1 -0
  24. package/dist/node/better-auth.d.ts +84 -0
  25. package/dist/node/better-auth.js +77 -0
  26. package/dist/node/index.d.ts +2 -0
  27. package/dist/node/index.js +2 -0
  28. package/dist/node/oauth.d.ts +18 -0
  29. package/dist/node/oauth.js +39 -0
  30. package/dist/react/McpActivity.d.ts +32 -0
  31. package/dist/react/McpActivity.js +182 -0
  32. package/dist/react/McpConnectedClients.d.ts +27 -0
  33. package/dist/react/McpConnectedClients.js +34 -0
  34. package/dist/react/McpConnectorOverview.d.ts +21 -0
  35. package/dist/react/McpConnectorOverview.js +12 -0
  36. package/dist/react/McpConsentPanel.d.ts +47 -0
  37. package/dist/react/McpConsentPanel.js +18 -0
  38. package/dist/react/McpDeveloperTokens.d.ts +27 -0
  39. package/dist/react/McpDeveloperTokens.js +46 -0
  40. package/dist/react/McpToolReference.d.ts +17 -0
  41. package/dist/react/McpToolReference.js +19 -0
  42. package/dist/react/index.d.ts +6 -0
  43. package/dist/react/index.js +6 -0
  44. package/dist/react/mcp-settings-shared.d.ts +29 -0
  45. package/dist/react/mcp-settings-shared.js +37 -0
  46. package/package.json +98 -3
@@ -0,0 +1,27 @@
1
+ import type { McpManagementClient } from "../client/management.ts";
2
+ import type { McpDeveloperTokenSummary, McpScopeDefinition, McpScopeValue } from "../core/types.ts";
3
+ /** Props for the developer-tokens section. */
4
+ export interface McpDeveloperTokensProps<S extends McpScopeValue = McpScopeValue> {
5
+ /** Product name used in placeholder copy. */
6
+ productName: string;
7
+ /** Workspace whose developer tokens are managed. */
8
+ workspace: {
9
+ id: string;
10
+ displayName?: string;
11
+ };
12
+ /** Current signed-in user creating and revoking tokens. */
13
+ user: {
14
+ id: string;
15
+ };
16
+ /** Product-supported MCP scopes offered during token creation. */
17
+ scopes: McpScopeDefinition<S>[];
18
+ /** Server-provided initial developer tokens for the workspace. */
19
+ initialDeveloperTokens: McpDeveloperTokenSummary<S>[];
20
+ /** Optional management client; defaults to `createMcpManagementClient`. */
21
+ managementClient?: McpManagementClient<S>;
22
+ }
23
+ /**
24
+ * Developer tokens: manually copied MCP bearer tokens for local agents,
25
+ * scripts, and debugging — create form, one-time secret reveal, list, revoke.
26
+ */
27
+ export declare function McpDeveloperTokens<S extends McpScopeValue = McpScopeValue>({ productName, workspace, user, scopes, initialDeveloperTokens, managementClient, }: McpDeveloperTokensProps<S>): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,46 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { KeyRound, Trash2 } from "lucide-react";
4
+ import * as React from "react";
5
+ import { Button, CopyField, SettingsSection } from "@peoplethread/saas-ui";
6
+ import { cx, formatDate, ScopePills, statusLabel, useMcpManagementClient } from "./mcp-settings-shared.js";
7
+ /**
8
+ * Developer tokens: manually copied MCP bearer tokens for local agents,
9
+ * scripts, and debugging — create form, one-time secret reveal, list, revoke.
10
+ */
11
+ export function McpDeveloperTokens({ productName, workspace, user, scopes, initialDeveloperTokens, managementClient, }) {
12
+ const client = useMcpManagementClient(workspace.id, managementClient);
13
+ const [developerTokens, setDeveloperTokens] = React.useState(initialDeveloperTokens);
14
+ const [createdSecret, setCreatedSecret] = React.useState(null);
15
+ const [pendingId, setPendingId] = React.useState(null);
16
+ async function revokeDeveloperToken(tokenId) {
17
+ setPendingId(tokenId);
18
+ try {
19
+ const response = await client.revokeDeveloperToken(tokenId, { userId: user.id });
20
+ setDeveloperTokens((current) => current.map((token) => (token.tokenId === tokenId ? response.token : token)));
21
+ }
22
+ finally {
23
+ setPendingId(null);
24
+ }
25
+ }
26
+ async function createDeveloperToken(event) {
27
+ event.preventDefault();
28
+ const formElement = event.currentTarget;
29
+ const form = new FormData(formElement);
30
+ const selectedScopes = form.getAll("scopes").map(String);
31
+ const response = await client.createDeveloperToken({
32
+ userId: user.id,
33
+ name: String(form.get("name") ?? ""),
34
+ scopes: selectedScopes,
35
+ expiresInDays: Number(form.get("expiresInDays") ?? 30),
36
+ });
37
+ setDeveloperTokens((current) => [response.token, ...current.filter((token) => token.tokenId !== response.token.tokenId)]);
38
+ setCreatedSecret({ tokenId: response.token.tokenId, rawToken: response.rawToken });
39
+ formElement.reset();
40
+ }
41
+ const inputClass = "h-10 w-full rounded-[var(--ssui-radius)] border border-[color:var(--ssui-border)] bg-[color:var(--ssui-surface)] px-3 text-sm text-[color:var(--ssui-text)]";
42
+ return (_jsxs(SettingsSection, { description: "Manually copied bearer tokens for local agents, scripts, and debugging.", icon: _jsx(KeyRound, {}), title: "Access tokens", children: [_jsxs("form", { className: "grid gap-3 border-b border-[color:var(--ssui-border)] pb-4 md:grid-cols-[minmax(0,1fr)_auto_auto]", onSubmit: createDeveloperToken, children: [_jsxs("label", { className: "block", children: [_jsx("span", { className: "mb-1.5 block text-xs font-medium text-[color:var(--ssui-text-muted)]", children: "Token name" }), _jsx("input", { className: inputClass, name: "name", placeholder: `Local ${productName} token`, required: true })] }), _jsxs("label", { className: "block", children: [_jsx("span", { className: "mb-1.5 block text-xs font-medium text-[color:var(--ssui-text-muted)]", children: "Expiration" }), _jsxs("select", { className: cx(inputClass, "min-w-32"), defaultValue: "30", name: "expiresInDays", children: [_jsx("option", { value: "7", children: "7 days" }), _jsx("option", { value: "30", children: "30 days" }), _jsx("option", { value: "90", children: "90 days" })] })] }), _jsxs("div", { children: [_jsx("span", { className: "mb-1.5 block text-xs font-medium text-[color:var(--ssui-text-muted)]", children: "Scopes" }), _jsx("div", { className: "flex min-h-10 flex-wrap items-center gap-2", children: scopes.map((scope, index) => (_jsxs("label", { className: "flex items-center gap-1.5 text-xs text-[color:var(--ssui-text-muted)]", children: [_jsx("input", { defaultChecked: index === 0, name: "scopes", type: "checkbox", value: scope.scope }), scope.title] }, scope.scope))) })] }), _jsx("div", { className: "md:col-span-3", children: _jsxs(Button, { type: "submit", children: [_jsx(KeyRound, { className: "h-4 w-4" }), "Create token"] }) })] }), createdSecret && (_jsxs("div", { className: "mt-4 space-y-2 rounded-[var(--ssui-radius)] border border-[color:var(--ssui-border)] bg-[color:var(--ssui-surface-muted)] p-3", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium", children: "New token secret" }), _jsx("div", { className: "mt-1 text-xs text-[color:var(--ssui-text-muted)]", children: "This secret is shown only now." })] }), _jsx(CopyField, { label: "Access token", value: createdSecret.rawToken })] })), _jsxs("div", { className: "mt-4 divide-y divide-[color:var(--ssui-border)] rounded-[var(--ssui-radius)] border border-[color:var(--ssui-border)]", children: [developerTokens.length === 0 && (_jsx("div", { className: "px-3 py-6 text-center text-sm text-[color:var(--ssui-text-muted)]", children: "No access tokens have been created." })), developerTokens.map((token) => {
43
+ const status = statusLabel(token);
44
+ return (_jsxs("div", { className: "grid gap-3 px-3 py-3 md:grid-cols-[minmax(0,1fr)_auto]", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "font-medium", children: token.name }), _jsxs("span", { className: cx("inline-flex items-center gap-1 rounded-md px-1.5 py-0.5 text-xs", status.className), children: [_jsx(status.Icon, { className: "h-3 w-3" }), status.label] })] }), _jsx("div", { className: "mt-2", children: _jsx(ScopePills, { scopes: token.scopes }) }), _jsxs("div", { className: "mt-2 grid gap-1 text-xs text-[color:var(--ssui-text-muted)] md:grid-cols-3", children: [_jsxs("div", { children: ["Created: ", formatDate(token.createdAt)] }), _jsxs("div", { children: ["Expires: ", formatDate(token.expiresAt)] }), _jsxs("div", { children: ["Last used: ", formatDate(token.lastUsedAt)] })] })] }), _jsxs(Button, { disabled: Boolean(token.revokedAt) || pendingId === token.tokenId, onClick: () => void revokeDeveloperToken(token.tokenId), title: "Revoke access token", type: "button", variant: "ghost", children: [_jsx(Trash2, { className: "h-4 w-4" }), "Revoke"] })] }, token.tokenId));
45
+ })] })] }));
46
+ }
@@ -0,0 +1,17 @@
1
+ import type { McpPromptDescriptor, McpResourceDescriptor, McpScopeDefinition, McpScopeValue, McpToolDescriptor } from "../core/types.ts";
2
+ /** Props for the full tools & capabilities reference. */
3
+ export interface McpToolReferenceProps<S extends McpScopeValue = McpScopeValue> {
4
+ /** Product-supported MCP scopes with descriptions. */
5
+ scopes: McpScopeDefinition<S>[];
6
+ /** Product-owned MCP tool descriptors; grouped by read/write, with anything else under "Other". */
7
+ tools: McpToolDescriptor<S>[];
8
+ /** MCP resources exposed by the product. */
9
+ resources?: McpResourceDescriptor<S>[];
10
+ /** MCP prompts exposed by the product. */
11
+ prompts?: McpPromptDescriptor<S>[];
12
+ }
13
+ /**
14
+ * Full reference for the MCP surface: scopes, tools grouped by read/write, and
15
+ * any resources and prompts. Presentational — reflects the current registry.
16
+ */
17
+ export declare function McpToolReference<S extends McpScopeValue = McpScopeValue>({ scopes, tools, resources, prompts, }: McpToolReferenceProps<S>): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { SettingsSection } from "@peoplethread/saas-ui";
4
+ /**
5
+ * Full reference for the MCP surface: scopes, tools grouped by read/write, and
6
+ * any resources and prompts. Presentational — reflects the current registry.
7
+ */
8
+ export function McpToolReference({ scopes, tools, resources = [], prompts = [], }) {
9
+ // Every tool must land in exactly one group so nothing is silently dropped:
10
+ // tools without a read/write activity kind (e.g. `management` or unset) fall
11
+ // into "Other", which only renders when it has members.
12
+ const otherTools = tools.filter((tool) => tool.activityKind !== "read" && tool.activityKind !== "write");
13
+ const toolGroups = [
14
+ { label: "Read", group: tools.filter((tool) => tool.activityKind === "read") },
15
+ { label: "Write", group: tools.filter((tool) => tool.activityKind === "write") },
16
+ ...(otherTools.length > 0 ? [{ label: "Other", group: otherTools }] : []),
17
+ ];
18
+ return (_jsxs("div", { className: "space-y-5", children: [_jsx(SettingsSection, { description: "Permissions an AI client requests and you approve during connection.", title: "Scopes", children: _jsx("div", { className: "space-y-2", children: scopes.map((scope) => (_jsxs("div", { className: "rounded-[var(--ssui-radius)] border border-[color:var(--ssui-border)] p-3", children: [_jsx("div", { className: "text-sm font-medium", children: scope.title }), _jsx("div", { className: "mt-1 text-xs text-[color:var(--ssui-text-muted)]", children: scope.description }), _jsx("div", { className: "mt-2 font-mono text-xs text-[color:var(--ssui-text-subtle)]", children: scope.scope })] }, scope.scope))) }) }), _jsx(SettingsSection, { description: "Every tool an AI client can call, grouped by read and write behavior (other tools listed under Other).", title: "Tools", children: _jsx("div", { className: "grid gap-2", children: toolGroups.map(({ label, group }) => (_jsxs("div", { className: "rounded-[var(--ssui-radius)] border border-[color:var(--ssui-border)]", children: [_jsx("div", { className: "border-b border-[color:var(--ssui-border)] px-3 py-2 text-xs font-medium uppercase text-[color:var(--ssui-text-muted)]", children: label }), _jsx("div", { className: "divide-y divide-[color:var(--ssui-border)]", children: group.length === 0 ? (_jsxs("div", { className: "px-3 py-2 text-xs text-[color:var(--ssui-text-muted)]", children: ["No ", label.toLowerCase(), " tools."] })) : (group.map((tool) => (_jsxs("div", { className: "px-3 py-2", children: [_jsx("div", { className: "text-sm font-medium", children: tool.title }), _jsx("div", { className: "mt-1 text-xs text-[color:var(--ssui-text-muted)]", children: tool.description }), _jsx("div", { className: "mt-1 font-mono text-xs text-[color:var(--ssui-text-subtle)]", children: tool.name })] }, tool.name)))) })] }, label))) }) }), (resources.length > 0 || prompts.length > 0) && (_jsxs("div", { className: "grid gap-5 md:grid-cols-2", children: [resources.length > 0 && (_jsx(SettingsSection, { description: "Cacheable context an AI client can read.", title: "Resources", children: _jsx("div", { className: "divide-y divide-[color:var(--ssui-border)] rounded-[var(--ssui-radius)] border border-[color:var(--ssui-border)]", children: resources.map((resource) => (_jsxs("div", { className: "px-3 py-2", children: [_jsx("div", { className: "text-sm font-medium", children: resource.title }), _jsx("div", { className: "mt-1 text-xs text-[color:var(--ssui-text-muted)]", children: resource.description }), _jsx("div", { className: "mt-1 font-mono text-xs text-[color:var(--ssui-text-subtle)]", children: resource.uri })] }, resource.uri))) }) })), prompts.length > 0 && (_jsx(SettingsSection, { description: "Guided workflows an AI client can invoke.", title: "Prompts", children: _jsx("div", { className: "divide-y divide-[color:var(--ssui-border)] rounded-[var(--ssui-radius)] border border-[color:var(--ssui-border)]", children: prompts.map((prompt) => (_jsxs("div", { className: "px-3 py-2", children: [_jsx("div", { className: "text-sm font-medium", children: prompt.title }), _jsx("div", { className: "mt-1 text-xs text-[color:var(--ssui-text-muted)]", children: prompt.description }), _jsx("div", { className: "mt-1 font-mono text-xs text-[color:var(--ssui-text-subtle)]", children: prompt.name })] }, prompt.name))) }) }))] }))] }));
19
+ }
@@ -0,0 +1,6 @@
1
+ export * from "./McpConsentPanel.tsx";
2
+ export * from "./McpConnectorOverview.tsx";
3
+ export * from "./McpConnectedClients.tsx";
4
+ export * from "./McpDeveloperTokens.tsx";
5
+ export * from "./McpToolReference.tsx";
6
+ export * from "./McpActivity.tsx";
@@ -0,0 +1,6 @@
1
+ export * from "./McpConsentPanel.js";
2
+ export * from "./McpConnectorOverview.js";
3
+ export * from "./McpConnectedClients.js";
4
+ export * from "./McpDeveloperTokens.js";
5
+ export * from "./McpToolReference.js";
6
+ export * from "./McpActivity.js";
@@ -0,0 +1,29 @@
1
+ import { Check } from "lucide-react";
2
+ import { type McpManagementClient } from "../client/management.ts";
3
+ import type { McpScopeValue } from "../core/types.ts";
4
+ /** Join truthy class fragments. */
5
+ export declare function cx(...values: Array<string | false | null | undefined>): string;
6
+ /** Format an ISO timestamp for settings tables, or "Never" when absent. */
7
+ export declare function formatDate(value: string | null | undefined): string;
8
+ /** Derive a status pill (label, classes, icon) for a token or connection. */
9
+ export declare function statusLabel(item: {
10
+ revokedAt: string | null;
11
+ expired?: boolean;
12
+ }): {
13
+ label: string;
14
+ className: string;
15
+ Icon: typeof Check;
16
+ };
17
+ /** Compact list of scope strings rendered as monospace pills. */
18
+ export declare function ScopePills<S extends McpScopeValue>({ scopes }: {
19
+ scopes: readonly S[];
20
+ }): import("react/jsx-runtime").JSX.Element;
21
+ /** Pretty-printed JSON block used for audit metadata. */
22
+ export declare function JsonBlock({ value }: {
23
+ value: unknown;
24
+ }): import("react/jsx-runtime").JSX.Element;
25
+ /**
26
+ * Resolve the management client for a section: prefer an explicitly provided
27
+ * client, otherwise create one bound to the workspace's default routes.
28
+ */
29
+ export declare function useMcpManagementClient<S extends McpScopeValue = McpScopeValue>(workspaceId: string, managementClient?: McpManagementClient<S>): McpManagementClient<S>;
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Check, XCircle } from "lucide-react";
3
+ import * as React from "react";
4
+ import { createMcpManagementClient } from "../client/management.js";
5
+ /** Join truthy class fragments. */
6
+ export function cx(...values) {
7
+ return values.filter(Boolean).join(" ");
8
+ }
9
+ /** Format an ISO timestamp for settings tables, or "Never" when absent. */
10
+ export function formatDate(value) {
11
+ if (!value)
12
+ return "Never";
13
+ return new Intl.DateTimeFormat(undefined, { dateStyle: "medium", timeStyle: "short" }).format(new Date(value));
14
+ }
15
+ /** Derive a status pill (label, classes, icon) for a token or connection. */
16
+ export function statusLabel(item) {
17
+ if (item.revokedAt)
18
+ return { label: "Revoked", className: "bg-red-100 text-red-700", Icon: XCircle };
19
+ if (item.expired)
20
+ return { label: "Expired", className: "bg-zinc-100 text-zinc-600", Icon: XCircle };
21
+ return { label: "Active", className: "bg-emerald-100 text-emerald-700", Icon: Check };
22
+ }
23
+ /** Compact list of scope strings rendered as monospace pills. */
24
+ export function ScopePills({ scopes }) {
25
+ return (_jsx("div", { className: "flex flex-wrap gap-1", children: scopes.map((scope) => (_jsx("span", { className: "rounded border border-[color:var(--ssui-border)] bg-[color:var(--ssui-surface-muted)] px-1.5 py-0.5 font-mono text-xs text-[color:var(--ssui-text-muted)]", children: scope }, scope))) }));
26
+ }
27
+ /** Pretty-printed JSON block used for audit metadata. */
28
+ export function JsonBlock({ value }) {
29
+ return (_jsx("pre", { className: "max-h-56 overflow-auto rounded-[var(--ssui-radius)] border border-[color:var(--ssui-border)] bg-[color:var(--ssui-surface-muted)] p-3 font-mono text-xs text-[color:var(--ssui-text-muted)]", children: JSON.stringify(value, null, 2) }));
30
+ }
31
+ /**
32
+ * Resolve the management client for a section: prefer an explicitly provided
33
+ * client, otherwise create one bound to the workspace's default routes.
34
+ */
35
+ export function useMcpManagementClient(workspaceId, managementClient) {
36
+ return React.useMemo(() => managementClient ?? createMcpManagementClient({ workspaceId }), [managementClient, workspaceId]);
37
+ }
package/package.json CHANGED
@@ -1,6 +1,101 @@
1
1
  {
2
2
  "name": "@peoplethread/saas-mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.3.1",
4
+ "description": "Shared MCP contracts, OAuth helpers, Hono route plumbing, management client, and React UI for ForwardReach SaaS applications.",
5
+ "type": "module",
6
+ "private": false,
7
+ "license": "UNLICENSED",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/peoplethread/peoplethread.git",
11
+ "directory": "packages/saas-mcp"
12
+ },
13
+ "homepage": "https://github.com/peoplethread/peoplethread/tree/main/packages/saas-mcp#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/peoplethread/peoplethread/issues"
16
+ },
17
+ "publishConfig": {
18
+ "access": "public",
19
+ "registry": "https://registry.npmjs.org/"
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md"
24
+ ],
25
+ "sideEffects": false,
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js"
30
+ },
31
+ "./core": {
32
+ "types": "./dist/core/index.d.ts",
33
+ "import": "./dist/core/index.js"
34
+ },
35
+ "./client": {
36
+ "types": "./dist/client/index.d.ts",
37
+ "import": "./dist/client/index.js"
38
+ },
39
+ "./react": {
40
+ "types": "./dist/react/index.d.ts",
41
+ "import": "./dist/react/index.js"
42
+ },
43
+ "./node": {
44
+ "types": "./dist/node/index.d.ts",
45
+ "import": "./dist/node/index.js"
46
+ },
47
+ "./hono": {
48
+ "types": "./dist/hono/index.d.ts",
49
+ "import": "./dist/hono/index.js"
50
+ },
51
+ "./package.json": "./package.json"
52
+ },
53
+ "peerDependencies": {
54
+ "@modelcontextprotocol/sdk": "^1.29.0",
55
+ "@peoplethread/saas-ui": ">=0.4.0 <1",
56
+ "better-auth": "^1.6.10",
57
+ "hono": "^4.10.3",
58
+ "react": "^19.0.0",
59
+ "react-dom": "^19.0.0"
60
+ },
61
+ "peerDependenciesMeta": {
62
+ "@modelcontextprotocol/sdk": {
63
+ "optional": true
64
+ },
65
+ "better-auth": {
66
+ "optional": true
67
+ },
68
+ "hono": {
69
+ "optional": true
70
+ }
71
+ },
72
+ "dependencies": {
73
+ "lucide-react": "^0.468.0",
74
+ "zod": "^4.4.3"
75
+ },
76
+ "devDependencies": {
77
+ "@modelcontextprotocol/sdk": "^1.29.0",
78
+ "@testing-library/react": "^16.3.0",
79
+ "@types/node": "^24.9.2",
80
+ "@types/react": "^19.2.2",
81
+ "@types/react-dom": "^19.2.2",
82
+ "better-auth": "^1.6.10",
83
+ "hono": "^4.12.29",
84
+ "jsdom": "^26.1.0",
85
+ "rimraf": "^6.0.1",
86
+ "typescript": "^5.9.3",
87
+ "vitest": "^3.2.4",
88
+ "@peoplethread/saas-ui": "0.14.1"
89
+ },
90
+ "gitHead": "138aadb33172fa065e8db02c19e0754b1d64abbf",
91
+ "packedFromDirtyTree": true,
92
+ "packedAt": "2026-10-15T05:08:06.264Z",
93
+ "scripts": {
94
+ "build": "pnpm clean && tsc -p tsconfig.build.json",
95
+ "dev": "tsc -p tsconfig.build.json --watch",
96
+ "clean": "rimraf dist coverage dist-pack *.tgz",
97
+ "pack:local": "pnpm build && mkdir -p dist-pack && pnpm pack --out dist-pack/peoplethread-saas-mcp-local.tgz",
98
+ "test": "vitest run",
99
+ "typecheck": "tsc --noEmit"
100
+ }
6
101
  }