@ekoindia/eps-context-mcp 0.1.3 → 0.1.4
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 +61 -25
- package/data/eps.json +6 -6
- package/dist/index.js +131 -25
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -15,9 +15,13 @@ fetch full detail when it needs it.
|
|
|
15
15
|
No install step required — run it on demand with `npx`:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
npx -y @ekoindia/eps-context-mcp
|
|
18
|
+
npx -y @ekoindia/eps-context-mcp@latest
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
The `@latest` tag makes `npx` re-resolve the newest published version on each
|
|
22
|
+
launch, so you always run the current server + API bundle without editing your
|
|
23
|
+
config (see [Staying up to date](#staying-up-to-date)).
|
|
24
|
+
|
|
21
25
|
The server speaks MCP over **stdio**, so it is meant to be launched by an MCP
|
|
22
26
|
client (see per-harness config below) rather than run by hand. It logs its
|
|
23
27
|
status to **stderr** and waits for an MCP client on stdin/stdout.
|
|
@@ -27,30 +31,37 @@ Requires **Node.js ≥ 18**.
|
|
|
27
31
|
## Tools
|
|
28
32
|
|
|
29
33
|
All tools are read-only and **secret-free** — none of them accept an
|
|
30
|
-
`access_key` or any other credential.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
42
|
-
| `
|
|
34
|
+
`access_key` or any other credential. Every tool also declares MCP annotations
|
|
35
|
+
(`readOnlyHint: true`, `idempotentHint: true`, `openWorldHint: false`) so
|
|
36
|
+
clients can see this programmatically.
|
|
37
|
+
|
|
38
|
+
| Tool | Arguments | Returns |
|
|
39
|
+
| --------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
40
|
+
| `list_apis` | `category?`, `limit?` | Compact index of EPS endpoints (no request/response bodies). `category` is validated against the bundle's categories; all entries by default. |
|
|
41
|
+
| `list_topics` | — | Documentation topic ids: `auth`, `errors`, `pricing`, `environments`. |
|
|
42
|
+
| `list_recipes` | — | Multi-step recipe ids + names (e.g. `dmt-send-money`, `aeps-cash-withdrawal`). |
|
|
43
|
+
| `search` | `query`, `limit?` | Ranked endpoint matches for a query (ids only, no bodies). Top 10 by default; raise `limit` for more. |
|
|
44
|
+
| `get_api` | `slug` | Full detail for one endpoint (params, response fields, errors, examples). |
|
|
45
|
+
| `get_topic` | `topic` (`auth` \| `errors` \| `pricing` \| `environments`) | One documentation topic. |
|
|
46
|
+
| `get_recipe` | `id` | One multi-step recipe (steps + branches). |
|
|
47
|
+
| `get_signing_snippet` | `language` (`php` \| `java` \| `csharp` \| `javascript` \| `python` \| `go`) | Paste-ready **backend** code to compute the request `secret-key`. |
|
|
48
|
+
| `get_meta` | — | Bundle org/version, data source (`baked` or `remote`), this server's `packageVersion`, and `updateAvailable` (whether a newer npm release exists). |
|
|
43
49
|
|
|
44
50
|
**Tiered usage:** start with `list_apis` / `search` (cheap, compact), then call
|
|
45
51
|
`get_api` only for the endpoint(s) you actually need. Same pattern for
|
|
46
52
|
`list_topics` → `get_topic` and `list_recipes` → `get_recipe`.
|
|
47
53
|
|
|
54
|
+
**Errors are actionable:** an unknown `slug`/`id` returns an MCP error result
|
|
55
|
+
(`isError: true`) with "did you mean" suggestions and a pointer to
|
|
56
|
+
`search`/`list_apis`; an invalid `category` fails schema validation with the
|
|
57
|
+
valid values listed.
|
|
58
|
+
|
|
48
59
|
## Client configuration
|
|
49
60
|
|
|
50
61
|
### Claude Code
|
|
51
62
|
|
|
52
63
|
```bash
|
|
53
|
-
claude mcp add eps --scope project -- npx -y @ekoindia/eps-context-mcp
|
|
64
|
+
claude mcp add eps --scope project -- npx -y @ekoindia/eps-context-mcp@latest
|
|
54
65
|
```
|
|
55
66
|
|
|
56
67
|
`--scope project` writes a shared `.mcp.json` committed with the repo. Use
|
|
@@ -62,7 +73,10 @@ Or add to your MCP config (project `.mcp.json`, or `~/.claude.json` for user sco
|
|
|
62
73
|
```json
|
|
63
74
|
{
|
|
64
75
|
"mcpServers": {
|
|
65
|
-
"eps": {
|
|
76
|
+
"eps": {
|
|
77
|
+
"command": "npx",
|
|
78
|
+
"args": ["-y", "@ekoindia/eps-context-mcp@latest"]
|
|
79
|
+
}
|
|
66
80
|
}
|
|
67
81
|
}
|
|
68
82
|
```
|
|
@@ -74,7 +88,10 @@ Or add to your MCP config (project `.mcp.json`, or `~/.claude.json` for user sco
|
|
|
74
88
|
```json
|
|
75
89
|
{
|
|
76
90
|
"mcpServers": {
|
|
77
|
-
"eps": {
|
|
91
|
+
"eps": {
|
|
92
|
+
"command": "npx",
|
|
93
|
+
"args": ["-y", "@ekoindia/eps-context-mcp@latest"]
|
|
94
|
+
}
|
|
78
95
|
}
|
|
79
96
|
}
|
|
80
97
|
```
|
|
@@ -88,7 +105,7 @@ Or add to your MCP config (project `.mcp.json`, or `~/.claude.json` for user sco
|
|
|
88
105
|
"mcp": {
|
|
89
106
|
"eps": {
|
|
90
107
|
"type": "local",
|
|
91
|
-
"command": ["npx", "-y", "@ekoindia/eps-context-mcp"],
|
|
108
|
+
"command": ["npx", "-y", "@ekoindia/eps-context-mcp@latest"],
|
|
92
109
|
"enabled": true
|
|
93
110
|
}
|
|
94
111
|
}
|
|
@@ -103,7 +120,7 @@ Or add to your MCP config (project `.mcp.json`, or `~/.claude.json` for user sco
|
|
|
103
120
|
mcpServers:
|
|
104
121
|
- name: eps
|
|
105
122
|
command: npx
|
|
106
|
-
args: ["-y", "@ekoindia/eps-context-mcp"]
|
|
123
|
+
args: ["-y", "@ekoindia/eps-context-mcp@latest"]
|
|
107
124
|
```
|
|
108
125
|
|
|
109
126
|
### Codex CLI
|
|
@@ -113,7 +130,7 @@ mcpServers:
|
|
|
113
130
|
```toml
|
|
114
131
|
[mcp_servers.eps]
|
|
115
132
|
command = "npx"
|
|
116
|
-
args = ["-y", "@ekoindia/eps-context-mcp"]
|
|
133
|
+
args = ["-y", "@ekoindia/eps-context-mcp@latest"]
|
|
117
134
|
```
|
|
118
135
|
|
|
119
136
|
### Gemini CLI
|
|
@@ -123,11 +140,30 @@ args = ["-y", "@ekoindia/eps-context-mcp"]
|
|
|
123
140
|
```json
|
|
124
141
|
{
|
|
125
142
|
"mcpServers": {
|
|
126
|
-
"eps": {
|
|
143
|
+
"eps": {
|
|
144
|
+
"command": "npx",
|
|
145
|
+
"args": ["-y", "@ekoindia/eps-context-mcp@latest"]
|
|
146
|
+
}
|
|
127
147
|
}
|
|
128
148
|
}
|
|
129
149
|
```
|
|
130
150
|
|
|
151
|
+
## Staying up to date
|
|
152
|
+
|
|
153
|
+
The package is auto-published on every merge to `main`, and each publish carries
|
|
154
|
+
the freshest baked API bundle. To keep users current:
|
|
155
|
+
|
|
156
|
+
- **Use `@latest` in your config** (all snippets above do). `npx` then
|
|
157
|
+
re-resolves the newest publish on each launch — no manual updates. Offline
|
|
158
|
+
launches fall back to the npx cache; pin `@<version>` if you need a frozen
|
|
159
|
+
build.
|
|
160
|
+
- **Update check.** On startup the server does one best-effort request to the
|
|
161
|
+
npm registry (3s timeout). If a newer version is published, it logs a nudge to
|
|
162
|
+
**stderr** and sets `updateAvailable` on `get_meta` — so an agent can tell you
|
|
163
|
+
to switch to `@latest`. The check is silent on any failure (offline, proxy)
|
|
164
|
+
and never blocks startup. Set `EPS_NO_UPDATE_CHECK=1` to disable it entirely
|
|
165
|
+
(no network request).
|
|
166
|
+
|
|
131
167
|
## Configuration
|
|
132
168
|
|
|
133
169
|
### `EPS_BUNDLE_URL` (optional)
|
|
@@ -140,16 +176,16 @@ bundle at startup (e.g. the latest generated `eps.json`), set `EPS_BUNDLE_URL`:
|
|
|
140
176
|
"mcpServers": {
|
|
141
177
|
"eps": {
|
|
142
178
|
"command": "npx",
|
|
143
|
-
"args": ["-y", "@ekoindia/eps-context-mcp"],
|
|
179
|
+
"args": ["-y", "@ekoindia/eps-context-mcp@latest"],
|
|
144
180
|
"env": { "EPS_BUNDLE_URL": "https://eps.eko.in/agent/eps.json" }
|
|
145
181
|
}
|
|
146
182
|
}
|
|
147
183
|
}
|
|
148
184
|
```
|
|
149
185
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
`remote`).
|
|
186
|
+
The remote fetch is capped at **5 seconds**; if it fails or times out for any
|
|
187
|
+
reason, the server transparently falls back to the baked bundle. `get_meta`
|
|
188
|
+
reports which source is in effect (`baked` or `remote`).
|
|
153
189
|
|
|
154
190
|
## Security: backend-only signing
|
|
155
191
|
|
package/data/eps.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"meta": {
|
|
3
3
|
"org": "ekoindia",
|
|
4
4
|
"apiVersion": "v3",
|
|
5
|
-
"bundleVersion": "
|
|
5
|
+
"bundleVersion": "1e83ecd3",
|
|
6
6
|
"environments": [
|
|
7
7
|
{
|
|
8
8
|
"id": "sandbox",
|
|
@@ -1856,8 +1856,8 @@
|
|
|
1856
1856
|
"in": "header",
|
|
1857
1857
|
"type": "string",
|
|
1858
1858
|
"required": true,
|
|
1859
|
-
"description": "
|
|
1860
|
-
"example": "
|
|
1859
|
+
"description": "multipart/form-data — let your HTTP client set this header itself (it generates the required boundary); do not hardcode the value.",
|
|
1860
|
+
"example": "multipart/form-data"
|
|
1861
1861
|
}
|
|
1862
1862
|
],
|
|
1863
1863
|
"requestParams": [
|
|
@@ -1921,7 +1921,7 @@
|
|
|
1921
1921
|
},
|
|
1922
1922
|
{
|
|
1923
1923
|
"name": "pan_card",
|
|
1924
|
-
"type": "
|
|
1924
|
+
"type": "file",
|
|
1925
1925
|
"required": true,
|
|
1926
1926
|
"description": "PAN card document upload (multipart/form-data). Accepted formats: JPEG, JPG, PDF. Max size: 1 MB. PNG not accepted.",
|
|
1927
1927
|
"example": "<binary file>",
|
|
@@ -1929,7 +1929,7 @@
|
|
|
1929
1929
|
},
|
|
1930
1930
|
{
|
|
1931
1931
|
"name": "aadhar_front",
|
|
1932
|
-
"type": "
|
|
1932
|
+
"type": "file",
|
|
1933
1933
|
"required": true,
|
|
1934
1934
|
"description": "Front side of the Aadhaar card (multipart/form-data). Accepted formats: JPEG, JPG, PDF. Max size: 1 MB.",
|
|
1935
1935
|
"example": "<binary file>",
|
|
@@ -1937,7 +1937,7 @@
|
|
|
1937
1937
|
},
|
|
1938
1938
|
{
|
|
1939
1939
|
"name": "aadhar_back",
|
|
1940
|
-
"type": "
|
|
1940
|
+
"type": "file",
|
|
1941
1941
|
"required": true,
|
|
1942
1942
|
"description": "Back side of the Aadhaar card (multipart/form-data). Accepted formats: JPEG, JPG, PDF. Max size: 1 MB.",
|
|
1943
1943
|
"example": "<binary file>",
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
+
import { readFileSync } from "fs";
|
|
5
|
+
import path2 from "path";
|
|
6
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4
7
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
8
|
|
|
6
9
|
// src/load-bundle.ts
|
|
@@ -13,7 +16,7 @@ var loadBundle = async () => {
|
|
|
13
16
|
const url = process.env.EPS_BUNDLE_URL;
|
|
14
17
|
if (url) {
|
|
15
18
|
try {
|
|
16
|
-
const res = await fetch(url);
|
|
19
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(5e3) });
|
|
17
20
|
if (res.ok)
|
|
18
21
|
return { bundle: await res.json(), source: "remote" };
|
|
19
22
|
} catch {
|
|
@@ -39,19 +42,26 @@ var toIndexEntry = (a) => ({
|
|
|
39
42
|
category: a.category,
|
|
40
43
|
relevance: a.relevance
|
|
41
44
|
});
|
|
42
|
-
var listApis = (bundle, category
|
|
45
|
+
var listApis = (bundle, category, limit) => {
|
|
46
|
+
const entries = bundle.apis.filter((a) => !category || a.category === category).map(toIndexEntry);
|
|
47
|
+
return limit !== void 0 ? entries.slice(0, limit) : entries;
|
|
48
|
+
};
|
|
49
|
+
var listCategories = (bundle) => [
|
|
50
|
+
...new Set(bundle.apis.map((a) => a.category))
|
|
51
|
+
];
|
|
43
52
|
var listTopics = (bundle) => Object.keys(bundle.topics);
|
|
44
53
|
var listRecipes = (bundle) => bundle.recipes.map((r) => ({ id: r.id, name: r.name, summary: r.summary }));
|
|
45
|
-
var searchApis = (bundle, query) => {
|
|
54
|
+
var searchApis = (bundle, query, limit) => {
|
|
46
55
|
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
47
|
-
if (!terms.length) return listApis(bundle);
|
|
56
|
+
if (!terms.length) return listApis(bundle, void 0, limit);
|
|
48
57
|
const scored = bundle.apis.map((a) => {
|
|
49
58
|
const hay = `${a.name} ${a.summary} ${a.path} ${a.category} ${a.productName}`.toLowerCase();
|
|
50
59
|
let score = 0;
|
|
51
60
|
for (const t of terms) if (hay.includes(t)) score += 1;
|
|
52
61
|
return { a, score };
|
|
53
62
|
});
|
|
54
|
-
|
|
63
|
+
const ranked = scored.filter((s) => s.score > 0).sort((x, y) => y.score - x.score).map((s) => toIndexEntry(s.a));
|
|
64
|
+
return limit !== void 0 ? ranked.slice(0, limit) : ranked;
|
|
55
65
|
};
|
|
56
66
|
var getApi = (bundle, slug) => bundle.apis.find((a) => a.slug === slug);
|
|
57
67
|
var getTopic = (bundle, topic) => bundle.topics[topic];
|
|
@@ -146,28 +156,46 @@ var getSigningSnippet = (language) => SNIPPETS[language] ?? `Unsupported languag
|
|
|
146
156
|
|
|
147
157
|
// src/server.ts
|
|
148
158
|
var json = (value) => ({
|
|
149
|
-
content: [{ type: "text", text: JSON.stringify(value
|
|
159
|
+
content: [{ type: "text", text: JSON.stringify(value) }]
|
|
150
160
|
});
|
|
151
|
-
var
|
|
161
|
+
var notFound = (message) => ({
|
|
162
|
+
isError: true,
|
|
163
|
+
content: [{ type: "text", text: message }]
|
|
164
|
+
});
|
|
165
|
+
var READ_ONLY = {
|
|
166
|
+
readOnlyHint: true,
|
|
167
|
+
idempotentHint: true,
|
|
168
|
+
openWorldHint: false
|
|
169
|
+
};
|
|
170
|
+
var DEFAULT_SEARCH_LIMIT = 10;
|
|
171
|
+
var createEpsServer = (bundle, source, versionState) => {
|
|
152
172
|
const server = new McpServer({
|
|
153
173
|
name: "eps-context-mcp",
|
|
154
174
|
version: bundle.meta.bundleVersion
|
|
155
175
|
});
|
|
176
|
+
const categories = listCategories(bundle);
|
|
177
|
+
const categorySchema = categories.length ? z.enum(categories) : z.string();
|
|
178
|
+
const limitSchema = z.number().int().positive().optional();
|
|
156
179
|
server.registerTool(
|
|
157
180
|
"list_apis",
|
|
158
181
|
{
|
|
159
182
|
title: "List EPS APIs",
|
|
160
|
-
description: "Compact index of EPS API endpoints (no request/response bodies).
|
|
161
|
-
inputSchema: {
|
|
183
|
+
description: "Compact index of EPS API endpoints (no request/response bodies). Unfiltered, all ~99 entries are returned (the full tiered index); narrow with category and/or limit when you don't need everything.",
|
|
184
|
+
inputSchema: {
|
|
185
|
+
category: categorySchema.optional().describe(`One of: ${categories.join(", ")}`),
|
|
186
|
+
limit: limitSchema.describe("Max entries to return (default: all)")
|
|
187
|
+
},
|
|
188
|
+
annotations: READ_ONLY
|
|
162
189
|
},
|
|
163
|
-
async ({ category }) => json(listApis(bundle, category))
|
|
190
|
+
async ({ category, limit }) => json(listApis(bundle, category, limit))
|
|
164
191
|
);
|
|
165
192
|
server.registerTool(
|
|
166
193
|
"list_topics",
|
|
167
194
|
{
|
|
168
195
|
title: "List topics",
|
|
169
196
|
description: "List documentation topic ids.",
|
|
170
|
-
inputSchema: {}
|
|
197
|
+
inputSchema: {},
|
|
198
|
+
annotations: READ_ONLY
|
|
171
199
|
},
|
|
172
200
|
async () => json(listTopics(bundle))
|
|
173
201
|
);
|
|
@@ -176,7 +204,8 @@ var createEpsServer = (bundle, source) => {
|
|
|
176
204
|
{
|
|
177
205
|
title: "List recipes",
|
|
178
206
|
description: "List multi-step recipe ids + names.",
|
|
179
|
-
inputSchema: {}
|
|
207
|
+
inputSchema: {},
|
|
208
|
+
annotations: READ_ONLY
|
|
180
209
|
},
|
|
181
210
|
async () => json(listRecipes(bundle))
|
|
182
211
|
);
|
|
@@ -184,21 +213,34 @@ var createEpsServer = (bundle, source) => {
|
|
|
184
213
|
"search",
|
|
185
214
|
{
|
|
186
215
|
title: "Search APIs",
|
|
187
|
-
description:
|
|
188
|
-
inputSchema: {
|
|
216
|
+
description: `Ranked endpoint matches for a query (ids only, no bodies). Returns the top ${DEFAULT_SEARCH_LIMIT} by default; raise limit for more.`,
|
|
217
|
+
inputSchema: {
|
|
218
|
+
query: z.string(),
|
|
219
|
+
limit: limitSchema.describe(
|
|
220
|
+
`Max results (default ${DEFAULT_SEARCH_LIMIT})`
|
|
221
|
+
)
|
|
222
|
+
},
|
|
223
|
+
annotations: READ_ONLY
|
|
189
224
|
},
|
|
190
|
-
async ({ query }) => json(searchApis(bundle, query))
|
|
225
|
+
async ({ query, limit }) => json(searchApis(bundle, query, limit ?? DEFAULT_SEARCH_LIMIT))
|
|
191
226
|
);
|
|
192
227
|
server.registerTool(
|
|
193
228
|
"get_api",
|
|
194
229
|
{
|
|
195
230
|
title: "Get API detail",
|
|
196
231
|
description: "Full detail for one endpoint by slug.",
|
|
197
|
-
inputSchema: { slug: z.string() }
|
|
232
|
+
inputSchema: { slug: z.string() },
|
|
233
|
+
annotations: READ_ONLY
|
|
198
234
|
},
|
|
199
235
|
async ({ slug }) => {
|
|
200
236
|
const api = getApi(bundle, slug);
|
|
201
|
-
|
|
237
|
+
if (api) return json(api);
|
|
238
|
+
const suggestions = searchApis(bundle, slug.replace(/[-_]/g, " "), 3).map(
|
|
239
|
+
(a) => a.slug
|
|
240
|
+
);
|
|
241
|
+
return notFound(
|
|
242
|
+
`Unknown slug "${slug}".` + (suggestions.length ? ` Did you mean: ${suggestions.join(", ")}?` : "") + ` Use search or list_apis to find valid slugs.`
|
|
243
|
+
);
|
|
202
244
|
}
|
|
203
245
|
);
|
|
204
246
|
server.registerTool(
|
|
@@ -208,7 +250,8 @@ var createEpsServer = (bundle, source) => {
|
|
|
208
250
|
description: "One topic: auth | errors | pricing | environments.",
|
|
209
251
|
inputSchema: {
|
|
210
252
|
topic: z.enum(["auth", "errors", "pricing", "environments"])
|
|
211
|
-
}
|
|
253
|
+
},
|
|
254
|
+
annotations: READ_ONLY
|
|
212
255
|
},
|
|
213
256
|
async ({ topic }) => json(getTopic(bundle, topic))
|
|
214
257
|
);
|
|
@@ -217,11 +260,16 @@ var createEpsServer = (bundle, source) => {
|
|
|
217
260
|
{
|
|
218
261
|
title: "Get recipe",
|
|
219
262
|
description: "One multi-step recipe (steps + branches) by id.",
|
|
220
|
-
inputSchema: { id: z.string() }
|
|
263
|
+
inputSchema: { id: z.string() },
|
|
264
|
+
annotations: READ_ONLY
|
|
221
265
|
},
|
|
222
266
|
async ({ id }) => {
|
|
223
267
|
const recipe = getRecipe(bundle, id);
|
|
224
|
-
|
|
268
|
+
if (recipe) return json(recipe);
|
|
269
|
+
const valid = listRecipes(bundle).map((r) => r.id).join(", ");
|
|
270
|
+
return notFound(
|
|
271
|
+
`Unknown recipe "${id}". Valid recipe ids: ${valid}. Use list_recipes for details.`
|
|
272
|
+
);
|
|
225
273
|
}
|
|
226
274
|
);
|
|
227
275
|
server.registerTool(
|
|
@@ -229,7 +277,8 @@ var createEpsServer = (bundle, source) => {
|
|
|
229
277
|
{
|
|
230
278
|
title: "Get signing snippet",
|
|
231
279
|
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) }
|
|
280
|
+
inputSchema: { language: z.enum(SIGNING_LANGUAGES) },
|
|
281
|
+
annotations: READ_ONLY
|
|
233
282
|
},
|
|
234
283
|
async ({ language }) => ({
|
|
235
284
|
content: [{ type: "text", text: getSigningSnippet(language) }]
|
|
@@ -239,18 +288,75 @@ var createEpsServer = (bundle, source) => {
|
|
|
239
288
|
"get_meta",
|
|
240
289
|
{
|
|
241
290
|
title: "Get meta",
|
|
242
|
-
description: "Bundle org/version + data source.",
|
|
243
|
-
inputSchema: {}
|
|
291
|
+
description: "Bundle org/version + data source, plus this server's package version and whether a newer npm release is available (updateAvailable). If an update is available, tell the user to run this server via `npx -y @ekoindia/eps-context-mcp@latest`.",
|
|
292
|
+
inputSchema: {},
|
|
293
|
+
annotations: READ_ONLY
|
|
244
294
|
},
|
|
245
|
-
async () => json({
|
|
295
|
+
async () => json({
|
|
296
|
+
...bundle.meta,
|
|
297
|
+
source,
|
|
298
|
+
packageVersion: versionState?.current,
|
|
299
|
+
latestVersion: versionState?.latest,
|
|
300
|
+
updateAvailable: versionState?.updateAvailable
|
|
301
|
+
})
|
|
246
302
|
);
|
|
247
303
|
return server;
|
|
248
304
|
};
|
|
249
305
|
|
|
306
|
+
// src/update-check.ts
|
|
307
|
+
var REGISTRY_URL = "https://registry.npmjs.org/@ekoindia/eps-context-mcp/latest";
|
|
308
|
+
var compareVersions = (a, b) => {
|
|
309
|
+
const parse = (v) => {
|
|
310
|
+
const m = /^(\d+)\.(\d+)\.(\d+)$/.exec(v.trim());
|
|
311
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
312
|
+
};
|
|
313
|
+
const pa = parse(a);
|
|
314
|
+
const pb = parse(b);
|
|
315
|
+
if (!pa || !pb) return null;
|
|
316
|
+
for (let i = 0; i < 3; i++) {
|
|
317
|
+
if (pa[i] !== pb[i]) return pa[i] > pb[i] ? 1 : -1;
|
|
318
|
+
}
|
|
319
|
+
return 0;
|
|
320
|
+
};
|
|
321
|
+
var checkForUpdate = async (state) => {
|
|
322
|
+
if (process.env.EPS_NO_UPDATE_CHECK) return;
|
|
323
|
+
try {
|
|
324
|
+
const res = await fetch(REGISTRY_URL, {
|
|
325
|
+
signal: AbortSignal.timeout(3e3)
|
|
326
|
+
});
|
|
327
|
+
if (!res.ok) return;
|
|
328
|
+
const data = await res.json();
|
|
329
|
+
if (typeof data.version !== "string") return;
|
|
330
|
+
const cmp = compareVersions(data.version, state.current);
|
|
331
|
+
if (cmp === null) return;
|
|
332
|
+
state.latest = data.version;
|
|
333
|
+
state.updateAvailable = cmp > 0;
|
|
334
|
+
if (state.updateAvailable) {
|
|
335
|
+
console.error(
|
|
336
|
+
`eps-context-mcp ${data.version} available (running ${state.current}). Use "npx -y @ekoindia/eps-context-mcp@latest" in your MCP config to always run the newest version.`
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
} catch {
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
|
|
250
343
|
// src/index.ts
|
|
344
|
+
function readPackageVersion() {
|
|
345
|
+
try {
|
|
346
|
+
const here2 = path2.dirname(fileURLToPath2(import.meta.url));
|
|
347
|
+
const pkg = JSON.parse(
|
|
348
|
+
readFileSync(path2.resolve(here2, "../package.json"), "utf8")
|
|
349
|
+
);
|
|
350
|
+
return pkg.version ?? "0.0.0";
|
|
351
|
+
} catch {
|
|
352
|
+
return "0.0.0";
|
|
353
|
+
}
|
|
354
|
+
}
|
|
251
355
|
async function main() {
|
|
252
356
|
const { bundle, source } = await loadBundle();
|
|
253
|
-
const
|
|
357
|
+
const versionState = { current: readPackageVersion() };
|
|
358
|
+
void checkForUpdate(versionState);
|
|
359
|
+
const server = createEpsServer(bundle, source, versionState);
|
|
254
360
|
const transport = new StdioServerTransport();
|
|
255
361
|
await server.connect(transport);
|
|
256
362
|
console.error(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ekoindia/eps-context-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Local MCP server giving AI coding agents context for Eko Platform Services (EPS) APIs.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -23,11 +23,12 @@
|
|
|
23
23
|
"bake": "node scripts/bake-bundle.mjs",
|
|
24
24
|
"build": "tsup",
|
|
25
25
|
"test": "vitest run",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
26
27
|
"prepublishOnly": "npm run bake && npm run build"
|
|
27
28
|
},
|
|
28
29
|
"dependencies": {
|
|
29
30
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
30
|
-
"zod": "^
|
|
31
|
+
"zod": "^4.0.0"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"tsup": "^8.0.0",
|