@lofter-admin/mcp-backend 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/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # LOFTER Admin MCP Server
2
+
3
+ 将 LOFTER 管理后台的核心能力封装为 AI 可直接调用的工具集,让 AI 能够查询文章、创作者、IP、标签等内容数据,辅助运营决策和内容分析。
4
+
5
+ ## 快速接入
6
+
7
+ ### 环境要求
8
+
9
+ - Node.js >= 20
10
+
11
+ ### stdio 模式(本地集成)
12
+
13
+ 在 `~/.config/codemaker/codemaker.json` 的 `mcp` 字段中添加:
14
+
15
+ ```json
16
+ {
17
+ "mcp": {
18
+ "LOFTER-admin": {
19
+ "type": "local",
20
+ "command": [
21
+ "npx",
22
+ "@lofter-admin/mcp-backend",
23
+ "start",
24
+ "-t",
25
+ "stdio"
26
+ ],
27
+ "enabled": true,
28
+ "environment": {
29
+ "LOFTER_ADMIN_SECRET": "<你的密钥>"
30
+ },
31
+ "timeout": 60000
32
+ }
33
+ }
34
+ }
35
+ ```
36
+
37
+ `LOFTER_ADMIN_SECRET` 为 LOFTER 管理后台鉴权密钥,向管理员申请。
38
+
39
+ 也可通过 `lofter-admin-cli` 写入本地配置,无需在 MCP 配置中重复填写:
40
+
41
+ ```bash
42
+ lofter-admin-cli config --set secret=<你的密钥>
43
+ ```
44
+
45
+ 写入后保存在 `~/.lofter-micro/config`,mcp-backend 会自动读取。
46
+
47
+ ### HTTP 模式(远程服务 / 多用户)
48
+
49
+ 启动 HTTP MCP Server(可选配默认密钥作为 fallback):
50
+
51
+ ```bash
52
+ LOFTER_ADMIN_SECRET=<默认密钥> npx @lofter-admin/mcp-backend start -t http -p 3000
53
+ ```
54
+
55
+ **Secret 优先级**:请求 header > 环境变量 `LOFTER_ADMIN_SECRET` > `lofter-admin-cli config --set secret=xxx` 写入的本地配置(`~/.lofter-micro/config`)。多用户并发场景下,每个请求独立使用自己的 secret,互不干扰。未提供 header 时依次 fallback 到环境变量和本地配置;三者均未设置则返回错误。
56
+
57
+ ---
58
+
59
+ ## 技术信息
60
+
61
+ - **包名**:`@lofter-admin/mcp-backend`
62
+ - **协议**:MCP(Model Context Protocol)
63
+ - **传输模式**:stdio(本地集成)/ HTTP SSE(远程集成)
64
+ - **上游服务**:`https://lofter.hz.netease.com`
65
+ - **认证方式**:Header `lofter-micro-secret`;HTTP 模式支持通过请求 header `LOFTER_ADMIN_SECRET` 动态传入;也可通过 `lofter-admin-cli config --set secret=xxx` 写入本地配置
66
+
67
+ ---
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
5
+ import { createServer } from 'node:http';
6
+ import { createMcpServer } from './server';
7
+ import { requestContext } from './context';
8
+ const program = new Command();
9
+ program
10
+ .name('lofter-mcp')
11
+ .description('LOFTER Admin MCP Server CLI')
12
+ .version('0.1.0');
13
+ program
14
+ .command('start')
15
+ .description('启动 MCP Server')
16
+ .option('-t, --transport <type>', 'transport 类型: stdio | http', 'stdio')
17
+ .option('-p, --port <number>', 'HTTP 模式监听端口(仅 --transport http 时有效)', '3000')
18
+ .option('-b, --base-url <url>', '后台服务 BASE URL,也可通过 LOFTER_BASE_URL 环境变量设置')
19
+ .action(async (options) => {
20
+ const { transport, port, baseUrl } = options;
21
+ if (transport === 'stdio') {
22
+ await startStdio();
23
+ }
24
+ else if (transport === 'http') {
25
+ await startHttp(parseInt(port, 10), baseUrl);
26
+ }
27
+ else {
28
+ process.stderr.write(`未知的 transport 类型: ${transport},支持 stdio | http\n`);
29
+ process.exit(1);
30
+ }
31
+ });
32
+ program.parse();
33
+ async function startStdio() {
34
+ const server = createMcpServer();
35
+ const transport = new StdioServerTransport();
36
+ await server.connect(transport);
37
+ // stdio 模式下避免向 stdout 写入非协议内容
38
+ process.stderr.write('[lofter-mcp] stdio 模式已启动\n');
39
+ }
40
+ const MCP_ENDPOINT = '/mcp';
41
+ const ALLOWED_METHODS = new Set(['GET', 'POST', 'DELETE']);
42
+ async function startHttp(port, baseUrl) {
43
+ const httpServer = createServer(async (req, res) => {
44
+ // MCP v2 (2025-03-26) Streamable HTTP: 所有请求集中在单一 MCP endpoint
45
+ // POST /mcp - 发送 JSON-RPC 消息(requests / notifications / responses)
46
+ // GET /mcp - 建立 SSE 流,接收服务端推送
47
+ // DELETE /mcp - 显式终止 session(stateful 模式)
48
+ const url = new URL(req.url ?? '/', `http://localhost:${port}`);
49
+ const method = req.method ?? '';
50
+ if (url.pathname !== MCP_ENDPOINT) {
51
+ res.writeHead(404, { 'Content-Type': 'application/json' });
52
+ res.end(JSON.stringify({ error: `Not Found. MCP endpoint is ${MCP_ENDPOINT}` }));
53
+ return;
54
+ }
55
+ if (!ALLOWED_METHODS.has(method)) {
56
+ res.writeHead(405, {
57
+ 'Content-Type': 'application/json',
58
+ Allow: 'GET, POST, DELETE',
59
+ });
60
+ res.end(JSON.stringify({ error: 'Method Not Allowed' }));
61
+ return;
62
+ }
63
+ const rawSecret = req.headers['lofter-admin-secret'];
64
+ const secret = typeof rawSecret === 'string' ? rawSecret : undefined;
65
+ const server = createMcpServer();
66
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
67
+ await server.connect(transport);
68
+ await requestContext.run({ secret, baseUrl }, () => transport.handleRequest(req, res));
69
+ });
70
+ httpServer.listen(port, () => {
71
+ console.log(`[lofter-mcp] HTTP MCP Server 已启动,端口: ${port}`);
72
+ console.log(`[lofter-mcp] MCP endpoint: http://localhost:${port}${MCP_ENDPOINT}`);
73
+ console.log(`[lofter-mcp] POST ${MCP_ENDPOINT} - 发送 JSON-RPC 消息`);
74
+ console.log(`[lofter-mcp] GET ${MCP_ENDPOINT} - 建立 SSE 推送流`);
75
+ console.log(`[lofter-mcp] DELETE ${MCP_ENDPOINT} - 终止 session`);
76
+ });
77
+ }
78
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ interface RequestContext {
3
+ secret?: string;
4
+ baseUrl?: string;
5
+ }
6
+ export declare const requestContext: AsyncLocalStorage<RequestContext>;
7
+ export declare function getSecret(): string;
8
+ export declare function getBaseUrl(): string;
9
+ export {};
10
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAQrD,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,cAAc,mCAA0C,CAAC;AAYtE,wBAAgB,SAAS,IAAI,MAAM,CASlC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAGnC"}
@@ -0,0 +1,30 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ const DEFAULT_BASE_URL = 'https://lofter.hz.netease.com';
6
+ const LOFTER_MICRO_CONFIG_FILE_PATH = path.join(os.homedir(), '.lofter-micro', 'config');
7
+ export const requestContext = new AsyncLocalStorage();
8
+ function getSecretFromUserConfig() {
9
+ try {
10
+ const configStr = fs.readFileSync(LOFTER_MICRO_CONFIG_FILE_PATH, 'utf-8');
11
+ const configs = JSON.parse(configStr);
12
+ return configs.secret;
13
+ }
14
+ catch {
15
+ return undefined;
16
+ }
17
+ }
18
+ export function getSecret() {
19
+ const ctx = requestContext.getStore();
20
+ const secret = ctx?.secret ?? process.env.LOFTER_ADMIN_SECRET ?? getSecretFromUserConfig();
21
+ if (!secret) {
22
+ throw new Error('LOFTER_ADMIN_SECRET 未设置(请求 header、环境变量或 ~/.lofter-micro/config 中的 secret)');
23
+ }
24
+ return secret;
25
+ }
26
+ export function getBaseUrl() {
27
+ const ctx = requestContext.getStore();
28
+ return ctx?.baseUrl ?? process.env.LOFTER_BASE_URL ?? DEFAULT_BASE_URL;
29
+ }
30
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,gBAAgB,GAAG,+BAA+B,CAAC;AACzD,MAAM,6BAA6B,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,QAAQ,CAAC,CAAC;AAOzF,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,iBAAiB,EAAkB,CAAC;AAEtE,SAAS,uBAAuB;IAC9B,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,EAAE,CAAC,YAAY,CAAC,6BAA6B,EAAE,OAAO,CAAC,CAAC;QAC1E,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAA2B,CAAC;QAChE,OAAO,OAAO,CAAC,MAAM,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC;IACtC,MAAM,MAAM,GAAG,GAAG,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,uBAAuB,EAAE,CAAC;IAC3F,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,2EAA2E,CAC5E,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC;IACtC,OAAO,GAAG,EAAE,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,gBAAgB,CAAC;AACzE,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare function httpGet<T = unknown>(path: string, options?: {
2
+ withAuth?: boolean;
3
+ timeout?: number;
4
+ params?: Record<string, unknown>;
5
+ }): Promise<T>;
6
+ //# sourceMappingURL=http-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../src/http-client.ts"],"names":[],"mappings":"AAKA,wBAAsB,OAAO,CAAC,CAAC,GAAG,OAAO,EACvC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IACP,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7B,GACL,OAAO,CAAC,CAAC,CAAC,CAiBZ"}
@@ -0,0 +1,19 @@
1
+ import axios from 'axios';
2
+ import { getSecret, getBaseUrl } from './context';
3
+ const DEFAULT_TIMEOUT = 30_000;
4
+ export async function httpGet(path, options = {}) {
5
+ const { withAuth = false, timeout = DEFAULT_TIMEOUT, params } = options;
6
+ const config = {
7
+ method: 'GET',
8
+ url: `${getBaseUrl()}${path}`,
9
+ timeout,
10
+ params,
11
+ headers: {},
12
+ };
13
+ if (withAuth) {
14
+ config.headers['lofter-micro-secret'] = getSecret();
15
+ }
16
+ const response = await axios.request(config);
17
+ return response.data;
18
+ }
19
+ //# sourceMappingURL=http-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-client.js","sourceRoot":"","sources":["../src/http-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAkC,MAAM,OAAO,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAElD,MAAM,eAAe,GAAG,MAAM,CAAC;AAE/B,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,IAAY,EACZ,UAII,EAAE;IAEN,MAAM,EAAE,QAAQ,GAAG,KAAK,EAAE,OAAO,GAAG,eAAe,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAExE,MAAM,MAAM,GAAuB;QACjC,MAAM,EAAE,KAAK;QACb,GAAG,EAAE,GAAG,UAAU,EAAE,GAAG,IAAI,EAAE;QAC7B,OAAO;QACP,MAAM;QACN,OAAO,EAAE,EAA4B;KACtC,CAAC;IAEF,IAAI,QAAQ,EAAE,CAAC;QACZ,MAAM,CAAC,OAAkC,CAAC,qBAAqB,CAAC,GAAG,SAAS,EAAE,CAAC;IAClF,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,CAAI,MAAM,CAAC,CAAC;IAChD,OAAO,QAAQ,CAAC,IAAI,CAAC;AACvB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { IncomingMessage, ServerResponse } from 'node:http';
3
+ export declare function createMcpServer(): McpServer;
4
+ export declare function createMcpRequestHandler(options?: {
5
+ baseUrl?: string;
6
+ }): Promise<(req: IncomingMessage, res: ServerResponse) => Promise<void>>;
7
+ //# sourceMappingURL=server.d.ts.map
@@ -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;AAEpE,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAOjE,wBAAgB,eAAe,IAAI,SAAS,CAyF3C;AAED,wBAAsB,uBAAuB,CAAC,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CACpF,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAC7D,CAQA"}
package/dist/server.js ADDED
@@ -0,0 +1,90 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
3
+ import { z } from 'zod';
4
+ import { requestContext } from './context';
5
+ import { checkConnection } from './tools/check-connection';
6
+ import { listMicroApps } from './tools/list-micro-apps';
7
+ import { commonRequest } from './tools/common/request';
8
+ export function createMcpServer() {
9
+ const server = new McpServer({
10
+ name: 'lofter-admin-mcp',
11
+ version: '0.1.0',
12
+ });
13
+ // Tool: check_connection
14
+ server.tool('check_connection', '测试与 LOFTER 管理后台的连接是否正常', {}, async () => {
15
+ const result = await checkConnection();
16
+ return {
17
+ content: [
18
+ {
19
+ type: 'text',
20
+ text: JSON.stringify(result, null, 2),
21
+ },
22
+ ],
23
+ };
24
+ });
25
+ // Tool: list_micro_apps
26
+ server.tool('list_micro_apps', '获取 LOFTER 管理后台所有子应用的配置信息,包括主应用信息和子应用列表', {}, async () => {
27
+ try {
28
+ const result = await listMicroApps();
29
+ return {
30
+ content: [
31
+ {
32
+ type: 'text',
33
+ text: JSON.stringify(result, null, 2),
34
+ },
35
+ ],
36
+ };
37
+ }
38
+ catch (err) {
39
+ const message = err instanceof Error ? err.message : String(err);
40
+ return {
41
+ content: [
42
+ {
43
+ type: 'text',
44
+ text: `错误: ${message}`,
45
+ },
46
+ ],
47
+ isError: true,
48
+ };
49
+ }
50
+ });
51
+ // Tool: common_request
52
+ server.tool('common_request', '通用 HTTP 请求工具,转发至 LOFTER 管理后台接口,支持 GET/POST/PUT/DELETE 及多种请求体格式', {
53
+ functionDomain: z.enum(['api', 'platform']).optional().describe('功能域:api = 业务数据(/api/*),platform = 应用管理(/platform/*),默认 api'),
54
+ functionName: z.string().describe('功能路径,拼接到域前缀后,例如 "content-manager/resource/post/search" 或 "application/list"'),
55
+ method: z.enum(['get', 'post', 'put', 'delete']).describe('请求方法'),
56
+ headers: z.record(z.string()).optional().describe('自定义请求头,JSON 格式'),
57
+ timeout: z.number().optional().describe('超时时间(毫秒),默认 10000'),
58
+ responseType: z.enum(['json', 'text', 'buffer']).optional().describe('响应类型,默认 json'),
59
+ query: z.record(z.unknown()).optional().describe('Query 参数,拼接到 URL'),
60
+ formData: z.record(z.unknown()).optional().describe('表单数据,自动设置 Content-Type: application/x-www-form-urlencoded'),
61
+ json: z.record(z.unknown()).optional().describe('JSON 请求体,自动设置 Content-Type: application/json'),
62
+ text: z.string().optional().describe('文本请求体'),
63
+ buffer: z.string().optional().describe('二进制请求体,Base64 编码字符串'),
64
+ }, async (params) => {
65
+ try {
66
+ const result = await commonRequest(params);
67
+ return {
68
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
69
+ };
70
+ }
71
+ catch (err) {
72
+ const message = err instanceof Error ? err.message : String(err);
73
+ return {
74
+ content: [{ type: 'text', text: `错误: ${message}` }],
75
+ isError: true,
76
+ };
77
+ }
78
+ });
79
+ return server;
80
+ }
81
+ export async function createMcpRequestHandler(options) {
82
+ return async (req, res) => {
83
+ const secret = req.headers['lofter-micro-secret'];
84
+ const server = createMcpServer();
85
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
86
+ await server.connect(transport);
87
+ return requestContext.run({ secret, baseUrl: options?.baseUrl }, () => transport.handleRequest(req, res));
88
+ };
89
+ }
90
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export type CheckConnectionResult = {
2
+ connected: boolean;
3
+ message: string;
4
+ };
5
+ export declare function checkConnection(): Promise<CheckConnectionResult>;
6
+ //# sourceMappingURL=check-connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-connection.d.ts","sourceRoot":"","sources":["../../src/tools/check-connection.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAsB,eAAe,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAgBtE"}
@@ -0,0 +1,19 @@
1
+ import { httpGet } from '../http-client';
2
+ export async function checkConnection() {
3
+ try {
4
+ const data = await httpGet('/health/check', {
5
+ withAuth: false,
6
+ timeout: 5_000,
7
+ });
8
+ const body = typeof data === 'string' ? data.trim() : String(data);
9
+ if (body === '200') {
10
+ return { connected: true, message: '连接正常' };
11
+ }
12
+ return { connected: false, message: `服务返回异常内容: ${body}` };
13
+ }
14
+ catch (err) {
15
+ const message = err instanceof Error ? err.message : String(err);
16
+ return { connected: false, message: `连接失败: ${message}` };
17
+ }
18
+ }
19
+ //# sourceMappingURL=check-connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-connection.js","sourceRoot":"","sources":["../../src/tools/check-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAOzC,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAS,eAAe,EAAE;YAClD,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnE,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;YACnB,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAC9C,CAAC;QACD,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,aAAa,IAAI,EAAE,EAAE,CAAC;IAC5D,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,OAAO,EAAE,EAAE,CAAC;IAC3D,CAAC;AACH,CAAC"}
@@ -0,0 +1,37 @@
1
+ export type RequestMethod = 'get' | 'post' | 'put' | 'delete';
2
+ export type RequestResponseType = 'json' | 'text' | 'buffer';
3
+ export type FunctionDomain = 'api' | 'platform';
4
+ export interface CommonRequestParams {
5
+ /** 功能域:api = 业务数据(/api/*),platform = 应用管理(/platform/*),默认 api */
6
+ functionDomain?: FunctionDomain;
7
+ /** 功能路径,拼接到域前缀后,例如 "content-manager/resource/post/search" */
8
+ functionName: string;
9
+ /** 请求方法 */
10
+ method: RequestMethod;
11
+ /** 自定义请求头 */
12
+ headers?: Record<string, string>;
13
+ /** 超时时间(毫秒),默认 10000 */
14
+ timeout?: number;
15
+ /** 响应类型,默认 json */
16
+ responseType?: RequestResponseType;
17
+ /** Query 参数,拼接到 URL */
18
+ query?: Record<string, unknown>;
19
+ /** 表单数据,Content-Type: application/x-www-form-urlencoded */
20
+ formData?: Record<string, unknown>;
21
+ /** JSON 数据,Content-Type: application/json */
22
+ json?: Record<string, unknown>;
23
+ /** 文本数据 */
24
+ text?: string;
25
+ /** 二进制数据(Base64 字符串,内部转为 Buffer) */
26
+ buffer?: string;
27
+ }
28
+ export interface CommonRequestResult {
29
+ /** HTTP 状态码 */
30
+ status: number;
31
+ /** 响应头 */
32
+ headers: Record<string, string>;
33
+ /** 响应数据:json 类型为对象,text 类型为字符串,buffer 类型为 Base64 字符串 */
34
+ data: unknown;
35
+ }
36
+ export declare function commonRequest(params: CommonRequestParams): Promise<CommonRequestResult>;
37
+ //# sourceMappingURL=request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../../src/tools/common/request.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;AAC9D,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE7D,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,UAAU,CAAC;AAEhD,MAAM,WAAW,mBAAmB;IAClC,iEAAiE;IACjE,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,6DAA6D;IAC7D,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW;IACX,MAAM,EAAE,aAAa,CAAC;IACtB,aAAa;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,wBAAwB;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mBAAmB;IACnB,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,uBAAuB;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,WAAW;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,eAAe;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU;IACV,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,wDAAwD;IACxD,IAAI,EAAE,OAAO,CAAC;CACf;AA2ED,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CAE9B"}
@@ -0,0 +1,58 @@
1
+ import axios from 'axios';
2
+ import { getSecret, getBaseUrl } from '../../context';
3
+ const DEFAULT_TIMEOUT = 10_000;
4
+ async function makeRequest(params) {
5
+ const { functionDomain = 'api', functionName, method, headers = {}, timeout = DEFAULT_TIMEOUT, responseType = 'json', query, formData, json, text, buffer, } = params;
6
+ const url = `${getBaseUrl()}/${functionDomain}/${functionName}`;
7
+ // 构建请求头
8
+ const requestHeaders = {
9
+ 'lofter-micro-secret': getSecret(),
10
+ ...headers,
11
+ };
12
+ // 构建请求体
13
+ let data;
14
+ if (formData !== undefined) {
15
+ requestHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
16
+ data = new URLSearchParams(Object.entries(formData).map(([k, v]) => [k, String(v)])).toString();
17
+ }
18
+ else if (json !== undefined) {
19
+ requestHeaders['Content-Type'] = 'application/json';
20
+ data = json;
21
+ }
22
+ else if (text !== undefined) {
23
+ data = text;
24
+ }
25
+ else if (buffer !== undefined) {
26
+ data = Buffer.from(buffer, 'base64');
27
+ }
28
+ // axios responseType 映射
29
+ const axiosResponseType = responseType === 'buffer' ? 'arraybuffer' : responseType;
30
+ const config = {
31
+ method: method.toUpperCase(),
32
+ url,
33
+ timeout,
34
+ params: query,
35
+ headers: requestHeaders,
36
+ data,
37
+ responseType: axiosResponseType,
38
+ };
39
+ const response = await axios.request(config);
40
+ // 处理响应数据
41
+ let responseData;
42
+ if (responseType === 'buffer') {
43
+ // arraybuffer 转 Base64 字符串返回
44
+ responseData = Buffer.from(response.data).toString('base64');
45
+ }
46
+ else {
47
+ responseData = response.data;
48
+ }
49
+ return {
50
+ status: response.status,
51
+ headers: response.headers,
52
+ data: responseData,
53
+ };
54
+ }
55
+ export async function commonRequest(params) {
56
+ return makeRequest(params);
57
+ }
58
+ //# sourceMappingURL=request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../../../src/tools/common/request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAqD,MAAM,OAAO,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACtD,MAAM,eAAe,GAAG,MAAM,CAAC;AAyC/B,KAAK,UAAU,WAAW,CACxB,MAA2B;IAE3B,MAAM,EACJ,cAAc,GAAG,KAAK,EACtB,YAAY,EACZ,MAAM,EACN,OAAO,GAAG,EAAE,EACZ,OAAO,GAAG,eAAe,EACzB,YAAY,GAAG,MAAM,EACrB,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,IAAI,EACJ,MAAM,GACP,GAAG,MAAM,CAAC;IAEX,MAAM,GAAG,GAAG,GAAG,UAAU,EAAE,IAAI,cAAc,IAAI,YAAY,EAAE,CAAC;IAEhE,QAAQ;IACR,MAAM,cAAc,GAA2B;QAC7C,qBAAqB,EAAE,SAAS,EAAE;QAClC,GAAG,OAAO;KACX,CAAC;IAEF,QAAQ;IACR,IAAI,IAAa,CAAC;IAClB,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,cAAc,CAAC,cAAc,CAAC,GAAG,mCAAmC,CAAC;QACrE,IAAI,GAAG,IAAI,eAAe,CACxB,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CACzD,CAAC,QAAQ,EAAE,CAAC;IACf,CAAC;SAAM,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,cAAc,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QACpD,IAAI,GAAG,IAAI,CAAC;IACd,CAAC;SAAM,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,GAAG,IAAI,CAAC;IACd,CAAC;SAAM,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAChC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACvC,CAAC;IAED,wBAAwB;IACxB,MAAM,iBAAiB,GACrB,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC;IAE3D,MAAM,MAAM,GAAuB;QACjC,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE;QAC5B,GAAG;QACH,OAAO;QACP,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,cAAc;QACvB,IAAI;QACJ,YAAY,EAAE,iBAAiB;KAChC,CAAC;IAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAE7C,SAAS;IACT,IAAI,YAAqB,CAAC;IAC1B,IAAI,YAAY,KAAK,QAAQ,EAAE,CAAC;QAC9B,6BAA6B;QAC7B,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAmB,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC9E,CAAC;SAAM,CAAC;QACN,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC;IAC/B,CAAC;IAED,OAAO;QACL,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,OAAO,EAAE,QAAQ,CAAC,OAAiC;QACnD,IAAI,EAAE,YAAY;KACnB,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAA2B;IAE3B,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,30 @@
1
+ export interface MainApp {
2
+ id: number;
3
+ appName: string;
4
+ remark: string;
5
+ type: number;
6
+ features: {
7
+ transpond: boolean;
8
+ auth: boolean;
9
+ };
10
+ configs: Record<string, unknown>;
11
+ }
12
+ export interface MicroApp {
13
+ id: number;
14
+ appName: string;
15
+ remark: string;
16
+ configs: {
17
+ category?: string;
18
+ icon?: string;
19
+ };
20
+ version: string;
21
+ /** JSON 字符串,包含 htmlRoot、htmlRemote、entry、requestRewrite */
22
+ resource: string;
23
+ activeRule: string;
24
+ }
25
+ export interface ListMicroAppsResult {
26
+ mainApp: MainApp;
27
+ apps: MicroApp[];
28
+ }
29
+ export declare function listMicroApps(): Promise<ListMicroAppsResult>;
30
+ //# sourceMappingURL=list-micro-apps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-micro-apps.d.ts","sourceRoot":"","sources":["../../src/tools/list-micro-apps.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE;QACR,SAAS,EAAE,OAAO,CAAC;QACnB,IAAI,EAAE,OAAO,CAAC;KACf,CAAC;IACF,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE;QACP,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,QAAQ,EAAE,CAAC;CAClB;AAQD,wBAAsB,aAAa,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAWlE"}
@@ -0,0 +1,12 @@
1
+ import { httpGet } from '../http-client';
2
+ export async function listMicroApps() {
3
+ const resp = await httpGet('/micro/config', {
4
+ withAuth: true,
5
+ timeout: 10_000,
6
+ });
7
+ if (resp.code !== 0) {
8
+ throw new Error(`接口返回错误: ${resp.message ?? resp.code}`);
9
+ }
10
+ return resp.data;
11
+ }
12
+ //# sourceMappingURL=list-micro-apps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-micro-apps.js","sourceRoot":"","sources":["../../src/tools/list-micro-apps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAuCzC,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAsB,eAAe,EAAE;QAC/D,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE,MAAM;KAChB,CAAC,CAAC;IAEH,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO,IAAI,CAAC,IAAI,CAAC;AACnB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@lofter-admin/mcp-backend",
3
+ "version": "0.1.0",
4
+ "description": "LOFTER Admin MCP Server",
5
+ "type": "module",
6
+ "bin": {
7
+ "lofter-mcp": "./dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "exports": {
14
+ ".": "./dist/cli.js",
15
+ "./server": "./dist/server.js"
16
+ },
17
+ "publishConfig": {
18
+ "access": "public",
19
+ "registry": "https://registry.npmjs.org"
20
+ },
21
+ "dependencies": {
22
+ "@modelcontextprotocol/sdk": "^1.10.2",
23
+ "axios": "^1.7.0",
24
+ "commander": "^12.0.0",
25
+ "zod": "^3.22.0"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^20.0.0",
29
+ "typescript": "^5.4.0",
30
+ "rimraf": "^3.0.2"
31
+ },
32
+ "engines": {
33
+ "node": ">=20"
34
+ },
35
+ "scripts": {
36
+ "clean": "rimraf dist",
37
+ "dev": "tsc --watch",
38
+ "build": "npm run clean && tsc",
39
+ "start": "node dist/cli.js start"
40
+ }
41
+ }