# Directory Structure

```
├── .github
│   └── workflows
│       ├── publish.yml
│       └── release.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── smithery.yaml
├── src
│   └── index.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Dependencies
node_modules/

# TypeScript build output
dist/
build/

# Environment variables
.env

# Logs
logs/
*.log
npm-debug.log*

# IDE specific files
.idea/
*.swp
*.swo

# OS specific files
.DS_Store
Thumbs.db

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# MCP SQLite Server

[![smithery badge](https://smithery.ai/badge/mcp-server-sqlite-npx)](https://smithery.ai/server/mcp-server-sqlite-npx) [![MseeP.ai Security Assessment Badge](https://mseep.net/mseep-audited.png)](https://mseep.ai/app/johnnyoshika-mcp-server-sqlite-npx)

A Node.js implementation of the Model Context Protocol SQLite server, based on the [official Python reference](https://github.com/modelcontextprotocol/servers/tree/main/src/sqlite). This version provides an npx-based alternative for environments where Python's UVX runner is not available, such as [LibreChat](https://github.com/danny-avila/LibreChat/issues/4876#issuecomment-2561363955).

## Use with Claude Desktop

### Installing Manually

Add the following to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "sqlite": {
      "command": "/absolute/path/to/npx",
      "args": [
        "-y",
        "mcp-server-sqlite-npx",
        "/absolute/path/to/database.db"
      ],
      "env": {
        "PATH": "/absolute/path/to/executables",
        "NODE_PATH": "/absolute/path/to/node_modules"
      }
    }
  }
}
```

Full example when using nvm on macoS:

`~/Library/Application Support/Claude/claude_desktop_config.json`

```json
{
  "mcpServers": {
    "sqlite": {
      "command": "/Users/{username}/.nvm/versions/node/v22.12.0/bin/npx",
      "args": [
        "-y",
        "mcp-server-sqlite-npx",
        "/Users/{username}/projects/database.db"
      ],
      "env": {
        "PATH": "/Users/{username}/.nvm/versions/node/v22.12.0/bin:/usr/local/bin:/usr/bin:/bin",
        "NODE_PATH": "/Users/{username}/.nvm/versions/node/v22.12.0/lib/node_modules"
      }
    }
  }
}
```

Full example when using nvm on Windows:

`%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "sqlite": {
      "command": "C:\\Program Files\\nodejs\\npx.cmd",
      "args": [
        "-y",
        "mcp-server-sqlite-npx",
        "C:\\Users\\{username}\\projects\\database.db"
      ],
      "env": {
        "PATH": "C:\\Program Files\\nodejs;%PATH%",
        "NODE_PATH": "C:\\Program Files\\nodejs\\node_modules"
      }
    }
  }
}
```

### Installing via Smithery

To install MCP SQLite Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/mcp-server-sqlite-npx):

```bash
npx -y @smithery/cli install mcp-server-sqlite-npx --client claude
```

## Development

1. Install dependencies:

```bash
npm ci
```

2. Build the TypeScript code:

```bash
npm run build
```

### Testing with MCP Inspector

You can test the server using the [MCP Inspector tool](https://modelcontextprotocol.io/docs/tools/inspector):

```bash
npx @modelcontextprotocol/inspector node dist/index.js /absolute/path/to/database.db
```

`Connect` and go to `Tools` to start using the server.

### Testing with Claude Desktop

Add the following to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "sqlite": {
      "command": "/absolute/path/to/node",
      "args": [
        "/absolute/path/to/dist/index.js",
        "/absolute/path/to/database.db"
      ]
    }
  }
}
```

Examples:

- `/absolute/path/to/node`: `/Users/{username}/.nvm/versions/node/v20.18.1/bin/node`
- `/absolute/path/to/index.js`: `/Users/{username}/projects/mcp-server-sqlite-npx/dist/index.js`
- `/absolute/path/to/database.db`: `/Users/{username}/projects/database.db`

### Publish

- Bump version in package.json
- `npm install`
- Commit with message: `Release {version, e.g. 0.1.6}`

```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "outDir": "./dist",
    "rootDir": "./src",
  },
  "include": [
    "./**/*.ts"
  ],
  "exclude": ["node_modules"]
}
```

--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------

```yaml
# https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages

name: Publish NPM Package

on:
  release:
    types: [created]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 20
          registry-url: https://registry.npmjs.org/
      - run: npm ci
      - run: npm run build
      - run: npm publish --access public
        env:
          NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}

```

--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------

```yaml
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml

startCommand:
  type: stdio
  configSchema:
    # JSON Schema defining the configuration options for the MCP.
    type: object
    required:
      - databasePath
    properties:
      databasePath:
        type: string
        description: The absolute path to the SQLite database file.
  commandFunction:
    # A function that produces the CLI command to start the MCP on stdio.
    |-
    (config) => ({command:'node',args:['dist/index.js', config.databasePath]})
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "mcp-server-sqlite-npx",
  "version": "0.8.0",
  "type": "module",
  "main": "dist/index.js",
  "bin": {
    "mcp-server-sqlite-npx": "./dist/index.js"
  },
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "type-check": "tsc --noEmit",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "devDependencies": {
    "@types/node": "^22.10.2",
    "typescript": "^5.7.2"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.20.2",
    "sqlite3": "^5.1.7",
    "zod": "^3.25.76"
  }
}

```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
# Use Node.js 22.12-alpine as the base image
FROM node:22.12-alpine AS builder

# Create and change to the app directory
WORKDIR /app

# Copy package.json and package-lock.json
COPY package.json package-lock.json ./

# Install dependencies
RUN --mount=type=cache,target=/root/.npm npm ci

# Copy the source code
COPY src ./src
COPY tsconfig.json ./

# Build the TypeScript source
RUN npm run build

# Create a new release image
FROM node:22-alpine AS release

# Set the working directory
WORKDIR /app

# Copy the built application and the necessary files
COPY --from=builder /app/dist /app/dist
COPY --from=builder /app/package.json /app/package-lock.json ./

# Set the environment variable for production
ENV NODE_ENV=production

# Install only production dependencies
RUN npm ci --ignore-scripts --omit=dev

# Set the command to run the server
ENTRYPOINT ["node", "dist/index.js"]
```

--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------

```yaml
name: Create GitHub Release

on:
  push:
    branches:
      - main
    paths:
      - '**/*.yml'
      - 'src/**'
      - 'package.json'

jobs:
  version_check:
    runs-on: ubuntu-latest
    outputs:
      # https://github.community/t/sharing-a-variable-between-jobs/16967/14
      changed: ${{steps.check.outputs.changed}}
      version: ${{steps.check.outputs.version}}
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 20
      - name: Check if version changed in package.json
        id: check
        uses: EndBug/version-check@v2 # https://github.com/marketplace/actions/version-check
        with:
          diff-search: true
          file-name: ./package.json

  create_release:
    needs: version_check
    if: ${{needs.version_check.outputs.changed == 'true'}}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      - name: Create Release
        # https://github.com/actions/create-release
        id: create_release
        uses: actions/create-release@v1
        env:
          # Must use personal access token and not secrets.GITHUB_TOKEN to trigger on.release.created event, which publish.yml watches for
          # https://github.community/t/github-action-trigger-on-release-not-working-if-releases-was-created-by-automation/16559
          GITHUB_TOKEN: ${{secrets.PERSONAL_GITHUB_TOKEN}}
        with:
          tag_name: v${{needs.version_check.outputs.version}}
          release_name: Release ${{needs.version_check.outputs.version}}
          body: |
            mcp-server-sqlite-npx
          draft: false
          prerelease: false

```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import sqlite3 from "sqlite3";
import { z } from "zod";
import path from "path";

// Command line argument parsing
const args = process.argv.slice(2);
if (args.length !== 1) {
  console.error("Usage: mcp-server-sqlite-npx <database-path>");
  process.exit(1);
}

const dbPath = path.resolve(args[0]);

/**
 * Wrapper for sqlite3.Database that bridges CommonJS and ESM modules.
 * This abstraction is necessary because:
 * 1. sqlite3 is a CommonJS module while we're using ESM (type: "module")
 * 2. The module interop requires careful handling of the Database import
 * 3. We need to promisify the callback-based API to work better with async/await
 */
class DatabaseWrapper {
  private readonly db: sqlite3.Database;

  constructor(filename: string) {
    this.db = new sqlite3.Database(filename);
  }

  query(sql: string, params: any[] = []): Promise<any[]> {
    return new Promise((resolve, reject) => {
      this.db.all(sql, params, (err: Error | null, rows: any[]) => {
        if (err) reject(err);
        else resolve(rows);
      });
    });
  }

  execute(
    sql: string,
    params: any[] = []
  ): Promise<
    {
      affectedRows: number;
    }[]
  > {
    return new Promise((resolve, reject) => {
      this.db.run(
        sql,
        params,
        function (this: sqlite3.RunResult, err: Error | null) {
          if (err) reject(err);
          else resolve([{ affectedRows: this.changes }]);
        }
      );
    });
  }
}

class SqliteDatabase {
  private readonly db: DatabaseWrapper;

  constructor(dbPath: string) {
    this.db = new DatabaseWrapper(dbPath);
  }

  private async query<T>(sql: string, params: any[] = []): Promise<T[]> {
    return this.db.query(sql, params);
  }

  async listTables(): Promise<any[]> {
    return this.query("SELECT name FROM sqlite_master WHERE type='table'");
  }

  async describeTable(tableName: string): Promise<any[]> {
    return this.query(`PRAGMA table_info(${tableName})`);
  }

  async executeReadQuery(query: string): Promise<any[]> {
    if (!query.trim().toUpperCase().startsWith("SELECT")) {
      throw new Error("Only SELECT queries are allowed for read_query");
    }
    return this.query(query);
  }

  async executeWriteQuery(query: string): Promise<any[]> {
    if (query.trim().toUpperCase().startsWith("SELECT")) {
      throw new Error("SELECT queries are not allowed for write_query");
    }
    return this.query(query);
  }

  async createTable(query: string): Promise<any[]> {
    if (!query.trim().toUpperCase().startsWith("CREATE TABLE")) {
      throw new Error("Only CREATE TABLE statements are allowed");
    }
    return this.query(query);
  }
}

const db = new SqliteDatabase(dbPath);

async function withErrorHandling<T>(fn: () => Promise<T>) {
  try {
    const result = await fn();
    return {
      content: [
        {
          type: "text" as const,
          text: JSON.stringify(result, null, 2),
        },
      ],
    };
  } catch (error) {
    return {
      content: [
        {
          type: "text" as const,
          text: `Error: ${
            error instanceof Error ? error.message : String(error)
          }`,
        },
      ],
      isError: true,
    };
  }
}

// Server setup
const server = new McpServer(
  {
    name: "sqlite-manager",
    version: "0.8.0",
  },
  {
    capabilities: {
      tools: {},
    },
  }
);


server.tool(
  "read_query",
  "Execute a SELECT query on the SQLite database",
  {
    query: z.string().describe("SELECT SQL query to execute"),
  },
  async ({ query }) => withErrorHandling(() => db.executeReadQuery(query))
);

server.tool(
  "write_query",
  "Execute an INSERT, UPDATE, or DELETE query on the SQLite database",
  {
    query: z
      .string()
      .describe("INSERT, UPDATE, or DELETE SQL query to execute"),
  },
  async ({ query }) => withErrorHandling(() => db.executeWriteQuery(query))
);

server.tool(
  "create_table",
  "Create a new table in the SQLite database",
  {
    query: z.string().describe("CREATE TABLE SQL statement"),
  },
  async ({ query }) => withErrorHandling(() => db.createTable(query))
);

server.tool(
  "list_tables",
  "List all tables in the SQLite database",
  {},
  async () => withErrorHandling(() => db.listTables())
);

server.tool(
  "describe_table",
  "Get the schema information for a specific table",
  {
    table_name: z.string().describe("Name of the table to describe"),
  },
  async ({ table_name }) =>
    withErrorHandling(() => db.describeTable(table_name))
);

// Start server
async function runServer() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  // Use console.error to show error output.
  // console.log results in JSon exception.
  console.error("SQLite MCP Server running on stdio");
  console.error("Database path:", dbPath);
}

runServer().catch((error) => {
  console.error("Fatal error running server:", error);
  process.exit(1);
});

```

