Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ coverage/

# Misc
.npmrc
.temp/
.tmp/
cursor_logs/

# Scripts
scripts/*.bat
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Tagged releases are published to npm from GitHub Actions when a **GitHub Release

### Removed

- **Breaking (library):** Trimmed internal-only re-exports from the package root and `/alliance` entry to shrink the public surface and blast radius: `trimOptional`, `createUnconfiguredAllianceContext` (core), and the concrete URL generators `generatorMailing`, `generatorSlackCpplang` (alliance). These were internal helpers, not part of the documented API; register built-ins via `registerBuiltinUrlGenerators` and build contexts via `createServer` / `createIsolatedContext`. A snapshot test now guards the runtime export surface so internal symbols cannot leak back in. See [MIGRATION.md](docs/MIGRATION.md#internal-only-re-exports-removed-203). (#203)
- **Breaking (library):** Trimmed internal-only re-exports from the package root and `/alliance` entry to shrink the public surface and blast radius: `trimOptional`, `createUnconfiguredAllianceContext` (core), and the concrete URL generators `generatorMailing`, `generatorSlackCpplang` (alliance). These were internal helpers, not part of the documented API; register built-ins via `registerBuiltinUrlGenerators` and build contexts via `createServer` / `createIsolatedContext`. A snapshot test now guards the runtime export surface so internal symbols cannot leak back in. See [MIGRATION.md](docs/MIGRATION.md#050-internal-only-re-exports-removed-203). (#203)

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Current version: 0.5.0 (npm `latest` after publish). Pin `@0.5.0` in install and

## Upgrading to 0.5.0

Version **0.5.0** includes breaking MCP and library changes: `list_sources` returns `sources: { name, description? }[]`, `PineconeClient.listNamespacesWithMetadata()` returns `{ namespaces, warnings }`, internal-only package re-exports were removed (#203), the MCP SDK floor is **^1.29.0**, and hybrid leg failure with zero surviving hits is signaled under `experimental.degraded` / `experimental.hybrid_leg_failed` (#228). Legacy module facades remain deprecated but are **not** removed in this release. See [docs/MIGRATION.md § list_sources](docs/MIGRATION.md#050-list_sources-response-shape), [§ listNamespacesWithMetadata](docs/MIGRATION.md#050-pineconeclientlistnamespaceswithmetadata-return-shape), [§ internal re-exports (#203)](docs/MIGRATION.md#internal-only-re-exports-removed-203), and the [CHANGELOG](CHANGELOG.md#050---2026-07-25).
Version **0.5.0** includes breaking MCP and library changes: `list_sources` returns `sources: { name, description? }[]`, `PineconeClient.listNamespacesWithMetadata()` returns `{ namespaces, warnings }`, internal-only package re-exports were removed (#203), the MCP SDK floor is **^1.29.0**, and hybrid leg failure with zero surviving hits is signaled under `experimental.degraded` / `experimental.hybrid_leg_failed` (#228). Legacy module facades remain deprecated but are **not** removed in this release. See [docs/MIGRATION.md § list_sources](docs/MIGRATION.md#050-list_sources-response-shape), [§ listNamespacesWithMetadata](docs/MIGRATION.md#050-pineconeclientlistnamespaceswithmetadata-return-shape), [§ internal re-exports (#203)](docs/MIGRATION.md#050-internal-only-re-exports-removed-203), and the [CHANGELOG](CHANGELOG.md#050---2026-07-25).

## Upgrading to 0.4.0

Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ Guides for operators, MCP client authors, and library embedders.
| [deprecation-policy.md](./deprecation-policy.md) | Deprecation window, semver, CHANGELOG conventions |
| [templates/breaking-change-release-notes.md](./templates/breaking-change-release-notes.md) | GitHub Release body template for breaking versions |
| [RELEASING.md](./RELEASING.md) | npm publish via GitHub Releases |
| [release-verification.md](./release-verification.md) | Post-publish verification and production-readiness sign-offs |

The main [README.md](../README.md) remains the high-level overview; deep reference lives here.
4 changes: 4 additions & 0 deletions docs/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ Packages are published to npm as **`@will-cppa/pinecone-read-only-mcp`**.
## Version source

`SERVER_VERSION` is read from `package.json` at runtime so MCP `serverInfo` matches the published package.

## Post-publish verification

After a release is on npm, record artifact and CI alignment in [release-verification.md](./release-verification.md) (see [0.5.0 sign-off](./release-verification-0.5.0.md) for `v0.5.0`).
4 changes: 2 additions & 2 deletions docs/deprecation-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Renames may ship the **replacement immediately** alongside the deprecated alias

APIs deprecated **before** this policy was published follow the removal target recorded in CHANGELOG and source comments at deprecation time. The `paper_number` field on query result rows (use `document_id` instead) was deprecated in **0.2.0** with removal planned no earlier than the **next major** release after `1.0.0`; it will not be removed in a `0.y` minor without an explicit CHANGELOG entry and MIGRATION update.

### Active deprecations - legacy module facades
### Active deprecations: legacy module facades

Module-level singleton facades delegate to `getDefaultServerContext()`. Migrate to **`ServerContext`** instance methods via `createServer(config)` and pass `{ context: ctx }` to `setupCoreServer` / `setupAllianceServer`. Deprecated in **`0.3.0`**; earliest removal **`0.5.0`** (per [Deprecation window](#deprecation-window) above). See [MIGRATION.md § Legacy module-facade deprecations](./MIGRATION.md#030-legacy-module-facade-deprecations).

Expand Down Expand Up @@ -132,7 +132,7 @@ Each breaking bullet should state:

- **What changed** — concrete behavior or schema difference.
- **Who is affected** — MCP clients, library embedders, operators, etc.
- **Migration** — link to [MIGRATION.md](./MIGRATION.md#anchor) (or “see MIGRATION.md § …”).
- **Migration** — link to the specific [MIGRATION.md](./MIGRATION.md) heading covering the change (or “see MIGRATION.md § …”).

### Deprecated entries

Expand Down
124 changes: 124 additions & 0 deletions docs/release-verification-0.5.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Production readiness sign-off: `@will-cppa/pinecone-read-only-mcp@0.5.0`

- **Package:** `@will-cppa/pinecone-read-only-mcp`
- **Version:** `0.5.0`
- **Git tag:** `v0.5.0` → `c28e346efafadbfef98891e523d566eebe957dad` (“Updated documents for new release (#236)”)
- **Recorded:** 2026-07-29 (UTC+8 local)
- **Verifier:** Jonathan (@jonathanMLDev), assignee for week 5 verification task
- **Scope:** Verification and sign-off only. No product code changes. `ServerContext` decomposition and legacy facade removal remain deferred past July 2026.

## Acceptance criteria evidence

### AC 1 — npm registry version and publish time

| Field | Value |
| ---------------------- | ------------------------------------------------------------------------------------------------------- |
| `npm view … version` | `0.5.0` |
| `dist-tags.latest` | `0.5.0` |
| `time['0.5.0']` (UTC) | `2026-07-24T18:41:39.687Z` |
| CHANGELOG release date | `2026-07-25` ([CHANGELOG.md](../CHANGELOG.md#050---2026-07-25); calendar date vs UTC publish timestamp) |

Commands used: `npm view @will-cppa/pinecone-read-only-mcp@0.5.0 version gitHead dist-tags time --json`

### AC 2 — `package.json` in tarball vs tagged tree

Repository state for verification: **detached HEAD at `v0.5.0`** (`c28e346`).

Compared packed `package/package.json` from:

1. Local `npm pack` after `npm ci` and `npm run build` on `v0.5.0`
2. Registry `npm pack @will-cppa/pinecone-read-only-mcp@0.5.0`

These fields are **identical** in both tarballs (local pack vs registry pack; no differences in `version`, `files`, `exports`, or `bin`):

- `version`: `0.5.0`
- `files`: `dist`, `README.md`, `LICENSE`, `CHANGELOG.md`
- `bin`:

```json
{
"pinecone-read-only-mcp": "dist/index.js"
}
```

- `exports` (same object in both packed `package/package.json` files):

```json
{
".": {
"types": "./dist/core/index.d.ts",
"import": "./dist/core/index.js"
},
"./alliance": {
"types": "./dist/alliance/index.d.ts",
"import": "./dist/alliance/index.js"
},
"./package.json": "./package.json"
}
```

`npm pack --dry-run` on the tag reported **208** packaged files; registry pack lists the same paths (no `src/`).

### AC 3 — `dist/` contents and hygiene

- Fresh build on tag: `npm run build` (runs `clean` then `tsc -p tsconfig.build.json`) before pack.
- **No** `*.test.*` (or other test sources) under packed `dist/`.
- **File path set** under `package/` (local vs registry): identical.
- **`dist/**` content:** SHA-256 compared for every file under `dist/` in both tarballs → **0 mismatches** (functional parity of compiled output).
Comment thread
jonathanMLDev marked this conversation as resolved.
- **Tarball bytes:** full `.tgz` SHA-256 differs between local pack and registry. This is expected from tar entry ordering and gzip stream differences across npm/Node versions (local pack used Node v24.11.1; registry was published from CI on Node 20.x) — **not** content drift: `package.json`, `README.md`, `CHANGELOG.md`, and `LICENSE` are byte-identical between the packed tarball and `git show v0.5.0:<path>` for each file (verified independently of the local `npm pack`, so this holds regardless of the packer's Node version). Registry tarball matches npm `dist.shasum` `f912f88fee5a8499eadac70ddcbf4a25660fbe76`.
- Local verification used **Node v24.11.1**; publish workflow uses **Node 20.x** on Ubuntu ([publish.yml](../.github/workflows/publish.yml)). Despite Node major difference, compiled `dist/` artifacts matched registry byte-for-byte.

### AC 4 — CI, CodeQL, and Publish workflow (release gate)

**npm `gitHead`:** `c28e346efafadbfef98891e523d566eebe957dad` — matches `git rev-parse v0.5.0^{commit}`.

Workflow runs confirmed via the public [GitHub Actions](https://github.com/cppalliance/pinecone-read-only-mcp-typescript/actions) UI on 2026-07-29 (listed as successful; no failure marker on these runs):

| Check | Run | Link |
| ---------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| **CI** on merge commit `c28e346` (#236) | CI **#480** — “Updated documents for new release (#236)” on `main` | [Run #480](https://github.com/cppalliance/pinecone-read-only-mcp-typescript/actions/runs/30117302054) — Success |
| **CodeQL** on `c28e346` | CodeQL **#505** — same commit on `main` | [Run #505](https://github.com/cppalliance/pinecone-read-only-mcp-typescript/actions/runs/30117301927) — Success |
| **Publish to npm** for release `v0.5.0` | Publish **#11** — “Release v0.5.0 published” (~3m 25s) | [Run #11](https://github.com/cppalliance/pinecone-read-only-mcp-typescript/actions/runs/30117705749) — Success |

Direct run permalinks (not `?query=` filters, which GitHub Actions silently ignores for this UI and would otherwise land on the unfiltered run list).

Release: [v0.5.0](https://github.com/cppalliance/pinecone-read-only-mcp-typescript/releases/tag/v0.5.0) · [Commit checks](https://github.com/cppalliance/pinecone-read-only-mcp-typescript/commit/c28e346efafadbfef98891e523d566eebe957dad/checks)

Publish path aligns with [RELEASING.md](./RELEASING.md): `workflow_call` to `ci.yml`, then `npm publish --provenance --access public`; `prepublishOnly` runs `npm run ci`.

### AC 5 — Artifact alignment (npm vs tag `v0.5.0`)

**Artifact verification is complete.** The published npm package matches repository tag `v0.5.0` (`c28e346`) for the checks in AC 1–4:

- Registry version and `gitHead` align with tag `c28e346`.
- Packed manifest (`version`, `files`, `exports`, `bin`) matches the tagged `package.json` (see AC 2).
- Published `dist/` matches a clean tag build (no test sources in artifact; `dist/**` hash parity with registry).
- CI (#480), CodeQL (#505), and Publish (#11) for the `v0.5.0` release completed successfully per GitHub Actions (see AC 4).

No re-publish or version bump is required based on this verification.

**Registry tarball:** `https://registry.npmjs.org/@will-cppa/pinecone-read-only-mcp/-/pinecone-read-only-mcp-0.5.0.tgz` (`dist.shasum` `f912f88fee5a8499eadac70ddcbf4a25660fbe76`).

**Process sign-off:** Organizational production-readiness sign-off (AC 6) remains **pending** until this documentation PR is approved and merged. Until then, do not treat the release process as fully signed off in-repo.

### AC 6 — Pull request and review

Sign-off is committed under `docs/`. **Requires:**

- [x] PR opened with this file and doc index updates ([README.md](./README.md), [RELEASING.md](./RELEASING.md)) — [PR #242](https://github.com/cppalliance/pinecone-read-only-mcp-typescript/pull/242)
- [ ] ≥ 1 reviewer approval
- [ ] Approver and approval date recorded here after review
- [ ] Merged PR URL recorded here after merge (same as #242 when merged)

## Commands reference (replay on tag)

```powershell
git checkout v0.5.0
npm ci
npm run build
npm pack --dry-run
npm pack
npm pack @will-cppa/pinecone-read-only-mcp@0.5.0
```

**Docs link-check:** `npm run docs:link-check` — exit **0** on 2026-07-30 (local, after extending the checker with heading-anchor validation and fixing the anchors it flagged). **Pending:** record the PR #242 `quality` job run URL here once CI finishes on the pushed commit — see the [PR #242 checks tab](https://github.com/cppalliance/pinecone-read-only-mcp-typescript/pull/242/checks).
9 changes: 9 additions & 0 deletions docs/release-verification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Release verification

Post-publish records that npm artifacts match tagged repository state and release CI gates.

| Version | Sign-off |
| ------- | -------- |
| 0.5.0 | [release-verification-0.5.0.md](./release-verification-0.5.0.md) |

Add a new `release-verification-X.Y.Z.md` after each production release and link it from this table.
123 changes: 119 additions & 4 deletions scripts/docs-link-check.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
/**
* Run markdown-link-check once for README, CHANGELOG, and every *.md under docs/.
* Avoids per-file `npx` invocations (slow / flaky under registry hiccups).
*
* markdown-link-check verifies that link *targets* (files/URLs) exist, but does not
* reliably validate heading *fragments* (`file.md#some-heading`), especially across
* files (tcort/markdown-link-check#212, #225). `checkHeadingAnchors` below closes that
* gap with a self-contained GitHub-slug implementation (no extra dependency) so a
* renamed/retitled heading that leaves a dangling `#anchor` fails CI.
*/

import { spawnSync } from 'node:child_process';
import { readdirSync, statSync } from 'node:fs';
import { join } from 'node:path';
import { readFileSync, readdirSync, statSync } from 'node:fs';
import { dirname, join, relative, resolve } from 'node:path';
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/** @param {string} dir @returns {string[]} */
function walkMarkdownFiles(dir) {
Expand All @@ -25,11 +31,120 @@ function walkMarkdownFiles(dir) {

const paths = ['README.md', 'CHANGELOG.md', ...walkMarkdownFiles('docs')];

/**
* ASCII + common Unicode punctuation stripped by GitHub's heading slugger
* (verified against `github-slugger@2` output for every heading in this repo's
* docs). Deliberately excludes `-` and `_`, which GitHub preserves.
*/
const SLUG_STRIP_RE = /[!-,./:-@[-^`{-~\u00A1-\u00BF\u00D7\u00F7\u2000-\u206F\u2190-\u21FF]/g;

/** @param {string} text @returns {string} */
function stripInlineMarkdown(text) {
Comment thread
jonathanMLDev marked this conversation as resolved.
Outdated
const codeSpans = [];
let out = text.replace(/`([^`]*)`/g, (_m, inner) => {
codeSpans.push(inner);
return `\u0000${codeSpans.length - 1}\u0000`;
});
out = out
.replace(/\*\*([^*]*)\*\*/g, '$1')
.replace(/\*([^*]*)\*/g, '$1')
.replace(/_([^_]*)_/g, '$1');
Comment thread
jonathanMLDev marked this conversation as resolved.
Outdated
return out.replace(/\u0000(\d+)\u0000/g, (_m, i) => codeSpans[Number(i)]);
}

/** @param {string} text @returns {string} */
function slugify(text) {
return stripInlineMarkdown(text).toLowerCase().replace(SLUG_STRIP_RE, '').replace(/ /g, '-');
}

/** @param {string} content @returns {Set<string>} */
function headingSlugs(content) {
const slugs = new Set();
const occurrences = Object.create(null);
let inCodeBlock = false;
for (const line of content.split(/\r?\n/)) {
if (/^```/.test(line.trim())) {
inCodeBlock = !inCodeBlock;
continue;
}
if (inCodeBlock) continue;
const m = line.match(/^(#{1,6})\s+(.*)$/);
if (!m) continue;
const base = slugify(m[2]);
let slug = base;
if (Object.prototype.hasOwnProperty.call(occurrences, base)) {
occurrences[base]++;
slug = `${base}-${occurrences[base]}`;
} else {
occurrences[base] = 0;
}
slugs.add(slug);
}
return slugs;
}

/** @param {string} content @returns {Array<{line: number, target: string}>} */
function extractFragmentLinks(content) {
const links = [];
const linkRe = /\[[^\]]*\]\(([^)]+)\)/g;
let inCodeBlock = false;
content.split(/\r?\n/).forEach((line, idx) => {
if (/^```/.test(line.trim())) {
inCodeBlock = !inCodeBlock;
return;
}
if (inCodeBlock) return;
Comment thread
jonathanMLDev marked this conversation as resolved.
let m;
while ((m = linkRe.exec(line)) !== null) {
const target = m[1].trim();
if (/^https?:\/\//.test(target) || target.startsWith('mailto:')) continue;
if (!target.includes('#')) continue;
links.push({ line: idx + 1, target });
}
});
return links;
}

/** @returns {string[]} human-readable failure descriptions */
function checkHeadingAnchors() {
const slugsByFile = new Map();
for (const p of paths) {
slugsByFile.set(resolve(p), headingSlugs(readFileSync(p, 'utf8')));
}

const failures = [];
for (const p of paths) {
const abs = resolve(p);
const content = readFileSync(p, 'utf8');
for (const { line, target } of extractFragmentLinks(content)) {
const hashIdx = target.indexOf('#');
const filePart = target.slice(0, hashIdx);
const anchor = target.slice(hashIdx + 1);
const targetAbs = filePart === '' ? abs : resolve(dirname(abs), filePart);
const slugs = slugsByFile.get(targetAbs);
if (!slugs) continue; // target file outside the checked set (e.g. not markdown); skip
if (!slugs.has(anchor)) {
failures.push(`${p}:${line} -> ${target} (no heading slug "${anchor}" in ${relative('.', targetAbs)})`);
}
}
}
return failures;
}

const shell = process.platform === 'win32';
const r = spawnSync(
const linkResult = spawnSync(
'npx',
['--yes', 'markdown-link-check@3', '-c', '.markdown-link-check.json', ...paths],
{ stdio: 'inherit', shell }
);
const linkExit = linkResult.status === null ? 1 : linkResult.status;

const anchorFailures = checkHeadingAnchors();
if (anchorFailures.length > 0) {
console.error(`\nERROR: ${anchorFailures.length} dead heading anchor(s) found!`);
for (const f of anchorFailures) console.error(` [✖] ${f}`);
} else {
console.log('\nAll heading anchors resolve.');
}

process.exit(r.status === null ? 1 : r.status);
process.exit(linkExit !== 0 ? linkExit : anchorFailures.length > 0 ? 1 : 0);
Loading