Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 109 additions & 9 deletions bin/knowledge-mcp.js
Original file line number Diff line number Diff line change
Expand Up @@ -18209,7 +18209,8 @@ function knowledgeAuthPath(env = process.env) {
return join5(root, "auth.json");
}
function resolveKnowledgeApiUrl(config2, env = process.env) {
return normalizeKnowledgeApiOrigin(env.KNOWLEDGE_API_URL ?? config2?.hosted?.api_url ?? DEFAULT_KNOWLEDGE_API_URL);
const envApiUrl = KNOWLEDGE_API_URL_ENV_KEYS.map((key) => env[key]?.trim()).find((value) => Boolean(value));
return normalizeKnowledgeApiOrigin(envApiUrl ?? config2?.hosted?.api_url ?? DEFAULT_KNOWLEDGE_API_URL);
}
function getKnowledgeAuth(env = process.env) {
try {
Expand Down Expand Up @@ -18243,17 +18244,17 @@ function clearKnowledgeAuth(env = process.env) {
}
}
function getKnowledgeApiKey(env = process.env) {
if (env.KNOWLEDGE_API_KEY)
return { apiKey: env.KNOWLEDGE_API_KEY, source: "env" };
if (env.HASNA_KNOWLEDGE_API_KEY)
return { apiKey: env.HASNA_KNOWLEDGE_API_KEY, source: "env" };
const envApiKey = KNOWLEDGE_API_KEY_ENV_KEYS.map((key) => env[key]).find((value) => Boolean(value));
if (envApiKey)
return { apiKey: envApiKey, source: "env" };
const auth = getKnowledgeAuth(env);
return auth?.api_key ? { apiKey: auth.api_key, source: "file" } : { apiKey: null, source: "none" };
}
function knowledgeAuthStatus(config2, env = process.env) {
const auth = getKnowledgeAuth(env);
const key = getKnowledgeApiKey(env);
const apiUrl = env.KNOWLEDGE_API_URL ? resolveKnowledgeApiUrl(config2, env) : auth?.api_url ? normalizeKnowledgeApiOrigin(auth.api_url) : resolveKnowledgeApiUrl(config2, env);
const hasEnvApiUrl = KNOWLEDGE_API_URL_ENV_KEYS.some((name) => Boolean(env[name]?.trim()));
const apiUrl = hasEnvApiUrl ? resolveKnowledgeApiUrl(config2, env) : auth?.api_url ? normalizeKnowledgeApiOrigin(auth.api_url) : resolveKnowledgeApiUrl(config2, env);
return {
authenticated: Boolean(key.apiKey),
source: key.source,
Expand Down Expand Up @@ -30907,6 +30908,96 @@ function createLocalKnowledgeProjectLinksAuthority(input) {
db.exec(sqliteKnowledgeProjectLinksSchemaSql());
return new PackageOwnedKnowledgeProjectLinksAuthority(new SqliteProjectLinksSql(db), (id) => input.itemStore.get(id), input.options);
}
var KNOWLEDGE_PROJECT_LINKS_ERROR_CODES = new Set([
"KNOWLEDGE_PROJECT_LINKS_INVALID_INPUT",
"KNOWLEDGE_PROJECT_LINKS_CAPABILITY_MISMATCH",
"KNOWLEDGE_PROJECT_LINKS_DIGEST_MISMATCH",
"KNOWLEDGE_PROJECT_LINKS_IDEMPOTENCY_MISMATCH",
"KNOWLEDGE_PROJECT_LINKS_CONFLICT",
"KNOWLEDGE_PROJECT_LINKS_NOT_FOUND",
"KNOWLEDGE_PROJECT_LINKS_CURSOR_STALE",
"KNOWLEDGE_PROJECT_LINKS_INCOMPLETE_POPULATION",
"KNOWLEDGE_PROJECT_LINKS_INVALID_RESPONSE"
]);
function isRecord(value) {
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
function isResourceKind(value) {
return value === "project" || value === "collection" || value === "item" || value === "taxonomy";
}
function invalidProjectLinksResponse(message, details = {}) {
throw new KnowledgeProjectLinksError("KNOWLEDGE_PROJECT_LINKS_INVALID_RESPONSE", message, details);
}
function assertProjectResource(value, context) {
if (!isRecord(value)) {
invalidProjectLinksResponse(`${context} is not an object.`);
}
const requiredStrings = [
"key",
"id",
"project_id",
"source_project_id",
"collection_id",
"revision",
"digest",
"title"
];
for (const field of requiredStrings) {
if (typeof value[field] !== "string") {
invalidProjectLinksResponse(`${context}.${field} is not a string.`);
}
}
if (!isResourceKind(value.kind)) {
invalidProjectLinksResponse(`${context}.kind is not a supported resource kind.`);
}
if (!isRecord(value.locator) || value.locator.kind !== "external_uuid" && value.locator.kind !== "canonical_uri" || typeof value.locator.value !== "string") {
invalidProjectLinksResponse(`${context}.locator is not a valid stable locator.`);
}
if (!isRecord(value.metadata)) {
invalidProjectLinksResponse(`${context}.metadata is not an object.`);
}
return value;
}
function assertProjectResourcePage(value) {
if (!isRecord(value)) {
invalidProjectLinksResponse("Knowledge project-resources response is not an object.");
}
if (value.schema !== "knowledge.project-resources.page.v1" || value.authority !== "knowledge" || value.route !== KNOWLEDGE_PROJECT_RESOURCES_ROUTE) {
invalidProjectLinksResponse("Knowledge project-resources response does not carry the expected producer contract.");
}
for (const field of [
"authority_id",
"tenant_id",
"corpus_id",
"project_id",
"source_project_id",
"collection_id",
"collection_revision",
"population_digest"
]) {
if (typeof value[field] !== "string") {
invalidProjectLinksResponse(`Knowledge project-resources response.${field} is not a string.`);
}
}
if (!Array.isArray(value.resource_kinds) || !value.resource_kinds.every(isResourceKind)) {
invalidProjectLinksResponse("Knowledge project-resources response.resource_kinds is invalid.");
}
if (!Array.isArray(value.resources)) {
invalidProjectLinksResponse("Knowledge project-resources response.resources is not an array.");
}
const resources = value.resources.map((resource, index) => assertProjectResource(resource, `Knowledge project-resources response.resources[${index}]`));
if (!Number.isInteger(value.count) || value.count !== resources.length || !Number.isInteger(value.total) || Number(value.total) < resources.length || !Number.isInteger(value.limit) || Number(value.limit) < 1) {
invalidProjectLinksResponse("Knowledge project-resources response pagination counts are invalid.");
}
if (value.cursor !== null && typeof value.cursor !== "string" || value.next_cursor !== null && typeof value.next_cursor !== "string" || typeof value.has_more !== "boolean" || typeof value.complete !== "boolean" || value.truncated !== false) {
invalidProjectLinksResponse("Knowledge project-resources response pagination state is invalid.");
}
if (value.has_more && (typeof value.next_cursor !== "string" || value.next_cursor.length === 0)) {
invalidProjectLinksResponse("Knowledge project-resources response claims more data without a continuation cursor.");
}
return { ...value, resources };
}

class KnowledgeProjectLinksHttpClient {
options;
fetchImpl;
Expand All @@ -30933,7 +31024,13 @@ class KnowledgeProjectLinksHttpClient {
});
const body = await response.json().catch(() => ({}));
if (!response.ok) {
throw new KnowledgeProjectLinksError(typeof body.error === "string" ? body.error : "KNOWLEDGE_PROJECT_LINKS_CONFLICT", typeof body.message === "string" ? body.message : `Knowledge project-links HTTP ${response.status}`, body.details && typeof body.details === "object" ? body.details : {});
const upstreamError = typeof body.error === "string" ? body.error : null;
const code = upstreamError && KNOWLEDGE_PROJECT_LINKS_ERROR_CODES.has(upstreamError) ? upstreamError : response.status === 404 ? "KNOWLEDGE_PROJECT_LINKS_NOT_FOUND" : "KNOWLEDGE_PROJECT_LINKS_CONFLICT";
throw new KnowledgeProjectLinksError(code, typeof body.message === "string" ? body.message : `Knowledge project-links HTTP ${response.status}`, {
...isRecord(body.details) ? body.details : {},
http_status: response.status,
...upstreamError && upstreamError !== code ? { upstream_error: upstreamError } : {}
});
}
return body;
}
Expand Down Expand Up @@ -30986,11 +31083,14 @@ class KnowledgeProjectLinksHttpClient {
for (const kind of options.kinds ?? [])
query2.append("kind", kind);
const suffix = query2.size > 0 ? `?${query2.toString()}` : "";
return this.request(`/v1/projects/${encodeURIComponent(projectId)}/resources${suffix}`);
return assertProjectResourcePage(await this.request(`/v1/projects/${encodeURIComponent(projectId)}/resources${suffix}`));
}
async readProjectResource(projectId, kind, resourceId) {
const body = await this.request(`/v1/projects/${encodeURIComponent(projectId)}/resources/${kind}/${encodeURIComponent(resourceId)}`);
return body.resource;
if (!isRecord(body) || !Object.hasOwn(body, "resource")) {
invalidProjectLinksResponse("Knowledge project-resource response does not contain a resource envelope.");
}
return assertProjectResource(body.resource, "Knowledge project-resource response.resource");
}
async readAllProjectResources(projectId, options = {}) {
const resources = [];
Expand Down
11 changes: 11 additions & 0 deletions bin/knowledge-serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -2890,6 +2890,17 @@ function knowledgeProjectLinksErrorResponse(error) {
}
throw error;
}
var KNOWLEDGE_PROJECT_LINKS_ERROR_CODES = new Set([
"KNOWLEDGE_PROJECT_LINKS_INVALID_INPUT",
"KNOWLEDGE_PROJECT_LINKS_CAPABILITY_MISMATCH",
"KNOWLEDGE_PROJECT_LINKS_DIGEST_MISMATCH",
"KNOWLEDGE_PROJECT_LINKS_IDEMPOTENCY_MISMATCH",
"KNOWLEDGE_PROJECT_LINKS_CONFLICT",
"KNOWLEDGE_PROJECT_LINKS_NOT_FOUND",
"KNOWLEDGE_PROJECT_LINKS_CURSOR_STALE",
"KNOWLEDGE_PROJECT_LINKS_INCOMPLETE_POPULATION",
"KNOWLEDGE_PROJECT_LINKS_INVALID_RESPONSE"
]);

// src/serve.ts
var KNOWLEDGE_SERVE_APP = "knowledge";
Expand Down
Loading
Loading