@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 CHANGED
@@ -1,4 +1,4 @@
1
- import { type SigningIdentity } from '@enkaku/token';
1
+ import { type SigningIdentity } from '@kokuin/token';
2
2
  import type { GraphsProvider } from '@kubun/protocol';
3
3
  export type ClientConfig = {
4
4
  type: 'memory';
package/lib/client.js CHANGED
@@ -1 +1,79 @@
1
- import{randomIdentity as t}from"@enkaku/token";import{KubunDB as e}from"@kubun/db";import{NodeSQLiteAdapter as r}from"@kubun/db-node-sqlite";import{PostgresAdapter as p}from"@kubun/db-postgres";import{KubunEngine as s}from"@kubun/engine";import{HTTPClient as o}from"@kubun/http-client";export function parseDatabase(t){return t.startsWith("http://")||t.startsWith("https://")?{type:"http",url:t}:t.startsWith("postgres://")?{type:"postgres",url:t}:":memory:"===t?{type:"memory"}:{type:"sqlite",path:t}}export async function createConnection(n){let i=n.identity??t(),a=function(t){switch(t.type){case"http":case"postgres":return t.url;case"sqlite":return t.path;case"memory":return":memory:"}}(n);if("http"===n.type)return{provider:new o({identity:i,serverID:n.serverID,url:n.url}),dispose:async()=>{},type:"http",address:a};let u=new e({adapter:"postgres"===n.type?new p({url:n.url}):new r({database:"sqlite"===n.type?n.path:":memory:"})}),m=new s({db:u,identity:i,plugins:[]});return{provider:m,dispose:async()=>{await m.dispose(),await u.close()},type:n.type,address:a}}
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
@@ -1,4 +1,4 @@
1
- import type { SigningIdentity } from '@enkaku/token';
1
+ import type { SigningIdentity } from '@kokuin/token';
2
2
  import type { ServerConfig } from '@mokei/context-server';
3
3
  export type MCPConfig = {
4
4
  connect?: string;
package/lib/config.js CHANGED
@@ -1 +1,47 @@
1
- import{createConnection as t,parseDatabase as n}from"./client.js";import{prompts as o}from"./prompts/index.js";import{createClusterTools as e}from"./tools/cluster.js";import{createConnectionTools as i}from"./tools/connection.js";import{createGraphTools as r}from"./tools/graph.js";export async function createConfig(l={}){let s=null!=l.connect?{type:"http",url:l.connect,identity:l.identity}:null!=l.database?{...n(l.database),identity:l.identity}:null,a={current:null!=s?await t(s):null},c=i(a,l.identity),m=e(a),u=r(a);return{name:"kubun",version:"0.1.0",prompts:o,tools:{...c,...m,...u}}}
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(t){return t.current?.provider??null}export function notConnectedError(){return{isError:!0,content:[{type:"text",text:"No database connected. Use the connect tool first."}]}}
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"./client.js";export{createConfig}from"./config.js";
1
+ export { createConnection, parseDatabase } from './client.js';
2
+ export { createConfig } from './config.js';
@@ -1,4 +1,4 @@
1
- export const KUBUN_PROMPT_INSTRUCTIONS=`# Kubun Protocol Data Model Generator
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.`;
@@ -1 +1,19 @@
1
- import{KUBUN_PROMPT_INSTRUCTIONS as e}from"./data-model-designer.js";export const prompts={"data-model-designer":{description:"Instructions to help design a data model following the Kubun protocol specification",handler:()=>({messages:[{role:"user",content:{type:"text",text:e}}]})}};
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{parseArgs as t}from"node:util";import{serveProcess as e}from"@mokei/context-server";import{createConfig as o}from"./config.js";let n=t({options:{connect:{type:"string"},db:{type:"string"}}});e(await o({connect:n.values.connect,database:n.values.db}));
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);
@@ -1 +1,92 @@
1
- import{ClusterBuilder as e,clusterModel as t}from"@kubun/protocol";import{createTool as r}from"@mokei/context-server";import{getProvider as s,notConnectedError as o}from"../container.js";export function createClusterTools(n){return{create_cluster:r("Create a new cluster from model definitions",{type:"object",properties:{models:{type:"array",items:{type:"object"}}},required:["models"],additionalProperties:!1},async t=>{let r=new e;return r.addAll(t.arguments.models),{content:[{type:"text",text:"Cluster created"}],structuredContent:{cluster:r.build()}}}),deploy_clusters:r("Deploy clusters to the Kubun backend",{type:"object",properties:{clusters:{type:"array",items:t},id:{type:"string"},name:{type:"string"}},required:["clusters"],additionalProperties:!1},async e=>{let t=s(n);if(null==t)return o();let r=await t.deployGraph({clusters:e.arguments.clusters,id:e.arguments.id,name:e.arguments.name});return{content:[{type:"text",text:`Clusters deployed to graph "${r.id}"`}],structuredContent:{id:r.id,record:r.record,aliases:r.aliases}}})}}
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 '@enkaku/token';
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>;
@@ -1 +1,118 @@
1
- import{createTool as t}from"@mokei/context-server";import{createConnection as e,parseDatabase as n}from"../client.js";export function createConnectionTools(r,o){let c=t("Connect to a Kubun database. Accepts a SQLite file path, postgres:// URL, http(s):// URL, or :memory: for in-memory.",{type:"object",properties:{database:{type:"string",description:"SQLite file path, postgres:// URL, http(s):// URL, or :memory:"}},required:["database"],additionalProperties:!1},async t=>{null!=r.current&&await r.current.dispose();try{let c=n(t.arguments.database),a=await e({...c,identity:o});return r.current=a,{content:[{type:"text",text:`Connected to ${a.type} database: ${a.address}`}],structuredContent:{type:a.type,address:a.address}}}catch(t){return r.current=null,{isError:!0,content:[{type:"text",text:`Failed to connect: ${t instanceof Error?t.message:String(t)}`}]}}});return{connect:c,disconnect:t("Disconnect from the current Kubun database",{type:"object"},async()=>(null!=r.current&&(await r.current.dispose(),r.current=null),{content:[{type:"text",text:"Disconnected"}]})),connection_info:t("Get information about the current database connection",{type:"object"},async()=>null==r.current?{content:[{type:"text",text:"Not connected"}],structuredContent:{connected:!1}}:{content:[{type:"text",text:`Connected to ${r.current.type} database: ${r.current.address}`}],structuredContent:{connected:!0,type:r.current.type,address:r.current.address}})}}
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
+ }
@@ -1 +1,293 @@
1
- import{createSchema as e}from"@kubun/graphql";import{createTool as t}from"@mokei/context-server";import{printSchema as r}from"graphql";import{getProvider as n,notConnectedError as i}from"../container.js";export function createGraphTools(a){let o=t("List all available graphs",{type:"object"},async()=>{let e=n(a);if(null==e)return i();let t=await e.listGraphs(),r=t.graphs.map(e=>`${e.name} (${e.id})`).join(", ");return{content:[{type:"text",text:""===r?"No graphs available":`Available graphs: ${r}`}],structuredContent:{graphs:t.graphs}}}),s=t("Get information about a specific graph",{type:"object",properties:{id:{type:"string"}},required:["id"],additionalProperties:!1},async e=>{let t=n(a);if(null==t)return i();try{var r;let n,i=(n=(r=await t.loadGraph({id:e.arguments.id})).aliases??{},Object.entries(r.record).reduce((e,[t,r])=>(e[n[t]??r.name]={id:t,behavior:r.behavior,interfaces:r.interfaces,fields:Object.keys(r.schema.properties)},e),{}));return{content:[{type:"text",text:JSON.stringify(i)}],structuredContent:i}}catch(e){return{isError:!0,content:[{type:"text",text:`Failed to get GraphQL schema: ${e instanceof Error?e.message:String(e)}`}]}}}),u=t("Get the GraphQL schema for the given graph ID. Supports selective generation to reduce schema size for LLM context.",{type:"object",properties:{id:{type:"string",description:"The graph ID"},onlyModels:{type:"array",items:{type:"string"},description:"Optional array of model IDs or aliases to include in schema. If not provided, all models are included."},includeInterfaceDependencies:{type:"boolean",description:"Whether to include interface dependencies (default: true)"},includeRelationDependencies:{type:"boolean",description:"Whether to include relation dependencies (default: true)"},includeMutations:{type:"boolean",description:"Whether to include mutations in the schema (default: true)"},includeSubscriptions:{type:"boolean",description:"Whether to include subscriptions in the schema (default: true)"}},required:["id"],additionalProperties:!1},async t=>{let o=n(a);if(null==o)return i();try{let n=await o.loadGraph({id:t.arguments.id}),i=e({record:n.record,aliases:n.aliases,onlyModels:t.arguments.onlyModels,includeInterfaceDependencies:t.arguments.includeInterfaceDependencies,includeRelationDependencies:t.arguments.includeRelationDependencies,includeMutations:t.arguments.includeMutations,includeSubscriptions:t.arguments.includeSubscriptions});return{content:[{type:"text",text:r(i)}]}}catch(e){return{isError:!0,content:[{type:"text",text:`Failed to get GraphQL schema: ${e instanceof Error?e.message:String(e)}`}]}}});return{graph_list:o,graph_info:s,graph_schema:u,graph_query:t("Execute a GraphQL query on the given graph ID",{type:"object",properties:{id:{type:"string"},query:{type:"string"},variables:{type:"object"}},required:["id","query"],additionalProperties:!1},async e=>{let t=n(a);if(null==t)return i();try{let r=await t.queryGraph({id:e.arguments.id,text:e.arguments.query,variables:e.arguments.variables??{}});if(null!=r.errors&&r.errors.length>0)return{isError:!0,content:[{type:"text",text:r.errors.map(e=>e.toString()).join(", ")}],structuredContent:{errors:r.errors}};return{content:[{type:"text",text:JSON.stringify(r.data)}],structuredContent:{data:r.data}}}catch(e){return{isError:!0,content:[{type:"text",text:`Failed to execute GraphQL query: ${e instanceof Error?e.message:String(e)}`}]}}}),graph_mutate:t("Execute a GraphQL mutation on the given graph ID",{type:"object",properties:{id:{type:"string"},query:{type:"string"},variables:{type:"object"}},required:["id","query"],additionalProperties:!1},async e=>{let t=n(a);if(null==t)return i();try{let r=await t.mutateGraph({id:e.arguments.id,text:e.arguments.query,variables:e.arguments.variables??{}});if(null!=r.errors&&r.errors.length>0)return{isError:!0,content:[{type:"text",text:r.errors.map(e=>e.toString()).join(", ")}],structuredContent:{errors:r.errors}};return{content:[{type:"text",text:JSON.stringify(r.data)}],structuredContent:{data:r.data}}}catch(e){return{isError:!0,content:[{type:"text",text:`Failed to execute GraphQL mutation: ${e instanceof Error?e.message:String(e)}`}]}}})}}
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
+ }
@@ -1 +1,3 @@
1
- export{createClusterTools}from"./cluster.js";export{createConnectionTools}from"./connection.js";export{createGraphTools}from"./graph.js";
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.10.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
- "@enkaku/token": "^0.16.0",
22
- "@mokei/context-server": "^0.7.0",
23
- "graphql": "^16.13.2",
24
- "@kubun/db-node-sqlite": "^0.10.0",
25
- "@kubun/db-postgres": "^0.10.0",
26
- "@kubun/db": "^0.10.0",
27
- "@kubun/engine": "^0.10.0",
28
- "@kubun/graphql": "^0.10.0",
29
- "@kubun/protocol": "^0.10.0",
30
- "@kubun/http-client": "^0.10.0"
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.16.0",
34
- "@mokei/context-client": "^0.7.0",
35
- "@mokei/context-protocol": "^0.7.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
- "build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
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
  }