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
93 changes: 77 additions & 16 deletions docs/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,67 @@ This guide is for **library and MCP client authors** upgrading from earlier **0.

Under [semver 0.y.z](https://semver.org/spec/v2.0.0.html#spec-item-4), **0.1.x → 0.2.0 is a breaking minor** — pin `@0.2.0` only after reading this guide.

## Unreleased: `ServerContext` instance APIs (phase 1)

**Rationale:** Process-global singletons (Pinecone client slot, config, URL registry, suggest-flow gate, namespaces cache) complicate testing and multi-tenant embedding. Phase 1 introduces an opt-in **`ServerContext`** without removing legacy getters.

**Now (0.2.x — unchanged for existing embedders):**

```ts
import { PineconeClient, setPineconeClient } from '@will-cppa/pinecone-read-only-mcp';
import {
resolveAllianceConfig,
setupAllianceServer,
} from '@will-cppa/pinecone-read-only-mcp/alliance';

const config = resolveAllianceConfig({ apiKey: process.env.PINECONE_API_KEY! });
setPineconeClient(
new PineconeClient({
/* ... */
})
);
const server = await setupAllianceServer(config);
```

Module-level helpers (`getPineconeClient`, `registerUrlGenerator`, `requireSuggested`, etc.) continue to work; they delegate to a process-default context.

**New (opt-in instance path):**

```ts
import { createServer, PineconeClient } from '@will-cppa/pinecone-read-only-mcp';
import {
resolveAllianceConfig,
setupAllianceServer,
} from '@will-cppa/pinecone-read-only-mcp/alliance';

const config = resolveAllianceConfig({ apiKey: process.env.PINECONE_API_KEY! });
const ctx = createServer(config); // installs process-default + returns instance
ctx.setClient(
new PineconeClient({
apiKey: config.apiKey,
indexName: config.indexName,
sparseIndexName: config.sparseIndexName,
rerankModel: config.rerankModel,
defaultTopK: config.defaultTopK,
requestTimeoutMs: config.requestTimeoutMs,
})
);
const server = await setupAllianceServer(config); // uses process-default ctx for migrated tools
Comment thread
coderabbitai[bot] marked this conversation as resolved.
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

For custom tool wiring, pass `ctx` to migrated registrars:

```ts
import { registerQueryTool, registerCountTool, registerListNamespacesTool } from '…'; // internal today
registerQueryTool(server, ctx);
```

**Later (future minors/major):** Legacy module getters will be marked `### Deprecated` per [deprecation-policy.md](./deprecation-policy.md). Multi-tenant HTTP embedders should use one `ServerContext` per session rather than sharing process-global state.

See also [deprecation-policy.md § Future instance APIs](./deprecation-policy.md#future-instance-apis-servercontext).

---

## Unreleased: core vs Alliance config defaults

**Rationale:** Generic npm consumers must not silently connect to Alliance infrastructure or inherit Alliance rerank settings when using `resolveConfig` from the package root.
Expand Down Expand Up @@ -70,12 +131,12 @@ const ns = userInput.trim();

**`code` values (discriminated union):**

| `code` | `recoverable` | Notes |
| ------ | --------------- | ----- |
| `FLOW_GATE` | `true` | Suggestion: call `suggest_query_params` for the namespace first |
| `VALIDATION` | `true` | **`field` required** — input or `metadata_filter` dot-path |
| `PINECONE_ERROR` | `true` or `false` | Upstream / network / Pinecone failures |
| `TIMEOUT` | `true` | Outbound deadline exceeded |
| `code` | `recoverable` | Notes |
| ---------------- | ----------------- | --------------------------------------------------------------- |
| `FLOW_GATE` | `true` | Suggestion: call `suggest_query_params` for the namespace first |
| `VALIDATION` | `true` | **`field` required** — input or `metadata_filter` dot-path |
| `PINECONE_ERROR` | `true` or `false` | Upstream / network / Pinecone failures |
| `TIMEOUT` | `true` | Outbound deadline exceeded |

**Migration steps:**

Expand Down Expand Up @@ -104,12 +165,12 @@ const ns = userInput.trim();

**Rationale:** Align routing hints with the unified `query` tool vocabulary.

| Old (legacy) | New |
| ------------ | --- |
| `query_fast` | `fast` |
| `query_detailed` | `detailed` |
| `count` | `count` (unchanged) |
| _(n/a)_ | `full` (explicit preset) |
| Old (legacy) | New |
| ---------------- | ------------------------ |
| `query_fast` | `fast` |
| `query_detailed` | `detailed` |
| `count` | `count` (unchanged) |
| _(n/a)_ | `full` (explicit preset) |

**Migration steps:**

Expand All @@ -122,10 +183,10 @@ const ns = userInput.trim();

**Rationale:** One hybrid tool with a `preset` knob instead of duplicate registrations.

| Legacy tool call | New `query` call |
| ---------------- | ---------------- |
| `query_fast({ ...params })` | `query({ ...params, preset: 'fast' })` |
| `query_detailed({ ...params })` | `query({ ...params, preset: 'detailed' })` |
| Legacy tool call | New `query` call |
| ------------------------------- | --------------------------------------------------------------- |
| `query_fast({ ...params })` | `query({ ...params, preset: 'fast' })` |
| `query_detailed({ ...params })` | `query({ ...params, preset: 'detailed' })` |
| Custom / explicit rerank+fields | `query({ ...params, preset: 'full', use_reranking?, fields? })` |

**Example:**
Expand Down
6 changes: 3 additions & 3 deletions docs/deprecation-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,14 @@ Security fixes may break behavior when required; document impact in CHANGELOG an

## Future instance APIs (`ServerContext`)

A planned refactor introduces **`ServerContext`** and **`createServer(config)`** while keeping legacy module-level getters during a transition. That work will:
Phase 1 of the **`ServerContext`** / **`createServer(config)`** refactor is available while legacy module-level getters remain supported. See [MIGRATION.md § ServerContext instance APIs](./MIGRATION.md#unreleased-servercontext-instance-apis-phase-1) for upgrade steps.

That work will:

- Add new instance APIs without removing legacy getters in the same release.
- Document legacy getters under `### Deprecated` with a named removal target per this policy.
- Link migration steps from [MIGRATION.md](./MIGRATION.md) to this document.

Until that migration guide is published, treat this section as the policy constraint for that refactor.

## CHANGELOG format for breaking changes

This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). Each version block should use the sections that apply: `### Added`, `### Changed`, `### Deprecated`, `### Removed`, `### Fixed`.
Expand Down
2 changes: 1 addition & 1 deletion examples/alliance/library-embedding-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* Pattern (mirrors `src/index.ts`):
* 1. `resolveAllianceConfig({ apiKey, indexName, ... })` — Alliance index/rerank defaults when unset.
* 2. `new PineconeClient({ ... })` + `setPineconeClient(client)`.
* 2. `new PineconeClient({ ... })` + `setPineconeClient(client)` (legacy), or `createServer(config)` + `ctx.setClient(...)`.
* 3. `await setupAllianceServer(config)` then `server.connect(transport)`.
*
* **Single process:** `setupAllianceServer` registers tools against process-global
Expand Down
1 change: 1 addition & 0 deletions src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

export { setPineconeClient } from './server/client-context.js';
export { ServerContext, createServer, getDefaultServerContext } from './server/server-context.js';
export {
validateMetadataFilter,
validateMetadataFilterDetailed,
Expand Down
13 changes: 4 additions & 9 deletions src/core/server/client-context.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import { PineconeClient } from '../pinecone-client.js';

// Global Pinecone client (initialized lazily)
let pineconeClient: PineconeClient | null = null;
import { getDefaultServerContext } from './server-context.js';

/** Return the shared Pinecone client; throws if setPineconeClient has not been called. */
export function getPineconeClient(): PineconeClient {
if (!pineconeClient) {
throw new Error('Pinecone client not initialized. Call setPineconeClient first.');
}
return pineconeClient;
return getDefaultServerContext().getClientIfSet();
}

/** Set the shared Pinecone client used by all MCP tools. */
export function setPineconeClient(client: PineconeClient): void {
pineconeClient = client;
getDefaultServerContext().setClient(client);
}

/** Clear the shared client (used by {@link teardownServer} and tests). */
export function clearPineconeClient(): void {
pineconeClient = null;
getDefaultServerContext().clearClient();
}
17 changes: 8 additions & 9 deletions src/core/server/config-context.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import type { ServerConfig } from '../config.js';
import { resolveConfig } from '../config.js';

let activeConfig: ServerConfig | null = null;
import {
getDefaultServerContext,
setDefaultServerContext,
setPendingServerConfig,
} from './server-context.js';

/** Replace the process-global server config (called from setup with CLI/env-derived config). */
export function setServerConfig(config: ServerConfig): void {
activeConfig = config;
setPendingServerConfig(config);
}

/** Clear active config so the next `getServerConfig()` resolves again (used by {@link teardownServer}). */
export function resetServerConfig(): void {
activeConfig = null;
setDefaultServerContext(null);
}

/**
Expand All @@ -22,8 +24,5 @@ export function resetServerConfig(): void {
* pass config from `resolveAllianceConfig()` into `setupAllianceServer(config)`.
*/
export function getServerConfig(): ServerConfig {
if (!activeConfig) {
activeConfig = resolveConfig({});
}
return activeConfig;
return getDefaultServerContext().getConfig();
}
34 changes: 4 additions & 30 deletions src/core/server/namespaces-cache.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,6 @@
import { getServerConfig } from './config-context.js';
import { getPineconeClient } from './client-context.js';
import { getDefaultServerContext, type NamespaceInfo } from './server-context.js';

export type NamespaceInfo = {
namespace: string;
recordCount: number;
metadata: Record<string, string>;
};

type CacheEntry = {
data: NamespaceInfo[];
expiresAt: number;
};

let namespacesCache: CacheEntry | null = null;
export type { NamespaceInfo };

/**
* Return namespace list with metadata; uses an in-memory cache whose TTL is
Expand All @@ -23,24 +11,10 @@ export async function getNamespacesWithCache(): Promise<{
cache_hit: boolean;
expires_at: number;
}> {
const now = Date.now();
if (namespacesCache && now < namespacesCache.expiresAt) {
return {
data: namespacesCache.data,
cache_hit: true,
expires_at: namespacesCache.expiresAt,
};
}

const client = getPineconeClient();
const data = await client.listNamespacesWithMetadata();
const ttlMs = getServerConfig().cacheTtlMs;
const expiresAt = now + ttlMs;
namespacesCache = { data, expiresAt };
return { data, cache_hit: false, expires_at: expiresAt };
return getDefaultServerContext().getNamespacesWithCache();
}

/** Clear the namespaces cache so the next call to getNamespacesWithCache refetches. */
export function invalidateNamespacesCache(): void {
namespacesCache = null;
getDefaultServerContext().invalidateNamespacesCache();
}
Loading
Loading