@vaiftech/mcp 1.3.0 → 1.5.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 (3) hide show
  1. package/README.md +35 -4
  2. package/dist/index.js +69 -3
  3. package/package.json +11 -19
package/README.md CHANGED
@@ -120,14 +120,14 @@ You can provide both for full access to all tools, or just one depending on your
120
120
 
121
121
  | Tool | Description |
122
122
  |------|-------------|
123
- | `list_functions` | List all serverless functions |
123
+ | `list_functions` | List all serverless functions (source excluded by default; pass `include_source=true` for full code) |
124
124
  | `invoke_function` | Invoke a function by name or UUID with an optional JSON payload |
125
125
  | `create_function` | Create a new serverless function definition |
126
- | `deploy_function` | Deploy source code to a function |
126
+ | `deploy_function` | Deploy source code to a function by name or UUID |
127
127
  | `set_secret` | Create or update a function secret |
128
128
  | `list_secrets` | List all secret names (values hidden) |
129
129
  | `delete_secret` | Delete a secret by ID |
130
- | `get_function_logs` | Get execution history and console output for a function |
130
+ | `get_function_logs` | Get execution logs by function name or UUID, with optional `status` filter |
131
131
 
132
132
  ### Realtime
133
133
 
@@ -142,6 +142,29 @@ You can provide both for full access to all tools, or just one depending on your
142
142
  |------|-------------|
143
143
  | `get_schema` | Get the full database schema as JSON |
144
144
 
145
+ ## Available Prompts
146
+
147
+ Prompts are guided workflows that appear when you type `/mcp` in Claude Code. They provide context-aware templates for common tasks.
148
+
149
+ | Prompt | Description |
150
+ |--------|-------------|
151
+ | `setup-backend` | Set up a new backend from scratch — design tables, seed data, and generate SDK code |
152
+ | `add-feature` | Add a new feature to an existing backend — create tables, functions, and storage |
153
+ | `generate-api-code` | Generate TypeScript SDK code for your current schema (auto-fetches schema) |
154
+ | `debug-query` | Debug a failing query — inspect schema, test queries, and get the fix |
155
+
156
+ ### Using prompts
157
+
158
+ In Claude Code, type `/mcp` and select a prompt, or reference them directly:
159
+
160
+ ```
161
+ > /mcp setup-backend
162
+ Description: a task management app with teams and projects
163
+
164
+ > /mcp debug-query
165
+ Issue: filter on metadata->status not working
166
+ ```
167
+
145
168
  ## Available Resources
146
169
 
147
170
  | Resource | URI | Description |
@@ -208,7 +231,15 @@ Using enable_realtime with tables=["messages"]
208
231
  > Create and deploy a hello_world function
209
232
 
210
233
  Using create_function with name="hello_world", runtime="nodejs20"
211
- Using deploy_function with functionId="fn_xyz", source="export default async..."
234
+ Using deploy_function with function="hello_world", source="export default async..."
235
+ ```
236
+
237
+ ### View function logs
238
+
239
+ ```
240
+ > Show me recent errors for the send_email function
241
+
242
+ Using get_function_logs with function="send_email", status="error", limit=10
212
243
  ```
213
244
 
214
245
  > **Function naming**: Names must be alphanumeric and underscores only (`^[a-zA-Z0-9_]+$`). Use `send_email` not `send-email`.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
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 C=Object.defineProperty;var O=(n,t,e)=>t in n?C(n,t,{enumerable:true,configurable:true,writable:true,value:e}):n[t]=e;var f=(n,t,e)=>O(n,typeof t!="symbol"?t+"":t,e);var g=class{constructor(t){f(this,"apiUrl");f(this,"projectId");f(this,"apiKey");f(this,"authToken");this.apiUrl=t.apiUrl.replace(/\/+$/,""),this.projectId=t.projectId,this.apiKey=t.apiKey,this.authToken=t.authToken;}async dataPlane(t,e,r){if(!this.apiKey)throw new Error("API key is required for data plane requests. Set --api-key or VAIF_API_KEY.");let o=`${this.apiUrl}${e}`,i={"Content-Type":"application/json","x-vaif-key":this.apiKey},s=await fetch(o,{method:t,headers:i,body:r?JSON.stringify(r):void 0});if(!s.ok){let p=await s.text().catch(()=>"");throw new Error(`Data plane ${t} ${e} failed (${s.status}): ${p}`)}let u=await s.text();if(u)return JSON.parse(u)}async controlPlane(t,e,r){if(!this.authToken)throw new Error("Auth token is required for control plane requests. Set --auth-token or VAIF_AUTH_TOKEN.");let o=`${this.apiUrl}${e}`,i={"Content-Type":"application/json",Authorization:`Bearer ${this.authToken}`,"x-project-id":this.projectId},s=await fetch(o,{method:t,headers:i,body:r?JSON.stringify(r):void 0});if(!s.ok){let p=await s.text().catch(()=>"");throw new Error(`Control plane ${t} ${e} failed (${s.status}): ${p}`)}let u=await s.text();if(u)return JSON.parse(u)}};function y(n,t){n.tool("list_tables","List all tables in the VAIF project database. Returns table names and basic metadata.",{},async()=>{try{let r=((await t.controlPlane("GET",`/schema-engine/introspect/${t.projectId}`)).tables||[]).map(o=>o.name);return {content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(e){return {content:[{type:"text",text:`Error listing tables: ${e instanceof Error?e.message:String(e)}`}]}}}),n.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 o=((await t.controlPlane("GET",`/schema-engine/introspect/${t.projectId}`)).tables||[]).find(i=>i.name===e);return o?{content:[{type:"text",text:JSON.stringify(o,null,2)}]}:{content:[{type:"text",text:`Table "${e}" not found. Use list_tables to see available tables.`}]}}catch(r){return {content:[{type:"text",text:`Error describing table: ${r instanceof Error?r.message:String(r)}`}]}}}),n.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:r,limit:o,offset:i,order_by:s,order:u})=>{try{let p=new URLSearchParams;if(r)for(let[_,$]of Object.entries(r))p.append(`filter[${_}]`,$);o!==void 0&&p.append("limit",String(o)),i!==void 0&&p.append("offset",String(i)),s&&p.append("order_by",s),u&&p.append("order",u);let m=p.toString(),P=`/generated/${e}${m?`?${m}`:""}`,v=await t.dataPlane("GET",P);return {content:[{type:"text",text:JSON.stringify(v,null,2)}]}}catch(p){return {content:[{type:"text",text:`Error querying rows: ${p instanceof Error?p.message:String(p)}`}]}}}),n.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:r})=>{try{let o=await t.dataPlane("POST",`/generated/${e}`,r);return {content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(o){return {content:[{type:"text",text:`Error inserting row: ${o instanceof Error?o.message:String(o)}`}]}}}),n.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:r,data:o})=>{try{let i=await t.dataPlane("PATCH",`/generated/${e}/${r}`,o);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)}`}]}}}),n.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:r})=>{try{let o=await t.dataPlane("DELETE",`/generated/${e}/${r}`);return {content:[{type:"text",text:JSON.stringify(o??{success:!0,deleted:r},null,2)}]}}catch(o){return {content:[{type:"text",text:`Error deleting row: ${o instanceof Error?o.message:String(o)}`}]}}});}function h(n,t){n.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)}`}]}}}),n.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:r})=>{try{let o=new URLSearchParams;r&&o.append("prefix",r);let i=o.toString(),s=`/storage/files/${t.projectId}/${e}${i?`?${i}`:""}`,u=await t.controlPlane("GET",s);return {content:[{type:"text",text:JSON.stringify(u,null,2)}]}}catch(o){return {content:[{type:"text",text:`Error listing files: ${o instanceof Error?o.message:String(o)}`}]}}}),n.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:r,expiresIn:o})=>{try{let i=await t.controlPlane("POST","/storage/download",{bucket:e,path:r,expiresIn:o});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)}`}]}}}),n.tool("create_bucket","Create a new storage bucket in the VAIF project. Buckets organize files and control public/private access.",{name:z.string().describe("Bucket name (lowercase, no spaces, e.g. 'avatars', 'documents')"),public:z.boolean().optional().describe("Whether files are publicly accessible without auth (default: false)")},async({name:e,public:r})=>{try{let o=await t.controlPlane("POST","/storage/buckets",{name:e,projectId:t.projectId,public:r??!1});return {content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(o){return {content:[{type:"text",text:`Error creating bucket: ${o instanceof Error?o.message:String(o)}`}]}}}),n.tool("list_storage_policies","List access policies for a storage bucket. Policies control who can upload, download, or delete files.",{bucketId:z.string().describe("The bucket ID to list policies for")},async({bucketId:e})=>{try{let r=await t.controlPlane("GET",`/storage/buckets/${e}/policies`);return {content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(r){return {content:[{type:"text",text:`Error listing storage policies: ${r instanceof Error?r.message:String(r)}`}]}}}),n.tool("create_storage_policy","Create an access policy for a storage bucket. Policies define who can perform operations (upload, download, delete) on files in the bucket.",{bucketId:z.string().describe("The bucket ID to create the policy for"),name:z.string().describe("Policy name (e.g. 'allow-authenticated-uploads')"),operation:z.enum(["SELECT","INSERT","UPDATE","DELETE"]).describe("The storage operation this policy applies to (SELECT=download, INSERT=upload, UPDATE=replace, DELETE=remove)"),definition:z.string().describe("SQL-like policy definition (e.g. 'auth.uid() IS NOT NULL' or 'true' for public access)")},async({bucketId:e,name:r,operation:o,definition:i})=>{try{let s=await t.controlPlane("POST",`/storage/buckets/${e}/policies`,{name:r,operation:o,definition:i});return {content:[{type:"text",text:JSON.stringify(s,null,2)}]}}catch(s){return {content:[{type:"text",text:`Error creating storage policy: ${s instanceof Error?s.message:String(s)}`}]}}});}function b(n,t){n.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)}`}]}}}),n.tool("invoke_function","Invoke a serverless function by name or UUID with an optional JSON payload. Returns the function's response.",{function:z.string().describe("Function name (e.g. 'send_email') or UUID"),payload:z.record(z.any()).optional().describe("JSON payload to pass to the function")},async e=>{let r=e.function,o=e.payload;try{let i=await t.controlPlane("POST",`/functions/${encodeURIComponent(r)}/invoke`,o?{body:o}:{});return {content:[{type:"text",text:JSON.stringify(i,null,2)}]}}catch(i){return {content:[{type:"text",text:`Error invoking function: ${i instanceof Error?i.message:String(i)}`}]}}}),n.tool("create_function","Create a new serverless function in the VAIF project. After creation, use deploy_function to upload the source code.",{name:z.string().describe("Function name (alphanumeric and underscores only, e.g. 'send_email', 'process_webhook')"),runtime:z.string().optional().describe("Function runtime (default: 'nodejs20'). Options: nodejs20, nodejs18, deno, python311"),timeoutMs:z.number().optional().describe("Execution timeout in milliseconds (default: 10000, range: 1000-30000)")},async({name:e,runtime:r,timeoutMs:o})=>{try{let i=await t.controlPlane("POST","/functions",{name:e,projectId:t.projectId,runtime:r??"nodejs20",timeoutMs:o??1e4});return {content:[{type:"text",text:JSON.stringify(i,null,2)}]}}catch(i){return {content:[{type:"text",text:`Error creating function: ${i instanceof Error?i.message:String(i)}`}]}}}),n.tool("deploy_function","Deploy source code to an existing serverless function. Uploads the code and triggers a build/deploy cycle. The function must already exist (use create_function first).",{functionId:z.string().describe("The ID of the function to deploy to"),source:z.string().describe("The function source code as a string (TypeScript or JavaScript)")},async({functionId:e,source:r})=>{try{let o=await t.controlPlane("PUT",`/functions/${e}/source`,{sourceCode:r});return {content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(o){return {content:[{type:"text",text:`Error deploying function: ${o instanceof Error?o.message:String(o)}`}]}}}),n.tool("set_secret","Create or update a function secret for the VAIF project.",{key:z.string().describe("Secret name (e.g. 'STRIPE_KEY')"),value:z.string().describe("Secret value"),envId:z.string().optional().describe("Optional environment ID to scope the secret to")},async({key:e,value:r,envId:o})=>{try{let i=await t.controlPlane("POST","/functions/secrets",{projectId:t.projectId,key:e,value:r,envId:o});return {content:[{type:"text",text:JSON.stringify(i,null,2)}]}}catch(i){return {content:[{type:"text",text:`Error setting secret: ${i instanceof Error?i.message:String(i)}`}]}}}),n.tool("list_secrets","List all secret names for the project. Values are hidden for security.",{envId:z.string().optional().describe("Optional environment ID to filter secrets by")},async({envId:e})=>{try{let r=e?`?envId=${encodeURIComponent(e)}`:"",o=await t.controlPlane("GET",`/functions/secrets/project/${t.projectId}${r}`);return {content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(r){return {content:[{type:"text",text:`Error listing secrets: ${r instanceof Error?r.message:String(r)}`}]}}}),n.tool("delete_secret","Delete a function secret by its ID.",{secretId:z.string().describe("The ID of the secret to delete")},async({secretId:e})=>{try{let r=await t.controlPlane("DELETE",`/functions/secrets/${e}`);return {content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(r){return {content:[{type:"text",text:`Error deleting secret: ${r instanceof Error?r.message:String(r)}`}]}}}),n.tool("get_function_logs","Get execution logs for a serverless function. Returns invocation history and console output.",{functionId:z.string().describe("The ID of the function to get logs for"),limit:z.number().optional().describe("Maximum number of log entries to return (default: 50)"),executionId:z.string().optional().describe("Filter logs to a specific execution ID")},async({functionId:e,limit:r,executionId:o})=>{try{let i=new URLSearchParams;i.set("limit",String(r??50)),o&&i.set("executionId",o);let s=await t.controlPlane("GET",`/functions/${e}/logs?${i.toString()}`);return {content:[{type:"text",text:JSON.stringify(s,null,2)}]}}catch(i){return {content:[{type:"text",text:`Error fetching function logs: ${i instanceof Error?i.message:String(i)}`}]}}});}function x(n,t){n.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)}`}]}}}),n.tool("create_tables",`Create or update database tables in the VAIF project. Accepts a full schema definition with tables, columns, indexes, and foreign keys. This is a declarative operation \u2014 provide the desired end-state and VAIF will diff and apply changes.
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 O=Object.defineProperty;var V=(n,t,e)=>t in n?O(n,t,{enumerable:true,configurable:true,writable:true,value:e}):n[t]=e;var y=(n,t,e)=>V(n,typeof t!="symbol"?t+"":t,e);var h=class{constructor(t){y(this,"apiUrl");y(this,"projectId");y(this,"apiKey");y(this,"authToken");this.apiUrl=t.apiUrl.replace(/\/+$/,""),this.projectId=t.projectId,this.apiKey=t.apiKey,this.authToken=t.authToken;}async dataPlane(t,e,r){if(!this.apiKey)throw new Error("API key is required for data plane requests. Set --api-key or VAIF_API_KEY.");let o=`${this.apiUrl}${e}`,i={"Content-Type":"application/json","x-vaif-key":this.apiKey},a=await fetch(o,{method:t,headers:i,body:r?JSON.stringify(r):void 0});if(!a.ok){let c=await a.text().catch(()=>"");throw new Error(`Data plane ${t} ${e} failed (${a.status}): ${c}`)}let l=await a.text();if(l)return JSON.parse(l)}async controlPlane(t,e,r){if(!this.authToken)throw new Error("Auth token is required for control plane requests. Set --auth-token or VAIF_AUTH_TOKEN.");let o=`${this.apiUrl}${e}`,i={"Content-Type":"application/json",Authorization:`Bearer ${this.authToken}`,"x-project-id":this.projectId},a=await fetch(o,{method:t,headers:i,body:r?JSON.stringify(r):void 0});if(!a.ok){let c=await a.text().catch(()=>"");throw new Error(`Control plane ${t} ${e} failed (${a.status}): ${c}`)}let l=await a.text();if(l)return JSON.parse(l)}};function I(n,t){n.tool("list_tables","List all tables in the VAIF project database. Returns table names and basic metadata.",{},async()=>{try{let r=((await t.controlPlane("GET",`/schema-engine/introspect/${t.projectId}`)).tables||[]).map(o=>o.name);return {content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(e){return {content:[{type:"text",text:`Error listing tables: ${e instanceof Error?e.message:String(e)}`}]}}}),n.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 o=((await t.controlPlane("GET",`/schema-engine/introspect/${t.projectId}`)).tables||[]).find(i=>i.name===e);return o?{content:[{type:"text",text:JSON.stringify(o,null,2)}]}:{content:[{type:"text",text:`Table "${e}" not found. Use list_tables to see available tables.`}]}}catch(r){return {content:[{type:"text",text:`Error describing table: ${r instanceof Error?r.message:String(r)}`}]}}}),n.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:r,limit:o,offset:i,order_by:a,order:l})=>{try{let c=new URLSearchParams;if(r)for(let[b,U]of Object.entries(r))c.append(`filter[${b}]`,U);o!==void 0&&c.append("limit",String(o)),i!==void 0&&c.append("offset",String(i)),a&&c.append("order_by",a),l&&c.append("order",l);let g=c.toString(),m=`/generated/${e}${g?`?${g}`:""}`,d=await t.dataPlane("GET",m);return {content:[{type:"text",text:JSON.stringify(d,null,2)}]}}catch(c){return {content:[{type:"text",text:`Error querying rows: ${c instanceof Error?c.message:String(c)}`}]}}}),n.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:r})=>{try{let o=await t.dataPlane("POST",`/generated/${e}`,r);return {content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(o){return {content:[{type:"text",text:`Error inserting row: ${o instanceof Error?o.message:String(o)}`}]}}}),n.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:r,data:o})=>{try{let i=await t.dataPlane("PATCH",`/generated/${e}/${r}`,o);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)}`}]}}}),n.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:r})=>{try{let o=await t.dataPlane("DELETE",`/generated/${e}/${r}`);return {content:[{type:"text",text:JSON.stringify(o??{success:!0,deleted:r},null,2)}]}}catch(o){return {content:[{type:"text",text:`Error deleting row: ${o instanceof Error?o.message:String(o)}`}]}}});}function S(n,t){n.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)}`}]}}}),n.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:r})=>{try{let o=new URLSearchParams;r&&o.append("prefix",r);let i=o.toString(),a=`/storage/files/${t.projectId}/${e}${i?`?${i}`:""}`,l=await t.controlPlane("GET",a);return {content:[{type:"text",text:JSON.stringify(l,null,2)}]}}catch(o){return {content:[{type:"text",text:`Error listing files: ${o instanceof Error?o.message:String(o)}`}]}}}),n.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:r,expiresIn:o})=>{try{let i=await t.controlPlane("POST","/storage/download",{bucket:e,path:r,expiresIn:o});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)}`}]}}}),n.tool("create_bucket","Create a new storage bucket in the VAIF project. Buckets organize files and control public/private access.",{name:z.string().describe("Bucket name (lowercase, no spaces, e.g. 'avatars', 'documents')"),public:z.boolean().optional().describe("Whether files are publicly accessible without auth (default: false)")},async({name:e,public:r})=>{try{let o=await t.controlPlane("POST","/storage/buckets",{name:e,projectId:t.projectId,public:r??!1});return {content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(o){return {content:[{type:"text",text:`Error creating bucket: ${o instanceof Error?o.message:String(o)}`}]}}}),n.tool("list_storage_policies","List access policies for a storage bucket. Policies control who can upload, download, or delete files.",{bucketId:z.string().describe("The bucket ID to list policies for")},async({bucketId:e})=>{try{let r=await t.controlPlane("GET",`/storage/buckets/${e}/policies`);return {content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(r){return {content:[{type:"text",text:`Error listing storage policies: ${r instanceof Error?r.message:String(r)}`}]}}}),n.tool("create_storage_policy","Create an access policy for a storage bucket. Policies define who can perform operations (upload, download, delete) on files in the bucket.",{bucketId:z.string().describe("The bucket ID to create the policy for"),name:z.string().describe("Policy name (e.g. 'allow-authenticated-uploads')"),operation:z.enum(["SELECT","INSERT","UPDATE","DELETE"]).describe("The storage operation this policy applies to (SELECT=download, INSERT=upload, UPDATE=replace, DELETE=remove)"),definition:z.string().describe("SQL-like policy definition (e.g. 'auth.uid() IS NOT NULL' or 'true' for public access)")},async({bucketId:e,name:r,operation:o,definition:i})=>{try{let a=await t.controlPlane("POST",`/storage/buckets/${e}/policies`,{name:r,operation:o,definition:i});return {content:[{type:"text",text:JSON.stringify(a,null,2)}]}}catch(a){return {content:[{type:"text",text:`Error creating storage policy: ${a instanceof Error?a.message:String(a)}`}]}}});}function w(n,t){n.tool("list_functions","List all serverless functions in the VAIF project. By default excludes source code for performance.",{include_source:z.boolean().optional().default(false).describe("Include function source code in response (default: false, source can be very large)")},async({include_source:e})=>{try{let r=await t.controlPlane("GET",`/functions/project/${t.projectId}`);if(!e&&Array.isArray(r)){let o=r.map(i=>{let{sourceCode:a,source:l,bundlePath:c,...g}=i;return g});return {content:[{type:"text",text:JSON.stringify(o,null,2)}]}}return {content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(r){return {content:[{type:"text",text:`Error listing functions: ${r instanceof Error?r.message:String(r)}`}]}}}),n.tool("invoke_function","Invoke a serverless function by name or UUID with an optional JSON payload. Returns the function's response.",{function:z.string().describe("Function name (e.g. 'send_email') or UUID"),payload:z.record(z.any()).optional().describe("JSON payload to pass to the function")},async e=>{let r=e.function,o=e.payload;try{let i=await t.controlPlane("POST",`/functions/${encodeURIComponent(r)}/invoke`,o?{body:o}:{});return {content:[{type:"text",text:JSON.stringify(i,null,2)}]}}catch(i){return {content:[{type:"text",text:`Error invoking function: ${i instanceof Error?i.message:String(i)}`}]}}}),n.tool("create_function","Create a new serverless function in the VAIF project. After creation, use deploy_function to upload the source code.",{name:z.string().describe("Function name (alphanumeric and underscores only, e.g. 'send_email', 'process_webhook')"),runtime:z.string().optional().describe("Function runtime (default: 'nodejs20'). Options: nodejs20, nodejs18, deno, python311"),timeoutMs:z.number().optional().describe("Execution timeout in milliseconds (default: 10000, range: 1000-30000)")},async({name:e,runtime:r,timeoutMs:o})=>{try{let i=await t.controlPlane("POST","/functions",{name:e,projectId:t.projectId,runtime:r??"nodejs20",timeoutMs:o??1e4});return {content:[{type:"text",text:JSON.stringify(i,null,2)}]}}catch(i){return {content:[{type:"text",text:`Error creating function: ${i instanceof Error?i.message:String(i)}`}]}}}),n.tool("deploy_function","Deploy source code to a serverless function by name or UUID. Uploads the code and triggers a build/deploy cycle. The function must already exist (use create_function first).",{function:z.string().describe("Function name (e.g. 'send_email') or UUID"),source:z.string().describe("The function source code as a string (TypeScript or JavaScript)")},async({function:e,source:r})=>{try{let o=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e),i=e;if(!o){let l=await t.controlPlane("GET",`/functions/project/${t.projectId}`),c=(Array.isArray(l)?l:[]).find(g=>g.name===e);if(!c)return {content:[{type:"text",text:`Function "${e}" not found. Use list_functions to see available functions.`}]};i=c.id;}let a=await t.controlPlane("PUT",`/functions/${i}/source`,{sourceCode:r});return {content:[{type:"text",text:JSON.stringify(a,null,2)}]}}catch(o){return {content:[{type:"text",text:`Error deploying function: ${o instanceof Error?o.message:String(o)}`}]}}}),n.tool("set_secret","Create or update a function secret for the VAIF project.",{key:z.string().describe("Secret name (e.g. 'STRIPE_KEY')"),value:z.string().describe("Secret value"),envId:z.string().optional().describe("Optional environment ID to scope the secret to")},async({key:e,value:r,envId:o})=>{try{let i=await t.controlPlane("POST","/functions/secrets",{projectId:t.projectId,key:e,value:r,envId:o});return {content:[{type:"text",text:JSON.stringify(i,null,2)}]}}catch(i){return {content:[{type:"text",text:`Error setting secret: ${i instanceof Error?i.message:String(i)}`}]}}}),n.tool("list_secrets","List all secret names for the project. Values are hidden for security.",{envId:z.string().optional().describe("Optional environment ID to filter secrets by")},async({envId:e})=>{try{let r=e?`?envId=${encodeURIComponent(e)}`:"",o=await t.controlPlane("GET",`/functions/secrets/project/${t.projectId}${r}`);return {content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(r){return {content:[{type:"text",text:`Error listing secrets: ${r instanceof Error?r.message:String(r)}`}]}}}),n.tool("delete_secret","Delete a function secret by its ID.",{secretId:z.string().describe("The ID of the secret to delete")},async({secretId:e})=>{try{let r=await t.controlPlane("DELETE",`/functions/secrets/${e}`);return {content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(r){return {content:[{type:"text",text:`Error deleting secret: ${r instanceof Error?r.message:String(r)}`}]}}}),n.tool("get_function_logs","Get execution logs for a serverless function. Accepts function name or UUID.",{function:z.string().describe("Function name (e.g. 'send_email') or UUID"),limit:z.number().optional().describe("Max log entries (default: 50)"),status:z.enum(["success","error","all"]).optional().describe("Filter by execution status"),executionId:z.string().optional().describe("Filter to a specific execution")},async({function:e,limit:r,status:o,executionId:i})=>{try{let a=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e),l=e;if(!a){let m=await t.controlPlane("GET",`/functions/project/${t.projectId}`),d=(Array.isArray(m)?m:[]).find(b=>b.name===e);if(!d)return {content:[{type:"text",text:`Function "${e}" not found.`}]};l=d.id;}let c=new URLSearchParams;c.set("limit",String(r??50)),i&&c.set("executionId",i),o&&o!=="all"&&c.set("status",o);let g=await t.controlPlane("GET",`/functions/${l}/logs?${c.toString()}`);if(Array.isArray(g)){let m=g.map(d=>({timestamp:d.createdAt||d.timestamp,status:d.success?"\u2713 success":"\u2717 error",duration:d.durationMs?`${d.durationMs}ms`:"unknown",statusCode:d.statusCode,...d.error&&{error:d.error},...d.logs?.length&&{output:d.logs}}));return {content:[{type:"text",text:JSON.stringify(m,null,2)}]}}return {content:[{type:"text",text:JSON.stringify(g,null,2)}]}}catch(a){return {content:[{type:"text",text:`Error fetching logs: ${a instanceof Error?a.message:String(a)}`}]}}});}function T(n,t){n.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)}`}]}}}),n.tool("create_tables",`Create or update database tables in the VAIF project. Accepts a full schema definition with tables, columns, indexes, and foreign keys. This is a declarative operation \u2014 provide the desired end-state and VAIF will diff and apply changes.
3
3
 
4
4
  Valid column types: uuid, text, varchar, string, int, integer, bigint, boolean, bool, jsonb, json, timestamptz, timestamp, date, numeric, decimal, float, double, text[], integer[]
5
5
 
@@ -16,7 +16,73 @@ Example:
16
16
  ],
17
17
  "indexes": [{ "name": "idx_posts_author", "columns": ["author_id"] }]
18
18
  }]
19
- }`,{tables:z.array(z.object({name:z.string().describe("Table name"),columns:z.array(z.object({name:z.string().describe("Column name"),type:z.enum(["uuid","text","varchar","string","int","integer","bigint","boolean","bool","jsonb","json","timestamptz","timestamp","date","numeric","decimal","float","double","text[]","integer[]"]).describe("Column type"),primaryKey:z.boolean().optional().describe("Is primary key"),unique:z.boolean().optional().describe("Has unique constraint"),nullable:z.boolean().optional().describe("Allows NULL (default: true)"),default:z.union([z.string(),z.number(),z.boolean()]).optional().describe("Default value (e.g. 'gen_random_uuid()', 'now()', true)"),references:z.object({table:z.string().describe("Referenced table"),column:z.string().optional().describe("Referenced column (default: id)"),onDelete:z.enum(["CASCADE","RESTRICT","SET NULL","SET DEFAULT","NO ACTION"]).optional(),onUpdate:z.enum(["CASCADE","RESTRICT","SET NULL","SET DEFAULT","NO ACTION"]).optional()}).optional().describe("Foreign key reference")})).describe("Table columns"),indexes:z.array(z.object({name:z.string().describe("Index name"),columns:z.array(z.string()).describe("Indexed columns"),unique:z.boolean().optional().describe("Unique index")})).optional().describe("Table indexes")})).describe("Tables to create or update"),migration_name:z.string().optional().describe("Optional migration name for tracking"),allow_destructive:z.boolean().optional().describe("Allow destructive changes like dropping columns (default: false)")},async({tables:e,migration_name:r,allow_destructive:o})=>{try{let i={schemaVersion:"1.0",tables:e},s=await t.controlPlane("POST","/schema-engine/apply",{projectId:t.projectId,definition:i,migrationName:r,allowDestructive:o??!1});return {content:[{type:"text",text:JSON.stringify(s,null,2)}]}}catch(i){return {content:[{type:"text",text:`Error creating tables: ${i instanceof Error?i.message:String(i)}`}]}}});}function S(n,t){n.tool("list_api_keys","List all API keys for the VAIF project. Returns key metadata (name, prefix, created date) but not the full secret.",{},async()=>{try{let e=await t.controlPlane("GET",`/v1/projects/${t.projectId}/api-keys`);return {content:[{type:"text",text:JSON.stringify(e,null,2)}]}}catch(e){return {content:[{type:"text",text:`Error listing API keys: ${e instanceof Error?e.message:String(e)}`}]}}}),n.tool("create_api_key","Create a new API key for the VAIF project. The full key is only shown once in the response \u2014 store it securely.",{name:z.string().describe("A descriptive name for the API key (e.g. 'mobile-app', 'ci-pipeline')")},async({name:e})=>{try{let r=await t.controlPlane("POST",`/v1/projects/${t.projectId}/api-keys`,{name:e});return {content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(r){return {content:[{type:"text",text:`Error creating API key: ${r instanceof Error?r.message:String(r)}`}]}}});}function T(n,t){n.tool("realtime_status","Check the realtime subscription status for the VAIF project. Shows which tables have realtime enabled and the connection state.",{},async()=>{try{let e=await t.controlPlane("GET",`/realtime/status/project/${t.projectId}`);return {content:[{type:"text",text:JSON.stringify(e,null,2)}]}}catch(e){return {content:[{type:"text",text:`Error getting realtime status: ${e instanceof Error?e.message:String(e)}`}]}}}),n.tool("enable_realtime","Enable realtime subscriptions for one or more tables. Clients can then subscribe to INSERT, UPDATE, and DELETE events on these tables via WebSocket.",{tables:z.array(z.string()).describe("Table names to enable realtime on (e.g. ['messages', 'notifications'])")},async({tables:e})=>{try{let r=await t.controlPlane("POST","/realtime/install",{projectId:t.projectId,tables:e});return {content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(r){return {content:[{type:"text",text:`Error enabling realtime: ${r instanceof Error?r.message:String(r)}`}]}}});}function E(n,t){n.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 j(n,t){n.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",`/projects/${t.projectId}`);return {contents:[{uri:"vaif://project-info",mimeType:"application/json",text:JSON.stringify(e,null,2)}]}});}function w(n){let t=new McpServer({name:"vaif-studio",version:"1.2.0"}),e=new g(n);return y(t,e),h(t,e),b(t,e),x(t,e),S(t,e),T(t,e),E(t,e),j(t,e),t}function R(){let n=process.argv.slice(2),t={};for(let e=0;e<n.length;e++){let r=n[e],o=n[e+1];switch(r){case "--api-key":t.apiKey=o,e++;break;case "--project-id":t.projectId=o,e++;break;case "--api-url":t.apiUrl=o,e++;break;case "--auth-token":t.authToken=o,e++;break;case "--help":case "-h":console.error(`
19
+ }`,{tables:z.array(z.object({name:z.string().describe("Table name"),columns:z.array(z.object({name:z.string().describe("Column name"),type:z.enum(["uuid","text","varchar","string","int","integer","bigint","boolean","bool","jsonb","json","timestamptz","timestamp","date","numeric","decimal","float","double","text[]","integer[]"]).describe("Column type"),primaryKey:z.boolean().optional().describe("Is primary key"),unique:z.boolean().optional().describe("Has unique constraint"),nullable:z.boolean().optional().describe("Allows NULL (default: true)"),default:z.union([z.string(),z.number(),z.boolean()]).optional().describe("Default value (e.g. 'gen_random_uuid()', 'now()', true)"),references:z.object({table:z.string().describe("Referenced table"),column:z.string().optional().describe("Referenced column (default: id)"),onDelete:z.enum(["CASCADE","RESTRICT","SET NULL","SET DEFAULT","NO ACTION"]).optional(),onUpdate:z.enum(["CASCADE","RESTRICT","SET NULL","SET DEFAULT","NO ACTION"]).optional()}).optional().describe("Foreign key reference")})).describe("Table columns"),indexes:z.array(z.object({name:z.string().describe("Index name"),columns:z.array(z.string()).describe("Indexed columns"),unique:z.boolean().optional().describe("Unique index")})).optional().describe("Table indexes")})).describe("Tables to create or update"),migration_name:z.string().optional().describe("Optional migration name for tracking"),allow_destructive:z.boolean().optional().describe("Allow destructive changes like dropping columns (default: false)")},async({tables:e,migration_name:r,allow_destructive:o})=>{try{let i={schemaVersion:"1.0",tables:e},a=await t.controlPlane("POST","/schema-engine/apply",{projectId:t.projectId,definition:i,migrationName:r,allowDestructive:o??!1});return {content:[{type:"text",text:JSON.stringify(a,null,2)}]}}catch(i){return {content:[{type:"text",text:`Error creating tables: ${i instanceof Error?i.message:String(i)}`}]}}});}function A(n,t){n.tool("list_api_keys","List all API keys for the VAIF project. Returns key metadata (name, prefix, created date) but not the full secret.",{},async()=>{try{let e=await t.controlPlane("GET",`/v1/projects/${t.projectId}/api-keys`);return {content:[{type:"text",text:JSON.stringify(e,null,2)}]}}catch(e){return {content:[{type:"text",text:`Error listing API keys: ${e instanceof Error?e.message:String(e)}`}]}}}),n.tool("create_api_key","Create a new API key for the VAIF project. The full key is only shown once in the response \u2014 store it securely.",{name:z.string().describe("A descriptive name for the API key (e.g. 'mobile-app', 'ci-pipeline')")},async({name:e})=>{try{let r=await t.controlPlane("POST",`/v1/projects/${t.projectId}/api-keys`,{name:e});return {content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(r){return {content:[{type:"text",text:`Error creating API key: ${r instanceof Error?r.message:String(r)}`}]}}});}function j(n,t){n.tool("realtime_status","Check the realtime subscription status for the VAIF project. Shows which tables have realtime enabled and the connection state.",{},async()=>{try{let e=await t.controlPlane("GET",`/realtime/status/project/${t.projectId}`);return {content:[{type:"text",text:JSON.stringify(e,null,2)}]}}catch(e){return {content:[{type:"text",text:`Error getting realtime status: ${e instanceof Error?e.message:String(e)}`}]}}}),n.tool("enable_realtime","Enable realtime subscriptions for one or more tables. Clients can then subscribe to INSERT, UPDATE, and DELETE events on these tables via WebSocket.",{tables:z.array(z.string()).describe("Table names to enable realtime on (e.g. ['messages', 'notifications'])")},async({tables:e})=>{try{let r=await t.controlPlane("POST","/realtime/install",{projectId:t.projectId,tables:e});return {content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(r){return {content:[{type:"text",text:`Error enabling realtime: ${r instanceof Error?r.message:String(r)}`}]}}});}function E(n,t){n.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 P(n,t){n.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",`/projects/${t.projectId}`);return {contents:[{uri:"vaif://project-info",mimeType:"application/json",text:JSON.stringify(e,null,2)}]}});}function _(n,t){n.prompt("setup-backend","Set up a new backend from scratch \u2014 create tables, enable auth, configure storage, and deploy functions.",{description:z.string().describe("Describe the app you're building (e.g. 'a task management app with teams and projects')")},async({description:e})=>({messages:[{role:"user",content:{type:"text",text:`I'm building: ${e}
20
+
21
+ Help me set up the VAIF Studio backend for this. Please:
22
+
23
+ 1. **Design the database schema** \u2014 Use the \`create_tables\` tool to create all necessary tables with proper column types, foreign keys, indexes, and constraints. Include id (uuid, PK, default gen_random_uuid()), created_at (timestamptz, default now()), and updated_at where appropriate.
24
+
25
+ 2. **Verify the schema** \u2014 Use \`list_tables\` and \`describe_table\` to confirm tables were created correctly.
26
+
27
+ 3. **Seed sample data** \u2014 Use \`insert_row\` to add a few example rows so I can test immediately.
28
+
29
+ 4. **Show me the SDK code** \u2014 Generate TypeScript code using @vaiftech/client that demonstrates the main CRUD operations for my app.
30
+
31
+ Project ID: ${t.projectId}
32
+ API URL: ${t.apiUrl}`}}]})),n.prompt("add-feature","Add a new feature to an existing VAIF backend \u2014 create tables, functions, or storage for a specific feature.",{feature:z.string().describe("Describe the feature (e.g. 'user notifications with email and push')")},async({feature:e})=>({messages:[{role:"user",content:{type:"text",text:`I want to add this feature to my existing VAIF backend: ${e}
33
+
34
+ Please:
35
+
36
+ 1. **Check the current schema** \u2014 Use \`get_schema\` to understand the existing tables and relationships.
37
+ 2. **Design the new tables/columns** \u2014 Use \`create_tables\` to add what's needed. Make sure foreign keys reference existing tables correctly.
38
+ 3. **Create any needed functions** \u2014 If this feature requires server-side logic (webhooks, scheduled tasks, triggers), use \`deploy_function\` to create and deploy them.
39
+ 4. **Create storage buckets** if needed \u2014 Use the storage tools.
40
+ 5. **Show the frontend code** \u2014 Generate TypeScript examples using @vaiftech/client for the new feature.
41
+
42
+ Project ID: ${t.projectId}`}}]})),n.prompt("generate-api-code","Generate TypeScript SDK code for your current VAIF schema \u2014 CRUD, auth, storage, and realtime examples.",{},async()=>{let e="Schema not available";try{let r=await t.controlPlane("GET",`/schema-engine/introspect/${t.projectId}`);e=JSON.stringify(r,null,2);}catch{}return {messages:[{role:"user",content:{type:"text",text:`Generate comprehensive TypeScript code for my VAIF Studio backend.
43
+
44
+ Current schema:
45
+ \`\`\`json
46
+ ${e}
47
+ \`\`\`
48
+
49
+ Generate code for:
50
+
51
+ 1. **Client setup** \u2014 Initialize @vaiftech/client with my project
52
+ 2. **CRUD operations** \u2014 For each table: list (with filters, pagination), create, update, delete
53
+ 3. **Auth integration** \u2014 Signup, login, token refresh, protected requests
54
+ 4. **Storage** \u2014 Upload files, download, signed URLs
55
+ 5. **Realtime** \u2014 Subscribe to table changes
56
+ 6. **Advanced queries** \u2014 JSONB filters, compound filters (AND+OR), full-text search, aggregation, joins
57
+
58
+ Use these API details:
59
+ - API URL: ${t.apiUrl}
60
+ - Project ID: ${t.projectId}
61
+ - Data-plane auth: \`x-vaif-key\` header with API key
62
+ - Control-plane auth: \`Authorization: Bearer <jwt>\` header
63
+
64
+ JSONB filter syntax: \`filter[jsonb_col->key]=value\`, \`filter[jsonb_col->key.op]=value\`
65
+ Compound filters: \`filter[field]=value&or_filter[field]=value\`
66
+ Aggregation: \`POST /generated/{table}/aggregate\` with \`{ aggregates: [{fn, column}], groupBy: [] }\`
67
+ Search: \`POST /generated/{table}/search\` with \`{ query, columns, limit }\`
68
+ Joins: \`?include=foreign_key_col\`
69
+ Upsert: POST with \`{ ..., _upsert: true, _conflictColumns: ["col"] }\``}}]}}),n.prompt("debug-query","Debug a failing VAIF query \u2014 inspect the schema and test the query step by step.",{issue:z.string().describe("Describe the issue (e.g. 'filter on metadata->status not working' or 'insert returns 400')")},async({issue:e})=>({messages:[{role:"user",content:{type:"text",text:`I'm having an issue with my VAIF query: ${e}
70
+
71
+ Please help me debug this:
72
+
73
+ 1. **Inspect the schema** \u2014 Use \`get_schema\` to check the table structure, column types, and constraints.
74
+ 2. **Test the query** \u2014 Use \`query_rows\` to try the query directly and see what comes back.
75
+ 3. **Check the data** \u2014 Use \`query_rows\` to look at existing rows and verify the data matches expectations.
76
+ 4. **Explain the fix** \u2014 Tell me what's wrong and show the corrected code.
77
+
78
+ Common issues to check:
79
+ - Column type mismatches (e.g. filtering string on UUID column)
80
+ - JSONB filters need arrow notation: \`filter[col->key]=value\`
81
+ - Array columns need proper format: \`{a,b,c}\`
82
+ - Numeric/decimal columns serialize as strings
83
+ - Missing required columns or constraint violations
84
+
85
+ Project ID: ${t.projectId}`}}]}));}function v(n){let t=new McpServer({name:"vaif-studio",version:"1.5.0"}),e=new h(n);return I(t,e),S(t,e),w(t,e),T(t,e),A(t,e),j(t,e),E(t,e),P(t,e),_(t,e),t}function L(){let n=process.argv.slice(2),t={};for(let e=0;e<n.length;e++){let r=n[e],o=n[e+1];switch(r){case "--api-key":t.apiKey=o,e++;break;case "--project-id":t.projectId=o,e++;break;case "--api-url":t.apiUrl=o,e++;break;case "--auth-token":t.authToken=o,e++;break;case "--help":case "-h":console.error(`
20
86
  vaif-mcp \u2014 MCP server for VAIF Studio
21
87
 
22
88
  Usage:
@@ -38,4 +104,4 @@ Environment variables:
38
104
  Config files (checked in order):
39
105
  ./vaif.config.json Local project config
40
106
  ~/.vaif/auth.json User auth config
41
- `),process.exit(0);}}return t}function k(n){try{let t=readFileSync(n,"utf-8");return JSON.parse(t)}catch{return null}}function J(){let n=R(),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")),r=e?.projectId,o=e?.api?.apiKey,i=k(join(homedir(),".vaif","auth.json")),s=i?.token,u=i?.projectId;return {apiUrl:n.apiUrl||t.apiUrl||"https://api.vaif.studio",projectId:n.projectId||t.projectId||r||u,apiKey:n.apiKey||t.apiKey||o,authToken:n.authToken||t.authToken||s}}async function L(){let n=J();n.projectId||(console.error("Error: Project ID is required. Set --project-id, VAIF_PROJECT_ID, or configure vaif.config.json."),process.exit(1)),!n.apiKey&&!n.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: ${n.projectId}`),console.error(` API URL: ${n.apiUrl}`),console.error(` Auth: ${n.apiKey?"API Key":""}${n.apiKey&&n.authToken?" + ":""}${n.authToken?"Auth Token":""}`);let t=w({apiUrl:n.apiUrl,projectId:n.projectId,apiKey:n.apiKey,authToken:n.authToken}),e=new StdioServerTransport;await t.connect(e),console.error("VAIF MCP Server running on stdio");}L().catch(n=>{console.error("Fatal error:",n),process.exit(1);});
107
+ `),process.exit(0);}}return t}function C(n){try{let t=readFileSync(n,"utf-8");return JSON.parse(t)}catch{return null}}function K(){let n=L(),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=C(join(process.cwd(),"vaif.config.json")),r=e?.projectId,o=e?.api?.apiKey,i=C(join(homedir(),".vaif","auth.json")),a=i?.token,l=i?.projectId;return {apiUrl:n.apiUrl||t.apiUrl||"https://api.vaif.studio",projectId:n.projectId||t.projectId||r||l,apiKey:n.apiKey||t.apiKey||o,authToken:n.authToken||t.authToken||a}}async function q(){let n=K();n.projectId||(console.error("Error: Project ID is required. Set --project-id, VAIF_PROJECT_ID, or configure vaif.config.json."),process.exit(1)),!n.apiKey&&!n.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: ${n.projectId}`),console.error(` API URL: ${n.apiUrl}`),console.error(` Auth: ${n.apiKey?"API Key":""}${n.apiKey&&n.authToken?" + ":""}${n.authToken?"Auth Token":""}`);let t=v({apiUrl:n.apiUrl,projectId:n.projectId,apiKey:n.apiKey,authToken:n.authToken}),e=new StdioServerTransport;await t.connect(e),console.error("VAIF MCP Server running on stdio");}q().catch(n=>{console.error("Fatal error:",n),process.exit(1);});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaiftech/mcp",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "MCP server for VAIF Studio — connect Claude Code to your VAIF project",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -8,9 +8,13 @@
8
8
  "bin": {
9
9
  "vaif-mcp": "./dist/index.js"
10
10
  },
11
- "files": [
12
- "dist"
13
- ],
11
+ "files": ["dist"],
12
+ "scripts": {
13
+ "build": "tsup",
14
+ "dev": "tsup --watch",
15
+ "typecheck": "tsc --noEmit",
16
+ "clean": "rm -rf dist"
17
+ },
14
18
  "dependencies": {
15
19
  "@modelcontextprotocol/sdk": "^1.26.0",
16
20
  "zod": "^3.23.0"
@@ -23,13 +27,7 @@
23
27
  "engines": {
24
28
  "node": ">=18.0.0"
25
29
  },
26
- "keywords": [
27
- "vaif",
28
- "mcp",
29
- "claude",
30
- "ai",
31
- "backend"
32
- ],
30
+ "keywords": ["vaif", "mcp", "claude", "ai", "backend"],
33
31
  "author": "VAIF Technologies",
34
32
  "license": "MIT",
35
33
  "repository": {
@@ -37,11 +35,5 @@
37
35
  "url": "https://github.com/vaif-technologies/vaif-studio",
38
36
  "directory": "packages/mcp"
39
37
  },
40
- "homepage": "https://vaif.studio",
41
- "scripts": {
42
- "build": "tsup",
43
- "dev": "tsup --watch",
44
- "typecheck": "tsc --noEmit",
45
- "clean": "rm -rf dist"
46
- }
47
- }
38
+ "homepage": "https://vaif.studio"
39
+ }