react-native-sqlite-mcp 1.0.0 → 1.0.2

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.
@@ -0,0 +1,35 @@
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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Brian Murillo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,19 +1,23 @@
1
- # Universal React Native SQLite MCP
1
+ # 🚀 Universal React Native SQLite MCP
2
2
 
3
- A Model Context Protocol (MCP) server that connects LLMs (Claude, Cursor, etc.) to your local React Native SQLite databases running on an iOS Simulator or Android Emulator.
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.
4
4
 
5
- This essentially acts as a "Database Inspector" for AI agents, allowing them to automatically view your DB schema and execute queries against the live app database without you having to manually export or describe tables.
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.
6
6
 
7
- ## Requirements
8
- * NodeJS
9
- * iOS: `xcrun simctl` (available with Xcode)
10
- * Android: `adb` (available with Android Studio / Android SDK)
7
+ ---
11
8
 
12
- ## Quick Start (Recommended)
9
+ ## 🤔 Why did I build this?
13
10
 
14
- The easiest way to use this MCP server is via `npx`. This doesn't require cloning the repository.
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.
15
12
 
16
- Add this to your `claude_desktop_config.json` (or Cursor/other MCP client settings):
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.
14
+
15
+
16
+ ## 📦 Quick Start (The Magic Way)
17
+
18
+ You don't even need to clone this repo. The easiest way to get rolling is via `npx`.
19
+
20
+ Toss this bad boy into your `mcp.json` (or your Claude/Cursor/agent settings):
17
21
 
18
22
  ```json
19
23
  {
@@ -30,18 +34,20 @@ Add this to your `claude_desktop_config.json` (or Cursor/other MCP client settin
30
34
  }
31
35
  ```
32
36
 
33
- ## Manual Installation (From Source)
37
+ Boom. You're connected. 🤝
34
38
 
35
- If you want to run it locally from source:
39
+ ## 🛠️ Manual Installation (For the brave)
40
+
41
+ Prefer to tinker with the source code yourself? I respect it.
36
42
 
37
43
  ```bash
38
- git clone <your-repo> react-native-sqlite-mcp
44
+ git clone https://github.com/your-username/react-native-sqlite-mcp.git
39
45
  cd react-native-sqlite-mcp
40
46
  npm install
41
47
  npm run build
42
48
  ```
43
49
 
44
- Then configure your MCP client to point to the local file:
50
+ Then point your MCP client to your local build:
45
51
 
46
52
  ```json
47
53
  {
@@ -57,27 +63,42 @@ Then configure your MCP client to point to the local file:
57
63
  }
58
64
  }
59
65
  ```
60
- *Note: Replace `/absolute/path/to/react-native-sqlite-mcp` with the absolute path to where you cloned this repository.*
61
66
 
62
- ### Environment Variables
63
- - `DB_NAME`: The filename of your database (e.g., `my_app.db`) or a glob pattern (`*.db`).
64
- - `ANDROID_BUNDLE_ID`: Only required for Android. The application ID/package name of your app (e.g., `com.mycompany.app`). Optional: If omitted, the MCP will scan all third-party apps on the emulator for SQLite databases.
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.
65
72
 
66
- ## Features
73
+ ## 🦸‍♂️ Features (What this bad boy can do)
67
74
 
68
- This MCP provides four core tools:
75
+ This MCP arms your AI with four super-powered tools:
69
76
 
70
- - **`list_databases`**: Discovers and returns a list of all SQLite databases currently available. You can optionally pass `platform` ('ios' or 'android') to explicitly target one environment.
71
- - **`sync_database`**: Pulls a local copy of a database from the active device so the AI can inspect it, and sets it as the active database. `dbName`, `bundleId`, and `platform` are all optional; if omitted, it will automatically select the first discovered default database across all running emulators.
72
- - **`inspect_schema`**: Returns the `CREATE TABLE` and column information for the currently active synced database. Gives the AI the map of your database. Optionally accepts `tableName`, `dbName`, and `platform` to skip explicit syncing.
73
- - **`read_table_contents`**: Returns all rows from a specified table. Equivalent to `SELECT * FROM table_name`, limited to 100 rows by default.
74
- - **`query_db`**: Accepts a raw SQL query and returns the results for the currently active database. Optionally accepts `dbName` and `platform` to skip explicit syncing.
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.
75
82
 
76
- ## How it Works
83
+ ## ⚙️ How it Actually Works (Under the hood)
77
84
 
78
- 1. **Auto-Detect Platform**: By default, the tool will scan **both** iOS and Android environments. It locates booted iOS Simulators using `simctl` and active Android Emulators using `adb`.
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`.
79
86
  2. **Auto-Locate Database**:
80
- - For iOS, the simulator's app sandbox files are directly accessed without needing root.
81
- - For Android, it uses `adb exec-out run-as com.pkg.name cat ...` to copy the database file, along with `-wal` and `-shm` temp files, bypassing strict root permission boundaries on debug profiles.
82
- 3. **Platform Switching**: The MCP server maintains a single active database connection. If you want to switch between iOS and Android, the AI simply calls `sync_database` targeting the desired platform.
83
- 4. **Execution**: Wraps arbitrary requests allowing the LLM to learn the schema and query live data.
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.
91
+
92
+ ## 🤝 Contributing (Yes, please!)
93
+
94
+ Got an idea to make this objectively cooler? Found a bug where it accidentally queried your smart fridge?
95
+
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.
97
+
98
+ Check out the [CONTRIBUTING.md](./CONTRIBUTING.md) guide to see how we party.
99
+
100
+ ## 📜 License
101
+
102
+ This project is licensed under the **MIT License**.
103
+
104
+ See the [LICENSE](./LICENSE) file for the legal jargon.
package/dist/db.js CHANGED
@@ -1,74 +1,89 @@
1
- import sqlite3 from "sqlite3";
2
- export class Database {
3
- db;
4
- constructor(filename) {
5
- this.db = new sqlite3.Database(filename);
1
+ import initSqlJs from "sql.js";
2
+ import fs from "fs";
3
+ import { logger } from "./logger.js";
4
+ let SQL = null;
5
+ async function getSqlJs() {
6
+ if (!SQL) {
7
+ SQL = await initSqlJs();
8
+ logger.debug("sql.js WASM engine initialized");
6
9
  }
7
- all(sql, params = []) {
8
- return new Promise((resolve, reject) => {
9
- this.db.all(sql, params, (err, rows) => {
10
- if (err)
11
- reject(err);
12
- else
13
- resolve(rows);
14
- });
15
- });
10
+ return SQL;
11
+ }
12
+ const CACHE_TTL_MS = 60_000;
13
+ const connectionCache = new Map();
14
+ async function getCachedDb(dbPath) {
15
+ const existing = connectionCache.get(dbPath);
16
+ if (existing) {
17
+ clearTimeout(existing.timer);
18
+ existing.lastUsed = Date.now();
19
+ existing.timer = setTimeout(() => evictConnection(dbPath), CACHE_TTL_MS);
20
+ return existing.db;
16
21
  }
17
- get(sql, params = []) {
18
- return new Promise((resolve, reject) => {
19
- this.db.get(sql, params, (err, row) => {
20
- if (err)
21
- reject(err);
22
- else
23
- resolve(row);
24
- });
25
- });
22
+ const sqlJs = await getSqlJs();
23
+ const buffer = fs.readFileSync(dbPath);
24
+ const db = new sqlJs.Database(buffer);
25
+ const timer = setTimeout(() => evictConnection(dbPath), CACHE_TTL_MS);
26
+ connectionCache.set(dbPath, { db, dbPath, lastUsed: Date.now(), timer });
27
+ logger.debug(`Opened DB connection: ${dbPath}`);
28
+ return db;
29
+ }
30
+ function evictConnection(dbPath) {
31
+ const entry = connectionCache.get(dbPath);
32
+ if (!entry)
33
+ return;
34
+ connectionCache.delete(dbPath);
35
+ try {
36
+ entry.db.close();
37
+ logger.debug(`Closed idle DB connection: ${dbPath}`);
26
38
  }
27
- close() {
28
- return new Promise((resolve, reject) => {
29
- this.db.close((err) => {
30
- if (err)
31
- reject(err);
32
- else
33
- resolve();
34
- });
35
- });
39
+ catch (e) {
40
+ logger.warn(`Error closing DB: ${dbPath}`, { error: String(e) });
36
41
  }
37
42
  }
38
- /**
39
- * Executes a simple query on the database.
40
- */
41
- export async function queryDb(dbPath, sql, params = []) {
42
- const db = new Database(dbPath);
43
- try {
44
- return await db.all(sql, params);
43
+ export async function closeAllConnections() {
44
+ const paths = [...connectionCache.keys()];
45
+ for (const p of paths) {
46
+ evictConnection(p);
45
47
  }
46
- finally {
47
- await db.close();
48
+ logger.info(`Closed ${paths.length} cached DB connection(s)`);
49
+ }
50
+ const QUERY_TIMEOUT_MS = 30_000;
51
+ function withTimeout(promise, ms, label) {
52
+ return new Promise((resolve, reject) => {
53
+ const timer = setTimeout(() => {
54
+ reject(new Error(`Query timed out after ${ms}ms: ${label}`));
55
+ }, ms);
56
+ promise
57
+ .then((result) => { clearTimeout(timer); resolve(result); })
58
+ .catch((err) => { clearTimeout(timer); reject(err); });
59
+ });
60
+ }
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());
48
68
  }
69
+ stmt.free();
70
+ return results;
71
+ }
72
+ export async function queryDb(dbPath, sql, params = []) {
73
+ const db = await getCachedDb(dbPath);
74
+ return withTimeout(Promise.resolve(runQuery(db, sql, params)), QUERY_TIMEOUT_MS, sql.slice(0, 80));
49
75
  }
50
- /**
51
- * Returns a detailed schema of all tables in the database.
52
- */
53
76
  export async function inspectSchema(dbPath) {
54
- const db = new Database(dbPath);
55
- try {
56
- // Get all table names
57
- const tables = await db.all("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;");
58
- const schemaInfo = {};
59
- for (const table of tables) {
60
- // For each table, get column definitions
61
- const columns = await db.all(`PRAGMA table_info("${table.name}");`);
62
- // Get table creation SQL
63
- const createSql = await db.get(`SELECT sql FROM sqlite_master WHERE type='table' AND name=?`, [table.name]);
64
- schemaInfo[table.name] = {
65
- columns,
66
- createSql: createSql?.sql
67
- };
68
- }
69
- return schemaInfo;
70
- }
71
- finally {
72
- await db.close();
77
+ const db = await getCachedDb(dbPath);
78
+ const tables = runQuery(db, "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;");
79
+ const schemaInfo = {};
80
+ for (const table of tables) {
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]);
83
+ schemaInfo[table.name] = {
84
+ columns,
85
+ createSql: createSqlRows[0]?.sql
86
+ };
73
87
  }
88
+ return schemaInfo;
74
89
  }