mcp-data-agent 0.1.0

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.
Files changed (37) hide show
  1. package/.env.example +18 -0
  2. package/.idea/jsLibraryMappings.xml +6 -0
  3. package/.idea/mcp_engine.iml +8 -0
  4. package/.idea/modules.xml +8 -0
  5. package/.idea/php.xml +19 -0
  6. package/.idea/vcs.xml +6 -0
  7. package/README.md +587 -0
  8. package/examples/basic/package.json +26 -0
  9. package/examples/basic/src/index.ts +107 -0
  10. package/examples/basic/tsconfig.json +15 -0
  11. package/package.json +106 -0
  12. package/packages/core/README.md +7 -0
  13. package/packages/core/package.json +38 -0
  14. package/packages/core/src/agent.test.ts +213 -0
  15. package/packages/core/src/agent.ts +373 -0
  16. package/packages/core/src/errors.ts +73 -0
  17. package/packages/core/src/index.ts +78 -0
  18. package/packages/core/src/permissions.ts +71 -0
  19. package/packages/core/src/query-safety.ts +125 -0
  20. package/packages/core/src/schema-safety.ts +118 -0
  21. package/packages/core/src/tenant.ts +58 -0
  22. package/packages/core/src/tool-registry.ts +61 -0
  23. package/packages/core/src/types.ts +250 -0
  24. package/packages/core/tsconfig.json +10 -0
  25. package/packages/mcp/README.md +7 -0
  26. package/packages/mcp/package.json +46 -0
  27. package/packages/mcp/src/index.ts +15 -0
  28. package/packages/mcp/src/server.ts +170 -0
  29. package/packages/mcp/tsconfig.json +11 -0
  30. package/packages/postgres/README.md +7 -0
  31. package/packages/postgres/package.json +46 -0
  32. package/packages/postgres/src/adapter.ts +358 -0
  33. package/packages/postgres/src/index.ts +19 -0
  34. package/packages/postgres/tsconfig.json +11 -0
  35. package/tsconfig.base.json +22 -0
  36. package/tsconfig.json +8 -0
  37. package/vitest.config.ts +14 -0
@@ -0,0 +1,170 @@
1
+ /**
2
+ * MCP Data Agent — @mcp-data-agent/mcp
3
+ * Created by Arslan Habib
4
+ *
5
+ * ---------------------------------------------------------------------------
6
+ * FILE: server.ts
7
+ * PURPOSE:
8
+ * Bridges MCPDataAgent tools to the Model Context Protocol so AI assistants
9
+ * (Cursor, Claude Desktop, custom MCP clients) can call them.
10
+ *
11
+ * WHAT IT DOES:
12
+ * - Creates an McpServer (official TypeScript SDK v2)
13
+ * - Registers database_schema and database_query MCP tools
14
+ * - Optionally mirrors custom agent tools (dots → underscores in names)
15
+ * - Returns JSON text content (or structured errors) to the client
16
+ *
17
+ * AUTH / TENANT:
18
+ * Still resolved by agent.resolveContext on every invoke — MCP args never
19
+ * supply tenantId.
20
+ * ---------------------------------------------------------------------------
21
+ */
22
+
23
+ import type { MCPDataAgent } from "@mcp-data-agent/core";
24
+ import { McpDataAgentError } from "@mcp-data-agent/core";
25
+ import { McpServer } from "@modelcontextprotocol/server";
26
+ import * as z from "zod/v4";
27
+
28
+ /** Options for createMcpDataAgentServer(). */
29
+ export interface CreateMcpServerOptions {
30
+ /** Fully configured MCPDataAgent instance. */
31
+ agent: MCPDataAgent;
32
+ /** MCP server name shown to clients. */
33
+ name?: string;
34
+ /** MCP server version shown to clients. */
35
+ version?: string;
36
+ /**
37
+ * When true, register every custom tool currently on the agent.
38
+ * Register custom tools BEFORE calling this factory.
39
+ */
40
+ includeCustomTools?: boolean;
41
+ }
42
+
43
+ /** Wrap successful tool output as MCP text content (JSON). */
44
+ function toTextResult(data: unknown) {
45
+ return {
46
+ content: [
47
+ {
48
+ type: "text" as const,
49
+ text: JSON.stringify(data, null, 2),
50
+ },
51
+ ],
52
+ };
53
+ }
54
+
55
+ /** Wrap failures as MCP error content with a stable error code when possible. */
56
+ function toErrorResult(error: unknown) {
57
+ const message =
58
+ error instanceof Error ? error.message : "Unknown tool execution error";
59
+ const code =
60
+ error instanceof McpDataAgentError ? error.code : "TOOL_EXECUTION_ERROR";
61
+ return {
62
+ isError: true as const,
63
+ content: [
64
+ {
65
+ type: "text" as const,
66
+ text: JSON.stringify({ error: code, message }, null, 2),
67
+ },
68
+ ],
69
+ };
70
+ }
71
+
72
+ /**
73
+ * Build an MCP server that exposes MCP Data Agent tools to AI assistants.
74
+ * Connect it with StdioServerTransport or an HTTP transport in the host app.
75
+ */
76
+ export function createMcpDataAgentServer(
77
+ options: CreateMcpServerOptions,
78
+ ): McpServer {
79
+ const {
80
+ agent,
81
+ name = "mcp-data-agent",
82
+ version = "0.1.0",
83
+ includeCustomTools = true,
84
+ } = options;
85
+
86
+ const server = new McpServer({ name, version });
87
+
88
+ // --- Built-in: safe schema for the authenticated user ---
89
+ server.registerTool(
90
+ "database_schema",
91
+ {
92
+ description:
93
+ "Return a safe, redacted database schema and business entities for the authenticated user.",
94
+ inputSchema: z.object({}),
95
+ },
96
+ async () => {
97
+ try {
98
+ const result = await agent.invokeTool("database.schema", {});
99
+ return toTextResult(result);
100
+ } catch (error) {
101
+ return toErrorResult(error);
102
+ }
103
+ },
104
+ );
105
+
106
+ // --- Built-in: validated read-only SQL ---
107
+ server.registerTool(
108
+ "database_query",
109
+ {
110
+ description:
111
+ "Execute a validated read-only SQL SELECT. Tenant isolation and permissions are enforced by the host application session — do not pass tenantId.",
112
+ inputSchema: z.object({
113
+ sql: z.string().describe("Read-only SELECT (or WITH … SELECT) SQL"),
114
+ params: z
115
+ .array(z.unknown())
116
+ .optional()
117
+ .describe("Bound parameters for the query"),
118
+ limit: z
119
+ .number()
120
+ .int()
121
+ .positive()
122
+ .max(1000)
123
+ .optional()
124
+ .describe("Maximum rows to return"),
125
+ }),
126
+ },
127
+ async ({ sql, params, limit }) => {
128
+ try {
129
+ const result = await agent.invokeTool("database.query", {
130
+ sql,
131
+ params,
132
+ limit,
133
+ });
134
+ return toTextResult(result);
135
+ } catch (error) {
136
+ return toErrorResult(error);
137
+ }
138
+ },
139
+ );
140
+
141
+ // --- Custom tools registered on the agent (e.g. members.statistics) ---
142
+ if (includeCustomTools) {
143
+ for (const tool of agent.listTools()) {
144
+ // Skip built-ins already registered above under MCP-friendly names
145
+ if (tool.name === "database.schema" || tool.name === "database.query") {
146
+ continue;
147
+ }
148
+ // MCP tool names typically use underscores: members.statistics → members_statistics
149
+ const mcpName = tool.name.replace(/\./g, "_");
150
+ server.registerTool(
151
+ mcpName,
152
+ {
153
+ description: tool.description,
154
+ // Passthrough object: domain tools define their own shapes
155
+ inputSchema: z.object({}).passthrough(),
156
+ },
157
+ async (args) => {
158
+ try {
159
+ const result = await agent.invokeTool(tool.name, args);
160
+ return toTextResult(result);
161
+ } catch (error) {
162
+ return toErrorResult(error);
163
+ }
164
+ },
165
+ );
166
+ }
167
+ }
168
+
169
+ return server;
170
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist",
6
+ "composite": true
7
+ },
8
+ "include": ["src/**/*.ts"],
9
+ "exclude": ["src/**/*.test.ts", "dist"],
10
+ "references": [{ "path": "../core" }]
11
+ }
@@ -0,0 +1,7 @@
1
+ # @mcp-data-agent/postgres
2
+
3
+ PostgreSQL adapter for **MCP Data Agent**.
4
+
5
+ **Created by Arslan Habib**
6
+
7
+ Prefer a **read-only** database role for analytical access. Enable RLS policies that read `current_setting('app.tenant_id', true)`.
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@mcp-data-agent/postgres",
3
+ "version": "0.1.0",
4
+ "description": "PostgreSQL adapter for MCP Data Agent. Created by Arslan Habib.",
5
+ "author": {
6
+ "name": "Arslan Habib"
7
+ },
8
+ "creator": "Arslan Habib",
9
+ "license": "UNLICENSED",
10
+ "type": "module",
11
+ "main": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "README.md"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsc -p tsconfig.json",
25
+ "clean": "rm -rf dist *.tsbuildinfo",
26
+ "typecheck": "tsc -p tsconfig.json --noEmit"
27
+ },
28
+ "engines": {
29
+ "node": ">=20"
30
+ },
31
+ "peerDependencies": {
32
+ "@mcp-data-agent/core": "0.1.0",
33
+ "pg": "^8.13.0"
34
+ },
35
+ "devDependencies": {
36
+ "@mcp-data-agent/core": "*",
37
+ "@types/pg": "^8.11.11",
38
+ "pg": "^8.14.1"
39
+ },
40
+ "keywords": [
41
+ "mcp-data-agent",
42
+ "postgresql",
43
+ "postgres",
44
+ "database-adapter"
45
+ ]
46
+ }
@@ -0,0 +1,358 @@
1
+ /**
2
+ * MCP Data Agent — @mcp-data-agent/postgres
3
+ * Created by Arslan Habib
4
+ *
5
+ * ---------------------------------------------------------------------------
6
+ * FILE: adapter.ts
7
+ * PURPOSE:
8
+ * PostgreSQL implementation of DatabaseAdapter. Responsibilities:
9
+ * 1) Inspect information_schema → normalized DatabaseSchema
10
+ * 2) Run validated read-only queries inside a READ ONLY transaction
11
+ * 3) Set session GUCs (app.tenant_id, app.user_id) for Postgres RLS
12
+ *
13
+ * SECURITY:
14
+ * Prefer a read-only DB role. Enable RLS policies that read
15
+ * current_setting('app.tenant_id', true) so tenant isolation is enforced
16
+ * at the database layer, not only in application code.
17
+ * ---------------------------------------------------------------------------
18
+ */
19
+
20
+ import type {
21
+ ColumnDataType,
22
+ DatabaseAdapter,
23
+ DatabaseSchema,
24
+ QueryRequest,
25
+ QueryResult,
26
+ RequestContext,
27
+ TableSchema,
28
+ } from "@mcp-data-agent/core";
29
+ import {
30
+ ConfigurationError,
31
+ getTenantColumn,
32
+ TenantIsolationError,
33
+ } from "@mcp-data-agent/core";
34
+ import pg from "pg";
35
+
36
+ const { Pool } = pg;
37
+
38
+ /** Configuration for createPostgresAdapter / PostgresAdapter. */
39
+ export interface PostgresAdapterOptions {
40
+ /** Connection string. Prefer env vars (DATABASE_URL) in production. */
41
+ connectionString?: string;
42
+ /** Reuse an existing pg Pool owned by the host app. */
43
+ pool?: pg.Pool;
44
+ /** Passed to `new Pool(...)` when connectionString is not set. */
45
+ poolConfig?: pg.PoolConfig;
46
+ /** Schemas to inspect. Default: ["public"]. */
47
+ schemas?: string[];
48
+ /**
49
+ * When true (default), require tenant context and set app.tenant_id /
50
+ * app.user_id session variables for RLS policies.
51
+ */
52
+ enforceTenant?: boolean;
53
+ }
54
+
55
+ /**
56
+ * Maps PostgreSQL udt_name values to the framework's normalized ColumnDataType.
57
+ * Keeps AI-facing schema DB-agnostic.
58
+ */
59
+ function mapDataType(udtName: string): ColumnDataType {
60
+ switch (udtName) {
61
+ case "int2":
62
+ case "int4":
63
+ case "int8":
64
+ case "numeric":
65
+ case "float4":
66
+ case "float8":
67
+ case "money":
68
+ return "number";
69
+ case "bool":
70
+ return "boolean";
71
+ case "date":
72
+ return "date";
73
+ case "timestamp":
74
+ case "timestamptz":
75
+ case "time":
76
+ case "timetz":
77
+ return "datetime";
78
+ case "json":
79
+ case "jsonb":
80
+ return "json";
81
+ case "bytea":
82
+ return "binary";
83
+ case "text":
84
+ case "varchar":
85
+ case "bpchar":
86
+ case "uuid":
87
+ case "citext":
88
+ return "string";
89
+ default:
90
+ return "unknown";
91
+ }
92
+ }
93
+
94
+ /**
95
+ * PostgreSQL DatabaseAdapter.
96
+ * Implements getSchema() and query() for @mcp-data-agent/core.
97
+ */
98
+ export class PostgresAdapter implements DatabaseAdapter {
99
+ readonly name = "postgres";
100
+ private readonly pool: pg.Pool;
101
+ private readonly schemas: string[];
102
+ private readonly enforceTenant: boolean;
103
+ /** True when this adapter created the pool and must close it. */
104
+ private readonly ownsPool: boolean;
105
+
106
+ constructor(options: PostgresAdapterOptions = {}) {
107
+ // Prefer an injected pool (host apps often share one across services)
108
+ if (options.pool) {
109
+ this.pool = options.pool;
110
+ this.ownsPool = false;
111
+ } else if (options.connectionString || options.poolConfig) {
112
+ this.pool = new Pool(
113
+ options.connectionString
114
+ ? { connectionString: options.connectionString }
115
+ : options.poolConfig,
116
+ );
117
+ this.ownsPool = true;
118
+ } else {
119
+ throw new ConfigurationError(
120
+ "PostgresAdapter requires connectionString, poolConfig, or an existing pool",
121
+ );
122
+ }
123
+ this.schemas = options.schemas ?? ["public"];
124
+ this.enforceTenant = options.enforceTenant ?? true;
125
+ }
126
+
127
+ /**
128
+ * Reads tables, columns, primary keys, and foreign keys from
129
+ * information_schema and returns a normalized DatabaseSchema.
130
+ */
131
+ async getSchema(): Promise<DatabaseSchema> {
132
+ const client = await this.pool.connect();
133
+ try {
134
+ // --- Base tables in configured schemas ---
135
+ const tablesResult = await client.query<{
136
+ table_schema: string;
137
+ table_name: string;
138
+ }>(
139
+ `
140
+ SELECT table_schema, table_name
141
+ FROM information_schema.tables
142
+ WHERE table_type = 'BASE TABLE'
143
+ AND table_schema = ANY($1::text[])
144
+ ORDER BY table_schema, table_name
145
+ `,
146
+ [this.schemas],
147
+ );
148
+
149
+ // --- Columns with native Postgres types ---
150
+ const columnsResult = await client.query<{
151
+ table_schema: string;
152
+ table_name: string;
153
+ column_name: string;
154
+ is_nullable: string;
155
+ udt_name: string;
156
+ ordinal_position: number;
157
+ }>(
158
+ `
159
+ SELECT table_schema, table_name, column_name, is_nullable, udt_name, ordinal_position
160
+ FROM information_schema.columns
161
+ WHERE table_schema = ANY($1::text[])
162
+ ORDER BY table_schema, table_name, ordinal_position
163
+ `,
164
+ [this.schemas],
165
+ );
166
+
167
+ // --- Primary key columns ---
168
+ const pkResult = await client.query<{
169
+ table_schema: string;
170
+ table_name: string;
171
+ column_name: string;
172
+ }>(
173
+ `
174
+ SELECT tc.table_schema, tc.table_name, kcu.column_name
175
+ FROM information_schema.table_constraints tc
176
+ JOIN information_schema.key_column_usage kcu
177
+ ON tc.constraint_name = kcu.constraint_name
178
+ AND tc.table_schema = kcu.table_schema
179
+ WHERE tc.constraint_type = 'PRIMARY KEY'
180
+ AND tc.table_schema = ANY($1::text[])
181
+ `,
182
+ [this.schemas],
183
+ );
184
+
185
+ // --- Foreign key relationships ---
186
+ const fkResult = await client.query<{
187
+ table_schema: string;
188
+ table_name: string;
189
+ column_name: string;
190
+ foreign_table_name: string;
191
+ foreign_column_name: string;
192
+ }>(
193
+ `
194
+ SELECT
195
+ tc.table_schema,
196
+ tc.table_name,
197
+ kcu.column_name,
198
+ ccu.table_name AS foreign_table_name,
199
+ ccu.column_name AS foreign_column_name
200
+ FROM information_schema.table_constraints tc
201
+ JOIN information_schema.key_column_usage kcu
202
+ ON tc.constraint_name = kcu.constraint_name
203
+ AND tc.table_schema = kcu.table_schema
204
+ JOIN information_schema.constraint_column_usage ccu
205
+ ON ccu.constraint_name = tc.constraint_name
206
+ AND ccu.table_schema = tc.table_schema
207
+ WHERE tc.constraint_type = 'FOREIGN KEY'
208
+ AND tc.table_schema = ANY($1::text[])
209
+ `,
210
+ [this.schemas],
211
+ );
212
+
213
+ // Index PK/FK for fast lookup while assembling ColumnSchema objects
214
+ const pkSet = new Set(
215
+ pkResult.rows.map(
216
+ (r) => `${r.table_schema}.${r.table_name}.${r.column_name}`,
217
+ ),
218
+ );
219
+ const fkMap = new Map(
220
+ fkResult.rows.map((r) => [
221
+ `${r.table_schema}.${r.table_name}.${r.column_name}`,
222
+ { table: r.foreign_table_name, column: r.foreign_column_name },
223
+ ]),
224
+ );
225
+
226
+ // Group columns by table
227
+ const columnsByTable = new Map<string, TableSchema["columns"]>();
228
+ for (const col of columnsResult.rows) {
229
+ const key = `${col.table_schema}.${col.table_name}`;
230
+ const list = columnsByTable.get(key) ?? [];
231
+ const full = `${col.table_schema}.${col.table_name}.${col.column_name}`;
232
+ const fk = fkMap.get(full);
233
+ list.push({
234
+ name: col.column_name,
235
+ dataType: mapDataType(col.udt_name),
236
+ nativeType: col.udt_name,
237
+ nullable: col.is_nullable === "YES",
238
+ isPrimaryKey: pkSet.has(full),
239
+ isForeignKey: Boolean(fk),
240
+ ...(fk ? { references: fk } : {}),
241
+ });
242
+ columnsByTable.set(key, list);
243
+ }
244
+
245
+ const tables: TableSchema[] = tablesResult.rows.map((t) => {
246
+ const key = `${t.table_schema}.${t.table_name}`;
247
+ const columns = columnsByTable.get(key) ?? [];
248
+ const primaryKey = columns.filter((c) => c.isPrimaryKey).map((c) => c.name);
249
+ return {
250
+ name: t.table_name,
251
+ schema: t.table_schema,
252
+ columns,
253
+ ...(primaryKey.length ? { primaryKey } : {}),
254
+ };
255
+ });
256
+
257
+ return {
258
+ dialect: "postgresql",
259
+ inspectedAt: new Date().toISOString(),
260
+ tables,
261
+ };
262
+ } finally {
263
+ // Always return the client to the pool
264
+ client.release();
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Executes a (already validated) read-only query.
270
+ * Opens BEGIN READ ONLY, sets tenant/user GUCs, applies statement_timeout,
271
+ * runs the query with bound params, then commits.
272
+ */
273
+ async query(
274
+ request: QueryRequest,
275
+ context: RequestContext,
276
+ ): Promise<QueryResult> {
277
+ if (this.enforceTenant) {
278
+ if (!context.tenant?.tenantId) {
279
+ throw new TenantIsolationError();
280
+ }
281
+ }
282
+
283
+ const client = await this.pool.connect();
284
+ const started = Date.now();
285
+ try {
286
+ // READ ONLY transaction: DB rejects writes even if SQL slipped through
287
+ await client.query("BEGIN READ ONLY");
288
+
289
+ if (this.enforceTenant) {
290
+ // Local-to-transaction GUCs for RLS:
291
+ // current_setting('app.tenant_id', true)
292
+ await client.query(`SELECT set_config('app.tenant_id', $1, true)`, [
293
+ context.tenant.tenantId,
294
+ ]);
295
+ await client.query(`SELECT set_config('app.user_id', $1, true)`, [
296
+ context.auth.userId,
297
+ ]);
298
+ }
299
+
300
+ if (request.timeoutMs) {
301
+ await client.query(`SET LOCAL statement_timeout = $1`, [
302
+ request.timeoutMs,
303
+ ]);
304
+ }
305
+
306
+ // Always use bound parameters — never interpolate AI text into SQL
307
+ const result = await client.query(request.sql, request.params ?? []);
308
+ await client.query("COMMIT");
309
+
310
+ // Apply row limit in memory as a second safety net
311
+ const limit = request.limit ?? result.rowCount ?? 0;
312
+ const rows = result.rows.slice(0, limit).map((row) => {
313
+ const record: Record<string, unknown> = {};
314
+ for (const [key, value] of Object.entries(row)) {
315
+ record[key] = value;
316
+ }
317
+ return record;
318
+ });
319
+
320
+ return {
321
+ columns: result.fields.map((f) => ({
322
+ name: f.name,
323
+ dataType: "unknown" as const,
324
+ })),
325
+ rows,
326
+ rowCount: rows.length,
327
+ truncated: (result.rowCount ?? 0) > rows.length,
328
+ executionMs: Date.now() - started,
329
+ };
330
+ } catch (error) {
331
+ try {
332
+ await client.query("ROLLBACK");
333
+ } catch {
334
+ // Ignore rollback errors; rethrow the original failure
335
+ }
336
+ throw error;
337
+ } finally {
338
+ client.release();
339
+ }
340
+ }
341
+
342
+ /** Closes the pool only if this adapter created it. */
343
+ async close(): Promise<void> {
344
+ if (this.ownsPool) {
345
+ await this.pool.end();
346
+ }
347
+ }
348
+ }
349
+
350
+ /** Convenience factory: createPostgresAdapter({ connectionString }). */
351
+ export function createPostgresAdapter(
352
+ options: PostgresAdapterOptions,
353
+ ): PostgresAdapter {
354
+ return new PostgresAdapter(options);
355
+ }
356
+
357
+ /** Re-export for host apps writing RLS policies. */
358
+ export { getTenantColumn };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * MCP Data Agent — @mcp-data-agent/postgres
3
+ * Created by Arslan Habib
4
+ *
5
+ * ---------------------------------------------------------------------------
6
+ * FILE: index.ts
7
+ * PURPOSE:
8
+ * Public entry point for the PostgreSQL adapter package.
9
+ *
10
+ * import { createPostgresAdapter } from "@mcp-data-agent/postgres";
11
+ * ---------------------------------------------------------------------------
12
+ */
13
+
14
+ export {
15
+ createPostgresAdapter,
16
+ getTenantColumn,
17
+ PostgresAdapter,
18
+ } from "./adapter.js";
19
+ export type { PostgresAdapterOptions } from "./adapter.js";
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist",
6
+ "composite": true
7
+ },
8
+ "include": ["src/**/*.ts"],
9
+ "exclude": ["src/**/*.test.ts", "dist"],
10
+ "references": [{ "path": "../core" }]
11
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "lib": ["ES2022"],
7
+ "types": ["node"],
8
+ "strict": true,
9
+ "noImplicitOverride": true,
10
+ "noUncheckedIndexedAccess": true,
11
+ "exactOptionalPropertyTypes": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "esModuleInterop": true,
14
+ "skipLibCheck": true,
15
+ "declaration": true,
16
+ "declarationMap": true,
17
+ "sourceMap": true,
18
+ "composite": true,
19
+ "rootDir": "src",
20
+ "outDir": "dist"
21
+ }
22
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ { "path": "./packages/core" },
5
+ { "path": "./packages/postgres" },
6
+ { "path": "./packages/mcp" }
7
+ ]
8
+ }
@@ -0,0 +1,14 @@
1
+ // MCP Data Agent — Created by Arslan Habib
2
+ //
3
+ // FILE: vitest.config.ts
4
+ // PURPOSE: Vitest config for package unit tests (colocated *.test.ts files).
5
+
6
+ import { defineConfig } from "vitest/config";
7
+
8
+ export default defineConfig({
9
+ test: {
10
+ // Pick up tests colocated with package source
11
+ include: ["packages/*/src/**/*.test.ts"],
12
+ environment: "node",
13
+ },
14
+ });