@indemn/cli 1.7.9 → 1.7.10
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/mcp/tool-meta.d.ts.map +1 -1
- package/dist/mcp/tool-meta.js +5 -0
- package/dist/mcp/tool-meta.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +18 -2
- package/dist/mcp/tools.js.map +1 -1
- package/dist/sdk/access.d.ts +4 -0
- package/dist/sdk/access.d.ts.map +1 -1
- package/dist/sdk/access.js +11 -0
- package/dist/sdk/access.js.map +1 -1
- package/dist/sdk/orgs.d.ts +16 -0
- package/dist/sdk/orgs.d.ts.map +1 -1
- package/dist/sdk/orgs.js +30 -0
- package/dist/sdk/orgs.js.map +1 -1
- package/package.json +1 -1
- package/src/mcp/tool-meta.ts +5 -0
- package/src/mcp/tools.ts +19 -2
- package/src/sdk/access.ts +12 -0
- package/src/sdk/orgs.ts +37 -0
- package/test/mcp/__snapshots__/tools-snapshot.test.ts.snap +1 -0
- package/test/mcp/access.test.ts +18 -0
- package/test/mcp/tools-snapshot.test.ts +1 -1
- package/test/sdk/orgs.test.ts +31 -0
package/dist/sdk/access.d.ts
CHANGED
|
@@ -51,6 +51,10 @@ export declare function refreshAccess(client: IndemnClient): Promise<Access | nu
|
|
|
51
51
|
export declare function usableSomewhere(tool: string, access: Access | null, onlyOrgId?: string): boolean;
|
|
52
52
|
/** The person's role in an org, for messages ("agent in Acme"). */
|
|
53
53
|
export declare function roleIn(access: Access | null, orgId: string | undefined): string | null;
|
|
54
|
+
/** The org's project where the person can invite people: copilot's invite route needs owner or admin there. */
|
|
55
|
+
export declare function inviteProject(access: Access | null, orgId: string): AccessProject | undefined;
|
|
56
|
+
/** Why the person can't invite people to this org, or null (also when access is unknown: the server decides). */
|
|
57
|
+
export declare function inviteDenial(access: Access | null, orgId: string | undefined): string | null;
|
|
54
58
|
/**
|
|
55
59
|
* For org-scoped requirements: why this org can't be used, or null. Project-scoped
|
|
56
60
|
* requirements are decided by the server (the project comes from the agent).
|
package/dist/sdk/access.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/sdk/access.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACzD,eAAe,EAAE,OAAO,CAAC;IACzB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,KAAK,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;AAExC,6GAA6G;AAC7G,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAcvF,CAAC;AAIH,yGAAyG;AACzG,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAKlE;AAiBD,kDAAkD;AAClD,wBAAgB,WAAW,IAAI,IAAI,CAElC;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAWhF;AAMD,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAOhG;AAED,mEAAmE;AACnE,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAKtF;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAgBvG"}
|
|
1
|
+
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/sdk/access.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACzD,eAAe,EAAE,OAAO,CAAC;IACzB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,KAAK,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;AAExC,6GAA6G;AAC7G,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAcvF,CAAC;AAIH,yGAAyG;AACzG,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAKlE;AAiBD,kDAAkD;AAClD,wBAAgB,WAAW,IAAI,IAAI,CAElC;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAWhF;AAMD,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAOhG;AAED,mEAAmE;AACnE,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAKtF;AAED,+GAA+G;AAC/G,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAE7F;AAED,iHAAiH;AACjH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAI5F;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAgBvG"}
|
package/dist/sdk/access.js
CHANGED
|
@@ -86,6 +86,17 @@ export function roleIn(access, orgId) {
|
|
|
86
86
|
return null;
|
|
87
87
|
return `${org.role ?? 'member'} in ${org.org_name}`;
|
|
88
88
|
}
|
|
89
|
+
/** The org's project where the person can invite people: copilot's invite route needs owner or admin there. */
|
|
90
|
+
export function inviteProject(access, orgId) {
|
|
91
|
+
return access?.orgs.find((o) => o.org_id === orgId)?.projects.find((p) => p.role === 'owner' || p.role === 'admin');
|
|
92
|
+
}
|
|
93
|
+
/** Why the person can't invite people to this org, or null (also when access is unknown: the server decides). */
|
|
94
|
+
export function inviteDenial(access, orgId) {
|
|
95
|
+
const org = orgId ? access?.orgs.find((o) => o.org_id === orgId) : undefined;
|
|
96
|
+
if (!org || inviteProject(access, org.org_id))
|
|
97
|
+
return null;
|
|
98
|
+
return `Only owners and admins can invite people, and your role in ${org.org_name} is ${org.role ?? 'member'}.`;
|
|
99
|
+
}
|
|
89
100
|
/**
|
|
90
101
|
* For org-scoped requirements: why this org can't be used, or null. Project-scoped
|
|
91
102
|
* requirements are decided by the server (the project comes from the agent).
|
package/dist/sdk/access.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/sdk/access.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/sdk/access.ts"],"names":[],"mappings":"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"}
|
package/dist/sdk/orgs.d.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { IndemnClient } from './client.js';
|
|
2
2
|
import type { Channel, OrgMember, OrgSummary } from './types.js';
|
|
3
|
+
/** The roles the dashboard's Invite user page offers. copilot saves any role it is sent, so never owner here. */
|
|
4
|
+
export declare const INVITE_ROLES: readonly ["admin", "agent", "member"];
|
|
5
|
+
export type InviteRole = (typeof INVITE_ROLES)[number];
|
|
6
|
+
export interface InviteResult {
|
|
7
|
+
email: string;
|
|
8
|
+
role: InviteRole;
|
|
9
|
+
/** invited: no Indemn account yet, a sign-up email went out; added: an existing account joined now. The others changed nothing. */
|
|
10
|
+
outcome: 'invited' | 'added' | 'already_invited' | 'already_member';
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
3
13
|
export interface SandboxOrg {
|
|
4
14
|
organization_id: string;
|
|
5
15
|
name: string;
|
|
@@ -14,6 +24,12 @@ export declare class OrgsSDK {
|
|
|
14
24
|
listOrganizations(): Promise<OrgSummary[]>;
|
|
15
25
|
/** People with owner, admin or agent roles in the org, one row per person. */
|
|
16
26
|
listMembers(): Promise<OrgMember[]>;
|
|
27
|
+
/**
|
|
28
|
+
* Invite someone to the org's project, as the dashboard's Invite user page does (copilot allows owners and
|
|
29
|
+
* admins only). A new email gets a sign-up invitation; an existing Indemn account is added at once and told
|
|
30
|
+
* by email. Already invited or already a member: nothing is sent, and the result says so.
|
|
31
|
+
*/
|
|
32
|
+
inviteMember(projectId: string, email: string, role: InviteRole): Promise<InviteResult>;
|
|
17
33
|
/**
|
|
18
34
|
* A sandbox organization for a prospect. Only the Indemn support account may create one (copilot
|
|
19
35
|
* refuses anyone else), and copilot always puts it on the sandbox plan set for the platform.
|
package/dist/sdk/orgs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orgs.d.ts","sourceRoot":"","sources":["../../src/sdk/orgs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AASjE,MAAM,WAAW,UAAU;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,qBAAa,OAAO;IACN,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,YAAY;IAExC,+FAA+F;IACzF,iBAAiB,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAIhD,8EAA8E;IACxE,WAAW,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAkCzC;;;OAGG;IACG,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAetD,+DAA+D;IACzD,YAAY,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;CAIzC"}
|
|
1
|
+
{"version":3,"file":"orgs.d.ts","sourceRoot":"","sources":["../../src/sdk/orgs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AASjE,iHAAiH;AACjH,eAAO,MAAM,YAAY,uCAAwC,CAAC;AAClE,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,UAAU,CAAC;IACjB,mIAAmI;IACnI,OAAO,EAAE,SAAS,GAAG,OAAO,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;IACpE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,qBAAa,OAAO;IACN,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,YAAY;IAExC,+FAA+F;IACzF,iBAAiB,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAIhD,8EAA8E;IACxE,WAAW,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAkCzC;;;;OAIG;IACG,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC;IAoB7F;;;OAGG;IACG,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAetD,+DAA+D;IACzD,YAAY,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;CAIzC"}
|
package/dist/sdk/orgs.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** The roles the dashboard's Invite user page offers. copilot saves any role it is sent, so never owner here. */
|
|
2
|
+
export const INVITE_ROLES = ['admin', 'agent', 'member'];
|
|
1
3
|
export class OrgsSDK {
|
|
2
4
|
client;
|
|
3
5
|
constructor(client) {
|
|
@@ -45,6 +47,34 @@ export class OrgsSDK {
|
|
|
45
47
|
}
|
|
46
48
|
return [...byUser.values()];
|
|
47
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Invite someone to the org's project, as the dashboard's Invite user page does (copilot allows owners and
|
|
52
|
+
* admins only). A new email gets a sign-up invitation; an existing Indemn account is added at once and told
|
|
53
|
+
* by email. Already invited or already a member: nothing is sent, and the result says so.
|
|
54
|
+
*/
|
|
55
|
+
async inviteMember(projectId, email, role) {
|
|
56
|
+
const address = email.trim().toLowerCase();
|
|
57
|
+
const result = (outcome, message) => ({ email: address, role, outcome, message });
|
|
58
|
+
try {
|
|
59
|
+
const res = await this.client.copilotPost(`/${projectId}/project_users/invite`, {
|
|
60
|
+
email: address, role, user_available: false, id_organization: await this.client.currentOrgId(),
|
|
61
|
+
});
|
|
62
|
+
return res?.pendingInvitation
|
|
63
|
+
? result('invited', `Invitation sent to ${address}. They join as ${role} once they sign up from the email.`)
|
|
64
|
+
: result('added', `${address} already had an Indemn account and is now in the organization as ${role}. They were told by email.`);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
// copilot answers these with 200 {success:false} and 500/403 with a code; the dashboard shows them as messages.
|
|
68
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
69
|
+
if (/Pending Invitation already exist/i.test(message))
|
|
70
|
+
return result('already_invited', `${address} already has a pending invitation. Nothing was sent again.`);
|
|
71
|
+
if (/already a member/i.test(message))
|
|
72
|
+
return result('already_member', `${address} is already a member of the organization. Nothing was changed.`);
|
|
73
|
+
if (/invite oneself/i.test(message))
|
|
74
|
+
throw new Error('You can\'t invite yourself. Nothing was sent.');
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
48
78
|
/**
|
|
49
79
|
* A sandbox organization for a prospect. Only the Indemn support account may create one (copilot
|
|
50
80
|
* refuses anyone else), and copilot always puts it on the sandbox plan set for the platform.
|
package/dist/sdk/orgs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orgs.js","sourceRoot":"","sources":["../../src/sdk/orgs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"orgs.js","sourceRoot":"","sources":["../../src/sdk/orgs.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/mcp/tool-meta.ts
CHANGED
|
@@ -57,6 +57,11 @@ export const TOOL_META: Record<string, ToolMeta> = {
|
|
|
57
57
|
describe: (a) => `This will create a new organization "${a.name}" on the sandbox plan (its trial, then the plan's monthly price, invoiced to the support account).`,
|
|
58
58
|
},
|
|
59
59
|
list_org_members: { ...read('List organization members'), paginate: true },
|
|
60
|
+
invite_org_member: {
|
|
61
|
+
...write('Invite organization member'),
|
|
62
|
+
guard: true,
|
|
63
|
+
describe: (a) => `This will invite ${a.email} to the organization as ${({ admin: 'Administrator', agent: 'Agent', member: 'Member' } as Record<string, string>)[String(a.role)] ?? a.role}. Without an Indemn account they get an email to sign up; with one they join at once.`,
|
|
64
|
+
},
|
|
60
65
|
list_channels: read('List channels'),
|
|
61
66
|
|
|
62
67
|
// Agents
|
package/src/mcp/tools.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod/v3';
|
|
2
|
-
import { currentAccess, orgDenial, refreshAccess, roleIn, usableSomewhere } from '../sdk/access.js';
|
|
2
|
+
import { currentAccess, inviteDenial, inviteProject, orgDenial, refreshAccess, roleIn, usableSomewhere } from '../sdk/access.js';
|
|
3
3
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
4
|
import type { IndemnClient } from '../sdk/client.js';
|
|
5
5
|
import { AgentInsightsSDK, AgentsSDK } from '../sdk/agents.js';
|
|
@@ -28,7 +28,7 @@ import { exchange, openTestChat } from '../sdk/test-chat.js';
|
|
|
28
28
|
import { ConversationsSDK, conversationToTestSet, conversationUrl, isCliConversation, isTestConversation, projectOfAgent } from '../sdk/conversations.js';
|
|
29
29
|
import { exportEvalMarkdown, exportEvalReport, exportAgentCard } from '../report/export.js';
|
|
30
30
|
import { createRegistry, type Registry } from './registry.js';
|
|
31
|
-
import { OrgsSDK } from '../sdk/orgs.js';
|
|
31
|
+
import { INVITE_ROLES, OrgsSDK } from '../sdk/orgs.js';
|
|
32
32
|
import { whoami } from '../sdk/auth.js';
|
|
33
33
|
import { NeedsInputError, SessionExpiredError } from '../core/runtime.js';
|
|
34
34
|
|
|
@@ -182,6 +182,7 @@ export function registerTools(server: McpServer, client: IndemnClient): Registry
|
|
|
182
182
|
visible: (name) => usableSomewhere(name, currentAccess(client), orgInView()),
|
|
183
183
|
// Refuse early only when the saved access still says no after asking again (it may be stale).
|
|
184
184
|
deny: async (name, orgId) => {
|
|
185
|
+
if (name === 'invite_org_member') return inviteDenial((await refreshAccess(client)) ?? currentAccess(client), orgId);
|
|
185
186
|
if (CAMPAIGN_TOOLS.has(name) && orgId) {
|
|
186
187
|
const on = await client.copilotGet<{ enableSolutions?: boolean }>(`/organization/${orgId}/configurations`).then((c) => c?.enableSolutions === true, () => false);
|
|
187
188
|
if (!on) return SOLUTIONS_OFF;
|
|
@@ -292,6 +293,22 @@ export function registerTools(server: McpServer, client: IndemnClient): Registry
|
|
|
292
293
|
},
|
|
293
294
|
);
|
|
294
295
|
|
|
296
|
+
reg.tool(
|
|
297
|
+
'invite_org_member',
|
|
298
|
+
'Invite someone to the organization by email, as the dashboard\'s Invite user page does. Only owners and admins can invite. Roles: admin (manages agents, knowledge bases, conversations, teammates and organization settings; not billing), agent (handles live conversations, with full AI Studio access; cannot manage teammates or settings) or member (tests and reviews agents and sees conversations). Ask for the role if the person did not say one; there is no owner role here. Someone without an Indemn account gets an email to sign up; an existing account joins at once. The host asks the person to approve.',
|
|
299
|
+
{
|
|
300
|
+
email: z.string().trim().email().describe('Email address of the person to invite'),
|
|
301
|
+
role: z.enum(INVITE_ROLES).describe('admin, agent or member'),
|
|
302
|
+
},
|
|
303
|
+
async (args) => {
|
|
304
|
+
try {
|
|
305
|
+
const project = inviteProject(currentAccess(client) ?? await refreshAccess(client), await client.currentOrgId());
|
|
306
|
+
if (!project) throw new Error('Could not find a project in this organization where you are an owner or admin. Nothing was sent.');
|
|
307
|
+
return success(await orgs.inviteMember(project.project_id, args.email, args.role));
|
|
308
|
+
} catch (err) { return error(err); }
|
|
309
|
+
},
|
|
310
|
+
);
|
|
311
|
+
|
|
295
312
|
reg.tool(
|
|
296
313
|
'list_channels',
|
|
297
314
|
'List the channels an agent can be published on, such as WEB or VOICE.',
|
package/src/sdk/access.ts
CHANGED
|
@@ -120,6 +120,18 @@ export function roleIn(access: Access | null, orgId: string | undefined): string
|
|
|
120
120
|
return `${org.role ?? 'member'} in ${org.org_name}`;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
/** The org's project where the person can invite people: copilot's invite route needs owner or admin there. */
|
|
124
|
+
export function inviteProject(access: Access | null, orgId: string): AccessProject | undefined {
|
|
125
|
+
return access?.orgs.find((o) => o.org_id === orgId)?.projects.find((p) => p.role === 'owner' || p.role === 'admin');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Why the person can't invite people to this org, or null (also when access is unknown: the server decides). */
|
|
129
|
+
export function inviteDenial(access: Access | null, orgId: string | undefined): string | null {
|
|
130
|
+
const org = orgId ? access?.orgs.find((o) => o.org_id === orgId) : undefined;
|
|
131
|
+
if (!org || inviteProject(access, org.org_id)) return null;
|
|
132
|
+
return `Only owners and admins can invite people, and your role in ${org.org_name} is ${org.role ?? 'member'}.`;
|
|
133
|
+
}
|
|
134
|
+
|
|
123
135
|
/**
|
|
124
136
|
* For org-scoped requirements: why this org can't be used, or null. Project-scoped
|
|
125
137
|
* requirements are decided by the server (the project comes from the agent).
|
package/src/sdk/orgs.ts
CHANGED
|
@@ -8,6 +8,18 @@ interface RawProjectUser {
|
|
|
8
8
|
id_project?: { name?: string } | string | null;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/** The roles the dashboard's Invite user page offers. copilot saves any role it is sent, so never owner here. */
|
|
12
|
+
export const INVITE_ROLES = ['admin', 'agent', 'member'] as const;
|
|
13
|
+
export type InviteRole = (typeof INVITE_ROLES)[number];
|
|
14
|
+
|
|
15
|
+
export interface InviteResult {
|
|
16
|
+
email: string;
|
|
17
|
+
role: InviteRole;
|
|
18
|
+
/** invited: no Indemn account yet, a sign-up email went out; added: an existing account joined now. The others changed nothing. */
|
|
19
|
+
outcome: 'invited' | 'added' | 'already_invited' | 'already_member';
|
|
20
|
+
message: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
11
23
|
export interface SandboxOrg {
|
|
12
24
|
organization_id: string;
|
|
13
25
|
name: string;
|
|
@@ -59,6 +71,31 @@ export class OrgsSDK {
|
|
|
59
71
|
return [...byUser.values()];
|
|
60
72
|
}
|
|
61
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Invite someone to the org's project, as the dashboard's Invite user page does (copilot allows owners and
|
|
76
|
+
* admins only). A new email gets a sign-up invitation; an existing Indemn account is added at once and told
|
|
77
|
+
* by email. Already invited or already a member: nothing is sent, and the result says so.
|
|
78
|
+
*/
|
|
79
|
+
async inviteMember(projectId: string, email: string, role: InviteRole): Promise<InviteResult> {
|
|
80
|
+
const address = email.trim().toLowerCase();
|
|
81
|
+
const result = (outcome: InviteResult['outcome'], message: string): InviteResult => ({ email: address, role, outcome, message });
|
|
82
|
+
try {
|
|
83
|
+
const res = await this.client.copilotPost<{ pendingInvitation?: unknown }>(`/${projectId}/project_users/invite`, {
|
|
84
|
+
email: address, role, user_available: false, id_organization: await this.client.currentOrgId(),
|
|
85
|
+
});
|
|
86
|
+
return res?.pendingInvitation
|
|
87
|
+
? result('invited', `Invitation sent to ${address}. They join as ${role} once they sign up from the email.`)
|
|
88
|
+
: result('added', `${address} already had an Indemn account and is now in the organization as ${role}. They were told by email.`);
|
|
89
|
+
} catch (err) {
|
|
90
|
+
// copilot answers these with 200 {success:false} and 500/403 with a code; the dashboard shows them as messages.
|
|
91
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
92
|
+
if (/Pending Invitation already exist/i.test(message)) return result('already_invited', `${address} already has a pending invitation. Nothing was sent again.`);
|
|
93
|
+
if (/already a member/i.test(message)) return result('already_member', `${address} is already a member of the organization. Nothing was changed.`);
|
|
94
|
+
if (/invite oneself/i.test(message)) throw new Error('You can\'t invite yourself. Nothing was sent.');
|
|
95
|
+
throw err;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
62
99
|
/**
|
|
63
100
|
* A sandbox organization for a prospect. Only the Indemn support account may create one (copilot
|
|
64
101
|
* refuses anyone else), and copilot always puts it on the sandbox plan set for the platform.
|
package/test/mcp/access.test.ts
CHANGED
|
@@ -127,6 +127,24 @@ describe('role-aware MCP tools', () => {
|
|
|
127
127
|
await pair.close();
|
|
128
128
|
});
|
|
129
129
|
|
|
130
|
+
it('invites only where the person is an owner or admin, into that project, after approval', async () => {
|
|
131
|
+
const { calls } = recordFetch([
|
|
132
|
+
on('GET', '/auth/me/access', { json: accessWith() }),
|
|
133
|
+
on('POST', '/project_users/invite', { json: { pendingInvitation: { _id: 'i1' } } }),
|
|
134
|
+
]);
|
|
135
|
+
const pair = await connectPair(setup, { elicit: () => ({ action: 'accept', content: { confirm: true } }) });
|
|
136
|
+
const refused = await pair.call('invite_org_member', { org_id: B, email: 'jane@acme.test', role: 'agent' });
|
|
137
|
+
expect(refused.isError).toBe(true);
|
|
138
|
+
expect(refused.body).toContain('Only owners and admins can invite people, and your role in QA B is guest.');
|
|
139
|
+
expect(pair.questions).toHaveLength(0);
|
|
140
|
+
const sent = await pair.call('invite_org_member', { org_id: A, email: 'jane@acme.test', role: 'agent' });
|
|
141
|
+
expect(sent.body.data).toMatchObject({ outcome: 'invited', email: 'jane@acme.test' });
|
|
142
|
+
expect(pair.questions[0].message).toContain('This will invite jane@acme.test to the organization as Agent.');
|
|
143
|
+
const invites = calls.filter((c) => c.url.includes('/project_users/invite'));
|
|
144
|
+
expect(invites.map((c) => new URL(c.url).pathname)).toEqual(['/api/p1/project_users/invite']);
|
|
145
|
+
await pair.close();
|
|
146
|
+
});
|
|
147
|
+
|
|
130
148
|
it('tells the host when the visible tools change', async () => {
|
|
131
149
|
vi.stubEnv('INDEMN_ORG_ID', B);
|
|
132
150
|
recordFetch([on('GET', '/auth/me/access', { json: accessWith(true) })]);
|
|
@@ -17,7 +17,7 @@ describe('MCP tool list', () => {
|
|
|
17
17
|
expect(typeof tool.annotations?.readOnlyHint, tool.name).toBe('boolean');
|
|
18
18
|
}
|
|
19
19
|
expect(tools.map((t) => t.name).sort()).toMatchSnapshot();
|
|
20
|
-
expect(tools).toHaveLength(
|
|
20
|
+
expect(tools).toHaveLength(149); // +invite_org_member // +7 campaign tools // +numbers // +set_agent_transcriber // +set_org_admin_settings // +remove_holiday // +create_sandbox_org
|
|
21
21
|
const props = (name: string) => Object.keys((tools.find((t) => t.name === name)!.inputSchema as { properties?: object }).properties ?? {});
|
|
22
22
|
for (const name of ['list_agents', 'delete_agent', 'set_prompt', 'list_org_members', 'list_channels', 'chat_with_agent']) {
|
|
23
23
|
expect(props(name), name).toContain('org_id');
|
package/test/sdk/orgs.test.ts
CHANGED
|
@@ -58,6 +58,37 @@ describe('OrgsSDK', () => {
|
|
|
58
58
|
await expect(new OrgsSDK(new IndemnClient({ orgId: A })).listMembers()).resolves.toEqual([]);
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
+
it('invites by email into the project, as the dashboard does', async () => {
|
|
62
|
+
const { calls } = recordFetch([on('POST', '/project_users/invite', { json: { msg: 'User not found, save invite in pending ', pendingInvitation: { _id: 'i1' } } })]);
|
|
63
|
+
await expect(new OrgsSDK(new IndemnClient({ orgId: A })).inviteMember('p1', ' Jane@Acme.test ', 'agent')).resolves.toEqual({
|
|
64
|
+
email: 'jane@acme.test', role: 'agent', outcome: 'invited',
|
|
65
|
+
message: 'Invitation sent to jane@acme.test. They join as agent once they sign up from the email.',
|
|
66
|
+
});
|
|
67
|
+
expect(new URL(calls[0].url).pathname).toBe('/api/p1/project_users/invite');
|
|
68
|
+
expect(calls[0].body).toEqual({ email: 'jane@acme.test', role: 'agent', user_available: false, id_organization: A });
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('says when an existing account joined at once', async () => {
|
|
72
|
+
recordFetch([on('POST', '/project_users/invite', { json: { _id: 'pu1', role: 'admin', id_project: 'p1' } })]);
|
|
73
|
+
await expect(new OrgsSDK(new IndemnClient({ orgId: A })).inviteMember('p1', 'jo@acme.test', 'admin')).resolves.toMatchObject({ outcome: 'added', role: 'admin' });
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('already invited or already a member: nothing sent, said plainly', async () => {
|
|
77
|
+
recordFetch([on('POST', '/project_users/invite', { json: { success: false, msg: 'Pending Invitation already exist.', invite: {} } })]);
|
|
78
|
+
await expect(new OrgsSDK(new IndemnClient({ orgId: A })).inviteMember('p1', 'jane@acme.test', 'agent')).resolves.toMatchObject({
|
|
79
|
+
outcome: 'already_invited', message: 'jane@acme.test already has a pending invitation. Nothing was sent again.',
|
|
80
|
+
});
|
|
81
|
+
recordFetch([on('POST', '/project_users/invite', { status: 500, json: { success: false, msg: 'Forbidden. User is already a member', code: 4001, alreadyCurrentProjectMember: true } })]);
|
|
82
|
+
await expect(new OrgsSDK(new IndemnClient({ orgId: A })).inviteMember('p1', 'jo@acme.test', 'member')).resolves.toMatchObject({ outcome: 'already_member' });
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('refuses inviting yourself and keeps copilot\'s other refusals', async () => {
|
|
86
|
+
recordFetch([on('POST', '/project_users/invite', { status: 403, json: { success: false, msg: 'Forbidden. It is not allowed to invite oneself', code: 4000 } })]);
|
|
87
|
+
await expect(new OrgsSDK(new IndemnClient({ orgId: A })).inviteMember('p1', 'pat@acme.test', 'agent')).rejects.toThrow('You can\'t invite yourself. Nothing was sent.');
|
|
88
|
+
recordFetch([on('POST', '/project_users/invite', { status: 403, json: { success: false, msg: 'You don\'t have the required role.' } })]);
|
|
89
|
+
await expect(new OrgsSDK(new IndemnClient({ orgId: A })).inviteMember('p1', 'jane@acme.test', 'agent')).rejects.toThrow('Not allowed: You don\'t have the required role.');
|
|
90
|
+
});
|
|
91
|
+
|
|
61
92
|
it('creates a sandbox org with only its name and links to it', async () => {
|
|
62
93
|
const { calls } = recordFetch([on('POST', '/organization/sandbox', { json: { success: true, id_organization: A, id_project: 'p1', id_plan: 'plan1' } })]);
|
|
63
94
|
await expect(new OrgsSDK(new IndemnClient()).createSandbox('Acme Insurance')).resolves.toEqual({
|