@tightknitai/tightknit 0.1.0-alpha.7 → 0.1.0-alpha.8

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.
@@ -113,7 +113,7 @@ async function parseErrorResponse(response) {
113
113
  let message;
114
114
  try {
115
115
  const body = await response.json();
116
- message = body.message || body.error || response.statusText;
116
+ message = JSON.stringify(body);
117
117
  } catch {
118
118
  message = response.statusText;
119
119
  }
@@ -176,9 +176,10 @@ async function listEvents(params) {
176
176
  );
177
177
  }
178
178
  async function createEvent(input) {
179
+ const { description, ...rest } = input;
179
180
  return apiRequest("/calendar_events", {
180
181
  method: "POST",
181
- body: input,
182
+ body: { ...rest, description: { text: description } },
182
183
  idempotencyKey: crypto.randomUUID()
183
184
  });
184
185
  }
@@ -277,4 +278,4 @@ export {
277
278
  sendMessage,
278
279
  search
279
280
  };
280
- //# sourceMappingURL=chunk-PRA5JZ27.js.map
281
+ //# sourceMappingURL=chunk-DW4LVC2F.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/config.ts","../src/core/version.ts","../src/core/client.ts"],"sourcesContent":["import Conf from \"conf\";\n\nexport type Environment = \"production\" | \"staging\";\n\ninterface TightknitConfig {\n apiKey: string;\n defaultOutput: \"json\" | \"table\";\n environment: Environment;\n}\n\nconst config = new Conf<TightknitConfig>({\n projectName: \"tightknit\",\n defaults: {\n apiKey: \"\",\n defaultOutput: \"table\",\n environment: \"production\",\n },\n});\n\n/** Runtime API key set via --api-key flag (highest precedence) */\nlet runtimeApiKey: string | undefined;\n\n/** Set a runtime API key (from --api-key flag). Takes highest precedence. */\nexport function setRuntimeApiKey(key: string | undefined): void {\n runtimeApiKey = key;\n}\n\n/** Get the API key — --api-key flag > env var > stored config */\nexport function getApiKey(): string {\n return runtimeApiKey || process.env.TIGHTKNIT_API_KEY || config.get(\"apiKey\");\n}\n\n/** Set the API key */\nexport function setApiKey(key: string): void {\n config.set(\"apiKey\", key);\n}\n\n/** Get the default output format */\nexport function getDefaultOutput(): \"json\" | \"table\" {\n return config.get(\"defaultOutput\");\n}\n\n/** Set the default output format */\nexport function setDefaultOutput(format: \"json\" | \"table\"): void {\n config.set(\"defaultOutput\", format);\n}\n\n/** Get the configured environment */\nexport function getEnvironment(): Environment {\n return (process.env.TIGHTKNIT_ENVIRONMENT as Environment) || config.get(\"environment\");\n}\n\n/** Set the environment */\nexport function setEnvironment(env: Environment): void {\n config.set(\"environment\", env);\n}\n\nconst VALID_CONFIG_KEYS = \"api-key, default-output, environment\";\n\n/** Get a config value by key name */\nexport function getConfigValue(key: string): string {\n switch (key) {\n case \"api-key\":\n return getApiKey();\n case \"default-output\":\n return getDefaultOutput();\n case \"environment\":\n return getEnvironment();\n default:\n throw new Error(`Unknown config key: ${key}. Valid keys: ${VALID_CONFIG_KEYS}`);\n }\n}\n\n/** Set a config value by key name */\nexport function setConfigValue(key: string, value: string): void {\n switch (key) {\n case \"api-key\":\n setApiKey(value);\n break;\n case \"default-output\":\n if (value !== \"json\" && value !== \"table\") {\n throw new Error(`Invalid output format: ${value}. Must be \"json\" or \"table\"`);\n }\n setDefaultOutput(value);\n break;\n case \"environment\":\n if (value !== \"production\" && value !== \"staging\") {\n throw new Error(`Invalid environment: ${value}. Must be \"production\" or \"staging\"`);\n }\n setEnvironment(value);\n break;\n default:\n throw new Error(`Unknown config key: ${key}. Valid keys: ${VALID_CONFIG_KEYS}`);\n }\n}\n\n/** Get the config file path (useful for debugging) */\nexport function getConfigPath(): string {\n return config.path;\n}\n","import { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\n\n/** Walks up from the current module to find the CLI's package.json version */\nfunction readVersion(): string {\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 5; i++) {\n try {\n const pkgPath = join(dir, 'package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as {\n name?: string;\n version?: string;\n };\n if (pkg.name === '@tightknitai/tightknit') {\n return pkg.version ?? '0.0.0';\n }\n } catch {\n // continue walking up\n }\n dir = dirname(dir);\n }\n return '0.0.0';\n}\n\nexport const VERSION = readVersion();\n","import { getApiKey, getEnvironment } from \"./config\";\nimport type { ApiError } from \"./types\";\n\nconst BASE_URLS = {\n production: \"https://api.tightknit.ai\",\n staging: \"https://staging-api.tightknit.ai\",\n} as const;\n\nconst API_PREFIX = \"/admin/v0\";\n\n/** Get the base URL for the current environment */\nfunction getBaseUrl(): string {\n return BASE_URLS[getEnvironment()];\n}\n\ninterface RequestOptions {\n method?: \"GET\" | \"POST\" | \"PATCH\" | \"DELETE\";\n body?: Record<string, unknown>;\n params?: Record<string, string | number | boolean | undefined>;\n idempotencyKey?: string;\n}\n\n/** Build a URL with query parameters */\nfunction buildUrl(path: string, params?: Record<string, string | number | boolean | undefined>): string {\n const url = new URL(`${API_PREFIX}${path}`, getBaseUrl());\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n return url.toString();\n}\n\n/** Parse an API error response into a structured error */\nasync function parseErrorResponse(response: Response): Promise<ApiError> {\n let message: string;\n try {\n const body = await response.json() as Record<string, unknown>;\n message = JSON.stringify(body);\n } catch {\n message = response.statusText;\n }\n\n return {\n error: true,\n code: response.status,\n message: `${message} (${response.status})`,\n };\n}\n\n/** Make an authenticated request to the Tightknit API */\nexport async function apiRequest<T>(path: string, options: RequestOptions = {}): Promise<T> {\n const { method = \"GET\", body, params, idempotencyKey } = options;\n\n const apiKey = getApiKey();\n if (!apiKey) {\n throw new TightknitApiError(\n 'No API key configured. Run: tightknit config set api-key <YOUR_KEY>',\n 401\n );\n }\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n\n if (idempotencyKey && (method === \"POST\" || method === \"PATCH\")) {\n headers[\"Idempotency-Key\"] = idempotencyKey;\n }\n\n const url = buildUrl(path, params);\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const apiError = await parseErrorResponse(response);\n throw new TightknitApiError(apiError.message, apiError.code);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return (await response.json()) as T;\n}\n\n/** Custom error class for Tightknit API errors */\nexport class TightknitApiError extends Error {\n code: number;\n\n constructor(message: string, code: number) {\n super(message);\n this.name = \"TightknitApiError\";\n this.code = code;\n }\n\n toJSON(): ApiError {\n return {\n error: true,\n code: this.code,\n message: this.message,\n };\n }\n}\n\n// --- Calendar Events ---\n// Paths use underscores: /calendar_events\n\nexport interface ListEventsParams {\n page?: number;\n per_page?: number;\n time_filter?: \"upcoming\" | \"past\";\n date_filter?: string;\n status?: \"draft\" | \"needs_approval\" | \"published\";\n published_to_site?: boolean;\n is_unlisted?: boolean;\n feed_id?: string;\n tag_ids?: string;\n}\n\n/**\n * List calendar events with optional filtering and pagination.\n * @param params - Optional filters for time, status, feed, pagination, etc.\n * @returns Paginated list of calendar events.\n */\nexport async function listEvents(params?: ListEventsParams) {\n return apiRequest<{ data: unknown[]; page: number; per_page: number }>(\n \"/calendar_events\",\n { params: params as Record<string, string | number | boolean | undefined> }\n );\n}\n\nexport interface CreateEventInput {\n title: string;\n description: string;\n start_date: string;\n end_date: string;\n location?: string;\n link?: string;\n status?: \"needs_approval\" | \"published\";\n slug?: string;\n publish_to_site?: boolean;\n is_unlisted?: boolean;\n allow_public_guest_list?: boolean;\n enable_registration_button?: boolean;\n triggers_webhooks?: boolean;\n external_speakers?: string;\n cover_image_file_id?: string;\n reminders_config?: number[];\n publish_to_slack_channels?: string[];\n}\n\n/**\n * Create a new calendar event.\n * @param input - Event details including title, dates, and optional settings.\n * @returns The created event's ID and success status.\n */\nexport async function createEvent(input: CreateEventInput) {\n const { description, ...rest } = input;\n return apiRequest<{ success: boolean; data: { calendar_event_id: string } }>(\"/calendar_events\", {\n method: \"POST\",\n body: { ...rest, description: { text: description } },\n idempotencyKey: crypto.randomUUID(),\n });\n}\n\n/**\n * Get a single calendar event by ID.\n * @param id - The calendar event ID.\n * @returns The calendar event details.\n */\nexport async function getEvent(id: string) {\n return apiRequest<unknown>(`/calendar_events/${id}`);\n}\n\n/**\n * Delete a calendar event by ID.\n * @param id - The calendar event ID.\n * @returns void on success (204 response).\n */\nexport async function deleteEvent(id: string) {\n return apiRequest<void>(`/calendar_events/${id}`, { method: \"DELETE\" });\n}\n\nexport interface UpdateAttendeeInput {\n user: { slack_user_id: string } | { email: string } | { profile_id: string };\n personal_join_link?: string;\n}\n\n/**\n * Update or add an attendee for a calendar event.\n * @param eventId - The calendar event ID.\n * @param input - Attendee user identifier and optional join link.\n * @returns The updated attendee details.\n */\nexport async function updateAttendee(eventId: string, input: UpdateAttendeeInput) {\n return apiRequest<unknown>(`/calendar_events/${eventId}/attendees`, {\n method: \"PATCH\",\n body: input as unknown as Record<string, unknown>,\n idempotencyKey: crypto.randomUUID(),\n });\n}\n\n// --- Awards ---\n\nexport interface AssignAwardInput {\n recipient: { slack_user_id: string } | { email: string } | { profile_id: string };\n sender?: { slack_user_id: string } | { email: string } | { profile_id: string } | null;\n send_anonymously?: boolean;\n}\n\n/**\n * Assign an award to a recipient.\n * @param awardId - The award ID to assign.\n * @param input - Recipient identifier and optional sender/anonymity settings.\n * @returns Success status.\n */\nexport async function assignAward(awardId: string, input: AssignAwardInput) {\n return apiRequest<{ success: boolean }>(`/awards/${awardId}/assign`, {\n method: \"POST\",\n body: input as unknown as Record<string, unknown>,\n idempotencyKey: crypto.randomUUID(),\n });\n}\n\n// --- Feeds ---\n\nexport interface ListFeedsParams {\n page?: number;\n per_page?: number;\n is_unlisted?: boolean;\n is_archived?: boolean;\n}\n\n/**\n * List feeds with optional filtering and pagination.\n * @param params - Optional filters for unlisted/archived status and pagination.\n * @returns Paginated list of feeds.\n */\nexport async function listFeeds(params?: ListFeedsParams) {\n return apiRequest<{ data: unknown[]; page: number; per_page: number }>(\n \"/feeds\",\n { params: params as Record<string, string | number | boolean | undefined> }\n );\n}\n\n/**\n * Get a single feed by ID.\n * @param feedId - The feed ID.\n * @returns The feed details.\n */\nexport async function getFeed(feedId: string) {\n return apiRequest<unknown>(`/feeds/${feedId}`);\n}\n\nexport interface ListPostsInFeedParams {\n page?: number;\n per_page?: number;\n sort?: \"oldest\" | \"newest\" | \"most-recent-activity\";\n}\n\n/**\n * List posts within a feed with optional sorting and pagination.\n * @param feedId - The feed ID to list posts from.\n * @param params - Optional sort order and pagination.\n * @returns Paginated list of posts.\n */\nexport async function listPostsInFeed(feedId: string, params?: ListPostsInFeedParams) {\n return apiRequest<{ data: unknown[]; page: number; per_page: number }>(\n `/feeds/${feedId}/posts`,\n { params: params as Record<string, string | number | boolean | undefined> }\n );\n}\n\n// --- Posts ---\n\n/**\n * Get a single post by ID.\n * @param postId - The post ID.\n * @returns The post details.\n */\nexport async function getPost(postId: string) {\n return apiRequest<unknown>(`/posts/${postId}`);\n}\n\n// --- Members ---\n\nexport interface AddMemberInput {\n email: string;\n full_name: string;\n avatar_url_original?: string;\n}\n\n/**\n * Add a new member to the community (Enterprise feature).\n * @param input - Member details including email, name, and optional avatar.\n * @returns The created member details.\n */\nexport async function addMember(input: AddMemberInput) {\n return apiRequest<unknown>(\"/members\", {\n method: \"POST\",\n body: input as unknown as Record<string, unknown>,\n idempotencyKey: crypto.randomUUID(),\n });\n}\n\n/**\n * Check if a user is a member of the community by email.\n * @param email - The email address to check.\n * @returns Membership status and details.\n */\nexport async function checkMembership(email: string) {\n return apiRequest<unknown>(\"/members/check\", {\n method: \"POST\",\n body: { email },\n });\n}\n\n// --- Groups ---\n\nexport interface AddUserToGroupInput {\n user: { slack_user_id: string } | { email: string } | { profile_id: string };\n}\n\n/**\n * Add a user to a group.\n * @param groupId - The group ID.\n * @param input - User identifier to add to the group.\n * @returns The updated group membership details.\n */\nexport async function addUserToGroup(groupId: string, input: AddUserToGroupInput) {\n return apiRequest<unknown>(`/groups/${groupId}/members`, {\n method: \"POST\",\n body: input as unknown as Record<string, unknown>,\n idempotencyKey: crypto.randomUUID(),\n });\n}\n\n// --- Messages ---\n\nexport interface SendMessageInput {\n channel: string;\n text: string;\n thread_ts?: string;\n}\n\n/**\n * Send a message to a Slack channel.\n * @param input - Channel, message text, and optional thread timestamp.\n * @returns The sent message details.\n */\nexport async function sendMessage(input: SendMessageInput) {\n return apiRequest<unknown>(\"/messages\", {\n method: \"POST\",\n body: input as unknown as Record<string, unknown>,\n idempotencyKey: crypto.randomUUID(),\n });\n}\n\n// --- Search ---\n\nexport interface SearchParams {\n q: string;\n type: \"post\" | \"comment\" | \"content_resource\";\n page?: number;\n per_page?: number;\n}\n\n/**\n * Search across community content (Beta).\n * @param params - Search query, content type, and optional pagination.\n * @returns Paginated search results.\n */\nexport async function search(params: SearchParams) {\n return apiRequest<{ data: unknown[]; page: number; per_page: number }>(\n \"/search\",\n { params: params as unknown as Record<string, string | number | boolean | undefined> }\n );\n}\n"],"mappings":"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","names":[]}
package/dist/cli/index.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  setConfigValue,
21
21
  setRuntimeApiKey,
22
22
  updateAttendee
23
- } from "../chunk-PRA5JZ27.js";
23
+ } from "../chunk-DW4LVC2F.js";
24
24
 
25
25
  // src/cli/index.ts
26
26
  import { spawn } from "child_process";
@@ -17,7 +17,7 @@ import {
17
17
  sendMessage,
18
18
  setConfigValue,
19
19
  updateAttendee
20
- } from "../chunk-PRA5JZ27.js";
20
+ } from "../chunk-DW4LVC2F.js";
21
21
 
22
22
  // src/mcp/server.ts
23
23
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -30,6 +30,9 @@ var UserIdentifierSchema = z.union([
30
30
  z.object({ email: z.string() }),
31
31
  z.object({ profile_id: z.string() })
32
32
  ]);
33
+ var READ_ONLY = { readOnlyHint: true, destructiveHint: false };
34
+ var WRITE = { readOnlyHint: false, destructiveHint: false };
35
+ var DESTRUCTIVE = { readOnlyHint: false, destructiveHint: true };
33
36
  function registerTools(server2) {
34
37
  server2.tool(
35
38
  "list_events",
@@ -42,6 +45,7 @@ function registerTools(server2) {
42
45
  feed_id: z.string().optional().describe("Filter by feed ID"),
43
46
  tag_ids: z.string().optional().describe("Comma-separated tag UUIDs")
44
47
  },
48
+ READ_ONLY,
45
49
  async (params) => {
46
50
  const result = await listEvents(params);
47
51
  return {
@@ -55,6 +59,7 @@ function registerTools(server2) {
55
59
  "get_event",
56
60
  "Retrieve a calendar event by ID",
57
61
  { id: z.string().describe("Calendar event ID") },
62
+ READ_ONLY,
58
63
  async ({ id }) => {
59
64
  const result = await getEvent(id);
60
65
  return {
@@ -66,20 +71,21 @@ function registerTools(server2) {
66
71
  );
67
72
  server2.tool(
68
73
  "create_event",
69
- "Create a new calendar event",
74
+ 'Create a new calendar event. IMPORTANT: You must provide either a "link" or "location" (or both).',
70
75
  {
71
76
  title: z.string().describe("Event title (3-70 chars)"),
72
- description: z.string().describe("Event description"),
73
- start_date: z.string().describe("Start date/time (ISO 8601)"),
74
- end_date: z.string().describe("End date/time (ISO 8601)"),
75
- location: z.string().optional().describe("Event location"),
76
- link: z.string().optional().describe("Event link/URL"),
77
- slug: z.string().optional().describe("URL slug"),
78
- status: z.enum(["needs_approval", "published"]).optional().describe("Event status"),
79
- publish_to_site: z.boolean().optional().describe("Publish to companion site"),
77
+ description: z.string().describe("Event description (plain text)"),
78
+ start_date: z.string().describe("Start date/time (ISO 8601 with timezone, e.g. 2026-04-10T16:00:00-04:00)"),
79
+ end_date: z.string().describe("End date/time (ISO 8601 with timezone, e.g. 2026-04-10T17:00:00-04:00)"),
80
+ location: z.string().optional().describe('Event location/address. At least one of "link" or "location" is required.'),
81
+ link: z.string().optional().describe('Event link/URL (e.g. Zoom/Meet link). At least one of "link" or "location" is required.'),
82
+ slug: z.string().optional().describe("URL slug (3-70 chars, auto-generated if omitted)"),
83
+ status: z.enum(["needs_approval", "published"]).optional().describe("Event status (defaults to published)"),
84
+ publish_to_site: z.boolean().optional().describe("Publish to companion site (defaults to true)"),
80
85
  enable_registration_button: z.boolean().optional().describe("Enable registration button"),
81
86
  triggers_webhooks: z.boolean().optional().describe("Trigger webhooks")
82
87
  },
88
+ WRITE,
83
89
  async (input) => {
84
90
  const result = await createEvent(input);
85
91
  return {
@@ -93,6 +99,7 @@ function registerTools(server2) {
93
99
  "delete_event",
94
100
  "Delete a calendar event by ID",
95
101
  { id: z.string().describe("Calendar event ID") },
102
+ DESTRUCTIVE,
96
103
  async ({ id }) => {
97
104
  await deleteEvent(id);
98
105
  return {
@@ -113,8 +120,9 @@ function registerTools(server2) {
113
120
  user: UserIdentifierSchema.describe(
114
121
  "User identifier (email, slack_user_id, or profile_id)"
115
122
  ),
116
- personal_join_link: z.string().optional().describe("Personal join link for the attendee")
123
+ personal_join_link: z.string().optional().describe("Personal join link URL for the attendee (must be a valid URL)")
117
124
  },
125
+ WRITE,
118
126
  async ({ event_id, user, personal_join_link }) => {
119
127
  const result = await updateAttendee(event_id, {
120
128
  user,
@@ -138,6 +146,7 @@ function registerTools(server2) {
138
146
  ),
139
147
  send_anonymously: z.boolean().optional().describe("Send anonymously")
140
148
  },
149
+ WRITE,
141
150
  async ({ award_id, recipient, sender, send_anonymously }) => {
142
151
  const result = await assignAward(award_id, {
143
152
  recipient,
@@ -160,6 +169,7 @@ function registerTools(server2) {
160
169
  is_unlisted: z.boolean().optional().describe("Filter to unlisted feeds"),
161
170
  is_archived: z.boolean().optional().describe("Filter to archived feeds")
162
171
  },
172
+ READ_ONLY,
163
173
  async (params) => {
164
174
  const result = await listFeeds(params);
165
175
  return {
@@ -173,6 +183,7 @@ function registerTools(server2) {
173
183
  "get_feed",
174
184
  "Retrieve a feed by ID",
175
185
  { feed_id: z.string().describe("Feed ID") },
186
+ READ_ONLY,
176
187
  async ({ feed_id }) => {
177
188
  const result = await getFeed(feed_id);
178
189
  return {
@@ -191,6 +202,7 @@ function registerTools(server2) {
191
202
  per_page: z.number().optional().describe("Records per page"),
192
203
  sort: z.enum(["oldest", "newest", "most-recent-activity"]).optional().describe("Sort method")
193
204
  },
205
+ READ_ONLY,
194
206
  async ({ feed_id, ...params }) => {
195
207
  const result = await listPostsInFeed(feed_id, params);
196
208
  return {
@@ -204,6 +216,7 @@ function registerTools(server2) {
204
216
  "get_post",
205
217
  "Retrieve a post by ID",
206
218
  { post_id: z.string().describe("Post ID") },
219
+ READ_ONLY,
207
220
  async ({ post_id }) => {
208
221
  const result = await getPost(post_id);
209
222
  return {
@@ -221,6 +234,7 @@ function registerTools(server2) {
221
234
  full_name: z.string().describe("Member full name"),
222
235
  avatar_url_original: z.string().optional().describe("Avatar image URL")
223
236
  },
237
+ WRITE,
224
238
  async (input) => {
225
239
  const result = await addMember(input);
226
240
  return {
@@ -234,6 +248,7 @@ function registerTools(server2) {
234
248
  "check_membership",
235
249
  "Check if an email is a community member",
236
250
  { email: z.string().describe("Email address to check") },
251
+ READ_ONLY,
237
252
  async ({ email }) => {
238
253
  const result = await checkMembership(email);
239
254
  return {
@@ -251,6 +266,7 @@ function registerTools(server2) {
251
266
  text: z.string().describe("Message text (plain text or Slack mrkdwn)"),
252
267
  thread_ts: z.string().optional().describe("Thread timestamp to reply to")
253
268
  },
269
+ WRITE,
254
270
  async (input) => {
255
271
  const result = await sendMessage(input);
256
272
  return {
@@ -267,6 +283,7 @@ function registerTools(server2) {
267
283
  group_id: z.string().describe("Group ID"),
268
284
  user: UserIdentifierSchema.describe("User identifier")
269
285
  },
286
+ WRITE,
270
287
  async ({ group_id, user }) => {
271
288
  const result = await addUserToGroup(group_id, { user });
272
289
  return {
@@ -285,6 +302,7 @@ function registerTools(server2) {
285
302
  page: z.number().optional().describe("Page number (0-indexed)"),
286
303
  per_page: z.number().optional().describe("Records per page")
287
304
  },
305
+ READ_ONLY,
288
306
  async (params) => {
289
307
  const result = await search(params);
290
308
  return {
@@ -298,6 +316,7 @@ function registerTools(server2) {
298
316
  "get_config",
299
317
  "Get a CLI configuration value",
300
318
  { key: z.string().describe("Config key (api-key, default-output, environment)") },
319
+ READ_ONLY,
301
320
  async ({ key }) => {
302
321
  const value = getConfigValue(key);
303
322
  const displayValue = key === "api-key" && value ? `${value.slice(0, 8)}...` : value;
@@ -318,6 +337,7 @@ function registerTools(server2) {
318
337
  key: z.string().describe("Config key (api-key, default-output)"),
319
338
  value: z.string().describe("Config value")
320
339
  },
340
+ WRITE,
321
341
  async ({ key, value }) => {
322
342
  setConfigValue(key, value);
323
343
  return {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/mcp/server.ts","../../src/mcp/tools.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { VERSION } from '../core/version';\nimport { registerTools } from './tools';\n\nconst server = new McpServer({\n name: 'tightknit',\n version: VERSION,\n});\n\nregisterTools(server);\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport {\n listEvents,\n getEvent,\n createEvent,\n deleteEvent,\n updateAttendee,\n assignAward,\n listFeeds,\n getFeed,\n listPostsInFeed,\n getPost,\n addMember,\n checkMembership,\n sendMessage,\n addUserToGroup,\n search\n} from '../core/client';\nimport { getConfigValue, setConfigValue } from '../core/config';\n\nconst UserIdentifierSchema = z.union([\n z.object({ slack_user_id: z.string() }),\n z.object({ email: z.string() }),\n z.object({ profile_id: z.string() })\n]);\n\n/** Register all Tightknit tools on the MCP server */\nexport function registerTools(server: McpServer): void {\n // --- Calendar Events ---\n\n server.tool(\n 'list_events',\n 'List calendar events with pagination and filters',\n {\n page: z.number().optional().describe('Page number (0-indexed)'),\n per_page: z.number().optional().describe('Records per page'),\n time_filter: z\n .enum(['upcoming', 'past'])\n .optional()\n .describe('Filter by upcoming or past'),\n status: z\n .enum(['draft', 'needs_approval', 'published'])\n .optional()\n .describe('Filter by status'),\n feed_id: z.string().optional().describe('Filter by feed ID'),\n tag_ids: z.string().optional().describe('Comma-separated tag UUIDs')\n },\n async (params) => {\n const result = await listEvents(params);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n server.tool(\n 'get_event',\n 'Retrieve a calendar event by ID',\n { id: z.string().describe('Calendar event ID') },\n async ({ id }) => {\n const result = await getEvent(id);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n server.tool(\n 'create_event',\n 'Create a new calendar event',\n {\n title: z.string().describe('Event title (3-70 chars)'),\n description: z.string().describe('Event description'),\n start_date: z.string().describe('Start date/time (ISO 8601)'),\n end_date: z.string().describe('End date/time (ISO 8601)'),\n location: z.string().optional().describe('Event location'),\n link: z.string().optional().describe('Event link/URL'),\n slug: z.string().optional().describe('URL slug'),\n status: z\n .enum(['needs_approval', 'published'])\n .optional()\n .describe('Event status'),\n publish_to_site: z\n .boolean()\n .optional()\n .describe('Publish to companion site'),\n enable_registration_button: z\n .boolean()\n .optional()\n .describe('Enable registration button'),\n triggers_webhooks: z.boolean().optional().describe('Trigger webhooks')\n },\n async (input) => {\n const result = await createEvent(input);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n server.tool(\n 'delete_event',\n 'Delete a calendar event by ID',\n { id: z.string().describe('Calendar event ID') },\n async ({ id }) => {\n await deleteEvent(id);\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({ success: true }, null, 2)\n }\n ]\n };\n }\n );\n\n server.tool(\n 'update_event_attendee',\n 'Update a calendar event attendee',\n {\n event_id: z.string().describe('Calendar event ID'),\n user: UserIdentifierSchema.describe(\n 'User identifier (email, slack_user_id, or profile_id)'\n ),\n personal_join_link: z\n .string()\n .optional()\n .describe('Personal join link for the attendee')\n },\n async ({ event_id, user, personal_join_link }) => {\n const result = await updateAttendee(event_id, {\n user,\n personal_join_link\n });\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n // --- Awards ---\n\n server.tool(\n 'assign_award',\n 'Assign an award to a user and send a Slack notification',\n {\n award_id: z.string().describe('Award UUID'),\n recipient: UserIdentifierSchema.describe('Recipient user identifier'),\n sender: UserIdentifierSchema.optional().describe(\n 'Sender user identifier'\n ),\n send_anonymously: z.boolean().optional().describe('Send anonymously')\n },\n async ({ award_id, recipient, sender, send_anonymously }) => {\n const result = await assignAward(award_id, {\n recipient,\n sender,\n send_anonymously\n });\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n // --- Feeds ---\n\n server.tool(\n 'list_feeds',\n 'List feeds with pagination',\n {\n page: z.number().optional().describe('Page number (0-indexed)'),\n per_page: z.number().optional().describe('Records per page'),\n is_unlisted: z.boolean().optional().describe('Filter to unlisted feeds'),\n is_archived: z.boolean().optional().describe('Filter to archived feeds')\n },\n async (params) => {\n const result = await listFeeds(params);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n server.tool(\n 'get_feed',\n 'Retrieve a feed by ID',\n { feed_id: z.string().describe('Feed ID') },\n async ({ feed_id }) => {\n const result = await getFeed(feed_id);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n server.tool(\n 'list_feed_posts',\n 'List posts in a feed',\n {\n feed_id: z.string().describe('Feed ID or \"home\" for the Home feed'),\n page: z.number().optional().describe('Page number (0-indexed)'),\n per_page: z.number().optional().describe('Records per page'),\n sort: z\n .enum(['oldest', 'newest', 'most-recent-activity'])\n .optional()\n .describe('Sort method')\n },\n async ({ feed_id, ...params }) => {\n const result = await listPostsInFeed(feed_id, params);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n // --- Posts ---\n\n server.tool(\n 'get_post',\n 'Retrieve a post by ID',\n { post_id: z.string().describe('Post ID') },\n async ({ post_id }) => {\n const result = await getPost(post_id);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n // --- Members ---\n\n server.tool(\n 'add_member',\n 'Add a member to the community (Enterprise plan required)',\n {\n email: z.string().describe('Member email address'),\n full_name: z.string().describe('Member full name'),\n avatar_url_original: z.string().optional().describe('Avatar image URL')\n },\n async (input) => {\n const result = await addMember(input);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n server.tool(\n 'check_membership',\n 'Check if an email is a community member',\n { email: z.string().describe('Email address to check') },\n async ({ email }) => {\n const result = await checkMembership(email);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n // --- Messages ---\n\n server.tool(\n 'send_message',\n 'Send a Slack message to a channel or user',\n {\n channel: z.string().describe('Slack channel ID or user ID'),\n text: z.string().describe('Message text (plain text or Slack mrkdwn)'),\n thread_ts: z.string().optional().describe('Thread timestamp to reply to')\n },\n async (input) => {\n const result = await sendMessage(input);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n // --- Groups ---\n\n server.tool(\n 'add_group_member',\n 'Add a user to a group',\n {\n group_id: z.string().describe('Group ID'),\n user: UserIdentifierSchema.describe('User identifier')\n },\n async ({ group_id, user }) => {\n const result = await addUserToGroup(group_id, { user });\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n // --- Search ---\n\n server.tool(\n 'search',\n '[Beta] Search documents by query',\n {\n q: z.string().describe('Search query string'),\n type: z\n .enum(['post', 'comment', 'content_resource'])\n .describe('Entity type to search'),\n page: z.number().optional().describe('Page number (0-indexed)'),\n per_page: z.number().optional().describe('Records per page')\n },\n async (params) => {\n const result = await search(params);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n // --- Config ---\n\n server.tool(\n 'get_config',\n 'Get a CLI configuration value',\n { key: z.string().describe('Config key (api-key, default-output, environment)') },\n async ({ key }) => {\n const value = getConfigValue(key);\n const displayValue =\n key === 'api-key' && value ? `${value.slice(0, 8)}...` : value;\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({ key, value: displayValue }, null, 2)\n }\n ]\n };\n }\n );\n\n server.tool(\n 'set_config',\n 'Set a CLI configuration value',\n {\n key: z.string().describe('Config key (api-key, default-output)'),\n value: z.string().describe('Config value')\n },\n async ({ key, value }) => {\n setConfigValue(key, value);\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(\n { success: true, key, value: key === 'api-key' ? '***' : value },\n null,\n 2\n )\n }\n ]\n };\n }\n );\n}\n"],"mappings":"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","names":["server"]}
1
+ {"version":3,"sources":["../../src/mcp/server.ts","../../src/mcp/tools.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { VERSION } from '../core/version';\nimport { registerTools } from './tools';\n\nconst server = new McpServer({\n name: 'tightknit',\n version: VERSION,\n});\n\nregisterTools(server);\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport {\n listEvents,\n getEvent,\n createEvent,\n deleteEvent,\n updateAttendee,\n assignAward,\n listFeeds,\n getFeed,\n listPostsInFeed,\n getPost,\n addMember,\n checkMembership,\n sendMessage,\n addUserToGroup,\n search\n} from '../core/client';\nimport { getConfigValue, setConfigValue } from '../core/config';\n\nconst UserIdentifierSchema = z.union([\n z.object({ slack_user_id: z.string() }),\n z.object({ email: z.string() }),\n z.object({ profile_id: z.string() })\n]);\n\nconst READ_ONLY = { readOnlyHint: true, destructiveHint: false } as const;\nconst WRITE = { readOnlyHint: false, destructiveHint: false } as const;\nconst DESTRUCTIVE = { readOnlyHint: false, destructiveHint: true } as const;\n\n/** Register all Tightknit tools on the MCP server */\nexport function registerTools(server: McpServer): void {\n // --- Calendar Events ---\n\n server.tool(\n 'list_events',\n 'List calendar events with pagination and filters',\n {\n page: z.number().optional().describe('Page number (0-indexed)'),\n per_page: z.number().optional().describe('Records per page'),\n time_filter: z\n .enum(['upcoming', 'past'])\n .optional()\n .describe('Filter by upcoming or past'),\n status: z\n .enum(['draft', 'needs_approval', 'published'])\n .optional()\n .describe('Filter by status'),\n feed_id: z.string().optional().describe('Filter by feed ID'),\n tag_ids: z.string().optional().describe('Comma-separated tag UUIDs')\n },\n READ_ONLY,\n async (params) => {\n const result = await listEvents(params);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n server.tool(\n 'get_event',\n 'Retrieve a calendar event by ID',\n { id: z.string().describe('Calendar event ID') },\n READ_ONLY,\n async ({ id }) => {\n const result = await getEvent(id);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n server.tool(\n 'create_event',\n 'Create a new calendar event. IMPORTANT: You must provide either a \"link\" or \"location\" (or both).',\n {\n title: z.string().describe('Event title (3-70 chars)'),\n description: z.string().describe('Event description (plain text)'),\n start_date: z.string().describe('Start date/time (ISO 8601 with timezone, e.g. 2026-04-10T16:00:00-04:00)'),\n end_date: z.string().describe('End date/time (ISO 8601 with timezone, e.g. 2026-04-10T17:00:00-04:00)'),\n location: z.string().optional().describe('Event location/address. At least one of \"link\" or \"location\" is required.'),\n link: z.string().optional().describe('Event link/URL (e.g. Zoom/Meet link). At least one of \"link\" or \"location\" is required.'),\n slug: z.string().optional().describe('URL slug (3-70 chars, auto-generated if omitted)'),\n status: z\n .enum(['needs_approval', 'published'])\n .optional()\n .describe('Event status (defaults to published)'),\n publish_to_site: z\n .boolean()\n .optional()\n .describe('Publish to companion site (defaults to true)'),\n enable_registration_button: z\n .boolean()\n .optional()\n .describe('Enable registration button'),\n triggers_webhooks: z.boolean().optional().describe('Trigger webhooks')\n },\n WRITE,\n async (input) => {\n const result = await createEvent(input);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n server.tool(\n 'delete_event',\n 'Delete a calendar event by ID',\n { id: z.string().describe('Calendar event ID') },\n DESTRUCTIVE,\n async ({ id }) => {\n await deleteEvent(id);\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({ success: true }, null, 2)\n }\n ]\n };\n }\n );\n\n server.tool(\n 'update_event_attendee',\n 'Update a calendar event attendee',\n {\n event_id: z.string().describe('Calendar event ID'),\n user: UserIdentifierSchema.describe(\n 'User identifier (email, slack_user_id, or profile_id)'\n ),\n personal_join_link: z\n .string()\n .optional()\n .describe('Personal join link URL for the attendee (must be a valid URL)')\n },\n WRITE,\n async ({ event_id, user, personal_join_link }) => {\n const result = await updateAttendee(event_id, {\n user,\n personal_join_link\n });\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n // --- Awards ---\n\n server.tool(\n 'assign_award',\n 'Assign an award to a user and send a Slack notification',\n {\n award_id: z.string().describe('Award UUID'),\n recipient: UserIdentifierSchema.describe('Recipient user identifier'),\n sender: UserIdentifierSchema.optional().describe(\n 'Sender user identifier'\n ),\n send_anonymously: z.boolean().optional().describe('Send anonymously')\n },\n WRITE,\n async ({ award_id, recipient, sender, send_anonymously }) => {\n const result = await assignAward(award_id, {\n recipient,\n sender,\n send_anonymously\n });\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n // --- Feeds ---\n\n server.tool(\n 'list_feeds',\n 'List feeds with pagination',\n {\n page: z.number().optional().describe('Page number (0-indexed)'),\n per_page: z.number().optional().describe('Records per page'),\n is_unlisted: z.boolean().optional().describe('Filter to unlisted feeds'),\n is_archived: z.boolean().optional().describe('Filter to archived feeds')\n },\n READ_ONLY,\n async (params) => {\n const result = await listFeeds(params);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n server.tool(\n 'get_feed',\n 'Retrieve a feed by ID',\n { feed_id: z.string().describe('Feed ID') },\n READ_ONLY,\n async ({ feed_id }) => {\n const result = await getFeed(feed_id);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n server.tool(\n 'list_feed_posts',\n 'List posts in a feed',\n {\n feed_id: z.string().describe('Feed ID or \"home\" for the Home feed'),\n page: z.number().optional().describe('Page number (0-indexed)'),\n per_page: z.number().optional().describe('Records per page'),\n sort: z\n .enum(['oldest', 'newest', 'most-recent-activity'])\n .optional()\n .describe('Sort method')\n },\n READ_ONLY,\n async ({ feed_id, ...params }) => {\n const result = await listPostsInFeed(feed_id, params);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n // --- Posts ---\n\n server.tool(\n 'get_post',\n 'Retrieve a post by ID',\n { post_id: z.string().describe('Post ID') },\n READ_ONLY,\n async ({ post_id }) => {\n const result = await getPost(post_id);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n // --- Members ---\n\n server.tool(\n 'add_member',\n 'Add a member to the community (Enterprise plan required)',\n {\n email: z.string().describe('Member email address'),\n full_name: z.string().describe('Member full name'),\n avatar_url_original: z.string().optional().describe('Avatar image URL')\n },\n WRITE,\n async (input) => {\n const result = await addMember(input);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n server.tool(\n 'check_membership',\n 'Check if an email is a community member',\n { email: z.string().describe('Email address to check') },\n READ_ONLY,\n async ({ email }) => {\n const result = await checkMembership(email);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n // --- Messages ---\n\n server.tool(\n 'send_message',\n 'Send a Slack message to a channel or user',\n {\n channel: z.string().describe('Slack channel ID or user ID'),\n text: z.string().describe('Message text (plain text or Slack mrkdwn)'),\n thread_ts: z.string().optional().describe('Thread timestamp to reply to')\n },\n WRITE,\n async (input) => {\n const result = await sendMessage(input);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n // --- Groups ---\n\n server.tool(\n 'add_group_member',\n 'Add a user to a group',\n {\n group_id: z.string().describe('Group ID'),\n user: UserIdentifierSchema.describe('User identifier')\n },\n WRITE,\n async ({ group_id, user }) => {\n const result = await addUserToGroup(group_id, { user });\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n // --- Search ---\n\n server.tool(\n 'search',\n '[Beta] Search documents by query',\n {\n q: z.string().describe('Search query string'),\n type: z\n .enum(['post', 'comment', 'content_resource'])\n .describe('Entity type to search'),\n page: z.number().optional().describe('Page number (0-indexed)'),\n per_page: z.number().optional().describe('Records per page')\n },\n READ_ONLY,\n async (params) => {\n const result = await search(params);\n return {\n content: [\n { type: 'text' as const, text: JSON.stringify(result, null, 2) }\n ]\n };\n }\n );\n\n // --- Config ---\n\n server.tool(\n 'get_config',\n 'Get a CLI configuration value',\n { key: z.string().describe('Config key (api-key, default-output, environment)') },\n READ_ONLY,\n async ({ key }) => {\n const value = getConfigValue(key);\n const displayValue =\n key === 'api-key' && value ? `${value.slice(0, 8)}...` : value;\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({ key, value: displayValue }, null, 2)\n }\n ]\n };\n }\n );\n\n server.tool(\n 'set_config',\n 'Set a CLI configuration value',\n {\n key: z.string().describe('Config key (api-key, default-output)'),\n value: z.string().describe('Config value')\n },\n WRITE,\n async ({ key, value }) => {\n setConfigValue(key, value);\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(\n { success: true, key, value: key === 'api-key' ? '***' : value },\n null,\n 2\n )\n }\n ]\n };\n }\n );\n}\n"],"mappings":"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","names":["server"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tightknitai/tightknit",
3
- "version": "0.1.0-alpha.7",
3
+ "version": "0.1.0-alpha.8",
4
4
  "description": "CLI and MCP server for the Tightknit API",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -34,6 +34,8 @@
34
34
  "test": "vitest run",
35
35
  "test:unit": "vitest run",
36
36
  "test:unit-silent": "vitest run --reporter=dot",
37
+ "test:mcp": "npx @modelcontextprotocol/inspector node bin/tightknit-mcp.js",
38
+ "test:mcp:dev": "npx @modelcontextprotocol/inspector tsx src/mcp/server.ts",
37
39
  "typecheck": "tsc --noEmit"
38
40
  },
39
41
  "dependencies": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/config.ts","../src/core/version.ts","../src/core/client.ts"],"sourcesContent":["import Conf from \"conf\";\n\nexport type Environment = \"production\" | \"staging\";\n\ninterface TightknitConfig {\n apiKey: string;\n defaultOutput: \"json\" | \"table\";\n environment: Environment;\n}\n\nconst config = new Conf<TightknitConfig>({\n projectName: \"tightknit\",\n defaults: {\n apiKey: \"\",\n defaultOutput: \"table\",\n environment: \"production\",\n },\n});\n\n/** Runtime API key set via --api-key flag (highest precedence) */\nlet runtimeApiKey: string | undefined;\n\n/** Set a runtime API key (from --api-key flag). Takes highest precedence. */\nexport function setRuntimeApiKey(key: string | undefined): void {\n runtimeApiKey = key;\n}\n\n/** Get the API key — --api-key flag > env var > stored config */\nexport function getApiKey(): string {\n return runtimeApiKey || process.env.TIGHTKNIT_API_KEY || config.get(\"apiKey\");\n}\n\n/** Set the API key */\nexport function setApiKey(key: string): void {\n config.set(\"apiKey\", key);\n}\n\n/** Get the default output format */\nexport function getDefaultOutput(): \"json\" | \"table\" {\n return config.get(\"defaultOutput\");\n}\n\n/** Set the default output format */\nexport function setDefaultOutput(format: \"json\" | \"table\"): void {\n config.set(\"defaultOutput\", format);\n}\n\n/** Get the configured environment */\nexport function getEnvironment(): Environment {\n return (process.env.TIGHTKNIT_ENVIRONMENT as Environment) || config.get(\"environment\");\n}\n\n/** Set the environment */\nexport function setEnvironment(env: Environment): void {\n config.set(\"environment\", env);\n}\n\nconst VALID_CONFIG_KEYS = \"api-key, default-output, environment\";\n\n/** Get a config value by key name */\nexport function getConfigValue(key: string): string {\n switch (key) {\n case \"api-key\":\n return getApiKey();\n case \"default-output\":\n return getDefaultOutput();\n case \"environment\":\n return getEnvironment();\n default:\n throw new Error(`Unknown config key: ${key}. Valid keys: ${VALID_CONFIG_KEYS}`);\n }\n}\n\n/** Set a config value by key name */\nexport function setConfigValue(key: string, value: string): void {\n switch (key) {\n case \"api-key\":\n setApiKey(value);\n break;\n case \"default-output\":\n if (value !== \"json\" && value !== \"table\") {\n throw new Error(`Invalid output format: ${value}. Must be \"json\" or \"table\"`);\n }\n setDefaultOutput(value);\n break;\n case \"environment\":\n if (value !== \"production\" && value !== \"staging\") {\n throw new Error(`Invalid environment: ${value}. Must be \"production\" or \"staging\"`);\n }\n setEnvironment(value);\n break;\n default:\n throw new Error(`Unknown config key: ${key}. Valid keys: ${VALID_CONFIG_KEYS}`);\n }\n}\n\n/** Get the config file path (useful for debugging) */\nexport function getConfigPath(): string {\n return config.path;\n}\n","import { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\n\n/** Walks up from the current module to find the CLI's package.json version */\nfunction readVersion(): string {\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 5; i++) {\n try {\n const pkgPath = join(dir, 'package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as {\n name?: string;\n version?: string;\n };\n if (pkg.name === '@tightknitai/tightknit') {\n return pkg.version ?? '0.0.0';\n }\n } catch {\n // continue walking up\n }\n dir = dirname(dir);\n }\n return '0.0.0';\n}\n\nexport const VERSION = readVersion();\n","import { getApiKey, getEnvironment } from \"./config\";\nimport type { ApiError } from \"./types\";\n\nconst BASE_URLS = {\n production: \"https://api.tightknit.ai\",\n staging: \"https://staging-api.tightknit.ai\",\n} as const;\n\nconst API_PREFIX = \"/admin/v0\";\n\n/** Get the base URL for the current environment */\nfunction getBaseUrl(): string {\n return BASE_URLS[getEnvironment()];\n}\n\ninterface RequestOptions {\n method?: \"GET\" | \"POST\" | \"PATCH\" | \"DELETE\";\n body?: Record<string, unknown>;\n params?: Record<string, string | number | boolean | undefined>;\n idempotencyKey?: string;\n}\n\n/** Build a URL with query parameters */\nfunction buildUrl(path: string, params?: Record<string, string | number | boolean | undefined>): string {\n const url = new URL(`${API_PREFIX}${path}`, getBaseUrl());\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n return url.toString();\n}\n\n/** Parse an API error response into a structured error */\nasync function parseErrorResponse(response: Response): Promise<ApiError> {\n let message: string;\n try {\n const body = await response.json() as Record<string, unknown>;\n message = (body.message as string) || (body.error as string) || response.statusText;\n } catch {\n message = response.statusText;\n }\n\n return {\n error: true,\n code: response.status,\n message: `${message} (${response.status})`,\n };\n}\n\n/** Make an authenticated request to the Tightknit API */\nexport async function apiRequest<T>(path: string, options: RequestOptions = {}): Promise<T> {\n const { method = \"GET\", body, params, idempotencyKey } = options;\n\n const apiKey = getApiKey();\n if (!apiKey) {\n throw new TightknitApiError(\n 'No API key configured. Run: tightknit config set api-key <YOUR_KEY>',\n 401\n );\n }\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n\n if (idempotencyKey && (method === \"POST\" || method === \"PATCH\")) {\n headers[\"Idempotency-Key\"] = idempotencyKey;\n }\n\n const url = buildUrl(path, params);\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const apiError = await parseErrorResponse(response);\n throw new TightknitApiError(apiError.message, apiError.code);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return (await response.json()) as T;\n}\n\n/** Custom error class for Tightknit API errors */\nexport class TightknitApiError extends Error {\n code: number;\n\n constructor(message: string, code: number) {\n super(message);\n this.name = \"TightknitApiError\";\n this.code = code;\n }\n\n toJSON(): ApiError {\n return {\n error: true,\n code: this.code,\n message: this.message,\n };\n }\n}\n\n// --- Calendar Events ---\n// Paths use underscores: /calendar_events\n\nexport interface ListEventsParams {\n page?: number;\n per_page?: number;\n time_filter?: \"upcoming\" | \"past\";\n date_filter?: string;\n status?: \"draft\" | \"needs_approval\" | \"published\";\n published_to_site?: boolean;\n is_unlisted?: boolean;\n feed_id?: string;\n tag_ids?: string;\n}\n\n/**\n * List calendar events with optional filtering and pagination.\n * @param params - Optional filters for time, status, feed, pagination, etc.\n * @returns Paginated list of calendar events.\n */\nexport async function listEvents(params?: ListEventsParams) {\n return apiRequest<{ data: unknown[]; page: number; per_page: number }>(\n \"/calendar_events\",\n { params: params as Record<string, string | number | boolean | undefined> }\n );\n}\n\nexport interface CreateEventInput {\n title: string;\n description: string;\n start_date: string;\n end_date: string;\n location?: string;\n link?: string;\n status?: \"needs_approval\" | \"published\";\n slug?: string;\n publish_to_site?: boolean;\n is_unlisted?: boolean;\n allow_public_guest_list?: boolean;\n enable_registration_button?: boolean;\n triggers_webhooks?: boolean;\n external_speakers?: string;\n cover_image_file_id?: string;\n reminders_config?: number[];\n publish_to_slack_channels?: string[];\n}\n\n/**\n * Create a new calendar event.\n * @param input - Event details including title, dates, and optional settings.\n * @returns The created event's ID and success status.\n */\nexport async function createEvent(input: CreateEventInput) {\n return apiRequest<{ success: boolean; data: { calendar_event_id: string } }>(\"/calendar_events\", {\n method: \"POST\",\n body: input as unknown as Record<string, unknown>,\n idempotencyKey: crypto.randomUUID(),\n });\n}\n\n/**\n * Get a single calendar event by ID.\n * @param id - The calendar event ID.\n * @returns The calendar event details.\n */\nexport async function getEvent(id: string) {\n return apiRequest<unknown>(`/calendar_events/${id}`);\n}\n\n/**\n * Delete a calendar event by ID.\n * @param id - The calendar event ID.\n * @returns void on success (204 response).\n */\nexport async function deleteEvent(id: string) {\n return apiRequest<void>(`/calendar_events/${id}`, { method: \"DELETE\" });\n}\n\nexport interface UpdateAttendeeInput {\n user: { slack_user_id: string } | { email: string } | { profile_id: string };\n personal_join_link?: string;\n}\n\n/**\n * Update or add an attendee for a calendar event.\n * @param eventId - The calendar event ID.\n * @param input - Attendee user identifier and optional join link.\n * @returns The updated attendee details.\n */\nexport async function updateAttendee(eventId: string, input: UpdateAttendeeInput) {\n return apiRequest<unknown>(`/calendar_events/${eventId}/attendees`, {\n method: \"PATCH\",\n body: input as unknown as Record<string, unknown>,\n idempotencyKey: crypto.randomUUID(),\n });\n}\n\n// --- Awards ---\n\nexport interface AssignAwardInput {\n recipient: { slack_user_id: string } | { email: string } | { profile_id: string };\n sender?: { slack_user_id: string } | { email: string } | { profile_id: string } | null;\n send_anonymously?: boolean;\n}\n\n/**\n * Assign an award to a recipient.\n * @param awardId - The award ID to assign.\n * @param input - Recipient identifier and optional sender/anonymity settings.\n * @returns Success status.\n */\nexport async function assignAward(awardId: string, input: AssignAwardInput) {\n return apiRequest<{ success: boolean }>(`/awards/${awardId}/assign`, {\n method: \"POST\",\n body: input as unknown as Record<string, unknown>,\n idempotencyKey: crypto.randomUUID(),\n });\n}\n\n// --- Feeds ---\n\nexport interface ListFeedsParams {\n page?: number;\n per_page?: number;\n is_unlisted?: boolean;\n is_archived?: boolean;\n}\n\n/**\n * List feeds with optional filtering and pagination.\n * @param params - Optional filters for unlisted/archived status and pagination.\n * @returns Paginated list of feeds.\n */\nexport async function listFeeds(params?: ListFeedsParams) {\n return apiRequest<{ data: unknown[]; page: number; per_page: number }>(\n \"/feeds\",\n { params: params as Record<string, string | number | boolean | undefined> }\n );\n}\n\n/**\n * Get a single feed by ID.\n * @param feedId - The feed ID.\n * @returns The feed details.\n */\nexport async function getFeed(feedId: string) {\n return apiRequest<unknown>(`/feeds/${feedId}`);\n}\n\nexport interface ListPostsInFeedParams {\n page?: number;\n per_page?: number;\n sort?: \"oldest\" | \"newest\" | \"most-recent-activity\";\n}\n\n/**\n * List posts within a feed with optional sorting and pagination.\n * @param feedId - The feed ID to list posts from.\n * @param params - Optional sort order and pagination.\n * @returns Paginated list of posts.\n */\nexport async function listPostsInFeed(feedId: string, params?: ListPostsInFeedParams) {\n return apiRequest<{ data: unknown[]; page: number; per_page: number }>(\n `/feeds/${feedId}/posts`,\n { params: params as Record<string, string | number | boolean | undefined> }\n );\n}\n\n// --- Posts ---\n\n/**\n * Get a single post by ID.\n * @param postId - The post ID.\n * @returns The post details.\n */\nexport async function getPost(postId: string) {\n return apiRequest<unknown>(`/posts/${postId}`);\n}\n\n// --- Members ---\n\nexport interface AddMemberInput {\n email: string;\n full_name: string;\n avatar_url_original?: string;\n}\n\n/**\n * Add a new member to the community (Enterprise feature).\n * @param input - Member details including email, name, and optional avatar.\n * @returns The created member details.\n */\nexport async function addMember(input: AddMemberInput) {\n return apiRequest<unknown>(\"/members\", {\n method: \"POST\",\n body: input as unknown as Record<string, unknown>,\n idempotencyKey: crypto.randomUUID(),\n });\n}\n\n/**\n * Check if a user is a member of the community by email.\n * @param email - The email address to check.\n * @returns Membership status and details.\n */\nexport async function checkMembership(email: string) {\n return apiRequest<unknown>(\"/members/check\", {\n method: \"POST\",\n body: { email },\n });\n}\n\n// --- Groups ---\n\nexport interface AddUserToGroupInput {\n user: { slack_user_id: string } | { email: string } | { profile_id: string };\n}\n\n/**\n * Add a user to a group.\n * @param groupId - The group ID.\n * @param input - User identifier to add to the group.\n * @returns The updated group membership details.\n */\nexport async function addUserToGroup(groupId: string, input: AddUserToGroupInput) {\n return apiRequest<unknown>(`/groups/${groupId}/members`, {\n method: \"POST\",\n body: input as unknown as Record<string, unknown>,\n idempotencyKey: crypto.randomUUID(),\n });\n}\n\n// --- Messages ---\n\nexport interface SendMessageInput {\n channel: string;\n text: string;\n thread_ts?: string;\n}\n\n/**\n * Send a message to a Slack channel.\n * @param input - Channel, message text, and optional thread timestamp.\n * @returns The sent message details.\n */\nexport async function sendMessage(input: SendMessageInput) {\n return apiRequest<unknown>(\"/messages\", {\n method: \"POST\",\n body: input as unknown as Record<string, unknown>,\n idempotencyKey: crypto.randomUUID(),\n });\n}\n\n// --- Search ---\n\nexport interface SearchParams {\n q: string;\n type: \"post\" | \"comment\" | \"content_resource\";\n page?: number;\n per_page?: number;\n}\n\n/**\n * Search across community content (Beta).\n * @param params - Search query, content type, and optional pagination.\n * @returns Paginated search results.\n */\nexport async function search(params: SearchParams) {\n return apiRequest<{ data: unknown[]; page: number; per_page: number }>(\n \"/search\",\n { params: params as unknown as Record<string, string | number | boolean | undefined> }\n );\n}\n"],"mappings":"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","names":[]}