🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@kepler-project/almanac

Package Overview
Dependencies
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@kepler-project/almanac - npm Package Compare versions

Comparing version
0.2.11
to
0.3.0
+74
-182
bin/kepler-almanac.mjs

@@ -30,8 +30,2 @@ #!/usr/bin/env node

function authBaseUrlFromGrpcEndpoint(grpcEndpoint) {
const [host] = normalizeGrpcEndpoint(grpcEndpoint).split(":");
const authHost = host.startsWith("grpc.") ? host.replace(/^grpc\./, "api.") : host;
return `https://${authHost}`;
}
function loadGrpcPackages() {

@@ -135,50 +129,29 @@ const packageDefinition = protoLoader.loadSync(

function shouldFallbackToRest(error) {
return String(error?.message || error || "").includes("gRPC ");
}
function isNotFoundError(error) {
const message = String(error?.message || error || "").toLowerCase();
return (
message.includes("not_found") ||
message.includes("not found") ||
message.includes("(404)")
);
}
async function fetchJson(pathname, { method = "GET", query = {}, body = undefined, auth = true, _retried = false } = {}) {
const apiBase = authBaseUrlFromGrpcEndpoint(GRPC_ENDPOINT);
const url = new URL(pathname, apiBase.endsWith("/") ? apiBase : `${apiBase}/`);
Object.entries(query).forEach(([k, v]) => {
if (v !== undefined && v !== null && v !== "") url.searchParams.set(k, String(v));
});
const headers = { "Content-Type": "application/json" };
if (auth) headers.Authorization = `Bearer ${await getAccessToken()}`;
async function fetchAuthToken() {
// AUTH QUARANTINE: This is the only REST call. Service-token has no gRPC equivalent.
const [grpcHost] = normalizeGrpcEndpoint(GRPC_ENDPOINT).split(":");
const authHost = grpcHost.startsWith("grpc.") ? grpcHost.replace(/^grpc\./, "api.") : grpcHost;
const url = new URL("/v1/auth/service-token", `https://${authHost}`);
const resp = await fetch(url, {
method,
headers,
body: body === undefined ? undefined : JSON.stringify(body),
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ client_id: CLIENT_ID, client_secret: CLIENT_SECRET }),
});
if (resp.status === 401 && auth && !_retried) {
tokenCache = null;
return fetchJson(pathname, { method, query, body, auth, _retried: true });
const txt = await resp.text();
let parsed = {};
if (txt) {
try {
parsed = JSON.parse(txt);
} catch {
throw new Error(`POST ${url.pathname} failed (${resp.status}): ${txt}`);
}
}
const txt = await resp.text();
const parsed = txt ? safeJson(txt) : {};
if (!resp.ok) throw new Error(`${method} ${url.pathname} failed (${resp.status}): ${txt}`);
if (!resp.ok) throw new Error(`POST ${url.pathname} failed (${resp.status}): ${txt}`);
return parsed;
}
function safeJson(s) {
try { return JSON.parse(s); } catch { return { raw: s }; }
}
async function getAccessToken() {
const now = Date.now();
if (tokenCache && now < tokenExpiresAt - 60_000) return tokenCache;
const out = await fetchJson("/v1/auth/service-token", {
method: "POST",
auth: false,
body: { client_id: CLIENT_ID, client_secret: CLIENT_SECRET },
});
const out = await fetchAuthToken();
tokenCache = out.access_token;

@@ -189,3 +162,3 @@ tokenExpiresAt = now + ((out.expires_in || 3600) * 1000);

const server = new McpServer({ name: "kepler-almanac", version: "0.2.8" });
const server = new McpServer({ name: "kepler-almanac", version: "0.3.0" });

@@ -210,28 +183,19 @@ function jsonResult(data) {

}, async ({ search = "", limit = 50 }) => {
const clients = await getGrpcClients();
try {
const clients = await getGrpcClients();
const response = await grpcUnary(
clients.communications,
"listAllCustomers",
{ limit: search ? Math.max(limit * 20, 500) : limit, offset: 0 },
).catch((e) => { throw grpcError(e); });
const customers = search
? (response.customers || [])
.filter((c) => (c.name || "").toLowerCase().includes(search.toLowerCase()))
.slice(0, limit)
: (response.customers || []);
if (customers.length > 0 || !search) {
return jsonResult(search
? { customers, count: customers.length, partialName: search }
: response);
if (search) {
const response = await grpcUnary(clients.communications, "searchCustomers", {
partialName: search,
limit,
});
return jsonResult({
customers: response.customers || [],
count: response.count ?? (response.customers || []).length,
partialName: search,
});
}
} catch {
// Fall through to REST fallback for customer listing/search.
return jsonResult(await grpcUnary(clients.communications, "listAllCustomers", { limit, offset: 0 }));
} catch (error) {
throw grpcError(error);
}
const path = search ? "/v1/communications/customers/search" : "/v1/communications/customers/all";
const query = search ? { partial_name: search, limit } : { limit };
return jsonResult(await fetchJson(path, { query }));
});

@@ -264,3 +228,3 @@

include: [],
}).catch((e) => { throw grpcError(e); });
});
return jsonResult({

@@ -271,14 +235,3 @@ results: response.results,

} catch (error) {
if (!shouldFallbackToRest(error)) throw error;
return jsonResult(await fetchJson("/v1/communications/search", {
query: {
account_id: a.account_id,
customer_name: a.participant_name,
customer_names: a.customer_names?.join(","),
type: a.type,
sent_after: a.start_date,
sent_before: a.end_date,
limit: a.limit ?? 50,
},
}));
throw grpcError(error);
}

@@ -295,13 +248,8 @@ });

const clients = await getGrpcClients();
return jsonResult(await grpcUnary(clients.briefs, "getBrief", { briefId, enrich: false }).catch((e) => { throw grpcError(e); }));
return jsonResult(await grpcUnary(clients.briefs, "getBrief", { briefId, enrich: false }));
} catch (error) {
if (!shouldFallbackToRest(error) && !isNotFoundError(error)) throw error;
try {
return jsonResult(await fetchJson(`/v1/briefs/${encodeURIComponent(briefId)}`));
} catch (inner) {
if (isNotFoundError(inner)) {
return jsonResult({ status: "not_found", brief_id: briefId });
}
throw inner;
if (error?.code === grpc.status.NOT_FOUND) {
return jsonResult({ status: "not_found", brief_id: briefId });
}
throw grpcError(error);
}

@@ -311,17 +259,14 @@ });

server.tool("almanac_get_communication_detail", { id: z.string() }, async ({ id }) => {
const clients = await getGrpcClients();
try {
const clients = await getGrpcClients();
return jsonResult(await grpcUnary(clients.communications, "getCall", { callId: id }).catch((e) => { throw grpcError(e); }));
return jsonResult(await grpcUnary(clients.communications, "getCall", { callId: id }));
} catch (error) {
if (!String(error.message).includes("NOT_FOUND") && !shouldFallbackToRest(error)) throw error;
if (error?.code !== grpc.status.NOT_FOUND) throw grpcError(error);
try {
const clients = await getGrpcClients();
return jsonResult(await grpcUnary(clients.communications, "getEmail", { emailId: id }).catch((e) => { throw grpcError(e); }));
return jsonResult(await grpcUnary(clients.communications, "getEmail", { emailId: id }));
} catch (inner) {
if (!shouldFallbackToRest(inner)) throw inner;
try {
return jsonResult(await fetchJson(`/v1/communications/calls/${encodeURIComponent(id)}`));
} catch {
return jsonResult(await fetchJson(`/v1/communications/emails/${encodeURIComponent(id)}`));
if (inner?.code === grpc.status.NOT_FOUND) {
return jsonResult({ status: "not_found", id });
}
throw grpcError(inner);
}

@@ -339,6 +284,5 @@ }

const clients = await getGrpcClients();
return jsonResult(await grpcUnary(clients.knowledge, "getRawContent", { communicationId: cid }).catch((e) => { throw grpcError(e); }));
return jsonResult(await grpcUnary(clients.knowledge, "getRawContent", { communicationId: cid }));
} catch (error) {
if (!shouldFallbackToRest(error)) throw error;
return jsonResult(await fetchJson(`/v1/communications/${encodeURIComponent(cid)}/raw-content`));
throw grpcError(error);
}

@@ -369,17 +313,5 @@ });

enrich: false,
}).catch((e) => { throw grpcError(e); }));
}));
} catch (error) {
if (!shouldFallbackToRest(error)) throw error;
return jsonResult(await fetchJson("/v1/communications/semantic-search", {
query: {
q: a.query,
account_ids: a.account_ids?.join(","),
customer_names: a.customer_names?.join(","),
type: a.type,
sent_after: a.sent_after,
sent_before: a.sent_before,
min_similarity: a.min_similarity,
limit: a.limit ?? 20,
},
}));
throw grpcError(error);
}

@@ -399,6 +331,5 @@ });

limit,
}).catch((e) => { throw grpcError(e); }));
}));
} catch (error) {
if (!shouldFallbackToRest(error)) throw error;
return jsonResult(await fetchJson("/v1/gaps/semantic-search", { query: { q: query, min_similarity, limit } }));
throw grpcError(error);
}

@@ -410,6 +341,5 @@ });

const clients = await getGrpcClients();
return jsonResult(await grpcUnary(clients.knowledge, "getAccountGaps", { accountId: account_id, enrich: false }).catch((e) => { throw grpcError(e); }));
return jsonResult(await grpcUnary(clients.knowledge, "getAccountGaps", { accountId: account_id, enrich: false }));
} catch (error) {
if (!shouldFallbackToRest(error)) throw error;
return jsonResult(await fetchJson(`/v1/gaps/accounts/${encodeURIComponent(account_id)}`));
throw grpcError(error);
}

@@ -421,6 +351,5 @@ });

const clients = await getGrpcClients();
return jsonResult(await grpcUnary(clients.knowledge, "getOpportunityGaps", { opportunityId: opportunity_id }).catch((e) => { throw grpcError(e); }));
return jsonResult(await grpcUnary(clients.knowledge, "getOpportunityGaps", { opportunityId: opportunity_id }));
} catch (error) {
if (!shouldFallbackToRest(error)) throw error;
return jsonResult(await fetchJson(`/v1/gaps/opportunities/${encodeURIComponent(opportunity_id)}`));
throw grpcError(error);
}

@@ -442,13 +371,5 @@ });

limit: a.limit ?? 25,
}).catch((e) => { throw grpcError(e); }));
}));
} catch (error) {
if (!shouldFallbackToRest(error)) throw error;
return jsonResult(await fetchJson("/v1/gaps/search", {
query: {
q: a.query,
product_area: a.product_area,
min_account_count: a.min_account_count ?? 0,
limit: a.limit ?? 25,
},
}));
throw grpcError(error);
}

@@ -460,6 +381,5 @@ });

const clients = await getGrpcClients();
return jsonResult(await grpcUnary(clients.salesforce, "getAccount", { accountId: account_id, enrich: false }).catch((e) => { throw grpcError(e); }));
return jsonResult(await grpcUnary(clients.salesforce, "getAccount", { accountId: account_id, enrich: false }));
} catch (error) {
if (!shouldFallbackToRest(error)) throw error;
return jsonResult(await fetchJson(`/v1/salesforce/accounts/${encodeURIComponent(account_id)}`));
throw grpcError(error);
}

@@ -485,14 +405,5 @@ });

cursor: "",
}).catch((e) => { throw grpcError(e); }));
}));
} catch (error) {
if (!shouldFallbackToRest(error)) throw error;
return jsonResult(await fetchJson("/v1/team/communications", {
query: {
manager_email: a.manager_email,
start_at: a.start_at,
end_at: a.end_at,
type: a.type,
limit: a.limit ?? 500,
},
}));
throw grpcError(error);
}

@@ -514,8 +425,5 @@ });

limit,
}).catch((e) => { throw grpcError(e); }));
}));
} catch (error) {
if (!shouldFallbackToRest(error)) throw error;
return jsonResult(await fetchJson("/v1/knowledge/confluence/search", {
query: { q: query, space_key, min_similarity, limit },
}));
throw grpcError(error);
}

@@ -533,16 +441,8 @@ });

maxChars: max_chars ?? 0,
}).catch((e) => { throw grpcError(e); }));
}));
} catch (error) {
if (!shouldFallbackToRest(error) && !isNotFoundError(error)) throw error;
try {
return jsonResult(await fetchJson(`/v1/knowledge/confluence/pages/${encodeURIComponent(page_id)}`, { query: { max_chars } }));
} catch (inner) {
if (isNotFoundError(inner)) {
return jsonResult({ status: "not_found", page_id });
}
if (String(inner?.message || inner || "").includes("/v1/knowledge/confluence/pages/")) {
return jsonResult({ status: "unavailable", page_id, reason: "backend_lookup_failed" });
}
throw inner;
if (error?.code === grpc.status.NOT_FOUND) {
return jsonResult({ status: "not_found", page_id });
}
throw grpcError(error);
}

@@ -563,8 +463,5 @@ });

limit,
}).catch((e) => { throw grpcError(e); }));
}));
} catch (error) {
if (!shouldFallbackToRest(error)) throw error;
return jsonResult(await fetchJson("/v1/knowledge/jira/search", {
query: { q: query, project_key, limit },
}));
throw grpcError(error);
}

@@ -579,13 +476,8 @@ });

enrich: false,
}).catch((e) => { throw grpcError(e); }));
}));
} catch (error) {
if (!shouldFallbackToRest(error) && !isNotFoundError(error)) throw error;
try {
return jsonResult(await fetchJson(`/v1/knowledge/jira/issues/${encodeURIComponent(issue_key)}`));
} catch (inner) {
if (isNotFoundError(inner)) {
return jsonResult({ status: "not_found", issue_key });
}
throw inner;
if (error?.code === grpc.status.NOT_FOUND) {
return jsonResult({ status: "not_found", issue_key });
}
throw grpcError(error);
}

@@ -592,0 +484,0 @@ });

{
"name": "@kepler-project/almanac",
"version": "0.2.11",
"version": "0.3.0",
"description": "Kepler Almanac MCP server.",

@@ -5,0 +5,0 @@ "license": "UNLICENSED",

@@ -25,2 +25,4 @@ # Kepler Almanac MCP Server

Tool execution in this package is gRPC-only. The only HTTP request is the auth token mint to `/v1/auth/service-token`, which has no gRPC equivalent.
## Requirements

@@ -27,0 +29,0 @@