@kubun/mcp 0.11.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/config.js +7 -0
- package/lib/tools/cluster.js +80 -67
- package/lib/tools/connection.js +88 -73
- package/lib/tools/graph.js +235 -226
- package/package.json +20 -20
package/lib/config.js
CHANGED
|
@@ -30,6 +30,13 @@ export async function createConfig(config = {}) {
|
|
|
30
30
|
return {
|
|
31
31
|
name: 'kubun',
|
|
32
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
|
+
],
|
|
33
40
|
prompts,
|
|
34
41
|
tools: {
|
|
35
42
|
...connectionTools,
|
package/lib/tools/cluster.js
CHANGED
|
@@ -1,79 +1,92 @@
|
|
|
1
1
|
import { ClusterBuilder, clusterModel } from '@kubun/protocol';
|
|
2
2
|
import { createTool } from '@mokei/context-server';
|
|
3
3
|
import { getProvider, notConnectedError } from '../container.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
type: 'object'
|
|
12
|
-
}
|
|
4
|
+
const createClusterInput = {
|
|
5
|
+
type: 'object',
|
|
6
|
+
properties: {
|
|
7
|
+
models: {
|
|
8
|
+
type: 'array',
|
|
9
|
+
items: {
|
|
10
|
+
type: 'object'
|
|
13
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
|
|
14
25
|
},
|
|
15
|
-
|
|
16
|
-
'
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
|
28
58
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
cluster
|
|
32
|
-
}
|
|
33
|
-
};
|
|
59
|
+
};
|
|
60
|
+
}
|
|
34
61
|
});
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
clusters: ctx.arguments.clusters,
|
|
58
|
-
id: ctx.arguments.id,
|
|
59
|
-
name: ctx.arguments.name
|
|
60
|
-
});
|
|
61
|
-
return {
|
|
62
|
-
content: [
|
|
63
|
-
{
|
|
64
|
-
type: 'text',
|
|
65
|
-
text: `Clusters deployed to graph "${result.id}"`
|
|
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
|
|
66
84
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
id: result.id,
|
|
70
|
-
record: result.record,
|
|
71
|
-
aliases: result.aliases
|
|
72
|
-
}
|
|
73
|
-
};
|
|
85
|
+
};
|
|
86
|
+
}
|
|
74
87
|
});
|
|
75
88
|
return {
|
|
76
|
-
create_cluster,
|
|
77
|
-
deploy_clusters
|
|
89
|
+
create_cluster: createCluster,
|
|
90
|
+
deploy_clusters: deployClusters
|
|
78
91
|
};
|
|
79
92
|
}
|
package/lib/tools/connection.js
CHANGED
|
@@ -1,103 +1,118 @@
|
|
|
1
1
|
import { createTool } from '@mokei/context-server';
|
|
2
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
|
+
};
|
|
3
19
|
export function createConnectionTools(container, identity) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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();
|
|
10
29
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
content: [
|
|
29
|
-
{
|
|
30
|
-
type: 'text',
|
|
31
|
-
text: `Connected to ${connection.type} database: ${connection.address}`
|
|
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
|
|
32
47
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
+
}
|
|
41
71
|
return {
|
|
42
|
-
isError: true,
|
|
43
72
|
content: [
|
|
44
73
|
{
|
|
45
74
|
type: 'text',
|
|
46
|
-
text:
|
|
75
|
+
text: 'Disconnected'
|
|
47
76
|
}
|
|
48
77
|
]
|
|
49
78
|
};
|
|
50
79
|
}
|
|
51
80
|
});
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
type: 'object'
|
|
70
|
-
}, async ()=>{
|
|
71
|
-
if (container.current == null) {
|
|
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
|
+
}
|
|
72
98
|
return {
|
|
73
99
|
content: [
|
|
74
100
|
{
|
|
75
101
|
type: 'text',
|
|
76
|
-
text:
|
|
102
|
+
text: `Connected to ${container.current.type} database: ${container.current.address}`
|
|
77
103
|
}
|
|
78
104
|
],
|
|
79
105
|
structuredContent: {
|
|
80
|
-
connected:
|
|
106
|
+
connected: true,
|
|
107
|
+
type: container.current.type,
|
|
108
|
+
address: container.current.address
|
|
81
109
|
}
|
|
82
110
|
};
|
|
83
111
|
}
|
|
84
|
-
return {
|
|
85
|
-
content: [
|
|
86
|
-
{
|
|
87
|
-
type: 'text',
|
|
88
|
-
text: `Connected to ${container.current.type} database: ${container.current.address}`
|
|
89
|
-
}
|
|
90
|
-
],
|
|
91
|
-
structuredContent: {
|
|
92
|
-
connected: true,
|
|
93
|
-
type: container.current.type,
|
|
94
|
-
address: container.current.address
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
112
|
});
|
|
98
113
|
return {
|
|
99
114
|
connect,
|
|
100
115
|
disconnect,
|
|
101
|
-
connection_info
|
|
116
|
+
connection_info: connectionInfo
|
|
102
117
|
};
|
|
103
118
|
}
|
package/lib/tools/graph.js
CHANGED
|
@@ -2,6 +2,76 @@ import { createSchema } from '@kubun/graphql';
|
|
|
2
2
|
import { createTool } from '@mokei/context-server';
|
|
3
3
|
import { printSchema } from 'graphql';
|
|
4
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
|
+
};
|
|
5
75
|
function getModelsInfo(result) {
|
|
6
76
|
const aliases = result.aliases ?? {};
|
|
7
77
|
return Object.entries(result.record).reduce((acc, [id, model])=>{
|
|
@@ -16,269 +86,208 @@ function getModelsInfo(result) {
|
|
|
16
86
|
}, {});
|
|
17
87
|
}
|
|
18
88
|
export function createGraphTools(container) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
text: graphs === '' ? 'No graphs available' : `Available graphs: ${graphs}`
|
|
31
|
-
}
|
|
32
|
-
],
|
|
33
|
-
structuredContent: {
|
|
34
|
-
graphs: result.graphs
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
});
|
|
38
|
-
const graph_info = createTool('Get information about a specific graph', {
|
|
39
|
-
type: 'object',
|
|
40
|
-
properties: {
|
|
41
|
-
id: {
|
|
42
|
-
type: 'string'
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
required: [
|
|
46
|
-
'id'
|
|
47
|
-
],
|
|
48
|
-
additionalProperties: false
|
|
49
|
-
}, async (ctx)=>{
|
|
50
|
-
const provider = getProvider(container);
|
|
51
|
-
if (provider == null) return notConnectedError();
|
|
52
|
-
try {
|
|
53
|
-
const result = await provider.loadGraph({
|
|
54
|
-
id: ctx.arguments.id
|
|
55
|
-
});
|
|
56
|
-
const info = getModelsInfo(result);
|
|
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(', ');
|
|
57
100
|
return {
|
|
58
101
|
content: [
|
|
59
102
|
{
|
|
60
103
|
type: 'text',
|
|
61
|
-
text:
|
|
104
|
+
text: graphs === '' ? 'No graphs available' : `Available graphs: ${graphs}`
|
|
62
105
|
}
|
|
63
106
|
],
|
|
64
|
-
structuredContent:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
isError: true,
|
|
69
|
-
content: [
|
|
70
|
-
{
|
|
71
|
-
type: 'text',
|
|
72
|
-
text: `Failed to get GraphQL schema: ${error instanceof Error ? error.message : String(error)}`
|
|
73
|
-
}
|
|
74
|
-
]
|
|
107
|
+
structuredContent: {
|
|
108
|
+
graphs: result.graphs
|
|
109
|
+
}
|
|
75
110
|
};
|
|
76
111
|
}
|
|
77
112
|
});
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
+
};
|
|
107
143
|
}
|
|
108
|
-
},
|
|
109
|
-
required: [
|
|
110
|
-
'id'
|
|
111
|
-
],
|
|
112
|
-
additionalProperties: false
|
|
113
|
-
}, async (ctx)=>{
|
|
114
|
-
const provider = getProvider(container);
|
|
115
|
-
if (provider == null) return notConnectedError();
|
|
116
|
-
try {
|
|
117
|
-
const result = await provider.loadGraph({
|
|
118
|
-
id: ctx.arguments.id
|
|
119
|
-
});
|
|
120
|
-
const schema = createSchema({
|
|
121
|
-
record: result.record,
|
|
122
|
-
aliases: result.aliases,
|
|
123
|
-
onlyModels: ctx.arguments.onlyModels,
|
|
124
|
-
includeInterfaceDependencies: ctx.arguments.includeInterfaceDependencies,
|
|
125
|
-
includeRelationDependencies: ctx.arguments.includeRelationDependencies,
|
|
126
|
-
includeMutations: ctx.arguments.includeMutations,
|
|
127
|
-
includeSubscriptions: ctx.arguments.includeSubscriptions
|
|
128
|
-
});
|
|
129
|
-
return {
|
|
130
|
-
content: [
|
|
131
|
-
{
|
|
132
|
-
type: 'text',
|
|
133
|
-
text: printSchema(schema)
|
|
134
|
-
}
|
|
135
|
-
]
|
|
136
|
-
};
|
|
137
|
-
} catch (error) {
|
|
138
|
-
return {
|
|
139
|
-
isError: true,
|
|
140
|
-
content: [
|
|
141
|
-
{
|
|
142
|
-
type: 'text',
|
|
143
|
-
text: `Failed to get GraphQL schema: ${error instanceof Error ? error.message : String(error)}`
|
|
144
|
-
}
|
|
145
|
-
]
|
|
146
|
-
};
|
|
147
144
|
}
|
|
148
145
|
});
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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) {
|
|
177
174
|
return {
|
|
178
175
|
isError: true,
|
|
179
176
|
content: [
|
|
180
177
|
{
|
|
181
178
|
type: 'text',
|
|
182
|
-
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)
|
|
183
217
|
}
|
|
184
218
|
],
|
|
185
219
|
structuredContent: {
|
|
186
|
-
|
|
220
|
+
data: result.data
|
|
187
221
|
}
|
|
188
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
|
+
};
|
|
189
233
|
}
|
|
190
|
-
return {
|
|
191
|
-
content: [
|
|
192
|
-
{
|
|
193
|
-
type: 'text',
|
|
194
|
-
text: JSON.stringify(result.data)
|
|
195
|
-
}
|
|
196
|
-
],
|
|
197
|
-
structuredContent: {
|
|
198
|
-
data: result.data
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
} catch (error) {
|
|
202
|
-
return {
|
|
203
|
-
isError: true,
|
|
204
|
-
content: [
|
|
205
|
-
{
|
|
206
|
-
type: 'text',
|
|
207
|
-
text: `Failed to execute GraphQL query: ${error instanceof Error ? error.message : String(error)}`
|
|
208
|
-
}
|
|
209
|
-
]
|
|
210
|
-
};
|
|
211
234
|
}
|
|
212
235
|
});
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
});
|
|
240
|
-
if (result.errors != null && result.errors.length > 0) {
|
|
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
|
+
}
|
|
241
262
|
return {
|
|
242
|
-
isError: true,
|
|
243
263
|
content: [
|
|
244
264
|
{
|
|
245
265
|
type: 'text',
|
|
246
|
-
text:
|
|
266
|
+
text: JSON.stringify(result.data)
|
|
247
267
|
}
|
|
248
268
|
],
|
|
249
269
|
structuredContent: {
|
|
250
|
-
|
|
270
|
+
data: result.data
|
|
251
271
|
}
|
|
252
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
|
+
};
|
|
253
283
|
}
|
|
254
|
-
return {
|
|
255
|
-
content: [
|
|
256
|
-
{
|
|
257
|
-
type: 'text',
|
|
258
|
-
text: JSON.stringify(result.data)
|
|
259
|
-
}
|
|
260
|
-
],
|
|
261
|
-
structuredContent: {
|
|
262
|
-
data: result.data
|
|
263
|
-
}
|
|
264
|
-
};
|
|
265
|
-
} catch (error) {
|
|
266
|
-
return {
|
|
267
|
-
isError: true,
|
|
268
|
-
content: [
|
|
269
|
-
{
|
|
270
|
-
type: 'text',
|
|
271
|
-
text: `Failed to execute GraphQL mutation: ${error instanceof Error ? error.message : String(error)}`
|
|
272
|
-
}
|
|
273
|
-
]
|
|
274
|
-
};
|
|
275
284
|
}
|
|
276
285
|
});
|
|
277
286
|
return {
|
|
278
|
-
graph_list,
|
|
279
|
-
graph_info,
|
|
280
|
-
graph_schema,
|
|
281
|
-
graph_query,
|
|
282
|
-
graph_mutate
|
|
287
|
+
graph_list: graphList,
|
|
288
|
+
graph_info: graphInfo,
|
|
289
|
+
graph_schema: graphSchema,
|
|
290
|
+
graph_query: graphQuery,
|
|
291
|
+
graph_mutate: graphMutate
|
|
283
292
|
};
|
|
284
293
|
}
|
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
|
-
"@kokuin/token": "^0.
|
|
22
|
-
"@mokei/context-server": "^0.
|
|
21
|
+
"@kokuin/token": "^0.3.0",
|
|
22
|
+
"@mokei/context-server": "^0.11.0",
|
|
23
23
|
"graphql": "^16.14.2",
|
|
24
|
-
"@kubun/db": "^0.
|
|
25
|
-
"@kubun/
|
|
26
|
-
"@kubun/db-
|
|
27
|
-
"@kubun/
|
|
28
|
-
"@kubun/http-client": "^0.
|
|
29
|
-
"@kubun/protocol": "^0.
|
|
30
|
-
"@kubun/
|
|
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
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
|
}
|