@vistal/mcp 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.
- package/README.md +111 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +21 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +65 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +52 -0
- package/dist/index.js.map +1 -0
- package/dist/policy.d.ts +14 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +44 -0
- package/dist/policy.js.map +1 -0
- package/dist/prepare.d.ts +17 -0
- package/dist/prepare.d.ts.map +1 -0
- package/dist/prepare.js +79 -0
- package/dist/prepare.js.map +1 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# @vistal/mcp
|
|
2
|
+
|
|
3
|
+
**Point it at a database URL. Claude talks to your data. No SQL, no code.**
|
|
4
|
+
|
|
5
|
+
A zero-config [MCP](https://modelcontextprotocol.io) server for any database
|
|
6
|
+
[vistal](https://github.com/vista-libs/vista) supports through Prisma
|
|
7
|
+
(PostgreSQL, MySQL, SQLite, SQL Server). Give it a `DATABASE_URL` and it:
|
|
8
|
+
|
|
9
|
+
1. introspects your live schema (`prisma db pull`) — no `schema.prisma` needed,
|
|
10
|
+
2. generates the typed tools an agent uses to query it,
|
|
11
|
+
3. serves them over MCP — **read-only by default**, scoped by your policies.
|
|
12
|
+
|
|
13
|
+
No SQL ever reaches the model, and it can only do what the policy allows.
|
|
14
|
+
|
|
15
|
+
## Quick start (Claude Code)
|
|
16
|
+
|
|
17
|
+
Add it to your `.mcp.json` — nothing to install or write:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"mcpServers": {
|
|
22
|
+
"db": {
|
|
23
|
+
"command": "npx",
|
|
24
|
+
"args": ["-y", "@vistal/mcp"],
|
|
25
|
+
"env": { "DATABASE_URL": "postgresql://user:pass@localhost:5432/mydb" }
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
That's it. Claude can now discover your tables (`list_resources`,
|
|
32
|
+
`describe_resource`) and read them (`query`, `get`, `aggregate`) — all
|
|
33
|
+
policy-gated, all without writing SQL.
|
|
34
|
+
|
|
35
|
+
You can also run it directly:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
DATABASE_URL="postgresql://…" npx @vistal/mcp
|
|
39
|
+
# or pass the URL as the first argument
|
|
40
|
+
npx @vistal/mcp "postgresql://…"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Defaults & safety
|
|
44
|
+
|
|
45
|
+
- **Read-only.** Out of the box only `query` / `get` / `aggregate` are exposed —
|
|
46
|
+
never `create` / `update` / `delete`. Writes require a policy file (below).
|
|
47
|
+
- **All tables, opt-out.** Every table is exposed; narrow it with allow/deny lists.
|
|
48
|
+
- **No SQL, no leaks.** The model calls typed tools; vistal turns them into scoped
|
|
49
|
+
queries server-side. Fields marked sensitive in your schema never reach it.
|
|
50
|
+
|
|
51
|
+
## Configuration
|
|
52
|
+
|
|
53
|
+
All via environment variables:
|
|
54
|
+
|
|
55
|
+
| Variable | Description |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| `DATABASE_URL` | Connection string (required; or pass as the first arg). |
|
|
58
|
+
| `VISTAL_PROVIDER` | `postgresql` \| `mysql` \| `sqlite` \| `sqlserver` \| `mongodb`. Inferred from the URL when omitted. |
|
|
59
|
+
| `VISTAL_TABLES` | Comma-separated allow-list. Only these tables are exposed. |
|
|
60
|
+
| `VISTAL_EXCLUDE` | Comma-separated deny-list, applied after the allow-list. |
|
|
61
|
+
| `VISTAL_POLICY_FILE` | Path to a policy module that takes full control (enables writes — see below). |
|
|
62
|
+
| `VISTAL_CONTEXT` | JSON policy context passed to your policy file. Defaults to `{}`. |
|
|
63
|
+
| `VISTAL_HTTP_PORT` | Serve over Streamable HTTP on this port instead of stdio. |
|
|
64
|
+
|
|
65
|
+
### Enabling writes / richer policies
|
|
66
|
+
|
|
67
|
+
For anything beyond read-only, point `VISTAL_POLICY_FILE` at a CommonJS module
|
|
68
|
+
that receives the configured vistal instance and declares
|
|
69
|
+
[policies](https://github.com/vista-libs/vista#policy-reference). It takes full
|
|
70
|
+
control of access (the allow/deny lists are then ignored):
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
// db-policy.cjs
|
|
74
|
+
module.exports = (vistal) => {
|
|
75
|
+
vistal.policy("orders", () => ({ read: true, write: true, delete: false }))
|
|
76
|
+
vistal.policy("users", () => ({ read: true, write: false, delete: false }))
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{ "env": {
|
|
82
|
+
"DATABASE_URL": "postgresql://…",
|
|
83
|
+
"VISTAL_POLICY_FILE": "./db-policy.cjs"
|
|
84
|
+
} }
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Use `VISTAL_CONTEXT` to feed runtime context (tenant, role, …) your policies read:
|
|
88
|
+
`"VISTAL_CONTEXT": "{\"tenant\":{\"id\":\"acme\"}}"`.
|
|
89
|
+
|
|
90
|
+
## How it works
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
Claude Code ─MCP─▶ @vistal/mcp
|
|
94
|
+
│ prisma db pull + generate → live schema + typed client
|
|
95
|
+
│ @vistal/core → policy engine (read-only default)
|
|
96
|
+
▼
|
|
97
|
+
@vistal/prisma → PrismaClient → your database
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The generated client lives in a temp directory and is removed on shutdown; your
|
|
101
|
+
project is never modified. Requires network access to the database and the
|
|
102
|
+
ability to run the bundled Prisma CLI.
|
|
103
|
+
|
|
104
|
+
## Need policies-in-code instead?
|
|
105
|
+
|
|
106
|
+
If you'd rather define your adapter and policies yourself and embed an MCP server
|
|
107
|
+
in your own app, use [`@vistal/mcp-sdk`](../mcp-sdk/README.md) directly.
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const config_1 = require("./config");
|
|
5
|
+
const index_1 = require("./index");
|
|
6
|
+
async function main() {
|
|
7
|
+
const config = (0, config_1.configFromEnv)(process.env, process.argv.slice(2));
|
|
8
|
+
const server = await (0, index_1.startVistalMcpServer)(config);
|
|
9
|
+
const shutdown = async () => {
|
|
10
|
+
await server.stop();
|
|
11
|
+
process.exit(0);
|
|
12
|
+
};
|
|
13
|
+
process.on("SIGINT", shutdown);
|
|
14
|
+
process.on("SIGTERM", shutdown);
|
|
15
|
+
}
|
|
16
|
+
main().catch((err) => {
|
|
17
|
+
// Never write errors to stdout — it's the MCP transport.
|
|
18
|
+
process.stderr.write(`[vistal] ${err instanceof Error ? err.message : String(err)}\n`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
});
|
|
21
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;AACA,qCAAwC;AACxC,mCAA8C;AAE9C,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,IAAA,sBAAa,EAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IAChE,MAAM,MAAM,GAAG,MAAM,IAAA,4BAAoB,EAAC,MAAM,CAAC,CAAA;IAEjD,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;QACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC,CAAA;IACD,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IAC9B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;AACjC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,yDAAyD;IACzD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACtF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type Provider = "postgresql" | "mysql" | "sqlite" | "sqlserver" | "mongodb";
|
|
2
|
+
export interface ServerConfig {
|
|
3
|
+
/** Database connection string. */
|
|
4
|
+
databaseUrl: string;
|
|
5
|
+
/** Prisma datasource provider. Inferred from the URL when omitted. */
|
|
6
|
+
provider?: Provider;
|
|
7
|
+
/** Path to a policy module that takes full control of access (optional). */
|
|
8
|
+
policyFile?: string;
|
|
9
|
+
/** Allow-list of resource (table) names. When set, only these are exposed. */
|
|
10
|
+
tables?: string[];
|
|
11
|
+
/** Deny-list of resource (table) names, applied after the allow-list. */
|
|
12
|
+
exclude?: string[];
|
|
13
|
+
/** Policy context object (JSON). Defaults to `{}`. */
|
|
14
|
+
context?: unknown;
|
|
15
|
+
/** When set, serve over Streamable HTTP on this port instead of stdio. */
|
|
16
|
+
httpPort?: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function inferProvider(url: string): Provider;
|
|
19
|
+
/**
|
|
20
|
+
* Build a {@link ServerConfig} from environment variables and CLI args.
|
|
21
|
+
* The connection string comes from `DATABASE_URL` or the first positional arg.
|
|
22
|
+
*/
|
|
23
|
+
export declare function configFromEnv(env: NodeJS.ProcessEnv, argv: string[]): ServerConfig;
|
|
24
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,SAAS,CAAA;AAElF,MAAM,WAAW,YAAY;IAC3B,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAA;IACnB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,sDAAsD;IACtD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAUD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAQnD;AAWD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,YAAY,CAoClF"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.inferProvider = inferProvider;
|
|
4
|
+
exports.configFromEnv = configFromEnv;
|
|
5
|
+
const PROVIDER_PATTERNS = [
|
|
6
|
+
[/^postgres(ql)?:\/\//i, "postgresql"],
|
|
7
|
+
[/^mysql:\/\//i, "mysql"],
|
|
8
|
+
[/^sqlserver:\/\//i, "sqlserver"],
|
|
9
|
+
[/^(file:|sqlite:)/i, "sqlite"],
|
|
10
|
+
[/^mongodb(\+srv)?:\/\//i, "mongodb"],
|
|
11
|
+
];
|
|
12
|
+
function inferProvider(url) {
|
|
13
|
+
for (const [pattern, provider] of PROVIDER_PATTERNS) {
|
|
14
|
+
if (pattern.test(url))
|
|
15
|
+
return provider;
|
|
16
|
+
}
|
|
17
|
+
throw new Error(`Could not infer the database provider from the connection string. ` +
|
|
18
|
+
`Set VISTAL_PROVIDER to one of: postgresql, mysql, sqlite, sqlserver, mongodb.`);
|
|
19
|
+
}
|
|
20
|
+
function csv(value) {
|
|
21
|
+
if (!value)
|
|
22
|
+
return undefined;
|
|
23
|
+
const items = value
|
|
24
|
+
.split(",")
|
|
25
|
+
.map((s) => s.trim())
|
|
26
|
+
.filter(Boolean);
|
|
27
|
+
return items.length > 0 ? items : undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Build a {@link ServerConfig} from environment variables and CLI args.
|
|
31
|
+
* The connection string comes from `DATABASE_URL` or the first positional arg.
|
|
32
|
+
*/
|
|
33
|
+
function configFromEnv(env, argv) {
|
|
34
|
+
const positional = argv.find((a) => !a.startsWith("-"));
|
|
35
|
+
const databaseUrl = env.DATABASE_URL ?? positional;
|
|
36
|
+
if (!databaseUrl) {
|
|
37
|
+
throw new Error("No database connection string. Set DATABASE_URL (or pass it as the first argument).");
|
|
38
|
+
}
|
|
39
|
+
const provider = env.VISTAL_PROVIDER
|
|
40
|
+
? env.VISTAL_PROVIDER
|
|
41
|
+
: inferProvider(databaseUrl);
|
|
42
|
+
let context = {};
|
|
43
|
+
if (env.VISTAL_CONTEXT) {
|
|
44
|
+
try {
|
|
45
|
+
context = JSON.parse(env.VISTAL_CONTEXT);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
throw new Error("VISTAL_CONTEXT must be valid JSON.");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const httpPort = env.VISTAL_HTTP_PORT ? Number(env.VISTAL_HTTP_PORT) : undefined;
|
|
52
|
+
if (httpPort !== undefined && Number.isNaN(httpPort)) {
|
|
53
|
+
throw new Error("VISTAL_HTTP_PORT must be a number.");
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
databaseUrl,
|
|
57
|
+
provider,
|
|
58
|
+
policyFile: env.VISTAL_POLICY_FILE,
|
|
59
|
+
tables: csv(env.VISTAL_TABLES),
|
|
60
|
+
exclude: csv(env.VISTAL_EXCLUDE),
|
|
61
|
+
context,
|
|
62
|
+
httpPort,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;AA2BA,sCAQC;AAeD,sCAoCC;AAnED,MAAM,iBAAiB,GAAyB;IAC9C,CAAC,sBAAsB,EAAE,YAAY,CAAC;IACtC,CAAC,cAAc,EAAE,OAAO,CAAC;IACzB,CAAC,kBAAkB,EAAE,WAAW,CAAC;IACjC,CAAC,mBAAmB,EAAE,QAAQ,CAAC;IAC/B,CAAC,wBAAwB,EAAE,SAAS,CAAC;CACtC,CAAA;AAED,SAAgB,aAAa,CAAC,GAAW;IACvC,KAAK,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,iBAAiB,EAAE,CAAC;QACpD,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,QAAQ,CAAA;IACxC,CAAC;IACD,MAAM,IAAI,KAAK,CACb,oEAAoE;QAClE,+EAA+E,CAClF,CAAA;AACH,CAAC;AAED,SAAS,GAAG,CAAC,KAAyB;IACpC,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAA;IAC5B,MAAM,KAAK,GAAG,KAAK;SAChB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CAAA;IAClB,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AAC7C,CAAC;AAED;;;GAGG;AACH,SAAgB,aAAa,CAAC,GAAsB,EAAE,IAAc;IAClE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAA;IACvD,MAAM,WAAW,GAAG,GAAG,CAAC,YAAY,IAAI,UAAU,CAAA;IAClD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,qFAAqF,CACtF,CAAA;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,CAAC,eAAe;QAClC,CAAC,CAAE,GAAG,CAAC,eAA4B;QACnC,CAAC,CAAC,aAAa,CAAC,WAAW,CAAC,CAAA;IAE9B,IAAI,OAAO,GAAY,EAAE,CAAA;IACzB,IAAI,GAAG,CAAC,cAAc,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAA;QACvD,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAChF,IAAI,QAAQ,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAA;IACvD,CAAC;IAED,OAAO;QACL,WAAW;QACX,QAAQ;QACR,UAAU,EAAE,GAAG,CAAC,kBAAkB;QAClC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC;QAC9B,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC;QAChC,OAAO;QACP,QAAQ;KACT,CAAA;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ServerConfig } from "./config";
|
|
2
|
+
export type { ServerConfig, Provider } from "./config";
|
|
3
|
+
export { configFromEnv, inferProvider } from "./config";
|
|
4
|
+
export interface RunningServer {
|
|
5
|
+
/** Disconnect the database and remove the generated temp client. */
|
|
6
|
+
stop: () => Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Boot a zero-config vistal MCP server against a live database: introspect the
|
|
10
|
+
* schema, build a policy-gated vistal instance (read-only by default), and serve
|
|
11
|
+
* it over stdio (or Streamable HTTP when `httpPort` is set).
|
|
12
|
+
*
|
|
13
|
+
* All progress is logged to stderr so stdout stays a clean MCP channel.
|
|
14
|
+
*/
|
|
15
|
+
export declare function startVistalMcpServer(config: ServerConfig): Promise<RunningServer>;
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAM5C,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAEvD,MAAM,WAAW,aAAa;IAC5B,oEAAoE;IACpE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAuCvF"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.inferProvider = exports.configFromEnv = void 0;
|
|
4
|
+
exports.startVistalMcpServer = startVistalMcpServer;
|
|
5
|
+
const prisma_1 = require("@vistal/prisma");
|
|
6
|
+
const mcp_sdk_1 = require("@vistal/mcp-sdk");
|
|
7
|
+
const prepare_1 = require("./prepare");
|
|
8
|
+
const policy_1 = require("./policy");
|
|
9
|
+
var config_1 = require("./config");
|
|
10
|
+
Object.defineProperty(exports, "configFromEnv", { enumerable: true, get: function () { return config_1.configFromEnv; } });
|
|
11
|
+
Object.defineProperty(exports, "inferProvider", { enumerable: true, get: function () { return config_1.inferProvider; } });
|
|
12
|
+
/**
|
|
13
|
+
* Boot a zero-config vistal MCP server against a live database: introspect the
|
|
14
|
+
* schema, build a policy-gated vistal instance (read-only by default), and serve
|
|
15
|
+
* it over stdio (or Streamable HTTP when `httpPort` is set).
|
|
16
|
+
*
|
|
17
|
+
* All progress is logged to stderr so stdout stays a clean MCP channel.
|
|
18
|
+
*/
|
|
19
|
+
async function startVistalMcpServer(config) {
|
|
20
|
+
const provider = config.provider ?? "postgresql";
|
|
21
|
+
const prepared = await (0, prepare_1.prepareDatabase)(config.databaseUrl, provider);
|
|
22
|
+
const vistal = (0, prisma_1.createVistal)(prepared.prisma, {
|
|
23
|
+
schemaPath: prepared.schemaPath,
|
|
24
|
+
defaultPolicy: "deny-all",
|
|
25
|
+
});
|
|
26
|
+
const { resources } = await (0, policy_1.applyAccess)(vistal, config);
|
|
27
|
+
process.stderr.write(`[vistal] Exposing ${resources.length} resource(s)` +
|
|
28
|
+
(config.policyFile ? " (policy file in control)" : " (read-only)") +
|
|
29
|
+
`: ${resources.join(", ") || "(none)"}\n`);
|
|
30
|
+
const options = {
|
|
31
|
+
context: () => config.context ?? {},
|
|
32
|
+
toolOptions: { resources },
|
|
33
|
+
};
|
|
34
|
+
if (config.httpPort !== undefined) {
|
|
35
|
+
await (0, mcp_sdk_1.startHttpServer)(vistal, { ...options, port: config.httpPort });
|
|
36
|
+
process.stderr.write(`[vistal] MCP server listening on http://localhost:${config.httpPort}/mcp\n`);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
await (0, mcp_sdk_1.startStdioServer)(vistal, options);
|
|
40
|
+
process.stderr.write("[vistal] MCP server ready on stdio.\n");
|
|
41
|
+
}
|
|
42
|
+
const stop = async () => {
|
|
43
|
+
try {
|
|
44
|
+
await prepared.prisma.$disconnect();
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
prepared.cleanup();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
return { stop };
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AA0BA,oDAuCC;AAhED,2CAA6C;AAC7C,6CAAmE;AACnE,uCAA2C;AAC3C,qCAAsC;AAQtC,mCAAuD;AAA9C,uGAAA,aAAa,OAAA;AAAE,uGAAA,aAAa,OAAA;AAOrC;;;;;;GAMG;AACI,KAAK,UAAU,oBAAoB,CAAC,MAAoB;IAC7D,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,YAAY,CAAA;IAChD,MAAM,QAAQ,GAAG,MAAM,IAAA,yBAAe,EAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;IAEpE,MAAM,MAAM,GAAG,IAAA,qBAAY,EAAoB,QAAQ,CAAC,MAAM,EAAE;QAC9D,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,aAAa,EAAE,UAAU;KAC1B,CAAC,CAAA;IAEF,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,IAAA,oBAAW,EAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,qBAAqB,SAAS,CAAC,MAAM,cAAc;QACjD,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,cAAc,CAAC;QAClE,KAAK,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,IAAI,CAC5C,CAAA;IAED,MAAM,OAAO,GAAG;QACd,OAAO,EAAE,GAAQ,EAAE,CAAE,MAAM,CAAC,OAAe,IAAI,EAAE;QACjD,WAAW,EAAE,EAAE,SAAS,EAAE;KAC3B,CAAA;IAED,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAClC,MAAM,IAAA,yBAAe,EAAC,MAAM,EAAE,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;QACpE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,qDAAqD,MAAM,CAAC,QAAQ,QAAQ,CAC7E,CAAA;IACH,CAAC;SAAM,CAAC;QACN,MAAM,IAAA,0BAAgB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACvC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAA;IAC/D,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE;QACtB,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,CAAA;QACrC,CAAC;gBAAS,CAAC;YACT,QAAQ,CAAC,OAAO,EAAE,CAAA;QACpB,CAAC;IACH,CAAC,CAAA;IACD,OAAO,EAAE,IAAI,EAAE,CAAA;AACjB,CAAC"}
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Vistal } from "@vistal/core";
|
|
2
|
+
import type { ServerConfig } from "./config";
|
|
3
|
+
type AnyVistal = Vistal<any, any>;
|
|
4
|
+
/**
|
|
5
|
+
* Apply access rules to the vistal instance and return the set of resources to
|
|
6
|
+
* expose as tools. When a policy file is supplied it takes full control;
|
|
7
|
+
* otherwise the default is read-only (query/get/aggregate, never write/delete),
|
|
8
|
+
* scoped by the optional table allow/deny lists.
|
|
9
|
+
*/
|
|
10
|
+
export declare function applyAccess(vistal: AnyVistal, config: ServerConfig): Promise<{
|
|
11
|
+
resources: string[];
|
|
12
|
+
}>;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAG5C,KAAK,SAAS,GAAG,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;AA4BjC;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA2BlC"}
|
package/dist/policy.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyAccess = applyAccess;
|
|
4
|
+
const node_path_1 = require("node:path");
|
|
5
|
+
function loadPolicyFile(path, vistal) {
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
7
|
+
const mod = require((0, node_path_1.resolve)(path));
|
|
8
|
+
const fn = typeof mod === "function" ? mod : (mod.default ?? mod.applyPolicies);
|
|
9
|
+
if (typeof fn !== "function") {
|
|
10
|
+
throw new Error(`Policy file "${path}" must export a function (default export or \`applyPolicies\`).`);
|
|
11
|
+
}
|
|
12
|
+
fn(vistal);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Apply access rules to the vistal instance and return the set of resources to
|
|
16
|
+
* expose as tools. When a policy file is supplied it takes full control;
|
|
17
|
+
* otherwise the default is read-only (query/get/aggregate, never write/delete),
|
|
18
|
+
* scoped by the optional table allow/deny lists.
|
|
19
|
+
*/
|
|
20
|
+
async function applyAccess(vistal, config) {
|
|
21
|
+
const all = await vistal.resources();
|
|
22
|
+
if (config.policyFile) {
|
|
23
|
+
loadPolicyFile(config.policyFile, vistal);
|
|
24
|
+
// The policy file owns access; expose everything it permits (vistal suppresses
|
|
25
|
+
// tools for resources it denies). Allow/deny lists are ignored in this mode.
|
|
26
|
+
return { resources: all };
|
|
27
|
+
}
|
|
28
|
+
// Default: read-only everywhere.
|
|
29
|
+
vistal.policy("*", () => ({ read: true, aggregate: true, write: false, delete: false }));
|
|
30
|
+
// Table scoping: allow-list first (if given), then deny-list.
|
|
31
|
+
let allowed = config.tables ? all.filter((t) => config.tables.includes(t)) : all;
|
|
32
|
+
if (config.exclude)
|
|
33
|
+
allowed = allowed.filter((t) => !config.exclude.includes(t));
|
|
34
|
+
// Authoritatively deny everything not allowed, so a model can't reach an
|
|
35
|
+
// excluded table even by passing its name to a consolidated verb.
|
|
36
|
+
const allowedSet = new Set(allowed);
|
|
37
|
+
for (const name of all) {
|
|
38
|
+
if (!allowedSet.has(name)) {
|
|
39
|
+
vistal.policy(name, () => ({ read: false, write: false, delete: false }));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { resources: allowed };
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":";;AAuCA,kCA8BC;AArED,yCAAmC;AAqBnC,SAAS,cAAc,CAAC,IAAY,EAAE,MAAiB;IACrD,8DAA8D;IAC9D,MAAM,GAAG,GAAG,OAAO,CAAC,IAAA,mBAAO,EAAC,IAAI,CAAC,CAAiB,CAAA;IAClD,MAAM,EAAE,GAAG,OAAO,GAAG,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,aAAa,CAAC,CAAA;IAC/E,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,gBAAgB,IAAI,iEAAiE,CACtF,CAAA;IACH,CAAC;IACD,EAAE,CAAC,MAAM,CAAC,CAAA;AACZ,CAAC;AAED;;;;;GAKG;AACI,KAAK,UAAU,WAAW,CAC/B,MAAiB,EACjB,MAAoB;IAEpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAA;IAEpC,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,cAAc,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;QACzC,+EAA+E;QAC/E,6EAA6E;QAC7E,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,CAAA;IAC3B,CAAC;IAED,iCAAiC;IACjC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;IAExF,8DAA8D;IAC9D,IAAI,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,MAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;IACzF,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,OAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAA;IAEzF,yEAAyE;IACzE,kEAAkE;IAClE,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAA;IACnC,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;QAC3E,CAAC;IACH,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAA;AAC/B,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PrismaClient } from "@prisma/client";
|
|
2
|
+
import type { Provider } from "./config";
|
|
3
|
+
export interface PreparedDatabase {
|
|
4
|
+
prisma: PrismaClient;
|
|
5
|
+
schemaPath: string;
|
|
6
|
+
/** Remove the generated client + schema temp dir. */
|
|
7
|
+
cleanup: () => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Introspect a live database into a throwaway Prisma schema + client, returning
|
|
11
|
+
* a ready `PrismaClient`. This is what makes the server "zero-config": no
|
|
12
|
+
* `schema.prisma` and no generated client need to exist ahead of time.
|
|
13
|
+
*
|
|
14
|
+
* Diagnostics are written to stderr so stdout stays clean for the MCP transport.
|
|
15
|
+
*/
|
|
16
|
+
export declare function prepareDatabase(databaseUrl: string, provider: Provider): Promise<PreparedDatabase>;
|
|
17
|
+
//# sourceMappingURL=prepare.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepare.d.ts","sourceRoot":"","sources":["../src/prepare.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAWxC,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,YAAY,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,qDAAqD;IACrD,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAqBD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,gBAAgB,CAAC,CA4C3B"}
|
package/dist/prepare.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.prepareDatabase = prepareDatabase;
|
|
4
|
+
const node_child_process_1 = require("node:child_process");
|
|
5
|
+
const node_fs_1 = require("node:fs");
|
|
6
|
+
const node_path_1 = require("node:path");
|
|
7
|
+
// The node_modules directory that holds @prisma/client. We generate the throwaway
|
|
8
|
+
// client *inside* it so the generated runtime resolves @prisma/client normally —
|
|
9
|
+
// generating into an unrelated temp dir makes Prisma walk up to "/", fail to find
|
|
10
|
+
// the installed prisma, and attempt a detached auto-install.
|
|
11
|
+
function clientBaseDir() {
|
|
12
|
+
const pkg = require.resolve("@prisma/client/package.json");
|
|
13
|
+
return (0, node_path_1.dirname)((0, node_path_1.dirname)((0, node_path_1.dirname)(pkg))); // .../node_modules/@prisma/client/package.json → .../node_modules
|
|
14
|
+
}
|
|
15
|
+
// Resolve the locally-installed Prisma CLI entrypoint so we can run it with the
|
|
16
|
+
// current node binary — more reliable than relying on `npx` resolution.
|
|
17
|
+
function resolvePrismaCli() {
|
|
18
|
+
const pkgJsonPath = require.resolve("prisma/package.json");
|
|
19
|
+
const pkg = require("prisma/package.json");
|
|
20
|
+
const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.prisma;
|
|
21
|
+
if (!binRel)
|
|
22
|
+
throw new Error("Could not locate the Prisma CLI entrypoint.");
|
|
23
|
+
return (0, node_path_1.join)((0, node_path_1.dirname)(pkgJsonPath), binRel);
|
|
24
|
+
}
|
|
25
|
+
// Run a Prisma CLI command. Child stdout is routed to *our* stderr so it never
|
|
26
|
+
// pollutes the MCP stdio channel (which speaks JSON-RPC on stdout).
|
|
27
|
+
function runPrisma(cliPath, args, env) {
|
|
28
|
+
(0, node_child_process_1.execFileSync)(process.execPath, [cliPath, ...args], {
|
|
29
|
+
env,
|
|
30
|
+
stdio: ["ignore", 2, 2],
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Introspect a live database into a throwaway Prisma schema + client, returning
|
|
35
|
+
* a ready `PrismaClient`. This is what makes the server "zero-config": no
|
|
36
|
+
* `schema.prisma` and no generated client need to exist ahead of time.
|
|
37
|
+
*
|
|
38
|
+
* Diagnostics are written to stderr so stdout stays clean for the MCP transport.
|
|
39
|
+
*/
|
|
40
|
+
async function prepareDatabase(databaseUrl, provider) {
|
|
41
|
+
const dir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)(clientBaseDir(), ".vistal-mcp-"));
|
|
42
|
+
const schemaPath = (0, node_path_1.join)(dir, "schema.prisma");
|
|
43
|
+
const clientOutput = (0, node_path_1.join)(dir, "client");
|
|
44
|
+
const schema = `generator client {
|
|
45
|
+
provider = "prisma-client-js"
|
|
46
|
+
output = "${clientOutput.replace(/\\/g, "\\\\")}"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
datasource db {
|
|
50
|
+
provider = "${provider}"
|
|
51
|
+
url = env("DATABASE_URL")
|
|
52
|
+
}
|
|
53
|
+
`;
|
|
54
|
+
(0, node_fs_1.writeFileSync)(schemaPath, schema);
|
|
55
|
+
const env = { ...process.env, DATABASE_URL: databaseUrl };
|
|
56
|
+
const cli = resolvePrismaCli();
|
|
57
|
+
process.stderr.write("[vistal] Introspecting database schema…\n");
|
|
58
|
+
runPrisma(cli, ["db", "pull", "--schema", schemaPath], env);
|
|
59
|
+
process.stderr.write("[vistal] Generating client…\n");
|
|
60
|
+
runPrisma(cli, ["generate", "--schema", schemaPath], env);
|
|
61
|
+
const { PrismaClient: GeneratedClient } = require(clientOutput);
|
|
62
|
+
// Pass the URL straight to the client. The schema's datasource reads
|
|
63
|
+
// env("DATABASE_URL"), which isn't set in this process — and this also avoids
|
|
64
|
+
// ever writing the connection string to disk.
|
|
65
|
+
const prisma = new GeneratedClient({ datasources: { db: { url: databaseUrl } } });
|
|
66
|
+
return {
|
|
67
|
+
prisma,
|
|
68
|
+
schemaPath,
|
|
69
|
+
cleanup: () => {
|
|
70
|
+
try {
|
|
71
|
+
(0, node_fs_1.rmSync)(dir, { recursive: true, force: true });
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// best-effort temp cleanup
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=prepare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepare.js","sourceRoot":"","sources":["../src/prepare.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vistal/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Zero-config MCP server for any database — point it at a DATABASE_URL and let coding agents (Claude Code) query it safely, no SQL and no code",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"mcp",
|
|
7
|
+
"model context protocol",
|
|
8
|
+
"claude code",
|
|
9
|
+
"database",
|
|
10
|
+
"postgres",
|
|
11
|
+
"mysql",
|
|
12
|
+
"sqlite",
|
|
13
|
+
"prisma",
|
|
14
|
+
"ai agent",
|
|
15
|
+
"row-level security",
|
|
16
|
+
"cli"
|
|
17
|
+
],
|
|
18
|
+
"author": "Nicolò D'Addabbo <nicolodaddabbo@gmail.com> (https://nicolod.com)",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/vista-libs/vista.git",
|
|
23
|
+
"directory": "packages/mcp"
|
|
24
|
+
},
|
|
25
|
+
"main": "dist/index.js",
|
|
26
|
+
"types": "dist/index.d.ts",
|
|
27
|
+
"bin": {
|
|
28
|
+
"vistal-mcp": "dist/cli.js"
|
|
29
|
+
},
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"require": "./dist/index.js",
|
|
33
|
+
"types": "./dist/index.d.ts"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@prisma/client": "^5.0.0",
|
|
44
|
+
"@vistal/core": "*",
|
|
45
|
+
"@vistal/mcp-sdk": "*",
|
|
46
|
+
"@vistal/prisma": "*",
|
|
47
|
+
"prisma": "^5.0.0"
|
|
48
|
+
}
|
|
49
|
+
}
|