@vaiftech/mcp 1.0.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 +182 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +24 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# @vaiftech/mcp
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) server for VAIF Studio. Connect Claude Code, Cursor, or any MCP-compatible AI assistant directly to your VAIF project to query databases, manage storage, invoke functions, and inspect schemas.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @vaiftech/mcp
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or run directly with npx:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @vaiftech/mcp --api-key vk_xxx --project-id proj_xxx
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage with Claude Code
|
|
18
|
+
|
|
19
|
+
Add the VAIF MCP server to Claude Code:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
claude mcp add vaif-studio -- npx @vaiftech/mcp --api-key vk_xxx --project-id proj_xxx
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or with environment variables:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
export VAIF_API_KEY=vk_xxx
|
|
29
|
+
export VAIF_PROJECT_ID=proj_xxx
|
|
30
|
+
claude mcp add vaif-studio -- npx @vaiftech/mcp
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Configuration
|
|
34
|
+
|
|
35
|
+
Configuration is resolved in priority order:
|
|
36
|
+
|
|
37
|
+
### 1. CLI Flags (highest priority)
|
|
38
|
+
|
|
39
|
+
| Flag | Description |
|
|
40
|
+
|------|-------------|
|
|
41
|
+
| `--api-key <key>` | VAIF API key (`vk_...`) |
|
|
42
|
+
| `--project-id <id>` | VAIF project ID (`proj_...`) |
|
|
43
|
+
| `--api-url <url>` | API URL (default: `https://api.vaif.studio`) |
|
|
44
|
+
| `--auth-token <token>` | JWT auth token |
|
|
45
|
+
|
|
46
|
+
### 2. Environment Variables
|
|
47
|
+
|
|
48
|
+
| Variable | Description |
|
|
49
|
+
|----------|-------------|
|
|
50
|
+
| `VAIF_API_KEY` | API key |
|
|
51
|
+
| `VAIF_PROJECT_ID` | Project ID |
|
|
52
|
+
| `VAIF_API_URL` | API URL |
|
|
53
|
+
| `VAIF_AUTH_TOKEN` | Auth token |
|
|
54
|
+
|
|
55
|
+
### 3. Local Config File
|
|
56
|
+
|
|
57
|
+
`vaif.config.json` in the current working directory:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"projectId": "proj_xxx",
|
|
62
|
+
"api": {
|
|
63
|
+
"apiKey": "vk_xxx"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 4. User Auth File (lowest priority)
|
|
69
|
+
|
|
70
|
+
`~/.vaif/auth.json`:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"token": "eyJhbG...",
|
|
75
|
+
"projectId": "proj_xxx"
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Authentication
|
|
80
|
+
|
|
81
|
+
The server supports two authentication modes:
|
|
82
|
+
|
|
83
|
+
- **API Key** (`--api-key` / `VAIF_API_KEY`): Used for data plane operations (CRUD on database tables via generated REST endpoints).
|
|
84
|
+
- **Auth Token** (`--auth-token` / `VAIF_AUTH_TOKEN`): Used for control plane operations (schema introspection, storage, functions, project info).
|
|
85
|
+
|
|
86
|
+
You can provide both for full access to all tools, or just one depending on your needs.
|
|
87
|
+
|
|
88
|
+
## Available Tools
|
|
89
|
+
|
|
90
|
+
### Database
|
|
91
|
+
|
|
92
|
+
| Tool | Description |
|
|
93
|
+
|------|-------------|
|
|
94
|
+
| `list_tables` | List all tables in the project database |
|
|
95
|
+
| `describe_table` | Describe a table's columns, types, and constraints |
|
|
96
|
+
| `query_rows` | Query rows with filtering, sorting, and pagination |
|
|
97
|
+
| `insert_row` | Insert a new row into a table |
|
|
98
|
+
| `update_row` | Update an existing row by ID |
|
|
99
|
+
| `delete_row` | Delete a row by ID |
|
|
100
|
+
|
|
101
|
+
### Storage
|
|
102
|
+
|
|
103
|
+
| Tool | Description |
|
|
104
|
+
|------|-------------|
|
|
105
|
+
| `list_buckets` | List all storage buckets |
|
|
106
|
+
| `list_files` | List files in a bucket with optional path prefix |
|
|
107
|
+
| `get_signed_url` | Generate a temporary signed download URL |
|
|
108
|
+
|
|
109
|
+
### Functions
|
|
110
|
+
|
|
111
|
+
| Tool | Description |
|
|
112
|
+
|------|-------------|
|
|
113
|
+
| `list_functions` | List all serverless functions |
|
|
114
|
+
| `invoke_function` | Invoke a function with an optional JSON payload |
|
|
115
|
+
|
|
116
|
+
### Schema
|
|
117
|
+
|
|
118
|
+
| Tool | Description |
|
|
119
|
+
|------|-------------|
|
|
120
|
+
| `get_schema` | Get the full database schema as JSON |
|
|
121
|
+
|
|
122
|
+
## Available Resources
|
|
123
|
+
|
|
124
|
+
| Resource | URI | Description |
|
|
125
|
+
|----------|-----|-------------|
|
|
126
|
+
| Schema | `vaif://schema` | Full database schema (tables, columns, types, relationships) |
|
|
127
|
+
| Project Info | `vaif://project-info` | Project metadata (name, region, settings) |
|
|
128
|
+
|
|
129
|
+
## Examples
|
|
130
|
+
|
|
131
|
+
### Query database rows
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
> Use the query_rows tool to get the 10 most recent users
|
|
135
|
+
|
|
136
|
+
Using query_rows with table="users", limit=10, order_by="created_at", order="desc"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Filter with operators
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
> Find all orders where total is greater than 100
|
|
143
|
+
|
|
144
|
+
Using query_rows with table="orders", filter={"total.gt": "100"}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Inspect schema
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
> What tables are in my database?
|
|
151
|
+
|
|
152
|
+
Using list_tables
|
|
153
|
+
→ ["users", "orders", "products", "categories"]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Invoke a function
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
> Run the send-welcome-email function for user 123
|
|
160
|
+
|
|
161
|
+
Using invoke_function with functionId="fn_abc", payload={"userId": "123"}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Development
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Install dependencies
|
|
168
|
+
pnpm install
|
|
169
|
+
|
|
170
|
+
# Build
|
|
171
|
+
pnpm build
|
|
172
|
+
|
|
173
|
+
# Watch mode
|
|
174
|
+
pnpm dev
|
|
175
|
+
|
|
176
|
+
# Type check
|
|
177
|
+
pnpm typecheck
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## License
|
|
181
|
+
|
|
182
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {McpServer}from'@modelcontextprotocol/sdk/server/mcp.js';import {z}from'zod';import {readFileSync}from'fs';import {join}from'path';import {homedir}from'os';var P=Object.defineProperty;var E=(r,t,e)=>t in r?P(r,t,{enumerable:true,configurable:true,writable:true,value:e}):r[t]=e;var l=(r,t,e)=>E(r,typeof t!="symbol"?t+"":t,e);var f=class{constructor(t){l(this,"apiUrl");l(this,"projectId");l(this,"apiKey");l(this,"authToken");this.apiUrl=t.apiUrl.replace(/\/+$/,""),this.projectId=t.projectId,this.apiKey=t.apiKey,this.authToken=t.authToken;}async dataPlane(t,e,o){if(!this.apiKey)throw new Error("API key is required for data plane requests. Set --api-key or VAIF_API_KEY.");let n=`${this.apiUrl}${e}`,i={"Content-Type":"application/json","x-vaif-key":this.apiKey},s=await fetch(n,{method:t,headers:i,body:o?JSON.stringify(o):void 0});if(!s.ok){let c=await s.text().catch(()=>"");throw new Error(`Data plane ${t} ${e} failed (${s.status}): ${c}`)}let p=await s.text();if(p)return JSON.parse(p)}async controlPlane(t,e,o){if(!this.authToken)throw new Error("Auth token is required for control plane requests. Set --auth-token or VAIF_AUTH_TOKEN.");let n=`${this.apiUrl}${e}`,i={"Content-Type":"application/json",Authorization:`Bearer ${this.authToken}`},s=await fetch(n,{method:t,headers:i,body:o?JSON.stringify(o):void 0});if(!s.ok){let c=await s.text().catch(()=>"");throw new Error(`Control plane ${t} ${e} failed (${s.status}): ${c}`)}let p=await s.text();if(p)return JSON.parse(p)}};function m(r,t){r.tool("list_tables","List all tables in the VAIF project database. Returns table names and basic metadata.",{},async()=>{try{let o=((await t.controlPlane("GET",`/schema-engine/introspect/${t.projectId}`)).tables||[]).map(n=>n.name);return {content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(e){return {content:[{type:"text",text:`Error listing tables: ${e instanceof Error?e.message:String(e)}`}]}}}),r.tool("describe_table","Describe a specific table's columns, types, constraints, and relationships.",{table:z.string().describe("The name of the table to describe")},async({table:e})=>{try{let n=((await t.controlPlane("GET",`/schema-engine/introspect/${t.projectId}`)).tables||[]).find(i=>i.name===e);return n?{content:[{type:"text",text:JSON.stringify(n,null,2)}]}:{content:[{type:"text",text:`Table "${e}" not found. Use list_tables to see available tables.`}]}}catch(o){return {content:[{type:"text",text:`Error describing table: ${o instanceof Error?o.message:String(o)}`}]}}}),r.tool("query_rows","Query rows from a table with optional filtering, sorting, and pagination. Filter operators: eq, neq, gt, lt, gte, lte, in, like, ilike, is.",{table:z.string().describe("The table to query"),filter:z.record(z.string()).optional().describe('Filter conditions as key-value pairs. Use "field" for equality or "field.op" for operators (e.g. "age.gt": "18", "name.like": "%john%")'),limit:z.number().optional().describe("Maximum number of rows to return (default: 50)"),offset:z.number().optional().describe("Number of rows to skip for pagination"),order_by:z.string().optional().describe("Column name to order results by"),order:z.enum(["asc","desc"]).optional().describe("Sort direction (asc or desc)")},async({table:e,filter:o,limit:n,offset:i,order_by:s,order:p})=>{try{let c=new URLSearchParams;if(o)for(let[j,v]of Object.entries(o))c.append(`filter[${j}]`,v);n!==void 0&&c.append("limit",String(n)),i!==void 0&&c.append("offset",String(i)),s&&c.append("order_by",s),p&&c.append("order",p);let g=c.toString(),w=`/generated/${e}${g?`?${g}`:""}`,A=await t.dataPlane("GET",w);return {content:[{type:"text",text:JSON.stringify(A,null,2)}]}}catch(c){return {content:[{type:"text",text:`Error querying rows: ${c instanceof Error?c.message:String(c)}`}]}}}),r.tool("insert_row","Insert a new row into a table. Returns the created row with generated fields (id, timestamps, etc).",{table:z.string().describe("The table to insert into"),data:z.record(z.any()).describe("Column values for the new row")},async({table:e,data:o})=>{try{let n=await t.dataPlane("POST",`/generated/${e}`,o);return {content:[{type:"text",text:JSON.stringify(n,null,2)}]}}catch(n){return {content:[{type:"text",text:`Error inserting row: ${n instanceof Error?n.message:String(n)}`}]}}}),r.tool("update_row","Update an existing row by ID. Returns the updated row.",{table:z.string().describe("The table containing the row"),id:z.string().describe("The ID of the row to update"),data:z.record(z.any()).describe("Column values to update")},async({table:e,id:o,data:n})=>{try{let i=await t.dataPlane("PATCH",`/generated/${e}/${o}`,n);return {content:[{type:"text",text:JSON.stringify(i,null,2)}]}}catch(i){return {content:[{type:"text",text:`Error updating row: ${i instanceof Error?i.message:String(i)}`}]}}}),r.tool("delete_row","Delete a row by ID. Returns confirmation of the deletion.",{table:z.string().describe("The table containing the row"),id:z.string().describe("The ID of the row to delete")},async({table:e,id:o})=>{try{let n=await t.dataPlane("DELETE",`/generated/${e}/${o}`);return {content:[{type:"text",text:JSON.stringify(n??{success:!0,deleted:o},null,2)}]}}catch(n){return {content:[{type:"text",text:`Error deleting row: ${n instanceof Error?n.message:String(n)}`}]}}});}function h(r,t){r.tool("list_buckets","List all storage buckets in the VAIF project.",{},async()=>{try{let e=await t.controlPlane("GET",`/storage/buckets?projectId=${t.projectId}`);return {content:[{type:"text",text:JSON.stringify(e,null,2)}]}}catch(e){return {content:[{type:"text",text:`Error listing buckets: ${e instanceof Error?e.message:String(e)}`}]}}}),r.tool("list_files","List files in a storage bucket, optionally filtered by path prefix.",{bucket:z.string().describe("The bucket name"),path:z.string().optional().describe("Path prefix to filter files (e.g. 'images/avatars/')")},async({bucket:e,path:o})=>{try{let n=new URLSearchParams;o&&n.append("prefix",o);let i=n.toString(),s=`/storage/files/${t.projectId}/${e}${i?`?${i}`:""}`,p=await t.controlPlane("GET",s);return {content:[{type:"text",text:JSON.stringify(p,null,2)}]}}catch(n){return {content:[{type:"text",text:`Error listing files: ${n instanceof Error?n.message:String(n)}`}]}}}),r.tool("get_signed_url","Generate a signed download URL for a file in storage. The URL is temporary and expires after the specified duration.",{bucket:z.string().describe("The bucket name"),path:z.string().describe("Path to the file within the bucket"),expiresIn:z.number().optional().describe("URL expiration time in seconds (default: 3600)")},async({bucket:e,path:o,expiresIn:n})=>{try{let i=await t.controlPlane("POST","/storage/download",{bucket:e,path:o,expiresIn:n});return {content:[{type:"text",text:JSON.stringify(i,null,2)}]}}catch(i){return {content:[{type:"text",text:`Error getting signed URL: ${i instanceof Error?i.message:String(i)}`}]}}});}function y(r,t){r.tool("list_functions","List all serverless functions deployed in the VAIF project.",{},async()=>{try{let e=await t.controlPlane("GET",`/functions/project/${t.projectId}`);return {content:[{type:"text",text:JSON.stringify(e,null,2)}]}}catch(e){return {content:[{type:"text",text:`Error listing functions: ${e instanceof Error?e.message:String(e)}`}]}}}),r.tool("invoke_function","Invoke a serverless function by ID with an optional JSON payload. Returns the function's response.",{functionId:z.string().describe("The ID of the function to invoke"),payload:z.record(z.any()).optional().describe("JSON payload to pass to the function")},async({functionId:e,payload:o})=>{try{let n=await t.controlPlane("POST",`/functions/${e}/invoke`,o??{});return {content:[{type:"text",text:JSON.stringify(n,null,2)}]}}catch(n){return {content:[{type:"text",text:`Error invoking function: ${n instanceof Error?n.message:String(n)}`}]}}});}function b(r,t){r.tool("get_schema","Get the full database schema for the VAIF project, including all tables, columns, types, constraints, and relationships.",{},async()=>{try{let e=await t.controlPlane("GET",`/schema-engine/introspect/${t.projectId}`);return {content:[{type:"text",text:JSON.stringify(e,null,2)}]}}catch(e){return {content:[{type:"text",text:`Error getting schema: ${e instanceof Error?e.message:String(e)}`}]}}});}function x(r,t){r.resource("schema","vaif://schema",{description:"The full database schema for the connected VAIF project, including tables, columns, types, and relationships.",mimeType:"application/json"},async()=>{let e=await t.controlPlane("GET",`/schema-engine/introspect/${t.projectId}`);return {contents:[{uri:"vaif://schema",mimeType:"application/json",text:JSON.stringify(e,null,2)}]}});}function I(r,t){r.resource("project-info","vaif://project-info",{description:"Project metadata for the connected VAIF project, including name, region, settings, and status.",mimeType:"application/json"},async()=>{let e=await t.controlPlane("GET",`/v1/projects/${t.projectId}`);return {contents:[{uri:"vaif://project-info",mimeType:"application/json",text:JSON.stringify(e,null,2)}]}});}function T(r){let t=new McpServer({name:"vaif-studio",version:"1.0.0"}),e=new f(r);return m(t,e),h(t,e),y(t,e),b(t,e),x(t,e),I(t,e),t}function U(){let r=process.argv.slice(2),t={};for(let e=0;e<r.length;e++){let o=r[e],n=r[e+1];switch(o){case "--api-key":t.apiKey=n,e++;break;case "--project-id":t.projectId=n,e++;break;case "--api-url":t.apiUrl=n,e++;break;case "--auth-token":t.authToken=n,e++;break;case "--help":case "-h":console.error(`
|
|
3
|
+
vaif-mcp \u2014 MCP server for VAIF Studio
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
vaif-mcp [options]
|
|
7
|
+
|
|
8
|
+
Options:
|
|
9
|
+
--api-key <key> VAIF API key (vk_...)
|
|
10
|
+
--project-id <id> VAIF project ID (proj_...)
|
|
11
|
+
--api-url <url> VAIF API URL (default: https://api.vaif.studio)
|
|
12
|
+
--auth-token <token> VAIF auth token (JWT)
|
|
13
|
+
--help, -h Show this help message
|
|
14
|
+
|
|
15
|
+
Environment variables:
|
|
16
|
+
VAIF_API_KEY API key
|
|
17
|
+
VAIF_PROJECT_ID Project ID
|
|
18
|
+
VAIF_API_URL API URL
|
|
19
|
+
VAIF_AUTH_TOKEN Auth token
|
|
20
|
+
|
|
21
|
+
Config files (checked in order):
|
|
22
|
+
./vaif.config.json Local project config
|
|
23
|
+
~/.vaif/auth.json User auth config
|
|
24
|
+
`),process.exit(0);}}return t}function k(r){try{let t=readFileSync(r,"utf-8");return JSON.parse(t)}catch{return null}}function F(){let r=U(),t={apiKey:process.env.VAIF_API_KEY,projectId:process.env.VAIF_PROJECT_ID,apiUrl:process.env.VAIF_API_URL,authToken:process.env.VAIF_AUTH_TOKEN},e=k(join(process.cwd(),"vaif.config.json")),o=e?.projectId,n=e?.api?.apiKey,i=k(join(homedir(),".vaif","auth.json")),s=i?.token,p=i?.projectId;return {apiUrl:r.apiUrl||t.apiUrl||"https://api.vaif.studio",projectId:r.projectId||t.projectId||o||p,apiKey:r.apiKey||t.apiKey||n,authToken:r.authToken||t.authToken||s}}async function O(){let r=F();r.projectId||(console.error("Error: Project ID is required. Set --project-id, VAIF_PROJECT_ID, or configure vaif.config.json."),process.exit(1)),!r.apiKey&&!r.authToken&&(console.error("Error: Authentication required. Set --api-key / VAIF_API_KEY or --auth-token / VAIF_AUTH_TOKEN."),process.exit(1)),console.error("VAIF MCP Server v1.0.0"),console.error(` Project: ${r.projectId}`),console.error(` API URL: ${r.apiUrl}`),console.error(` Auth: ${r.apiKey?"API Key":""}${r.apiKey&&r.authToken?" + ":""}${r.authToken?"Auth Token":""}`);let t=T({apiUrl:r.apiUrl,projectId:r.projectId,apiKey:r.apiKey,authToken:r.authToken}),e=new StdioServerTransport;await t.connect(e),console.error("VAIF MCP Server running on stdio");}O().catch(r=>{console.error("Fatal error:",r),process.exit(1);});
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vaiftech/mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for VAIF Studio — connect Claude Code to your VAIF project",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"vaif-mcp": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": ["dist"],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup",
|
|
14
|
+
"dev": "tsup --watch",
|
|
15
|
+
"typecheck": "tsc --noEmit",
|
|
16
|
+
"clean": "rm -rf dist"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
20
|
+
"zod": "^3.23.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^22.14.0",
|
|
24
|
+
"typescript": "^5.9.3",
|
|
25
|
+
"tsup": "^8.5.1"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18.0.0"
|
|
29
|
+
},
|
|
30
|
+
"keywords": ["vaif", "mcp", "claude", "ai", "backend"],
|
|
31
|
+
"author": "VAIF Technologies",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/vaif-technologies/vaif-studio",
|
|
36
|
+
"directory": "packages/mcp"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://vaif.studio"
|
|
39
|
+
}
|