@codazen/harmonica-mcp 4.0.1 → 4.0.2
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/index.js +60 -31
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -25533,17 +25533,14 @@ function registerMembershipTools(server, ctx, client) {
|
|
|
25533
25533
|
);
|
|
25534
25534
|
server.tool(
|
|
25535
25535
|
"add_member",
|
|
25536
|
-
"
|
|
25536
|
+
"Grant a user access to the configured organization. Their name and avatar are filled in when they first sign in.",
|
|
25537
25537
|
{
|
|
25538
|
-
name: external_exports.string().describe("Full name of the user"),
|
|
25539
25538
|
email: external_exports.string().email().describe("Email address (used as the user ID)"),
|
|
25540
|
-
title: external_exports.string().optional().describe("Job title"),
|
|
25541
|
-
responsibility: external_exports.string().optional().describe("Primary responsibility or role description"),
|
|
25542
25539
|
role: external_exports.enum(["member", "admin", "client"]).optional().describe('Organization role (defaults to "member")')
|
|
25543
25540
|
},
|
|
25544
|
-
async ({
|
|
25541
|
+
async ({ email: email2, role }) => {
|
|
25545
25542
|
try {
|
|
25546
|
-
const member = await client.addOrgMember(ctx.orgId, {
|
|
25543
|
+
const member = await client.addOrgMember(ctx.orgId, { email: email2, role });
|
|
25547
25544
|
const lines = [
|
|
25548
25545
|
"Member added successfully.",
|
|
25549
25546
|
"",
|
|
@@ -25562,17 +25559,14 @@ function registerMembershipTools(server, ctx, client) {
|
|
|
25562
25559
|
);
|
|
25563
25560
|
server.tool(
|
|
25564
25561
|
"update_member_role",
|
|
25565
|
-
"
|
|
25562
|
+
"Change a member's role within the configured organization.",
|
|
25566
25563
|
{
|
|
25567
25564
|
userId: external_exports.string().describe("User ID (email address) of the member to update"),
|
|
25568
|
-
role: external_exports.enum(["member", "admin", "client"]).
|
|
25569
|
-
name: external_exports.string().optional().describe("Updated display name"),
|
|
25570
|
-
title: external_exports.string().optional().describe("Updated job title"),
|
|
25571
|
-
responsibility: external_exports.string().optional().describe("Updated responsibility description")
|
|
25565
|
+
role: external_exports.enum(["member", "admin", "client"]).describe("New organization role")
|
|
25572
25566
|
},
|
|
25573
|
-
async ({ userId, role
|
|
25567
|
+
async ({ userId, role }) => {
|
|
25574
25568
|
try {
|
|
25575
|
-
const member = await client.updateOrgMember(ctx.orgId, userId, { role
|
|
25569
|
+
const member = await client.updateOrgMember(ctx.orgId, userId, { role });
|
|
25576
25570
|
if (!member) {
|
|
25577
25571
|
return { content: [{ type: "text", text: `Member not found: "${userId}"` }], isError: true };
|
|
25578
25572
|
}
|
|
@@ -32688,6 +32682,16 @@ function safeParseErrorBody(body) {
|
|
|
32688
32682
|
return void 0;
|
|
32689
32683
|
}
|
|
32690
32684
|
}
|
|
32685
|
+
var ORG_ROLE_PRECEDENCE = ["admin", "member", "client"];
|
|
32686
|
+
function asLegacyMember(m) {
|
|
32687
|
+
const roles = Array.isArray(m["roles"]) ? m["roles"] : [];
|
|
32688
|
+
const rank = (role2) => {
|
|
32689
|
+
const i = ORG_ROLE_PRECEDENCE.indexOf(role2);
|
|
32690
|
+
return i === -1 ? ORG_ROLE_PRECEDENCE.length : i;
|
|
32691
|
+
};
|
|
32692
|
+
const role = [...roles].sort((a, b) => rank(a) - rank(b) || a.localeCompare(b))[0];
|
|
32693
|
+
return { ...m, role: role ?? "" };
|
|
32694
|
+
}
|
|
32691
32695
|
function createHttpClient(config2) {
|
|
32692
32696
|
const url = new URL(config2.apiBaseUrl);
|
|
32693
32697
|
const isLocalhost = url.hostname === "localhost" || url.hostname === "127.0.0.1";
|
|
@@ -32802,21 +32806,37 @@ function createHttpClient(config2) {
|
|
|
32802
32806
|
getMembership: async (userId, orgId) => {
|
|
32803
32807
|
return { userId, orgId, role: "member" };
|
|
32804
32808
|
},
|
|
32805
|
-
// Org membership management
|
|
32809
|
+
// Org membership management — the generic member API, shared with the
|
|
32810
|
+
// teamspace, account and system tiers (rev-4551c900). The org-only routes
|
|
32811
|
+
// this used to call were a second implementation over the same Grant rows
|
|
32812
|
+
// that skipped the lockout rule.
|
|
32813
|
+
//
|
|
32814
|
+
// Those routes also set a name, title and responsibility on the User
|
|
32815
|
+
// record. Nothing does that any more: a name is written on first sign-in
|
|
32816
|
+
// from the identity provider, and the fields are gone from the tool
|
|
32817
|
+
// schemas rather than accepted and silently dropped.
|
|
32806
32818
|
listOrgMembers: async (orgId) => {
|
|
32807
|
-
const result = await request("GET", `/api/
|
|
32808
|
-
return result ?? [];
|
|
32819
|
+
const result = await request("GET", `/api/orgs/${encodeURIComponent(orgId)}/members`);
|
|
32820
|
+
return (result?.members ?? []).map(asLegacyMember);
|
|
32809
32821
|
},
|
|
32810
32822
|
addOrgMember: async (orgId, input) => {
|
|
32811
|
-
const result = await request("POST", `/api/
|
|
32812
|
-
|
|
32823
|
+
const result = await request("POST", `/api/orgs/${encodeURIComponent(orgId)}/members`, {
|
|
32824
|
+
userId: input.email.toLowerCase(),
|
|
32825
|
+
roles: [input.role ?? "member"]
|
|
32826
|
+
});
|
|
32827
|
+
return asLegacyMember(result?.member ?? {});
|
|
32813
32828
|
},
|
|
32814
32829
|
updateOrgMember: async (orgId, userId, input) => {
|
|
32815
|
-
|
|
32816
|
-
|
|
32830
|
+
if (!input.role) {
|
|
32831
|
+
const current = await request("GET", `/api/orgs/${encodeURIComponent(orgId)}/members`);
|
|
32832
|
+
const found = (current?.members ?? []).find((m) => String(m["userId"]).toLowerCase() === userId.toLowerCase());
|
|
32833
|
+
return found ? asLegacyMember(found) : void 0;
|
|
32834
|
+
}
|
|
32835
|
+
const result = await request("PATCH", `/api/orgs/${encodeURIComponent(orgId)}/members/${encodeURIComponent(userId)}`, { roles: [input.role] });
|
|
32836
|
+
return asLegacyMember(result?.member ?? {});
|
|
32817
32837
|
},
|
|
32818
32838
|
removeOrgMember: async (orgId, userId) => {
|
|
32819
|
-
await request("DELETE", `/api/
|
|
32839
|
+
await request("DELETE", `/api/orgs/${encodeURIComponent(orgId)}/members/${encodeURIComponent(userId)}`);
|
|
32820
32840
|
},
|
|
32821
32841
|
// Both renameOrganization and updateOrganization PATCH the same endpoint.
|
|
32822
32842
|
// Server-side routing determines which service runs based on which fields are present.
|
|
@@ -32846,7 +32866,7 @@ function createHttpClient(config2) {
|
|
|
32846
32866
|
try {
|
|
32847
32867
|
const result = await request(
|
|
32848
32868
|
"POST",
|
|
32849
|
-
`/api/organizations/${encodeURIComponent(sourceOrgId)}/transfer-
|
|
32869
|
+
`/api/organizations/${encodeURIComponent(sourceOrgId)}/transfer-system`,
|
|
32850
32870
|
{ systemId, targetOrgId }
|
|
32851
32871
|
);
|
|
32852
32872
|
const system = result?.system ? {
|
|
@@ -32903,7 +32923,11 @@ function createHttpClient(config2) {
|
|
|
32903
32923
|
const result = await request("GET", `/api/systems/${encodeURIComponent(systemId)}/facts`);
|
|
32904
32924
|
return result?.data;
|
|
32905
32925
|
},
|
|
32906
|
-
saveSystemFact: (input) =>
|
|
32926
|
+
saveSystemFact: (input) => {
|
|
32927
|
+
const effectiveId = input.systemId ?? input.projectId;
|
|
32928
|
+
if (!effectiveId) throw new Error("saveSystemFact: systemId (or legacy projectId) is required");
|
|
32929
|
+
return request("PUT", `/api/systems/${encodeURIComponent(String(effectiveId))}/facts`, input);
|
|
32930
|
+
},
|
|
32907
32931
|
transitionSystemLifecycleState: async (systemId, targetState, options) => {
|
|
32908
32932
|
return request("POST", `/api/systems/${encodeURIComponent(systemId)}/lifecycle/transitions`, {
|
|
32909
32933
|
targetState,
|
|
@@ -33136,12 +33160,15 @@ function createHttpClient(config2) {
|
|
|
33136
33160
|
return result?.note ?? result;
|
|
33137
33161
|
},
|
|
33138
33162
|
createNote: async (input) => {
|
|
33139
|
-
const
|
|
33163
|
+
const inputRec = input;
|
|
33164
|
+
const effectiveSystemId = inputRec.systemId ?? input.projectId;
|
|
33165
|
+
if (!effectiveSystemId) throw new Error("createNote: systemId (or legacy projectId) is required");
|
|
33166
|
+
const result = await request("POST", `/api/systems/${encodeURIComponent(effectiveSystemId)}/notes`, input);
|
|
33140
33167
|
return result?.note ?? result;
|
|
33141
33168
|
},
|
|
33142
33169
|
updateNote: async (noteId, input) => {
|
|
33143
33170
|
const existing = await request("GET", `/api/notes/${encodeURIComponent(noteId)}`);
|
|
33144
|
-
const systemId = existing?.
|
|
33171
|
+
const systemId = existing?.systemId ?? existing?.note?.systemId;
|
|
33145
33172
|
if (systemId) {
|
|
33146
33173
|
const result2 = await request("PATCH", `/api/systems/${encodeURIComponent(systemId)}/notes/${encodeURIComponent(noteId)}`, input);
|
|
33147
33174
|
return result2?.note ?? result2;
|
|
@@ -33182,20 +33209,20 @@ function createHttpClient(config2) {
|
|
|
33182
33209
|
},
|
|
33183
33210
|
reassignNote: async (noteId, targetBeatId, reason, _actorId, targetRevisionId) => {
|
|
33184
33211
|
const note = await request("GET", `/api/notes/${encodeURIComponent(noteId)}`);
|
|
33185
|
-
if (!note?.note) return void 0;
|
|
33212
|
+
if (!note?.note?.systemId) return void 0;
|
|
33186
33213
|
const result = await request(
|
|
33187
33214
|
"POST",
|
|
33188
|
-
`/api/systems/${encodeURIComponent(note.note.
|
|
33215
|
+
`/api/systems/${encodeURIComponent(note.note.systemId)}/notes/${encodeURIComponent(noteId)}/reassign`,
|
|
33189
33216
|
{ targetBeatId, targetRevisionId, reason }
|
|
33190
33217
|
);
|
|
33191
33218
|
return result?.note ?? result;
|
|
33192
33219
|
},
|
|
33193
33220
|
removeNote: async (noteId, reason, _actorId) => {
|
|
33194
33221
|
const note = await request("GET", `/api/notes/${encodeURIComponent(noteId)}`);
|
|
33195
|
-
if (!note?.note) return void 0;
|
|
33222
|
+
if (!note?.note?.systemId) return void 0;
|
|
33196
33223
|
const result = await request(
|
|
33197
33224
|
"POST",
|
|
33198
|
-
`/api/systems/${encodeURIComponent(note.note.
|
|
33225
|
+
`/api/systems/${encodeURIComponent(note.note.systemId)}/notes/${encodeURIComponent(noteId)}/remove`,
|
|
33199
33226
|
{ reason }
|
|
33200
33227
|
);
|
|
33201
33228
|
return result?.note ?? result;
|
|
@@ -34145,7 +34172,9 @@ function createHttpClient(config2) {
|
|
|
34145
34172
|
...parentGroupId !== void 0 && { parentGroupId },
|
|
34146
34173
|
...createdBy !== void 0 && { createdBy }
|
|
34147
34174
|
};
|
|
34148
|
-
const
|
|
34175
|
+
const inputRec = input;
|
|
34176
|
+
const effectiveSystemId = inputRec.systemId ?? input.projectId;
|
|
34177
|
+
const url2 = effectiveSystemId ? `/api/systems/${encodeURIComponent(effectiveSystemId)}/deliverable-groups` : `/api/teamspaces/${encodeURIComponent(input.teamspaceId)}/deliverable-groups`;
|
|
34149
34178
|
const result = await request("POST", url2, body);
|
|
34150
34179
|
if (result === void 0) throw new Error(`Resource not found when creating DeliverableGroup`);
|
|
34151
34180
|
return result;
|
|
@@ -34965,7 +34994,7 @@ function loadConfig() {
|
|
|
34965
34994
|
};
|
|
34966
34995
|
}
|
|
34967
34996
|
async function main() {
|
|
34968
|
-
console.error(`[harmonica-mcp] v${"4.0.
|
|
34997
|
+
console.error(`[harmonica-mcp] v${"4.0.2"} starting\u2026`);
|
|
34969
34998
|
const config2 = loadConfig();
|
|
34970
34999
|
const client = createHttpClient({
|
|
34971
35000
|
apiBaseUrl: config2.apiBaseUrl,
|