UNPKG

@withpica/mcp-server

Version:

MCP Server for withPICA — enables AI assistants to interact with your withPICA music catalog

108 lines • 5.22 kB
// Copyright (c) 2025-2026 Withpica Ltd. All rights reserved. import { parseChangelogText, CHANGELOG_SECTION_NAMES, } from "../lib/changelog.js"; import { CHANGELOG_MD, PACKAGE_NAME, PACKAGE_VERSION, } from "../lib/changelog.generated.js"; const RESOURCE_URI = "release-notes://withpica/mcp-server/latest"; const DEFAULT_LIMIT = 3; const MAX_LIMIT = 20; function compareSemver(a, b) { const parse = (v) => v.split(".").map((n) => parseInt(n, 10) || 0); const [a1, a2, a3] = parse(a); const [b1, b2, b3] = parse(b); if (a1 !== b1) return a1 - b1; if (a2 !== b2) return a2 - b2; return a3 - b3; } export class ReleaseNotesTools { pica; constructor(pica) { this.pica = pica; } getTools() { return [ { definition: { name: "pica_release_notes", scope: "read:catalog", audiences: ["stdio", "http"], tier: "read", vernacular_kind: "specialist", vernacular_reason: "Server self-introspection — surfaces platform release notes for the MCP server itself. Not a catalog tool; not yet covered by the Creator Question Atlas (ADR-226). Disambiguated from the ambiguous 'what's new' phrasing by the NOT-FOR clause in the description.", description: "Use when the user asks: 'what's new in pica', 'any updates to the server', " + "'what changed in the MCP', 'what version am I on'. " + "Returns recent releases of the PICA MCP server (platform updates). " + "NOT for catalog activity, recent works, or 'what's new in my catalogue' — " + "use pica_dashboard_briefing or pica_works_query for that. " + "Returns { package, current_version, releases: [{ version, date, sections, markdown }], resource_uri }. " + "Defaults to the last 3 releases; use `since_version` to bound the output " + "or `sections` to filter to Added/Fixed/etc.", workflows: "infrastructure", inputSchema: { type: "object", properties: { since_version: { type: "string", description: "If set, returns only releases strictly greater than this version " + "(semver-compared). Example: '2.50.0' returns 2.50.1+, 2.51.0+, etc. " + "Versions equal to since_version are excluded.", }, limit: { type: "number", minimum: 1, maximum: MAX_LIMIT, description: `Maximum number of releases to return. Default ${DEFAULT_LIMIT}, max ${MAX_LIMIT}.`, }, sections: { type: "array", items: { type: "string", enum: [...CHANGELOG_SECTION_NAMES] }, description: "Filter each release's sections to only these names " + "(e.g. ['Added', 'Fixed']). Sections not in the list are omitted from output.", }, }, }, }, executor: this.releaseNotes.bind(this), }, ]; } releaseNotes = async (args) => { const sinceVersion = typeof args.since_version === "string" ? args.since_version : null; const rawLimit = typeof args.limit === "number" ? args.limit : DEFAULT_LIMIT; const limit = Math.max(1, Math.min(MAX_LIMIT, Math.floor(rawLimit))); const sectionFilter = Array.isArray(args.sections) ? args.sections.filter((s) => CHANGELOG_SECTION_NAMES.includes(s)) : null; const all = parseChangelogText(CHANGELOG_MD); let filtered = all; if (sinceVersion) { filtered = all.filter((e) => compareSemver(e.version, sinceVersion) > 0); } filtered = filtered.slice(0, limit); const releases = filtered.map((e) => { const sections = sectionFilter ? Object.fromEntries(Object.entries(e.sections).filter(([k]) => sectionFilter.includes(k))) : e.sections; return { version: e.version, date: e.date, sections, markdown: e.markdown, }; }); return { content: [ { type: "text", text: JSON.stringify({ package: PACKAGE_NAME, current_version: PACKAGE_VERSION, releases, resource_uri: RESOURCE_URI, }, null, 2), }, ], }; }; } //# sourceMappingURL=release-notes.js.map