mcp-bitdak-culture-server 1.0.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/dist/index.js +153 -0
- package/package.json +28 -0
- package/src/index.ts +173 -0
- package/tsconfig.json +16 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import axios from "axios";
|
|
6
|
+
import dotenv from "dotenv";
|
|
7
|
+
dotenv.config();
|
|
8
|
+
const API_URL = process.env.CULTURE_API_URL || "http://localhost:3000/api/mcp/culture";
|
|
9
|
+
const API_KEY = process.env.MCP_API_KEY;
|
|
10
|
+
if (!API_KEY) {
|
|
11
|
+
console.error("MCP_API_KEY is missing in environment variables. Set it in your .env file.");
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
const apiClient = axios.create({
|
|
15
|
+
baseURL: API_URL,
|
|
16
|
+
headers: {
|
|
17
|
+
Authorization: `Bearer ${API_KEY}`,
|
|
18
|
+
"Content-Type": "application/json",
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
const tools = [
|
|
22
|
+
{
|
|
23
|
+
name: "get_culture_posts",
|
|
24
|
+
description: "Get a list of recent culture posts",
|
|
25
|
+
inputSchema: {
|
|
26
|
+
type: "object",
|
|
27
|
+
properties: {
|
|
28
|
+
limit: { type: "number", description: "Number of posts to fetch (default: 20)" },
|
|
29
|
+
category: { type: "string", description: "Filter by category (e.g., 'art', 'writing', 'av', 'lobby')" },
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "get_culture_post",
|
|
35
|
+
description: "Get a specific culture post by ID",
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: "object",
|
|
38
|
+
properties: {
|
|
39
|
+
id: { type: "string", description: "The UUID of the culture post" },
|
|
40
|
+
},
|
|
41
|
+
required: ["id"],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "create_culture_post",
|
|
46
|
+
description: "Create a new culture post",
|
|
47
|
+
inputSchema: {
|
|
48
|
+
type: "object",
|
|
49
|
+
properties: {
|
|
50
|
+
title: { type: "string", description: "Title of the post" },
|
|
51
|
+
description: { type: "string", description: "Description or excerpt" },
|
|
52
|
+
cultureCategory: { type: "string", description: "Category: 'art' | 'writing' | 'av' | 'lobby'" },
|
|
53
|
+
contentType: { type: "string", description: "Content type: 'image' | 'text' | 'video'" },
|
|
54
|
+
images: { type: "array", items: { type: "string" }, description: "Array of image URLs" },
|
|
55
|
+
richTextContent: { type: "string", description: "HTML content for text posts" },
|
|
56
|
+
videoEmbedUrl: { type: "string", description: "Video embed URL (e.g., YouTube)" },
|
|
57
|
+
aiTool: { type: "string", description: "Name of the AI tool used (e.g., 'Claude', 'Midjourney')" },
|
|
58
|
+
promptText: { type: "string", description: "The main AI prompt used" },
|
|
59
|
+
negativePrompt: { type: "string", description: "The negative AI prompt used" },
|
|
60
|
+
promptIsPublic: { type: "boolean", description: "Whether the prompt is visible to others (default: true)" },
|
|
61
|
+
},
|
|
62
|
+
required: ["title", "cultureCategory", "contentType"],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "update_culture_post",
|
|
67
|
+
description: "Update an existing culture post",
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: "object",
|
|
70
|
+
properties: {
|
|
71
|
+
id: { type: "string", description: "The UUID of the culture post to update" },
|
|
72
|
+
title: { type: "string", description: "New title" },
|
|
73
|
+
description: { type: "string", description: "New description" },
|
|
74
|
+
cultureCategory: { type: "string", description: "New category" },
|
|
75
|
+
contentType: { type: "string", description: "New content type" },
|
|
76
|
+
images: { type: "array", items: { type: "string" }, description: "New array of image URLs" },
|
|
77
|
+
richTextContent: { type: "string", description: "New HTML content" },
|
|
78
|
+
videoEmbedUrl: { type: "string", description: "New video embed URL" },
|
|
79
|
+
aiTool: { type: "string", description: "New AI tool name" },
|
|
80
|
+
promptText: { type: "string", description: "New prompt text" },
|
|
81
|
+
promptIsPublic: { type: "boolean", description: "New prompt visibility" },
|
|
82
|
+
},
|
|
83
|
+
required: ["id"],
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
const server = new Server({
|
|
88
|
+
name: "bitdak-culture-mcp",
|
|
89
|
+
version: "1.0.0",
|
|
90
|
+
}, {
|
|
91
|
+
capabilities: {
|
|
92
|
+
tools: {},
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
96
|
+
return { tools };
|
|
97
|
+
});
|
|
98
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
99
|
+
try {
|
|
100
|
+
const { name, arguments: args } = request.params;
|
|
101
|
+
if (name === "get_culture_posts") {
|
|
102
|
+
const response = await apiClient.get("/", { params: args });
|
|
103
|
+
return {
|
|
104
|
+
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
else if (name === "get_culture_post") {
|
|
108
|
+
if (!args || !args.id)
|
|
109
|
+
throw new Error("ID is required");
|
|
110
|
+
const response = await apiClient.get(`/${args.id}`);
|
|
111
|
+
return {
|
|
112
|
+
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
else if (name === "create_culture_post") {
|
|
116
|
+
const response = await apiClient.post("/", args);
|
|
117
|
+
return {
|
|
118
|
+
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
else if (name === "update_culture_post") {
|
|
122
|
+
if (!args || !args.id)
|
|
123
|
+
throw new Error("ID is required");
|
|
124
|
+
const { id, ...updates } = args;
|
|
125
|
+
const response = await apiClient.patch(`/${id}`, updates);
|
|
126
|
+
return {
|
|
127
|
+
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
throw new Error(`Tool not found: ${name}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
let errorMessage = error.message;
|
|
136
|
+
if (error.response) {
|
|
137
|
+
errorMessage = `API Error ${error.response.status}: ${JSON.stringify(error.response.data)}`;
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
141
|
+
isError: true,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
async function main() {
|
|
146
|
+
const transport = new StdioServerTransport();
|
|
147
|
+
await server.connect(transport);
|
|
148
|
+
console.error("Bitdak Culture Center MCP Server running on stdio");
|
|
149
|
+
}
|
|
150
|
+
main().catch((error) => {
|
|
151
|
+
console.error("Server error:", error);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mcp-bitdak-culture-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for interacting with Bitdak Culture Center",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mcp-culture-server": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"start": "node dist/index.js",
|
|
16
|
+
"dev": "ts-node-esm src/index.ts"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@modelcontextprotocol/sdk": "^1.0.1",
|
|
20
|
+
"axios": "^1.7.9",
|
|
21
|
+
"dotenv": "^16.4.7"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^22.10.1",
|
|
25
|
+
"ts-node": "^10.9.2",
|
|
26
|
+
"typescript": "^5.7.2"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import {
|
|
6
|
+
CallToolRequestSchema,
|
|
7
|
+
ListToolsRequestSchema,
|
|
8
|
+
Tool,
|
|
9
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
+
import axios from "axios";
|
|
11
|
+
import dotenv from "dotenv";
|
|
12
|
+
|
|
13
|
+
dotenv.config();
|
|
14
|
+
|
|
15
|
+
const API_URL = process.env.CULTURE_API_URL || "http://localhost:3000/api/mcp/culture";
|
|
16
|
+
const API_KEY = process.env.MCP_API_KEY;
|
|
17
|
+
|
|
18
|
+
if (!API_KEY) {
|
|
19
|
+
console.error("MCP_API_KEY is missing in environment variables. Set it in your .env file.");
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const apiClient = axios.create({
|
|
24
|
+
baseURL: API_URL,
|
|
25
|
+
headers: {
|
|
26
|
+
Authorization: `Bearer ${API_KEY}`,
|
|
27
|
+
"Content-Type": "application/json",
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const tools: Tool[] = [
|
|
32
|
+
{
|
|
33
|
+
name: "get_culture_posts",
|
|
34
|
+
description: "Get a list of recent culture posts",
|
|
35
|
+
inputSchema: {
|
|
36
|
+
type: "object",
|
|
37
|
+
properties: {
|
|
38
|
+
limit: { type: "number", description: "Number of posts to fetch (default: 20)" },
|
|
39
|
+
category: { type: "string", description: "Filter by category (e.g., 'art', 'writing', 'av', 'lobby')" },
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "get_culture_post",
|
|
45
|
+
description: "Get a specific culture post by ID",
|
|
46
|
+
inputSchema: {
|
|
47
|
+
type: "object",
|
|
48
|
+
properties: {
|
|
49
|
+
id: { type: "string", description: "The UUID of the culture post" },
|
|
50
|
+
},
|
|
51
|
+
required: ["id"],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "create_culture_post",
|
|
56
|
+
description: "Create a new culture post",
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: "object",
|
|
59
|
+
properties: {
|
|
60
|
+
title: { type: "string", description: "Title of the post" },
|
|
61
|
+
description: { type: "string", description: "Description or excerpt" },
|
|
62
|
+
cultureCategory: { type: "string", description: "Category: 'art' | 'writing' | 'av' | 'lobby'" },
|
|
63
|
+
contentType: { type: "string", description: "Content type: 'image' | 'text' | 'video'" },
|
|
64
|
+
images: { type: "array", items: { type: "string" }, description: "Array of image URLs" },
|
|
65
|
+
richTextContent: { type: "string", description: "HTML content for text posts" },
|
|
66
|
+
videoEmbedUrl: { type: "string", description: "Video embed URL (e.g., YouTube)" },
|
|
67
|
+
aiTool: { type: "string", description: "Name of the AI tool used (e.g., 'Claude', 'Midjourney')" },
|
|
68
|
+
promptText: { type: "string", description: "The main AI prompt used" },
|
|
69
|
+
negativePrompt: { type: "string", description: "The negative AI prompt used" },
|
|
70
|
+
promptIsPublic: { type: "boolean", description: "Whether the prompt is visible to others (default: true)" },
|
|
71
|
+
},
|
|
72
|
+
required: ["title", "cultureCategory", "contentType"],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "update_culture_post",
|
|
77
|
+
description: "Update an existing culture post",
|
|
78
|
+
inputSchema: {
|
|
79
|
+
type: "object",
|
|
80
|
+
properties: {
|
|
81
|
+
id: { type: "string", description: "The UUID of the culture post to update" },
|
|
82
|
+
title: { type: "string", description: "New title" },
|
|
83
|
+
description: { type: "string", description: "New description" },
|
|
84
|
+
cultureCategory: { type: "string", description: "New category" },
|
|
85
|
+
contentType: { type: "string", description: "New content type" },
|
|
86
|
+
images: { type: "array", items: { type: "string" }, description: "New array of image URLs" },
|
|
87
|
+
richTextContent: { type: "string", description: "New HTML content" },
|
|
88
|
+
videoEmbedUrl: { type: "string", description: "New video embed URL" },
|
|
89
|
+
aiTool: { type: "string", description: "New AI tool name" },
|
|
90
|
+
promptText: { type: "string", description: "New prompt text" },
|
|
91
|
+
promptIsPublic: { type: "boolean", description: "New prompt visibility" },
|
|
92
|
+
},
|
|
93
|
+
required: ["id"],
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
const server = new Server(
|
|
99
|
+
{
|
|
100
|
+
name: "bitdak-culture-mcp",
|
|
101
|
+
version: "1.0.0",
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
capabilities: {
|
|
105
|
+
tools: {},
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
111
|
+
return { tools };
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
115
|
+
try {
|
|
116
|
+
const { name, arguments: args } = request.params;
|
|
117
|
+
|
|
118
|
+
if (name === "get_culture_posts") {
|
|
119
|
+
const response = await apiClient.get("/", { params: args });
|
|
120
|
+
return {
|
|
121
|
+
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
else if (name === "get_culture_post") {
|
|
126
|
+
if (!args || !args.id) throw new Error("ID is required");
|
|
127
|
+
const response = await apiClient.get(`/${args.id}`);
|
|
128
|
+
return {
|
|
129
|
+
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
else if (name === "create_culture_post") {
|
|
134
|
+
const response = await apiClient.post("/", args);
|
|
135
|
+
return {
|
|
136
|
+
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
else if (name === "update_culture_post") {
|
|
141
|
+
if (!args || !args.id) throw new Error("ID is required");
|
|
142
|
+
const { id, ...updates } = args;
|
|
143
|
+
const response = await apiClient.patch(`/${id}`, updates);
|
|
144
|
+
return {
|
|
145
|
+
content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
else {
|
|
150
|
+
throw new Error(`Tool not found: ${name}`);
|
|
151
|
+
}
|
|
152
|
+
} catch (error: any) {
|
|
153
|
+
let errorMessage = error.message;
|
|
154
|
+
if (error.response) {
|
|
155
|
+
errorMessage = `API Error ${error.response.status}: ${JSON.stringify(error.response.data)}`;
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
159
|
+
isError: true,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
async function main() {
|
|
165
|
+
const transport = new StdioServerTransport();
|
|
166
|
+
await server.connect(transport);
|
|
167
|
+
console.error("Bitdak Culture Center MCP Server running on stdio");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
main().catch((error) => {
|
|
171
|
+
console.error("Server error:", error);
|
|
172
|
+
process.exit(1);
|
|
173
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true
|
|
12
|
+
},
|
|
13
|
+
"include": [
|
|
14
|
+
"src/**/*"
|
|
15
|
+
]
|
|
16
|
+
}
|