From fe7d8243f6cc092dd4da36f5e940690c7d6a35f6 Mon Sep 17 00:00:00 2001 From: janghw Date: Thu, 20 Aug 2026 09:45:53 +0900 Subject: [PATCH] delete portal --- potal/agent-test-backend/.classpath | 18 - potal/agent-test-backend/.project | 28 - .../org.eclipse.buildship.core.prefs | 13 - .../.settings/org.eclipse.jdt.core.prefs | 4 - .../org.springframework.ide.eclipse.prefs | 2 - potal/agent-test-backend/README.md | 137 ---- .../bin/main/application.yml | 18 - .../AgentTestBackendApplication.class | Bin 896 -> 0 bytes .../agenttest/AgentTestProperties$Mcp.class | Bin 1563 -> 0 bytes .../AgentTestProperties$Portal.class | Bin 1714 -> 0 bytes .../AgentTestProperties$ToolServer.class | Bin 1580 -> 0 bytes .../agenttest/AgentTestProperties.class | Bin 2349 -> 0 bytes .../McpProxyController$ChatRequest.class | Bin 1437 -> 0 bytes .../McpProxyController$PlannedTool.class | Bin 1987 -> 0 bytes .../McpProxyController$ToolCallRequest.class | Bin 1861 -> 0 bytes .../agenttest/McpProxyController.class | Bin 25416 -> 0 bytes .../agenttest/PortalApiExceptionHandler.class | Bin 2194 -> 0 bytes .../agenttest/PortalBundleController.class | Bin 2196 -> 0 bytes ...PortalBundleService$BundleDefinition.class | Bin 2920 -> 0 bytes ...BundleService$BundlePublicDefinition.class | Bin 2936 -> 0 bytes .../PortalBundleService$BundleRequest.class | Bin 2905 -> 0 bytes .../PortalBundleService$BundleState.class | Bin 2403 -> 0 bytes .../PortalBundleService$BundleView.class | Bin 2720 -> 0 bytes .../agenttest/PortalBundleService.class | Bin 20327 -> 0 bytes .../bin/main/static/index.html | 625 ------------------ potal/agent-test-backend/build.gradle | 27 - .../gradle/wrapper/gradle-wrapper.jar | Bin 43764 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 - potal/agent-test-backend/gradlew | 251 ------- potal/agent-test-backend/gradlew.bat | 94 --- potal/agent-test-backend/settings.gradle | 15 - .../AgentTestBackendApplication.java | 16 - .../agenttest/AgentTestProperties.java | 16 - .../example/agenttest/McpProxyController.java | 476 ------------- .../agenttest/PortalApiExceptionHandler.java | 21 - .../agenttest/PortalBundleController.java | 45 -- .../agenttest/PortalBundleService.java | 346 ---------- .../src/main/resources/application.yml | 18 - .../src/main/resources/static/index.html | 625 ------------------ .../agenttest/McpProxyControllerTest.java | 123 ---- .../PortalApiExceptionHandlerTest.java | 24 - .../agenttest/PortalBundleServiceTest.java | 83 --- 42 files changed, 3032 deletions(-) delete mode 100644 potal/agent-test-backend/.classpath delete mode 100644 potal/agent-test-backend/.project delete mode 100644 potal/agent-test-backend/.settings/org.eclipse.buildship.core.prefs delete mode 100644 potal/agent-test-backend/.settings/org.eclipse.jdt.core.prefs delete mode 100644 potal/agent-test-backend/.settings/org.springframework.ide.eclipse.prefs delete mode 100644 potal/agent-test-backend/README.md delete mode 100644 potal/agent-test-backend/bin/main/application.yml delete mode 100644 potal/agent-test-backend/bin/main/com/example/agenttest/AgentTestBackendApplication.class delete mode 100644 potal/agent-test-backend/bin/main/com/example/agenttest/AgentTestProperties$Mcp.class delete mode 100644 potal/agent-test-backend/bin/main/com/example/agenttest/AgentTestProperties$Portal.class delete mode 100644 potal/agent-test-backend/bin/main/com/example/agenttest/AgentTestProperties$ToolServer.class delete mode 100644 potal/agent-test-backend/bin/main/com/example/agenttest/AgentTestProperties.class delete mode 100644 potal/agent-test-backend/bin/main/com/example/agenttest/McpProxyController$ChatRequest.class delete mode 100644 potal/agent-test-backend/bin/main/com/example/agenttest/McpProxyController$PlannedTool.class delete mode 100644 potal/agent-test-backend/bin/main/com/example/agenttest/McpProxyController$ToolCallRequest.class delete mode 100644 potal/agent-test-backend/bin/main/com/example/agenttest/McpProxyController.class delete mode 100644 potal/agent-test-backend/bin/main/com/example/agenttest/PortalApiExceptionHandler.class delete mode 100644 potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleController.class delete mode 100644 potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleService$BundleDefinition.class delete mode 100644 potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleService$BundlePublicDefinition.class delete mode 100644 potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleService$BundleRequest.class delete mode 100644 potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleService$BundleState.class delete mode 100644 potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleService$BundleView.class delete mode 100644 potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleService.class delete mode 100644 potal/agent-test-backend/bin/main/static/index.html delete mode 100644 potal/agent-test-backend/build.gradle delete mode 100644 potal/agent-test-backend/gradle/wrapper/gradle-wrapper.jar delete mode 100644 potal/agent-test-backend/gradle/wrapper/gradle-wrapper.properties delete mode 100644 potal/agent-test-backend/gradlew delete mode 100644 potal/agent-test-backend/gradlew.bat delete mode 100644 potal/agent-test-backend/settings.gradle delete mode 100644 potal/agent-test-backend/src/main/java/com/example/agenttest/AgentTestBackendApplication.java delete mode 100644 potal/agent-test-backend/src/main/java/com/example/agenttest/AgentTestProperties.java delete mode 100644 potal/agent-test-backend/src/main/java/com/example/agenttest/McpProxyController.java delete mode 100644 potal/agent-test-backend/src/main/java/com/example/agenttest/PortalApiExceptionHandler.java delete mode 100644 potal/agent-test-backend/src/main/java/com/example/agenttest/PortalBundleController.java delete mode 100644 potal/agent-test-backend/src/main/java/com/example/agenttest/PortalBundleService.java delete mode 100644 potal/agent-test-backend/src/main/resources/application.yml delete mode 100644 potal/agent-test-backend/src/main/resources/static/index.html delete mode 100644 potal/agent-test-backend/src/test/java/com/example/agenttest/McpProxyControllerTest.java delete mode 100644 potal/agent-test-backend/src/test/java/com/example/agenttest/PortalApiExceptionHandlerTest.java delete mode 100644 potal/agent-test-backend/src/test/java/com/example/agenttest/PortalBundleServiceTest.java diff --git a/potal/agent-test-backend/.classpath b/potal/agent-test-backend/.classpath deleted file mode 100644 index be88c88..0000000 --- a/potal/agent-test-backend/.classpath +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/potal/agent-test-backend/.project b/potal/agent-test-backend/.project deleted file mode 100644 index f5751fa..0000000 --- a/potal/agent-test-backend/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - agent-test-backend - Project agent-test-backend created by Buildship. - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.buildship.core.gradleprojectbuilder - - - - - org.springframework.ide.eclipse.boot.validation.springbootbuilder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.buildship.core.gradleprojectnature - - diff --git a/potal/agent-test-backend/.settings/org.eclipse.buildship.core.prefs b/potal/agent-test-backend/.settings/org.eclipse.buildship.core.prefs deleted file mode 100644 index e479558..0000000 --- a/potal/agent-test-backend/.settings/org.eclipse.buildship.core.prefs +++ /dev/null @@ -1,13 +0,0 @@ -arguments= -auto.sync=false -build.scans.enabled=false -connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) -connection.project.dir= -eclipse.preferences.version=1 -gradle.user.home= -java.home= -jvm.arguments= -offline.mode=false -override.workspace.settings=false -show.console.view=false -show.executions.view=false diff --git a/potal/agent-test-backend/.settings/org.eclipse.jdt.core.prefs b/potal/agent-test-backend/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index e9186c3..0000000 --- a/potal/agent-test-backend/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 -org.eclipse.jdt.core.compiler.compliance=21 -org.eclipse.jdt.core.compiler.source=21 diff --git a/potal/agent-test-backend/.settings/org.springframework.ide.eclipse.prefs b/potal/agent-test-backend/.settings/org.springframework.ide.eclipse.prefs deleted file mode 100644 index a12794d..0000000 --- a/potal/agent-test-backend/.settings/org.springframework.ide.eclipse.prefs +++ /dev/null @@ -1,2 +0,0 @@ -boot.validation.initialized=true -eclipse.preferences.version=1 diff --git a/potal/agent-test-backend/README.md b/potal/agent-test-backend/README.md deleted file mode 100644 index b1875f0..0000000 --- a/potal/agent-test-backend/README.md +++ /dev/null @@ -1,137 +0,0 @@ -# Agent Test Backend / Portal PoC - -Browser-based PoC tester and hardcoded Portal Registry screen for: - -```text -Browser -> Agent Test Backend -> MCP Server -> Tool Server -``` - -## Ports - -```text -Tool Server http://localhost:9092 -MCP Server http://localhost:8080/mcp -Agent Test Backend http://localhost:7070 -``` - -## STS Run - -Import this folder as an existing Gradle project: - -```text -C:\Users\hyo\Documents\Codex\agent-test-backend -``` - -Run `com.example.agenttest.AgentTestBackendApplication`. - -Default environment: - -```text -AGENT_TEST_PORT=7070 -MCP_ENDPOINT_URL=http://localhost:8080/mcp -MCP_PROTOCOL_VERSION=2025-11-25 -TOOL_MANIFEST_URL=http://localhost:9092/tool-manifest -TOOL_SERVER_API_KEY=tool-server-key -PORTAL_REGISTRY_REVISION=1 -PORTAL_ROUTE_KEY=external -TOOL_SERVICE_DOMAIN=http://localhost:9092 -``` - -Open: - -```text -http://localhost:7070 -``` - -## Recommended Verification Order - -1. Start Tool Server in STS. -2. Start MCP Server with Tool Server environment values. -3. Start Agent Test Backend. -4. Open `http://localhost:7070`. -5. Click `Initialize`, `Tools/List`, then run `Agent Chat` or `Tools/Call`. - -## Portal PoC Scope - -The current screen intentionally uses hardcoded registry data instead of DB tables: - -```text -MCP Route external -> http://localhost:8080/mcp -Tool Service external-tools -> http://localhost:9092/tool-manifest -Mapping external -> external-tools -``` - -The MCP-facing Portal Registry API is: - -```text -GET http://localhost:7070/api/portal/registry/external -``` - -For this PoC, MCP can use these IntelliJ environment variables after applying -`C:\Users\hyo\Documents\Codex\mcp-portal-registry.patch` to the MCP project: - -```text -MCP_DISCOVERY_ENABLED=true -MCP_PORTAL_ENABLED=true -MCP_PORTAL_ROUTE_KEY=external -MCP_PORTAL_REGISTRY_URL=http://localhost:7070/api/portal/registry/external -MCP_REGISTRY_REFRESH_INTERVAL_SECONDS=60 -``` - -`POST /api/agent/chat` is a first Agent Backend skeleton. It currently uses a simple rule-based -planner so it can run without an OpenAI API key: - -```text -"서울 날씨 알려줘" -> external.weather_lookup -"달러 환율 알려줘" -> external.exchange_rate -``` - -Later, replace the rule-based planner with Codex/OpenAI model selection while keeping the same MCP -`tools/list` and `tools/call` boundary. - -## Portal Tool Bundle Management - -The Portal now manages both seeded pull-discovery bundles through one API and UI flow: - -```text -external-tools -> http://localhost:9092/tool-manifest -business-tools -> http://localhost:9090/tool-manifest -``` - -Bundle APIs: - -```text -GET /api/portal/bundles -POST /api/portal/bundles -PUT /api/portal/bundles/{bundleId} -POST /api/portal/bundles/{bundleId}/sync -``` - -The API key is write-only. Read responses expose only `apiKeyConfigured`, and logs never contain -the key value. Manifest synchronization validates the configured Bundle ID, Tool name prefix, -required metadata objects, and the configured execution endpoint for every mapped Tool. - -Business Tool endpoint mappings are owned by the Portal/MCP configuration rather than the -manifest: - -```text -business.customer_search -> POST http://localhost:9090/internal/tools/customer-search -business.order_status -> POST http://localhost:9090/internal/tools/order-status -business.ticket_create -> POST http://localhost:9090/internal/tools/ticket-create -``` - -The Bundle definitions and synchronized snapshots are currently held in memory because this PoC -does not include a database dependency or an existing schema/migration framework. Therefore no DB -migration is required. Restarting the Portal restores the two seeded definitions and clears cached -manifest snapshots. For persistent operation, the `PortalBundleService` store is the boundary to -replace with the project's chosen repository and migration framework. - -Start the business Tool server with pull discovery only: - -```text -TOOL_AUTO_REGISTER_ENABLED=false -TOOL_HEARTBEAT_ENABLED=false -TOOL_SERVER_PORT=9090 -``` - -After starting the Tool server, open the Portal and click `Sync Manifest` for `business-tools`. diff --git a/potal/agent-test-backend/bin/main/application.yml b/potal/agent-test-backend/bin/main/application.yml deleted file mode 100644 index 7c5fa8f..0000000 --- a/potal/agent-test-backend/bin/main/application.yml +++ /dev/null @@ -1,18 +0,0 @@ -server: - port: ${AGENT_TEST_PORT:7070} - -agent-test: - mcp: - endpoint-url: ${MCP_ENDPOINT_URL:http://localhost:8080/mcp} - protocol-version: ${MCP_PROTOCOL_VERSION:2025-11-25} - tool-server: - manifest-url: ${TOOL_MANIFEST_URL:http://localhost:9092/tool-manifest} - api-key: ${TOOL_SERVER_API_KEY:tool-server-key} - portal: - registry-revision: ${PORTAL_REGISTRY_REVISION:1} - route-key: ${PORTAL_ROUTE_KEY:cus} - tool-service-domain: ${TOOL_SERVICE_DOMAIN:http://localhost:9092} - -logging: - level: - com.example.agenttest: INFO diff --git a/potal/agent-test-backend/bin/main/com/example/agenttest/AgentTestBackendApplication.class b/potal/agent-test-backend/bin/main/com/example/agenttest/AgentTestBackendApplication.class deleted file mode 100644 index a2f5fb4e87ffe6e10069c1be32edde00eaa22f7b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 896 zcmb7CO>fgc5Ph4Zb!q}_Qz(=#m0Owvs|txrs!C}D5|RZ7SK+{E<80k+?OkiVhW;&1 zAP)QheiUNXlq3g(ge>puzL|aVW@dl<{PrEd7>@(A7!DJYb9p85B9mOCQadM0$H#tm zO1l%0TuMD37e%HL;grzuX-gL$G)IB7{w;13L}O3}I|4o5+{SH*o(SV|wZvz_9nO(oW@a zs!HV(YHeISM#^y9sIzW^sGK3ewNQE8loRUDieOUu>hA=9sx7Q3q;*P`%fkIDdb_~m zL}=nVYUC=Db2+avniCfb>HLNE&5IYl5uSiXM9&&%(T?65tpS}lSw?nk_yyh1wL7HM zyMY_@0lnV=WN*@G1GlhAh`;>2CIh;CHvEdM_sjbsc|ef?#V$Fz*u`DKTew%Rd$?am Nwef)LL;4>9zW~J8{wM$d diff --git a/potal/agent-test-backend/bin/main/com/example/agenttest/AgentTestProperties$Mcp.class b/potal/agent-test-backend/bin/main/com/example/agenttest/AgentTestProperties$Mcp.class deleted file mode 100644 index 6e2e70e7aea4ca1f05d2f946836e9cf8d19e81ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1563 zcmb7ETT|0O6#ll*rmc}e1qD&?0@9XC6ucLmK^O;S3NnIy(5E!rfk2v#n=PaN$p@K{ zafSzffIrIdY+7uT!pK8%HYevh-?{G3U&lWHEa8QL1jDQ&8W#U#Hv*4a_73-z;-RvZ z^l6(;n^FW^Dwl^7>rP;xm!W^pKCms%_IIqe+!3FV!D zsR20?h6bdSXwl{=(Y%R%Twz!)R4$ZixodFgT|3;>0W$OyiffwSnu$RSGt6I@V2vTg zKQ?VIWEd*MYVoGF#~oEJzSsV5m>9uLhLwx@@8*yg7hT^~FBzudjO{kXAZ<;tnne$8 zn;65LDCa5-z&cmEqP}TMyTKK;lOek$n$qE~UEQAHF4HroM}nZQgixW9cF^(>W>G-N zK#^g*Gc>a4E4RU|Rvp@wCZ;jNkg1(`U^tAY;Q9w*pIa^KRokz7G_w^U_M1WZbY3w{ z8@T;B;43_Iq#Gz99 zYrfBA#Lq(m4;hO8J;cZQrfZ&x*k?Vs7Tkk2k?NF zu!lxAfxKQ_y?uHkNv7ye5@~v8=xdTBBbh9HL-s2j5-{6FAfwBX24rG%h4h^b8K7l9 zVVNSgn#Cv`V*vOU#QyLM&;k#UsSYg;x(a-WLg@q-2aVu*8&n{s1X-p_)5jP+4@CvW zIN53{i=?hruJj$ZO4CQkmS&F7cX%e{UHX$rP#q~JdXkukOeb-VWFN&ci(Y!q;XZX@ go^*L)U|7H-xQ^~SW+7j;Yrc|2S2HF_95BL$!+T1B-w?tm50@I7028fBi_yIfh`J7D&v(v5~ZJxLP=q8`<2B8DhAr)AZVBxU4^i9Yl*ET`80 zH7B_XERyzp?(Ta*_z|soHm;i(zzv4!|FN;k&@SFrxa~6Zr9v^j**g$)$lG zbQ>fQL7cvq=rw{CqJT)|3(QYMw4jr|?LM;;T|Ni&GSK}CLK`Z8BtAER#4d#N{27v< zMN+de8aQTT&Tttx52CFWxR#*Ja5Zo(47vtD-9lp;$lBpJ<&o#AFP6r;%; zCRs0W)0E-}-F=y_7|9Hs!pw}HB9R$rJ4Me4{Rk(rZa77MI;u#6$QYdn(!O{GV|07* t-zOf!1G4c5kMV@KNm9o!gB%4ri#fcYd)`OQV1e#l-y5&o)nfal{t3FOjVJ&B diff --git a/potal/agent-test-backend/bin/main/com/example/agenttest/AgentTestProperties$ToolServer.class b/potal/agent-test-backend/bin/main/com/example/agenttest/AgentTestProperties$ToolServer.class deleted file mode 100644 index 41cc1e67ab997d816fba3cda4bf1b28414c9c8b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1580 zcmbVMTT|0O6#ll*rY*6B3JTuv0@4;FDtN)7Gl=7WRAi*`pikR&g+Q9Eo0O6NI{7i?%E#-^-ehqxpK#1 z-B-4|Sz<`=roAHe7zXo&Zuh;5vMy2}Unb&6Fl5WND^^>Ls!$bPb%gdSYuwr3%GTe3 zQUkIk3=K&7vdxyGG;=2Uah~B-zI>KMOI>43ZgFo*$H~x>FDz@GizWs!%rJLWo@FYH z_|)Q#$1s$SROU@}Th#ng;k~B0VqyeW8CK3n)6Fjt&)cr;KVz7P5{E5|KtfGnBaI&1 zFmVkxg9>cWFuWH2maMOF#T&vGin2(r%a*E%m$vTNaF>Ca*5g6Y=TiEfuXwZV!%HKN zqJaX#XlH;_%k}Msu-fGaElo^fiXl}!^1!ekO~H0|<&Lo0)=S*2J2b{+DR)}U(($|^ znh;poYl;`bt0}wbOBIDII^6T3im)~xjys96|20oxXR`@RBw^89N4BTbUXv`9k)8~L zx$L?^1+>C5Fw0Q*?|~*K(cOK-Fy1NbDK#=M$8ha*Q<|Q3L22<;Y4ew)=aa7eN5DPW z%O0BB7;<`>^+M_;C7Ga`IFj^E(bqUhMlxRfhV)lD#9)R-Kp)Np8jy+66_Pm_GC=!( z#4x zQL@!k7D-*LZ1Fp;7bg#pE>0aFvwtGxExJj?sE(8qJ#mZ$rsKFxGDET4K`*_hahE!A gpL985V3@%J`i(rqV>}^Q3NSO64=~!B?oZIA->=$w6#xJL diff --git a/potal/agent-test-backend/bin/main/com/example/agenttest/AgentTestProperties.class b/potal/agent-test-backend/bin/main/com/example/agenttest/AgentTestProperties.class deleted file mode 100644 index 52f942fdc99c98c072cf404fc0ed0bf3e5ca39d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2349 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 IUrqJ?0=!Sra{vGU diff --git a/potal/agent-test-backend/bin/main/com/example/agenttest/McpProxyController.class b/potal/agent-test-backend/bin/main/com/example/agenttest/McpProxyController.class deleted file mode 100644 index cfb93d88bc096b8938554092ce4a9df259047353..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25416 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 Ve|!!}&+zdP8a8zs@O=@N{~u9ypvM3J diff --git a/potal/agent-test-backend/bin/main/com/example/agenttest/PortalApiExceptionHandler.class b/potal/agent-test-backend/bin/main/com/example/agenttest/PortalApiExceptionHandler.class deleted file mode 100644 index 97ad0ff3d04fa9e352e60106af2d0314a60629e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2194 zcmcIl+j84f6kW%;$ad7GHNmB&Eh!Z0G-*T#Eul^WPDqHsX^I`vVR%8dPaHL}WTfM? z`4xVES9oa~nBlcE{0jd-Sx3GzPAJSUvL&6Zz4uvr?aTT7&tHB6ki$a*F#)UM*Dd+P zt~Xq1*){1YB|~LB@B?MLxrVd;q#_&2@x4vktGY5UFe-5Fk$q@euI<&V?eZg8Q3B)3 zj_0TqfsxG2uE6M;UzJIWAYoz*;{uZf$CLM)^|B0h?6NBbQU$+aySsMa=yh8-s`i~w zV4?6YVP%P6KSC%_$rSvcW`&Kw@oIa4U6+sj;J|t;%T_3ZLm5~_88&<`lqF@WX4nle zJ2TX9U#UjdWZhGaI^uSTl2h|&9x#+2{{J193lZl{<+#?C-DoeP6qJ+>G}}_>OGqwHB)8Bt6Fwf*Xokgt^+zAJG9*h@WE-|rTl%^yu`{#~ax)@je!~O{2+PT*~xWwi~w0Y?Mz(4n!tC%+ieK@uinwuSN-bIYSVG6G9b(A_}svT zz~YH%9z@n}ZekO8j=(ZK3fvo#j8h73&@H=cvc-3&@hM3U4$h-SXL#NoRDH!6!BS#{ zR}NT3(GWJUJuu-rsZW;tW>AqEOr5~h6Ay@aT^<4}Ck}$8a7gKF?D68vj$@LR9d2#x+%Z0j;V1eXg~pj?C?K9V@mwLATg0NUuqx=C4WB95|N0Dwz5gj}Dn%|OK LuX6zGjwk*C<579h diff --git a/potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleController.class b/potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleController.class deleted file mode 100644 index 4a761b6b23c4d772ef64c39a87d0f3ccf8cea135..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2196 zcmbVNU2hvj6g}g(+1Pc`HifpK1)8)4I|&&+`iaw^G_ zLOk(@K!PCgzz^U@AX67 zYpMD=uvRBlHpAE||3Mn`e7zF}HV%DX#~z9TXP>KmC4Ck2<=36(x@!fzPG*=0tTp~0 zuchOC)75o>*-aA|yCtwuo#I@rEl}JEd%BDnoUXt_MPQ~{YnLFfP{Aq83Y=+}K!24E zIy!!)I=&WIY=m9qw^eMMJX99#p5e7OCUF0f2Hw2rn}mTcR%?xnj?@}oHmKvfQp@xM zWz(2WpZ>>Sa{^aGhOX!Qw~aoiZxMKDHw~;A=(b4=L)Z?2&?;-ffDznmgmGUcQEY<# zx3L=NgE0P19_Wtjn4l-s*j9d_?UyQwXjd=eEnM+%S)ev?+A27YH<-F^td(ViFQ!P{ zFrZ!iB4v@)YbT>Z;MT;!Hp65bt-#%h&+Y0YBCondg42ZDpW0V`%5ZNS3$bTyBp>rJ zw)b{+A8tQ;__@H!(X2I<%Z}CYk@QU-(U80cVef}5fA3fDHr`(v#j#0=JJ%*>SDWRCaGPAei&H z5s2%Pk1ANjnj0f8=iiVQfpRNMIMrzXCUpf`UF14T?Io_x&&S*gu5+uuVg3+Hzi^*>aaO>U484-u4AF&aOkz5$ zbF_Y!>s4ATYpmcJu2b|J>J4t3kNdQ7v6P&TwL`r3x|5YxLMV+xD4~iPVT7yCp}Pf2 z>-;T$1Cii^%-3RpiVdesAi0d(KIR!maXE9leuxiW_gl;SUd^q>e#=C0v^Wyo4GMh! k38eyVW+5+fcZ>3E=Q6v;9o(ht^!M0Mbu3|%>lT*&1)1Y$TmS$7 diff --git a/potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleService$BundleDefinition.class b/potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleService$BundleDefinition.class deleted file mode 100644 index 21af85596ef71edb0c22c8540b71866876a8e000..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2920 zcmc&#TT|Oc6#kZrZ-^LZpn*_A%gq)LH*HhWn34oYiU|-W5SozQ*7gRJ$dW6`O!C%W z)2DW(nTe+}JoE?jM|FC3WrHk~3{xKZaCBC?`<=6UF5mwB&(U81?w}k;2gA*>UCoJC zyjn9wj_(W0^Mvc=inil%bGdF+OtCGTL!&Gv{l8W5+^`JKu&p?{82VoDL!L9awV!(= z%C=KsNR$+;K*NEJljUvCF|7SO!v)W_O=(m(Tf8b5daB$qo|D_hj>!-&aaX8O%A)0> zLk?fjw6J)|6l9d`FlvRSsRic34z8DhL<+!wDIdZoL&R;6YemPdJFXddi(0)=?k2mSt1)3GuhJ%3|%XBMI^!Sp@we6q<6y- zTlH#5I6Lw_GDdcpo4edGq@)x8v4*r+4gKdvjbFBpK7>@YYfHA#(%G? zd}JgO2i!f7(Nvc#NN?9Qe1Zk^9tfr>@^T<0bg$6zLq#RsJqqU`0fd(zoW4GW+| zKRlPSDC^{YQMS|)!!JSi&4vj;&NZ{Q{w_<$6_MR#`1OB1!Wj!NyPHBMZfjV?9aRr> z@V-sq9oUs3cZfBJ5Gb3eZM*K2#XUpz*`)|hyDI64pv$)Hxt_yoz702pZ%~M1jp51( zfjM={GpZuzmql}@hIQO03MsWLFuVyy(6A2emm=ryUgMHa-N_Bxep#>O-<1!=rcL0^ z>zY^REk7QxDSpO2N{V>V7R43UhGjFOCzjFU`|Op|0t<`}esWeG>!SGaKy{b$e2 zE;705ISRg8-!n{|5QE$(kgi4tzlT&dB0L`VP{}6lq4fTYrn8 zKh%~>^o_|$Hicm|Lql>N1sMrLrUQ^fBxE!UnF&CWk&v-4Bolz7A|c~pNHzfJ!6$U1 z@bxEt3te9iKzdcAd`QP}#HN1_L1qJxbmYM@;e*WuAbrp&M}DSePaSYB44prQ_S5^> z%Fhg$kSUGpN5}yo5G}Qm+E35m=gmL_L0?KK)^pSL9g}xnF#a+^)WMqT(Fl^!h zig<`e*v1YXV;A4z3GEnA!F`Hn6vglG1AZdSm4&A$k-kly{C@Sj)$dimQ~f^myVUPd JzeC&l^IxHkjtKw& diff --git a/potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleService$BundlePublicDefinition.class b/potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleService$BundlePublicDefinition.class deleted file mode 100644 index 80d433f62db758a114995f37a3c743c7080486a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2936 zcmcgt+fo}x5Iw_1Hx>(sAqEpn!o|{ptza92BSJ722Vua9!Pv&Rjl=+JuXdE(6=gmo zf00zhRZ+Q$hkQUjDwUpH308|jQDs*idV6+ePWN>8^qIf^Ir$5~LlmQEVOS{2QbxSx zrLrk9{6JW)D;zhIm$u8z#fr6OiY;Lu8AUPb|K%$M(mc(~6s5h#5G#1#91VNc&z84b+prF@3|Cw!O=Vixo4h0#I!fF!_R0A(+hm9qxFft# z!lLE8O%C7Ew6J)=6l9d`GF-1i^3pVOmP^(+*(^$H&!HV_bTD2v9*g58Y3&;a6fHHI&dDRc2ZrPU_ zZexmJC$;{e_bl5qpRq&k9IBjpq2`p&X$^O9pJC-=_{=dx#4ig3D$;=n!+N-R2o&@DR0T3P3(+W_NAZH+6a?(mM>l{+}K%dEwGK z3AAEC!yF!Zm8WxdZ3y>J?&Y~nd_hz|6-#W%id__s4AocHnsC}}MN9-;l+txvo0okX zP6FQ|7sV>WjWg=9E0$}NM8+?DZKsAcJSNI0)GaW)3nt94j^rDW@prFs#i!2bx|DA! zSIEJCyii{d9b+r?X{S^fGF7jDvXwkjQ$=KOaNQ$&@m zL>Y1Ps0V;XJ&M;oO|0%|;&e|FqkEc|+|$J1o+keGH1RE2Qv}q4Pl(?cS-eW$UYZ#r z%}BTFf1>XXZ_73MM${ymLccddLvr2>G7yH`2|!{^A%kJa-2f!s6fzWs+zUVwO(DZ! z$aDbGfiEbcaQGv?g~GodfOLA9@*yp!85{pS1epmyl1;s&!d_+rkS^#{BfnA;=MFd- zj_JWEw42_~dVOw^302aleu4}T0?|^vsNM7c-mN7f2)YM^4%q;j!E6nbB2O*kDW*@H z;K4;GMhyHlU1&eFc`vjV^xr|~S^Yh}(I=|t(%H+RQhI-)iph74xeuIikyggybjv9TBr%dWNrFT<>?BE&bdj`UiM}mZ#tP{{ zGO|v47&h<(d3=Yb*uplRVF%Cgf_C(H$$g1el*RAyBYq~$Jqs^UAiY4I{C@Sj)$dim RQ~f^myVUPdze5}P^IzR=mG}Sv diff --git a/potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleService$BundleRequest.class b/potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleService$BundleRequest.class deleted file mode 100644 index acb032ce66184e194b87408060e05f38e1ae3959..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2905 zcmc&$TT|Oc6#kZ*FNknypfM02c6OeT5g!|_?|?sv}axqRz?{yqL1z%3MGXcL$(I;E_9 zZj{QF%o+#M_B>hfvYU?U8P-zO-nZnobdSuUoC?0Sc4X?tAk z4bD{cOe?!?lzE`Uws~M1Ue)DUt~akPoV~!=nMK+-pU#|J5a?KT_GJPBA8P1Cm-24f za-&)*NOwowN5#k~8rH7in(Eu1RDcl;F?6ZXMGf&_G_D~Lj4o+NDs%1W%%0jarJ);| z|GlcdFKg(*IW@{?=v6!W8wU(sd_Fq&me*BwuW0B)KeO#QL1qV7QXgx$ifaPv>9uz( zs$9!3#t)6kp$esc!@P2JUBf5%TwvinT;*9NtYyPuSq!E_`MY0uDvMq&v!^_LsbL;W z@y5G*YGsyv%2=ZPtd#kUrbW#YJU4h^J*CV`T-DP%@Xva+r3%KQ1 z0uSD|F18s)%7C5dm4k6}e% z{FJxcs_mI2nGI^9zEi_0?vXhP4GRJqNVr#Eh%!tWrHoM~D3g>KN}4h!pyh2_y8eA-B?hv7 z=B-JEv>$n^eh`>GO=^n>HS!a~Pdw6_x3YJGWRm|j_;9T_6_0bl3y5o^$957&0t1Tl zIT}!0?rSo+ugT-SCX4%;?Coo^wy(+Az9z@gnxdgLd_YbYw0NGsgPfVB7Sx^kD-8YV zZ@IwVE;T7qNcl4y(s?V$a1=5eg2Y=wMx&6C5G2tOG8To5h9Jq7kclYdVhGZWk9ec# z^(TLiTz@PC>G2~KK-x|sHuFaWG9H5Twmeumda#KQqz^iC6l7}l%mL@3&`T%Ke!iKF z{9L99mC~4gj4TiVajB8ie!hO6)&mg+eFj8A(FB^ujXEezPi^!R*JqFM#hXxkWAPOW zI=I@Rf3=7F?k4(%{ujR1XKNVJ=V}<%uhcNAr#fmF(^H)_Oz4AMHB9P5u^MLdRHBBo zo=Vm*_p&+mp))RVWjetsXAmfHN`jK4D2F|iUP>RO6U+Qmv&)IXIea_20_sL+M&>E&7oUGWoX4W;wka}oLc;6Tn(J zIjY0vs(FrK?G@y=E~$7|`VM-wZOXnB&Z^ep(w@V6D#qoj{+4DLyWGQ@jw$W$tGF!f zN<(u+Xv?XXl(vk;o2w3Y-DBx`Ma2iW%C5qaMQ0-1Cp+@Zw2BY$5#jtR@TzT^Wy@m* z8uQR?tI2)A-b>Q^nu_ZZKBcvdW$|4GGx$`&jbkS!2uj6G|AU5GIU#3oOE&avf}MRmVgHw9XK5#Z>V7#_S5P8ci&}Hp zdHuMHdkPk~h<=HE(2C)DDi%>D#Oz%@eaC{8Z#mi`mSwaR!uQc9LT~35-XuF+&INzE z7@lZxaoatIgHVio#$Th!jljc0f5Zh3hfrG6b>X_>(|*;XmLQl!(T9&eG%`FgH&ncZ z5k5OLyW{9$!I0DP?BCoMS7mt#YT2@cQ!+Kz6|RD9LhfI4=A;If&f%~ij0C3MO!3J- zazQb_K$E{b%9R2*1M2x#C&6Yma7Fdh1%uF8en)($3KeHCY z3I0DQchWt+`xenhWel-xB=E*CPO`@Pd{n|1PIWQoLd;YTUPkVv3X-&P1S{3WN`+W4 zmZw90d6vZANlyNPfuHyxpq7$GTJvGlz#)W*$#Stp2KOAQAN;!9TzQH!+fz?4%IhMp zF$XI~e7; WyM@nL63Z{xT4ebp>tFf)TYmx86mc;C diff --git a/potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleService$BundleView.class b/potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleService$BundleView.class deleted file mode 100644 index 55fe73555cd6d889019e623792098d81eccb5a47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2720 zcmd5;T~`}L7=8xGC#ygN1XNm;+GJ@PtM#LXrhK&}ZJtFD% zcwx^m$8)&w!b^XY$9HxUvLq1&z3}AB&dfXWykGNvLeg&|G{RBoB?$l%>FTUfA zrYrLNPN{SE?6-Ndvki>y z$c||9wEA4sbfHK;hRtm5zi`0VhOCPe$U+M-#2F^ajwg0njjB-lyy}w9nX;^L_kb(M z>^sD15Y8J=8H%rL$rc*Uevk>Q7nvmMB+h={>B{u@d!IC2LJzx`jdxolMC4$>IK zM;313;~-SXsL5@ik7Rw9E8Y-VD0=R6MYdE;JakOT%?+uKw@kVbbX`jAYsH%(4?m5& zC?xO&#eOd#Rm;;(L*&D_>D;uif_qets$C8YucATbc*pXE$cMMLxT($lQd!Cut!Cle z2kij&UpB?2@N3FxYN?`-4XTuWl+X!*uOn=SNI#Q!ofMde8cU#091-w6hRR5_nVuHStM7c&Yqd8{(jH#c3D{s&@Ziwt2rUMEsiTMnD z`ysRakYWUq8VZ@~hun`q(nBE&{gBlNWE^jkU7>aF3@EtR4_!NjPEiBu+H{>*7&j#B z6TA!bf#|5~h$-qO#%GgsISTp!_%ZO+srSx+aAUhQn*NW81BLasU7T diff --git a/potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleService.class b/potal/agent-test-backend/bin/main/com/example/agenttest/PortalBundleService.class deleted file mode 100644 index c9abcd77c896bfd32b6d508bf3b70d69cdb5ee0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20327 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 diff --git a/potal/agent-test-backend/bin/main/static/index.html b/potal/agent-test-backend/bin/main/static/index.html deleted file mode 100644 index f47ca89..0000000 --- a/potal/agent-test-backend/bin/main/static/index.html +++ /dev/null @@ -1,625 +0,0 @@ - - - - - - AX HUB Portal PoC - - - -
-
-

AX HUB Portal PoC

-
Hardcoded Registry + Agent Backend + MCP Tool Call
-
-
Loading configuration...
-
- -
-
-
-

Portal Registry

-

필요할 때 MCP가 Registry를 다시 확인하도록 revision을 갱신합니다.

- -
- - -
-
- Tool Server 설정 -
- - - - - - - - - - - -
- 고급 설정 - - - - - - -
-
- - -
-
-
-
- -
-
- MCP 연결 테스트 - - -
- - - -
-
-
-
-
- -
-
-
- Agent 테스트 - - -
- - - - - - - - - - - - - - - - - - - - - -
-
Agent 응답 대기 중
-
-
- -
-
- Tool 직접 호출 - - -
-
- - -
-
- - -
-
-
- - - -
-
-
- -
-
- 상세 응답 보기 -
Waiting...
-
-
-
-
- - - - diff --git a/potal/agent-test-backend/build.gradle b/potal/agent-test-backend/build.gradle deleted file mode 100644 index b0177cc..0000000 --- a/potal/agent-test-backend/build.gradle +++ /dev/null @@ -1,27 +0,0 @@ -plugins { - id 'java' - id 'org.springframework.boot' version '3.5.11' - id 'io.spring.dependency-management' version '1.1.7' -} - -group = 'com.example' -version = '0.1.0' -description = 'agent-test-backend' - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) - } -} - -dependencies { - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-validation' - - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' -} - -tasks.named('test') { - useJUnitPlatform() -} diff --git a/potal/agent-test-backend/gradle/wrapper/gradle-wrapper.jar b/potal/agent-test-backend/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 1b33c55baabb587c669f562ae36f953de2481846..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43764 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 '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH="\\\"\\\"" - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='-Dfile.encoding=UTF-8 "-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/potal/agent-test-backend/gradlew.bat b/potal/agent-test-backend/gradlew.bat deleted file mode 100644 index 8de1053..0000000 --- a/potal/agent-test-backend/gradlew.bat +++ /dev/null @@ -1,94 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS=-Dfile.encoding=UTF-8 "-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH= - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/potal/agent-test-backend/settings.gradle b/potal/agent-test-backend/settings.gradle deleted file mode 100644 index 9576414..0000000 --- a/potal/agent-test-backend/settings.gradle +++ /dev/null @@ -1,15 +0,0 @@ -pluginManagement { - repositories { - mavenCentral() - gradlePluginPortal() - } -} - -dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) - repositories { - mavenCentral() - } -} - -rootProject.name = 'agent-test-backend' diff --git a/potal/agent-test-backend/src/main/java/com/example/agenttest/AgentTestBackendApplication.java b/potal/agent-test-backend/src/main/java/com/example/agenttest/AgentTestBackendApplication.java deleted file mode 100644 index 9f126c0..0000000 --- a/potal/agent-test-backend/src/main/java/com/example/agenttest/AgentTestBackendApplication.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.example.agenttest; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.context.properties.ConfigurationPropertiesScan; -import org.springframework.scheduling.annotation.EnableScheduling; - -@SpringBootApplication -@ConfigurationPropertiesScan -@EnableScheduling -public class AgentTestBackendApplication { - - public static void main(String[] args) { - SpringApplication.run(AgentTestBackendApplication.class, args); - } -} diff --git a/potal/agent-test-backend/src/main/java/com/example/agenttest/AgentTestProperties.java b/potal/agent-test-backend/src/main/java/com/example/agenttest/AgentTestProperties.java deleted file mode 100644 index 6785715..0000000 --- a/potal/agent-test-backend/src/main/java/com/example/agenttest/AgentTestProperties.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.example.agenttest; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -@ConfigurationProperties(prefix = "agent-test") -public record AgentTestProperties(Mcp mcp, ToolServer toolServer, Portal portal) { - - public record Mcp(String endpointUrl, String protocolVersion) { - } - - public record ToolServer(String manifestUrl, String apiKey) { - } - - public record Portal(long registryRevision, String routeKey, String toolServiceDomain) { - } -} diff --git a/potal/agent-test-backend/src/main/java/com/example/agenttest/McpProxyController.java b/potal/agent-test-backend/src/main/java/com/example/agenttest/McpProxyController.java deleted file mode 100644 index f12f1bb..0000000 --- a/potal/agent-test-backend/src/main/java/com/example/agenttest/McpProxyController.java +++ /dev/null @@ -1,476 +0,0 @@ -package com.example.agenttest; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.servlet.http.HttpServletRequest; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.time.Year; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReference; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.client.RestClient; - -@RestController -@RequestMapping("/api") -public class McpProxyController { - - private static final Logger log = LoggerFactory.getLogger(McpProxyController.class); - private static final String MCP_SESSION_ID_HEADER = "Mcp-Session-Id"; - private static final String MCP_PROTOCOL_VERSION_HEADER = "MCP-Protocol-Version"; - private static final String TOOL_SERVER_API_KEY_HEADER = "X-Tool-Server-API-Key"; - - private final AgentTestProperties properties; - private final ObjectMapper objectMapper; - private final RestClient restClient; - private final AtomicLong ids = new AtomicLong(1); - private final PortalBundleService portalBundles; - private final AtomicReference latestSessionId = new AtomicReference<>(); - - public McpProxyController( - AgentTestProperties properties, - ObjectMapper objectMapper, - RestClient.Builder builder, - PortalBundleService portalBundles) { - this.properties = properties; - this.objectMapper = objectMapper; - this.restClient = builder.build(); - this.portalBundles = portalBundles; - } - - @GetMapping("/config") - public Map config() { - Map config = new LinkedHashMap<>(); - config.put("mcpEndpointUrl", properties.mcp().endpointUrl()); - config.put("mcpProtocolVersion", properties.mcp().protocolVersion()); - config.put("toolManifestUrl", properties.toolServer().manifestUrl()); - config.put("defaultRouteKey", defaultRouteKey()); - config.put("defaultRoutedMcpEndpointUrl", mcpEndpointUrl(defaultRouteKey())); - config.put("latestSessionId", latestSessionId.get()); - return config; - } - - @GetMapping("/registry") - public Map registry() { - String routeKey = defaultRouteKey(); - return portalBundles.screenRegistry(routeKey, mcpEndpointUrl(routeKey)); - } - - @GetMapping("/portal/registry") - public Map portalRegistry(HttpServletRequest request) { - log.info("Inbound portal aggregate registry request: method={}, uri={}, remoteAddress={}, userAgent={}, accept={}", - request.getMethod(), request.getRequestURI(), request.getRemoteAddr(), - request.getHeader("User-Agent"), request.getHeader("Accept")); - - Map registry = portalBundles.portalRegistry(); - log.info("Portal aggregate registry response: registryRevision={}, routes={}", - registry.get("registryRevision"), registry.get("routes")); - return registry; - } - - @GetMapping("/portal/registry/{routeKey}") - public Map portalRegistry( - @PathVariable("routeKey") String routeKey, - HttpServletRequest request) { - log.info("Inbound portal registry request: method={}, uri={}, remoteAddress={}, userAgent={}, accept={}", - request.getMethod(), request.getRequestURI(), request.getRemoteAddr(), - request.getHeader("User-Agent"), request.getHeader("Accept")); - - Map registry = portalBundles.portalRegistry(routeKey); - log.info("Portal registry response: routeKey={}, registryRevision={}, toolServices={}", - routeKey, registry.get("registryRevision"), registry.get("toolServices")); - return registry; - } - - @PostMapping("/portal/registry/{routeKey}/revision") - public Map bumpPortalRegistryRevision(@PathVariable("routeKey") String routeKey) { - long nextRevision = portalBundles.bumpRevision(); - log.info("Portal registry revision changed: routeKey={}, registryRevision={}", routeKey, nextRevision); - return Map.of( - "routeKey", routeKey, - "registryRevision", nextRevision); - } - - @GetMapping("/tool-manifest") - public Map toolManifest() { - log.info("Outbound tool-server request: method=GET, uri={}, headers={{{}={}}}", - properties.toolServer().manifestUrl(), TOOL_SERVER_API_KEY_HEADER, masked()); - ResponseEntity response = restClient.get() - .uri(properties.toolServer().manifestUrl()) - .header(TOOL_SERVER_API_KEY_HEADER, properties.toolServer().apiKey()) - .retrieve() - .toEntity(JsonNode.class); - log.info("Inbound tool-server response: status={}, body={}", - response.getStatusCode().value(), response.getBody()); - return response("tool-manifest", response); - } - - @PostMapping("/mcp/initialize") - public Map initialize() { - return initialize(properties.portal().routeKey()); - } - - @PostMapping("/mcp/{routeKey}/initialize") - public Map initialize(@PathVariable("routeKey") String routeKey) { - Map params = Map.of( - "protocolVersion", properties.mcp().protocolVersion(), - "capabilities", Map.of(), - "clientInfo", Map.of( - "name", "agent-test-backend", - "version", "0.1.0")); - ResponseEntity response = postMcp(routeKey, jsonRpc("initialize", params), false); - String sessionId = response.getHeaders().getFirst(MCP_SESSION_ID_HEADER); - if (sessionId != null && !sessionId.isBlank()) { - latestSessionId.set(sessionId); - } - return response("initialize", response); - } - - @PostMapping("/mcp/initialized") - public Map initialized() { - return initialized(properties.portal().routeKey()); - } - - @PostMapping("/mcp/{routeKey}/initialized") - public Map initialized(@PathVariable("routeKey") String routeKey) { - ResponseEntity response = postMcp(routeKey, notification("notifications/initialized"), true); - return response("notifications/initialized", response); - } - - @PostMapping("/mcp/tools/list") - public Map toolsList() { - return toolsList(properties.portal().routeKey()); - } - - @PostMapping("/mcp/{routeKey}/tools/list") - public Map toolsList(@PathVariable("routeKey") String routeKey) { - ResponseEntity response = postMcp(routeKey, jsonRpc("tools/list", Map.of()), true); - return response("tools/list", response); - } - - @PostMapping("/mcp/tools/call") - public Map toolsCall(@RequestBody ToolCallRequest request) { - return callTool(routeKeyForTool(request.name()), request.name(), request.arguments() == null ? Map.of() : request.arguments()); - } - - @PostMapping("/mcp/{routeKey}/tools/call") - public Map toolsCall( - @PathVariable("routeKey") String routeKey, - @RequestBody ToolCallRequest request) { - return callTool(routeKey, request.name(), request.arguments() == null ? Map.of() : request.arguments()); - } - - @PostMapping("/agent/chat") - public Map chat(@RequestBody ChatRequest request) { - PlannedTool plannedTool = plan(request.message()); - String routeKey = plannedTool.routeKey(); - Map toolCall = callTool(routeKey, plannedTool.name(), plannedTool.arguments()); - JsonNode body = objectMapper.valueToTree(toolCall.get("body")); - JsonNode toolPayload = firstToolText(body); - if (body.path("result").path("isError").asBoolean(false)) { - String toolError = toolPayload.path("text").asText("도구 호출에 실패했습니다."); - toolPayload = objectMapper.createObjectNode().put("error", toolError); - } - - Map result = new LinkedHashMap<>(); - result.put("routeKey", routeKey); - result.put("mcpEndpointUrl", mcpEndpointUrl(routeKey)); - result.put("message", request.message()); - result.put("selectedTool", plannedTool.name()); - result.put("routeDecision", "%s prefix Tool은 %s route로 전송".formatted( - toolPrefix(plannedTool.name()), routeKey)); - result.put("arguments", plannedTool.arguments()); - result.put("answer", answer(plannedTool.name(), toolPayload)); - result.put("toolResult", toolPayload); - result.put("rawMcpResponse", toolCall); - return result; - } - - private Map callTool(String routeKey, String name, Map arguments) { - Map params = Map.of("name", name, "arguments", arguments); - ResponseEntity response = postMcp(routeKey, jsonRpc("tools/call", params), true); - return response("tools/call", response); - } - - private PlannedTool plan(String message) { - return planRequest(message); - } - - static PlannedTool planRequest(String message) { - String normalized = message == null ? "" : message.toLowerCase(Locale.ROOT); - if (normalized.contains("메타 공통코드")) { - return new PlannedTool("cus", "cmm_comcode_lookup", - Map.of("groupCode", "GRP_COMM_CD", "useYn", "Y")); - } - if (normalized.contains("메타 테이블")) { - return new PlannedTool("cus", "cmm_meta_table", - Map.of("tableName", "TB_CUST_BAS", "owner", "DAPADM")); - } - if (normalized.contains("템플릿")) { - return new PlannedTool("cus", "cmm_template_url", Map.of("templateId", "TPL_001")); - } - if (normalized.contains("sol 의뢰서 상세") || normalized.contains("sol 상세")) { - return new PlannedTool("cus", "sol_request_detail", Map.of("srId", "SR-001")); - } - if (normalized.contains("sol 의뢰서") || normalized.contains("sol 목록")) { - return new PlannedTool("cus", "sol_request_list", - Map.of("status", "진행중", "period", "1개월", "target", "나의 업무")); - } - if (normalized.contains("보험금 청구")) { - return new PlannedTool("cus", "ins_insurance_processor", Map.of( - "claimNumber", "CLM20230001", "claimAmount", 1500000, "claimDate", "2026-08-12")); - } - if (normalized.contains("가입설계 한도") || normalized.contains("onnba3011")) { - return new PlannedTool("cus", "oth_onnba3011_call", Map.of( - "dalScCd", "1", "cstSucoRltyCd", "01", "csNo", "000000000001")); - } - if (normalized.contains("cus") && (normalized.contains("환율") || normalized.contains("exchange"))) { - return new PlannedTool("cus", "smp_exchange_inquiry", Map.of("currencyCode", "USD")); - } - if (normalized.contains("cus") && (normalized.contains("날씨") || normalized.contains("weather"))) { - return new PlannedTool("cus", "smp_weather_inquiry", Map.of("city", "서울")); - } - if (normalized.contains("오늘의 명언") || normalized.contains("명언")) { - return new PlannedTool("cus", "smp_quote_daily", Map.of("category", "속담")); - } - if (normalized.contains("tool 파트") || normalized.contains("툴 파트") || normalized.contains("파트 구성원")) { - return new PlannedTool("cus", "smp_team_list", Map.of("teamName", "TOOL")); - } - if (normalized.contains("공휴일") || normalized.contains("휴일") || normalized.contains("holiday")) { - return new PlannedTool("external", "external.public_holiday_lookup", Map.of( - "countryCode", "KR", - "year", Year.now().getValue())); - } - if ((normalized.contains("고객") || normalized.contains("customer")) - && !normalized.contains("티켓") && !normalized.contains("ticket")) { - return new PlannedTool("business", "business.customer_search", Map.of("keyword", "C-1001")); - } - if (normalized.contains("주문") || normalized.contains("order")) { - return new PlannedTool("business", "business.order_status", Map.of("orderId", "O-9001")); - } - if (normalized.contains("티켓") || normalized.contains("ticket")) { - return new PlannedTool("business", "business.ticket_create", Map.of( - "title", "Portal test ticket", - "priority", "normal", - "description", "Created from the Portal Agent test screen")); - } - if (normalized.contains("좌표") || normalized.contains("지오코딩") || normalized.contains("geocoding")) { - return new PlannedTool("external", "external.geocoding_lookup", Map.of("city", "Seoul", "language", "ko")); - } - if (normalized.contains("국가") || normalized.contains("나라") || normalized.contains("country")) { - return new PlannedTool("external", "external.country_info_lookup", Map.of("countryCode", "KR")); - } - if (normalized.contains("환율") || normalized.contains("달러") || normalized.contains("usd") - || normalized.contains("exchange")) { - return new PlannedTool("external", "external.exchange_rate", Map.of("from", "USD", "to", "KRW")); - } - return new PlannedTool("external", "external.weather_lookup", Map.of( - "city", city(normalized), - "timezone", "Asia/Seoul")); - } - - private static String toolPrefix(String toolName) { - int dot = toolName.indexOf('.'); - if (dot > 0) { - return toolName.substring(0, dot); - } - int underscore = toolName.indexOf('_'); - return underscore > 0 ? toolName.substring(0, underscore) : toolName; - } - - private static String city(String message) { - if (message.contains("부산") || message.contains("busan")) { - return "Busan"; - } - if (message.contains("대구") || message.contains("daegu")) { - return "Daegu"; - } - if (message.contains("인천") || message.contains("incheon")) { - return "Incheon"; - } - return "Seoul"; - } - - private JsonNode firstToolText(JsonNode mcpBody) { - JsonNode content = mcpBody.path("result").path("content"); - if (!content.isArray() || content.isEmpty()) { - return mcpBody; - } - String text = content.get(0).path("text").asText(""); - if (text.isBlank()) { - return content.get(0); - } - try { - return objectMapper.readTree(text); - } catch (Exception ignored) { - return objectMapper.createObjectNode().put("text", text); - } - } - - static String answer(String toolName, JsonNode payload) { - boolean directToolResult = !payload.has("success") && !payload.has("error"); - if (!directToolResult && !payload.path("success").asBoolean(false)) { - String error = payload.path("error").asText(); - if (error.isBlank()) { - error = payload.path("text").asText("도구 호출에 실패했습니다."); - } - return "도구 실행이 실패했습니다: " + error; - } - JsonNode data = directToolResult ? payload : payload.path("data"); - if ("external.public_holiday_lookup".equals(toolName)) { - JsonNode holidays = data.path("holidays"); - List preview = new java.util.ArrayList<>(); - if (holidays.isArray()) { - for (int index = 0; index < Math.min(holidays.size(), 5); index++) { - JsonNode holiday = holidays.get(index); - preview.add("%s %s".formatted( - holiday.path("date").asText(), - holiday.path("localName").asText(holiday.path("name").asText()))); - } - } - return "%s년 대한민국 공휴일은 총 %s일입니다.%s".formatted( - data.path("year").asText(String.valueOf(Year.now().getValue())), - holidays.isArray() ? holidays.size() : 0, - preview.isEmpty() ? "" : " 주요 공휴일: " + String.join(", ", preview)); - } - if (toolName.startsWith("business.")) { - return "%s 실행 결과: %s".formatted(toolName, data.toString()); - } - if (isCusTool(toolName)) { - return "%s 실행 결과: %s".formatted(toolName, data.toString()); - } - if ("external.geocoding_lookup".equals(toolName)) { - return "도시 좌표 조회 결과: " + data.path("results").toString(); - } - if ("external.country_info_lookup".equals(toolName)) { - return "국가 정보 조회 결과: " + data.path("countries").toString(); - } - if ("external.exchange_rate".equals(toolName)) { - return "%s 기준 %s/%s 환율은 %s입니다.".formatted( - data.path("date").asText("현재"), - data.path("base").asText("USD"), - data.path("target").asText("KRW"), - data.path("rate").asText()); - } - return "%s 현재 기온은 %s도이고 풍속은 %s입니다.".formatted( - data.path("city").asText("해당 지역"), - data.path("temperature").asText(), - data.path("windSpeed").asText()); - } - - private static boolean isCusTool(String toolName) { - return toolName.startsWith("cmm_") || toolName.startsWith("ins_") - || toolName.startsWith("oth_") || toolName.startsWith("smp_") - || toolName.startsWith("sol_"); - } - - private ResponseEntity postMcp(String routeKey, Map payload, boolean includeProtocolHeaders) { - String sessionId = includeProtocolHeaders ? latestSessionId.get() : null; - String endpointUrl = mcpEndpointUrl(routeKey); - Map headers = new LinkedHashMap<>(); - headers.put("Content-Type", MediaType.APPLICATION_JSON_VALUE); - if (includeProtocolHeaders) { - headers.put(MCP_PROTOCOL_VERSION_HEADER, properties.mcp().protocolVersion()); - if (sessionId != null && !sessionId.isBlank()) { - headers.put(MCP_SESSION_ID_HEADER, sessionId); - } - } - log.info("Outbound MCP request: method=POST, uri={}, headers={}, body={}", - endpointUrl, headers, payload); - - RestClient.RequestBodySpec spec = restClient.post() - .uri(endpointUrl) - .contentType(MediaType.APPLICATION_JSON); - if (includeProtocolHeaders) { - spec.header(MCP_PROTOCOL_VERSION_HEADER, properties.mcp().protocolVersion()); - if (sessionId != null && !sessionId.isBlank()) { - spec.header(MCP_SESSION_ID_HEADER, sessionId); - } - } - ResponseEntity response = spec.body(payload).retrieve().toEntity(JsonNode.class); - log.info("Inbound MCP response: status={}, headers={{{}={}}}, body={}", - response.getStatusCode().value(), MCP_SESSION_ID_HEADER, - response.getHeaders().getFirst(MCP_SESSION_ID_HEADER), response.getBody()); - return response; - } - - private String mcpEndpointUrl(String routeKey) { - String baseUrl = properties.mcp().endpointUrl().replaceAll("/+$", ""); - String normalizedRouteKey = normalizeRouteKey(routeKey); - return normalizedRouteKey.isBlank() ? baseUrl : baseUrl + "/" + normalizedRouteKey; - } - - private String normalizeRouteKey(String routeKey) { - if (routeKey == null || routeKey.isBlank()) { - return defaultRouteKey(); - } - return routeKey.trim(); - } - - private String defaultRouteKey() { - String configured = properties.portal().routeKey(); - return configured == null || configured.isBlank() ? "cus" : configured.trim(); - } - - private String routeKeyForTool(String toolName) { - if (toolName == null || toolName.isBlank()) { - return defaultRouteKey(); - } - if (toolName.startsWith("business.")) { - return "business"; - } - if (isCusTool(toolName)) { - return "cus"; - } - return "external"; - } - private String masked() { - return properties.toolServer().apiKey() == null || properties.toolServer().apiKey().isBlank() - ? "" - : "********"; - } - - private Map jsonRpc(String method, Map params) { - return Map.of( - "jsonrpc", "2.0", - "id", ids.getAndIncrement(), - "method", method, - "params", params); - } - - private Map notification(String method) { - return Map.of( - "jsonrpc", "2.0", - "method", method, - "params", Map.of()); - } - - private Map response(String action, ResponseEntity response) { - Map result = new LinkedHashMap<>(); - result.put("action", action); - result.put("httpStatus", response.getStatusCode().value()); - result.put("mcpSessionId", response.getHeaders().getFirst(MCP_SESSION_ID_HEADER)); - result.put("body", response.getBody() == null ? objectMapper.createObjectNode() : response.getBody()); - return result; - } - - public record ToolCallRequest(String name, Map arguments) { - } - - public record ChatRequest(String message) { - } - - record PlannedTool(String routeKey, String name, Map arguments) { - } -} diff --git a/potal/agent-test-backend/src/main/java/com/example/agenttest/PortalApiExceptionHandler.java b/potal/agent-test-backend/src/main/java/com/example/agenttest/PortalApiExceptionHandler.java deleted file mode 100644 index b2d287d..0000000 --- a/potal/agent-test-backend/src/main/java/com/example/agenttest/PortalApiExceptionHandler.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.example.agenttest; - -import java.util.LinkedHashMap; -import java.util.Map; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; -import org.springframework.web.server.ResponseStatusException; - -@RestControllerAdvice(assignableTypes = PortalBundleController.class) -public class PortalApiExceptionHandler { - - @ExceptionHandler(ResponseStatusException.class) - public ResponseEntity> handle(ResponseStatusException error) { - Map body = new LinkedHashMap<>(); - body.put("status", error.getStatusCode().value()); - body.put("error", error.getStatusCode().toString()); - body.put("message", error.getReason() == null ? "Request failed" : error.getReason()); - return ResponseEntity.status(error.getStatusCode()).body(body); - } -} diff --git a/potal/agent-test-backend/src/main/java/com/example/agenttest/PortalBundleController.java b/potal/agent-test-backend/src/main/java/com/example/agenttest/PortalBundleController.java deleted file mode 100644 index 38ae21c..0000000 --- a/potal/agent-test-backend/src/main/java/com/example/agenttest/PortalBundleController.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.example.agenttest; - -import com.example.agenttest.PortalBundleService.BundleRequest; -import com.example.agenttest.PortalBundleService.BundleView; -import java.util.List; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/api/portal/bundles") -public class PortalBundleController { - - private final PortalBundleService bundles; - - public PortalBundleController(PortalBundleService bundles) { - this.bundles = bundles; - } - - @GetMapping - public List list() { - return bundles.list(); - } - - @PostMapping - @ResponseStatus(HttpStatus.CREATED) - public BundleView create(@RequestBody BundleRequest request) { - return bundles.create(request); - } - - @PutMapping("/{bundleId}") - public BundleView update( - @PathVariable("bundleId") String bundleId, - @RequestBody BundleRequest request) { - return bundles.update(bundleId, request); - } - - -} diff --git a/potal/agent-test-backend/src/main/java/com/example/agenttest/PortalBundleService.java b/potal/agent-test-backend/src/main/java/com/example/agenttest/PortalBundleService.java deleted file mode 100644 index a52aa88..0000000 --- a/potal/agent-test-backend/src/main/java/com/example/agenttest/PortalBundleService.java +++ /dev/null @@ -1,346 +0,0 @@ -package com.example.agenttest; - -import java.net.URI; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicLong; -import java.util.regex.Pattern; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestClient; -import org.springframework.web.server.ResponseStatusException; - -@Service -public class PortalBundleService { - - private static final Pattern BUNDLE_ID = Pattern.compile("[A-Za-z0-9._-]{1,64}"); - private static final Pattern TOOL_NAME = Pattern.compile("[A-Za-z0-9_./-]{1,64}"); - - private final Map bundles = new ConcurrentHashMap<>(); - private final AtomicLong registryRevision; - - public PortalBundleService(AgentTestProperties properties, RestClient.Builder builder) { - this.registryRevision = new AtomicLong(properties.portal().registryRevision()); - String sharedApiKey = properties.toolServer().apiKey(); - putSeed(new BundleDefinition( - "external-tools", "External Tool Server", - properties.toolServer().manifestUrl(), properties.portal().toolServiceDomain(), - "external.", true, 10, sharedApiKey, - Map.of( - "external.weather_lookup", "/mcp/external.weather_lookup", - "external.exchange_rate", "/mcp/external.exchange_rate", - "external.public_holiday_lookup", "/mcp/external.public_holiday_lookup", - "external.geocoding_lookup", "/mcp/external.geocoding_lookup", - "external.country_info_lookup", "/mcp/external.country_info_lookup"))); - putSeed(new BundleDefinition( - "business-tools", "Business Tool Server", - "http://localhost:9090/tool-manifest", "http://localhost:9090", - "business.", true, 10, sharedApiKey, - Map.of( - "business.customer_search", "/mcp/business.customer_search", - "business.order_status", "/mcp/business.order_status", - "business.ticket_create", "/mcp/business.ticket_create"))); - putSeed(new BundleDefinition( - "was-cus", "DAP WAS CUS Tool Server", - "http://localhost:8084/tool-manifest", "http://localhost:8084", - "", true, 10, null, - cusToolEndpoints())); - } - - public List list() { - return bundles.values().stream() - .map(BundleState::view) - .sorted(Comparator.comparing(view -> view.definition().bundleId())) - .toList(); - } - - public BundleView create(BundleRequest request) { - BundleDefinition definition = validated(request, null); - if (bundles.containsKey(definition.bundleId())) { - throw conflict("Bundle ID already exists: " + definition.bundleId()); - } - ensureManifestUrlUnique(definition.manifestUrl(), null); - BundleState state = new BundleState(definition); - bundles.put(definition.bundleId(), state); - registryRevision.incrementAndGet(); - return state.view(); - } - - public BundleView update(String bundleId, BundleRequest request) { - BundleState current = required(bundleId); - BundleDefinition definition = validated(request, current.definition().apiKey()); - if (!bundleId.equals(definition.bundleId())) { - throw badRequest("Bundle ID cannot be changed"); - } - ensureManifestUrlUnique(definition.manifestUrl(), bundleId); - current.update(definition); - registryRevision.incrementAndGet(); - return current.view(); - } - - public Map portalRegistry(String routeKey) { - List> services = bundles.values().stream() - .filter(state -> state.definition().enabled()) - .filter(state -> belongsToRoute(state.definition(), routeKey)) - .map(state -> service(state.definition())) - .sorted(Comparator.comparing(item -> String.valueOf(item.get("serviceKey")))) - .toList(); - return Map.of( - "routeKey", routeKey, - "registryRevision", registryRevision.get(), - "toolServices", services); - } - - public Map portalRegistry() { - List> routes = bundles.values().stream() - .filter(state -> state.definition().enabled()) - .map(state -> routeKey(state.definition())) - .distinct() - .sorted() - .map(routeKey -> Map.of( - "routeKey", routeKey, - "toolServices", bundles.values().stream() - .filter(state -> state.definition().enabled()) - .filter(state -> belongsToRoute(state.definition(), routeKey)) - .map(state -> service(state.definition())) - .sorted(Comparator.comparing(item -> String.valueOf(item.get("serviceKey")))) - .toList())) - .toList(); - return Map.of( - "registryRevision", registryRevision.get(), - "routes", routes); - } - - private boolean belongsToRoute(BundleDefinition definition, String routeKey) { - String normalizedRoute = routeKey == null ? "" : routeKey.trim().toLowerCase(java.util.Locale.ROOT); - return routeKey(definition).equalsIgnoreCase(normalizedRoute); - } - - private String routeKey(BundleDefinition definition) { - String prefix = definition.namePrefix(); - if (prefix == null || prefix.isBlank()) { - return definition.bundleId().startsWith("was-") - ? definition.bundleId().substring("was-".length()) - : definition.bundleId(); - } - int dot = prefix.indexOf('.'); - int underscore = prefix.indexOf('_'); - int end = dot >= 0 && underscore >= 0 ? Math.min(dot, underscore) : Math.max(dot, underscore); - return end > 0 ? prefix.substring(0, end) : prefix.replaceAll("[._]+$", ""); - } - - public Map screenRegistry(String routeKey, String mcpEndpointUrl) { - Map route = new LinkedHashMap<>(); - route.put("routeKey", routeKey); - route.put("displayName", "Portal MCP Route"); - route.put("routePath", "/mcp/" + routeKey); - route.put("mcpEndpointUrl", mcpEndpointUrl); - route.put("status", "ACTIVE"); - - List> services = list().stream().map(view -> { - BundlePublicDefinition item = view.definition(); - Map service = new LinkedHashMap<>(); - service.put("serviceKey", item.bundleId()); - service.put("displayName", item.toolServerName()); - service.put("manifestUrl", item.manifestUrl()); - service.put("baseEndpoint", item.baseUrl()); - service.put("namePrefix", item.namePrefix()); - service.put("enabled", item.enabled()); - service.put("manifestPollIntervalSeconds", item.manifestPollIntervalSeconds()); - service.put("status", item.enabled() ? "ACTIVE" : "INACTIVE"); - return service; - }).toList(); - return Map.of("mcpRoutes", List.of(route), "toolServices", services, - "mappings", services.stream().map(service -> Map.of( - "routeKey", routeKey, - "serviceKey", service.get("serviceKey"), - "status", service.get("status"), - "source", "portal-bundle-registry", - "registryRevision", registryRevision.get())).toList()); - } - - public long bumpRevision() { - return registryRevision.incrementAndGet(); - } - - private Map service(BundleDefinition definition) { - URI manifest = URI.create(definition.manifestUrl()); - String manifestPath = manifest.getRawPath(); - Map service = new LinkedHashMap<>(); - service.put("serviceKey", definition.bundleId()); - service.put("displayName", definition.toolServerName()); - service.put("serviceDomain", definition.baseUrl()); - service.put("manifestPath", manifestPath == null || manifestPath.isBlank() ? "/tool-manifest" : manifestPath); - service.put("executeBasePath", ""); - service.put("namePrefix", definition.namePrefix()); - service.put("toolEndpoints", definition.toolEndpoints()); - service.put("status", definition.enabled() ? "ACTIVE" : "INACTIVE"); - return service; - } - - private BundleDefinition validated(BundleRequest request, String existingApiKey) { - if (request == null) { - throw badRequest("Request body is required"); - } - String bundleId = requiredText(request.bundleId(), "bundleId"); - if (!BUNDLE_ID.matcher(bundleId).matches()) { - throw badRequest("Bundle ID is invalid"); - } - String name = requiredText(request.toolServerName(), "toolServerName"); - String manifestUrl = validUrl(request.manifestUrl(), "manifestUrl"); - String baseUrl = validUrl(request.baseUrl(), "baseUrl").replaceAll("/+$", ""); - String prefix = request.namePrefix() == null ? "" : request.namePrefix().trim(); - if (!prefix.isBlank() && !prefix.endsWith(".") && !prefix.endsWith("_")) { - throw badRequest("Tool name prefix must end with '.' or '_'"); - } - long interval = request.manifestPollIntervalSeconds(); - if (interval < 5 || interval > 86_400) { - throw badRequest("Manifest poll interval must be between 5 and 86400 seconds"); - } - Map endpoints = request.toolEndpoints() == null - ? Map.of() : Map.copyOf(request.toolEndpoints()); - endpoints.forEach((toolName, path) -> { - if (!TOOL_NAME.matcher(toolName).matches() - || (!prefix.isBlank() && !toolName.startsWith(prefix))) { - throw badRequest("Tool endpoint name must start with " + prefix + ": " + toolName); - } - if (path == null || !path.startsWith("/") || path.startsWith("//")) { - throw badRequest("Tool endpoint path must start with a single '/': " + toolName); - } - }); - String apiKey = request.apiKey() == null || request.apiKey().isBlank() ? existingApiKey : request.apiKey(); - return new BundleDefinition(bundleId, name, manifestUrl, baseUrl, prefix, - request.enabled(), interval, apiKey, endpoints); - } - - private Map cusToolEndpoints() { - List names = List.of( - "cmm_comcode_lookup", "cmm_customer_tool", "cmm_meta_table", "cmm_template_url", - "ins_insurance_processor", "oth_onnba3011_call", - "smp_exchange_inquiry", "smp_quote_daily", "smp_team_list", - "smp_weather_inquiry", "sol_request_detail", "sol_request_list"); - Map endpoints = new LinkedHashMap<>(); - names.forEach(name -> endpoints.put(name, "/mcp/" + name)); - return Map.copyOf(endpoints); - } - - private void ensureManifestUrlUnique(String manifestUrl, String excludedBundleId) { - boolean duplicate = bundles.values().stream().anyMatch(state -> - !state.definition().bundleId().equals(excludedBundleId) - && state.definition().manifestUrl().equalsIgnoreCase(manifestUrl)); - if (duplicate) { - throw conflict("Manifest URL already exists: " + manifestUrl); - } - } - - private BundleState required(String bundleId) { - BundleState state = bundles.get(bundleId); - if (state == null) { - throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Bundle not found: " + bundleId); - } - return state; - } - - private void putSeed(BundleDefinition definition) { - bundles.put(definition.bundleId(), new BundleState(definition)); - } - - private String requiredText(String value, String field) { - if (value == null || value.isBlank()) { - throw badRequest(field + " is required"); - } - return value.trim(); - } - - private String validUrl(String value, String field) { - String text = requiredText(value, field); - try { - URI uri = URI.create(text); - if (!uri.isAbsolute() || !("http".equalsIgnoreCase(uri.getScheme()) || "https".equalsIgnoreCase(uri.getScheme())) - || uri.getHost() == null) { - throw new IllegalArgumentException(); - } - return uri.toString(); - } catch (RuntimeException error) { - throw badRequest(field + " must be an absolute HTTP(S) URL"); - } - } - - private ResponseStatusException badRequest(String message) { - return new ResponseStatusException(HttpStatus.BAD_REQUEST, message); - } - - private ResponseStatusException conflict(String message) { - return new ResponseStatusException(HttpStatus.CONFLICT, message); - } - - public record BundleRequest( - String bundleId, - String toolServerName, - String manifestUrl, - String baseUrl, - String namePrefix, - boolean enabled, - long manifestPollIntervalSeconds, - String apiKey, - Map toolEndpoints) { - } - - public record BundleDefinition( - String bundleId, - String toolServerName, - String manifestUrl, - String baseUrl, - String namePrefix, - boolean enabled, - long manifestPollIntervalSeconds, - String apiKey, - Map toolEndpoints) { - } - - public record BundlePublicDefinition( - String bundleId, - String toolServerName, - String manifestUrl, - String baseUrl, - String namePrefix, - boolean enabled, - long manifestPollIntervalSeconds, - boolean apiKeyConfigured, - Map toolEndpoints) { - } - - public record BundleView( - BundlePublicDefinition definition, - String manifestRevision, - String lastSynchronizedAt, - String lastError, - List> tools) { - } - - private static final class BundleState { - private volatile BundleDefinition definition; - - private BundleState(BundleDefinition definition) { - this.definition = definition; - } - - private synchronized void update(BundleDefinition definition) { - this.definition = definition; - } - - private BundleDefinition definition() { return definition; } - - private BundleView view() { - BundleDefinition item = definition; - BundlePublicDefinition publicDefinition = new BundlePublicDefinition( - item.bundleId(), item.toolServerName(), item.manifestUrl(), item.baseUrl(), - item.namePrefix(), item.enabled(), item.manifestPollIntervalSeconds(), - item.apiKey() != null && !item.apiKey().isBlank(), item.toolEndpoints()); - return new BundleView(publicDefinition, null, null, null, List.of()); - } - } -} diff --git a/potal/agent-test-backend/src/main/resources/application.yml b/potal/agent-test-backend/src/main/resources/application.yml deleted file mode 100644 index 7c5fa8f..0000000 --- a/potal/agent-test-backend/src/main/resources/application.yml +++ /dev/null @@ -1,18 +0,0 @@ -server: - port: ${AGENT_TEST_PORT:7070} - -agent-test: - mcp: - endpoint-url: ${MCP_ENDPOINT_URL:http://localhost:8080/mcp} - protocol-version: ${MCP_PROTOCOL_VERSION:2025-11-25} - tool-server: - manifest-url: ${TOOL_MANIFEST_URL:http://localhost:9092/tool-manifest} - api-key: ${TOOL_SERVER_API_KEY:tool-server-key} - portal: - registry-revision: ${PORTAL_REGISTRY_REVISION:1} - route-key: ${PORTAL_ROUTE_KEY:cus} - tool-service-domain: ${TOOL_SERVICE_DOMAIN:http://localhost:9092} - -logging: - level: - com.example.agenttest: INFO diff --git a/potal/agent-test-backend/src/main/resources/static/index.html b/potal/agent-test-backend/src/main/resources/static/index.html deleted file mode 100644 index f47ca89..0000000 --- a/potal/agent-test-backend/src/main/resources/static/index.html +++ /dev/null @@ -1,625 +0,0 @@ - - - - - - AX HUB Portal PoC - - - -
-
-

AX HUB Portal PoC

-
Hardcoded Registry + Agent Backend + MCP Tool Call
-
-
Loading configuration...
-
- -
-
-
-

Portal Registry

-

필요할 때 MCP가 Registry를 다시 확인하도록 revision을 갱신합니다.

- -
- - -
-
- Tool Server 설정 -
- - - - - - - - - - - -
- 고급 설정 - - - - - - -
-
- - -
-
-
-
- -
-
- MCP 연결 테스트 - - -
- - - -
-
-
-
-
- -
-
-
- Agent 테스트 - - -
- - - - - - - - - - - - - - - - - - - - - -
-
Agent 응답 대기 중
-
-
- -
-
- Tool 직접 호출 - - -
-
- - -
-
- - -
-
-
- - - -
-
-
- -
-
- 상세 응답 보기 -
Waiting...
-
-
-
-
- - - - diff --git a/potal/agent-test-backend/src/test/java/com/example/agenttest/McpProxyControllerTest.java b/potal/agent-test-backend/src/test/java/com/example/agenttest/McpProxyControllerTest.java deleted file mode 100644 index 89a0942..0000000 --- a/potal/agent-test-backend/src/test/java/com/example/agenttest/McpProxyControllerTest.java +++ /dev/null @@ -1,123 +0,0 @@ -package com.example.agenttest; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.web.servlet.MockMvc; - -@SpringBootTest -@AutoConfigureMockMvc -class McpProxyControllerTest { - - @Test - void treatsDirectExternalToolPayloadAsSuccessfulResult() throws Exception { - var payload = new com.fasterxml.jackson.databind.ObjectMapper().readTree( - "{\"year\":2026,\"holidays\":[{\"date\":\"2026-01-01\",\"localName\":\"New Year\"}]}" ); - - assertThat(McpProxyController.answer("external.public_holiday_lookup", payload)) - .contains("2026", "1", "2026-01-01", "New Year") - .doesNotContain("실패"); - } - - @Test - void treatsDirectOthToolPayloadAsSuccessfulResult() throws Exception { - var payload = new com.fasterxml.jackson.databind.ObjectMapper().readTree( - "{\"codeList\":[{\"code\":\"CD001\",\"codeName\":\"진행중\"}]}"); - - assertThat(McpProxyController.answer("cmm_comcode_lookup", payload)) - .contains("cmm_comcode_lookup 실행 결과") - .contains("CD001") - .doesNotContain("실패"); - } - - @Autowired - private MockMvc mockMvc; - - @Test - void portalRegistryReturnsOkForExternalRoute() throws Exception { - mockMvc.perform(get("/api/portal/registry/external")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.routeKey").value("external")) - .andExpect(jsonPath("$.toolServices.length()").value(1)) - .andExpect(jsonPath("$.toolServices[0].serviceKey").value("external-tools")); - } - - @Test - void bumpsPortalRegistryRevision() throws Exception { - mockMvc.perform(post("/api/portal/registry/external/revision")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.routeKey").value("external")) - .andExpect(jsonPath("$.registryRevision").isNumber()); - } - - @Test - void aggregatePortalRegistryReturnsEveryRouteAndToolService() throws Exception { - mockMvc.perform(get("/api/portal/registry")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.registryRevision").isNumber()) - .andExpect(jsonPath("$.routes[?(@.routeKey=='external')].toolServices[0].serviceKey") - .value("external-tools")) - .andExpect(jsonPath("$.routes[?(@.routeKey=='business')].toolServices[0].serviceKey") - .value("business-tools")) - .andExpect(jsonPath("$.routes[?(@.routeKey=='cus')].toolServices[0].serviceKey") - .value("was-cus")); - } - - @Test - void exposesBusinessBundleAndEndpointMappingsWithoutApiKey() throws Exception { - mockMvc.perform(get("/api/portal/bundles")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$[0].definition.apiKey").doesNotExist()) - .andExpect(jsonPath("$[0].definition.apiKeyConfigured").isBoolean()) - .andExpect(jsonPath("$[0].definition.bundleId").value("business-tools")) - .andExpect(jsonPath("$[0].definition.toolEndpoints['business.customer_search']") - .value("/mcp/business.customer_search")); - - mockMvc.perform(get("/api/portal/registry/business")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.toolServices[0].serviceKey").value("business-tools")) - .andExpect(jsonPath("$.toolServices[0].toolEndpoints['business.ticket_create']") - .value("/mcp/business.ticket_create")); - } - - @Test - void selectsPublicHolidayToolForKoreanHolidayRequest() { - McpProxyController.PlannedTool plan = McpProxyController.planRequest("지금 현재 공휴일 조회해줘"); - - assertThat(plan.name()).isEqualTo("external.public_holiday_lookup"); - assertThat(plan.routeKey()).isEqualTo("external"); - assertThat(plan.arguments()).containsEntry("countryCode", "KR").containsKey("year"); - } - - @Test - void selectsEveryManifestToolFromNaturalLanguageExamples() { - assertThat(McpProxyController.planRequest("서울 날씨 조회").name()).isEqualTo("external.weather_lookup"); - assertThat(McpProxyController.planRequest("달러 환율 조회").name()).isEqualTo("external.exchange_rate"); - assertThat(McpProxyController.planRequest("서울 좌표 조회").name()).isEqualTo("external.geocoding_lookup"); - assertThat(McpProxyController.planRequest("한국 국가 정보 조회").name()).isEqualTo("external.country_info_lookup"); - assertThat(McpProxyController.planRequest("고객 검색").name()).isEqualTo("business.customer_search"); - assertThat(McpProxyController.planRequest("주문 상태 조회").name()).isEqualTo("business.order_status"); - assertThat(McpProxyController.planRequest("고객 지원 티켓 생성").name()).isEqualTo("business.ticket_create"); - assertThat(McpProxyController.planRequest("메타 공통코드 조회").name()).isEqualTo("cmm_comcode_lookup"); - assertThat(McpProxyController.planRequest("메타 테이블 조회").name()).isEqualTo("cmm_meta_table"); - assertThat(McpProxyController.planRequest("템플릿 다운로드 URL 알려줘").name()).isEqualTo("cmm_template_url"); - assertThat(McpProxyController.planRequest("SOL 의뢰서 목록 조회").name()).isEqualTo("sol_request_list"); - assertThat(McpProxyController.planRequest("SOL 의뢰서 상세 조회").name()).isEqualTo("sol_request_detail"); - assertThat(McpProxyController.planRequest("보험금 청구 처리").name()).isEqualTo("ins_insurance_processor"); - assertThat(McpProxyController.planRequest("가입설계 한도 조회").name()).isEqualTo("oth_onnba3011_call"); - assertThat(McpProxyController.planRequest("CUS 달러 환율 조회").name()).isEqualTo("smp_exchange_inquiry"); - assertThat(McpProxyController.planRequest("CUS 서울 날씨 조회").name()).isEqualTo("smp_weather_inquiry"); - assertThat(McpProxyController.planRequest("오늘의 명언 알려줘").name()).isEqualTo("smp_quote_daily"); - assertThat(McpProxyController.planRequest("TOOL 파트 구성원 조회").name()).isEqualTo("smp_team_list"); - assertThat(McpProxyController.planRequest("고객 검색").routeKey()).isEqualTo("business"); - assertThat(McpProxyController.planRequest("서울 날씨 조회").routeKey()).isEqualTo("external"); - assertThat(McpProxyController.planRequest("메타 테이블 조회").routeKey()).isEqualTo("cus"); - } -} diff --git a/potal/agent-test-backend/src/test/java/com/example/agenttest/PortalApiExceptionHandlerTest.java b/potal/agent-test-backend/src/test/java/com/example/agenttest/PortalApiExceptionHandlerTest.java deleted file mode 100644 index eecc88c..0000000 --- a/potal/agent-test-backend/src/test/java/com/example/agenttest/PortalApiExceptionHandlerTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.example.agenttest; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Map; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; -import org.springframework.web.server.ResponseStatusException; - -class PortalApiExceptionHandlerTest { - - @Test - void exposesSafeValidationReasonToPortalUi() { - var response = new PortalApiExceptionHandler().handle( - new ResponseStatusException(HttpStatus.BAD_REQUEST, - "Manifest Bundle ID does not match configuration")); - - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); - assertThat(response.getBody()).containsAllEntriesOf(Map.of( - "status", 400, - "error", "400 BAD_REQUEST", - "message", "Manifest Bundle ID does not match configuration")); - } -} diff --git a/potal/agent-test-backend/src/test/java/com/example/agenttest/PortalBundleServiceTest.java b/potal/agent-test-backend/src/test/java/com/example/agenttest/PortalBundleServiceTest.java deleted file mode 100644 index 6b7517b..0000000 --- a/potal/agent-test-backend/src/test/java/com/example/agenttest/PortalBundleServiceTest.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.example.agenttest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import com.example.agenttest.PortalBundleService.BundleRequest; -import java.util.Map; -import org.junit.jupiter.api.Test; -import org.springframework.web.client.RestClient; -import org.springframework.web.server.ResponseStatusException; - -class PortalBundleServiceTest { - - @Test - void exposesSeededWasCusBundleOnCusRoute() { - PortalBundleService service = new PortalBundleService(properties(), RestClient.builder()); - - var bundle = service.list().stream() - .filter(item -> item.definition().bundleId().equals("was-cus")) - .findFirst().orElseThrow(); - - assertThat(bundle.definition().manifestUrl()).isEqualTo("http://localhost:8084/tool-manifest"); - assertThat(bundle.definition().namePrefix()).isEmpty(); - assertThat(bundle.definition().manifestPollIntervalSeconds()).isEqualTo(10); - assertThat(bundle.definition().toolEndpoints()) - .containsEntry("smp_weather_inquiry", "/mcp/smp_weather_inquiry") - .containsEntry("ins_insurance_processor", "/mcp/ins_insurance_processor") - .containsEntry("cmm_customer_tool", "/mcp/cmm_customer_tool") - .hasSize(12); - assertThat(service.portalRegistry("cus").get("toolServices").toString()).contains("was-cus"); - assertThat(service.portalRegistry("external").get("toolServices").toString()).doesNotContain("was-cus"); - } - - @Test - void exposesOnlyEndpointRegistryWithoutManifestSnapshot() { - PortalBundleService service = new PortalBundleService(properties(), RestClient.builder()); - - var business = service.list().stream() - .filter(item -> item.definition().bundleId().equals("business-tools")) - .findFirst().orElseThrow(); - Map registry = service.portalRegistry("business"); - - assertThat(business.manifestRevision()).isNull(); - assertThat(business.lastSynchronizedAt()).isNull(); - assertThat(business.lastError()).isNull(); - assertThat(business.tools()).isEmpty(); - assertThat(registry.get("toolServices").toString()) - .contains("serviceDomain=http://localhost:9090") - .contains("manifestPath=/tool-manifest") - .contains("business.customer_search=/mcp/business.customer_search"); - } - - @Test - void rejectsDuplicateBundleIdManifestUrlAndInvalidUrl() { - PortalBundleService service = new PortalBundleService(properties(), RestClient.builder()); - BundleRequest duplicateId = request("business-tools", "http://localhost:9191/tool-manifest"); - BundleRequest duplicateUrl = request("another-tools", "http://localhost:9090/tool-manifest"); - BundleRequest invalidUrl = request("invalid-tools", "not-a-url"); - - assertThatThrownBy(() -> service.create(duplicateId)) - .isInstanceOf(ResponseStatusException.class) - .hasMessageContaining("409 CONFLICT"); - assertThatThrownBy(() -> service.create(duplicateUrl)) - .isInstanceOf(ResponseStatusException.class) - .hasMessageContaining("409 CONFLICT"); - assertThatThrownBy(() -> service.create(invalidUrl)) - .isInstanceOf(ResponseStatusException.class) - .hasMessageContaining("400 BAD_REQUEST"); - } - - private BundleRequest request(String bundleId, String manifestUrl) { - return new BundleRequest(bundleId, "Test", manifestUrl, "http://localhost:9191", - "test.", true, 60, "", Map.of()); - } - - private AgentTestProperties properties() { - return new AgentTestProperties( - new AgentTestProperties.Mcp("http://localhost:8080/mcp", "2025-11-25"), - new AgentTestProperties.ToolServer("http://localhost:9092/tool-manifest", "secret-key"), - new AgentTestProperties.Portal(1, "external", "http://localhost:9092")); - } - -}