gencow 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.
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * packages/cli/bin/gencow-mcp.mjs
4
+ *
5
+ * Gencow MCP Server — AI 에이전트가 Gencow API를 자동 조회·호출할 수 있도록
6
+ * MCP (Model Context Protocol) 서버를 stdio 전송으로 제공합니다.
7
+ *
8
+ * Resources:
9
+ * - gencow://openapi-spec → OpenAPI 3.0 JSON
10
+ * - gencow://api-list → 등록된 API 경로 목록
11
+ *
12
+ * Tools:
13
+ * - list_apis → 등록된 query/mutation 목록
14
+ * - call_query → query 호출
15
+ * - call_mutation → mutation 호출
16
+ * - get_openapi_spec → OpenAPI 스펙 조회
17
+ *
18
+ * Usage:
19
+ * gencow mcp (기본 포트 5456)
20
+ * gencow mcp --port 3000 (커스텀 포트)
21
+ * GENCOW_PORT=3000 gencow mcp (환경변수)
22
+ */
23
+
24
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
25
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
26
+ import {
27
+ ListToolsRequestSchema,
28
+ CallToolRequestSchema,
29
+ ListResourcesRequestSchema,
30
+ ReadResourceRequestSchema,
31
+ } from "@modelcontextprotocol/sdk/types.js";
32
+
33
+ const BASE_URL = `http://localhost:${process.env.GENCOW_PORT || 5456}`;
34
+
35
+ // ─── Helpers ────────────────────────────────────────────
36
+
37
+ async function fetchJSON(path) {
38
+ const res = await fetch(`${BASE_URL}${path}`);
39
+ if (!res.ok) throw new Error(`Gencow API error: ${res.status} ${await res.text()}`);
40
+ return res.json();
41
+ }
42
+
43
+ async function postJSON(path, body) {
44
+ const res = await fetch(`${BASE_URL}${path}`, {
45
+ method: "POST",
46
+ headers: { "Content-Type": "application/json" },
47
+ body: JSON.stringify(body),
48
+ });
49
+ const data = await res.json();
50
+ return { status: res.status, data };
51
+ }
52
+
53
+ // ─── MCP Server ─────────────────────────────────────────
54
+
55
+ const server = new Server(
56
+ { name: "gencow", version: "0.1.0" },
57
+ {
58
+ capabilities: {
59
+ tools: {},
60
+ resources: {},
61
+ },
62
+ }
63
+ );
64
+
65
+ // ── Tools ───────────────────────────────────────────────
66
+
67
+ const TOOLS = [
68
+ {
69
+ name: "list_apis",
70
+ description: "등록된 전체 API 목록을 조회합니다. 각 API의 이름, 경로, 타입(query/mutation), 지원 HTTP 메서드를 반환합니다.",
71
+ inputSchema: { type: "object", properties: {} },
72
+ },
73
+ {
74
+ name: "call_query",
75
+ description: "Gencow query를 호출합니다. 예: name='tasks.list', args={}. 경로: POST /api/fn/{module}/{name}",
76
+ inputSchema: {
77
+ type: "object",
78
+ required: ["name"],
79
+ properties: {
80
+ name: { type: "string", description: "Query name (dot notation), e.g. 'tasks.list', 'tasks.get', 'files.list'" },
81
+ args: { type: "object", description: "Query arguments as JSON object", additionalProperties: true },
82
+ },
83
+ },
84
+ },
85
+ {
86
+ name: "call_mutation",
87
+ description: "Gencow mutation을 호출합니다. 예: name='tasks.create', args={ title: '새 태스크' }. 경로: POST /api/fn/{module}/{name}",
88
+ inputSchema: {
89
+ type: "object",
90
+ required: ["name"],
91
+ properties: {
92
+ name: { type: "string", description: "Mutation name (dot notation), e.g. 'tasks.create', 'tasks.update', 'tasks.delete'" },
93
+ args: { type: "object", description: "Mutation arguments as JSON object", additionalProperties: true },
94
+ },
95
+ },
96
+ },
97
+ {
98
+ name: "get_openapi_spec",
99
+ description: "OpenAPI 3.0 스펙을 JSON으로 반환합니다. API 구조, 요청/응답 스키마, 인증 방법을 포함합니다.",
100
+ inputSchema: { type: "object", properties: {} },
101
+ },
102
+ ];
103
+
104
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
105
+
106
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
107
+ const { name: toolName, arguments: toolArgs } = request.params;
108
+
109
+ try {
110
+ switch (toolName) {
111
+ case "list_apis": {
112
+ const list = await fetchJSON("/api/fn");
113
+ return { content: [{ type: "text", text: JSON.stringify(list, null, 2) }] };
114
+ }
115
+ case "call_query": {
116
+ const path = "/api/fn/" + toolArgs.name.replace(/\./g, "/");
117
+ const result = await postJSON(path, toolArgs.args || {});
118
+ return { content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }] };
119
+ }
120
+ case "call_mutation": {
121
+ const path = "/api/fn/" + toolArgs.name.replace(/\./g, "/");
122
+ const result = await postJSON(path, toolArgs.args || {});
123
+ return { content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }] };
124
+ }
125
+ case "get_openapi_spec": {
126
+ const spec = await fetchJSON("/api/openapi.json");
127
+ return { content: [{ type: "text", text: JSON.stringify(spec, null, 2) }] };
128
+ }
129
+ default:
130
+ return { content: [{ type: "text", text: `Unknown tool: ${toolName}` }], isError: true };
131
+ }
132
+ } catch (err) {
133
+ return { content: [{ type: "text", text: `Error: ${err.message}` }], isError: true };
134
+ }
135
+ });
136
+
137
+ // ── Resources ───────────────────────────────────────────
138
+
139
+ const RESOURCES = [
140
+ {
141
+ uri: "gencow://openapi-spec",
142
+ name: "openapi-spec",
143
+ description: "Gencow OpenAPI 3.0 specification (auto-generated from registered queries/mutations)",
144
+ mimeType: "application/json",
145
+ },
146
+ {
147
+ uri: "gencow://api-list",
148
+ name: "api-list",
149
+ description: "List of all registered API endpoints with paths and methods",
150
+ mimeType: "application/json",
151
+ },
152
+ ];
153
+
154
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: RESOURCES }));
155
+
156
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
157
+ const { uri } = request.params;
158
+
159
+ switch (uri) {
160
+ case "gencow://openapi-spec": {
161
+ const spec = await fetchJSON("/api/openapi.json");
162
+ return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify(spec, null, 2) }] };
163
+ }
164
+ case "gencow://api-list": {
165
+ const list = await fetchJSON("/api/fn");
166
+ return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify(list, null, 2) }] };
167
+ }
168
+ default:
169
+ throw new Error(`Unknown resource: ${uri}`);
170
+ }
171
+ });
172
+
173
+ // ── Start ───────────────────────────────────────────────
174
+
175
+ const transport = new StdioServerTransport();
176
+ await server.connect(transport);