#
tokens: 5154/50000 9/9 files
lines: on (toggle) GitHub
raw markdown copy reset
# 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:
--------------------------------------------------------------------------------

```
 1 | # Dependencies
 2 | node_modules/
 3 | 
 4 | # TypeScript build output
 5 | dist/
 6 | build/
 7 | 
 8 | # Environment variables
 9 | .env
10 | 
11 | # Logs
12 | logs/
13 | *.log
14 | npm-debug.log*
15 | 
16 | # IDE specific files
17 | .idea/
18 | *.swp
19 | *.swo
20 | 
21 | # OS specific files
22 | .DS_Store
23 | Thumbs.db
24 | 
```

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

```markdown
  1 | # MCP SQLite Server
  2 | 
  3 | [![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)
  4 | 
  5 | 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).
  6 | 
  7 | ## Use with Claude Desktop
  8 | 
  9 | ### Installing Manually
 10 | 
 11 | Add the following to `claude_desktop_config.json`:
 12 | 
 13 | ```json
 14 | {
 15 |   "mcpServers": {
 16 |     "sqlite": {
 17 |       "command": "/absolute/path/to/npx",
 18 |       "args": [
 19 |         "-y",
 20 |         "mcp-server-sqlite-npx",
 21 |         "/absolute/path/to/database.db"
 22 |       ],
 23 |       "env": {
 24 |         "PATH": "/absolute/path/to/executables",
 25 |         "NODE_PATH": "/absolute/path/to/node_modules"
 26 |       }
 27 |     }
 28 |   }
 29 | }
 30 | ```
 31 | 
 32 | Full example when using nvm on macoS:
 33 | 
 34 | `~/Library/Application Support/Claude/claude_desktop_config.json`
 35 | 
 36 | ```json
 37 | {
 38 |   "mcpServers": {
 39 |     "sqlite": {
 40 |       "command": "/Users/{username}/.nvm/versions/node/v22.12.0/bin/npx",
 41 |       "args": [
 42 |         "-y",
 43 |         "mcp-server-sqlite-npx",
 44 |         "/Users/{username}/projects/database.db"
 45 |       ],
 46 |       "env": {
 47 |         "PATH": "/Users/{username}/.nvm/versions/node/v22.12.0/bin:/usr/local/bin:/usr/bin:/bin",
 48 |         "NODE_PATH": "/Users/{username}/.nvm/versions/node/v22.12.0/lib/node_modules"
 49 |       }
 50 |     }
 51 |   }
 52 | }
 53 | ```
 54 | 
 55 | Full example when using nvm on Windows:
 56 | 
 57 | `%APPDATA%\Claude\claude_desktop_config.json`
 58 | 
 59 | ```json
 60 | {
 61 |   "mcpServers": {
 62 |     "sqlite": {
 63 |       "command": "C:\\Program Files\\nodejs\\npx.cmd",
 64 |       "args": [
 65 |         "-y",
 66 |         "mcp-server-sqlite-npx",
 67 |         "C:\\Users\\{username}\\projects\\database.db"
 68 |       ],
 69 |       "env": {
 70 |         "PATH": "C:\\Program Files\\nodejs;%PATH%",
 71 |         "NODE_PATH": "C:\\Program Files\\nodejs\\node_modules"
 72 |       }
 73 |     }
 74 |   }
 75 | }
 76 | ```
 77 | 
 78 | ### Installing via Smithery
 79 | 
 80 | To install MCP SQLite Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/mcp-server-sqlite-npx):
 81 | 
 82 | ```bash
 83 | npx -y @smithery/cli install mcp-server-sqlite-npx --client claude
 84 | ```
 85 | 
 86 | ## Development
 87 | 
 88 | 1. Install dependencies:
 89 | 
 90 | ```bash
 91 | npm ci
 92 | ```
 93 | 
 94 | 2. Build the TypeScript code:
 95 | 
 96 | ```bash
 97 | npm run build
 98 | ```
 99 | 
100 | ### Testing with MCP Inspector
101 | 
102 | You can test the server using the [MCP Inspector tool](https://modelcontextprotocol.io/docs/tools/inspector):
103 | 
104 | ```bash
105 | npx @modelcontextprotocol/inspector node dist/index.js /absolute/path/to/database.db
106 | ```
107 | 
108 | `Connect` and go to `Tools` to start using the server.
109 | 
110 | ### Testing with Claude Desktop
111 | 
112 | Add the following to `claude_desktop_config.json`:
113 | 
114 | ```json
115 | {
116 |   "mcpServers": {
117 |     "sqlite": {
118 |       "command": "/absolute/path/to/node",
119 |       "args": [
120 |         "/absolute/path/to/dist/index.js",
121 |         "/absolute/path/to/database.db"
122 |       ]
123 |     }
124 |   }
125 | }
126 | ```
127 | 
128 | Examples:
129 | 
130 | - `/absolute/path/to/node`: `/Users/{username}/.nvm/versions/node/v20.18.1/bin/node`
131 | - `/absolute/path/to/index.js`: `/Users/{username}/projects/mcp-server-sqlite-npx/dist/index.js`
132 | - `/absolute/path/to/database.db`: `/Users/{username}/projects/database.db`
133 | 
134 | ### Publish
135 | 
136 | - Bump version in package.json
137 | - `npm install`
138 | - Commit with message: `Release {version, e.g. 0.1.6}`
139 | 
```

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

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2022",
 4 |     "module": "NodeNext",
 5 |     "moduleResolution": "NodeNext",
 6 |     "strict": true,
 7 |     "esModuleInterop": true,
 8 |     "skipLibCheck": true,
 9 |     "forceConsistentCasingInFileNames": true,
10 |     "resolveJsonModule": true,
11 |     "outDir": "./dist",
12 |     "rootDir": "./src",
13 |   },
14 |   "include": [
15 |     "./**/*.ts"
16 |   ],
17 |   "exclude": ["node_modules"]
18 | }
```

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

```yaml
 1 | # https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
 2 | 
 3 | name: Publish NPM Package
 4 | 
 5 | on:
 6 |   release:
 7 |     types: [created]
 8 | 
 9 | jobs:
10 |   publish:
11 |     runs-on: ubuntu-latest
12 |     steps:
13 |       - uses: actions/checkout@v3
14 |       - uses: actions/setup-node@v3
15 |         with:
16 |           node-version: 20
17 |           registry-url: https://registry.npmjs.org/
18 |       - run: npm ci
19 |       - run: npm run build
20 |       - run: npm publish --access public
21 |         env:
22 |           NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
23 | 
```

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

```yaml
 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
 2 | 
 3 | startCommand:
 4 |   type: stdio
 5 |   configSchema:
 6 |     # JSON Schema defining the configuration options for the MCP.
 7 |     type: object
 8 |     required:
 9 |       - databasePath
10 |     properties:
11 |       databasePath:
12 |         type: string
13 |         description: The absolute path to the SQLite database file.
14 |   commandFunction:
15 |     # A function that produces the CLI command to start the MCP on stdio.
16 |     |-
17 |     (config) => ({command:'node',args:['dist/index.js', config.databasePath]})
```

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

```json
 1 | {
 2 |   "name": "mcp-server-sqlite-npx",
 3 |   "version": "0.8.0",
 4 |   "type": "module",
 5 |   "main": "dist/index.js",
 6 |   "bin": {
 7 |     "mcp-server-sqlite-npx": "./dist/index.js"
 8 |   },
 9 |   "scripts": {
10 |     "build": "tsc",
11 |     "start": "node dist/index.js",
12 |     "type-check": "tsc --noEmit",
13 |     "test": "echo \"Error: no test specified\" && exit 1"
14 |   },
15 |   "keywords": [],
16 |   "author": "",
17 |   "license": "ISC",
18 |   "description": "",
19 |   "devDependencies": {
20 |     "@types/node": "^22.10.2",
21 |     "typescript": "^5.7.2"
22 |   },
23 |   "dependencies": {
24 |     "@modelcontextprotocol/sdk": "^1.20.2",
25 |     "sqlite3": "^5.1.7",
26 |     "zod": "^3.25.76"
27 |   }
28 | }
29 | 
```

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

```dockerfile
 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
 2 | # Use Node.js 22.12-alpine as the base image
 3 | FROM node:22.12-alpine AS builder
 4 | 
 5 | # Create and change to the app directory
 6 | WORKDIR /app
 7 | 
 8 | # Copy package.json and package-lock.json
 9 | COPY package.json package-lock.json ./
10 | 
11 | # Install dependencies
12 | RUN --mount=type=cache,target=/root/.npm npm ci
13 | 
14 | # Copy the source code
15 | COPY src ./src
16 | COPY tsconfig.json ./
17 | 
18 | # Build the TypeScript source
19 | RUN npm run build
20 | 
21 | # Create a new release image
22 | FROM node:22-alpine AS release
23 | 
24 | # Set the working directory
25 | WORKDIR /app
26 | 
27 | # Copy the built application and the necessary files
28 | COPY --from=builder /app/dist /app/dist
29 | COPY --from=builder /app/package.json /app/package-lock.json ./
30 | 
31 | # Set the environment variable for production
32 | ENV NODE_ENV=production
33 | 
34 | # Install only production dependencies
35 | RUN npm ci --ignore-scripts --omit=dev
36 | 
37 | # Set the command to run the server
38 | ENTRYPOINT ["node", "dist/index.js"]
```

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

```yaml
 1 | name: Create GitHub Release
 2 | 
 3 | on:
 4 |   push:
 5 |     branches:
 6 |       - main
 7 |     paths:
 8 |       - '**/*.yml'
 9 |       - 'src/**'
10 |       - 'package.json'
11 | 
12 | jobs:
13 |   version_check:
14 |     runs-on: ubuntu-latest
15 |     outputs:
16 |       # https://github.community/t/sharing-a-variable-between-jobs/16967/14
17 |       changed: ${{steps.check.outputs.changed}}
18 |       version: ${{steps.check.outputs.version}}
19 |     steps:
20 |       - uses: actions/checkout@v3
21 |       - uses: actions/setup-node@v3
22 |         with:
23 |           node-version: 20
24 |       - name: Check if version changed in package.json
25 |         id: check
26 |         uses: EndBug/version-check@v2 # https://github.com/marketplace/actions/version-check
27 |         with:
28 |           diff-search: true
29 |           file-name: ./package.json
30 | 
31 |   create_release:
32 |     needs: version_check
33 |     if: ${{needs.version_check.outputs.changed == 'true'}}
34 |     runs-on: ubuntu-latest
35 |     steps:
36 |       - name: Checkout code
37 |         uses: actions/checkout@v3
38 |       - name: Create Release
39 |         # https://github.com/actions/create-release
40 |         id: create_release
41 |         uses: actions/create-release@v1
42 |         env:
43 |           # Must use personal access token and not secrets.GITHUB_TOKEN to trigger on.release.created event, which publish.yml watches for
44 |           # https://github.community/t/github-action-trigger-on-release-not-working-if-releases-was-created-by-automation/16559
45 |           GITHUB_TOKEN: ${{secrets.PERSONAL_GITHUB_TOKEN}}
46 |         with:
47 |           tag_name: v${{needs.version_check.outputs.version}}
48 |           release_name: Release ${{needs.version_check.outputs.version}}
49 |           body: |
50 |             mcp-server-sqlite-npx
51 |           draft: false
52 |           prerelease: false
53 | 
```

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

```typescript
  1 | #!/usr/bin/env node
  2 | 
  3 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
  4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
  5 | import sqlite3 from "sqlite3";
  6 | import { z } from "zod";
  7 | import path from "path";
  8 | 
  9 | // Command line argument parsing
 10 | const args = process.argv.slice(2);
 11 | if (args.length !== 1) {
 12 |   console.error("Usage: mcp-server-sqlite-npx <database-path>");
 13 |   process.exit(1);
 14 | }
 15 | 
 16 | const dbPath = path.resolve(args[0]);
 17 | 
 18 | /**
 19 |  * Wrapper for sqlite3.Database that bridges CommonJS and ESM modules.
 20 |  * This abstraction is necessary because:
 21 |  * 1. sqlite3 is a CommonJS module while we're using ESM (type: "module")
 22 |  * 2. The module interop requires careful handling of the Database import
 23 |  * 3. We need to promisify the callback-based API to work better with async/await
 24 |  */
 25 | class DatabaseWrapper {
 26 |   private readonly db: sqlite3.Database;
 27 | 
 28 |   constructor(filename: string) {
 29 |     this.db = new sqlite3.Database(filename);
 30 |   }
 31 | 
 32 |   query(sql: string, params: any[] = []): Promise<any[]> {
 33 |     return new Promise((resolve, reject) => {
 34 |       this.db.all(sql, params, (err: Error | null, rows: any[]) => {
 35 |         if (err) reject(err);
 36 |         else resolve(rows);
 37 |       });
 38 |     });
 39 |   }
 40 | 
 41 |   execute(
 42 |     sql: string,
 43 |     params: any[] = []
 44 |   ): Promise<
 45 |     {
 46 |       affectedRows: number;
 47 |     }[]
 48 |   > {
 49 |     return new Promise((resolve, reject) => {
 50 |       this.db.run(
 51 |         sql,
 52 |         params,
 53 |         function (this: sqlite3.RunResult, err: Error | null) {
 54 |           if (err) reject(err);
 55 |           else resolve([{ affectedRows: this.changes }]);
 56 |         }
 57 |       );
 58 |     });
 59 |   }
 60 | }
 61 | 
 62 | class SqliteDatabase {
 63 |   private readonly db: DatabaseWrapper;
 64 | 
 65 |   constructor(dbPath: string) {
 66 |     this.db = new DatabaseWrapper(dbPath);
 67 |   }
 68 | 
 69 |   private async query<T>(sql: string, params: any[] = []): Promise<T[]> {
 70 |     return this.db.query(sql, params);
 71 |   }
 72 | 
 73 |   async listTables(): Promise<any[]> {
 74 |     return this.query("SELECT name FROM sqlite_master WHERE type='table'");
 75 |   }
 76 | 
 77 |   async describeTable(tableName: string): Promise<any[]> {
 78 |     return this.query(`PRAGMA table_info(${tableName})`);
 79 |   }
 80 | 
 81 |   async executeReadQuery(query: string): Promise<any[]> {
 82 |     if (!query.trim().toUpperCase().startsWith("SELECT")) {
 83 |       throw new Error("Only SELECT queries are allowed for read_query");
 84 |     }
 85 |     return this.query(query);
 86 |   }
 87 | 
 88 |   async executeWriteQuery(query: string): Promise<any[]> {
 89 |     if (query.trim().toUpperCase().startsWith("SELECT")) {
 90 |       throw new Error("SELECT queries are not allowed for write_query");
 91 |     }
 92 |     return this.query(query);
 93 |   }
 94 | 
 95 |   async createTable(query: string): Promise<any[]> {
 96 |     if (!query.trim().toUpperCase().startsWith("CREATE TABLE")) {
 97 |       throw new Error("Only CREATE TABLE statements are allowed");
 98 |     }
 99 |     return this.query(query);
100 |   }
101 | }
102 | 
103 | const db = new SqliteDatabase(dbPath);
104 | 
105 | async function withErrorHandling<T>(fn: () => Promise<T>) {
106 |   try {
107 |     const result = await fn();
108 |     return {
109 |       content: [
110 |         {
111 |           type: "text" as const,
112 |           text: JSON.stringify(result, null, 2),
113 |         },
114 |       ],
115 |     };
116 |   } catch (error) {
117 |     return {
118 |       content: [
119 |         {
120 |           type: "text" as const,
121 |           text: `Error: ${
122 |             error instanceof Error ? error.message : String(error)
123 |           }`,
124 |         },
125 |       ],
126 |       isError: true,
127 |     };
128 |   }
129 | }
130 | 
131 | // Server setup
132 | const server = new McpServer(
133 |   {
134 |     name: "sqlite-manager",
135 |     version: "0.8.0",
136 |   },
137 |   {
138 |     capabilities: {
139 |       tools: {},
140 |     },
141 |   }
142 | );
143 | 
144 | 
145 | server.tool(
146 |   "read_query",
147 |   "Execute a SELECT query on the SQLite database",
148 |   {
149 |     query: z.string().describe("SELECT SQL query to execute"),
150 |   },
151 |   async ({ query }) => withErrorHandling(() => db.executeReadQuery(query))
152 | );
153 | 
154 | server.tool(
155 |   "write_query",
156 |   "Execute an INSERT, UPDATE, or DELETE query on the SQLite database",
157 |   {
158 |     query: z
159 |       .string()
160 |       .describe("INSERT, UPDATE, or DELETE SQL query to execute"),
161 |   },
162 |   async ({ query }) => withErrorHandling(() => db.executeWriteQuery(query))
163 | );
164 | 
165 | server.tool(
166 |   "create_table",
167 |   "Create a new table in the SQLite database",
168 |   {
169 |     query: z.string().describe("CREATE TABLE SQL statement"),
170 |   },
171 |   async ({ query }) => withErrorHandling(() => db.createTable(query))
172 | );
173 | 
174 | server.tool(
175 |   "list_tables",
176 |   "List all tables in the SQLite database",
177 |   {},
178 |   async () => withErrorHandling(() => db.listTables())
179 | );
180 | 
181 | server.tool(
182 |   "describe_table",
183 |   "Get the schema information for a specific table",
184 |   {
185 |     table_name: z.string().describe("Name of the table to describe"),
186 |   },
187 |   async ({ table_name }) =>
188 |     withErrorHandling(() => db.describeTable(table_name))
189 | );
190 | 
191 | // Start server
192 | async function runServer() {
193 |   const transport = new StdioServerTransport();
194 |   await server.connect(transport);
195 |   // Use console.error to show error output.
196 |   // console.log results in JSon exception.
197 |   console.error("SQLite MCP Server running on stdio");
198 |   console.error("Database path:", dbPath);
199 | }
200 | 
201 | runServer().catch((error) => {
202 |   console.error("Fatal error running server:", error);
203 |   process.exit(1);
204 | });
205 | 
```