@codazen/harmonica-mcp 3.0.1 → 3.2.0
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 +231 -13
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22444,7 +22444,8 @@ var CHECK_MAX_SCORES = {
|
|
|
22444
22444
|
pii_scan: 100,
|
|
22445
22445
|
portfolio_coherence: 5,
|
|
22446
22446
|
revision_quality: 5,
|
|
22447
|
-
drop_quality: 5
|
|
22447
|
+
drop_quality: 5,
|
|
22448
|
+
root_coda_quality: 5
|
|
22448
22449
|
};
|
|
22449
22450
|
|
|
22450
22451
|
// ../../libs/harmonica-services/src/mcp/tools/beat-quality-tools.ts
|
|
@@ -22648,6 +22649,17 @@ var NOTE_TYPE_LABELS = {
|
|
|
22648
22649
|
decision: "Decisions",
|
|
22649
22650
|
document: "Documents"
|
|
22650
22651
|
};
|
|
22652
|
+
function formatDeliveryPolicy(policy, unresolvedReason) {
|
|
22653
|
+
const LABELS = {
|
|
22654
|
+
openPullRequest: "Open a pull request",
|
|
22655
|
+
requestAgentReview: "Request the agent code review",
|
|
22656
|
+
watchPullRequest: "Watch the pull request to green"
|
|
22657
|
+
};
|
|
22658
|
+
const verdict = (value) => value === true ? "yes \u2014 proceed without asking" : value === false ? "NO \u2014 prohibited, say so rather than asking" : "not set \u2014 ask first";
|
|
22659
|
+
const lines = Object.keys(LABELS).map((step) => `- ${LABELS[step]}: ${verdict(policy?.[step] ?? void 0)}`);
|
|
22660
|
+
const footer = unresolvedReason ? [`- _Policy could not be resolved (${unresolvedReason}) \u2014 ask first; this is not proof that no policy is set._`] : [];
|
|
22661
|
+
return ["## Agent Delivery Policy", ...lines, ...footer].join("\n");
|
|
22662
|
+
}
|
|
22651
22663
|
function formatProjectContext(project, notes, orgCoda) {
|
|
22652
22664
|
const sections = [];
|
|
22653
22665
|
sections.push(`# Project: ${project.title}`);
|
|
@@ -22657,6 +22669,8 @@ function formatProjectContext(project, notes, orgCoda) {
|
|
|
22657
22669
|
const branch = project.repoDefaultBranch ?? "main";
|
|
22658
22670
|
sections.push(`**Repo:** ${project.repoOwner}/${project.repoName} (branch: ${branch})`);
|
|
22659
22671
|
}
|
|
22672
|
+
sections.push(`
|
|
22673
|
+
${formatDeliveryPolicy(project.agentDeliveryPolicy)}`);
|
|
22660
22674
|
if (orgCoda) {
|
|
22661
22675
|
sections.push(`
|
|
22662
22676
|
## Organization Coda
|
|
@@ -23255,7 +23269,14 @@ ${formatBeatDetail(beat)}${similarityWarning}` }] };
|
|
|
23255
23269
|
return { content: [{ type: "text", text: `Revision not found: ${revisionId}` }], isError: true };
|
|
23256
23270
|
}
|
|
23257
23271
|
await assertBeatInOrg(client, revision.beatId, ctx.orgId);
|
|
23258
|
-
|
|
23272
|
+
const resolution = await client.resolveDeliveryPolicy(revisionId);
|
|
23273
|
+
const policyBlock = `
|
|
23274
|
+
|
|
23275
|
+
${formatDeliveryPolicy(
|
|
23276
|
+
resolution.resolved ? resolution.policy : void 0,
|
|
23277
|
+
resolution.resolved ? void 0 : resolution.reason
|
|
23278
|
+
)}`;
|
|
23279
|
+
return { content: [{ type: "text", text: formatRevisionDetail(revision) + policyBlock }] };
|
|
23259
23280
|
} catch (err) {
|
|
23260
23281
|
const message = err instanceof Error ? err.message : String(err);
|
|
23261
23282
|
return { content: [{ type: "text", text: `Failed to get revision: ${message}` }], isError: true };
|
|
@@ -26200,6 +26221,7 @@ var ScopeNotebookApiSchema = external_exports.object({
|
|
|
26200
26221
|
var NotebookResponseSchema = external_exports.object({ notebook: NotebookApiSchema }).passthrough();
|
|
26201
26222
|
var NotebookScopesResponseSchema = external_exports.object({ scopes: external_exports.array(NotebookScopeRefSchema) }).passthrough();
|
|
26202
26223
|
var ScopeNotebooksResponseSchema = external_exports.object({ notebooks: external_exports.array(ScopeNotebookApiSchema) }).passthrough();
|
|
26224
|
+
var NotebookSharingResponseSchema = external_exports.object({ guestView: external_exports.boolean() }).passthrough();
|
|
26203
26225
|
var NotebookShortcutApiSchema = external_exports.object({
|
|
26204
26226
|
notebookId: external_exports.string(),
|
|
26205
26227
|
scopeType: NotebookScopeTypeSchema,
|
|
@@ -26325,7 +26347,8 @@ var CHECK_TARGET_TYPES = [
|
|
|
26325
26347
|
"revision",
|
|
26326
26348
|
"beat_version",
|
|
26327
26349
|
"project",
|
|
26328
|
-
"drop"
|
|
26350
|
+
"drop",
|
|
26351
|
+
"organization"
|
|
26329
26352
|
];
|
|
26330
26353
|
var CheckTargetTypeSchema = external_exports.enum(CHECK_TARGET_TYPES);
|
|
26331
26354
|
var CheckApiSchema = external_exports.object({
|
|
@@ -26577,7 +26600,7 @@ var StoredMessageSchema = external_exports.object({
|
|
|
26577
26600
|
var ConversationsResponseSchema = external_exports.array(ConversationSummarySchema);
|
|
26578
26601
|
var MessagesResponseSchema = external_exports.array(StoredMessageSchema);
|
|
26579
26602
|
var StartSessionRequestBodySchema = external_exports.object({
|
|
26580
|
-
|
|
26603
|
+
systemId: external_exports.string().min(1),
|
|
26581
26604
|
message: external_exports.string().min(1),
|
|
26582
26605
|
revisionId: external_exports.string().min(1).optional(),
|
|
26583
26606
|
enableWebSearch: external_exports.boolean().optional()
|
|
@@ -26862,7 +26885,7 @@ var EligibleBeatVersionForDropSchema = external_exports.object({
|
|
|
26862
26885
|
createdAt: external_exports.string(),
|
|
26863
26886
|
updatedAt: external_exports.string(),
|
|
26864
26887
|
humanAssignee: external_exports.object({ email: external_exports.string(), name: external_exports.string() }).optional(),
|
|
26865
|
-
currentDropAssignment: external_exports.object({ dropId: external_exports.string(), dropCode: external_exports.string(), state: external_exports.string() }).optional()
|
|
26888
|
+
currentDropAssignment: external_exports.object({ dropId: external_exports.string(), dropCode: external_exports.string().optional(), state: external_exports.string() }).optional()
|
|
26866
26889
|
}).passthrough();
|
|
26867
26890
|
var ScoredDropCandidateSchema = external_exports.object({
|
|
26868
26891
|
bv: EligibleBeatVersionForDropSchema,
|
|
@@ -26879,6 +26902,34 @@ var ScoredDropCandidateSchema = external_exports.object({
|
|
|
26879
26902
|
atRisk: external_exports.boolean(),
|
|
26880
26903
|
requiredSlope: external_exports.number().nullable().optional()
|
|
26881
26904
|
}).passthrough();
|
|
26905
|
+
var DUTY_ALTITUDES = ["portfolio", "system", "account"];
|
|
26906
|
+
var DUTY_HOLDER_TYPES = ["human", "agent"];
|
|
26907
|
+
var DUTY_MODES = ["accountable", "responsible"];
|
|
26908
|
+
var DutyAltitudeApiSchema = external_exports.enum(DUTY_ALTITUDES);
|
|
26909
|
+
var DutyHolderTypeApiSchema = external_exports.enum(DUTY_HOLDER_TYPES);
|
|
26910
|
+
var DutyModeApiSchema = external_exports.enum(DUTY_MODES);
|
|
26911
|
+
var DerivedStaffingStateApiSchema = external_exports.enum(["agent-held", "supported", "observed"]);
|
|
26912
|
+
var DutyAssignmentApiSchema = external_exports.object({
|
|
26913
|
+
dutyAssignmentId: external_exports.string(),
|
|
26914
|
+
accountId: external_exports.string(),
|
|
26915
|
+
orgId: external_exports.string(),
|
|
26916
|
+
role: external_exports.string(),
|
|
26917
|
+
altitude: DutyAltitudeApiSchema,
|
|
26918
|
+
scopeId: external_exports.string(),
|
|
26919
|
+
holderType: DutyHolderTypeApiSchema,
|
|
26920
|
+
holderId: external_exports.string(),
|
|
26921
|
+
supportingAssistantId: external_exports.string().optional(),
|
|
26922
|
+
mode: DutyModeApiSchema,
|
|
26923
|
+
parentDutyAssignmentId: external_exports.string().optional(),
|
|
26924
|
+
effectiveFrom: external_exports.string(),
|
|
26925
|
+
effectiveTo: external_exports.string().optional(),
|
|
26926
|
+
createdBy: external_exports.string().optional(),
|
|
26927
|
+
version: external_exports.number().optional(),
|
|
26928
|
+
createdAt: external_exports.string(),
|
|
26929
|
+
updatedAt: external_exports.string()
|
|
26930
|
+
}).passthrough();
|
|
26931
|
+
var DutyAssignmentsResponseSchema = external_exports.object({ dutyAssignments: external_exports.array(DutyAssignmentApiSchema).optional() }).passthrough();
|
|
26932
|
+
var DutyAssignmentResponseSchema = external_exports.object({ dutyAssignment: DutyAssignmentApiSchema }).passthrough();
|
|
26882
26933
|
var DropProposalApiSchema = external_exports.object({
|
|
26883
26934
|
accountId: external_exports.string(),
|
|
26884
26935
|
revisionIds: external_exports.array(external_exports.string()),
|
|
@@ -28459,6 +28510,59 @@ Notes filed: ${createdNotes.length}`);
|
|
|
28459
28510
|
}
|
|
28460
28511
|
|
|
28461
28512
|
// ../../libs/harmonica-services/src/mcp/tools/organization-tools.ts
|
|
28513
|
+
var ROOT_CODA_POLL_INTERVAL_MS = 4e3;
|
|
28514
|
+
var ROOT_CODA_POLL_TIMEOUT_MS = 18e4;
|
|
28515
|
+
var ROOT_CODA_POLL_MAX_CONSECUTIVE_ERRORS = 3;
|
|
28516
|
+
async function pollForRootCodaCheck(client, taskId) {
|
|
28517
|
+
const deadline = Date.now() + ROOT_CODA_POLL_TIMEOUT_MS;
|
|
28518
|
+
let consecutiveErrors = 0;
|
|
28519
|
+
while (Date.now() < deadline) {
|
|
28520
|
+
const [task, err] = await client.getTask(taskId).then(
|
|
28521
|
+
(t) => [t, null],
|
|
28522
|
+
(e) => [null, e]
|
|
28523
|
+
);
|
|
28524
|
+
if (err) {
|
|
28525
|
+
consecutiveErrors++;
|
|
28526
|
+
if (consecutiveErrors >= ROOT_CODA_POLL_MAX_CONSECUTIVE_ERRORS) {
|
|
28527
|
+
throw new Error(`getTask failed ${consecutiveErrors} times: ${err instanceof Error ? err.message : String(err)}`);
|
|
28528
|
+
}
|
|
28529
|
+
await new Promise((r) => setTimeout(r, ROOT_CODA_POLL_INTERVAL_MS));
|
|
28530
|
+
continue;
|
|
28531
|
+
}
|
|
28532
|
+
consecutiveErrors = 0;
|
|
28533
|
+
if (!task) throw new Error(`Task ${taskId} not found`);
|
|
28534
|
+
if (task.status === "completed") {
|
|
28535
|
+
if (!task.result) throw new Error(`Task ${taskId} completed with no result`);
|
|
28536
|
+
const result = task.result;
|
|
28537
|
+
if (!Array.isArray(result.dimensions) || typeof result.overallScore !== "number") {
|
|
28538
|
+
throw new Error(`Task ${taskId} did not return a root Coda scorecard \u2014 was it a run_check task?`);
|
|
28539
|
+
}
|
|
28540
|
+
return result;
|
|
28541
|
+
}
|
|
28542
|
+
if (task.status === "failed") throw new Error(`Root Coda quality check failed: ${task.error ?? "unknown error"}`);
|
|
28543
|
+
await new Promise((r) => setTimeout(r, ROOT_CODA_POLL_INTERVAL_MS));
|
|
28544
|
+
}
|
|
28545
|
+
throw new Error(`Root Coda quality check timed out after ${ROOT_CODA_POLL_TIMEOUT_MS / 1e3}s`);
|
|
28546
|
+
}
|
|
28547
|
+
function formatRootCodaReport(check2) {
|
|
28548
|
+
const bar = (score, max) => {
|
|
28549
|
+
const filled = Math.max(0, Math.min(max, Math.round(score)));
|
|
28550
|
+
return "\u2588".repeat(filled) + "\u2591".repeat(max - filled);
|
|
28551
|
+
};
|
|
28552
|
+
const dims = check2.dimensions.map(
|
|
28553
|
+
(d) => `**${d.label}** ${bar(d.score, d.maxScore)} ${d.score}/${d.maxScore}
|
|
28554
|
+
${d.rationale}${d.suggestions?.length ? `
|
|
28555
|
+
\u2022 ${d.suggestions.join("\n\u2022 ")}` : ""}`
|
|
28556
|
+
).join("\n\n");
|
|
28557
|
+
return [
|
|
28558
|
+
`## Root Coda Quality \u2014 ${check2.targetId}`,
|
|
28559
|
+
`**Overall Score:** ${check2.overallScore}/5`,
|
|
28560
|
+
`**Summary:** ${check2.summary}`,
|
|
28561
|
+
check2.topSuggestion ? `**Top Suggestion:** ${check2.topSuggestion}` : "",
|
|
28562
|
+
"",
|
|
28563
|
+
dims
|
|
28564
|
+
].filter(Boolean).join("\n");
|
|
28565
|
+
}
|
|
28462
28566
|
function registerOrganizationTools(server, ctx, client) {
|
|
28463
28567
|
server.tool(
|
|
28464
28568
|
"get_organization",
|
|
@@ -28588,6 +28692,67 @@ function registerOrganizationTools(server, ctx, client) {
|
|
|
28588
28692
|
return { content: [{ type: "text", text }] };
|
|
28589
28693
|
}
|
|
28590
28694
|
);
|
|
28695
|
+
server.tool(
|
|
28696
|
+
"compose_root_coda",
|
|
28697
|
+
"Generate and apply an Organization's root Coda (top-level purpose statement) from its name, industry, description, and existing Coda if any. Drafts via LLM, applies directly to the Organization, then runs a root_coda_quality check \u2014 if the first score is below 4.0, performs exactly one automatic self-revision pass incorporating the feedback before finishing. Returns a job ID \u2014 use get_job_status to track progress.",
|
|
28698
|
+
{
|
|
28699
|
+
orgId: external_exports.string().min(1).describe("The organization ID")
|
|
28700
|
+
},
|
|
28701
|
+
async ({ orgId }) => {
|
|
28702
|
+
try {
|
|
28703
|
+
const task = await client.composeRootCoda(orgId);
|
|
28704
|
+
return {
|
|
28705
|
+
content: [{
|
|
28706
|
+
type: "text",
|
|
28707
|
+
text: [
|
|
28708
|
+
"Root Coda composition queued.",
|
|
28709
|
+
"",
|
|
28710
|
+
`**Organization:** ${orgId}`,
|
|
28711
|
+
`**Task ID:** ${task.taskId}`,
|
|
28712
|
+
`**Status:** ${task.status}`,
|
|
28713
|
+
"",
|
|
28714
|
+
"The agent will gather context, draft a Coda, apply it to the Organization, and grade it \u2014 revising once automatically if the first score is below 4.0.",
|
|
28715
|
+
"Use `get_job_status` with the job ID to check progress."
|
|
28716
|
+
].join("\n")
|
|
28717
|
+
}]
|
|
28718
|
+
};
|
|
28719
|
+
} catch (err) {
|
|
28720
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
28721
|
+
return { content: [{ type: "text", text: `compose_root_coda failed: ${message}` }], isError: true };
|
|
28722
|
+
}
|
|
28723
|
+
}
|
|
28724
|
+
);
|
|
28725
|
+
server.tool(
|
|
28726
|
+
"check_root_coda_quality",
|
|
28727
|
+
"Run a six-dimension quality check on an Organization's root Coda (Discriminating, Attributed, Reciprocal, Bounded, Generative, Consequential). By default returns a taskId immediately \u2014 set wait=true to block and receive the full scorecard inline.",
|
|
28728
|
+
{
|
|
28729
|
+
orgId: external_exports.string().min(1).describe("The organization ID"),
|
|
28730
|
+
wait: external_exports.boolean().optional().describe("Block until the check completes and return the scorecard inline. Default: false.")
|
|
28731
|
+
},
|
|
28732
|
+
async ({ orgId, wait }) => {
|
|
28733
|
+
try {
|
|
28734
|
+
const { taskId } = await client.runCheck(orgId, "root_coda_quality", orgId);
|
|
28735
|
+
if (!wait) {
|
|
28736
|
+
return {
|
|
28737
|
+
content: [{
|
|
28738
|
+
type: "text",
|
|
28739
|
+
text: [
|
|
28740
|
+
`Root Coda quality check enqueued for ${orgId}.`,
|
|
28741
|
+
`Task ID: ${taskId}`,
|
|
28742
|
+
"",
|
|
28743
|
+
`Use get_job_status with jobId="${taskId}" to poll, or list_checks with checkType="root_coda_quality" to view results.`
|
|
28744
|
+
].join("\n")
|
|
28745
|
+
}]
|
|
28746
|
+
};
|
|
28747
|
+
}
|
|
28748
|
+
const check2 = await pollForRootCodaCheck(client, taskId);
|
|
28749
|
+
return { content: [{ type: "text", text: formatRootCodaReport(check2) }] };
|
|
28750
|
+
} catch (err) {
|
|
28751
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
28752
|
+
return { content: [{ type: "text", text: `Root Coda quality check failed: ${message}` }], isError: true };
|
|
28753
|
+
}
|
|
28754
|
+
}
|
|
28755
|
+
);
|
|
28591
28756
|
}
|
|
28592
28757
|
|
|
28593
28758
|
// ../../libs/harmonica-services/src/mcp/tools/plan-quality-tools.ts
|
|
@@ -30213,6 +30378,13 @@ function registerProjectLifecycleTools(server, ctx, client) {
|
|
|
30213
30378
|
|
|
30214
30379
|
// ../../libs/harmonica-services/src/mcp/tools/system-tools.ts
|
|
30215
30380
|
var import_crypto5 = require("crypto");
|
|
30381
|
+
|
|
30382
|
+
// ../../libs/harmonica-services/src/utils/normalize-create-account-id.ts
|
|
30383
|
+
function normalizeCreateAccountId(accountId) {
|
|
30384
|
+
return accountId?.trim() || void 0;
|
|
30385
|
+
}
|
|
30386
|
+
|
|
30387
|
+
// ../../libs/harmonica-services/src/mcp/tools/system-tools.ts
|
|
30216
30388
|
function accountLine(request, resolved) {
|
|
30217
30389
|
const requested = request["accountId"];
|
|
30218
30390
|
if (requested === null || requested === "") return "**Account:** (unlinked)";
|
|
@@ -30281,7 +30453,12 @@ function registerProjectTools(server, ctx, client) {
|
|
|
30281
30453
|
repoOwner: external_exports.string().optional().describe("GitHub repository owner (org or user)"),
|
|
30282
30454
|
repoName: external_exports.string().optional().describe("GitHub repository name"),
|
|
30283
30455
|
repoDefaultBranch: external_exports.string().optional().describe('Default branch for code check (defaults to "main")'),
|
|
30284
|
-
rateLimitOverrides: external_exports.record(external_exports.string(), external_exports.object({ maxPerHour: external_exports.number().min(0) })).optional().describe('Per-task-type rate limit overrides, e.g. {"agent_chat":{"maxPerHour":100}}. Overrides env var and compiled defaults.')
|
|
30456
|
+
rateLimitOverrides: external_exports.record(external_exports.string(), external_exports.object({ maxPerHour: external_exports.number().min(0) })).optional().describe('Per-task-type rate limit overrides, e.g. {"agent_chat":{"maxPerHour":100}}. Overrides env var and compiled defaults.'),
|
|
30457
|
+
agentDeliveryPolicy: external_exports.object({
|
|
30458
|
+
openPullRequest: external_exports.boolean().nullable().optional().describe("Open a pull request when the work is ready."),
|
|
30459
|
+
requestAgentReview: external_exports.boolean().nullable().optional().describe("Request the agent code review (the agent-review label)."),
|
|
30460
|
+
watchPullRequest: external_exports.boolean().nullable().optional().describe("Watch the pull request and drive it to green.")
|
|
30461
|
+
}).optional().describe("Delivery latitude for agents on this System: which pull-request steps they may take without asking. Each field is tri-state \u2014 omit it to leave it alone, true to let agents proceed, false to forbid the step outright, null to clear it back to asking.")
|
|
30285
30462
|
};
|
|
30286
30463
|
const updateSystemHandler = async ({ systemId, ...updates }) => {
|
|
30287
30464
|
try {
|
|
@@ -30366,11 +30543,12 @@ function registerProjectTools(server, ctx, client) {
|
|
|
30366
30543
|
description: external_exports.string().optional().describe("System description"),
|
|
30367
30544
|
strategy: external_exports.string().optional().describe('System strategy markdown \u2014 Org Strategy + System Strategy ("System Coda")'),
|
|
30368
30545
|
teamspaceId: external_exports.string().optional().describe("Teamspace ID to associate this system with"),
|
|
30546
|
+
accountId: external_exports.string().optional().describe("Account that owns this System \u2014 the client or internal org unit. Set it at creation so the System appears in the Account's Systems list; omit only for a System that belongs to no Account."),
|
|
30369
30547
|
repoOwner: external_exports.string().optional().describe("GitHub repository owner (org or user)"),
|
|
30370
30548
|
repoName: external_exports.string().optional().describe("GitHub repository name"),
|
|
30371
30549
|
repoDefaultBranch: external_exports.string().optional().describe('Default branch for code check (defaults to "main")')
|
|
30372
30550
|
};
|
|
30373
|
-
const createSystemHandler = async ({ title, description, strategy, teamspaceId, repoOwner, repoName, repoDefaultBranch }) => {
|
|
30551
|
+
const createSystemHandler = async ({ title, description, strategy, teamspaceId, accountId, repoOwner, repoName, repoDefaultBranch }) => {
|
|
30374
30552
|
if (teamspaceId) {
|
|
30375
30553
|
const teamspace = await client.getTeamspace(teamspaceId);
|
|
30376
30554
|
if (!teamspace) {
|
|
@@ -30380,6 +30558,7 @@ function registerProjectTools(server, ctx, client) {
|
|
|
30380
30558
|
return { content: [{ type: "text", text: "Teamspace does not belong to this organization" }], isError: true };
|
|
30381
30559
|
}
|
|
30382
30560
|
}
|
|
30561
|
+
const account = normalizeCreateAccountId(accountId);
|
|
30383
30562
|
try {
|
|
30384
30563
|
const project = await client.createSystem({
|
|
30385
30564
|
projectId: (0, import_crypto5.randomUUID)(),
|
|
@@ -30390,6 +30569,7 @@ function registerProjectTools(server, ctx, client) {
|
|
|
30390
30569
|
strategy,
|
|
30391
30570
|
status: "active",
|
|
30392
30571
|
teamspaceId,
|
|
30572
|
+
...account !== void 0 && { accountId: account },
|
|
30393
30573
|
repoOwner,
|
|
30394
30574
|
repoName,
|
|
30395
30575
|
repoDefaultBranch
|
|
@@ -30400,6 +30580,7 @@ function registerProjectTools(server, ctx, client) {
|
|
|
30400
30580
|
`**ID:** ${project.projectId}`,
|
|
30401
30581
|
`**Title:** ${project.title}`,
|
|
30402
30582
|
`**Status:** ${project.status}`,
|
|
30583
|
+
project.accountId ? `**Account:** ${project.accountId}` : "",
|
|
30403
30584
|
project.description ? `**Description:** ${project.description}` : "",
|
|
30404
30585
|
project.repoOwner ? `**Repo:** ${project.repoOwner}/${project.repoName}` : "",
|
|
30405
30586
|
project.repoDefaultBranch ? `**Default Branch:** ${project.repoDefaultBranch}` : ""
|
|
@@ -30410,7 +30591,7 @@ function registerProjectTools(server, ctx, client) {
|
|
|
30410
30591
|
return { content: [{ type: "text", text: `Failed to create system: ${message}` }], isError: true };
|
|
30411
30592
|
}
|
|
30412
30593
|
};
|
|
30413
|
-
server.tool("create_system", "Create a new system in the configured organization", createSystemSchema, createSystemHandler);
|
|
30594
|
+
server.tool("create_system", "Create a new system in the configured organization. Pass accountId whenever the system belongs to a client \u2014 a system created without it is not listed under any Account.", createSystemSchema, createSystemHandler);
|
|
30414
30595
|
}
|
|
30415
30596
|
|
|
30416
30597
|
// ../../libs/harmonica-services/src/mcp/tools/teamspace-tools.ts
|
|
@@ -31828,7 +32009,7 @@ function createHttpClient(config2) {
|
|
|
31828
32009
|
const result = await request(
|
|
31829
32010
|
"POST",
|
|
31830
32011
|
`/api/organizations/${encodeURIComponent(sourceOrgId)}/transfer-project`,
|
|
31831
|
-
{ projectId, targetOrgId }
|
|
32012
|
+
{ systemId: projectId, targetOrgId }
|
|
31832
32013
|
);
|
|
31833
32014
|
const project = result?.project ? {
|
|
31834
32015
|
...result.project,
|
|
@@ -31863,7 +32044,10 @@ function createHttpClient(config2) {
|
|
|
31863
32044
|
teamspaceId: input.teamspaceId,
|
|
31864
32045
|
name: input.title,
|
|
31865
32046
|
description: input.description,
|
|
31866
|
-
strategy: input.strategy
|
|
32047
|
+
strategy: input.strategy,
|
|
32048
|
+
// Without this the owning Account is dropped on the wire and the System
|
|
32049
|
+
// is created unlinked — invisible in the Account's Systems list.
|
|
32050
|
+
...input.accountId !== void 0 && { accountId: input.accountId }
|
|
31867
32051
|
});
|
|
31868
32052
|
return { ...raw, projectId: raw?.id ?? raw?.projectId, title: raw?.name ?? raw?.title, orgId: raw?.organizationId ?? raw?.orgId };
|
|
31869
32053
|
},
|
|
@@ -31898,7 +32082,10 @@ function createHttpClient(config2) {
|
|
|
31898
32082
|
teamspaceId: input.teamspaceId,
|
|
31899
32083
|
name: input.title,
|
|
31900
32084
|
description: input.description,
|
|
31901
|
-
strategy: input.strategy
|
|
32085
|
+
strategy: input.strategy,
|
|
32086
|
+
// Without this the owning Account is dropped on the wire and the System
|
|
32087
|
+
// is created unlinked — invisible in the Account's Systems list.
|
|
32088
|
+
...input.accountId !== void 0 && { accountId: input.accountId }
|
|
31902
32089
|
});
|
|
31903
32090
|
return { ...raw, orgId: raw?.organizationId ?? raw?.orgId };
|
|
31904
32091
|
},
|
|
@@ -32930,6 +33117,15 @@ function createHttpClient(config2) {
|
|
|
32930
33117
|
if (!result?.taskId) throw new Error("Draft coda enqueue failed: no taskId returned");
|
|
32931
33118
|
return { taskId: result.taskId, taskType: "draft_coda", status: result.status ?? "pending", projectId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
32932
33119
|
},
|
|
33120
|
+
composeRootCoda: async (orgId) => {
|
|
33121
|
+
const result = await request(
|
|
33122
|
+
"POST",
|
|
33123
|
+
`/api/organizations/${encodeURIComponent(orgId)}/compose-root-coda`,
|
|
33124
|
+
{}
|
|
33125
|
+
);
|
|
33126
|
+
if (!result?.taskId) throw new Error("Compose root Coda enqueue failed: no taskId returned");
|
|
33127
|
+
return { taskId: result.taskId, taskType: "compose_root_coda", status: result.status ?? "pending", projectId: orgId, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
33128
|
+
},
|
|
32933
33129
|
validateAssumptions: async (projectId, noteIds) => {
|
|
32934
33130
|
const result = await request(
|
|
32935
33131
|
"POST",
|
|
@@ -32997,7 +33193,17 @@ function createHttpClient(config2) {
|
|
|
32997
33193
|
return download;
|
|
32998
33194
|
},
|
|
32999
33195
|
importSystemSnapshot: (snapshot, options) => request("POST", "/api/systems/import", { ...snapshot, importOptions: options }, LONG_RUNNING_TIMEOUT_MS),
|
|
33000
|
-
importSystemSnapshotFromUrl: (url2, options) =>
|
|
33196
|
+
importSystemSnapshotFromUrl: async (url2, options) => {
|
|
33197
|
+
const enqueued = await request(
|
|
33198
|
+
"POST",
|
|
33199
|
+
"/api/systems/import",
|
|
33200
|
+
{ snapshotUrl: url2, importOptions: options }
|
|
33201
|
+
);
|
|
33202
|
+
if (!enqueued?.taskId) {
|
|
33203
|
+
throw new Error("importSystemSnapshotFromUrl: API did not return a taskId");
|
|
33204
|
+
}
|
|
33205
|
+
return pollTaskResult(enqueued.taskId);
|
|
33206
|
+
},
|
|
33001
33207
|
// Embedding similarity
|
|
33002
33208
|
embedProjectEntities: (projectId) => request("POST", `/api/systems/${encodeURIComponent(projectId)}/embeddings/generate`),
|
|
33003
33209
|
findSimilarNotes: async (noteId, projectId, options) => {
|
|
@@ -33393,6 +33599,18 @@ function createHttpClient(config2) {
|
|
|
33393
33599
|
// Systems owned directly by an Account (B-278 v17). A 404 here means either
|
|
33394
33600
|
// no such Account or the feature flag is off; both read as "nothing to show",
|
|
33395
33601
|
// which is why request() returning undefined maps to an empty list.
|
|
33602
|
+
// Delegates to the endpoint that calls the same service the direct adapter
|
|
33603
|
+
// uses, so neither adapter carries its own copy of the fail-closed rule.
|
|
33604
|
+
// request() maps a 404 to undefined; that is itself unresolvable, so it
|
|
33605
|
+
// becomes an explicit unresolved result rather than a thrown error.
|
|
33606
|
+
resolveDeliveryPolicy: async (revisionId) => {
|
|
33607
|
+
const res = await request(
|
|
33608
|
+
"GET",
|
|
33609
|
+
`/api/revisions/${encodeURIComponent(revisionId)}/delivery-policy`
|
|
33610
|
+
);
|
|
33611
|
+
if (!res) return { resolved: false, reason: "revision_not_found" };
|
|
33612
|
+
return res;
|
|
33613
|
+
},
|
|
33396
33614
|
listAccountSystems: async (accountId) => {
|
|
33397
33615
|
const res = await request("GET", `/api/accounts/${encodeURIComponent(accountId)}/systems`);
|
|
33398
33616
|
return res?.systems ?? [];
|
|
@@ -33828,7 +34046,7 @@ function loadConfig() {
|
|
|
33828
34046
|
};
|
|
33829
34047
|
}
|
|
33830
34048
|
async function main() {
|
|
33831
|
-
console.error(`[harmonica-mcp] v${"3.0
|
|
34049
|
+
console.error(`[harmonica-mcp] v${"3.2.0"} starting\u2026`);
|
|
33832
34050
|
const config2 = loadConfig();
|
|
33833
34051
|
const client = createHttpClient({
|
|
33834
34052
|
apiBaseUrl: config2.apiBaseUrl,
|