react-native-sqlite-mcp 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,23 +1,70 @@
1
- # 🚀 Universal React Native SQLite MCP
1
+ <div align="center">
2
2
 
3
- **TL;DR:** A Model Context Protocol (MCP) server that gives your favorite LLM (Claude, Cursor, Antigravity, etc.) X-ray vision into your local React Native SQLite databases.
3
+ # react-native-sqlite-mcp
4
4
 
5
- No more flying blind. No more manually exporting `.db` files from emulators to figure out why your app is broken. Just ask your AI: *"Hey, what does the `users` table look like on my Android emulator?"* and watch the magic happen.
5
+ Expose your React Native SQLite database to MCP-compatible tooling with emulator support.
6
+
7
+ <br />
8
+
9
+ ![npm version](https://img.shields.io/npm/v/react-native-sqlite-mcp?style=for-the-badge)
10
+ ![license](https://img.shields.io/github/license/MisterMur/react-native-sqlite-mcp?style=for-the-badge)
11
+ ![node](https://img.shields.io/badge/node-%3E%3D18-43853D?style=for-the-badge&logo=node.js)
12
+ ![react-native](https://img.shields.io/badge/React%20Native-supported-61DAFB?style=for-the-badge&logo=react)
13
+
14
+ <br />
15
+
16
+ ![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178C6?style=for-the-badge&logo=typescript)
17
+ ![SQLite](https://img.shields.io/badge/SQLite-3-003B57?style=for-the-badge&logo=sqlite)
18
+ ![MCP](https://img.shields.io/badge/MCP-compatible-black?style=for-the-badge)
19
+
20
+ </div>
6
21
 
7
22
  ---
8
23
 
9
- ## 🤔 Why did I build this?
24
+ ## Overview
25
+
26
+ SQLite MCP servers exist, but they all assume your database is a file sitting on your desktop or server. They open a path, read the file, done.
10
27
 
11
- Honestly? I was tired of jumping through hoops to inspect local databases while building React Native apps. Extracting SQLite files from an iOS Simulator or bypassing root permissions on an Android Emulator just to run a `SELECT *` was ruining my flow state.
28
+ That doesn't work for mobile. On iOS Simulator, SQLite databases are buried deep inside sandboxed app containers. On Android Emulator, they're behind `run-as` permission boundaries that require `adb` to access. No existing SQLite MCP server handles either of these.
12
29
 
13
- I wanted my AI assistant to just *know* what my database looked like and query it in real-time. So I built this bridge. It's mobile development less painful.
30
+ I built this because I was tired of manually extracting `.db` files from emulators just to figure out why my app's local state was wrong.
14
31
 
32
+ `react-native-sqlite-mcp` is a SQLite MCP server purpose-built for mobile emulators. It auto-discovers databases inside iOS Simulator and Android Emulator sandboxes, pulls them transparently, and exposes schema + query access to any MCP-compatible tool.
15
33
 
16
- ## 📦 Quick Start (The Magic Way)
34
+ ---
17
35
 
18
- You don't even need to clone this repo. The easiest way to get rolling is via `npx`.
36
+ ## Why This Exists
19
37
 
20
- Toss this bad boy into your `mcp.json` (or your Claude/Cursor/agent settings):
38
+ - Existing SQLite MCP servers can't reach databases inside iOS Simulator or Android Emulator sandboxes
39
+ - Mobile MCP tooling focuses on UI automation, not the data layer
40
+ - SQLite-backed state is critical in local-first React Native apps
41
+ - There was no MCP bridge for inspecting emulator databases directly from your AI tooling
42
+
43
+ This project fills that gap.
44
+
45
+ ---
46
+
47
+ ## How It Works
48
+
49
+ ```mermaid
50
+ graph LR
51
+ subgraph Emulator
52
+ RN[React Native App] --> SQLite[(SQLite DB)]
53
+ end
54
+ SQLite -- "simctl / adb pull" --> MCP[MCP Server]
55
+ MCP -- "JSON-RPC stdio" --> AI[Claude / Cursor / Antigravity]
56
+ ```
57
+
58
+ 1. Auto-detects booted iOS Simulators (`xcrun simctl`) and Android Emulators (`adb`)
59
+ 2. Discovers SQLite databases inside sandboxed app containers
60
+ 3. Pulls database files transparently (iOS reads in-place, Android uses `run-as` + `adb pull`)
61
+ 4. Exposes schema and query access over the MCP stdio transport
62
+
63
+ ---
64
+
65
+ ## Quick Start
66
+
67
+ Add this to your MCP config (`mcp.json`, `.cursor/mcp.json`, or `.gemini/settings.json`):
21
68
 
22
69
  ```json
23
70
  {
@@ -26,7 +73,7 @@ Toss this bad boy into your `mcp.json` (or your Claude/Cursor/agent settings):
26
73
  "command": "npx",
27
74
  "args": ["-y", "react-native-sqlite-mcp"],
28
75
  "env": {
29
- "DB_NAME": "my_database.db",
76
+ "DB_NAME": "my_app.db",
30
77
  "ANDROID_BUNDLE_ID": "com.mycompany.myapp"
31
78
  }
32
79
  }
@@ -34,20 +81,87 @@ Toss this bad boy into your `mcp.json` (or your Claude/Cursor/agent settings):
34
81
  }
35
82
  ```
36
83
 
37
- Boom. You're connected. 🤝
84
+ Restart your editor. Your AI can now query your emulator databases directly.
85
+
86
+ ---
87
+
88
+ ## Environment Variables
89
+
90
+ | Variable | Required | Description |
91
+ |---|---|---|
92
+ | `DB_NAME` | No | Database filename to auto-sync on startup (e.g. `my_app.db`). Supports glob patterns (`*.db`). If omitted, auto-selects the first discovered database. |
93
+ | `ANDROID_BUNDLE_ID` | No | Android app package name (e.g. `com.mycompany.app`). If omitted, scans all third-party packages on the emulator. |
94
+ | `READ_ONLY` | No | Set to `true` to restrict `query_db` to SELECT, PRAGMA, and EXPLAIN statements only. Default: `false`. |
95
+ | `MCP_LOG_LEVEL` | No | Log verbosity: `debug`, `info`, `warn`, `error`. Default: `info`. Logs go to stderr only. |
96
+
97
+ ---
98
+
99
+ ## Tools
38
100
 
39
- ## 🛠️ Manual Installation (For the brave)
101
+ ### `list_databases`
40
102
 
41
- Prefer to tinker with the source code yourself? I respect it.
103
+ Scans for all SQLite databases on booted emulators/simulators.
104
+
105
+ | Argument | Type | Description |
106
+ |---|---|---|
107
+ | `platform` | string | Optional. `ios` or `android`. If omitted, scans both. |
108
+ | `bundleId` | string | Optional. Android-only app package name to narrow the search. |
109
+
110
+ ### `sync_database`
111
+
112
+ Pulls a fresh copy of the database from the emulator so all subsequent queries use the latest data.
113
+
114
+ | Argument | Type | Description |
115
+ |---|---|---|
116
+ | `dbName` | string | Optional. Database filename or glob pattern. Auto-selects if omitted. |
117
+ | `bundleId` | string | Optional. Android-only app package name. |
118
+ | `platform` | string | Optional. `ios` or `android`. |
119
+
120
+ ### `inspect_schema`
121
+
122
+ Returns all tables, columns, and `CREATE TABLE` statements for the synced database.
123
+
124
+ | Argument | Type | Description |
125
+ |---|---|---|
126
+ | `dbName` | string | Optional. Target a specific database. |
127
+ | `platform` | string | Optional. `ios` or `android`. |
128
+
129
+ ### `read_table_contents`
130
+
131
+ Returns rows from a table (`SELECT * FROM table LIMIT n`).
132
+
133
+ | Argument | Type | Description |
134
+ |---|---|---|
135
+ | `tableName` | string | **Required.** The table to read. |
136
+ | `limit` | number | Optional. Max rows to return. Default: `100`. |
137
+ | `dbName` | string | Optional. Target a specific database. |
138
+ | `platform` | string | Optional. `ios` or `android`. |
139
+
140
+ ### `query_db`
141
+
142
+ Executes a raw SQL query and returns the result set.
143
+
144
+ | Argument | Type | Description |
145
+ |---|---|---|
146
+ | `sql` | string | **Required.** The SQL statement to execute. |
147
+ | `params` | array | Optional. Bind parameters for `?` placeholders. |
148
+ | `dbName` | string | Optional. Target a specific database. |
149
+ | `platform` | string | Optional. `ios` or `android`. |
150
+
151
+ > When `READ_ONLY=true`, only `SELECT`, `PRAGMA`, and `EXPLAIN` statements are allowed.
152
+
153
+ ---
154
+
155
+ ## Manual Installation
42
156
 
43
157
  ```bash
44
- git clone https://github.com/your-username/react-native-sqlite-mcp.git
158
+ git clone https://github.com/MisterMur/react-native-sqlite-mcp.git
45
159
  cd react-native-sqlite-mcp
46
160
  npm install
47
161
  npm run build
48
162
  ```
49
163
 
50
- Then point your MCP client to your local build:
164
+ Point your MCP config at the local build:
51
165
 
52
166
  ```json
53
167
  {
@@ -56,49 +170,32 @@ Then point your MCP client to your local build:
56
170
  "command": "node",
57
171
  "args": ["/absolute/path/to/react-native-sqlite-mcp/dist/index.js"],
58
172
  "env": {
59
- "DB_NAME": "my_database.db",
60
- "ANDROID_BUNDLE_ID": "com.mycompany.myapp"
173
+ "DB_NAME": "my_app.db"
61
174
  }
62
175
  }
63
176
  }
64
177
  }
65
178
  ```
66
179
 
67
- ## 🎛️ Environment Variables (The Knobs)
68
-
69
- - `DB_NAME`: The filename of your database (e.g., `my_app.db`). You can also use a glob pattern (`*.db`) if you're feeling adventurous.
70
- - `ANDROID_BUNDLE_ID`: *(Android Only)* The application ID/package name of your app (e.g., `com.mycompany.app`).
71
- - **Pro-Tip:** If you leave this out, the MCP will go rogue and scan *all* third-party apps on your emulator for SQLite databases. Use with caution/glee.
72
-
73
- ## 🦸‍♂️ Features (What this bad boy can do)
74
-
75
- This MCP arms your AI with four super-powered tools:
76
-
77
- - 🕵️‍♂️ **`list_databases`**: Scours the device and returns a list of all available SQLite databases. Toss in `platform` ('ios' or 'android') to narrow the search.
78
- - 🔄 **`sync_database`**: Yanks a copy of a database from your active device into the MCP's working directory so the AI can inspect it to its heart's content. Leave the arguments blank, and it'll just grab the first default database it finds.
79
- - 🗺️ **`inspect_schema`**: The holy grail. Returns the `CREATE TABLE` and column info for your synced database. It literally gives the AI the map to your data.
80
- - 📖 **`read_table_contents`**: Dumps all rows from a specific table (capped at 100 rows so we don't blow up the context window).
81
- - 🤖 **`query_db`**: Lets the AI fire raw SQL queries right at the database and get the results back.
180
+ ---
82
181
 
83
- ## ⚙️ How it Actually Works (Under the hood)
182
+ ## Limitations
84
183
 
85
- 1. **Auto-Detect Platform**: It scans **both** iOS and Android environments simultaneously. It hunts down booted iOS Simulators using `simctl` and active Android Emulators using `adb`.
86
- 2. **Auto-Locate Database**:
87
- - **iOS:** We dive straight into the simulator's app sandbox. No root needed.
88
- - **Android:** We do a sneaky `adb exec-out run-as com.pkg.name cat ...` to copy the database file, along with its `-wal` and `-shm` sidekicks, completely bypassing the strict root permission boundaries on debug profiles.
89
- 3. **Platform Switching**: The server keeps one active database connection open. Want to switch from iOS to Android? The AI just calls `sync_database` for the other platform. Simple.
90
- 4. **Execution**: It wraps all this up nicely so the LLM can learn your schema and query live data without bothering you.
184
+ - Development environments only not designed for production
185
+ - iOS requires a booted Simulator with `xcrun simctl`
186
+ - Android requires a booted Emulator with `adb` and a debuggable app (debug build)
187
+ - Queries operate on a pulled snapshot, not a live connection
91
188
 
92
- ## 🤝 Contributing (Yes, please!)
189
+ ---
93
190
 
94
- Got an idea to make this objectively cooler? Found a bug where it accidentally queried your smart fridge?
191
+ ## Contributing
95
192
 
96
- I am **all in** on community contributions. Whether you're fixing a typo, optimizing the ADB scripts, or adding support for Windows Phone (please don't), I want your PRs.
193
+ Issues and pull requests are welcome.
97
194
 
98
- Check out the [CONTRIBUTING.md](./CONTRIBUTING.md) guide to see how we party.
195
+ If you're working in the local-first React Native space and hit edge cases, open an issue. This tool exists because that gap was real.
99
196
 
100
- ## 📜 License
197
+ ---
101
198
 
102
- This project is licensed under the **MIT License**.
199
+ ## License
103
200
 
104
- See the [LICENSE](./LICENSE) file for the legal jargon.
201
+ MIT
package/dist/db.js CHANGED
@@ -1,89 +1,52 @@
1
- import sqlite3 from "sqlite3";
1
+ import initSqlJs from "sql.js";
2
+ import fs from "fs";
2
3
  import { logger } from "./logger.js";
3
- /**
4
- * Lightweight sqlite3 wrapper with Promise-based API.
5
- */
6
- export class Database {
7
- db;
8
- constructor(filename) {
9
- this.db = new sqlite3.Database(filename);
10
- }
11
- all(sql, params = []) {
12
- return new Promise((resolve, reject) => {
13
- this.db.all(sql, params, (err, rows) => {
14
- if (err)
15
- reject(err);
16
- else
17
- resolve(rows);
18
- });
19
- });
20
- }
21
- get(sql, params = []) {
22
- return new Promise((resolve, reject) => {
23
- this.db.get(sql, params, (err, row) => {
24
- if (err)
25
- reject(err);
26
- else
27
- resolve(row);
28
- });
29
- });
30
- }
31
- close() {
32
- return new Promise((resolve, reject) => {
33
- this.db.close((err) => {
34
- if (err)
35
- reject(err);
36
- else
37
- resolve();
38
- });
39
- });
4
+ let SQL = null;
5
+ async function getSqlJs() {
6
+ if (!SQL) {
7
+ SQL = await initSqlJs();
8
+ logger.debug("sql.js WASM engine initialized");
40
9
  }
10
+ return SQL;
41
11
  }
42
- const CACHE_TTL_MS = 60_000; // Close idle connections after 60s
12
+ const CACHE_TTL_MS = 60_000;
43
13
  const connectionCache = new Map();
44
- function getCachedDb(dbPath) {
14
+ async function getCachedDb(dbPath) {
45
15
  const existing = connectionCache.get(dbPath);
46
16
  if (existing) {
47
- // Refresh the idle timer
48
17
  clearTimeout(existing.timer);
49
18
  existing.lastUsed = Date.now();
50
19
  existing.timer = setTimeout(() => evictConnection(dbPath), CACHE_TTL_MS);
51
20
  return existing.db;
52
21
  }
53
- // Open a new connection
54
- const db = new Database(dbPath);
22
+ const sqlJs = await getSqlJs();
23
+ const buffer = fs.readFileSync(dbPath);
24
+ const db = new sqlJs.Database(buffer);
55
25
  const timer = setTimeout(() => evictConnection(dbPath), CACHE_TTL_MS);
56
- connectionCache.set(dbPath, { db, lastUsed: Date.now(), timer });
26
+ connectionCache.set(dbPath, { db, dbPath, lastUsed: Date.now(), timer });
57
27
  logger.debug(`Opened DB connection: ${dbPath}`);
58
28
  return db;
59
29
  }
60
- async function evictConnection(dbPath) {
30
+ function evictConnection(dbPath) {
61
31
  const entry = connectionCache.get(dbPath);
62
32
  if (!entry)
63
33
  return;
64
34
  connectionCache.delete(dbPath);
65
35
  try {
66
- await entry.db.close();
36
+ entry.db.close();
67
37
  logger.debug(`Closed idle DB connection: ${dbPath}`);
68
38
  }
69
39
  catch (e) {
70
40
  logger.warn(`Error closing DB: ${dbPath}`, { error: String(e) });
71
41
  }
72
42
  }
73
- /**
74
- * Close all cached connections. Called during graceful shutdown.
75
- */
76
43
  export async function closeAllConnections() {
77
44
  const paths = [...connectionCache.keys()];
78
45
  for (const p of paths) {
79
- await evictConnection(p);
46
+ evictConnection(p);
80
47
  }
81
48
  logger.info(`Closed ${paths.length} cached DB connection(s)`);
82
49
  }
83
- // ---------------------------------------------------------------------------
84
- // Public query API
85
- // ---------------------------------------------------------------------------
86
- /** Query timeout — prevents stuck queries from blocking everything */
87
50
  const QUERY_TIMEOUT_MS = 30_000;
88
51
  function withTimeout(promise, ms, label) {
89
52
  return new Promise((resolve, reject) => {
@@ -95,26 +58,31 @@ function withTimeout(promise, ms, label) {
95
58
  .catch((err) => { clearTimeout(timer); reject(err); });
96
59
  });
97
60
  }
98
- /**
99
- * Executes a simple query on the database.
100
- */
61
+ function runQuery(db, sql, params = []) {
62
+ const stmt = db.prepare(sql);
63
+ if (params.length > 0)
64
+ stmt.bind(params);
65
+ const results = [];
66
+ while (stmt.step()) {
67
+ results.push(stmt.getAsObject());
68
+ }
69
+ stmt.free();
70
+ return results;
71
+ }
101
72
  export async function queryDb(dbPath, sql, params = []) {
102
- const db = getCachedDb(dbPath);
103
- return withTimeout(db.all(sql, params), QUERY_TIMEOUT_MS, sql.slice(0, 80));
73
+ const db = await getCachedDb(dbPath);
74
+ return withTimeout(Promise.resolve(runQuery(db, sql, params)), QUERY_TIMEOUT_MS, sql.slice(0, 80));
104
75
  }
105
- /**
106
- * Returns a detailed schema of all tables in the database.
107
- */
108
76
  export async function inspectSchema(dbPath) {
109
- const db = getCachedDb(dbPath);
110
- const tables = await withTimeout(db.all("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;"), QUERY_TIMEOUT_MS, "inspect_schema:tables");
77
+ const db = await getCachedDb(dbPath);
78
+ const tables = runQuery(db, "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;");
111
79
  const schemaInfo = {};
112
80
  for (const table of tables) {
113
- const columns = await db.all(`PRAGMA table_info("${table.name}");`);
114
- const createSql = await db.get(`SELECT sql FROM sqlite_master WHERE type='table' AND name=?`, [table.name]);
81
+ const columns = runQuery(db, `PRAGMA table_info("${table.name}");`);
82
+ const createSqlRows = runQuery(db, `SELECT sql FROM sqlite_master WHERE type='table' AND name=?`, [table.name]);
115
83
  schemaInfo[table.name] = {
116
84
  columns,
117
- createSql: createSql?.sql
85
+ createSql: createSqlRows[0]?.sql
118
86
  };
119
87
  }
120
88
  return schemaInfo;
package/dist/index.js CHANGED
@@ -5,6 +5,9 @@ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextpro
5
5
  import { listDatabases, syncDatabase } from "./locator.js";
6
6
  import { inspectSchema, queryDb, closeAllConnections } from "./db.js";
7
7
  import { logger } from "./logger.js";
8
+ import { createRequire } from "module";
9
+ const require = createRequire(import.meta.url);
10
+ const { version } = require("../package.json");
8
11
  // ---------------------------------------------------------------------------
9
12
  // Process-level guards — prevent silent crashes that cause EOF errors
10
13
  // ---------------------------------------------------------------------------
@@ -20,13 +23,17 @@ process.on("unhandledRejection", (reason) => {
20
23
  reason: String(reason),
21
24
  });
22
25
  });
26
+ // ---------------------------------------------------------------------------
27
+ // Config
28
+ // ---------------------------------------------------------------------------
29
+ const READ_ONLY = process.env.READ_ONLY === 'true' || process.env.READ_ONLY === '1';
23
30
  let activeDatabases = [];
24
31
  // ---------------------------------------------------------------------------
25
32
  // Server setup
26
33
  // ---------------------------------------------------------------------------
27
34
  const server = new Server({
28
35
  name: "react-native-sqlite-bridge",
29
- version: "1.0.0",
36
+ version,
30
37
  }, {
31
38
  capabilities: {
32
39
  tools: {},
@@ -258,6 +265,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
258
265
  if (!sql) {
259
266
  throw new Error("Missing required argument: sql");
260
267
  }
268
+ if (READ_ONLY) {
269
+ const normalized = sql.trim().toUpperCase();
270
+ if (!normalized.startsWith("SELECT") && !normalized.startsWith("PRAGMA") && !normalized.startsWith("EXPLAIN")) {
271
+ throw new Error("READ_ONLY mode is enabled. Only SELECT, PRAGMA, and EXPLAIN statements are allowed.");
272
+ }
273
+ }
261
274
  const results = await queryDb(activeDb.localPath, sql, params);
262
275
  return {
263
276
  content: [
package/dist/locator.js CHANGED
@@ -49,7 +49,7 @@ export async function listDatabases(bundleId, targetPlatform) {
49
49
  }
50
50
  return results;
51
51
  }
52
- // Discover packages to scan
52
+ // if we have a specific bundleId-use it otherwise hunt
53
53
  let packagesToScan = [];
54
54
  if (bundleId) {
55
55
  packagesToScan = [bundleId];
@@ -76,12 +76,12 @@ export async function listDatabases(bundleId, targetPlatform) {
76
76
  try {
77
77
  await shell(`adb shell run-as ${pkg} ls -d ${baseDir}`, { timeout: 3_000, label: `adb-ls-${pkg}` });
78
78
  let foundFiles = [];
79
- // Find .db / .sqlite / .sqlite3 files recursively
79
+ // find .db / .sqlite / .sqlite3 files recursively
80
80
  const findOut = await shell(`adb shell "run-as ${pkg} find ${baseDir} -type f \\( -name \\"*.db\\" -o -name \\"*.sqlite\\" -o -name \\"*.sqlite3\\" \\)"`, { timeout: 8_000, ignoreErrors: true, label: `adb-find-${pkg}` });
81
81
  if (findOut) {
82
82
  foundFiles.push(...findOut.split('\n').map(l => l.trim().replace(/\r/g, '')).filter(Boolean));
83
83
  }
84
- // Also check for extensionless files in /databases
84
+ // also check for extensionless files in /databases
85
85
  const lsOut = await shell(`adb shell run-as ${pkg} ls -1p ${baseDir}/databases`, { timeout: 3_000, ignoreErrors: true, label: `adb-ls-dbs-${pkg}` });
86
86
  if (lsOut) {
87
87
  const lsFiles = lsOut.split('\n')
@@ -120,12 +120,6 @@ export async function listDatabases(bundleId, targetPlatform) {
120
120
  }
121
121
  return results;
122
122
  }
123
- /**
124
- * Finds the DB file based on platform and a provided/detected filename.
125
- * If dbNameGlob is empty/undefined, it will auto-select the first discovered database.
126
- * Prioritizes iOS (simctl), falls back to Android (adb).
127
- * Returns the local file path (either the iOS original or a pulled Android copy).
128
- */
129
123
  export async function syncDatabase(dbNameGlob, bundleId, targetPlatform) {
130
124
  const locations = await listDatabases(bundleId, targetPlatform);
131
125
  if (locations.length === 0) {
@@ -176,7 +170,6 @@ export async function syncDatabase(dbNameGlob, bundleId, targetPlatform) {
176
170
  continue;
177
171
  }
178
172
  const [targetDbDir, targetPkg] = appDir.split("::");
179
- // Force-stop is best-effort (ensures WAL is flushed)
180
173
  await shell(`adb shell am force-stop ${targetPkg}`, { timeout: 5_000, ignoreErrors: true, label: `adb-force-stop-${targetPkg}` });
181
174
  const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "rn-sqlite-mcp-"));
182
175
  const safeLocalName = targetDbName.replace(/\//g, '_');
package/dist/logger.js CHANGED
@@ -1,7 +1,3 @@
1
- /**
2
- * Structured logging utility for MCP servers.
3
- * ALL output goes to stderr to avoid polluting the stdio JSON-RPC transport.
4
- */
5
1
  const LOG_LEVELS = {
6
2
  debug: 0,
7
3
  info: 1,
package/dist/shell.js CHANGED
@@ -1,8 +1,3 @@
1
- /**
2
- * Async shell execution wrapper.
3
- * Replaces all execSync usage to keep the Node.js event loop alive
4
- * so MCP stdio transport can continue processing heartbeats/messages.
5
- */
6
1
  import { exec as execCb } from "child_process";
7
2
  import { promisify } from "util";
8
3
  import { logger } from "./logger.js";
@@ -10,10 +5,6 @@ const execAsync = promisify(execCb);
10
5
  function sleep(ms) {
11
6
  return new Promise((resolve) => setTimeout(resolve, ms));
12
7
  }
13
- /**
14
- * Execute a shell command asynchronously with timeout and retry support.
15
- * This is the core replacement for `execSync` — it does NOT block the event loop.
16
- */
17
8
  export async function shell(command, options = {}) {
18
9
  const { timeout = 10_000, retries = 0, retryDelay = 1_000, ignoreErrors = false, label, } = options;
19
10
  const tag = label || command.slice(0, 60);
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "react-native-sqlite-mcp",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Universal React Native SQLite MCP Server",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "react-native-sqlite-mcp": "dist/index.js"
9
9
  },
10
+ "files": [
11
+ "dist"
12
+ ],
10
13
  "scripts": {
11
14
  "build": "tsc",
12
15
  "start": "node dist/index.js",
@@ -23,11 +26,11 @@
23
26
  "license": "MIT",
24
27
  "dependencies": {
25
28
  "@modelcontextprotocol/sdk": "^1.6.0",
26
- "sqlite3": "^5.1.7"
29
+ "sql.js": "^1.14.0"
27
30
  },
28
31
  "devDependencies": {
29
32
  "@types/node": "^22.13.4",
30
- "@types/sqlite3": "^3.1.11",
33
+ "@types/sql.js": "^1.4.9",
31
34
  "typescript": "^5.7.3"
32
35
  }
33
36
  }
package/CONTRIBUTING.md DELETED
@@ -1,35 +0,0 @@
1
- # Welcome to the Contributing Guide
2
-
3
- First off, thank you for considering contributing to the **Universal React Native SQLite MCP**!
4
-
5
- Whether you're fixing a typo, optimizing an ADB command, or pointing out a glaring architecture flaw, your help is what makes open source awesome. I built this tool to fill a massive gap in the current MCP ecosystem around mobile development. There are plenty of SQLite tools out there, but none that seamlessly hook into your local emulators and simulators. And all the MCP's around mobile development are primarily UI focused. So hopefully this fills a niche. Let's make mobile dev less painful together!
6
-
7
- ## The Vibe
8
-
9
- - **Keep it chill, but keep it working**: Have fun with the code and the docs, but let's make sure the core functionality is rock solid.
10
- - **Assume good intent**: We're all here to build cool stuff and learn. Be kind in your PRs and specific in your code reviews.
11
- - **Developer Experience (DX) is king**: This tool exists to make life easier. If a feature makes the tool harder to use or setup, we probably need to rethink it.
12
-
13
- ## 🛠️ How to get started
14
-
15
- 1. **Fork the repo** and clone it locally.
16
- 2. **Install dependencies**: `npm install`
17
- 3. **Make your changes**: Branch off `main` and do your thing.
18
- 4. **Test it out locally**: Build it with `npm run build` and point your local MCP client to it to verify it actually works against an emulator/simulator.
19
- 5. **Open a PR**: Give it a descriptive title and tell me what you fixed or added.
20
-
21
- ## Found a Bug?
22
-
23
- Please open an issue! Tell me:
24
- - What you were trying to do.
25
- - What actually happened.
26
- - Are you on iOS, Android, or both?
27
- - Any logs you have from your AI client.
28
-
29
- ## Got a Feature Idea?
30
-
31
- Open an issue and let's chat about it before you spend hours coding it up. I'm totally open to new ideas (especially if they involve making the auto-discovery even smarter).
32
-
33
- ---
34
-
35
- Let's build something epic together.