@konseki/mcp 1.0.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/LICENSE +21 -0
- package/README.md +135 -0
- package/SECURITY.md +14 -0
- package/dist/client.d.ts +33 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +108 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +11 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +22 -0
- package/dist/tools.d.ts +25 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +75 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Konseki
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Konseki MCP
|
|
2
|
+
|
|
3
|
+
Official Model Context Protocol server for Konseki.
|
|
4
|
+
|
|
5
|
+
Konseki MCP is a direct wrapper around the Konseki public API. It lets AI agents and AI trading tools call Konseki endpoints through MCP tools while preserving the raw API response JSON for the user or downstream application to interpret.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- Node.js 20 or newer.
|
|
10
|
+
- A Konseki API key.
|
|
11
|
+
|
|
12
|
+
## Design Principles
|
|
13
|
+
|
|
14
|
+
- Direct API wrapper: the MCP server fetches Konseki API responses and returns them without interpretation.
|
|
15
|
+
- User-owned interpretation: users, builders, and downstream AI clients decide how to analyze or summarize the returned JSON.
|
|
16
|
+
- Public API only: the server uses `X-API-Key` against documented Konseki endpoints.
|
|
17
|
+
- No internal credentials: non-public service or operational credentials are never required for this package.
|
|
18
|
+
|
|
19
|
+
## Configuration
|
|
20
|
+
|
|
21
|
+
The server reads configuration from environment variables:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
KONSEKI_API_KEY=ks_live_your_api_key
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Do not commit real API keys.
|
|
28
|
+
|
|
29
|
+
## Intended Architecture
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
AI client
|
|
33
|
+
|
|
|
34
|
+
v
|
|
35
|
+
Konseki MCP server
|
|
36
|
+
|
|
|
37
|
+
v
|
|
38
|
+
Konseki public API
|
|
39
|
+
|
|
|
40
|
+
v
|
|
41
|
+
Raw historical market context JSON
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The MCP server should not bypass Konseki public API behavior. It should behave like any other public API client.
|
|
45
|
+
|
|
46
|
+
## Tools
|
|
47
|
+
|
|
48
|
+
### `get_konseki_metadata`
|
|
49
|
+
|
|
50
|
+
Fetches raw JSON from `GET /v1/metadata`.
|
|
51
|
+
|
|
52
|
+
Input: none.
|
|
53
|
+
|
|
54
|
+
### `list_konseki_symbols`
|
|
55
|
+
|
|
56
|
+
Fetches raw JSON from `GET /v1/symbols`.
|
|
57
|
+
|
|
58
|
+
Input: none.
|
|
59
|
+
|
|
60
|
+
### `get_konseki_analysis`
|
|
61
|
+
|
|
62
|
+
Fetches raw JSON from `GET /v1/analysis/{symbol}-{exchange}?lookback={lookback}`.
|
|
63
|
+
|
|
64
|
+
Input:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"symbol": "AAPL",
|
|
69
|
+
"exchange": "NASDAQ",
|
|
70
|
+
"lookback": 15
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Supported `lookback` values: `5`, `10`, `15`, `20`, `25`, `30`, `40`, `50`.
|
|
75
|
+
|
|
76
|
+
## Response Compression
|
|
77
|
+
|
|
78
|
+
The server requests gzip-compressed API responses and decompresses them locally before returning JSON to the MCP client. This is handled automatically; users do not need to configure compression.
|
|
79
|
+
|
|
80
|
+
## Installation
|
|
81
|
+
|
|
82
|
+
Use the package through an MCP client with `npx`:
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"mcpServers": {
|
|
87
|
+
"konseki": {
|
|
88
|
+
"command": "npx",
|
|
89
|
+
"args": ["-y", "@konseki/mcp"],
|
|
90
|
+
"env": {
|
|
91
|
+
"KONSEKI_API_KEY": "ks_live_your_api_key"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
For local development from this checkout, configure your MCP client to run the built server:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"mcpServers": {
|
|
103
|
+
"konseki": {
|
|
104
|
+
"command": "node",
|
|
105
|
+
"args": ["/absolute/path/to/konseki-mcp/dist/index.js"],
|
|
106
|
+
"env": {
|
|
107
|
+
"KONSEKI_API_KEY": "ks_live_your_api_key"
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Development
|
|
115
|
+
|
|
116
|
+
Install dependencies:
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
npm install
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Run verification:
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
npm run typecheck
|
|
126
|
+
npm test
|
|
127
|
+
npm run build
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Security
|
|
131
|
+
|
|
132
|
+
- Never commit real API keys.
|
|
133
|
+
- Never log raw API keys.
|
|
134
|
+
- Never include user credentials in test snapshots or examples.
|
|
135
|
+
- Use fake keys in documentation and tests.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
## Reporting Vulnerabilities
|
|
4
|
+
|
|
5
|
+
Please report suspected security vulnerabilities privately through the repository's GitHub security advisory flow when available.
|
|
6
|
+
|
|
7
|
+
Do not include real API keys, customer data, or other secrets in public issues, pull requests, tests, or examples.
|
|
8
|
+
|
|
9
|
+
## Secret Handling
|
|
10
|
+
|
|
11
|
+
- Configure the server with `KONSEKI_API_KEY` in your local MCP client environment.
|
|
12
|
+
- Do not commit real API keys.
|
|
13
|
+
- Do not paste real API keys into bug reports or screenshots.
|
|
14
|
+
- Use fake keys in tests and documentation.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type KonsekiMcpConfig } from "./config.js";
|
|
2
|
+
export type ApiJsonObject = Record<string, unknown>;
|
|
3
|
+
export type KonsekiApiResult = {
|
|
4
|
+
json: ApiJsonObject;
|
|
5
|
+
ok: true;
|
|
6
|
+
status: number;
|
|
7
|
+
} | {
|
|
8
|
+
json?: ApiJsonObject;
|
|
9
|
+
message: string;
|
|
10
|
+
ok: false;
|
|
11
|
+
status?: number;
|
|
12
|
+
};
|
|
13
|
+
type FetchLike = typeof fetch;
|
|
14
|
+
export type KonsekiApiClientOptions = {
|
|
15
|
+
fetchImpl?: FetchLike;
|
|
16
|
+
timeoutMs?: number;
|
|
17
|
+
};
|
|
18
|
+
export declare class KonsekiApiClient {
|
|
19
|
+
private readonly apiKey;
|
|
20
|
+
private readonly fetchImpl;
|
|
21
|
+
private readonly timeoutMs;
|
|
22
|
+
constructor(config: KonsekiMcpConfig, options?: KonsekiApiClientOptions);
|
|
23
|
+
getMetadata(): Promise<KonsekiApiResult>;
|
|
24
|
+
listSymbols(): Promise<KonsekiApiResult>;
|
|
25
|
+
getAnalysis(params: {
|
|
26
|
+
exchange: string;
|
|
27
|
+
lookback: string;
|
|
28
|
+
symbol: string;
|
|
29
|
+
}): Promise<KonsekiApiResult>;
|
|
30
|
+
private request;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkD,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpG,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAIpD,MAAM,MAAM,gBAAgB,GACxB;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEN,KAAK,SAAS,GAAG,OAAO,KAAK,CAAC;AAE9B,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,MAAM,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,uBAAuB;IAMjE,WAAW,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAIxC,WAAW,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAIxC,WAAW,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;YAS9F,OAAO;CAsDtB"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { gunzip } from "node:zlib";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { KONSEKI_API_ORIGIN, KONSEKI_REQUEST_TIMEOUT_MS } from "./config.js";
|
|
4
|
+
const gunzipAsync = promisify(gunzip);
|
|
5
|
+
export class KonsekiApiClient {
|
|
6
|
+
apiKey;
|
|
7
|
+
fetchImpl;
|
|
8
|
+
timeoutMs;
|
|
9
|
+
constructor(config, options) {
|
|
10
|
+
this.apiKey = config.apiKey;
|
|
11
|
+
this.fetchImpl = options?.fetchImpl ?? fetch;
|
|
12
|
+
this.timeoutMs = options?.timeoutMs ?? KONSEKI_REQUEST_TIMEOUT_MS;
|
|
13
|
+
}
|
|
14
|
+
async getMetadata() {
|
|
15
|
+
return this.request("/v1/metadata");
|
|
16
|
+
}
|
|
17
|
+
async listSymbols() {
|
|
18
|
+
return this.request("/v1/symbols");
|
|
19
|
+
}
|
|
20
|
+
async getAnalysis(params) {
|
|
21
|
+
const symbolExchange = `${encodeURIComponent(params.symbol)}-${encodeURIComponent(params.exchange)}`;
|
|
22
|
+
const searchParams = new URLSearchParams({
|
|
23
|
+
lookback: params.lookback,
|
|
24
|
+
});
|
|
25
|
+
return this.request(`/v1/analysis/${symbolExchange}?${searchParams.toString()}`);
|
|
26
|
+
}
|
|
27
|
+
async request(pathAndQuery) {
|
|
28
|
+
const url = new URL(pathAndQuery, KONSEKI_API_ORIGIN);
|
|
29
|
+
const controller = new AbortController();
|
|
30
|
+
const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
31
|
+
try {
|
|
32
|
+
const response = await this.fetchImpl(url, {
|
|
33
|
+
headers: {
|
|
34
|
+
Accept: "application/json",
|
|
35
|
+
"Accept-Encoding": "gzip",
|
|
36
|
+
"X-API-Key": this.apiKey,
|
|
37
|
+
},
|
|
38
|
+
method: "GET",
|
|
39
|
+
signal: controller.signal,
|
|
40
|
+
});
|
|
41
|
+
const parsed = await parseJsonObject(response);
|
|
42
|
+
if (!parsed.ok) {
|
|
43
|
+
return {
|
|
44
|
+
message: parsed.message,
|
|
45
|
+
ok: false,
|
|
46
|
+
status: response.status,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
return {
|
|
51
|
+
json: parsed.json,
|
|
52
|
+
message: `Konseki API returned HTTP ${response.status}.`,
|
|
53
|
+
ok: false,
|
|
54
|
+
status: response.status,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
json: parsed.json,
|
|
59
|
+
ok: true,
|
|
60
|
+
status: response.status,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
const message = error instanceof DOMException && error.name === "AbortError"
|
|
65
|
+
? "Konseki API request timed out."
|
|
66
|
+
: "Unable to reach Konseki API.";
|
|
67
|
+
return {
|
|
68
|
+
message,
|
|
69
|
+
ok: false,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
clearTimeout(timeout);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function parseJsonObject(response) {
|
|
78
|
+
try {
|
|
79
|
+
const json = JSON.parse(await responseBodyText(response));
|
|
80
|
+
if (!isJsonObject(json)) {
|
|
81
|
+
return {
|
|
82
|
+
message: "Konseki API returned JSON that is not an object.",
|
|
83
|
+
ok: false,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
json,
|
|
88
|
+
ok: true,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return {
|
|
93
|
+
message: "Konseki API returned a non-JSON response.",
|
|
94
|
+
ok: false,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async function responseBodyText(response) {
|
|
99
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
100
|
+
const decompressed = isGzip(bytes) ? await gunzipAsync(bytes) : bytes;
|
|
101
|
+
return new TextDecoder().decode(decompressed);
|
|
102
|
+
}
|
|
103
|
+
function isGzip(bytes) {
|
|
104
|
+
return bytes.length >= 2 && bytes[0] === 0x1f && bytes[1] === 0x8b;
|
|
105
|
+
}
|
|
106
|
+
function isJsonObject(value) {
|
|
107
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
108
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type KonsekiMcpConfig = {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
};
|
|
4
|
+
export declare const KONSEKI_API_ORIGIN = "https://api.konseki.io";
|
|
5
|
+
export declare const KONSEKI_REQUEST_TIMEOUT_MS = 30000;
|
|
6
|
+
export declare function loadConfigFromEnv(env?: Record<string, string | undefined>): KonsekiMcpConfig;
|
|
7
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,kBAAkB,2BAA2B,CAAC;AAC3D,eAAO,MAAM,0BAA0B,QAAS,CAAC;AAEjD,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,gBAAgB,CAUzG"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const KONSEKI_API_ORIGIN = "https://api.konseki.io";
|
|
2
|
+
export const KONSEKI_REQUEST_TIMEOUT_MS = 30_000;
|
|
3
|
+
export function loadConfigFromEnv(env = process.env) {
|
|
4
|
+
const apiKey = env.KONSEKI_API_KEY?.trim();
|
|
5
|
+
if (!apiKey) {
|
|
6
|
+
throw new Error("KONSEKI_API_KEY is required.");
|
|
7
|
+
}
|
|
8
|
+
return {
|
|
9
|
+
apiKey,
|
|
10
|
+
};
|
|
11
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export { KonsekiApiClient } from "./client.js";
|
|
3
|
+
export { KONSEKI_API_ORIGIN, KONSEKI_REQUEST_TIMEOUT_MS, loadConfigFromEnv, type KonsekiMcpConfig } from "./config.js";
|
|
4
|
+
export { createKonsekiMcpServer } from "./server.js";
|
|
5
|
+
export { normalizeAnalysisInput } from "./tools.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAOA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACvH,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { KonsekiApiClient } from "./client.js";
|
|
5
|
+
import { loadConfigFromEnv } from "./config.js";
|
|
6
|
+
import { createKonsekiMcpServer } from "./server.js";
|
|
7
|
+
export { KonsekiApiClient } from "./client.js";
|
|
8
|
+
export { KONSEKI_API_ORIGIN, KONSEKI_REQUEST_TIMEOUT_MS, loadConfigFromEnv } from "./config.js";
|
|
9
|
+
export { createKonsekiMcpServer } from "./server.js";
|
|
10
|
+
export { normalizeAnalysisInput } from "./tools.js";
|
|
11
|
+
async function main() {
|
|
12
|
+
const config = loadConfigFromEnv();
|
|
13
|
+
const client = new KonsekiApiClient(config);
|
|
14
|
+
const server = createKonsekiMcpServer(client);
|
|
15
|
+
await server.connect(new StdioServerTransport());
|
|
16
|
+
}
|
|
17
|
+
if (isCliEntryPoint()) {
|
|
18
|
+
main().catch((error) => {
|
|
19
|
+
const message = error instanceof Error ? error.message : "Unable to start Konseki MCP server.";
|
|
20
|
+
console.error(message);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function isCliEntryPoint() {
|
|
25
|
+
const entrypoint = process.argv[1];
|
|
26
|
+
return entrypoint ? import.meta.url === pathToFileURL(entrypoint).href : false;
|
|
27
|
+
}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAQpD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,SAAS,CAmC1E"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { analysisInputSchema, createAnalysisToolHandler, createMetadataToolHandler, createSymbolsToolHandler, } from "./tools.js";
|
|
3
|
+
export function createKonsekiMcpServer(client) {
|
|
4
|
+
const server = new McpServer({
|
|
5
|
+
name: "konseki-mcp",
|
|
6
|
+
version: "1.0.0",
|
|
7
|
+
});
|
|
8
|
+
server.registerTool("get_konseki_metadata", {
|
|
9
|
+
description: "Fetch raw metadata JSON from the Konseki public API.",
|
|
10
|
+
title: "Get Konseki Metadata",
|
|
11
|
+
}, createMetadataToolHandler(client));
|
|
12
|
+
server.registerTool("list_konseki_symbols", {
|
|
13
|
+
description: "Fetch raw supported symbols JSON from the Konseki public API.",
|
|
14
|
+
title: "List Konseki Symbols",
|
|
15
|
+
}, createSymbolsToolHandler(client));
|
|
16
|
+
server.registerTool("get_konseki_analysis", {
|
|
17
|
+
description: "Fetch raw analysis JSON for a symbol, exchange, and lookback from the Konseki public API.",
|
|
18
|
+
inputSchema: analysisInputSchema,
|
|
19
|
+
title: "Get Konseki Analysis",
|
|
20
|
+
}, createAnalysisToolHandler(client));
|
|
21
|
+
return server;
|
|
22
|
+
}
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { ApiJsonObject, KonsekiApiClient, KonsekiApiResult } from "./client.js";
|
|
4
|
+
export declare const analysisInputSchema: {
|
|
5
|
+
exchange: z.ZodString;
|
|
6
|
+
lookback: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
7
|
+
symbol: z.ZodString;
|
|
8
|
+
};
|
|
9
|
+
export type AnalysisInput = {
|
|
10
|
+
exchange: string;
|
|
11
|
+
lookback: number | string;
|
|
12
|
+
symbol: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function normalizeAnalysisInput(input: AnalysisInput): {
|
|
15
|
+
exchange: string;
|
|
16
|
+
lookback: string;
|
|
17
|
+
symbol: string;
|
|
18
|
+
};
|
|
19
|
+
export declare function jsonToolResult(json: ApiJsonObject): CallToolResult;
|
|
20
|
+
export declare function apiResultToToolResult(result: KonsekiApiResult): CallToolResult;
|
|
21
|
+
export declare function safeErrorResult(message: string): CallToolResult;
|
|
22
|
+
export declare function createMetadataToolHandler(client: KonsekiApiClient): () => Promise<CallToolResult>;
|
|
23
|
+
export declare function createSymbolsToolHandler(client: KonsekiApiClient): () => Promise<CallToolResult>;
|
|
24
|
+
export declare function createAnalysisToolHandler(client: KonsekiApiClient): (input: AnalysisInput) => Promise<CallToolResult>;
|
|
25
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIrF,eAAO,MAAM,mBAAmB;;;;CAI/B,CAAC;AAIF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,aAAa,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAYnH;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,aAAa,GAAG,cAAc,CAUlE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,gBAAgB,GAAG,cAAc,CAa9E;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAU/D;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,CAEjG;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,CAEhG;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,gBAAgB,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,cAAc,CAAC,CAiBrH"}
|
package/dist/tools.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const supportedLookbacks = new Set(["5", "10", "15", "20", "25", "30", "40", "50"]);
|
|
3
|
+
export const analysisInputSchema = {
|
|
4
|
+
exchange: z.string().trim().min(1).regex(/^[A-Za-z]+$/, "exchange must contain only letters"),
|
|
5
|
+
lookback: z.union([z.string(), z.number()]),
|
|
6
|
+
symbol: z.string().trim().min(1).regex(/^[A-Za-z0-9.]+$/, "symbol must contain only letters, numbers, and dots"),
|
|
7
|
+
};
|
|
8
|
+
const analysisInputObjectSchema = z.object(analysisInputSchema);
|
|
9
|
+
export function normalizeAnalysisInput(input) {
|
|
10
|
+
const lookback = String(input.lookback).trim();
|
|
11
|
+
if (!supportedLookbacks.has(lookback)) {
|
|
12
|
+
throw new Error("Unsupported lookback value.");
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
exchange: input.exchange.trim().toUpperCase(),
|
|
16
|
+
lookback,
|
|
17
|
+
symbol: input.symbol.trim().toUpperCase(),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function jsonToolResult(json) {
|
|
21
|
+
return {
|
|
22
|
+
content: [
|
|
23
|
+
{
|
|
24
|
+
text: JSON.stringify(json, null, 2),
|
|
25
|
+
type: "text",
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
structuredContent: json,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function apiResultToToolResult(result) {
|
|
32
|
+
if (result.ok) {
|
|
33
|
+
return jsonToolResult(result.json);
|
|
34
|
+
}
|
|
35
|
+
if (result.json) {
|
|
36
|
+
return {
|
|
37
|
+
...jsonToolResult(result.json),
|
|
38
|
+
isError: true,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return safeErrorResult(result.message);
|
|
42
|
+
}
|
|
43
|
+
export function safeErrorResult(message) {
|
|
44
|
+
return {
|
|
45
|
+
content: [
|
|
46
|
+
{
|
|
47
|
+
text: message,
|
|
48
|
+
type: "text",
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
isError: true,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function createMetadataToolHandler(client) {
|
|
55
|
+
return async () => apiResultToToolResult(await client.getMetadata());
|
|
56
|
+
}
|
|
57
|
+
export function createSymbolsToolHandler(client) {
|
|
58
|
+
return async () => apiResultToToolResult(await client.listSymbols());
|
|
59
|
+
}
|
|
60
|
+
export function createAnalysisToolHandler(client) {
|
|
61
|
+
return async (input) => {
|
|
62
|
+
try {
|
|
63
|
+
const normalizedInput = normalizeAnalysisInput(analysisInputObjectSchema.parse(input));
|
|
64
|
+
return apiResultToToolResult(await client.getAnalysis(normalizedInput));
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
const message = error instanceof z.ZodError
|
|
68
|
+
? "Invalid analysis request."
|
|
69
|
+
: error instanceof Error
|
|
70
|
+
? error.message
|
|
71
|
+
: "Invalid analysis request.";
|
|
72
|
+
return safeErrorResult(message);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@konseki/mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Official Konseki MCP server: a direct AI-agent wrapper around the Konseki public API for historical market context.",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"konseki-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"LICENSE",
|
|
13
|
+
"README.md",
|
|
14
|
+
"SECURITY.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -p tsconfig.json",
|
|
18
|
+
"typecheck": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.test.json",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"lint": "npm run typecheck",
|
|
21
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"konseki",
|
|
25
|
+
"mcp",
|
|
26
|
+
"model-context-protocol",
|
|
27
|
+
"trading",
|
|
28
|
+
"market-data",
|
|
29
|
+
"historical-context"
|
|
30
|
+
],
|
|
31
|
+
"author": "Konseki",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=20"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
38
|
+
"zod": "^4.4.3"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^20.0.0",
|
|
42
|
+
"typescript": "^6.0.3",
|
|
43
|
+
"vitest": "^4.1.9"
|
|
44
|
+
}
|
|
45
|
+
}
|