From ac403b5e6f0173375c2b5072e7f6ed76f23d005c Mon Sep 17 00:00:00 2001 From: yuanzhipeng <2501363769@qq.com> Date: Fri, 26 Dec 2025 19:58:38 +0800 Subject: [PATCH] =?UTF-8?q?feat(core):=20=E6=94=AF=E6=8C=81=E5=A4=9A?= =?UTF-8?q?=E7=A7=8D=E8=AF=B7=E6=B1=82=E4=BD=93=E6=A0=BC=E5=BC=8F=E5=92=8C?= =?UTF-8?q?Content-Type=E8=87=AA=E5=8A=A8=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增FORM、FORMDATA、PATH、PARAMS参数类型支持 - 实现multipart/form-data格式的文件上传功能 - 优化Content-Type自动设置逻辑,避免手动设置boundary问题 - 添加Content-Type不匹配的诊断功能,帮助排查请求错误 feat(config): 使用环境变量配置业务平台URL - 从环境变量LZWCAI_CORP_MANAGER_URL获取基础URL - 移除硬编码的默认URL配置 - 添加URL配置验证和错误提示 chore: 更新版本号至0.2.0 - 版本从0.1.30升级到0.2.0 - 更新包信息和项目配置文件 --- .../PKG-INFO | 2 +- .../.env_lzwcai_mcp_api_converter | 1 + .../__pycache__/business_util.cpython-312.pyc | Bin 24998 -> 25065 bytes .../get_business_api.cpython-312.pyc | Bin 8223 -> 8430 bytes .../src/business/business_util.py | 6 +- .../src/business/get_business_api.py | 12 +- .../api_auth_service.cpython-312.pyc | Bin 42788 -> 42765 bytes .../core/__pycache__/api_base.cpython-312.pyc | Bin 22181 -> 22656 bytes .../__pycache__/core_server.cpython-312.pyc | Bin 35070 -> 41513 bytes .../core/__pycache__/get_auth.cpython-312.pyc | Bin 12095 -> 12348 bytes .../src/core/core_server.py | 187 ++++++++++++++++-- .../src/core/get_auth.py | 14 +- .../__pycache__/logger_config.cpython-312.pyc | Bin 19603 -> 19573 bytes lzwcai_mcp_api_converter/main.py | 5 +- lzwcai_mcp_api_converter/pyproject.toml | 2 +- 15 files changed, 202 insertions(+), 27 deletions(-) create mode 100644 lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/.env_lzwcai_mcp_api_converter diff --git a/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter.egg-info/PKG-INFO b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter.egg-info/PKG-INFO index 5755dba..d5a8bb6 100644 --- a/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter.egg-info/PKG-INFO +++ b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: lzwcai-mcp-api-converter -Version: 0.1.30 +Version: 0.2.0 Summary: 基于FastMCP框架的动态API工具服务器,自动将企业业务API配置转换为MCP协议工具,支持多种传输方式、企业认证和参数验证,为AI助手提供标准化的业务接口访问能力。 Requires-Python: >=3.10 Description-Content-Type: text/markdown diff --git a/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/.env_lzwcai_mcp_api_converter b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/.env_lzwcai_mcp_api_converter new file mode 100644 index 0000000..b31b845 --- /dev/null +++ b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/.env_lzwcai_mcp_api_converter @@ -0,0 +1 @@ +lzwc19781970385781825785858token={"authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMTAwMDAwMDEiLCJsb2dpbl91c2VyX2tleSI6IjJmNmViMWVkYTk3MGRlNzI1OTM1YTczNzY5YWZmODJmZDE3MmFmMGIiLCJhYmJyIjoiXHU3MDc1XHU2Y2ZkXHU0ZTA3XHU1ZGRkIiwiYXVkIjoiIiwiZXhwIjoxNzY3MzQ4OTQxLCJpYXQiOjE3NjY3NDQxNDEsImlzcyI6IiIsImp0aSI6IjUyOTIyNzc0ZTdmZDA3MjZkNGEyY2FkMTgyYzEzNjM4IiwibmJmIjoxNzY2NzQ0MTQxLCJzdWIiOiIifQ.S8cvKtUfojJu0JvA1aPgd6H9y5ccd7XOa7UHMqZzn5w"} diff --git a/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/business/__pycache__/business_util.cpython-312.pyc b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/business/__pycache__/business_util.cpython-312.pyc index b4327935208b39c1236f865f8e67f4bea19df08f..a6a7be83a289b615357f797c61c3e11f990bb4fd 100644 GIT binary patch delta 1677 zcmZXUTTC2P7{_~gyDO=P+kqo;7opf)W-L=@* zf}2uo#kA=uKGaJ!rfs6ppi@nwSReXi@S#b4s3-BEMye)iy*-$k7|(Y`pvHOl%{Sk< z{=f4-bMYGb;Tm!-IUMpj_BclZ`n&4;&WB}moHWW_V@`e~2W=%=ZRKZjMSH|LBu8?G zY|qMyjoh+-R40xnaEw}k5jCw+F{!2xP)kz9YAi+V(Zh$rs@|4}kLwcw#ZE3cs*1&q z;6NX>0^5CieN^oC?d_tLe&2wvFGQ|8YEeD;+flU1mN(sgb3?&&*G(Qd?w{^33Y_zH z%XYR;yp3Eg{2W!0qYs=w&BR)?L&B9TPkM_gr4V#5CX0pq^WCQu# zeY&v&!V*9=pbgLt=mhu~6mvGb2NFTR0J&1~pxg~1;A?WLWXTrbEev6ST(7&&SSsC= zQ@j(BUI1(~poq8-KJoyafOhg)O|c6qpjh#fkm0%q$rm+y&curz)O zFhn+Z>d-K0@w{5t3W+G-ziYCy`omL(0wmw--7?79smxtHs>hkz!!RI1qTZd-^DKPZ zSnysJcEJ_`d@*G`S7S=xv+FPdN<}SLizS}XsQkDZ&1im%6IjVH1!l=bU!8QE_4>y6 z%C{)Epo;7~TG`9owEudvW-oD0SUmxqP_mRn0}IE4eGFjU&x3Z1zi zepLLLb5HYodj?v$yCn^3$^|o5HA9{&;)dqFJWFOn71DXua=8A1oPGpj!DxOeM_$bT( zKEqD~z^8Ph>9{0LBsHAYwG^Hr1^XJL3#{>&F|hAK4w@r>4!5B;;@Mw6H3kFg0bKxo z;Jiri$1?vZ{3656!*G8vco&12Oi#cOV>ky4rNOWa71`QxipHrJO{kH3stML$w>1+> z(!35mss+f8`yI;bFpwAGEJT|DTLAeCcdedSB9e(}E%-xHI?^pWQQ5ES7=EcTG9&fk EKhji}EdT%j delta 1543 zcmZXUQB0dv6vw%I{Yv?0D?=?>TUV%nUnp!fqZ^|I2WttibYs;y0+m(xXiA`MPU+Yn z8OVli&TYoCMWbff9<~>gIsFo|2=L#q5&fXjSI&I+wXM!_)T&nCm-hFo z?Nf8JW2sC}E<2G)$5k6v+aJI$94?fc0p`W6PLF=bdERc_$^*g{{KNejYs60;ImLn) z_jK3{Yd!_3=>nehJTC9yYqI)^XPB`8z4pU26~Iv4D{Y-*))ATr9fUBUixA;Z zWf&m4kI+l#$3N>fE4#=fe1(tI-?GNUjQ)FlpxnEi>^1`3FRog^O@mE@F6?aZwV11E zEMimGf`!A!fAA3bcdz5GGCbYz66@7njYXLS@a5*FniiUDCC>!3q+`hP)^(COoUVp7>Q# zDICxA4*oGLMNnB%*K(08v+z$PV)PG zMF+QhYv-13U%39m%89$HZ-`1jh`Zq#LIU@PTiH|ieE2Ol9SR!Zzi0AVZIAd_42L6; z+Bly#6wy|rne0RkMrcD4--&c>n&akG#wEVVu~_z-8!r#J^and%mAdIladEg>A=H_Y z#A(k_1WKJ@1#LQaSTmFZsi~qC1;~L~ZVH~qJG)xt1-|NA-P64))lgP>eM*-;hCfAp z4qCx;zQ{i?kF~LO`6M^*=}*VrllCuhGp;J;CQ+C&(CFIo1Dlb6K8K|+FKU2rM08v*}SSDPvG9PKI+ z4in-yyEi4D=VrV9&EBk3vyGeoq+grSpa3yUCh7-77K9*CI(q76Z1H?g(@=D~tWi#d zVBTdx@(V?x!9|~*!Os&-^6PwNT)&t2$g)sUB)VKk61`)v)*|)t-PMzDfPeq5bE9iP z+(5i2dr7C^oX!*q{OygJ|57F0hLVO{F+ZAuv|4~!x+e7= za0Ew&w#gUy%}H<7YcwUKp26Ovmt8|G*{3MX|4SLi&-MC^WYF?A DIRs?n diff --git a/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/business/__pycache__/get_business_api.cpython-312.pyc b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/business/__pycache__/get_business_api.cpython-312.pyc index 0aa2a10914e854b3ac8324f0c367c8da1149b63b..f5849edd4a65f44f06bbd3bc676afd0c2e0a9383 100644 GIT binary patch delta 1437 zcmZ`(YfM{J96#qi%I$6Mr7!vb+5%;;Do`0B&^V-F%TN(F7Pf5D)yl118I z|KIQbIQMa~?bm>CO%Qm1*JQ9Odh;{kTS$#D=o>=?VyPO`NV(BJc5OwHQJI8JVF{5e ziJ68uYYa+kl2sX$Hd@eSBfnl{M{p#mkqT7$7&t*moTQcb<8-n>3lt$I0-#SshbVKA zMlDc*r29la&Ez6UPUTRW$%1Am9nJNb(KV{1P^&B#13=X(7WAXRStzVFplbVUC_tGV zV&0eJRbDFG-vof{$wwOul5v_^uCjI%lgb|hl3CT^Zr6;J*EuW^u%gqi=|qpdq>G>( z35=^UUl;;ae~4D~5g3CJkkl*om4Th4#af%<&U;jSUWLosPtf@t$*~=HfTBnHnNoD0 zv2c|@wnBV*=xe4_lpJ~H7+mHwu{L&vfX=Z3%xMw!JCj0ASS6LH0(l~<$$MulOh znDR~aJ%rw6J=LYF9Hk~7!(CM4WUC|j$Vh@ysn0W10sYH5jLTVv!OQe6Fbut}lvi_< zel=t3+`Y3s@MN(4+3u~uX98V;Et|W8J>8u{qd39*J7=>Wyq29fa`%mivi~8L{e1l6 zyRW}JKXc}{FW$Oy_S~H_m#}^N{L$>;sqE*e?8K+plNaWv4&t1caQo7m*;8ZwvVDpY z_xpVA{*mF{P_$MG55#MG2jzI}KqwZ9gk^v8Bh8PEtV2I&t0?G8dH5O%I?z`A!vz{S zp~%rV8qs^83B9j>4~nQ!C@WcokNzZ?D^I17Fz!Pjs}> zSFH9eb@Y`wFSciG6&)?~Yzy1bnmTQGg@RnF(tIDH0#QKKMWgtQ{!6ffhr7MjqFD=)@lby zlF{@I#$Jj>5*ob89f{nG{?o`~;pNfPV`hL4IXR)p7CPoldq3?@uAK>$;A7 z3vvko7y8B3+DM9J=x9t?%DJW(k$eQ?PC{+Qu*f|=FenZ6hg;wOgZ41tIzHo1ev7n%7%Q7@gT4XT3jm+>L)WiS7KA;5Kncn#9IXw`nUiRV$=6qEc0<#_E{Vu4`MH zjRIx2YAPhu6Ne8WM5!S3QWOqUK0ted3ei^d;0jKbxB#ajsz^vk0`jt^!83vdY`aBCg;?e2`@Lzvz)tPA}S5_V^<;AhyG zXV{VdVTZ(!7~ zwLWXYW!Nh0^B93`y%qEZe+sSOM1Q^L`H=6sm_27%wamoCmvLfzJrtXn~_b7ef1l?54ld0cJxa;2h*|aEB{tC*)6pM%-yUSV=zu3A(|4a zy$tA&ip;|drYS73zr1zn<_vrti;73-`={`8?4c51oL&a~a66uzVTU#OV#R7{b{xh= zK11daMrBbprI#;M$#S`nHBZ3UKze$Cruc;1+_P5PK&<|1VQamAsB!$&#>rES(`Ond Y&pbMwdvxrzKU$29GJG&_LST~r0E2NJ=Kufz diff --git a/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/business/business_util.py b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/business/business_util.py index a8d6629..a00afd0 100644 --- a/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/business/business_util.py +++ b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/business/business_util.py @@ -76,7 +76,11 @@ class RequestType: """ HEADER = "header" # 请求头参数 QUERY = "query" # 查询参数(URL参数) - BODY = "body" # 请求体参数 + BODY = "body" # 请求体参数(JSON格式) + FORM = "form" # 表单参数(application/x-www-form-urlencoded) + FORMDATA = "formdata" # 多部分表单参数(multipart/form-data,支持文件上传) + PATH = "path" # 路径参数 + PARAMS = "params" # 路径参数(别名) LZWCAI_CONFIG = "lzwcaiConfig" # lzwcaiConfig参数(新的用户ID存储位置) diff --git a/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/business/get_business_api.py b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/business/get_business_api.py index 04d9213..71f596b 100644 --- a/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/business/get_business_api.py +++ b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/business/get_business_api.py @@ -47,10 +47,14 @@ def get_business_api_details(api_ids: List[int], auth_token: str = None) -> List token = auth_token or default_token # 接口URL - 支持环境变量配置 - # 默认URL - default_url = "http://lzwcai-demp-corp-manager:8086/system/mcpServer/bizSys/api/getByIds" - # 从环境变量获取URL,如果没有设置则使用默认URL - url = os.getenv("lzwcai_mcp_dyntoolapi_auth_url", default_url) + # 从环境变量获取基础URL,必须配置 + base_url = os.getenv("LZWCAI_CORP_MANAGER_URL",'http://lzwcai-demp-corp-manager:8086') + if not base_url: + raise ValueError("环境变量 LZWCAI_CORP_MANAGER_URL 未配置,请设置业务平台基础URL,例如: http://lzwcai-demp-corp-manager:8086") + + # API路径 + api_path = "/system/mcpServer/bizSys/api/getByIds" + url = base_url.rstrip("/") + api_path # 请求头 headers = { diff --git a/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/core/__pycache__/api_auth_service.cpython-312.pyc b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/core/__pycache__/api_auth_service.cpython-312.pyc index dadea0497c2124f08fa1a9be055bce6c6331615f..6ee8b9d1c4a8426b0d1e1b47974ff2a2d037d54b 100644 GIT binary patch delta 64 zcmZ2-j;Z$?6YptWUM>b8F!bihEZoQ&!OGY=IgQnJ^Iq1?Qf#t%sd;7blP|DJZ0=V0 TFUDSYS)u5%K=I~@=9BXQfm0Rg delta 92 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 diff --git a/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/core/__pycache__/core_server.cpython-312.pyc b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/core/__pycache__/core_server.cpython-312.pyc index 8c97464abe2a30beaa4f975132c422e258c6e034..9362451e70ec5acaf306dc7f269d573e214e2a7c 100644 GIT binary patch delta 8498 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 delta 2910 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 diff --git a/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/core/__pycache__/get_auth.cpython-312.pyc b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/core/__pycache__/get_auth.cpython-312.pyc index 444a991d545515bddfb566acc3f4baf934fa071a..cded94361504df0e3520d1d7d2ad9b1f9dcfd846 100644 GIT binary patch delta 2191 zcmbtVYfMvD9KWac-nR5bp#m+Xw7i7!G#V2SMUmiR)1vs;M6?v{1=^LCo!hd(P}$}j zF;ieCQ8%2#TdF5N${>?kvchVqFvH0EozAd>VE9yH1)uxC+{ zy$70rhWMw)tPx(9xK#*38jTh9A zzV`T&?Ge+D+$qsh}(HPo;7HZ(PF@@}m6)UR!9_HJw5;JSbKQexzY&NcUWvEbnmnU{q|xIE;x`-Gug#3H1u1*7Q{MLIgw1tcfu!i0kcBS+=A7WBu{GWp}b_ zhu8gTu}@{X;(7Q^rW7|zzgS(dy_y-7J{uO{TnbJS)>R@qIicH#7Lz}9=Xgd1{U#Io ztc-C+fOZ0^236zCXZ#*Eg&8Dixfx{4mPw}cX-G}mVC3H@0Z(b5ovgW5A7Ar& zym3d|)IyBrJDyswJ5s*7j=9dJu4b9*mMY*k*rls$*&Ee}@@0tfT9)!UN5cyCrrp|5 z&EBl$fy>7E(7b{#;)_+LwR4_D;1e$fkojQPCrFrPDs%o&XISP1u|3*_>3xz}e<&ie zK{14j=n7g2yad3l#Yz=wsGyeomeJr)^47w9?zp>>nHPX$piIepOJ*ht#R8c>bEuZA zvfI?l>FPS-wddBoO6dj)8v$hA*X!TjBy$~MLBv&5!uCjAQCilS;nBpwlWrE%BH~RH zYRP4LC0a!Ouos1uIG^-wKF)QD8Gvk#LXW(@Xr$ZQ5e{|wyKo~Fr^1F_e?;u@3Sz|P zhlYm4x0He<8~WeG3Srqmg_0P_v7(P-=W>QsCA6k^B?YBSs$`kAgFu1{0kuh1Wix4Y zy3rMKz`0Gog<8`f@YdLja~Z4OMfq+DZKSLq-`PTG2Y_sjbohFG?f#%Y(qB(2Nc5wW55b zv0Le$VE~B*fR-lX?xf3;NxO=z=xW1d181$qN8 zJq(x@4sWJFgTd70X$Dg#*iNh^4ugf)e{Oliz%W}Ht1oFq*@}|>-_5-?lquKLMJ~Bp z(POfp^z5qt`%uSzFU>|q8mya}f7MdToi2vg!h8XMC!}Skc0G;_n~$& zU7qKR(zPz!4+GAipmc?m`X!tOQ&LdjK_bBwEB5tKt#V40tOn?m`ou#3vL+br>hg!W zWNy7@O%qlQ?<%V8qM%&DcPLBN4A^7s@}95|4T^Pm45sv7RLTLIWf4LT)hyy3=@?|b z%joYiy1PsYsjO_N>t#{?Lq@@FO*- P^xtj{kRL01I2ype<&`tO delta 1858 zcma)6U2GIp6rQ^?v%Br=>~2~5vt_qTOAEVnyK9T3wF0J8ksoR69}z5+ZD+ddvOneS zF11Y&OqxJ2LCi@+1%DoRA*czii9|3`c+i9gH!;3UVho8Q#1{xY>4Tm#+ZvFNILUnT z-E+@9_nhzCxtI5Tv&Vg1RjUPj-WhNuUkP1sZx(B9hBRT*#W{JGtV@QZ%lO*iEwR?) zn3Hx@J!pd?qO`4AuQD8l({N7M{1{8O>(vvIuAsM~yB?e|?Dvn!8$DI@bm?wupL^m{ z%noa$Fyd}$8LNhAsT0min*0SipMu?fzjmG>#b;I+U0{MxkSjJpNZH|eIjBmD<5?l) zgn5?_&Z<6JN`)KJ(gt1Bv9L4ZVk9O^+ry6H9DFao8o7Px-CLiX{pI3U+P*?zvM(0v ze4;Dbxwbdj742HHwy*cG-rj0$;ei|WA#pJ5fUS-pb_(8itcR~1N0sMr^CQ9?x8QFs z<()TeN*x+_Bf&Dzl_;wRP`0xu+)zFV+`=MG2|Al)rv=?MYdbA`ARQ8`u)LH&i8B#~ ztCqrTR}GUedmp$}7js~djqVqPg4I5%)hf*=J7=X7x~WScr^?KYtQL-|Ud2v6^q?9s zcde(5J-pVAYiUKS%3JXjgtt%DLZjQaq*dT8hvfh6R+@0vv&Qa6>IAQQf=q=oo;O5= z`=HI+QPYBv`3eM6Dj2U7cnyquTOYqqADR-2JZ(C;k)OwlD35A+B8_Fh`u2YUle8=fV)nP3ZoDaR+1JBCbYJf|DH zoh)J|pD2=wH;jwd z-oki%GCr0}Ckxa4q)%Ap5y-TzYN~WnFJ>n5VOM3-`{Kgqt)~_1)D2>onP#gQnKl>u zwlve`W6WPVv2uiIRzrtrxBUot5dm9S>Eaj;tYWp0pZx)Sp{>ny{hebo{zv~lZ-%;A z1I&kf?W@q2HxbZr;2Obp0!oNe@N)vEfV>|54h3BvS^3y^Pr%QemQpA@!Wykfx-Ay& zAz}(a06ttj!hV9kR-a$-KkmNL&Gts7Qa)j6@k&!WmitK_Q%x9!%F&=-h0ma^8lLHB zXMQ-)aggnSTOG|!2WYLvi}>L+1eW+>emZa3a(M_v2U#4ZqU&`|lh#SJ@;*pI3dv^? zOh-DGNF=ifQ`$1PX^2|~bREr(6Iegm6tRjb$o_P$%jEQ8+StHP;E>^)Z-s_f(Ulfi r%U!|3z3^VFDUdu+<_F7DGv(RC<*CDW(UJAr@RMR2ZE!u7ktpat1J|*) diff --git a/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/core/core_server.py b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/core/core_server.py index 3e8b538..176a9e3 100644 --- a/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/core/core_server.py +++ b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/core/core_server.py @@ -574,11 +574,17 @@ class ApiClient: full_url = RequestBuilder.build_url_with_path_params(full_url, path_params) # 根据请求体内容设置Content-Type (如果未被显式设置) + # 注意:httpx 会自动处理某些 Content-Type,但显式设置可以避免歧义 if "content-type" not in headers: if json_data is not None: - headers["Content-Type"] = "application/json" - # httpx会自动为form_data设置'application/x-www-form-urlencoded' - # httpx会自动为formdata_data设置'multipart/form-data'并添加boundary + headers["content-type"] = "application/json" + elif form_data is not None: + # application/x-www-form-urlencoded 类型 + headers["content-type"] = "application/x-www-form-urlencoded" + elif formdata_data is not None: + # multipart/form-data 类型 - 不设置 Content-Type,让 httpx 自动添加 boundary + # 如果手动设置会缺少 boundary 参数导致请求失败 + pass # 发送请求 logger.info(f"发送HTTP请求: {method} {full_url}") @@ -665,15 +671,156 @@ class ApiClient: def _contains_file(self, data: Dict[str, Any]) -> bool: - """检查数据字典中是否包含文件类对象""" + """ + 检查数据字典中是否包含文件类对象 + + 支持的文件类型: + - bytes: 字节数据 + - 具有 read 属性的对象(文件句柄) + - tuple: (filename, content) 或 (filename, content, content_type) 格式 + """ if not data: return False for value in data.values(): - # 检查是否为字节流或具有read属性的对象(文件句柄) - if isinstance(value, bytes) or hasattr(value, 'read'): + # 检查是否为字节流 + if isinstance(value, bytes): + return True + # 检查是否为文件句柄(具有read属性) + if hasattr(value, 'read'): + return True + # 检查是否为元组格式的文件 (filename, content) 或 (filename, content, content_type) + if isinstance(value, tuple) and len(value) >= 2: return True return False + def _prepare_multipart_data(self, formdata_data: Dict[str, Any]) -> List[Tuple[str, Any]]: + """ + 准备 multipart/form-data 格式的数据 + + 将普通字段和文件字段统一转换为 httpx 可接受的格式 + + httpx 的 files 参数支持两种格式: + 1. Dict[str, tuple] - 每个字段只有一个值 + 2. List[Tuple[str, tuple]] - 支持同名多值参数(如 status[]) + + 为了支持数组参数,我们使用列表格式 + """ + prepared_data = [] + + for key, value in formdata_data.items(): + if value is None: + continue + + # 处理数组类型的值 + if isinstance(value, list): + # 数组参数:为每个元素创建一个同名字段 + for item in value: + if item is None: + # 空值也需要发送 + prepared_data.append((key, (None, ""))) + elif isinstance(item, tuple): + prepared_data.append((key, item)) + elif isinstance(item, bytes): + prepared_data.append((key, (None, item, 'application/octet-stream'))) + elif hasattr(item, 'read'): + prepared_data.append((key, item)) + else: + prepared_data.append((key, (None, str(item) if not isinstance(item, str) else item))) + # 如果已经是元组格式(文件),直接使用 + elif isinstance(value, tuple): + prepared_data.append((key, value)) + # 如果是字节数据,包装为文件格式 + elif isinstance(value, bytes): + prepared_data.append((key, (None, value, 'application/octet-stream'))) + # 如果是文件句柄,直接使用 + elif hasattr(value, 'read'): + prepared_data.append((key, value)) + # 普通字段,转换为 (None, value) 格式 + else: + prepared_data.append((key, (None, str(value) if not isinstance(value, str) else value))) + + return prepared_data + + def _diagnose_content_type_issue( + self, + status_code: int, + response_text: str, + headers: Dict[str, str], + json_data: Optional[Dict[str, Any]], + form_data: Optional[Dict[str, Any]], + formdata_data: Optional[Dict[str, Any]], + ) -> None: + """ + 诊断 Content-Type 相关问题 + + 当请求失败时,检查是否可能是 Content-Type 不匹配导致的问题, + 并给出相应的诊断建议。 + + Args: + status_code: HTTP 状态码 + response_text: 响应内容 + headers: 请求头 + json_data: JSON 请求体数据 + form_data: 表单数据 + formdata_data: multipart 表单数据 + """ + # 常见的 Content-Type 相关错误状态码 + content_type_error_codes = [400, 415, 422] + + if status_code not in content_type_error_codes: + return + + # 获取当前设置的 Content-Type + current_content_type = headers.get("content-type", "").lower() + + # 检查响应中是否包含 Content-Type 相关的错误信息 + response_lower = response_text.lower() if response_text else "" + content_type_keywords = [ + "content-type", "content type", "media type", + "unsupported media", "invalid content", "expected json", + "expected form", "multipart", "boundary" + ] + + has_content_type_error = any(kw in response_lower for kw in content_type_keywords) + + if has_content_type_error or status_code == 415: + logger.warning("=" * 60) + logger.warning("⚠️ 可能的 Content-Type 问题诊断:") + logger.warning(f" 当前 Content-Type: {current_content_type or '未设置'}") + + # 分析数据类型和建议 + if json_data is not None: + logger.warning(" 数据类型: JSON") + if "application/json" not in current_content_type: + logger.warning(" 💡 建议: 请求体是 JSON 格式,但 Content-Type 可能不正确") + logger.warning(" 应该使用: application/json") + + elif form_data is not None: + logger.warning(" 数据类型: Form (application/x-www-form-urlencoded)") + if "application/x-www-form-urlencoded" not in current_content_type: + logger.warning(" 💡 建议: 请求体是表单格式,但 Content-Type 可能不正确") + logger.warning(" 应该使用: application/x-www-form-urlencoded") + + elif formdata_data is not None: + logger.warning(" 数据类型: FormData (multipart/form-data)") + if "multipart/form-data" not in current_content_type: + logger.warning(" 💡 建议: 请求体是 multipart 格式") + logger.warning(" Content-Type 应由 httpx 自动设置(包含 boundary)") + logger.warning(" 如果手动设置了 Content-Type,请移除它") + + else: + logger.warning(" 数据类型: 无请求体") + if current_content_type: + logger.warning(" 💡 建议: 没有请求体但设置了 Content-Type,可能导致问题") + + # 检查常见的配置错误 + if "boundary" in response_lower and formdata_data is not None: + logger.warning(" ⚠️ 检测到 boundary 相关错误:") + logger.warning(" multipart/form-data 需要 boundary 参数") + logger.warning(" 请确保不要手动设置 Content-Type,让 httpx 自动处理") + + logger.warning("=" * 60) + async def _send_request( self, method: str, @@ -696,17 +843,21 @@ class ApiClient: } # 为有请求体的方法添加数据 + # 优先级: json > formdata > form if method.upper() in ["POST", "PUT", "PATCH", "DELETE"]: if json_data is not None: request_kwargs["json"] = json_data - elif form_data is not None: - request_kwargs["data"] = form_data elif formdata_data is not None: - # 区分文件上传和普通formdata - if self._contains_file(formdata_data): - request_kwargs["files"] = formdata_data - else: - request_kwargs["data"] = formdata_data + # multipart/form-data 类型处理 + # 使用统一的方法准备数据 + prepared_data = self._prepare_multipart_data(formdata_data) + if prepared_data: + request_kwargs["files"] = prepared_data + # 移除手动设置的 content-type,让 httpx 自动添加带 boundary 的 + headers.pop("content-type", None) + elif form_data is not None: + # application/x-www-form-urlencoded 类型 + request_kwargs["data"] = form_data # 根据HTTP方法发送请求 request_func = getattr(client, method.lower(), None) @@ -739,6 +890,16 @@ class ApiClient: if e.response.headers: logger.info(f"响应头: {dict(e.response.headers)}") + # Content-Type 不匹配诊断 + self._diagnose_content_type_issue( + e.response.status_code, + response_text, + headers, + json_data, + form_data, + formdata_data + ) + return { "status": "error", "status_code": e.response.status_code, diff --git a/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/core/get_auth.py b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/core/get_auth.py index 7dd5ab2..3906a8a 100644 --- a/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/core/get_auth.py +++ b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/core/get_auth.py @@ -26,13 +26,17 @@ logger = get_logger(__name__) class AuthDataTransformer: """认证数据转换器""" - def __init__(self, base_url: str = "http://lzwcai-demp-corp-manager:8086"): + def __init__(self, base_url: str = None): """ 初始化转换器 Args: - base_url: API基础URL,默认为 http://lzwcai-demp-corp-manager:8086 + base_url: API基础URL,如果不提供则从环境变量 LZWCAI_CORP_MANAGER_URL 获取 """ + if base_url is None: + base_url = os.getenv("LZWCAI_CORP_MANAGER_URL",'http://lzwcai-demp-corp-manager:8086') + if not base_url: + raise ValueError("环境变量 LZWCAI_CORP_MANAGER_URL 未配置,请设置业务平台基础URL") self.base_url = base_url.rstrip('/') self.session = requests.Session() @@ -84,7 +88,7 @@ class AuthDataTransformer: 原始API响应数据,失败时返回None """ # url = f"{self.base_url}/system/mcpServer/auth/info/{user_id}/{business_system_id}" - url = f"http://lzwcai-demp-corp-manager:8086/system/mcpServer/auth/info/{user_id}/{business_system_id}" + url = f"{self.base_url}/system/mcpServer/auth/info/{user_id}/{business_system_id}" try: response = self.session.get(url, timeout=30) @@ -263,14 +267,14 @@ class AuthDataTransformer: # 便捷函数 -def get_auth_data(user_id: str, business_system_id: str, base_url: str = "http://lzwcai-demp-corp-manager:8086") -> Optional[Dict[Any, Any]]: +def get_auth_data(user_id: str, business_system_id: str, base_url: str = None) -> Optional[Dict[Any, Any]]: """ 便捷函数:获取转换后的认证数据 Args: user_id: 用户ID business_system_id: 业务系统ID - base_url: API基础URL,默认为 http://lzwcai-demp-corp-manager:8086 + base_url: API基础URL,如果不提供则从环境变量 LZWCAI_CORP_MANAGER_URL 获取 Returns: 转换后的认证数据JSON,失败时返回None diff --git a/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/util/__pycache__/logger_config.cpython-312.pyc b/lzwcai_mcp_api_converter/lzwcai_mcp_api_converter/src/util/__pycache__/logger_config.cpython-312.pyc index c1490563e3b801c8acfe7f15922c65d3f4a31567..3c1dd2394ca45eafeb49e2fd277c178257294952 100644 GIT binary patch delta 53 zcmbO{lkw{eM&8rByj%=G;Ni`axoRV?Hy5MKZ!T$toT~EV#LW2I