@spooky-sync/cli 0.0.1-canary.33 → 0.0.1-canary.35
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/mcp-proxy/dist/index.d.ts +2 -0
- package/mcp-proxy/dist/index.js +25 -0
- package/mcp-proxy/dist/server.d.ts +3 -0
- package/mcp-proxy/dist/server.js +74 -0
- package/mcp-proxy/dist/surreal.d.ts +13 -0
- package/mcp-proxy/dist/surreal.js +27 -0
- package/mcp-proxy/index.d.ts +2 -0
- package/mcp-proxy/index.js +25 -0
- package/mcp-proxy/server.d.ts +3 -0
- package/mcp-proxy/server.js +74 -0
- package/mcp-proxy/surreal.d.ts +13 -0
- package/mcp-proxy/surreal.js +27 -0
- package/package.json +10 -8
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { SurrealClient } from './surreal.js';
|
|
4
|
+
import { createServer } from './server.js';
|
|
5
|
+
function main() {
|
|
6
|
+
const surreal = new SurrealClient({
|
|
7
|
+
url: process.env.SURREAL_URL ?? 'http://localhost:8666',
|
|
8
|
+
namespace: process.env.SURREAL_NS ?? 'main',
|
|
9
|
+
database: process.env.SURREAL_DB ?? 'main',
|
|
10
|
+
username: process.env.SURREAL_USER ?? 'root',
|
|
11
|
+
password: process.env.SURREAL_PASS ?? 'root',
|
|
12
|
+
});
|
|
13
|
+
const server = createServer(surreal);
|
|
14
|
+
const transport = new StdioServerTransport();
|
|
15
|
+
server.connect(transport).then(() => {
|
|
16
|
+
process.stderr.write('[sp00ky-mcp-proxy] MCP server running on stdio\n');
|
|
17
|
+
});
|
|
18
|
+
const cleanup = async () => {
|
|
19
|
+
process.stderr.write('[sp00ky-mcp-proxy] Shutting down...\n');
|
|
20
|
+
process.exit(0);
|
|
21
|
+
};
|
|
22
|
+
process.on('SIGINT', cleanup);
|
|
23
|
+
process.on('SIGTERM', cleanup);
|
|
24
|
+
}
|
|
25
|
+
main();
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export function createServer(surreal) {
|
|
4
|
+
const server = new McpServer({
|
|
5
|
+
name: 'sp00ky-mcp-proxy',
|
|
6
|
+
version: '0.0.1',
|
|
7
|
+
});
|
|
8
|
+
// --- Tools ---
|
|
9
|
+
server.tool('run_query', 'Execute a SurrealQL query against the database', {
|
|
10
|
+
query: z.string().describe('SurrealQL query to execute'),
|
|
11
|
+
}, async ({ query }) => {
|
|
12
|
+
const result = await surreal.query(query);
|
|
13
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
14
|
+
});
|
|
15
|
+
server.tool('list_tables', 'List all database tables', {}, async () => {
|
|
16
|
+
const result = await surreal.query('INFO FOR DB;');
|
|
17
|
+
const info = result;
|
|
18
|
+
const tables = info?.[0]?.result?.tables ?? info?.[0]?.tables ?? {};
|
|
19
|
+
const tableNames = Object.keys(tables);
|
|
20
|
+
return { content: [{ type: 'text', text: JSON.stringify(tableNames, null, 2) }] };
|
|
21
|
+
});
|
|
22
|
+
server.tool('get_table_data', 'Fetch records from a database table', {
|
|
23
|
+
tableName: z.string().describe('Name of the table'),
|
|
24
|
+
limit: z.number().optional().default(100).describe('Max number of records to return'),
|
|
25
|
+
}, async ({ tableName, limit }) => {
|
|
26
|
+
const result = await surreal.query(`SELECT * FROM \`${tableName}\` LIMIT ${limit};`);
|
|
27
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
28
|
+
});
|
|
29
|
+
server.tool('update_table_row', 'Update a record in a database table', {
|
|
30
|
+
recordId: z.string().describe('Record ID to update (e.g. "users:abc123")'),
|
|
31
|
+
updates: z.record(z.unknown()).describe('Fields to update'),
|
|
32
|
+
}, async ({ recordId, updates }) => {
|
|
33
|
+
const result = await surreal.query(`UPDATE ${recordId} MERGE ${JSON.stringify(updates)};`);
|
|
34
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
35
|
+
});
|
|
36
|
+
server.tool('delete_table_row', 'Delete a record from a database table', {
|
|
37
|
+
recordId: z.string().describe('Record ID to delete (e.g. "users:abc123")'),
|
|
38
|
+
}, async ({ recordId }) => {
|
|
39
|
+
const result = await surreal.query(`DELETE ${recordId};`);
|
|
40
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
41
|
+
});
|
|
42
|
+
server.tool('get_active_queries', 'Get all active live queries registered with the SSP', {}, async () => {
|
|
43
|
+
const result = await surreal.query('SELECT * FROM _00_query;');
|
|
44
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
45
|
+
});
|
|
46
|
+
server.tool('get_events', 'Get event history, optionally limited', {
|
|
47
|
+
limit: z.number().optional().default(50).describe('Max number of events to return'),
|
|
48
|
+
}, async ({ limit }) => {
|
|
49
|
+
const result = await surreal.query(`SELECT * FROM _00_events ORDER BY timestamp DESC LIMIT ${limit};`);
|
|
50
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
51
|
+
});
|
|
52
|
+
// --- Resources ---
|
|
53
|
+
server.resource('tables', 'sp00ky://tables', { description: 'List of database tables' }, async (uri) => {
|
|
54
|
+
const result = await surreal.query('INFO FOR DB;');
|
|
55
|
+
const info = result;
|
|
56
|
+
const tables = info?.[0]?.result?.tables ?? info?.[0]?.tables ?? {};
|
|
57
|
+
const tableNames = Object.keys(tables);
|
|
58
|
+
return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(tableNames, null, 2) }] };
|
|
59
|
+
});
|
|
60
|
+
server.resource('table-data', new ResourceTemplate('sp00ky://tables/{tableName}', { list: undefined }), { description: 'Contents of a specific database table' }, async (uri, variables) => {
|
|
61
|
+
const tableName = variables.tableName;
|
|
62
|
+
const result = await surreal.query(`SELECT * FROM \`${tableName}\` LIMIT 100;`);
|
|
63
|
+
return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(result, null, 2) }] };
|
|
64
|
+
});
|
|
65
|
+
server.resource('queries', 'sp00ky://queries', { description: 'Active live queries' }, async (uri) => {
|
|
66
|
+
const result = await surreal.query('SELECT * FROM _00_query;');
|
|
67
|
+
return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(result, null, 2) }] };
|
|
68
|
+
});
|
|
69
|
+
server.resource('events', 'sp00ky://events', { description: 'Event history' }, async (uri) => {
|
|
70
|
+
const result = await surreal.query('SELECT * FROM _00_events ORDER BY timestamp DESC LIMIT 50;');
|
|
71
|
+
return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(result, null, 2) }] };
|
|
72
|
+
});
|
|
73
|
+
return server;
|
|
74
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface SurrealConfig {
|
|
2
|
+
url: string;
|
|
3
|
+
namespace: string;
|
|
4
|
+
database: string;
|
|
5
|
+
username: string;
|
|
6
|
+
password: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class SurrealClient {
|
|
9
|
+
private config;
|
|
10
|
+
private authHeader;
|
|
11
|
+
constructor(config: SurrealConfig);
|
|
12
|
+
query(surql: string): Promise<unknown[]>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export class SurrealClient {
|
|
2
|
+
config;
|
|
3
|
+
authHeader;
|
|
4
|
+
constructor(config) {
|
|
5
|
+
this.config = config;
|
|
6
|
+
this.authHeader =
|
|
7
|
+
'Basic ' + Buffer.from(`${config.username}:${config.password}`).toString('base64');
|
|
8
|
+
}
|
|
9
|
+
async query(surql) {
|
|
10
|
+
const res = await fetch(`${this.config.url}/sql`, {
|
|
11
|
+
method: 'POST',
|
|
12
|
+
headers: {
|
|
13
|
+
'Content-Type': 'application/json',
|
|
14
|
+
Authorization: this.authHeader,
|
|
15
|
+
'surreal-ns': this.config.namespace,
|
|
16
|
+
'surreal-db': this.config.database,
|
|
17
|
+
Accept: 'application/json',
|
|
18
|
+
},
|
|
19
|
+
body: surql,
|
|
20
|
+
});
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
const text = await res.text();
|
|
23
|
+
throw new Error(`SurrealDB query failed (${res.status}): ${text}`);
|
|
24
|
+
}
|
|
25
|
+
return res.json();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { SurrealClient } from './surreal.js';
|
|
4
|
+
import { createServer } from './server.js';
|
|
5
|
+
function main() {
|
|
6
|
+
const surreal = new SurrealClient({
|
|
7
|
+
url: process.env.SURREAL_URL ?? 'http://localhost:8666',
|
|
8
|
+
namespace: process.env.SURREAL_NS ?? 'main',
|
|
9
|
+
database: process.env.SURREAL_DB ?? 'main',
|
|
10
|
+
username: process.env.SURREAL_USER ?? 'root',
|
|
11
|
+
password: process.env.SURREAL_PASS ?? 'root',
|
|
12
|
+
});
|
|
13
|
+
const server = createServer(surreal);
|
|
14
|
+
const transport = new StdioServerTransport();
|
|
15
|
+
server.connect(transport).then(() => {
|
|
16
|
+
process.stderr.write('[sp00ky-mcp-proxy] MCP server running on stdio\n');
|
|
17
|
+
});
|
|
18
|
+
const cleanup = async () => {
|
|
19
|
+
process.stderr.write('[sp00ky-mcp-proxy] Shutting down...\n');
|
|
20
|
+
process.exit(0);
|
|
21
|
+
};
|
|
22
|
+
process.on('SIGINT', cleanup);
|
|
23
|
+
process.on('SIGTERM', cleanup);
|
|
24
|
+
}
|
|
25
|
+
main();
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export function createServer(surreal) {
|
|
4
|
+
const server = new McpServer({
|
|
5
|
+
name: 'sp00ky-mcp-proxy',
|
|
6
|
+
version: '0.0.1',
|
|
7
|
+
});
|
|
8
|
+
// --- Tools ---
|
|
9
|
+
server.tool('run_query', 'Execute a SurrealQL query against the database', {
|
|
10
|
+
query: z.string().describe('SurrealQL query to execute'),
|
|
11
|
+
}, async ({ query }) => {
|
|
12
|
+
const result = await surreal.query(query);
|
|
13
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
14
|
+
});
|
|
15
|
+
server.tool('list_tables', 'List all database tables', {}, async () => {
|
|
16
|
+
const result = await surreal.query('INFO FOR DB;');
|
|
17
|
+
const info = result;
|
|
18
|
+
const tables = info?.[0]?.result?.tables ?? info?.[0]?.tables ?? {};
|
|
19
|
+
const tableNames = Object.keys(tables);
|
|
20
|
+
return { content: [{ type: 'text', text: JSON.stringify(tableNames, null, 2) }] };
|
|
21
|
+
});
|
|
22
|
+
server.tool('get_table_data', 'Fetch records from a database table', {
|
|
23
|
+
tableName: z.string().describe('Name of the table'),
|
|
24
|
+
limit: z.number().optional().default(100).describe('Max number of records to return'),
|
|
25
|
+
}, async ({ tableName, limit }) => {
|
|
26
|
+
const result = await surreal.query(`SELECT * FROM \`${tableName}\` LIMIT ${limit};`);
|
|
27
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
28
|
+
});
|
|
29
|
+
server.tool('update_table_row', 'Update a record in a database table', {
|
|
30
|
+
recordId: z.string().describe('Record ID to update (e.g. "users:abc123")'),
|
|
31
|
+
updates: z.record(z.unknown()).describe('Fields to update'),
|
|
32
|
+
}, async ({ recordId, updates }) => {
|
|
33
|
+
const result = await surreal.query(`UPDATE ${recordId} MERGE ${JSON.stringify(updates)};`);
|
|
34
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
35
|
+
});
|
|
36
|
+
server.tool('delete_table_row', 'Delete a record from a database table', {
|
|
37
|
+
recordId: z.string().describe('Record ID to delete (e.g. "users:abc123")'),
|
|
38
|
+
}, async ({ recordId }) => {
|
|
39
|
+
const result = await surreal.query(`DELETE ${recordId};`);
|
|
40
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
41
|
+
});
|
|
42
|
+
server.tool('get_active_queries', 'Get all active live queries registered with the SSP', {}, async () => {
|
|
43
|
+
const result = await surreal.query('SELECT * FROM _00_query;');
|
|
44
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
45
|
+
});
|
|
46
|
+
server.tool('get_events', 'Get event history, optionally limited', {
|
|
47
|
+
limit: z.number().optional().default(50).describe('Max number of events to return'),
|
|
48
|
+
}, async ({ limit }) => {
|
|
49
|
+
const result = await surreal.query(`SELECT * FROM _00_events ORDER BY timestamp DESC LIMIT ${limit};`);
|
|
50
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
51
|
+
});
|
|
52
|
+
// --- Resources ---
|
|
53
|
+
server.resource('tables', 'sp00ky://tables', { description: 'List of database tables' }, async (uri) => {
|
|
54
|
+
const result = await surreal.query('INFO FOR DB;');
|
|
55
|
+
const info = result;
|
|
56
|
+
const tables = info?.[0]?.result?.tables ?? info?.[0]?.tables ?? {};
|
|
57
|
+
const tableNames = Object.keys(tables);
|
|
58
|
+
return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(tableNames, null, 2) }] };
|
|
59
|
+
});
|
|
60
|
+
server.resource('table-data', new ResourceTemplate('sp00ky://tables/{tableName}', { list: undefined }), { description: 'Contents of a specific database table' }, async (uri, variables) => {
|
|
61
|
+
const tableName = variables.tableName;
|
|
62
|
+
const result = await surreal.query(`SELECT * FROM \`${tableName}\` LIMIT 100;`);
|
|
63
|
+
return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(result, null, 2) }] };
|
|
64
|
+
});
|
|
65
|
+
server.resource('queries', 'sp00ky://queries', { description: 'Active live queries' }, async (uri) => {
|
|
66
|
+
const result = await surreal.query('SELECT * FROM _00_query;');
|
|
67
|
+
return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(result, null, 2) }] };
|
|
68
|
+
});
|
|
69
|
+
server.resource('events', 'sp00ky://events', { description: 'Event history' }, async (uri) => {
|
|
70
|
+
const result = await surreal.query('SELECT * FROM _00_events ORDER BY timestamp DESC LIMIT 50;');
|
|
71
|
+
return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(result, null, 2) }] };
|
|
72
|
+
});
|
|
73
|
+
return server;
|
|
74
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface SurrealConfig {
|
|
2
|
+
url: string;
|
|
3
|
+
namespace: string;
|
|
4
|
+
database: string;
|
|
5
|
+
username: string;
|
|
6
|
+
password: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class SurrealClient {
|
|
9
|
+
private config;
|
|
10
|
+
private authHeader;
|
|
11
|
+
constructor(config: SurrealConfig);
|
|
12
|
+
query(surql: string): Promise<unknown[]>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export class SurrealClient {
|
|
2
|
+
config;
|
|
3
|
+
authHeader;
|
|
4
|
+
constructor(config) {
|
|
5
|
+
this.config = config;
|
|
6
|
+
this.authHeader =
|
|
7
|
+
'Basic ' + Buffer.from(`${config.username}:${config.password}`).toString('base64');
|
|
8
|
+
}
|
|
9
|
+
async query(surql) {
|
|
10
|
+
const res = await fetch(`${this.config.url}/sql`, {
|
|
11
|
+
method: 'POST',
|
|
12
|
+
headers: {
|
|
13
|
+
'Content-Type': 'application/json',
|
|
14
|
+
Authorization: this.authHeader,
|
|
15
|
+
'surreal-ns': this.config.namespace,
|
|
16
|
+
'surreal-db': this.config.database,
|
|
17
|
+
Accept: 'application/json',
|
|
18
|
+
},
|
|
19
|
+
body: surql,
|
|
20
|
+
});
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
const text = await res.text();
|
|
23
|
+
throw new Error(`SurrealDB query failed (${res.status}): ${text}`);
|
|
24
|
+
}
|
|
25
|
+
return res.json();
|
|
26
|
+
}
|
|
27
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spooky-sync/cli",
|
|
3
|
-
"version": "0.0.1-canary.
|
|
3
|
+
"version": "0.0.1-canary.35",
|
|
4
4
|
"description": "Generate TypeScript/Dart types from SurrealDB schema files",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/syncgen.cjs",
|
|
@@ -17,15 +17,17 @@
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
|
-
"dist"
|
|
20
|
+
"dist",
|
|
21
|
+
"mcp-proxy"
|
|
21
22
|
],
|
|
22
23
|
"scripts": {
|
|
23
24
|
"dev": "vite",
|
|
24
25
|
"build:rust": "cargo build --release",
|
|
25
26
|
"build:vite": "vite build",
|
|
26
27
|
"build:types": "tsc --emitDeclarationOnly --declaration --declarationDir dist",
|
|
28
|
+
"build:mcp-proxy": "cd ../mcp-proxy && npm run build && cp -r dist ../cli/mcp-proxy",
|
|
27
29
|
"build:js": "npm run build:vite && npm run build:types",
|
|
28
|
-
"build": "npm run build:rust && npm run build:js",
|
|
30
|
+
"build": "npm run build:rust && npm run build:mcp-proxy && npm run build:js",
|
|
29
31
|
"preview": "vite preview",
|
|
30
32
|
"test": "vitest",
|
|
31
33
|
"lint": "eslint src",
|
|
@@ -56,10 +58,10 @@
|
|
|
56
58
|
"vitest": "^1.0.0"
|
|
57
59
|
},
|
|
58
60
|
"optionalDependencies": {
|
|
59
|
-
"@spooky-sync/cli-darwin-arm64": "0.0.1-canary.
|
|
60
|
-
"@spooky-sync/cli-darwin-x64": "0.0.1-canary.
|
|
61
|
-
"@spooky-sync/cli-linux-arm64": "0.0.1-canary.
|
|
62
|
-
"@spooky-sync/cli-linux-x64": "0.0.1-canary.
|
|
63
|
-
"@spooky-sync/cli-win32-x64": "0.0.1-canary.
|
|
61
|
+
"@spooky-sync/cli-darwin-arm64": "0.0.1-canary.35",
|
|
62
|
+
"@spooky-sync/cli-darwin-x64": "0.0.1-canary.35",
|
|
63
|
+
"@spooky-sync/cli-linux-arm64": "0.0.1-canary.35",
|
|
64
|
+
"@spooky-sync/cli-linux-x64": "0.0.1-canary.35",
|
|
65
|
+
"@spooky-sync/cli-win32-x64": "0.0.1-canary.35"
|
|
64
66
|
}
|
|
65
67
|
}
|