Convert confluence and jira into nox catalog modules
Port the HAL confluence and jira integrations to the tools-only, outbound-only external-SaaS pattern proven by the merged gitlab module: runtime-only containers (container-runtime capability), own-secret token + broker, a settings-managed config.json for public config, and a per-module runtime image. Each module carries its own Atlassian API client and tools (ADR 0039), translated from HAL's @hal/sdk zod-schema/MCP-content shape into mesh-sdk's input/run-returns-data shape. Public config (ATLASSIAN_URL, ATLASSIAN_EMAIL) lives in config.json; the API token is the one own-secret. Clients are built lazily and never throw at registration, so each runtime serves its full tool surface with no credentials (the Servarr lesson) — confluence serves 3 tools, jira serves 8. jira's periodic ticket-poller (update-tickets.service/.timer) is NOT ported: the mesh has no scheduled-task primitive yet (a pending decision). Only jira's tools are ported; a top-of-file note records the deferral. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
// confluence's tools — confluence's own code (novox/hq ADR 0039), importing confluence's own
|
||||
// Confluence API client. They return structured data; the mesh serves them through the sdk's tool
|
||||
// harness. confluence is tools-only and outbound-only: no service, no events, no listener — it
|
||||
// reaches out to an Atlassian Confluence instance and exposes its content search, pages and spaces.
|
||||
//
|
||||
// Every tool is registered unconditionally, even with no credentials configured (the Servarr
|
||||
// lesson): the client is built lazily and never throws, so the runtime always comes up and serves
|
||||
// the full tool surface — a call made before the URL/email/token are set fails with a clear error,
|
||||
// but the runtime does not refuse to serve. The install proof is the runtime logging
|
||||
// `[mesh-tools] serving N tool(s)`.
|
||||
|
||||
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
||||
import { ConfluenceClient } from "../client.js";
|
||||
|
||||
function str(value: unknown): string {
|
||||
return String(value ?? "");
|
||||
}
|
||||
|
||||
function num(value: unknown, fallback: number): number {
|
||||
const n = Number(value);
|
||||
return Number.isFinite(n) ? n : fallback;
|
||||
}
|
||||
|
||||
export function getConfluenceTools(confluence: ConfluenceClient): ToolDefinition[] {
|
||||
return [
|
||||
{
|
||||
name: "confluence_search",
|
||||
description: "Search Confluence content using CQL.",
|
||||
input: {
|
||||
cql: { type: "string", description: "CQL query string" },
|
||||
limit: { type: "number", description: "max results (default 25)" },
|
||||
},
|
||||
run: async (args) => ({ results: await confluence.search(str(args.cql), num(args.limit, 25)) }),
|
||||
},
|
||||
{
|
||||
name: "confluence_get_page",
|
||||
description: "Get a Confluence page by ID.",
|
||||
input: {
|
||||
page_id: { type: "string", description: "Page ID" },
|
||||
},
|
||||
run: async (args) => ({ page: await confluence.getPage(str(args.page_id)) }),
|
||||
},
|
||||
{
|
||||
name: "confluence_list_spaces",
|
||||
description: "List Confluence spaces.",
|
||||
input: {
|
||||
page: { type: "number", description: "page number, 1-based (default 1)" },
|
||||
limit: { type: "number", description: "results per page (default 25)" },
|
||||
},
|
||||
run: async (args) => {
|
||||
const page = num(args.page, 1);
|
||||
const limit = num(args.limit, 25);
|
||||
const cursor = String((page - 1) * limit);
|
||||
return { spaces: await confluence.listSpaces({ cursor, limit: String(limit) }) };
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// confluence always registers its full tool surface: the client is built lazily and never throws, so
|
||||
// the runtime comes up and serves every tool even before the URL/email/token are configured (the lab
|
||||
// has no real Atlassian). A tool called before the module is configured fails with a clear error from
|
||||
// that call.
|
||||
registerModuleTools("confluence", (env) => {
|
||||
try {
|
||||
return getConfluenceTools(ConfluenceClient.fromEnv(env));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user