@ekoindia/eps-context-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.
Files changed (4) hide show
  1. package/README.md +171 -0
  2. package/data/eps.json +13867 -0
  3. package/dist/index.js +263 -0
  4. package/package.json +40 -0
package/dist/index.js ADDED
@@ -0,0 +1,263 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+
6
+ // src/load-bundle.ts
7
+ import { promises as fs } from "fs";
8
+ import path from "path";
9
+ import { fileURLToPath } from "url";
10
+ var here = path.dirname(fileURLToPath(import.meta.url));
11
+ var BAKED_PATH = path.resolve(here, "../data/eps.json");
12
+ var loadBundle = async () => {
13
+ const url = process.env.EPS_BUNDLE_URL;
14
+ if (url) {
15
+ try {
16
+ const res = await fetch(url);
17
+ if (res.ok)
18
+ return { bundle: await res.json(), source: "remote" };
19
+ } catch {
20
+ }
21
+ }
22
+ const raw = await fs.readFile(BAKED_PATH, "utf8");
23
+ return { bundle: JSON.parse(raw), source: "baked" };
24
+ };
25
+
26
+ // src/server.ts
27
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
28
+ import { z } from "zod";
29
+
30
+ // src/bundle-access.ts
31
+ var toIndexEntry = (a) => ({
32
+ slug: a.slug,
33
+ productId: a.productId,
34
+ productName: a.productName,
35
+ name: a.name,
36
+ method: a.method,
37
+ path: a.path,
38
+ summary: a.summary,
39
+ category: a.category,
40
+ relevance: a.relevance
41
+ });
42
+ var listApis = (bundle, category) => bundle.apis.filter((a) => !category || a.category === category).map(toIndexEntry);
43
+ var listTopics = (bundle) => Object.keys(bundle.topics);
44
+ var listRecipes = (bundle) => bundle.recipes.map((r) => ({ id: r.id, name: r.name, summary: r.summary }));
45
+ var searchApis = (bundle, query) => {
46
+ const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
47
+ if (!terms.length) return listApis(bundle);
48
+ const scored = bundle.apis.map((a) => {
49
+ const hay = `${a.name} ${a.summary} ${a.path} ${a.category} ${a.productName}`.toLowerCase();
50
+ let score = 0;
51
+ for (const t of terms) if (hay.includes(t)) score += 1;
52
+ return { a, score };
53
+ });
54
+ return scored.filter((s) => s.score > 0).sort((x, y) => y.score - x.score).map((s) => toIndexEntry(s.a));
55
+ };
56
+ var getApi = (bundle, slug) => bundle.apis.find((a) => a.slug === slug);
57
+ var getTopic = (bundle, topic) => bundle.topics[topic];
58
+ var getRecipe = (bundle, id) => bundle.recipes.find((r) => r.id === id);
59
+
60
+ // src/signing-snippets.ts
61
+ var SIGNING_LANGUAGES = [
62
+ "php",
63
+ "java",
64
+ "csharp",
65
+ "javascript",
66
+ "python",
67
+ "go"
68
+ ];
69
+ var SNIPPETS = {
70
+ php: `<?php
71
+ // Backend only. Never expose access_key in a browser.
72
+ $accessKey = getenv('EKO_ACCESS_KEY');
73
+ $timestamp = (string) round(microtime(true) * 1000);
74
+ $encodedKey = base64_encode($accessKey);
75
+ $secretKey = base64_encode(hash_hmac('sha256', $timestamp, $encodedKey, true));
76
+ // Send headers: developer_key, secret-key: $secretKey, secret-key-timestamp: $timestamp
77
+ `,
78
+ java: `// Backend only. Never expose access_key in a browser.
79
+ import javax.crypto.Mac;
80
+ import javax.crypto.spec.SecretKeySpec;
81
+ import java.util.Base64;
82
+
83
+ String accessKey = System.getenv("EKO_ACCESS_KEY");
84
+ String timestamp = String.valueOf(System.currentTimeMillis());
85
+ String encodedKey = Base64.getEncoder().encodeToString(accessKey.getBytes("UTF-8"));
86
+ Mac mac = Mac.getInstance("HmacSHA256");
87
+ mac.init(new SecretKeySpec(encodedKey.getBytes("UTF-8"), "HmacSHA256"));
88
+ String secretKey = Base64.getEncoder().encodeToString(mac.doFinal(timestamp.getBytes("UTF-8")));
89
+ `,
90
+ csharp: `// Backend only. Never expose access_key in a browser.
91
+ using System;
92
+ using System.Security.Cryptography;
93
+ using System.Text;
94
+
95
+ string accessKey = Environment.GetEnvironmentVariable("EKO_ACCESS_KEY");
96
+ string timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString();
97
+ string encodedKey = Convert.ToBase64String(Encoding.UTF8.GetBytes(accessKey));
98
+ using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(encodedKey));
99
+ string secretKey = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(timestamp)));
100
+ `,
101
+ javascript: `// Backend only (Node.js). Never expose access_key in a browser.
102
+ import crypto from "node:crypto";
103
+
104
+ const accessKey = process.env.EKO_ACCESS_KEY;
105
+ const timestamp = Date.now().toString();
106
+ const encodedKey = Buffer.from(accessKey).toString("base64");
107
+ const secretKey = crypto
108
+ .createHmac("sha256", encodedKey)
109
+ .update(timestamp)
110
+ .digest("base64");
111
+ // headers: { developer_key, "secret-key": secretKey, "secret-key-timestamp": timestamp }
112
+ `,
113
+ python: `# Backend only. Never expose access_key in a browser.
114
+ import base64, hashlib, hmac, os, time
115
+
116
+ access_key = os.environ["EKO_ACCESS_KEY"]
117
+ timestamp = str(int(time.time() * 1000))
118
+ encoded_key = base64.b64encode(access_key.encode())
119
+ secret_key = base64.b64encode(
120
+ hmac.new(encoded_key, timestamp.encode(), hashlib.sha256).digest()
121
+ ).decode()
122
+ `,
123
+ go: `// Backend only. Never expose access_key in a browser.
124
+ package main
125
+
126
+ import (
127
+ "crypto/hmac"
128
+ "crypto/sha256"
129
+ "encoding/base64"
130
+ "fmt"
131
+ "os"
132
+ "time"
133
+ )
134
+
135
+ func secretKey() (string, string) {
136
+ accessKey := os.Getenv("EKO_ACCESS_KEY")
137
+ timestamp := fmt.Sprintf("%d", time.Now().UnixMilli())
138
+ encodedKey := base64.StdEncoding.EncodeToString([]byte(accessKey))
139
+ mac := hmac.New(sha256.New, []byte(encodedKey))
140
+ mac.Write([]byte(timestamp))
141
+ return base64.StdEncoding.EncodeToString(mac.Sum(nil)), timestamp
142
+ }
143
+ `
144
+ };
145
+ var getSigningSnippet = (language) => SNIPPETS[language] ?? `Unsupported language "${language}". Supported: ${SIGNING_LANGUAGES.join(", ")}.`;
146
+
147
+ // src/server.ts
148
+ var json = (value) => ({
149
+ content: [{ type: "text", text: JSON.stringify(value, null, 2) }]
150
+ });
151
+ var createEpsServer = (bundle, source) => {
152
+ const server = new McpServer({
153
+ name: "eps-context-mcp",
154
+ version: bundle.meta.bundleVersion
155
+ });
156
+ server.registerTool(
157
+ "list_apis",
158
+ {
159
+ title: "List EPS APIs",
160
+ description: "Compact index of EPS API endpoints (no request/response bodies). Optionally filter by category.",
161
+ inputSchema: { category: z.string().optional() }
162
+ },
163
+ async ({ category }) => json(listApis(bundle, category))
164
+ );
165
+ server.registerTool(
166
+ "list_topics",
167
+ {
168
+ title: "List topics",
169
+ description: "List documentation topic ids.",
170
+ inputSchema: {}
171
+ },
172
+ async () => json(listTopics(bundle))
173
+ );
174
+ server.registerTool(
175
+ "list_recipes",
176
+ {
177
+ title: "List recipes",
178
+ description: "List multi-step recipe ids + names.",
179
+ inputSchema: {}
180
+ },
181
+ async () => json(listRecipes(bundle))
182
+ );
183
+ server.registerTool(
184
+ "search",
185
+ {
186
+ title: "Search APIs",
187
+ description: "Ranked endpoint matches for a query (ids only, no bodies).",
188
+ inputSchema: { query: z.string() }
189
+ },
190
+ async ({ query }) => json(searchApis(bundle, query))
191
+ );
192
+ server.registerTool(
193
+ "get_api",
194
+ {
195
+ title: "Get API detail",
196
+ description: "Full detail for one endpoint by slug.",
197
+ inputSchema: { slug: z.string() }
198
+ },
199
+ async ({ slug }) => {
200
+ const api = getApi(bundle, slug);
201
+ return api ? json(api) : json({ error: `Unknown slug "${slug}"` });
202
+ }
203
+ );
204
+ server.registerTool(
205
+ "get_topic",
206
+ {
207
+ title: "Get topic",
208
+ description: "One topic: auth | errors | pricing | environments.",
209
+ inputSchema: {
210
+ topic: z.enum(["auth", "errors", "pricing", "environments"])
211
+ }
212
+ },
213
+ async ({ topic }) => json(getTopic(bundle, topic))
214
+ );
215
+ server.registerTool(
216
+ "get_recipe",
217
+ {
218
+ title: "Get recipe",
219
+ description: "One multi-step recipe (steps + branches) by id.",
220
+ inputSchema: { id: z.string() }
221
+ },
222
+ async ({ id }) => {
223
+ const recipe = getRecipe(bundle, id);
224
+ return recipe ? json(recipe) : json({ error: `Unknown recipe "${id}"` });
225
+ }
226
+ );
227
+ server.registerTool(
228
+ "get_signing_snippet",
229
+ {
230
+ title: "Get signing snippet",
231
+ description: "Paste-ready BACKEND code to compute the secret-key. Secret-free: access_key comes from your secret store.",
232
+ inputSchema: { language: z.enum(SIGNING_LANGUAGES) }
233
+ },
234
+ async ({ language }) => ({
235
+ content: [{ type: "text", text: getSigningSnippet(language) }]
236
+ })
237
+ );
238
+ server.registerTool(
239
+ "get_meta",
240
+ {
241
+ title: "Get meta",
242
+ description: "Bundle org/version + data source.",
243
+ inputSchema: {}
244
+ },
245
+ async () => json({ ...bundle.meta, source })
246
+ );
247
+ return server;
248
+ };
249
+
250
+ // src/index.ts
251
+ async function main() {
252
+ const { bundle, source } = await loadBundle();
253
+ const server = createEpsServer(bundle, source);
254
+ const transport = new StdioServerTransport();
255
+ await server.connect(transport);
256
+ console.error(
257
+ `eps-context-mcp running (bundle ${bundle.meta.bundleVersion}, ${source})`
258
+ );
259
+ }
260
+ main().catch((err) => {
261
+ console.error("eps-context-mcp fatal:", err);
262
+ process.exit(1);
263
+ });
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@ekoindia/eps-context-mcp",
3
+ "version": "0.1.0",
4
+ "description": "Local MCP server giving AI coding agents context for Eko Platform Services (EPS) APIs.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/ekoindia/eko-eps-website.git",
10
+ "directory": "packages/eps-context-mcp"
11
+ },
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "bin": {
16
+ "eps-context-mcp": "dist/index.js"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "data"
21
+ ],
22
+ "scripts": {
23
+ "bake": "node scripts/bake-bundle.mjs",
24
+ "build": "tsup",
25
+ "test": "vitest run",
26
+ "prepublishOnly": "npm run bake && npm run build"
27
+ },
28
+ "dependencies": {
29
+ "@modelcontextprotocol/sdk": "^1.0.0",
30
+ "zod": "^3.25.76"
31
+ },
32
+ "devDependencies": {
33
+ "tsup": "^8.0.0",
34
+ "typescript": "^5.8.3",
35
+ "vitest": "^3.2.4"
36
+ },
37
+ "engines": {
38
+ "node": ">=18"
39
+ }
40
+ }