@gokiteam/goki-dev 0.2.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/README.md +478 -0
- package/bin/goki-dev.js +452 -0
- package/bin/mcp-server.js +16 -0
- package/bin/secrets-cli.js +302 -0
- package/cli/ComposeOverrideGenerator.js +226 -0
- package/cli/ComposeParser.js +73 -0
- package/cli/ConfigGenerator.js +304 -0
- package/cli/ConfigManager.js +46 -0
- package/cli/DatabaseManager.js +94 -0
- package/cli/DevToolsChecker.js +21 -0
- package/cli/DevToolsDir.js +66 -0
- package/cli/DevToolsManager.js +451 -0
- package/cli/DockerManager.js +138 -0
- package/cli/FunctionManager.js +95 -0
- package/cli/HttpProxyRewriter.js +91 -0
- package/cli/Logger.js +10 -0
- package/cli/McpConfigManager.js +123 -0
- package/cli/NgrokManager.js +431 -0
- package/cli/ProjectCLI.js +2322 -0
- package/cli/PubSubManager.js +129 -0
- package/cli/SnapshotManager.js +88 -0
- package/cli/UiFormatter.js +292 -0
- package/cli/WebhookUrlRewriter.js +32 -0
- package/cli/secrets/BiometricAuth.js +125 -0
- package/cli/secrets/SecretInjector.js +47 -0
- package/cli/secrets/SecretsConfig.js +141 -0
- package/cli/secrets/SecretsDoctor.js +384 -0
- package/cli/secrets/SecretsManager.js +255 -0
- package/client/dist/client.d.ts +332 -0
- package/client/dist/client.js +507 -0
- package/client/dist/helpers.d.ts +62 -0
- package/client/dist/helpers.js +122 -0
- package/client/dist/index.d.ts +59 -0
- package/client/dist/index.js +78 -0
- package/client/dist/package.json +1 -0
- package/client/dist/types.d.ts +280 -0
- package/client/dist/types.js +7 -0
- package/config.development +46 -0
- package/config.test +18 -0
- package/guidelines/CodingStyleGuideline.md +148 -0
- package/guidelines/CommentingGuideline.md +10 -0
- package/guidelines/HttpApiImplementationGuideline.md +137 -0
- package/guidelines/NamingGuideline.md +182 -0
- package/package.json +138 -0
- package/patterns/api/[collectionName]/Controllers.md +62 -0
- package/patterns/api/[collectionName]/Logic.md +154 -0
- package/patterns/api/[collectionName]/Permissions.md +81 -0
- package/patterns/api/[collectionName]/Router.md +83 -0
- package/patterns/api/[collectionName]/Schemas.md +197 -0
- package/patterns/configs/Patterns.md +7 -0
- package/patterns/enums/Patterns.md +24 -0
- package/patterns/errorHandling/Patterns.md +185 -0
- package/patterns/testing/Patterns.md +232 -0
- package/src/Server.js +238 -0
- package/src/api/dashboard/Controllers.js +9 -0
- package/src/api/dashboard/Logic.js +76 -0
- package/src/api/dashboard/Router.js +11 -0
- package/src/api/dashboard/Schemas.js +47 -0
- package/src/api/data/Controllers.js +26 -0
- package/src/api/data/Logic.js +188 -0
- package/src/api/data/Router.js +16 -0
- package/src/api/docker/Controllers.js +33 -0
- package/src/api/docker/Logic.js +268 -0
- package/src/api/docker/Router.js +15 -0
- package/src/api/docker/Schemas.js +80 -0
- package/src/api/docs/Controllers.js +15 -0
- package/src/api/docs/Logic.js +85 -0
- package/src/api/docs/Router.js +12 -0
- package/src/api/export/Controllers.js +30 -0
- package/src/api/export/Logic.js +143 -0
- package/src/api/export/Router.js +18 -0
- package/src/api/export/Schemas.js +104 -0
- package/src/api/firestore/Controllers.js +152 -0
- package/src/api/firestore/Logic.js +474 -0
- package/src/api/firestore/Router.js +23 -0
- package/src/api/functions/Controllers.js +261 -0
- package/src/api/functions/Logic.js +710 -0
- package/src/api/functions/Router.js +50 -0
- package/src/api/functions/Schemas.js +193 -0
- package/src/api/gateway/Controllers.js +72 -0
- package/src/api/gateway/Logic.js +74 -0
- package/src/api/gateway/Router.js +10 -0
- package/src/api/gateway/Schemas.js +19 -0
- package/src/api/health/Controllers.js +14 -0
- package/src/api/health/Logic.js +24 -0
- package/src/api/health/Router.js +12 -0
- package/src/api/httpTraffic/Controllers.js +29 -0
- package/src/api/httpTraffic/Logic.js +33 -0
- package/src/api/httpTraffic/Router.js +9 -0
- package/src/api/httpTraffic/Schemas.js +23 -0
- package/src/api/logging/Controllers.js +80 -0
- package/src/api/logging/Logic.js +461 -0
- package/src/api/logging/Router.js +24 -0
- package/src/api/logging/Schemas.js +43 -0
- package/src/api/mqtt/Controllers.js +17 -0
- package/src/api/mqtt/Logic.js +66 -0
- package/src/api/mqtt/Router.js +12 -0
- package/src/api/postgres/Controllers.js +97 -0
- package/src/api/postgres/Logic.js +221 -0
- package/src/api/postgres/Router.js +21 -0
- package/src/api/pubsub/Controllers.js +236 -0
- package/src/api/pubsub/Logic.js +732 -0
- package/src/api/pubsub/Router.js +41 -0
- package/src/api/pubsub/Schemas.js +355 -0
- package/src/api/redis/Controllers.js +63 -0
- package/src/api/redis/Logic.js +239 -0
- package/src/api/redis/Router.js +21 -0
- package/src/api/scheduler/Controllers.js +27 -0
- package/src/api/scheduler/Logic.js +49 -0
- package/src/api/scheduler/Router.js +16 -0
- package/src/api/services/Controllers.js +26 -0
- package/src/api/services/Logic.js +205 -0
- package/src/api/services/Router.js +14 -0
- package/src/api/services/Schemas.js +66 -0
- package/src/api/snapshots/Controllers.js +37 -0
- package/src/api/snapshots/Logic.js +797 -0
- package/src/api/snapshots/Router.js +15 -0
- package/src/api/snapshots/Schemas.js +23 -0
- package/src/api/webhooks/Controllers.js +49 -0
- package/src/api/webhooks/Logic.js +137 -0
- package/src/api/webhooks/Router.js +12 -0
- package/src/api/webhooks/Schemas.js +31 -0
- package/src/configs/Application.js +147 -0
- package/src/configs/Default.js +13 -0
- package/src/consumers/BlackboxLogsConsumer.js +235 -0
- package/src/consumers/DockerLogsConsumer.js +687 -0
- package/src/db/Tables.js +66 -0
- package/src/db/schemas/firestore.js +18 -0
- package/src/db/schemas/functions.js +65 -0
- package/src/db/schemas/httpTraffic.js +43 -0
- package/src/db/schemas/logging.js +74 -0
- package/src/db/schemas/migrations.js +64 -0
- package/src/db/schemas/mqtt.js +56 -0
- package/src/db/schemas/pubsub.js +90 -0
- package/src/db/schemas/pubsubRegistry.js +22 -0
- package/src/db/schemas/webhooks.js +28 -0
- package/src/emulation/awsiot/Controllers.js +91 -0
- package/src/emulation/awsiot/Logic.js +70 -0
- package/src/emulation/awsiot/Router.js +19 -0
- package/src/emulation/awsiot/Server.js +100 -0
- package/src/emulation/firestore/Server.js +136 -0
- package/src/emulation/logging/Controllers.js +212 -0
- package/src/emulation/logging/Logic.js +416 -0
- package/src/emulation/logging/Router.js +36 -0
- package/src/emulation/logging/Schemas.js +82 -0
- package/src/emulation/logging/Server.js +108 -0
- package/src/emulation/pubsub/Controllers.js +279 -0
- package/src/emulation/pubsub/DefaultTopics.js +162 -0
- package/src/emulation/pubsub/Logic.js +427 -0
- package/src/emulation/pubsub/README.md +309 -0
- package/src/emulation/pubsub/Router.js +33 -0
- package/src/emulation/pubsub/Server.js +104 -0
- package/src/emulation/pubsub/ShadowPoller.js +276 -0
- package/src/emulation/pubsub/ShadowSubscriptionManager.js +199 -0
- package/src/enums/ContainerNames.js +106 -0
- package/src/enums/ErrorReason.js +28 -0
- package/src/enums/FunctionStatuses.js +15 -0
- package/src/enums/FunctionTriggerTypes.js +15 -0
- package/src/enums/GatewayState.js +7 -0
- package/src/enums/ServiceNames.js +68 -0
- package/src/jobs/DatabaseMaintenance.js +184 -0
- package/src/jobs/MessageHistoryCleanup.js +152 -0
- package/src/mcp/ApiClient.js +25 -0
- package/src/mcp/Server.js +52 -0
- package/src/mcp/prompts/debugging.js +104 -0
- package/src/mcp/resources/platform.js +118 -0
- package/src/mcp/tools/data.js +84 -0
- package/src/mcp/tools/docker.js +166 -0
- package/src/mcp/tools/firestore.js +162 -0
- package/src/mcp/tools/functions.js +380 -0
- package/src/mcp/tools/httpTraffic.js +69 -0
- package/src/mcp/tools/logging.js +174 -0
- package/src/mcp/tools/mqtt.js +37 -0
- package/src/mcp/tools/postgres.js +130 -0
- package/src/mcp/tools/pubsub.js +316 -0
- package/src/mcp/tools/redis.js +146 -0
- package/src/mcp/tools/services.js +169 -0
- package/src/mcp/tools/snapshots.js +88 -0
- package/src/mcp/tools/webhooks.js +115 -0
- package/src/middleware/DevProxy.js +67 -0
- package/src/middleware/ErrorCatcher.js +35 -0
- package/src/middleware/HttpProxy.js +215 -0
- package/src/middleware/Reply.js +24 -0
- package/src/middleware/TraceId.js +9 -0
- package/src/middleware/WebhookProxy.js +234 -0
- package/src/protocols/mqtt/Broker.js +92 -0
- package/src/protocols/mqtt/Handlers.js +175 -0
- package/src/protocols/mqtt/PubSubBridge.js +162 -0
- package/src/protocols/mqtt/Server.js +116 -0
- package/src/runtime/FunctionRunner.js +179 -0
- package/src/services/AppGatewayService.js +582 -0
- package/src/singletons/FirestoreBroadcaster.js +367 -0
- package/src/singletons/FunctionTriggerDispatcher.js +456 -0
- package/src/singletons/FunctionsService.js +418 -0
- package/src/singletons/HttpProxy.js +224 -0
- package/src/singletons/LogBroadcaster.js +159 -0
- package/src/singletons/Logger.js +49 -0
- package/src/singletons/MemoryJsonStore.js +175 -0
- package/src/singletons/MessageBroadcaster.js +190 -0
- package/src/singletons/PostgresBroadcaster.js +367 -0
- package/src/singletons/PostgresClient.js +180 -0
- package/src/singletons/RedisClient.js +184 -0
- package/src/singletons/SqliteStore.js +480 -0
- package/src/singletons/TickService.js +151 -0
- package/src/singletons/WebhookProxy.js +223 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { exec } from 'child_process'
|
|
3
|
+
import { promisify } from 'util'
|
|
4
|
+
|
|
5
|
+
const execAsync = promisify(exec)
|
|
6
|
+
|
|
7
|
+
// Container display names for better UX
|
|
8
|
+
const CONTAINER_DISPLAY_NAMES = {
|
|
9
|
+
'goki-postgres': 'PostgreSQL',
|
|
10
|
+
'goki-redis': 'Redis',
|
|
11
|
+
'goki-redis-logs': 'Redis (Logs)',
|
|
12
|
+
'goki-pubsub-emulator': 'Pub/Sub Emulator',
|
|
13
|
+
'goki-firestore-emulator': 'Firestore Emulator',
|
|
14
|
+
'goki-elasticsearch': 'Elasticsearch',
|
|
15
|
+
'goki-kibana': 'Kibana',
|
|
16
|
+
'goki-dev-tools-backend': 'Dev Tools Backend',
|
|
17
|
+
'goki-dev-tools-frontend': 'Dev Tools Frontend',
|
|
18
|
+
'device-native-app': 'Device Native',
|
|
19
|
+
'device-simulator-app': 'Device Simulator',
|
|
20
|
+
'integration-apaleo-app': 'Integration Apaleo',
|
|
21
|
+
'core-pms-app': 'Core PMS',
|
|
22
|
+
'core-key-app': 'Core Key',
|
|
23
|
+
'integration-mews-app': 'Integration Mews',
|
|
24
|
+
'integration-opera-app': 'Integration Opera'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const parseContainerStatus = (status) => {
|
|
28
|
+
if (!status) return 'unknown'
|
|
29
|
+
const lower = status.toLowerCase()
|
|
30
|
+
if (lower.includes('up')) return 'running'
|
|
31
|
+
if (lower.includes('exited')) return 'stopped'
|
|
32
|
+
if (lower.includes('created')) return 'created'
|
|
33
|
+
if (lower.includes('restarting')) return 'restarting'
|
|
34
|
+
if (lower.includes('paused')) return 'paused'
|
|
35
|
+
return 'unknown'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const parseUptime = (status) => {
|
|
39
|
+
if (!status) return null
|
|
40
|
+
const match = status.match(/Up\s+(.+?)(?:\s+\(|$)/i)
|
|
41
|
+
if (!match) return null
|
|
42
|
+
return match[1].trim()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const parsePorts = (portsStr) => {
|
|
46
|
+
if (!portsStr) return null
|
|
47
|
+
const match = portsStr.match(/0\.0\.0\.0:(\d+)->/)
|
|
48
|
+
return match ? parseInt(match[1], 10) : null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const generateDisplayName = (containerName) => {
|
|
52
|
+
return containerName
|
|
53
|
+
.replace(/-app$/, '')
|
|
54
|
+
.replace(/^goki-/, '')
|
|
55
|
+
.split('-')
|
|
56
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
57
|
+
.join(' ')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Docker commands run directly on host (MCP server runs on host, not in container)
|
|
61
|
+
export function registerDockerTools (server, apiClient) {
|
|
62
|
+
server.tool(
|
|
63
|
+
'docker_list_containers',
|
|
64
|
+
'List all Docker containers on the goki-network with their status, ports, and uptime',
|
|
65
|
+
{},
|
|
66
|
+
async () => {
|
|
67
|
+
try {
|
|
68
|
+
const { stdout } = await execAsync(
|
|
69
|
+
'docker ps -a --filter "network=goki-network" --format "{{.Names}}\t{{.Status}}\t{{.Ports}}\t{{.Image}}"'
|
|
70
|
+
)
|
|
71
|
+
const lines = stdout.trim().split('\n').filter(Boolean)
|
|
72
|
+
const containers = lines.map(line => {
|
|
73
|
+
const [name, status, ports, image] = line.split('\t')
|
|
74
|
+
const isSelf = name === 'goki-dev-tools-backend'
|
|
75
|
+
return {
|
|
76
|
+
name,
|
|
77
|
+
displayName: CONTAINER_DISPLAY_NAMES[name] || generateDisplayName(name),
|
|
78
|
+
status: parseContainerStatus(status),
|
|
79
|
+
statusText: status,
|
|
80
|
+
uptime: parseUptime(status),
|
|
81
|
+
port: parsePorts(ports),
|
|
82
|
+
image,
|
|
83
|
+
capabilities: {
|
|
84
|
+
canStart: !isSelf,
|
|
85
|
+
canStop: !isSelf,
|
|
86
|
+
canRestart: !isSelf
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
containers.sort((a, b) => a.displayName.localeCompare(b.displayName))
|
|
91
|
+
return { content: [{ type: 'text', text: JSON.stringify({ containers, total: containers.length }, null, 2) }] }
|
|
92
|
+
} catch (error) {
|
|
93
|
+
return { content: [{ type: 'text', text: `Failed to list containers: ${error.message}` }], isError: true }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
server.tool(
|
|
99
|
+
'docker_start_container',
|
|
100
|
+
'Start a stopped Docker container by name',
|
|
101
|
+
{
|
|
102
|
+
containerName: z.string().describe('Name of the Docker container to start')
|
|
103
|
+
},
|
|
104
|
+
async ({ containerName }) => {
|
|
105
|
+
try {
|
|
106
|
+
await execAsync(`docker start ${containerName}`)
|
|
107
|
+
const displayName = CONTAINER_DISPLAY_NAMES[containerName] || containerName
|
|
108
|
+
return { content: [{ type: 'text', text: JSON.stringify({ message: `${displayName} started successfully` }, null, 2) }] }
|
|
109
|
+
} catch (error) {
|
|
110
|
+
return { content: [{ type: 'text', text: `Failed to start container: ${error.message}` }], isError: true }
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
server.tool(
|
|
116
|
+
'docker_stop_container',
|
|
117
|
+
'Stop a running Docker container by name',
|
|
118
|
+
{
|
|
119
|
+
containerName: z.string().describe('Name of the Docker container to stop')
|
|
120
|
+
},
|
|
121
|
+
async ({ containerName }) => {
|
|
122
|
+
try {
|
|
123
|
+
await execAsync(`docker stop ${containerName}`)
|
|
124
|
+
const displayName = CONTAINER_DISPLAY_NAMES[containerName] || containerName
|
|
125
|
+
return { content: [{ type: 'text', text: JSON.stringify({ message: `${displayName} stopped successfully` }, null, 2) }] }
|
|
126
|
+
} catch (error) {
|
|
127
|
+
return { content: [{ type: 'text', text: `Failed to stop container: ${error.message}` }], isError: true }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
server.tool(
|
|
133
|
+
'docker_restart_container',
|
|
134
|
+
'Restart a Docker container by name',
|
|
135
|
+
{
|
|
136
|
+
containerName: z.string().describe('Name of the Docker container to restart')
|
|
137
|
+
},
|
|
138
|
+
async ({ containerName }) => {
|
|
139
|
+
try {
|
|
140
|
+
await execAsync(`docker restart ${containerName}`)
|
|
141
|
+
const displayName = CONTAINER_DISPLAY_NAMES[containerName] || containerName
|
|
142
|
+
return { content: [{ type: 'text', text: JSON.stringify({ message: `${displayName} restarted successfully` }, null, 2) }] }
|
|
143
|
+
} catch (error) {
|
|
144
|
+
return { content: [{ type: 'text', text: `Failed to restart container: ${error.message}` }], isError: true }
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
server.tool(
|
|
150
|
+
'docker_get_logs',
|
|
151
|
+
'Get recent logs from a Docker container. Returns the last N lines (default 100).',
|
|
152
|
+
{
|
|
153
|
+
containerName: z.string().describe('Name of the Docker container'),
|
|
154
|
+
lines: z.number().optional().describe('Number of recent log lines to retrieve (default 100)')
|
|
155
|
+
},
|
|
156
|
+
async ({ containerName, lines = 100 }) => {
|
|
157
|
+
try {
|
|
158
|
+
const { stdout, stderr } = await execAsync(`docker logs --tail ${lines} ${containerName} 2>&1`)
|
|
159
|
+
const logs = stdout || stderr
|
|
160
|
+
return { content: [{ type: 'text', text: JSON.stringify({ logs, containerName, lines }, null, 2) }] }
|
|
161
|
+
} catch (error) {
|
|
162
|
+
return { content: [{ type: 'text', text: `Failed to get container logs: ${error.message}` }], isError: true }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
)
|
|
166
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export function registerFirestoreTools (server, apiClient) {
|
|
4
|
+
server.tool(
|
|
5
|
+
'firestore_list_projects',
|
|
6
|
+
'List configured Firestore emulator projects',
|
|
7
|
+
{},
|
|
8
|
+
async () => {
|
|
9
|
+
try {
|
|
10
|
+
const data = await apiClient.post('/v1/firestore/projects/list')
|
|
11
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] }
|
|
12
|
+
} catch (error) {
|
|
13
|
+
return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true }
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
server.tool(
|
|
19
|
+
'firestore_list_collections',
|
|
20
|
+
'List collections in a Firestore project',
|
|
21
|
+
{
|
|
22
|
+
projectId: z.string().optional().describe('Firestore project ID')
|
|
23
|
+
},
|
|
24
|
+
async ({ projectId }) => {
|
|
25
|
+
try {
|
|
26
|
+
const data = await apiClient.post('/v1/firestore/collections/list', { projectId })
|
|
27
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] }
|
|
28
|
+
} catch (error) {
|
|
29
|
+
return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
server.tool(
|
|
35
|
+
'firestore_list_documents',
|
|
36
|
+
'List documents in a Firestore collection',
|
|
37
|
+
{
|
|
38
|
+
projectId: z.string().optional().describe('Firestore project ID'),
|
|
39
|
+
collectionPath: z.string().describe('Collection path'),
|
|
40
|
+
page: z.object({ limit: z.number().optional(), offset: z.number().optional() }).optional().describe('Pagination options with limit and offset')
|
|
41
|
+
},
|
|
42
|
+
async ({ projectId, collectionPath, page }) => {
|
|
43
|
+
try {
|
|
44
|
+
const data = await apiClient.post('/v1/firestore/documents/list', { projectId, collectionPath, page })
|
|
45
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] }
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
server.tool(
|
|
53
|
+
'firestore_get_document',
|
|
54
|
+
'Get a single Firestore document by ID',
|
|
55
|
+
{
|
|
56
|
+
projectId: z.string().optional().describe('Firestore project ID'),
|
|
57
|
+
collectionPath: z.string().describe('Collection path'),
|
|
58
|
+
documentId: z.string().describe('Document ID')
|
|
59
|
+
},
|
|
60
|
+
async ({ projectId, collectionPath, documentId }) => {
|
|
61
|
+
try {
|
|
62
|
+
const data = await apiClient.post('/v1/firestore/documents/get', { projectId, collectionPath, documentId })
|
|
63
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] }
|
|
64
|
+
} catch (error) {
|
|
65
|
+
return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
server.tool(
|
|
71
|
+
'firestore_create_document',
|
|
72
|
+
'Create a new Firestore document',
|
|
73
|
+
{
|
|
74
|
+
projectId: z.string().optional().describe('Firestore project ID'),
|
|
75
|
+
collectionPath: z.string().describe('Collection path'),
|
|
76
|
+
fields: z.record(z.any()).describe('Document fields'),
|
|
77
|
+
documentId: z.string().optional().describe('Document ID (auto-generated if omitted)')
|
|
78
|
+
},
|
|
79
|
+
async ({ projectId, collectionPath, fields, documentId }) => {
|
|
80
|
+
try {
|
|
81
|
+
const data = await apiClient.post('/v1/firestore/documents/create', { projectId, collectionPath, fields, documentId })
|
|
82
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] }
|
|
83
|
+
} catch (error) {
|
|
84
|
+
return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
server.tool(
|
|
90
|
+
'firestore_update_document',
|
|
91
|
+
'Update an existing Firestore document',
|
|
92
|
+
{
|
|
93
|
+
projectId: z.string().optional().describe('Firestore project ID'),
|
|
94
|
+
collectionPath: z.string().describe('Collection path'),
|
|
95
|
+
documentId: z.string().describe('Document ID'),
|
|
96
|
+
fields: z.record(z.any()).describe('Fields to update')
|
|
97
|
+
},
|
|
98
|
+
async ({ projectId, collectionPath, documentId, fields }) => {
|
|
99
|
+
try {
|
|
100
|
+
const data = await apiClient.post('/v1/firestore/documents/update', { projectId, collectionPath, documentId, fields })
|
|
101
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] }
|
|
102
|
+
} catch (error) {
|
|
103
|
+
return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
server.tool(
|
|
109
|
+
'firestore_delete_document',
|
|
110
|
+
'Delete a Firestore document',
|
|
111
|
+
{
|
|
112
|
+
projectId: z.string().optional().describe('Firestore project ID'),
|
|
113
|
+
collectionPath: z.string().describe('Collection path'),
|
|
114
|
+
documentId: z.string().describe('Document ID')
|
|
115
|
+
},
|
|
116
|
+
async ({ projectId, collectionPath, documentId }) => {
|
|
117
|
+
try {
|
|
118
|
+
const data = await apiClient.post('/v1/firestore/documents/delete', { projectId, collectionPath, documentId })
|
|
119
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] }
|
|
120
|
+
} catch (error) {
|
|
121
|
+
return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true }
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
server.tool(
|
|
127
|
+
'firestore_query',
|
|
128
|
+
'Execute a query against a Firestore collection with optional where/orderBy/limit',
|
|
129
|
+
{
|
|
130
|
+
projectId: z.string().optional().describe('Firestore project ID'),
|
|
131
|
+
collectionPath: z.string().describe('Collection path'),
|
|
132
|
+
where: z.array(z.any()).optional().describe('Array of where clauses'),
|
|
133
|
+
orderBy: z.array(z.any()).optional().describe('Array of orderBy clauses'),
|
|
134
|
+
limit: z.number().optional().describe('Maximum number of results')
|
|
135
|
+
},
|
|
136
|
+
async ({ projectId, collectionPath, where, orderBy, limit }) => {
|
|
137
|
+
try {
|
|
138
|
+
const data = await apiClient.post('/v1/firestore/query/execute', { projectId, collectionPath, where, orderBy, limit })
|
|
139
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] }
|
|
140
|
+
} catch (error) {
|
|
141
|
+
return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true }
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
server.tool(
|
|
147
|
+
'firestore_clear_collection',
|
|
148
|
+
'Delete all documents in a Firestore collection',
|
|
149
|
+
{
|
|
150
|
+
projectId: z.string().optional().describe('Firestore project ID'),
|
|
151
|
+
collectionPath: z.string().describe('Collection path')
|
|
152
|
+
},
|
|
153
|
+
async ({ projectId, collectionPath }) => {
|
|
154
|
+
try {
|
|
155
|
+
const data = await apiClient.post('/v1/firestore/collection/clear', { projectId, collectionPath })
|
|
156
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] }
|
|
157
|
+
} catch (error) {
|
|
158
|
+
return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true }
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
)
|
|
162
|
+
}
|