Skip to content
Open
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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ All notable changes to Memento MCP will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.4.0] - 2025-10-28

### Added
- Support for Google's Gemini embedding models.
- Ability to specify custom vector dimensions for both Google and OpenAI embedding services.
- New environment variables `EMBEDDING_DIMENSIONS` and `ENABLE_CUSTOM_DIMENSIONS` to control the new feature.
- Support for custom OpenAI-compatible API endpoints via the `EMBEDDING_ENDPOINT` environment variable.

### Changed
- Standardized embedding-related environment variables (`EMBEDDING_MODEL`, `EMBEDDING_ENDPOINT`) for consistency across providers.

## [0.3.9] - 2025-05-08

### Changed
Expand Down
40 changes: 33 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,12 +418,16 @@ NEO4J_VECTOR_DIMENSIONS=1536
NEO4J_SIMILARITY_FUNCTION=cosine

# Embedding Service Configuration
MEMORY_STORAGE_TYPE=neo4j
OPENAI_API_KEY=your-openai-api-key
OPENAI_EMBEDDING_MODEL=text-embedding-3-small
- `MEMORY_STORAGE_TYPE`: The storage type to use. Currently, only `neo4j` is supported.
- `OPENAI_API_KEY`: Your OpenAI API key. For local models (e.g., Ollama, LM Studio), you can set this to any non-empty string like `"ollama"`.
- `GOOGLE_API_KEY`: Your Google API key.
- `EMBEDDING_MODEL`: The embedding model to use (e.g., `text-embedding-3-small`, `models/embedding-001`).
- `EMBEDDING_DIMENSIONS`: The dimensions of the embeddings. **Important:** This value must match the dimensions of your selected model and the `NEO4J_VECTOR_DIMENSIONS`. Set this to the default dimensions of your embedding model. If your model supports other dimensions you MUST also enable `ENABLE_CUSTOM_DIMENSIONS`.
- `EMBEDDING_ENDPOINT`: Optional custom endpoint for OpenAI-compatible APIs (e.g., `http://localhost:1234/v1/embeddings` for a local model server).
- `ENABLE_CUSTOM_DIMENSIONS`: Set to `true` or `false`. Controls whether the `dimensions` (for OpenAI and compatible) or `output_dimensionality` (for Google) parameter is passed in the API request. Some models don't support this. Defaults to `false`.

# Debug Settings
DEBUG=true
- `DEBUG`: Set to `true` to enable debug logging.
```

### Command Line Options
Expand All @@ -450,9 +454,13 @@ Available OpenAI embedding models:
- `text-embedding-3-large`: Higher accuracy, more expensive (3072 dimensions)
- `text-embedding-ada-002`: Legacy model (1536 dimensions)

Available Google embedding models:

- `gemini-embedding-001`: A powerful and efficient model (3072 dimensions)

#### OpenAI API Configuration

To use semantic search, you'll need to configure OpenAI API credentials:
To use semantic search with OpenAI, you'll need to configure your API credentials:

1. Obtain an API key from [OpenAI](https://platform.openai.com/api-keys)
2. Configure your environment with:
Expand All @@ -461,7 +469,23 @@ To use semantic search, you'll need to configure OpenAI API credentials:
# OpenAI API Key for embeddings
OPENAI_API_KEY=your-openai-api-key
# Default embedding model
OPENAI_EMBEDDING_MODEL=text-embedding-3-small
EMBEDDING_MODEL=text-embedding-3-small
EMBEDDING_DIMENSIONS=1536
```

#### Google API Configuration

To use semantic search with Google, you'll need to configure your API credentials:

1. Obtain an API key from [Google AI Studio](https://aistudio.google.com/app/apikey)
2. Configure your environment with:

```bash
# Google API Key for embeddings
GOOGLE_API_KEY=your-google-api-key
# Default embedding model
EMBEDDING_MODEL=gemini-embedding-001
EMBEDDING_DIMENSIONS=3072
```

> **Note**: For testing environments, the system will mock embedding generation if no API key is provided. However, using real embeddings is recommended for integration testing.
Expand All @@ -488,7 +512,9 @@ Add this to your `claude_desktop_config.json`:
"NEO4J_VECTOR_DIMENSIONS": "1536",
"NEO4J_SIMILARITY_FUNCTION": "cosine",
"OPENAI_API_KEY": "your-openai-api-key",
"OPENAI_EMBEDDING_MODEL": "text-embedding-3-small",
"GOOGLE_API_KEY": "your-google-api-key",
"EMBEDDING_MODEL": "text-embedding-3-small",
"EMBEDDING_DIMENSIONS": "1536",
"DEBUG": "true"
}
}
Expand Down
27 changes: 23 additions & 4 deletions example.env
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
# Tests will mock embedding genration if no API key is provided.
# To run tests with real embeddings (recommended), reaame this file to .env file with your OpenAI API key.
# To run tests with real embeddings (recommended), rename this file to .env and add your API keys.
# If only one API key is provided, the corresponding embedding service will be used.
# If both are provided, OpenAI will be used by default.

# OpenAI API Key for embeddings
# OPENAI_API_KEY="lmstudio"
# OPENAI_API_KEY="ollama"
OPENAI_API_KEY=your-openai-api-key
# Default embedding model
OPENAI_EMBEDDING_MODEL=text-embedding-3-small

# Google API Key for embeddings
GOOGLE_API_KEY=your-google-api-key

# The model to use for generating embeddings (e.g., 'text-embedding-3-small', 'gemini-embedding-001')
EMBEDDING_MODEL=text-embedding-3-small

# Optional custom endpoint for OpenAI compatible APIs
# EMBEDDING_ENDPOINT=https://api.custom.com/v1/embeddings

# Optional dimensions (must be the same as neo4j), set to the model's default
EMBEDDING_DIMENSIONS=1536

# Optionally controls whether the `dimensions` (for OpenAI and compatible)
# or `output_dimensionality` (for Google) parameter is passed in the API request.
# Some models don't support this, only enable if you're sure your model does.
ENABLE_CUSTOM_DIMENSIONS=false
21 changes: 17 additions & 4 deletions smithery.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,15 @@ startCommand:
- neo4jUsername
- neo4jPassword
- neo4jDatabase
- openAiApiKey
properties:
embeddingModel:
type: string
default: 'text-embedding-3-small'
description: The embedding model to use
embeddingEndpoint:
type: string
default: 'https://api.openai.com/v1/embeddings'
description: Optional custom endpoint for the embedding service
neo4jUri:
type: string
default: bolt://127.0.0.1:7687
Expand All @@ -30,8 +37,10 @@ startCommand:
description: Neo4j database name
openAiApiKey:
type: string
default: your-openai-api-key
description: OpenAI API key for embeddings
googleApiKey:
type: string
description: Google API key for embeddings
commandFunction:
# A JS function that produces the CLI command based on the given config to start the MCP on stdio.
|-
Expand All @@ -48,7 +57,9 @@ startCommand:
NEO4J_VECTOR_DIMENSIONS: '1536',
NEO4J_SIMILARITY_FUNCTION: 'cosine',
OPENAI_API_KEY: config.openAiApiKey,
OPENAI_EMBEDDING_MODEL: 'text-embedding-3-small',
GOOGLE_API_KEY: config.googleApiKey,
EMBEDDING_MODEL: config.embeddingModel,
EMBEDDING_ENDPOINT: config.embeddingEndpoint,
DEBUG: 'true'
}
})
Expand All @@ -57,4 +68,6 @@ startCommand:
neo4jUsername: neo4j
neo4jPassword: memento_password
neo4jDatabase: neo4j
openAiApiKey: your-openai-api-key
openAiApiKey: 'your-openai-api-key'
googleApiKey: 'your-google-api-key'
embeddingModel: 'text-embedding-3-small'
21 changes: 14 additions & 7 deletions src/cli/cli-README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,13 @@ NEO4J_VECTOR_DIMENSIONS=1536
NEO4J_SIMILARITY_FUNCTION=cosine

# Embedding Service Configuration
MEMORY_STORAGE_TYPE=neo4j
OPENAI_API_KEY=your-openai-api-key
OPENAI_EMBEDDING_MODEL=text-embedding-3-small
- `MEMORY_STORAGE_TYPE`: The storage type to use. Currently, only `neo4j` is supported.
- `OPENAI_API_KEY`: Your OpenAI API key. For local models (e.g., Ollama, LM Studio), you can set this to any non-empty string like `"ollama"`.
- `GOOGLE_API_KEY`: Your Google API key.
- `EMBEDDING_MODEL`: The embedding model to use (e.g., `text-embedding-3-small`, `models/embedding-001`).
- `EMBEDDING_DIMENSIONS`: The dimensions of the embeddings. **Important:** This value must match the dimensions of your selected model and the `NEO4J_VECTOR_DIMENSIONS`. Set this to the default dimensions of your embedding model. If your model supports other dimensions you MUST also enable `ENABLE_CUSTOM_DIMENSIONS`.
- `EMBEDDING_ENDPOINT`: Optional custom endpoint for OpenAI-compatible APIs (e.g., `http://localhost:1234/v1/embeddings` for a local model server).
- `ENABLE_CUSTOM_DIMENSIONS`: Set to `true` or `false`. Controls whether the `dimensions` (for OpenAI and compatible) or `output_dimensionality` (for Google) parameter is passed in the API request. Some models don't support this. Defaults to `false`.

# Optional Diagnostic Settings
DEBUG=true
Expand All @@ -113,7 +117,9 @@ Or directly in the Claude Desktop configuration:
"NEO4J_VECTOR_DIMENSIONS": "1536",
"NEO4J_SIMILARITY_FUNCTION": "cosine",
"OPENAI_API_KEY": "your-openai-api-key",
"OPENAI_EMBEDDING_MODEL": "text-embedding-3-small",
"GOOGLE_API_KEY": "your-google-api-key",
"EMBEDDING_MODEL": "text-embedding-3-small",
"EMBEDDING_ENDPOINT": "https://your-custom-endpoint/v1/embeddings",
"DEBUG": "true"
}
}
Expand Down Expand Up @@ -141,9 +147,10 @@ The Neo4j CLI tools support the following options:

For vector search functionality, an embedding service is required:

- `OPENAI_API_KEY`: Your OpenAI API key (required)
- `OPENAI_EMBEDDING_MODEL`: The embedding model to use (default: `text-embedding-3-small`)
- Options: `text-embedding-3-small` (1536 dimensions), `text-embedding-3-large` (3072 dimensions)
- `OPENAI_API_KEY`: Your OpenAI API key.
- `GOOGLE_API_KEY`: Your Google API key.
- `EMBEDDING_MODEL`: The embedding model to use (e.g., `text-embedding-3-small`, `gemini-embedding-001`).
- `EMBEDDING_ENDPOINT`: Optional custom endpoint for compatible APIs.

#### Optional Configuration

Expand Down
6 changes: 5 additions & 1 deletion src/embeddings/DefaultEmbeddingService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ export class DefaultEmbeddingService extends EmbeddingService {
const isMockMode = process.env.MOCK_EMBEDDINGS === 'true';
const defaultDimensions = isMockMode ? 1536 : 384;

this.dimensions = config.dimensions || defaultDimensions;
this.dimensions =
config.dimensions ||
(process.env.EMBEDDING_DIMENSIONS
? parseInt(process.env.EMBEDDING_DIMENSIONS, 10)
: defaultDimensions);
this.modelName = config.model || (isMockMode ? 'text-embedding-3-small-mock' : modelName);
this.modelVersion = config.version?.toString() || modelVersion;
}
Expand Down
106 changes: 82 additions & 24 deletions src/embeddings/EmbeddingServiceFactory.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { EmbeddingService } from './EmbeddingService.js';
import { DefaultEmbeddingService } from './DefaultEmbeddingService.js';
import { GoogleEmbeddingService } from './GoogleEmbeddingService.js';
import { OpenAIEmbeddingService } from './OpenAIEmbeddingService.js';
import { logger } from '../utils/logger.js';

Expand All @@ -11,6 +12,7 @@ export interface EmbeddingServiceConfig {
model?: string;
dimensions?: number;
apiKey?: string;
apiEndpoint?: string;
[key: string]: unknown;
}

Expand Down Expand Up @@ -103,42 +105,84 @@ export class EmbeddingServiceFactory {
logger.debug('EmbeddingServiceFactory: Creating service from environment variables', {
mockEmbeddings: useMockEmbeddings,
openaiKeyPresent: !!process.env.OPENAI_API_KEY,
embeddingModel: process.env.OPENAI_EMBEDDING_MODEL || 'default',
googleKeyPresent: !!process.env.GOOGLE_API_KEY,
embeddingModel: process.env.EMBEDDING_MODEL || 'default',
embeddingEndpoint: process.env.EMBEDDING_ENDPOINT,
});

if (useMockEmbeddings) {
logger.info('EmbeddingServiceFactory: Using mock embeddings for testing');
return new DefaultEmbeddingService();
}

const embeddingModel = process.env.EMBEDDING_MODEL;
const embeddingDimensions = process.env.EMBEDDING_DIMENSIONS
? parseInt(process.env.EMBEDDING_DIMENSIONS, 10)
: undefined;
const embeddingEndpoint = process.env.EMBEDDING_ENDPOINT;
const openaiApiKey = process.env.OPENAI_API_KEY;
const embeddingModel = process.env.OPENAI_EMBEDDING_MODEL || 'text-embedding-3-small';
const googleApiKey = process.env.GOOGLE_API_KEY;

if (openaiApiKey) {
try {
logger.debug('EmbeddingServiceFactory: Creating OpenAI embedding service', {
model: embeddingModel,
});
const service = new OpenAIEmbeddingService({
apiKey: openaiApiKey,
model: embeddingModel,
});
logger.info('EmbeddingServiceFactory: OpenAI embedding service created successfully', {
model: service.getModelInfo().name,
dimensions: service.getModelInfo().dimensions,
});
return service;
} catch (error) {
logger.error('EmbeddingServiceFactory: Failed to create OpenAI service', error);
logger.info('EmbeddingServiceFactory: Falling back to default embedding service');
// Fallback to default if OpenAI service creation fails
return new DefaultEmbeddingService();
let provider: 'google' | 'openai' | undefined;

// Determine provider based on model name first
if (embeddingModel) {
if (embeddingModel.startsWith('models/') || embeddingModel.includes('embedding-00')) {
provider = 'google';
} else if (embeddingModel.startsWith('text-embedding-')) {
provider = 'openai';
}
}

// If provider not determined by model, use API keys as a fallback
if (!provider) {
if (openaiApiKey) {
provider = 'openai';
} else if (googleApiKey) {
provider = 'google';
}
}

logger.debug(`EmbeddingServiceFactory: Selected provider "${provider || 'none'}"`);

if (provider === 'google') {
if (!googleApiKey) {
logger.error('Google provider selected, but GOOGLE_API_KEY is missing.');
} else {
try {
logger.debug('EmbeddingServiceFactory: Creating Google embedding service.');
return new GoogleEmbeddingService({
apiKey: googleApiKey,
model: embeddingModel,
dimensions: embeddingDimensions,
apiEndpoint: embeddingEndpoint,
});
} catch (error) {
logger.error('EmbeddingServiceFactory: Failed to create Google service', error);
}
}
}

if (provider === 'openai') {
if (!openaiApiKey) {
logger.error('OpenAI provider selected, but OPENAI_API_KEY is missing.');
} else {
try {
logger.debug('EmbeddingServiceFactory: Creating OpenAI embedding service.');
return new OpenAIEmbeddingService({
apiKey: openaiApiKey,
model: embeddingModel || 'text-embedding-3-small',
dimensions: embeddingDimensions,
apiEndpoint: embeddingEndpoint,
});
} catch (error) {
logger.error('EmbeddingServiceFactory: Failed to create OpenAI service', error);
}
}
}

// No OpenAI API key, using default embedding service
logger.info(
'EmbeddingServiceFactory: No OpenAI API key found, using default embedding service'
'EmbeddingServiceFactory: No provider could be successfully initialized. Falling back to default service.'
);
return new DefaultEmbeddingService();
}
Expand All @@ -154,12 +198,14 @@ export class EmbeddingServiceFactory {
static createOpenAIService(
apiKey: string,
model?: string,
dimensions?: number
dimensions?: number,
apiEndpoint?: string
): EmbeddingService {
return new OpenAIEmbeddingService({
apiKey,
model,
dimensions,
apiEndpoint,
});
}

Expand Down Expand Up @@ -188,5 +234,17 @@ EmbeddingServiceFactory.registerProvider('openai', (config = {}) => {
apiKey: config.apiKey,
model: config.model,
dimensions: config.dimensions,
apiEndpoint: config.apiEndpoint,
});
});

EmbeddingServiceFactory.registerProvider('google', (config = {}) => {
if (!config.apiKey) {
throw new Error('API key is required for Google embedding service');
}
return new GoogleEmbeddingService({
apiKey: config.apiKey,
model: config.model,
dimensions: config.dimensions,
});
});
Loading