@cerema/cadriciel-mcp 0.1.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/Dockerfile +26 -0
- package/README.md +98 -0
- package/dist/client.d.ts +31 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +77 -0
- package/dist/client.js.map +1 -0
- package/dist/http-server.d.ts +13 -0
- package/dist/http-server.d.ts.map +1 -0
- package/dist/http-server.js +184 -0
- package/dist/http-server.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/database.d.ts +70 -0
- package/dist/tools/database.d.ts.map +1 -0
- package/dist/tools/database.js +77 -0
- package/dist/tools/database.js.map +1 -0
- package/dist/tools/deploy.d.ts +73 -0
- package/dist/tools/deploy.d.ts.map +1 -0
- package/dist/tools/deploy.js +103 -0
- package/dist/tools/deploy.js.map +1 -0
- package/dist/tools/handlers.d.ts +13 -0
- package/dist/tools/handlers.d.ts.map +1 -0
- package/dist/tools/handlers.js +225 -0
- package/dist/tools/handlers.js.map +1 -0
- package/dist/tools/index.d.ts +11 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +298 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/projects.d.ts +45 -0
- package/dist/tools/projects.d.ts.map +1 -0
- package/dist/tools/projects.js +71 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/workflows.d.ts +66 -0
- package/dist/tools/workflows.d.ts.map +1 -0
- package/dist/tools/workflows.js +90 -0
- package/dist/tools/workflows.js.map +1 -0
- package/k8s/deployment.yaml +48 -0
- package/k8s/ingress.yaml +27 -0
- package/k8s/kustomization.yaml +14 -0
- package/k8s/service.yaml +15 -0
- package/package.json +36 -0
- package/src/client.ts +97 -0
- package/src/http-server.ts +213 -0
- package/src/index.ts +75 -0
- package/src/tools/database.ts +105 -0
- package/src/tools/deploy.ts +127 -0
- package/src/tools/handlers.ts +241 -0
- package/src/tools/index.ts +275 -0
- package/src/tools/projects.ts +89 -0
- package/src/tools/workflows.ts +117 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
2
|
+
kind: Kustomization
|
|
3
|
+
|
|
4
|
+
resources:
|
|
5
|
+
- deployment.yaml
|
|
6
|
+
- service.yaml
|
|
7
|
+
- ingress.yaml
|
|
8
|
+
|
|
9
|
+
commonLabels:
|
|
10
|
+
app.kubernetes.io/name: cadriciel-mcp
|
|
11
|
+
app.kubernetes.io/component: mcp-server
|
|
12
|
+
app.kubernetes.io/part-of: cadriciel
|
|
13
|
+
|
|
14
|
+
namespace: cadriciel-dev
|
package/k8s/service.yaml
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cerema/cadriciel-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP Server Cadriciel - Low-code platform integration",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"cadriciel-mcp": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"start": "node dist/index.js",
|
|
12
|
+
"start:http": "node dist/http-server.js",
|
|
13
|
+
"dev": "ts-node src/index.ts",
|
|
14
|
+
"dev:http": "ts-node src/http-server.ts",
|
|
15
|
+
"watch": "tsc -w"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"mcp",
|
|
19
|
+
"cadriciel",
|
|
20
|
+
"claude",
|
|
21
|
+
"anthropic"
|
|
22
|
+
],
|
|
23
|
+
"author": "CEREMA",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^20.0.0",
|
|
30
|
+
"typescript": "^5.0.0",
|
|
31
|
+
"ts-node": "^10.9.0"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cadriciel API Client
|
|
3
|
+
*
|
|
4
|
+
* HTTP client for communicating with the Cadriciel backend API.
|
|
5
|
+
* Uses the API key for authentication.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface CadricielConfig {
|
|
9
|
+
apiUrl: string;
|
|
10
|
+
apiKey: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ApiResponse<T = any> {
|
|
14
|
+
success: boolean;
|
|
15
|
+
data?: T;
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class CadricielClient {
|
|
20
|
+
private apiUrl: string;
|
|
21
|
+
private apiKey: string;
|
|
22
|
+
|
|
23
|
+
constructor(config: CadricielConfig) {
|
|
24
|
+
this.apiUrl = config.apiUrl.replace(/\/$/, ''); // Remove trailing slash
|
|
25
|
+
this.apiKey = config.apiKey;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Make an authenticated request to the Cadriciel API
|
|
30
|
+
*/
|
|
31
|
+
async request<T = any>(
|
|
32
|
+
method: string,
|
|
33
|
+
endpoint: string,
|
|
34
|
+
body?: any
|
|
35
|
+
): Promise<ApiResponse<T>> {
|
|
36
|
+
const url = `${this.apiUrl}${endpoint}`;
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const response = await fetch(url, {
|
|
40
|
+
method,
|
|
41
|
+
headers: {
|
|
42
|
+
'Content-Type': 'application/json',
|
|
43
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
44
|
+
},
|
|
45
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
const errorText = await response.text();
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
error: `HTTP ${response.status}: ${errorText}`,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const data = await response.json() as T;
|
|
57
|
+
return { success: true, data };
|
|
58
|
+
} catch (error) {
|
|
59
|
+
return {
|
|
60
|
+
success: false,
|
|
61
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Convenience methods
|
|
67
|
+
async get<T = any>(endpoint: string): Promise<ApiResponse<T>> {
|
|
68
|
+
return this.request<T>('GET', endpoint);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async post<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>> {
|
|
72
|
+
return this.request<T>('POST', endpoint, body);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async put<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>> {
|
|
76
|
+
return this.request<T>('PUT', endpoint, body);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async delete<T = any>(endpoint: string): Promise<ApiResponse<T>> {
|
|
80
|
+
return this.request<T>('DELETE', endpoint);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Singleton instance
|
|
85
|
+
let clientInstance: CadricielClient | null = null;
|
|
86
|
+
|
|
87
|
+
export function initClient(config: CadricielConfig): CadricielClient {
|
|
88
|
+
clientInstance = new CadricielClient(config);
|
|
89
|
+
return clientInstance;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function getClient(): CadricielClient {
|
|
93
|
+
if (!clientInstance) {
|
|
94
|
+
throw new Error('Cadriciel client not initialized. Call initClient() first.');
|
|
95
|
+
}
|
|
96
|
+
return clientInstance;
|
|
97
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cadriciel MCP HTTP Server
|
|
3
|
+
*
|
|
4
|
+
* HTTP-based MCP server for remote access.
|
|
5
|
+
* Uses SSE for streaming and HTTP POST for tool calls.
|
|
6
|
+
*
|
|
7
|
+
* Authentication: API key passed in X-API-Key header
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import http from 'http';
|
|
11
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
12
|
+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
13
|
+
import { CadricielClient } from './client';
|
|
14
|
+
import { createToolHandlers } from './tools/handlers';
|
|
15
|
+
|
|
16
|
+
const PORT = parseInt(process.env.PORT || '3000', 10);
|
|
17
|
+
const CADRICIEL_API_URL = process.env.CADRICIEL_API_URL || 'https://api.cerema.dev';
|
|
18
|
+
|
|
19
|
+
// Store active transports by session ID
|
|
20
|
+
const transports = new Map<string, SSEServerTransport>();
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create an MCP server instance for a specific API key
|
|
24
|
+
*/
|
|
25
|
+
function createMcpServer(apiKey: string): Server {
|
|
26
|
+
const client = new CadricielClient({
|
|
27
|
+
apiUrl: CADRICIEL_API_URL,
|
|
28
|
+
apiKey,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const server = new Server(
|
|
32
|
+
{
|
|
33
|
+
name: 'cadriciel-mcp',
|
|
34
|
+
version: '0.1.0',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
capabilities: {
|
|
38
|
+
tools: {},
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// Register tool handlers with this client
|
|
44
|
+
createToolHandlers(server, client);
|
|
45
|
+
|
|
46
|
+
return server;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Parse API key from request headers
|
|
51
|
+
*/
|
|
52
|
+
function getApiKey(req: http.IncomingMessage): string | null {
|
|
53
|
+
const apiKey = req.headers['x-api-key'];
|
|
54
|
+
if (typeof apiKey === 'string' && apiKey.length > 0) {
|
|
55
|
+
return apiKey;
|
|
56
|
+
}
|
|
57
|
+
// Also check Authorization header
|
|
58
|
+
const auth = req.headers['authorization'];
|
|
59
|
+
if (typeof auth === 'string' && auth.startsWith('Bearer ')) {
|
|
60
|
+
return auth.slice(7);
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Generate a unique session ID
|
|
67
|
+
*/
|
|
68
|
+
function generateSessionId(): string {
|
|
69
|
+
return `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* HTTP request handler
|
|
74
|
+
*/
|
|
75
|
+
async function handleRequest(
|
|
76
|
+
req: http.IncomingMessage,
|
|
77
|
+
res: http.ServerResponse
|
|
78
|
+
): Promise<void> {
|
|
79
|
+
// CORS headers
|
|
80
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
81
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
82
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-API-Key, Authorization');
|
|
83
|
+
|
|
84
|
+
// Handle preflight
|
|
85
|
+
if (req.method === 'OPTIONS') {
|
|
86
|
+
res.writeHead(204);
|
|
87
|
+
res.end();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const url = new URL(req.url || '/', `http://${req.headers.host}`);
|
|
92
|
+
|
|
93
|
+
// Health check
|
|
94
|
+
if (url.pathname === '/health' || url.pathname === '/') {
|
|
95
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
96
|
+
res.end(JSON.stringify({
|
|
97
|
+
status: 'ok',
|
|
98
|
+
service: 'cadriciel-mcp',
|
|
99
|
+
version: '0.1.0',
|
|
100
|
+
}));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// SSE endpoint for MCP connection
|
|
105
|
+
if (url.pathname === '/sse' && req.method === 'GET') {
|
|
106
|
+
const apiKey = getApiKey(req);
|
|
107
|
+
if (!apiKey) {
|
|
108
|
+
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
109
|
+
res.end(JSON.stringify({ error: 'API key required (X-API-Key header)' }));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const sessionId = generateSessionId();
|
|
114
|
+
console.log(`[MCP] New SSE connection: ${sessionId}`);
|
|
115
|
+
|
|
116
|
+
// Create MCP server for this session
|
|
117
|
+
const mcpServer = createMcpServer(apiKey);
|
|
118
|
+
|
|
119
|
+
// Create SSE transport
|
|
120
|
+
const transport = new SSEServerTransport('/messages', res);
|
|
121
|
+
transports.set(sessionId, transport);
|
|
122
|
+
|
|
123
|
+
// Connect server to transport
|
|
124
|
+
await mcpServer.connect(transport);
|
|
125
|
+
|
|
126
|
+
// Cleanup on close
|
|
127
|
+
res.on('close', () => {
|
|
128
|
+
console.log(`[MCP] SSE connection closed: ${sessionId}`);
|
|
129
|
+
transports.delete(sessionId);
|
|
130
|
+
mcpServer.close();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Message endpoint for MCP requests
|
|
137
|
+
if (url.pathname === '/messages' && req.method === 'POST') {
|
|
138
|
+
const sessionId = url.searchParams.get('sessionId');
|
|
139
|
+
|
|
140
|
+
if (!sessionId) {
|
|
141
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
142
|
+
res.end(JSON.stringify({ error: 'sessionId required' }));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const transport = transports.get(sessionId);
|
|
147
|
+
if (!transport) {
|
|
148
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
149
|
+
res.end(JSON.stringify({ error: 'Session not found' }));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Read body
|
|
154
|
+
let body = '';
|
|
155
|
+
req.on('data', (chunk) => {
|
|
156
|
+
body += chunk.toString();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
req.on('end', async () => {
|
|
160
|
+
try {
|
|
161
|
+
await transport.handlePostMessage(req, res, body);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error('[MCP] Message handling error:', error);
|
|
164
|
+
if (!res.headersSent) {
|
|
165
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
166
|
+
res.end(JSON.stringify({ error: 'Internal server error' }));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 404 for unknown routes
|
|
175
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
176
|
+
res.end(JSON.stringify({ error: 'Not found' }));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Start the HTTP server
|
|
181
|
+
*/
|
|
182
|
+
export function startHttpServer(): void {
|
|
183
|
+
const server = http.createServer(handleRequest);
|
|
184
|
+
|
|
185
|
+
server.listen(PORT, () => {
|
|
186
|
+
console.log(`
|
|
187
|
+
╔═══════════════════════════════════════════════════════════════╗
|
|
188
|
+
║ Cadriciel MCP HTTP Server ║
|
|
189
|
+
╠═══════════════════════════════════════════════════════════════╣
|
|
190
|
+
║ Port: ${String(PORT).padEnd(48)}║
|
|
191
|
+
║ API URL: ${CADRICIEL_API_URL.padEnd(48)}║
|
|
192
|
+
║ Endpoints: ║
|
|
193
|
+
║ GET /health Health check ║
|
|
194
|
+
║ GET /sse SSE connection (X-API-Key required) ║
|
|
195
|
+
║ POST /messages MCP messages ║
|
|
196
|
+
╚═══════════════════════════════════════════════════════════════╝
|
|
197
|
+
`);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Graceful shutdown
|
|
201
|
+
process.on('SIGTERM', () => {
|
|
202
|
+
console.log('[MCP] Shutting down...');
|
|
203
|
+
server.close(() => {
|
|
204
|
+
console.log('[MCP] Server closed');
|
|
205
|
+
process.exit(0);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Start if run directly
|
|
211
|
+
if (require.main === module) {
|
|
212
|
+
startHttpServer();
|
|
213
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Cadriciel MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Model Context Protocol server for Cadriciel platform integration.
|
|
6
|
+
* Allows AI assistants like Claude to interact with Cadriciel projects,
|
|
7
|
+
* workflows, databases, and deployments.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* CADRICIEL_API_URL=https://studio.cerema.dev CADRICIEL_API_KEY=xxx cadriciel-mcp
|
|
11
|
+
*
|
|
12
|
+
* Environment variables:
|
|
13
|
+
* CADRICIEL_API_URL - Base URL of the Cadriciel API (required)
|
|
14
|
+
* CADRICIEL_API_KEY - API key for authentication (required)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
18
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
19
|
+
import { initClient } from './client';
|
|
20
|
+
import { registerTools } from './tools';
|
|
21
|
+
|
|
22
|
+
const SERVER_NAME = 'cadriciel-mcp';
|
|
23
|
+
const SERVER_VERSION = '0.1.0';
|
|
24
|
+
|
|
25
|
+
async function main(): Promise<void> {
|
|
26
|
+
// Read configuration from environment
|
|
27
|
+
const apiUrl = process.env.CADRICIEL_API_URL;
|
|
28
|
+
const apiKey = process.env.CADRICIEL_API_KEY;
|
|
29
|
+
|
|
30
|
+
if (!apiUrl) {
|
|
31
|
+
console.error('Error: CADRICIEL_API_URL environment variable is required');
|
|
32
|
+
console.error('Example: CADRICIEL_API_URL=https://studio.cerema.dev');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!apiKey) {
|
|
37
|
+
console.error('Error: CADRICIEL_API_KEY environment variable is required');
|
|
38
|
+
console.error('Generate your API key in Cadriciel > Configuration > Cle API');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Initialize API client
|
|
43
|
+
initClient({ apiUrl, apiKey });
|
|
44
|
+
|
|
45
|
+
// Create MCP server
|
|
46
|
+
const server = new Server(
|
|
47
|
+
{
|
|
48
|
+
name: SERVER_NAME,
|
|
49
|
+
version: SERVER_VERSION,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
capabilities: {
|
|
53
|
+
tools: {},
|
|
54
|
+
// TODO: Add resources capability
|
|
55
|
+
// resources: {},
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// Register tools
|
|
61
|
+
registerTools(server);
|
|
62
|
+
|
|
63
|
+
// Connect via stdio transport
|
|
64
|
+
const transport = new StdioServerTransport();
|
|
65
|
+
await server.connect(transport);
|
|
66
|
+
|
|
67
|
+
console.error(`[${SERVER_NAME}] Server started (v${SERVER_VERSION})`);
|
|
68
|
+
console.error(`[${SERVER_NAME}] Connected to: ${apiUrl}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Handle errors
|
|
72
|
+
main().catch((error) => {
|
|
73
|
+
console.error('Fatal error:', error);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Tools
|
|
3
|
+
*
|
|
4
|
+
* Tools for interacting with project databases.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// ============================================================
|
|
8
|
+
// TODO: Implement these tools
|
|
9
|
+
// ============================================================
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* TODO: Get database schema (tables, columns, relationships)
|
|
13
|
+
*
|
|
14
|
+
* @param workspaceId - The workspace/project ID
|
|
15
|
+
* @returns Database schema information
|
|
16
|
+
*/
|
|
17
|
+
export async function getSchema(workspaceId: number): Promise<{
|
|
18
|
+
tables: Array<{
|
|
19
|
+
name: string;
|
|
20
|
+
schema: string;
|
|
21
|
+
columns: Array<{
|
|
22
|
+
name: string;
|
|
23
|
+
type: string;
|
|
24
|
+
nullable: boolean;
|
|
25
|
+
defaultValue?: string;
|
|
26
|
+
isPrimaryKey: boolean;
|
|
27
|
+
isForeignKey: boolean;
|
|
28
|
+
}>;
|
|
29
|
+
}>;
|
|
30
|
+
}> {
|
|
31
|
+
// TODO: Implement
|
|
32
|
+
// Endpoint: GET /api/studio/workspace/:workspaceId/db/tables
|
|
33
|
+
throw new Error('Not implemented yet');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* TODO: Execute a SQL query (SELECT only for safety)
|
|
38
|
+
*
|
|
39
|
+
* @param workspaceId - The workspace/project ID
|
|
40
|
+
* @param query - SQL SELECT query
|
|
41
|
+
* @param params - Query parameters
|
|
42
|
+
* @returns Query results
|
|
43
|
+
*/
|
|
44
|
+
export async function queryDatabase(
|
|
45
|
+
workspaceId: number,
|
|
46
|
+
query: string,
|
|
47
|
+
params?: any[]
|
|
48
|
+
): Promise<{
|
|
49
|
+
rows: any[];
|
|
50
|
+
rowCount: number;
|
|
51
|
+
fields: Array<{ name: string; type: string }>;
|
|
52
|
+
}> {
|
|
53
|
+
// TODO: Implement
|
|
54
|
+
// Endpoint: POST /api/studio/workspace/:workspaceId/db/sql
|
|
55
|
+
// Body: { sql: query, params }
|
|
56
|
+
// IMPORTANT: Validate query is SELECT only (no INSERT/UPDATE/DELETE)
|
|
57
|
+
throw new Error('Not implemented yet');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* TODO: Get table data with pagination
|
|
62
|
+
*
|
|
63
|
+
* @param workspaceId - The workspace/project ID
|
|
64
|
+
* @param tableName - Table name
|
|
65
|
+
* @param options - Pagination and filter options
|
|
66
|
+
* @returns Table rows
|
|
67
|
+
*/
|
|
68
|
+
export async function getTableData(
|
|
69
|
+
workspaceId: number,
|
|
70
|
+
tableName: string,
|
|
71
|
+
options?: {
|
|
72
|
+
limit?: number;
|
|
73
|
+
offset?: number;
|
|
74
|
+
orderBy?: string;
|
|
75
|
+
filter?: Record<string, any>;
|
|
76
|
+
}
|
|
77
|
+
): Promise<any> {
|
|
78
|
+
// TODO: Implement
|
|
79
|
+
// Endpoint: GET /api/studio/workspace/:workspaceId/db/data?table=:tableName
|
|
80
|
+
throw new Error('Not implemented yet');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* TODO: Get ERD (Entity Relationship Diagram) data
|
|
85
|
+
*
|
|
86
|
+
* @param workspaceId - The workspace/project ID
|
|
87
|
+
* @returns ERD data including tables and relationships
|
|
88
|
+
*/
|
|
89
|
+
export async function getERD(workspaceId: number): Promise<any> {
|
|
90
|
+
// TODO: Implement
|
|
91
|
+
// Endpoint: GET /api/studio/workspace/:workspaceId/db/erd
|
|
92
|
+
throw new Error('Not implemented yet');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* TODO: List database functions
|
|
97
|
+
*
|
|
98
|
+
* @param workspaceId - The workspace/project ID
|
|
99
|
+
* @returns List of stored procedures and functions
|
|
100
|
+
*/
|
|
101
|
+
export async function listFunctions(workspaceId: number): Promise<any> {
|
|
102
|
+
// TODO: Implement
|
|
103
|
+
// Endpoint: GET /api/studio/workspace/:workspaceId/db/functions
|
|
104
|
+
throw new Error('Not implemented yet');
|
|
105
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment Tools
|
|
3
|
+
*
|
|
4
|
+
* Tools for managing deployments, builds, and environment variables.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// ============================================================
|
|
8
|
+
// TODO: Implement these tools
|
|
9
|
+
// ============================================================
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* TODO: Get deployment status and info
|
|
13
|
+
*
|
|
14
|
+
* @param workspaceId - The workspace/project ID
|
|
15
|
+
* @returns Current deployment status
|
|
16
|
+
*/
|
|
17
|
+
export async function getDeploymentStatus(workspaceId: number): Promise<{
|
|
18
|
+
status: string;
|
|
19
|
+
version?: string;
|
|
20
|
+
url?: string;
|
|
21
|
+
lastDeployedAt?: string;
|
|
22
|
+
}> {
|
|
23
|
+
// TODO: Implement
|
|
24
|
+
// Endpoint: GET /api/studio/workspace/:workspaceId/deploy
|
|
25
|
+
throw new Error('Not implemented yet');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* TODO: List available builds
|
|
30
|
+
*
|
|
31
|
+
* @param workspaceId - The workspace/project ID
|
|
32
|
+
* @returns List of builds with their tags and status
|
|
33
|
+
*/
|
|
34
|
+
export async function listBuilds(workspaceId: number): Promise<any> {
|
|
35
|
+
// TODO: Implement
|
|
36
|
+
// Endpoint: GET /api/studio/workspace/:workspaceId/deploy/builds
|
|
37
|
+
throw new Error('Not implemented yet');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* TODO: Deploy a specific version
|
|
42
|
+
*
|
|
43
|
+
* @param workspaceId - The workspace/project ID
|
|
44
|
+
* @param version - Version tag to deploy
|
|
45
|
+
* @returns Deployment status
|
|
46
|
+
*/
|
|
47
|
+
export async function deployVersion(
|
|
48
|
+
workspaceId: number,
|
|
49
|
+
version: string
|
|
50
|
+
): Promise<any> {
|
|
51
|
+
// TODO: Implement
|
|
52
|
+
// Endpoint: POST /api/studio/workspace/:workspaceId/deploy/deploy
|
|
53
|
+
// Body: { version }
|
|
54
|
+
throw new Error('Not implemented yet');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* TODO: Get environment variables
|
|
59
|
+
*
|
|
60
|
+
* @param workspaceId - The workspace/project ID
|
|
61
|
+
* @returns Environment variables (values hidden)
|
|
62
|
+
*/
|
|
63
|
+
export async function getEnvVars(workspaceId: number): Promise<{
|
|
64
|
+
variables: Array<{
|
|
65
|
+
key: string;
|
|
66
|
+
hasValue: boolean;
|
|
67
|
+
isSystem: boolean;
|
|
68
|
+
}>;
|
|
69
|
+
}> {
|
|
70
|
+
// TODO: Implement
|
|
71
|
+
// Endpoint: GET /api/studio/workspace/:workspaceId/deploy/env-var-values
|
|
72
|
+
throw new Error('Not implemented yet');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* TODO: Set an environment variable
|
|
77
|
+
*
|
|
78
|
+
* @param workspaceId - The workspace/project ID
|
|
79
|
+
* @param key - Variable name
|
|
80
|
+
* @param value - Variable value
|
|
81
|
+
*/
|
|
82
|
+
export async function setEnvVar(
|
|
83
|
+
workspaceId: number,
|
|
84
|
+
key: string,
|
|
85
|
+
value: string
|
|
86
|
+
): Promise<void> {
|
|
87
|
+
// TODO: Implement
|
|
88
|
+
// Endpoint: POST /api/studio/workspace/:workspaceId/deploy/save-env-var
|
|
89
|
+
// Body: { key, value }
|
|
90
|
+
throw new Error('Not implemented yet');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* TODO: Control services (start/stop/restart)
|
|
95
|
+
*
|
|
96
|
+
* @param workspaceId - The workspace/project ID
|
|
97
|
+
* @param action - Action to perform
|
|
98
|
+
* @param service - Optional specific service
|
|
99
|
+
*/
|
|
100
|
+
export async function serviceControl(
|
|
101
|
+
workspaceId: number,
|
|
102
|
+
action: 'start' | 'stop' | 'restart',
|
|
103
|
+
service?: string
|
|
104
|
+
): Promise<void> {
|
|
105
|
+
// TODO: Implement
|
|
106
|
+
// Endpoint: POST /api/studio/workspace/:workspaceId/deploy/service-control
|
|
107
|
+
// Body: { action, service }
|
|
108
|
+
throw new Error('Not implemented yet');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* TODO: Get service logs
|
|
113
|
+
*
|
|
114
|
+
* @param workspaceId - The workspace/project ID
|
|
115
|
+
* @param service - Service name
|
|
116
|
+
* @param lines - Number of log lines
|
|
117
|
+
* @returns Log output
|
|
118
|
+
*/
|
|
119
|
+
export async function getLogs(
|
|
120
|
+
workspaceId: number,
|
|
121
|
+
service: string,
|
|
122
|
+
lines?: number
|
|
123
|
+
): Promise<{ logs: string }> {
|
|
124
|
+
// TODO: Implement
|
|
125
|
+
// Endpoint: GET /api/studio/workspace/:workspaceId/logs/:service
|
|
126
|
+
throw new Error('Not implemented yet');
|
|
127
|
+
}
|