cheap-llm-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.
- package/.env.example +10 -0
- package/CHANGELOG.md +9 -0
- package/CONTRIBUTING.md +21 -0
- package/LICENSE +21 -0
- package/README.md +243 -0
- package/README.zh-CN.md +161 -0
- package/SECURITY.md +17 -0
- package/codex-config.example.toml +16 -0
- package/dist/accounting.d.ts +39 -0
- package/dist/accounting.js +79 -0
- package/dist/accounting.js.map +1 -0
- package/dist/chat.d.ts +4 -0
- package/dist/chat.js +82 -0
- package/dist/chat.js.map +1 -0
- package/dist/doctor.d.ts +1 -0
- package/dist/doctor.js +49 -0
- package/dist/doctor.js.map +1 -0
- package/dist/env.d.ts +3 -0
- package/dist/env.js +20 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/providers.d.ts +43 -0
- package/dist/providers.js +133 -0
- package/dist/providers.js.map +1 -0
- package/dist/safety.d.ts +10 -0
- package/dist/safety.js +62 -0
- package/dist/safety.js.map +1 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.js +115 -0
- package/dist/server.js.map +1 -0
- package/dist/setup.d.ts +22 -0
- package/dist/setup.js +133 -0
- package/dist/setup.js.map +1 -0
- package/dist/types.d.ts +45 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +63 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { getUsageSummary, resetUsage } from "./accounting.js";
|
|
5
|
+
import { callChatCompletion } from "./chat.js";
|
|
6
|
+
import { envFlag } from "./env.js";
|
|
7
|
+
import { allProviders, providerSetupStatus } from "./providers.js";
|
|
8
|
+
import { maxPromptChars } from "./safety.js";
|
|
9
|
+
export const SERVER_NAME = "cheap-llm-mcp";
|
|
10
|
+
export const SERVER_VERSION = "0.1.0";
|
|
11
|
+
export function createServer(source = process.env) {
|
|
12
|
+
const server = new McpServer({
|
|
13
|
+
name: SERVER_NAME,
|
|
14
|
+
version: SERVER_VERSION
|
|
15
|
+
});
|
|
16
|
+
server.registerTool("list_simple_model_providers", {
|
|
17
|
+
title: "List configured simple LLM providers",
|
|
18
|
+
description: "Show configured low-cost OpenAI-compatible model providers without exposing API keys.",
|
|
19
|
+
inputSchema: {}
|
|
20
|
+
}, async () => ({
|
|
21
|
+
content: [
|
|
22
|
+
{
|
|
23
|
+
type: "text",
|
|
24
|
+
text: JSON.stringify(providerSetupStatus(source), null, 2)
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}));
|
|
28
|
+
server.registerTool("ask_simple_model", {
|
|
29
|
+
title: "Ask a cheap simple-task LLM",
|
|
30
|
+
description: "Delegate simple, low-risk tasks to a configured cheap OpenAI-compatible model. Use for summarizing, rewriting, translating, classification, extraction, short regex, or small code snippets. Treat the result as a draft: the host AI must lightly review it against the original task before using it, without sending extra context or asking for a second review unless necessary. Do not use for architecture decisions, risky edits, security-sensitive decisions, or private workspace context unless the user explicitly permits sending that context to the provider.",
|
|
31
|
+
inputSchema: {
|
|
32
|
+
prompt: z.string().min(1).describe("The exact self-contained task to send to the provider."),
|
|
33
|
+
provider: z.string().optional().describe("Provider name, for example deepseek, qwen, or mimo."),
|
|
34
|
+
model: z.string().optional().describe("Override the provider default model."),
|
|
35
|
+
system: z.string().optional().describe("Optional system instruction."),
|
|
36
|
+
approvedForExternalApi: z
|
|
37
|
+
.boolean()
|
|
38
|
+
.optional()
|
|
39
|
+
.describe("Must be true only after confirming the prompt is safe to send to the third-party model API."),
|
|
40
|
+
dataClassification: z
|
|
41
|
+
.enum(["public", "internal", "private", "sensitive"])
|
|
42
|
+
.optional()
|
|
43
|
+
.describe("Classification of the prompt content. sensitive content is always rejected."),
|
|
44
|
+
temperature: z.number().min(0).max(2).optional().describe("Sampling temperature. Defaults to 0.2."),
|
|
45
|
+
maxTokens: z.number().int().positive().max(8192).optional().describe("Maximum output tokens. Defaults to 800."),
|
|
46
|
+
responseFormat: z.enum(["text", "json_object"]).optional().describe("Request text or JSON object output."),
|
|
47
|
+
includeUsage: z.boolean().optional().describe("Append provider usage metadata when available."),
|
|
48
|
+
requestTimeoutMs: z.number().int().positive().max(300000).optional().describe("Abort the provider request after this many milliseconds."),
|
|
49
|
+
extraBody: z.record(z.unknown()).optional().describe("Provider-specific JSON body overrides.")
|
|
50
|
+
}
|
|
51
|
+
}, async (input) => ({
|
|
52
|
+
content: [
|
|
53
|
+
{
|
|
54
|
+
type: "text",
|
|
55
|
+
text: await callChatCompletion(input, source)
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
}));
|
|
59
|
+
server.registerTool("get_token_savings", {
|
|
60
|
+
title: "Get cheap LLM token savings",
|
|
61
|
+
description: "Show how many provider-reported tokens have been routed to cheap models in this MCP server session. Treat this as actual cheap-model token usage plus a rough premium-token volume avoided estimate.",
|
|
62
|
+
inputSchema: {
|
|
63
|
+
reset: z.boolean().optional().describe("Reset in-memory counters after returning the current summary.")
|
|
64
|
+
}
|
|
65
|
+
}, async (input) => {
|
|
66
|
+
const summary = getUsageSummary();
|
|
67
|
+
if (input.reset) {
|
|
68
|
+
resetUsage();
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
content: [
|
|
72
|
+
{
|
|
73
|
+
type: "text",
|
|
74
|
+
text: JSON.stringify(summary, null, 2)
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
server.registerTool("check_simple_model_setup", {
|
|
80
|
+
title: "Check cheap LLM MCP setup",
|
|
81
|
+
description: "Validate local provider configuration without making a model request. Shows missing API keys, endpoint URLs, HTTPS status, and safety limits.",
|
|
82
|
+
inputSchema: {}
|
|
83
|
+
}, async () => {
|
|
84
|
+
const providers = providerSetupStatus(source);
|
|
85
|
+
return {
|
|
86
|
+
content: [
|
|
87
|
+
{
|
|
88
|
+
type: "text",
|
|
89
|
+
text: JSON.stringify({
|
|
90
|
+
ok: providers.length > 0 && providers.every((provider) => provider.hasApiKey && provider.https),
|
|
91
|
+
safety: {
|
|
92
|
+
maxPromptChars: maxPromptChars(source),
|
|
93
|
+
sensitiveDataRejected: true,
|
|
94
|
+
secretPatternScan: true,
|
|
95
|
+
requiresApprovedForExternalApi: true,
|
|
96
|
+
chineseDefault: envFlag("SIMPLE_LLM_CHINESE_DEFAULT", true, source),
|
|
97
|
+
stabilityDefault: envFlag("SIMPLE_LLM_STABILITY_DEFAULT", true, source),
|
|
98
|
+
hostReviewRequired: true,
|
|
99
|
+
allowHttp: envFlag("SIMPLE_LLM_ALLOW_HTTP", false, source)
|
|
100
|
+
},
|
|
101
|
+
providers
|
|
102
|
+
}, null, 2)
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
return server;
|
|
108
|
+
}
|
|
109
|
+
export async function startStdioServer(source = process.env) {
|
|
110
|
+
allProviders(source);
|
|
111
|
+
const server = createServer(source);
|
|
112
|
+
const transport = new StdioServerTransport();
|
|
113
|
+
await server.connect(transport);
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/setup.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
type Client = "claude" | "codex";
|
|
2
|
+
export type ProviderAnswers = {
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
model?: string;
|
|
6
|
+
chatPath?: string;
|
|
7
|
+
};
|
|
8
|
+
export type SetupOptions = ProviderAnswers & {
|
|
9
|
+
clients: Client[];
|
|
10
|
+
execute?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare function buildClaudeCommand(answer: ProviderAnswers): string[];
|
|
13
|
+
export declare function buildCodexCommand(answer: ProviderAnswers): string[];
|
|
14
|
+
export declare function shellQuote(value: string): string;
|
|
15
|
+
export declare function commandToString(command: string[]): string;
|
|
16
|
+
export declare function codexTomlSnippet(answer: ProviderAnswers): string;
|
|
17
|
+
export declare function claudeJsonSnippet(answer: ProviderAnswers): string;
|
|
18
|
+
export declare function collectSetupOptions(): Promise<SetupOptions>;
|
|
19
|
+
export declare function commandsForSetup(options: SetupOptions): string[][];
|
|
20
|
+
export declare function runSetup(options?: SetupOptions): Promise<number>;
|
|
21
|
+
export declare function printConfig(): void;
|
|
22
|
+
export {};
|
package/dist/setup.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { createInterface } from "node:readline/promises";
|
|
3
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
4
|
+
const PACKAGE_SPEC = "cheap-llm-mcp@latest";
|
|
5
|
+
const SERVER_NAME = "cheap-llm";
|
|
6
|
+
function envPairsForProvider(answer) {
|
|
7
|
+
const env = {
|
|
8
|
+
CHEAP_LLM_BASE_URL: answer.baseUrl ?? "https://api.deepseek.com",
|
|
9
|
+
CHEAP_LLM_MODEL: answer.model ?? "deepseek-chat",
|
|
10
|
+
CHEAP_LLM_CHAT_PATH: answer.chatPath ?? "/chat/completions",
|
|
11
|
+
SIMPLE_LLM_DEFAULT_PROVIDER: "cheap",
|
|
12
|
+
SIMPLE_LLM_CHINESE_DEFAULT: "true",
|
|
13
|
+
SIMPLE_LLM_STABILITY_DEFAULT: "true",
|
|
14
|
+
SIMPLE_LLM_MAX_PROMPT_CHARS: "12000",
|
|
15
|
+
SIMPLE_LLM_TIMEOUT_MS: "60000"
|
|
16
|
+
};
|
|
17
|
+
if (answer.apiKey) {
|
|
18
|
+
env.CHEAP_LLM_API_KEY = answer.apiKey;
|
|
19
|
+
}
|
|
20
|
+
return Object.fromEntries(Object.entries(env).filter(([, value]) => value.length > 0));
|
|
21
|
+
}
|
|
22
|
+
export function buildClaudeCommand(answer) {
|
|
23
|
+
const envArgs = Object.entries(envPairsForProvider(answer)).flatMap(([key, value]) => ["--env", `${key}=${value}`]);
|
|
24
|
+
return ["claude", "mcp", "add", "--transport", "stdio", "--scope", "user", ...envArgs, SERVER_NAME, "--", "npx", "-y", PACKAGE_SPEC];
|
|
25
|
+
}
|
|
26
|
+
export function buildCodexCommand(answer) {
|
|
27
|
+
const envArgs = Object.entries(envPairsForProvider(answer)).flatMap(([key, value]) => ["--env", `${key}=${value}`]);
|
|
28
|
+
return ["codex", "mcp", "add", SERVER_NAME, ...envArgs, "--", "npx", "-y", PACKAGE_SPEC];
|
|
29
|
+
}
|
|
30
|
+
export function shellQuote(value) {
|
|
31
|
+
return /\s|["']/.test(value) ? JSON.stringify(value) : value;
|
|
32
|
+
}
|
|
33
|
+
export function commandToString(command) {
|
|
34
|
+
return command.map(shellQuote).join(" ");
|
|
35
|
+
}
|
|
36
|
+
export function codexTomlSnippet(answer) {
|
|
37
|
+
const envLines = Object.entries(envPairsForProvider(answer))
|
|
38
|
+
.map(([key, value]) => `${key} = ${JSON.stringify(value)}`)
|
|
39
|
+
.join("\n");
|
|
40
|
+
return `[mcp_servers.${SERVER_NAME}]
|
|
41
|
+
command = "npx"
|
|
42
|
+
args = ["-y", "${PACKAGE_SPEC}"]
|
|
43
|
+
|
|
44
|
+
[mcp_servers.${SERVER_NAME}.env]
|
|
45
|
+
${envLines}
|
|
46
|
+
`;
|
|
47
|
+
}
|
|
48
|
+
export function claudeJsonSnippet(answer) {
|
|
49
|
+
return JSON.stringify({
|
|
50
|
+
type: "stdio",
|
|
51
|
+
command: "npx",
|
|
52
|
+
args: ["-y", PACKAGE_SPEC],
|
|
53
|
+
env: envPairsForProvider(answer)
|
|
54
|
+
}, null, 2);
|
|
55
|
+
}
|
|
56
|
+
async function choose(prompt, choices, defaultIndex = 0) {
|
|
57
|
+
const rl = createInterface({ input, output });
|
|
58
|
+
try {
|
|
59
|
+
output.write(`${prompt}\n`);
|
|
60
|
+
choices.forEach((choice, index) => output.write(` ${index + 1}. ${choice}${index === defaultIndex ? " (default)" : ""}\n`));
|
|
61
|
+
const raw = await rl.question("> ");
|
|
62
|
+
const index = raw.trim() ? Number(raw.trim()) - 1 : defaultIndex;
|
|
63
|
+
return choices[index] ?? choices[defaultIndex];
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
rl.close();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function ask(prompt, defaultValue = "") {
|
|
70
|
+
const rl = createInterface({ input, output });
|
|
71
|
+
try {
|
|
72
|
+
const suffix = defaultValue ? ` (${defaultValue})` : "";
|
|
73
|
+
const answer = await rl.question(`${prompt}${suffix}: `);
|
|
74
|
+
return answer.trim() || defaultValue;
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
rl.close();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export async function collectSetupOptions() {
|
|
81
|
+
const clientChoice = await choose("Which client should be configured?", ["Claude Code", "Codex", "Both"], 2);
|
|
82
|
+
const baseUrl = await ask("OpenAI-compatible base URL", "https://api.deepseek.com");
|
|
83
|
+
const model = await ask("Model id", "deepseek-chat");
|
|
84
|
+
const chatPath = await ask("Chat completions path", "/chat/completions");
|
|
85
|
+
const apiKey = await ask("API key (leave blank if you will fill it manually in the client config)");
|
|
86
|
+
const execute = (await choose("Execute these install commands now?", ["Yes", "No"], 0)) === "Yes";
|
|
87
|
+
return {
|
|
88
|
+
clients: clientChoice === "Both" ? ["claude", "codex"] : clientChoice === "Claude Code" ? ["claude"] : ["codex"],
|
|
89
|
+
apiKey: apiKey || undefined,
|
|
90
|
+
baseUrl,
|
|
91
|
+
model,
|
|
92
|
+
chatPath,
|
|
93
|
+
execute
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
export function commandsForSetup(options) {
|
|
97
|
+
return options.clients.map((client) => (client === "claude" ? buildClaudeCommand(options) : buildCodexCommand(options)));
|
|
98
|
+
}
|
|
99
|
+
export async function runSetup(options) {
|
|
100
|
+
const selected = options ?? (await collectSetupOptions());
|
|
101
|
+
const commands = commandsForSetup(selected);
|
|
102
|
+
output.write("\nCommands:\n");
|
|
103
|
+
commands.forEach((command) => output.write(` ${commandToString(command)}\n`));
|
|
104
|
+
if (!selected.execute) {
|
|
105
|
+
output.write("\nSkipped execution. Copy a command above, or run setup again and choose execution.\n");
|
|
106
|
+
output.write("\nManual Codex fallback:\n");
|
|
107
|
+
output.write(codexTomlSnippet(selected));
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
for (const command of commands) {
|
|
111
|
+
output.write(`\nRunning: ${commandToString(command)}\n`);
|
|
112
|
+
const result = process.platform === "win32"
|
|
113
|
+
? spawnSync("cmd.exe", ["/d", "/s", "/c", commandToString(command)], { stdio: "inherit" })
|
|
114
|
+
: spawnSync(command[0], command.slice(1), { stdio: "inherit" });
|
|
115
|
+
if (result.status !== 0) {
|
|
116
|
+
output.write("\nCommand failed. Manual Codex fallback:\n");
|
|
117
|
+
output.write(codexTomlSnippet(selected));
|
|
118
|
+
return result.status ?? 1;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
output.write("\nDone. Restart Claude Code or Codex, then check /mcp or run codex mcp list.\n");
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
export function printConfig() {
|
|
125
|
+
const answer = {};
|
|
126
|
+
output.write("Claude Code:\n");
|
|
127
|
+
output.write(`${commandToString(buildClaudeCommand(answer))}\n\n`);
|
|
128
|
+
output.write("Codex:\n");
|
|
129
|
+
output.write(`${commandToString(buildCodexCommand(answer))}\n\n`);
|
|
130
|
+
output.write("Codex config.toml fallback:\n");
|
|
131
|
+
output.write(codexTomlSnippet(answer));
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export type DataClassification = "public" | "internal" | "private" | "sensitive";
|
|
2
|
+
export type ProviderConfig = {
|
|
3
|
+
name: string;
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
chatPath?: string;
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
apiKeyEnv?: string;
|
|
8
|
+
model: string;
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
defaultBody?: Record<string, unknown>;
|
|
11
|
+
timeoutMs?: number;
|
|
12
|
+
};
|
|
13
|
+
export type ChatCompletionResponse = {
|
|
14
|
+
choices?: Array<{
|
|
15
|
+
message?: {
|
|
16
|
+
content?: string | null;
|
|
17
|
+
reasoning_content?: string | null;
|
|
18
|
+
};
|
|
19
|
+
finish_reason?: string;
|
|
20
|
+
}>;
|
|
21
|
+
usage?: {
|
|
22
|
+
prompt_tokens?: number;
|
|
23
|
+
completion_tokens?: number;
|
|
24
|
+
total_tokens?: number;
|
|
25
|
+
};
|
|
26
|
+
model?: string;
|
|
27
|
+
};
|
|
28
|
+
export type ChatMessage = {
|
|
29
|
+
role: "system" | "user";
|
|
30
|
+
content: string;
|
|
31
|
+
};
|
|
32
|
+
export type AskSimpleModelInput = {
|
|
33
|
+
provider?: string;
|
|
34
|
+
model?: string;
|
|
35
|
+
system?: string;
|
|
36
|
+
prompt: string;
|
|
37
|
+
temperature?: number;
|
|
38
|
+
maxTokens?: number;
|
|
39
|
+
responseFormat?: "text" | "json_object";
|
|
40
|
+
includeUsage?: boolean;
|
|
41
|
+
extraBody?: Record<string, unknown>;
|
|
42
|
+
requestTimeoutMs?: number;
|
|
43
|
+
approvedForExternalApi?: boolean;
|
|
44
|
+
dataClassification?: DataClassification;
|
|
45
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cheap-llm-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Save premium-model tokens by routing simple tasks to cheap OpenAI-compatible LLMs.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"cheap-llm-mcp": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"typecheck": "tsc --noEmit",
|
|
12
|
+
"test": "node --import tsx --test test/*.test.ts",
|
|
13
|
+
"ci": "npm run typecheck && npm run build && npm test",
|
|
14
|
+
"prepublishOnly": "npm run ci",
|
|
15
|
+
"start": "node dist/index.js",
|
|
16
|
+
"dev": "tsx src/index.ts"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"mcp",
|
|
20
|
+
"model-context-protocol",
|
|
21
|
+
"token-saver",
|
|
22
|
+
"llm-router",
|
|
23
|
+
"deepseek",
|
|
24
|
+
"mimo",
|
|
25
|
+
"qwen",
|
|
26
|
+
"openai-compatible"
|
|
27
|
+
],
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/stBlackCat/cheap-llm-mcp.git"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/stBlackCat/cheap-llm-mcp#readme",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/stBlackCat/cheap-llm-mcp/issues"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"README.md",
|
|
39
|
+
"README.zh-CN.md",
|
|
40
|
+
"LICENSE",
|
|
41
|
+
"SECURITY.md",
|
|
42
|
+
"CHANGELOG.md",
|
|
43
|
+
"CONTRIBUTING.md",
|
|
44
|
+
".env.example",
|
|
45
|
+
"codex-config.example.toml"
|
|
46
|
+
],
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"license": "MIT",
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
53
|
+
"zod": "^3.25.76"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node": "^24.10.1",
|
|
57
|
+
"tsx": "^4.21.0",
|
|
58
|
+
"typescript": "^5.9.3"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=20"
|
|
62
|
+
}
|
|
63
|
+
}
|