@mcpjam/inspector 0.3.5 → 0.3.6

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.
@@ -1072,6 +1072,9 @@ video {
1072
1072
  .right-2 {
1073
1073
  right: 0.5rem;
1074
1074
  }
1075
+ .right-3 {
1076
+ right: 0.75rem;
1077
+ }
1075
1078
  .right-4 {
1076
1079
  right: 1rem;
1077
1080
  }
@@ -1134,6 +1137,9 @@ video {
1134
1137
  margin-top: auto;
1135
1138
  margin-bottom: auto;
1136
1139
  }
1140
+ .-ml-1 {
1141
+ margin-left: -0.25rem;
1142
+ }
1137
1143
  .mb-1 {
1138
1144
  margin-bottom: 0.25rem;
1139
1145
  }
@@ -1167,9 +1173,6 @@ video {
1167
1173
  .ml-7 {
1168
1174
  margin-left: 1.75rem;
1169
1175
  }
1170
- .ml-8 {
1171
- margin-left: 2rem;
1172
- }
1173
1176
  .ml-auto {
1174
1177
  margin-left: auto;
1175
1178
  }
@@ -1525,6 +1528,9 @@ video {
1525
1528
  .cursor-default {
1526
1529
  cursor: default;
1527
1530
  }
1531
+ .cursor-help {
1532
+ cursor: help;
1533
+ }
1528
1534
  .cursor-not-allowed {
1529
1535
  cursor: not-allowed;
1530
1536
  }
@@ -1788,6 +1794,10 @@ video {
1788
1794
  --tw-border-opacity: 1;
1789
1795
  border-color: rgb(209 213 219 / var(--tw-border-opacity, 1));
1790
1796
  }
1797
+ .border-gray-400 {
1798
+ --tw-border-opacity: 1;
1799
+ border-color: rgb(156 163 175 / var(--tw-border-opacity, 1));
1800
+ }
1791
1801
  .border-green-200 {
1792
1802
  --tw-border-opacity: 1;
1793
1803
  border-color: rgb(187 247 208 / var(--tw-border-opacity, 1));
@@ -1901,6 +1911,10 @@ video {
1901
1911
  --tw-bg-opacity: 1;
1902
1912
  background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1));
1903
1913
  }
1914
+ .bg-gray-600 {
1915
+ --tw-bg-opacity: 1;
1916
+ background-color: rgb(75 85 99 / var(--tw-bg-opacity, 1));
1917
+ }
1904
1918
  .bg-gray-900 {
1905
1919
  --tw-bg-opacity: 1;
1906
1920
  background-color: rgb(17 24 39 / var(--tw-bg-opacity, 1));
@@ -2262,15 +2276,27 @@ video {
2262
2276
  .pb-4 {
2263
2277
  padding-bottom: 1rem;
2264
2278
  }
2279
+ .pl-12 {
2280
+ padding-left: 3rem;
2281
+ }
2265
2282
  .pl-2 {
2266
2283
  padding-left: 0.5rem;
2267
2284
  }
2285
+ .pl-4 {
2286
+ padding-left: 1rem;
2287
+ }
2268
2288
  .pr-10 {
2269
2289
  padding-right: 2.5rem;
2270
2290
  }
2291
+ .pr-12 {
2292
+ padding-right: 3rem;
2293
+ }
2271
2294
  .pr-3 {
2272
2295
  padding-right: 0.75rem;
2273
2296
  }
2297
+ .pr-4 {
2298
+ padding-right: 1rem;
2299
+ }
2274
2300
  .pr-6 {
2275
2301
  padding-right: 1.5rem;
2276
2302
  }
@@ -2826,6 +2852,11 @@ h1 {
2826
2852
  border-color: hsl(var(--border) / 0.6);
2827
2853
  }
2828
2854
 
2855
+ .hover\:border-gray-500:hover {
2856
+ --tw-border-opacity: 1;
2857
+ border-color: rgb(107 114 128 / var(--tw-border-opacity, 1));
2858
+ }
2859
+
2829
2860
  .hover\:border-primary\/20:hover {
2830
2861
  border-color: hsl(var(--primary) / 0.2);
2831
2862
  }
@@ -2871,6 +2902,11 @@ h1 {
2871
2902
  background-color: hsl(var(--destructive) / 0.9);
2872
2903
  }
2873
2904
 
2905
+ .hover\:bg-gray-700:hover {
2906
+ --tw-bg-opacity: 1;
2907
+ background-color: rgb(55 65 81 / var(--tw-bg-opacity, 1));
2908
+ }
2909
+
2874
2910
  .hover\:bg-gray-800\/10:hover {
2875
2911
  background-color: rgb(31 41 55 / 0.1);
2876
2912
  }
@@ -3004,6 +3040,11 @@ h1 {
3004
3040
  color: hsl(var(--foreground));
3005
3041
  }
3006
3042
 
3043
+ .hover\:text-gray-600:hover {
3044
+ --tw-text-opacity: 1;
3045
+ color: rgb(75 85 99 / var(--tw-text-opacity, 1));
3046
+ }
3047
+
3007
3048
  .hover\:text-primary:hover {
3008
3049
  color: hsl(var(--primary));
3009
3050
  }
@@ -3097,6 +3138,11 @@ h1 {
3097
3138
  --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1));
3098
3139
  }
3099
3140
 
3141
+ .focus\:ring-indigo-400:focus {
3142
+ --tw-ring-opacity: 1;
3143
+ --tw-ring-color: rgb(129 140 248 / var(--tw-ring-opacity, 1));
3144
+ }
3145
+
3100
3146
  .focus\:ring-primary\/20:focus {
3101
3147
  --tw-ring-color: hsl(var(--primary) / 0.2);
3102
3148
  }
@@ -3895,6 +3941,11 @@ h1 {
3895
3941
  --tw-gradient-to: hsl(var(--secondary) / 0.3) var(--tw-gradient-to-position);
3896
3942
  }
3897
3943
 
3944
+ .dark\:hover\:text-gray-300:hover:is(.dark *) {
3945
+ --tw-text-opacity: 1;
3946
+ color: rgb(209 213 219 / var(--tw-text-opacity, 1));
3947
+ }
3948
+
3898
3949
  .dark\:hover\:text-slate-100:hover:is(.dark *) {
3899
3950
  --tw-text-opacity: 1;
3900
3951
  color: rgb(241 245 249 / var(--tw-text-opacity, 1));
@@ -3983,6 +4034,17 @@ h1 {
3983
4034
  .md\:max-w-\[420px\] {
3984
4035
  max-width: 420px;
3985
4036
  }
4037
+
4038
+ .md\:grid-cols-2 {
4039
+ grid-template-columns: repeat(2, minmax(0, 1fr));
4040
+ }
4041
+ }
4042
+
4043
+ @media (min-width: 1024px) {
4044
+
4045
+ .lg\:grid-cols-3 {
4046
+ grid-template-columns: repeat(3, minmax(0, 1fr));
4047
+ }
3986
4048
  }
3987
4049
 
3988
4050
  .\[\&\+div\]\:text-xs+div {
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/mcp_jam.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>MCPJam Inspector</title>
8
- <script type="module" crossorigin src="/assets/index-DQYTYcqe.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-DegSReJM.css">
8
+ <script type="module" crossorigin src="/assets/index-Bgrnc5s2.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-CWDemo1t.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root" class="w-full"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcpjam/inspector",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "MCPJam inspector",
5
5
  "license": "Apache-2.0",
6
6
  "author": "MCPJam (https://mcpjam.com)",
@@ -0,0 +1,108 @@
1
+ /**
2
+ * DatabaseManager - Basic libSQL database manager for MCPJam Inspector
3
+ * Simple local SQLite database foundation
4
+ */
5
+ import { createClient } from "@libsql/client";
6
+ import { readFileSync } from "fs";
7
+ import { join, dirname } from "path";
8
+ import { fileURLToPath } from "url";
9
+ import { ensureDirectoryExists, ensureMCPJamDirectory, getResolvedDatabasePath, } from "./utils.js";
10
+ import { DatabaseError, QueryError, } from "./types.js";
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ export class DatabaseManager {
13
+ client;
14
+ initialized = false;
15
+ config;
16
+ constructor(config) {
17
+ this.config = config || { localPath: getResolvedDatabasePath() };
18
+ this.client = this.createClient();
19
+ }
20
+ createClient() {
21
+ // Use the configured database path
22
+ const dbPath = this.config.localPath;
23
+ return createClient({
24
+ url: `file:${dbPath}`,
25
+ });
26
+ }
27
+ async initialize() {
28
+ if (this.initialized)
29
+ return;
30
+ try {
31
+ console.log("🔄 Initializing database...");
32
+ // Ensure .mcpjam directory exists and database directory
33
+ await ensureMCPJamDirectory();
34
+ const dbPath = getResolvedDatabasePath();
35
+ await ensureDirectoryExists(dbPath);
36
+ // Read and execute schema
37
+ const schemaPath = join(__dirname, "schema.sql");
38
+ const schema = readFileSync(schemaPath, "utf-8");
39
+ await this.client.executeMultiple(schema);
40
+ this.initialized = true;
41
+ console.log("✅ Database initialized successfully");
42
+ }
43
+ catch (error) {
44
+ throw new DatabaseError("Failed to initialize database", "INIT_ERROR", error);
45
+ }
46
+ }
47
+ // ============================================================================
48
+ // APP METADATA OPERATIONS
49
+ // ============================================================================
50
+ async getMetadata(key) {
51
+ try {
52
+ const result = await this.client.execute({
53
+ sql: "SELECT value FROM app_metadata WHERE key = ?",
54
+ args: [key],
55
+ });
56
+ if (result.rows.length === 0)
57
+ return null;
58
+ return result.rows[0].value;
59
+ }
60
+ catch (error) {
61
+ throw new QueryError("Failed to get metadata", undefined, error);
62
+ }
63
+ }
64
+ async setMetadata(key, value) {
65
+ try {
66
+ await this.client.execute({
67
+ sql: "INSERT OR REPLACE INTO app_metadata (key, value) VALUES (?, ?)",
68
+ args: [key, value],
69
+ });
70
+ }
71
+ catch (error) {
72
+ throw new QueryError("Failed to set metadata", undefined, error);
73
+ }
74
+ }
75
+ async getAllMetadata() {
76
+ try {
77
+ const result = await this.client.execute({
78
+ sql: "SELECT * FROM app_metadata ORDER BY key",
79
+ args: [],
80
+ });
81
+ return result.rows.map((row) => ({
82
+ key: row.key,
83
+ value: row.value,
84
+ createdAt: new Date(row.created_at),
85
+ updatedAt: new Date(row.updated_at),
86
+ }));
87
+ }
88
+ catch (error) {
89
+ throw new QueryError("Failed to get all metadata", undefined, error);
90
+ }
91
+ }
92
+ async deleteMetadata(key) {
93
+ try {
94
+ await this.client.execute({
95
+ sql: "DELETE FROM app_metadata WHERE key = ?",
96
+ args: [key],
97
+ });
98
+ }
99
+ catch (error) {
100
+ throw new QueryError("Failed to delete metadata", undefined, error);
101
+ }
102
+ }
103
+ async close() {
104
+ if (this.client) {
105
+ this.client.close();
106
+ }
107
+ }
108
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Database module exports for MCPJam Inspector
3
+ * Provides comprehensive libSQL database functionality
4
+ */
5
+ export { DatabaseManager } from "./DatabaseManager.js";
6
+ export { createDatabaseRoutes } from "./routes.js";
7
+ export * from "./types.js";
8
+ export * from "./utils.js";
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Database API routes for MCPJam Inspector
3
+ * Basic endpoints for app metadata operations
4
+ */
5
+ import { Router } from "express";
6
+ export function createDatabaseRoutes(databaseManager) {
7
+ const router = Router();
8
+ // Error handler wrapper
9
+ const asyncHandler = (fn) => (req, res, next) => {
10
+ Promise.resolve(fn(req, res, next)).catch(next);
11
+ };
12
+ // ============================================================================
13
+ // APP METADATA OPERATIONS
14
+ // ============================================================================
15
+ // Get all metadata
16
+ router.get("/metadata", asyncHandler(async (req, res) => {
17
+ const metadata = await databaseManager.getAllMetadata();
18
+ res.json({
19
+ success: true,
20
+ data: metadata,
21
+ count: metadata.length,
22
+ });
23
+ }));
24
+ // Get single metadata value
25
+ router.get("/metadata/:key", asyncHandler(async (req, res) => {
26
+ const value = await databaseManager.getMetadata(req.params.key);
27
+ if (value === null) {
28
+ res.status(404).json({
29
+ success: false,
30
+ error: "Metadata key not found",
31
+ });
32
+ return;
33
+ }
34
+ res.json({
35
+ success: true,
36
+ data: { key: req.params.key, value },
37
+ });
38
+ }));
39
+ // Set metadata value
40
+ router.post("/metadata/:key", asyncHandler(async (req, res) => {
41
+ const { value } = req.body;
42
+ if (value === undefined) {
43
+ res.status(400).json({
44
+ success: false,
45
+ error: "Missing required field: value",
46
+ });
47
+ return;
48
+ }
49
+ await databaseManager.setMetadata(req.params.key, value);
50
+ res.json({
51
+ success: true,
52
+ message: "Metadata stored successfully",
53
+ });
54
+ }));
55
+ // Delete metadata key
56
+ router.delete("/metadata/:key", asyncHandler(async (req, res) => {
57
+ await databaseManager.deleteMetadata(req.params.key);
58
+ res.json({
59
+ success: true,
60
+ message: "Metadata deleted successfully",
61
+ });
62
+ }));
63
+ // ============================================================================
64
+ // HEALTH CHECK
65
+ // ============================================================================
66
+ router.get("/health", asyncHandler(async (req, res) => {
67
+ res.json({
68
+ success: true,
69
+ message: "Database API is healthy",
70
+ timestamp: new Date().toISOString(),
71
+ });
72
+ }));
73
+ // Error handling middleware
74
+ router.use((error, req, res, next) => {
75
+ console.error("Database API Error:", error);
76
+ const statusCode = error.status || 500;
77
+ const message = error.message || "Internal server error";
78
+ res.status(statusCode).json({
79
+ success: false,
80
+ error: message,
81
+ code: error.code,
82
+ ...(process.env.NODE_ENV === "development" && { stack: error.stack }),
83
+ });
84
+ });
85
+ return router;
86
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Database types and interfaces for MCPJam Inspector
3
+ * Basic foundation types for local SQLite database
4
+ */
5
+ // ============================================================================
6
+ // ERROR TYPES
7
+ // ============================================================================
8
+ export class DatabaseError extends Error {
9
+ code;
10
+ cause;
11
+ constructor(message, code, cause) {
12
+ super(message);
13
+ this.code = code;
14
+ this.cause = cause;
15
+ this.name = "DatabaseError";
16
+ }
17
+ }
18
+ export class QueryError extends Error {
19
+ query;
20
+ cause;
21
+ constructor(message, query, cause) {
22
+ super(message);
23
+ this.query = query;
24
+ this.cause = cause;
25
+ this.name = "QueryError";
26
+ }
27
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Database utilities for MCPJam Inspector
3
+ * Basic utilities for local SQLite database setup
4
+ */
5
+ import { mkdir, access } from "fs/promises";
6
+ import { dirname } from "path";
7
+ import { homedir } from "os";
8
+ import { join } from "path";
9
+ /**
10
+ * Ensures the .mcpjam directory exists in the user's home directory
11
+ */
12
+ export async function ensureMCPJamDirectory() {
13
+ const mcpjamDir = join(homedir(), ".mcpjam");
14
+ try {
15
+ await access(mcpjamDir);
16
+ }
17
+ catch {
18
+ // Directory doesn't exist, create it
19
+ await mkdir(mcpjamDir, { recursive: true });
20
+ console.log(`📁 Created MCPJam directory: ${mcpjamDir}`);
21
+ }
22
+ return mcpjamDir;
23
+ }
24
+ /**
25
+ * Ensures the directory for a given file path exists
26
+ */
27
+ export async function ensureDirectoryExists(filePath) {
28
+ const dir = dirname(filePath);
29
+ try {
30
+ await access(dir);
31
+ }
32
+ catch {
33
+ await mkdir(dir, { recursive: true });
34
+ }
35
+ }
36
+ /**
37
+ * Gets the resolved database path - THE SINGLE SOURCE OF TRUTH
38
+ * Priority: MCPJAM_DB_PATH env var > default ~/.mcpjam/data.db
39
+ */
40
+ export function getResolvedDatabasePath() {
41
+ return process.env.MCPJAM_DB_PATH || join(homedir(), ".mcpjam", "data.db");
42
+ }
43
+ /**
44
+ * Environment configuration helper
45
+ */
46
+ export function getDatabaseConfig() {
47
+ return {
48
+ localPath: getResolvedDatabasePath(),
49
+ };
50
+ }
51
+ /**
52
+ * Checks if the database file exists
53
+ */
54
+ export async function databaseExists(databasePath) {
55
+ try {
56
+ await access(databasePath);
57
+ return true;
58
+ }
59
+ catch {
60
+ return false;
61
+ }
62
+ }
63
+ /**
64
+ * Database connection test
65
+ */
66
+ export async function testDatabaseConnection(config) {
67
+ try {
68
+ const { createClient } = await import("@libsql/client");
69
+ // Use the single source of truth for database path
70
+ const dbPath = config?.localPath || getResolvedDatabasePath();
71
+ await ensureDirectoryExists(dbPath);
72
+ const client = createClient({
73
+ url: `file:${dbPath}`,
74
+ });
75
+ // Test the connection with a simple query
76
+ await client.execute("SELECT 1");
77
+ client.close();
78
+ return { success: true };
79
+ }
80
+ catch (error) {
81
+ return {
82
+ success: false,
83
+ error: error instanceof Error ? error.message : "Unknown error",
84
+ };
85
+ }
86
+ }