@agentrysh/mcp 0.0.13 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tools.js CHANGED
@@ -16,12 +16,14 @@ export const TOOL_DESCRIPTORS = [
16
16
  },
17
17
  {
18
18
  name: "agentry_login",
19
- description: "Authenticate the user via GitHub device flow. Returns an API key, stored locally. " +
19
+ description: "Authenticate the user via the agentry sign-in page (GitHub, Google, or magic link — " +
20
+ "user picks any provider). Returns an API key, stored locally. " +
20
21
  "" +
21
22
  "RECOMMENDED two-call sequence for interactive sessions: " +
22
- " 1. Call with mode='start_only' → returns user_code + verification_uri + device_code. " +
23
- " Show the user the code and URL (DO NOT ask them to confirm authorization — they'll " +
24
- " just open the URL, paste the code, and you'll poll). " +
23
+ " 1. Call with mode='start_only' → returns user_code + verification_uri + device_code " +
24
+ " (plus verification_uri_complete with the code pre-filled). Show the user the URL " +
25
+ " and code (DO NOT ask them to confirm authorization — they'll open the URL, sign in " +
26
+ " with their provider of choice, and you'll poll). " +
25
27
  " 2. IMMEDIATELY call again with mode='full' + the device_code from step 1. " +
26
28
  " This blocks and auto-polls every ~5s for up to timeout_seconds (default 300 = 5min). " +
27
29
  " Returns the api_key when the user authorizes, or status='expired'/'denied' on failure. " +
@@ -606,15 +608,20 @@ export const TOOL_DESCRIPTORS = [
606
608
  },
607
609
  {
608
610
  name: "agentry_repair_analytics",
609
- description: "Re-attempt PostHog provisioning for the authenticated user. Idempotent — if the user " +
610
- "already has a PostHog project, returns its id without recreating. Use this when " +
611
+ description: "Re-attempt PostHog provisioning for one project. Idempotent — if the project already " +
612
+ "has a PostHog team binding, returns its id without recreating. Use this when " +
611
613
  "agentry_verify_install reports analytics ❌ with reason 'no_posthog_project' OR when a " +
612
614
  "/v1/track/ call returns 503 with that code. " +
613
615
  "" +
614
616
  "DO NOT re-run agentry_login for this failure mode — that mints a new api_key and " +
615
- "churns the user's local config. This tool runs only the provisioning step that was " +
616
- "supposed to happen at login.",
617
- inputSchema: { type: "object", properties: {}, additionalProperties: false },
617
+ "churns the user's local config. This tool repairs the project-scoped analytics binding only.",
618
+ inputSchema: {
619
+ type: "object",
620
+ properties: {
621
+ project_id: { type: "string", description: "Project to repair. Defaults to local default_project_id." },
622
+ },
623
+ additionalProperties: false,
624
+ },
618
625
  },
619
626
  {
620
627
  name: "agentry_rotate_key",
@@ -1339,6 +1346,59 @@ export const TOOL_DESCRIPTORS = [
1339
1346
  additionalProperties: false,
1340
1347
  },
1341
1348
  },
1349
+ {
1350
+ name: "agentry_invite_teammate",
1351
+ description: "Mint a one-shot invite URL granting a teammate access to a project. Owner-only. " +
1352
+ "The recipient opens the URL, signs in with any provider (GitHub / Google / magic link), " +
1353
+ "and gets their own agentry account + API key automatically scoped to this project — they " +
1354
+ "do NOT use your API key. " +
1355
+ "" +
1356
+ "After this returns, share `invite_url` with your teammate (Slack, email — manual for v1). " +
1357
+ "Invite expires in 7 days, single-use.",
1358
+ inputSchema: {
1359
+ type: "object",
1360
+ properties: {
1361
+ project_id: { type: "string", description: "Defaults to the local default project." },
1362
+ email: {
1363
+ type: "string",
1364
+ description: "Optional — shown on the invite landing page as a hint to the recipient. " +
1365
+ "Does NOT restrict who can consume the invite; the link is the credential.",
1366
+ },
1367
+ role: {
1368
+ type: "string",
1369
+ enum: ["member", "owner"],
1370
+ description: "Defaults to 'member'. 'owner' is rare — only add other owners deliberately.",
1371
+ },
1372
+ },
1373
+ additionalProperties: false,
1374
+ },
1375
+ },
1376
+ {
1377
+ name: "agentry_list_members",
1378
+ description: "List members of a project + any pending (un-consumed, un-expired) invites. " +
1379
+ "Any member can see the full roster; remove members via agentry_remove_member.",
1380
+ inputSchema: {
1381
+ type: "object",
1382
+ properties: {
1383
+ project_id: { type: "string", description: "Defaults to the local default project." },
1384
+ },
1385
+ additionalProperties: false,
1386
+ },
1387
+ },
1388
+ {
1389
+ name: "agentry_remove_member",
1390
+ description: "Remove a teammate from a project. Owner-only. Cannot remove the last remaining owner, " +
1391
+ "and an owner cannot remove themselves (transfer ownership first — TBD).",
1392
+ inputSchema: {
1393
+ type: "object",
1394
+ properties: {
1395
+ project_id: { type: "string", description: "Defaults to the local default project." },
1396
+ user_id: { type: "string", description: "The user_id from agentry_list_members." },
1397
+ },
1398
+ required: ["user_id"],
1399
+ additionalProperties: false,
1400
+ },
1401
+ },
1342
1402
  ];
1343
1403
  // ---------------------------------------------------------------------------
1344
1404
  // Helpers shared across tool handlers
@@ -1453,7 +1513,7 @@ export async function dispatchTool(name, args) {
1453
1513
  case "agentry_rotate_key":
1454
1514
  return await handleRotateKey();
1455
1515
  case "agentry_repair_analytics":
1456
- return await handleRepairAnalytics();
1516
+ return await handleRepairAnalytics(a.project_id ? String(a.project_id) : undefined);
1457
1517
  case "agentry_publish_query":
1458
1518
  return await handlePublishQuery({
1459
1519
  project_id: a.project_id ? String(a.project_id) : undefined,
@@ -1821,6 +1881,19 @@ export async function dispatchTool(name, args) {
1821
1881
  kind: a.kind ? String(a.kind) : undefined,
1822
1882
  resolved: typeof a.resolved === "boolean" ? a.resolved : undefined,
1823
1883
  });
1884
+ case "agentry_invite_teammate":
1885
+ return await handleInviteTeammate({
1886
+ project_id: a.project_id ? String(a.project_id) : undefined,
1887
+ email: a.email ? String(a.email) : undefined,
1888
+ role: a.role === "owner" ? "owner" : "member",
1889
+ });
1890
+ case "agentry_list_members":
1891
+ return await handleListMembers(a.project_id ? String(a.project_id) : undefined);
1892
+ case "agentry_remove_member":
1893
+ return await handleRemoveMember({
1894
+ project_id: a.project_id ? String(a.project_id) : undefined,
1895
+ user_id: String(a.user_id ?? ""),
1896
+ });
1824
1897
  default:
1825
1898
  return {
1826
1899
  error: {
@@ -1850,7 +1923,13 @@ function handleStatus() {
1850
1923
  project_count: projectIds.length,
1851
1924
  projects: projectIds.map((id) => {
1852
1925
  const p = cfg.projects[id];
1853
- return { id, name: p.name, local_path: p.local_path };
1926
+ return {
1927
+ id,
1928
+ name: p.name,
1929
+ local_path: p.local_path,
1930
+ analytics_ready: p.analytics_ready ?? null,
1931
+ posthog_project_id: p.posthog_project_id ?? null,
1932
+ };
1854
1933
  }),
1855
1934
  onboarding: hint,
1856
1935
  next_steps: [hint.message, hint.next_action],
@@ -1860,14 +1939,16 @@ async function handleLogin(input) {
1860
1939
  const cfg = loadConfig();
1861
1940
  if (input.mode === "start_only") {
1862
1941
  const start = await api.startDeviceFlow(cfg);
1942
+ const openUrl = start.verification_uri_complete ?? start.verification_uri;
1863
1943
  return {
1864
1944
  mode: "start_only",
1865
1945
  verification_uri: start.verification_uri,
1946
+ verification_uri_complete: start.verification_uri_complete,
1866
1947
  user_code: start.user_code,
1867
1948
  device_code: start.device_code,
1868
1949
  interval: start.interval,
1869
1950
  expires_in: start.expires_in,
1870
- next_action: `Show the user: "Open ${start.verification_uri} and enter the code ${start.user_code}." ` +
1951
+ next_action: `Show the user a clickable link: ${openUrl} one click takes them to sign-in, no code entry. ` +
1871
1952
  `Then IMMEDIATELY call agentry_login again with mode='full' and device_code='${start.device_code}'. ` +
1872
1953
  `That call will block and auto-poll for up to ${start.expires_in}s — DO NOT ask the user to confirm authorization before polling.`,
1873
1954
  };
@@ -1966,7 +2047,7 @@ async function handleLogin(input) {
1966
2047
  device_code: deviceCode,
1967
2048
  next_action: `Timed out after ${input.timeout_seconds ?? 300}s. ` +
1968
2049
  (verificationUri && userCode
1969
- ? `Confirm the user opened ${verificationUri} and entered ${userCode}, `
2050
+ ? `Confirm the user opened ${verificationUri}?code=${userCode}, `
1970
2051
  : "Confirm the user has authorized, ") +
1971
2052
  `then call agentry_login again with mode='full' and device_code='${deviceCode}' to resume polling.`,
1972
2053
  };
@@ -1995,7 +2076,7 @@ async function handleRotateKey() {
1995
2076
  "New key stored locally. Old key is revoked — update any places it was pasted (CI envs, etc).",
1996
2077
  };
1997
2078
  }
1998
- async function handleRepairAnalytics() {
2079
+ async function handleRepairAnalytics(projectId) {
1999
2080
  const cfg = loadConfig();
2000
2081
  if (!cfg.api_key) {
2001
2082
  return {
@@ -2006,9 +2087,32 @@ async function handleRepairAnalytics() {
2006
2087
  },
2007
2088
  };
2008
2089
  }
2090
+ const picked = pickProject(cfg, projectId);
2091
+ if (!picked || !picked.project) {
2092
+ return {
2093
+ error: {
2094
+ code: "no_project",
2095
+ message: "No local project selected for analytics repair.",
2096
+ next_action: "Create a project first or pass a project_id from agentry_list_projects.",
2097
+ },
2098
+ };
2099
+ }
2009
2100
  try {
2010
- const resp = await api.repairAnalyticsBackend(cfg);
2011
- return resp;
2101
+ const resp = await api.repairAnalyticsBackend(cfg, picked.id);
2102
+ const updatedProject = {
2103
+ ...picked.project,
2104
+ analytics_ready: resp.posthog_project_id !== null,
2105
+ posthog_project_id: resp.posthog_project_id,
2106
+ };
2107
+ const nextCfg = {
2108
+ ...cfg,
2109
+ projects: {
2110
+ ...cfg.projects,
2111
+ [picked.id]: updatedProject,
2112
+ },
2113
+ };
2114
+ saveConfig(nextCfg);
2115
+ return { project_id: picked.id, ...resp };
2012
2116
  }
2013
2117
  catch (err) {
2014
2118
  return {
@@ -2016,7 +2120,7 @@ async function handleRepairAnalytics() {
2016
2120
  code: "repair_failed",
2017
2121
  message: err instanceof Error ? err.message : String(err),
2018
2122
  next_action: "Upstream PostHog provisioning failed. If the error mentions 5xx / timeouts / rate " +
2019
- "limits, wait 30–60s and call agentry_repair_analytics again. Errors and deploys are " +
2123
+ "limits, wait 30–60s and call agentry_repair_analytics again for the same project. Errors and deploys are " +
2020
2124
  "unaffected; only analytics ingest needs PostHog.",
2021
2125
  },
2022
2126
  };
@@ -2425,6 +2529,8 @@ async function handleListProjects() {
2425
2529
  ...p,
2426
2530
  local_path: local?.local_path ?? null,
2427
2531
  dsn_known_locally: Boolean(local?.dsn),
2532
+ analytics_ready: local?.analytics_ready ?? null,
2533
+ posthog_project_id: local?.posthog_project_id ?? null,
2428
2534
  is_default: cfg.default_project_id === p.id,
2429
2535
  };
2430
2536
  });
@@ -2469,6 +2575,8 @@ async function handleCreateProject(input) {
2469
2575
  dsn: resp.dsn,
2470
2576
  local_path: input.local_path ?? null,
2471
2577
  default_branch: resp.default_branch ?? input.default_branch ?? "main",
2578
+ analytics_ready: resp.analytics?.ready,
2579
+ posthog_project_id: resp.analytics?.posthog_project_id ?? null,
2472
2580
  };
2473
2581
  const nextCfg = {
2474
2582
  ...cfg,
@@ -2497,6 +2605,7 @@ async function handleCreateProject(input) {
2497
2605
  logs_url: resp.logs_url,
2498
2606
  analytics_url: resp.analytics_url,
2499
2607
  deploys_url: resp.deploys_url,
2608
+ analytics: resp.analytics,
2500
2609
  },
2501
2610
  install_snippet: install,
2502
2611
  next_action: resp.next_action ??
@@ -3310,17 +3419,17 @@ async function handleVerifyInstall(input) {
3310
3419
  const analyticsDetail = checks.analytics?.detail ?? "";
3311
3420
  const noPosthogProject = !checks.analytics?.ok &&
3312
3421
  (analyticsDetail.includes("no_posthog_project") ||
3313
- analyticsDetail.includes("user has no PostHog project provisioned"));
3422
+ analyticsDetail.includes("project has no PostHog project provisioned"));
3314
3423
  const baseAction = failed.length === 0
3315
3424
  ? "Install verified. Errors land in agentry_list_cases; analytics flow to PostHog; deploys via agentry_list_deploys."
3316
3425
  : noPosthogProject && failed.length === 1
3317
3426
  ? "Analytics is the only failed signal AND the cause is missing PostHog provisioning " +
3318
- "(first-login provisioning was best-effort and failed). Call agentry_repair_analytics " +
3427
+ "(the project binding is missing). Call agentry_repair_analytics " +
3319
3428
  "— it's idempotent and runs the same provisioning step. Then re-run agentry_verify_install. " +
3320
3429
  "DO NOT re-run agentry_login for this."
3321
3430
  : `Install incomplete. Failed signal types: ${failed.join(", ")}. ` +
3322
3431
  (noPosthogProject
3323
- ? "Analytics failed because the user has no PostHog project provisioned — " +
3432
+ ? "Analytics failed because the project has no PostHog project provisioned — " +
3324
3433
  "call agentry_repair_analytics, then re-run verify. "
3325
3434
  : "") +
3326
3435
  "For each failed type, re-read its corresponding step in agentry_install_guide and fix.";
@@ -3774,4 +3883,61 @@ async function handleListFeedback(input) {
3774
3883
  const resp = await api.listFeedback(cfg, opts);
3775
3884
  return resp;
3776
3885
  }
3886
+ // ---------------------------------------------------------------------------
3887
+ // Team invites + member management (Phase 4)
3888
+ // ---------------------------------------------------------------------------
3889
+ async function handleInviteTeammate(input) {
3890
+ const cfg = loadConfig();
3891
+ const picked = pickProject(cfg, input.project_id);
3892
+ if (!picked) {
3893
+ return {
3894
+ error: {
3895
+ code: "no_project",
3896
+ message: "No project_id given and no default project set.",
3897
+ next_action: "Pass project_id, or call agentry_create_project.",
3898
+ },
3899
+ };
3900
+ }
3901
+ const body = { role: input.role };
3902
+ if (input.email)
3903
+ body.email = input.email;
3904
+ return await api.inviteTeammate(cfg, picked.id, body);
3905
+ }
3906
+ async function handleListMembers(projectId) {
3907
+ const cfg = loadConfig();
3908
+ const picked = pickProject(cfg, projectId);
3909
+ if (!picked) {
3910
+ return {
3911
+ error: {
3912
+ code: "no_project",
3913
+ message: "No project_id given and no default project set.",
3914
+ next_action: "Pass project_id.",
3915
+ },
3916
+ };
3917
+ }
3918
+ return await api.listMembers(cfg, picked.id);
3919
+ }
3920
+ async function handleRemoveMember(input) {
3921
+ const cfg = loadConfig();
3922
+ const picked = pickProject(cfg, input.project_id);
3923
+ if (!picked) {
3924
+ return {
3925
+ error: {
3926
+ code: "no_project",
3927
+ message: "No project_id given and no default project set.",
3928
+ next_action: "Pass project_id.",
3929
+ },
3930
+ };
3931
+ }
3932
+ if (!input.user_id) {
3933
+ return {
3934
+ error: {
3935
+ code: "invalid_payload",
3936
+ message: "user_id is required.",
3937
+ next_action: "Call agentry_list_members to find the user_id to remove.",
3938
+ },
3939
+ };
3940
+ }
3941
+ return await api.removeMember(cfg, picked.id, input.user_id);
3942
+ }
3777
3943
  //# sourceMappingURL=tools.js.map