@kubun/mcp 0.10.0 → 0.12.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/lib/client.d.ts +1 -1
- package/lib/client.js +79 -1
- package/lib/config.d.ts +1 -1
- package/lib/config.js +47 -1
- package/lib/container.js +14 -1
- package/lib/index.js +2 -1
- package/lib/prompts/data-model-designer.js +2 -2
- package/lib/prompts/index.js +19 -1
- package/lib/run.js +18 -1
- package/lib/tools/cluster.js +92 -1
- package/lib/tools/connection.d.ts +1 -1
- package/lib/tools/connection.js +118 -1
- package/lib/tools/graph.js +293 -1
- package/lib/tools/index.js +3 -1
- package/package.json +22 -22
package/lib/client.d.ts
CHANGED
package/lib/client.js
CHANGED
|
@@ -1 +1,79 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { randomIdentity } from '@kokuin/token';
|
|
2
|
+
import { KubunDB } from '@kubun/db';
|
|
3
|
+
import { NodeSQLiteAdapter } from '@kubun/db-node-sqlite';
|
|
4
|
+
import { PostgresAdapter } from '@kubun/db-postgres';
|
|
5
|
+
import { KubunEngine } from '@kubun/engine';
|
|
6
|
+
import { HTTPClient } from '@kubun/http-client';
|
|
7
|
+
export function parseDatabase(database) {
|
|
8
|
+
if (database.startsWith('http://') || database.startsWith('https://')) {
|
|
9
|
+
return {
|
|
10
|
+
type: 'http',
|
|
11
|
+
url: database
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
if (database.startsWith('postgres://')) {
|
|
15
|
+
return {
|
|
16
|
+
type: 'postgres',
|
|
17
|
+
url: database
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (database === ':memory:') {
|
|
21
|
+
return {
|
|
22
|
+
type: 'memory'
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
type: 'sqlite',
|
|
27
|
+
path: database
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function getAddress(config) {
|
|
31
|
+
switch(config.type){
|
|
32
|
+
case 'http':
|
|
33
|
+
return config.url;
|
|
34
|
+
case 'postgres':
|
|
35
|
+
return config.url;
|
|
36
|
+
case 'sqlite':
|
|
37
|
+
return config.path;
|
|
38
|
+
case 'memory':
|
|
39
|
+
return ':memory:';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export async function createConnection(params) {
|
|
43
|
+
const identity = params.identity ?? randomIdentity();
|
|
44
|
+
const address = getAddress(params);
|
|
45
|
+
if (params.type === 'http') {
|
|
46
|
+
return {
|
|
47
|
+
provider: new HTTPClient({
|
|
48
|
+
identity,
|
|
49
|
+
serverID: params.serverID,
|
|
50
|
+
url: params.url
|
|
51
|
+
}),
|
|
52
|
+
dispose: async ()=>{},
|
|
53
|
+
type: 'http',
|
|
54
|
+
address
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const adapter = params.type === 'postgres' ? new PostgresAdapter({
|
|
58
|
+
url: params.url
|
|
59
|
+
}) : new NodeSQLiteAdapter({
|
|
60
|
+
database: params.type === 'sqlite' ? params.path : ':memory:'
|
|
61
|
+
});
|
|
62
|
+
const db = new KubunDB({
|
|
63
|
+
adapter
|
|
64
|
+
});
|
|
65
|
+
const engine = new KubunEngine({
|
|
66
|
+
db,
|
|
67
|
+
identity,
|
|
68
|
+
plugins: []
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
provider: engine,
|
|
72
|
+
dispose: async ()=>{
|
|
73
|
+
await engine.dispose();
|
|
74
|
+
await db.close();
|
|
75
|
+
},
|
|
76
|
+
type: params.type,
|
|
77
|
+
address
|
|
78
|
+
};
|
|
79
|
+
}
|
package/lib/config.d.ts
CHANGED
package/lib/config.js
CHANGED
|
@@ -1 +1,47 @@
|
|
|
1
|
-
import{createConnection
|
|
1
|
+
import { createConnection, parseDatabase } from './client.js';
|
|
2
|
+
import { prompts } from './prompts/index.js';
|
|
3
|
+
import { createClusterTools } from './tools/cluster.js';
|
|
4
|
+
import { createConnectionTools } from './tools/connection.js';
|
|
5
|
+
import { createGraphTools } from './tools/graph.js';
|
|
6
|
+
function parseConfig(config) {
|
|
7
|
+
if (config.connect != null) {
|
|
8
|
+
return {
|
|
9
|
+
type: 'http',
|
|
10
|
+
url: config.connect,
|
|
11
|
+
identity: config.identity
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
if (config.database != null) {
|
|
15
|
+
return {
|
|
16
|
+
...parseDatabase(config.database),
|
|
17
|
+
identity: config.identity
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
export async function createConfig(config = {}) {
|
|
23
|
+
const params = parseConfig(config);
|
|
24
|
+
const container = {
|
|
25
|
+
current: params != null ? await createConnection(params) : null
|
|
26
|
+
};
|
|
27
|
+
const connectionTools = createConnectionTools(container, config.identity);
|
|
28
|
+
const clusterTools = createClusterTools(container);
|
|
29
|
+
const graphTools = createGraphTools(container);
|
|
30
|
+
return {
|
|
31
|
+
name: 'kubun',
|
|
32
|
+
version: '0.1.0',
|
|
33
|
+
// Serve both revisions: hosts pinned to 2025-11-25 stay supported while 2026-07-28 clients
|
|
34
|
+
// get the stateless path. Nothing here uses server-initiated requests (elicit, sampling,
|
|
35
|
+
// roots), so no capability is lost by a client picking the newer revision.
|
|
36
|
+
protocolVersions: [
|
|
37
|
+
'2026-07-28',
|
|
38
|
+
'2025-11-25'
|
|
39
|
+
],
|
|
40
|
+
prompts,
|
|
41
|
+
tools: {
|
|
42
|
+
...connectionTools,
|
|
43
|
+
...clusterTools,
|
|
44
|
+
...graphTools
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
package/lib/container.js
CHANGED
|
@@ -1 +1,14 @@
|
|
|
1
|
-
export function getProvider(
|
|
1
|
+
export function getProvider(container) {
|
|
2
|
+
return container.current?.provider ?? null;
|
|
3
|
+
}
|
|
4
|
+
export function notConnectedError() {
|
|
5
|
+
return {
|
|
6
|
+
isError: true,
|
|
7
|
+
content: [
|
|
8
|
+
{
|
|
9
|
+
type: 'text',
|
|
10
|
+
text: 'No database connected. Use the connect tool first.'
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
};
|
|
14
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export{createConnection,parseDatabase}from
|
|
1
|
+
export { createConnection, parseDatabase } from './client.js';
|
|
2
|
+
export { createConfig } from './config.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const KUBUN_PROMPT_INSTRUCTIONS
|
|
1
|
+
export const KUBUN_PROMPT_INSTRUCTIONS = `# Kubun Protocol Data Model Generator
|
|
2
2
|
|
|
3
3
|
You are an expert at creating data models following the Kubun protocol specification. Your task is to generate valid JSON schemas for data models based on user requirements.
|
|
4
4
|
|
|
@@ -612,4 +612,4 @@ When generating a model cluster, organize models in this order:
|
|
|
612
612
|
|
|
613
613
|
## Response Format
|
|
614
614
|
|
|
615
|
-
Always generate models as a JSON array, even when a single model is needed. Validate that your output follows the specification exactly.`;
|
|
615
|
+
Always generate models as a JSON array, even when a single model is needed. Validate that your output follows the specification exactly.`;
|
package/lib/prompts/index.js
CHANGED
|
@@ -1 +1,19 @@
|
|
|
1
|
-
import{KUBUN_PROMPT_INSTRUCTIONS
|
|
1
|
+
import { KUBUN_PROMPT_INSTRUCTIONS } from './data-model-designer.js';
|
|
2
|
+
export const prompts = {
|
|
3
|
+
'data-model-designer': {
|
|
4
|
+
description: 'Instructions to help design a data model following the Kubun protocol specification',
|
|
5
|
+
handler: ()=>{
|
|
6
|
+
return {
|
|
7
|
+
messages: [
|
|
8
|
+
{
|
|
9
|
+
role: 'user',
|
|
10
|
+
content: {
|
|
11
|
+
type: 'text',
|
|
12
|
+
text: KUBUN_PROMPT_INSTRUCTIONS
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
package/lib/run.js
CHANGED
|
@@ -1,2 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import { parseArgs } from 'node:util';
|
|
3
|
+
import { serveProcess } from '@mokei/context-server';
|
|
4
|
+
import { createConfig } from './config.js';
|
|
5
|
+
const args = parseArgs({
|
|
6
|
+
options: {
|
|
7
|
+
connect: {
|
|
8
|
+
type: 'string'
|
|
9
|
+
},
|
|
10
|
+
db: {
|
|
11
|
+
type: 'string'
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
const config = await createConfig({
|
|
16
|
+
connect: args.values.connect,
|
|
17
|
+
database: args.values.db
|
|
18
|
+
});
|
|
19
|
+
serveProcess(config);
|
package/lib/tools/cluster.js
CHANGED
|
@@ -1 +1,92 @@
|
|
|
1
|
-
import{ClusterBuilder
|
|
1
|
+
import { ClusterBuilder, clusterModel } from '@kubun/protocol';
|
|
2
|
+
import { createTool } from '@mokei/context-server';
|
|
3
|
+
import { getProvider, notConnectedError } from '../container.js';
|
|
4
|
+
const createClusterInput = {
|
|
5
|
+
type: 'object',
|
|
6
|
+
properties: {
|
|
7
|
+
models: {
|
|
8
|
+
type: 'array',
|
|
9
|
+
items: {
|
|
10
|
+
type: 'object'
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
required: [
|
|
15
|
+
'models'
|
|
16
|
+
],
|
|
17
|
+
additionalProperties: false
|
|
18
|
+
};
|
|
19
|
+
const deployClustersInput = {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {
|
|
22
|
+
clusters: {
|
|
23
|
+
type: 'array',
|
|
24
|
+
items: clusterModel
|
|
25
|
+
},
|
|
26
|
+
id: {
|
|
27
|
+
type: 'string'
|
|
28
|
+
},
|
|
29
|
+
name: {
|
|
30
|
+
type: 'string'
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
required: [
|
|
34
|
+
'clusters'
|
|
35
|
+
],
|
|
36
|
+
additionalProperties: false
|
|
37
|
+
};
|
|
38
|
+
export function createClusterTools(container) {
|
|
39
|
+
// The explicit `undefined` output schema keeps the handler's return type open:
|
|
40
|
+
// a tool declares no output schema, so a handler may return an error result
|
|
41
|
+
// carrying no `structuredContent` alongside a successful structured one.
|
|
42
|
+
const createCluster = createTool({
|
|
43
|
+
description: 'Create a new cluster from model definitions',
|
|
44
|
+
inputSchema: createClusterInput,
|
|
45
|
+
handler: async (ctx)=>{
|
|
46
|
+
const builder = new ClusterBuilder();
|
|
47
|
+
builder.addAll(ctx.input.models);
|
|
48
|
+
const cluster = builder.build();
|
|
49
|
+
return {
|
|
50
|
+
content: [
|
|
51
|
+
{
|
|
52
|
+
type: 'text',
|
|
53
|
+
text: 'Cluster created'
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
structuredContent: {
|
|
57
|
+
cluster
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
const deployClusters = createTool({
|
|
63
|
+
description: 'Deploy clusters to the Kubun backend',
|
|
64
|
+
inputSchema: deployClustersInput,
|
|
65
|
+
handler: async (ctx)=>{
|
|
66
|
+
const provider = getProvider(container);
|
|
67
|
+
if (provider == null) return notConnectedError();
|
|
68
|
+
const result = await provider.deployGraph({
|
|
69
|
+
clusters: ctx.input.clusters,
|
|
70
|
+
id: ctx.input.id,
|
|
71
|
+
name: ctx.input.name
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
content: [
|
|
75
|
+
{
|
|
76
|
+
type: 'text',
|
|
77
|
+
text: `Clusters deployed to graph "${result.id}"`
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
structuredContent: {
|
|
81
|
+
id: result.id,
|
|
82
|
+
record: result.record,
|
|
83
|
+
aliases: result.aliases
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
create_cluster: createCluster,
|
|
90
|
+
deploy_clusters: deployClusters
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SigningIdentity } from '@
|
|
1
|
+
import type { SigningIdentity } from '@kokuin/token';
|
|
2
2
|
import { createTool } from '@mokei/context-server';
|
|
3
3
|
import type { ClientContainer } from '../container.js';
|
|
4
4
|
type ToolDefinition = ReturnType<typeof createTool>;
|
package/lib/tools/connection.js
CHANGED
|
@@ -1 +1,118 @@
|
|
|
1
|
-
import{createTool
|
|
1
|
+
import { createTool } from '@mokei/context-server';
|
|
2
|
+
import { createConnection, parseDatabase } from '../client.js';
|
|
3
|
+
const connectInput = {
|
|
4
|
+
type: 'object',
|
|
5
|
+
properties: {
|
|
6
|
+
database: {
|
|
7
|
+
type: 'string',
|
|
8
|
+
description: 'SQLite file path, postgres:// URL, http(s):// URL, or :memory:'
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
required: [
|
|
12
|
+
'database'
|
|
13
|
+
],
|
|
14
|
+
additionalProperties: false
|
|
15
|
+
};
|
|
16
|
+
const emptyInput = {
|
|
17
|
+
type: 'object'
|
|
18
|
+
};
|
|
19
|
+
export function createConnectionTools(container, identity) {
|
|
20
|
+
// The explicit `undefined` output schema keeps the handler's return type open:
|
|
21
|
+
// a tool declares no output schema, so a handler may return an error result
|
|
22
|
+
// carrying no `structuredContent` alongside a successful structured one.
|
|
23
|
+
const connect = createTool({
|
|
24
|
+
description: 'Connect to a Kubun database. Accepts a SQLite file path, postgres:// URL, http(s):// URL, or :memory: for in-memory.',
|
|
25
|
+
inputSchema: connectInput,
|
|
26
|
+
handler: async (ctx)=>{
|
|
27
|
+
if (container.current != null) {
|
|
28
|
+
await container.current.dispose();
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const config = parseDatabase(ctx.input.database);
|
|
32
|
+
const connection = await createConnection({
|
|
33
|
+
...config,
|
|
34
|
+
identity
|
|
35
|
+
});
|
|
36
|
+
container.current = connection;
|
|
37
|
+
return {
|
|
38
|
+
content: [
|
|
39
|
+
{
|
|
40
|
+
type: 'text',
|
|
41
|
+
text: `Connected to ${connection.type} database: ${connection.address}`
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
structuredContent: {
|
|
45
|
+
type: connection.type,
|
|
46
|
+
address: connection.address
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
} catch (error) {
|
|
50
|
+
container.current = null;
|
|
51
|
+
return {
|
|
52
|
+
isError: true,
|
|
53
|
+
content: [
|
|
54
|
+
{
|
|
55
|
+
type: 'text',
|
|
56
|
+
text: `Failed to connect: ${error instanceof Error ? error.message : String(error)}`
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
const disconnect = createTool({
|
|
64
|
+
description: 'Disconnect from the current Kubun database',
|
|
65
|
+
inputSchema: emptyInput,
|
|
66
|
+
handler: async ()=>{
|
|
67
|
+
if (container.current != null) {
|
|
68
|
+
await container.current.dispose();
|
|
69
|
+
container.current = null;
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
content: [
|
|
73
|
+
{
|
|
74
|
+
type: 'text',
|
|
75
|
+
text: 'Disconnected'
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
const connectionInfo = createTool({
|
|
82
|
+
description: 'Get information about the current database connection',
|
|
83
|
+
inputSchema: emptyInput,
|
|
84
|
+
handler: async ()=>{
|
|
85
|
+
if (container.current == null) {
|
|
86
|
+
return {
|
|
87
|
+
content: [
|
|
88
|
+
{
|
|
89
|
+
type: 'text',
|
|
90
|
+
text: 'Not connected'
|
|
91
|
+
}
|
|
92
|
+
],
|
|
93
|
+
structuredContent: {
|
|
94
|
+
connected: false
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
content: [
|
|
100
|
+
{
|
|
101
|
+
type: 'text',
|
|
102
|
+
text: `Connected to ${container.current.type} database: ${container.current.address}`
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
structuredContent: {
|
|
106
|
+
connected: true,
|
|
107
|
+
type: container.current.type,
|
|
108
|
+
address: container.current.address
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
return {
|
|
114
|
+
connect,
|
|
115
|
+
disconnect,
|
|
116
|
+
connection_info: connectionInfo
|
|
117
|
+
};
|
|
118
|
+
}
|
package/lib/tools/graph.js
CHANGED
|
@@ -1 +1,293 @@
|
|
|
1
|
-
import{createSchema
|
|
1
|
+
import { createSchema } from '@kubun/graphql';
|
|
2
|
+
import { createTool } from '@mokei/context-server';
|
|
3
|
+
import { printSchema } from 'graphql';
|
|
4
|
+
import { getProvider, notConnectedError } from '../container.js';
|
|
5
|
+
const emptyInput = {
|
|
6
|
+
type: 'object'
|
|
7
|
+
};
|
|
8
|
+
const graphInfoInput = {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
id: {
|
|
12
|
+
type: 'string'
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
required: [
|
|
16
|
+
'id'
|
|
17
|
+
],
|
|
18
|
+
additionalProperties: false
|
|
19
|
+
};
|
|
20
|
+
const graphSchemaInput = {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
id: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'The graph ID'
|
|
26
|
+
},
|
|
27
|
+
onlyModels: {
|
|
28
|
+
type: 'array',
|
|
29
|
+
items: {
|
|
30
|
+
type: 'string'
|
|
31
|
+
},
|
|
32
|
+
description: 'Optional array of model IDs or aliases to include in schema. If not provided, all models are included.'
|
|
33
|
+
},
|
|
34
|
+
includeInterfaceDependencies: {
|
|
35
|
+
type: 'boolean',
|
|
36
|
+
description: 'Whether to include interface dependencies (default: true)'
|
|
37
|
+
},
|
|
38
|
+
includeRelationDependencies: {
|
|
39
|
+
type: 'boolean',
|
|
40
|
+
description: 'Whether to include relation dependencies (default: true)'
|
|
41
|
+
},
|
|
42
|
+
includeMutations: {
|
|
43
|
+
type: 'boolean',
|
|
44
|
+
description: 'Whether to include mutations in the schema (default: true)'
|
|
45
|
+
},
|
|
46
|
+
includeSubscriptions: {
|
|
47
|
+
type: 'boolean',
|
|
48
|
+
description: 'Whether to include subscriptions in the schema (default: true)'
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
required: [
|
|
52
|
+
'id'
|
|
53
|
+
],
|
|
54
|
+
additionalProperties: false
|
|
55
|
+
};
|
|
56
|
+
const graphOperationInput = {
|
|
57
|
+
type: 'object',
|
|
58
|
+
properties: {
|
|
59
|
+
id: {
|
|
60
|
+
type: 'string'
|
|
61
|
+
},
|
|
62
|
+
query: {
|
|
63
|
+
type: 'string'
|
|
64
|
+
},
|
|
65
|
+
variables: {
|
|
66
|
+
type: 'object'
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
required: [
|
|
70
|
+
'id',
|
|
71
|
+
'query'
|
|
72
|
+
],
|
|
73
|
+
additionalProperties: false
|
|
74
|
+
};
|
|
75
|
+
function getModelsInfo(result) {
|
|
76
|
+
const aliases = result.aliases ?? {};
|
|
77
|
+
return Object.entries(result.record).reduce((acc, [id, model])=>{
|
|
78
|
+
const alias = aliases[id] ?? model.name;
|
|
79
|
+
acc[alias] = {
|
|
80
|
+
id,
|
|
81
|
+
behavior: model.behavior,
|
|
82
|
+
interfaces: model.interfaces,
|
|
83
|
+
fields: Object.keys(model.schema.properties)
|
|
84
|
+
};
|
|
85
|
+
return acc;
|
|
86
|
+
}, {});
|
|
87
|
+
}
|
|
88
|
+
export function createGraphTools(container) {
|
|
89
|
+
// The explicit `undefined` output schema keeps the handler's return type open:
|
|
90
|
+
// a tool declares no output schema, so a handler may return an error result
|
|
91
|
+
// carrying no `structuredContent` alongside a successful structured one.
|
|
92
|
+
const graphList = createTool({
|
|
93
|
+
description: 'List all available graphs',
|
|
94
|
+
inputSchema: emptyInput,
|
|
95
|
+
handler: async ()=>{
|
|
96
|
+
const provider = getProvider(container);
|
|
97
|
+
if (provider == null) return notConnectedError();
|
|
98
|
+
const result = await provider.listGraphs();
|
|
99
|
+
const graphs = result.graphs.map((graph)=>`${graph.name} (${graph.id})`).join(', ');
|
|
100
|
+
return {
|
|
101
|
+
content: [
|
|
102
|
+
{
|
|
103
|
+
type: 'text',
|
|
104
|
+
text: graphs === '' ? 'No graphs available' : `Available graphs: ${graphs}`
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
structuredContent: {
|
|
108
|
+
graphs: result.graphs
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
const graphInfo = createTool({
|
|
114
|
+
description: 'Get information about a specific graph',
|
|
115
|
+
inputSchema: graphInfoInput,
|
|
116
|
+
handler: async (ctx)=>{
|
|
117
|
+
const provider = getProvider(container);
|
|
118
|
+
if (provider == null) return notConnectedError();
|
|
119
|
+
try {
|
|
120
|
+
const result = await provider.loadGraph({
|
|
121
|
+
id: ctx.input.id
|
|
122
|
+
});
|
|
123
|
+
const info = getModelsInfo(result);
|
|
124
|
+
return {
|
|
125
|
+
content: [
|
|
126
|
+
{
|
|
127
|
+
type: 'text',
|
|
128
|
+
text: JSON.stringify(info)
|
|
129
|
+
}
|
|
130
|
+
],
|
|
131
|
+
structuredContent: info
|
|
132
|
+
};
|
|
133
|
+
} catch (error) {
|
|
134
|
+
return {
|
|
135
|
+
isError: true,
|
|
136
|
+
content: [
|
|
137
|
+
{
|
|
138
|
+
type: 'text',
|
|
139
|
+
text: `Failed to get GraphQL schema: ${error instanceof Error ? error.message : String(error)}`
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
const graphSchema = createTool({
|
|
147
|
+
description: 'Get the GraphQL schema for the given graph ID. Supports selective generation to reduce schema size for LLM context.',
|
|
148
|
+
inputSchema: graphSchemaInput,
|
|
149
|
+
handler: async (ctx)=>{
|
|
150
|
+
const provider = getProvider(container);
|
|
151
|
+
if (provider == null) return notConnectedError();
|
|
152
|
+
try {
|
|
153
|
+
const result = await provider.loadGraph({
|
|
154
|
+
id: ctx.input.id
|
|
155
|
+
});
|
|
156
|
+
const schema = createSchema({
|
|
157
|
+
record: result.record,
|
|
158
|
+
aliases: result.aliases,
|
|
159
|
+
onlyModels: ctx.input.onlyModels,
|
|
160
|
+
includeInterfaceDependencies: ctx.input.includeInterfaceDependencies,
|
|
161
|
+
includeRelationDependencies: ctx.input.includeRelationDependencies,
|
|
162
|
+
includeMutations: ctx.input.includeMutations,
|
|
163
|
+
includeSubscriptions: ctx.input.includeSubscriptions
|
|
164
|
+
});
|
|
165
|
+
return {
|
|
166
|
+
content: [
|
|
167
|
+
{
|
|
168
|
+
type: 'text',
|
|
169
|
+
text: printSchema(schema)
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
};
|
|
173
|
+
} catch (error) {
|
|
174
|
+
return {
|
|
175
|
+
isError: true,
|
|
176
|
+
content: [
|
|
177
|
+
{
|
|
178
|
+
type: 'text',
|
|
179
|
+
text: `Failed to get GraphQL schema: ${error instanceof Error ? error.message : String(error)}`
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
const graphQuery = createTool({
|
|
187
|
+
description: 'Execute a GraphQL query on the given graph ID',
|
|
188
|
+
inputSchema: graphOperationInput,
|
|
189
|
+
handler: async (ctx)=>{
|
|
190
|
+
const provider = getProvider(container);
|
|
191
|
+
if (provider == null) return notConnectedError();
|
|
192
|
+
try {
|
|
193
|
+
const result = await provider.queryGraph({
|
|
194
|
+
id: ctx.input.id,
|
|
195
|
+
text: ctx.input.query,
|
|
196
|
+
variables: ctx.input.variables ?? {}
|
|
197
|
+
});
|
|
198
|
+
if (result.errors != null && result.errors.length > 0) {
|
|
199
|
+
return {
|
|
200
|
+
isError: true,
|
|
201
|
+
content: [
|
|
202
|
+
{
|
|
203
|
+
type: 'text',
|
|
204
|
+
text: result.errors.map((e)=>e.toString()).join(', ')
|
|
205
|
+
}
|
|
206
|
+
],
|
|
207
|
+
structuredContent: {
|
|
208
|
+
errors: result.errors
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
content: [
|
|
214
|
+
{
|
|
215
|
+
type: 'text',
|
|
216
|
+
text: JSON.stringify(result.data)
|
|
217
|
+
}
|
|
218
|
+
],
|
|
219
|
+
structuredContent: {
|
|
220
|
+
data: result.data
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
} catch (error) {
|
|
224
|
+
return {
|
|
225
|
+
isError: true,
|
|
226
|
+
content: [
|
|
227
|
+
{
|
|
228
|
+
type: 'text',
|
|
229
|
+
text: `Failed to execute GraphQL query: ${error instanceof Error ? error.message : String(error)}`
|
|
230
|
+
}
|
|
231
|
+
]
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
const graphMutate = createTool({
|
|
237
|
+
description: 'Execute a GraphQL mutation on the given graph ID',
|
|
238
|
+
inputSchema: graphOperationInput,
|
|
239
|
+
handler: async (ctx)=>{
|
|
240
|
+
const provider = getProvider(container);
|
|
241
|
+
if (provider == null) return notConnectedError();
|
|
242
|
+
try {
|
|
243
|
+
const result = await provider.mutateGraph({
|
|
244
|
+
id: ctx.input.id,
|
|
245
|
+
text: ctx.input.query,
|
|
246
|
+
variables: ctx.input.variables ?? {}
|
|
247
|
+
});
|
|
248
|
+
if (result.errors != null && result.errors.length > 0) {
|
|
249
|
+
return {
|
|
250
|
+
isError: true,
|
|
251
|
+
content: [
|
|
252
|
+
{
|
|
253
|
+
type: 'text',
|
|
254
|
+
text: result.errors.map((e)=>e.toString()).join(', ')
|
|
255
|
+
}
|
|
256
|
+
],
|
|
257
|
+
structuredContent: {
|
|
258
|
+
errors: result.errors
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
content: [
|
|
264
|
+
{
|
|
265
|
+
type: 'text',
|
|
266
|
+
text: JSON.stringify(result.data)
|
|
267
|
+
}
|
|
268
|
+
],
|
|
269
|
+
structuredContent: {
|
|
270
|
+
data: result.data
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
} catch (error) {
|
|
274
|
+
return {
|
|
275
|
+
isError: true,
|
|
276
|
+
content: [
|
|
277
|
+
{
|
|
278
|
+
type: 'text',
|
|
279
|
+
text: `Failed to execute GraphQL mutation: ${error instanceof Error ? error.message : String(error)}`
|
|
280
|
+
}
|
|
281
|
+
]
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
return {
|
|
287
|
+
graph_list: graphList,
|
|
288
|
+
graph_info: graphInfo,
|
|
289
|
+
graph_schema: graphSchema,
|
|
290
|
+
graph_query: graphQuery,
|
|
291
|
+
graph_mutate: graphMutate
|
|
292
|
+
};
|
|
293
|
+
}
|
package/lib/tools/index.js
CHANGED
|
@@ -1 +1,3 @@
|
|
|
1
|
-
export{createClusterTools}from
|
|
1
|
+
export { createClusterTools } from './cluster.js';
|
|
2
|
+
export { createConnectionTools } from './connection.js';
|
|
3
|
+
export { createGraphTools } from './graph.js';
|
package/package.json
CHANGED
|
@@ -1,46 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubun/mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"license": "see LICENSE.md",
|
|
3
|
+
"version": "0.12.0",
|
|
5
4
|
"keywords": [],
|
|
5
|
+
"license": "see LICENSE.md",
|
|
6
|
+
"sideEffects": false,
|
|
6
7
|
"type": "module",
|
|
7
|
-
"main": "lib/index.js",
|
|
8
|
-
"types": "lib/index.d.ts",
|
|
9
8
|
"exports": {
|
|
10
9
|
".": "./lib/index.js",
|
|
11
10
|
"./run": "./lib/run.js"
|
|
12
11
|
},
|
|
12
|
+
"main": "lib/index.js",
|
|
13
|
+
"types": "lib/index.d.ts",
|
|
13
14
|
"bin": {
|
|
14
15
|
"kubun-mcp": "./lib/run.js"
|
|
15
16
|
},
|
|
16
17
|
"files": [
|
|
17
18
|
"lib/*"
|
|
18
19
|
],
|
|
19
|
-
"sideEffects": false,
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@
|
|
22
|
-
"@mokei/context-server": "^0.
|
|
23
|
-
"graphql": "^16.
|
|
24
|
-
"@kubun/db
|
|
25
|
-
"@kubun/
|
|
26
|
-
"@kubun/db": "^0.
|
|
27
|
-
"@kubun/
|
|
28
|
-
"@kubun/
|
|
29
|
-
"@kubun/protocol": "^0.
|
|
30
|
-
"@kubun/
|
|
21
|
+
"@kokuin/token": "^0.3.0",
|
|
22
|
+
"@mokei/context-server": "^0.11.0",
|
|
23
|
+
"graphql": "^16.14.2",
|
|
24
|
+
"@kubun/db": "^0.12.0",
|
|
25
|
+
"@kubun/graphql": "^0.12.0",
|
|
26
|
+
"@kubun/db-postgres": "^0.12.0",
|
|
27
|
+
"@kubun/db-node-sqlite": "^0.12.0",
|
|
28
|
+
"@kubun/http-client": "^0.12.0",
|
|
29
|
+
"@kubun/protocol": "^0.12.0",
|
|
30
|
+
"@kubun/engine": "^0.12.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@enkaku/transport": "^0.
|
|
34
|
-
"@mokei/context-client": "^0.
|
|
35
|
-
"@mokei/context-protocol": "^0.
|
|
33
|
+
"@enkaku/transport": "^0.20.0",
|
|
34
|
+
"@mokei/context-client": "^0.11.0",
|
|
35
|
+
"@mokei/context-protocol": "^0.11.0"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
|
+
"build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
|
|
38
39
|
"build:clean": "del lib",
|
|
39
|
-
"build:js": "swc src -d ./lib --config-file ../../swc.json --strip-leading-paths",
|
|
40
|
+
"build:js": "swc src -d ./lib --config-file ../../node_modules/@kigu/dev/swc.json --strip-leading-paths",
|
|
40
41
|
"build:types": "tsc --emitDeclarationOnly --skipLibCheck",
|
|
41
|
-
"
|
|
42
|
+
"test": "pnpm run test:types && pnpm run test:unit",
|
|
42
43
|
"test:types": "tsc --noEmit -p tsconfig.test.json",
|
|
43
|
-
"test:unit": "vitest run"
|
|
44
|
-
"test": "pnpm run test:types && pnpm run test:unit"
|
|
44
|
+
"test:unit": "vitest run"
|
|
45
45
|
}
|
|
46
46
|
}
|