lxr-data-mcp 0.0.3 → 0.0.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/package.json +1 -1
- package/server.js +1 -1
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const{FastMCP:FastMCP}=require("fastmcp"),{z:z}=require("zod"),{readFileSync:readFileSync,existsSync:existsSync}=require("fs"),{resolve:resolve,join:join,dirname:dirname,relative:relative}=require("path"),{glob:glob}=require("glob");let docsData=null,docPathMap=new Map,useEmbeddedDocs=!1;function initDocsData(){const
|
|
2
|
+
const{FastMCP:FastMCP}=require("fastmcp"),{z:z}=require("zod"),{readFileSync:readFileSync,existsSync:existsSync}=require("fs"),{resolve:resolve,join:join,dirname:dirname,relative:relative}=require("path"),{glob:glob}=require("glob");let docsData=null,docPathMap=new Map,useEmbeddedDocs=!1;function initDocsData(){const n=resolve(dirname(__filename), "docs-data.js");if(existsSync(n))try{return docsData=require(n),useEmbeddedDocs=!0,docPathMap=new Map(Object.entries(docsData)),void console.log(`已加载内嵌文档数据,共 ${docPathMap.size} 个文档`)}catch(n){console.warn("加载内嵌文档数据失败,回退到文件系统模式:",n.message)}const e=resolve(dirname(__filename), "docs"),o=[{pattern:"references/cn_company/**/*.md",prefix:"lxr-doc://cn_company/"},{pattern:"references/cn_indexes/**/*.md",prefix:"lxr-doc://cn_indexes/"},{pattern:"experiences/**/*.md",prefix:"lxr-doc://experiences/"}];for(const{pattern:n,prefix:c}of o){const o=glob.sync(n,{cwd:e,absolute:!0});for(const n of o){const o=relative(e,n).replace(/^references\/(cn_company|cn_indexes)\//,"").replace(/^experiences\//,"").replace(/\.md$/,"").replace(/\\/g,"/");docPathMap.set(c+o,n)}}const c=join(e,"SKILL.md");existsSync(c)&&docPathMap.set("lxr-doc://skill",c),console.log(`文档映射表已构建(文件系统模式),共 ${docPathMap.size} 个文档`)}function loadDoc(n){if(useEmbeddedDocs){const e=docsData[n];if(e)return e}else{const e=docPathMap.get(n);if(e&&existsSync(e))return readFileSync(e,"utf8")}const e=n.split("/").pop().toLowerCase(),o=[...docPathMap.keys()].filter(n=>n.split("/").pop().toLowerCase().includes(e)).slice(0,5),c=[...docPathMap.keys()].filter(n=>n.split("/").pop().toLowerCase()===e&&n.includes("/")).slice(0,3);let t="";throw t=c.length>0?`\n\n检测到多个同名文件,请使用完整路径:\n${c.map(n=>` - ${n}`).join("\n")}`:o.length>0?`\n\n可能的匹配项:\n${o.map(n=>` - ${n}`).join("\n")}`:"\n\n提示:使用 list_lxr_docs 查看所有可用文档",new Error(`文档不存在: ${n}${t}`)}async function fetchData(n,e={}){const o=process.env.LIXINGER_TOKEN;if(!o)throw new Error("未找到 LIXINGER_TOKEN 环境变量");const c=await globalThis.fetch("https://open.lixinger.com"+n,{method:"POST",headers:{"Content-Type":"application/json","Accept-Encoding":"gzip"},body:JSON.stringify({...e,token:o})});if(!c.ok)throw new Error(`请求失败: ${c.status} ${await c.text()}`);return c.json()}function log(n,e,o,c){const t=`[${(new Date).toISOString()}] [${n}]`;void 0!==c?console.log(`${t} [${e}]`,o,"string"==typeof c?c:JSON.stringify(c,null,2)):console.log(`${t} [${e}]`,o)}const server=new FastMCP({name:"lxr-data-mcp",version:"1.0.0",instructions:'理杏仁数据获取 MCP 工具。\n\n支持从理杏仁开放平台获取金融数据,包括:\n- 大陆市场的公司/指数/行业/基金数据\n- 香港市场的公司/指数/行业数据\n- 美国指数数据\n- 宏观经济数据\n\n【重要】调用 API 前必须先阅读文档!禁止编造接口路径和参数格式。\n\n标准工作流程:\n1. 【必须】加载索引文档,了解可用接口:\n - 公司数据:load_lxr_doc("lxr-doc://cn_company/INDEX")\n - 指数数据:load_lxr_doc("lxr-doc://cn_indexes/INDEX")\n\n2. 【必须】加载经验文档,了解参数格式和最佳实践:\n - load_lxr_doc("lxr-doc://experiences/INDEX")\n\n3. 【按需】加载具体接口文档,了解详细参数:\n - 根据索引文档中的链接,加载对应的接口文档\n\n4. 【最后】调用 fetch_lixinger_data 获取数据\n\n工具说明:\n- list_lxr_docs: 发现可用文档(不确定有哪些文档时使用)\n- load_lxr_doc: 加载文档内容(调用 API 前必须使用)\n- fetch_lixinger_data: 获取数据(必须在阅读文档后使用)'});server.addTool({name:"load_lxr_doc",description:"加载指定 URI 的文档内容。\n\n常用文档 URI:\n- lxr-doc://cn_company/INDEX - 公司数据接口索引\n- lxr-doc://cn_indexes/INDEX - 指数数据接口索引\n- lxr-doc://experiences/INDEX - 调用经验和参数格式规范\n\n具体接口文档(根据索引中的链接加载):\n- lxr-doc://cn_company/company - 公司基础信息\n- lxr-doc://cn_company/candlestick - K线数据\n- lxr-doc://cn_company/fundamental/non_financial - 基本面数据\n- lxr-doc://cn_company/fs/non_financial - 财务报表\n\n如果不知道有哪些文档,请先使用 list_lxr_docs 查看。",parameters:z.object({uri:z.string().describe("文档 URI,如 lxr-doc://cn_company/INDEX")}),execute:async({uri:n})=>{log("load_lxr_doc","入参","uri:",n);try{const e=loadDoc(n);return log("load_lxr_doc","出参",`文档长度: ${e.length} 字符`),e}catch(n){throw log("load_lxr_doc","错误",n.message),n}}}),server.addTool({name:"list_lxr_docs",description:"列出可用的文档。\n\n返回指定类别下所有文档的 URI 列表。获取列表后,使用 load_lxr_doc 加载具体文档。\n\n类别:cn_company(公司数据)| cn_indexes(指数数据)| experiences(调用经验)| all(全部)",parameters:z.object({category:z.enum(["cn_company","cn_indexes","experiences","all"]).optional().default("all")}),execute:async({category:n})=>{log("list_lxr_docs","入参","category:",n);const e=[...docPathMap.keys()].filter(e=>"all"===n||e.includes(`://${n}/`)||"lxr-doc://skill"===e).map(n=>({uri:n,name:n.split("/").pop(),category:n.split("://")[1]?.split("/")[0]||"skill"})).sort((n,e)=>n.category.localeCompare(e.category)||n.name.localeCompare(e.name));return log("list_lxr_docs","出参",`找到 ${e.length} 个文档`),JSON.stringify({category:n,count:e.length,docs:e},null,2)}}),server.addTool({name:"fetch_lixinger_data",description:'\n调用理杏仁 API 获取金融数据。严格限制:仅使用接口文档中明确列出的字段和格式。\n\n\n### 核心约束\n1. **字段验证强制要求**:\n - 调用前必须通过 `load_lxr_doc` 查阅目标接口的完整指标清单\n - 禁止使用文档中未明确列出的任何指标\n - 对每个指标,必须在文档中找到对应的字段名和格式示例\n - 禁止自定义或简化指标格式\n - 根据指标类型选择对应接口(估值指标 → 基本面接口;财报衍生指标 → 财务报表接口)\n - 若文档未明确说明指标归属,必须通过 `load_lxr_doc` 查阅多个相关文档确认\n - 禁止编造 `apiPath`,必须使用文档中的路径\n - 禁止编造参数格式,必须按文档要求\n - 禁止使用文档中未明确列出的指标\n - 禁止通过命名惯例或逻辑推断创建指标\n\n### 前置要求\n调用前必须已通过 `load_lxr_doc` 阅读:\n1. 接口索引文档(了解有哪些接口)\n2. 目标接口文档(确认支持的指标列表和格式)\n3. 公共文档(如 `lxr-doc://cn_company/fundamental/COMMON` 或 `lxr-doc://cn_company/fs/COMMON`)\n\n### 关键参数格式\n- **股票代码**:纯数字 6 位,如 `"300750"`(不是 `"300750.SZ"`)\n- **stockCodes** 是数组:`["300750"]`(不是 `"300750"`)\n- **日期**:YYYY-MM-DD 或 `"latest"`\n- **token** 会通过环境变量 `LIXINGER_TOKEN` 自动注入\n\n\n### 使用流程\n1. 查阅文档:使用 `list_lxr_docs` 和 `load_lxr_doc` 了解接口结构\n2. 确认指标:在目标接口文档中找到需要的指标及格式\n3. 构建参数:严格按照文档格式构建请求参数\n4. 执行调用:使用验证后的参数调用接口\n\n\n### 示例用法\n```json\n// 基本面接口示例(仅使用文档中明确列出的指标)\n{\n "apiPath": "/api/cn/company/fundamental/non_financial",\n "params": {\n "stockCodes": ["300750"],\n "date": "latest",\n "metricsList": ["pe_ttm", "pb", "dyr"]\n }\n}\n\n// 财务报表接口示例(使用完整格式)\n{\n "apiPath": "/api/cn/company/fs/non_financial",\n "params": {\n "stockCodes": ["300750"],\n "date": "latest",\n "metricsList": ["q.ps.wroe.t"]\n }\n}\n```\n\n\n### 说明\n该工具用于直接调用理杏仁开放平台的 API 接口,获取公司、指数等金融数据。使用时需严格遵守理杏仁 API 的参数规范和文档要求,确保数据获取的准确性和可靠性。',parameters:z.object({apiPath:z.string().describe("API 路径,必须来自文档,如 /api/cn/company/candlestick"),params:z.object({}).passthrough().optional().default({}).describe("请求参数,必须符合文档格式,token 自动注入")}),execute:async({apiPath:n,params:e={}})=>{delete{...e}.token;try{const o=await fetchData(n,e);return JSON.stringify(o,null,2)}catch(n){throw new Error(`获取数据失败: ${n.message}`)}}}),initDocsData(),server.start({transportType:"stdio"});
|